usb-drv-as3525v2.c: simplify a lot
code, especially init sequence, is much more similar to usb-s3c6400x.c git-svn-id: svn://svn.rockbox.org/rockbox/trunk@31231 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
parent
8afdad69c7
commit
9e8590ad23
3 changed files with 171 additions and 416 deletions
|
@ -326,11 +326,12 @@
|
|||
|
||||
/** Device IN Endpoint Common Interrupt Mask Register */
|
||||
#define DIEPMSK (*((uint32_t volatile*)(OTGBASE + 0x810)))
|
||||
/* the following apply to DIEPMSK and DIEPINT */
|
||||
#define DIEPINT_xfercompl (1 << 0) /** Transfer complete */
|
||||
#define DIEPINT_epdisabled (1 << 1) /** Endpoint disabled */
|
||||
#define DIEPINT_ahberr (1 << 2) /** AHB error */
|
||||
/* the following apply to DEPMSK and DEPINT */
|
||||
#define DEPINT_xfercompl (1 << 0) /** Transfer complete */
|
||||
#define DEPINT_epdisabled (1 << 1) /** Endpoint disabled */
|
||||
#define DEPINT_ahberr (1 << 2) /** AHB error */
|
||||
#define DIEPINT_timeout (1 << 3) /** Timeout handshake (non-iso TX) */
|
||||
#define DOEPINT_setup (1 << 3) /** Setup Phase Done (control EPs)*/
|
||||
#define DIEPINT_intktxfemp (1 << 4) /** IN token received with tx fifo empty */
|
||||
#define DIEPINT_intknepmis (1 << 5) /** IN token received with ep mismatch */
|
||||
#define DIEPINT_inepnakeff (1 << 6) /** IN endpoint NAK effective */
|
||||
|
@ -339,11 +340,6 @@
|
|||
|
||||
/** Device OUT Endpoint Common Interrupt Mask Register */
|
||||
#define DOEPMSK (*((uint32_t volatile*)(OTGBASE + 0x814)))
|
||||
/* the following apply to DOEPMSK and DOEPINT */
|
||||
#define DOEPINT_xfercompl (1 << 0) /** Transfer complete */
|
||||
#define DOEPINT_epdisabled (1 << 1) /** Endpoint disabled */
|
||||
#define DOEPINT_ahberr (1 << 2) /** AHB error */
|
||||
#define DOEPINT_setup (1 << 3) /** Setup Phase Done (control EPs)*/
|
||||
|
||||
/** Device All Endpoints Interrupt Register */
|
||||
#define DAINT (*((uint32_t volatile*)(OTGBASE + 0x818)))
|
||||
|
|
|
@ -39,24 +39,6 @@
|
|||
static const uint8_t in_ep_list[] = {0, 3, 5};
|
||||
static const uint8_t out_ep_list[] = {0, 2, 4};
|
||||
|
||||
/* iterate through each in/out ep except EP0
|
||||
* 'i' is the counter, 'ep' is the actual value */
|
||||
#define FOR_EACH_EP(list, start, i, ep) \
|
||||
for(ep = list[i = start]; \
|
||||
i < (sizeof(list)/sizeof(*list)); \
|
||||
i++, ep = list[i])
|
||||
|
||||
#define FOR_EACH_IN_EP_EX(include_ep0, i, ep) \
|
||||
FOR_EACH_EP(in_ep_list, !include_ep0, i, ep)
|
||||
|
||||
#define FOR_EACH_OUT_EP_EX(include_ep0, i, ep) \
|
||||
FOR_EACH_EP(out_ep_list, !include_ep0, i, ep)
|
||||
|
||||
#define FOR_EACH_IN_EP(i, ep) FOR_EACH_IN_EP_EX(false, i, ep)
|
||||
#define FOR_EACH_IN_EP_AND_EP0(i, ep) FOR_EACH_IN_EP_EX(true, i, ep)
|
||||
#define FOR_EACH_OUT_EP(i, ep) FOR_EACH_OUT_EP_EX(false, i, ep)
|
||||
#define FOR_EACH_OUT_EP_AND_EP0(i, ep) FOR_EACH_OUT_EP_EX(true, i, ep)
|
||||
|
||||
/* store per endpoint, per direction, information */
|
||||
struct usb_endpoint
|
||||
{
|
||||
|
@ -64,7 +46,7 @@ struct usb_endpoint
|
|||
struct semaphore complete; /* wait object */
|
||||
int8_t status; /* completion status (0 for success) */
|
||||
bool active; /* true is endpoint has been requested (true for EP0) */
|
||||
bool wait; /* true if usb thread is blocked on completion */
|
||||
bool done; /* transfer completed */
|
||||
bool busy; /* true is a transfer is pending */
|
||||
};
|
||||
|
||||
|
@ -107,114 +89,26 @@ static enum ep0state ep0_state;
|
|||
|
||||
void usb_attach(void)
|
||||
{
|
||||
logf("usb-drv: attach");
|
||||
logf("%s", __func__);
|
||||
/* Nothing to do */
|
||||
}
|
||||
|
||||
static inline void usb_delay(void)
|
||||
{
|
||||
register int i = 0;
|
||||
asm volatile(
|
||||
"1: nop \n"
|
||||
" add %0, %0, #1 \n"
|
||||
" cmp %0, #0x300 \n"
|
||||
" bne 1b \n"
|
||||
: "+r"(i)
|
||||
);
|
||||
}
|
||||
|
||||
static void as3525v2_connect(void)
|
||||
{
|
||||
logf("usb-drv: init as3525v2");
|
||||
/* 1) enable usb core clock */
|
||||
bitset32(&CGU_PERI, CGU_USB_CLOCK_ENABLE);
|
||||
usb_delay();
|
||||
/* 2) enable usb phy clock */
|
||||
CCU_USB = (CCU_USB & ~(3<<24)) | (1 << 24); /* ?? */
|
||||
/* PHY clock */
|
||||
CGU_USB = 1<<5 /* enable */
|
||||
| 0 << 2
|
||||
| 0; /* source = ? (24MHz crystal?) */
|
||||
usb_delay();
|
||||
/* 3) clear "stop pclk" */
|
||||
PCGCCTL &= ~0x1;
|
||||
usb_delay();
|
||||
/* 4) clear "power clamp" */
|
||||
PCGCCTL &= ~0x4;
|
||||
usb_delay();
|
||||
/* 5) clear "reset power down module" */
|
||||
PCGCCTL &= ~0x8;
|
||||
usb_delay();
|
||||
/* 6) set "power on program done" */
|
||||
DCTL |= DCTL_pwronprgdone;
|
||||
usb_delay();
|
||||
/* 7) core soft reset */
|
||||
GRSTCTL |= GRSTCTL_csftrst;
|
||||
usb_delay();
|
||||
/* 8) hclk soft reset */
|
||||
GRSTCTL |= GRSTCTL_hsftrst;
|
||||
usb_delay();
|
||||
/* 9) flush and reset everything */
|
||||
GRSTCTL |= 0x3f;
|
||||
usb_delay();
|
||||
/* 10) force device mode*/
|
||||
GUSBCFG &= ~GUSBCFG_force_host_mode;
|
||||
GUSBCFG |= GUSBCFG_force_device_mode;
|
||||
usb_delay();
|
||||
/* 11) Do something that is probably CCU related but undocumented*/
|
||||
CCU_USB |= 0x1000;
|
||||
CCU_USB &= ~0x300000;
|
||||
usb_delay();
|
||||
/* 12) reset usb core parameters (dev addr, speed, ...) */
|
||||
DCFG = 0;
|
||||
usb_delay();
|
||||
}
|
||||
|
||||
static void as3525v2_disconnect(void)
|
||||
{
|
||||
/* Disconnect */
|
||||
DCTL |= DCTL_sftdiscon;
|
||||
sleep(HZ/20);
|
||||
/* Disable clock */
|
||||
CGU_USB = 0;
|
||||
usb_delay();
|
||||
bitclr32(&CGU_PERI, CGU_USB_CLOCK_ENABLE);
|
||||
}
|
||||
|
||||
static void enable_device_interrupts(void)
|
||||
{
|
||||
/* Clear any pending interrupt */
|
||||
GINTSTS = 0xffffffff;
|
||||
/* Clear any pending otg interrupt */
|
||||
GOTGINT = 0xffffffff;
|
||||
/* Enable interrupts */
|
||||
GINTMSK = GINTMSK_usbreset
|
||||
| GINTMSK_enumdone
|
||||
| GINTMSK_inepintr
|
||||
| GINTMSK_outepintr
|
||||
| GINTMSK_disconnect
|
||||
| GINTMSK_usbsuspend
|
||||
| GINTMSK_wkupintr
|
||||
| GINTMSK_otgintr;
|
||||
}
|
||||
|
||||
static void flush_tx_fifos(int nums)
|
||||
static void flush_tx_fifos(void)
|
||||
{
|
||||
unsigned int i = 0;
|
||||
|
||||
GRSTCTL = (nums << GRSTCTL_txfnum_bitp)
|
||||
| GRSTCTL_txfflsh_flush;
|
||||
while(GRSTCTL & GRSTCTL_txfflsh_flush && i < 0x300)
|
||||
i++;
|
||||
if(GRSTCTL & GRSTCTL_txfflsh_flush)
|
||||
panicf("usb-drv: hang of flush tx fifos (%x)", nums);
|
||||
GRSTCTL = (0x10 << GRSTCTL_txfnum_bitp) | GRSTCTL_txfflsh_flush;
|
||||
while(GRSTCTL & GRSTCTL_txfflsh_flush)
|
||||
if (i++ >= 0x300)
|
||||
panicf("usb-drv: hang of flush tx fifos");
|
||||
|
||||
/* wait 3 phy clocks */
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
static void prepare_setup_ep0(void)
|
||||
{
|
||||
logf("usb-drv: prepare EP0");
|
||||
logf("%s", __func__);
|
||||
/* setup DMA */
|
||||
DEPDMA(0, true) = (void*)AS3525_PHYSICAL_ADDR(&_ep0_setup_pkt);
|
||||
|
||||
|
@ -253,11 +147,7 @@ static void handle_ep0_complete(bool is_ack)
|
|||
prepare_setup_ep0();
|
||||
break;
|
||||
case EP0_WAIT_DATA_ACK:
|
||||
/* update state */
|
||||
if(is_ack)
|
||||
ep0_state = EP0_WAIT_DATA;
|
||||
else
|
||||
ep0_state = EP0_WAIT_ACK;
|
||||
ep0_state = is_ack ? EP0_WAIT_DATA : EP0_WAIT_ACK;
|
||||
break;
|
||||
default:
|
||||
panicf("usb-drv: invalid EP0 state");
|
||||
|
@ -284,42 +174,29 @@ static void handle_ep0_setup(void)
|
|||
|
||||
static void reset_endpoints(void)
|
||||
{
|
||||
unsigned i;
|
||||
int ep;
|
||||
/* disable all endpoints except EP0 */
|
||||
FOR_EACH_IN_EP_AND_EP0(i, ep)
|
||||
{
|
||||
endpoints[ep][DIR_IN].active = false;
|
||||
endpoints[ep][DIR_IN].busy = false;
|
||||
endpoints[ep][DIR_IN].status = -1;
|
||||
if(endpoints[ep][DIR_IN].wait)
|
||||
for (int dir = 0; dir < 2; dir++)
|
||||
for (unsigned i = 0; i < sizeof((dir == DIR_IN) ? in_ep_list : out_ep_list); i++)
|
||||
{
|
||||
endpoints[ep][DIR_IN].wait = false;
|
||||
semaphore_release(&endpoints[ep][DIR_IN].complete);
|
||||
}
|
||||
DEPCTL(ep, false) = (DEPCTL(ep, false) & DEPCTL_epena)
|
||||
? DEPCTL_snak
|
||||
: 0;
|
||||
}
|
||||
int ep = ((dir == DIR_IN) ? in_ep_list : out_ep_list)[i];
|
||||
endpoints[ep][dir == DIR_OUT].active = false;
|
||||
endpoints[ep][dir == DIR_OUT].busy = false;
|
||||
endpoints[ep][dir == DIR_OUT].status = -1;
|
||||
endpoints[ep][dir == DIR_OUT].done = false;
|
||||
semaphore_release(&endpoints[ep][dir == DIR_OUT].complete);
|
||||
|
||||
FOR_EACH_OUT_EP_AND_EP0(i, ep)
|
||||
{
|
||||
endpoints[ep][DIR_OUT].active = false;
|
||||
endpoints[ep][DIR_OUT].busy = false;
|
||||
endpoints[ep][DIR_OUT].status = -1;
|
||||
if(endpoints[ep][DIR_OUT].wait)
|
||||
{
|
||||
endpoints[ep][DIR_OUT].wait = false;
|
||||
semaphore_release(&endpoints[ep][DIR_OUT].complete);
|
||||
if (i != 0)
|
||||
DEPCTL(ep, dir == DIR_OUT) = (DEPCTL(ep, dir == DIR_OUT) & DEPCTL_epena)
|
||||
? DEPCTL_snak
|
||||
: 0;
|
||||
else
|
||||
DEPCTL(0, dir == DIR_OUT) = (DEPCTL_MPS_64 << DEPCTL_mps_bitp) | DEPCTL_usbactep | DEPCTL_snak;
|
||||
}
|
||||
DEPCTL(ep, true) = (DEPCTL(ep, true) & DEPCTL_epena) ? DEPCTL_snak : 0;
|
||||
}
|
||||
/* 64 bytes packet size, active endpoint */
|
||||
DEPCTL(0, true) = (DEPCTL_MPS_64 << DEPCTL_mps_bitp) | DEPCTL_usbactep | DEPCTL_snak;
|
||||
DEPCTL(0, false) = (DEPCTL_MPS_64 << DEPCTL_mps_bitp) | DEPCTL_usbactep | DEPCTL_snak;
|
||||
|
||||
|
||||
/* Setup next chain for IN eps */
|
||||
FOR_EACH_IN_EP_AND_EP0(i, ep)
|
||||
for (unsigned i = 0; i < sizeof(in_ep_list); i++)
|
||||
{
|
||||
int ep = in_ep_list[i];
|
||||
int next_ep = in_ep_list[(i + 1) % sizeof(in_ep_list)];
|
||||
DEPCTL(ep, false) = (DEPCTL(ep, false) & ~bitm(DEPCTL, nextep)) | (next_ep << DEPCTL_nextep_bitp);
|
||||
}
|
||||
|
@ -327,45 +204,61 @@ static void reset_endpoints(void)
|
|||
|
||||
static void cancel_all_transfers(bool cancel_ep0)
|
||||
{
|
||||
logf("usb-drv: cancel all transfers");
|
||||
logf("%s", __func__);
|
||||
int flags = disable_irq_save();
|
||||
int ep;
|
||||
unsigned i;
|
||||
|
||||
FOR_EACH_IN_EP_EX(cancel_ep0, i, ep)
|
||||
{
|
||||
endpoints[ep][DIR_IN].status = -1;
|
||||
endpoints[ep][DIR_IN].busy = false;
|
||||
if(endpoints[ep][DIR_IN].wait)
|
||||
for (int dir = 0; dir < 2; dir++)
|
||||
for (unsigned i = !!cancel_ep0; i < sizeof((dir == DIR_IN) ? in_ep_list : out_ep_list); i++)
|
||||
{
|
||||
endpoints[ep][DIR_IN].wait = false;
|
||||
semaphore_release(&endpoints[ep][DIR_IN].complete);
|
||||
int ep = ((dir == DIR_IN) ? in_ep_list : out_ep_list)[i];
|
||||
endpoints[ep][dir == DIR_OUT].status = -1;
|
||||
endpoints[ep][dir == DIR_OUT].busy = false;
|
||||
endpoints[ep][dir == DIR_OUT].done = false;
|
||||
semaphore_release(&endpoints[ep][dir == DIR_OUT].complete);
|
||||
DEPCTL(ep, dir) = (DEPCTL(ep, dir) & ~DEPCTL_usbactep) | DEPCTL_snak;
|
||||
}
|
||||
DEPCTL(ep, false) = (DEPCTL(ep, false) & ~DEPCTL_usbactep) | DEPCTL_snak;
|
||||
}
|
||||
FOR_EACH_OUT_EP_EX(cancel_ep0, i, ep)
|
||||
{
|
||||
endpoints[ep][DIR_OUT].status = -1;
|
||||
endpoints[ep][DIR_OUT].busy = false;
|
||||
if(endpoints[ep][DIR_OUT].wait)
|
||||
{
|
||||
endpoints[ep][DIR_OUT].wait = false;
|
||||
semaphore_release(&endpoints[ep][DIR_OUT].complete);
|
||||
}
|
||||
DEPCTL(ep, true) = (DEPCTL(ep, true) & ~DEPCTL_usbactep) | DEPCTL_snak;
|
||||
}
|
||||
|
||||
restore_irq(flags);
|
||||
}
|
||||
|
||||
static void core_dev_init(void)
|
||||
void usb_drv_init(void)
|
||||
{
|
||||
int ep;
|
||||
unsigned int i;
|
||||
/* Restart the phy clock */
|
||||
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;
|
||||
/* Set phy speed : high speed */
|
||||
DCFG = (DCFG & ~bitm(DCFG, devspd)) | DCFG_devspd_hs_phy_hs;
|
||||
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
|
||||
* define the internal DMA model */
|
||||
/* Set burstlen and enable DMA*/
|
||||
GAHBCFG = (GAHBCFG_INT_DMA_BURST_INCR << GAHBCFG_hburstlen_bitp)
|
||||
| GAHBCFG_dma_enable;
|
||||
|
||||
/* 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)
|
||||
|
@ -379,140 +272,80 @@ static void core_dev_init(void)
|
|||
if(!(GHWCFG4 & GHWCFG4_ded_fifo_en)) /* it seems to be multiple tx fifo support */
|
||||
panicf("usb-drv: no multiple tx fifo");
|
||||
|
||||
#ifdef USE_CUSTOM_FIFO_LAYOUT
|
||||
if(!(GHWCFG2 & GHWCFG2_dyn_fifo))
|
||||
panicf("usb-drv: no dynamic fifo");
|
||||
if(GRXFSIZ != DATA_FIFO_DEPTH)
|
||||
panicf("usb-drv: wrong data fifo size");
|
||||
#endif /* USE_CUSTOM_FIFO_LAYOUT */
|
||||
|
||||
if(USB_NUM_ENDPOINTS != extract(GHWCFG2, num_ep))
|
||||
panicf("usb-drv: wrong endpoint number");
|
||||
|
||||
FOR_EACH_IN_EP_AND_EP0(i, ep)
|
||||
{
|
||||
int type = (GHWCFG1 >> GHWCFG1_epdir_bitp(ep)) & GHWCFG1_epdir_bits;
|
||||
if(type != GHWCFG1_EPDIR_BIDIR && type != GHWCFG1_EPDIR_IN)
|
||||
panicf("usb-drv: EP%d is no IN or BIDIR", ep);
|
||||
}
|
||||
FOR_EACH_OUT_EP_AND_EP0(i, ep)
|
||||
{
|
||||
int type = (GHWCFG1 >> GHWCFG1_epdir_bitp(ep)) & GHWCFG1_epdir_bits;
|
||||
if(type != GHWCFG1_EPDIR_BIDIR && type != GHWCFG1_EPDIR_OUT)
|
||||
panicf("usb-drv: EP%d is no OUT or BIDIR", ep);
|
||||
}
|
||||
for (int dir = 0; dir < 2; dir++)
|
||||
for (unsigned i = 0; i < sizeof((dir == DIR_IN) ? in_ep_list : out_ep_list); 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);
|
||||
}
|
||||
|
||||
/* Setup FIFOs */
|
||||
GRXFSIZ = 512;
|
||||
GNPTXFSIZ = MAKE_FIFOSIZE_DATA(512);
|
||||
|
||||
/* Setup interrupt masks for endpoints */
|
||||
/* Setup interrupt masks */
|
||||
DOEPMSK = DOEPINT_setup | DOEPINT_xfercompl | DOEPINT_ahberr;
|
||||
DIEPMSK = DIEPINT_xfercompl | DIEPINT_timeout | DIEPINT_ahberr;
|
||||
DOEPMSK = DEPINT_xfercompl | DEPINT_ahberr | DOEPINT_setup;
|
||||
DIEPMSK = DEPINT_xfercompl | DEPINT_ahberr | DIEPINT_timeout;
|
||||
DAINTMSK = 0xffffffff;
|
||||
|
||||
reset_endpoints();
|
||||
|
||||
prepare_setup_ep0();
|
||||
|
||||
/* enable USB interrupts */
|
||||
enable_device_interrupts();
|
||||
}
|
||||
GINTMSK = GINTMSK_usbreset
|
||||
| GINTMSK_enumdone
|
||||
| GINTMSK_inepintr
|
||||
| GINTMSK_outepintr
|
||||
| GINTMSK_disconnect
|
||||
| GINTMSK_usbsuspend
|
||||
| GINTMSK_wkupintr
|
||||
| GINTMSK_otgintr;
|
||||
|
||||
static void core_init(void)
|
||||
{
|
||||
/* Disconnect */
|
||||
DCTL |= DCTL_sftdiscon;
|
||||
/* Select UTMI+ 16 */
|
||||
GUSBCFG |= GUSBCFG_phy_if;
|
||||
GUSBCFG = (GUSBCFG & ~bitm(GUSBCFG, toutcal)) | 7 << GUSBCFG_toutcal_bitp;
|
||||
|
||||
/* fixme: the current code is for internal DMA only, the clip+ architecture
|
||||
* define the internal DMA model */
|
||||
/* Set burstlen and enable DMA*/
|
||||
GAHBCFG = (GAHBCFG_INT_DMA_BURST_INCR << GAHBCFG_hburstlen_bitp)
|
||||
| GAHBCFG_dma_enable;
|
||||
/* Disable HNP and SRP, not sure it's useful because we already forced dev mode */
|
||||
GUSBCFG &= ~(GUSBCFG_srpcap | GUSBCFG_hnpcapp);
|
||||
|
||||
/* perform device model specific init */
|
||||
core_dev_init();
|
||||
|
||||
/* Reconnect */
|
||||
DCTL &= ~DCTL_sftdiscon;
|
||||
}
|
||||
|
||||
static void enable_global_interrupts(void)
|
||||
{
|
||||
VIC_INT_ENABLE = INTERRUPT_USB;
|
||||
GAHBCFG |= GAHBCFG_glblintrmsk;
|
||||
}
|
||||
|
||||
static void disable_global_interrupts(void)
|
||||
{
|
||||
GAHBCFG &= ~GAHBCFG_glblintrmsk;
|
||||
VIC_INT_EN_CLEAR = INTERRUPT_USB;
|
||||
}
|
||||
|
||||
void usb_drv_init(void)
|
||||
{
|
||||
unsigned i, ep;
|
||||
logf("usb_drv_init");
|
||||
/* Boost cpu */
|
||||
cpu_boost(1);
|
||||
/* Enable PHY and clocks (but leave pullups disabled) */
|
||||
as3525v2_connect();
|
||||
logf("usb-drv: synopsis id: %lx", GSNPSID);
|
||||
/* Core init */
|
||||
core_init();
|
||||
FOR_EACH_IN_EP_AND_EP0(i, ep)
|
||||
semaphore_init(&endpoints[ep][DIR_IN].complete, 1, 0);
|
||||
FOR_EACH_OUT_EP_AND_EP0(i, ep)
|
||||
semaphore_init(&endpoints[ep][DIR_OUT].complete, 1, 0);
|
||||
/* Enable global interrupts */
|
||||
enable_global_interrupts();
|
||||
}
|
||||
|
||||
void usb_drv_exit(void)
|
||||
{
|
||||
logf("usb_drv_exit");
|
||||
logf("%s", __func__);
|
||||
|
||||
disable_global_interrupts();
|
||||
as3525v2_disconnect();
|
||||
cpu_boost(0);
|
||||
GAHBCFG &= ~GAHBCFG_glblintrmsk;
|
||||
VIC_INT_EN_CLEAR = INTERRUPT_USB;
|
||||
|
||||
DCTL = DCTL_pwronprgdone | DCTL_sftdiscon;
|
||||
sleep(HZ/20);
|
||||
|
||||
CGU_USB = 0;
|
||||
bitclr32(&CGU_PERI, CGU_USB_CLOCK_ENABLE);
|
||||
}
|
||||
|
||||
static void handle_ep_in_int(int ep)
|
||||
static void handle_ep_int(int ep, bool out)
|
||||
{
|
||||
struct usb_endpoint *endpoint = &endpoints[ep][DIR_IN];
|
||||
unsigned long sts = DEPINT(ep, false);
|
||||
if(sts & DIEPINT_ahberr)
|
||||
panicf("usb-drv: ahb error on EP%d IN", ep);
|
||||
if(sts & DIEPINT_xfercompl)
|
||||
struct usb_endpoint *endpoint = &endpoints[ep][out ? DIR_OUT : DIR_IN];
|
||||
unsigned long sts = DEPINT(ep, out);
|
||||
if(sts & DEPINT_ahberr)
|
||||
panicf("usb-drv: ahb error on EP%d %s", ep, out ? "OUT" : "IN");
|
||||
if(sts & DEPINT_xfercompl)
|
||||
{
|
||||
if(endpoint->busy)
|
||||
{
|
||||
endpoint->busy = false;
|
||||
endpoint->status = 0;
|
||||
/* works even for EP0 */
|
||||
int size = (DEPTSIZ(ep, false) & DEPTSIZ_xfersize_bits);
|
||||
int size = (DEPTSIZ(ep, out) & DEPTSIZ_xfersize_bits);
|
||||
int transfered = endpoint->len - size;
|
||||
logf("len=%d reg=%d xfer=%d", endpoint->len, size, transfered);
|
||||
/* handle EP0 state if necessary,
|
||||
* this is a ack if length is 0 */
|
||||
/* handle EP0 state if necessary, this is a ack if length is 0 */
|
||||
if(ep == 0)
|
||||
handle_ep0_complete(endpoint->len == 0);
|
||||
endpoint->len = size;
|
||||
usb_core_transfer_complete(ep, USB_DIR_IN, 0, transfered);
|
||||
if(endpoint->wait)
|
||||
{
|
||||
endpoint->wait = false;
|
||||
semaphore_release(&endpoint->complete);
|
||||
}
|
||||
if (!out)
|
||||
endpoint->len = size;
|
||||
usb_core_transfer_complete(ep, out ? USB_DIR_OUT : USB_DIR_IN, 0, transfered);
|
||||
endpoint->done = true;
|
||||
semaphore_release(&endpoint->complete);
|
||||
}
|
||||
}
|
||||
if(sts & DIEPINT_timeout)
|
||||
if(!out && (sts & DIEPINT_timeout))
|
||||
{
|
||||
panicf("usb-drv: timeout on EP%d IN", ep);
|
||||
if(endpoint->busy)
|
||||
|
@ -522,48 +355,11 @@ static void handle_ep_in_int(int ep)
|
|||
/* for safety, act as if no bytes as been transfered */
|
||||
endpoint->len = 0;
|
||||
usb_core_transfer_complete(ep, USB_DIR_IN, 1, 0);
|
||||
if(endpoint->wait)
|
||||
{
|
||||
endpoint->wait = false;
|
||||
semaphore_release(&endpoint->complete);
|
||||
}
|
||||
endpoint->done = true;
|
||||
semaphore_release(&endpoint->complete);
|
||||
}
|
||||
}
|
||||
/* clear interrupts */
|
||||
DEPINT(ep, false) = sts;
|
||||
}
|
||||
|
||||
static void handle_ep_out_int(int ep)
|
||||
{
|
||||
struct usb_endpoint *endpoint = &endpoints[ep][DIR_OUT];
|
||||
unsigned long sts = DEPINT(ep, true);
|
||||
if(sts & DOEPINT_ahberr)
|
||||
panicf("usb-drv: ahb error on EP%d OUT", ep);
|
||||
if(sts & DOEPINT_xfercompl)
|
||||
{
|
||||
logf("usb-drv: xfer complete on EP%d OUT", ep);
|
||||
if(endpoint->busy)
|
||||
{
|
||||
endpoint->busy = false;
|
||||
endpoint->status = 0;
|
||||
/* works even for EP0 */
|
||||
int transfered = endpoint->len - (DEPTSIZ(ep, true) & DEPTSIZ_xfersize_bits);
|
||||
logf("len=%d reg=%ld xfer=%d", endpoint->len,
|
||||
(DEPTSIZ(ep, true) & DEPTSIZ_xfersize_bits),
|
||||
transfered);
|
||||
/* handle EP0 state if necessary,
|
||||
* this is a ack if length is 0 */
|
||||
if(ep == 0)
|
||||
handle_ep0_complete(endpoint->len == 0);
|
||||
usb_core_transfer_complete(ep, USB_DIR_OUT, 0, transfered);
|
||||
if(endpoint->wait)
|
||||
{
|
||||
endpoint->wait = false;
|
||||
semaphore_release(&endpoint->complete);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(sts & DOEPINT_setup)
|
||||
if(out && (sts & DOEPINT_setup))
|
||||
{
|
||||
logf("usb-drv: setup on EP%d OUT", ep);
|
||||
if(ep != 0)
|
||||
|
@ -588,24 +384,24 @@ static void handle_ep_out_int(int ep)
|
|||
usb_core_control_request(ep0_setup_pkt);
|
||||
}
|
||||
}
|
||||
|
||||
/* clear interrupts */
|
||||
DEPINT(ep, true) = sts;
|
||||
DEPINT(ep, out) = sts;
|
||||
}
|
||||
|
||||
static void handle_ep_ints(void)
|
||||
{
|
||||
logf("usb-drv: 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_in_int(ep);
|
||||
|
||||
FOR_EACH_OUT_EP_AND_EP0(i, ep)
|
||||
if(daint & DAINT_OUT_EP(ep))
|
||||
handle_ep_out_int(ep);
|
||||
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);
|
||||
}
|
||||
|
||||
/* write back to clear status */
|
||||
DAINT = daint;
|
||||
|
@ -625,8 +421,7 @@ void INT_USB(void)
|
|||
/* Clear the Remote Wakeup Signalling */
|
||||
DCTL &= ~DCTL_rmtwkupsig;
|
||||
|
||||
/* Flush FIFOs */
|
||||
flush_tx_fifos(0x10);
|
||||
flush_tx_fifos();
|
||||
|
||||
/* Flush the Learning Queue */
|
||||
GRSTCTL = GRSTCTL_intknqflsh;
|
||||
|
@ -641,15 +436,7 @@ void INT_USB(void)
|
|||
}
|
||||
|
||||
if(sts & GINTMSK_enumdone)
|
||||
{
|
||||
logf("usb-drv: enum done");
|
||||
|
||||
/* read speed */
|
||||
if(usb_drv_port_speed())
|
||||
logf("usb-drv: HS");
|
||||
else
|
||||
logf("usb-drv: FS");
|
||||
}
|
||||
logf("usb-drv: enum done: speed %cS", usb_drv_port_speed() ? 'H' : 'F');
|
||||
|
||||
if(sts & GINTMSK_otgintr)
|
||||
{
|
||||
|
@ -658,14 +445,10 @@ void INT_USB(void)
|
|||
}
|
||||
|
||||
if(sts & (GINTMSK_outepintr | GINTMSK_inepintr))
|
||||
{
|
||||
handle_ep_ints();
|
||||
}
|
||||
|
||||
if(sts & GINTMSK_disconnect)
|
||||
{
|
||||
cancel_all_transfers(true);
|
||||
}
|
||||
|
||||
GINTSTS = sts;
|
||||
}
|
||||
|
@ -701,45 +484,28 @@ static unsigned long usb_drv_mps_by_type(int type)
|
|||
|
||||
int usb_drv_request_endpoint(int type, int dir)
|
||||
{
|
||||
int ep, ret = -1;
|
||||
unsigned i;
|
||||
logf("usb-drv: request endpoint (type=%d,dir=%s)", type, dir == USB_DIR_IN ? "IN" : "OUT");
|
||||
|
||||
if(dir == USB_DIR_IN)
|
||||
FOR_EACH_IN_EP(i, ep)
|
||||
{
|
||||
if(endpoints[ep][DIR_IN].active)
|
||||
continue;
|
||||
endpoints[ep][DIR_IN].active = true;
|
||||
ret = ep | dir;
|
||||
break;
|
||||
}
|
||||
else
|
||||
FOR_EACH_OUT_EP(i, ep)
|
||||
{
|
||||
if(endpoints[ep][DIR_OUT].active)
|
||||
continue;
|
||||
endpoints[ep][DIR_OUT].active = true;
|
||||
ret = ep | dir;
|
||||
break;
|
||||
}
|
||||
|
||||
if(ret == -1)
|
||||
for (unsigned i = 1; i < sizeof((dir == USB_DIR_IN) ? in_ep_list : out_ep_list); i++)
|
||||
{
|
||||
logf("usb-drv: request failed");
|
||||
return -1;
|
||||
int ep = ((dir == USB_DIR_IN) ? in_ep_list : out_ep_list)[i];
|
||||
bool *active = &endpoints[ep][(dir == USB_DIR_IN) ? DIR_IN : DIR_OUT].active;
|
||||
if(*active)
|
||||
continue;
|
||||
*active = true;
|
||||
DEPCTL(ep, dir != USB_DIR_IN) = (DEPCTL(ep, dir != USB_DIR_IN) & ~(bitm(DEPCTL, eptype) | bitm(DEPCTL, mps)))
|
||||
| DEPCTL_setd0pid | (type << DEPCTL_eptype_bitp)
|
||||
| (usb_drv_mps_by_type(type) << DEPCTL_mps_bitp) | DEPCTL_usbactep | DEPCTL_snak;
|
||||
return ep | dir;
|
||||
}
|
||||
|
||||
DEPCTL(ep, dir != USB_DIR_IN) = (DEPCTL(ep, true) & ~(bitm(DEPCTL, eptype) | bitm(DEPCTL, mps)))
|
||||
| DEPCTL_setd0pid | (type << DEPCTL_eptype_bitp)
|
||||
| (usb_drv_mps_by_type(type) << DEPCTL_mps_bitp) | DEPCTL_usbactep | DEPCTL_snak;
|
||||
|
||||
return ret;
|
||||
logf("usb-drv: request failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
void usb_drv_release_endpoint(int ep)
|
||||
{
|
||||
logf("usb-drv: release EP%d %s", EP_NUM(ep), EP_DIR(ep) == DIR_IN ? "IN" : "OUT");
|
||||
logf("usb-drv: release EP%d %s", EP_NUM(ep), EP_DIR(ep) == USB_DIR_IN ? "IN" : "OUT");
|
||||
endpoints[EP_NUM(ep)][EP_DIR(ep)].active = false;
|
||||
}
|
||||
|
||||
|
@ -748,23 +514,18 @@ void usb_drv_cancel_all_transfers()
|
|||
cancel_all_transfers(false);
|
||||
}
|
||||
|
||||
static void usb_drv_transfer(int ep, void *ptr, int len, bool dir_in, bool blocking)
|
||||
static void usb_drv_transfer(int ep, void *ptr, int len, bool dir_in)
|
||||
{
|
||||
ep = EP_NUM(ep);
|
||||
struct usb_endpoint *endpoint = &endpoints[ep][dir_in];
|
||||
|
||||
logf("usb-drv: xfer EP%d, len=%d, dir_in=%d, blocking=%d", ep,
|
||||
len, dir_in, blocking);
|
||||
logf("usb-drv: xfer EP%d, len=%d, dir_in=%d, busy=%d", ep, len, dir_in, endpoint->busy);
|
||||
|
||||
/* disable interrupts to avoid any race */
|
||||
int oldlevel = disable_irq_save();
|
||||
|
||||
if(endpoint->busy)
|
||||
logf("usb-drv: EP%d %s is already busy", ep, dir_in ? "IN" : "OUT");
|
||||
|
||||
endpoint->busy = true;
|
||||
endpoint->len = len;
|
||||
endpoint->wait = blocking;
|
||||
endpoint->status = -1;
|
||||
|
||||
DEPCTL(ep, !dir_in) = (DEPCTL(ep, !dir_in) & ~DEPCTL_stall) | DEPCTL_usbactep;
|
||||
|
@ -772,23 +533,19 @@ static void usb_drv_transfer(int ep, void *ptr, int len, bool dir_in, bool block
|
|||
int type = (DEPCTL(ep, !dir_in) >> DEPCTL_eptype_bitp) & DEPCTL_eptype_bits;
|
||||
int mps = usb_drv_mps_by_type(type);
|
||||
int nb_packets = (len + mps - 1) / mps;
|
||||
if (nb_packets == 0)
|
||||
nb_packets = 1;
|
||||
|
||||
if(len == 0)
|
||||
{
|
||||
DEPDMA(ep, !dir_in) = (void*)0x10000000;
|
||||
DEPTSIZ(ep, !dir_in) = 1 << DEPTSIZ_pkcnt_bitp;
|
||||
}
|
||||
DEPDMA(ep, !dir_in) = len
|
||||
? (void*)AS3525_PHYSICAL_ADDR(ptr)
|
||||
: (void*)0x10000000;
|
||||
DEPTSIZ(ep, !dir_in) = (nb_packets << DEPTSIZ_pkcnt_bitp) | len;
|
||||
if(dir_in)
|
||||
clean_dcache_range(ptr, len);
|
||||
else
|
||||
{
|
||||
DEPDMA(ep, !dir_in) = (void*)AS3525_PHYSICAL_ADDR(ptr);
|
||||
DEPTSIZ(ep, !dir_in) = (nb_packets << DEPTSIZ_pkcnt_bitp) | len;
|
||||
if(dir_in)
|
||||
clean_dcache_range(ptr, len);
|
||||
else
|
||||
dump_dcache_range(ptr, len);
|
||||
}
|
||||
dump_dcache_range(ptr, len);
|
||||
|
||||
logf("pkt=%d dma=%lx", nb_packets, DEPDMA);
|
||||
logf("pkt=%d dma=%lx", nb_packets, DEPDMA(ep, !dir_in));
|
||||
|
||||
DEPCTL(ep, !dir_in) |= DEPCTL_epena | DEPCTL_cnak;
|
||||
|
||||
|
@ -797,21 +554,23 @@ static void usb_drv_transfer(int ep, void *ptr, int len, bool dir_in, bool block
|
|||
|
||||
int usb_drv_recv(int ep, void *ptr, int len)
|
||||
{
|
||||
usb_drv_transfer(ep, ptr, len, false, false);
|
||||
usb_drv_transfer(ep, ptr, len, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int usb_drv_send(int ep, void *ptr, int len)
|
||||
{
|
||||
usb_drv_transfer(ep, ptr, len, true, true);
|
||||
struct usb_endpoint *endpoint = &endpoints[ep][1];
|
||||
semaphore_wait(&endpoint->complete, TIMEOUT_BLOCK);
|
||||
endpoint->done = false;
|
||||
usb_drv_transfer(ep, ptr, len, true);
|
||||
while (endpoint->busy && !endpoint->done)
|
||||
semaphore_wait(&endpoint->complete, TIMEOUT_BLOCK);
|
||||
return endpoint->status;
|
||||
}
|
||||
|
||||
int usb_drv_send_nonblocking(int ep, void *ptr, int len)
|
||||
{
|
||||
usb_drv_transfer(ep, ptr, len, true, false);
|
||||
usb_drv_transfer(ep, ptr, len, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -155,8 +155,8 @@ static void usb_reset(void)
|
|||
|
||||
DCFG = DCFG_nzstsouthshk; /* Address 0 */
|
||||
DCTL = DCTL_pwronprgdone; /* Soft Reconnect */
|
||||
DIEPMSK = DIEPINT_timeout | DIEPINT_ahberr | DIEPINT_xfercompl;
|
||||
DOEPMSK = DIEPINT_timeout | DIEPINT_ahberr | DIEPINT_xfercompl;
|
||||
DIEPMSK = DIEPINT_timeout | DEPINT_ahberr | DEPINT_xfercompl;
|
||||
DOEPMSK = DOEPINT_setup | DEPINT_ahberr | DEPINT_xfercompl;
|
||||
DAINTMSK = 0xFFFFFFFF; /* Enable interrupts on all endpoints */
|
||||
GINTMSK = GINTMSK_outepintr | GINTMSK_inepintr | GINTMSK_usbreset | GINTMSK_enumdone;
|
||||
|
||||
|
@ -172,7 +172,7 @@ static void handle_ep_int(int out)
|
|||
if (!epints)
|
||||
continue;
|
||||
|
||||
if (epints & DIEPINT_xfercompl)
|
||||
if (epints & DEPINT_xfercompl)
|
||||
{
|
||||
invalidate_dcache();
|
||||
int bytes = endpoints[ep].size - (DEPTSIZ(ep, out) & (DEPTSIZ_xfersize_bits < DEPTSIZ_xfersize_bitp));
|
||||
|
@ -185,7 +185,7 @@ static void handle_ep_int(int out)
|
|||
semaphore_release(&endpoints[ep].complete);
|
||||
}
|
||||
}
|
||||
if (epints & DIEPINT_ahberr)
|
||||
if (epints & DEPINT_ahberr)
|
||||
panicf("USB: AHB error on EP%d (dir %d)", ep, out);
|
||||
if (epints & DIEPINT_timeout)
|
||||
{
|
||||
|
@ -200,7 +200,7 @@ static void handle_ep_int(int out)
|
|||
}
|
||||
}
|
||||
else
|
||||
{ /* SETUP phase done */
|
||||
{ /* DOEPINT_setup */
|
||||
invalidate_dcache();
|
||||
if (ep == 0)
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue