Gadget Samsung S3C2443 driver problem

Hello,

I have a problem with a driver that control gadget serial linking with the USB device in a S3C2443 platform and I need some help. I work in a critical control system and I have to configurate it throw the USB device. The problem appear when I connect a Windows 7 PC with a USB 3.0 hub device to my system, if I connect the two ports of the Hub, one with the USB wire and the other for example with a optical mouse the kernel keep totally freeze, until I disconnect the USB wire and don’t respond to any command. In fact my system make a reset due to an external watch-dog.

I think the problem is in the S3C2443_udc.c driver that control the IRQ handlers and the endpoints of the USB device. I attach the source file for more information. I activate the Debug traces and I reproduce the problem explained before:
….
<7>s3c2443-udc: s3c24xx_udc_irq() High Speed interrupt
<7>s3c2443-udc: s3c24xx_udc_irq() UDC IRQ: stat 0x00000050 (0x00000010) | in 0x00000000 | out 0x00000000
<7>s3c2443-udc: s3c24xx_udc_irq() High Speed interrupt
<7>s3c2443-udc: s3c24xx_udc_irq() UDC IRQ: stat 0x00000050 (0x00000010) | in 0x00000000 | out 0x00000000
………….

This traces repeat over and over again until I plug-out the USB wire. I think the system enter in a infinite loop handling this interrupt. Could you help me? I need a patch for this issue or an explanation to make a patch. I need that my system never halt due to plug-in a USB wire.

Thanks in advance.

Code:

/* -*- linux-c -*-
 *
 * drivers/usb/gadget/s3c24xx_udc.c
 *
 * Samsung S3C on-chip full/high speed USB device controllers
 *
 * $Id: s3c-udc-hs.c,v 1.26 2007/02/22 09:45:04 ihlee215 Exp $*
 *
 * Copyright (C) 2006 for Samsung Electronics
 *
 * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include "s3c2443_udc.h"
#include 
#include 
#include 

/* @TODO: USB Device DMA support */
#define RX_DMA_MODE 0
#define TX_DMA_MODE 0

#if 0
#define DEBUG_S3C2443_UDC
#endif

#define pk_err(fmt, args...)			printk(KERN_ERR "[ ERROR ] s3c2443-udc: " fmt, ## args)
#define pk_info(fmt, args...)			printk(KERN_DEBUG "s3c2443-udc: " fmt, ## args)

#ifdef DEBUG_S3C2443_UDC
#define pk_dbg(fmt, args...)			printk(KERN_DEBUG "s3c2443-udc: %s() " fmt, __func__ , \
							## args)
#else
#define pk_dbg(fmt, args...)			do { } while(0)
#endif

#if 0
#define S3C2443_UDC_DBG_OUT
#endif

#if defined(S3C2443_UDC_DBG_OUT)
#define pk_dbg_out(fmt, args...)		printk(KERN_DEBUG "[OUT] " fmt, ## args)
#else
#define pk_dbg_out(fmt, args...)		do { } while(0)
#endif /* S3C2443_UDC_DBG_OUT */

/*
 * This macro enables the debug messages when the driver is going to access to the
 * internal queue of the IN-endpoints
 */
#if 0
#define DEBUG_S3C2443_UDC_QUEUE
#endif

/* Some driver infos */
#define	DRIVER_DESC		                "S3C2443 Dual-speed USB Device"
#define DRIVER_NAME                             "s3c2443_udc"
#define	DRIVER_BUILD_TIME	                 __TIME__
#define	DRIVER_BUILD_DATE	                 __DATE__

#define IOMEMSIZE(s)		                (s-&gt;end - s-&gt;start + 1)

/* Internal variables */
struct s3c24xx_udc *the_controller;
static const char driver_desc[] = DRIVER_DESC;
static const char ep0name[] = "ep0-control";

/* Max packet sizes */
static u32 ep0_fifo_size = 64;
static u32 ep_fifo_size =  512;
static u32 ep_fifo_size2 = 1024;

/* Internal functions */
static int s3c24xx_udc_ep_enable(struct usb_ep *ep,
				 const struct usb_endpoint_descriptor *);
static int s3c24xx_udc_ep_disable(struct usb_ep *ep);
static struct usb_request *s3c24xx_udc_alloc_request(struct usb_ep *ep, gfp_t gfp_flags);
static void s3c24xx_udc_free_request(struct usb_ep *ep, struct usb_request *);
static int s3c24xx_udc_queue(struct usb_ep *ep, struct usb_request *, gfp_t gfp_flags);
static int s3c24xx_udc_dequeue(struct usb_ep *ep, struct usb_request *);
static int s3c24xx_udc_set_halt(struct usb_ep *ep, int);
static int s3c24xx_udc_fifo_status(struct usb_ep *ep);
static void s3c24xx_udc_fifo_flush(struct usb_ep *ep);
static void s3c24xx_udc_ep0_kick(struct s3c24xx_udc *udc, struct s3c_ep *ep);
static void s3c24xx_handle_ep0(struct s3c24xx_udc *udc);
static void done(struct s3c_ep *ep, struct s3c_request *req, int status);
static void stop_activity(struct s3c24xx_udc *dev, struct usb_gadget_driver *driver);
static int s3c24xx_udc_enable(struct s3c24xx_udc *udc);
static void s3c24xx_udc_set_address(struct s3c24xx_udc *dev, unsigned char address);
static void reconfig_usbd(struct s3c24xx_udc *udc);
static void s3c24xx_ep0_setup(struct s3c24xx_udc *udc);
static int s3c24xx_udc_write_fifo(struct s3c_ep *ep, struct s3c_request *req);


static inline struct s3c24xx_udc *gadget_to_udc(struct usb_gadget *gadget)
{
        return container_of(gadget, struct s3c24xx_udc, gadget);
}

static spinlock_t regs_lock = SPIN_LOCK_UNLOCKED;

static inline void s3c2443_print_err_packet_setup(int errcode,
						  struct usb_ctrlrequest *pctrl)
{
	printk(KERN_DEBUG "[ ERROR ] s3c2443-udc: Err %i | bRequestType 0x%02x | "
	       "bRequest 0x%02x | wValue 0x%04x | wIndex 0x%04x | wLength %u
",
	       errcode, pctrl-&gt;bRequestType, pctrl-&gt;bRequest,
	       pctrl-&gt;wValue, pctrl-&gt;wIndex, pctrl-&gt;wLength);
}

/* Read access to one of the indexed registers */
static inline ulong usb_read(struct s3c24xx_udc *udc, ulong port, u8 ind)
{
	ulong retval;

	spin_lock(&amp;regs_lock);
	writel(ind, udc-&gt;base + S3C24XX_UDC_IR_REG);
	retval = readl(udc-&gt;base + port);
	spin_unlock(&amp;regs_lock);
	return retval;
}

/* Write access to one of the indexed registers */
static inline void usb_write(struct s3c24xx_udc *udc, ulong val, ulong port, u8 ind)
{
	spin_lock(&amp;regs_lock);
	writel(ind, udc-&gt;base + S3C24XX_UDC_IR_REG);
	writel(val, udc-&gt;base + port);
	spin_unlock(&amp;regs_lock);
}

static inline void usb_set(struct s3c24xx_udc *udc, ulong val, ulong port, u8 ind)
{
	spin_lock(&amp;regs_lock);
	writel(ind, udc-&gt;base + S3C24XX_UDC_IR_REG);
	writel(readl(udc-&gt;base + port) | val, udc-&gt;base + port);
	spin_unlock(&amp;regs_lock);
}

static inline void usb_clear(struct s3c24xx_udc *udc, ulong val, ulong port, u8 ind)
{
	spin_lock(&amp;regs_lock);
	writel(ind, udc-&gt;base + S3C24XX_UDC_IR_REG);
	writel(readl(udc-&gt;base + port) &amp; ~val, udc-&gt;base + port);
	spin_unlock(&amp;regs_lock);
}

/* Return a value different than zero if the EP is enabled */
static inline int s3c24xx_ep_enabled(struct s3c24xx_udc *udc, int epnr)
{
	ulong regval;

	regval = readl(udc-&gt;base + S3C24XX_UDC_EIER_REG);
	return (regval &amp; (1 &lt;&lt; epnr));
}

/* Enable/disable the interrupt of the passed EP-number */
static inline void s3c24xx_ep_irq_enable(struct s3c24xx_udc *udc, int epnr, int enable)
{
	ulong eier;

	eier = readl(udc-&gt;base + S3C24XX_UDC_EIER_REG);
	if (enable)
		eier |= (1 &lt;&lt; epnr);
	else
		eier &amp;= ~(1 &lt;&lt; epnr);
	writel(eier, udc-&gt;base + S3C24XX_UDC_EIER_REG);
}

static inline void s3c2443_udc_print_regs(char *marke, struct s3c24xx_udc *udc, int epnr)
{
	struct regs_t {
		char *name;
		ulong addr;
	};

	int pos, old_epnr;
	ulong regval;
	static const struct regs_t regs[] = {
		{ "EIR   ", S3C24XX_UDC_EIR_REG },
		{ "EIER  ", S3C24XX_UDC_EIER_REG },
		{ "EDR   ", S3C24XX_UDC_EDR_REG },
		{ "TR    ", S3C24XX_UDC_TR_REG },
		{ "SSR   ", S3C24XX_UDC_SSR_REG },
		{ "SCR   ", S3C24XX_UDC_SCR_REG },
		{ "EP0SR ", S3C24XX_UDC_EP0SR_REG },
		{ "FCON  ", S3C24XX_UDC_FIFO_CON_REG },
		{ "FSTAT ", S3C24XX_UDC_FIFO_STATUS_REG },
		{ "ESR   ", S3C24XX_UDC_ESR_REG },
		{ "ECR   ", S3C24XX_UDC_ECR_REG },
		{ "BRCR  ", S3C24XX_UDC_BRCR_REG },
		{ "BWCR  ", S3C24XX_UDC_BWCR_REG },
	};

	/* First get a backup of the current EP number */
	old_epnr = readl(udc-&gt;base + S3C24XX_UDC_IR_REG);
	writel(epnr, udc-&gt;base + S3C24XX_UDC_IR_REG);

	/* Now print all the registers */
	printk(KERN_DEBUG "%s
", marke);
	for (pos = 0; pos &lt; ARRAY_SIZE(regs); pos++) {
		regval = usb_read(udc, regs[pos].addr, epnr);
		printk(KERN_DEBUG "%s: 0x%08lx
", regs[pos].name, regval);
	}

	writel(old_epnr, udc-&gt;base + S3C24XX_UDC_IR_REG);
}

static struct usb_ep_ops s3c24xx_ep_ops = {
	.enable = s3c24xx_udc_ep_enable,
	.disable = s3c24xx_udc_ep_disable,

	.alloc_request = s3c24xx_udc_alloc_request,
	.free_request = s3c24xx_udc_free_request,

	.queue = s3c24xx_udc_queue,
	.dequeue = s3c24xx_udc_dequeue,

	.set_halt = s3c24xx_udc_set_halt,
	.fifo_status = s3c24xx_udc_fifo_status,
	.fifo_flush = s3c24xx_udc_fifo_flush,
};

/*
 * Function for writing from the request buffer into the EP-FIFO
 * The function updates the internal actual length of the USB-request for a possible
 * next transfer of the same request.
 * The return value is the number of remaining bytes in the request. If the return
 * value is equal zero, then there is no more data to process in the request
 * (Luis Galdos)
 */
static inline int s3c24xx_udc_write_packet(struct s3c_ep *ep, struct s3c_request *req)
{
	u16 *buf;
	int length, count;
	u32 fifo = ep-&gt;fifo;
	struct s3c24xx_udc *udc;
	int max, remaining, epnr;
	u8 *ptr;

	/* @XXX: Need some sanity checks (Luis Galdos) */
	udc = ep-&gt;dev;
	max = ep-&gt;ep.maxpacket;
	epnr = ep_index(ep);

	/* Get the number of remaining bytes */
	remaining = req-&gt;req.length - req-&gt;req.actual;
	if (!remaining) {
		pk_dbg("EP%i: Sending ZLP (actual: %i)
",
			     epnr, req-&gt;req.actual);

		/* Send a frame with zero length */
		/* usb_set(udc, S3C24XX_UDC_ECR_TZLS, S3C24XX_UDC_ECR_REG, epnr); */
		usb_write(udc, 0, S3C24XX_UDC_BWCR_REG, epnr);

		length = remaining;
		goto exit_write_packet;
	}

	/* Use first a u8 pointer for obtaining the correct buffer address */
	ptr = req-&gt;req.buf + req-&gt;req.actual;
	buf = (u16 *)ptr;
	prefetch(buf);

	/* Only send the maximal allowed number of bytes */
	length = min(remaining, max);
	req-&gt;req.actual += length;

	/* First write the number of bytes to transfer, and then fill the FIFO */
	usb_write(udc, length, S3C24XX_UDC_BWCR_REG, epnr);
	for (count = 0; count &lt; length; count += 2)
		writel(*buf++, udc-&gt;base + fifo);

	/* Return the number of remaining bytes of the passed request */
exit_write_packet:
	return (remaining - length);
}

/*
 * Test function which returns the number of bytes written into the FIFO.
 * This new function was implemented due an unknown issue registered with the
 * Ethernet-gadget (the UDC send packets with size of 514 bytes!)
 * (Luis Galdos)
 */
static inline int s3c24xx_udc_write_packet2(struct s3c_ep *ep, struct s3c_request *req)
{
	u16 *buf;
	int length, count;
	u32 fifo = ep-&gt;fifo;
	struct s3c24xx_udc *udc;
	int max, remaining, epnr;
	u8 *ptr;

	udc = ep-&gt;dev;
	max = ep-&gt;ep.maxpacket;
	epnr = ep_index(ep);

	/* Get the number of remaining bytes */
	remaining = req-&gt;req.length - req-&gt;req.actual;
	if (!remaining) {

		pk_dbg("EP%i: Sending ZLP (actual: %i)
", epnr,
			     req-&gt;req.actual);

		/*
		 * Send a frame with zero length.
		 * DONT use the TZLS control bit of the EP control register ECR.
		 */
		usb_write(udc, 0, S3C24XX_UDC_BWCR_REG, epnr);
		length = 0;
		goto exit_write_packet;
	}

	/* Use first an u8 pointer for obtaining the correct buffer address */
	ptr = req-&gt;req.buf + req-&gt;req.actual;
	buf = (u16 *)ptr;
	prefetch(buf);

	/* Only send the maximal allowed number of bytes */
	length = min(remaining, max);
	req-&gt;req.actual += length;

	/* First write the number of bytes to transfer, and then fill the FIFO */
	usb_write(udc, length, S3C24XX_UDC_BWCR_REG, epnr);
	for (count = 0; count &lt; length; count += 2)
		writel(*buf++, udc-&gt;base + fifo);

	/* Sanity check before writting into the FIFO */
#if defined(DEBUG_S3C2443_UDC_QUEUE)
	{
		ulong esr;

		esr = usb_read(udc, S3C24XX_UDC_ESR_REG, ep_index(ep));
		printk(KERN_DEBUG
		       "%p: len=%i, act=%i, ep=%02x, bwcr=0x%04x, esr=0x%04lx
",
		       req, req-&gt;req.length, req-&gt;req.actual, epnr, length, esr);
	}
#endif

	/* Return the number of remaining bytes of the passed request */
 exit_write_packet:
	return length;
}

/*
 * Check the current state of the VBUS pin. If no VBUS pin was passed through the
 * platform data, then assume the bus is always ON.
 * (Luis Galdos)
 */
static inline int s3c2443_udc_vbus_state(struct s3c24xx_udc *udc)
{
	int retval;
	struct s3c2410_udc_mach_info *info;

	info = udc-&gt;mach_info;
	retval = 1;
	if (info &amp;&amp; info-&gt;vbus_pin) {
		unsigned long iocfg;

		/* @XXX: Do we really need to change to INPUT first? */
		iocfg = s3c2410_gpio_getcfg(info-&gt;vbus_pin);
		s3c2410_gpio_cfgpin(info-&gt;vbus_pin, S3C2410_GPIO_INPUT);
                retval = s3c2410_gpio_getpin(info-&gt;vbus_pin);
                s3c2410_gpio_cfgpin(info-&gt;vbus_pin, iocfg);

                if (info-&gt;vbus_pin_inverted)
                        retval = !retval;
	}

	return retval;
}

/*
 * Disable the controller by resetting the PHY for informing the USB-host
 * that the device was disconnected
 * (Luis Galdos)
 */
static void s3c24xx_udc_disable(struct s3c24xx_udc *udc)
{
	ulong regval;

	pk_dbg("UDC disable called
");

	/* Disable the EP interrupts */
	writel(0, udc-&gt;base + S3C24XX_UDC_EIER_REG);
	writel(0xff, udc-&gt;base + S3C24XX_UDC_EIR_REG);

	/* Clear all the status bits of the EP0 and flush it */
	writel(S3C24XX_UDC_EP0SR_RSR | S3C24XX_UDC_EP0SR_TST |
	       S3C24XX_UDC_EP0SR_SHT | S3C24XX_UDC_EP0SR_LWO,
	       udc-&gt;base + S3C24XX_UDC_EP0SR_REG);
	writel(0, udc-&gt;base + S3C24XX_UDC_EP0CR_REG);

	/* Unset the function address */
	s3c24xx_udc_set_address(udc, 0);

	udc-&gt;ep0state = WAIT_FOR_SETUP;
	udc-&gt;gadget.speed = USB_SPEED_UNKNOWN;
	udc-&gt;usb_address = 0;

	/* Clear all the status bits from the system status register */
	regval = S3C24XX_UDC_INT_RESET | S3C24XX_UDC_INT_SUSPEND |
		S3C24XX_UDC_INT_RESUME | S3C24XX_UDC_INT_SDE |
		S3C24XX_UDC_SSR_TBM | S3C24XX_UDC_INT_VBUSON |
		S3C24XX_UDC_SSR_VBUSOFF;
	writel(regval, udc-&gt;base + S3C24XX_UDC_SSR_REG);

	/* Reset the USB-function and the PHY */
	writel(S3C2443_URSTCON_PHY | S3C2443_URSTCON_FUNC, S3C2443_URSTCON);

	/* PHY power disable */
	regval = readl(S3C2443_PWRCFG);
	regval &amp;= ~S3C2443_PWRCFG_USBPHY_ON;
	writel(regval, S3C2443_PWRCFG);
}

/*
 * Function for sending request data to the FIFO
 * This function uses the EP-lock for avoiding the wrong queue order of the packets
 * that are incoming from the Gadget-driver
 * (Luis Galdos)
 */
static void s3c24xx_udc_epin_tasklet_func(unsigned long data)
{
	struct s3c_ep *ep;
	struct s3c_request *req;
	int retval;
	ulong esr;
	struct s3c24xx_udc *udc;

	ep = (struct s3c_ep *)data;
	if (!ep) {
		pk_err("Invalid EP pointer. Aborting %s
", __func__);
		return;
	}

	spin_lock(&amp;ep-&gt;lock);

	udc = ep-&gt;dev;
	esr = usb_read(udc, S3C24XX_UDC_ESR_REG, ep_index(ep));

	/*
	 * Paranoic sanity check: If the FIFO has still a packet, then abort this
	 * tasklet and wait for the call from the interrupt handler (TPS)
	 */
	if (S3C24XX_UDC_ESR_PSIFNR(esr) == 2) {
		pk_dbg("The FIFO seems to have still a packet
");
		goto exit_unlock;
	}

	/* Check if there is a pending request for us */
	if (list_empty(&amp;ep-&gt;queue))
		goto exit_unlock;

	/* Get the next request from the queue of the endpoint */
	req = list_entry(ep-&gt;queue.next, struct s3c_request, queue);
	if (!req) {
		pk_err("EP%i: NULL request pointer.
", ep_index(ep));
		goto exit_unlock;
	}

#if defined(DEBUG_S3C2443_UDC_QUEUE)
	{
		u8 ch1, ch2;
		int len, act;
		u8 *ptr = (u8 *)req-&gt;req.buf;
		len = req-&gt;req.length;
		act = req-&gt;req.actual;
		ch1 = *ptr;
		ch2 = *(ptr + len - 1);
		printk(KERN_DEBUG "%p: act=%i, ep=%02x, 0x%02x ... 0x%02x
",
		       req, act, ep_index(ep), ch1, ch2);
	}
#endif
	retval = s3c24xx_udc_write_fifo(ep, req);

 exit_unlock:
	spin_unlock(&amp;ep-&gt;lock);
}

/*
 * Restart the UDC and the corresponding resources (tasklet, queues, etc.)
 * (Luis Galdos)
 */
static void s3c24xx_udc_reinit(struct s3c24xx_udc *udc)
{
	u32 i;

	/* device/ep0 records init */
	INIT_LIST_HEAD(&amp;udc-&gt;gadget.ep_list);
	INIT_LIST_HEAD(&amp;udc-&gt;gadget.ep0-&gt;ep_list);
	udc-&gt;ep0state = WAIT_FOR_SETUP;

	/* basic endpoint records init */
	for (i = 0; i &lt; S3C_MAX_ENDPOINTS; i++) {
		struct s3c_ep *ep = &amp;udc-&gt;ep[i];

		if (i != 0)
			list_add_tail(&amp;ep-&gt;ep.ep_list, &amp;udc-&gt;gadget.ep_list);

		ep-&gt;desc = 0;
		ep-&gt;stopped = 0;
		INIT_LIST_HEAD(&amp;ep-&gt;queue);
		ep-&gt;pio_irqs = 0;
	}

	/* the rest was statically initialized, and is read-only */
}

#define BYTES2MAXP(x)	(x / 8)
#define MAXP2BYTES(x)	(x * 8)

/*
 * Until it's enabled, this UDC should be completely invisible
 * to any USB host.
 */
static int s3c24xx_udc_enable(struct s3c24xx_udc *udc)
{
	unsigned long regval;

	pk_dbg("UDC enable called
");

	/* First disable the HOST functionality! */
	regval = __raw_readl(S3C2443_UCLKCON);
	regval &amp;= ~S3C2443_UCLKCON_HOST_ENABLE;
	regval |= S3C2443_UCLKCON_THOST_DISABLE;
	__raw_writel(regval, S3C2443_UCLKCON);

	/* if reset by sleep wakeup, control the retention I/O cell */
	if (__raw_readl(S3C2443_RSTSTAT) &amp; 0x8)
		__raw_writel(__raw_readl(S3C2443_RSTCON)|(1&lt;&lt;16), S3C2443_RSTCON);

	/* PHY power enable */
	regval = __raw_readl(S3C2443_PWRCFG);
	regval |= S3C2443_PWRCFG_USBPHY_ON;
	__raw_writel(regval, S3C2443_PWRCFG);

	/*
	 * USB device 2.0 must reset like bellow,
	 * 1st phy reset and after at least 10us, func_reset &amp; host reset
	 * phy reset can reset bellow registers.
	 */
	/* PHY 2.0 S/W reset */
	regval = S3C2443_URSTCON_PHY;
	__raw_writel(regval, S3C2443_URSTCON);
	udelay(20);
	__raw_writel(0x00, S3C2443_URSTCON);

	/* Function reset, but DONT TOUCH THE HOST! */
	regval = S3C2443_URSTCON_FUNC;
	__raw_writel(regval, S3C2443_URSTCON);
	__raw_writel(0x00, S3C2443_URSTCON);

	/* 48Mhz, Oscillator, External X-tal, device */
	regval = S3C2443_PHYCTRL_EXTCLK_OSCI;
	__raw_writel(regval, S3C2443_PHYCTRL);

	/*
	 * D+ pull up disable(VBUS detect), USB2.0 Function clock Enable,
	 * USB1.1 HOST disable, USB2.0 PHY test enable
	 */
	regval = __raw_readl(S3C2443_UCLKCON);
	regval |= S3C2443_UCLKCON_FUNC_ENABLE;
	__raw_writel(regval, S3C2443_UCLKCON);

	reconfig_usbd(udc);

	udc-&gt;gadget.speed = USB_SPEED_UNKNOWN;

	/*
	 * So, now enable the pull up, USB2.0 Function clock Enable and
	 * USB2.0 PHY test enable
	 */
	regval = __raw_readl(S3C2443_UCLKCON);
	regval |= S3C2443_UCLKCON_VBUS_PULLUP | S3C2443_UCLKCON_FUNC_ENABLE |
		S3C2443_UCLKCON_TFUNC_ENABLE | S3C2443_UCLKCON_THOST_DISABLE;
	__raw_writel(regval, S3C2443_UCLKCON);
	return 0;
}

/*
 * Function called from the Gadget-drivers for registering a new profile.
 */
int usb_gadget_register_driver(struct usb_gadget_driver *driver)
{
	struct s3c24xx_udc *udc = the_controller;
	int retval;

	if (!driver)
		return -EINVAL;

	pk_dbg("Starting to register '%s'
", driver-&gt;driver.name);

	if (driver-&gt;speed != USB_SPEED_FULL &amp;&amp; driver-&gt;speed != USB_SPEED_HIGH) {
		pk_err("Only Full and High speed supported.
");
		return -EINVAL;
	}

	/*
	 * The 'unbind' function is not required when the Gadget driver is compiled
	 * as built-in (Luis Galdos)
	 */
	if (!driver-&gt;bind || !driver-&gt;disconnect || !driver-&gt;setup) {
		pk_err("Missing function: Bind %p | Disconnect %p | Setup %p
",
			   driver-&gt;bind, driver-&gt;disconnect, driver-&gt;setup);
		return -EINVAL;
	}

	if (!udc) {
		pk_err("No UDC-controller probed? Aborting.
");
		return -ENODEV;
	}

	if (udc-&gt;driver) {
		pk_err("UDC already in use by '%s'
", udc-&gt;driver-&gt;driver.name);
		return -EBUSY;
	}

	/* first hook up the driver ... */
	udc-&gt;driver = driver;
	udc-&gt;gadget.dev.driver = &amp;driver-&gt;driver;

	retval = device_add(&amp;udc-&gt;gadget.dev);
	if (retval) {
		pk_err("Couldn't add the new Gadget device (%i)
", retval);
		goto err_exit;
	}

	retval = driver-&gt;bind(&amp;udc-&gt;gadget);
	if (retval) {
		pk_err("%s: bind to driver %s --&gt; error %d
", udc-&gt;gadget.name,
		       driver-&gt;driver.name, retval);
		goto err_del_device;
	}

	enable_irq(IRQ_USBD);

	/*
	 * If a host was already detected, then only call the UDC enable function,
	 * otherwise check over the configured GPIO if a host is connected.
	 */
	if (udc-&gt;vbus)
		s3c24xx_udc_enable(udc);
	else {
		struct s3c2410_udc_mach_info *info;
		unsigned long state, iocfg;

		info = udc-&gt;mach_info;

		iocfg = s3c2410_gpio_getcfg(info-&gt;vbus_pin);
		s3c2410_gpio_cfgpin(info-&gt;vbus_pin, S3C2410_GPIO_INPUT);
		state = s3c2410_gpio_getpin(info-&gt;vbus_pin);
		s3c2410_gpio_cfgpin(info-&gt;vbus_pin, iocfg);

		if (info-&gt;vbus_pin_inverted)
			state = !state;

		if (state)
			s3c24xx_udc_enable(udc);
	}

	pk_dbg("Gadget '%s' registered
", driver-&gt;driver.name);
	return 0;

 err_del_device:
	device_del(&amp;udc-&gt;gadget.dev);

 err_exit:
	udc-&gt;driver = NULL;
	udc-&gt;gadget.dev.driver = NULL;
	return retval;
}

EXPORT_SYMBOL(usb_gadget_register_driver);

/*
 * Unregister entry point for the peripheral controller driver.
 */
int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
{
	struct s3c24xx_udc *udc = the_controller;
	unsigned long flags;

	if (!udc)
		return -ENODEV;

	if (!driver || driver != udc-&gt;driver || !driver-&gt;unbind)
		return -EINVAL;

	spin_lock_irqsave(&amp;udc-&gt;lock, flags);
	udc-&gt;driver = NULL;
	stop_activity(udc, driver);
	spin_unlock_irqrestore(&amp;udc-&gt;lock, flags);

	driver-&gt;unbind(&amp;udc-&gt;gadget);

	device_del(&amp;udc-&gt;gadget.dev);

	disable_irq(IRQ_USBD);

	pk_dbg("Unregistered gadget driver '%s'
", driver-&gt;driver.name);

	/* Disable the pull-up for informing the host about the removed driver */
	s3c24xx_udc_disable(udc);

	return 0;
}

EXPORT_SYMBOL(usb_gadget_unregister_driver);

/*
 * Write request to FIFO (max write == maxp size)
 * Return:  0 = still running, 1 = completed, negative = errno
 */
static int s3c24xx_udc_write_fifo(struct s3c_ep *ep, struct s3c_request *req)
{
	int max, count;
	int is_last, is_short;

	count = s3c24xx_udc_write_packet2(ep, req);
	max = le16_to_cpu(ep-&gt;desc-&gt;wMaxPacketSize);

	is_short = (count !=  max) ? (1) : (0);

	/* If the packet is short, we dont need to send an additional ZLP */
	if (is_short) {
		pk_dbg("EP%i: Short packet
", ep_index(ep));
		is_last = 1;
	} else {
		if (req-&gt;req.length != req-&gt;req.actual || req-&gt;req.zero)
			is_last = 0;
		else {
			pk_dbg("EP%i: Clear ZLP
", ep_index(ep));
			is_last = 1;
		}
	}

	pk_dbg("TX EP%i: C %i | L %i - A %i | %c %c %c
",
		     ep_index(ep), count, req-&gt;req.length, req-&gt;req.actual,
		     is_last ? 'L':' ', is_short ? 'S':' ', req-&gt;req.zero ? 'Z':' ');

	/* If this was the last packet, then call the done callback */
	if (is_last) {

#if defined(DEBUG_S3C2443_UDC_QUEUE)
		int len, act;
		len = req-&gt;req.length;
		act = req-&gt;req.actual;
		printk(KERN_DEBUG "%p: len=%i, act=%i, ep=%02x [D]
",
		       req, len, act, ep_index(ep));
#endif

		done(ep, req, 0);
	}

	return 0;
}

/*
 * Read to request from FIFO (max read == bytes in fifo)
 * Return: 0 = still running, 1 = completed, negative = errno
 */
static int s3c24xx_udc_read_fifo(struct s3c_ep *ep, struct s3c_request *req)
{
	u32 csr;
	u16 *buf;
	unsigned bufferspace, count, count_bytes, is_short = 0, is_done = 0;
	u32 fifo = ep-&gt;fifo;
	struct s3c24xx_udc *udc;

	udc = ep-&gt;dev;
	csr = usb_read(udc, S3C24XX_UDC_ESR_REG, ep_index(ep));

	/*
	 * If the FIFO is empty then return zero, so that a caller, like the queue-
	 * function, doesn't fail. Returning zero means that the request is not done
	 * and it can be added to the internal EP-request queue
	 * (Luis Galdos)
	 */
	if (!(csr &amp; S3C24XX_UDC_ESR_RPS)) {
		pk_dbg("EP%i: No packet to read.
", ep_index(ep));
		return 0;
	}

	buf = req-&gt;req.buf + req-&gt;req.actual;
	prefetchw(buf);

	/* Calculate the current buffer space */
	bufferspace = req-&gt;req.length - req-&gt;req.actual;

	/* Read all bytes from this packet */
	count = usb_read(udc, S3C24XX_UDC_BRCR_REG, ep_index(ep));
	if (csr &amp; S3C24XX_UDC_ESR_LWO)
		count_bytes = count * 2 - 1;
	else
		count_bytes = count * 2;

	/* Update the actual variable of the request */
	req-&gt;req.actual += min(count_bytes, bufferspace);

	is_short = (count_bytes &lt; ep-&gt;ep.maxpacket);
	is_done = (req-&gt;req.actual == req-&gt;req.length) ? 1 : 0;

	/*
	 * G : Got
	 * A : Actual
	 * T : Total to get
	 */
	if (is_short || is_done) {
		pk_dbg_out("EP%u: G %d | A %d | T %d [%c%c]
",
			   ep_index(ep), count_bytes, req-&gt;req.actual, req-&gt;req.length,
			   is_short ? 'S' : ' ', is_done ? 'D' : ' ');
	}

	while (likely(count-- != 0)) {
		u16 byte = (u16)readl(udc-&gt;base + fifo);

		/*
		 * If there is no more space in the request-buffer, then continue
		 * reading from the FIFO and return with the done value
		 * (Luis Galdos)
		 */
		if (unlikely(bufferspace == 0)) {
			req-&gt;req.status = -EOVERFLOW;
			is_short = 1;
		} else {
			*buf++ = byte;
			bufferspace--;
		}
	}

	/*
	 * If the complete FIFO-data passed into the request-buffer, then
	 * return one, otherwise skip the return
	 * (Luis Galdos)
	 */
	if (is_short || is_done) {
		done(ep, req, 0);
		return 1;
	}

	/* finished that packet.  the next one may be waiting... */
	return 0;
}

/*
 * Retire a request from the internal EP-queue and call the complete
 * function of the Gadget-request
 * (Luis Galdos)
 */
static void done(struct s3c_ep *ep, struct s3c_request *req, int status)
{
	unsigned int stopped = ep-&gt;stopped;

	list_del_init(&amp;req-&gt;queue);

	/*
	 * If the queue is empty and the EP has the OUT direction, then disable
	 * the receive operation, otherwise we will lost some packets from
	 * the host.
	 */
	if (!ep_is_in(ep) &amp;&amp; list_empty(&amp;ep-&gt;queue)) {
		ulong ecr;
		struct s3c24xx_udc *udc;

		udc = ep-&gt;dev;
		ecr = usb_read(udc, S3C24XX_UDC_ECR_REG, ep_index(ep));
		ecr |= S3C24XX_UDC_ECR_OUTPKTHLD;
		usb_write(udc, ecr, S3C24XX_UDC_ECR_REG, ep_index(ep));
	}

	if (likely(req-&gt;req.status == -EINPROGRESS))
		req-&gt;req.status = status;
	else
		status = req-&gt;req.status;

	if (status &amp;&amp; status != -ESHUTDOWN) {
		pk_dbg("EP%i: done req %p | stat %d | actual %u | length %u
",
			     ep_index(ep),
			     &amp;req-&gt;req, status, req-&gt;req.actual, req-&gt;req.length);
	}

	/* don't modify queue heads during completion callback */
	ep-&gt;stopped = 1;

	/*
	 * We must unlock the queue of the EP at this place, then the Gadget-driver
	 * probably will try to enqueue a new request by calling our queue-function.
	 * (Luis Galdos)
	 */
/* 	spin_unlock(&amp;ep-&gt;lock); */
/* 	spin_unlock(&amp;ep-&gt;dev-&gt;lock); */
	req-&gt;req.complete(&amp;ep-&gt;ep, &amp;req-&gt;req);
/* 	spin_lock(&amp;ep-&gt;dev-&gt;lock); */
/* 	spin_lock(&amp;ep-&gt;lock); */

	ep-&gt;stopped = stopped;
}

/* Nuke/dequeue all the requested transfers */
void nuke(struct s3c_ep *ep, int status)
{
	struct s3c_request *req;

	pk_dbg("EP%i: Nuke function called
", ep_index(ep));

	/* called with irqs blocked */
	while (!list_empty(&amp;ep-&gt;queue)) {
		req = list_entry(ep-&gt;queue.next, struct s3c_request, queue);
		done(ep, req, status);
	}
}

/*
 * This function handles the IN-operations of the endpoints different than zero
 */
static void s3c24xx_udc_in_epn(struct s3c24xx_udc *udc, u32 epnr)
{
	ulong esr, handled;
	struct s3c_ep *ep = &amp;udc-&gt;ep[epnr];

	handled = 0;

	spin_lock(&amp;ep-&gt;lock);

	esr = usb_read(udc, S3C24XX_UDC_ESR_REG, epnr);

	/* ACK the function stall condition */
	if (esr &amp; S3C24XX_UDC_ESR_FSC) {
		pk_dbg("EP%i: Function stall
", epnr);
		usb_set(udc, S3C24XX_UDC_ESR_FSC, S3C24XX_UDC_ESR_REG, epnr);
		handled = 1;
	}

	/* The flush operation generates an interrupt too */
	if (esr &amp; S3C24XX_UDC_ESR_FFS) {
		pk_dbg("EP%i: FIFO flush detected
", epnr);
		usb_set(udc, S3C24XX_UDC_ESR_FFS, S3C24XX_UDC_ESR_REG, epnr);
		handled = 1;
	}

	/* Underflow check */
	if (esr &amp; S3C24XX_UDC_ESR_FUDR) {
		pk_dbg("EP%i: Underflow detected
", epnr);
		usb_set(udc, S3C24XX_UDC_ESR_FUDR, S3C24XX_UDC_ESR_REG, epnr);
		handled = 1;
	}

	/* Overflow check */
	if (esr &amp; S3C24XX_UDC_ESR_FOVF) {
		pk_dbg("EP%i: Overflow detected
", epnr);
		usb_set(udc, S3C24XX_UDC_ESR_FOVF, S3C24XX_UDC_ESR_REG, epnr);
		handled = 1;
	}

	/* By successed transfer of a IN-packet then only schedule the tasklet */
	if (esr &amp; S3C24XX_UDC_ESR_TPS) {
		usb_set(udc, S3C24XX_UDC_ESR_TPS, S3C24XX_UDC_ESR_REG, epnr);
		tasklet_hi_schedule(&amp;ep-&gt;in_tasklet);
		handled = 1;
	}

	spin_unlock(&amp;ep-&gt;lock);

	if (!handled)
		pk_info("EP%i: Unhandled IRQ (ESR 0x%04lx)
", epnr, esr);
}

/*
 * This function is used for reading OUT-frames from the EP0. We can't use the same
 * function for the SETUP-requests, then here we must pass the data to the
 * higher Gadget-driver.
 */
static void s3c2443_udc_ep0_read(struct s3c24xx_udc *udc)
{
	ulong ep0sr;
	int bytes, count, bufferspace;
	struct s3c_ep *ep;
	struct s3c_request *req;
	u16 *buf;

	ep = &amp;udc-&gt;ep[0];

	spin_lock(&amp;ep-&gt;lock);

	/*
	 * @FIXME: Remove this delay. At this moment we need it for having a
	 * working RNDIS-support when connected to a WinXP host machine.
	 * (Luis Galdos)
	 */
	if (udc-&gt;ep0state == DATA_STATE_RECV)
		udelay(100);

	/* If there is nothing to read only return at this point */
	ep0sr = readl(udc-&gt;base + S3C24XX_UDC_EP0SR_REG);
        if (!(ep0sr &amp; S3C24XX_UDC_EP0SR_RSR))
		goto exit_unlock;

	/* Check if we are waiting for a setup frame */
	if (udc-&gt;ep0state == WAIT_FOR_SETUP) {
		s3c24xx_ep0_setup(udc);
		goto exit_unlock;
	}

	pk_dbg("Current state of EP0 is %i
", udc-&gt;ep0state);

	/* Now get the number of bytes to read from the FIFO */
        count = usb_read(udc, S3C24XX_UDC_BRCR_REG, ep_index(ep));
        if (ep0sr &amp; S3C24XX_UDC_EP0SR_LWO)
                bytes = count * 2 - 1;
        else
                bytes = count * 2;

	/* Check if we have a request for this data */
	req = list_entry(ep-&gt;queue.next, struct s3c_request, queue);

	if (!req) {
		pk_err("Going to flush a EP0 frame
");
		goto exit_ack;
	}

        buf = req-&gt;req.buf + req-&gt;req.actual;
        prefetchw(buf);
	bufferspace = req-&gt;req.length - req-&gt;req.actual;
	req-&gt;req.actual += min(bytes, bufferspace);

	pk_dbg("EP0 READ: %i bytes | space %i | req.len %i | reg.act %i
",
		     bytes, bufferspace, req-&gt;req.length, req-&gt;req.actual);
	while (likely(count-- != 0)) {
                u16 byte = (u16)readl(udc-&gt;base + ep-&gt;fifo);
		*buf++ = byte;
	}

	/* If we are done with this request then call the corresponding function */
	if (req-&gt;req.length == req-&gt;req.actual) {
		udc-&gt;ep0state = WAIT_FOR_SETUP;
		done(ep, req, 0);
	}

 exit_ack:
	writel(S3C24XX_UDC_EP0_RX_SUCCESS, udc-&gt;base + S3C24XX_UDC_EP0SR_REG);

 exit_unlock:
	spin_unlock(&amp;ep-&gt;lock);
}


/*
 * The below function is called when data was received with an OUT-transaction
 */
static void s3c24xx_udc_out_epn(struct s3c24xx_udc *udc, u32 ep_idx)
{
	struct s3c_ep *ep;
	struct s3c_request *req;
	ulong esr, epnr, handled;

	ep = &amp;udc-&gt;ep[ep_idx];
	epnr = ep_index(ep);
	if (epnr != ep_idx) {
		pk_err("Invalid EP structure (%lu) or index (%u) passed
",
			   epnr, ep_idx);
		return;
	}

	/* Read the status register of the EP */
	handled = 0;
	esr = usb_read(udc, S3C24XX_UDC_ESR_REG, epnr);
	pk_dbg("EP%lu: Status reg 0x%08lx
", epnr, esr);

	if (unlikely(!(ep-&gt;desc))) {
		pk_err("No descriptor for EP%lu
", epnr);
		return;
	}

	if (esr &amp; S3C24XX_UDC_ESR_FSC) {
		pk_dbg("EP%lu stall sent
", epnr);
		usb_set(udc, S3C24XX_UDC_ESR_FSC, S3C24XX_UDC_

If you are still using Digi Embedded Linux 5.2, you should update to Digi Embedded Linux 5.7. 5.7 includes some updates on the USB gadget driver for S3C2443