aboutsummaryrefslogtreecommitdiffstats
path: root/recipes/linux/linux-2.6.28/collie/0009-add-sa1100-usb-gadget-driver-hack.patch
diff options
context:
space:
mode:
Diffstat (limited to 'recipes/linux/linux-2.6.28/collie/0009-add-sa1100-usb-gadget-driver-hack.patch')
-rw-r--r--recipes/linux/linux-2.6.28/collie/0009-add-sa1100-usb-gadget-driver-hack.patch2629
1 files changed, 2629 insertions, 0 deletions
diff --git a/recipes/linux/linux-2.6.28/collie/0009-add-sa1100-usb-gadget-driver-hack.patch b/recipes/linux/linux-2.6.28/collie/0009-add-sa1100-usb-gadget-driver-hack.patch
new file mode 100644
index 0000000000..bb8dd5cd63
--- /dev/null
+++ b/recipes/linux/linux-2.6.28/collie/0009-add-sa1100-usb-gadget-driver-hack.patch
@@ -0,0 +1,2629 @@
+From 923ac0a48c2a064e4639b0fa53dbd0a18d87043e Mon Sep 17 00:00:00 2001
+From: Thomas Kunze <thommycheck@gmx.de>
+Date: Tue, 10 Feb 2009 18:09:03 +0100
+Subject: [PATCH 09/23] add sa1100 usb gadget driver hack
+
+Conflicts:
+
+ drivers/usb/gadget/Makefile
+---
+ arch/arm/mach-sa1100/include/mach/collie.h | 5 +-
+ drivers/usb/gadget/Kconfig | 14 +
+ drivers/usb/gadget/Makefile | 1 +
+ drivers/usb/gadget/sa1100_udc.c | 2447 ++++++++++++++++++++++++++++
+ drivers/usb/gadget/sa1100_udc.h | 94 ++
+ 5 files changed, 2558 insertions(+), 3 deletions(-)
+ create mode 100644 drivers/usb/gadget/sa1100_udc.c
+ create mode 100644 drivers/usb/gadget/sa1100_udc.h
+
+diff --git a/arch/arm/mach-sa1100/include/mach/collie.h b/arch/arm/mach-sa1100/include/mach/collie.h
+index 9bc5349..799c930 100644
+--- a/arch/arm/mach-sa1100/include/mach/collie.h
++++ b/arch/arm/mach-sa1100/include/mach/collie.h
+@@ -23,11 +23,10 @@
+ #define COLLIE_SCP_5VON SCOOP_GPCR_PA16
+ #define COLLIE_SCP_AMP_ON SCOOP_GPCR_PA17
+ #define COLLIE_GPIO_VPEN (COLLIE_SCOOP_GPIO_BASE + 7)
+-#define COLLIE_SCP_LB_VOL_CHG SCOOP_GPCR_PA19
++#define COLLIE_GPIO_LB_VOL_CHG (COLLIE_SCOOP_GPIO_BASE + 8)
+
+ #define COLLIE_SCOOP_IO_DIR ( COLLIE_SCP_CHARGE_ON | COLLIE_SCP_MUTE_L | COLLIE_SCP_MUTE_R | \
+- COLLIE_SCP_5VON | COLLIE_SCP_AMP_ON | \
+- COLLIE_SCP_LB_VOL_CHG )
++ COLLIE_SCP_5VON | COLLIE_SCP_AMP_ON )
+ #define COLLIE_SCOOP_IO_OUT ( COLLIE_SCP_MUTE_L | COLLIE_SCP_MUTE_R | \
+ COLLIE_SCP_CHARGE_ON )
+
+diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
+index dd4cd5a..efb65ac 100644
+--- a/drivers/usb/gadget/Kconfig
++++ b/drivers/usb/gadget/Kconfig
+@@ -419,6 +419,20 @@ config USB_GOKU
+ default USB_GADGET
+ select USB_GADGET_SELECTED
+
++config USB_GADGET_SA1100
++ boolean "SA1100 USB Device Port"
++ depends on ARCH_SA1100
++ select USB_GADGET_SELECTED
++ help
++
++ Say "y" to link the driver statically, or "m" to build a
++ dynamically linked module called "sa1100_udc" and force all
++ gadget drivers to also be dynamically linked.
++
++config USB_SA1100
++ tristate
++ depends on USB_GADGET_SA1100
++ default USB_GADGET
+
+ #
+ # LAST -- dummy/emulated controller
+diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
+index bd4041b..5cdd0ce 100644
+--- a/drivers/usb/gadget/Makefile
++++ b/drivers/usb/gadget/Makefile
+@@ -19,6 +19,7 @@ obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o
+ obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o
+ obj-$(CONFIG_USB_M66592) += m66592-udc.o
+ obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o
++obj-$(CONFIG_USB_SA1100) += sa1100_udc.o
+
+ #
+ # USB gadget drivers
+diff --git a/drivers/usb/gadget/sa1100_udc.c b/drivers/usb/gadget/sa1100_udc.c
+new file mode 100644
+index 0000000..5e26a6d
+--- /dev/null
++++ b/drivers/usb/gadget/sa1100_udc.c
+@@ -0,0 +1,2447 @@
++/*
++ * SA1100 USB Device Controller (UDC) driver.
++ *
++ * Copyright (C) Compaq Computer Corporation, 1998, 1999
++ * Copyright (C) Extenex Corporation, 2001
++ * Copyright (C) David Brownell, 2003
++ * Copyright (C) Nick Bane, 2005, 2006, 2007
++ * Many fragments from pxa2xx_udc.c and mach-sa1100 driver with various
++ * GPL Copyright authors incl Russel king and Nicolas Pitre
++ * Working port to 2.6.32-1 by N C Bane
++ *
++ * This file provides interrupt routing and overall coordination for the
++ * sa1100 USB endpoints: ep0, ep1out-bulk, ep2in-bulk, as well as device
++ * initialization and some parts of USB "Chapter 9" device behavior.
++ *
++ * It implements the "USB gadget controller" API, abstracting most hardware
++ * details so that drivers running on top of this API are mostly independent
++ * of hardware. A key exception is that ep0 logic needs to understand which
++ * endpoints a given controller has, and their capabilities. Also, hardware
++ * that doesn't fully support USB (like sa1100) may need workarounds in the
++ * protocols implemented by device functions.
++ *
++ * See linux/Documentation/arm/SA1100/SA1100_USB for more info, or the
++ * kerneldoc for the API exposed to gadget drivers.
++ *
++ */
++//#define DEBUG 1
++//#define VERBOSE 1
++
++//#define SA1100_USB_DEBUG
++#ifdef SA1100_USB_DEBUG
++static int sa1100_usb_debug=0;
++#endif
++
++#define NCB_DMA_FIX
++#ifdef NCB_DMA_FIX
++// This is a clunky fix for dma alignemnt issues
++// It should probably be done better by someone more
++// steeped in DMA lore
++#include <linux/slab.h>
++#define SEND_BUFFER_SIZE 4096 /* this is probably a bit big */
++#define RECEIVE_BUFFER_SIZE 256 /* 64 may be all that is necessary */
++static char *send_buffer=NULL;
++static char *receive_buffer=NULL;
++#endif
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/ioport.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/smp_lock.h>
++#include <linux/errno.h>
++#include <linux/init.h>
++#include <linux/timer.h>
++#include <linux/list.h>
++#include <linux/interrupt.h>
++#include <linux/version.h>
++#include <linux/device.h>
++#include <linux/platform_device.h>
++
++#include <asm/byteorder.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <mach/dma.h>
++#include <asm/system.h>
++#include <asm/mach-types.h>
++#include <asm/unaligned.h>
++
++#include <linux/usb.h>
++#include <linux/usb/ch9.h>
++#include <linux/usb/gadget.h>
++
++#if CONFIG_PROC_FS
++#include <linux/proc_fs.h>
++#endif
++
++#if defined(CONFIG_SA1100_BALLOON)
++#include <asm/arch/balloon2.h>
++#endif
++
++#if defined(CONFIG_SA1100_COLLIE)
++#include <linux/gpio.h>
++#include <mach/collie.h>
++#endif
++
++#define DRIVER_VERSION __DATE__
++
++#define DMA_ADDR_INVALID (~(dma_addr_t)0)
++
++
++static const char driver_name [] = "sa1100_udc";
++static const char driver_desc [] = "SA-1110 USB Device Controller";
++
++static const char ep0name [] = "ep0";
++
++#ifdef DEBUG
++static char *type_string (u8 bmAttributes)
++{
++ switch ( (bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) {
++ case USB_ENDPOINT_XFER_BULK: return "bulk";
++ //case USB_ENDPOINT_XFER_ISOC: return "iso";
++ case USB_ENDPOINT_XFER_INT: return "intr";
++ };
++ return "control";
++}
++#endif
++
++#include <linux/dma-mapping.h>
++struct usb_stats_t {
++ unsigned long ep0_fifo_write_failures;
++ unsigned long ep0_bytes_written;
++ unsigned long ep0_fifo_read_failures;
++ unsigned long ep0_bytes_read;
++};
++
++struct usb_info_t {
++ dma_regs_t *dmaregs_tx, *dmaregs_rx;
++ int state;
++ unsigned char address;
++ struct usb_stats_t stats;
++};
++
++enum { kError=-1, kEvSuspend=0, kEvReset=1,
++ kEvResume=2, kEvAddress=3, kEvConfig=4, kEvDeConfig=5 };
++int usbctl_next_state_on_event( int event ) {
++ return 0;
++}
++static struct usb_info_t usbd_info;
++
++/* receiver */
++void ep1_reset(void);
++void ep1_stall(void);
++int sa1100_usb_recv (struct usb_request *req, void (*callback) (int,int));
++
++/* xmitter */
++void ep2_reset(void);
++void ep2_stall(void);
++int sa1100_usb_send (struct usb_request *req, void (*callback) (int,int));
++
++/* UDC register utility functions */
++#define UDC_write(reg, val) { \
++ int i = 10000; \
++ do { \
++ (reg) = (val); \
++ if (i-- <= 0) { \
++ printk( "%s [%d]: write %#x to %p (%#lx) failed\n", \
++ __FUNCTION__, __LINE__, (val), &(reg), (reg)); \
++ break; \
++ } \
++ } while((reg) != (val)); \
++}
++
++#define UDC_set(reg, val) { \
++ int i = 10000; \
++ do { \
++ (reg) |= (val); \
++ if (i-- <= 0) { \
++ printk( "%s [%d]: set %#x of %p (%#lx) failed\n", \
++ __FUNCTION__, __LINE__, (val), &(reg), (reg)); \
++ break; \
++ } \
++ } while(!((reg) & (val))); \
++}
++
++#define UDC_clear(reg, val) { \
++ int i = 10000; \
++ do { \
++ (reg) &= ~(val); \
++ if (i-- <= 0) { \
++ printk( "%s [%d]: clear %#x of %p (%#lx) failed\n", \
++ __FUNCTION__, __LINE__, (val), &(reg), (reg)); \
++ break; \
++ } \
++ } while((reg) & (val)); \
++}
++
++#define UDC_flip(reg, val) { \
++ int i = 10000; \
++ (reg) = (val); \
++ do { \
++ (reg) = (val); \
++ if (i-- <= 0) { \
++ printk( "%s [%d]: flip %#x of %p (%#lx) failed\n", \
++ __FUNCTION__, __LINE__, (val), &(reg), (reg)); \
++ break; \
++ } \
++ } while(((reg) & (val))); \
++}
++
++#include "sa1100_udc.h"
++
++static struct sa1100_udc *the_controller;
++static void nuke (struct sa1100_ep *, int status);
++static void done (struct sa1100_ep *ep, struct sa1100_request *req, int status);
++static inline void ep0_idle (struct sa1100_udc *dev)
++{
++ dev->ep0state = EP0_IDLE;
++}
++
++// ep0 handlers
++
++// 1 == lots of trace noise, 0 = only "important' stuff
++#define VERBOSITY 0
++
++#if 1 && !defined( ASSERT )
++# define ASSERT(expr) \
++ if(!(expr)) { \
++ printk( "Assertion failed! %s,%s,%s,line=%d\n",\
++ #expr,__FILE__,__FUNCTION__,__LINE__); \
++ }
++#else
++# define ASSERT(expr)
++#endif
++
++#if VERBOSITY
++#define PRINTKD(fmt, args...) printk( fmt , ## args)
++#else
++#define PRINTKD(fmt, args...)
++#endif
++
++/* other subroutines */
++unsigned int (*wrint)(void);
++void ep0_int_hndlr( void );
++static void ep0_queue(void *buf, unsigned int req, unsigned int act);
++static void write_fifo( void );
++static int read_fifo( struct usb_ctrlrequest * p );
++
++/* some voodo helpers 01Mar01ww */
++static void set_cs_bits( __u32 set_bits );
++static void set_de( void );
++static void set_ipr( void );
++static void set_ipr_and_de( void );
++static bool clear_opr( void );
++
++/***************************************************************************
++Inline Helpers
++***************************************************************************/
++
++/* Data extraction from usb_request_t fields */
++enum { kTargetDevice=0, kTargetInterface=1, kTargetEndpoint=2 };
++static inline int request_target( __u8 b ) { return (int) ( b & 0x0F); }
++
++static inline int windex_to_ep_num( __u16 w ) { return (int) ( w & 0x000F); }
++inline int type_code_from_request( __u8 by ) { return (( by >> 4 ) & 3); }
++
++/* following is hook for self-powered flag in GET_STATUS. Some devices
++ .. might like to override and return real info */
++static inline bool self_powered_hook( void ) { return true; }
++
++#if VERBOSITY
++/* "pcs" == "print control status" */
++static inline void pcs( void )
++{
++ __u32 foo = Ser0UDCCS0;
++ printk( "%8.8X: %s %s %s %s\n",
++ foo,
++ foo & UDCCS0_SE ? "SE" : "",
++ foo & UDCCS0_OPR ? "OPR" : "",
++ foo & UDCCS0_IPR ? "IPR" : "",
++ foo & UDCCS0_SST ? "SST" : ""
++ );
++}
++static inline void preq( struct usb_ctrlrequest * pReq )
++{
++ static char * tnames[] = { "dev", "intf", "ep", "oth" };
++ static char * rnames[] = { "std", "class", "vendor", "???" };
++ char * psz;
++ switch( pReq->bRequest ) {
++ case USB_REQ_GET_STATUS: psz = "get stat"; break;
++ case USB_REQ_CLEAR_FEATURE: psz = "clr feat"; break;
++ case USB_REQ_SET_FEATURE: psz = "set feat"; break;
++ case USB_REQ_SET_ADDRESS: psz = "set addr"; break;
++ case USB_REQ_GET_DESCRIPTOR: psz = "get desc"; break;
++ case USB_REQ_SET_DESCRIPTOR: psz = "set desc"; break;
++ case USB_REQ_GET_CONFIGURATION: psz = "get cfg"; break;
++ case USB_REQ_SET_CONFIGURATION: psz = "set cfg"; break;
++ case USB_REQ_GET_INTERFACE: psz = "get intf"; break;
++ case USB_REQ_SET_INTERFACE: psz = "set intf"; break;
++ default: psz = "unknown"; break;
++ }
++ printk( "- [%s: %s req to %s. dir=%s]\n", psz,
++ rnames[ (pReq->bRequestType >> 5) & 3 ],
++ tnames[ pReq->bRequestType & 3 ],
++ ( pReq->bRequestType & 0x80 ) ? "in" : "out" );
++}
++
++static inline void usbctl_dump_request(const char *prefix, const struct usb_ctrlrequest *req)
++{
++ printk("%s: bRequestType=0x%02x bRequest=0x%02x "
++ "wValue=0x%04x wIndex=0x%04x wLength=0x%04x\n",
++ prefix, req->bRequestType, req->bRequest,
++ le16_to_cpu(req->wValue), le16_to_cpu(req->wIndex),
++ le16_to_cpu(req->wLength));
++}
++#else
++static inline void pcs( void ){}
++//static inline void preq( void ){}
++static inline void preq( void *x ){}
++static inline void usbctl_dump_request(const char *prefix, const struct usb_ctrlrequest *req) {}
++#endif
++
++/***************************************************************************
++Globals
++***************************************************************************/
++static const char pszMe[] = "usbep0: ";
++
++
++/* global write struct to keep write
++ ..state around across interrupts */
++static struct {
++ unsigned char *p;
++ int bytes_left;
++} wr;
++
++/***************************************************************************
++Public Interface
++***************************************************************************/
++
++/* reset received from HUB (or controller just went nuts and reset by itself!)
++ so udc core has been reset, track this state here */
++void ep0_reset(void)
++{
++ /* reset state machine */
++ wr.p = NULL;
++ wr.bytes_left = 0;
++ usbd_info.address=0;
++// needed?
++ Ser0UDCAR = 0;
++}
++
++
++/* handle interrupt for endpoint zero */
++
++inline void ep0_clear_write(void) {
++ wr.p = NULL;
++ wr.bytes_left = 0;
++}
++
++/* this is a config packet parser based on that from the updated HH 2.6 udc */
++static void ep0_read_packet(void)
++{
++ unsigned char status_buf[2]; /* returned in GET_STATUS */
++ struct usb_ctrlrequest req;
++ int request_type;
++ int n;
++ __u32 address;
++ __u32 in, out;
++
++ /* reset previous count */
++ the_controller->ep0_req_len=-1;
++
++ /* read the setup request */
++ n = read_fifo( &req );
++ usbctl_dump_request("ep0_read_packet",&req);
++
++ if ( n != sizeof( req ) ) {
++ printk( "%ssetup begin: fifo READ ERROR wanted %d bytes got %d. "
++ " Stalling out...\n",
++ pszMe, sizeof( req ), n );
++ /* force stall, serviced out */
++ set_cs_bits( UDCCS0_FST | UDCCS0_SO );
++ goto sh_sb_end;
++ }
++
++ /* Is it a standard request? (not vendor or class request) */
++ request_type = type_code_from_request( req.bRequestType );
++ if ( request_type != 0 ) {
++ printk( "%ssetup begin: unsupported bRequestType: %d ignored\n",
++ pszMe, request_type );
++ set_cs_bits( UDCCS0_DE | UDCCS0_SO );
++ goto sh_sb_end;
++ }
++
++ /* save requested reply size */
++ the_controller->ep0_req_len=le16_to_cpu(req.wLength);
++ PRINTKD("%s: request length is %d\n",__FUNCTION__,the_controller->ep0_req_len);
++
++#if VERBOSITY
++ {
++ unsigned char * pdb = (unsigned char *) &req;
++ PRINTKD( "%2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X ",
++ pdb[0], pdb[1], pdb[2], pdb[3], pdb[4], pdb[5], pdb[6], pdb[7]
++ );
++ preq( &req );
++ }
++#endif
++
++ /* Handle it */
++ switch( req.bRequest ) {
++
++ /* This first bunch have no data phase */
++
++ case USB_REQ_SET_ADDRESS:
++ address = (__u32) (req.wValue & 0x7F);
++ /* when SO and DE sent, UDC will enter status phase and ack,
++ ..propagating new address to udc core. Next control transfer
++ ..will be on the new address. You can't see the change in a
++ ..read back of CAR until then. (about 250us later, on my box).
++ ..The original Intel driver sets S0 and DE and code to check
++ ..that address has propagated here. I tried this, but it
++ ..would only work sometimes! The rest of the time it would
++ ..never propagate and we'd spin forever. So now I just set
++ ..it and pray...
++ */
++ Ser0UDCAR = address;
++ usbd_info.address = address;
++ usbctl_next_state_on_event( kEvAddress );
++ set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */
++ printk( "%sI have been assigned address: %d\n", pszMe, address );
++ break;
++
++
++ case USB_REQ_SET_CONFIGURATION:
++ if ( req.wValue == 1 ) {
++ /* configured */
++ if (usbctl_next_state_on_event( kEvConfig ) != kError) {
++ /* (re)set the out and in max packet sizes */
++ PRINTKD( "%s: calling the_controller.driver->setup with SET_CONFIGURATION\n", __FUNCTION__ );
++ the_controller->driver->setup(&the_controller->gadget, &req);
++ in = __le16_to_cpu( the_controller->ep[1].ep.maxpacket );
++ out = __le16_to_cpu( the_controller->ep[2].ep.maxpacket );
++ Ser0UDCOMP = ( out - 1 );
++ Ser0UDCIMP = ( in - 1 );
++ // we are configured
++ usbd_info.state = USB_STATE_CONFIGURED;
++ // enable rx and tx interrupts
++ Ser0UDCCR &= ~(UDCCR_RIM | UDCCR_TIM);
++
++ printk( "%sConfigured (OMP=%8.8X IMP=%8.8X)\n", pszMe, out, in );
++ break;
++ }
++ } else if ( req.wValue == 0 ) {
++ /* de-configured */
++ if (usbctl_next_state_on_event( kEvDeConfig ) != kError )
++ printk( "%sDe-Configured\n", pszMe );
++ usbd_info.state = 0;
++ Ser0UDCCR |= UDCCR_RIM | UDCCR_TIM;
++ ep1_reset ();
++ ep2_reset ();
++ printk("%s: de-configured. Tx and Rx interrupts disabled. ep1 and ep2 reset\n",__FUNCTION__);
++ } else {
++ printk( "%ssetup phase: Unknown "
++ "\"set configuration\" data %d\n",
++ pszMe, req.wValue );
++ }
++ set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */
++ break;
++
++ case USB_REQ_CLEAR_FEATURE:
++ /* could check data length, direction...26Jan01ww */
++ if ( req.wValue == 0 ) { /* clearing ENDPOINT_HALT/STALL */
++ int ep = windex_to_ep_num( req.wIndex );
++ if ( ep == 1 ) {
++ printk( "%sclear feature \"endpoint halt\" "
++ " on receiver\n", pszMe );
++ ep1_reset();
++ }
++ else if ( ep == 2 ) {
++ printk( "%sclear feature \"endpoint halt\" "
++ "on xmitter\n", pszMe );
++ ep2_reset();
++ } else {
++ printk( "%sclear feature \"endpoint halt\" "
++ "on unsupported ep # %d\n",
++ pszMe, ep );
++ }
++ } else {
++ printk( "%sUnsupported feature selector (%d) "
++ "in clear feature. Ignored.\n" ,
++ pszMe, req.wValue );
++ }
++ set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */
++ break;
++
++ case USB_REQ_SET_FEATURE:
++ if ( req.wValue == 0 ) { /* setting ENDPOINT_HALT/STALL */
++ int ep = windex_to_ep_num( req.wValue );
++ if ( ep == 1 ) {
++ printk( "%set feature \"endpoint halt\" "
++ "on receiver\n", pszMe );
++ ep1_stall();
++ }
++ else if ( ep == 2 ) {
++ printk( "%sset feature \"endpoint halt\" "
++ " on xmitter\n", pszMe );
++ ep2_stall();
++ } else {
++ printk( "%sset feature \"endpoint halt\" "
++ "on unsupported ep # %d\n",
++ pszMe, ep );
++ }
++ }
++ else {
++ printk( "%sUnsupported feature selector "
++ "(%d) in set feature\n",
++ pszMe, req.wValue );
++ }
++ set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */
++ break;
++
++ /* The rest have a data phase that writes back to the host */
++ case USB_REQ_GET_STATUS:
++ /* return status bit flags */
++ status_buf[0] = status_buf[1] = 0;
++ n = request_target(req.bRequestType);
++ switch( n ) {
++ case kTargetDevice:
++ if ( self_powered_hook() )
++ status_buf[0] |= 1;
++ break;
++ case kTargetInterface:
++ break;
++ case kTargetEndpoint:
++ /* return stalled bit */
++ n = windex_to_ep_num( req.wIndex );
++ if ( n == 1 )
++ status_buf[0] |= (Ser0UDCCS1 & UDCCS1_FST) >> 4;
++ else if ( n == 2 )
++ status_buf[0] |= (Ser0UDCCS2 & UDCCS2_FST) >> 5;
++ else {
++ printk( "%sUnknown endpoint (%d) "
++ "in GET_STATUS\n", pszMe, n );
++ }
++ break;
++ default:
++ printk( "%sUnknown target (%d) in GET_STATUS\n",
++ pszMe, n );
++ /* fall thru */
++ break;
++ }
++ PRINTKD("%s: GET_STATUS writing %d\n",__FUNCTION__,req.wLength);
++ ep0_queue( status_buf, req.wLength, sizeof( status_buf ));
++ break;
++ case USB_REQ_GET_DESCRIPTOR:
++ PRINTKD( "%s: calling the_controller.driver->setup with GET_DESCRIPTOR\n", __FUNCTION__ );
++ the_controller->driver->setup(&the_controller->gadget, &req);
++ break;
++ case USB_REQ_GET_CONFIGURATION:
++ PRINTKD( "%s: calling the_controller.driver->setup with GET_CONFIGURATION\n", __FUNCTION__ );
++ the_controller->driver->setup(&the_controller->gadget, &req);
++ break;
++ case USB_REQ_GET_INTERFACE:
++ PRINTKD( "%s: calling the_controller->driver->setup with GET_INTERFACE\n", __FUNCTION__ );
++ the_controller->driver->setup(&the_controller->gadget, &req);
++ break;
++ case USB_REQ_SET_INTERFACE:
++ PRINTKD( "%s: calling the_controller->driver->setup with SET_INTERFACE\n", __FUNCTION__ );
++ the_controller->driver->setup(&the_controller->gadget, &req);
++ break;
++ default :
++ printk("%sunknown request 0x%x\n", pszMe, req.bRequest);
++ break;
++ } /* switch( bRequest ) */
++
++sh_sb_end:
++ return;
++
++}
++
++void ep0_int_hndlr(void)
++{
++ u32 cs_reg_in;
++
++ pcs();
++
++ cs_reg_in = Ser0UDCCS0;
++
++ /*
++ * If "setup end" has been set, the usb controller has terminated
++ * a setup transaction before we set DE. This happens during
++ * enumeration with some hosts. For example, the host will ask for
++ * our device descriptor and specify a return of 64 bytes. When we
++ * hand back the first 8, the host will know our max packet size
++ * and turn around and issue a new setup immediately. This causes
++ * the UDC to auto-ack the new setup and set SE. We must then
++ * "unload" (process) the new setup, which is what will happen
++ * after this preamble is finished executing.
++ */
++ if (cs_reg_in & UDCCS0_SE) {
++ PRINTKD("UDC: early termination of setup\n");
++
++ /*
++ * Clear setup end
++ */
++ set_cs_bits(UDCCS0_SSE);
++
++ /*
++ * Clear any pending write.
++ */
++ ep0_clear_write();
++ }
++
++ /*
++ * UDC sent a stall due to a protocol violation.
++ */
++ if (cs_reg_in & UDCCS0_SST) {
++ PRINTKD("UDC: write_preamble: UDC sent stall\n");
++
++ /*
++ * Clear sent stall
++ */
++ set_cs_bits(UDCCS0_SST);
++
++ /*
++ * Clear any pending write.
++ */
++ ep0_clear_write();
++ }
++
++ switch (cs_reg_in & (UDCCS0_OPR | UDCCS0_IPR)) {
++ case UDCCS0_OPR | UDCCS0_IPR:
++ PRINTKD("UDC: write_preamble: see OPR. Stopping write to "
++ "handle new SETUP\n");
++
++ /*
++ * very rarely, you can get OPR and
++ * leftover IPR. Try to clear
++ */
++ UDC_clear(Ser0UDCCS0, UDCCS0_IPR);
++
++ /*
++ * Clear any pending write.
++ */
++ ep0_clear_write();
++
++ /*FALLTHROUGH*/
++ case UDCCS0_OPR:
++ /*
++ * A new setup request is pending. Handle
++ * it. Note that we don't try to read a
++ * packet if SE was set and OPR is clear.
++ */
++ ep0_read_packet();
++ break;
++
++ case 0:
++ // if data pending ...
++ if (wr.p) {
++ unsigned int cs_bits = 0;
++ if (wr.bytes_left != 0) {
++ /*
++ * More data to go
++ */
++ write_fifo();
++ // packet ready
++ cs_bits |= UDCCS0_IPR;
++ }
++
++ if (wr.bytes_left == 0) {
++ /*
++ * All data sent.
++ */
++ cs_bits |= wrint();
++ // a null packet may be following
++ if (!wrint)
++ ep0_clear_write();
++ }
++ set_cs_bits(cs_bits);
++ }
++ else
++ PRINTKD("%s: No data - probably an ACK\n",__FUNCTION__);
++ break;
++
++ case UDCCS0_IPR:
++ PRINTKD("UDC: IPR set, not writing\n");
++ break;
++ }
++
++ pcs();
++ PRINTKD( "-end-\n" );
++}
++
++static unsigned int ep0_sh_write_data(void)
++{
++ /*
++ * If bytes left is zero, we are coming in on the
++ * interrupt after the last packet went out. And
++ * we know we don't have to empty packet this
++ * transfer so just set DE and we are done
++ */
++ PRINTKD("UDC: normal packet ended\n");
++ wrint=NULL;
++ return UDCCS0_DE;
++}
++
++static unsigned int ep0_sh_write_with_empty_packet(void)
++{
++ /*
++ * If bytes left is zero, we are coming in on the
++ * interrupt after the last packet went out.
++ * We must do short packet suff, so set DE and IPR
++ */
++ PRINTKD("UDC: short packet sent\n");
++ wrint=NULL;
++ return UDCCS0_IPR | UDCCS0_DE;
++}
++
++static unsigned int ep0_sh_write_data_then_empty_packet(void)
++{
++ PRINTKD("UDC: last packet full. Send empty packet next\n");
++ wrint=ep0_sh_write_with_empty_packet;
++ return 0;
++}
++
++static void ep0_queue(void *buf, unsigned int len, unsigned int req_len)
++{
++ __u32 cs_reg_bits = UDCCS0_IPR;
++
++ PRINTKD("a=%d r=%d\n", len, req_len);
++
++ if (len == 0) {
++ // no output packet to wait for
++ PRINTKD("%s: zero byte packet being queued. Setting DE and OPR end exiting\n",__FUNCTION__);
++ set_cs_bits(UDCCS0_DE | UDCCS0_SO);
++ return;
++ }
++
++ /*
++ * thou shalt not enter data phase until
++ * Out Packet Ready is clear
++ */
++ if (!clear_opr()) {
++ printk("UDC: SO did not clear OPR\n");
++ set_cs_bits(UDCCS0_DE | UDCCS0_SO);
++ return;
++ }
++
++ // note data to xmit stored
++ wr.p=buf;
++ wr.bytes_left=min(len, req_len);
++
++ // write the first block
++ write_fifo();
++
++ // done already?
++ if (wr.bytes_left == 0) {
++ /*
++ * out in one, so data end
++ */
++ cs_reg_bits |= UDCCS0_DE;
++ ep0_clear_write();
++ // rest is a shorter than expected reply?
++ } else if (len < req_len) {
++ /*
++ * we are going to short-change host
++ * so need nul to not stall
++ */
++ if (len % 8) {
++ PRINTKD("%s: %d more to go ending in a short packet.\n",__FUNCTION__,wr.bytes_left);
++ wrint=ep0_sh_write_with_empty_packet;
++ }
++ // unless we are on a packet boundary. Then send full packet plus null packet.
++ else {
++ PRINTKD("%s: %d more to go then add empty packet.\n",__FUNCTION__,wr.bytes_left);
++ wrint=ep0_sh_write_data_then_empty_packet;
++ }
++ } else {
++ /*
++ * we have as much or more than requested
++ */
++ PRINTKD("%s: %d more to go.\n",__FUNCTION__,wr.bytes_left);
++ wrint=ep0_sh_write_data;
++ }
++
++ /*
++ * note: IPR was set uncondtionally at start of routine
++ */
++ set_cs_bits(cs_reg_bits);
++}
++
++/*
++ * write_fifo()
++ * Stick bytes in the 8 bytes endpoint zero FIFO.
++ * This version uses a variety of tricks to make sure the bytes
++ * are written correctly. 1. The count register is checked to
++ * see if the byte went in, and the write is attempted again
++ * if not. 2. An overall counter is used to break out so we
++ * don't hang in those (rare) cases where the UDC reverses
++ * direction of the FIFO underneath us without notification
++ * (in response to host aborting a setup transaction early).
++ *
++ */
++static void write_fifo( void )
++{
++ int bytes_this_time = min(wr.bytes_left, 8);
++ int bytes_written = 0;
++
++ PRINTKD( "WF=%d: ", bytes_this_time );
++
++ while( bytes_this_time-- ) {
++ unsigned int cwc;
++ int i;
++ PRINTKD( "%2.2X ", *wr.p );
++ cwc = Ser0UDCWC & 15;
++ i = 10;
++ do {
++ Ser0UDCD0 = *wr.p;
++ udelay( 20 ); /* voodo 28Feb01ww */
++ } while( (Ser0UDCWC &15) == cwc && --i );
++
++ if ( i == 0 ) {
++ printk( "%swrite_fifo: write failure\n", pszMe );
++ usbd_info.stats.ep0_fifo_write_failures++;
++ }
++
++ wr.p++;
++ bytes_written++;
++ }
++ wr.bytes_left -= bytes_written;
++
++ /* following propagation voodo so maybe caller writing IPR in
++ ..a moment might actually get it to stick 28Feb01ww */
++ udelay( 300 );
++
++ usbd_info.stats.ep0_bytes_written += bytes_written;
++ PRINTKD( "L=%d WCR=%8.8lX\n", wr.bytes_left, Ser0UDCWC );
++}
++/*
++ * read_fifo()
++ * Read 1-8 bytes out of FIFO and put in request.
++ * Called to do the initial read of setup requests
++ * from the host. Return number of bytes read.
++ *
++ * Like write fifo above, this driver uses multiple
++ * reads checked agains the count register with an
++ * overall timeout.
++ *
++ */
++static int
++read_fifo( struct usb_ctrlrequest * request )
++{
++ int bytes_read = 0;
++ int fifo_count;
++
++ unsigned char * pOut = (unsigned char*) request;
++
++ fifo_count = ( Ser0UDCWC & 0xFF );
++
++ ASSERT( fifo_count <= 8 );
++ PRINTKD( "RF=%d ", fifo_count );
++
++ while( fifo_count-- ) {
++ unsigned int cwc;
++ int i;
++
++ cwc = Ser0UDCWC & 15;
++
++ i = 10;
++ do {
++ *pOut = (unsigned char) Ser0UDCD0;
++ udelay( 20 );
++ } while( ( Ser0UDCWC & 15 ) == cwc && --i );
++
++ if ( i == 0 ) {
++ printk( "%sread_fifo(): read failure\n", pszMe );
++ usbd_info.stats.ep0_fifo_read_failures++;
++ }
++ pOut++;
++ bytes_read++;
++ }
++
++ PRINTKD( "fc=%d\n", bytes_read );
++ usbd_info.stats.ep0_bytes_read++;
++ return bytes_read;
++}
++
++/* some voodo I am adding, since the vanilla macros just aren't doing it 1Mar01ww */
++
++#define ABORT_BITS ( UDCCS0_SST | UDCCS0_SE )
++#define OK_TO_WRITE (!( Ser0UDCCS0 & ABORT_BITS ))
++#define BOTH_BITS (UDCCS0_IPR | UDCCS0_DE)
++
++static void set_cs_bits( __u32 bits )
++{
++ if ( bits & ( UDCCS0_SO | UDCCS0_SSE | UDCCS0_FST | UDCCS0_SST) )
++ Ser0UDCCS0 = bits;
++ else if ( (bits & BOTH_BITS) == BOTH_BITS )
++ set_ipr_and_de();
++ else if ( bits & UDCCS0_IPR )
++ set_ipr();
++ else if ( bits & UDCCS0_DE )
++ set_de();
++}
++
++static void set_de( void )
++{
++ int i = 1;
++ while( 1 ) {
++ if ( OK_TO_WRITE ) {
++ Ser0UDCCS0 |= UDCCS0_DE;
++ } else {
++ PRINTKD( "%sQuitting set DE because SST or SE set\n", pszMe );
++ break;
++ }
++ if ( Ser0UDCCS0 & UDCCS0_DE )
++ break;
++ udelay( i );
++ if ( ++i == 50 ) {
++ printk( "%sDangnabbbit! Cannot set DE! (DE=%8.8X CCS0=%8.8lX)\n",
++ pszMe, UDCCS0_DE, Ser0UDCCS0 );
++ break;
++ }
++ }
++}
++
++static void set_ipr( void )
++{
++ int i = 1;
++ while( 1 ) {
++ if ( OK_TO_WRITE ) {
++ Ser0UDCCS0 |= UDCCS0_IPR;
++ } else {
++ PRINTKD( "%sQuitting set IPR because SST or SE set\n", pszMe );
++ break;
++ }
++ if ( Ser0UDCCS0 & UDCCS0_IPR )
++ break;
++ udelay( i );
++ if ( ++i == 50 ) {
++ printk( "%sDangnabbbit! Cannot set IPR! (IPR=%8.8X CCS0=%8.8lX)\n",
++ pszMe, UDCCS0_IPR, Ser0UDCCS0 );
++ break;
++ }
++ }
++}
++
++static void set_ipr_and_de( void )
++{
++ int i = 1;
++ while( 1 ) {
++ if ( OK_TO_WRITE ) {
++ Ser0UDCCS0 |= BOTH_BITS;
++ } else {
++ PRINTKD( "%sQuitting set IPR/DE because SST or SE set\n", pszMe );
++ break;
++ }
++ if ( (Ser0UDCCS0 & BOTH_BITS) == BOTH_BITS)
++ break;
++ udelay( i );
++ if ( ++i == 50 ) {
++ printk( "%sDangnabbbit! Cannot set DE/IPR! (DE=%8.8X IPR=%8.8X CCS0=%8.8lX)\n",
++ pszMe, UDCCS0_DE, UDCCS0_IPR, Ser0UDCCS0 );
++ break;
++ }
++ }
++}
++
++static bool clear_opr( void )
++{
++ int i = 10000;
++ bool is_clear;
++ do {
++ Ser0UDCCS0 = UDCCS0_SO;
++ is_clear = ! ( Ser0UDCCS0 & UDCCS0_OPR );
++ if ( i-- <= 0 ) {
++ printk( "%sclear_opr(): failed\n", pszMe );
++ break;
++ }
++ } while( ! is_clear );
++ return is_clear;
++}
++
++
++
++// ep1 handlers
++
++static char *ep1_buf;
++static int ep1_len;
++static void (*ep1_callback)(int flag, int size);
++static char *ep1_curdmabuf;
++static dma_addr_t ep1_curdmapos;
++static int ep1_curdmalen;
++static int ep1_remain;
++static int ep1_used;
++
++static dma_regs_t *dmaregs_rx = NULL;
++static int rx_pktsize;
++
++static int naking;
++
++static void
++ep1_start(void)
++{
++ sa1100_reset_dma(dmaregs_rx);
++ if (!ep1_curdmalen) {
++ ep1_curdmalen = rx_pktsize;
++ if (ep1_curdmalen > ep1_remain)
++ ep1_curdmalen = ep1_remain;
++ ep1_curdmapos = dma_map_single(NULL, ep1_curdmabuf, ep1_curdmalen,
++ DMA_FROM_DEVICE);
++ }
++
++ UDC_write( Ser0UDCOMP, ep1_curdmalen-1 );
++
++ sa1100_start_dma(dmaregs_rx, ep1_curdmapos, ep1_curdmalen);
++
++ if ( naking ) {
++ /* turn off NAK of OUT packets, if set */
++ UDC_flip( Ser0UDCCS1, UDCCS1_RPC );
++ naking = 0;
++ }
++}
++
++static void
++ep1_done(int flag)
++{
++ int size = ep1_len - ep1_remain;
++
++ if (!ep1_len)
++ return;
++ if (ep1_curdmalen)
++ dma_unmap_single(NULL, ep1_curdmapos, ep1_curdmalen,
++ DMA_FROM_DEVICE);
++ ep1_len = ep1_curdmalen = 0;
++ if (ep1_callback)
++ ep1_callback(flag, size);
++}
++
++void
++ep1_state_change_notify( int new_state )
++{
++
++}
++
++void
++ep1_stall( void )
++{
++ /* SET_FEATURE force stall at UDC */
++ UDC_set( Ser0UDCCS1, UDCCS1_FST );
++}
++
++int
++ep1_init(dma_regs_t *dmaregs)
++{
++ dmaregs_rx = dmaregs;
++ sa1100_reset_dma(dmaregs_rx);
++ ep1_done(-EAGAIN);
++ return 0;
++}
++
++void
++ep1_reset(void)
++{
++ if (dmaregs_rx)
++ sa1100_reset_dma(dmaregs_rx);
++ UDC_clear(Ser0UDCCS1, UDCCS1_FST);
++ ep1_done(-EINTR);
++}
++
++void ep1_int_hndlr(int udcsr)
++{
++ dma_addr_t dma_addr;
++ unsigned int len;
++ int status = Ser0UDCCS1;
++
++ if ( naking ) printk( "%sEh? in ISR but naking = %d\n", "usbrx: ", naking );
++
++ if (status & UDCCS1_RPC) {
++
++ if (!ep1_curdmalen) {
++ printk("usb_recv: RPC for non-existent buffer\n");
++ naking=1;
++ return;
++ }
++
++ sa1100_stop_dma(dmaregs_rx);
++
++ if (status & UDCCS1_SST) {
++ printk("usb_recv: stall sent OMP=%ld\n", Ser0UDCOMP);
++ UDC_flip(Ser0UDCCS1, UDCCS1_SST);
++ ep1_done(-EIO); // UDC aborted current transfer, so we do
++ return;
++ }
++
++ if (status & UDCCS1_RPE) {
++ printk("usb_recv: RPError %x\n", status);
++ UDC_flip(Ser0UDCCS1, UDCCS1_RPC);
++ ep1_done(-EIO);
++ return;
++ }
++
++ dma_addr=sa1100_get_dma_pos(dmaregs_rx);
++ dma_unmap_single(NULL, ep1_curdmapos, ep1_curdmalen,
++ DMA_FROM_DEVICE);
++ len = dma_addr - ep1_curdmapos;
++#ifdef SA1100_USB_DEBUG
++ if (sa1100_usb_debug) {
++ int i;
++ printk("usb rx %d :\n ",len);
++ if (sa1100_usb_debug>1) {
++ for (i=0; i<len; i++) {
++ if ((i % 32)==31)
++ printk("\n ");
++ printk("%2.2x ",((char *)ep1_curdmapos)[i]);
++ }
++ }
++ printk("\n");
++ }
++#endif
++ if (len < ep1_curdmalen) {
++ char *buf = ep1_curdmabuf + len;
++ while (Ser0UDCCS1 & UDCCS1_RNE) {
++ if (len >= ep1_curdmalen) {
++ printk("usb_recv: too much data in fifo\n");
++ break;
++ }
++ *buf++ = Ser0UDCDR;
++ len++;
++ }
++ } else if (Ser0UDCCS1 & UDCCS1_RNE) {
++ printk("usb_recv: fifo screwed, shouldn't contain data\n");
++ len = 0;
++ }
++
++#if defined(NCB_DMA_FIX)
++// if (len && (ep1_buf != ep1_curdmabuf))
++// memcpy(ep1_buf,ep1_curdmabuf,len);
++ if (len)
++ memcpy(&(((unsigned char *)ep1_buf)[ep1_used]),ep1_curdmabuf,len);
++#endif
++
++ ep1_curdmalen = 0; /* dma unmap already done */
++ ep1_remain -= len;
++ ep1_used += len;
++// ep1_curdmabuf += len; // use same buffer again
++ naking = 1;
++//printk("%s: received %d, %d remaining\n",__FUNCTION__,len,ep1_remain);
++ if (len && (len == rx_pktsize))
++ ep1_start();
++ else
++ ep1_done((len) ? 0 : -EPIPE);
++ }
++ /* else, you can get here if we are holding NAK */
++}
++
++int
++sa1100_usb_recv(struct usb_request *req, void (*callback)(int flag, int size))
++{
++ unsigned long flags;
++ char *buf=req->buf;
++ int len=req->length;
++
++ if (ep1_len)
++ return -EBUSY;
++
++ local_irq_save(flags);
++ ep1_buf = buf;
++ ep1_len = len;
++ ep1_callback = callback;
++ ep1_remain = len;
++ ep1_used = 0;
++#ifdef NCB_DMA_FIX
++// if (((size_t)buf)&3)
++ if (1)
++ ep1_curdmabuf = receive_buffer;
++ else
++#else
++ ep1_curdmabuf = buf;
++#endif
++ ep1_curdmalen = 0;
++ ep1_start();
++ local_irq_restore(flags);
++
++ return 0;
++}
++
++// ep2 handlers
++
++static char *ep2_buf;
++static int ep2_len;
++static void (*ep2_callback)(int status, int size);
++static dma_addr_t ep2_dma;
++static dma_addr_t ep2_curdmapos;
++static int ep2_curdmalen;
++static int ep2_remain;
++static dma_regs_t *dmaregs_tx = NULL;
++static int tx_pktsize;
++
++/* device state is changing, async */
++void
++ep2_state_change_notify( int new_state )
++{
++}
++
++/* set feature stall executing, async */
++void
++ep2_stall( void )
++{
++ UDC_set( Ser0UDCCS2, UDCCS2_FST ); /* force stall at UDC */
++}
++
++static void
++ep2_start(void)
++{
++ if (!ep2_len)
++ return;
++
++ ep2_curdmalen = tx_pktsize;
++ if (ep2_curdmalen > ep2_remain)
++ ep2_curdmalen = ep2_remain;
++
++ /* must do this _before_ queue buffer.. */
++ UDC_flip( Ser0UDCCS2,UDCCS2_TPC ); /* stop NAKing IN tokens */
++ UDC_write( Ser0UDCIMP, ep2_curdmalen-1 );
++
++ Ser0UDCAR = usbd_info.address; // fighting stupid silicon bug
++ sa1100_start_dma(dmaregs_tx, ep2_curdmapos, ep2_curdmalen);
++}
++
++static void
++ep2_done(int flag)
++{
++ int size = ep2_len - ep2_remain;
++ if (ep2_len) {
++ dma_unmap_single(NULL, ep2_dma, ep2_len, DMA_TO_DEVICE);
++ ep2_len = 0;
++ if (ep2_callback)
++ ep2_callback(flag, size);
++ }
++}
++
++int ep2_init(dma_regs_t *dmaregs)
++{
++ dmaregs_tx = dmaregs;
++ sa1100_reset_dma(dmaregs_tx);
++ ep2_done(-EAGAIN);
++ return 0;
++}
++
++void ep2_reset(void)
++{
++ UDC_clear(Ser0UDCCS2, UDCCS2_FST);
++ if (dmaregs_tx)
++ sa1100_reset_dma(dmaregs_tx);
++ ep2_done(-EINTR);
++}
++
++void ep2_int_hndlr(int udcsr)
++{
++ int status = Ser0UDCCS2;
++
++ if (Ser0UDCAR != usbd_info.address) // check for stupid silicon bug.
++ Ser0UDCAR = usbd_info.address;
++
++ if (status & UDCCS2_TPC) {
++
++ UDC_flip(Ser0UDCCS2, UDCCS2_SST);
++
++ sa1100_reset_dma(dmaregs_tx);
++
++ if (status & (UDCCS2_TPE | UDCCS2_TUR)) {
++ printk("usb_send: transmit error %x\n", status);
++ ep2_done(-EIO);
++ } else {
++ ep2_curdmapos += ep2_curdmalen;
++ ep2_remain -= ep2_curdmalen;
++
++ if (ep2_remain != 0)
++ ep2_start();
++ else
++ ep2_done(0);
++ }
++ } else {
++ printk("usb_send: Not TPC: UDCCS2 = %x\n", status);
++ }
++}
++
++int
++sa1100_usb_send(struct usb_request *req, void (*callback)(int status, int size))
++{
++ char *buf=req->buf;
++ int len=req->length;
++ unsigned long flags;
++
++ if (usbd_info.state != USB_STATE_CONFIGURED) {
++ PRINTKD("%s: return -ENODEV\n",__FUNCTION__);
++ return -ENODEV;
++ }
++
++ if (ep2_len) {
++ PRINTKD("%s: return -EBUSY\n",__FUNCTION__);
++ return -EBUSY;
++ }
++
++ local_irq_save(flags);
++#ifdef NCB_DMA_FIX
++ // if misaligned, copy to aligned buffer
++// if (((size_t)buf)&3) {
++ if (1) {
++ PRINTKD("%s: copying %d bytes to send_buffer\n",__FUNCTION__,len);
++ memcpy(send_buffer,buf,len);
++ ep2_buf = send_buffer;
++ }
++ else
++#endif
++ ep2_buf = buf;
++
++ ep2_len = len;
++ ep2_dma = dma_map_single(NULL, ep2_buf, len,DMA_TO_DEVICE);
++ PRINTKD("%s: mapped dma to buffer(%p0\n",__FUNCTION__,buf);
++
++ ep2_callback = callback;
++ ep2_remain = len;
++ ep2_curdmapos = ep2_dma;
++
++ PRINTKD("%s: calling ep2_start\n",__FUNCTION__);
++ ep2_start();
++ local_irq_restore(flags);
++
++ return 0;
++}
++/*-------------------------------------------------------------------------*/
++
++static int
++sa1100_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
++{
++ struct sa1100_udc *dev;
++ struct sa1100_ep *ep;
++ u32 max;
++ int type;
++
++ ep = container_of (_ep, struct sa1100_ep, ep);
++ if (!_ep || !desc || ep->desc || _ep->name == ep0name
++ || desc->bDescriptorType != USB_DT_ENDPOINT) {
++ PRINTKD("%s: _ep = %p, desc = %p\n",__FUNCTION__,_ep,desc);
++ if (_ep && desc)
++ PRINTKD("%s: ep->desc = %p, _ep->name = %s desc->bDescriptorType = %s\n",__FUNCTION__,ep->desc,_ep->name,
++ (desc->bDescriptorType == USB_DT_ENDPOINT) ? "USB_DT_ENDPOINT":"bad!!");
++ return -EINVAL;
++ }
++
++ dev = ep->dev;
++ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
++ return -ESHUTDOWN;
++
++ type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
++ max = le16_to_cpu (desc->wMaxPacketSize);
++ switch (max) {
++ case 64: case 32:
++ /* note: maxpacket > 16 means DMA might overrun/underrun */
++ case 16: case 8:
++ break;
++ default:
++ if (type == USB_ENDPOINT_XFER_INT && max < 64)
++ break;
++ return -EDOM;
++ }
++
++ switch (type) {
++ case USB_ENDPOINT_XFER_BULK:
++ case USB_ENDPOINT_XFER_INT:
++ if (ep == &dev->ep[2]) {
++ if (desc->bEndpointAddress != (USB_DIR_IN|2)) {
++ PRINTKD("%s: ep[2] has invalid endpoint\n",__FUNCTION__);
++ return -EINVAL;
++ }
++ tx_pktsize = max;
++ Ser0UDCOMP = max - 1;
++ PRINTKD("%s: ep2 max packet size is %d\n",__FUNCTION__,max);
++ break;
++ } else if (ep == &dev->ep[1]) {
++ if (desc->bEndpointAddress != (USB_DIR_OUT|1)) {
++ PRINTKD("%s: ep[1] has invalid endpoint\n",__FUNCTION__);
++ return -EINVAL;
++ }
++ rx_pktsize = max;
++ Ser0UDCIMP = max - 1;
++ PRINTKD("%s: ep1 max packet size is %d\n",__FUNCTION__,max);
++ break;
++ }
++ // FALLTHROUGH
++ default:
++ PRINTKD("%s: Invalid endpoint\n",__FUNCTION__);
++ return -EINVAL;
++ }
++
++ _ep->maxpacket = max;
++ ep->desc = desc;
++ ep->stopped = 0;
++
++ DEBUG (dev, "enabled %s %s max %04x\n", _ep->name,
++ type_string (desc->bmAttributes), max);
++
++ return 0;
++}
++
++static int sa1100_disable (struct usb_ep *_ep)
++{
++ struct sa1100_ep *ep;
++
++ ep = container_of (_ep, struct sa1100_ep, ep);
++ if (!_ep || !ep->desc || _ep->name == ep0name)
++ return -EINVAL;
++
++ nuke (ep, -ESHUTDOWN);
++
++ DEBUG (ep->dev, "disabled %s\n", _ep->name);
++
++ ep->desc = NULL;
++ ep->stopped = 1;
++ return 0;
++}
++
++/*-------------------------------------------------------------------------*/
++
++static struct usb_request *
++sa1100_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags)
++{
++ struct sa1100_request *req;
++
++ if (!_ep)
++ return 0;
++
++ req = kzalloc(sizeof *req, gfp_flags);
++ if (!req)
++ return 0;
++
++ memset (req, 0, sizeof *req);
++ req->req.dma = DMA_ADDR_INVALID;
++ INIT_LIST_HEAD (&req->queue);
++ return &req->req;
++}
++
++static void sa1100_free_request(struct usb_ep *_ep, struct usb_request *_req)
++{
++ struct sa1100_request *req;
++
++ req = container_of (_req, struct sa1100_request, req);
++ WARN_ON (!list_empty (&req->queue));
++ kfree(req); //NCB - see pxa2xx_udc
++}
++
++/*-------------------------------------------------------------------------*/
++
++static void done(struct sa1100_ep *ep, struct sa1100_request *req, int status)
++{
++ unsigned stopped = ep->stopped;
++
++ list_del_init (&req->queue);
++
++ if (likely(req->req.status == -EINPROGRESS))
++ req->req.status = status;
++ else
++ status = req->req.status;
++
++ if (status && status != -ESHUTDOWN)
++ VDEBUG (ep->dev, "complete %s req %p stat %d len %u/%u\n",
++ ep->ep.name, &req->req, status,
++ req->req.actual, req->req.length);
++
++ /* don't modify queue heads during completion callback */
++ ep->stopped = 1;
++ req->req.complete (&ep->ep, &req->req);
++ ep->stopped = stopped;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* FIXME move away from the old non-queued api.
++ * - forces extra work on us
++ * - stores request state twice
++ * - doesn't let gadget driver handle dma mapping
++ * - status codes need mapping
++ */
++
++static int map_status(int status)
++{
++ switch (status) {
++ case 0:
++ case -EIO: /* ep[12]_int_handler */
++ return status;
++ case -EPIPE: /* ep1_int_handler */
++ return 0;
++ // case -EAGAIN: /* ep[12]_init */
++ // case -EINTR: /* ep[12]_reset */
++ default:
++ return -ESHUTDOWN;
++ }
++}
++
++static void tx_callback(int status, int size)
++{
++ struct sa1100_ep *ep = &the_controller->ep[2];
++ struct sa1100_request *req;
++
++ if (list_empty (&ep->queue)) {
++ if (status != -EAGAIN)
++ DEBUG (ep->dev, "%s, bogus tx callback %d/%d\n",
++ ep->ep.name, status, size);
++ return;
++ }
++ req = list_entry (ep->queue.next, struct sa1100_request, queue);
++ req->req.actual = size;
++ done (ep, req, map_status (status));
++
++ if (ep->stopped || list_empty (&ep->queue))
++ return;
++ req = list_entry (ep->queue.next, struct sa1100_request, queue);
++ sa1100_usb_send (&req->req, tx_callback);
++}
++
++static void rx_callback (int status, int size)
++{
++ struct sa1100_ep *ep = &the_controller->ep[1];
++ struct sa1100_request *req;
++
++ if (list_empty (&ep->queue)) {
++ if (status != -EAGAIN)
++ DEBUG (ep->dev, "%s, bogus tx callback %d/%d\n",
++ ep->ep.name, status, size);
++ return;
++ }
++ req = list_entry (ep->queue.next, struct sa1100_request, queue);
++ req->req.actual = size;
++ done (ep, req, map_status (status));
++
++ if (ep->stopped || list_empty (&ep->queue))
++ return;
++ req = list_entry (ep->queue.next, struct sa1100_request, queue);
++ sa1100_usb_recv (&req->req, rx_callback);
++}
++
++
++static int
++sa1100_queue (struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
++{
++ struct sa1100_request *req;
++ struct sa1100_ep *ep;
++ struct sa1100_udc *dev;
++ unsigned long flags;
++
++ req = container_of (_req, struct sa1100_request, req);
++ if (!_req || !_req->complete || !_req->buf
++ || !list_empty (&req->queue))
++ return -EINVAL;
++
++ ep = container_of (_ep, struct sa1100_ep, ep);
++ if (unlikely(!_ep || (!ep->desc && _ep->name != ep0name)))
++ return -EINVAL;
++
++ dev = ep->dev;
++ if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN))
++ return -ESHUTDOWN;
++
++ // handle ep0
++ if (_ep->name == ep0name) {
++ ep0_queue( _req->buf, _req->length, dev->ep0_req_len >=0 ? dev->ep0_req_len: _req->length );
++ return 0;
++ }
++
++ /* sa1100 udc can't write zlps */
++ if (ep == &dev->ep[2] && _req->length == 0)
++ return -ERANGE;
++
++ /* the old sa1100 api doesn't use 'unsigned' for lengths */
++ if (_req->length > INT_MAX)
++ return -ERANGE;
++
++ VDEBUG (dev, "%s queue req %p, len %d buf %p\n",
++ _ep->name, _req, _req->length, _req->buf);
++
++ local_irq_save (flags);
++
++ _req->status = -EINPROGRESS;
++ _req->actual = 0;
++
++ if (list_empty (&ep->queue) && !ep->stopped) {
++ /* FIXME this does DMA mapping wrong. caller is allowed
++ * to provide buffers that don't need mapping, but this
++ * doesn't use them.
++ */
++ if (ep == &ep->dev->ep[2]) {
++ PRINTKD("%s: sa1100_usb_send buf %p length %d\n",__FUNCTION__,_req->buf,_req->length);
++ sa1100_usb_send (_req, tx_callback);
++ }
++ else if (ep == &ep->dev->ep[1]) {
++ PRINTKD("%s: sa1100_usb_recv buf %p length %d\n",__FUNCTION__,_req->buf,_req->length);
++ sa1100_usb_recv (_req, rx_callback);
++ }
++ /* ep0 rx/tx is handled separately */
++ }
++ list_add_tail (&req->queue, &ep->queue);
++
++ local_irq_restore (flags);
++
++ return 0;
++}
++
++/* dequeue ALL requests */
++static void nuke (struct sa1100_ep *ep, int status)
++{
++ struct sa1100_request *req;
++
++ /* called with irqs blocked */
++ while (!list_empty (&ep->queue)) {
++ req = list_entry (ep->queue.next,
++ struct sa1100_request,
++ queue);
++ done (ep, req, status);
++ }
++ if (ep == &ep->dev->ep[1])
++ ep1_reset ();
++ else if (ep == &ep->dev->ep[2])
++ ep2_reset ();
++}
++
++/* dequeue JUST ONE request */
++static int sa1100_dequeue (struct usb_ep *_ep, struct usb_request *_req)
++{
++ struct sa1100_ep *ep;
++ struct sa1100_request *req;
++ unsigned long flags;
++
++ ep = container_of (_ep, struct sa1100_ep, ep);
++ if (!_ep || (!ep->desc && _ep->name != ep0name) || !_req)
++ return -EINVAL;
++
++ local_irq_save (flags);
++
++ /* make sure it's actually queued on this endpoint */
++ list_for_each_entry (req, &ep->queue, queue) {
++ if (&req->req == _req)
++ break;
++ }
++ if (&req->req != _req) {
++ local_irq_restore(flags);
++ return -EINVAL;
++ }
++
++ done(ep, req, -ECONNRESET);
++
++ local_irq_restore(flags);
++
++ return 0;
++}
++
++/*-------------------------------------------------------------------------*/
++
++static int
++sa1100_set_halt (struct usb_ep *_ep, int value)
++{
++ struct sa1100_ep *ep;
++
++ ep = container_of (_ep, struct sa1100_ep, ep);
++ if (unlikely(!_ep
++ || (!ep->desc && _ep->name != ep0name))
++ || (ep->desc->bmAttributes & 0x03) == USB_ENDPOINT_XFER_ISOC)
++ return -EINVAL;
++ if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
++ return -ESHUTDOWN;
++
++ VDEBUG (ep->dev, "%s %s halt\n", _ep->name, value ? "set" : "clear");
++
++ /* set/clear, then synch memory views with the device */
++ if (value) {
++ if (ep == &ep->dev->ep[1])
++ ep1_stall ();
++ else
++ ep2_stall ();
++ } else {
++ if (ep == &ep->dev->ep[1])
++ ep1_reset ();
++ else
++ ep2_reset ();
++ }
++
++ return 0;
++}
++
++static struct usb_ep_ops sa1100_ep_ops = {
++ .enable = sa1100_enable,
++ .disable = sa1100_disable,
++
++ .alloc_request = sa1100_alloc_request,
++ .free_request = sa1100_free_request,
++
++ .queue = sa1100_queue,
++ .dequeue = sa1100_dequeue,
++
++ .set_halt = sa1100_set_halt,
++ // .fifo_status = sa1100_fifo_status,
++ // .fifo_flush = sa1100_fifo_flush,
++};
++
++/*-------------------------------------------------------------------------*/
++
++static int sa1100_get_frame (struct usb_gadget *_gadget)
++{
++ return -EOPNOTSUPP;
++}
++
++static int sa1100_wakeup (struct usb_gadget *_gadget)
++{
++ struct sa1100_udc *dev;
++
++ if (!_gadget)
++ return 0;
++ dev = container_of (_gadget, struct sa1100_udc, gadget);
++
++ // FIXME
++
++ return 0;
++}
++
++static const struct usb_gadget_ops sa1100_ops = {
++ .get_frame = sa1100_get_frame,
++ .wakeup = sa1100_wakeup,
++
++ // .set_selfpowered = sa1100_set_selfpowered,
++};
++
++/*-------------------------------------------------------------------------*/
++
++static inline void enable_resume_mask_suspend (void)
++{
++ int i = 0;
++
++ while (1) {
++ Ser0UDCCR |= UDCCR_SUSIM; // mask future suspend events
++ udelay (i);
++ if ( (Ser0UDCCR & UDCCR_SUSIM) || (Ser0UDCSR & UDCSR_RSTIR))
++ break;
++ if (++i == 50) {
++ WARN_ (&the_controller, "%s Could not set SUSIM %8.8lX\n",
++ __FUNCTION__, Ser0UDCCR);
++ break;
++ }
++ }
++
++ i = 0;
++ while (1) {
++ Ser0UDCCR &= ~UDCCR_RESIM;
++ udelay (i);
++ if ( (Ser0UDCCR & UDCCR_RESIM) == 0
++ || (Ser0UDCSR & UDCSR_RSTIR))
++ break;
++ if (++i == 50) {
++ WARN_ (&the_controller, "%s Could not clear RESIM %8.8lX\n",
++ __FUNCTION__, Ser0UDCCR);
++ break;
++ }
++ }
++}
++
++static inline void enable_suspend_mask_resume (void)
++{
++ int i = 0;
++ while (1) {
++ Ser0UDCCR |= UDCCR_RESIM; // mask future resume events
++ udelay (i);
++ if (Ser0UDCCR & UDCCR_RESIM || (Ser0UDCSR & UDCSR_RSTIR))
++ break;
++ if (++i == 50) {
++ WARN_ (&the_controller, "%s could not set RESIM %8.8lX\n",
++ __FUNCTION__, Ser0UDCCR);
++ break;
++ }
++ }
++ i = 0;
++ while (1) {
++ Ser0UDCCR &= ~UDCCR_SUSIM;
++ udelay (i);
++ if ( (Ser0UDCCR & UDCCR_SUSIM) == 0
++ || (Ser0UDCSR & UDCSR_RSTIR))
++ break;
++ if (++i == 50) {
++ WARN_ (&the_controller, "%s Could not clear SUSIM %8.8lX\n",
++ __FUNCTION__, Ser0UDCCR);
++ break;
++ }
++ }
++}
++
++// HACK DEBUG 3Mar01ww
++// Well, maybe not, it really seems to help! 08Mar01ww
++static void core_kicker (void)
++{
++ u32 car = Ser0UDCAR;
++ u32 imp = Ser0UDCIMP;
++ u32 omp = Ser0UDCOMP;
++
++ UDC_set (Ser0UDCCR, UDCCR_UDD);
++ udelay (300);
++ UDC_clear (Ser0UDCCR, UDCCR_UDD);
++
++ Ser0UDCAR = car;
++ Ser0UDCIMP = imp;
++ Ser0UDCOMP = omp;
++}
++
++static irqreturn_t udc_int_hndlr(int irq, void *_dev)
++{
++ struct sa1100_udc *dev = _dev;
++ u32 status = Ser0UDCSR;
++
++ PRINTKD("%s: status = 0x%x and control = 0x%lx\n", __FUNCTION__,
++ status, Ser0UDCCR);
++ /* ReSeT Interrupt Request - UDC has been reset */
++ if (status & UDCSR_RSTIR) {
++ PRINTKD("%s: processing UDCSR_RSTIR\n", __FUNCTION__);
++ if (usbctl_next_state_on_event(kEvReset) != kError) {
++ /* starting 20ms or so reset sequence now... */
++ INFO (dev, "Resetting\n");
++ ep0_reset(); // just set state to idle
++ ep1_reset(); // flush dma, clear false stall
++ ep2_reset(); // flush dma, clear false stall
++ }
++ // mask reset ints, they flood during sequence, enable
++ // suspend and resume
++ UDC_set(Ser0UDCCR, UDCCR_REM); // mask reset
++ UDC_clear(Ser0UDCCR, (UDCCR_SUSIM | UDCCR_RESIM)); // enable suspend and resume
++ UDC_flip(Ser0UDCSR, status); // clear all pending sources
++ PRINTKD("%s: setting USB_FULL_SPEED\n",__FUNCTION__);
++ dev->gadget.speed = USB_SPEED_FULL;
++ return IRQ_HANDLED; // NCB
++ }
++
++ /* else we have done something other than reset,
++ * so be sure reset enabled
++ */
++ UDC_clear(Ser0UDCCR, UDCCR_REM);
++
++ /* RESume Interrupt Request */
++ if (status & UDCSR_RESIR) {
++ struct usb_gadget_driver *driver = dev->driver;
++
++ PRINTKD("%s: processing UDCSR_RESIR\n",__FUNCTION__);
++ if (driver->resume)
++ driver->resume (&dev->gadget);
++ core_kicker ();
++ enable_suspend_mask_resume ();
++ }
++
++ /* SUSpend Interrupt Request */
++ if (status & UDCSR_SUSIR) {
++ struct usb_gadget_driver *driver = dev->driver;
++
++ PRINTKD("%s: processing UDCSR_SUSIR\n",__FUNCTION__);
++ if (driver->suspend)
++ driver->suspend (&dev->gadget);
++ enable_resume_mask_suspend ();
++ }
++
++ UDC_flip(Ser0UDCSR, status); // clear all pending sources
++
++ if (status & UDCSR_EIR)
++ PRINTKD("%s: processing ep0_int_hndlr\n",__FUNCTION__);
++ ep0_int_hndlr();
++
++ if (status & UDCSR_RIR) {
++ PRINTKD("%s: processing ep1_int_hndlr\n",__FUNCTION__);
++ ep1_int_hndlr(status);
++ }
++ if (status & UDCSR_TIR) {
++ PRINTKD("%s: processing ep2_int_hndlr\n",__FUNCTION__);
++ ep2_int_hndlr(status);
++ }
++
++ return IRQ_HANDLED; // NCB
++}
++
++/* soft_connect_hook ()
++ * Some devices have platform-specific circuitry to make USB
++ * not seem to be plugged in, even when it is. This allows
++ * software to control when a device 'appears' on the USB bus
++ * (after Linux has booted and this driver has loaded, for
++ * example). If you have such a circuit, control it here.
++ */
++#ifdef CONFIG_SA1100_EXTENEX1
++static void soft_connect_hook(int enable)
++{
++ if (machine_is_extenex1 ()) {
++ if (enable) {
++ PPDR |= PPC_USB_SOFT_CON;
++ PPSR |= PPC_USB_SOFT_CON;
++ } else {
++ PPSR &= ~PPC_USB_SOFT_CON;
++ PPDR &= ~PPC_USB_SOFT_CON;
++ }
++ }
++}
++#elif defined(CONFIG_SA1100_BALLOON)
++static void soft_connect_hook(int enable)
++{
++ if (machine_is_balloon()) {
++ if (enable)
++ balloon_cpld_control(BALLOON_UDC_DISCONNECT, 0);
++ else
++ balloon_cpld_control(BALLOON_UDC_DISCONNECT, 1);
++ }
++}
++#elif defined(CONFIG_SA1100_COLLIE)
++static int collie_usb_init(void)
++{
++ int rc;
++ rc = gpio_request(COLLIE_GPIO_LB_VOL_CHG, "usb enable");
++ if (rc)
++ return rc;
++
++ rc = gpio_direction_output(COLLIE_GPIO_LB_VOL_CHG, 1);
++ if (rc)
++ gpio_free(COLLIE_GPIO_LB_VOL_CHG);
++
++ return rc;
++}
++
++static void collie_set_usb(int enable)
++{
++ gpio_set_value(COLLIE_GPIO_LB_VOL_CHG, enable);
++}
++
++static void collie_usb_exit(void)
++{
++ gpio_free(COLLIE_GPIO_LB_VOL_CHG);
++}
++
++static void soft_connect_hook(int enable)
++{
++ collie_set_usb(enable);
++}
++#else
++#define soft_connect_hook(x) do { } while (0);
++#endif
++
++/* "function" sysfs attribute */
++static ssize_t
++show_function(struct device *_dev, struct device_attribute *attr, char *buf)
++{
++ struct sa1100_udc *dev = dev_get_drvdata (_dev);
++
++ if (!dev->driver
++ || !dev->driver->function
++ || strlen(dev->driver->function) > PAGE_SIZE)
++ return 0;
++ return scnprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function);
++}
++static DEVICE_ATTR(function, S_IRUGO, show_function, NULL);
++
++/* disable the UDC at the source */
++static void udc_disable(struct sa1100_udc *dev)
++{
++ soft_connect_hook(0);
++ UDC_set(Ser0UDCCR, UDCCR_UDD);
++ dev->gadget.speed = USB_SPEED_UNKNOWN;
++ ep0_idle(dev);
++}
++
++static void udc_reinit(struct sa1100_udc *dev)
++{
++ u32 i;
++
++ /* Initialize the gadget controller data structure */
++ INIT_LIST_HEAD(&dev->gadget.ep_list);
++ INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
++ ep0_idle(dev);
++ for ( i = 0 ; i < 3 ; i++) {
++ struct sa1100_ep *ep = &dev->ep[i];
++ if (i != 0)
++ list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);
++ ep->desc = NULL;
++ ep->stopped = 0;
++ INIT_LIST_HEAD(&ep->queue);
++ }
++}
++
++/* enable the udc at the source */
++static void udc_enable(struct sa1100_udc *dev)
++{
++ UDC_clear (Ser0UDCCR, UDCCR_UDD);
++ ep0_idle(dev);
++}
++
++static void ep0_start(struct sa1100_udc *dev)
++{
++ udc_enable(dev);
++ udelay(100);
++
++ /* clear stall - receiver seems to start stalled? 19Jan01ww */
++ /* also clear other stuff just to be thurough 22Feb01ww */
++ UDC_clear(Ser0UDCCS1, UDCCS1_FST | UDCCS1_RPE | UDCCS1_RPC );
++ UDC_clear(Ser0UDCCS2, UDCCS2_FST | UDCCS2_TPE | UDCCS2_TPC );
++
++ /* mask everything */
++ Ser0UDCCR = 0xFC;
++
++ /* flush DMA and fire through some -EAGAINs */
++ ep1_init(dev->ep[1].dmaregs);
++ ep2_init(dev->ep[2].dmaregs);
++
++ /* enable any platform specific hardware */
++ soft_connect_hook(1);
++
++ /* clear all top-level sources */
++ Ser0UDCSR = UDCSR_RSTIR | UDCSR_RESIR | UDCSR_EIR |
++ UDCSR_RIR | UDCSR_TIR | UDCSR_SUSIR ;
++
++ /* EXERIMENT - a short line in the spec says toggling this
++ * bit diddles the internal state machine in the udc to
++ * expect a suspend
++ */
++ Ser0UDCCR |= UDCCR_RESIM;
++ /* END EXPERIMENT 10Feb01ww */
++
++ /* enable any platform specific hardware */
++ soft_connect_hook(1);
++
++ /* Enable interrupts. If you are unplugged you will immediately
++ * get a suspend interrupt. If you are plugged and have a soft
++ * connect-circuit, you will get a reset. If you are plugged
++ * without a soft-connect, I think you also get suspend. In short,
++ * start with suspend masked and everything else enabled
++ */
++ UDC_write(Ser0UDCCR, UDCCR_SUSIM);
++}
++
++
++/* when a driver is successfully registered, it will receive
++ * control requests including set_configuration(), which enables
++ * non-control requests. then usb traffic follows until a
++ * disconnect is reported. then a host may connect again, or
++ * the driver might get unbound.
++ */
++int usb_gadget_register_driver(struct usb_gadget_driver *driver)
++{
++ struct sa1100_udc *dev = the_controller;
++ int retval;
++
++ if (!driver || !driver->bind || !driver->setup)
++ return -EINVAL;
++ if (!dev)
++ return -ENODEV;
++ if (dev->driver)
++ return -EBUSY;
++
++ /* hook up the driver ... */
++ dev->driver = driver;
++ dev->gadget.dev.driver = &driver->driver;
++
++ retval = device_add(&dev->gadget.dev);
++ if (retval != 0) {
++ printk(KERN_ERR "Error in device_add() : %d\n",retval);
++ goto register_error;
++ }
++
++ retval = driver->bind (&dev->gadget);
++ if (retval != 0) {
++ DEBUG(dev, "bind to driver %s --> %d\n",
++ driver->driver.name, retval);
++ device_del(&dev->gadget.dev);
++ goto register_error;
++ }
++
++ retval = device_create_file(dev->dev, &dev_attr_function);
++
++ /* ... then enable host detection and ep0; and we're ready
++ * for set_configuration as well as eventual disconnect.
++ */
++ ep0_start(dev);
++
++ DEBUG(dev, "%s ready\n", driver->driver.name);
++
++ return 0;
++
++register_error:
++ dev->driver = NULL;
++ dev->gadget.dev.driver = NULL;
++ return retval;
++}
++EXPORT_SYMBOL (usb_gadget_register_driver);
++
++static void
++stop_activity(struct sa1100_udc *dev, struct usb_gadget_driver *driver)
++{
++ int i;
++
++ /* don't disconnect if it's not connected */
++ if (dev->gadget.speed == USB_SPEED_UNKNOWN)
++ driver = NULL;
++ dev->gadget.speed = USB_SPEED_UNKNOWN;
++
++ /* mask everything */
++ Ser0UDCCR = 0xFC;
++
++ /* stop hardware; prevent new request submissions;
++ * and kill any outstanding requests.
++ */
++ for (i = 0; i < 3; i++) {
++ struct sa1100_ep *ep = &dev->ep[i];
++ ep->stopped = 1;
++ nuke(ep, -ESHUTDOWN);
++ }
++ udc_disable (dev);
++
++ /* report disconnect; the driver is already quiesced */
++ if (driver)
++ driver->disconnect(&dev->gadget);
++
++ /* re-init driver-visible data structures */
++ udc_reinit(dev);
++}
++
++int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
++{
++ struct sa1100_udc *dev = the_controller;
++
++ if (!dev)
++ return -ENODEV;
++ if (!driver || driver != dev->driver)
++ return -EINVAL;
++
++ local_irq_disable();
++ stop_activity (dev, driver);
++ local_irq_enable();
++ if (driver->unbind)
++ driver->unbind(&dev->gadget);
++ dev->driver = 0;
++
++ device_del(&dev->gadget.dev);
++ device_remove_file(dev->dev, &dev_attr_function);
++
++ DEBUG (dev, "unregistered driver '%s'\n", driver->driver.name);
++ return 0;
++}
++EXPORT_SYMBOL (usb_gadget_unregister_driver);
++
++
++/*-------------------------------------------------------------------------*/
++
++/*-------------------------------------------------------------------------*/
++
++//////////////////////////////////////////////////////////////////////////////
++// Proc Filesystem Support
++//////////////////////////////////////////////////////////////////////////////
++
++#if CONFIG_PROC_FS
++
++#define SAY(fmt,args...) p += sprintf (p, fmt, ## args)
++#define SAYV(num) p += sprintf (p, num_fmt, "Value", num)
++#define SAYC(label,yn) p += sprintf (p, yn_fmt, label, yn)
++#define SAYS(label,v) p += sprintf (p, cnt_fmt, label, v)
++
++static int usbctl_read_proc (char *page, char **start, off_t off,
++ int count, int *eof, void *data)
++{
++ const char * num_fmt = "%25.25s: %8.8lX\n";
++ const char * cnt_fmt = "%25.25s: %lu\n";
++ const char * yn_fmt = "%25.25s: %s\n";
++ const char * yes = "YES";
++ const char * no = "NO";
++ unsigned long v;
++ char * p = page;
++ int len;
++
++ SAY ("SA1100 USB Controller Core\n");
++
++ SAYS ("ep0 bytes read", usbd_info.stats.ep0_bytes_read);
++ SAYS ("ep0 bytes written", usbd_info.stats.ep0_bytes_written);
++ SAYS ("ep0 FIFO read failures", usbd_info.stats.ep0_fifo_read_failures);
++ SAYS ("ep0 FIFO write failures", usbd_info.stats.ep0_fifo_write_failures);
++
++ SAY ("\n");
++
++ v = Ser0UDCAR;
++ SAY ("%25.25s: 0x%8.8lX - %ld\n", "Address Register", v, v);
++ v = Ser0UDCIMP;
++ SAY ("%25.25s: %ld (%8.8lX)\n", "IN max packet size", v+1, v);
++ v = Ser0UDCOMP;
++ SAY ("%25.25s: %ld (%8.8lX)\n", "OUT max packet size", v+1, v);
++
++ v = Ser0UDCCR;
++ SAY ("\nUDC Mask Register\n");
++ SAYV (v);
++ SAYC ("UDC Active", (v & UDCCR_UDA) ? yes : no);
++ SAYC ("Suspend interrupts masked", (v & UDCCR_SUSIM) ? yes : no);
++ SAYC ("Resume interrupts masked", (v & UDCCR_RESIM) ? yes : no);
++ SAYC ("Reset interrupts masked", (v & UDCCR_REM) ? yes : no);
++
++ v = Ser0UDCSR;
++ SAY ("\nUDC Interrupt Request Register\n");
++ SAYV (v);
++ SAYC ("Reset pending", (v & UDCSR_RSTIR) ? yes : no);
++ SAYC ("Suspend pending", (v & UDCSR_SUSIR) ? yes : no);
++ SAYC ("Resume pending", (v & UDCSR_RESIR) ? yes : no);
++ SAYC ("ep0 pending", (v & UDCSR_EIR) ? yes : no);
++ SAYC ("receiver pending", (v & UDCSR_RIR) ? yes : no);
++ SAYC ("tramsitter pending", (v & UDCSR_TIR) ? yes : no);
++
++#ifdef CONFIG_SA1100_EXTENEX1
++ SAYC ("\nSoft connect", (PPSR & PPC_USB_SOFT_CON) ? "Visible" : "Hidden");
++#endif
++
++#if 1
++ SAY ("\nDMA Tx registers\n");
++ {
++ dma_regs_t *r=the_controller->ep[2].dmaregs;
++ SAY (" DDAR");
++ SAYV(r->DDAR);
++ SAY (" DCSR");
++ SAYV(r->RdDCSR);
++ SAY (" DBSA (address buf A) ");
++ SAYV(r->DBSA);
++ SAY (" DBTA (transfer count A) ");
++ SAYV(r->DBTA);
++ SAY (" DBSB (address buf B) ");
++ SAYV(r->DBSB);
++ SAY (" DBTB (transfer count B) ");
++ SAYV(r->DBTB);
++
++ }
++ SAY ("\nDMA Rx registers\n");
++ {
++ dma_regs_t *r=the_controller->ep[1].dmaregs;
++ SAY (" DDAR");
++ SAYV(r->DDAR);
++ SAY (" DCSR");
++ SAYV(r->RdDCSR);
++ SAY (" DBSA (address buf A) ");
++ SAYV(r->DBSA);
++ SAY (" DBTA (transfer count A) ");
++ SAYV(r->DBTA);
++ SAY (" DBSB (address buf B) ");
++ SAYV(r->DBSB);
++ SAY (" DBTB (transfer count B) ");
++ SAYV(r->DBTB);
++
++ }
++#endif
++#if 1
++ v = Ser0UDCCS0;
++ SAY ("\nUDC Endpoint Zero Status Register\n");
++ SAYV (v);
++ SAYC ("Out Packet Ready", (v & UDCCS0_OPR) ? yes : no);
++ SAYC ("In Packet Ready", (v & UDCCS0_IPR) ? yes : no);
++ SAYC ("Sent Stall", (v & UDCCS0_SST) ? yes : no);
++ SAYC ("Force Stall", (v & UDCCS0_FST) ? yes : no);
++ SAYC ("Data End", (v & UDCCS0_DE) ? yes : no);
++ SAYC ("Data Setup End", (v & UDCCS0_SE) ? yes : no);
++ SAYC ("Serviced (SO)", (v & UDCCS0_SO) ? yes : no);
++
++ v = Ser0UDCCS1;
++ SAY ("\nUDC Receiver Status Register\n");
++ SAYV (v);
++ SAYC ("Receive Packet Complete", (v & UDCCS1_RPC) ? yes : no);
++ SAYC ("Sent Stall", (v & UDCCS1_SST) ? yes : no);
++ SAYC ("Force Stall", (v & UDCCS1_FST) ? yes : no);
++ SAYC ("Receive Packet Error", (v & UDCCS1_RPE) ? yes : no);
++ SAYC ("Receive FIFO not empty", (v & UDCCS1_RNE) ? yes : no);
++
++ v = Ser0UDCCS2;
++ SAY ("\nUDC Transmitter Status Register\n");
++ SAYV (v);
++ SAYC ("FIFO has < 8 of 16 chars", (v & UDCCS2_TFS) ? yes : no);
++ SAYC ("Transmit Packet Complete", (v & UDCCS2_TPC) ? yes : no);
++ SAYC ("Transmit FIFO underrun", (v & UDCCS2_TUR) ? yes : no);
++ SAYC ("Transmit Packet Error", (v & UDCCS2_TPE) ? yes : no);
++ SAYC ("Sent Stall", (v & UDCCS2_SST) ? yes : no);
++ SAYC ("Force Stall", (v & UDCCS2_FST) ? yes : no);
++#endif
++
++ len = (p - page) - off;
++ if (len < 0)
++ len = 0;
++ *eof = (len <=count) ? 1 : 0;
++ *start = page + off;
++ return len;
++}
++
++static inline void register_proc_entry (void)
++{
++ create_proc_read_entry (driver_name, 0, NULL,
++ usbctl_read_proc, NULL);
++}
++
++static inline void unregister_proc_entry (void)
++{
++ remove_proc_entry (driver_name, NULL);
++}
++
++#else
++
++#define register_proc_entry() do {} while (0)
++#define unregister_proc_entry() do {} while (0)
++
++#endif /* CONFIG_PROC_FS */
++
++/*-------------------------------------------------------------------------*/
++
++MODULE_DESCRIPTION ("sa1100_udc");
++MODULE_AUTHOR ("Various");
++MODULE_LICENSE ("GPL");
++
++static struct sa1100_udc memory = {
++ .gadget = {
++ .ops = &sa1100_ops,
++ .ep0 = &memory.ep[0].ep,
++ .name = driver_name,
++ .dev = {
++ .bus_id = "gadget",
++ },
++ },
++
++ /* control endpoint */
++ .ep[0] = {
++ .ep = {
++ .name = ep0name,
++ .ops = &sa1100_ep_ops,
++ .maxpacket = EP0_FIFO_SIZE,
++ },
++ .dev = &memory,
++ },
++
++ /* first group of endpoints */
++ .ep[1] = {
++ .ep = {
++ .name = "ep1out-bulk",
++ .ops = &sa1100_ep_ops,
++ .maxpacket = BULK_FIFO_SIZE,
++ },
++ .dev = &memory,
++ },
++ .ep[2] = {
++ .ep = {
++ .name = "ep2in-bulk",
++ .ops = &sa1100_ep_ops,
++ .maxpacket = BULK_FIFO_SIZE,
++ },
++ .dev = &memory,
++ }
++};
++
++static int __init sa1100_udc_probe(struct device *_dev)
++{
++ struct sa1100_udc *dev = &memory;
++ int retval = 0;
++
++ /* setup dev */
++ dev->dev = _dev;
++// dev->mach = _dev->platform_data;
++
++ device_initialize(&dev->gadget.dev);
++ dev->gadget.dev.parent = _dev;
++ dev->gadget.dev.dma_mask = _dev->dma_mask;
++
++ the_controller = dev;
++ dev_set_drvdata(_dev, dev);
++
++ /* controller stays disabled until gadget driver is bound */
++ udc_disable(dev);
++ udc_reinit(dev);
++
++// spin_lock_init(&the_udc.lock);
++ register_proc_entry();
++
++#if defined(CONFIG_SA1100_COLLIE)
++ collie_usb_init();
++#endif
++
++ /* setup dma channels and IRQ */
++ retval = sa1100_request_dma(DMA_Ser0UDCRd, "USB receive",
++ NULL, NULL, &dev->ep[1].dmaregs);
++ if (retval) {
++ ERROR(dev, "couldn't get rx dma, err %d\n", retval);
++ goto err_rx_dma;
++ }
++ retval = sa1100_request_dma(DMA_Ser0UDCWr, "USB transmit",
++ NULL, NULL, &dev->ep[2].dmaregs);
++ if (retval) {
++ ERROR(dev, "couldn't get tx dma, err %d\n", retval);
++ goto err_tx_dma;
++ }
++ retval = request_irq(IRQ_Ser0UDC, udc_int_hndlr, IRQF_DISABLED,
++ driver_name, dev);
++ if (retval) {
++ ERROR(dev, "couldn't get irq, err %d\n", retval);
++ goto err_irq;
++ }
++
++ INFO(dev, "initialized, rx %p tx %p irq %d\n",
++ dev->ep[1].dmaregs, dev->ep[2].dmaregs, IRQ_Ser0UDC);
++ return 0;
++
++err_irq:
++ sa1100_free_dma(dev->ep[2].dmaregs);
++ usbd_info.dmaregs_rx = 0;
++err_tx_dma:
++ sa1100_free_dma(dev->ep[1].dmaregs);
++ usbd_info.dmaregs_tx = 0;
++err_rx_dma:
++ return retval;
++}
++
++static int __exit sa1100_udc_remove(struct device *_dev)
++{
++ struct sa1100_udc *dev = dev_get_drvdata(_dev);
++
++ udc_disable(dev);
++ unregister_proc_entry();
++ usb_gadget_unregister_driver(dev->driver);
++ sa1100_free_dma(dev->ep[1].dmaregs);
++ sa1100_free_dma(dev->ep[2].dmaregs);
++ free_irq(IRQ_Ser0UDC, dev);
++ dev_set_drvdata(_dev,NULL);
++ the_controller = NULL;
++#if defined(CONFIG_SA1100_COLLIE)
++ collie_usb_exit();
++#endif
++ return 0;
++}
++
++static struct device_driver udc_driver = {
++ .name = "sa11x0-udc",
++ .bus = &platform_bus_type,
++ .probe = sa1100_udc_probe,
++ .remove = __exit_p(sa1100_udc_remove),
++// .suspend = sa1100_udc_suspend,
++// .resume = sa1100_udc_resume,
++ .owner = THIS_MODULE,
++};
++
++static int __init udc_init(void)
++{
++ printk(KERN_INFO "%s: version %s\n", driver_name, DRIVER_VERSION);
++#ifdef NCB_DMA_FIX
++ send_buffer = (char*) kzalloc(SEND_BUFFER_SIZE, GFP_KERNEL | GFP_DMA );
++ receive_buffer = (char*) kzalloc(RECEIVE_BUFFER_SIZE, GFP_KERNEL | GFP_DMA );
++#endif
++ return driver_register(&udc_driver);
++}
++module_init(udc_init);
++
++static void __exit udc_exit(void)
++{
++#ifdef NCB_DMA_FIX
++ if (send_buffer) {
++ kfree(send_buffer);
++ send_buffer = NULL;
++ }
++ if (receive_buffer) {
++ kfree(receive_buffer);
++ receive_buffer = NULL;
++ }
++#endif
++ driver_unregister(&udc_driver);
++}
++module_exit(udc_exit);
+diff --git a/drivers/usb/gadget/sa1100_udc.h b/drivers/usb/gadget/sa1100_udc.h
+new file mode 100644
+index 0000000..86fa28d
+--- /dev/null
++++ b/drivers/usb/gadget/sa1100_udc.h
+@@ -0,0 +1,94 @@
++/*
++ * internals of "new style" UDC controller
++ * <linux/usb_gadget.h> replaces ARM-specific "sa1100_usb.h".
++ */
++
++struct sa1100_ep {
++ struct usb_ep ep;
++ struct sa1100_udc *dev;
++ //unsigned long irqs;
++
++ const struct usb_endpoint_descriptor *desc;
++ struct list_head queue;
++ dma_regs_t *dmaregs;
++ unsigned stopped : 1;
++};
++
++struct sa1100_request {
++ struct usb_request req;
++ struct list_head queue;
++// NCB unsigned mapped : 1;
++};
++
++enum ep0_state {
++ EP0_IDLE,
++ EP0_IN_DATA_PHASE,
++ EP0_OUT_DATA_PHASE,
++ EP0_END_XFER,
++ EP0_STALL,
++};
++
++#define EP0_FIFO_SIZE ((unsigned)8)
++#define BULK_FIFO_SIZE ((unsigned)64)
++//#define ISO_FIFO_SIZE ((unsigned)256)
++//#define INT_FIFO_SIZE ((unsigned)8)
++
++struct udc_stats {
++ struct ep0stats {
++ unsigned long ops;
++ unsigned long bytes;
++ } read, write;
++ unsigned long irqs;
++};
++
++struct sa1100_udc {
++ struct usb_gadget gadget;
++ struct usb_gadget_driver *driver;
++ struct device *dev;
++ enum ep0_state ep0state;
++ struct udc_stats stats;
++// NCB spinlock_t lock;
++// NCB dma_regs_t *dmaregs_tx, *dmaregs_rx;
++ unsigned got_irq : 1,
++ vbus : 1,
++ pullup : 1,
++ has_cfr : 1,
++ req_pending : 1,
++ req_std : 1,
++ req_config : 1;
++ struct timer_list timer;
++ u64 dma_mask;
++ unsigned char address;
++ struct sa1100_ep ep[3];
++ int ep0_req_len;
++};
++
++/*-------------------------------------------------------------------------*/
++
++#define xprintk(dev,level,fmt,args...) \
++ printk(level "%s: " fmt , driver_name , ## args)
++
++#ifdef DEBUG
++#undef DEBUG
++#define DEBUG(dev,fmt,args...) \
++ xprintk(dev , KERN_DEBUG , fmt , ## args)
++#else
++#define DEBUG(dev,fmt,args...) \
++ do { } while (0)
++#endif /* DEBUG */
++
++#ifdef VERBOSE
++#define VDEBUG DEBUG
++#else
++#define VDEBUG(dev,fmt,args...) \
++ do { } while (0)
++#endif /* VERBOSE */
++
++#define ERROR(dev,fmt,args...) \
++ xprintk(dev , KERN_ERR , fmt , ## args)
++#define WARN_(dev,fmt,args...) \
++ xprintk(dev , KERN_WARNING , fmt , ## args)
++#define INFO(dev,fmt,args...) \
++ xprintk(dev , KERN_INFO , fmt , ## args)
++
++/*-------------------------------------------------------------------------*/
+--
+1.5.6.5
+