📄 usbs_sa11x0.c
字号:
//==========================================================================
//
// usbs_sa11x0.c
//
// Device driver for the SA11x0 USB port.
//
//==========================================================================
//####COPYRIGHTBEGIN####
//
// -------------------------------------------
// The contents of this file are subject to the Red Hat eCos Public License
// Version 1.1 (the "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of the License at
// http://www.redhat.com/
//
// Software distributed under the License is distributed on an "AS IS"
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
// License for the specific language governing rights and limitations under
// the License.
//
// The Original Code is eCos - Embedded Configurable Operating System,
// released September 30, 1998.
//
// The Initial Developer of the Original Code is Red Hat.
// Portions created by Red Hat are
// Copyright (C) 2000, 2001 Red Hat, Inc.
// All Rights Reserved.
// -------------------------------------------
//
//####COPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): bartv
// Contributors: bartv
// Date: 2000-10-04
//
// This code implements support for the on-chip USB port on the SA11x0
// family of processors. The code has been developed on the SA1110 and
// may or may not work on other members of the SA11x0 family. There
// have problems with the USB support on certain revisions of the silicon,
// so the errata sheet appropriate to the specific processor being used
// should be consulted. There also appear to be problems which do not
// appear on any errata, which this code attempts to work around.
//
//####DESCRIPTIONEND####
//==========================================================================
#include <cyg/infra/cyg_type.h>
#include <cyg/infra/cyg_ass.h>
#include <cyg/infra/cyg_trac.h>
#include <cyg/infra/diag.h>
#include <pkgconf/hal_arm.h>
#include <pkgconf/devs_usb_sa11x0.h>
#include <cyg/hal/drv_api.h>
#include <cyg/hal/hal_arch.h>
#include <cyg/hal/hal_io.h>
#include <cyg/hal/hal_cache.h>
#include <cyg/hal/hal_sa11x0.h>
#include <cyg/error/codes.h>
#include <cyg/io/usb/usb.h>
#include <cyg/io/usb/usbs.h>
// Debugging support. By default this driver operates mostly at
// DSR level, with the ISR doing a minimal amount of processing.
// However is also possible to run most of the code at thread-level,
// This is subject to some restrictions because the USB standard
// imposes timing constraints, e.g. some control operations such
// as SET-ADDRESS have to complete within 50ms. However it is
// very useful for debugging, specifically it allows you to put
// printf()'s in various places.
//
// Right now these configuration options are not exported to the
// user because running at DSR level is likely to be good enough
// for everybody not actively debugging this code. The options
// could be exported if necessary.
//#define CYGPKG_DEVS_USB_SA11X0_THREAD
#undef CYGPKG_DEVS_USB_SA11X0_THREAD
#ifdef CYGPKG_DEVS_USB_SA11X0_THREAD
// Default stack size should be CYGNUM_HAL_STACK_SIZE_TYPICAL
# define CYGNUM_DEVS_USB_SA11X0_THREAD_STACK_SIZE 4096
# define CYGNUM_DEVS_USB_SA11X0_THREAD_PRIORITY 7
# include <cyg/kernel/kapi.h>
#endif
#if 0
# define DBG(a) diag_printf a
#else
# define DBG(a)
#endif
#undef FAILURES
#ifdef FAILURES
static volatile int ep1_failure = 7;
#endif
#undef STATS
#ifdef STATS
int ep1_receives = 0;
int ep1_errors = 0;
int ep2_transmits = 0;
int ep2_errors = 0;
# define INCR_STAT(a) (a) += 1
# define SET_STAT(a, b) (a) = (b)
#else
# define INCR_STAT(a)
# define SET_STAT(a, b)
#endif
// ----------------------------------------------------------------------------
// Serial port 0 on the SA11x0 provides a USB slave connection (aka a
// USB device controller or UDC). The functionality is somewhat
// limited, there are just three endpoints.
//
// Endpoint 0 can only be used for control messages. It has an 8 byte
// fifo which cannot be connected to a DMA engine. Hence incoming
// control packets have to be limited to 8 bytes by the enumeration
// data. The endpoint has to be managed at a low-level, i.e. the
// incoming request has to be extracted from the fifo, processed, and
// any response put back into the fifo within the permitted USB
// response times.
//
// Endpoint 1 can only be used for host->slave bulk OUT transfers. It
// has a 20 byte receive fifo, and it can be hooked up to any of the
// six DMA engines. Since bulk transfers will typically involve 64
// byte packets, most applications will require the use of DMA.
//
// Endpoint 2 can only be used for slave-host bulk IN transfers. There
// is a 16 byte transmit fifo so small messages can be transferred in
// software. The fifo can also be hooked up to DMA, which is a more
// likely scenario.
//
// Start with definitions of the hardware. The use of a structure and
// a const base pointer should allow the compiler to do base/offset
// addressing and keep the hardware base address in a register. This
// is better than defining each hardware register via a separate
// address. Although the registers are only a byte wide, the peripheral
// bus only supports word accesses.
//
// The USBS_CONTROL etc. macros allow for an alternative way of
// accessing the hardware if a better approach is presented, without
// having to rewrite all the code. Macros that correspond to registers
// are actually addresses, making it easier in the code to distinguish
// them from bit values: the & and * operators will just cancel out.
typedef struct usbs_sa11x0_hardware {
volatile int control;
volatile int address;
volatile int out_size;
volatile int in_size;
volatile int ep0_control;
volatile int ep1_control;
volatile int ep2_control;
volatile int ep0_data;
volatile int ep0_write_count;
int dummy1;
volatile int fifo;
int dummy2;
volatile int status;
} usbs_sa11x0_hardware;
static usbs_sa11x0_hardware* const usbs_sa11x0_base = (usbs_sa11x0_hardware* const) 0x80000000;
#define USBS_CONTROL (&(usbs_sa11x0_base->control))
#define USBS_ADDRESS (&(usbs_sa11x0_base->address))
#define USBS_OUT_SIZE (&(usbs_sa11x0_base->out_size))
#define USBS_IN_SIZE (&(usbs_sa11x0_base->in_size))
#define EP0_CONTROL (&(usbs_sa11x0_base->ep0_control))
#define EP1_CONTROL (&(usbs_sa11x0_base->ep1_control))
#define EP2_CONTROL (&(usbs_sa11x0_base->ep2_control))
#define EP0_DATA (&(usbs_sa11x0_base->ep0_data))
#define EP0_WRITE_COUNT (&(usbs_sa11x0_base->ep0_write_count))
#define EP1_DATA (&(usbs_sa11x0_base->fifo))
#define EP2_DATA (&(usbs_sa11x0_base->fifo))
#define USBS_STATUS (&(usbs_sa11x0_base->status))
#define CONTROL_DISABLE (1 << 0)
#define CONTROL_ACTIVE (1 << 1)
// The meaning of bit 2 changed, see errata
#define CONTROL_RESUME_INTR (1 << 2)
#define CONTROL_EP0_INTR (1 << 3)
#define CONTROL_EP1_INTR (1 << 4)
#define CONTROL_EP2_INTR (1 << 5)
// The meaning of bit 6 also changed, see errata
#define CONTROL_SUSPEND_INTR (1 << 6)
#define CONTROL_RESET_INTR (1 << 7)
// Getting the control register settings right is a little bit tricky.
// Bit 0 is the disable bit so touching that is dangerous, and the
// other bits have inverted meanings i.e. 0 enables interrupts. The
// following macro encapsulates this.
#define CONTROL_ALL_INTR 0x00FC
#define CONTROL_INTR_ENABLE(bits) ((~(bits)) & CONTROL_ALL_INTR)
#define CONTROL_INTR_CLEAR(bits) ((bits) & CONTROL_ALL_INTR)
// All the endpoint interrupt numbers can be handled en masse,
// but some of the endpoints may be disabled.
#if defined(CYGPKG_DEVS_USB_SA11X0_EP1) && defined(CYGPKG_DEVS_USB_SA11X0_EP2)
# define CONTROL_EP_INTR_BITS (CONTROL_EP0_INTR | CONTROL_EP1_INTR | CONTROL_EP2_INTR)
#elif defined(CYGPKG_DEVS_USB_SA11X0_EP1)
# define CONTROL_EP_INTR_BITS (CONTROL_EP0_INTR | CONTROL_EP1_INTR)
#elif defined(CYGPKG_DEVS_USB_SA11X0_EP2)
# define CONTROL_EP_INTR_BITS (CONTROL_EP0_INTR | CONTROL_EP2_INTR)
#else
# define CONTROL_EP_INTR_BITS (CONTROL_EP0_INTR)
#endif
#define EP0_OUT_READY (1 << 0)
#define EP0_IN_READY (1 << 1)
#define EP0_SENT_STALL (1 << 2)
#define EP0_FORCE_STALL (1 << 3)
#define EP0_DATA_END (1 << 4)
#define EP0_SETUP_END (1 << 5)
#define EP0_SERVICED_OPR (1 << 6)
#define EP0_SERVICED_SETUP_END (1 << 7)
#define EP1_FIFO_SERVICE (1 << 0)
#define EP1_PACKET_COMPLETE (1 << 1)
#define EP1_PACKET_ERROR (1 << 2)
#define EP1_SENT_STALL (1 << 3)
#define EP1_FORCE_STALL (1 << 4)
#define EP1_FIFO_NOT_EMPTY (1 << 5)
#define EP2_FIFO_SERVICE (1 << 0)
#define EP2_PACKET_COMPLETE (1 << 1)
#define EP2_PACKET_ERROR (1 << 2)
#define EP2_PACKET_UNDERRUN (1 << 3)
#define EP2_SENT_STALL (1 << 4)
#define EP2_FORCE_STALL (1 << 5)
#define STATUS_EP0_INTR (1 << 0)
#define STATUS_EP1_INTR (1 << 1)
#define STATUS_EP2_INTR (1 << 2)
#define STATUS_SUSPEND_INTR (1 << 3)
#define STATUS_RESUME_INTR (1 << 4)
#define STATUS_RESET_INTR (1 << 5)
#define EP0_FIFO_SIZE 8
#define EP0_MTU 8
#define EP1_FIFO_SIZE 20
#ifdef CYGNUM_DEVS_USB_SA11X0_EP1_DMA_CHANNEL
# define EP1_MTU 64
#else
# define EP1_MTU 16
#endif
#define EP2_FIFO_SIZE 16
#ifdef CYGNUM_DEVS_USB_SA11X0_EP2_DMA_CHANNEL
# define EP2_MTU 64
#else
# define EP2_MTU 16
#endif
#if defined(CYGNUM_DEVS_USB_SA11X0_EP1_DMA_CHANNEL) || defined(CYGNUM_DEVS_USB_SA11X0_EP2_DMA_CHANNEL)
typedef struct usbs_sa11x0_dma {
volatile int address;
volatile int control_set;
volatile int control_clear;
volatile int status;
volatile int buf_a_address; // Absolute, not remapped
volatile int buf_a_size;
volatile int buf_b_address; // Absolute, not remapped
volatile int buf_b_size;
} usbs_sa11x0_dma;
#define DMA_CONTROL_RUN (1 << 0)
#define DMA_CONTROL_INTR_ENABLE (1 << 1)
#define DMA_STATUS_ERROR (1 << 2)
#define DMA_STATUS_DONE_A (1 << 3)
#define DMA_CONTROL_START_A (1 << 4)
#define DMA_STATUS_DONE_B (1 << 5)
#define DMA_CONTROL_START_B (1 << 6)
#define DMA_STATUS_BUFFER_IN_USE (1 << 7)
// All the bits that are useful to clear. BUFFER_IN_USE is read-only.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -