Re: [PATCH] mfd: Add ACPI support to Kontron PLD driver

From: Lee Jones
Date: Wed Aug 19 2020 - 06:15:49 EST


On Wed, 12 Aug 2020, Michael Brunner wrote:

> Recent Kontron COMe modules identify the PLD device using the hardware
> id KEM0001 in the ACPI table.
> This patch adds support for probing the device using the HID and also
> retrieving the resources.
>
> As this is not available for all products, the DMI based detection still
> needs to be around for older systems. It is executed if no matching ACPI
> HID is found during registering the platform driver or no specific
> device id is forced.
> If a device is detected using ACPI and no resource information is
> available, the default io resource is used.
>
> Forcing a device id with the force_device_id parameter and therefore
> manually generating a platform device takes precedence over ACPI during
> probing.
>
> Signed-off-by: Michael Brunner <michael.brunner@xxxxxxxxxxx>
> ---
> drivers/mfd/kempld-core.c | 97 ++++++++++++++++++++++++++++++++++++---
> 1 file changed, 91 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/mfd/kempld-core.c b/drivers/mfd/kempld-core.c
> index f48e21d8b97c..408cad1958d9 100644
> --- a/drivers/mfd/kempld-core.c
> +++ b/drivers/mfd/kempld-core.c
> @@ -13,6 +13,7 @@
> #include <linux/dmi.h>
> #include <linux/io.h>
> #include <linux/delay.h>
> +#include <linux/acpi.h>
>
> #define MAX_ID_LEN 4
> static char force_device_id[MAX_ID_LEN + 1] = "";
> @@ -132,6 +133,7 @@ static const struct kempld_platform_data kempld_platform_data_generic = {
> };
>
> static struct platform_device *kempld_pdev;
> +static bool kempld_acpi_mode;
>
> static int kempld_create_platform_device(const struct dmi_system_id *id)
> {
> @@ -434,13 +436,87 @@ static int kempld_detect_device(struct kempld_device_data *pld)
> return ret;
> }
>
> +#ifdef CONFIG_ACPI

Not keen on #ifdefery if at all avoidable.

Can you use if (IS_ENABLED(CONFIG_ACPI)) at the call-site instead?

The compiler should take care of the rest, no?

> +static const struct acpi_device_id kempld_acpi_table[] = {
> + { "KEM0001", (kernel_ulong_t)&kempld_platform_data_generic },
> + {}
> +};
> +MODULE_DEVICE_TABLE(acpi, kempld_acpi_table);

I'd prefer if this was moved down to just above where it's used
i.e. where we usually place the of_device_id tables.

> +static int kempld_get_acpi_data(struct platform_device *pdev)
> +{
> + struct list_head resource_list;
> + struct resource *resources;
> + struct resource_entry *rentry;
> + struct device *dev = &pdev->dev;
> + struct acpi_device *acpi_dev = ACPI_COMPANION(dev);
> + const struct kempld_platform_data *pdata;
> + int ret;
> + int count;
> +
> + pdata = acpi_device_get_match_data(dev);
> + ret = platform_device_add_data(pdev, pdata,
> + sizeof(struct kempld_platform_data));
> + if (ret)
> + return ret;
> +
> + INIT_LIST_HEAD(&resource_list);
> + ret = acpi_dev_get_resources(acpi_dev, &resource_list, NULL, NULL);
> + if (ret < 0)
> + goto out;
> +
> + count = ret;

if (count == 0) {
ret = platform_device_add_resources(pdev, pdata->ioresource, 1);
goto out;
}

Then drop the next check and pull the indented code back:

> + if (count > 0) {
> + resources = devm_kcalloc(&acpi_dev->dev, count,
> + sizeof(struct resource), GFP_KERNEL);

sizeof(*resources) is preferred.

> + if (!resources) {
> + ret = -ENOMEM;
> + goto out;
> + }
> +
> + count = 0;
> + list_for_each_entry(rentry, &resource_list, node) {
> + memcpy(&resources[count], rentry->res,
> + sizeof(*resources));
> + count++;
> + }
> +
> + ret = platform_device_add_resources(pdev, resources, count);
> + if (ret)
> + goto out;
> + } else
> + ret = platform_device_add_resources(pdev, pdata->ioresource, 1);
> +
> +out:
> + acpi_dev_free_resource_list(&resource_list);
> +
> + return ret;
> +}
> +#else
> +static int kempld_get_acpi_data(struct platform_device *pdev)
> +{
> + return -ENODEV;
> +}
> +#endif /* CONFIG_ACPI */
> +
> static int kempld_probe(struct platform_device *pdev)
> {
> - const struct kempld_platform_data *pdata =
> - dev_get_platdata(&pdev->dev);
> + const struct kempld_platform_data *pdata;
> struct device *dev = &pdev->dev;
> struct kempld_device_data *pld;
> struct resource *ioport;
> + int ret;
> +
> + if (kempld_pdev == NULL) {

Comment please. What does !kempld_pdev actually imply?

> + ret = kempld_get_acpi_data(pdev);
> + if (ret < 0)
> + return ret;

Is 'ret > 0' valid?

If not, then just 'if (ret)'.

> + kempld_acpi_mode = true;
> + } else if (kempld_pdev != pdev) {

> + dev_notice(dev, "platform device exists - not using ACPI\n");

Why dev_notice() and not dev_err()?

Is that what 'kempld_pdev != pdev' means?

Could you explain this to me in more depth please?

> + return -ENODEV;
> + }
> + pdata = dev_get_platdata(dev);
>
> pld = devm_kzalloc(dev, sizeof(*pld), GFP_KERNEL);
> if (!pld)
> @@ -482,6 +558,7 @@ static int kempld_remove(struct platform_device *pdev)
> static struct platform_driver kempld_driver = {
> .driver = {
> .name = "kempld",
> + .acpi_match_table = ACPI_PTR(kempld_acpi_table),
> },
> .probe = kempld_probe,
> .remove = kempld_remove,
> @@ -800,6 +877,7 @@ MODULE_DEVICE_TABLE(dmi, kempld_dmi_table);
> static int __init kempld_init(void)
> {
> const struct dmi_system_id *id;
> + int ret;
>
> if (force_device_id[0]) {
> for (id = kempld_dmi_table;
> @@ -809,12 +887,19 @@ static int __init kempld_init(void)
> break;
> if (id->matches[0].slot == DMI_NONE)
> return -ENODEV;
> - } else {
> - if (!dmi_check_system(kempld_dmi_table))
> - return -ENODEV;
> }
>
> - return platform_driver_register(&kempld_driver);
> + ret = platform_driver_register(&kempld_driver);
> + if (ret)
> + return ret;

Is it guaranteed that the child device has probed at this point?

> + if (!kempld_pdev && !kempld_acpi_mode)

Again, comment please. What has gone on to get to this point?

> + if (!dmi_check_system(kempld_dmi_table)) {
> + platform_driver_unregister(&kempld_driver);
> + return -ENODEV;
> + }
> +
> + return 0;
> }
>
> static void __exit kempld_exit(void)

--
Lee Jones [李琼斯]
Senior Technical Lead - Developer Services
Linaro.org │ Open source software for Arm SoCs
Follow Linaro: Facebook | Twitter | Blog