浏览代码

Merge 2.6.39-rc4 into usb-next

This is needed to help resolve some xhci issues and other minor
differences.

Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Greg Kroah-Hartman 14 年之前
父节点
当前提交
50ee9339c7
共有 58 个文件被更改,包括 4776 次插入657 次删除
  1. 1 0
      arch/arm/mach-exynos4/Kconfig
  2. 2 0
      arch/arm/mach-exynos4/Makefile
  3. 6 1
      arch/arm/mach-exynos4/cpu.c
  4. 4 0
      arch/arm/mach-exynos4/include/mach/map.h
  5. 3 0
      arch/arm/mach-exynos4/include/mach/regs-pmu.h
  6. 64 0
      arch/arm/mach-exynos4/include/mach/regs-usb-phy.h
  7. 16 0
      arch/arm/mach-exynos4/mach-nuri.c
  8. 136 0
      arch/arm/mach-exynos4/usb-phy.c
  9. 5 0
      arch/arm/plat-s5p/Kconfig
  10. 1 0
      arch/arm/plat-s5p/Makefile
  11. 57 0
      arch/arm/plat-s5p/dev-ehci.c
  12. 21 0
      arch/arm/plat-s5p/include/plat/ehci.h
  13. 1 1
      arch/arm/plat-s5p/include/plat/map-s5p.h
  14. 22 0
      arch/arm/plat-s5p/include/plat/usb-phy.h
  15. 2 0
      arch/arm/plat-samsung/include/plat/devs.h
  16. 5 0
      arch/mips/ath79/Kconfig
  17. 2 3
      drivers/Makefile
  18. 3 0
      drivers/usb/Kconfig
  19. 5 0
      drivers/usb/Makefile
  20. 189 277
      drivers/usb/class/cdc-acm.c
  21. 6 17
      drivers/usb/class/cdc-acm.h
  22. 11 11
      drivers/usb/core/devices.c
  23. 0 8
      drivers/usb/core/file.c
  24. 18 0
      drivers/usb/gadget/Kconfig
  25. 17 43
      drivers/usb/gadget/f_mass_storage.c
  26. 12 41
      drivers/usb/gadget/file_storage.c
  27. 1 1
      drivers/usb/gadget/fsl_qe_udc.h
  28. 9 0
      drivers/usb/gadget/gadget_chips.h
  29. 9 6
      drivers/usb/gadget/storage_common.c
  30. 33 0
      drivers/usb/host/Kconfig
  31. 202 0
      drivers/usb/host/ehci-ath79.c
  32. 10 0
      drivers/usb/host/ehci-hcd.c
  33. 1 1
      drivers/usb/host/ehci-hub.c
  34. 4 0
      drivers/usb/host/ehci-q.c
  35. 201 0
      drivers/usb/host/ehci-s5p.c
  36. 1 0
      drivers/usb/host/ehci.h
  37. 151 0
      drivers/usb/host/ohci-ath79.c
  38. 5 0
      drivers/usb/host/ohci-hcd.c
  39. 1 7
      drivers/usb/host/sl811-hcd.c
  40. 1 2
      drivers/usb/host/u132-hcd.c
  41. 10 16
      drivers/usb/host/uhci-hcd.c
  42. 1 2
      drivers/usb/misc/ftdi-elan.c
  43. 8 1
      drivers/usb/otg/twl4030-usb.c
  44. 1 1
      drivers/usb/otg/twl6030-usb.c
  45. 15 0
      drivers/usb/renesas_usbhs/Kconfig
  46. 9 0
      drivers/usb/renesas_usbhs/Makefile
  47. 394 0
      drivers/usb/renesas_usbhs/common.c
  48. 225 0
      drivers/usb/renesas_usbhs/common.h
  49. 276 0
      drivers/usb/renesas_usbhs/mod.c
  50. 122 0
      drivers/usb/renesas_usbhs/mod.h
  51. 1341 0
      drivers/usb/renesas_usbhs/mod_gadget.c
  52. 880 0
      drivers/usb/renesas_usbhs/pipe.c
  53. 105 0
      drivers/usb/renesas_usbhs/pipe.h
  54. 0 9
      drivers/usb/serial/Kconfig
  55. 0 1
      drivers/usb/serial/Makefile
  56. 0 206
      drivers/usb/serial/sam-ba.c
  57. 2 2
      include/linux/usb/gadget.h
  58. 149 0
      include/linux/usb/renesas_usbhs.h

+ 1 - 0
arch/arm/mach-exynos4/Kconfig

@@ -170,6 +170,7 @@ config MACH_NURI
 	select S3C_DEV_HSMMC3
 	select S3C_DEV_I2C1
 	select S3C_DEV_I2C5
+	select S5P_DEV_USB_EHCI
 	select EXYNOS4_SETUP_I2C1
 	select EXYNOS4_SETUP_I2C5
 	select EXYNOS4_SETUP_SDHCI

+ 2 - 0
arch/arm/mach-exynos4/Makefile

@@ -54,3 +54,5 @@ obj-$(CONFIG_EXYNOS4_SETUP_I2C7)	+= setup-i2c7.o
 obj-$(CONFIG_EXYNOS4_SETUP_KEYPAD)	+= setup-keypad.o
 obj-$(CONFIG_EXYNOS4_SETUP_SDHCI)	+= setup-sdhci.o
 obj-$(CONFIG_EXYNOS4_SETUP_SDHCI_GPIO)	+= setup-sdhci-gpio.o
+
+obj-$(CONFIG_USB_SUPPORT)		+= usb-phy.o

+ 6 - 1
arch/arm/mach-exynos4/cpu.c

@@ -97,7 +97,12 @@ static struct map_desc exynos4_iodesc[] __initdata = {
 		.pfn		= __phys_to_pfn(EXYNOS4_PA_SROMC),
 		.length		= SZ_4K,
 		.type		= MT_DEVICE,
-	},
+	}, {
+		.virtual	= (unsigned long)S5P_VA_USB_HSPHY,
+		.pfn		= __phys_to_pfn(EXYNOS4_PA_HSPHY),
+		.length		= SZ_4K,
+		.type		= MT_DEVICE,
+	}
 };
 
 static void exynos4_idle(void)

+ 4 - 0
arch/arm/mach-exynos4/include/mach/map.h

@@ -101,6 +101,9 @@
 
 #define EXYNOS4_PA_SROMC		0x12570000
 
+#define EXYNOS4_PA_EHCI			0x12580000
+#define EXYNOS4_PA_HSPHY		0x125B0000
+
 #define EXYNOS4_PA_UART			0x13800000
 
 #define EXYNOS4_PA_IIC(x)		(0x13860000 + ((x) * 0x10000))
@@ -143,6 +146,7 @@
 #define S5P_PA_SROMC			EXYNOS4_PA_SROMC
 #define S5P_PA_SYSCON			EXYNOS4_PA_SYSCON
 #define S5P_PA_TIMER			EXYNOS4_PA_TIMER
+#define S5P_PA_EHCI			EXYNOS4_PA_EHCI
 
 #define SAMSUNG_PA_KEYPAD		EXYNOS4_PA_KEYPAD
 

+ 3 - 0
arch/arm/mach-exynos4/include/mach/regs-pmu.h

@@ -33,6 +33,9 @@
 #define S5P_EINT_WAKEUP_MASK			S5P_PMUREG(0x0604)
 #define S5P_WAKEUP_MASK				S5P_PMUREG(0x0608)
 
+#define S5P_USBHOST_PHY_CONTROL			S5P_PMUREG(0x0708)
+#define S5P_USBHOST_PHY_ENABLE			(1 << 0)
+
 #define S5P_MIPI_DPHY_CONTROL(n)		S5P_PMUREG(0x0710 + (n) * 4)
 #define S5P_MIPI_DPHY_ENABLE			(1 << 0)
 #define S5P_MIPI_DPHY_SRESETN			(1 << 1)

+ 64 - 0
arch/arm/mach-exynos4/include/mach/regs-usb-phy.h

@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011 Samsung Electronics Co.Ltd
+ * 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.
+ */
+
+#ifndef __PLAT_S5P_REGS_USB_PHY_H
+#define __PLAT_S5P_REGS_USB_PHY_H
+
+#define EXYNOS4_HSOTG_PHYREG(x)		((x) + S5P_VA_USB_HSPHY)
+
+#define EXYNOS4_PHYPWR			EXYNOS4_HSOTG_PHYREG(0x00)
+#define PHY1_HSIC_NORMAL_MASK		(0xf << 9)
+#define PHY1_HSIC1_SLEEP		(1 << 12)
+#define PHY1_HSIC1_FORCE_SUSPEND	(1 << 11)
+#define PHY1_HSIC0_SLEEP		(1 << 10)
+#define PHY1_HSIC0_FORCE_SUSPEND	(1 << 9)
+
+#define PHY1_STD_NORMAL_MASK		(0x7 << 6)
+#define PHY1_STD_SLEEP			(1 << 8)
+#define PHY1_STD_ANALOG_POWERDOWN	(1 << 7)
+#define PHY1_STD_FORCE_SUSPEND		(1 << 6)
+
+#define PHY0_NORMAL_MASK		(0x39 << 0)
+#define PHY0_SLEEP			(1 << 5)
+#define PHY0_OTG_DISABLE		(1 << 4)
+#define PHY0_ANALOG_POWERDOWN		(1 << 3)
+#define PHY0_FORCE_SUSPEND		(1 << 0)
+
+#define EXYNOS4_PHYCLK			EXYNOS4_HSOTG_PHYREG(0x04)
+#define PHY1_COMMON_ON_N		(1 << 7)
+#define PHY0_COMMON_ON_N		(1 << 4)
+#define PHY0_ID_PULLUP			(1 << 2)
+#define CLKSEL_MASK			(0x3 << 0)
+#define CLKSEL_SHIFT			(0)
+#define CLKSEL_48M			(0x0 << 0)
+#define CLKSEL_12M			(0x2 << 0)
+#define CLKSEL_24M			(0x3 << 0)
+
+#define EXYNOS4_RSTCON			EXYNOS4_HSOTG_PHYREG(0x08)
+#define HOST_LINK_PORT_SWRST_MASK	(0xf << 6)
+#define HOST_LINK_PORT2_SWRST		(1 << 9)
+#define HOST_LINK_PORT1_SWRST		(1 << 8)
+#define HOST_LINK_PORT0_SWRST		(1 << 7)
+#define HOST_LINK_ALL_SWRST		(1 << 6)
+
+#define PHY1_SWRST_MASK			(0x7 << 3)
+#define PHY1_HSIC_SWRST			(1 << 5)
+#define PHY1_STD_SWRST			(1 << 4)
+#define PHY1_ALL_SWRST			(1 << 3)
+
+#define PHY0_SWRST_MASK			(0x7 << 0)
+#define PHY0_PHYLINK_SWRST		(1 << 2)
+#define PHY0_HLINK_SWRST		(1 << 1)
+#define PHY0_SWRST			(1 << 0)
+
+#define EXYNOS4_PHY1CON			EXYNOS4_HSOTG_PHYREG(0x34)
+#define FPENABLEN			(1 << 0)
+
+#endif /* __PLAT_S5P_REGS_USB_PHY_H */

+ 16 - 0
arch/arm/mach-exynos4/mach-nuri.c

@@ -30,6 +30,8 @@
 #include <plat/cpu.h>
 #include <plat/devs.h>
 #include <plat/sdhci.h>
+#include <plat/ehci.h>
+#include <plat/clock.h>
 
 #include <mach/map.h>
 
@@ -262,6 +264,16 @@ static struct i2c_board_info i2c5_devs[] __initdata = {
 	/* max8997, To be updated */
 };
 
+/* USB EHCI */
+static struct s5p_ehci_platdata nuri_ehci_pdata;
+
+static void __init nuri_ehci_init(void)
+{
+	struct s5p_ehci_platdata *pdata = &nuri_ehci_pdata;
+
+	s5p_ehci_set_platdata(pdata);
+}
+
 static struct platform_device *nuri_devices[] __initdata = {
 	/* Samsung Platform Devices */
 	&emmc_fixed_voltage,
@@ -270,6 +282,7 @@ static struct platform_device *nuri_devices[] __initdata = {
 	&s3c_device_hsmmc3,
 	&s3c_device_wdt,
 	&s3c_device_timer[0],
+	&s5p_device_ehci,
 
 	/* NURI Devices */
 	&nuri_gpio_keys,
@@ -291,6 +304,9 @@ static void __init nuri_machine_init(void)
 	i2c_register_board_info(1, i2c1_devs, ARRAY_SIZE(i2c1_devs));
 	i2c_register_board_info(5, i2c5_devs, ARRAY_SIZE(i2c5_devs));
 
+	nuri_ehci_init();
+	clk_xusbxti.rate = 24000000;
+
 	/* Last */
 	platform_add_devices(nuri_devices, ARRAY_SIZE(nuri_devices));
 }

+ 136 - 0
arch/arm/mach-exynos4/usb-phy.c

@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2011 Samsung Electronics Co.Ltd
+ * 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/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <mach/regs-pmu.h>
+#include <mach/regs-usb-phy.h>
+#include <plat/cpu.h>
+#include <plat/usb-phy.h>
+
+static int exynos4_usb_phy1_init(struct platform_device *pdev)
+{
+	struct clk *otg_clk;
+	struct clk *xusbxti_clk;
+	u32 phyclk;
+	u32 rstcon;
+	int err;
+
+	otg_clk = clk_get(&pdev->dev, "otg");
+	if (IS_ERR(otg_clk)) {
+		dev_err(&pdev->dev, "Failed to get otg clock\n");
+		return PTR_ERR(otg_clk);
+	}
+
+	err = clk_enable(otg_clk);
+	if (err) {
+		clk_put(otg_clk);
+		return err;
+	}
+
+	writel(readl(S5P_USBHOST_PHY_CONTROL) | S5P_USBHOST_PHY_ENABLE,
+			S5P_USBHOST_PHY_CONTROL);
+
+	/* set clock frequency for PLL */
+	phyclk = readl(EXYNOS4_PHYCLK) & ~CLKSEL_MASK;
+
+	xusbxti_clk = clk_get(&pdev->dev, "xusbxti");
+	if (xusbxti_clk && !IS_ERR(xusbxti_clk)) {
+		switch (clk_get_rate(xusbxti_clk)) {
+		case 12 * MHZ:
+			phyclk |= CLKSEL_12M;
+			break;
+		case 24 * MHZ:
+			phyclk |= CLKSEL_24M;
+			break;
+		default:
+		case 48 * MHZ:
+			/* default reference clock */
+			break;
+		}
+		clk_put(xusbxti_clk);
+	}
+
+	writel(phyclk, EXYNOS4_PHYCLK);
+
+	/* floating prevention logic: disable */
+	writel((readl(EXYNOS4_PHY1CON) | FPENABLEN), EXYNOS4_PHY1CON);
+
+	/* set to normal HSIC 0 and 1 of PHY1 */
+	writel((readl(EXYNOS4_PHYPWR) & ~PHY1_HSIC_NORMAL_MASK),
+			EXYNOS4_PHYPWR);
+
+	/* set to normal standard USB of PHY1 */
+	writel((readl(EXYNOS4_PHYPWR) & ~PHY1_STD_NORMAL_MASK), EXYNOS4_PHYPWR);
+
+	/* reset all ports of both PHY and Link */
+	rstcon = readl(EXYNOS4_RSTCON) | HOST_LINK_PORT_SWRST_MASK |
+		PHY1_SWRST_MASK;
+	writel(rstcon, EXYNOS4_RSTCON);
+	udelay(10);
+
+	rstcon &= ~(HOST_LINK_PORT_SWRST_MASK | PHY1_SWRST_MASK);
+	writel(rstcon, EXYNOS4_RSTCON);
+	udelay(50);
+
+	clk_disable(otg_clk);
+	clk_put(otg_clk);
+
+	return 0;
+}
+
+static int exynos4_usb_phy1_exit(struct platform_device *pdev)
+{
+	struct clk *otg_clk;
+	int err;
+
+	otg_clk = clk_get(&pdev->dev, "otg");
+	if (IS_ERR(otg_clk)) {
+		dev_err(&pdev->dev, "Failed to get otg clock\n");
+		return PTR_ERR(otg_clk);
+	}
+
+	err = clk_enable(otg_clk);
+	if (err) {
+		clk_put(otg_clk);
+		return err;
+	}
+
+	writel((readl(EXYNOS4_PHYPWR) | PHY1_STD_ANALOG_POWERDOWN),
+			EXYNOS4_PHYPWR);
+
+	writel(readl(S5P_USBHOST_PHY_CONTROL) & ~S5P_USBHOST_PHY_ENABLE,
+			S5P_USBHOST_PHY_CONTROL);
+
+	clk_disable(otg_clk);
+	clk_put(otg_clk);
+
+	return 0;
+}
+
+int s5p_usb_phy_init(struct platform_device *pdev, int type)
+{
+	if (type == S5P_USB_PHY_HOST)
+		return exynos4_usb_phy1_init(pdev);
+
+	return -EINVAL;
+}
+
+int s5p_usb_phy_exit(struct platform_device *pdev, int type)
+{
+	if (type == S5P_USB_PHY_HOST)
+		return exynos4_usb_phy1_exit(pdev);
+
+	return -EINVAL;
+}

+ 5 - 0
arch/arm/plat-s5p/Kconfig

@@ -85,6 +85,11 @@ config S5P_DEV_CSIS1
 	help
 	  Compile in platform device definitions for MIPI-CSIS channel 1
 
+config S5P_DEV_USB_EHCI
+	bool
+	help
+	  Compile in platform device definition for USB EHCI
+
 config S5P_SETUP_MIPIPHY
 	bool
 	help

+ 1 - 0
arch/arm/plat-s5p/Makefile

@@ -33,4 +33,5 @@ obj-$(CONFIG_S5P_DEV_FIMC3)	+= dev-fimc3.o
 obj-$(CONFIG_S5P_DEV_ONENAND)	+= dev-onenand.o
 obj-$(CONFIG_S5P_DEV_CSIS0)	+= dev-csis0.o
 obj-$(CONFIG_S5P_DEV_CSIS1)	+= dev-csis1.o
+obj-$(CONFIG_S5P_DEV_USB_EHCI)	+= dev-ehci.o
 obj-$(CONFIG_S5P_SETUP_MIPIPHY)	+= setup-mipiphy.o

+ 57 - 0
arch/arm/plat-s5p/dev-ehci.c

@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2011 Samsung Electronics Co.Ltd
+ * 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/platform_device.h>
+#include <mach/irqs.h>
+#include <mach/map.h>
+#include <plat/devs.h>
+#include <plat/ehci.h>
+#include <plat/usb-phy.h>
+
+/* USB EHCI Host Controller registration */
+static struct resource s5p_ehci_resource[] = {
+	[0] = {
+		.start	= S5P_PA_EHCI,
+		.end	= S5P_PA_EHCI + SZ_256 - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= IRQ_USB_HOST,
+		.end	= IRQ_USB_HOST,
+		.flags	= IORESOURCE_IRQ,
+	}
+};
+
+static u64 s5p_device_ehci_dmamask = 0xffffffffUL;
+
+struct platform_device s5p_device_ehci = {
+	.name		= "s5p-ehci",
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(s5p_ehci_resource),
+	.resource	= s5p_ehci_resource,
+	.dev		= {
+		.dma_mask = &s5p_device_ehci_dmamask,
+		.coherent_dma_mask = 0xffffffffUL
+	}
+};
+
+void __init s5p_ehci_set_platdata(struct s5p_ehci_platdata *pd)
+{
+	struct s5p_ehci_platdata *npd;
+
+	npd = s3c_set_platdata(pd, sizeof(struct s5p_ehci_platdata),
+			&s5p_device_ehci);
+
+	if (!npd->phy_init)
+		npd->phy_init = s5p_usb_phy_init;
+	if (!npd->phy_exit)
+		npd->phy_exit = s5p_usb_phy_exit;
+}

+ 21 - 0
arch/arm/plat-s5p/include/plat/ehci.h

@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2011 Samsung Electronics Co.Ltd
+ * 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.
+ */
+
+#ifndef __PLAT_S5P_EHCI_H
+#define __PLAT_S5P_EHCI_H
+
+struct s5p_ehci_platdata {
+	int (*phy_init)(struct platform_device *pdev, int type);
+	int (*phy_exit)(struct platform_device *pdev, int type);
+};
+
+extern void s5p_ehci_set_platdata(struct s5p_ehci_platdata *pd);
+
+#endif /* __PLAT_S5P_EHCI_H */

+ 1 - 1
arch/arm/plat-s5p/include/plat/map-s5p.h

@@ -39,7 +39,7 @@
 #define S5P_VA_TWD		S5P_VA_COREPERI(0x600)
 #define S5P_VA_GIC_DIST		S5P_VA_COREPERI(0x1000)
 
-#define S3C_VA_USB_HSPHY	S3C_ADDR(0x02900000)
+#define S5P_VA_USB_HSPHY	S3C_ADDR(0x02900000)
 
 #define VA_VIC(x)		(S3C_VA_IRQ + ((x) * 0x10000))
 #define VA_VIC0			VA_VIC(0)

+ 22 - 0
arch/arm/plat-s5p/include/plat/usb-phy.h

@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2011 Samsung Electronics Co.Ltd
+ * 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.
+ */
+
+#ifndef __PLAT_S5P_USB_PHY_H
+#define __PLAT_S5P_USB_PHY_H
+
+enum s5p_usb_phy_type {
+	S5P_USB_PHY_DEVICE,
+	S5P_USB_PHY_HOST,
+};
+
+extern int s5p_usb_phy_init(struct platform_device *pdev, int type);
+extern int s5p_usb_phy_exit(struct platform_device *pdev, int type);
+
+#endif /* __PLAT_S5P_REGS_USB_PHY_H */

+ 2 - 0
arch/arm/plat-samsung/include/plat/devs.h

@@ -142,6 +142,8 @@ extern struct platform_device s5p_device_fimc3;
 extern struct platform_device s5p_device_mipi_csis0;
 extern struct platform_device s5p_device_mipi_csis1;
 
+extern struct platform_device s5p_device_ehci;
+
 extern struct platform_device exynos4_device_sysmmu;
 
 /* s3c2440 specific devices */

+ 5 - 0
arch/mips/ath79/Kconfig

@@ -26,12 +26,17 @@ config ATH79_MACH_PB44
 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
+	select USB_ARCH_HAS_EHCI
 	def_bool n
 
 config ATH79_DEV_AR913X_WMAC

+ 2 - 3
drivers/Makefile

@@ -64,11 +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_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/

+ 3 - 0
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 y if MICROBLAZE
 	default PCI
@@ -116,6 +117,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"

+ 5 - 0
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/

+ 189 - 277
drivers/usb/class/cdc-acm.c

@@ -7,35 +7,12 @@
  * 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
  *
  * 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
@@ -74,13 +51,7 @@
 #include "cdc-acm.h"
 
 
-#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_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;
@@ -94,12 +65,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.
  */
@@ -111,8 +76,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 +158,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 +179,8 @@ static int acm_write_start(struct acm *acm, int wbn)
 		return -ENODEV;
 	}
 
-	dbg("%s susp_count: %d", __func__, acm->susp_count);
+	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) {
 		if (!acm->delayed_wb)
@@ -287,10 +256,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 +275,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 +286,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 +295,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 +309,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;
@@ -340,166 +320,96 @@ 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", __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 */
-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;
+
+	if (!test_and_clear_bit(index, &acm->read_urbs_free))
+		return 0;
 
-	dbg("Entering acm_read_bulk with status %d", status);
+	dev_vdbg(&acm->data->dev, "%s - urb %d\n", __func__, index);
 
-	if (!ACM_READY(acm)) {
-		dev_dbg(&acm->data->dev, "Aborting, acm not ready");
-		return;
+	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, "bulk rx status %d\n", 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;
-
-	dbg("Entering acm_rx_tasklet");
 
-	if (!ACM_READY(acm)) {
-		dbg("acm_rx_tasklet: ACM not ready");
+	if (!urb->actual_length)
 		return;
-	}
-
-	spin_lock_irqsave(&acm->throttle_lock, flags);
-	throttled = acm->throttle;
-	spin_unlock_irqrestore(&acm->throttle_lock, flags);
-	if (throttled) {
-		dbg("acm_rx_tasklet: throttled");
-		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);
-
-	dbg("acm_rx_tasklet: procesing buf 0x%p, size = %d", 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);
-			dbg("Throttling noticed");
-			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);
-			dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", 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 */
@@ -509,9 +419,9 @@ 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, "tx %d/%d bytes -- > %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,
 			urb->status);
@@ -521,8 +431,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)
@@ -530,7 +438,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);
@@ -548,8 +457,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);
 
@@ -559,6 +466,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;
@@ -578,38 +487,28 @@ 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;
 	}
 
 	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);
 
-	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:
 	mutex_unlock(&open_mutex);
 	return rv;
 
-full_bailout:
-	usb_kill_urb(acm->ctrlurb);
 bail_out:
 	acm->port.count--;
 	mutex_unlock(&acm->mutex);
@@ -622,26 +521,24 @@ 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++)
-		usb_free_urb(acm->ru[i].urb);
+	for (i = 0; i < acm->rx_buflimit; i++)
+		usb_free_urb(acm->read_urbs[i]);
 	kfree(acm->country_codes);
 	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;
+	int i;
+
 	mutex_lock(&open_mutex);
 	if (acm->dev) {
 		usb_autopm_get_interface(acm->control);
@@ -649,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 < nr; i++)
-			usb_kill_urb(acm->ru[i].urb);
-		tasklet_enable(&acm->urb_task);
+		for (i = 0; i < acm->rx_buflimit; i++)
+			usb_kill_urb(acm->read_urbs[i]);
 		acm->control->needs_remote_wakeup = 0;
 		usb_autopm_put_interface(acm->control);
 	}
@@ -698,13 +593,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_vdbg(&acm->data->dev, "%s - count %d\n", __func__, count);
+
 	spin_lock_irqsave(&acm->write_lock, flags);
 	wbn = acm_wb_alloc(acm);
 	if (wbn < 0) {
@@ -714,7 +609,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_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);
@@ -751,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)
@@ -777,7 +681,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;
 }
 
@@ -872,7 +777,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);
@@ -897,11 +804,11 @@ 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);
+			  acm->read_buffers[i].base, acm->read_buffers[i].dma);
 }
 
 /* Little helper: write buffers allocate */
@@ -1133,7 +1040,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) {
@@ -1143,7 +1050,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;
 	}
 
@@ -1162,11 +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);
-	init_waitqueue_head(&acm->drain_wait);
-	spin_lock_init(&acm->throttle_lock);
 	spin_lock_init(&acm->write_lock);
 	spin_lock_init(&acm->read_lock);
 	mutex_init(&acm->mutex);
@@ -1179,53 +1082,69 @@ 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++) {
-		struct acm_ru *rcv = &(acm->ru[i]);
+		struct acm_rb *rb = &(acm->read_buffers[i]);
+		struct urb *urb;
 
-		rcv->urb = usb_alloc_urb(0, GFP_KERNEL);
-		if (rcv->urb == NULL) {
-			dev_dbg(&intf->dev,
-				"out of memory (read urbs usb_alloc_urb)\n");
+		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;
 		}
+		rb->index = i;
+		rb->instance = acm;
 
-		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_dbg(&intf->dev,
-				"out of memory (read bufs usb_alloc_coherent)\n");
-			goto alloc_fail7;
+		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);
 		}
+
+		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]);
 
 		snd->urb = usb_alloc_urb(0, GFP_KERNEL);
 		if (snd->urb == NULL) {
-			dev_dbg(&intf->dev,
-				"out of memory (write urbs usb_alloc_urb)");
-			goto alloc_fail8;
+			dev_err(&intf->dev,
+				"out of memory (write urbs usb_alloc_urb)\n");
+			goto alloc_fail7;
 		}
 
 		if (usb_endpoint_xfer_int(epwrite))
@@ -1244,7 +1163,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);
@@ -1296,14 +1215,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);
+		usb_free_urb(acm->read_urbs[i]);
+	acm_read_buffers_free(acm);
 	usb_free_urb(acm->ctrlurb);
 alloc_fail5:
 	acm_write_buffers_free(acm);
@@ -1318,17 +1236,14 @@ alloc_fail:
 static void stop_data_traffic(struct acm *acm)
 {
 	int i;
-	dbg("Entering stop_data_traffic");
 
-	tasklet_disable(&acm->urb_task);
+	dev_dbg(&acm->control->dev, "%s\n", __func__);
 
 	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);
 }
@@ -1389,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;
 	}
@@ -1455,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:
@@ -1716,8 +1629,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;
 }

+ 6 - 17
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,35 +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 */
-	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) */
 	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 */
 };

+ 11 - 11
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";
 

+ 0 - 8
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;

+ 18 - 0
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)

+ 17 - 43
drivers/usb/gadget/f_mass_storage.c

@@ -613,6 +613,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:
@@ -1584,37 +1589,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;
@@ -1702,6 +1676,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;
@@ -1710,24 +1688,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;
 
@@ -2800,6 +2773,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;

+ 12 - 41
drivers/usb/gadget/file_storage.c

@@ -1947,37 +1947,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;
@@ -2082,18 +2051,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;
 

+ 1 - 1
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 */

+ 9 - 0
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;
 }
 

+ 9 - 6
drivers/usb/gadget/storage_common.c

@@ -711,10 +711,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
@@ -738,10 +739,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)

+ 33 - 0
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
@@ -202,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
@@ -287,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)
@@ -444,6 +467,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

+ 202 - 0
drivers/usb/host/ehci-ath79.c

@@ -0,0 +1,202 @@
+/*
+ *  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->has_synopsys_hc_bug = 1;
+
+		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");

+ 10 - 0
drivers/usb/host/ehci-hcd.c

@@ -1265,6 +1265,16 @@ 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
+
+#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)

+ 1 - 1
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;

+ 4 - 0
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 */

+ 201 - 0
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");

+ 1 - 0
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)

+ 151 - 0
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");

+ 5 - 0
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) &&	\

+ 1 - 7
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

+ 1 - 2
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;

+ 10 - 16
drivers/usb/host/uhci-hcd.c

@@ -139,10 +139,7 @@ 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 +185,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 +353,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 +441,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 */
@@ -842,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 */

+ 1 - 2
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");

+ 8 - 1
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 */

+ 1 - 1
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)
 
 /*-------------------------------------------------------------------------*/
 

+ 15 - 0
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.

+ 9 - 0
drivers/usb/renesas_usbhs/Makefile

@@ -0,0 +1,9 @@
+#
+# for Renesas USB
+#
+
+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

+ 394 - 0
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>");

+ 225 - 0
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 */

+ 276 - 0
drivers/usb/renesas_usbhs/mod.c

@@ -0,0 +1,276 @@
+/*
+ * 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;
+
+	/*
+	 * 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) {
+		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);
+}
+
+/*
+ *		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);
+}

+ 122 - 0
drivers/usb/renesas_usbhs/mod.h

@@ -0,0 +1,122 @@
+/*
+ * 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);			\
+	})
+
+/*
+ * 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 */

+ 1341 - 0
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);
+}

+ 880 - 0
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);
+}

+ 105 - 0
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 */

+ 0 - 9
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

+ 0 - 1
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

+ 0 - 206
drivers/usb/serial/sam-ba.c

@@ -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");

+ 2 - 2
include/linux/usb/gadget.h

@@ -890,8 +890,8 @@ static inline void usb_free_descriptors(struct usb_descriptor_header **v)
 /* utility wrapping a simple endpoint selection policy */
 
 extern struct usb_ep *usb_ep_autoconfig(struct usb_gadget *,
-			struct usb_endpoint_descriptor *) __devinit;
+			struct usb_endpoint_descriptor *);
 
-extern void usb_ep_autoconfig_reset(struct usb_gadget *) __devinit;
+extern void usb_ep_autoconfig_reset(struct usb_gadget *);
 
 #endif /* __LINUX_USB_GADGET_H */

+ 149 - 0
include/linux/usb/renesas_usbhs.h

@@ -0,0 +1,149 @@
+/*
+ * Renesas USB
+ *
+ * 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_H
+#define RENESAS_USB_H
+#include <linux/platform_device.h>
+#include <linux/usb/ch9.h>
+
+/*
+ * module type
+ *
+ * it will be return value from get_id
+ */
+enum {
+	USBHS_HOST = 0,
+	USBHS_GADGET,
+	USBHS_MAX,
+};
+
+/*
+ * callback functions table for driver
+ *
+ * These functions are called from platform for driver.
+ * Callback function's pointer will be set before
+ * renesas_usbhs_platform_callback :: hardware_init was called
+ */
+struct renesas_usbhs_driver_callback {
+	int (*notify_hotplug)(struct platform_device *pdev);
+};
+
+/*
+ * callback functions for platform
+ *
+ * These functions are called from driver for platform
+ */
+struct renesas_usbhs_platform_callback {
+
+	/*
+	 * option:
+	 *
+	 * Hardware init function for platform.
+	 * it is called when driver was probed.
+	 */
+	int (*hardware_init)(struct platform_device *pdev);
+
+	/*
+	 * option:
+	 *
+	 * Hardware exit function for platform.
+	 * it is called when driver was removed
+	 */
+	void (*hardware_exit)(struct platform_device *pdev);
+
+	/*
+	 * option:
+	 *
+	 * Phy reset for platform
+	 */
+	void (*phy_reset)(struct platform_device *pdev);
+
+	/*
+	 * get USB ID function
+	 *  - USBHS_HOST
+	 *  - USBHS_GADGET
+	 */
+	int (*get_id)(struct platform_device *pdev);
+
+	/*
+	 * get VBUS status function.
+	 */
+	int (*get_vbus)(struct platform_device *pdev);
+};
+
+/*
+ * parameters for renesas usbhs
+ *
+ * some register needs USB chip specific parameters.
+ * This struct show it to driver
+ */
+struct renesas_usbhs_driver_param {
+	/*
+	 * pipe settings
+	 */
+	u32 *pipe_type; /* array of USB_ENDPOINT_XFER_xxx (from ep0) */
+	int pipe_size; /* pipe_type array size */
+
+	/*
+	 * option:
+	 *
+	 * for BUSWAIT :: BWAIT
+	 * */
+	int buswait_bwait;
+};
+
+/*
+ * option:
+ *
+ * platform information for renesas_usbhs driver.
+ */
+struct renesas_usbhs_platform_info {
+	/*
+	 * option:
+	 *
+	 * platform set these functions before
+	 * call platform_add_devices if needed
+	 */
+	struct renesas_usbhs_platform_callback	platform_callback;
+
+	/*
+	 * driver set these callback functions pointer.
+	 * platform can use it on callback functions
+	 */
+	struct renesas_usbhs_driver_callback	driver_callback;
+
+	/*
+	 * option:
+	 *
+	 * driver use these param for some register
+	 */
+	struct renesas_usbhs_driver_param	driver_param;
+};
+
+/*
+ * macro for platform
+ */
+#define renesas_usbhs_get_info(pdev)\
+	((struct renesas_usbhs_platform_info *)(pdev)->dev.platform_data)
+
+#define renesas_usbhs_call_notify_hotplug(pdev)				\
+	({								\
+		struct renesas_usbhs_driver_callback *dc;		\
+		dc = &(renesas_usbhs_get_info(pdev)->driver_callback);	\
+		if (dc)							\
+			dc->notify_hotplug(pdev);			\
+	})
+#endif /* RENESAS_USB_H */