root/drivers/platform/chrome/cros_ec_sysfs.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. reboot_show
  2. reboot_store
  3. version_show
  4. flashinfo_show
  5. kb_wake_angle_show
  6. kb_wake_angle_store
  7. cros_ec_ctrl_visible
  8. cros_ec_sysfs_probe
  9. cros_ec_sysfs_remove

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

/* [<][>][^][v][top][bottom][index][help] */