| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Kontron PLD MFD core driver |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (c) 2010-2013 Kontron Europe GmbH |
|---|
| 5 | 6 | * Author: Michael Brunner <michael.brunner@kontron.com> |
|---|
| 6 | | - * |
|---|
| 7 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 8 | | - * it under the terms of the GNU General Public License 2 as published |
|---|
| 9 | | - * by the Free Software Foundation. |
|---|
| 10 | | - * |
|---|
| 11 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 12 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 13 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 14 | | - * GNU General Public License for more details. |
|---|
| 15 | 7 | */ |
|---|
| 16 | 8 | |
|---|
| 17 | 9 | #include <linux/platform_device.h> |
|---|
| .. | .. |
|---|
| 21 | 13 | #include <linux/dmi.h> |
|---|
| 22 | 14 | #include <linux/io.h> |
|---|
| 23 | 15 | #include <linux/delay.h> |
|---|
| 16 | +#include <linux/acpi.h> |
|---|
| 24 | 17 | |
|---|
| 25 | 18 | #define MAX_ID_LEN 4 |
|---|
| 26 | 19 | static char force_device_id[MAX_ID_LEN + 1] = ""; |
|---|
| .. | .. |
|---|
| 87 | 80 | KEMPLD_UART, |
|---|
| 88 | 81 | }; |
|---|
| 89 | 82 | |
|---|
| 90 | | -static const struct mfd_cell kempld_devs[] = { |
|---|
| 91 | | - [KEMPLD_I2C] = { |
|---|
| 92 | | - .name = "kempld-i2c", |
|---|
| 93 | | - }, |
|---|
| 94 | | - [KEMPLD_WDT] = { |
|---|
| 95 | | - .name = "kempld-wdt", |
|---|
| 96 | | - }, |
|---|
| 97 | | - [KEMPLD_GPIO] = { |
|---|
| 98 | | - .name = "kempld-gpio", |
|---|
| 99 | | - }, |
|---|
| 100 | | - [KEMPLD_UART] = { |
|---|
| 101 | | - .name = "kempld-uart", |
|---|
| 102 | | - }, |
|---|
| 83 | +static const char *kempld_dev_names[] = { |
|---|
| 84 | + [KEMPLD_I2C] = "kempld-i2c", |
|---|
| 85 | + [KEMPLD_WDT] = "kempld-wdt", |
|---|
| 86 | + [KEMPLD_GPIO] = "kempld-gpio", |
|---|
| 87 | + [KEMPLD_UART] = "kempld-uart", |
|---|
| 103 | 88 | }; |
|---|
| 104 | 89 | |
|---|
| 105 | | -#define KEMPLD_MAX_DEVS ARRAY_SIZE(kempld_devs) |
|---|
| 90 | +#define KEMPLD_MAX_DEVS ARRAY_SIZE(kempld_dev_names) |
|---|
| 106 | 91 | |
|---|
| 107 | 92 | static int kempld_register_cells_generic(struct kempld_device_data *pld) |
|---|
| 108 | 93 | { |
|---|
| 109 | | - struct mfd_cell devs[KEMPLD_MAX_DEVS]; |
|---|
| 94 | + struct mfd_cell devs[KEMPLD_MAX_DEVS] = {}; |
|---|
| 110 | 95 | int i = 0; |
|---|
| 111 | 96 | |
|---|
| 112 | 97 | if (pld->feature_mask & KEMPLD_FEATURE_BIT_I2C) |
|---|
| 113 | | - devs[i++] = kempld_devs[KEMPLD_I2C]; |
|---|
| 98 | + devs[i++].name = kempld_dev_names[KEMPLD_I2C]; |
|---|
| 114 | 99 | |
|---|
| 115 | 100 | if (pld->feature_mask & KEMPLD_FEATURE_BIT_WATCHDOG) |
|---|
| 116 | | - devs[i++] = kempld_devs[KEMPLD_WDT]; |
|---|
| 101 | + devs[i++].name = kempld_dev_names[KEMPLD_WDT]; |
|---|
| 117 | 102 | |
|---|
| 118 | 103 | if (pld->feature_mask & KEMPLD_FEATURE_BIT_GPIO) |
|---|
| 119 | | - devs[i++] = kempld_devs[KEMPLD_GPIO]; |
|---|
| 104 | + devs[i++].name = kempld_dev_names[KEMPLD_GPIO]; |
|---|
| 120 | 105 | |
|---|
| 121 | 106 | if (pld->feature_mask & KEMPLD_FEATURE_MASK_UART) |
|---|
| 122 | | - devs[i++] = kempld_devs[KEMPLD_UART]; |
|---|
| 107 | + devs[i++].name = kempld_dev_names[KEMPLD_UART]; |
|---|
| 123 | 108 | |
|---|
| 124 | 109 | return mfd_add_devices(pld->dev, -1, devs, i, NULL, 0, NULL); |
|---|
| 125 | 110 | } |
|---|
| .. | .. |
|---|
| 140 | 125 | }; |
|---|
| 141 | 126 | |
|---|
| 142 | 127 | static struct platform_device *kempld_pdev; |
|---|
| 128 | +static bool kempld_acpi_mode; |
|---|
| 143 | 129 | |
|---|
| 144 | 130 | static int kempld_create_platform_device(const struct dmi_system_id *id) |
|---|
| 145 | 131 | { |
|---|
| .. | .. |
|---|
| 442 | 428 | return ret; |
|---|
| 443 | 429 | } |
|---|
| 444 | 430 | |
|---|
| 431 | +#ifdef CONFIG_ACPI |
|---|
| 432 | +static int kempld_get_acpi_data(struct platform_device *pdev) |
|---|
| 433 | +{ |
|---|
| 434 | + struct list_head resource_list; |
|---|
| 435 | + struct resource *resources; |
|---|
| 436 | + struct resource_entry *rentry; |
|---|
| 437 | + struct device *dev = &pdev->dev; |
|---|
| 438 | + struct acpi_device *acpi_dev = ACPI_COMPANION(dev); |
|---|
| 439 | + const struct kempld_platform_data *pdata; |
|---|
| 440 | + int ret; |
|---|
| 441 | + int count; |
|---|
| 442 | + |
|---|
| 443 | + pdata = acpi_device_get_match_data(dev); |
|---|
| 444 | + ret = platform_device_add_data(pdev, pdata, |
|---|
| 445 | + sizeof(struct kempld_platform_data)); |
|---|
| 446 | + if (ret) |
|---|
| 447 | + return ret; |
|---|
| 448 | + |
|---|
| 449 | + INIT_LIST_HEAD(&resource_list); |
|---|
| 450 | + ret = acpi_dev_get_resources(acpi_dev, &resource_list, NULL, NULL); |
|---|
| 451 | + if (ret < 0) |
|---|
| 452 | + goto out; |
|---|
| 453 | + |
|---|
| 454 | + count = ret; |
|---|
| 455 | + |
|---|
| 456 | + if (count == 0) { |
|---|
| 457 | + ret = platform_device_add_resources(pdev, pdata->ioresource, 1); |
|---|
| 458 | + goto out; |
|---|
| 459 | + } |
|---|
| 460 | + |
|---|
| 461 | + resources = devm_kcalloc(&acpi_dev->dev, count, sizeof(*resources), |
|---|
| 462 | + GFP_KERNEL); |
|---|
| 463 | + if (!resources) { |
|---|
| 464 | + ret = -ENOMEM; |
|---|
| 465 | + goto out; |
|---|
| 466 | + } |
|---|
| 467 | + |
|---|
| 468 | + count = 0; |
|---|
| 469 | + list_for_each_entry(rentry, &resource_list, node) { |
|---|
| 470 | + memcpy(&resources[count], rentry->res, |
|---|
| 471 | + sizeof(*resources)); |
|---|
| 472 | + count++; |
|---|
| 473 | + } |
|---|
| 474 | + ret = platform_device_add_resources(pdev, resources, count); |
|---|
| 475 | + |
|---|
| 476 | +out: |
|---|
| 477 | + acpi_dev_free_resource_list(&resource_list); |
|---|
| 478 | + |
|---|
| 479 | + return ret; |
|---|
| 480 | +} |
|---|
| 481 | +#else |
|---|
| 482 | +static int kempld_get_acpi_data(struct platform_device *pdev) |
|---|
| 483 | +{ |
|---|
| 484 | + return -ENODEV; |
|---|
| 485 | +} |
|---|
| 486 | +#endif /* CONFIG_ACPI */ |
|---|
| 487 | + |
|---|
| 445 | 488 | static int kempld_probe(struct platform_device *pdev) |
|---|
| 446 | 489 | { |
|---|
| 447 | | - const struct kempld_platform_data *pdata = |
|---|
| 448 | | - dev_get_platdata(&pdev->dev); |
|---|
| 490 | + const struct kempld_platform_data *pdata; |
|---|
| 449 | 491 | struct device *dev = &pdev->dev; |
|---|
| 450 | 492 | struct kempld_device_data *pld; |
|---|
| 451 | 493 | struct resource *ioport; |
|---|
| 494 | + int ret; |
|---|
| 495 | + |
|---|
| 496 | + if (kempld_pdev == NULL) { |
|---|
| 497 | + /* |
|---|
| 498 | + * No kempld_pdev device has been registered in kempld_init, |
|---|
| 499 | + * so we seem to be probing an ACPI platform device. |
|---|
| 500 | + */ |
|---|
| 501 | + ret = kempld_get_acpi_data(pdev); |
|---|
| 502 | + if (ret) |
|---|
| 503 | + return ret; |
|---|
| 504 | + |
|---|
| 505 | + kempld_acpi_mode = true; |
|---|
| 506 | + } else if (kempld_pdev != pdev) { |
|---|
| 507 | + /* |
|---|
| 508 | + * The platform device we are probing is not the one we |
|---|
| 509 | + * registered in kempld_init using the DMI table, so this one |
|---|
| 510 | + * comes from ACPI. |
|---|
| 511 | + * As we can only probe one - abort here and use the DMI |
|---|
| 512 | + * based one instead. |
|---|
| 513 | + */ |
|---|
| 514 | + dev_notice(dev, "platform device exists - not using ACPI\n"); |
|---|
| 515 | + return -ENODEV; |
|---|
| 516 | + } |
|---|
| 517 | + pdata = dev_get_platdata(dev); |
|---|
| 452 | 518 | |
|---|
| 453 | 519 | pld = devm_kzalloc(dev, sizeof(*pld), GFP_KERNEL); |
|---|
| 454 | 520 | if (!pld) |
|---|
| .. | .. |
|---|
| 487 | 553 | return 0; |
|---|
| 488 | 554 | } |
|---|
| 489 | 555 | |
|---|
| 556 | +#ifdef CONFIG_ACPI |
|---|
| 557 | +static const struct acpi_device_id kempld_acpi_table[] = { |
|---|
| 558 | + { "KEM0001", (kernel_ulong_t)&kempld_platform_data_generic }, |
|---|
| 559 | + {} |
|---|
| 560 | +}; |
|---|
| 561 | +MODULE_DEVICE_TABLE(acpi, kempld_acpi_table); |
|---|
| 562 | +#endif |
|---|
| 563 | + |
|---|
| 490 | 564 | static struct platform_driver kempld_driver = { |
|---|
| 491 | 565 | .driver = { |
|---|
| 492 | 566 | .name = "kempld", |
|---|
| 567 | + .acpi_match_table = ACPI_PTR(kempld_acpi_table), |
|---|
| 568 | + .probe_type = PROBE_FORCE_SYNCHRONOUS, |
|---|
| 493 | 569 | }, |
|---|
| 494 | 570 | .probe = kempld_probe, |
|---|
| 495 | 571 | .remove = kempld_remove, |
|---|
| .. | .. |
|---|
| 808 | 884 | static int __init kempld_init(void) |
|---|
| 809 | 885 | { |
|---|
| 810 | 886 | const struct dmi_system_id *id; |
|---|
| 887 | + int ret; |
|---|
| 811 | 888 | |
|---|
| 812 | 889 | if (force_device_id[0]) { |
|---|
| 813 | 890 | for (id = kempld_dmi_table; |
|---|
| .. | .. |
|---|
| 817 | 894 | break; |
|---|
| 818 | 895 | if (id->matches[0].slot == DMI_NONE) |
|---|
| 819 | 896 | return -ENODEV; |
|---|
| 820 | | - } else { |
|---|
| 821 | | - if (!dmi_check_system(kempld_dmi_table)) |
|---|
| 822 | | - return -ENODEV; |
|---|
| 823 | 897 | } |
|---|
| 824 | 898 | |
|---|
| 825 | | - return platform_driver_register(&kempld_driver); |
|---|
| 899 | + ret = platform_driver_register(&kempld_driver); |
|---|
| 900 | + if (ret) |
|---|
| 901 | + return ret; |
|---|
| 902 | + |
|---|
| 903 | + /* |
|---|
| 904 | + * With synchronous probing the device should already be probed now. |
|---|
| 905 | + * If no device id is forced and also no ACPI definition for the |
|---|
| 906 | + * device was found, scan DMI table as fallback. |
|---|
| 907 | + * |
|---|
| 908 | + * If drivers_autoprobing is disabled and the device is found here, |
|---|
| 909 | + * only that device can be bound manually later. |
|---|
| 910 | + */ |
|---|
| 911 | + if (!kempld_pdev && !kempld_acpi_mode) |
|---|
| 912 | + dmi_check_system(kempld_dmi_table); |
|---|
| 913 | + |
|---|
| 914 | + return 0; |
|---|
| 826 | 915 | } |
|---|
| 827 | 916 | |
|---|
| 828 | 917 | static void __exit kempld_exit(void) |
|---|