11ccea77eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
25e011558SThierry Escande /*
3*fd0fec40STzung-Bi Shih * ChromeOS Embedded Controller
45e011558SThierry Escande *
55e011558SThierry Escande * Copyright (C) 2014 Google, Inc.
65e011558SThierry Escande */
75e011558SThierry Escande
8a75f4d1fSGwendal Grignou #include <linux/dmi.h>
94602dce0SPrashant Malani #include <linux/kconfig.h>
105e011558SThierry Escande #include <linux/mfd/core.h>
115e011558SThierry Escande #include <linux/module.h>
12ac316725SRandy Dunlap #include <linux/mod_devicetable.h>
13dc0c386eSRob Herring #include <linux/of.h>
145e011558SThierry Escande #include <linux/platform_device.h>
15459aedb9SEnric Balletbo i Serra #include <linux/platform_data/cros_ec_chardev.h>
16840d9f13SEnric Balletbo i Serra #include <linux/platform_data/cros_ec_commands.h>
17840d9f13SEnric Balletbo i Serra #include <linux/platform_data/cros_ec_proto.h>
185e011558SThierry Escande #include <linux/slab.h>
195e011558SThierry Escande
205e011558SThierry Escande #define DRV_NAME "cros-ec-dev"
215e011558SThierry Escande
225e011558SThierry Escande static struct class cros_class = {
235e011558SThierry Escande .name = "chromeos",
245e011558SThierry Escande };
255e011558SThierry Escande
26b027dcf7SEnric Balletbo i Serra /**
275ae3d1bcSLee Jones * struct cros_feature_to_name - CrOS feature id to name/short description.
28b027dcf7SEnric Balletbo i Serra * @id: The feature identifier.
29b027dcf7SEnric Balletbo i Serra * @name: Device name associated with the feature id.
30b027dcf7SEnric Balletbo i Serra * @desc: Short name that will be displayed.
31b027dcf7SEnric Balletbo i Serra */
32b027dcf7SEnric Balletbo i Serra struct cros_feature_to_name {
33b027dcf7SEnric Balletbo i Serra unsigned int id;
34b027dcf7SEnric Balletbo i Serra const char *name;
35b027dcf7SEnric Balletbo i Serra const char *desc;
36b027dcf7SEnric Balletbo i Serra };
37b027dcf7SEnric Balletbo i Serra
38832a636fSEnric Balletbo i Serra /**
395ae3d1bcSLee Jones * struct cros_feature_to_cells - CrOS feature id to mfd cells association.
40832a636fSEnric Balletbo i Serra * @id: The feature identifier.
41832a636fSEnric Balletbo i Serra * @mfd_cells: Pointer to the array of mfd cells that needs to be added.
42832a636fSEnric Balletbo i Serra * @num_cells: Number of mfd cells into the array.
43832a636fSEnric Balletbo i Serra */
44832a636fSEnric Balletbo i Serra struct cros_feature_to_cells {
45832a636fSEnric Balletbo i Serra unsigned int id;
46832a636fSEnric Balletbo i Serra const struct mfd_cell *mfd_cells;
47832a636fSEnric Balletbo i Serra unsigned int num_cells;
48832a636fSEnric Balletbo i Serra };
49832a636fSEnric Balletbo i Serra
50b027dcf7SEnric Balletbo i Serra static const struct cros_feature_to_name cros_mcu_devices[] = {
51b027dcf7SEnric Balletbo i Serra {
52b027dcf7SEnric Balletbo i Serra .id = EC_FEATURE_FINGERPRINT,
53b027dcf7SEnric Balletbo i Serra .name = CROS_EC_DEV_FP_NAME,
54b027dcf7SEnric Balletbo i Serra .desc = "Fingerprint",
55b027dcf7SEnric Balletbo i Serra },
56b027dcf7SEnric Balletbo i Serra {
57b027dcf7SEnric Balletbo i Serra .id = EC_FEATURE_ISH,
58b027dcf7SEnric Balletbo i Serra .name = CROS_EC_DEV_ISH_NAME,
59b027dcf7SEnric Balletbo i Serra .desc = "Integrated Sensor Hub",
60b027dcf7SEnric Balletbo i Serra },
61b027dcf7SEnric Balletbo i Serra {
62b027dcf7SEnric Balletbo i Serra .id = EC_FEATURE_SCP,
63b027dcf7SEnric Balletbo i Serra .name = CROS_EC_DEV_SCP_NAME,
64b027dcf7SEnric Balletbo i Serra .desc = "System Control Processor",
65b027dcf7SEnric Balletbo i Serra },
66b027dcf7SEnric Balletbo i Serra {
67b027dcf7SEnric Balletbo i Serra .id = EC_FEATURE_TOUCHPAD,
68b027dcf7SEnric Balletbo i Serra .name = CROS_EC_DEV_TP_NAME,
69b027dcf7SEnric Balletbo i Serra .desc = "Touchpad",
70b027dcf7SEnric Balletbo i Serra },
71b027dcf7SEnric Balletbo i Serra };
72b027dcf7SEnric Balletbo i Serra
73832a636fSEnric Balletbo i Serra static const struct mfd_cell cros_ec_cec_cells[] = {
74832a636fSEnric Balletbo i Serra { .name = "cros-ec-cec", },
75832a636fSEnric Balletbo i Serra };
76832a636fSEnric Balletbo i Serra
778f49b623SStephen Boyd static const struct mfd_cell cros_ec_gpio_cells[] = {
788f49b623SStephen Boyd { .name = "cros-ec-gpio", },
798f49b623SStephen Boyd };
808f49b623SStephen Boyd
81832a636fSEnric Balletbo i Serra static const struct mfd_cell cros_ec_rtc_cells[] = {
82832a636fSEnric Balletbo i Serra { .name = "cros-ec-rtc", },
83832a636fSEnric Balletbo i Serra };
84832a636fSEnric Balletbo i Serra
85d60ac88aSGwendal Grignou static const struct mfd_cell cros_ec_sensorhub_cells[] = {
86d60ac88aSGwendal Grignou { .name = "cros-ec-sensorhub", },
87d60ac88aSGwendal Grignou };
88d60ac88aSGwendal Grignou
89832a636fSEnric Balletbo i Serra static const struct mfd_cell cros_usbpd_charger_cells[] = {
90555b5fcdSThomas Weißschuh { .name = "cros-charge-control", },
91832a636fSEnric Balletbo i Serra { .name = "cros-usbpd-charger", },
92832a636fSEnric Balletbo i Serra { .name = "cros-usbpd-logger", },
93832a636fSEnric Balletbo i Serra };
94832a636fSEnric Balletbo i Serra
954602dce0SPrashant Malani static const struct mfd_cell cros_usbpd_notify_cells[] = {
964602dce0SPrashant Malani { .name = "cros-usbpd-notify", },
974602dce0SPrashant Malani };
984602dce0SPrashant Malani
996cea614bSLukasz Majczak static const struct mfd_cell cros_ec_wdt_cells[] = {
1006cea614bSLukasz Majczak { .name = "cros-ec-wdt", }
1016cea614bSLukasz Majczak };
1026cea614bSLukasz Majczak
103b107093fSThomas Weißschuh static const struct mfd_cell cros_ec_led_cells[] = {
104b107093fSThomas Weißschuh { .name = "cros-ec-led", },
105b107093fSThomas Weißschuh };
106b107093fSThomas Weißschuh
107970c3a6bSThomas Weißschuh static const struct mfd_cell cros_ec_keyboard_leds_cells[] = {
108970c3a6bSThomas Weißschuh { .name = "cros-keyboard-leds", },
109970c3a6bSThomas Weißschuh };
110970c3a6bSThomas Weißschuh
111832a636fSEnric Balletbo i Serra static const struct cros_feature_to_cells cros_subdevices[] = {
112832a636fSEnric Balletbo i Serra {
113832a636fSEnric Balletbo i Serra .id = EC_FEATURE_CEC,
114832a636fSEnric Balletbo i Serra .mfd_cells = cros_ec_cec_cells,
115832a636fSEnric Balletbo i Serra .num_cells = ARRAY_SIZE(cros_ec_cec_cells),
116832a636fSEnric Balletbo i Serra },
117832a636fSEnric Balletbo i Serra {
1188f49b623SStephen Boyd .id = EC_FEATURE_GPIO,
1198f49b623SStephen Boyd .mfd_cells = cros_ec_gpio_cells,
1208f49b623SStephen Boyd .num_cells = ARRAY_SIZE(cros_ec_gpio_cells),
1218f49b623SStephen Boyd },
1228f49b623SStephen Boyd {
123832a636fSEnric Balletbo i Serra .id = EC_FEATURE_RTC,
124832a636fSEnric Balletbo i Serra .mfd_cells = cros_ec_rtc_cells,
125832a636fSEnric Balletbo i Serra .num_cells = ARRAY_SIZE(cros_ec_rtc_cells),
126832a636fSEnric Balletbo i Serra },
127832a636fSEnric Balletbo i Serra {
128832a636fSEnric Balletbo i Serra .id = EC_FEATURE_USB_PD,
129832a636fSEnric Balletbo i Serra .mfd_cells = cros_usbpd_charger_cells,
130832a636fSEnric Balletbo i Serra .num_cells = ARRAY_SIZE(cros_usbpd_charger_cells),
131832a636fSEnric Balletbo i Serra },
1326cea614bSLukasz Majczak {
1336cea614bSLukasz Majczak .id = EC_FEATURE_HANG_DETECT,
1346cea614bSLukasz Majczak .mfd_cells = cros_ec_wdt_cells,
1356cea614bSLukasz Majczak .num_cells = ARRAY_SIZE(cros_ec_wdt_cells),
1366cea614bSLukasz Majczak },
137b107093fSThomas Weißschuh {
138b107093fSThomas Weißschuh .id = EC_FEATURE_LED,
139b107093fSThomas Weißschuh .mfd_cells = cros_ec_led_cells,
140b107093fSThomas Weißschuh .num_cells = ARRAY_SIZE(cros_ec_led_cells),
141b107093fSThomas Weißschuh },
1425ffe70d8SLee Jones {
143970c3a6bSThomas Weißschuh .id = EC_FEATURE_PWM_KEYB,
144970c3a6bSThomas Weißschuh .mfd_cells = cros_ec_keyboard_leds_cells,
145970c3a6bSThomas Weißschuh .num_cells = ARRAY_SIZE(cros_ec_keyboard_leds_cells),
146970c3a6bSThomas Weißschuh },
147832a636fSEnric Balletbo i Serra };
148832a636fSEnric Balletbo i Serra
149832a636fSEnric Balletbo i Serra static const struct mfd_cell cros_ec_platform_cells[] = {
150832a636fSEnric Balletbo i Serra { .name = "cros-ec-chardev", },
151832a636fSEnric Balletbo i Serra { .name = "cros-ec-debugfs", },
1526ca6a63eSThomas Weißschuh { .name = "cros-ec-hwmon", },
153832a636fSEnric Balletbo i Serra { .name = "cros-ec-sysfs", },
154ff23a46eSStephen Boyd };
155ff23a46eSStephen Boyd
156ff23a46eSStephen Boyd static const struct mfd_cell cros_ec_pchg_cells[] = {
1578a14ded5SDaisuke Nojiri { .name = "cros-ec-pchg", },
158832a636fSEnric Balletbo i Serra };
159832a636fSEnric Balletbo i Serra
160a75f4d1fSGwendal Grignou static const struct mfd_cell cros_ec_lightbar_cells[] = {
161a75f4d1fSGwendal Grignou { .name = "cros-ec-lightbar", }
162a75f4d1fSGwendal Grignou };
163a75f4d1fSGwendal Grignou
164832a636fSEnric Balletbo i Serra static const struct mfd_cell cros_ec_vbc_cells[] = {
165832a636fSEnric Balletbo i Serra { .name = "cros-ec-vbc", }
166832a636fSEnric Balletbo i Serra };
167832a636fSEnric Balletbo i Serra
cros_ec_class_release(struct device * dev)16848a2ca0eSEnric Balletbo i Serra static void cros_ec_class_release(struct device *dev)
16948a2ca0eSEnric Balletbo i Serra {
17048a2ca0eSEnric Balletbo i Serra kfree(to_cros_ec_dev(dev));
17148a2ca0eSEnric Balletbo i Serra }
17248a2ca0eSEnric Balletbo i Serra
ec_device_probe(struct platform_device * pdev)1735e011558SThierry Escande static int ec_device_probe(struct platform_device *pdev)
1745e011558SThierry Escande {
1755e011558SThierry Escande int retval = -ENOMEM;
1760545625bSEnric Balletbo i Serra struct device_node *node;
1775e011558SThierry Escande struct device *dev = &pdev->dev;
1785e011558SThierry Escande struct cros_ec_platform *ec_platform = dev_get_platdata(dev);
17948a2ca0eSEnric Balletbo i Serra struct cros_ec_dev *ec = kzalloc(sizeof(*ec), GFP_KERNEL);
180ff23a46eSStephen Boyd struct ec_response_pchg_count pchg_count;
181b027dcf7SEnric Balletbo i Serra int i;
1825e011558SThierry Escande
1835e011558SThierry Escande if (!ec)
1845e011558SThierry Escande return retval;
1855e011558SThierry Escande
1865e011558SThierry Escande dev_set_drvdata(dev, ec);
1875e011558SThierry Escande ec->ec_dev = dev_get_drvdata(dev->parent);
1885e011558SThierry Escande ec->dev = dev;
1895e011558SThierry Escande ec->cmd_offset = ec_platform->cmd_offset;
1907ff22787SPrashant Malani ec->features.flags[0] = -1U; /* Not cached yet */
1917ff22787SPrashant Malani ec->features.flags[1] = -1U; /* Not cached yet */
1925e011558SThierry Escande device_initialize(&ec->class_dev);
1935e011558SThierry Escande
194b027dcf7SEnric Balletbo i Serra for (i = 0; i < ARRAY_SIZE(cros_mcu_devices); i++) {
19590486af5SEnric Balletbo i Serra /*
196b027dcf7SEnric Balletbo i Serra * Check whether this is actually a dedicated MCU rather
197b027dcf7SEnric Balletbo i Serra * than an standard EC.
198b027dcf7SEnric Balletbo i Serra */
199b027dcf7SEnric Balletbo i Serra if (cros_ec_check_features(ec, cros_mcu_devices[i].id)) {
200b027dcf7SEnric Balletbo i Serra dev_info(dev, "CrOS %s MCU detected\n",
201b027dcf7SEnric Balletbo i Serra cros_mcu_devices[i].desc);
202b027dcf7SEnric Balletbo i Serra /*
203b027dcf7SEnric Balletbo i Serra * Help userspace differentiating ECs from other MCU,
20490486af5SEnric Balletbo i Serra * regardless of the probing order.
20590486af5SEnric Balletbo i Serra */
206b027dcf7SEnric Balletbo i Serra ec_platform->ec_name = cros_mcu_devices[i].name;
207b027dcf7SEnric Balletbo i Serra break;
20890486af5SEnric Balletbo i Serra }
209554e937eSPi-Hsun Shih }
210554e937eSPi-Hsun Shih
211d4cee950SRushikesh S Kadam /*
2125e011558SThierry Escande * Add the class device
2135e011558SThierry Escande */
2145e011558SThierry Escande ec->class_dev.class = &cros_class;
2155e011558SThierry Escande ec->class_dev.parent = dev;
21648a2ca0eSEnric Balletbo i Serra ec->class_dev.release = cros_ec_class_release;
2175e011558SThierry Escande
2185e011558SThierry Escande retval = dev_set_name(&ec->class_dev, "%s", ec_platform->ec_name);
2195e011558SThierry Escande if (retval) {
2205e011558SThierry Escande dev_err(dev, "dev_set_name failed => %d\n", retval);
2215e011558SThierry Escande goto failed;
2225e011558SThierry Escande }
2235e011558SThierry Escande
224459aedb9SEnric Balletbo i Serra retval = device_add(&ec->class_dev);
225459aedb9SEnric Balletbo i Serra if (retval)
226459aedb9SEnric Balletbo i Serra goto failed;
227459aedb9SEnric Balletbo i Serra
228c1d1e91aSGwendal Grignou /* check whether this EC is a sensor hub. */
229d60ac88aSGwendal Grignou if (cros_ec_get_sensor_count(ec) > 0) {
230d60ac88aSGwendal Grignou retval = mfd_add_hotplug_devices(ec->dev,
231d60ac88aSGwendal Grignou cros_ec_sensorhub_cells,
232d60ac88aSGwendal Grignou ARRAY_SIZE(cros_ec_sensorhub_cells));
233d60ac88aSGwendal Grignou if (retval)
234d60ac88aSGwendal Grignou dev_err(ec->dev, "failed to add %s subdevice: %d\n",
235d60ac88aSGwendal Grignou cros_ec_sensorhub_cells->name, retval);
236d60ac88aSGwendal Grignou }
237c1d1e91aSGwendal Grignou
238832a636fSEnric Balletbo i Serra /*
239832a636fSEnric Balletbo i Serra * The following subdevices can be detected by sending the
240832a636fSEnric Balletbo i Serra * EC_FEATURE_GET_CMD Embedded Controller device.
241832a636fSEnric Balletbo i Serra */
242832a636fSEnric Balletbo i Serra for (i = 0; i < ARRAY_SIZE(cros_subdevices); i++) {
243832a636fSEnric Balletbo i Serra if (cros_ec_check_features(ec, cros_subdevices[i].id)) {
244832a636fSEnric Balletbo i Serra retval = mfd_add_hotplug_devices(ec->dev,
245832a636fSEnric Balletbo i Serra cros_subdevices[i].mfd_cells,
246832a636fSEnric Balletbo i Serra cros_subdevices[i].num_cells);
24703a5755cSNeil Armstrong if (retval)
24803a5755cSNeil Armstrong dev_err(ec->dev,
249832a636fSEnric Balletbo i Serra "failed to add %s subdevice: %d\n",
250832a636fSEnric Balletbo i Serra cros_subdevices[i].mfd_cells->name,
25103a5755cSNeil Armstrong retval);
25203a5755cSNeil Armstrong }
25395a4d07fSEnric Balletbo i Serra }
25495a4d07fSEnric Balletbo i Serra
255832a636fSEnric Balletbo i Serra /*
256a75f4d1fSGwendal Grignou * Lightbar is a special case. Newer devices support autodetection,
257a75f4d1fSGwendal Grignou * but older ones do not.
258a75f4d1fSGwendal Grignou */
259a75f4d1fSGwendal Grignou if (cros_ec_check_features(ec, EC_FEATURE_LIGHTBAR) ||
260a75f4d1fSGwendal Grignou dmi_match(DMI_PRODUCT_NAME, "Link")) {
261a75f4d1fSGwendal Grignou retval = mfd_add_hotplug_devices(ec->dev,
262a75f4d1fSGwendal Grignou cros_ec_lightbar_cells,
263a75f4d1fSGwendal Grignou ARRAY_SIZE(cros_ec_lightbar_cells));
264a75f4d1fSGwendal Grignou if (retval)
265a75f4d1fSGwendal Grignou dev_warn(ec->dev, "failed to add lightbar: %d\n",
266a75f4d1fSGwendal Grignou retval);
267a75f4d1fSGwendal Grignou }
268a75f4d1fSGwendal Grignou
269a75f4d1fSGwendal Grignou /*
2704602dce0SPrashant Malani * The PD notifier driver cell is separate since it only needs to be
2714602dce0SPrashant Malani * explicitly added on platforms that don't have the PD notifier ACPI
2724602dce0SPrashant Malani * device entry defined.
2734602dce0SPrashant Malani */
274f8db89d1SPrashant Malani if (IS_ENABLED(CONFIG_OF) && ec->ec_dev->dev->of_node) {
2754602dce0SPrashant Malani if (cros_ec_check_features(ec, EC_FEATURE_USB_PD)) {
2764602dce0SPrashant Malani retval = mfd_add_hotplug_devices(ec->dev,
2774602dce0SPrashant Malani cros_usbpd_notify_cells,
2784602dce0SPrashant Malani ARRAY_SIZE(cros_usbpd_notify_cells));
2794602dce0SPrashant Malani if (retval)
2804602dce0SPrashant Malani dev_err(ec->dev,
2814602dce0SPrashant Malani "failed to add PD notify devices: %d\n",
2824602dce0SPrashant Malani retval);
2834602dce0SPrashant Malani }
2844602dce0SPrashant Malani }
2854602dce0SPrashant Malani
2864602dce0SPrashant Malani /*
287ff23a46eSStephen Boyd * The PCHG device cannot be detected by sending EC_FEATURE_GET_CMD, but
288ff23a46eSStephen Boyd * it can be detected by querying the number of peripheral chargers.
289ff23a46eSStephen Boyd */
290b1d288d9SPrashant Malani retval = cros_ec_cmd(ec->ec_dev, 0, EC_CMD_PCHG_COUNT, NULL, 0,
291ff23a46eSStephen Boyd &pchg_count, sizeof(pchg_count));
292ff23a46eSStephen Boyd if (retval >= 0 && pchg_count.port_count) {
293ff23a46eSStephen Boyd retval = mfd_add_hotplug_devices(ec->dev,
294ff23a46eSStephen Boyd cros_ec_pchg_cells,
295ff23a46eSStephen Boyd ARRAY_SIZE(cros_ec_pchg_cells));
296ff23a46eSStephen Boyd if (retval)
297ff23a46eSStephen Boyd dev_warn(ec->dev, "failed to add pchg: %d\n",
298ff23a46eSStephen Boyd retval);
299ff23a46eSStephen Boyd }
300ff23a46eSStephen Boyd
301ff23a46eSStephen Boyd /*
302832a636fSEnric Balletbo i Serra * The following subdevices cannot be detected by sending the
303832a636fSEnric Balletbo i Serra * EC_FEATURE_GET_CMD to the Embedded Controller device.
304832a636fSEnric Balletbo i Serra */
30528e6fcc8SEnric Balletbo i Serra retval = mfd_add_hotplug_devices(ec->dev, cros_ec_platform_cells,
30628e6fcc8SEnric Balletbo i Serra ARRAY_SIZE(cros_ec_platform_cells));
307ecf8a6cdSEnric Balletbo i Serra if (retval)
308ecf8a6cdSEnric Balletbo i Serra dev_warn(ec->dev,
309ecf8a6cdSEnric Balletbo i Serra "failed to add cros-ec platform devices: %d\n",
310ecf8a6cdSEnric Balletbo i Serra retval);
311ecf8a6cdSEnric Balletbo i Serra
3120545625bSEnric Balletbo i Serra /* Check whether this EC instance has a VBC NVRAM */
3130545625bSEnric Balletbo i Serra node = ec->ec_dev->dev->of_node;
3140545625bSEnric Balletbo i Serra if (of_property_read_bool(node, "google,has-vbc-nvram")) {
31528e6fcc8SEnric Balletbo i Serra retval = mfd_add_hotplug_devices(ec->dev, cros_ec_vbc_cells,
31628e6fcc8SEnric Balletbo i Serra ARRAY_SIZE(cros_ec_vbc_cells));
3170545625bSEnric Balletbo i Serra if (retval)
3180545625bSEnric Balletbo i Serra dev_warn(ec->dev, "failed to add VBC devices: %d\n",
3190545625bSEnric Balletbo i Serra retval);
3200545625bSEnric Balletbo i Serra }
3210545625bSEnric Balletbo i Serra
3225e011558SThierry Escande return 0;
3235e011558SThierry Escande
3245e011558SThierry Escande failed:
3255e011558SThierry Escande put_device(&ec->class_dev);
3265e011558SThierry Escande return retval;
3275e011558SThierry Escande }
3285e011558SThierry Escande
ec_device_remove(struct platform_device * pdev)32913b254c0SUwe Kleine-König static void ec_device_remove(struct platform_device *pdev)
3305e011558SThierry Escande {
3315e011558SThierry Escande struct cros_ec_dev *ec = dev_get_drvdata(&pdev->dev);
3325e011558SThierry Escande
33318e294ddSEnric Balletbo i Serra mfd_remove_devices(ec->dev);
3345e011558SThierry Escande device_unregister(&ec->class_dev);
3355e011558SThierry Escande }
3365e011558SThierry Escande
3375e011558SThierry Escande static const struct platform_device_id cros_ec_id[] = {
3385e011558SThierry Escande { DRV_NAME, 0 },
339abeed71bSWei-Ning Huang { /* sentinel */ }
3405e011558SThierry Escande };
3415e011558SThierry Escande MODULE_DEVICE_TABLE(platform, cros_ec_id);
3425e011558SThierry Escande
3435e011558SThierry Escande static struct platform_driver cros_ec_dev_driver = {
3445e011558SThierry Escande .driver = {
3455e011558SThierry Escande .name = DRV_NAME,
3465e011558SThierry Escande },
3476eb35784SNathan Chancellor .id_table = cros_ec_id,
3485e011558SThierry Escande .probe = ec_device_probe,
34913b254c0SUwe Kleine-König .remove_new = ec_device_remove,
3505e011558SThierry Escande };
3515e011558SThierry Escande
cros_ec_dev_init(void)3525e011558SThierry Escande static int __init cros_ec_dev_init(void)
3535e011558SThierry Escande {
3545e011558SThierry Escande int ret;
3555e011558SThierry Escande
3565e011558SThierry Escande ret = class_register(&cros_class);
3575e011558SThierry Escande if (ret) {
3585e011558SThierry Escande pr_err(CROS_EC_DEV_NAME ": failed to register device class\n");
3595e011558SThierry Escande return ret;
3605e011558SThierry Escande }
3615e011558SThierry Escande
3625e011558SThierry Escande ret = platform_driver_register(&cros_ec_dev_driver);
363dc98e25bSTzung-Bi Shih if (ret) {
3645e011558SThierry Escande pr_warn(CROS_EC_DEV_NAME ": can't register driver: %d\n", ret);
3655e011558SThierry Escande class_unregister(&cros_class);
366dc98e25bSTzung-Bi Shih }
3675e011558SThierry Escande return ret;
3685e011558SThierry Escande }
3695e011558SThierry Escande
cros_ec_dev_exit(void)3705e011558SThierry Escande static void __exit cros_ec_dev_exit(void)
3715e011558SThierry Escande {
3725e011558SThierry Escande platform_driver_unregister(&cros_ec_dev_driver);
3735e011558SThierry Escande class_unregister(&cros_class);
3745e011558SThierry Escande }
3755e011558SThierry Escande
3765e011558SThierry Escande module_init(cros_ec_dev_init);
3775e011558SThierry Escande module_exit(cros_ec_dev_exit);
3785e011558SThierry Escande
3795e011558SThierry Escande MODULE_AUTHOR("Bill Richardson <wfrichar@chromium.org>");
380*fd0fec40STzung-Bi Shih MODULE_DESCRIPTION("ChromeOS Embedded Controller");
3815e011558SThierry Escande MODULE_VERSION("1.0");
3825e011558SThierry Escande MODULE_LICENSE("GPL");
383