Skip to content

Commit

Permalink
usb: cdnsp: fix lack of ZLP for ep0
Browse files Browse the repository at this point in the history
commit ae423ef upstream.

Patch implements the handling of ZLP for control transfer.
To send the ZLP driver must prepare the extra TRB in TD with
length set to zero and TRB type to TRB_NORMAL.
The first TRB must have set TRB_CHAIN flag, TD_SIZE = 1
and TRB type to TRB_DATA.

Fixes: 3d82904 ("usb: cdnsp: cdns3 Add main part of Cadence USBSSP DRD Driver")
cc: <stable@vger.kernel.org>
Reviewed-by: Peter Chen <peter.chen@kernel.org>
Signed-off-by: Pawel Laszczak <pawell@cadence.com>
Link: https://lore.kernel.org/r/20221122085138.332434-1-pawell@cadence.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
pawellcdns authored and gregkh committed Dec 31, 2022
1 parent bcac79d commit fcacd97
Showing 1 changed file with 32 additions and 10 deletions.
42 changes: 32 additions & 10 deletions drivers/usb/cdns3/cdnsp-ring.c
Expand Up @@ -2006,10 +2006,11 @@ int cdnsp_queue_bulk_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq)

int cdnsp_queue_ctrl_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq)
{
u32 field, length_field, remainder;
u32 field, length_field, zlp = 0;
struct cdnsp_ep *pep = preq->pep;
struct cdnsp_ring *ep_ring;
int num_trbs;
u32 maxp;
int ret;

ep_ring = cdnsp_request_to_transfer_ring(pdev, preq);
Expand All @@ -2019,33 +2020,54 @@ int cdnsp_queue_ctrl_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq)
/* 1 TRB for data, 1 for status */
num_trbs = (pdev->three_stage_setup) ? 2 : 1;

maxp = usb_endpoint_maxp(pep->endpoint.desc);

if (preq->request.zero && preq->request.length &&
(preq->request.length % maxp == 0)) {
num_trbs++;
zlp = 1;
}

ret = cdnsp_prepare_transfer(pdev, preq, num_trbs);
if (ret)
return ret;

/* If there's data, queue data TRBs */
if (pdev->ep0_expect_in)
field = TRB_TYPE(TRB_DATA) | TRB_IOC;
else
field = TRB_ISP | TRB_TYPE(TRB_DATA) | TRB_IOC;

if (preq->request.length > 0) {
remainder = cdnsp_td_remainder(pdev, 0, preq->request.length,
preq->request.length, preq, 1, 0);
field = TRB_TYPE(TRB_DATA);

length_field = TRB_LEN(preq->request.length) |
TRB_TD_SIZE(remainder) | TRB_INTR_TARGET(0);
if (zlp)
field |= TRB_CHAIN;
else
field |= TRB_IOC | (pdev->ep0_expect_in ? 0 : TRB_ISP);

if (pdev->ep0_expect_in)
field |= TRB_DIR_IN;

length_field = TRB_LEN(preq->request.length) |
TRB_TD_SIZE(zlp) | TRB_INTR_TARGET(0);

cdnsp_queue_trb(pdev, ep_ring, true,
lower_32_bits(preq->request.dma),
upper_32_bits(preq->request.dma), length_field,
field | ep_ring->cycle_state |
TRB_SETUPID(pdev->setup_id) |
pdev->setup_speed);

if (zlp) {
field = TRB_TYPE(TRB_NORMAL) | TRB_IOC;

if (!pdev->ep0_expect_in)
field = TRB_ISP;

cdnsp_queue_trb(pdev, ep_ring, true,
lower_32_bits(preq->request.dma),
upper_32_bits(preq->request.dma), 0,
field | ep_ring->cycle_state |
TRB_SETUPID(pdev->setup_id) |
pdev->setup_speed);
}

pdev->ep0_stage = CDNSP_DATA_STAGE;
}

Expand Down

0 comments on commit fcacd97

Please sign in to comment.