diff options
-rw-r--r-- | Documentation/ioctl/ioctl-number.txt | 1 | ||||
-rw-r--r-- | drivers/platform/chrome/Kconfig | 14 | ||||
-rw-r--r-- | drivers/platform/chrome/Makefile | 1 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_dev.c | 268 | ||||
-rw-r--r-- | drivers/platform/chrome/cros_ec_dev.h | 47 |
5 files changed, 328 insertions, 3 deletions
diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 8136e1fd30fd..51f4221657bf 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt | |||
@@ -321,6 +321,7 @@ Code Seq#(hex) Include File Comments | |||
321 | 0xDB 00-0F drivers/char/mwave/mwavepub.h | 321 | 0xDB 00-0F drivers/char/mwave/mwavepub.h |
322 | 0xDD 00-3F ZFCP device driver see drivers/s390/scsi/ | 322 | 0xDD 00-3F ZFCP device driver see drivers/s390/scsi/ |
323 | <mailto:aherrman@de.ibm.com> | 323 | <mailto:aherrman@de.ibm.com> |
324 | 0xEC 00-01 drivers/platform/chrome/cros_ec_dev.h ChromeOS EC driver | ||
324 | 0xF3 00-3F drivers/usb/misc/sisusbvga/sisusb.h sisfb (in development) | 325 | 0xF3 00-3F drivers/usb/misc/sisusbvga/sisusb.h sisfb (in development) |
325 | <mailto:thomas@winischhofer.net> | 326 | <mailto:thomas@winischhofer.net> |
326 | 0xF4 00-1F video/mbxfb.h mbxfb | 327 | 0xF4 00-1F video/mbxfb.h mbxfb |
diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig index 6c5f5a1e1175..d4befbffae85 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig | |||
@@ -4,7 +4,7 @@ | |||
4 | 4 | ||
5 | menuconfig CHROME_PLATFORMS | 5 | menuconfig CHROME_PLATFORMS |
6 | bool "Platform support for Chrome hardware" | 6 | bool "Platform support for Chrome hardware" |
7 | depends on X86 | 7 | depends on X86 || ARM |
8 | ---help--- | 8 | ---help--- |
9 | Say Y here to get to see options for platform support for | 9 | Say Y here to get to see options for platform support for |
10 | various Chromebooks and Chromeboxes. This option alone does | 10 | various Chromebooks and Chromeboxes. This option alone does |
@@ -16,8 +16,7 @@ if CHROME_PLATFORMS | |||
16 | 16 | ||
17 | config CHROMEOS_LAPTOP | 17 | config CHROMEOS_LAPTOP |
18 | tristate "Chrome OS Laptop" | 18 | tristate "Chrome OS Laptop" |
19 | depends on I2C | 19 | depends on I2C && DMI && X86 |
20 | depends on DMI | ||
21 | ---help--- | 20 | ---help--- |
22 | This driver instantiates i2c and smbus devices such as | 21 | This driver instantiates i2c and smbus devices such as |
23 | light sensors and touchpads. | 22 | light sensors and touchpads. |
@@ -27,6 +26,7 @@ config CHROMEOS_LAPTOP | |||
27 | 26 | ||
28 | config CHROMEOS_PSTORE | 27 | config CHROMEOS_PSTORE |
29 | tristate "Chrome OS pstore support" | 28 | tristate "Chrome OS pstore support" |
29 | depends on X86 | ||
30 | ---help--- | 30 | ---help--- |
31 | This module instantiates the persistent storage on x86 ChromeOS | 31 | This module instantiates the persistent storage on x86 ChromeOS |
32 | devices. It can be used to store away console logs and crash | 32 | devices. It can be used to store away console logs and crash |
@@ -38,6 +38,14 @@ config CHROMEOS_PSTORE | |||
38 | If you have a supported Chromebook, choose Y or M here. | 38 | If you have a supported Chromebook, choose Y or M here. |
39 | The module will be called chromeos_pstore. | 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 | config CROS_EC_LPC | 50 | config CROS_EC_LPC |
43 | tristate "ChromeOS Embedded Controller (LPC)" | 51 | tristate "ChromeOS Embedded Controller (LPC)" |
diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile index 03c0260369d9..ec682900f889 100644 --- a/drivers/platform/chrome/Makefile +++ b/drivers/platform/chrome/Makefile | |||
@@ -1,4 +1,5 @@ | |||
1 | 1 | ||
2 | obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o | 2 | obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o |
3 | obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o | 3 | obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o |
4 | obj-$(CONFIG_CROS_EC_CHARDEV) += cros_ec_dev.o | ||
4 | obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpc.o | 5 | obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpc.o |
diff --git a/drivers/platform/chrome/cros_ec_dev.c b/drivers/platform/chrome/cros_ec_dev.c new file mode 100644 index 000000000000..94c1442d5104 --- /dev/null +++ b/drivers/platform/chrome/cros_ec_dev.c | |||
@@ -0,0 +1,268 @@ | |||
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"); | ||
diff --git a/drivers/platform/chrome/cros_ec_dev.h b/drivers/platform/chrome/cros_ec_dev.h new file mode 100644 index 000000000000..15c54c4c5531 --- /dev/null +++ b/drivers/platform/chrome/cros_ec_dev.h | |||
@@ -0,0 +1,47 @@ | |||
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_ */ | ||