Patch modifies the TD_SIZE in TRB before ZLP TRB. The TD_SIZE in TRB before ZLP TRB must be set to 1 to force processing ZLP TRB by controller.
cc: stable@vger.kernel.org Fixes: 3d82904559f4 ("usb: cdnsp: cdns3 Add main part of Cadence USBSSP DRD Driver") Signed-off-by: Pawel Laszczak pawell@cadence.com --- v2: - returned value for last TRB must be 0 v3: - fix issue for request->length > 64KB
drivers/usb/cdns3/cdnsp-ring.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/drivers/usb/cdns3/cdnsp-ring.c b/drivers/usb/cdns3/cdnsp-ring.c index 794e413800ae..86e1141e150f 100644 --- a/drivers/usb/cdns3/cdnsp-ring.c +++ b/drivers/usb/cdns3/cdnsp-ring.c @@ -1763,10 +1763,15 @@ static u32 cdnsp_td_remainder(struct cdnsp_device *pdev, int trb_buff_len, unsigned int td_total_len, struct cdnsp_request *preq, - bool more_trbs_coming) + bool more_trbs_coming, + bool zlp) { u32 maxp, total_packet_count;
+ /* Before ZLP driver needs set TD_SIZE = 1. */ + if (zlp) + return 1; + /* One TRB with a zero-length data packet. */ if (!more_trbs_coming || (transferred == 0 && trb_buff_len == 0) || trb_buff_len == td_total_len) @@ -1960,7 +1965,8 @@ int cdnsp_queue_bulk_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq) /* Set the TRB length, TD size, and interrupter fields. */ remainder = cdnsp_td_remainder(pdev, enqd_len, trb_buff_len, full_len, preq, - more_trbs_coming); + more_trbs_coming, + zero_len_trb);
length_field = TRB_LEN(trb_buff_len) | TRB_TD_SIZE(remainder) | TRB_INTR_TARGET(0); @@ -2025,7 +2031,7 @@ int cdnsp_queue_ctrl_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq)
if (preq->request.length > 0) { remainder = cdnsp_td_remainder(pdev, 0, preq->request.length, - preq->request.length, preq, 1); + preq->request.length, preq, 1, 0);
length_field = TRB_LEN(preq->request.length) | TRB_TD_SIZE(remainder) | TRB_INTR_TARGET(0); @@ -2225,7 +2231,7 @@ static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev, /* Set the TRB length, TD size, & interrupter fields. */ remainder = cdnsp_td_remainder(pdev, running_total, trb_buff_len, td_len, preq, - more_trbs_coming); + more_trbs_coming, 0);
length_field = TRB_LEN(trb_buff_len) | TRB_INTR_TARGET(0);
On Tue, Nov 15, 2022 at 5:22 PM Pawel Laszczak pawell@cadence.com wrote:
Patch modifies the TD_SIZE in TRB before ZLP TRB. The TD_SIZE in TRB before ZLP TRB must be set to 1 to force processing ZLP TRB by controller.
cc: stable@vger.kernel.org Fixes: 3d82904559f4 ("usb: cdnsp: cdns3 Add main part of Cadence USBSSP DRD Driver") Signed-off-by: Pawel Laszczak pawell@cadence.com
v2:
- returned value for last TRB must be 0
v3:
- fix issue for request->length > 64KB
drivers/usb/cdns3/cdnsp-ring.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/drivers/usb/cdns3/cdnsp-ring.c b/drivers/usb/cdns3/cdnsp-ring.c index 794e413800ae..86e1141e150f 100644 --- a/drivers/usb/cdns3/cdnsp-ring.c +++ b/drivers/usb/cdns3/cdnsp-ring.c @@ -1763,10 +1763,15 @@ static u32 cdnsp_td_remainder(struct cdnsp_device *pdev, int trb_buff_len, unsigned int td_total_len, struct cdnsp_request *preq,
bool more_trbs_coming)
bool more_trbs_coming,
bool zlp)
{ u32 maxp, total_packet_count;
/* Before ZLP driver needs set TD_SIZE = 1. */
if (zlp)
return 1;
Pawel, with your change, the TD_SIZE is 1 or 0, but not like the kernel doc defined like below:
/* * TD size is the number of max packet sized packets remaining in the TD * (*not* including this TRB). * * Total TD packet count = total_packet_count = * DIV_ROUND_UP(TD size in bytes / wMaxPacketSize) * * Packets transferred up to and including this TRB = packets_transferred = * rounddown(total bytes transferred including this TRB / wMaxPacketSize) * * TD size = total_packet_count - packets_transferred * * It must fit in bits 21:17, so it can't be bigger than 31. * This is taken care of in the TRB_TD_SIZE() macro * * The last TRB in a TD must have the TD size set to zero. */
Peter
/* One TRB with a zero-length data packet. */ if (!more_trbs_coming || (transferred == 0 && trb_buff_len == 0) || trb_buff_len == td_total_len)
@@ -1960,7 +1965,8 @@ int cdnsp_queue_bulk_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq) /* Set the TRB length, TD size, and interrupter fields. */ remainder = cdnsp_td_remainder(pdev, enqd_len, trb_buff_len, full_len, preq,
more_trbs_coming);
more_trbs_coming,
zero_len_trb); length_field = TRB_LEN(trb_buff_len) | TRB_TD_SIZE(remainder) | TRB_INTR_TARGET(0);
@@ -2025,7 +2031,7 @@ int cdnsp_queue_ctrl_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq)
if (preq->request.length > 0) { remainder = cdnsp_td_remainder(pdev, 0, preq->request.length,
preq->request.length, preq, 1);
preq->request.length, preq, 1, 0); length_field = TRB_LEN(preq->request.length) | TRB_TD_SIZE(remainder) | TRB_INTR_TARGET(0);
@@ -2225,7 +2231,7 @@ static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev, /* Set the TRB length, TD size, & interrupter fields. */ remainder = cdnsp_td_remainder(pdev, running_total, trb_buff_len, td_len, preq,
more_trbs_coming);
more_trbs_coming, 0); length_field = TRB_LEN(trb_buff_len) | TRB_INTR_TARGET(0);
-- 2.25.1
On Tue, Nov 15, 2022 at 5:22 PM Pawel Laszczak pawell@cadence.com wrote:
Patch modifies the TD_SIZE in TRB before ZLP TRB. The TD_SIZE in TRB before ZLP TRB must be set to 1 to force processing ZLP TRB by controller.
cc: stable@vger.kernel.org Fixes: 3d82904559f4 ("usb: cdnsp: cdns3 Add main part of Cadence USBSSP DRD Driver") Signed-off-by: Pawel Laszczak pawell@cadence.com
v2:
- returned value for last TRB must be 0
v3:
- fix issue for request->length > 64KB
drivers/usb/cdns3/cdnsp-ring.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/drivers/usb/cdns3/cdnsp-ring.c b/drivers/usb/cdns3/cdnsp-ring.c index 794e413800ae..86e1141e150f 100644 --- a/drivers/usb/cdns3/cdnsp-ring.c +++ b/drivers/usb/cdns3/cdnsp-ring.c @@ -1763,10 +1763,15 @@ static u32 cdnsp_td_remainder(struct
cdnsp_device *pdev,
int trb_buff_len, unsigned int td_total_len, struct cdnsp_request *preq,
bool more_trbs_coming)
bool more_trbs_coming,
bool zlp)
{ u32 maxp, total_packet_count;
/* Before ZLP driver needs set TD_SIZE = 1. */
if (zlp)
return 1;
Pawel, with your change, the TD_SIZE is 1 or 0, but not like the kernel doc defined like below:
Description is not added in kernel doc but is added before "if (zlp)".
I will add as last lines in kernel doc the description:
* * TD containing ZLP must have TD size set to one in penultimate TRB in TD. */
Will this patch be correct for you then?
Pawel
/*
- TD size is the number of max packet sized packets remaining in the TD
- (*not* including this TRB).
- Total TD packet count = total_packet_count =
DIV_ROUND_UP(TD size in bytes / wMaxPacketSize)
- Packets transferred up to and including this TRB = packets_transferred =
rounddown(total bytes transferred including this TRB / wMaxPacketSize)
- TD size = total_packet_count - packets_transferred
- It must fit in bits 21:17, so it can't be bigger than 31.
- This is taken care of in the TRB_TD_SIZE() macro
- The last TRB in a TD must have the TD size set to zero.
*/
Peter
/* One TRB with a zero-length data packet. */ if (!more_trbs_coming || (transferred == 0 && trb_buff_len == 0) || trb_buff_len == td_total_len) @@ -1960,7 +1965,8 @@ int
cdnsp_queue_bulk_tx(struct cdnsp_device *pdev, struct cdnsp_request
*preq)
/* Set the TRB length, TD size, and interrupter fields. */ remainder = cdnsp_td_remainder(pdev, enqd_len, trb_buff_len, full_len, preq,
more_trbs_coming);
more_trbs_coming,
zero_len_trb); length_field = TRB_LEN(trb_buff_len) | TRB_TD_SIZE(remainder) | TRB_INTR_TARGET(0); @@ -2025,7 +2031,7 @@ int
cdnsp_queue_ctrl_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq)
if (preq->request.length > 0) { remainder = cdnsp_td_remainder(pdev, 0, preq->request.length,
preq->request.length, preq, 1);
preq->request.length,
preq, 1, 0);
length_field = TRB_LEN(preq->request.length) | TRB_TD_SIZE(remainder) |
TRB_INTR_TARGET(0); @@ -2225,7 +2231,7 @@ static int
cdnsp_queue_isoc_tx(struct cdnsp_device *pdev,
/* Set the TRB length, TD size, & interrupter fields. */ remainder = cdnsp_td_remainder(pdev, running_total, trb_buff_len, td_len, preq,
more_trbs_coming);
more_trbs_coming, 0); length_field = TRB_LEN(trb_buff_len) |
TRB_INTR_TARGET(0);
-- 2.25.1
Pawel, with your change, the TD_SIZE is 1 or 0, but not like the kernel doc defined like below:
Please omit my comments, I did not check the code carefully.
/*
- TD size is the number of max packet sized packets remaining in the TD
- (*not* including this TRB).
With your current change, it may work. But your change conflicts with the xHCI spec that described above. With ZLP, the last useful trb's TD size should be 0, but if it is 0, the controller will be confused.
With your change, it makes the code more different with xhci's. Do you consider handling ZLP packet at another TD instead of current at the same TD?
Peter
- Total TD packet count = total_packet_count =
DIV_ROUND_UP(TD size in bytes / wMaxPacketSize)
- Packets transferred up to and including this TRB = packets_transferred =
rounddown(total bytes transferred including this TRB / wMaxPacketSize)
- TD size = total_packet_count - packets_transferred
- It must fit in bits 21:17, so it can't be bigger than 31.
- This is taken care of in the TRB_TD_SIZE() macro
- The last TRB in a TD must have the TD size set to zero.
*/
Peter
/* One TRB with a zero-length data packet. */ if (!more_trbs_coming || (transferred == 0 && trb_buff_len == 0) || trb_buff_len == td_total_len)
@@ -1960,7 +1965,8 @@ int cdnsp_queue_bulk_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq) /* Set the TRB length, TD size, and interrupter fields. */ remainder = cdnsp_td_remainder(pdev, enqd_len, trb_buff_len, full_len, preq,
more_trbs_coming);
more_trbs_coming,
zero_len_trb); length_field = TRB_LEN(trb_buff_len) | TRB_TD_SIZE(remainder) | TRB_INTR_TARGET(0);
@@ -2025,7 +2031,7 @@ int cdnsp_queue_ctrl_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq)
if (preq->request.length > 0) { remainder = cdnsp_td_remainder(pdev, 0, preq->request.length,
preq->request.length, preq, 1);
preq->request.length, preq, 1, 0); length_field = TRB_LEN(preq->request.length) | TRB_TD_SIZE(remainder) | TRB_INTR_TARGET(0);
@@ -2225,7 +2231,7 @@ static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev, /* Set the TRB length, TD size, & interrupter fields. */ remainder = cdnsp_td_remainder(pdev, running_total, trb_buff_len, td_len, preq,
more_trbs_coming);
more_trbs_coming, 0); length_field = TRB_LEN(trb_buff_len) | TRB_INTR_TARGET(0);
-- 2.25.1
Pawel, with your change, the TD_SIZE is 1 or 0, but not like the kernel doc defined like below:
Please omit my comments, I did not check the code carefully.
/*
- TD size is the number of max packet sized packets remaining in the
TD
- (*not* including this TRB).
With your current change, it may work. But your change conflicts with the xHCI spec that described above. With ZLP, the last useful trb's TD size should be 0, but if it is 0, the controller will be confused.
With your change, it makes the code more different with xhci's. Do you consider handling ZLP packet at another TD instead of current at the same TD ?
Yes, I considered it, but to simplify the driver I resigned from handling multiple TD per usb_request. Current solution is simpler and supported by device controller.
Additionally, the next ZLP fix must be added for control endpoint for Data Stage and probably it will not work with 2 TDs.
Pawel
- Total TD packet count = total_packet_count =
DIV_ROUND_UP(TD size in bytes / wMaxPacketSize)
- Packets transferred up to and including this TRB = packets_transferred =
rounddown(total bytes transferred including this TRB /
wMaxPacketSize)
- TD size = total_packet_count - packets_transferred
- It must fit in bits 21:17, so it can't be bigger than 31.
- This is taken care of in the TRB_TD_SIZE() macro
- The last TRB in a TD must have the TD size set to zero.
*/
Peter
/* One TRB with a zero-length data packet. */ if (!more_trbs_coming || (transferred == 0 && trb_buff_len == 0) || trb_buff_len == td_total_len) @@ -1960,7 +1965,8 @@ int
cdnsp_queue_bulk_tx(struct cdnsp_device *pdev, struct cdnsp_request
*preq)
/* Set the TRB length, TD size, and interrupter fields. */ remainder = cdnsp_td_remainder(pdev, enqd_len, trb_buff_len, full_len, preq,
more_trbs_coming);
more_trbs_coming,
zero_len_trb); length_field = TRB_LEN(trb_buff_len) | TRB_TD_SIZE(remainder)
|
TRB_INTR_TARGET(0); @@ -2025,7 +2031,7 @@
int cdnsp_queue_ctrl_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq)
if (preq->request.length > 0) { remainder = cdnsp_td_remainder(pdev, 0, preq->request.length,
preq->request.length, preq, 1);
preq->request.length,
preq, 1, 0);
length_field = TRB_LEN(preq->request.length) | TRB_TD_SIZE(remainder) |
TRB_INTR_TARGET(0); @@ -2225,7 +2231,7 @@ static int
cdnsp_queue_isoc_tx(struct cdnsp_device *pdev,
/* Set the TRB length, TD size, & interrupter fields. */ remainder = cdnsp_td_remainder(pdev, running_total, trb_buff_len, td_len, preq,
more_trbs_coming);
more_trbs_coming, 0); length_field = TRB_LEN(trb_buff_len) |
TRB_INTR_TARGET(0);
-- 2.25.1
On Tue, Nov 15, 2022 at 5:22 PM Pawel Laszczak pawell@cadence.com wrote:
Patch modifies the TD_SIZE in TRB before ZLP TRB. The TD_SIZE in TRB before ZLP TRB must be set to 1 to force processing ZLP TRB by controller.
cc: stable@vger.kernel.org Fixes: 3d82904559f4 ("usb: cdnsp: cdns3 Add main part of Cadence USBSSP DRD Driver") Signed-off-by: Pawel Laszczak pawell@cadence.com
Reviewed-by: Peter Chen peter.chen@kernel.org
Peter
v2:
- returned value for last TRB must be 0
v3:
- fix issue for request->length > 64KB
drivers/usb/cdns3/cdnsp-ring.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-)
diff --git a/drivers/usb/cdns3/cdnsp-ring.c b/drivers/usb/cdns3/cdnsp-ring.c index 794e413800ae..86e1141e150f 100644 --- a/drivers/usb/cdns3/cdnsp-ring.c +++ b/drivers/usb/cdns3/cdnsp-ring.c @@ -1763,10 +1763,15 @@ static u32 cdnsp_td_remainder(struct cdnsp_device *pdev, int trb_buff_len, unsigned int td_total_len, struct cdnsp_request *preq,
bool more_trbs_coming)
bool more_trbs_coming,
bool zlp)
{ u32 maxp, total_packet_count;
/* Before ZLP driver needs set TD_SIZE = 1. */
if (zlp)
return 1;
/* One TRB with a zero-length data packet. */ if (!more_trbs_coming || (transferred == 0 && trb_buff_len == 0) || trb_buff_len == td_total_len)
@@ -1960,7 +1965,8 @@ int cdnsp_queue_bulk_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq) /* Set the TRB length, TD size, and interrupter fields. */ remainder = cdnsp_td_remainder(pdev, enqd_len, trb_buff_len, full_len, preq,
more_trbs_coming);
more_trbs_coming,
zero_len_trb); length_field = TRB_LEN(trb_buff_len) | TRB_TD_SIZE(remainder) | TRB_INTR_TARGET(0);
@@ -2025,7 +2031,7 @@ int cdnsp_queue_ctrl_tx(struct cdnsp_device *pdev, struct cdnsp_request *preq)
if (preq->request.length > 0) { remainder = cdnsp_td_remainder(pdev, 0, preq->request.length,
preq->request.length, preq, 1);
preq->request.length, preq, 1, 0); length_field = TRB_LEN(preq->request.length) | TRB_TD_SIZE(remainder) | TRB_INTR_TARGET(0);
@@ -2225,7 +2231,7 @@ static int cdnsp_queue_isoc_tx(struct cdnsp_device *pdev, /* Set the TRB length, TD size, & interrupter fields. */ remainder = cdnsp_td_remainder(pdev, running_total, trb_buff_len, td_len, preq,
more_trbs_coming);
more_trbs_coming, 0); length_field = TRB_LEN(trb_buff_len) | TRB_INTR_TARGET(0);
-- 2.25.1
linux-stable-mirror@lists.linaro.org