1
0
Fork 0
forked from len0rd/rockbox

usb-s3c6400: merge the 2 drivers

Player-specific code remaining: usb_drv_(init,exit)

The iPods lack a MMU, so:
    - physical, virtual, and uncached addresses are identical
    - since we can't access uncached memory we discard caches when receiving data

Still not quite reliable on nano2g

Change-Id: Iebb79df64818b9ae3b68eccb8be8975ebd6c21ea
This commit is contained in:
Rafaël Carré 2012-04-28 15:49:31 -04:00
parent c1a71ab9ae
commit c9c1349773
4 changed files with 185 additions and 398 deletions

View file

@ -239,7 +239,7 @@
#define CONFIG_USBOTG USBOTG_S3C6400X
#define USB_VENDOR_ID 0x05AC
#define USB_PRODUCT_ID 0x1261
#define USB_NUM_ENDPOINTS 5
#define USB_NUM_ENDPOINTS 6
#define USE_ROCKBOX_USB
#define USB_DEVBSS_ATTR __attribute__((aligned(16)))

View file

@ -227,7 +227,7 @@
#define CONFIG_USBOTG USBOTG_S3C6400X
#define USB_VENDOR_ID 0x05AC
#define USB_PRODUCT_ID 0x1260
#define USB_NUM_ENDPOINTS 5
#define USB_NUM_ENDPOINTS 6
#define USE_ROCKBOX_USB
#define USB_DEVBSS_ATTR __attribute__((aligned(16)))

View file

@ -57,7 +57,7 @@ void fiq_handler(void) __attribute__((interrupt ("FIQ")));
default_interrupt(INT_WATCHDOG);
default_interrupt(INT_TIMER1);
default_interrupt(INT_TIMER2);
default_interrupt(INT_USB);
default_interrupt(INT_USB_FUNC);
default_interrupt(INT_DMAC);
default_interrupt(INT_NAND);
default_interrupt(INT_IDE);
@ -134,7 +134,7 @@ static const struct { int source; void (*isr) (void); } vec_int_srcs[] =
#if (defined HAVE_MULTIDRIVE && CONFIG_CPU == AS3525)
{ INT_SRC_MCI0, INT_MCI0 },
#endif
{ INT_SRC_USB, INT_USB, },
{ INT_SRC_USB, INT_USB_FUNC, },
{ INT_SRC_TIMER1, INT_TIMER1 },
{ INT_SRC_TIMER2, INT_TIMER2 },
{ INT_SRC_I2C_AUDIO, INT_I2C_AUDIO },

View file

@ -39,6 +39,16 @@
//#define LOGF_ENABLE
#include "logf.h"
#if CONFIG_CPU == AS3525v2
#define UNCACHED_ADDR AS3525_UNCACHED_ADDR
#define PHYSICAL_ADDR AS3525_PHYSICAL_ADDR
static inline void discard_dma_buffer_cache(void) {}
#else
#define UNCACHED_ADDR
#define PHYSICAL_ADDR
static inline void discard_dma_buffer_cache(void) { commit_discard_dcache(); }
#endif
/* store per endpoint, per direction, information */
struct ep_type
{
@ -50,6 +60,46 @@ struct ep_type
bool busy; /* true is a transfer is pending */
};
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 = UNCACHED_ADDR(&_ep0_setup_pkt.header);
/* state of EP0 */
static enum ep0state ep0_state;
bool usb_drv_stalled(int endpoint, bool in)
{
return DEPCTL(endpoint, !in) & DEPCTL_stall;
@ -72,7 +122,40 @@ void usb_drv_set_address(int address)
into the USB core, which will then call this dummy function. */
}
static void ep_transfer(int ep, void *ptr, int length, bool out);
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;
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*)PHYSICAL_ADDR(ptr) : NULL;
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));
// if (!out) while (((GNPTXSTS & 0xffff) << 2) < MIN(mps, length));
DEPCTL(ep, out) |= DEPCTL_epena | DEPCTL_cnak;
restore_irq(oldlevel);
}
int usb_drv_send_nonblocking(int endpoint, void *ptr, int length)
{
ep_transfer(EP_NUM(endpoint), ptr, length, false);
@ -109,55 +192,15 @@ void usb_drv_set_test_mode(int mode)
DCTL = (DCTL & ~bitm(DCTL, tstctl)) | (mode << DCTL_tstctl_bitp);
}
#if CONFIG_CPU == AS3525v2 /* FIXME FIXME FIXME */
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)
{
usb_enable(true); // s5l only ?
/* Nothing to do */
}
static void prepare_setup_ep0(void)
{
DEPDMA(0, true) = (void*)AS3525_PHYSICAL_ADDR(&_ep0_setup_pkt);
DEPDMA(0, true) = (void*)PHYSICAL_ADDR(&_ep0_setup_pkt);
DEPTSIZ(0, true) = (1 << DEPTSIZ0_supcnt_bitp)
| (1 << DEPTSIZ0_pkcnt_bitp)
| 8;
@ -183,7 +226,7 @@ static void reset_endpoints(void)
endpoint->active = false;
endpoint->busy = false;
endpoint->status = -1;
endpoint->done = false;
endpoint->done = true;
semaphore_release(&endpoint->complete);
if (i != 0)
@ -222,6 +265,7 @@ static void cancel_all_transfers(bool cancel_ep0)
restore_irq(flags);
}
#if CONFIG_CPU == AS3525v2
void usb_drv_init(void)
{
for (int i = 0; i < USB_NUM_ENDPOINTS; i++)
@ -311,10 +355,89 @@ void usb_drv_exit(void)
CGU_USB = 0;
bitclr32(&CGU_PERI, CGU_USB_CLOCK_ENABLE);
}
#elif CONFIG_CPU == S5L8701 || CONFIG_CPU == S5L8702
static void usb_reset(void)
{
DCTL = DCTL_pwronprgdone | DCTL_sftdiscon;
OPHYPWR = 0; /* PHY: Power up */
udelay(10);
OPHYUNK1 = 1;
OPHYUNK2 = 0xE3F;
ORSTCON = 1; /* PHY: Assert Software Reset */
udelay(10);
ORSTCON = 0; /* PHY: Deassert Software Reset */
OPHYUNK3 = 0x600;
OPHYCLK = SYNOPSYSOTG_CLOCK;
udelay(400);
GRSTCTL = GRSTCTL_csftrst; /* OTG: Assert Software Reset */
while (GRSTCTL & GRSTCTL_csftrst); /* Wait for OTG to ack reset */
while (!(GRSTCTL & GRSTCTL_ahbidle)); /* Wait for OTG AHB master idle */
GRXFSIZ = 1024;
GNPTXFSIZ = (256 << 16) | 1024;
GAHBCFG = SYNOPSYSOTG_AHBCFG;
GUSBCFG = (1 << 12) | (1 << 10) | GUSBCFG_phy_if; /* OTG: 16bit PHY and some reserved bits */
DCFG = DCFG_nzstsouthshk; /* Address 0 */
DCTL = DCTL_pwronprgdone; /* Soft Reconnect */
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;
reset_endpoints();
}
void usb_drv_init(void)
{
for (unsigned i = 0; i < sizeof(endpoints)/(2*sizeof(struct ep_type)); i++)
for (unsigned dir = 0; dir < 2; dir++)
semaphore_init(&endpoints[i][dir].complete, 1, 0);
/* Enable USB clock */
#if CONFIG_CPU==S5L8701
PWRCON &= ~0x4000;
PWRCONEXT &= ~0x800;
INTMSK |= INTMSK_USB_OTG;
#elif CONFIG_CPU==S5L8702
PWRCON(0) &= ~0x4;
PWRCON(1) &= ~0x8;
VIC0INTENABLE |= 1 << 19;
#endif
PCGCCTL = 0;
/* reset the beast */
usb_reset();
}
void usb_drv_exit(void)
{
DCTL = DCTL_pwronprgdone | DCTL_sftdiscon;
OPHYPWR = 0xF; /* PHY: Power down */
udelay(10);
ORSTCON = 7; /* Put the PHY into reset (needed to get current down) */
udelay(10);
PCGCCTL = 1; /* Shut down PHY clock */
#if CONFIG_CPU==S5L8701
PWRCON |= 0x4000;
PWRCONEXT |= 0x800;
#elif CONFIG_CPU==S5L8702
PWRCON(0) |= 0x4;
PWRCON(1) |= 0x8;
#endif
}
#endif
static void handle_ep_int(int ep, bool out)
{
unsigned long sts = DEPINT(ep, out);
struct ep_type *endpoint = &endpoints[ep][out ? DIR_OUT : DIR_IN];
logf("%s(%d %s): sts = 0x%lx", __func__, ep, out?"OUT":"IN", sts);
if(sts & DEPINT_ahberr)
@ -322,7 +445,7 @@ static void handle_ep_int(int ep, bool out)
if(sts & DEPINT_xfercompl)
{
struct ep_type *endpoint = &endpoints[ep][out ? DIR_OUT : DIR_IN];
discard_dma_buffer_cache();
if(endpoint->busy)
{
endpoint->busy = false;
@ -357,11 +480,19 @@ static void handle_ep_int(int ep, bool out)
}
}
if(!out && (sts & DIEPINT_timeout))
panicf("usb-drv: timeout on EP%d IN", ep);
if(!out && (sts & DIEPINT_timeout)) {
if (endpoint->busy)
{
endpoint->busy = false;
endpoint->status = 1;
endpoint->done = true;
semaphore_release(&endpoint->complete);
}
}
if(out && (sts & DOEPINT_setup))
{
discard_dma_buffer_cache();
if(ep != 0)
panicf("usb-drv: setup not on EP0, this is impossible");
if((DEPTSIZ(ep, true) & DEPTSIZ_xfersize_bits) != 0)
@ -390,7 +521,7 @@ static void handle_ep_int(int ep, bool out)
DEPINT(ep, out) = sts;
}
void INT_USB(void)
void INT_USB_FUNC(void)
{
/* some bits in GINTSTS can be set even though we didn't enable the interrupt source
* so AND it with the actual mask */
@ -461,6 +592,8 @@ int usb_drv_request_endpoint(int type, int dir)
void usb_drv_release_endpoint(int ep)
{
if ((ep & 0x7f) == 0)
return;
endpoints[EP_NUM(ep)][EP_DIR(ep)].active = false;
}
@ -469,39 +602,6 @@ 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;
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) : NULL;
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));
// if (!out) while (((GNPTXSTS & 0xffff) << 2) < MIN(mps, length));
DEPCTL(ep, out) |= DEPCTL_epena | DEPCTL_cnak;
restore_irq(oldlevel);
}
int usb_drv_send(int ep, void *ptr, int len)
{
@ -513,316 +613,3 @@ int usb_drv_send(int ep, void *ptr, int len)
semaphore_wait(&endpoint->complete, TIMEOUT_BLOCK);
return endpoint->status;
}
#else
static struct ep_type endpoints[USB_NUM_ENDPOINTS][2];
/* 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];
} ctrlreq USB_DEVBSS_ATTR;
static volatile bool inflight = false;
static volatile bool plugged = false;
static void reset_endpoints(int reinit)
{
for (unsigned i = 0; i < sizeof(endpoints)/(2*sizeof(struct ep_type)); i++)
for (unsigned dir = 0; dir < 2; dir++)
{
if (reinit) endpoints[i][dir].active = false;
endpoints[i][dir].busy = false;
endpoints[i][dir].status = -1;
endpoints[i][dir].done = true;
semaphore_release(&endpoints[i][dir].complete);
}
DEPCTL(0, false) = DEPCTL_usbactep | (1 << DEPCTL_nextep_bitp);
DEPCTL(0, true) = DEPCTL_usbactep;
DEPTSIZ(0, true) = (1 << DEPTSIZ_pkcnt_bitp) | (1 << DEPTSIZ0_supcnt_bitp) | 64;
DEPDMA(0, true) = &ctrlreq;
DEPCTL(0, true) |= DEPCTL_epena | DEPCTL_cnak;
/* HACK: Enable all endpoints here, because we have no other chance to do it */
if (reinit)
{
/* The size is getting set to zero, because we don't know
whether we are Full Speed or High Speed at this stage */
DEPCTL(1, false) = DEPCTL_usbactep | DEPCTL_setd0pid | (3 << DEPCTL_nextep_bitp);
DEPCTL(2, true) = DEPCTL_usbactep | DEPCTL_setd0pid;
DEPCTL(3, false) = DEPCTL_usbactep | DEPCTL_setd0pid | (0 << DEPCTL_nextep_bitp);
DEPCTL(4, true) = DEPCTL_usbactep | DEPCTL_setd0pid;
}
else
{
DEPCTL(1, false) = DEPCTL(1, false) | DEPCTL_usbactep | DEPCTL_setd0pid;
DEPCTL(2, true) = DEPCTL(2, true) | DEPCTL_usbactep | DEPCTL_setd0pid;
DEPCTL(3, false) = DEPCTL(3, false) | DEPCTL_usbactep | DEPCTL_setd0pid;
DEPCTL(4, true) = DEPCTL(4, true) | DEPCTL_usbactep | DEPCTL_setd0pid;
}
DAINTMSK = 0xFFFFFFFF; /* Enable interrupts on all EPs */
inflight = false;
}
int usb_drv_request_endpoint(int type, int dir)
{
bool out = dir == USB_DIR_OUT;
for(size_t ep = out ? 2 : 1; ep < USB_NUM_ENDPOINTS; ep += 2) {
if (!endpoints[ep][out ? DIR_OUT : DIR_IN].active)
{
endpoints[ep][out ? DIR_OUT : DIR_IN].active = true;
DEPCTL(ep, out) = (DEPCTL(ep, out) & ~(DEPCTL_eptype_bits << DEPCTL_eptype_bitp)) |
(type << DEPCTL_eptype_bitp);
return ep | dir;
}
}
return -1;
}
void usb_drv_release_endpoint(int ep)
{
bool out = !(ep & USB_DIR_IN);
ep = ep & 0x7f;
if (ep < 1 || ep > USB_NUM_ENDPOINTS)
return;
endpoints[ep][out ? DIR_OUT : DIR_IN].active = false;
}
static void usb_reset(void)
{
DCTL = DCTL_pwronprgdone | DCTL_sftdiscon;
OPHYPWR = 0; /* PHY: Power up */
udelay(10);
OPHYUNK1 = 1;
OPHYUNK2 = 0xE3F;
ORSTCON = 1; /* PHY: Assert Software Reset */
udelay(10);
ORSTCON = 0; /* PHY: Deassert Software Reset */
OPHYUNK3 = 0x600;
OPHYCLK = SYNOPSYSOTG_CLOCK;
udelay(400);
GRSTCTL = GRSTCTL_csftrst; /* OTG: Assert Software Reset */
while (GRSTCTL & GRSTCTL_csftrst); /* Wait for OTG to ack reset */
while (!(GRSTCTL & GRSTCTL_ahbidle)); /* Wait for OTG AHB master idle */
GRXFSIZ = 1024;
GNPTXFSIZ = (256 << 16) | 1024;
GAHBCFG = SYNOPSYSOTG_AHBCFG;
GUSBCFG = (1 << 12) | (1 << 10) | GUSBCFG_phy_if; /* OTG: 16bit PHY and some reserved bits */
DCFG = DCFG_nzstsouthshk; /* Address 0 */
DCTL = DCTL_pwronprgdone; /* Soft Reconnect */
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;
reset_endpoints(1);
}
static void handle_ep_int(bool out)
{
static const uint8_t eps[2][3] = { /* IN */ {0, 1, 3}, /* OUT */ {0, 2, 4}};
for (int i = 0; i < 3; i++)
{
int ep = eps[!!out][i];
uint32_t epints = DEPINT(ep, out);
if (!epints)
continue;
if (epints & DEPINT_xfercompl)
{
if (!out) inflight = false;
commit_discard_dcache();
int bytes = endpoints[ep][out ? DIR_OUT : DIR_IN].size - (DEPTSIZ(ep, out) & (DEPTSIZ_xfersize_bits < DEPTSIZ_xfersize_bitp));
if (endpoints[ep][out ? DIR_OUT : DIR_IN].busy)
{
endpoints[ep][out ? DIR_OUT : DIR_IN].busy = false;
endpoints[ep][out ? DIR_OUT : DIR_IN].status = 0;
endpoints[ep][out ? DIR_OUT : DIR_IN].done = true;
usb_core_transfer_complete(ep, out ? USB_DIR_OUT : USB_DIR_IN, 0, bytes);
semaphore_release(&endpoints[ep][out ? DIR_OUT : DIR_IN].complete);
}
}
if (epints & DEPINT_ahberr)
panicf("USB: AHB error on EP%d (dir %d)", ep, out);
if (!out && (epints & DIEPINT_timeout))
{
if (endpoints[ep][out ? DIR_OUT : DIR_IN].busy)
{
endpoints[ep][out ? DIR_OUT : DIR_IN].busy = false;
endpoints[ep][out ? DIR_OUT : DIR_IN].status = 1;
endpoints[ep][out ? DIR_OUT : DIR_IN].done = true;
semaphore_release(&endpoints[ep][out ? DIR_OUT : DIR_IN].complete);
}
}
if (out && (epints & DOEPINT_setup))
{
commit_discard_dcache();
if (ep != 0)
panicf("USB: SETUP done on OUT EP%d!?", ep);
/* Set the new address here, before passing the packet to the core.
See usb_drv_set_address() for details. */
if (ctrlreq.header.bRequest == USB_REQ_SET_ADDRESS)
DCFG = (DCFG & ~(DCFG_devadr_bits << DCFG_devadr_bitp))
| (ctrlreq.header.wValue << DCFG_devadr_bitp);
usb_core_control_request(&ctrlreq.header);
}
/* Make sure EP0 OUT is set up to accept the next request */
if (out && ep == 0)
{
DEPTSIZ(0, true) = (1 << DEPTSIZ0_supcnt_bitp) | (1 << DEPTSIZ0_pkcnt_bitp) | 64;
DEPDMA(0, true) = &ctrlreq;
DEPCTL(0, true) |= DEPCTL_epena | DEPCTL_cnak;
}
DEPINT(ep, out) = epints;
}
}
/* IRQ handler */
void INT_USB_FUNC(void)
{
uint32_t ints = GINTSTS;
if (ints & GINTMSK_usbreset)
{
DCFG = DCFG_nzstsouthshk; /* Address 0 */
reset_endpoints(1);
usb_core_bus_reset();
}
if (ints & 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;
DEPCTL(1, false) = (DEPCTL(1, false) & ~(DEPCTL_mps_bits << DEPCTL_mps_bitp)) | maxpacket;
DEPCTL(2, true) = (DEPCTL(2, true) & ~(DEPCTL_mps_bits << DEPCTL_mps_bitp)) | maxpacket;
DEPCTL(3, false) = (DEPCTL(3, false) & ~(DEPCTL_mps_bits << DEPCTL_mps_bitp)) | maxpacket;
DEPCTL(4, true) = (DEPCTL(4, true) & ~(DEPCTL_mps_bits << DEPCTL_mps_bitp)) | maxpacket;
}
if (ints & GINTMSK_inepintr)
handle_ep_int(false);
if (ints & GINTMSK_outepintr)
handle_ep_int(true);
GINTSTS = ints;
}
static void ep_transfer(int ep, void *ptr, int len, bool out)
{
while (!out && inflight && plugged);
if (!plugged) return;
/* disable interrupts to avoid any race */
int oldlevel = disable_irq_save();
if (!out) inflight = true;
endpoints[ep][out ? DIR_OUT : DIR_IN].busy = true;
endpoints[ep][out ? DIR_OUT : DIR_IN].size = len;
if (out) DEPCTL(ep, out) &= ~DEPCTL_stall;
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 ? ptr : NULL;
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 endpoint, void *ptr, int length)
{
endpoint = EP_NUM(endpoint);
endpoints[endpoint][1].done = false;
ep_transfer(endpoint, ptr, length, false);
while (!endpoints[endpoint][1].done && endpoints[endpoint][1].busy)
semaphore_wait(&endpoints[endpoint][1].complete, TIMEOUT_BLOCK);
return endpoints[endpoint][1].status;
}
void usb_drv_cancel_all_transfers(void)
{
int flags = disable_irq_save();
reset_endpoints(0);
restore_irq(flags);
}
void usb_drv_init(void)
{
for (unsigned i = 0; i < sizeof(endpoints)/(2*sizeof(struct ep_type)); i++)
for (unsigned dir = 0; dir < 2; dir++)
semaphore_init(&endpoints[i][dir].complete, 1, 0);
/* Enable USB clock */
#if CONFIG_CPU==S5L8701
PWRCON &= ~0x4000;
PWRCONEXT &= ~0x800;
INTMSK |= INTMSK_USB_OTG;
#elif CONFIG_CPU==S5L8702
PWRCON(0) &= ~0x4;
PWRCON(1) &= ~0x8;
VIC0INTENABLE |= 1 << 19;
#endif
PCGCCTL = 0;
/* reset the beast */
plugged = true;
usb_reset();
}
void usb_drv_exit(void)
{
plugged = false;
DCTL = DCTL_pwronprgdone | DCTL_sftdiscon;
OPHYPWR = 0xF; /* PHY: Power down */
udelay(10);
ORSTCON = 7; /* Put the PHY into reset (needed to get current down) */
udelay(10);
PCGCCTL = 1; /* Shut down PHY clock */
#if CONFIG_CPU==S5L8701
PWRCON |= 0x4000;
PWRCONEXT |= 0x800;
#elif CONFIG_CPU==S5L8702
PWRCON(0) |= 0x4;
PWRCON(1) |= 0x8;
#endif
}
void usb_attach(void)
{
usb_enable(true);
}
#endif // CONFIG_CPU == AS3525v2 /* FIXME FIXME FIXME */