From 923ac0a48c2a064e4639b0fa53dbd0a18d87043e Mon Sep 17 00:00:00 2001 From: Thomas Kunze 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 +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#if CONFIG_PROC_FS +#include +#endif + +#if defined(CONFIG_SA1100_BALLOON) +#include +#endif + +#if defined(CONFIG_SA1100_COLLIE) +#include +#include +#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 +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= 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 + * 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