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


/*
 * linux/drivers/usbd/l7205_bi/udc.c -- L7205 USB controller driver. 
 *
 * Copyright (c) 2000, 2001, 2002 Lineo
 *
 * 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.
 *
 */


// Debug enabling defines
//#define TRIGGER       1
//#define FLAG_F1ERR    1
// End of debug enabling defines

#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 

#include 

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

#ifdef CONFIG_IRIS
#include 
#include 
#include 
#include 
#endif

#include "hardware.h"
#include "l7205.h"
#include "udc.h"

int sof_saw_tx_active;
int sof_saw_rx_f1ne;		// track if we have seen F1NE at SOF
int host_sus_interrupt_disabled;	// set if SUS interrupt received and disabled, waiting for SOF
int host_reset_interrupt_disabled;	// set if HRST interrupt received and disabled, waiting for SOF
int ep2_active;			// set if transmit on ep2 is active, used to track missing interrupts

extern unsigned int udc_interrupts;
extern unsigned int udc_interrupts_last;

static struct usb_device_instance *udc_device;	// for interrupt handler

struct usb_device_instance *ep1_device;
struct usb_endpoint_instance *ep1_endpoint;


#ifdef CONFIG_IRIS
#define USB_PULLUP_GPIO bitPB6
// USB pullup resistor control.
// USB cable and AC power connect status
extern int iris_read_usb_sync (void);
extern int iris_read_ac_sync (void);
extern void iris_AUXPLL_ON_for_usb (void);
extern void iris_AUXPLL_OFF_for_usb (void);
#endif

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

struct usb_device_instance *ep2_device;
struct usb_endpoint_instance *ep2_endpoint;

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

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

/**
 * ep1_clear
 *
 * Clear ep1 fifo by reading all data and resetting F1BCNT to 32
 */
static /* __inline__ */ void ep1_clear (void)
{
	int j;
	volatile unsigned long junk;
	for (j = 0; j < 8; j++) {
		junk = IO_USBF_FIFO1;
	}
	IO_USBF_F1BCNT = 32;
}

/*
 * Endpoint 1 interrupts will be directed here.
 *
 */
static __inline__ void ep1_int_hndlr (void)
{
	if (ep1_endpoint) {

		if (!ep1_endpoint->rcv_urb) {

			ep1_endpoint->rcv_urb = first_urb_detached (&ep1_endpoint->rdy);

			// XXX could we call usbd_fill_rcv() here?
		}

		if (ep1_endpoint->rcv_urb) {

			int len = IO_USBF_F1BCNT;
			unsigned long *lp =
			    (unsigned long *) (ep1_endpoint->rcv_urb->buffer +
					       ep1_endpoint->rcv_urb->actual_length);

			// copy 4 bytes at a time, fetching more than available seems ok, use unrolled loop
			lp[0] = IO_USBF_FIFO1;
			lp[1] = IO_USBF_FIFO1;
			lp[2] = IO_USBF_FIFO1;
			lp[3] = IO_USBF_FIFO1;

			lp[4] = IO_USBF_FIFO1;
			lp[5] = IO_USBF_FIFO1;
			lp[6] = IO_USBF_FIFO1;
			lp[7] = IO_USBF_FIFO1;

			usbd_rcv_complete_irq (ep1_endpoint, len, 0);

			// reset F1BCNT
			IO_USBF_F1BCNT = 32;
			return;
		}
		// fall through if error of any type
		usbd_rcv_complete_irq (ep1_endpoint, 0, 0);
	}
	ep1_clear ();
}

/*
 * Endpoint 1 interrupts will be directed here if there was an error.
 *
 */
void ep1_int_hndlr_error (void)
{
#if 1
	int i;
	volatile unsigned char *cp = (volatile unsigned char *) IO_USBF_FIFO_RX;
	printk (KERN_DEBUG "ep1_int_hndlr: ERROR ");
	for (i = 0; i < 32; i++) {
		printk ("%02x ", cp[i]);
	}
	printk ("\n");
#endif

	if (ep1_endpoint) {
		usbd_rcv_complete_irq (ep1_endpoint, 0, 0);
	}
	ep1_clear ();
}

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

static int __inline__ ep2_fill (unsigned char *cp, int len, int delay)
{
	unsigned long *buf = (unsigned long *) cp;
	volatile unsigned long *p2 = (volatile unsigned long *) IO_USBF_FIFO_TX;

	// unrolled loop to fill fifo
	IO_USBF_FIFO2 = buf[0];
	IO_USBF_FIFO2 = buf[1];
	IO_USBF_FIFO2 = buf[2];
	IO_USBF_FIFO2 = buf[3];
	IO_USBF_FIFO2 = buf[4];
	IO_USBF_FIFO2 = buf[5];
	IO_USBF_FIFO2 = buf[6];
	IO_USBF_FIFO2 = buf[7];

	if ((buf[0] != p2[0]) || (buf[1] != p2[1]) || (buf[2] != p2[2]) || (buf[3] != p2[3]) ||
	    (buf[4] != p2[4]) || (buf[5] != p2[5]) || (buf[6] != p2[6]) || (buf[7] != p2[7])) {
		udelay (8);	// 
		// reset fifo read pointer
		l7205_toggle (IO_USBF_CONTROL, USBF_CONTROL_F2CLR);

		// unrolled loop to fill fifo
		IO_USBF_FIFO2 = buf[0];
		IO_USBF_FIFO2 = buf[1];
		IO_USBF_FIFO2 = buf[2];
		IO_USBF_FIFO2 = buf[3];
		IO_USBF_FIFO2 = buf[4];
		IO_USBF_FIFO2 = buf[5];
		IO_USBF_FIFO2 = buf[6];
		IO_USBF_FIFO2 = buf[7];

		return (buf[0] != p2[0]) || (buf[1] != p2[1]) || (buf[2] != p2[2])
		    || (buf[3] != p2[3]) || (buf[4] != p2[4]) || (buf[5] != p2[5])
		    || (buf[6] != p2[6]) || (buf[7] != p2[7]);
	}
	return 0;
}

/**
 * ep2_start
 *
 * Fill ep2 fifo with data.
 */
static void ep2_start (void)
{
	int len;

	if (ep2_endpoint->tx_urb &&
	    (len = MIN ((ep2_endpoint->tx_urb->actual_length - ep2_endpoint->sent),
			ep2_endpoint->tx_urb->endpoint->tx_packetSize))) {
		ep2_active = 1;

		// fill the FIFO, if this fails send a short packet to end of this
		// attempt of the BULK transfer and force the restart of this urb at
		// th beginning, the host will see a CRC error and drop the
		// intermediate results

		if (ep2_fill (ep2_endpoint->tx_urb->buffer + ep2_endpoint->sent, len, 0)) {

			// ep2_fill failed, reset bulk transfer and decrement len to
			// send current usb packet one byte short to guarantee host will
			// terminate bulk transfer and it will fail CRC check

			IO_USBF_F2BCNT = len - 1;
			ep2_endpoint->last = ep2_endpoint->sent = 0;
		} else {
			ep2_endpoint->last = IO_USBF_F2BCNT = len;
		}
	} else {
		ep2_active = 0;
	}
}

/**
 * ep2_int_hndlr - transmit interrupt handler, fast version
 */
static __inline__ void ep2_int_hndlr (unsigned int status)
{
	// if we have active buffer, umap the buffer and update position if previous send was successful
	if (!(status & (USBF_STATUS_F2ERR | USBF_STATUS_F2BSY))) {
		if (ep2_endpoint->tx_urb) {

			ep2_endpoint->sent += ep2_endpoint->last;
			ep2_endpoint->last = 0;

			// if current buffer is finished call urb sent and advance to next urb 
			if ((ep2_endpoint->tx_urb->actual_length - ep2_endpoint->sent) <= 0) {
				usbd_urb_sent_irq (ep2_endpoint->tx_urb, SEND_FINISHED_OK);
				ep2_endpoint->tx_urb = NULL;
			}

			ep2_start ();
		}
	}
}

/**
 * ep2_int_hndlr_error - transmit interrupt handler, slow version
 */
static void ep2_int_hndlr_error (unsigned int status, int error, int skip)
{
	if (!skip) {
		if ((status & (USBF_STATUS_F2ERR | USBF_STATUS_F2BSY | USBF_STATUS_F2NE)) || error) {

			if (!(status & USBF_STATUS_F2BSY)) {

				dbg_tx (0, "[%d]: F2ERR %08lx", udc_interrupts, IO_USBF_STATUS);

				l7205_toggle (IO_USBF_CONTROL, USBF_CONTROL_F2CLR);
			}
		} else {
			ep2_int_hndlr (status);
		}
	}
}

/*
 * l7205
 */
void l7205_dump_fifos (char *msg, int max)
{
	int i;

	dbg_udc (9, " ");
	dbg_udc (9, "*********** %s ***********", msg);

	if (7 <= dbgflg_usbdbi_udc) {
		for (i = 0; i < max; i++) {
			if ((i % 32) == 0) {
				printk ("\nFF[%02x]: ", i);
			}
			printk ("%02x ", __IOB (USBF_FIFOS + i));
		}
		printk ("\n");
	}
}

void l7205_regs (char *msg)
{
	if (1 <= dbgflg_usbdbi_udc) {
		int i;
		printk (KERN_DEBUG "\n");
		printk (KERN_DEBUG "*********** %s ***********\n", msg);
		printk (KERN_DEBUG "\n");
		printk (KERN_DEBUG "udc: Rev:  %08lx Ctl:  %08lx Sts:  %08lx\n",
			IO_USBF_REVISION, IO_USBF_CONTROL, IO_USBF_STATUS);
		printk (KERN_DEBUG "udc: IntE: %08lx IntD: %08lx Ep0:  %08lx\n",
			IO_USBF_INTENA, IO_USBF_INTDIS, IO_USBF_ENDPTBUF0);
		printk (KERN_DEBUG "udc: Ep1:  %08lx Ep2:  %08lx Ep3:  %08lx\n",
			IO_USBF_ENDPTBUF1, IO_USBF_ENDPTBUF2, IO_USBF_ENDPTBUF3);
		printk (KERN_DEBUG "udc: CFG:  %08lx ST0:  %08lx ST1:  %08lx\n",
			IO_USBF_CONFIGBUF1, IO_USBF_STRINGBUF0, IO_USBF_STRINGBUF1);
		printk (KERN_DEBUG "udc: ST2:  %08lx ST3:  %08lx ST4:  %08lx\n",
			IO_USBF_STRINGBUF2, IO_USBF_STRINGBUF3, IO_USBF_STRINGBUF4);
		printk (KERN_DEBUG "\n");

		printk (KERN_DEBUG "CLK ENA: %08lx AUX: %08lx SEL: %08lx\n",
			IO_SYS_CLOCK_ENABLE, IO_SYS_CLOCK_AUX, IO_SYS_CLOCK_SELECT);
		printk (KERN_DEBUG "\n");

		printk (KERN_DEBUG
			"udc: CFG:  %02lx:%02lx    ST0:  %02lx:%02lx    ST1:  %02lx:%02lx\n",
			__IOL (USBF_CONFIGBUF1) >> 9, __IOL (USBF_CONFIGBUF1) & 0x1ff,
			__IOL (USBF_STRINGBUF0) >> 9, __IOL (USBF_STRINGBUF0) & 0x1ff,
			__IOL (USBF_STRINGBUF1) >> 9, __IOL (USBF_STRINGBUF1) & 0x1ff);

		printk (KERN_DEBUG
			"udc: ST2:  %02lx:%02lx    ST3:  %02lx:%02lx    ST4:  %02lx:%02lx\n",
			__IOL (USBF_STRINGBUF2) >> 9, __IOL (USBF_STRINGBUF2) & 0x1ff,
			__IOL (USBF_STRINGBUF3) >> 9, __IOL (USBF_STRINGBUF3) & 0x1ff,
			__IOL (USBF_STRINGBUF4) >> 9, __IOL (USBF_STRINGBUF4) & 0x1ff);

		for (i = 0; i < 168; i++) {
			if ((i % 32) == 0) {
				printk ("\nDS[%02x]: ", i);
			}
			printk ("%02x ", __IOB (USBF_DESCRIPTORS + i));
		}
		printk ("\n");
		printk ("\n");
	}
}

void udc_regs (void)
{
	if (1 <= dbgflg_usbdbi_tick) {
		printk (KERN_DEBUG
			"%u Ctl: %08lx Sts: %08lx Raw: %08lx Ena: %08lx EP0: %08lx EP1: %08lx EP2: %08lx "
			"int: %2d\n",
			udc_interrupts,
			IO_USBF_CONTROL, IO_USBF_STATUS, IO_USBF_RAWSTATUS, IO_USBF_INTENA,
			IO_USBF_F0BCNT, IO_USBF_F1BCNT, IO_USBF_F2BCNT,
			udc_interrupts - udc_interrupts_last);
		udc_interrupts_last = udc_interrupts;
	}
}


/*
 * l7205_copy_descriptor
 *
 * Copy descriptor to shared SRAM and optionally set descriptor register to point
 * to where it is located.
 *
 * Note that reg CAN be NULL (there is no device descriptor register it is 
 * always at zero offset)
 */
static int l7205_copy_descriptor (unsigned char *descriptors, volatile unsigned long reg,
				  int offset, struct usb_descriptor *descriptor, int size)
{
	// set default 
	if (reg) {
		__IOL (reg) = 0;
	}

	if (!descriptors || !descriptor) {
		return offset;
	}

	if (!size) {
		if (!(size = descriptor->descriptor.generic.bLength)) {
			return offset;
		}
	}

	if (((offset + size) >= (USBF_DESCRIPTORS_MAX - 4))) {
		dbg_udc (0, "string too large to copy: offset: %x size: %d", offset, size);
		if (reg) {
			__IOL (reg) = (4 << 9) | (USBF_DESCRIPTORS_MAX - 4);
		}
		descriptors[USBF_DESCRIPTORS_MAX - 4] = 4;
		descriptors[USBF_DESCRIPTORS_MAX - 3] = 3;
		descriptors[USBF_DESCRIPTORS_MAX - 2] = 'A';
		descriptors[USBF_DESCRIPTORS_MAX - 1] = 0;
		return offset;
	}

	dbg_udc (3, "reg: %08lx offset: %02x descriptor: %p size: %02x", reg, offset, descriptor,
		 size);

	// set size and offset if we have a register
	if (reg) {
		__IOL (reg) = (size << 9) | offset;
		dbg_udc (3, "reg: %8lx %8lx size: %2lx off: %2lx", reg, __IOL (reg),
			 __IOL (reg) >> 9, __IOL (reg) & 0x1ff);
	}

	memcpy (descriptors + offset, descriptor, size);

	return (offset + size + 3) & 0x1fc;	// 4
}


static int l7205_copy_request (unsigned char *descriptors, struct usb_device_instance *device,
			       volatile unsigned long reg, int offset, int request)
{
	struct urb *udc_urb;
	usb_device_state_t device_state;

	/*
	 * fake a setup request to get descriptors
	 */
	if (!device || !(udc_urb = usbd_alloc_urb (device, NULL, 0, 512))) {
		dbg_udc (0, "cannot alloc urb");
		return offset;
	}

	udc_urb->device_request.bmRequestType = 0x80;
	udc_urb->device_request.bRequest = USB_REQ_GET_DESCRIPTOR;
	udc_urb->device_request.wValue = cpu_to_le16 ((request << 8));
	udc_urb->device_request.wLength = cpu_to_le16 (200);	// max 168?

	device_state = device->device_state;
	device->device_state = STATE_ADDRESSED;

	if (usbd_recv_setup (udc_urb)) {
		dbg_udc (0, "cannot process setup request");
		usbd_dealloc_urb (udc_urb);
		return offset;
	}

	device->device_state = device_state;
	offset =
	    l7205_copy_descriptor (descriptors, reg, offset,
				   (struct usb_descriptor *) udc_urb->buffer,
				   udc_urb->actual_length);
	usbd_dealloc_urb (udc_urb);

	return offset;
}

/**
 * udc_init - initialize L7205 USB Controller
 * 
 * Get ready to use the L7205 USB Controller.
 *
 * Register an interrupt handler and initialize dma.
 */
int udc_init (void)
{
	// This should never happen!
	if (IO_USBF_REVISION != USBF_REVISION_11) {
		dbg_udc (0, "incorrect or not found version: %02lx should be %02x",
			 IO_USBF_REVISION, USBF_REVISION_11);
		return -EINVAL;
	}
	// disable the udc just in case
	udc_disable_interrupts (0);
	udc_disconnect ();
	udc_disable ();
	return 0;
}

/*
 * Switch off the L7205 USB Function
 */
void udc_disable (void)
{
	udc_device = NULL;

	// disable interrupts
	IO_USBF_INTDIS =
	    USBF_INTDIS_DNSUS |
	    USBF_INTDIS_DNF0ERR |
	    USBF_INTDIS_DNF1OR |
	    USBF_INTDIS_DNF1ERR |
	    USBF_INTDIS_DNF2UR |
	    USBF_INTDIS_DNF2ERR |
	    USBF_INTDIS_DNF3ERR |
	    USBF_INTDIS_DNGRSM |
	    USBF_INTDIS_DNF0RQ |
	    USBF_INTDIS_DNF1RQ |
	    USBF_INTDIS_DNF2RQ | USBF_INTDIS_DNF3RQ | USBF_INTDIS_DNSOF | USBF_INTDIS_DNHRST;

	// set USBFUNC_EN
	IO_SYS_CLOCK_ENABLE &= ~SYS_CLOCK_USBFUNC_EN;

	// reset USB function
	// IO_USBF_CONTROL |= USBF_CONTROL_FRST;

	IO_USBF_CONTROL = 0;

	// turn off clocks
#ifdef xCONFIG_IRIS
	iris_AUXPLL_OFF_for_usb ();
#else
	IO_SYS_CLOCK_ENABLE &= ~(SYS_CLOCK_AUXPLL_EN | SYS_CLOCK_AUXCLK_EN);
#endif
}

/*
 * initialize L7205 USB Function
 */
void l7205_init (struct usb_device_instance *device)
{
	//int i;
	int offset;
	unsigned char *descriptors;

	// XXX disable everything
	IO_USBF_CONTROL = 0;
	udelay (20);

#if defined(CONFIG_L7205SDB)
	/*
	 * Linkup reports that there may be a problem with video conflicting with
	 * USB Function, this will disable it.
	 */

	dbg_udc (2, "1. CLCDCON: %08lx SYS_CLOCK_ENABLE: %08lx", IO_CLCDCON, IO_SYS_CLOCK_ENABLE);

	// disable LCD power (bit 22) and wait 20MS
	IO_CLCDCON &= ~0x20000;
	udelay (100);
	dbg_udc (2, "2. CLCDCON: %08lx SYS_CLOCK_ENABLE: %08lx", IO_CLCDCON, IO_SYS_CLOCK_ENABLE);

	// disable LCD (bit 0)
	IO_CLCDCON &= ~0x1;
	dbg_udc (2, "3. CLCDCON: %08lx SYS_CLOCK_ENABLE: %08lx", IO_CLCDCON, IO_SYS_CLOCK_ENABLE);

	// disable Vee and backlight bits 1, 2
	IO_SYS_CLOCK_ENABLE &= ~0x3;
	dbg_udc (2, "4. CLCDCON: %08lx SYS_CLOCK_ENABLE: %08lx", IO_CLCDCON, IO_SYS_CLOCK_ENABLE);
#endif

	/*
	 * Setup FIRCLK to use internal clock - c.f. 13.1.2 Clocks
	 */
	dbg_udc (5, "setting clocks (internal PLL)");

	// clear AUXOSCMUX, AUXPLLMUX and AUXPLLMUL bits of CLOCK_AUX
	IO_SYS_CLOCK_AUX = 0;

	// clear AUX0SCMUX and set AUXPLLMUL to 13 for 48Mhz
	IO_SYS_CLOCK_AUX |= SYS_CLOCK_AUXPLLMUL_48;

	// set FIRAUX_SEL
	IO_SYS_CLOCK_SELECT |= SYS_CLOCK_FIRAUX_SEL;

	// set AUXPLL_EN and AUXCLK_EN 
#ifdef xCONFIG_IRIS
	iris_AUXPLL_ON_for_usb ();
#else
	IO_SYS_CLOCK_ENABLE |= SYS_CLOCK_AUXPLL_EN | SYS_CLOCK_AUXCLK_EN;
#endif

	// wait 8us
	udelay (10);

	// set USBFUNC_EN to enable clock
	IO_SYS_CLOCK_ENABLE |= SYS_CLOCK_USBFUNC_EN;

	// wait 8us
	udelay (10);

	// enable USB Function
	IO_USBF_CONTROL |= USBF_CONTROL_ENBL;
	udelay (20);

	// set program modes
	IO_USBF_CONTROL |= USBF_CONTROL_F1MOD_IO | USBF_CONTROL_F2MOD_IO;

	// Force USB function core reset
	// l7205_toggle(IO_USBF_CONTROL, USBF_CONTROL_FRST);

	// Reset and clear all FIFO's
	// l7205_toggle(IO_USBF_CONTROL, USBF_CONTROL_F0CLR | USBF_CONTROL_F1CLR | USBF_CONTROL_F2CLR | USBF_CONTROL_F3CLR);

	// allocate tmp buffer
	if ((descriptors = kmalloc (USBF_DESCRIPTORS_MAX, GFP_ATOMIC))) {

		// clear the descriptor space
		memset (descriptors, 0, USBF_DESCRIPTORS_MAX);

		// XXX memset((void *)(__IOA(USBF_DESCRIPTORS)), 0, USBF_DESCRIPTORS_MAX);

		// copy in device, configuration, langid and up to four strings
		offset = l7205_copy_request (descriptors, device, 0, 0, USB_DESCRIPTOR_TYPE_DEVICE);
		offset =
		    l7205_copy_request (descriptors, device, USBF_CONFIGBUF1, offset,
					USB_DESCRIPTOR_TYPE_CONFIGURATION);
		offset =
		    l7205_copy_descriptor (descriptors, USBF_STRINGBUF0, offset,
					   (struct usb_descriptor *) usbd_get_string (0), 0);
		offset =
		    l7205_copy_descriptor (descriptors, USBF_STRINGBUF1, offset,
					   (struct usb_descriptor *) usbd_get_string (1), 0);
		offset =
		    l7205_copy_descriptor (descriptors, USBF_STRINGBUF2, offset,
					   (struct usb_descriptor *) usbd_get_string (2), 0);
		offset =
		    l7205_copy_descriptor (descriptors, USBF_STRINGBUF3, offset,
					   (struct usb_descriptor *) usbd_get_string (3), 0);
		offset =
		    l7205_copy_descriptor (descriptors, USBF_STRINGBUF4, offset,
					   (struct usb_descriptor *) usbd_get_string (4), 0);

		// copy descriptors to shared RAM
		memcpy ((unsigned char *) __IOA (USBF_DESCRIPTORS), descriptors,
			USBF_DESCRIPTORS_MAX);
		kfree (descriptors);
	} else {
		// should never happen
		dbg_udc (0, "cannot malloc descriptors");
	}

	/*
	 * Configuration and Intialization - c.f. 13.1.4 and c.f. 13.1.1
	 */
	IO_USBF_ENDPTBUF0 = USBF_ENDPTBUF0_EP0MSIZE_8;
	IO_USBF_ENDPTBUF1 = USBF_ENDPTBUF1_EP1TYPE_BULK | 0x20;
	IO_USBF_ENDPTBUF2 = USBF_ENDPTBUF2_EP2TYPE_BULK | 0x20;
	IO_USBF_ENDPTBUF3 = USBF_ENDPTBUF3_EP3TYPE_INT;

	IO_USBF_F1TOUT = 1;
	IO_USBF_F1BCNT = 0x20;
	IO_USBF_F2BCNT = 0;

	// Force USB function core reset
	l7205_toggle (IO_USBF_CONTROL, USBF_CONTROL_FRST);

	// Reset and clear all FIFO's
	l7205_toggle (IO_USBF_CONTROL,
		      USBF_CONTROL_F0CLR | USBF_CONTROL_F1CLR | USBF_CONTROL_F2CLR |
		      USBF_CONTROL_F3CLR);

	// Clear interrupts
	IO_USBF_INTCLR = 0x3fff;

	// set initialization done bit to indicate descriptors and registers are ready
	IO_USBF_CONTROL |= USBF_CONTROL_INTD;

	// enable interrupts
	IO_USBF_INTENA =
	    USBF_INTENA_ENSUS |
	    USBF_INTENA_ENF0ERR |
	    USBF_INTENA_ENF1OR |
	    USBF_INTENA_ENF1ERR |
	    USBF_INTENA_ENF2UR |
	    USBF_INTENA_ENF2ERR |
	    USBF_INTENA_ENF3ERR |
	    USBF_INTENA_ENGRSM |
	    USBF_INTENA_ENF0RQ |
	    USBF_INTENA_ENF1RQ |
	    USBF_INTENA_ENF2RQ | USBF_INTENA_ENF3RQ | USBF_INTENA_ENSOF | USBF_INTENA_ENHRST;

	// wait 
	udelay (60);		// XXX

	l7205_regs ("finished");
}


/*
 * Switch on the L7205 USB Function
 */
void udc_enable (struct usb_device_instance *device)
{
	udc_device = device;
	l7205_init (udc_device);	// XXX do when Vbus is present....
	return;
}

#if defined(TRIGGER)

static unsigned int max_triggers = 64;

static void send_usb_trigger (unsigned int status, char *id)
{
	/* This code is to insert a "trigger" into the outgoing data stream
	   so that the a USB protocol anlyser can capture the data surrounding
	   it.  It is only used when debugging strange hardware states.
	   The "trigger" is data that looks like "0xdeadbeef". */
	if (max_triggers == 0) {
		printk (KERN_DEBUG "%s & no more triggers\n", id);
		return;
	}
	if (status & USBF_STATUS_F2BSY) {
		printk (KERN_DEBUG "%s & F2BSY\n", id);
	} else {
		static char flag_bytes[48] = {
			0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
			    0xde, 0xad, 0xbe, 0xef,
			0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
			    0xde, 0xad, 0xbe, 0xef,
			0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef,
			    0xde, 0xad, 0xbe, 0xef
		};
		unsigned long *slp;
		unsigned long *txf = IO_USBF_FIFO_TX;
		int i, j, fifo_good;
		slp =
		    (unsigned long *) (void *) (0xfffffffc &
						(unsigned long) (3 + (void *) flag_bytes));
		j = 0;
		do {
			for (i = 0; i < 8; i++) {
				IO_USBF_FIFO2 = slp[i];
			}
			fifo_good = 1;
			for (i = 0; i < 8; i++) {
				fifo_good &= (slp[i] == txf[i]);
			}
		} while (++j < 10 && !fifo_good);
		if (fifo_good) {
			IO_USBF_F2BCNT = 32;
			printk (KERN_DEBUG "deadbeef %u %s\n", max_triggers, id);
			max_triggers -= 1;
		} else {
			// too busy receiving?
			printk (KERN_DEBUG "%s & F1BSY\n", id);
		}
	}
}
#endif

#define STATUS_NO_INTR_MASK   ~(USBF_STATUS_F1NE | USBF_STATUS_F2NF | USBF_STATUS_F2NE | USBF_STATUS_F2BSY | USBF_STATUS_VCCMD);


/**
 * udc_int_hndlr - interrupt handler
 *
 */
void udc_int_hndlr (int irq, void *dev_id, struct pt_regs *regs)
{
	int loopcount;
	unsigned int rawstatus;
	int ep2_int_hndlr_called = 0;

	udc_interrupts++;
	// XXX rawstatus = IO_USBF_RAWSTATUS;
	for (loopcount = 0; 0 != (rawstatus = IO_USBF_RAWSTATUS) && (loopcount < 4); loopcount++) {
		unsigned int status = IO_USBF_STATUS;

		IO_USBF_INTCLR = rawstatus;

		/*
		 * Handle high priority interrupts first to reduce latency and
		 * overhead.
		 */

		if (rawstatus & USBF_RAWSTATUS_RF1RQ) {
			ep1_int_hndlr ();
			sof_saw_rx_f1ne = 0;
		}

		if (rawstatus & USBF_RAWSTATUS_RF2RQ) {
			ep2_int_hndlr (status);
			ep2_int_hndlr_called++;
			sof_saw_tx_active = 0;
		}

		/*
		 * check if we have one of the un-common interrupts, this is faster
		 * than testing for each one sequentially. Note that we will do the
		 * SOF test first, and then another general test for the really
		 * un-common interrupts.
		 */
		if (rawstatus &
		    (USBF_RAWSTATUS_RSUS | USBF_RAWSTATUS_RF0ERR | USBF_RAWSTATUS_RF1OR |
		     USBF_RAWSTATUS_RF1ERR | USBF_RAWSTATUS_RF2ERR | USBF_RAWSTATUS_RF3ERR |
		     USBF_RAWSTATUS_RGRSM | USBF_RAWSTATUS_RF0RQ | USBF_RAWSTATUS_RF3RQ |
		     USBF_RAWSTATUS_RHRST | USBF_RAWSTATUS_RSOF)) {
			/*
			 * SOF - Start of frame interrupt, use as a safe place to check
			 * for missed transmit, receive interrupts, turn back on SUS or
			 * RST interrupts etc.
			 */
			if (rawstatus & USBF_RAWSTATUS_RSOF) {

				/*
				 * Simulate an rx interrupt, if we have not seen a rcv
				 * interrupt recently.
				 */
				if (IO_USBF_STATUS & USBF_STATUS_F1NE) {
					if ((sof_saw_rx_f1ne++ > 2)) {
						dbg_tick (1, "[%d]: F1NE %ld", udc_interrupts,
							  IO_USBF_F1BCNT);
						ep1_clear ();
						sof_saw_rx_f1ne = 0;
					}
				} else {
					sof_saw_rx_f1ne = 0;
				}

				/*
				 * This is required in case we miss a tx interrupt or we
				 * abandoned a tx attempt due to contention with rx fifo
				 */
				if ((sof_saw_tx_active++ > 10) && ep2_active) {
					ep2_int_hndlr_error (status, 0, ep2_int_hndlr_called++);
					sof_saw_tx_active = 0;
				}

				/*
				 * The SUS and HRST interrupts disable themselves, now is
				 * the time to re-enable them.
				 */
				if (host_sus_interrupt_disabled) {
					IO_USBF_INTENA = USBF_INTENA_ENSUS;
					host_sus_interrupt_disabled = 0;
					usbd_device_event (udc_device, DEVICE_BUS_ACTIVITY, 0);
				}
				if (host_reset_interrupt_disabled) {
					IO_USBF_INTENA = USBF_INTENA_ENHRST;
					host_reset_interrupt_disabled = 0;
					usbd_device_event (udc_device, DEVICE_ADDRESS_ASSIGNED, 0);
					udc_device->configuration = 0;
					usbd_device_event (udc_device, DEVICE_CONFIGURED, 0);
					udc_device->interface = 0;
					udc_device->alternate = 0;
					usbd_device_event (udc_device, DEVICE_SET_INTERFACE, 0);
				}

				/*
				 * If these three bits are on all is lost, we will
				 * disconnect.
				 */
				if ((status & USBF_STATUS_F2BSY) && (status & USBF_STATUS_F2NE)
				    && (status & USBF_STATUS_F2NF)) {
					// disconnect and reset udc
					// XXX udc_disconnect();
					// XXX udc_connect();
				}
			}

			/*
			 * check if we have a really uncommon interrupt, this saves
			 * sequential testing of all of these during SOF only
			 * interrupts.
			 */
			if (rawstatus &
			    (USBF_RAWSTATUS_RSUS | USBF_RAWSTATUS_RF0ERR | USBF_RAWSTATUS_RF1OR |
			     USBF_RAWSTATUS_RF1ERR | USBF_RAWSTATUS_RF2ERR | USBF_RAWSTATUS_RF3ERR |
			     USBF_RAWSTATUS_RGRSM | USBF_RAWSTATUS_RF0RQ | USBF_RAWSTATUS_RF3RQ |
			     USBF_RAWSTATUS_RHRST)) {
				/*
				 * SUS - suspend interrupt, issued device event and disable SUS interrupt
				 * until we see an SOF interrupt
				 */
				if (rawstatus & USBF_RAWSTATUS_RSUS) {
					dbg_intr (0, "[%u]: SUS Sts: %08x Raw: %08x",
						  udc_interrupts, status, rawstatus);
					// disable host interrupt
					IO_USBF_INTDIS = USBF_INTDIS_DNSUS;
					host_sus_interrupt_disabled++;
					usbd_device_event (udc_device, DEVICE_BUS_INACTIVE, 0);
				}

				/*
				 * HRST - Host Reset interrupt, issued device event and disable SUS interrupt
				 * until we see an SOF interrupt
				 */
				if (rawstatus & USBF_RAWSTATUS_RHRST) {
					// L7210 AB version would give multiple HRST when the first is cleared,
					// ignore them until SOF interrupt

					dbg_intr (0, "[%u]: HRST", udc_interrupts);
					// XXX l7205_toggle(IO_USBF_CONTROL, USBF_CONTROL_FRST);

					// reset udc
					l7205_init (udc_device);	// XXX 

					// disable host interrupt
					IO_USBF_INTDIS = USBF_INTDIS_DNHRST;
					host_reset_interrupt_disabled++;
					usbd_device_event (udc_device, DEVICE_RESET, 0);
				}

				/*
				 * F1ERR - FIFO 1 error, a receive error, bad news, usually once
				 * this has happened we cannot continue or reset to normal
				 * state. In desparation we will drop connection and hope that
				 * HRST will restore things.
				 */
				if (rawstatus & USBF_RAWSTATUS_RF1ERR) {
					dbg_intr (0, "[%u]: F1ERR Sts: %08x Raw: %08x F1Bcnt: %ld",
						  udc_interrupts, status, rawstatus,
						  IO_USBF_F1BCNT);
					sof_saw_rx_f1ne = 0;
#if defined(FLAG_F1ERR) && defined(TRIGGER)
					send_usb_trigger (status, "F1ERR");
#endif
					udc_disconnect ();
					ep1_int_hndlr_error ();
					udelay (100);
					udc_connect ();
					udelay (100);
					udc_disconnect ();

					// XXX udc_disconnect();
					// XXX l7205_init(udc_device); // XXX 
				}

				/*
				 * Misc Error interrupts - have never seen any of these.
				 */
				if (rawstatus & USBF_RAWSTATUS_RF0ERR) {
					dbg_intr (0, "[%u]: F0ERR Sts: %08x Raw: %08x",
						  udc_interrupts, status, rawstatus);
				}
				if (rawstatus & USBF_RAWSTATUS_RF1OR) {
					dbg_intr (0, "[%u]: F1OR Sts: %08x Raw: %08x",
						  udc_interrupts, status, rawstatus);
					ep1_int_hndlr_error ();
					sof_saw_rx_f1ne = 0;
				}
				if (rawstatus & USBF_RAWSTATUS_RF2UR) {
					dbg_intr (0, "[%u]: F2UR Sts: %08x Raw: %08x",
						  udc_interrupts, status, rawstatus);
					sof_saw_tx_active = 0;
					ep2_int_hndlr_error (status, 1, ep2_int_hndlr_called++);
				}
				if (rawstatus & USBF_RAWSTATUS_RF2ERR) {
					dbg_intr (0, "[%u]: F2ERR Sts: %08x Raw: %08x",
						  udc_interrupts, status, rawstatus);
					sof_saw_tx_active = 0;
					ep2_int_hndlr_error (status, 1, ep2_int_hndlr_called++);
				}
				if (rawstatus & USBF_RAWSTATUS_RF3ERR) {
					dbg_intr (0, "[%u]: F3ERR Sts: %08x Raw: %08x",
						  udc_interrupts, status, rawstatus);
				}
				if (rawstatus & USBF_RAWSTATUS_RGRSM) {
					dbg_intr (0, "[%u]: GRSM Sts: %08x Raw: %08x",
						  udc_interrupts, status, rawstatus);
				}
				if (rawstatus & USBF_RAWSTATUS_RF0RQ) {
					dbg_intr (0, "[%u]: F0RQ Sts: %08x Raw: %08x",
						  udc_interrupts, status, rawstatus);
				}
				if (rawstatus & USBF_RAWSTATUS_RF3RQ) {
					dbg_intr (0, "[%u]: F3RQ Sts: %08x Raw: %08x",
						  udc_interrupts, status, rawstatus);
				}
			}
		}
	}			// end of loopcount loop
}

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

/**
 * udc_request_udc_irq - request UDC interrupt
 *
 * Return non-zero if not successful.
 */
int udc_request_udc_irq ()
{
	// request IRQ 
	if (request_irq
	    (IRQ_USBF, udc_int_hndlr, SA_INTERRUPT | SA_SAMPLE_RANDOM, "L7205 USBD Bus Interface",
	     NULL) != 0) {
		dbg_init (0, "Couldn't request USB irq");
		return -EINVAL;
	}
	return 0;
}

/**
 * udc_start_in_irq - start transmit
 * @eendpoint: endpoint instance
 *
 * Called by bus interface driver to see if we need to start a data transmission.
 */
void udc_start_in_irq (struct usb_endpoint_instance *endpoint)
{
	if (!(IO_USBF_STATUS & USBF_STATUS_F2BSY)) {
		ep2_start ();
	}
}

/**
 * udc_stall_ep - stall endpoint
 * @ep: physical endpoint
 *
 * Stall the endpoint.
 */
void udc_stall_ep (unsigned int ep)
{
}

/**
 * udc_reset_ep - reset endpoint
 * @ep: physical endpoint
 * reset the endpoint.
 *
 * returns : 0 if ok, -1 otherwise
 */
void udc_reset_ep (unsigned int ep)
{
	if (ep < UDC_MAX_ENDPOINTS) {
		// reset

		switch (ep) {
		case 0:
		case 1:
		case 0x82:
			/* do nothing */
			break;
		}
	}
}

/**
 * udc_endpoint_halted - is endpoint halted
 * @ep:
 *
 * Return non-zero if endpoint is halted
 */
int udc_endpoint_halted (unsigned int ep)
{
	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)
{
}


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;
}

/**
 * udc_serial_init - set a serial number if available
 */
int __init udc_serial_init (struct usb_bus_instance *bus)
{
#ifdef CONFIG_IRIS
	iris_serial_number_struct serial;

	if (!iris_get_serial_number (&serial)
	    && (bus->serial_number_str = kmalloc (36, GFP_KERNEL))) {
		sprintf (bus->serial_number_str, "%08x-%08x-%08x-%08x", serial.b3, serial.b2,
			 serial.b1, serial.b0);
		bus->serial_number = crc32 (bus->serial_number_str, 32);
		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 
 * @lep:
 *
 * 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 > 32) {
		return 0;
	}
	switch (logical_endpoint) {
	case 1:
		return 1;
	case 0x82:
		return 2;
	case 0x83:
		return 3;
	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_init (1, "ep: %d", ep);
	if (ep < UDC_MAX_ENDPOINTS) {
		ep_endpoints[ep] = endpoint;
		switch (ep) {
		case 0:	// Control
			break;
		case 1:	// OUT
			usbd_fill_rcv (device, endpoint, 10);	// XXX
			endpoint->rcv_urb = first_urb_detached (&endpoint->rdy);
			ep1_device = device;
			ep1_endpoint = endpoint;
			break;
		case 2:	// IN
			ep2_device = device;
			ep2_endpoint = endpoint;
			break;
		}
	}
}

/**
 * udc_disable_ep - disable endpoint
 * @ep:
 *
 * Disable specified endpoint 
 */
void udc_disable_ep (unsigned int ep)
{
	if (ep < UDC_MAX_ENDPOINTS) {
		struct usb_endpoint_instance *endpoint;

		switch (ep) {
		case 0:	// Control
			break;
		case 1:	// OUT
			ep1_endpoint = NULL;
			break;
		case 2:	// IN
			ep2_device = NULL;
			ep2_endpoint = NULL;
			break;
		}

		if ((endpoint = ep_endpoints[ep])) {
			ep_endpoints[ep] = NULL;
			usbd_flush_ep (endpoint);
		}

	}
}

/**
 * udc_connected - is the USB cable connected
 *
 * Return non-zeron if cable is connected.
 */
int udc_connected ()
{
#ifdef CONFIG_IRIS
	// Return TRUE if USB cable shows Vbus _and_ AC power present
	dbg_pur (0, "Vbus: %x AC: %x", iris_read_usb_sync (), iris_read_ac_sync ());
	return (iris_read_usb_sync () && iris_read_ac_sync ());
#else
	return 1;
#endif
}

/**
 * udc_connect - enable pullup resistor
 *
 * Turn on the USB connection by enabling the pullup resistor.
 */
void udc_connect (void)
{
#ifdef CONFIG_IRIS
	// Enable the pullup resistor.
	dbg_pur (0, "Enabling IRIS USB pullup resistor");
	SET_PB_OUT (USB_PULLUP_GPIO);
	SET_PBDR_HI (USB_PULLUP_GPIO);
	SET_PBSBSR_LO (USB_PULLUP_GPIO);
#endif
}

/**
 * udc_disconnect - disable pullup resistor
 *
 * Turn off the USB connection by disabling the pullup resistor.
 */
void udc_disconnect (void)
{
#ifdef CONFIG_IRIS
	// Disable the pullup resistor.
	dbg_pur (0, "Disabling IRIS USB pullup resistor");
	SET_PB_OUT (USB_PULLUP_GPIO);
	SET_PBDR_LO (USB_PULLUP_GPIO);
	SET_PBSBSR_LO (USB_PULLUP_GPIO);
#endif
}

/**
 * udc_all_interrupts - enable interrupts
 *
 * Switch on UDC interrupts.
 *
 */
void udc_all_interrupts (struct usb_device_instance *device)
{
	dbg_init (1, " ");
	// set interrupt mask
}

/**
 * udc_suspended_interrupts - enable suspended interrupts
 *
 * Switch on only UDC resume interrupt.
 *
 */
void udc_suspended_interrupts (struct usb_device_instance *device)
{
	dbg_init (1, " ");
	// set interrupt mask
}

/**
 * udc_disable_interrupts - disable interrupts.
 *
 * switch off interrupts
 */
void udc_disable_interrupts (struct usb_device_instance *device)
{
	dbg_init (1, " ");
	// reset interrupt mask
}

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

/**
 * udc_startup_events - 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);

	// XXX moved to first SOF after HRST
	//usbd_device_event(device, DEVICE_RESET, 0);            // XXX should be done from device event
	// XXX usbd_device_event(device, DEVICE_ADDRESS_ASSIGNED, 0);
	//device->configuration = 0; 
	// XXX usbd_device_event(device, DEVICE_CONFIGURED, 0);
	//device->interface = 1; 
	//device->alternate = 0;
	// XXX usbd_device_event(device, DEVICE_SET_INTERFACE, 0);
}

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

/**
 * udc_request_cable_irq - request Cable interrupt
 *
 * Return non-zero if not successful.
 */
int udc_request_cable_irq ()
{
	return 0;
}

#ifdef CONFIG_USBD_PROCFS
/* Proc Filesystem *************************************************************************** */

/* *
 * l7205_proc_read - implement proc file system read.
 * @file
 * @buf
 * @count
 * @pos
 *
 * Standard proc file system read function.
 */
static ssize_t l7205_proc_read (struct file *file, char *buf, size_t count, loff_t * pos)
{
	unsigned long page;
	int len = 0;
	int index;

	MOD_INC_USE_COUNT;
	// get a page, max 4095 bytes of data...
	if (!(page = get_free_page (GFP_KERNEL))) {
		MOD_DEC_USE_COUNT;
		return -ENOMEM;
	}

	len = 0;
	index = (*pos)++;

	if (index == 0) {
		len += sprintf ((char *) page + len, "l7205 UDC status\n");
	}

	if (index == 1) {
		len +=
		    sprintf ((char *) page + len, "%s\n",
			     udc_connected ()? "Connected" : "Not connected");
	}

	if (len > count) {
		len = -EINVAL;
	} else if (len > 0 && copy_to_user (buf, (char *) page, len)) {
		len = -EFAULT;
	}
	free_page (page);
	MOD_DEC_USE_COUNT;
	return len;
}

/* *
 * l7205_proc_write - implement proc file system write.
 * @file
 * @buf
 * @count
 * @pos
 *
 * Proc file system write function, used to signal monitor actions complete.
 * (Hotplug script (or whatever) writes to the file to signal the completion
 * of the script.)  An ugly hack.
 */
static ssize_t l7205_proc_write (struct file *file, const char *buf, size_t count, loff_t * pos)
{
	size_t n = count;
	char command[64];
	char *cp = command;
	int i = 0;
	MOD_INC_USE_COUNT;
	//printk(KERN_DEBUG "%s: count=%u\n",__FUNCTION__,count);
	while ((n > 0) && (i < 64)) {
		// Not too efficient, but it shouldn't matter
		if (copy_from_user (cp++, buf + (count - n), 1)) {
			count = -EFAULT;
			break;
		}
		*cp = '\0';
		i++;
		n -= 1;
		//printk(KERN_DEBUG "%s: %u/%u %02x\n",__FUNCTION__,count-n,count,c);
	}
	if (!strncmp (command, "plug", 4)) {
		udc_connect ();
	} else if (!strncmp (command, "unplug", 6)) {
		udc_disconnect ();
	}
	MOD_DEC_USE_COUNT;
	return (count);
}

static struct file_operations l7205_proc_operations_functions = {
	read:l7205_proc_read,
	write:l7205_proc_write,
};

#endif

/**
 * udc_request_udc_io - request UDC io region
 *
 * Return non-zero if not successful.
 */
int udc_request_io ()
{
#ifdef CONFIG_USBD_PROCFS
	{
		struct proc_dir_entry *p;

		// create proc filesystem entries
		if ((p = create_proc_entry ("l7205", 0, 0)) == NULL) {
			return -ENOMEM;
		}
		p->proc_fops = &l7205_proc_operations_functions;
	}
#endif
	return 0;
}

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

/**
 * udc_release_cable_irq - release Cable irq
 */
void udc_release_cable_irq ()
{
}

/**
 * udc_release_release_io - release UDC io region
 */
void udc_release_io ()
{
#ifdef CONFIG_USBD_PROCFS
	remove_proc_entry ("l7205", NULL);
#endif

}