Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0+
0002 // Expose the ChromeOS EC through sysfs
0003 //
0004 // Copyright (C) 2014 Google, Inc.
0005 
0006 #include <linux/ctype.h>
0007 #include <linux/delay.h>
0008 #include <linux/device.h>
0009 #include <linux/fs.h>
0010 #include <linux/kobject.h>
0011 #include <linux/module.h>
0012 #include <linux/platform_data/cros_ec_commands.h>
0013 #include <linux/platform_data/cros_ec_proto.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/printk.h>
0016 #include <linux/slab.h>
0017 #include <linux/stat.h>
0018 #include <linux/types.h>
0019 #include <linux/uaccess.h>
0020 
0021 #define DRV_NAME "cros-ec-sysfs"
0022 
0023 /* Accessor functions */
0024 
0025 static ssize_t reboot_show(struct device *dev,
0026                struct device_attribute *attr, char *buf)
0027 {
0028     int count = 0;
0029 
0030     count += scnprintf(buf + count, PAGE_SIZE - count,
0031                "ro|rw|cancel|cold|disable-jump|hibernate|cold-ap-off");
0032     count += scnprintf(buf + count, PAGE_SIZE - count,
0033                " [at-shutdown]\n");
0034     return count;
0035 }
0036 
0037 static ssize_t reboot_store(struct device *dev,
0038                 struct device_attribute *attr,
0039                 const char *buf, size_t count)
0040 {
0041     static const struct {
0042         const char * const str;
0043         uint8_t cmd;
0044         uint8_t flags;
0045     } words[] = {
0046         {"cancel",       EC_REBOOT_CANCEL, 0},
0047         {"ro",           EC_REBOOT_JUMP_RO, 0},
0048         {"rw",           EC_REBOOT_JUMP_RW, 0},
0049         {"cold-ap-off",  EC_REBOOT_COLD_AP_OFF, 0},
0050         {"cold",         EC_REBOOT_COLD, 0},
0051         {"disable-jump", EC_REBOOT_DISABLE_JUMP, 0},
0052         {"hibernate",    EC_REBOOT_HIBERNATE, 0},
0053         {"at-shutdown",  -1, EC_REBOOT_FLAG_ON_AP_SHUTDOWN},
0054     };
0055     struct cros_ec_command *msg;
0056     struct ec_params_reboot_ec *param;
0057     int got_cmd = 0, offset = 0;
0058     int i;
0059     int ret;
0060     struct cros_ec_dev *ec = to_cros_ec_dev(dev);
0061 
0062     msg = kmalloc(sizeof(*msg) + sizeof(*param), GFP_KERNEL);
0063     if (!msg)
0064         return -ENOMEM;
0065 
0066     param = (struct ec_params_reboot_ec *)msg->data;
0067 
0068     param->flags = 0;
0069     while (1) {
0070         /* Find word to start scanning */
0071         while (buf[offset] && isspace(buf[offset]))
0072             offset++;
0073         if (!buf[offset])
0074             break;
0075 
0076         for (i = 0; i < ARRAY_SIZE(words); i++) {
0077             if (!strncasecmp(words[i].str, buf+offset,
0078                      strlen(words[i].str))) {
0079                 if (words[i].flags) {
0080                     param->flags |= words[i].flags;
0081                 } else {
0082                     param->cmd = words[i].cmd;
0083                     got_cmd = 1;
0084                 }
0085                 break;
0086             }
0087         }
0088 
0089         /* On to the next word, if any */
0090         while (buf[offset] && !isspace(buf[offset]))
0091             offset++;
0092     }
0093 
0094     if (!got_cmd) {
0095         count = -EINVAL;
0096         goto exit;
0097     }
0098 
0099     msg->version = 0;
0100     msg->command = EC_CMD_REBOOT_EC + ec->cmd_offset;
0101     msg->outsize = sizeof(*param);
0102     msg->insize = 0;
0103     ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
0104     if (ret < 0)
0105         count = ret;
0106 exit:
0107     kfree(msg);
0108     return count;
0109 }
0110 
0111 static ssize_t version_show(struct device *dev,
0112                 struct device_attribute *attr, char *buf)
0113 {
0114     static const char * const image_names[] = {"unknown", "RO", "RW"};
0115     struct ec_response_get_version *r_ver;
0116     struct ec_response_get_chip_info *r_chip;
0117     struct ec_response_board_version *r_board;
0118     struct cros_ec_command *msg;
0119     int ret;
0120     int count = 0;
0121     struct cros_ec_dev *ec = to_cros_ec_dev(dev);
0122 
0123     msg = kmalloc(sizeof(*msg) + EC_HOST_PARAM_SIZE, GFP_KERNEL);
0124     if (!msg)
0125         return -ENOMEM;
0126 
0127     /* Get versions. RW may change. */
0128     msg->version = 0;
0129     msg->command = EC_CMD_GET_VERSION + ec->cmd_offset;
0130     msg->insize = sizeof(*r_ver);
0131     msg->outsize = 0;
0132     ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
0133     if (ret < 0) {
0134         count = ret;
0135         goto exit;
0136     }
0137     r_ver = (struct ec_response_get_version *)msg->data;
0138     /* Strings should be null-terminated, but let's be sure. */
0139     r_ver->version_string_ro[sizeof(r_ver->version_string_ro) - 1] = '\0';
0140     r_ver->version_string_rw[sizeof(r_ver->version_string_rw) - 1] = '\0';
0141     count += scnprintf(buf + count, PAGE_SIZE - count,
0142                "RO version:    %s\n", r_ver->version_string_ro);
0143     count += scnprintf(buf + count, PAGE_SIZE - count,
0144                "RW version:    %s\n", r_ver->version_string_rw);
0145     count += scnprintf(buf + count, PAGE_SIZE - count,
0146                "Firmware copy: %s\n",
0147                (r_ver->current_image < ARRAY_SIZE(image_names) ?
0148                 image_names[r_ver->current_image] : "?"));
0149 
0150     /* Get build info. */
0151     msg->command = EC_CMD_GET_BUILD_INFO + ec->cmd_offset;
0152     msg->insize = EC_HOST_PARAM_SIZE;
0153     ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
0154     if (ret < 0) {
0155         count += scnprintf(buf + count, PAGE_SIZE - count,
0156                    "Build info:    XFER / EC ERROR %d / %d\n",
0157                    ret, msg->result);
0158     } else {
0159         msg->data[EC_HOST_PARAM_SIZE - 1] = '\0';
0160         count += scnprintf(buf + count, PAGE_SIZE - count,
0161                    "Build info:    %s\n", msg->data);
0162     }
0163 
0164     /* Get chip info. */
0165     msg->command = EC_CMD_GET_CHIP_INFO + ec->cmd_offset;
0166     msg->insize = sizeof(*r_chip);
0167     ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
0168     if (ret < 0) {
0169         count += scnprintf(buf + count, PAGE_SIZE - count,
0170                    "Chip info:     XFER / EC ERROR %d / %d\n",
0171                    ret, msg->result);
0172     } else {
0173         r_chip = (struct ec_response_get_chip_info *)msg->data;
0174 
0175         r_chip->vendor[sizeof(r_chip->vendor) - 1] = '\0';
0176         r_chip->name[sizeof(r_chip->name) - 1] = '\0';
0177         r_chip->revision[sizeof(r_chip->revision) - 1] = '\0';
0178         count += scnprintf(buf + count, PAGE_SIZE - count,
0179                    "Chip vendor:   %s\n", r_chip->vendor);
0180         count += scnprintf(buf + count, PAGE_SIZE - count,
0181                    "Chip name:     %s\n", r_chip->name);
0182         count += scnprintf(buf + count, PAGE_SIZE - count,
0183                    "Chip revision: %s\n", r_chip->revision);
0184     }
0185 
0186     /* Get board version */
0187     msg->command = EC_CMD_GET_BOARD_VERSION + ec->cmd_offset;
0188     msg->insize = sizeof(*r_board);
0189     ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
0190     if (ret < 0) {
0191         count += scnprintf(buf + count, PAGE_SIZE - count,
0192                    "Board version: XFER / EC ERROR %d / %d\n",
0193                    ret, msg->result);
0194     } else {
0195         r_board = (struct ec_response_board_version *)msg->data;
0196 
0197         count += scnprintf(buf + count, PAGE_SIZE - count,
0198                    "Board version: %d\n",
0199                    r_board->board_version);
0200     }
0201 
0202 exit:
0203     kfree(msg);
0204     return count;
0205 }
0206 
0207 static ssize_t flashinfo_show(struct device *dev,
0208                   struct device_attribute *attr, char *buf)
0209 {
0210     struct ec_response_flash_info *resp;
0211     struct cros_ec_command *msg;
0212     int ret;
0213     struct cros_ec_dev *ec = to_cros_ec_dev(dev);
0214 
0215     msg = kmalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL);
0216     if (!msg)
0217         return -ENOMEM;
0218 
0219     /* The flash info shouldn't ever change, but ask each time anyway. */
0220     msg->version = 0;
0221     msg->command = EC_CMD_FLASH_INFO + ec->cmd_offset;
0222     msg->insize = sizeof(*resp);
0223     msg->outsize = 0;
0224     ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
0225     if (ret < 0)
0226         goto exit;
0227 
0228     resp = (struct ec_response_flash_info *)msg->data;
0229 
0230     ret = scnprintf(buf, PAGE_SIZE,
0231             "FlashSize %d\nWriteSize %d\n"
0232             "EraseSize %d\nProtectSize %d\n",
0233             resp->flash_size, resp->write_block_size,
0234             resp->erase_block_size, resp->protect_block_size);
0235 exit:
0236     kfree(msg);
0237     return ret;
0238 }
0239 
0240 /* Keyboard wake angle control */
0241 static ssize_t kb_wake_angle_show(struct device *dev,
0242                   struct device_attribute *attr, char *buf)
0243 {
0244     struct cros_ec_dev *ec = to_cros_ec_dev(dev);
0245     struct ec_response_motion_sense *resp;
0246     struct ec_params_motion_sense *param;
0247     struct cros_ec_command *msg;
0248     int ret;
0249 
0250     msg = kmalloc(sizeof(*msg) + EC_HOST_PARAM_SIZE, GFP_KERNEL);
0251     if (!msg)
0252         return -ENOMEM;
0253 
0254     param = (struct ec_params_motion_sense *)msg->data;
0255     msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
0256     msg->version = 2;
0257     param->cmd = MOTIONSENSE_CMD_KB_WAKE_ANGLE;
0258     param->kb_wake_angle.data = EC_MOTION_SENSE_NO_VALUE;
0259     msg->outsize = sizeof(*param);
0260     msg->insize = sizeof(*resp);
0261 
0262     ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
0263     if (ret < 0)
0264         goto exit;
0265 
0266     resp = (struct ec_response_motion_sense *)msg->data;
0267     ret = scnprintf(buf, PAGE_SIZE, "%d\n", resp->kb_wake_angle.ret);
0268 exit:
0269     kfree(msg);
0270     return ret;
0271 }
0272 
0273 static ssize_t kb_wake_angle_store(struct device *dev,
0274                    struct device_attribute *attr,
0275                    const char *buf, size_t count)
0276 {
0277     struct cros_ec_dev *ec = to_cros_ec_dev(dev);
0278     struct ec_params_motion_sense *param;
0279     struct cros_ec_command *msg;
0280     u16 angle;
0281     int ret;
0282 
0283     ret = kstrtou16(buf, 0, &angle);
0284     if (ret)
0285         return ret;
0286 
0287     msg = kmalloc(sizeof(*msg) + EC_HOST_PARAM_SIZE, GFP_KERNEL);
0288     if (!msg)
0289         return -ENOMEM;
0290 
0291     param = (struct ec_params_motion_sense *)msg->data;
0292     msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
0293     msg->version = 2;
0294     param->cmd = MOTIONSENSE_CMD_KB_WAKE_ANGLE;
0295     param->kb_wake_angle.data = angle;
0296     msg->outsize = sizeof(*param);
0297     msg->insize = sizeof(struct ec_response_motion_sense);
0298 
0299     ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg);
0300     kfree(msg);
0301     if (ret < 0)
0302         return ret;
0303     return count;
0304 }
0305 
0306 /* Module initialization */
0307 
0308 static DEVICE_ATTR_RW(reboot);
0309 static DEVICE_ATTR_RO(version);
0310 static DEVICE_ATTR_RO(flashinfo);
0311 static DEVICE_ATTR_RW(kb_wake_angle);
0312 
0313 static struct attribute *__ec_attrs[] = {
0314     &dev_attr_kb_wake_angle.attr,
0315     &dev_attr_reboot.attr,
0316     &dev_attr_version.attr,
0317     &dev_attr_flashinfo.attr,
0318     NULL,
0319 };
0320 
0321 static umode_t cros_ec_ctrl_visible(struct kobject *kobj,
0322                     struct attribute *a, int n)
0323 {
0324     struct device *dev = kobj_to_dev(kobj);
0325     struct cros_ec_dev *ec = to_cros_ec_dev(dev);
0326 
0327     if (a == &dev_attr_kb_wake_angle.attr && !ec->has_kb_wake_angle)
0328         return 0;
0329 
0330     return a->mode;
0331 }
0332 
0333 static const struct attribute_group cros_ec_attr_group = {
0334     .attrs = __ec_attrs,
0335     .is_visible = cros_ec_ctrl_visible,
0336 };
0337 
0338 static int cros_ec_sysfs_probe(struct platform_device *pd)
0339 {
0340     struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent);
0341     struct device *dev = &pd->dev;
0342     int ret;
0343 
0344     ret = sysfs_create_group(&ec_dev->class_dev.kobj, &cros_ec_attr_group);
0345     if (ret < 0)
0346         dev_err(dev, "failed to create attributes. err=%d\n", ret);
0347 
0348     return ret;
0349 }
0350 
0351 static int cros_ec_sysfs_remove(struct platform_device *pd)
0352 {
0353     struct cros_ec_dev *ec_dev = dev_get_drvdata(pd->dev.parent);
0354 
0355     sysfs_remove_group(&ec_dev->class_dev.kobj, &cros_ec_attr_group);
0356 
0357     return 0;
0358 }
0359 
0360 static struct platform_driver cros_ec_sysfs_driver = {
0361     .driver = {
0362         .name = DRV_NAME,
0363     },
0364     .probe = cros_ec_sysfs_probe,
0365     .remove = cros_ec_sysfs_remove,
0366 };
0367 
0368 module_platform_driver(cros_ec_sysfs_driver);
0369 
0370 MODULE_LICENSE("GPL");
0371 MODULE_DESCRIPTION("Expose the ChromeOS EC through sysfs");
0372 MODULE_ALIAS("platform:" DRV_NAME);