Thread (7 messages) 7 messages, 3 authors, 2021-08-28

Re: [PATCH v2] usb: chipidea: add loop timeout for hw_ep_set_halt()

From: Jeaho Hwang <hidden>
Date: 2021-08-24 08:32:02
Also in: lkml

2021년 8월 17일 (화) 오후 3:44, Jeaho Hwang [off-list ref]님이 작성:
If ctrl EP priming is failed (very rare case in standard linux),
hw_ep_set_halt goes infinite loop. waiting 100 times was enough
for zynq7000.
Hi Peter.
I found from zynq7000 TRM that the hardware clears Stall bit if a
setup packet is received on a control endpoint.
I think hw_ep_set_halt goes infinite loop since:

1. hw_ep_prime for control EP which is called from
isr_tr_complete_handler -> isr_setup_status_phase is failed due to a
setup packet received.
2. in isr_tr_complete_handler it tries to call _ep_set_halt if either
isr_tr_complete_low or isr_setup_status_phase returns error.
3. Since the control EP got a setup packet, HW resets TXS bit just as
the driver sets inside hw_ep_set_halt so it goes infinite loop.

Does it make sense? If it is right, we shouldn't call _ep_set_halt if
the err is -EAGAIN, which could be returned ONLY due to the setup
packet issue described above.
And the loop timeout is not required anymore.

Can I ask your opinion on this, Peter and USB experts?

Thanks.
quoted hunk ↗ jump to hunk
Signed-off-by: Jeaho Hwang <redacted>
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 8834ca613721..d73fadb18f32 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -209,6 +209,9 @@ static int hw_ep_prime(struct ci_hdrc *ci, int num, int dir, int is_ctrl)
        return 0;
 }

+/* enough for zynq7000 evaluation board */
+#define HW_EP_SET_HALT_COUNT_MAX 100
+
 /**
  * hw_ep_set_halt: configures ep halt & resets data toggle after clear (execute
  *                 without interruption)
@@ -221,6 +224,7 @@ static int hw_ep_prime(struct ci_hdrc *ci, int num, int dir, int is_ctrl)
  */
 static int hw_ep_set_halt(struct ci_hdrc *ci, int num, int dir, int value)
 {
+       int count = HW_EP_SET_HALT_COUNT_MAX;
        if (value != 0 && value != 1)
                return -EINVAL;
@@ -232,9 +236,9 @@ static int hw_ep_set_halt(struct ci_hdrc *ci, int num, int dir, int value)
                /* data toggle - reserved for EP0 but it's in ESS */
                hw_write(ci, reg, mask_xs|mask_xr,
                          value ? mask_xs : mask_xr);
-       } while (value != hw_ep_get_halt(ci, num, dir));
+       } while (value != hw_ep_get_halt(ci, num, dir) && --count > 0);

-       return 0;
+       return count ? 0 : -EAGAIN;
 }

 /**
--
2.25.1
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help