From d6a14bcbdcc2e6c5eabd0e26e6f3834d675040f9 Mon Sep 17 00:00:00 2001 From: Jack Pham Date: Thu, 20 Jun 2013 13:31:37 -0700 Subject: [PATCH 1/5] usb: xhci: Add support for SINGLE_STEP_SET_FEATURE test of EHSET The Embedded High-speed Host Electrical Test (EHSET) procedure defines the SINGLE_STEP_SET_FEATURE test for an embedded USB Host port. Upon activating this test mode, the SETUP stage of a GetDescriptor request is sent and followed by a delay of 15 seconds before finishing with the DATA and STATUS stages. The idea is that this delay will give the test operator sufficient time to configure the oscilloscope to perform a measurement of the response time delay between the latter two stages. This test is not implemented by the EHCI/xHCI host controller itself but can be implemented in software. Similar to commit 9841f37a1cca ("usb: ehci: Add support for SINGLE_STEP_SET_FEATURE test of EHSET"), this patch adds such support to the xHCI driver. Change-Id: I638ca552f6dae735147378f3e6f6068e0003094b Signed-off-by: Jack Pham --- drivers/usb/host/xhci-hub.c | 154 ++++++++++++++++++++++++++++++++++- drivers/usb/host/xhci-ring.c | 144 ++++++++++++++++++++++++++++++++ drivers/usb/host/xhci.h | 4 + 3 files changed, 301 insertions(+), 1 deletion(-) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index b7d23c438756..7752caf6cfe2 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -8,7 +8,7 @@ * Some code borrowed from the Linux EHCI driver. */ - +#include #include #include @@ -1065,6 +1065,151 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, return status; } +static void xhci_single_step_completion(struct urb *urb) +{ + struct completion *done = urb->context; + + complete(done); +} + +/* + * Allocate a URB and initialize the various fields of it. + * This API is used by the single_step_set_feature test of + * EHSET where IN packet of the GetDescriptor request is + * sent 15secs after the SETUP packet. + * Return NULL if failed. + */ +static struct urb *xhci_request_single_step_set_feature_urb( + struct usb_device *udev, + void *dr, + void *buf, + struct completion *done) +{ + struct urb *urb; + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + struct usb_host_endpoint *ep; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) + return NULL; + + urb->pipe = usb_rcvctrlpipe(udev, 0); + ep = udev->ep_in[usb_pipeendpoint(urb->pipe)]; + if (!ep) { + usb_free_urb(urb); + return NULL; + } + + /* + * Initialize the various URB fields as these are used by the HCD + * driver to queue it and as well as when completion happens. + */ + urb->ep = ep; + urb->dev = udev; + urb->setup_packet = dr; + urb->transfer_buffer = buf; + urb->transfer_buffer_length = USB_DT_DEVICE_SIZE; + urb->complete = xhci_single_step_completion; + urb->status = -EINPROGRESS; + urb->actual_length = 0; + urb->transfer_flags = URB_DIR_IN; + usb_get_urb(urb); + atomic_inc(&urb->use_count); + atomic_inc(&urb->dev->urbnum); + usb_hcd_map_urb_for_dma(hcd, urb, GFP_KERNEL); + urb->context = done; + return urb; +} + +/* + * 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; + 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) { + retval = -ENOMEM; + 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; +} + int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { @@ -1359,6 +1504,13 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, /* 4.19.6 Port Test Modes (USB2 Test Mode) */ if (hcd->speed != HCD_USB2) goto error; + if (test_mode == 6) { /* TEST_SINGLE_STEP_SET_FEATURE */ + spin_unlock_irqrestore(&xhci->lock, flags); + retval = xhci_ehset_single_step_set_feature(hcd, + wIndex); + spin_lock_irqsave(&xhci->lock, flags); + break; + } if (test_mode > TEST_FORCE_EN || test_mode < TEST_J) goto error; retval = xhci_enter_test_mode(xhci, test_mode, wIndex, diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index e7aab31fd9a5..c420bdcb1c8e 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -3519,6 +3519,150 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, return 0; } +/* + * Variant of xhci_queue_ctrl_tx() used to implement EHSET + * SINGLE_STEP_SET_FEATURE test mode. It differs in that the control + * transfer is broken up so that the SETUP stage can happen and call + * the URB's completion handler before the DATA/STATUS stages are + * executed by the xHC hardware. This assumes the control transfer is a + * GetDescriptor, with a DATA stage in the IN direction, and an OUT + * STATUS stage. + * + * This function is called twice, usually with a 15-second delay in between. + * - with is_setup==true, the SETUP stage for the control request + * (GetDescriptor) is queued in the TRB ring and sent to HW immediately + * - with is_setup==false, the DATA and STATUS TRBs are queued and exceuted + * + * Caller must have locked xhci->lock + */ +int xhci_submit_single_step_set_feature(struct usb_hcd *hcd, struct urb *urb, + int is_setup) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct xhci_ring *ep_ring; + int num_trbs; + int ret; + unsigned int slot_id, ep_index; + struct usb_ctrlrequest *setup; + struct xhci_generic_trb *start_trb; + int start_cycle; + u32 field, length_field, remainder; + struct urb_priv *urb_priv; + struct xhci_td *td; + + ep_ring = xhci_urb_to_transfer_ring(xhci, urb); + if (!ep_ring) + return -EINVAL; + + /* Need buffer for data stage */ + if (urb->transfer_buffer_length <= 0) + return -EINVAL; + + /* + * Need to copy setup packet into setup TRB, so we can't use the setup + * DMA address. + */ + if (!urb->setup_packet) + return -EINVAL; + setup = (struct usb_ctrlrequest *) urb->setup_packet; + + slot_id = urb->dev->slot_id; + ep_index = xhci_get_endpoint_index(&urb->ep->desc); + + urb_priv = kzalloc(sizeof(struct urb_priv) + + sizeof(struct xhci_td *), GFP_ATOMIC); + if (!urb_priv) + return -ENOMEM; + + td = &urb_priv->td[0]; + urb_priv->num_tds = 1; + urb_priv->num_tds_done = 0; + urb->hcpriv = urb_priv; + + num_trbs = is_setup ? 1 : 2; + + ret = prepare_transfer(xhci, xhci->devs[slot_id], + ep_index, urb->stream_id, + num_trbs, urb, 0, GFP_ATOMIC); + if (ret < 0) { + kfree(urb_priv); + return ret; + } + + /* + * Don't give the first TRB to the hardware (by toggling the cycle bit) + * until we've finished creating all the other TRBs. The ring's cycle + * state may change as we enqueue the other TRBs, so save it too. + */ + start_trb = &ep_ring->enqueue->generic; + start_cycle = ep_ring->cycle_state; + + if (is_setup) { + /* Queue only the setup TRB */ + field = TRB_IDT | TRB_IOC | TRB_TYPE(TRB_SETUP); + if (start_cycle == 0) + field |= 0x1; + + /* xHCI 1.0/1.1 6.4.1.2.1: Transfer Type field */ + if (xhci->hci_version >= 0x100) { + if (setup->bRequestType & USB_DIR_IN) + field |= TRB_TX_TYPE(TRB_DATA_IN); + else + field |= TRB_TX_TYPE(TRB_DATA_OUT); + } + + /* Save the DMA address of the last TRB in the TD */ + td->last_trb = ep_ring->enqueue; + + queue_trb(xhci, ep_ring, false, + setup->bRequestType | setup->bRequest << 8 | + le16_to_cpu(setup->wValue) << 16, + le16_to_cpu(setup->wIndex) | + le16_to_cpu(setup->wLength) << 16, + TRB_LEN(8) | TRB_INTR_TARGET(0), + field); + } else { + /* Queue data TRB */ + field = TRB_ISP | TRB_TYPE(TRB_DATA); + if (start_cycle == 0) + field |= 0x1; + if (setup->bRequestType & USB_DIR_IN) + field |= TRB_DIR_IN; + + remainder = xhci_td_remainder(xhci, 0, + urb->transfer_buffer_length, + urb->transfer_buffer_length, + urb, 1); + + length_field = TRB_LEN(urb->transfer_buffer_length) | + TRB_TD_SIZE(remainder) | + TRB_INTR_TARGET(0); + + queue_trb(xhci, ep_ring, true, + lower_32_bits(urb->transfer_dma), + upper_32_bits(urb->transfer_dma), + length_field, + field); + + /* Save the DMA address of the last TRB in the TD */ + td->last_trb = ep_ring->enqueue; + + /* Queue status TRB */ + field = TRB_IOC | TRB_TYPE(TRB_STATUS); + if (!(setup->bRequestType & USB_DIR_IN)) + field |= TRB_DIR_IN; + + queue_trb(xhci, ep_ring, false, + 0, + 0, + TRB_INTR_TARGET(0), + field | ep_ring->cycle_state); + } + + giveback_first_trb(xhci, slot_id, ep_index, 0, start_cycle, start_trb); + return 0; +} + /* * The transfer burst count field of the isochronous TRB defines the number of * bursts that are required to move all packets in this TD. Only SuperSpeed diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index f9f88626a57a..1f02e38820c0 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -2674,4 +2674,8 @@ static inline const char *xhci_decode_ep_context(u32 info, u32 info2, u64 deq, return str; } +/* EHSET */ +int xhci_submit_single_step_set_feature(struct usb_hcd *hcd, struct urb *urb, + int is_setup); + #endif /* __LINUX_XHCI_HCD_H */ From 4c5b785727058dfb0d3df1e3062adfeefa1260ca Mon Sep 17 00:00:00 2001 From: Mayank Rana Date: Tue, 20 Mar 2018 10:47:43 -0700 Subject: [PATCH 2/5] xhci-plat: Use USB glue driver device as sysdev Currently xhci-plat's parent device is used as sysdev whereas USB driver related page mapping is crated using xhci-plat's parent device's parent device (i.e. USB glue driver device). This results into SMMU page fault when XHCI controller is trying to access queued URB. Fix this issue by checking sysdev is having property "linux,sysdev_is_parent" set, and if it is set, use sysdev->parent as sysdev. Change-Id: Iabf5b661b756c109663ad8b11a5a1dd9330c1171 Signed-off-by: Mayank Rana Signed-off-by: Hemant Kumar --- drivers/usb/host/xhci-plat.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 1d597f8d5c44..008f30234a71 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -193,6 +193,14 @@ static int xhci_plat_probe(struct platform_device *pdev) if (!sysdev) sysdev = &pdev->dev; + /* + * If sysdev dev is having parent i.e. "linux,sysdev_is_parent" is true, + * then use sysdev->parent device. + */ + if (sysdev->parent && sysdev->parent->of_node && + device_property_read_bool(sysdev, "linux,sysdev_is_parent")) + sysdev = sysdev->parent; + /* Try to set 64-bit DMA first */ if (WARN_ON(!sysdev->dma_mask)) /* Platform did not initialize dma_mask */ From 896836350b0062cc768cd039453e95f48141ab5a Mon Sep 17 00:00:00 2001 From: Hemant Kumar Date: Wed, 13 Jun 2018 16:19:25 -0700 Subject: [PATCH 3/5] usb: xhci-plat: Remove system PM call backs system PM callbacks are just calling xhci_suspend() and xhci_resume() functions which are already called from runtime PM callbacks. Calling it from system PM again results into un-clocked register access. Change-Id: I4e3216773246e5f4ec94d8e921b98c51fff2654d Signed-off-by: Hemant Kumar --- drivers/usb/host/xhci-plat.c | 51 ++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 008f30234a71..e5568687a358 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -390,33 +390,21 @@ static int xhci_plat_remove(struct platform_device *dev) return 0; } -static int __maybe_unused xhci_plat_suspend(struct device *dev) +static int __maybe_unused xhci_plat_runtime_idle(struct device *dev) { - struct usb_hcd *hcd = dev_get_drvdata(dev); - struct xhci_hcd *xhci = hcd_to_xhci(hcd); - /* - * xhci_suspend() needs `do_wakeup` to know whether host is allowed - * to do wakeup during suspend. Since xhci_plat_suspend is currently - * only designed for system suspend, device_may_wakeup() is enough - * to dertermine whether host is allowed to do wakeup. Need to - * reconsider this when xhci_plat_suspend enlarges its scope, e.g., - * also applies to runtime suspend. + * When pm_runtime_put_autosuspend() is called on this device, + * after this idle callback returns the PM core will schedule the + * autosuspend if there is any remaining time until expiry. However, + * when reaching this point because the child_count becomes 0, the + * core does not honor autosuspend in that case and results in + * idle/suspend happening immediately. In order to have a delay + * before suspend we have to call pm_runtime_autosuspend() manually. */ - return xhci_suspend(xhci, device_may_wakeup(dev)); -} -static int __maybe_unused xhci_plat_resume(struct device *dev) -{ - struct usb_hcd *hcd = dev_get_drvdata(dev); - struct xhci_hcd *xhci = hcd_to_xhci(hcd); - int ret; - - ret = xhci_priv_resume_quirk(hcd); - if (ret) - return ret; - - return xhci_resume(xhci, 0); + pm_runtime_mark_last_busy(dev); + pm_runtime_autosuspend(dev); + return -EBUSY; } static int __maybe_unused xhci_plat_runtime_suspend(struct device *dev) @@ -431,12 +419,25 @@ static int __maybe_unused xhci_plat_runtime_resume(struct device *dev) { struct usb_hcd *hcd = dev_get_drvdata(dev); struct xhci_hcd *xhci = hcd_to_xhci(hcd); + int ret; - return xhci_resume(xhci, 0); + if (!xhci) + return 0; + + dev_dbg(dev, "xhci-plat runtime resume\n"); + + ret = xhci_priv_resume_quirk(hcd); + if (ret) + return ret; + + ret = xhci_resume(xhci, false); + pm_runtime_mark_last_busy(dev); + + return ret; } static const struct dev_pm_ops xhci_plat_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(xhci_plat_suspend, xhci_plat_resume) + SET_SYSTEM_SLEEP_PM_OPS(NULL, NULL) SET_RUNTIME_PM_OPS(xhci_plat_runtime_suspend, xhci_plat_runtime_resume, From 517f1673b8661e5bdbba3acebc8d1c66c3e44679 Mon Sep 17 00:00:00 2001 From: Hemant Kumar Date: Fri, 17 Mar 2017 14:02:00 -0700 Subject: [PATCH 4/5] usb: core: Enable xhci irq after starting controller There is a possibility of port change event triggering xhci irq as soon as halt bit is cleared in xhci_start(). As a result before xhci state is changed from XHCI_STATE_HALTED to 0 port change event keeps on generated until port status is acknowledged. This does not allow xhci_start() to finish and handle the port change event if irq is keep on getting fired on same core where xhci_start() is running. Fix this issue by disabling irq before starting controller and enable it back after clearing halt bit. Change-Id: I798620f99a7ba522258455642e6e8091ebf2cd34 Signed-off-by: Hemant Kumar --- drivers/usb/host/xhci.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 6c17e3fe181a..8081fee0e45d 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -132,7 +132,13 @@ int xhci_start(struct xhci_hcd *xhci) { u32 temp; int ret; + struct usb_hcd *hcd = xhci_to_hcd(xhci); + /* + * disable irq to avoid xhci_irq flooding due to unhandeled port + * change event in halt state, as soon as xhci_start clears halt bit + */ + disable_irq(hcd->irq); temp = readl(&xhci->op_regs->command); temp |= (CMD_RUN); xhci_dbg_trace(xhci, trace_xhci_dbg_init, "// Turn on HC, cmd = 0x%x.", @@ -153,6 +159,8 @@ int xhci_start(struct xhci_hcd *xhci) /* clear state flags. Including dying, halted or removing */ xhci->xhc_state = 0; + enable_irq(hcd->irq); + return ret; } From 034ca2c3b6a781f49ce246c2248fbdbe24133195 Mon Sep 17 00:00:00 2001 From: Hemant Kumar Date: Thu, 11 May 2017 15:48:48 -0700 Subject: [PATCH 5/5] usb: host: xhci: Change L1 timeout default to 128us L1 timeout default is set to 512us which reduces the possibility of entering in to L1 state for smaller service interval value. This impacts the power saving due to L1. Hence set the timeout to lowest allowed value i.e. 128us. Change-Id: Ic261ba25ddd8097f43dffa5f6af88d97f0e417d3 Signed-off-by: Hemant Kumar --- drivers/usb/host/xhci.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 1f02e38820c0..e76a0650b49f 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -437,8 +437,8 @@ struct xhci_op_regs { #define PORT_L1_TIMEOUT(p)(((p) & 0xff) << 2) #define PORT_BESLD(p)(((p) & 0xf) << 10) -/* use 512 microseconds as USB2 LPM L1 default timeout. */ -#define XHCI_L1_TIMEOUT 512 +/* use 128 microseconds as USB2 LPM L1 default timeout. */ +#define XHCI_L1_TIMEOUT 128 /* Set default HIRD/BESL value to 4 (350/400us) for USB2 L1 LPM resume latency. * Safe to use with mixed HIRD and BESL systems (host and device) and is used