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 acpi_bus_get_device(parent_handle, &adev);
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 adev = ACPI_COMPANION(udev->dev.parent);
210 return acpi_find_child_device(adev, 0, false);
211 }
212
213 hub = usb_hub_to_struct_hub(udev->parent);
214 if (!hub)
215 return NULL;
216
217
218
219
220
221 port_dev = hub->ports[udev->portnum - 1];
222 return usb_acpi_get_companion_for_port(port_dev);
223}
224
225static struct acpi_device *usb_acpi_find_companion(struct device *dev)
226{
227
228
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 if (is_usb_device(dev))
256 return usb_acpi_find_companion_for_device(to_usb_device(dev));
257 else if (is_usb_port(dev))
258 return usb_acpi_find_companion_for_port(to_usb_port(dev));
259
260 return NULL;
261}
262
263static bool usb_acpi_bus_match(struct device *dev)
264{
265 return is_usb_device(dev) || is_usb_port(dev);
266}
267
268static struct acpi_bus_type usb_acpi_bus = {
269 .name = "USB",
270 .match = usb_acpi_bus_match,
271 .find_companion = usb_acpi_find_companion,
272};
273
274int usb_acpi_register(void)
275{
276 return register_acpi_bus_type(&usb_acpi_bus);
277}
278
279void usb_acpi_unregister(void)
280{
281 unregister_acpi_bus_type(&usb_acpi_bus);
282}
283