]>
Commit | Line | Data |
---|---|---|
ec2f33ab BR |
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 | ||
12278dc7 | 24 | #include <linux/acpi.h> |
ec2f33ab BR |
25 | #include <linux/dmi.h> |
26 | #include <linux/delay.h> | |
18d0dc24 | 27 | #include <linux/io.h> |
ec2f33ab BR |
28 | #include <linux/mfd/cros_ec.h> |
29 | #include <linux/mfd/cros_ec_commands.h> | |
bce70fef | 30 | #include <linux/mfd/cros_ec_lpc_reg.h> |
ec2f33ab BR |
31 | #include <linux/module.h> |
32 | #include <linux/platform_device.h> | |
33 | #include <linux/printk.h> | |
34 | ||
bce70fef | 35 | #define DRV_NAME "cros_ec_lpcs" |
12278dc7 | 36 | #define ACPI_DRV_NAME "GOOG0004" |
ec2f33ab BR |
37 | |
38 | static int ec_response_timed_out(void) | |
39 | { | |
40 | unsigned long one_second = jiffies + HZ; | |
bce70fef | 41 | u8 data; |
ec2f33ab BR |
42 | |
43 | usleep_range(200, 300); | |
44 | do { | |
bce70fef SN |
45 | if (!(cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_CMD, 1, &data) & |
46 | EC_LPC_STATUS_BUSY_MASK)) | |
ec2f33ab BR |
47 | return 0; |
48 | usleep_range(100, 200); | |
49 | } while (time_before(jiffies, one_second)); | |
50 | ||
51 | return 1; | |
52 | } | |
53 | ||
d3654070 SB |
54 | static int cros_ec_pkt_xfer_lpc(struct cros_ec_device *ec, |
55 | struct cros_ec_command *msg) | |
56 | { | |
57 | struct ec_host_request *request; | |
58 | struct ec_host_response response; | |
bce70fef | 59 | u8 sum; |
d3654070 SB |
60 | int ret = 0; |
61 | u8 *dout; | |
62 | ||
63 | ret = cros_ec_prepare_tx(ec, msg); | |
64 | ||
65 | /* Write buffer */ | |
bce70fef | 66 | cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_PACKET, ret, ec->dout); |
d3654070 SB |
67 | |
68 | request = (struct ec_host_request *)ec->dout; | |
69 | ||
70 | /* Here we go */ | |
bce70fef SN |
71 | sum = EC_COMMAND_PROTOCOL_3; |
72 | cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_CMD, 1, &sum); | |
d3654070 SB |
73 | |
74 | if (ec_response_timed_out()) { | |
75 | dev_warn(ec->dev, "EC responsed timed out\n"); | |
76 | ret = -EIO; | |
77 | goto done; | |
78 | } | |
79 | ||
80 | /* Check result */ | |
bce70fef | 81 | msg->result = cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_DATA, 1, &sum); |
d3654070 SB |
82 | ret = cros_ec_check_result(ec, msg); |
83 | if (ret) | |
84 | goto done; | |
85 | ||
86 | /* Read back response */ | |
87 | dout = (u8 *)&response; | |
bce70fef SN |
88 | sum = cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_PACKET, sizeof(response), |
89 | dout); | |
d3654070 SB |
90 | |
91 | msg->result = response.result; | |
92 | ||
93 | if (response.data_len > msg->insize) { | |
94 | dev_err(ec->dev, | |
95 | "packet too long (%d bytes, expected %d)", | |
96 | response.data_len, msg->insize); | |
97 | ret = -EMSGSIZE; | |
98 | goto done; | |
99 | } | |
100 | ||
101 | /* Read response and process checksum */ | |
bce70fef SN |
102 | sum += cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_PACKET + |
103 | sizeof(response), response.data_len, | |
104 | msg->data); | |
d3654070 SB |
105 | |
106 | if (sum) { | |
107 | dev_err(ec->dev, | |
108 | "bad packet checksum %02x\n", | |
109 | response.checksum); | |
110 | ret = -EBADMSG; | |
111 | goto done; | |
112 | } | |
113 | ||
114 | /* Return actual amount of data received */ | |
115 | ret = response.data_len; | |
116 | done: | |
117 | return ret; | |
118 | } | |
119 | ||
ec2f33ab BR |
120 | static int cros_ec_cmd_xfer_lpc(struct cros_ec_device *ec, |
121 | struct cros_ec_command *msg) | |
122 | { | |
123 | struct ec_lpc_host_args args; | |
bce70fef | 124 | u8 sum; |
ec2f33ab BR |
125 | int ret = 0; |
126 | ||
127 | if (msg->outsize > EC_PROTO2_MAX_PARAM_SIZE || | |
128 | msg->insize > EC_PROTO2_MAX_PARAM_SIZE) { | |
129 | dev_err(ec->dev, | |
130 | "invalid buffer sizes (out %d, in %d)\n", | |
131 | msg->outsize, msg->insize); | |
132 | return -EINVAL; | |
133 | } | |
134 | ||
135 | /* Now actually send the command to the EC and get the result */ | |
136 | args.flags = EC_HOST_ARGS_FLAG_FROM_HOST; | |
137 | args.command_version = msg->version; | |
138 | args.data_size = msg->outsize; | |
139 | ||
140 | /* Initialize checksum */ | |
bce70fef | 141 | sum = msg->command + args.flags + args.command_version + args.data_size; |
ec2f33ab BR |
142 | |
143 | /* Copy data and update checksum */ | |
bce70fef SN |
144 | sum += cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_PARAM, msg->outsize, |
145 | msg->data); | |
ec2f33ab BR |
146 | |
147 | /* Finalize checksum and write args */ | |
bce70fef SN |
148 | args.checksum = sum; |
149 | cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_ARGS, sizeof(args), | |
150 | (u8 *)&args); | |
ec2f33ab BR |
151 | |
152 | /* Here we go */ | |
bce70fef SN |
153 | sum = msg->command; |
154 | cros_ec_lpc_write_bytes(EC_LPC_ADDR_HOST_CMD, 1, &sum); | |
ec2f33ab BR |
155 | |
156 | if (ec_response_timed_out()) { | |
157 | dev_warn(ec->dev, "EC responsed timed out\n"); | |
158 | ret = -EIO; | |
159 | goto done; | |
160 | } | |
161 | ||
162 | /* Check result */ | |
bce70fef | 163 | msg->result = cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_DATA, 1, &sum); |
fbf40727 JMC |
164 | ret = cros_ec_check_result(ec, msg); |
165 | if (ret) | |
ec2f33ab | 166 | goto done; |
ec2f33ab BR |
167 | |
168 | /* Read back args */ | |
bce70fef SN |
169 | cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_ARGS, sizeof(args), |
170 | (u8 *)&args); | |
ec2f33ab BR |
171 | |
172 | if (args.data_size > msg->insize) { | |
173 | dev_err(ec->dev, | |
174 | "packet too long (%d bytes, expected %d)", | |
175 | args.data_size, msg->insize); | |
176 | ret = -ENOSPC; | |
177 | goto done; | |
178 | } | |
179 | ||
180 | /* Start calculating response checksum */ | |
bce70fef | 181 | sum = msg->command + args.flags + args.command_version + args.data_size; |
ec2f33ab BR |
182 | |
183 | /* Read response and update checksum */ | |
bce70fef SN |
184 | sum += cros_ec_lpc_read_bytes(EC_LPC_ADDR_HOST_PARAM, args.data_size, |
185 | msg->data); | |
ec2f33ab BR |
186 | |
187 | /* Verify checksum */ | |
bce70fef | 188 | if (args.checksum != sum) { |
ec2f33ab BR |
189 | dev_err(ec->dev, |
190 | "bad packet checksum, expected %02x, got %02x\n", | |
bce70fef | 191 | args.checksum, sum); |
ec2f33ab BR |
192 | ret = -EBADMSG; |
193 | goto done; | |
194 | } | |
195 | ||
196 | /* Return actual amount of data received */ | |
197 | ret = args.data_size; | |
198 | done: | |
199 | return ret; | |
200 | } | |
201 | ||
202 | /* Returns num bytes read, or negative on error. Doesn't need locking. */ | |
203 | static int cros_ec_lpc_readmem(struct cros_ec_device *ec, unsigned int offset, | |
204 | unsigned int bytes, void *dest) | |
205 | { | |
206 | int i = offset; | |
207 | char *s = dest; | |
208 | int cnt = 0; | |
209 | ||
210 | if (offset >= EC_MEMMAP_SIZE - bytes) | |
211 | return -EINVAL; | |
212 | ||
213 | /* fixed length */ | |
214 | if (bytes) { | |
bce70fef SN |
215 | cros_ec_lpc_read_bytes(EC_LPC_ADDR_MEMMAP + offset, bytes, s); |
216 | return bytes; | |
ec2f33ab BR |
217 | } |
218 | ||
219 | /* string */ | |
220 | for (; i < EC_MEMMAP_SIZE; i++, s++) { | |
bce70fef | 221 | cros_ec_lpc_read_bytes(EC_LPC_ADDR_MEMMAP + i, 1, s); |
ec2f33ab BR |
222 | cnt++; |
223 | if (!*s) | |
224 | break; | |
225 | } | |
226 | ||
227 | return cnt; | |
228 | } | |
229 | ||
a6df7798 GG |
230 | static void cros_ec_lpc_acpi_notify(acpi_handle device, u32 value, void *data) |
231 | { | |
232 | struct cros_ec_device *ec_dev = data; | |
233 | ||
29d99b96 SN |
234 | if (ec_dev->mkbp_event_supported && |
235 | cros_ec_get_next_event(ec_dev, NULL) > 0) | |
a6df7798 GG |
236 | blocking_notifier_call_chain(&ec_dev->event_notifier, 0, |
237 | ec_dev); | |
238 | } | |
239 | ||
ec2f33ab BR |
240 | static int cros_ec_lpc_probe(struct platform_device *pdev) |
241 | { | |
242 | struct device *dev = &pdev->dev; | |
a6df7798 GG |
243 | struct acpi_device *adev; |
244 | acpi_status status; | |
ec2f33ab | 245 | struct cros_ec_device *ec_dev; |
bce70fef | 246 | u8 buf[2]; |
ec2f33ab BR |
247 | int ret; |
248 | ||
249 | if (!devm_request_region(dev, EC_LPC_ADDR_MEMMAP, EC_MEMMAP_SIZE, | |
250 | dev_name(dev))) { | |
251 | dev_err(dev, "couldn't reserve memmap region\n"); | |
252 | return -EBUSY; | |
253 | } | |
254 | ||
bce70fef SN |
255 | cros_ec_lpc_read_bytes(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID, 2, buf); |
256 | if (buf[0] != 'E' || buf[1] != 'C') { | |
ec2f33ab BR |
257 | dev_err(dev, "EC ID not detected\n"); |
258 | return -ENODEV; | |
259 | } | |
260 | ||
261 | if (!devm_request_region(dev, EC_HOST_CMD_REGION0, | |
262 | EC_HOST_CMD_REGION_SIZE, dev_name(dev))) { | |
263 | dev_err(dev, "couldn't reserve region0\n"); | |
264 | return -EBUSY; | |
265 | } | |
266 | if (!devm_request_region(dev, EC_HOST_CMD_REGION1, | |
267 | EC_HOST_CMD_REGION_SIZE, dev_name(dev))) { | |
268 | dev_err(dev, "couldn't reserve region1\n"); | |
269 | return -EBUSY; | |
270 | } | |
271 | ||
272 | ec_dev = devm_kzalloc(dev, sizeof(*ec_dev), GFP_KERNEL); | |
273 | if (!ec_dev) | |
274 | return -ENOMEM; | |
275 | ||
276 | platform_set_drvdata(pdev, ec_dev); | |
277 | ec_dev->dev = dev; | |
ec2f33ab | 278 | ec_dev->phys_name = dev_name(dev); |
ec2f33ab | 279 | ec_dev->cmd_xfer = cros_ec_cmd_xfer_lpc; |
d3654070 | 280 | ec_dev->pkt_xfer = cros_ec_pkt_xfer_lpc; |
ec2f33ab | 281 | ec_dev->cmd_readmem = cros_ec_lpc_readmem; |
2c7589af SB |
282 | ec_dev->din_size = sizeof(struct ec_host_response) + |
283 | sizeof(struct ec_response_get_protocol_info); | |
284 | ec_dev->dout_size = sizeof(struct ec_host_request); | |
ec2f33ab BR |
285 | |
286 | ret = cros_ec_register(ec_dev); | |
287 | if (ret) { | |
288 | dev_err(dev, "couldn't register ec_dev (%d)\n", ret); | |
289 | return ret; | |
290 | } | |
291 | ||
a6df7798 GG |
292 | /* |
293 | * Connect a notify handler to process MKBP messages if we have a | |
294 | * companion ACPI device. | |
295 | */ | |
296 | adev = ACPI_COMPANION(dev); | |
297 | if (adev) { | |
298 | status = acpi_install_notify_handler(adev->handle, | |
299 | ACPI_ALL_NOTIFY, | |
300 | cros_ec_lpc_acpi_notify, | |
301 | ec_dev); | |
302 | if (ACPI_FAILURE(status)) | |
303 | dev_warn(dev, "Failed to register notifier %08x\n", | |
304 | status); | |
305 | } | |
306 | ||
ec2f33ab BR |
307 | return 0; |
308 | } | |
309 | ||
310 | static int cros_ec_lpc_remove(struct platform_device *pdev) | |
311 | { | |
312 | struct cros_ec_device *ec_dev; | |
a6df7798 GG |
313 | struct acpi_device *adev; |
314 | ||
315 | adev = ACPI_COMPANION(&pdev->dev); | |
316 | if (adev) | |
317 | acpi_remove_notify_handler(adev->handle, ACPI_ALL_NOTIFY, | |
318 | cros_ec_lpc_acpi_notify); | |
ec2f33ab BR |
319 | |
320 | ec_dev = platform_get_drvdata(pdev); | |
321 | cros_ec_remove(ec_dev); | |
322 | ||
323 | return 0; | |
324 | } | |
325 | ||
12278dc7 GG |
326 | static const struct acpi_device_id cros_ec_lpc_acpi_device_ids[] = { |
327 | { ACPI_DRV_NAME, 0 }, | |
328 | { } | |
329 | }; | |
330 | MODULE_DEVICE_TABLE(acpi, cros_ec_lpc_acpi_device_ids); | |
331 | ||
6faadbbb | 332 | static const struct dmi_system_id cros_ec_lpc_dmi_table[] __initconst = { |
ec2f33ab BR |
333 | { |
334 | /* | |
335 | * Today all Chromebooks/boxes ship with Google_* as version and | |
336 | * coreboot as bios vendor. No other systems with this | |
337 | * combination are known to date. | |
338 | */ | |
339 | .matches = { | |
340 | DMI_MATCH(DMI_BIOS_VENDOR, "coreboot"), | |
341 | DMI_MATCH(DMI_BIOS_VERSION, "Google_"), | |
342 | }, | |
343 | }, | |
344 | { | |
345 | /* x86-link, the Chromebook Pixel. */ | |
346 | .matches = { | |
347 | DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), | |
348 | DMI_MATCH(DMI_PRODUCT_NAME, "Link"), | |
349 | }, | |
350 | }, | |
85bba84e JMC |
351 | { |
352 | /* x86-samus, the Chromebook Pixel 2. */ | |
353 | .matches = { | |
354 | DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"), | |
355 | DMI_MATCH(DMI_PRODUCT_NAME, "Samus"), | |
356 | }, | |
357 | }, | |
ec2f33ab BR |
358 | { |
359 | /* x86-peppy, the Acer C720 Chromebook. */ | |
360 | .matches = { | |
361 | DMI_MATCH(DMI_SYS_VENDOR, "Acer"), | |
362 | DMI_MATCH(DMI_PRODUCT_NAME, "Peppy"), | |
363 | }, | |
364 | }, | |
365 | { /* sentinel */ } | |
366 | }; | |
367 | MODULE_DEVICE_TABLE(dmi, cros_ec_lpc_dmi_table); | |
368 | ||
450de8f4 AP |
369 | #ifdef CONFIG_PM_SLEEP |
370 | static int cros_ec_lpc_suspend(struct device *dev) | |
371 | { | |
372 | struct cros_ec_device *ec_dev = dev_get_drvdata(dev); | |
373 | ||
374 | return cros_ec_suspend(ec_dev); | |
375 | } | |
376 | ||
377 | static int cros_ec_lpc_resume(struct device *dev) | |
378 | { | |
379 | struct cros_ec_device *ec_dev = dev_get_drvdata(dev); | |
380 | ||
381 | return cros_ec_resume(ec_dev); | |
382 | } | |
383 | #endif | |
384 | ||
385 | const struct dev_pm_ops cros_ec_lpc_pm_ops = { | |
386 | SET_LATE_SYSTEM_SLEEP_PM_OPS(cros_ec_lpc_suspend, cros_ec_lpc_resume) | |
387 | }; | |
388 | ||
ec2f33ab BR |
389 | static struct platform_driver cros_ec_lpc_driver = { |
390 | .driver = { | |
391 | .name = DRV_NAME, | |
12278dc7 | 392 | .acpi_match_table = cros_ec_lpc_acpi_device_ids, |
450de8f4 | 393 | .pm = &cros_ec_lpc_pm_ops, |
ec2f33ab BR |
394 | }, |
395 | .probe = cros_ec_lpc_probe, | |
396 | .remove = cros_ec_lpc_remove, | |
397 | }; | |
398 | ||
ec2f33ab BR |
399 | static int __init cros_ec_lpc_init(void) |
400 | { | |
401 | int ret; | |
402 | ||
403 | if (!dmi_check_system(cros_ec_lpc_dmi_table)) { | |
404 | pr_err(DRV_NAME ": unsupported system.\n"); | |
405 | return -ENODEV; | |
406 | } | |
407 | ||
8d4a3dc4 SN |
408 | cros_ec_lpc_reg_init(); |
409 | ||
ec2f33ab BR |
410 | /* Register the driver */ |
411 | ret = platform_driver_register(&cros_ec_lpc_driver); | |
412 | if (ret) { | |
413 | pr_err(DRV_NAME ": can't register driver: %d\n", ret); | |
8d4a3dc4 | 414 | cros_ec_lpc_reg_destroy(); |
ec2f33ab BR |
415 | return ret; |
416 | } | |
417 | ||
ec2f33ab BR |
418 | return 0; |
419 | } | |
420 | ||
421 | static void __exit cros_ec_lpc_exit(void) | |
422 | { | |
ec2f33ab | 423 | platform_driver_unregister(&cros_ec_lpc_driver); |
8d4a3dc4 | 424 | cros_ec_lpc_reg_destroy(); |
ec2f33ab BR |
425 | } |
426 | ||
427 | module_init(cros_ec_lpc_init); | |
428 | module_exit(cros_ec_lpc_exit); | |
429 | ||
430 | MODULE_LICENSE("GPL"); | |
431 | MODULE_DESCRIPTION("ChromeOS EC LPC driver"); |