diff options
author | Amaury Pouly <pamaury@rockbox.org> | 2010-06-23 22:03:31 +0000 |
---|---|---|
committer | Amaury Pouly <pamaury@rockbox.org> | 2010-06-23 22:03:31 +0000 |
commit | 237d9666ccd5d35db920df640e493ceec8962aaa (patch) | |
tree | fda1100af5181f9d71927070c24a5a3383c04680 /firmware/target/arm/as3525/usb-drv-as3525v2.c | |
parent | 58ad1e7c4b7e404d0bcda752914e2878c68feafd (diff) |
as3525v2-usb: define number of enpoints correctly, write interrupt handler
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@27098 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target/arm/as3525/usb-drv-as3525v2.c')
-rw-r--r-- | firmware/target/arm/as3525/usb-drv-as3525v2.c | 262 |
1 files changed, 147 insertions, 115 deletions
diff --git a/firmware/target/arm/as3525/usb-drv-as3525v2.c b/firmware/target/arm/as3525/usb-drv-as3525v2.c index 5e3a325a02..cea87159a6 100644 --- a/firmware/target/arm/as3525/usb-drv-as3525v2.c +++ b/firmware/target/arm/as3525/usb-drv-as3525v2.c @@ -38,32 +38,38 @@ static int __in_ep_list[NUM_IN_EP] = {IN_EP_LIST}; static int __out_ep_list[NUM_OUT_EP] = {OUT_EP_LIST}; +static int __in_ep_list_ep0[NUM_IN_EP + 1] = {0, IN_EP_LIST}; +static int __out_ep_list_ep0[NUM_OUT_EP + 1] = {0, OUT_EP_LIST}; /* iterate through each in/out ep except EP0 * 'counter' is the counter, 'ep' is the actual value */ #define FOR_EACH_IN_EP(counter, ep) \ for(counter = 0, ep = __in_ep_list[0]; counter < NUM_IN_EP; counter++, ep = __in_ep_list[counter]) +#define FOR_EACH_IN_EP_AND_EP0(counter, ep) \ + for(counter = 0, ep = __in_ep_list_ep0[0]; counter <= NUM_IN_EP; counter++, ep = __in_ep_list_ep0[counter]) + #define FOR_EACH_OUT_EP(counter, ep) \ for(counter = 0, ep = __out_ep_list[0]; counter < NUM_OUT_EP; counter++, ep = __out_ep_list[counter]) +#define FOR_EACH_OUT_EP_AND_EP0(counter, ep) \ + for(counter = 0, ep = __out_ep_list_ep0[0]; counter <= NUM_OUT_EP; counter++, ep = __out_ep_list_ep0[counter]) + struct usb_endpoint { - void *buf; unsigned int len; - union - { - unsigned int sent; - unsigned int received; - }; bool wait; bool busy; + bool done; + int status; + struct wakeup complete; }; -#if 0 -static struct usb_endpoint endpoints[USB_NUM_ENDPOINTS*2]; -#endif -static struct usb_ctrlrequest ep0_setup_pkt __attribute__((aligned(16))); +/* NOTE: the following structure is not used to EP0 + * and make the assumption that each endpoint is + * either IN or OUT but not bidirectional */ +static struct usb_endpoint endpoints[USB_NUM_ENDPOINTS]; +static struct usb_ctrlrequest ep0_setup_pkt USB_DEVBSS_ATTR; void usb_attach(void) { @@ -175,10 +181,9 @@ static void reset_endpoints(void) /* Setup EP0 OUT with the following parameters: * packet count = 1 * setup packet count = 1 - * transfer size = 64 + * transfer size = 8 (setup packet) * Setup EP0 IN/OUT with 64 byte maximum packet size and activate both. Enable transfer on EP0 OUT */ - DOEPTSIZ(0) = (1 << DEPTSIZ0_supcnt_bitp) | (1 << DEPTSIZ0_pkcnt_bitp) | 8; @@ -305,10 +310,8 @@ static void core_dev_init(void) /* Setup interrupt masks for endpoints */ /* Setup interrupt masks */ - DOEPMSK = DOEPINT_setup | DOEPINT_xfercompl | DOEPINT_ahberr - | DOEPINT_epdisabled; - DIEPMSK = DIEPINT_xfercompl | DIEPINT_timeout - | DIEPINT_epdisabled | DIEPINT_ahberr; + DOEPMSK = DOEPINT_setup | DOEPINT_xfercompl | DOEPINT_ahberr; + DIEPMSK = DIEPINT_xfercompl | DIEPINT_timeout | DIEPINT_ahberr; DAINTMSK = 0xffffffff; reset_endpoints(); @@ -381,80 +384,95 @@ void usb_drv_exit(void) disable_global_interrupts(); } -static void dump_regs(void) +static void handle_ep_int(int ep, bool dir_in) { - logf("DSTS: %lx", DSTS); - logf("DOEPCTL0=%lx", DOEPCTL(0)); - logf("DOEPTSIZ=%lx", DOEPTSIZ(0)); - logf("DIEPCTL0=%lx", DIEPCTL(0)); - logf("DOEPMSK=%lx", DOEPMSK); - logf("DIEPMSK=%lx", DIEPMSK); - logf("DAINTMSK=%lx", DAINTMSK); - logf("DAINT=%lx", DAINT); - logf("GINTSTS=%lx", GINTSTS); - logf("GINTMSK=%lx", GINTMSK); - logf("DCTL=%lx", DCTL); - logf("GAHBCFG=%lx", GAHBCFG); - logf("GUSBCFG=%lx", GUSBCFG); - logf("DCFG=%lx", DCFG); - logf("DTHRCTL=%lx", DTHRCTL); -} - -static bool handle_reset(void) -{ - logf("usb: bus reset"); - - dump_regs(); - /* Clear the Remote Wakeup Signalling */ - DCTL &= ~DCTL_rmtwkupsig; - - /* Flush FIFOs */ - flush_tx_fifos(0x10); - - reset_endpoints(); - - /* Reset Device Address */ - DCFG &= bitm(DCFG, devadr); - - usb_core_bus_reset(); - - return true; -} - -static bool handle_enum_done(void) -{ - logf("usb: enum done"); - - /* read speed */ - switch(extract(DSTS, enumspd)) + if(dir_in) { - case DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ: - logf("usb: HS"); - break; - case DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ: - case DSTS_ENUMSPD_FS_PHY_48MHZ: - logf("usb: FS"); - break; - case DSTS_ENUMSPD_LS_PHY_6MHZ: - panicf("usb: LS is not supported"); + if(DIEPINT(ep) & DIEPINT_ahberr) + panicf("usb: ahb error on EP%d IN", ep); + if(DIEPINT(ep) & DIEPINT_xfercompl) + { + logf("usb: xfer complete on EP%d IN", ep); + if(endpoints[ep].busy) + { + endpoints[ep].busy = false; + endpoints[ep].status = 0; + endpoints[ep].done = true; + /* works even for PE0 */ + int transfered = endpoints[ep].len - (DIEPTSIZ(ep) & DEPTSIZ_xfersize_bits); + clean_dcache_range((void *)DIEPDMA(ep), transfered); + usb_core_transfer_complete(ep, USB_DIR_IN, 0, transfered); + } + } + if(DIEPINT(ep) & DIEPINT_timeout) + { + logf("usb: timeout on EP%d IN", ep); + if(endpoints[ep].busy) + { + endpoints[ep].busy = false; + endpoints[ep].status = 1; + endpoints[ep].done = true; + /* for safety, act as if no bytes as been transfered */ + endpoints[ep].len = 0; + usb_core_transfer_complete(ep, USB_DIR_IN, 1, 0); + wakeup_signal(&endpoints[ep].complete); + } + } + /* clear interrupts */ + DIEPINT(ep) = DIEPINT(ep); + } + else + { + if(DOEPINT(ep) & DOEPINT_ahberr) + panicf("usb: ahb error on EP%d OUT", ep); + if(DOEPINT(ep) & DOEPINT_xfercompl) + { + logf("usb: xfer complete on EP%d OUT", ep); + if(endpoints[ep].busy) + { + endpoints[ep].busy = false; + endpoints[ep].status = 0; + endpoints[ep].done = true; + /* works even for PE0 */ + int transfered = endpoints[ep].len - (DOEPTSIZ(ep) & DEPTSIZ_xfersize_bits); + clean_dcache_range((void *)DOEPDMA(ep), transfered); + usb_core_transfer_complete(ep, USB_DIR_OUT, 0, transfered); + } + } + if(DOEPINT(ep) & DOEPINT_setup) + { + logf("usb: setup on EP%d OUT", ep); + if(ep != 0) + panicf("usb: setup not on EP0, this is impossible"); + clean_dcache_range((void*)&ep0_setup_pkt, sizeof ep0_setup_pkt); /* force write back */ + usb_core_control_request(&ep0_setup_pkt); + } + /* setup EP0 for the next transfer */ + DOEPTSIZ(0) = (1 << DEPTSIZ0_supcnt_bitp) | (1 << DEPTSIZ0_pkcnt_bitp) | 8; + DOEPDMA(0) = (unsigned long)&ep0_setup_pkt; /* virtual address=physical address */ + DOEPCTL(0) |= DEPCTL_epena | DEPCTL_cnak; + + /* clear interrupts */ + DOEPINT(ep) = DOEPINT(ep); } - - /* fixme: change EP0 mps here */ - dump_regs(); - - return true; -} - -static bool handle_in_ep_int(void) -{ - panicf("usb: in ep int"); - return false; } -static bool handle_out_ep_int(void) +static void handle_ep_ints(void) { - panicf("usb: out ep int"); - return false; + logf("usb: ep int"); + /* we must read it */ + unsigned long daint = DAINT; + unsigned i, ep; + + FOR_EACH_IN_EP_AND_EP0(i, ep) + if(daint & DAINT_IN_EP(ep)) + handle_ep_int(ep, true); + FOR_EACH_OUT_EP_AND_EP0(i, ep) + if(daint & DAINT_OUT_EP(ep)) + handle_ep_int(ep, false); + + /* write back to clear status */ + DAINT = daint; } /* interrupt service routine */ @@ -463,45 +481,59 @@ void INT_USB(void) /* some bits in GINTSTS can be set even though we didn't enable the interrupt source * so AND it with the actual mask */ unsigned long sts = GINTSTS & GINTMSK; - unsigned long handled_one = 0; /* mask of all listed one (either handled or not) */ - - #define HANDLED_CASE(bitmask, callfn) \ - handled_one |= bitmask; \ - if(sts & bitmask) \ - { \ - if(!callfn()) \ - goto Lerr; \ - } - - #define UNHANDLED_CASE(bitmask) \ - handled_one |= bitmask; \ - if(sts & bitmask) \ - goto Lunhandled; /* device part */ - HANDLED_CASE(GINTMSK_usbreset, handle_reset) - HANDLED_CASE(GINTMSK_enumdone, handle_enum_done) - HANDLED_CASE(GINTMSK_inepintr, handle_in_ep_int) - HANDLED_CASE(GINTMSK_outepintr, handle_out_ep_int) + if(sts & GINTMSK_usbreset) + { + logf("usb: bus reset"); - /* common part */ - UNHANDLED_CASE(GINTMSK_otgintr) - UNHANDLED_CASE(GINTMSK_conidstschng) - UNHANDLED_CASE(GINTMSK_disconnect) + /* Clear the Remote Wakeup Signalling */ + DCTL &= ~DCTL_rmtwkupsig; - /* unlisted ones */ - if(sts & ~handled_one) - goto Lunhandled; + /* Flush FIFOs */ + flush_tx_fifos(0x10); - GINTSTS = GINTSTS; + reset_endpoints(); + + /* Reset Device Address */ + DCFG &= bitm(DCFG, devadr); + + usb_core_bus_reset(); + } + + if(sts & GINTMSK_enumdone) + { + logf("usb: enum done"); + + /* read speed */ + switch(extract(DSTS, enumspd)) + { + case DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ: + logf("usb: HS"); + break; + case DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ: + case DSTS_ENUMSPD_FS_PHY_48MHZ: + logf("usb: FS"); + break; + case DSTS_ENUMSPD_LS_PHY_6MHZ: + panicf("usb: LS is not supported"); + } + + /* fixme: change EP0 mps here */ + } - return; + if(sts & (GINTMSK_outepintr | GINTMSK_inepintr)) + { + handle_ep_ints(); + } - Lunhandled: - panicf("unhandled usb int: %lx", sts); + /* common part */ + if(sts & GINTMSK_otgintr) + { + panicf("usb: otg int"); + } - Lerr: - panicf("error in usb int: %lx", sts); + GINTSTS = GINTSTS; } int usb_drv_port_speed(void) |