1/* 2 * Intel MIC Platform Software Stack (MPSS) 3 * 4 * Copyright(c) 2015 Intel Corporation. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License, version 2, as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, but 11 * WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * General Public License for more details. 14 * 15 * The full GNU General Public License is included in this distribution in 16 * the file called "COPYING". 17 * 18 * Intel MIC Coprocessor State Management (COSM) Driver 19 * 20 */ 21#include <linux/slab.h> 22#include "cosm_main.h" 23 24/* 25 * A state-to-string lookup table, for exposing a human readable state 26 * via sysfs. Always keep in sync with enum cosm_states 27 */ 28const char * const cosm_state_string[] = { 29 [MIC_READY] = "ready", 30 [MIC_BOOTING] = "booting", 31 [MIC_ONLINE] = "online", 32 [MIC_SHUTTING_DOWN] = "shutting_down", 33 [MIC_RESETTING] = "resetting", 34 [MIC_RESET_FAILED] = "reset_failed", 35}; 36 37/* 38 * A shutdown-status-to-string lookup table, for exposing a human 39 * readable state via sysfs. Always keep in sync with enum cosm_shutdown_status 40 */ 41const char * const cosm_shutdown_status_string[] = { 42 [MIC_NOP] = "nop", 43 [MIC_CRASHED] = "crashed", 44 [MIC_HALTED] = "halted", 45 [MIC_POWER_OFF] = "poweroff", 46 [MIC_RESTART] = "restart", 47}; 48 49void cosm_set_shutdown_status(struct cosm_device *cdev, u8 shutdown_status) 50{ 51 dev_dbg(&cdev->dev, "Shutdown Status %s -> %s\n", 52 cosm_shutdown_status_string[cdev->shutdown_status], 53 cosm_shutdown_status_string[shutdown_status]); 54 cdev->shutdown_status = shutdown_status; 55} 56 57void cosm_set_state(struct cosm_device *cdev, u8 state) 58{ 59 dev_dbg(&cdev->dev, "State %s -> %s\n", 60 cosm_state_string[cdev->state], 61 cosm_state_string[state]); 62 cdev->state = state; 63 sysfs_notify_dirent(cdev->state_sysfs); 64} 65 66static ssize_t 67family_show(struct device *dev, struct device_attribute *attr, char *buf) 68{ 69 struct cosm_device *cdev = dev_get_drvdata(dev); 70 71 if (!cdev) 72 return -EINVAL; 73 74 return cdev->hw_ops->family(cdev, buf); 75} 76static DEVICE_ATTR_RO(family); 77 78static ssize_t 79stepping_show(struct device *dev, struct device_attribute *attr, char *buf) 80{ 81 struct cosm_device *cdev = dev_get_drvdata(dev); 82 83 if (!cdev) 84 return -EINVAL; 85 86 return cdev->hw_ops->stepping(cdev, buf); 87} 88static DEVICE_ATTR_RO(stepping); 89 90static ssize_t 91state_show(struct device *dev, struct device_attribute *attr, char *buf) 92{ 93 struct cosm_device *cdev = dev_get_drvdata(dev); 94 95 if (!cdev || cdev->state >= MIC_LAST) 96 return -EINVAL; 97 98 return scnprintf(buf, PAGE_SIZE, "%s\n", 99 cosm_state_string[cdev->state]); 100} 101 102static ssize_t 103state_store(struct device *dev, struct device_attribute *attr, 104 const char *buf, size_t count) 105{ 106 struct cosm_device *cdev = dev_get_drvdata(dev); 107 int rc; 108 109 if (!cdev) 110 return -EINVAL; 111 112 if (sysfs_streq(buf, "boot")) { 113 rc = cosm_start(cdev); 114 goto done; 115 } 116 if (sysfs_streq(buf, "reset")) { 117 rc = cosm_reset(cdev); 118 goto done; 119 } 120 121 if (sysfs_streq(buf, "shutdown")) { 122 rc = cosm_shutdown(cdev); 123 goto done; 124 } 125 rc = -EINVAL; 126done: 127 if (rc) 128 count = rc; 129 return count; 130} 131static DEVICE_ATTR_RW(state); 132 133static ssize_t shutdown_status_show(struct device *dev, 134 struct device_attribute *attr, char *buf) 135{ 136 struct cosm_device *cdev = dev_get_drvdata(dev); 137 138 if (!cdev || cdev->shutdown_status >= MIC_STATUS_LAST) 139 return -EINVAL; 140 141 return scnprintf(buf, PAGE_SIZE, "%s\n", 142 cosm_shutdown_status_string[cdev->shutdown_status]); 143} 144static DEVICE_ATTR_RO(shutdown_status); 145 146static ssize_t 147heartbeat_enable_show(struct device *dev, 148 struct device_attribute *attr, char *buf) 149{ 150 struct cosm_device *cdev = dev_get_drvdata(dev); 151 152 if (!cdev) 153 return -EINVAL; 154 155 return scnprintf(buf, PAGE_SIZE, "%d\n", cdev->sysfs_heartbeat_enable); 156} 157 158static ssize_t 159heartbeat_enable_store(struct device *dev, 160 struct device_attribute *attr, 161 const char *buf, size_t count) 162{ 163 struct cosm_device *cdev = dev_get_drvdata(dev); 164 int enable; 165 int ret; 166 167 if (!cdev) 168 return -EINVAL; 169 170 mutex_lock(&cdev->cosm_mutex); 171 ret = kstrtoint(buf, 10, &enable); 172 if (ret) 173 goto unlock; 174 175 cdev->sysfs_heartbeat_enable = enable; 176 /* if state is not online, cdev->heartbeat_watchdog_enable is 0 */ 177 if (cdev->state == MIC_ONLINE) 178 cdev->heartbeat_watchdog_enable = enable; 179 ret = count; 180unlock: 181 mutex_unlock(&cdev->cosm_mutex); 182 return ret; 183} 184static DEVICE_ATTR_RW(heartbeat_enable); 185 186static ssize_t 187cmdline_show(struct device *dev, struct device_attribute *attr, char *buf) 188{ 189 struct cosm_device *cdev = dev_get_drvdata(dev); 190 char *cmdline; 191 192 if (!cdev) 193 return -EINVAL; 194 195 cmdline = cdev->cmdline; 196 197 if (cmdline) 198 return scnprintf(buf, PAGE_SIZE, "%s\n", cmdline); 199 return 0; 200} 201 202static ssize_t 203cmdline_store(struct device *dev, struct device_attribute *attr, 204 const char *buf, size_t count) 205{ 206 struct cosm_device *cdev = dev_get_drvdata(dev); 207 208 if (!cdev) 209 return -EINVAL; 210 211 mutex_lock(&cdev->cosm_mutex); 212 kfree(cdev->cmdline); 213 214 cdev->cmdline = kmalloc(count + 1, GFP_KERNEL); 215 if (!cdev->cmdline) { 216 count = -ENOMEM; 217 goto unlock; 218 } 219 220 strncpy(cdev->cmdline, buf, count); 221 222 if (cdev->cmdline[count - 1] == '\n') 223 cdev->cmdline[count - 1] = '\0'; 224 else 225 cdev->cmdline[count] = '\0'; 226unlock: 227 mutex_unlock(&cdev->cosm_mutex); 228 return count; 229} 230static DEVICE_ATTR_RW(cmdline); 231 232static ssize_t 233firmware_show(struct device *dev, struct device_attribute *attr, char *buf) 234{ 235 struct cosm_device *cdev = dev_get_drvdata(dev); 236 char *firmware; 237 238 if (!cdev) 239 return -EINVAL; 240 241 firmware = cdev->firmware; 242 243 if (firmware) 244 return scnprintf(buf, PAGE_SIZE, "%s\n", firmware); 245 return 0; 246} 247 248static ssize_t 249firmware_store(struct device *dev, struct device_attribute *attr, 250 const char *buf, size_t count) 251{ 252 struct cosm_device *cdev = dev_get_drvdata(dev); 253 254 if (!cdev) 255 return -EINVAL; 256 257 mutex_lock(&cdev->cosm_mutex); 258 kfree(cdev->firmware); 259 260 cdev->firmware = kmalloc(count + 1, GFP_KERNEL); 261 if (!cdev->firmware) { 262 count = -ENOMEM; 263 goto unlock; 264 } 265 strncpy(cdev->firmware, buf, count); 266 267 if (cdev->firmware[count - 1] == '\n') 268 cdev->firmware[count - 1] = '\0'; 269 else 270 cdev->firmware[count] = '\0'; 271unlock: 272 mutex_unlock(&cdev->cosm_mutex); 273 return count; 274} 275static DEVICE_ATTR_RW(firmware); 276 277static ssize_t 278ramdisk_show(struct device *dev, struct device_attribute *attr, char *buf) 279{ 280 struct cosm_device *cdev = dev_get_drvdata(dev); 281 char *ramdisk; 282 283 if (!cdev) 284 return -EINVAL; 285 286 ramdisk = cdev->ramdisk; 287 288 if (ramdisk) 289 return scnprintf(buf, PAGE_SIZE, "%s\n", ramdisk); 290 return 0; 291} 292 293static ssize_t 294ramdisk_store(struct device *dev, struct device_attribute *attr, 295 const char *buf, size_t count) 296{ 297 struct cosm_device *cdev = dev_get_drvdata(dev); 298 299 if (!cdev) 300 return -EINVAL; 301 302 mutex_lock(&cdev->cosm_mutex); 303 kfree(cdev->ramdisk); 304 305 cdev->ramdisk = kmalloc(count + 1, GFP_KERNEL); 306 if (!cdev->ramdisk) { 307 count = -ENOMEM; 308 goto unlock; 309 } 310 311 strncpy(cdev->ramdisk, buf, count); 312 313 if (cdev->ramdisk[count - 1] == '\n') 314 cdev->ramdisk[count - 1] = '\0'; 315 else 316 cdev->ramdisk[count] = '\0'; 317unlock: 318 mutex_unlock(&cdev->cosm_mutex); 319 return count; 320} 321static DEVICE_ATTR_RW(ramdisk); 322 323static ssize_t 324bootmode_show(struct device *dev, struct device_attribute *attr, char *buf) 325{ 326 struct cosm_device *cdev = dev_get_drvdata(dev); 327 char *bootmode; 328 329 if (!cdev) 330 return -EINVAL; 331 332 bootmode = cdev->bootmode; 333 334 if (bootmode) 335 return scnprintf(buf, PAGE_SIZE, "%s\n", bootmode); 336 return 0; 337} 338 339static ssize_t 340bootmode_store(struct device *dev, struct device_attribute *attr, 341 const char *buf, size_t count) 342{ 343 struct cosm_device *cdev = dev_get_drvdata(dev); 344 345 if (!cdev) 346 return -EINVAL; 347 348 if (!sysfs_streq(buf, "linux") && !sysfs_streq(buf, "flash")) 349 return -EINVAL; 350 351 mutex_lock(&cdev->cosm_mutex); 352 kfree(cdev->bootmode); 353 354 cdev->bootmode = kmalloc(count + 1, GFP_KERNEL); 355 if (!cdev->bootmode) { 356 count = -ENOMEM; 357 goto unlock; 358 } 359 360 strncpy(cdev->bootmode, buf, count); 361 362 if (cdev->bootmode[count - 1] == '\n') 363 cdev->bootmode[count - 1] = '\0'; 364 else 365 cdev->bootmode[count] = '\0'; 366unlock: 367 mutex_unlock(&cdev->cosm_mutex); 368 return count; 369} 370static DEVICE_ATTR_RW(bootmode); 371 372static ssize_t 373log_buf_addr_show(struct device *dev, struct device_attribute *attr, 374 char *buf) 375{ 376 struct cosm_device *cdev = dev_get_drvdata(dev); 377 378 if (!cdev) 379 return -EINVAL; 380 381 return scnprintf(buf, PAGE_SIZE, "%p\n", cdev->log_buf_addr); 382} 383 384static ssize_t 385log_buf_addr_store(struct device *dev, struct device_attribute *attr, 386 const char *buf, size_t count) 387{ 388 struct cosm_device *cdev = dev_get_drvdata(dev); 389 int ret; 390 unsigned long addr; 391 392 if (!cdev) 393 return -EINVAL; 394 395 ret = kstrtoul(buf, 16, &addr); 396 if (ret) 397 goto exit; 398 399 cdev->log_buf_addr = (void *)addr; 400 ret = count; 401exit: 402 return ret; 403} 404static DEVICE_ATTR_RW(log_buf_addr); 405 406static ssize_t 407log_buf_len_show(struct device *dev, struct device_attribute *attr, 408 char *buf) 409{ 410 struct cosm_device *cdev = dev_get_drvdata(dev); 411 412 if (!cdev) 413 return -EINVAL; 414 415 return scnprintf(buf, PAGE_SIZE, "%p\n", cdev->log_buf_len); 416} 417 418static ssize_t 419log_buf_len_store(struct device *dev, struct device_attribute *attr, 420 const char *buf, size_t count) 421{ 422 struct cosm_device *cdev = dev_get_drvdata(dev); 423 int ret; 424 unsigned long addr; 425 426 if (!cdev) 427 return -EINVAL; 428 429 ret = kstrtoul(buf, 16, &addr); 430 if (ret) 431 goto exit; 432 433 cdev->log_buf_len = (int *)addr; 434 ret = count; 435exit: 436 return ret; 437} 438static DEVICE_ATTR_RW(log_buf_len); 439 440static struct attribute *cosm_default_attrs[] = { 441 &dev_attr_family.attr, 442 &dev_attr_stepping.attr, 443 &dev_attr_state.attr, 444 &dev_attr_shutdown_status.attr, 445 &dev_attr_heartbeat_enable.attr, 446 &dev_attr_cmdline.attr, 447 &dev_attr_firmware.attr, 448 &dev_attr_ramdisk.attr, 449 &dev_attr_bootmode.attr, 450 &dev_attr_log_buf_addr.attr, 451 &dev_attr_log_buf_len.attr, 452 453 NULL 454}; 455 456ATTRIBUTE_GROUPS(cosm_default); 457 458void cosm_sysfs_init(struct cosm_device *cdev) 459{ 460 cdev->attr_group = cosm_default_groups; 461} 462