diff options
Diffstat (limited to 'drivers/usb/host/xhci-mem.c')
-rw-r--r-- | drivers/usb/host/xhci-mem.c | 58 |
1 files changed, 57 insertions, 1 deletions
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index e5ace8995b3b..88192866ab62 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1892,16 +1892,24 @@ no_bw: xhci->cmd_ring_reserved_trbs = 0; xhci->num_usb2_ports = 0; xhci->num_usb3_ports = 0; + xhci->usb2_rhub.num_ports = 0; + xhci->usb3_rhub.num_ports = 0; xhci->num_active_eps = 0; kfree(xhci->usb2_ports); kfree(xhci->usb3_ports); kfree(xhci->port_array); + kfree(xhci->usb2_rhub.ports); + kfree(xhci->usb3_rhub.ports); + kfree(xhci->hw_ports); kfree(xhci->rh_bw); kfree(xhci->ext_caps); xhci->usb2_ports = NULL; xhci->usb3_ports = NULL; xhci->port_array = NULL; + xhci->usb2_rhub.ports = NULL; + xhci->usb3_rhub.ports = NULL; + xhci->hw_ports = NULL; xhci->rh_bw = NULL; xhci->ext_caps = NULL; @@ -2186,8 +2194,10 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, port_offset--; for (i = port_offset; i < (port_offset + port_count); i++) { + struct xhci_port *hw_port = &xhci->hw_ports[i]; /* Duplicate entry. Ignore the port if the revisions differ. */ - if (xhci->port_array[i] != 0) { + if (xhci->port_array[i] != 0 || + hw_port->rhub) { xhci_warn(xhci, "Duplicate port entry, Ext Cap %p," " port %u\n", addr, i); xhci_warn(xhci, "Port was marked as USB %u, " @@ -2205,9 +2215,16 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, xhci->port_array[i] = DUPLICATE_ENTRY; } /* FIXME: Should we disable the port? */ + if (hw_port->rhub != rhub && + hw_port->hcd_portnum != DUPLICATE_ENTRY) { + hw_port->rhub->num_ports--; + hw_port->hcd_portnum = DUPLICATE_ENTRY; + } continue; } xhci->port_array[i] = major_revision; + hw_port->rhub = rhub; + rhub->num_ports++; if (major_revision == 0x03) xhci->num_usb3_ports++; else @@ -2216,6 +2233,27 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, /* FIXME: Should we disable ports not in the Extended Capabilities? */ } +static void xhci_create_rhub_port_array(struct xhci_hcd *xhci, + struct xhci_hub *rhub, gfp_t flags) +{ + int port_index = 0; + int i; + + if (!rhub->num_ports) + return; + rhub->ports = kcalloc(rhub->num_ports, sizeof(rhub->ports), flags); + for (i = 0; i < HCS_MAX_PORTS(xhci->hcs_params1); i++) { + if (xhci->hw_ports[i].rhub != rhub || + xhci->hw_ports[i].hcd_portnum == DUPLICATE_ENTRY) + continue; + xhci->hw_ports[i].hcd_portnum = port_index; + rhub->ports[port_index] = &xhci->hw_ports[i]; + port_index++; + if (port_index == rhub->num_ports) + break; + } +} + /* * Scan the Extended Capabilities for the "Supported Protocol Capabilities" that * specify what speeds each port is supposed to be. We can't count on the port @@ -2234,9 +2272,16 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) num_ports = HCS_MAX_PORTS(xhci->hcs_params1); xhci->port_array = kzalloc(sizeof(*xhci->port_array)*num_ports, flags); + xhci->hw_ports = kcalloc(num_ports, sizeof(*xhci->hw_ports), flags); if (!xhci->port_array) return -ENOMEM; + for (i = 0; i < num_ports; i++) { + xhci->hw_ports[i].addr = &xhci->op_regs->port_status_base + + NUM_PORT_REGS * i; + xhci->hw_ports[i].hw_portnum = i; + } + xhci->rh_bw = kzalloc(sizeof(*xhci->rh_bw)*num_ports, flags); if (!xhci->rh_bw) return -ENOMEM; @@ -2274,6 +2319,9 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) xhci_add_in_port(xhci, num_ports, base + offset, cap_count); if (xhci->num_usb2_ports + xhci->num_usb3_ports == num_ports) break; + if (xhci->usb2_rhub.num_ports + xhci->usb3_rhub.num_ports == + num_ports) + break; offset = xhci_find_next_ext_cap(base, offset, XHCI_EXT_CAPS_PROTOCOL); } @@ -2282,6 +2330,10 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) xhci_warn(xhci, "No ports on the roothubs?\n"); return -ENODEV; } + if (xhci->usb2_rhub.num_ports == 0 && xhci->usb3_rhub.num_ports == 0) { + xhci_warn(xhci, "No ports on the roothubs?\n"); + return -ENODEV; + } xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Found %u USB 2.0 ports and %u USB 3.0 ports.", xhci->num_usb2_ports, xhci->num_usb3_ports); @@ -2306,6 +2358,10 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) * Note we could have all USB 3.0 ports, or all USB 2.0 ports. * Not sure how the USB core will handle a hub with no ports... */ + + xhci_create_rhub_port_array(xhci, &xhci->usb2_rhub, flags); + xhci_create_rhub_port_array(xhci, &xhci->usb3_rhub, flags); + if (xhci->num_usb2_ports) { xhci->usb2_ports = kmalloc(sizeof(*xhci->usb2_ports)* xhci->num_usb2_ports, flags); |