aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/xhci-ring.c
diff options
context:
space:
mode:
authorAndiry Xu <andiry.xu@amd.com>2010-10-14 10:23:00 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2010-10-22 13:22:13 -0400
commit561925318725a41189a69f36ebe99199b3fb84c4 (patch)
treee84c72692f8fd9ed67c298db17d26ccb9970a7a8 /drivers/usb/host/xhci-ring.c
parentbe88fe4f4dda93e3264a887745123b1e6c4a6845 (diff)
USB: xHCI: port remote wakeup implementation
This commit implements port remote wakeup. When a port is in U3 state and resume signaling is detected from a device, the port transitions to the Resume state, and the xHC generates a Port Status Change Event. For USB3 port, software write a '0' to the PLS field to complete the resume signaling. For USB2 port, the resume should be signaling for at least 20ms, irq handler set a timer for port remote wakeup, and then finishes process in hub_control GetPortStatus. Some codes are borrowed from EHCI code. Signed-off-by: Andiry Xu <andiry.xu@amd.com> Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/host/xhci-ring.c')
-rw-r--r--drivers/usb/host/xhci-ring.c57
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,
1165static void handle_port_status(struct xhci_hcd *xhci, 1165static 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
1233cleanup:
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