diff options
Diffstat (limited to 'target/linux/layerscape/patches-5.4/820-usb-0015-MLK-17380-4-usb-host-xhci-add-EH-SINGLE_STEP_SET_FEA.patch')
-rw-r--r-- | target/linux/layerscape/patches-5.4/820-usb-0015-MLK-17380-4-usb-host-xhci-add-EH-SINGLE_STEP_SET_FEA.patch | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/target/linux/layerscape/patches-5.4/820-usb-0015-MLK-17380-4-usb-host-xhci-add-EH-SINGLE_STEP_SET_FEA.patch b/target/linux/layerscape/patches-5.4/820-usb-0015-MLK-17380-4-usb-host-xhci-add-EH-SINGLE_STEP_SET_FEA.patch new file mode 100644 index 0000000000..cec6a90682 --- /dev/null +++ b/target/linux/layerscape/patches-5.4/820-usb-0015-MLK-17380-4-usb-host-xhci-add-EH-SINGLE_STEP_SET_FEA.patch @@ -0,0 +1,203 @@ +From 6a313b6072925ab757688328b9f91b16d5a39bb3 Mon Sep 17 00:00:00 2001 +From: Peter Chen <peter.chen@nxp.com> +Date: Fri, 9 Feb 2018 10:11:32 +0800 +Subject: [PATCH] MLK-17380-4 usb: host: xhci: add EH SINGLE_STEP_SET_FEATURE + Test for USB2 + +This function is similar with EHCI's, but implemented using XHCI. +The USB2 host needs to send SETUP packet first, then wait 15 +seconds before DATA (IN) + STATUS stage. + +It is needed at USB Certification test for Embedded Host 2.0, and +the detail is at CH6.4.1.1 of On-The-Go and Embedded Host Supplement +to the USB Revision 2.0 Specification + +Acked-by: Jun Li <jun.li@nxp.com> +Signed-off-by: Peter Chen <peter.chen@nxp.com> +(cherry picked from commit 8d46e3bca527a5d899446d3858274fb5cbab1a1e) +--- + drivers/usb/host/xhci-hub.c | 9 ++++ + drivers/usb/host/xhci-ring.c | 123 +++++++++++++++++++++++++++++++++++++++++++ + drivers/usb/host/xhci.c | 1 + + drivers/usb/host/xhci.h | 10 ++++ + 4 files changed, 143 insertions(+) + +--- a/drivers/usb/host/xhci-hub.c ++++ b/drivers/usb/host/xhci-hub.c +@@ -1378,6 +1378,15 @@ int xhci_hub_control(struct usb_hcd *hcd + /* 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, +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -3545,6 +3545,129 @@ int xhci_queue_ctrl_tx(struct xhci_hcd * + 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 +--- a/drivers/usb/host/xhci.c ++++ b/drivers/usb/host/xhci.c +@@ -5352,6 +5352,7 @@ static const struct hc_driver xhci_hc_dr + .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, +--- a/drivers/usb/host/xhci.h ++++ b/drivers/usb/host/xhci.h +@@ -2141,6 +2141,16 @@ int xhci_find_raw_port_number(struct usb + 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); |