LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
* [PATCH] PXA27x UDC driver.
@ 2007-06-28 10:36 Rodolfo Giometti
2007-06-28 11:15 ` Li Yang-r58472
` (3 more replies)
0 siblings, 4 replies; 22+ messages in thread
From: Rodolfo Giometti @ 2007-06-28 10:36 UTC (permalink / raw)
To: linux-arm-kernel; +Cc: linux-kernel, Andrew Morton
[-- Attachment #1: Type: text/plain, Size: 583 bytes --]
Hello,
attached you can find new version of my patch for PXA27x UDC driver
support against kernel 2.6.22-rc3 (but it applies also ro rc6).
I'd like to know what I have to do in order to prepare this patch for
kernel inclusion. It's time PXA27x has its UDC driver into linux! :)
Thanks for your suggestions,
Rodolfo
--
GNU/Linux Solutions e-mail: giometti@enneenne.com
Linux Device Driver giometti@gnudd.com
Embedded Systems giometti@linux.it
UNIX programming phone: +39 349 2432127
[-- Attachment #2: pxa27x-udc-support.3 --]
[-- Type: text/plain, Size: 85568 bytes --]
diff --git a/arch/arm/mach-pxa/generic.c b/arch/arm/mach-pxa/generic.c
index 64b08b7..7f390fd 100644
--- a/arch/arm/mach-pxa/generic.c
+++ b/arch/arm/mach-pxa/generic.c
@@ -282,7 +282,11 @@ static struct resource pxa2xx_udc_resources[] = {
static u64 udc_dma_mask = ~(u32)0;
static struct platform_device udc_device = {
+#ifdef CONFIG_PXA27x
+ .name = "pxa27x-udc",
+#else
.name = "pxa2xx-udc",
+#endif
.id = -1,
.resource = pxa2xx_udc_resources,
.num_resources = ARRAY_SIZE(pxa2xx_udc_resources),
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index f771a7c..eccd2c7 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -132,6 +132,24 @@ config USB_PXA2XX
default USB_GADGET
select USB_GADGET_SELECTED
+config USB_GADGET_PXA27X
+ boolean "PXA 27x"
+ depends on ARCH_PXA && PXA27x
+ help
+ Intel's PXA 27x series XScale processors include an integrated
+ full speed USB 1.1 device controller.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "pxa2xx_udc" and force all
+ gadget drivers to also be dynamically linked.
+
+
+config USB_PXA27X
+ tristate
+ depends on USB_GADGET_PXA27X
+ default USB_GADGET
+ select USB_GADGET_SELECTED
+
# if there's only one gadget driver, using only two bulk endpoints,
# don't waste memory for the other endpoints
config USB_PXA2XX_SMALL
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 5db1939..640a5a8 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o
obj-$(CONFIG_USB_NET2280) += net2280.o
obj-$(CONFIG_USB_PXA2XX) += pxa2xx_udc.o
+obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o
obj-$(CONFIG_USB_GOKU) += goku_udc.o
obj-$(CONFIG_USB_OMAP) += omap_udc.o
obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index f28af06..e7d72ff 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -230,7 +230,8 @@ find_ep (struct usb_gadget *gadget, const char *name)
*/
struct usb_ep * __devinit usb_ep_autoconfig (
struct usb_gadget *gadget,
- struct usb_endpoint_descriptor *desc
+ struct usb_endpoint_descriptor *desc,
+ int config, int interface, int alt
)
{
struct usb_ep *ep;
@@ -238,6 +239,11 @@ struct usb_ep * __devinit usb_ep_autoconfig (
type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+ /* If have ep_alloc() function use it! */
+ if (gadget->ops->ep_alloc)
+ return gadget->ops->ep_alloc(gadget, desc,
+ config, interface, alt);
+
/* First, apply chip-specific "best usage" knowledge.
* This might make a good usb_gadget_ops hook ...
*/
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index 325bf7c..d4f5870 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -257,10 +257,6 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
#define DEV_CONFIG_CDC
#endif
-#ifdef CONFIG_USB_GADGET_PXA27X
-#define DEV_CONFIG_CDC
-#endif
-
#ifdef CONFIG_USB_GADGET_S3C2410
#define DEV_CONFIG_CDC
#endif
@@ -292,6 +288,10 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
#define DEV_CONFIG_SUBSET
#endif
+#ifdef CONFIG_USB_GADGET_PXA27X
+#define DEV_CONFIG_SUBSET
+#endif
+
#ifdef CONFIG_USB_GADGET_SH
#define DEV_CONFIG_SUBSET
#endif
@@ -1034,13 +1034,12 @@ static int alloc_requests (struct eth_dev *dev, unsigned n, gfp_t gfp_flags);
static int
set_ether_config (struct eth_dev *dev, gfp_t gfp_flags)
{
- int result = 0;
- struct usb_gadget *gadget = dev->gadget;
+ int result = 0;
#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
/* status endpoint used for RNDIS and (optionally) CDC */
if (!subset_active(dev) && dev->status_ep) {
- dev->status = ep_desc (gadget, &hs_status_desc,
+ dev->status = ep_desc (dev->gadget, &hs_status_desc,
&fs_status_desc);
dev->status_ep->driver_data = dev;
@@ -1053,10 +1052,10 @@ set_ether_config (struct eth_dev *dev, gfp_t gfp_flags)
}
#endif
- dev->in = ep_desc(gadget, &hs_source_desc, &fs_source_desc);
+ dev->in = ep_desc(dev->gadget, &hs_source_desc, &fs_source_desc);
dev->in_ep->driver_data = dev;
- dev->out = ep_desc(gadget, &hs_sink_desc, &fs_sink_desc);
+ dev->out = ep_desc(dev->gadget, &hs_sink_desc, &fs_sink_desc);
dev->out_ep->driver_data = dev;
/* With CDC, the host isn't allowed to use these two data
@@ -2311,6 +2310,9 @@ eth_bind (struct usb_gadget *gadget)
* non-CDC to be compatible with ARM Linux-2.4 "usb-eth".
*/
cdc = 0;
+ } else if (gadget_is_pxa27x(gadget)) {
+ /* hardware can't write zlps */
+ zlp = 0;
}
gcnum = usb_gadget_controller_number (gadget);
@@ -2377,7 +2379,22 @@ eth_bind (struct usb_gadget *gadget)
/* all we really need is bulk IN/OUT */
usb_ep_autoconfig_reset (gadget);
- in_ep = usb_ep_autoconfig (gadget, &fs_source_desc);
+#ifdef CONFIG_USB_ETH_RNDIS
+ in_ep = usb_ep_autoconfig (gadget, &fs_source_desc,
+ DEV_RNDIS_CONFIG_VALUE,
+ (int)rndis_data_intf.bInterfaceNumber,
+ (int)rndis_data_intf.bAlternateSetting);
+#elif defined(DEV_CONFIG_CDC)
+ in_ep = usb_ep_autoconfig (gadget, &fs_source_desc,
+ DEV_CONFIG_VALUE,
+ (int)data_intf.bInterfaceNumber,
+ (int)data_intf.bAlternateSetting);
+#elif defined(DEV_CONFIG_SUBSET)
+ in_ep = usb_ep_autoconfig (gadget, &fs_source_desc,
+ DEV_CONFIG_VALUE,
+ (int)subset_data_intf.bInterfaceNumber,
+ (int)subset_data_intf.bAlternateSetting);
+#endif /* CONFIG_USB_ETH_RNDIS */
if (!in_ep) {
autoconf_fail:
dev_err (&gadget->dev,
@@ -2386,8 +2403,24 @@ autoconf_fail:
return -ENODEV;
}
in_ep->driver_data = in_ep; /* claim */
+
+#ifdef CONFIG_USB_ETH_RNDIS
+ out_ep = usb_ep_autoconfig (gadget, &fs_sink_desc,
+ DEV_RNDIS_CONFIG_VALUE,
+ (int)rndis_data_intf.bInterfaceNumber,
+ (int)rndis_data_intf.bAlternateSetting);
+#elif defined(DEV_CONFIG_CDC)
+ out_ep = usb_ep_autoconfig (gadget, &fs_sink_desc,
+ DEV_CONFIG_VALUE,
+ (int)data_intf.bInterfaceNumber,
+ (int)data_intf.bAlternateSetting);
+#elif defined(DEV_CONFIG_SUBSET)
+ out_ep = usb_ep_autoconfig (gadget, &fs_sink_desc,
+ DEV_CONFIG_VALUE,
+ (int)subset_data_intf.bInterfaceNumber,
+ (int)subset_data_intf.bAlternateSetting);
+#endif /* CONFIG_USB_ETH_RNDIS */
- out_ep = usb_ep_autoconfig (gadget, &fs_sink_desc);
if (!out_ep)
goto autoconf_fail;
out_ep->driver_data = out_ep; /* claim */
@@ -2397,7 +2430,18 @@ autoconf_fail:
* Since some hosts expect one, try to allocate one anyway.
*/
if (cdc || rndis) {
- status_ep = usb_ep_autoconfig (gadget, &fs_status_desc);
+#ifdef CONFIG_USB_ETH_RNDIS
+ status_ep = usb_ep_autoconfig (gadget, &fs_status_desc,
+ DEV_RNDIS_CONFIG_VALUE,
+ (int)rndis_control_intf.bInterfaceNumber,
+ (int)rndis_control_intf.bAlternateSetting);
+#elif defined(DEV_CONFIG_CDC)
+ status_ep = usb_ep_autoconfig (gadget, &fs_status_desc,
+ DEV_CONFIG_VALUE,
+ (int)control_intf.bInterfaceNumber,
+ (int)control_intf.bAlternateSetting);
+#endif /* CONFIG_USB_ETH_RNDIS */
+
if (status_ep) {
status_ep->driver_data = status_ep; /* claim */
} else if (rndis) {
@@ -2405,13 +2449,14 @@ autoconf_fail:
"can't run RNDIS on %s\n",
gadget->name);
return -ENODEV;
+ }
#ifdef DEV_CONFIG_CDC
- /* pxa25x only does CDC subset; often used with RNDIS */
- } else if (cdc) {
+ /* pxa25x only does CDC subset; often used with RNDIS */
+ else if (cdc) {
control_intf.bNumEndpoints = 0;
/* FIXME remove endpoint from descriptor list */
-#endif
}
+#endif
}
#endif
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
index c6b6479..6af5fdd 100644
--- a/drivers/usb/gadget/file_storage.c
+++ b/drivers/usb/gadget/file_storage.c
@@ -3920,20 +3920,20 @@ static int __init fsg_bind(struct usb_gadget *gadget)
/* Find all the endpoints we will use */
usb_ep_autoconfig_reset(gadget);
- ep = usb_ep_autoconfig(gadget, &fs_bulk_in_desc);
+ ep = usb_ep_autoconfig(gadget, &fs_bulk_in_desc, 0, 0, 0);
if (!ep)
goto autoconf_fail;
ep->driver_data = fsg; // claim the endpoint
fsg->bulk_in = ep;
- ep = usb_ep_autoconfig(gadget, &fs_bulk_out_desc);
+ ep = usb_ep_autoconfig(gadget, &fs_bulk_out_desc, 0, 0, 0);
if (!ep)
goto autoconf_fail;
ep->driver_data = fsg; // claim the endpoint
fsg->bulk_out = ep;
if (transport_is_cbi()) {
- ep = usb_ep_autoconfig(gadget, &fs_intr_in_desc);
+ ep = usb_ep_autoconfig(gadget, &fs_intr_in_desc, 0, 0, 0);
if (!ep)
goto autoconf_fail;
ep->driver_data = fsg; // claim the endpoint
diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c
index d08a8d0..d51feb2 100644
--- a/drivers/usb/gadget/gmidi.c
+++ b/drivers/usb/gadget/gmidi.c
@@ -1204,7 +1204,7 @@ static int __devinit gmidi_bind(struct usb_gadget *gadget)
* but there may also be important quirks to address.
*/
usb_ep_autoconfig_reset(gadget);
- in_ep = usb_ep_autoconfig(gadget, &bulk_in_desc);
+ in_ep = usb_ep_autoconfig(gadget, &bulk_in_desc, 0, 0, 0);
if (!in_ep) {
autoconf_fail:
printk(KERN_ERR "%s: can't autoconfigure on %s\n",
@@ -1214,7 +1214,7 @@ autoconf_fail:
EP_IN_NAME = in_ep->name;
in_ep->driver_data = in_ep; /* claim */
- out_ep = usb_ep_autoconfig(gadget, &bulk_out_desc);
+ out_ep = usb_ep_autoconfig(gadget, &bulk_out_desc, 0, 0, 0);
if (!out_ep) {
goto autoconf_fail;
}
diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c
new file mode 100644
index 0000000..affca46
--- /dev/null
+++ b/drivers/usb/gadget/pxa27x_udc.c
@@ -0,0 +1,2395 @@
+/*
+ * linux/drivers/usb/gadget/pxa27x_udc.c
+ * Intel PXA2xx 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) 2004 Intel Corporation
+ * Copyright (C) 2007 Rodolfo Giometti <giometti@linux.it>
+ *
+ * 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
+ *
+ */
+
+#undef DEBUG
+/* #define VERBOSE DBG_VERBOSE */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+
+#include <asm/byteorder.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <asm/unaligned.h>
+#include <asm/hardware.h>
+#include <asm/arch/pxa-regs.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb_gadget.h>
+
+#include <asm/arch/udc.h>
+
+/*
+ * This driver handles the USB Device Controller (UDC) in Intel's PXA 27x
+ * series processors.
+ * 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.
+ */
+
+#define DRIVER_VERSION "28-Jun-2007"
+#define DRIVER_DESC "PXA 27x USB Device Controller driver"
+
+static const char driver_name[] = "pxa27x_udc";
+
+static const char ep0name[] = "ep0";
+
+#undef USE_DMA
+#undef DISABLE_TEST_MODE
+
+#ifdef CONFIG_PROC_FS
+#define UDC_PROC_FILE
+#endif
+
+#include "pxa27x_udc.h"
+
+#ifdef CONFIG_EMBEDDED
+/* few strings, and little code to use them */
+#undef DEBUG
+#undef UDC_PROC_FILE
+#endif
+
+#ifdef USE_DMA
+static int use_dma = 1;
+module_param(use_dma, bool, 0);
+MODULE_PARM_DESC(use_dma, "true to use dma");
+
+static void dma_nodesc_handler(int dmach, void *_ep);
+static void kick_dma(struct pxa27x_ep *ep, struct pxa27x_request *req);
+
+#define DMASTR " (dma support)"
+
+#else /* !USE_DMA */
+#define DMASTR " (pio only)"
+#endif
+
+#ifdef CONFIG_USB_PXA27X_SMALL
+#define SIZE_STR " (small)"
+#else
+#define SIZE_STR ""
+#endif
+
+#ifdef DISABLE_TEST_MODE
+/* (mode == 0) == no undocumented chip tweaks
+ * (mode & 1) == double buffer bulk IN
+ * (mode & 2) == double buffer bulk OUT
+ * ... so mode = 3 (or 7, 15, etc) does it for both
+ */
+static ushort fifo_mode = 0;
+module_param(fifo_mode, ushort, 0);
+MODULE_PARM_DESC(fifo_mode, "pxa27x udc fifo mode");
+#endif
+
+#define UDCISR0_IR0 0x3
+#define UDCISR_INT_MASK (UDC_INT_FIFOERROR | UDC_INT_PACKETCMP)
+#define UDCICR_INT_MASK UDCISR_INT_MASK
+
+#define UDCCSR_MASK (UDCCSR_FST | UDCCSR_DME)
+/* ---------------------------------------------------------------------------
+ * endpoint related parts of the api to the usb controller hardware,
+ * used by gadget driver; and the inner talker-to-hardware core.
+ * ---------------------------------------------------------------------------
+ */
+
+static void pxa27x_ep_fifo_flush(struct usb_ep *ep);
+static void nuke(struct pxa27x_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->gpio_pullup)
+ udc_gpio_set(mach->gpio_pullup, 0);
+ else 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->gpio_pullup)
+ udc_gpio_set(mach->gpio_pullup, 1);
+ else if (mach->udc_command)
+ mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
+}
+
+static void pio_irq_enable(int ep_num)
+{
+ if (ep_num < 16)
+ UDCICR0 |= 3 << (ep_num * 2);
+ else {
+ ep_num -= 16;
+ UDCICR1 |= 3 << (ep_num * 2);
+ }
+}
+
+static void pio_irq_disable(int ep_num)
+{
+ ep_num &= 0xf;
+ if (ep_num < 16)
+ UDCICR0 &= ~(3 << (ep_num * 2));
+ else {
+ ep_num -= 16;
+ UDCICR1 &= ~(3 << (ep_num * 2));
+ }
+}
+
+/* The UDCCR reg contains mask and interrupt status bits,
+ * so using '|=' isn't safe as it may ack an interrupt.
+ */
+#define UDCCR_MASK_BITS (UDCCR_OEN | UDCCR_UDE)
+
+static inline void udc_set_mask_UDCCR(int mask)
+{
+ UDCCR = (UDCCR & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS);
+}
+
+static inline void udc_clear_mask_UDCCR(int mask)
+{
+ UDCCR = (UDCCR & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS);
+}
+
+static inline void udc_ack_int_UDCCR(int mask)
+{
+ /* udccr contains the bits we dont want to change */
+ __u32 udccr = UDCCR & UDCCR_MASK_BITS;
+
+ UDCCR = udccr | (mask & ~UDCCR_MASK_BITS);
+}
+
+/*
+ * endpoint enable/disable
+ *
+ * we need to verify the descriptors used to enable endpoints. since pxa27x
+ * endpoint configurations are fixed, and are pretty much always enabled,
+ * there's not a lot to manage here.
+ *
+ * because pxa27x 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 pxa27x_ep_enable(struct usb_ep *_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct pxa27x_ep *ep;
+ struct pxa27x_udc *dev;
+
+ ep = container_of(_ep, struct pxa27x_ep, ep);
+ if (!_ep || !desc || _ep->name == ep0name
+ || desc->bDescriptorType != USB_DT_ENDPOINT
+ || ep->fifo_size < le16_to_cpu(desc->wMaxPacketSize)) {
+ DMSG("%s, bad ep or descriptor\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ /* xfer types must match, except that interrupt ~= bulk */
+ if (ep->ep_type != USB_ENDPOINT_XFER_BULK
+ && desc->bmAttributes != USB_ENDPOINT_XFER_INT) {
+ DMSG("%s, %s type mismatch\n", __FUNCTION__, _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) {
+ DMSG("%s, bad %s maxpacket\n", __FUNCTION__, _ep->name);
+ return -ERANGE;
+ }
+
+ dev = ep->dev;
+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
+ DMSG("%s, bogus device state\n", __FUNCTION__);
+ return -ESHUTDOWN;
+ }
+
+ ep->desc = desc;
+ ep->dma = -1;
+ ep->stopped = 0;
+ ep->pio_irqs = ep->dma_irqs = 0;
+ ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+
+ /* flush fifo (mostly for OUT buffers) */
+ pxa27x_ep_fifo_flush(_ep);
+
+ /* ... reset halt state too, if we could ... */
+
+#ifdef USE_DMA
+ /* for (some) bulk and ISO endpoints, try to get a DMA channel and
+ * bind it to the endpoint. otherwise use PIO.
+ */
+ DMSG("%s: called attributes=%d\n", __FUNCTION__, ep->ep_type);
+ switch (ep->ep_type) {
+ case USB_ENDPOINT_XFER_ISOC:
+ if (le16_to_cpu(desc->wMaxPacketSize) % 32)
+ break;
+ /* fall through */
+ case USB_ENDPOINT_XFER_BULK:
+ if (!use_dma || !ep->reg_drcmr)
+ break;
+ ep->dma = pxa_request_dma((char *)_ep->name,
+ (le16_to_cpu(desc->wMaxPacketSize) > 64)
+ ? DMA_PRIO_MEDIUM /* some iso */
+ : DMA_PRIO_LOW,
+ dma_nodesc_handler, ep);
+ if (ep->dma >= 0) {
+ *ep->reg_drcmr = DRCMR_MAPVLD | ep->dma;
+ DMSG("%s using dma%d\n", _ep->name, ep->dma);
+ }
+ default:
+ break;
+ }
+#endif
+ DBG(DBG_VERBOSE, "enabled %s\n", _ep->name);
+ return 0;
+}
+
+static int pxa27x_ep_disable(struct usb_ep *_ep)
+{
+ struct pxa27x_ep *ep;
+
+ ep = container_of(_ep, struct pxa27x_ep, ep);
+ if (!_ep || !ep->desc) {
+ DMSG("%s, %s not enabled\n", __FUNCTION__,
+ _ep ? ep->ep.name : NULL);
+ return -EINVAL;
+ }
+ nuke(ep, -ESHUTDOWN);
+
+#ifdef USE_DMA
+ if (ep->dma >= 0) {
+ *ep->reg_drcmr = 0;
+ pxa_free_dma(ep->dma);
+ ep->dma = -1;
+ }
+#endif
+
+ /* flush fifo (mostly for IN buffers) */
+ pxa27x_ep_fifo_flush(_ep);
+
+ ep->desc = 0;
+ ep->stopped = 1;
+
+ DBG(DBG_VERBOSE, "%s disabled\n", _ep->name);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* for the pxa27x, 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).
+ */
+
+/*
+ * pxa27x_ep_alloc_request - allocate a request data structure
+ */
+static struct usb_request *pxa27x_ep_alloc_request(struct usb_ep *_ep,
+ unsigned int gfp_flags)
+{
+ struct pxa27x_request *req;
+
+ req = kmalloc(sizeof *req, gfp_flags);
+ if (!req)
+ return 0;
+
+ memset(req, 0, sizeof *req);
+ INIT_LIST_HEAD(&req->queue);
+ return &req->req;
+}
+
+/*
+ * pxa27x_ep_free_request - deallocate a request data structure
+ */
+static void pxa27x_ep_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct pxa27x_request *req;
+
+ req = container_of(_req, struct pxa27x_request, req);
+ WARN_ON(!list_empty(&req->queue));
+ kfree(req);
+}
+
+/* PXA cache needs flushing with DMA I/O (it's dma-incoherent), but there's
+ * no device-affinity and the heap works perfectly well for i/o buffers.
+ * It wastes much less memory than dma_alloc_coherent() would, and even
+ * prevents cacheline (32 bytes wide) sharing problems.
+ */
+static void *pxa27x_ep_alloc_buffer(struct usb_ep *_ep, unsigned bytes,
+ dma_addr_t * dma, unsigned int gfp_flags)
+{
+ char *retval;
+
+ retval = kmalloc(bytes, gfp_flags & ~(__GFP_DMA | __GFP_HIGHMEM));
+ if (retval)
+#ifdef USE_DMA
+ *dma = virt_to_bus(retval);
+#else
+ *dma = (dma_addr_t) ~0;
+#endif
+ return retval;
+}
+
+static void
+pxa27x_ep_free_buffer(struct usb_ep *_ep, void *buf, dma_addr_t dma,
+ unsigned bytes)
+{
+ kfree(buf);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * done - retire a request; caller blocked irqs
+ */
+static void done(struct pxa27x_ep *ep, struct pxa27x_request *req, int status)
+{
+ list_del_init(&req->queue);
+ if (likely(req->req.status == -EINPROGRESS))
+ req->req.status = status;
+ else
+ status = req->req.status;
+
+ if (status && status != -ESHUTDOWN)
+ DBG(DBG_VERBOSE, "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 */
+ req->req.complete(&ep->ep, &req->req);
+}
+
+static inline void ep0_idle(struct pxa27x_udc *dev)
+{
+ dev->ep0state = EP0_IDLE;
+ LED_EP0_OFF;
+}
+
+static int
+write_packet(volatile u32 * uddr, struct pxa27x_request *req, unsigned max)
+{
+ u32 *buf;
+ int length, count, remain;
+
+ buf = (u32 *) (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;
+
+ remain = length & 0x3;
+ count = length & ~(0x3);
+
+ while (likely(count)) {
+ *uddr = *buf++;
+ count -= 4;
+ }
+
+ if (remain) {
+ volatile u8 *reg = (u8 *) uddr;
+ char *rd = (u8 *) buf;
+
+ while (remain--) {
+ *reg = *rd++;
+ }
+ }
+
+ 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 pxa27x_ep *ep, struct pxa27x_request *req)
+{
+ unsigned max;
+
+ max = le16_to_cpu(ep->desc->wMaxPacketSize);
+ do {
+ int count;
+ int is_last, is_short;
+
+ count = write_packet(ep->reg_udcdr, 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);
+ }
+
+ DMSG("wrote %s count:%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->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.
+ */
+ *ep->reg_udccsr = UDCCSR_PC;
+ if (is_short)
+ *ep->reg_udccsr = UDCCSR_SP;
+
+ /* requests complete when all IN data is in the FIFO */
+ if (is_last) {
+ done(ep, req, 0);
+ if (list_empty(&ep->queue) || unlikely(ep->dma >= 0)) {
+ pio_irq_disable(ep->ep_num);
+#ifdef USE_DMA
+ /* unaligned data and zlps couldn't use dma */
+ if (unlikely(!list_empty(&ep->queue))) {
+ req = list_entry(ep->queue.next,
+ struct pxa27x_request,
+ queue);
+ kick_dma(ep, req);
+ return 0;
+ }
+#endif
+ }
+ 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 (*ep->reg_udccsr & UDCCSR_FS);
+ 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 pxa27x_udc *dev, u32 flags, const char *tag)
+{
+ UDCCSR0 = flags | UDCCSR0_SA | UDCCSR0_OPC;
+ UDCISR0 = UDCICR_INT(0, UDC_INT_FIFOERROR | UDC_INT_PACKETCMP);
+ dev->req_pending = 0;
+ DBG(DBG_VERY_NOISY, "%s %s, %02x/%02x\n",
+ __FUNCTION__, tag, UDCCSR0, flags);
+}
+
+static int write_ep0_fifo(struct pxa27x_ep *ep, struct pxa27x_request *req)
+{
+ unsigned count;
+ int is_short;
+
+ count = write_packet(&UDCDR0, req, EP0_FIFO_SIZE);
+ ep->dev->stats.write.bytes += count;
+
+ /* last packet "must be" short (or a zlp) */
+ is_short = (count != EP0_FIFO_SIZE);
+
+ DBG(DBG_VERY_NOISY, "ep0in %d bytes %d left %p\n", count,
+ req->req.length - req->req.actual, &req->req);
+
+ if (unlikely(is_short)) {
+ if (ep->dev->req_pending)
+ ep0start(ep->dev, UDCCSR0_IPR, "short IN");
+ else
+ UDCCSR0 = UDCCSR0_IPR;
+
+ count = req->req.length;
+ done(ep, req, 0);
+ ep0_idle(ep->dev);
+#if 0
+ /* 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 ((UDCCSR0 & UDCCSR0_OPC) != 0) {
+ /* clear OPC, generate ack */
+ UDCCSR0 = UDCCSR0_OPC;
+ break;
+ }
+ count--;
+ udelay(1);
+ } while (count);
+ }
+#endif
+ } 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 pxa27x_ep *ep, struct pxa27x_request *req)
+{
+ for (;;) {
+ u32 *buf;
+ int bufferspace, count, is_short;
+
+ /* make sure there's a packet in the FIFO. */
+ if (unlikely((*ep->reg_udccsr & UDCCSR_PC) == 0))
+ break;
+ buf = (u32 *) (req->req.buf + req->req.actual);
+ prefetchw(buf);
+ bufferspace = req->req.length - req->req.actual;
+
+ /* read all bytes from this packet */
+ if (likely(*ep->reg_udccsr & UDCCSR_BNE)) {
+ count = 0x3ff & *ep->reg_udcbcr;
+ req->req.actual += min(count, bufferspace);
+ } else /* zlp */
+ count = 0;
+
+ is_short = (count < ep->ep.maxpacket);
+ DMSG("read %s udccsr:%02x, count:%d bytes%s req %p %d/%d\n",
+ ep->ep.name, *ep->reg_udccsr, count,
+ is_short ? "/S" : "",
+ &req->req, req->req.actual, req->req.length);
+
+#if 0
+ dump_regs(ep->ep_num );
+#endif
+ count = min(count, bufferspace);
+ while (likely(count > 0)) {
+ *buf++ = *ep->reg_udcdr;
+ count -= 4;
+ }
+ DMSG("Buf:0x%p\n", req->req.buf);
+
+ *ep->reg_udccsr = UDCCSR_PC;
+ /* RPC/RSP/RNE could now reflect the other packet buffer */
+
+ /* completion */
+ if (is_short || req->req.actual == req->req.length) {
+ done(ep, req, 0);
+ if (list_empty(&ep->queue))
+ pio_irq_disable(ep->ep_num);
+ 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 pxa27x_ep *ep, struct pxa27x_request *req)
+{
+ u32 *buf, word;
+ unsigned bufferspace;
+
+ buf = (u32 *) (req->req.buf + req->req.actual);
+ bufferspace = req->req.length - req->req.actual;
+
+ while (UDCCSR0 & UDCCSR0_RNE) {
+ word = UDCDR0;
+
+ 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)
+ DMSG("%s overflow\n", ep->ep.name);
+ req->req.status = -EOVERFLOW;
+ } else {
+ *buf++ = word;
+ req->req.actual += 4;
+ bufferspace -= 4;
+ }
+ }
+
+ UDCCSR0 = UDCCSR0_OPC;
+
+ /* completion */
+ if (req->req.actual >= req->req.length)
+ return 1;
+
+ /* finished that packet. the next one may be waiting... */
+ return 0;
+}
+
+#ifdef USE_DMA
+
+#define MAX_IN_DMA ((DCMD_LENGTH + 1) - BULK_FIFO_SIZE)
+static void kick_dma(struct pxa27x_ep *ep, struct pxa27x_request *req)
+{
+ u32 dcmd = 0;
+ u32 len = req->req.length;
+ u32 buf = req->req.dma;
+ u32 fifo = io_v2p((u32) ep->reg_udcdr);
+
+ buf += req->req.actual;
+ len -= req->req.actual;
+ ep->dma_con = 0;
+
+ DMSG("%s: req:0x%p length:%d, actual:%d dma:%d\n",
+ __FUNCTION__, &req->req, req->req.length,
+ req->req.actual, ep->dma);
+
+ /* no-descriptor mode can be simple for bulk-in, iso-in, iso-out */
+ DCSR(ep->dma) = DCSR_NODESC;
+ if (buf & 0x3)
+ DALGN |= 1 << ep->dma;
+ else
+ DALGN &= ~(1 << ep->dma);
+
+ if (ep->dir_in) {
+ DSADR(ep->dma) = buf;
+ DTADR(ep->dma) = fifo;
+ if (len > MAX_IN_DMA) {
+ len = MAX_IN_DMA;
+ ep->dma_con = 1;
+ } else if (len >= ep->ep.maxpacket) {
+ if ((ep->dma_con = (len % ep->ep.maxpacket) != 0))
+ len = ep->ep.maxpacket;
+ }
+ dcmd = len | DCMD_BURST32 | DCMD_WIDTH4 | DCMD_ENDIRQEN
+ | DCMD_FLOWTRG | DCMD_INCSRCADDR;
+ } else {
+ DSADR(ep->dma) = fifo;
+ DTADR(ep->dma) = buf;
+ dcmd = len | DCMD_BURST32 | DCMD_WIDTH4 | DCMD_ENDIRQEN
+ | DCMD_FLOWSRC | DCMD_INCTRGADDR;
+ }
+ *ep->reg_udccsr = UDCCSR_DME;
+ DCMD(ep->dma) = dcmd;
+ DCSR(ep->dma) = DCSR_NODESC | DCSR_EORIRQEN
+ | ((ep->dir_in) ? DCSR_STOPIRQEN : 0);
+ *ep->reg_drcmr = ep->dma | DRCMR_MAPVLD;
+ DCSR(ep->dma) |= DCSR_RUN;
+}
+
+static void cancel_dma(struct pxa27x_ep *ep)
+{
+ struct pxa27x_request *req;
+ u32 tmp;
+
+ if (DCSR(ep->dma) == 0 || list_empty(&ep->queue))
+ return;
+
+ DMSG("hehe dma:%d,dcsr:0x%x\n", ep->dma, DCSR(ep->dma));
+ DCSR(ep->dma) = 0;
+ while ((DCSR(ep->dma) & DCSR_STOPSTATE) == 0)
+ cpu_relax();
+
+ req = list_entry(ep->queue.next, struct pxa27x_request, queue);
+ tmp = DCMD(ep->dma) & DCMD_LENGTH;
+ req->req.actual = req->req.length - tmp;
+
+ /* the last tx packet may be incomplete, so flush the fifo.
+ * FIXME correct req.actual if we can
+ */
+ *ep->reg_udccsr = UDCCSR_FEF;
+}
+
+static void dma_nodesc_handler(int dmach, void *_ep, struct pt_regs *r)
+{
+ struct pxa27x_ep *ep = _ep;
+ struct pxa27x_request *req, *req_next;
+ u32 dcsr, tmp, completed;
+
+ local_irq_disable();
+
+ req = list_entry(ep->queue.next, struct pxa27x_request, queue);
+
+ DMSG("%s, buf:0x%p\n", __FUNCTION__, req->req.buf);
+
+ ep->dma_irqs++;
+ ep->dev->stats.irqs++;
+ HEX_DISPLAY(ep->dev->stats.irqs);
+
+ completed = 0;
+
+ dcsr = DCSR(dmach);
+ DCSR(ep->dma) &= ~DCSR_RUN;
+
+ if (dcsr & DCSR_BUSERR) {
+ DCSR(dmach) = DCSR_BUSERR;
+ printk(KERN_ERR " Buss Error\n");
+ req->req.status = -EIO;
+ completed = 1;
+ } else if (dcsr & DCSR_ENDINTR) {
+ DCSR(dmach) = DCSR_ENDINTR;
+ if (ep->dir_in) {
+ tmp = req->req.length - req->req.actual;
+ /* Last packet is a short one */
+ if (tmp < ep->ep.maxpacket) {
+ int count = 0;
+
+ *ep->reg_udccsr = UDCCSR_SP |
+ (*ep->reg_udccsr & UDCCSR_MASK);
+ /*Wait for packet out */
+ while ((count++ < 10000) &&
+ !(*ep->reg_udccsr & UDCCSR_FS)) ;
+ if (count >= 10000)
+ DMSG("Failed to send packet\n");
+ else
+ DMSG("%s: short packet sent len:%d,"
+ "length:%d,actual:%d\n",
+ __FUNCTION__, tmp, req->req.length,
+ req->req.actual);
+ req->req.actual = req->req.length;
+ completed = 1;
+ /* There are still packets to transfer */
+ } else if (ep->dma_con) {
+ DMSG("%s: more packets,length:%d,actual:%d\n",
+ __FUNCTION__, req->req.length,
+ req->req.actual);
+ req->req.actual += ep->ep.maxpacket;
+ completed = 0;
+ } else {
+ DMSG("%s: no more packets,length:%d,"
+ "actual:%d\n", __FUNCTION__,
+ req->req.length, req->req.actual);
+ req->req.actual = req->req.length;
+ completed = 1;
+ }
+ } else {
+ req->req.actual = req->req.length;
+ completed = 1;
+ }
+ } else if (dcsr & DCSR_EORINTR) { /* Only happened in OUT DMA */
+ int remain, udccsr;
+
+ DCSR(dmach) = DCSR_EORINTR;
+ remain = DCMD(dmach) & DCMD_LENGTH;
+ req->req.actual = req->req.length - remain;
+
+ udccsr = *ep->reg_udccsr;
+ if (udccsr & UDCCSR_SP) {
+ *ep->reg_udccsr = UDCCSR_PC | (udccsr & UDCCSR_MASK);
+ completed = 1;
+ }
+ DMSG("%s: length:%d actual:%d\n",
+ __FUNCTION__, req->req.length, req->req.actual);
+ } else
+ DMSG("%s: Others dma:%d DCSR:0x%x DCMD:0x%x\n",
+ __FUNCTION__, dmach, DCSR(dmach), DCMD(dmach));
+
+ if (likely(completed)) {
+ if (req->queue.next != &ep->queue) {
+ req_next = list_entry(req->queue.next,
+ struct pxa27x_request, queue);
+ kick_dma(ep, req_next);
+ }
+ done(ep, req, 0);
+ } else {
+ kick_dma(ep, req);
+ }
+
+ local_irq_enable();
+}
+
+#endif
+/*-------------------------------------------------------------------------*/
+
+static int
+pxa27x_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
+ unsigned int gfp_flags)
+{
+ struct pxa27x_ep *ep;
+ struct pxa27x_request *req;
+ struct pxa27x_udc *dev;
+ unsigned long flags;
+
+ req = container_of(_req, struct pxa27x_request, req);
+ if (unlikely(!_req || !_req->complete || !_req->buf ||
+ !list_empty(&req->queue))) {
+ DMSG("%s, bad params\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ ep = container_of(_ep, struct pxa27x_ep, ep);
+ if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) {
+ DMSG("%s, bad ep\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ DMSG("%s, ep point %d is queue\n", __FUNCTION__, ep->ep_num);
+
+ dev = ep->dev;
+ if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) {
+ DMSG("%s, bogus device state\n", __FUNCTION__);
+ 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->ep_type == USB_ENDPOINT_XFER_ISOC
+ && req->req.length > le16_to_cpu
+ (ep->desc->wMaxPacketSize)))
+ return -EMSGSIZE;
+
+#ifdef USE_DMA
+ /* FIXME: caller may already have done the dma mapping */
+ if (ep->dma >= 0) {
+ _req->dma = dma_map_single(dev->dev, _req->buf, _req->length,
+ (ep->
+ dir_in) ? DMA_TO_DEVICE :
+ DMA_FROM_DEVICE);
+ }
+#endif
+
+ DBG(DBG_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 == 0 /* ep0 */ ) {
+ unsigned length = _req->length;
+
+ switch (dev->ep0state) {
+ case EP0_IN_DATA_PHASE:
+ dev->stats.write.ops++;
+ if (write_ep0_fifo(ep, req))
+ req = 0;
+ break;
+
+ case EP0_OUT_DATA_PHASE:
+ dev->stats.read.ops++;
+ if (dev->req_pending)
+ ep0start(dev, UDCCSR0_IPR, "OUT");
+ if (length == 0 || ((UDCCSR0 & UDCCSR0_RNE) != 0
+ && read_ep0_fifo(ep,
+ req))) {
+ ep0_idle(dev);
+ done(ep, req, 0);
+ req = 0;
+ }
+ break;
+ case EP0_NO_ACTION:
+ ep0_idle(dev);
+ req = 0;
+ break;
+ default:
+ DMSG("ep0 i/o, odd state %d\n", dev->ep0state);
+ local_irq_restore(flags);
+ return -EL2HLT;
+ }
+#ifdef USE_DMA
+ /* either start dma or prime pio pump */
+ } else if (ep->dma >= 0) {
+ kick_dma(ep, req);
+#endif
+ /* can the FIFO can satisfy the request immediately? */
+ } else if (ep->dir_in
+ && (*ep->reg_udccsr & UDCCSR_FS) != 0
+ && write_fifo(ep, req)) {
+ req = 0;
+ } else if ((*ep->reg_udccsr & UDCCSR_FS) != 0
+ && read_fifo(ep, req)) {
+ req = 0;
+ }
+ DMSG("req:%p,ep->desc:%p,ep->dma:%d\n", req, ep->desc, ep->dma);
+ if (likely(req && ep->desc) && ep->dma < 0)
+ pio_irq_enable(ep->ep_num);
+ }
+
+ /* pio or dma irq handler advances the queue. */
+ if (likely(req != 0))
+ list_add_tail(&req->queue, &ep->queue);
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+/*
+ * nuke - dequeue ALL requests
+ */
+static void nuke(struct pxa27x_ep *ep, int status)
+{
+ struct pxa27x_request *req;
+
+ /* called with irqs blocked */
+#ifdef USE_DMA
+ if (ep->dma >= 0 && !ep->stopped)
+ cancel_dma(ep);
+#endif
+ while (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next, struct pxa27x_request, queue);
+ done(ep, req, status);
+ }
+ if (ep->desc)
+ pio_irq_disable(ep->ep_num);
+}
+
+/* dequeue JUST ONE request */
+static int pxa27x_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct pxa27x_ep *ep;
+ struct pxa27x_request *req;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct pxa27x_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;
+ }
+#ifdef USE_DMA
+ if (ep->dma >= 0 && ep->queue.next == &req->queue && !ep->stopped) {
+ cancel_dma(ep);
+ done(ep, req, -ECONNRESET);
+ /* restart i/o */
+ if (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next,
+ struct pxa27x_request, queue);
+ kick_dma(ep, req);
+ }
+ } else
+#endif
+ done(ep, req, -ECONNRESET);
+
+ local_irq_restore(flags);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int pxa27x_ep_set_halt(struct usb_ep *_ep, int value)
+{
+ struct pxa27x_ep *ep;
+ unsigned long flags;
+
+ DMSG("%s is called\n", __FUNCTION__);
+ ep = container_of(_ep, struct pxa27x_ep, ep);
+ if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))
+ || ep->ep_type == USB_ENDPOINT_XFER_ISOC) {
+ DMSG("%s, bad ep\n", __FUNCTION__);
+ 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.
+ */
+ DMSG("only host can clear %s halt\n", _ep->name);
+ return -EROFS;
+ }
+
+ local_irq_save(flags);
+
+ if (ep->dir_in && ((*ep->reg_udccsr & UDCCSR_FS) == 0
+ || !list_empty(&ep->queue))) {
+ local_irq_restore(flags);
+ return -EAGAIN;
+ }
+
+ /* FST bit is the same for control, bulk in, bulk out, interrupt in */
+ *ep->reg_udccsr = UDCCSR_FST | UDCCSR_FEF;
+
+ /* ep0 needs special care */
+ if (!ep->desc) {
+ start_watchdog(ep->dev);
+ ep->dev->req_pending = 0;
+ ep->dev->ep0state = EP0_STALL;
+ LED_EP0_OFF;
+
+ /* and bulk/intr endpoints like dropping stalls too */
+ } else {
+ unsigned i;
+ for (i = 0; i < 1000; i += 20) {
+ if (*ep->reg_udccsr & UDCCSR_SST)
+ break;
+ udelay(20);
+ }
+ }
+ local_irq_restore(flags);
+
+ DBG(DBG_VERBOSE, "%s halt\n", _ep->name);
+ return 0;
+}
+
+static int pxa27x_ep_fifo_status(struct usb_ep *_ep)
+{
+ struct pxa27x_ep *ep;
+
+ ep = container_of(_ep, struct pxa27x_ep, ep);
+ if (!_ep) {
+ DMSG("%s, bad ep\n", __FUNCTION__);
+ return -ENODEV;
+ }
+ /* pxa can't report unclaimed bytes from IN fifos */
+ if (ep->dir_in)
+ return -EOPNOTSUPP;
+ if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN
+ || (*ep->reg_udccsr & UDCCSR_FS) == 0)
+ return 0;
+ else
+ return (*ep->reg_udcbcr & 0xfff) + 1;
+}
+
+static void pxa27x_ep_fifo_flush(struct usb_ep *_ep)
+{
+ struct pxa27x_ep *ep;
+
+ ep = container_of(_ep, struct pxa27x_ep, ep);
+ if (!_ep || ep->ep.name == ep0name || !list_empty(&ep->queue)) {
+ DMSG("%s, bad ep\n", __FUNCTION__);
+ return;
+ }
+
+ /* toggle and halt bits stay unchanged */
+
+ /* for OUT, just read and discard the FIFO contents. */
+ if (!ep->dir_in) {
+ while (((*ep->reg_udccsr) & UDCCSR_BNE) != 0)
+ (void)*ep->reg_udcdr;
+ return;
+ }
+
+ /* most IN status is the same, but ISO can't stall */
+ *ep->reg_udccsr = UDCCSR_PC | UDCCSR_FST | UDCCSR_TRN
+ | (ep->ep_type == USB_ENDPOINT_XFER_ISOC)
+ ? 0 : UDCCSR_SST;
+}
+
+static struct usb_ep_ops pxa27x_ep_ops = {
+ .enable = pxa27x_ep_enable,
+ .disable = pxa27x_ep_disable,
+
+ .alloc_request = pxa27x_ep_alloc_request,
+ .free_request = pxa27x_ep_free_request,
+
+ .alloc_buffer = pxa27x_ep_alloc_buffer,
+ .free_buffer = pxa27x_ep_free_buffer,
+
+ .queue = pxa27x_ep_queue,
+ .dequeue = pxa27x_ep_dequeue,
+
+ .set_halt = pxa27x_ep_set_halt,
+ .fifo_status = pxa27x_ep_fifo_status,
+ .fifo_flush = pxa27x_ep_fifo_flush,
+};
+
+/* ---------------------------------------------------------------------------
+ * device-scoped parts of the api to the usb controller hardware
+ * ---------------------------------------------------------------------------
+ */
+
+static inline void validate_fifo_size(struct pxa27x_ep *pxa_ep, u8 bmAttributes)
+{
+ switch (bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ pxa_ep->fifo_size = EP0_FIFO_SIZE;
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ pxa_ep->fifo_size = ISO_FIFO_SIZE;
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ pxa_ep->fifo_size = BULK_FIFO_SIZE;
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ pxa_ep->fifo_size = INT_FIFO_SIZE;
+ break;
+ default:
+ break;
+ }
+}
+
+#define NAME_SIZE 18
+struct usb_ep *pxa27x_ep_alloc(struct usb_gadget *gadget,
+ struct usb_endpoint_descriptor *desc, int config,
+ int interface, int alt)
+{
+ u32 tmp;
+ unsigned i;
+ char *name;
+ struct usb_ep *ep = NULL;
+ struct pxa27x_ep *pxa_ep = NULL;
+ struct pxa27x_udc *dev = the_controller;
+
+ DMSG("pxa27x_config_ep is called\n");
+ DMSG(" usb endpoint descriptor is:\n"
+ " bLength:%d\n"
+ " bDescriptorType:%x\n"
+ " bEndpointAddress:%x\n"
+ " bmAttributes:%x\n"
+ " wMaxPacketSize:%d\n",
+ desc->bLength,
+ desc->bDescriptorType, desc->bEndpointAddress,
+ desc->bmAttributes, desc->wMaxPacketSize);
+
+ for (i = 1; i < UDC_EP_NUM; i++) {
+ if (!dev->ep[i].assigned) {
+ pxa_ep = &dev->ep[i];
+ pxa_ep->assigned = 1;
+ pxa_ep->ep_num = i;
+ break;
+ }
+ }
+ if (unlikely(i == UDC_EP_NUM)) {
+ printk(KERN_ERR __FILE__ ": Failed to find a spare endpoint\n");
+ return ep;
+ }
+
+ ep = &pxa_ep->ep;
+
+ pxa_ep->dev = dev;
+ pxa_ep->desc = desc;
+ pxa_ep->pio_irqs = pxa_ep->dma_irqs = 0;
+ pxa_ep->dma = -1;
+
+ if (!(desc->bEndpointAddress & 0xF))
+ desc->bEndpointAddress |= i;
+
+ if (!(desc->wMaxPacketSize)) {
+ validate_fifo_size(pxa_ep, desc->bmAttributes);
+ desc->wMaxPacketSize = pxa_ep->fifo_size;
+ } else
+ pxa_ep->fifo_size = desc->wMaxPacketSize;
+
+ pxa_ep->dir_in = (desc->bEndpointAddress & USB_DIR_IN) ? 1 : 0;
+ pxa_ep->ep_type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+ pxa_ep->stopped = 1;
+ pxa_ep->dma_con = 0;
+ pxa_ep->config = config;
+ pxa_ep->interface = interface;
+ pxa_ep->aisn = alt;
+
+ pxa_ep->reg_udccsr = &UDCCSR0 + i;
+ pxa_ep->reg_udcbcr = &UDCBCR0 + i;
+ pxa_ep->reg_udcdr = &UDCDR0 + i;
+ pxa_ep->reg_udccr = &UDCCRA - 1 + i;
+#ifdef USE_DMA
+ pxa_ep->reg_drcmr = &DRCMR24 + i;
+#endif
+
+ DMSG("udccsr=0x%8x, udcbcr=0x%8x, udcdr=0x%8x,"
+ "udccr0=0x%8x\n",
+ (unsigned)pxa_ep->reg_udccsr,
+ (unsigned)pxa_ep->reg_udcbcr,
+ (unsigned)pxa_ep->reg_udcdr, (unsigned)pxa_ep->reg_udccr);
+
+ /* Configure UDCCR */
+ tmp = 0;
+ tmp |= (pxa_ep->config << UDCCONR_CN_S) & UDCCONR_CN;
+#if 0
+ tmp |= (pxa_ep->interface << UDCCONR_IN_S) & UDCCONR_IN;
+ tmp |= (pxa_ep->aisn << UDCCONR_AISN_S) & UDCCONR_AISN;
+#else
+ tmp |= (0 << UDCCONR_IN_S) & UDCCONR_IN;
+ tmp |= (0 << UDCCONR_AISN_S) & UDCCONR_AISN;
+#endif
+ tmp |= (desc->bEndpointAddress << UDCCONR_EN_S) & UDCCONR_EN;
+ tmp |= (pxa_ep->ep_type << UDCCONR_ET_S) & UDCCONR_ET;
+ tmp |= (pxa_ep->dir_in) ? UDCCONR_ED : 0;
+ tmp |= (min(pxa_ep->fifo_size, (unsigned)desc->wMaxPacketSize)
+ << UDCCONR_MPS_S) & UDCCONR_MPS;
+ tmp |= UDCCONR_DE | UDCCONR_EE;
+#if 0
+ tmp |= UDCCONR_EE;
+#endif
+
+ *pxa_ep->reg_udccr = tmp;
+
+#ifdef USE_DMA
+ /* Only BULK use DMA */
+ if ((pxa_ep->ep_type & USB_ENDPOINT_XFERTYPE_MASK)
+ == USB_ENDPOINT_XFER_BULK)
+ *pxa_ep->reg_udccsr = UDCCSR_DME;
+#endif
+
+ DMSG("UDCCR: 0x%p is 0x%x\n", pxa_ep->reg_udccr, *pxa_ep->reg_udccr);
+
+ /* Fill ep name */
+ name = kmalloc(NAME_SIZE, GFP_KERNEL);
+ if (!name) {
+ printk(KERN_ERR "%s: Error\n", __FUNCTION__);
+ return NULL;
+ }
+
+ switch (pxa_ep->ep_type) {
+ case USB_ENDPOINT_XFER_BULK:
+ sprintf(name, "Bulk-%s-%d", (pxa_ep->dir_in ? "in" : "out"), i);
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ sprintf(name, "Interrupt-%s-%d", (pxa_ep->dir_in ?
+ "in" : "out"), i);
+ break;
+ default:
+ sprintf(name, "endpoint-%s-%d", (pxa_ep->dir_in ?
+ "in" : "out"), i);
+ break;
+ }
+ ep->name = name;
+
+ ep->ops = &pxa27x_ep_ops;
+ ep->maxpacket = min((ushort) pxa_ep->fifo_size, desc->wMaxPacketSize);
+
+ list_add_tail(&ep->ep_list, &gadget->ep_list);
+ return ep;
+}
+
+static int pxa27x_udc_get_frame(struct usb_gadget *_gadget)
+{
+ return (UDCFNR & 0x3FF);
+}
+
+static int pxa27x_udc_wakeup(struct usb_gadget *_gadget)
+{
+ /* host may not have enabled remote wakeup */
+ if ((UDCCR & UDCCR_DWRE) == 0)
+ return -EHOSTUNREACH;
+ udc_set_mask_UDCCR(UDCCR_UDR);
+ return 0;
+}
+
+static const struct usb_gadget_ops pxa27x_udc_ops = {
+ .ep_alloc = pxa27x_ep_alloc,
+ .get_frame = pxa27x_udc_get_frame,
+ .wakeup = pxa27x_udc_wakeup,
+ /* current versions must always be self-powered */
+};
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef UDC_PROC_FILE
+
+static const char proc_node_name[] = "driver/udc";
+
+static int
+udc_proc_read(char *page, char **start, off_t off, int count,
+ int *eof, void *_dev)
+{
+ char *buf = page;
+ struct pxa27x_udc *dev = _dev;
+ char *next = buf;
+ unsigned size = count;
+ unsigned long flags;
+ int i, t;
+ u32 tmp;
+
+ if (off != 0)
+ return 0;
+
+ local_irq_save(flags);
+
+ /* basic device status */
+ t = scnprintf(next, size, DRIVER_DESC "\n"
+ "%s version: %s\nGadget driver: %s\n",
+ driver_name, DRIVER_VERSION SIZE_STR DMASTR,
+ dev->driver ? dev->driver->driver.name : "(none)");
+ size -= t;
+ next += t;
+
+ /* registers for device and ep0 */
+ t = scnprintf(next, size,
+ "uicr %02X.%02X, usir %02X.%02x, ufnr %02X\n",
+ UDCICR1, UDCICR0, UDCISR1, UDCISR0, UDCFNR);
+ size -= t;
+ next += t;
+
+ tmp = UDCCR;
+ t = scnprintf(next, size,
+ "udccr %02X =%s%s%s%s%s%s%s%s%s%s, con=%d,inter=%d,altinter=%d\n",
+ tmp, (tmp & UDCCR_OEN) ? " oen" : "",
+ (tmp & UDCCR_AALTHNP) ? " aalthnp" : "",
+ (tmp & UDCCR_AHNP) ? " rem" : "",
+ (tmp & UDCCR_BHNP) ? " rstir" : "",
+ (tmp & UDCCR_DWRE) ? " dwre" : "",
+ (tmp & UDCCR_SMAC) ? " smac" : "",
+ (tmp & UDCCR_EMCE) ? " emce" : "",
+ (tmp & UDCCR_UDR) ? " udr" : "",
+ (tmp & UDCCR_UDA) ? " uda" : "",
+ (tmp & UDCCR_UDE) ? " ude" : "",
+ (tmp & UDCCR_ACN) >> UDCCR_ACN_S,
+ (tmp & UDCCR_AIN) >> UDCCR_AIN_S,
+ (tmp & UDCCR_AAISN) >> UDCCR_AAISN_S);
+
+ size -= t;
+ next += t;
+
+ tmp = UDCCSR0;
+ t = scnprintf(next, size,
+ "udccsr0 %02X =%s%s%s%s%s%s%s\n", tmp,
+ (tmp & UDCCSR0_SA) ? " sa" : "",
+ (tmp & UDCCSR0_RNE) ? " rne" : "",
+ (tmp & UDCCSR0_FST) ? " fst" : "",
+ (tmp & UDCCSR0_SST) ? " sst" : "",
+ (tmp & UDCCSR0_DME) ? " dme" : "",
+ (tmp & UDCCSR0_IPR) ? " ipr" : "",
+ (tmp & UDCCSR0_OPC) ? " opc" : "");
+ size -= t;
+ next += t;
+
+ if (!dev->driver)
+ goto done;
+
+ t = scnprintf(next, size, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n",
+ dev->stats.write.bytes, dev->stats.write.ops,
+ dev->stats.read.bytes, dev->stats.read.ops,
+ dev->stats.irqs);
+ size -= t;
+ next += t;
+
+ /* dump endpoint queues */
+ for (i = 0; i < UDC_EP_NUM; i++) {
+ struct pxa27x_ep *ep = &dev->ep[i];
+ struct pxa27x_request *req;
+ int t;
+
+ if (i != 0) {
+ const struct usb_endpoint_descriptor *d;
+
+ d = ep->desc;
+ if (!d)
+ continue;
+ tmp = *dev->ep[i].reg_udccsr;
+ t = scnprintf(next, size,
+ "%s max %d %s udccs %02x udccr:0x%x\n",
+ ep->ep.name,
+ le16_to_cpu(d->wMaxPacketSize),
+ (ep->dma >= 0) ? "dma" : "pio", tmp,
+ *dev->ep[i].reg_udccr);
+ /* TODO translate all five groups of udccs bits! */
+
+ } else /* ep0 should only have one transfer queued */
+ t = scnprintf(next, size, "ep0 max 16 pio irqs %lu\n",
+ ep->pio_irqs);
+ if (t <= 0 || t > size)
+ goto done;
+ size -= t;
+ next += t;
+
+ if (list_empty(&ep->queue)) {
+ t = scnprintf(next, size, "\t(nothing queued)\n");
+ if (t <= 0 || t > size)
+ goto done;
+ size -= t;
+ next += t;
+ continue;
+ }
+ list_for_each_entry(req, &ep->queue, queue) {
+#ifdef USE_DMA
+ if (ep->dma >= 0 && req->queue.prev == &ep->queue)
+ t = scnprintf(next, size,
+ "\treq %p len %d/%d "
+ "buf %p (dma%d dcmd %08x)\n",
+ &req->req, req->req.actual,
+ req->req.length, req->req.buf,
+ ep->dma, DCMD(ep->dma)
+ /* low 13 bits == bytes-to-go */
+ );
+ else
+#endif
+ t = scnprintf(next, size,
+ "\treq %p len %d/%d buf %p\n",
+ &req->req, req->req.actual,
+ req->req.length, req->req.buf);
+ if (t <= 0 || t > size)
+ goto done;
+ size -= t;
+ next += t;
+ }
+ }
+
+ done:
+ local_irq_restore(flags);
+ *eof = 1;
+ return count - size;
+}
+
+#define create_proc_files() \
+ create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev)
+#define remove_proc_files() \
+ remove_proc_entry(proc_node_name, NULL)
+
+#else /* !UDC_PROC_FILE */
+#define create_proc_files() do {} while (0)
+#define remove_proc_files() do {} while (0)
+
+#endif /* UDC_PROC_FILE */
+
+/* "function" sysfs attribute */
+static ssize_t
+show_function(struct device *_dev, struct device_attribute *attr, char *buf)
+{
+ struct pxa27x_udc *dev = dev_get_drvdata(_dev);
+
+ if (!dev->driver
+ || !dev->driver->function
+ || strlen(dev->driver->function) > PAGE_SIZE)
+ return 0;
+ return scnprintf(buf, PAGE_SIZE, "%s\n", dev->driver->function);
+}
+
+static DEVICE_ATTR(function, S_IRUGO, show_function, NULL);
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * udc_disable - disable USB device controller
+ */
+static void udc_disable(struct pxa27x_udc *dev)
+{
+ UDCICR0 = 0x00000000;
+ UDCICR1 = 0x00000000;
+
+ udc_clear_mask_UDCCR(UDCCR_UDE);
+
+ /* Disable clock for USB device */
+ pxa_set_cken(CKEN_USB, 0);
+
+ ep0_idle(dev);
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+ pullup_off();
+}
+
+/*
+ * udc_reinit - initialize software state
+ */
+static void udc_reinit(struct pxa27x_udc *dev)
+{
+ u32 i;
+
+ dev->ep0state = EP0_IDLE;
+
+ /* basic endpoint records init */
+ for (i = 0; i < UDC_EP_NUM; i++) {
+ struct pxa27x_ep *ep = &dev->ep[i];
+
+ ep->stopped = 0;
+ ep->pio_irqs = ep->dma_irqs = 0;
+ }
+ dev->configuration = 0;
+ dev->interface = 0;
+ dev->alternate = 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 pxa27x_udc *dev)
+{
+ udc_clear_mask_UDCCR(UDCCR_UDE);
+
+ /* Enable clock for USB device */
+ pxa_set_cken(CKEN_USB, 1);
+
+ UDCICR0 = UDCICR1 = 0;
+
+ ep0_idle(dev);
+ dev->gadget.speed = USB_SPEED_FULL;
+ dev->stats.irqs = 0;
+
+ udc_set_mask_UDCCR(UDCCR_UDE);
+ udelay(2);
+ if (UDCCR & UDCCR_EMCE) {
+ printk(KERN_ERR
+ ": There are error in configuration, udc disabled\n");
+ }
+
+ /* caller must be able to sleep in order to cope
+ * with startup transients.
+ */
+ msleep(100);
+
+ /* enable suspend/resume and reset irqs */
+ UDCICR1 = UDCICR1_IECC | UDCICR1_IERU | UDCICR1_IESU | UDCICR1_IERS;
+
+ /* enable ep0 irqs */
+ UDCICR0 = UDCICR_INT(0, UDCICR_INT_MASK);
+#if 0
+ for (i = 1; i < UDC_EP_NUM; i++) {
+ if (dev->ep[i].assigned)
+ pio_irq_enable(i);
+ }
+#endif
+
+ pullup_on();
+}
+
+/* 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 pxa27x_udc *dev = the_controller;
+ int retval;
+
+ DMSG("dev=0x%x, driver=0x%x, speed=%d, "
+ "bind=0x%x, unbind=0x%x, disconnect=0x%x, setup=0x%x\n",
+ (unsigned)dev, (unsigned)driver, driver->speed,
+ (unsigned)driver->bind, (unsigned)driver->unbind,
+ (unsigned)driver->disconnect, (unsigned)driver->setup);
+
+ if (!driver || driver->speed != USB_SPEED_FULL
+ || !driver->bind
+ || !driver->unbind || !driver->disconnect || !driver->setup)
+ return -EINVAL;
+ if (!dev)
+ return -ENODEV;
+ if (dev->driver)
+ return -EBUSY;
+
+ /* first hook up the driver ... */
+ dev->driver = driver;
+ dev->gadget.dev.driver = &driver->driver;
+
+ retval = device_add(&dev->gadget.dev);
+ if (retval) {
+ DMSG("unable to add device for %s --> error %d\n",
+ driver->driver.name, retval);
+ goto device_add_error;
+ }
+ retval = driver->bind(&dev->gadget);
+ if (retval) {
+ DMSG("bind to driver %s --> error %d\n",
+ driver->driver.name, retval);
+ goto device_bind_error;
+ }
+ retval = device_create_file(dev->dev, &dev_attr_function);
+ if (retval) {
+ DMSG("unable to create file for %s --> error %d\n",
+ driver->driver.name, retval);
+ goto create_file_error;
+ }
+
+ /* ... then enable host detection and ep0; and we're ready
+ * for set_configuration as well as eventual disconnect.
+ * NOTE: this shouldn't power up until later.
+ */
+ DMSG("registered gadget driver '%s'\n", driver->driver.name);
+ udc_enable(dev);
+ dump_state(dev);
+
+ return 0;
+
+ create_file_error:
+ driver->unbind(&dev->gadget);
+
+ device_bind_error:
+ device_del(&dev->gadget.dev);
+
+ device_add_error:
+ dev->driver = 0;
+ dev->gadget.dev.driver = 0;
+
+ return retval;
+}
+
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+static void
+stop_activity(struct pxa27x_udc *dev, struct usb_gadget_driver *driver)
+{
+ int i;
+
+ DMSG("Trace path 1\n");
+ /* don't disconnect drivers more than once */
+ if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+ driver = 0;
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+ /* prevent new request submissions, kill any outstanding requests */
+ for (i = 0; i < UDC_EP_NUM; i++) {
+ struct pxa27x_ep *ep = &dev->ep[i];
+
+ ep->stopped = 1;
+ nuke(ep, -ESHUTDOWN);
+ }
+ del_timer_sync(&dev->timer);
+
+ /* 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 pxa27x_udc *dev = the_controller;
+
+ if (!dev)
+ return -ENODEV;
+ if (!driver || driver != dev->driver)
+ return -EINVAL;
+
+ local_irq_disable();
+ udc_disable(dev);
+ stop_activity(dev, driver);
+ local_irq_enable();
+
+ driver->unbind(&dev->gadget);
+ dev->driver = 0;
+
+ device_del(&dev->gadget.dev);
+ device_remove_file(dev->dev, &dev_attr_function);
+
+ DMSG("unregistered gadget driver '%s'\n", driver->driver.name);
+ dump_state(dev);
+ return 0;
+}
+
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+#ifndef enable_disconnect_irq
+#define enable_disconnect_irq() do {} while (0)
+#define disable_disconnect_irq() do {} while (0)
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+static inline void clear_ep_state(struct pxa27x_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 < UDC_EP_NUM; i++)
+ nuke(&dev->ep[i], -ECONNABORTED);
+}
+
+static void udc_watchdog(unsigned long _dev)
+{
+ struct pxa27x_udc *dev = (void *)_dev;
+
+ local_irq_disable();
+ if (dev->ep0state == EP0_STALL
+ && (UDCCSR0 & UDCCSR0_FST) == 0 && (UDCCSR0 & UDCCSR0_SST) == 0) {
+ UDCCSR0 = UDCCSR0_FST | UDCCSR0_FTF;
+ DBG(DBG_VERBOSE, "ep0 re-stall\n");
+ start_watchdog(dev);
+ }
+ local_irq_enable();
+}
+
+static void handle_ep0(struct pxa27x_udc *dev)
+{
+ u32 udccsr0 = UDCCSR0;
+ struct pxa27x_ep *ep = &dev->ep[0];
+ struct pxa27x_request *req;
+ union {
+ struct usb_ctrlrequest r;
+ u8 raw[8];
+ u32 word[2];
+ } u;
+
+ if (list_empty(&ep->queue))
+ req = 0;
+ else
+ req = list_entry(ep->queue.next, struct pxa27x_request, queue);
+
+ /* clear stall status */
+ if (udccsr0 & UDCCSR0_SST) {
+ nuke(ep, -EPIPE);
+ UDCCSR0 = UDCCSR0_SST;
+ del_timer(&dev->timer);
+ ep0_idle(dev);
+ }
+
+ /* previous request unfinished? non-error iff back-to-back ... */
+ if ((udccsr0 & UDCCSR0_SA) != 0 && dev->ep0state != EP0_IDLE) {
+ nuke(ep, 0);
+ del_timer(&dev->timer);
+ ep0_idle(dev);
+ }
+
+ switch (dev->ep0state) {
+ case EP0_NO_ACTION:
+ printk(KERN_INFO "%s: Busy\n", __FUNCTION__);
+ /*Fall through */
+ case EP0_IDLE:
+ /* late-breaking status? */
+ udccsr0 = UDCCSR0;
+
+ /* start control request? */
+ if (likely((udccsr0 & (UDCCSR0_OPC | UDCCSR0_SA | UDCCSR0_RNE))
+ == (UDCCSR0_OPC | UDCCSR0_SA | UDCCSR0_RNE))) {
+ int i;
+
+ nuke(ep, -EPROTO);
+ /* read SETUP packet */
+ for (i = 0; i < 2; i++) {
+ if (unlikely(!(UDCCSR0 & UDCCSR0_RNE))) {
+ bad_setup:
+ DMSG("SETUP %d!\n", i);
+ goto stall;
+ }
+ u.word[i] = UDCDR0;
+ }
+ if (unlikely((UDCCSR0 & UDCCSR0_RNE) != 0))
+ goto bad_setup;
+
+ le16_to_cpus(&u.r.wValue);
+ le16_to_cpus(&u.r.wIndex);
+ le16_to_cpus(&u.r.wLength);
+
+ LED_EP0_ON;
+
+ DBG(DBG_VERBOSE, "SETUP %02x.%02x v%04x i%04x l%04x\n",
+ u.r.bRequestType, u.r.bRequest,
+ u.r.wValue, u.r.wIndex, 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;
+#if 0
+ switch (u.r.bRequest) {
+ /* hardware was supposed to hide this */
+ case USB_REQ_SET_CONFIGURATION:
+ case USB_REQ_SET_INTERFACE:
+ case USB_REQ_SET_ADDRESS:
+ printk(KERN_ERR "Should not come here\n");
+ break;
+ }
+
+#endif
+ 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...
+ */
+ WARN("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!
+ */
+ }
+ DBG(DBG_VERBOSE, "protocol STALL, "
+ "%02x err %d\n", UDCCSR0, i);
+ stall:
+ /* the watchdog timer helps deal with cases
+ * where udc seems to clear FST wrongly, and
+ * then NAKs instead of STALLing.
+ */
+ ep0start(dev, UDCCSR0_FST | UDCCSR0_FTF,
+ "stall");
+ start_watchdog(dev);
+ dev->ep0state = EP0_STALL;
+ LED_EP0_OFF;
+
+ /* 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, UDCCSR0_IPR, "defer/IPR");
+ }
+
+ /* expect at least one data or status stage irq */
+ return;
+
+ } else {
+ /* some random early IRQ:
+ * - we acked FST
+ * - IPR cleared
+ * - OPC got set, without SA (likely status stage)
+ */
+ UDCCSR0 = udccsr0 & (UDCCSR0_SA | UDCCSR0_OPC);
+ }
+ break;
+ case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */
+ if (udccsr0 & UDCCSR0_OPC) {
+ UDCCSR0 = UDCCSR0_OPC | UDCCSR0_FTF;
+ DBG(DBG_VERBOSE, "ep0in premature status\n");
+ if (req)
+ done(ep, req, 0);
+ ep0_idle(dev);
+ } else { /* irq was IPR clearing */
+
+ if (req) {
+ /* 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 (udccsr0 & UDCCSR0_OPC) {
+ 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 */
+
+ DBG(DBG_VERBOSE, "ep0out premature status\n");
+ if (req)
+ done(ep, req, 0);
+ ep0_idle(dev);
+ }
+ break;
+ case EP0_STALL:
+ UDCCSR0 = UDCCSR0_FST;
+ break;
+ }
+ UDCISR0 = UDCISR_INT(0, UDCISR_INT_MASK);
+}
+
+static void handle_ep(struct pxa27x_ep *ep)
+{
+ struct pxa27x_request *req;
+ int completed;
+ u32 udccsr = 0;
+
+ DMSG("%s is called\n", __FUNCTION__);
+ do {
+ completed = 0;
+ if (likely(!list_empty(&ep->queue))) {
+ req = list_entry(ep->queue.next,
+ struct pxa27x_request, queue);
+ } else
+ req = 0;
+
+#if 0
+ udccsr = *ep->reg_udccsr;
+#endif
+ DMSG("%s: req:%p, udcisr0:0x%x udccsr %p:0x%x\n", __FUNCTION__,
+ req, UDCISR0, ep->reg_udccsr, *ep->reg_udccsr);
+ if (unlikely(ep->dir_in)) {
+ udccsr = (UDCCSR_SST | UDCCSR_TRN) & *ep->reg_udccsr;
+ if (unlikely(udccsr))
+ *ep->reg_udccsr = udccsr;
+
+ if (req && likely((*ep->reg_udccsr & UDCCSR_FS) != 0))
+ completed = write_fifo(ep, req);
+
+ } else {
+ udccsr = (UDCCSR_SST | UDCCSR_TRN) & *ep->reg_udccsr;
+ if (unlikely(udccsr))
+ *ep->reg_udccsr = udccsr;
+
+ /* fifos can hold packets, ready for reading... */
+ if (likely(req)) {
+ completed = read_fifo(ep, req);
+ } else {
+ pio_irq_disable(ep->ep_num);
+ *ep->reg_udccsr = UDCCSR_FEF;
+ DMSG("%s: no req for out data\n", __FUNCTION__);
+ }
+ }
+ ep->pio_irqs++;
+ } while (completed);
+}
+
+static void pxa27x_change_configuration(struct pxa27x_udc *dev)
+{
+ struct usb_ctrlrequest req;
+
+ req.bRequestType = 0;
+ req.bRequest = USB_REQ_SET_CONFIGURATION;
+ req.wValue = dev->configuration;
+ req.wIndex = 0;
+ req.wLength = 0;
+
+ dev->ep0state = EP0_NO_ACTION;
+ dev->driver->setup(&dev->gadget, &req);
+
+}
+
+static void pxa27x_change_interface(struct pxa27x_udc *dev)
+{
+ struct usb_ctrlrequest req;
+
+ req.bRequestType = USB_RECIP_INTERFACE;
+ req.bRequest = USB_REQ_SET_INTERFACE;
+ req.wValue = dev->alternate;
+ req.wIndex = dev->interface;
+ req.wLength = 0;
+
+ dev->ep0state = EP0_NO_ACTION;
+ dev->driver->setup(&dev->gadget, &req);
+}
+
+/*
+ * pxa27x_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 irqreturn_t pxa27x_udc_irq(int irq, void *_dev)
+{
+ struct pxa27x_udc *dev = _dev;
+ int handled;
+
+ dev->stats.irqs++;
+ HEX_DISPLAY(dev->stats.irqs);
+
+ DBG(DBG_VERBOSE, "Interrupt, UDCISR0:0x%08x, UDCISR1:0x%08x, "
+ "UDCCR:0x%08x\n", UDCISR0, UDCISR1, UDCCR);
+
+ do {
+ u32 udcir = UDCISR1 & 0xF8000000;
+
+ handled = 0;
+
+ /* SUSpend Interrupt Request */
+ if (unlikely(udcir & UDCISR1_IRSU)) {
+ UDCISR1 = UDCISR1_IRSU;
+ handled = 1;
+ DBG(DBG_VERBOSE, "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(udcir & UDCISR1_IRRU)) {
+ UDCISR1 = UDCISR1_IRRU;
+ handled = 1;
+ DBG(DBG_VERBOSE, "USB resume\n");
+
+ if (dev->gadget.speed != USB_SPEED_UNKNOWN
+ && dev->driver && dev->driver->resume)
+ dev->driver->resume(&dev->gadget);
+ }
+
+ if (unlikely(udcir & UDCISR1_IRCC)) {
+ unsigned config, interface, alternate;
+
+ handled = 1;
+ DBG(DBG_VERBOSE, "USB SET_CONFIGURATION or "
+ "SET_INTERFACE command received\n");
+
+ UDCCR |= UDCCR_SMAC;
+
+ config = (UDCCR & UDCCR_ACN) >> UDCCR_ACN_S;
+
+ if (dev->configuration != config) {
+ dev->configuration = config;
+ pxa27x_change_configuration(dev);
+ }
+
+ interface = (UDCCR & UDCCR_AIN) >> UDCCR_AIN_S;
+ alternate = (UDCCR & UDCCR_AAISN) >> UDCCR_AAISN_S;
+
+ if ((dev->configuration != interface) ||
+ (dev->alternate != alternate)) {
+ dev->interface = config;
+ dev->alternate = alternate;
+ pxa27x_change_interface(dev);
+ }
+
+ UDCISR1 = UDCISR1_IRCC;
+ DMSG("%s: con:%d,inter:%d,alt:%d\n",
+ __FUNCTION__, config, interface, alternate);
+ }
+
+ /* ReSeT Interrupt Request - USB reset */
+ if (unlikely(udcir & UDCISR1_IRRS)) {
+ UDCISR1 = UDCISR1_IRRS;
+ handled = 1;
+
+ if ((UDCCR & UDCCR_UDA) == 0) {
+ DBG(DBG_VERBOSE, "SB reset start\n");
+
+ /* reset driver and endpoints,
+ * in case that's not yet done
+ */
+ stop_activity(dev, dev->driver);
+
+ }
+ INFO("USB reset\n");
+ dev->gadget.speed = USB_SPEED_FULL;
+ memset(&dev->stats, 0, sizeof dev->stats);
+
+ } else {
+ u32 udcisr0 = UDCISR0;
+ u32 udcisr1 = UDCISR1 & 0xFFFF;
+ int i;
+
+ if (unlikely(!udcisr0 && !udcisr1))
+ continue;
+
+ DBG(DBG_VERY_NOISY, "irq %02x.%02x\n", udcisr1,
+ udcisr0);
+
+ /* control traffic */
+ if (udcisr0 & UDCISR0_IR0) {
+ dev->ep[0].pio_irqs++;
+ handle_ep0(dev);
+ handled = 1;
+ }
+
+ udcisr0 >>= 2;
+ /* endpoint data transfers */
+ for (i = 1; udcisr0 != 0 && i < 16; udcisr0 >>= 2, i++) {
+ UDCISR0 = UDCISR_INT(i, UDCISR_INT_MASK);
+
+ if (udcisr0 & UDC_INT_FIFOERROR)
+ printk(KERN_ERR
+ " Endpoint %d Fifo error\n", i);
+ if (udcisr0 & UDC_INT_PACKETCMP) {
+ handle_ep(&dev->ep[i]);
+ handled = 1;
+ }
+
+ }
+
+ for (i = 0; udcisr1 != 0 && i < 8; udcisr1 >>= 2, i++) {
+ UDCISR1 = UDCISR_INT(i, UDCISR_INT_MASK);
+
+ if (udcisr1 & UDC_INT_FIFOERROR) {
+ printk(KERN_ERR
+ " Endpoint %d fifo error\n",
+ (i + 16));
+ }
+
+ if (udcisr1 & UDC_INT_PACKETCMP) {
+ handle_ep(&dev->ep[i + 16]);
+ handled = 1;
+ }
+ }
+ }
+
+ /* we could also ask for 1 msec SOF (SIR) interrupts */
+
+ } while (handled);
+ return IRQ_HANDLED;
+}
+
+static void udc_init_ep(struct pxa27x_udc *dev)
+{
+ int i;
+
+ INIT_LIST_HEAD(&dev->gadget.ep_list);
+ INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+
+ for (i = 0; i < UDC_EP_NUM; i++) {
+ struct pxa27x_ep *ep = &dev->ep[i];
+
+ ep->dma = -1;
+ if (i != 0) {
+ memset(ep, 0, sizeof(*ep));
+ }
+ INIT_LIST_HEAD(&ep->queue);
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void nop_release(struct device *dev)
+{
+ DMSG("%s %s\n", __FUNCTION__, dev->bus_id);
+}
+
+/* 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 pxa27x_udc memory = {
+ .gadget = {
+ .ops = &pxa27x_udc_ops,
+ .ep0 = &memory.ep[0].ep,
+ .name = driver_name,
+ .dev = {
+ .bus_id = "gadget",
+ .release = nop_release,
+ },
+ },
+
+ /* control endpoint */
+ .ep[0] = {
+ .ep = {
+ .name = ep0name,
+ .ops = &pxa27x_ep_ops,
+ .maxpacket = EP0_FIFO_SIZE,
+ },
+ .dev = &memory,
+ .reg_udccsr = &UDCCSR0,
+ .reg_udcdr = &UDCDR0,
+ }
+};
+
+#define CP15R0_VENDOR_MASK 0xffffe000
+
+#define CP15R0_XSCALE_VALUE 0x69054000 /* intel/arm/xscale */
+
+/*
+ * probe - binds to the platform device
+ */
+static int __init pxa27x_udc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct pxa27x_udc *udc = &memory;
+ int irq, retval;
+ u32 chiprev;
+
+ /* insist on Intel/ARM/XScale */
+ asm("mrc%? p15, 0, %0, c0, c0":"=r"(chiprev));
+ if ((chiprev & CP15R0_VENDOR_MASK) != CP15R0_XSCALE_VALUE) {
+ printk(KERN_ERR "%s: not XScale!\n", driver_name);
+ return -ENODEV;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return -ENODEV;
+ pr_debug("%s: IRQ %d\n", driver_name, irq);
+
+ /* other non-static parts of init */
+ udc->dev = dev;
+ udc->mach = dev->platform_data;
+
+ /* Disable irq, erase old events and disable the pull up on the bus */
+ UDCICR0 = 0x00000000;
+ UDCICR1 = 0x00000000;
+ UDCISR0 = 0xffffffff;
+ UDCISR1 = 0xffffffff;
+ if (udc->mach->gpio_pullup)
+ udc_gpio_init_pullup(udc->mach->gpio_pullup);
+
+ init_timer(&udc->timer);
+ udc->timer.function = udc_watchdog;
+ udc->timer.data = (unsigned long)udc;
+
+ device_initialize(&udc->gadget.dev);
+ udc->gadget.dev.parent = dev;
+ udc->gadget.dev.dma_mask = dev->dma_mask;
+
+ the_controller = udc;
+ dev_set_drvdata(dev, udc);
+
+ udc_disable(udc);
+ udc_init_ep(udc);
+ udc_reinit(udc);
+
+ /* irq setup after old hardware state is cleaned up */
+ retval = request_irq(irq, pxa27x_udc_irq, 0, driver_name, udc);
+ if (retval != 0) {
+ printk(KERN_ERR "%s: can't get irq %i, err %d\n",
+ driver_name, irq, retval);
+ return -EBUSY;
+ }
+ udc->got_irq = 1;
+
+ create_proc_files();
+
+ return 0;
+}
+
+static void pxa27x_udc_shutdown(struct platform_device *_dev)
+{
+ pullup_off();
+}
+
+static int __exit pxa27x_udc_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct pxa27x_udc *udc = dev->driver_data;
+
+ udc_disable(udc);
+ remove_proc_files();
+ usb_gadget_unregister_driver(udc->driver);
+
+ if (udc->got_irq) {
+ free_irq(platform_get_irq(pdev, 0), udc);
+ udc->got_irq = 0;
+ }
+ if (machine_is_lubbock() && udc->got_disc) {
+ free_irq(LUBBOCK_USB_DISC_IRQ, udc);
+ udc->got_disc = 0;
+ }
+ dev_set_drvdata(dev, 0);
+ the_controller = 0;
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int pxa27x_udc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct device *dev = &pdev->dev;
+ struct pxa27x_udc *udc = dev->driver_data;
+ int i;
+
+ DMSG("%s will go into SUSPEND_POWER_DOWN\n", __FUNCTION__);
+ udc->udccsr0 = UDCCSR0;
+ for (i = 1; (i < UDC_EP_NUM); i++) {
+ if (udc->ep[i].assigned) {
+ struct pxa27x_ep *ep = &udc->ep[i];
+
+ ep->udccsr_value = *ep->reg_udccsr;
+ ep->udccr_value = *ep->reg_udccr;
+ DMSG("EP%d, udccsr:0x%x, udccr:0x%x\n",
+ i, *ep->reg_udccsr, *ep->reg_udccr);
+ }
+ }
+
+ udc_clear_mask_UDCCR(UDCCR_UDE);
+ pxa_set_cken(CKEN_USB, 0);
+
+ return 0;
+}
+
+static int pxa27x_udc_resume(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct pxa27x_udc *udc = dev->driver_data;
+ int i;
+
+ DMSG("%s: udc resume\n", __FUNCTION__);
+
+ UDCCSR0 = udc->udccsr0 & (UDCCSR0_FST | UDCCSR0_DME);
+ for (i = 1; i < UDC_EP_NUM; i++) {
+ if (udc->ep[i].assigned) {
+ struct pxa27x_ep *ep = &udc->ep[i];
+
+ *ep->reg_udccsr = ep->udccsr_value;
+ *ep->reg_udccr = ep->udccr_value;
+ DMSG("EP%d, udccsr:0x%x, udccr:0x%x\n",
+ i, *ep->reg_udccsr, *ep->reg_udccr);
+ }
+ }
+ udc_enable(udc);
+ /* OTGPH bit is set when sleep mode is entered.
+ * it indicates that OTG pad is retaining its state.
+ * Upon exit from sleep mode and before clearing OTGPH,
+ * Software must configure the USB OTG pad, UDC, and UHC
+ * to the state they were in before entering sleep mode.*/
+ PSSR |= PSSR_OTGPH;
+
+ return 0;
+}
+#else
+#define pxa27x_udc_suspend NULL
+#define pxa27x_udc_resume NULL
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+static struct platform_driver pxa27x_udc_driver = {
+ .shutdown = pxa27x_udc_shutdown,
+ .remove = __exit_p(pxa27x_udc_remove),
+ .suspend = pxa27x_udc_suspend,
+ .resume = pxa27x_udc_resume,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "pxa27x-udc",
+ },
+};
+
+static int __init udc_init(void)
+{
+ printk(KERN_INFO "%s: version %s\n", driver_name, DRIVER_VERSION);
+ return platform_driver_probe(&pxa27x_udc_driver, pxa27x_udc_probe);
+}
+
+static void __exit udc_exit(void)
+{
+ platform_driver_unregister(&pxa27x_udc_driver);
+}
+
+module_init(udc_init);
+module_exit(udc_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Frank Becker, Robert Schwebel, David Brownell, Rodolfo Giometti");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/pxa27x_udc.h
new file mode 100644
index 0000000..633d332
--- /dev/null
+++ b/drivers/usb/gadget/pxa27x_udc.h
@@ -0,0 +1,304 @@
+/*
+ * linux/drivers/usb/gadget/pxa27x_udc.h
+ * Intel PXA27x on-chip full speed USB device controller
+ *
+ * Copyright (C) 2003 Robert Schwebel <r.schwebel@pengutronix.de>, Pengutronix
+ * Copyright (C) 2003 David Brownell
+ * Copyright (C) 2004 Intel Corporation
+ *
+ * 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_PXA27X_H
+#define __LINUX_USB_GADGET_PXA27X_H
+
+#include <linux/types.h>
+
+struct pxa27x_udc;
+
+struct pxa27x_ep {
+ struct usb_ep ep;
+ struct pxa27x_udc *dev;
+
+ const struct usb_endpoint_descriptor *desc;
+ struct list_head queue;
+ unsigned long pio_irqs;
+ unsigned long dma_irqs;
+
+ int dma;
+ unsigned fifo_size;
+ unsigned ep_num;
+ unsigned ep_type;
+
+ unsigned stopped : 1;
+ unsigned dma_con : 1;
+ unsigned dir_in : 1;
+ unsigned assigned : 1;
+
+ unsigned config;
+ unsigned interface;
+ unsigned aisn;
+ /* UDCCSR = UDC Control/Status Register for this EP
+ * UBCR = UDC Byte Count Remaining (contents of OUT fifo)
+ * UDCDR = UDC Endpoint Data Register (the fifo)
+ * UDCCR = UDC Endpoint Configuration Registers
+ * DRCM = DMA Request Channel Map
+ */
+ volatile u32 *reg_udccsr;
+ volatile u32 *reg_udcbcr;
+ volatile u32 *reg_udcdr;
+ volatile u32 *reg_udccr;
+#ifdef USE_DMA
+ volatile u32 *reg_drcmr;
+#define drcmr(n) .reg_drcmr = & DRCMR ## n ,
+#else
+#define drcmr(n)
+#endif
+
+#ifdef CONFIG_PM
+ unsigned udccsr_value;
+ unsigned udccr_value;
+#endif
+};
+
+struct pxa27x_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,
+ EP0_NO_ACTION
+};
+
+#define EP0_FIFO_SIZE ((unsigned)16)
+#define BULK_FIFO_SIZE ((unsigned)64)
+#define ISO_FIFO_SIZE ((unsigned)256)
+#define INT_FIFO_SIZE ((unsigned)8)
+
+struct udc_stats {
+ struct ep0stats {
+ unsigned long ops;
+ unsigned long bytes;
+ } read, write;
+ unsigned long irqs;
+};
+
+#ifdef CONFIG_USB_PXA27X_SMALL
+/* when memory's tight, SMALL config saves code+data. */
+//#undef USE_DMA
+//#define UDC_EP_NUM 3
+#endif
+
+#ifndef UDC_EP_NUM
+#define UDC_EP_NUM 24
+#endif
+
+struct pxa27x_udc {
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *driver;
+
+ enum ep0_state ep0state;
+ struct udc_stats stats;
+ unsigned got_irq : 1,
+ got_disc : 1,
+ has_cfr : 1,
+ req_pending : 1,
+ req_std : 1,
+ req_config : 1;
+
+#define start_watchdog(dev) mod_timer(&dev->timer, jiffies + (HZ/200))
+ struct timer_list timer;
+
+ struct device *dev;
+ struct pxa2xx_udc_mach_info *mach;
+ u64 dma_mask;
+ struct pxa27x_ep ep [UDC_EP_NUM];
+
+ unsigned configuration,
+ interface,
+ alternate;
+#ifdef CONFIG_PM
+ unsigned udccsr0;
+#endif
+};
+
+/*-------------------------------------------------------------------------*/
+#if 0
+#ifdef DEBUG
+#define HEX_DISPLAY(n) do { \
+ if (machine_is_mainstone())\
+ { MST_LEDDAT1 = (n); } \
+ } while(0)
+
+#define HEX_DISPLAY1(n) HEX_DISPLAY(n)
+
+#define HEX_DISPLAY2(n) do { \
+ if (machine_is_mainstone()) \
+ { MST_LEDDAT2 = (n); } \
+ } while(0)
+
+#endif /* DEBUG */
+#endif
+/*-------------------------------------------------------------------------*/
+
+/* LEDs are only for debug */
+#ifndef HEX_DISPLAY
+#define HEX_DISPLAY(n) do {} while(0)
+#endif
+
+#ifndef LED_CONNECTED_ON
+#define LED_CONNECTED_ON do {} while(0)
+#define LED_CONNECTED_OFF do {} while(0)
+#endif
+#ifndef LED_EP0_ON
+#define LED_EP0_ON do {} while (0)
+#define LED_EP0_OFF do {} while (0)
+#endif
+
+static struct pxa27x_udc *the_controller;
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Debugging support vanishes in non-debug builds. DBG_NORMAL should be
+ * mostly silent during normal use/testing, with no timing side-effects.
+ */
+#define DBG_NORMAL 1 /* error paths, device state transitions */
+#define DBG_VERBOSE 2 /* add some success path trace info */
+#define DBG_NOISY 3 /* ... even more: request level */
+#define DBG_VERY_NOISY 4 /* ... even more: packet level */
+
+#ifdef DEBUG
+
+static const char *state_name[] = {
+ "EP0_IDLE",
+ "EP0_IN_DATA_PHASE", "EP0_OUT_DATA_PHASE",
+ "EP0_END_XFER", "EP0_STALL"
+};
+
+#define DMSG(stuff...) printk(KERN_ERR "udc: " stuff)
+
+#ifdef VERBOSE
+# define UDC_DEBUG DBG_VERBOSE
+#else
+# define UDC_DEBUG DBG_NORMAL
+#endif
+
+static void __attribute__ ((__unused__))
+dump_udccr(const char *label)
+{
+ u32 udccr = UDCCR;
+ DMSG("%s 0x%08x =%s%s%s%s%s%s%s%s%s%s, con=%d,inter=%d,altinter=%d\n",
+ label, udccr,
+ (udccr & UDCCR_OEN) ? " oen":"",
+ (udccr & UDCCR_AALTHNP) ? " aalthnp":"",
+ (udccr & UDCCR_AHNP) ? " rem" : "",
+ (udccr & UDCCR_BHNP) ? " rstir" : "",
+ (udccr & UDCCR_DWRE) ? " dwre" : "",
+ (udccr & UDCCR_SMAC) ? " smac" : "",
+ (udccr & UDCCR_EMCE) ? " emce" : "",
+ (udccr & UDCCR_UDR) ? " udr" : "",
+ (udccr & UDCCR_UDA) ? " uda" : "",
+ (udccr & UDCCR_UDE) ? " ude" : "",
+ (udccr & UDCCR_ACN) >> UDCCR_ACN_S,
+ (udccr & UDCCR_AIN) >> UDCCR_AIN_S,
+ (udccr & UDCCR_AAISN)>> UDCCR_AAISN_S );
+}
+
+static void __attribute__ ((__unused__))
+dump_udccsr0(const char *label)
+{
+ u32 udccsr0 = UDCCSR0;
+
+ DMSG("%s %s 0x%08x =%s%s%s%s%s%s%s\n",
+ label, state_name[the_controller->ep0state], udccsr0,
+ (udccsr0 & UDCCSR0_SA) ? " sa" : "",
+ (udccsr0 & UDCCSR0_RNE) ? " rne" : "",
+ (udccsr0 & UDCCSR0_FST) ? " fst" : "",
+ (udccsr0 & UDCCSR0_SST) ? " sst" : "",
+ (udccsr0 & UDCCSR0_DME) ? " dme" : "",
+ (udccsr0 & UDCCSR0_IPR) ? " ipr" : "",
+ (udccsr0 & UDCCSR0_OPC) ? " opr" : "");
+}
+
+static void __attribute__ ((__unused__))
+dump_state(struct pxa27x_udc *dev)
+{
+ unsigned i;
+
+ DMSG("%s, udcicr %02X.%02X, udcsir %02X.%02x, udcfnr %02X\n",
+ state_name[dev->ep0state],
+ UDCICR1, UDCICR0, UDCISR1, UDCISR0, UDCFNR);
+ dump_udccr("udccr");
+
+ if (!dev->driver) {
+ DMSG("no gadget driver bound\n");
+ return;
+ } else
+ DMSG("ep0 driver '%s'\n", dev->driver->driver.name);
+
+
+ dump_udccsr0 ("udccsr0");
+ DMSG("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 < UDC_EP_NUM; i++) {
+ if (dev->ep [i].desc == 0)
+ continue;
+ DMSG ("udccs%d = %02x\n", i, *dev->ep->reg_udccsr);
+ }
+}
+
+#if 0
+static void dump_regs(u8 ep)
+{
+ DMSG("EP:%d UDCCSR:0x%08x UDCBCR:0x%08x\n UDCCR:0x%08x\n",
+ ep,UDCCSN(ep), UDCBCN(ep), UDCCN(ep));
+}
+static void dump_req (struct pxa27x_request *req)
+{
+ struct usb_request *r = &req->req;
+
+ DMSG("%s: buf:0x%08x length:%d dma:0x%08x actual:%d\n",
+ __FUNCTION__, (unsigned)r->buf, r->length,
+ r->dma, r->actual);
+}
+#endif
+
+#else
+
+#define DMSG(stuff...) do{}while(0)
+
+#define dump_udccr(x) do{}while(0)
+#define dump_udccsr0(x) do{}while(0)
+#define dump_state(x) do{}while(0)
+
+#define UDC_DEBUG ((unsigned)0)
+
+#endif
+
+#define DBG(lvl, stuff...) do{if ((lvl) <= UDC_DEBUG) DMSG(stuff);}while(0)
+
+#define WARN(stuff...) printk(KERN_WARNING "udc: " stuff)
+#define INFO(stuff...) printk(KERN_INFO "udc: " stuff)
+
+
+#endif /* __LINUX_USB_GADGET_PXA27X_H */
diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c
index f847c34..dd6535d 100644
--- a/drivers/usb/gadget/serial.c
+++ b/drivers/usb/gadget/serial.c
@@ -1377,20 +1377,20 @@ static int __init gs_bind(struct usb_gadget *gadget)
usb_ep_autoconfig_reset(gadget);
- ep = usb_ep_autoconfig(gadget, &gs_fullspeed_in_desc);
+ ep = usb_ep_autoconfig(gadget, &gs_fullspeed_in_desc, 0, 0, 0);
if (!ep)
goto autoconf_fail;
EP_IN_NAME = ep->name;
ep->driver_data = ep; /* claim the endpoint */
- ep = usb_ep_autoconfig(gadget, &gs_fullspeed_out_desc);
+ ep = usb_ep_autoconfig(gadget, &gs_fullspeed_out_desc, 0, 0, 0);
if (!ep)
goto autoconf_fail;
EP_OUT_NAME = ep->name;
ep->driver_data = ep; /* claim the endpoint */
if (use_acm) {
- ep = usb_ep_autoconfig(gadget, &gs_fullspeed_notify_desc);
+ ep = usb_ep_autoconfig(gadget, &gs_fullspeed_notify_desc, 0, 0, 0);
if (!ep) {
printk(KERN_ERR "gs_bind: cannot run ACM on %s\n", gadget->name);
goto autoconf_fail;
diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c
index 7078374..cc50c1d 100644
--- a/drivers/usb/gadget/zero.c
+++ b/drivers/usb/gadget/zero.c
@@ -1154,7 +1154,7 @@ zero_bind (struct usb_gadget *gadget)
* but there may also be important quirks to address.
*/
usb_ep_autoconfig_reset (gadget);
- ep = usb_ep_autoconfig (gadget, &fs_source_desc);
+ ep = usb_ep_autoconfig (gadget, &fs_source_desc, 0, 0, 0);
if (!ep) {
autoconf_fail:
printk (KERN_ERR "%s: can't autoconfigure on %s\n",
@@ -1164,7 +1164,7 @@ autoconf_fail:
EP_IN_NAME = ep->name;
ep->driver_data = ep; /* claim */
- ep = usb_ep_autoconfig (gadget, &fs_sink_desc);
+ ep = usb_ep_autoconfig (gadget, &fs_sink_desc, 0, 0, 0);
if (!ep)
goto autoconf_fail;
EP_OUT_NAME = ep->name;
diff --git a/include/asm-arm/arch-pxa/udc.h b/include/asm-arm/arch-pxa/udc.h
index 8bc6f9c..969616b 100644
--- a/include/asm-arm/arch-pxa/udc.h
+++ b/include/asm-arm/arch-pxa/udc.h
@@ -1,10 +1,11 @@
/*
* linux/include/asm-arm/arch-pxa/udc.h
*
- * This supports machine-specific differences in how the PXA2xx
+ * This supports machine-specific differences in how the PXA2xx/PXA27x
* USB Device Controller (UDC) is wired.
*
*/
+#include <asm/arch/pxa-regs.h>
#include <asm/mach/udc_pxa2xx.h>
extern void pxa_set_udc_info(struct pxa2xx_udc_mach_info *info);
diff --git a/include/linux/usb_gadget.h b/include/linux/usb_gadget.h
index e17186d..64c81fd 100644
--- a/include/linux/usb_gadget.h
+++ b/include/linux/usb_gadget.h
@@ -445,10 +445,28 @@ usb_ep_fifo_flush (struct usb_ep *ep)
struct usb_gadget;
+/**
+ * struct usb_endpoint_config - possible configurations of a given endpoint
+ * @config: the configuration number
+ * @interface: the interface number
+ * @altinterface: the altinterface number
+ *
+ * Used as an array to pass information about the possible configurations
+ * of a given endpoint to the bus controller.
+ */
+struct usb_endpoint_config {
+ int config;
+ int interface;
+ int altinterface;
+};
+
/* the rest of the api to the controller hardware: device operations,
* which don't involve endpoints (or i/o).
*/
struct usb_gadget_ops {
+ struct usb_ep* (*ep_alloc)(struct usb_gadget *gadget,
+ struct usb_endpoint_descriptor *desc,
+ int config, int interface, int alt);
int (*get_frame)(struct usb_gadget *);
int (*wakeup)(struct usb_gadget *);
int (*set_selfpowered) (struct usb_gadget *, int is_selfpowered);
@@ -872,7 +890,8 @@ int usb_gadget_config_buf(const struct usb_config_descriptor *config,
/* utility wrapping a simple endpoint selection policy */
extern struct usb_ep *usb_ep_autoconfig (struct usb_gadget *,
- struct usb_endpoint_descriptor *) __devinit;
+ struct usb_endpoint_descriptor *,
+ int, int, int) __devinit;
extern void usb_ep_autoconfig_reset (struct usb_gadget *) __devinit;
^ permalink raw reply related [flat|nested] 22+ messages in thread
* RE: [PATCH] PXA27x UDC driver.
2007-06-28 10:36 [PATCH] PXA27x UDC driver Rodolfo Giometti
@ 2007-06-28 11:15 ` Li Yang-r58472
2007-06-28 14:12 ` Rodolfo Giometti
2007-06-29 17:04 ` Andy Isaacson
` (2 subsequent siblings)
3 siblings, 1 reply; 22+ messages in thread
From: Li Yang-r58472 @ 2007-06-28 11:15 UTC (permalink / raw)
To: Rodolfo Giometti, linux-arm-kernel; +Cc: linux-kernel, Andrew Morton
> -----Original Message-----
> From: linux-kernel-owner@vger.kernel.org
> [mailto:linux-kernel-owner@vger.kernel.org] On Behalf Of Rodolfo
Giometti
> Sent: Thursday, June 28, 2007 6:36 PM
> To: linux-arm-kernel@lists.arm.linux.org.uk
> Cc: linux-kernel@vger.kernel.org; Andrew Morton
> Subject: [PATCH] PXA27x UDC driver.
>
> Hello,
>
> attached you can find new version of my patch for PXA27x UDC driver
> support against kernel 2.6.22-rc3 (but it applies also ro rc6).
>
> I'd like to know what I have to do in order to prepare this patch for
> kernel inclusion. It's time PXA27x has its UDC driver into linux! :)
>
> Thanks for your suggestions,
You should probably also cc: linux-usb-devel@lists.sourceforge.net and
David Brownell for UDC matters.
- Leo
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH] PXA27x UDC driver.
2007-06-28 11:15 ` Li Yang-r58472
@ 2007-06-28 14:12 ` Rodolfo Giometti
2007-06-28 21:29 ` Andrew Morton
2007-06-28 21:53 ` David Brownell
0 siblings, 2 replies; 22+ messages in thread
From: Rodolfo Giometti @ 2007-06-28 14:12 UTC (permalink / raw)
To: David Brownell, linux-usb-devel
Cc: linux-arm-kernel, linux-kernel, Andrew Morton, Li Yang-r58472
[-- Attachment #1: Type: text/plain, Size: 782 bytes --]
On Thu, Jun 28, 2007 at 07:15:06PM +0800, Li Yang-r58472 wrote:
> You should probably also cc: linux-usb-devel@lists.sourceforge.net and
> David Brownell for UDC matters.
As suggest by Leo let me propose to you my new patch for PXA27x UDC
support.
Please, let me know what I have to do for kernel inclusion. :)
Thanks,
Rodolfo
P.S. Please, in the replay also add:
linux-arm-kernel@lists.arm.linux.org.uk, linux-kernel@vger.kernel.org
and Andrew Morton <akpm@linux-foundation.org> where I already sent the
patch.
--
GNU/Linux Solutions e-mail: giometti@enneenne.com
Linux Device Driver giometti@gnudd.com
Embedded Systems giometti@linux.it
UNIX programming phone: +39 349 2432127
[-- Attachment #2: pxa27x-udc-support.3 --]
[-- Type: text/plain, Size: 85568 bytes --]
diff --git a/arch/arm/mach-pxa/generic.c b/arch/arm/mach-pxa/generic.c
index 64b08b7..7f390fd 100644
--- a/arch/arm/mach-pxa/generic.c
+++ b/arch/arm/mach-pxa/generic.c
@@ -282,7 +282,11 @@ static struct resource pxa2xx_udc_resources[] = {
static u64 udc_dma_mask = ~(u32)0;
static struct platform_device udc_device = {
+#ifdef CONFIG_PXA27x
+ .name = "pxa27x-udc",
+#else
.name = "pxa2xx-udc",
+#endif
.id = -1,
.resource = pxa2xx_udc_resources,
.num_resources = ARRAY_SIZE(pxa2xx_udc_resources),
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index f771a7c..eccd2c7 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -132,6 +132,24 @@ config USB_PXA2XX
default USB_GADGET
select USB_GADGET_SELECTED
+config USB_GADGET_PXA27X
+ boolean "PXA 27x"
+ depends on ARCH_PXA && PXA27x
+ help
+ Intel's PXA 27x series XScale processors include an integrated
+ full speed USB 1.1 device controller.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "pxa2xx_udc" and force all
+ gadget drivers to also be dynamically linked.
+
+
+config USB_PXA27X
+ tristate
+ depends on USB_GADGET_PXA27X
+ default USB_GADGET
+ select USB_GADGET_SELECTED
+
# if there's only one gadget driver, using only two bulk endpoints,
# don't waste memory for the other endpoints
config USB_PXA2XX_SMALL
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 5db1939..640a5a8 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o
obj-$(CONFIG_USB_NET2280) += net2280.o
obj-$(CONFIG_USB_PXA2XX) += pxa2xx_udc.o
+obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o
obj-$(CONFIG_USB_GOKU) += goku_udc.o
obj-$(CONFIG_USB_OMAP) += omap_udc.o
obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index f28af06..e7d72ff 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -230,7 +230,8 @@ find_ep (struct usb_gadget *gadget, const char *name)
*/
struct usb_ep * __devinit usb_ep_autoconfig (
struct usb_gadget *gadget,
- struct usb_endpoint_descriptor *desc
+ struct usb_endpoint_descriptor *desc,
+ int config, int interface, int alt
)
{
struct usb_ep *ep;
@@ -238,6 +239,11 @@ struct usb_ep * __devinit usb_ep_autoconfig (
type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+ /* If have ep_alloc() function use it! */
+ if (gadget->ops->ep_alloc)
+ return gadget->ops->ep_alloc(gadget, desc,
+ config, interface, alt);
+
/* First, apply chip-specific "best usage" knowledge.
* This might make a good usb_gadget_ops hook ...
*/
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index 325bf7c..d4f5870 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -257,10 +257,6 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
#define DEV_CONFIG_CDC
#endif
-#ifdef CONFIG_USB_GADGET_PXA27X
-#define DEV_CONFIG_CDC
-#endif
-
#ifdef CONFIG_USB_GADGET_S3C2410
#define DEV_CONFIG_CDC
#endif
@@ -292,6 +288,10 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
#define DEV_CONFIG_SUBSET
#endif
+#ifdef CONFIG_USB_GADGET_PXA27X
+#define DEV_CONFIG_SUBSET
+#endif
+
#ifdef CONFIG_USB_GADGET_SH
#define DEV_CONFIG_SUBSET
#endif
@@ -1034,13 +1034,12 @@ static int alloc_requests (struct eth_dev *dev, unsigned n, gfp_t gfp_flags);
static int
set_ether_config (struct eth_dev *dev, gfp_t gfp_flags)
{
- int result = 0;
- struct usb_gadget *gadget = dev->gadget;
+ int result = 0;
#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
/* status endpoint used for RNDIS and (optionally) CDC */
if (!subset_active(dev) && dev->status_ep) {
- dev->status = ep_desc (gadget, &hs_status_desc,
+ dev->status = ep_desc (dev->gadget, &hs_status_desc,
&fs_status_desc);
dev->status_ep->driver_data = dev;
@@ -1053,10 +1052,10 @@ set_ether_config (struct eth_dev *dev, gfp_t gfp_flags)
}
#endif
- dev->in = ep_desc(gadget, &hs_source_desc, &fs_source_desc);
+ dev->in = ep_desc(dev->gadget, &hs_source_desc, &fs_source_desc);
dev->in_ep->driver_data = dev;
- dev->out = ep_desc(gadget, &hs_sink_desc, &fs_sink_desc);
+ dev->out = ep_desc(dev->gadget, &hs_sink_desc, &fs_sink_desc);
dev->out_ep->driver_data = dev;
/* With CDC, the host isn't allowed to use these two data
@@ -2311,6 +2310,9 @@ eth_bind (struct usb_gadget *gadget)
* non-CDC to be compatible with ARM Linux-2.4 "usb-eth".
*/
cdc = 0;
+ } else if (gadget_is_pxa27x(gadget)) {
+ /* hardware can't write zlps */
+ zlp = 0;
}
gcnum = usb_gadget_controller_number (gadget);
@@ -2377,7 +2379,22 @@ eth_bind (struct usb_gadget *gadget)
/* all we really need is bulk IN/OUT */
usb_ep_autoconfig_reset (gadget);
- in_ep = usb_ep_autoconfig (gadget, &fs_source_desc);
+#ifdef CONFIG_USB_ETH_RNDIS
+ in_ep = usb_ep_autoconfig (gadget, &fs_source_desc,
+ DEV_RNDIS_CONFIG_VALUE,
+ (int)rndis_data_intf.bInterfaceNumber,
+ (int)rndis_data_intf.bAlternateSetting);
+#elif defined(DEV_CONFIG_CDC)
+ in_ep = usb_ep_autoconfig (gadget, &fs_source_desc,
+ DEV_CONFIG_VALUE,
+ (int)data_intf.bInterfaceNumber,
+ (int)data_intf.bAlternateSetting);
+#elif defined(DEV_CONFIG_SUBSET)
+ in_ep = usb_ep_autoconfig (gadget, &fs_source_desc,
+ DEV_CONFIG_VALUE,
+ (int)subset_data_intf.bInterfaceNumber,
+ (int)subset_data_intf.bAlternateSetting);
+#endif /* CONFIG_USB_ETH_RNDIS */
if (!in_ep) {
autoconf_fail:
dev_err (&gadget->dev,
@@ -2386,8 +2403,24 @@ autoconf_fail:
return -ENODEV;
}
in_ep->driver_data = in_ep; /* claim */
+
+#ifdef CONFIG_USB_ETH_RNDIS
+ out_ep = usb_ep_autoconfig (gadget, &fs_sink_desc,
+ DEV_RNDIS_CONFIG_VALUE,
+ (int)rndis_data_intf.bInterfaceNumber,
+ (int)rndis_data_intf.bAlternateSetting);
+#elif defined(DEV_CONFIG_CDC)
+ out_ep = usb_ep_autoconfig (gadget, &fs_sink_desc,
+ DEV_CONFIG_VALUE,
+ (int)data_intf.bInterfaceNumber,
+ (int)data_intf.bAlternateSetting);
+#elif defined(DEV_CONFIG_SUBSET)
+ out_ep = usb_ep_autoconfig (gadget, &fs_sink_desc,
+ DEV_CONFIG_VALUE,
+ (int)subset_data_intf.bInterfaceNumber,
+ (int)subset_data_intf.bAlternateSetting);
+#endif /* CONFIG_USB_ETH_RNDIS */
- out_ep = usb_ep_autoconfig (gadget, &fs_sink_desc);
if (!out_ep)
goto autoconf_fail;
out_ep->driver_data = out_ep; /* claim */
@@ -2397,7 +2430,18 @@ autoconf_fail:
* Since some hosts expect one, try to allocate one anyway.
*/
if (cdc || rndis) {
- status_ep = usb_ep_autoconfig (gadget, &fs_status_desc);
+#ifdef CONFIG_USB_ETH_RNDIS
+ status_ep = usb_ep_autoconfig (gadget, &fs_status_desc,
+ DEV_RNDIS_CONFIG_VALUE,
+ (int)rndis_control_intf.bInterfaceNumber,
+ (int)rndis_control_intf.bAlternateSetting);
+#elif defined(DEV_CONFIG_CDC)
+ status_ep = usb_ep_autoconfig (gadget, &fs_status_desc,
+ DEV_CONFIG_VALUE,
+ (int)control_intf.bInterfaceNumber,
+ (int)control_intf.bAlternateSetting);
+#endif /* CONFIG_USB_ETH_RNDIS */
+
if (status_ep) {
status_ep->driver_data = status_ep; /* claim */
} else if (rndis) {
@@ -2405,13 +2449,14 @@ autoconf_fail:
"can't run RNDIS on %s\n",
gadget->name);
return -ENODEV;
+ }
#ifdef DEV_CONFIG_CDC
- /* pxa25x only does CDC subset; often used with RNDIS */
- } else if (cdc) {
+ /* pxa25x only does CDC subset; often used with RNDIS */
+ else if (cdc) {
control_intf.bNumEndpoints = 0;
/* FIXME remove endpoint from descriptor list */
-#endif
}
+#endif
}
#endif
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
index c6b6479..6af5fdd 100644
--- a/drivers/usb/gadget/file_storage.c
+++ b/drivers/usb/gadget/file_storage.c
@@ -3920,20 +3920,20 @@ static int __init fsg_bind(struct usb_gadget *gadget)
/* Find all the endpoints we will use */
usb_ep_autoconfig_reset(gadget);
- ep = usb_ep_autoconfig(gadget, &fs_bulk_in_desc);
+ ep = usb_ep_autoconfig(gadget, &fs_bulk_in_desc, 0, 0, 0);
if (!ep)
goto autoconf_fail;
ep->driver_data = fsg; // claim the endpoint
fsg->bulk_in = ep;
- ep = usb_ep_autoconfig(gadget, &fs_bulk_out_desc);
+ ep = usb_ep_autoconfig(gadget, &fs_bulk_out_desc, 0, 0, 0);
if (!ep)
goto autoconf_fail;
ep->driver_data = fsg; // claim the endpoint
fsg->bulk_out = ep;
if (transport_is_cbi()) {
- ep = usb_ep_autoconfig(gadget, &fs_intr_in_desc);
+ ep = usb_ep_autoconfig(gadget, &fs_intr_in_desc, 0, 0, 0);
if (!ep)
goto autoconf_fail;
ep->driver_data = fsg; // claim the endpoint
diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c
index d08a8d0..d51feb2 100644
--- a/drivers/usb/gadget/gmidi.c
+++ b/drivers/usb/gadget/gmidi.c
@@ -1204,7 +1204,7 @@ static int __devinit gmidi_bind(struct usb_gadget *gadget)
* but there may also be important quirks to address.
*/
usb_ep_autoconfig_reset(gadget);
- in_ep = usb_ep_autoconfig(gadget, &bulk_in_desc);
+ in_ep = usb_ep_autoconfig(gadget, &bulk_in_desc, 0, 0, 0);
if (!in_ep) {
autoconf_fail:
printk(KERN_ERR "%s: can't autoconfigure on %s\n",
@@ -1214,7 +1214,7 @@ autoconf_fail:
EP_IN_NAME = in_ep->name;
in_ep->driver_data = in_ep; /* claim */
- out_ep = usb_ep_autoconfig(gadget, &bulk_out_desc);
+ out_ep = usb_ep_autoconfig(gadget, &bulk_out_desc, 0, 0, 0);
if (!out_ep) {
goto autoconf_fail;
}
diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c
new file mode 100644
index 0000000..affca46
--- /dev/null
+++ b/drivers/usb/gadget/pxa27x_udc.c
@@ -0,0 +1,2395 @@
+/*
+ * linux/drivers/usb/gadget/pxa27x_udc.c
+ * Intel PXA2xx 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) 2004 Intel Corporation
+ * Copyright (C) 2007 Rodolfo Giometti <giometti@linux.it>
+ *
+ * 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
+ *
+ */
+
+#undef DEBUG
+/* #define VERBOSE DBG_VERBOSE */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+
+#include <asm/byteorder.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/mach-types.h>
+#include <asm/unaligned.h>
+#include <asm/hardware.h>
+#include <asm/arch/pxa-regs.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb_gadget.h>
+
+#include <asm/arch/udc.h>
+
+/*
+ * This driver handles the USB Device Controller (UDC) in Intel's PXA 27x
+ * series processors.
+ * 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.
+ */
+
+#define DRIVER_VERSION "28-Jun-2007"
+#define DRIVER_DESC "PXA 27x USB Device Controller driver"
+
+static const char driver_name[] = "pxa27x_udc";
+
+static const char ep0name[] = "ep0";
+
+#undef USE_DMA
+#undef DISABLE_TEST_MODE
+
+#ifdef CONFIG_PROC_FS
+#define UDC_PROC_FILE
+#endif
+
+#include "pxa27x_udc.h"
+
+#ifdef CONFIG_EMBEDDED
+/* few strings, and little code to use them */
+#undef DEBUG
+#undef UDC_PROC_FILE
+#endif
+
+#ifdef USE_DMA
+static int use_dma = 1;
+module_param(use_dma, bool, 0);
+MODULE_PARM_DESC(use_dma, "true to use dma");
+
+static void dma_nodesc_handler(int dmach, void *_ep);
+static void kick_dma(struct pxa27x_ep *ep, struct pxa27x_request *req);
+
+#define DMASTR " (dma support)"
+
+#else /* !USE_DMA */
+#define DMASTR " (pio only)"
+#endif
+
+#ifdef CONFIG_USB_PXA27X_SMALL
+#define SIZE_STR " (small)"
+#else
+#define SIZE_STR ""
+#endif
+
+#ifdef DISABLE_TEST_MODE
+/* (mode == 0) == no undocumented chip tweaks
+ * (mode & 1) == double buffer bulk IN
+ * (mode & 2) == double buffer bulk OUT
+ * ... so mode = 3 (or 7, 15, etc) does it for both
+ */
+static ushort fifo_mode = 0;
+module_param(fifo_mode, ushort, 0);
+MODULE_PARM_DESC(fifo_mode, "pxa27x udc fifo mode");
+#endif
+
+#define UDCISR0_IR0 0x3
+#define UDCISR_INT_MASK (UDC_INT_FIFOERROR | UDC_INT_PACKETCMP)
+#define UDCICR_INT_MASK UDCISR_INT_MASK
+
+#define UDCCSR_MASK (UDCCSR_FST | UDCCSR_DME)
+/* ---------------------------------------------------------------------------
+ * endpoint related parts of the api to the usb controller hardware,
+ * used by gadget driver; and the inner talker-to-hardware core.
+ * ---------------------------------------------------------------------------
+ */
+
+static void pxa27x_ep_fifo_flush(struct usb_ep *ep);
+static void nuke(struct pxa27x_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->gpio_pullup)
+ udc_gpio_set(mach->gpio_pullup, 0);
+ else 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->gpio_pullup)
+ udc_gpio_set(mach->gpio_pullup, 1);
+ else if (mach->udc_command)
+ mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
+}
+
+static void pio_irq_enable(int ep_num)
+{
+ if (ep_num < 16)
+ UDCICR0 |= 3 << (ep_num * 2);
+ else {
+ ep_num -= 16;
+ UDCICR1 |= 3 << (ep_num * 2);
+ }
+}
+
+static void pio_irq_disable(int ep_num)
+{
+ ep_num &= 0xf;
+ if (ep_num < 16)
+ UDCICR0 &= ~(3 << (ep_num * 2));
+ else {
+ ep_num -= 16;
+ UDCICR1 &= ~(3 << (ep_num * 2));
+ }
+}
+
+/* The UDCCR reg contains mask and interrupt status bits,
+ * so using '|=' isn't safe as it may ack an interrupt.
+ */
+#define UDCCR_MASK_BITS (UDCCR_OEN | UDCCR_UDE)
+
+static inline void udc_set_mask_UDCCR(int mask)
+{
+ UDCCR = (UDCCR & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS);
+}
+
+static inline void udc_clear_mask_UDCCR(int mask)
+{
+ UDCCR = (UDCCR & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS);
+}
+
+static inline void udc_ack_int_UDCCR(int mask)
+{
+ /* udccr contains the bits we dont want to change */
+ __u32 udccr = UDCCR & UDCCR_MASK_BITS;
+
+ UDCCR = udccr | (mask & ~UDCCR_MASK_BITS);
+}
+
+/*
+ * endpoint enable/disable
+ *
+ * we need to verify the descriptors used to enable endpoints. since pxa27x
+ * endpoint configurations are fixed, and are pretty much always enabled,
+ * there's not a lot to manage here.
+ *
+ * because pxa27x 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 pxa27x_ep_enable(struct usb_ep *_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct pxa27x_ep *ep;
+ struct pxa27x_udc *dev;
+
+ ep = container_of(_ep, struct pxa27x_ep, ep);
+ if (!_ep || !desc || _ep->name == ep0name
+ || desc->bDescriptorType != USB_DT_ENDPOINT
+ || ep->fifo_size < le16_to_cpu(desc->wMaxPacketSize)) {
+ DMSG("%s, bad ep or descriptor\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ /* xfer types must match, except that interrupt ~= bulk */
+ if (ep->ep_type != USB_ENDPOINT_XFER_BULK
+ && desc->bmAttributes != USB_ENDPOINT_XFER_INT) {
+ DMSG("%s, %s type mismatch\n", __FUNCTION__, _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) {
+ DMSG("%s, bad %s maxpacket\n", __FUNCTION__, _ep->name);
+ return -ERANGE;
+ }
+
+ dev = ep->dev;
+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
+ DMSG("%s, bogus device state\n", __FUNCTION__);
+ return -ESHUTDOWN;
+ }
+
+ ep->desc = desc;
+ ep->dma = -1;
+ ep->stopped = 0;
+ ep->pio_irqs = ep->dma_irqs = 0;
+ ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+
+ /* flush fifo (mostly for OUT buffers) */
+ pxa27x_ep_fifo_flush(_ep);
+
+ /* ... reset halt state too, if we could ... */
+
+#ifdef USE_DMA
+ /* for (some) bulk and ISO endpoints, try to get a DMA channel and
+ * bind it to the endpoint. otherwise use PIO.
+ */
+ DMSG("%s: called attributes=%d\n", __FUNCTION__, ep->ep_type);
+ switch (ep->ep_type) {
+ case USB_ENDPOINT_XFER_ISOC:
+ if (le16_to_cpu(desc->wMaxPacketSize) % 32)
+ break;
+ /* fall through */
+ case USB_ENDPOINT_XFER_BULK:
+ if (!use_dma || !ep->reg_drcmr)
+ break;
+ ep->dma = pxa_request_dma((char *)_ep->name,
+ (le16_to_cpu(desc->wMaxPacketSize) > 64)
+ ? DMA_PRIO_MEDIUM /* some iso */
+ : DMA_PRIO_LOW,
+ dma_nodesc_handler, ep);
+ if (ep->dma >= 0) {
+ *ep->reg_drcmr = DRCMR_MAPVLD | ep->dma;
+ DMSG("%s using dma%d\n", _ep->name, ep->dma);
+ }
+ default:
+ break;
+ }
+#endif
+ DBG(DBG_VERBOSE, "enabled %s\n", _ep->name);
+ return 0;
+}
+
+static int pxa27x_ep_disable(struct usb_ep *_ep)
+{
+ struct pxa27x_ep *ep;
+
+ ep = container_of(_ep, struct pxa27x_ep, ep);
+ if (!_ep || !ep->desc) {
+ DMSG("%s, %s not enabled\n", __FUNCTION__,
+ _ep ? ep->ep.name : NULL);
+ return -EINVAL;
+ }
+ nuke(ep, -ESHUTDOWN);
+
+#ifdef USE_DMA
+ if (ep->dma >= 0) {
+ *ep->reg_drcmr = 0;
+ pxa_free_dma(ep->dma);
+ ep->dma = -1;
+ }
+#endif
+
+ /* flush fifo (mostly for IN buffers) */
+ pxa27x_ep_fifo_flush(_ep);
+
+ ep->desc = 0;
+ ep->stopped = 1;
+
+ DBG(DBG_VERBOSE, "%s disabled\n", _ep->name);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* for the pxa27x, 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).
+ */
+
+/*
+ * pxa27x_ep_alloc_request - allocate a request data structure
+ */
+static struct usb_request *pxa27x_ep_alloc_request(struct usb_ep *_ep,
+ unsigned int gfp_flags)
+{
+ struct pxa27x_request *req;
+
+ req = kmalloc(sizeof *req, gfp_flags);
+ if (!req)
+ return 0;
+
+ memset(req, 0, sizeof *req);
+ INIT_LIST_HEAD(&req->queue);
+ return &req->req;
+}
+
+/*
+ * pxa27x_ep_free_request - deallocate a request data structure
+ */
+static void pxa27x_ep_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct pxa27x_request *req;
+
+ req = container_of(_req, struct pxa27x_request, req);
+ WARN_ON(!list_empty(&req->queue));
+ kfree(req);
+}
+
+/* PXA cache needs flushing with DMA I/O (it's dma-incoherent), but there's
+ * no device-affinity and the heap works perfectly well for i/o buffers.
+ * It wastes much less memory than dma_alloc_coherent() would, and even
+ * prevents cacheline (32 bytes wide) sharing problems.
+ */
+static void *pxa27x_ep_alloc_buffer(struct usb_ep *_ep, unsigned bytes,
+ dma_addr_t * dma, unsigned int gfp_flags)
+{
+ char *retval;
+
+ retval = kmalloc(bytes, gfp_flags & ~(__GFP_DMA | __GFP_HIGHMEM));
+ if (retval)
+#ifdef USE_DMA
+ *dma = virt_to_bus(retval);
+#else
+ *dma = (dma_addr_t) ~0;
+#endif
+ return retval;
+}
+
+static void
+pxa27x_ep_free_buffer(struct usb_ep *_ep, void *buf, dma_addr_t dma,
+ unsigned bytes)
+{
+ kfree(buf);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * done - retire a request; caller blocked irqs
+ */
+static void done(struct pxa27x_ep *ep, struct pxa27x_request *req, int status)
+{
+ list_del_init(&req->queue);
+ if (likely(req->req.status == -EINPROGRESS))
+ req->req.status = status;
+ else
+ status = req->req.status;
+
+ if (status && status != -ESHUTDOWN)
+ DBG(DBG_VERBOSE, "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 */
+ req->req.complete(&ep->ep, &req->req);
+}
+
+static inline void ep0_idle(struct pxa27x_udc *dev)
+{
+ dev->ep0state = EP0_IDLE;
+ LED_EP0_OFF;
+}
+
+static int
+write_packet(volatile u32 * uddr, struct pxa27x_request *req, unsigned max)
+{
+ u32 *buf;
+ int length, count, remain;
+
+ buf = (u32 *) (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;
+
+ remain = length & 0x3;
+ count = length & ~(0x3);
+
+ while (likely(count)) {
+ *uddr = *buf++;
+ count -= 4;
+ }
+
+ if (remain) {
+ volatile u8 *reg = (u8 *) uddr;
+ char *rd = (u8 *) buf;
+
+ while (remain--) {
+ *reg = *rd++;
+ }
+ }
+
+ 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 pxa27x_ep *ep, struct pxa27x_request *req)
+{
+ unsigned max;
+
+ max = le16_to_cpu(ep->desc->wMaxPacketSize);
+ do {
+ int count;
+ int is_last, is_short;
+
+ count = write_packet(ep->reg_udcdr, 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);
+ }
+
+ DMSG("wrote %s count:%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->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.
+ */
+ *ep->reg_udccsr = UDCCSR_PC;
+ if (is_short)
+ *ep->reg_udccsr = UDCCSR_SP;
+
+ /* requests complete when all IN data is in the FIFO */
+ if (is_last) {
+ done(ep, req, 0);
+ if (list_empty(&ep->queue) || unlikely(ep->dma >= 0)) {
+ pio_irq_disable(ep->ep_num);
+#ifdef USE_DMA
+ /* unaligned data and zlps couldn't use dma */
+ if (unlikely(!list_empty(&ep->queue))) {
+ req = list_entry(ep->queue.next,
+ struct pxa27x_request,
+ queue);
+ kick_dma(ep, req);
+ return 0;
+ }
+#endif
+ }
+ 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 (*ep->reg_udccsr & UDCCSR_FS);
+ 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 pxa27x_udc *dev, u32 flags, const char *tag)
+{
+ UDCCSR0 = flags | UDCCSR0_SA | UDCCSR0_OPC;
+ UDCISR0 = UDCICR_INT(0, UDC_INT_FIFOERROR | UDC_INT_PACKETCMP);
+ dev->req_pending = 0;
+ DBG(DBG_VERY_NOISY, "%s %s, %02x/%02x\n",
+ __FUNCTION__, tag, UDCCSR0, flags);
+}
+
+static int write_ep0_fifo(struct pxa27x_ep *ep, struct pxa27x_request *req)
+{
+ unsigned count;
+ int is_short;
+
+ count = write_packet(&UDCDR0, req, EP0_FIFO_SIZE);
+ ep->dev->stats.write.bytes += count;
+
+ /* last packet "must be" short (or a zlp) */
+ is_short = (count != EP0_FIFO_SIZE);
+
+ DBG(DBG_VERY_NOISY, "ep0in %d bytes %d left %p\n", count,
+ req->req.length - req->req.actual, &req->req);
+
+ if (unlikely(is_short)) {
+ if (ep->dev->req_pending)
+ ep0start(ep->dev, UDCCSR0_IPR, "short IN");
+ else
+ UDCCSR0 = UDCCSR0_IPR;
+
+ count = req->req.length;
+ done(ep, req, 0);
+ ep0_idle(ep->dev);
+#if 0
+ /* 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 ((UDCCSR0 & UDCCSR0_OPC) != 0) {
+ /* clear OPC, generate ack */
+ UDCCSR0 = UDCCSR0_OPC;
+ break;
+ }
+ count--;
+ udelay(1);
+ } while (count);
+ }
+#endif
+ } 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 pxa27x_ep *ep, struct pxa27x_request *req)
+{
+ for (;;) {
+ u32 *buf;
+ int bufferspace, count, is_short;
+
+ /* make sure there's a packet in the FIFO. */
+ if (unlikely((*ep->reg_udccsr & UDCCSR_PC) == 0))
+ break;
+ buf = (u32 *) (req->req.buf + req->req.actual);
+ prefetchw(buf);
+ bufferspace = req->req.length - req->req.actual;
+
+ /* read all bytes from this packet */
+ if (likely(*ep->reg_udccsr & UDCCSR_BNE)) {
+ count = 0x3ff & *ep->reg_udcbcr;
+ req->req.actual += min(count, bufferspace);
+ } else /* zlp */
+ count = 0;
+
+ is_short = (count < ep->ep.maxpacket);
+ DMSG("read %s udccsr:%02x, count:%d bytes%s req %p %d/%d\n",
+ ep->ep.name, *ep->reg_udccsr, count,
+ is_short ? "/S" : "",
+ &req->req, req->req.actual, req->req.length);
+
+#if 0
+ dump_regs(ep->ep_num );
+#endif
+ count = min(count, bufferspace);
+ while (likely(count > 0)) {
+ *buf++ = *ep->reg_udcdr;
+ count -= 4;
+ }
+ DMSG("Buf:0x%p\n", req->req.buf);
+
+ *ep->reg_udccsr = UDCCSR_PC;
+ /* RPC/RSP/RNE could now reflect the other packet buffer */
+
+ /* completion */
+ if (is_short || req->req.actual == req->req.length) {
+ done(ep, req, 0);
+ if (list_empty(&ep->queue))
+ pio_irq_disable(ep->ep_num);
+ 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 pxa27x_ep *ep, struct pxa27x_request *req)
+{
+ u32 *buf, word;
+ unsigned bufferspace;
+
+ buf = (u32 *) (req->req.buf + req->req.actual);
+ bufferspace = req->req.length - req->req.actual;
+
+ while (UDCCSR0 & UDCCSR0_RNE) {
+ word = UDCDR0;
+
+ 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)
+ DMSG("%s overflow\n", ep->ep.name);
+ req->req.status = -EOVERFLOW;
+ } else {
+ *buf++ = word;
+ req->req.actual += 4;
+ bufferspace -= 4;
+ }
+ }
+
+ UDCCSR0 = UDCCSR0_OPC;
+
+ /* completion */
+ if (req->req.actual >= req->req.length)
+ return 1;
+
+ /* finished that packet. the next one may be waiting... */
+ return 0;
+}
+
+#ifdef USE_DMA
+
+#define MAX_IN_DMA ((DCMD_LENGTH + 1) - BULK_FIFO_SIZE)
+static void kick_dma(struct pxa27x_ep *ep, struct pxa27x_request *req)
+{
+ u32 dcmd = 0;
+ u32 len = req->req.length;
+ u32 buf = req->req.dma;
+ u32 fifo = io_v2p((u32) ep->reg_udcdr);
+
+ buf += req->req.actual;
+ len -= req->req.actual;
+ ep->dma_con = 0;
+
+ DMSG("%s: req:0x%p length:%d, actual:%d dma:%d\n",
+ __FUNCTION__, &req->req, req->req.length,
+ req->req.actual, ep->dma);
+
+ /* no-descriptor mode can be simple for bulk-in, iso-in, iso-out */
+ DCSR(ep->dma) = DCSR_NODESC;
+ if (buf & 0x3)
+ DALGN |= 1 << ep->dma;
+ else
+ DALGN &= ~(1 << ep->dma);
+
+ if (ep->dir_in) {
+ DSADR(ep->dma) = buf;
+ DTADR(ep->dma) = fifo;
+ if (len > MAX_IN_DMA) {
+ len = MAX_IN_DMA;
+ ep->dma_con = 1;
+ } else if (len >= ep->ep.maxpacket) {
+ if ((ep->dma_con = (len % ep->ep.maxpacket) != 0))
+ len = ep->ep.maxpacket;
+ }
+ dcmd = len | DCMD_BURST32 | DCMD_WIDTH4 | DCMD_ENDIRQEN
+ | DCMD_FLOWTRG | DCMD_INCSRCADDR;
+ } else {
+ DSADR(ep->dma) = fifo;
+ DTADR(ep->dma) = buf;
+ dcmd = len | DCMD_BURST32 | DCMD_WIDTH4 | DCMD_ENDIRQEN
+ | DCMD_FLOWSRC | DCMD_INCTRGADDR;
+ }
+ *ep->reg_udccsr = UDCCSR_DME;
+ DCMD(ep->dma) = dcmd;
+ DCSR(ep->dma) = DCSR_NODESC | DCSR_EORIRQEN
+ | ((ep->dir_in) ? DCSR_STOPIRQEN : 0);
+ *ep->reg_drcmr = ep->dma | DRCMR_MAPVLD;
+ DCSR(ep->dma) |= DCSR_RUN;
+}
+
+static void cancel_dma(struct pxa27x_ep *ep)
+{
+ struct pxa27x_request *req;
+ u32 tmp;
+
+ if (DCSR(ep->dma) == 0 || list_empty(&ep->queue))
+ return;
+
+ DMSG("hehe dma:%d,dcsr:0x%x\n", ep->dma, DCSR(ep->dma));
+ DCSR(ep->dma) = 0;
+ while ((DCSR(ep->dma) & DCSR_STOPSTATE) == 0)
+ cpu_relax();
+
+ req = list_entry(ep->queue.next, struct pxa27x_request, queue);
+ tmp = DCMD(ep->dma) & DCMD_LENGTH;
+ req->req.actual = req->req.length - tmp;
+
+ /* the last tx packet may be incomplete, so flush the fifo.
+ * FIXME correct req.actual if we can
+ */
+ *ep->reg_udccsr = UDCCSR_FEF;
+}
+
+static void dma_nodesc_handler(int dmach, void *_ep, struct pt_regs *r)
+{
+ struct pxa27x_ep *ep = _ep;
+ struct pxa27x_request *req, *req_next;
+ u32 dcsr, tmp, completed;
+
+ local_irq_disable();
+
+ req = list_entry(ep->queue.next, struct pxa27x_request, queue);
+
+ DMSG("%s, buf:0x%p\n", __FUNCTION__, req->req.buf);
+
+ ep->dma_irqs++;
+ ep->dev->stats.irqs++;
+ HEX_DISPLAY(ep->dev->stats.irqs);
+
+ completed = 0;
+
+ dcsr = DCSR(dmach);
+ DCSR(ep->dma) &= ~DCSR_RUN;
+
+ if (dcsr & DCSR_BUSERR) {
+ DCSR(dmach) = DCSR_BUSERR;
+ printk(KERN_ERR " Buss Error\n");
+ req->req.status = -EIO;
+ completed = 1;
+ } else if (dcsr & DCSR_ENDINTR) {
+ DCSR(dmach) = DCSR_ENDINTR;
+ if (ep->dir_in) {
+ tmp = req->req.length - req->req.actual;
+ /* Last packet is a short one */
+ if (tmp < ep->ep.maxpacket) {
+ int count = 0;
+
+ *ep->reg_udccsr = UDCCSR_SP |
+ (*ep->reg_udccsr & UDCCSR_MASK);
+ /*Wait for packet out */
+ while ((count++ < 10000) &&
+ !(*ep->reg_udccsr & UDCCSR_FS)) ;
+ if (count >= 10000)
+ DMSG("Failed to send packet\n");
+ else
+ DMSG("%s: short packet sent len:%d,"
+ "length:%d,actual:%d\n",
+ __FUNCTION__, tmp, req->req.length,
+ req->req.actual);
+ req->req.actual = req->req.length;
+ completed = 1;
+ /* There are still packets to transfer */
+ } else if (ep->dma_con) {
+ DMSG("%s: more packets,length:%d,actual:%d\n",
+ __FUNCTION__, req->req.length,
+ req->req.actual);
+ req->req.actual += ep->ep.maxpacket;
+ completed = 0;
+ } else {
+ DMSG("%s: no more packets,length:%d,"
+ "actual:%d\n", __FUNCTION__,
+ req->req.length, req->req.actual);
+ req->req.actual = req->req.length;
+ completed = 1;
+ }
+ } else {
+ req->req.actual = req->req.length;
+ completed = 1;
+ }
+ } else if (dcsr & DCSR_EORINTR) { /* Only happened in OUT DMA */
+ int remain, udccsr;
+
+ DCSR(dmach) = DCSR_EORINTR;
+ remain = DCMD(dmach) & DCMD_LENGTH;
+ req->req.actual = req->req.length - remain;
+
+ udccsr = *ep->reg_udccsr;
+ if (udccsr & UDCCSR_SP) {
+ *ep->reg_udccsr = UDCCSR_PC | (udccsr & UDCCSR_MASK);
+ completed = 1;
+ }
+ DMSG("%s: length:%d actual:%d\n",
+ __FUNCTION__, req->req.length, req->req.actual);
+ } else
+ DMSG("%s: Others dma:%d DCSR:0x%x DCMD:0x%x\n",
+ __FUNCTION__, dmach, DCSR(dmach), DCMD(dmach));
+
+ if (likely(completed)) {
+ if (req->queue.next != &ep->queue) {
+ req_next = list_entry(req->queue.next,
+ struct pxa27x_request, queue);
+ kick_dma(ep, req_next);
+ }
+ done(ep, req, 0);
+ } else {
+ kick_dma(ep, req);
+ }
+
+ local_irq_enable();
+}
+
+#endif
+/*-------------------------------------------------------------------------*/
+
+static int
+pxa27x_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
+ unsigned int gfp_flags)
+{
+ struct pxa27x_ep *ep;
+ struct pxa27x_request *req;
+ struct pxa27x_udc *dev;
+ unsigned long flags;
+
+ req = container_of(_req, struct pxa27x_request, req);
+ if (unlikely(!_req || !_req->complete || !_req->buf ||
+ !list_empty(&req->queue))) {
+ DMSG("%s, bad params\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ ep = container_of(_ep, struct pxa27x_ep, ep);
+ if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) {
+ DMSG("%s, bad ep\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ DMSG("%s, ep point %d is queue\n", __FUNCTION__, ep->ep_num);
+
+ dev = ep->dev;
+ if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) {
+ DMSG("%s, bogus device state\n", __FUNCTION__);
+ 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->ep_type == USB_ENDPOINT_XFER_ISOC
+ && req->req.length > le16_to_cpu
+ (ep->desc->wMaxPacketSize)))
+ return -EMSGSIZE;
+
+#ifdef USE_DMA
+ /* FIXME: caller may already have done the dma mapping */
+ if (ep->dma >= 0) {
+ _req->dma = dma_map_single(dev->dev, _req->buf, _req->length,
+ (ep->
+ dir_in) ? DMA_TO_DEVICE :
+ DMA_FROM_DEVICE);
+ }
+#endif
+
+ DBG(DBG_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 == 0 /* ep0 */ ) {
+ unsigned length = _req->length;
+
+ switch (dev->ep0state) {
+ case EP0_IN_DATA_PHASE:
+ dev->stats.write.ops++;
+ if (write_ep0_fifo(ep, req))
+ req = 0;
+ break;
+
+ case EP0_OUT_DATA_PHASE:
+ dev->stats.read.ops++;
+ if (dev->req_pending)
+ ep0start(dev, UDCCSR0_IPR, "OUT");
+ if (length == 0 || ((UDCCSR0 & UDCCSR0_RNE) != 0
+ && read_ep0_fifo(ep,
+ req))) {
+ ep0_idle(dev);
+ done(ep, req, 0);
+ req = 0;
+ }
+ break;
+ case EP0_NO_ACTION:
+ ep0_idle(dev);
+ req = 0;
+ break;
+ default:
+ DMSG("ep0 i/o, odd state %d\n", dev->ep0state);
+ local_irq_restore(flags);
+ return -EL2HLT;
+ }
+#ifdef USE_DMA
+ /* either start dma or prime pio pump */
+ } else if (ep->dma >= 0) {
+ kick_dma(ep, req);
+#endif
+ /* can the FIFO can satisfy the request immediately? */
+ } else if (ep->dir_in
+ && (*ep->reg_udccsr & UDCCSR_FS) != 0
+ && write_fifo(ep, req)) {
+ req = 0;
+ } else if ((*ep->reg_udccsr & UDCCSR_FS) != 0
+ && read_fifo(ep, req)) {
+ req = 0;
+ }
+ DMSG("req:%p,ep->desc:%p,ep->dma:%d\n", req, ep->desc, ep->dma);
+ if (likely(req && ep->desc) && ep->dma < 0)
+ pio_irq_enable(ep->ep_num);
+ }
+
+ /* pio or dma irq handler advances the queue. */
+ if (likely(req != 0))
+ list_add_tail(&req->queue, &ep->queue);
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+/*
+ * nuke - dequeue ALL requests
+ */
+static void nuke(struct pxa27x_ep *ep, int status)
+{
+ struct pxa27x_request *req;
+
+ /* called with irqs blocked */
+#ifdef USE_DMA
+ if (ep->dma >= 0 && !ep->stopped)
+ cancel_dma(ep);
+#endif
+ while (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next, struct pxa27x_request, queue);
+ done(ep, req, status);
+ }
+ if (ep->desc)
+ pio_irq_disable(ep->ep_num);
+}
+
+/* dequeue JUST ONE request */
+static int pxa27x_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct pxa27x_ep *ep;
+ struct pxa27x_request *req;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct pxa27x_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;
+ }
+#ifdef USE_DMA
+ if (ep->dma >= 0 && ep->queue.next == &req->queue && !ep->stopped) {
+ cancel_dma(ep);
+ done(ep, req, -ECONNRESET);
+ /* restart i/o */
+ if (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next,
+ struct pxa27x_request, queue);
+ kick_dma(ep, req);
+ }
+ } else
+#endif
+ done(ep, req, -ECONNRESET);
+
+ local_irq_restore(flags);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int pxa27x_ep_set_halt(struct usb_ep *_ep, int value)
+{
+ struct pxa27x_ep *ep;
+ unsigned long flags;
+
+ DMSG("%s is called\n", __FUNCTION__);
+ ep = container_of(_ep, struct pxa27x_ep, ep);
+ if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))
+ || ep->ep_type == USB_ENDPOINT_XFER_ISOC) {
+ DMSG("%s, bad ep\n", __FUNCTION__);
+ 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.
+ */
+ DMSG("only host can clear %s halt\n", _ep->name);
+ return -EROFS;
+ }
+
+ local_irq_save(flags);
+
+ if (ep->dir_in && ((*ep->reg_udccsr & UDCCSR_FS) == 0
+ || !list_empty(&ep->queue))) {
+ local_irq_restore(flags);
+ return -EAGAIN;
+ }
+
+ /* FST bit is the same for control, bulk in, bulk out, interrupt in */
+ *ep->reg_udccsr = UDCCSR_FST | UDCCSR_FEF;
+
+ /* ep0 needs special care */
+ if (!ep->desc) {
+ start_watchdog(ep->dev);
+ ep->dev->req_pending = 0;
+ ep->dev->ep0state = EP0_STALL;
+ LED_EP0_OFF;
+
+ /* and bulk/intr endpoints like dropping stalls too */
+ } else {
+ unsigned i;
+ for (i = 0; i < 1000; i += 20) {
+ if (*ep->reg_udccsr & UDCCSR_SST)
+ break;
+ udelay(20);
+ }
+ }
+ local_irq_restore(flags);
+
+ DBG(DBG_VERBOSE, "%s halt\n", _ep->name);
+ return 0;
+}
+
+static int pxa27x_ep_fifo_status(struct usb_ep *_ep)
+{
+ struct pxa27x_ep *ep;
+
+ ep = container_of(_ep, struct pxa27x_ep, ep);
+ if (!_ep) {
+ DMSG("%s, bad ep\n", __FUNCTION__);
+ return -ENODEV;
+ }
+ /* pxa can't report unclaimed bytes from IN fifos */
+ if (ep->dir_in)
+ return -EOPNOTSUPP;
+ if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN
+ || (*ep->reg_udccsr & UDCCSR_FS) == 0)
+ return 0;
+ else
+ return (*ep->reg_udcbcr & 0xfff) + 1;
+}
+
+static void pxa27x_ep_fifo_flush(struct usb_ep *_ep)
+{
+ struct pxa27x_ep *ep;
+
+ ep = container_of(_ep, struct pxa27x_ep, ep);
+ if (!_ep || ep->ep.name == ep0name || !list_empty(&ep->queue)) {
+ DMSG("%s, bad ep\n", __FUNCTION__);
+ return;
+ }
+
+ /* toggle and halt bits stay unchanged */
+
+ /* for OUT, just read and discard the FIFO contents. */
+ if (!ep->dir_in) {
+ while (((*ep->reg_udccsr) & UDCCSR_BNE) != 0)
+ (void)*ep->reg_udcdr;
+ return;
+ }
+
+ /* most IN status is the same, but ISO can't stall */
+ *ep->reg_udccsr = UDCCSR_PC | UDCCSR_FST | UDCCSR_TRN
+ | (ep->ep_type == USB_ENDPOINT_XFER_ISOC)
+ ? 0 : UDCCSR_SST;
+}
+
+static struct usb_ep_ops pxa27x_ep_ops = {
+ .enable = pxa27x_ep_enable,
+ .disable = pxa27x_ep_disable,
+
+ .alloc_request = pxa27x_ep_alloc_request,
+ .free_request = pxa27x_ep_free_request,
+
+ .alloc_buffer = pxa27x_ep_alloc_buffer,
+ .free_buffer = pxa27x_ep_free_buffer,
+
+ .queue = pxa27x_ep_queue,
+ .dequeue = pxa27x_ep_dequeue,
+
+ .set_halt = pxa27x_ep_set_halt,
+ .fifo_status = pxa27x_ep_fifo_status,
+ .fifo_flush = pxa27x_ep_fifo_flush,
+};
+
+/* ---------------------------------------------------------------------------
+ * device-scoped parts of the api to the usb controller hardware
+ * ---------------------------------------------------------------------------
+ */
+
+static inline void validate_fifo_size(struct pxa27x_ep *pxa_ep, u8 bmAttributes)
+{
+ switch (bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+ case USB_ENDPOINT_XFER_CONTROL:
+ pxa_ep->fifo_size = EP0_FIFO_SIZE;
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ pxa_ep->fifo_size = ISO_FIFO_SIZE;
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ pxa_ep->fifo_size = BULK_FIFO_SIZE;
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ pxa_ep->fifo_size = INT_FIFO_SIZE;
+ break;
+ default:
+ break;
+ }
+}
+
+#define NAME_SIZE 18
+struct usb_ep *pxa27x_ep_alloc(struct usb_gadget *gadget,
+ struct usb_endpoint_descriptor *desc, int config,
+ int interface, int alt)
+{
+ u32 tmp;
+ unsigned i;
+ char *name;
+ struct usb_ep *ep = NULL;
+ struct pxa27x_ep *pxa_ep = NULL;
+ struct pxa27x_udc *dev = the_controller;
+
+ DMSG("pxa27x_config_ep is called\n");
+ DMSG(" usb endpoint descriptor is:\n"
+ " bLength:%d\n"
+ " bDescriptorType:%x\n"
+ " bEndpointAddress:%x\n"
+ " bmAttributes:%x\n"
+ " wMaxPacketSize:%d\n",
+ desc->bLength,
+ desc->bDescriptorType, desc->bEndpointAddress,
+ desc->bmAttributes, desc->wMaxPacketSize);
+
+ for (i = 1; i < UDC_EP_NUM; i++) {
+ if (!dev->ep[i].assigned) {
+ pxa_ep = &dev->ep[i];
+ pxa_ep->assigned = 1;
+ pxa_ep->ep_num = i;
+ break;
+ }
+ }
+ if (unlikely(i == UDC_EP_NUM)) {
+ printk(KERN_ERR __FILE__ ": Failed to find a spare endpoint\n");
+ return ep;
+ }
+
+ ep = &pxa_ep->ep;
+
+ pxa_ep->dev = dev;
+ pxa_ep->desc = desc;
+ pxa_ep->pio_irqs = pxa_ep->dma_irqs = 0;
+ pxa_ep->dma = -1;
+
+ if (!(desc->bEndpointAddress & 0xF))
+ desc->bEndpointAddress |= i;
+
+ if (!(desc->wMaxPacketSize)) {
+ validate_fifo_size(pxa_ep, desc->bmAttributes);
+ desc->wMaxPacketSize = pxa_ep->fifo_size;
+ } else
+ pxa_ep->fifo_size = desc->wMaxPacketSize;
+
+ pxa_ep->dir_in = (desc->bEndpointAddress & USB_DIR_IN) ? 1 : 0;
+ pxa_ep->ep_type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+ pxa_ep->stopped = 1;
+ pxa_ep->dma_con = 0;
+ pxa_ep->config = config;
+ pxa_ep->interface = interface;
+ pxa_ep->aisn = alt;
+
+ pxa_ep->reg_udccsr = &UDCCSR0 + i;
+ pxa_ep->reg_udcbcr = &UDCBCR0 + i;
+ pxa_ep->reg_udcdr = &UDCDR0 + i;
+ pxa_ep->reg_udccr = &UDCCRA - 1 + i;
+#ifdef USE_DMA
+ pxa_ep->reg_drcmr = &DRCMR24 + i;
+#endif
+
+ DMSG("udccsr=0x%8x, udcbcr=0x%8x, udcdr=0x%8x,"
+ "udccr0=0x%8x\n",
+ (unsigned)pxa_ep->reg_udccsr,
+ (unsigned)pxa_ep->reg_udcbcr,
+ (unsigned)pxa_ep->reg_udcdr, (unsigned)pxa_ep->reg_udccr);
+
+ /* Configure UDCCR */
+ tmp = 0;
+ tmp |= (pxa_ep->config << UDCCONR_CN_S) & UDCCONR_CN;
+#if 0
+ tmp |= (pxa_ep->interface << UDCCONR_IN_S) & UDCCONR_IN;
+ tmp |= (pxa_ep->aisn << UDCCONR_AISN_S) & UDCCONR_AISN;
+#else
+ tmp |= (0 << UDCCONR_IN_S) & UDCCONR_IN;
+ tmp |= (0 << UDCCONR_AISN_S) & UDCCONR_AISN;
+#endif
+ tmp |= (desc->bEndpointAddress << UDCCONR_EN_S) & UDCCONR_EN;
+ tmp |= (pxa_ep->ep_type << UDCCONR_ET_S) & UDCCONR_ET;
+ tmp |= (pxa_ep->dir_in) ? UDCCONR_ED : 0;
+ tmp |= (min(pxa_ep->fifo_size, (unsigned)desc->wMaxPacketSize)
+ << UDCCONR_MPS_S) & UDCCONR_MPS;
+ tmp |= UDCCONR_DE | UDCCONR_EE;
+#if 0
+ tmp |= UDCCONR_EE;
+#endif
+
+ *pxa_ep->reg_udccr = tmp;
+
+#ifdef USE_DMA
+ /* Only BULK use DMA */
+ if ((pxa_ep->ep_type & USB_ENDPOINT_XFERTYPE_MASK)
+ == USB_ENDPOINT_XFER_BULK)
+ *pxa_ep->reg_udccsr = UDCCSR_DME;
+#endif
+
+ DMSG("UDCCR: 0x%p is 0x%x\n", pxa_ep->reg_udccr, *pxa_ep->reg_udccr);
+
+ /* Fill ep name */
+ name = kmalloc(NAME_SIZE, GFP_KERNEL);
+ if (!name) {
+ printk(KERN_ERR "%s: Error\n", __FUNCTION__);
+ return NULL;
+ }
+
+ switch (pxa_ep->ep_type) {
+ case USB_ENDPOINT_XFER_BULK:
+ sprintf(name, "Bulk-%s-%d", (pxa_ep->dir_in ? "in" : "out"), i);
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ sprintf(name, "Interrupt-%s-%d", (pxa_ep->dir_in ?
+ "in" : "out"), i);
+ break;
+ default:
+ sprintf(name, "endpoint-%s-%d", (pxa_ep->dir_in ?
+ "in" : "out"), i);
+ break;
+ }
+ ep->name = name;
+
+ ep->ops = &pxa27x_ep_ops;
+ ep->maxpacket = min((ushort) pxa_ep->fifo_size, desc->wMaxPacketSize);
+
+ list_add_tail(&ep->ep_list, &gadget->ep_list);
+ return ep;
+}
+
+static int pxa27x_udc_get_frame(struct usb_gadget *_gadget)
+{
+ return (UDCFNR & 0x3FF);
+}
+
+static int pxa27x_udc_wakeup(struct usb_gadget *_gadget)
+{
+ /* host may not have enabled remote wakeup */
+ if ((UDCCR & UDCCR_DWRE) == 0)
+ return -EHOSTUNREACH;
+ udc_set_mask_UDCCR(UDCCR_UDR);
+ return 0;
+}
+
+static const struct usb_gadget_ops pxa27x_udc_ops = {
+ .ep_alloc = pxa27x_ep_alloc,
+ .get_frame = pxa27x_udc_get_frame,
+ .wakeup = pxa27x_udc_wakeup,
+ /* current versions must always be self-powered */
+};
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef UDC_PROC_FILE
+
+static const char proc_node_name[] = "driver/udc";
+
+static int
+udc_proc_read(char *page, char **start, off_t off, int count,
+ int *eof, void *_dev)
+{
+ char *buf = page;
+ struct pxa27x_udc *dev = _dev;
+ char *next = buf;
+ unsigned size = count;
+ unsigned long flags;
+ int i, t;
+ u32 tmp;
+
+ if (off != 0)
+ return 0;
+
+ local_irq_save(flags);
+
+ /* basic device status */
+ t = scnprintf(next, size, DRIVER_DESC "\n"
+ "%s version: %s\nGadget driver: %s\n",
+ driver_name, DRIVER_VERSION SIZE_STR DMASTR,
+ dev->driver ? dev->driver->driver.name : "(none)");
+ size -= t;
+ next += t;
+
+ /* registers for device and ep0 */
+ t = scnprintf(next, size,
+ "uicr %02X.%02X, usir %02X.%02x, ufnr %02X\n",
+ UDCICR1, UDCICR0, UDCISR1, UDCISR0, UDCFNR);
+ size -= t;
+ next += t;
+
+ tmp = UDCCR;
+ t = scnprintf(next, size,
+ "udccr %02X =%s%s%s%s%s%s%s%s%s%s, con=%d,inter=%d,altinter=%d\n",
+ tmp, (tmp & UDCCR_OEN) ? " oen" : "",
+ (tmp & UDCCR_AALTHNP) ? " aalthnp" : "",
+ (tmp & UDCCR_AHNP) ? " rem" : "",
+ (tmp & UDCCR_BHNP) ? " rstir" : "",
+ (tmp & UDCCR_DWRE) ? " dwre" : "",
+ (tmp & UDCCR_SMAC) ? " smac" : "",
+ (tmp & UDCCR_EMCE) ? " emce" : "",
+ (tmp & UDCCR_UDR) ? " udr" : "",
+ (tmp & UDCCR_UDA) ? " uda" : "",
+ (tmp & UDCCR_UDE) ? " ude" : "",
+ (tmp & UDCCR_ACN) >> UDCCR_ACN_S,
+ (tmp & UDCCR_AIN) >> UDCCR_AIN_S,
+ (tmp & UDCCR_AAISN) >> UDCCR_AAISN_S);
+
+ size -= t;
+ next += t;
+
+ tmp = UDCCSR0;
+ t = scnprintf(next, size,
+ "udccsr0 %02X =%s%s%s%s%s%s%s\n", tmp,
+ (tmp & UDCCSR0_SA) ? " sa" : "",
+ (tmp & UDCCSR0_RNE) ? " rne" : "",
+ (tmp & UDCCSR0_FST) ? " fst" : "",
+ (tmp & UDCCSR0_SST) ? " sst" : "",
+ (tmp & UDCCSR0_DME) ? " dme" : "",
+ (tmp & UDCCSR0_IPR) ? " ipr" : "",
+ (tmp & UDCCSR0_OPC) ? " opc" : "");
+ size -= t;
+ next += t;
+
+ if (!dev->driver)
+ goto done;
+
+ t = scnprintf(next, size, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n",
+ dev->stats.write.bytes, dev->stats.write.ops,
+ dev->stats.read.bytes, dev->stats.read.ops,
+ dev->stats.irqs);
+ size -= t;
+ next += t;
+
+ /* dump endpoint queues */
+ for (i = 0; i < UDC_EP_NUM; i++) {
+ struct pxa27x_ep *ep = &dev->ep[i];
+ struct pxa27x_request *req;
+ int t;
+
+ if (i != 0) {
+ const struct usb_endpoint_descriptor *d;
+
+ d = ep->desc;
+ if (!d)
+ continue;
+ tmp = *dev->ep[i].reg_udccsr;
+ t = scnprintf(next, size,
+ "%s max %d %s udccs %02x udccr:0x%x\n",
+ ep->ep.name,
+ le16_to_cpu(d->wMaxPacketSize),
+ (ep->dma >= 0) ? "dma" : "pio", tmp,
+ *dev->ep[i].reg_udccr);
+ /* TODO translate all five groups of udccs bits! */
+
+ } else /* ep0 should only have one transfer queued */
+ t = scnprintf(next, size, "ep0 max 16 pio irqs %lu\n",
+ ep->pio_irqs);
+ if (t <= 0 || t > size)
+ goto done;
+ size -= t;
+ next += t;
+
+ if (list_empty(&ep->queue)) {
+ t = scnprintf(next, size, "\t(nothing queued)\n");
+ if (t <= 0 || t > size)
+ goto done;
+ size -= t;
+ next += t;
+ continue;
+ }
+ list_for_each_entry(req, &ep->queue, queue) {
+#ifdef USE_DMA
+ if (ep->dma >= 0 && req->queue.prev == &ep->queue)
+ t = scnprintf(next, size,
+ "\treq %p len %d/%d "
+ "buf %p (dma%d dcmd %08x)\n",
+ &req->req, req->req.actual,
+ req->req.length, req->req.buf,
+ ep->dma, DCMD(ep->dma)
+ /* low 13 bits == bytes-to-go */
+ );
+ else
+#endif
+ t = scnprintf(next, size,
+ "\treq %p len %d/%d buf %p\n",
+ &req->req, req->req.actual,
+ req->req.length, req->req.buf);
+ if (t <= 0 || t > size)
+ goto done;
+ size -= t;
+ next += t;
+ }
+ }
+
+ done:
+ local_irq_restore(flags);
+ *eof = 1;
+ return count - size;
+}
+
+#define create_proc_files() \
+ create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev)
+#define remove_proc_files() \
+ remove_proc_entry(proc_node_name, NULL)
+
+#else /* !UDC_PROC_FILE */
+#define create_proc_files() do {} while (0)
+#define remove_proc_files() do {} while (0)
+
+#endif /* UDC_PROC_FILE */
+
+/* "function" sysfs attribute */
+static ssize_t
+show_function(struct device *_dev, struct device_attribute *attr, char *buf)
+{
+ struct pxa27x_udc *dev = dev_get_drvdata(_dev);
+
+ if (!dev->driver
+ || !dev->driver->function
+ || strlen(dev->driver->function) > PAGE_SIZE)
+ return 0;
+ return scnprintf(buf, PAGE_SIZE, "%s\n", dev->driver->function);
+}
+
+static DEVICE_ATTR(function, S_IRUGO, show_function, NULL);
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * udc_disable - disable USB device controller
+ */
+static void udc_disable(struct pxa27x_udc *dev)
+{
+ UDCICR0 = 0x00000000;
+ UDCICR1 = 0x00000000;
+
+ udc_clear_mask_UDCCR(UDCCR_UDE);
+
+ /* Disable clock for USB device */
+ pxa_set_cken(CKEN_USB, 0);
+
+ ep0_idle(dev);
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+ pullup_off();
+}
+
+/*
+ * udc_reinit - initialize software state
+ */
+static void udc_reinit(struct pxa27x_udc *dev)
+{
+ u32 i;
+
+ dev->ep0state = EP0_IDLE;
+
+ /* basic endpoint records init */
+ for (i = 0; i < UDC_EP_NUM; i++) {
+ struct pxa27x_ep *ep = &dev->ep[i];
+
+ ep->stopped = 0;
+ ep->pio_irqs = ep->dma_irqs = 0;
+ }
+ dev->configuration = 0;
+ dev->interface = 0;
+ dev->alternate = 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 pxa27x_udc *dev)
+{
+ udc_clear_mask_UDCCR(UDCCR_UDE);
+
+ /* Enable clock for USB device */
+ pxa_set_cken(CKEN_USB, 1);
+
+ UDCICR0 = UDCICR1 = 0;
+
+ ep0_idle(dev);
+ dev->gadget.speed = USB_SPEED_FULL;
+ dev->stats.irqs = 0;
+
+ udc_set_mask_UDCCR(UDCCR_UDE);
+ udelay(2);
+ if (UDCCR & UDCCR_EMCE) {
+ printk(KERN_ERR
+ ": There are error in configuration, udc disabled\n");
+ }
+
+ /* caller must be able to sleep in order to cope
+ * with startup transients.
+ */
+ msleep(100);
+
+ /* enable suspend/resume and reset irqs */
+ UDCICR1 = UDCICR1_IECC | UDCICR1_IERU | UDCICR1_IESU | UDCICR1_IERS;
+
+ /* enable ep0 irqs */
+ UDCICR0 = UDCICR_INT(0, UDCICR_INT_MASK);
+#if 0
+ for (i = 1; i < UDC_EP_NUM; i++) {
+ if (dev->ep[i].assigned)
+ pio_irq_enable(i);
+ }
+#endif
+
+ pullup_on();
+}
+
+/* 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 pxa27x_udc *dev = the_controller;
+ int retval;
+
+ DMSG("dev=0x%x, driver=0x%x, speed=%d, "
+ "bind=0x%x, unbind=0x%x, disconnect=0x%x, setup=0x%x\n",
+ (unsigned)dev, (unsigned)driver, driver->speed,
+ (unsigned)driver->bind, (unsigned)driver->unbind,
+ (unsigned)driver->disconnect, (unsigned)driver->setup);
+
+ if (!driver || driver->speed != USB_SPEED_FULL
+ || !driver->bind
+ || !driver->unbind || !driver->disconnect || !driver->setup)
+ return -EINVAL;
+ if (!dev)
+ return -ENODEV;
+ if (dev->driver)
+ return -EBUSY;
+
+ /* first hook up the driver ... */
+ dev->driver = driver;
+ dev->gadget.dev.driver = &driver->driver;
+
+ retval = device_add(&dev->gadget.dev);
+ if (retval) {
+ DMSG("unable to add device for %s --> error %d\n",
+ driver->driver.name, retval);
+ goto device_add_error;
+ }
+ retval = driver->bind(&dev->gadget);
+ if (retval) {
+ DMSG("bind to driver %s --> error %d\n",
+ driver->driver.name, retval);
+ goto device_bind_error;
+ }
+ retval = device_create_file(dev->dev, &dev_attr_function);
+ if (retval) {
+ DMSG("unable to create file for %s --> error %d\n",
+ driver->driver.name, retval);
+ goto create_file_error;
+ }
+
+ /* ... then enable host detection and ep0; and we're ready
+ * for set_configuration as well as eventual disconnect.
+ * NOTE: this shouldn't power up until later.
+ */
+ DMSG("registered gadget driver '%s'\n", driver->driver.name);
+ udc_enable(dev);
+ dump_state(dev);
+
+ return 0;
+
+ create_file_error:
+ driver->unbind(&dev->gadget);
+
+ device_bind_error:
+ device_del(&dev->gadget.dev);
+
+ device_add_error:
+ dev->driver = 0;
+ dev->gadget.dev.driver = 0;
+
+ return retval;
+}
+
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+static void
+stop_activity(struct pxa27x_udc *dev, struct usb_gadget_driver *driver)
+{
+ int i;
+
+ DMSG("Trace path 1\n");
+ /* don't disconnect drivers more than once */
+ if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+ driver = 0;
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+ /* prevent new request submissions, kill any outstanding requests */
+ for (i = 0; i < UDC_EP_NUM; i++) {
+ struct pxa27x_ep *ep = &dev->ep[i];
+
+ ep->stopped = 1;
+ nuke(ep, -ESHUTDOWN);
+ }
+ del_timer_sync(&dev->timer);
+
+ /* 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 pxa27x_udc *dev = the_controller;
+
+ if (!dev)
+ return -ENODEV;
+ if (!driver || driver != dev->driver)
+ return -EINVAL;
+
+ local_irq_disable();
+ udc_disable(dev);
+ stop_activity(dev, driver);
+ local_irq_enable();
+
+ driver->unbind(&dev->gadget);
+ dev->driver = 0;
+
+ device_del(&dev->gadget.dev);
+ device_remove_file(dev->dev, &dev_attr_function);
+
+ DMSG("unregistered gadget driver '%s'\n", driver->driver.name);
+ dump_state(dev);
+ return 0;
+}
+
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+#ifndef enable_disconnect_irq
+#define enable_disconnect_irq() do {} while (0)
+#define disable_disconnect_irq() do {} while (0)
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+static inline void clear_ep_state(struct pxa27x_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 < UDC_EP_NUM; i++)
+ nuke(&dev->ep[i], -ECONNABORTED);
+}
+
+static void udc_watchdog(unsigned long _dev)
+{
+ struct pxa27x_udc *dev = (void *)_dev;
+
+ local_irq_disable();
+ if (dev->ep0state == EP0_STALL
+ && (UDCCSR0 & UDCCSR0_FST) == 0 && (UDCCSR0 & UDCCSR0_SST) == 0) {
+ UDCCSR0 = UDCCSR0_FST | UDCCSR0_FTF;
+ DBG(DBG_VERBOSE, "ep0 re-stall\n");
+ start_watchdog(dev);
+ }
+ local_irq_enable();
+}
+
+static void handle_ep0(struct pxa27x_udc *dev)
+{
+ u32 udccsr0 = UDCCSR0;
+ struct pxa27x_ep *ep = &dev->ep[0];
+ struct pxa27x_request *req;
+ union {
+ struct usb_ctrlrequest r;
+ u8 raw[8];
+ u32 word[2];
+ } u;
+
+ if (list_empty(&ep->queue))
+ req = 0;
+ else
+ req = list_entry(ep->queue.next, struct pxa27x_request, queue);
+
+ /* clear stall status */
+ if (udccsr0 & UDCCSR0_SST) {
+ nuke(ep, -EPIPE);
+ UDCCSR0 = UDCCSR0_SST;
+ del_timer(&dev->timer);
+ ep0_idle(dev);
+ }
+
+ /* previous request unfinished? non-error iff back-to-back ... */
+ if ((udccsr0 & UDCCSR0_SA) != 0 && dev->ep0state != EP0_IDLE) {
+ nuke(ep, 0);
+ del_timer(&dev->timer);
+ ep0_idle(dev);
+ }
+
+ switch (dev->ep0state) {
+ case EP0_NO_ACTION:
+ printk(KERN_INFO "%s: Busy\n", __FUNCTION__);
+ /*Fall through */
+ case EP0_IDLE:
+ /* late-breaking status? */
+ udccsr0 = UDCCSR0;
+
+ /* start control request? */
+ if (likely((udccsr0 & (UDCCSR0_OPC | UDCCSR0_SA | UDCCSR0_RNE))
+ == (UDCCSR0_OPC | UDCCSR0_SA | UDCCSR0_RNE))) {
+ int i;
+
+ nuke(ep, -EPROTO);
+ /* read SETUP packet */
+ for (i = 0; i < 2; i++) {
+ if (unlikely(!(UDCCSR0 & UDCCSR0_RNE))) {
+ bad_setup:
+ DMSG("SETUP %d!\n", i);
+ goto stall;
+ }
+ u.word[i] = UDCDR0;
+ }
+ if (unlikely((UDCCSR0 & UDCCSR0_RNE) != 0))
+ goto bad_setup;
+
+ le16_to_cpus(&u.r.wValue);
+ le16_to_cpus(&u.r.wIndex);
+ le16_to_cpus(&u.r.wLength);
+
+ LED_EP0_ON;
+
+ DBG(DBG_VERBOSE, "SETUP %02x.%02x v%04x i%04x l%04x\n",
+ u.r.bRequestType, u.r.bRequest,
+ u.r.wValue, u.r.wIndex, 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;
+#if 0
+ switch (u.r.bRequest) {
+ /* hardware was supposed to hide this */
+ case USB_REQ_SET_CONFIGURATION:
+ case USB_REQ_SET_INTERFACE:
+ case USB_REQ_SET_ADDRESS:
+ printk(KERN_ERR "Should not come here\n");
+ break;
+ }
+
+#endif
+ 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...
+ */
+ WARN("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!
+ */
+ }
+ DBG(DBG_VERBOSE, "protocol STALL, "
+ "%02x err %d\n", UDCCSR0, i);
+ stall:
+ /* the watchdog timer helps deal with cases
+ * where udc seems to clear FST wrongly, and
+ * then NAKs instead of STALLing.
+ */
+ ep0start(dev, UDCCSR0_FST | UDCCSR0_FTF,
+ "stall");
+ start_watchdog(dev);
+ dev->ep0state = EP0_STALL;
+ LED_EP0_OFF;
+
+ /* 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, UDCCSR0_IPR, "defer/IPR");
+ }
+
+ /* expect at least one data or status stage irq */
+ return;
+
+ } else {
+ /* some random early IRQ:
+ * - we acked FST
+ * - IPR cleared
+ * - OPC got set, without SA (likely status stage)
+ */
+ UDCCSR0 = udccsr0 & (UDCCSR0_SA | UDCCSR0_OPC);
+ }
+ break;
+ case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */
+ if (udccsr0 & UDCCSR0_OPC) {
+ UDCCSR0 = UDCCSR0_OPC | UDCCSR0_FTF;
+ DBG(DBG_VERBOSE, "ep0in premature status\n");
+ if (req)
+ done(ep, req, 0);
+ ep0_idle(dev);
+ } else { /* irq was IPR clearing */
+
+ if (req) {
+ /* 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 (udccsr0 & UDCCSR0_OPC) {
+ 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 */
+
+ DBG(DBG_VERBOSE, "ep0out premature status\n");
+ if (req)
+ done(ep, req, 0);
+ ep0_idle(dev);
+ }
+ break;
+ case EP0_STALL:
+ UDCCSR0 = UDCCSR0_FST;
+ break;
+ }
+ UDCISR0 = UDCISR_INT(0, UDCISR_INT_MASK);
+}
+
+static void handle_ep(struct pxa27x_ep *ep)
+{
+ struct pxa27x_request *req;
+ int completed;
+ u32 udccsr = 0;
+
+ DMSG("%s is called\n", __FUNCTION__);
+ do {
+ completed = 0;
+ if (likely(!list_empty(&ep->queue))) {
+ req = list_entry(ep->queue.next,
+ struct pxa27x_request, queue);
+ } else
+ req = 0;
+
+#if 0
+ udccsr = *ep->reg_udccsr;
+#endif
+ DMSG("%s: req:%p, udcisr0:0x%x udccsr %p:0x%x\n", __FUNCTION__,
+ req, UDCISR0, ep->reg_udccsr, *ep->reg_udccsr);
+ if (unlikely(ep->dir_in)) {
+ udccsr = (UDCCSR_SST | UDCCSR_TRN) & *ep->reg_udccsr;
+ if (unlikely(udccsr))
+ *ep->reg_udccsr = udccsr;
+
+ if (req && likely((*ep->reg_udccsr & UDCCSR_FS) != 0))
+ completed = write_fifo(ep, req);
+
+ } else {
+ udccsr = (UDCCSR_SST | UDCCSR_TRN) & *ep->reg_udccsr;
+ if (unlikely(udccsr))
+ *ep->reg_udccsr = udccsr;
+
+ /* fifos can hold packets, ready for reading... */
+ if (likely(req)) {
+ completed = read_fifo(ep, req);
+ } else {
+ pio_irq_disable(ep->ep_num);
+ *ep->reg_udccsr = UDCCSR_FEF;
+ DMSG("%s: no req for out data\n", __FUNCTION__);
+ }
+ }
+ ep->pio_irqs++;
+ } while (completed);
+}
+
+static void pxa27x_change_configuration(struct pxa27x_udc *dev)
+{
+ struct usb_ctrlrequest req;
+
+ req.bRequestType = 0;
+ req.bRequest = USB_REQ_SET_CONFIGURATION;
+ req.wValue = dev->configuration;
+ req.wIndex = 0;
+ req.wLength = 0;
+
+ dev->ep0state = EP0_NO_ACTION;
+ dev->driver->setup(&dev->gadget, &req);
+
+}
+
+static void pxa27x_change_interface(struct pxa27x_udc *dev)
+{
+ struct usb_ctrlrequest req;
+
+ req.bRequestType = USB_RECIP_INTERFACE;
+ req.bRequest = USB_REQ_SET_INTERFACE;
+ req.wValue = dev->alternate;
+ req.wIndex = dev->interface;
+ req.wLength = 0;
+
+ dev->ep0state = EP0_NO_ACTION;
+ dev->driver->setup(&dev->gadget, &req);
+}
+
+/*
+ * pxa27x_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 irqreturn_t pxa27x_udc_irq(int irq, void *_dev)
+{
+ struct pxa27x_udc *dev = _dev;
+ int handled;
+
+ dev->stats.irqs++;
+ HEX_DISPLAY(dev->stats.irqs);
+
+ DBG(DBG_VERBOSE, "Interrupt, UDCISR0:0x%08x, UDCISR1:0x%08x, "
+ "UDCCR:0x%08x\n", UDCISR0, UDCISR1, UDCCR);
+
+ do {
+ u32 udcir = UDCISR1 & 0xF8000000;
+
+ handled = 0;
+
+ /* SUSpend Interrupt Request */
+ if (unlikely(udcir & UDCISR1_IRSU)) {
+ UDCISR1 = UDCISR1_IRSU;
+ handled = 1;
+ DBG(DBG_VERBOSE, "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(udcir & UDCISR1_IRRU)) {
+ UDCISR1 = UDCISR1_IRRU;
+ handled = 1;
+ DBG(DBG_VERBOSE, "USB resume\n");
+
+ if (dev->gadget.speed != USB_SPEED_UNKNOWN
+ && dev->driver && dev->driver->resume)
+ dev->driver->resume(&dev->gadget);
+ }
+
+ if (unlikely(udcir & UDCISR1_IRCC)) {
+ unsigned config, interface, alternate;
+
+ handled = 1;
+ DBG(DBG_VERBOSE, "USB SET_CONFIGURATION or "
+ "SET_INTERFACE command received\n");
+
+ UDCCR |= UDCCR_SMAC;
+
+ config = (UDCCR & UDCCR_ACN) >> UDCCR_ACN_S;
+
+ if (dev->configuration != config) {
+ dev->configuration = config;
+ pxa27x_change_configuration(dev);
+ }
+
+ interface = (UDCCR & UDCCR_AIN) >> UDCCR_AIN_S;
+ alternate = (UDCCR & UDCCR_AAISN) >> UDCCR_AAISN_S;
+
+ if ((dev->configuration != interface) ||
+ (dev->alternate != alternate)) {
+ dev->interface = config;
+ dev->alternate = alternate;
+ pxa27x_change_interface(dev);
+ }
+
+ UDCISR1 = UDCISR1_IRCC;
+ DMSG("%s: con:%d,inter:%d,alt:%d\n",
+ __FUNCTION__, config, interface, alternate);
+ }
+
+ /* ReSeT Interrupt Request - USB reset */
+ if (unlikely(udcir & UDCISR1_IRRS)) {
+ UDCISR1 = UDCISR1_IRRS;
+ handled = 1;
+
+ if ((UDCCR & UDCCR_UDA) == 0) {
+ DBG(DBG_VERBOSE, "SB reset start\n");
+
+ /* reset driver and endpoints,
+ * in case that's not yet done
+ */
+ stop_activity(dev, dev->driver);
+
+ }
+ INFO("USB reset\n");
+ dev->gadget.speed = USB_SPEED_FULL;
+ memset(&dev->stats, 0, sizeof dev->stats);
+
+ } else {
+ u32 udcisr0 = UDCISR0;
+ u32 udcisr1 = UDCISR1 & 0xFFFF;
+ int i;
+
+ if (unlikely(!udcisr0 && !udcisr1))
+ continue;
+
+ DBG(DBG_VERY_NOISY, "irq %02x.%02x\n", udcisr1,
+ udcisr0);
+
+ /* control traffic */
+ if (udcisr0 & UDCISR0_IR0) {
+ dev->ep[0].pio_irqs++;
+ handle_ep0(dev);
+ handled = 1;
+ }
+
+ udcisr0 >>= 2;
+ /* endpoint data transfers */
+ for (i = 1; udcisr0 != 0 && i < 16; udcisr0 >>= 2, i++) {
+ UDCISR0 = UDCISR_INT(i, UDCISR_INT_MASK);
+
+ if (udcisr0 & UDC_INT_FIFOERROR)
+ printk(KERN_ERR
+ " Endpoint %d Fifo error\n", i);
+ if (udcisr0 & UDC_INT_PACKETCMP) {
+ handle_ep(&dev->ep[i]);
+ handled = 1;
+ }
+
+ }
+
+ for (i = 0; udcisr1 != 0 && i < 8; udcisr1 >>= 2, i++) {
+ UDCISR1 = UDCISR_INT(i, UDCISR_INT_MASK);
+
+ if (udcisr1 & UDC_INT_FIFOERROR) {
+ printk(KERN_ERR
+ " Endpoint %d fifo error\n",
+ (i + 16));
+ }
+
+ if (udcisr1 & UDC_INT_PACKETCMP) {
+ handle_ep(&dev->ep[i + 16]);
+ handled = 1;
+ }
+ }
+ }
+
+ /* we could also ask for 1 msec SOF (SIR) interrupts */
+
+ } while (handled);
+ return IRQ_HANDLED;
+}
+
+static void udc_init_ep(struct pxa27x_udc *dev)
+{
+ int i;
+
+ INIT_LIST_HEAD(&dev->gadget.ep_list);
+ INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+
+ for (i = 0; i < UDC_EP_NUM; i++) {
+ struct pxa27x_ep *ep = &dev->ep[i];
+
+ ep->dma = -1;
+ if (i != 0) {
+ memset(ep, 0, sizeof(*ep));
+ }
+ INIT_LIST_HEAD(&ep->queue);
+ }
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void nop_release(struct device *dev)
+{
+ DMSG("%s %s\n", __FUNCTION__, dev->bus_id);
+}
+
+/* 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 pxa27x_udc memory = {
+ .gadget = {
+ .ops = &pxa27x_udc_ops,
+ .ep0 = &memory.ep[0].ep,
+ .name = driver_name,
+ .dev = {
+ .bus_id = "gadget",
+ .release = nop_release,
+ },
+ },
+
+ /* control endpoint */
+ .ep[0] = {
+ .ep = {
+ .name = ep0name,
+ .ops = &pxa27x_ep_ops,
+ .maxpacket = EP0_FIFO_SIZE,
+ },
+ .dev = &memory,
+ .reg_udccsr = &UDCCSR0,
+ .reg_udcdr = &UDCDR0,
+ }
+};
+
+#define CP15R0_VENDOR_MASK 0xffffe000
+
+#define CP15R0_XSCALE_VALUE 0x69054000 /* intel/arm/xscale */
+
+/*
+ * probe - binds to the platform device
+ */
+static int __init pxa27x_udc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct pxa27x_udc *udc = &memory;
+ int irq, retval;
+ u32 chiprev;
+
+ /* insist on Intel/ARM/XScale */
+ asm("mrc%? p15, 0, %0, c0, c0":"=r"(chiprev));
+ if ((chiprev & CP15R0_VENDOR_MASK) != CP15R0_XSCALE_VALUE) {
+ printk(KERN_ERR "%s: not XScale!\n", driver_name);
+ return -ENODEV;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return -ENODEV;
+ pr_debug("%s: IRQ %d\n", driver_name, irq);
+
+ /* other non-static parts of init */
+ udc->dev = dev;
+ udc->mach = dev->platform_data;
+
+ /* Disable irq, erase old events and disable the pull up on the bus */
+ UDCICR0 = 0x00000000;
+ UDCICR1 = 0x00000000;
+ UDCISR0 = 0xffffffff;
+ UDCISR1 = 0xffffffff;
+ if (udc->mach->gpio_pullup)
+ udc_gpio_init_pullup(udc->mach->gpio_pullup);
+
+ init_timer(&udc->timer);
+ udc->timer.function = udc_watchdog;
+ udc->timer.data = (unsigned long)udc;
+
+ device_initialize(&udc->gadget.dev);
+ udc->gadget.dev.parent = dev;
+ udc->gadget.dev.dma_mask = dev->dma_mask;
+
+ the_controller = udc;
+ dev_set_drvdata(dev, udc);
+
+ udc_disable(udc);
+ udc_init_ep(udc);
+ udc_reinit(udc);
+
+ /* irq setup after old hardware state is cleaned up */
+ retval = request_irq(irq, pxa27x_udc_irq, 0, driver_name, udc);
+ if (retval != 0) {
+ printk(KERN_ERR "%s: can't get irq %i, err %d\n",
+ driver_name, irq, retval);
+ return -EBUSY;
+ }
+ udc->got_irq = 1;
+
+ create_proc_files();
+
+ return 0;
+}
+
+static void pxa27x_udc_shutdown(struct platform_device *_dev)
+{
+ pullup_off();
+}
+
+static int __exit pxa27x_udc_remove(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct pxa27x_udc *udc = dev->driver_data;
+
+ udc_disable(udc);
+ remove_proc_files();
+ usb_gadget_unregister_driver(udc->driver);
+
+ if (udc->got_irq) {
+ free_irq(platform_get_irq(pdev, 0), udc);
+ udc->got_irq = 0;
+ }
+ if (machine_is_lubbock() && udc->got_disc) {
+ free_irq(LUBBOCK_USB_DISC_IRQ, udc);
+ udc->got_disc = 0;
+ }
+ dev_set_drvdata(dev, 0);
+ the_controller = 0;
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int pxa27x_udc_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct device *dev = &pdev->dev;
+ struct pxa27x_udc *udc = dev->driver_data;
+ int i;
+
+ DMSG("%s will go into SUSPEND_POWER_DOWN\n", __FUNCTION__);
+ udc->udccsr0 = UDCCSR0;
+ for (i = 1; (i < UDC_EP_NUM); i++) {
+ if (udc->ep[i].assigned) {
+ struct pxa27x_ep *ep = &udc->ep[i];
+
+ ep->udccsr_value = *ep->reg_udccsr;
+ ep->udccr_value = *ep->reg_udccr;
+ DMSG("EP%d, udccsr:0x%x, udccr:0x%x\n",
+ i, *ep->reg_udccsr, *ep->reg_udccr);
+ }
+ }
+
+ udc_clear_mask_UDCCR(UDCCR_UDE);
+ pxa_set_cken(CKEN_USB, 0);
+
+ return 0;
+}
+
+static int pxa27x_udc_resume(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct pxa27x_udc *udc = dev->driver_data;
+ int i;
+
+ DMSG("%s: udc resume\n", __FUNCTION__);
+
+ UDCCSR0 = udc->udccsr0 & (UDCCSR0_FST | UDCCSR0_DME);
+ for (i = 1; i < UDC_EP_NUM; i++) {
+ if (udc->ep[i].assigned) {
+ struct pxa27x_ep *ep = &udc->ep[i];
+
+ *ep->reg_udccsr = ep->udccsr_value;
+ *ep->reg_udccr = ep->udccr_value;
+ DMSG("EP%d, udccsr:0x%x, udccr:0x%x\n",
+ i, *ep->reg_udccsr, *ep->reg_udccr);
+ }
+ }
+ udc_enable(udc);
+ /* OTGPH bit is set when sleep mode is entered.
+ * it indicates that OTG pad is retaining its state.
+ * Upon exit from sleep mode and before clearing OTGPH,
+ * Software must configure the USB OTG pad, UDC, and UHC
+ * to the state they were in before entering sleep mode.*/
+ PSSR |= PSSR_OTGPH;
+
+ return 0;
+}
+#else
+#define pxa27x_udc_suspend NULL
+#define pxa27x_udc_resume NULL
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+static struct platform_driver pxa27x_udc_driver = {
+ .shutdown = pxa27x_udc_shutdown,
+ .remove = __exit_p(pxa27x_udc_remove),
+ .suspend = pxa27x_udc_suspend,
+ .resume = pxa27x_udc_resume,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "pxa27x-udc",
+ },
+};
+
+static int __init udc_init(void)
+{
+ printk(KERN_INFO "%s: version %s\n", driver_name, DRIVER_VERSION);
+ return platform_driver_probe(&pxa27x_udc_driver, pxa27x_udc_probe);
+}
+
+static void __exit udc_exit(void)
+{
+ platform_driver_unregister(&pxa27x_udc_driver);
+}
+
+module_init(udc_init);
+module_exit(udc_exit);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Frank Becker, Robert Schwebel, David Brownell, Rodolfo Giometti");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/pxa27x_udc.h
new file mode 100644
index 0000000..633d332
--- /dev/null
+++ b/drivers/usb/gadget/pxa27x_udc.h
@@ -0,0 +1,304 @@
+/*
+ * linux/drivers/usb/gadget/pxa27x_udc.h
+ * Intel PXA27x on-chip full speed USB device controller
+ *
+ * Copyright (C) 2003 Robert Schwebel <r.schwebel@pengutronix.de>, Pengutronix
+ * Copyright (C) 2003 David Brownell
+ * Copyright (C) 2004 Intel Corporation
+ *
+ * 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_PXA27X_H
+#define __LINUX_USB_GADGET_PXA27X_H
+
+#include <linux/types.h>
+
+struct pxa27x_udc;
+
+struct pxa27x_ep {
+ struct usb_ep ep;
+ struct pxa27x_udc *dev;
+
+ const struct usb_endpoint_descriptor *desc;
+ struct list_head queue;
+ unsigned long pio_irqs;
+ unsigned long dma_irqs;
+
+ int dma;
+ unsigned fifo_size;
+ unsigned ep_num;
+ unsigned ep_type;
+
+ unsigned stopped : 1;
+ unsigned dma_con : 1;
+ unsigned dir_in : 1;
+ unsigned assigned : 1;
+
+ unsigned config;
+ unsigned interface;
+ unsigned aisn;
+ /* UDCCSR = UDC Control/Status Register for this EP
+ * UBCR = UDC Byte Count Remaining (contents of OUT fifo)
+ * UDCDR = UDC Endpoint Data Register (the fifo)
+ * UDCCR = UDC Endpoint Configuration Registers
+ * DRCM = DMA Request Channel Map
+ */
+ volatile u32 *reg_udccsr;
+ volatile u32 *reg_udcbcr;
+ volatile u32 *reg_udcdr;
+ volatile u32 *reg_udccr;
+#ifdef USE_DMA
+ volatile u32 *reg_drcmr;
+#define drcmr(n) .reg_drcmr = & DRCMR ## n ,
+#else
+#define drcmr(n)
+#endif
+
+#ifdef CONFIG_PM
+ unsigned udccsr_value;
+ unsigned udccr_value;
+#endif
+};
+
+struct pxa27x_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,
+ EP0_NO_ACTION
+};
+
+#define EP0_FIFO_SIZE ((unsigned)16)
+#define BULK_FIFO_SIZE ((unsigned)64)
+#define ISO_FIFO_SIZE ((unsigned)256)
+#define INT_FIFO_SIZE ((unsigned)8)
+
+struct udc_stats {
+ struct ep0stats {
+ unsigned long ops;
+ unsigned long bytes;
+ } read, write;
+ unsigned long irqs;
+};
+
+#ifdef CONFIG_USB_PXA27X_SMALL
+/* when memory's tight, SMALL config saves code+data. */
+//#undef USE_DMA
+//#define UDC_EP_NUM 3
+#endif
+
+#ifndef UDC_EP_NUM
+#define UDC_EP_NUM 24
+#endif
+
+struct pxa27x_udc {
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *driver;
+
+ enum ep0_state ep0state;
+ struct udc_stats stats;
+ unsigned got_irq : 1,
+ got_disc : 1,
+ has_cfr : 1,
+ req_pending : 1,
+ req_std : 1,
+ req_config : 1;
+
+#define start_watchdog(dev) mod_timer(&dev->timer, jiffies + (HZ/200))
+ struct timer_list timer;
+
+ struct device *dev;
+ struct pxa2xx_udc_mach_info *mach;
+ u64 dma_mask;
+ struct pxa27x_ep ep [UDC_EP_NUM];
+
+ unsigned configuration,
+ interface,
+ alternate;
+#ifdef CONFIG_PM
+ unsigned udccsr0;
+#endif
+};
+
+/*-------------------------------------------------------------------------*/
+#if 0
+#ifdef DEBUG
+#define HEX_DISPLAY(n) do { \
+ if (machine_is_mainstone())\
+ { MST_LEDDAT1 = (n); } \
+ } while(0)
+
+#define HEX_DISPLAY1(n) HEX_DISPLAY(n)
+
+#define HEX_DISPLAY2(n) do { \
+ if (machine_is_mainstone()) \
+ { MST_LEDDAT2 = (n); } \
+ } while(0)
+
+#endif /* DEBUG */
+#endif
+/*-------------------------------------------------------------------------*/
+
+/* LEDs are only for debug */
+#ifndef HEX_DISPLAY
+#define HEX_DISPLAY(n) do {} while(0)
+#endif
+
+#ifndef LED_CONNECTED_ON
+#define LED_CONNECTED_ON do {} while(0)
+#define LED_CONNECTED_OFF do {} while(0)
+#endif
+#ifndef LED_EP0_ON
+#define LED_EP0_ON do {} while (0)
+#define LED_EP0_OFF do {} while (0)
+#endif
+
+static struct pxa27x_udc *the_controller;
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Debugging support vanishes in non-debug builds. DBG_NORMAL should be
+ * mostly silent during normal use/testing, with no timing side-effects.
+ */
+#define DBG_NORMAL 1 /* error paths, device state transitions */
+#define DBG_VERBOSE 2 /* add some success path trace info */
+#define DBG_NOISY 3 /* ... even more: request level */
+#define DBG_VERY_NOISY 4 /* ... even more: packet level */
+
+#ifdef DEBUG
+
+static const char *state_name[] = {
+ "EP0_IDLE",
+ "EP0_IN_DATA_PHASE", "EP0_OUT_DATA_PHASE",
+ "EP0_END_XFER", "EP0_STALL"
+};
+
+#define DMSG(stuff...) printk(KERN_ERR "udc: " stuff)
+
+#ifdef VERBOSE
+# define UDC_DEBUG DBG_VERBOSE
+#else
+# define UDC_DEBUG DBG_NORMAL
+#endif
+
+static void __attribute__ ((__unused__))
+dump_udccr(const char *label)
+{
+ u32 udccr = UDCCR;
+ DMSG("%s 0x%08x =%s%s%s%s%s%s%s%s%s%s, con=%d,inter=%d,altinter=%d\n",
+ label, udccr,
+ (udccr & UDCCR_OEN) ? " oen":"",
+ (udccr & UDCCR_AALTHNP) ? " aalthnp":"",
+ (udccr & UDCCR_AHNP) ? " rem" : "",
+ (udccr & UDCCR_BHNP) ? " rstir" : "",
+ (udccr & UDCCR_DWRE) ? " dwre" : "",
+ (udccr & UDCCR_SMAC) ? " smac" : "",
+ (udccr & UDCCR_EMCE) ? " emce" : "",
+ (udccr & UDCCR_UDR) ? " udr" : "",
+ (udccr & UDCCR_UDA) ? " uda" : "",
+ (udccr & UDCCR_UDE) ? " ude" : "",
+ (udccr & UDCCR_ACN) >> UDCCR_ACN_S,
+ (udccr & UDCCR_AIN) >> UDCCR_AIN_S,
+ (udccr & UDCCR_AAISN)>> UDCCR_AAISN_S );
+}
+
+static void __attribute__ ((__unused__))
+dump_udccsr0(const char *label)
+{
+ u32 udccsr0 = UDCCSR0;
+
+ DMSG("%s %s 0x%08x =%s%s%s%s%s%s%s\n",
+ label, state_name[the_controller->ep0state], udccsr0,
+ (udccsr0 & UDCCSR0_SA) ? " sa" : "",
+ (udccsr0 & UDCCSR0_RNE) ? " rne" : "",
+ (udccsr0 & UDCCSR0_FST) ? " fst" : "",
+ (udccsr0 & UDCCSR0_SST) ? " sst" : "",
+ (udccsr0 & UDCCSR0_DME) ? " dme" : "",
+ (udccsr0 & UDCCSR0_IPR) ? " ipr" : "",
+ (udccsr0 & UDCCSR0_OPC) ? " opr" : "");
+}
+
+static void __attribute__ ((__unused__))
+dump_state(struct pxa27x_udc *dev)
+{
+ unsigned i;
+
+ DMSG("%s, udcicr %02X.%02X, udcsir %02X.%02x, udcfnr %02X\n",
+ state_name[dev->ep0state],
+ UDCICR1, UDCICR0, UDCISR1, UDCISR0, UDCFNR);
+ dump_udccr("udccr");
+
+ if (!dev->driver) {
+ DMSG("no gadget driver bound\n");
+ return;
+ } else
+ DMSG("ep0 driver '%s'\n", dev->driver->driver.name);
+
+
+ dump_udccsr0 ("udccsr0");
+ DMSG("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 < UDC_EP_NUM; i++) {
+ if (dev->ep [i].desc == 0)
+ continue;
+ DMSG ("udccs%d = %02x\n", i, *dev->ep->reg_udccsr);
+ }
+}
+
+#if 0
+static void dump_regs(u8 ep)
+{
+ DMSG("EP:%d UDCCSR:0x%08x UDCBCR:0x%08x\n UDCCR:0x%08x\n",
+ ep,UDCCSN(ep), UDCBCN(ep), UDCCN(ep));
+}
+static void dump_req (struct pxa27x_request *req)
+{
+ struct usb_request *r = &req->req;
+
+ DMSG("%s: buf:0x%08x length:%d dma:0x%08x actual:%d\n",
+ __FUNCTION__, (unsigned)r->buf, r->length,
+ r->dma, r->actual);
+}
+#endif
+
+#else
+
+#define DMSG(stuff...) do{}while(0)
+
+#define dump_udccr(x) do{}while(0)
+#define dump_udccsr0(x) do{}while(0)
+#define dump_state(x) do{}while(0)
+
+#define UDC_DEBUG ((unsigned)0)
+
+#endif
+
+#define DBG(lvl, stuff...) do{if ((lvl) <= UDC_DEBUG) DMSG(stuff);}while(0)
+
+#define WARN(stuff...) printk(KERN_WARNING "udc: " stuff)
+#define INFO(stuff...) printk(KERN_INFO "udc: " stuff)
+
+
+#endif /* __LINUX_USB_GADGET_PXA27X_H */
diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c
index f847c34..dd6535d 100644
--- a/drivers/usb/gadget/serial.c
+++ b/drivers/usb/gadget/serial.c
@@ -1377,20 +1377,20 @@ static int __init gs_bind(struct usb_gadget *gadget)
usb_ep_autoconfig_reset(gadget);
- ep = usb_ep_autoconfig(gadget, &gs_fullspeed_in_desc);
+ ep = usb_ep_autoconfig(gadget, &gs_fullspeed_in_desc, 0, 0, 0);
if (!ep)
goto autoconf_fail;
EP_IN_NAME = ep->name;
ep->driver_data = ep; /* claim the endpoint */
- ep = usb_ep_autoconfig(gadget, &gs_fullspeed_out_desc);
+ ep = usb_ep_autoconfig(gadget, &gs_fullspeed_out_desc, 0, 0, 0);
if (!ep)
goto autoconf_fail;
EP_OUT_NAME = ep->name;
ep->driver_data = ep; /* claim the endpoint */
if (use_acm) {
- ep = usb_ep_autoconfig(gadget, &gs_fullspeed_notify_desc);
+ ep = usb_ep_autoconfig(gadget, &gs_fullspeed_notify_desc, 0, 0, 0);
if (!ep) {
printk(KERN_ERR "gs_bind: cannot run ACM on %s\n", gadget->name);
goto autoconf_fail;
diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c
index 7078374..cc50c1d 100644
--- a/drivers/usb/gadget/zero.c
+++ b/drivers/usb/gadget/zero.c
@@ -1154,7 +1154,7 @@ zero_bind (struct usb_gadget *gadget)
* but there may also be important quirks to address.
*/
usb_ep_autoconfig_reset (gadget);
- ep = usb_ep_autoconfig (gadget, &fs_source_desc);
+ ep = usb_ep_autoconfig (gadget, &fs_source_desc, 0, 0, 0);
if (!ep) {
autoconf_fail:
printk (KERN_ERR "%s: can't autoconfigure on %s\n",
@@ -1164,7 +1164,7 @@ autoconf_fail:
EP_IN_NAME = ep->name;
ep->driver_data = ep; /* claim */
- ep = usb_ep_autoconfig (gadget, &fs_sink_desc);
+ ep = usb_ep_autoconfig (gadget, &fs_sink_desc, 0, 0, 0);
if (!ep)
goto autoconf_fail;
EP_OUT_NAME = ep->name;
diff --git a/include/asm-arm/arch-pxa/udc.h b/include/asm-arm/arch-pxa/udc.h
index 8bc6f9c..969616b 100644
--- a/include/asm-arm/arch-pxa/udc.h
+++ b/include/asm-arm/arch-pxa/udc.h
@@ -1,10 +1,11 @@
/*
* linux/include/asm-arm/arch-pxa/udc.h
*
- * This supports machine-specific differences in how the PXA2xx
+ * This supports machine-specific differences in how the PXA2xx/PXA27x
* USB Device Controller (UDC) is wired.
*
*/
+#include <asm/arch/pxa-regs.h>
#include <asm/mach/udc_pxa2xx.h>
extern void pxa_set_udc_info(struct pxa2xx_udc_mach_info *info);
diff --git a/include/linux/usb_gadget.h b/include/linux/usb_gadget.h
index e17186d..64c81fd 100644
--- a/include/linux/usb_gadget.h
+++ b/include/linux/usb_gadget.h
@@ -445,10 +445,28 @@ usb_ep_fifo_flush (struct usb_ep *ep)
struct usb_gadget;
+/**
+ * struct usb_endpoint_config - possible configurations of a given endpoint
+ * @config: the configuration number
+ * @interface: the interface number
+ * @altinterface: the altinterface number
+ *
+ * Used as an array to pass information about the possible configurations
+ * of a given endpoint to the bus controller.
+ */
+struct usb_endpoint_config {
+ int config;
+ int interface;
+ int altinterface;
+};
+
/* the rest of the api to the controller hardware: device operations,
* which don't involve endpoints (or i/o).
*/
struct usb_gadget_ops {
+ struct usb_ep* (*ep_alloc)(struct usb_gadget *gadget,
+ struct usb_endpoint_descriptor *desc,
+ int config, int interface, int alt);
int (*get_frame)(struct usb_gadget *);
int (*wakeup)(struct usb_gadget *);
int (*set_selfpowered) (struct usb_gadget *, int is_selfpowered);
@@ -872,7 +890,8 @@ int usb_gadget_config_buf(const struct usb_config_descriptor *config,
/* utility wrapping a simple endpoint selection policy */
extern struct usb_ep *usb_ep_autoconfig (struct usb_gadget *,
- struct usb_endpoint_descriptor *) __devinit;
+ struct usb_endpoint_descriptor *,
+ int, int, int) __devinit;
extern void usb_ep_autoconfig_reset (struct usb_gadget *) __devinit;
^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [PATCH] PXA27x UDC driver.
2007-06-28 14:12 ` Rodolfo Giometti
@ 2007-06-28 21:29 ` Andrew Morton
2007-06-28 22:44 ` David Brownell
` (2 more replies)
2007-06-28 21:53 ` David Brownell
1 sibling, 3 replies; 22+ messages in thread
From: Andrew Morton @ 2007-06-28 21:29 UTC (permalink / raw)
To: Rodolfo Giometti
Cc: David Brownell, linux-usb-devel, linux-arm-kernel, linux-kernel,
Li Yang-r58472
On Thu, 28 Jun 2007 16:12:07 +0200
Rodolfo Giometti <giometti@enneenne.com> wrote:
> On Thu, Jun 28, 2007 at 07:15:06PM +0800, Li Yang-r58472 wrote:
>
> > You should probably also cc: linux-usb-devel@lists.sourceforge.net and
> > David Brownell for UDC matters.
>
> As suggest by Leo let me propose to you my new patch for PXA27x UDC
> support.
>
> Please, let me know what I have to do for kernel inclusion. :)
>
Please feed it through the current version of scripts/checkpatch.pl and
think about fixing the large amount of trivia spew which ensues.
> diff --git a/arch/arm/mach-pxa/generic.c b/arch/arm/mach-pxa/generic.c
> index 64b08b7..7f390fd 100644
> --- a/arch/arm/mach-pxa/generic.c
> +++ b/arch/arm/mach-pxa/generic.c
> @@ -282,7 +282,11 @@ static struct resource pxa2xx_udc_resources[] = {
> static u64 udc_dma_mask = ~(u32)0;
>
> static struct platform_device udc_device = {
> +#ifdef CONFIG_PXA27x
> + .name = "pxa27x-udc",
> +#else
> .name = "pxa2xx-udc",
> +#endif
This looks odd. The same driver presents itself under a different name
according to a config option?
> @@ -0,0 +1,2395 @@
> +/*
> + * linux/drivers/usb/gadget/pxa27x_udc.c
> + * Intel PXA2xx 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) 2004 Intel Corporation
> + * Copyright (C) 2007 Rodolfo Giometti <giometti@linux.it>
> + *
> + * 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
> + *
> + */
> +
> +#undef DEBUG
Why? -DDEBUG is normally provided on the command line. If the user _did_
go and set DEBUG, he presumably wants DEBUG.
> +/* #define VERBOSE DBG_VERBOSE */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/ioport.h>
> +#include <linux/types.h>
> +#include <linux/version.h>
> +#include <linux/errno.h>
> +#include <linux/delay.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/init.h>
> +#include <linux/timer.h>
> +#include <linux/list.h>
> +#include <linux/interrupt.h>
> +#include <linux/proc_fs.h>
> +#include <linux/mm.h>
> +#include <linux/device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/platform_device.h>
> +
> +#include <asm/byteorder.h>
> +#include <asm/dma.h>
> +#include <asm/io.h>
> +#include <asm/irq.h>
> +#include <asm/system.h>
> +#include <asm/mach-types.h>
> +#include <asm/unaligned.h>
> +#include <asm/hardware.h>
> +#include <asm/arch/pxa-regs.h>
> +
> +#include <linux/usb/ch9.h>
> +#include <linux/usb_gadget.h>
> +
> +#include <asm/arch/udc.h>
> +
> +/*
> + * This driver handles the USB Device Controller (UDC) in Intel's PXA 27x
> + * series processors.
> + * 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.
> + */
> +
> +#define DRIVER_VERSION "28-Jun-2007"
> +#define DRIVER_DESC "PXA 27x USB Device Controller driver"
> +
> +static const char driver_name[] = "pxa27x_udc";
> +
> +static const char ep0name[] = "ep0";
> +
> +#undef USE_DMA
So DMA is busted?
> +#undef DISABLE_TEST_MODE
enabling DISABLE_TEST_MODE seens to enable test mode. Confused.
> +#ifdef CONFIG_PROC_FS
> +#define UDC_PROC_FILE
> +#endif
> +
> +#include "pxa27x_udc.h"
> +
> +#ifdef CONFIG_EMBEDDED
> +/* few strings, and little code to use them */
> +#undef DEBUG
> +#undef UDC_PROC_FILE
> +#endif
gag, this looks like a mess. Thise sort of logic should be implemented in
Kconfig, not in .c.
> +#ifdef USE_DMA
> +static int use_dma = 1;
> +module_param(use_dma, bool, 0);
> +MODULE_PARM_DESC(use_dma, "true to use dma");
> +
> +static void dma_nodesc_handler(int dmach, void *_ep);
> +static void kick_dma(struct pxa27x_ep *ep, struct pxa27x_request *req);
> +
> +#define DMASTR " (dma support)"
> +
> +#else /* !USE_DMA */
> +#define DMASTR " (pio only)"
> +#endif
> +
> +#ifdef CONFIG_USB_PXA27X_SMALL
> +#define SIZE_STR " (small)"
> +#else
> +#define SIZE_STR ""
> +#endif
> +
> +#ifdef DISABLE_TEST_MODE
> +/* (mode == 0) == no undocumented chip tweaks
> + * (mode & 1) == double buffer bulk IN
> + * (mode & 2) == double buffer bulk OUT
> + * ... so mode = 3 (or 7, 15, etc) does it for both
> + */
> +static ushort fifo_mode = 0;
Unneeded initialisation (checkpatch should catch this)
Use u16, not ushort. Or just "int".
> +module_param(fifo_mode, ushort, 0);
> +MODULE_PARM_DESC(fifo_mode, "pxa27x udc fifo mode");
> +#endif
> +
> +#define UDCISR0_IR0 0x3
> +#define UDCISR_INT_MASK (UDC_INT_FIFOERROR | UDC_INT_PACKETCMP)
> +#define UDCICR_INT_MASK UDCISR_INT_MASK
> +
> +#define UDCCSR_MASK (UDCCSR_FST | UDCCSR_DME)
> +/* ---------------------------------------------------------------------------
> + * endpoint related parts of the api to the usb controller hardware,
> + * used by gadget driver; and the inner talker-to-hardware core.
> + * ---------------------------------------------------------------------------
> + */
kernel comments normally look like
/*
* endpoint related parts of the api to the usb controller hardware,
* used by gadget driver; and the inner talker-to-hardware core.
*
*/
> +
> +static void pxa27x_ep_fifo_flush(struct usb_ep *ep);
> +static void nuke(struct pxa27x_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->gpio_pullup)
> + udc_gpio_set(mach->gpio_pullup, 0);
> + else 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->gpio_pullup)
> + udc_gpio_set(mach->gpio_pullup, 1);
> + else if (mach->udc_command)
> + mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
> +}
> +
> +static void pio_irq_enable(int ep_num)
> +{
> + if (ep_num < 16)
> + UDCICR0 |= 3 << (ep_num * 2);
> + else {
> + ep_num -= 16;
> + UDCICR1 |= 3 << (ep_num * 2);
> + }
> +}
We'd normally put braces around the "if" clause if there are braces around
the "else" clause. What you have there looks a bit funny.
> +static void pio_irq_disable(int ep_num)
> +{
> + ep_num &= 0xf;
> + if (ep_num < 16)
> + UDCICR0 &= ~(3 << (ep_num * 2));
> + else {
> + ep_num -= 16;
> + UDCICR1 &= ~(3 << (ep_num * 2));
> + }
> +}
Ditto
> + if (ep->ep_type != USB_ENDPOINT_XFER_BULK
> + && desc->bmAttributes != USB_ENDPOINT_XFER_INT) {
We'd normally lay this out as
if (ep->ep_type != USB_ENDPOINT_XFER_BULK &&
desc->bmAttributes != USB_ENDPOINT_XFER_INT) {
but what you have there is pretty common.
> + DMSG("%s, %s type mismatch\n", __FUNCTION__, _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) {
that expression is quite hard to read.
/* 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) {
or something like that?
> + DMSG("%s, bad %s maxpacket\n", __FUNCTION__, _ep->name);
> + return -ERANGE;
> + }
> +
> + dev = ep->dev;
> + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
> + DMSG("%s, bogus device state\n", __FUNCTION__);
> + return -ESHUTDOWN;
> + }
> +
> + ep->desc = desc;
> + ep->dma = -1;
> + ep->stopped = 0;
> + ep->pio_irqs = ep->dma_irqs = 0;
> + ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
> +
> + /* flush fifo (mostly for OUT buffers) */
> + pxa27x_ep_fifo_flush(_ep);
> +
> + /* ... reset halt state too, if we could ... */
> +
> +#ifdef USE_DMA
> + /* for (some) bulk and ISO endpoints, try to get a DMA channel and
> + * bind it to the endpoint. otherwise use PIO.
> + */
> + DMSG("%s: called attributes=%d\n", __FUNCTION__, ep->ep_type);
> + switch (ep->ep_type) {
> + case USB_ENDPOINT_XFER_ISOC:
> + if (le16_to_cpu(desc->wMaxPacketSize) % 32)
> + break;
> + /* fall through */
> + case USB_ENDPOINT_XFER_BULK:
> + if (!use_dma || !ep->reg_drcmr)
> + break;
> + ep->dma = pxa_request_dma((char *)_ep->name,
> + (le16_to_cpu(desc->wMaxPacketSize) > 64)
> + ? DMA_PRIO_MEDIUM /* some iso */
> + : DMA_PRIO_LOW,
> + dma_nodesc_handler, ep);
> + if (ep->dma >= 0) {
> + *ep->reg_drcmr = DRCMR_MAPVLD | ep->dma;
> + DMSG("%s using dma%d\n", _ep->name, ep->dma);
> + }
> + default:
> + break;
> + }
> +#endif
> + DBG(DBG_VERBOSE, "enabled %s\n", _ep->name);
> + return 0;
> +}
> +
> +/*-------------------------------------------------------------------------*/
> +
> +/* for the pxa27x, 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).
> + */
> +
> +/*
> + * pxa27x_ep_alloc_request - allocate a request data structure
> + */
> +static struct usb_request *pxa27x_ep_alloc_request(struct usb_ep *_ep,
> + unsigned int gfp_flags)
> +{
> + struct pxa27x_request *req;
> +
> + req = kmalloc(sizeof *req, gfp_flags);
> + if (!req)
> + return 0;
> +
> + memset(req, 0, sizeof *req);
use kzalloc()
> + INIT_LIST_HEAD(&req->queue);
> + return &req->req;
> +}
> +
>
>
> +
> +static int
> +write_packet(volatile u32 * uddr, struct pxa27x_request *req, unsigned max)
Please review Documentation/volatile-considered-harmful.txt
> +{
> + u32 *buf;
> + int length, count, remain;
> +
> + buf = (u32 *) (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;
> +
> + remain = length & 0x3;
> + count = length & ~(0x3);
> +
> + while (likely(count)) {
> + *uddr = *buf++;
> + count -= 4;
> + }
> +
> + if (remain) {
> + volatile u8 *reg = (u8 *) uddr;
> + char *rd = (u8 *) buf;
> +
> + while (remain--) {
> + *reg = *rd++;
> + }
> + }
> +
> + return length;
> +}
> +
>
> ...
>
> +
> +static void dma_nodesc_handler(int dmach, void *_ep, struct pt_regs *r)
> +{
> + struct pxa27x_ep *ep = _ep;
> + struct pxa27x_request *req, *req_next;
> + u32 dcsr, tmp, completed;
> +
> + local_irq_disable();
this looks fishy. local_irq_disable() only provides cpu-local protection.
Is this code SMP-correct? What's happening here? A comment is needed
explaining this, at least.
> +static inline void validate_fifo_size(struct pxa27x_ep *pxa_ep, u8 bmAttributes)
> +{
> + switch (bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
> + case USB_ENDPOINT_XFER_CONTROL:
> + pxa_ep->fifo_size = EP0_FIFO_SIZE;
> + break;
> + case USB_ENDPOINT_XFER_ISOC:
> + pxa_ep->fifo_size = ISO_FIFO_SIZE;
> + break;
> + case USB_ENDPOINT_XFER_BULK:
> + pxa_ep->fifo_size = BULK_FIFO_SIZE;
> + break;
> + case USB_ENDPOINT_XFER_INT:
> + pxa_ep->fifo_size = INT_FIFO_SIZE;
> + break;
> + default:
> + break;
> + }
> +}
There's no point in inlining this. With only one callsite the compiler
will inline it anyway. If we grow a second callsite, this fucntion is too
large to inline.
This is general truth, so please generally avoid inline.
> + ep->maxpacket = min((ushort) pxa_ep->fifo_size, desc->wMaxPacketSize);
Use min_t, not an open-coded cast.
Use u16.
> +#ifdef UDC_PROC_FILE
> +
> +static const char proc_node_name[] = "driver/udc";
/proc is for process-related things. Driver-related things go in /sys
> +
> +#define create_proc_files() \
> + create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev)
> +#define remove_proc_files() \
> + remove_proc_entry(proc_node_name, NULL)
> +
> +#else /* !UDC_PROC_FILE */
> +#define create_proc_files() do {} while (0)
> +#define remove_proc_files() do {} while (0)
Please only use macros when you *must* use macros. When for some reason
you cannot use C. This is not such a case. IOW, convert to static
inlines.
> +#endif /* UDC_PROC_FILE */
> +
> + DMSG("registered gadget driver '%s'\n", driver->driver.name);
> + udc_enable(dev);
> + dump_state(dev);
> +
> + return 0;
> +
> + create_file_error:
> + driver->unbind(&dev->gadget);
> +
> + device_bind_error:
> + device_del(&dev->gadget.dev);
> +
> + device_add_error:
> + dev->driver = 0;
> + dev->gadget.dev.driver = 0;
> +
> + return retval;
> +}
We normally indent labels with zero or one spaces, not six.
> +}
> +
> +EXPORT_SYMBOL(usb_gadget_unregister_driver);
We normally leave zero blank lines between the } and the EXPORT_SYMBOL
(checkpatch should report this)
> +#ifndef enable_disconnect_irq
> +#define enable_disconnect_irq() do {} while (0)
> +#define disable_disconnect_irq() do {} while (0)
> +#endif
hm, OK, I guess this is somewhere where a macro is appropriate.
> +/*-------------------------------------------------------------------------*/
> +
> +static inline void clear_ep_state(struct pxa27x_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 < UDC_EP_NUM; i++)
> + nuke(&dev->ep[i], -ECONNABORTED);
> +}
Probably too big to inline.
Has no callers.
> + 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...
> + */
> + WARN("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!
> + */
> + }
> + DBG(DBG_VERBOSE, "protocol STALL, "
> + "%02x err %d\n", UDCCSR0, i);
> + stall:
what's that label doing all the way over there. It makes it rather hard to
find.
> + /* the watchdog timer helps deal with cases
> + * where udc seems to clear FST wrongly, and
> + * then NAKs instead of STALLing.
> + */
> + ep0start(dev, UDCCSR0_FST | UDCCSR0_FTF,
> + "stall");
> + start_watchdog(dev);
> + dev->ep0state = EP0_STALL;
> + LED_EP0_OFF;
> +
> + /* 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, UDCCSR0_IPR, "defer/IPR");
> + }
> +
> + /* expect at least one data or status stage irq */
> + return;
Burying returns deep inside large functions is unpopular: it can easily
lead to resource leaks and locking errors as the code evolves. It makes
review and auditing harder.
> + } else {
> + /* some random early IRQ:
> + * - we acked FST
> + * - IPR cleared
> + * - OPC got set, without SA (likely status stage)
> + */
> + UDCCSR0 = udccsr0 & (UDCCSR0_SA | UDCCSR0_OPC);
> + }
> + break;
> + case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */
> + if (udccsr0 & UDCCSR0_OPC) {
> + UDCCSR0 = UDCCSR0_OPC | UDCCSR0_FTF;
> + DBG(DBG_VERBOSE, "ep0in premature status\n");
> + if (req)
> + done(ep, req, 0);
> + ep0_idle(dev);
> + } else { /* irq was IPR clearing */
> +
> + if (req) {
> + /* 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 (udccsr0 & UDCCSR0_OPC) {
> + 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 */
> +
> + DBG(DBG_VERBOSE, "ep0out premature status\n");
> + if (req)
> + done(ep, req, 0);
> + ep0_idle(dev);
> + }
> + break;
> + case EP0_STALL:
> + UDCCSR0 = UDCCSR0_FST;
> + break;
> + }
> + UDCISR0 = UDCISR_INT(0, UDCISR_INT_MASK);
> +}
> +
>
> ...
>
> +static void udc_init_ep(struct pxa27x_udc *dev)
> +{
> + int i;
> +
> + INIT_LIST_HEAD(&dev->gadget.ep_list);
> + INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
> +
> + for (i = 0; i < UDC_EP_NUM; i++) {
> + struct pxa27x_ep *ep = &dev->ep[i];
> +
> + ep->dma = -1;
> + if (i != 0) {
> + memset(ep, 0, sizeof(*ep));
> + }
unneeded braces
> + INIT_LIST_HEAD(&ep->queue);
> + }
> +}
> +
> +/*-------------------------------------------------------------------------*/
> +
> +static void nop_release(struct device *dev)
> +{
> + DMSG("%s %s\n", __FUNCTION__, dev->bus_id);
> +}
> +
> +/* 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 pxa27x_udc memory = {
> + .gadget = {
> + .ops = &pxa27x_udc_ops,
> + .ep0 = &memory.ep[0].ep,
> + .name = driver_name,
> + .dev = {
> + .bus_id = "gadget",
> + .release = nop_release,
> + },
> + },
> +
> + /* control endpoint */
> + .ep[0] = {
> + .ep = {
> + .name = ep0name,
> + .ops = &pxa27x_ep_ops,
> + .maxpacket = EP0_FIFO_SIZE,
> + },
> + .dev = &memory,
> + .reg_udccsr = &UDCCSR0,
> + .reg_udcdr = &UDCDR0,
whitespace broke
> + }
> +};
> +
> +#define CP15R0_VENDOR_MASK 0xffffe000
> +
> +#define CP15R0_XSCALE_VALUE 0x69054000 /* intel/arm/xscale */
> +
> +/*
> + * probe - binds to the platform device
> + */
> +static int __init pxa27x_udc_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct pxa27x_udc *udc = &memory;
> + int irq, retval;
> + u32 chiprev;
> +
> + /* insist on Intel/ARM/XScale */
> + asm("mrc%? p15, 0, %0, c0, c0":"=r"(chiprev));
whitespace
> + if ((chiprev & CP15R0_VENDOR_MASK) != CP15R0_XSCALE_VALUE) {
> + printk(KERN_ERR "%s: not XScale!\n", driver_name);
> + return -ENODEV;
> + }
> +
> + irq = platform_get_irq(pdev, 0);
> + if (irq < 0)
> + return -ENODEV;
> + pr_debug("%s: IRQ %d\n", driver_name, irq);
> +
> + /* other non-static parts of init */
> + udc->dev = dev;
> + udc->mach = dev->platform_data;
> +
> + /* Disable irq, erase old events and disable the pull up on the bus */
> + UDCICR0 = 0x00000000;
> + UDCICR1 = 0x00000000;
> + UDCISR0 = 0xffffffff;
> + UDCISR1 = 0xffffffff;
> + if (udc->mach->gpio_pullup)
> + udc_gpio_init_pullup(udc->mach->gpio_pullup);
> +
> + init_timer(&udc->timer);
> + udc->timer.function = udc_watchdog;
> + udc->timer.data = (unsigned long)udc;
> +
> + device_initialize(&udc->gadget.dev);
> + udc->gadget.dev.parent = dev;
> + udc->gadget.dev.dma_mask = dev->dma_mask;
> +
> + the_controller = udc;
> + dev_set_drvdata(dev, udc);
> +
> + udc_disable(udc);
> + udc_init_ep(udc);
> + udc_reinit(udc);
> +
> + /* irq setup after old hardware state is cleaned up */
> + retval = request_irq(irq, pxa27x_udc_irq, 0, driver_name, udc);
> + if (retval != 0) {
> + printk(KERN_ERR "%s: can't get irq %i, err %d\n",
> + driver_name, irq, retval);
> + return -EBUSY;
> + }
> + udc->got_irq = 1;
> +
> + create_proc_files();
> +
> + return 0;
> +}
> +
>
> ...
>
> + /* UDCCSR = UDC Control/Status Register for this EP
> + * UBCR = UDC Byte Count Remaining (contents of OUT fifo)
> + * UDCDR = UDC Endpoint Data Register (the fifo)
> + * UDCCR = UDC Endpoint Configuration Registers
> + * DRCM = DMA Request Channel Map
> + */
> + volatile u32 *reg_udccsr;
> + volatile u32 *reg_udcbcr;
> + volatile u32 *reg_udcdr;
> + volatile u32 *reg_udccr;
more volatiles
> +#ifdef USE_DMA
> + volatile u32 *reg_drcmr;
> +#define drcmr(n) .reg_drcmr = & DRCMR ## n ,
> +#else
> +#define drcmr(n)
> +#endif
> +
> +#ifdef CONFIG_PM
> + unsigned udccsr_value;
> + unsigned udccr_value;
> +#endif
> +};
> +
>
> ...
>
> +
> +#define EP0_FIFO_SIZE ((unsigned)16)
> +#define BULK_FIFO_SIZE ((unsigned)64)
> +#define ISO_FIFO_SIZE ((unsigned)256)
> +#define INT_FIFO_SIZE ((unsigned)8)
#define INT_FIFO_SIZE 8U
would be nicer.
> +struct pxa27x_udc {
> + struct usb_gadget gadget;
> + struct usb_gadget_driver *driver;
> +
> + enum ep0_state ep0state;
> + struct udc_stats stats;
> + unsigned got_irq : 1,
> + got_disc : 1,
> + has_cfr : 1,
> + req_pending : 1,
> + req_std : 1,
> + req_config : 1;
> +
> +#define start_watchdog(dev) mod_timer(&dev->timer, jiffies + (HZ/200))
write it in C or, better, just open-code it at the callsite.
> + struct timer_list timer;
> +
> + struct device *dev;
> + struct pxa2xx_udc_mach_info *mach;
> + u64 dma_mask;
> + struct pxa27x_ep ep [UDC_EP_NUM];
> +
> + unsigned configuration,
> + interface,
> + alternate;
> +#ifdef CONFIG_PM
> + unsigned udccsr0;
> +#endif
> +};
>
> ...
>
> +#define HEX_DISPLAY2(n) do { \
> + if (machine_is_mainstone()) \
> + { MST_LEDDAT2 = (n); } \
> + } while(0)
See, this references "n"
> +
> +/* LEDs are only for debug */
> +#ifndef HEX_DISPLAY
> +#define HEX_DISPLAY(n) do {} while(0)
> +#endif
but this doesn't. So we risk getting unused-var warnings in callers
depending upon config options. Writing this in C rather than cpp (the
general rule) will fix this, as well as lots of other stuff.
HEX_DISPLAY2 gets different treatment from HEX_DISPLAY here.
> +#ifndef LED_CONNECTED_ON
> +#define LED_CONNECTED_ON do {} while(0)
> +#define LED_CONNECTED_OFF do {} while(0)
> +#endif
> +#ifndef LED_EP0_ON
> +#define LED_EP0_ON do {} while (0)
> +#define LED_EP0_OFF do {} while (0)
> +#endif
> +
> +static struct pxa27x_udc *the_controller;
eep, you can't do that! We'll get separate instances of the_controller in
each C file which includes this header.
> +/*-------------------------------------------------------------------------*/
> +
> +/*
> + * Debugging support vanishes in non-debug builds. DBG_NORMAL should be
> + * mostly silent during normal use/testing, with no timing side-effects.
> + */
> +#define DBG_NORMAL 1 /* error paths, device state transitions */
> +#define DBG_VERBOSE 2 /* add some success path trace info */
> +#define DBG_NOISY 3 /* ... even more: request level */
> +#define DBG_VERY_NOISY 4 /* ... even more: packet level */
> +
> +#ifdef DEBUG
> +
> +static const char *state_name[] = {
> + "EP0_IDLE",
> + "EP0_IN_DATA_PHASE", "EP0_OUT_DATA_PHASE",
> + "EP0_END_XFER", "EP0_STALL"
> +};
> +
> +#define DMSG(stuff...) printk(KERN_ERR "udc: " stuff)
> +
> +#ifdef VERBOSE
> +# define UDC_DEBUG DBG_VERBOSE
> +#else
> +# define UDC_DEBUG DBG_NORMAL
> +#endif
> +
> +static void __attribute__ ((__unused__))
Use __maybe_unused
> +dump_udccr(const char *label)
> +{
> + u32 udccr = UDCCR;
> + DMSG("%s 0x%08x =%s%s%s%s%s%s%s%s%s%s, con=%d,inter=%d,altinter=%d\n",
> + label, udccr,
> + (udccr & UDCCR_OEN) ? " oen":"",
> + (udccr & UDCCR_AALTHNP) ? " aalthnp":"",
> + (udccr & UDCCR_AHNP) ? " rem" : "",
> + (udccr & UDCCR_BHNP) ? " rstir" : "",
> + (udccr & UDCCR_DWRE) ? " dwre" : "",
> + (udccr & UDCCR_SMAC) ? " smac" : "",
> + (udccr & UDCCR_EMCE) ? " emce" : "",
> + (udccr & UDCCR_UDR) ? " udr" : "",
> + (udccr & UDCCR_UDA) ? " uda" : "",
> + (udccr & UDCCR_UDE) ? " ude" : "",
> + (udccr & UDCCR_ACN) >> UDCCR_ACN_S,
> + (udccr & UDCCR_AIN) >> UDCCR_AIN_S,
> + (udccr & UDCCR_AAISN)>> UDCCR_AAISN_S );
> +}
> +
> +static void __attribute__ ((__unused__))
ditto
> +dump_udccsr0(const char *label)
> +{
> + u32 udccsr0 = UDCCSR0;
> +
> + DMSG("%s %s 0x%08x =%s%s%s%s%s%s%s\n",
> + label, state_name[the_controller->ep0state], udccsr0,
> + (udccsr0 & UDCCSR0_SA) ? " sa" : "",
> + (udccsr0 & UDCCSR0_RNE) ? " rne" : "",
> + (udccsr0 & UDCCSR0_FST) ? " fst" : "",
> + (udccsr0 & UDCCSR0_SST) ? " sst" : "",
> + (udccsr0 & UDCCSR0_DME) ? " dme" : "",
> + (udccsr0 & UDCCSR0_IPR) ? " ipr" : "",
> + (udccsr0 & UDCCSR0_OPC) ? " opr" : "");
> +}
> +
> +static void __attribute__ ((__unused__))
ditto
> +dump_state(struct pxa27x_udc *dev)
> +{
> + unsigned i;
> +
> + DMSG("%s, udcicr %02X.%02X, udcsir %02X.%02x, udcfnr %02X\n",
> + state_name[dev->ep0state],
> + UDCICR1, UDCICR0, UDCISR1, UDCISR0, UDCFNR);
> + dump_udccr("udccr");
> +
> + if (!dev->driver) {
> + DMSG("no gadget driver bound\n");
> + return;
> + } else
> + DMSG("ep0 driver '%s'\n", dev->driver->driver.name);
> +
> +
> + dump_udccsr0 ("udccsr0");
> + DMSG("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 < UDC_EP_NUM; i++) {
> + if (dev->ep [i].desc == 0)
> + continue;
> + DMSG ("udccs%d = %02x\n", i, *dev->ep->reg_udccsr);
> + }
> +}
> +
>
> ..
>
> +
> +#define WARN(stuff...) printk(KERN_WARNING "udc: " stuff)
> +#define INFO(stuff...) printk(KERN_INFO "udc: " stuff)
hrm. Why does every driver in the tree need to invent its own boilerplate
infrastructure?
can we use dev_warn() here or something?
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH] PXA27x UDC driver.
2007-06-28 14:12 ` Rodolfo Giometti
2007-06-28 21:29 ` Andrew Morton
@ 2007-06-28 21:53 ` David Brownell
2007-06-29 8:58 ` Rodolfo Giometti
2007-06-29 9:03 ` [linux-usb-devel] " Dmitry Krivoschekov
1 sibling, 2 replies; 22+ messages in thread
From: David Brownell @ 2007-06-28 21:53 UTC (permalink / raw)
To: Rodolfo Giometti
Cc: linux-usb-devel, linux-arm-kernel, linux-kernel, Andrew Morton,
Li Yang-r58472
On Thursday 28 June 2007, Rodolfo Giometti wrote:
> As suggest by Leo let me propose to you my new patch for PXA27x UDC
> support.
>
> Please, let me know what I have to do for kernel inclusion. :)
Let's start with *JUST* a driver, not trying to update everything
else in the USB Gadget stack so that it looks like it's designed
specifically to handle all of Intel's design botches related to
endpoint config ... and work worse for essentially everything else.
(Unlike pretty much every other vendor, Intel wanted hardware config
management. It was unusably buggy in pxa21x/25x/26x, and not much
better in pxa27x.)
So in technical terms, and to repeat what I've said before: just
configure it to act more like a PXA 25x chip (no altsettings) and
get it so it passes all the tests [1], modulo errata which have no
workarounds ... then submit that. No epautoconfig updates, no
patches to every gadget driver to cope with updated autoconfig.
Once there's a basic working no-frills version merged, then we can
talk about whether things in the rest of the stack should change
to accomodate the bizarre concepts of this controller.
- Dave
[1] http://www.linux-usb.org/usbtest/
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH] PXA27x UDC driver.
2007-06-28 21:29 ` Andrew Morton
@ 2007-06-28 22:44 ` David Brownell
2007-06-30 14:28 ` [linux-usb-devel] " David Brownell
2007-07-10 15:49 ` Rodolfo Giometti
2 siblings, 0 replies; 22+ messages in thread
From: David Brownell @ 2007-06-28 22:44 UTC (permalink / raw)
To: Andrew Morton, Rodolfo Giometti
Cc: linux-usb-devel, linux-arm-kernel, linux-kernel, Li Yang-r58472
By the way, there are three or four versions of a pxa27x UDC
driver floating around; which one is this? One you updated?
It looks like it forked from the pxa2xx driver several years ago
but never got cleaned up ... e.g. it's a different controller,
so none of the comments relevant to earlier controller versions
(pxa250 rev a0 etc) could possibly apply, or even later ones
(like pxa255, effectively end-of-the-line for that UDC).
Quite a lot of the code issues in this driver are inherited from
some rather ancient code dating to ... 2.4.19-rmk7 kernels, if
I don't mis-recall. Some of them are even specific to the now
obsolete "Lubbock" reference hardware.
In short: whatever version of a pxa27x_udc driver gets submitted,
much cleanup seems *STILL* to be needed.
On Thursday 28 June 2007, Andrew Morton wrote:
> > --- a/arch/arm/mach-pxa/generic.c
> > +++ b/arch/arm/mach-pxa/generic.c
> > @@ -282,7 +282,11 @@ static struct resource pxa2xx_udc_resources[] = {
> > static u64 udc_dma_mask = ~(u32)0;
> >
> > static struct platform_device udc_device = {
> > +#ifdef CONFIG_PXA27x
> > + .name = "pxa27x-udc",
> > +#else
> > .name = "pxa2xx-udc",
> > +#endif
>
> This looks odd. The same driver presents itself under a different name
> according to a config option?
The PXA 27x platform devices should really have been in a
different file ... the PXA platform has been rather neglected,
since it effectively has no maintainer.
>
> > + * 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.
And *STILL* people carry around comments from the pxa2xx_udc
code (for "x" in 1, 6, and mostly 5).
ISTR that every time someone has submitted a pxa27x driver I've
had to ask that obsolete comments be removed. Still waiting...
> > +#undef USE_DMA
>
> So DMA is busted?
This driver was largely cut'n'paste from pxa2xx_udc, which
had to cope with errata which mostly meant that it couldn't
work. Then there were *DESIGN BUGS* in the DMA, making it
not worth using in the most significant cases ... even in
the later chip revisions where DMA would allegedly work.
(Which reminds me, maybe I should just rip DMA out of the
pxa2xx_udc code ... nobody seems to have picked it up and
finished it, so there's no point in keeping that around.)
I'm told that DMA works better in pxa27x, and that at least
one version of the pxa27x_udc driver enabled it by default.
(For TX, if not RX where it's most useful...) This would
seem not to be that version.
> > +#ifdef CONFIG_EMBEDDED
> > +/* few strings, and little code to use them */
> > +#undef DEBUG
> > +#undef UDC_PROC_FILE
> > +#endif
>
> gag, this looks like a mess. Thise sort of logic should be implemented in
> Kconfig, not in .c.
The debug file stuff *DOES* have a Kconfig hook...
> > +#ifndef enable_disconnect_irq
> > +#define enable_disconnect_irq() do {} while (0)
> > +#define disable_disconnect_irq() do {} while (0)
> > +#endif
>
> hm, OK, I guess this is somewhere where a macro is appropriate.
Not really. It's an artifact of a rather bizarre FPGA design
on the PXA25x reference design ("Lubbock"), which was very
gratuitously different from what Intel docs recommended. The
normal case is that there is a single IRQ.
That stuff is long gone even from pxa2xx_udc ... and in any case,
the VBUS irqs should not be called "connect/disconnect", it's
just confusing to do that.
> > + /* hardware sometimes neglects to tell
> > + * tell us about config change events,
> > + * so later ones may fail...
> > + */
Curious ... did that pxa255 erratum transfer somehow to the
newer pxa27x silicon?
> > + WARN("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!
> > + */
Another comment that shouldn't be relevant on this driver.
ISTR that all PXA 27x chips have CFR. It was new in PXA255,
which is why the pxa2xx_udc had to cope with its absence.
> HEX_DISPLAY2 gets different treatment from HEX_DISPLAY here.
Yeah, but all that stuff was specific to the Lubbock platform.
Best to just remove it.
> > +#define WARN(stuff...) printk(KERN_WARNING "udc: " stuff)
> > +#define INFO(stuff...) printk(KERN_INFO "udc: " stuff)
>
> hrm. Why does every driver in the tree need to invent its own boilerplate
> infrastructure?
>
> can we use dev_warn() here or something?
That stuff dates from 2.4.19-rmk7 kernel support, which
significantly predates dev_warn(). ;)
- Dave
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH] PXA27x UDC driver.
2007-06-28 21:53 ` David Brownell
@ 2007-06-29 8:58 ` Rodolfo Giometti
2007-06-29 9:33 ` [linux-usb-devel] " Dmitry Krivoschekov
2007-06-30 14:25 ` David Brownell
2007-06-29 9:03 ` [linux-usb-devel] " Dmitry Krivoschekov
1 sibling, 2 replies; 22+ messages in thread
From: Rodolfo Giometti @ 2007-06-29 8:58 UTC (permalink / raw)
To: David Brownell
Cc: linux-usb-devel, linux-arm-kernel, linux-kernel, Andrew Morton,
Li Yang-r58472
On Thu, Jun 28, 2007 at 02:53:22PM -0700, David Brownell wrote:
>
> Let's start with *JUST* a driver, not trying to update everything
> else in the USB Gadget stack so that it looks like it's designed
> specifically to handle all of Intel's design botches related to
> endpoint config ... and work worse for essentially everything else.
>
> (Unlike pretty much every other vendor, Intel wanted hardware config
> management. It was unusably buggy in pxa21x/25x/26x, and not much
> better in pxa27x.)
>
>
> So in technical terms, and to repeat what I've said before: just
> configure it to act more like a PXA 25x chip (no altsettings) and
> get it so it passes all the tests [1], modulo errata which have no
> workarounds ... then submit that. No epautoconfig updates, no
> patches to every gadget driver to cope with updated autoconfig.
This looks interesting... as you alredy told this driver derives from
an older one, I just maintained it till now.
If I well understand I should remove usb_ep_autoconfig() and program
into the controller only one (the default) configuration. Is that
right?
Currently I tested the driver only with ether gadget, but if I do as
above, should I get the driver working with all gadgets automagically?
:)
Thanks a lot,
Rodolfo
--
GNU/Linux Solutions e-mail: giometti@enneenne.com
Linux Device Driver giometti@gnudd.com
Embedded Systems giometti@linux.it
UNIX programming phone: +39 349 2432127
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [linux-usb-devel] [PATCH] PXA27x UDC driver.
2007-06-28 21:53 ` David Brownell
2007-06-29 8:58 ` Rodolfo Giometti
@ 2007-06-29 9:03 ` Dmitry Krivoschekov
2007-06-30 13:46 ` David Brownell
1 sibling, 1 reply; 22+ messages in thread
From: Dmitry Krivoschekov @ 2007-06-29 9:03 UTC (permalink / raw)
To: David Brownell
Cc: Rodolfo Giometti, linux-kernel, Andrew Morton, Li Yang-r58472,
linux-usb-devel, linux-arm-kernel
David Brownell wrote:
> On Thursday 28 June 2007, Rodolfo Giometti wrote:
>
>> As suggest by Leo let me propose to you my new patch for PXA27x UDC
>> support.
>>
>> Please, let me know what I have to do for kernel inclusion. :)
>
> Let's start with *JUST* a driver, not trying to update everything
> else in the USB Gadget stack so that it looks like it's designed
> specifically to handle all of Intel's design botches related to
> endpoint config ... and work worse for essentially everything else.
>
> (Unlike pretty much every other vendor, Intel wanted hardware config
> management. It was unusably buggy in pxa21x/25x/26x, and not much
> better in pxa27x.)
>
>
> So in technical terms, and to repeat what I've said before: just
> configure it to act more like a PXA 25x chip (no altsettings) and
> get it so it passes all the tests [1], modulo errata which have no
> workarounds
Other options are:
1. pre-program endpoints so the setting covers all gadget
configurations, it seems it's feasible. The only appreciable
change is, CDC Ethernet config number should be 3 instead of 1.
It should not break anything.
2. Implement a FAKE call of GET_CONFIGURATION command so upon
gadget binding you can issue the command and program endpoints
according to the received gadget configuration.
Also, considering that PXA3XX processors include PXA27x-compatible
USB device controller it makes sense to develop a driver that
will support both processor families. Hopefully PXA3XX arch
support will be merged some day (the arch support has been already
submitted here, but I don't know about its current status).
Regards,
Dmitry
> ... then submit that. No epautoconfig updates, no
> patches to every gadget driver to cope with updated autoconfig.
>
> Once there's a basic working no-frills version merged, then we can
> talk about whether things in the rest of the stack should change
> to accomodate the bizarre concepts of this controller.
>
> - Dave
>
>
> [1] http://www.linux-usb.org/usbtest/
>
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [linux-usb-devel] [PATCH] PXA27x UDC driver.
2007-06-29 8:58 ` Rodolfo Giometti
@ 2007-06-29 9:33 ` Dmitry Krivoschekov
2007-06-30 14:25 ` David Brownell
1 sibling, 0 replies; 22+ messages in thread
From: Dmitry Krivoschekov @ 2007-06-29 9:33 UTC (permalink / raw)
To: Rodolfo Giometti
Cc: David Brownell, linux-kernel, Andrew Morton, Li Yang-r58472,
linux-usb-devel, linux-arm-kernel
Rodolfo Giometti wrote:
> On Thu, Jun 28, 2007 at 02:53:22PM -0700, David Brownell wrote:
>> Let's start with *JUST* a driver, not trying to update everything
>> else in the USB Gadget stack so that it looks like it's designed
>> specifically to handle all of Intel's design botches related to
>> endpoint config ... and work worse for essentially everything else.
>>
>> (Unlike pretty much every other vendor, Intel wanted hardware config
>> management. It was unusably buggy in pxa21x/25x/26x, and not much
>> better in pxa27x.)
>>
>>
>> So in technical terms, and to repeat what I've said before: just
>> configure it to act more like a PXA 25x chip (no altsettings) and
>> get it so it passes all the tests [1], modulo errata which have no
>> workarounds ... then submit that. No epautoconfig updates, no
>> patches to every gadget driver to cope with updated autoconfig.
>
> This looks interesting... as you alredy told this driver derives from
> an older one, I just maintained it till now.
>
> If I well understand I should remove usb_ep_autoconfig() and program
> into the controller only one (the default) configuration. Is that
> right?
You should leave the usb_ep_autoconfig() as is, i.e. do not introduce
any new parameters to it.
>
> Currently I tested the driver only with ether gadget,
Why have you submitted the limited driver then? You should test
the driver with all gadgets. Also, please report how it works
in DMA and PIO modes. And what transfer rate the driver achives
in both modes.
> but if I do as
> above, should I get the driver working with all gadgets automagically?
Probably no, but technically there is no reason to not support
all gadgets.
BTW, I suggest that you wait until PXA3XX arch support will be
merged into mainline kernel, then, pxa27x_udc-compatible driver
will probably be submitted too. Taking into account that
pxa27_udc have not been merged for years, I think extra
2-3 month is ok.
Regards,
Dmitry
> :)
>
> Thanks a lot,
>
> Rodolfo
>
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH] PXA27x UDC driver.
2007-06-28 10:36 [PATCH] PXA27x UDC driver Rodolfo Giometti
2007-06-28 11:15 ` Li Yang-r58472
@ 2007-06-29 17:04 ` Andy Isaacson
2007-07-01 14:55 ` Lennert Buytenhek
2007-07-01 15:00 ` Russell King - ARM Linux
3 siblings, 0 replies; 22+ messages in thread
From: Andy Isaacson @ 2007-06-29 17:04 UTC (permalink / raw)
To: Rodolfo Giometti; +Cc: linux-arm-kernel, linux-kernel, Andrew Morton
Thanks for taking the lead on this! I can't wait to have a sane PXA27x
gadget driver in mainline.
On Thu, Jun 28, 2007 at 12:36:20PM +0200, Rodolfo Giometti wrote:
> +config USB_GADGET_PXA27X
> + boolean "PXA 27x"
> + depends on ARCH_PXA && PXA27x
> + help
> + Intel's PXA 27x series XScale processors include an integrated
XScale is a Marvell product now, not Intel. How about
XScale PXA 27x processors (from Marvell, formerly Intel) include ...
> + Say "y" to link the driver statically, or "m" to build a
> + dynamically linked module called "pxa2xx_udc" and force all
> + gadget drivers to also be dynamically linked.
Please copy the boilerplate on this:
To compile this driver as a module, choose M here: the
module will be called pxa27x_udc.
(Note the fixed module name, too.)
> + if (!_ep || !ep->desc) {
> + DMSG("%s, %s not enabled\n", __FUNCTION__,
> + _ep ? ep->ep.name : NULL);
I wouldn't pass NULL to a printf-format-string-using function, and ':'
would be a more traditional separator than ','. (Many instances of the
latter.)
> + if (dcsr & DCSR_BUSERR) {
> + DCSR(dmach) = DCSR_BUSERR;
> + printk(KERN_ERR " Buss Error\n");
Extra space after ", and Bus is misspelled. This printk should include
as much information about the error as reasonable.
> +static int pxa27x_ep_fifo_status(struct usb_ep *_ep)
> +{
> + struct pxa27x_ep *ep;
> +
> + ep = container_of(_ep, struct pxa27x_ep, ep);
No reason not to write
struct pxa27x_ep *ep = container_of(_ep, struct pxa27x_ep, ep);
> + while (((*ep->reg_udccsr) & UDCCSR_BNE) != 0)
> + (void)*ep->reg_udcdr;
That looks funky. On x86 I think you'd want a cpu_relax() in the loop
body, but I don't know if that's necessary on ARM. At the very least,
there's no reason to waste every other volatile read, so you should
remove the ->reg_udcdr read outside of the condition. Instead, the
standard seems to be
while (((*ep->reg_udccsr) & UDCCSR_BNE) != 0)
;
> + *ep->reg_udccsr = UDCCSR_PC | UDCCSR_FST | UDCCSR_TRN
> + | (ep->ep_type == USB_ENDPOINT_XFER_ISOC)
> + ? 0 : UDCCSR_SST;
More parentheses and/or indentation to make it clear how the ?: nests
with |.
> +#define NAME_SIZE 18
If this code isn't killed by David Brownell's comments, please either
make this a local variable or move the define to the top of the file
(and give it a name that describes what name it's the size of).
> + DMSG("udccsr=0x%8x, udcbcr=0x%8x, udcdr=0x%8x,"
> + "udccr0=0x%8x\n",
> + (unsigned)pxa_ep->reg_udccsr,
> + (unsigned)pxa_ep->reg_udcbcr,
> + (unsigned)pxa_ep->reg_udcdr, (unsigned)pxa_ep->reg_udccr);
Print pointers using %p.
> +#if 0
> + tmp |= (pxa_ep->interface << UDCCONR_IN_S) & UDCCONR_IN;
> + tmp |= (pxa_ep->aisn << UDCCONR_AISN_S) & UDCCONR_AISN;
> +#else
> + tmp |= (0 << UDCCONR_IN_S) & UDCCONR_IN;
> + tmp |= (0 << UDCCONR_AISN_S) & UDCCONR_AISN;
> +#endif
This stuff is wierd. It would be slightly more sane to just have the
code commented out, with a comment explaining why.
> + name = kmalloc(NAME_SIZE, GFP_KERNEL);
> + if (!name) {
> + printk(KERN_ERR "%s: Error\n", __FUNCTION__);
Useless printk. Should probably propagate ERR_PTR(-ENOMEM) back up the
call tree instead. (Several instances.)
> +udc_proc_read(char *page, char **start, off_t off, int count,
> + int *eof, void *_dev)
> +{
[snip]
> + t = scnprintf(next, size,
> + "uicr %02X.%02X, usir %02X.%02x, ufnr %02X\n",
> + UDCICR1, UDCICR0, UDCISR1, UDCISR0, UDCFNR);
> + size -= t;
> + next += t;
This code will get a lot simpler if you use seq_printf instead.
> +#define create_proc_files() \
> + create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev)
> +#define remove_proc_files() \
> + remove_proc_entry(proc_node_name, NULL)
> +#else /* !UDC_PROC_FILE */
> +#define create_proc_files() do {} while (0)
> +#define remove_proc_files() do {} while (0)
> +#endif /* UDC_PROC_FILE */
The create_proc_files()/remove_proc_files() macros are nasty, and will
become unnecessary if you move the proc stuff to a separate file and use
Kconfig to optionally build it.
> +static DEVICE_ATTR(function, S_IRUGO, show_function, NULL);
Huh? This isn't used AFAICS.
> +static void udc_reinit(struct pxa27x_udc *dev)
> +{
> + u32 i;
No reason for this to be u32, use int. (Unless there's a significant
benefit in the generated code on ARM, perhaps.)
> + if (UDCCR & UDCCR_EMCE) {
> + printk(KERN_ERR
> + ": There are error in configuration, udc disabled\n");
Add the device name or some other identifier here. And there's probably
a missing return or goto.
> +#define CP15R0_VENDOR_MASK 0xffffe000
> +
> +#define CP15R0_XSCALE_VALUE 0x69054000 /* intel/arm/xscale */
These belong in a header file.
> +struct pxa27x_udc {
> + struct usb_gadget gadget;
> + struct usb_gadget_driver *driver;
> +
> + enum ep0_state ep0state;
> + struct udc_stats stats;
> + unsigned got_irq : 1,
> + got_disc : 1,
> + has_cfr : 1,
> + req_pending : 1,
> + req_std : 1,
> + req_config : 1;
> +
> +#define start_watchdog(dev) mod_timer(&dev->timer, jiffies + (HZ/200))
This define definitely doesn't belong here, and I don't think it belongs
in this header file at all -- or if it does, it needs a less generic
name.
> + struct timer_list timer;
> +
> + struct device *dev;
> + struct pxa2xx_udc_mach_info *mach;
> + u64 dma_mask;
> + struct pxa27x_ep ep [UDC_EP_NUM];
No space ^ here.
-andy
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [linux-usb-devel] [PATCH] PXA27x UDC driver.
2007-06-29 9:03 ` [linux-usb-devel] " Dmitry Krivoschekov
@ 2007-06-30 13:46 ` David Brownell
2007-07-02 11:42 ` Dmitry Krivoschekov
2007-07-09 13:51 ` Rodolfo Giometti
0 siblings, 2 replies; 22+ messages in thread
From: David Brownell @ 2007-06-30 13:46 UTC (permalink / raw)
To: Dmitry Krivoschekov
Cc: Rodolfo Giometti, linux-kernel, Andrew Morton, Li Yang-r58472,
linux-usb-devel, linux-arm-kernel
On Friday 29 June 2007, Dmitry Krivoschekov wrote:
> David Brownell wrote:
> > On Thursday 28 June 2007, Rodolfo Giometti wrote:
> >
> >> As suggest by Leo let me propose to you my new patch for PXA27x UDC
> >> support.
> >>
> >> Please, let me know what I have to do for kernel inclusion. :)
> >
> > Let's start with *JUST* a driver, not trying to update everything
> > else in the USB Gadget stack so that it looks like it's designed
> > specifically to handle all of Intel's design botches related to
> > endpoint config ... and work worse for essentially everything else.
> >
> > (Unlike pretty much every other vendor, Intel wanted hardware config
> > management. It was unusably buggy in pxa21x/25x/26x, and not much
> > better in pxa27x.)
> >
> >
> > So in technical terms, and to repeat what I've said before: just
> > configure it to act more like a PXA 25x chip (no altsettings) and
> > get it so it passes all the tests [1], modulo errata which have no
> > workarounds
>
> Other options are:
>
> 1. pre-program endpoints so the setting covers all gadget
> configurations, it seems it's feasible. The only appreciable
> change is, CDC Ethernet config number should be 3 instead of 1.
> It should not break anything.
ISTR looking at this in some detail a few years back, and
concluding that it wouldn't quite work. That was before
started to hear back from people that the pxa27x hardware
didn't really match its documentation, too ...
> 2. Implement a FAKE call of GET_CONFIGURATION command so upon
> gadget binding you can issue the command and program endpoints
> according to the received gadget configuration.
That would involve *multiple* such fake calls. Not the most
elegant of solutions, but ISTR using it in some other context.
But again, that presumes sane hardware, or at least hardware
that matches the docs. And people who've looked at this in
more pain than I have are consistent in telling me that pxa27x
endpoint configuration has problems that the docs and errata
do not describe. (If it were just one person, rather than four
different developers, I'd be somewhat skeptical.) Hence my
eventual conclusion (and advice) to just stop trying to use
that misfeature.
> Also, considering that PXA3XX processors include PXA27x-compatible
> USB device controller it makes sense to develop a driver that
> will support both processor families. Hopefully PXA3XX arch
> support will be merged some day (the arch support has been already
> submitted here, but I don't know about its current status).
Has someone actually signed up to develop and maintain such a
controller driver? If so, that would be a Fine Thing, but not
one I've heard rumored before. I've assumed that the best we'd
have is someone (Rodolfo?!) finally cleaning up a pxa27x version
so it could be merged.
- Dave
>
>
> Regards,
> Dmitry
>
> > ... then submit that. No epautoconfig updates, no
> > patches to every gadget driver to cope with updated autoconfig.
> >
> > Once there's a basic working no-frills version merged, then we can
> > talk about whether things in the rest of the stack should change
> > to accomodate the bizarre concepts of this controller.
> >
> > - Dave
> >
> >
> > [1] http://www.linux-usb.org/usbtest/
> >
>
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH] PXA27x UDC driver.
2007-06-29 8:58 ` Rodolfo Giometti
2007-06-29 9:33 ` [linux-usb-devel] " Dmitry Krivoschekov
@ 2007-06-30 14:25 ` David Brownell
1 sibling, 0 replies; 22+ messages in thread
From: David Brownell @ 2007-06-30 14:25 UTC (permalink / raw)
To: Rodolfo Giometti
Cc: linux-usb-devel, linux-arm-kernel, linux-kernel, Andrew Morton,
Li Yang-r58472
On Friday 29 June 2007, Rodolfo Giometti wrote:
> On Thu, Jun 28, 2007 at 02:53:22PM -0700, David Brownell wrote:
> >
> > Let's start with *JUST* a driver, not trying to update everything
> > else in the USB Gadget stack so that it looks like it's designed
> > specifically to handle all of Intel's design botches related to
> > endpoint config ... and work worse for essentially everything else.
> >
> > (Unlike pretty much every other vendor, Intel wanted hardware config
> > management. It was unusably buggy in pxa21x/25x/26x, and not much
> > better in pxa27x.)
> >
> >
> > So in technical terms, and to repeat what I've said before: just
> > configure it to act more like a PXA 25x chip (no altsettings) and
> > get it so it passes all the tests [1], modulo errata which have no
> > workarounds ... then submit that. No epautoconfig updates, no
> > patches to every gadget driver to cope with updated autoconfig.
>
> This looks interesting... as you alredy told this driver derives from
> an older one, I just maintained it till now.
>
> If I well understand I should remove usb_ep_autoconfig() and program
> into the controller only one (the default) configuration. Is that
> right?
Other than the fact that "configuration" means something very specific
in USB terms, so there would be three of them ... yes. That's what the
PXA2[156]x UDC hardware does.
> Currently I tested the driver only with ether gadget, but if I do as
> above, should I get the driver working with all gadgets automagically?
> :)
There's not much point in merging a driver that only works with one
of what are currently six gadget drivers ... others are on the way.
Yes, the point of working more like PXA2[156]x hardware is that we
know that kind of hardware setup works with everything ... while we
know that every attempt to use the PXA27x magic has failed on some
of those drivers. (By all reports, because of hardware bugs.)
- Dave
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [linux-usb-devel] [PATCH] PXA27x UDC driver.
2007-06-28 21:29 ` Andrew Morton
2007-06-28 22:44 ` David Brownell
@ 2007-06-30 14:28 ` David Brownell
2007-07-10 15:49 ` Rodolfo Giometti
2 siblings, 0 replies; 22+ messages in thread
From: David Brownell @ 2007-06-30 14:28 UTC (permalink / raw)
To: linux-usb-devel, Andrew Morton
Cc: Rodolfo Giometti, linux-kernel, Yang-r58472, linux-arm-kernel
On Thursday 28 June 2007, Andrew Morton wrote:
> > +#undef DISABLE_TEST_MODE
>
> enabling DISABLE_TEST_MODE seens to enable test mode. Confused.
Blame it on Intel. ISTR that early pxa2[156]x silicon had
something called "test mode". And a boatload of errata, with
a common thread in workarounds: using the "test mode" helped
many things work better, although it was neither necessary nor
sufficient. If one were to #define DISABLE_TEST_MODE, software
could experiment with other workarounds ... and maybe be able
to get the documented "double buffering" feature to work.
Later silicon stopped documenting "test mode" as such, effectively
making certain workarounds become the standard way to use the chip.
Which doesn't explain why pxa270 code has pxa2[156]x devel hooks,
but explains why pxa2[156]x code wants the hardware "test mode"
to be enabled ... except during developer experiments.
- Dave
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH] PXA27x UDC driver.
2007-06-28 10:36 [PATCH] PXA27x UDC driver Rodolfo Giometti
2007-06-28 11:15 ` Li Yang-r58472
2007-06-29 17:04 ` Andy Isaacson
@ 2007-07-01 14:55 ` Lennert Buytenhek
2007-07-01 15:00 ` Russell King - ARM Linux
3 siblings, 0 replies; 22+ messages in thread
From: Lennert Buytenhek @ 2007-07-01 14:55 UTC (permalink / raw)
To: Rodolfo Giometti; +Cc: linux-arm-kernel, Andrew Morton, linux-kernel
On Thu, Jun 28, 2007 at 12:36:20PM +0200, Rodolfo Giometti wrote:
> attached you can find new version of my patch for PXA27x UDC driver
> support against kernel 2.6.22-rc3 (but it applies also ro rc6).
>
> I'd like to know what I have to do in order to prepare this patch for
> kernel inclusion. It's time PXA27x has its UDC driver into linux! :)
Aren't there about 300 different versions of the PXA27x gadget
driver out there?
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH] PXA27x UDC driver.
2007-06-28 10:36 [PATCH] PXA27x UDC driver Rodolfo Giometti
` (2 preceding siblings ...)
2007-07-01 14:55 ` Lennert Buytenhek
@ 2007-07-01 15:00 ` Russell King - ARM Linux
2007-07-01 17:49 ` David Brownell
3 siblings, 1 reply; 22+ messages in thread
From: Russell King - ARM Linux @ 2007-07-01 15:00 UTC (permalink / raw)
To: Rodolfo Giometti; +Cc: linux-arm-kernel, Andrew Morton, linux-kernel
On Thu, Jun 28, 2007 at 12:36:20PM +0200, Rodolfo Giometti wrote:
> attached you can find new version of my patch for PXA27x UDC driver
> support against kernel 2.6.22-rc3 (but it applies also ro rc6).
>
> I'd like to know what I have to do in order to prepare this patch for
> kernel inclusion. It's time PXA27x has its UDC driver into linux! :)
We're moving the PXA kernel towards being able to be built to support
both PXA25x and PXA27x at the same time.
> diff --git a/arch/arm/mach-pxa/generic.c b/arch/arm/mach-pxa/generic.c
> index 64b08b7..7f390fd 100644
> --- a/arch/arm/mach-pxa/generic.c
> +++ b/arch/arm/mach-pxa/generic.c
> @@ -282,7 +282,11 @@ static struct resource pxa2xx_udc_resources[] = {
> static u64 udc_dma_mask = ~(u32)0;
>
> static struct platform_device udc_device = {
> +#ifdef CONFIG_PXA27x
> + .name = "pxa27x-udc",
> +#else
> .name = "pxa2xx-udc",
> +#endif
This precludes that aim.
> diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
> index 325bf7c..d4f5870 100644
> --- a/drivers/usb/gadget/ether.c
> +++ b/drivers/usb/gadget/ether.c
> @@ -257,10 +257,6 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
> #define DEV_CONFIG_CDC
> #endif
>
> -#ifdef CONFIG_USB_GADGET_PXA27X
> -#define DEV_CONFIG_CDC
> -#endif
> -
Good.
> #ifdef CONFIG_USB_GADGET_S3C2410
> #define DEV_CONFIG_CDC
> #endif
> @@ -292,6 +288,10 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
> #define DEV_CONFIG_SUBSET
> #endif
>
> +#ifdef CONFIG_USB_GADGET_PXA27X
> +#define DEV_CONFIG_SUBSET
> +#endif
> +
Bad - can we not make this runtime dependent?
> +#ifdef CONFIG_EMBEDDED
> +/* few strings, and little code to use them */
> +#undef DEBUG
Very very silly. So, if you want to turn on debugging, and also select
some features which are only accessible with EMBEDDED enabled, you have
to edit this file. No. Get rid of this. If people want to get rid of
the space used by debugging, they can just turn debugging off. No need
to make it any harder than that.
> +/* PXA cache needs flushing with DMA I/O (it's dma-incoherent), but there's
> + * no device-affinity and the heap works perfectly well for i/o buffers.
> + * It wastes much less memory than dma_alloc_coherent() would, and even
> + * prevents cacheline (32 bytes wide) sharing problems.
> + */
> +static void *pxa27x_ep_alloc_buffer(struct usb_ep *_ep, unsigned bytes,
> + dma_addr_t * dma, unsigned int gfp_flags)
> +{
> + char *retval;
> +
> + retval = kmalloc(bytes, gfp_flags & ~(__GFP_DMA | __GFP_HIGHMEM));
> + if (retval)
> +#ifdef USE_DMA
> + *dma = virt_to_bus(retval);
> +#else
> + *dma = (dma_addr_t) ~0;
> +#endif
Err, no. There's a perfectly good replacement. DMA pools. Please use
the provided infrastructure instead of inventing your own solution.
> + create_file_error:
> + driver->unbind(&dev->gadget);
> +
> + device_bind_error:
> + device_del(&dev->gadget.dev);
> +
> + device_add_error:
> + dev->driver = 0;
> + dev->gadget.dev.driver = 0;
Pointers which are cleared are set to NULL not zero. Zero is an integer.
NULL is a pointer. Don't confuse the two.
> + /* insist on Intel/ARM/XScale */
> + asm("mrc%? p15, 0, %0, c0, c0":"=r"(chiprev));
> + if ((chiprev & CP15R0_VENDOR_MASK) != CP15R0_XSCALE_VALUE) {
> + printk(KERN_ERR "%s: not XScale!\n", driver_name);
> + return -ENODEV;
> + }
Please use:
if ((processor_id & CP15R0_VENDOR_MASK) != CP15R0_XSCALE_VALUE)
it's already in a variable which is exported, so there's no point not
using it. Alternatively, use:
chiprev = read_cpuid(CPUID_ID);
> +#ifdef DEBUG
> +
> +static const char *state_name[] = {
> + "EP0_IDLE",
> + "EP0_IN_DATA_PHASE", "EP0_OUT_DATA_PHASE",
> + "EP0_END_XFER", "EP0_STALL"
> +};
> +
> +#define DMSG(stuff...) printk(KERN_ERR "udc: " stuff)
Is pr_debug() buggy?
> +
> +#ifdef VERBOSE
> +# define UDC_DEBUG DBG_VERBOSE
> +#else
> +# define UDC_DEBUG DBG_NORMAL
> +#endif
> +
> +static void __attribute__ ((__unused__))
> +dump_udccr(const char *label)
> +{
> + u32 udccr = UDCCR;
> + DMSG("%s 0x%08x =%s%s%s%s%s%s%s%s%s%s, con=%d,inter=%d,altinter=%d\n",
> + label, udccr,
> + (udccr & UDCCR_OEN) ? " oen":"",
> + (udccr & UDCCR_AALTHNP) ? " aalthnp":"",
> + (udccr & UDCCR_AHNP) ? " rem" : "",
> + (udccr & UDCCR_BHNP) ? " rstir" : "",
> + (udccr & UDCCR_DWRE) ? " dwre" : "",
> + (udccr & UDCCR_SMAC) ? " smac" : "",
> + (udccr & UDCCR_EMCE) ? " emce" : "",
> + (udccr & UDCCR_UDR) ? " udr" : "",
> + (udccr & UDCCR_UDA) ? " uda" : "",
> + (udccr & UDCCR_UDE) ? " ude" : "",
> + (udccr & UDCCR_ACN) >> UDCCR_ACN_S,
> + (udccr & UDCCR_AIN) >> UDCCR_AIN_S,
> + (udccr & UDCCR_AAISN)>> UDCCR_AAISN_S );
> +}
> +
> +static void __attribute__ ((__unused__))
> +dump_udccsr0(const char *label)
> +{
> + u32 udccsr0 = UDCCSR0;
> +
> + DMSG("%s %s 0x%08x =%s%s%s%s%s%s%s\n",
> + label, state_name[the_controller->ep0state], udccsr0,
> + (udccsr0 & UDCCSR0_SA) ? " sa" : "",
> + (udccsr0 & UDCCSR0_RNE) ? " rne" : "",
> + (udccsr0 & UDCCSR0_FST) ? " fst" : "",
> + (udccsr0 & UDCCSR0_SST) ? " sst" : "",
> + (udccsr0 & UDCCSR0_DME) ? " dme" : "",
> + (udccsr0 & UDCCSR0_IPR) ? " ipr" : "",
> + (udccsr0 & UDCCSR0_OPC) ? " opr" : "");
> +}
> +
> +static void __attribute__ ((__unused__))
> +dump_state(struct pxa27x_udc *dev)
> +{
> + unsigned i;
> +
> + DMSG("%s, udcicr %02X.%02X, udcsir %02X.%02x, udcfnr %02X\n",
> + state_name[dev->ep0state],
> + UDCICR1, UDCICR0, UDCISR1, UDCISR0, UDCFNR);
> + dump_udccr("udccr");
> +
> + if (!dev->driver) {
> + DMSG("no gadget driver bound\n");
> + return;
> + } else
> + DMSG("ep0 driver '%s'\n", dev->driver->driver.name);
> +
> +
> + dump_udccsr0 ("udccsr0");
> + DMSG("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 < UDC_EP_NUM; i++) {
> + if (dev->ep [i].desc == 0)
> + continue;
> + DMSG ("udccs%d = %02x\n", i, *dev->ep->reg_udccsr);
> + }
> +}
> +
> +#if 0
> +static void dump_regs(u8 ep)
> +{
> + DMSG("EP:%d UDCCSR:0x%08x UDCBCR:0x%08x\n UDCCR:0x%08x\n",
> + ep,UDCCSN(ep), UDCBCN(ep), UDCCN(ep));
> +}
> +static void dump_req (struct pxa27x_request *req)
> +{
> + struct usb_request *r = &req->req;
> +
> + DMSG("%s: buf:0x%08x length:%d dma:0x%08x actual:%d\n",
> + __FUNCTION__, (unsigned)r->buf, r->length,
> + r->dma, r->actual);
> +}
> +#endif
> +
> +#else
> +
> +#define DMSG(stuff...) do{}while(0)
> +
> +#define dump_udccr(x) do{}while(0)
> +#define dump_udccsr0(x) do{}while(0)
> +#define dump_state(x) do{}while(0)
> +
> +#define UDC_DEBUG ((unsigned)0)
> +
> +#endif
> +
> +#define DBG(lvl, stuff...) do{if ((lvl) <= UDC_DEBUG) DMSG(stuff);}while(0)
> +
> +#define WARN(stuff...) printk(KERN_WARNING "udc: " stuff)
> +#define INFO(stuff...) printk(KERN_INFO "udc: " stuff)
pr_info() ?
> +
> +
> +#endif /* __LINUX_USB_GADGET_PXA27X_H */
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH] PXA27x UDC driver.
2007-07-01 15:00 ` Russell King - ARM Linux
@ 2007-07-01 17:49 ` David Brownell
0 siblings, 0 replies; 22+ messages in thread
From: David Brownell @ 2007-07-01 17:49 UTC (permalink / raw)
To: linux-arm-kernel
Cc: Russell King - ARM Linux, Rodolfo Giometti, Andrew Morton, linux-kernel
On Sunday 01 July 2007, Russell King - ARM Linux wrote:
> >
> > +#ifdef CONFIG_USB_GADGET_PXA27X
> > +#define DEV_CONFIG_SUBSET
> > +#endif
> > +
>
> Bad - can we not make this runtime dependent?
The #define controls which descriptors are statically linked
into every object. I suppose someone could submit a patch which
does that differently ... I'm not keen on having lots of "will
never use" data bloating any driver, but it could be moved to
__initdata, at the cost of adding code to kmalloc (and kfree)
all the individual descriptors. Restricting code bloat to init
sections would still be worse than no bloat...
> > +static void *pxa27x_ep_alloc_buffer(struct usb_ep *_ep, unsigned bytes,
> > + dma_addr_t * dma, unsigned int gfp_flags)
> > +{
> > + ...
>
> Err, no. There's a perfectly good replacement. DMA pools. Please use
> the provided infrastructure instead of inventing your own solution.
This was cloned from code which predates dma pools. :)
Regardless, I'm planning to remove that interface from the gadget stack.
It's utility is far exceeded by the number of controller driver bugs
in that area. Plus, if any gadget driver needs such a mechanism, the
dma_pool stuff that now exists could be called directly.
> Pointers which are cleared are set to NULL not zero. Zero is an integer.
> NULL is a pointer. Don't confuse the two.
ISTR this class of error would be reported by sparse ("make check").
- Dave
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [linux-usb-devel] [PATCH] PXA27x UDC driver.
2007-06-30 13:46 ` David Brownell
@ 2007-07-02 11:42 ` Dmitry Krivoschekov
2007-07-09 13:51 ` Rodolfo Giometti
1 sibling, 0 replies; 22+ messages in thread
From: Dmitry Krivoschekov @ 2007-07-02 11:42 UTC (permalink / raw)
To: David Brownell
Cc: Rodolfo Giometti, linux-kernel, Andrew Morton, Li Yang-r58472,
linux-usb-devel, linux-arm-kernel
David Brownell wrote:
> On Friday 29 June 2007, Dmitry Krivoschekov wrote:
>> David Brownell wrote:
>>> On Thursday 28 June 2007, Rodolfo Giometti wrote:
>>>
>>>> As suggest by Leo let me propose to you my new patch for PXA27x UDC
>>>> support.
>>>>
>>>> Please, let me know what I have to do for kernel inclusion. :)
>>> Let's start with *JUST* a driver, not trying to update everything
>>> else in the USB Gadget stack so that it looks like it's designed
>>> specifically to handle all of Intel's design botches related to
>>> endpoint config ... and work worse for essentially everything else.
>>>
>>> (Unlike pretty much every other vendor, Intel wanted hardware config
>>> management. It was unusably buggy in pxa21x/25x/26x, and not much
>>> better in pxa27x.)
>>>
>>>
>>> So in technical terms, and to repeat what I've said before: just
>>> configure it to act more like a PXA 25x chip (no altsettings) and
>>> get it so it passes all the tests [1], modulo errata which have no
>>> workarounds
>> Other options are:
>>
>> 1. pre-program endpoints so the setting covers all gadget
>> configurations, it seems it's feasible. The only appreciable
>> change is, CDC Ethernet config number should be 3 instead of 1.
>> It should not break anything.
>
> ISTR looking at this in some detail a few years back, and
> concluding that it wouldn't quite work. That was before
> started to hear back from people that the pxa27x hardware
> didn't really match its documentation, too ...
>
>
>> 2. Implement a FAKE call of GET_CONFIGURATION command so upon
>> gadget binding you can issue the command and program endpoints
>> according to the received gadget configuration.
>
> That would involve *multiple* such fake calls. Not the most
> elegant of solutions, but ISTR using it in some other context.
>
> But again, that presumes sane hardware, or at least hardware
> that matches the docs. And people who've looked at this in
> more pain than I have are consistent in telling me that pxa27x
> endpoint configuration has problems that the docs and errata
> do not describe. (If it were just one person, rather than four
> different developers, I'd be somewhat skeptical.) Hence my
> eventual conclusion (and advice) to just stop trying to use
> that misfeature.
>
Actually it's nor a feature nor a misfeature, i.e. you can't just
disable it, so you can't stop using it. The only default
endpoint is ep0, other endpoints need to be configured. So,
if you want to use some static endpoints setting, you will
have to program this configuration and you can't avoid
endpoint configuration stage. That is, you may always
run into that configuration problems. Thankfully,
personally I haven't faced the problems. I used number 1 option
plus some logic that reshuffle ep list so appropriate
endpoints to be chosen by usb_ep_autoconfig(). It's not
a perfect solution but, it worked at least with three gadgets
g_ether, g_serial, g_file_storage. To be honest I dislike
the solution but it seems it's the only way to avoid changing
of gadget files.
If I could change gadget files, probably I'd modify gadgets
..._bind calls so on pxa27x they don't use usb_ep_autoconfig
but just pass USB descriptor to device driver but device
driver, in turn, configures the controller according to
the descriptor.
>
>> Also, considering that PXA3XX processors include PXA27x-compatible
>> USB device controller it makes sense to develop a driver that
>> will support both processor families. Hopefully PXA3XX arch
>> support will be merged some day (the arch support has been already
>> submitted here, but I don't know about its current status).
>
> Has someone actually signed up to develop and maintain such a
> controller driver? If so, that would be a Fine Thing, but not
> one I've heard rumored before. I've assumed that the best we'd
> have is someone (Rodolfo?!) finally cleaning up a pxa27x version
> so it could be merged.
>
>
If it was just a cleanup I think we would have the driver long time
ago. Unless we decide to make some appropriate changes to gadget files,
I doubt we will see the driver in mainline kernel.
Regards,
Dmitry
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [linux-usb-devel] [PATCH] PXA27x UDC driver.
2007-06-30 13:46 ` David Brownell
2007-07-02 11:42 ` Dmitry Krivoschekov
@ 2007-07-09 13:51 ` Rodolfo Giometti
2007-07-10 14:50 ` Vernon Sauder
2007-07-10 18:16 ` David Brownell
1 sibling, 2 replies; 22+ messages in thread
From: Rodolfo Giometti @ 2007-07-09 13:51 UTC (permalink / raw)
To: David Brownell
Cc: Dmitry Krivoschekov, linux-kernel, Andrew Morton, Li Yang-r58472,
linux-usb-devel, linux-arm-kernel
On Sat, Jun 30, 2007 at 06:46:14AM -0700, David Brownell wrote:
>
> Has someone actually signed up to develop and maintain such a
> controller driver? If so, that would be a Fine Thing, but not
> one I've heard rumored before. I've assumed that the best we'd
> have is someone (Rodolfo?!) finally cleaning up a pxa27x version
> so it could be merged.
That's what I wish to do... I wish cleaning up as much possible my
pxa27x version af the driver so we can have something common where we
can work on.
Sorry for the delay but in these next days I'll read back all your
suggestions and I'll provide a new patch ASAP.
However I would like avoiding to remove what I did on
usb_ep_autoconfig() since I got functional g_ether device with RNDIS
support... there could be another way to resolve this problem?
Thanks a lot,
Rodolfo
--
GNU/Linux Solutions e-mail: giometti@enneenne.com
Linux Device Driver giometti@gnudd.com
Embedded Systems giometti@linux.it
UNIX programming phone: +39 349 2432127
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [linux-usb-devel] [PATCH] PXA27x UDC driver.
2007-07-09 13:51 ` Rodolfo Giometti
@ 2007-07-10 14:50 ` Vernon Sauder
2007-07-10 18:16 ` David Brownell
1 sibling, 0 replies; 22+ messages in thread
From: Vernon Sauder @ 2007-07-10 14:50 UTC (permalink / raw)
To: Rodolfo Giometti
Cc: David Brownell, linux-usb-devel, linux-kernel, Andrew Morton,
Li Yang-r58472, linux-arm-kernel
Rodolfo Giometti wrote:
> That's what I wish to do... I wish cleaning up as much possible my
> pxa27x version af the driver so we can have something common where we
> can work on.
>
> Sorry for the delay but in these next days I'll read back all your
> suggestions and I'll provide a new patch ASAP.
>
> However I would like avoiding to remove what I did on
> usb_ep_autoconfig() since I got functional g_ether device with RNDIS
> support... there could be another way to resolve this problem?
>
> Thanks a lot,
>
> Rodolfo
>
Rodolfo,
I am eager to see an "official" USB driver. I have used a patch I found
on the web that was attributed to you for 2.6.20 in our recent project.
It patched cleanly and has worked fairly well. I am busy with other
projects right now but might be able to squeeze some time to test on our
board.
We have a custom PXA 270 board and we are moving to the PXA 320. I was
very concerned that we have no 270 driver in mainline and no hope of a
320 driver.
Regards,
Vern
InHand Electronics, Inc.
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH] PXA27x UDC driver.
2007-06-28 21:29 ` Andrew Morton
2007-06-28 22:44 ` David Brownell
2007-06-30 14:28 ` [linux-usb-devel] " David Brownell
@ 2007-07-10 15:49 ` Rodolfo Giometti
2007-07-10 16:16 ` Lothar Wassmann
2 siblings, 1 reply; 22+ messages in thread
From: Rodolfo Giometti @ 2007-07-10 15:49 UTC (permalink / raw)
To: Andrew Morton
Cc: David Brownell, linux-usb-devel, linux-arm-kernel, linux-kernel,
Li Yang-r58472
On Thu, Jun 28, 2007 at 02:29:49PM -0700, Andrew Morton wrote:
> >
> > +
> > +static int
> > +write_packet(volatile u32 * uddr, struct pxa27x_request *req, unsigned max)
>
> Please review Documentation/volatile-considered-harmful.txt
The attibute volatile here is necessary in order to avoid getting the
following warnings from the compiler:
CC drivers/usb/gadget/pxa27x_udc.o
drivers/usb/gadget/pxa27x_udc.c: In function `write_ep0_fifo':
drivers/usb/gadget/pxa27x_udc.c:512: warning: passing arg 1 of `write_packet' discards qualifiers from pointer target type
drivers/usb/gadget/pxa27x_udc.c: In function `pxa27x_ep_alloc':
drivers/usb/gadget/pxa27x_udc.c:1206: warning: assignment discards qualifiers from pointer target type
drivers/usb/gadget/pxa27x_udc.c:1207: warning: assignment discards qualifiers from pointer target type
drivers/usb/gadget/pxa27x_udc.c:1208: warning: assignment discards qualifiers from pointer target type
drivers/usb/gadget/pxa27x_udc.c:1209: warning: assignment discards qualifiers from pointer target type
drivers/usb/gadget/pxa27x_udc.c: At top level:
drivers/usb/gadget/pxa27x_udc.c:2159: warning: initialization discards qualifiers from pointer target type
drivers/usb/gadget/pxa27x_udc.c:2160: warning: initialization discards qualifiers from pointer target type
This because the variables reg_* are assigned as:
.reg_udccsr = &UDCCSR0
where UDCCSR0 are defined as:
#define UDCCSR0 __REG(0x40600100) /* UDC Control/Status register - Endpoint 0 */
and:
define __REG(x) (*((volatile u32 *)io_p2v(x)))
Do you know how I can resolve this?
Thanks in advance,
Rodolfo
--
GNU/Linux Solutions e-mail: giometti@enneenne.com
Linux Device Driver giometti@gnudd.com
Embedded Systems giometti@linux.it
UNIX programming phone: +39 349 2432127
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH] PXA27x UDC driver.
2007-07-10 15:49 ` Rodolfo Giometti
@ 2007-07-10 16:16 ` Lothar Wassmann
0 siblings, 0 replies; 22+ messages in thread
From: Lothar Wassmann @ 2007-07-10 16:16 UTC (permalink / raw)
To: Rodolfo Giometti
Cc: Andrew Morton, linux-kernel, Li Yang-r58472, linux-usb-devel,
linux-arm-kernel
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 823 bytes --]
Hi,
> > > +static int
> > > +write_packet(volatile u32 * uddr, struct pxa27x_request *req, unsigned max)
> >
> > Please review Documentation/volatile-considered-harmful.txt
>
> The attibute volatile here is necessary in order to avoid getting the
> following warnings from the compiler:
>
[...]
>
> Do you know how I can resolve this?
>
ioremap() the memory area for the UDC registers and use an __iomem
cookie.
Lothar Wassmann
--
___________________________________________________________
Ka-Ro electronics GmbH | Pascalstraße 22 | D - 52076 Aachen
Phone: +49 2408 1402-0 | Fax: +49 2408 1402-10
Geschäftsführer: Matthias Kaussen, Rolf Rosenstein
Handelsregistereintrag: Amtsgericht Aachen, HRB 4996
www.karo-electronics.de | info@karo-electronics.de
___________________________________________________________
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [linux-usb-devel] [PATCH] PXA27x UDC driver.
2007-07-09 13:51 ` Rodolfo Giometti
2007-07-10 14:50 ` Vernon Sauder
@ 2007-07-10 18:16 ` David Brownell
1 sibling, 0 replies; 22+ messages in thread
From: David Brownell @ 2007-07-10 18:16 UTC (permalink / raw)
To: Rodolfo Giometti
Cc: Dmitry Krivoschekov, linux-kernel, Andrew Morton, Li Yang-r58472,
linux-usb-devel, linux-arm-kernel
On Monday 09 July 2007, Rodolfo Giometti wrote:
> On Sat, Jun 30, 2007 at 06:46:14AM -0700, David Brownell wrote:
> >
> > Has someone actually signed up to develop and maintain such a
> > controller driver? If so, that would be a Fine Thing, but not
> > one I've heard rumored before. I've assumed that the best we'd
> > have is someone (Rodolfo?!) finally cleaning up a pxa27x version
> > so it could be merged.
>
> That's what I wish to do... I wish cleaning up as much possible my
> pxa27x version af the driver so we can have something common where we
> can work on.
Good!
> Sorry for the delay but in these next days I'll read back all your
> suggestions and I'll provide a new patch ASAP.
>
> However I would like avoiding to remove what I did on
> usb_ep_autoconfig() since I got functional g_ether device with RNDIS
> support... there could be another way to resolve this problem?
I *really* don't want to change the whole gadget stack just to
make one excessively quirky bit of hardware work with it ... when
we know there's a simple solution that doesn't involve that.
(Changing the whole gadget stack would involve retesting a boatload
of systems... not all of which are easily tested. Plus, those changes
would need far more review than they can get just now.)
So for now, just do what I outlined before: set up the pxa27x UDC
to resemble the pxa2[156] chips: a bunch of bulk endpoint with no
altsettings and acting the same in all three hardware-supported configs.
That will support RNDIS. In fact the only thing it _won't_ handle is
the "real CDC Ethernet" mode.
Once that's working, we can revisit the issue of how to configure
endpoint hardware ... focussing on just that issue, and with enough
flexibility that non-pxa27x hardware can also be addressed.
- Dave
^ permalink raw reply [flat|nested] 22+ messages in thread
end of thread, other threads:[~2007-07-10 18:17 UTC | newest]
Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-06-28 10:36 [PATCH] PXA27x UDC driver Rodolfo Giometti
2007-06-28 11:15 ` Li Yang-r58472
2007-06-28 14:12 ` Rodolfo Giometti
2007-06-28 21:29 ` Andrew Morton
2007-06-28 22:44 ` David Brownell
2007-06-30 14:28 ` [linux-usb-devel] " David Brownell
2007-07-10 15:49 ` Rodolfo Giometti
2007-07-10 16:16 ` Lothar Wassmann
2007-06-28 21:53 ` David Brownell
2007-06-29 8:58 ` Rodolfo Giometti
2007-06-29 9:33 ` [linux-usb-devel] " Dmitry Krivoschekov
2007-06-30 14:25 ` David Brownell
2007-06-29 9:03 ` [linux-usb-devel] " Dmitry Krivoschekov
2007-06-30 13:46 ` David Brownell
2007-07-02 11:42 ` Dmitry Krivoschekov
2007-07-09 13:51 ` Rodolfo Giometti
2007-07-10 14:50 ` Vernon Sauder
2007-07-10 18:16 ` David Brownell
2007-06-29 17:04 ` Andy Isaacson
2007-07-01 14:55 ` Lennert Buytenhek
2007-07-01 15:00 ` Russell King - ARM Linux
2007-07-01 17:49 ` David Brownell
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).