diff options
author | Marcin Bukat <marcin.bukat@gmail.com> | 2016-11-10 12:54:12 +0100 |
---|---|---|
committer | Marcin Bukat <marcin.bukat@gmail.com> | 2016-11-10 13:57:02 +0100 |
commit | ba9f405dc47c1ec24327537b77a61c886ad13f34 (patch) | |
tree | 680e5b9cc997a2d74d7f6a9ff8dc1171a2324a4e /utils/hwstub | |
parent | f2da975be636961df6375abe92d05a6b52da34b2 (diff) |
ATJ hwstub make irq based usb driver work
0e2b490 introduced rework of usb driver which was broken. It was reverted
in f2da975 to restore hwstub functionality on ATJ.
This commit reenables usb rework AND fixes remining issues.
The problem was with 0 length OUT thransfers. Additionally
a few cleanups were made.
Change-Id: I529ea9ad6540509e9287ca7e1cd2b44369b03cbb
Diffstat (limited to 'utils/hwstub')
-rw-r--r-- | utils/hwstub/stub/atj213x/usb_drv_atj213x.c | 307 |
1 files changed, 195 insertions, 112 deletions
diff --git a/utils/hwstub/stub/atj213x/usb_drv_atj213x.c b/utils/hwstub/stub/atj213x/usb_drv_atj213x.c index ef66766527..fa545a767c 100644 --- a/utils/hwstub/stub/atj213x/usb_drv_atj213x.c +++ b/utils/hwstub/stub/atj213x/usb_drv_atj213x.c @@ -31,6 +31,17 @@ volatile bool setup_data_valid = false; volatile int udc_speed = USB_FULL_SPEED; +struct endpoint_t +{ + void *buf; + int length; + bool zlp; + bool finished; +}; + +static volatile struct endpoint_t ep0in; +static volatile struct endpoint_t ep0out; + static void usb_copy_from(void *ptr, volatile void *reg, size_t sz) { uint32_t *p = ptr; @@ -67,93 +78,70 @@ static void usb_copy_to(volatile void *reg, void *ptr, size_t sz) *rp++ = *p++; } -void INT_UDC(void) +static void reset_all_fifos(void) { - /* get possible sources */ - unsigned int usbirq = OTG_USBIRQ; - unsigned int otgirq = OTG_OTGIRQ; -#if 0 - unsigned int usbeirq = OTG_USBEIRQ; - unsigned int epinirq = OTG_IN04IRQ; - unsigned int epoutirq = OTG_OUT04IRQ; -#endif - - /* HS, Reset, Setup */ - if (usbirq) - { - - if (usbirq & (1<<5)) - { - /* HS irq */ - udc_speed = USB_HIGH_SPEED; - } - else if (usbirq & (1<<4)) - { - /* Reset */ - udc_speed = USB_FULL_SPEED; - - /* clear all pending irqs */ - OTG_OUT04IRQ = 0xff; - OTG_IN04IRQ = 0xff; - } - else if (usbirq & (1<<0)) - { - /* Setup data valid */ - setup_data_valid = true; - } - - /* clear irq flags */ - OTG_USBIRQ = usbirq; - } + /* reset all ep fifos */ -#if 0 - if (epoutirq) - { - OTG_OUT04IRQ = epoutirq; - } - - if (epinirq) - { - OTG_IN04IRQ = epinirq; - } -#endif + /* IN fifos */ + OTG_ENDPRST = 0x10; + OTG_ENDPRST = 0x70; - if (otgirq) - { - OTG_OTGIRQ = otgirq; - } + /* OUT fifos */ + OTG_ENDPRST = 0x00; + OTG_ENDPRST = 0x60; +} - OTG_USBEIRQ = 0x50; +static void cancel_all_transfers(void) +{ + ep0out.buf = NULL; + ep0out.length = 0; + ep0out.zlp = false; + ep0out.finished = true; + + ep0in.buf = NULL; + ep0in.length = 0; + ep0in.zlp = false; + ep0in.finished = true; } void usb_drv_init(void) { - OTG_USBCS |= 0x40; /* soft disconnect */ + /* soft disconnect */ + OTG_USBCS |= 0x40; - OTG_ENDPRST = 0x10; /* reset all ep fifos */ - OTG_ENDPRST = 0x70; - OTG_ENDPRST = 0x00; - OTG_ENDPRST = 0x60; + cancel_all_transfers(); + reset_all_fifos(); - OTG_USBIRQ = 0xff; /* clear all pending interrupts */ + /* clear all pending interrupts */ + OTG_USBIRQ = 0xff; OTG_OTGIRQ = 0xff; OTG_IN04IRQ = 0xff; OTG_OUT04IRQ = 0xff; - OTG_USBEIRQ = 0x50; /* UDC ? with 0x40 there is irq storm */ - OTG_USBIEN = (1<<5) | (1<<4) | (1<<0); /* HS, Reset, Setup_data */ + /* bit6 - USB wakeup + * bit4 - connect/disconnect + * + * with 0x40 here there is irq storm + */ + OTG_USBEIRQ = 0x50; + + /* HS, Reset, Setup_data */ + OTG_USBIEN = (1<<5) | (1<<4) | (1<<0); + + /* No OTG interrupts ? */ OTG_OTGIEN = 0; - /* disable interrupts from ep0 */ - OTG_IN04IEN = 0; - OTG_OUT04IEN = 0; + /* enable interrupts from ep0 */ + OTG_IN04IEN = 1; + OTG_OUT04IEN = 1; /* unmask UDC interrupt in interrupt controller */ INTC_MSK = (1<<4); target_mdelay(100); - OTG_USBCS &= ~0x40; /* soft connect */ + /* soft connect */ + OTG_USBCS &= ~0x40; } int usb_drv_recv_setup(struct usb_ctrlrequest *req) @@ -163,6 +151,7 @@ int usb_drv_recv_setup(struct usb_ctrlrequest *req) usb_copy_from(req, &OTG_SETUPDAT, sizeof(struct usb_ctrlrequest)); setup_data_valid = false; + return 0; } @@ -183,71 +172,82 @@ void usb_drv_set_address(int address) /* UDC sets this automaticaly */ } -/* TODO: Maybe adapt to irq scheme */ +static void ep0_write(void) +{ + int xfer_size = MIN(ep0in.length, 64); + + /* copy data to UDC buffer */ + usb_copy_to(&OTG_EP0INDAT, ep0in.buf, xfer_size); + ep0in.buf += xfer_size; + ep0in.length -= xfer_size; + + /* this marks data as ready to send */ + OTG_IN0BC = xfer_size; +} + int usb_drv_send(int endpoint, void *ptr, int length) { (void)endpoint; - int xfer_size, cnt = length; - - while (cnt) + if (length) { - xfer_size = MIN(cnt, 64); + ep0in.length = length; + ep0in.buf = ptr; + ep0in.zlp = (length % 64 == 0) ? true : false; + ep0in.finished = false; - /* copy data to ep0in buffer */ - usb_copy_to(&OTG_EP0INDAT, ptr, xfer_size); + ep0_write(); - /* this marks data as ready to send */ - OTG_IN0BC = xfer_size; - - /* wait for the transfer end */ - while(OTG_EP0CS & 0x04) + while(!ep0in.finished) ; - - cnt -= xfer_size; - ptr += xfer_size; } - - /* ZLP stage */ - if((length % 64) == 0) + else + { + /* clear NAK bit to ACK host */ OTG_EP0CS = 2; + } return 0; } -/* TODO: Maybe adapt to irq scheme */ -int usb_drv_recv(int endpoint, void* ptr, int length) +static int ep0_read(void) { - (void)endpoint; - int xfer_size, cnt = 0; + int xfer_size = OTG_OUT0BC; + usb_copy_from(ep0out.buf, &OTG_EP0OUTDAT, xfer_size); + ep0out.buf += xfer_size; + ep0out.length -= xfer_size; - while (cnt < length) - { - /* Arm receiving buffer by writing - * any value to OUT0BC. This sets - * OUT_BUSY bit in EP0CS until the data - * are correctly received and ACK'd - */ - OTG_OUT0BC = 0; - - while (OTG_EP0CS & 0x08) - ; - - xfer_size = OTG_OUT0BC; - - usb_copy_from(ptr, &OTG_EP0OUTDAT, xfer_size); - cnt += xfer_size; - ptr += xfer_size; - - if (xfer_size < 64) - break; - } + return xfer_size; +} - /* ZLP stage */ - if (length == 0) - OTG_EP0CS = 2; +int usb_drv_recv(int endpoint, void* ptr, int length) +{ + (void)endpoint; - return cnt; + ep0out.length = length; + + if (length > 0) + { + ep0out.buf = ptr; + ep0out.finished = false; + + /* Arm receiving buffer by writing + * any value to OUT0BC. This sets + * OUT_BUSY bit in EP0CS until the data + * are correctly received and ACK'd + */ + OTG_OUT0BC = 0; + + while (!ep0out.finished) + ; + } + else + { + /* clear NAK bit to ACK host */ + OTG_EP0CS = 2; + } + + return (length - ep0out.length); } void usb_drv_stall(int endpoint, bool stall, bool in) @@ -265,3 +265,86 @@ void usb_drv_stall(int endpoint, bool stall, bool in) void usb_drv_exit(void) { } + +void INT_UDC(void) +{ + /* get possible sources */ + unsigned int usbirq = OTG_USBIRQ; + unsigned int otgirq = OTG_OTGIRQ; + unsigned int epinirq = OTG_IN04IRQ; + unsigned int epoutirq = OTG_OUT04IRQ; + + /* HS, Reset, Setup */ + if (usbirq) + { + + if (usbirq & (1<<5)) /* HS irq */ + { + udc_speed = USB_HIGH_SPEED; + } + else if (usbirq & (1<<4)) /* Reset irq */ + { + cancel_all_transfers(); + reset_all_fifos(); + + /* clear all pending EP irqs */ + OTG_OUT04IRQ = 0xff; + OTG_IN04IRQ = 0xff; + + udc_speed = USB_FULL_SPEED; + setup_data_valid = false; + } + else if (usbirq & (1<<0)) /* Setup data valid */ + { + setup_data_valid = true; + } + + /* clear irq flags */ + OTG_USBIRQ = usbirq; + } + + if (epoutirq) + { + if (ep0_read() == 64) + { + /* rearm receive buffer */ + OTG_OUT0BC = 0; + } + else + { + /* short packet means end of transfer */ + ep0out.finished = true; + } + + /* ack interrupt */ + OTG_OUT04IRQ = epoutirq; + } + + if (epinirq) + { + if (ep0in.length) + { + ep0_write(); + } + else + { + if (ep0in.zlp) + { + /* clear NAK bit to ACK hosts ZLP */ + OTG_EP0CS = 2; + } + + ep0in.finished = true; + } + + /* ack interrupt */ + OTG_IN04IRQ = epinirq; + } + + if (otgirq) + { + OTG_OTGIRQ = otgirq; + } + + OTG_USBEIRQ = 0x50; +} |