Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 90da096e authored by Balaji Rao's avatar Balaji Rao Committed by Greg Kroah-Hartman
Browse files

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: default avatarBalaji Rao <balajirrao@gmail.com>
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent eb0be47d
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -210,6 +210,9 @@ struct hc_driver {
	int		(*start_port_reset)(struct usb_hcd *, unsigned port_num);
	void		(*hub_irq_enable)(struct usb_hcd *);
		/* 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);
+3 −0
Original line number Diff line number Diff line
@@ -2482,6 +2482,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
{
	struct usb_device *hdev = hub->hdev;
	struct device *hub_dev = hub->intfdev;
	struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
	u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
	int status, i;
 
@@ -2645,6 +2646,8 @@ loop:
 
done:
	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)
+1 −0
Original line number Diff line number Diff line
@@ -222,6 +222,7 @@ static const struct hc_driver ehci_au1xxx_hc_driver = {
	.hub_control = ehci_hub_control,
	.bus_suspend = ehci_bus_suspend,
	.bus_resume = ehci_bus_resume,
	.relinquish_port = ehci_relinquish_port,
};

/*-------------------------------------------------------------------------*/
+1 −0
Original line number Diff line number Diff line
@@ -323,6 +323,7 @@ static const struct hc_driver ehci_fsl_hc_driver = {
	.hub_control = ehci_hub_control,
	.bus_suspend = ehci_bus_suspend,
	.bus_resume = ehci_bus_resume,
	.relinquish_port = ehci_relinquish_port,
};

static int ehci_fsl_drv_probe(struct platform_device *pdev)
+44 −24
Original line number Diff line number Diff line
@@ -314,41 +314,21 @@ static ssize_t show_companion(struct device *dev,
}

/*
 * 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.
 * Sets the owner of a port
 */
static ssize_t store_companion(struct device *dev,
			       struct device_attribute *attr,
			       const char *buf, size_t count)
static void set_owner(struct ehci_hcd *ehci, int portnum, int new_owner)
{
	struct ehci_hcd		*ehci;
	int			portnum, new_owner, try;
	u32 __iomem		*status_reg;
	u32			port_status;
	int 			try;

	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;
	status_reg = &ehci->regs->port_status[--portnum];
	if (new_owner)
		set_bit(portnum, &ehci->companion_ports);
	else
		clear_bit(portnum, &ehci->companion_ports);
	status_reg = &ehci->regs->port_status[portnum];

	/*
	 * The controller won't set the OWNER bit if the port is
	 * enabled, so this loop will sometimes require at least two
	 * iterations: one to disable the port and one to set OWNER.
	 */

	for (try = 4; try > 0; --try) {
		spin_lock_irq(&ehci->lock);
		port_status = ehci_readl(ehci, status_reg);
@@ -365,6 +345,36 @@ static ssize_t store_companion(struct device *dev,
		if (try > 1)
			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;
}
static DEVICE_ATTR(companion, 0644, show_companion, store_companion);
@@ -867,3 +877,13 @@ error:
	spin_unlock_irqrestore (&ehci->lock, flags);
	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);
}
Loading