xref: /linux/drivers/platform/chrome/chromeos_of_hw_prober.c (revision 7f71507851fc7764b36a3221839607d3a45c2025)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * ChromeOS Device Tree Hardware Prober
4  *
5  * Copyright (c) 2024 Google LLC
6  */
7 
8 #include <linux/array_size.h>
9 #include <linux/errno.h>
10 #include <linux/i2c-of-prober.h>
11 #include <linux/module.h>
12 #include <linux/of.h>
13 #include <linux/platform_device.h>
14 #include <linux/stddef.h>
15 
16 #define DRV_NAME	"chromeos_of_hw_prober"
17 
18 /**
19  * struct hw_prober_entry - Holds an entry for the hardware prober
20  *
21  * @compatible:	compatible string to match against the machine
22  * @prober:	prober function to call when machine matches
23  * @data:	extra data for the prober function
24  */
25 struct hw_prober_entry {
26 	const char *compatible;
27 	int (*prober)(struct device *dev, const void *data);
28 	const void *data;
29 };
30 
31 struct chromeos_i2c_probe_data {
32 	const struct i2c_of_probe_cfg *cfg;
33 	const struct i2c_of_probe_simple_opts *opts;
34 };
35 
36 static int chromeos_i2c_component_prober(struct device *dev, const void *_data)
37 {
38 	const struct chromeos_i2c_probe_data *data = _data;
39 	struct i2c_of_probe_simple_ctx ctx = {
40 		.opts = data->opts,
41 	};
42 
43 	return i2c_of_probe_component(dev, data->cfg, &ctx);
44 }
45 
46 #define DEFINE_CHROMEOS_I2C_PROBE_CFG_SIMPLE_BY_TYPE(_type)					\
47 	static const struct i2c_of_probe_cfg chromeos_i2c_probe_simple_ ## _type ## _cfg = {	\
48 		.type = #_type,									\
49 		.ops = &i2c_of_probe_simple_ops,						\
50 	}
51 
52 #define DEFINE_CHROMEOS_I2C_PROBE_DATA_DUMB_BY_TYPE(_type)					\
53 	static const struct chromeos_i2c_probe_data chromeos_i2c_probe_dumb_ ## _type = {	\
54 		.cfg = &(const struct i2c_of_probe_cfg) {					\
55 			.type = #_type,								\
56 		},										\
57 	}
58 
59 DEFINE_CHROMEOS_I2C_PROBE_DATA_DUMB_BY_TYPE(touchscreen);
60 
61 DEFINE_CHROMEOS_I2C_PROBE_CFG_SIMPLE_BY_TYPE(trackpad);
62 
63 static const struct chromeos_i2c_probe_data chromeos_i2c_probe_hana_trackpad = {
64 	.cfg = &chromeos_i2c_probe_simple_trackpad_cfg,
65 	.opts = &(const struct i2c_of_probe_simple_opts) {
66 		.res_node_compatible = "elan,ekth3000",
67 		.supply_name = "vcc",
68 		/*
69 		 * ELAN trackpad needs 2 ms for H/W init and 100 ms for F/W init.
70 		 * Synaptics trackpad needs 100 ms.
71 		 * However, the regulator is set to "always-on", presumably to
72 		 * avoid this delay. The ELAN driver is also missing delays.
73 		 */
74 		.post_power_on_delay_ms = 0,
75 	},
76 };
77 
78 static const struct hw_prober_entry hw_prober_platforms[] = {
79 	{
80 		.compatible = "google,hana",
81 		.prober = chromeos_i2c_component_prober,
82 		.data = &chromeos_i2c_probe_dumb_touchscreen,
83 	}, {
84 		.compatible = "google,hana",
85 		.prober = chromeos_i2c_component_prober,
86 		.data = &chromeos_i2c_probe_hana_trackpad,
87 	},
88 };
89 
90 static int chromeos_of_hw_prober_probe(struct platform_device *pdev)
91 {
92 	for (size_t i = 0; i < ARRAY_SIZE(hw_prober_platforms); i++) {
93 		int ret;
94 
95 		if (!of_machine_is_compatible(hw_prober_platforms[i].compatible))
96 			continue;
97 
98 		ret = hw_prober_platforms[i].prober(&pdev->dev, hw_prober_platforms[i].data);
99 		/* Ignore unrecoverable errors and keep going through other probers */
100 		if (ret == -EPROBE_DEFER)
101 			return ret;
102 	}
103 
104 	return 0;
105 }
106 
107 static struct platform_driver chromeos_of_hw_prober_driver = {
108 	.probe	= chromeos_of_hw_prober_probe,
109 	.driver	= {
110 		.name = DRV_NAME,
111 	},
112 };
113 
114 static struct platform_device *chromeos_of_hw_prober_pdev;
115 
116 static int chromeos_of_hw_prober_driver_init(void)
117 {
118 	size_t i;
119 	int ret;
120 
121 	for (i = 0; i < ARRAY_SIZE(hw_prober_platforms); i++)
122 		if (of_machine_is_compatible(hw_prober_platforms[i].compatible))
123 			break;
124 	if (i == ARRAY_SIZE(hw_prober_platforms))
125 		return -ENODEV;
126 
127 	ret = platform_driver_register(&chromeos_of_hw_prober_driver);
128 	if (ret)
129 		return ret;
130 
131 	chromeos_of_hw_prober_pdev =
132 			platform_device_register_simple(DRV_NAME, PLATFORM_DEVID_NONE, NULL, 0);
133 	if (IS_ERR(chromeos_of_hw_prober_pdev))
134 		goto err;
135 
136 	return 0;
137 
138 err:
139 	platform_driver_unregister(&chromeos_of_hw_prober_driver);
140 
141 	return PTR_ERR(chromeos_of_hw_prober_pdev);
142 }
143 module_init(chromeos_of_hw_prober_driver_init);
144 
145 static void chromeos_of_hw_prober_driver_exit(void)
146 {
147 	platform_device_unregister(chromeos_of_hw_prober_pdev);
148 	platform_driver_unregister(&chromeos_of_hw_prober_driver);
149 }
150 module_exit(chromeos_of_hw_prober_driver_exit);
151 
152 MODULE_LICENSE("GPL");
153 MODULE_DESCRIPTION("ChromeOS device tree hardware prober");
154 MODULE_IMPORT_NS(I2C_OF_PROBER);
155