mirror of
https://github.com/Rockbox/rockbox.git
synced 2025-11-09 21:22:39 -05:00
rk27xx: implement usb driver
Change-Id: Iee3036944652fd6431d3177ab619e5df1f9bd44c
This commit is contained in:
parent
21672ba31c
commit
310f9e068d
4 changed files with 457 additions and 296 deletions
|
|
@ -834,7 +834,6 @@ Lyre prototype 1 */
|
||||||
#define USB_STATUS_BY_EVENT
|
#define USB_STATUS_BY_EVENT
|
||||||
#define USB_DETECT_BY_REQUEST
|
#define USB_DETECT_BY_REQUEST
|
||||||
#elif CONFIG_USBOTG == USBOTG_RK27XX
|
#elif CONFIG_USBOTG == USBOTG_RK27XX
|
||||||
#define USB_STATUS_BY_EVENT
|
|
||||||
#define USB_DETECT_BY_REQUEST
|
#define USB_DETECT_BY_REQUEST
|
||||||
#endif /* CONFIG_USB == */
|
#endif /* CONFIG_USB == */
|
||||||
#endif /* HAVE_USBSTACK */
|
#endif /* HAVE_USBSTACK */
|
||||||
|
|
@ -1069,7 +1068,8 @@ Lyre prototype 1 */
|
||||||
#elif (CONFIG_USBOTG == USBOTG_ARC) || \
|
#elif (CONFIG_USBOTG == USBOTG_ARC) || \
|
||||||
(CONFIG_USBOTG == USBOTG_JZ4740) || \
|
(CONFIG_USBOTG == USBOTG_JZ4740) || \
|
||||||
(CONFIG_USBOTG == USBOTG_M66591) || \
|
(CONFIG_USBOTG == USBOTG_M66591) || \
|
||||||
(CONFIG_USBOTG == USBOTG_AS3525)
|
(CONFIG_USBOTG == USBOTG_AS3525) || \
|
||||||
|
(CONFIG_USBOTG == USBOTG_RK27XX)
|
||||||
#define USB_HAS_BULK
|
#define USB_HAS_BULK
|
||||||
#define USB_HAS_INTERRUPT
|
#define USB_HAS_INTERRUPT
|
||||||
#elif defined(CPU_TCC780X) || defined(CPU_TCC77X)
|
#elif defined(CPU_TCC780X) || defined(CPU_TCC77X)
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@
|
||||||
#define FLASH_BANK1 0x11000000
|
#define FLASH_BANK1 0x11000000
|
||||||
|
|
||||||
#define USB_NUM_ENDPOINTS 16
|
#define USB_NUM_ENDPOINTS 16
|
||||||
#define USB_DEVBSS_ATTR
|
/* cache aligned */
|
||||||
|
#define USB_DEVBSS_ATTR __attribute__((aligned(CACHEALIGN_SIZE)))
|
||||||
|
|
||||||
/* Timers */
|
/* Timers */
|
||||||
#define APB0_TIMER (ARM_BUS0_BASE + 0x00000000)
|
#define APB0_TIMER (ARM_BUS0_BASE + 0x00000000)
|
||||||
|
|
@ -811,6 +812,7 @@
|
||||||
#define RXVOIDINTEN (1<<5)
|
#define RXVOIDINTEN (1<<5)
|
||||||
#define RXERRINTEN (1<<6)
|
#define RXERRINTEN (1<<6)
|
||||||
#define RXACKINTEN (1<<7)
|
#define RXACKINTEN (1<<7)
|
||||||
|
#define RXCFINTE (1<<12)
|
||||||
/* bits 31:8 reserved for EP0 */
|
/* bits 31:8 reserved for EP0 */
|
||||||
/* bits 31:14 reserved for others */
|
/* bits 31:14 reserved for others */
|
||||||
|
|
||||||
|
|
@ -833,6 +835,7 @@
|
||||||
#define TXERRINTEN (1<<5)
|
#define TXERRINTEN (1<<5)
|
||||||
#define TXACKINTEN (1<<6)
|
#define TXACKINTEN (1<<6)
|
||||||
#define TXDMADNEN (1<<7) /* reserved for EP0 */
|
#define TXDMADNEN (1<<7) /* reserved for EP0 */
|
||||||
|
#define TXCFINTE (1<<12)
|
||||||
/* bits 31:8 reserved */
|
/* bits 31:8 reserved */
|
||||||
|
|
||||||
/* TXnBUF bits */
|
/* TXnBUF bits */
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@
|
||||||
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
||||||
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
||||||
* \/ \/ \/ \/ \/
|
* \/ \/ \/ \/ \/
|
||||||
* $Id$
|
|
||||||
*
|
*
|
||||||
* Copyright (C) 2011 by Marcin Bukat
|
* Copyright (C) 2011 by Marcin Bukat
|
||||||
|
* Copyright (C) 2012 by Amaury Pouly
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or
|
* This program is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU General Public License
|
* modify it under the terms of the GNU General Public License
|
||||||
|
|
@ -28,13 +28,12 @@
|
||||||
#include "kernel.h"
|
#include "kernel.h"
|
||||||
#include "panic.h"
|
#include "panic.h"
|
||||||
|
|
||||||
//#include "usb-s3c6400x.h"
|
|
||||||
|
|
||||||
#include "usb_ch9.h"
|
#include "usb_ch9.h"
|
||||||
#include "usb_core.h"
|
#include "usb_core.h"
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include "power.h"
|
#include "power.h"
|
||||||
|
|
||||||
|
/*#define LOGF_ENABLE*/
|
||||||
#include "logf.h"
|
#include "logf.h"
|
||||||
|
|
||||||
typedef volatile uint32_t reg32;
|
typedef volatile uint32_t reg32;
|
||||||
|
|
@ -59,6 +58,16 @@ typedef volatile uint32_t reg32;
|
||||||
#define IIN_DMAINCTL(ep_num) (*(reg32*)(AHB0_UDC+0x84+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)))
|
#define IIN_DMAINLMADDR(ep_num) (*(reg32*)(AHB0_UDC+0x88+0x38*((ep_num/3)-1)))
|
||||||
|
|
||||||
|
#define USB_FULL_SPEED 0
|
||||||
|
#define USB_HIGH_SPEED 1
|
||||||
|
|
||||||
|
/* max allowed packet size definitions */
|
||||||
|
#define CTL_MAX_SIZE 64
|
||||||
|
#define BLK_HS_MAX_SIZE 512
|
||||||
|
#define BLK_FS_MAX_SIZE 64
|
||||||
|
#define INT_HS_MAX_SIZE 1024
|
||||||
|
#define INT_FS_MAX_SIZE 64
|
||||||
|
|
||||||
#ifdef LOGF_ENABLE
|
#ifdef LOGF_ENABLE
|
||||||
#define XFER_DIR_STR(dir) ((dir) ? "IN" : "OUT")
|
#define XFER_DIR_STR(dir) ((dir) ? "IN" : "OUT")
|
||||||
#define XFER_TYPE_STR(type) \
|
#define XFER_TYPE_STR(type) \
|
||||||
|
|
@ -71,6 +80,7 @@ typedef volatile uint32_t reg32;
|
||||||
struct endpoint_t {
|
struct endpoint_t {
|
||||||
const int type; /* EP type */
|
const int type; /* EP type */
|
||||||
const int dir; /* DIR_IN/DIR_OUT */
|
const int dir; /* DIR_IN/DIR_OUT */
|
||||||
|
const unsigned int intr_mask;
|
||||||
bool allocated; /* flag to mark EPs taken */
|
bool allocated; /* flag to mark EPs taken */
|
||||||
volatile void *buf; /* tx/rx buffer address */
|
volatile void *buf; /* tx/rx buffer address */
|
||||||
volatile int len; /* size of the transfer (bytes) */
|
volatile int len; /* size of the transfer (bytes) */
|
||||||
|
|
@ -80,30 +90,33 @@ struct endpoint_t {
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct endpoint_t ctrlep[2] = {
|
static struct endpoint_t ctrlep[2] = {
|
||||||
{USB_ENDPOINT_XFER_CONTROL, DIR_OUT, true, NULL, 0, 0, true, {0, 0, 0}},
|
{USB_ENDPOINT_XFER_CONTROL, DIR_OUT, 0, true, NULL, 0, 0, true, {0, 0, 0}},
|
||||||
{USB_ENDPOINT_XFER_CONTROL, DIR_IN, true, NULL, 0, 0, true, {0, 0, 0}}
|
{USB_ENDPOINT_XFER_CONTROL, DIR_IN, 0, true, NULL, 0, 0, true, {0, 0, 0}}
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct endpoint_t endpoints[16] = {
|
static struct endpoint_t endpoints[16] = {
|
||||||
{USB_ENDPOINT_XFER_CONTROL, 3, true, NULL, 0, 0, true, {0, 0, 0}}, /* stub */
|
{USB_ENDPOINT_XFER_CONTROL, 3, 0, true, NULL, 0, 0, true, {0, 0, 0}}, /* stub */
|
||||||
{USB_ENDPOINT_XFER_BULK, DIR_OUT, false, NULL, 0, 0, false, {0, 0, 0}}, /* BOUT1 */
|
{USB_ENDPOINT_XFER_BULK, DIR_OUT, BOUT1_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* BOUT1 */
|
||||||
{USB_ENDPOINT_XFER_BULK, DIR_IN, false, NULL, 0, 0, false, {0, 0, 0}}, /* BIN2 */
|
{USB_ENDPOINT_XFER_BULK, DIR_IN, BIN2_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* BIN2 */
|
||||||
{USB_ENDPOINT_XFER_INT, DIR_IN, false, NULL, 0, 0, false, {0, 0, 0}}, /* IIN3 */
|
{USB_ENDPOINT_XFER_INT, DIR_IN, IIN3_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* IIN3 */
|
||||||
{USB_ENDPOINT_XFER_BULK, DIR_OUT, false, NULL, 0, 0, false, {0, 0, 0}}, /* BOUT4 */
|
{USB_ENDPOINT_XFER_BULK, DIR_OUT, BOUT4_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* BOUT4 */
|
||||||
{USB_ENDPOINT_XFER_BULK, DIR_IN, false, NULL, 0, 0, false, {0, 0, 0}}, /* BIN5 */
|
{USB_ENDPOINT_XFER_BULK, DIR_IN, BIN5_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* BIN5 */
|
||||||
{USB_ENDPOINT_XFER_INT, DIR_IN, false, NULL, 0, 0, false, {0, 0, 0}}, /* IIN6 */
|
{USB_ENDPOINT_XFER_INT, DIR_IN, IIN6_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* IIN6 */
|
||||||
{USB_ENDPOINT_XFER_BULK, DIR_OUT, false, NULL, 0, 0, false, {0, 0, 0}}, /* BOUT7 */
|
{USB_ENDPOINT_XFER_BULK, DIR_OUT, BOUT7_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* BOUT7 */
|
||||||
{USB_ENDPOINT_XFER_BULK, DIR_IN, false, NULL, 0, 0, false, {0, 0, 0}}, /* BIN8 */
|
{USB_ENDPOINT_XFER_BULK, DIR_IN, BIN8_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* BIN8 */
|
||||||
{USB_ENDPOINT_XFER_INT, DIR_IN, false, NULL, 0, 0, false, {0, 0, 0}}, /* IIN9 */
|
{USB_ENDPOINT_XFER_INT, DIR_IN, IIN9_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* IIN9 */
|
||||||
{USB_ENDPOINT_XFER_BULK, DIR_OUT, false, NULL, 0, 0, false, {0, 0, 0}}, /* BOUT10 */
|
{USB_ENDPOINT_XFER_BULK, DIR_OUT, BOUT10_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* BOUT10 */
|
||||||
{USB_ENDPOINT_XFER_BULK, DIR_IN, false, NULL, 0, 0, false, {0, 0, 0}}, /* BIN11 */
|
{USB_ENDPOINT_XFER_BULK, DIR_IN, BIN11_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* BIN11 */
|
||||||
{USB_ENDPOINT_XFER_INT, DIR_IN, false, NULL, 0, 0, false, {0, 0, 0}}, /* IIN12 */
|
{USB_ENDPOINT_XFER_INT, DIR_IN, IIN12_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* IIN12 */
|
||||||
{USB_ENDPOINT_XFER_BULK, DIR_OUT, false, NULL, 0, 0, false, {0, 0, 0}}, /* BOUT13 */
|
{USB_ENDPOINT_XFER_BULK, DIR_OUT, BOUT13_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* BOUT13 */
|
||||||
{USB_ENDPOINT_XFER_BULK, DIR_IN, false, NULL, 0, 0, false, {0, 0, 0}}, /* BIN14 */
|
{USB_ENDPOINT_XFER_BULK, DIR_IN, BIN14_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* BIN14 */
|
||||||
{USB_ENDPOINT_XFER_INT, DIR_IN, false, NULL, 0, 0, false, {0, 0, 0}}, /* IIN15 */
|
{USB_ENDPOINT_XFER_INT, DIR_IN, IIN15_INTR, false, NULL, 0, 0, false, {0, 0, 0}}, /* IIN15 */
|
||||||
};
|
};
|
||||||
|
|
||||||
static void setup_received(void)
|
static volatile bool set_address = false;
|
||||||
|
static volatile bool set_configuration = false;
|
||||||
|
|
||||||
|
static void setup_irq_handler(void)
|
||||||
{
|
{
|
||||||
static uint32_t setup_data[2];
|
static uint32_t setup_data[2];
|
||||||
|
|
||||||
|
|
@ -111,10 +124,6 @@ static void setup_received(void)
|
||||||
setup_data[0] = SETUP1;
|
setup_data[0] = SETUP1;
|
||||||
setup_data[1] = SETUP2;
|
setup_data[1] = SETUP2;
|
||||||
|
|
||||||
/* clear all pending control transfers
|
|
||||||
* do we need this here?
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* pass setup data to the upper layer */
|
/* pass setup data to the upper layer */
|
||||||
usb_core_control_request((struct usb_ctrlrequest*)setup_data);
|
usb_core_control_request((struct usb_ctrlrequest*)setup_data);
|
||||||
}
|
}
|
||||||
|
|
@ -122,12 +131,12 @@ static void setup_received(void)
|
||||||
/* service ep0 IN transaction */
|
/* service ep0 IN transaction */
|
||||||
static void ctr_write(void)
|
static void ctr_write(void)
|
||||||
{
|
{
|
||||||
int xfer_size = (ctrlep[DIR_IN].cnt > 64) ? 64 : ctrlep[DIR_IN].cnt;
|
int xfer_size = MIN(ctrlep[DIR_IN].cnt, CTL_MAX_SIZE);
|
||||||
unsigned int timeout = current_tick + HZ/10;
|
unsigned int timeout = current_tick + HZ/10;
|
||||||
|
|
||||||
while (TX0BUF & TXFULL) /* TX0FULL flag */
|
while (TX0BUF & TXFULL) /* TX0FULL flag */
|
||||||
{
|
{
|
||||||
if(TIME_AFTER(current_tick, timeout))
|
if (TIME_AFTER(current_tick, timeout))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -143,7 +152,7 @@ static void ctr_write(void)
|
||||||
* get zero len after transfer which indicates we need to send
|
* get zero len after transfer which indicates we need to send
|
||||||
* zero length packet to signal host end of the transfer.
|
* zero length packet to signal host end of the transfer.
|
||||||
*/
|
*/
|
||||||
ctrlep[DIR_IN].cnt -= 64;
|
ctrlep[DIR_IN].cnt -= CTL_MAX_SIZE;
|
||||||
ctrlep[DIR_IN].buf += xfer_size;
|
ctrlep[DIR_IN].buf += xfer_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -161,60 +170,147 @@ static void ctr_read(void)
|
||||||
RX0DMACTLO = DMA_START; /* start DMA */
|
RX0DMACTLO = DMA_START; /* start DMA */
|
||||||
}
|
}
|
||||||
|
|
||||||
static void blk_write(int ep)
|
static void blk_write(int ep_num)
|
||||||
{
|
{
|
||||||
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;
|
|
||||||
unsigned int timeout = current_tick + HZ/10;
|
unsigned int timeout = current_tick + HZ/10;
|
||||||
|
int max = usb_drv_port_speed() ? BLK_HS_MAX_SIZE : BLK_FS_MAX_SIZE;
|
||||||
|
int xfer_size = MIN(endpoints[ep_num].cnt, max);
|
||||||
|
|
||||||
while (BIN_TXBUF(ep_num) & TXFULL) /* TXFULL flag */
|
while (BIN_TXBUF(ep_num) & TXFULL) /* TXFULL flag */
|
||||||
{
|
{
|
||||||
if(TIME_AFTER(current_tick, timeout))
|
if (TIME_AFTER(current_tick, timeout))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
BIN_TXSTAT(ep_num) = xfer_size; /* size */
|
BIN_TXSTAT(ep_num) = xfer_size; /* size */
|
||||||
BIN_DMAINLMADDR(ep_num) = (uint32_t)endpoints[ep_num].buf; /* buf address */
|
BIN_DMAINLMADDR(ep_num) = (uint32_t)endpoints[ep_num].buf; /* buf address */
|
||||||
BIN_DMAINCTL(ep_num) = DMA_START; /* start DMA */
|
BIN_DMAINCTL(ep_num) = DMA_START; /* start DMA */
|
||||||
BIN_TXCON(ep_num) &= ~TXNAK; /* clear NAK */
|
BIN_TXCON(ep_num) &= ~TXNAK; /* clear NAK */
|
||||||
|
|
||||||
/* Decrement by max packet size is intentional.
|
/* Decrement by max packet size is intentional.
|
||||||
* This way if we have final packet short one we will get negative len
|
* 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
|
* 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
|
* zero length packet. If the final packet is max sized packet we will
|
||||||
* get zero len after transfer which indicates we need to send
|
* get zero len after transfer which indicates we need to send
|
||||||
* zero length packet to signal host end of the transfer.
|
* zero length packet to signal host end of the transfer.
|
||||||
*/
|
*/
|
||||||
endpoints[ep_num].cnt -= max;
|
endpoints[ep_num].cnt -= max;
|
||||||
endpoints[ep_num].buf += xfer_size;
|
endpoints[ep_num].buf += xfer_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void blk_read(int ep)
|
static void blk_in_irq_handler(int ep_num)
|
||||||
{
|
{
|
||||||
int ep_num = EP_NUM(ep);
|
uint32_t txstat = BIN_TXSTAT(ep_num);
|
||||||
int xfer_size = BOUT_RXSTAT(ep_num) & 0xffff;
|
struct endpoint_t *endp = &endpoints[ep_num];
|
||||||
|
|
||||||
/* clear NAK bit */
|
if (txstat & TXERR)
|
||||||
BOUT_RXCON(ep_num) &= ~RXNAK;
|
{
|
||||||
|
panicf("error condition ep%d, %ld", ep_num, current_tick);
|
||||||
endpoints[ep_num].cnt -= xfer_size;
|
}
|
||||||
endpoints[ep_num].buf += xfer_size;
|
|
||||||
|
if (txstat & TXCFINT)
|
||||||
BOUT_DMAOUTLMADDR(ep_num) = (uint32_t)endpoints[ep_num].buf;
|
{
|
||||||
BOUT_DMAOUTCTL(ep_num) = DMA_START;
|
logf("blk_write: cf(0x%x), %ld", ep_num, current_tick);
|
||||||
|
/* bit cleared by read */
|
||||||
|
usb_drv_stall(ep_num, false, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (txstat & TXACK) /* check TxACK flag */
|
||||||
|
{
|
||||||
|
if (endp->cnt > 0)
|
||||||
|
{
|
||||||
|
logf("blk_write: ack(0x%x), %ld", ep_num, current_tick);
|
||||||
|
/* we still have data to send (or ZLP) */
|
||||||
|
blk_write(ep_num);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logf("udc_intr: usb_core_transfer_complete(0x%x, USB_DIR_IN, 0, 0x%x), %ld",
|
||||||
|
ep_num, endp->len, current_tick);
|
||||||
|
|
||||||
|
/* final ack received */
|
||||||
|
usb_core_transfer_complete(ep_num, /* ep */
|
||||||
|
USB_DIR_IN, /* dir */
|
||||||
|
0, /* status */
|
||||||
|
endp->len); /* length */
|
||||||
|
|
||||||
|
/* release semaphore for blocking transfer */
|
||||||
|
if (endp->block)
|
||||||
|
{
|
||||||
|
logf("udc_intr: ep=0x%x, semaphore_release(), %ld",
|
||||||
|
ep_num, current_tick);
|
||||||
|
|
||||||
|
semaphore_release(&endp->complete);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void blk_out_irq_handler(int ep_num)
|
||||||
|
{
|
||||||
|
uint32_t rxstat = BOUT_RXSTAT(ep_num);
|
||||||
|
int xfer_size = rxstat & 0xffff;
|
||||||
|
int max = usb_drv_port_speed() ? BLK_HS_MAX_SIZE : BLK_FS_MAX_SIZE;
|
||||||
|
struct endpoint_t *endp = &endpoints[ep_num];
|
||||||
|
|
||||||
|
if (rxstat & RXERR)
|
||||||
|
{
|
||||||
|
panicf("error condition ep%d, %ld", ep_num, current_tick);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rxstat & RXOVF)
|
||||||
|
{
|
||||||
|
panicf("rxovf ep%d, %ld", ep_num, current_tick);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rxstat & RXCFINT)
|
||||||
|
{
|
||||||
|
logf("blk_read:cf(0x%x), %ld", ep_num, current_tick);
|
||||||
|
usb_drv_stall(ep_num, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((rxstat & RXACK) && endp->cnt > 0)
|
||||||
|
{
|
||||||
|
logf("blk_read:ack(0x%x,0x%x), %ld", ep_num, xfer_size, current_tick);
|
||||||
|
|
||||||
|
/* clear NAK bit */
|
||||||
|
BOUT_RXCON(ep_num) &= ~RXNAK;
|
||||||
|
|
||||||
|
endp->cnt -= xfer_size;
|
||||||
|
endp->buf += xfer_size;
|
||||||
|
|
||||||
|
/* if the transfer was short or if the total count is zero,
|
||||||
|
* transfer is complete
|
||||||
|
*/
|
||||||
|
if (endp->cnt == 0 || xfer_size < max)
|
||||||
|
{
|
||||||
|
logf("udc_intr: usb_core_transfer_complete(0x%x, USB_DIR_OUT, 0, 0x%x), %ld",
|
||||||
|
ep_num, endp->len, current_tick);
|
||||||
|
|
||||||
|
usb_core_transfer_complete(ep_num, /* ep */
|
||||||
|
USB_DIR_OUT, /* dir */
|
||||||
|
0, /* status */
|
||||||
|
endp->len - endp->cnt); /* length */
|
||||||
|
endp->cnt = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
BOUT_DMAOUTLMADDR(ep_num) = (uint32_t)endp->buf;
|
||||||
|
BOUT_DMAOUTCTL(ep_num) = DMA_START;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void int_write(int ep)
|
static void int_write(int ep)
|
||||||
{
|
{
|
||||||
int ep_num = EP_NUM(ep);
|
int ep_num = EP_NUM(ep);
|
||||||
int max = usb_drv_port_speed() ? 1024 : 64;
|
int max = usb_drv_port_speed() ? INT_HS_MAX_SIZE : INT_FS_MAX_SIZE;
|
||||||
int xfer_size = (endpoints[ep_num].cnt > max) ? max : endpoints[ep_num].cnt;
|
int xfer_size = MIN(endpoints[ep_num].cnt, max);
|
||||||
unsigned int timeout = current_tick + HZ/10;
|
unsigned int timeout = current_tick + HZ/10;
|
||||||
|
|
||||||
while (IIN_TXBUF(ep_num) & TXFULL) /* TXFULL flag */
|
while (IIN_TXBUF(ep_num) & TXFULL)
|
||||||
{
|
{
|
||||||
if(TIME_AFTER(current_tick, timeout))
|
if (TIME_AFTER(current_tick, timeout))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -234,167 +330,99 @@ static void int_write(int ep)
|
||||||
endpoints[ep_num].buf += xfer_size;
|
endpoints[ep_num].buf += xfer_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* UDC ISR function */
|
static void int_in_irq_handler(int ep_num)
|
||||||
void INT_UDC(void)
|
|
||||||
{
|
{
|
||||||
uint32_t txstat, rxstat;
|
uint32_t txstat = IIN_TXSTAT(ep_num);
|
||||||
int tmp, ep_num;
|
struct endpoint_t *endp = &endpoints[ep_num];
|
||||||
|
|
||||||
/* read what caused UDC irq */
|
|
||||||
uint32_t intsrc = INT2FLAG & 0x7fffff;
|
|
||||||
|
|
||||||
if (intsrc & SETUP_INTR) /* setup interrupt */
|
|
||||||
{
|
|
||||||
setup_received();
|
|
||||||
}
|
|
||||||
else if (intsrc & IN0_INTR) /* ep0 in interrupt */
|
|
||||||
{
|
|
||||||
txstat = TX0STAT; /* read clears flags */
|
|
||||||
|
|
||||||
/* TODO handle errors */
|
|
||||||
if (txstat & TXACK) /* check TxACK flag */
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (intsrc & OUT0_INTR) /* ep0 out interrupt */
|
|
||||||
{
|
|
||||||
rxstat = RX0STAT;
|
|
||||||
|
|
||||||
/* TODO handle errors */
|
if (txstat & TXCFINT)
|
||||||
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();
|
logf("int_write: cf(0x%x), %ld", ep_num, current_tick);
|
||||||
|
/* bit cleared by read */
|
||||||
|
usb_drv_stall(ep_num, false, true);
|
||||||
}
|
}
|
||||||
else if (intsrc & RESUME_INTR) /* usb resume */
|
|
||||||
{
|
|
||||||
TX0CON |= TXCLR; /* TxClr */
|
|
||||||
TX0CON &= ~TXCLR;
|
|
||||||
RX0CON |= RXCLR; /* RxClr */
|
|
||||||
RX0CON &= ~RXCLR;
|
|
||||||
}
|
|
||||||
else if (intsrc & SUSP_INTR) /* usb suspend */
|
|
||||||
{
|
|
||||||
}
|
|
||||||
else if (intsrc & CONN_INTR) /* usb connect */
|
|
||||||
{
|
|
||||||
}
|
|
||||||
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)))
|
|
||||||
{
|
|
||||||
/* 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (intsrc & ((1<<10)|(1<13)|(1<<16)|(1<<19)|(1<<22)))
|
|
||||||
{
|
|
||||||
/* int in */
|
|
||||||
txstat = IIN_TXSTAT(ep_num);
|
|
||||||
|
|
||||||
/* TODO handle errors */
|
if (txstat & TXERR)
|
||||||
if (txstat & TXACK) /* check TxACK flag */
|
{
|
||||||
|
panicf("error condition ep%d, %ld", ep_num, current_tick);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (txstat & TXACK) /* check TxACK flag */
|
||||||
|
{
|
||||||
|
if (endp->cnt > 0)
|
||||||
|
{
|
||||||
|
logf("int_write: ack(0x%x), %ld", ep_num, current_tick);
|
||||||
|
/* we still have data to send (or ZLP) */
|
||||||
|
int_write(ep_num);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logf("udc_intr: usb_core_transfer_complete(0x%x, USB_DIR_IN, 0, 0x%x), %ld",
|
||||||
|
ep_num, endp->len, current_tick);
|
||||||
|
|
||||||
|
/* final ack received */
|
||||||
|
usb_core_transfer_complete(ep_num, /* ep */
|
||||||
|
USB_DIR_IN, /* dir */
|
||||||
|
0, /* status */
|
||||||
|
endp->len); /* length */
|
||||||
|
|
||||||
|
/* release semaphore for blocking transfer */
|
||||||
|
if (endp->block)
|
||||||
{
|
{
|
||||||
if (endpoints[ep_num].cnt >= 0)
|
logf("udc_intr: ep=0x%x, semaphore_release(), %ld",
|
||||||
{
|
ep_num, current_tick);
|
||||||
/* we still have data to send (or ZLP) */
|
|
||||||
int_write(ep_num);
|
semaphore_release(&endp->complete);
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* return port speed FS=0, HS=1 */
|
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 == false)
|
||||||
|
if ((dev_info & 0x7f))
|
||||||
|
{
|
||||||
|
set_address = true;
|
||||||
|
usb_core_notify_set_address(dev_info & 0x7f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* SET CONFIGURATION request */
|
||||||
|
if (set_configuration == false)
|
||||||
|
if (dev_info & DEV_EN)
|
||||||
|
{
|
||||||
|
set_configuration = true;
|
||||||
|
usb_core_notify_set_config(((dev_info >> 7) & 0xf) + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* return port speed */
|
||||||
int usb_drv_port_speed(void)
|
int usb_drv_port_speed(void)
|
||||||
{
|
{
|
||||||
return ((DEV_INFO & DEV_SPEED) == 0) ? 0 : 1;
|
return ((DEV_INFO & DEV_SPEED) ? USB_FULL_SPEED : USB_HIGH_SPEED);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reserve endpoint */
|
/* Reserve endpoint */
|
||||||
|
|
@ -410,7 +438,7 @@ int usb_drv_request_endpoint(int type, int dir)
|
||||||
logf("req: %s %s", XFER_DIR_STR(ep_dir), XFER_TYPE_STR(ep_type));
|
logf("req: %s %s", XFER_DIR_STR(ep_dir), XFER_TYPE_STR(ep_type));
|
||||||
|
|
||||||
/* Find an available ep/dir pair */
|
/* Find an available ep/dir pair */
|
||||||
for (ep_num=1;ep_num<USB_NUM_ENDPOINTS;ep_num++)
|
for (ep_num = 1; ep_num < USB_NUM_ENDPOINTS; ep_num++)
|
||||||
{
|
{
|
||||||
struct endpoint_t* endpoint = &endpoints[ep_num];
|
struct endpoint_t* endpoint = &endpoints[ep_num];
|
||||||
|
|
||||||
|
|
@ -420,7 +448,35 @@ int usb_drv_request_endpoint(int type, int dir)
|
||||||
{
|
{
|
||||||
/* mark endpoint as taken */
|
/* mark endpoint as taken */
|
||||||
endpoint->allocated = true;
|
endpoint->allocated = true;
|
||||||
|
|
||||||
|
if (ep_num%3 == 0) /* IIN 3, 6, 9, 12, 15 */
|
||||||
|
{
|
||||||
|
IIN_TXCON(ep_num) = (ep_num<<8) | /* set ep number */
|
||||||
|
TXERRINTEN |
|
||||||
|
TXEPEN | /* endpoint enable */
|
||||||
|
TXNAK | /* respond with NAK */
|
||||||
|
TXACKINTEN | /* irq on ACK */
|
||||||
|
TXCFINTE; /* irq on Clear feature */
|
||||||
|
}
|
||||||
|
else if (ep_num%3 == 1) /* BOUT 1, 4, 7, 10, 13 */
|
||||||
|
{
|
||||||
|
BOUT_RXCON(ep_num) = (ep_num<<8) | /* set ep number */
|
||||||
|
RXERRINTEN |
|
||||||
|
RXEPEN | /* endpoint enable */
|
||||||
|
RXNAK | /* respond with NAK */
|
||||||
|
RXACKINTEN | /* irq on ACK */
|
||||||
|
RXCFINTE; /* irq on Clear feature */
|
||||||
|
}
|
||||||
|
else if (ep_num%3 == 2) /* BIN 2, 5, 8, 11, 14 */
|
||||||
|
{
|
||||||
|
BIN_TXCON(ep_num) = (ep_num<<8) | /* set ep number */
|
||||||
|
TXERRINTEN |
|
||||||
|
TXEPEN | /* endpoint enable */
|
||||||
|
TXNAK | /* respond with NAK */
|
||||||
|
TXACKINTEN | /* irq on ACK */
|
||||||
|
TXCFINTE; /* irq on Clear feature */
|
||||||
|
}
|
||||||
|
|
||||||
/* enable interrupt from this endpoint */
|
/* enable interrupt from this endpoint */
|
||||||
EN_INT |= (1<<(ep_num+7));
|
EN_INT |= (1<<(ep_num+7));
|
||||||
|
|
||||||
|
|
@ -435,10 +491,7 @@ int usb_drv_request_endpoint(int type, int dir)
|
||||||
void usb_drv_release_endpoint(int ep)
|
void usb_drv_release_endpoint(int ep)
|
||||||
{
|
{
|
||||||
int ep_num = EP_NUM(ep);
|
int ep_num = EP_NUM(ep);
|
||||||
int ep_dir = EP_DIR(ep);
|
logf("rel: ep%d %s", ep_num, XFER_DIR_STR(EP_DIR(ep)));
|
||||||
(void) ep_dir;
|
|
||||||
|
|
||||||
logf("rel: ep%d %s", ep_num, XFER_DIR_STR(ep_dir));
|
|
||||||
endpoints[ep_num].allocated = false;
|
endpoints[ep_num].allocated = false;
|
||||||
|
|
||||||
/* disable interrupt from this endpoint */
|
/* disable interrupt from this endpoint */
|
||||||
|
|
@ -454,14 +507,20 @@ void usb_drv_release_endpoint(int ep)
|
||||||
void usb_drv_set_address(int address)
|
void usb_drv_set_address(int address)
|
||||||
{
|
{
|
||||||
(void)address;
|
(void)address;
|
||||||
/* UDC seems to set this automaticaly */
|
/* UDC sets this automaticaly */
|
||||||
}
|
}
|
||||||
|
|
||||||
static int _usb_drv_send(int endpoint, void *ptr, int length, bool block)
|
static int _usb_drv_send(int endpoint, void *ptr, int length, bool block)
|
||||||
{
|
{
|
||||||
struct endpoint_t *ep;
|
struct endpoint_t *ep;
|
||||||
int ep_num = EP_NUM(endpoint);
|
int ep_num = EP_NUM(endpoint);
|
||||||
|
|
||||||
|
/* for send transfers, make sure the data is committed */
|
||||||
|
commit_discard_dcache_range(ptr, length);
|
||||||
|
|
||||||
|
logf("_usb_drv_send: endpt=0x%x, len=0x%x, block=%d",
|
||||||
|
endpoint, length, block);
|
||||||
|
|
||||||
if (ep_num == 0)
|
if (ep_num == 0)
|
||||||
ep = &ctrlep[DIR_IN];
|
ep = &ctrlep[DIR_IN];
|
||||||
else
|
else
|
||||||
|
|
@ -477,7 +536,7 @@ static int _usb_drv_send(int endpoint, void *ptr, int length, bool block)
|
||||||
|
|
||||||
switch (ep->type)
|
switch (ep->type)
|
||||||
{
|
{
|
||||||
case USB_ENDPOINT_XFER_CONTROL:
|
case USB_ENDPOINT_XFER_CONTROL:
|
||||||
ctr_write();
|
ctr_write();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
@ -515,30 +574,40 @@ int usb_drv_recv(int endpoint, void* ptr, int length)
|
||||||
struct endpoint_t *ep;
|
struct endpoint_t *ep;
|
||||||
int ep_num = EP_NUM(endpoint);
|
int ep_num = EP_NUM(endpoint);
|
||||||
|
|
||||||
|
logf("usb_drv_recv: endpt=0x%x, len=0x%x, %ld",
|
||||||
|
endpoint, length, current_tick);
|
||||||
|
|
||||||
|
/* for recv, discard the cache lines related to the buffer */
|
||||||
|
commit_discard_dcache_range(ptr, length);
|
||||||
|
|
||||||
if (ep_num == 0)
|
if (ep_num == 0)
|
||||||
{
|
{
|
||||||
ep = &ctrlep[DIR_OUT];
|
ep = &ctrlep[DIR_OUT];
|
||||||
|
ep->buf = ptr;
|
||||||
ctr_read();
|
ep->len = ep->cnt = length;
|
||||||
|
|
||||||
|
/* clear NAK bit */
|
||||||
|
RX0CON &= ~RXNAK;
|
||||||
|
RX0DMAOUTLMADDR = (uint32_t)ptr; /* buffer address */
|
||||||
|
RX0DMACTLO = DMA_START; /* start DMA */
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ep = &endpoints[ep_num];
|
ep = &endpoints[ep_num];
|
||||||
|
ep->buf = ptr;
|
||||||
|
ep->len = ep->cnt = length;
|
||||||
|
|
||||||
/* clear NAK bit */
|
/* clear NAK bit */
|
||||||
BOUT_RXCON(ep_num) &= ~RXNAK;
|
BOUT_RXCON(ep_num) &= ~RXNAK;
|
||||||
BOUT_DMAOUTLMADDR(ep_num) = (uint32_t)ptr;
|
BOUT_DMAOUTLMADDR(ep_num) = (uint32_t)ptr;
|
||||||
BOUT_DMAOUTCTL(ep_num) = DMA_START;
|
BOUT_DMAOUTCTL(ep_num) = DMA_START;
|
||||||
}
|
}
|
||||||
|
|
||||||
ep->buf = ptr;
|
|
||||||
ep->len = ep->cnt = length;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Kill all transfers. Usually you need to set a bit for each endpoint
|
/* Kill all transfers. Usually you need to set a bit for each endpoint
|
||||||
* and flush fifos. You should also call the completion handler with
|
* and flush fifos. You should also call the completion handler with
|
||||||
* error status for everything
|
* error status for everything
|
||||||
*/
|
*/
|
||||||
void usb_drv_cancel_all_transfers(void)
|
void usb_drv_cancel_all_transfers(void)
|
||||||
|
|
@ -592,6 +661,8 @@ bool usb_drv_stalled(int endpoint, bool in)
|
||||||
void usb_drv_stall(int endpoint, bool stall, bool in)
|
void usb_drv_stall(int endpoint, bool stall, bool in)
|
||||||
{
|
{
|
||||||
int ep_num = EP_NUM(endpoint);
|
int ep_num = EP_NUM(endpoint);
|
||||||
|
logf("usb_drv: %sstall EP%d %s",
|
||||||
|
stall ? "": "un", ep_num, in ? "IN" : "OUT");
|
||||||
|
|
||||||
switch (endpoints[ep_num].type)
|
switch (endpoints[ep_num].type)
|
||||||
{
|
{
|
||||||
|
|
@ -645,87 +716,23 @@ void usb_drv_stall(int endpoint, bool stall, bool in)
|
||||||
void usb_drv_init(void)
|
void usb_drv_init(void)
|
||||||
{
|
{
|
||||||
int ep_num;
|
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 */
|
/* init semaphore of ep0 */
|
||||||
semaphore_init(&ctrlep[DIR_OUT].complete, 1, 0);
|
semaphore_init(&ctrlep[DIR_OUT].complete, 1, 0);
|
||||||
semaphore_init(&ctrlep[DIR_IN].complete, 1, 0);
|
semaphore_init(&ctrlep[DIR_IN].complete, 1, 0);
|
||||||
|
|
||||||
|
/* init semaphores for other endpoints */
|
||||||
for (ep_num = 1; ep_num < USB_NUM_ENDPOINTS; ep_num++)
|
for (ep_num = 1; ep_num < USB_NUM_ENDPOINTS; ep_num++)
|
||||||
{
|
|
||||||
semaphore_init(&endpoints[ep_num].complete, 1, 0);
|
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 */
|
/* turn off usb core */
|
||||||
void usb_drv_exit(void)
|
void usb_drv_exit(void)
|
||||||
{
|
{
|
||||||
DEV_CTL = DEV_SELF_PWR;
|
/* udc module reset */
|
||||||
|
SCU_RSTCFG |= (1<<1);
|
||||||
/* disable USB interrupts in interrupt controller */
|
udelay(10);
|
||||||
INTC_IMR &= ~IRQ_ARM_UDC;
|
SCU_RSTCFG &= ~(1<<1);
|
||||||
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
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int usb_detect(void)
|
int usb_detect(void)
|
||||||
|
|
@ -736,3 +743,140 @@ int usb_detect(void)
|
||||||
return USB_EXTRACTED;
|
return USB_EXTRACTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* UDC ISR function */
|
||||||
|
void INT_UDC(void)
|
||||||
|
{
|
||||||
|
uint32_t txstat, rxstat;
|
||||||
|
int ep_num;
|
||||||
|
|
||||||
|
/* 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_irq_handler();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intsrc & IN0_INTR) /* ep0 in interrupt */
|
||||||
|
{
|
||||||
|
txstat = TX0STAT; /* read clears flags */
|
||||||
|
|
||||||
|
/* TODO handle errors */
|
||||||
|
if (txstat & TXACK) /* check TxACK flag */
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* endpoints other then EP0 */
|
||||||
|
if (intsrc & 0x7fff00)
|
||||||
|
{
|
||||||
|
/* lets figure out which ep generated irq */
|
||||||
|
for (ep_num = 1; ep_num < USB_NUM_ENDPOINTS; ep_num++)
|
||||||
|
{
|
||||||
|
struct endpoint_t *ep = &endpoints[ep_num];
|
||||||
|
|
||||||
|
if ((intsrc & ep->intr_mask) && ep->allocated)
|
||||||
|
{
|
||||||
|
switch (ep->type)
|
||||||
|
{
|
||||||
|
case USB_ENDPOINT_XFER_BULK:
|
||||||
|
if (ep->dir == DIR_OUT)
|
||||||
|
blk_out_irq_handler(ep_num);
|
||||||
|
else
|
||||||
|
blk_in_irq_handler(ep_num);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USB_ENDPOINT_XFER_INT:
|
||||||
|
int_in_irq_handler(ep_num);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,20 @@ int usb_status = USB_EXTRACTED;
|
||||||
|
|
||||||
void usb_init_device(void)
|
void usb_init_device(void)
|
||||||
{
|
{
|
||||||
|
/* enable UDC interrupt */
|
||||||
|
INTC_IMR |= (1<<16);
|
||||||
|
INTC_IECR |= (1<<16);
|
||||||
|
|
||||||
|
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 */
|
||||||
|
|
||||||
|
/* configure INTCON */
|
||||||
|
INTCON = UDC_INTHIGH_ACT | /* interrupt high active */
|
||||||
|
UDC_INTEN; /* enable EP0 interrupts */
|
||||||
}
|
}
|
||||||
|
|
||||||
void usb_attach(void)
|
void usb_attach(void)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue