www.pudn.com > Linux2410_device.rar > sa1100.c


/*
 * linux/drivers/usbd/sa1100_bi/udc.c
 *
 * Copyright (c) 2000, 2001, 2002 Lineo
 * Copyright (c) 2001 Hewlett Packard
 *
 * By: 
 *      Stuart Lynne , 
 *      Tom Rushworth , 
 *      Bruce Balden 
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

/*
 * Please note that this driver works reasonably well with StrongARM parts
 * running at 206Mhz. 
 *
 * It is not possible to run the USB with DMA on the StrongARM running at 133Mhz.
 * Running without DMA is possible and reasonably reliable. This can be done by
 * restricting the USB packetsize to 16bytes for your bulk endpoints.
 */ 



#include 
#include 

#include "../usbd-export.h"
#include "../usbd-build.h"
#include "../usbd-module.h"

MODULE_AUTHOR ("sl@lineo.com, tbr@lineo.com");
MODULE_DESCRIPTION ("USB Device SA-1100 Bus Interface");

USBD_MODULE_INFO ("sa1100_bi 0.2");

#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 

#include 
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,6)
#define USE_ADD_DEL_TIMER_FOR_USBADDR_CHECK 1
#include 
#else
#undef USE_ADD_DEL_TIMER_FOR_USBADDR_CHECK
#include 
#endif

#include 
#include 
#include 
#include 


#include 
#include 
#include 
#include 
#include 

#include 

#include 
#include 
#include 
#include 


#include "../usbd-debug.h"

#include "../usbd.h"
#include "../usbd-func.h"
#include "../usbd-bus.h"
#include "../usbd-inline.h"
#include "usbd-bi.h"

#include "sa1100.h"


#if defined(CONFIG_SA1100_COLLIE)       // XXX change to 5500
#include 
#include 
#include 
#endif



#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define MAX(a,b) ((a) > (b) ? (a) : (b))

#define ACTIVEA 1
#define ACTIVEB 2

static struct usb_device_instance *udc_device;	// required for the interrupt handler

/*
 * ep_endpoints - map physical endpoints to logical endpoints
 */
static struct usb_endpoint_instance *ep_endpoints[UDC_MAX_ENDPOINTS];

//static struct urb ep0_urb;
unsigned char usb_address;

extern unsigned int udc_interrupts;
extern unsigned int udc_interrupts_last;

unsigned int ep0_interrupts;
unsigned int tx_interrupts;
unsigned int rx_interrupts;
unsigned int sus_interrupts;
unsigned int res_interrupts;
unsigned int udc_address_errors;
unsigned int udc_ticks;
unsigned int udc_fixed;

unsigned int ep0_interrupts_last;
unsigned int rx_interrupts_last;
unsigned int tx_interrupts_last;

unsigned int udc_rpe_errors;
unsigned int udc_ep1_errors;
unsigned int udc_ep2_errors;
unsigned int udc_ep2_tpe;
unsigned int udc_ep2_tur;
unsigned int udc_ep2_sst;
unsigned int udc_ep2_fst;




int usbd_rcv_dma;
int usbd_tx_dma;

static int udc_saw_sof;

#ifdef USE_ADD_DEL_TIMER_FOR_USBADDR_CHECK
static struct timer_list sa1100_usb_dev_addr_check;
static int usb_addr_check_initialized = 0;
#define CHECK_INTERVAL   1
#else
static struct tq_struct sa1100_tq;
#endif


/*
 * DMA control register structure
 */
typedef struct {
	volatile u_long DDAR;
	volatile u_long SetDCSR;
	volatile u_long ClrDCSR;
	volatile u_long RdDCSR;
	volatile dma_addr_t DBSA;
	volatile u_long DBTA;
	volatile dma_addr_t DBSB;
	volatile u_long DBTB;
} dma_regs_t;

/* ********************************************************************************************* */
/* IO
 */

volatile int udc (volatile unsigned int *regaddr)
{
	volatile unsigned int value;
	int ok;
	for (ok = 1000, value = *(regaddr); value != *(regaddr) && ok--; value = *(regaddr));
	if (!ok) {
		dbg_udc (0, "NOT OK: %p %x", regaddr, value);
	}
	return value;
}

static __inline__ volatile int _udc (volatile unsigned int *regaddr)
{
	volatile unsigned int value;
	int ok;
	for (ok = 1000, value = *(regaddr); value != *(regaddr) && ok--; value = *(regaddr));
	if (!ok) {
		printk (KERN_ERR "NOT OK: %p %x\n", regaddr, value);
	}
	return value;
}


static __inline__ void _sa1100_bi_dma_run (dmach_t channel, int active)
{
	dma_regs_t *regs = (dma_regs_t *) io_p2v (_DDAR (channel));
	if (active == ACTIVEA) {
		regs->SetDCSR = DCSR_STRTA | DCSR_RUN;
	} else {
		regs->SetDCSR = DCSR_STRTB | DCSR_RUN;
	}
}

static __inline__ int _sa1100_bi_dma_queue_buffer_irq (dmach_t channel, dma_addr_t data, int size)
{
	int status;
	dma_regs_t *regs = (dma_regs_t *) io_p2v (_DDAR (channel));

	status = regs->RdDCSR;
	if (((status & DCSR_BIU) && (status & DCSR_STRTB)) || (!(status & DCSR_BIU) && !(status & DCSR_STRTA))) {
		regs->ClrDCSR = DCSR_DONEA | DCSR_STRTA;
		regs->DBSA = data;
		regs->DBTA = size;
		// Once is good, twice is better....
		regs->DBSA = data;
		regs->DBTA = size;
		return ACTIVEA;
	} else {
		regs->ClrDCSR = DCSR_DONEB | DCSR_STRTB;
		regs->DBSB = data;
		regs->DBTB = size;
		// Once is good, twice is better....
		regs->DBSB = data;
		regs->DBTB = size;
		return ACTIVEB;
	}
}

static __inline__ int _sa1100_bi_dma_flush_all_irq (dmach_t channel)
{
	dma_regs_t *regs = (dma_regs_t *) io_p2v (_DDAR (channel));
	regs->ClrDCSR = DCSR_STRTA | DCSR_STRTB | DCSR_RUN | DCSR_IE;
	return 0;
}

static __inline__ int _sa1100_bi_dma_stop_get_current_irq (dmach_t channel, dma_addr_t * addr, int active)
{
	dma_regs_t *regs = (dma_regs_t *) io_p2v (_DDAR (channel));

	// addr sometimes can be set incorrectly, the caller must do bounds checking
	switch (active) {
	case ACTIVEA:
		regs->ClrDCSR = DCSR_RUN | DCSR_STRTA | DCSR_RUN | DCSR_IE;
		*addr = regs->DBSA;	// not reliable
		return 0;
	case ACTIVEB:
		regs->ClrDCSR = DCSR_RUN | DCSR_STRTB | DCSR_RUN | DCSR_IE;
		*addr = regs->DBSB;	// not reliable
		return 0;
	default:
		*addr = 0;
		return -ENXIO;
	}
}



/* ********************************************************************************************* */


static u32 getCPUID (char **stepping)
{
	u32 cpuID;

	__asm__ __volatile__ (" mrc   p15, 0, %0, c0, c0":"=r" (cpuID)
			      :);

	if (NULL != stepping) {
		switch (cpuID & 0xf) {
		case 0:
			*stepping = "A0";
			break;
		case 4:
			*stepping = "B0";
			break;
		case 5:
			*stepping = "B1";
			break;
		case 6:
			*stepping = "B2";
			break;
		case 8:
			*stepping = "B4";
			break;
		case 9:
			*stepping = "B5";
			break;
		default:
			*stepping = "??";
			dbg_udc (0, "stepping unknown, ID#%x", (cpuID & 0xf));
		}
	}

	return (cpuID);
}



void udc_fix_errata_29 (char *msg)
{
	int ok;
	u32 cpuID;		// from init.c

	cpuID = getCPUID (NULL);

	// Set errata 29 fix bit, if possible
	if (cpuID == 0 || (cpuID & 0xF) >= 9) {
		// Unknown CPU, or B5 stepping and above
		// set errata 29 fix enable (for B5 and above, B4 will ignore)
		int udc_cr;
		for (ok = 10; ok > 0; ok--) {
			*(UDCCR) |= /*UDCCR_ERR29 */ 0x80;
			// Do some dummy reads....
			udc_cr = *(UDCCR);
			udc_cr = *(UDCCR);
			udc_cr = *(UDCCR);
			if (udc (UDCCR) & /*UDCCR_ERR29 */ 0x80) {
				dbg_udc (0,
					 "%s: set errata 29 fix bit worked, UDCCR#%02x ok=%d cpuID#%08x",
					 msg, udc (UDCCR), ok, cpuID);
				ok = -2;
				break;
			}
		}
		if (ok != -2) {
			dbg_udc (0, "%s: set errata 29 fix bit failed, UDCCR#%02x ok=%d cpuID#%08x",
				 msg, udc (UDCCR), ok, cpuID);
		}
	} else {
		dbg_udc (0, "%s: errata 29 fix bit not available, cpuID#%08x", msg, cpuID);
	}
}

/* ********************************************************************************************* */

/**
 * sa1100_tick - clock timer task 
 * @data:
 *  
 * Run from global clock tick to check if we are suspended.
 */
#ifdef USE_ADD_DEL_TIMER_FOR_USBADDR_CHECK
static void sa1100_tick (unsigned long data)
#else
static void sa1100_tick (void *data)
#endif
{
	udc_ticks++;

	// is driver active
	if (data) {

		if (_udc (UDCAR) != usb_address) {
			dbg_udc (0, "sa1100_tick: ADDRESS ERROR DETECTED %02x %02x", *(UDCAR), usb_address);
			udc_address_errors++;
			udc_fixed++;
		}
		*(UDCAR) = usb_address;

		// re-queue task
#ifdef USE_ADD_DEL_TIMER_FOR_USBADDR_CHECK
		sa1100_usb_dev_addr_check.expires = jiffies + CHECK_INTERVAL;
		add_timer (&sa1100_usb_dev_addr_check);
#else
		queue_task (&sa1100_tq, &tq_timer);
#endif

	}
}


/* ********************************************************************************************* */
/* Interrupt Handler
 */

/**
 * int_hndlr_cable - interrupt handler for cable
 */
static void int_hndlr_cable (int irq, void *dev_id, struct pt_regs *regs)
{
#ifdef CONFIG_SA1100_USBCABLE_GPIO
	dbg_udc (1, "udc_cradle_interrupt:");
	udc_cable_event ();
#endif
}


/**
 * int_hndlr_device - interrupt handler
 *
 */
static void int_hndlr_device (int irq, void *dev_id, struct pt_regs *regs)
{
	unsigned int status;

	status = *(UDCSR);
	*(UDCSR) = status;

	udc_interrupts++;

	//dbg_udc(0, "");
	dbg_intr(2, "[%d]: CSR: %02x CCR: %02x CAR: %02x:%02x", udc_interrupts, status, *(UDCCR), *(UDCAR), usb_address);

	// Handle common interrupts first, IN (tx) and OUT (recv)

	if (status & UDCSR_RIR) {
		ep1_int_hndlr (status);
	}

	if (status & UDCSR_TIR) {
		ep2_int_hndlr (status, 1);
	}
	// handle less common interrupts
	if (status & (UDCSR_EIR | UDCSR_RSTIR | UDCSR_SUSIR | UDCSR_RESIR)) {
		if (status & UDCSR_EIR) {
			ep0_int_hndlr (status);
		}

		if (status & UDCSR_RSTIR) {
			dbg_intr (1, "[%d] DEVICE_RESET: CSR: %02x CS0: %02x CAR: %02x", 
                                        udc_interrupts, *(UDCSR), *(UDCCS0), *(UDCAR));
			usbd_device_event (udc_device, DEVICE_RESET, 0);
		}

		if (status & UDCSR_SUSIR) {
			dbg_intr (1, "[%d] SUSPEND address: %02x irq: %02x status: %02x", 
                                        udc_interrupts, *(UDCAR), irq, status);
			sus_interrupts++;
#if defined(CONFIG_SA1100_COLLIE)	// XXX change to 5500
			usbd_device_event (udc_device, DEVICE_BUS_INACTIVE, 0);
#else
			usbd_device_event (udc_device, DEVICE_BUS_INACTIVE, 0);
			//usbd_device_event (udc_device, DEVICE_RESET, 0);
#endif
			udc_suspended_interrupts (udc_device);
			udc_ticker_poke ();
		}

		if (status & UDCSR_RESIR) {
			dbg_intr (1, "[%d] RESUME address: %02x irq: %02x status: %02x", udc_interrupts, *(UDCAR), irq, status);
                        *(UDCAR) = usb_address;
			res_interrupts++;
#if defined(CONFIG_SA1100_COLLIE)	// XXX change to 5500
			usbd_device_event (udc_device, DEVICE_BUS_ACTIVITY, 0);
#endif
			udc_all_interrupts (udc_device);
			udc_ticker_poke ();
		}
	}
	// Check that the UDC has not forgotton it's address, force it back to correct value
	//if (_udc (UDCAR) != usb_address) {
	//	udc_address_errors++;
	//}
	*(UDCAR) = usb_address;

}


/* ********************************************************************************************* */
/*
 * Start of public functions.
 */

/**
 * udc_init - initialize
 *
 * Return non-zero if we cannot see device.
 **/
int udc_init (void)
{
	// reset

	return 0;
}

/**
 * udc_start_in_irq - start transmit
 * @endpoint:
 *
 * Called with interrupts disabled.
 */
void udc_start_in_irq (struct usb_endpoint_instance *endpoint)
{
	ep2_int_hndlr (0, 0);
}


void udc_stall_ep0 (void)
{
	int ok = 0;
	// QQQ eh?
	dbg_udc (0, "stalling ep0 (UDCCS0_FST,UDCCS0_FST,UDCCS0_SST)");

	// write 1 to set FST
	SET_AND_TEST ((*(UDCCS0) &= UDCCS0_FST), !(udc (UDCCS0) & UDCCS0_FST), ok);
	if (!ok) {
		dbg_udc (0, "cannot stall !(UDCCS0&UDCCS0_FST) UDCCS0: %02x", *(UDCCS0));
	}
	// write 0 to reset FST
	SET_AND_TEST ((*(UDCCS0) &= ~UDCCS0_FST), (udc (UDCCS0) & UDCCS0_FST), ok);
	if (!ok) {
		dbg_udc (0, "cannot stall (UDCCS0&UDCCS0_FST) UDCCS0: %02x", *(UDCCS0));
	}
	// write 1 to reset SST
	SET_AND_TEST ((*(UDCCS0) = UDCCS0_SST), (udc (UDCCS0) & UDCCS0_SST), ok);
	if (!ok) {
		dbg_udc (0, "cannot stall (UDCCS0&UDCCS0_SST) UDCCS0: %02x", *(UDCCS0));
	}
}

static void stall_ep_n (int ep, volatile unsigned int *regaddr, int fst)
{
	int ok;
	dbg_udc (0, "stalling ep %d (FST)", ep);

	// write 1 to set FST
	SET_AND_TEST ((*(regaddr) = fst), !(udc (regaddr) & fst), ok);
	if (!ok) {
		dbg_udc (0, "cannot stall !(reg&fst) UDCCS%d: %02x", ep, *(regaddr));
	}
}

static void reset_ep_n (int ep, volatile unsigned int *regaddr, int fst, int sst)
{
	int ok;
	dbg_udc (1, "reset ep %d (FST)", ep);

	// write 0 to reset FST
	SET_AND_TEST ((*(regaddr) &= ~fst), (udc (regaddr) & fst), ok);
	if (!ok) {
		dbg_udc (0, "cannot stall !(reg&fst) UDCCS%d: %02x", ep, *(regaddr));
	}
	// write 1 to reset SST
	SET_AND_TEST ((*(regaddr) = sst), (udc (regaddr) & sst), ok);
	if (!ok) {
		dbg_udc (0, "cannot stall (reg&sst) UDCCS%d: %02x", ep, *(UDCCS0));
	}
}

/**
 * udc_stall_ep - stall endpoint
 * @ep: physical endpoint
 *
 * Stall the endpoint.
 */
void udc_stall_ep (unsigned int ep)
{
	dbg_udc (0, "STALLING %d (FST)", ep);
	switch (ep) {
	case 1:
		stall_ep_n (1, UDCCS1, UDCCS1_FST);
		break;
	case 2:
		stall_ep_n (2, UDCCS2, UDCCS2_FST);
		break;
	}
}


/**
 * udc_reset_ep - reset endpoint
 * @ep: physical endpoint
 * reset the endpoint.
 *
 * returns : 0 if ok, -1 otherwise
 */
void udc_reset_ep (unsigned int ep)
{
	dbg_udc (1, "RESETING %d (FST)", ep);
	switch (ep) {
	case 0:
		reset_ep_n (1, UDCCS0, UDCCS0_FST, UDCCS0_SST);
		break;
	case 1:
		reset_ep_n (1, UDCCS1, UDCCS1_FST, UDCCS1_SST);
		break;
	case 2:
		reset_ep_n (2, UDCCS2, UDCCS2_FST, UDCCS2_SST);
		break;
	}
}


/**
 * udc_endpoint_halted - is endpoint halted
 * @ep:
 *
 * Return non-zero if endpoint is halted
 */
int udc_endpoint_halted (unsigned int ep)
{
	switch (ep) {
	case 0:
		return (udc (UDCCS0) & UDCCS0_FST) != 0;
	case 1:
		return (udc (UDCCS1) & UDCCS1_FST) != 0;
	case 2:
		return (udc (UDCCS2) & UDCCS2_FST) != 0;
	}
	return 0;
}


/**
 * udc_set_address - set the USB address for this device
 * @address:
 *
 * Called from control endpoint function after it decodes a set address setup packet.
 */
void udc_set_address (unsigned char address)
{
	usb_address = address;
#if 0
        int ok;
	// address can be setup, udc will wait until ack received
	dbg_udc(1, "%02d", address);
        IOIOIO(UDCAR, usb_address, *(UDCAR) = address , (udc(UDCAR) != address), ok);

        if (*(UDCAR) != address) {
                dbg_udc(0, "AA %02d %02d ok: %d", udc(UDCAR), address, ok);
                udelay(20);
                IOIOIO(UDCAR, usb_address, *(UDCAR) = address , (udc(UDCAR) != address), ok);
                if (*(UDCAR) != address) {
                        dbg_udc(0, "BB %02d %02d ok: %d", udc(UDCAR), address, ok);
                        udelay(40);
                        IOIOIO(UDCAR, usb_address, *(UDCAR) = address , (udc(UDCAR) != address), ok);
                }
        }
        dbg_udc(0, "CC %02d %02d ok: %d", udc(UDCAR), address, ok);
#endif
}

#ifdef CONFIG_SA1100_BITSY
static int crc32 (char *s, int length)
{
	/* indices */
	int pByte;
	int pBit;

	const unsigned long poly = 0xedb88320;
	unsigned long crc_value = 0xffffffff;

	for (pByte = 0; pByte < length; pByte++) {
		unsigned char c = *(s++);
		for (pBit = 0; pBit < 8; pBit++) {
			crc_value = (crc_value >> 1) ^ (((crc_value ^ c) & 0x01) ? poly : 0);
			c >>= 1;
		}
	}
	return crc_value;
}
#endif

/**
 * udc_serial_init - set a serial number if available
 */
int __init udc_serial_init (struct usb_bus_instance *bus)
{
#if defined(SHOW_CPU_STEPPING)
	char *stepping;
	u32 id = getCPUID (&stepping);

	dbg_init (0, "CPU ID #%08x stepping %s",id,stepping);
#else
	dbg_init (2, "serial number");
#endif

#ifdef CONFIG_SA1100_BITSY
	if (machine_is_bitsy ()) {
		char serial_number[22];
		int i;
		int j;
		memset (&serial_number, 0, sizeof (serial_number));
		for (i = 0, j = 0; i < 20; i++, j++) {
			char buf[4];
			h3600_eeprom_read (5 + i, buf, 2);
			serial_number[j] = buf[1];
		}
		serial_number[j] = '\0';
		bus->serial_number = crc32 (serial_number, 22);
		if (bus->serial_number_str = kmalloc (strlen (serial_number) + 1, GFP_KERNEL)) {
			strcpy (bus->serial_number_str, serial_number);
		}
		//dbg_udc(0,  "serial: %s %08x", bus->serial_number_str, bus->serial_number);
		return 0;
	}
#endif

#ifdef CONFIG_SA1100_CALYPSO
	if (machine_is_calypso ()) {
		__u32 eerom_serial;
		int i;
		if ((i =
		     CalypsoIicGet (IIC_ADDRESS_SERIAL0, (unsigned char *) &eerom_serial,
				    sizeof (eerom_serial)))) {
			bus->serial_number = eerom_serial;
		}
		if (bus->serial_number_str = kmalloc (9, GFP_KERNEL)) {
			sprintf (bus->serial_number_str, "%08X", eerom_serial & 0xffffffff);
		}
		//dbg_udc(0,  "serial: %s %08x", bus->serial_number_str, bus->serial_number);
		return 0;
	}
#endif
	return -EINVAL;

}


/* ********************************************************************************************* */

/**
 * udc_max_endpoints - max physical endpoints 
 *
 * Return number of physical endpoints.
 */
int udc_max_endpoints (void)
{
	return UDC_MAX_ENDPOINTS;
}


/**
 * udc_check_ep - check logical endpoint 
 * @logical_endpoint:
 *
 * Return physical endpoint number to use for this logical endpoint or zero if not valid.
 */
int udc_check_ep (int logical_endpoint, int packetsize)
{
	if (packetsize > 64) {
		return 0;
	}
	switch (logical_endpoint) {
	case 1:
		return 1;
	case 0x82:
		return 2;
	default:
		return 0;
	}
}


/**
 * udc_set_ep - setup endpoint 
 * @ep:
 * @endpoint:
 *
 * Associate a physical endpoint with endpoint_instance
 */
void udc_setup_ep (struct usb_device_instance *device, unsigned int ep,
		   struct usb_endpoint_instance *endpoint)
{
	dbg_udc (1, "[%d]:", ep);

	if (ep < UDC_MAX_ENDPOINTS) {

		ep_endpoints[ep] = endpoint;
                dbg_udc (1, "[%d] ep_endpoint %p endpoint: %p", ep, ep_endpoints[ep], endpoint);


		if (endpoint) {
			switch (ep) {
			case 0:	// Control
				ep0_enable (device, endpoint);
				break;

			case 1:	// OUT
                                dbg_udc (1, "[%d]: CHECKING rcv_urb: %p", ep, endpoint->rcv_urb);
				// XXX XXX 
                                if (!endpoint->rcv_urb) {
                                        usbd_fill_rcv (device, endpoint, 5);
                                        endpoint->rcv_urb = first_urb_detached (&endpoint->rdy);
                                        dbg_udc (1, "[%d]: SETTING rcv_urb: %p", ep, endpoint->rcv_urb);
                                }
				ep1_enable (device, endpoint, usbd_rcv_dma);
				break;

			case 2:	// IN
				ep2_enable (device, endpoint, usbd_tx_dma);
				break;
			}
		}
	}
}


/**
 * udc_disable_ep - disable endpoint
 * @ep:
 *
 * Disable specified endpoint 
 */
void udc_disable_ep (unsigned int ep)
{
	dbg_udc (1, "[%d]:", ep);
	if (ep < UDC_MAX_ENDPOINTS) {
		struct usb_endpoint_instance *endpoint;

		if ((endpoint = ep_endpoints[ep])) {
			ep_endpoints[ep] = NULL;
			usbd_flush_ep (endpoint);
		}
		switch (ep) {
		case 0:
			ep0_disable ();
			break;
		case 1:
			ep1_disable ();
			break;
		case 2:
			ep2_disable ();
			break;
		}
	}
}

/* ********************************************************************************************* */

/**
 * udc_incradle - is the USB cable connected
 *
 * Return non-zeron if cable is connected.
 */
int udc_connected ()
{
	int rc = 1;
#ifdef CONFIG_SA1100_USBCABLE_GPIO

#ifdef CONFIG_SA1100_USBCABLE_ACTIVE_HIGH
	rc = (GPLR & GPIO_GPIO (23)) != 0;
	dbg_udc (1, "udc_connected: ACTIVE_HIGH: %d", rc);
#else
	rc = (GPLR & GPIO_GPIO (23)) == 0;
	dbg_udc (1, "udc_connected: ACTIVE_LOW: %d", rc);
#endif
#else
#warning UDC_CONNECTED not implemented
#endif
	return rc;
}


/**
 * udc_connect - enable pullup resistor
 *
 * Turn on the USB connection by enabling the pullup resistor.
 */
void udc_connect (void)
{

#if defined(CONFIG_SA1100_COLLIE)	// XXX change to 5500

        #warning COLLIE CONNECT
        if (ucb1200_test_io(TC35143_GPIO_VERSION0) != 0 ||
                        ucb1200_test_io(TC35143_GPIO_VERSION1) != 0) {
                SCP_REG_GPCR |= SCP_GPCR_PA19;    /* direction : out mode */
                SCP_REG_GPWR |= SCP_GPCR_PA19;    /* set Hi */
                dbg_udc(1, "udc_connect: %d GPCR: %x GPWR: %x GPRR: %x",
                                SCP_GPCR_PA19, SCP_REG_GPCR, SCP_REG_GPWR, SCP_REG_GPRR);
        }

#elif defined(CONFIG_SA1100_CONNECT_GPIO)

	GAFR |= GPIO_GPIO (CONFIG_SA1100_CONNECT_GPIO);
	GPDR |= GPIO_GPIO (CONFIG_SA1100_CONNECT_GPIO);

        #ifdef CONFIG_SA1100_CONNECT_ACTIVE_HIGH
	GPSR = GPIO_GPIO (CONFIG_SA1100_CONNECT_GPIO);
	dbg_udc (1, "udc_connect: %d ACTIVE_HIGH %x %x %x", CONFIG_SA1100_CONNECT_GPIO,
		 GPIO_GPIO (CONFIG_SA1100_CONNECT_GPIO), GPDR, GPLR);
        #else
	GPCR = GPIO_GPIO (CONFIG_SA1100_CONNECT_GPIO);
	dbg_udc (1, "udc_connect: %d ACTIVE_LOW %x %x %x", CONFIG_SA1100_CONNECT_GPIO,
		 GPIO_GPIO (CONFIG_SA1100_CONNECT_GPIO), GPDR, GPLR);
        #endif

#else				/* defined(CONFIG_SA1100_CONNECT_GPIO) */

#warning UDC_CONNECT not implemented

#endif

}


/**
 * udc_disconnect - disable pullup resistor
 *
 * Turn off the USB connection by disabling the pullup resistor.
 */
void udc_disconnect (void)
{

#if defined(CONFIG_SA1100_COLLIE)	// XXX change to 5500

        if (ucb1200_test_io(TC35143_GPIO_VERSION0) != 0 ||
                        ucb1200_test_io(TC35143_GPIO_VERSION1) != 0) {
                SCP_REG_GPWR &= ~SCP_GPCR_PA19;    /* set Hi */
                SCP_REG_GPCR &= ~SCP_GPCR_PA19;    /* direction : out mode */

                dbg_udc(1, "udc_disconnect: %d GPCR: %x GPWR: %x GPRR: %x",
                                SCP_GPCR_PA19, SCP_REG_GPCR, SCP_REG_GPWR, SCP_REG_GPRR);
        }


#elif defined(CONFIG_SA1100_CONNECT_GPIO)

#ifdef CONFIG_SA1100_CONNECT_ACTIVE_HIGH
	GPCR = GPIO_GPIO (CONFIG_SA1100_CONNECT_GPIO);
	dbg_udc (1, "udc_disconnect: %d ACTIVE_HIGH", CONFIG_SA1100_CONNECT_GPIO);
#else
	GPSR = GPIO_GPIO (CONFIG_SA1100_CONNECT_GPIO);
	dbg_udc (1, "udc_disconnect: %d ACTIVE_LOW", CONFIG_SA1100_CONNECT_GPIO);
#endif

	GPDR &= ~GPIO_GPIO (CONFIG_SA1100_CONNECT_GPIO);

#endif
}


/* ********************************************************************************************* */

static __inline__ void set_interrupts (int flags)
{
	int ok;
	//dbg_udc(1, "udc_set_interrupts: %02x", flags);

	// set interrupt mask
	SET_AND_TEST ((*(UDCCR) = flags), (udc (UDCCR) != flags), ok);
	if (!ok) {
		dbg_intr (0, "failed to set UDCCR %02x to %02x", *(UDCCR), flags);
	}
}

/**
 * udc_enable_interrupts - enable interrupts
 *
 * Switch on UDC interrupts.
 *
 */
void udc_all_interrupts (struct usb_device_instance *device)
{
	set_interrupts (0);
	dbg_udc (1, "%02x", *(UDCCR));

	// setup tick
#ifdef USE_ADD_DEL_TIMER_FOR_USBADDR_CHECK
	if (!usb_addr_check_initialized) {
		init_timer (&sa1100_usb_dev_addr_check);
		usb_addr_check_initialized = 1;
		sa1100_usb_dev_addr_check.function = sa1100_tick;
		sa1100_usb_dev_addr_check.data = 1;
		sa1100_usb_dev_addr_check.expires = jiffies + CHECK_INTERVAL;
		add_timer (&sa1100_usb_dev_addr_check);
	}
#else
	// XXX sa1100_tq.sync = 0;
	sa1100_tq.routine = sa1100_tick;
	sa1100_tq.data = &udc_saw_sof;
	queue_task (&sa1100_tq, &tq_timer);
#endif

}


/**
 * udc_suspended_interrupts - enable suspended interrupts
 *
 * Switch on only UDC resume interrupt.
 *
 */
void udc_suspended_interrupts (struct usb_device_instance *device)
{
	set_interrupts (UDCCR_SRM);
	dbg_udc (1, "%02x", *(UDCCR));
}


/**
 * udc_disable_interrupts - disable interrupts.
 *
 * switch off interrupts
 */
void udc_disable_interrupts (struct usb_device_instance *device)
{
	set_interrupts (UDCCR_SRM | UDCCR_EIM | UDCCR_RIM | UDCCR_TIM | UDCCR_SRM | UDCCR_REM);
	dbg_udc (1, "%02x", *(UDCCR));
}

/* ********************************************************************************************* */

/**
 * udc_ep0_packetsize - return ep0 packetsize
 */
int udc_ep0_packetsize (void)
{
	return EP0_PACKETSIZE;
}

/**
 * udc_enable - enable the UDC
 *
 * Switch on the UDC
 */
void udc_enable (struct usb_device_instance *device)
{
	int ok;
	dbg_udc (1, "************************* udc_enable:");

	//dbg_udc(0, "");
	//dbg_udc(0, "udc_enable: device: %p", device);

	// save the device structure pointer
	udc_device = device;

	// enable UDC
	for (ok = 0; (*(UDCCR) & UDCCR_UDA) && ok < 100000; ok++);
	if (ok > 10000) {
		dbg_udc (0, "Waiting too long to go inactive: UDCCR: %02x", *(UDCCR));
	}
	// set UDD
	SET_AND_TEST ((*(UDCCR) |= UDCCR_UDD), (!udc (UDCCR) & UDCCR_UDD), ok);
	if (!ok) {
		dbg_udc (0, "Waiting too long to disable: UDCCR: %02x", *(UDCCR));
	}
	// reset UDD
	SET_AND_TEST ((*(UDCCR) &= ~UDCCR_UDD), (udc (UDCCR) & UDCCR_UDD), ok);
	if (!ok) {
		dbg_udc (0, "can't enable udc, FAILED: %02x", *(UDCCR));
	}
}


/**
 * udc_disable - disable the UDC
 *
 * Switch off the UDC
 */
void udc_disable (void)
{
	int ok;
	dbg_udc (1, "************************* udc_disable:");

	// tell tick task to stop
#ifdef USE_ADD_DEL_TIMER_FOR_USBADDR_CHECK
	del_timer (&sa1100_usb_dev_addr_check);
	sa1100_usb_dev_addr_check.data = 0;
	usb_addr_check_initialized = 0;
#else
	sa1100_tq.data = NULL;
	while (sa1100_tq.sync) {
		//printk(KERN_DEBUG"waiting for sa1100_tq to stop\n");
		schedule_timeout (10 * HZ);
	}
#endif

	// disable UDC

	for (ok = 0; (*(UDCCR) & UDCCR_UDA) && ok < 100000; ok++);
	if (ok > 10000) {
		dbg_udc (0, "Waiting too long to go inactive: UDCCR: %02x", *(UDCCR));
	}
	// set UDD
	SET_AND_TEST ((*(UDCCR) |= UDCCR_UDD), (!udc (UDCCR) & UDCCR_UDD), ok);
	if (!ok) {
		dbg_udc (0, "Waiting too long to disable: UDCCR: %02x", *(UDCCR));
	}

	dbg_udc (7, "CCR: %02x", *(UDCCR));

	// reset device pointer
	udc_device = NULL;
}


/**
 * udc_startup - allow udc code to do any additional startup
 */
void udc_startup_events (struct usb_device_instance *device)
{
	usbd_device_event (device, DEVICE_INIT, 0);
	usbd_device_event (device, DEVICE_CREATE, 0);
	usbd_device_event (device, DEVICE_HUB_CONFIGURED, 0);
	usbd_device_event (device, DEVICE_RESET, 0);	// XXX should be done from device event
}


/* ********************************************************************************************* */

/**
 * udc_name - return name of USB Device Controller
 */
char *udc_name (void)
{
	return UDC_NAME;
}

/**
 * udc_request_udc_irq - request UDC interrupt
 *
 * Return non-zero if not successful.
 */
int udc_request_udc_irq ()
{
	// request IRQ and two dma channels
	// XXX GPDR &= ~GPIO_GPIO(23);
	// XXX set_GPIO_IRQ_edge(GPIO_GPIO(CONFIG_SA1100_USBCABLE_GPIO), GPIO_BOTH_EDGES);

	dbg_init (2, "requesting udc irq: %d %d", 13, IRQ_Ser0UDC);

	return request_irq (IRQ_Ser0UDC, int_hndlr_device, SA_INTERRUPT | SA_SAMPLE_RANDOM,
			    "SA1100 USBD Bus Interface", NULL);
}

/**
 * udc_request_cable_irq - request Cable interrupt
 *
 * Return non-zero if not successful.
 */
int udc_request_cable_irq ()
{
#ifdef CONFIG_SA1100_USBCABLE_GPIO
	dbg_init (2, "requesting cable irq: %d %d",
		  CONFIG_SA1100_USBCABLE_GPIO, SA1100_GPIO_TO_IRQ (CONFIG_SA1100_USBCABLE_GPIO));
	GPDR &= ~GPIO_GPIO (23);
	set_GPIO_IRQ_edge (GPIO_GPIO (CONFIG_SA1100_USBCABLE_GPIO), GPIO_BOTH_EDGES);
	return request_irq (SA1100_GPIO_TO_IRQ (CONFIG_SA1100_USBCABLE_GPIO), int_hndlr_cable,
			    SA_INTERRUPT | SA_SAMPLE_RANDOM, "SA1100 Monitor", NULL);
#else
	return 0;
#endif
}

/**
 * udc_request_udc_io - request UDC io region
 *
 * Return non-zero if not successful.
 */
int udc_request_io ()
{
	//sa1100_bi_init_dma ();

        usbd_rcv_dma = usbd_tx_dma = -1;

	if (sa1100_request_dma (&usbd_rcv_dma, "SA1100 USBD OUT - rcv dma", DMA_Ser0UDCRd)) {
		dbg_init (2, "channel %d taken", usbd_rcv_dma);
		return -EINVAL;
	}


	if (sa1100_request_dma (&usbd_tx_dma, "SA1100 USBD IN - tx dma", DMA_Ser0UDCWr)) {
		dbg_init (2, "channel %d taken", usbd_tx_dma);
		sa1100_free_dma (usbd_rcv_dma);
		return -EINVAL;
	}

	return 0;
}

/**
 * udc_release_udc_irq - release UDC irq
 */
void udc_release_udc_irq ()
{
	free_irq (IRQ_Ser0UDC, NULL);
}

/**
 * udc_release_cable_irq - release Cable irq
 */
void udc_release_cable_irq ()
{
#ifdef CONFIG_SA1100_USBCABLE_GPIO
	dbg_init (2, "freeing cable irq: %d", CONFIG_SA1100_USBCABLE_GPIO);
	free_irq (SA1100_GPIO_TO_IRQ (CONFIG_SA1100_USBCABLE_GPIO), NULL);
#endif
}

/**
 * udc_release_io - release UDC io region
 */
void udc_release_io ()
{
	sa1100_free_dma (usbd_rcv_dma);
	sa1100_free_dma (usbd_tx_dma);
}

/* ep0 dma engine functions ******************************************************************** */

/* 
 * EP0 State - See the Intel Application Note
 */

#define EP0_STATE_IDLE 0
#define EP0_STATE_IN_DATA_PHASE 1
#define EP0_STATE_END_IN 2

char *ep0_state_desc[] = {
	"idle", "in", "end"
};

static int ep0_state = EP0_STATE_IDLE;

//struct urb ep0_urb;
struct urb *ep0_urb;


static unsigned char *buffer;
static unsigned int buffer_cnt;


/*
 * There are two scenarios: 
 *
 *   1. Data to be sent is an exact multiple of the packetsize and less than
 *   the requested size. An empty packet needs to be sent.
 *
 *   2. Everything else. The last packet will be recognized as the last
 *   because it is less than the packetsize OR because we have sent exactly
 *   the required amount of data.
 */

static unsigned int send_zlp;

struct usb_endpoint_instance *ep0_endpoint;

char *send_zlp_description[] = {
	"exact", "forced"
};

int ep0_enable (struct usb_device_instance *device, struct usb_endpoint_instance *endpoint)
{
	ep0_endpoint = endpoint;

        dbg_ep0(0, "ep0_urb: %p", ep0_urb);
	if (!ep0_urb) {
		if (!(ep0_urb = usbd_alloc_urb (device, device->function_instance_array, 0, 512))) {
			dbg_ep0 (1, "ep0_enable: usbd_alloc_urb failed\n");
		}
	} else {
		dbg_ep0 (1, "ep0_enable: ep0_urb already allocated\n");
	}
	return 0;
}

void ep0_disable (void)
{
        dbg_ep0(0, "ep0_urb: %p", ep0_urb);
	if (ep0_urb) {
		usbd_dealloc_urb (ep0_urb);
		ep0_urb = 0;
	} else {
		dbg_ep0 (1, "ep0_disable: ep0_urb already NULL\n");
	}
}

int ep0_reset (void)
{
	dbgENTER (dbgflg_usbdbi_ep0, 1);
	ep0_state = EP0_STATE_IDLE;
	udc_reset_ep (0);
	return 0;
}

/* 
 * ep0_set_opr
 *
 * Set OPR and optionally DE.
 */
static void ep0_set_opr (void)
{
        int ok;
        IOIOIO(UDCCS0, UDCCS0_S0, *(UDCCS0) = UDCCS0_SO , (*(UDCCS0) & UDCCS0_OPR), ok);
        if (!ok) {
                dbg_ep0(0, "failed to reset OPR");
        }
        dbg_ep0(1,"CS0: %02x ok: %d", *(UDCCS0), ok);
}

/* 
 * ep0_set_opr_and_de
 *
 * Set OPR and optionally DE.
 */
static void ep0_set_opr_and_de (void)
{
        int ok;
        IOIOIO(UDCCS0, UDCCS0_S0, *(UDCCS0) = (UDCCS0_SO | UDCCS0_DE) , (*(UDCCS0) & UDCCS0_OPR), ok);
        if (!ok) {
                dbg_ep0(0, "failed to reset OPR");
        }
        dbg_ep0(1,"CS0: %02x ok: %d", *(UDCCS0), ok);
}


/*
 * ep0_read_data
 *
 * Retreive setup data from FIFO.
 */
__inline__ static int ep0_read_data (struct usb_device_request *request, int max)
{
	int fifo_count;
	int bytes_read;
	int i;
	int reads;
	unsigned char *cp = (unsigned char *) request;
        int udcwc;
        int udccs0;

        udccs0 = *(UDCCS0);
	udcwc = fifo_count = udc(UDCWC) & 0xff;

	if (fifo_count != max) {
		dbg_ep0 (0, "ep0_read_data: have %d bytes; want max\n", fifo_count);
	}

	reads = 0;
	bytes_read = 0;

	for (reads = 0, bytes_read = 0; udcwc && fifo_count--;) {
		i = 0;
		do {
			*cp = (unsigned char) *(UDCD0);
                        // XXX without this reads may be corrupt, typically the wLength field
                        // will contain bad data, which in turn will cause get descriptor config
                        // to return the full set of descriptors in response to limited first
                        // get descriptor 9, which will BSOD windows
			udelay(10);
			reads++;
			i++;
		} while ((((udcwc = udc(UDCWC) & 0xff)) > fifo_count) && (i < 10));

		if (i == 10) {
			dbg_ep0 (0, "failed: UDCWC: %2x %2x bytes: %d fifo: %d reads: %d no change\n", 
                                        udcwc, *(UDCWC), bytes_read, fifo_count, reads);
		}

                if (udcwc < (fifo_count - 1)) {
			dbg_ep0 (0, "failed: UDCWC: %2x %2x bytes: %d fifo: %d reads: %d too low\n", 
                                        udcwc, *(UDCWC), bytes_read, fifo_count, reads);
                }

		cp++;
		bytes_read++;

		if ((bytes_read >= max) && (fifo_count > 0)) {
			dbg_ep0 (0, "reading too much data\n");
			break;
		}
	}

        dbg_ep0 ((bytes_read == max)?2:0, 
                        "ep0_read_data: reads: %d want: %d fifo_count: %d bytes_read: %d CS0: %02x:%02x CWC: %02x",
                        reads, max, fifo_count, bytes_read, *(UDCCS0), udccs0, udcwc);

	return bytes_read;
}


/* *
 * ep0_read_request
 *
 * Called when in IDLE state to get a setup packet.
 */
static void ep0_read_request (unsigned int udccs0, unsigned int udccr)
{
	int i;
	int ok;

	if (!(udccr & UDCCR_UDA)) {
		dbg_ep0 (3, "warning, UDC not active");
	}
	// reset SST
	if (udccs0 & UDCCS0_SST) {
		IOIOIO (UDCCS0, UDCCS0_SST, *(UDCCS0) = UDCCS0_SST, *(UDCCS0) & UDCCS0_SST, ok);
		if (!ok) {
			dbg_ep0 (0, "failed to reset SST CS0: %02x ok: %d", *(UDCCS0), ok);
		}
	}
	// check UDC Endpoint 0 Control/Status register for OUT Packet Ready (OPR) (c.f. 11.8.7.1)
	if (!(udccs0 & UDCCS0_OPR)) {
		// wait for another interrupt
		dbg_ep0 (3, "wait for another interrupt CS0: %02x", udccs0);
		return;
	}

	// read a device request
	ep0_urb->actual_length = 0;
	if ((i = ep0_read_data (&ep0_urb->device_request, 8)) < 8) {
		ep0_set_opr ();
		dbg_ep0 (2, "not enough data: %d", i);
		return;
	}
	//ep0_need_data = le16_to_cpu(ep0_urb->device_request.wLength);
	dbg_ep0 (3, "bmRequestType:%02x bRequest:%02x wValue:%04x wIndex:%04x wLength:%04x",
		 ep0_urb->device_request.bmRequestType,
		 ep0_urb->device_request.bRequest,
		 le16_to_cpu (ep0_urb->device_request.wValue),
		 le16_to_cpu (ep0_urb->device_request.wIndex),
		 le16_to_cpu (ep0_urb->device_request.wLength));

	{
		char *cp = (char *) &ep0_urb->device_request;
		dbg_ep0 (1, "%02x %02x %02x %02x %02x %02x %02x %02x",
			 cp[0], cp[1], cp[2], cp[3], cp[4], cp[5], cp[6], cp[7]
		    );
	}

	// check if we have been initialized
	if (!ep0_urb->device) {
		ep0_set_opr_and_de ();
		udc_stall_ep (0);
		return;
	}

	// check direction of data transfer
        if ((ep0_urb->device_request.bmRequestType & USB_REQ_DIRECTION_MASK) == USB_REQ_HOST2DEVICE) {

                // if this is a set address save it
                if ((ep0_urb->device_request.bmRequestType == 0) && (ep0_urb->device_request.bRequest == USB_REQ_SET_ADDRESS)) {

                        usb_address = le16_to_cpu(ep0_urb->device_request.wValue) & 0xff;

                        /*
                         * very occasionally register write will fail, delay and redo twice
                         * to ensure that it actually got through, cannot check as UDC
                         * will not propagate until SO and DE are set. We also need a delay
                         * after setting SO and DE, exiting too soon can also result in
                         * problems if address has not propagated.
                         */


                        *(UDCAR) = usb_address;
                        udelay(2);
                        *(UDCAR) = usb_address;
                        udelay(2);
                        *(UDCAR) = usb_address;
                        ep0_set_opr_and_de();   
                        udelay(40);            

                        if (usbd_recv_setup(ep0_urb)) {
                                dbg_ep0(5, "usb_recv_setup failed, not stalling");
                        }
                }
                else {
                        //ep0_set_opr_and_de();
                        // submit urb to ep0, stall if non-zero result
                        if (usbd_recv_setup(ep0_urb)) {
                                dbg_ep0(1, "usb_recv_setup failed, stalling");
                                udc_stall_ep(0);
                        }
                        ep0_set_opr_and_de(); // tbr getting better results with this.
                }
                return;
	}

	// submit urb to ep0, stall if non-zero result
	if (usbd_recv_setup (ep0_urb)) {
		dbg_ep0 (2, "usb_recv_setup failed, stalling");
		ep0_set_opr_and_de ();
		udc_stall_ep (0);
		return;
	}

	// device reqeust has specified Device-to-Host, so we should be returning data

	// check request length, zero is not legal
	if (!le16_to_cpu (ep0_urb->device_request.wLength)) {
		dbg_ep0 (0, "wLength zero, stall");
		ep0_set_opr_and_de ();
		udc_stall_ep (0);
		return;
	}
	// check that we have some data to send back, zero should not be possible
	if (!ep0_urb->actual_length) {
		dbg_ep0 (0, "no data, stall");
		ep0_set_opr_and_de ();
		udc_stall_ep (0);
		return;
	}
	// everything looks sane, so set buffer pointers and setup for data transfer
	buffer = ep0_urb->buffer;
	buffer_cnt = ep0_urb->actual_length;

	/*
	 * IFF we are sending a multiple of the packet size AND less than was
	 * requested we will need to send an empty packet after all the data packets.
	 */
	if (!(buffer_cnt % 8) && (buffer_cnt < le16_to_cpu (ep0_urb->device_request.wLength))) {
		send_zlp = 1;
		dbg_ep0 (1, "DEVICE2HOST: SEND ZLP %x %x", ep0_urb->actual_length,
			 le16_to_cpu (ep0_urb->device_request.wLength));
	} else {
		send_zlp = 0;
		dbg_ep0 (2, "DEVICE2HOST: NO ZLP %x %x", ep0_urb->actual_length,
			 le16_to_cpu (ep0_urb->device_request.wLength));
	}

	ep0_state = EP0_STATE_IN_DATA_PHASE;
}

/* 
 * ep0_in
 *
 * Send some data to host.
 */
static void ep0_in (unsigned int udccs0)
{
	int transfer_cnt = 0;
	unsigned int fifo_count;
	volatile int writes = 0;
	unsigned long start = jiffies;
	int ok;

	udccs0 = *(UDCCS0);
	dbg_ep0 (2, "CS0: %02x", udccs0);

	// check for non empty FIFO
	if (*(UDCWC)) {
		dbg_ep0 (0, "FIFO not empty");
		return;
	}
	// process iff ep0 is not stalled, no premature end and IPR is clear
	if (udccs0 & (UDCCS0_SE | UDCCS0_SST)) {
		dbg_ep0 (1, "ep0_in: SE or SST set\n");
		return;
	}
	// first check that IPR is not set, if it is then we wait for it to clear.
	if (udccs0 & UDCCS0_IPR) {

		for (ok = 0; (*(UDCCS0) & UDCCS0_IPR) && ok < 2000; ok++) {
			udelay (25);	// XXX 
		}
		if (ok == 2000) {
			dbg_ep0 (0, "IPR clear timed out");
		}
		dbg_ep0 (0, "IPR was set count: %d", ok);
	}
	// get up to first 8 bytes of data
	transfer_cnt = MIN (8, buffer_cnt);
	buffer_cnt -= transfer_cnt;
	fifo_count = 0;

	do {
		int count = 0;
                int udcwc;
		do {
			*(UDCD0) = (unsigned long) buffer[fifo_count];
			count++;
			writes++;
		} while (((udcwc = udc (UDCWC)) == fifo_count) && (count < 2000));

		if (udcwc <= fifo_count) {
			dbg_ep0 (0,
				 "sending STALL, failed writing FIFO transfer: %d fifo_count: %x CS0: %02x CWC: %02x",
				 transfer_cnt, fifo_count, *(UDCCS0), udcwc);
			udc_stall_ep (0);
			ep0_state = EP0_STATE_IDLE;
			return;
		}

		fifo_count++;

	} while (fifo_count < transfer_cnt);

	dbg_ep0 (3, "elapsed: %lx writes: %d fifo_count: %d transfer_cnt: %d buffer_cnt: %d CS0: %02x CWC: %02x",
		 jiffies - start, writes, fifo_count, transfer_cnt, buffer_cnt, *(UDCCS0), *(UDCWC));

	buffer += transfer_cnt;

	// set data end, and start 
	if (buffer_cnt == 0) {
		ep0_state = EP0_STATE_END_IN;
	}

	IOIOIO (UDCCS0, UDCCS0_IPR,
		*(UDCCS0) = (UDCCS0_IPR | ((((!buffer_cnt) && (send_zlp != 1)) ? UDCCS0_DE : 0))),
		!(*(UDCCS0) & UDCCS0_IPR), ok);

	if (!ok) {
		dbg_ep0 (0, "failed to set IPR CS0: %02x ok: %d", *(UDCCS0), ok);
	}
}

/*
 * ep0_end_in
 *
 * Handle end of IN, possibly with ZLP.
 */
static void ep0_end_in (unsigned int udccs0)
{
	int ok;

	dbg_ep0 (2, " status: %02x CS0: %02x", udccs0, *(UDCCS0));

	// reset SST if necessary
	if (udccs0 & UDCCS0_SST) {
		IOIOIO (UDCCS0, UDCCS0_SST, *(UDCCS0) = UDCCS0_SST, *(UDCCS0) & UDCCS0_SST, ok);
		if (!ok) {
			dbg_ep0 (0, "failed to reset SST");
		}
	}
	// reset SE if necessary
	if (udccs0 & UDCCS0_SE) {
		IOIOIO (UDCCS0, UDCCS0_SSE, *(UDCCS0) = UDCCS0_SSE, *(UDCCS0) & UDCCS0_SSE, ok);
		if (!ok) {
			dbg_ep0 (0, "failed to reset SE");
		}
	}
	// ensure that IPR is not set and send zero length packet if necessary
	if (!(udc (UDCCS0) & UDCCS0_IPR) && (send_zlp == 1)) {
                IOIOIO (UDCCS0, UDCCS0_DE | UDCCS0_IPR, 
                                *(UDCCS0) = (UDCCS0_DE | UDCCS0_IPR), 
                                (*(UDCCS0) & (UDCCS0_DE | UDCCS0_IPR)) != (UDCCS0_DE | UDCCS0_IPR), ok);
                if (!ok) {
                        dbg_ep0 (0, "failed to set DE and IPR CS0: %02x", *(UDCCS0));
                }
                else {
                        dbg_ep0 (1, "set DE and IPR, ZLP sent CS0: %02x", *(UDCCS0));
                }
	}
	// set state to IDLE
	ep0_state = EP0_STATE_IDLE;
	send_zlp = 0;
}



/* 
 * ep0_int_hndlr
 *
 * Handle ep0 interrupts.
 *
 * Generally follows the example in the Intel Application Note.
 *
 */
void ep0_int_hndlr (unsigned int status)
{
	unsigned int udccs0;
	unsigned int udccr;

	udccs0 = udc (UDCCS0);
	udccr = udc (UDCCR);

	ep0_interrupts++;

	dbg_ep0 (4, "------------>[%d:%s:%s CCR: %02x CS0: %02x CWC: %02x CAR: %02x]",
		 ep0_interrupts, ep0_state_desc[ep0_state], send_zlp_description[send_zlp], udccr,
		 udccs0, *(UDCWC), *(UDCAR));

	if (udccs0 == UDCCS0_IPR) {
		dbg_ep0 (3, "IPR SET [%d:%02x]", ep0_state, udccs0);
		return;
	}

	if (udccs0 & UDCCS0_SST) {
		dbg_ep0 (3, "previously sent stall [%d:%02x]", ep0_state, udccs0);
	}
	// SE 
	if (udccs0 & UDCCS0_SE) {
		int ok;
		dbg_ep0 (3, "unloading, found UDCCS0_SE: CS0: %02x", udccs0);

		// reset SE
		IOIOIO (UDCCS0, UDCCS0_SSE, *(UDCCS0) = UDCCS0_SSE, *(UDCCS0) & UDCCS0_SE, ok);
		if (!ok) {
			dbg_ep0 (0, "failed to reset SE CS0: %02x ok: %d", *(UDCCS0), ok);
		}
		// clear SE before unloading any setup packets 
		if (udccs0 & UDCCS0_OPR) {
			// reset OPR
			*(UDCCS0) = UDCCS0_SO;
		}
		ep0_state = EP0_STATE_IDLE;
                if (send_zlp) {
			dbg_ep0 (0, "not sending ZLP CS0: %02x", *(UDCCS0));
                }
		send_zlp = 0;
	}

	if ((ep0_state != EP0_STATE_IDLE) && (udccs0 & UDCCS0_OPR)) {
		dbg_ep0 (3, "Forcing EP0_STATE_IDLE CS0: %02x", udccs0);
		ep0_state = EP0_STATE_IDLE;
	}

	switch (ep0_state) {
	case EP0_STATE_IDLE:
		dbg_ep0 (4, "state: EP0_STATE_IDLE CS0: %02x", udccs0);
		ep0_read_request (udccs0, udccr);
		if (ep0_state != EP0_STATE_IN_DATA_PHASE) {
			break;
		}
		// fall through to data phase
		ep0_set_opr ();

	case EP0_STATE_IN_DATA_PHASE:
		dbg_ep0 (4, "state: EP0_STATE_IN_DATA_PHASE %x", udccs0);
		ep0_in (udccs0);
		break;

	case EP0_STATE_END_IN:
		dbg_ep0 (4, "state: EP0_STATE_END_IN %x", udccs0);
		ep0_end_in (udccs0);
		break;
	}
	dbg_ep0 (4, "<------------[%s:%s CCR: %02x CS0: %02x CWC: %02x]",
		 ep0_state_desc[ep0_state], send_zlp_description[send_zlp], *(UDCCR), *(UDCCS0),
		 *(UDCWC));
}

/* ep1 dma engine functions ******************************************************************** */

static int dmachn_rx;		// rx dma channel
static int dma_enabled;		// dma channel has been setup
static int dma_is_active;		// dma engine is running
static int dma_rx_active;		// dma engine is running

static dma_addr_t dma_rx_curpos;
struct usb_device_instance *ep1_device;
struct usb_endpoint_instance *ep1_endpoint;

/* *
 * ep1_stop_dma - stop the DMA engine
 *
 * Stop the DMA engine. 
 */
static void ep1_stop_dma (struct urb *urb)
{
	if (urb && dma_enabled) {
		unsigned long flags;
		save_flags_cli (flags);
		if (!dma_is_active) {
			restore_flags (flags);
			dbg_rx (1, "dma OFF: %x %x %x", dmachn_rx, dma_enabled, dma_is_active);
			return;
		}

		dma_is_active = 0;
		restore_flags (flags);

		if (ep1_endpoint->rcv_packetSize > 20) {
			_sa1100_bi_dma_flush_all_irq(dmachn_rx);
		}
	}
}


/* *
 * ep1_start_dma - start the DMA engine running
 *
 * Start the DMA engine. This should allow incoming data to be stored
 * in the DMA receive buffer.
 *
 */
static void ep1_start_dma (struct urb *urb)
{
	if (urb && dma_enabled) {
		unsigned long flags;
		int ok;
		save_flags_cli (flags);
		if (dma_is_active) {
			restore_flags (flags);
			dbg_rx (1, "dma ON: %x %x %x", dmachn_rx, dma_enabled, dma_is_active);
			return;
		}

		dma_is_active = 1;

		dbg_rx (1, "urb: %p urb->buffer: %p actual_length: %d rcv_packetSize: %d",
			urb, urb->buffer, urb->actual_length, urb->endpoint->rcv_packetSize);

		if (!(dma_rx_curpos = pci_map_single (NULL, (void *) urb->buffer + urb->actual_length,
						   urb->endpoint->rcv_packetSize,
						   PCI_DMA_FROMDEVICE))) {
			restore_flags (flags);
			dbg_rx (0, "pci_map_single failed");
			return;
		}

		SET_AND_TEST (*(UDCCS1) = UDCCS1_RPC, _udc (UDCCS1) & UDCCS1_RPC, ok);


		// enable DMA only if packet to big for FIFO (twenty bytes)
		if (ep1_endpoint->rcv_packetSize > 20) {
			dma_rx_active = 
                            _sa1100_bi_dma_queue_buffer_irq (dmachn_rx, dma_rx_curpos, urb->endpoint->rcv_packetSize);
			_sa1100_bi_dma_run (dmachn_rx, dma_rx_active);
		}

		restore_flags (flags);
	}
}


/* ep1 public functions ************************************************************************ */

/**
 * ep1_init - initialize the endpoint
 *
 */
void ep1_enable (struct usb_device_instance *device, struct usb_endpoint_instance *endpoint,
		 int chn)
{
	if (endpoint) {
		int ok;
		ep1_device = device;
		ep1_endpoint = endpoint;
		dmachn_rx = chn;

                dbg_rx (1, "ep1_endpoint %p", ep1_endpoint);

		//usbd_fill_rcv(device, endpoint, 5);

		SET_AND_TEST (*(UDCOMP) =
			      ep1_endpoint->rcv_packetSize - 1,
			      udc (UDCOMP) != ep1_endpoint->rcv_packetSize - 1, ok);
		if (!ok) {
			dbg_rx (0, "FAILED setting pktsize %d", *(UDCOMP));
		}
		// setup the dma engine operating parameters
		dma_is_active = 0;
		dma_enabled = 1;
                if (!ep1_endpoint->rcv_urb) {
                        ep1_endpoint->rcv_urb = first_urb_detached (&ep1_endpoint->rdy);
                        dbg_rx (1, "SETTING ep1_endpoint->rcv_urb: %p", ep1_endpoint->rcv_urb);
                }
                else {
                        dbg_rx (1, "CHECKING ep1_endpoint->rcv_urb: %p", ep1_endpoint->rcv_urb);
                }
		ep1_start_dma (ep1_endpoint->rcv_urb);
	}
}


/**
 * ep1_reset - reset the endpoint
 *
 * Return non-zero if error.
 */
void ep1_reset (void)
{
	int ok;

	dbg_rx (1, "CHECKING ep1_endpoint->rcv_urb: %p", ep1_endpoint->rcv_urb);

	ep1_stop_dma (ep1_endpoint->rcv_urb);

	if (ep1_endpoint->rcv_urb) {
		ep1_endpoint->rcv_urb->actual_length = 0;
	}
	ep1_start_dma (ep1_endpoint->rcv_urb);

	udc_reset_ep (1);
	SET_AND_TEST (*(UDCOMP) =
		      ep1_endpoint->rcv_packetSize - 1,
		      udc (UDCOMP) != ep1_endpoint->rcv_packetSize - 1, ok);
	if (!ok) {
		dbg_rx (0, "FAILED setting pktsize %d", *(UDCOMP));
	}
}


/**
 * ep1_disable - disable endpoint for use
 *
 */
void ep1_disable ()
{
	dbg_rx (1, "dma_is_active: %d dma_enabled: %d", dma_is_active, dma_enabled);

	if (dma_is_active) {
		ep1_stop_dma (ep1_endpoint->rcv_urb);
	}

	if (!dma_enabled) {
		dbg_rx (1, "!dma_enabled: %x %x %x", dmachn_rx, dma_enabled, dma_is_active);
		return;
	}
	{
		unsigned long flags;
		save_flags_cli (flags);
		if (dma_is_active) {
			restore_flags (flags);
			dbg_rx (1, "dma_is_active: %x %x %x", dmachn_rx, dma_enabled, dma_is_active);
			ep1_stop_dma (ep1_endpoint->rcv_urb);
			usbd_dealloc_urb (ep1_endpoint->rcv_urb);
                        dbg_rx(1, "CLEARING rcv_urb %p", ep1_endpoint->rcv_urb);
			ep1_endpoint->rcv_urb = NULL;
		}
		dma_enabled = 0;
		restore_flags (flags);
	}

	usbd_flush_rcv (ep1_endpoint);
	ep1_endpoint = NULL;
}


/*
 * Endpoint 1 interrupts will be directed here.
 *
 * status : the contents of the UDC Status/Interrupt Register.
 */
void ep1_int_hndlr (unsigned int status)
{
	dma_addr_t dma_address;
	unsigned int udccs1;
	int ok;
	unsigned int len = 0;
	unsigned int urb_bad = 0;

	rx_interrupts++;

	if (!((udccs1 = *(UDCCS1)) & UDCCS1_RPC)) {
		SET_AND_TEST (*(UDCCS1) = UDCCS1_SST, _udc (UDCCS1) & UDCCS1_SST, ok);
		SET_AND_TEST (*(UDCCS1) = UDCCS1_RPC, _udc (UDCCS1) & UDCCS1_RPC, ok);
		return;
	}
	//dbg_rx(4, "udccs1: %02x", udccs1);

	dma_is_active = 0;

	// DMA or non-DMA
	// XXX - we should be able to collapse these together
	if (ep1_endpoint->rcv_packetSize <= 20) {

		// non-DMA version
		if (!(udccs1 & (UDCCS1_RPE))) {

			// get residual data from fifo
			if (ep1_endpoint->rcv_urb) {
				unsigned char *dmabuf;

				dmabuf = ep1_endpoint->rcv_urb->buffer + ep1_endpoint->rcv_urb->actual_length;

				// XXX Note that it is apparantly impossible to do this with 100%
				// reliability. Only copy data in if/while length is less than packet size

				for (;
				     (len < (unsigned) ep1_endpoint->rcv_packetSize)
				     && *(UDCCS1) & UDCCS1_RNE; len++) {
					*dmabuf++ = *(UDCDR);
					//udelay(1);
				}
				// record an error if the FIFO is not empty
				udc_ep1_errors += ((urb_bad = *(UDCCS1) & UDCCS1_RNE)) ? 1 : 0;
			} else {
				dbg_rx (0, "NO RCV URB");
			}
		} else {
			udc_rpe_errors++;
		}
	}
	// retrieve dma_address, note that this is not reliable
	else if (!_sa1100_bi_dma_stop_get_current_irq (dmachn_rx, &dma_address, dma_rx_active)) {

		// DMA version
		// stop dma engine, if we can get the current address, unmap, get residual data, and give to bottom half
		pci_unmap_single (NULL, dma_rx_curpos, ep1_endpoint->rcv_packetSize,
				  PCI_DMA_FROMDEVICE);

		if (!(udccs1 & (UDCCS1_RPE))) {

			// get residual data from fifo
			if (ep1_endpoint->rcv_urb) {
				unsigned char *dmabuf;

				len = dma_address - dma_rx_curpos;
				dmabuf = ep1_endpoint->rcv_urb->buffer + ep1_endpoint->rcv_urb->actual_length + len;

				/* WARNING - it is impossible empty the FIFO with 100% accuracy. 
				 *
				 * We only copy data in if/while length is less than packet size
				 *
				 * There are two problems. First fetching the dma address can fail. In this case len will
				 * be too large and we do not attempt to read any data.
				 *
				 * Second, reads sometimes fail to update the FIFO read address resulting in too much
				 * data being retrieved. Slightly delaying between each read helps to reduce this problem
				 * at the expense of increasing latency.
				 *
				 * This (and the similiar problem with the TX FIFO) are the reason that it is NOT SAFE
				 * to operate the SA1100 without a CRC across all bulk transfers. The normal USB CRC which
				 * the hardware uses to protect each packet being sent is not sufficent. The data is being
				 * damaged getting it into and out of the FIFO(s).
				 */

				for (;
				     (len < (unsigned) ep1_endpoint->rcv_packetSize)
				     && *(UDCCS1) & UDCCS1_RNE; len++) {
					*dmabuf++ = *(UDCDR);
					udelay (0);
				}

				// record an error if the FIFO is not empty
				udc_ep1_errors += ((urb_bad = *(UDCCS1) & UDCCS1_RNE)) ? 1 : 0;
			} else {
				dbg_rx (0, "NO RCV URB");
			}
		} else {
			udc_rpe_errors++;
		}
	} else {
		//dbg_rx(0, "nothing to unmap");
	}

	// let the upper levels handle the urb
	usbd_rcv_complete_irq (ep1_endpoint, len, urb_bad);

	// clear SST if necessary
	if (udccs1 & UDCCS1_SST) {
		SET_AND_TEST (*(UDCCS1) = UDCCS1_SST, _udc (UDCCS1) & UDCCS1_SST, ok);
		if (!ok) {
			dbg_rx (0, "could not clear SST: %02x", *(UDCCS1));
		}
	}
	// start DMA and reset RPC 
	SET_AND_TEST (*(UDCCS1) = UDCCS1_RPC, _udc (UDCCS1) & UDCCS1_RPC, ok);
	if (!ok) {
		dbg_rx (0, "could not clear RPC: %02x", *(UDCCS1));
	}

	if (dma_enabled && ep1_endpoint->rcv_urb) {
		dma_is_active = 1;

		// start DMA if packetsize to big for FIFO (twenty bytes)
		if ((dma_rx_curpos =
		     pci_map_single (NULL,
				     (void *) ep1_endpoint->rcv_urb->buffer +
				     ep1_endpoint->rcv_urb->actual_length,
				     ep1_endpoint->rcv_packetSize, PCI_DMA_FROMDEVICE))
		    && (ep1_endpoint->rcv_packetSize > 20)) 
                {
			dma_rx_active = _sa1100_bi_dma_queue_buffer_irq (dmachn_rx, dma_rx_curpos, ep1_endpoint->rcv_packetSize);
			_sa1100_bi_dma_run (dmachn_rx, dma_rx_active);
		}
	}
}

/* ep2 dma engine functions ******************************************************************** */



static int dmachn_tx;		// tx dma channel
static int dma_tx_active;
dma_addr_t dma_tx_curpos;

static struct usb_endpoint_instance *ep2_endpoint;

/* *
 * ep2_reset_dma - 
 * @restart:
 *
 * Reset the DMA engine parameters. 
 */
static void ep2_reset_dma (void)
{
	unsigned long flags;

	local_irq_save (flags);

	// stop current dma
	_sa1100_bi_dma_flush_all_irq (dmachn_tx);
	udelay (10);		// XXX

	ep2_endpoint->tx_urb = NULL;
	if (dma_tx_curpos) {
		pci_unmap_single (NULL, dma_tx_curpos, ep2_endpoint->last, PCI_DMA_TODEVICE);
	}
	dma_tx_curpos = (dma_addr_t) NULL;
	ep2_endpoint->tx_urb = NULL;
	ep2_endpoint->sent = 0;

	usbd_flush_tx (ep2_endpoint);

	local_irq_restore (flags);
}


/* ep2 public functions ************************************************************************ */

/**
 * ep2_enable -
 *
 * Initialize the dma variables. Called once.
 *
 */
int ep2_enable (struct usb_device_instance *device, struct usb_endpoint_instance *endpoint, int chn)
{
	dbgENTER (dbgflg_usbdbi_tx, 1);
	dmachn_tx = chn;
	ep2_disable ();
	//ep2_device = device;
	{
		unsigned long flags;
		local_irq_save (flags);
		ep2_endpoint = endpoint;
		dma_tx_curpos = 0;
		ep2_endpoint->sent = 0;
		ep2_endpoint->tx_urb = NULL;
		local_irq_restore (flags);
	}
	return 0;
}


/*
 * Initialise the DMA system to use the given chn for receiving
 * packets over endpoint 2.
 *
 * chn     : the dma channel to use.
 * returns : 0 if ok, -1 if channel couldn't be used.
 */
void ep2_disable (void)
{
	unsigned long flags;
	dbgENTER (dbgflg_usbdbi_tx, 1);
	local_irq_save (flags);
	if (ep2_endpoint) {
		ep2_reset_dma ();
		ep2_endpoint = NULL;
	}
	local_irq_restore (flags);
}


/*
 * reset any txpkts, it will be up to the client driver to free up the send
 * queue.
 */
void ep2_reset (void)
{
	dbgENTER (dbgflg_usbdbi_tx, 1);
	ep2_reset_dma ();
	udc_reset_ep (2);
}


/* Interrupt service routines ****************************************************************** */


/*
 *
 * Interrupt handler - called with interrupts disabled.
 */
void ep2_int_hndlr (unsigned int status, int in_interrupt)
{
	unsigned int udccs2;
	int ok;
	int restart = 0;

	udccs2 = *(UDCCS2);

	// do not do anything if FST is set
	if (udccs2 & UDCCS2_FST) {
		dbg_tx (0, "FST set! (%sint)", in_interrupt ? "" : "not-");
		udc_ep2_fst++;
		return;
	} else if (udccs2 & UDCCS2_SST) {
		dbg_tx (0, "SST set, stalling! (%sint)", in_interrupt ? "" : "not-");
		udc_stall_ep (2);
		return;
	}
	// if in interrupt we have to stop any current activity
	if (in_interrupt) {

		tx_interrupts++;

		if (!((udccs2 = *(UDCCS2)) & UDCCS2_TPC)) {
			return;
		}

		_sa1100_bi_dma_flush_all_irq (dmachn_tx);

		// if requred reset stall and restart 
		if (udccs2 & (UDCCS2_TPE | UDCCS2_TUR | UDCCS2_SST)) {

			if (udccs2 & UDCCS2_SST) {
				SET_AND_TEST (*(UDCCS2) =
					      UDCCS2_SST, _udc (UDCCS2) & UDCCS2_SST, ok);
				if (!ok) {
					dbg_tx (0, "Waiting too long to set SST %02x", *(UDCCS2));
				}
			}
			udc_ep2_errors++;
			restart = 1;
			if (udccs2 & UDCCS2_TPE) {
				udc_ep2_tpe++;
			}
			//QQQ was: if (udccs2 & UDCCS2_TUR | UDCCS2_SST) 
			if (udccs2 & UDCCS2_TUR) {
				udc_ep2_tur++;
			}
			if (udccs2 & UDCCS2_SST) {
				udc_ep2_sst++;
			}

		}
		// if we have an active buffer, umap the buffer and update position if previous send was successful
		if (dma_tx_curpos) {
			pci_unmap_single (NULL, dma_tx_curpos, ep2_endpoint->last, PCI_DMA_TODEVICE);
			dma_tx_curpos = (dma_addr_t) NULL;
		}
	}

	usbd_tx_complete_irq (ep2_endpoint, restart);

	// start dma if we have something to send
	if (ep2_endpoint->tx_urb
	    && ((ep2_endpoint->tx_urb->actual_length - ep2_endpoint->sent) > 0)) {
		int ok1, ok2;
		//int count;
		ep2_endpoint->last = MIN (ep2_endpoint->tx_urb->actual_length - ep2_endpoint->sent,
					  ep2_endpoint->tx_urb->endpoint->tx_packetSize);

		if (!
		    (dma_tx_curpos =
		     pci_map_single (NULL, ep2_endpoint->tx_urb->buffer + ep2_endpoint->sent,
				     ep2_endpoint->last, PCI_DMA_TODEVICE))) {
			dbg_tx (0, "pci_map_single failed");
		}

		/* The order of the following is critical....
		 * Reseting the TPC clears the FIFO, so the DMA engine cannot be started until after
		 * TPC is set. But this opens a window where the USB UCD can fail because it is
		 * waiting for the DMA engine to fill the FIFO and it receives an IN packet from
		 * the host. The best we can do is to minimize the window. [Note that this effect
		 * is most noticeable on slow, 133Mhz, processors.]
		 *
		 * 1. Set IMP and all DMA registers, but do not start DMA yet
		 * 2. reset TPC
		 * 3. start DMA
		 */
		SET_AND_TEST (*(UDCIMP) =
			      ep2_endpoint->last - 1, _udc (UDCIMP) != (ep2_endpoint->last - 1),
			      ok1);
		if (ep2_endpoint->last > 16) {
			dma_tx_active = _sa1100_bi_dma_queue_buffer_irq (dmachn_tx, dma_tx_curpos, ep2_endpoint->last);

			//for (count = 8; count--; *(UDCDR) = '\0');

			SET_AND_TEST (*(UDCCS2) = UDCCS2_TPC, _udc (UDCCS2) & UDCCS2_TPC, ok2);
			_sa1100_bi_dma_run (dmachn_tx, dma_tx_active);

			if (!ok1 || !ok2) {
				dbg_tx (0, "Waiting too long to set UDCIMP %02x or TPC: %02x",
					*(UDCIMP), *(UDCCS2));
			}
			dbg_tx (4, "queued %d bytes using DMA", ep2_endpoint->last);
		} else {

			// Entire packet is small enough to fit in FIFO, stuff it
			// in directly and forget DMA.

			unsigned char *dmabuf = (unsigned char *) dma_tx_curpos;
			int count = ep2_endpoint->last;

			SET_AND_TEST (*(UDCCS2) = UDCCS2_TPC, _udc (UDCCS2) & UDCCS2_TPC, ok2);
			for (; count--; *(UDCDR) = *dmabuf++);

			dbg_tx (4, "queued %d bytes directly to FIFO", ep2_endpoint->last);
		}
	}
}

/* Clock Tick Debug support ****************************************************************** */


#define RETRYTIME 10
#if defined(CONFIG_USBD_STALL_TIMEOUT)
  // If an URB waits more than...
#define USBD_STALL_TIMEOUT_SECONDS        CONFIG_USBD_STALL_TIMEOUT
#else
#define USBD_STALL_TIMEOUT_SECONDS        0	// Stall watchdog hobbled
#endif

#if defined(CONFIG_USBD_STALL_DISCONNECT_DURATION)
#define USBD_STALL_DISCONNECT_DURATION    CONFIG_USBD_STALL_DISCONNECT_DURATION
#else
#define USBD_STALL_DISCONNECT_DURATION    2	// seconds
#endif

char hexdigit (int hex)
{
	hex &= 0xf;
	return (hex < 0xa) ? ('0' + hex) : ('a' + hex - 0xa);
}

int hex2buf (char *buf, char *str, int num)
{
	char *cp = buf;
	while (*str) {
		*cp++ = *str++;
	}
	*cp++ = hexdigit (num >> 4);
	*cp++ = hexdigit (num & 0xf);
	*cp++ = ' ';
	return (cp - buf);
}

extern int send_length;
extern int send_todo;
extern int sent_last;

static void show_info (void)
{
	char buf[100];
	char *cp;
	volatile short int gpdr;
/*
    if (udc_interrupts_last == udc_interrupts) {
        udc_irq_enable();
    }
*/
	if (udc_interrupts != udc_interrupts_last) {
		printk (KERN_DEBUG "--------------\n");
	}
	// do some work
	memset (buf, 0, sizeof (buf));
	cp = buf;
	cp += hex2buf (cp, "CCR:", *(UDCCR));
	cp += hex2buf (cp, "CSR:", *(UDCSR));
	cp += hex2buf (cp, "CAR:", *(UDCAR));
	cp += hex2buf (cp, "CS0:", *(UDCCS0));
	cp += hex2buf (cp, "CS1:", *(UDCCS1));
	cp += hex2buf (cp, "CS2:", *(UDCCS2));
	cp += hex2buf (cp, "IMP:", *(UDCIMP));
	cp += hex2buf (cp, "OMP:", *(UDCOMP));
	cp += hex2buf (cp, "WC:", *(UDCWC));
	cp += hex2buf (cp, "SR:", *(UDCSR));

	gpdr = GPDR;
	printk (KERN_DEBUG
		"[%u] %s int:%2d ep0:%d rx:%d tx:%d sus:%d res:%d er:%d re:%d e1:%d e2:%d tpe:%d tur:%d sst:%d fst:%d tck:%d fix:%d\n",
		udc_interrupts, buf, udc_interrupts - udc_interrupts_last,
		ep0_interrupts - ep0_interrupts_last,
		rx_interrupts - rx_interrupts_last,
		tx_interrupts - tx_interrupts_last,
		sus_interrupts, res_interrupts,
		udc_address_errors,
		udc_rpe_errors,
		udc_ep1_errors,
		udc_ep2_errors,
		udc_ep2_tpe, udc_ep2_tur, udc_ep2_sst, udc_ep2_fst, udc_ticks, udc_fixed);
	udc_interrupts_last = udc_interrupts;
	ep0_interrupts_last = ep0_interrupts;
	rx_interrupts_last = rx_interrupts;
	tx_interrupts_last = tx_interrupts;
}

void udc_regs (void)
{
	if (_udc (UDCAR) != usb_address) {
		printk (KERN_DEBUG "ADDRESS ERROR DETECTED\n");
		udc_address_errors++;
	}
	*(UDCAR) = usb_address;

	show_info ();
}