1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * DMI based code to deal with broken DSDTs on X86 tablets which ship with |
4 | * Android as (part of) the factory image. The factory kernels shipped on these |
5 | * devices typically have a bunch of things hardcoded, rather than specified |
6 | * in their DSDT. |
7 | * |
8 | * Copyright (C) 2021-2023 Hans de Goede <hdegoede@redhat.com> |
9 | */ |
10 | |
11 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
12 | |
13 | #include <linux/acpi.h> |
14 | #include <linux/dmi.h> |
15 | #include <linux/gpio/consumer.h> |
16 | #include <linux/gpio/machine.h> |
17 | #include <linux/irq.h> |
18 | #include <linux/module.h> |
19 | #include <linux/platform_device.h> |
20 | #include <linux/serdev.h> |
21 | #include <linux/string.h> |
22 | |
23 | #include "x86-android-tablets.h" |
24 | #include "../serdev_helpers.h" |
25 | |
26 | static struct platform_device *x86_android_tablet_device; |
27 | |
28 | /* |
29 | * This helper allows getting a gpio_desc *before* the actual device consuming |
30 | * the GPIO has been instantiated. This function _must_ only be used to handle |
31 | * this special case such as e.g. : |
32 | * |
33 | * 1. Getting an IRQ from a GPIO for i2c_board_info.irq which is passed to |
34 | * i2c_client_new() to instantiate i2c_client-s; or |
35 | * 2. Calling desc_to_gpio() to get an old style GPIO number for gpio_keys |
36 | * platform_data which still uses old style GPIO numbers. |
37 | * |
38 | * Since the consuming device has not been instatiated yet a dynamic lookup |
39 | * is generated using the special x86_android_tablet dev for dev_id. |
40 | * |
41 | * For normal GPIO lookups a standard static gpiod_lookup_table _must_ be used. |
42 | */ |
43 | int x86_android_tablet_get_gpiod(const char *chip, int pin, const char *con_id, |
44 | bool active_low, enum gpiod_flags dflags, |
45 | struct gpio_desc **desc) |
46 | { |
47 | struct gpiod_lookup_table *lookup; |
48 | struct gpio_desc *gpiod; |
49 | |
50 | lookup = kzalloc(struct_size(lookup, table, 2), GFP_KERNEL); |
51 | if (!lookup) |
52 | return -ENOMEM; |
53 | |
54 | lookup->dev_id = KBUILD_MODNAME; |
55 | lookup->table[0].key = chip; |
56 | lookup->table[0].chip_hwnum = pin; |
57 | lookup->table[0].con_id = con_id; |
58 | lookup->table[0].flags = active_low ? GPIO_ACTIVE_LOW : GPIO_ACTIVE_HIGH; |
59 | |
60 | gpiod_add_lookup_table(table: lookup); |
61 | gpiod = devm_gpiod_get(dev: &x86_android_tablet_device->dev, con_id, flags: dflags); |
62 | gpiod_remove_lookup_table(table: lookup); |
63 | kfree(objp: lookup); |
64 | |
65 | if (IS_ERR(ptr: gpiod)) { |
66 | pr_err("error %ld getting GPIO %s %d\n" , PTR_ERR(gpiod), chip, pin); |
67 | return PTR_ERR(ptr: gpiod); |
68 | } |
69 | |
70 | if (desc) |
71 | *desc = gpiod; |
72 | |
73 | return 0; |
74 | } |
75 | |
76 | int x86_acpi_irq_helper_get(const struct x86_acpi_irq_data *data) |
77 | { |
78 | struct irq_fwspec fwspec = { }; |
79 | struct irq_domain *domain; |
80 | struct acpi_device *adev; |
81 | struct gpio_desc *gpiod; |
82 | unsigned int irq_type; |
83 | acpi_handle handle; |
84 | acpi_status status; |
85 | int irq, ret; |
86 | |
87 | switch (data->type) { |
88 | case X86_ACPI_IRQ_TYPE_APIC: |
89 | /* |
90 | * The DSDT may already reference the GSI in a device skipped by |
91 | * acpi_quirk_skip_i2c_client_enumeration(). Unregister the GSI |
92 | * to avoid EBUSY errors in this case. |
93 | */ |
94 | acpi_unregister_gsi(gsi: data->index); |
95 | irq = acpi_register_gsi(NULL, gsi: data->index, triggering: data->trigger, polarity: data->polarity); |
96 | if (irq < 0) |
97 | pr_err("error %d getting APIC IRQ %d\n" , irq, data->index); |
98 | |
99 | return irq; |
100 | case X86_ACPI_IRQ_TYPE_GPIOINT: |
101 | /* Like acpi_dev_gpio_irq_get(), but without parsing ACPI resources */ |
102 | ret = x86_android_tablet_get_gpiod(chip: data->chip, pin: data->index, con_id: data->con_id, |
103 | active_low: false, dflags: GPIOD_ASIS, desc: &gpiod); |
104 | if (ret) |
105 | return ret; |
106 | |
107 | irq = gpiod_to_irq(desc: gpiod); |
108 | if (irq < 0) { |
109 | pr_err("error %d getting IRQ %s %d\n" , irq, data->chip, data->index); |
110 | return irq; |
111 | } |
112 | |
113 | irq_type = acpi_dev_get_irq_type(triggering: data->trigger, polarity: data->polarity); |
114 | if (irq_type != IRQ_TYPE_NONE && irq_type != irq_get_trigger_type(irq)) |
115 | irq_set_irq_type(irq, type: irq_type); |
116 | |
117 | if (data->free_gpio) |
118 | devm_gpiod_put(dev: &x86_android_tablet_device->dev, desc: gpiod); |
119 | |
120 | return irq; |
121 | case X86_ACPI_IRQ_TYPE_PMIC: |
122 | status = acpi_get_handle(NULL, pathname: data->chip, ret_handle: &handle); |
123 | if (ACPI_FAILURE(status)) { |
124 | pr_err("error could not get %s handle\n" , data->chip); |
125 | return -ENODEV; |
126 | } |
127 | |
128 | adev = acpi_fetch_acpi_dev(handle); |
129 | if (!adev) { |
130 | pr_err("error could not get %s adev\n" , data->chip); |
131 | return -ENODEV; |
132 | } |
133 | |
134 | fwspec.fwnode = acpi_fwnode_handle(adev); |
135 | domain = irq_find_matching_fwspec(fwspec: &fwspec, bus_token: data->domain); |
136 | if (!domain) { |
137 | pr_err("error could not find IRQ domain for %s\n" , data->chip); |
138 | return -ENODEV; |
139 | } |
140 | |
141 | return irq_create_mapping(host: domain, hwirq: data->index); |
142 | default: |
143 | return 0; |
144 | } |
145 | } |
146 | |
147 | static int i2c_client_count; |
148 | static int spi_dev_count; |
149 | static int pdev_count; |
150 | static int serdev_count; |
151 | static struct i2c_client **i2c_clients; |
152 | static struct spi_device **spi_devs; |
153 | static struct platform_device **pdevs; |
154 | static struct serdev_device **serdevs; |
155 | static struct gpio_keys_button *buttons; |
156 | static struct gpiod_lookup_table * const *gpiod_lookup_tables; |
157 | static const struct software_node *bat_swnode; |
158 | static void (*exit_handler)(void); |
159 | |
160 | static __init int x86_instantiate_i2c_client(const struct x86_dev_info *dev_info, |
161 | int idx) |
162 | { |
163 | const struct x86_i2c_client_info *client_info = &dev_info->i2c_client_info[idx]; |
164 | struct i2c_board_info board_info = client_info->board_info; |
165 | struct i2c_adapter *adap; |
166 | acpi_handle handle; |
167 | acpi_status status; |
168 | |
169 | board_info.irq = x86_acpi_irq_helper_get(data: &client_info->irq_data); |
170 | if (board_info.irq < 0) |
171 | return board_info.irq; |
172 | |
173 | status = acpi_get_handle(NULL, pathname: client_info->adapter_path, ret_handle: &handle); |
174 | if (ACPI_FAILURE(status)) { |
175 | pr_err("Error could not get %s handle\n" , client_info->adapter_path); |
176 | return -ENODEV; |
177 | } |
178 | |
179 | adap = i2c_acpi_find_adapter_by_handle(handle); |
180 | if (!adap) { |
181 | pr_err("error could not get %s adapter\n" , client_info->adapter_path); |
182 | return -ENODEV; |
183 | } |
184 | |
185 | i2c_clients[idx] = i2c_new_client_device(adap, info: &board_info); |
186 | put_device(dev: &adap->dev); |
187 | if (IS_ERR(ptr: i2c_clients[idx])) |
188 | return dev_err_probe(dev: &adap->dev, err: PTR_ERR(ptr: i2c_clients[idx]), |
189 | fmt: "creating I2C-client %d\n" , idx); |
190 | |
191 | return 0; |
192 | } |
193 | |
194 | static __init int x86_instantiate_spi_dev(const struct x86_dev_info *dev_info, int idx) |
195 | { |
196 | const struct x86_spi_dev_info *spi_dev_info = &dev_info->spi_dev_info[idx]; |
197 | struct spi_board_info board_info = spi_dev_info->board_info; |
198 | struct spi_controller *controller; |
199 | struct acpi_device *adev; |
200 | acpi_handle handle; |
201 | acpi_status status; |
202 | |
203 | board_info.irq = x86_acpi_irq_helper_get(data: &spi_dev_info->irq_data); |
204 | if (board_info.irq < 0) |
205 | return board_info.irq; |
206 | |
207 | status = acpi_get_handle(NULL, pathname: spi_dev_info->ctrl_path, ret_handle: &handle); |
208 | if (ACPI_FAILURE(status)) { |
209 | pr_err("Error could not get %s handle\n" , spi_dev_info->ctrl_path); |
210 | return -ENODEV; |
211 | } |
212 | |
213 | adev = acpi_fetch_acpi_dev(handle); |
214 | if (!adev) { |
215 | pr_err("Error could not get adev for %s\n" , spi_dev_info->ctrl_path); |
216 | return -ENODEV; |
217 | } |
218 | |
219 | controller = acpi_spi_find_controller_by_adev(adev); |
220 | if (!controller) { |
221 | pr_err("Error could not get SPI controller for %s\n" , spi_dev_info->ctrl_path); |
222 | return -ENODEV; |
223 | } |
224 | |
225 | spi_devs[idx] = spi_new_device(controller, &board_info); |
226 | put_device(dev: &controller->dev); |
227 | if (!spi_devs[idx]) |
228 | return dev_err_probe(dev: &controller->dev, err: -ENOMEM, |
229 | fmt: "creating SPI-device %d\n" , idx); |
230 | |
231 | return 0; |
232 | } |
233 | |
234 | static __init int x86_instantiate_serdev(const struct x86_serdev_info *info, int idx) |
235 | { |
236 | struct acpi_device *serdev_adev; |
237 | struct serdev_device *serdev; |
238 | struct device *ctrl_dev; |
239 | int ret = -ENODEV; |
240 | |
241 | ctrl_dev = get_serdev_controller(serial_ctrl_hid: info->ctrl_hid, serial_ctrl_uid: info->ctrl_uid, serial_ctrl_port: 0, |
242 | serdev_ctrl_name: info->ctrl_devname); |
243 | if (IS_ERR(ptr: ctrl_dev)) |
244 | return PTR_ERR(ptr: ctrl_dev); |
245 | |
246 | serdev_adev = acpi_dev_get_first_match_dev(hid: info->serdev_hid, NULL, hrv: -1); |
247 | if (!serdev_adev) { |
248 | pr_err("error could not get %s serdev adev\n" , info->serdev_hid); |
249 | goto put_ctrl_dev; |
250 | } |
251 | |
252 | serdev = serdev_device_alloc(to_serdev_controller(d: ctrl_dev)); |
253 | if (!serdev) { |
254 | ret = -ENOMEM; |
255 | goto put_serdev_adev; |
256 | } |
257 | |
258 | ACPI_COMPANION_SET(&serdev->dev, serdev_adev); |
259 | acpi_device_set_enumerated(adev: serdev_adev); |
260 | |
261 | ret = serdev_device_add(serdev); |
262 | if (ret) { |
263 | dev_err(&serdev->dev, "error %d adding serdev\n" , ret); |
264 | serdev_device_put(serdev); |
265 | goto put_serdev_adev; |
266 | } |
267 | |
268 | serdevs[idx] = serdev; |
269 | |
270 | put_serdev_adev: |
271 | acpi_dev_put(adev: serdev_adev); |
272 | put_ctrl_dev: |
273 | put_device(dev: ctrl_dev); |
274 | return ret; |
275 | } |
276 | |
277 | static void x86_android_tablet_remove(struct platform_device *pdev) |
278 | { |
279 | int i; |
280 | |
281 | for (i = 0; i < serdev_count; i++) { |
282 | if (serdevs[i]) |
283 | serdev_device_remove(serdevs[i]); |
284 | } |
285 | |
286 | kfree(objp: serdevs); |
287 | |
288 | for (i = 0; i < pdev_count; i++) |
289 | platform_device_unregister(pdevs[i]); |
290 | |
291 | kfree(objp: pdevs); |
292 | kfree(objp: buttons); |
293 | |
294 | for (i = 0; i < spi_dev_count; i++) |
295 | spi_unregister_device(spi: spi_devs[i]); |
296 | |
297 | kfree(objp: spi_devs); |
298 | |
299 | for (i = 0; i < i2c_client_count; i++) |
300 | i2c_unregister_device(client: i2c_clients[i]); |
301 | |
302 | kfree(objp: i2c_clients); |
303 | |
304 | if (exit_handler) |
305 | exit_handler(); |
306 | |
307 | for (i = 0; gpiod_lookup_tables && gpiod_lookup_tables[i]; i++) |
308 | gpiod_remove_lookup_table(table: gpiod_lookup_tables[i]); |
309 | |
310 | software_node_unregister(node: bat_swnode); |
311 | } |
312 | |
313 | static __init int x86_android_tablet_probe(struct platform_device *pdev) |
314 | { |
315 | const struct x86_dev_info *dev_info; |
316 | const struct dmi_system_id *id; |
317 | int i, ret = 0; |
318 | |
319 | id = dmi_first_match(list: x86_android_tablet_ids); |
320 | if (!id) |
321 | return -ENODEV; |
322 | |
323 | dev_info = id->driver_data; |
324 | /* Allow x86_android_tablet_device use before probe() exits */ |
325 | x86_android_tablet_device = pdev; |
326 | |
327 | /* |
328 | * Since this runs from module_init() it cannot use -EPROBE_DEFER, |
329 | * instead pre-load any modules which are listed as requirements. |
330 | */ |
331 | for (i = 0; dev_info->modules && dev_info->modules[i]; i++) |
332 | request_module(dev_info->modules[i]); |
333 | |
334 | bat_swnode = dev_info->bat_swnode; |
335 | if (bat_swnode) { |
336 | ret = software_node_register(node: bat_swnode); |
337 | if (ret) |
338 | return ret; |
339 | } |
340 | |
341 | gpiod_lookup_tables = dev_info->gpiod_lookup_tables; |
342 | for (i = 0; gpiod_lookup_tables && gpiod_lookup_tables[i]; i++) |
343 | gpiod_add_lookup_table(table: gpiod_lookup_tables[i]); |
344 | |
345 | if (dev_info->init) { |
346 | ret = dev_info->init(); |
347 | if (ret < 0) { |
348 | x86_android_tablet_remove(pdev); |
349 | return ret; |
350 | } |
351 | exit_handler = dev_info->exit; |
352 | } |
353 | |
354 | i2c_clients = kcalloc(n: dev_info->i2c_client_count, size: sizeof(*i2c_clients), GFP_KERNEL); |
355 | if (!i2c_clients) { |
356 | x86_android_tablet_remove(pdev); |
357 | return -ENOMEM; |
358 | } |
359 | |
360 | i2c_client_count = dev_info->i2c_client_count; |
361 | for (i = 0; i < i2c_client_count; i++) { |
362 | ret = x86_instantiate_i2c_client(dev_info, idx: i); |
363 | if (ret < 0) { |
364 | x86_android_tablet_remove(pdev); |
365 | return ret; |
366 | } |
367 | } |
368 | |
369 | spi_devs = kcalloc(n: dev_info->spi_dev_count, size: sizeof(*spi_devs), GFP_KERNEL); |
370 | if (!spi_devs) { |
371 | x86_android_tablet_remove(pdev); |
372 | return -ENOMEM; |
373 | } |
374 | |
375 | spi_dev_count = dev_info->spi_dev_count; |
376 | for (i = 0; i < spi_dev_count; i++) { |
377 | ret = x86_instantiate_spi_dev(dev_info, idx: i); |
378 | if (ret < 0) { |
379 | x86_android_tablet_remove(pdev); |
380 | return ret; |
381 | } |
382 | } |
383 | |
384 | /* + 1 to make space for (optional) gpio_keys_button pdev */ |
385 | pdevs = kcalloc(n: dev_info->pdev_count + 1, size: sizeof(*pdevs), GFP_KERNEL); |
386 | if (!pdevs) { |
387 | x86_android_tablet_remove(pdev); |
388 | return -ENOMEM; |
389 | } |
390 | |
391 | pdev_count = dev_info->pdev_count; |
392 | for (i = 0; i < pdev_count; i++) { |
393 | pdevs[i] = platform_device_register_full(pdevinfo: &dev_info->pdev_info[i]); |
394 | if (IS_ERR(ptr: pdevs[i])) { |
395 | x86_android_tablet_remove(pdev); |
396 | return PTR_ERR(ptr: pdevs[i]); |
397 | } |
398 | } |
399 | |
400 | serdevs = kcalloc(n: dev_info->serdev_count, size: sizeof(*serdevs), GFP_KERNEL); |
401 | if (!serdevs) { |
402 | x86_android_tablet_remove(pdev); |
403 | return -ENOMEM; |
404 | } |
405 | |
406 | serdev_count = dev_info->serdev_count; |
407 | for (i = 0; i < serdev_count; i++) { |
408 | ret = x86_instantiate_serdev(info: &dev_info->serdev_info[i], idx: i); |
409 | if (ret < 0) { |
410 | x86_android_tablet_remove(pdev); |
411 | return ret; |
412 | } |
413 | } |
414 | |
415 | if (dev_info->gpio_button_count) { |
416 | struct gpio_keys_platform_data pdata = { }; |
417 | struct gpio_desc *gpiod; |
418 | |
419 | buttons = kcalloc(n: dev_info->gpio_button_count, size: sizeof(*buttons), GFP_KERNEL); |
420 | if (!buttons) { |
421 | x86_android_tablet_remove(pdev); |
422 | return -ENOMEM; |
423 | } |
424 | |
425 | for (i = 0; i < dev_info->gpio_button_count; i++) { |
426 | ret = x86_android_tablet_get_gpiod(chip: dev_info->gpio_button[i].chip, |
427 | pin: dev_info->gpio_button[i].pin, |
428 | con_id: dev_info->gpio_button[i].button.desc, |
429 | active_low: false, dflags: GPIOD_IN, desc: &gpiod); |
430 | if (ret < 0) { |
431 | x86_android_tablet_remove(pdev); |
432 | return ret; |
433 | } |
434 | |
435 | buttons[i] = dev_info->gpio_button[i].button; |
436 | buttons[i].gpio = desc_to_gpio(desc: gpiod); |
437 | /* Release gpiod so that gpio-keys can request it */ |
438 | devm_gpiod_put(dev: &x86_android_tablet_device->dev, desc: gpiod); |
439 | } |
440 | |
441 | pdata.buttons = buttons; |
442 | pdata.nbuttons = dev_info->gpio_button_count; |
443 | |
444 | pdevs[pdev_count] = platform_device_register_data(parent: &pdev->dev, name: "gpio-keys" , |
445 | PLATFORM_DEVID_AUTO, |
446 | data: &pdata, size: sizeof(pdata)); |
447 | if (IS_ERR(ptr: pdevs[pdev_count])) { |
448 | x86_android_tablet_remove(pdev); |
449 | return PTR_ERR(ptr: pdevs[pdev_count]); |
450 | } |
451 | pdev_count++; |
452 | } |
453 | |
454 | return 0; |
455 | } |
456 | |
457 | static struct platform_driver x86_android_tablet_driver = { |
458 | .driver = { |
459 | .name = KBUILD_MODNAME, |
460 | }, |
461 | .remove_new = x86_android_tablet_remove, |
462 | }; |
463 | |
464 | static int __init x86_android_tablet_init(void) |
465 | { |
466 | x86_android_tablet_device = platform_create_bundle(&x86_android_tablet_driver, |
467 | x86_android_tablet_probe, |
468 | NULL, 0, NULL, 0); |
469 | |
470 | return PTR_ERR_OR_ZERO(ptr: x86_android_tablet_device); |
471 | } |
472 | module_init(x86_android_tablet_init); |
473 | |
474 | static void __exit x86_android_tablet_exit(void) |
475 | { |
476 | platform_device_unregister(x86_android_tablet_device); |
477 | platform_driver_unregister(&x86_android_tablet_driver); |
478 | } |
479 | module_exit(x86_android_tablet_exit); |
480 | |
481 | MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>" ); |
482 | MODULE_DESCRIPTION("X86 Android tablets DSDT fixups driver" ); |
483 | MODULE_LICENSE("GPL" ); |
484 | |