Skip to content

Commit

Permalink
MLK-17380-4 usb: host: xhci: add EH SINGLE_STEP_SET_FEATURE Test for …
Browse files Browse the repository at this point in the history
…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 8d46e3b)
  • Loading branch information
Peter Chen authored and Dong Aisheng committed Nov 25, 2019
1 parent a5395cb commit fd48765
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 0 deletions.
9 changes: 9 additions & 0 deletions drivers/usb/host/xhci-hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
123 changes: 123 additions & 0 deletions drivers/usb/host/xhci-ring.c
Original file line number Diff line number Diff line change
Expand Up @@ -3519,6 +3519,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
Expand Down
1 change: 1 addition & 0 deletions drivers/usb/host/xhci.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
10 changes: 10 additions & 0 deletions drivers/usb/host/xhci.h
Original file line number Diff line number Diff line change
Expand Up @@ -2132,6 +2132,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);
Expand Down

0 comments on commit fd48765

Please sign in to comment.