Commit 26095a01 authored by Suthikulpanit, Suravee's avatar Suthikulpanit, Suravee Committed by Rafael J. Wysocki

ACPI / scan: Add support for ACPI _CLS device matching

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. Also, certain classes of devices
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.

To support loadable module, current design uses _HID or _CID to match device's
modalias. With the new way of matching with _CLS this would requires modification
to the current ACPI modalias key to include _CLS. This patch appends PCI-defined
class-code to the existing ACPI modalias as following.

    acpi:<HID>:<CID1>:<CID2>:..:<CIDn>:<bbsspp>:
E.g:
    # cat /sys/devices/platform/AMDI0600:00/modalias
    acpi:AMDI0600:010601:

where bb is th base-class code, ss is te sub-class code, and pp is the
programming interface code

Since there would not be _HID/_CID in the ACPI matching table of the driver,
this patch adds a field to acpi_device_id to specify the matching _CLS.

    static const struct acpi_device_id ahci_acpi_match[] = {
        { ACPI_DEVICE_CLASS(PCI_CLASS_STORAGE_SATA_AHCI, 0xffffff) },
        {},
    };

In this case, the corresponded entry in modules.alias file would be:

    alias acpi*:010601:* ahci_platform
Acked-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: default avatarHanjun Guo <hanjun.guo@linaro.org>
Signed-off-by: default avatarSuravee Suthikulpanit <Suravee.Suthikulpanit@amd.com>
Signed-off-by: default avatarRafael J. Wysocki <rafael.j.wysocki@intel.com>
parent d770e558
...@@ -1019,6 +1019,29 @@ static bool acpi_of_match_device(struct acpi_device *adev, ...@@ -1019,6 +1019,29 @@ static bool acpi_of_match_device(struct acpi_device *adev,
return false; return false;
} }
static bool __acpi_match_device_cls(const struct acpi_device_id *id,
struct acpi_hardware_id *hwid)
{
int i, msk, byte_shift;
char buf[3];
if (!id->cls)
return false;
/* Apply class-code bitmask, before checking each class-code byte */
for (i = 1; i <= 3; i++) {
byte_shift = 8 * (3 - i);
msk = (id->cls_msk >> byte_shift) & 0xFF;
if (!msk)
continue;
sprintf(buf, "%02x", (id->cls >> byte_shift) & msk);
if (strncmp(buf, &hwid->id[(i - 1) * 2], 2))
return false;
}
return true;
}
static const struct acpi_device_id *__acpi_match_device( static const struct acpi_device_id *__acpi_match_device(
struct acpi_device *device, struct acpi_device *device,
const struct acpi_device_id *ids, const struct acpi_device_id *ids,
...@@ -1036,9 +1059,12 @@ static const struct acpi_device_id *__acpi_match_device( ...@@ -1036,9 +1059,12 @@ static const struct acpi_device_id *__acpi_match_device(
list_for_each_entry(hwid, &device->pnp.ids, list) { list_for_each_entry(hwid, &device->pnp.ids, list) {
/* First, check the ACPI/PNP IDs provided by the caller. */ /* First, check the ACPI/PNP IDs provided by the caller. */
for (id = ids; id->id[0]; id++) for (id = ids; id->id[0] || id->cls; id++) {
if (!strcmp((char *) id->id, hwid->id)) if (id->id[0] && !strcmp((char *) id->id, hwid->id))
return id; return id;
else if (id->cls && __acpi_match_device_cls(id, hwid))
return id;
}
/* /*
* Next, check ACPI_DT_NAMESPACE_HID and try to match the * Next, check ACPI_DT_NAMESPACE_HID and try to match the
...@@ -2101,6 +2127,8 @@ static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp, ...@@ -2101,6 +2127,8 @@ static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp,
if (info->valid & ACPI_VALID_UID) if (info->valid & ACPI_VALID_UID)
pnp->unique_id = kstrdup(info->unique_id.string, pnp->unique_id = kstrdup(info->unique_id.string,
GFP_KERNEL); GFP_KERNEL);
if (info->valid & ACPI_VALID_CLS)
acpi_add_id(pnp, info->class_code.string);
kfree(info); kfree(info);
......
...@@ -58,6 +58,19 @@ static inline acpi_handle acpi_device_handle(struct acpi_device *adev) ...@@ -58,6 +58,19 @@ static inline acpi_handle acpi_device_handle(struct acpi_device *adev)
acpi_fwnode_handle(adev) : NULL) acpi_fwnode_handle(adev) : NULL)
#define ACPI_HANDLE(dev) acpi_device_handle(ACPI_COMPANION(dev)) #define ACPI_HANDLE(dev) acpi_device_handle(ACPI_COMPANION(dev))
/**
* ACPI_DEVICE_CLASS - macro used to describe an ACPI device with
* the PCI-defined class-code information
*
* @_cls : the class, subclass, prog-if triple for this device
* @_msk : the class mask for this device
*
* This macro is used to create a struct acpi_device_id that matches a
* specific PCI class. The .id and .driver_data fields will be left
* initialized with the default value.
*/
#define ACPI_DEVICE_CLASS(_cls, _msk) .cls = (_cls), .cls_msk = (_msk),
static inline bool has_acpi_companion(struct device *dev) static inline bool has_acpi_companion(struct device *dev)
{ {
return is_acpi_node(dev->fwnode); return is_acpi_node(dev->fwnode);
...@@ -446,6 +459,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *); ...@@ -446,6 +459,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *);
#define ACPI_COMPANION(dev) (NULL) #define ACPI_COMPANION(dev) (NULL)
#define ACPI_COMPANION_SET(dev, adev) do { } while (0) #define ACPI_COMPANION_SET(dev, adev) do { } while (0)
#define ACPI_HANDLE(dev) (NULL) #define ACPI_HANDLE(dev) (NULL)
#define ACPI_DEVICE_CLASS(_cls, _msk) .cls = (0), .cls_msk = (0),
struct fwnode_handle; struct fwnode_handle;
......
...@@ -189,6 +189,8 @@ struct css_device_id { ...@@ -189,6 +189,8 @@ struct css_device_id {
struct acpi_device_id { struct acpi_device_id {
__u8 id[ACPI_ID_LEN]; __u8 id[ACPI_ID_LEN];
kernel_ulong_t driver_data; kernel_ulong_t driver_data;
__u32 cls;
__u32 cls_msk;
}; };
#define PNP_ID_LEN 8 #define PNP_ID_LEN 8
......
...@@ -63,6 +63,8 @@ int main(void) ...@@ -63,6 +63,8 @@ int main(void)
DEVID(acpi_device_id); DEVID(acpi_device_id);
DEVID_FIELD(acpi_device_id, id); DEVID_FIELD(acpi_device_id, id);
DEVID_FIELD(acpi_device_id, cls);
DEVID_FIELD(acpi_device_id, cls_msk);
DEVID(pnp_device_id); DEVID(pnp_device_id);
DEVID_FIELD(pnp_device_id, id); DEVID_FIELD(pnp_device_id, id);
......
...@@ -523,12 +523,40 @@ static int do_serio_entry(const char *filename, ...@@ -523,12 +523,40 @@ static int do_serio_entry(const char *filename,
} }
ADD_TO_DEVTABLE("serio", serio_device_id, do_serio_entry); ADD_TO_DEVTABLE("serio", serio_device_id, do_serio_entry);
/* looks like: "acpi:ACPI0003 or acpi:PNP0C0B" or "acpi:LNXVIDEO" */ /* looks like: "acpi:ACPI0003" or "acpi:PNP0C0B" or "acpi:LNXVIDEO" or
* "acpi:bbsspp" (bb=base-class, ss=sub-class, pp=prog-if)
*
* NOTE: Each driver should use one of the following : _HID, _CIDs
* or _CLS. Also, bb, ss, and pp can be substituted with ??
* as don't care byte.
*/
static int do_acpi_entry(const char *filename, static int do_acpi_entry(const char *filename,
void *symval, char *alias) void *symval, char *alias)
{ {
DEF_FIELD_ADDR(symval, acpi_device_id, id); DEF_FIELD_ADDR(symval, acpi_device_id, id);
sprintf(alias, "acpi*:%s:*", *id); DEF_FIELD_ADDR(symval, acpi_device_id, cls);
DEF_FIELD_ADDR(symval, acpi_device_id, cls_msk);
if (id && strlen((const char *)*id))
sprintf(alias, "acpi*:%s:*", *id);
else if (cls) {
int i, byte_shift, cnt = 0;
unsigned int msk;
sprintf(&alias[cnt], "acpi*:");
cnt = 6;
for (i = 1; i <= 3; i++) {
byte_shift = 8 * (3-i);
msk = (*cls_msk >> byte_shift) & 0xFF;
if (msk)
sprintf(&alias[cnt], "%02x",
(*cls >> byte_shift) & 0xFF);
else
sprintf(&alias[cnt], "??");
cnt += 2;
}
sprintf(&alias[cnt], ":*");
}
return 1; return 1;
} }
ADD_TO_DEVTABLE("acpi", acpi_device_id, do_acpi_entry); ADD_TO_DEVTABLE("acpi", acpi_device_id, do_acpi_entry);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment