diff options
author | Rafaël Carré <rafael.carre@gmail.com> | 2011-12-31 21:22:48 +0000 |
---|---|---|
committer | Rafaël Carré <rafael.carre@gmail.com> | 2011-12-31 21:22:48 +0000 |
commit | 7b22ea0d377f69ec7a0d7be7215ef3c560ce13e7 (patch) | |
tree | 72666d2b75270d5809bbd31f436fab3a9c730e36 | |
parent | fd5cb720c4b922979e854ee3d07979b81f60e776 (diff) |
usb-drv-as3525v2.c: merge in usb-s3c6400x.c
as3525v2 specific part is 400 LoC, ipod specific less than 300
(comments and whitespace included)
TODO: merge properly
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@31509 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r-- | firmware/target/arm/as3525/usb-drv-as3525v2.c | 441 | ||||
-rw-r--r-- | firmware/target/arm/usb-s3c6400x.c | 408 |
2 files changed, 407 insertions, 442 deletions
diff --git a/firmware/target/arm/as3525/usb-drv-as3525v2.c b/firmware/target/arm/as3525/usb-drv-as3525v2.c deleted file mode 100644 index 8c19758b65..0000000000 --- a/firmware/target/arm/as3525/usb-drv-as3525v2.c +++ /dev/null @@ -1,441 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright © 2010 Amaury Pouly - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ****************************************************************************/ - -#include "usb.h" -#include "usb_drv.h" -#include "as3525v2.h" -#include "clock-target.h" -#include "ascodec.h" -#include "as3514.h" -#include "stdbool.h" -#include "string.h" -#include "stdio.h" -#include "panic.h" -#include "mmu-arm.h" -#include "system.h" -//#define LOGF_ENABLE -#include "logf.h" -#include "usb_core.h" - -static const uint8_t in_ep_list[] = {0, 1, 3, 5}; -static const uint8_t out_ep_list[] = {0, 2, 4}; - -/* state of EP0 (to correctly schedule setup packet enqueing) */ -enum ep0state -{ - /* Setup packet is enqueud, waiting for actual data */ - EP0_WAIT_SETUP = 0, - /* Waiting for ack (either IN or OUT) */ - EP0_WAIT_ACK = 1, - /* Ack complete, waiting for data (either IN or OUT) - * This state is necessary because if both ack and data complete in the - * same interrupt, we might process data completion before ack completion - * so we need this bizarre state */ - EP0_WAIT_DATA = 2, - /* Setup packet complete, waiting for ack and data */ - EP0_WAIT_DATA_ACK = 3, -}; - -/* endpoints[ep_num][DIR_IN/DIR_OUT] */ -static struct ep_type endpoints[USB_NUM_ENDPOINTS][2]; -/* setup packet for EP0 */ - -/* USB control requests may be up to 64 bytes in size. - Even though we never use anything more than the 8 header bytes, - we are required to accept request packets of up to 64 bytes size. - Provide buffer space for these additional payload bytes so that - e.g. write descriptor requests (which are rejected by us, but the - payload is transferred anyway) do not cause memory corruption. - Fixes FS#12310. -- Michael Sparmann (theseven) */ -static union { - struct usb_ctrlrequest header; /* 8 bytes */ - unsigned char payload[64]; -} _ep0_setup_pkt USB_DEVBSS_ATTR; - -static struct usb_ctrlrequest *ep0_setup_pkt = AS3525_UNCACHED_ADDR(&_ep0_setup_pkt.header); - -/* state of EP0 */ -static enum ep0state ep0_state; - -void usb_attach(void) -{ - /* Nothing to do */ -} - -static void prepare_setup_ep0(void) -{ - DEPDMA(0, true) = (void*)AS3525_PHYSICAL_ADDR(&_ep0_setup_pkt); - DEPTSIZ(0, true) = (1 << DEPTSIZ0_supcnt_bitp) - | (1 << DEPTSIZ0_pkcnt_bitp) - | 8; - DEPCTL(0, true) |= DEPCTL_epena | DEPCTL_cnak; - - ep0_state = EP0_WAIT_SETUP; -} - -static size_t num_eps(bool out) -{ - return out ? sizeof(out_ep_list) : sizeof(in_ep_list); -} - -static void reset_endpoints(void) -{ - for (int dir = 0; dir < 2; dir++) - { - bool out = dir == DIR_OUT; - for (unsigned i = 0; i < num_eps(dir == DIR_OUT); i++) - { - int ep = ((dir == DIR_IN) ? in_ep_list : out_ep_list)[i]; - struct ep_type *endpoint = &endpoints[ep][out]; - endpoint->active = false; - endpoint->busy = false; - endpoint->status = -1; - endpoint->done = false; - semaphore_release(&endpoint->complete); - - if (i != 0) - DEPCTL(ep, out) = DEPCTL_setd0pid; - } - DEPCTL(0, out) = /*(DEPCTL_MPS_64 << DEPCTL_mps_bitp) | */ DEPCTL_usbactep; - } - - /* Setup next chain for IN eps */ - for (unsigned i = 0; i < num_eps(false); i++) - { - int ep = in_ep_list[i]; - int next_ep = in_ep_list[(i + 1) % num_eps(false)]; - DEPCTL(ep, false) |= next_ep << DEPCTL_nextep_bitp; - } - - prepare_setup_ep0(); -} - -static void cancel_all_transfers(bool cancel_ep0) -{ - int flags = disable_irq_save(); - - for (int dir = 0; dir < 2; dir++) - for (unsigned i = !!cancel_ep0; i < num_eps(dir == DIR_OUT); i++) - { - int ep = ((dir == DIR_IN) ? in_ep_list : out_ep_list)[i]; - struct ep_type *endpoint = &endpoints[ep][dir == DIR_OUT]; - endpoint->status = -1; - endpoint->busy = false; - endpoint->done = false; - semaphore_release(&endpoint->complete); - DEPCTL(ep, dir) = (DEPCTL(ep, dir) & ~DEPCTL_usbactep); - } - - restore_irq(flags); -} - -void usb_drv_init(void) -{ - for (int i = 0; i < USB_NUM_ENDPOINTS; i++) - for (int dir = 0; dir < 2; dir++) - semaphore_init(&endpoints[i][dir].complete, 1, 0); - - bitset32(&CGU_PERI, CGU_USB_CLOCK_ENABLE); - CCU_USB = (CCU_USB & ~(3<<24)) | (1 << 24); /* ?? */ - /* PHY clock */ - CGU_USB = 1<<5 /* enable */ - | 0 << 2 - | 0; /* source = ? (24MHz crystal?) */ - - PCGCCTL = 0; - DCTL = DCTL_pwronprgdone | DCTL_sftdiscon; - - GRSTCTL = GRSTCTL_csftrst; - while (GRSTCTL & GRSTCTL_csftrst); /* Wait for OTG to ack reset */ - while (!(GRSTCTL & GRSTCTL_ahbidle)); /* Wait for OTG AHB master idle */ - - GRXFSIZ = 512; - GNPTXFSIZ = MAKE_FIFOSIZE_DATA(512); - - /* FIXME: the current code is for internal DMA only, the clip+ architecture - * defines the internal DMA model */ - GAHBCFG = (GAHBCFG_INT_DMA_BURST_INCR << GAHBCFG_hburstlen_bitp) - | GAHBCFG_dma_enable | GAHBCFG_glblintrmsk; - - /* Select UTMI+ 16 */ - GUSBCFG = GUSBCFG_force_device_mode | GUSBCFG_phy_if | 7 << GUSBCFG_toutcal_bitp; - - /* Do something that is probably CCU related but undocumented*/ - CCU_USB |= 0x1000; - CCU_USB &= ~0x300000; - - DCFG = DCFG_nzstsouthshk | DCFG_devspd_hs_phy_hs; /* Address 0, high speed */ - DCTL = DCTL_pwronprgdone; - - /* Check hardware capabilities */ - if(extract(GHWCFG2, arch) != GHWCFG2_ARCH_INTERNAL_DMA) - panicf("usb-drv: wrong architecture (%ld)", extract(GHWCFG2, arch)); - if(extract(GHWCFG2, hs_phy_type) != GHWCFG2_PHY_TYPE_UTMI) - panicf("usb-drv: wrong HS phy type (%ld)", extract(GHWCFG2, hs_phy_type)); - if(extract(GHWCFG2, fs_phy_type) != GHWCFG2_PHY_TYPE_UNSUPPORTED) - panicf("usb-drv: wrong FS phy type (%ld)", extract(GHWCFG2, fs_phy_type)); - if(extract(GHWCFG4, utmi_phy_data_width) != 0x2) - panicf("usb-drv: wrong utmi data width (%ld)", extract(GHWCFG4, utmi_phy_data_width)); - if(!(GHWCFG4 & GHWCFG4_ded_fifo_en)) /* it seems to be multiple tx fifo support */ - panicf("usb-drv: no multiple tx fifo"); - - if(USB_NUM_ENDPOINTS != extract(GHWCFG2, num_ep)) - panicf("usb-drv: wrong endpoint number"); - - for (int dir = 0; dir < 2; dir++) - for (unsigned i = 0; i < num_eps(dir == DIR_OUT); i++) - { - int ep = ((dir == DIR_IN) ? in_ep_list : out_ep_list)[i]; - int type = (GHWCFG1 >> GHWCFG1_epdir_bitp(ep)) & GHWCFG1_epdir_bits; - int flag = (dir == DIR_IN) ? GHWCFG1_EPDIR_IN : GHWCFG1_EPDIR_OUT; - if(type != GHWCFG1_EPDIR_BIDIR && type != flag) - panicf("usb-drv: EP%d not in correct direction", ep); - } - - DOEPMSK = DEPINT_xfercompl | DEPINT_ahberr | DOEPINT_setup; - DIEPMSK = DEPINT_xfercompl | DEPINT_ahberr | DIEPINT_timeout; - DAINTMSK = 0xffffffff; - - reset_endpoints(); - - GINTMSK = GINTMSK_usbreset - | GINTMSK_enumdone - | GINTMSK_inepintr - | GINTMSK_outepintr - | GINTMSK_disconnect; - - VIC_INT_ENABLE = INTERRUPT_USB; -} - -void usb_drv_exit(void) -{ - DCTL = DCTL_pwronprgdone | DCTL_sftdiscon; - - VIC_INT_EN_CLEAR = INTERRUPT_USB; - - sleep(HZ/20); - - CGU_USB = 0; - bitclr32(&CGU_PERI, CGU_USB_CLOCK_ENABLE); -} - -static void handle_ep_int(int ep, bool out) -{ - unsigned long sts = DEPINT(ep, out); - logf("%s(%d %s): sts = 0x%lx", __func__, ep, out?"OUT":"IN", sts); - - if(sts & DEPINT_ahberr) - panicf("usb-drv: ahb error on EP%d %s", ep, out ? "OUT" : "IN"); - - if(sts & DEPINT_xfercompl) - { - struct ep_type *endpoint = &endpoints[ep][out ? DIR_OUT : DIR_IN]; - if(endpoint->busy) - { - endpoint->busy = false; - endpoint->status = 0; - /* works even for EP0 */ - int size = (DEPTSIZ(ep, out) & DEPTSIZ_xfersize_bits); - int transfered = endpoint->size - size; - if(ep == 0) - { - bool is_ack = endpoint->size == 0; - switch(ep0_state) - { - case EP0_WAIT_SETUP: - panicf("usb-drv: EP0 completion while waiting for SETUP"); - case EP0_WAIT_DATA_ACK: - ep0_state = is_ack ? EP0_WAIT_DATA : EP0_WAIT_ACK; - break; - case EP0_WAIT_ACK: - case EP0_WAIT_DATA: - if((!is_ack && ep0_state == EP0_WAIT_ACK) || (is_ack && ep0_state == EP0_WAIT_DATA)) - panicf("usb-drv: bad EP0 state"); - - prepare_setup_ep0(); - break; - } - } - if (!out) - endpoint->size = size; - usb_core_transfer_complete(ep, out ? USB_DIR_OUT : USB_DIR_IN, 0, transfered); - endpoint->done = true; - semaphore_release(&endpoint->complete); - } - } - - if(!out && (sts & DIEPINT_timeout)) - panicf("usb-drv: timeout on EP%d IN", ep); - - if(out && (sts & DOEPINT_setup)) - { - if(ep != 0) - panicf("usb-drv: setup not on EP0, this is impossible"); - if((DEPTSIZ(ep, true) & DEPTSIZ_xfersize_bits) != 0) - { - logf("usb-drv: ignore spurious setup (xfersize=%ld)", DOEPTSIZ(ep) & DEPTSIZ_xfersize_bits); - prepare_setup_ep0(); - } - else - { - if(ep0_state == EP0_WAIT_SETUP) - { - bool data_phase = ep0_setup_pkt->wLength != 0; - ep0_state = data_phase ? EP0_WAIT_DATA_ACK : EP0_WAIT_ACK; - } - - logf(" rt=%x r=%x", ep0_setup_pkt->bRequestType, ep0_setup_pkt->bRequest); - - if(ep0_setup_pkt->bRequestType == USB_TYPE_STANDARD && - ep0_setup_pkt->bRequest == USB_REQ_SET_ADDRESS) - DCFG = (DCFG & ~bitm(DCFG, devadr)) | (ep0_setup_pkt->wValue << DCFG_devadr_bitp); - - usb_core_control_request(ep0_setup_pkt); - } - } - - DEPINT(ep, out) = sts; -} - -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; - logf("usb-drv: INT 0x%lx", sts); - - if(sts & GINTMSK_usbreset) - { - DCFG &= ~bitm(DCFG, devadr); /* Address 0 */ - reset_endpoints(); - usb_core_bus_reset(); - } - - if(sts & GINTMSK_enumdone) /* enumeration done, we now know the speed */ - { - /* Set up the maximum packet sizes accordingly */ - uint32_t maxpacket = (usb_drv_port_speed() ? 512 : 64) << DEPCTL_mps_bitp; - for (int dir = 0; dir < 2; dir++) - { - bool out = dir == DIR_OUT; - for (unsigned i = 1; i < num_eps(out); i++) - { - int ep = (out ? out_ep_list : in_ep_list)[i]; - DEPCTL(ep, out) &= ~(DEPCTL_mps_bits << DEPCTL_mps_bitp); - DEPCTL(ep, out) |= maxpacket; - } - } - } - - if(sts & (GINTMSK_outepintr | GINTMSK_inepintr)) - { - unsigned long daint = DAINT; - - for (int i = 0; i < USB_NUM_ENDPOINTS; i++) - { - if (daint & DAINT_IN_EP(i)) - handle_ep_int(i, false); - if (daint & DAINT_OUT_EP(i)) - handle_ep_int(i, true); - } - - DAINT = daint; - } - - if(sts & GINTMSK_disconnect) - cancel_all_transfers(true); - - GINTSTS = sts; -} - -int usb_drv_request_endpoint(int type, int dir) -{ - bool out = dir == USB_DIR_OUT; - for (unsigned i = 1; i < num_eps(out); i++) - { - int ep = (out ? out_ep_list : in_ep_list)[i]; - bool *active = &endpoints[ep][out ? DIR_OUT : DIR_IN].active; - if(*active) - continue; - *active = true; - DEPCTL(ep, out) = (DEPCTL(ep, out) & ~(DEPCTL_eptype_bits << DEPCTL_eptype_bitp)) - | DEPCTL_setd0pid | (type << DEPCTL_eptype_bitp) | DEPCTL_usbactep; - return ep | dir; - } - - return -1; -} - -void usb_drv_release_endpoint(int ep) -{ - endpoints[EP_NUM(ep)][EP_DIR(ep)].active = false; -} - -void usb_drv_cancel_all_transfers() -{ - cancel_all_transfers(false); -} - -static void ep_transfer(int ep, void *ptr, int len, bool out) -{ - /* disable interrupts to avoid any race */ - int oldlevel = disable_irq_save(); - - struct ep_type *endpoint = &endpoints[ep][out ? DIR_OUT : DIR_IN]; - endpoint->busy = true; - endpoint->size = len; - endpoint->status = -1; - - if (out) - DEPCTL(ep, out) &= ~DEPCTL_stall; - DEPCTL(ep, out) |= DEPCTL_usbactep; - - int mps = usb_drv_port_speed() ? 512 : 64; - int nb_packets = (len + mps - 1) / mps; - if (nb_packets == 0) - nb_packets = 1; - - DEPDMA(ep, out) = len - ? (void*)AS3525_PHYSICAL_ADDR(ptr) - : (void*)0x10000000; - DEPTSIZ(ep, out) = (nb_packets << DEPTSIZ_pkcnt_bitp) | len; - if(out) - discard_dcache_range(ptr, len); - else - commit_dcache_range(ptr, len); - - logf("pkt=%d dma=%lx", nb_packets, DEPDMA(ep, out)); - - DEPCTL(ep, out) |= DEPCTL_epena | DEPCTL_cnak; - - restore_irq(oldlevel); -} - -int usb_drv_send(int ep, void *ptr, int len) -{ - ep = EP_NUM(ep); - struct ep_type *endpoint = &endpoints[ep][1]; - endpoint->done = false; - ep_transfer(ep, ptr, len, false); - while (endpoint->busy && !endpoint->done) - semaphore_wait(&endpoint->complete, TIMEOUT_BLOCK); - return endpoint->status; -} diff --git a/firmware/target/arm/usb-s3c6400x.c b/firmware/target/arm/usb-s3c6400x.c index 43c9f2fd77..2b221b1963 100644 --- a/firmware/target/arm/usb-s3c6400x.c +++ b/firmware/target/arm/usb-s3c6400x.c @@ -36,6 +36,9 @@ #include <inttypes.h> #include "power.h" +//#define LOGF_ENABLE +#include "logf.h" + /* store per endpoint, per direction, information */ struct ep_type { @@ -107,7 +110,410 @@ void usb_drv_set_test_mode(int mode) } #if CONFIG_CPU == AS3525v2 /* FIXME FIXME FIXME */ -# include "as3525/usb-drv-as3525v2.c" +static const uint8_t in_ep_list[] = {0, 1, 3, 5}; +static const uint8_t out_ep_list[] = {0, 2, 4}; + +/* state of EP0 (to correctly schedule setup packet enqueing) */ +enum ep0state +{ + /* Setup packet is enqueud, waiting for actual data */ + EP0_WAIT_SETUP = 0, + /* Waiting for ack (either IN or OUT) */ + EP0_WAIT_ACK = 1, + /* Ack complete, waiting for data (either IN or OUT) + * This state is necessary because if both ack and data complete in the + * same interrupt, we might process data completion before ack completion + * so we need this bizarre state */ + EP0_WAIT_DATA = 2, + /* Setup packet complete, waiting for ack and data */ + EP0_WAIT_DATA_ACK = 3, +}; + +/* endpoints[ep_num][DIR_IN/DIR_OUT] */ +static struct ep_type endpoints[USB_NUM_ENDPOINTS][2]; +/* setup packet for EP0 */ + +/* USB control requests may be up to 64 bytes in size. + Even though we never use anything more than the 8 header bytes, + we are required to accept request packets of up to 64 bytes size. + Provide buffer space for these additional payload bytes so that + e.g. write descriptor requests (which are rejected by us, but the + payload is transferred anyway) do not cause memory corruption. + Fixes FS#12310. -- Michael Sparmann (theseven) */ +static union { + struct usb_ctrlrequest header; /* 8 bytes */ + unsigned char payload[64]; +} _ep0_setup_pkt USB_DEVBSS_ATTR; + +static struct usb_ctrlrequest *ep0_setup_pkt = AS3525_UNCACHED_ADDR(&_ep0_setup_pkt.header); + +/* state of EP0 */ +static enum ep0state ep0_state; + +void usb_attach(void) +{ + /* Nothing to do */ +} + +static void prepare_setup_ep0(void) +{ + DEPDMA(0, true) = (void*)AS3525_PHYSICAL_ADDR(&_ep0_setup_pkt); + DEPTSIZ(0, true) = (1 << DEPTSIZ0_supcnt_bitp) + | (1 << DEPTSIZ0_pkcnt_bitp) + | 8; + DEPCTL(0, true) |= DEPCTL_epena | DEPCTL_cnak; + + ep0_state = EP0_WAIT_SETUP; +} + +static size_t num_eps(bool out) +{ + return out ? sizeof(out_ep_list) : sizeof(in_ep_list); +} + +static void reset_endpoints(void) +{ + for (int dir = 0; dir < 2; dir++) + { + bool out = dir == DIR_OUT; + for (unsigned i = 0; i < num_eps(dir == DIR_OUT); i++) + { + int ep = ((dir == DIR_IN) ? in_ep_list : out_ep_list)[i]; + struct ep_type *endpoint = &endpoints[ep][out]; + endpoint->active = false; + endpoint->busy = false; + endpoint->status = -1; + endpoint->done = false; + semaphore_release(&endpoint->complete); + + if (i != 0) + DEPCTL(ep, out) = DEPCTL_setd0pid; + } + DEPCTL(0, out) = /*(DEPCTL_MPS_64 << DEPCTL_mps_bitp) | */ DEPCTL_usbactep; + } + + /* Setup next chain for IN eps */ + for (unsigned i = 0; i < num_eps(false); i++) + { + int ep = in_ep_list[i]; + int next_ep = in_ep_list[(i + 1) % num_eps(false)]; + DEPCTL(ep, false) |= next_ep << DEPCTL_nextep_bitp; + } + + prepare_setup_ep0(); +} + +static void cancel_all_transfers(bool cancel_ep0) +{ + int flags = disable_irq_save(); + + for (int dir = 0; dir < 2; dir++) + for (unsigned i = !!cancel_ep0; i < num_eps(dir == DIR_OUT); i++) + { + int ep = ((dir == DIR_IN) ? in_ep_list : out_ep_list)[i]; + struct ep_type *endpoint = &endpoints[ep][dir == DIR_OUT]; + endpoint->status = -1; + endpoint->busy = false; + endpoint->done = false; + semaphore_release(&endpoint->complete); + DEPCTL(ep, dir) = (DEPCTL(ep, dir) & ~DEPCTL_usbactep); + } + + restore_irq(flags); +} + +void usb_drv_init(void) +{ + for (int i = 0; i < USB_NUM_ENDPOINTS; i++) + for (int dir = 0; dir < 2; dir++) + semaphore_init(&endpoints[i][dir].complete, 1, 0); + + bitset32(&CGU_PERI, CGU_USB_CLOCK_ENABLE); + CCU_USB = (CCU_USB & ~(3<<24)) | (1 << 24); /* ?? */ + /* PHY clock */ + CGU_USB = 1<<5 /* enable */ + | 0 << 2 + | 0; /* source = ? (24MHz crystal?) */ + + PCGCCTL = 0; + DCTL = DCTL_pwronprgdone | DCTL_sftdiscon; + + GRSTCTL = GRSTCTL_csftrst; + while (GRSTCTL & GRSTCTL_csftrst); /* Wait for OTG to ack reset */ + while (!(GRSTCTL & GRSTCTL_ahbidle)); /* Wait for OTG AHB master idle */ + + GRXFSIZ = 512; + GNPTXFSIZ = MAKE_FIFOSIZE_DATA(512); + + /* FIXME: the current code is for internal DMA only, the clip+ architecture + * defines the internal DMA model */ + GAHBCFG = (GAHBCFG_INT_DMA_BURST_INCR << GAHBCFG_hburstlen_bitp) + | GAHBCFG_dma_enable | GAHBCFG_glblintrmsk; + + /* Select UTMI+ 16 */ + GUSBCFG = GUSBCFG_force_device_mode | GUSBCFG_phy_if | 7 << GUSBCFG_toutcal_bitp; + + /* Do something that is probably CCU related but undocumented*/ + CCU_USB |= 0x1000; + CCU_USB &= ~0x300000; + + DCFG = DCFG_nzstsouthshk | DCFG_devspd_hs_phy_hs; /* Address 0, high speed */ + DCTL = DCTL_pwronprgdone; + + /* Check hardware capabilities */ + if(extract(GHWCFG2, arch) != GHWCFG2_ARCH_INTERNAL_DMA) + panicf("usb-drv: wrong architecture (%ld)", extract(GHWCFG2, arch)); + if(extract(GHWCFG2, hs_phy_type) != GHWCFG2_PHY_TYPE_UTMI) + panicf("usb-drv: wrong HS phy type (%ld)", extract(GHWCFG2, hs_phy_type)); + if(extract(GHWCFG2, fs_phy_type) != GHWCFG2_PHY_TYPE_UNSUPPORTED) + panicf("usb-drv: wrong FS phy type (%ld)", extract(GHWCFG2, fs_phy_type)); + if(extract(GHWCFG4, utmi_phy_data_width) != 0x2) + panicf("usb-drv: wrong utmi data width (%ld)", extract(GHWCFG4, utmi_phy_data_width)); + if(!(GHWCFG4 & GHWCFG4_ded_fifo_en)) /* it seems to be multiple tx fifo support */ + panicf("usb-drv: no multiple tx fifo"); + + if(USB_NUM_ENDPOINTS != extract(GHWCFG2, num_ep)) + panicf("usb-drv: wrong endpoint number"); + + for (int dir = 0; dir < 2; dir++) + for (unsigned i = 0; i < num_eps(dir == DIR_OUT); i++) + { + int ep = ((dir == DIR_IN) ? in_ep_list : out_ep_list)[i]; + int type = (GHWCFG1 >> GHWCFG1_epdir_bitp(ep)) & GHWCFG1_epdir_bits; + int flag = (dir == DIR_IN) ? GHWCFG1_EPDIR_IN : GHWCFG1_EPDIR_OUT; + if(type != GHWCFG1_EPDIR_BIDIR && type != flag) + panicf("usb-drv: EP%d not in correct direction", ep); + } + + DOEPMSK = DEPINT_xfercompl | DEPINT_ahberr | DOEPINT_setup; + DIEPMSK = DEPINT_xfercompl | DEPINT_ahberr | DIEPINT_timeout; + DAINTMSK = 0xffffffff; + + reset_endpoints(); + + GINTMSK = GINTMSK_usbreset + | GINTMSK_enumdone + | GINTMSK_inepintr + | GINTMSK_outepintr + | GINTMSK_disconnect; + + VIC_INT_ENABLE = INTERRUPT_USB; +} + +void usb_drv_exit(void) +{ + DCTL = DCTL_pwronprgdone | DCTL_sftdiscon; + + VIC_INT_EN_CLEAR = INTERRUPT_USB; + + sleep(HZ/20); + + CGU_USB = 0; + bitclr32(&CGU_PERI, CGU_USB_CLOCK_ENABLE); +} + +static void handle_ep_int(int ep, bool out) +{ + unsigned long sts = DEPINT(ep, out); + logf("%s(%d %s): sts = 0x%lx", __func__, ep, out?"OUT":"IN", sts); + + if(sts & DEPINT_ahberr) + panicf("usb-drv: ahb error on EP%d %s", ep, out ? "OUT" : "IN"); + + if(sts & DEPINT_xfercompl) + { + struct ep_type *endpoint = &endpoints[ep][out ? DIR_OUT : DIR_IN]; + if(endpoint->busy) + { + endpoint->busy = false; + endpoint->status = 0; + /* works even for EP0 */ + int size = (DEPTSIZ(ep, out) & DEPTSIZ_xfersize_bits); + int transfered = endpoint->size - size; + if(ep == 0) + { + bool is_ack = endpoint->size == 0; + switch(ep0_state) + { + case EP0_WAIT_SETUP: + panicf("usb-drv: EP0 completion while waiting for SETUP"); + case EP0_WAIT_DATA_ACK: + ep0_state = is_ack ? EP0_WAIT_DATA : EP0_WAIT_ACK; + break; + case EP0_WAIT_ACK: + case EP0_WAIT_DATA: + if((!is_ack && ep0_state == EP0_WAIT_ACK) || (is_ack && ep0_state == EP0_WAIT_DATA)) + panicf("usb-drv: bad EP0 state"); + + prepare_setup_ep0(); + break; + } + } + if (!out) + endpoint->size = size; + usb_core_transfer_complete(ep, out ? USB_DIR_OUT : USB_DIR_IN, 0, transfered); + endpoint->done = true; + semaphore_release(&endpoint->complete); + } + } + + if(!out && (sts & DIEPINT_timeout)) + panicf("usb-drv: timeout on EP%d IN", ep); + + if(out && (sts & DOEPINT_setup)) + { + if(ep != 0) + panicf("usb-drv: setup not on EP0, this is impossible"); + if((DEPTSIZ(ep, true) & DEPTSIZ_xfersize_bits) != 0) + { + logf("usb-drv: ignore spurious setup (xfersize=%ld)", DOEPTSIZ(ep) & DEPTSIZ_xfersize_bits); + prepare_setup_ep0(); + } + else + { + if(ep0_state == EP0_WAIT_SETUP) + { + bool data_phase = ep0_setup_pkt->wLength != 0; + ep0_state = data_phase ? EP0_WAIT_DATA_ACK : EP0_WAIT_ACK; + } + + logf(" rt=%x r=%x", ep0_setup_pkt->bRequestType, ep0_setup_pkt->bRequest); + + if(ep0_setup_pkt->bRequestType == USB_TYPE_STANDARD && + ep0_setup_pkt->bRequest == USB_REQ_SET_ADDRESS) + DCFG = (DCFG & ~bitm(DCFG, devadr)) | (ep0_setup_pkt->wValue << DCFG_devadr_bitp); + + usb_core_control_request(ep0_setup_pkt); + } + } + + DEPINT(ep, out) = sts; +} + +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; + logf("usb-drv: INT 0x%lx", sts); + + if(sts & GINTMSK_usbreset) + { + DCFG &= ~bitm(DCFG, devadr); /* Address 0 */ + reset_endpoints(); + usb_core_bus_reset(); + } + + if(sts & GINTMSK_enumdone) /* enumeration done, we now know the speed */ + { + /* Set up the maximum packet sizes accordingly */ + uint32_t maxpacket = (usb_drv_port_speed() ? 512 : 64) << DEPCTL_mps_bitp; + for (int dir = 0; dir < 2; dir++) + { + bool out = dir == DIR_OUT; + for (unsigned i = 1; i < num_eps(out); i++) + { + int ep = (out ? out_ep_list : in_ep_list)[i]; + DEPCTL(ep, out) &= ~(DEPCTL_mps_bits << DEPCTL_mps_bitp); + DEPCTL(ep, out) |= maxpacket; + } + } + } + + if(sts & (GINTMSK_outepintr | GINTMSK_inepintr)) + { + unsigned long daint = DAINT; + + for (int i = 0; i < USB_NUM_ENDPOINTS; i++) + { + if (daint & DAINT_IN_EP(i)) + handle_ep_int(i, false); + if (daint & DAINT_OUT_EP(i)) + handle_ep_int(i, true); + } + + DAINT = daint; + } + + if(sts & GINTMSK_disconnect) + cancel_all_transfers(true); + + GINTSTS = sts; +} + +int usb_drv_request_endpoint(int type, int dir) +{ + bool out = dir == USB_DIR_OUT; + for (unsigned i = 1; i < num_eps(out); i++) + { + int ep = (out ? out_ep_list : in_ep_list)[i]; + bool *active = &endpoints[ep][out ? DIR_OUT : DIR_IN].active; + if(*active) + continue; + *active = true; + DEPCTL(ep, out) = (DEPCTL(ep, out) & ~(DEPCTL_eptype_bits << DEPCTL_eptype_bitp)) + | DEPCTL_setd0pid | (type << DEPCTL_eptype_bitp) | DEPCTL_usbactep; + return ep | dir; + } + + return -1; +} + +void usb_drv_release_endpoint(int ep) +{ + endpoints[EP_NUM(ep)][EP_DIR(ep)].active = false; +} + +void usb_drv_cancel_all_transfers() +{ + cancel_all_transfers(false); +} + +static void ep_transfer(int ep, void *ptr, int len, bool out) +{ + /* disable interrupts to avoid any race */ + int oldlevel = disable_irq_save(); + + struct ep_type *endpoint = &endpoints[ep][out ? DIR_OUT : DIR_IN]; + endpoint->busy = true; + endpoint->size = len; + endpoint->status = -1; + + if (out) + DEPCTL(ep, out) &= ~DEPCTL_stall; + DEPCTL(ep, out) |= DEPCTL_usbactep; + + int mps = usb_drv_port_speed() ? 512 : 64; + int nb_packets = (len + mps - 1) / mps; + if (nb_packets == 0) + nb_packets = 1; + + DEPDMA(ep, out) = len + ? (void*)AS3525_PHYSICAL_ADDR(ptr) + : (void*)0x10000000; + DEPTSIZ(ep, out) = (nb_packets << DEPTSIZ_pkcnt_bitp) | len; + if(out) + discard_dcache_range(ptr, len); + else + commit_dcache_range(ptr, len); + + logf("pkt=%d dma=%lx", nb_packets, DEPDMA(ep, out)); + + DEPCTL(ep, out) |= DEPCTL_epena | DEPCTL_cnak; + + restore_irq(oldlevel); +} + +int usb_drv_send(int ep, void *ptr, int len) +{ + ep = EP_NUM(ep); + struct ep_type *endpoint = &endpoints[ep][1]; + endpoint->done = false; + ep_transfer(ep, ptr, len, false); + while (endpoint->busy && !endpoint->done) + semaphore_wait(&endpoint->complete, TIMEOUT_BLOCK); + return endpoint->status; +} #else static struct ep_type endpoints[USB_NUM_ENDPOINTS]; |