1
2
3
4
5
6
7#include <linux/module.h>
8#include <linux/usb.h>
9#include <linux/device.h>
10#include <linux/errno.h>
11#include <linux/kernel.h>
12#include <linux/acpi.h>
13#include <linux/pci.h>
14#include <linux/usb/hcd.h>
15
16#include "hub.h"
17
18
19
20
21
22
23
24
25
26bool usb_acpi_power_manageable(struct usb_device *hdev, int index)
27{
28 acpi_handle port_handle;
29 int port1 = index + 1;
30
31 port_handle = usb_get_hub_port_acpi_handle(hdev,
32 port1);
33 if (port_handle)
34 return acpi_bus_power_manageable(port_handle);
35 else
36 return false;
37}
38EXPORT_SYMBOL_GPL(usb_acpi_power_manageable);
39
40
41
42
43
44
45
46
47
48
49
50
51
52int usb_acpi_set_power_state(struct usb_device *hdev, int index, bool enable)
53{
54 struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
55 struct usb_port *port_dev;
56 acpi_handle port_handle;
57 unsigned char state;
58 int port1 = index + 1;
59 int error = -EINVAL;
60
61 if (!hub)
62 return -ENODEV;
63 port_dev = hub->ports[port1 - 1];
64
65 port_handle = (acpi_handle) usb_get_hub_port_acpi_handle(hdev, port1);
66 if (!port_handle)
67 return error;
68
69 if (enable)
70 state = ACPI_STATE_D0;
71 else
72 state = ACPI_STATE_D3_COLD;
73
74 error = acpi_bus_set_power(port_handle, state);
75 if (!error)
76 dev_dbg(&port_dev->dev, "acpi: power was set to %d\n", enable);
77 else
78 dev_dbg(&port_dev->dev, "acpi: power failed to be set\n");
79
80 return error;
81}
82EXPORT_SYMBOL_GPL(usb_acpi_set_power_state);
83
84static enum usb_port_connect_type usb_acpi_get_connect_type(acpi_handle handle,
85 struct acpi_pld_info *pld)
86{
87 enum usb_port_connect_type connect_type = USB_PORT_CONNECT_TYPE_UNKNOWN;
88 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
89 union acpi_object *upc = NULL;
90 acpi_status status;
91
92
93
94
95
96
97
98
99
100 status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer);
101 if (ACPI_FAILURE(status))
102 goto out;
103
104 upc = buffer.pointer;
105 if (!upc || (upc->type != ACPI_TYPE_PACKAGE) || upc->package.count != 4)
106 goto out;
107
108 if (upc->package.elements[0].integer.value)
109 if (pld->user_visible)
110 connect_type = USB_PORT_CONNECT_TYPE_HOT_PLUG;
111 else
112 connect_type = USB_PORT_CONNECT_TYPE_HARD_WIRED;
113 else if (!pld->user_visible)
114 connect_type = USB_PORT_NOT_USED;
115out:
116 kfree(upc);
117 return connect_type;
118}
119
120
121
122
123
124
125#define USB_ACPI_LOCATION_VALID (1 << 31)
126
127static struct acpi_device *usb_acpi_find_port(struct acpi_device *parent,
128 int raw)
129{
130 struct acpi_device *adev;
131
132 if (!parent)
133 return NULL;
134
135 list_for_each_entry(adev, &parent->children, node) {
136 if (acpi_device_adr(adev) == raw)
137 return adev;
138 }
139
140 return acpi_find_child_device(parent, raw, false);
141}
142
143static struct acpi_device *
144usb_acpi_get_companion_for_port(struct usb_port *port_dev)
145{
146 struct usb_device *udev;
147 struct acpi_device *adev;
148 acpi_handle *parent_handle;
149 int port1;
150
151
152 udev = to_usb_device(port_dev->dev.parent->parent);
153
154
155
156
157
158
159 if (!udev->parent) {
160 adev = ACPI_COMPANION(&udev->dev);
161 port1 = usb_hcd_find_raw_port_number(bus_to_hcd(udev->bus),
162 port_dev->portnum);
163 } else {
164 parent_handle = usb_get_hub_port_acpi_handle(udev->parent,
165 udev->portnum);
166 if (!parent_handle)
167 return NULL;
168
169 adev = acpi_fetch_acpi_dev(parent_handle);
170 port1 = port_dev->portnum;
171 }
172
173 return usb_acpi_find_port(adev, port1);
174}
175
176static struct acpi_device *
177usb_acpi_find_companion_for_port(struct usb_port *port_dev)
178{
179 struct acpi_device *adev;
180 struct acpi_pld_info *pld;
181 acpi_handle *handle;
182 acpi_status status;
183
184 adev = usb_acpi_get_companion_for_port(port_dev);
185 if (!adev)
186 return NULL;
187
188 handle = adev->handle;
189 status = acpi_get_physical_device_location(handle, &pld);
190 if (ACPI_SUCCESS(status) && pld) {
191 port_dev->location = USB_ACPI_LOCATION_VALID
192 | pld->group_token << 8 | pld->group_position;
193 port_dev->connect_type = usb_acpi_get_connect_type(handle, pld);
194 ACPI_FREE(pld);
195 }
196
197 return adev;
198}
199
200static struct acpi_device *
201usb_acpi_find_companion_for_device(struct usb_device *udev)
202{
203 struct acpi_device *adev;
204 struct usb_port *port_dev;
205 struct usb_hub *hub;
206
207 if (!udev->parent) {
208
209
210
211
212 adev = ACPI_COMPANION(udev->bus->sysdev);
213 return acpi_find_child_device(adev, 0, false);
214 }
215
216 hub = usb_hub_to_struct_hub(udev->parent);
217 if (!hub)
218 return NULL;
219
220
221
222
223
224 port_dev = hub->ports[udev->portnum - 1];
225 return usb_acpi_get_companion_for_port(port_dev);
226}
227
228static struct acpi_device *usb_acpi_find_companion(struct device *dev)
229{
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258 if (is_usb_device(dev))
259 return usb_acpi_find_companion_for_device(to_usb_device(dev));
260 else if (is_usb_port(dev))
261 return usb_acpi_find_companion_for_port(to_usb_port(dev));
262
263 return NULL;
264}
265
266static bool usb_acpi_bus_match(struct device *dev)
267{
268 return is_usb_device(dev) || is_usb_port(dev);
269}
270
271static struct acpi_bus_type usb_acpi_bus = {
272 .name = "USB",
273 .match = usb_acpi_bus_match,
274 .find_companion = usb_acpi_find_companion,
275};
276
277int usb_acpi_register(void)
278{
279 return register_acpi_bus_type(&usb_acpi_bus);
280}
281
282void usb_acpi_unregister(void)
283{
284 unregister_acpi_bus_type(&usb_acpi_bus);
285}
286