USB: force handover port to companion when hub_port_connect_change fails
This patch hands over the port to the companion when the hub_port_connect_change fails. Signed-off-by: Balaji Rao <balajirrao@gmail.com> Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
eb0be47dbb
commit
90da096ee4
|
@ -210,6 +210,9 @@ struct hc_driver {
|
||||||
int (*start_port_reset)(struct usb_hcd *, unsigned port_num);
|
int (*start_port_reset)(struct usb_hcd *, unsigned port_num);
|
||||||
void (*hub_irq_enable)(struct usb_hcd *);
|
void (*hub_irq_enable)(struct usb_hcd *);
|
||||||
/* Needed only if port-change IRQs are level-triggered */
|
/* Needed only if port-change IRQs are level-triggered */
|
||||||
|
|
||||||
|
/* force handover of high-speed port to full-speed companion */
|
||||||
|
void (*relinquish_port)(struct usb_hcd *, int);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb);
|
extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb);
|
||||||
|
|
|
@ -2482,6 +2482,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
|
||||||
{
|
{
|
||||||
struct usb_device *hdev = hub->hdev;
|
struct usb_device *hdev = hub->hdev;
|
||||||
struct device *hub_dev = hub->intfdev;
|
struct device *hub_dev = hub->intfdev;
|
||||||
|
struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
|
||||||
u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
|
u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
|
||||||
int status, i;
|
int status, i;
|
||||||
|
|
||||||
|
@ -2645,6 +2646,8 @@ loop:
|
||||||
|
|
||||||
done:
|
done:
|
||||||
hub_port_disable(hub, port1, 1);
|
hub_port_disable(hub, port1, 1);
|
||||||
|
if (hcd->driver->relinquish_port && !hub->hdev->parent)
|
||||||
|
hcd->driver->relinquish_port(hcd, port1);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hub_events(void)
|
static void hub_events(void)
|
||||||
|
|
|
@ -222,6 +222,7 @@ static const struct hc_driver ehci_au1xxx_hc_driver = {
|
||||||
.hub_control = ehci_hub_control,
|
.hub_control = ehci_hub_control,
|
||||||
.bus_suspend = ehci_bus_suspend,
|
.bus_suspend = ehci_bus_suspend,
|
||||||
.bus_resume = ehci_bus_resume,
|
.bus_resume = ehci_bus_resume,
|
||||||
|
.relinquish_port = ehci_relinquish_port,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
|
@ -323,6 +323,7 @@ static const struct hc_driver ehci_fsl_hc_driver = {
|
||||||
.hub_control = ehci_hub_control,
|
.hub_control = ehci_hub_control,
|
||||||
.bus_suspend = ehci_bus_suspend,
|
.bus_suspend = ehci_bus_suspend,
|
||||||
.bus_resume = ehci_bus_resume,
|
.bus_resume = ehci_bus_resume,
|
||||||
|
.relinquish_port = ehci_relinquish_port,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int ehci_fsl_drv_probe(struct platform_device *pdev)
|
static int ehci_fsl_drv_probe(struct platform_device *pdev)
|
||||||
|
|
|
@ -314,41 +314,21 @@ static ssize_t show_companion(struct device *dev,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Dedicate or undedicate a port to the companion controller.
|
* Sets the owner of a port
|
||||||
* Syntax is "[-]portnum", where a leading '-' sign means
|
|
||||||
* return control of the port to the EHCI controller.
|
|
||||||
*/
|
*/
|
||||||
static ssize_t store_companion(struct device *dev,
|
static void set_owner(struct ehci_hcd *ehci, int portnum, int new_owner)
|
||||||
struct device_attribute *attr,
|
|
||||||
const char *buf, size_t count)
|
|
||||||
{
|
{
|
||||||
struct ehci_hcd *ehci;
|
|
||||||
int portnum, new_owner, try;
|
|
||||||
u32 __iomem *status_reg;
|
u32 __iomem *status_reg;
|
||||||
u32 port_status;
|
u32 port_status;
|
||||||
|
int try;
|
||||||
|
|
||||||
ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev)));
|
status_reg = &ehci->regs->port_status[portnum];
|
||||||
new_owner = PORT_OWNER; /* Owned by companion */
|
|
||||||
if (sscanf(buf, "%d", &portnum) != 1)
|
|
||||||
return -EINVAL;
|
|
||||||
if (portnum < 0) {
|
|
||||||
portnum = - portnum;
|
|
||||||
new_owner = 0; /* Owned by EHCI */
|
|
||||||
}
|
|
||||||
if (portnum <= 0 || portnum > HCS_N_PORTS(ehci->hcs_params))
|
|
||||||
return -ENOENT;
|
|
||||||
status_reg = &ehci->regs->port_status[--portnum];
|
|
||||||
if (new_owner)
|
|
||||||
set_bit(portnum, &ehci->companion_ports);
|
|
||||||
else
|
|
||||||
clear_bit(portnum, &ehci->companion_ports);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The controller won't set the OWNER bit if the port is
|
* The controller won't set the OWNER bit if the port is
|
||||||
* enabled, so this loop will sometimes require at least two
|
* enabled, so this loop will sometimes require at least two
|
||||||
* iterations: one to disable the port and one to set OWNER.
|
* iterations: one to disable the port and one to set OWNER.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
for (try = 4; try > 0; --try) {
|
for (try = 4; try > 0; --try) {
|
||||||
spin_lock_irq(&ehci->lock);
|
spin_lock_irq(&ehci->lock);
|
||||||
port_status = ehci_readl(ehci, status_reg);
|
port_status = ehci_readl(ehci, status_reg);
|
||||||
|
@ -365,6 +345,36 @@ static ssize_t store_companion(struct device *dev,
|
||||||
if (try > 1)
|
if (try > 1)
|
||||||
msleep(5);
|
msleep(5);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Dedicate or undedicate a port to the companion controller.
|
||||||
|
* Syntax is "[-]portnum", where a leading '-' sign means
|
||||||
|
* return control of the port to the EHCI controller.
|
||||||
|
*/
|
||||||
|
static ssize_t store_companion(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buf, size_t count)
|
||||||
|
{
|
||||||
|
struct ehci_hcd *ehci;
|
||||||
|
int portnum, new_owner;
|
||||||
|
|
||||||
|
ehci = hcd_to_ehci(bus_to_hcd(dev_get_drvdata(dev)));
|
||||||
|
new_owner = PORT_OWNER; /* Owned by companion */
|
||||||
|
if (sscanf(buf, "%d", &portnum) != 1)
|
||||||
|
return -EINVAL;
|
||||||
|
if (portnum < 0) {
|
||||||
|
portnum = - portnum;
|
||||||
|
new_owner = 0; /* Owned by EHCI */
|
||||||
|
}
|
||||||
|
if (portnum <= 0 || portnum > HCS_N_PORTS(ehci->hcs_params))
|
||||||
|
return -ENOENT;
|
||||||
|
portnum--;
|
||||||
|
if (new_owner)
|
||||||
|
set_bit(portnum, &ehci->companion_ports);
|
||||||
|
else
|
||||||
|
clear_bit(portnum, &ehci->companion_ports);
|
||||||
|
set_owner(ehci, portnum, new_owner);
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
static DEVICE_ATTR(companion, 0644, show_companion, store_companion);
|
static DEVICE_ATTR(companion, 0644, show_companion, store_companion);
|
||||||
|
@ -867,3 +877,13 @@ error:
|
||||||
spin_unlock_irqrestore (&ehci->lock, flags);
|
spin_unlock_irqrestore (&ehci->lock, flags);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ehci_relinquish_port(struct usb_hcd *hcd, int portnum)
|
||||||
|
{
|
||||||
|
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||||
|
|
||||||
|
if (ehci_is_TDI(ehci))
|
||||||
|
return;
|
||||||
|
set_owner(ehci, --portnum, PORT_OWNER);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -364,6 +364,7 @@ static const struct hc_driver ehci_pci_hc_driver = {
|
||||||
.hub_control = ehci_hub_control,
|
.hub_control = ehci_hub_control,
|
||||||
.bus_suspend = ehci_bus_suspend,
|
.bus_suspend = ehci_bus_suspend,
|
||||||
.bus_resume = ehci_bus_resume,
|
.bus_resume = ehci_bus_resume,
|
||||||
|
.relinquish_port = ehci_relinquish_port,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*-------------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------------*/
|
||||||
|
|
|
@ -162,6 +162,7 @@ static const struct hc_driver ehci_ppc_soc_hc_driver = {
|
||||||
.hub_control = ehci_hub_control,
|
.hub_control = ehci_hub_control,
|
||||||
.bus_suspend = ehci_bus_suspend,
|
.bus_suspend = ehci_bus_suspend,
|
||||||
.bus_resume = ehci_bus_resume,
|
.bus_resume = ehci_bus_resume,
|
||||||
|
.relinquish_port = ehci_relinquish_port,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int ehci_hcd_ppc_soc_drv_probe(struct platform_device *pdev)
|
static int ehci_hcd_ppc_soc_drv_probe(struct platform_device *pdev)
|
||||||
|
|
|
@ -72,6 +72,7 @@ static const struct hc_driver ps3_ehci_hc_driver = {
|
||||||
.bus_suspend = ehci_bus_suspend,
|
.bus_suspend = ehci_bus_suspend,
|
||||||
.bus_resume = ehci_bus_resume,
|
.bus_resume = ehci_bus_resume,
|
||||||
#endif
|
#endif
|
||||||
|
.relinquish_port = ehci_relinquish_port,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int ps3_ehci_probe(struct ps3_system_bus_device *dev)
|
static int ps3_ehci_probe(struct ps3_system_bus_device *dev)
|
||||||
|
|
Loading…
Reference in New Issue