diff options
author | Sarah Sharp <sarah.a.sharp@linux.intel.com> | 2010-10-28 18:40:26 -0400 |
---|---|---|
committer | Sarah Sharp <sarah.a.sharp@linux.intel.com> | 2011-03-13 21:23:06 -0400 |
commit | c56354378426e550aaf6ddf3983f502a8fddeab5 (patch) | |
tree | 35464da7692b5ac096bc05841b2dc4761e711117 /drivers/usb/core | |
parent | 83de4b2b90887b5b317d8313864fe4cc5db35280 (diff) |
usb: Make core allocate resources per PCI-device.
Introduce the notion of a PCI device that may be associated with more than
one USB host controller driver (struct usb_hcd). This patch is the start
of the work to separate the xHCI host controller into two roothubs: a USB
3.0 roothub with SuperSpeed-only ports, and a USB 2.0 roothub with
HS/FS/LS ports.
One usb_hcd structure is designated to be the "primary HCD", and a pointer
is added to the usb_hcd structure to keep track of that. A new function
call, usb_hcd_is_primary_hcd() is added to check whether the USB hcd is
marked as the primary HCD (or if it is not part of a roothub pair). To
allow the USB core and xHCI driver to access either roothub in a pair, a
"shared_hcd" pointer is added to the usb_hcd structure.
Add a new function, usb_create_shared_hcd(), that does roothub allocation
for paired roothubs. It will act just like usb_create_hcd() did if the
primary_hcd pointer argument is NULL. If it is passed a non-NULL
primary_hcd pointer, it sets usb_hcd->shared_hcd and usb_hcd->primary_hcd
fields. It will also skip the bandwidth_mutex allocation, and set the
secondary hcd's bandwidth_mutex pointer to the primary HCD's mutex.
IRQs are only allocated once for the primary roothub.
Introduce a new usb_hcd driver flag that indicates the host controller
driver wants to create two roothubs. If the HCD_SHARED flag is set, then
the USB core PCI probe methods will allocate a second roothub, and make
sure that second roothub gets freed during rmmod and in initialization
error paths.
When usb_hc_died() is called with the primary HCD, make sure that any
roothubs that share that host controller are also marked as being dead.
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/hcd.c | 108 |
1 files changed, 89 insertions, 19 deletions
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index a0adcac3da0..ba15eeab824 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c | |||
@@ -2143,7 +2143,9 @@ EXPORT_SYMBOL_GPL(usb_hcd_irq); | |||
2143 | * | 2143 | * |
2144 | * This is called by bus glue to report a USB host controller that died | 2144 | * This is called by bus glue to report a USB host controller that died |
2145 | * while operations may still have been pending. It's called automatically | 2145 | * while operations may still have been pending. It's called automatically |
2146 | * by the PCI glue, so only glue for non-PCI busses should need to call it. | 2146 | * by the PCI glue, so only glue for non-PCI busses should need to call it. |
2147 | * | ||
2148 | * Only call this function with the primary HCD. | ||
2147 | */ | 2149 | */ |
2148 | void usb_hc_died (struct usb_hcd *hcd) | 2150 | void usb_hc_died (struct usb_hcd *hcd) |
2149 | { | 2151 | { |
@@ -2162,17 +2164,31 @@ void usb_hc_died (struct usb_hcd *hcd) | |||
2162 | USB_STATE_NOTATTACHED); | 2164 | USB_STATE_NOTATTACHED); |
2163 | usb_kick_khubd (hcd->self.root_hub); | 2165 | usb_kick_khubd (hcd->self.root_hub); |
2164 | } | 2166 | } |
2167 | if (usb_hcd_is_primary_hcd(hcd) && hcd->shared_hcd) { | ||
2168 | hcd = hcd->shared_hcd; | ||
2169 | if (hcd->rh_registered) { | ||
2170 | clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); | ||
2171 | |||
2172 | /* make khubd clean up old urbs and devices */ | ||
2173 | usb_set_device_state(hcd->self.root_hub, | ||
2174 | USB_STATE_NOTATTACHED); | ||
2175 | usb_kick_khubd(hcd->self.root_hub); | ||
2176 | } | ||
2177 | } | ||
2165 | spin_unlock_irqrestore (&hcd_root_hub_lock, flags); | 2178 | spin_unlock_irqrestore (&hcd_root_hub_lock, flags); |
2179 | /* Make sure that the other roothub is also deallocated. */ | ||
2166 | } | 2180 | } |
2167 | EXPORT_SYMBOL_GPL (usb_hc_died); | 2181 | EXPORT_SYMBOL_GPL (usb_hc_died); |
2168 | 2182 | ||
2169 | /*-------------------------------------------------------------------------*/ | 2183 | /*-------------------------------------------------------------------------*/ |
2170 | 2184 | ||
2171 | /** | 2185 | /** |
2172 | * usb_create_hcd - create and initialize an HCD structure | 2186 | * usb_create_shared_hcd - create and initialize an HCD structure |
2173 | * @driver: HC driver that will use this hcd | 2187 | * @driver: HC driver that will use this hcd |
2174 | * @dev: device for this HC, stored in hcd->self.controller | 2188 | * @dev: device for this HC, stored in hcd->self.controller |
2175 | * @bus_name: value to store in hcd->self.bus_name | 2189 | * @bus_name: value to store in hcd->self.bus_name |
2190 | * @primary_hcd: a pointer to the usb_hcd structure that is sharing the | ||
2191 | * PCI device. Only allocate certain resources for the primary HCD | ||
2176 | * Context: !in_interrupt() | 2192 | * Context: !in_interrupt() |
2177 | * | 2193 | * |
2178 | * Allocate a struct usb_hcd, with extra space at the end for the | 2194 | * Allocate a struct usb_hcd, with extra space at the end for the |
@@ -2181,8 +2197,9 @@ EXPORT_SYMBOL_GPL (usb_hc_died); | |||
2181 | * | 2197 | * |
2182 | * If memory is unavailable, returns NULL. | 2198 | * If memory is unavailable, returns NULL. |
2183 | */ | 2199 | */ |
2184 | struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, | 2200 | struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver, |
2185 | struct device *dev, const char *bus_name) | 2201 | struct device *dev, const char *bus_name, |
2202 | struct usb_hcd *primary_hcd) | ||
2186 | { | 2203 | { |
2187 | struct usb_hcd *hcd; | 2204 | struct usb_hcd *hcd; |
2188 | 2205 | ||
@@ -2191,16 +2208,24 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, | |||
2191 | dev_dbg (dev, "hcd alloc failed\n"); | 2208 | dev_dbg (dev, "hcd alloc failed\n"); |
2192 | return NULL; | 2209 | return NULL; |
2193 | } | 2210 | } |
2194 | hcd->bandwidth_mutex = kmalloc(sizeof(*hcd->bandwidth_mutex), | 2211 | if (primary_hcd == NULL) { |
2195 | GFP_KERNEL); | 2212 | hcd->bandwidth_mutex = kmalloc(sizeof(*hcd->bandwidth_mutex), |
2196 | if (!hcd->bandwidth_mutex) { | 2213 | GFP_KERNEL); |
2197 | kfree(hcd); | 2214 | if (!hcd->bandwidth_mutex) { |
2198 | dev_dbg(dev, "hcd bandwidth mutex alloc failed\n"); | 2215 | kfree(hcd); |
2199 | return NULL; | 2216 | dev_dbg(dev, "hcd bandwidth mutex alloc failed\n"); |
2217 | return NULL; | ||
2218 | } | ||
2219 | mutex_init(hcd->bandwidth_mutex); | ||
2220 | dev_set_drvdata(dev, hcd); | ||
2221 | } else { | ||
2222 | hcd->bandwidth_mutex = primary_hcd->bandwidth_mutex; | ||
2223 | hcd->primary_hcd = primary_hcd; | ||
2224 | primary_hcd->primary_hcd = primary_hcd; | ||
2225 | hcd->shared_hcd = primary_hcd; | ||
2226 | primary_hcd->shared_hcd = hcd; | ||
2200 | } | 2227 | } |
2201 | mutex_init(hcd->bandwidth_mutex); | ||
2202 | 2228 | ||
2203 | dev_set_drvdata(dev, hcd); | ||
2204 | kref_init(&hcd->kref); | 2229 | kref_init(&hcd->kref); |
2205 | 2230 | ||
2206 | usb_bus_init(&hcd->self); | 2231 | usb_bus_init(&hcd->self); |
@@ -2221,13 +2246,46 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, | |||
2221 | "USB Host Controller"; | 2246 | "USB Host Controller"; |
2222 | return hcd; | 2247 | return hcd; |
2223 | } | 2248 | } |
2249 | EXPORT_SYMBOL_GPL(usb_create_shared_hcd); | ||
2250 | |||
2251 | /** | ||
2252 | * usb_create_hcd - create and initialize an HCD structure | ||
2253 | * @driver: HC driver that will use this hcd | ||
2254 | * @dev: device for this HC, stored in hcd->self.controller | ||
2255 | * @bus_name: value to store in hcd->self.bus_name | ||
2256 | * Context: !in_interrupt() | ||
2257 | * | ||
2258 | * Allocate a struct usb_hcd, with extra space at the end for the | ||
2259 | * HC driver's private data. Initialize the generic members of the | ||
2260 | * hcd structure. | ||
2261 | * | ||
2262 | * If memory is unavailable, returns NULL. | ||
2263 | */ | ||
2264 | struct usb_hcd *usb_create_hcd(const struct hc_driver *driver, | ||
2265 | struct device *dev, const char *bus_name) | ||
2266 | { | ||
2267 | return usb_create_shared_hcd(driver, dev, bus_name, NULL); | ||
2268 | } | ||
2224 | EXPORT_SYMBOL_GPL(usb_create_hcd); | 2269 | EXPORT_SYMBOL_GPL(usb_create_hcd); |
2225 | 2270 | ||
2271 | /* | ||
2272 | * Roothubs that share one PCI device must also share the bandwidth mutex. | ||
2273 | * Don't deallocate the bandwidth_mutex until the last shared usb_hcd is | ||
2274 | * deallocated. | ||
2275 | * | ||
2276 | * Make sure to only deallocate the bandwidth_mutex when the primary HCD is | ||
2277 | * freed. When hcd_release() is called for the non-primary HCD, set the | ||
2278 | * primary_hcd's shared_hcd pointer to null (since the non-primary HCD will be | ||
2279 | * freed shortly). | ||
2280 | */ | ||
2226 | static void hcd_release (struct kref *kref) | 2281 | static void hcd_release (struct kref *kref) |
2227 | { | 2282 | { |
2228 | struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref); | 2283 | struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref); |
2229 | 2284 | ||
2230 | kfree(hcd->bandwidth_mutex); | 2285 | if (usb_hcd_is_primary_hcd(hcd)) |
2286 | kfree(hcd->bandwidth_mutex); | ||
2287 | else | ||
2288 | hcd->shared_hcd->shared_hcd = NULL; | ||
2231 | kfree(hcd); | 2289 | kfree(hcd); |
2232 | } | 2290 | } |
2233 | 2291 | ||
@@ -2246,6 +2304,14 @@ void usb_put_hcd (struct usb_hcd *hcd) | |||
2246 | } | 2304 | } |
2247 | EXPORT_SYMBOL_GPL(usb_put_hcd); | 2305 | EXPORT_SYMBOL_GPL(usb_put_hcd); |
2248 | 2306 | ||
2307 | int usb_hcd_is_primary_hcd(struct usb_hcd *hcd) | ||
2308 | { | ||
2309 | if (!hcd->primary_hcd) | ||
2310 | return 1; | ||
2311 | return hcd == hcd->primary_hcd; | ||
2312 | } | ||
2313 | EXPORT_SYMBOL_GPL(usb_hcd_is_primary_hcd); | ||
2314 | |||
2249 | static int usb_hcd_request_irqs(struct usb_hcd *hcd, | 2315 | static int usb_hcd_request_irqs(struct usb_hcd *hcd, |
2250 | unsigned int irqnum, unsigned long irqflags) | 2316 | unsigned int irqnum, unsigned long irqflags) |
2251 | { | 2317 | { |
@@ -2367,9 +2433,11 @@ int usb_add_hcd(struct usb_hcd *hcd, | |||
2367 | dev_dbg(hcd->self.controller, "supports USB remote wakeup\n"); | 2433 | dev_dbg(hcd->self.controller, "supports USB remote wakeup\n"); |
2368 | 2434 | ||
2369 | /* enable irqs just before we start the controller */ | 2435 | /* enable irqs just before we start the controller */ |
2370 | retval = usb_hcd_request_irqs(hcd, irqnum, irqflags); | 2436 | if (usb_hcd_is_primary_hcd(hcd)) { |
2371 | if (retval) | 2437 | retval = usb_hcd_request_irqs(hcd, irqnum, irqflags); |
2372 | goto err_request_irq; | 2438 | if (retval) |
2439 | goto err_request_irq; | ||
2440 | } | ||
2373 | 2441 | ||
2374 | hcd->state = HC_STATE_RUNNING; | 2442 | hcd->state = HC_STATE_RUNNING; |
2375 | retval = hcd->driver->start(hcd); | 2443 | retval = hcd->driver->start(hcd); |
@@ -2416,7 +2484,7 @@ err_register_root_hub: | |||
2416 | clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); | 2484 | clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); |
2417 | del_timer_sync(&hcd->rh_timer); | 2485 | del_timer_sync(&hcd->rh_timer); |
2418 | err_hcd_driver_start: | 2486 | err_hcd_driver_start: |
2419 | if (hcd->irq >= 0) | 2487 | if (usb_hcd_is_primary_hcd(hcd) && hcd->irq >= 0) |
2420 | free_irq(irqnum, hcd); | 2488 | free_irq(irqnum, hcd); |
2421 | err_request_irq: | 2489 | err_request_irq: |
2422 | err_hcd_driver_setup: | 2490 | err_hcd_driver_setup: |
@@ -2480,8 +2548,10 @@ void usb_remove_hcd(struct usb_hcd *hcd) | |||
2480 | clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); | 2548 | clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); |
2481 | del_timer_sync(&hcd->rh_timer); | 2549 | del_timer_sync(&hcd->rh_timer); |
2482 | 2550 | ||
2483 | if (hcd->irq >= 0) | 2551 | if (usb_hcd_is_primary_hcd(hcd)) { |
2484 | free_irq(hcd->irq, hcd); | 2552 | if (hcd->irq >= 0) |
2553 | free_irq(hcd->irq, hcd); | ||
2554 | } | ||
2485 | 2555 | ||
2486 | usb_put_dev(hcd->self.root_hub); | 2556 | usb_put_dev(hcd->self.root_hub); |
2487 | usb_deregister_bus(&hcd->self); | 2557 | usb_deregister_bus(&hcd->self); |