hw_ep_prime sometimes fails if irq occurs while it rus on RT kernel. to prevent local_irq_save should keep the function from irqs. I am not sure where is the best to submit this patch, between RT and USB community so sending to both. thanks. Signed-off-by: Jeaho Hwang <jhhwang@xxxxxxxxxx> --- drivers/usb/chipidea/udc.c | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 5f35cdd2cf1d..a90498f17cc4 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -102,6 +102,9 @@ static int hw_ep_flush(struct ci_hdrc *ci, int num, int dir) { int n = hw_ep_bit(num, dir); + /* From zynq-7000 TRM, It can take a long time + * so irq disable is not a good option for RT + */ do { /* flush any pending transfer */ hw_write(ci, OP_ENDPTFLUSH, ~0, BIT(n)); @@ -190,22 +193,32 @@ static int hw_ep_get_halt(struct ci_hdrc *ci, int num, int dir) static int hw_ep_prime(struct ci_hdrc *ci, int num, int dir, int is_ctrl) { int n = hw_ep_bit(num, dir); + unsigned long flags; + int ret = 0; /* Synchronize before ep prime */ wmb(); - if (is_ctrl && dir == RX && hw_read(ci, OP_ENDPTSETUPSTAT, BIT(num))) - return -EAGAIN; + /* irq affects this routine so irq should be disabled on RT. + * on standard kernel, irq is already disabled by callers. + */ + local_irq_save(flags); + if (is_ctrl && dir == RX && hw_read(ci, OP_ENDPTSETUPSTAT, BIT(num))) { + ret = -EAGAIN; + goto out; + } hw_write(ci, OP_ENDPTPRIME, ~0, BIT(n)); while (hw_read(ci, OP_ENDPTPRIME, BIT(n))) cpu_relax(); if (is_ctrl && dir == RX && hw_read(ci, OP_ENDPTSETUPSTAT, BIT(num))) - return -EAGAIN; + ret = -EAGAIN; +out: + local_irq_restore(flags); /* status shoult be tested according with manual but it doesn't work */ - return 0; + return ret; } /** -- 2.25.1