Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

platform/chrome: Add Chrome OS EC userspace device interface

This patch adds a device interface to access the
Chrome OS Embedded Controller from user-space.

Signed-off-by: Bill Richardson <wfrichar@chromium.org>
Reviewed-by: Simon Glass <sjg@google.com>
Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk>
Reviewed-by: Gwendal Grignou <gwendal@chromium.org>
Tested-by: Gwendal Grignou <gwendal@chromium.org>
Reviewed-by: Gwendal Grignou <gwendal@chromium.org>
Signed-off-by: Olof Johansson <olof@lixom.net>

authored by

Bill Richardson and committed by
Olof Johansson
e7c256fb ec2f33ab

+328 -3
+1
Documentation/ioctl/ioctl-number.txt
··· 321 321 0xDB 00-0F drivers/char/mwave/mwavepub.h 322 322 0xDD 00-3F ZFCP device driver see drivers/s390/scsi/ 323 323 <mailto:aherrman@de.ibm.com> 324 + 0xEC 00-01 drivers/platform/chrome/cros_ec_dev.h ChromeOS EC driver 324 325 0xF3 00-3F drivers/usb/misc/sisusbvga/sisusb.h sisfb (in development) 325 326 <mailto:thomas@winischhofer.net> 326 327 0xF4 00-1F video/mbxfb.h mbxfb
+11 -3
drivers/platform/chrome/Kconfig
··· 4 4 5 5 menuconfig CHROME_PLATFORMS 6 6 bool "Platform support for Chrome hardware" 7 - depends on X86 7 + depends on X86 || ARM 8 8 ---help--- 9 9 Say Y here to get to see options for platform support for 10 10 various Chromebooks and Chromeboxes. This option alone does ··· 16 16 17 17 config CHROMEOS_LAPTOP 18 18 tristate "Chrome OS Laptop" 19 - depends on I2C 20 - depends on DMI 19 + depends on I2C && DMI && X86 21 20 ---help--- 22 21 This driver instantiates i2c and smbus devices such as 23 22 light sensors and touchpads. ··· 26 27 27 28 config CHROMEOS_PSTORE 28 29 tristate "Chrome OS pstore support" 30 + depends on X86 29 31 ---help--- 30 32 This module instantiates the persistent storage on x86 ChromeOS 31 33 devices. It can be used to store away console logs and crash ··· 38 38 If you have a supported Chromebook, choose Y or M here. 39 39 The module will be called chromeos_pstore. 40 40 41 + config CROS_EC_CHARDEV 42 + tristate "Chrome OS Embedded Controller userspace device interface" 43 + depends on MFD_CROS_EC 44 + ---help--- 45 + This driver adds support to talk with the ChromeOS EC from userspace. 46 + 47 + If you have a supported Chromebook, choose Y or M here. 48 + The module will be called cros_ec_dev. 41 49 42 50 config CROS_EC_LPC 43 51 tristate "ChromeOS Embedded Controller (LPC)"
+1
drivers/platform/chrome/Makefile
··· 1 1 2 2 obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o 3 3 obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o 4 + obj-$(CONFIG_CROS_EC_CHARDEV) += cros_ec_dev.o 4 5 obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpc.o
+268
drivers/platform/chrome/cros_ec_dev.c
··· 1 + /* 2 + * cros_ec_dev - expose the Chrome OS Embedded Controller to user-space 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 + #include <linux/fs.h> 21 + #include <linux/module.h> 22 + #include <linux/platform_device.h> 23 + #include <linux/uaccess.h> 24 + 25 + #include "cros_ec_dev.h" 26 + 27 + /* Device variables */ 28 + #define CROS_MAX_DEV 128 29 + static struct class *cros_class; 30 + static int ec_major; 31 + 32 + /* Basic communication */ 33 + static int ec_get_version(struct cros_ec_device *ec, char *str, int maxlen) 34 + { 35 + struct ec_response_get_version *resp; 36 + static const char * const current_image_name[] = { 37 + "unknown", "read-only", "read-write", "invalid", 38 + }; 39 + struct cros_ec_command msg = { 40 + .version = 0, 41 + .command = EC_CMD_GET_VERSION, 42 + .outdata = { 0 }, 43 + .outsize = 0, 44 + .indata = { 0 }, 45 + .insize = sizeof(*resp), 46 + }; 47 + int ret; 48 + 49 + ret = cros_ec_cmd_xfer(ec, &msg); 50 + if (ret < 0) 51 + return ret; 52 + 53 + if (msg.result != EC_RES_SUCCESS) { 54 + snprintf(str, maxlen, 55 + "%s\nUnknown EC version: EC returned %d\n", 56 + CROS_EC_DEV_VERSION, msg.result); 57 + return 0; 58 + } 59 + 60 + resp = (struct ec_response_get_version *)msg.indata; 61 + if (resp->current_image >= ARRAY_SIZE(current_image_name)) 62 + resp->current_image = 3; /* invalid */ 63 + 64 + snprintf(str, maxlen, "%s\n%s\n%s\n\%s\n", CROS_EC_DEV_VERSION, 65 + resp->version_string_ro, resp->version_string_rw, 66 + current_image_name[resp->current_image]); 67 + 68 + return 0; 69 + } 70 + 71 + /* Device file ops */ 72 + static int ec_device_open(struct inode *inode, struct file *filp) 73 + { 74 + filp->private_data = container_of(inode->i_cdev, 75 + struct cros_ec_device, cdev); 76 + return 0; 77 + } 78 + 79 + static int ec_device_release(struct inode *inode, struct file *filp) 80 + { 81 + return 0; 82 + } 83 + 84 + static ssize_t ec_device_read(struct file *filp, char __user *buffer, 85 + size_t length, loff_t *offset) 86 + { 87 + struct cros_ec_device *ec = filp->private_data; 88 + char msg[sizeof(struct ec_response_get_version) + 89 + sizeof(CROS_EC_DEV_VERSION)]; 90 + size_t count; 91 + int ret; 92 + 93 + if (*offset != 0) 94 + return 0; 95 + 96 + ret = ec_get_version(ec, msg, sizeof(msg)); 97 + if (ret) 98 + return ret; 99 + 100 + count = min(length, strlen(msg)); 101 + 102 + if (copy_to_user(buffer, msg, count)) 103 + return -EFAULT; 104 + 105 + *offset = count; 106 + return count; 107 + } 108 + 109 + /* Ioctls */ 110 + static long ec_device_ioctl_xcmd(struct cros_ec_device *ec, void __user *arg) 111 + { 112 + long ret; 113 + struct cros_ec_command s_cmd = { }; 114 + 115 + if (copy_from_user(&s_cmd, arg, sizeof(s_cmd))) 116 + return -EFAULT; 117 + 118 + ret = cros_ec_cmd_xfer(ec, &s_cmd); 119 + /* Only copy data to userland if data was received. */ 120 + if (ret < 0) 121 + return ret; 122 + 123 + if (copy_to_user(arg, &s_cmd, sizeof(s_cmd))) 124 + return -EFAULT; 125 + 126 + return 0; 127 + } 128 + 129 + static long ec_device_ioctl_readmem(struct cros_ec_device *ec, void __user *arg) 130 + { 131 + struct cros_ec_readmem s_mem = { }; 132 + long num; 133 + 134 + /* Not every platform supports direct reads */ 135 + if (!ec->cmd_readmem) 136 + return -ENOTTY; 137 + 138 + if (copy_from_user(&s_mem, arg, sizeof(s_mem))) 139 + return -EFAULT; 140 + 141 + num = ec->cmd_readmem(ec, s_mem.offset, s_mem.bytes, s_mem.buffer); 142 + if (num <= 0) 143 + return num; 144 + 145 + if (copy_to_user((void __user *)arg, &s_mem, sizeof(s_mem))) 146 + return -EFAULT; 147 + 148 + return 0; 149 + } 150 + 151 + static long ec_device_ioctl(struct file *filp, unsigned int cmd, 152 + unsigned long arg) 153 + { 154 + struct cros_ec_device *ec = filp->private_data; 155 + 156 + if (_IOC_TYPE(cmd) != CROS_EC_DEV_IOC) 157 + return -ENOTTY; 158 + 159 + switch (cmd) { 160 + case CROS_EC_DEV_IOCXCMD: 161 + return ec_device_ioctl_xcmd(ec, (void __user *)arg); 162 + case CROS_EC_DEV_IOCRDMEM: 163 + return ec_device_ioctl_readmem(ec, (void __user *)arg); 164 + } 165 + 166 + return -ENOTTY; 167 + } 168 + 169 + /* Module initialization */ 170 + static const struct file_operations fops = { 171 + .open = ec_device_open, 172 + .release = ec_device_release, 173 + .read = ec_device_read, 174 + .unlocked_ioctl = ec_device_ioctl, 175 + }; 176 + 177 + static int ec_device_probe(struct platform_device *pdev) 178 + { 179 + struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); 180 + int retval = -ENOTTY; 181 + dev_t devno = MKDEV(ec_major, 0); 182 + 183 + /* Instantiate it (and remember the EC) */ 184 + cdev_init(&ec->cdev, &fops); 185 + 186 + retval = cdev_add(&ec->cdev, devno, 1); 187 + if (retval) { 188 + dev_err(&pdev->dev, ": failed to add character device\n"); 189 + return retval; 190 + } 191 + 192 + ec->vdev = device_create(cros_class, NULL, devno, ec, 193 + CROS_EC_DEV_NAME); 194 + if (IS_ERR(ec->vdev)) { 195 + retval = PTR_ERR(ec->vdev); 196 + dev_err(&pdev->dev, ": failed to create device\n"); 197 + cdev_del(&ec->cdev); 198 + return retval; 199 + } 200 + 201 + return 0; 202 + } 203 + 204 + static int ec_device_remove(struct platform_device *pdev) 205 + { 206 + struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); 207 + 208 + device_destroy(cros_class, MKDEV(ec_major, 0)); 209 + cdev_del(&ec->cdev); 210 + return 0; 211 + } 212 + 213 + static struct platform_driver cros_ec_dev_driver = { 214 + .driver = { 215 + .name = "cros-ec-ctl", 216 + }, 217 + .probe = ec_device_probe, 218 + .remove = ec_device_remove, 219 + }; 220 + 221 + static int __init cros_ec_dev_init(void) 222 + { 223 + int ret; 224 + dev_t dev = 0; 225 + 226 + cros_class = class_create(THIS_MODULE, "chromeos"); 227 + if (IS_ERR(cros_class)) { 228 + pr_err(CROS_EC_DEV_NAME ": failed to register device class\n"); 229 + return PTR_ERR(cros_class); 230 + } 231 + 232 + /* Get a range of minor numbers (starting with 0) to work with */ 233 + ret = alloc_chrdev_region(&dev, 0, CROS_MAX_DEV, CROS_EC_DEV_NAME); 234 + if (ret < 0) { 235 + pr_err(CROS_EC_DEV_NAME ": alloc_chrdev_region() failed\n"); 236 + goto failed_chrdevreg; 237 + } 238 + ec_major = MAJOR(dev); 239 + 240 + /* Register the driver */ 241 + ret = platform_driver_register(&cros_ec_dev_driver); 242 + if (ret < 0) { 243 + pr_warn(CROS_EC_DEV_NAME ": can't register driver: %d\n", ret); 244 + goto failed_devreg; 245 + } 246 + return 0; 247 + 248 + failed_devreg: 249 + unregister_chrdev_region(MKDEV(ec_major, 0), CROS_MAX_DEV); 250 + failed_chrdevreg: 251 + class_destroy(cros_class); 252 + return ret; 253 + } 254 + 255 + static void __exit cros_ec_dev_exit(void) 256 + { 257 + platform_driver_unregister(&cros_ec_dev_driver); 258 + unregister_chrdev(ec_major, CROS_EC_DEV_NAME); 259 + class_destroy(cros_class); 260 + } 261 + 262 + module_init(cros_ec_dev_init); 263 + module_exit(cros_ec_dev_exit); 264 + 265 + MODULE_AUTHOR("Bill Richardson <wfrichar@chromium.org>"); 266 + MODULE_DESCRIPTION("Userspace interface to the Chrome OS Embedded Controller"); 267 + MODULE_VERSION("1.0"); 268 + MODULE_LICENSE("GPL");
+47
drivers/platform/chrome/cros_ec_dev.h
··· 1 + /* 2 + * cros_ec_dev - expose the Chrome OS Embedded Controller to userspace 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 + #ifndef _CROS_EC_DEV_H_ 21 + #define _CROS_EC_DEV_H_ 22 + 23 + #include <linux/ioctl.h> 24 + #include <linux/types.h> 25 + #include <linux/mfd/cros_ec.h> 26 + 27 + #define CROS_EC_DEV_NAME "cros_ec" 28 + #define CROS_EC_DEV_VERSION "1.0.0" 29 + 30 + /* 31 + * @offset: within EC_LPC_ADDR_MEMMAP region 32 + * @bytes: number of bytes to read. zero means "read a string" (including '\0') 33 + * (at most only EC_MEMMAP_SIZE bytes can be read) 34 + * @buffer: where to store the result 35 + * ioctl returns the number of bytes read, negative on error 36 + */ 37 + struct cros_ec_readmem { 38 + uint32_t offset; 39 + uint32_t bytes; 40 + uint8_t buffer[EC_MEMMAP_SIZE]; 41 + }; 42 + 43 + #define CROS_EC_DEV_IOC 0xEC 44 + #define CROS_EC_DEV_IOCXCMD _IOWR(CROS_EC_DEV_IOC, 0, struct cros_ec_command) 45 + #define CROS_EC_DEV_IOCRDMEM _IOWR(CROS_EC_DEV_IOC, 1, struct cros_ec_readmem) 46 + 47 + #endif /* _CROS_EC_DEV_H_ */