mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 00:28:52 +02:00 
			
		
		
		
	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: Mika Westerberg <mika.westerberg@linux.intel.com>
Reviewed-by: Hanjun Guo <hanjun.guo@linaro.org>
Signed-off-by: Suravee Suthikulpanit <Suravee.Suthikulpanit@amd.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
			
			
This commit is contained in:
		
							parent
							
								
									d770e558e2
								
							
						
					
					
						commit
						26095a01d3
					
				
					 5 changed files with 78 additions and 4 deletions
				
			
		|  | @ -1019,6 +1019,29 @@ static bool acpi_of_match_device(struct acpi_device *adev, | |||
| 	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( | ||||
| 	struct acpi_device *device, | ||||
| 	const struct acpi_device_id *ids, | ||||
|  | @ -1036,9 +1059,12 @@ static const struct acpi_device_id *__acpi_match_device( | |||
| 
 | ||||
| 	list_for_each_entry(hwid, &device->pnp.ids, list) { | ||||
| 		/* First, check the ACPI/PNP IDs provided by the caller. */ | ||||
| 		for (id = ids; id->id[0]; id++) | ||||
| 			if (!strcmp((char *) id->id, hwid->id)) | ||||
| 		for (id = ids; id->id[0] || id->cls; id++) { | ||||
| 			if (id->id[0] && !strcmp((char *) id->id, hwid->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 | ||||
|  | @ -2101,6 +2127,8 @@ static void acpi_set_pnp_ids(acpi_handle handle, struct acpi_device_pnp *pnp, | |||
| 		if (info->valid & ACPI_VALID_UID) | ||||
| 			pnp->unique_id = kstrdup(info->unique_id.string, | ||||
| 							GFP_KERNEL); | ||||
| 		if (info->valid & ACPI_VALID_CLS) | ||||
| 			acpi_add_id(pnp, info->class_code.string); | ||||
| 
 | ||||
| 		kfree(info); | ||||
| 
 | ||||
|  |  | |||
|  | @ -58,6 +58,19 @@ static inline acpi_handle acpi_device_handle(struct acpi_device *adev) | |||
| 	acpi_fwnode_handle(adev) : NULL) | ||||
| #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) | ||||
| { | ||||
| 	return is_acpi_node(dev->fwnode); | ||||
|  | @ -446,6 +459,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *); | |||
| #define ACPI_COMPANION(dev)		(NULL) | ||||
| #define ACPI_COMPANION_SET(dev, adev)	do { } while (0) | ||||
| #define ACPI_HANDLE(dev)		(NULL) | ||||
| #define ACPI_DEVICE_CLASS(_cls, _msk)	.cls = (0), .cls_msk = (0), | ||||
| 
 | ||||
| struct fwnode_handle; | ||||
| 
 | ||||
|  |  | |||
|  | @ -189,6 +189,8 @@ struct css_device_id { | |||
| struct acpi_device_id { | ||||
| 	__u8 id[ACPI_ID_LEN]; | ||||
| 	kernel_ulong_t driver_data; | ||||
| 	__u32 cls; | ||||
| 	__u32 cls_msk; | ||||
| }; | ||||
| 
 | ||||
| #define PNP_ID_LEN	8 | ||||
|  |  | |||
|  | @ -63,6 +63,8 @@ int main(void) | |||
| 
 | ||||
| 	DEVID(acpi_device_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_FIELD(pnp_device_id, id); | ||||
|  |  | |||
|  | @ -523,12 +523,40 @@ static int do_serio_entry(const char *filename, | |||
| } | ||||
| 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, | ||||
| 			void *symval, char *alias) | ||||
| { | ||||
| 	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; | ||||
| } | ||||
| ADD_TO_DEVTABLE("acpi", acpi_device_id, do_acpi_entry); | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Suthikulpanit, Suravee
						Suthikulpanit, Suravee