📄 usbs_upd985xx.c
字号:
// endpoint control registers, thus avoiding unnecessary code
// duplication. These will not work for the isochronous endpoints
// because those are just too special.
#define EPtx_CR_EpxEN (0x01 << 31)
#define EPtx_CR_SSx (0x01 << 18)
#define EPtx_CR_NAKx (0x01 << 16)
#define EPtx_CR_MAXPx_MASK (0x7F << 0)
#define EPtx_CR_MAXPx_SHIFT 0
#define EPrx_CR_EPxEN (0x01 << 31)
#define EPrx_CR_SSx (0x01 << 18)
#define EPrx_CR_NHSKx (0x01 << 17)
#define EPrx_CR_NAKx (0x01 << 16)
#define EPrx_CR_MAXPx_MASK (0x7F << 0)
#define EPrx_CR_MAXPx_SHIFT 0
// USB command register
#define USBS_CMR_BUSY (0x01 << 31)
#define USBS_CMR_COMMAND_MASK (0x07 << 24)
#define USBS_CMR_COMMAND_SHIFT 24
#define USBS_CMR_COMMAND_TX_EP0 (0x00 << 24)
#define USBS_CMR_COMMAND_TX_EP1 (0x01 << 24)
#define USBS_CMR_COMMAND_TX_EP3 (0x02 << 24)
#define USBS_CMR_COMMAND_TX_EP5 (0x03 << 24)
#define USBS_CMR_COMMAND_ADD_POOL0 (0x04 << 24)
#define USBS_CMR_COMMAND_ADD_POOL1 (0x05 << 24)
#define USBS_CMR_COMMAND_ADD_POOL2 (0x06 << 24)
#define USBS_CMR_SIZE_MASK (0x0FFFF << 0)
#define USBS_CMR_SIZE_SHIFT 0
// TX Endpoint status
#define USBS_TEPSR_EP5TS_MASK (0x03 << 24)
#define USBS_TEPSR_EP5TS_SHIFT 24
#define USBS_TEPSR_EP5TS_IDLE (0x00 << 24)
#define USBS_TEPSR_EP5TS_ONE (0x01 << 24)
#define USBS_TEPSR_EP5TS_TWO (0x02 << 24)
#define USBS_TEPSR_EP3TS_MASK (0x03 << 16)
#define USBS_TEPSR_EP3TS_SHIFT 16
#define USBS_TEPSR_EP3TS_IDLE (0x00 << 16)
#define USBS_TEPSR_EP3TS_ONE (0x01 << 16)
#define USBS_TEPSR_EP3TS_TWO (0x02 << 16)
#define USBS_TEPSR_EP1TS_MASK (0x03 << 8)
#define USBS_TEPSR_EP1TS_SHIFT 8
#define USBS_TEPSR_EP1TS_IDLE (0x00 << 8)
#define USBS_TEPSR_EP1TS_ONE (0x01 << 8)
#define USBS_TEPSR_EP1TS_TWO (0x02 << 8)
#define USBS_TEPSR_EP0TS_MASK (0x03 << 0)
#define USBS_TEPSR_EP0TS_SHIFT 0
#define USBS_TEPSR_EP0TS_IDLE (0x00 << 0)
#define USBS_TEPSR_EP0TS_ONE (0x01 << 0)
#define USBS_TEPSR_EP0TS_TWO (0x02 << 0)
// Receive pools. The RP0IR, RP1IR and RP2IR registers
// all use the same bits.
#define USBS_RPxIR_AL_MASK (0x07 << 28)
#define USBS_RPxIR_AL_SHIFT 28
#define USBS_RPxIR_AL_NONE (0 << 28)
#define USBS_RPxIR_RNOD_MASK (0x0FFFF << 0)
#define USBS_RPxIR_RNOD_SHIFT 0
// The other registers do not have special bits.
// Data transfers involve buffer descriptors and mailboxes. The
// relevant data structures and fields need to be defined. For now
// assume 32-bit mode of operation, i.e. there will be no padding
// between two successive 32-bit entities
// A transmit packet directory consists of up to 255 buffer
// descriptors. Each buffer descriptor specifies a buffer and a size
// of up to 64K.
typedef struct TxBufferDescriptor {
cyg_uint32 control;
void* buffer;
} TxBufferDescriptor;
#define TXBUFDESC_CTRL_LAST (0x01 << 31)
#define TXBUFDESC_CTRL_BUFDESC_MASK (0x01 << 30)
#define TXBUFDESC_CTRL_BUFDESC_SHIFT 30
#define TXBUFDESC_CTRL_BUFDESC_LINK (0x00 << 30)
#define TXBUFDESC_CTRL_BUFDESC_BUFDESC (0x01 << 30)
#define TXBUFDESC_CTRL_SIZE_MASK (0x0FFFF << 0)
#define TXBUFDESC_CTRL_SIZE_SHIFT 0
// The result of a transmit operation gets written to a mailbox
// structure in memory.
typedef struct TxMailbox {
cyg_uint32 status;
} TxMailbox;
#define TXMBOX_STATUS_IBUS_ERROR (0x01 << 10)
#define TXMBOX_STATUS_UNDERRUN (0x01 << 9)
#define TXMBOX_STATUS_MODE_MASK (0x01 << 8)
#define TXMBOX_STATUS_MODE_SHIFT 8
#define TXMBOX_STATUS_MODE_SZLP (0x00 << 8)
#define TXMBOX_STATUS_MODE_NZLP (0x01 << 8)
#define TXMBOX_STATUS_EPN_MASK (0x07 << 0)
#define TXMBOX_STATUS_EPN_SHIFT 0
#define TXMBOX_STATUS_EPN_EP0 (0x00 << 0)
#define TXMBOX_STATUS_EPN_EP1 (0x02 << 0)
#define TXMBOX_STATUS_EPN_EP3 (0x04 << 0)
#define TXMBOX_STATUS_EPN_EP5 (0x06 << 0)
// Now for receive operations. This involves adding buffer descriptors
// to one of three pools. The pools are managed by registers.
typedef struct RxBufferDescriptor {
cyg_uint32 control;
void* buffer;
} RxBufferDescriptor;
#define RXBUFDESC_CTRL_LAST (0x01 << 31)
#define RXBUFDESC_CTRL_BUFDESC_MASK (0x01 << 30)
#define RXBUFDESC_CTRL_BUFDESC_SHIFT 30
#define RXBUFDESC_CTRL_BUFDESC_LINK (0x00 << 30)
#define RXBUFDESC_CTRL_BUFDESC_BUFDESC (0x01 << 30)
#define RXBUFDESC_CTRL_SIZE_MASK (0x0FFFF << 0)
#define RXBUFDESC_CTRL_SIZE_SHIFT 0
typedef struct RxMailbox {
cyg_uint32 status;
void* address;
} RxMailbox;
#define RXMBOX_STATUS_EPN_MASK (0x07 << 29)
#define RXMBOX_STATUS_EPN_SHIFT 29
#define RXMBOX_STATUS_EPN_EP0 (0x01 << 29)
#define RXMBOX_STATUS_EPN_EP2 (0x03 << 29)
#define RXMBOX_STATUS_EPN_EP4 (0x05 << 29)
#define RXMBOX_STATUS_EPN_EP6 (0x07 << 29)
#define RXMBOX_STATUS_CORRUPTION (0x01 << 25)
#define RXMBOX_STATUS_IBUS_ERROR (0x01 << 24)
#define RXMBOX_STATUS_SETUP_MASK (0x01 << 23)
#define RXMBOX_STATUS_SETUP_SHIFT 23
#define RXMBOX_STATUS_SETUP_NORMAL (0x00 << 23)
#define RXMBOX_STATUS_SETUP_SETUP (0x01 << 23)
#define RXMBOX_STATUS_OVERRUN (0x01 << 22)
#define RXMBOX_STATUS_DATA_TOGGLE (0x01 << 21)
#define RXMBOX_STATUS_CRC (0x01 << 20)
#define RXMBOX_STATUS_BIT_STUFFING (0x01 << 19)
#define RXMBOX_STATUS_64K (0x01 << 18)
#define RXMBOX_STATUS_MODE_MASK (0x03 << 16)
#define RXMBOX_STATUS_MODE_SHIFT 16
#define RXMBOX_STATUS_MODE_NORMAL (0x00 << 16)
#define RXMBOX_STATUS_MODE_NORMAL2 (0x01 << 16)
#define RXMBOX_STATUS_MODE_ASSEMBLE (0x02 << 16)
#define RXMBOX_STATUS_MODE_SEPARATE (0x03 << 16)
#define RXMBOX_STATUS_SIZE_MASK (0x0FFFF << 0)
#define RXMBOX_STATUS_SIZE_SHIFT 0
// ----------------------------------------------------------------------------
// Hardware work around - see NEC erratum S1, CPU to IBUS write restriction.
// Reading back from the USB device after every write prevents any problems.
// Strictly speaking it is only necessary to do this after every three
// writes, but if there is concurrent ethernet activity then doing it
// after eveyr write is safer. The frame number/version register seems
// like a good one to read back from.
#ifdef CYGIMP_DEVS_USB_UPD985XX_IBUS_WRITE_LIMIT
# define FLUSH_IBUS() \
CYG_MACRO_START \
(void)*USBS_VER; \
CYG_MACRO_END
#else
# define FLUSH_IBUS() CYG_EMPTY_STATEMENT
#endif
// ----------------------------------------------------------------------------
// Static data. There is a data structure for each endpoint. The
// implementation is essentially a private class that inherits from
// common classes for control and data endpoints, but device drivers
// are supposed to be written in C so some ugliness is required.
//
// Devtab entries are defined in usbs_upd985xx_data.cxx to make sure
// that the linker does not garbage-collect them.
// Support for the interrupt handling code.
static cyg_interrupt usbs_upd985xx_intr_data;
static cyg_handle_t usbs_upd985xx_intr_handle;
// The various bits in the two interrupt status registers are read-once,
// i.e. reading the register clears the bits. Since much of the processing
// is deferred to DSR level, it is necessary to keep track of pending
// interrupts in separate variables. If another interrupt happens during
// DSR processing, these variables will be updated. The main DSR loops
// until there are no interesting bits left. Interrupts have to be
// disabled briefly when clearing bits.
static volatile cyg_uint32 usbs_upd985xx_gsr1 = 0;
static volatile cyg_uint32 usbs_upd985xx_gsr2 = 0;
// Many of the interrupt bits are of no interest and it is convenient
// to mask them out in the ISR, thus avoiding unnecessary dsr
// invocations.
static cyg_uint32 usbs_upd985xx_gsr1_mask = 0;
static cyg_uint32 usbs_upd985xx_gsr2_mask = 0;
// Sizes for the receive and transmit mboxes.
// NOTE: it is not clear what the optimal size for these
// mailboxes is. For receives maybe one per rx endpoint,
// plus a spare. For transmits maybe just two, since only
// one transmit at a time is supported. Mailboxes are
// relatively small, so for now four each should be ok.
#define RXMBOX_COUNT 4
#define TXMBOX_COUNT 4
// There is one instance of this data structure. It is allocated
// in kseg0 cached memory, but during initialization a separate
// pointer value is set to the kseg1 uncached equivalent. This
// makes it easier to point the hardware at uncached memory without
// having to worry about cache line boundaries everywhere.
typedef struct uncached_data {
// This partial cacheline does not actually store any data.
// However it ensures that the data does not share a cacheline
// with some other static, with updates to that other static
// causing funny side effects on the uncached data. There is a
// memory optimisation of subtracting sizeof(RxMailbox.status),
// i.e. exploit knowledge of alignment.
unsigned char cacheline_start[HAL_DCACHE_LINE_SIZE - sizeof(cyg_uint32)];
RxMailbox rx_mboxes[RXMBOX_COUNT];
TxMailbox tx_mboxes[TXMBOX_COUNT];
// For transmits a single buffer descriptor per endpoint suffices.
// If transmit locking is enabled then actually a single buffer
// descriptor for the whole system would suffice.
TxBufferDescriptor ep0_tx_bufdesc;
#ifdef CYGPKG_DEVS_USB_UPD985XX_EP3
TxBufferDescriptor ep3_tx_bufdesc;
#endif
#ifdef CYGPKG_DEVS_USB_UPD985XX_EP5
TxBufferDescriptor ep5_tx_bufdesc;
#endif
// More buffer descriptors are needed than might be expected, see
// the start_rx routines.
RxBufferDescriptor ep0_rx_bufdescs[4];
#ifdef CYGPKG_DEVS_USB_UPD985XX_EP4
RxBufferDescriptor ep4_rx_bufdescs[8];
#endif
#ifdef CYGPKG_DEVS_USB_UPD985XX_EP4
// Space for the start and end of a transfer, avoiding problems
// with invalidating partial cache lines.
unsigned char ep4_head[HAL_DCACHE_LINE_SIZE];
unsigned char ep4_tail[HAL_DCACHE_LINE_SIZE];
#endif
// The "big" buffers come last, reducing the offsets for the previous
// structures. It is not clear this really matters for MIPS.
//
// Endpoint 0 receive and transmit buffers. A transmit buffer is
// convenient because the hardware pretty much expects all of the
// data to be in contiguous memory, as opposed to the normal eCos
// USB driver model with refill buffers etc. An alternative
// implementation would keep the data in separate areas but would
// require lots of TxBufferDescriptors, so in memory terms the
// overheads of a single transmit buffer are not as big as might
// seem. It might be possible to get things working eight bytes
// at a time since the hardware appears to depend on zero-byte
// terminating packets in places, but that has not been attempted.
//
// A separate receive buffer is useful because it can be placed in
// uncached memory, avoiding the need for invalidation and
// worrying about other data in the cache lines. Note that this
// buffer may also get used for endpoint 6 interrupt receives
// because the two endpoints share a single pool.
unsigned char ep0_rx_buffer[CYGNUM_DEVS_USB_UPD985XX_EP0_RXBUFSIZE];
unsigned char ep0_tx_buffer[CYGNUM_DEVS_USB_UPD985XX_EP0_TXBUFSIZE];
// Another cacheline to prevent overlap with other statics.
// This has to be full-sized since the previous field is only byte-aligned.
unsigned char cacheline_end[HAL_DCACHE_LINE_SIZE];
} uncached_data;
// This data structure is quite large so making it all uninitialized
// means a potentially big saving in ROM-booting systems. This
// requires additional effort by the endpoint initialization routines.
static uncached_data cached_copy;
static uncached_data* uncached = (uncached_data*)0;
// Endpoint 0. See the description below.
static void usbs_upd985xx_ep0_start(usbs_control_endpoint*);
static void usbs_upd985xx_poll(usbs_control_endpoint*);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -