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