diff options
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/core/hub.h | 2 | ||||
-rw-r--r-- | drivers/usb/core/port.c | 56 | ||||
-rw-r--r-- | drivers/usb/core/usb-acpi.c | 41 | ||||
-rw-r--r-- | drivers/usb/core/usb.h | 6 |
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 | */ | ||
195 | static 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 | */ |
194 | static void find_and_link_peer(struct usb_hub *hub, int port1) | 228 | static 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 | } |
86 | EXPORT_SYMBOL_GPL(usb_acpi_set_power_state); | 86 | EXPORT_SYMBOL_GPL(usb_acpi_set_power_state); |
87 | 87 | ||
88 | static int usb_acpi_check_port_connect_type(struct usb_device *hdev, | 88 | static 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 | |||
131 | out: | 118 | out: |
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 | |||
137 | static struct acpi_device *usb_acpi_find_companion(struct device *dev) | 130 | static 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); | |||
171 | extern int usb_devio_init(void); | 171 | extern int usb_devio_init(void); |
172 | extern void usb_devio_cleanup(void); | 172 | extern void usb_devio_cleanup(void); |
173 | 173 | ||
174 | /* | ||
175 | * Firmware specific cookie identifying a port's location. '0' == no location | ||
176 | * data available | ||
177 | */ | ||
178 | typedef u32 usb_port_location_t; | ||
179 | |||
174 | /* internal notify stuff */ | 180 | /* internal notify stuff */ |
175 | extern void usb_notify_add_device(struct usb_device *udev); | 181 | extern void usb_notify_add_device(struct usb_device *udev); |
176 | extern void usb_notify_remove_device(struct usb_device *udev); | 182 | extern void usb_notify_remove_device(struct usb_device *udev); |