2014-12-17 23:16:58

by Suthikulpanit, Suravee

[permalink] [raw]
Subject: [RFC PATCH 0/2] Introduce ACPI support for ahci_platform driver

From: Suravee Suthikulpanit <[email protected]>

This patch series introduce ACPI support for non-PCI AHCI platform driver.
Existing ACPI support for AHCI assumes the device controller is a PCI device.

Also, since there is no ACPI _HID/_CID for generic AHCI controller, the driver
could not use them for matching devices. Therefore, this patch introduces a mechanism
for drivers to match devices using ACPI _CLS method.

This patch series is rebased from:

http://git.linaro.org/leg/acpi/acpi.git acpi-5.1-v6

This topic was discussed earlier here (as part of introducing support for
AMD Seattle SATA controller):

http://marc.info/?l=linux-arm-kernel&m=141083492521584&w=2

Suravee Suthikulpanit (2):
ACPI / scan: Add support for ACPI _CLS device matching
ata: ahci_platform: Add ACPI _CLS matching

drivers/acpi/scan.c | 73 +++++++++++++++++++++++++++++++++++++++++
drivers/ata/Kconfig | 2 +-
drivers/ata/ahci_platform.c | 10 ++++++
include/acpi/acnames.h | 1 +
include/linux/acpi.h | 12 ++++++-
include/linux/device.h | 1 +
include/linux/mod_devicetable.h | 6 ++++
7 files changed, 103 insertions(+), 2 deletions(-)

--
1.9.3


2014-12-17 23:17:05

by Suthikulpanit, Suravee

[permalink] [raw]
Subject: [RFC PATCH 1/2] ACPI / scan: Add support for ACPI _CLS device matching

From: Suravee Suthikulpanit <[email protected]>

Device drivers typically use ACPI _HIDs/_CIDs listed in struct device_driver
acpi_match_table to match devices. However, for generic drivers, we do
not want to list _HID for all supported devices, and some device classes
do not have _CID (e.g. SATA, USB). Instead, we can leverage ACPI _CLS,
which specifies PCI-defined class code (i.e. base-class, subclass and
programming interface).

This patch adds support for matching ACPI devices using the _CLS method.

Signed-off-by: Suravee Suthikulpanit <[email protected]>
---
drivers/acpi/scan.c | 73 +++++++++++++++++++++++++++++++++++++++++
include/acpi/acnames.h | 1 +
include/linux/acpi.h | 12 ++++++-
include/linux/device.h | 1 +
include/linux/mod_devicetable.h | 6 ++++
5 files changed, 92 insertions(+), 1 deletion(-)

diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index d670158..6406648 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -864,6 +864,79 @@ int acpi_match_device_ids(struct acpi_device *device,
}
EXPORT_SYMBOL(acpi_match_device_ids);

+/**
+ * acpi_match_device_cls - Match a struct device against a ACPI _CLS method
+ * @dev_cls: A pointer to struct acpi_device_cls object to match against.
+ * @dev: The ACPI device structure to match.
+ *
+ * Check if @dev has a valid ACPI and _CLS handle. If there is a
+ * struct acpi_device_cls object for that handle, use that object to match
+ * against the given struct acpi_device_cls object.
+ *
+ * Return 0 on success or error code on failure.
+ */
+int acpi_match_device_cls(const struct acpi_device_cls *dev_cls,
+ const struct device *dev)
+{
+ int ret = -EINVAL;
+ acpi_status status;
+ struct acpi_device *adev;
+ union acpi_object *pkg;
+ struct acpi_device_cls cls;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+ struct acpi_buffer format = { sizeof("NNN"), "NNN" };
+ struct acpi_buffer state = { 0, NULL };
+ acpi_handle handle = ACPI_HANDLE(dev);
+ acpi_handle cls_handle;
+
+ if (!handle || acpi_bus_get_device(handle, &adev))
+ return ret;
+
+ if (!adev->status.present || !dev_cls)
+ return ret;
+
+ status = acpi_get_handle(adev->handle, METHOD_NAME__CLS, &cls_handle);
+ if (ACPI_FAILURE(status))
+ return ret;
+
+ status = acpi_evaluate_object(cls_handle, "_CLS", NULL, &buffer);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status, "Failed to Evaluat _CLS"));
+ return ret;
+ }
+
+ /**
+ * Note:
+ * ACPIv5.1 defines the package to contain 3 integers for
+ * Base-Class code, Sub-Class code, and Programming Interface code.
+ */
+ pkg = buffer.pointer;
+ if (!pkg ||
+ (pkg->type != ACPI_TYPE_PACKAGE) ||
+ (pkg->package.count != 3)) {
+ dev_err(&adev->dev, "Invalid _CLS data\n");
+ goto out;
+ }
+
+ state.length = sizeof(struct acpi_device_cls);
+ state.pointer = &cls;
+
+ status = acpi_extract_package(pkg, &format, &state);
+ if (ACPI_FAILURE(status)) {
+ ACPI_EXCEPTION((AE_INFO, status, "Invalid data"));
+ goto out;
+ }
+
+ if ((dev_cls->base_class == cls.base_class) &&
+ (dev_cls->sub_class == cls.sub_class) &&
+ (dev_cls->prog_interface == cls.prog_interface))
+ ret = 0;
+out:
+ kfree(buffer.pointer);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(acpi_match_device_cls);
+
static void acpi_free_power_resources_lists(struct acpi_device *device)
{
int i;
diff --git a/include/acpi/acnames.h b/include/acpi/acnames.h
index 7461327..22332a6 100644
--- a/include/acpi/acnames.h
+++ b/include/acpi/acnames.h
@@ -51,6 +51,7 @@
#define METHOD_NAME__BBN "_BBN"
#define METHOD_NAME__CBA "_CBA"
#define METHOD_NAME__CID "_CID"
+#define METHOD_NAME__CLS "_CLS"
#define METHOD_NAME__CRS "_CRS"
#define METHOD_NAME__DDN "_DDN"
#define METHOD_NAME__HID "_HID"
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 5a92d49..8680afa 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -428,10 +428,14 @@ extern int acpi_nvs_for_each_region(int (*func)(__u64, __u64, void *),
const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids,
const struct device *dev);

+int acpi_match_device_cls(const struct acpi_device_cls *dev_cls,
+ const struct device *dev);
+
static inline bool acpi_driver_match_device(struct device *dev,
const struct device_driver *drv)
{
- return !!acpi_match_device(drv->acpi_match_table, dev);
+ return (!!acpi_match_device(drv->acpi_match_table, dev) ||
+ !acpi_match_device_cls(drv->acpi_cls, dev));
}

int acpi_device_uevent_modalias(struct device *, struct kobj_uevent_env *);
@@ -511,6 +515,12 @@ static inline const struct acpi_device_id *acpi_match_device(
return NULL;
}

+static int acpi_match_device_cls(const struct acpi_device_cls *dev_cls,
+ const struct device *dev)
+{
+ return -EINVAL;
+}
+
static inline bool acpi_driver_match_device(struct device *dev,
const struct device_driver *drv)
{
diff --git a/include/linux/device.h b/include/linux/device.h
index ce1f2160..a469adc 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -237,6 +237,7 @@ struct device_driver {

const struct of_device_id *of_match_table;
const struct acpi_device_id *acpi_match_table;
+ const struct acpi_device_cls *acpi_cls;

int (*probe) (struct device *dev);
int (*remove) (struct device *dev);
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index 44eeef0..f2ef3c6 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -186,6 +186,12 @@ struct css_device_id {

#define ACPI_ID_LEN 9

+struct acpi_device_cls {
+ kernel_ulong_t base_class;
+ kernel_ulong_t sub_class;
+ kernel_ulong_t prog_interface;
+};
+
struct acpi_device_id {
__u8 id[ACPI_ID_LEN];
kernel_ulong_t driver_data;
--
1.9.3

2014-12-17 23:17:03

by Suthikulpanit, Suravee

[permalink] [raw]
Subject: [RFC PATCH 2/2] ata: ahci_platform: Add ACPI _CLS matching

From: Suravee Suthikulpanit <[email protected]>

This patch adds ACPI supports for AHCI platform driver, which uses _CLS
method to match the device.

The following is an example of ASL structure in DSDT for a SATA controller,
which contains _CLS package to be matched by the ahci_platform driver:

Device (AHC0) // AHCI Controller
{
Name(_HID, "AMDI0600")
Name (_CCA, 1)
Name (_CLS, Package (3)
{
0x01, // Base Class: Mass Storage
0x06, // Sub-Class: serial ATA
0x01, // Interface: AHCI
})
Name (_CRS, ResourceTemplate ()
{
Memory32Fixed (ReadWrite, 0xE0300000, 0x00010000)
Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive,,,) { 387 }
})
}

Also, since ATA driver should not require PCI support for ATA_ACPI,
this patch removes dependency in the driver/ata/Kconfig.

Signed-off-by: Suravee Suthikulpanit <[email protected]>
---
drivers/ata/Kconfig | 2 +-
drivers/ata/ahci_platform.c | 10 ++++++++++
2 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index cd4cccb..edb00c6 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -48,7 +48,7 @@ config ATA_VERBOSE_ERROR

config ATA_ACPI
bool "ATA ACPI Support"
- depends on ACPI && PCI
+ depends on ACPI
default y
help
This option adds support for ATA-related ACPI objects.
diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
index 06f1d59..54568ec 100644
--- a/drivers/ata/ahci_platform.c
+++ b/drivers/ata/ahci_platform.c
@@ -20,6 +20,9 @@
#include <linux/platform_device.h>
#include <linux/libata.h>
#include <linux/ahci_platform.h>
+#ifdef CONFIG_ATA_ACPI
+#include <linux/acpi.h>
+#endif
#include "ahci.h"

static const struct ata_port_info ahci_port_info = {
@@ -71,6 +74,10 @@ static const struct of_device_id ahci_of_match[] = {
};
MODULE_DEVICE_TABLE(of, ahci_of_match);

+#ifdef CONFIG_ATA_ACPI
+static const struct acpi_device_cls ahci_cls = {0x01, 0x06, 0x01};
+#endif
+
static struct platform_driver ahci_driver = {
.probe = ahci_probe,
.remove = ata_platform_remove_one,
@@ -78,6 +85,9 @@ static struct platform_driver ahci_driver = {
.name = "ahci",
.owner = THIS_MODULE,
.of_match_table = ahci_of_match,
+#ifdef CONFIG_ATA_ACPI
+ .acpi_cls = &ahci_cls,
+#endif
.pm = &ahci_pm_ops,
},
};
--
1.9.3

2014-12-18 09:03:13

by Hans de Goede

[permalink] [raw]
Subject: Re: [RFC PATCH 2/2] ata: ahci_platform: Add ACPI _CLS matching

Hi,

On 18-12-14 00:16, Suravee Suthikulpanit wrote:
> From: Suravee Suthikulpanit <[email protected]>
>
> This patch adds ACPI supports for AHCI platform driver, which uses _CLS
> method to match the device.
>
> The following is an example of ASL structure in DSDT for a SATA controller,
> which contains _CLS package to be matched by the ahci_platform driver:
>
> Device (AHC0) // AHCI Controller
> {
> Name(_HID, "AMDI0600")
> Name (_CCA, 1)
> Name (_CLS, Package (3)
> {
> 0x01, // Base Class: Mass Storage
> 0x06, // Sub-Class: serial ATA
> 0x01, // Interface: AHCI
> })
> Name (_CRS, ResourceTemplate ()
> {
> Memory32Fixed (ReadWrite, 0xE0300000, 0x00010000)
> Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive,,,) { 387 }
> })
> }
>
> Also, since ATA driver should not require PCI support for ATA_ACPI,
> this patch removes dependency in the driver/ata/Kconfig.
>
> Signed-off-by: Suravee Suthikulpanit <[email protected]>

The ahci_platform changes done here look good to me:

Acked-by: Hans de Goede <[email protected]>

Regards,

Hans


> ---
> drivers/ata/Kconfig | 2 +-
> drivers/ata/ahci_platform.c | 10 ++++++++++
> 2 files changed, 11 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
> index cd4cccb..edb00c6 100644
> --- a/drivers/ata/Kconfig
> +++ b/drivers/ata/Kconfig
> @@ -48,7 +48,7 @@ config ATA_VERBOSE_ERROR
>
> config ATA_ACPI
> bool "ATA ACPI Support"
> - depends on ACPI && PCI
> + depends on ACPI
> default y
> help
> This option adds support for ATA-related ACPI objects.
> diff --git a/drivers/ata/ahci_platform.c b/drivers/ata/ahci_platform.c
> index 06f1d59..54568ec 100644
> --- a/drivers/ata/ahci_platform.c
> +++ b/drivers/ata/ahci_platform.c
> @@ -20,6 +20,9 @@
> #include <linux/platform_device.h>
> #include <linux/libata.h>
> #include <linux/ahci_platform.h>
> +#ifdef CONFIG_ATA_ACPI
> +#include <linux/acpi.h>
> +#endif
> #include "ahci.h"
>
> static const struct ata_port_info ahci_port_info = {
> @@ -71,6 +74,10 @@ static const struct of_device_id ahci_of_match[] = {
> };
> MODULE_DEVICE_TABLE(of, ahci_of_match);
>
> +#ifdef CONFIG_ATA_ACPI
> +static const struct acpi_device_cls ahci_cls = {0x01, 0x06, 0x01};
> +#endif
> +
> static struct platform_driver ahci_driver = {
> .probe = ahci_probe,
> .remove = ata_platform_remove_one,
> @@ -78,6 +85,9 @@ static struct platform_driver ahci_driver = {
> .name = "ahci",
> .owner = THIS_MODULE,
> .of_match_table = ahci_of_match,
> +#ifdef CONFIG_ATA_ACPI
> + .acpi_cls = &ahci_cls,
> +#endif
> .pm = &ahci_pm_ops,
> },
> };
>

2014-12-18 09:17:27

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [Linaro-acpi] [RFC PATCH 2/2] ata: ahci_platform: Add ACPI _CLS matching

On Wednesday 17 December 2014 17:16:35 Suravee Suthikulpanit wrote:
> +#ifdef CONFIG_ATA_ACPI
> +#include <linux/acpi.h>
> +#endif
> #include "ahci.h"
>
> static const struct ata_port_info ahci_port_info = {
> @@ -71,6 +74,10 @@ static const struct of_device_id ahci_of_match[] = {
> };
> MODULE_DEVICE_TABLE(of, ahci_of_match);
>
> +#ifdef CONFIG_ATA_ACPI
> +static const struct acpi_device_cls ahci_cls = {0x01, 0x06, 0x01};
> +#endif
> +
> static struct platform_driver ahci_driver = {
> .probe = ahci_probe,
> .remove = ata_platform_remove_one,
> @@ -78,6 +85,9 @@ static struct platform_driver ahci_driver = {
> .name = "ahci",
> .owner = THIS_MODULE,
> .of_match_table = ahci_of_match,
> +#ifdef CONFIG_ATA_ACPI
> + .acpi_cls = &ahci_cls,
> +#endif
> .pm = &ahci_pm_ops,
>

It would be better to leave out the various #ifdef here, in particular the
one around the header file inclusion.

Arnd

2014-12-19 14:57:02

by Hanjun Guo

[permalink] [raw]
Subject: Re: [RFC PATCH 1/2] ACPI / scan: Add support for ACPI _CLS device matching

Hi Suravee,

On 2014年12月18日 07:16, Suravee Suthikulpanit wrote:
> From: Suravee Suthikulpanit <[email protected]>
>
> Device drivers typically use ACPI _HIDs/_CIDs listed in struct device_driver
> acpi_match_table to match devices. However, for generic drivers, we do
> not want to list _HID for all supported devices, and some device classes
> do not have _CID (e.g. SATA, USB). Instead, we can leverage ACPI _CLS,
> which specifies PCI-defined class code (i.e. base-class, subclass and
> programming interface).
>
> This patch adds support for matching ACPI devices using the _CLS method.
>
> Signed-off-by: Suravee Suthikulpanit <[email protected]>
> ---
> drivers/acpi/scan.c | 73 +++++++++++++++++++++++++++++++++++++++++
> include/acpi/acnames.h | 1 +
> include/linux/acpi.h | 12 ++++++-
> include/linux/device.h | 1 +
> include/linux/mod_devicetable.h | 6 ++++
> 5 files changed, 92 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
> index d670158..6406648 100644
> --- a/drivers/acpi/scan.c
> +++ b/drivers/acpi/scan.c
> @@ -864,6 +864,79 @@ int acpi_match_device_ids(struct acpi_device *device,
> }
> EXPORT_SYMBOL(acpi_match_device_ids);
>
> +/**
> + * acpi_match_device_cls - Match a struct device against a ACPI _CLS method
> + * @dev_cls: A pointer to struct acpi_device_cls object to match against.
> + * @dev: The ACPI device structure to match.
> + *
> + * Check if @dev has a valid ACPI and _CLS handle. If there is a
> + * struct acpi_device_cls object for that handle, use that object to match
> + * against the given struct acpi_device_cls object.
> + *
> + * Return 0 on success or error code on failure.
> + */
> +int acpi_match_device_cls(const struct acpi_device_cls *dev_cls,
> + const struct device *dev)
> +{
> + int ret = -EINVAL;
> + acpi_status status;
> + struct acpi_device *adev;
> + union acpi_object *pkg;
> + struct acpi_device_cls cls;
> + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
> + struct acpi_buffer format = { sizeof("NNN"), "NNN" };
> + struct acpi_buffer state = { 0, NULL };
> + acpi_handle handle = ACPI_HANDLE(dev);

...

> + acpi_handle cls_handle;
> +
> + if (!handle || acpi_bus_get_device(handle, &adev))

if handle is not NULL, adev will not NULL too :)
because you get the handle from adev, ACPI_HANDLE() is defined as:
acpi_device_handle(ACPI_COMPANION(dev)), and adev = ACPI_COMPANION(dev);

you may use adev = ACPI_COMPANION(dev) to simplify the code.

> + return ret;
> +
> + if (!adev->status.present || !dev_cls)
> + return ret;
> +
> + status = acpi_get_handle(adev->handle, METHOD_NAME__CLS, &cls_handle);

do we need this function called here? _CLS is the method under ACPI
device object in DSDT/SSDT, and you will get adev->handle == cls_handle
if I'm not wrong :)

> + if (ACPI_FAILURE(status))
> + return ret;
> +
> + status = acpi_evaluate_object(cls_handle, "_CLS", NULL, &buffer);
> + if (ACPI_FAILURE(status)) {
> + ACPI_EXCEPTION((AE_INFO, status, "Failed to Evaluat _CLS"));
> + return ret;
> + }

I think you can evaluate _CLS directly with handle here.

Thanks
Hanjun

> +
> + /**
> + * Note:
> + * ACPIv5.1 defines the package to contain 3 integers for
> + * Base-Class code, Sub-Class code, and Programming Interface code.
> + */
> + pkg = buffer.pointer;
> + if (!pkg ||
> + (pkg->type != ACPI_TYPE_PACKAGE) ||
> + (pkg->package.count != 3)) {
> + dev_err(&adev->dev, "Invalid _CLS data\n");
> + goto out;
> + }
> +
> + state.length = sizeof(struct acpi_device_cls);
> + state.pointer = &cls;
> +
> + status = acpi_extract_package(pkg, &format, &state);
> + if (ACPI_FAILURE(status)) {
> + ACPI_EXCEPTION((AE_INFO, status, "Invalid data"));
> + goto out;
> + }
> +
> + if ((dev_cls->base_class == cls.base_class) &&
> + (dev_cls->sub_class == cls.sub_class) &&
> + (dev_cls->prog_interface == cls.prog_interface))
> + ret = 0;
> +out:
> + kfree(buffer.pointer);
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(acpi_match_device_cls);
> +
> static void acpi_free_power_resources_lists(struct acpi_device *device)
> {
> int i;
> diff --git a/include/acpi/acnames.h b/include/acpi/acnames.h
> index 7461327..22332a6 100644
> --- a/include/acpi/acnames.h
> +++ b/include/acpi/acnames.h
> @@ -51,6 +51,7 @@
> #define METHOD_NAME__BBN "_BBN"
> #define METHOD_NAME__CBA "_CBA"
> #define METHOD_NAME__CID "_CID"
> +#define METHOD_NAME__CLS "_CLS"
> #define METHOD_NAME__CRS "_CRS"
> #define METHOD_NAME__DDN "_DDN"
> #define METHOD_NAME__HID "_HID"
> diff --git a/include/linux/acpi.h b/include/linux/acpi.h
> index 5a92d49..8680afa 100644
> --- a/include/linux/acpi.h
> +++ b/include/linux/acpi.h
> @@ -428,10 +428,14 @@ extern int acpi_nvs_for_each_region(int (*func)(__u64, __u64, void *),
> const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids,
> const struct device *dev);
>
> +int acpi_match_device_cls(const struct acpi_device_cls *dev_cls,
> + const struct device *dev);
> +
> static inline bool acpi_driver_match_device(struct device *dev,
> const struct device_driver *drv)
> {
> - return !!acpi_match_device(drv->acpi_match_table, dev);
> + return (!!acpi_match_device(drv->acpi_match_table, dev) ||
> + !acpi_match_device_cls(drv->acpi_cls, dev));
> }
>
> int acpi_device_uevent_modalias(struct device *, struct kobj_uevent_env *);
> @@ -511,6 +515,12 @@ static inline const struct acpi_device_id *acpi_match_device(
> return NULL;
> }
>
> +static int acpi_match_device_cls(const struct acpi_device_cls *dev_cls,
> + const struct device *dev)
> +{
> + return -EINVAL;
> +}
> +
> static inline bool acpi_driver_match_device(struct device *dev,
> const struct device_driver *drv)
> {
> diff --git a/include/linux/device.h b/include/linux/device.h
> index ce1f2160..a469adc 100644
> --- a/include/linux/device.h
> +++ b/include/linux/device.h
> @@ -237,6 +237,7 @@ struct device_driver {
>
> const struct of_device_id *of_match_table;
> const struct acpi_device_id *acpi_match_table;
> + const struct acpi_device_cls *acpi_cls;
>
> int (*probe) (struct device *dev);
> int (*remove) (struct device *dev);
> diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
> index 44eeef0..f2ef3c6 100644
> --- a/include/linux/mod_devicetable.h
> +++ b/include/linux/mod_devicetable.h
> @@ -186,6 +186,12 @@ struct css_device_id {
>
> #define ACPI_ID_LEN 9
>
> +struct acpi_device_cls {
> + kernel_ulong_t base_class;
> + kernel_ulong_t sub_class;
> + kernel_ulong_t prog_interface;
> +};
> +
> struct acpi_device_id {
> __u8 id[ACPI_ID_LEN];
> kernel_ulong_t driver_data;
>

2014-12-19 18:22:09

by Suthikulpanit, Suravee

[permalink] [raw]
Subject: Re: [Linaro-acpi] [RFC PATCH 2/2] ata: ahci_platform: Add ACPI _CLS matching

On 12/18/2014 3:17 AM, Arnd Bergmann wrote:
> On Wednesday 17 December 2014 17:16:35 Suravee Suthikulpanit wrote:
>> +#ifdef CONFIG_ATA_ACPI
>> +#include <linux/acpi.h>
>> +#endif
>> #include "ahci.h"
>>
>> static const struct ata_port_info ahci_port_info = {
>> @@ -71,6 +74,10 @@ static const struct of_device_id ahci_of_match[] = {
>> };
>> MODULE_DEVICE_TABLE(of, ahci_of_match);
>>
>> +#ifdef CONFIG_ATA_ACPI
>> +static const struct acpi_device_cls ahci_cls = {0x01, 0x06, 0x01};
>> +#endif
>> +
>> static struct platform_driver ahci_driver = {
>> .probe = ahci_probe,
>> .remove = ata_platform_remove_one,
>> @@ -78,6 +85,9 @@ static struct platform_driver ahci_driver = {
>> .name = "ahci",
>> .owner = THIS_MODULE,
>> .of_match_table = ahci_of_match,
>> +#ifdef CONFIG_ATA_ACPI
>> + .acpi_cls = &ahci_cls,
>> +#endif
>> .pm = &ahci_pm_ops,
>>
>
> It would be better to leave out the various #ifdef here, in particular the
> one around the header file inclusion.
>
> Arnd
>

Right. I'll get rid of them. Also, I noticed that I should have declared
acpi_match_device_cls() in include/linux/acpi.h as "inline" for when
building w/o ACPI to avoid warning messages.

Thanks,

Suravee

2014-12-19 19:33:48

by Suthikulpanit, Suravee

[permalink] [raw]
Subject: Re: [RFC PATCH 1/2] ACPI / scan: Add support for ACPI _CLS device matching

On 12/19/2014 12:47 PM, Suravee Suthikulanit wrote:
> On 12/19/2014 8:56 AM, Hanjun Guo wrote:
>> Hi Suravee,
>>
>> On 2014年12月18日 07:16, Suravee Suthikulpanit wrote:
>>> From: Suravee Suthikulpanit <[email protected]>
>>>
>>> Device drivers typically use ACPI _HIDs/_CIDs listed in struct
>>> device_driver
>>> acpi_match_table to match devices. However, for generic drivers, we do
>>> not want to list _HID for all supported devices, and some device classes
>>> do not have _CID (e.g. SATA, USB). Instead, we can leverage ACPI _CLS,
>>> which specifies PCI-defined class code (i.e. base-class, subclass and
>>> programming interface).
>>>
>>> This patch adds support for matching ACPI devices using the _CLS method.
>>>
>>> Signed-off-by: Suravee Suthikulpanit <[email protected]>
>>> ---
>>> drivers/acpi/scan.c | 73
>>> +++++++++++++++++++++++++++++++++++++++++
>>> include/acpi/acnames.h | 1 +
>>> include/linux/acpi.h | 12 ++++++-
>>> include/linux/device.h | 1 +
>>> include/linux/mod_devicetable.h | 6 ++++
>>> 5 files changed, 92 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
>>> index d670158..6406648 100644
>>> --- a/drivers/acpi/scan.c
>>> +++ b/drivers/acpi/scan.c
>>> @@ -864,6 +864,79 @@ int acpi_match_device_ids(struct acpi_device
>>> *device,
>>> }
>>> EXPORT_SYMBOL(acpi_match_device_ids);
>>>
>>> +/**
>>> + * acpi_match_device_cls - Match a struct device against a ACPI _CLS
>>> method
>>> + * @dev_cls: A pointer to struct acpi_device_cls object to match
>>> against.
>>> + * @dev: The ACPI device structure to match.
>>> + *
>>> + * Check if @dev has a valid ACPI and _CLS handle. If there is a
>>> + * struct acpi_device_cls object for that handle, use that object to
>>> match
>>> + * against the given struct acpi_device_cls object.
>>> + *
>>> + * Return 0 on success or error code on failure.
>>> + */
>>> +int acpi_match_device_cls(const struct acpi_device_cls *dev_cls,
>>> + const struct device *dev)
>>> +{
>>> + int ret = -EINVAL;
>>> + acpi_status status;
>>> + struct acpi_device *adev;
>>> + union acpi_object *pkg;
>>> + struct acpi_device_cls cls;
>>> + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
>>> + struct acpi_buffer format = { sizeof("NNN"), "NNN" };
>>> + struct acpi_buffer state = { 0, NULL };
>>> + acpi_handle handle = ACPI_HANDLE(dev);
>>
>> ...
>>
>>> + acpi_handle cls_handle;
>>> +
>>> + if (!handle || acpi_bus_get_device(handle, &adev))
>>
>> if handle is not NULL, adev will not NULL too :)
>> because you get the handle from adev, ACPI_HANDLE() is defined as:
>> acpi_device_handle(ACPI_COMPANION(dev)), and adev = ACPI_COMPANION(dev);
>>
>> you may use adev = ACPI_COMPANION(dev) to simplify the code.
>>

Thanks for the pointer.

>>> + return ret;
>>> +
>>> + if (!adev->status.present || !dev_cls)
>>> + return ret;
>>> +
>>> + status = acpi_get_handle(adev->handle, METHOD_NAME__CLS,
>>> &cls_handle);
>>
>> do we need this function called here? _CLS is the method under ACPI
>> device object in DSDT/SSDT, and you will get adev->handle == cls_handle
>> if I'm not wrong :)

You are right. It is not needed, and we can just evaluate right from the
handle.

>>> + if (ACPI_FAILURE(status))
>>> + return ret;
>>> +
>>> + status = acpi_evaluate_object(cls_handle, "_CLS", NULL, &buffer);
>>> + if (ACPI_FAILURE(status)) {
>>> + ACPI_EXCEPTION((AE_INFO, status, "Failed to Evaluat _CLS"));
>>> + return ret;
>>> + }
>>
>> I think you can evaluate _CLS directly with handle here.
>>
>> Thanks
>> Hanjun
>

Yep. I will send out the new patch in a bit.

Thanks,

Suravee