コード例 #1
0
ファイル: xhci.c プロジェクト: redareda9/linux
static int xhci_try_enable_msi(struct usb_hcd *hcd)
{
	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
	struct pci_dev  *pdev;
	int ret;

	/* The xhci platform device has set up IRQs through usb_add_hcd. */
	if (xhci->quirks & XHCI_PLAT)
		return 0;

	pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
	/*
	 * Some Fresco Logic host controllers advertise MSI, but fail to
	 * generate interrupts.  Don't even try to enable MSI.
	 */
	if (xhci->quirks & XHCI_BROKEN_MSI)
		goto legacy_irq;

	/* unregister the legacy interrupt */
	if (hcd->irq)
		free_irq(hcd->irq, hcd);
	hcd->irq = 0;

	ret = xhci_setup_msix(xhci);
	if (ret)
		/* fall back to msi*/
		ret = xhci_setup_msi(xhci);

	if (!ret)
		/* hcd->irq is 0, we have MSI */
		return 0;

	if (!pdev->irq) {
		xhci_err(xhci, "No msi-x/msi found and no IRQ in BIOS\n");
		return -EINVAL;
	}

 legacy_irq:
	if (!strlen(hcd->irq_descr))
		snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d",
			 hcd->driver->description, hcd->self.busnum);

	/* fall back to legacy interrupt*/
	ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED,
			hcd->irq_descr, hcd);
	if (ret) {
		xhci_err(xhci, "request interrupt %d failed\n",
				pdev->irq);
		return ret;
	}
	hcd->irq = pdev->irq;
	return 0;
}
コード例 #2
0
void xhci_init_ej188_v00100900(struct xhci_hcd *xhci)
{
	int i, error_flag = 0;
	struct usb_hcd *hcd = xhci_to_hcd(xhci);
	struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
	u8 reg8 = 0;

	for (i = 0; i < CFG_ITEMS_V00100900; i++) {
		pci_write_config_byte(pdev, cfg_items_v00100900[i].offset,
			cfg_items_v00100900[i].value);
	}

	for (i = 0; i < MMIO_ITEMS_V00100900; i++) {
		xhci_writeb(xhci, mmio_items_v00100900[i].value,
			mmio_items_v00100900[i].offset);
	}

	for (i = 0; i < MMIO_ITEMS_V00100900; i++) {
		if ((0x1811 != mmio_items_v00100900[i].offset) && (0 == error_flag)) {
			reg8 = xhci_readb(xhci, mmio_items_v00100900[i].offset);
			if (reg8 != (u8)mmio_items_v00100900[i].value)
				error_flag = 1;
		}
	}

	if (error_flag) {
		for (i = 0; i < MMIO_ITEMS_V00100900; i++) {
			if (0x1811 != mmio_items_v00100900[i].offset) {
				reg8 = xhci_readb(xhci, mmio_items_v00100900[i].offset);
				xhci_err(xhci, "%s - @%04x %02x\n",
					__func__, mmio_items_v00100900[i].offset, reg8);
			}
		}
	}
}
コード例 #3
0
/* Some SS UMS will be enter polling state after plug in with micro A cable.
 * If trigger warm reset, then link can be rescued to U0.
 *
 * This function copy from hub_port_reset function is USB core.
 */
static int dwc3_link_issue_wa(struct xhci_hcd *xhci)
{
	__le32 __iomem **addr;
	int delay_time, ret;
	u32 pls, val, delay;

	addr = dwc3_xhci.xhci->usb3_ports;
	val = xhci_readl(dwc3_xhci.xhci, addr[0]);

	/* If PORTSC.CCS bit haven't set. We can trigger warm reset
	 * to double confirm if really have no device or link can't trained to
	 * U0.
	 */
	if (!(val & PORT_CONNECT)) {
		val |= PORT_WR;
		xhci_writel(xhci, val, addr[0]);
		xhci_dbg(xhci, "%s: trigger warm reset\n", __func__);
	}

	/* Waiting warm reset complete. */
	for (delay_time = 0; delay_time < 800; delay_time += delay) {
		msleep(delay);
		val = xhci_readl(dwc3_xhci.xhci, addr[0]);
		if (!(val & PORT_RESET))
			break;

		if (delay_time >= 20)
			delay = 200;
	}

	if (val & PORT_RESET)
		xhci_err(xhci, "%s port reset failed!\n", __func__);

	return 0;
}
コード例 #4
0
static int xhci_evaluate_context_result(struct xhci_hcd *xhci,
		struct usb_device *udev, int *cmd_status)
{
	int ret;
	struct xhci_virt_device *virt_dev = xhci->devs[udev->slot_id];

	switch (*cmd_status) {
	case COMP_EINVAL:
		dev_warn(&udev->dev, "WARN: xHCI driver setup invalid evaluate "
				"context command.\n");
		ret = -EINVAL;
		break;
	case COMP_EBADSLT:
		dev_warn(&udev->dev, "WARN: slot not enabled for"
				"evaluate context command.\n");
	case COMP_CTX_STATE:
		dev_warn(&udev->dev, "WARN: invalid context state for "
				"evaluate context command.\n");
		xhci_dbg_ctx(xhci, virt_dev->out_ctx, 1);
		ret = -EINVAL;
		break;
	case COMP_SUCCESS:
		dev_dbg(&udev->dev, "Successful evaluate context command\n");
		ret = 0;
		break;
	default:
		xhci_err(xhci, "ERROR: unexpected command completion "
				"code 0x%x.\n", *cmd_status);
		ret = -EINVAL;
		break;
	}
	return ret;
}
コード例 #5
0
ファイル: xhci.c プロジェクト: redareda9/linux
/*
 * Set the run bit and wait for the host to be running.
 */
static int xhci_start(struct xhci_hcd *xhci)
{
	u32 temp;
	int ret;

	temp = readl(&xhci->op_regs->command);
	temp |= (CMD_RUN);
	xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Turn on HC, cmd = 0x%x.",
			temp);
	writel(temp, &xhci->op_regs->command);

	/*
	 * Wait for the HCHalted Status bit to be 0 to indicate the host is
	 * running.
	 */
	ret = xhci_handshake(&xhci->op_regs->status,
			STS_HALT, 0, XHCI_MAX_HALT_USEC);
	if (ret == -ETIMEDOUT)
		xhci_err(xhci, "Host took too long to start, "
				"waited %u microseconds.\n",
				XHCI_MAX_HALT_USEC);
	if (!ret)
		xhci->xhc_state &= ~XHCI_STATE_HALTED;
	return ret;
}
コード例 #6
0
ファイル: xhci.c プロジェクト: redareda9/linux
/*
 * Set up MSI-X
 */
static int xhci_setup_msix(struct xhci_hcd *xhci)
{
	int i, ret = 0;
	struct usb_hcd *hcd = xhci_to_hcd(xhci);
	struct pci_dev *pdev = to_pci_dev(hcd->self.controller);

	/*
	 * calculate number of msi-x vectors supported.
	 * - HCS_MAX_INTRS: the max number of interrupts the host can handle,
	 *   with max number of interrupters based on the xhci HCSPARAMS1.
	 * - num_online_cpus: maximum msi-x vectors per CPUs core.
	 *   Add additional 1 vector to ensure always available interrupt.
	 */
	xhci->msix_count = min(num_online_cpus() + 1,
				HCS_MAX_INTRS(xhci->hcs_params1));

	xhci->msix_entries =
		kmalloc((sizeof(struct msix_entry))*xhci->msix_count,
				GFP_KERNEL);
	if (!xhci->msix_entries) {
		xhci_err(xhci, "Failed to allocate MSI-X entries\n");
		return -ENOMEM;
	}

	for (i = 0; i < xhci->msix_count; i++) {
		xhci->msix_entries[i].entry = i;
		xhci->msix_entries[i].vector = 0;
	}

	ret = pci_enable_msix_exact(pdev, xhci->msix_entries, xhci->msix_count);
	if (ret) {
		xhci_dbg_trace(xhci, trace_xhci_dbg_init,
				"Failed to enable MSI-X");
		goto free_entries;
	}

	for (i = 0; i < xhci->msix_count; i++) {
		ret = request_irq(xhci->msix_entries[i].vector,
				xhci_msi_irq,
				0, "xhci_hcd", xhci_to_hcd(xhci));
		if (ret)
			goto disable_msix;
	}

	hcd->msix_enabled = 1;
	return ret;

disable_msix:
	xhci_dbg_trace(xhci, trace_xhci_dbg_init, "disable MSI-X interrupt");
	xhci_free_irq(xhci);
	pci_disable_msix(pdev);
free_entries:
	kfree(xhci->msix_entries);
	xhci->msix_entries = NULL;
	return ret;
}
コード例 #7
0
ファイル: xhci-pci.c プロジェクト: Vincentxiaojie/xpenology
static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
{
	struct pci_dev		*pdev = to_pci_dev(dev);

	/* Look for vendor-specific quirks */
	if (pdev->vendor == PCI_VENDOR_ID_FRESCO_LOGIC &&
			pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_PDK) {
		if (pdev->revision == 0x0) {
			xhci->quirks |= XHCI_RESET_EP_QUIRK;
			xhci_dbg(xhci, "QUIRK: Fresco Logic xHC needs configure"
					" endpoint cmd after reset endpoint\n");
		}
		/* Fresco Logic confirms: all revisions of this chip do not
		 * support MSI, even though some of them claim to in their PCI
		 * capabilities.
		 */
		xhci->quirks |= XHCI_BROKEN_MSI;
		xhci_dbg(xhci, "QUIRK: Fresco Logic revision %u "
				"has broken MSI implementation\n",
				pdev->revision);
	}

	if (pdev->vendor == PCI_VENDOR_ID_NEC)
		xhci->quirks |= XHCI_NEC_HOST;

	if (pdev->vendor == PCI_VENDOR_ID_AMD && xhci->hci_version == 0x96)
		xhci->quirks |= XHCI_AMD_0x96_HOST;

	/* AMD PLL quirk */
	if (pdev->vendor == PCI_VENDOR_ID_AMD && usb_amd_find_chipset_info())
		xhci->quirks |= XHCI_AMD_PLL_FIX;
	if (pdev->vendor == PCI_VENDOR_ID_INTEL &&
			pdev->device == PCI_DEVICE_ID_INTEL_PANTHERPOINT_XHCI) {
		xhci->quirks |= XHCI_SPURIOUS_SUCCESS;
		xhci->quirks |= XHCI_EP_LIMIT_QUIRK;
		xhci->limit_active_eps = 64;
		xhci->quirks |= XHCI_SW_BW_CHECKING;
	}

#ifdef MY_ABC_HERE
	xhci_vendor = pdev->vendor;
#endif

	if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
			pdev->device == PCI_DEVICE_ID_ASROCK_P67) {
#ifdef MY_ABC_HERE
		xhci_err(xhci, "Etron chip found.\n");
#endif
		xhci->quirks |= XHCI_RESET_ON_RESUME;
		xhci_dbg(xhci, "QUIRK: Resetting on resume\n");
	}
}
コード例 #8
0
static int xhci_setup_msix(struct xhci_hcd *xhci)
{
	int ret;
	struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);

	xhci->msix_count = 0;
	
	xhci->msix_entries = kmalloc(sizeof(struct msix_entry), GFP_KERNEL);
	if (!xhci->msix_entries) {
		xhci_err(xhci, "Failed to allocate MSI-X entries\n");
		return -ENOMEM;
	}
	xhci->msix_entries[0].entry = 0;

	ret = pci_enable_msix(pdev, xhci->msix_entries, xhci->msix_count);
	if (ret) {
		xhci_err(xhci, "Failed to enable MSI-X\n");
		goto free_entries;
	}

	
	ret = request_irq(xhci->msix_entries[0].vector, &xhci_irq, 0,
			"xHCI", xhci_to_hcd(xhci));
	if (ret) {
		xhci_err(xhci, "Failed to allocate MSI-X interrupt\n");
		goto disable_msix;
	}
	xhci_dbg(xhci, "Finished setting up MSI-X\n");
	return 0;

disable_msix:
	pci_disable_msix(pdev);
free_entries:
	kfree(xhci->msix_entries);
	xhci->msix_entries = NULL;
	return ret;
}
コード例 #9
0
void mtk_xhci_dbg_ctx(struct xhci_hcd *xhci,
		  struct xhci_container_ctx *ctx,
		  unsigned int last_ep)
{
	int i;
	/* Fields are 32 bits wide, DMA addresses are in bytes */
	int field_size = 32 / 8;
	struct xhci_slot_ctx *slot_ctx;
	dma_addr_t dma = ctx->dma;
	int csz = HCC_64BYTE_CONTEXT(xhci->hcc_params);

	if (ctx->type == XHCI_CTX_TYPE_INPUT) {
		struct xhci_input_control_ctx *ctrl_ctx =
			xhci_get_input_control_ctx(xhci, ctx);
		xhci_err(xhci, "@%p (virt) @%08llx (dma) %#08x - drop flags\n",
			 &ctrl_ctx->drop_flags, (unsigned long long)dma,
			 ctrl_ctx->drop_flags);
		dma += field_size;
		xhci_err(xhci, "@%p (virt) @%08llx (dma) %#08x - add flags\n",
			 &ctrl_ctx->add_flags, (unsigned long long)dma,
			 ctrl_ctx->add_flags);
		dma += field_size;
		for (i = 0; i < 6; ++i) {
			xhci_err(xhci, "@%p (virt) @%08llx (dma) %#08x - rsvd2[%d]\n",
				 &ctrl_ctx->rsvd2[i], (unsigned long long)dma,
				 ctrl_ctx->rsvd2[i], i);
			dma += field_size;
		}

		if (csz)
			dbg_rsvd64(xhci, (u64 *)ctrl_ctx, dma);
	}

	slot_ctx = xhci_get_slot_ctx(xhci, ctx);
	mtk_xhci_dbg_slot_ctx(xhci, ctx);
	mtk_xhci_dbg_ep_ctx(xhci, ctx, last_ep);
}
コード例 #10
0
int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
{
	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
	unsigned long flags;
	int timeleft;
	int ret;

	spin_lock_irqsave(&xhci->lock, flags);
	ret = xhci_queue_slot_control(xhci, TRB_ENABLE_SLOT, 0);
	if (ret) {
		spin_unlock_irqrestore(&xhci->lock, flags);
		xhci_dbg(xhci, "FIXME: allocate a command ring segment\n");
		return 0;
	}
	xhci_ring_cmd_db(xhci);
	spin_unlock_irqrestore(&xhci->lock, flags);

	
	timeleft = wait_for_completion_interruptible_timeout(&xhci->addr_dev,
			USB_CTRL_SET_TIMEOUT);
	if (timeleft <= 0) {
		xhci_warn(xhci, "%s while waiting for a slot\n",
				timeleft == 0 ? "Timeout" : "Signal");
		
		return 0;
	}

	if (!xhci->slot_id) {
		xhci_err(xhci, "Error while assigning device slot ID\n");
		return 0;
	}
	
	if (!xhci_alloc_virt_device(xhci, xhci->slot_id, udev, GFP_KERNEL)) {
		
		xhci_warn(xhci, "Could not allocate xHCI USB device data structures\n");
		spin_lock_irqsave(&xhci->lock, flags);
		if (!xhci_queue_slot_control(xhci, TRB_DISABLE_SLOT, udev->slot_id))
			xhci_ring_cmd_db(xhci);
		spin_unlock_irqrestore(&xhci->lock, flags);
		return 0;
	}
	udev->slot_id = xhci->slot_id;
	
	
	return 1;
}
コード例 #11
0
ファイル: xhci-hub.c プロジェクト: ReneNyffenegger/linux
static int xhci_exit_test_mode(struct xhci_hcd *xhci)
{
	int retval;

	if (!xhci->test_mode) {
		xhci_err(xhci, "Not in test mode, do nothing.\n");
		return 0;
	}
	if (xhci->test_mode == TEST_FORCE_EN &&
		!(xhci->xhc_state & XHCI_STATE_HALTED)) {
		retval = xhci_halt(xhci);
		if (retval)
			return retval;
	}
	pm_runtime_allow(xhci_to_hcd(xhci)->self.controller);
	xhci->test_mode = 0;
	return xhci_reset(xhci);
}
コード例 #12
0
ファイル: xhci-hub.c プロジェクト: ReneNyffenegger/linux
static int xhci_enter_test_mode(struct xhci_hcd *xhci,
				u16 test_mode, u16 wIndex, unsigned long *flags)
{
	int i, retval;

	/* Disable all Device Slots */
	xhci_dbg(xhci, "Disable all slots\n");
	spin_unlock_irqrestore(&xhci->lock, *flags);
	for (i = 1; i <= HCS_MAX_SLOTS(xhci->hcs_params1); i++) {
		if (!xhci->devs[i])
			continue;

		retval = xhci_disable_slot(xhci, i);
		if (retval)
			xhci_err(xhci, "Failed to disable slot %d, %d. Enter test mode anyway\n",
				 i, retval);
	}
	spin_lock_irqsave(&xhci->lock, *flags);
	/* Put all ports to the Disable state by clear PP */
	xhci_dbg(xhci, "Disable all port (PP = 0)\n");
	/* Power off USB3 ports*/
	for (i = 0; i < xhci->num_usb3_ports; i++)
		xhci_set_port_power(xhci, xhci->shared_hcd, i, false, flags);
	/* Power off USB2 ports*/
	for (i = 0; i < xhci->num_usb2_ports; i++)
		xhci_set_port_power(xhci, xhci->main_hcd, i, false, flags);
	/* Stop the controller */
	xhci_dbg(xhci, "Stop controller\n");
	retval = xhci_halt(xhci);
	if (retval)
		return retval;
	/* Disable runtime PM for test mode */
	pm_runtime_forbid(xhci_to_hcd(xhci)->self.controller);
	/* Set PORTPMSC.PTC field to enter selected test mode */
	/* Port is selected by wIndex. port_id = wIndex + 1 */
	xhci_dbg(xhci, "Enter Test Mode: %d, Port_id=%d\n",
					test_mode, wIndex + 1);
	xhci_port_set_test_mode(xhci, test_mode, wIndex);
	return retval;
}
コード例 #13
0
void mtk_xhci_dbg_ep_ctx(struct xhci_hcd *xhci,
		     struct xhci_container_ctx *ctx,
		     unsigned int last_ep)
{
	int i, j;
	int last_ep_ctx = 31;
	/* Fields are 32 bits wide, DMA addresses are in bytes */
	int field_size = 32 / 8;
	int csz = HCC_64BYTE_CONTEXT(xhci->hcc_params);

	if (last_ep < 31)
		last_ep_ctx = last_ep + 1;
	for (i = 0; i < last_ep_ctx; ++i) {
		struct xhci_ep_ctx *ep_ctx = xhci_get_ep_ctx(xhci, ctx, i);
		dma_addr_t dma = ctx->dma +
			((unsigned long)ep_ctx - (unsigned long)ctx->bytes);

		xhci_err(xhci, "Endpoint %02d Context:\n", i);
		xhci_err(xhci, "@%p (virt) @%08llx (dma) %#08x - ep_info\n",
				&ep_ctx->ep_info,
				(unsigned long long)dma, ep_ctx->ep_info);
		dma += field_size;
		xhci_err(xhci, "@%p (virt) @%08llx (dma) %#08x - ep_info2\n",
				&ep_ctx->ep_info2,
				(unsigned long long)dma, ep_ctx->ep_info2);
		dma += field_size;
		xhci_err(xhci, "@%p (virt) @%08llx (dma) %#08llx - deq\n",
				&ep_ctx->deq,
				(unsigned long long)dma, ep_ctx->deq);
		dma += 2*field_size;
		xhci_err(xhci, "@%p (virt) @%08llx (dma) %#08x - tx_info\n",
				&ep_ctx->tx_info,
				(unsigned long long)dma, ep_ctx->tx_info);
		dma += field_size;
		for (j = 0; j < 3; ++j) {
			xhci_err(xhci, "@%p (virt) @%08llx (dma) %#08x - rsvd[%d]\n",
					&ep_ctx->reserved[j],
					(unsigned long long)dma,
					ep_ctx->reserved[j], j);
			dma += field_size;
		}

		if (csz)
			dbg_rsvd64(xhci, (u64 *)ep_ctx, dma);
	}
}
コード例 #14
0
static int xhci_configure_endpoint_result(struct xhci_hcd *xhci,
		struct usb_device *udev, int *cmd_status)
{
	int ret;

	switch (*cmd_status) {
	case COMP_ENOMEM:
		dev_warn(&udev->dev, "Not enough host controller resources "
				"for new device state.\n");
		ret = -ENOMEM;
		
		break;
	case COMP_BW_ERR:
		dev_warn(&udev->dev, "Not enough bandwidth "
				"for new device state.\n");
		ret = -ENOSPC;
		
		break;
	case COMP_TRB_ERR:
		
		dev_warn(&udev->dev, "ERROR: Endpoint drop flag = 0, "
				"add flag = 1, "
				"and endpoint is not disabled.\n");
		ret = -EINVAL;
		break;
	case COMP_SUCCESS:
		dev_dbg(&udev->dev, "Successful Endpoint Configure command\n");
		ret = 0;
		break;
	default:
		xhci_err(xhci, "ERROR: unexpected command completion "
				"code 0x%x.\n", *cmd_status);
		ret = -EINVAL;
		break;
	}
	return ret;
}
コード例 #15
0
void mtk_xhci_dbg_slot_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx)
{
	/* Fields are 32 bits wide, DMA addresses are in bytes */
	int field_size = 32 / 8;
	int i;

	struct xhci_slot_ctx *slot_ctx = xhci_get_slot_ctx(xhci, ctx);
	dma_addr_t dma = ctx->dma +
		((unsigned long)slot_ctx - (unsigned long)ctx->bytes);
	int csz = HCC_64BYTE_CONTEXT(xhci->hcc_params);

	xhci_err(xhci, "Slot Context:\n");
	xhci_err(xhci, "@%p (virt) @%08llx (dma) %#08x - dev_info\n",
			&slot_ctx->dev_info,
			(unsigned long long)dma, slot_ctx->dev_info);
	dma += field_size;
	xhci_err(xhci, "@%p (virt) @%08llx (dma) %#08x - dev_info2\n",
			&slot_ctx->dev_info2,
			(unsigned long long)dma, slot_ctx->dev_info2);
	dma += field_size;
	xhci_err(xhci, "@%p (virt) @%08llx (dma) %#08x - tt_info\n",
			&slot_ctx->tt_info,
			(unsigned long long)dma, slot_ctx->tt_info);
	dma += field_size;
	xhci_err(xhci, "@%p (virt) @%08llx (dma) %#08x - dev_state\n",
			&slot_ctx->dev_state,
			(unsigned long long)dma, slot_ctx->dev_state);
	dma += field_size;
	for (i = 0; i < 4; ++i) {
		xhci_err(xhci, "@%p (virt) @%08llx (dma) %#08x - rsvd[%d]\n",
				&slot_ctx->reserved[i], (unsigned long long)dma,
				slot_ctx->reserved[i], i);
		dma += field_size;
	}

	if (csz)
		dbg_rsvd64(xhci, (u64 *)slot_ctx, dma);
}
コード例 #16
0
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, max_portpmsc;
	unsigned long flags;
	u32 temp, status;
	int retval = 0;
	__le32 __iomem **port_array;
	__le32 __iomem **portpmsc_array;
	int slot_id;
	struct xhci_bus_state *bus_state;
	u16 link_state = 0;
	u16 wake_mask = 0;
	u8 selector;

	max_ports = xhci_get_ports(hcd, &port_array);
	max_portpmsc = xhci_get_portpmsc(hcd, &portpmsc_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;

		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]);

		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)
			status |= USB_PORT_STAT_C_CONNECTION << 16;
		if (temp & PORT_PEC)
			status |= USB_PORT_STAT_C_ENABLE << 16;
		if ((temp & PORT_OCC))
			status |= USB_PORT_STAT_C_OVERCURRENT << 16;
		if ((temp & PORT_RC))
			status |= USB_PORT_STAT_C_RESET << 16;
		/* USB3.0 only */
		if (hcd->speed == HCD_USB3) {
			if ((temp & PORT_PLC))
				status |= USB_PORT_STAT_C_LINK_STATE << 16;
			if ((temp & PORT_WRC))
				status |= USB_PORT_STAT_C_BH_RESET << 16;
			if ((temp & PORT_CEC))
				status |= USB_PORT_STAT_C_CONFIG_ERROR << 16;
		}

		if (hcd->speed != HCD_USB3) {
			if ((temp & PORT_PLS_MASK) == XDEV_U3
					&& (temp & 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 (time_after_eq(jiffies,
					bus_state->resume_done[wIndex])) {
				xhci_dbg(xhci, "Resume USB2 port %d\n",
					wIndex + 1);
				bus_state->resume_done[wIndex] = 0;
				clear_bit(wIndex, &bus_state->resuming_ports);
				xhci_set_link_state(xhci, port_array, wIndex,
							XDEV_U0);
				xhci_dbg(xhci, "set port %d resume\n",
					wIndex + 1);
				slot_id = xhci_find_slot_id_by_port(hcd, xhci,
								 wIndex + 1);
				if (!slot_id) {
					xhci_dbg(xhci, "slot_id is zero\n");
					goto error;
				}
				xhci_ring_device(xhci, slot_id);
				bus_state->port_c_suspend |= 1 << wIndex;
				bus_state->suspended_ports &= ~(1 << wIndex);
			} else {
				/*
				 * The resume has been signaling for less than
				 * 20ms. Report the port status as SUSPEND,
				 * let the usbcore check port status again
				 * and clear resume signaling later.
				 */
				status |= USB_PORT_STAT_SUSPEND;
			}
		}
		if ((temp & PORT_PLS_MASK) == XDEV_U0
			&& (temp & 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) {
			status |= USB_PORT_STAT_CONNECTION;
			status |= xhci_port_speed(temp);
		}
		if (temp & PORT_PE)
			status |= USB_PORT_STAT_ENABLE;
		if (temp & PORT_OC)
			status |= USB_PORT_STAT_OVERCURRENT;
		if (temp & PORT_RESET)
			status |= USB_PORT_STAT_RESET;
		if (temp & PORT_POWER) {
			if (hcd->speed == HCD_USB3)
				status |= USB_SS_PORT_STAT_POWER;
			else
				status |= USB_PORT_STAT_POWER;
		}
		/* Update Port Link State for super speed ports*/
		if (hcd->speed == HCD_USB3) {
			xhci_hub_report_link_state(xhci, &status, temp);
			/*
			 * Verify if all USB3 Ports Have entered U0 already.
			 * Delete Compliance Mode Timer if so.
			 */
			xhci_del_comp_mod_timer(xhci, temp, wIndex);
		}
		if (bus_state->port_c_suspend & (1 << wIndex))
			status |= 1 << USB_PORT_FEAT_C_SUSPEND;
		xhci_dbg(xhci, "Get port status returned 0x%x\n", status);
		put_unaligned(cpu_to_le32(status), (__le32 *) buf);
		break;
	case SetPortFeature:
		selector = wIndex >> 8;
		if (wValue == USB_PORT_FEAT_LINK_STATE)
			link_state = (wIndex & 0xff00) >> 3;
		if (wValue == USB_PORT_FEAT_REMOTE_WAKE_MASK)
			wake_mask = wIndex & 0xff00;
		wIndex &= 0xff;
		if (!wIndex || wIndex > max_ports)
			goto error;
		wIndex--;
		temp = xhci_readl(xhci, port_array[wIndex]);
		if (temp == 0xffffffff) {
			retval = -ENODEV;
			break;
		}
		temp = xhci_port_state_to_neutral(temp);
		/* FIXME: What new port features do we need to support? */
		switch (wValue) {
		case USB_PORT_FEAT_SUSPEND:
			temp = xhci_readl(xhci, port_array[wIndex]);
			if ((temp & PORT_PLS_MASK) != XDEV_U0) {
				/* Resume the port to U0 first */
				xhci_set_link_state(xhci, port_array, wIndex,
							XDEV_U0);
				spin_unlock_irqrestore(&xhci->lock, flags);
				msleep(10);
				spin_lock_irqsave(&xhci->lock, flags);
			}
			/* In spec software should not attempt to suspend
			 * a port unless the port reports that it is in the
			 * enabled (PED = ??1??,PLS < ??3??) state.
			 */
			temp = xhci_readl(xhci, port_array[wIndex]);
			if ((temp & PORT_PE) == 0 || (temp & PORT_RESET)
				|| (temp & PORT_PLS_MASK) >= XDEV_U3) {
				xhci_warn(xhci, "USB core suspending device "
					  "not in U0/U1/U2.\n");
				goto error;
			}

			slot_id = xhci_find_slot_id_by_port(hcd, xhci,
					wIndex + 1);
			if (!slot_id) {
				xhci_warn(xhci, "slot_id is zero\n");
				goto error;
			}
			/* unlock to execute stop endpoint commands */
			spin_unlock_irqrestore(&xhci->lock, flags);
			xhci_stop_device(xhci, slot_id, 1);
			spin_lock_irqsave(&xhci->lock, flags);

			xhci_set_link_state(xhci, port_array, wIndex, XDEV_U3);

			spin_unlock_irqrestore(&xhci->lock, flags);
			msleep(10); /* wait device to enter */
			spin_lock_irqsave(&xhci->lock, flags);

			temp = xhci_readl(xhci, port_array[wIndex]);
			bus_state->suspended_ports |= 1 << wIndex;
			break;
		case USB_PORT_FEAT_LINK_STATE:
			temp = xhci_readl(xhci, port_array[wIndex]);

			/* Disable port */
			if (link_state == USB_SS_PORT_LS_SS_DISABLED) {
				xhci_dbg(xhci, "Disable port %d\n", wIndex);
				temp = xhci_port_state_to_neutral(temp);
				/*
				 * Clear all change bits, so that we get a new
				 * connection event.
				 */
				temp |= PORT_CSC | PORT_PEC | PORT_WRC |
					PORT_OCC | PORT_RC | PORT_PLC |
					PORT_CEC;
				xhci_writel(xhci, temp | PORT_PE,
					port_array[wIndex]);
				temp = xhci_readl(xhci, port_array[wIndex]);
				break;
			}

			/* Put link in RxDetect (enable port) */
			if (link_state == USB_SS_PORT_LS_RX_DETECT) {
				xhci_dbg(xhci, "Enable port %d\n", wIndex);
				xhci_set_link_state(xhci, port_array, wIndex,
						link_state);
				temp = xhci_readl(xhci, port_array[wIndex]);
				break;
			}

			/* Software should not attempt to set
			 * port link state above '3' (U3) and the port
			 * must be enabled.
			 */
			if ((temp & PORT_PE) == 0 ||
				(link_state > USB_SS_PORT_LS_U3)) {
				xhci_warn(xhci, "Cannot set link state.\n");
				goto error;
			}

			if (link_state == USB_SS_PORT_LS_U3) {
				slot_id = xhci_find_slot_id_by_port(hcd, xhci,
						wIndex + 1);
				if (slot_id) {
					/* unlock to execute stop endpoint
					 * commands */
					spin_unlock_irqrestore(&xhci->lock,
								flags);
					xhci_stop_device(xhci, slot_id, 1);
					spin_lock_irqsave(&xhci->lock, flags);
				}
			}

			xhci_set_link_state(xhci, port_array, wIndex,
						link_state);

			spin_unlock_irqrestore(&xhci->lock, flags);
			msleep(20); /* wait device to enter */
			spin_lock_irqsave(&xhci->lock, flags);

			temp = xhci_readl(xhci, port_array[wIndex]);
			if (link_state == USB_SS_PORT_LS_U3)
				bus_state->suspended_ports |= 1 << wIndex;
			break;
		case USB_PORT_FEAT_POWER:
			/*
			 * Turn on ports, even if there isn't per-port switching.
			 * HC will report connect events even before this is set.
			 * However, khubd will ignore the roothub events until
			 * the roothub is registered.
			 */
			xhci_writel(xhci, temp | PORT_POWER,
					port_array[wIndex]);

			temp = xhci_readl(xhci, port_array[wIndex]);
			xhci_dbg(xhci, "set port power, actual port %d status  = 0x%x\n", wIndex, temp);
			break;
		case USB_PORT_FEAT_RESET:
			temp = (temp | PORT_RESET);
			xhci_writel(xhci, temp, port_array[wIndex]);

			temp = xhci_readl(xhci, port_array[wIndex]);
			xhci_dbg(xhci, "set port reset, actual port %d status  = 0x%x\n", wIndex, temp);
			break;

		/*
		 * For downstream facing ports (these):  one hub port is put
		 * into test mode according to USB2 11.24.2.13, then the hub
		 * must be reset (which for root hub now means rmmod+modprobe,
		 * or else system reboot).  See EHCI 2.3.9 and 4.14 for info
		 * about the EHCI-specific stuff.
		 */
		case USB_PORT_FEAT_TEST:
#ifdef CONFIG_HOST_COMPLIANT_TEST
			retval = xhci_port_test(hcd, selector, wIndex, flags);
			if (retval < 0) {
				xhci_err(xhci, "USB2 Host Test Fail!!!\n");
				goto error;
			}
#endif
			break;
		case USB_PORT_FEAT_REMOTE_WAKE_MASK:
			xhci_set_remote_wake_mask(xhci, port_array,
					wIndex, wake_mask);
			temp = xhci_readl(xhci, port_array[wIndex]);
			xhci_dbg(xhci, "set port remote wake mask, "
					"actual port %d status  = 0x%x\n",
					wIndex, temp);
			break;
		case USB_PORT_FEAT_BH_PORT_RESET:
			temp |= PORT_WR;
			xhci_writel(xhci, temp, port_array[wIndex]);

			temp = xhci_readl(xhci, port_array[wIndex]);
			break;
		default:
			goto error;
		}
		/* unblock any posted writes */
		temp = xhci_readl(xhci, port_array[wIndex]);
		break;
	case ClearPortFeature:
		if (!wIndex || wIndex > max_ports)
			goto error;
		wIndex--;
		temp = xhci_readl(xhci, port_array[wIndex]);
		if (temp == 0xffffffff) {
			retval = -ENODEV;
			break;
		}
		/* FIXME: What new port features do we need to support? */
		temp = xhci_port_state_to_neutral(temp);
		switch (wValue) {
		case USB_PORT_FEAT_SUSPEND:
			temp = xhci_readl(xhci, port_array[wIndex]);
			xhci_dbg(xhci, "clear USB_PORT_FEAT_SUSPEND\n");
			xhci_dbg(xhci, "PORTSC %04x\n", temp);
			if (temp & PORT_RESET)
				goto error;
			if ((temp & PORT_PLS_MASK) == XDEV_U3) {
				if ((temp & PORT_PE) == 0)
					goto error;

				xhci_set_link_state(xhci, port_array, wIndex,
							XDEV_RESUME);
				spin_unlock_irqrestore(&xhci->lock, flags);
				msleep(20);
				spin_lock_irqsave(&xhci->lock, flags);
				xhci_set_link_state(xhci, port_array, wIndex,
							XDEV_U0);
			}
			bus_state->port_c_suspend |= 1 << wIndex;

			slot_id = xhci_find_slot_id_by_port(hcd, xhci,
					wIndex + 1);
			if (!slot_id) {
				xhci_dbg(xhci, "slot_id is zero\n");
				goto error;
			}
			xhci_ring_device(xhci, slot_id);
			break;
		case USB_PORT_FEAT_C_SUSPEND:
			bus_state->port_c_suspend &= ~(1 << wIndex);
		case USB_PORT_FEAT_C_RESET:
		case USB_PORT_FEAT_C_BH_PORT_RESET:
		case USB_PORT_FEAT_C_CONNECTION:
		case USB_PORT_FEAT_C_OVER_CURRENT:
		case USB_PORT_FEAT_C_ENABLE:
		case USB_PORT_FEAT_C_PORT_LINK_STATE:
		case USB_PORT_FEAT_C_PORT_CONFIG_ERROR:
			xhci_clear_port_change_bit(xhci, wValue, wIndex,
					port_array[wIndex], temp);
			break;
		case USB_PORT_FEAT_ENABLE:
			xhci_disable_port(hcd, xhci, wIndex,
					port_array[wIndex], temp);
			break;
		default:
			goto error;
		}
		break;
	default:
error:
		/* "stall" on error */
		retval = -EPIPE;
	}
	spin_unlock_irqrestore(&xhci->lock, flags);
	return retval;
}
コード例 #17
0
ファイル: xhci-hub.c プロジェクト: GAXUSXX/G935FGaXusKernel2
static int xhci_port_test(struct usb_hcd *hcd, u8 selector, u8 port,
		unsigned long flags)
{
	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
	int max_portpmsc;
	__le32 __iomem **portpmsc_array;
	u32 temp;
	int retval = 0;

	xhci_info(xhci, "TEST MODE !!! selector = 0x%x\n", selector);
	xhci_info(xhci, "running XHCI test %x on port %x\n", selector, port);

	max_portpmsc = xhci_get_portpmsc(hcd, &portpmsc_array);

	temp = xhci_readl(xhci, portpmsc_array[port]);
	temp &= ~PORT_TEST(0xf);
	xhci_writel(xhci, temp, portpmsc_array[port]);

	switch (selector) {
	case USB_PORT_TEST_J:
		xhci_info(xhci, "Port Test J State\n");
		/*
		 * For J/K/SE0_NAK/TEST_PACKET/FORCE_ENABLE
		 * 1. Set the Run/Stop bit in the USBCMD register
		 * to a '0' and wait for the HCHalted bit
		 * in the USBSTS regster, to transitio to a '1'
		 * 2. Set the Port Test Control field in the port
		 * under test PORTPMSC register
		 */
		retval = xhci_halt(xhci);
		if (retval < 0)
			goto error;
		temp = xhci_readl(xhci, portpmsc_array[port]);
		temp |= PORT_TEST_J;
		xhci_writel(xhci, temp, portpmsc_array[port]);
		break;
	case USB_PORT_TEST_K:
		xhci_info(xhci, "Port Test K State\n");
		retval = xhci_halt(xhci);
		if (retval < 0)
			goto error;
		temp = xhci_readl(xhci, portpmsc_array[port]);
		temp |= PORT_TEST_K;
		xhci_writel(xhci, temp, portpmsc_array[port]);
		break;
	case USB_PORT_TEST_SE0_NAK:
		xhci_info(xhci, "Port Test SE0_NAK\n");
		retval = xhci_halt(xhci);
		if (retval < 0)
			goto error;
		temp = xhci_readl(xhci, portpmsc_array[port]);
		temp |= PORT_TEST_SE0_NAK;
		xhci_writel(xhci, temp, portpmsc_array[port]);
		break;
	case USB_PORT_TEST_PACKET:
		xhci_info(xhci, "Port Test Packet\n");
		retval = xhci_halt(xhci);
		if (retval < 0)
			goto error;
		temp = xhci_readl(xhci, portpmsc_array[port]);
		temp |= PORT_TEST_PKT;
		xhci_writel(xhci, temp, portpmsc_array[port]);
		break;
	case USB_PORT_TEST_FORCE_ENABLE:
		xhci_info(xhci, "Port Test Force Enable\n");
		retval = xhci_halt(xhci);
		if (retval < 0)
			goto error;
		temp = xhci_readl(xhci, portpmsc_array[port]);
		temp |= PORT_TEST_FORCE;
		xhci_writel(xhci, temp, portpmsc_array[port]);
		break;
	case (EHSET_HS_HOST_PORT_SUSPEND_RESUME & 0xFF):
		xhci_info(xhci, "HS Host Port Suspend Resume\n");
		spin_unlock_irqrestore(&xhci->lock, flags);
		retval = hs_host_port_suspend_resume(hcd, port);
		spin_lock_irqsave(&xhci->lock, flags);
		if (retval < 0)
			goto error;
		break;
	case (EHSET_SINGLE_STEP_GET_DEV_DESC & 0xFF):
		xhci_info(xhci, "EHSET Single Step Get Device Descriptor\n");
		spin_unlock_irqrestore(&xhci->lock, flags);
		retval = single_step_get_dev_desc(hcd, port);
		spin_lock_irqsave(&xhci->lock, flags);
		if (retval < 0)
			goto error;
		break;
	case (EHSET_SINGLE_STEP_SET_FEATURE & 0xFF):
		xhci_info(xhci, "EHSET Single Step Get Device Descriptor\n");
		spin_unlock_irqrestore(&xhci->lock, flags);
		retval = single_step_set_feature(hcd, port);
		spin_lock_irqsave(&xhci->lock, flags);
		if (retval < 0)
			goto error;
		break;
	default:
		xhci_err(xhci, "Unknown Test Mode : %d\n", selector);
		retval = -EINVAL;
		goto error;
	}

	temp = xhci_readl(xhci, portpmsc_array[port]);
	xhci_info(xhci, "PORTPMSC: actual port %d status & control = 0x%x\n",
			port, temp);
	xhci_info(xhci, "USB2.0 Port Test Done !!!\n");
	return retval;

error:
	xhci_err(xhci, "USB2.0 Port Test Error : %d\n", retval);
	return retval;
}
コード例 #18
0
ファイル: xhci-hub.c プロジェクト: GAXUSXX/G935FGaXusKernel2
static int single_step_get_dev_desc(struct usb_hcd *hcd, u8 port)
{
	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
	struct urb      *urb;
	struct usb_device       *hdev;
	struct usb_device       *udev = NULL;
	struct usb_hub		*hub = NULL;
	struct usb_ctrlrequest  setup_packet;
	char data_buffer[USB_DT_DEVICE_SIZE];
	int ret = 0;

	xhci_info(xhci, "Testing SINGLE_STEP_GET_DEV_DESC\n");

	hdev = hcd->self.root_hub;
	if (!hdev) {
		xhci_err(xhci, "EHSET: root_hub pointer is NULL\n");
		ret = -EPIPE;
		goto error;
	}
	hub = usb_hub_to_struct_hub(hdev);
	if (hub == NULL) {
		xhci_err(xhci, "EHSET: hub pointer is NULL\n");
		ret = -EPIPE;
		goto error;
	}

	if (hub->ports[port]->child != NULL)
		udev = hub->ports[port]->child;

	if (!udev) {
		xhci_err(xhci, "EHSET: device available is NOT found\n");
		ret = -EPIPE;
		goto error;
	}

	urb = usb_alloc_urb(0, GFP_ATOMIC);
	if (!urb) {
		xhci_err(xhci, "urb : get alloc failed\n");
		ret = -ENOMEM;
		goto error;
	}

	setup_packet.bRequestType = USB_DIR_IN |
		USB_TYPE_STANDARD | USB_RECIP_DEVICE;
	setup_packet.bRequest = USB_REQ_GET_DESCRIPTOR;
	setup_packet.wValue = (USB_DT_DEVICE << 8);
	setup_packet.wIndex = 0;
	setup_packet.wLength = USB_DT_DEVICE_SIZE;

	urb->dev = udev;
	urb->hcpriv = udev->ep0.hcpriv;
	urb->setup_packet = (unsigned char *)&setup_packet;
	urb->transfer_buffer = data_buffer;
	urb->transfer_buffer_length = USB_DT_DEVICE_SIZE;
	urb->actual_length = 0;
	urb->transfer_flags = URB_DIR_IN | URB_HCD_DRIVER_TEST;
	urb->pipe = usb_rcvctrlpipe(udev, 0);
	urb->ep = usb_pipe_endpoint(udev, urb->pipe);
	if (!urb->ep) {
		xhci_err(xhci, "urb->ep is NULL\n");
		ret = -ENOENT;
		goto error_urb_ep;
	}

	urb->setup_dma = dma_map_single(
			hcd->self.controller,
			urb->setup_packet,
			sizeof(struct usb_ctrlrequest),
			DMA_TO_DEVICE);
	if (dma_mapping_error(hcd->self.controller, urb->setup_dma)) {
		xhci_err(xhci, "setup : dma_map_single failed\n");
		ret = -EBUSY;
		goto error_setup_dma;
	}

	urb->transfer_dma = dma_map_single(
			hcd->self.controller,
			urb->transfer_buffer,
			urb->transfer_buffer_length,
			DMA_TO_DEVICE);
	if (dma_mapping_error(hcd->self.controller, urb->transfer_dma)) {
		xhci_err(xhci, "xfer : dma_map_single failed\n");
		ret = -EBUSY;
		goto error_xfer_dma;
	}

	ret = xhci_urb_enqueue_single_step(hcd, urb, GFP_ATOMIC, 1);

	dma_unmap_single(hcd->self.controller, urb->transfer_dma,
			sizeof(struct usb_ctrlrequest), DMA_TO_DEVICE);
error_xfer_dma:
	dma_unmap_single(hcd->self.controller, urb->setup_dma,
			sizeof(struct usb_ctrlrequest), DMA_TO_DEVICE);
error_setup_dma:
error_urb_ep:
	usb_free_urb(urb);
error:
	return ret;
}
コード例 #19
0
int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
{
	unsigned long flags;
	int timeleft;
	struct xhci_virt_device *virt_dev;
	int ret = 0;
	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
	struct xhci_slot_ctx *slot_ctx;
	struct xhci_input_control_ctx *ctrl_ctx;
	u64 temp_64;

	if (!udev->slot_id) {
		xhci_dbg(xhci, "Bad Slot ID %d\n", udev->slot_id);
		return -EINVAL;
	}

	virt_dev = xhci->devs[udev->slot_id];

	
	if (!udev->config)
		xhci_setup_addressable_virt_dev(xhci, udev);
	
	xhci_dbg(xhci, "Slot ID %d Input Context:\n", udev->slot_id);
	xhci_dbg_ctx(xhci, virt_dev->in_ctx, 2);

	spin_lock_irqsave(&xhci->lock, flags);
	ret = xhci_queue_address_device(xhci, virt_dev->in_ctx->dma,
					udev->slot_id);
	if (ret) {
		spin_unlock_irqrestore(&xhci->lock, flags);
		xhci_dbg(xhci, "FIXME: allocate a command ring segment\n");
		return ret;
	}
	xhci_ring_cmd_db(xhci);
	spin_unlock_irqrestore(&xhci->lock, flags);

	
	timeleft = wait_for_completion_interruptible_timeout(&xhci->addr_dev,
			USB_CTRL_SET_TIMEOUT);
	
	if (timeleft <= 0) {
		xhci_warn(xhci, "%s while waiting for a slot\n",
				timeleft == 0 ? "Timeout" : "Signal");
		
		return -ETIME;
	}

	switch (virt_dev->cmd_status) {
	case COMP_CTX_STATE:
	case COMP_EBADSLT:
		xhci_err(xhci, "Setup ERROR: address device command for slot %d.\n",
				udev->slot_id);
		ret = -EINVAL;
		break;
	case COMP_TX_ERR:
		dev_warn(&udev->dev, "Device not responding to set address.\n");
		ret = -EPROTO;
		break;
	case COMP_SUCCESS:
		xhci_dbg(xhci, "Successful Address Device command\n");
		break;
	default:
		xhci_err(xhci, "ERROR: unexpected command completion "
				"code 0x%x.\n", virt_dev->cmd_status);
		xhci_dbg(xhci, "Slot ID %d Output Context:\n", udev->slot_id);
		xhci_dbg_ctx(xhci, virt_dev->out_ctx, 2);
		ret = -EINVAL;
		break;
	}
	if (ret) {
		return ret;
	}
	temp_64 = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr);
	xhci_dbg(xhci, "Op regs DCBAA ptr = %#016llx\n", temp_64);
	xhci_dbg(xhci, "Slot ID %d dcbaa entry @%p = %#016llx\n",
			udev->slot_id,
			&xhci->dcbaa->dev_context_ptrs[udev->slot_id],
			(unsigned long long)
				xhci->dcbaa->dev_context_ptrs[udev->slot_id]);
	xhci_dbg(xhci, "Output Context DMA address = %#08llx\n",
			(unsigned long long)virt_dev->out_ctx->dma);
	xhci_dbg(xhci, "Slot ID %d Input Context:\n", udev->slot_id);
	xhci_dbg_ctx(xhci, virt_dev->in_ctx, 2);
	xhci_dbg(xhci, "Slot ID %d Output Context:\n", udev->slot_id);
	xhci_dbg_ctx(xhci, virt_dev->out_ctx, 2);
	
	slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->out_ctx);
	udev->devnum = (slot_ctx->dev_state & DEV_ADDR_MASK) + 1;
	
	ctrl_ctx = xhci_get_input_control_ctx(xhci, virt_dev->in_ctx);
	ctrl_ctx->add_flags = 0;
	ctrl_ctx->drop_flags = 0;

	xhci_dbg(xhci, "Device address = %d\n", udev->devnum);
	
	set_bit(udev->devnum, udev->bus->devmap.devicemap);

	return 0;
}
コード例 #20
0
ファイル: xhci-hub.c プロジェクト: Pafcholini/Beta_TW
/*
 * This function implements the USB_PORT_FEAT_TEST handling of the
 * SINGLE_STEP_SET_FEATURE test mode as defined in the Embedded
 * High-Speed Electrical Test (EHSET) specification. This simply
 * issues a GetDescriptor control transfer, with an inserted 15-second
 * delay after the end of the SETUP stage and before the IN token of
 * the DATA stage is set. The idea is that this gives the test operator
 * enough time to configure the oscilloscope to perform a measurement
 * of the response time between the DATA and ACK packets that follow.
 */
static int xhci_ehset_single_step_set_feature(struct usb_hcd *hcd, int port)
{
	int retval = -ENOMEM;
	struct usb_ctrlrequest *dr;
	struct urb *urb;
	struct usb_device *udev;
	struct xhci_hcd	*xhci = hcd_to_xhci(hcd);
	struct usb_device_descriptor *buf;
	unsigned long flags;
	DECLARE_COMPLETION_ONSTACK(done);

	/* Obtain udev of the rhub's child port */
	udev = usb_hub_find_child(hcd->self.root_hub, port);
	if (!udev) {
		xhci_err(xhci, "No device attached to the RootHub\n");
		return -ENODEV;
	}
	buf = kmalloc(USB_DT_DEVICE_SIZE, GFP_KERNEL);
	if (!buf)
		return -ENOMEM;

	dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
	if (!dr) {
		kfree(buf);
		return -ENOMEM;
	}

	/* Fill Setup packet for GetDescriptor */
	dr->bRequestType = USB_DIR_IN;
	dr->bRequest = USB_REQ_GET_DESCRIPTOR;
	dr->wValue = cpu_to_le16(USB_DT_DEVICE << 8);
	dr->wIndex = 0;
	dr->wLength = cpu_to_le16(USB_DT_DEVICE_SIZE);
	urb = xhci_request_single_step_set_feature_urb(udev, dr, buf, &done);
	if (!urb)
		goto cleanup;

	/* Now complete just the SETUP stage */
	spin_lock_irqsave(&xhci->lock, flags);
	retval = xhci_submit_single_step_set_feature(hcd, urb, 1);
	spin_unlock_irqrestore(&xhci->lock, flags);
	if (retval)
		goto out1;

	if (!wait_for_completion_timeout(&done, msecs_to_jiffies(2000))) {
		usb_kill_urb(urb);
		retval = -ETIMEDOUT;
		xhci_err(xhci, "%s SETUP stage timed out on ep0\n", __func__);
		goto out1;
	}

	/* Sleep for 15 seconds; HC will send SOFs during this period */
	msleep(15 * 1000);

	/* Complete remaining DATA and status stages. Re-use same URB */
	urb->status = -EINPROGRESS;
	usb_get_urb(urb);
	atomic_inc(&urb->use_count);
	atomic_inc(&urb->dev->urbnum);

	spin_lock_irqsave(&xhci->lock, flags);
	retval = xhci_submit_single_step_set_feature(hcd, urb, 0);
	spin_unlock_irqrestore(&xhci->lock, flags);
	if (!retval && !wait_for_completion_timeout(&done,
						msecs_to_jiffies(2000))) {
		usb_kill_urb(urb);
		retval = -ETIMEDOUT;
		xhci_err(xhci, "%s IN stage timed out on ep0\n", __func__);
	}
out1:
	usb_free_urb(urb);
cleanup:
	kfree(dr);
	kfree(buf);
	return retval;
}
コード例 #21
0
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;
	u32 __iomem *status_reg = NULL;
	u32 i, command, num_ports, selector;


	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;

		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]);

		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)
			status |= USB_PORT_STAT_C_CONNECTION << 16;
		if (temp & PORT_PEC)
			status |= USB_PORT_STAT_C_ENABLE << 16;
		if ((temp & PORT_OCC))
			status |= USB_PORT_STAT_C_OVERCURRENT << 16;
		if ((temp & PORT_RC))
			status |= USB_PORT_STAT_C_RESET << 16;
		/* USB3.0 only */
		if (hcd->speed == HCD_USB3) {
			if ((temp & PORT_PLC))
				status |= USB_PORT_STAT_C_LINK_STATE << 16;
			if ((temp & 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))
				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 (time_after_eq(jiffies,
					bus_state->resume_done[wIndex])) {
				xhci_dbg(xhci, "Resume USB2 port %d\n",
					wIndex + 1);
				bus_state->resume_done[wIndex] = 0;
				clear_bit(wIndex, &bus_state->resuming_ports);
				xhci_set_link_state(xhci, port_array, wIndex,
							XDEV_U0);
				xhci_dbg(xhci, "set port %d resume\n",
					wIndex + 1);
				slot_id = xhci_find_slot_id_by_port(hcd, xhci,
								 wIndex + 1);
				if (!slot_id) {
					xhci_dbg(xhci, "slot_id is zero\n");
					goto error;
				}
				xhci_ring_device(xhci, slot_id);
				bus_state->port_c_suspend |= 1 << wIndex;
				bus_state->suspended_ports &= ~(1 << wIndex);
			} else {
				/*
				 * The resume has been signaling for less than
				 * 20ms. Report the port status as SUSPEND,
				 * let the usbcore check port status again
				 * and clear resume signaling later.
				 */
				status |= USB_PORT_STAT_SUSPEND;
			}
		}
		if ((temp & PORT_PLS_MASK) == XDEV_U0
			&& (temp & 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) {
			status |= USB_PORT_STAT_CONNECTION;
			status |= xhci_port_speed(temp);
		}
		if (temp & PORT_PE)
			status |= USB_PORT_STAT_ENABLE;
		if (temp & PORT_OC)
			status |= USB_PORT_STAT_OVERCURRENT;
		if (temp & PORT_RESET)
			status |= USB_PORT_STAT_RESET;
		if (temp & PORT_POWER) {
			if (hcd->speed == HCD_USB3)
				status |= USB_SS_PORT_STAT_POWER;
			else
				status |= USB_PORT_STAT_POWER;
		}
		/* Update Port Link State for super speed ports*/
		if (hcd->speed == HCD_USB3) {
			xhci_hub_report_link_state(&status, temp);
			/*
			 * Verify if all USB3 Ports Have entered U0 already.
			 * Delete Compliance Mode Timer if so.
			 */
			xhci_del_comp_mod_timer(xhci, temp, wIndex);
		}
		if (bus_state->port_c_suspend & (1 << wIndex))
			status |= 1 << USB_PORT_FEAT_C_SUSPEND;
		xhci_dbg(xhci, "Get port status returned 0x%x\n", status);
		put_unaligned(cpu_to_le32(status), (__le32 *) buf);
		break;
	case SetPortFeature:
		if (wValue == USB_PORT_FEAT_LINK_STATE)
			link_state = (wIndex & 0xff00) >> 3;
		if (wValue == USB_PORT_FEAT_REMOTE_WAKE_MASK)
			wake_mask = wIndex & 0xff00;
		selector = wIndex >> 8;
		wIndex &= 0xff;
		if (!wIndex || wIndex > max_ports)
			goto error;
		wIndex--;
		status_reg = &xhci->op_regs->port_power_base +
			NUM_PORT_REGS*wIndex;
		temp = xhci_readl(xhci, port_array[wIndex]);
		if (temp == 0xffffffff) {
			retval = -ENODEV;
			break;
		}
		temp = xhci_port_state_to_neutral(temp);
		/* FIXME: What new port features do we need to support? */
		switch (wValue) {
		case USB_PORT_FEAT_SUSPEND:
			temp = xhci_readl(xhci, port_array[wIndex]);
			if ((temp & PORT_PLS_MASK) != XDEV_U0) {
				/* Resume the port to U0 first */
				xhci_set_link_state(xhci, port_array, wIndex,
							XDEV_U0);
				spin_unlock_irqrestore(&xhci->lock, flags);
				msleep(10);
				spin_lock_irqsave(&xhci->lock, flags);
			}
			/* In spec software should not attempt to suspend
			 * a port unless the port reports that it is in the
			 * enabled (PED = ‘1’,PLS < ‘3’) state.
			 */
			temp = xhci_readl(xhci, port_array[wIndex]);
			if ((temp & PORT_PE) == 0 || (temp & PORT_RESET)
				|| (temp & PORT_PLS_MASK) >= XDEV_U3) {
				xhci_warn(xhci, "USB core suspending device "
					  "not in U0/U1/U2.\n");
				goto error;
			}

			slot_id = xhci_find_slot_id_by_port(hcd, xhci,
					wIndex + 1);
			if (!slot_id) {
				xhci_warn(xhci, "slot_id is zero\n");
				goto error;
			}
			/* unlock to execute stop endpoint commands */
			spin_unlock_irqrestore(&xhci->lock, flags);
			xhci_stop_device(xhci, slot_id, 1);
			spin_lock_irqsave(&xhci->lock, flags);

			xhci_set_link_state(xhci, port_array, wIndex, XDEV_U3);

			spin_unlock_irqrestore(&xhci->lock, flags);
			msleep(10); /* wait device to enter */
			spin_lock_irqsave(&xhci->lock, flags);

			temp = xhci_readl(xhci, port_array[wIndex]);
			bus_state->suspended_ports |= 1 << wIndex;
			break;
		case USB_PORT_FEAT_LINK_STATE:
			temp = xhci_readl(xhci, port_array[wIndex]);

			/* Disable port */
			if (link_state == USB_SS_PORT_LS_SS_DISABLED) {
				xhci_dbg(xhci, "Disable port %d\n", wIndex);
				temp = xhci_port_state_to_neutral(temp);
				/*
				 * Clear all change bits, so that we get a new
				 * connection event.
				 */
				temp |= PORT_CSC | PORT_PEC | PORT_WRC |
					PORT_OCC | PORT_RC | PORT_PLC |
					PORT_CEC;
				xhci_writel(xhci, temp | PORT_PE,
					port_array[wIndex]);
				temp = xhci_readl(xhci, port_array[wIndex]);
				break;
			}

			/* Put link in RxDetect (enable port) */
			if (link_state == USB_SS_PORT_LS_RX_DETECT) {
				xhci_dbg(xhci, "Enable port %d\n", wIndex);
				xhci_set_link_state(xhci, port_array, wIndex,
						link_state);
				temp = xhci_readl(xhci, port_array[wIndex]);
				break;
			}

			/* Software should not attempt to set
			 * port link state above '3' (U3) and the port
			 * must be enabled.
			 */
			if ((temp & PORT_PE) == 0 ||
				(link_state > USB_SS_PORT_LS_U3)) {
				xhci_warn(xhci, "Cannot set link state.\n");
				goto error;
			}

			if (link_state == USB_SS_PORT_LS_U3) {
				slot_id = xhci_find_slot_id_by_port(hcd, xhci,
						wIndex + 1);
				if (slot_id) {
					/* unlock to execute stop endpoint
					 * commands */
					spin_unlock_irqrestore(&xhci->lock,
								flags);
					xhci_stop_device(xhci, slot_id, 1);
					spin_lock_irqsave(&xhci->lock, flags);
				}
			}

			xhci_set_link_state(xhci, port_array, wIndex,
						link_state);

			spin_unlock_irqrestore(&xhci->lock, flags);
			msleep(20); /* wait device to enter */
			spin_lock_irqsave(&xhci->lock, flags);

			temp = xhci_readl(xhci, port_array[wIndex]);
			if (link_state == USB_SS_PORT_LS_U3)
				bus_state->suspended_ports |= 1 << wIndex;
			break;
		case USB_PORT_FEAT_POWER:
			/* FIXME Do not turn on BYT XHCI port 6 power,
			 * Disable this port's power to disable HSIC hub
			 */
			 if ((xhci->quirks & XHCI_PORT_DISABLE_QUIRK) &&
				(wIndex == 5)) {
				temp = xhci_readl(xhci, port_array[wIndex]);
				temp &= ~PORT_POWER;
				xhci_writel(xhci, temp, port_array[wIndex]);
				break;
			}
			/*
			 * Turn on ports, even if there isn't per-port switching.
			 * HC will report connect events even before this is set.
			 * However, khubd will ignore the roothub events until
			 * the roothub is registered.
			 */
			xhci_writel(xhci, temp | PORT_POWER,
					port_array[wIndex]);

			temp = xhci_readl(xhci, port_array[wIndex]);
			xhci_dbg(xhci, "set port power, actual port %d status  = 0x%x\n", wIndex, temp);
			break;
		case USB_PORT_FEAT_RESET:
			temp = (temp | PORT_RESET);
			xhci_writel(xhci, temp, port_array[wIndex]);

			temp = xhci_readl(xhci, port_array[wIndex]);
			xhci_dbg(xhci, "set port reset, actual port %d status  = 0x%x\n", wIndex, temp);
			break;
		case USB_PORT_FEAT_REMOTE_WAKE_MASK:
			xhci_set_remote_wake_mask(xhci, port_array,
					wIndex, wake_mask);
			temp = xhci_readl(xhci, port_array[wIndex]);
			xhci_dbg(xhci, "set port remote wake mask, "
					"actual port %d status  = 0x%x\n",
					wIndex, temp);
			break;
		case USB_PORT_FEAT_BH_PORT_RESET:
			temp |= PORT_WR;
			xhci_writel(xhci, temp, port_array[wIndex]);

			temp = xhci_readl(xhci, port_array[wIndex]);
			break;
		case USB_PORT_FEAT_TEST:
			if (!selector || selector >= 5 || !status_reg)
				goto error;
			/*
			 * Disable all Device Slots.
			 */
			for (i = 0; i < MAX_HC_SLOTS; i++) {
				if (xhci->dcbaa->dev_context_ptrs[i]) {
					if (xhci_queue_slot_control(xhci,
						TRB_DISABLE_SLOT, i)) {
						xhci_err(xhci,
						"Disable slot[%d] failed!\n",
						i);
						goto error;
					}
				xhci_dbg(xhci, "Disable Slot[%d].\n", i);
				}
			}
			/*
			 *	All ports shall be in the Disable state (PP = 0)
			 */
			xhci_dbg(xhci, "Disable all port (PP = 0)\n");
			num_ports = HCS_MAX_PORTS(xhci->hcs_params1);
			for (i = 0; i < num_ports; i++) {
				u32 __iomem *sreg =
					&xhci->op_regs->port_status_base +
						NUM_PORT_REGS*i;
				temp = xhci_readl(xhci, sreg);
				temp &= ~PORT_POWER;
				xhci_writel(xhci, temp, sreg);
			}

			/*	Set the Run/Stop (R/S) bit in the USBCMD
			 *	register to a '0' and wait for HCHalted(HCH) bit
			 *	in the USBSTS register, to transition to a '1'.
			 */
			xhci_dbg(xhci, "Stop controller\n");
			command = xhci_readl(xhci, &xhci->op_regs->command);
			command &= ~CMD_RUN;
			xhci_writel(xhci, command, &xhci->op_regs->command);
			if (handshake(xhci, &xhci->op_regs->status,
						STS_HALT, STS_HALT, 100*100)) {
				xhci_warn(xhci, "WARN: xHC CMD_RUN timeout\n");
				return -ETIMEDOUT;
			}

			/*
			 * start to test
			 */
			xhci_dbg(xhci, "test case:");
			switch (selector) {
			case 1:
				xhci_dbg(xhci, "TEST_J\n");
				break;
			case 2:
				xhci_dbg(xhci, "TEST_K\n");
				break;
			case 3:
				xhci_dbg(xhci, "TEST_SE0_NAK\n");
				break;
			case 4:
				xhci_dbg(xhci, "TEST_PACKET\n");
				break;
			default:
				xhci_dbg(xhci, "Invalide test case!\n");
				goto error;
			}
			temp = xhci_readl(xhci, status_reg);
			temp |= selector << 28;
			xhci_writel(xhci, temp, status_reg);
			break;
		default:
			goto error;
		}
		/* unblock any posted writes */
		temp = xhci_readl(xhci, port_array[wIndex]);
		break;
	case ClearPortFeature:
		if (!wIndex || wIndex > max_ports)
			goto error;
		wIndex--;
		temp = xhci_readl(xhci, port_array[wIndex]);
		if (temp == 0xffffffff) {
			retval = -ENODEV;
			break;
		}
		/* FIXME: What new port features do we need to support? */
		temp = xhci_port_state_to_neutral(temp);
		switch (wValue) {
		case USB_PORT_FEAT_SUSPEND:
			temp = xhci_readl(xhci, port_array[wIndex]);
			xhci_dbg(xhci, "clear USB_PORT_FEAT_SUSPEND\n");
			xhci_dbg(xhci, "PORTSC %04x\n", temp);
			if (temp & PORT_RESET)
				goto error;
			if ((temp & PORT_PLS_MASK) == XDEV_U3) {
				if ((temp & PORT_PE) == 0)
					goto error;

				xhci_set_link_state(xhci, port_array, wIndex,
							XDEV_RESUME);
				spin_unlock_irqrestore(&xhci->lock, flags);
				msleep(20);
				spin_lock_irqsave(&xhci->lock, flags);
				xhci_set_link_state(xhci, port_array, wIndex,
							XDEV_U0);
			}
			bus_state->port_c_suspend |= 1 << wIndex;

			slot_id = xhci_find_slot_id_by_port(hcd, xhci,
					wIndex + 1);
			if (!slot_id) {
				xhci_dbg(xhci, "slot_id is zero\n");
				goto error;
			}
			xhci_ring_device(xhci, slot_id);
			break;
		case USB_PORT_FEAT_POWER:
			/*
			 * Turn off ports even if there isn't per-port
			 * swithing. HC will report connect events even
			 * before this is set. However, khubd will ignore
			 * the roothub events until the roothub is registered.
			 */
			xhci_writel(xhci, temp & ~PORT_POWER,
					port_array[wIndex]);

			temp = xhci_readl(xhci, port_array[wIndex]);
			xhci_dbg(xhci, "clear PP, port %d status  = 0x%x\n",
					wIndex, temp);
			break;
		case USB_PORT_FEAT_C_SUSPEND:
			bus_state->port_c_suspend &= ~(1 << wIndex);
		case USB_PORT_FEAT_C_RESET:
		case USB_PORT_FEAT_C_BH_PORT_RESET:
		case USB_PORT_FEAT_C_CONNECTION:
		case USB_PORT_FEAT_C_OVER_CURRENT:
		case USB_PORT_FEAT_C_ENABLE:
		case USB_PORT_FEAT_C_PORT_LINK_STATE:
			xhci_clear_port_change_bit(xhci, wValue, wIndex,
					port_array[wIndex], temp);
			break;
		case USB_PORT_FEAT_ENABLE:
			xhci_disable_port(hcd, xhci, wIndex,
					port_array[wIndex], temp);
			break;
		default:
			goto error;
		}
		break;
	default:
error:
		/* "stall" on error */
		retval = -EPIPE;
	}
	spin_unlock_irqrestore(&xhci->lock, flags);
	return retval;
}