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 show_ec_reboot(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 store_ec_reboot(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 = container_of(dev, 74 struct cros_ec_dev, class_dev); 75 76 msg = kmalloc(sizeof(*msg) + sizeof(*param), GFP_KERNEL); 77 if (!msg) 78 return -ENOMEM; 79 80 param = (struct ec_params_reboot_ec *)msg->data; 81 82 param->flags = 0; 83 while (1) { 84 /* Find word to start scanning */ 85 while (buf[offset] && isspace(buf[offset])) 86 offset++; 87 if (!buf[offset]) 88 break; 89 90 for (i = 0; i < ARRAY_SIZE(words); i++) { 91 if (!strncasecmp(words[i].str, buf+offset, 92 strlen(words[i].str))) { 93 if (words[i].flags) { 94 param->flags |= words[i].flags; 95 } else { 96 param->cmd = words[i].cmd; 97 got_cmd = 1; 98 } 99 break; 100 } 101 } 102 103 /* On to the next word, if any */ 104 while (buf[offset] && !isspace(buf[offset])) 105 offset++; 106 } 107 108 if (!got_cmd) { 109 count = -EINVAL; 110 goto exit; 111 } 112 113 msg->version = 0; 114 msg->command = EC_CMD_REBOOT_EC + ec->cmd_offset; 115 msg->outsize = sizeof(*param); 116 msg->insize = 0; 117 ret = cros_ec_cmd_xfer(ec->ec_dev, msg); 118 if (ret < 0) { 119 count = ret; 120 goto exit; 121 } 122 if (msg->result != EC_RES_SUCCESS) { 123 dev_dbg(ec->dev, "EC result %d\n", msg->result); 124 count = -EINVAL; 125 } 126 exit: 127 kfree(msg); 128 return count; 129 } 130 131 static ssize_t show_ec_version(struct device *dev, 132 struct device_attribute *attr, char *buf) 133 { 134 static const char * const image_names[] = {"unknown", "RO", "RW"}; 135 struct ec_response_get_version *r_ver; 136 struct ec_response_get_chip_info *r_chip; 137 struct ec_response_board_version *r_board; 138 struct cros_ec_command *msg; 139 int ret; 140 int count = 0; 141 struct cros_ec_dev *ec = container_of(dev, 142 struct cros_ec_dev, class_dev); 143 144 msg = kmalloc(sizeof(*msg) + EC_HOST_PARAM_SIZE, GFP_KERNEL); 145 if (!msg) 146 return -ENOMEM; 147 148 /* Get versions. RW may change. */ 149 msg->version = 0; 150 msg->command = EC_CMD_GET_VERSION + ec->cmd_offset; 151 msg->insize = sizeof(*r_ver); 152 msg->outsize = 0; 153 ret = cros_ec_cmd_xfer(ec->ec_dev, msg); 154 if (ret < 0) { 155 count = ret; 156 goto exit; 157 } 158 if (msg->result != EC_RES_SUCCESS) { 159 count = scnprintf(buf, PAGE_SIZE, 160 "ERROR: EC returned %d\n", msg->result); 161 goto exit; 162 } 163 164 r_ver = (struct ec_response_get_version *)msg->data; 165 /* Strings should be null-terminated, but let's be sure. */ 166 r_ver->version_string_ro[sizeof(r_ver->version_string_ro) - 1] = '\0'; 167 r_ver->version_string_rw[sizeof(r_ver->version_string_rw) - 1] = '\0'; 168 count += scnprintf(buf + count, PAGE_SIZE - count, 169 "RO version: %s\n", r_ver->version_string_ro); 170 count += scnprintf(buf + count, PAGE_SIZE - count, 171 "RW version: %s\n", r_ver->version_string_rw); 172 count += scnprintf(buf + count, PAGE_SIZE - count, 173 "Firmware copy: %s\n", 174 (r_ver->current_image < ARRAY_SIZE(image_names) ? 175 image_names[r_ver->current_image] : "?")); 176 177 /* Get build info. */ 178 msg->command = EC_CMD_GET_BUILD_INFO + ec->cmd_offset; 179 msg->insize = EC_HOST_PARAM_SIZE; 180 ret = cros_ec_cmd_xfer(ec->ec_dev, msg); 181 if (ret < 0) 182 count += scnprintf(buf + count, PAGE_SIZE - count, 183 "Build info: XFER ERROR %d\n", ret); 184 else if (msg->result != EC_RES_SUCCESS) 185 count += scnprintf(buf + count, PAGE_SIZE - count, 186 "Build info: EC error %d\n", msg->result); 187 else { 188 msg->data[EC_HOST_PARAM_SIZE - 1] = '\0'; 189 count += scnprintf(buf + count, PAGE_SIZE - count, 190 "Build info: %s\n", msg->data); 191 } 192 193 /* Get chip info. */ 194 msg->command = EC_CMD_GET_CHIP_INFO + ec->cmd_offset; 195 msg->insize = sizeof(*r_chip); 196 ret = cros_ec_cmd_xfer(ec->ec_dev, msg); 197 if (ret < 0) 198 count += scnprintf(buf + count, PAGE_SIZE - count, 199 "Chip info: XFER ERROR %d\n", ret); 200 else if (msg->result != EC_RES_SUCCESS) 201 count += scnprintf(buf + count, PAGE_SIZE - count, 202 "Chip info: EC error %d\n", msg->result); 203 else { 204 r_chip = (struct ec_response_get_chip_info *)msg->data; 205 206 r_chip->vendor[sizeof(r_chip->vendor) - 1] = '\0'; 207 r_chip->name[sizeof(r_chip->name) - 1] = '\0'; 208 r_chip->revision[sizeof(r_chip->revision) - 1] = '\0'; 209 count += scnprintf(buf + count, PAGE_SIZE - count, 210 "Chip vendor: %s\n", r_chip->vendor); 211 count += scnprintf(buf + count, PAGE_SIZE - count, 212 "Chip name: %s\n", r_chip->name); 213 count += scnprintf(buf + count, PAGE_SIZE - count, 214 "Chip revision: %s\n", r_chip->revision); 215 } 216 217 /* Get board version */ 218 msg->command = EC_CMD_GET_BOARD_VERSION + ec->cmd_offset; 219 msg->insize = sizeof(*r_board); 220 ret = cros_ec_cmd_xfer(ec->ec_dev, msg); 221 if (ret < 0) 222 count += scnprintf(buf + count, PAGE_SIZE - count, 223 "Board version: XFER ERROR %d\n", ret); 224 else if (msg->result != EC_RES_SUCCESS) 225 count += scnprintf(buf + count, PAGE_SIZE - count, 226 "Board version: EC error %d\n", msg->result); 227 else { 228 r_board = (struct ec_response_board_version *)msg->data; 229 230 count += scnprintf(buf + count, PAGE_SIZE - count, 231 "Board version: %d\n", 232 r_board->board_version); 233 } 234 235 exit: 236 kfree(msg); 237 return count; 238 } 239 240 static ssize_t show_ec_flashinfo(struct device *dev, 241 struct device_attribute *attr, char *buf) 242 { 243 struct ec_response_flash_info *resp; 244 struct cros_ec_command *msg; 245 int ret; 246 struct cros_ec_dev *ec = container_of(dev, 247 struct cros_ec_dev, class_dev); 248 249 msg = kmalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL); 250 if (!msg) 251 return -ENOMEM; 252 253 /* The flash info shouldn't ever change, but ask each time anyway. */ 254 msg->version = 0; 255 msg->command = EC_CMD_FLASH_INFO + ec->cmd_offset; 256 msg->insize = sizeof(*resp); 257 msg->outsize = 0; 258 ret = cros_ec_cmd_xfer(ec->ec_dev, msg); 259 if (ret < 0) 260 goto exit; 261 if (msg->result != EC_RES_SUCCESS) { 262 ret = scnprintf(buf, PAGE_SIZE, 263 "ERROR: EC returned %d\n", msg->result); 264 goto exit; 265 } 266 267 resp = (struct ec_response_flash_info *)msg->data; 268 269 ret = scnprintf(buf, PAGE_SIZE, 270 "FlashSize %d\nWriteSize %d\n" 271 "EraseSize %d\nProtectSize %d\n", 272 resp->flash_size, resp->write_block_size, 273 resp->erase_block_size, resp->protect_block_size); 274 exit: 275 kfree(msg); 276 return ret; 277 } 278 279 /* Module initialization */ 280 281 static DEVICE_ATTR(reboot, S_IWUSR | S_IRUGO, show_ec_reboot, store_ec_reboot); 282 static DEVICE_ATTR(version, S_IRUGO, show_ec_version, NULL); 283 static DEVICE_ATTR(flashinfo, S_IRUGO, show_ec_flashinfo, NULL); 284 285 static struct attribute *__ec_attrs[] = { 286 &dev_attr_reboot.attr, 287 &dev_attr_version.attr, 288 &dev_attr_flashinfo.attr, 289 NULL, 290 }; 291 292 struct attribute_group cros_ec_attr_group = { 293 .attrs = __ec_attrs, 294 }; 295 EXPORT_SYMBOL(cros_ec_attr_group); 296 297 MODULE_LICENSE("GPL"); 298 MODULE_DESCRIPTION("ChromeOS EC control driver"); 299