1 /* 2 * cros_ec_sysfs - expose the Chrome OS EC through sysfs 3 * 4 * Copyright (C) 2014 Google, Inc. 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 as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #define pr_fmt(fmt) "cros_ec_sysfs: " fmt 21 22 #include <linux/ctype.h> 23 #include <linux/delay.h> 24 #include <linux/device.h> 25 #include <linux/fs.h> 26 #include <linux/kobject.h> 27 #include <linux/mfd/cros_ec.h> 28 #include <linux/mfd/cros_ec_commands.h> 29 #include <linux/module.h> 30 #include <linux/platform_device.h> 31 #include <linux/printk.h> 32 #include <linux/slab.h> 33 #include <linux/stat.h> 34 #include <linux/types.h> 35 #include <linux/uaccess.h> 36 37 /* Accessor functions */ 38 39 static ssize_t reboot_show(struct device *dev, 40 struct device_attribute *attr, char *buf) 41 { 42 int count = 0; 43 44 count += scnprintf(buf + count, PAGE_SIZE - count, 45 "ro|rw|cancel|cold|disable-jump|hibernate"); 46 count += scnprintf(buf + count, PAGE_SIZE - count, 47 " [at-shutdown]\n"); 48 return count; 49 } 50 51 static ssize_t reboot_store(struct device *dev, 52 struct device_attribute *attr, 53 const char *buf, size_t count) 54 { 55 static const struct { 56 const char * const str; 57 uint8_t cmd; 58 uint8_t flags; 59 } words[] = { 60 {"cancel", EC_REBOOT_CANCEL, 0}, 61 {"ro", EC_REBOOT_JUMP_RO, 0}, 62 {"rw", EC_REBOOT_JUMP_RW, 0}, 63 {"cold", EC_REBOOT_COLD, 0}, 64 {"disable-jump", EC_REBOOT_DISABLE_JUMP, 0}, 65 {"hibernate", EC_REBOOT_HIBERNATE, 0}, 66 {"at-shutdown", -1, EC_REBOOT_FLAG_ON_AP_SHUTDOWN}, 67 }; 68 struct cros_ec_command *msg; 69 struct ec_params_reboot_ec *param; 70 int got_cmd = 0, offset = 0; 71 int i; 72 int ret; 73 struct cros_ec_dev *ec = to_cros_ec_dev(dev); 74 75 msg = kmalloc(sizeof(*msg) + sizeof(*param), GFP_KERNEL); 76 if (!msg) 77 return -ENOMEM; 78 79 param = (struct ec_params_reboot_ec *)msg->data; 80 81 param->flags = 0; 82 while (1) { 83 /* Find word to start scanning */ 84 while (buf[offset] && isspace(buf[offset])) 85 offset++; 86 if (!buf[offset]) 87 break; 88 89 for (i = 0; i < ARRAY_SIZE(words); i++) { 90 if (!strncasecmp(words[i].str, buf+offset, 91 strlen(words[i].str))) { 92 if (words[i].flags) { 93 param->flags |= words[i].flags; 94 } else { 95 param->cmd = words[i].cmd; 96 got_cmd = 1; 97 } 98 break; 99 } 100 } 101 102 /* On to the next word, if any */ 103 while (buf[offset] && !isspace(buf[offset])) 104 offset++; 105 } 106 107 if (!got_cmd) { 108 count = -EINVAL; 109 goto exit; 110 } 111 112 msg->version = 0; 113 msg->command = EC_CMD_REBOOT_EC + ec->cmd_offset; 114 msg->outsize = sizeof(*param); 115 msg->insize = 0; 116 ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); 117 if (ret < 0) 118 count = ret; 119 exit: 120 kfree(msg); 121 return count; 122 } 123 124 static ssize_t version_show(struct device *dev, 125 struct device_attribute *attr, char *buf) 126 { 127 static const char * const image_names[] = {"unknown", "RO", "RW"}; 128 struct ec_response_get_version *r_ver; 129 struct ec_response_get_chip_info *r_chip; 130 struct ec_response_board_version *r_board; 131 struct cros_ec_command *msg; 132 int ret; 133 int count = 0; 134 struct cros_ec_dev *ec = to_cros_ec_dev(dev); 135 136 msg = kmalloc(sizeof(*msg) + EC_HOST_PARAM_SIZE, GFP_KERNEL); 137 if (!msg) 138 return -ENOMEM; 139 140 /* Get versions. RW may change. */ 141 msg->version = 0; 142 msg->command = EC_CMD_GET_VERSION + ec->cmd_offset; 143 msg->insize = sizeof(*r_ver); 144 msg->outsize = 0; 145 ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); 146 if (ret < 0) { 147 count = ret; 148 goto exit; 149 } 150 r_ver = (struct ec_response_get_version *)msg->data; 151 /* Strings should be null-terminated, but let's be sure. */ 152 r_ver->version_string_ro[sizeof(r_ver->version_string_ro) - 1] = '\0'; 153 r_ver->version_string_rw[sizeof(r_ver->version_string_rw) - 1] = '\0'; 154 count += scnprintf(buf + count, PAGE_SIZE - count, 155 "RO version: %s\n", r_ver->version_string_ro); 156 count += scnprintf(buf + count, PAGE_SIZE - count, 157 "RW version: %s\n", r_ver->version_string_rw); 158 count += scnprintf(buf + count, PAGE_SIZE - count, 159 "Firmware copy: %s\n", 160 (r_ver->current_image < ARRAY_SIZE(image_names) ? 161 image_names[r_ver->current_image] : "?")); 162 163 /* Get build info. */ 164 msg->command = EC_CMD_GET_BUILD_INFO + ec->cmd_offset; 165 msg->insize = EC_HOST_PARAM_SIZE; 166 ret = cros_ec_cmd_xfer(ec->ec_dev, msg); 167 if (ret < 0) 168 count += scnprintf(buf + count, PAGE_SIZE - count, 169 "Build info: XFER ERROR %d\n", ret); 170 else if (msg->result != EC_RES_SUCCESS) 171 count += scnprintf(buf + count, PAGE_SIZE - count, 172 "Build info: EC error %d\n", msg->result); 173 else { 174 msg->data[EC_HOST_PARAM_SIZE - 1] = '\0'; 175 count += scnprintf(buf + count, PAGE_SIZE - count, 176 "Build info: %s\n", msg->data); 177 } 178 179 /* Get chip info. */ 180 msg->command = EC_CMD_GET_CHIP_INFO + ec->cmd_offset; 181 msg->insize = sizeof(*r_chip); 182 ret = cros_ec_cmd_xfer(ec->ec_dev, msg); 183 if (ret < 0) 184 count += scnprintf(buf + count, PAGE_SIZE - count, 185 "Chip info: XFER ERROR %d\n", ret); 186 else if (msg->result != EC_RES_SUCCESS) 187 count += scnprintf(buf + count, PAGE_SIZE - count, 188 "Chip info: EC error %d\n", msg->result); 189 else { 190 r_chip = (struct ec_response_get_chip_info *)msg->data; 191 192 r_chip->vendor[sizeof(r_chip->vendor) - 1] = '\0'; 193 r_chip->name[sizeof(r_chip->name) - 1] = '\0'; 194 r_chip->revision[sizeof(r_chip->revision) - 1] = '\0'; 195 count += scnprintf(buf + count, PAGE_SIZE - count, 196 "Chip vendor: %s\n", r_chip->vendor); 197 count += scnprintf(buf + count, PAGE_SIZE - count, 198 "Chip name: %s\n", r_chip->name); 199 count += scnprintf(buf + count, PAGE_SIZE - count, 200 "Chip revision: %s\n", r_chip->revision); 201 } 202 203 /* Get board version */ 204 msg->command = EC_CMD_GET_BOARD_VERSION + ec->cmd_offset; 205 msg->insize = sizeof(*r_board); 206 ret = cros_ec_cmd_xfer(ec->ec_dev, msg); 207 if (ret < 0) 208 count += scnprintf(buf + count, PAGE_SIZE - count, 209 "Board version: XFER ERROR %d\n", ret); 210 else if (msg->result != EC_RES_SUCCESS) 211 count += scnprintf(buf + count, PAGE_SIZE - count, 212 "Board version: EC error %d\n", msg->result); 213 else { 214 r_board = (struct ec_response_board_version *)msg->data; 215 216 count += scnprintf(buf + count, PAGE_SIZE - count, 217 "Board version: %d\n", 218 r_board->board_version); 219 } 220 221 exit: 222 kfree(msg); 223 return count; 224 } 225 226 static ssize_t flashinfo_show(struct device *dev, 227 struct device_attribute *attr, char *buf) 228 { 229 struct ec_response_flash_info *resp; 230 struct cros_ec_command *msg; 231 int ret; 232 struct cros_ec_dev *ec = to_cros_ec_dev(dev); 233 234 msg = kmalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL); 235 if (!msg) 236 return -ENOMEM; 237 238 /* The flash info shouldn't ever change, but ask each time anyway. */ 239 msg->version = 0; 240 msg->command = EC_CMD_FLASH_INFO + ec->cmd_offset; 241 msg->insize = sizeof(*resp); 242 msg->outsize = 0; 243 ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); 244 if (ret < 0) 245 goto exit; 246 247 resp = (struct ec_response_flash_info *)msg->data; 248 249 ret = scnprintf(buf, PAGE_SIZE, 250 "FlashSize %d\nWriteSize %d\n" 251 "EraseSize %d\nProtectSize %d\n", 252 resp->flash_size, resp->write_block_size, 253 resp->erase_block_size, resp->protect_block_size); 254 exit: 255 kfree(msg); 256 return ret; 257 } 258 259 /* Keyboard wake angle control */ 260 static ssize_t kb_wake_angle_show(struct device *dev, 261 struct device_attribute *attr, char *buf) 262 { 263 struct cros_ec_dev *ec = to_cros_ec_dev(dev); 264 struct ec_response_motion_sense *resp; 265 struct ec_params_motion_sense *param; 266 struct cros_ec_command *msg; 267 int ret; 268 269 msg = kmalloc(sizeof(*msg) + EC_HOST_PARAM_SIZE, GFP_KERNEL); 270 if (!msg) 271 return -ENOMEM; 272 273 param = (struct ec_params_motion_sense *)msg->data; 274 msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset; 275 msg->version = 2; 276 param->cmd = MOTIONSENSE_CMD_KB_WAKE_ANGLE; 277 param->kb_wake_angle.data = EC_MOTION_SENSE_NO_VALUE; 278 msg->outsize = sizeof(*param); 279 msg->insize = sizeof(*resp); 280 281 ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); 282 if (ret < 0) 283 goto exit; 284 285 resp = (struct ec_response_motion_sense *)msg->data; 286 ret = scnprintf(buf, PAGE_SIZE, "%d\n", resp->kb_wake_angle.ret); 287 exit: 288 kfree(msg); 289 return ret; 290 } 291 292 static ssize_t kb_wake_angle_store(struct device *dev, 293 struct device_attribute *attr, 294 const char *buf, size_t count) 295 { 296 struct cros_ec_dev *ec = to_cros_ec_dev(dev); 297 struct ec_params_motion_sense *param; 298 struct cros_ec_command *msg; 299 u16 angle; 300 int ret; 301 302 ret = kstrtou16(buf, 0, &angle); 303 if (ret) 304 return ret; 305 306 msg = kmalloc(sizeof(*msg) + EC_HOST_PARAM_SIZE, GFP_KERNEL); 307 if (!msg) 308 return -ENOMEM; 309 310 param = (struct ec_params_motion_sense *)msg->data; 311 msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset; 312 msg->version = 2; 313 param->cmd = MOTIONSENSE_CMD_KB_WAKE_ANGLE; 314 param->kb_wake_angle.data = angle; 315 msg->outsize = sizeof(*param); 316 msg->insize = sizeof(struct ec_response_motion_sense); 317 318 ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg); 319 kfree(msg); 320 if (ret < 0) 321 return ret; 322 return count; 323 } 324 325 /* Module initialization */ 326 327 static DEVICE_ATTR_RW(reboot); 328 static DEVICE_ATTR_RO(version); 329 static DEVICE_ATTR_RO(flashinfo); 330 static DEVICE_ATTR_RW(kb_wake_angle); 331 332 static struct attribute *__ec_attrs[] = { 333 &dev_attr_kb_wake_angle.attr, 334 &dev_attr_reboot.attr, 335 &dev_attr_version.attr, 336 &dev_attr_flashinfo.attr, 337 NULL, 338 }; 339 340 static umode_t cros_ec_ctrl_visible(struct kobject *kobj, 341 struct attribute *a, int n) 342 { 343 struct device *dev = container_of(kobj, struct device, kobj); 344 struct cros_ec_dev *ec = to_cros_ec_dev(dev); 345 346 if (a == &dev_attr_kb_wake_angle.attr && !ec->has_kb_wake_angle) 347 return 0; 348 349 return a->mode; 350 } 351 352 struct attribute_group cros_ec_attr_group = { 353 .attrs = __ec_attrs, 354 .is_visible = cros_ec_ctrl_visible, 355 }; 356 EXPORT_SYMBOL(cros_ec_attr_group); 357 358 MODULE_LICENSE("GPL"); 359 MODULE_DESCRIPTION("ChromeOS EC control driver"); 360