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 DEFINE_CHROMEOS_I2C_PROBE_DATA_DUMB_BY_TYPE(trackpad); 61 62 DEFINE_CHROMEOS_I2C_PROBE_CFG_SIMPLE_BY_TYPE(touchscreen); 63 DEFINE_CHROMEOS_I2C_PROBE_CFG_SIMPLE_BY_TYPE(trackpad); 64 65 static const struct chromeos_i2c_probe_data chromeos_i2c_probe_hana_trackpad = { 66 .cfg = &chromeos_i2c_probe_simple_trackpad_cfg, 67 .opts = &(const struct i2c_of_probe_simple_opts) { 68 .res_node_compatible = "elan,ekth3000", 69 .supply_name = "vcc", 70 /* 71 * ELAN trackpad needs 2 ms for H/W init and 100 ms for F/W init. 72 * Synaptics trackpad needs 100 ms. 73 * However, the regulator is set to "always-on", presumably to 74 * avoid this delay. The ELAN driver is also missing delays. 75 */ 76 .post_power_on_delay_ms = 0, 77 }, 78 }; 79 80 static const struct chromeos_i2c_probe_data chromeos_i2c_probe_squirtle_touchscreen = { 81 .cfg = &chromeos_i2c_probe_simple_touchscreen_cfg, 82 .opts = &(const struct i2c_of_probe_simple_opts) { 83 .res_node_compatible = "elan,ekth6a12nay", 84 .supply_name = "vcc33", 85 .gpio_name = "reset", 86 .post_power_on_delay_ms = 10, 87 .post_gpio_config_delay_ms = 300, 88 }, 89 }; 90 91 static const struct hw_prober_entry hw_prober_platforms[] = { 92 { 93 .compatible = "google,hana", 94 .prober = chromeos_i2c_component_prober, 95 .data = &chromeos_i2c_probe_dumb_touchscreen, 96 }, { 97 .compatible = "google,hana", 98 .prober = chromeos_i2c_component_prober, 99 .data = &chromeos_i2c_probe_hana_trackpad, 100 }, { 101 .compatible = "google,spherion", 102 .prober = chromeos_i2c_component_prober, 103 .data = &chromeos_i2c_probe_hana_trackpad, 104 }, { 105 .compatible = "google,squirtle", 106 .prober = chromeos_i2c_component_prober, 107 .data = &chromeos_i2c_probe_dumb_trackpad, 108 }, { 109 .compatible = "google,squirtle", 110 .prober = chromeos_i2c_component_prober, 111 .data = &chromeos_i2c_probe_squirtle_touchscreen, 112 }, { 113 .compatible = "google,steelix", 114 .prober = chromeos_i2c_component_prober, 115 .data = &chromeos_i2c_probe_dumb_trackpad, 116 }, { 117 .compatible = "google,voltorb", 118 .prober = chromeos_i2c_component_prober, 119 .data = &chromeos_i2c_probe_dumb_trackpad, 120 }, 121 }; 122 123 static int chromeos_of_hw_prober_probe(struct platform_device *pdev) 124 { 125 for (size_t i = 0; i < ARRAY_SIZE(hw_prober_platforms); i++) { 126 int ret; 127 128 if (!of_machine_is_compatible(hw_prober_platforms[i].compatible)) 129 continue; 130 131 ret = hw_prober_platforms[i].prober(&pdev->dev, hw_prober_platforms[i].data); 132 /* Ignore unrecoverable errors and keep going through other probers */ 133 if (ret == -EPROBE_DEFER) 134 return ret; 135 } 136 137 return 0; 138 } 139 140 static struct platform_driver chromeos_of_hw_prober_driver = { 141 .probe = chromeos_of_hw_prober_probe, 142 .driver = { 143 .name = DRV_NAME, 144 }, 145 }; 146 147 static struct platform_device *chromeos_of_hw_prober_pdev; 148 149 static int chromeos_of_hw_prober_driver_init(void) 150 { 151 size_t i; 152 int ret; 153 154 for (i = 0; i < ARRAY_SIZE(hw_prober_platforms); i++) 155 if (of_machine_is_compatible(hw_prober_platforms[i].compatible)) 156 break; 157 if (i == ARRAY_SIZE(hw_prober_platforms)) 158 return -ENODEV; 159 160 ret = platform_driver_register(&chromeos_of_hw_prober_driver); 161 if (ret) 162 return ret; 163 164 chromeos_of_hw_prober_pdev = 165 platform_device_register_simple(DRV_NAME, PLATFORM_DEVID_NONE, NULL, 0); 166 if (IS_ERR(chromeos_of_hw_prober_pdev)) 167 goto err; 168 169 return 0; 170 171 err: 172 platform_driver_unregister(&chromeos_of_hw_prober_driver); 173 174 return PTR_ERR(chromeos_of_hw_prober_pdev); 175 } 176 module_init(chromeos_of_hw_prober_driver_init); 177 178 static void chromeos_of_hw_prober_driver_exit(void) 179 { 180 platform_device_unregister(chromeos_of_hw_prober_pdev); 181 platform_driver_unregister(&chromeos_of_hw_prober_driver); 182 } 183 module_exit(chromeos_of_hw_prober_driver_exit); 184 185 MODULE_LICENSE("GPL"); 186 MODULE_DESCRIPTION("ChromeOS device tree hardware prober"); 187 MODULE_IMPORT_NS("I2C_OF_PROBER"); 188