1 // SPDX-License-Identifier: GPL-2.0
5 * Copyright 2012 Red Hat <mjg@redhat.com>
7 #include <linux/module.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>
19 * usb_acpi_power_manageable - check whether usb port has
20 * acpi power resource.
21 * @hdev: USB device belonging to the usb hub
22 * @index: port index based zero
24 * Return true if the port has acpi power resource and false if no.
26 bool usb_acpi_power_manageable(struct usb_device
*hdev
, int index
)
28 acpi_handle port_handle
;
29 int port1
= index
+ 1;
31 port_handle
= usb_get_hub_port_acpi_handle(hdev
,
34 return acpi_bus_power_manageable(port_handle
);
38 EXPORT_SYMBOL_GPL(usb_acpi_power_manageable
);
41 * usb_acpi_set_power_state - control usb port's power via acpi power
43 * @hdev: USB device belonging to the usb hub
44 * @index: port index based zero
45 * @enable: power state expected to be set
47 * Notice to use usb_acpi_power_manageable() to check whether the usb port
48 * has acpi power resource before invoking this function.
50 * Returns 0 on success, else negative errno.
52 int usb_acpi_set_power_state(struct usb_device
*hdev
, int index
, bool enable
)
54 struct usb_hub
*hub
= usb_hub_to_struct_hub(hdev
);
55 struct usb_port
*port_dev
;
56 acpi_handle port_handle
;
58 int port1
= index
+ 1;
63 port_dev
= hub
->ports
[port1
- 1];
65 port_handle
= (acpi_handle
) usb_get_hub_port_acpi_handle(hdev
, port1
);
70 state
= ACPI_STATE_D0
;
72 state
= ACPI_STATE_D3_COLD
;
74 error
= acpi_bus_set_power(port_handle
, state
);
76 dev_dbg(&port_dev
->dev
, "acpi: power was set to %d\n", enable
);
78 dev_dbg(&port_dev
->dev
, "acpi: power failed to be set\n");
82 EXPORT_SYMBOL_GPL(usb_acpi_set_power_state
);
84 static enum usb_port_connect_type
usb_acpi_get_connect_type(acpi_handle handle
,
85 struct acpi_pld_info
*pld
)
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
;
93 * According to 9.14 in ACPI Spec 6.2. _PLD indicates whether usb port
94 * is user visible and _UPC indicates whether it is connectable. If
95 * the port was visible and connectable, it could be freely connected
96 * and disconnected with USB devices. If no visible and connectable,
97 * a usb device is directly hard-wired to the port. If no visible and
98 * no connectable, the port would be not used.
100 status
= acpi_evaluate_object(handle
, "_UPC", NULL
, &buffer
);
101 if (ACPI_FAILURE(status
))
104 upc
= buffer
.pointer
;
105 if (!upc
|| (upc
->type
!= ACPI_TYPE_PACKAGE
) || upc
->package
.count
!= 4)
108 if (upc
->package
.elements
[0].integer
.value
)
109 if (pld
->user_visible
)
110 connect_type
= USB_PORT_CONNECT_TYPE_HOT_PLUG
;
112 connect_type
= USB_PORT_CONNECT_TYPE_HARD_WIRED
;
113 else if (!pld
->user_visible
)
114 connect_type
= USB_PORT_NOT_USED
;
122 * Private to usb-acpi, all the core needs to know is that
123 * port_dev->location is non-zero when it has been set by the firmware.
125 #define USB_ACPI_LOCATION_VALID (1 << 31)
127 static struct acpi_device
*usb_acpi_find_port(struct acpi_device
*parent
,
130 struct acpi_device
*adev
;
135 list_for_each_entry(adev
, &parent
->children
, node
) {
136 if (acpi_device_adr(adev
) == raw
)
140 return acpi_find_child_device(parent
, raw
, false);
143 static struct acpi_device
*
144 usb_acpi_get_companion_for_port(struct usb_port
*port_dev
)
146 struct usb_device
*udev
;
147 struct acpi_device
*adev
;
148 acpi_handle
*parent_handle
;
151 /* Get the struct usb_device point of port's hub */
152 udev
= to_usb_device(port_dev
->dev
.parent
->parent
);
155 * The root hub ports' parent is the root hub. The non-root-hub
156 * ports' parent is the parent hub port which the hub is
160 adev
= ACPI_COMPANION(&udev
->dev
);
161 port1
= usb_hcd_find_raw_port_number(bus_to_hcd(udev
->bus
),
164 parent_handle
= usb_get_hub_port_acpi_handle(udev
->parent
,
169 acpi_bus_get_device(parent_handle
, &adev
);
170 port1
= port_dev
->portnum
;
173 return usb_acpi_find_port(adev
, port1
);
176 static struct acpi_device
*
177 usb_acpi_find_companion_for_port(struct usb_port
*port_dev
)
179 struct acpi_device
*adev
;
180 struct acpi_pld_info
*pld
;
184 adev
= usb_acpi_get_companion_for_port(port_dev
);
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
);
200 static struct acpi_device
*
201 usb_acpi_find_companion_for_device(struct usb_device
*udev
)
203 struct acpi_device
*adev
;
204 struct usb_port
*port_dev
;
208 /* root hub is only child (_ADR=0) under its parent, the HC */
209 adev
= ACPI_COMPANION(udev
->dev
.parent
);
210 return acpi_find_child_device(adev
, 0, false);
213 hub
= usb_hub_to_struct_hub(udev
->parent
);
218 * This is an embedded USB device connected to a port and such
219 * devices share port's ACPI companion.
221 port_dev
= hub
->ports
[udev
->portnum
- 1];
222 return usb_acpi_get_companion_for_port(port_dev
);
225 static struct acpi_device
*usb_acpi_find_companion(struct device
*dev
)
228 * The USB hierarchy like following:
239 * where HUBN is root hub, and PRNN are USB ports and devices
240 * connected to them, and FNNN are individualk functions for
241 * connected composite USB devices. PRNN and FNNN may contain
242 * _CRS and other methods describing sideband resources for
243 * the connected device.
245 * On the kernel side both root hub and embedded USB devices are
246 * represented as instances of usb_device structure, and ports
247 * are represented as usb_port structures, so the whole process
248 * is split into 2 parts: finding companions for devices and
249 * finding companions for ports.
251 * Note that we do not handle individual functions of composite
252 * devices yet, for that we would need to assign companions to
253 * devices corresponding to USB interfaces.
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
));
263 static bool usb_acpi_bus_match(struct device
*dev
)
265 return is_usb_device(dev
) || is_usb_port(dev
);
268 static struct acpi_bus_type usb_acpi_bus
= {
270 .match
= usb_acpi_bus_match
,
271 .find_companion
= usb_acpi_find_companion
,
274 int usb_acpi_register(void)
276 return register_acpi_bus_type(&usb_acpi_bus
);
279 void usb_acpi_unregister(void)
281 unregister_acpi_bus_type(&usb_acpi_bus
);