Commit eae5b176 authored by Sarah Sharp's avatar Sarah Sharp

xhci: Refactor port status into a new function.

The hub control function is *way* too long.  Refactor it into a new
function, and document the side effects of calling that function.
Signed-off-by: default avatarSarah Sharp <sarah.a.sharp@linux.intel.com>
parent 57d04eb1
...@@ -534,104 +534,51 @@ void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, u16 wIndex) ...@@ -534,104 +534,51 @@ void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, u16 wIndex)
} }
} }
int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, /*
u16 wIndex, char *buf, u16 wLength) * Converts a raw xHCI port status into the format that external USB 2.0 or USB
* 3.0 hubs use.
*
* Possible side effects:
* - Mark a port as being done with device resume,
* and ring the endpoint doorbells.
* - Stop the Synopsys redriver Compliance Mode polling.
*/
static u32 xhci_get_port_status(struct usb_hcd *hcd,
struct xhci_bus_state *bus_state,
__le32 __iomem **port_array,
u16 wIndex, u32 raw_port_status)
{ {
struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct xhci_hcd *xhci = hcd_to_xhci(hcd);
int max_ports; u32 status = 0;
unsigned long flags;
u32 temp, status;
int retval = 0;
__le32 __iomem **port_array;
int slot_id; int slot_id;
struct xhci_bus_state *bus_state;
u16 link_state = 0;
u16 wake_mask = 0;
u16 timeout = 0;
max_ports = xhci_get_ports(hcd, &port_array);
bus_state = &xhci->bus_state[hcd_index(hcd)];
spin_lock_irqsave(&xhci->lock, flags);
switch (typeReq) {
case GetHubStatus:
/* No power source, over-current reported per port */
memset(buf, 0, 4);
break;
case GetHubDescriptor:
/* Check to make sure userspace is asking for the USB 3.0 hub
* descriptor for the USB 3.0 roothub. If not, we stall the
* endpoint, like external hubs do.
*/
if (hcd->speed == HCD_USB3 &&
(wLength < USB_DT_SS_HUB_SIZE ||
wValue != (USB_DT_SS_HUB << 8))) {
xhci_dbg(xhci, "Wrong hub descriptor type for "
"USB 3.0 roothub.\n");
goto error;
}
xhci_hub_descriptor(hcd, xhci,
(struct usb_hub_descriptor *) buf);
break;
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
if ((wValue & 0xff00) != (USB_DT_BOS << 8))
goto error;
if (hcd->speed != HCD_USB3)
goto error;
/* Set the U1 and U2 exit latencies. */
memcpy(buf, &usb_bos_descriptor,
USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE);
temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params3);
buf[12] = HCS_U1_LATENCY(temp);
put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]);
/* Indicate whether the host has LTM support. */
temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params);
if (HCC_LTC(temp))
buf[8] |= USB_LTM_SUPPORT;
spin_unlock_irqrestore(&xhci->lock, flags);
return USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE;
case GetPortStatus:
if (!wIndex || wIndex > max_ports)
goto error;
wIndex--;
status = 0;
temp = xhci_readl(xhci, port_array[wIndex]);
if (temp == 0xffffffff) {
retval = -ENODEV;
break;
}
xhci_dbg(xhci, "get port status, actual port %d status = 0x%x\n", wIndex, temp);
/* wPortChange bits */ /* wPortChange bits */
if (temp & PORT_CSC) if (raw_port_status & PORT_CSC)
status |= USB_PORT_STAT_C_CONNECTION << 16; status |= USB_PORT_STAT_C_CONNECTION << 16;
if (temp & PORT_PEC) if (raw_port_status & PORT_PEC)
status |= USB_PORT_STAT_C_ENABLE << 16; status |= USB_PORT_STAT_C_ENABLE << 16;
if ((temp & PORT_OCC)) if ((raw_port_status & PORT_OCC))
status |= USB_PORT_STAT_C_OVERCURRENT << 16; status |= USB_PORT_STAT_C_OVERCURRENT << 16;
if ((temp & PORT_RC)) if ((raw_port_status & PORT_RC))
status |= USB_PORT_STAT_C_RESET << 16; status |= USB_PORT_STAT_C_RESET << 16;
/* USB3.0 only */ /* USB3.0 only */
if (hcd->speed == HCD_USB3) { if (hcd->speed == HCD_USB3) {
if ((temp & PORT_PLC)) if ((raw_port_status & PORT_PLC))
status |= USB_PORT_STAT_C_LINK_STATE << 16; status |= USB_PORT_STAT_C_LINK_STATE << 16;
if ((temp & PORT_WRC)) if ((raw_port_status & PORT_WRC))
status |= USB_PORT_STAT_C_BH_RESET << 16; status |= USB_PORT_STAT_C_BH_RESET << 16;
} }
if (hcd->speed != HCD_USB3) { if (hcd->speed != HCD_USB3) {
if ((temp & PORT_PLS_MASK) == XDEV_U3 if ((raw_port_status & PORT_PLS_MASK) == XDEV_U3
&& (temp & PORT_POWER)) && (raw_port_status & PORT_POWER))
status |= USB_PORT_STAT_SUSPEND; status |= USB_PORT_STAT_SUSPEND;
} }
if ((temp & PORT_PLS_MASK) == XDEV_RESUME && if ((raw_port_status & PORT_PLS_MASK) == XDEV_RESUME &&
!DEV_SUPERSPEED(temp)) { !DEV_SUPERSPEED(raw_port_status)) {
if ((temp & PORT_RESET) || !(temp & PORT_PE)) if ((raw_port_status & PORT_RESET) ||
goto error; !(raw_port_status & PORT_PE))
return 0xffffffff;
if (time_after_eq(jiffies, if (time_after_eq(jiffies,
bus_state->resume_done[wIndex])) { bus_state->resume_done[wIndex])) {
xhci_dbg(xhci, "Resume USB2 port %d\n", xhci_dbg(xhci, "Resume USB2 port %d\n",
...@@ -646,7 +593,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, ...@@ -646,7 +593,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
wIndex + 1); wIndex + 1);
if (!slot_id) { if (!slot_id) {
xhci_dbg(xhci, "slot_id is zero\n"); xhci_dbg(xhci, "slot_id is zero\n");
goto error; return 0xffffffff;
} }
xhci_ring_device(xhci, slot_id); xhci_ring_device(xhci, slot_id);
bus_state->port_c_suspend |= 1 << wIndex; bus_state->port_c_suspend |= 1 << wIndex;
...@@ -661,24 +608,24 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, ...@@ -661,24 +608,24 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
status |= USB_PORT_STAT_SUSPEND; status |= USB_PORT_STAT_SUSPEND;
} }
} }
if ((temp & PORT_PLS_MASK) == XDEV_U0 if ((raw_port_status & PORT_PLS_MASK) == XDEV_U0
&& (temp & PORT_POWER) && (raw_port_status & PORT_POWER)
&& (bus_state->suspended_ports & (1 << wIndex))) { && (bus_state->suspended_ports & (1 << wIndex))) {
bus_state->suspended_ports &= ~(1 << wIndex); bus_state->suspended_ports &= ~(1 << wIndex);
if (hcd->speed != HCD_USB3) if (hcd->speed != HCD_USB3)
bus_state->port_c_suspend |= 1 << wIndex; bus_state->port_c_suspend |= 1 << wIndex;
} }
if (temp & PORT_CONNECT) { if (raw_port_status & PORT_CONNECT) {
status |= USB_PORT_STAT_CONNECTION; status |= USB_PORT_STAT_CONNECTION;
status |= xhci_port_speed(temp); status |= xhci_port_speed(raw_port_status);
} }
if (temp & PORT_PE) if (raw_port_status & PORT_PE)
status |= USB_PORT_STAT_ENABLE; status |= USB_PORT_STAT_ENABLE;
if (temp & PORT_OC) if (raw_port_status & PORT_OC)
status |= USB_PORT_STAT_OVERCURRENT; status |= USB_PORT_STAT_OVERCURRENT;
if (temp & PORT_RESET) if (raw_port_status & PORT_RESET)
status |= USB_PORT_STAT_RESET; status |= USB_PORT_STAT_RESET;
if (temp & PORT_POWER) { if (raw_port_status & PORT_POWER) {
if (hcd->speed == HCD_USB3) if (hcd->speed == HCD_USB3)
status |= USB_SS_PORT_STAT_POWER; status |= USB_SS_PORT_STAT_POWER;
else else
...@@ -686,16 +633,97 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, ...@@ -686,16 +633,97 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
} }
/* Update Port Link State for super speed ports*/ /* Update Port Link State for super speed ports*/
if (hcd->speed == HCD_USB3) { if (hcd->speed == HCD_USB3) {
xhci_hub_report_link_state(&status, temp); xhci_hub_report_link_state(&status, raw_port_status);
/* /*
* Verify if all USB3 Ports Have entered U0 already. * Verify if all USB3 Ports Have entered U0 already.
* Delete Compliance Mode Timer if so. * Delete Compliance Mode Timer if so.
*/ */
xhci_del_comp_mod_timer(xhci, temp, wIndex); xhci_del_comp_mod_timer(xhci, raw_port_status, wIndex);
} }
if (bus_state->port_c_suspend & (1 << wIndex)) if (bus_state->port_c_suspend & (1 << wIndex))
status |= 1 << USB_PORT_FEAT_C_SUSPEND; status |= 1 << USB_PORT_FEAT_C_SUSPEND;
return status;
}
int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
u16 wIndex, char *buf, u16 wLength)
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
int max_ports;
unsigned long flags;
u32 temp, status;
int retval = 0;
__le32 __iomem **port_array;
int slot_id;
struct xhci_bus_state *bus_state;
u16 link_state = 0;
u16 wake_mask = 0;
u16 timeout = 0;
max_ports = xhci_get_ports(hcd, &port_array);
bus_state = &xhci->bus_state[hcd_index(hcd)];
spin_lock_irqsave(&xhci->lock, flags);
switch (typeReq) {
case GetHubStatus:
/* No power source, over-current reported per port */
memset(buf, 0, 4);
break;
case GetHubDescriptor:
/* Check to make sure userspace is asking for the USB 3.0 hub
* descriptor for the USB 3.0 roothub. If not, we stall the
* endpoint, like external hubs do.
*/
if (hcd->speed == HCD_USB3 &&
(wLength < USB_DT_SS_HUB_SIZE ||
wValue != (USB_DT_SS_HUB << 8))) {
xhci_dbg(xhci, "Wrong hub descriptor type for "
"USB 3.0 roothub.\n");
goto error;
}
xhci_hub_descriptor(hcd, xhci,
(struct usb_hub_descriptor *) buf);
break;
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
if ((wValue & 0xff00) != (USB_DT_BOS << 8))
goto error;
if (hcd->speed != HCD_USB3)
goto error;
/* Set the U1 and U2 exit latencies. */
memcpy(buf, &usb_bos_descriptor,
USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE);
temp = xhci_readl(xhci, &xhci->cap_regs->hcs_params3);
buf[12] = HCS_U1_LATENCY(temp);
put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]);
/* Indicate whether the host has LTM support. */
temp = xhci_readl(xhci, &xhci->cap_regs->hcc_params);
if (HCC_LTC(temp))
buf[8] |= USB_LTM_SUPPORT;
spin_unlock_irqrestore(&xhci->lock, flags);
return USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE;
case GetPortStatus:
if (!wIndex || wIndex > max_ports)
goto error;
wIndex--;
temp = xhci_readl(xhci, port_array[wIndex]);
if (temp == 0xffffffff) {
retval = -ENODEV;
break;
}
status = xhci_get_port_status(hcd, bus_state, port_array,
wIndex, temp);
if (status == 0xffffffff)
goto error;
xhci_dbg(xhci, "get port status, actual port %d status = 0x%x\n",
wIndex, temp);
xhci_dbg(xhci, "Get port status returned 0x%x\n", status); xhci_dbg(xhci, "Get port status returned 0x%x\n", status);
put_unaligned(cpu_to_le32(status), (__le32 *) buf); put_unaligned(cpu_to_le32(status), (__le32 *) buf);
break; break;
case SetPortFeature: case SetPortFeature:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment