cros_ec_sysfs.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369
  1. // SPDX-License-Identifier: GPL-2.0+
  2. // Expose the ChromeOS EC through sysfs
  3. //
  4. // Copyright (C) 2014 Google, Inc.
  5. #include <linux/ctype.h>
  6. #include <linux/delay.h>
  7. #include <linux/device.h>
  8. #include <linux/fs.h>
  9. #include <linux/kobject.h>
  10. #include <linux/mod_devicetable.h>
  11. #include <linux/module.h>
  12. #include <linux/platform_data/cros_ec_commands.h>
  13. #include <linux/platform_data/cros_ec_proto.h>
  14. #include <linux/platform_device.h>
  15. #include <linux/printk.h>
  16. #include <linux/slab.h>
  17. #include <linux/stat.h>
  18. #include <linux/types.h>
  19. #include <linux/uaccess.h>
  20. #define DRV_NAME "cros-ec-sysfs"
  21. /* Accessor functions */
  22. static ssize_t reboot_show(struct device *dev,
  23. struct device_attribute *attr, char *buf)
  24. {
  25. int count = 0;
  26. count += sysfs_emit_at(buf, count,
  27. "ro|rw|cancel|cold|disable-jump|hibernate|cold-ap-off");
  28. count += sysfs_emit_at(buf, count, " [at-shutdown]\n");
  29. return count;
  30. }
  31. static ssize_t reboot_store(struct device *dev,
  32. struct device_attribute *attr,
  33. const char *buf, size_t count)
  34. {
  35. static const struct {
  36. const char * const str;
  37. uint8_t cmd;
  38. uint8_t flags;
  39. } words[] = {
  40. {"cancel", EC_REBOOT_CANCEL, 0},
  41. {"ro", EC_REBOOT_JUMP_RO, 0},
  42. {"rw", EC_REBOOT_JUMP_RW, 0},
  43. {"cold-ap-off", EC_REBOOT_COLD_AP_OFF, 0},
  44. {"cold", EC_REBOOT_COLD, 0},
  45. {"disable-jump", EC_REBOOT_DISABLE_JUMP, 0},
  46. {"hibernate", EC_REBOOT_HIBERNATE, 0},
  47. {"at-shutdown", -1, EC_REBOOT_FLAG_ON_AP_SHUTDOWN},
  48. };
  49. struct cros_ec_command *msg;
  50. struct ec_params_reboot_ec *param;
  51. int got_cmd = 0, offset = 0;
  52. int i;
  53. int ret;
  54. struct cros_ec_dev *ec = to_cros_ec_dev(dev);
  55. msg = kmalloc(sizeof(*msg) + sizeof(*param), GFP_KERNEL);
  56. if (!msg)
  57. return -ENOMEM;
  58. param = (struct ec_params_reboot_ec *)msg->data;
  59. param->flags = 0;
  60. while (1) {
  61. /* Find word to start scanning */
  62. while (buf[offset] && isspace(buf[offset]))
  63. offset++;
  64. if (!buf[offset])
  65. break;
  66. for (i = 0; i < ARRAY_SIZE(words); i++) {
  67. if (!strncasecmp(words[i].str, buf+offset,
  68. strlen(words[i].str))) {
  69. if (words[i].flags) {
  70. param->flags |= words[i].flags;
  71. } else {
  72. param->cmd = words[i].cmd;
  73. got_cmd = 1;
  74. }
  75. break;
  76. }
  77. }
  78. /* On to the next word, if any */
  79. while (buf[offset] && !isspace(buf[offset]))
  80. offset++;
  81. }
  82. if (!got_cmd) {
  83. count = -EINVAL;
  84. goto exit;
  85. }
  86. msg->version = 0;
  87. msg->command = EC_CMD_REBOOT_EC + ec->cmd_offset;
  88. msg->outsize = sizeof(*param);
  89. msg->insize = 0;
  90. ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
  91. if (ret < 0)
  92. count = ret;
  93. exit:
  94. kfree(msg);
  95. return count;
  96. }
  97. static ssize_t version_show(struct device *dev,
  98. struct device_attribute *attr, char *buf)
  99. {
  100. static const char * const image_names[] = {"unknown", "RO", "RW"};
  101. struct ec_response_get_version *r_ver;
  102. struct ec_response_get_chip_info *r_chip;
  103. struct ec_response_board_version *r_board;
  104. struct cros_ec_command *msg;
  105. int ret;
  106. int count = 0;
  107. struct cros_ec_dev *ec = to_cros_ec_dev(dev);
  108. msg = kmalloc(sizeof(*msg) + EC_HOST_PARAM_SIZE, GFP_KERNEL);
  109. if (!msg)
  110. return -ENOMEM;
  111. /* Get versions. RW may change. */
  112. msg->version = 0;
  113. msg->command = EC_CMD_GET_VERSION + ec->cmd_offset;
  114. msg->insize = sizeof(*r_ver);
  115. msg->outsize = 0;
  116. ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
  117. if (ret < 0) {
  118. count = ret;
  119. goto exit;
  120. }
  121. r_ver = (struct ec_response_get_version *)msg->data;
  122. /* Strings should be null-terminated, but let's be sure. */
  123. r_ver->version_string_ro[sizeof(r_ver->version_string_ro) - 1] = '\0';
  124. r_ver->version_string_rw[sizeof(r_ver->version_string_rw) - 1] = '\0';
  125. count += sysfs_emit_at(buf, count, "RO version: %s\n", r_ver->version_string_ro);
  126. count += sysfs_emit_at(buf, count, "RW version: %s\n", r_ver->version_string_rw);
  127. count += sysfs_emit_at(buf, count, "Firmware copy: %s\n",
  128. (r_ver->current_image < ARRAY_SIZE(image_names) ?
  129. image_names[r_ver->current_image] : "?"));
  130. /* Get build info. */
  131. msg->command = EC_CMD_GET_BUILD_INFO + ec->cmd_offset;
  132. msg->insize = EC_HOST_PARAM_SIZE;
  133. ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
  134. if (ret < 0) {
  135. count += sysfs_emit_at(buf, count,
  136. "Build info: XFER / EC ERROR %d / %d\n",
  137. ret, msg->result);
  138. } else {
  139. msg->data[EC_HOST_PARAM_SIZE - 1] = '\0';
  140. count += sysfs_emit_at(buf, count, "Build info: %s\n", msg->data);
  141. }
  142. /* Get chip info. */
  143. msg->command = EC_CMD_GET_CHIP_INFO + ec->cmd_offset;
  144. msg->insize = sizeof(*r_chip);
  145. ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
  146. if (ret < 0) {
  147. count += sysfs_emit_at(buf, count,
  148. "Chip info: XFER / EC ERROR %d / %d\n",
  149. ret, msg->result);
  150. } else {
  151. r_chip = (struct ec_response_get_chip_info *)msg->data;
  152. r_chip->vendor[sizeof(r_chip->vendor) - 1] = '\0';
  153. r_chip->name[sizeof(r_chip->name) - 1] = '\0';
  154. r_chip->revision[sizeof(r_chip->revision) - 1] = '\0';
  155. count += sysfs_emit_at(buf, count, "Chip vendor: %s\n", r_chip->vendor);
  156. count += sysfs_emit_at(buf, count, "Chip name: %s\n", r_chip->name);
  157. count += sysfs_emit_at(buf, count, "Chip revision: %s\n", r_chip->revision);
  158. }
  159. /* Get board version */
  160. msg->command = EC_CMD_GET_BOARD_VERSION + ec->cmd_offset;
  161. msg->insize = sizeof(*r_board);
  162. ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
  163. if (ret < 0) {
  164. count += sysfs_emit_at(buf, count,
  165. "Board version: XFER / EC ERROR %d / %d\n",
  166. ret, msg->result);
  167. } else {
  168. r_board = (struct ec_response_board_version *)msg->data;
  169. count += sysfs_emit_at(buf, count,
  170. "Board version: %d\n",
  171. r_board->board_version);
  172. }
  173. exit:
  174. kfree(msg);
  175. return count;
  176. }
  177. static ssize_t flashinfo_show(struct device *dev,
  178. struct device_attribute *attr, char *buf)
  179. {
  180. struct ec_response_flash_info *resp;
  181. struct cros_ec_command *msg;
  182. int ret;
  183. struct cros_ec_dev *ec = to_cros_ec_dev(dev);
  184. msg = kmalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL);
  185. if (!msg)
  186. return -ENOMEM;
  187. /* The flash info shouldn't ever change, but ask each time anyway. */
  188. msg->version = 0;
  189. msg->command = EC_CMD_FLASH_INFO + ec->cmd_offset;
  190. msg->insize = sizeof(*resp);
  191. msg->outsize = 0;
  192. ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
  193. if (ret < 0)
  194. goto exit;
  195. resp = (struct ec_response_flash_info *)msg->data;
  196. ret = sysfs_emit(buf,
  197. "FlashSize %d\nWriteSize %d\n"
  198. "EraseSize %d\nProtectSize %d\n",
  199. resp->flash_size, resp->write_block_size,
  200. resp->erase_block_size, resp->protect_block_size);
  201. exit:
  202. kfree(msg);
  203. return ret;
  204. }
  205. /* Keyboard wake angle control */
  206. static ssize_t kb_wake_angle_show(struct device *dev,
  207. struct device_attribute *attr, char *buf)
  208. {
  209. struct cros_ec_dev *ec = to_cros_ec_dev(dev);
  210. struct ec_response_motion_sense *resp;
  211. struct ec_params_motion_sense *param;
  212. struct cros_ec_command *msg;
  213. int ret;
  214. msg = kmalloc(sizeof(*msg) + EC_HOST_PARAM_SIZE, GFP_KERNEL);
  215. if (!msg)
  216. return -ENOMEM;
  217. param = (struct ec_params_motion_sense *)msg->data;
  218. msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
  219. msg->version = 2;
  220. param->cmd = MOTIONSENSE_CMD_KB_WAKE_ANGLE;
  221. param->kb_wake_angle.data = EC_MOTION_SENSE_NO_VALUE;
  222. msg->outsize = sizeof(*param);
  223. msg->insize = sizeof(*resp);
  224. ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
  225. if (ret < 0)
  226. goto exit;
  227. resp = (struct ec_response_motion_sense *)msg->data;
  228. ret = sysfs_emit(buf, "%d\n", resp->kb_wake_angle.ret);
  229. exit:
  230. kfree(msg);
  231. return ret;
  232. }
  233. static ssize_t kb_wake_angle_store(struct device *dev,
  234. struct device_attribute *attr,
  235. const char *buf, size_t count)
  236. {
  237. struct cros_ec_dev *ec = to_cros_ec_dev(dev);
  238. struct ec_params_motion_sense *param;
  239. struct cros_ec_command *msg;
  240. u16 angle;
  241. int ret;
  242. ret = kstrtou16(buf, 0, &angle);
  243. if (ret)
  244. return ret;
  245. msg = kmalloc(sizeof(*msg) + EC_HOST_PARAM_SIZE, GFP_KERNEL);
  246. if (!msg)
  247. return -ENOMEM;
  248. param = (struct ec_params_motion_sense *)msg->data;
  249. msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
  250. msg->version = 2;
  251. param->cmd = MOTIONSENSE_CMD_KB_WAKE_ANGLE;
  252. param->kb_wake_angle.data = angle;
  253. msg->outsize = sizeof(*param);
  254. msg->insize = sizeof(struct ec_response_motion_sense);
  255. ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
  256. kfree(msg);
  257. if (ret < 0)
  258. return ret;
  259. return count;
  260. }
  261. /* Module initialization */
  262. static DEVICE_ATTR_RW(reboot);
  263. static DEVICE_ATTR_RO(version);
  264. static DEVICE_ATTR_RO(flashinfo);
  265. static DEVICE_ATTR_RW(kb_wake_angle);
  266. static struct attribute *__ec_attrs[] = {
  267. &dev_attr_kb_wake_angle.attr,
  268. &dev_attr_reboot.attr,
  269. &dev_attr_version.attr,
  270. &dev_attr_flashinfo.attr,
  271. NULL,
  272. };
  273. static umode_t cros_ec_ctrl_visible(struct kobject *kobj,
  274. struct attribute *a, int n)
  275. {
  276. struct device *dev = kobj_to_dev(kobj);
  277. struct cros_ec_dev *ec = to_cros_ec_dev(dev);
  278. if (a == &dev_attr_kb_wake_angle.attr && !ec->has_kb_wake_angle)
  279. return 0;
  280. return a->mode;
  281. }
  282. static const struct attribute_group cros_ec_attr_group = {
  283. .attrs = __ec_attrs,
  284. .is_visible = cros_ec_ctrl_visible,
  285. };
  286. static int cros_ec_sysfs_probe(struct platform_device *pd)
  287. {
  288. struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent);
  289. struct device *dev = &pd->dev;
  290. int ret;
  291. ret = sysfs_create_group(&ec_dev->class_dev.kobj, &cros_ec_attr_group);
  292. if (ret < 0)
  293. dev_err(dev, "failed to create attributes. err=%d\n", ret);
  294. return ret;
  295. }
  296. static void cros_ec_sysfs_remove(struct platform_device *pd)
  297. {
  298. struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent);
  299. sysfs_remove_group(&ec_dev->class_dev.kobj, &cros_ec_attr_group);
  300. }
  301. static const struct platform_device_id cros_ec_sysfs_id[] = {
  302. { DRV_NAME, 0 },
  303. {}
  304. };
  305. MODULE_DEVICE_TABLE(platform, cros_ec_sysfs_id);
  306. static struct platform_driver cros_ec_sysfs_driver = {
  307. .driver = {
  308. .name = DRV_NAME,
  309. },
  310. .probe = cros_ec_sysfs_probe,
  311. .remove_new = cros_ec_sysfs_remove,
  312. .id_table = cros_ec_sysfs_id,
  313. };
  314. module_platform_driver(cros_ec_sysfs_driver);
  315. MODULE_LICENSE("GPL");
  316. MODULE_DESCRIPTION("Expose the ChromeOS EC through sysfs");