[RFC 12/15] usb: Register second xHCI roothub.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch requires changes in both the USB core and the xHCI host
controller driver to allow two roothubs (usb_hcd structures) to be
allocated for one xHCI PCI device.  There are really three functional
changes included in this (rather long) patch, but there's no way to break
them up without breaking git-bisect:

 1. Changes to the USB PCI code to allocate two usb_hcd structures, and
    corresponding changes to the xHCI initialization and shutdown code.
    The only really tricky bit is ensuring that the host controller doesn't
    start kicking khubd in response to port status changes before both
    usb_hcd structures are registered.  xhci_run() only starts the xHC
    running once it has been called with the USB 2.0 roothub.

 2. Changes to the USB roothub emulation code to return the proper speed
    and hub descriptors for each usb_hcd in the roothub pair.

 3. Changes to the xHCI roothub emulation code to lookup the proper port
    address in the two roothub port arrays, based on which usb_hcd is
    passed in.  There are also changes in the xHCI port status change
    event handler.  It needs to map the internal hardware port index into
    the proper usb_hcd to kick khubd for.  It indexes into an array of
    port protocol versions to do this.

When there's an issue that halts the xHCI host controller, we need to set
the hcd->state to HC_STATE_HALT for both shared roothubs (and possibly let
the USB core know *both* died).

Signed-off-by: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx>
---
 drivers/usb/core/hcd-pci.c   |   35 +++++++++++++++++
 drivers/usb/core/hcd.c       |   24 ++++++++++--
 drivers/usb/host/xhci-hub.c  |   87 +++++++++++++++++++++++++-----------------
 drivers/usb/host/xhci-mem.c  |   41 ++++++++++++++++---
 drivers/usb/host/xhci-ring.c |   37 +++++++++++++++++-
 drivers/usb/host/xhci.c      |   47 +++++++++++++++++++----
 6 files changed, 215 insertions(+), 56 deletions(-)

diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index bffdad4..e91d06b 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -172,6 +172,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 {
 	struct hc_driver	*driver;
 	struct usb_hcd		*hcd;
+	struct usb_hcd		*xhci_usb2_hcd = NULL;
 	int			retval;
 
 	if (usb_disabled())
@@ -200,6 +201,14 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 		retval = -ENOMEM;
 		goto disable_pci;
 	}
+	if ((driver->flags & HCD_MASK) == HCD_USB3) {
+		xhci_usb2_hcd = usb_create_shared_hcd(driver, &dev->dev,
+				pci_name(dev), hcd);
+		if (!xhci_usb2_hcd) {
+			retval = -ENOMEM;
+			goto dealloc_hcd;
+		}
+	}
 
 	if (driver->flags & HCD_MEMORY) {
 		/* EHCI, OHCI */
@@ -245,12 +254,20 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 	retval = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED);
 	if (retval != 0)
 		goto unmap_registers;
+	if ((driver->flags & HCD_MASK) == HCD_USB3) {
+		retval = usb_add_hcd(xhci_usb2_hcd, dev->irq,
+				IRQF_DISABLED | IRQF_SHARED);
+		if (retval != 0)
+			goto remove_hcd;
+	}
 	set_hs_companion(dev, hcd);
 
 	if (pci_dev_run_wake(dev))
 		pm_runtime_put_noidle(&dev->dev);
 	return retval;
 
+remove_hcd:
+	usb_remove_hcd(hcd);
 unmap_registers:
 	if (driver->flags & HCD_MEMORY) {
 		iounmap(hcd->regs);
@@ -260,6 +277,8 @@ release_mem_region:
 		release_region(hcd->rsrc_start, hcd->rsrc_len);
 clear_companion:
 	clear_hs_companion(dev, hcd);
+	usb_put_hcd(xhci_usb2_hcd);
+dealloc_hcd:
 	usb_put_hcd(hcd);
 disable_pci:
 	pci_disable_device(dev);
@@ -286,6 +305,7 @@ EXPORT_SYMBOL_GPL(usb_hcd_pci_probe);
 void usb_hcd_pci_remove(struct pci_dev *dev)
 {
 	struct usb_hcd		*hcd;
+	struct usb_hcd		*xhci_usb2_hcd;
 
 	hcd = pci_get_drvdata(dev);
 	if (!hcd)
@@ -302,15 +322,30 @@ void usb_hcd_pci_remove(struct pci_dev *dev)
 	usb_hcd_irq(0, hcd);
 	local_irq_enable();
 
+	if (hcd->driver->flags & HCD_USB3)
+		xhci_usb2_hcd = hcd->shared_hcd;
+	else
+		xhci_usb2_hcd = NULL;
+
+	if (xhci_usb2_hcd)
+		usb_remove_hcd(xhci_usb2_hcd);
 	usb_remove_hcd(hcd);
+
 	if (hcd->driver->flags & HCD_MEMORY) {
 		iounmap(hcd->regs);
 		release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
 	} else {
 		release_region(hcd->rsrc_start, hcd->rsrc_len);
 	}
+
+	if (xhci_usb2_hcd)
+		clear_hs_companion(dev, xhci_usb2_hcd);
 	clear_hs_companion(dev, hcd);
+
+	if (xhci_usb2_hcd)
+		usb_put_hcd(xhci_usb2_hcd);
 	usb_put_hcd(hcd);
+
 	pci_disable_device(dev);
 }
 EXPORT_SYMBOL_GPL(usb_hcd_pci_remove);
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 091da8e..cfc333f 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -509,7 +509,10 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
 		case USB_DT_DEVICE << 8:
 			switch (hcd->driver->flags & HCD_MASK) {
 			case HCD_USB3:
-				bufp = usb3_rh_dev_descriptor;
+				if (usb_hcd_is_main_hcd(hcd))
+					bufp = usb3_rh_dev_descriptor;
+				else
+					bufp = usb2_rh_dev_descriptor;
 				break;
 			case HCD_USB2:
 				bufp = usb2_rh_dev_descriptor;
@@ -527,8 +530,13 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
 		case USB_DT_CONFIG << 8:
 			switch (hcd->driver->flags & HCD_MASK) {
 			case HCD_USB3:
-				bufp = ss_rh_config_descriptor;
-				len = sizeof ss_rh_config_descriptor;
+				if (usb_hcd_is_main_hcd(hcd)) {
+					bufp = ss_rh_config_descriptor;
+					len = sizeof ss_rh_config_descriptor;
+				} else {
+					bufp = hs_rh_config_descriptor;
+					len = sizeof hs_rh_config_descriptor;
+				}
 				break;
 			case HCD_USB2:
 				bufp = hs_rh_config_descriptor;
@@ -2338,7 +2346,15 @@ int usb_add_hcd(struct usb_hcd *hcd,
 		rhdev->speed = USB_SPEED_HIGH;
 		break;
 	case HCD_USB3:
-		rhdev->speed = USB_SPEED_SUPER;
+		/*
+		 * There is only one xHCI host controller that handles all
+		 * device speeds, but we fake two roothubs to emulate how
+		 * USB 3.0 hubs work.  They show up as two devices.
+		 */
+		if (usb_hcd_is_main_hcd(hcd))
+			rhdev->speed = USB_SPEED_SUPER;
+		else
+			rhdev->speed = USB_SPEED_HIGH;
 		break;
 	default:
 		goto err_set_rh_speed;
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index c242d18..38e272c 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -28,13 +28,20 @@
 #define	PORT_RWC_BITS	(PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | \
 			 PORT_RC | PORT_PLC | PORT_PE)
 
-static void xhci_hub_descriptor(struct xhci_hcd *xhci,
+static void xhci_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci,
 		struct usb_hub_descriptor *desc)
 {
 	int ports;
 	u16 temp;
 
-	ports = HCS_MAX_PORTS(xhci->hcs_params1);
+	if (usb_hcd_is_main_hcd(hcd))
+		ports = xhci->num_usb3_ports;
+	else
+		ports = xhci->num_usb2_ports;
+
+	/* FIXME: return a USB 3.0 hub descriptor if this request was for the
+	 * USB3 roothub.
+	 */
 
 	/* USB 3.0 hubs have a different descriptor, but we fake this for now */
 	desc->bDescriptorType = 0x29;
@@ -282,10 +289,16 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 	unsigned long flags;
 	u32 temp, temp1, status;
 	int retval = 0;
-	u32 __iomem *addr;
+	u32 __iomem **port_array;
 	int slot_id;
 
-	ports = HCS_MAX_PORTS(xhci->hcs_params1);
+	if (usb_hcd_is_main_hcd(hcd)) {
+		ports = xhci->num_usb3_ports;
+		port_array = xhci->usb3_ports;
+	} else {
+		ports = xhci->num_usb2_ports;
+		port_array = xhci->usb2_ports;
+	}
 
 	spin_lock_irqsave(&xhci->lock, flags);
 	switch (typeReq) {
@@ -294,15 +307,15 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 		memset(buf, 0, 4);
 		break;
 	case GetHubDescriptor:
-		xhci_hub_descriptor(xhci, (struct usb_hub_descriptor *) buf);
+		xhci_hub_descriptor(hcd, xhci,
+				(struct usb_hub_descriptor *) buf);
 		break;
 	case GetPortStatus:
 		if (!wIndex || wIndex > ports)
 			goto error;
 		wIndex--;
 		status = 0;
-		addr = &xhci->op_regs->port_status_base + NUM_PORT_REGS*(wIndex & 0xff);
-		temp = xhci_readl(xhci, addr);
+		temp = xhci_readl(xhci, port_array[wIndex]);
 		xhci_dbg(xhci, "get port status, actual port %d status  = 0x%x\n", wIndex, temp);
 
 		/* wPortChange bits */
@@ -330,7 +343,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 				temp1 = xhci_port_state_to_neutral(temp);
 				temp1 &= ~PORT_PLS_MASK;
 				temp1 |= PORT_LINK_STROBE | XDEV_U0;
-				xhci_writel(xhci, temp1, addr);
+				xhci_writel(xhci, temp1, port_array[wIndex]);
 
 				xhci_dbg(xhci, "set port %d resume\n",
 					wIndex + 1);
@@ -378,12 +391,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 		if (!wIndex || wIndex > ports)
 			goto error;
 		wIndex--;
-		addr = &xhci->op_regs->port_status_base + NUM_PORT_REGS*(wIndex & 0xff);
-		temp = xhci_readl(xhci, addr);
+		temp = xhci_readl(xhci, port_array[wIndex]);
 		temp = xhci_port_state_to_neutral(temp);
 		switch (wValue) {
 		case USB_PORT_FEAT_SUSPEND:
-			temp = xhci_readl(xhci, addr);
+			temp = xhci_readl(xhci, port_array[wIndex]);
 			/* In spec software should not attempt to suspend
 			 * a port unless the port reports that it is in the
 			 * enabled (PED = â1â,PLS < â3â) state.
@@ -408,13 +420,13 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 			temp = xhci_port_state_to_neutral(temp);
 			temp &= ~PORT_PLS_MASK;
 			temp |= PORT_LINK_STROBE | XDEV_U3;
-			xhci_writel(xhci, temp, addr);
+			xhci_writel(xhci, temp, port_array[wIndex]);
 
 			spin_unlock_irqrestore(&xhci->lock, flags);
 			msleep(10); /* wait device to enter */
 			spin_lock_irqsave(&xhci->lock, flags);
 
-			temp = xhci_readl(xhci, addr);
+			temp = xhci_readl(xhci, port_array[wIndex]);
 			xhci->suspended_ports[wIndex >> 5] |=
 					1 << (wIndex & (31));
 			break;
@@ -425,34 +437,34 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 			 * However, khubd will ignore the roothub events until
 			 * the roothub is registered.
 			 */
-			xhci_writel(xhci, temp | PORT_POWER, addr);
+			xhci_writel(xhci, temp | PORT_POWER,
+					port_array[wIndex]);
 
-			temp = xhci_readl(xhci, addr);
+			temp = xhci_readl(xhci, port_array[wIndex]);
 			xhci_dbg(xhci, "set port power, actual port %d status  = 0x%x\n", wIndex, temp);
 			break;
 		case USB_PORT_FEAT_RESET:
 			temp = (temp | PORT_RESET);
-			xhci_writel(xhci, temp, addr);
+			xhci_writel(xhci, temp, port_array[wIndex]);
 
-			temp = xhci_readl(xhci, addr);
+			temp = xhci_readl(xhci, port_array[wIndex]);
 			xhci_dbg(xhci, "set port reset, actual port %d status  = 0x%x\n", wIndex, temp);
 			break;
 		default:
 			goto error;
 		}
-		temp = xhci_readl(xhci, addr); /* unblock any posted writes */
+		/* unblock any posted writes */
+		temp = xhci_readl(xhci, port_array[wIndex]);
 		break;
 	case ClearPortFeature:
 		if (!wIndex || wIndex > ports)
 			goto error;
 		wIndex--;
-		addr = &xhci->op_regs->port_status_base +
-			NUM_PORT_REGS*(wIndex & 0xff);
-		temp = xhci_readl(xhci, addr);
+		temp = xhci_readl(xhci, port_array[wIndex]);
 		temp = xhci_port_state_to_neutral(temp);
 		switch (wValue) {
 		case USB_PORT_FEAT_SUSPEND:
-			temp = xhci_readl(xhci, addr);
+			temp = xhci_readl(xhci, port_array[wIndex]);
 			xhci_dbg(xhci, "clear USB_PORT_FEAT_SUSPEND\n");
 			xhci_dbg(xhci, "PORTSC %04x\n", temp);
 			if (temp & PORT_RESET)
@@ -464,24 +476,24 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 					temp = xhci_port_state_to_neutral(temp);
 					temp &= ~PORT_PLS_MASK;
 					temp |= PORT_LINK_STROBE | XDEV_U0;
-					xhci_writel(xhci, temp, addr);
-					xhci_readl(xhci, addr);
+					xhci_writel(xhci, temp, port_array[wIndex]);
+					xhci_readl(xhci, port_array[wIndex]);
 				} else {
 					temp = xhci_port_state_to_neutral(temp);
 					temp &= ~PORT_PLS_MASK;
 					temp |= PORT_LINK_STROBE | XDEV_RESUME;
-					xhci_writel(xhci, temp, addr);
+					xhci_writel(xhci, temp, port_array[wIndex]);
 
 					spin_unlock_irqrestore(&xhci->lock,
 							       flags);
 					msleep(20);
 					spin_lock_irqsave(&xhci->lock, flags);
 
-					temp = xhci_readl(xhci, addr);
+					temp = xhci_readl(xhci, port_array[wIndex]);
 					temp = xhci_port_state_to_neutral(temp);
 					temp &= ~PORT_PLS_MASK;
 					temp |= PORT_LINK_STROBE | XDEV_U0;
-					xhci_writel(xhci, temp, addr);
+					xhci_writel(xhci, temp, port_array[wIndex]);
 				}
 				xhci->port_c_suspend[wIndex >> 5] |=
 						1 << (wIndex & 31);
@@ -502,10 +514,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 		case USB_PORT_FEAT_C_OVER_CURRENT:
 		case USB_PORT_FEAT_C_ENABLE:
 			xhci_clear_port_change_bit(xhci, wValue, wIndex,
-					addr, temp);
+					port_array[wIndex], temp);
 			break;
 		case USB_PORT_FEAT_ENABLE:
-			xhci_disable_port(xhci, wIndex, addr, temp);
+			xhci_disable_port(xhci, wIndex,
+					port_array[wIndex], temp);
 			break;
 		default:
 			goto error;
@@ -536,9 +549,15 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
 	int i, retval;
 	struct xhci_hcd	*xhci = hcd_to_xhci(hcd);
 	int ports;
-	u32 __iomem *addr;
-
-	ports = HCS_MAX_PORTS(xhci->hcs_params1);
+	u32 __iomem **port_array;
+
+	if (usb_hcd_is_main_hcd(hcd)) {
+		ports = xhci->num_usb3_ports;
+		port_array = xhci->usb3_ports;
+	} else {
+		ports = xhci->num_usb2_ports;
+		port_array = xhci->usb2_ports;
+	}
 
 	/* Initial status is no changes */
 	retval = (ports + 8) / 8;
@@ -550,9 +569,7 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
 	spin_lock_irqsave(&xhci->lock, flags);
 	/* For each port, did anything change?  If so, set that bit in buf. */
 	for (i = 0; i < ports; i++) {
-		addr = &xhci->op_regs->port_status_base +
-			NUM_PORT_REGS*i;
-		temp = xhci_readl(xhci, addr);
+		temp = xhci_readl(xhci, port_array[i]);
 		if ((temp & mask) != 0 ||
 			(xhci->port_c_suspend[i >> 5] &	1 << (i & 31)) ||
 			(xhci->resume_done[i] && time_after_eq(
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index e446639..8e5f0c3 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -814,14 +814,41 @@ void xhci_copy_ep0_dequeue_into_input_ctx(struct xhci_hcd *xhci,
 	ep0_ctx->deq |= ep_ring->cycle_state;
 }
 
+static u32 xhci_find_real_port_number(struct xhci_hcd *xhci,
+		struct usb_device *udev)
+{
+	struct usb_device *top_dev;
+	unsigned int num_similar_speed_ports;
+	unsigned int faked_port_num;
+	int i;
+
+	for (top_dev = udev; top_dev->parent && top_dev->parent->parent;
+			top_dev = top_dev->parent)
+		/* Found device below root hub */;
+	faked_port_num = top_dev->portnum;
+	for (i = 0, num_similar_speed_ports = 0;
+			i < HCS_MAX_PORTS(xhci->hcs_params1); i++) {
+		u8 speed = xhci->port_array[i];
+		if (speed == 0 || speed == -1)
+			continue;
+		if ((speed == 0x03 && udev->speed == USB_SPEED_SUPER) ||
+				(speed != 0x03 && udev->speed != USB_SPEED_SUPER))
+			num_similar_speed_ports++;
+		if (num_similar_speed_ports == faked_port_num)
+			/* Roothub ports are numbered from 1 to N */
+			return i+1;
+	}
+	return 0;
+}
+
 /* Setup an xHCI virtual device for a Set Address command */
 int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *udev)
 {
 	struct xhci_virt_device *dev;
 	struct xhci_ep_ctx	*ep0_ctx;
-	struct usb_device	*top_dev;
 	struct xhci_slot_ctx    *slot_ctx;
 	struct xhci_input_control_ctx *ctrl_ctx;
+	u32			port_num;
 
 	dev = xhci->devs[udev->slot_id];
 	/* Slot ID 0 is reserved */
@@ -863,12 +890,12 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud
 		BUG();
 	}
 	/* Find the root hub port this device is under */
-	for (top_dev = udev; top_dev->parent && top_dev->parent->parent;
-			top_dev = top_dev->parent)
-		/* Found device below root hub */;
-	slot_ctx->dev_info2 |= (u32) ROOT_HUB_PORT(top_dev->portnum);
-	dev->port = top_dev->portnum;
-	xhci_dbg(xhci, "Set root hub portnum to %d\n", top_dev->portnum);
+	port_num = xhci_find_real_port_number(xhci, udev);
+	if (!port_num)
+		return -EINVAL;
+	slot_ctx->dev_info2 |= (u32) ROOT_HUB_PORT(port_num);
+	dev->port = port_num;
+	xhci_dbg(xhci, "Set root hub portnum to %d\n", port_num);
 
 	/* Is this a LS/FS device under a HS hub? */
 	if ((udev->speed == USB_SPEED_LOW || udev->speed == USB_SPEED_FULL) &&
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index e11d648..06a50ea 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -872,8 +872,10 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg)
 	}
 	spin_unlock(&xhci->lock);
 	xhci_to_hcd(xhci)->state = HC_STATE_HALT;
+	xhci_to_hcd(xhci)->shared_hcd->state = HC_STATE_HALT;
 	xhci_dbg(xhci, "Calling usb_hc_died()\n");
 	usb_hc_died(xhci_to_hcd(xhci));
+	usb_hc_died(xhci_to_hcd(xhci)->shared_hcd);
 	xhci_dbg(xhci, "xHCI host controller is dead.\n");
 }
 
@@ -1165,12 +1167,13 @@ static void handle_vendor_event(struct xhci_hcd *xhci,
 static void handle_port_status(struct xhci_hcd *xhci,
 		union xhci_trb *event)
 {
-	struct usb_hcd *hcd = xhci_to_hcd(xhci);
+	struct usb_hcd *hcd;
 	u32 port_id;
 	u32 temp, temp1;
 	u32 __iomem *addr;
 	int ports;
 	int slot_id;
+	u8 major_revision;
 
 	/* Port status change events always have a successful completion code */
 	if (GET_COMP_CODE(event->generic.field[2]) != COMP_SUCCESS) {
@@ -1186,10 +1189,39 @@ static void handle_port_status(struct xhci_hcd *xhci,
 		goto cleanup;
 	}
 
+	/* Figure out which usb_hcd this port is attached to:
+	 * is it a USB 3.0 port or a USB 2.0/1.1 port?
+	 */
+	if (port_id > HCS_MAX_PORTS(xhci->hcs_params1)) {
+		xhci_warn(xhci, "Bad port ID, host supports %u ports,"
+				" event for port %u\n",
+				HCS_MAX_PORTS(xhci->hcs_params1),
+				port_id);
+		goto cleanup;
+	}
+	major_revision = xhci->port_array[port_id - 1];
+	if (major_revision == 0) {
+		xhci_warn(xhci, "Event for port %u not in "
+				"Extended Capabilities, ignoring.\n",
+				port_id);
+		goto cleanup;
+	}
+	if (major_revision == (u8) -1) {
+		xhci_warn(xhci, "Event for port %u duplicated in"
+				"Extended Capabilities, ignoring.\n",
+				port_id);
+		goto cleanup;
+	}
+	if (major_revision == 0x03)
+		hcd = xhci_to_hcd(xhci);
+	else
+		hcd = xhci_to_hcd(xhci)->shared_hcd;
+
 	addr = &xhci->op_regs->port_status_base + NUM_PORT_REGS * (port_id - 1);
 	temp = xhci_readl(xhci, addr);
 	if ((temp & PORT_CONNECT) && (hcd->state == HC_STATE_SUSPENDED)) {
 		xhci_dbg(xhci, "resume root hub\n");
+		/* FIXME: should we also be resuming the partner usb_hcd? */
 		usb_hcd_resume_root_hub(hcd);
 	}
 
@@ -1236,7 +1268,7 @@ cleanup:
 
 	spin_unlock(&xhci->lock);
 	/* Pass this up to the core */
-	usb_hcd_poll_rh_status(xhci_to_hcd(xhci));
+	usb_hcd_poll_rh_status(hcd);
 	spin_lock(&xhci->lock);
 }
 
@@ -2122,6 +2154,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
 		xhci_halt(xhci);
 hw_died:
 		xhci_to_hcd(xhci)->state = HC_STATE_HALT;
+		xhci_to_hcd(xhci)->shared_hcd->state = HC_STATE_HALT;
 		spin_unlock(&xhci->lock);
 		return -ESHUTDOWN;
 	}
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index cb4caf6..2f7c89f 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -374,6 +374,21 @@ void xhci_event_ring_work(unsigned long arg)
 }
 #endif
 
+static int xhci_run_finished(struct xhci_hcd *xhci)
+{
+	if (xhci_start(xhci)) {
+		xhci_halt(xhci);
+		return -ENODEV;
+	}
+	xhci_to_hcd(xhci)->shared_hcd->state = HC_STATE_RUNNING;
+
+	if (xhci->quirks & XHCI_NEC_HOST)
+		xhci_ring_cmd_db(xhci);
+
+	xhci_dbg(xhci, "Finished xhci_run for USB2 roothub\n");
+	return 0;
+}
+
 /*
  * Start the HC after it was halted.
  *
@@ -394,6 +409,14 @@ int xhci_run(struct usb_hcd *hcd)
 	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
 	struct pci_dev  *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
 
+	/* Start the xHCI host controller running only after the USB 2.0 roothub
+	 * is setup.
+	 */
+
+	if (!usb_hcd_is_main_hcd(hcd))
+		return xhci_run_finished(xhci);
+
+	xhci = hcd_to_xhci(hcd);
 	hcd->uses_new_polling = 1;
 
 	xhci_dbg(xhci, "xhci_run\n");
@@ -469,16 +492,18 @@ int xhci_run(struct usb_hcd *hcd)
 		xhci_queue_vendor_command(xhci, 0, 0, 0,
 				TRB_TYPE(TRB_NEC_GET_FW));
 
-	if (xhci_start(xhci)) {
-		xhci_halt(xhci);
-		return -ENODEV;
-	}
+	xhci_dbg(xhci, "Finished xhci_run for USB3 roothub\n");
+	return 0;
+}
 
-	if (xhci->quirks & XHCI_NEC_HOST)
-		xhci_ring_cmd_db(xhci);
+static void xhci_only_stop_hcd(struct usb_hcd *hcd)
+{
+	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
 
-	xhci_dbg(xhci, "Finished xhci_run\n");
-	return 0;
+	spin_lock_irq(&xhci->lock);
+	xhci_halt(xhci);
+	xhci_to_hcd(xhci)->shared_hcd->state = HC_STATE_HALT;
+	spin_unlock_irq(&xhci->lock);
 }
 
 /*
@@ -496,11 +521,15 @@ void xhci_stop(struct usb_hcd *hcd)
 	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
 
 	if (!usb_hcd_is_main_hcd(hcd)) {
+		xhci_only_stop_hcd(hcd->shared_hcd);
 		*((struct xhci_hcd **) hcd->hcd_priv) = NULL;
 		return;
 	}
 
 	spin_lock_irq(&xhci->lock);
+	/* Make sure the xHC is halted for a USB3 roothub
+	 * (xhci_stop() could be called as part of failed init).
+	 */
 	xhci_halt(xhci);
 	xhci_reset(xhci);
 	xhci_cleanup_msix(xhci);
@@ -534,6 +563,8 @@ void xhci_stop(struct usb_hcd *hcd)
  * This is called when the machine is rebooting or halting.  We assume that the
  * machine will be powered off, and the HC's internal state will be reset.
  * Don't bother to free memory.
+ *
+ * This will only ever be called with the main usb_hcd (the USB3 roothub).
  */
 void xhci_shutdown(struct usb_hcd *hcd)
 {
-- 
1.6.3.3

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux