Merge tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / drivers / mfd / cros_ec_dev.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * cros_ec_dev - expose the Chrome OS Embedded Controller to user-space
4  *
5  * Copyright (C) 2014 Google, Inc.
6  */
7
8 #include <linux/kconfig.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/platform_data/cros_ec_chardev.h>
15 #include <linux/platform_data/cros_ec_commands.h>
16 #include <linux/platform_data/cros_ec_proto.h>
17 #include <linux/slab.h>
18
19 #define DRV_NAME "cros-ec-dev"
20
21 static struct class cros_class = {
22         .owner          = THIS_MODULE,
23         .name           = "chromeos",
24 };
25
26 /**
27  * struct cros_feature_to_name - CrOS feature id to name/short description.
28  * @id: The feature identifier.
29  * @name: Device name associated with the feature id.
30  * @desc: Short name that will be displayed.
31  */
32 struct cros_feature_to_name {
33         unsigned int id;
34         const char *name;
35         const char *desc;
36 };
37
38 /**
39  * struct cros_feature_to_cells - CrOS feature id to mfd cells association.
40  * @id: The feature identifier.
41  * @mfd_cells: Pointer to the array of mfd cells that needs to be added.
42  * @num_cells: Number of mfd cells into the array.
43  */
44 struct cros_feature_to_cells {
45         unsigned int id;
46         const struct mfd_cell *mfd_cells;
47         unsigned int num_cells;
48 };
49
50 static const struct cros_feature_to_name cros_mcu_devices[] = {
51         {
52                 .id     = EC_FEATURE_FINGERPRINT,
53                 .name   = CROS_EC_DEV_FP_NAME,
54                 .desc   = "Fingerprint",
55         },
56         {
57                 .id     = EC_FEATURE_ISH,
58                 .name   = CROS_EC_DEV_ISH_NAME,
59                 .desc   = "Integrated Sensor Hub",
60         },
61         {
62                 .id     = EC_FEATURE_SCP,
63                 .name   = CROS_EC_DEV_SCP_NAME,
64                 .desc   = "System Control Processor",
65         },
66         {
67                 .id     = EC_FEATURE_TOUCHPAD,
68                 .name   = CROS_EC_DEV_TP_NAME,
69                 .desc   = "Touchpad",
70         },
71 };
72
73 static const struct mfd_cell cros_ec_cec_cells[] = {
74         { .name = "cros-ec-cec", },
75 };
76
77 static const struct mfd_cell cros_ec_rtc_cells[] = {
78         { .name = "cros-ec-rtc", },
79 };
80
81 static const struct mfd_cell cros_ec_sensorhub_cells[] = {
82         { .name = "cros-ec-sensorhub", },
83 };
84
85 static const struct mfd_cell cros_usbpd_charger_cells[] = {
86         { .name = "cros-usbpd-charger", },
87         { .name = "cros-usbpd-logger", },
88 };
89
90 static const struct mfd_cell cros_usbpd_notify_cells[] = {
91         { .name = "cros-usbpd-notify", },
92 };
93
94 static const struct cros_feature_to_cells cros_subdevices[] = {
95         {
96                 .id             = EC_FEATURE_CEC,
97                 .mfd_cells      = cros_ec_cec_cells,
98                 .num_cells      = ARRAY_SIZE(cros_ec_cec_cells),
99         },
100         {
101                 .id             = EC_FEATURE_RTC,
102                 .mfd_cells      = cros_ec_rtc_cells,
103                 .num_cells      = ARRAY_SIZE(cros_ec_rtc_cells),
104         },
105         {
106                 .id             = EC_FEATURE_USB_PD,
107                 .mfd_cells      = cros_usbpd_charger_cells,
108                 .num_cells      = ARRAY_SIZE(cros_usbpd_charger_cells),
109         },
110 };
111
112 static const struct mfd_cell cros_ec_platform_cells[] = {
113         { .name = "cros-ec-chardev", },
114         { .name = "cros-ec-debugfs", },
115         { .name = "cros-ec-lightbar", },
116         { .name = "cros-ec-sysfs", },
117 };
118
119 static const struct mfd_cell cros_ec_vbc_cells[] = {
120         { .name = "cros-ec-vbc", }
121 };
122
123 static void cros_ec_class_release(struct device *dev)
124 {
125         kfree(to_cros_ec_dev(dev));
126 }
127
128 static int ec_device_probe(struct platform_device *pdev)
129 {
130         int retval = -ENOMEM;
131         struct device_node *node;
132         struct device *dev = &pdev->dev;
133         struct cros_ec_platform *ec_platform = dev_get_platdata(dev);
134         struct cros_ec_dev *ec = kzalloc(sizeof(*ec), GFP_KERNEL);
135         int i;
136
137         if (!ec)
138                 return retval;
139
140         dev_set_drvdata(dev, ec);
141         ec->ec_dev = dev_get_drvdata(dev->parent);
142         ec->dev = dev;
143         ec->cmd_offset = ec_platform->cmd_offset;
144         ec->features[0] = -1U; /* Not cached yet */
145         ec->features[1] = -1U; /* Not cached yet */
146         device_initialize(&ec->class_dev);
147
148         for (i = 0; i < ARRAY_SIZE(cros_mcu_devices); i++) {
149                 /*
150                  * Check whether this is actually a dedicated MCU rather
151                  * than an standard EC.
152                  */
153                 if (cros_ec_check_features(ec, cros_mcu_devices[i].id)) {
154                         dev_info(dev, "CrOS %s MCU detected\n",
155                                  cros_mcu_devices[i].desc);
156                         /*
157                          * Help userspace differentiating ECs from other MCU,
158                          * regardless of the probing order.
159                          */
160                         ec_platform->ec_name = cros_mcu_devices[i].name;
161                         break;
162                 }
163         }
164
165         /*
166          * Add the class device
167          */
168         ec->class_dev.class = &cros_class;
169         ec->class_dev.parent = dev;
170         ec->class_dev.release = cros_ec_class_release;
171
172         retval = dev_set_name(&ec->class_dev, "%s", ec_platform->ec_name);
173         if (retval) {
174                 dev_err(dev, "dev_set_name failed => %d\n", retval);
175                 goto failed;
176         }
177
178         retval = device_add(&ec->class_dev);
179         if (retval)
180                 goto failed;
181
182         /* check whether this EC is a sensor hub. */
183         if (cros_ec_get_sensor_count(ec) > 0) {
184                 retval = mfd_add_hotplug_devices(ec->dev,
185                                 cros_ec_sensorhub_cells,
186                                 ARRAY_SIZE(cros_ec_sensorhub_cells));
187                 if (retval)
188                         dev_err(ec->dev, "failed to add %s subdevice: %d\n",
189                                 cros_ec_sensorhub_cells->name, retval);
190         }
191
192         /*
193          * The following subdevices can be detected by sending the
194          * EC_FEATURE_GET_CMD Embedded Controller device.
195          */
196         for (i = 0; i < ARRAY_SIZE(cros_subdevices); i++) {
197                 if (cros_ec_check_features(ec, cros_subdevices[i].id)) {
198                         retval = mfd_add_hotplug_devices(ec->dev,
199                                                 cros_subdevices[i].mfd_cells,
200                                                 cros_subdevices[i].num_cells);
201                         if (retval)
202                                 dev_err(ec->dev,
203                                         "failed to add %s subdevice: %d\n",
204                                         cros_subdevices[i].mfd_cells->name,
205                                         retval);
206                 }
207         }
208
209         /*
210          * The PD notifier driver cell is separate since it only needs to be
211          * explicitly added on platforms that don't have the PD notifier ACPI
212          * device entry defined.
213          */
214         if (IS_ENABLED(CONFIG_OF) && ec->ec_dev->dev->of_node) {
215                 if (cros_ec_check_features(ec, EC_FEATURE_USB_PD)) {
216                         retval = mfd_add_hotplug_devices(ec->dev,
217                                         cros_usbpd_notify_cells,
218                                         ARRAY_SIZE(cros_usbpd_notify_cells));
219                         if (retval)
220                                 dev_err(ec->dev,
221                                         "failed to add PD notify devices: %d\n",
222                                         retval);
223                 }
224         }
225
226         /*
227          * The following subdevices cannot be detected by sending the
228          * EC_FEATURE_GET_CMD to the Embedded Controller device.
229          */
230         retval = mfd_add_hotplug_devices(ec->dev, cros_ec_platform_cells,
231                                          ARRAY_SIZE(cros_ec_platform_cells));
232         if (retval)
233                 dev_warn(ec->dev,
234                          "failed to add cros-ec platform devices: %d\n",
235                          retval);
236
237         /* Check whether this EC instance has a VBC NVRAM */
238         node = ec->ec_dev->dev->of_node;
239         if (of_property_read_bool(node, "google,has-vbc-nvram")) {
240                 retval = mfd_add_hotplug_devices(ec->dev, cros_ec_vbc_cells,
241                                                 ARRAY_SIZE(cros_ec_vbc_cells));
242                 if (retval)
243                         dev_warn(ec->dev, "failed to add VBC devices: %d\n",
244                                  retval);
245         }
246
247         return 0;
248
249 failed:
250         put_device(&ec->class_dev);
251         return retval;
252 }
253
254 static int ec_device_remove(struct platform_device *pdev)
255 {
256         struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev);
257
258         mfd_remove_devices(ec->dev);
259         device_unregister(&ec->class_dev);
260         return 0;
261 }
262
263 static const struct platform_device_id cros_ec_id[] = {
264         { DRV_NAME, 0 },
265         { /* sentinel */ }
266 };
267 MODULE_DEVICE_TABLE(platform, cros_ec_id);
268
269 static struct platform_driver cros_ec_dev_driver = {
270         .driver = {
271                 .name = DRV_NAME,
272         },
273         .id_table = cros_ec_id,
274         .probe = ec_device_probe,
275         .remove = ec_device_remove,
276 };
277
278 static int __init cros_ec_dev_init(void)
279 {
280         int ret;
281
282         ret  = class_register(&cros_class);
283         if (ret) {
284                 pr_err(CROS_EC_DEV_NAME ": failed to register device class\n");
285                 return ret;
286         }
287
288         /* Register the driver */
289         ret = platform_driver_register(&cros_ec_dev_driver);
290         if (ret < 0) {
291                 pr_warn(CROS_EC_DEV_NAME ": can't register driver: %d\n", ret);
292                 goto failed_devreg;
293         }
294         return 0;
295
296 failed_devreg:
297         class_unregister(&cros_class);
298         return ret;
299 }
300
301 static void __exit cros_ec_dev_exit(void)
302 {
303         platform_driver_unregister(&cros_ec_dev_driver);
304         class_unregister(&cros_class);
305 }
306
307 module_init(cros_ec_dev_init);
308 module_exit(cros_ec_dev_exit);
309
310 MODULE_ALIAS("platform:" DRV_NAME);
311 MODULE_AUTHOR("Bill Richardson <wfrichar@chromium.org>");
312 MODULE_DESCRIPTION("Userspace interface to the Chrome OS Embedded Controller");
313 MODULE_VERSION("1.0");
314 MODULE_LICENSE("GPL");