Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eae5b176 authored by Sarah Sharp's avatar Sarah Sharp
Browse files

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
Loading
Loading
Loading
Loading
+119 −91
Original line number Diff line number Diff line
@@ -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);
	int max_ports;
	unsigned long flags;
	u32 temp, status;
	int retval = 0;
	__le32 __iomem **port_array;
	u32 status = 0;
	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 */
		if (temp & PORT_CSC)
	if (raw_port_status & PORT_CSC)
		status |= USB_PORT_STAT_C_CONNECTION << 16;
		if (temp & PORT_PEC)
	if (raw_port_status & PORT_PEC)
		status |= USB_PORT_STAT_C_ENABLE << 16;
		if ((temp & PORT_OCC))
	if ((raw_port_status & PORT_OCC))
		status |= USB_PORT_STAT_C_OVERCURRENT << 16;
		if ((temp & PORT_RC))
	if ((raw_port_status & PORT_RC))
		status |= USB_PORT_STAT_C_RESET << 16;
	/* USB3.0 only */
	if (hcd->speed == HCD_USB3) {
			if ((temp & PORT_PLC))
		if ((raw_port_status & PORT_PLC))
			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;
	}

	if (hcd->speed != HCD_USB3) {
			if ((temp & PORT_PLS_MASK) == XDEV_U3
					&& (temp & PORT_POWER))
		if ((raw_port_status & PORT_PLS_MASK) == XDEV_U3
				&& (raw_port_status & PORT_POWER))
			status |= USB_PORT_STAT_SUSPEND;
	}
		if ((temp & PORT_PLS_MASK) == XDEV_RESUME &&
				!DEV_SUPERSPEED(temp)) {
			if ((temp & PORT_RESET) || !(temp & PORT_PE))
				goto error;
	if ((raw_port_status & PORT_PLS_MASK) == XDEV_RESUME &&
			!DEV_SUPERSPEED(raw_port_status)) {
		if ((raw_port_status & PORT_RESET) ||
				!(raw_port_status & PORT_PE))
			return 0xffffffff;
		if (time_after_eq(jiffies,
					bus_state->resume_done[wIndex])) {
			xhci_dbg(xhci, "Resume USB2 port %d\n",
@@ -646,7 +593,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
					wIndex + 1);
			if (!slot_id) {
				xhci_dbg(xhci, "slot_id is zero\n");
					goto error;
				return 0xffffffff;
			}
			xhci_ring_device(xhci, slot_id);
			bus_state->port_c_suspend |= 1 << wIndex;
@@ -661,24 +608,24 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
			status |= USB_PORT_STAT_SUSPEND;
		}
	}
		if ((temp & PORT_PLS_MASK) == XDEV_U0
			&& (temp & PORT_POWER)
	if ((raw_port_status & PORT_PLS_MASK) == XDEV_U0
			&& (raw_port_status & PORT_POWER)
			&& (bus_state->suspended_ports & (1 << wIndex))) {
		bus_state->suspended_ports &= ~(1 << wIndex);
		if (hcd->speed != HCD_USB3)
			bus_state->port_c_suspend |= 1 << wIndex;
	}
		if (temp & PORT_CONNECT) {
	if (raw_port_status & PORT_CONNECT) {
		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;
		if (temp & PORT_OC)
	if (raw_port_status & PORT_OC)
		status |= USB_PORT_STAT_OVERCURRENT;
		if (temp & PORT_RESET)
	if (raw_port_status & PORT_RESET)
		status |= USB_PORT_STAT_RESET;
		if (temp & PORT_POWER) {
	if (raw_port_status & PORT_POWER) {
		if (hcd->speed == HCD_USB3)
			status |= USB_SS_PORT_STAT_POWER;
		else
@@ -686,16 +633,97 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
	}
	/* Update Port Link State for super speed ports*/
	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.
		 * 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))
		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);

		put_unaligned(cpu_to_le32(status), (__le32 *) buf);
		break;
	case SetPortFeature: