diff options
Diffstat (limited to 'drivers/usb/host/xhci-ring.c')
-rw-r--r-- | drivers/usb/host/xhci-ring.c | 57 |
1 files changed, 56 insertions, 1 deletions
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index b18e00ecb468..9f3115e729b1 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c | |||
@@ -1165,17 +1165,72 @@ static void handle_vendor_event(struct xhci_hcd *xhci, | |||
1165 | static void handle_port_status(struct xhci_hcd *xhci, | 1165 | static void handle_port_status(struct xhci_hcd *xhci, |
1166 | union xhci_trb *event) | 1166 | union xhci_trb *event) |
1167 | { | 1167 | { |
1168 | struct usb_hcd *hcd = xhci_to_hcd(xhci); | ||
1168 | u32 port_id; | 1169 | u32 port_id; |
1170 | u32 temp, temp1; | ||
1171 | u32 __iomem *addr; | ||
1172 | int ports; | ||
1173 | int slot_id; | ||
1169 | 1174 | ||
1170 | /* Port status change events always have a successful completion code */ | 1175 | /* Port status change events always have a successful completion code */ |
1171 | if (GET_COMP_CODE(event->generic.field[2]) != COMP_SUCCESS) { | 1176 | if (GET_COMP_CODE(event->generic.field[2]) != COMP_SUCCESS) { |
1172 | xhci_warn(xhci, "WARN: xHC returned failed port status event\n"); | 1177 | xhci_warn(xhci, "WARN: xHC returned failed port status event\n"); |
1173 | xhci->error_bitmask |= 1 << 8; | 1178 | xhci->error_bitmask |= 1 << 8; |
1174 | } | 1179 | } |
1175 | /* FIXME: core doesn't care about all port link state changes yet */ | ||
1176 | port_id = GET_PORT_ID(event->generic.field[0]); | 1180 | port_id = GET_PORT_ID(event->generic.field[0]); |
1177 | xhci_dbg(xhci, "Port Status Change Event for port %d\n", port_id); | 1181 | xhci_dbg(xhci, "Port Status Change Event for port %d\n", port_id); |
1178 | 1182 | ||
1183 | ports = HCS_MAX_PORTS(xhci->hcs_params1); | ||
1184 | if ((port_id <= 0) || (port_id > ports)) { | ||
1185 | xhci_warn(xhci, "Invalid port id %d\n", port_id); | ||
1186 | goto cleanup; | ||
1187 | } | ||
1188 | |||
1189 | addr = &xhci->op_regs->port_status_base + NUM_PORT_REGS * (port_id - 1); | ||
1190 | temp = xhci_readl(xhci, addr); | ||
1191 | if ((temp & PORT_CONNECT) && (hcd->state == HC_STATE_SUSPENDED)) { | ||
1192 | xhci_dbg(xhci, "resume root hub\n"); | ||
1193 | usb_hcd_resume_root_hub(hcd); | ||
1194 | } | ||
1195 | |||
1196 | if ((temp & PORT_PLC) && (temp & PORT_PLS_MASK) == XDEV_RESUME) { | ||
1197 | xhci_dbg(xhci, "port resume event for port %d\n", port_id); | ||
1198 | |||
1199 | temp1 = xhci_readl(xhci, &xhci->op_regs->command); | ||
1200 | if (!(temp1 & CMD_RUN)) { | ||
1201 | xhci_warn(xhci, "xHC is not running.\n"); | ||
1202 | goto cleanup; | ||
1203 | } | ||
1204 | |||
1205 | if (DEV_SUPERSPEED(temp)) { | ||
1206 | xhci_dbg(xhci, "resume SS port %d\n", port_id); | ||
1207 | temp = xhci_port_state_to_neutral(temp); | ||
1208 | temp &= ~PORT_PLS_MASK; | ||
1209 | temp |= PORT_LINK_STROBE | XDEV_U0; | ||
1210 | xhci_writel(xhci, temp, addr); | ||
1211 | slot_id = xhci_find_slot_id_by_port(xhci, port_id); | ||
1212 | if (!slot_id) { | ||
1213 | xhci_dbg(xhci, "slot_id is zero\n"); | ||
1214 | goto cleanup; | ||
1215 | } | ||
1216 | xhci_ring_device(xhci, slot_id); | ||
1217 | xhci_dbg(xhci, "resume SS port %d finished\n", port_id); | ||
1218 | /* Clear PORT_PLC */ | ||
1219 | temp = xhci_readl(xhci, addr); | ||
1220 | temp = xhci_port_state_to_neutral(temp); | ||
1221 | temp |= PORT_PLC; | ||
1222 | xhci_writel(xhci, temp, addr); | ||
1223 | } else { | ||
1224 | xhci_dbg(xhci, "resume HS port %d\n", port_id); | ||
1225 | xhci->resume_done[port_id - 1] = jiffies + | ||
1226 | msecs_to_jiffies(20); | ||
1227 | mod_timer(&hcd->rh_timer, | ||
1228 | xhci->resume_done[port_id - 1]); | ||
1229 | /* Do the rest in GetPortStatus */ | ||
1230 | } | ||
1231 | } | ||
1232 | |||
1233 | cleanup: | ||
1179 | /* Update event ring dequeue pointer before dropping the lock */ | 1234 | /* Update event ring dequeue pointer before dropping the lock */ |
1180 | inc_deq(xhci, xhci->event_ring, true); | 1235 | inc_deq(xhci, xhci->event_ring, true); |
1181 | 1236 | ||