From d4942cc74c82c465ea395637c77ed06565b8b497 Mon Sep 17 00:00:00 2001 From: Solomon Peachy Date: Fri, 29 Jun 2018 16:09:28 -0400 Subject: Add Xuelin iHIFI 770/770C/800 support Taken from the xvortex fork (Roman Stolyarov) Ported, rebased, and cleaned up by myself. Change-Id: I7b2bca2d29502f2e4544e42f3d122786dd4b7978 --- firmware/target/arm/rk27xx/usb-drv-rk27xx.c | 787 +++++++++++----------------- 1 file changed, 293 insertions(+), 494 deletions(-) (limited to 'firmware/target/arm/rk27xx/usb-drv-rk27xx.c') diff --git a/firmware/target/arm/rk27xx/usb-drv-rk27xx.c b/firmware/target/arm/rk27xx/usb-drv-rk27xx.c index badc3ab5ed..057ecf6ebc 100644 --- a/firmware/target/arm/rk27xx/usb-drv-rk27xx.c +++ b/firmware/target/arm/rk27xx/usb-drv-rk27xx.c @@ -28,37 +28,16 @@ #include "kernel.h" #include "panic.h" -//#include "usb-s3c6400x.h" - #include "usb_ch9.h" #include "usb_core.h" #include #include "power.h" +#define LOGF_ENABLE #include "logf.h" typedef volatile uint32_t reg32; -/* Bulk OUT: ep1, ep4, ep7, ep10, ep13 */ -#define BOUT_RXSTAT(ep_num) (*(reg32*)(AHB0_UDC+0x54+0x38*(ep_num/3))) -#define BOUT_RXCON(ep_num) (*(reg32*)(AHB0_UDC+0x58+0x38*(ep_num/3))) -#define BOUT_DMAOUTCTL(ep_num) (*(reg32*)(AHB0_UDC+0x5C+0x38*(ep_num/3))) -#define BOUT_DMAOUTLMADDR(ep_num) (*(reg32*)(AHB0_UDC+0x60+0x38*(ep_num/3))) - -/* Bulk IN: ep2, ep5, ep8, ep11, ep4 */ -#define BIN_TXSTAT(ep_num) (*(reg32*)(AHB0_UDC+0x64+0x38*(ep_num/3))) -#define BIN_TXCON(ep_num) (*(reg32*)(AHB0_UDC+0x68+0x38*(ep_num/3))) -#define BIN_TXBUF(ep_num) (*(reg32*)(AHB0_UDC+0x6C+0x38*(ep_num/3))) -#define BIN_DMAINCTL(ep_num) (*(reg32*)(AHB0_UDC+0x70+0x38*(ep_num/3))) -#define BIN_DMAINLMADDR(ep_num) (*(reg32*)(AHB0_UDC+0x74+0x38*(ep_num/3))) - -/* INTERRUPT IN: ep3, ep6, ep9, ep12, ep15 */ -#define IIN_TXSTAT(ep_num) (*(reg32*)(AHB0_UDC+0x78+0x38*((ep_num/3)-1))) -#define IIN_TXCON(ep_num) (*(reg32*)(AHB0_UDC+0x7C+0x38*((ep_num/3)-1))) -#define IIN_TXBUF(ep_num) (*(reg32*)(AHB0_UDC+0x80+0x38*((ep_num/3)-1))) -#define IIN_DMAINCTL(ep_num) (*(reg32*)(AHB0_UDC+0x84+0x38*((ep_num/3)-1))) -#define IIN_DMAINLMADDR(ep_num) (*(reg32*)(AHB0_UDC+0x88+0x38*((ep_num/3)-1))) - #ifdef LOGF_ENABLE #define XFER_DIR_STR(dir) ((dir) ? "IN" : "OUT") #define XFER_TYPE_STR(type) \ @@ -68,9 +47,12 @@ typedef volatile uint32_t reg32; ((type) == USB_ENDPOINT_XFER_INT ? "INTR" : "INVL")))) #endif -struct endpoint_t { +struct endpoint_t +{ + const int ep_num; /* EP number */ const int type; /* EP type */ const int dir; /* DIR_IN/DIR_OUT */ + volatile unsigned long *stat; /* RXSTAT/TXSTAT register */ bool allocated; /* flag to mark EPs taken */ volatile void *buf; /* tx/rx buffer address */ volatile int len; /* size of the transfer (bytes) */ @@ -79,110 +61,91 @@ struct endpoint_t { struct semaphore complete; /* semaphore for blocking transfers */ }; -#define EP_INIT(_type, _dir, _alloced, _buf, _len, _cnt, _block) \ - { .type = (_type), .dir = (_dir), .allocated = (_alloced), .buf = (_buf), \ - .len = (_len), .cnt = (_cnt), .block = (_block) } - -static struct endpoint_t ctrlep[2] = { - EP_INIT(USB_ENDPOINT_XFER_CONTROL, DIR_OUT, true, NULL, 0, 0, true), - EP_INIT(USB_ENDPOINT_XFER_CONTROL, DIR_IN, true, NULL, 0, 0, true), +/* compute RXCON address from RXSTAT, and so on */ +#define RXSTAT(endp) *((endp)->stat) +#define RXCON(endp) *(1 + (endp)->stat) +#define DMAOUTCTL(endp) *(2 + (endp)->stat) +#define DMAOUTLMADDR(endp) *(3 + (endp)->stat) +/* compute TXCON address from TXSTAT, and so on */ +#define TXSTAT(endp) *((endp)->stat) +#define TXCON(endp) *(1 + (endp)->stat) +#define TXBUF(endp) *(2 + (endp)->stat) +#define DMAINCTL(endp) *(3 + (endp)->stat) +#define DMAINLMADDR(endp) *(4 + (endp)->stat) + +#define ENDPOINT(num, type, dir, reg) \ + {num, USB_ENDPOINT_XFER_##type, USB_DIR_##dir, reg, false, NULL, 0, 0, true, {{0, 0}, 0, 0}} + +static struct endpoint_t ctrlep[2] = +{ + ENDPOINT(0, CONTROL, OUT, &RX0STAT), + ENDPOINT(0, CONTROL, IN, &TX0STAT), }; -static struct endpoint_t endpoints[16] = { - EP_INIT(USB_ENDPOINT_XFER_CONTROL, 3, true, NULL, 0, 0, true ), /* stub */ - EP_INIT(USB_ENDPOINT_XFER_BULK, DIR_OUT, false, NULL, 0, 0, false ), /* BOUT1 */ - EP_INIT(USB_ENDPOINT_XFER_BULK, DIR_IN, false, NULL, 0, 0, false ), /* BIN2 */ - EP_INIT(USB_ENDPOINT_XFER_INT, DIR_IN, false, NULL, 0, 0, false ), /* IIN3 */ - EP_INIT(USB_ENDPOINT_XFER_BULK, DIR_OUT, false, NULL, 0, 0, false ), /* BOUT4 */ - EP_INIT(USB_ENDPOINT_XFER_BULK, DIR_IN, false, NULL, 0, 0, false ), /* BIN5 */ - EP_INIT(USB_ENDPOINT_XFER_INT, DIR_IN, false, NULL, 0, 0, false ), /* IIN6 */ - EP_INIT(USB_ENDPOINT_XFER_BULK, DIR_OUT, false, NULL, 0, 0, false ), /* BOUT7 */ - EP_INIT(USB_ENDPOINT_XFER_BULK, DIR_IN, false, NULL, 0, 0, false ), /* BIN8 */ - EP_INIT(USB_ENDPOINT_XFER_INT, DIR_IN, false, NULL, 0, 0, false ), /* IIN9 */ - EP_INIT(USB_ENDPOINT_XFER_BULK, DIR_OUT, false, NULL, 0, 0, false ), /* BOUT10 */ - EP_INIT(USB_ENDPOINT_XFER_BULK, DIR_IN, false, NULL, 0, 0, false ), /* BIN11 */ - EP_INIT(USB_ENDPOINT_XFER_INT, DIR_IN, false, NULL, 0, 0, false ), /* IIN12 */ - EP_INIT(USB_ENDPOINT_XFER_BULK, DIR_OUT, false, NULL, 0, 0, false ), /* BOUT13 */ - EP_INIT(USB_ENDPOINT_XFER_BULK, DIR_IN, false, NULL, 0, 0, false ), /* BIN14 */ - EP_INIT(USB_ENDPOINT_XFER_INT, DIR_IN, false, NULL, 0, 0, false ), /* IIN15 */ +static struct endpoint_t endpoints[16] = +{ + ENDPOINT(0, CONTROL, OUT, NULL), /* stub */ + ENDPOINT(1, BULK, OUT, &RX1STAT), /* BOUT1 */ + ENDPOINT(2, BULK, IN, &TX2STAT), /* BIN2 */ + ENDPOINT(3, INT, IN, &TX3STAT), /* IIN3 */ + ENDPOINT(4, BULK, OUT, &RX4STAT), /* BOUT4 */ + ENDPOINT(5, BULK, IN, &TX5STAT), /* BIN5 */ + ENDPOINT(6, INT, IN, &TX6STAT), /* IIN6 */ + ENDPOINT(7, BULK, OUT, &RX7STAT), /* BOUT7 */ + ENDPOINT(8, BULK, IN, &TX8STAT), /* BIN8 */ + ENDPOINT(9, INT, IN, &TX9STAT), /* IIN9 */ + ENDPOINT(10, BULK, OUT, &RX10STAT), /* BOUT10 */ + ENDPOINT(11, BULK, IN, &TX11STAT), /* BIN11 */ + ENDPOINT(12, INT, IN, &TX12STAT), /* IIN12 */ + ENDPOINT(13, BULK, OUT, &RX13STAT), /* BOUT13 */ + ENDPOINT(14, BULK, IN, &TX14STAT), /* BIN14 */ + ENDPOINT(15, INT, IN, &TX15STAT), /* IIN15 */ }; +static volatile bool set_address = false; +static volatile bool set_configuration = false; + +#undef ENDPOINT + static void setup_received(void) { static uint32_t setup_data[2]; - + logf("udc: setup"); + /* copy setup data from packet */ setup_data[0] = SETUP1; setup_data[1] = SETUP2; - /* clear all pending control transfers - * do we need this here? - */ - /* pass setup data to the upper layer */ usb_core_control_request((struct usb_ctrlrequest*)setup_data); } -/* service ep0 IN transaction */ -static void ctr_write(void) +static int max_pkt_size(struct endpoint_t *endp) { - int xfer_size = (ctrlep[DIR_IN].cnt > 64) ? 64 : ctrlep[DIR_IN].cnt; - unsigned int timeout = current_tick + HZ/10; - - while (TX0BUF & TXFULL) /* TX0FULL flag */ + switch(endp->type) { - if(TIME_AFTER(current_tick, timeout)) - break; + case USB_ENDPOINT_XFER_CONTROL: return 64; + case USB_ENDPOINT_XFER_BULK: return usb_drv_port_speed() ? 512 : 64; + case USB_ENDPOINT_XFER_INT: return usb_drv_port_speed() ? 1024 : 64; + default: panicf("die"); return 0; } - - TX0STAT = xfer_size; /* size of the transfer */ - TX0DMALM_IADDR = (uint32_t)ctrlep[DIR_IN].buf; /* local buffer address */ - TX0DMAINCTL = DMA_START; /* start DMA */ - TX0CON &= ~TXNAK; /* clear NAK */ - - /* Decrement by max packet size is intentional. - * This way if we have final packet short one we will get negative len - * after transfer, which in turn indicates we *don't* need to send - * zero length packet. If the final packet is max sized packet we will - * get zero len after transfer which indicates we need to send - * zero length packet to signal host end of the transfer. - */ - ctrlep[DIR_IN].cnt -= 64; - ctrlep[DIR_IN].buf += xfer_size; -} - -static void ctr_read(void) -{ - int xfer_size = RX0STAT & 0xffff; - - /* clear NAK bit */ - RX0CON &= ~RXNAK; - - ctrlep[DIR_OUT].cnt -= xfer_size; - ctrlep[DIR_OUT].buf += xfer_size; - - RX0DMAOUTLMADDR = (uint32_t)ctrlep[DIR_OUT].buf; /* buffer address */ - RX0DMACTLO = DMA_START; /* start DMA */ } -static void blk_write(int ep) +static void ep_write(struct endpoint_t *endp) { - int ep_num = EP_NUM(ep); - int max = usb_drv_port_speed() ? 512 : 64; - int xfer_size = (endpoints[ep_num].cnt > max) ? max : endpoints[ep_num].cnt; + int xfer_size = MIN(max_pkt_size(endp), endp->cnt); unsigned int timeout = current_tick + HZ/10; - - while (BIN_TXBUF(ep_num) & TXFULL) /* TXFULL flag */ + + while(TXBUF(endp) & TXFULL) /* TXFULL flag */ { if(TIME_AFTER(current_tick, timeout)) break; } - - BIN_TXSTAT(ep_num) = xfer_size; /* size */ - BIN_DMAINLMADDR(ep_num) = (uint32_t)endpoints[ep_num].buf; /* buf address */ - BIN_DMAINCTL(ep_num) = DMA_START; /* start DMA */ - BIN_TXCON(ep_num) &= ~TXNAK; /* clear NAK */ - + + /* setup transfer size and DMA */ + TXSTAT(endp) = xfer_size; + DMAINLMADDR(endp) = (uint32_t)endp->buf; /* local buffer address */ + DMAINCTL(endp) = DMA_START; /* Decrement by max packet size is intentional. * This way if we have final packet short one we will get negative len * after transfer, which in turn indicates we *don't* need to send @@ -190,247 +153,141 @@ static void blk_write(int ep) * get zero len after transfer which indicates we need to send * zero length packet to signal host end of the transfer. */ - endpoints[ep_num].cnt -= max; - endpoints[ep_num].buf += xfer_size; + endp->cnt -= max_pkt_size(endp); + endp->buf += xfer_size; + /* clear NAK */ + TXCON(endp) &= ~TXNAK; } -static void blk_read(int ep) +static void ep_read(struct endpoint_t *endp) { - int ep_num = EP_NUM(ep); - int xfer_size = BOUT_RXSTAT(ep_num) & 0xffff; - - /* clear NAK bit */ - BOUT_RXCON(ep_num) &= ~RXNAK; - - endpoints[ep_num].cnt -= xfer_size; - endpoints[ep_num].buf += xfer_size; - - BOUT_DMAOUTLMADDR(ep_num) = (uint32_t)endpoints[ep_num].buf; - BOUT_DMAOUTCTL(ep_num) = DMA_START; -} - -static void int_write(int ep) -{ - int ep_num = EP_NUM(ep); - int max = usb_drv_port_speed() ? 1024 : 64; - int xfer_size = (endpoints[ep_num].cnt > max) ? max : endpoints[ep_num].cnt; - unsigned int timeout = current_tick + HZ/10; - - while (IIN_TXBUF(ep_num) & TXFULL) /* TXFULL flag */ - { - if(TIME_AFTER(current_tick, timeout)) - break; - } - - IIN_TXSTAT(ep_num) = xfer_size; /* size */ - IIN_DMAINLMADDR(ep_num) = (uint32_t)endpoints[ep_num].buf; /* buf address */ - IIN_DMAINCTL(ep_num) = DMA_START; /* start DMA */ - IIN_TXCON(ep_num) &= ~TXNAK; /* clear NAK */ - - /* Decrement by max packet size is intentional. - * This way if we have final packet short one we will get negative len - * after transfer, which in turn indicates we *don't* need to send - * zero length packet. If the final packet is max sized packet we will - * get zero len after transfer which indicates we need to send - * zero length packet to signal host end of the transfer. - */ - endpoints[ep_num].cnt -= max; - endpoints[ep_num].buf += xfer_size; + /* setup DMA */ + DMAOUTLMADDR(endp) = (uint32_t)endp->buf; /* local buffer address */ + DMAOUTCTL(endp) = DMA_START; + /* clear NAK */ + RXCON(endp) &= ~RXNAK; } -/* UDC ISR function */ -void INT_UDC(void) +static void in_intr(struct endpoint_t *endp) { - uint32_t txstat, rxstat; - int tmp, ep_num; - - /* read what caused UDC irq */ - uint32_t intsrc = INT2FLAG & 0x7fffff; - - if (intsrc & SETUP_INTR) /* setup interrupt */ + uint32_t txstat = TXSTAT(endp); + /* check if clear feature was sent by host */ + if(txstat & TXCFINT) { - setup_received(); + logf("clear_stall: %d", endp->ep_num); + usb_drv_stall(endp->ep_num, false, true); } - else if (intsrc & IN0_INTR) /* ep0 in interrupt */ + /* check if a transfer has finished */ + if(txstat & TXACK) { - txstat = TX0STAT; /* read clears flags */ - - /* TODO handle errors */ - if (txstat & TXACK) /* check TxACK flag */ + logf("udc: ack(%d)", endp->ep_num); + /* finished ? */ + if(endp->cnt <= 0) { - if (ctrlep[DIR_IN].cnt >= 0) - { - /* we still have data to send (or ZLP) */ - ctr_write(); - } - else - { - /* final ack received */ - usb_core_transfer_complete(0, /* ep */ - USB_DIR_IN, /* dir */ - 0, /* status */ - ctrlep[DIR_IN].len); /* length */ - - /* release semaphore for blocking transfer */ - if (ctrlep[DIR_IN].block) - semaphore_release(&ctrlep[DIR_IN].complete); - } + usb_core_transfer_complete(endp->ep_num, endp->dir, 0, endp->len); + /* release semaphore for blocking transfer */ + if(endp->block) + semaphore_release(&endp->complete); } + else /* more data to send */ + ep_write(endp); } - else if (intsrc & OUT0_INTR) /* ep0 out interrupt */ - { - rxstat = RX0STAT; +} - /* TODO handle errors */ - if (rxstat & RXACK) /* RxACK */ - { - if (ctrlep[DIR_OUT].cnt > 0) - ctr_read(); - else - usb_core_transfer_complete(0, /* ep */ - USB_DIR_OUT, /* dir */ - 0, /* status */ - ctrlep[DIR_OUT].len); /* length */ - } - } - else if (intsrc & USBRST_INTR) /* usb reset */ - { - usb_drv_init(); - } - else if (intsrc & RESUME_INTR) /* usb resume */ - { - TX0CON |= TXCLR; /* TxClr */ - TX0CON &= ~TXCLR; - RX0CON |= RXCLR; /* RxClr */ - RX0CON &= ~RXCLR; - } - else if (intsrc & SUSP_INTR) /* usb suspend */ +static void out_intr(struct endpoint_t *endp) +{ + uint32_t rxstat = RXSTAT(endp); + logf("udc: out intr(%d)", endp->ep_num); + /* check if clear feature was sent by host */ + if(rxstat & RXCFINT) { + logf("clear_stall: %d", endp->ep_num); + usb_drv_stall(endp->ep_num, false, false); } - else if (intsrc & CONN_INTR) /* usb connect */ + /* check if a transfer has finished */ + if(rxstat & RXACK) { + int xfer_size = rxstat & 0xffff; + endp->cnt -= xfer_size; + endp->buf += xfer_size; + logf("udc: ack(%d) -> %d/%d", endp->ep_num, xfer_size, endp->cnt); + /* finished ? */ + if(endp->cnt <= 0 || xfer_size < max_pkt_size(endp)) + usb_core_transfer_complete(endp->ep_num, endp->dir, 0, endp->len); + else + ep_read(endp); } - else - { - /* lets figure out which ep generated irq */ - tmp = intsrc >> 7; - for (ep_num=1; ep_num < 15; ep_num++) - { - tmp >>= ep_num; - if (tmp & 0x01) - break; - } - - if (intsrc & ((1<<8)|(1<<11)|(1<<14)|(1<<17)|(1<<20))) - { - /* bulk out */ - rxstat = BOUT_RXSTAT(ep_num); - - /* TODO handle errors */ - if (rxstat & (1<<18)) /* RxACK */ - { - if (endpoints[ep_num].cnt > 0) - blk_read(ep_num); - else - usb_core_transfer_complete(ep_num, /* ep */ - USB_DIR_OUT, /* dir */ - 0, /* status */ - endpoints[ep_num].len); /* length */ - } - } - else if (intsrc & ((1<<9)|(1<<12)|(1<<15)|(1<<18)|(1<<21))) +} + +static void udc_phy_reset(void) +{ + DEV_CTL |= SOFT_POR; + udelay(10000); /* min 10ms */ + DEV_CTL &= ~SOFT_POR; +} + +static void udc_soft_connect(void) +{ + DEV_CTL |= CSR_DONE | + DEV_SOFT_CN | + DEV_SELF_PWR; +} + +static void udc_helper(void) +{ + uint32_t dev_info = DEV_INFO; + + /* This polls for DEV_EN bit set in DEV_INFO register + * as well as tracks current requested configuration + * (DEV_INFO [11:8]). On state change it notifies usb stack + * about it. + */ + + /* SET ADDRESS request */ + if(!set_address) + if(dev_info & 0x7f) { - /* bulk in */ - txstat = BIN_TXSTAT(ep_num); - - /* TODO handle errors */ - if (txstat & (1<<18)) /* check TxACK flag */ - { - if (endpoints[ep_num].cnt >= 0) - { - /* we still have data to send (or ZLP) */ - blk_write(ep_num); - } - else - { - /* final ack received */ - usb_core_transfer_complete(ep_num, /* ep */ - USB_DIR_IN, /* dir */ - 0, /* status */ - endpoints[ep_num].len); /* length */ - - /* release semaphore for blocking transfer */ - if (endpoints[ep_num].block) - semaphore_release(&endpoints[ep_num].complete); - } - } + set_address = true; + usb_core_notify_set_address(dev_info & 0x7f); } - else if (intsrc & ((1<<10)|(1<13)|(1<<16)|(1<<19)|(1<<22))) + + /* SET CONFIGURATION request */ + if(!set_configuration) + if(dev_info & DEV_EN) { - /* int in */ - txstat = IIN_TXSTAT(ep_num); - - /* TODO handle errors */ - if (txstat & TXACK) /* check TxACK flag */ - { - if (endpoints[ep_num].cnt >= 0) - { - /* we still have data to send (or ZLP) */ - int_write(ep_num); - } - else - { - /* final ack received */ - usb_core_transfer_complete(ep_num, /* ep */ - USB_DIR_IN, /* dir */ - 0, /* status */ - endpoints[ep_num].len); /* length */ - - /* release semaphore for blocking transfer */ - if (endpoints[ep_num].block) - semaphore_release(&endpoints[ep_num].complete); - } - } + set_configuration = true; + usb_core_notify_set_config(((dev_info >> 7) & 0xf) + 1); } - } } /* return port speed FS=0, HS=1 */ int usb_drv_port_speed(void) { - return ((DEV_INFO & DEV_SPEED) == 0) ? 0 : 1; + return (DEV_INFO & DEV_SPEED) ? 0 : 1; } /* Reserve endpoint */ int usb_drv_request_endpoint(int type, int dir) { - int ep_num, ep_dir; - int ep_type; - - /* Safety */ - ep_dir = EP_DIR(dir); - ep_type = type & USB_ENDPOINT_XFERTYPE_MASK; + logf("req: %s %s", XFER_DIR_STR(dir), XFER_TYPE_STR(type)); - logf("req: %s %s", XFER_DIR_STR(ep_dir), XFER_TYPE_STR(ep_type)); - /* Find an available ep/dir pair */ - for (ep_num=1;ep_numtype == ep_type && - endpoint->dir == ep_dir && - !endpoint->allocated) - { - /* mark endpoint as taken */ - endpoint->allocated = true; - - /* enable interrupt from this endpoint */ - EN_INT |= (1<<(ep_num+7)); - - logf("add: ep%d %s", ep_num, XFER_DIR_STR(ep_dir)); - return (ep_num | (dir & USB_ENDPOINT_DIR_MASK)); - } + struct endpoint_t *endp = &endpoints[ep_num]; + + if(endp->allocated || endp->type != type || endp->dir != dir) + continue; + /* allocate endpoint and enable interrupt */ + endp->allocated = true; + if(dir == USB_DIR_IN) + TXCON(endp) = (ep_num << 8) | TXEPEN | TXNAK | TXACKINTEN | TXCFINTE; + else + RXCON(endp) = (ep_num << 8) | RXEPEN | RXNAK | RXACKINTEN | RXCFINTE | RXERRINTEN; + EN_INT |= 1 << (ep_num + 7); + + logf("add: ep%d %s", ep_num, XFER_DIR_STR(dir)); + return ep_num | dir; } return -1; } @@ -439,14 +296,12 @@ int usb_drv_request_endpoint(int type, int dir) void usb_drv_release_endpoint(int ep) { int ep_num = EP_NUM(ep); - int ep_dir = EP_DIR(ep); - (void) ep_dir; - logf("rel: ep%d %s", ep_num, XFER_DIR_STR(ep_dir)); + logf("rel: ep%d", ep_num); endpoints[ep_num].allocated = false; - + /* disable interrupt from this endpoint */ - EN_INT &= ~(1<<(ep_num+7)); + EN_INT &= ~(1 << (ep_num + 7)); } /* Set the address (usually it's in a register). @@ -463,39 +318,25 @@ void usb_drv_set_address(int address) static int _usb_drv_send(int endpoint, void *ptr, int length, bool block) { + logf("udc: send(%x)", endpoint); struct endpoint_t *ep; int ep_num = EP_NUM(endpoint); - + if (ep_num == 0) ep = &ctrlep[DIR_IN]; else ep = &endpoints[ep_num]; + /* for send transfers, make sure the data is committed */ + commit_discard_dcache_range(ptr, length); ep->buf = ptr; ep->len = ep->cnt = length; - - if (block) - ep->block = true; - else - ep->block = false; - - switch (ep->type) - { - case USB_ENDPOINT_XFER_CONTROL: - ctr_write(); - break; - - case USB_ENDPOINT_XFER_BULK: - blk_write(ep_num); - break; - - case USB_ENDPOINT_XFER_INT: - int_write(ep_num); - break; - } - - if (block) - /* wait for transfer to end */ + ep->block = block; + + ep_write(ep); + + /* wait for transfer to end */ + if(block) semaphore_wait(&ep->complete, TIMEOUT_BLOCK); return 0; @@ -516,28 +357,20 @@ int usb_drv_send_nonblocking(int endpoint, void *ptr, int length) /* Setup a receive transfer. (non blocking) */ int usb_drv_recv(int endpoint, void* ptr, int length) { + logf("udc: recv(%x)", endpoint); struct endpoint_t *ep; int ep_num = EP_NUM(endpoint); - - if (ep_num == 0) - { + + if(ep_num == 0) ep = &ctrlep[DIR_OUT]; - - ctr_read(); - } else - { ep = &endpoints[ep_num]; - - /* clear NAK bit */ - BOUT_RXCON(ep_num) &= ~RXNAK; - BOUT_DMAOUTLMADDR(ep_num) = (uint32_t)ptr; - BOUT_DMAOUTCTL(ep_num) = DMA_START; - } - + + /* for recv, discard the cache lines related to the buffer */ + commit_discard_dcache_range(ptr, length); ep->buf = ptr; ep->len = ep->cnt = length; - + ep_read(ep); return 0; } @@ -560,173 +393,54 @@ void usb_drv_set_test_mode(int mode) /* Check if endpoint is in stall state */ bool usb_drv_stalled(int endpoint, bool in) { - int ep_num = EP_NUM(endpoint); - - switch (endpoints[ep_num].type) - { - case USB_ENDPOINT_XFER_CONTROL: - if (in) - return (TX0CON & TXSTALL) ? true : false; - else - return (RX0CON & RXSTALL) ? true : false; - - break; + struct endpoint_t *endp = &endpoints[EP_NUM(endpoint)]; - case USB_ENDPOINT_XFER_BULK: - if (in) - return (BIN_TXCON(ep_num) & TXSTALL) ? true : false; - else - return (BOUT_RXCON(ep_num) & RXSTALL) ? true : false; - - break; - - case USB_ENDPOINT_XFER_INT: - if (in) - return (IIN_TXCON(ep_num) & TXSTALL) ? true : false; - else - return false; /* we don't have such endpoint anyway */ - - break; - } - - return false; + if(in) + return !!(TXCON(endp) & TXSTALL); + else + return !!(RXCON(endp) & RXSTALL); } /* Stall the endpoint. Usually set a flag in the controller */ void usb_drv_stall(int endpoint, bool stall, bool in) { - int ep_num = EP_NUM(endpoint); - - switch (endpoints[ep_num].type) + struct endpoint_t *endp = &endpoints[EP_NUM(endpoint)]; + if(in) { - case USB_ENDPOINT_XFER_CONTROL: - if (in) - { - if (stall) - TX0CON |= TXSTALL; - else - TX0CON &= ~TXSTALL; - } - else - { - if (stall) - RX0CON |= RXSTALL; - else - RX0CON &= ~RXSTALL; /* doc says Auto clear by UDC 2.0 */ - } - break; - - case USB_ENDPOINT_XFER_BULK: - if (in) - { - if (stall) - BIN_TXCON(ep_num) |= TXSTALL; - else - BIN_TXCON(ep_num) &= ~TXSTALL; - } - else - { - if (stall) - BOUT_RXCON(ep_num) |= RXSTALL; - else - BOUT_RXCON(ep_num) &= ~RXSTALL; - } - break; - - case USB_ENDPOINT_XFER_INT: - if (in) - { - if (stall) - IIN_TXCON(ep_num) |= TXSTALL; - else - IIN_TXCON(ep_num) &= ~TXSTALL; - } - break; + if(stall) + TXCON(endp) |= TXSTALL; + else + TXCON(endp) &= ~TXSTALL; + } + else + { + if(stall) + RXCON(endp) |= RXSTALL; + else + RXCON(endp) &= ~RXSTALL; } } /* one time init (once per connection) - basicaly enable usb core */ void usb_drv_init(void) { - int ep_num; - - /* enable USB clock */ - SCU_CLKCFG &= ~CLKCFG_UDC; - - /* 1. do soft disconnect */ - DEV_CTL = DEV_SELF_PWR; - - /* 2. do power on reset to PHY */ - DEV_CTL = DEV_SELF_PWR | - SOFT_POR; - - /* 3. wait more than 10ms */ - udelay(20000); - - /* 4. clear SOFT_POR bit */ - DEV_CTL &= ~SOFT_POR; - - /* 5. configure minimal EN_INT */ - EN_INT = EN_SUSP_INTR | /* Enable Suspend Interrupt */ - EN_RESUME_INTR | /* Enable Resume Interrupt */ - EN_USBRST_INTR | /* Enable USB Reset Interrupt */ - EN_OUT0_INTR | /* Enable OUT Token receive Interrupt EP0 */ - EN_IN0_INTR | /* Enable IN Token transmits Interrupt EP0 */ - EN_SETUP_INTR; /* Enable SETUP Packet Receive Interrupt */ - - /* 6. configure INTCON */ - INTCON = UDC_INTHIGH_ACT | /* interrupt high active */ - UDC_INTEN; /* enable EP0 interrupts */ - - /* 7. configure EP0 control registers */ - TX0CON = TXACKINTEN | /* Set as one to enable the EP0 tx irq */ - TXNAK; /* Set as one to response NAK handshake */ - - RX0CON = RXACKINTEN | - RXEPEN | /* Endpoint 0 Enable. When cleared the endpoint does - * not respond to an SETUP or OUT token - */ - - RXNAK; /* Set as one to response NAK handshake */ - - /* 8. write final bits to DEV_CTL */ - DEV_CTL = CSR_DONE | /* Configure CSR done */ - DEV_PHY16BIT | /* 16-bit data path enabled. udc_clk = 30MHz */ - DEV_SOFT_CN | /* Device soft connect */ - DEV_SELF_PWR; /* Device self power */ - /* init semaphore of ep0 */ semaphore_init(&ctrlep[DIR_OUT].complete, 1, 0); semaphore_init(&ctrlep[DIR_IN].complete, 1, 0); - - for (ep_num = 1; ep_num < USB_NUM_ENDPOINTS; ep_num++) - { + + for(int ep_num = 1; ep_num < USB_NUM_ENDPOINTS; ep_num++) semaphore_init(&endpoints[ep_num].complete, 1, 0); - - if (ep_num%3 == 0) /* IIN 3, 6, 9, 12, 15 */ - { - IIN_TXCON(ep_num) |= (ep_num<<8)|TXEPEN|TXNAK; /* ep_num, enable, NAK */ - } - else if (ep_num%3 == 1) /* BOUT 1, 4, 7, 10, 13 */ - { - BOUT_RXCON(ep_num) |= (ep_num<<8)|RXEPEN|RXNAK; /* ep_num, NAK, enable */ - } - else if (ep_num%3 == 2) /* BIN 2, 5, 8, 11, 14 */ - { - BIN_TXCON(ep_num) |= (ep_num<<8)|TXEPEN|TXNAK; /* ep_num, enable, NAK */ - } - } } /* turn off usb core */ void usb_drv_exit(void) { DEV_CTL = DEV_SELF_PWR; - + /* disable USB interrupts in interrupt controller */ INTC_IMR &= ~IRQ_ARM_UDC; INTC_IECR &= ~IRQ_ARM_UDC; - + /* we cannot disable UDC clock since this causes data abort * when reading DEV_INFO in order to check usb connect event */ @@ -734,9 +448,94 @@ void usb_drv_exit(void) int usb_detect(void) { - if (DEV_INFO & VBUS_STS) + if(DEV_INFO & VBUS_STS) return USB_INSERTED; else return USB_EXTRACTED; } +/* UDC ISR function */ +void INT_UDC(void) +{ + /* read what caused UDC irq */ + uint32_t intsrc = INT2FLAG & 0x7fffff; + + if(intsrc & USBRST_INTR) /* usb reset */ + { + logf("udc_int: reset, %ld", current_tick); + + EN_INT = EN_SUSP_INTR | /* Enable Suspend Irq */ + EN_RESUME_INTR | /* Enable Resume Irq */ + EN_USBRST_INTR | /* Enable USB Reset Irq */ + EN_OUT0_INTR | /* Enable OUT Token receive Irq EP0 */ + EN_IN0_INTR | /* Enable IN Token transmit Irq EP0 */ + EN_SETUP_INTR; /* Enable SETUP Packet Receive Irq */ + + INTCON = UDC_INTHIGH_ACT | /* interrupt high active */ + UDC_INTEN; /* enable EP0 irqs */ + + TX0CON = TXACKINTEN | /* Set as one to enable the EP0 tx irq */ + TXNAK; /* Set as one to response NAK handshake */ + + RX0CON = RXACKINTEN | + RXEPEN | /* Endpoint 0 Enable. When cleared the + * endpoint does not respond to an SETUP + * or OUT token */ + RXNAK; /* Set as one to response NAK handshake */ + + set_address = false; + set_configuration = false; + } + /* This needs to be processed AFTER usb reset */ + udc_helper(); + + if(intsrc & SETUP_INTR) /* setup interrupt */ + { + setup_received(); + } + if(intsrc & IN0_INTR) + { + /* EP0 IN done */ + in_intr(&ctrlep[DIR_IN]); + } + if(intsrc & OUT0_INTR) + { + /* EP0 OUT done */ + out_intr(&ctrlep[DIR_OUT]); + } + if(intsrc & USBRST_INTR) + { + /* usb reset */ + usb_drv_init(); + } + if(intsrc & RESUME_INTR) + { + /* usb resume */ + TX0CON |= TXCLR; /* TxClr */ + TX0CON &= ~TXCLR; + RX0CON |= RXCLR; /* RxClr */ + RX0CON &= ~RXCLR; + } + if(intsrc & SUSP_INTR) + { + /* usb suspend */ + } + if(intsrc & CONN_INTR) + { + /* usb connect */ + udc_phy_reset(); + udelay(10000); /* wait at least 10ms */ + udc_soft_connect(); + } + /* other endpoints */ + for(int ep_num = 1; ep_num < 16; ep_num++) + { + if(!(intsrc & (1 << (ep_num + 7)))) + continue; + struct endpoint_t *endp = &endpoints[ep_num]; + if(endp->dir == USB_DIR_IN) + in_intr(endp); + else + out_intr(endp); + } +} -- cgit v1.2.3