diff options
| author | Lukasz Dalek <luk0104@gmail.com> | 2012-10-02 17:04:32 +0200 | 
|---|---|---|
| committer | Tom Rini <trini@ti.com> | 2012-10-15 11:54:01 -0700 | 
| commit | 3c09a2836df2f0ca73396ce00104385f0a622015 (patch) | |
| tree | 7ec2fd4ca8669c773e4479eabe58003bbd06105c | |
| parent | 2bb378841e6c093476cd6365aacd7bfdbe60f658 (diff) | |
| download | olio-uboot-2014.01-3c09a2836df2f0ca73396ce00104385f0a622015.tar.xz olio-uboot-2014.01-3c09a2836df2f0ca73396ce00104385f0a622015.zip | |
pxa25x: Add support for USB ethernet gadget
Add to pxa25x based devices support for USB ethernet gadget. This is a
port of pxa25x UDC driver from Linux kernel.
Signed-off-by: Lukasz Dalek <luk0104@gmail.com>
| -rw-r--r-- | drivers/usb/gadget/Makefile | 1 | ||||
| -rw-r--r-- | drivers/usb/gadget/pxa25x_udc.c | 2059 | ||||
| -rw-r--r-- | drivers/usb/gadget/pxa25x_udc.h | 162 | 
3 files changed, 2222 insertions, 0 deletions
| diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 5bbdd368f..040eaba3b 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -36,6 +36,7 @@ ifdef CONFIG_USB_ETHER  COBJS-y += ether.o epautoconf.o config.o usbstring.o  COBJS-$(CONFIG_USB_ETH_RNDIS) += rndis.o  COBJS-$(CONFIG_MV_UDC)	+= mv_udc.o +COBJS-$(CONFIG_CPU_PXA25X) += pxa25x_udc.o  else  # Devices not related to the new gadget layer depend on CONFIG_USB_DEVICE  ifdef CONFIG_USB_DEVICE diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c new file mode 100644 index 000000000..dd741439a --- /dev/null +++ b/drivers/usb/gadget/pxa25x_udc.c @@ -0,0 +1,2059 @@ +/* + * Intel PXA25x and IXP4xx on-chip full speed USB device controllers + * + * Copyright (C) 2002 Intrinsyc, Inc. (Frank Becker) + * Copyright (C) 2003 Robert Schwebel, Pengutronix + * Copyright (C) 2003 Benedikt Spranger, Pengutronix + * Copyright (C) 2003 David Brownell + * Copyright (C) 2003 Joshua Wise + * Copyright (C) 2012 Lukasz Dalek <luk0104@gmail.com> + * + * 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 + * + * MODULE_AUTHOR("Frank Becker, Robert Schwebel, David Brownell"); + */ + +#define CONFIG_USB_PXA25X_SMALL +#define DRIVER_NAME "pxa25x_udc_linux" +#define ARCH_HAS_PREFETCH + +#include <common.h> +#include <errno.h> +#include <asm/byteorder.h> +#include <asm/system.h> +#include <asm/mach-types.h> +#include <asm/unaligned.h> +#include <linux/compat.h> +#include <malloc.h> +#include <asm/io.h> +#include <asm/arch/pxa.h> + +#include <usbdescriptors.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <usb/lin_gadget_compat.h> +#include <asm/arch/pxa-regs.h> + +#include "pxa25x_udc.h" + +/* + * This driver handles the USB Device Controller (UDC) in Intel's PXA 25x + * series processors.  The UDC for the IXP 4xx series is very similar. + * There are fifteen endpoints, in addition to ep0. + * + * Such controller drivers work with a gadget driver.  The gadget driver + * returns descriptors, implements configuration and data protocols used + * by the host to interact with this device, and allocates endpoints to + * the different protocol interfaces.  The controller driver virtualizes + * usb hardware so that the gadget drivers will be more portable. + * + * This UDC hardware wants to implement a bit too much USB protocol, so + * it constrains the sorts of USB configuration change events that work. + * The errata for these chips are misleading; some "fixed" bugs from + * pxa250 a0/a1 b0/b1/b2 sure act like they're still there. + * + * Note that the UDC hardware supports DMA (except on IXP) but that's + * not used here.  IN-DMA (to host) is simple enough, when the data is + * suitably aligned (16 bytes) ... the network stack doesn't do that, + * other software can.  OUT-DMA is buggy in most chip versions, as well + * as poorly designed (data toggle not automatic).  So this driver won't + * bother using DMA.  (Mostly-working IN-DMA support was available in + * kernels before 2.6.23, but was never enabled or well tested.) + */ + +#define DRIVER_VERSION	"18-August-2012" +#define DRIVER_DESC	"PXA 25x USB Device Controller driver" + +static const char driver_name[] = "pxa25x_udc"; +static const char ep0name[] = "ep0"; + +/* Watchdog */ +static inline void start_watchdog(struct pxa25x_udc *udc) +{ +	debug("Started watchdog\n"); +	udc->watchdog.base = get_timer(0); +	udc->watchdog.running = 1; +} + +static inline void stop_watchdog(struct pxa25x_udc *udc) +{ +	udc->watchdog.running = 0; +	debug("Stopped watchdog\n"); +} + +static inline void test_watchdog(struct pxa25x_udc *udc) +{ +	if (!udc->watchdog.running) +		return; + +	debug("watchdog %ld %ld\n", get_timer(udc->watchdog.base), +		udc->watchdog.period); + +	if (get_timer(udc->watchdog.base) >= udc->watchdog.period) { +		stop_watchdog(udc); +		udc->watchdog.function(udc); +	} +} + +static void udc_watchdog(struct pxa25x_udc *dev) +{ +	uint32_t udccs0 = readl(&dev->regs->udccs[0]); + +	debug("Fired up udc_watchdog\n"); + +	local_irq_disable(); +	if (dev->ep0state == EP0_STALL +			&& (udccs0 & UDCCS0_FST) == 0 +			&& (udccs0 & UDCCS0_SST) == 0) { +		writel(UDCCS0_FST|UDCCS0_FTF, &dev->regs->udccs[0]); +		debug("ep0 re-stall\n"); +		start_watchdog(dev); +	} +	local_irq_enable(); +} + +#ifdef DEBUG + +static const char * const state_name[] = { +	"EP0_IDLE", +	"EP0_IN_DATA_PHASE", "EP0_OUT_DATA_PHASE", +	"EP0_END_XFER", "EP0_STALL" +}; + +static void +dump_udccr(const char *label) +{ +	u32 udccr = readl(&UDC_REGS->udccr); +	debug("%s %02X =%s%s%s%s%s%s%s%s\n", +		label, udccr, +		(udccr & UDCCR_REM) ? " rem" : "", +		(udccr & UDCCR_RSTIR) ? " rstir" : "", +		(udccr & UDCCR_SRM) ? " srm" : "", +		(udccr & UDCCR_SUSIR) ? " susir" : "", +		(udccr & UDCCR_RESIR) ? " resir" : "", +		(udccr & UDCCR_RSM) ? " rsm" : "", +		(udccr & UDCCR_UDA) ? " uda" : "", +		(udccr & UDCCR_UDE) ? " ude" : ""); +} + +static void +dump_udccs0(const char *label) +{ +	u32 udccs0 = readl(&UDC_REGS->udccs[0]); + +	debug("%s %s %02X =%s%s%s%s%s%s%s%s\n", +		label, state_name[the_controller->ep0state], udccs0, +		(udccs0 & UDCCS0_SA) ? " sa" : "", +		(udccs0 & UDCCS0_RNE) ? " rne" : "", +		(udccs0 & UDCCS0_FST) ? " fst" : "", +		(udccs0 & UDCCS0_SST) ? " sst" : "", +		(udccs0 & UDCCS0_DRWF) ? " dwrf" : "", +		(udccs0 & UDCCS0_FTF) ? " ftf" : "", +		(udccs0 & UDCCS0_IPR) ? " ipr" : "", +		(udccs0 & UDCCS0_OPR) ? " opr" : ""); +} + +static void +dump_state(struct pxa25x_udc *dev) +{ +	u32 tmp; +	unsigned i; + +	debug("%s, uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n", +		state_name[dev->ep0state], +		readl(&UDC_REGS->uicr1), readl(&UDC_REGS->uicr0), +		readl(&UDC_REGS->usir1), readl(&UDC_REGS->usir0), +		readl(&UDC_REGS->ufnrh), readl(&UDC_REGS->ufnrl)); +	dump_udccr("udccr"); +	if (dev->has_cfr) { +		tmp = readl(&UDC_REGS->udccfr); +		debug("udccfr %02X =%s%s\n", tmp, +			(tmp & UDCCFR_AREN) ? " aren" : "", +			(tmp & UDCCFR_ACM) ? " acm" : ""); +	} + +	if (!dev->driver) { +		debug("no gadget driver bound\n"); +		return; +	} else +		debug("ep0 driver '%s'\n", "ether"); + +	dump_udccs0("udccs0"); +	debug("ep0 IN %lu/%lu, OUT %lu/%lu\n", +		dev->stats.write.bytes, dev->stats.write.ops, +		dev->stats.read.bytes, dev->stats.read.ops); + +	for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++) { +		if (dev->ep[i].desc == NULL) +			continue; +		debug("udccs%d = %02x\n", i, *dev->ep->reg_udccs); +	} +} + +#else /* DEBUG */ + +static inline void dump_udccr(const char *label) { } +static inline void dump_udccs0(const char *label) { } +static inline void dump_state(struct pxa25x_udc *dev) { } + +#endif /* DEBUG */ + +/* + * --------------------------------------------------------------------------- + *	endpoint related parts of the api to the usb controller hardware, + *	used by gadget driver; and the inner talker-to-hardware core. + * --------------------------------------------------------------------------- + */ + +static void pxa25x_ep_fifo_flush(struct usb_ep *ep); +static void nuke(struct pxa25x_ep *, int status); + +/* one GPIO should control a D+ pullup, so host sees this device (or not) */ +static void pullup_off(void) +{ +	struct pxa2xx_udc_mach_info *mach = the_controller->mach; + +	if (mach->udc_command) +		mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT); +} + +static void pullup_on(void) +{ +	struct pxa2xx_udc_mach_info *mach = the_controller->mach; + +	if (mach->udc_command) +		mach->udc_command(PXA2XX_UDC_CMD_CONNECT); +} + +static void pio_irq_enable(int bEndpointAddress) +{ +	bEndpointAddress &= 0xf; +	if (bEndpointAddress < 8) { +		clrbits_le32(&the_controller->regs->uicr0, +			1 << bEndpointAddress); +	} else { +		bEndpointAddress -= 8; +		clrbits_le32(&the_controller->regs->uicr1, +			1 << bEndpointAddress); +	} +} + +static void pio_irq_disable(int bEndpointAddress) +{ +	bEndpointAddress &= 0xf; +	if (bEndpointAddress < 8) { +		setbits_le32(&the_controller->regs->uicr0, +			1 << bEndpointAddress); +	} else { +		bEndpointAddress -= 8; +		setbits_le32(&the_controller->regs->uicr1, +			1 << bEndpointAddress); +	} +} + +static inline void udc_set_mask_UDCCR(int mask) +{ +	/* +	 * The UDCCR reg contains mask and interrupt status bits, +	 * so using '|=' isn't safe as it may ack an interrupt. +	 */ +	const uint32_t mask_bits = UDCCR_REM | UDCCR_SRM | UDCCR_UDE; + +	mask &= mask_bits; +	clrsetbits_le32(&the_controller->regs->udccr, ~mask_bits, mask); +} + +static inline void udc_clear_mask_UDCCR(int mask) +{ +	const uint32_t mask_bits = UDCCR_REM | UDCCR_SRM | UDCCR_UDE; + +	mask = ~mask & mask_bits; +	clrbits_le32(&the_controller->regs->udccr, ~mask); +} + +static inline void udc_ack_int_UDCCR(int mask) +{ +	const uint32_t mask_bits = UDCCR_REM | UDCCR_SRM | UDCCR_UDE; + +	mask &= ~mask_bits; +	clrsetbits_le32(&the_controller->regs->udccr, ~mask_bits, mask); +} + +/* + * endpoint enable/disable + * + * we need to verify the descriptors used to enable endpoints.  since pxa25x + * endpoint configurations are fixed, and are pretty much always enabled, + * there's not a lot to manage here. + * + * because pxa25x can't selectively initialize bulk (or interrupt) endpoints, + * (resetting endpoint halt and toggle), SET_INTERFACE is unusable except + * for a single interface (with only the default altsetting) and for gadget + * drivers that don't halt endpoints (not reset by set_interface).  that also + * means that if you use ISO, you must violate the USB spec rule that all + * iso endpoints must be in non-default altsettings. + */ +static int pxa25x_ep_enable(struct usb_ep *_ep, +		const struct usb_endpoint_descriptor *desc) +{ +	struct pxa25x_ep *ep; +	struct pxa25x_udc *dev; + +	ep = container_of(_ep, struct pxa25x_ep, ep); +	if (!_ep || !desc || ep->desc || _ep->name == ep0name +			|| desc->bDescriptorType != USB_DT_ENDPOINT +			|| ep->bEndpointAddress != desc->bEndpointAddress +			|| ep->fifo_size < le16_to_cpu(desc->wMaxPacketSize)) { +		printf("%s, bad ep or descriptor\n", __func__); +		return -EINVAL; +	} + +	/* xfer types must match, except that interrupt ~= bulk */ +	if (ep->bmAttributes != desc->bmAttributes +			&& ep->bmAttributes != USB_ENDPOINT_XFER_BULK +			&& desc->bmAttributes != USB_ENDPOINT_XFER_INT) { +		printf("%s, %s type mismatch\n", __func__, _ep->name); +		return -EINVAL; +	} + +	/* hardware _could_ do smaller, but driver doesn't */ +	if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK +				&& le16_to_cpu(desc->wMaxPacketSize) +						!= BULK_FIFO_SIZE) +			|| !desc->wMaxPacketSize) { +		printf("%s, bad %s maxpacket\n", __func__, _ep->name); +		return -ERANGE; +	} + +	dev = ep->dev; +	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { +		printf("%s, bogus device state\n", __func__); +		return -ESHUTDOWN; +	} + +	ep->desc = desc; +	ep->stopped = 0; +	ep->pio_irqs = 0; +	ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + +	/* flush fifo (mostly for OUT buffers) */ +	pxa25x_ep_fifo_flush(_ep); + +	/* ... reset halt state too, if we could ... */ + +	debug("enabled %s\n", _ep->name); +	return 0; +} + +static int pxa25x_ep_disable(struct usb_ep *_ep) +{ +	struct pxa25x_ep *ep; +	unsigned long flags; + +	ep = container_of(_ep, struct pxa25x_ep, ep); +	if (!_ep || !ep->desc) { +		printf("%s, %s not enabled\n", __func__, +			_ep ? ep->ep.name : NULL); +		return -EINVAL; +	} +	local_irq_save(flags); + +	nuke(ep, -ESHUTDOWN); + +	/* flush fifo (mostly for IN buffers) */ +	pxa25x_ep_fifo_flush(_ep); + +	ep->desc = NULL; +	ep->stopped = 1; + +	local_irq_restore(flags); +	debug("%s disabled\n", _ep->name); +	return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* + * for the pxa25x, these can just wrap kmalloc/kfree.  gadget drivers + * must still pass correctly initialized endpoints, since other controller + * drivers may care about how it's currently set up (dma issues etc). + */ + +/* + *	pxa25x_ep_alloc_request - allocate a request data structure + */ +static struct usb_request * +pxa25x_ep_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) +{ +	struct pxa25x_request *req; + +	req = kzalloc(sizeof(*req), gfp_flags); +	if (!req) +		return NULL; + +	INIT_LIST_HEAD(&req->queue); +	return &req->req; +} + + +/* + *	pxa25x_ep_free_request - deallocate a request data structure + */ +static void +pxa25x_ep_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ +	struct pxa25x_request	*req; + +	req = container_of(_req, struct pxa25x_request, req); +	WARN_ON(!list_empty(&req->queue)); +	kfree(req); +} + +/*-------------------------------------------------------------------------*/ + +/* + *	done - retire a request; caller blocked irqs + */ +static void done(struct pxa25x_ep *ep, struct pxa25x_request *req, int status) +{ +	unsigned stopped = ep->stopped; + +	list_del_init(&req->queue); + +	if (likely(req->req.status == -EINPROGRESS)) +		req->req.status = status; +	else +		status = req->req.status; + +	if (status && status != -ESHUTDOWN) +		debug("complete %s req %p stat %d len %u/%u\n", +			ep->ep.name, &req->req, status, +			req->req.actual, req->req.length); + +	/* don't modify queue heads during completion callback */ +	ep->stopped = 1; +	req->req.complete(&ep->ep, &req->req); +	ep->stopped = stopped; +} + + +static inline void ep0_idle(struct pxa25x_udc *dev) +{ +	dev->ep0state = EP0_IDLE; +} + +static int +write_packet(u32 *uddr, struct pxa25x_request *req, unsigned max) +{ +	u8 *buf; +	unsigned length, count; + +	debug("%s(): uddr %p\n", __func__, uddr); + +	buf = req->req.buf + req->req.actual; +	prefetch(buf); + +	/* how big will this packet be? */ +	length = min(req->req.length - req->req.actual, max); +	req->req.actual += length; + +	count = length; +	while (likely(count--)) +		writeb(*buf++, uddr); + +	return length; +} + +/* + * write to an IN endpoint fifo, as many packets as possible. + * irqs will use this to write the rest later. + * caller guarantees at least one packet buffer is ready (or a zlp). + */ +static int +write_fifo(struct pxa25x_ep *ep, struct pxa25x_request *req) +{ +	unsigned max; + +	max = le16_to_cpu(ep->desc->wMaxPacketSize); +	do { +		unsigned count; +		int is_last, is_short; + +		count = write_packet(ep->reg_uddr, req, max); + +		/* last packet is usually short (or a zlp) */ +		if (unlikely(count != max)) +			is_last = is_short = 1; +		else { +			if (likely(req->req.length != req->req.actual) +					|| req->req.zero) +				is_last = 0; +			else +				is_last = 1; +			/* interrupt/iso maxpacket may not fill the fifo */ +			is_short = unlikely(max < ep->fifo_size); +		} + +		debug_cond(NOISY, "wrote %s %d bytes%s%s %d left %p\n", +			ep->ep.name, count, +			is_last ? "/L" : "", is_short ? "/S" : "", +			req->req.length - req->req.actual, req); + +		/* +		 * let loose that packet. maybe try writing another one, +		 * double buffering might work.  TSP, TPC, and TFS +		 * bit values are the same for all normal IN endpoints. +		 */ +		writel(UDCCS_BI_TPC, ep->reg_udccs); +		if (is_short) +			writel(UDCCS_BI_TSP, ep->reg_udccs); + +		/* requests complete when all IN data is in the FIFO */ +		if (is_last) { +			done(ep, req, 0); +			if (list_empty(&ep->queue)) +				pio_irq_disable(ep->bEndpointAddress); +			return 1; +		} + +		/* +		 * TODO experiment: how robust can fifo mode tweaking be? +		 * double buffering is off in the default fifo mode, which +		 * prevents TFS from being set here. +		 */ + +	} while (readl(ep->reg_udccs) & UDCCS_BI_TFS); +	return 0; +} + +/* + * caller asserts req->pending (ep0 irq status nyet cleared); starts + * ep0 data stage.  these chips want very simple state transitions. + */ +static inline +void ep0start(struct pxa25x_udc *dev, u32 flags, const char *tag) +{ +	writel(flags|UDCCS0_SA|UDCCS0_OPR, &dev->regs->udccs[0]); +	writel(USIR0_IR0, &dev->regs->usir0); +	dev->req_pending = 0; +	debug_cond(NOISY, "%s() %s, udccs0: %02x/%02x usir: %X.%X\n", +		__func__, tag, readl(&dev->regs->udccs[0]), flags, +		readl(&dev->regs->usir1), readl(&dev->regs->usir0)); +} + +static int +write_ep0_fifo(struct pxa25x_ep *ep, struct pxa25x_request *req) +{ +	unsigned count; +	int is_short; + +	count = write_packet(&ep->dev->regs->uddr0, req, EP0_FIFO_SIZE); +	ep->dev->stats.write.bytes += count; + +	/* last packet "must be" short (or a zlp) */ +	is_short = (count != EP0_FIFO_SIZE); + +	debug_cond(NOISY, "ep0in %d bytes %d left %p\n", count, +		req->req.length - req->req.actual, req); + +	if (unlikely(is_short)) { +		if (ep->dev->req_pending) +			ep0start(ep->dev, UDCCS0_IPR, "short IN"); +		else +			writel(UDCCS0_IPR, &ep->dev->regs->udccs[0]); + +		count = req->req.length; +		done(ep, req, 0); +		ep0_idle(ep->dev); + +		/* +		 * This seems to get rid of lost status irqs in some cases: +		 * host responds quickly, or next request involves config +		 * change automagic, or should have been hidden, or ... +		 * +		 * FIXME get rid of all udelays possible... +		 */ +		if (count >= EP0_FIFO_SIZE) { +			count = 100; +			do { +				if ((readl(&ep->dev->regs->udccs[0]) & +				     UDCCS0_OPR) != 0) { +					/* clear OPR, generate ack */ +					writel(UDCCS0_OPR, +						&ep->dev->regs->udccs[0]); +					break; +				} +				count--; +				udelay(1); +			} while (count); +		} +	} else if (ep->dev->req_pending) +		ep0start(ep->dev, 0, "IN"); + +	return is_short; +} + + +/* + * read_fifo -  unload packet(s) from the fifo we use for usb OUT + * transfers and put them into the request.  caller should have made + * sure there's at least one packet ready. + * + * returns true if the request completed because of short packet or the + * request buffer having filled (and maybe overran till end-of-packet). + */ +static int +read_fifo(struct pxa25x_ep *ep, struct pxa25x_request *req) +{ +	u32 udccs; +	u8 *buf; +	unsigned bufferspace, count, is_short; + +	for (;;) { +		/* +		 * make sure there's a packet in the FIFO. +		 * UDCCS_{BO,IO}_RPC are all the same bit value. +		 * UDCCS_{BO,IO}_RNE are all the same bit value. +		 */ +		udccs = readl(ep->reg_udccs); +		if (unlikely((udccs & UDCCS_BO_RPC) == 0)) +			break; +		buf = req->req.buf + req->req.actual; +		prefetchw(buf); +		bufferspace = req->req.length - req->req.actual; + +		/* read all bytes from this packet */ +		if (likely(udccs & UDCCS_BO_RNE)) { +			count = 1 + (0x0ff & readl(ep->reg_ubcr)); +			req->req.actual += min(count, bufferspace); +		} else /* zlp */ +			count = 0; +		is_short = (count < ep->ep.maxpacket); +		debug_cond(NOISY, "read %s %02x, %d bytes%s req %p %d/%d\n", +			ep->ep.name, udccs, count, +			is_short ? "/S" : "", +			req, req->req.actual, req->req.length); +		while (likely(count-- != 0)) { +			u8 byte = readb(ep->reg_uddr); + +			if (unlikely(bufferspace == 0)) { +				/* +				 * this happens when the driver's buffer +				 * is smaller than what the host sent. +				 * discard the extra data. +				 */ +				if (req->req.status != -EOVERFLOW) +					printf("%s overflow %d\n", +						ep->ep.name, count); +				req->req.status = -EOVERFLOW; +			} else { +				*buf++ = byte; +				bufferspace--; +			} +		} +		writel(UDCCS_BO_RPC, ep->reg_udccs); +		/* RPC/RSP/RNE could now reflect the other packet buffer */ + +		/* iso is one request per packet */ +		if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) { +			if (udccs & UDCCS_IO_ROF) +				req->req.status = -EHOSTUNREACH; +			/* more like "is_done" */ +			is_short = 1; +		} + +		/* completion */ +		if (is_short || req->req.actual == req->req.length) { +			done(ep, req, 0); +			if (list_empty(&ep->queue)) +				pio_irq_disable(ep->bEndpointAddress); +			return 1; +		} + +		/* finished that packet.  the next one may be waiting... */ +	} +	return 0; +} + +/* + * special ep0 version of the above.  no UBCR0 or double buffering; status + * handshaking is magic.  most device protocols don't need control-OUT. + * CDC vendor commands (and RNDIS), mass storage CB/CBI, and some other + * protocols do use them. + */ +static int +read_ep0_fifo(struct pxa25x_ep *ep, struct pxa25x_request *req) +{ +	u8 *buf, byte; +	unsigned bufferspace; + +	buf = req->req.buf + req->req.actual; +	bufferspace = req->req.length - req->req.actual; + +	while (readl(&ep->dev->regs->udccs[0]) & UDCCS0_RNE) { +		byte = (u8)readb(&ep->dev->regs->uddr0); + +		if (unlikely(bufferspace == 0)) { +			/* +			 * this happens when the driver's buffer +			 * is smaller than what the host sent. +			 * discard the extra data. +			 */ +			if (req->req.status != -EOVERFLOW) +				printf("%s overflow\n", ep->ep.name); +			req->req.status = -EOVERFLOW; +		} else { +			*buf++ = byte; +			req->req.actual++; +			bufferspace--; +		} +	} + +	writel(UDCCS0_OPR | UDCCS0_IPR, &ep->dev->regs->udccs[0]); + +	/* completion */ +	if (req->req.actual >= req->req.length) +		return 1; + +	/* finished that packet.  the next one may be waiting... */ +	return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int +pxa25x_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ +	struct pxa25x_request *req; +	struct pxa25x_ep *ep; +	struct pxa25x_udc *dev; +	unsigned long flags; + +	req = container_of(_req, struct pxa25x_request, req); +	if (unlikely(!_req || !_req->complete || !_req->buf +			|| !list_empty(&req->queue))) { +		printf("%s, bad params\n", __func__); +		return -EINVAL; +	} + +	ep = container_of(_ep, struct pxa25x_ep, ep); +	if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { +		printf("%s, bad ep\n", __func__); +		return -EINVAL; +	} + +	dev = ep->dev; +	if (unlikely(!dev->driver +			|| dev->gadget.speed == USB_SPEED_UNKNOWN)) { +		printf("%s, bogus device state\n", __func__); +		return -ESHUTDOWN; +	} + +	/* +	 * iso is always one packet per request, that's the only way +	 * we can report per-packet status.  that also helps with dma. +	 */ +	if (unlikely(ep->bmAttributes == USB_ENDPOINT_XFER_ISOC +			&& req->req.length > +			le16_to_cpu(ep->desc->wMaxPacketSize))) +		return -EMSGSIZE; + +	debug_cond(NOISY, "%s queue req %p, len %d buf %p\n", +		_ep->name, _req, _req->length, _req->buf); + +	local_irq_save(flags); + +	_req->status = -EINPROGRESS; +	_req->actual = 0; + +	/* kickstart this i/o queue? */ +	if (list_empty(&ep->queue) && !ep->stopped) { +		if (ep->desc == NULL/* ep0 */) { +			unsigned length = _req->length; + +			switch (dev->ep0state) { +			case EP0_IN_DATA_PHASE: +				dev->stats.write.ops++; +				if (write_ep0_fifo(ep, req)) +					req = NULL; +				break; + +			case EP0_OUT_DATA_PHASE: +				dev->stats.read.ops++; +				/* messy ... */ +				if (dev->req_config) { +					debug("ep0 config ack%s\n", +						dev->has_cfr ?  "" : " raced"); +					if (dev->has_cfr) +						writel(UDCCFR_AREN|UDCCFR_ACM +							|UDCCFR_MB1, +							&ep->dev->regs->udccfr); +					done(ep, req, 0); +					dev->ep0state = EP0_END_XFER; +					local_irq_restore(flags); +					return 0; +				} +				if (dev->req_pending) +					ep0start(dev, UDCCS0_IPR, "OUT"); +				if (length == 0 || +						((readl( +						&ep->dev->regs->udccs[0]) +						& UDCCS0_RNE) != 0 +						&& read_ep0_fifo(ep, req))) { +					ep0_idle(dev); +					done(ep, req, 0); +					req = NULL; +				} +				break; + +			default: +				printf("ep0 i/o, odd state %d\n", +					dev->ep0state); +				local_irq_restore(flags); +				return -EL2HLT; +			} +		/* can the FIFO can satisfy the request immediately? */ +		} else if ((ep->bEndpointAddress & USB_DIR_IN) != 0) { +			if ((readl(ep->reg_udccs) & UDCCS_BI_TFS) != 0 +					&& write_fifo(ep, req)) +				req = NULL; +		} else if ((readl(ep->reg_udccs) & UDCCS_BO_RFS) != 0 +				&& read_fifo(ep, req)) { +			req = NULL; +		} + +		if (likely(req && ep->desc)) +			pio_irq_enable(ep->bEndpointAddress); +	} + +	/* pio or dma irq handler advances the queue. */ +	if (likely(req != NULL)) +		list_add_tail(&req->queue, &ep->queue); +	local_irq_restore(flags); + +	return 0; +} + + +/* + *	nuke - dequeue ALL requests + */ +static void nuke(struct pxa25x_ep *ep, int status) +{ +	struct pxa25x_request *req; + +	/* called with irqs blocked */ +	while (!list_empty(&ep->queue)) { +		req = list_entry(ep->queue.next, +				struct pxa25x_request, +				queue); +		done(ep, req, status); +	} +	if (ep->desc) +		pio_irq_disable(ep->bEndpointAddress); +} + + +/* dequeue JUST ONE request */ +static int pxa25x_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ +	struct pxa25x_ep *ep; +	struct pxa25x_request *req; +	unsigned long flags; + +	ep = container_of(_ep, struct pxa25x_ep, ep); +	if (!_ep || ep->ep.name == ep0name) +		return -EINVAL; + +	local_irq_save(flags); + +	/* make sure it's actually queued on this endpoint */ +	list_for_each_entry(req, &ep->queue, queue) { +		if (&req->req == _req) +			break; +	} +	if (&req->req != _req) { +		local_irq_restore(flags); +		return -EINVAL; +	} + +	done(ep, req, -ECONNRESET); + +	local_irq_restore(flags); +	return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int pxa25x_ep_set_halt(struct usb_ep *_ep, int value) +{ +	struct pxa25x_ep *ep; +	unsigned long flags; + +	ep = container_of(_ep, struct pxa25x_ep, ep); +	if (unlikely(!_ep +			|| (!ep->desc && ep->ep.name != ep0name)) +			|| ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) { +		printf("%s, bad ep\n", __func__); +		return -EINVAL; +	} +	if (value == 0) { +		/* +		 * this path (reset toggle+halt) is needed to implement +		 * SET_INTERFACE on normal hardware.  but it can't be +		 * done from software on the PXA UDC, and the hardware +		 * forgets to do it as part of SET_INTERFACE automagic. +		 */ +		printf("only host can clear %s halt\n", _ep->name); +		return -EROFS; +	} + +	local_irq_save(flags); + +	if ((ep->bEndpointAddress & USB_DIR_IN) != 0 +			&& ((readl(ep->reg_udccs) & UDCCS_BI_TFS) == 0 +			   || !list_empty(&ep->queue))) { +		local_irq_restore(flags); +		return -EAGAIN; +	} + +	/* FST bit is the same for control, bulk in, bulk out, interrupt in */ +	writel(UDCCS_BI_FST|UDCCS_BI_FTF, ep->reg_udccs); + +	/* ep0 needs special care */ +	if (!ep->desc) { +		start_watchdog(ep->dev); +		ep->dev->req_pending = 0; +		ep->dev->ep0state = EP0_STALL; + +	/* and bulk/intr endpoints like dropping stalls too */ +	} else { +		unsigned i; +		for (i = 0; i < 1000; i += 20) { +			if (readl(ep->reg_udccs) & UDCCS_BI_SST) +				break; +			udelay(20); +		} +	} +	local_irq_restore(flags); + +	debug("%s halt\n", _ep->name); +	return 0; +} + +static int pxa25x_ep_fifo_status(struct usb_ep *_ep) +{ +	struct pxa25x_ep        *ep; + +	ep = container_of(_ep, struct pxa25x_ep, ep); +	if (!_ep) { +		printf("%s, bad ep\n", __func__); +		return -ENODEV; +	} +	/* pxa can't report unclaimed bytes from IN fifos */ +	if ((ep->bEndpointAddress & USB_DIR_IN) != 0) +		return -EOPNOTSUPP; +	if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN +			|| (readl(ep->reg_udccs) & UDCCS_BO_RFS) == 0) +		return 0; +	else +		return (readl(ep->reg_ubcr) & 0xfff) + 1; +} + +static void pxa25x_ep_fifo_flush(struct usb_ep *_ep) +{ +	struct pxa25x_ep        *ep; + +	ep = container_of(_ep, struct pxa25x_ep, ep); +	if (!_ep || ep->ep.name == ep0name || !list_empty(&ep->queue)) { +		printf("%s, bad ep\n", __func__); +		return; +	} + +	/* toggle and halt bits stay unchanged */ + +	/* for OUT, just read and discard the FIFO contents. */ +	if ((ep->bEndpointAddress & USB_DIR_IN) == 0) { +		while (((readl(ep->reg_udccs)) & UDCCS_BO_RNE) != 0) +			(void)readb(ep->reg_uddr); +		return; +	} + +	/* most IN status is the same, but ISO can't stall */ +	writel(UDCCS_BI_TPC|UDCCS_BI_FTF|UDCCS_BI_TUR +		| (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC +			? 0 : UDCCS_BI_SST), ep->reg_udccs); +} + + +static struct usb_ep_ops pxa25x_ep_ops = { +	.enable		= pxa25x_ep_enable, +	.disable	= pxa25x_ep_disable, + +	.alloc_request	= pxa25x_ep_alloc_request, +	.free_request	= pxa25x_ep_free_request, + +	.queue		= pxa25x_ep_queue, +	.dequeue	= pxa25x_ep_dequeue, + +	.set_halt	= pxa25x_ep_set_halt, +	.fifo_status	= pxa25x_ep_fifo_status, +	.fifo_flush	= pxa25x_ep_fifo_flush, +}; + + +/* --------------------------------------------------------------------------- + *	device-scoped parts of the api to the usb controller hardware + * --------------------------------------------------------------------------- + */ + +static int pxa25x_udc_get_frame(struct usb_gadget *_gadget) +{ +	return ((readl(&the_controller->regs->ufnrh) & 0x07) << 8) | +		(readl(&the_controller->regs->ufnrl) & 0xff); +} + +static int pxa25x_udc_wakeup(struct usb_gadget *_gadget) +{ +	/* host may not have enabled remote wakeup */ +	if ((readl(&the_controller->regs->udccs[0]) & UDCCS0_DRWF) == 0) +		return -EHOSTUNREACH; +	udc_set_mask_UDCCR(UDCCR_RSM); +	return 0; +} + +static void stop_activity(struct pxa25x_udc *, struct usb_gadget_driver *); +static void udc_enable(struct pxa25x_udc *); +static void udc_disable(struct pxa25x_udc *); + +/* + * We disable the UDC -- and its 48 MHz clock -- whenever it's not + * in active use. + */ +static int pullup(struct pxa25x_udc *udc) +{ +	if (udc->pullup) +		pullup_on(); +	else +		pullup_off(); + + +	int is_active = udc->pullup; +	if (is_active) { +		if (!udc->active) { +			udc->active = 1; +			udc_enable(udc); +		} +	} else { +		if (udc->active) { +			if (udc->gadget.speed != USB_SPEED_UNKNOWN) +				stop_activity(udc, udc->driver); +			udc_disable(udc); +			udc->active = 0; +		} + +	} +	return 0; +} + +/* VBUS reporting logically comes from a transceiver */ +static int pxa25x_udc_vbus_session(struct usb_gadget *_gadget, int is_active) +{ +	struct pxa25x_udc *udc; + +	udc = container_of(_gadget, struct pxa25x_udc, gadget); +	printf("vbus %s\n", is_active ? "supplied" : "inactive"); +	pullup(udc); +	return 0; +} + +/* drivers may have software control over D+ pullup */ +static int pxa25x_udc_pullup(struct usb_gadget *_gadget, int is_active) +{ +	struct pxa25x_udc	*udc; + +	udc = container_of(_gadget, struct pxa25x_udc, gadget); + +	/* not all boards support pullup control */ +	if (!udc->mach->udc_command) +		return -EOPNOTSUPP; + +	udc->pullup = (is_active != 0); +	pullup(udc); +	return 0; +} + +/* + * boards may consume current from VBUS, up to 100-500mA based on config. + * the 500uA suspend ceiling means that exclusively vbus-powered PXA designs + * violate USB specs. + */ +static int pxa25x_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA) +{ +	return -EOPNOTSUPP; +} + +static const struct usb_gadget_ops pxa25x_udc_ops = { +	.get_frame	= pxa25x_udc_get_frame, +	.wakeup		= pxa25x_udc_wakeup, +	.vbus_session	= pxa25x_udc_vbus_session, +	.pullup		= pxa25x_udc_pullup, +	.vbus_draw	= pxa25x_udc_vbus_draw, +}; + +/*-------------------------------------------------------------------------*/ + +/* + *	udc_disable - disable USB device controller + */ +static void udc_disable(struct pxa25x_udc *dev) +{ +	/* block all irqs */ +	udc_set_mask_UDCCR(UDCCR_SRM|UDCCR_REM); +	writel(0xff, &dev->regs->uicr0); +	writel(0xff, &dev->regs->uicr1); +	writel(UFNRH_SIM, &dev->regs->ufnrh); + +	/* if hardware supports it, disconnect from usb */ +	pullup_off(); + +	udc_clear_mask_UDCCR(UDCCR_UDE); + +	ep0_idle(dev); +	dev->gadget.speed = USB_SPEED_UNKNOWN; +} + +/* + *	udc_reinit - initialize software state + */ +static void udc_reinit(struct pxa25x_udc *dev) +{ +	u32 i; + +	/* device/ep0 records init */ +	INIT_LIST_HEAD(&dev->gadget.ep_list); +	INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); +	dev->ep0state = EP0_IDLE; + +	/* basic endpoint records init */ +	for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { +		struct pxa25x_ep *ep = &dev->ep[i]; + +		if (i != 0) +			list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list); + +		ep->desc = NULL; +		ep->stopped = 0; +		INIT_LIST_HEAD(&ep->queue); +		ep->pio_irqs = 0; +	} + +	/* the rest was statically initialized, and is read-only */ +} + +/* + * until it's enabled, this UDC should be completely invisible + * to any USB host. + */ +static void udc_enable(struct pxa25x_udc *dev) +{ +	debug("udc: enabling udc\n"); + +	udc_clear_mask_UDCCR(UDCCR_UDE); + +	/* +	 * Try to clear these bits before we enable the udc. +	 * Do not touch reset ack bit, we would take care of it in +	 * interrupt handle routine +	 */ +	udc_ack_int_UDCCR(UDCCR_SUSIR|UDCCR_RESIR); + +	ep0_idle(dev); +	dev->gadget.speed = USB_SPEED_UNKNOWN; +	dev->stats.irqs = 0; + +	/* +	 * sequence taken from chapter 12.5.10, PXA250 AppProcDevManual: +	 * - enable UDC +	 * - if RESET is already in progress, ack interrupt +	 * - unmask reset interrupt +	 */ +	udc_set_mask_UDCCR(UDCCR_UDE); +	if (!(readl(&dev->regs->udccr) & UDCCR_UDA)) +		udc_ack_int_UDCCR(UDCCR_RSTIR); + +	if (dev->has_cfr /* UDC_RES2 is defined */) { +		/* +		 * pxa255 (a0+) can avoid a set_config race that could +		 * prevent gadget drivers from configuring correctly +		 */ +		writel(UDCCFR_ACM | UDCCFR_MB1, &dev->regs->udccfr); +	} + +	/* enable suspend/resume and reset irqs */ +	udc_clear_mask_UDCCR(UDCCR_SRM | UDCCR_REM); + +	/* enable ep0 irqs */ +	clrbits_le32(&dev->regs->uicr0, UICR0_IM0); + +	/* if hardware supports it, pullup D+ and wait for reset */ +	pullup_on(); +} + +static inline void clear_ep_state(struct pxa25x_udc *dev) +{ +	unsigned i; + +	/* +	 * hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint +	 * fifos, and pending transactions mustn't be continued in any case. +	 */ +	for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++) +		nuke(&dev->ep[i], -ECONNABORTED); +} + +static void handle_ep0(struct pxa25x_udc *dev) +{ +	u32 udccs0 = readl(&dev->regs->udccs[0]); +	struct pxa25x_ep *ep = &dev->ep[0]; +	struct pxa25x_request *req; +	union { +		struct usb_ctrlrequest	r; +		u8			raw[8]; +		u32			word[2]; +	} u; + +	if (list_empty(&ep->queue)) +		req = NULL; +	else +		req = list_entry(ep->queue.next, struct pxa25x_request, queue); + +	/* clear stall status */ +	if (udccs0 & UDCCS0_SST) { +		nuke(ep, -EPIPE); +		writel(UDCCS0_SST, &dev->regs->udccs[0]); +		stop_watchdog(dev); +		ep0_idle(dev); +	} + +	/* previous request unfinished?  non-error iff back-to-back ... */ +	if ((udccs0 & UDCCS0_SA) != 0 && dev->ep0state != EP0_IDLE) { +		nuke(ep, 0); +		stop_watchdog(dev); +		ep0_idle(dev); +	} + +	switch (dev->ep0state) { +	case EP0_IDLE: +		/* late-breaking status? */ +		udccs0 = readl(&dev->regs->udccs[0]); + +		/* start control request? */ +		if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE)) +				== (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE))) { +			int i; + +			nuke(ep, -EPROTO); + +			/* read SETUP packet */ +			for (i = 0; i < 8; i++) { +				if (unlikely(!(readl(&dev->regs->udccs[0]) & +						UDCCS0_RNE))) { +bad_setup: +					debug("SETUP %d!\n", i); +					goto stall; +				} +				u.raw[i] = (u8)readb(&dev->regs->uddr0); +			} +			if (unlikely((readl(&dev->regs->udccs[0]) & +					UDCCS0_RNE) != 0)) +				goto bad_setup; + +got_setup: +			debug("SETUP %02x.%02x v%04x i%04x l%04x\n", +				u.r.bRequestType, u.r.bRequest, +				le16_to_cpu(u.r.wValue), +				le16_to_cpu(u.r.wIndex), +				le16_to_cpu(u.r.wLength)); + +			/* cope with automagic for some standard requests. */ +			dev->req_std = (u.r.bRequestType & USB_TYPE_MASK) +						== USB_TYPE_STANDARD; +			dev->req_config = 0; +			dev->req_pending = 1; +			switch (u.r.bRequest) { +			/* hardware restricts gadget drivers here! */ +			case USB_REQ_SET_CONFIGURATION: +				debug("GOT SET_CONFIGURATION\n"); +				if (u.r.bRequestType == USB_RECIP_DEVICE) { +					/* +					 * reflect hardware's automagic +					 * up to the gadget driver. +					 */ +config_change: +					dev->req_config = 1; +					clear_ep_state(dev); +					/* +					 * if !has_cfr, there's no synch +					 * else use AREN (later) not SA|OPR +					 * USIR0_IR0 acts edge sensitive +					 */ +				} +				break; +			/* ... and here, even more ... */ +			case USB_REQ_SET_INTERFACE: +				if (u.r.bRequestType == USB_RECIP_INTERFACE) { +					/* +					 * udc hardware is broken by design: +					 *  - altsetting may only be zero; +					 *  - hw resets all interfaces' eps; +					 *  - ep reset doesn't include halt(?). +					 */ +					printf("broken set_interface (%d/%d)\n", +						le16_to_cpu(u.r.wIndex), +						le16_to_cpu(u.r.wValue)); +					goto config_change; +				} +				break; +			/* hardware was supposed to hide this */ +			case USB_REQ_SET_ADDRESS: +				debug("GOT SET ADDRESS\n"); +				if (u.r.bRequestType == USB_RECIP_DEVICE) { +					ep0start(dev, 0, "address"); +					return; +				} +				break; +			} + +			if (u.r.bRequestType & USB_DIR_IN) +				dev->ep0state = EP0_IN_DATA_PHASE; +			else +				dev->ep0state = EP0_OUT_DATA_PHASE; + +			i = dev->driver->setup(&dev->gadget, &u.r); +			if (i < 0) { +				/* hardware automagic preventing STALL... */ +				if (dev->req_config) { +					/* +					 * hardware sometimes neglects to tell +					 * tell us about config change events, +					 * so later ones may fail... +					 */ +					printf("config change %02x fail %d?\n", +						u.r.bRequest, i); +					return; +					/* +					 * TODO experiment:  if has_cfr, +					 * hardware didn't ACK; maybe we +					 * could actually STALL! +					 */ +				} +				if (0) { +stall: +					/* uninitialized when goto stall */ +					i = 0; +				} +				debug("protocol STALL, " +					"%02x err %d\n", +					readl(&dev->regs->udccs[0]), i); + +				/* +				 * the watchdog timer helps deal with cases +				 * where udc seems to clear FST wrongly, and +				 * then NAKs instead of STALLing. +				 */ +				ep0start(dev, UDCCS0_FST|UDCCS0_FTF, "stall"); +				start_watchdog(dev); +				dev->ep0state = EP0_STALL; + +			/* deferred i/o == no response yet */ +			} else if (dev->req_pending) { +				if (likely(dev->ep0state == EP0_IN_DATA_PHASE +						|| dev->req_std || u.r.wLength)) +					ep0start(dev, 0, "defer"); +				else +					ep0start(dev, UDCCS0_IPR, "defer/IPR"); +			} + +			/* expect at least one data or status stage irq */ +			return; + +		} else if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA)) +				== (UDCCS0_OPR|UDCCS0_SA))) { +			unsigned i; + +			/* +			 * pxa210/250 erratum 131 for B0/B1 says RNE lies. +			 * still observed on a pxa255 a0. +			 */ +			debug("e131\n"); +			nuke(ep, -EPROTO); + +			/* read SETUP data, but don't trust it too much */ +			for (i = 0; i < 8; i++) +				u.raw[i] = (u8)readb(&dev->regs->uddr0); +			if ((u.r.bRequestType & USB_RECIP_MASK) +					> USB_RECIP_OTHER) +				goto stall; +			if (u.word[0] == 0 && u.word[1] == 0) +				goto stall; +			goto got_setup; +		} else { +			/* +			 * some random early IRQ: +			 * - we acked FST +			 * - IPR cleared +			 * - OPR got set, without SA (likely status stage) +			 */ +			debug("random IRQ %X %X\n", udccs0, +				readl(&dev->regs->udccs[0])); +			writel(udccs0 & (UDCCS0_SA|UDCCS0_OPR), +				&dev->regs->udccs[0]); +		} +		break; +	case EP0_IN_DATA_PHASE:			/* GET_DESCRIPTOR etc */ +		if (udccs0 & UDCCS0_OPR) { +			debug("ep0in premature status\n"); +			if (req) +				done(ep, req, 0); +			ep0_idle(dev); +		} else /* irq was IPR clearing */ { +			if (req) { +				debug("next ep0 in packet\n"); +				/* this IN packet might finish the request */ +				(void) write_ep0_fifo(ep, req); +			} /* else IN token before response was written */ +		} +		break; +	case EP0_OUT_DATA_PHASE:		/* SET_DESCRIPTOR etc */ +		if (udccs0 & UDCCS0_OPR) { +			if (req) { +				/* this OUT packet might finish the request */ +				if (read_ep0_fifo(ep, req)) +					done(ep, req, 0); +				/* else more OUT packets expected */ +			} /* else OUT token before read was issued */ +		} else /* irq was IPR clearing */ { +			debug("ep0out premature status\n"); +			if (req) +				done(ep, req, 0); +			ep0_idle(dev); +		} +		break; +	case EP0_END_XFER: +		if (req) +			done(ep, req, 0); +		/* +		 * ack control-IN status (maybe in-zlp was skipped) +		 * also appears after some config change events. +		 */ +		if (udccs0 & UDCCS0_OPR) +			writel(UDCCS0_OPR, &dev->regs->udccs[0]); +		ep0_idle(dev); +		break; +	case EP0_STALL: +		writel(UDCCS0_FST, &dev->regs->udccs[0]); +		break; +	} + +	writel(USIR0_IR0, &dev->regs->usir0); +} + +static void handle_ep(struct pxa25x_ep *ep) +{ +	struct pxa25x_request	*req; +	int			is_in = ep->bEndpointAddress & USB_DIR_IN; +	int			completed; +	u32			udccs, tmp; + +	do { +		completed = 0; +		if (likely(!list_empty(&ep->queue))) +			req = list_entry(ep->queue.next, +					struct pxa25x_request, queue); +		else +			req = NULL; + +		/* TODO check FST handling */ + +		udccs = readl(ep->reg_udccs); +		if (unlikely(is_in)) {	/* irq from TPC, SST, or (ISO) TUR */ +			tmp = UDCCS_BI_TUR; +			if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK)) +				tmp |= UDCCS_BI_SST; +			tmp &= udccs; +			if (likely(tmp)) +				writel(tmp, ep->reg_udccs); +			if (req && likely((udccs & UDCCS_BI_TFS) != 0)) +				completed = write_fifo(ep, req); + +		} else {	/* irq from RPC (or for ISO, ROF) */ +			if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK)) +				tmp = UDCCS_BO_SST | UDCCS_BO_DME; +			else +				tmp = UDCCS_IO_ROF | UDCCS_IO_DME; +			tmp &= udccs; +			if (likely(tmp)) +				writel(tmp, ep->reg_udccs); + +			/* fifos can hold packets, ready for reading... */ +			if (likely(req)) +				completed = read_fifo(ep, req); +			else +				pio_irq_disable(ep->bEndpointAddress); +		} +		ep->pio_irqs++; +	} while (completed); +} + +/* + *	pxa25x_udc_irq - interrupt handler + * + * avoid delays in ep0 processing. the control handshaking isn't always + * under software control (pxa250c0 and the pxa255 are better), and delays + * could cause usb protocol errors. + */ +static struct pxa25x_udc memory; +static int +pxa25x_udc_irq(void) +{ +	struct pxa25x_udc *dev = &memory; +	int handled; + +	test_watchdog(dev); + +	dev->stats.irqs++; +	do { +		u32 udccr = readl(&dev->regs->udccr); + +		handled = 0; + +		/* SUSpend Interrupt Request */ +		if (unlikely(udccr & UDCCR_SUSIR)) { +			udc_ack_int_UDCCR(UDCCR_SUSIR); +			handled = 1; +			debug("USB suspend\n"); + +			if (dev->gadget.speed != USB_SPEED_UNKNOWN +					&& dev->driver +					&& dev->driver->suspend) +				dev->driver->suspend(&dev->gadget); +			ep0_idle(dev); +		} + +		/* RESume Interrupt Request */ +		if (unlikely(udccr & UDCCR_RESIR)) { +			udc_ack_int_UDCCR(UDCCR_RESIR); +			handled = 1; +			debug("USB resume\n"); + +			if (dev->gadget.speed != USB_SPEED_UNKNOWN +					&& dev->driver +					&& dev->driver->resume) +				dev->driver->resume(&dev->gadget); +		} + +		/* ReSeT Interrupt Request - USB reset */ +		if (unlikely(udccr & UDCCR_RSTIR)) { +			udc_ack_int_UDCCR(UDCCR_RSTIR); +			handled = 1; + +			if ((readl(&dev->regs->udccr) & UDCCR_UDA) == 0) { +				debug("USB reset start\n"); + +				/* +				 * reset driver and endpoints, +				 * in case that's not yet done +				 */ +				stop_activity(dev, dev->driver); + +			} else { +				debug("USB reset end\n"); +				dev->gadget.speed = USB_SPEED_FULL; +				memset(&dev->stats, 0, sizeof dev->stats); +				/* driver and endpoints are still reset */ +			} + +		} else { +			u32 uicr0 = readl(&dev->regs->uicr0); +			u32 uicr1 = readl(&dev->regs->uicr1); +			u32 usir0 = readl(&dev->regs->usir0); +			u32 usir1 = readl(&dev->regs->usir1); + +			usir0 = usir0 & ~uicr0; +			usir1 = usir1 & ~uicr1; +			int i; + +			if (unlikely(!usir0 && !usir1)) +				continue; + +			debug_cond(NOISY, "irq %02x.%02x\n", usir1, usir0); + +			/* control traffic */ +			if (usir0 & USIR0_IR0) { +				dev->ep[0].pio_irqs++; +				handle_ep0(dev); +				handled = 1; +			} + +			/* endpoint data transfers */ +			for (i = 0; i < 8; i++) { +				u32	tmp = 1 << i; + +				if (i && (usir0 & tmp)) { +					handle_ep(&dev->ep[i]); +					setbits_le32(&dev->regs->usir0, tmp); +					handled = 1; +				} +#ifndef	CONFIG_USB_PXA25X_SMALL +				if (usir1 & tmp) { +					handle_ep(&dev->ep[i+8]); +					setbits_le32(&dev->regs->usir1, tmp); +					handled = 1; +				} +#endif +			} +		} + +		/* we could also ask for 1 msec SOF (SIR) interrupts */ + +	} while (handled); +	return IRQ_HANDLED; +} + +/*-------------------------------------------------------------------------*/ + +/* + * this uses load-time allocation and initialization (instead of + * doing it at run-time) to save code, eliminate fault paths, and + * be more obviously correct. + */ +static struct pxa25x_udc memory = { +	.regs = UDC_REGS, + +	.gadget = { +		.ops		= &pxa25x_udc_ops, +		.ep0		= &memory.ep[0].ep, +		.name		= driver_name, +	}, + +	/* control endpoint */ +	.ep[0] = { +		.ep = { +			.name		= ep0name, +			.ops		= &pxa25x_ep_ops, +			.maxpacket	= EP0_FIFO_SIZE, +		}, +		.dev		= &memory, +		.reg_udccs	= &UDC_REGS->udccs[0], +		.reg_uddr	= &UDC_REGS->uddr0, +	}, + +	/* first group of endpoints */ +	.ep[1] = { +		.ep = { +			.name		= "ep1in-bulk", +			.ops		= &pxa25x_ep_ops, +			.maxpacket	= BULK_FIFO_SIZE, +		}, +		.dev		= &memory, +		.fifo_size	= BULK_FIFO_SIZE, +		.bEndpointAddress = USB_DIR_IN | 1, +		.bmAttributes	= USB_ENDPOINT_XFER_BULK, +		.reg_udccs	= &UDC_REGS->udccs[1], +		.reg_uddr	= &UDC_REGS->uddr1, +	}, +	.ep[2] = { +		.ep = { +			.name		= "ep2out-bulk", +			.ops		= &pxa25x_ep_ops, +			.maxpacket	= BULK_FIFO_SIZE, +		}, +		.dev		= &memory, +		.fifo_size	= BULK_FIFO_SIZE, +		.bEndpointAddress = 2, +		.bmAttributes	= USB_ENDPOINT_XFER_BULK, +		.reg_udccs	= &UDC_REGS->udccs[2], +		.reg_ubcr	= &UDC_REGS->ubcr2, +		.reg_uddr	= &UDC_REGS->uddr2, +	}, +#ifndef CONFIG_USB_PXA25X_SMALL +	.ep[3] = { +		.ep = { +			.name		= "ep3in-iso", +			.ops		= &pxa25x_ep_ops, +			.maxpacket	= ISO_FIFO_SIZE, +		}, +		.dev		= &memory, +		.fifo_size	= ISO_FIFO_SIZE, +		.bEndpointAddress = USB_DIR_IN | 3, +		.bmAttributes	= USB_ENDPOINT_XFER_ISOC, +		.reg_udccs	= &UDC_REGS->udccs[3], +		.reg_uddr	= &UDC_REGS->uddr3, +	}, +	.ep[4] = { +		.ep = { +			.name		= "ep4out-iso", +			.ops		= &pxa25x_ep_ops, +			.maxpacket	= ISO_FIFO_SIZE, +		}, +		.dev		= &memory, +		.fifo_size	= ISO_FIFO_SIZE, +		.bEndpointAddress = 4, +		.bmAttributes	= USB_ENDPOINT_XFER_ISOC, +		.reg_udccs	= &UDC_REGS->udccs[4], +		.reg_ubcr	= &UDC_REGS->ubcr4, +		.reg_uddr	= &UDC_REGS->uddr4, +	}, +	.ep[5] = { +		.ep = { +			.name		= "ep5in-int", +			.ops		= &pxa25x_ep_ops, +			.maxpacket	= INT_FIFO_SIZE, +		}, +		.dev		= &memory, +		.fifo_size	= INT_FIFO_SIZE, +		.bEndpointAddress = USB_DIR_IN | 5, +		.bmAttributes	= USB_ENDPOINT_XFER_INT, +		.reg_udccs	= &UDC_REGS->udccs[5], +		.reg_uddr	= &UDC_REGS->uddr5, +	}, + +	/* second group of endpoints */ +	.ep[6] = { +		.ep = { +			.name		= "ep6in-bulk", +			.ops		= &pxa25x_ep_ops, +			.maxpacket	= BULK_FIFO_SIZE, +		}, +		.dev		= &memory, +		.fifo_size	= BULK_FIFO_SIZE, +		.bEndpointAddress = USB_DIR_IN | 6, +		.bmAttributes	= USB_ENDPOINT_XFER_BULK, +		.reg_udccs	= &UDC_REGS->udccs[6], +		.reg_uddr	= &UDC_REGS->uddr6, +	}, +	.ep[7] = { +		.ep = { +			.name		= "ep7out-bulk", +			.ops		= &pxa25x_ep_ops, +			.maxpacket	= BULK_FIFO_SIZE, +		}, +		.dev		= &memory, +		.fifo_size	= BULK_FIFO_SIZE, +		.bEndpointAddress = 7, +		.bmAttributes	= USB_ENDPOINT_XFER_BULK, +		.reg_udccs	= &UDC_REGS->udccs[7], +		.reg_ubcr	= &UDC_REGS->ubcr7, +		.reg_uddr	= &UDC_REGS->uddr7, +	}, +	.ep[8] = { +		.ep = { +			.name		= "ep8in-iso", +			.ops		= &pxa25x_ep_ops, +			.maxpacket	= ISO_FIFO_SIZE, +		}, +		.dev		= &memory, +		.fifo_size	= ISO_FIFO_SIZE, +		.bEndpointAddress = USB_DIR_IN | 8, +		.bmAttributes	= USB_ENDPOINT_XFER_ISOC, +		.reg_udccs	= &UDC_REGS->udccs[8], +		.reg_uddr	= &UDC_REGS->uddr8, +	}, +	.ep[9] = { +		.ep = { +			.name		= "ep9out-iso", +			.ops		= &pxa25x_ep_ops, +			.maxpacket	= ISO_FIFO_SIZE, +		}, +		.dev		= &memory, +		.fifo_size	= ISO_FIFO_SIZE, +		.bEndpointAddress = 9, +		.bmAttributes	= USB_ENDPOINT_XFER_ISOC, +		.reg_udccs	= &UDC_REGS->udccs[9], +		.reg_ubcr	= &UDC_REGS->ubcr9, +		.reg_uddr	= &UDC_REGS->uddr9, +	}, +	.ep[10] = { +		.ep = { +			.name		= "ep10in-int", +			.ops		= &pxa25x_ep_ops, +			.maxpacket	= INT_FIFO_SIZE, +		}, +		.dev		= &memory, +		.fifo_size	= INT_FIFO_SIZE, +		.bEndpointAddress = USB_DIR_IN | 10, +		.bmAttributes	= USB_ENDPOINT_XFER_INT, +		.reg_udccs	= &UDC_REGS->udccs[10], +		.reg_uddr	= &UDC_REGS->uddr10, +	}, + +	/* third group of endpoints */ +	.ep[11] = { +		.ep = { +			.name		= "ep11in-bulk", +			.ops		= &pxa25x_ep_ops, +			.maxpacket	= BULK_FIFO_SIZE, +		}, +		.dev		= &memory, +		.fifo_size	= BULK_FIFO_SIZE, +		.bEndpointAddress = USB_DIR_IN | 11, +		.bmAttributes	= USB_ENDPOINT_XFER_BULK, +		.reg_udccs	= &UDC_REGS->udccs[11], +		.reg_uddr	= &UDC_REGS->uddr11, +	}, +	.ep[12] = { +		.ep = { +			.name		= "ep12out-bulk", +			.ops		= &pxa25x_ep_ops, +			.maxpacket	= BULK_FIFO_SIZE, +		}, +		.dev		= &memory, +		.fifo_size	= BULK_FIFO_SIZE, +		.bEndpointAddress = 12, +		.bmAttributes	= USB_ENDPOINT_XFER_BULK, +		.reg_udccs	= &UDC_REGS->udccs[12], +		.reg_ubcr	= &UDC_REGS->ubcr12, +		.reg_uddr	= &UDC_REGS->uddr12, +	}, +	.ep[13] = { +		.ep = { +			.name		= "ep13in-iso", +			.ops		= &pxa25x_ep_ops, +			.maxpacket	= ISO_FIFO_SIZE, +		}, +		.dev		= &memory, +		.fifo_size	= ISO_FIFO_SIZE, +		.bEndpointAddress = USB_DIR_IN | 13, +		.bmAttributes	= USB_ENDPOINT_XFER_ISOC, +		.reg_udccs	= &UDC_REGS->udccs[13], +		.reg_uddr	= &UDC_REGS->uddr13, +	}, +	.ep[14] = { +		.ep = { +			.name		= "ep14out-iso", +			.ops		= &pxa25x_ep_ops, +			.maxpacket	= ISO_FIFO_SIZE, +		}, +		.dev		= &memory, +		.fifo_size	= ISO_FIFO_SIZE, +		.bEndpointAddress = 14, +		.bmAttributes	= USB_ENDPOINT_XFER_ISOC, +		.reg_udccs	= &UDC_REGS->udccs[14], +		.reg_ubcr	= &UDC_REGS->ubcr14, +		.reg_uddr	= &UDC_REGS->uddr14, +	}, +	.ep[15] = { +		.ep = { +			.name		= "ep15in-int", +			.ops		= &pxa25x_ep_ops, +			.maxpacket	= INT_FIFO_SIZE, +		}, +		.dev		= &memory, +		.fifo_size	= INT_FIFO_SIZE, +		.bEndpointAddress = USB_DIR_IN | 15, +		.bmAttributes	= USB_ENDPOINT_XFER_INT, +		.reg_udccs	= &UDC_REGS->udccs[15], +		.reg_uddr	= &UDC_REGS->uddr15, +	}, +#endif /* !CONFIG_USB_PXA25X_SMALL */ +}; + +static void udc_command(int cmd) +{ +	switch (cmd) { +	case PXA2XX_UDC_CMD_CONNECT: +		setbits_le32(GPDR(CONFIG_USB_DEV_PULLUP_GPIO), +			GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO)); + +		/* enable pullup */ +		writel(GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO), +			GPCR(CONFIG_USB_DEV_PULLUP_GPIO)); + +		debug("Connected to USB\n"); +		break; + +	case PXA2XX_UDC_CMD_DISCONNECT: +		/* disable pullup resistor */ +		writel(GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO), +			GPSR(CONFIG_USB_DEV_PULLUP_GPIO)); + +		/* setup pin as input, line will float */ +		clrbits_le32(GPDR(CONFIG_USB_DEV_PULLUP_GPIO), +			GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO)); + +		debug("Disconnected from USB\n"); +		break; +	} +} + +static struct pxa2xx_udc_mach_info mach_info = { +	.udc_command = udc_command, +}; + +/* + * when a driver is successfully registered, it will receive + * control requests including set_configuration(), which enables + * non-control requests.  then usb traffic follows until a + * disconnect is reported.  then a host may connect again, or + * the driver might get unbound. + */ +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ +	struct pxa25x_udc *dev = &memory; +	int retval; +	uint32_t chiprev; + +	if (!driver +			|| driver->speed < USB_SPEED_FULL +			|| !driver->disconnect +			|| !driver->setup) +		return -EINVAL; +	if (!dev) +		return -ENODEV; +	if (dev->driver) +		return -EBUSY; + +	/* Enable clock for usb controller */ +	setbits_le32(CKEN, CKEN11_USB); + +	/* first hook up the driver ... */ +	dev->driver = driver; +	dev->pullup = 1; + +	/* trigger chiprev-specific logic */ +	switch ((chiprev = pxa_get_cpu_revision())) { +	case PXA255_A0: +		dev->has_cfr = 1; +		break; +	case PXA250_A0: +	case PXA250_A1: +		/* A0/A1 "not released"; ep 13, 15 unusable */ +		/* fall through */ +	case PXA250_B2: case PXA210_B2: +	case PXA250_B1: case PXA210_B1: +	case PXA250_B0: case PXA210_B0: +		/* OUT-DMA is broken ... */ +		/* fall through */ +	case PXA250_C0: case PXA210_C0: +		break; +	default: +		printf("%s: unrecognized processor: %08x\n", +			DRIVER_NAME, chiprev); +		return -ENODEV; +	} + +	the_controller = dev; + +	/* prepare watchdog timer */ +	dev->watchdog.running = 0; +	dev->watchdog.period = 5000 * CONFIG_SYS_HZ / 1000000; /* 5 ms */ +	dev->watchdog.function = udc_watchdog; + +	udc_disable(dev); +	udc_reinit(dev); + +	dev->mach = &mach_info; + +	dev->gadget.name = "pxa2xx_udc"; +	retval = driver->bind(&dev->gadget); +	if (retval) { +		printf("bind to driver %s --> error %d\n", +				DRIVER_NAME, retval); +		dev->driver = NULL; +		return retval; +	} + +	/* +	 * ... then enable host detection and ep0; and we're ready +	 * for set_configuration as well as eventual disconnect. +	 */ +	printf("registered gadget driver '%s'\n", DRIVER_NAME); + +	pullup(dev); +	dump_state(dev); +	return 0; +} + +static void +stop_activity(struct pxa25x_udc *dev, struct usb_gadget_driver *driver) +{ +	int i; + +	/* don't disconnect drivers more than once */ +	if (dev->gadget.speed == USB_SPEED_UNKNOWN) +		driver = NULL; +	dev->gadget.speed = USB_SPEED_UNKNOWN; + +	/* prevent new request submissions, kill any outstanding requests  */ +	for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { +		struct pxa25x_ep *ep = &dev->ep[i]; + +		ep->stopped = 1; +		nuke(ep, -ESHUTDOWN); +	} +	stop_watchdog(dev); + +	/* report disconnect; the driver is already quiesced */ +	if (driver) +		driver->disconnect(&dev->gadget); + +	/* re-init driver-visible data structures */ +	udc_reinit(dev); +} + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ +	struct pxa25x_udc	*dev = the_controller; + +	if (!dev) +		return -ENODEV; +	if (!driver || driver != dev->driver || !driver->unbind) +		return -EINVAL; + +	local_irq_disable(); +	dev->pullup = 0; +	pullup(dev); +	stop_activity(dev, driver); +	local_irq_enable(); + +	driver->unbind(&dev->gadget); +	dev->driver = NULL; + +	printf("unregistered gadget driver '%s'\n", DRIVER_NAME); +	dump_state(dev); + +	the_controller = NULL; + +	clrbits_le32(CKEN, CKEN11_USB); + +	return 0; +} + +extern void udc_disconnect(void) +{ +	setbits_le32(CKEN, CKEN11_USB); +	udc_clear_mask_UDCCR(UDCCR_UDE); +	udc_command(PXA2XX_UDC_CMD_DISCONNECT); +	clrbits_le32(CKEN, CKEN11_USB); +} + +/*-------------------------------------------------------------------------*/ + +extern int +usb_gadget_handle_interrupts(void) +{ +	return pxa25x_udc_irq(); +} diff --git a/drivers/usb/gadget/pxa25x_udc.h b/drivers/usb/gadget/pxa25x_udc.h new file mode 100644 index 000000000..de28a697f --- /dev/null +++ b/drivers/usb/gadget/pxa25x_udc.h @@ -0,0 +1,162 @@ +/* + * Intel PXA25x on-chip full speed USB device controller + * + * Copyright (C) 2003 Robert Schwebel <r.schwebel@pengutronix.de>, Pengutronix + * Copyright (C) 2003 David Brownell + * Copyright (C) 2012 Lukasz Dalek <luk0104@gmail.com> + * + * 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 + */ + +#ifndef __LINUX_USB_GADGET_PXA25X_H +#define __LINUX_USB_GADGET_PXA25X_H + +#include <linux/types.h> +#include <asm/arch/regs-usb.h> + +/* + * Prefetching support - only ARMv5. + */ + +#ifdef ARCH_HAS_PREFETCH +static inline void prefetch(const void *ptr) +{ +	__asm__ __volatile__( +		"pld\t%a0" +		: +		: "p" (ptr) +		: "cc"); +} + +#define prefetchw(ptr)	prefetch(ptr) +#endif /* ARCH_HAS_PREFETCH */ + +/*-------------------------------------------------------------------------*/ + +#define UDC_REGS	((struct pxa25x_udc_regs *)PXA25X_UDC_BASE) + +/*-------------------------------------------------------------------------*/ + +struct pxa2xx_udc_mach_info { +	int  (*udc_is_connected)(void);		/* do we see host? */ +	void (*udc_command)(int cmd); +#define	PXA2XX_UDC_CMD_CONNECT		0	/* let host see us */ +#define	PXA2XX_UDC_CMD_DISCONNECT	1	/* so host won't see us */ +}; + +struct pxa25x_udc; + +struct pxa25x_ep { +	struct usb_ep				ep; +	struct pxa25x_udc			*dev; + +	const struct usb_endpoint_descriptor	*desc; +	struct list_head			queue; +	unsigned long				pio_irqs; + +	unsigned short				fifo_size; +	u8					bEndpointAddress; +	u8					bmAttributes; + +	unsigned				stopped:1; + +	/* UDCCS = UDC Control/Status for this EP +	 * UBCR = UDC Byte Count Remaining (contents of OUT fifo) +	 * UDDR = UDC Endpoint Data Register (the fifo) +	 * DRCM = DMA Request Channel Map +	 */ +	u32					*reg_udccs; +	u32					*reg_ubcr; +	u32					*reg_uddr; +}; + +struct pxa25x_request { +	struct usb_request			req; +	struct list_head			queue; +}; + +enum ep0_state { +	EP0_IDLE, +	EP0_IN_DATA_PHASE, +	EP0_OUT_DATA_PHASE, +	EP0_END_XFER, +	EP0_STALL, +}; + +#define EP0_FIFO_SIZE	16U +#define BULK_FIFO_SIZE	64U +#define ISO_FIFO_SIZE	256U +#define INT_FIFO_SIZE	8U + +struct udc_stats { +	struct ep0stats { +		unsigned long		ops; +		unsigned long		bytes; +	} read, write; +	unsigned long			irqs; +}; + +#ifdef CONFIG_USB_PXA25X_SMALL +/* when memory's tight, SMALL config saves code+data.  */ +#define	PXA_UDC_NUM_ENDPOINTS	3 +#endif + +#ifndef	PXA_UDC_NUM_ENDPOINTS +#define	PXA_UDC_NUM_ENDPOINTS	16 +#endif + +struct pxa25x_watchdog { +	unsigned				running:1; +	ulong					period; +	ulong					base; +	struct pxa25x_udc			*udc; + +	void (*function)(struct pxa25x_udc *udc); +}; + +struct pxa25x_udc { +	struct usb_gadget			gadget; +	struct usb_gadget_driver		*driver; +	struct pxa25x_udc_regs			*regs; + +	enum ep0_state				ep0state; +	struct udc_stats			stats; +	unsigned				got_irq:1, +						pullup:1, +						has_cfr:1, +						req_pending:1, +						req_std:1, +						req_config:1, +						active:1; + +	struct clk				*clk; +	struct pxa2xx_udc_mach_info		*mach; +	u64					dma_mask; +	struct pxa25x_ep			ep[PXA_UDC_NUM_ENDPOINTS]; + +	struct pxa25x_watchdog			watchdog; +}; + +/*-------------------------------------------------------------------------*/ + +static struct pxa25x_udc *the_controller; + +/*-------------------------------------------------------------------------*/ + +#ifndef DEBUG +# define NOISY 0 +#endif + +#endif /* __LINUX_USB_GADGET_PXA25X_H */ |