Message ID | 1418267089-5555-1-git-send-email-Ken.Xue@amd.com (mailing list archive) |
---|---|
State | Changes Requested, archived |
Headers | show |
On Thu, Dec 11, 2014 at 11:04:49AM +0800, Ken Xue wrote: > This patch is supposed to deliver some common codes for AMD APD and > INTEL LPSS. It can help to convert some specific acpi devices to be INTEL -> Intel, acpi -> ACPI > platform devices. > > Signed-off-by: Ken Xue <Ken.Xue@amd.com> > --- > drivers/acpi/Makefile | 2 +- > drivers/acpi/acpi_soc.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++++ > drivers/acpi/acpi_soc.h | 98 +++++++++++++++++++++ > 3 files changed, 323 insertions(+), 1 deletion(-) > create mode 100644 drivers/acpi/acpi_soc.c > create mode 100644 drivers/acpi/acpi_soc.h > > diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile > index c3b2fcb..ae3397d 100644 > --- a/drivers/acpi/Makefile > +++ b/drivers/acpi/Makefile > @@ -40,7 +40,7 @@ acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o > acpi-y += ec.o > acpi-$(CONFIG_ACPI_DOCK) += dock.o > acpi-y += pci_root.o pci_link.o pci_irq.o > -acpi-y += acpi_lpss.o > +acpi-y += acpi_soc.o acpi_lpss.o > acpi-y += acpi_platform.o > acpi-y += acpi_pnp.o > acpi-y += int340x_thermal.o > diff --git a/drivers/acpi/acpi_soc.c b/drivers/acpi/acpi_soc.c > new file mode 100644 > index 0000000..46901d5 > --- /dev/null > +++ b/drivers/acpi/acpi_soc.c > @@ -0,0 +1,224 @@ > +/* > + * ACPI SOC support for Intel Lynxpoint LPSS and AMD APD. > + * > + * Copyright (C) 2015, Intel Corporation & AMD Corporation > + * Authors: Ken Xue <Ken.Xue@amd.com> > + * Mika Westerberg <mika.westerberg@linux.intel.com> > + * Rafael J. Wysocki <rafael.j.wysocki@intel.com> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + */ > + > +#include <linux/err.h> > +#include <linux/list.h> > +#include <linux/pm_domain.h> > +#include <linux/platform_device.h> > + > +#include "acpi_soc.h" > +#include "internal.h" > + > +ACPI_MODULE_NAME("acpi_soc"); > + > +/* A list for all acpi soc device */ > +static LIST_HEAD(a_soc_list); > + > +static int is_memory(struct acpi_resource *res, void *not_used) > +{ > + struct resource r; > + > + return !acpi_dev_resource_memory(res, &r); > +} > + > +static int acpi_soc_create_device(struct acpi_device *adev, > + const struct acpi_device_id *id) > +{ > + struct acpi_soc_dev_desc *dev_desc; > + struct acpi_soc_dev_private_data *pdata; > + struct resource_list_entry *rentry; > + struct list_head resource_list; > + struct platform_device *pdev; > + int ret; > + > + dev_desc = (struct acpi_soc_dev_desc *)id->driver_data; > + if (!dev_desc) { > + pdev = acpi_create_platform_device(adev); > + return IS_ERR_OR_NULL(pdev) ? PTR_ERR(pdev) : 1; > + } > + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); > + if (!pdata) > + return -ENOMEM; > + > + INIT_LIST_HEAD(&resource_list); > + ret = acpi_dev_get_resources(adev, &resource_list, is_memory, NULL); > + if (ret < 0) > + goto err_out; > + > + list_for_each_entry(rentry, &resource_list, node) > + if (resource_type(&rentry->res) == IORESOURCE_MEM) { > + if (dev_desc->mem_size_override) > + pdata->mmio_size = dev_desc->mem_size_override; > + else > + pdata->mmio_size = resource_size(&rentry->res); > + pdata->mmio_base = ioremap(rentry->res.start, > + pdata->mmio_size); > + break; > + } > + > + acpi_dev_free_resource_list(&resource_list); > + > + pdata->adev = adev; > + pdata->dev_desc = dev_desc; > + > + if (dev_desc->setup) { > + ret = dev_desc->setup(pdata); > + if (ret) > + goto err_out; > + } > + > + /* > + * This works around a known issue in ACPI tables where acpi soc devices acpi soc -> ACPI SoC Please use these consistently. > + * have _PS0 and _PS3 without _PSC (and no power resources), so > + * acpi_bus_init_power() will assume that the BIOS has put them into D0. > + */ > + ret = acpi_device_fix_up_power(adev); > + if (ret) { > + /* Skip the device, but continue the namespace scan. */ > + ret = 0; > + goto err_out; > + } > + > + adev->driver_data = pdata; > + pdev = acpi_create_platform_device(adev); > + if (!IS_ERR_OR_NULL(pdev)) > + return 1; > + > + ret = PTR_ERR(pdev); > + adev->driver_data = NULL; > + > + err_out: > + kfree(pdata); > + return ret; > +} > + > +static int acpi_soc_platform_notify(struct notifier_block *nb, > + unsigned long action, void *data) > +{ > + struct platform_device *pdev = to_platform_device(data); > + struct acpi_soc_dev_private_data *pdata; > + struct acpi_device *adev; > + struct acpi_soc *a_soc_entry; > + const struct acpi_device_id *id = NULL; > + > + list_for_each_entry(a_soc_entry, &a_soc_list, list) { > + id = acpi_match_device(a_soc_entry->ids, &pdev->dev); > + if (id) > + break; > + } > + > + if (!id || !id->driver_data) > + return 0; > + > + if (acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev)) > + return 0; > + > + pdata = acpi_driver_data(adev); > + if (!pdata || !pdata->mmio_base) > + return 0; > + > + switch (action) { > + case BUS_NOTIFY_BOUND_DRIVER: > + if ((pdata->dev_desc->flags & ACPI_SOC_PM)) { > + if (a_soc_entry->pm_domain) > + pdev->dev.pm_domain = a_soc_entry->pm_domain; > + else if (pdata->dev_desc->flags & ACPI_SOC_PM_ON) > + dev_pm_domain_attach(&pdev->dev, true); Too much indent. > + else > + dev_pm_domain_attach(&pdev->dev, false); Ditto. > + } > + break; > + case BUS_NOTIFY_UNBOUND_DRIVER: > + if ((pdata->dev_desc->flags & ACPI_SOC_PM)) { > + if (a_soc_entry->pm_domain) > + pdev->dev.pm_domain = a_soc_entry->pm_domain; > + else if (pdata->dev_desc->flags & ACPI_SOC_PM_ON) > + dev_pm_domain_detach(&pdev->dev, true); Ditto. > + else > + dev_pm_domain_detach(&pdev->dev, false); Ditto > + } > + break; > + case BUS_NOTIFY_ADD_DEVICE: > + if ((pdata->dev_desc->flags & ACPI_SOC_SYSFS) > + && a_soc_entry->attr_group) > + sysfs_create_group(&pdev->dev.kobj, > + a_soc_entry->attr_group); > + break; > + case BUS_NOTIFY_DEL_DEVICE: > + if ((pdata->dev_desc->flags & ACPI_SOC_SYSFS) > + && a_soc_entry->attr_group) > + sysfs_remove_group(&pdev->dev.kobj, > + a_soc_entry->attr_group); > + break; > + } > + > + return 0; > +} > + > +static struct notifier_block acpi_soc_nb = { > + .notifier_call = acpi_soc_platform_notify, > +}; > + > +static void acpi_soc_bind(struct device *dev) > +{ > + struct acpi_soc_dev_private_data *pdata; > + > + pdata = acpi_driver_data(ACPI_COMPANION(dev)); > + > + if (!pdata || !pdata->dev_desc || !pdata->dev_desc->bind) > + return; > + > + pdata->dev_desc->bind(pdata, dev); > +} > + > +static void acpi_soc_unbind(struct device *dev) > +{ > + struct acpi_soc_dev_private_data *pdata; > + > + pdata = acpi_driver_data(ACPI_COMPANION(dev)); > + > + if (!pdata || !pdata->dev_desc || !pdata->dev_desc->unbind) > + return; > + > + pdata->dev_desc->unbind(pdata, dev); > +} > + > +/** > + * register_acpi_soc - register a new acpi soc > + * @a_soc: acpi soc > + * @disable_scan_handler: true means remove default scan handle > + * false means use default scan handle > + * > + * register a new acpi soc into asoc_list and install default scan handle. > + */ > +void register_acpi_soc(struct acpi_soc *a_soc, bool disable_scan_handler) I still think passing scan handler here is better. Up to Rafael to decide. > +{ > + static int init; > + struct acpi_scan_handler *acpi_soc_handler; > + > + INIT_LIST_HEAD(&a_soc->list); > + list_add(&a_soc->list, &a_soc_list); > + > + acpi_soc_handler = kzalloc(sizeof(*acpi_soc_handler), GFP_KERNEL); > + acpi_soc_handler->ids = a_soc->ids; > + if (!disable_scan_handler) { > + acpi_soc_handler->attach = acpi_soc_create_device; > + acpi_soc_handler->bind = acpi_soc_bind; > + acpi_soc_handler->unbind = acpi_soc_unbind; > + if (init == 0) { > + init++; > + bus_register_notifier(&platform_bus_type, &acpi_soc_nb); > + } > + } > + acpi_scan_add_handler(acpi_soc_handler); > +} > diff --git a/drivers/acpi/acpi_soc.h b/drivers/acpi/acpi_soc.h > new file mode 100644 > index 0000000..bada2a1 > --- /dev/null > +++ b/drivers/acpi/acpi_soc.h > @@ -0,0 +1,98 @@ > +/* > + * ACPI SOC support for Intel Lynxpoint LPSS and AMD APD. > + * > + * Copyright (C) 2015, Intel Corporation & AMD Corporation > + * Authors: Ken Xue <Ken.Xue@amd.com> > + * Mika Westerberg <mika.westerberg@linux.intel.com> > + * Rafael J. Wysocki <rafael.j.wysocki@intel.com> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + */ > +#ifndef _ACPI_SOC_H > +#define _ACPI_SOC_H > + > +#include <linux/acpi.h> > +#include <linux/clk.h> > +#include <linux/pm.h> > + > +struct acpi_soc_dev_private_data; > + > +/** > + * struct acpi_soc - acpi soc > + * @list: list head > + * @ids: all acpi device ids for acpi soc > + * @pm_domain: power domain for all acpi device;can be NULL > + * @attr_group: attribute group for sysfs support of acpi soc;can be NULL > + */ > +struct acpi_soc { > + struct list_head list; > + struct acpi_device_id *ids; > + struct dev_pm_domain *pm_domain; > + struct attribute_group *attr_group; > +}; > + > + > +/** > + * device flags of acpi_soc_dev_desc. > + * bit 16 to 31 reserved for acpi soc. > + * bit 0 ~15 reserved for private flags. > + * ACPI_SOC_SYSFS : add device attributes in sysfs > + * ACPI_SOC_PM : attach power domain to device > + * ACPI_SOC_PM_ON : power on device when attach power domain > + */ > +#define ACPI_SOC_SYSFS BIT(16) > +#define ACPI_SOC_PM BIT(17) > +#define ACPI_SOC_PM_ON BIT(18) > + > +/** > + * struct acpi_soc_dev_desc - a descriptor for acpi device > + * @flags: device flags like ACPI_SOC_SYSFS ACPI_SOC_PM ACPI_SOC_PM_ON > + * @clk: clock device > + * @fixed_clk_rate: fixed rate input clock source for acpi device; > + * 0 means no fixed rate input clock source > + * @mem_size_override: a workaround for override device memsize; > + * 0 means no needs for this WA > + * @prv_offset: reg offest of lpss features > + * @setup: a hook routine to set device resource during create platform device > + * @bind: a hook of acpi_scan_handler.bind > + * @unbind: a hook of acpi_scan_handler.unbind > + * > + * device description defined as acpi_device_id.driver_data > + */ > +struct acpi_soc_dev_desc { > + unsigned int flags; > + struct clk *clk; > + unsigned int fixed_clk_rate; > + size_t mem_size_override; > + unsigned int prv_offset; > + int (*setup)(struct acpi_soc_dev_private_data *pdata); > + void (*bind)(struct acpi_soc_dev_private_data *pdata, > + struct device *dev); > + void (*unbind)(struct acpi_soc_dev_private_data *pdata, > + struct device *dev); > +}; > + > +#define ACPI_SOC_REG_CONTEXT_MAX 10 > + > +/** > + * struct acpi_soc_dev_private_data - acpi device private data > + * @mmio_base: virtual memory base addr of the device > + * @mmio_size: device memory size > + * @dev_desc: device description > + * @adev: acpi device > + * @prv_reg_ctx: reg context for power management > + */ > +struct acpi_soc_dev_private_data { > + void __iomem *mmio_base; > + resource_size_t mmio_size; > + > + struct acpi_soc_dev_desc *dev_desc; > + struct acpi_device *adev; > + u32 prv_reg_ctx[ACPI_SOC_REG_CONTEXT_MAX]; > +}; > + > +void register_acpi_soc(struct acpi_soc *a_soc, bool disable_scan_handler); > + > +#endif > -- > 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Thu, Dec 11, 2014 at 5:04 AM, Ken Xue <Ken.Xue@amd.com> wrote: > This patch is supposed to deliver some common codes for AMD APD and > INTEL LPSS. It can help to convert some specific acpi devices to be > platform devices. My few comments below. First of all, please, add me to the Cc list of this patch set in the future. > > Signed-off-by: Ken Xue <Ken.Xue@amd.com> > --- > drivers/acpi/Makefile | 2 +- > drivers/acpi/acpi_soc.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++++ > drivers/acpi/acpi_soc.h | 98 +++++++++++++++++++++ > 3 files changed, 323 insertions(+), 1 deletion(-) > create mode 100644 drivers/acpi/acpi_soc.c > create mode 100644 drivers/acpi/acpi_soc.h > > diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile > index c3b2fcb..ae3397d 100644 > --- a/drivers/acpi/Makefile > +++ b/drivers/acpi/Makefile > @@ -40,7 +40,7 @@ acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o > acpi-y += ec.o > acpi-$(CONFIG_ACPI_DOCK) += dock.o > acpi-y += pci_root.o pci_link.o pci_irq.o > -acpi-y += acpi_lpss.o > +acpi-y += acpi_soc.o acpi_lpss.o > acpi-y += acpi_platform.o > acpi-y += acpi_pnp.o > acpi-y += int340x_thermal.o > diff --git a/drivers/acpi/acpi_soc.c b/drivers/acpi/acpi_soc.c > new file mode 100644 > index 0000000..46901d5 > --- /dev/null > +++ b/drivers/acpi/acpi_soc.c > @@ -0,0 +1,224 @@ > +/* > + * ACPI SOC support for Intel Lynxpoint LPSS and AMD APD. > + * > + * Copyright (C) 2015, Intel Corporation & AMD Corporation 2015? Wait couple of weeks :-) Moreover, Intel code is copyrighted starting from 2013. I think it would be better to keep two lines, one is original from Intel and one from AMD. > + * Authors: Ken Xue <Ken.Xue@amd.com> > + * Mika Westerberg <mika.westerberg@linux.intel.com> > + * Rafael J. Wysocki <rafael.j.wysocki@intel.com> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + */ > + > +#include <linux/err.h> > +#include <linux/list.h> > +#include <linux/pm_domain.h> > +#include <linux/platform_device.h> > + > +#include "acpi_soc.h" > +#include "internal.h" > + > +ACPI_MODULE_NAME("acpi_soc"); > + > +/* A list for all acpi soc device */ > +static LIST_HEAD(a_soc_list); > + > +static int is_memory(struct acpi_resource *res, void *not_used) > +{ > + struct resource r; > + > + return !acpi_dev_resource_memory(res, &r); > +} > + > +static int acpi_soc_create_device(struct acpi_device *adev, > + const struct acpi_device_id *id) > +{ > + struct acpi_soc_dev_desc *dev_desc; > + struct acpi_soc_dev_private_data *pdata; > + struct resource_list_entry *rentry; > + struct list_head resource_list; > + struct platform_device *pdev; > + int ret; > + > + dev_desc = (struct acpi_soc_dev_desc *)id->driver_data; > + if (!dev_desc) { > + pdev = acpi_create_platform_device(adev); > + return IS_ERR_OR_NULL(pdev) ? PTR_ERR(pdev) : 1; > + } > + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); > + if (!pdata) > + return -ENOMEM; > + > + INIT_LIST_HEAD(&resource_list); > + ret = acpi_dev_get_resources(adev, &resource_list, is_memory, NULL); > + if (ret < 0) > + goto err_out; > + > + list_for_each_entry(rentry, &resource_list, node) > + if (resource_type(&rentry->res) == IORESOURCE_MEM) { > + if (dev_desc->mem_size_override) > + pdata->mmio_size = dev_desc->mem_size_override; > + else > + pdata->mmio_size = resource_size(&rentry->res); > + pdata->mmio_base = ioremap(rentry->res.start, > + pdata->mmio_size); > + break; > + } > + > + acpi_dev_free_resource_list(&resource_list); > + > + pdata->adev = adev; > + pdata->dev_desc = dev_desc; > + > + if (dev_desc->setup) { > + ret = dev_desc->setup(pdata); > + if (ret) > + goto err_out; > + } > + > + /* > + * This works around a known issue in ACPI tables where acpi soc devices > + * have _PS0 and _PS3 without _PSC (and no power resources), so > + * acpi_bus_init_power() will assume that the BIOS has put them into D0. > + */ > + ret = acpi_device_fix_up_power(adev); > + if (ret) { > + /* Skip the device, but continue the namespace scan. */ > + ret = 0; > + goto err_out; > + } > + > + adev->driver_data = pdata; > + pdev = acpi_create_platform_device(adev); > + if (!IS_ERR_OR_NULL(pdev)) > + return 1; > + > + ret = PTR_ERR(pdev); > + adev->driver_data = NULL; > + > + err_out: > + kfree(pdata); > + return ret; > +} > + > +static int acpi_soc_platform_notify(struct notifier_block *nb, > + unsigned long action, void *data) > +{ > + struct platform_device *pdev = to_platform_device(data); > + struct acpi_soc_dev_private_data *pdata; > + struct acpi_device *adev; > + struct acpi_soc *a_soc_entry; > + const struct acpi_device_id *id = NULL; > + > + list_for_each_entry(a_soc_entry, &a_soc_list, list) { > + id = acpi_match_device(a_soc_entry->ids, &pdev->dev); > + if (id) > + break; > + } > + > + if (!id || !id->driver_data) > + return 0; > + > + if (acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev)) > + return 0; > + > + pdata = acpi_driver_data(adev); > + if (!pdata || !pdata->mmio_base) > + return 0; > + > + switch (action) { > + case BUS_NOTIFY_BOUND_DRIVER: > + if ((pdata->dev_desc->flags & ACPI_SOC_PM)) { No need to have double parentheses here and below in the other conditions. It seems you took an old code here. We used to have a nasty bug which was fixed recently, namely by cb39dcdd4ef6, 01ac170ba29a, and 6c17ee44d524. Please, take most recent version from linux-pm tree. > + if (a_soc_entry->pm_domain) > + pdev->dev.pm_domain = a_soc_entry->pm_domain; > + else if (pdata->dev_desc->flags & ACPI_SOC_PM_ON) > + dev_pm_domain_attach(&pdev->dev, true); > + else > + dev_pm_domain_attach(&pdev->dev, false); > + } > + break; > + case BUS_NOTIFY_UNBOUND_DRIVER: > + if ((pdata->dev_desc->flags & ACPI_SOC_PM)) { > + if (a_soc_entry->pm_domain) > + pdev->dev.pm_domain = a_soc_entry->pm_domain; > + else if (pdata->dev_desc->flags & ACPI_SOC_PM_ON) > + dev_pm_domain_detach(&pdev->dev, true); > + else > + dev_pm_domain_detach(&pdev->dev, false); > + } > + break; > + case BUS_NOTIFY_ADD_DEVICE: > + if ((pdata->dev_desc->flags & ACPI_SOC_SYSFS) > + && a_soc_entry->attr_group) Unnecessary parentheses. Could it be one line? > + sysfs_create_group(&pdev->dev.kobj, > + a_soc_entry->attr_group); > + break; > + case BUS_NOTIFY_DEL_DEVICE: > + if ((pdata->dev_desc->flags & ACPI_SOC_SYSFS) > + && a_soc_entry->attr_group) Ditto. > + sysfs_remove_group(&pdev->dev.kobj, > + a_soc_entry->attr_group); > + break; > + } > + > + return 0; > +} > + > +static struct notifier_block acpi_soc_nb = { > + .notifier_call = acpi_soc_platform_notify, > +}; > + > +static void acpi_soc_bind(struct device *dev) > +{ > + struct acpi_soc_dev_private_data *pdata; > + > + pdata = acpi_driver_data(ACPI_COMPANION(dev)); > + > + if (!pdata || !pdata->dev_desc || !pdata->dev_desc->bind) > + return; > + > + pdata->dev_desc->bind(pdata, dev); > +} > + > +static void acpi_soc_unbind(struct device *dev) > +{ > + struct acpi_soc_dev_private_data *pdata; > + > + pdata = acpi_driver_data(ACPI_COMPANION(dev)); > + > + if (!pdata || !pdata->dev_desc || !pdata->dev_desc->unbind) > + return; > + > + pdata->dev_desc->unbind(pdata, dev); > +} > + > +/** > + * register_acpi_soc - register a new acpi soc > + * @a_soc: acpi soc > + * @disable_scan_handler: true means remove default scan handle > + * false means use default scan handle > + * > + * register a new acpi soc into asoc_list and install default scan handle. > + */ > +void register_acpi_soc(struct acpi_soc *a_soc, bool disable_scan_handler) > +{ > + static int init; > + struct acpi_scan_handler *acpi_soc_handler; > + > + INIT_LIST_HEAD(&a_soc->list); > + list_add(&a_soc->list, &a_soc_list); > + > + acpi_soc_handler = kzalloc(sizeof(*acpi_soc_handler), GFP_KERNEL); > + acpi_soc_handler->ids = a_soc->ids; > + if (!disable_scan_handler) { > + acpi_soc_handler->attach = acpi_soc_create_device; > + acpi_soc_handler->bind = acpi_soc_bind; > + acpi_soc_handler->unbind = acpi_soc_unbind; > + if (init == 0) { > + init++; > + bus_register_notifier(&platform_bus_type, &acpi_soc_nb); > + } > + } > + acpi_scan_add_handler(acpi_soc_handler); > +} > diff --git a/drivers/acpi/acpi_soc.h b/drivers/acpi/acpi_soc.h > new file mode 100644 > index 0000000..bada2a1 > --- /dev/null > +++ b/drivers/acpi/acpi_soc.h > @@ -0,0 +1,98 @@ > +/* > + * ACPI SOC support for Intel Lynxpoint LPSS and AMD APD. > + * > + * Copyright (C) 2015, Intel Corporation & AMD Corporation > + * Authors: Ken Xue <Ken.Xue@amd.com> > + * Mika Westerberg <mika.westerberg@linux.intel.com> > + * Rafael J. Wysocki <rafael.j.wysocki@intel.com> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 as > + * published by the Free Software Foundation. > + */ > +#ifndef _ACPI_SOC_H > +#define _ACPI_SOC_H > + > +#include <linux/acpi.h> > +#include <linux/clk.h> > +#include <linux/pm.h> > + > +struct acpi_soc_dev_private_data; > + > +/** > + * struct acpi_soc - acpi soc > + * @list: list head > + * @ids: all acpi device ids for acpi soc > + * @pm_domain: power domain for all acpi device;can be NULL > + * @attr_group: attribute group for sysfs support of acpi soc;can be NULL > + */ > +struct acpi_soc { > + struct list_head list; > + struct acpi_device_id *ids; > + struct dev_pm_domain *pm_domain; > + struct attribute_group *attr_group; > +}; > + > + > +/** > + * device flags of acpi_soc_dev_desc. > + * bit 16 to 31 reserved for acpi soc. > + * bit 0 ~15 reserved for private flags. > + * ACPI_SOC_SYSFS : add device attributes in sysfs > + * ACPI_SOC_PM : attach power domain to device > + * ACPI_SOC_PM_ON : power on device when attach power domain > + */ > +#define ACPI_SOC_SYSFS BIT(16) > +#define ACPI_SOC_PM BIT(17) > +#define ACPI_SOC_PM_ON BIT(18) > + > +/** > + * struct acpi_soc_dev_desc - a descriptor for acpi device > + * @flags: device flags like ACPI_SOC_SYSFS ACPI_SOC_PM ACPI_SOC_PM_ON > + * @clk: clock device > + * @fixed_clk_rate: fixed rate input clock source for acpi device; > + * 0 means no fixed rate input clock source > + * @mem_size_override: a workaround for override device memsize; > + * 0 means no needs for this WA > + * @prv_offset: reg offest of lpss features > + * @setup: a hook routine to set device resource during create platform device > + * @bind: a hook of acpi_scan_handler.bind > + * @unbind: a hook of acpi_scan_handler.unbind > + * > + * device description defined as acpi_device_id.driver_data > + */ > +struct acpi_soc_dev_desc { > + unsigned int flags; > + struct clk *clk; > + unsigned int fixed_clk_rate; > + size_t mem_size_override; > + unsigned int prv_offset; > + int (*setup)(struct acpi_soc_dev_private_data *pdata); > + void (*bind)(struct acpi_soc_dev_private_data *pdata, > + struct device *dev); > + void (*unbind)(struct acpi_soc_dev_private_data *pdata, > + struct device *dev); > +}; > + > +#define ACPI_SOC_REG_CONTEXT_MAX 10 > + > +/** > + * struct acpi_soc_dev_private_data - acpi device private data > + * @mmio_base: virtual memory base addr of the device > + * @mmio_size: device memory size > + * @dev_desc: device description > + * @adev: acpi device > + * @prv_reg_ctx: reg context for power management > + */ > +struct acpi_soc_dev_private_data { > + void __iomem *mmio_base; > + resource_size_t mmio_size; > + > + struct acpi_soc_dev_desc *dev_desc; > + struct acpi_device *adev; > + u32 prv_reg_ctx[ACPI_SOC_REG_CONTEXT_MAX]; > +}; > + > +void register_acpi_soc(struct acpi_soc *a_soc, bool disable_scan_handler); > + > +#endif > -- > 1.9.1 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-kernel" in > the body of a message to majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > Please read the FAQ at http://www.tux.org/lkml/
On Tue, 2014-12-16 at 12:19 +0200, Andy Shevchenko wrote: > On Thu, Dec 11, 2014 at 5:04 AM, Ken Xue <Ken.Xue@amd.com> wrote: > > This patch is supposed to deliver some common codes for AMD APD and > > INTEL LPSS. It can help to convert some specific acpi devices to be > > platform devices. > > My few comments below. > > First of all, please, add me to the Cc list of this patch set in the future. > [ken]got it. > > > > Signed-off-by: Ken Xue <Ken.Xue@amd.com> > > --- > > drivers/acpi/Makefile | 2 +- > > drivers/acpi/acpi_soc.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++++ > > drivers/acpi/acpi_soc.h | 98 +++++++++++++++++++++ > > 3 files changed, 323 insertions(+), 1 deletion(-) > > create mode 100644 drivers/acpi/acpi_soc.c > > create mode 100644 drivers/acpi/acpi_soc.h > > > > diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile > > index c3b2fcb..ae3397d 100644 > > --- a/drivers/acpi/Makefile > > +++ b/drivers/acpi/Makefile > > @@ -40,7 +40,7 @@ acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o > > acpi-y += ec.o > > acpi-$(CONFIG_ACPI_DOCK) += dock.o > > acpi-y += pci_root.o pci_link.o pci_irq.o > > -acpi-y += acpi_lpss.o > > +acpi-y += acpi_soc.o acpi_lpss.o > > acpi-y += acpi_platform.o > > acpi-y += acpi_pnp.o > > acpi-y += int340x_thermal.o > > diff --git a/drivers/acpi/acpi_soc.c b/drivers/acpi/acpi_soc.c > > new file mode 100644 > > index 0000000..46901d5 > > --- /dev/null > > +++ b/drivers/acpi/acpi_soc.c > > @@ -0,0 +1,224 @@ > > +/* > > + * ACPI SOC support for Intel Lynxpoint LPSS and AMD APD. > > + * > > + * Copyright (C) 2015, Intel Corporation & AMD Corporation > > 2015? Wait couple of weeks :-) > Moreover, Intel code is copyrighted starting from 2013. I think it > would be better to keep two lines, one is original from Intel and one > from AMD. > [ken]ok. > > + * Authors: Ken Xue <Ken.Xue@amd.com> > > + * Mika Westerberg <mika.westerberg@linux.intel.com> > > + * Rafael J. Wysocki <rafael.j.wysocki@intel.com> > > + * > > + * This program is free software; you can redistribute it and/or modify > > + * it under the terms of the GNU General Public License version 2 as > > + * published by the Free Software Foundation. > > + */ > > + > > +#include <linux/err.h> > > +#include <linux/list.h> > > +#include <linux/pm_domain.h> > > +#include <linux/platform_device.h> > > + > > +#include "acpi_soc.h" > > +#include "internal.h" > > + > > +ACPI_MODULE_NAME("acpi_soc"); > > + > > +/* A list for all acpi soc device */ > > +static LIST_HEAD(a_soc_list); > > + > > +static int is_memory(struct acpi_resource *res, void *not_used) > > +{ > > + struct resource r; > > + > > + return !acpi_dev_resource_memory(res, &r); > > +} > > + > > +static int acpi_soc_create_device(struct acpi_device *adev, > > + const struct acpi_device_id *id) > > +{ > > + struct acpi_soc_dev_desc *dev_desc; > > + struct acpi_soc_dev_private_data *pdata; > > + struct resource_list_entry *rentry; > > + struct list_head resource_list; > > + struct platform_device *pdev; > > + int ret; > > + > > + dev_desc = (struct acpi_soc_dev_desc *)id->driver_data; > > + if (!dev_desc) { > > + pdev = acpi_create_platform_device(adev); > > + return IS_ERR_OR_NULL(pdev) ? PTR_ERR(pdev) : 1; > > + } > > + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); > > + if (!pdata) > > + return -ENOMEM; > > + > > + INIT_LIST_HEAD(&resource_list); > > + ret = acpi_dev_get_resources(adev, &resource_list, is_memory, NULL); > > + if (ret < 0) > > + goto err_out; > > + > > + list_for_each_entry(rentry, &resource_list, node) > > + if (resource_type(&rentry->res) == IORESOURCE_MEM) { > > + if (dev_desc->mem_size_override) > > + pdata->mmio_size = dev_desc->mem_size_override; > > + else > > + pdata->mmio_size = resource_size(&rentry->res); > > + pdata->mmio_base = ioremap(rentry->res.start, > > + pdata->mmio_size); > > + break; > > + } > > + > > + acpi_dev_free_resource_list(&resource_list); > > + > > + pdata->adev = adev; > > + pdata->dev_desc = dev_desc; > > + > > + if (dev_desc->setup) { > > + ret = dev_desc->setup(pdata); > > + if (ret) > > + goto err_out; > > + } > > + > > + /* > > + * This works around a known issue in ACPI tables where acpi soc devices > > + * have _PS0 and _PS3 without _PSC (and no power resources), so > > + * acpi_bus_init_power() will assume that the BIOS has put them into D0. > > + */ > > + ret = acpi_device_fix_up_power(adev); > > + if (ret) { > > + /* Skip the device, but continue the namespace scan. */ > > + ret = 0; > > + goto err_out; > > + } > > + > > + adev->driver_data = pdata; > > + pdev = acpi_create_platform_device(adev); > > + if (!IS_ERR_OR_NULL(pdev)) > > + return 1; > > + > > + ret = PTR_ERR(pdev); > > + adev->driver_data = NULL; > > + > > + err_out: > > + kfree(pdata); > > + return ret; > > +} > > + > > +static int acpi_soc_platform_notify(struct notifier_block *nb, > > + unsigned long action, void *data) > > +{ > > + struct platform_device *pdev = to_platform_device(data); > > + struct acpi_soc_dev_private_data *pdata; > > + struct acpi_device *adev; > > + struct acpi_soc *a_soc_entry; > > + const struct acpi_device_id *id = NULL; > > + > > + list_for_each_entry(a_soc_entry, &a_soc_list, list) { > > + id = acpi_match_device(a_soc_entry->ids, &pdev->dev); > > + if (id) > > + break; > > + } > > + > > + if (!id || !id->driver_data) > > + return 0; > > + > > + if (acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev)) > > + return 0; > > + > > + pdata = acpi_driver_data(adev); > > + if (!pdata || !pdata->mmio_base) > > + return 0; > > + > > + switch (action) { > > + case BUS_NOTIFY_BOUND_DRIVER: > > + if ((pdata->dev_desc->flags & ACPI_SOC_PM)) { > > No need to have double parentheses here and below in the other conditions. > > It seems you took an old code here. We used to have a nasty bug which > was fixed recently, namely by cb39dcdd4ef6, 01ac170ba29a, and > 6c17ee44d524. > Please, take most recent version from linux-pm tree. > [Ken] i checked your latest patches. About 'proxy' device, it looks like a WA and it is hard to implement in acpi soc elegantly. do you have any ideal? > > + if (a_soc_entry->pm_domain) > > + pdev->dev.pm_domain = a_soc_entry->pm_domain; > > + else if (pdata->dev_desc->flags & ACPI_SOC_PM_ON) > > + dev_pm_domain_attach(&pdev->dev, true); > > + else > > + dev_pm_domain_attach(&pdev->dev, false); > > + } > > + break; > > + case BUS_NOTIFY_UNBOUND_DRIVER: > > + if ((pdata->dev_desc->flags & ACPI_SOC_PM)) { > > + if (a_soc_entry->pm_domain) > > + pdev->dev.pm_domain = a_soc_entry->pm_domain; > > + else if (pdata->dev_desc->flags & ACPI_SOC_PM_ON) > > + dev_pm_domain_detach(&pdev->dev, true); > > + else > > + dev_pm_domain_detach(&pdev->dev, false); > > + } > > + break; > > + case BUS_NOTIFY_ADD_DEVICE: > > + if ((pdata->dev_desc->flags & ACPI_SOC_SYSFS) > > + && a_soc_entry->attr_group) > > Unnecessary parentheses. Could it be one line? > > > + sysfs_create_group(&pdev->dev.kobj, > > + a_soc_entry->attr_group); > > + break; > > + case BUS_NOTIFY_DEL_DEVICE: > > + if ((pdata->dev_desc->flags & ACPI_SOC_SYSFS) > > + && a_soc_entry->attr_group) > > Ditto. [Ken]i want to make logic clear with parentheses instead of priority. and it will be safe for extending conditions. > > + sysfs_remove_group(&pdev->dev.kobj, > > + a_soc_entry->attr_group); > > + break; > > + } > > + > > + return 0; > > +} > > + > > +static struct notifier_block acpi_soc_nb = { > > + .notifier_call = acpi_soc_platform_notify, > > +}; > > + > > +static void acpi_soc_bind(struct device *dev) > > +{ > > + struct acpi_soc_dev_private_data *pdata; > > + > > + pdata = acpi_driver_data(ACPI_COMPANION(dev)); > > + > > + if (!pdata || !pdata->dev_desc || !pdata->dev_desc->bind) > > + return; > > + > > + pdata->dev_desc->bind(pdata, dev); > > +} > > + > > +static void acpi_soc_unbind(struct device *dev) > > +{ > > + struct acpi_soc_dev_private_data *pdata; > > + > > + pdata = acpi_driver_data(ACPI_COMPANION(dev)); > > + > > + if (!pdata || !pdata->dev_desc || !pdata->dev_desc->unbind) > > + return; > > + > > + pdata->dev_desc->unbind(pdata, dev); > > +} > > + > > +/** > > + * register_acpi_soc - register a new acpi soc > > + * @a_soc: acpi soc > > + * @disable_scan_handler: true means remove default scan handle > > + * false means use default scan handle > > + * > > + * register a new acpi soc into asoc_list and install default scan handle. > > + */ > > +void register_acpi_soc(struct acpi_soc *a_soc, bool disable_scan_handler) > > +{ > > + static int init; > > + struct acpi_scan_handler *acpi_soc_handler; > > + > > + INIT_LIST_HEAD(&a_soc->list); > > + list_add(&a_soc->list, &a_soc_list); > > + > > + acpi_soc_handler = kzalloc(sizeof(*acpi_soc_handler), GFP_KERNEL); > > + acpi_soc_handler->ids = a_soc->ids; > > + if (!disable_scan_handler) { > > + acpi_soc_handler->attach = acpi_soc_create_device; > > + acpi_soc_handler->bind = acpi_soc_bind; > > + acpi_soc_handler->unbind = acpi_soc_unbind; > > + if (init == 0) { > > + init++; > > + bus_register_notifier(&platform_bus_type, &acpi_soc_nb); > > + } > > + } > > + acpi_scan_add_handler(acpi_soc_handler); > > +} > > diff --git a/drivers/acpi/acpi_soc.h b/drivers/acpi/acpi_soc.h > > new file mode 100644 > > index 0000000..bada2a1 > > --- /dev/null > > +++ b/drivers/acpi/acpi_soc.h > > @@ -0,0 +1,98 @@ > > +/* > > + * ACPI SOC support for Intel Lynxpoint LPSS and AMD APD. > > + * > > + * Copyright (C) 2015, Intel Corporation & AMD Corporation > > + * Authors: Ken Xue <Ken.Xue@amd.com> > > + * Mika Westerberg <mika.westerberg@linux.intel.com> > > + * Rafael J. Wysocki <rafael.j.wysocki@intel.com> > > + * > > + * This program is free software; you can redistribute it and/or modify > > + * it under the terms of the GNU General Public License version 2 as > > + * published by the Free Software Foundation. > > + */ > > +#ifndef _ACPI_SOC_H > > +#define _ACPI_SOC_H > > + > > +#include <linux/acpi.h> > > +#include <linux/clk.h> > > +#include <linux/pm.h> > > + > > +struct acpi_soc_dev_private_data; > > + > > +/** > > + * struct acpi_soc - acpi soc > > + * @list: list head > > + * @ids: all acpi device ids for acpi soc > > + * @pm_domain: power domain for all acpi device;can be NULL > > + * @attr_group: attribute group for sysfs support of acpi soc;can be NULL > > + */ > > +struct acpi_soc { > > + struct list_head list; > > + struct acpi_device_id *ids; > > + struct dev_pm_domain *pm_domain; > > + struct attribute_group *attr_group; > > +}; > > + > > + > > +/** > > + * device flags of acpi_soc_dev_desc. > > + * bit 16 to 31 reserved for acpi soc. > > + * bit 0 ~15 reserved for private flags. > > + * ACPI_SOC_SYSFS : add device attributes in sysfs > > + * ACPI_SOC_PM : attach power domain to device > > + * ACPI_SOC_PM_ON : power on device when attach power domain > > + */ > > +#define ACPI_SOC_SYSFS BIT(16) > > +#define ACPI_SOC_PM BIT(17) > > +#define ACPI_SOC_PM_ON BIT(18) > > + > > +/** > > + * struct acpi_soc_dev_desc - a descriptor for acpi device > > + * @flags: device flags like ACPI_SOC_SYSFS ACPI_SOC_PM ACPI_SOC_PM_ON > > + * @clk: clock device > > + * @fixed_clk_rate: fixed rate input clock source for acpi device; > > + * 0 means no fixed rate input clock source > > + * @mem_size_override: a workaround for override device memsize; > > + * 0 means no needs for this WA > > + * @prv_offset: reg offest of lpss features > > + * @setup: a hook routine to set device resource during create platform device > > + * @bind: a hook of acpi_scan_handler.bind > > + * @unbind: a hook of acpi_scan_handler.unbind > > + * > > + * device description defined as acpi_device_id.driver_data > > + */ > > +struct acpi_soc_dev_desc { > > + unsigned int flags; > > + struct clk *clk; > > + unsigned int fixed_clk_rate; > > + size_t mem_size_override; > > + unsigned int prv_offset; > > + int (*setup)(struct acpi_soc_dev_private_data *pdata); > > + void (*bind)(struct acpi_soc_dev_private_data *pdata, > > + struct device *dev); > > + void (*unbind)(struct acpi_soc_dev_private_data *pdata, > > + struct device *dev); > > +}; > > + > > +#define ACPI_SOC_REG_CONTEXT_MAX 10 > > + > > +/** > > + * struct acpi_soc_dev_private_data - acpi device private data > > + * @mmio_base: virtual memory base addr of the device > > + * @mmio_size: device memory size > > + * @dev_desc: device description > > + * @adev: acpi device > > + * @prv_reg_ctx: reg context for power management > > + */ > > +struct acpi_soc_dev_private_data { > > + void __iomem *mmio_base; > > + resource_size_t mmio_size; > > + > > + struct acpi_soc_dev_desc *dev_desc; > > + struct acpi_device *adev; > > + u32 prv_reg_ctx[ACPI_SOC_REG_CONTEXT_MAX]; > > +}; > > + > > +void register_acpi_soc(struct acpi_soc *a_soc, bool disable_scan_handler); > > + > > +#endif > > -- > > 1.9.1 > > > > -- > > To unsubscribe from this list: send the line "unsubscribe linux-kernel" in > > the body of a message to majordomo@vger.kernel.org > > More majordomo info at http://vger.kernel.org/majordomo-info.html > > Please read the FAQ at http://www.tux.org/lkml/ > > > -- To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On Tue, 2014-12-16 at 12:01 +0200, Mika Westerberg wrote: > On Thu, Dec 11, 2014 at 11:04:49AM +0800, Ken Xue wrote: > > This patch is supposed to deliver some common codes for AMD APD and > > INTEL LPSS. It can help to convert some specific acpi devices to be > > INTEL -> Intel, acpi -> ACPI > [ken]ok. > > platform devices. > > > > Signed-off-by: Ken Xue <Ken.Xue@amd.com> > > --- > > drivers/acpi/Makefile | 2 +- > > drivers/acpi/acpi_soc.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++++ > > drivers/acpi/acpi_soc.h | 98 +++++++++++++++++++++ > > 3 files changed, 323 insertions(+), 1 deletion(-) > > create mode 100644 drivers/acpi/acpi_soc.c > > create mode 100644 drivers/acpi/acpi_soc.h > > > > diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile > > index c3b2fcb..ae3397d 100644 > > --- a/drivers/acpi/Makefile > > +++ b/drivers/acpi/Makefile > > @@ -40,7 +40,7 @@ acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o > > acpi-y += ec.o > > acpi-$(CONFIG_ACPI_DOCK) += dock.o > > acpi-y += pci_root.o pci_link.o pci_irq.o > > -acpi-y += acpi_lpss.o > > +acpi-y += acpi_soc.o acpi_lpss.o > > acpi-y += acpi_platform.o > > acpi-y += acpi_pnp.o > > acpi-y += int340x_thermal.o > > diff --git a/drivers/acpi/acpi_soc.c b/drivers/acpi/acpi_soc.c > > new file mode 100644 > > index 0000000..46901d5 > > --- /dev/null > > +++ b/drivers/acpi/acpi_soc.c > > @@ -0,0 +1,224 @@ > > +/* > > + * ACPI SOC support for Intel Lynxpoint LPSS and AMD APD. > > + * > > + * Copyright (C) 2015, Intel Corporation & AMD Corporation > > + * Authors: Ken Xue <Ken.Xue@amd.com> > > + * Mika Westerberg <mika.westerberg@linux.intel.com> > > + * Rafael J. Wysocki <rafael.j.wysocki@intel.com> > > + * > > + * This program is free software; you can redistribute it and/or modify > > + * it under the terms of the GNU General Public License version 2 as > > + * published by the Free Software Foundation. > > + */ > > + > > +#include <linux/err.h> > > +#include <linux/list.h> > > +#include <linux/pm_domain.h> > > +#include <linux/platform_device.h> > > + > > +#include "acpi_soc.h" > > +#include "internal.h" > > + > > +ACPI_MODULE_NAME("acpi_soc"); > > + > > +/* A list for all acpi soc device */ > > +static LIST_HEAD(a_soc_list); > > + > > +static int is_memory(struct acpi_resource *res, void *not_used) > > +{ > > + struct resource r; > > + > > + return !acpi_dev_resource_memory(res, &r); > > +} > > + > > +static int acpi_soc_create_device(struct acpi_device *adev, > > + const struct acpi_device_id *id) > > +{ > > + struct acpi_soc_dev_desc *dev_desc; > > + struct acpi_soc_dev_private_data *pdata; > > + struct resource_list_entry *rentry; > > + struct list_head resource_list; > > + struct platform_device *pdev; > > + int ret; > > + > > + dev_desc = (struct acpi_soc_dev_desc *)id->driver_data; > > + if (!dev_desc) { > > + pdev = acpi_create_platform_device(adev); > > + return IS_ERR_OR_NULL(pdev) ? PTR_ERR(pdev) : 1; > > + } > > + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); > > + if (!pdata) > > + return -ENOMEM; > > + > > + INIT_LIST_HEAD(&resource_list); > > + ret = acpi_dev_get_resources(adev, &resource_list, is_memory, NULL); > > + if (ret < 0) > > + goto err_out; > > + > > + list_for_each_entry(rentry, &resource_list, node) > > + if (resource_type(&rentry->res) == IORESOURCE_MEM) { > > + if (dev_desc->mem_size_override) > > + pdata->mmio_size = dev_desc->mem_size_override; > > + else > > + pdata->mmio_size = resource_size(&rentry->res); > > + pdata->mmio_base = ioremap(rentry->res.start, > > + pdata->mmio_size); > > + break; > > + } > > + > > + acpi_dev_free_resource_list(&resource_list); > > + > > + pdata->adev = adev; > > + pdata->dev_desc = dev_desc; > > + > > + if (dev_desc->setup) { > > + ret = dev_desc->setup(pdata); > > + if (ret) > > + goto err_out; > > + } > > + > > + /* > > + * This works around a known issue in ACPI tables where acpi soc devices > > acpi soc -> ACPI SoC > > Please use these consistently. > [ken]got it. > > + * have _PS0 and _PS3 without _PSC (and no power resources), so > > + * acpi_bus_init_power() will assume that the BIOS has put them into D0. > > + */ > > + ret = acpi_device_fix_up_power(adev); > > + if (ret) { > > + /* Skip the device, but continue the namespace scan. */ > > + ret = 0; > > + goto err_out; > > + } > > + > > + adev->driver_data = pdata; > > + pdev = acpi_create_platform_device(adev); > > + if (!IS_ERR_OR_NULL(pdev)) > > + return 1; > > + > > + ret = PTR_ERR(pdev); > > + adev->driver_data = NULL; > > + > > + err_out: > > + kfree(pdata); > > + return ret; > > +} > > + > > +static int acpi_soc_platform_notify(struct notifier_block *nb, > > + unsigned long action, void *data) > > +{ > > + struct platform_device *pdev = to_platform_device(data); > > + struct acpi_soc_dev_private_data *pdata; > > + struct acpi_device *adev; > > + struct acpi_soc *a_soc_entry; > > + const struct acpi_device_id *id = NULL; > > + > > + list_for_each_entry(a_soc_entry, &a_soc_list, list) { > > + id = acpi_match_device(a_soc_entry->ids, &pdev->dev); > > + if (id) > > + break; > > + } > > + > > + if (!id || !id->driver_data) > > + return 0; > > + > > + if (acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev)) > > + return 0; > > + > > + pdata = acpi_driver_data(adev); > > + if (!pdata || !pdata->mmio_base) > > + return 0; > > + > > + switch (action) { > > + case BUS_NOTIFY_BOUND_DRIVER: > > + if ((pdata->dev_desc->flags & ACPI_SOC_PM)) { > > + if (a_soc_entry->pm_domain) > > + pdev->dev.pm_domain = a_soc_entry->pm_domain; > > + else if (pdata->dev_desc->flags & ACPI_SOC_PM_ON) > > + dev_pm_domain_attach(&pdev->dev, true); > > Too much indent. > > > + else > > + dev_pm_domain_attach(&pdev->dev, false); > > Ditto. > > > + } > > + break; > > + case BUS_NOTIFY_UNBOUND_DRIVER: > > + if ((pdata->dev_desc->flags & ACPI_SOC_PM)) { > > + if (a_soc_entry->pm_domain) > > + pdev->dev.pm_domain = a_soc_entry->pm_domain; > > + else if (pdata->dev_desc->flags & ACPI_SOC_PM_ON) > > + dev_pm_domain_detach(&pdev->dev, true); > > Ditto. > > > + else > > + dev_pm_domain_detach(&pdev->dev, false); > > Ditto > [ken]got it. > > + } > > + break; > > + case BUS_NOTIFY_ADD_DEVICE: > > + if ((pdata->dev_desc->flags & ACPI_SOC_SYSFS) > > + && a_soc_entry->attr_group) > > + sysfs_create_group(&pdev->dev.kobj, > > + a_soc_entry->attr_group); > > + break; > > + case BUS_NOTIFY_DEL_DEVICE: > > + if ((pdata->dev_desc->flags & ACPI_SOC_SYSFS) > > + && a_soc_entry->attr_group) > > + sysfs_remove_group(&pdev->dev.kobj, > > + a_soc_entry->attr_group); > > + break; > > + } > > + > > + return 0; > > +} > > + > > +static struct notifier_block acpi_soc_nb = { > > + .notifier_call = acpi_soc_platform_notify, > > +}; > > + > > +static void acpi_soc_bind(struct device *dev) > > +{ > > + struct acpi_soc_dev_private_data *pdata; > > + > > + pdata = acpi_driver_data(ACPI_COMPANION(dev)); > > + > > + if (!pdata || !pdata->dev_desc || !pdata->dev_desc->bind) > > + return; > > + > > + pdata->dev_desc->bind(pdata, dev); > > +} > > + > > +static void acpi_soc_unbind(struct device *dev) > > +{ > > + struct acpi_soc_dev_private_data *pdata; > > + > > + pdata = acpi_driver_data(ACPI_COMPANION(dev)); > > + > > + if (!pdata || !pdata->dev_desc || !pdata->dev_desc->unbind) > > + return; > > + > > + pdata->dev_desc->unbind(pdata, dev); > > +} > > + > > +/** > > + * register_acpi_soc - register a new acpi soc > > + * @a_soc: acpi soc > > + * @disable_scan_handler: true means remove default scan handle > > + * false means use default scan handle > > + * > > + * register a new acpi soc into asoc_list and install default scan handle. > > + */ > > +void register_acpi_soc(struct acpi_soc *a_soc, bool disable_scan_handler) > > I still think passing scan handler here is better. Up to Rafael to > decide. [ken]as i have described, i add some hooks in acpi_soc_dev_desc like "setup" "bind" "unbind".both Mika's approach and mine can be used for different implementation of platform. > > +{ > > + static int init; > > + struct acpi_scan_handler *acpi_soc_handler; > > + > > + INIT_LIST_HEAD(&a_soc->list); > > + list_add(&a_soc->list, &a_soc_list); > > + > > + acpi_soc_handler = kzalloc(sizeof(*acpi_soc_handler), GFP_KERNEL); > > + acpi_soc_handler->ids = a_soc->ids; > > + if (!disable_scan_handler) { > > + acpi_soc_handler->attach = acpi_soc_create_device; > > + acpi_soc_handler->bind = acpi_soc_bind; > > + acpi_soc_handler->unbind = acpi_soc_unbind; > > + if (init == 0) { > > + init++; > > + bus_register_notifier(&platform_bus_type, &acpi_soc_nb); > > + } > > + } > > + acpi_scan_add_handler(acpi_soc_handler); > > +} > > diff --git a/drivers/acpi/acpi_soc.h b/drivers/acpi/acpi_soc.h > > new file mode 100644 > > index 0000000..bada2a1 > > --- /dev/null > > +++ b/drivers/acpi/acpi_soc.h > > @@ -0,0 +1,98 @@ > > +/* > > + * ACPI SOC support for Intel Lynxpoint LPSS and AMD APD. > > + * > > + * Copyright (C) 2015, Intel Corporation & AMD Corporation > > + * Authors: Ken Xue <Ken.Xue@amd.com> > > + * Mika Westerberg <mika.westerberg@linux.intel.com> > > + * Rafael J. Wysocki <rafael.j.wysocki@intel.com> > > + * > > + * This program is free software; you can redistribute it and/or modify > > + * it under the terms of the GNU General Public License version 2 as > > + * published by the Free Software Foundation. > > + */ > > +#ifndef _ACPI_SOC_H > > +#define _ACPI_SOC_H > > + > > +#include <linux/acpi.h> > > +#include <linux/clk.h> > > +#include <linux/pm.h> > > + > > +struct acpi_soc_dev_private_data; > > + > > +/** > > + * struct acpi_soc - acpi soc > > + * @list: list head > > + * @ids: all acpi device ids for acpi soc > > + * @pm_domain: power domain for all acpi device;can be NULL > > + * @attr_group: attribute group for sysfs support of acpi soc;can be NULL > > + */ > > +struct acpi_soc { > > + struct list_head list; > > + struct acpi_device_id *ids; > > + struct dev_pm_domain *pm_domain; > > + struct attribute_group *attr_group; > > +}; > > + > > + > > +/** > > + * device flags of acpi_soc_dev_desc. > > + * bit 16 to 31 reserved for acpi soc. > > + * bit 0 ~15 reserved for private flags. > > + * ACPI_SOC_SYSFS : add device attributes in sysfs > > + * ACPI_SOC_PM : attach power domain to device > > + * ACPI_SOC_PM_ON : power on device when attach power domain > > + */ > > +#define ACPI_SOC_SYSFS BIT(16) > > +#define ACPI_SOC_PM BIT(17) > > +#define ACPI_SOC_PM_ON BIT(18) > > + > > +/** > > + * struct acpi_soc_dev_desc - a descriptor for acpi device > > + * @flags: device flags like ACPI_SOC_SYSFS ACPI_SOC_PM ACPI_SOC_PM_ON > > + * @clk: clock device > > + * @fixed_clk_rate: fixed rate input clock source for acpi device; > > + * 0 means no fixed rate input clock source > > + * @mem_size_override: a workaround for override device memsize; > > + * 0 means no needs for this WA > > + * @prv_offset: reg offest of lpss features > > + * @setup: a hook routine to set device resource during create platform device > > + * @bind: a hook of acpi_scan_handler.bind > > + * @unbind: a hook of acpi_scan_handler.unbind > > + * > > + * device description defined as acpi_device_id.driver_data > > + */ > > +struct acpi_soc_dev_desc { > > + unsigned int flags; > > + struct clk *clk; > > + unsigned int fixed_clk_rate; > > + size_t mem_size_override; > > + unsigned int prv_offset; > > + int (*setup)(struct acpi_soc_dev_private_data *pdata); > > + void (*bind)(struct acpi_soc_dev_private_data *pdata, > > + struct device *dev); > > + void (*unbind)(struct acpi_soc_dev_private_data *pdata, > > + struct device *dev); > > +}; > > + > > +#define ACPI_SOC_REG_CONTEXT_MAX 10 > > + > > +/** > > + * struct acpi_soc_dev_private_data - acpi device private data > > + * @mmio_base: virtual memory base addr of the device > > + * @mmio_size: device memory size > > + * @dev_desc: device description > > + * @adev: acpi device > > + * @prv_reg_ctx: reg context for power management > > + */ > > +struct acpi_soc_dev_private_data { > > + void __iomem *mmio_base; > > + resource_size_t mmio_size; > > + > > + struct acpi_soc_dev_desc *dev_desc; > > + struct acpi_device *adev; > > + u32 prv_reg_ctx[ACPI_SOC_REG_CONTEXT_MAX]; > > +}; > > + > > +void register_acpi_soc(struct acpi_soc *a_soc, bool disable_scan_handler); > > + > > +#endif > > -- > > 1.9.1 -- To unsubscribe from this list: send the line "unsubscribe linux-acpi" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index c3b2fcb..ae3397d 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -40,7 +40,7 @@ acpi-$(CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC) += processor_pdc.o acpi-y += ec.o acpi-$(CONFIG_ACPI_DOCK) += dock.o acpi-y += pci_root.o pci_link.o pci_irq.o -acpi-y += acpi_lpss.o +acpi-y += acpi_soc.o acpi_lpss.o acpi-y += acpi_platform.o acpi-y += acpi_pnp.o acpi-y += int340x_thermal.o diff --git a/drivers/acpi/acpi_soc.c b/drivers/acpi/acpi_soc.c new file mode 100644 index 0000000..46901d5 --- /dev/null +++ b/drivers/acpi/acpi_soc.c @@ -0,0 +1,224 @@ +/* + * ACPI SOC support for Intel Lynxpoint LPSS and AMD APD. + * + * Copyright (C) 2015, Intel Corporation & AMD Corporation + * Authors: Ken Xue <Ken.Xue@amd.com> + * Mika Westerberg <mika.westerberg@linux.intel.com> + * Rafael J. Wysocki <rafael.j.wysocki@intel.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/err.h> +#include <linux/list.h> +#include <linux/pm_domain.h> +#include <linux/platform_device.h> + +#include "acpi_soc.h" +#include "internal.h" + +ACPI_MODULE_NAME("acpi_soc"); + +/* A list for all acpi soc device */ +static LIST_HEAD(a_soc_list); + +static int is_memory(struct acpi_resource *res, void *not_used) +{ + struct resource r; + + return !acpi_dev_resource_memory(res, &r); +} + +static int acpi_soc_create_device(struct acpi_device *adev, + const struct acpi_device_id *id) +{ + struct acpi_soc_dev_desc *dev_desc; + struct acpi_soc_dev_private_data *pdata; + struct resource_list_entry *rentry; + struct list_head resource_list; + struct platform_device *pdev; + int ret; + + dev_desc = (struct acpi_soc_dev_desc *)id->driver_data; + if (!dev_desc) { + pdev = acpi_create_platform_device(adev); + return IS_ERR_OR_NULL(pdev) ? PTR_ERR(pdev) : 1; + } + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + INIT_LIST_HEAD(&resource_list); + ret = acpi_dev_get_resources(adev, &resource_list, is_memory, NULL); + if (ret < 0) + goto err_out; + + list_for_each_entry(rentry, &resource_list, node) + if (resource_type(&rentry->res) == IORESOURCE_MEM) { + if (dev_desc->mem_size_override) + pdata->mmio_size = dev_desc->mem_size_override; + else + pdata->mmio_size = resource_size(&rentry->res); + pdata->mmio_base = ioremap(rentry->res.start, + pdata->mmio_size); + break; + } + + acpi_dev_free_resource_list(&resource_list); + + pdata->adev = adev; + pdata->dev_desc = dev_desc; + + if (dev_desc->setup) { + ret = dev_desc->setup(pdata); + if (ret) + goto err_out; + } + + /* + * This works around a known issue in ACPI tables where acpi soc devices + * have _PS0 and _PS3 without _PSC (and no power resources), so + * acpi_bus_init_power() will assume that the BIOS has put them into D0. + */ + ret = acpi_device_fix_up_power(adev); + if (ret) { + /* Skip the device, but continue the namespace scan. */ + ret = 0; + goto err_out; + } + + adev->driver_data = pdata; + pdev = acpi_create_platform_device(adev); + if (!IS_ERR_OR_NULL(pdev)) + return 1; + + ret = PTR_ERR(pdev); + adev->driver_data = NULL; + + err_out: + kfree(pdata); + return ret; +} + +static int acpi_soc_platform_notify(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct platform_device *pdev = to_platform_device(data); + struct acpi_soc_dev_private_data *pdata; + struct acpi_device *adev; + struct acpi_soc *a_soc_entry; + const struct acpi_device_id *id = NULL; + + list_for_each_entry(a_soc_entry, &a_soc_list, list) { + id = acpi_match_device(a_soc_entry->ids, &pdev->dev); + if (id) + break; + } + + if (!id || !id->driver_data) + return 0; + + if (acpi_bus_get_device(ACPI_HANDLE(&pdev->dev), &adev)) + return 0; + + pdata = acpi_driver_data(adev); + if (!pdata || !pdata->mmio_base) + return 0; + + switch (action) { + case BUS_NOTIFY_BOUND_DRIVER: + if ((pdata->dev_desc->flags & ACPI_SOC_PM)) { + if (a_soc_entry->pm_domain) + pdev->dev.pm_domain = a_soc_entry->pm_domain; + else if (pdata->dev_desc->flags & ACPI_SOC_PM_ON) + dev_pm_domain_attach(&pdev->dev, true); + else + dev_pm_domain_attach(&pdev->dev, false); + } + break; + case BUS_NOTIFY_UNBOUND_DRIVER: + if ((pdata->dev_desc->flags & ACPI_SOC_PM)) { + if (a_soc_entry->pm_domain) + pdev->dev.pm_domain = a_soc_entry->pm_domain; + else if (pdata->dev_desc->flags & ACPI_SOC_PM_ON) + dev_pm_domain_detach(&pdev->dev, true); + else + dev_pm_domain_detach(&pdev->dev, false); + } + break; + case BUS_NOTIFY_ADD_DEVICE: + if ((pdata->dev_desc->flags & ACPI_SOC_SYSFS) + && a_soc_entry->attr_group) + sysfs_create_group(&pdev->dev.kobj, + a_soc_entry->attr_group); + break; + case BUS_NOTIFY_DEL_DEVICE: + if ((pdata->dev_desc->flags & ACPI_SOC_SYSFS) + && a_soc_entry->attr_group) + sysfs_remove_group(&pdev->dev.kobj, + a_soc_entry->attr_group); + break; + } + + return 0; +} + +static struct notifier_block acpi_soc_nb = { + .notifier_call = acpi_soc_platform_notify, +}; + +static void acpi_soc_bind(struct device *dev) +{ + struct acpi_soc_dev_private_data *pdata; + + pdata = acpi_driver_data(ACPI_COMPANION(dev)); + + if (!pdata || !pdata->dev_desc || !pdata->dev_desc->bind) + return; + + pdata->dev_desc->bind(pdata, dev); +} + +static void acpi_soc_unbind(struct device *dev) +{ + struct acpi_soc_dev_private_data *pdata; + + pdata = acpi_driver_data(ACPI_COMPANION(dev)); + + if (!pdata || !pdata->dev_desc || !pdata->dev_desc->unbind) + return; + + pdata->dev_desc->unbind(pdata, dev); +} + +/** + * register_acpi_soc - register a new acpi soc + * @a_soc: acpi soc + * @disable_scan_handler: true means remove default scan handle + * false means use default scan handle + * + * register a new acpi soc into asoc_list and install default scan handle. + */ +void register_acpi_soc(struct acpi_soc *a_soc, bool disable_scan_handler) +{ + static int init; + struct acpi_scan_handler *acpi_soc_handler; + + INIT_LIST_HEAD(&a_soc->list); + list_add(&a_soc->list, &a_soc_list); + + acpi_soc_handler = kzalloc(sizeof(*acpi_soc_handler), GFP_KERNEL); + acpi_soc_handler->ids = a_soc->ids; + if (!disable_scan_handler) { + acpi_soc_handler->attach = acpi_soc_create_device; + acpi_soc_handler->bind = acpi_soc_bind; + acpi_soc_handler->unbind = acpi_soc_unbind; + if (init == 0) { + init++; + bus_register_notifier(&platform_bus_type, &acpi_soc_nb); + } + } + acpi_scan_add_handler(acpi_soc_handler); +} diff --git a/drivers/acpi/acpi_soc.h b/drivers/acpi/acpi_soc.h new file mode 100644 index 0000000..bada2a1 --- /dev/null +++ b/drivers/acpi/acpi_soc.h @@ -0,0 +1,98 @@ +/* + * ACPI SOC support for Intel Lynxpoint LPSS and AMD APD. + * + * Copyright (C) 2015, Intel Corporation & AMD Corporation + * Authors: Ken Xue <Ken.Xue@amd.com> + * Mika Westerberg <mika.westerberg@linux.intel.com> + * Rafael J. Wysocki <rafael.j.wysocki@intel.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef _ACPI_SOC_H +#define _ACPI_SOC_H + +#include <linux/acpi.h> +#include <linux/clk.h> +#include <linux/pm.h> + +struct acpi_soc_dev_private_data; + +/** + * struct acpi_soc - acpi soc + * @list: list head + * @ids: all acpi device ids for acpi soc + * @pm_domain: power domain for all acpi device;can be NULL + * @attr_group: attribute group for sysfs support of acpi soc;can be NULL + */ +struct acpi_soc { + struct list_head list; + struct acpi_device_id *ids; + struct dev_pm_domain *pm_domain; + struct attribute_group *attr_group; +}; + + +/** + * device flags of acpi_soc_dev_desc. + * bit 16 to 31 reserved for acpi soc. + * bit 0 ~15 reserved for private flags. + * ACPI_SOC_SYSFS : add device attributes in sysfs + * ACPI_SOC_PM : attach power domain to device + * ACPI_SOC_PM_ON : power on device when attach power domain + */ +#define ACPI_SOC_SYSFS BIT(16) +#define ACPI_SOC_PM BIT(17) +#define ACPI_SOC_PM_ON BIT(18) + +/** + * struct acpi_soc_dev_desc - a descriptor for acpi device + * @flags: device flags like ACPI_SOC_SYSFS ACPI_SOC_PM ACPI_SOC_PM_ON + * @clk: clock device + * @fixed_clk_rate: fixed rate input clock source for acpi device; + * 0 means no fixed rate input clock source + * @mem_size_override: a workaround for override device memsize; + * 0 means no needs for this WA + * @prv_offset: reg offest of lpss features + * @setup: a hook routine to set device resource during create platform device + * @bind: a hook of acpi_scan_handler.bind + * @unbind: a hook of acpi_scan_handler.unbind + * + * device description defined as acpi_device_id.driver_data + */ +struct acpi_soc_dev_desc { + unsigned int flags; + struct clk *clk; + unsigned int fixed_clk_rate; + size_t mem_size_override; + unsigned int prv_offset; + int (*setup)(struct acpi_soc_dev_private_data *pdata); + void (*bind)(struct acpi_soc_dev_private_data *pdata, + struct device *dev); + void (*unbind)(struct acpi_soc_dev_private_data *pdata, + struct device *dev); +}; + +#define ACPI_SOC_REG_CONTEXT_MAX 10 + +/** + * struct acpi_soc_dev_private_data - acpi device private data + * @mmio_base: virtual memory base addr of the device + * @mmio_size: device memory size + * @dev_desc: device description + * @adev: acpi device + * @prv_reg_ctx: reg context for power management + */ +struct acpi_soc_dev_private_data { + void __iomem *mmio_base; + resource_size_t mmio_size; + + struct acpi_soc_dev_desc *dev_desc; + struct acpi_device *adev; + u32 prv_reg_ctx[ACPI_SOC_REG_CONTEXT_MAX]; +}; + +void register_acpi_soc(struct acpi_soc *a_soc, bool disable_scan_handler); + +#endif
This patch is supposed to deliver some common codes for AMD APD and INTEL LPSS. It can help to convert some specific acpi devices to be platform devices. Signed-off-by: Ken Xue <Ken.Xue@amd.com> --- drivers/acpi/Makefile | 2 +- drivers/acpi/acpi_soc.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/acpi/acpi_soc.h | 98 +++++++++++++++++++++ 3 files changed, 323 insertions(+), 1 deletion(-) create mode 100644 drivers/acpi/acpi_soc.c create mode 100644 drivers/acpi/acpi_soc.h