From f1407d5c66240b33d11a7f1a41d55ccf6a9d7647 Mon Sep 17 00:00:00 2001
From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Date: Mon, 4 Apr 2011 13:44:59 +0900
Subject: usb: renesas_usbhs: Add Renesas USBHS common code

Renesas SuperH has USBHS IP which can switch Host / Function.
This driver is designed so that Host / Function may dynamically change.
This patch add usb/renesas_usbhs and common code for SuperH USBHS.

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/Kconfig                |   2 +
 drivers/usb/renesas_usbhs/Kconfig  |  15 +
 drivers/usb/renesas_usbhs/Makefile |   7 +
 drivers/usb/renesas_usbhs/common.c | 394 +++++++++++++++++
 drivers/usb/renesas_usbhs/common.h | 225 ++++++++++
 drivers/usb/renesas_usbhs/mod.c    | 261 +++++++++++
 drivers/usb/renesas_usbhs/mod.h    | 106 +++++
 drivers/usb/renesas_usbhs/pipe.c   | 880 +++++++++++++++++++++++++++++++++++++
 drivers/usb/renesas_usbhs/pipe.h   | 105 +++++
 9 files changed, 1995 insertions(+)
 create mode 100644 drivers/usb/renesas_usbhs/Kconfig
 create mode 100644 drivers/usb/renesas_usbhs/Makefile
 create mode 100644 drivers/usb/renesas_usbhs/common.c
 create mode 100644 drivers/usb/renesas_usbhs/common.h
 create mode 100644 drivers/usb/renesas_usbhs/mod.c
 create mode 100644 drivers/usb/renesas_usbhs/mod.h
 create mode 100644 drivers/usb/renesas_usbhs/pipe.c
 create mode 100644 drivers/usb/renesas_usbhs/pipe.h

(limited to 'drivers/usb')

diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 41b6e51188e4..d299906e4f00 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -115,6 +115,8 @@ source "drivers/usb/host/Kconfig"
 
 source "drivers/usb/musb/Kconfig"
 
+source "drivers/usb/renesas_usbhs/Kconfig"
+
 source "drivers/usb/class/Kconfig"
 
 source "drivers/usb/storage/Kconfig"
diff --git a/drivers/usb/renesas_usbhs/Kconfig b/drivers/usb/renesas_usbhs/Kconfig
new file mode 100644
index 000000000000..481490e5500a
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/Kconfig
@@ -0,0 +1,15 @@
+#
+# Renesas USB Controller Drivers
+#
+
+config USB_RENESAS_USBHS
+	tristate 'Renesas USBHS controller'
+	default n
+	help
+	   Renesas USBHS is a discrete USB host and peripheral controller chip
+	   that supports both full and high speed USB 2.0 data transfers.
+	   It has nine or more configurable endpoints, and endpoint zero.
+
+	   Say "y" to link the driver statically, or "m" to build a
+	   dynamically linked module called "renesas_usbhs" and force all
+	   gadget drivers to also be dynamically linked.
diff --git a/drivers/usb/renesas_usbhs/Makefile b/drivers/usb/renesas_usbhs/Makefile
new file mode 100644
index 000000000000..d76f3dd3b9d1
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/Makefile
@@ -0,0 +1,7 @@
+#
+# for Renesas USB
+#
+
+obj-$(CONFIG_USB_RENESAS_USBHS)	+= renesas_usbhs.o
+
+renesas_usbhs-y			:= common.o mod.o pipe.o
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
new file mode 100644
index 000000000000..d9ad60d1c156
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -0,0 +1,394 @@
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include "./common.h"
+
+/*
+ * platform call back
+ *
+ * renesas usb support platform callback function.
+ * Below macro call it.
+ * if platform doesn't have callback, it return 0 (no error)
+ */
+#define usbhs_platform_call(priv, func, args...)\
+	(!(priv) ? -ENODEV :			\
+	 !((priv)->pfunc->func) ? 0 :		\
+	 (priv)->pfunc->func(args))
+
+/*
+ *		common functions
+ */
+u16 usbhs_read(struct usbhs_priv *priv, u32 reg)
+{
+	return ioread16(priv->base + reg);
+}
+
+void usbhs_write(struct usbhs_priv *priv, u32 reg, u16 data)
+{
+	iowrite16(data, priv->base + reg);
+}
+
+void usbhs_bset(struct usbhs_priv *priv, u32 reg, u16 mask, u16 data)
+{
+	u16 val = usbhs_read(priv, reg);
+
+	val &= ~mask;
+	val |= data & mask;
+
+	usbhs_write(priv, reg, val);
+}
+
+/*
+ *		syscfg functions
+ */
+void usbhs_sys_clock_ctrl(struct usbhs_priv *priv, int enable)
+{
+	usbhs_bset(priv, SYSCFG, SCKE, enable ? SCKE : 0);
+}
+
+void usbhs_sys_hispeed_ctrl(struct usbhs_priv *priv, int enable)
+{
+	usbhs_bset(priv, SYSCFG, HSE, enable ? HSE : 0);
+}
+
+void usbhs_sys_usb_ctrl(struct usbhs_priv *priv, int enable)
+{
+	usbhs_bset(priv, SYSCFG, USBE, enable ? USBE : 0);
+}
+
+void usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable)
+{
+	u16 mask = DCFM | DRPD | DPRPU;
+	u16 val  = DCFM | DRPD;
+
+	/*
+	 * if enable
+	 *
+	 * - select Host mode
+	 * - D+ Line/D- Line Pull-down
+	 */
+	usbhs_bset(priv, SYSCFG, mask, enable ? val : 0);
+}
+
+void usbhs_sys_function_ctrl(struct usbhs_priv *priv, int enable)
+{
+	u16 mask = DCFM | DRPD | DPRPU;
+	u16 val  = DPRPU;
+
+	/*
+	 * if enable
+	 *
+	 * - select Function mode
+	 * - D+ Line Pull-up
+	 */
+	usbhs_bset(priv, SYSCFG, mask, enable ? val : 0);
+}
+
+/*
+ *		frame functions
+ */
+int usbhs_frame_get_num(struct usbhs_priv *priv)
+{
+	return usbhs_read(priv, FRMNUM) & FRNM_MASK;
+}
+
+/*
+ *		local functions
+ */
+static struct usbhs_priv *usbhsc_pdev_to_priv(struct platform_device *pdev)
+{
+	return dev_get_drvdata(&pdev->dev);
+}
+
+static void usbhsc_bus_ctrl(struct usbhs_priv *priv, int enable)
+{
+	int wait = usbhs_get_dparam(priv, buswait_bwait);
+	u16 data = 0;
+
+	if (enable) {
+		/* set bus wait if platform have */
+		if (wait)
+			usbhs_bset(priv, BUSWAIT, 0x000F, wait);
+	}
+	usbhs_write(priv, DVSTCTR, data);
+}
+
+/*
+ *		platform default param
+ */
+static u32 usbhsc_default_pipe_type[] = {
+		USB_ENDPOINT_XFER_CONTROL,
+		USB_ENDPOINT_XFER_ISOC,
+		USB_ENDPOINT_XFER_ISOC,
+		USB_ENDPOINT_XFER_BULK,
+		USB_ENDPOINT_XFER_BULK,
+		USB_ENDPOINT_XFER_BULK,
+		USB_ENDPOINT_XFER_INT,
+		USB_ENDPOINT_XFER_INT,
+		USB_ENDPOINT_XFER_INT,
+		USB_ENDPOINT_XFER_INT,
+};
+
+/*
+ *		driver callback functions
+ */
+static void usbhsc_notify_hotplug(struct work_struct *work)
+{
+	struct usbhs_priv *priv = container_of(work,
+					       struct usbhs_priv,
+					       notify_hotplug_work);
+	struct platform_device *pdev = usbhs_priv_to_pdev(priv);
+	struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+	int id;
+	int enable;
+	int ret;
+
+	/*
+	 * get vbus status from platform
+	 */
+	enable = usbhs_platform_call(priv, get_vbus, pdev);
+
+	/*
+	 * get id from platform
+	 */
+	id = usbhs_platform_call(priv, get_id, pdev);
+
+	if (enable && !mod) {
+		ret = usbhs_mod_change(priv, id);
+		if (ret < 0)
+			return;
+
+		dev_dbg(&pdev->dev, "%s enable\n", __func__);
+
+		/* enable PM */
+		pm_runtime_get_sync(&pdev->dev);
+
+		/* USB on */
+		usbhs_sys_clock_ctrl(priv, enable);
+		usbhsc_bus_ctrl(priv, enable);
+
+		/* module start */
+		usbhs_mod_call(priv, start, priv);
+
+	} else if (!enable && mod) {
+		dev_dbg(&pdev->dev, "%s disable\n", __func__);
+
+		/* module stop */
+		usbhs_mod_call(priv, stop, priv);
+
+		/* USB off */
+		usbhsc_bus_ctrl(priv, enable);
+		usbhs_sys_clock_ctrl(priv, enable);
+
+		/* disable PM */
+		pm_runtime_put_sync(&pdev->dev);
+
+		usbhs_mod_change(priv, -1);
+
+		/* reset phy for next connection */
+		usbhs_platform_call(priv, phy_reset, pdev);
+	}
+}
+
+static int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev)
+{
+	struct usbhs_priv *priv = usbhsc_pdev_to_priv(pdev);
+
+	/*
+	 * This functions will be called in interrupt.
+	 * To make sure safety context,
+	 * use workqueue for usbhs_notify_hotplug
+	 */
+	schedule_work(&priv->notify_hotplug_work);
+	return 0;
+}
+
+/*
+ *		platform functions
+ */
+static int __devinit usbhs_probe(struct platform_device *pdev)
+{
+	struct renesas_usbhs_platform_info *info = pdev->dev.platform_data;
+	struct renesas_usbhs_driver_callback *dfunc;
+	struct usbhs_priv *priv;
+	struct resource *res;
+	unsigned int irq;
+	int ret;
+
+	/* check platform information */
+	if (!info ||
+	    !info->platform_callback.get_id ||
+	    !info->platform_callback.get_vbus) {
+		dev_err(&pdev->dev, "no platform information\n");
+		return -EINVAL;
+	}
+
+	/* platform data */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	irq = platform_get_irq(pdev, 0);
+	if (!res || (int)irq <= 0) {
+		dev_err(&pdev->dev, "Not enough Renesas USB platform resources.\n");
+		return -ENODEV;
+	}
+
+	/* usb private data */
+	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		dev_err(&pdev->dev, "Could not allocate priv\n");
+		return -ENOMEM;
+	}
+
+	priv->base = ioremap_nocache(res->start, resource_size(res));
+	if (!priv->base) {
+		dev_err(&pdev->dev, "ioremap error.\n");
+		ret = -ENOMEM;
+		goto probe_end_kfree;
+	}
+
+	/*
+	 * care platform info
+	 */
+	priv->pfunc	= &info->platform_callback;
+	priv->dparam	= &info->driver_param;
+
+	/* set driver callback functions for platform */
+	dfunc			= &info->driver_callback;
+	dfunc->notify_hotplug	= usbhsc_drvcllbck_notify_hotplug;
+
+	/* set default param if platform doesn't have */
+	if (!priv->dparam->pipe_type) {
+		priv->dparam->pipe_type = usbhsc_default_pipe_type;
+		priv->dparam->pipe_size = ARRAY_SIZE(usbhsc_default_pipe_type);
+	}
+
+	/*
+	 * priv settings
+	 */
+	priv->irq	= irq;
+	priv->pdev	= pdev;
+	INIT_WORK(&priv->notify_hotplug_work, usbhsc_notify_hotplug);
+	spin_lock_init(usbhs_priv_to_lock(priv));
+
+	/* call pipe and module init */
+	ret = usbhs_pipe_probe(priv);
+	if (ret < 0)
+		goto probe_end_mod_exit;
+
+	ret = usbhs_mod_probe(priv);
+	if (ret < 0)
+		goto probe_end_iounmap;
+
+	/* dev_set_drvdata should be called after usbhs_mod_init */
+	dev_set_drvdata(&pdev->dev, priv);
+
+	/*
+	 * deviece reset here because
+	 * USB device might be used in boot loader.
+	 */
+	usbhs_sys_clock_ctrl(priv, 0);
+
+	/*
+	 * platform call
+	 *
+	 * USB phy setup might depend on CPU/Board.
+	 * If platform has its callback functions,
+	 * call it here.
+	 */
+	ret = usbhs_platform_call(priv, hardware_init, pdev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "platform prove failed.\n");
+		goto probe_end_pipe_exit;
+	}
+
+	/* reset phy for connection */
+	usbhs_platform_call(priv, phy_reset, pdev);
+
+	/*
+	 * manual call notify_hotplug for cold plug
+	 */
+	pm_runtime_enable(&pdev->dev);
+	ret = usbhsc_drvcllbck_notify_hotplug(pdev);
+	if (ret < 0)
+		goto probe_end_call_remove;
+
+	dev_info(&pdev->dev, "probed\n");
+
+	return ret;
+
+probe_end_call_remove:
+	usbhs_platform_call(priv, hardware_exit, pdev);
+probe_end_pipe_exit:
+	usbhs_pipe_remove(priv);
+probe_end_mod_exit:
+	usbhs_mod_remove(priv);
+probe_end_iounmap:
+	iounmap(priv->base);
+probe_end_kfree:
+	kfree(priv);
+
+	dev_info(&pdev->dev, "probe failed\n");
+
+	return ret;
+}
+
+static int __devexit usbhs_remove(struct platform_device *pdev)
+{
+	struct usbhs_priv *priv = usbhsc_pdev_to_priv(pdev);
+
+	dev_dbg(&pdev->dev, "usb remove\n");
+
+	pm_runtime_disable(&pdev->dev);
+
+	usbhsc_bus_ctrl(priv, 0);
+
+	usbhs_platform_call(priv, hardware_exit, pdev);
+	usbhs_pipe_remove(priv);
+	usbhs_mod_remove(priv);
+	iounmap(priv->base);
+	kfree(priv);
+
+	return 0;
+}
+
+static struct platform_driver renesas_usbhs_driver = {
+	.driver		= {
+		.name	= "renesas_usbhs",
+	},
+	.probe		= usbhs_probe,
+	.remove		= __devexit_p(usbhs_remove),
+};
+
+static int __init usbhs_init(void)
+{
+	return platform_driver_register(&renesas_usbhs_driver);
+}
+
+static void __exit usbhs_exit(void)
+{
+	platform_driver_unregister(&renesas_usbhs_driver);
+}
+
+module_init(usbhs_init);
+module_exit(usbhs_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Renesas USB driver");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h
new file mode 100644
index 000000000000..f1a2b62f93f9
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/common.h
@@ -0,0 +1,225 @@
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#ifndef RENESAS_USB_DRIVER_H
+#define RENESAS_USB_DRIVER_H
+
+#include <linux/platform_device.h>
+#include <linux/usb/renesas_usbhs.h>
+
+struct usbhs_priv;
+
+#include "./mod.h"
+#include "./pipe.h"
+
+/*
+ *
+ *		register define
+ *
+ */
+#define SYSCFG		0x0000
+#define BUSWAIT		0x0002
+#define DVSTCTR		0x0008
+#define CFIFO		0x0014
+#define CFIFOSEL	0x0020
+#define CFIFOCTR	0x0022
+#define INTENB0		0x0030
+#define INTENB1		0x0032
+#define BRDYENB		0x0036
+#define NRDYENB		0x0038
+#define BEMPENB		0x003A
+#define INTSTS0		0x0040
+#define INTSTS1		0x0042
+#define BRDYSTS		0x0046
+#define NRDYSTS		0x0048
+#define BEMPSTS		0x004A
+#define FRMNUM		0x004C
+#define USBREQ		0x0054	/* USB request type register */
+#define USBVAL		0x0056	/* USB request value register */
+#define USBINDX		0x0058	/* USB request index register */
+#define USBLENG		0x005A	/* USB request length register */
+#define DCPCFG		0x005C
+#define DCPMAXP		0x005E
+#define DCPCTR		0x0060
+#define PIPESEL		0x0064
+#define PIPECFG		0x0068
+#define PIPEBUF		0x006A
+#define PIPEMAXP	0x006C
+#define PIPEPERI	0x006E
+#define PIPEnCTR	0x0070
+
+/* SYSCFG */
+#define SCKE	(1 << 10)	/* USB Module Clock Enable */
+#define HSE	(1 << 7)	/* High-Speed Operation Enable */
+#define DCFM	(1 << 6)	/* Controller Function Select */
+#define DRPD	(1 << 5)	/* D+ Line/D- Line Resistance Control */
+#define DPRPU	(1 << 4)	/* D+ Line Resistance Control */
+#define USBE	(1 << 0)	/* USB Module Operation Enable */
+
+/* DVSTCTR */
+#define EXTLP	(1 << 10)	/* Controls the EXTLP pin output state */
+#define PWEN	(1 << 9)	/* Controls the PWEN pin output state */
+#define RHST	(0x7)		/* Reset Handshake */
+#define  RHST_LOW_SPEED  1	/* Low-speed connection */
+#define  RHST_FULL_SPEED 2	/* Full-speed connection */
+#define  RHST_HIGH_SPEED 3	/* High-speed connection */
+
+/* CFIFOSEL */
+#define MBW_32	(0x2 << 10)	/* CFIFO Port Access Bit Width */
+
+/* CFIFOCTR */
+#define BVAL	(1 << 15)	/* Buffer Memory Enable Flag */
+#define BCLR	(1 << 14)	/* CPU buffer clear */
+#define FRDY	(1 << 13)	/* FIFO Port Ready */
+#define DTLN_MASK (0x0FFF)	/* Receive Data Length */
+
+/* INTENB0 */
+#define VBSE	(1 << 15)	/* Enable IRQ VBUS_0 and VBUSIN_0 */
+#define RSME	(1 << 14)	/* Enable IRQ Resume */
+#define SOFE	(1 << 13)	/* Enable IRQ Frame Number Update */
+#define DVSE	(1 << 12)	/* Enable IRQ Device State Transition */
+#define CTRE	(1 << 11)	/* Enable IRQ Control Stage Transition */
+#define BEMPE	(1 << 10)	/* Enable IRQ Buffer Empty */
+#define NRDYE	(1 << 9)	/* Enable IRQ Buffer Not Ready Response */
+#define BRDYE	(1 << 8)	/* Enable IRQ Buffer Ready */
+
+/* INTENB1 */
+#define BCHGE	(1 << 14)	/* USB Bus Change Interrupt Enable */
+#define DTCHE	(1 << 12)	/* Disconnection Detect Interrupt Enable */
+#define ATTCHE	(1 << 11)	/* Connection Detect Interrupt Enable */
+#define EOFERRE	(1 << 6)	/* EOF Error Detect Interrupt Enable */
+#define SIGNE	(1 << 5)	/* Setup Transaction Error Interrupt Enable */
+#define SACKE	(1 << 4)	/* Setup Transaction ACK Interrupt Enable */
+
+/* INTSTS0 */
+#define DVST	(1 << 12)	/* Device State Transition Interrupt Status */
+#define CTRT	(1 << 11)	/* Control Stage Interrupt Status */
+#define BEMP	(1 << 10)	/* Buffer Empty Interrupt Status */
+#define BRDY	(1 << 8)	/* Buffer Ready Interrupt Status */
+#define VBSTS	(1 << 7)	/* VBUS_0 and VBUSIN_0 Input Status */
+#define VALID	(1 << 3)	/* USB Request Receive */
+
+#define DVSQ_MASK		(0x3 << 4)	/* Device State */
+#define  POWER_STATE		(0 << 4)
+#define  DEFAULT_STATE		(1 << 4)
+#define  ADDRESS_STATE		(2 << 4)
+#define  CONFIGURATION_STATE	(3 << 4)
+
+#define CTSQ_MASK		(0x7)	/* Control Transfer Stage */
+#define  IDLE_SETUP_STAGE	0	/* Idle stage or setup stage */
+#define  READ_DATA_STAGE	1	/* Control read data stage */
+#define  READ_STATUS_STAGE	2	/* Control read status stage */
+#define  WRITE_DATA_STAGE	3	/* Control write data stage */
+#define  WRITE_STATUS_STAGE	4	/* Control write status stage */
+#define  NODATA_STATUS_STAGE	5	/* Control write NoData status stage */
+#define  SEQUENCE_ERROR		6	/* Control transfer sequence error */
+
+/* PIPECFG */
+/* DCPCFG */
+#define TYPE_NONE	(0 << 14)	/* Transfer Type */
+#define TYPE_BULK	(1 << 14)
+#define TYPE_INT	(2 << 14)
+#define TYPE_ISO	(3 << 14)
+#define DBLB		(1 << 9)	/* Double Buffer Mode */
+#define SHTNAK		(1 << 7)	/* Pipe Disable in Transfer End */
+#define DIR_OUT		(1 << 4)	/* Transfer Direction */
+
+/* PIPEMAXP */
+/* DCPMAXP */
+#define DEVSEL_MASK	(0xF << 12)	/* Device Select */
+#define DCP_MAXP_MASK	(0x7F)
+#define PIPE_MAXP_MASK	(0x7FF)
+
+/* PIPEBUF */
+#define BUFSIZE_SHIFT	10
+#define BUFSIZE_MASK	(0x1F << BUFSIZE_SHIFT)
+#define BUFNMB_MASK	(0xFF)
+
+/* PIPEnCTR */
+/* DCPCTR */
+#define BSTS		(1 << 15)	/* Buffer Status */
+#define CSSTS		(1 << 12)	/* CSSTS Status */
+#define SQCLR		(1 << 8)	/* Toggle Bit Clear */
+#define	ACLRM		(1 << 9)	/* Buffer Auto-Clear Mode */
+#define PBUSY		(1 << 5)	/* Pipe Busy */
+#define PID_MASK	(0x3)		/* Response PID */
+#define  PID_NAK	0
+#define  PID_BUF	1
+#define  PID_STALL10	2
+#define  PID_STALL11	3
+
+#define CCPL		(1 << 2)	/* Control Transfer End Enable */
+
+/* FRMNUM */
+#define FRNM_MASK	(0x7FF)
+
+/*
+ *		struct
+ */
+struct usbhs_priv {
+
+	void __iomem *base;
+	unsigned int irq;
+
+	struct renesas_usbhs_platform_callback	*pfunc;
+	struct renesas_usbhs_driver_param	*dparam;
+
+	struct work_struct notify_hotplug_work;
+	struct platform_device *pdev;
+
+	spinlock_t		lock;
+
+	/*
+	 * module control
+	 */
+	struct usbhs_mod_info mod_info;
+
+	/*
+	 * pipe control
+	 */
+	struct usbhs_pipe_info pipe_info;
+};
+
+/*
+ * common
+ */
+u16 usbhs_read(struct usbhs_priv *priv, u32 reg);
+void usbhs_write(struct usbhs_priv *priv, u32 reg, u16 data);
+void usbhs_bset(struct usbhs_priv *priv, u32 reg, u16 mask, u16 data);
+
+/*
+ * sysconfig
+ */
+void usbhs_sys_clock_ctrl(struct usbhs_priv *priv, int enable);
+void usbhs_sys_hispeed_ctrl(struct usbhs_priv *priv, int enable);
+void usbhs_sys_usb_ctrl(struct usbhs_priv *priv, int enable);
+void usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable);
+void usbhs_sys_function_ctrl(struct usbhs_priv *priv, int enable);
+
+/*
+ * frame
+ */
+int usbhs_frame_get_num(struct usbhs_priv *priv);
+
+/*
+ * data
+ */
+#define usbhs_get_dparam(priv, param)	(priv->dparam->param)
+#define usbhs_priv_to_pdev(priv)	(priv->pdev)
+#define usbhs_priv_to_dev(priv)		(&priv->pdev->dev)
+#define usbhs_priv_to_lock(priv)	(&priv->lock)
+
+#endif /* RENESAS_USB_DRIVER_H */
diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c
new file mode 100644
index 000000000000..4a3398484cd7
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/mod.c
@@ -0,0 +1,261 @@
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#include <linux/interrupt.h>
+
+#include "./common.h"
+#include "./mod.h"
+
+#define usbhs_priv_to_modinfo(priv) (&priv->mod_info)
+
+/*
+ *		host / gadget functions
+ *
+ * renesas_usbhs host/gadget can register itself by below functions.
+ * these functions are called when probe
+ *
+ */
+void usbhs_mod_register(struct usbhs_priv *priv, struct usbhs_mod *mod, int id)
+{
+	struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
+
+	info->mod[id]	= mod;
+	mod->priv	= priv;
+}
+
+struct usbhs_mod *usbhs_mod_get(struct usbhs_priv *priv, int id)
+{
+	struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
+	struct usbhs_mod *ret = NULL;
+
+	switch (id) {
+	case USBHS_HOST:
+	case USBHS_GADGET:
+		ret = info->mod[id];
+		break;
+	}
+
+	return ret;
+}
+
+int usbhs_mod_is_host(struct usbhs_priv *priv, struct usbhs_mod *mod)
+{
+	struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
+
+	if (!mod)
+		return -EINVAL;
+
+	return info->mod[USBHS_HOST] == mod;
+}
+
+struct usbhs_mod *usbhs_mod_get_current(struct usbhs_priv *priv)
+{
+	struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
+
+	return info->curt;
+}
+
+int usbhs_mod_change(struct usbhs_priv *priv, int id)
+{
+	struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
+	struct usbhs_mod *mod = NULL;
+	int ret = 0;
+
+	/* id < 0 mean no current */
+	switch (id) {
+	case USBHS_HOST:
+	case USBHS_GADGET:
+		mod = info->mod[id];
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	info->curt = mod;
+
+	return ret;
+}
+
+static irqreturn_t usbhs_interrupt(int irq, void *data);
+int usbhs_mod_probe(struct usbhs_priv *priv)
+{
+	struct device *dev = usbhs_priv_to_dev(priv);
+	int ret;
+
+	/* irq settings */
+	ret = request_irq(priv->irq, usbhs_interrupt,
+			  IRQF_DISABLED, dev_name(dev), priv);
+	if (ret)
+		dev_err(dev, "irq request err\n");
+
+	return ret;
+}
+
+void usbhs_mod_remove(struct usbhs_priv *priv)
+{
+	free_irq(priv->irq, priv);
+}
+
+/*
+ *		status functions
+ */
+int usbhs_status_get_usb_speed(struct usbhs_irq_state *irq_state)
+{
+	switch (irq_state->dvstctr & RHST) {
+	case RHST_LOW_SPEED:
+		return USB_SPEED_LOW;
+	case RHST_FULL_SPEED:
+		return USB_SPEED_FULL;
+	case RHST_HIGH_SPEED:
+		return USB_SPEED_HIGH;
+	}
+
+	return USB_SPEED_UNKNOWN;
+}
+
+int usbhs_status_get_device_state(struct usbhs_irq_state *irq_state)
+{
+	int state = irq_state->intsts0 & DVSQ_MASK;
+
+	switch (state) {
+	case POWER_STATE:
+	case DEFAULT_STATE:
+	case ADDRESS_STATE:
+	case CONFIGURATION_STATE:
+		return state;
+	}
+
+	return -EIO;
+}
+
+int usbhs_status_get_ctrl_stage(struct usbhs_irq_state *irq_state)
+{
+	/*
+	 * return value
+	 *
+	 * IDLE_SETUP_STAGE
+	 * READ_DATA_STAGE
+	 * READ_STATUS_STAGE
+	 * WRITE_DATA_STAGE
+	 * WRITE_STATUS_STAGE
+	 * NODATA_STATUS_STAGE
+	 * SEQUENCE_ERROR
+	 */
+	return (int)irq_state->intsts0 & CTSQ_MASK;
+}
+
+static void usbhs_status_get_each_irq(struct usbhs_priv *priv,
+				      struct usbhs_irq_state *state)
+{
+	struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+
+	state->intsts0 = usbhs_read(priv, INTSTS0);
+	state->intsts1 = usbhs_read(priv, INTSTS1);
+
+	state->brdysts = usbhs_read(priv, BRDYSTS);
+	state->nrdysts = usbhs_read(priv, NRDYSTS);
+	state->bempsts = usbhs_read(priv, BEMPSTS);
+
+	state->dvstctr = usbhs_read(priv, DVSTCTR);
+
+	/* mask */
+	state->bempsts &= mod->irq_bempsts;
+	state->brdysts &= mod->irq_brdysts;
+}
+
+/*
+ *		interrupt
+ */
+#define INTSTS0_MAGIC 0xF800 /* acknowledge magical interrupt sources */
+#define INTSTS1_MAGIC 0xA870 /* acknowledge magical interrupt sources */
+static irqreturn_t usbhs_interrupt(int irq, void *data)
+{
+	struct usbhs_priv *priv = data;
+	struct usbhs_irq_state irq_state;
+
+	usbhs_status_get_each_irq(priv, &irq_state);
+
+	/*
+	 * clear interrupt
+	 *
+	 * The hardware is _very_ picky to clear interrupt bit.
+	 * Especially INTSTS0_MAGIC, INTSTS1_MAGIC value.
+	 *
+	 * see
+	 *	"Operation"
+	 *	 - "Control Transfer (DCP)"
+	 *	   - Function :: VALID bit should 0
+	 */
+	usbhs_write(priv, INTSTS0, ~irq_state.intsts0 & INTSTS0_MAGIC);
+	usbhs_write(priv, INTSTS1, ~irq_state.intsts1 & INTSTS1_MAGIC);
+
+	usbhs_write(priv, BRDYSTS, 0);
+	usbhs_write(priv, NRDYSTS, 0);
+	usbhs_write(priv, BEMPSTS, 0);
+
+	/*
+	 * call irq callback functions
+	 * see also
+	 *	usbhs_irq_setting_update
+	 */
+	if (irq_state.intsts0 & DVST)
+		usbhs_mod_call(priv, irq_dev_state, priv, &irq_state);
+
+	if (irq_state.intsts0 & CTRT)
+		usbhs_mod_call(priv, irq_ctrl_stage, priv, &irq_state);
+
+	if (irq_state.intsts0 & BEMP)
+		usbhs_mod_call(priv, irq_empty, priv, &irq_state);
+
+	if (irq_state.intsts0 & BRDY)
+		usbhs_mod_call(priv, irq_ready, priv, &irq_state);
+
+	return IRQ_HANDLED;
+}
+
+void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod)
+{
+	u16 intenb0 = 0;
+
+	usbhs_write(priv, INTENB0, 0);
+
+	usbhs_write(priv, BEMPENB, 0);
+	usbhs_write(priv, BRDYENB, 0);
+
+	/*
+	 * see also
+	 *	usbhs_interrupt
+	 */
+
+	/*
+	 * it don't enable DVSE (intenb0) here
+	 * but "mod->irq_dev_state" will be called.
+	 */
+
+	if (mod->irq_ctrl_stage)
+		intenb0 |= CTRE;
+
+	if (mod->irq_empty && mod->irq_bempsts) {
+		usbhs_write(priv, BEMPENB, mod->irq_bempsts);
+		intenb0 |= BEMPE;
+	}
+
+	if (mod->irq_ready && mod->irq_brdysts) {
+		usbhs_write(priv, BRDYENB, mod->irq_brdysts);
+		intenb0 |= BRDYE;
+	}
+
+	usbhs_write(priv, INTENB0, intenb0);
+}
diff --git a/drivers/usb/renesas_usbhs/mod.h b/drivers/usb/renesas_usbhs/mod.h
new file mode 100644
index 000000000000..bd873d5b432a
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/mod.h
@@ -0,0 +1,106 @@
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#ifndef RENESAS_USB_MOD_H
+#define RENESAS_USB_MOD_H
+
+#include <linux/spinlock.h>
+#include <linux/usb/renesas_usbhs.h>
+#include "./common.h"
+
+/*
+ *	struct
+ */
+struct usbhs_irq_state {
+	u16 intsts0;
+	u16 intsts1;
+	u16 brdysts;
+	u16 nrdysts;
+	u16 bempsts;
+	u16 dvstctr;
+};
+
+struct usbhs_mod {
+	char *name;
+
+	/*
+	 * entry point from common.c
+	 */
+	int (*start)(struct usbhs_priv *priv);
+	int (*stop)(struct usbhs_priv *priv);
+
+	/* INTSTS0 :: DVST (DVSQ) */
+	int (*irq_dev_state)(struct usbhs_priv *priv,
+			     struct usbhs_irq_state *irq_state);
+
+	/* INTSTS0 :: CTRT (CTSQ) */
+	int (*irq_ctrl_stage)(struct usbhs_priv *priv,
+			      struct usbhs_irq_state *irq_state);
+
+	/* INTSTS0 :: BEMP */
+	/* BEMPSTS */
+	int (*irq_empty)(struct usbhs_priv *priv,
+			 struct usbhs_irq_state *irq_state);
+	u16 irq_bempsts;
+
+	/* INTSTS0 :: BRDY */
+	/* BRDYSTS */
+	int (*irq_ready)(struct usbhs_priv *priv,
+			 struct usbhs_irq_state *irq_state);
+	u16 irq_brdysts;
+
+	struct usbhs_priv *priv;
+};
+
+struct usbhs_mod_info {
+	struct usbhs_mod *mod[USBHS_MAX];
+	struct usbhs_mod *curt; /* current mod */
+};
+
+/*
+ *		for host/gadget module
+ */
+struct usbhs_mod *usbhs_mod_get(struct usbhs_priv *priv, int id);
+struct usbhs_mod *usbhs_mod_get_current(struct usbhs_priv *priv);
+void usbhs_mod_register(struct usbhs_priv *priv, struct usbhs_mod *usb, int id);
+int usbhs_mod_is_host(struct usbhs_priv *priv, struct usbhs_mod *mod);
+int usbhs_mod_change(struct usbhs_priv *priv, int id);
+int usbhs_mod_probe(struct usbhs_priv *priv);
+void usbhs_mod_remove(struct usbhs_priv *priv);
+
+/*
+ *		status functions
+ */
+int usbhs_status_get_usb_speed(struct usbhs_irq_state *irq_state);
+int usbhs_status_get_device_state(struct usbhs_irq_state *irq_state);
+int usbhs_status_get_ctrl_stage(struct usbhs_irq_state *irq_state);
+
+/*
+ *		callback functions
+ */
+void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod);
+
+
+#define usbhs_mod_call(priv, func, param...)		\
+	({						\
+		struct usbhs_mod *mod;			\
+		mod = usbhs_mod_get_current(priv);	\
+		!mod		? -ENODEV :		\
+		!mod->func	? 0 :			\
+		 mod->func(param);			\
+	})
+
+#endif /* RENESAS_USB_MOD_H */
diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c
new file mode 100644
index 000000000000..b7a9137f599b
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/pipe.c
@@ -0,0 +1,880 @@
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include "./common.h"
+#include "./pipe.h"
+
+/*
+ *		macros
+ */
+#define usbhsp_priv_to_pipeinfo(pr)	(&(pr)->pipe_info)
+#define usbhsp_pipe_to_priv(p)		((p)->priv)
+
+#define usbhsp_addr_offset(p)	((usbhs_pipe_number(p) - 1) * 2)
+
+#define usbhsp_is_dcp(p)	((p)->priv->pipe_info.pipe == (p))
+
+#define usbhsp_flags_set(p, f)	((p)->flags |=  USBHS_PIPE_FLAGS_##f)
+#define usbhsp_flags_clr(p, f)	((p)->flags &= ~USBHS_PIPE_FLAGS_##f)
+#define usbhsp_flags_has(p, f)	((p)->flags &   USBHS_PIPE_FLAGS_##f)
+#define usbhsp_flags_init(p)	do {(p)->flags = 0; } while (0)
+
+#define usbhsp_type(p)		((p)->pipe_type)
+#define usbhsp_type_is(p, t)	((p)->pipe_type == t)
+
+/*
+ * for debug
+ */
+static char *usbhsp_pipe_name[] = {
+	[USB_ENDPOINT_XFER_CONTROL]	= "DCP",
+	[USB_ENDPOINT_XFER_BULK]	= "BULK",
+	[USB_ENDPOINT_XFER_INT]		= "INT",
+	[USB_ENDPOINT_XFER_ISOC]	= "ISO",
+};
+
+/*
+ *		usb request functions
+ */
+void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req)
+{
+	u16 val;
+
+	val = usbhs_read(priv, USBREQ);
+	req->bRequest		= (val >> 8) & 0xFF;
+	req->bRequestType	= (val >> 0) & 0xFF;
+
+	req->wValue	= usbhs_read(priv, USBVAL);
+	req->wIndex	= usbhs_read(priv, USBINDX);
+	req->wLength	= usbhs_read(priv, USBLENG);
+}
+
+void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req)
+{
+	usbhs_write(priv, USBREQ,  (req->bRequest << 8) | req->bRequestType);
+	usbhs_write(priv, USBVAL,  req->wValue);
+	usbhs_write(priv, USBINDX, req->wIndex);
+	usbhs_write(priv, USBLENG, req->wLength);
+}
+
+/*
+ *		DCPCTR/PIPEnCTR functions
+ */
+static void usbhsp_pipectrl_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
+{
+	struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+	int offset = usbhsp_addr_offset(pipe);
+
+	if (usbhsp_is_dcp(pipe))
+		usbhs_bset(priv, DCPCTR, mask, val);
+	else
+		usbhs_bset(priv, PIPEnCTR + offset, mask, val);
+}
+
+static u16 usbhsp_pipectrl_get(struct usbhs_pipe *pipe)
+{
+	struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+	int offset = usbhsp_addr_offset(pipe);
+
+	if (usbhsp_is_dcp(pipe))
+		return usbhs_read(priv, DCPCTR);
+	else
+		return usbhs_read(priv, PIPEnCTR + offset);
+}
+
+/*
+ *		DCP/PIPE functions
+ */
+static void __usbhsp_pipe_xxx_set(struct usbhs_pipe *pipe,
+				  u16 dcp_reg, u16 pipe_reg,
+				  u16 mask, u16 val)
+{
+	struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+
+	if (usbhsp_is_dcp(pipe))
+		usbhs_bset(priv, dcp_reg, mask, val);
+	else
+		usbhs_bset(priv, pipe_reg, mask, val);
+}
+
+static u16 __usbhsp_pipe_xxx_get(struct usbhs_pipe *pipe,
+				 u16 dcp_reg, u16 pipe_reg)
+{
+	struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+
+	if (usbhsp_is_dcp(pipe))
+		return usbhs_read(priv, dcp_reg);
+	else
+		return usbhs_read(priv, pipe_reg);
+}
+
+/*
+ *		DCPCFG/PIPECFG functions
+ */
+static void usbhsp_pipe_cfg_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
+{
+	__usbhsp_pipe_xxx_set(pipe, DCPCFG, PIPECFG, mask, val);
+}
+
+/*
+ *		PIPEBUF
+ */
+static void usbhsp_pipe_buf_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
+{
+	if (usbhsp_is_dcp(pipe))
+		return;
+
+	__usbhsp_pipe_xxx_set(pipe, 0, PIPEBUF, mask, val);
+}
+
+/*
+ *		DCPMAXP/PIPEMAXP
+ */
+static void usbhsp_pipe_maxp_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
+{
+	__usbhsp_pipe_xxx_set(pipe, DCPMAXP, PIPEMAXP, mask, val);
+}
+
+static u16 usbhsp_pipe_maxp_get(struct usbhs_pipe *pipe)
+{
+	return __usbhsp_pipe_xxx_get(pipe, DCPMAXP, PIPEMAXP);
+}
+
+/*
+ *		pipe control functions
+ */
+static void usbhsp_pipe_select(struct usbhs_pipe *pipe)
+{
+	struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+
+	/*
+	 * On pipe, this is necessary before
+	 * accesses to below registers.
+	 *
+	 * PIPESEL	: usbhsp_pipe_select
+	 * PIPECFG	: usbhsp_pipe_cfg_xxx
+	 * PIPEBUF	: usbhsp_pipe_buf_xxx
+	 * PIPEMAXP	: usbhsp_pipe_maxp_xxx
+	 * PIPEPERI
+	 */
+
+	/*
+	 * if pipe is dcp, no pipe is selected.
+	 * it is no problem, because dcp have its register
+	 */
+	usbhs_write(priv, PIPESEL, 0xF & usbhs_pipe_number(pipe));
+}
+
+static int usbhsp_pipe_barrier(struct usbhs_pipe *pipe)
+{
+	struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+	struct device *dev = usbhs_priv_to_dev(priv);
+	int timeout = 1024;
+	u16 val;
+
+	/*
+	 * make sure....
+	 *
+	 * Modify these bits when CSSTS = 0, PID = NAK, and no pipe number is
+	 * specified by the CURPIPE bits.
+	 * When changing the setting of this bit after changing
+	 * the PID bits for the selected pipe from BUF to NAK,
+	 * check that CSSTS = 0 and PBUSY = 0.
+	 */
+
+	/*
+	 * CURPIPE bit = 0
+	 *
+	 * see also
+	 *  "Operation"
+	 *  - "Pipe Control"
+	 *   - "Pipe Control Registers Switching Procedure"
+	 */
+	usbhs_write(priv, CFIFOSEL, 0);
+
+	do {
+		val  = usbhsp_pipectrl_get(pipe);
+		val &= CSSTS | PID_MASK;
+		if (!val)
+			return 0;
+
+		udelay(10);
+
+	} while (timeout--);
+
+	/*
+	 * force NAK
+	 */
+	timeout = 1024;
+	usbhs_fifo_disable(pipe);
+	do {
+		val  = usbhsp_pipectrl_get(pipe);
+		val &= PBUSY;
+		if (!val)
+			return 0;
+
+	} while (timeout--);
+
+	dev_err(dev, "pipe barrier failed\n");
+
+	return -EBUSY;
+}
+
+static int usbhsp_pipe_is_accessible(struct usbhs_pipe *pipe)
+{
+	u16 val;
+
+	val = usbhsp_pipectrl_get(pipe);
+	if (val & BSTS)
+		return 0;
+
+	return -EBUSY;
+}
+
+/*
+ *		PID ctrl
+ */
+static void __usbhsp_pid_try_nak_if_stall(struct usbhs_pipe *pipe)
+{
+	u16 pid = usbhsp_pipectrl_get(pipe);
+
+	pid &= PID_MASK;
+
+	/*
+	 * see
+	 * "Pipe n Control Register" - "PID"
+	 */
+	switch (pid) {
+	case PID_STALL11:
+		usbhsp_pipectrl_set(pipe, PID_MASK, PID_STALL10);
+		/* fall-through */
+	case PID_STALL10:
+		usbhsp_pipectrl_set(pipe, PID_MASK, PID_NAK);
+	}
+}
+
+void usbhs_fifo_disable(struct usbhs_pipe *pipe)
+{
+	/* see "Pipe n Control Register" - "PID" */
+	__usbhsp_pid_try_nak_if_stall(pipe);
+
+	usbhsp_pipectrl_set(pipe, PID_MASK, PID_NAK);
+}
+
+void usbhs_fifo_enable(struct usbhs_pipe *pipe)
+{
+	/* see "Pipe n Control Register" - "PID" */
+	__usbhsp_pid_try_nak_if_stall(pipe);
+
+	usbhsp_pipectrl_set(pipe, PID_MASK, PID_BUF);
+}
+
+void usbhs_fifo_stall(struct usbhs_pipe *pipe)
+{
+	u16 pid = usbhsp_pipectrl_get(pipe);
+
+	pid &= PID_MASK;
+
+	/*
+	 * see
+	 * "Pipe n Control Register" - "PID"
+	 */
+	switch (pid) {
+	case PID_NAK:
+		usbhsp_pipectrl_set(pipe, PID_MASK, PID_STALL10);
+		break;
+	case PID_BUF:
+		usbhsp_pipectrl_set(pipe, PID_MASK, PID_STALL11);
+		break;
+	}
+}
+
+/*
+ *		CFIFO ctrl
+ */
+void usbhs_fifo_send_terminator(struct usbhs_pipe *pipe)
+{
+	struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+
+	usbhs_bset(priv, CFIFOCTR, BVAL, BVAL);
+}
+
+static void usbhsp_fifo_clear(struct usbhs_pipe *pipe)
+{
+	struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+
+	usbhs_write(priv, CFIFOCTR, BCLR);
+}
+
+static int usbhsp_fifo_barrier(struct usbhs_priv *priv)
+{
+	int timeout = 1024;
+
+	do {
+		/* The FIFO port is accessible */
+		if (usbhs_read(priv, CFIFOCTR) & FRDY)
+			return 0;
+
+		udelay(10);
+	} while (timeout--);
+
+	return -EBUSY;
+}
+
+static int usbhsp_fifo_rcv_len(struct usbhs_priv *priv)
+{
+	return usbhs_read(priv, CFIFOCTR) & DTLN_MASK;
+}
+
+static int usbhsp_fifo_select(struct usbhs_pipe *pipe, int write)
+{
+	struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+	struct device *dev = usbhs_priv_to_dev(priv);
+	int timeout = 1024;
+	u16 mask = ((1 << 5) | 0xF);		/* mask of ISEL | CURPIPE */
+	u16 base = usbhs_pipe_number(pipe);	/* CURPIPE */
+
+	if (usbhsp_is_dcp(pipe))
+		base |= (1 == write) << 5;	/* ISEL */
+
+	/* "base" will be used below  */
+	usbhs_write(priv, CFIFOSEL, base | MBW_32);
+
+	/* check ISEL and CURPIPE value */
+	while (timeout--) {
+		if (base == (mask & usbhs_read(priv, CFIFOSEL)))
+			return 0;
+		udelay(10);
+	}
+
+	dev_err(dev, "fifo select error\n");
+
+	return -EIO;
+}
+
+int usbhs_fifo_prepare_write(struct usbhs_pipe *pipe)
+{
+	int ret;
+
+	ret = usbhsp_fifo_select(pipe, 1);
+	if (ret < 0)
+		return ret;
+
+	usbhsp_fifo_clear(pipe);
+
+	return ret;
+}
+
+int usbhs_fifo_write(struct usbhs_pipe *pipe, u8 *buf, int len)
+{
+	struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+	void __iomem *addr = priv->base + CFIFO;
+	int maxp = usbhs_pipe_get_maxpacket(pipe);
+	int total_len;
+	int i, ret;
+
+	ret = usbhsp_pipe_is_accessible(pipe);
+	if (ret < 0)
+		return ret;
+
+	ret = usbhs_fifo_prepare_write(pipe);
+	if (ret < 0)
+		return ret;
+
+	ret = usbhsp_fifo_barrier(priv);
+	if (ret < 0)
+		return ret;
+
+	len = min(len, maxp);
+	total_len = len;
+
+	/*
+	 * FIXME
+	 *
+	 * 32-bit access only
+	 */
+	if (len >= 4 &&
+	    !((unsigned long)buf & 0x03)) {
+		iowrite32_rep(addr, buf, len / 4);
+		len %= 4;
+		buf += total_len - len;
+	}
+
+	/* the rest operation */
+	for (i = 0; i < len; i++)
+		iowrite8(buf[i], addr + (0x03 - (i & 0x03)));
+
+	if (total_len < maxp)
+		usbhs_fifo_send_terminator(pipe);
+
+	return total_len;
+}
+
+int usbhs_fifo_prepare_read(struct usbhs_pipe *pipe)
+{
+	int ret;
+
+	/*
+	 * select pipe and enable it to prepare packet receive
+	 */
+	ret = usbhsp_fifo_select(pipe, 0);
+	if (ret < 0)
+		return ret;
+
+	usbhs_fifo_enable(pipe);
+
+	return ret;
+}
+
+int usbhs_fifo_read(struct usbhs_pipe *pipe, u8 *buf, int len)
+{
+	struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+	void __iomem *addr = priv->base + CFIFO;
+	int rcv_len;
+	int i, ret;
+	int total_len;
+	u32 data = 0;
+
+	ret = usbhsp_fifo_select(pipe, 0);
+	if (ret < 0)
+		return ret;
+
+	ret = usbhsp_fifo_barrier(priv);
+	if (ret < 0)
+		return ret;
+
+	rcv_len = usbhsp_fifo_rcv_len(priv);
+
+	/*
+	 * Buffer clear if Zero-Length packet
+	 *
+	 * see
+	 * "Operation" - "FIFO Buffer Memory" - "FIFO Port Function"
+	 */
+	if (0 == rcv_len) {
+		usbhsp_fifo_clear(pipe);
+		return 0;
+	}
+
+	len = min(rcv_len, len);
+	total_len = len;
+
+	/*
+	 * FIXME
+	 *
+	 * 32-bit access only
+	 */
+	if (len >= 4 &&
+	    !((unsigned long)buf & 0x03)) {
+		ioread32_rep(addr, buf, len / 4);
+		len %= 4;
+		buf += rcv_len - len;
+	}
+
+	/* the rest operation */
+	for (i = 0; i < len; i++) {
+		if (!(i & 0x03))
+			data = ioread32(addr);
+
+		buf[i] = (data >> ((i & 0x03) * 8)) & 0xff;
+	}
+
+	return total_len;
+}
+
+/*
+ *		pipe setup
+ */
+static int usbhsp_possible_double_buffer(struct usbhs_pipe *pipe)
+{
+	/*
+	 * only ISO / BULK pipe can use double buffer
+	 */
+	if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK) ||
+	    usbhsp_type_is(pipe, USB_ENDPOINT_XFER_ISOC))
+		return 1;
+
+	return 0;
+}
+
+static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe,
+				const struct usb_endpoint_descriptor *desc,
+				int is_host)
+{
+	u16 type = 0;
+	u16 bfre = 0;
+	u16 dblb = 0;
+	u16 cntmd = 0;
+	u16 dir = 0;
+	u16 epnum = 0;
+	u16 shtnak = 0;
+	u16 type_array[] = {
+		[USB_ENDPOINT_XFER_BULK] = TYPE_BULK,
+		[USB_ENDPOINT_XFER_INT]  = TYPE_INT,
+		[USB_ENDPOINT_XFER_ISOC] = TYPE_ISO,
+	};
+	int is_double = usbhsp_possible_double_buffer(pipe);
+
+	if (usbhsp_is_dcp(pipe))
+		return -EINVAL;
+
+	/*
+	 * PIPECFG
+	 *
+	 * see
+	 *  - "Register Descriptions" - "PIPECFG" register
+	 *  - "Features"  - "Pipe configuration"
+	 *  - "Operation" - "Pipe Control"
+	 */
+
+	/* TYPE */
+	type = type_array[usbhsp_type(pipe)];
+
+	/* BFRE */
+	if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_ISOC) ||
+	    usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK))
+		bfre = 0; /* FIXME */
+
+	/* DBLB */
+	if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_ISOC) ||
+	    usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK))
+		dblb = (is_double) ? DBLB : 0;
+
+	/* CNTMD */
+	if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK))
+		cntmd = 0; /* FIXME */
+
+	/* DIR */
+	if (usb_endpoint_dir_in(desc))
+		usbhsp_flags_set(pipe, IS_DIR_IN);
+
+	if ((is_host  && usb_endpoint_dir_out(desc)) ||
+	    (!is_host && usb_endpoint_dir_in(desc)))
+		dir |= DIR_OUT;
+
+	/* SHTNAK */
+	if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK) &&
+	    !dir)
+		shtnak = SHTNAK;
+
+	/* EPNUM */
+	epnum = 0xF & usb_endpoint_num(desc);
+
+	return	type	|
+		bfre	|
+		dblb	|
+		cntmd	|
+		dir	|
+		shtnak	|
+		epnum;
+}
+
+static u16 usbhsp_setup_pipemaxp(struct usbhs_pipe *pipe,
+				 const struct usb_endpoint_descriptor *desc,
+				 int is_host)
+{
+	/* host should set DEVSEL */
+
+	/* reutn MXPS */
+	return PIPE_MAXP_MASK & le16_to_cpu(desc->wMaxPacketSize);
+}
+
+static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe,
+				 const struct usb_endpoint_descriptor *desc,
+				 int is_host)
+{
+	struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+	struct usbhs_pipe_info *info = usbhsp_priv_to_pipeinfo(priv);
+	struct device *dev = usbhs_priv_to_dev(priv);
+	int pipe_num = usbhs_pipe_number(pipe);
+	int is_double = usbhsp_possible_double_buffer(pipe);
+	u16 buff_size;
+	u16 bufnmb;
+	u16 bufnmb_cnt;
+
+	/*
+	 * PIPEBUF
+	 *
+	 * see
+	 *  - "Register Descriptions" - "PIPEBUF" register
+	 *  - "Features"  - "Pipe configuration"
+	 *  - "Operation" - "FIFO Buffer Memory"
+	 *  - "Operation" - "Pipe Control"
+	 *
+	 * ex) if pipe6 - pipe9 are USB_ENDPOINT_XFER_INT (SH7724)
+	 *
+	 * BUFNMB:	PIPE
+	 * 0:		pipe0 (DCP 256byte)
+	 * 1:		-
+	 * 2:		-
+	 * 3:		-
+	 * 4:		pipe6 (INT 64byte)
+	 * 5:		pipe7 (INT 64byte)
+	 * 6:		pipe8 (INT 64byte)
+	 * 7:		pipe9 (INT 64byte)
+	 * 8 - xx:	free (for BULK, ISOC)
+	 */
+
+	/*
+	 * FIXME
+	 *
+	 * it doesn't have good buffer allocator
+	 *
+	 * DCP : 256 byte
+	 * BULK: 512 byte
+	 * INT :  64 byte
+	 * ISOC: 512 byte
+	 */
+	if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_CONTROL))
+		buff_size = 256;
+	else if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_INT))
+		buff_size = 64;
+	else
+		buff_size = 512;
+
+	/* change buff_size to register value */
+	bufnmb_cnt = (buff_size / 64) - 1;
+
+	/* BUFNMB has been reserved for INT pipe
+	 * see above */
+	if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_INT)) {
+		bufnmb = pipe_num - 2;
+	} else {
+		bufnmb = info->bufnmb_last;
+		info->bufnmb_last += bufnmb_cnt + 1;
+
+		/*
+		 * double buffer
+		 */
+		if (is_double)
+			info->bufnmb_last += bufnmb_cnt + 1;
+	}
+
+	dev_dbg(dev, "pipe : %d : buff_size 0x%x: bufnmb 0x%x\n",
+		pipe_num, buff_size, bufnmb);
+
+	return	(0x1f & bufnmb_cnt)	<< 10 |
+		(0xff & bufnmb)		<<  0;
+}
+
+/*
+ *		pipe control
+ */
+int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe)
+{
+	u16 mask = usbhsp_is_dcp(pipe) ? DCP_MAXP_MASK : PIPE_MAXP_MASK;
+
+	usbhsp_pipe_select(pipe);
+
+	return (int)(usbhsp_pipe_maxp_get(pipe) & mask);
+}
+
+int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe)
+{
+	return usbhsp_flags_has(pipe, IS_DIR_IN);
+}
+
+void usbhs_pipe_clear_sequence(struct usbhs_pipe *pipe)
+{
+	usbhsp_pipectrl_set(pipe, SQCLR, SQCLR);
+}
+
+static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type)
+{
+	struct usbhs_pipe *pos, *pipe;
+	int i;
+
+	/*
+	 * find target pipe
+	 */
+	pipe = NULL;
+	usbhs_for_each_pipe_with_dcp(pos, priv, i) {
+		if (!usbhsp_type_is(pos, type))
+			continue;
+		if (usbhsp_flags_has(pos, IS_USED))
+			continue;
+
+		pipe = pos;
+		break;
+	}
+
+	if (!pipe)
+		return NULL;
+
+	/*
+	 * initialize pipe flags
+	 */
+	usbhsp_flags_init(pipe);
+	usbhsp_flags_set(pipe, IS_USED);
+
+	return pipe;
+}
+
+void usbhs_pipe_init(struct usbhs_priv *priv)
+{
+	struct usbhs_pipe_info *info = usbhsp_priv_to_pipeinfo(priv);
+	struct usbhs_pipe *pipe;
+	int i;
+
+	/*
+	 * FIXME
+	 *
+	 * driver needs good allocator.
+	 *
+	 * find first free buffer area (BULK, ISOC)
+	 * (DCP, INT area is fixed)
+	 *
+	 * buffer number 0 - 3 have been reserved for DCP
+	 * see
+	 *	usbhsp_to_bufnmb
+	 */
+	info->bufnmb_last = 4;
+	usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
+		if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_INT))
+			info->bufnmb_last++;
+
+		usbhsp_flags_init(pipe);
+		pipe->mod_private = NULL;
+	}
+}
+
+struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv,
+				     const struct usb_endpoint_descriptor *desc)
+{
+	struct device *dev = usbhs_priv_to_dev(priv);
+	struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+	struct usbhs_pipe *pipe;
+	int is_host = usbhs_mod_is_host(priv, mod);
+	int ret;
+	u16 pipecfg, pipebuf, pipemaxp;
+
+	pipe = usbhsp_get_pipe(priv, usb_endpoint_type(desc));
+	if (!pipe)
+		return NULL;
+
+	usbhs_fifo_disable(pipe);
+
+	/* make sure pipe is not busy */
+	ret = usbhsp_pipe_barrier(pipe);
+	if (ret < 0) {
+		dev_err(dev, "pipe setup failed %d\n", usbhs_pipe_number(pipe));
+		return NULL;
+	}
+
+	pipecfg  = usbhsp_setup_pipecfg(pipe,  desc, is_host);
+	pipebuf  = usbhsp_setup_pipebuff(pipe, desc, is_host);
+	pipemaxp = usbhsp_setup_pipemaxp(pipe, desc, is_host);
+
+	/* buffer clear
+	 * see PIPECFG :: BFRE */
+	usbhsp_pipectrl_set(pipe, ACLRM, ACLRM);
+	usbhsp_pipectrl_set(pipe, ACLRM, 0);
+
+	usbhsp_pipe_select(pipe);
+	usbhsp_pipe_cfg_set(pipe, 0xFFFF, pipecfg);
+	usbhsp_pipe_buf_set(pipe, 0xFFFF, pipebuf);
+	usbhsp_pipe_maxp_set(pipe, 0xFFFF, pipemaxp);
+
+	usbhs_pipe_clear_sequence(pipe);
+
+	dev_dbg(dev, "enable pipe %d : %s (%s)\n",
+		usbhs_pipe_number(pipe),
+		usbhsp_pipe_name[usb_endpoint_type(desc)],
+		usbhs_pipe_is_dir_in(pipe) ? "in" : "out");
+
+	return pipe;
+}
+
+/*
+ *		dcp control
+ */
+struct usbhs_pipe *usbhs_dcp_malloc(struct usbhs_priv *priv)
+{
+	struct usbhs_pipe *pipe;
+
+	pipe = usbhsp_get_pipe(priv, USB_ENDPOINT_XFER_CONTROL);
+	if (!pipe)
+		return NULL;
+
+	/*
+	 * dcpcfg  : default
+	 * dcpmaxp : default
+	 * pipebuf : nothing to do
+	 */
+
+	usbhsp_pipe_select(pipe);
+	usbhs_pipe_clear_sequence(pipe);
+
+	return pipe;
+}
+
+void usbhs_dcp_control_transfer_done(struct usbhs_pipe *pipe)
+{
+	WARN_ON(!usbhsp_is_dcp(pipe));
+
+	usbhs_fifo_enable(pipe);
+	usbhsp_pipectrl_set(pipe, CCPL, CCPL);
+}
+
+
+/*
+ *		pipe module function
+ */
+int usbhs_pipe_probe(struct usbhs_priv *priv)
+{
+	struct usbhs_pipe_info *info = usbhsp_priv_to_pipeinfo(priv);
+	struct usbhs_pipe *pipe;
+	struct device *dev = usbhs_priv_to_dev(priv);
+	u32 *pipe_type = usbhs_get_dparam(priv, pipe_type);
+	int pipe_size = usbhs_get_dparam(priv, pipe_size);
+	int i;
+
+	/* This driver expects 1st pipe is DCP */
+	if (pipe_type[0] != USB_ENDPOINT_XFER_CONTROL) {
+		dev_err(dev, "1st PIPE is not DCP\n");
+		return -EINVAL;
+	}
+
+	info->pipe = kzalloc(sizeof(struct usbhs_pipe) * pipe_size, GFP_KERNEL);
+	if (!info->pipe) {
+		dev_err(dev, "Could not allocate pipe\n");
+		return -ENOMEM;
+	}
+
+	info->size = pipe_size;
+
+	/*
+	 * init pipe
+	 */
+	usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
+		pipe->priv = priv;
+		usbhsp_type(pipe) = pipe_type[i] & USB_ENDPOINT_XFERTYPE_MASK;
+
+		dev_dbg(dev, "pipe %x\t: %s\n",
+			i, usbhsp_pipe_name[pipe_type[i]]);
+	}
+
+	return 0;
+}
+
+void usbhs_pipe_remove(struct usbhs_priv *priv)
+{
+	struct usbhs_pipe_info *info = usbhsp_priv_to_pipeinfo(priv);
+
+	kfree(info->pipe);
+}
diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h
new file mode 100644
index 000000000000..4a60dcef9676
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/pipe.h
@@ -0,0 +1,105 @@
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#ifndef RENESAS_USB_PIPE_H
+#define RENESAS_USB_PIPE_H
+
+#include "./common.h"
+
+/*
+ *	struct
+ */
+struct usbhs_pipe {
+	u32 pipe_type;	/* USB_ENDPOINT_XFER_xxx */
+
+	struct usbhs_priv *priv;
+
+	u32 flags;
+#define USBHS_PIPE_FLAGS_IS_USED		(1 << 0)
+#define USBHS_PIPE_FLAGS_IS_DIR_IN		(1 << 1)
+
+	void *mod_private;
+};
+
+struct usbhs_pipe_info {
+	struct usbhs_pipe *pipe;
+	int size;	/* array size of "pipe" */
+	int bufnmb_last;	/* FIXME : driver needs good allocator */
+};
+
+/*
+ * pipe list
+ */
+#define __usbhs_for_each_pipe(start, pos, info, i)	\
+	for (i = start, pos = (info)->pipe;		\
+	     i < (info)->size;				\
+	     i++, pos = (info)->pipe + i)
+
+#define usbhs_for_each_pipe(pos, priv, i)			\
+	__usbhs_for_each_pipe(1, pos, &((priv)->pipe_info), i)
+
+#define usbhs_for_each_pipe_with_dcp(pos, priv, i)		\
+	__usbhs_for_each_pipe(0, pos, &((priv)->pipe_info), i)
+
+/*
+ * pipe module probe / remove
+ */
+int usbhs_pipe_probe(struct usbhs_priv *priv);
+void usbhs_pipe_remove(struct usbhs_priv *priv);
+
+/*
+ * cfifo
+ */
+int usbhs_fifo_write(struct usbhs_pipe *pipe, u8 *buf, int len);
+int usbhs_fifo_read(struct usbhs_pipe *pipe, u8 *buf, int len);
+int usbhs_fifo_prepare_write(struct usbhs_pipe *pipe);
+int usbhs_fifo_prepare_read(struct usbhs_pipe *pipe);
+
+void usbhs_fifo_enable(struct usbhs_pipe *pipe);
+void usbhs_fifo_disable(struct usbhs_pipe *pipe);
+void usbhs_fifo_stall(struct usbhs_pipe *pipe);
+
+void usbhs_fifo_send_terminator(struct usbhs_pipe *pipe);
+
+
+/*
+ * usb request
+ */
+void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req);
+void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req);
+
+/*
+ * pipe control
+ */
+struct usbhs_pipe
+*usbhs_pipe_malloc(struct usbhs_priv *priv,
+		   const struct usb_endpoint_descriptor *desc);
+
+int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe);
+void usbhs_pipe_init(struct usbhs_priv *priv);
+int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe);
+void usbhs_pipe_clear_sequence(struct usbhs_pipe *pipe);
+
+#define usbhs_pipe_number(p)	(((u32)(p) - (u32)(p)->priv->pipe_info.pipe) / \
+				 sizeof(struct usbhs_pipe))
+
+/*
+ * dcp control
+ */
+struct usbhs_pipe *usbhs_dcp_malloc(struct usbhs_priv *priv);
+void usbhs_dcp_control_transfer_done(struct usbhs_pipe *pipe);
+
+#endif /* RENESAS_USB_PIPE_H */
-- 
cgit v1.2.3-70-g09d2


From 2f98382dcdfe1f0048b447da35f34507ffb514dc Mon Sep 17 00:00:00 2001
From: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Date: Tue, 5 Apr 2011 11:40:54 +0900
Subject: usb: renesas_usbhs: Add Renesas USBHS Gadget

This patch add usb gadget code to SuperH USBHS.

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/gadget/Kconfig             |   18 +
 drivers/usb/gadget/gadget_chips.h      |    9 +
 drivers/usb/renesas_usbhs/Makefile     |    2 +
 drivers/usb/renesas_usbhs/mod.c        |   17 +-
 drivers/usb/renesas_usbhs/mod.h        |   16 +
 drivers/usb/renesas_usbhs/mod_gadget.c | 1341 ++++++++++++++++++++++++++++++++
 6 files changed, 1402 insertions(+), 1 deletion(-)
 create mode 100644 drivers/usb/renesas_usbhs/mod_gadget.c

(limited to 'drivers/usb')

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index bc5123cf41c2..4c02b9f1597e 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -260,6 +260,24 @@ config USB_R8A66597
 	default USB_GADGET
 	select USB_GADGET_SELECTED
 
+config USB_GADGET_RENESAS_USBHS
+	boolean "Renesas USBHS"
+	depends on USB_RENESAS_USBHS
+	select USB_GADGET_DUALSPEED
+	help
+	   Renesas USBHS is a discrete USB host and peripheral controller
+	   chip that supports both full and high speed USB 2.0 data transfers.
+	   platform is able to configure endpoint (pipe) style
+
+	   Say "y" to enable the gadget specific portion of the USBHS driver.
+
+
+config USB_RENESAS_USBHS_UDC
+	tristate
+	depends on USB_GADGET_RENESAS_USBHS
+	default USB_GADGET
+	select USB_GADGET_SELECTED
+
 config USB_GADGET_PXA27X
 	boolean "PXA 27x"
 	depends on ARCH_PXA && (PXA27x || PXA3xx)
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index e896f6359dfe..ec3fd979c71d 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -148,6 +148,12 @@
 #define gadget_is_ci13xxx_msm(g)	0
 #endif
 
+#ifdef CONFIG_USB_GADGET_RENESAS_USBHS
+#define	gadget_is_renesas_usbhs(g) (!strcmp("renesas_usbhs_udc", (g)->name))
+#else
+#define	gadget_is_renesas_usbhs(g) 0
+#endif
+
 /**
  * usb_gadget_controller_number - support bcdDevice id convention
  * @gadget: the controller being driven
@@ -207,6 +213,9 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
 		return 0x27;
 	else if (gadget_is_ci13xxx_msm(gadget))
 		return 0x28;
+	else if (gadget_is_renesas_usbhs(gadget))
+		return 0x29;
+
 	return -ENOENT;
 }
 
diff --git a/drivers/usb/renesas_usbhs/Makefile b/drivers/usb/renesas_usbhs/Makefile
index d76f3dd3b9d1..b8798ad16278 100644
--- a/drivers/usb/renesas_usbhs/Makefile
+++ b/drivers/usb/renesas_usbhs/Makefile
@@ -5,3 +5,5 @@
 obj-$(CONFIG_USB_RENESAS_USBHS)	+= renesas_usbhs.o
 
 renesas_usbhs-y			:= common.o mod.o pipe.o
+
+renesas_usbhs-$(CONFIG_USB_RENESAS_USBHS_UDC)	+= mod_gadget.o
diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c
index 4a3398484cd7..73604a1d6843 100644
--- a/drivers/usb/renesas_usbhs/mod.c
+++ b/drivers/usb/renesas_usbhs/mod.c
@@ -94,17 +94,32 @@ int usbhs_mod_probe(struct usbhs_priv *priv)
 	struct device *dev = usbhs_priv_to_dev(priv);
 	int ret;
 
+	/*
+	 * install host/gadget driver
+	 */
+	ret = usbhs_mod_gadget_probe(priv);
+	if (ret < 0)
+		return ret;
+
 	/* irq settings */
 	ret = request_irq(priv->irq, usbhs_interrupt,
 			  IRQF_DISABLED, dev_name(dev), priv);
-	if (ret)
+	if (ret) {
 		dev_err(dev, "irq request err\n");
+		goto mod_init_gadget_err;
+	}
+
+	return ret;
+
+mod_init_gadget_err:
+	usbhs_mod_gadget_remove(priv);
 
 	return ret;
 }
 
 void usbhs_mod_remove(struct usbhs_priv *priv)
 {
+	usbhs_mod_gadget_remove(priv);
 	free_irq(priv->irq, priv);
 }
 
diff --git a/drivers/usb/renesas_usbhs/mod.h b/drivers/usb/renesas_usbhs/mod.h
index bd873d5b432a..8644191e164f 100644
--- a/drivers/usb/renesas_usbhs/mod.h
+++ b/drivers/usb/renesas_usbhs/mod.h
@@ -103,4 +103,20 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod);
 		 mod->func(param);			\
 	})
 
+/*
+ * gadget control
+ */
+#ifdef CONFIG_USB_RENESAS_USBHS_UDC
+extern int __devinit usbhs_mod_gadget_probe(struct usbhs_priv *priv);
+extern void __devexit usbhs_mod_gadget_remove(struct usbhs_priv *priv);
+#else
+static inline int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
+{
+	return 0;
+}
+static inline void usbhs_mod_gadget_remove(struct usbhs_priv *priv)
+{
+}
+#endif
+
 #endif /* RENESAS_USB_MOD_H */
diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c
new file mode 100644
index 000000000000..9a5ac02077b9
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/mod_gadget.c
@@ -0,0 +1,1341 @@
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include "common.h"
+
+/*
+ *		struct
+ */
+struct usbhsg_request {
+	struct usb_request	req;
+	struct list_head	node;
+};
+
+#define EP_NAME_SIZE 8
+struct usbhsg_gpriv;
+struct usbhsg_pipe_handle;
+struct usbhsg_uep {
+	struct usb_ep		 ep;
+	struct usbhs_pipe	*pipe;
+	struct list_head	 list;
+
+	char ep_name[EP_NAME_SIZE];
+
+	struct usbhsg_gpriv *gpriv;
+	struct usbhsg_pipe_handle *handler;
+};
+
+struct usbhsg_gpriv {
+	struct usb_gadget	 gadget;
+	struct usbhs_mod	 mod;
+
+	struct usbhsg_uep	*uep;
+	int			 uep_size;
+
+	struct usb_gadget_driver	*driver;
+
+	u32	status;
+#define USBHSG_STATUS_STARTED		(1 << 0)
+#define USBHSG_STATUS_REGISTERD		(1 << 1)
+#define USBHSG_STATUS_WEDGE		(1 << 2)
+};
+
+struct usbhsg_pipe_handle {
+	int (*prepare)(struct usbhsg_uep *uep, struct usbhsg_request *ureq);
+	int (*try_run)(struct usbhsg_uep *uep, struct usbhsg_request *ureq);
+	void (*irq_mask)(struct usbhsg_uep *uep, int enable);
+};
+
+struct usbhsg_recip_handle {
+	char *name;
+	int (*device)(struct usbhs_priv *priv, struct usbhsg_uep *uep,
+		      struct usb_ctrlrequest *ctrl);
+	int (*interface)(struct usbhs_priv *priv, struct usbhsg_uep *uep,
+			 struct usb_ctrlrequest *ctrl);
+	int (*endpoint)(struct usbhs_priv *priv, struct usbhsg_uep *uep,
+			struct usb_ctrlrequest *ctrl);
+};
+
+/*
+ *		macro
+ */
+#define usbhsg_priv_to_gpriv(priv)			\
+	container_of(					\
+		usbhs_mod_get(priv, USBHS_GADGET),	\
+		struct usbhsg_gpriv, mod)
+
+#define __usbhsg_for_each_uep(start, pos, g, i)	\
+	for (i = start, pos = (g)->uep;		\
+	     i < (g)->uep_size;			\
+	     i++, pos = (g)->uep + i)
+
+#define usbhsg_for_each_uep(pos, gpriv, i)	\
+	__usbhsg_for_each_uep(1, pos, gpriv, i)
+
+#define usbhsg_for_each_uep_with_dcp(pos, gpriv, i)	\
+	__usbhsg_for_each_uep(0, pos, gpriv, i)
+
+#define usbhsg_gadget_to_gpriv(g)\
+	container_of(g, struct usbhsg_gpriv, gadget)
+
+#define usbhsg_req_to_ureq(r)\
+	container_of(r, struct usbhsg_request, req)
+
+#define usbhsg_ep_to_uep(e)		container_of(e, struct usbhsg_uep, ep)
+#define usbhsg_gpriv_to_lock(gp)	usbhs_priv_to_lock((gp)->mod.priv)
+#define usbhsg_gpriv_to_dev(gp)		usbhs_priv_to_dev((gp)->mod.priv)
+#define usbhsg_gpriv_to_priv(gp)	((gp)->mod.priv)
+#define usbhsg_gpriv_to_dcp(gp)		((gp)->uep)
+#define usbhsg_gpriv_to_nth_uep(gp, i)	((gp)->uep + i)
+#define usbhsg_uep_to_gpriv(u)		((u)->gpriv)
+#define usbhsg_uep_to_pipe(u)		((u)->pipe)
+#define usbhsg_pipe_to_uep(p)		((p)->mod_private)
+#define usbhsg_is_dcp(u)		((u) == usbhsg_gpriv_to_dcp((u)->gpriv))
+
+#define usbhsg_is_not_connected(gp) ((gp)->gadget.speed == USB_SPEED_UNKNOWN)
+
+/* status */
+#define usbhsg_status_init(gp)   do {(gp)->status = 0; } while (0)
+#define usbhsg_status_set(gp, b) (gp->status |=  b)
+#define usbhsg_status_clr(gp, b) (gp->status &= ~b)
+#define usbhsg_status_has(gp, b) (gp->status &   b)
+
+/*
+ *		list push/pop
+ */
+static void usbhsg_queue_push(struct usbhsg_uep *uep,
+			      struct usbhsg_request *ureq)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+	struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+	struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+
+	/*
+	 *********  assume under spin lock  *********
+	 */
+	list_del_init(&ureq->node);
+	list_add_tail(&ureq->node, &uep->list);
+	ureq->req.actual = 0;
+	ureq->req.status = -EINPROGRESS;
+
+	dev_dbg(dev, "pipe %d : queue push (%d)\n",
+		usbhs_pipe_number(pipe),
+		ureq->req.length);
+}
+
+static struct usbhsg_request *usbhsg_queue_get(struct usbhsg_uep *uep)
+{
+	/*
+	 *********  assume under spin lock  *********
+	 */
+	if (list_empty(&uep->list))
+		return NULL;
+
+	return list_entry(uep->list.next, struct usbhsg_request, node);
+}
+
+#define usbhsg_queue_prepare(uep) __usbhsg_queue_handler(uep, 1);
+#define usbhsg_queue_handle(uep)  __usbhsg_queue_handler(uep, 0);
+static int __usbhsg_queue_handler(struct usbhsg_uep *uep, int prepare)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+	struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+	struct usbhsg_request *ureq;
+	spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv);
+	unsigned long flags;
+	int is_locked;
+	int ret = 0;
+
+	if (!uep->handler) {
+		dev_err(dev, "no handler function\n");
+		return -EIO;
+	}
+
+	/*
+	 * CAUTION [*queue handler*]
+	 *
+	 * This function will be called for start/restart queue operation.
+	 * OTOH the most much worry for USB driver is spinlock nest.
+	 * Specially it are
+	 *   - usb_ep_ops  :: queue
+	 *   - usb_request :: complete
+	 *
+	 * But the caller of this function need not care about spinlock.
+	 * This function is using spin_trylock_irqsave for it.
+	 * if "is_locked" is 1, this mean this function lock it.
+	 * but if it is 0, this mean it is already under spin lock.
+	 * see also
+	 *   CAUTION [*endpoint queue*]
+	 *   CAUTION [*request complete*]
+	 */
+
+	/******************  spin try lock *******************/
+	is_locked = spin_trylock_irqsave(lock, flags);
+	ureq = usbhsg_queue_get(uep);
+	if (ureq) {
+		if (prepare)
+			ret = uep->handler->prepare(uep, ureq);
+		else
+			ret = uep->handler->try_run(uep, ureq);
+	}
+	if (is_locked)
+		spin_unlock_irqrestore(lock, flags);
+	/********************  spin unlock ******************/
+
+	return ret;
+}
+
+static void usbhsg_queue_pop(struct usbhsg_uep *uep,
+			     struct usbhsg_request *ureq,
+			     int status)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+	struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+	struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+
+	/*
+	 *********  assume under spin lock  *********
+	 */
+
+	/*
+	 * CAUTION [*request complete*]
+	 *
+	 * There is a possibility not to be called in correct order
+	 * if "complete" is called without spinlock.
+	 *
+	 * So, this function assume it is under spinlock,
+	 * and call usb_request :: complete.
+	 *
+	 * But this "complete" will push next usb_request.
+	 * It mean "usb_ep_ops :: queue" which is using spinlock is called
+	 * under spinlock.
+	 *
+	 * To avoid dead-lock, this driver is using spin_trylock.
+	 *   CAUTION [*endpoint queue*]
+	 *   CAUTION [*queue handler*]
+	 */
+
+	dev_dbg(dev, "pipe %d : queue pop\n", usbhs_pipe_number(pipe));
+
+	list_del_init(&ureq->node);
+
+	ureq->req.status = status;
+	ureq->req.complete(&uep->ep, &ureq->req);
+
+	/* more request ? */
+	if (0 == status)
+		usbhsg_queue_prepare(uep);
+}
+
+/*
+ *		irq enable/disable function
+ */
+#define usbhsg_irq_callback_ctrl(uep, status, enable)			\
+	({								\
+		struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);	\
+		struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);	\
+		struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);	\
+		struct usbhs_mod *mod = usbhs_mod_get_current(priv);	\
+		if (!mod)						\
+			return;						\
+		if (enable)						\
+			mod->irq_##status |= (1 << usbhs_pipe_number(pipe)); \
+		else							\
+			mod->irq_##status &= ~(1 << usbhs_pipe_number(pipe)); \
+		usbhs_irq_callback_update(priv, mod);			\
+	})
+
+static void usbhsg_irq_empty_ctrl(struct usbhsg_uep *uep, int enable)
+{
+	usbhsg_irq_callback_ctrl(uep, bempsts, enable);
+}
+
+static void usbhsg_irq_ready_ctrl(struct usbhsg_uep *uep, int enable)
+{
+	usbhsg_irq_callback_ctrl(uep, brdysts, enable);
+}
+
+/*
+ *		handler function
+ */
+static int usbhsg_try_run_ctrl_stage_end(struct usbhsg_uep *uep,
+					 struct usbhsg_request *ureq)
+{
+	struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+
+	/*
+	 *********  assume under spin lock  *********
+	 */
+
+	usbhs_dcp_control_transfer_done(pipe);
+	usbhsg_queue_pop(uep, ureq, 0);
+
+	return 0;
+}
+
+static int usbhsg_try_run_send_packet(struct usbhsg_uep *uep,
+				      struct usbhsg_request *ureq)
+{
+	struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+	struct usb_request *req = &ureq->req;
+	struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+	struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+	void *buf;
+	int remainder, send;
+	int is_done = 0;
+	int enable;
+	int maxp;
+
+	/*
+	 *********  assume under spin lock  *********
+	 */
+
+	maxp		= usbhs_pipe_get_maxpacket(pipe);
+	buf		= req->buf    + req->actual;
+	remainder	= req->length - req->actual;
+
+	send = usbhs_fifo_write(pipe, buf, remainder);
+
+	/*
+	 * send < 0 : pipe busy
+	 * send = 0 : send zero packet
+	 * send > 0 : send data
+	 *
+	 * send <= max_packet
+	 */
+	if (send > 0)
+		req->actual += send;
+
+	/* send all packet ? */
+	if (send < remainder)
+		is_done = 0;		/* there are remainder data */
+	else if (send < maxp)
+		is_done = 1;		/* short packet */
+	else
+		is_done = !req->zero;	/* send zero packet ? */
+
+	dev_dbg(dev, "  send %d (%d/ %d/ %d/ %d)\n",
+		usbhs_pipe_number(pipe),
+		remainder, send, is_done, req->zero);
+
+	/*
+	 * enable interrupt and send again in irq handler
+	 * if it still have remainder data which should be sent.
+	 */
+	enable = !is_done;
+	uep->handler->irq_mask(uep, enable);
+
+	/*
+	 * usbhs_fifo_enable execute
+	 *  - after callback_update,
+	 *  - before queue_pop / stage_end
+	 */
+	usbhs_fifo_enable(pipe);
+
+	/*
+	 * all data were sent ?
+	 */
+	if (is_done) {
+		/* it care below call in
+		   "function mode" */
+		if (usbhsg_is_dcp(uep))
+			usbhs_dcp_control_transfer_done(pipe);
+
+		usbhsg_queue_pop(uep, ureq, 0);
+	}
+
+	return 0;
+}
+
+static int usbhsg_prepare_send_packet(struct usbhsg_uep *uep,
+				      struct usbhsg_request *ureq)
+{
+	struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+
+	/*
+	 *********  assume under spin lock  *********
+	 */
+
+	usbhs_fifo_prepare_write(pipe);
+	usbhsg_try_run_send_packet(uep, ureq);
+
+	return 0;
+}
+
+static int usbhsg_try_run_receive_packet(struct usbhsg_uep *uep,
+					 struct usbhsg_request *ureq)
+{
+	struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+	struct usb_request *req = &ureq->req;
+	struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+	struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+	void *buf;
+	int maxp;
+	int remainder, recv;
+	int is_done = 0;
+
+	/*
+	 *********  assume under spin lock  *********
+	 */
+
+	maxp		= usbhs_pipe_get_maxpacket(pipe);
+	buf		= req->buf    + req->actual;
+	remainder	= req->length - req->actual;
+
+	recv = usbhs_fifo_read(pipe, buf, remainder);
+	/*
+	 * recv < 0  : pipe busy
+	 * recv >= 0 : receive data
+	 *
+	 * recv <= max_packet
+	 */
+	if (recv < 0)
+		return -EBUSY;
+
+	/* update parameters */
+	req->actual += recv;
+
+	if ((recv == remainder) ||	/* receive all data */
+	    (recv < maxp))		/* short packet */
+		is_done = 1;
+
+	dev_dbg(dev, "  recv %d (%d/ %d/ %d/ %d)\n",
+		usbhs_pipe_number(pipe),
+		remainder, recv, is_done, req->zero);
+
+	/* read all data ? */
+	if (is_done) {
+		int disable = 0;
+
+		uep->handler->irq_mask(uep, disable);
+		usbhs_fifo_disable(pipe);
+		usbhsg_queue_pop(uep, ureq, 0);
+	}
+
+	return 0;
+}
+
+static int usbhsg_prepare_receive_packet(struct usbhsg_uep *uep,
+					 struct usbhsg_request *ureq)
+{
+	struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+	int enable = 1;
+	int ret;
+
+	/*
+	 *********  assume under spin lock  *********
+	 */
+
+	ret = usbhs_fifo_prepare_read(pipe);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * data will be read in interrupt handler
+	 */
+	uep->handler->irq_mask(uep, enable);
+
+	return ret;
+}
+
+static struct usbhsg_pipe_handle usbhsg_handler_send_by_empty = {
+	.prepare	= usbhsg_prepare_send_packet,
+	.try_run	= usbhsg_try_run_send_packet,
+	.irq_mask	= usbhsg_irq_empty_ctrl,
+};
+
+static struct usbhsg_pipe_handle usbhsg_handler_send_by_ready = {
+	.prepare	= usbhsg_prepare_send_packet,
+	.try_run	= usbhsg_try_run_send_packet,
+	.irq_mask	= usbhsg_irq_ready_ctrl,
+};
+
+static struct usbhsg_pipe_handle usbhsg_handler_recv_by_ready = {
+	.prepare	= usbhsg_prepare_receive_packet,
+	.try_run	= usbhsg_try_run_receive_packet,
+	.irq_mask	= usbhsg_irq_ready_ctrl,
+};
+
+static struct usbhsg_pipe_handle usbhsg_handler_ctrl_stage_end = {
+	.prepare	= usbhsg_try_run_ctrl_stage_end,
+	.try_run	= usbhsg_try_run_ctrl_stage_end,
+};
+
+/*
+ * DCP pipe can NOT use "ready interrupt" for "send"
+ * it should use "empty" interrupt.
+ * see
+ *   "Operation" - "Interrupt Function" - "BRDY Interrupt"
+ *
+ * on the other hand, normal pipe can use "ready interrupt" for "send"
+ * even though it is single/double buffer
+ */
+#define usbhsg_handler_send_ctrl	usbhsg_handler_send_by_empty
+#define usbhsg_handler_recv_ctrl	usbhsg_handler_recv_by_ready
+
+#define usbhsg_handler_send_packet	usbhsg_handler_send_by_ready
+#define usbhsg_handler_recv_packet	usbhsg_handler_recv_by_ready
+
+/*
+ *		USB_TYPE_STANDARD / clear feature functions
+ */
+static int usbhsg_recip_handler_std_control_done(struct usbhs_priv *priv,
+						 struct usbhsg_uep *uep,
+						 struct usb_ctrlrequest *ctrl)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+	struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv);
+	struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(dcp);
+
+	usbhs_dcp_control_transfer_done(pipe);
+
+	return 0;
+}
+
+static int usbhsg_recip_handler_std_clear_endpoint(struct usbhs_priv *priv,
+						   struct usbhsg_uep *uep,
+						   struct usb_ctrlrequest *ctrl)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+	struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+
+	if (!usbhsg_status_has(gpriv, USBHSG_STATUS_WEDGE)) {
+		usbhs_fifo_disable(pipe);
+		usbhs_pipe_clear_sequence(pipe);
+		usbhs_fifo_enable(pipe);
+	}
+
+	usbhsg_recip_handler_std_control_done(priv, uep, ctrl);
+
+	usbhsg_queue_prepare(uep);
+
+	return 0;
+}
+
+struct usbhsg_recip_handle req_clear_feature = {
+	.name		= "clear feature",
+	.device		= usbhsg_recip_handler_std_control_done,
+	.interface	= usbhsg_recip_handler_std_control_done,
+	.endpoint	= usbhsg_recip_handler_std_clear_endpoint,
+};
+
+/*
+ *		USB_TYPE handler
+ */
+static int usbhsg_recip_run_handle(struct usbhs_priv *priv,
+				   struct usbhsg_recip_handle *handler,
+				   struct usb_ctrlrequest *ctrl)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+	struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+	struct usbhsg_uep *uep;
+	int recip = ctrl->bRequestType & USB_RECIP_MASK;
+	int nth = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK;
+	int ret;
+	int (*func)(struct usbhs_priv *priv, struct usbhsg_uep *uep,
+		    struct usb_ctrlrequest *ctrl);
+	char *msg;
+
+	uep = usbhsg_gpriv_to_nth_uep(gpriv, nth);
+
+	switch (recip) {
+	case USB_RECIP_DEVICE:
+		msg	= "DEVICE";
+		func	= handler->device;
+		break;
+	case USB_RECIP_INTERFACE:
+		msg	= "INTERFACE";
+		func	= handler->interface;
+		break;
+	case USB_RECIP_ENDPOINT:
+		msg	= "ENDPOINT";
+		func	= handler->endpoint;
+		break;
+	default:
+		dev_warn(dev, "unsupported RECIP(%d)\n", recip);
+		func = NULL;
+		ret = -EINVAL;
+	}
+
+	if (func) {
+		dev_dbg(dev, "%s (pipe %d :%s)\n", handler->name, nth, msg);
+		ret = func(priv, uep, ctrl);
+	}
+
+	return ret;
+}
+
+/*
+ *		irq functions
+ *
+ * it will be called from usbhs_interrupt
+ */
+static int usbhsg_irq_dev_state(struct usbhs_priv *priv,
+				struct usbhs_irq_state *irq_state)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+	struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+
+	gpriv->gadget.speed = usbhs_status_get_usb_speed(irq_state);
+
+	dev_dbg(dev, "state = %x : speed : %d\n",
+		usbhs_status_get_device_state(irq_state),
+		gpriv->gadget.speed);
+
+	return 0;
+}
+
+static int usbhsg_irq_ctrl_stage(struct usbhs_priv *priv,
+				 struct usbhs_irq_state *irq_state)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+	struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv);
+	struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(dcp);
+	struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+	struct usb_ctrlrequest ctrl;
+	struct usbhsg_recip_handle *recip_handler = NULL;
+	int stage = usbhs_status_get_ctrl_stage(irq_state);
+	int ret = 0;
+
+	dev_dbg(dev, "stage = %d\n", stage);
+
+	/*
+	 * see Manual
+	 *
+	 *  "Operation"
+	 *  - "Interrupt Function"
+	 *    - "Control Transfer Stage Transition Interrupt"
+	 *      - Fig. "Control Transfer Stage Transitions"
+	 */
+
+	switch (stage) {
+	case READ_DATA_STAGE:
+		dcp->handler = &usbhsg_handler_send_ctrl;
+		break;
+	case WRITE_DATA_STAGE:
+		dcp->handler = &usbhsg_handler_recv_ctrl;
+		break;
+	case NODATA_STATUS_STAGE:
+		dcp->handler = &usbhsg_handler_ctrl_stage_end;
+		break;
+	default:
+		return ret;
+	}
+
+	/*
+	 * get usb request
+	 */
+	usbhs_usbreq_get_val(priv, &ctrl);
+
+	switch (ctrl.bRequestType & USB_TYPE_MASK) {
+	case USB_TYPE_STANDARD:
+		switch (ctrl.bRequest) {
+		case USB_REQ_CLEAR_FEATURE:
+			recip_handler = &req_clear_feature;
+			break;
+		}
+	}
+
+	/*
+	 * setup stage / run recip
+	 */
+	if (recip_handler)
+		ret = usbhsg_recip_run_handle(priv, recip_handler, &ctrl);
+	else
+		ret = gpriv->driver->setup(&gpriv->gadget, &ctrl);
+
+	if (ret < 0)
+		usbhs_fifo_stall(pipe);
+
+	return ret;
+}
+
+static int usbhsg_irq_empty(struct usbhs_priv *priv,
+			    struct usbhs_irq_state *irq_state)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+	struct usbhsg_uep *uep;
+	struct usbhs_pipe *pipe;
+	struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+	int i, ret;
+
+	if (!irq_state->bempsts) {
+		dev_err(dev, "debug %s !!\n", __func__);
+		return -EIO;
+	}
+
+	dev_dbg(dev, "irq empty [0x%04x]\n", irq_state->bempsts);
+
+	/*
+	 * search interrupted "pipe"
+	 * not "uep".
+	 */
+	usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
+		if (!(irq_state->bempsts & (1 << i)))
+			continue;
+
+		uep	= usbhsg_pipe_to_uep(pipe);
+		ret	= usbhsg_queue_handle(uep);
+		if (ret < 0)
+			dev_err(dev, "send error %d : %d\n", i, ret);
+	}
+
+	return 0;
+}
+
+static int usbhsg_irq_ready(struct usbhs_priv *priv,
+			    struct usbhs_irq_state *irq_state)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+	struct usbhsg_uep *uep;
+	struct usbhs_pipe *pipe;
+	struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+	int i, ret;
+
+	if (!irq_state->brdysts) {
+		dev_err(dev, "debug %s !!\n", __func__);
+		return -EIO;
+	}
+
+	dev_dbg(dev, "irq ready [0x%04x]\n", irq_state->brdysts);
+
+	/*
+	 * search interrupted "pipe"
+	 * not "uep".
+	 */
+	usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
+		if (!(irq_state->brdysts & (1 << i)))
+			continue;
+
+		uep	= usbhsg_pipe_to_uep(pipe);
+		ret	= usbhsg_queue_handle(uep);
+		if (ret < 0)
+			dev_err(dev, "receive error %d : %d\n", i, ret);
+	}
+
+	return 0;
+}
+
+/*
+ *
+ *		usb_dcp_ops
+ *
+ */
+static int usbhsg_dcp_enable(struct usbhsg_uep *uep)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+	struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
+	struct usbhs_pipe *pipe;
+
+	/*
+	 *********  assume under spin lock  *********
+	 */
+
+	pipe = usbhs_dcp_malloc(priv);
+	if (!pipe)
+		return -EIO;
+
+	uep->pipe		= pipe;
+	uep->pipe->mod_private	= uep;
+	INIT_LIST_HEAD(&uep->list);
+
+	return 0;
+}
+
+#define usbhsg_dcp_disable usbhsg_pipe_disable
+static int usbhsg_pipe_disable(struct usbhsg_uep *uep)
+{
+	struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+	struct usbhsg_request *ureq;
+	int disable = 0;
+
+	/*
+	 *********  assume under spin lock  *********
+	 */
+
+	usbhs_fifo_disable(pipe);
+
+	/*
+	 * disable pipe irq
+	 */
+	usbhsg_irq_empty_ctrl(uep, disable);
+	usbhsg_irq_ready_ctrl(uep, disable);
+
+	while (1) {
+		ureq = usbhsg_queue_get(uep);
+		if (!ureq)
+			break;
+
+		usbhsg_queue_pop(uep, ureq, -ECONNRESET);
+	}
+
+	uep->pipe->mod_private	= NULL;
+	uep->pipe		= NULL;
+
+	return 0;
+}
+
+/*
+ *
+ *		usb_ep_ops
+ *
+ */
+static int usbhsg_ep_enable(struct usb_ep *ep,
+			 const struct usb_endpoint_descriptor *desc)
+{
+	struct usbhsg_uep *uep   = usbhsg_ep_to_uep(ep);
+	struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+	struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
+	struct usbhs_pipe *pipe;
+	spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv);
+	unsigned long flags;
+	int ret = -EIO;
+
+	/********************  spin lock ********************/
+	spin_lock_irqsave(lock, flags);
+
+	pipe = usbhs_pipe_malloc(priv, desc);
+	if (pipe) {
+		uep->pipe		= pipe;
+		pipe->mod_private	= uep;
+		INIT_LIST_HEAD(&uep->list);
+
+		if (usb_endpoint_dir_in(desc))
+			uep->handler = &usbhsg_handler_send_packet;
+		else
+			uep->handler = &usbhsg_handler_recv_packet;
+
+		ret = 0;
+	}
+	spin_unlock_irqrestore(lock, flags);
+	/********************  spin unlock ******************/
+
+	return ret;
+}
+
+static int usbhsg_ep_disable(struct usb_ep *ep)
+{
+	struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep);
+	struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+	spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv);
+	unsigned long flags;
+	int ret;
+
+	/********************  spin lock ********************/
+	spin_lock_irqsave(lock, flags);
+	ret = usbhsg_pipe_disable(uep);
+	spin_unlock_irqrestore(lock, flags);
+	/********************  spin unlock ******************/
+
+	return ret;
+}
+
+static struct usb_request *usbhsg_ep_alloc_request(struct usb_ep *ep,
+						   gfp_t gfp_flags)
+{
+	struct usbhsg_request *ureq;
+
+	ureq = kzalloc(sizeof *ureq, gfp_flags);
+	if (!ureq)
+		return NULL;
+
+	INIT_LIST_HEAD(&ureq->node);
+	return &ureq->req;
+}
+
+static void usbhsg_ep_free_request(struct usb_ep *ep,
+				   struct usb_request *req)
+{
+	struct usbhsg_request *ureq = usbhsg_req_to_ureq(req);
+
+	WARN_ON(!list_empty(&ureq->node));
+	kfree(ureq);
+}
+
+static int usbhsg_ep_queue(struct usb_ep *ep, struct usb_request *req,
+			  gfp_t gfp_flags)
+{
+	struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep);
+	struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+	struct usbhsg_request *ureq = usbhsg_req_to_ureq(req);
+	struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+	spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv);
+	unsigned long flags;
+	int ret = 0;
+	int is_locked;
+
+	/*
+	 * CAUTION [*endpoint queue*]
+	 *
+	 * This function will be called from usb_request :: complete
+	 * or usb driver timing.
+	 * If this function is called from usb_request :: complete,
+	 * it is already under spinlock on this driver.
+	 * but it is called frm usb driver, this function should call spinlock.
+	 *
+	 * This function is using spin_trylock_irqsave to solve this issue.
+	 * if "is_locked" is 1, this mean this function lock it.
+	 * but if it is 0, this mean it is already under spin lock.
+	 * see also
+	 *   CAUTION [*queue handler*]
+	 *   CAUTION [*request complete*]
+	 */
+
+	/********************  spin lock ********************/
+	is_locked = spin_trylock_irqsave(lock, flags);
+
+	/* param check */
+	if (usbhsg_is_not_connected(gpriv)	||
+	    unlikely(!gpriv->driver)		||
+	    unlikely(!pipe))
+		ret = -ESHUTDOWN;
+	else
+		usbhsg_queue_push(uep, ureq);
+
+	if (is_locked)
+		spin_unlock_irqrestore(lock, flags);
+	/********************  spin unlock ******************/
+
+	usbhsg_queue_prepare(uep);
+
+	return ret;
+}
+
+static int usbhsg_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
+{
+	struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep);
+	struct usbhsg_request *ureq = usbhsg_req_to_ureq(req);
+	struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+	spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv);
+	unsigned long flags;
+	int is_locked;
+
+	/*
+	 * see
+	 *   CAUTION [*queue handler*]
+	 *   CAUTION [*endpoint queue*]
+	 *   CAUTION [*request complete*]
+	 */
+
+	/********************  spin lock ********************/
+	is_locked = spin_trylock_irqsave(lock, flags);
+
+	usbhsg_queue_pop(uep, ureq, -ECONNRESET);
+
+	if (is_locked)
+		spin_unlock_irqrestore(lock, flags);
+	/********************  spin unlock ******************/
+
+	return 0;
+}
+
+static int __usbhsg_ep_set_halt_wedge(struct usb_ep *ep, int halt, int wedge)
+{
+	struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep);
+	struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+	struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+	struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+	spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv);
+	unsigned long flags;
+	int ret = -EAGAIN;
+	int is_locked;
+
+	/*
+	 * see
+	 *   CAUTION [*queue handler*]
+	 *   CAUTION [*endpoint queue*]
+	 *   CAUTION [*request complete*]
+	 */
+
+	/********************  spin lock ********************/
+	is_locked = spin_trylock_irqsave(lock, flags);
+	if (!usbhsg_queue_get(uep)) {
+
+		dev_dbg(dev, "set halt %d (pipe %d)\n",
+			halt, usbhs_pipe_number(pipe));
+
+		if (halt)
+			usbhs_fifo_stall(pipe);
+		else
+			usbhs_fifo_disable(pipe);
+
+		if (halt && wedge)
+			usbhsg_status_set(gpriv, USBHSG_STATUS_WEDGE);
+		else
+			usbhsg_status_clr(gpriv, USBHSG_STATUS_WEDGE);
+
+		ret = 0;
+	}
+
+	if (is_locked)
+		spin_unlock_irqrestore(lock, flags);
+	/********************  spin unlock ******************/
+
+	return ret;
+}
+
+static int usbhsg_ep_set_halt(struct usb_ep *ep, int value)
+{
+	return __usbhsg_ep_set_halt_wedge(ep, value, 0);
+}
+
+static int usbhsg_ep_set_wedge(struct usb_ep *ep)
+{
+	return __usbhsg_ep_set_halt_wedge(ep, 1, 1);
+}
+
+static struct usb_ep_ops usbhsg_ep_ops = {
+	.enable		= usbhsg_ep_enable,
+	.disable	= usbhsg_ep_disable,
+
+	.alloc_request	= usbhsg_ep_alloc_request,
+	.free_request	= usbhsg_ep_free_request,
+
+	.queue		= usbhsg_ep_queue,
+	.dequeue	= usbhsg_ep_dequeue,
+
+	.set_halt	= usbhsg_ep_set_halt,
+	.set_wedge	= usbhsg_ep_set_wedge,
+};
+
+/*
+ *		usb module start/end
+ */
+static int usbhsg_try_start(struct usbhs_priv *priv, u32 status)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+	struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv);
+	struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+	struct device *dev = usbhs_priv_to_dev(priv);
+	spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv);
+	unsigned long flags;
+
+	/********************  spin lock ********************/
+	spin_lock_irqsave(lock, flags);
+
+	/*
+	 * enable interrupt and systems if ready
+	 */
+	usbhsg_status_set(gpriv, status);
+	if (!(usbhsg_status_has(gpriv, USBHSG_STATUS_STARTED) &&
+	      usbhsg_status_has(gpriv, USBHSG_STATUS_REGISTERD)))
+		goto usbhsg_try_start_unlock;
+
+	dev_dbg(dev, "start gadget\n");
+
+	/*
+	 * pipe initialize and enable DCP
+	 */
+	usbhs_pipe_init(priv);
+	usbhsg_dcp_enable(dcp);
+
+	/*
+	 * system config enble
+	 * - HI speed
+	 * - function
+	 * - usb module
+	 */
+	usbhs_sys_hispeed_ctrl(priv, 1);
+	usbhs_sys_function_ctrl(priv, 1);
+	usbhs_sys_usb_ctrl(priv, 1);
+
+	/*
+	 * enable irq callback
+	 */
+	mod->irq_dev_state	= usbhsg_irq_dev_state;
+	mod->irq_ctrl_stage	= usbhsg_irq_ctrl_stage;
+	mod->irq_empty		= usbhsg_irq_empty;
+	mod->irq_ready		= usbhsg_irq_ready;
+	mod->irq_bempsts	= 0;
+	mod->irq_brdysts	= 0;
+	usbhs_irq_callback_update(priv, mod);
+
+usbhsg_try_start_unlock:
+	spin_unlock_irqrestore(lock, flags);
+	/********************  spin unlock ********************/
+
+	return 0;
+}
+
+static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+	struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+	struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv);
+	struct device *dev = usbhs_priv_to_dev(priv);
+	spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv);
+	unsigned long flags;
+
+	/********************  spin lock ********************/
+	spin_lock_irqsave(lock, flags);
+
+	/*
+	 * disable interrupt and systems if 1st try
+	 */
+	usbhsg_status_clr(gpriv, status);
+	if (!usbhsg_status_has(gpriv, USBHSG_STATUS_STARTED) &&
+	    !usbhsg_status_has(gpriv, USBHSG_STATUS_REGISTERD))
+		goto usbhsg_try_stop_unlock;
+
+	/* disable all irq */
+	mod->irq_dev_state	= NULL;
+	mod->irq_ctrl_stage	= NULL;
+	mod->irq_empty		= NULL;
+	mod->irq_ready		= NULL;
+	mod->irq_bempsts	= 0;
+	mod->irq_brdysts	= 0;
+	usbhs_irq_callback_update(priv, mod);
+
+	usbhsg_dcp_disable(dcp);
+
+	gpriv->gadget.speed = USB_SPEED_UNKNOWN;
+
+	/* disable sys */
+	usbhs_sys_hispeed_ctrl(priv, 0);
+	usbhs_sys_function_ctrl(priv, 0);
+	usbhs_sys_usb_ctrl(priv, 0);
+
+	spin_unlock_irqrestore(lock, flags);
+	/********************  spin unlock ********************/
+
+	if (gpriv->driver &&
+	    gpriv->driver->disconnect)
+		gpriv->driver->disconnect(&gpriv->gadget);
+
+	dev_dbg(dev, "stop gadget\n");
+
+	return 0;
+
+usbhsg_try_stop_unlock:
+	spin_unlock_irqrestore(lock, flags);
+
+	return 0;
+}
+
+/*
+ *
+ *		linux usb function
+ *
+ */
+struct usbhsg_gpriv *the_controller;
+int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+			    int (*bind)(struct usb_gadget *))
+{
+	struct usbhsg_gpriv *gpriv = the_controller;
+	struct usbhs_priv *priv;
+	struct device *dev;
+	int ret;
+
+	if (!bind		||
+	    !driver		||
+	    !driver->setup	||
+	    driver->speed != USB_SPEED_HIGH)
+		return -EINVAL;
+	if (!gpriv)
+		return -ENODEV;
+	if (gpriv->driver)
+		return -EBUSY;
+
+	dev  = usbhsg_gpriv_to_dev(gpriv);
+	priv = usbhsg_gpriv_to_priv(gpriv);
+
+	/* first hook up the driver ... */
+	gpriv->driver = driver;
+	gpriv->gadget.dev.driver = &driver->driver;
+
+	ret = device_add(&gpriv->gadget.dev);
+	if (ret) {
+		dev_err(dev, "device_add error %d\n", ret);
+		goto add_fail;
+	}
+
+	ret = bind(&gpriv->gadget);
+	if (ret) {
+		dev_err(dev, "bind to driver %s error %d\n",
+			driver->driver.name, ret);
+		goto bind_fail;
+	}
+
+	dev_dbg(dev, "bind %s\n", driver->driver.name);
+
+	return usbhsg_try_start(priv, USBHSG_STATUS_REGISTERD);
+
+bind_fail:
+	device_del(&gpriv->gadget.dev);
+add_fail:
+	gpriv->driver = NULL;
+	gpriv->gadget.dev.driver = NULL;
+
+	return ret;
+}
+EXPORT_SYMBOL(usb_gadget_probe_driver);
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+	struct usbhsg_gpriv *gpriv = the_controller;
+	struct usbhs_priv *priv;
+	struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+
+	if (!gpriv)
+		return -ENODEV;
+
+	if (!driver		||
+	    !driver->unbind	||
+	    driver != gpriv->driver)
+		return -EINVAL;
+
+	dev  = usbhsg_gpriv_to_dev(gpriv);
+	priv = usbhsg_gpriv_to_priv(gpriv);
+
+	usbhsg_try_stop(priv, USBHSG_STATUS_REGISTERD);
+	device_del(&gpriv->gadget.dev);
+	gpriv->driver = NULL;
+
+	if (driver->disconnect)
+		driver->disconnect(&gpriv->gadget);
+
+	driver->unbind(&gpriv->gadget);
+	dev_dbg(dev, "unbind %s\n", driver->driver.name);
+
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+/*
+ *		usb gadget ops
+ */
+static int usbhsg_get_frame(struct usb_gadget *gadget)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget);
+	struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
+
+	return usbhs_frame_get_num(priv);
+}
+
+static struct usb_gadget_ops usbhsg_gadget_ops = {
+	.get_frame		= usbhsg_get_frame,
+};
+
+static int usbhsg_start(struct usbhs_priv *priv)
+{
+	return usbhsg_try_start(priv, USBHSG_STATUS_STARTED);
+}
+
+static int usbhsg_stop(struct usbhs_priv *priv)
+{
+	return usbhsg_try_stop(priv, USBHSG_STATUS_STARTED);
+}
+
+int __devinit usbhs_mod_gadget_probe(struct usbhs_priv *priv)
+{
+	struct usbhsg_gpriv *gpriv;
+	struct usbhsg_uep *uep;
+	struct device *dev = usbhs_priv_to_dev(priv);
+	int pipe_size = usbhs_get_dparam(priv, pipe_size);
+	int i;
+
+	gpriv = kzalloc(sizeof(struct usbhsg_gpriv), GFP_KERNEL);
+	if (!gpriv) {
+		dev_err(dev, "Could not allocate gadget priv\n");
+		return -ENOMEM;
+	}
+
+	uep = kzalloc(sizeof(struct usbhsg_uep) * pipe_size, GFP_KERNEL);
+	if (!uep) {
+		dev_err(dev, "Could not allocate ep\n");
+		goto usbhs_mod_gadget_probe_err_gpriv;
+	}
+
+	/*
+	 * CAUTION
+	 *
+	 * There is no guarantee that it is possible to access usb module here.
+	 * Don't accesses to it.
+	 * The accesse will be enable after "usbhsg_start"
+	 */
+
+	/*
+	 * register itself
+	 */
+	usbhs_mod_register(priv, &gpriv->mod, USBHS_GADGET);
+
+	/* init gpriv */
+	gpriv->mod.name		= "gadget";
+	gpriv->mod.start	= usbhsg_start;
+	gpriv->mod.stop		= usbhsg_stop;
+	gpriv->uep		= uep;
+	gpriv->uep_size		= pipe_size;
+	usbhsg_status_init(gpriv);
+
+	/*
+	 * init gadget
+	 */
+	device_initialize(&gpriv->gadget.dev);
+	dev_set_name(&gpriv->gadget.dev, "gadget");
+	gpriv->gadget.dev.parent	= dev;
+	gpriv->gadget.name		= "renesas_usbhs_udc";
+	gpriv->gadget.ops		= &usbhsg_gadget_ops;
+	gpriv->gadget.is_dualspeed	= 1;
+
+	INIT_LIST_HEAD(&gpriv->gadget.ep_list);
+
+	/*
+	 * init usb_ep
+	 */
+	usbhsg_for_each_uep_with_dcp(uep, gpriv, i) {
+		uep->gpriv	= gpriv;
+		snprintf(uep->ep_name, EP_NAME_SIZE, "ep%d", i);
+
+		uep->ep.name		= uep->ep_name;
+		uep->ep.ops		= &usbhsg_ep_ops;
+		INIT_LIST_HEAD(&uep->ep.ep_list);
+		INIT_LIST_HEAD(&uep->list);
+
+		/* init DCP */
+		if (usbhsg_is_dcp(uep)) {
+			gpriv->gadget.ep0 = &uep->ep;
+			uep->ep.maxpacket = 64;
+		}
+		/* init normal pipe */
+		else {
+			uep->ep.maxpacket = 512;
+			list_add_tail(&uep->ep.ep_list, &gpriv->gadget.ep_list);
+		}
+	}
+
+	the_controller = gpriv;
+
+	dev_info(dev, "gadget probed\n");
+
+	return 0;
+
+usbhs_mod_gadget_probe_err_gpriv:
+	kfree(gpriv);
+
+	return -ENOMEM;
+}
+
+void __devexit usbhs_mod_gadget_remove(struct usbhs_priv *priv)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+
+	kfree(gpriv);
+}
-- 
cgit v1.2.3-70-g09d2


From bce1a702ed9bd9aa3549352b3134e110bf076586 Mon Sep 17 00:00:00 2001
From: Dmitry Torokhov <dtor@vmware.com>
Date: Sun, 20 Mar 2011 02:02:39 -0700
Subject: USB: change the way we initialize format strings

Changing initialization from

	static const char *string = "blah";

to

	static const char string[] = "blah";

saves us one pointer per each string.

Signed-off-by: Dmitry Torokhov <dtor@vmware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/core/devices.c | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

(limited to 'drivers/usb')

diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index a3d2e2399655..d382291d1b15 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -64,49 +64,49 @@
 /* Define ALLOW_SERIAL_NUMBER if you want to see the serial number of devices */
 #define ALLOW_SERIAL_NUMBER
 
-static const char *format_topo =
+static const char format_topo[] =
 /* T:  Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=dddd MxCh=dd */
 "\nT:  Bus=%2.2d Lev=%2.2d Prnt=%2.2d Port=%2.2d Cnt=%2.2d Dev#=%3d Spd=%-4s MxCh=%2d\n";
 
-static const char *format_string_manufacturer =
+static const char format_string_manufacturer[] =
 /* S:  Manufacturer=xxxx */
   "S:  Manufacturer=%.100s\n";
 
-static const char *format_string_product =
+static const char format_string_product[] =
 /* S:  Product=xxxx */
   "S:  Product=%.100s\n";
 
 #ifdef ALLOW_SERIAL_NUMBER
-static const char *format_string_serialnumber =
+static const char format_string_serialnumber[] =
 /* S:  SerialNumber=xxxx */
   "S:  SerialNumber=%.100s\n";
 #endif
 
-static const char *format_bandwidth =
+static const char format_bandwidth[] =
 /* B:  Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd */
   "B:  Alloc=%3d/%3d us (%2d%%), #Int=%3d, #Iso=%3d\n";
 
-static const char *format_device1 =
+static const char format_device1[] =
 /* D:  Ver=xx.xx Cls=xx(sssss) Sub=xx Prot=xx MxPS=dd #Cfgs=dd */
   "D:  Ver=%2x.%02x Cls=%02x(%-5s) Sub=%02x Prot=%02x MxPS=%2d #Cfgs=%3d\n";
 
-static const char *format_device2 =
+static const char format_device2[] =
 /* P:  Vendor=xxxx ProdID=xxxx Rev=xx.xx */
   "P:  Vendor=%04x ProdID=%04x Rev=%2x.%02x\n";
 
-static const char *format_config =
+static const char format_config[] =
 /* C:  #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA */
   "C:%c #Ifs=%2d Cfg#=%2d Atr=%02x MxPwr=%3dmA\n";
 
-static const char *format_iad =
+static const char format_iad[] =
 /* A:  FirstIf#=dd IfCount=dd Cls=xx(sssss) Sub=xx Prot=xx */
   "A:  FirstIf#=%2d IfCount=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x\n";
 
-static const char *format_iface =
+static const char format_iface[] =
 /* I:  If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=xxxx*/
   "I:%c If#=%2d Alt=%2d #EPs=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x Driver=%s\n";
 
-static const char *format_endpt =
+static const char format_endpt[] =
 /* E:  Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=D?s */
   "E:  Ad=%02x(%c) Atr=%02x(%-4s) MxPS=%4d Ivl=%d%cs\n";
 
-- 
cgit v1.2.3-70-g09d2


From 59d7fec7c6908604862658a3679ac44c2c3eea44 Mon Sep 17 00:00:00 2001
From: Johan Hovold <jhovold@gmail.com>
Date: Tue, 22 Mar 2011 11:12:12 +0100
Subject: USB: cdc-acm: add missing newlines to dev_dbg and dev_err

Add missing newline to two dev_dbg and dev_err messages.

Signed-off-by: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/class/cdc-acm.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

(limited to 'drivers/usb')

diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index e057e5381465..849aa04e6093 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -341,7 +341,7 @@ exit:
 	retval = usb_submit_urb(urb, GFP_ATOMIC);
 	if (retval)
 		dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with "
-			"result %d", __func__, retval);
+			"result %d\n", __func__, retval);
 }
 
 /* data interface returns incoming bytes, or we got unthrottled */
@@ -355,7 +355,7 @@ static void acm_read_bulk(struct urb *urb)
 	dbg("Entering acm_read_bulk with status %d", status);
 
 	if (!ACM_READY(acm)) {
-		dev_dbg(&acm->data->dev, "Aborting, acm not ready");
+		dev_dbg(&acm->data->dev, "Aborting, acm not ready\n");
 		return;
 	}
 	usb_mark_last_busy(acm->dev);
@@ -1224,7 +1224,7 @@ made_compressed_probe:
 		snd->urb = usb_alloc_urb(0, GFP_KERNEL);
 		if (snd->urb == NULL) {
 			dev_dbg(&intf->dev,
-				"out of memory (write urbs usb_alloc_urb)");
+				"out of memory (write urbs usb_alloc_urb)\n");
 			goto alloc_fail8;
 		}
 
-- 
cgit v1.2.3-70-g09d2


From 255ab56c5ae30165d506b837f6576944a02ecdf0 Mon Sep 17 00:00:00 2001
From: Johan Hovold <jhovold@gmail.com>
Date: Tue, 22 Mar 2011 11:12:13 +0100
Subject: USB: cdc-acm: use dev_err to report failed allocations

Upgrade out-of-memory dev_dbg to dev_err.

Signed-off-by: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/class/cdc-acm.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

(limited to 'drivers/usb')

diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 849aa04e6093..c9f8a837c5fa 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -1143,7 +1143,7 @@ made_compressed_probe:
 
 	acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
 	if (acm == NULL) {
-		dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n");
+		dev_err(&intf->dev, "out of memory (acm kzalloc)\n");
 		goto alloc_fail;
 	}
 
@@ -1179,19 +1179,19 @@ made_compressed_probe:
 
 	buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
 	if (!buf) {
-		dev_dbg(&intf->dev, "out of memory (ctrl buffer alloc)\n");
+		dev_err(&intf->dev, "out of memory (ctrl buffer alloc)\n");
 		goto alloc_fail2;
 	}
 	acm->ctrl_buffer = buf;
 
 	if (acm_write_buffers_alloc(acm) < 0) {
-		dev_dbg(&intf->dev, "out of memory (write buffer alloc)\n");
+		dev_err(&intf->dev, "out of memory (write buffer alloc)\n");
 		goto alloc_fail4;
 	}
 
 	acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
 	if (!acm->ctrlurb) {
-		dev_dbg(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
+		dev_err(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
 		goto alloc_fail5;
 	}
 	for (i = 0; i < num_rx_buf; i++) {
@@ -1199,7 +1199,7 @@ made_compressed_probe:
 
 		rcv->urb = usb_alloc_urb(0, GFP_KERNEL);
 		if (rcv->urb == NULL) {
-			dev_dbg(&intf->dev,
+			dev_err(&intf->dev,
 				"out of memory (read urbs usb_alloc_urb)\n");
 			goto alloc_fail6;
 		}
@@ -1213,7 +1213,7 @@ made_compressed_probe:
 		rb->base = usb_alloc_coherent(acm->dev, readsize,
 				GFP_KERNEL, &rb->dma);
 		if (!rb->base) {
-			dev_dbg(&intf->dev,
+			dev_err(&intf->dev,
 				"out of memory (read bufs usb_alloc_coherent)\n");
 			goto alloc_fail7;
 		}
@@ -1223,7 +1223,7 @@ made_compressed_probe:
 
 		snd->urb = usb_alloc_urb(0, GFP_KERNEL);
 		if (snd->urb == NULL) {
-			dev_dbg(&intf->dev,
+			dev_err(&intf->dev,
 				"out of memory (write urbs usb_alloc_urb)\n");
 			goto alloc_fail8;
 		}
-- 
cgit v1.2.3-70-g09d2


From 1d9846e505febb71255b098910ace741433312b7 Mon Sep 17 00:00:00 2001
From: Johan Hovold <jhovold@gmail.com>
Date: Tue, 22 Mar 2011 11:12:14 +0100
Subject: USB: cdc-acm: clean up dev_err and dev_dbg

Clean up some dev_err and dev_dbg messages and make sure that the
appropriate interface device is used for reporting consistently
throughout.

Signed-off-by: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/class/cdc-acm.c | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

(limited to 'drivers/usb')

diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index c9f8a837c5fa..612b9ff0fcf2 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -340,8 +340,8 @@ static void acm_ctrl_irq(struct urb *urb)
 exit:
 	retval = usb_submit_urb(urb, GFP_ATOMIC);
 	if (retval)
-		dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with "
-			"result %d\n", __func__, retval);
+		dev_err(&acm->control->dev, "%s - usb_submit_urb failed: %d\n",
+							__func__, retval);
 }
 
 /* data interface returns incoming bytes, or we got unthrottled */
@@ -355,13 +355,14 @@ static void acm_read_bulk(struct urb *urb)
 	dbg("Entering acm_read_bulk with status %d", status);
 
 	if (!ACM_READY(acm)) {
-		dev_dbg(&acm->data->dev, "Aborting, acm not ready\n");
+		dev_dbg(&acm->data->dev, "%s - acm not ready\n", __func__);
 		return;
 	}
 	usb_mark_last_busy(acm->dev);
 
 	if (status)
-		dev_dbg(&acm->data->dev, "bulk rx status %d\n", status);
+		dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n",
+							__func__, status);
 
 	buf = rcv->buffer;
 	buf->size = urb->actual_length;
@@ -511,7 +512,8 @@ static void acm_write_bulk(struct urb *urb)
 
 	if (verbose || urb->status
 			|| (urb->actual_length != urb->transfer_buffer_length))
-		dev_dbg(&acm->data->dev, "tx %d/%d bytes -- > %d\n",
+		dev_dbg(&acm->data->dev, "%s - len %d/%d, status %d\n",
+			__func__,
 			urb->actual_length,
 			urb->transfer_buffer_length,
 			urb->status);
@@ -530,7 +532,8 @@ static void acm_softint(struct work_struct *work)
 	struct acm *acm = container_of(work, struct acm, work);
 	struct tty_struct *tty;
 
-	dev_vdbg(&acm->data->dev, "tx work\n");
+	dev_vdbg(&acm->data->dev, "%s\n", __func__);
+
 	if (!ACM_READY(acm))
 		return;
 	tty = tty_port_tty_get(&acm->port);
-- 
cgit v1.2.3-70-g09d2


From a5cc7ef92f69a88a1984cc3e09f6c19656efeb2e Mon Sep 17 00:00:00 2001
From: Johan Hovold <jhovold@gmail.com>
Date: Tue, 22 Mar 2011 11:12:15 +0100
Subject: USB: cdc-acm: replace dbg macros with dev_dbg

Replace all remaining instances of dbg with dev_dbg.

Signed-off-by: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/class/cdc-acm.c | 80 +++++++++++++++++++++++++++++----------------
 1 file changed, 52 insertions(+), 28 deletions(-)

(limited to 'drivers/usb')

diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 612b9ff0fcf2..fee7d8b94fe2 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -111,8 +111,9 @@ static int acm_ctrl_msg(struct acm *acm, int request, int value,
 		request, USB_RT_ACM, value,
 		acm->control->altsetting[0].desc.bInterfaceNumber,
 		buf, len, 5000);
-	dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d",
-						request, value, len, retval);
+	dev_dbg(&acm->control->dev,
+			"%s - rq 0x%02x, val %#x, len %#x, result %d\n",
+			__func__, request, value, len, retval);
 	return retval < 0 ? retval : 0;
 }
 
@@ -192,7 +193,9 @@ static int acm_start_wb(struct acm *acm, struct acm_wb *wb)
 
 	rc = usb_submit_urb(wb->urb, GFP_ATOMIC);
 	if (rc < 0) {
-		dbg("usb_submit_urb(write bulk) failed: %d", rc);
+		dev_err(&acm->data->dev,
+			"%s - usb_submit_urb(write bulk) failed: %d\n",
+			__func__, rc);
 		acm_write_done(acm, wb);
 	}
 	return rc;
@@ -211,7 +214,8 @@ static int acm_write_start(struct acm *acm, int wbn)
 		return -ENODEV;
 	}
 
-	dbg("%s susp_count: %d", __func__, acm->susp_count);
+	dev_dbg(&acm->data->dev, "%s - susp_count %d\n", __func__,
+							acm->susp_count);
 	usb_autopm_get_interface_async(acm->control);
 	if (acm->susp_count) {
 		if (!acm->delayed_wb)
@@ -287,10 +291,14 @@ static void acm_ctrl_irq(struct urb *urb)
 	case -ENOENT:
 	case -ESHUTDOWN:
 		/* this urb is terminated, clean up */
-		dbg("%s - urb shutting down with status: %d", __func__, status);
+		dev_dbg(&acm->control->dev,
+				"%s - urb shutting down with status: %d\n",
+				__func__, status);
 		return;
 	default:
-		dbg("%s - nonzero urb status received: %d", __func__, status);
+		dev_dbg(&acm->control->dev,
+				"%s - nonzero urb status received: %d\n",
+				__func__, status);
 		goto exit;
 	}
 
@@ -302,8 +310,8 @@ static void acm_ctrl_irq(struct urb *urb)
 	data = (unsigned char *)(dr + 1);
 	switch (dr->bNotificationType) {
 	case USB_CDC_NOTIFY_NETWORK_CONNECTION:
-		dbg("%s network", dr->wValue ?
-					"connected to" : "disconnected from");
+		dev_dbg(&acm->control->dev, "%s - network connection: %d\n",
+							__func__, dr->wValue);
 		break;
 
 	case USB_CDC_NOTIFY_SERIAL_STATE:
@@ -313,7 +321,8 @@ static void acm_ctrl_irq(struct urb *urb)
 		if (tty) {
 			if (!acm->clocal &&
 				(acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) {
-				dbg("calling hangup");
+				dev_dbg(&acm->control->dev,
+					"%s - calling hangup\n", __func__);
 				tty_hangup(tty);
 			}
 			tty_kref_put(tty);
@@ -321,7 +330,10 @@ static void acm_ctrl_irq(struct urb *urb)
 
 		acm->ctrlin = newctrl;
 
-		dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c",
+		dev_dbg(&acm->control->dev,
+			"%s - input control lines: dcd%c dsr%c break%c "
+			"ring%c framing%c parity%c overrun%c\n",
+			__func__,
 			acm->ctrlin & ACM_CTRL_DCD ? '+' : '-',
 			acm->ctrlin & ACM_CTRL_DSR ? '+' : '-',
 			acm->ctrlin & ACM_CTRL_BRK ? '+' : '-',
@@ -332,7 +344,10 @@ static void acm_ctrl_irq(struct urb *urb)
 			break;
 
 	default:
-		dbg("unknown notification %d received: index %d len %d data0 %d data1 %d",
+		dev_dbg(&acm->control->dev,
+			"%s - unknown notification %d received: index %d "
+			"len %d data0 %d data1 %d\n",
+			__func__,
 			dr->bNotificationType, dr->wIndex,
 			dr->wLength, data[0], data[1]);
 		break;
@@ -352,7 +367,7 @@ static void acm_read_bulk(struct urb *urb)
 	struct acm *acm = rcv->instance;
 	int status = urb->status;
 
-	dbg("Entering acm_read_bulk with status %d", status);
+	dev_dbg(&acm->data->dev, "%s - status %d\n", __func__, status);
 
 	if (!ACM_READY(acm)) {
 		dev_dbg(&acm->data->dev, "%s - acm not ready\n", __func__);
@@ -395,10 +410,10 @@ static void acm_rx_tasklet(unsigned long _acm)
 	unsigned long flags;
 	unsigned char throttled;
 
-	dbg("Entering acm_rx_tasklet");
+	dev_dbg(&acm->data->dev, "%s\n", __func__);
 
 	if (!ACM_READY(acm)) {
-		dbg("acm_rx_tasklet: ACM not ready");
+		dev_dbg(&acm->data->dev, "%s - acm not ready\n", __func__);
 		return;
 	}
 
@@ -406,7 +421,7 @@ static void acm_rx_tasklet(unsigned long _acm)
 	throttled = acm->throttle;
 	spin_unlock_irqrestore(&acm->throttle_lock, flags);
 	if (throttled) {
-		dbg("acm_rx_tasklet: throttled");
+		dev_dbg(&acm->data->dev, "%s - throttled\n", __func__);
 		return;
 	}
 
@@ -423,8 +438,8 @@ next_buffer:
 	list_del(&buf->list);
 	spin_unlock_irqrestore(&acm->read_lock, flags);
 
-	dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", buf, buf->size);
-
+	dev_dbg(&acm->data->dev, "%s - processing buf 0x%p, size = %d\n",
+						__func__, buf, buf->size);
 	if (tty) {
 		spin_lock_irqsave(&acm->throttle_lock, flags);
 		throttled = acm->throttle;
@@ -434,7 +449,8 @@ next_buffer:
 			tty_flip_buffer_push(tty);
 		} else {
 			tty_kref_put(tty);
-			dbg("Throttling noticed");
+			dev_dbg(&acm->data->dev, "%s - throttling noticed\n",
+								__func__);
 			spin_lock_irqsave(&acm->read_lock, flags);
 			list_add(&buf->list, &acm->filled_read_bufs);
 			spin_unlock_irqrestore(&acm->read_lock, flags);
@@ -495,7 +511,9 @@ urbs:
 			return;
 		} else {
 			spin_unlock_irqrestore(&acm->read_lock, flags);
-			dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf);
+			dev_dbg(&acm->data->dev,
+				"%s - sending urb 0x%p, rcv 0x%p, buf 0x%p\n",
+				__func__, rcv->urb, rcv, buf);
 		}
 	}
 	spin_lock_irqsave(&acm->read_lock, flags);
@@ -552,7 +570,6 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
 	struct acm *acm;
 	int rv = -ENODEV;
 	int i;
-	dbg("Entering acm_tty_open.");
 
 	mutex_lock(&open_mutex);
 
@@ -562,6 +579,8 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
 	else
 		rv = 0;
 
+	dev_dbg(&acm->control->dev, "%s\n", __func__);
+
 	set_bit(TTY_NO_WRITE_SPLIT, &tty->flags);
 
 	tty->driver_data = acm;
@@ -581,7 +600,8 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
 
 	acm->ctrlurb->dev = acm->dev;
 	if (usb_submit_urb(acm->ctrlurb, GFP_KERNEL)) {
-		dbg("usb_submit_urb(ctrl irq) failed");
+		dev_err(&acm->control->dev,
+			"%s - usb_submit_urb(ctrl irq) failed\n", __func__);
 		goto bail_out;
 	}
 
@@ -701,13 +721,13 @@ static int acm_tty_write(struct tty_struct *tty,
 	int wbn;
 	struct acm_wb *wb;
 
-	dbg("Entering acm_tty_write to write %d bytes,", count);
-
 	if (!ACM_READY(acm))
 		return -EINVAL;
 	if (!count)
 		return 0;
 
+	dev_dbg(&acm->data->dev, "%s - count %d\n", __func__, count);
+
 	spin_lock_irqsave(&acm->write_lock, flags);
 	wbn = acm_wb_alloc(acm);
 	if (wbn < 0) {
@@ -717,7 +737,7 @@ static int acm_tty_write(struct tty_struct *tty,
 	wb = &acm->wb[wbn];
 
 	count = (count > acm->writesize) ? acm->writesize : count;
-	dbg("Get %d bytes...", count);
+	dev_dbg(&acm->data->dev, "%s - write %d\n", __func__, count);
 	memcpy(wb->buf, buf, count);
 	wb->len = count;
 	spin_unlock_irqrestore(&acm->write_lock, flags);
@@ -780,7 +800,8 @@ static int acm_tty_break_ctl(struct tty_struct *tty, int state)
 		return -EINVAL;
 	retval = acm_send_break(acm, state ? 0xffff : 0);
 	if (retval < 0)
-		dbg("send break failed");
+		dev_dbg(&acm->control->dev, "%s - send break failed\n",
+								__func__);
 	return retval;
 }
 
@@ -875,7 +896,9 @@ static void acm_tty_set_termios(struct tty_struct *tty,
 
 	if (memcmp(&acm->line, &newline, sizeof newline)) {
 		memcpy(&acm->line, &newline, sizeof newline);
-		dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate),
+		dev_dbg(&acm->control->dev, "%s - set line: %d %d %d %d\n",
+			__func__,
+			le32_to_cpu(newline.dwDTERate),
 			newline.bCharFormat, newline.bParityType,
 			newline.bDataBits);
 		acm_set_line(acm, &acm->line);
@@ -1136,7 +1159,7 @@ skip_normal_probe:
 		epwrite = t;
 	}
 made_compressed_probe:
-	dbg("interfaces are valid");
+	dev_dbg(&intf->dev, "interfaces are valid\n");
 	for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
 
 	if (minor == ACM_TTY_MINORS) {
@@ -1321,7 +1344,8 @@ alloc_fail:
 static void stop_data_traffic(struct acm *acm)
 {
 	int i;
-	dbg("Entering stop_data_traffic");
+
+	dev_dbg(&acm->control->dev, "%s\n", __func__);
 
 	tasklet_disable(&acm->urb_task);
 
-- 
cgit v1.2.3-70-g09d2


From 4fa4626cd43679dc62a73ee3e347665e761abc9c Mon Sep 17 00:00:00 2001
From: Johan Hovold <jhovold@gmail.com>
Date: Tue, 22 Mar 2011 11:12:16 +0100
Subject: USB: cdc-acm: clean up verbose debug

Clean up use of verbose debug.

Signed-off-by: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/class/cdc-acm.c | 11 ++---------
 1 file changed, 2 insertions(+), 9 deletions(-)

(limited to 'drivers/usb')

diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index fee7d8b94fe2..44a5291c5699 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -94,12 +94,6 @@ static DEFINE_MUTEX(open_mutex);
 static const struct tty_port_operations acm_port_ops = {
 };
 
-#ifdef VERBOSE_DEBUG
-#define verbose	1
-#else
-#define verbose	0
-#endif
-
 /*
  * Functions for ACM control messages.
  */
@@ -528,9 +522,8 @@ static void acm_write_bulk(struct urb *urb)
 	struct acm *acm = wb->instance;
 	unsigned long flags;
 
-	if (verbose || urb->status
-			|| (urb->actual_length != urb->transfer_buffer_length))
-		dev_dbg(&acm->data->dev, "%s - len %d/%d, status %d\n",
+	if (urb->status	|| (urb->actual_length != urb->transfer_buffer_length))
+		dev_vdbg(&acm->data->dev, "%s - len %d/%d, status %d\n",
 			__func__,
 			urb->actual_length,
 			urb->transfer_buffer_length,
-- 
cgit v1.2.3-70-g09d2


From 5e9e75f8bd97864d552ec2b8d1a00e2b3012a6b3 Mon Sep 17 00:00:00 2001
From: Johan Hovold <jhovold@gmail.com>
Date: Tue, 22 Mar 2011 11:12:17 +0100
Subject: USB: cdc-acm: use dev_vdbg in read/write paths

Replace dev_dbg with verbose dev_vdbg in read/write paths where
appropriate.

Signed-off-by: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/class/cdc-acm.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

(limited to 'drivers/usb')

diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 44a5291c5699..3c0d4b913a89 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -208,7 +208,7 @@ static int acm_write_start(struct acm *acm, int wbn)
 		return -ENODEV;
 	}
 
-	dev_dbg(&acm->data->dev, "%s - susp_count %d\n", __func__,
+	dev_vdbg(&acm->data->dev, "%s - susp_count %d\n", __func__,
 							acm->susp_count);
 	usb_autopm_get_interface_async(acm->control);
 	if (acm->susp_count) {
@@ -361,7 +361,7 @@ static void acm_read_bulk(struct urb *urb)
 	struct acm *acm = rcv->instance;
 	int status = urb->status;
 
-	dev_dbg(&acm->data->dev, "%s - status %d\n", __func__, status);
+	dev_vdbg(&acm->data->dev, "%s - status %d\n", __func__, status);
 
 	if (!ACM_READY(acm)) {
 		dev_dbg(&acm->data->dev, "%s - acm not ready\n", __func__);
@@ -404,7 +404,7 @@ static void acm_rx_tasklet(unsigned long _acm)
 	unsigned long flags;
 	unsigned char throttled;
 
-	dev_dbg(&acm->data->dev, "%s\n", __func__);
+	dev_vdbg(&acm->data->dev, "%s\n", __func__);
 
 	if (!ACM_READY(acm)) {
 		dev_dbg(&acm->data->dev, "%s - acm not ready\n", __func__);
@@ -432,7 +432,7 @@ next_buffer:
 	list_del(&buf->list);
 	spin_unlock_irqrestore(&acm->read_lock, flags);
 
-	dev_dbg(&acm->data->dev, "%s - processing buf 0x%p, size = %d\n",
+	dev_vdbg(&acm->data->dev, "%s - processing buf 0x%p, size = %d\n",
 						__func__, buf, buf->size);
 	if (tty) {
 		spin_lock_irqsave(&acm->throttle_lock, flags);
@@ -505,7 +505,7 @@ urbs:
 			return;
 		} else {
 			spin_unlock_irqrestore(&acm->read_lock, flags);
-			dev_dbg(&acm->data->dev,
+			dev_vdbg(&acm->data->dev,
 				"%s - sending urb 0x%p, rcv 0x%p, buf 0x%p\n",
 				__func__, rcv->urb, rcv, buf);
 		}
@@ -719,7 +719,7 @@ static int acm_tty_write(struct tty_struct *tty,
 	if (!count)
 		return 0;
 
-	dev_dbg(&acm->data->dev, "%s - count %d\n", __func__, count);
+	dev_vdbg(&acm->data->dev, "%s - count %d\n", __func__, count);
 
 	spin_lock_irqsave(&acm->write_lock, flags);
 	wbn = acm_wb_alloc(acm);
@@ -730,7 +730,7 @@ static int acm_tty_write(struct tty_struct *tty,
 	wb = &acm->wb[wbn];
 
 	count = (count > acm->writesize) ? acm->writesize : count;
-	dev_dbg(&acm->data->dev, "%s - write %d\n", __func__, count);
+	dev_vdbg(&acm->data->dev, "%s - write %d\n", __func__, count);
 	memcpy(wb->buf, buf, count);
 	wb->len = count;
 	spin_unlock_irqrestore(&acm->write_lock, flags);
-- 
cgit v1.2.3-70-g09d2


From a2c7b9353e8f782590852052fe2948692020147e Mon Sep 17 00:00:00 2001
From: Johan Hovold <jhovold@gmail.com>
Date: Tue, 22 Mar 2011 11:12:18 +0100
Subject: USB: cdc-acm: remove version information and changelog

Remove driver version and changelog which can be retrieved from git
history.

Signed-off-by: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/class/cdc-acm.c | 32 ++------------------------------
 1 file changed, 2 insertions(+), 30 deletions(-)

(limited to 'drivers/usb')

diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 3c0d4b913a89..a693381ed3cc 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -12,30 +12,6 @@
  *
  * Sponsored by SuSE
  *
- * ChangeLog:
- *	v0.9  - thorough cleaning, URBification, almost a rewrite
- *	v0.10 - some more cleanups
- *	v0.11 - fixed flow control, read error doesn't stop reads
- *	v0.12 - added TIOCM ioctls, added break handling, made struct acm
- *		kmalloced
- *	v0.13 - added termios, added hangup
- *	v0.14 - sized down struct acm
- *	v0.15 - fixed flow control again - characters could be lost
- *	v0.16 - added code for modems with swapped data and control interfaces
- *	v0.17 - added new style probing
- *	v0.18 - fixed new style probing for devices with more configurations
- *	v0.19 - fixed CLOCAL handling (thanks to Richard Shih-Ping Chan)
- *	v0.20 - switched to probing on interface (rather than device) class
- *	v0.21 - revert to probing on device for devices with multiple configs
- *	v0.22 - probe only the control interface. if usbcore doesn't choose the
- *		config we want, sysadmin changes bConfigurationValue in sysfs.
- *	v0.23 - use softirq for rx processing, as needed by tty layer
- *	v0.24 - change probe method to evaluate CDC union descriptor
- *	v0.25 - downstream tasks paralelized to maximize throughput
- *	v0.26 - multiple write urbs, writesize increased
- */
-
-/*
  * 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
@@ -76,10 +52,7 @@
 
 #define ACM_CLOSE_TIMEOUT	15	/* seconds to let writes drain */
 
-/*
- * Version Information
- */
-#define DRIVER_VERSION "v0.26"
+
 #define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek"
 #define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
 
@@ -1736,8 +1709,7 @@ static int __init acm_init(void)
 		return retval;
 	}
 
-	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
-	       DRIVER_DESC "\n");
+	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
 
 	return 0;
 }
-- 
cgit v1.2.3-70-g09d2


From 3a42610812c55e8a60e2660e01c6cb6eb01d4171 Mon Sep 17 00:00:00 2001
From: Johan Hovold <jhovold@gmail.com>
Date: Tue, 22 Mar 2011 11:12:19 +0100
Subject: USB: cdc-acm: remove superfluous prototype

Remove unnecessary acm_tty_chars_in_buffer prototype.

Signed-off-by: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/class/cdc-acm.c | 2 --
 1 file changed, 2 deletions(-)

(limited to 'drivers/usb')

diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index a693381ed3cc..b84ccec8fc7b 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -626,8 +626,6 @@ static void acm_tty_unregister(struct acm *acm)
 	kfree(acm);
 }
 
-static int acm_tty_chars_in_buffer(struct tty_struct *tty);
-
 static void acm_port_down(struct acm *acm)
 {
 	int i, nr = acm->rx_buflimit;
-- 
cgit v1.2.3-70-g09d2


From 6fb6b8846458afb7b5689f86cf35705c40eb70e5 Mon Sep 17 00:00:00 2001
From: Johan Hovold <jhovold@gmail.com>
Date: Tue, 22 Mar 2011 11:12:20 +0100
Subject: USB: cdc-acm: remove unused drain-delay code

The drain-delay code is no longer used, so remove it.

Signed-off-by: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/class/cdc-acm.c | 6 ------
 drivers/usb/class/cdc-acm.h | 1 -
 2 files changed, 7 deletions(-)

(limited to 'drivers/usb')

diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index b84ccec8fc7b..e0f9febee7e6 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -50,9 +50,6 @@
 #include "cdc-acm.h"
 
 
-#define ACM_CLOSE_TIMEOUT	15	/* seconds to let writes drain */
-
-
 #define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek"
 #define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
 
@@ -507,8 +504,6 @@ static void acm_write_bulk(struct urb *urb)
 	spin_unlock_irqrestore(&acm->write_lock, flags);
 	if (ACM_READY(acm))
 		schedule_work(&acm->work);
-	else
-		wake_up_interruptible(&acm->drain_wait);
 }
 
 static void acm_softint(struct work_struct *work)
@@ -1155,7 +1150,6 @@ made_compressed_probe:
 	acm->urb_task.func = acm_rx_tasklet;
 	acm->urb_task.data = (unsigned long) acm;
 	INIT_WORK(&acm->work, acm_softint);
-	init_waitqueue_head(&acm->drain_wait);
 	spin_lock_init(&acm->throttle_lock);
 	spin_lock_init(&acm->write_lock);
 	spin_lock_init(&acm->read_lock);
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index b4ea54dbf323..7282d1f4912b 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -112,7 +112,6 @@ struct acm {
 	struct mutex mutex;
 	struct usb_cdc_line_coding line;		/* bits, stop, parity */
 	struct work_struct work;			/* work queue entry for line discipline waking up */
-	wait_queue_head_t drain_wait;			/* close processing */
 	struct tasklet_struct urb_task;                 /* rx processing */
 	spinlock_t throttle_lock;			/* synchronize throtteling and read callback */
 	unsigned int ctrlin;				/* input control lines (DCD, DSR, RI, break, overruns) */
-- 
cgit v1.2.3-70-g09d2


From dab54c9f1e26f47a3313300bc1f4dc0eecb47375 Mon Sep 17 00:00:00 2001
From: Johan Hovold <jhovold@gmail.com>
Date: Tue, 22 Mar 2011 11:12:21 +0100
Subject: USB: cdc-acm: clean up rx_buflimit references

Clean up references to rx_buflimit.

Signed-off-by: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/class/cdc-acm.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

(limited to 'drivers/usb')

diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index e0f9febee7e6..2d8d6b838235 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -606,16 +606,15 @@ early_bail:
 
 static void acm_tty_unregister(struct acm *acm)
 {
-	int i, nr;
+	int i;
 
-	nr = acm->rx_buflimit;
 	tty_unregister_device(acm_tty_driver, acm->minor);
 	usb_put_intf(acm->control);
 	acm_table[acm->minor] = NULL;
 	usb_free_urb(acm->ctrlurb);
 	for (i = 0; i < ACM_NW; i++)
 		usb_free_urb(acm->wb[i].urb);
-	for (i = 0; i < nr; i++)
+	for (i = 0; i < acm->rx_buflimit; i++)
 		usb_free_urb(acm->ru[i].urb);
 	kfree(acm->country_codes);
 	kfree(acm);
@@ -623,7 +622,8 @@ static void acm_tty_unregister(struct acm *acm)
 
 static void acm_port_down(struct acm *acm)
 {
-	int i, nr = acm->rx_buflimit;
+	int i;
+
 	mutex_lock(&open_mutex);
 	if (acm->dev) {
 		usb_autopm_get_interface(acm->control);
@@ -632,7 +632,7 @@ static void acm_port_down(struct acm *acm)
 		for (i = 0; i < ACM_NW; i++)
 			usb_kill_urb(acm->wb[i].urb);
 		tasklet_disable(&acm->urb_task);
-		for (i = 0; i < nr; i++)
+		for (i = 0; i < acm->rx_buflimit; i++)
 			usb_kill_urb(acm->ru[i].urb);
 		tasklet_enable(&acm->urb_task);
 		acm->control->needs_remote_wakeup = 0;
@@ -882,9 +882,9 @@ static void acm_write_buffers_free(struct acm *acm)
 static void acm_read_buffers_free(struct acm *acm)
 {
 	struct usb_device *usb_dev = interface_to_usbdev(acm->control);
-	int i, n = acm->rx_buflimit;
+	int i;
 
-	for (i = 0; i < n; i++)
+	for (i = 0; i < acm->rx_buflimit; i++)
 		usb_free_coherent(usb_dev, acm->readsize,
 				  acm->rb[i].base, acm->rb[i].dma);
 }
-- 
cgit v1.2.3-70-g09d2


From 94d4c8919de3ae9e2e029ed121adfed43803bb5d Mon Sep 17 00:00:00 2001
From: Johan Hovold <jhovold@gmail.com>
Date: Tue, 22 Mar 2011 11:12:22 +0100
Subject: USB: cdc-acm: clean up open error handling

No need to kill ctrl urb on errors as this is done later during close.

Signed-off-by: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/class/cdc-acm.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

(limited to 'drivers/usb')

diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 2d8d6b838235..f239e3bd6423 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -568,7 +568,7 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
 
 	if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
 	    (acm->ctrl_caps & USB_CDC_CAP_LINE))
-		goto full_bailout;
+		goto bail_out;
 
 	usb_autopm_put_interface(acm->control);
 
@@ -592,8 +592,6 @@ out:
 	mutex_unlock(&open_mutex);
 	return rv;
 
-full_bailout:
-	usb_kill_urb(acm->ctrlurb);
 bail_out:
 	acm->port.count--;
 	mutex_unlock(&acm->mutex);
-- 
cgit v1.2.3-70-g09d2


From 74f5e1babde76149c2bb35ca5dbf4d0b9b38f161 Mon Sep 17 00:00:00 2001
From: Johan Hovold <jhovold@gmail.com>
Date: Tue, 22 Mar 2011 11:12:23 +0100
Subject: USB: cdc-acm: clean up read urb allocation

Allocate read urbs and read buffers in the same loop during probe.

Signed-off-by: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/class/cdc-acm.c | 29 +++++++++++++----------------
 1 file changed, 13 insertions(+), 16 deletions(-)

(limited to 'drivers/usb')

diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index f239e3bd6423..519c7b933508 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -1177,8 +1177,17 @@ made_compressed_probe:
 		goto alloc_fail5;
 	}
 	for (i = 0; i < num_rx_buf; i++) {
+		struct acm_rb *rb = &(acm->rb[i]);
 		struct acm_ru *rcv = &(acm->ru[i]);
 
+		rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL,
+								&rb->dma);
+		if (!rb->base) {
+			dev_err(&intf->dev, "out of memory "
+					"(read bufs usb_alloc_coherent)\n");
+			goto alloc_fail6;
+		}
+
 		rcv->urb = usb_alloc_urb(0, GFP_KERNEL);
 		if (rcv->urb == NULL) {
 			dev_err(&intf->dev,
@@ -1189,17 +1198,6 @@ made_compressed_probe:
 		rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 		rcv->instance = acm;
 	}
-	for (i = 0; i < num_rx_buf; i++) {
-		struct acm_rb *rb = &(acm->rb[i]);
-
-		rb->base = usb_alloc_coherent(acm->dev, readsize,
-				GFP_KERNEL, &rb->dma);
-		if (!rb->base) {
-			dev_err(&intf->dev,
-				"out of memory (read bufs usb_alloc_coherent)\n");
-			goto alloc_fail7;
-		}
-	}
 	for (i = 0; i < ACM_NW; i++) {
 		struct acm_wb *snd = &(acm->wb[i]);
 
@@ -1207,7 +1205,7 @@ made_compressed_probe:
 		if (snd->urb == NULL) {
 			dev_err(&intf->dev,
 				"out of memory (write urbs usb_alloc_urb)\n");
-			goto alloc_fail8;
+			goto alloc_fail7;
 		}
 
 		if (usb_endpoint_xfer_int(epwrite))
@@ -1226,7 +1224,7 @@ made_compressed_probe:
 
 	i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
 	if (i < 0)
-		goto alloc_fail8;
+		goto alloc_fail7;
 
 	if (cfd) { /* export the country data */
 		acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
@@ -1278,14 +1276,13 @@ skip_countries:
 	acm_table[minor] = acm;
 
 	return 0;
-alloc_fail8:
+alloc_fail7:
 	for (i = 0; i < ACM_NW; i++)
 		usb_free_urb(acm->wb[i].urb);
-alloc_fail7:
-	acm_read_buffers_free(acm);
 alloc_fail6:
 	for (i = 0; i < num_rx_buf; i++)
 		usb_free_urb(acm->ru[i].urb);
+	acm_read_buffers_free(acm);
 	usb_free_urb(acm->ctrlurb);
 alloc_fail5:
 	acm_write_buffers_free(acm);
-- 
cgit v1.2.3-70-g09d2


From 088c64f812847b3623b03d167ed329f90f3e38a4 Mon Sep 17 00:00:00 2001
From: Johan Hovold <jhovold@gmail.com>
Date: Fri, 25 Mar 2011 11:06:02 +0100
Subject: USB: cdc-acm: re-write read processing

Kill rx tasklet, which is no longer needed, and re-write read processing.

Signed-off-by: Johan Hovold <jhovold@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/class/cdc-acm.c | 295 +++++++++++++++++---------------------------
 drivers/usb/class/cdc-acm.h |  22 +---
 2 files changed, 120 insertions(+), 197 deletions(-)

(limited to 'drivers/usb')

diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 519c7b933508..58754b508a0f 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -7,6 +7,7 @@
  * Copyright (c) 2000 Vojtech Pavlik	<vojtech@suse.cz>
  * Copyright (c) 2004 Oliver Neukum	<oliver@neukum.name>
  * Copyright (c) 2005 David Kubicek	<dave@awk.cz>
+ * Copyright (c) 2011 Johan Hovold	<jhovold@gmail.com>
  *
  * USB Abstract Control Model driver for USB modems and ISDN adapters
  *
@@ -50,7 +51,7 @@
 #include "cdc-acm.h"
 
 
-#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek"
+#define DRIVER_AUTHOR "Armin Fuerst, Pavel Machek, Johannes Erdfelt, Vojtech Pavlik, David Kubicek, Johan Hovold"
 #define DRIVER_DESC "USB Abstract Control Model driver for USB modems and ISDN adapters"
 
 static struct usb_driver acm_driver;
@@ -323,166 +324,92 @@ exit:
 							__func__, retval);
 }
 
-/* data interface returns incoming bytes, or we got unthrottled */
-static void acm_read_bulk(struct urb *urb)
+static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags)
 {
-	struct acm_rb *buf;
-	struct acm_ru *rcv = urb->context;
-	struct acm *acm = rcv->instance;
-	int status = urb->status;
+	int res;
 
-	dev_vdbg(&acm->data->dev, "%s - status %d\n", __func__, status);
+	if (!test_and_clear_bit(index, &acm->read_urbs_free))
+		return 0;
 
-	if (!ACM_READY(acm)) {
-		dev_dbg(&acm->data->dev, "%s - acm not ready\n", __func__);
-		return;
+	dev_vdbg(&acm->data->dev, "%s - urb %d\n", __func__, index);
+
+	res = usb_submit_urb(acm->read_urbs[index], mem_flags);
+	if (res) {
+		if (res != -EPERM) {
+			dev_err(&acm->data->dev,
+					"%s - usb_submit_urb failed: %d\n",
+					__func__, res);
+		}
+		set_bit(index, &acm->read_urbs_free);
+		return res;
 	}
-	usb_mark_last_busy(acm->dev);
 
-	if (status)
-		dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n",
-							__func__, status);
+	return 0;
+}
 
-	buf = rcv->buffer;
-	buf->size = urb->actual_length;
+static int acm_submit_read_urbs(struct acm *acm, gfp_t mem_flags)
+{
+	int res;
+	int i;
 
-	if (likely(status == 0)) {
-		spin_lock(&acm->read_lock);
-		acm->processing++;
-		list_add_tail(&rcv->list, &acm->spare_read_urbs);
-		list_add_tail(&buf->list, &acm->filled_read_bufs);
-		spin_unlock(&acm->read_lock);
-	} else {
-		/* we drop the buffer due to an error */
-		spin_lock(&acm->read_lock);
-		list_add_tail(&rcv->list, &acm->spare_read_urbs);
-		list_add(&buf->list, &acm->spare_read_bufs);
-		spin_unlock(&acm->read_lock);
-		/* nevertheless the tasklet must be kicked unconditionally
-		so the queue cannot dry up */
+	for (i = 0; i < acm->rx_buflimit; ++i) {
+		res = acm_submit_read_urb(acm, i, mem_flags);
+		if (res)
+			return res;
 	}
-	if (likely(!acm->susp_count))
-		tasklet_schedule(&acm->urb_task);
+
+	return 0;
 }
 
-static void acm_rx_tasklet(unsigned long _acm)
+static void acm_process_read_urb(struct acm *acm, struct urb *urb)
 {
-	struct acm *acm = (void *)_acm;
-	struct acm_rb *buf;
 	struct tty_struct *tty;
-	struct acm_ru *rcv;
-	unsigned long flags;
-	unsigned char throttled;
-
-	dev_vdbg(&acm->data->dev, "%s\n", __func__);
 
-	if (!ACM_READY(acm)) {
-		dev_dbg(&acm->data->dev, "%s - acm not ready\n", __func__);
+	if (!urb->actual_length)
 		return;
-	}
-
-	spin_lock_irqsave(&acm->throttle_lock, flags);
-	throttled = acm->throttle;
-	spin_unlock_irqrestore(&acm->throttle_lock, flags);
-	if (throttled) {
-		dev_dbg(&acm->data->dev, "%s - throttled\n", __func__);
-		return;
-	}
 
 	tty = tty_port_tty_get(&acm->port);
+	if (!tty)
+		return;
 
-next_buffer:
-	spin_lock_irqsave(&acm->read_lock, flags);
-	if (list_empty(&acm->filled_read_bufs)) {
-		spin_unlock_irqrestore(&acm->read_lock, flags);
-		goto urbs;
-	}
-	buf = list_entry(acm->filled_read_bufs.next,
-			 struct acm_rb, list);
-	list_del(&buf->list);
-	spin_unlock_irqrestore(&acm->read_lock, flags);
-
-	dev_vdbg(&acm->data->dev, "%s - processing buf 0x%p, size = %d\n",
-						__func__, buf, buf->size);
-	if (tty) {
-		spin_lock_irqsave(&acm->throttle_lock, flags);
-		throttled = acm->throttle;
-		spin_unlock_irqrestore(&acm->throttle_lock, flags);
-		if (!throttled) {
-			tty_insert_flip_string(tty, buf->base, buf->size);
-			tty_flip_buffer_push(tty);
-		} else {
-			tty_kref_put(tty);
-			dev_dbg(&acm->data->dev, "%s - throttling noticed\n",
-								__func__);
-			spin_lock_irqsave(&acm->read_lock, flags);
-			list_add(&buf->list, &acm->filled_read_bufs);
-			spin_unlock_irqrestore(&acm->read_lock, flags);
-			return;
-		}
-	}
-
-	spin_lock_irqsave(&acm->read_lock, flags);
-	list_add(&buf->list, &acm->spare_read_bufs);
-	spin_unlock_irqrestore(&acm->read_lock, flags);
-	goto next_buffer;
+	tty_insert_flip_string(tty, urb->transfer_buffer, urb->actual_length);
+	tty_flip_buffer_push(tty);
 
-urbs:
 	tty_kref_put(tty);
+}
 
-	while (!list_empty(&acm->spare_read_bufs)) {
-		spin_lock_irqsave(&acm->read_lock, flags);
-		if (list_empty(&acm->spare_read_urbs)) {
-			acm->processing = 0;
-			spin_unlock_irqrestore(&acm->read_lock, flags);
-			return;
-		}
-		rcv = list_entry(acm->spare_read_urbs.next,
-				 struct acm_ru, list);
-		list_del(&rcv->list);
-		spin_unlock_irqrestore(&acm->read_lock, flags);
+static void acm_read_bulk_callback(struct urb *urb)
+{
+	struct acm_rb *rb = urb->context;
+	struct acm *acm = rb->instance;
+	unsigned long flags;
 
-		buf = list_entry(acm->spare_read_bufs.next,
-				 struct acm_rb, list);
-		list_del(&buf->list);
+	dev_vdbg(&acm->data->dev, "%s - urb %d, len %d\n", __func__,
+					rb->index, urb->actual_length);
+	set_bit(rb->index, &acm->read_urbs_free);
 
-		rcv->buffer = buf;
+	if (!acm->dev) {
+		dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__);
+		return;
+	}
+	usb_mark_last_busy(acm->dev);
 
-		if (acm->is_int_ep)
-			usb_fill_int_urb(rcv->urb, acm->dev,
-					 acm->rx_endpoint,
-					 buf->base,
-					 acm->readsize,
-					 acm_read_bulk, rcv, acm->bInterval);
-		else
-			usb_fill_bulk_urb(rcv->urb, acm->dev,
-					  acm->rx_endpoint,
-					  buf->base,
-					  acm->readsize,
-					  acm_read_bulk, rcv);
-		rcv->urb->transfer_dma = buf->dma;
-		rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
-
-		/* This shouldn't kill the driver as unsuccessful URBs are
-		   returned to the free-urbs-pool and resubmited ASAP */
-		spin_lock_irqsave(&acm->read_lock, flags);
-		if (acm->susp_count ||
-				usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
-			list_add(&buf->list, &acm->spare_read_bufs);
-			list_add(&rcv->list, &acm->spare_read_urbs);
-			acm->processing = 0;
-			spin_unlock_irqrestore(&acm->read_lock, flags);
-			return;
-		} else {
-			spin_unlock_irqrestore(&acm->read_lock, flags);
-			dev_vdbg(&acm->data->dev,
-				"%s - sending urb 0x%p, rcv 0x%p, buf 0x%p\n",
-				__func__, rcv->urb, rcv, buf);
-		}
+	if (urb->status) {
+		dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n",
+							__func__, urb->status);
+		return;
 	}
+	acm_process_read_urb(acm, urb);
+
+	/* throttle device if requested by tty */
 	spin_lock_irqsave(&acm->read_lock, flags);
-	acm->processing = 0;
-	spin_unlock_irqrestore(&acm->read_lock, flags);
+	acm->throttled = acm->throttle_req;
+	if (!acm->throttled && !acm->susp_count) {
+		spin_unlock_irqrestore(&acm->read_lock, flags);
+		acm_submit_read_urb(acm, rb->index, GFP_ATOMIC);
+	} else {
+		spin_unlock_irqrestore(&acm->read_lock, flags);
+	}
 }
 
 /* data interface wrote those outgoing bytes */
@@ -530,7 +457,6 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
 {
 	struct acm *acm;
 	int rv = -ENODEV;
-	int i;
 
 	mutex_lock(&open_mutex);
 
@@ -572,20 +498,11 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
 
 	usb_autopm_put_interface(acm->control);
 
-	INIT_LIST_HEAD(&acm->spare_read_urbs);
-	INIT_LIST_HEAD(&acm->spare_read_bufs);
-	INIT_LIST_HEAD(&acm->filled_read_bufs);
-
-	for (i = 0; i < acm->rx_buflimit; i++)
-		list_add(&(acm->ru[i].list), &acm->spare_read_urbs);
-	for (i = 0; i < acm->rx_buflimit; i++)
-		list_add(&(acm->rb[i].list), &acm->spare_read_bufs);
-
-	acm->throttle = 0;
+	if (acm_submit_read_urbs(acm, GFP_KERNEL))
+		goto bail_out;
 
 	set_bit(ASYNCB_INITIALIZED, &acm->port.flags);
 	rv = tty_port_block_til_ready(&acm->port, tty, filp);
-	tasklet_schedule(&acm->urb_task);
 
 	mutex_unlock(&acm->mutex);
 out:
@@ -613,7 +530,7 @@ static void acm_tty_unregister(struct acm *acm)
 	for (i = 0; i < ACM_NW; i++)
 		usb_free_urb(acm->wb[i].urb);
 	for (i = 0; i < acm->rx_buflimit; i++)
-		usb_free_urb(acm->ru[i].urb);
+		usb_free_urb(acm->read_urbs[i]);
 	kfree(acm->country_codes);
 	kfree(acm);
 }
@@ -629,10 +546,8 @@ static void acm_port_down(struct acm *acm)
 		usb_kill_urb(acm->ctrlurb);
 		for (i = 0; i < ACM_NW; i++)
 			usb_kill_urb(acm->wb[i].urb);
-		tasklet_disable(&acm->urb_task);
 		for (i = 0; i < acm->rx_buflimit; i++)
-			usb_kill_urb(acm->ru[i].urb);
-		tasklet_enable(&acm->urb_task);
+			usb_kill_urb(acm->read_urbs[i]);
 		acm->control->needs_remote_wakeup = 0;
 		usb_autopm_put_interface(acm->control);
 	}
@@ -731,22 +646,31 @@ static int acm_tty_chars_in_buffer(struct tty_struct *tty)
 static void acm_tty_throttle(struct tty_struct *tty)
 {
 	struct acm *acm = tty->driver_data;
+
 	if (!ACM_READY(acm))
 		return;
-	spin_lock_bh(&acm->throttle_lock);
-	acm->throttle = 1;
-	spin_unlock_bh(&acm->throttle_lock);
+
+	spin_lock_irq(&acm->read_lock);
+	acm->throttle_req = 1;
+	spin_unlock_irq(&acm->read_lock);
 }
 
 static void acm_tty_unthrottle(struct tty_struct *tty)
 {
 	struct acm *acm = tty->driver_data;
+	unsigned int was_throttled;
+
 	if (!ACM_READY(acm))
 		return;
-	spin_lock_bh(&acm->throttle_lock);
-	acm->throttle = 0;
-	spin_unlock_bh(&acm->throttle_lock);
-	tasklet_schedule(&acm->urb_task);
+
+	spin_lock_irq(&acm->read_lock);
+	was_throttled = acm->throttled;
+	acm->throttled = 0;
+	acm->throttle_req = 0;
+	spin_unlock_irq(&acm->read_lock);
+
+	if (was_throttled)
+		acm_submit_read_urbs(acm, GFP_KERNEL);
 }
 
 static int acm_tty_break_ctl(struct tty_struct *tty, int state)
@@ -884,7 +808,7 @@ static void acm_read_buffers_free(struct acm *acm)
 
 	for (i = 0; i < acm->rx_buflimit; i++)
 		usb_free_coherent(usb_dev, acm->readsize,
-				  acm->rb[i].base, acm->rb[i].dma);
+			  acm->read_buffers[i].base, acm->read_buffers[i].dma);
 }
 
 /* Little helper: write buffers allocate */
@@ -1145,10 +1069,7 @@ made_compressed_probe:
 	acm->ctrlsize = ctrlsize;
 	acm->readsize = readsize;
 	acm->rx_buflimit = num_rx_buf;
-	acm->urb_task.func = acm_rx_tasklet;
-	acm->urb_task.data = (unsigned long) acm;
 	INIT_WORK(&acm->work, acm_softint);
-	spin_lock_init(&acm->throttle_lock);
 	spin_lock_init(&acm->write_lock);
 	spin_lock_init(&acm->read_lock);
 	mutex_init(&acm->mutex);
@@ -1177,8 +1098,8 @@ made_compressed_probe:
 		goto alloc_fail5;
 	}
 	for (i = 0; i < num_rx_buf; i++) {
-		struct acm_rb *rb = &(acm->rb[i]);
-		struct acm_ru *rcv = &(acm->ru[i]);
+		struct acm_rb *rb = &(acm->read_buffers[i]);
+		struct urb *urb;
 
 		rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL,
 								&rb->dma);
@@ -1187,16 +1108,34 @@ made_compressed_probe:
 					"(read bufs usb_alloc_coherent)\n");
 			goto alloc_fail6;
 		}
+		rb->index = i;
+		rb->instance = acm;
 
-		rcv->urb = usb_alloc_urb(0, GFP_KERNEL);
-		if (rcv->urb == NULL) {
+		urb = usb_alloc_urb(0, GFP_KERNEL);
+		if (!urb) {
 			dev_err(&intf->dev,
 				"out of memory (read urbs usb_alloc_urb)\n");
 			goto alloc_fail6;
 		}
+		urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+		urb->transfer_dma = rb->dma;
+		if (acm->is_int_ep) {
+			usb_fill_int_urb(urb, acm->dev,
+					 acm->rx_endpoint,
+					 rb->base,
+					 acm->readsize,
+					 acm_read_bulk_callback, rb,
+					 acm->bInterval);
+		} else {
+			usb_fill_bulk_urb(urb, acm->dev,
+					  acm->rx_endpoint,
+					  rb->base,
+					  acm->readsize,
+					  acm_read_bulk_callback, rb);
+		}
 
-		rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
-		rcv->instance = acm;
+		acm->read_urbs[i] = urb;
+		__set_bit(i, &acm->read_urbs_free);
 	}
 	for (i = 0; i < ACM_NW; i++) {
 		struct acm_wb *snd = &(acm->wb[i]);
@@ -1281,7 +1220,7 @@ alloc_fail7:
 		usb_free_urb(acm->wb[i].urb);
 alloc_fail6:
 	for (i = 0; i < num_rx_buf; i++)
-		usb_free_urb(acm->ru[i].urb);
+		usb_free_urb(acm->read_urbs[i]);
 	acm_read_buffers_free(acm);
 	usb_free_urb(acm->ctrlurb);
 alloc_fail5:
@@ -1300,15 +1239,11 @@ static void stop_data_traffic(struct acm *acm)
 
 	dev_dbg(&acm->control->dev, "%s\n", __func__);
 
-	tasklet_disable(&acm->urb_task);
-
 	usb_kill_urb(acm->ctrlurb);
 	for (i = 0; i < ACM_NW; i++)
 		usb_kill_urb(acm->wb[i].urb);
 	for (i = 0; i < acm->rx_buflimit; i++)
-		usb_kill_urb(acm->ru[i].urb);
-
-	tasklet_enable(&acm->urb_task);
+		usb_kill_urb(acm->read_urbs[i]);
 
 	cancel_work_sync(&acm->work);
 }
@@ -1369,11 +1304,9 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)
 	if (message.event & PM_EVENT_AUTO) {
 		int b;
 
-		spin_lock_irq(&acm->read_lock);
-		spin_lock(&acm->write_lock);
-		b = acm->processing + acm->transmitting;
-		spin_unlock(&acm->write_lock);
-		spin_unlock_irq(&acm->read_lock);
+		spin_lock_irq(&acm->write_lock);
+		b = acm->transmitting;
+		spin_unlock_irq(&acm->write_lock);
 		if (b)
 			return -EBUSY;
 	}
@@ -1435,7 +1368,7 @@ static int acm_resume(struct usb_interface *intf)
 		if (rv < 0)
 			goto err_out;
 
-		tasklet_schedule(&acm->urb_task);
+		rv = acm_submit_read_urbs(acm, GFP_NOIO);
 	}
 
 err_out:
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index 7282d1f4912b..7b5c0bd07f80 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -72,16 +72,10 @@ struct acm_wb {
 };
 
 struct acm_rb {
-	struct list_head	list;
 	int			size;
 	unsigned char		*base;
 	dma_addr_t		dma;
-};
-
-struct acm_ru {
-	struct list_head	list;
-	struct acm_rb		*buffer;
-	struct urb		*urb;
+	int			index;
 	struct acm		*instance;
 };
 
@@ -97,34 +91,30 @@ struct acm {
 	unsigned int country_code_size;			/* size of this buffer */
 	unsigned int country_rel_date;			/* release date of version */
 	struct acm_wb wb[ACM_NW];
-	struct acm_ru ru[ACM_NR];
-	struct acm_rb rb[ACM_NR];
+	unsigned long read_urbs_free;
+	struct urb *read_urbs[ACM_NR];
+	struct acm_rb read_buffers[ACM_NR];
 	int rx_buflimit;
 	int rx_endpoint;
 	spinlock_t read_lock;
-	struct list_head spare_read_urbs;
-	struct list_head spare_read_bufs;
-	struct list_head filled_read_bufs;
 	int write_used;					/* number of non-empty write buffers */
-	int processing;
 	int transmitting;
 	spinlock_t write_lock;
 	struct mutex mutex;
 	struct usb_cdc_line_coding line;		/* bits, stop, parity */
 	struct work_struct work;			/* work queue entry for line discipline waking up */
-	struct tasklet_struct urb_task;                 /* rx processing */
-	spinlock_t throttle_lock;			/* synchronize throtteling and read callback */
 	unsigned int ctrlin;				/* input control lines (DCD, DSR, RI, break, overruns) */
 	unsigned int ctrlout;				/* output control lines (DTR, RTS) */
 	unsigned int writesize;				/* max packet size for the output bulk endpoint */
 	unsigned int readsize,ctrlsize;			/* buffer sizes for freeing */
 	unsigned int minor;				/* acm minor number */
-	unsigned char throttle;				/* throttled by tty layer */
 	unsigned char clocal;				/* termios CLOCAL */
 	unsigned int ctrl_caps;				/* control capabilities from the class specific header */
 	unsigned int susp_count;			/* number of suspended interfaces */
 	unsigned int combined_interfaces:1;		/* control and data collapsed */
 	unsigned int is_int_ep:1;			/* interrupt endpoints contrary to spec used */
+	unsigned int throttled:1;			/* actually throttled */
+	unsigned int throttle_req:1;			/* throttle requested */
 	u8 bInterval;
 	struct acm_wb *delayed_wb;			/* write queued for a device about to be woken */
 };
-- 
cgit v1.2.3-70-g09d2


From 806e8f8fcc27e1753947bd9f059ba2316cf8f92a Mon Sep 17 00:00:00 2001
From: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>
Date: Thu, 24 Mar 2011 12:20:13 +0100
Subject: usb: usb_storage: do not align length of request for CBW to maxp size

Mass-storage and file-storage gadgets align the length to maximum-packet-size
when preparing the request to receive CBW. This is unnecessary and prevents the
controller driver from knowing that a short-packet is expected.

It is incorrect to set short_not_ok when preparing the request to receive CBW.
CBW will be a short-packet so short_not_ok must not be set.

This makes bh->bulk_out_intended_length unnecessary so it is also removed.

Signed-off-by: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/gadget/f_mass_storage.c | 24 ++++--------------------
 drivers/usb/gadget/file_storage.c   | 28 ++++++----------------------
 drivers/usb/gadget/storage_common.c |  7 -------
 3 files changed, 10 insertions(+), 49 deletions(-)

(limited to 'drivers/usb')

diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c
index 6d8e533949eb..125587ac5d0b 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -474,20 +474,6 @@ static int exception_in_progress(struct fsg_common *common)
 	return common->state > FSG_STATE_IDLE;
 }
 
-/* Make bulk-out requests be divisible by the maxpacket size */
-static void set_bulk_out_req_length(struct fsg_common *common,
-				    struct fsg_buffhd *bh, unsigned int length)
-{
-	unsigned int	rem;
-
-	bh->bulk_out_intended_length = length;
-	rem = length % common->bulk_out_maxpacket;
-	if (rem > 0)
-		length += common->bulk_out_maxpacket - rem;
-	bh->outreq->length = length;
-}
-
-
 /*-------------------------------------------------------------------------*/
 
 static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep)
@@ -586,9 +572,9 @@ static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req)
 	struct fsg_buffhd	*bh = req->context;
 
 	dump_msg(common, "bulk-out", req->buf, req->actual);
-	if (req->status || req->actual != bh->bulk_out_intended_length)
+	if (req->status || req->actual != req->length)
 		DBG(common, "%s --> %d, %u/%u\n", __func__,
-		    req->status, req->actual, bh->bulk_out_intended_length);
+		    req->status, req->actual, req->length);
 	if (req->status == -ECONNRESET)		/* Request was cancelled */
 		usb_ep_fifo_flush(ep);
 
@@ -975,7 +961,6 @@ static int do_write(struct fsg_common *common)
 			 * the bulk-out maxpacket size
 			 */
 			bh->outreq->length = amount;
-			bh->bulk_out_intended_length = amount;
 			bh->outreq->short_not_ok = 1;
 			if (!start_out_transfer(common, bh))
 				/* Dunno what to do if common->fsg is NULL */
@@ -1652,7 +1637,6 @@ static int throw_away_data(struct fsg_common *common)
 			 * the bulk-out maxpacket size.
 			 */
 			bh->outreq->length = amount;
-			bh->bulk_out_intended_length = amount;
 			bh->outreq->short_not_ok = 1;
 			if (!start_out_transfer(common, bh))
 				/* Dunno what to do if common->fsg is NULL */
@@ -2322,8 +2306,8 @@ static int get_next_command(struct fsg_common *common)
 	}
 
 	/* Queue a request to read a Bulk-only CBW */
-	set_bulk_out_req_length(common, bh, USB_BULK_CB_WRAP_LEN);
-	bh->outreq->short_not_ok = 1;
+	bh->outreq->length = USB_BULK_CB_WRAP_LEN;
+	bh->outreq->short_not_ok = 0;
 	if (!start_out_transfer(common, bh))
 		/* Don't know what to do if common->fsg is NULL */
 		return -EIO;
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
index a6eacb59571b..d04e0e6b019d 100644
--- a/drivers/usb/gadget/file_storage.c
+++ b/drivers/usb/gadget/file_storage.c
@@ -497,19 +497,6 @@ static int exception_in_progress(struct fsg_dev *fsg)
 	return (fsg->state > FSG_STATE_IDLE);
 }
 
-/* Make bulk-out requests be divisible by the maxpacket size */
-static void set_bulk_out_req_length(struct fsg_dev *fsg,
-		struct fsg_buffhd *bh, unsigned int length)
-{
-	unsigned int	rem;
-
-	bh->bulk_out_intended_length = length;
-	rem = length % fsg->bulk_out_maxpacket;
-	if (rem > 0)
-		length += fsg->bulk_out_maxpacket - rem;
-	bh->outreq->length = length;
-}
-
 static struct fsg_dev			*the_fsg;
 static struct usb_gadget_driver		fsg_driver;
 
@@ -730,10 +717,9 @@ static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req)
 	struct fsg_buffhd	*bh = req->context;
 
 	dump_msg(fsg, "bulk-out", req->buf, req->actual);
-	if (req->status || req->actual != bh->bulk_out_intended_length)
+	if (req->status || req->actual != req->length)
 		DBG(fsg, "%s --> %d, %u/%u\n", __func__,
-				req->status, req->actual,
-				bh->bulk_out_intended_length);
+				req->status, req->actual, req->length);
 	if (req->status == -ECONNRESET)		// Request was cancelled
 		usb_ep_fifo_flush(ep);
 
@@ -1349,8 +1335,7 @@ static int do_write(struct fsg_dev *fsg)
 
 			/* amount is always divisible by 512, hence by
 			 * the bulk-out maxpacket size */
-			bh->outreq->length = bh->bulk_out_intended_length =
-					amount;
+			bh->outreq->length = amount;
 			bh->outreq->short_not_ok = 1;
 			start_transfer(fsg, fsg->bulk_out, bh->outreq,
 					&bh->outreq_busy, &bh->state);
@@ -2010,8 +1995,7 @@ static int throw_away_data(struct fsg_dev *fsg)
 
 			/* amount is always divisible by 512, hence by
 			 * the bulk-out maxpacket size */
-			bh->outreq->length = bh->bulk_out_intended_length =
-					amount;
+			bh->outreq->length = amount;
 			bh->outreq->short_not_ok = 1;
 			start_transfer(fsg, fsg->bulk_out, bh->outreq,
 					&bh->outreq_busy, &bh->state);
@@ -2688,8 +2672,8 @@ static int get_next_command(struct fsg_dev *fsg)
 		}
 
 		/* Queue a request to read a Bulk-only CBW */
-		set_bulk_out_req_length(fsg, bh, USB_BULK_CB_WRAP_LEN);
-		bh->outreq->short_not_ok = 1;
+		bh->outreq->length = USB_BULK_CB_WRAP_LEN;
+		bh->outreq->short_not_ok = 0;
 		start_transfer(fsg, fsg->bulk_out, bh->outreq,
 				&bh->outreq_busy, &bh->state);
 
diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c
index b015561fd602..3179b8bb6ced 100644
--- a/drivers/usb/gadget/storage_common.c
+++ b/drivers/usb/gadget/storage_common.c
@@ -286,13 +286,6 @@ struct fsg_buffhd {
 	enum fsg_buffer_state		state;
 	struct fsg_buffhd		*next;
 
-	/*
-	 * The NetChip 2280 is faster, and handles some protocol faults
-	 * better, if we don't submit any short bulk-out read requests.
-	 * So we will record the intended request length here.
-	 */
-	unsigned int			bulk_out_intended_length;
-
 	struct usb_request		*inreq;
 	int				inreq_busy;
 	struct usb_request		*outreq;
-- 
cgit v1.2.3-70-g09d2


From edf847decc4159128041d2a81f8d93e6eb567ecb Mon Sep 17 00:00:00 2001
From: Libor Pechacek <lpechacek@suse.cz>
Date: Fri, 25 Mar 2011 09:36:49 +0100
Subject: USB: remove dead code from usb_deregister_dev()

The `name' variable is unused in usb_deregister_dev() since commit d6e5bcf
(devfs: Remove the mode field from usb_class_driver as it's no longer needed).

Signed-off-by: Libor Pechacek <lpechacek@suse.cz>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/core/file.c | 8 --------
 1 file changed, 8 deletions(-)

(limited to 'drivers/usb')

diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c
index cf6a5423de09..99458c843d60 100644
--- a/drivers/usb/core/file.c
+++ b/drivers/usb/core/file.c
@@ -236,13 +236,6 @@ EXPORT_SYMBOL_GPL(usb_register_dev);
 void usb_deregister_dev(struct usb_interface *intf,
 			struct usb_class_driver *class_driver)
 {
-	int minor_base = class_driver->minor_base;
-	char name[20];
-
-#ifdef CONFIG_USB_DYNAMIC_MINORS
-	minor_base = 0;
-#endif
-
 	if (intf->minor == -1)
 		return;
 
@@ -252,7 +245,6 @@ void usb_deregister_dev(struct usb_interface *intf,
 	usb_minors[intf->minor] = NULL;
 	up_write(&minor_rwsem);
 
-	snprintf(name, sizeof(name), class_driver->name, intf->minor - minor_base);
 	device_destroy(usb_class->class, MKDEV(USB_MAJOR, intf->minor));
 	intf->usb_dev = NULL;
 	intf->minor = -1;
-- 
cgit v1.2.3-70-g09d2


From ee81b3e086c907a3347b15ef219a24fc8bf900f6 Mon Sep 17 00:00:00 2001
From: Alan Stern <stern@rowland.harvard.edu>
Date: Fri, 25 Mar 2011 11:46:27 -0400
Subject: USB: g_file_storage: don't send padding when stall=n

This patch (as1455) removes the extra padding sent by g_file_storage
and g_mass_storage when the gadget wants to send less data than
requested by the host and isn't allowed to halt the bulk-IN endpoint.

Although the Bulk-Only Transport specification requires the padding to
be present, it isn't truly needed since the transfer will be terminated
by a short packet anyway.  Furthermore, many existing devices don't
bother to send any padding.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Acked-By: Michal Nazarewicz <mina86@mina86.com>
CC: Roger Quadros <roger.quadros@nokia.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/gadget/f_mass_storage.c | 54 ++++++++-----------------------------
 drivers/usb/gadget/file_storage.c   | 53 +++++++++---------------------------
 2 files changed, 23 insertions(+), 84 deletions(-)

(limited to 'drivers/usb')

diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c
index 125587ac5d0b..98d6b39061d2 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -1569,37 +1569,6 @@ static int wedge_bulk_in_endpoint(struct fsg_dev *fsg)
 	return rc;
 }
 
-static int pad_with_zeros(struct fsg_dev *fsg)
-{
-	struct fsg_buffhd	*bh = fsg->common->next_buffhd_to_fill;
-	u32			nkeep = bh->inreq->length;
-	u32			nsend;
-	int			rc;
-
-	bh->state = BUF_STATE_EMPTY;		/* For the first iteration */
-	fsg->common->usb_amount_left = nkeep + fsg->common->residue;
-	while (fsg->common->usb_amount_left > 0) {
-
-		/* Wait for the next buffer to be free */
-		while (bh->state != BUF_STATE_EMPTY) {
-			rc = sleep_thread(fsg->common);
-			if (rc)
-				return rc;
-		}
-
-		nsend = min(fsg->common->usb_amount_left, FSG_BUFLEN);
-		memset(bh->buf + nkeep, 0, nsend - nkeep);
-		bh->inreq->length = nsend;
-		bh->inreq->zero = 0;
-		start_transfer(fsg, fsg->bulk_in, bh->inreq,
-			       &bh->inreq_busy, &bh->state);
-		bh = fsg->common->next_buffhd_to_fill = bh->next;
-		fsg->common->usb_amount_left -= nsend;
-		nkeep = 0;
-	}
-	return 0;
-}
-
 static int throw_away_data(struct fsg_common *common)
 {
 	struct fsg_buffhd	*bh;
@@ -1686,6 +1655,10 @@ static int finish_reply(struct fsg_common *common)
 		if (common->data_size == 0) {
 			/* Nothing to send */
 
+		/* Don't know what to do if common->fsg is NULL */
+		} else if (!fsg_is_set(common)) {
+			rc = -EIO;
+
 		/* If there's no residue, simply send the last buffer */
 		} else if (common->residue == 0) {
 			bh->inreq->zero = 0;
@@ -1694,24 +1667,19 @@ static int finish_reply(struct fsg_common *common)
 			common->next_buffhd_to_fill = bh->next;
 
 		/*
-		 * For Bulk-only, if we're allowed to stall then send the
-		 * short packet and halt the bulk-in endpoint.  If we can't
-		 * stall, pad out the remaining data with 0's.
+		 * For Bulk-only, mark the end of the data with a short
+		 * packet.  If we are allowed to stall, halt the bulk-in
+		 * endpoint.  (Note: This violates the Bulk-Only Transport
+		 * specification, which requires us to pad the data if we
+		 * don't halt the endpoint.  Presumably nobody will mind.)
 		 */
-		} else if (common->can_stall) {
+		} else {
 			bh->inreq->zero = 1;
 			if (!start_in_transfer(common, bh))
-				/* Don't know what to do if
-				 * common->fsg is NULL */
 				rc = -EIO;
 			common->next_buffhd_to_fill = bh->next;
-			if (common->fsg)
+			if (common->can_stall)
 				rc = halt_bulk_in_endpoint(common->fsg);
-		} else if (fsg_is_set(common)) {
-			rc = pad_with_zeros(common->fsg);
-		} else {
-			/* Don't know what to do if common->fsg is NULL */
-			rc = -EIO;
 		}
 		break;
 
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
index d04e0e6b019d..aebfb81f3ba4 100644
--- a/drivers/usb/gadget/file_storage.c
+++ b/drivers/usb/gadget/file_storage.c
@@ -1932,37 +1932,6 @@ static int wedge_bulk_in_endpoint(struct fsg_dev *fsg)
 	return rc;
 }
 
-static int pad_with_zeros(struct fsg_dev *fsg)
-{
-	struct fsg_buffhd	*bh = fsg->next_buffhd_to_fill;
-	u32			nkeep = bh->inreq->length;
-	u32			nsend;
-	int			rc;
-
-	bh->state = BUF_STATE_EMPTY;		// For the first iteration
-	fsg->usb_amount_left = nkeep + fsg->residue;
-	while (fsg->usb_amount_left > 0) {
-
-		/* Wait for the next buffer to be free */
-		while (bh->state != BUF_STATE_EMPTY) {
-			rc = sleep_thread(fsg);
-			if (rc)
-				return rc;
-		}
-
-		nsend = min(fsg->usb_amount_left, (u32) mod_data.buflen);
-		memset(bh->buf + nkeep, 0, nsend - nkeep);
-		bh->inreq->length = nsend;
-		bh->inreq->zero = 0;
-		start_transfer(fsg, fsg->bulk_in, bh->inreq,
-				&bh->inreq_busy, &bh->state);
-		bh = fsg->next_buffhd_to_fill = bh->next;
-		fsg->usb_amount_left -= nsend;
-		nkeep = 0;
-	}
-	return 0;
-}
-
 static int throw_away_data(struct fsg_dev *fsg)
 {
 	struct fsg_buffhd	*bh;
@@ -2066,18 +2035,20 @@ static int finish_reply(struct fsg_dev *fsg)
 			}
 		}
 
-		/* For Bulk-only, if we're allowed to stall then send the
-		 * short packet and halt the bulk-in endpoint.  If we can't
-		 * stall, pad out the remaining data with 0's. */
+		/*
+		 * For Bulk-only, mark the end of the data with a short
+		 * packet.  If we are allowed to stall, halt the bulk-in
+		 * endpoint.  (Note: This violates the Bulk-Only Transport
+		 * specification, which requires us to pad the data if we
+		 * don't halt the endpoint.  Presumably nobody will mind.)
+		 */
 		else {
-			if (mod_data.can_stall) {
-				bh->inreq->zero = 1;
-				start_transfer(fsg, fsg->bulk_in, bh->inreq,
-						&bh->inreq_busy, &bh->state);
-				fsg->next_buffhd_to_fill = bh->next;
+			bh->inreq->zero = 1;
+			start_transfer(fsg, fsg->bulk_in, bh->inreq,
+					&bh->inreq_busy, &bh->state);
+			fsg->next_buffhd_to_fill = bh->next;
+			if (mod_data.can_stall)
 				rc = halt_bulk_in_endpoint(fsg);
-			} else
-				rc = pad_with_zeros(fsg);
 		}
 		break;
 
-- 
cgit v1.2.3-70-g09d2


From ad7c56f07e24c758d78e797ceeb9cf049dec66aa Mon Sep 17 00:00:00 2001
From: Mike Frysinger <vapier@gentoo.org>
Date: Tue, 22 Mar 2011 15:35:39 -0400
Subject: USB: sl811: add Kconfig option for ISOCHRONOUS mode

Some bluetooth dongles want ISO mode, and the limited support that the
sl811 offers today is sufficient.  So add a Kconfig option for people
to optionally get access to the partial functionality.

Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/host/Kconfig     | 10 ++++++++++
 drivers/usb/host/sl811-hcd.c |  8 +-------
 2 files changed, 11 insertions(+), 7 deletions(-)

(limited to 'drivers/usb')

diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index e0e0787b724b..5f518ded1955 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -444,6 +444,16 @@ config USB_SL811_HCD
 	  To compile this driver as a module, choose M here: the
 	  module will be called sl811-hcd.
 
+config USB_SL811_HCD_ISO
+	bool "partial ISO support"
+	depends on USB_SL811_HCD
+	help
+	  The driver doesn't support iso_frame_desc (yet), but for some simple
+	  devices that just queue one ISO frame per URB, then ISO transfers
+	  "should" work using the normal urb status fields.
+
+	  If unsure, say N.
+
 config USB_SL811_CS
 	tristate "CF/PCMCIA support for SL811HS HCD"
 	depends on USB_SL811_HCD && PCMCIA
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
index 18b7099a8125..5adcba016fcf 100644
--- a/drivers/usb/host/sl811-hcd.c
+++ b/drivers/usb/host/sl811-hcd.c
@@ -71,12 +71,6 @@ MODULE_ALIAS("platform:sl811-hcd");
 /* for now, use only one transfer register bank */
 #undef	USE_B
 
-/* this doesn't understand urb->iso_frame_desc[], but if you had a driver
- * that just queued one ISO frame per URB then iso transfers "should" work
- * using the normal urb status fields.
- */
-#define	DISABLE_ISO
-
 // #define	QUIRK2
 #define	QUIRK3
 
@@ -807,7 +801,7 @@ static int sl811h_urb_enqueue(
 	int			retval;
 	struct usb_host_endpoint	*hep = urb->ep;
 
-#ifdef	DISABLE_ISO
+#ifndef CONFIG_USB_SL811_HCD_ISO
 	if (type == PIPE_ISOCHRONOUS)
 		return -ENOSPC;
 #endif
-- 
cgit v1.2.3-70-g09d2


From 4661ffc91befc8c5ee080720120da1d53851060a Mon Sep 17 00:00:00 2001
From: Felipe Balbi <balbi@ti.com>
Date: Thu, 7 Apr 2011 10:59:34 +0300
Subject: usb: don't enter usb subdirectories directly

Instead, make we enter usb/ directory on all
needed cases and enter the subdirectories from
drivers/usb/Makefile.

Signed-off-by: Felipe Balbi <balbi@ti.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/Makefile     | 6 ++----
 drivers/usb/Makefile | 5 +++++
 2 files changed, 7 insertions(+), 4 deletions(-)

(limited to 'drivers/usb')

diff --git a/drivers/Makefile b/drivers/Makefile
index ad67b7d4c271..0cf73a90fca8 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -64,12 +64,10 @@ obj-$(CONFIG_ATA_OVER_ETH)	+= block/aoe/
 obj-$(CONFIG_PARIDE) 		+= block/paride/
 obj-$(CONFIG_TC)		+= tc/
 obj-$(CONFIG_UWB)		+= uwb/
-obj-$(CONFIG_USB_OTG_UTILS)	+= usb/otg/
+obj-$(CONFIG_USB_OTG_UTILS)	+= usb/
 obj-$(CONFIG_USB)		+= usb/
-obj-$(CONFIG_USB_MUSB_HDRC)	+= usb/musb/
-obj-$(CONFIG_USB_RENESAS_USBHS)	+= usb/renesas_usbhs/
 obj-$(CONFIG_PCI)		+= usb/
-obj-$(CONFIG_USB_GADGET)	+= usb/gadget/
+obj-$(CONFIG_USB_GADGET)	+= usb/
 obj-$(CONFIG_SERIO)		+= input/serio/
 obj-$(CONFIG_GAMEPORT)		+= input/gameport/
 obj-$(CONFIG_INPUT)		+= input/
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 239f050efa35..9bc8aeb3c96f 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -45,3 +45,8 @@ obj-$(CONFIG_EARLY_PRINTK_DBGP)	+= early/
 
 obj-$(CONFIG_USB_ATM)		+= atm/
 obj-$(CONFIG_USB_SPEEDTOUCH)	+= atm/
+
+obj-$(CONFIG_USB_MUSB_HDRC)	+= musb/
+obj-$(CONFIG_USB_RENESAS_USBHS)	+= renesas_usbhs/
+obj-$(CONFIG_USB_OTG_UTILS)	+= otg/
+obj-$(CONFIG_USB_GADGET)	+= gadget/
-- 
cgit v1.2.3-70-g09d2


From 7d670a2ed770a3405a7edb1159e3fa9b3f43fe46 Mon Sep 17 00:00:00 2001
From: Alan Stern <stern@rowland.harvard.edu>
Date: Tue, 5 Apr 2011 13:36:04 -0400
Subject: USB: UHCI: remove uses of hcd->state

This patch (as1456) removes all uses of hcd->state from the uhci-hcd
driver, as part of the overall strategy to eliminate hcd->state
completely.  Now when a controller dies we call usb_hc_died()
directly, instead of relying on the core interrupt handler to see that
hcd->state has changed to HC_STATE_HALT and make the call for us.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/host/uhci-hcd.c | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

(limited to 'drivers/usb')

diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index 4f65b14e5e08..73db5569f57b 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -139,7 +139,6 @@ static void finish_reset(struct uhci_hcd *uhci)
 	uhci->port_c_suspend = uhci->resuming_ports = 0;
 	uhci->rh_state = UHCI_RH_RESET;
 	uhci->is_stopped = UHCI_IS_STOPPED;
-	uhci_to_hcd(uhci)->state = HC_STATE_HALT;
 	clear_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags);
 
 	uhci->dead = 0;		/* Full reset resurrects the controller */
@@ -188,10 +187,6 @@ static void configure_hc(struct uhci_hcd *uhci)
 	outw(uhci->frame_number & UHCI_MAX_SOF_NUMBER,
 			uhci->io_addr + USBFRNUM);
 
-	/* Mark controller as not halted before we enable interrupts */
-	uhci_to_hcd(uhci)->state = HC_STATE_SUSPENDED;
-	mb();
-
 	/* Enable PIRQ */
 	pci_write_config_word(pdev, USBLEGSUP, USBLEGSUP_DEFAULT);
 
@@ -360,7 +355,6 @@ __acquires(uhci->lock)
 
 static void start_rh(struct uhci_hcd *uhci)
 {
-	uhci_to_hcd(uhci)->state = HC_STATE_RUNNING;
 	uhci->is_stopped = 0;
 
 	/* Mark it configured and running with a 64-byte max packet.
@@ -449,6 +443,7 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd)
 					lprintk(errbuf);
 				}
 				uhci_hc_died(uhci);
+				usb_hc_died(hcd);
 
 				/* Force a callback in case there are
 				 * pending unlinks */
-- 
cgit v1.2.3-70-g09d2


From 99083f16f04e050eab0059167b4980cd67e7aa5a Mon Sep 17 00:00:00 2001
From: Alan Stern <stern@rowland.harvard.edu>
Date: Tue, 5 Apr 2011 13:35:53 -0400
Subject: USB: UHCI: don't try to revive a dead controller

This patch (as1457) abandons the curious strategy of declaring a
controller dead following hibernation merely in order to reset and
then revive it.  The core no longer allow dead controllers to spring
back to life when the system resumes, so there's no reason to declare
a working controller temporarily dead.  Instead we do an explicit
reset.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/host/uhci-hcd.c | 19 +++++++++----------
 1 file changed, 9 insertions(+), 10 deletions(-)

(limited to 'drivers/usb')

diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index 73db5569f57b..83344d688ff0 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -140,8 +140,6 @@ static void finish_reset(struct uhci_hcd *uhci)
 	uhci->rh_state = UHCI_RH_RESET;
 	uhci->is_stopped = UHCI_IS_STOPPED;
 	clear_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags);
-
-	uhci->dead = 0;		/* Full reset resurrects the controller */
 }
 
 /*
@@ -837,16 +835,17 @@ static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
 	spin_lock_irq(&uhci->lock);
 
 	/* Make sure resume from hibernation re-enumerates everything */
-	if (hibernated)
-		uhci_hc_died(uhci);
+	if (hibernated) {
+		uhci_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr);
+		finish_reset(uhci);
+	}
 
-	/* The firmware or a boot kernel may have changed the controller
-	 * settings during a system wakeup.  Check it and reconfigure
-	 * to avoid problems.
+	/* The firmware may have changed the controller settings during
+	 * a system wakeup.  Check it and reconfigure to avoid problems.
 	 */
-	check_and_reset_hc(uhci);
-
-	/* If the controller was dead before, it's back alive now */
+	else {
+		check_and_reset_hc(uhci);
+	}
 	configure_hc(uhci);
 
 	/* Tell the core if the controller had to be reset */
-- 
cgit v1.2.3-70-g09d2


From 3482f00d018fb5e476beb867272c1d82f4f5c7d6 Mon Sep 17 00:00:00 2001
From: Michal Marek <mmarek@suse.cz>
Date: Tue, 5 Apr 2011 16:59:12 +0200
Subject: usb: ftdi-elan: Drop __TIME__ usage

The kernel already prints its build timestamp during boot, no need to
repeat it in random drivers and produce different object files each
time.

Cc: Tony Olech <tony.olech@elandigitalsystems.com>
Cc: linux-usb@vger.kernel.org
Signed-off-by: Michal Marek <mmarek@suse.cz>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/misc/ftdi-elan.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

(limited to 'drivers/usb')

diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c
index 7839c98fa742..b16bd3ce3915 100644
--- a/drivers/usb/misc/ftdi-elan.c
+++ b/drivers/usb/misc/ftdi-elan.c
@@ -2889,8 +2889,7 @@ static struct usb_driver ftdi_elan_driver = {
 static int __init ftdi_elan_init(void)
 {
         int result;
-        printk(KERN_INFO "driver %s built at %s on %s\n", ftdi_elan_driver.name,
-	       __TIME__, __DATE__);
+        printk(KERN_INFO "driver %s\n", ftdi_elan_driver.name);
         mutex_init(&ftdi_module_lock);
         INIT_LIST_HEAD(&ftdi_static_list);
         status_queue = create_singlethread_workqueue("ftdi-status-control");
-- 
cgit v1.2.3-70-g09d2


From 654d121ad8c84e3442efee20b2a0703edb18c212 Mon Sep 17 00:00:00 2001
From: Michal Marek <mmarek@suse.cz>
Date: Tue, 5 Apr 2011 16:59:11 +0200
Subject: usb: u132-hcd: Drop __TIME__ usage

The kernel already prints its build timestamp during boot, no need to
repeat it in random drivers and produce different object files each
time.

Cc: Tony Olech <tony.olech@elandigitalsystems.com>
Cc: linux-usb@vger.kernel.org
Signed-off-by: Michal Marek <mmarek@suse.cz>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/host/u132-hcd.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

(limited to 'drivers/usb')

diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c
index b4785934e091..533d12cca371 100644
--- a/drivers/usb/host/u132-hcd.c
+++ b/drivers/usb/host/u132-hcd.c
@@ -3230,8 +3230,7 @@ static int __init u132_hcd_init(void)
 	mutex_init(&u132_module_lock);
 	if (usb_disabled())
 		return -ENODEV;
-	printk(KERN_INFO "driver %s built at %s on %s\n", hcd_name, __TIME__,
-		__DATE__);
+	printk(KERN_INFO "driver %s\n", hcd_name);
 	workqueue = create_singlethread_workqueue("u132");
 	retval = platform_driver_register(&u132_platform_driver);
 	return retval;
-- 
cgit v1.2.3-70-g09d2


From 73ee4da994e7b97bd8241e39099cf3dd94675d79 Mon Sep 17 00:00:00 2001
From: Roger Quadros <roger.quadros@nokia.com>
Date: Tue, 5 Apr 2011 18:36:38 +0300
Subject: usb: gadget: f_mass_storage: Fix Bulk-only RESET handling

The ep0 request tag was not recorded thus resulting in phase
problems while sending status/response in handle_execption() handler.
This was resulting in MSC compliance test failures with USBCV tool.

With this patch, the Bulk-Only Mass storage RESET request is
handled correctly and the MSC compliance tests pass.

Signed-off-by: Roger Quadros <roger.quadros@nokia.com>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/gadget/f_mass_storage.c | 5 +++++
 1 file changed, 5 insertions(+)

(limited to 'drivers/usb')

diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c
index 98d6b39061d2..e9de33d1c9a0 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -599,6 +599,11 @@ static int fsg_setup(struct usb_function *f,
 	if (!fsg_is_set(fsg->common))
 		return -EOPNOTSUPP;
 
+	++fsg->common->ep0_req_tag;	/* Record arrival of a new request */
+	req->context = NULL;
+	req->length = 0;
+	dump_msg(fsg, "ep0-setup", (u8 *) ctrl, sizeof(*ctrl));
+
 	switch (ctrl->bRequest) {
 
 	case USB_BULK_RESET_REQUEST:
-- 
cgit v1.2.3-70-g09d2


From 3c624d4962a583516acadcbf60a8ca2a48421f5c Mon Sep 17 00:00:00 2001
From: Roger Quadros <roger.quadros@nokia.com>
Date: Tue, 5 Apr 2011 18:36:39 +0300
Subject: usb: gadget: f_mass_storage: If 'ro'/'cdrom' specified, open file as
 read-only

If we don't need Write access then attempt to open backing file in Read Only
mode instead of bailing out too soon.

Signed-off-by: Roger Quadros <roger.quadros@nokia.com>
Acked-by: Michal Nazarewicz <mina86@mina86.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/gadget/f_mass_storage.c | 1 +
 1 file changed, 1 insertion(+)

(limited to 'drivers/usb')

diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c
index e9de33d1c9a0..7d95a2cf58a3 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -2757,6 +2757,7 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
 	for (i = 0, lcfg = cfg->luns; i < nluns; ++i, ++curlun, ++lcfg) {
 		curlun->cdrom = !!lcfg->cdrom;
 		curlun->ro = lcfg->cdrom || lcfg->ro;
+		curlun->initially_ro = curlun->ro;
 		curlun->removable = lcfg->removable;
 		curlun->dev.release = fsg_lun_release;
 		curlun->dev.parent = &gadget->dev;
-- 
cgit v1.2.3-70-g09d2


From 84f6c3fb7e8eef51f994e959015e18bb927127e0 Mon Sep 17 00:00:00 2001
From: Randy Dunlap <randy.dunlap@oracle.com>
Date: Fri, 8 Apr 2011 10:11:44 -0700
Subject: usb/otg: fix twl6030 macro

Fix warning caused by stray semi-colon at end of macro:

drivers/usb/otg/twl6030-usb.c:183: warning: ISO C90 forbids mixed declarations and code

Signed-off-by: Randy Dunlap <randy.dunlap@oracle.com>
Cc: Hema HK <hemahk@ti.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/otg/twl6030-usb.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'drivers/usb')

diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c
index 8a91b4b832a1..6e920de64ef6 100644
--- a/drivers/usb/otg/twl6030-usb.c
+++ b/drivers/usb/otg/twl6030-usb.c
@@ -101,7 +101,7 @@ struct twl6030_usb {
 	bool			irq_enabled;
 };
 
-#define xceiv_to_twl(x)		container_of((x), struct twl6030_usb, otg);
+#define xceiv_to_twl(x)		container_of((x), struct twl6030_usb, otg)
 
 /*-------------------------------------------------------------------------*/
 
-- 
cgit v1.2.3-70-g09d2


From 74d1dc8d8d13fef55c33b49f06ab84eeebf967c4 Mon Sep 17 00:00:00 2001
From: "Weiping Pan(潘卫平)" <panweiping3@gmail.com>
Date: Mon, 11 Apr 2011 18:16:15 +0800
Subject: usb: fix a typo in a comment
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

replace tranmitted with transmitted.

Signed-off-by: Weiping Pan(潘卫平) <panweiping3@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/gadget/fsl_qe_udc.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'drivers/usb')

diff --git a/drivers/usb/gadget/fsl_qe_udc.h b/drivers/usb/gadget/fsl_qe_udc.h
index e35e24fd64bb..1da5fb03d218 100644
--- a/drivers/usb/gadget/fsl_qe_udc.h
+++ b/drivers/usb/gadget/fsl_qe_udc.h
@@ -207,7 +207,7 @@ struct qe_frame{
 
 /* Frame status field */
 /* Receive side */
-#define FRAME_OK               0x00000000 /* Frame tranmitted or received OK */
+#define FRAME_OK               0x00000000 /* Frame transmitted or received OK */
 #define FRAME_ERROR            0x80000000 /* Error occurred on frame */
 #define START_FRAME_LOST       0x40000000 /* START_FRAME_LOST */
 #define END_FRAME_LOST         0x20000000 /* END_FRAME_LOST */
-- 
cgit v1.2.3-70-g09d2


From 0b0cd6c81defc4e4fccb9e9103666547fefdb9c0 Mon Sep 17 00:00:00 2001
From: Fabio Estevam <fabio.estevam@freescale.com>
Date: Tue, 12 Apr 2011 09:31:54 -0300
Subject: USB: Mark ehci_adjust_port_wakeup_flags as __maybe_unused

Mark ehci_adjust_port_wakeup_flags as __maybe_unused to avoid the following
warning when building the ehci-mxc driver:

  CC      drivers/usb/host/ehci-hcd.o
drivers/usb/host/ehci-hub.c:130: warning: 'ehci_adjust_port_wakeup_flags' defined but not used

Current ehci-mxc driver implementation does not support suspend/resume.

Signed-off-by: Fabio Estevam <fabio.estevam@freescale.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/host/ehci-hub.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

(limited to 'drivers/usb')

diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index d05ea03cfb4d..1a21799195af 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -127,7 +127,7 @@ static int ehci_port_change(struct ehci_hcd *ehci)
 	return 0;
 }
 
-static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
+static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
 		bool suspending, bool do_wakeup)
 {
 	int		port;
-- 
cgit v1.2.3-70-g09d2


From a87103a6d4a91cbb9be49d3bbd2f35dcf5510da1 Mon Sep 17 00:00:00 2001
From: Matthias Kaehlcke <matthias.kaehlcke@tomtom.com>
Date: Fri, 1 Apr 2011 23:02:07 +0200
Subject: USB: twl4030-usb: Report correct vbus value for accessory charger
 adapters1

The twl4030-usb driver exports the status of VBUS as sysfs attribute.
In case an accessory charger adapter (ACA) is connected to the OTG
transceiver the attribute is always 'off', even when the charger
provides VBUS. Added a variable to keep track of the status of VBUS
and report it correctly

Signed-off-by: Matthias Kaehlcke <matthias.kaehlcke@tomtom.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/otg/twl4030-usb.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

(limited to 'drivers/usb')

diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c
index e01b073cc489..efeb4d1517ff 100644
--- a/drivers/usb/otg/twl4030-usb.c
+++ b/drivers/usb/otg/twl4030-usb.c
@@ -160,6 +160,7 @@ struct twl4030_usb {
 
 	int			irq;
 	u8			linkstat;
+	bool			vbus_supplied;
 	u8			asleep;
 	bool			irq_enabled;
 };
@@ -250,6 +251,8 @@ static enum usb_xceiv_events twl4030_usb_linkstat(struct twl4030_usb *twl)
 	int	status;
 	int	linkstat = USB_EVENT_NONE;
 
+	twl->vbus_supplied = false;
+
 	/*
 	 * For ID/VBUS sensing, see manual section 15.4.8 ...
 	 * except when using only battery backup power, two
@@ -265,6 +268,9 @@ static enum usb_xceiv_events twl4030_usb_linkstat(struct twl4030_usb *twl)
 	if (status < 0)
 		dev_err(twl->dev, "USB link status err %d\n", status);
 	else if (status & (BIT(7) | BIT(2))) {
+		if (status & (BIT(7)))
+                        twl->vbus_supplied = true;
+
 		if (status & BIT(2))
 			linkstat = USB_EVENT_ID;
 		else
@@ -484,7 +490,7 @@ static ssize_t twl4030_usb_vbus_show(struct device *dev,
 
 	spin_lock_irqsave(&twl->lock, flags);
 	ret = sprintf(buf, "%s\n",
-			(twl->linkstat == USB_EVENT_VBUS) ? "on" : "off");
+			twl->vbus_supplied ? "on" : "off");
 	spin_unlock_irqrestore(&twl->lock, flags);
 
 	return ret;
@@ -608,6 +614,7 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev)
 	twl->otg.set_peripheral	= twl4030_set_peripheral;
 	twl->otg.set_suspend	= twl4030_set_suspend;
 	twl->usb_mode		= pdata->usb_mode;
+	twl->vbus_supplied	= false;
 	twl->asleep = 1;
 
 	/* init spinlock for workqueue */
-- 
cgit v1.2.3-70-g09d2


From b750106bafbf9e8cb888a2b3681f444d8183a296 Mon Sep 17 00:00:00 2001
From: Johan Hovold <jhovold@gmail.com>
Date: Mon, 4 Apr 2011 11:38:07 +0200
Subject: Revert "USB: sam-ba: add driver for Atmel SAM Boot Assistant
 (SAM-BA)"
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This reverts commit 90593899de83a6e6fdea563d058acd2f4334e3f9.

SAM-BA devices identify themselves CDC-ACM devices and should be using
the cdc-acm driver.

Since commit 5b239f0aebd4dd6f85b13decf5e18e86e35d57f0 (USB: cdc-acm: Add pseudo
modem without AT command capabilities) cdc-acm also binds to them.

Note that the Atmel SAM-BA tools expect to use a USB-serial driver and thus
require a symlink from /dev/ttyACMn to some /dev/ttyUSBm (with m < 30) to be
able to select the device. This is simply a UI-issue that should be
fixed by Atmel.

Tested with the SAM-BA 2.10 tools and an Atmel at91sam9260-ek.

Signed-off-by: Johan Hovold <jhovold@gmail.com>
Cc: Sven Köhler <sven.koehler@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/serial/Kconfig  |   9 --
 drivers/usb/serial/Makefile |   1 -
 drivers/usb/serial/sam-ba.c | 206 --------------------------------------------
 3 files changed, 216 deletions(-)
 delete mode 100644 drivers/usb/serial/sam-ba.c

(limited to 'drivers/usb')

diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index c2b29761fa98..b71e309116a3 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -527,15 +527,6 @@ config USB_SERIAL_SAFE_PADDED
 	bool "USB Secure Encapsulated Driver - Padded"
 	depends on USB_SERIAL_SAFE
 
-config USB_SERIAL_SAMBA
-	tristate "USB Atmel SAM Boot Assistant (SAM-BA) driver"
-	help
-	  Say Y here if you want to access the SAM-BA boot application of an
-	  Atmel AT91SAM device.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called sam-ba.
-
 config USB_SERIAL_SIEMENS_MPI
 	tristate "USB Siemens MPI driver"
 	help
diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile
index 9a2117f2b06e..9e536eefb32c 100644
--- a/drivers/usb/serial/Makefile
+++ b/drivers/usb/serial/Makefile
@@ -48,7 +48,6 @@ obj-$(CONFIG_USB_SERIAL_PL2303)			+= pl2303.o
 obj-$(CONFIG_USB_SERIAL_QCAUX)			+= qcaux.o
 obj-$(CONFIG_USB_SERIAL_QUALCOMM)		+= qcserial.o
 obj-$(CONFIG_USB_SERIAL_SAFE)			+= safe_serial.o
-obj-$(CONFIG_USB_SERIAL_SAMBA)			+= sam-ba.o
 obj-$(CONFIG_USB_SERIAL_SIEMENS_MPI)		+= siemens_mpi.o
 obj-$(CONFIG_USB_SERIAL_SIERRAWIRELESS)		+= sierra.o
 obj-$(CONFIG_USB_SERIAL_SPCP8X5)		+= spcp8x5.o
diff --git a/drivers/usb/serial/sam-ba.c b/drivers/usb/serial/sam-ba.c
deleted file mode 100644
index e3bba64afc57..000000000000
--- a/drivers/usb/serial/sam-ba.c
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Atmel SAM Boot Assistant (SAM-BA) driver
- *
- * Copyright (C) 2010 Johan Hovold <jhovold@gmail.com>
- *
- *	This program is free software; you can redistribute it and/or
- *	modify it under the terms of the GNU General Public License version
- *	2 as published by the Free Software Foundation.
- */
-
-#include <linux/kernel.h>
-#include <linux/tty.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/usb.h>
-#include <linux/usb/serial.h>
-
-
-#define DRIVER_VERSION	"v1.0"
-#define DRIVER_AUTHOR	"Johan Hovold <jhovold@gmail.com>"
-#define DRIVER_DESC	"Atmel SAM Boot Assistant (SAM-BA) driver"
-
-#define SAMBA_VENDOR_ID		0x3eb
-#define SAMBA_PRODUCT_ID	0x6124
-
-
-static int debug;
-
-static const struct usb_device_id id_table[] = {
-	/*
-	 * NOTE: Only match the CDC Data interface.
-	 */
-	{ USB_DEVICE_AND_INTERFACE_INFO(SAMBA_VENDOR_ID, SAMBA_PRODUCT_ID,
-					USB_CLASS_CDC_DATA, 0, 0) },
-	{ }
-};
-MODULE_DEVICE_TABLE(usb, id_table);
-
-static struct usb_driver samba_driver = {
-	.name		= "sam-ba",
-	.probe		= usb_serial_probe,
-	.disconnect	= usb_serial_disconnect,
-	.id_table	= id_table,
-	.no_dynamic_id	= 1,
-};
-
-
-/*
- * NOTE: The SAM-BA firmware cannot handle merged write requests so we cannot
- *       use the generic write implementation (which uses the port write fifo).
- */
-static int samba_write(struct tty_struct *tty, struct usb_serial_port *port,
-					const unsigned char *buf, int count)
-{
-	struct urb *urb;
-	unsigned long flags;
-	int result;
-	int i;
-
-	if (!count)
-		return 0;
-
-	count = min_t(int, count, port->bulk_out_size);
-
-	spin_lock_irqsave(&port->lock, flags);
-	if (!port->write_urbs_free) {
-		spin_unlock_irqrestore(&port->lock, flags);
-		return 0;
-	}
-	i = find_first_bit(&port->write_urbs_free,
-						ARRAY_SIZE(port->write_urbs));
-	__clear_bit(i, &port->write_urbs_free);
-	port->tx_bytes += count;
-	spin_unlock_irqrestore(&port->lock, flags);
-
-	urb = port->write_urbs[i];
-	memcpy(urb->transfer_buffer, buf, count);
-	urb->transfer_buffer_length = count;
-	usb_serial_debug_data(debug, &port->dev, __func__, count,
-						urb->transfer_buffer);
-	result = usb_submit_urb(urb, GFP_ATOMIC);
-	if (result) {
-		dev_err(&port->dev, "%s - error submitting urb: %d\n",
-						__func__, result);
-		spin_lock_irqsave(&port->lock, flags);
-		__set_bit(i, &port->write_urbs_free);
-		port->tx_bytes -= count;
-		spin_unlock_irqrestore(&port->lock, flags);
-
-		return result;
-	}
-
-	return count;
-}
-
-static int samba_write_room(struct tty_struct *tty)
-{
-	struct usb_serial_port *port = tty->driver_data;
-	unsigned long flags;
-	unsigned long free;
-	int count;
-	int room;
-
-	spin_lock_irqsave(&port->lock, flags);
-	free = port->write_urbs_free;
-	spin_unlock_irqrestore(&port->lock, flags);
-
-	count = hweight_long(free);
-	room = count * port->bulk_out_size;
-
-	dbg("%s - returns %d", __func__, room);
-
-	return room;
-}
-
-static int samba_chars_in_buffer(struct tty_struct *tty)
-{
-	struct usb_serial_port *port = tty->driver_data;
-	unsigned long flags;
-	int chars;
-
-	spin_lock_irqsave(&port->lock, flags);
-	chars = port->tx_bytes;
-	spin_unlock_irqrestore(&port->lock, flags);
-
-	dbg("%s - returns %d", __func__, chars);
-
-	return chars;
-}
-
-static void samba_write_bulk_callback(struct urb *urb)
-{
-	struct usb_serial_port *port = urb->context;
-	unsigned long flags;
-	int i;
-
-	dbg("%s - port %d", __func__, port->number);
-
-	for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) {
-		if (port->write_urbs[i] == urb)
-			break;
-	}
-	spin_lock_irqsave(&port->lock, flags);
-	__set_bit(i, &port->write_urbs_free);
-	port->tx_bytes -= urb->transfer_buffer_length;
-	spin_unlock_irqrestore(&port->lock, flags);
-
-	if (urb->status)
-		dbg("%s - non-zero urb status: %d", __func__, urb->status);
-
-	usb_serial_port_softint(port);
-}
-
-static struct usb_serial_driver samba_device = {
-	.driver = {
-		.owner		= THIS_MODULE,
-		.name		= "sam-ba",
-	},
-	.usb_driver		= &samba_driver,
-	.id_table		= id_table,
-	.num_ports		= 1,
-	.bulk_in_size		= 512,
-	.bulk_out_size		= 2048,
-	.write			= samba_write,
-	.write_room		= samba_write_room,
-	.chars_in_buffer	= samba_chars_in_buffer,
-	.write_bulk_callback	= samba_write_bulk_callback,
-	.throttle		= usb_serial_generic_throttle,
-	.unthrottle		= usb_serial_generic_unthrottle,
-};
-
-static int __init samba_init(void)
-{
-	int retval;
-
-	retval = usb_serial_register(&samba_device);
-	if (retval)
-		return retval;
-
-	retval = usb_register(&samba_driver);
-	if (retval) {
-		usb_serial_deregister(&samba_device);
-		return retval;
-	}
-
-	printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ": "
-							DRIVER_DESC "\n");
-	return 0;
-}
-
-static void __exit samba_exit(void)
-{
-	usb_deregister(&samba_driver);
-	usb_serial_deregister(&samba_device);
-}
-
-module_init(samba_init);
-module_exit(samba_exit);
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_VERSION(DRIVER_VERSION);
-MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Enable verbose debugging messages");
-- 
cgit v1.2.3-70-g09d2


From 1bcc5aa87f043d34522d783154d08173b435fb46 Mon Sep 17 00:00:00 2001
From: Joonyoung Shim <jy0922.shim@samsung.com>
Date: Fri, 8 Apr 2011 14:08:50 +0900
Subject: USB: Add initial S5P EHCI driver

This patch adds host USB high speed driver for samsung S5P series. This
is initial driver and we need additional implementation to support some
functions like power management.

Signed-off-by: Jingoo Han <jg1.han@samsung.com>
Signed-off-by: Joonyoung Shim <jy0922.shim@samsung.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/Kconfig         |   1 +
 drivers/usb/host/Kconfig    |   6 ++
 drivers/usb/host/ehci-hcd.c |   5 ++
 drivers/usb/host/ehci-s5p.c | 201 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 213 insertions(+)
 create mode 100644 drivers/usb/host/ehci-s5p.c

(limited to 'drivers/usb')

diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index d299906e4f00..cf7614d29f03 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -65,6 +65,7 @@ config USB_ARCH_HAS_EHCI
 	default y if ARCH_CNS3XXX
 	default y if ARCH_VT8500
 	default y if PLAT_SPEAR
+	default y if PLAT_S5P
 	default y if ARCH_MSM
 	default PCI
 
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 5f518ded1955..ade009081feb 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -188,6 +188,12 @@ config USB_EHCI_SH
 	  Enables support for the on-chip EHCI controller on the SuperH.
 	  If you use the PCI EHCI controller, this option is not necessary.
 
+config USB_EHCI_S5P
+       boolean "S5P EHCI support"
+       depends on USB_EHCI_HCD && PLAT_S5P
+       help
+	 Enable support for the S5P SOC's on-chip EHCI controller.
+
 config USB_W90X900_EHCI
 	bool "W90X900(W90P910) EHCI support"
 	depends on USB_EHCI_HCD && ARCH_W90X900
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 78561d112c04..6b20b3b12d65 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1265,6 +1265,11 @@ MODULE_LICENSE ("GPL");
 #define PLATFORM_DRIVER		tegra_ehci_driver
 #endif
 
+#ifdef CONFIG_USB_EHCI_S5P
+#include "ehci-s5p.c"
+#define PLATFORM_DRIVER		s5p_ehci_driver
+#endif
+
 #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
     !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \
     !defined(XILINX_OF_PLATFORM_DRIVER)
diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c
new file mode 100644
index 000000000000..0c18f280bf4c
--- /dev/null
+++ b/drivers/usb/host/ehci-s5p.c
@@ -0,0 +1,201 @@
+/*
+ * SAMSUNG S5P USB HOST EHCI Controller
+ *
+ * Copyright (C) 2011 Samsung Electronics Co.Ltd
+ * Author: Jingoo Han <jg1.han@samsung.com>
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <mach/regs-pmu.h>
+#include <plat/cpu.h>
+#include <plat/ehci.h>
+#include <plat/usb-phy.h>
+
+struct s5p_ehci_hcd {
+	struct device *dev;
+	struct usb_hcd *hcd;
+	struct clk *clk;
+};
+
+static const struct hc_driver s5p_ehci_hc_driver = {
+	.description		= hcd_name,
+	.product_desc		= "S5P EHCI Host Controller",
+	.hcd_priv_size		= sizeof(struct ehci_hcd),
+
+	.irq			= ehci_irq,
+	.flags			= HCD_MEMORY | HCD_USB2,
+
+	.reset			= ehci_init,
+	.start			= ehci_run,
+	.stop			= ehci_stop,
+	.shutdown		= ehci_shutdown,
+
+	.get_frame_number	= ehci_get_frame,
+
+	.urb_enqueue		= ehci_urb_enqueue,
+	.urb_dequeue		= ehci_urb_dequeue,
+	.endpoint_disable	= ehci_endpoint_disable,
+	.endpoint_reset		= ehci_endpoint_reset,
+
+	.hub_status_data	= ehci_hub_status_data,
+	.hub_control		= ehci_hub_control,
+	.bus_suspend		= ehci_bus_suspend,
+	.bus_resume		= ehci_bus_resume,
+
+	.relinquish_port	= ehci_relinquish_port,
+	.port_handed_over	= ehci_port_handed_over,
+
+	.clear_tt_buffer_complete	= ehci_clear_tt_buffer_complete,
+};
+
+static int s5p_ehci_probe(struct platform_device *pdev)
+{
+	struct s5p_ehci_platdata *pdata;
+	struct s5p_ehci_hcd *s5p_ehci;
+	struct usb_hcd *hcd;
+	struct ehci_hcd *ehci;
+	struct resource *res;
+	int irq;
+	int err;
+
+	pdata = pdev->dev.platform_data;
+	if (!pdata) {
+		dev_err(&pdev->dev, "No platform data defined\n");
+		return -EINVAL;
+	}
+
+	s5p_ehci = kzalloc(sizeof(struct s5p_ehci_hcd), GFP_KERNEL);
+	if (!s5p_ehci)
+		return -ENOMEM;
+
+	s5p_ehci->dev = &pdev->dev;
+
+	hcd = usb_create_hcd(&s5p_ehci_hc_driver, &pdev->dev,
+					dev_name(&pdev->dev));
+	if (!hcd) {
+		dev_err(&pdev->dev, "Unable to create HCD\n");
+		err = -ENOMEM;
+		goto fail_hcd;
+	}
+
+	s5p_ehci->clk = clk_get(&pdev->dev, "usbhost");
+
+	if (IS_ERR(s5p_ehci->clk)) {
+		dev_err(&pdev->dev, "Failed to get usbhost clock\n");
+		err = PTR_ERR(s5p_ehci->clk);
+		goto fail_clk;
+	}
+
+	err = clk_enable(s5p_ehci->clk);
+	if (err)
+		goto fail_clken;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "Failed to get I/O memory\n");
+		err = -ENXIO;
+		goto fail_io;
+	}
+
+	hcd->rsrc_start = res->start;
+	hcd->rsrc_len = resource_size(res);
+	hcd->regs = ioremap(res->start, resource_size(res));
+	if (!hcd->regs) {
+		dev_err(&pdev->dev, "Failed to remap I/O memory\n");
+		err = -ENOMEM;
+		goto fail_io;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (!irq) {
+		dev_err(&pdev->dev, "Failed to get IRQ\n");
+		err = -ENODEV;
+		goto fail;
+	}
+
+	if (pdata->phy_init)
+		pdata->phy_init(pdev, S5P_USB_PHY_HOST);
+
+	ehci = hcd_to_ehci(hcd);
+	ehci->caps = hcd->regs;
+	ehci->regs = hcd->regs + HC_LENGTH(readl(&ehci->caps->hc_capbase));
+
+	dbg_hcs_params(ehci, "reset");
+	dbg_hcc_params(ehci, "reset");
+
+	/* cache this readonly data; minimize chip reads */
+	ehci->hcs_params = readl(&ehci->caps->hcs_params);
+
+	err = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
+	if (err) {
+		dev_err(&pdev->dev, "Failed to add USB HCD\n");
+		goto fail;
+	}
+
+	platform_set_drvdata(pdev, s5p_ehci);
+
+	return 0;
+
+fail:
+	iounmap(hcd->regs);
+fail_io:
+	clk_disable(s5p_ehci->clk);
+fail_clken:
+	clk_put(s5p_ehci->clk);
+fail_clk:
+	usb_put_hcd(hcd);
+fail_hcd:
+	kfree(s5p_ehci);
+	return err;
+}
+
+static int s5p_ehci_remove(struct platform_device *pdev)
+{
+	struct s5p_ehci_platdata *pdata = pdev->dev.platform_data;
+	struct s5p_ehci_hcd *s5p_ehci = platform_get_drvdata(pdev);
+	struct usb_hcd *hcd = s5p_ehci->hcd;
+
+	usb_remove_hcd(hcd);
+
+	if (pdata && pdata->phy_exit)
+		pdata->phy_exit(pdev, S5P_USB_PHY_HOST);
+
+	iounmap(hcd->regs);
+
+	clk_disable(s5p_ehci->clk);
+	clk_put(s5p_ehci->clk);
+
+	usb_put_hcd(hcd);
+	kfree(s5p_ehci);
+
+	return 0;
+}
+
+static void s5p_ehci_shutdown(struct platform_device *pdev)
+{
+	struct s5p_ehci_hcd *s5p_ehci = platform_get_drvdata(pdev);
+	struct usb_hcd *hcd = s5p_ehci->hcd;
+
+	if (hcd->driver->shutdown)
+		hcd->driver->shutdown(hcd);
+}
+
+static struct platform_driver s5p_ehci_driver = {
+	.probe		= s5p_ehci_probe,
+	.remove		= s5p_ehci_remove,
+	.shutdown	= s5p_ehci_shutdown,
+	.driver = {
+		.name	= "s5p-ehci",
+		.owner	= THIS_MODULE,
+	}
+};
+
+MODULE_ALIAS("platform:s5p-ehci");
-- 
cgit v1.2.3-70-g09d2


From 502fa84195f47a79d7220470ebaa85a773659755 Mon Sep 17 00:00:00 2001
From: Gabor Juhos <juhosg@openwrt.org>
Date: Wed, 13 Apr 2011 10:54:22 +0200
Subject: USB: ehci: add bus glue for the Atheros AR71XX/AR724X/AR91XX SoCs

The Atheros AR71XX/AR91XX SoCs have a built-in EHCI controller.
This patch adds the necessary glue code to make the generic EHCI
driver usable for them.

Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
Signed-off-by: Imre Kaloz <kaloz@openwrt.org>
Cc: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 arch/mips/ath79/Kconfig       |   3 +
 drivers/usb/host/Kconfig      |   9 ++
 drivers/usb/host/ehci-ath79.c | 200 ++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/host/ehci-hcd.c   |   5 ++
 4 files changed, 217 insertions(+)
 create mode 100644 drivers/usb/host/ehci-ath79.c

(limited to 'drivers/usb')

diff --git a/arch/mips/ath79/Kconfig b/arch/mips/ath79/Kconfig
index b05828260f7f..649a2a3bde78 100644
--- a/arch/mips/ath79/Kconfig
+++ b/arch/mips/ath79/Kconfig
@@ -26,12 +26,15 @@ config ATH79_MACH_PB44
 endmenu
 
 config SOC_AR71XX
+	select USB_ARCH_HAS_EHCI
 	def_bool n
 
 config SOC_AR724X
+	select USB_ARCH_HAS_EHCI
 	def_bool n
 
 config SOC_AR913X
+	select USB_ARCH_HAS_EHCI
 	def_bool n
 
 config ATH79_DEV_AR913X_WMAC
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index ade009081feb..da3757cec7ac 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -208,6 +208,15 @@ config USB_CNS3XXX_EHCI
 	  It is needed for high-speed (480Mbit/sec) USB 2.0 device
 	  support.
 
+config USB_EHCI_ATH79
+	bool "EHCI support for AR7XXX/AR9XXX SoCs"
+	depends on USB_EHCI_HCD && (SOC_AR71XX || SOC_AR724X || SOC_AR913X)
+	select USB_EHCI_ROOT_HUB_TT
+	default y
+	---help---
+	  Enables support for the built-in EHCI controller present
+	  on the Atheros AR7XXX/AR9XXX SoCs.
+
 config USB_OXU210HP_HCD
 	tristate "OXU210HP HCD support"
 	depends on USB
diff --git a/drivers/usb/host/ehci-ath79.c b/drivers/usb/host/ehci-ath79.c
new file mode 100644
index 000000000000..74325b87bd77
--- /dev/null
+++ b/drivers/usb/host/ehci-ath79.c
@@ -0,0 +1,200 @@
+/*
+ *  Bus Glue for Atheros AR7XXX/AR9XXX built-in EHCI controller.
+ *
+ *  Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  Parts of this file are based on Atheros' 2.6.15 BSP
+ *	Copyright (C) 2007 Atheros Communications, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ */
+
+#include <linux/platform_device.h>
+
+enum {
+	EHCI_ATH79_IP_V1 = 0,
+	EHCI_ATH79_IP_V2,
+};
+
+static const struct platform_device_id ehci_ath79_id_table[] = {
+	{
+		.name		= "ar71xx-ehci",
+		.driver_data	= EHCI_ATH79_IP_V1,
+	},
+	{
+		.name		= "ar724x-ehci",
+		.driver_data	= EHCI_ATH79_IP_V2,
+	},
+	{
+		.name		= "ar913x-ehci",
+		.driver_data	= EHCI_ATH79_IP_V2,
+	},
+	{
+		/* terminating entry */
+	},
+};
+
+MODULE_DEVICE_TABLE(platform, ehci_ath79_id_table);
+
+static int ehci_ath79_init(struct usb_hcd *hcd)
+{
+	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+	struct platform_device *pdev = to_platform_device(hcd->self.controller);
+	const struct platform_device_id *id;
+	int ret;
+
+	id = platform_get_device_id(pdev);
+	if (!id) {
+		dev_err(hcd->self.controller, "missing device id\n");
+		return -EINVAL;
+	}
+
+	switch (id->driver_data) {
+	case EHCI_ATH79_IP_V1:
+		ehci->caps = hcd->regs;
+		ehci->regs = hcd->regs +
+			HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
+		break;
+
+	case EHCI_ATH79_IP_V2:
+		hcd->has_tt = 1;
+
+		ehci->caps = hcd->regs + 0x100;
+		ehci->regs = hcd->regs + 0x100 +
+			HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
+		break;
+
+	default:
+		BUG();
+	}
+
+	dbg_hcs_params(ehci, "reset");
+	dbg_hcc_params(ehci, "reset");
+	ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
+	ehci->sbrn = 0x20;
+
+	ehci_reset(ehci);
+
+	ret = ehci_init(hcd);
+	if (ret)
+		return ret;
+
+	ehci_port_power(ehci, 0);
+
+	return 0;
+}
+
+static const struct hc_driver ehci_ath79_hc_driver = {
+	.description		= hcd_name,
+	.product_desc		= "Atheros built-in EHCI controller",
+	.hcd_priv_size		= sizeof(struct ehci_hcd),
+	.irq			= ehci_irq,
+	.flags			= HCD_MEMORY | HCD_USB2,
+
+	.reset			= ehci_ath79_init,
+	.start			= ehci_run,
+	.stop			= ehci_stop,
+	.shutdown		= ehci_shutdown,
+
+	.urb_enqueue		= ehci_urb_enqueue,
+	.urb_dequeue		= ehci_urb_dequeue,
+	.endpoint_disable	= ehci_endpoint_disable,
+	.endpoint_reset		= ehci_endpoint_reset,
+
+	.get_frame_number	= ehci_get_frame,
+
+	.hub_status_data	= ehci_hub_status_data,
+	.hub_control		= ehci_hub_control,
+
+	.relinquish_port	= ehci_relinquish_port,
+	.port_handed_over	= ehci_port_handed_over,
+
+	.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
+};
+
+static int ehci_ath79_probe(struct platform_device *pdev)
+{
+	struct usb_hcd *hcd;
+	struct resource *res;
+	int irq;
+	int ret;
+
+	if (usb_disabled())
+		return -ENODEV;
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!res) {
+		dev_dbg(&pdev->dev, "no IRQ specified\n");
+		return -ENODEV;
+	}
+	irq = res->start;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_dbg(&pdev->dev, "no base address specified\n");
+		return -ENODEV;
+	}
+
+	hcd = usb_create_hcd(&ehci_ath79_hc_driver, &pdev->dev,
+			     dev_name(&pdev->dev));
+	if (!hcd)
+		return -ENOMEM;
+
+	hcd->rsrc_start	= res->start;
+	hcd->rsrc_len	= res->end - res->start + 1;
+
+	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
+		dev_dbg(&pdev->dev, "controller already in use\n");
+		ret = -EBUSY;
+		goto err_put_hcd;
+	}
+
+	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+	if (!hcd->regs) {
+		dev_dbg(&pdev->dev, "error mapping memory\n");
+		ret = -EFAULT;
+		goto err_release_region;
+	}
+
+	ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
+	if (ret)
+		goto err_iounmap;
+
+	return 0;
+
+err_iounmap:
+	iounmap(hcd->regs);
+
+err_release_region:
+	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+err_put_hcd:
+	usb_put_hcd(hcd);
+	return ret;
+}
+
+static int ehci_ath79_remove(struct platform_device *pdev)
+{
+	struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+	usb_remove_hcd(hcd);
+	iounmap(hcd->regs);
+	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+	usb_put_hcd(hcd);
+
+	return 0;
+}
+
+static struct platform_driver ehci_ath79_driver = {
+	.probe		= ehci_ath79_probe,
+	.remove		= ehci_ath79_remove,
+	.id_table	= ehci_ath79_id_table,
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "ath79-ehci",
+	}
+};
+
+MODULE_ALIAS(PLATFORM_MODULE_PREFIX "ath79-ehci");
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 6b20b3b12d65..83b7d5f02a15 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1270,6 +1270,11 @@ MODULE_LICENSE ("GPL");
 #define PLATFORM_DRIVER		s5p_ehci_driver
 #endif
 
+#ifdef CONFIG_USB_EHCI_ATH79
+#include "ehci-ath79.c"
+#define PLATFORM_DRIVER		ehci_ath79_driver
+#endif
+
 #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
     !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \
     !defined(XILINX_OF_PLATFORM_DRIVER)
-- 
cgit v1.2.3-70-g09d2


From 2f7ac6c199978d0a0e407a12534201aa675a6482 Mon Sep 17 00:00:00 2001
From: Gabor Juhos <juhosg@openwrt.org>
Date: Wed, 13 Apr 2011 10:54:23 +0200
Subject: USB: ehci: add workaround for Synopsys HC bug

A Synopsys USB core used in various SoCs has a bug which might cause
that the host controller not issuing ping.

When software uses the Doorbell mechanism to remove queue heads, the
host controller still has references to the removed queue head even
after indicating an Interrupt on Async Advance. This happens if the last
executed queue head's Next Link queue head is removed.

Consequences of the defect:
The Host controller fetches the removed queue head, using memory that
would otherwise be deallocated.This results in incorrect transactions on
both the USB and system memory. This may result in undefined behavior.

Workarounds:

1) If no queue head is active (no Status field's Active bit is set)
after removing the queue heads, the software can write one of the valid
queue head addresses to the ASYNCLISTADDR register and deallocate the
removed queue head's memory after 2 microframes.

If one or more of the queue heads is active (the Active bit is set in
the Status field) after removing the queue heads, the software can delay
memory deallocation after time X, where X is the time required for the
Host Controller to go through all the queue heads once. X varies with
the number of queue heads and the time required to process periodic
transactions: if more periodic transactions must be performed, the Host
Controller has less time to process asynchronous transaction processing.

2) Do not use the Doorbell mechanism to remove the queue heads. Disable
the Asynchronous Schedule Enable bit instead.

The bug has been discussed on the linux-usb-devel mailing-list
four years ago, the original thread can be found here:
http://www.mail-archive.com/linux-usb-devel@lists.sourceforge.net/msg45345.html

This patch implements the first workaround as suggested by David Brownell.

The built-in USB host controller of the Atheros AR7130/AR7141/AR7161 SoCs
requires this to work properly.

Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/host/ehci-ath79.c | 2 ++
 drivers/usb/host/ehci-q.c     | 4 ++++
 drivers/usb/host/ehci.h       | 1 +
 3 files changed, 7 insertions(+)

(limited to 'drivers/usb')

diff --git a/drivers/usb/host/ehci-ath79.c b/drivers/usb/host/ehci-ath79.c
index 74325b87bd77..7ea23b50f5d8 100644
--- a/drivers/usb/host/ehci-ath79.c
+++ b/drivers/usb/host/ehci-ath79.c
@@ -54,6 +54,8 @@ static int ehci_ath79_init(struct usb_hcd *hcd)
 
 	switch (id->driver_data) {
 	case EHCI_ATH79_IP_V1:
+		ehci->has_synopsys_hc_bug = 1;
+
 		ehci->caps = hcd->regs;
 		ehci->regs = hcd->regs +
 			HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 98ded66e8d3f..6582aeab6237 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -1183,6 +1183,10 @@ static void end_unlink_async (struct ehci_hcd *ehci)
 		ehci->reclaim = NULL;
 		start_unlink_async (ehci, next);
 	}
+
+	if (ehci->has_synopsys_hc_bug)
+		ehci_writel(ehci, (u32) ehci->async->qh_dma,
+			    &ehci->regs->async_next);
 }
 
 /* makes sure the async qh will become idle */
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 333ddc156919..168f1a88c4d0 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -134,6 +134,7 @@ struct ehci_hcd {			/* one per controller */
 	unsigned		amd_pll_fix:1;
 	unsigned		fs_i_thresh:1;	/* Intel iso scheduling */
 	unsigned		use_dummy_qh:1;	/* AMD Frame List table quirk*/
+	unsigned		has_synopsys_hc_bug:1; /* Synopsys HC */
 
 	/* required for usb32 quirk */
 	#define OHCI_CTRL_HCFS          (3 << 6)
-- 
cgit v1.2.3-70-g09d2


From 90e6ca5cda8a38b7bb53660e67eff0845c0abe3f Mon Sep 17 00:00:00 2001
From: Gabor Juhos <juhosg@openwrt.org>
Date: Wed, 13 Apr 2011 10:54:24 +0200
Subject: USB: ohci: add bus glue for the Atheros AR71XX/AR7240 SoCs

The Atheros AR71XX/AR7240 SoCs have a built-in OHCI controller.
This patch adds the necessary glue code to make the generic OHCI
driver usable for them.

Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
Signed-off-by: Imre Kaloz <kaloz@openwrt.org>
Cc: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 arch/mips/ath79/Kconfig       |   2 +
 drivers/usb/host/Kconfig      |   8 +++
 drivers/usb/host/ohci-ath79.c | 151 ++++++++++++++++++++++++++++++++++++++++++
 drivers/usb/host/ohci-hcd.c   |   5 ++
 4 files changed, 166 insertions(+)
 create mode 100644 drivers/usb/host/ohci-ath79.c

(limited to 'drivers/usb')

diff --git a/arch/mips/ath79/Kconfig b/arch/mips/ath79/Kconfig
index 649a2a3bde78..47707410582c 100644
--- a/arch/mips/ath79/Kconfig
+++ b/arch/mips/ath79/Kconfig
@@ -27,10 +27,12 @@ endmenu
 
 config SOC_AR71XX
 	select USB_ARCH_HAS_EHCI
+	select USB_ARCH_HAS_OHCI
 	def_bool n
 
 config SOC_AR724X
 	select USB_ARCH_HAS_EHCI
+	select USB_ARCH_HAS_OHCI
 	def_bool n
 
 config SOC_AR913X
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index da3757cec7ac..fe4beca00009 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -302,6 +302,14 @@ config USB_OHCI_HCD_OMAP3
 	  Enables support for the on-chip OHCI controller on
 	  OMAP3 and later chips.
 
+config USB_OHCI_ATH79
+	bool "USB OHCI support for the Atheros AR71XX/AR7240 SoCs"
+	depends on USB_OHCI_HCD && (SOC_AR71XX || SOC_AR724X)
+	default y
+	help
+	  Enables support for the built-in OHCI controller present on the
+	  Atheros AR71XX/AR7240 SoCs.
+
 config USB_OHCI_HCD_PPC_SOC
 	bool "OHCI support for on-chip PPC USB controller"
 	depends on USB_OHCI_HCD && (STB03xxx || PPC_MPC52xx)
diff --git a/drivers/usb/host/ohci-ath79.c b/drivers/usb/host/ohci-ath79.c
new file mode 100644
index 000000000000..ffea3e7cb0a8
--- /dev/null
+++ b/drivers/usb/host/ohci-ath79.c
@@ -0,0 +1,151 @@
+/*
+ *  OHCI HCD (Host Controller Driver) for USB.
+ *
+ *  Bus Glue for Atheros AR71XX/AR724X built-in OHCI controller.
+ *
+ *  Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  Parts of this file are based on Atheros' 2.6.15 BSP
+ *	Copyright (C) 2007 Atheros Communications, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ */
+
+#include <linux/platform_device.h>
+
+static int __devinit ohci_ath79_start(struct usb_hcd *hcd)
+{
+	struct ohci_hcd	*ohci = hcd_to_ohci(hcd);
+	int ret;
+
+	ret = ohci_init(ohci);
+	if (ret < 0)
+		return ret;
+
+	ret = ohci_run(ohci);
+	if (ret < 0)
+		goto err;
+
+	return 0;
+
+err:
+	ohci_stop(hcd);
+	return ret;
+}
+
+static const struct hc_driver ohci_ath79_hc_driver = {
+	.description		= hcd_name,
+	.product_desc		= "Atheros built-in OHCI controller",
+	.hcd_priv_size		= sizeof(struct ohci_hcd),
+
+	.irq			= ohci_irq,
+	.flags			= HCD_USB11 | HCD_MEMORY,
+
+	.start			= ohci_ath79_start,
+	.stop			= ohci_stop,
+	.shutdown		= ohci_shutdown,
+
+	.urb_enqueue		= ohci_urb_enqueue,
+	.urb_dequeue		= ohci_urb_dequeue,
+	.endpoint_disable	= ohci_endpoint_disable,
+
+	/*
+	 * scheduling support
+	 */
+	.get_frame_number	= ohci_get_frame,
+
+	/*
+	 * root hub support
+	 */
+	.hub_status_data	= ohci_hub_status_data,
+	.hub_control		= ohci_hub_control,
+	.start_port_reset	= ohci_start_port_reset,
+};
+
+static int ohci_ath79_probe(struct platform_device *pdev)
+{
+	struct usb_hcd *hcd;
+	struct resource *res;
+	int irq;
+	int ret;
+
+	if (usb_disabled())
+		return -ENODEV;
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!res) {
+		dev_dbg(&pdev->dev, "no IRQ specified\n");
+		return -ENODEV;
+	}
+	irq = res->start;
+
+	hcd = usb_create_hcd(&ohci_ath79_hc_driver, &pdev->dev,
+			     dev_name(&pdev->dev));
+	if (!hcd)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_dbg(&pdev->dev, "no base address specified\n");
+		ret = -ENODEV;
+		goto err_put_hcd;
+	}
+	hcd->rsrc_start	= res->start;
+	hcd->rsrc_len	= res->end - res->start + 1;
+
+	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
+		dev_dbg(&pdev->dev, "controller already in use\n");
+		ret = -EBUSY;
+		goto err_put_hcd;
+	}
+
+	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+	if (!hcd->regs) {
+		dev_dbg(&pdev->dev, "error mapping memory\n");
+		ret = -EFAULT;
+		goto err_release_region;
+	}
+
+	ohci_hcd_init(hcd_to_ohci(hcd));
+
+	ret = usb_add_hcd(hcd, irq, IRQF_DISABLED);
+	if (ret)
+		goto err_stop_hcd;
+
+	return 0;
+
+err_stop_hcd:
+	iounmap(hcd->regs);
+err_release_region:
+	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+err_put_hcd:
+	usb_put_hcd(hcd);
+	return ret;
+}
+
+static int ohci_ath79_remove(struct platform_device *pdev)
+{
+	struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+	usb_remove_hcd(hcd);
+	iounmap(hcd->regs);
+	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+	usb_put_hcd(hcd);
+
+	return 0;
+}
+
+static struct platform_driver ohci_hcd_ath79_driver = {
+	.probe		= ohci_ath79_probe,
+	.remove		= ohci_ath79_remove,
+	.shutdown	= usb_hcd_platform_shutdown,
+	.driver		= {
+		.name	= "ath79-ohci",
+		.owner	= THIS_MODULE,
+	},
+};
+
+MODULE_ALIAS(PLATFORM_MODULE_PREFIX "ath79-ohci");
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index d55723514860..8c8dc6559ac7 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -1105,6 +1105,11 @@ MODULE_LICENSE ("GPL");
 #define PLATFORM_DRIVER		ohci_hcd_cns3xxx_driver
 #endif
 
+#ifdef CONFIG_USB_OHCI_ATH79
+#include "ohci-ath79.c"
+#define PLATFORM_DRIVER		ohci_hcd_ath79_driver
+#endif
+
 #if	!defined(PCI_DRIVER) &&		\
 	!defined(PLATFORM_DRIVER) &&	\
 	!defined(OMAP1_PLATFORM_DRIVER) &&	\
-- 
cgit v1.2.3-70-g09d2


From db8fa2852ed5b46c05148db87be135300f17b8ce Mon Sep 17 00:00:00 2001
From: Michal Nazarewicz <mina86@mina86.com>
Date: Thu, 14 Apr 2011 00:37:00 +0200
Subject: usb: gadget: storage_common: use kstrto*()

This commit replaces the usage of strict_strtoul() (which
became deprecated after commit 33ee3b2e) with kstrtouint().

Signed-off-by: Michal Nazarewicz <mina86@mina86.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/gadget/storage_common.c | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

(limited to 'drivers/usb')

diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c
index 3179b8bb6ced..86fcebd89abe 100644
--- a/drivers/usb/gadget/storage_common.c
+++ b/drivers/usb/gadget/storage_common.c
@@ -704,10 +704,11 @@ static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr,
 	ssize_t		rc = count;
 	struct fsg_lun	*curlun = fsg_lun_from_dev(dev);
 	struct rw_semaphore	*filesem = dev_get_drvdata(dev);
-	unsigned long	ro;
+	unsigned	ro;
 
-	if (strict_strtoul(buf, 2, &ro))
-		return -EINVAL;
+	rc = kstrtouint(buf, 2, &ro);
+	if (rc)
+		return rc;
 
 	/*
 	 * Allow the write-enable status to change only while the
@@ -731,10 +732,12 @@ static ssize_t fsg_store_nofua(struct device *dev,
 			       const char *buf, size_t count)
 {
 	struct fsg_lun	*curlun = fsg_lun_from_dev(dev);
-	unsigned long	nofua;
+	unsigned	nofua;
+	int		ret;
 
-	if (strict_strtoul(buf, 2, &nofua))
-		return -EINVAL;
+	ret = kstrtouint(buf, 2, &nofua);
+	if (ret)
+		return ret;
 
 	/* Sync data when switching from async mode to sync */
 	if (!nofua && curlun->nofua)
-- 
cgit v1.2.3-70-g09d2


From 98346f7db014614a4814eb60639f651f8bbc591d Mon Sep 17 00:00:00 2001
From: Greg Kroah-Hartman <gregkh@suse.de>
Date: Thu, 14 Apr 2011 13:42:46 -0700
Subject: Revert "usb: usb_storage: do not align length of request for CBW to
 maxp size"

This reverts commit 806e8f8fcc27e1753947bd9f059ba2316cf8f92a.

To quote Alan Stern:
	The necessity for this patch has been under discussion.

	It turns out the UDC that Mian has been working on and Felipe's
	UDC have contradictory requirements.  Mian's UDC driver wants a
	bulk-OUT transfer length to be shorter than the maxpacket size
	if a short packet is expected, whereas Felipe's UDC hardware
	always needs bulk-OUT transfer lengths to be evenly divisible by
	the maxpacket size.

	Mian has agreed to go back over the driver to resolve this
	conflict.  This means we probably will not want this patch after
	all.  (In fact, we may ultimately decide to change the gadget
	framework to require that bulk-OUT transfer lengths _always_ be
	divisible by the maxpacket size -- only the g_file_storage and
	g_mass_storage gadgets would need to be changed.)

Cc: Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>
Cc: Michal Nazarewicz <mina86@mina86.com>
Cc: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/usb/gadget/f_mass_storage.c | 24 ++++++++++++++++++++----
 drivers/usb/gadget/file_storage.c   | 28 ++++++++++++++++++++++------
 drivers/usb/gadget/storage_common.c |  7 +++++++
 3 files changed, 49 insertions(+), 10 deletions(-)

(limited to 'drivers/usb')

diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c
index 7d95a2cf58a3..01ae27b60d4c 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -474,6 +474,20 @@ static int exception_in_progress(struct fsg_common *common)
 	return common->state > FSG_STATE_IDLE;
 }
 
+/* Make bulk-out requests be divisible by the maxpacket size */
+static void set_bulk_out_req_length(struct fsg_common *common,
+				    struct fsg_buffhd *bh, unsigned int length)
+{
+	unsigned int	rem;
+
+	bh->bulk_out_intended_length = length;
+	rem = length % common->bulk_out_maxpacket;
+	if (rem > 0)
+		length += common->bulk_out_maxpacket - rem;
+	bh->outreq->length = length;
+}
+
+
 /*-------------------------------------------------------------------------*/
 
 static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep)
@@ -572,9 +586,9 @@ static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req)
 	struct fsg_buffhd	*bh = req->context;
 
 	dump_msg(common, "bulk-out", req->buf, req->actual);
-	if (req->status || req->actual != req->length)
+	if (req->status || req->actual != bh->bulk_out_intended_length)
 		DBG(common, "%s --> %d, %u/%u\n", __func__,
-		    req->status, req->actual, req->length);
+		    req->status, req->actual, bh->bulk_out_intended_length);
 	if (req->status == -ECONNRESET)		/* Request was cancelled */
 		usb_ep_fifo_flush(ep);
 
@@ -966,6 +980,7 @@ static int do_write(struct fsg_common *common)
 			 * the bulk-out maxpacket size
 			 */
 			bh->outreq->length = amount;
+			bh->bulk_out_intended_length = amount;
 			bh->outreq->short_not_ok = 1;
 			if (!start_out_transfer(common, bh))
 				/* Dunno what to do if common->fsg is NULL */
@@ -1611,6 +1626,7 @@ static int throw_away_data(struct fsg_common *common)
 			 * the bulk-out maxpacket size.
 			 */
 			bh->outreq->length = amount;
+			bh->bulk_out_intended_length = amount;
 			bh->outreq->short_not_ok = 1;
 			if (!start_out_transfer(common, bh))
 				/* Dunno what to do if common->fsg is NULL */
@@ -2279,8 +2295,8 @@ static int get_next_command(struct fsg_common *common)
 	}
 
 	/* Queue a request to read a Bulk-only CBW */
-	bh->outreq->length = USB_BULK_CB_WRAP_LEN;
-	bh->outreq->short_not_ok = 0;
+	set_bulk_out_req_length(common, bh, USB_BULK_CB_WRAP_LEN);
+	bh->outreq->short_not_ok = 1;
 	if (!start_out_transfer(common, bh))
 		/* Don't know what to do if common->fsg is NULL */
 		return -EIO;
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
index aebfb81f3ba4..fcfc77c7ad70 100644
--- a/drivers/usb/gadget/file_storage.c
+++ b/drivers/usb/gadget/file_storage.c
@@ -497,6 +497,19 @@ static int exception_in_progress(struct fsg_dev *fsg)
 	return (fsg->state > FSG_STATE_IDLE);
 }
 
+/* Make bulk-out requests be divisible by the maxpacket size */
+static void set_bulk_out_req_length(struct fsg_dev *fsg,
+		struct fsg_buffhd *bh, unsigned int length)
+{
+	unsigned int	rem;
+
+	bh->bulk_out_intended_length = length;
+	rem = length % fsg->bulk_out_maxpacket;
+	if (rem > 0)
+		length += fsg->bulk_out_maxpacket - rem;
+	bh->outreq->length = length;
+}
+
 static struct fsg_dev			*the_fsg;
 static struct usb_gadget_driver		fsg_driver;
 
@@ -717,9 +730,10 @@ static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req)
 	struct fsg_buffhd	*bh = req->context;
 
 	dump_msg(fsg, "bulk-out", req->buf, req->actual);
-	if (req->status || req->actual != req->length)
+	if (req->status || req->actual != bh->bulk_out_intended_length)
 		DBG(fsg, "%s --> %d, %u/%u\n", __func__,
-				req->status, req->actual, req->length);
+				req->status, req->actual,
+				bh->bulk_out_intended_length);
 	if (req->status == -ECONNRESET)		// Request was cancelled
 		usb_ep_fifo_flush(ep);
 
@@ -1335,7 +1349,8 @@ static int do_write(struct fsg_dev *fsg)
 
 			/* amount is always divisible by 512, hence by
 			 * the bulk-out maxpacket size */
-			bh->outreq->length = amount;
+			bh->outreq->length = bh->bulk_out_intended_length =
+					amount;
 			bh->outreq->short_not_ok = 1;
 			start_transfer(fsg, fsg->bulk_out, bh->outreq,
 					&bh->outreq_busy, &bh->state);
@@ -1964,7 +1979,8 @@ static int throw_away_data(struct fsg_dev *fsg)
 
 			/* amount is always divisible by 512, hence by
 			 * the bulk-out maxpacket size */
-			bh->outreq->length = amount;
+			bh->outreq->length = bh->bulk_out_intended_length =
+					amount;
 			bh->outreq->short_not_ok = 1;
 			start_transfer(fsg, fsg->bulk_out, bh->outreq,
 					&bh->outreq_busy, &bh->state);
@@ -2643,8 +2659,8 @@ static int get_next_command(struct fsg_dev *fsg)
 		}
 
 		/* Queue a request to read a Bulk-only CBW */
-		bh->outreq->length = USB_BULK_CB_WRAP_LEN;
-		bh->outreq->short_not_ok = 0;
+		set_bulk_out_req_length(fsg, bh, USB_BULK_CB_WRAP_LEN);
+		bh->outreq->short_not_ok = 1;
 		start_transfer(fsg, fsg->bulk_out, bh->outreq,
 				&bh->outreq_busy, &bh->state);
 
diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c
index 86fcebd89abe..109635a84888 100644
--- a/drivers/usb/gadget/storage_common.c
+++ b/drivers/usb/gadget/storage_common.c
@@ -286,6 +286,13 @@ struct fsg_buffhd {
 	enum fsg_buffer_state		state;
 	struct fsg_buffhd		*next;
 
+	/*
+	 * The NetChip 2280 is faster, and handles some protocol faults
+	 * better, if we don't submit any short bulk-out read requests.
+	 * So we will record the intended request length here.
+	 */
+	unsigned int			bulk_out_intended_length;
+
 	struct usb_request		*inreq;
 	int				inreq_busy;
 	struct usb_request		*outreq;
-- 
cgit v1.2.3-70-g09d2