aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/core/hub.h2
-rw-r--r--drivers/usb/core/port.c56
-rw-r--r--drivers/usb/core/usb-acpi.c41
-rw-r--r--drivers/usb/core/usb.h6
4 files changed, 83 insertions, 22 deletions
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index fcad5f9d12f0..048c797f394c 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -84,6 +84,7 @@ struct usb_hub {
84 * @port_owner: port's owner 84 * @port_owner: port's owner
85 * @peer: related usb2 and usb3 ports (share the same connector) 85 * @peer: related usb2 and usb3 ports (share the same connector)
86 * @connect_type: port's connect type 86 * @connect_type: port's connect type
87 * @location: opaque representation of platform connector location
87 * @portnum: port index num based one 88 * @portnum: port index num based one
88 * @power_is_on: port's power state 89 * @power_is_on: port's power state
89 * @did_runtime_put: port has done pm_runtime_put(). 90 * @did_runtime_put: port has done pm_runtime_put().
@@ -94,6 +95,7 @@ struct usb_port {
94 struct usb_dev_state *port_owner; 95 struct usb_dev_state *port_owner;
95 struct usb_port *peer; 96 struct usb_port *peer;
96 enum usb_port_connect_type connect_type; 97 enum usb_port_connect_type connect_type;
98 usb_port_location_t location;
97 u8 portnum; 99 u8 portnum;
98 unsigned power_is_on:1; 100 unsigned power_is_on:1;
99 unsigned did_runtime_put:1; 101 unsigned did_runtime_put:1;
diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c
index 9b7496b52f2a..aea54e8dfe47 100644
--- a/drivers/usb/core/port.c
+++ b/drivers/usb/core/port.c
@@ -188,8 +188,42 @@ static void unlink_peers(struct usb_port *left, struct usb_port *right)
188} 188}
189 189
190/* 190/*
191 * Set the default peer port for root hubs, or via the upstream peer 191 * For each usb hub device in the system check to see if it is in the
192 * relationship for all other hubs 192 * peer domain of the given port_dev, and if it is check to see if it
193 * has a port that matches the given port by location
194 */
195static int match_location(struct usb_device *peer_hdev, void *p)
196{
197 int port1;
198 struct usb_hcd *hcd, *peer_hcd;
199 struct usb_port *port_dev = p, *peer;
200 struct usb_hub *peer_hub = usb_hub_to_struct_hub(peer_hdev);
201 struct usb_device *hdev = to_usb_device(port_dev->dev.parent->parent);
202
203 if (!peer_hub)
204 return 0;
205
206 hcd = bus_to_hcd(hdev->bus);
207 peer_hcd = bus_to_hcd(peer_hdev->bus);
208 /* peer_hcd is provisional until we verify it against the known peer */
209 if (peer_hcd != hcd->shared_hcd)
210 return 0;
211
212 for (port1 = 1; port1 <= peer_hdev->maxchild; port1++) {
213 peer = peer_hub->ports[port1 - 1];
214 if (peer && peer->location == port_dev->location) {
215 link_peers(port_dev, peer);
216 return 1; /* done */
217 }
218 }
219
220 return 0;
221}
222
223/*
224 * Find the peer port either via explicit platform firmware "location"
225 * data, the peer hcd for root hubs, or the upstream peer relationship
226 * for all other hubs.
193 */ 227 */
194static void find_and_link_peer(struct usb_hub *hub, int port1) 228static void find_and_link_peer(struct usb_hub *hub, int port1)
195{ 229{
@@ -198,7 +232,17 @@ static void find_and_link_peer(struct usb_hub *hub, int port1)
198 struct usb_device *peer_hdev; 232 struct usb_device *peer_hdev;
199 struct usb_hub *peer_hub; 233 struct usb_hub *peer_hub;
200 234
201 if (!hdev->parent) { 235 /*
236 * If location data is available then we can only peer this port
237 * by a location match, not the default peer (lest we create a
238 * situation where we need to go back and undo a default peering
239 * when the port is later peered by location data)
240 */
241 if (port_dev->location) {
242 /* we link the peer in match_location() if found */
243 usb_for_each_dev(port_dev, match_location);
244 return;
245 } else if (!hdev->parent) {
202 struct usb_hcd *hcd = bus_to_hcd(hdev->bus); 246 struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
203 struct usb_hcd *peer_hcd = hcd->shared_hcd; 247 struct usb_hcd *peer_hcd = hcd->shared_hcd;
204 248
@@ -225,8 +269,12 @@ static void find_and_link_peer(struct usb_hub *hub, int port1)
225 if (!peer_hub || port1 > peer_hdev->maxchild) 269 if (!peer_hub || port1 > peer_hdev->maxchild)
226 return; 270 return;
227 271
272 /*
273 * we found a valid default peer, last check is to make sure it
274 * does not have location data
275 */
228 peer = peer_hub->ports[port1 - 1]; 276 peer = peer_hub->ports[port1 - 1];
229 if (peer) 277 if (peer && peer->location == 0)
230 link_peers(port_dev, peer); 278 link_peers(port_dev, peer);
231} 279}
232 280
diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c
index d3e7e1b4125e..2776cfe64c09 100644
--- a/drivers/usb/core/usb-acpi.c
+++ b/drivers/usb/core/usb-acpi.c
@@ -85,19 +85,13 @@ int usb_acpi_set_power_state(struct usb_device *hdev, int index, bool enable)
85} 85}
86EXPORT_SYMBOL_GPL(usb_acpi_set_power_state); 86EXPORT_SYMBOL_GPL(usb_acpi_set_power_state);
87 87
88static int usb_acpi_check_port_connect_type(struct usb_device *hdev, 88static enum usb_port_connect_type usb_acpi_get_connect_type(acpi_handle handle,
89 acpi_handle handle, int port1) 89 struct acpi_pld_info *pld)
90{ 90{
91 enum usb_port_connect_type connect_type = USB_PORT_CONNECT_TYPE_UNKNOWN; 91 enum usb_port_connect_type connect_type = USB_PORT_CONNECT_TYPE_UNKNOWN;
92 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 92 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
93 struct usb_hub *hub = usb_hub_to_struct_hub(hdev);
94 struct acpi_pld_info *pld;
95 union acpi_object *upc; 93 union acpi_object *upc;
96 acpi_status status; 94 acpi_status status;
97 int ret = 0;
98
99 if (!hub)
100 return 0;
101 95
102 /* 96 /*
103 * According to ACPI Spec 9.13. PLD indicates whether usb port is 97 * According to ACPI Spec 9.13. PLD indicates whether usb port is
@@ -107,15 +101,10 @@ static int usb_acpi_check_port_connect_type(struct usb_device *hdev,
107 * a usb device is directly hard-wired to the port. If no visible and 101 * a usb device is directly hard-wired to the port. If no visible and
108 * no connectable, the port would be not used. 102 * no connectable, the port would be not used.
109 */ 103 */
110 status = acpi_get_physical_device_location(handle, &pld);
111 if (ACPI_FAILURE(status))
112 return -ENODEV;
113
114 status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer); 104 status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer);
115 upc = buffer.pointer; 105 upc = buffer.pointer;
116 if (!upc || (upc->type != ACPI_TYPE_PACKAGE) 106 if (!upc || (upc->type != ACPI_TYPE_PACKAGE)
117 || upc->package.count != 4) { 107 || upc->package.count != 4) {
118 ret = -EINVAL;
119 goto out; 108 goto out;
120 } 109 }
121 110
@@ -126,14 +115,18 @@ static int usb_acpi_check_port_connect_type(struct usb_device *hdev,
126 connect_type = USB_PORT_CONNECT_TYPE_HARD_WIRED; 115 connect_type = USB_PORT_CONNECT_TYPE_HARD_WIRED;
127 else if (!pld->user_visible) 116 else if (!pld->user_visible)
128 connect_type = USB_PORT_NOT_USED; 117 connect_type = USB_PORT_NOT_USED;
129 hub->ports[port1 - 1]->connect_type = connect_type;
130
131out: 118out:
132 ACPI_FREE(pld);
133 kfree(upc); 119 kfree(upc);
134 return ret; 120 return connect_type;
135} 121}
136 122
123
124/*
125 * Private to usb-acpi, all the core needs to know is that
126 * port_dev->location is non-zero when it has been set by the firmware.
127 */
128#define USB_ACPI_LOCATION_VALID (1 << 31)
129
137static struct acpi_device *usb_acpi_find_companion(struct device *dev) 130static struct acpi_device *usb_acpi_find_companion(struct device *dev)
138{ 131{
139 struct usb_device *udev; 132 struct usb_device *udev;
@@ -164,6 +157,9 @@ static struct acpi_device *usb_acpi_find_companion(struct device *dev)
164 } else if (is_usb_port(dev)) { 157 } else if (is_usb_port(dev)) {
165 struct usb_port *port_dev = to_usb_port(dev); 158 struct usb_port *port_dev = to_usb_port(dev);
166 int port1 = port_dev->portnum; 159 int port1 = port_dev->portnum;
160 struct acpi_pld_info *pld;
161 acpi_handle *handle;
162 acpi_status status;
167 163
168 /* Get the struct usb_device point of port's hub */ 164 /* Get the struct usb_device point of port's hub */
169 udev = to_usb_device(dev->parent->parent); 165 udev = to_usb_device(dev->parent->parent);
@@ -194,7 +190,16 @@ static struct acpi_device *usb_acpi_find_companion(struct device *dev)
194 if (!adev) 190 if (!adev)
195 return NULL; 191 return NULL;
196 } 192 }
197 usb_acpi_check_port_connect_type(udev, adev->handle, port1); 193 handle = adev->handle;
194 status = acpi_get_physical_device_location(handle, &pld);
195 if (ACPI_FAILURE(status) || !pld)
196 return adev;
197
198 port_dev->location = USB_ACPI_LOCATION_VALID
199 | pld->group_token << 8 | pld->group_position;
200 port_dev->connect_type = usb_acpi_get_connect_type(handle, pld);
201 ACPI_FREE(pld);
202
198 return adev; 203 return adev;
199 } 204 }
200 205
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 6afa738b5ba4..98dc08e13448 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -171,6 +171,12 @@ extern void usbfs_conn_disc_event(void);
171extern int usb_devio_init(void); 171extern int usb_devio_init(void);
172extern void usb_devio_cleanup(void); 172extern void usb_devio_cleanup(void);
173 173
174/*
175 * Firmware specific cookie identifying a port's location. '0' == no location
176 * data available
177 */
178typedef u32 usb_port_location_t;
179
174/* internal notify stuff */ 180/* internal notify stuff */
175extern void usb_notify_add_device(struct usb_device *udev); 181extern void usb_notify_add_device(struct usb_device *udev);
176extern void usb_notify_remove_device(struct usb_device *udev); 182extern void usb_notify_remove_device(struct usb_device *udev);