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 25 static struct platform_device *x86_android_tablet_device; 26 27 int x86_android_tablet_get_gpiod(const char *chip, int pin, const char *con_id, 28 bool active_low, enum gpiod_flags dflags, 29 struct gpio_desc **desc) 30 { 31 struct gpiod_lookup_table *lookup; 32 struct gpio_desc *gpiod; 33 34 lookup = kzalloc(struct_size(lookup, table, 2), GFP_KERNEL); 35 if (!lookup) 36 return -ENOMEM; 37 38 lookup->dev_id = KBUILD_MODNAME; 39 lookup->table[0].key = chip; 40 lookup->table[0].chip_hwnum = pin; 41 lookup->table[0].con_id = con_id; 42 lookup->table[0].flags = active_low ? GPIO_ACTIVE_LOW : GPIO_ACTIVE_HIGH; 43 44 gpiod_add_lookup_table(lookup); 45 gpiod = devm_gpiod_get(&x86_android_tablet_device->dev, con_id, dflags); 46 gpiod_remove_lookup_table(lookup); 47 kfree(lookup); 48 49 if (IS_ERR(gpiod)) { 50 pr_err("error %ld getting GPIO %s %d\n", PTR_ERR(gpiod), chip, pin); 51 return PTR_ERR(gpiod); 52 } 53 54 if (desc) 55 *desc = gpiod; 56 57 return 0; 58 } 59 60 int x86_acpi_irq_helper_get(const struct x86_acpi_irq_data *data) 61 { 62 struct irq_fwspec fwspec = { }; 63 struct irq_domain *domain; 64 struct acpi_device *adev; 65 struct gpio_desc *gpiod; 66 unsigned int irq_type; 67 acpi_handle handle; 68 acpi_status status; 69 int irq, ret; 70 71 switch (data->type) { 72 case X86_ACPI_IRQ_TYPE_APIC: 73 /* 74 * The DSDT may already reference the GSI in a device skipped by 75 * acpi_quirk_skip_i2c_client_enumeration(). Unregister the GSI 76 * to avoid EBUSY errors in this case. 77 */ 78 acpi_unregister_gsi(data->index); 79 irq = acpi_register_gsi(NULL, data->index, data->trigger, data->polarity); 80 if (irq < 0) 81 pr_err("error %d getting APIC IRQ %d\n", irq, data->index); 82 83 return irq; 84 case X86_ACPI_IRQ_TYPE_GPIOINT: 85 /* Like acpi_dev_gpio_irq_get(), but without parsing ACPI resources */ 86 ret = x86_android_tablet_get_gpiod(data->chip, data->index, data->con_id, 87 false, GPIOD_ASIS, &gpiod); 88 if (ret) 89 return ret; 90 91 irq = gpiod_to_irq(gpiod); 92 if (irq < 0) { 93 pr_err("error %d getting IRQ %s %d\n", irq, data->chip, data->index); 94 return irq; 95 } 96 97 irq_type = acpi_dev_get_irq_type(data->trigger, data->polarity); 98 if (irq_type != IRQ_TYPE_NONE && irq_type != irq_get_trigger_type(irq)) 99 irq_set_irq_type(irq, irq_type); 100 101 return irq; 102 case X86_ACPI_IRQ_TYPE_PMIC: 103 status = acpi_get_handle(NULL, data->chip, &handle); 104 if (ACPI_FAILURE(status)) { 105 pr_err("error could not get %s handle\n", data->chip); 106 return -ENODEV; 107 } 108 109 adev = acpi_fetch_acpi_dev(handle); 110 if (!adev) { 111 pr_err("error could not get %s adev\n", data->chip); 112 return -ENODEV; 113 } 114 115 fwspec.fwnode = acpi_fwnode_handle(adev); 116 domain = irq_find_matching_fwspec(&fwspec, data->domain); 117 if (!domain) { 118 pr_err("error could not find IRQ domain for %s\n", data->chip); 119 return -ENODEV; 120 } 121 122 return irq_create_mapping(domain, data->index); 123 default: 124 return 0; 125 } 126 } 127 128 static int i2c_client_count; 129 static int pdev_count; 130 static int serdev_count; 131 static struct i2c_client **i2c_clients; 132 static struct platform_device **pdevs; 133 static struct serdev_device **serdevs; 134 static struct gpio_keys_button *buttons; 135 static struct gpiod_lookup_table * const *gpiod_lookup_tables; 136 static const struct software_node *bat_swnode; 137 static void (*exit_handler)(void); 138 139 static __init int x86_instantiate_i2c_client(const struct x86_dev_info *dev_info, 140 int idx) 141 { 142 const struct x86_i2c_client_info *client_info = &dev_info->i2c_client_info[idx]; 143 struct i2c_board_info board_info = client_info->board_info; 144 struct i2c_adapter *adap; 145 acpi_handle handle; 146 acpi_status status; 147 148 board_info.irq = x86_acpi_irq_helper_get(&client_info->irq_data); 149 if (board_info.irq < 0) 150 return board_info.irq; 151 152 status = acpi_get_handle(NULL, client_info->adapter_path, &handle); 153 if (ACPI_FAILURE(status)) { 154 pr_err("Error could not get %s handle\n", client_info->adapter_path); 155 return -ENODEV; 156 } 157 158 adap = i2c_acpi_find_adapter_by_handle(handle); 159 if (!adap) { 160 pr_err("error could not get %s adapter\n", client_info->adapter_path); 161 return -ENODEV; 162 } 163 164 i2c_clients[idx] = i2c_new_client_device(adap, &board_info); 165 put_device(&adap->dev); 166 if (IS_ERR(i2c_clients[idx])) 167 return dev_err_probe(&adap->dev, PTR_ERR(i2c_clients[idx]), 168 "creating I2C-client %d\n", idx); 169 170 return 0; 171 } 172 173 static __init int x86_instantiate_serdev(const struct x86_serdev_info *info, int idx) 174 { 175 struct acpi_device *ctrl_adev, *serdev_adev; 176 struct serdev_device *serdev; 177 struct device *ctrl_dev; 178 int ret = -ENODEV; 179 180 ctrl_adev = acpi_dev_get_first_match_dev(info->ctrl_hid, info->ctrl_uid, -1); 181 if (!ctrl_adev) { 182 pr_err("error could not get %s/%s ctrl adev\n", 183 info->ctrl_hid, info->ctrl_uid); 184 return -ENODEV; 185 } 186 187 serdev_adev = acpi_dev_get_first_match_dev(info->serdev_hid, NULL, -1); 188 if (!serdev_adev) { 189 pr_err("error could not get %s serdev adev\n", info->serdev_hid); 190 goto put_ctrl_adev; 191 } 192 193 /* get_first_physical_node() returns a weak ref, no need to put() it */ 194 ctrl_dev = acpi_get_first_physical_node(ctrl_adev); 195 if (!ctrl_dev) { 196 pr_err("error could not get %s/%s ctrl physical dev\n", 197 info->ctrl_hid, info->ctrl_uid); 198 goto put_serdev_adev; 199 } 200 201 /* ctrl_dev now points to the controller's parent, get the controller */ 202 ctrl_dev = device_find_child_by_name(ctrl_dev, info->ctrl_devname); 203 if (!ctrl_dev) { 204 pr_err("error could not get %s/%s %s ctrl dev\n", 205 info->ctrl_hid, info->ctrl_uid, info->ctrl_devname); 206 goto put_serdev_adev; 207 } 208 209 serdev = serdev_device_alloc(to_serdev_controller(ctrl_dev)); 210 if (!serdev) { 211 ret = -ENOMEM; 212 goto put_serdev_adev; 213 } 214 215 ACPI_COMPANION_SET(&serdev->dev, serdev_adev); 216 acpi_device_set_enumerated(serdev_adev); 217 218 ret = serdev_device_add(serdev); 219 if (ret) { 220 dev_err(&serdev->dev, "error %d adding serdev\n", ret); 221 serdev_device_put(serdev); 222 goto put_serdev_adev; 223 } 224 225 serdevs[idx] = serdev; 226 227 put_serdev_adev: 228 acpi_dev_put(serdev_adev); 229 put_ctrl_adev: 230 acpi_dev_put(ctrl_adev); 231 return ret; 232 } 233 234 static void x86_android_tablet_remove(struct platform_device *pdev) 235 { 236 int i; 237 238 for (i = 0; i < serdev_count; i++) { 239 if (serdevs[i]) 240 serdev_device_remove(serdevs[i]); 241 } 242 243 kfree(serdevs); 244 245 for (i = 0; i < pdev_count; i++) 246 platform_device_unregister(pdevs[i]); 247 248 kfree(pdevs); 249 kfree(buttons); 250 251 for (i = 0; i < i2c_client_count; i++) 252 i2c_unregister_device(i2c_clients[i]); 253 254 kfree(i2c_clients); 255 256 if (exit_handler) 257 exit_handler(); 258 259 for (i = 0; gpiod_lookup_tables && gpiod_lookup_tables[i]; i++) 260 gpiod_remove_lookup_table(gpiod_lookup_tables[i]); 261 262 software_node_unregister(bat_swnode); 263 } 264 265 static __init int x86_android_tablet_probe(struct platform_device *pdev) 266 { 267 const struct x86_dev_info *dev_info; 268 const struct dmi_system_id *id; 269 int i, ret = 0; 270 271 id = dmi_first_match(x86_android_tablet_ids); 272 if (!id) 273 return -ENODEV; 274 275 dev_info = id->driver_data; 276 /* Allow x86_android_tablet_device use before probe() exits */ 277 x86_android_tablet_device = pdev; 278 279 /* 280 * Since this runs from module_init() it cannot use -EPROBE_DEFER, 281 * instead pre-load any modules which are listed as requirements. 282 */ 283 for (i = 0; dev_info->modules && dev_info->modules[i]; i++) 284 request_module(dev_info->modules[i]); 285 286 bat_swnode = dev_info->bat_swnode; 287 if (bat_swnode) { 288 ret = software_node_register(bat_swnode); 289 if (ret) 290 return ret; 291 } 292 293 gpiod_lookup_tables = dev_info->gpiod_lookup_tables; 294 for (i = 0; gpiod_lookup_tables && gpiod_lookup_tables[i]; i++) 295 gpiod_add_lookup_table(gpiod_lookup_tables[i]); 296 297 if (dev_info->init) { 298 ret = dev_info->init(); 299 if (ret < 0) { 300 x86_android_tablet_remove(pdev); 301 return ret; 302 } 303 exit_handler = dev_info->exit; 304 } 305 306 i2c_clients = kcalloc(dev_info->i2c_client_count, sizeof(*i2c_clients), GFP_KERNEL); 307 if (!i2c_clients) { 308 x86_android_tablet_remove(pdev); 309 return -ENOMEM; 310 } 311 312 i2c_client_count = dev_info->i2c_client_count; 313 for (i = 0; i < i2c_client_count; i++) { 314 ret = x86_instantiate_i2c_client(dev_info, i); 315 if (ret < 0) { 316 x86_android_tablet_remove(pdev); 317 return ret; 318 } 319 } 320 321 /* + 1 to make space for (optional) gpio_keys_button pdev */ 322 pdevs = kcalloc(dev_info->pdev_count + 1, sizeof(*pdevs), GFP_KERNEL); 323 if (!pdevs) { 324 x86_android_tablet_remove(pdev); 325 return -ENOMEM; 326 } 327 328 pdev_count = dev_info->pdev_count; 329 for (i = 0; i < pdev_count; i++) { 330 pdevs[i] = platform_device_register_full(&dev_info->pdev_info[i]); 331 if (IS_ERR(pdevs[i])) { 332 x86_android_tablet_remove(pdev); 333 return PTR_ERR(pdevs[i]); 334 } 335 } 336 337 serdevs = kcalloc(dev_info->serdev_count, sizeof(*serdevs), GFP_KERNEL); 338 if (!serdevs) { 339 x86_android_tablet_remove(pdev); 340 return -ENOMEM; 341 } 342 343 serdev_count = dev_info->serdev_count; 344 for (i = 0; i < serdev_count; i++) { 345 ret = x86_instantiate_serdev(&dev_info->serdev_info[i], i); 346 if (ret < 0) { 347 x86_android_tablet_remove(pdev); 348 return ret; 349 } 350 } 351 352 if (dev_info->gpio_button_count) { 353 struct gpio_keys_platform_data pdata = { }; 354 struct gpio_desc *gpiod; 355 356 buttons = kcalloc(dev_info->gpio_button_count, sizeof(*buttons), GFP_KERNEL); 357 if (!buttons) { 358 x86_android_tablet_remove(pdev); 359 return -ENOMEM; 360 } 361 362 for (i = 0; i < dev_info->gpio_button_count; i++) { 363 ret = x86_android_tablet_get_gpiod(dev_info->gpio_button[i].chip, 364 dev_info->gpio_button[i].pin, 365 dev_info->gpio_button[i].button.desc, 366 false, GPIOD_IN, &gpiod); 367 if (ret < 0) { 368 x86_android_tablet_remove(pdev); 369 return ret; 370 } 371 372 buttons[i] = dev_info->gpio_button[i].button; 373 buttons[i].gpio = desc_to_gpio(gpiod); 374 /* Release gpiod so that gpio-keys can request it */ 375 devm_gpiod_put(&x86_android_tablet_device->dev, gpiod); 376 } 377 378 pdata.buttons = buttons; 379 pdata.nbuttons = dev_info->gpio_button_count; 380 381 pdevs[pdev_count] = platform_device_register_data(&pdev->dev, "gpio-keys", 382 PLATFORM_DEVID_AUTO, 383 &pdata, sizeof(pdata)); 384 if (IS_ERR(pdevs[pdev_count])) { 385 x86_android_tablet_remove(pdev); 386 return PTR_ERR(pdevs[pdev_count]); 387 } 388 pdev_count++; 389 } 390 391 return 0; 392 } 393 394 static struct platform_driver x86_android_tablet_driver = { 395 .driver = { 396 .name = KBUILD_MODNAME, 397 }, 398 .remove_new = x86_android_tablet_remove, 399 }; 400 401 static int __init x86_android_tablet_init(void) 402 { 403 x86_android_tablet_device = platform_create_bundle(&x86_android_tablet_driver, 404 x86_android_tablet_probe, 405 NULL, 0, NULL, 0); 406 407 return PTR_ERR_OR_ZERO(x86_android_tablet_device); 408 } 409 module_init(x86_android_tablet_init); 410 411 static void __exit x86_android_tablet_exit(void) 412 { 413 platform_device_unregister(x86_android_tablet_device); 414 platform_driver_unregister(&x86_android_tablet_driver); 415 } 416 module_exit(x86_android_tablet_exit); 417 418 MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>"); 419 MODULE_DESCRIPTION("X86 Android tablets DSDT fixups driver"); 420 MODULE_LICENSE("GPL"); 421