From 8ede06aba5dffa78a27a18c47a9059eb38072ada Mon Sep 17 00:00:00 2001 From: Feng Tang Date: Tue, 21 Aug 2012 09:56:58 +0800 Subject: ACPI: Use ACPICA native way to decode the PLD buffer This patch is on top of the ACPICA 20120816 release, which implemented a native way to decode PLD buffer, so use it instead of leting upper level users do the decoding. v2: Modify the check for PLD buffer length to reject buffers whose length < 16 Signed-off-by: Feng Tang Signed-off-by: Bob Moore Signed-off-by: Len Brown --- include/acpi/acpi_bus.h | 31 +------------------------------ 1 file changed, 1 insertion(+), 30 deletions(-) (limited to 'include/acpi/acpi_bus.h') diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index bde976ee068d..0c1fcd4a2b42 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -54,37 +54,8 @@ acpi_status acpi_evaluate_hotplug_ost(acpi_handle handle, u32 source_event, u32 status_code, struct acpi_buffer *status_buf); -struct acpi_pld { - unsigned int revision:7; /* 0 */ - unsigned int ignore_colour:1; /* 7 */ - unsigned int colour:24; /* 8 */ - unsigned int width:16; /* 32 */ - unsigned int height:16; /* 48 */ - unsigned int user_visible:1; /* 64 */ - unsigned int dock:1; /* 65 */ - unsigned int lid:1; /* 66 */ - unsigned int panel:3; /* 67 */ - unsigned int vertical_pos:2; /* 70 */ - unsigned int horizontal_pos:2; /* 72 */ - unsigned int shape:4; /* 74 */ - unsigned int group_orientation:1; /* 78 */ - unsigned int group_token:8; /* 79 */ - unsigned int group_position:8; /* 87 */ - unsigned int bay:1; /* 95 */ - unsigned int ejectable:1; /* 96 */ - unsigned int ospm_eject_required:1; /* 97 */ - unsigned int cabinet_number:8; /* 98 */ - unsigned int card_cage_number:8; /* 106 */ - unsigned int reference:1; /* 114 */ - unsigned int rotation:4; /* 115 */ - unsigned int order:5; /* 119 */ - unsigned int reserved:4; /* 124 */ - unsigned int vertical_offset:16; /* 128 */ - unsigned int horizontal_offset:16; /* 144 */ -} __attribute__((__packed__)); - acpi_status -acpi_get_physical_device_location(acpi_handle handle, struct acpi_pld *pld); +acpi_get_physical_device_location(acpi_handle handle, struct acpi_pld_info **pld); #ifdef CONFIG_ACPI #include -- cgit v1.2.3 From 1033f9041d526dd694e2b2e12744e47c41040c4d Mon Sep 17 00:00:00 2001 From: Lan Tianyu Date: Fri, 17 Aug 2012 14:44:09 +0800 Subject: ACPI: Allow ACPI binding with USB-3.0 hub A USB port's position and connectability can't be identified on some boards via USB hub registers. ACPI _UPC and _PLD can help to resolve this issue and so it is necessary to bind USB with ACPI. This patch is to allow ACPI binding with USB-3.0 hub. Current ACPI only can bind one struct-device to one ACPI device node. This can not work with USB-3.0 hub, because the USB-3.0 hub has two logical devices. Each works for USB-2.0 and USB-3.0 devices. In the Linux USB subsystem, those two logical hubs are treated as two seperate devices that have two struct devices. But in the ACPI DSDT, these two logical hubs share one ACPI device node. So there is a requirement to bind multi struct-devices to one ACPI device node. This patch is to resolve such problem. Following is the ACPI device nodes' description under xhci hcd. Device (XHC) Device (RHUB) Device (HSP1) Device (HSP2) Device (HSP3) Device (HSP4) Device (SSP1) Device (SSP2) Device (SSP3) Device (SSP4) Topology in the Linux device XHC USB-2.0 logical hub USB-3.0 logical hub HSP1 SSP1 HSP2 SSP2 HSP3 SSP3 HSP4 SSP4 This patch also modifies the output of /proc/acpi/wakeup. One ACPI node can be associated with multiple devices: XHC S4 *enabled pci:0000:00:14.0 RHUB S0 disabled usb:usb1 disabled usb:usb2 Signed-off-by: Lan Tianyu Acked-by: Sarah Sharp Signed-off-by: Len Brown --- drivers/acpi/glue.c | 135 +++++++++++++++++++++++++++++---------------- drivers/acpi/proc.c | 57 ++++++++++++------- drivers/acpi/scan.c | 2 + drivers/pnp/pnpacpi/core.c | 7 +-- include/acpi/acpi_bus.h | 15 ++++- 5 files changed, 139 insertions(+), 77 deletions(-) (limited to 'include/acpi/acpi_bus.h') diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 243ee85e4d2e..d1a2d74033e9 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -25,6 +25,8 @@ static LIST_HEAD(bus_type_list); static DECLARE_RWSEM(bus_type_sem); +#define PHYSICAL_NODE_STRING "physical_node" + int register_acpi_bus_type(struct acpi_bus_type *type) { if (acpi_disabled) @@ -124,84 +126,119 @@ acpi_handle acpi_get_child(acpi_handle parent, u64 address) EXPORT_SYMBOL(acpi_get_child); -/* Link ACPI devices with physical devices */ -static void acpi_glue_data_handler(acpi_handle handle, - void *context) -{ - /* we provide an empty handler */ -} - -/* Note: a success call will increase reference count by one */ -struct device *acpi_get_physical_device(acpi_handle handle) -{ - acpi_status status; - struct device *dev; - - status = acpi_get_data(handle, acpi_glue_data_handler, (void **)&dev); - if (ACPI_SUCCESS(status)) - return get_device(dev); - return NULL; -} - -EXPORT_SYMBOL(acpi_get_physical_device); - static int acpi_bind_one(struct device *dev, acpi_handle handle) { struct acpi_device *acpi_dev; acpi_status status; + struct acpi_device_physical_node *physical_node; + char physical_node_name[sizeof(PHYSICAL_NODE_STRING) + 2]; + int retval = -EINVAL; if (dev->archdata.acpi_handle) { dev_warn(dev, "Drivers changed 'acpi_handle'\n"); return -EINVAL; } + get_device(dev); - status = acpi_attach_data(handle, acpi_glue_data_handler, dev); - if (ACPI_FAILURE(status)) { - put_device(dev); - return -EINVAL; + status = acpi_bus_get_device(handle, &acpi_dev); + if (ACPI_FAILURE(status)) + goto err; + + physical_node = kzalloc(sizeof(struct acpi_device_physical_node), + GFP_KERNEL); + if (!physical_node) { + retval = -ENOMEM; + goto err; } - dev->archdata.acpi_handle = handle; - status = acpi_bus_get_device(handle, &acpi_dev); - if (!ACPI_FAILURE(status)) { - int ret; - - ret = sysfs_create_link(&dev->kobj, &acpi_dev->dev.kobj, - "firmware_node"); - ret = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj, - "physical_node"); - if (acpi_dev->wakeup.flags.valid) - device_set_wakeup_capable(dev, true); + mutex_lock(&acpi_dev->physical_node_lock); + /* allocate physical node id according to physical_node_id_bitmap */ + physical_node->node_id = + find_first_zero_bit(acpi_dev->physical_node_id_bitmap, + ACPI_MAX_PHYSICAL_NODE); + if (physical_node->node_id >= ACPI_MAX_PHYSICAL_NODE) { + retval = -ENOSPC; + mutex_unlock(&acpi_dev->physical_node_lock); + goto err; } + set_bit(physical_node->node_id, acpi_dev->physical_node_id_bitmap); + physical_node->dev = dev; + list_add_tail(&physical_node->node, &acpi_dev->physical_node_list); + acpi_dev->physical_node_count++; + mutex_unlock(&acpi_dev->physical_node_lock); + + dev->archdata.acpi_handle = handle; + + if (!physical_node->node_id) + strcpy(physical_node_name, PHYSICAL_NODE_STRING); + else + sprintf(physical_node_name, + "physical_node%d", physical_node->node_id); + retval = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj, + physical_node_name); + retval = sysfs_create_link(&dev->kobj, &acpi_dev->dev.kobj, + "firmware_node"); + + if (acpi_dev->wakeup.flags.valid) + device_set_wakeup_capable(dev, true); + return 0; + + err: + put_device(dev); + return retval; } static int acpi_unbind_one(struct device *dev) { + struct acpi_device_physical_node *entry; + struct acpi_device *acpi_dev; + acpi_status status; + struct list_head *node, *next; + if (!dev->archdata.acpi_handle) return 0; - if (dev == acpi_get_physical_device(dev->archdata.acpi_handle)) { - struct acpi_device *acpi_dev; - /* acpi_get_physical_device increase refcnt by one */ - put_device(dev); + status = acpi_bus_get_device(dev->archdata.acpi_handle, + &acpi_dev); + if (ACPI_FAILURE(status)) + goto err; - if (!acpi_bus_get_device(dev->archdata.acpi_handle, - &acpi_dev)) { - sysfs_remove_link(&dev->kobj, "firmware_node"); - sysfs_remove_link(&acpi_dev->dev.kobj, "physical_node"); - } + mutex_lock(&acpi_dev->physical_node_lock); + list_for_each_safe(node, next, &acpi_dev->physical_node_list) { + char physical_node_name[sizeof(PHYSICAL_NODE_STRING) + 2]; + + entry = list_entry(node, struct acpi_device_physical_node, + node); + if (entry->dev != dev) + continue; + + list_del(node); + clear_bit(entry->node_id, acpi_dev->physical_node_id_bitmap); - acpi_detach_data(dev->archdata.acpi_handle, - acpi_glue_data_handler); + acpi_dev->physical_node_count--; + + if (!entry->node_id) + strcpy(physical_node_name, PHYSICAL_NODE_STRING); + else + sprintf(physical_node_name, + "physical_node%d", entry->node_id); + + sysfs_remove_link(&acpi_dev->dev.kobj, physical_node_name); + sysfs_remove_link(&dev->kobj, "firmware_node"); dev->archdata.acpi_handle = NULL; /* acpi_bind_one increase refcnt by one */ put_device(dev); - } else { - dev_err(dev, "Oops, 'acpi_handle' corrupt\n"); + kfree(entry); } + mutex_unlock(&acpi_dev->physical_node_lock); + return 0; + +err: + dev_err(dev, "Oops, 'acpi_handle' corrupt\n"); + return -EINVAL; } static int acpi_platform_notify(struct device *dev) diff --git a/drivers/acpi/proc.c b/drivers/acpi/proc.c index 251c7b6273a9..27adb090bb30 100644 --- a/drivers/acpi/proc.c +++ b/drivers/acpi/proc.c @@ -302,26 +302,41 @@ acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset) list_for_each_safe(node, next, &acpi_wakeup_device_list) { struct acpi_device *dev = container_of(node, struct acpi_device, wakeup_list); - struct device *ldev; + struct acpi_device_physical_node *entry; if (!dev->wakeup.flags.valid) continue; - ldev = acpi_get_physical_device(dev->handle); - seq_printf(seq, "%s\t S%d\t%c%-8s ", + seq_printf(seq, "%s\t S%d\t", dev->pnp.bus_id, - (u32) dev->wakeup.sleep_state, - dev->wakeup.flags.run_wake ? '*' : ' ', - (device_may_wakeup(&dev->dev) - || (ldev && device_may_wakeup(ldev))) ? - "enabled" : "disabled"); - if (ldev) - seq_printf(seq, "%s:%s", - ldev->bus ? ldev->bus->name : "no-bus", - dev_name(ldev)); - seq_printf(seq, "\n"); - put_device(ldev); - + (u32) dev->wakeup.sleep_state); + + if (!dev->physical_node_count) + seq_printf(seq, "%c%-8s\n", + dev->wakeup.flags.run_wake ? + '*' : ' ', "disabled"); + else { + struct device *ldev; + list_for_each_entry(entry, &dev->physical_node_list, + node) { + ldev = get_device(entry->dev); + if (!ldev) + continue; + + if (&entry->node != + dev->physical_node_list.next) + seq_printf(seq, "\t\t"); + + seq_printf(seq, "%c%-8s %s:%s\n", + dev->wakeup.flags.run_wake ? '*' : ' ', + (device_may_wakeup(&dev->dev) || + (ldev && device_may_wakeup(ldev))) ? + "enabled" : "disabled", + ldev->bus ? ldev->bus->name : + "no-bus", dev_name(ldev)); + put_device(ldev); + } + } } mutex_unlock(&acpi_device_lock); return 0; @@ -329,12 +344,14 @@ acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset) static void physical_device_enable_wakeup(struct acpi_device *adev) { - struct device *dev = acpi_get_physical_device(adev->handle); + struct acpi_device_physical_node *entry; - if (dev && device_can_wakeup(dev)) { - bool enable = !device_may_wakeup(dev); - device_set_wakeup_enable(dev, enable); - } + list_for_each_entry(entry, + &adev->physical_node_list, node) + if (entry->dev && device_can_wakeup(entry->dev)) { + bool enable = !device_may_wakeup(entry->dev); + device_set_wakeup_enable(entry->dev, enable); + } } static ssize_t diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index d1ecca2b641a..d730a939ead1 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -481,6 +481,8 @@ static int acpi_device_register(struct acpi_device *device) INIT_LIST_HEAD(&device->children); INIT_LIST_HEAD(&device->node); INIT_LIST_HEAD(&device->wakeup_list); + INIT_LIST_HEAD(&device->physical_node_list); + mutex_init(&device->physical_node_lock); new_bus_id = kzalloc(sizeof(struct acpi_device_bus_id), GFP_KERNEL); if (!new_bus_id) { diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c index 507a8e2b9a4c..26b5d4b18dd7 100644 --- a/drivers/pnp/pnpacpi/core.c +++ b/drivers/pnp/pnpacpi/core.c @@ -321,14 +321,9 @@ static int __init acpi_pnp_match(struct device *dev, void *_pnp) { struct acpi_device *acpi = to_acpi_device(dev); struct pnp_dev *pnp = _pnp; - struct device *physical_device; - - physical_device = acpi_get_physical_device(acpi->handle); - if (physical_device) - put_device(physical_device); /* true means it matched */ - return !physical_device + return !acpi->physical_node_count && compare_pnp_id(pnp->id, acpi_device_hid(acpi)); } diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index bde976ee068d..453596d4ce65 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -282,8 +282,16 @@ struct acpi_device_wakeup { int prepare_count; }; -/* Device */ +struct acpi_device_physical_node { + u8 node_id; + struct list_head node; + struct device *dev; +}; +/* set maximum of physical nodes to 32 for expansibility */ +#define ACPI_MAX_PHYSICAL_NODE 32 + +/* Device */ struct acpi_device { int device_type; acpi_handle handle; /* no handle for fixed hardware */ @@ -304,6 +312,10 @@ struct acpi_device { struct device dev; struct acpi_bus_ops bus_ops; /* workaround for different code path for hotplug */ enum acpi_bus_removal_type removal_type; /* indicate for different removal type */ + u8 physical_node_count; + struct list_head physical_node_list; + struct mutex physical_node_lock; + DECLARE_BITMAP(physical_node_id_bitmap, ACPI_MAX_PHYSICAL_NODE); }; static inline void *acpi_driver_data(struct acpi_device *d) @@ -394,7 +406,6 @@ struct acpi_bus_type { }; int register_acpi_bus_type(struct acpi_bus_type *); int unregister_acpi_bus_type(struct acpi_bus_type *); -struct device *acpi_get_physical_device(acpi_handle); struct acpi_pci_root { struct list_head node; -- cgit v1.2.3 From 0d2cf8f5aab358c897902ebad40fa4edd1b4556e Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Fri, 7 Sep 2012 10:31:38 +0300 Subject: ACPI: introduce module_acpi_driver() helper macro Add a helper macro module_acpi_driver() which reduces the boilerplate code for ACPI drivers. This is similar what is done for other busses (PCI, SPI, I2C etc). Signed-off-by: Mika Westerberg Signed-off-by: Len Brown --- include/acpi/acpi_bus.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'include/acpi/acpi_bus.h') diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index bde976ee068d..bddd90975e4e 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -381,6 +381,19 @@ int acpi_match_device_ids(struct acpi_device *device, int acpi_create_dir(struct acpi_device *); void acpi_remove_dir(struct acpi_device *); + +/** + * module_acpi_driver(acpi_driver) - Helper macro for registering an ACPI driver + * @__acpi_driver: acpi_driver struct + * + * Helper macro for ACPI drivers which do not do anything special in module + * init/exit. This eliminates a lot of boilerplate. Each module may only + * use this macro once, and calling it replaces module_init() and module_exit() + */ +#define module_acpi_driver(__acpi_driver) \ + module_driver(__acpi_driver, acpi_bus_register_driver, \ + acpi_bus_unregister_driver) + /* * Bind physical devices with ACPI devices */ -- cgit v1.2.3 From d1efe3c324ead77d3f6cd85093b50f6bd2e17aba Mon Sep 17 00:00:00 2001 From: Lance Ortiz Date: Tue, 2 Oct 2012 12:43:23 -0600 Subject: ACPI: Add new sysfs interface to export device description Add support to export the device description obtained from the ACPI _STR method, if one exists for a device, to user-space via a sysfs interface. This new interface provides a standard and platform neutral way for users to obtain the description text stored in the ACPI _STR method. If no _STR method exists for the device, no sysfs 'description' file will be created. The 'description' file will be located in the /sys/devices/ directory using the device's path. /sys/device///.../firmware_node/description Example: /sys/devices/pci0000:00/0000:00.07.0/0000:0e:00.0/firmware_node/description It can also be located using the ACPI device path, for example: /sys/devices/LNXSYSTM:00/device:00/ACPI0004:00/PNP0A08:00/device:13/device:15/description /sys/devices/LNXSYSTM:00/device:00/ACPI0004:00/ACPI0004:01/ACPI0007:02/description Execute the 'cat' command on the 'description' file to obtain the description string for that device. This patch also includes documentation describing how the new sysfs interface works Changes from v1-v2 based on comments by Len Brown and Fengguang Wu * Removed output "No Description" and leaving a NULL attribute if the _STR method failed to evaluate. * In acpi_device_remove_files() removed the redundent check of dev->pnp.str_obj before calling free. This check triggered a message from smatch. Signed-off-by: Lance Ortiz Signed-off-by: Len Brown --- .../ABI/testing/sysfs-devices-firmware_node | 17 +++++++ drivers/acpi/scan.c | 54 +++++++++++++++++++++- include/acpi/acpi_bus.h | 1 + 3 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-devices-firmware_node (limited to 'include/acpi/acpi_bus.h') diff --git a/Documentation/ABI/testing/sysfs-devices-firmware_node b/Documentation/ABI/testing/sysfs-devices-firmware_node new file mode 100644 index 000000000000..46badc9ea284 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-devices-firmware_node @@ -0,0 +1,17 @@ +What: /sys/devices/.../firmware_node/ +Date: September 2012 +Contact: <> +Description: + The /sys/devices/.../firmware_node directory contains attributes + allowing the user space to check and modify some firmware + related properties of given device. + +What: /sys/devices/.../firmware_node/description +Date: September 2012 +Contact: Lance Ortiz +Description: + The /sys/devices/.../firmware/description attribute contains a string + that describes the device as provided by the _STR method in the ACPI + namespace. This attribute is read-only. If the device does not have + an _STR method associated with it in the ACPI namespace, this + attribute is not present. diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index d1ecca2b641a..04302835723d 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -232,8 +233,35 @@ end: } static DEVICE_ATTR(path, 0444, acpi_device_path_show, NULL); +/* sysfs file that shows description text from the ACPI _STR method */ +static ssize_t description_show(struct device *dev, + struct device_attribute *attr, + char *buf) { + struct acpi_device *acpi_dev = to_acpi_device(dev); + int result; + + if (acpi_dev->pnp.str_obj == NULL) + return 0; + + /* + * The _STR object contains a Unicode identifier for a device. + * We need to convert to utf-8 so it can be displayed. + */ + result = utf16s_to_utf8s( + (wchar_t *)acpi_dev->pnp.str_obj->buffer.pointer, + acpi_dev->pnp.str_obj->buffer.length, + UTF16_LITTLE_ENDIAN, buf, + PAGE_SIZE); + + buf[result++] = '\n'; + + return result; +} +static DEVICE_ATTR(description, 0444, description_show, NULL); + static int acpi_device_setup_files(struct acpi_device *dev) { + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; acpi_status status; acpi_handle temp; int result = 0; @@ -257,6 +285,21 @@ static int acpi_device_setup_files(struct acpi_device *dev) goto end; } + /* + * If device has _STR, 'description' file is created + */ + status = acpi_get_handle(dev->handle, "_STR", &temp); + if (ACPI_SUCCESS(status)) { + status = acpi_evaluate_object(dev->handle, "_STR", + NULL, &buffer); + if (ACPI_FAILURE(status)) + buffer.pointer = NULL; + dev->pnp.str_obj = buffer.pointer; + result = device_create_file(&dev->dev, &dev_attr_description); + if (result) + goto end; + } + /* * If device has _EJ0, 'eject' file is created that is used to trigger * hot-removal function from userland. @@ -274,8 +317,15 @@ static void acpi_device_remove_files(struct acpi_device *dev) acpi_handle temp; /* - * If device has _EJ0, 'eject' file is created that is used to trigger - * hot-removal function from userland. + * If device has _STR, remove 'description' file + */ + status = acpi_get_handle(dev->handle, "_STR", &temp); + if (ACPI_SUCCESS(status)) { + kfree(dev->pnp.str_obj); + device_remove_file(&dev->dev, &dev_attr_description); + } + /* + * If device has _EJ0, remove 'eject' file. */ status = acpi_get_handle(dev->handle, "_EJ0", &temp); if (ACPI_SUCCESS(status)) diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index bde976ee068d..3eb9de318824 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -208,6 +208,7 @@ struct acpi_device_pnp { struct list_head ids; /* _HID and _CIDs */ acpi_device_name device_name; /* Driver-determined */ acpi_device_class device_class; /* " */ + union acpi_object *str_obj; /* unicode string for _STR method */ }; #define acpi_device_bid(d) ((d)->pnp.bus_id) -- cgit v1.2.3