aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBill Richardson <wfrichar@chromium.org>2015-02-02 06:26:24 -0500
committerOlof Johansson <olof@lixom.net>2015-02-26 18:45:02 -0500
commitec2f33ab582bf6bb14ef3e1ce4ed5f1973edd9f6 (patch)
tree4e34400849ea7834983ddc864da67036b5e53ed0
parent05c11ac4e0712def44cccbff82ad980d9e1d1b80 (diff)
platform/chrome: Add cros_ec_lpc driver for x86 devices
Chromebooks have an Embedded Controller (EC) that is used to implement various functions such as keyboard, power and battery. The AP can communicate with the EC through different bus types such as I2C, SPI or LPC. The cros_ec mfd driver is then composed of a core driver that register the sub-devices as mfd cells and provide a high level communication interface that is used by the rest of the kernel and bus specific interfaces modules. Each connection method then has its own driver, which register with the EC driver interface-agnostic interface. Currently, there are drivers to communicate with the EC over I2C and SPI and this driver adds support for LPC. Signed-off-by: Bill Richardson <wfrichar@chromium.org> Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk> Tested-by: Gwendal Grignou <gwendal@chromium.org> Reviewed-by: Gwendal Grignou <gwendal@chromium.org> Signed-off-by: Olof Johansson <olof@lixom.net>
-rw-r--r--drivers/platform/chrome/Kconfig12
-rw-r--r--drivers/platform/chrome/Makefile1
-rw-r--r--drivers/platform/chrome/cros_ec_lpc.c319
3 files changed, 332 insertions, 0 deletions
diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig
index 440ed776efd4..6c5f5a1e1175 100644
--- a/drivers/platform/chrome/Kconfig
+++ b/drivers/platform/chrome/Kconfig
@@ -39,4 +39,16 @@ config CHROMEOS_PSTORE
39 The module will be called chromeos_pstore. 39 The module will be called chromeos_pstore.
40 40
41 41
42config CROS_EC_LPC
43 tristate "ChromeOS Embedded Controller (LPC)"
44 depends on MFD_CROS_EC
45 help
46 If you say Y here, you get support for talking to the ChromeOS EC
47 over an LPC bus. This uses a simple byte-level protocol with a
48 checksum. This is used for userspace access only. The kernel
49 typically has its own communication methods.
50
51 To compile this driver as a module, choose M here: the
52 module will be called cros_ec_lpc.
53
42endif # CHROMEOS_PLATFORMS 54endif # CHROMEOS_PLATFORMS
diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile
index 2b860ca7450f..03c0260369d9 100644
--- a/drivers/platform/chrome/Makefile
+++ b/drivers/platform/chrome/Makefile
@@ -1,3 +1,4 @@
1 1
2obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o 2obj-$(CONFIG_CHROMEOS_LAPTOP) += chromeos_laptop.o
3obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o 3obj-$(CONFIG_CHROMEOS_PSTORE) += chromeos_pstore.o
4obj-$(CONFIG_CROS_EC_LPC) += cros_ec_lpc.o
diff --git a/drivers/platform/chrome/cros_ec_lpc.c b/drivers/platform/chrome/cros_ec_lpc.c
new file mode 100644
index 000000000000..822fdb36ded9
--- /dev/null
+++ b/drivers/platform/chrome/cros_ec_lpc.c
@@ -0,0 +1,319 @@
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/mfd/cros_ec.h>
27#include <linux/mfd/cros_ec_commands.h>
28#include <linux/module.h>
29#include <linux/platform_device.h>
30#include <linux/printk.h>
31
32#define DRV_NAME "cros_ec_lpc"
33
34static int ec_response_timed_out(void)
35{
36 unsigned long one_second = jiffies + HZ;
37
38 usleep_range(200, 300);
39 do {
40 if (!(inb(EC_LPC_ADDR_HOST_CMD) & EC_LPC_STATUS_BUSY_MASK))
41 return 0;
42 usleep_range(100, 200);
43 } while (time_before(jiffies, one_second));
44
45 return 1;
46}
47
48static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec,
49 struct cros_ec_command *msg)
50{
51 struct ec_lpc_host_args args;
52 int csum;
53 int i;
54 int ret = 0;
55
56 if (msg->outsize > EC_PROTO2_MAX_PARAM_SIZE ||
57 msg->insize > EC_PROTO2_MAX_PARAM_SIZE) {
58 dev_err(ec->dev,
59 "invalid buffer sizes (out %d, in %d)\n",
60 msg->outsize, msg->insize);
61 return -EINVAL;
62 }
63
64 /* Now actually send the command to the EC and get the result */
65 args.flags = EC_HOST_ARGS_FLAG_FROM_HOST;
66 args.command_version = msg->version;
67 args.data_size = msg->outsize;
68
69 /* Initialize checksum */
70 csum = msg->command + args.flags +
71 args.command_version + args.data_size;
72
73 /* Copy data and update checksum */
74 for (i = 0; i < msg->outsize; i++) {
75 outb(msg->outdata[i], EC_LPC_ADDR_HOST_PARAM + i);
76 csum += msg->outdata[i];
77 }
78
79 /* Finalize checksum and write args */
80 args.checksum = csum & 0xFF;
81 outb(args.flags, EC_LPC_ADDR_HOST_ARGS);
82 outb(args.command_version, EC_LPC_ADDR_HOST_ARGS + 1);
83 outb(args.data_size, EC_LPC_ADDR_HOST_ARGS + 2);
84 outb(args.checksum, EC_LPC_ADDR_HOST_ARGS + 3);
85
86 /* Here we go */
87 outb(msg->command, EC_LPC_ADDR_HOST_CMD);
88
89 if (ec_response_timed_out()) {
90 dev_warn(ec->dev, "EC responsed timed out\n");
91 ret = -EIO;
92 goto done;
93 }
94
95 /* Check result */
96 msg->result = inb(EC_LPC_ADDR_HOST_DATA);
97
98 switch (msg->result) {
99 case EC_RES_SUCCESS:
100 break;
101 case EC_RES_IN_PROGRESS:
102 ret = -EAGAIN;
103 dev_dbg(ec->dev, "command 0x%02x in progress\n",
104 msg->command);
105 goto done;
106 default:
107 dev_dbg(ec->dev, "command 0x%02x returned %d\n",
108 msg->command, msg->result);
109 }
110
111 /* Read back args */
112 args.flags = inb(EC_LPC_ADDR_HOST_ARGS);
113 args.command_version = inb(EC_LPC_ADDR_HOST_ARGS + 1);
114 args.data_size = inb(EC_LPC_ADDR_HOST_ARGS + 2);
115 args.checksum = inb(EC_LPC_ADDR_HOST_ARGS + 3);
116
117 if (args.data_size > msg->insize) {
118 dev_err(ec->dev,
119 "packet too long (%d bytes, expected %d)",
120 args.data_size, msg->insize);
121 ret = -ENOSPC;
122 goto done;
123 }
124
125 /* Start calculating response checksum */
126 csum = msg->command + args.flags +
127 args.command_version + args.data_size;
128
129 /* Read response and update checksum */
130 for (i = 0; i < args.data_size; i++) {
131 msg->indata[i] = inb(EC_LPC_ADDR_HOST_PARAM + i);
132 csum += msg->indata[i];
133 }
134
135 /* Verify checksum */
136 if (args.checksum != (csum & 0xFF)) {
137 dev_err(ec->dev,
138 "bad packet checksum, expected %02x, got %02x\n",
139 args.checksum, csum & 0xFF);
140 ret = -EBADMSG;
141 goto done;
142 }
143
144 /* Return actual amount of data received */
145 ret = args.data_size;
146done:
147 return ret;
148}
149
150/* Returns num bytes read, or negative on error. Doesn't need locking. */
151static int cros_ec_lpc_readmem(struct cros_ec_device *ec, unsigned int offset,
152 unsigned int bytes, void *dest)
153{
154 int i = offset;
155 char *s = dest;
156 int cnt = 0;
157
158 if (offset >= EC_MEMMAP_SIZE - bytes)
159 return -EINVAL;
160
161 /* fixed length */
162 if (bytes) {
163 for (; cnt < bytes; i++, s++, cnt++)
164 *s = inb(EC_LPC_ADDR_MEMMAP + i);
165 return cnt;
166 }
167
168 /* string */
169 for (; i < EC_MEMMAP_SIZE; i++, s++) {
170 *s = inb(EC_LPC_ADDR_MEMMAP + i);
171 cnt++;
172 if (!*s)
173 break;
174 }
175
176 return cnt;
177}
178
179static int cros_ec_lpc_probe(struct platform_device *pdev)
180{
181 struct device *dev = &pdev->dev;
182 struct cros_ec_device *ec_dev;
183 int ret;
184
185 if (!devm_request_region(dev, EC_LPC_ADDR_MEMMAP, EC_MEMMAP_SIZE,
186 dev_name(dev))) {
187 dev_err(dev, "couldn't reserve memmap region\n");
188 return -EBUSY;
189 }
190
191 if ((inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID) != 'E') ||
192 (inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID + 1) != 'C')) {
193 dev_err(dev, "EC ID not detected\n");
194 return -ENODEV;
195 }
196
197 if (!devm_request_region(dev, EC_HOST_CMD_REGION0,
198 EC_HOST_CMD_REGION_SIZE, dev_name(dev))) {
199 dev_err(dev, "couldn't reserve region0\n");
200 return -EBUSY;
201 }
202 if (!devm_request_region(dev, EC_HOST_CMD_REGION1,
203 EC_HOST_CMD_REGION_SIZE, dev_name(dev))) {
204 dev_err(dev, "couldn't reserve region1\n");
205 return -EBUSY;
206 }
207
208 ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL);
209 if (!ec_dev)
210 return -ENOMEM;
211
212 platform_set_drvdata(pdev, ec_dev);
213 ec_dev->dev = dev;
214 ec_dev->ec_name = pdev->name;
215 ec_dev->phys_name = dev_name(dev);
216 ec_dev->parent = dev;
217 ec_dev->cmd_xfer = cros_ec_cmd_xfer_lpc;
218 ec_dev->cmd_readmem = cros_ec_lpc_readmem;
219
220 ret = cros_ec_register(ec_dev);
221 if (ret) {
222 dev_err(dev, "couldn't register ec_dev (%d)\n", ret);
223 return ret;
224 }
225
226 return 0;
227}
228
229static int cros_ec_lpc_remove(struct platform_device *pdev)
230{
231 struct cros_ec_device *ec_dev;
232
233 ec_dev = platform_get_drvdata(pdev);
234 cros_ec_remove(ec_dev);
235
236 return 0;
237}
238
239static struct dmi_system_id cros_ec_lpc_dmi_table[] __initdata = {
240 {
241 /*
242 * Today all Chromebooks/boxes ship with Google_* as version and
243 * coreboot as bios vendor. No other systems with this
244 * combination are known to date.
245 */
246 .matches = {
247 DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"),
248 DMI_MATCH(DMI_BIOS_VERSION, "Google_"),
249 },
250 },
251 {
252 /* x86-link, the Chromebook Pixel. */
253 .matches = {
254 DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
255 DMI_MATCH(DMI_PRODUCT_NAME, "Link"),
256 },
257 },
258 {
259 /* x86-peppy, the Acer C720 Chromebook. */
260 .matches = {
261 DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
262 DMI_MATCH(DMI_PRODUCT_NAME, "Peppy"),
263 },
264 },
265 { /* sentinel */ }
266};
267MODULE_DEVICE_TABLE(dmi, cros_ec_lpc_dmi_table);
268
269static struct platform_driver cros_ec_lpc_driver = {
270 .driver = {
271 .name = DRV_NAME,
272 .owner = THIS_MODULE,
273 },
274 .probe = cros_ec_lpc_probe,
275 .remove = cros_ec_lpc_remove,
276};
277
278static struct platform_device cros_ec_lpc_device = {
279 .name = DRV_NAME
280};
281
282static 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
309static 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
315module_init(cros_ec_lpc_init);
316module_exit(cros_ec_lpc_exit);
317
318MODULE_LICENSE("GPL");
319MODULE_DESCRIPTION("ChromeOS EC LPC driver");