diff options
-rw-r--r-- | Documentation/devicetree/bindings/usb/usb-xhci.txt | 2 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 134 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 3 | ||||
-rw-r--r-- | drivers/usb/core/otg_whitelist.h | 73 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hcd.c | 6 | ||||
-rw-r--r-- | drivers/usb/host/ehci-hub.c | 139 | ||||
-rw-r--r-- | drivers/usb/host/ehci-q.c | 2 | ||||
-rw-r--r-- | drivers/usb/host/xhci-hub.c | 12 | ||||
-rw-r--r-- | drivers/usb/host/xhci-plat.c | 4 | ||||
-rw-r--r-- | drivers/usb/host/xhci-ring.c | 130 | ||||
-rw-r--r-- | drivers/usb/host/xhci.c | 5 | ||||
-rw-r--r-- | drivers/usb/host/xhci.h | 12 | ||||
-rw-r--r-- | include/linux/usb/hcd.h | 13 |
13 files changed, 364 insertions, 171 deletions
diff --git a/Documentation/devicetree/bindings/usb/usb-xhci.txt b/Documentation/devicetree/bindings/usb/usb-xhci.txt index b49b819571f9..170c56477c97 100644 --- a/Documentation/devicetree/bindings/usb/usb-xhci.txt +++ b/Documentation/devicetree/bindings/usb/usb-xhci.txt @@ -42,6 +42,8 @@ Optional properties: - quirk-broken-port-ped: set if the controller has broken port disable mechanism - imod-interval-ns: default interrupt moderation interval is 5000ns - phys : see usb-hcd.yaml in the current directory + - usb3-resume-missing-cas: set if the CAS(Cold Attach Status) may lose in case + device plugged in while system sleep. additionally the properties from usb-hcd.yaml (in the current directory) are supported. diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index f225eaa98ff8..ec01bf5837c2 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2104,6 +2104,140 @@ int usb_hcd_get_frame_number (struct usb_device *udev) } /*-------------------------------------------------------------------------*/ +#ifdef CONFIG_USB_HCD_TEST_MODE + +static void usb_ehset_completion(struct urb *urb) +{ + struct completion *done = urb->context; + + complete(done); +} +/* + * Allocate and initialize a control URB. This request will be used by the + * EHSET SINGLE_STEP_SET_FEATURE test in which the DATA and STATUS stages + * of the GetDescriptor request are sent 15 seconds after the SETUP stage. + * Return NULL if failed. + */ +static struct urb *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 = (usb_pipein(urb->pipe) ? udev->ep_in : udev->ep_out) + [usb_pipeendpoint(urb->pipe)]; + if (!ep) { + usb_free_urb(urb); + return NULL; + } + + urb->ep = ep; + urb->dev = udev; + urb->setup_packet = (void *)dr; + urb->transfer_buffer = buf; + urb->transfer_buffer_length = USB_DT_DEVICE_SIZE; + urb->complete = usb_ehset_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); + urb->setup_dma = dma_map_single( + hcd->self.sysdev, + urb->setup_packet, + sizeof(struct usb_ctrlrequest), + DMA_TO_DEVICE); + urb->transfer_dma = dma_map_single( + hcd->self.sysdev, + urb->transfer_buffer, + urb->transfer_buffer_length, + DMA_FROM_DEVICE); + urb->context = done; + return urb; +} + +int 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 usb_device_descriptor *buf; + DECLARE_COMPLETION_ONSTACK(done); + + /* Obtain udev of the rhub's child port */ + udev = usb_hub_find_child(hcd->self.root_hub, port); + if (!udev) { + dev_err(hcd->self.controller, "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 = request_single_step_set_feature_urb(udev, dr, buf, &done); + if (!urb) + goto cleanup; + + /* Submit just the SETUP stage */ + retval = hcd->driver->submit_single_step_set_feature(hcd, urb, 1); + if (retval) + goto out1; + if (!wait_for_completion_timeout(&done, msecs_to_jiffies(2000))) { + usb_kill_urb(urb); + retval = -ETIMEDOUT; + dev_err(hcd->self.controller, + "%s SETUP stage timed out on ep0\n", __func__); + goto out1; + } + msleep(15 * 1000); + + /* Complete remaining DATA and STATUS stages using the same URB */ + urb->status = -EINPROGRESS; + usb_get_urb(urb); + atomic_inc(&urb->use_count); + atomic_inc(&urb->dev->urbnum); + retval = hcd->driver->submit_single_step_set_feature(hcd, urb, 0); + if (!retval && !wait_for_completion_timeout(&done, + msecs_to_jiffies(2000))) { + usb_kill_urb(urb); + retval = -ETIMEDOUT; + dev_err(hcd->self.controller, + "%s IN stage timed out on ep0\n", __func__); + } +out1: + usb_free_urb(urb); +cleanup: + kfree(dr); + kfree(buf); + return retval; +} +EXPORT_SYMBOL_GPL(ehset_single_step_set_feature); +#endif /* CONFIG_USB_HCD_TEST_MODE */ + +/*-------------------------------------------------------------------------*/ #ifdef CONFIG_PM diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 236313f41f4a..c39985ea2eca 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -4705,7 +4705,8 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, } if (r) { if (r != -ENODEV) - dev_err(&udev->dev, "device descriptor read/64, error %d\n", + dev_err(&udev->dev, + "device no response, device descriptor read/64, error %d\n", r); retval = -EMSGSIZE; continue; diff --git a/drivers/usb/core/otg_whitelist.h b/drivers/usb/core/otg_whitelist.h index 2ae90158ded7..9d0896174628 100644 --- a/drivers/usb/core/otg_whitelist.h +++ b/drivers/usb/core/otg_whitelist.h @@ -13,35 +13,62 @@ */ static struct usb_device_id whitelist_table[] = { - -/* hubs are optional in OTG, but very handy ... */ -{ USB_DEVICE_INFO(USB_CLASS_HUB, 0, 0), }, -{ USB_DEVICE_INFO(USB_CLASS_HUB, 0, 1), }, - -#ifdef CONFIG_USB_PRINTER /* ignoring nonstatic linkage! */ -/* FIXME actually, printers are NOT supposed to use device classes; - * they're supposed to use interface classes... - */ -{ USB_DEVICE_INFO(7, 1, 1) }, -{ USB_DEVICE_INFO(7, 1, 2) }, -{ USB_DEVICE_INFO(7, 1, 3) }, +/* Add FSL i.mx whitelist, the default list is for USB Compliance Test */ +#if defined(CONFIG_USB_EHSET_TEST_FIXTURE) \ + || defined(CONFIG_USB_EHSET_TEST_FIXTURE_MODULE) +#define TEST_SE0_NAK_PID 0x0101 +#define TEST_J_PID 0x0102 +#define TEST_K_PID 0x0103 +#define TEST_PACKET_PID 0x0104 +#define TEST_HS_HOST_PORT_SUSPEND_RESUME 0x0106 +#define TEST_SINGLE_STEP_GET_DEV_DESC 0x0107 +#define TEST_SINGLE_STEP_SET_FEATURE 0x0108 +{ USB_DEVICE(0x1a0a, TEST_SE0_NAK_PID) }, +{ USB_DEVICE(0x1a0a, TEST_J_PID) }, +{ USB_DEVICE(0x1a0a, TEST_K_PID) }, +{ USB_DEVICE(0x1a0a, TEST_PACKET_PID) }, +{ USB_DEVICE(0x1a0a, TEST_HS_HOST_PORT_SUSPEND_RESUME) }, +{ USB_DEVICE(0x1a0a, TEST_SINGLE_STEP_GET_DEV_DESC) }, +{ USB_DEVICE(0x1a0a, TEST_SINGLE_STEP_SET_FEATURE) }, #endif -#ifdef CONFIG_USB_NET_CDCETHER -/* Linux-USB CDC Ethernet gadget */ -{ USB_DEVICE(0x0525, 0xa4a1), }, -/* Linux-USB CDC Ethernet + RNDIS gadget */ -{ USB_DEVICE(0x0525, 0xa4a2), }, -#endif +#define USB_INTERFACE_CLASS_INFO(cl) \ + .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS, \ + .bInterfaceClass = (cl) -#if IS_ENABLED(CONFIG_USB_TEST) -/* gadget zero, for testing */ -{ USB_DEVICE(0x0525, 0xa4a0), }, +{USB_INTERFACE_CLASS_INFO(USB_CLASS_HUB) }, +#if defined(CONFIG_USB_STORAGE) || defined(CONFIG_USB_STORAGE_MODULE) +{USB_INTERFACE_CLASS_INFO(USB_CLASS_MASS_STORAGE) }, +#endif +#if defined(CONFIG_USB_HID) || defined(CONFIG_USB_HID_MODULE) +{USB_INTERFACE_CLASS_INFO(USB_CLASS_HID) }, #endif { } /* Terminating entry */ }; +static bool match_int_class(struct usb_device_id *id, struct usb_device *udev) +{ + struct usb_host_config *c; + int num_configs, i; + + /* Copy the code from generic.c */ + c = udev->config; + num_configs = udev->descriptor.bNumConfigurations; + for (i = 0; i < num_configs; (i++, c++)) { + struct usb_interface_descriptor *desc = NULL; + + /* It's possible that a config has no interfaces! */ + if (c->desc.bNumInterfaces > 0) + desc = &c->intf_cache[0]->altsetting->desc; + + if (desc && (desc->bInterfaceClass == id->bInterfaceClass)) + return true; + } + + return false; +} + static int is_targeted(struct usb_device *dev) { struct usb_device_id *id = whitelist_table; @@ -90,6 +117,10 @@ static int is_targeted(struct usb_device *dev) (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol)) continue; + if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) && + (!match_int_class(id, dev))) + continue; + return 1; } diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index cf2b7ae93b7e..ba8c799b5521 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -80,7 +80,7 @@ module_param (log2_irq_thresh, int, S_IRUGO); MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes"); /* initial park setting: slower than hw default */ -static unsigned park = 0; +static unsigned park = 3; module_param (park, uint, S_IRUGO); MODULE_PARM_DESC (park, "park setting; 1-3 back-to-back async packets"); @@ -1232,6 +1232,10 @@ static const struct hc_driver ehci_hc_driver = { * device support */ .free_dev = ehci_remove_device, +#ifdef CONFIG_USB_HCD_TEST_MODE + /* EH SINGLE_STEP_SET_FEATURE test support */ + .submit_single_step_set_feature = ehci_submit_single_step_set_feature, +#endif }; void ehci_init_driver(struct hc_driver *drv, diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index ce0eaf7d7c12..fb4463f03b45 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -725,145 +725,6 @@ ehci_hub_descriptor ( } /*-------------------------------------------------------------------------*/ -#ifdef CONFIG_USB_HCD_TEST_MODE - -#define EHSET_TEST_SINGLE_STEP_SET_FEATURE 0x06 - -static void usb_ehset_completion(struct urb *urb) -{ - struct completion *done = urb->context; - - complete(done); -} -static int submit_single_step_set_feature( - struct usb_hcd *hcd, - struct urb *urb, - int is_setup -); - -/* - * Allocate and initialize a control URB. This request will be used by the - * EHSET SINGLE_STEP_SET_FEATURE test in which the DATA and STATUS stages - * of the GetDescriptor request are sent 15 seconds after the SETUP stage. - * Return NULL if failed. - */ -static struct urb *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 = (usb_pipein(urb->pipe) ? udev->ep_in : udev->ep_out) - [usb_pipeendpoint(urb->pipe)]; - if (!ep) { - usb_free_urb(urb); - return NULL; - } - - urb->ep = ep; - urb->dev = udev; - urb->setup_packet = (void *)dr; - urb->transfer_buffer = buf; - urb->transfer_buffer_length = USB_DT_DEVICE_SIZE; - urb->complete = usb_ehset_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); - urb->setup_dma = dma_map_single( - hcd->self.sysdev, - urb->setup_packet, - sizeof(struct usb_ctrlrequest), - DMA_TO_DEVICE); - urb->transfer_dma = dma_map_single( - hcd->self.sysdev, - urb->transfer_buffer, - urb->transfer_buffer_length, - DMA_FROM_DEVICE); - urb->context = done; - return urb; -} - -static int 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 ehci_hcd *ehci = hcd_to_ehci(hcd); - struct usb_device_descriptor *buf; - DECLARE_COMPLETION_ONSTACK(done); - - /* Obtain udev of the rhub's child port */ - udev = usb_hub_find_child(hcd->self.root_hub, port); - if (!udev) { - ehci_err(ehci, "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 = request_single_step_set_feature_urb(udev, dr, buf, &done); - if (!urb) - goto cleanup; - - /* Submit just the SETUP stage */ - retval = submit_single_step_set_feature(hcd, urb, 1); - if (retval) - goto out1; - if (!wait_for_completion_timeout(&done, msecs_to_jiffies(2000))) { - usb_kill_urb(urb); - retval = -ETIMEDOUT; - ehci_err(ehci, "%s SETUP stage timed out on ep0\n", __func__); - goto out1; - } - msleep(15 * 1000); - - /* Complete remaining DATA and STATUS stages using the same URB */ - urb->status = -EINPROGRESS; - usb_get_urb(urb); - atomic_inc(&urb->use_count); - atomic_inc(&urb->dev->urbnum); - retval = submit_single_step_set_feature(hcd, urb, 0); - if (!retval && !wait_for_completion_timeout(&done, - msecs_to_jiffies(2000))) { - usb_kill_urb(urb); - retval = -ETIMEDOUT; - ehci_err(ehci, "%s IN stage timed out on ep0\n", __func__); - } -out1: - usb_free_urb(urb); -cleanup: - kfree(dr); - kfree(buf); - return retval; -} -#endif /* CONFIG_USB_HCD_TEST_MODE */ -/*-------------------------------------------------------------------------*/ int ehci_hub_control( struct usb_hcd *hcd, diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index aa2f77f1506d..6709e5708453 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1154,7 +1154,7 @@ submit_async ( * performed; TRUE - SETUP and FALSE - IN+STATUS * Returns 0 if success */ -static int submit_single_step_set_feature( +static int ehci_submit_single_step_set_feature( struct usb_hcd *hcd, struct urb *urb, int is_setup diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index be5b3be4dfa4..e8db6429a4e3 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -1359,6 +1359,15 @@ 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; +#ifdef CONFIG_USB_HCD_TEST_MODE + if (test_mode == EHSET_TEST_SINGLE_STEP_SET_FEATURE) { + spin_unlock_irqrestore(&xhci->lock, flags); + retval = ehset_single_step_set_feature(hcd, + wIndex + 1); + spin_lock_irqsave(&xhci->lock, flags); + break; + } +#endif if (test_mode > TEST_FORCE_EN || test_mode < TEST_J) goto error; retval = xhci_enter_test_mode(xhci, test_mode, wIndex, @@ -1655,7 +1664,8 @@ static bool xhci_port_missing_cas_quirk(struct xhci_port *port) return false; if (((portsc & PORT_PLS_MASK) != XDEV_POLLING) && - ((portsc & PORT_PLS_MASK) != XDEV_COMP_MODE)) + ((portsc & PORT_PLS_MASK) != XDEV_COMP_MODE) && + ((portsc & PORT_PLS_MASK) != XDEV_RXDETECT)) return false; /* clear wakeup/change bits, and do a warm port reset */ diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index d90cd5ec09cf..4655016eaf45 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -291,6 +291,10 @@ static int xhci_plat_probe(struct platform_device *pdev) device_property_read_u32(tmpdev, "imod-interval-ns", &xhci->imod_interval); + + if (device_property_read_bool(tmpdev, + "usb3-resume-missing-cas")) + xhci->quirks |= XHCI_MISSING_CAS; } hcd->usb_phy = devm_usb_get_phy_by_phandle(sysdev, "usb-phy", 0); diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index e7aab31fd9a5..8000ad9db792 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2028,12 +2028,9 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, switch (trb_comp_code) { case COMP_SUCCESS: - if (trb_type != TRB_STATUS) { - xhci_warn(xhci, "WARN: Success on ctrl %s TRB without IOC set?\n", + if (trb_type != TRB_STATUS) + xhci_dbg(xhci, "Success on ctrl %s TRB without IOC set?\n", (trb_type == TRB_DATA) ? "data" : "setup"); - *status = -ESHUTDOWN; - break; - } *status = 0; break; case COMP_SHORT_PACKET: @@ -3519,6 +3516,129 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, return 0; } +#ifdef CONFIG_USB_HCD_TEST_MODE +/* + * This function prepare TRBs and submits them for the + * SINGLE_STEP_SET_FEATURE Test. + * This is done in two parts: first SETUP req for GetDesc is sent then + * 15 seconds later, the IN stage for GetDesc starts to req data from dev + * + * is_setup : argument decides which of the two stage needs to be + * performed; TRUE - SETUP and FALSE - IN+STATUS + * Returns 0 if success + */ +int xhci_submit_single_step_set_feature(struct usb_hcd *hcd, + struct urb *urb, int is_setup) +{ + int slot_id; + unsigned int ep_index; + struct xhci_ring *ep_ring; + int ret; + 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; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + + /* urb_priv will be free after transcation has completed */ + urb_priv = kzalloc(sizeof(struct urb_priv) + + sizeof(struct xhci_td), GFP_KERNEL); + 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; + + ep_ring = xhci_urb_to_transfer_ring(xhci, urb); + if (!ep_ring) { + ret = -EINVAL; + goto free_priv; + } + + slot_id = urb->dev->slot_id; + ep_index = xhci_get_endpoint_index(&urb->ep->desc); + + setup = (struct usb_ctrlrequest *) urb->setup_packet; + if (is_setup) { + ret = prepare_transfer(xhci, xhci->devs[slot_id], + ep_index, urb->stream_id, + 1, urb, 0, GFP_KERNEL); + if (ret < 0) + goto free_priv; + + start_trb = &ep_ring->enqueue->generic; + start_cycle = ep_ring->cycle_state; + /* Save the DMA address of the last TRB in the TD */ + td->last_trb = ep_ring->enqueue; + field = TRB_IOC | TRB_IDT | TRB_TYPE(TRB_SETUP) | start_cycle; + /* xHCI 1.0/1.1 6.4.1.2.1: Transfer Type field */ + if ((xhci->hci_version >= 0x100) || + (xhci->quirks & XHCI_MTK_HOST)) + field |= TRB_TX_TYPE(TRB_DATA_IN); + + 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), + /* Immediate data in pointer */ + field); + giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id, + start_cycle, start_trb); + return 0; + } + + ret = prepare_transfer(xhci, xhci->devs[slot_id], + ep_index, urb->stream_id, + 2, urb, 0, GFP_KERNEL); + if (ret < 0) + goto free_priv; + + start_trb = &ep_ring->enqueue->generic; + start_cycle = ep_ring->cycle_state; + field = TRB_ISP | TRB_TYPE(TRB_DATA); + + 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); + + if (urb->transfer_buffer_length > 0) { + field |= TRB_DIR_IN; + queue_trb(xhci, ep_ring, true, + lower_32_bits(urb->transfer_dma), + upper_32_bits(urb->transfer_dma), + length_field, + field | ep_ring->cycle_state); + } + + td->last_trb = ep_ring->enqueue; + field = TRB_IOC | TRB_TYPE(TRB_STATUS) | ep_ring->cycle_state; + queue_trb(xhci, ep_ring, false, + 0, + 0, + TRB_INTR_TARGET(0), + field); + + giveback_first_trb(xhci, slot_id, ep_index, 0, + start_cycle, start_trb); + + return 0; +free_priv: + xhci_urb_free_priv(urb_priv); + return ret; +} +#endif /* CONFIG_USB_HCD_TEST_MODE */ + /* * 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.c b/drivers/usb/host/xhci.c index 6c17e3fe181a..2a5d71374855 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -193,7 +193,7 @@ int xhci_reset(struct xhci_hcd *xhci) * Without this delay, the subsequent HC register access, * may result in a system hang very rarely. */ - if (xhci->quirks & XHCI_INTEL_HOST) + if (xhci->quirks & (XHCI_INTEL_HOST | XHCI_CDNS_HOST)) udelay(1000); ret = xhci_handshake(&xhci->op_regs->command, @@ -5355,6 +5355,7 @@ static const struct hc_driver xhci_hc_driver = { .disable_usb3_lpm_timeout = xhci_disable_usb3_lpm_timeout, .find_raw_port_number = xhci_find_raw_port_number, .clear_tt_buffer_complete = xhci_clear_tt_buffer_complete, + .submit_single_step_set_feature = xhci_submit_single_step_set_feature, }; void xhci_init_driver(struct hc_driver *drv, @@ -5371,6 +5372,8 @@ void xhci_init_driver(struct hc_driver *drv, drv->reset = over->reset; if (over->start) drv->start = over->start; + if (over->bus_suspend) + drv->bus_suspend = over->bus_suspend; } } EXPORT_SYMBOL_GPL(xhci_init_driver); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index f9f88626a57a..321cf42f50d9 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1867,6 +1867,7 @@ struct xhci_hcd { #define XHCI_DEFAULT_PM_RUNTIME_ALLOW BIT_ULL(33) #define XHCI_RESET_PLL_ON_DISCONNECT BIT_ULL(34) #define XHCI_SNPS_BROKEN_SUSPEND BIT_ULL(35) +#define XHCI_CDNS_HOST BIT_ULL(36) unsigned int num_active_eps; unsigned int limit_active_eps; @@ -1901,6 +1902,7 @@ struct xhci_driver_overrides { size_t extra_priv_size; int (*reset)(struct usb_hcd *hcd); int (*start)(struct usb_hcd *hcd); + int (*bus_suspend)(struct usb_hcd *hcd); }; #define XHCI_CFC_DELAY 10 @@ -2132,6 +2134,16 @@ int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1); struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd); void xhci_hc_died(struct xhci_hcd *xhci); +#ifdef CONFIG_USB_HCD_TEST_MODE +int xhci_submit_single_step_set_feature(struct usb_hcd *hcd, + struct urb *urb, int is_setup); +#else +static inline int xhci_submit_single_step_set_feature(struct usb_hcd *hcd, + struct urb *urb, int is_setup) +{ + return 0; +} +#endif #ifdef CONFIG_PM int xhci_bus_suspend(struct usb_hcd *hcd); diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 712b2a603645..5e2695d3852c 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -409,7 +409,10 @@ struct hc_driver { int (*find_raw_port_number)(struct usb_hcd *, int); /* Call for power on/off the port if necessary */ int (*port_power)(struct usb_hcd *hcd, int portnum, bool enable); - + /* Call for SINGLE_STEP_SET_FEATURE Test for USB2 EH certification */ +#define EHSET_TEST_SINGLE_STEP_SET_FEATURE 0x06 + int (*submit_single_step_set_feature)(struct usb_hcd *, + struct urb *, int); }; static inline int hcd_giveback_urb_in_bh(struct usb_hcd *hcd) @@ -474,6 +477,14 @@ int usb_hcd_setup_local_mem(struct usb_hcd *hcd, phys_addr_t phys_addr, struct platform_device; extern void usb_hcd_platform_shutdown(struct platform_device *dev); +#ifdef CONFIG_USB_HCD_TEST_MODE +extern int ehset_single_step_set_feature(struct usb_hcd *hcd, int port); +#else +static inline int ehset_single_step_set_feature(struct usb_hcd *hcd, int port) +{ + return 0; +} +#endif /* CONFIG_USB_HCD_TEST_MODE */ #ifdef CONFIG_USB_PCI struct pci_dev; |