1
2
3
4
5
6
7
8#include <linux/fs.h>
9#include <linux/mfd/core.h>
10#include <linux/module.h>
11#include <linux/mod_devicetable.h>
12#include <linux/of_platform.h>
13#include <linux/platform_device.h>
14#include <linux/pm.h>
15#include <linux/slab.h>
16#include <linux/uaccess.h>
17
18#include "cros_ec_dev.h"
19
20#define DRV_NAME "cros-ec-dev"
21
22
23#define CROS_MAX_DEV 128
24static int ec_major;
25
26static struct class cros_class = {
27 .owner = THIS_MODULE,
28 .name = "chromeos",
29};
30
31
32static int ec_get_version(struct cros_ec_dev *ec, char *str, int maxlen)
33{
34 struct ec_response_get_version *resp;
35 static const char * const current_image_name[] = {
36 "unknown", "read-only", "read-write", "invalid",
37 };
38 struct cros_ec_command *msg;
39 int ret;
40
41 msg = kmalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL);
42 if (!msg)
43 return -ENOMEM;
44
45 msg->version = 0;
46 msg->command = EC_CMD_GET_VERSION + ec->cmd_offset;
47 msg->insize = sizeof(*resp);
48 msg->outsize = 0;
49
50 ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
51 if (ret < 0)
52 goto exit;
53
54 if (msg->result != EC_RES_SUCCESS) {
55 snprintf(str, maxlen,
56 "%s\nUnknown EC version: EC returned %d\n",
57 CROS_EC_DEV_VERSION, msg->result);
58 ret = -EINVAL;
59 goto exit;
60 }
61
62 resp = (struct ec_response_get_version *)msg->data;
63 if (resp->current_image >= ARRAY_SIZE(current_image_name))
64 resp->current_image = 3;
65
66 snprintf(str, maxlen, "%s\n%s\n%s\n%s\n", CROS_EC_DEV_VERSION,
67 resp->version_string_ro, resp->version_string_rw,
68 current_image_name[resp->current_image]);
69
70 ret = 0;
71exit:
72 kfree(msg);
73 return ret;
74}
75
76static int cros_ec_check_features(struct cros_ec_dev *ec, int feature)
77{
78 struct cros_ec_command *msg;
79 int ret;
80
81 if (ec->features[0] == -1U && ec->features[1] == -1U) {
82
83
84 msg = kmalloc(sizeof(*msg) + sizeof(ec->features), GFP_KERNEL);
85 if (!msg)
86 return -ENOMEM;
87
88 msg->version = 0;
89 msg->command = EC_CMD_GET_FEATURES + ec->cmd_offset;
90 msg->insize = sizeof(ec->features);
91 msg->outsize = 0;
92
93 ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
94 if (ret < 0 || msg->result != EC_RES_SUCCESS) {
95 dev_warn(ec->dev, "cannot get EC features: %d/%d\n",
96 ret, msg->result);
97 memset(ec->features, 0, sizeof(ec->features));
98 } else {
99 memcpy(ec->features, msg->data, sizeof(ec->features));
100 }
101
102 dev_dbg(ec->dev, "EC features %08x %08x\n",
103 ec->features[0], ec->features[1]);
104
105 kfree(msg);
106 }
107
108 return ec->features[feature / 32] & EC_FEATURE_MASK_0(feature);
109}
110
111
112static int ec_device_open(struct inode *inode, struct file *filp)
113{
114 struct cros_ec_dev *ec = container_of(inode->i_cdev,
115 struct cros_ec_dev, cdev);
116 filp->private_data = ec;
117 nonseekable_open(inode, filp);
118 return 0;
119}
120
121static int ec_device_release(struct inode *inode, struct file *filp)
122{
123 return 0;
124}
125
126static ssize_t ec_device_read(struct file *filp, char __user *buffer,
127 size_t length, loff_t *offset)
128{
129 struct cros_ec_dev *ec = filp->private_data;
130 char msg[sizeof(struct ec_response_get_version) +
131 sizeof(CROS_EC_DEV_VERSION)];
132 size_t count;
133 int ret;
134
135 if (*offset != 0)
136 return 0;
137
138 ret = ec_get_version(ec, msg, sizeof(msg));
139 if (ret)
140 return ret;
141
142 count = min(length, strlen(msg));
143
144 if (copy_to_user(buffer, msg, count))
145 return -EFAULT;
146
147 *offset = count;
148 return count;
149}
150
151
152static long ec_device_ioctl_xcmd(struct cros_ec_dev *ec, void __user *arg)
153{
154 long ret;
155 struct cros_ec_command u_cmd;
156 struct cros_ec_command *s_cmd;
157
158 if (copy_from_user(&u_cmd, arg, sizeof(u_cmd)))
159 return -EFAULT;
160
161 if ((u_cmd.outsize > EC_MAX_MSG_BYTES) ||
162 (u_cmd.insize > EC_MAX_MSG_BYTES))
163 return -EINVAL;
164
165 s_cmd = kmalloc(sizeof(*s_cmd) + max(u_cmd.outsize, u_cmd.insize),
166 GFP_KERNEL);
167 if (!s_cmd)
168 return -ENOMEM;
169
170 if (copy_from_user(s_cmd, arg, sizeof(*s_cmd) + u_cmd.outsize)) {
171 ret = -EFAULT;
172 goto exit;
173 }
174
175 if (u_cmd.outsize != s_cmd->outsize ||
176 u_cmd.insize != s_cmd->insize) {
177 ret = -EINVAL;
178 goto exit;
179 }
180
181 s_cmd->command += ec->cmd_offset;
182 ret = cros_ec_cmd_xfer(ec->ec_dev, s_cmd);
183
184 if (ret < 0)
185 goto exit;
186
187 if (copy_to_user(arg, s_cmd, sizeof(*s_cmd) + s_cmd->insize))
188 ret = -EFAULT;
189exit:
190 kfree(s_cmd);
191 return ret;
192}
193
194static long ec_device_ioctl_readmem(struct cros_ec_dev *ec, void __user *arg)
195{
196 struct cros_ec_device *ec_dev = ec->ec_dev;
197 struct cros_ec_readmem s_mem = { };
198 long num;
199
200
201 if (!ec_dev->cmd_readmem)
202 return -ENOTTY;
203
204 if (copy_from_user(&s_mem, arg, sizeof(s_mem)))
205 return -EFAULT;
206
207 num = ec_dev->cmd_readmem(ec_dev, s_mem.offset, s_mem.bytes,
208 s_mem.buffer);
209 if (num <= 0)
210 return num;
211
212 if (copy_to_user((void __user *)arg, &s_mem, sizeof(s_mem)))
213 return -EFAULT;
214
215 return num;
216}
217
218static long ec_device_ioctl(struct file *filp, unsigned int cmd,
219 unsigned long arg)
220{
221 struct cros_ec_dev *ec = filp->private_data;
222
223 if (_IOC_TYPE(cmd) != CROS_EC_DEV_IOC)
224 return -ENOTTY;
225
226 switch (cmd) {
227 case CROS_EC_DEV_IOCXCMD:
228 return ec_device_ioctl_xcmd(ec, (void __user *)arg);
229 case CROS_EC_DEV_IOCRDMEM:
230 return ec_device_ioctl_readmem(ec, (void __user *)arg);
231 }
232
233 return -ENOTTY;
234}
235
236
237static const struct file_operations fops = {
238 .open = ec_device_open,
239 .release = ec_device_release,
240 .read = ec_device_read,
241 .unlocked_ioctl = ec_device_ioctl,
242#ifdef CONFIG_COMPAT
243 .compat_ioctl = ec_device_ioctl,
244#endif
245};
246
247static void cros_ec_class_release(struct device *dev)
248{
249 kfree(to_cros_ec_dev(dev));
250}
251
252static void cros_ec_sensors_register(struct cros_ec_dev *ec)
253{
254
255
256
257
258 int ret, i, id, sensor_num;
259 struct mfd_cell *sensor_cells;
260 struct cros_ec_sensor_platform *sensor_platforms;
261 int sensor_type[MOTIONSENSE_TYPE_MAX];
262 struct ec_params_motion_sense *params;
263 struct ec_response_motion_sense *resp;
264 struct cros_ec_command *msg;
265
266 msg = kzalloc(sizeof(struct cros_ec_command) +
267 max(sizeof(*params), sizeof(*resp)), GFP_KERNEL);
268 if (msg == NULL)
269 return;
270
271 msg->version = 2;
272 msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
273 msg->outsize = sizeof(*params);
274 msg->insize = sizeof(*resp);
275
276 params = (struct ec_params_motion_sense *)msg->data;
277 params->cmd = MOTIONSENSE_CMD_DUMP;
278
279 ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
280 if (ret < 0 || msg->result != EC_RES_SUCCESS) {
281 dev_warn(ec->dev, "cannot get EC sensor information: %d/%d\n",
282 ret, msg->result);
283 goto error;
284 }
285
286 resp = (struct ec_response_motion_sense *)msg->data;
287 sensor_num = resp->dump.sensor_count;
288
289
290
291 sensor_cells = kcalloc(sensor_num + 2, sizeof(struct mfd_cell),
292 GFP_KERNEL);
293 if (sensor_cells == NULL)
294 goto error;
295
296 sensor_platforms = kcalloc(sensor_num,
297 sizeof(struct cros_ec_sensor_platform),
298 GFP_KERNEL);
299 if (sensor_platforms == NULL)
300 goto error_platforms;
301
302 memset(sensor_type, 0, sizeof(sensor_type));
303 id = 0;
304 for (i = 0; i < sensor_num; i++) {
305 params->cmd = MOTIONSENSE_CMD_INFO;
306 params->info.sensor_num = i;
307 ret = cros_ec_cmd_xfer(ec->ec_dev, msg);
308 if (ret < 0 || msg->result != EC_RES_SUCCESS) {
309 dev_warn(ec->dev, "no info for EC sensor %d : %d/%d\n",
310 i, ret, msg->result);
311 continue;
312 }
313 switch (resp->info.type) {
314 case MOTIONSENSE_TYPE_ACCEL:
315 sensor_cells[id].name = "cros-ec-accel";
316 break;
317 case MOTIONSENSE_TYPE_BARO:
318 sensor_cells[id].name = "cros-ec-baro";
319 break;
320 case MOTIONSENSE_TYPE_GYRO:
321 sensor_cells[id].name = "cros-ec-gyro";
322 break;
323 case MOTIONSENSE_TYPE_MAG:
324 sensor_cells[id].name = "cros-ec-mag";
325 break;
326 case MOTIONSENSE_TYPE_PROX:
327 sensor_cells[id].name = "cros-ec-prox";
328 break;
329 case MOTIONSENSE_TYPE_LIGHT:
330 sensor_cells[id].name = "cros-ec-light";
331 break;
332 case MOTIONSENSE_TYPE_ACTIVITY:
333 sensor_cells[id].name = "cros-ec-activity";
334 break;
335 default:
336 dev_warn(ec->dev, "unknown type %d\n", resp->info.type);
337 continue;
338 }
339 sensor_platforms[id].sensor_num = i;
340 sensor_cells[id].id = sensor_type[resp->info.type];
341 sensor_cells[id].platform_data = &sensor_platforms[id];
342 sensor_cells[id].pdata_size =
343 sizeof(struct cros_ec_sensor_platform);
344
345 sensor_type[resp->info.type]++;
346 id++;
347 }
348
349 if (sensor_type[MOTIONSENSE_TYPE_ACCEL] >= 2)
350 ec->has_kb_wake_angle = true;
351
352 if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE_FIFO)) {
353 sensor_cells[id].name = "cros-ec-ring";
354 id++;
355 }
356 if (cros_ec_check_features(ec,
357 EC_FEATURE_REFINED_TABLET_MODE_HYSTERESIS)) {
358 sensor_cells[id].name = "cros-ec-lid-angle";
359 id++;
360 }
361
362 ret = mfd_add_devices(ec->dev, 0, sensor_cells, id,
363 NULL, 0, NULL);
364 if (ret)
365 dev_err(ec->dev, "failed to add EC sensors\n");
366
367 kfree(sensor_platforms);
368error_platforms:
369 kfree(sensor_cells);
370error:
371 kfree(msg);
372}
373
374static struct cros_ec_sensor_platform sensor_platforms[] = {
375 { .sensor_num = 0 },
376 { .sensor_num = 1 }
377};
378
379static const struct mfd_cell cros_ec_accel_legacy_cells[] = {
380 {
381 .name = "cros-ec-accel-legacy",
382 .platform_data = &sensor_platforms[0],
383 .pdata_size = sizeof(struct cros_ec_sensor_platform),
384 },
385 {
386 .name = "cros-ec-accel-legacy",
387 .platform_data = &sensor_platforms[1],
388 .pdata_size = sizeof(struct cros_ec_sensor_platform),
389 }
390};
391
392static void cros_ec_accel_legacy_register(struct cros_ec_dev *ec)
393{
394 struct cros_ec_device *ec_dev = ec->ec_dev;
395 u8 status;
396 int ret;
397
398
399
400
401
402 if (ec->cmd_offset != 0)
403 return;
404
405
406
407
408
409 if (ec_dev->cmd_readmem) {
410 ret = ec_dev->cmd_readmem(ec_dev, EC_MEMMAP_ACC_STATUS, 1,
411 &status);
412 if (ret < 0) {
413 dev_warn(ec->dev, "EC direct read error.\n");
414 return;
415 }
416
417
418 if (!(status & EC_MEMMAP_ACC_STATUS_PRESENCE_BIT)) {
419 dev_info(ec->dev, "EC does not have accelerometers.\n");
420 return;
421 }
422 }
423
424
425
426
427
428
429
430
431
432 ret = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO,
433 cros_ec_accel_legacy_cells,
434 ARRAY_SIZE(cros_ec_accel_legacy_cells),
435 NULL, 0, NULL);
436 if (ret)
437 dev_err(ec_dev->dev, "failed to add EC sensors\n");
438}
439
440static const struct mfd_cell cros_ec_cec_cells[] = {
441 { .name = "cros-ec-cec" }
442};
443
444static const struct mfd_cell cros_ec_rtc_cells[] = {
445 { .name = "cros-ec-rtc" }
446};
447
448static const struct mfd_cell cros_usbpd_charger_cells[] = {
449 { .name = "cros-usbpd-charger" },
450 { .name = "cros-usbpd-logger" },
451};
452
453static const struct mfd_cell cros_ec_platform_cells[] = {
454 { .name = "cros-ec-debugfs" },
455 { .name = "cros-ec-lightbar" },
456 { .name = "cros-ec-sysfs" },
457};
458
459static const struct mfd_cell cros_ec_vbc_cells[] = {
460 { .name = "cros-ec-vbc" }
461};
462
463static int ec_device_probe(struct platform_device *pdev)
464{
465 int retval = -ENOMEM;
466 struct device_node *node;
467 struct device *dev = &pdev->dev;
468 struct cros_ec_platform *ec_platform = dev_get_platdata(dev);
469 struct cros_ec_dev *ec = kzalloc(sizeof(*ec), GFP_KERNEL);
470
471 if (!ec)
472 return retval;
473
474 dev_set_drvdata(dev, ec);
475 ec->ec_dev = dev_get_drvdata(dev->parent);
476 ec->dev = dev;
477 ec->cmd_offset = ec_platform->cmd_offset;
478 ec->features[0] = -1U;
479 ec->features[1] = -1U;
480 device_initialize(&ec->class_dev);
481 cdev_init(&ec->cdev, &fops);
482
483
484 if (cros_ec_check_features(ec, EC_FEATURE_FINGERPRINT)) {
485 dev_info(dev, "CrOS Fingerprint MCU detected.\n");
486
487
488
489
490 ec_platform->ec_name = CROS_EC_DEV_FP_NAME;
491 }
492
493
494
495
496
497 if (cros_ec_check_features(ec, EC_FEATURE_ISH)) {
498 dev_info(dev, "CrOS ISH MCU detected.\n");
499
500
501
502
503 ec_platform->ec_name = CROS_EC_DEV_ISH_NAME;
504 }
505
506
507 if (cros_ec_check_features(ec, EC_FEATURE_TOUCHPAD)) {
508 dev_info(dev, "CrOS Touchpad MCU detected.\n");
509
510
511
512
513 ec_platform->ec_name = CROS_EC_DEV_TP_NAME;
514 }
515
516
517 if (cros_ec_check_features(ec, EC_FEATURE_SCP)) {
518 dev_info(dev, "CrOS SCP MCU detected.\n");
519
520
521
522
523 ec_platform->ec_name = CROS_EC_DEV_SCP_NAME;
524 }
525
526
527
528
529
530
531 ec->class_dev.devt = MKDEV(ec_major, pdev->id);
532 ec->class_dev.class = &cros_class;
533 ec->class_dev.parent = dev;
534 ec->class_dev.release = cros_ec_class_release;
535
536 retval = dev_set_name(&ec->class_dev, "%s", ec_platform->ec_name);
537 if (retval) {
538 dev_err(dev, "dev_set_name failed => %d\n", retval);
539 goto failed;
540 }
541
542
543 if (cros_ec_check_features(ec, EC_FEATURE_MOTION_SENSE))
544 cros_ec_sensors_register(ec);
545 else
546
547 cros_ec_accel_legacy_register(ec);
548
549
550 if (cros_ec_check_features(ec, EC_FEATURE_CEC)) {
551 retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO,
552 cros_ec_cec_cells,
553 ARRAY_SIZE(cros_ec_cec_cells),
554 NULL, 0, NULL);
555 if (retval)
556 dev_err(ec->dev,
557 "failed to add cros-ec-cec device: %d\n",
558 retval);
559 }
560
561
562 if (cros_ec_check_features(ec, EC_FEATURE_RTC)) {
563 retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO,
564 cros_ec_rtc_cells,
565 ARRAY_SIZE(cros_ec_rtc_cells),
566 NULL, 0, NULL);
567 if (retval)
568 dev_err(ec->dev,
569 "failed to add cros-ec-rtc device: %d\n",
570 retval);
571 }
572
573
574 if (cros_ec_check_features(ec, EC_FEATURE_USB_PD)) {
575 retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO,
576 cros_usbpd_charger_cells,
577 ARRAY_SIZE(cros_usbpd_charger_cells),
578 NULL, 0, NULL);
579 if (retval)
580 dev_err(ec->dev,
581 "failed to add cros-usbpd-charger device: %d\n",
582 retval);
583 }
584
585
586 retval = cdev_device_add(&ec->cdev, &ec->class_dev);
587 if (retval) {
588 dev_err(dev, "cdev_device_add failed => %d\n", retval);
589 goto failed;
590 }
591
592 retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO,
593 cros_ec_platform_cells,
594 ARRAY_SIZE(cros_ec_platform_cells),
595 NULL, 0, NULL);
596 if (retval)
597 dev_warn(ec->dev,
598 "failed to add cros-ec platform devices: %d\n",
599 retval);
600
601
602 node = ec->ec_dev->dev->of_node;
603 if (of_property_read_bool(node, "google,has-vbc-nvram")) {
604 retval = mfd_add_devices(ec->dev, PLATFORM_DEVID_AUTO,
605 cros_ec_vbc_cells,
606 ARRAY_SIZE(cros_ec_vbc_cells),
607 NULL, 0, NULL);
608 if (retval)
609 dev_warn(ec->dev, "failed to add VBC devices: %d\n",
610 retval);
611 }
612
613 return 0;
614
615failed:
616 put_device(&ec->class_dev);
617 return retval;
618}
619
620static int ec_device_remove(struct platform_device *pdev)
621{
622 struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev);
623
624 mfd_remove_devices(ec->dev);
625 cdev_del(&ec->cdev);
626 device_unregister(&ec->class_dev);
627 return 0;
628}
629
630static const struct platform_device_id cros_ec_id[] = {
631 { DRV_NAME, 0 },
632 { }
633};
634MODULE_DEVICE_TABLE(platform, cros_ec_id);
635
636static struct platform_driver cros_ec_dev_driver = {
637 .driver = {
638 .name = DRV_NAME,
639 },
640 .id_table = cros_ec_id,
641 .probe = ec_device_probe,
642 .remove = ec_device_remove,
643};
644
645static int __init cros_ec_dev_init(void)
646{
647 int ret;
648 dev_t dev = 0;
649
650 ret = class_register(&cros_class);
651 if (ret) {
652 pr_err(CROS_EC_DEV_NAME ": failed to register device class\n");
653 return ret;
654 }
655
656
657 ret = alloc_chrdev_region(&dev, 0, CROS_MAX_DEV, CROS_EC_DEV_NAME);
658 if (ret < 0) {
659 pr_err(CROS_EC_DEV_NAME ": alloc_chrdev_region() failed\n");
660 goto failed_chrdevreg;
661 }
662 ec_major = MAJOR(dev);
663
664
665 ret = platform_driver_register(&cros_ec_dev_driver);
666 if (ret < 0) {
667 pr_warn(CROS_EC_DEV_NAME ": can't register driver: %d\n", ret);
668 goto failed_devreg;
669 }
670 return 0;
671
672failed_devreg:
673 unregister_chrdev_region(MKDEV(ec_major, 0), CROS_MAX_DEV);
674failed_chrdevreg:
675 class_unregister(&cros_class);
676 return ret;
677}
678
679static void __exit cros_ec_dev_exit(void)
680{
681 platform_driver_unregister(&cros_ec_dev_driver);
682 unregister_chrdev(ec_major, CROS_EC_DEV_NAME);
683 class_unregister(&cros_class);
684}
685
686module_init(cros_ec_dev_init);
687module_exit(cros_ec_dev_exit);
688
689MODULE_ALIAS("platform:" DRV_NAME);
690MODULE_AUTHOR("Bill Richardson <wfrichar@chromium.org>");
691MODULE_DESCRIPTION("Userspace interface to the Chrome OS Embedded Controller");
692MODULE_VERSION("1.0");
693MODULE_LICENSE("GPL");
694