aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/layerscape/patches-5.4/820-usb-0015-MLK-17380-4-usb-host-xhci-add-EH-SINGLE_STEP_SET_FEA.patch
diff options
context:
space:
mode:
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.patch203
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);