Commit ae423ef5 authored by Pawel Laszczak's avatar Pawel Laszczak Committed by Greg Kroah-Hartman

usb: cdnsp: fix lack of ZLP for ep0

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: 3d829045 ("usb: cdnsp: cdns3 Add main part of Cadence USBSSP DRD Driver")
cc: <stable@vger.kernel.org>
Reviewed-by: default avatarPeter Chen <peter.chen@kernel.org>
Signed-off-by: default avatarPawel Laszczak <pawell@cadence.com>
Link: https://lore.kernel.org/r/20221122085138.332434-1-pawell@cadence.comSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 57f8e00d
...@@ -2006,10 +2006,11 @@ int cdnsp_queue_bulk_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq) ...@@ -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) 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_ep *pep = preq->pep;
struct cdnsp_ring *ep_ring; struct cdnsp_ring *ep_ring;
int num_trbs; int num_trbs;
u32 maxp;
int ret; int ret;
ep_ring = cdnsp_request_to_transfer_ring(pdev, preq); ep_ring = cdnsp_request_to_transfer_ring(pdev, preq);
...@@ -2019,26 +2020,33 @@ int cdnsp_queue_ctrl_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq) ...@@ -2019,26 +2020,33 @@ int cdnsp_queue_ctrl_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq)
/* 1 TRB for data, 1 for status */ /* 1 TRB for data, 1 for status */
num_trbs = (pdev->three_stage_setup) ? 2 : 1; 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); ret = cdnsp_prepare_transfer(pdev, preq, num_trbs);
if (ret) if (ret)
return ret; return ret;
/* If there's data, queue data TRBs */ /* 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) { if (preq->request.length > 0) {
remainder = cdnsp_td_remainder(pdev, 0, preq->request.length, field = TRB_TYPE(TRB_DATA);
preq->request.length, preq, 1, 0);
length_field = TRB_LEN(preq->request.length) | if (zlp)
TRB_TD_SIZE(remainder) | TRB_INTR_TARGET(0); field |= TRB_CHAIN;
else
field |= TRB_IOC | (pdev->ep0_expect_in ? 0 : TRB_ISP);
if (pdev->ep0_expect_in) if (pdev->ep0_expect_in)
field |= TRB_DIR_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, cdnsp_queue_trb(pdev, ep_ring, true,
lower_32_bits(preq->request.dma), lower_32_bits(preq->request.dma),
upper_32_bits(preq->request.dma), length_field, upper_32_bits(preq->request.dma), length_field,
...@@ -2046,6 +2054,20 @@ int cdnsp_queue_ctrl_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq) ...@@ -2046,6 +2054,20 @@ int cdnsp_queue_ctrl_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq)
TRB_SETUPID(pdev->setup_id) | TRB_SETUPID(pdev->setup_id) |
pdev->setup_speed); 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; pdev->ep0_stage = CDNSP_DATA_STAGE;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment