Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[linux-drm-fsl-dcu.git] / drivers / usb / core / usb-acpi.c
1 /*
2  * USB-ACPI glue code
3  *
4  * Copyright 2012 Red Hat <mjg@redhat.com>
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the Free
8  * Software Foundation, version 2.
9  *
10  */
11 #include <linux/module.h>
12 #include <linux/usb.h>
13 #include <linux/device.h>
14 #include <linux/errno.h>
15 #include <linux/kernel.h>
16 #include <linux/acpi.h>
17 #include <linux/pci.h>
18 #include <linux/usb/hcd.h>
19 #include <acpi/acpi_bus.h>
20
21 #include "usb.h"
22
23 /**
24  * usb_acpi_power_manageable - check whether usb port has
25  * acpi power resource.
26  * @hdev: USB device belonging to the usb hub
27  * @index: port index based zero
28  *
29  * Return true if the port has acpi power resource and false if no.
30  */
31 bool usb_acpi_power_manageable(struct usb_device *hdev, int index)
32 {
33         acpi_handle port_handle;
34         int port1 = index + 1;
35
36         port_handle = usb_get_hub_port_acpi_handle(hdev,
37                 port1);
38         if (port_handle)
39                 return acpi_bus_power_manageable(port_handle);
40         else
41                 return false;
42 }
43 EXPORT_SYMBOL_GPL(usb_acpi_power_manageable);
44
45 /**
46  * usb_acpi_set_power_state - control usb port's power via acpi power
47  * resource
48  * @hdev: USB device belonging to the usb hub
49  * @index: port index based zero
50  * @enable: power state expected to be set
51  *
52  * Notice to use usb_acpi_power_manageable() to check whether the usb port
53  * has acpi power resource before invoking this function.
54  *
55  * Returns 0 on success, else negative errno.
56  */
57 int usb_acpi_set_power_state(struct usb_device *hdev, int index, bool enable)
58 {
59         acpi_handle port_handle;
60         unsigned char state;
61         int port1 = index + 1;
62         int error = -EINVAL;
63
64         port_handle = (acpi_handle)usb_get_hub_port_acpi_handle(hdev,
65                 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(&hdev->dev, "The power of hub port %d was set to %d\n",
77                         port1, enable);
78         else
79                 dev_dbg(&hdev->dev, "The power of hub port failed to be set\n");
80
81         return error;
82 }
83 EXPORT_SYMBOL_GPL(usb_acpi_set_power_state);
84
85 static int usb_acpi_check_port_connect_type(struct usb_device *hdev,
86         acpi_handle handle, int port1)
87 {
88         acpi_status status;
89         struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
90         union acpi_object *upc;
91         struct acpi_pld_info *pld;
92         int ret = 0;
93
94         /*
95          * Accoding to ACPI Spec 9.13. PLD indicates whether usb port is
96          * user visible and _UPC indicates whether it is connectable. If
97          * the port was visible and connectable, it could be freely connected
98          * and disconnected with USB devices. If no visible and connectable,
99          * a usb device is directly hard-wired to the port. If no visible and
100          * no connectable, the port would be not used.
101          */
102         status = acpi_get_physical_device_location(handle, &pld);
103         if (ACPI_FAILURE(status))
104                 return -ENODEV;
105
106         status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer);
107         upc = buffer.pointer;
108         if (!upc || (upc->type != ACPI_TYPE_PACKAGE)
109                 || upc->package.count != 4) {
110                 ret = -EINVAL;
111                 goto out;
112         }
113
114         if (upc->package.elements[0].integer.value)
115                 if (pld->user_visible)
116                         usb_set_hub_port_connect_type(hdev, port1,
117                                 USB_PORT_CONNECT_TYPE_HOT_PLUG);
118                 else
119                         usb_set_hub_port_connect_type(hdev, port1,
120                                 USB_PORT_CONNECT_TYPE_HARD_WIRED);
121         else if (!pld->user_visible)
122                 usb_set_hub_port_connect_type(hdev, port1, USB_PORT_NOT_USED);
123
124 out:
125         ACPI_FREE(pld);
126         kfree(upc);
127         return ret;
128 }
129
130 static int usb_acpi_find_device(struct device *dev, acpi_handle *handle)
131 {
132         struct usb_device *udev;
133         acpi_handle *parent_handle;
134         int port_num;
135
136         /*
137          * In the ACPI DSDT table, only usb root hub and usb ports are
138          * acpi device nodes. The hierarchy like following.
139          * Device (EHC1)
140          *      Device (HUBN)
141          *              Device (PR01)
142          *                      Device (PR11)
143          *                      Device (PR12)
144          *                      Device (PR13)
145          *                      ...
146          * So all binding process is divided into two parts. binding
147          * root hub and usb ports.
148          */
149         if (is_usb_device(dev)) {
150                 udev = to_usb_device(dev);
151                 if (udev->parent) {
152                         enum usb_port_connect_type type;
153
154                         /*
155                          * According usb port's connect type to set usb device's
156                          * removability.
157                          */
158                         type = usb_get_hub_port_connect_type(udev->parent,
159                                 udev->portnum);
160                         switch (type) {
161                         case USB_PORT_CONNECT_TYPE_HOT_PLUG:
162                                 udev->removable = USB_DEVICE_REMOVABLE;
163                                 break;
164                         case USB_PORT_CONNECT_TYPE_HARD_WIRED:
165                                 udev->removable = USB_DEVICE_FIXED;
166                                 break;
167                         default:
168                                 udev->removable = USB_DEVICE_REMOVABLE_UNKNOWN;
169                                 break;
170                         }
171
172                         return -ENODEV;
173                 }
174
175                 /* root hub's parent is the usb hcd. */
176                 parent_handle = ACPI_HANDLE(dev->parent);
177                 *handle = acpi_get_child(parent_handle, udev->portnum);
178                 if (!*handle)
179                         return -ENODEV;
180                 return 0;
181         } else if (is_usb_port(dev)) {
182                 sscanf(dev_name(dev), "port%d", &port_num);
183                 /* Get the struct usb_device point of port's hub */
184                 udev = to_usb_device(dev->parent->parent);
185
186                 /*
187                  * The root hub ports' parent is the root hub. The non-root-hub
188                  * ports' parent is the parent hub port which the hub is
189                  * connected to.
190                  */
191                 if (!udev->parent) {
192                         struct usb_hcd *hcd = bus_to_hcd(udev->bus);
193                         int raw_port_num;
194
195                         raw_port_num = usb_hcd_find_raw_port_number(hcd,
196                                 port_num);
197                         *handle = acpi_get_child(ACPI_HANDLE(&udev->dev),
198                                 raw_port_num);
199                         if (!*handle)
200                                 return -ENODEV;
201                 } else {
202                         parent_handle =
203                                 usb_get_hub_port_acpi_handle(udev->parent,
204                                 udev->portnum);
205                         if (!parent_handle)
206                                 return -ENODEV;
207
208                         *handle = acpi_get_child(parent_handle, port_num);
209                         if (!*handle)
210                                 return -ENODEV;
211                 }
212                 usb_acpi_check_port_connect_type(udev, *handle, port_num);
213         } else
214                 return -ENODEV;
215
216         return 0;
217 }
218
219 static bool usb_acpi_bus_match(struct device *dev)
220 {
221         return is_usb_device(dev) || is_usb_port(dev);
222 }
223
224 static struct acpi_bus_type usb_acpi_bus = {
225         .name = "USB",
226         .match = usb_acpi_bus_match,
227         .find_device = usb_acpi_find_device,
228 };
229
230 int usb_acpi_register(void)
231 {
232         return register_acpi_bus_type(&usb_acpi_bus);
233 }
234
235 void usb_acpi_unregister(void)
236 {
237         unregister_acpi_bus_type(&usb_acpi_bus);
238 }