xref: /linux/drivers/platform/chrome/chromeos_of_hw_prober.c (revision cdd30ebb1b9f36159d66f088b61aee264e649d7a)
13fc361afSChen-Yu Tsai // SPDX-License-Identifier: GPL-2.0-only
23fc361afSChen-Yu Tsai /*
33fc361afSChen-Yu Tsai  * ChromeOS Device Tree Hardware Prober
43fc361afSChen-Yu Tsai  *
53fc361afSChen-Yu Tsai  * Copyright (c) 2024 Google LLC
63fc361afSChen-Yu Tsai  */
73fc361afSChen-Yu Tsai 
83fc361afSChen-Yu Tsai #include <linux/array_size.h>
93fc361afSChen-Yu Tsai #include <linux/errno.h>
103fc361afSChen-Yu Tsai #include <linux/i2c-of-prober.h>
113fc361afSChen-Yu Tsai #include <linux/module.h>
123fc361afSChen-Yu Tsai #include <linux/of.h>
133fc361afSChen-Yu Tsai #include <linux/platform_device.h>
143fc361afSChen-Yu Tsai #include <linux/stddef.h>
153fc361afSChen-Yu Tsai 
163fc361afSChen-Yu Tsai #define DRV_NAME	"chromeos_of_hw_prober"
173fc361afSChen-Yu Tsai 
183fc361afSChen-Yu Tsai /**
193fc361afSChen-Yu Tsai  * struct hw_prober_entry - Holds an entry for the hardware prober
203fc361afSChen-Yu Tsai  *
213fc361afSChen-Yu Tsai  * @compatible:	compatible string to match against the machine
223fc361afSChen-Yu Tsai  * @prober:	prober function to call when machine matches
233fc361afSChen-Yu Tsai  * @data:	extra data for the prober function
243fc361afSChen-Yu Tsai  */
253fc361afSChen-Yu Tsai struct hw_prober_entry {
263fc361afSChen-Yu Tsai 	const char *compatible;
273fc361afSChen-Yu Tsai 	int (*prober)(struct device *dev, const void *data);
283fc361afSChen-Yu Tsai 	const void *data;
293fc361afSChen-Yu Tsai };
303fc361afSChen-Yu Tsai 
313fc361afSChen-Yu Tsai struct chromeos_i2c_probe_data {
323fc361afSChen-Yu Tsai 	const struct i2c_of_probe_cfg *cfg;
333fc361afSChen-Yu Tsai 	const struct i2c_of_probe_simple_opts *opts;
343fc361afSChen-Yu Tsai };
353fc361afSChen-Yu Tsai 
chromeos_i2c_component_prober(struct device * dev,const void * _data)363fc361afSChen-Yu Tsai static int chromeos_i2c_component_prober(struct device *dev, const void *_data)
373fc361afSChen-Yu Tsai {
383fc361afSChen-Yu Tsai 	const struct chromeos_i2c_probe_data *data = _data;
393fc361afSChen-Yu Tsai 	struct i2c_of_probe_simple_ctx ctx = {
403fc361afSChen-Yu Tsai 		.opts = data->opts,
413fc361afSChen-Yu Tsai 	};
423fc361afSChen-Yu Tsai 
433fc361afSChen-Yu Tsai 	return i2c_of_probe_component(dev, data->cfg, &ctx);
443fc361afSChen-Yu Tsai }
453fc361afSChen-Yu Tsai 
463fc361afSChen-Yu Tsai #define DEFINE_CHROMEOS_I2C_PROBE_CFG_SIMPLE_BY_TYPE(_type)					\
473fc361afSChen-Yu Tsai 	static const struct i2c_of_probe_cfg chromeos_i2c_probe_simple_ ## _type ## _cfg = {	\
483fc361afSChen-Yu Tsai 		.type = #_type,									\
493fc361afSChen-Yu Tsai 		.ops = &i2c_of_probe_simple_ops,						\
503fc361afSChen-Yu Tsai 	}
513fc361afSChen-Yu Tsai 
523fc361afSChen-Yu Tsai #define DEFINE_CHROMEOS_I2C_PROBE_DATA_DUMB_BY_TYPE(_type)					\
533fc361afSChen-Yu Tsai 	static const struct chromeos_i2c_probe_data chromeos_i2c_probe_dumb_ ## _type = {	\
543fc361afSChen-Yu Tsai 		.cfg = &(const struct i2c_of_probe_cfg) {					\
553fc361afSChen-Yu Tsai 			.type = #_type,								\
563fc361afSChen-Yu Tsai 		},										\
573fc361afSChen-Yu Tsai 	}
583fc361afSChen-Yu Tsai 
593fc361afSChen-Yu Tsai DEFINE_CHROMEOS_I2C_PROBE_DATA_DUMB_BY_TYPE(touchscreen);
603fc361afSChen-Yu Tsai 
613fc361afSChen-Yu Tsai DEFINE_CHROMEOS_I2C_PROBE_CFG_SIMPLE_BY_TYPE(trackpad);
623fc361afSChen-Yu Tsai 
633fc361afSChen-Yu Tsai static const struct chromeos_i2c_probe_data chromeos_i2c_probe_hana_trackpad = {
643fc361afSChen-Yu Tsai 	.cfg = &chromeos_i2c_probe_simple_trackpad_cfg,
653fc361afSChen-Yu Tsai 	.opts = &(const struct i2c_of_probe_simple_opts) {
663fc361afSChen-Yu Tsai 		.res_node_compatible = "elan,ekth3000",
673fc361afSChen-Yu Tsai 		.supply_name = "vcc",
683fc361afSChen-Yu Tsai 		/*
693fc361afSChen-Yu Tsai 		 * ELAN trackpad needs 2 ms for H/W init and 100 ms for F/W init.
703fc361afSChen-Yu Tsai 		 * Synaptics trackpad needs 100 ms.
713fc361afSChen-Yu Tsai 		 * However, the regulator is set to "always-on", presumably to
723fc361afSChen-Yu Tsai 		 * avoid this delay. The ELAN driver is also missing delays.
733fc361afSChen-Yu Tsai 		 */
743fc361afSChen-Yu Tsai 		.post_power_on_delay_ms = 0,
753fc361afSChen-Yu Tsai 	},
763fc361afSChen-Yu Tsai };
773fc361afSChen-Yu Tsai 
783fc361afSChen-Yu Tsai static const struct hw_prober_entry hw_prober_platforms[] = {
793fc361afSChen-Yu Tsai 	{
803fc361afSChen-Yu Tsai 		.compatible = "google,hana",
813fc361afSChen-Yu Tsai 		.prober = chromeos_i2c_component_prober,
823fc361afSChen-Yu Tsai 		.data = &chromeos_i2c_probe_dumb_touchscreen,
833fc361afSChen-Yu Tsai 	}, {
843fc361afSChen-Yu Tsai 		.compatible = "google,hana",
853fc361afSChen-Yu Tsai 		.prober = chromeos_i2c_component_prober,
863fc361afSChen-Yu Tsai 		.data = &chromeos_i2c_probe_hana_trackpad,
873fc361afSChen-Yu Tsai 	},
883fc361afSChen-Yu Tsai };
893fc361afSChen-Yu Tsai 
chromeos_of_hw_prober_probe(struct platform_device * pdev)903fc361afSChen-Yu Tsai static int chromeos_of_hw_prober_probe(struct platform_device *pdev)
913fc361afSChen-Yu Tsai {
923fc361afSChen-Yu Tsai 	for (size_t i = 0; i < ARRAY_SIZE(hw_prober_platforms); i++) {
933fc361afSChen-Yu Tsai 		int ret;
943fc361afSChen-Yu Tsai 
953fc361afSChen-Yu Tsai 		if (!of_machine_is_compatible(hw_prober_platforms[i].compatible))
963fc361afSChen-Yu Tsai 			continue;
973fc361afSChen-Yu Tsai 
983fc361afSChen-Yu Tsai 		ret = hw_prober_platforms[i].prober(&pdev->dev, hw_prober_platforms[i].data);
993fc361afSChen-Yu Tsai 		/* Ignore unrecoverable errors and keep going through other probers */
1003fc361afSChen-Yu Tsai 		if (ret == -EPROBE_DEFER)
1013fc361afSChen-Yu Tsai 			return ret;
1023fc361afSChen-Yu Tsai 	}
1033fc361afSChen-Yu Tsai 
1043fc361afSChen-Yu Tsai 	return 0;
1053fc361afSChen-Yu Tsai }
1063fc361afSChen-Yu Tsai 
1073fc361afSChen-Yu Tsai static struct platform_driver chromeos_of_hw_prober_driver = {
1083fc361afSChen-Yu Tsai 	.probe	= chromeos_of_hw_prober_probe,
1093fc361afSChen-Yu Tsai 	.driver	= {
1103fc361afSChen-Yu Tsai 		.name = DRV_NAME,
1113fc361afSChen-Yu Tsai 	},
1123fc361afSChen-Yu Tsai };
1133fc361afSChen-Yu Tsai 
1143fc361afSChen-Yu Tsai static struct platform_device *chromeos_of_hw_prober_pdev;
1153fc361afSChen-Yu Tsai 
chromeos_of_hw_prober_driver_init(void)1163fc361afSChen-Yu Tsai static int chromeos_of_hw_prober_driver_init(void)
1173fc361afSChen-Yu Tsai {
1183fc361afSChen-Yu Tsai 	size_t i;
1193fc361afSChen-Yu Tsai 	int ret;
1203fc361afSChen-Yu Tsai 
1213fc361afSChen-Yu Tsai 	for (i = 0; i < ARRAY_SIZE(hw_prober_platforms); i++)
1223fc361afSChen-Yu Tsai 		if (of_machine_is_compatible(hw_prober_platforms[i].compatible))
1233fc361afSChen-Yu Tsai 			break;
1243fc361afSChen-Yu Tsai 	if (i == ARRAY_SIZE(hw_prober_platforms))
1253fc361afSChen-Yu Tsai 		return -ENODEV;
1263fc361afSChen-Yu Tsai 
1273fc361afSChen-Yu Tsai 	ret = platform_driver_register(&chromeos_of_hw_prober_driver);
1283fc361afSChen-Yu Tsai 	if (ret)
1293fc361afSChen-Yu Tsai 		return ret;
1303fc361afSChen-Yu Tsai 
1313fc361afSChen-Yu Tsai 	chromeos_of_hw_prober_pdev =
1323fc361afSChen-Yu Tsai 			platform_device_register_simple(DRV_NAME, PLATFORM_DEVID_NONE, NULL, 0);
1333fc361afSChen-Yu Tsai 	if (IS_ERR(chromeos_of_hw_prober_pdev))
1343fc361afSChen-Yu Tsai 		goto err;
1353fc361afSChen-Yu Tsai 
1363fc361afSChen-Yu Tsai 	return 0;
1373fc361afSChen-Yu Tsai 
1383fc361afSChen-Yu Tsai err:
1393fc361afSChen-Yu Tsai 	platform_driver_unregister(&chromeos_of_hw_prober_driver);
1403fc361afSChen-Yu Tsai 
1413fc361afSChen-Yu Tsai 	return PTR_ERR(chromeos_of_hw_prober_pdev);
1423fc361afSChen-Yu Tsai }
1433fc361afSChen-Yu Tsai module_init(chromeos_of_hw_prober_driver_init);
1443fc361afSChen-Yu Tsai 
chromeos_of_hw_prober_driver_exit(void)1453fc361afSChen-Yu Tsai static void chromeos_of_hw_prober_driver_exit(void)
1463fc361afSChen-Yu Tsai {
1473fc361afSChen-Yu Tsai 	platform_device_unregister(chromeos_of_hw_prober_pdev);
1483fc361afSChen-Yu Tsai 	platform_driver_unregister(&chromeos_of_hw_prober_driver);
1493fc361afSChen-Yu Tsai }
1503fc361afSChen-Yu Tsai module_exit(chromeos_of_hw_prober_driver_exit);
1513fc361afSChen-Yu Tsai 
1523fc361afSChen-Yu Tsai MODULE_LICENSE("GPL");
1533fc361afSChen-Yu Tsai MODULE_DESCRIPTION("ChromeOS device tree hardware prober");
154*cdd30ebbSPeter Zijlstra MODULE_IMPORT_NS("I2C_OF_PROBER");
155