aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/glue.c
diff options
context:
space:
mode:
authorLan Tianyu <tianyu.lan@intel.com>2012-08-17 02:44:09 -0400
committerLen Brown <len.brown@intel.com>2012-09-21 13:30:29 -0400
commit1033f9041d526dd694e2b2e12744e47c41040c4d (patch)
treea953100e337dd4f464253f88a476f990c4209f26 /drivers/acpi/glue.c
parentc46de2263f42fb4bbde411b9126f471e9343cb22 (diff)
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 <tianyu.lan@intel.com> Acked-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/acpi/glue.c')
-rw-r--r--drivers/acpi/glue.c135
1 files changed, 86 insertions, 49 deletions
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 @@
25static LIST_HEAD(bus_type_list); 25static LIST_HEAD(bus_type_list);
26static DECLARE_RWSEM(bus_type_sem); 26static DECLARE_RWSEM(bus_type_sem);
27 27
28#define PHYSICAL_NODE_STRING "physical_node"
29
28int register_acpi_bus_type(struct acpi_bus_type *type) 30int register_acpi_bus_type(struct acpi_bus_type *type)
29{ 31{
30 if (acpi_disabled) 32 if (acpi_disabled)
@@ -124,84 +126,119 @@ acpi_handle acpi_get_child(acpi_handle parent, u64 address)
124 126
125EXPORT_SYMBOL(acpi_get_child); 127EXPORT_SYMBOL(acpi_get_child);
126 128
127/* Link ACPI devices with physical devices */
128static void acpi_glue_data_handler(acpi_handle handle,
129 void *context)
130{
131 /* we provide an empty handler */
132}
133
134/* Note: a success call will increase reference count by one */
135struct device *acpi_get_physical_device(acpi_handle handle)
136{
137 acpi_status status;
138 struct device *dev;
139
140 status = acpi_get_data(handle, acpi_glue_data_handler, (void **)&dev);
141 if (ACPI_SUCCESS(status))
142 return get_device(dev);
143 return NULL;
144}
145
146EXPORT_SYMBOL(acpi_get_physical_device);
147
148static int acpi_bind_one(struct device *dev, acpi_handle handle) 129static int acpi_bind_one(struct device *dev, acpi_handle handle)
149{ 130{
150 struct acpi_device *acpi_dev; 131 struct acpi_device *acpi_dev;
151 acpi_status status; 132 acpi_status status;
133 struct acpi_device_physical_node *physical_node;
134 char physical_node_name[sizeof(PHYSICAL_NODE_STRING) + 2];
135 int retval = -EINVAL;
152 136
153 if (dev->archdata.acpi_handle) { 137 if (dev->archdata.acpi_handle) {
154 dev_warn(dev, "Drivers changed 'acpi_handle'\n"); 138 dev_warn(dev, "Drivers changed 'acpi_handle'\n");
155 return -EINVAL; 139 return -EINVAL;
156 } 140 }
141
157 get_device(dev); 142 get_device(dev);
158 status = acpi_attach_data(handle, acpi_glue_data_handler, dev); 143 status = acpi_bus_get_device(handle, &acpi_dev);
159 if (ACPI_FAILURE(status)) { 144 if (ACPI_FAILURE(status))
160 put_device(dev); 145 goto err;
161 return -EINVAL; 146
147 physical_node = kzalloc(sizeof(struct acpi_device_physical_node),
148 GFP_KERNEL);
149 if (!physical_node) {
150 retval = -ENOMEM;
151 goto err;
162 } 152 }
163 dev->archdata.acpi_handle = handle;
164 153
165 status = acpi_bus_get_device(handle, &acpi_dev); 154 mutex_lock(&acpi_dev->physical_node_lock);
166 if (!ACPI_FAILURE(status)) { 155 /* allocate physical node id according to physical_node_id_bitmap */
167 int ret; 156 physical_node->node_id =
168 157 find_first_zero_bit(acpi_dev->physical_node_id_bitmap,
169 ret = sysfs_create_link(&dev->kobj, &acpi_dev->dev.kobj, 158 ACPI_MAX_PHYSICAL_NODE);
170 "firmware_node"); 159 if (physical_node->node_id >= ACPI_MAX_PHYSICAL_NODE) {
171 ret = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj, 160 retval = -ENOSPC;
172 "physical_node"); 161 mutex_unlock(&acpi_dev->physical_node_lock);
173 if (acpi_dev->wakeup.flags.valid) 162 goto err;
174 device_set_wakeup_capable(dev, true);
175 } 163 }
176 164
165 set_bit(physical_node->node_id, acpi_dev->physical_node_id_bitmap);
166 physical_node->dev = dev;
167 list_add_tail(&physical_node->node, &acpi_dev->physical_node_list);
168 acpi_dev->physical_node_count++;
169 mutex_unlock(&acpi_dev->physical_node_lock);
170
171 dev->archdata.acpi_handle = handle;
172
173 if (!physical_node->node_id)
174 strcpy(physical_node_name, PHYSICAL_NODE_STRING);
175 else
176 sprintf(physical_node_name,
177 "physical_node%d", physical_node->node_id);
178 retval = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj,
179 physical_node_name);
180 retval = sysfs_create_link(&dev->kobj, &acpi_dev->dev.kobj,
181 "firmware_node");
182
183 if (acpi_dev->wakeup.flags.valid)
184 device_set_wakeup_capable(dev, true);
185
177 return 0; 186 return 0;
187
188 err:
189 put_device(dev);
190 return retval;
178} 191}
179 192
180static int acpi_unbind_one(struct device *dev) 193static int acpi_unbind_one(struct device *dev)
181{ 194{
195 struct acpi_device_physical_node *entry;
196 struct acpi_device *acpi_dev;
197 acpi_status status;
198 struct list_head *node, *next;
199
182 if (!dev->archdata.acpi_handle) 200 if (!dev->archdata.acpi_handle)
183 return 0; 201 return 0;
184 if (dev == acpi_get_physical_device(dev->archdata.acpi_handle)) {
185 struct acpi_device *acpi_dev;
186 202
187 /* acpi_get_physical_device increase refcnt by one */ 203 status = acpi_bus_get_device(dev->archdata.acpi_handle,
188 put_device(dev); 204 &acpi_dev);
205 if (ACPI_FAILURE(status))
206 goto err;
189 207
190 if (!acpi_bus_get_device(dev->archdata.acpi_handle, 208 mutex_lock(&acpi_dev->physical_node_lock);
191 &acpi_dev)) { 209 list_for_each_safe(node, next, &acpi_dev->physical_node_list) {
192 sysfs_remove_link(&dev->kobj, "firmware_node"); 210 char physical_node_name[sizeof(PHYSICAL_NODE_STRING) + 2];
193 sysfs_remove_link(&acpi_dev->dev.kobj, "physical_node"); 211
194 } 212 entry = list_entry(node, struct acpi_device_physical_node,
213 node);
214 if (entry->dev != dev)
215 continue;
216
217 list_del(node);
218 clear_bit(entry->node_id, acpi_dev->physical_node_id_bitmap);
195 219
196 acpi_detach_data(dev->archdata.acpi_handle, 220 acpi_dev->physical_node_count--;
197 acpi_glue_data_handler); 221
222 if (!entry->node_id)
223 strcpy(physical_node_name, PHYSICAL_NODE_STRING);
224 else
225 sprintf(physical_node_name,
226 "physical_node%d", entry->node_id);
227
228 sysfs_remove_link(&acpi_dev->dev.kobj, physical_node_name);
229 sysfs_remove_link(&dev->kobj, "firmware_node");
198 dev->archdata.acpi_handle = NULL; 230 dev->archdata.acpi_handle = NULL;
199 /* acpi_bind_one increase refcnt by one */ 231 /* acpi_bind_one increase refcnt by one */
200 put_device(dev); 232 put_device(dev);
201 } else { 233 kfree(entry);
202 dev_err(dev, "Oops, 'acpi_handle' corrupt\n");
203 } 234 }
235 mutex_unlock(&acpi_dev->physical_node_lock);
236
204 return 0; 237 return 0;
238
239err:
240 dev_err(dev, "Oops, 'acpi_handle' corrupt\n");
241 return -EINVAL;
205} 242}
206 243
207static int acpi_platform_notify(struct device *dev) 244static int acpi_platform_notify(struct device *dev)