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

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'chrome-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/olof/chrome-platform

Pull chrome platform updates from Olof Johansson:
"Here's a set of updates to the Chrome OS platform drivers for this
merge window.

Main new things this cycle is:

- Driver changes to expose the lightbar to users. With this, you can
make your own blinkenlights on Chromebook Pixels.

- Changes in the way that the atmel_mxt trackpads are probed. The
laptop driver is trying to be smart and not instantiate the devices
that don't answer to probe. For the trackpad that can come up in
two modes (bootloader or regular), this gets complicated since the
driver already knows how to handle the two modes including the
actual addresses used. So now the laptop driver needs to know more
too, instantiating the regular address even if the bootloader one
is the probe that passed.

- mfd driver improvements by Javier Martines Canillas, and a few
bugfixes from him, kbuild and myself"

* tag 'chrome-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/olof/chrome-platform:
platform/chrome: chromeos_laptop - instantiate Atmel at primary address
platform/chrome: cros_ec_lpc - Depend on X86 || COMPILE_TEST
platform/chrome: cros_ec_lpc - Include linux/io.h header file
platform/chrome: fix platform_no_drv_owner.cocci warnings
platform/chrome: cros_ec_lightbar - fix duplicate const warning
platform/chrome: cros_ec_dev - fix Unknown escape '%' warning
platform/chrome: Expose Chrome OS Lightbar to users
platform/chrome: Create sysfs attributes for the ChromeOS EC
mfd: cros_ec: Instantiate ChromeOS EC character device
platform/chrome: Add Chrome OS EC userspace device interface
platform/chrome: Add cros_ec_lpc driver for x86 devices
mfd: cros_ec: Add char dev and virtual dev pointers
mfd: cros_ec: Use fixed size arrays to transfer data with the EC

+1384 -71
+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
+10 -41
drivers/i2c/busses/i2c-cros-ec-tunnel.c
··· 182 182 const u16 bus_num = bus->remote_bus; 183 183 int request_len; 184 184 int response_len; 185 - u8 *request = NULL; 186 - u8 *response = NULL; 187 185 int result; 188 - struct cros_ec_command msg; 186 + struct cros_ec_command msg = { }; 189 187 190 188 request_len = ec_i2c_count_message(i2c_msgs, num); 191 189 if (request_len < 0) { 192 190 dev_warn(dev, "Error constructing message %d\n", request_len); 193 - result = request_len; 194 - goto exit; 191 + return request_len; 195 192 } 193 + 196 194 response_len = ec_i2c_count_response(i2c_msgs, num); 197 195 if (response_len < 0) { 198 196 /* Unexpected; no errors should come when NULL response */ 199 197 dev_warn(dev, "Error preparing response %d\n", response_len); 200 - result = response_len; 201 - goto exit; 198 + return response_len; 202 199 } 203 200 204 - if (request_len <= ARRAY_SIZE(bus->request_buf)) { 205 - request = bus->request_buf; 206 - } else { 207 - request = kzalloc(request_len, GFP_KERNEL); 208 - if (request == NULL) { 209 - result = -ENOMEM; 210 - goto exit; 211 - } 212 - } 213 - if (response_len <= ARRAY_SIZE(bus->response_buf)) { 214 - response = bus->response_buf; 215 - } else { 216 - response = kzalloc(response_len, GFP_KERNEL); 217 - if (response == NULL) { 218 - result = -ENOMEM; 219 - goto exit; 220 - } 221 - } 222 - 223 - result = ec_i2c_construct_message(request, i2c_msgs, num, bus_num); 201 + result = ec_i2c_construct_message(msg.outdata, i2c_msgs, num, bus_num); 224 202 if (result) 225 - goto exit; 203 + return result; 226 204 227 205 msg.version = 0; 228 206 msg.command = EC_CMD_I2C_PASSTHRU; 229 - msg.outdata = request; 230 207 msg.outsize = request_len; 231 - msg.indata = response; 232 208 msg.insize = response_len; 233 209 234 210 result = cros_ec_cmd_xfer(bus->ec, &msg); 235 211 if (result < 0) 236 - goto exit; 212 + return result; 237 213 238 - result = ec_i2c_parse_response(response, i2c_msgs, &num); 214 + result = ec_i2c_parse_response(msg.indata, i2c_msgs, &num); 239 215 if (result < 0) 240 - goto exit; 216 + return result; 241 217 242 218 /* Indicate success by saying how many messages were sent */ 243 - result = num; 244 - exit: 245 - if (request != bus->request_buf) 246 - kfree(request); 247 - if (response != bus->response_buf) 248 - kfree(response); 249 - 250 - return result; 219 + return num; 251 220 } 252 221 253 222 static u32 ec_i2c_functionality(struct i2c_adapter *adap)
+8 -5
drivers/input/keyboard/cros_ec_keyb.c
··· 148 148 149 149 static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state) 150 150 { 151 + int ret; 151 152 struct cros_ec_command msg = { 152 - .version = 0, 153 153 .command = EC_CMD_MKBP_STATE, 154 - .outdata = NULL, 155 - .outsize = 0, 156 - .indata = kb_state, 157 154 .insize = ckdev->cols, 158 155 }; 159 156 160 - return cros_ec_cmd_xfer(ckdev->ec, &msg); 157 + ret = cros_ec_cmd_xfer(ckdev->ec, &msg); 158 + if (ret < 0) 159 + return ret; 160 + 161 + memcpy(kb_state, msg.indata, ckdev->cols); 162 + 163 + return 0; 161 164 } 162 165 163 166 static irqreturn_t cros_ec_keyb_irq(int irq, void *data)
+11 -8
drivers/mfd/cros_ec.c
··· 74 74 ret = ec_dev->cmd_xfer(ec_dev, msg); 75 75 if (msg->result == EC_RES_IN_PROGRESS) { 76 76 int i; 77 - struct cros_ec_command status_msg; 78 - struct ec_response_get_comms_status status; 77 + struct cros_ec_command status_msg = { }; 78 + struct ec_response_get_comms_status *status; 79 79 80 - status_msg.version = 0; 81 80 status_msg.command = EC_CMD_GET_COMMS_STATUS; 82 - status_msg.outdata = NULL; 83 - status_msg.outsize = 0; 84 - status_msg.indata = (uint8_t *)&status; 85 - status_msg.insize = sizeof(status); 81 + status_msg.insize = sizeof(*status); 86 82 87 83 /* 88 84 * Query the EC's status until it's no longer busy or ··· 94 98 msg->result = status_msg.result; 95 99 if (status_msg.result != EC_RES_SUCCESS) 96 100 break; 97 - if (!(status.flags & EC_COMMS_STATUS_PROCESSING)) 101 + 102 + status = (struct ec_response_get_comms_status *) 103 + status_msg.indata; 104 + if (!(status->flags & EC_COMMS_STATUS_PROCESSING)) 98 105 break; 99 106 } 100 107 } ··· 117 118 .name = "cros-ec-i2c-tunnel", 118 119 .id = 2, 119 120 .of_compatible = "google,cros-ec-i2c-tunnel", 121 + }, 122 + { 123 + .name = "cros-ec-ctl", 124 + .id = 3, 120 125 }, 121 126 }; 122 127
+23 -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. 49 + 50 + config CROS_EC_LPC 51 + tristate "ChromeOS Embedded Controller (LPC)" 52 + depends on MFD_CROS_EC && (X86 || COMPILE_TEST) 53 + help 54 + If you say Y here, you get support for talking to the ChromeOS EC 55 + over an LPC bus. This uses a simple byte-level protocol with a 56 + checksum. This is used for userspace access only. The kernel 57 + typically has its own communication methods. 58 + 59 + To compile this driver as a module, choose M here: the 60 + module will be called cros_ec_lpc. 41 61 42 62 endif # CHROMEOS_PLATFORMS
+3
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 + cros_ec_devs-objs := cros_ec_dev.o cros_ec_sysfs.o cros_ec_lightbar.o 5 + obj-$(CONFIG_CROS_EC_CHARDEV) += cros_ec_devs.o 6 + obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpc.o
+26 -9
drivers/platform/chrome/chromeos_laptop.c
··· 133 133 const char *name, 134 134 int bus, 135 135 struct i2c_board_info *info, 136 - const unsigned short *addrs) 136 + const unsigned short *alt_addr_list) 137 137 { 138 138 const struct dmi_device *dmi_dev; 139 139 const struct dmi_dev_onboard *dev_data; 140 140 struct i2c_adapter *adapter; 141 - struct i2c_client *client; 141 + struct i2c_client *client = NULL; 142 + const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END }; 142 143 143 144 if (bus < 0) 144 145 return NULL; ··· 170 169 return NULL; 171 170 } 172 171 173 - /* add the i2c device */ 174 - client = i2c_new_probed_device(adapter, info, addrs, NULL); 172 + /* 173 + * Add the i2c device. If we can't detect it at the primary 174 + * address we scan secondary addresses. In any case the client 175 + * structure gets assigned primary address. 176 + */ 177 + client = i2c_new_probed_device(adapter, info, addr_list, NULL); 178 + if (!client && alt_addr_list) { 179 + struct i2c_board_info dummy_info = { 180 + I2C_BOARD_INFO("dummy", info->addr), 181 + }; 182 + struct i2c_client *dummy; 183 + 184 + dummy = i2c_new_probed_device(adapter, &dummy_info, 185 + alt_addr_list, NULL); 186 + if (dummy) { 187 + pr_debug("%s %d-%02x is probed at %02x\n", 188 + __func__, bus, info->addr, dummy->addr); 189 + i2c_unregister_device(dummy); 190 + client = i2c_new_device(adapter, info); 191 + } 192 + } 193 + 175 194 if (!client) 176 195 pr_notice("%s failed to register device %d-%02x\n", 177 196 __func__, bus, info->addr); ··· 275 254 enum i2c_adapter_type type, 276 255 struct i2c_board_info *info) 277 256 { 278 - const unsigned short addr_list[] = { info->addr, I2C_CLIENT_END }; 279 - 280 257 return __add_probed_i2c_device(name, 281 258 find_i2c_adapter_num(type), 282 259 info, 283 - addr_list); 260 + NULL); 284 261 } 285 262 286 263 static int setup_cyapa_tp(enum i2c_adapter_type type) ··· 294 275 static int setup_atmel_224s_tp(enum i2c_adapter_type type) 295 276 { 296 277 const unsigned short addr_list[] = { ATMEL_TP_I2C_BL_ADDR, 297 - ATMEL_TP_I2C_ADDR, 298 278 I2C_CLIENT_END }; 299 279 if (tp) 300 280 return 0; ··· 307 289 static int setup_atmel_1664s_ts(enum i2c_adapter_type type) 308 290 { 309 291 const unsigned short addr_list[] = { ATMEL_TS_I2C_BL_ADDR, 310 - ATMEL_TS_I2C_ADDR, 311 292 I2C_CLIENT_END }; 312 293 if (ts) 313 294 return 0;
+274
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 + /* Initialize extra interfaces */ 202 + ec_dev_sysfs_init(ec); 203 + ec_dev_lightbar_init(ec); 204 + 205 + return 0; 206 + } 207 + 208 + static int ec_device_remove(struct platform_device *pdev) 209 + { 210 + struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); 211 + 212 + ec_dev_lightbar_remove(ec); 213 + ec_dev_sysfs_remove(ec); 214 + device_destroy(cros_class, MKDEV(ec_major, 0)); 215 + cdev_del(&ec->cdev); 216 + return 0; 217 + } 218 + 219 + static struct platform_driver cros_ec_dev_driver = { 220 + .driver = { 221 + .name = "cros-ec-ctl", 222 + }, 223 + .probe = ec_device_probe, 224 + .remove = ec_device_remove, 225 + }; 226 + 227 + static int __init cros_ec_dev_init(void) 228 + { 229 + int ret; 230 + dev_t dev = 0; 231 + 232 + cros_class = class_create(THIS_MODULE, "chromeos"); 233 + if (IS_ERR(cros_class)) { 234 + pr_err(CROS_EC_DEV_NAME ": failed to register device class\n"); 235 + return PTR_ERR(cros_class); 236 + } 237 + 238 + /* Get a range of minor numbers (starting with 0) to work with */ 239 + ret = alloc_chrdev_region(&dev, 0, CROS_MAX_DEV, CROS_EC_DEV_NAME); 240 + if (ret < 0) { 241 + pr_err(CROS_EC_DEV_NAME ": alloc_chrdev_region() failed\n"); 242 + goto failed_chrdevreg; 243 + } 244 + ec_major = MAJOR(dev); 245 + 246 + /* Register the driver */ 247 + ret = platform_driver_register(&cros_ec_dev_driver); 248 + if (ret < 0) { 249 + pr_warn(CROS_EC_DEV_NAME ": can't register driver: %d\n", ret); 250 + goto failed_devreg; 251 + } 252 + return 0; 253 + 254 + failed_devreg: 255 + unregister_chrdev_region(MKDEV(ec_major, 0), CROS_MAX_DEV); 256 + failed_chrdevreg: 257 + class_destroy(cros_class); 258 + return ret; 259 + } 260 + 261 + static void __exit cros_ec_dev_exit(void) 262 + { 263 + platform_driver_unregister(&cros_ec_dev_driver); 264 + unregister_chrdev(ec_major, CROS_EC_DEV_NAME); 265 + class_destroy(cros_class); 266 + } 267 + 268 + module_init(cros_ec_dev_init); 269 + module_exit(cros_ec_dev_exit); 270 + 271 + MODULE_AUTHOR("Bill Richardson <wfrichar@chromium.org>"); 272 + MODULE_DESCRIPTION("Userspace interface to the Chrome OS Embedded Controller"); 273 + MODULE_VERSION("1.0"); 274 + MODULE_LICENSE("GPL");
+53
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 + void ec_dev_sysfs_init(struct cros_ec_device *); 48 + void ec_dev_sysfs_remove(struct cros_ec_device *); 49 + 50 + void ec_dev_lightbar_init(struct cros_ec_device *); 51 + void ec_dev_lightbar_remove(struct cros_ec_device *); 52 + 53 + #endif /* _CROS_EC_DEV_H_ */
+367
drivers/platform/chrome/cros_ec_lightbar.c
··· 1 + /* 2 + * cros_ec_lightbar - expose the Chromebook Pixel lightbar 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 + #define pr_fmt(fmt) "cros_ec_lightbar: " 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/sched.h> 32 + #include <linux/types.h> 33 + #include <linux/uaccess.h> 34 + 35 + #include "cros_ec_dev.h" 36 + 37 + /* Rate-limit the lightbar interface to prevent DoS. */ 38 + static unsigned long lb_interval_jiffies = 50 * HZ / 1000; 39 + 40 + static ssize_t interval_msec_show(struct device *dev, 41 + struct device_attribute *attr, char *buf) 42 + { 43 + unsigned long msec = lb_interval_jiffies * 1000 / HZ; 44 + 45 + return scnprintf(buf, PAGE_SIZE, "%lu\n", msec); 46 + } 47 + 48 + static ssize_t interval_msec_store(struct device *dev, 49 + struct device_attribute *attr, 50 + const char *buf, size_t count) 51 + { 52 + unsigned long msec; 53 + 54 + if (kstrtoul(buf, 0, &msec)) 55 + return -EINVAL; 56 + 57 + lb_interval_jiffies = msec * HZ / 1000; 58 + 59 + return count; 60 + } 61 + 62 + static DEFINE_MUTEX(lb_mutex); 63 + /* Return 0 if able to throttle correctly, error otherwise */ 64 + static int lb_throttle(void) 65 + { 66 + static unsigned long last_access; 67 + unsigned long now, next_timeslot; 68 + long delay; 69 + int ret = 0; 70 + 71 + mutex_lock(&lb_mutex); 72 + 73 + now = jiffies; 74 + next_timeslot = last_access + lb_interval_jiffies; 75 + 76 + if (time_before(now, next_timeslot)) { 77 + delay = (long)(next_timeslot) - (long)now; 78 + set_current_state(TASK_INTERRUPTIBLE); 79 + if (schedule_timeout(delay) > 0) { 80 + /* interrupted - just abort */ 81 + ret = -EINTR; 82 + goto out; 83 + } 84 + now = jiffies; 85 + } 86 + 87 + last_access = now; 88 + out: 89 + mutex_unlock(&lb_mutex); 90 + 91 + return ret; 92 + } 93 + 94 + #define INIT_MSG(P, R) { \ 95 + .command = EC_CMD_LIGHTBAR_CMD, \ 96 + .outsize = sizeof(*P), \ 97 + .insize = sizeof(*R), \ 98 + } 99 + 100 + static int get_lightbar_version(struct cros_ec_device *ec, 101 + uint32_t *ver_ptr, uint32_t *flg_ptr) 102 + { 103 + struct ec_params_lightbar *param; 104 + struct ec_response_lightbar *resp; 105 + struct cros_ec_command msg = INIT_MSG(param, resp); 106 + int ret; 107 + 108 + param = (struct ec_params_lightbar *)msg.outdata; 109 + param->cmd = LIGHTBAR_CMD_VERSION; 110 + ret = cros_ec_cmd_xfer(ec, &msg); 111 + if (ret < 0) 112 + return 0; 113 + 114 + switch (msg.result) { 115 + case EC_RES_INVALID_PARAM: 116 + /* Pixel had no version command. */ 117 + if (ver_ptr) 118 + *ver_ptr = 0; 119 + if (flg_ptr) 120 + *flg_ptr = 0; 121 + return 1; 122 + 123 + case EC_RES_SUCCESS: 124 + resp = (struct ec_response_lightbar *)msg.indata; 125 + 126 + /* Future devices w/lightbars should implement this command */ 127 + if (ver_ptr) 128 + *ver_ptr = resp->version.num; 129 + if (flg_ptr) 130 + *flg_ptr = resp->version.flags; 131 + return 1; 132 + } 133 + 134 + /* Anything else (ie, EC_RES_INVALID_COMMAND) - no lightbar */ 135 + return 0; 136 + } 137 + 138 + static ssize_t version_show(struct device *dev, 139 + struct device_attribute *attr, char *buf) 140 + { 141 + uint32_t version, flags; 142 + struct cros_ec_device *ec = dev_get_drvdata(dev); 143 + int ret; 144 + 145 + ret = lb_throttle(); 146 + if (ret) 147 + return ret; 148 + 149 + /* This should always succeed, because we check during init. */ 150 + if (!get_lightbar_version(ec, &version, &flags)) 151 + return -EIO; 152 + 153 + return scnprintf(buf, PAGE_SIZE, "%d %d\n", version, flags); 154 + } 155 + 156 + static ssize_t brightness_store(struct device *dev, 157 + struct device_attribute *attr, 158 + const char *buf, size_t count) 159 + { 160 + struct ec_params_lightbar *param; 161 + struct ec_response_lightbar *resp; 162 + struct cros_ec_command msg = INIT_MSG(param, resp); 163 + int ret; 164 + unsigned int val; 165 + struct cros_ec_device *ec = dev_get_drvdata(dev); 166 + 167 + if (kstrtouint(buf, 0, &val)) 168 + return -EINVAL; 169 + 170 + param = (struct ec_params_lightbar *)msg.outdata; 171 + param->cmd = LIGHTBAR_CMD_BRIGHTNESS; 172 + param->brightness.num = val; 173 + ret = lb_throttle(); 174 + if (ret) 175 + return ret; 176 + 177 + ret = cros_ec_cmd_xfer(ec, &msg); 178 + if (ret < 0) 179 + return ret; 180 + 181 + if (msg.result != EC_RES_SUCCESS) 182 + return -EINVAL; 183 + 184 + return count; 185 + } 186 + 187 + 188 + /* 189 + * We expect numbers, and we'll keep reading until we find them, skipping over 190 + * any whitespace (sysfs guarantees that the input is null-terminated). Every 191 + * four numbers are sent to the lightbar as <LED,R,G,B>. We fail at the first 192 + * parsing error, if we don't parse any numbers, or if we have numbers left 193 + * over. 194 + */ 195 + static ssize_t led_rgb_store(struct device *dev, struct device_attribute *attr, 196 + const char *buf, size_t count) 197 + { 198 + struct ec_params_lightbar *param; 199 + struct ec_response_lightbar *resp; 200 + struct cros_ec_command msg = INIT_MSG(param, resp); 201 + struct cros_ec_device *ec = dev_get_drvdata(dev); 202 + unsigned int val[4]; 203 + int ret, i = 0, j = 0, ok = 0; 204 + 205 + do { 206 + /* Skip any whitespace */ 207 + while (*buf && isspace(*buf)) 208 + buf++; 209 + 210 + if (!*buf) 211 + break; 212 + 213 + ret = sscanf(buf, "%i", &val[i++]); 214 + if (ret == 0) 215 + return -EINVAL; 216 + 217 + if (i == 4) { 218 + param = (struct ec_params_lightbar *)msg.outdata; 219 + param->cmd = LIGHTBAR_CMD_RGB; 220 + param->rgb.led = val[0]; 221 + param->rgb.red = val[1]; 222 + param->rgb.green = val[2]; 223 + param->rgb.blue = val[3]; 224 + /* 225 + * Throttle only the first of every four transactions, 226 + * so that the user can update all four LEDs at once. 227 + */ 228 + if ((j++ % 4) == 0) { 229 + ret = lb_throttle(); 230 + if (ret) 231 + return ret; 232 + } 233 + 234 + ret = cros_ec_cmd_xfer(ec, &msg); 235 + if (ret < 0) 236 + return ret; 237 + 238 + if (msg.result != EC_RES_SUCCESS) 239 + return -EINVAL; 240 + 241 + i = 0; 242 + ok = 1; 243 + } 244 + 245 + /* Skip over the number we just read */ 246 + while (*buf && !isspace(*buf)) 247 + buf++; 248 + 249 + } while (*buf); 250 + 251 + return (ok && i == 0) ? count : -EINVAL; 252 + } 253 + 254 + static char const *seqname[] = { 255 + "ERROR", "S5", "S3", "S0", "S5S3", "S3S0", 256 + "S0S3", "S3S5", "STOP", "RUN", "PULSE", "TEST", "KONAMI", 257 + }; 258 + 259 + static ssize_t sequence_show(struct device *dev, 260 + struct device_attribute *attr, char *buf) 261 + { 262 + struct ec_params_lightbar *param; 263 + struct ec_response_lightbar *resp; 264 + struct cros_ec_command msg = INIT_MSG(param, resp); 265 + int ret; 266 + struct cros_ec_device *ec = dev_get_drvdata(dev); 267 + 268 + param = (struct ec_params_lightbar *)msg.outdata; 269 + param->cmd = LIGHTBAR_CMD_GET_SEQ; 270 + ret = lb_throttle(); 271 + if (ret) 272 + return ret; 273 + 274 + ret = cros_ec_cmd_xfer(ec, &msg); 275 + if (ret < 0) 276 + return ret; 277 + 278 + if (msg.result != EC_RES_SUCCESS) 279 + return scnprintf(buf, PAGE_SIZE, 280 + "ERROR: EC returned %d\n", msg.result); 281 + 282 + resp = (struct ec_response_lightbar *)msg.indata; 283 + if (resp->get_seq.num >= ARRAY_SIZE(seqname)) 284 + return scnprintf(buf, PAGE_SIZE, "%d\n", resp->get_seq.num); 285 + else 286 + return scnprintf(buf, PAGE_SIZE, "%s\n", 287 + seqname[resp->get_seq.num]); 288 + } 289 + 290 + static ssize_t sequence_store(struct device *dev, struct device_attribute *attr, 291 + const char *buf, size_t count) 292 + { 293 + struct ec_params_lightbar *param; 294 + struct ec_response_lightbar *resp; 295 + struct cros_ec_command msg = INIT_MSG(param, resp); 296 + unsigned int num; 297 + int ret, len; 298 + struct cros_ec_device *ec = dev_get_drvdata(dev); 299 + 300 + for (len = 0; len < count; len++) 301 + if (!isalnum(buf[len])) 302 + break; 303 + 304 + for (num = 0; num < ARRAY_SIZE(seqname); num++) 305 + if (!strncasecmp(seqname[num], buf, len)) 306 + break; 307 + 308 + if (num >= ARRAY_SIZE(seqname)) { 309 + ret = kstrtouint(buf, 0, &num); 310 + if (ret) 311 + return ret; 312 + } 313 + 314 + param = (struct ec_params_lightbar *)msg.outdata; 315 + param->cmd = LIGHTBAR_CMD_SEQ; 316 + param->seq.num = num; 317 + ret = lb_throttle(); 318 + if (ret) 319 + return ret; 320 + 321 + ret = cros_ec_cmd_xfer(ec, &msg); 322 + if (ret < 0) 323 + return ret; 324 + 325 + if (msg.result != EC_RES_SUCCESS) 326 + return -EINVAL; 327 + 328 + return count; 329 + } 330 + 331 + /* Module initialization */ 332 + 333 + static DEVICE_ATTR_RW(interval_msec); 334 + static DEVICE_ATTR_RO(version); 335 + static DEVICE_ATTR_WO(brightness); 336 + static DEVICE_ATTR_WO(led_rgb); 337 + static DEVICE_ATTR_RW(sequence); 338 + static struct attribute *__lb_cmds_attrs[] = { 339 + &dev_attr_interval_msec.attr, 340 + &dev_attr_version.attr, 341 + &dev_attr_brightness.attr, 342 + &dev_attr_led_rgb.attr, 343 + &dev_attr_sequence.attr, 344 + NULL, 345 + }; 346 + static struct attribute_group lb_cmds_attr_group = { 347 + .name = "lightbar", 348 + .attrs = __lb_cmds_attrs, 349 + }; 350 + 351 + void ec_dev_lightbar_init(struct cros_ec_device *ec) 352 + { 353 + int ret = 0; 354 + 355 + /* Only instantiate this stuff if the EC has a lightbar */ 356 + if (!get_lightbar_version(ec, NULL, NULL)) 357 + return; 358 + 359 + ret = sysfs_create_group(&ec->vdev->kobj, &lb_cmds_attr_group); 360 + if (ret) 361 + pr_warn("sysfs_create_group() failed: %d\n", ret); 362 + } 363 + 364 + void ec_dev_lightbar_remove(struct cros_ec_device *ec) 365 + { 366 + sysfs_remove_group(&ec->vdev->kobj, &lb_cmds_attr_group); 367 + }
+319
drivers/platform/chrome/cros_ec_lpc.c
··· 1 + /* 2 + * cros_ec_lpc - LPC access to the Chrome OS Embedded Controller 3 + * 4 + * Copyright (C) 2012-2015 Google, Inc 5 + * 6 + * This software is licensed under the terms of the GNU General Public 7 + * License version 2, as published by the Free Software Foundation, and 8 + * may be copied, distributed, and modified under those terms. 9 + * 10 + * This program is distributed in the hope that it will be useful, 11 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 + * GNU General Public License for more details. 14 + * 15 + * This driver uses the Chrome OS EC byte-level message-based protocol for 16 + * communicating the keyboard state (which keys are pressed) from a keyboard EC 17 + * to the AP over some bus (such as i2c, lpc, spi). The EC does debouncing, 18 + * but everything else (including deghosting) is done here. The main 19 + * motivation for this is to keep the EC firmware as simple as possible, since 20 + * it cannot be easily upgraded and EC flash/IRAM space is relatively 21 + * expensive. 22 + */ 23 + 24 + #include <linux/dmi.h> 25 + #include <linux/delay.h> 26 + #include <linux/io.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 + 33 + #define DRV_NAME "cros_ec_lpc" 34 + 35 + static int ec_response_timed_out(void) 36 + { 37 + unsigned long one_second = jiffies + HZ; 38 + 39 + usleep_range(200, 300); 40 + do { 41 + if (!(inb(EC_LPC_ADDR_HOST_CMD) & EC_LPC_STATUS_BUSY_MASK)) 42 + return 0; 43 + usleep_range(100, 200); 44 + } while (time_before(jiffies, one_second)); 45 + 46 + return 1; 47 + } 48 + 49 + static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec, 50 + struct cros_ec_command *msg) 51 + { 52 + struct ec_lpc_host_args args; 53 + int csum; 54 + int i; 55 + int ret = 0; 56 + 57 + if (msg->outsize > EC_PROTO2_MAX_PARAM_SIZE || 58 + msg->insize > EC_PROTO2_MAX_PARAM_SIZE) { 59 + dev_err(ec->dev, 60 + "invalid buffer sizes (out %d, in %d)\n", 61 + msg->outsize, msg->insize); 62 + return -EINVAL; 63 + } 64 + 65 + /* Now actually send the command to the EC and get the result */ 66 + args.flags = EC_HOST_ARGS_FLAG_FROM_HOST; 67 + args.command_version = msg->version; 68 + args.data_size = msg->outsize; 69 + 70 + /* Initialize checksum */ 71 + csum = msg->command + args.flags + 72 + args.command_version + args.data_size; 73 + 74 + /* Copy data and update checksum */ 75 + for (i = 0; i < msg->outsize; i++) { 76 + outb(msg->outdata[i], EC_LPC_ADDR_HOST_PARAM + i); 77 + csum += msg->outdata[i]; 78 + } 79 + 80 + /* Finalize checksum and write args */ 81 + args.checksum = csum & 0xFF; 82 + outb(args.flags, EC_LPC_ADDR_HOST_ARGS); 83 + outb(args.command_version, EC_LPC_ADDR_HOST_ARGS + 1); 84 + outb(args.data_size, EC_LPC_ADDR_HOST_ARGS + 2); 85 + outb(args.checksum, EC_LPC_ADDR_HOST_ARGS + 3); 86 + 87 + /* Here we go */ 88 + outb(msg->command, EC_LPC_ADDR_HOST_CMD); 89 + 90 + if (ec_response_timed_out()) { 91 + dev_warn(ec->dev, "EC responsed timed out\n"); 92 + ret = -EIO; 93 + goto done; 94 + } 95 + 96 + /* Check result */ 97 + msg->result = inb(EC_LPC_ADDR_HOST_DATA); 98 + 99 + switch (msg->result) { 100 + case EC_RES_SUCCESS: 101 + break; 102 + case EC_RES_IN_PROGRESS: 103 + ret = -EAGAIN; 104 + dev_dbg(ec->dev, "command 0x%02x in progress\n", 105 + msg->command); 106 + goto done; 107 + default: 108 + dev_dbg(ec->dev, "command 0x%02x returned %d\n", 109 + msg->command, msg->result); 110 + } 111 + 112 + /* Read back args */ 113 + args.flags = inb(EC_LPC_ADDR_HOST_ARGS); 114 + args.command_version = inb(EC_LPC_ADDR_HOST_ARGS + 1); 115 + args.data_size = inb(EC_LPC_ADDR_HOST_ARGS + 2); 116 + args.checksum = inb(EC_LPC_ADDR_HOST_ARGS + 3); 117 + 118 + if (args.data_size > msg->insize) { 119 + dev_err(ec->dev, 120 + "packet too long (%d bytes, expected %d)", 121 + args.data_size, msg->insize); 122 + ret = -ENOSPC; 123 + goto done; 124 + } 125 + 126 + /* Start calculating response checksum */ 127 + csum = msg->command + args.flags + 128 + args.command_version + args.data_size; 129 + 130 + /* Read response and update checksum */ 131 + for (i = 0; i < args.data_size; i++) { 132 + msg->indata[i] = inb(EC_LPC_ADDR_HOST_PARAM + i); 133 + csum += msg->indata[i]; 134 + } 135 + 136 + /* Verify checksum */ 137 + if (args.checksum != (csum & 0xFF)) { 138 + dev_err(ec->dev, 139 + "bad packet checksum, expected %02x, got %02x\n", 140 + args.checksum, csum & 0xFF); 141 + ret = -EBADMSG; 142 + goto done; 143 + } 144 + 145 + /* Return actual amount of data received */ 146 + ret = args.data_size; 147 + done: 148 + return ret; 149 + } 150 + 151 + /* Returns num bytes read, or negative on error. Doesn't need locking. */ 152 + static int cros_ec_lpc_readmem(struct cros_ec_device *ec, unsigned int offset, 153 + unsigned int bytes, void *dest) 154 + { 155 + int i = offset; 156 + char *s = dest; 157 + int cnt = 0; 158 + 159 + if (offset >= EC_MEMMAP_SIZE - bytes) 160 + return -EINVAL; 161 + 162 + /* fixed length */ 163 + if (bytes) { 164 + for (; cnt < bytes; i++, s++, cnt++) 165 + *s = inb(EC_LPC_ADDR_MEMMAP + i); 166 + return cnt; 167 + } 168 + 169 + /* string */ 170 + for (; i < EC_MEMMAP_SIZE; i++, s++) { 171 + *s = inb(EC_LPC_ADDR_MEMMAP + i); 172 + cnt++; 173 + if (!*s) 174 + break; 175 + } 176 + 177 + return cnt; 178 + } 179 + 180 + static int cros_ec_lpc_probe(struct platform_device *pdev) 181 + { 182 + struct device *dev = &pdev->dev; 183 + struct cros_ec_device *ec_dev; 184 + int ret; 185 + 186 + if (!devm_request_region(dev, EC_LPC_ADDR_MEMMAP, EC_MEMMAP_SIZE, 187 + dev_name(dev))) { 188 + dev_err(dev, "couldn't reserve memmap region\n"); 189 + return -EBUSY; 190 + } 191 + 192 + if ((inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID) != 'E') || 193 + (inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID + 1) != 'C')) { 194 + dev_err(dev, "EC ID not detected\n"); 195 + return -ENODEV; 196 + } 197 + 198 + if (!devm_request_region(dev, EC_HOST_CMD_REGION0, 199 + EC_HOST_CMD_REGION_SIZE, dev_name(dev))) { 200 + dev_err(dev, "couldn't reserve region0\n"); 201 + return -EBUSY; 202 + } 203 + if (!devm_request_region(dev, EC_HOST_CMD_REGION1, 204 + EC_HOST_CMD_REGION_SIZE, dev_name(dev))) { 205 + dev_err(dev, "couldn't reserve region1\n"); 206 + return -EBUSY; 207 + } 208 + 209 + ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL); 210 + if (!ec_dev) 211 + return -ENOMEM; 212 + 213 + platform_set_drvdata(pdev, ec_dev); 214 + ec_dev->dev = dev; 215 + ec_dev->ec_name = pdev->name; 216 + ec_dev->phys_name = dev_name(dev); 217 + ec_dev->parent = dev; 218 + ec_dev->cmd_xfer = cros_ec_cmd_xfer_lpc; 219 + ec_dev->cmd_readmem = cros_ec_lpc_readmem; 220 + 221 + ret = cros_ec_register(ec_dev); 222 + if (ret) { 223 + dev_err(dev, "couldn't register ec_dev (%d)\n", ret); 224 + return ret; 225 + } 226 + 227 + return 0; 228 + } 229 + 230 + static int cros_ec_lpc_remove(struct platform_device *pdev) 231 + { 232 + struct cros_ec_device *ec_dev; 233 + 234 + ec_dev = platform_get_drvdata(pdev); 235 + cros_ec_remove(ec_dev); 236 + 237 + return 0; 238 + } 239 + 240 + static struct dmi_system_id cros_ec_lpc_dmi_table[] __initdata = { 241 + { 242 + /* 243 + * Today all Chromebooks/boxes ship with Google_* as version and 244 + * coreboot as bios vendor. No other systems with this 245 + * combination are known to date. 246 + */ 247 + .matches = { 248 + DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"), 249 + DMI_MATCH(DMI_BIOS_VERSION, "Google_"), 250 + }, 251 + }, 252 + { 253 + /* x86-link, the Chromebook Pixel. */ 254 + .matches = { 255 + DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), 256 + DMI_MATCH(DMI_PRODUCT_NAME, "Link"), 257 + }, 258 + }, 259 + { 260 + /* x86-peppy, the Acer C720 Chromebook. */ 261 + .matches = { 262 + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), 263 + DMI_MATCH(DMI_PRODUCT_NAME, "Peppy"), 264 + }, 265 + }, 266 + { /* sentinel */ } 267 + }; 268 + MODULE_DEVICE_TABLE(dmi, cros_ec_lpc_dmi_table); 269 + 270 + static struct platform_driver cros_ec_lpc_driver = { 271 + .driver = { 272 + .name = DRV_NAME, 273 + }, 274 + .probe = cros_ec_lpc_probe, 275 + .remove = cros_ec_lpc_remove, 276 + }; 277 + 278 + static struct platform_device cros_ec_lpc_device = { 279 + .name = DRV_NAME 280 + }; 281 + 282 + static int __init cros_ec_lpc_init(void) 283 + { 284 + int ret; 285 + 286 + if (!dmi_check_system(cros_ec_lpc_dmi_table)) { 287 + pr_err(DRV_NAME ": unsupported system.\n"); 288 + return -ENODEV; 289 + } 290 + 291 + /* Register the driver */ 292 + ret = platform_driver_register(&cros_ec_lpc_driver); 293 + if (ret) { 294 + pr_err(DRV_NAME ": can't register driver: %d\n", ret); 295 + return ret; 296 + } 297 + 298 + /* Register the device, and it'll get hooked up automatically */ 299 + ret = platform_device_register(&cros_ec_lpc_device); 300 + if (ret) { 301 + pr_err(DRV_NAME ": can't register device: %d\n", ret); 302 + platform_driver_unregister(&cros_ec_lpc_driver); 303 + return ret; 304 + } 305 + 306 + return 0; 307 + } 308 + 309 + static void __exit cros_ec_lpc_exit(void) 310 + { 311 + platform_device_unregister(&cros_ec_lpc_device); 312 + platform_driver_unregister(&cros_ec_lpc_driver); 313 + } 314 + 315 + module_init(cros_ec_lpc_init); 316 + module_exit(cros_ec_lpc_exit); 317 + 318 + MODULE_LICENSE("GPL"); 319 + MODULE_DESCRIPTION("ChromeOS EC LPC driver");
+271
drivers/platform/chrome/cros_ec_sysfs.c
··· 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/stat.h> 33 + #include <linux/types.h> 34 + #include <linux/uaccess.h> 35 + 36 + #include "cros_ec_dev.h" 37 + 38 + /* Accessor functions */ 39 + 40 + static ssize_t show_ec_reboot(struct device *dev, 41 + struct device_attribute *attr, char *buf) 42 + { 43 + int count = 0; 44 + 45 + count += scnprintf(buf + count, PAGE_SIZE - count, 46 + "ro|rw|cancel|cold|disable-jump|hibernate"); 47 + count += scnprintf(buf + count, PAGE_SIZE - count, 48 + " [at-shutdown]\n"); 49 + return count; 50 + } 51 + 52 + static ssize_t store_ec_reboot(struct device *dev, 53 + struct device_attribute *attr, 54 + const char *buf, size_t count) 55 + { 56 + static const struct { 57 + const char * const str; 58 + uint8_t cmd; 59 + uint8_t flags; 60 + } words[] = { 61 + {"cancel", EC_REBOOT_CANCEL, 0}, 62 + {"ro", EC_REBOOT_JUMP_RO, 0}, 63 + {"rw", EC_REBOOT_JUMP_RW, 0}, 64 + {"cold", EC_REBOOT_COLD, 0}, 65 + {"disable-jump", EC_REBOOT_DISABLE_JUMP, 0}, 66 + {"hibernate", EC_REBOOT_HIBERNATE, 0}, 67 + {"at-shutdown", -1, EC_REBOOT_FLAG_ON_AP_SHUTDOWN}, 68 + }; 69 + struct cros_ec_command msg = { 0 }; 70 + struct ec_params_reboot_ec *param = 71 + (struct ec_params_reboot_ec *)msg.outdata; 72 + int got_cmd = 0, offset = 0; 73 + int i; 74 + int ret; 75 + struct cros_ec_device *ec = dev_get_drvdata(dev); 76 + 77 + param->flags = 0; 78 + while (1) { 79 + /* Find word to start scanning */ 80 + while (buf[offset] && isspace(buf[offset])) 81 + offset++; 82 + if (!buf[offset]) 83 + break; 84 + 85 + for (i = 0; i < ARRAY_SIZE(words); i++) { 86 + if (!strncasecmp(words[i].str, buf+offset, 87 + strlen(words[i].str))) { 88 + if (words[i].flags) { 89 + param->flags |= words[i].flags; 90 + } else { 91 + param->cmd = words[i].cmd; 92 + got_cmd = 1; 93 + } 94 + break; 95 + } 96 + } 97 + 98 + /* On to the next word, if any */ 99 + while (buf[offset] && !isspace(buf[offset])) 100 + offset++; 101 + } 102 + 103 + if (!got_cmd) 104 + return -EINVAL; 105 + 106 + msg.command = EC_CMD_REBOOT_EC; 107 + msg.outsize = sizeof(param); 108 + ret = cros_ec_cmd_xfer(ec, &msg); 109 + if (ret < 0) 110 + return ret; 111 + if (msg.result != EC_RES_SUCCESS) { 112 + dev_dbg(ec->dev, "EC result %d\n", msg.result); 113 + return -EINVAL; 114 + } 115 + 116 + return count; 117 + } 118 + 119 + static ssize_t show_ec_version(struct device *dev, 120 + struct device_attribute *attr, char *buf) 121 + { 122 + static const char * const image_names[] = {"unknown", "RO", "RW"}; 123 + struct ec_response_get_version *r_ver; 124 + struct ec_response_get_chip_info *r_chip; 125 + struct ec_response_board_version *r_board; 126 + struct cros_ec_command msg = { 0 }; 127 + int ret; 128 + int count = 0; 129 + struct cros_ec_device *ec = dev_get_drvdata(dev); 130 + 131 + /* Get versions. RW may change. */ 132 + msg.command = EC_CMD_GET_VERSION; 133 + msg.insize = sizeof(*r_ver); 134 + ret = cros_ec_cmd_xfer(ec, &msg); 135 + if (ret < 0) 136 + return ret; 137 + if (msg.result != EC_RES_SUCCESS) 138 + return scnprintf(buf, PAGE_SIZE, 139 + "ERROR: EC returned %d\n", msg.result); 140 + 141 + r_ver = (struct ec_response_get_version *)msg.indata; 142 + /* Strings should be null-terminated, but let's be sure. */ 143 + r_ver->version_string_ro[sizeof(r_ver->version_string_ro) - 1] = '\0'; 144 + r_ver->version_string_rw[sizeof(r_ver->version_string_rw) - 1] = '\0'; 145 + count += scnprintf(buf + count, PAGE_SIZE - count, 146 + "RO version: %s\n", r_ver->version_string_ro); 147 + count += scnprintf(buf + count, PAGE_SIZE - count, 148 + "RW version: %s\n", r_ver->version_string_rw); 149 + count += scnprintf(buf + count, PAGE_SIZE - count, 150 + "Firmware copy: %s\n", 151 + (r_ver->current_image < ARRAY_SIZE(image_names) ? 152 + image_names[r_ver->current_image] : "?")); 153 + 154 + /* Get build info. */ 155 + msg.command = EC_CMD_GET_BUILD_INFO; 156 + msg.insize = sizeof(msg.indata); 157 + ret = cros_ec_cmd_xfer(ec, &msg); 158 + if (ret < 0) 159 + count += scnprintf(buf + count, PAGE_SIZE - count, 160 + "Build info: XFER ERROR %d\n", ret); 161 + else if (msg.result != EC_RES_SUCCESS) 162 + count += scnprintf(buf + count, PAGE_SIZE - count, 163 + "Build info: EC error %d\n", msg.result); 164 + else { 165 + msg.indata[sizeof(msg.indata) - 1] = '\0'; 166 + count += scnprintf(buf + count, PAGE_SIZE - count, 167 + "Build info: %s\n", msg.indata); 168 + } 169 + 170 + /* Get chip info. */ 171 + msg.command = EC_CMD_GET_CHIP_INFO; 172 + msg.insize = sizeof(*r_chip); 173 + ret = cros_ec_cmd_xfer(ec, &msg); 174 + if (ret < 0) 175 + count += scnprintf(buf + count, PAGE_SIZE - count, 176 + "Chip info: XFER ERROR %d\n", ret); 177 + else if (msg.result != EC_RES_SUCCESS) 178 + count += scnprintf(buf + count, PAGE_SIZE - count, 179 + "Chip info: EC error %d\n", msg.result); 180 + else { 181 + r_chip = (struct ec_response_get_chip_info *)msg.indata; 182 + 183 + r_chip->vendor[sizeof(r_chip->vendor) - 1] = '\0'; 184 + r_chip->name[sizeof(r_chip->name) - 1] = '\0'; 185 + r_chip->revision[sizeof(r_chip->revision) - 1] = '\0'; 186 + count += scnprintf(buf + count, PAGE_SIZE - count, 187 + "Chip vendor: %s\n", r_chip->vendor); 188 + count += scnprintf(buf + count, PAGE_SIZE - count, 189 + "Chip name: %s\n", r_chip->name); 190 + count += scnprintf(buf + count, PAGE_SIZE - count, 191 + "Chip revision: %s\n", r_chip->revision); 192 + } 193 + 194 + /* Get board version */ 195 + msg.command = EC_CMD_GET_BOARD_VERSION; 196 + msg.insize = sizeof(*r_board); 197 + ret = cros_ec_cmd_xfer(ec, &msg); 198 + if (ret < 0) 199 + count += scnprintf(buf + count, PAGE_SIZE - count, 200 + "Board version: XFER ERROR %d\n", ret); 201 + else if (msg.result != EC_RES_SUCCESS) 202 + count += scnprintf(buf + count, PAGE_SIZE - count, 203 + "Board version: EC error %d\n", msg.result); 204 + else { 205 + r_board = (struct ec_response_board_version *)msg.indata; 206 + 207 + count += scnprintf(buf + count, PAGE_SIZE - count, 208 + "Board version: %d\n", 209 + r_board->board_version); 210 + } 211 + 212 + return count; 213 + } 214 + 215 + static ssize_t show_ec_flashinfo(struct device *dev, 216 + struct device_attribute *attr, char *buf) 217 + { 218 + struct ec_response_flash_info *resp; 219 + struct cros_ec_command msg = { 0 }; 220 + int ret; 221 + struct cros_ec_device *ec = dev_get_drvdata(dev); 222 + 223 + /* The flash info shouldn't ever change, but ask each time anyway. */ 224 + msg.command = EC_CMD_FLASH_INFO; 225 + msg.insize = sizeof(*resp); 226 + ret = cros_ec_cmd_xfer(ec, &msg); 227 + if (ret < 0) 228 + return ret; 229 + if (msg.result != EC_RES_SUCCESS) 230 + return scnprintf(buf, PAGE_SIZE, 231 + "ERROR: EC returned %d\n", msg.result); 232 + 233 + resp = (struct ec_response_flash_info *)msg.indata; 234 + 235 + return scnprintf(buf, PAGE_SIZE, 236 + "FlashSize %d\nWriteSize %d\n" 237 + "EraseSize %d\nProtectSize %d\n", 238 + resp->flash_size, resp->write_block_size, 239 + resp->erase_block_size, resp->protect_block_size); 240 + } 241 + 242 + /* Module initialization */ 243 + 244 + static DEVICE_ATTR(reboot, S_IWUSR | S_IRUGO, show_ec_reboot, store_ec_reboot); 245 + static DEVICE_ATTR(version, S_IRUGO, show_ec_version, NULL); 246 + static DEVICE_ATTR(flashinfo, S_IRUGO, show_ec_flashinfo, NULL); 247 + 248 + static struct attribute *__ec_attrs[] = { 249 + &dev_attr_reboot.attr, 250 + &dev_attr_version.attr, 251 + &dev_attr_flashinfo.attr, 252 + NULL, 253 + }; 254 + 255 + static struct attribute_group ec_attr_group = { 256 + .attrs = __ec_attrs, 257 + }; 258 + 259 + void ec_dev_sysfs_init(struct cros_ec_device *ec) 260 + { 261 + int error; 262 + 263 + error = sysfs_create_group(&ec->vdev->kobj, &ec_attr_group); 264 + if (error) 265 + pr_warn("failed to create group: %d\n", error); 266 + } 267 + 268 + void ec_dev_sysfs_remove(struct cros_ec_device *ec) 269 + { 270 + sysfs_remove_group(&ec->vdev->kobj, &ec_attr_group); 271 + }
+18 -5
include/linux/mfd/cros_ec.h
··· 16 16 #ifndef __LINUX_MFD_CROS_EC_H 17 17 #define __LINUX_MFD_CROS_EC_H 18 18 19 + #include <linux/cdev.h> 19 20 #include <linux/notifier.h> 20 21 #include <linux/mfd/cros_ec_commands.h> 21 22 #include <linux/mutex.h> ··· 39 38 /* 40 39 * @version: Command version number (often 0) 41 40 * @command: Command to send (EC_CMD_...) 42 - * @outdata: Outgoing data to EC 43 41 * @outsize: Outgoing length in bytes 44 - * @indata: Where to put the incoming data from EC 45 42 * @insize: Max number of bytes to accept from EC 46 43 * @result: EC's response to the command (separate from communication failure) 44 + * @outdata: Outgoing data to EC 45 + * @indata: Where to put the incoming data from EC 47 46 */ 48 47 struct cros_ec_command { 49 48 uint32_t version; 50 49 uint32_t command; 51 - uint8_t *outdata; 52 50 uint32_t outsize; 53 - uint8_t *indata; 54 51 uint32_t insize; 55 52 uint32_t result; 53 + uint8_t outdata[EC_PROTO2_MAX_PARAM_SIZE]; 54 + uint8_t indata[EC_PROTO2_MAX_PARAM_SIZE]; 56 55 }; 57 56 58 57 /** ··· 60 59 * 61 60 * @ec_name: name of EC device (e.g. 'chromeos-ec') 62 61 * @phys_name: name of physical comms layer (e.g. 'i2c-4') 63 - * @dev: Device pointer 62 + * @dev: Device pointer for physical comms device 63 + * @vdev: Device pointer for virtual comms device 64 + * @cdev: Character device structure for virtual comms device 64 65 * @was_wake_device: true if this device was set to wake the system from 65 66 * sleep at the last suspend 67 + * @cmd_readmem: direct read of the EC memory-mapped region, if supported 68 + * @offset is within EC_LPC_ADDR_MEMMAP region. 69 + * @bytes: number of bytes to read. zero means "read a string" (including 70 + * the trailing '\0'). At most only EC_MEMMAP_SIZE bytes can be read. 71 + * Caller must ensure that the buffer is large enough for the result when 72 + * reading a string. 66 73 * 67 74 * @priv: Private data 68 75 * @irq: Interrupt to use ··· 99 90 const char *ec_name; 100 91 const char *phys_name; 101 92 struct device *dev; 93 + struct device *vdev; 94 + struct cdev cdev; 102 95 bool was_wake_device; 103 96 struct class *cros_class; 97 + int (*cmd_readmem)(struct cros_ec_device *ec, unsigned int offset, 98 + unsigned int bytes, void *dest); 104 99 105 100 /* These are used to implement the platform-specific interface */ 106 101 void *priv;