Merge tag 'soc-drivers-6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc
[linux-block.git] / drivers / platform / x86 / x86-android-tablets / core.c
CommitLineData
62a5f689
HG
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>
4014ae23 15#include <linux/gpio/consumer.h>
62a5f689
HG
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"
812a79b5 24#include "../serdev_helpers.h"
62a5f689 25
8b57d33a
HG
26static struct platform_device *x86_android_tablet_device;
27
eee9cd5d
HG
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 */
4014ae23
HG
43int 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)
62a5f689 46{
4014ae23 47 struct gpiod_lookup_table *lookup;
62a5f689 48 struct gpio_desc *gpiod;
62a5f689 49
4014ae23
HG
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(lookup);
61 gpiod = devm_gpiod_get(&x86_android_tablet_device->dev, con_id, dflags);
62 gpiod_remove_lookup_table(lookup);
63 kfree(lookup);
62a5f689 64
62a5f689 65 if (IS_ERR(gpiod)) {
4014ae23 66 pr_err("error %ld getting GPIO %s %d\n", PTR_ERR(gpiod), chip, pin);
62a5f689
HG
67 return PTR_ERR(gpiod);
68 }
69
4014ae23
HG
70 if (desc)
71 *desc = gpiod;
72
62a5f689
HG
73 return 0;
74}
75
76int 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(data->index);
95 irq = acpi_register_gsi(NULL, data->index, data->trigger, 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 */
4014ae23
HG
102 ret = x86_android_tablet_get_gpiod(data->chip, data->index, data->con_id,
103 false, GPIOD_ASIS, &gpiod);
62a5f689
HG
104 if (ret)
105 return ret;
106
107 irq = gpiod_to_irq(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(data->trigger, data->polarity);
114 if (irq_type != IRQ_TYPE_NONE && irq_type != irq_get_trigger_type(irq))
115 irq_set_irq_type(irq, irq_type);
116
bd8905d7
HG
117 if (data->free_gpio)
118 devm_gpiod_put(&x86_android_tablet_device->dev, gpiod);
119
62a5f689
HG
120 return irq;
121 case X86_ACPI_IRQ_TYPE_PMIC:
122 status = acpi_get_handle(NULL, data->chip, &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, 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(domain, data->index);
142 default:
143 return 0;
144 }
145}
146
147static int i2c_client_count;
70505ea6 148static int spi_dev_count;
62a5f689
HG
149static int pdev_count;
150static int serdev_count;
151static struct i2c_client **i2c_clients;
70505ea6 152static struct spi_device **spi_devs;
62a5f689
HG
153static struct platform_device **pdevs;
154static struct serdev_device **serdevs;
6dc6c0c1 155static struct gpio_keys_button *buttons;
62a5f689
HG
156static struct gpiod_lookup_table * const *gpiod_lookup_tables;
157static const struct software_node *bat_swnode;
158static void (*exit_handler)(void);
159
160static __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(&client_info->irq_data);
170 if (board_info.irq < 0)
171 return board_info.irq;
172
173 status = acpi_get_handle(NULL, client_info->adapter_path, &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, &board_info);
186 put_device(&adap->dev);
187 if (IS_ERR(i2c_clients[idx]))
188 return dev_err_probe(&adap->dev, PTR_ERR(i2c_clients[idx]),
189 "creating I2C-client %d\n", idx);
190
191 return 0;
192}
193
70505ea6
HG
194static __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(&spi_dev_info->irq_data);
204 if (board_info.irq < 0)
205 return board_info.irq;
206
207 status = acpi_get_handle(NULL, spi_dev_info->ctrl_path, &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(&controller->dev);
35ddd61c
DC
227 if (!spi_devs[idx])
228 return dev_err_probe(&controller->dev, -ENOMEM,
70505ea6
HG
229 "creating SPI-device %d\n", idx);
230
231 return 0;
232}
233
62a5f689
HG
234static __init int x86_instantiate_serdev(const struct x86_serdev_info *info, int idx)
235{
812a79b5 236 struct acpi_device *serdev_adev;
62a5f689
HG
237 struct serdev_device *serdev;
238 struct device *ctrl_dev;
239 int ret = -ENODEV;
240
812a79b5
HG
241 ctrl_dev = get_serdev_controller(info->ctrl_hid, info->ctrl_uid, 0,
242 info->ctrl_devname);
243 if (IS_ERR(ctrl_dev))
244 return PTR_ERR(ctrl_dev);
62a5f689
HG
245
246 serdev_adev = acpi_dev_get_first_match_dev(info->serdev_hid, NULL, -1);
247 if (!serdev_adev) {
248 pr_err("error could not get %s serdev adev\n", info->serdev_hid);
812a79b5 249 goto put_ctrl_dev;
62a5f689
HG
250 }
251
252 serdev = serdev_device_alloc(to_serdev_controller(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(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
270put_serdev_adev:
271 acpi_dev_put(serdev_adev);
812a79b5
HG
272put_ctrl_dev:
273 put_device(ctrl_dev);
62a5f689
HG
274 return ret;
275}
276
8b57d33a 277static void x86_android_tablet_remove(struct platform_device *pdev)
62a5f689
HG
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(serdevs);
287
288 for (i = 0; i < pdev_count; i++)
289 platform_device_unregister(pdevs[i]);
290
291 kfree(pdevs);
6dc6c0c1 292 kfree(buttons);
62a5f689 293
70505ea6
HG
294 for (i = 0; i < spi_dev_count; i++)
295 spi_unregister_device(spi_devs[i]);
296
297 kfree(spi_devs);
298
62a5f689
HG
299 for (i = 0; i < i2c_client_count; i++)
300 i2c_unregister_device(i2c_clients[i]);
301
302 kfree(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(gpiod_lookup_tables[i]);
309
310 software_node_unregister(bat_swnode);
311}
312
8b57d33a 313static __init int x86_android_tablet_probe(struct platform_device *pdev)
62a5f689
HG
314{
315 const struct x86_dev_info *dev_info;
316 const struct dmi_system_id *id;
62a5f689
HG
317 int i, ret = 0;
318
319 id = dmi_first_match(x86_android_tablet_ids);
320 if (!id)
321 return -ENODEV;
322
323 dev_info = id->driver_data;
8b57d33a
HG
324 /* Allow x86_android_tablet_device use before probe() exits */
325 x86_android_tablet_device = pdev;
62a5f689 326
62a5f689
HG
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(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(gpiod_lookup_tables[i]);
344
345 if (dev_info->init) {
346 ret = dev_info->init();
347 if (ret < 0) {
8b57d33a 348 x86_android_tablet_remove(pdev);
62a5f689
HG
349 return ret;
350 }
351 exit_handler = dev_info->exit;
352 }
353
354 i2c_clients = kcalloc(dev_info->i2c_client_count, sizeof(*i2c_clients), GFP_KERNEL);
355 if (!i2c_clients) {
8b57d33a 356 x86_android_tablet_remove(pdev);
62a5f689
HG
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, i);
363 if (ret < 0) {
8b57d33a 364 x86_android_tablet_remove(pdev);
62a5f689
HG
365 return ret;
366 }
367 }
368
70505ea6
HG
369 spi_devs = kcalloc(dev_info->spi_dev_count, 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, i);
378 if (ret < 0) {
379 x86_android_tablet_remove(pdev);
380 return ret;
381 }
382 }
383
e2200d3f
HG
384 /* + 1 to make space for (optional) gpio_keys_button pdev */
385 pdevs = kcalloc(dev_info->pdev_count + 1, sizeof(*pdevs), GFP_KERNEL);
62a5f689 386 if (!pdevs) {
8b57d33a 387 x86_android_tablet_remove(pdev);
62a5f689
HG
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(&dev_info->pdev_info[i]);
394 if (IS_ERR(pdevs[i])) {
8b57d33a 395 x86_android_tablet_remove(pdev);
62a5f689
HG
396 return PTR_ERR(pdevs[i]);
397 }
398 }
399
400 serdevs = kcalloc(dev_info->serdev_count, sizeof(*serdevs), GFP_KERNEL);
401 if (!serdevs) {
8b57d33a 402 x86_android_tablet_remove(pdev);
62a5f689
HG
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(&dev_info->serdev_info[i], i);
409 if (ret < 0) {
8b57d33a 410 x86_android_tablet_remove(pdev);
62a5f689
HG
411 return ret;
412 }
413 }
414
6dc6c0c1
HG
415 if (dev_info->gpio_button_count) {
416 struct gpio_keys_platform_data pdata = { };
e2200d3f
HG
417 struct gpio_desc *gpiod;
418
6dc6c0c1
HG
419 buttons = kcalloc(dev_info->gpio_button_count, sizeof(*buttons), GFP_KERNEL);
420 if (!buttons) {
8b57d33a 421 x86_android_tablet_remove(pdev);
6dc6c0c1
HG
422 return -ENOMEM;
423 }
424
425 for (i = 0; i < dev_info->gpio_button_count; i++) {
426 ret = x86_android_tablet_get_gpiod(dev_info->gpio_button[i].chip,
4014ae23
HG
427 dev_info->gpio_button[i].pin,
428 dev_info->gpio_button[i].button.desc,
429 false, GPIOD_IN, &gpiod);
6dc6c0c1 430 if (ret < 0) {
8b57d33a 431 x86_android_tablet_remove(pdev);
6dc6c0c1
HG
432 return ret;
433 }
434
435 buttons[i] = dev_info->gpio_button[i].button;
436 buttons[i].gpio = desc_to_gpio(gpiod);
4014ae23
HG
437 /* Release gpiod so that gpio-keys can request it */
438 devm_gpiod_put(&x86_android_tablet_device->dev, gpiod);
e2200d3f
HG
439 }
440
6dc6c0c1
HG
441 pdata.buttons = buttons;
442 pdata.nbuttons = dev_info->gpio_button_count;
e2200d3f 443
61226c1c 444 pdevs[pdev_count] = platform_device_register_data(&pdev->dev, "gpio-keys",
e2200d3f
HG
445 PLATFORM_DEVID_AUTO,
446 &pdata, sizeof(pdata));
447 if (IS_ERR(pdevs[pdev_count])) {
8b57d33a 448 x86_android_tablet_remove(pdev);
e2200d3f
HG
449 return PTR_ERR(pdevs[pdev_count]);
450 }
451 pdev_count++;
452 }
453
62a5f689
HG
454 return 0;
455}
456
8b57d33a
HG
457static struct platform_driver x86_android_tablet_driver = {
458 .driver = {
459 .name = KBUILD_MODNAME,
460 },
461 .remove_new = x86_android_tablet_remove,
462};
463
464static 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(x86_android_tablet_device);
471}
62a5f689 472module_init(x86_android_tablet_init);
8b57d33a
HG
473
474static 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}
479module_exit(x86_android_tablet_exit);
62a5f689
HG
480
481MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
482MODULE_DESCRIPTION("X86 Android tablets DSDT fixups driver");
483MODULE_LICENSE("GPL");