aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-exynos4/Kconfig1
-rw-r--r--arch/arm/mach-exynos4/Makefile2
-rw-r--r--arch/arm/mach-exynos4/cpu.c7
-rw-r--r--arch/arm/mach-exynos4/include/mach/map.h4
-rw-r--r--arch/arm/mach-exynos4/include/mach/regs-pmu.h3
-rw-r--r--arch/arm/mach-exynos4/include/mach/regs-usb-phy.h64
-rw-r--r--arch/arm/mach-exynos4/mach-nuri.c16
-rw-r--r--arch/arm/mach-exynos4/usb-phy.c136
-rw-r--r--arch/arm/plat-s5p/Kconfig5
-rw-r--r--arch/arm/plat-s5p/Makefile1
-rw-r--r--arch/arm/plat-s5p/dev-ehci.c57
-rw-r--r--arch/arm/plat-s5p/include/plat/ehci.h21
-rw-r--r--arch/arm/plat-s5p/include/plat/map-s5p.h2
-rw-r--r--arch/arm/plat-s5p/include/plat/usb-phy.h22
-rw-r--r--arch/arm/plat-samsung/include/plat/devs.h2
-rw-r--r--arch/mips/ath79/Kconfig5
-rw-r--r--drivers/Makefile5
-rw-r--r--drivers/usb/Kconfig3
-rw-r--r--drivers/usb/Makefile5
-rw-r--r--drivers/usb/class/cdc-acm.c466
-rw-r--r--drivers/usb/class/cdc-acm.h23
-rw-r--r--drivers/usb/core/devices.c22
-rw-r--r--drivers/usb/core/file.c8
-rw-r--r--drivers/usb/gadget/Kconfig18
-rw-r--r--drivers/usb/gadget/f_mass_storage.c60
-rw-r--r--drivers/usb/gadget/file_storage.c53
-rw-r--r--drivers/usb/gadget/fsl_qe_udc.h2
-rw-r--r--drivers/usb/gadget/gadget_chips.h9
-rw-r--r--drivers/usb/gadget/storage_common.c15
-rw-r--r--drivers/usb/host/Kconfig33
-rw-r--r--drivers/usb/host/ehci-ath79.c202
-rw-r--r--drivers/usb/host/ehci-hcd.c10
-rw-r--r--drivers/usb/host/ehci-hub.c2
-rw-r--r--drivers/usb/host/ehci-q.c4
-rw-r--r--drivers/usb/host/ehci-s5p.c201
-rw-r--r--drivers/usb/host/ehci.h1
-rw-r--r--drivers/usb/host/ohci-ath79.c151
-rw-r--r--drivers/usb/host/ohci-hcd.c5
-rw-r--r--drivers/usb/host/sl811-hcd.c8
-rw-r--r--drivers/usb/host/u132-hcd.c3
-rw-r--r--drivers/usb/host/uhci-hcd.c26
-rw-r--r--drivers/usb/misc/ftdi-elan.c3
-rw-r--r--drivers/usb/otg/twl4030-usb.c9
-rw-r--r--drivers/usb/otg/twl6030-usb.c2
-rw-r--r--drivers/usb/renesas_usbhs/Kconfig15
-rw-r--r--drivers/usb/renesas_usbhs/Makefile9
-rw-r--r--drivers/usb/renesas_usbhs/common.c394
-rw-r--r--drivers/usb/renesas_usbhs/common.h225
-rw-r--r--drivers/usb/renesas_usbhs/mod.c276
-rw-r--r--drivers/usb/renesas_usbhs/mod.h122
-rw-r--r--drivers/usb/renesas_usbhs/mod_gadget.c1341
-rw-r--r--drivers/usb/renesas_usbhs/pipe.c880
-rw-r--r--drivers/usb/renesas_usbhs/pipe.h105
-rw-r--r--drivers/usb/serial/Kconfig9
-rw-r--r--drivers/usb/serial/Makefile1
-rw-r--r--drivers/usb/serial/sam-ba.c206
-rw-r--r--include/linux/usb/gadget.h4
-rw-r--r--include/linux/usb/renesas_usbhs.h149
58 files changed, 4776 insertions, 657 deletions
diff --git a/arch/arm/mach-exynos4/Kconfig b/arch/arm/mach-exynos4/Kconfig
index e849f67be47..805196207ce 100644
--- a/arch/arm/mach-exynos4/Kconfig
+++ b/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
diff --git a/arch/arm/mach-exynos4/Makefile b/arch/arm/mach-exynos4/Makefile
index 9be104f63c0..777897551e4 100644
--- a/arch/arm/mach-exynos4/Makefile
+++ b/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
diff --git a/arch/arm/mach-exynos4/cpu.c b/arch/arm/mach-exynos4/cpu.c
index 79301139194..08813a6f66b 100644
--- a/arch/arm/mach-exynos4/cpu.c
+++ b/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)
diff --git a/arch/arm/mach-exynos4/include/mach/map.h b/arch/arm/mach-exynos4/include/mach/map.h
index 6330b73b9ea..0009e77a05f 100644
--- a/arch/arm/mach-exynos4/include/mach/map.h
+++ b/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
diff --git a/arch/arm/mach-exynos4/include/mach/regs-pmu.h b/arch/arm/mach-exynos4/include/mach/regs-pmu.h
index 62b0014d05e..a9643371f8e 100644
--- a/arch/arm/mach-exynos4/include/mach/regs-pmu.h
+++ b/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)
diff --git a/arch/arm/mach-exynos4/include/mach/regs-usb-phy.h b/arch/arm/mach-exynos4/include/mach/regs-usb-phy.h
new file mode 100644
index 00000000000..703118d5173
--- /dev/null
+++ b/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 */
diff --git a/arch/arm/mach-exynos4/mach-nuri.c b/arch/arm/mach-exynos4/mach-nuri.c
index b79ad010d19..bb5d12f43af 100644
--- a/arch/arm/mach-exynos4/mach-nuri.c
+++ b/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));
}
diff --git a/arch/arm/mach-exynos4/usb-phy.c b/arch/arm/mach-exynos4/usb-phy.c
new file mode 100644
index 00000000000..0883c1b824b
--- /dev/null
+++ b/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;
+}
diff --git a/arch/arm/plat-s5p/Kconfig b/arch/arm/plat-s5p/Kconfig
index 84922971658..6751bcf7b88 100644
--- a/arch/arm/plat-s5p/Kconfig
+++ b/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
diff --git a/arch/arm/plat-s5p/Makefile b/arch/arm/plat-s5p/Makefile
index 42afff7f60b..e234cc4d49a 100644
--- a/arch/arm/plat-s5p/Makefile
+++ b/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
diff --git a/arch/arm/plat-s5p/dev-ehci.c b/arch/arm/plat-s5p/dev-ehci.c
new file mode 100644
index 00000000000..94080fff9e9
--- /dev/null
+++ b/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;
+}
diff --git a/arch/arm/plat-s5p/include/plat/ehci.h b/arch/arm/plat-s5p/include/plat/ehci.h
new file mode 100644
index 00000000000..6ae6810c756
--- /dev/null
+++ b/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 */
diff --git a/arch/arm/plat-s5p/include/plat/map-s5p.h b/arch/arm/plat-s5p/include/plat/map-s5p.h
index d973d39666a..a6c3d327ce7 100644
--- a/arch/arm/plat-s5p/include/plat/map-s5p.h
+++ b/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)
diff --git a/arch/arm/plat-s5p/include/plat/usb-phy.h b/arch/arm/plat-s5p/include/plat/usb-phy.h
new file mode 100644
index 00000000000..6dd6bcfca3c
--- /dev/null
+++ b/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 */
diff --git a/arch/arm/plat-samsung/include/plat/devs.h b/arch/arm/plat-samsung/include/plat/devs.h
index f0da6b70fba..3f38debbb10 100644
--- a/arch/arm/plat-samsung/include/plat/devs.h
+++ b/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 */
diff --git a/arch/mips/ath79/Kconfig b/arch/mips/ath79/Kconfig
index b05828260f7..47707410582 100644
--- a/arch/mips/ath79/Kconfig
+++ b/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
diff --git a/drivers/Makefile b/drivers/Makefile
index 3f135b6fb01..0cf73a90fca 100644
--- a/drivers/Makefile
+++ b/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/
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 006489d82dc..a1b3750cadb 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -65,6 +65,7 @@ config USB_ARCH_HAS_EHCI
default y if ARCH_CNS3XXX
default y if ARCH_VT8500
default y if PLAT_SPEAR
+ default y if PLAT_S5P
default y if ARCH_MSM
default 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"
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 239f050efa3..9bc8aeb3c96 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -45,3 +45,8 @@ obj-$(CONFIG_EARLY_PRINTK_DBGP) += early/
obj-$(CONFIG_USB_ATM) += atm/
obj-$(CONFIG_USB_SPEEDTOUCH) += atm/
+
+obj-$(CONFIG_USB_MUSB_HDRC) += musb/
+obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs/
+obj-$(CONFIG_USB_OTG_UTILS) += otg/
+obj-$(CONFIG_USB_GADGET) += gadget/
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index e057e538146..58754b508a0 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/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;
}
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
index b4ea54dbf32..7b5c0bd07f8 100644
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -72,16 +72,10 @@ struct acm_wb {
};
struct acm_rb {
- struct list_head list;
int size;
unsigned char *base;
dma_addr_t dma;
-};
-
-struct acm_ru {
- struct list_head list;
- struct acm_rb *buffer;
- struct urb *urb;
+ int index;
struct acm *instance;
};
@@ -97,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 */
};
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index 96fdfb815f8..0149c0976e9 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -64,49 +64,49 @@
/* Define ALLOW_SERIAL_NUMBER if you want to see the serial number of devices */
#define ALLOW_SERIAL_NUMBER
-static const char *format_topo =
+static const char format_topo[] =
/* T: Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=dddd MxCh=dd */
"\nT: Bus=%2.2d Lev=%2.2d Prnt=%2.2d Port=%2.2d Cnt=%2.2d Dev#=%3d Spd=%-4s MxCh=%2d\n";
-static const char *format_string_manufacturer =
+static const char format_string_manufacturer[] =
/* S: Manufacturer=xxxx */
"S: Manufacturer=%.100s\n";
-static const char *format_string_product =
+static const char format_string_product[] =
/* S: Product=xxxx */
"S: Product=%.100s\n";
#ifdef ALLOW_SERIAL_NUMBER
-static const char *format_string_serialnumber =
+static const char format_string_serialnumber[] =
/* S: SerialNumber=xxxx */
"S: SerialNumber=%.100s\n";
#endif
-static const char *format_bandwidth =
+static const char format_bandwidth[] =
/* B: Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd */
"B: Alloc=%3d/%3d us (%2d%%), #Int=%3d, #Iso=%3d\n";
-static const char *format_device1 =
+static const char format_device1[] =
/* D: Ver=xx.xx Cls=xx(sssss) Sub=xx Prot=xx MxPS=dd #Cfgs=dd */
"D: Ver=%2x.%02x Cls=%02x(%-5s) Sub=%02x Prot=%02x MxPS=%2d #Cfgs=%3d\n";
-static const char *format_device2 =
+static const char format_device2[] =
/* P: Vendor=xxxx ProdID=xxxx Rev=xx.xx */
"P: Vendor=%04x ProdID=%04x Rev=%2x.%02x\n";
-static const char *format_config =
+static const char format_config[] =
/* C: #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA */
"C:%c #Ifs=%2d Cfg#=%2d Atr=%02x MxPwr=%3dmA\n";
-static const char *format_iad =
+static const char format_iad[] =
/* A: FirstIf#=dd IfCount=dd Cls=xx(sssss) Sub=xx Prot=xx */
"A: FirstIf#=%2d IfCount=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x\n";
-static const char *format_iface =
+static const char format_iface[] =
/* I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=xxxx*/
"I:%c If#=%2d Alt=%2d #EPs=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x Driver=%s\n";
-static const char *format_endpt =
+static const char format_endpt[] =
/* E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=D?s */
"E: Ad=%02x(%c) Atr=%02x(%-4s) MxPS=%4d Ivl=%d%cs\n";
diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c
index cf6a5423de0..99458c843d6 100644
--- a/drivers/usb/core/file.c
+++ b/drivers/usb/core/file.c
@@ -236,13 +236,6 @@ EXPORT_SYMBOL_GPL(usb_register_dev);
void usb_deregister_dev(struct usb_interface *intf,
struct usb_class_driver *class_driver)
{
- int minor_base = class_driver->minor_base;
- char name[20];
-
-#ifdef CONFIG_USB_DYNAMIC_MINORS
- minor_base = 0;
-#endif
-
if (intf->minor == -1)
return;
@@ -252,7 +245,6 @@ void usb_deregister_dev(struct usb_interface *intf,
usb_minors[intf->minor] = NULL;
up_write(&minor_rwsem);
- snprintf(name, sizeof(name), class_driver->name, intf->minor - minor_base);
device_destroy(usb_class->class, MKDEV(USB_MAJOR, intf->minor));
intf->usb_dev = NULL;
intf->minor = -1;
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index bc5123cf41c..4c02b9f1597 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -260,6 +260,24 @@ config USB_R8A66597
default USB_GADGET
select USB_GADGET_SELECTED
+config USB_GADGET_RENESAS_USBHS
+ boolean "Renesas USBHS"
+ depends on USB_RENESAS_USBHS
+ select USB_GADGET_DUALSPEED
+ help
+ Renesas USBHS is a discrete USB host and peripheral controller
+ chip that supports both full and high speed USB 2.0 data transfers.
+ platform is able to configure endpoint (pipe) style
+
+ Say "y" to enable the gadget specific portion of the USBHS driver.
+
+
+config USB_RENESAS_USBHS_UDC
+ tristate
+ depends on USB_GADGET_RENESAS_USBHS
+ default USB_GADGET
+ select USB_GADGET_SELECTED
+
config USB_GADGET_PXA27X
boolean "PXA 27x"
depends on ARCH_PXA && (PXA27x || PXA3xx)
diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c
index 6d8e533949e..01ae27b60d4 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/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;
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
index a6eacb59571..fcfc77c7ad7 100644
--- a/drivers/usb/gadget/file_storage.c
+++ b/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;
diff --git a/drivers/usb/gadget/fsl_qe_udc.h b/drivers/usb/gadget/fsl_qe_udc.h
index e35e24fd64b..1da5fb03d21 100644
--- a/drivers/usb/gadget/fsl_qe_udc.h
+++ b/drivers/usb/gadget/fsl_qe_udc.h
@@ -207,7 +207,7 @@ struct qe_frame{
/* Frame status field */
/* Receive side */
-#define FRAME_OK 0x00000000 /* Frame tranmitted or received OK */
+#define FRAME_OK 0x00000000 /* Frame transmitted or received OK */
#define FRAME_ERROR 0x80000000 /* Error occurred on frame */
#define START_FRAME_LOST 0x40000000 /* START_FRAME_LOST */
#define END_FRAME_LOST 0x20000000 /* END_FRAME_LOST */
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index e896f6359df..ec3fd979c71 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -148,6 +148,12 @@
#define gadget_is_ci13xxx_msm(g) 0
#endif
+#ifdef CONFIG_USB_GADGET_RENESAS_USBHS
+#define gadget_is_renesas_usbhs(g) (!strcmp("renesas_usbhs_udc", (g)->name))
+#else
+#define gadget_is_renesas_usbhs(g) 0
+#endif
+
/**
* usb_gadget_controller_number - support bcdDevice id convention
* @gadget: the controller being driven
@@ -207,6 +213,9 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
return 0x27;
else if (gadget_is_ci13xxx_msm(gadget))
return 0x28;
+ else if (gadget_is_renesas_usbhs(gadget))
+ return 0x29;
+
return -ENOENT;
}
diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c
index b015561fd60..109635a8488 100644
--- a/drivers/usb/gadget/storage_common.c
+++ b/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)
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index e0e0787b724..fe4beca0000 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -188,6 +188,12 @@ config USB_EHCI_SH
Enables support for the on-chip EHCI controller on the SuperH.
If you use the PCI EHCI controller, this option is not necessary.
+config USB_EHCI_S5P
+ boolean "S5P EHCI support"
+ depends on USB_EHCI_HCD && PLAT_S5P
+ help
+ Enable support for the S5P SOC's on-chip EHCI controller.
+
config USB_W90X900_EHCI
bool "W90X900(W90P910) EHCI support"
depends on USB_EHCI_HCD && ARCH_W90X900
@@ -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
diff --git a/drivers/usb/host/ehci-ath79.c b/drivers/usb/host/ehci-ath79.c
new file mode 100644
index 00000000000..7ea23b50f5d
--- /dev/null
+++ b/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");
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 78561d112c0..83b7d5f02a1 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/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)
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index d05ea03cfb4..1a21799195a 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -127,7 +127,7 @@ static int ehci_port_change(struct ehci_hcd *ehci)
return 0;
}
-static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
+static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
bool suspending, bool do_wakeup)
{
int port;
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 42abd0f603b..a46d6a1388c 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -1183,6 +1183,10 @@ static void end_unlink_async (struct ehci_hcd *ehci)
ehci->reclaim = NULL;
start_unlink_async (ehci, next);
}
+
+ if (ehci->has_synopsys_hc_bug)
+ ehci_writel(ehci, (u32) ehci->async->qh_dma,
+ &ehci->regs->async_next);
}
/* makes sure the async qh will become idle */
diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c
new file mode 100644
index 00000000000..0c18f280bf4
--- /dev/null
+++ b/drivers/usb/host/ehci-s5p.c
@@ -0,0 +1,201 @@
+/*
+ * SAMSUNG S5P USB HOST EHCI Controller
+ *
+ * Copyright (C) 2011 Samsung Electronics Co.Ltd
+ * Author: Jingoo Han <jg1.han@samsung.com>
+ * Author: Joonyoung Shim <jy0922.shim@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <mach/regs-pmu.h>
+#include <plat/cpu.h>
+#include <plat/ehci.h>
+#include <plat/usb-phy.h>
+
+struct s5p_ehci_hcd {
+ struct device *dev;
+ struct usb_hcd *hcd;
+ struct clk *clk;
+};
+
+static const struct hc_driver s5p_ehci_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "S5P EHCI Host Controller",
+ .hcd_priv_size = sizeof(struct ehci_hcd),
+
+ .irq = ehci_irq,
+ .flags = HCD_MEMORY | HCD_USB2,
+
+ .reset = ehci_init,
+ .start = ehci_run,
+ .stop = ehci_stop,
+ .shutdown = ehci_shutdown,
+
+ .get_frame_number = ehci_get_frame,
+
+ .urb_enqueue = ehci_urb_enqueue,
+ .urb_dequeue = ehci_urb_dequeue,
+ .endpoint_disable = ehci_endpoint_disable,
+ .endpoint_reset = ehci_endpoint_reset,
+
+ .hub_status_data = ehci_hub_status_data,
+ .hub_control = ehci_hub_control,
+ .bus_suspend = ehci_bus_suspend,
+ .bus_resume = ehci_bus_resume,
+
+ .relinquish_port = ehci_relinquish_port,
+ .port_handed_over = ehci_port_handed_over,
+
+ .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
+};
+
+static int s5p_ehci_probe(struct platform_device *pdev)
+{
+ struct s5p_ehci_platdata *pdata;
+ struct s5p_ehci_hcd *s5p_ehci;
+ struct usb_hcd *hcd;
+ struct ehci_hcd *ehci;
+ struct resource *res;
+ int irq;
+ int err;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "No platform data defined\n");
+ return -EINVAL;
+ }
+
+ s5p_ehci = kzalloc(sizeof(struct s5p_ehci_hcd), GFP_KERNEL);
+ if (!s5p_ehci)
+ return -ENOMEM;
+
+ s5p_ehci->dev = &pdev->dev;
+
+ hcd = usb_create_hcd(&s5p_ehci_hc_driver, &pdev->dev,
+ dev_name(&pdev->dev));
+ if (!hcd) {
+ dev_err(&pdev->dev, "Unable to create HCD\n");
+ err = -ENOMEM;
+ goto fail_hcd;
+ }
+
+ s5p_ehci->clk = clk_get(&pdev->dev, "usbhost");
+
+ if (IS_ERR(s5p_ehci->clk)) {
+ dev_err(&pdev->dev, "Failed to get usbhost clock\n");
+ err = PTR_ERR(s5p_ehci->clk);
+ goto fail_clk;
+ }
+
+ err = clk_enable(s5p_ehci->clk);
+ if (err)
+ goto fail_clken;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "Failed to get I/O memory\n");
+ err = -ENXIO;
+ goto fail_io;
+ }
+
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+ hcd->regs = ioremap(res->start, resource_size(res));
+ if (!hcd->regs) {
+ dev_err(&pdev->dev, "Failed to remap I/O memory\n");
+ err = -ENOMEM;
+ goto fail_io;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (!irq) {
+ dev_err(&pdev->dev, "Failed to get IRQ\n");
+ err = -ENODEV;
+ goto fail;
+ }
+
+ if (pdata->phy_init)
+ pdata->phy_init(pdev, S5P_USB_PHY_HOST);
+
+ ehci = hcd_to_ehci(hcd);
+ ehci->caps = hcd->regs;
+ ehci->regs = hcd->regs + HC_LENGTH(readl(&ehci->caps->hc_capbase));
+
+ dbg_hcs_params(ehci, "reset");
+ dbg_hcc_params(ehci, "reset");
+
+ /* cache this readonly data; minimize chip reads */
+ ehci->hcs_params = readl(&ehci->caps->hcs_params);
+
+ err = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
+ if (err) {
+ dev_err(&pdev->dev, "Failed to add USB HCD\n");
+ goto fail;
+ }
+
+ platform_set_drvdata(pdev, s5p_ehci);
+
+ return 0;
+
+fail:
+ iounmap(hcd->regs);
+fail_io:
+ clk_disable(s5p_ehci->clk);
+fail_clken:
+ clk_put(s5p_ehci->clk);
+fail_clk:
+ usb_put_hcd(hcd);
+fail_hcd:
+ kfree(s5p_ehci);
+ return err;
+}
+
+static int s5p_ehci_remove(struct platform_device *pdev)
+{
+ struct s5p_ehci_platdata *pdata = pdev->dev.platform_data;
+ struct s5p_ehci_hcd *s5p_ehci = platform_get_drvdata(pdev);
+ struct usb_hcd *hcd = s5p_ehci->hcd;
+
+ usb_remove_hcd(hcd);
+
+ if (pdata && pdata->phy_exit)
+ pdata->phy_exit(pdev, S5P_USB_PHY_HOST);
+
+ iounmap(hcd->regs);
+
+ clk_disable(s5p_ehci->clk);
+ clk_put(s5p_ehci->clk);
+
+ usb_put_hcd(hcd);
+ kfree(s5p_ehci);
+
+ return 0;
+}
+
+static void s5p_ehci_shutdown(struct platform_device *pdev)
+{
+ struct s5p_ehci_hcd *s5p_ehci = platform_get_drvdata(pdev);
+ struct usb_hcd *hcd = s5p_ehci->hcd;
+
+ if (hcd->driver->shutdown)
+ hcd->driver->shutdown(hcd);
+}
+
+static struct platform_driver s5p_ehci_driver = {
+ .probe = s5p_ehci_probe,
+ .remove = s5p_ehci_remove,
+ .shutdown = s5p_ehci_shutdown,
+ .driver = {
+ .name = "s5p-ehci",
+ .owner = THIS_MODULE,
+ }
+};
+
+MODULE_ALIAS("platform:s5p-ehci");
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 333ddc15691..168f1a88c4d 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -134,6 +134,7 @@ struct ehci_hcd { /* one per controller */
unsigned amd_pll_fix:1;
unsigned fs_i_thresh:1; /* Intel iso scheduling */
unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/
+ unsigned has_synopsys_hc_bug:1; /* Synopsys HC */
/* required for usb32 quirk */
#define OHCI_CTRL_HCFS (3 << 6)
diff --git a/drivers/usb/host/ohci-ath79.c b/drivers/usb/host/ohci-ath79.c
new file mode 100644
index 00000000000..ffea3e7cb0a
--- /dev/null
+++ b/drivers/usb/host/ohci-ath79.c
@@ -0,0 +1,151 @@
+/*
+ * OHCI HCD (Host Controller Driver) for USB.
+ *
+ * Bus Glue for Atheros AR71XX/AR724X built-in OHCI controller.
+ *
+ * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
+ * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ * Parts of this file are based on Atheros' 2.6.15 BSP
+ * Copyright (C) 2007 Atheros Communications, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ */
+
+#include <linux/platform_device.h>
+
+static int __devinit ohci_ath79_start(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ int ret;
+
+ ret = ohci_init(ohci);
+ if (ret < 0)
+ return ret;
+
+ ret = ohci_run(ohci);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+
+err:
+ ohci_stop(hcd);
+ return ret;
+}
+
+static const struct hc_driver ohci_ath79_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "Atheros built-in OHCI controller",
+ .hcd_priv_size = sizeof(struct ohci_hcd),
+
+ .irq = ohci_irq,
+ .flags = HCD_USB11 | HCD_MEMORY,
+
+ .start = ohci_ath79_start,
+ .stop = ohci_stop,
+ .shutdown = ohci_shutdown,
+
+ .urb_enqueue = ohci_urb_enqueue,
+ .urb_dequeue = ohci_urb_dequeue,
+ .endpoint_disable = ohci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ohci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
+ .start_port_reset = ohci_start_port_reset,
+};
+
+static int ohci_ath79_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+ struct resource *res;
+ int irq;
+ int ret;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ dev_dbg(&pdev->dev, "no IRQ specified\n");
+ return -ENODEV;
+ }
+ irq = res->start;
+
+ hcd = usb_create_hcd(&ohci_ath79_hc_driver, &pdev->dev,
+ dev_name(&pdev->dev));
+ if (!hcd)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_dbg(&pdev->dev, "no base address specified\n");
+ ret = -ENODEV;
+ goto err_put_hcd;
+ }
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = res->end - res->start + 1;
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
+ dev_dbg(&pdev->dev, "controller already in use\n");
+ ret = -EBUSY;
+ goto err_put_hcd;
+ }
+
+ hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ if (!hcd->regs) {
+ dev_dbg(&pdev->dev, "error mapping memory\n");
+ ret = -EFAULT;
+ goto err_release_region;
+ }
+
+ ohci_hcd_init(hcd_to_ohci(hcd));
+
+ ret = usb_add_hcd(hcd, irq, IRQF_DISABLED);
+ if (ret)
+ goto err_stop_hcd;
+
+ return 0;
+
+err_stop_hcd:
+ iounmap(hcd->regs);
+err_release_region:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+err_put_hcd:
+ usb_put_hcd(hcd);
+ return ret;
+}
+
+static int ohci_ath79_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ usb_remove_hcd(hcd);
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+
+ return 0;
+}
+
+static struct platform_driver ohci_hcd_ath79_driver = {
+ .probe = ohci_ath79_probe,
+ .remove = ohci_ath79_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver = {
+ .name = "ath79-ohci",
+ .owner = THIS_MODULE,
+ },
+};
+
+MODULE_ALIAS(PLATFORM_MODULE_PREFIX "ath79-ohci");
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index d5572351486..8c8dc6559ac 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -1105,6 +1105,11 @@ MODULE_LICENSE ("GPL");
#define PLATFORM_DRIVER ohci_hcd_cns3xxx_driver
#endif
+#ifdef CONFIG_USB_OHCI_ATH79
+#include "ohci-ath79.c"
+#define PLATFORM_DRIVER ohci_hcd_ath79_driver
+#endif
+
#if !defined(PCI_DRIVER) && \
!defined(PLATFORM_DRIVER) && \
!defined(OMAP1_PLATFORM_DRIVER) && \
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
index 18b7099a812..5adcba016fc 100644
--- a/drivers/usb/host/sl811-hcd.c
+++ b/drivers/usb/host/sl811-hcd.c
@@ -71,12 +71,6 @@ MODULE_ALIAS("platform:sl811-hcd");
/* for now, use only one transfer register bank */
#undef USE_B
-/* this doesn't understand urb->iso_frame_desc[], but if you had a driver
- * that just queued one ISO frame per URB then iso transfers "should" work
- * using the normal urb status fields.
- */
-#define DISABLE_ISO
-
// #define QUIRK2
#define QUIRK3
@@ -807,7 +801,7 @@ static int sl811h_urb_enqueue(
int retval;
struct usb_host_endpoint *hep = urb->ep;
-#ifdef DISABLE_ISO
+#ifndef CONFIG_USB_SL811_HCD_ISO
if (type == PIPE_ISOCHRONOUS)
return -ENOSPC;
#endif
diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c
index b4785934e09..533d12cca37 100644
--- a/drivers/usb/host/u132-hcd.c
+++ b/drivers/usb/host/u132-hcd.c
@@ -3230,8 +3230,7 @@ static int __init u132_hcd_init(void)
mutex_init(&u132_module_lock);
if (usb_disabled())
return -ENODEV;
- printk(KERN_INFO "driver %s built at %s on %s\n", hcd_name, __TIME__,
- __DATE__);
+ printk(KERN_INFO "driver %s\n", hcd_name);
workqueue = create_singlethread_workqueue("u132");
retval = platform_driver_register(&u132_platform_driver);
return retval;
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index 4f65b14e5e0..83344d688ff 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/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 */
diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c
index 7839c98fa74..b16bd3ce391 100644
--- a/drivers/usb/misc/ftdi-elan.c
+++ b/drivers/usb/misc/ftdi-elan.c
@@ -2889,8 +2889,7 @@ static struct usb_driver ftdi_elan_driver = {
static int __init ftdi_elan_init(void)
{
int result;
- printk(KERN_INFO "driver %s built at %s on %s\n", ftdi_elan_driver.name,
- __TIME__, __DATE__);
+ printk(KERN_INFO "driver %s\n", ftdi_elan_driver.name);
mutex_init(&ftdi_module_lock);
INIT_LIST_HEAD(&ftdi_static_list);
status_queue = create_singlethread_workqueue("ftdi-status-control");
diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c
index e01b073cc48..efeb4d1517f 100644
--- a/drivers/usb/otg/twl4030-usb.c
+++ b/drivers/usb/otg/twl4030-usb.c
@@ -160,6 +160,7 @@ struct twl4030_usb {
int irq;
u8 linkstat;
+ bool vbus_supplied;
u8 asleep;
bool irq_enabled;
};
@@ -250,6 +251,8 @@ static enum usb_xceiv_events twl4030_usb_linkstat(struct twl4030_usb *twl)
int status;
int linkstat = USB_EVENT_NONE;
+ twl->vbus_supplied = false;
+
/*
* For ID/VBUS sensing, see manual section 15.4.8 ...
* except when using only battery backup power, two
@@ -265,6 +268,9 @@ static enum usb_xceiv_events twl4030_usb_linkstat(struct twl4030_usb *twl)
if (status < 0)
dev_err(twl->dev, "USB link status err %d\n", status);
else if (status & (BIT(7) | BIT(2))) {
+ if (status & (BIT(7)))
+ twl->vbus_supplied = true;
+
if (status & BIT(2))
linkstat = USB_EVENT_ID;
else
@@ -484,7 +490,7 @@ static ssize_t twl4030_usb_vbus_show(struct device *dev,
spin_lock_irqsave(&twl->lock, flags);
ret = sprintf(buf, "%s\n",
- (twl->linkstat == USB_EVENT_VBUS) ? "on" : "off");
+ twl->vbus_supplied ? "on" : "off");
spin_unlock_irqrestore(&twl->lock, flags);
return ret;
@@ -608,6 +614,7 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev)
twl->otg.set_peripheral = twl4030_set_peripheral;
twl->otg.set_suspend = twl4030_set_suspend;
twl->usb_mode = pdata->usb_mode;
+ twl->vbus_supplied = false;
twl->asleep = 1;
/* init spinlock for workqueue */
diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c
index 8a91b4b832a..6e920de64ef 100644
--- a/drivers/usb/otg/twl6030-usb.c
+++ b/drivers/usb/otg/twl6030-usb.c
@@ -101,7 +101,7 @@ struct twl6030_usb {
bool irq_enabled;
};
-#define xceiv_to_twl(x) container_of((x), struct twl6030_usb, otg);
+#define xceiv_to_twl(x) container_of((x), struct twl6030_usb, otg)
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/renesas_usbhs/Kconfig b/drivers/usb/renesas_usbhs/Kconfig
new file mode 100644
index 00000000000..481490e5500
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/Kconfig
@@ -0,0 +1,15 @@
+#
+# Renesas USB Controller Drivers
+#
+
+config USB_RENESAS_USBHS
+ tristate 'Renesas USBHS controller'
+ default n
+ help
+ Renesas USBHS is a discrete USB host and peripheral controller chip
+ that supports both full and high speed USB 2.0 data transfers.
+ It has nine or more configurable endpoints, and endpoint zero.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "renesas_usbhs" and force all
+ gadget drivers to also be dynamically linked.
diff --git a/drivers/usb/renesas_usbhs/Makefile b/drivers/usb/renesas_usbhs/Makefile
new file mode 100644
index 00000000000..b8798ad1627
--- /dev/null
+++ b/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
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
new file mode 100644
index 00000000000..d9ad60d1c15
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -0,0 +1,394 @@
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include "./common.h"
+
+/*
+ * platform call back
+ *
+ * renesas usb support platform callback function.
+ * Below macro call it.
+ * if platform doesn't have callback, it return 0 (no error)
+ */
+#define usbhs_platform_call(priv, func, args...)\
+ (!(priv) ? -ENODEV : \
+ !((priv)->pfunc->func) ? 0 : \
+ (priv)->pfunc->func(args))
+
+/*
+ * common functions
+ */
+u16 usbhs_read(struct usbhs_priv *priv, u32 reg)
+{
+ return ioread16(priv->base + reg);
+}
+
+void usbhs_write(struct usbhs_priv *priv, u32 reg, u16 data)
+{
+ iowrite16(data, priv->base + reg);
+}
+
+void usbhs_bset(struct usbhs_priv *priv, u32 reg, u16 mask, u16 data)
+{
+ u16 val = usbhs_read(priv, reg);
+
+ val &= ~mask;
+ val |= data & mask;
+
+ usbhs_write(priv, reg, val);
+}
+
+/*
+ * syscfg functions
+ */
+void usbhs_sys_clock_ctrl(struct usbhs_priv *priv, int enable)
+{
+ usbhs_bset(priv, SYSCFG, SCKE, enable ? SCKE : 0);
+}
+
+void usbhs_sys_hispeed_ctrl(struct usbhs_priv *priv, int enable)
+{
+ usbhs_bset(priv, SYSCFG, HSE, enable ? HSE : 0);
+}
+
+void usbhs_sys_usb_ctrl(struct usbhs_priv *priv, int enable)
+{
+ usbhs_bset(priv, SYSCFG, USBE, enable ? USBE : 0);
+}
+
+void usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable)
+{
+ u16 mask = DCFM | DRPD | DPRPU;
+ u16 val = DCFM | DRPD;
+
+ /*
+ * if enable
+ *
+ * - select Host mode
+ * - D+ Line/D- Line Pull-down
+ */
+ usbhs_bset(priv, SYSCFG, mask, enable ? val : 0);
+}
+
+void usbhs_sys_function_ctrl(struct usbhs_priv *priv, int enable)
+{
+ u16 mask = DCFM | DRPD | DPRPU;
+ u16 val = DPRPU;
+
+ /*
+ * if enable
+ *
+ * - select Function mode
+ * - D+ Line Pull-up
+ */
+ usbhs_bset(priv, SYSCFG, mask, enable ? val : 0);
+}
+
+/*
+ * frame functions
+ */
+int usbhs_frame_get_num(struct usbhs_priv *priv)
+{
+ return usbhs_read(priv, FRMNUM) & FRNM_MASK;
+}
+
+/*
+ * local functions
+ */
+static struct usbhs_priv *usbhsc_pdev_to_priv(struct platform_device *pdev)
+{
+ return dev_get_drvdata(&pdev->dev);
+}
+
+static void usbhsc_bus_ctrl(struct usbhs_priv *priv, int enable)
+{
+ int wait = usbhs_get_dparam(priv, buswait_bwait);
+ u16 data = 0;
+
+ if (enable) {
+ /* set bus wait if platform have */
+ if (wait)
+ usbhs_bset(priv, BUSWAIT, 0x000F, wait);
+ }
+ usbhs_write(priv, DVSTCTR, data);
+}
+
+/*
+ * platform default param
+ */
+static u32 usbhsc_default_pipe_type[] = {
+ USB_ENDPOINT_XFER_CONTROL,
+ USB_ENDPOINT_XFER_ISOC,
+ USB_ENDPOINT_XFER_ISOC,
+ USB_ENDPOINT_XFER_BULK,
+ USB_ENDPOINT_XFER_BULK,
+ USB_ENDPOINT_XFER_BULK,
+ USB_ENDPOINT_XFER_INT,
+ USB_ENDPOINT_XFER_INT,
+ USB_ENDPOINT_XFER_INT,
+ USB_ENDPOINT_XFER_INT,
+};
+
+/*
+ * driver callback functions
+ */
+static void usbhsc_notify_hotplug(struct work_struct *work)
+{
+ struct usbhs_priv *priv = container_of(work,
+ struct usbhs_priv,
+ notify_hotplug_work);
+ struct platform_device *pdev = usbhs_priv_to_pdev(priv);
+ struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+ int id;
+ int enable;
+ int ret;
+
+ /*
+ * get vbus status from platform
+ */
+ enable = usbhs_platform_call(priv, get_vbus, pdev);
+
+ /*
+ * get id from platform
+ */
+ id = usbhs_platform_call(priv, get_id, pdev);
+
+ if (enable && !mod) {
+ ret = usbhs_mod_change(priv, id);
+ if (ret < 0)
+ return;
+
+ dev_dbg(&pdev->dev, "%s enable\n", __func__);
+
+ /* enable PM */
+ pm_runtime_get_sync(&pdev->dev);
+
+ /* USB on */
+ usbhs_sys_clock_ctrl(priv, enable);
+ usbhsc_bus_ctrl(priv, enable);
+
+ /* module start */
+ usbhs_mod_call(priv, start, priv);
+
+ } else if (!enable && mod) {
+ dev_dbg(&pdev->dev, "%s disable\n", __func__);
+
+ /* module stop */
+ usbhs_mod_call(priv, stop, priv);
+
+ /* USB off */
+ usbhsc_bus_ctrl(priv, enable);
+ usbhs_sys_clock_ctrl(priv, enable);
+
+ /* disable PM */
+ pm_runtime_put_sync(&pdev->dev);
+
+ usbhs_mod_change(priv, -1);
+
+ /* reset phy for next connection */
+ usbhs_platform_call(priv, phy_reset, pdev);
+ }
+}
+
+static int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev)
+{
+ struct usbhs_priv *priv = usbhsc_pdev_to_priv(pdev);
+
+ /*
+ * This functions will be called in interrupt.
+ * To make sure safety context,
+ * use workqueue for usbhs_notify_hotplug
+ */
+ schedule_work(&priv->notify_hotplug_work);
+ return 0;
+}
+
+/*
+ * platform functions
+ */
+static int __devinit usbhs_probe(struct platform_device *pdev)
+{
+ struct renesas_usbhs_platform_info *info = pdev->dev.platform_data;
+ struct renesas_usbhs_driver_callback *dfunc;
+ struct usbhs_priv *priv;
+ struct resource *res;
+ unsigned int irq;
+ int ret;
+
+ /* check platform information */
+ if (!info ||
+ !info->platform_callback.get_id ||
+ !info->platform_callback.get_vbus) {
+ dev_err(&pdev->dev, "no platform information\n");
+ return -EINVAL;
+ }
+
+ /* platform data */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (!res || (int)irq <= 0) {
+ dev_err(&pdev->dev, "Not enough Renesas USB platform resources.\n");
+ return -ENODEV;
+ }
+
+ /* usb private data */
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&pdev->dev, "Could not allocate priv\n");
+ return -ENOMEM;
+ }
+
+ priv->base = ioremap_nocache(res->start, resource_size(res));
+ if (!priv->base) {
+ dev_err(&pdev->dev, "ioremap error.\n");
+ ret = -ENOMEM;
+ goto probe_end_kfree;
+ }
+
+ /*
+ * care platform info
+ */
+ priv->pfunc = &info->platform_callback;
+ priv->dparam = &info->driver_param;
+
+ /* set driver callback functions for platform */
+ dfunc = &info->driver_callback;
+ dfunc->notify_hotplug = usbhsc_drvcllbck_notify_hotplug;
+
+ /* set default param if platform doesn't have */
+ if (!priv->dparam->pipe_type) {
+ priv->dparam->pipe_type = usbhsc_default_pipe_type;
+ priv->dparam->pipe_size = ARRAY_SIZE(usbhsc_default_pipe_type);
+ }
+
+ /*
+ * priv settings
+ */
+ priv->irq = irq;
+ priv->pdev = pdev;
+ INIT_WORK(&priv->notify_hotplug_work, usbhsc_notify_hotplug);
+ spin_lock_init(usbhs_priv_to_lock(priv));
+
+ /* call pipe and module init */
+ ret = usbhs_pipe_probe(priv);
+ if (ret < 0)
+ goto probe_end_mod_exit;
+
+ ret = usbhs_mod_probe(priv);
+ if (ret < 0)
+ goto probe_end_iounmap;
+
+ /* dev_set_drvdata should be called after usbhs_mod_init */
+ dev_set_drvdata(&pdev->dev, priv);
+
+ /*
+ * deviece reset here because
+ * USB device might be used in boot loader.
+ */
+ usbhs_sys_clock_ctrl(priv, 0);
+
+ /*
+ * platform call
+ *
+ * USB phy setup might depend on CPU/Board.
+ * If platform has its callback functions,
+ * call it here.
+ */
+ ret = usbhs_platform_call(priv, hardware_init, pdev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "platform prove failed.\n");
+ goto probe_end_pipe_exit;
+ }
+
+ /* reset phy for connection */
+ usbhs_platform_call(priv, phy_reset, pdev);
+
+ /*
+ * manual call notify_hotplug for cold plug
+ */
+ pm_runtime_enable(&pdev->dev);
+ ret = usbhsc_drvcllbck_notify_hotplug(pdev);
+ if (ret < 0)
+ goto probe_end_call_remove;
+
+ dev_info(&pdev->dev, "probed\n");
+
+ return ret;
+
+probe_end_call_remove:
+ usbhs_platform_call(priv, hardware_exit, pdev);
+probe_end_pipe_exit:
+ usbhs_pipe_remove(priv);
+probe_end_mod_exit:
+ usbhs_mod_remove(priv);
+probe_end_iounmap:
+ iounmap(priv->base);
+probe_end_kfree:
+ kfree(priv);
+
+ dev_info(&pdev->dev, "probe failed\n");
+
+ return ret;
+}
+
+static int __devexit usbhs_remove(struct platform_device *pdev)
+{
+ struct usbhs_priv *priv = usbhsc_pdev_to_priv(pdev);
+
+ dev_dbg(&pdev->dev, "usb remove\n");
+
+ pm_runtime_disable(&pdev->dev);
+
+ usbhsc_bus_ctrl(priv, 0);
+
+ usbhs_platform_call(priv, hardware_exit, pdev);
+ usbhs_pipe_remove(priv);
+ usbhs_mod_remove(priv);
+ iounmap(priv->base);
+ kfree(priv);
+
+ return 0;
+}
+
+static struct platform_driver renesas_usbhs_driver = {
+ .driver = {
+ .name = "renesas_usbhs",
+ },
+ .probe = usbhs_probe,
+ .remove = __devexit_p(usbhs_remove),
+};
+
+static int __init usbhs_init(void)
+{
+ return platform_driver_register(&renesas_usbhs_driver);
+}
+
+static void __exit usbhs_exit(void)
+{
+ platform_driver_unregister(&renesas_usbhs_driver);
+}
+
+module_init(usbhs_init);
+module_exit(usbhs_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Renesas USB driver");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h
new file mode 100644
index 00000000000..f1a2b62f93f
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/common.h
@@ -0,0 +1,225 @@
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#ifndef RENESAS_USB_DRIVER_H
+#define RENESAS_USB_DRIVER_H
+
+#include <linux/platform_device.h>
+#include <linux/usb/renesas_usbhs.h>
+
+struct usbhs_priv;
+
+#include "./mod.h"
+#include "./pipe.h"
+
+/*
+ *
+ * register define
+ *
+ */
+#define SYSCFG 0x0000
+#define BUSWAIT 0x0002
+#define DVSTCTR 0x0008
+#define CFIFO 0x0014
+#define CFIFOSEL 0x0020
+#define CFIFOCTR 0x0022
+#define INTENB0 0x0030
+#define INTENB1 0x0032
+#define BRDYENB 0x0036
+#define NRDYENB 0x0038
+#define BEMPENB 0x003A
+#define INTSTS0 0x0040
+#define INTSTS1 0x0042
+#define BRDYSTS 0x0046
+#define NRDYSTS 0x0048
+#define BEMPSTS 0x004A
+#define FRMNUM 0x004C
+#define USBREQ 0x0054 /* USB request type register */
+#define USBVAL 0x0056 /* USB request value register */
+#define USBINDX 0x0058 /* USB request index register */
+#define USBLENG 0x005A /* USB request length register */
+#define DCPCFG 0x005C
+#define DCPMAXP 0x005E
+#define DCPCTR 0x0060
+#define PIPESEL 0x0064
+#define PIPECFG 0x0068
+#define PIPEBUF 0x006A
+#define PIPEMAXP 0x006C
+#define PIPEPERI 0x006E
+#define PIPEnCTR 0x0070
+
+/* SYSCFG */
+#define SCKE (1 << 10) /* USB Module Clock Enable */
+#define HSE (1 << 7) /* High-Speed Operation Enable */
+#define DCFM (1 << 6) /* Controller Function Select */
+#define DRPD (1 << 5) /* D+ Line/D- Line Resistance Control */
+#define DPRPU (1 << 4) /* D+ Line Resistance Control */
+#define USBE (1 << 0) /* USB Module Operation Enable */
+
+/* DVSTCTR */
+#define EXTLP (1 << 10) /* Controls the EXTLP pin output state */
+#define PWEN (1 << 9) /* Controls the PWEN pin output state */
+#define RHST (0x7) /* Reset Handshake */
+#define RHST_LOW_SPEED 1 /* Low-speed connection */
+#define RHST_FULL_SPEED 2 /* Full-speed connection */
+#define RHST_HIGH_SPEED 3 /* High-speed connection */
+
+/* CFIFOSEL */
+#define MBW_32 (0x2 << 10) /* CFIFO Port Access Bit Width */
+
+/* CFIFOCTR */
+#define BVAL (1 << 15) /* Buffer Memory Enable Flag */
+#define BCLR (1 << 14) /* CPU buffer clear */
+#define FRDY (1 << 13) /* FIFO Port Ready */
+#define DTLN_MASK (0x0FFF) /* Receive Data Length */
+
+/* INTENB0 */
+#define VBSE (1 << 15) /* Enable IRQ VBUS_0 and VBUSIN_0 */
+#define RSME (1 << 14) /* Enable IRQ Resume */
+#define SOFE (1 << 13) /* Enable IRQ Frame Number Update */
+#define DVSE (1 << 12) /* Enable IRQ Device State Transition */
+#define CTRE (1 << 11) /* Enable IRQ Control Stage Transition */
+#define BEMPE (1 << 10) /* Enable IRQ Buffer Empty */
+#define NRDYE (1 << 9) /* Enable IRQ Buffer Not Ready Response */
+#define BRDYE (1 << 8) /* Enable IRQ Buffer Ready */
+
+/* INTENB1 */
+#define BCHGE (1 << 14) /* USB Bus Change Interrupt Enable */
+#define DTCHE (1 << 12) /* Disconnection Detect Interrupt Enable */
+#define ATTCHE (1 << 11) /* Connection Detect Interrupt Enable */
+#define EOFERRE (1 << 6) /* EOF Error Detect Interrupt Enable */
+#define SIGNE (1 << 5) /* Setup Transaction Error Interrupt Enable */
+#define SACKE (1 << 4) /* Setup Transaction ACK Interrupt Enable */
+
+/* INTSTS0 */
+#define DVST (1 << 12) /* Device State Transition Interrupt Status */
+#define CTRT (1 << 11) /* Control Stage Interrupt Status */
+#define BEMP (1 << 10) /* Buffer Empty Interrupt Status */
+#define BRDY (1 << 8) /* Buffer Ready Interrupt Status */
+#define VBSTS (1 << 7) /* VBUS_0 and VBUSIN_0 Input Status */
+#define VALID (1 << 3) /* USB Request Receive */
+
+#define DVSQ_MASK (0x3 << 4) /* Device State */
+#define POWER_STATE (0 << 4)
+#define DEFAULT_STATE (1 << 4)
+#define ADDRESS_STATE (2 << 4)
+#define CONFIGURATION_STATE (3 << 4)
+
+#define CTSQ_MASK (0x7) /* Control Transfer Stage */
+#define IDLE_SETUP_STAGE 0 /* Idle stage or setup stage */
+#define READ_DATA_STAGE 1 /* Control read data stage */
+#define READ_STATUS_STAGE 2 /* Control read status stage */
+#define WRITE_DATA_STAGE 3 /* Control write data stage */
+#define WRITE_STATUS_STAGE 4 /* Control write status stage */
+#define NODATA_STATUS_STAGE 5 /* Control write NoData status stage */
+#define SEQUENCE_ERROR 6 /* Control transfer sequence error */
+
+/* PIPECFG */
+/* DCPCFG */
+#define TYPE_NONE (0 << 14) /* Transfer Type */
+#define TYPE_BULK (1 << 14)
+#define TYPE_INT (2 << 14)
+#define TYPE_ISO (3 << 14)
+#define DBLB (1 << 9) /* Double Buffer Mode */
+#define SHTNAK (1 << 7) /* Pipe Disable in Transfer End */
+#define DIR_OUT (1 << 4) /* Transfer Direction */
+
+/* PIPEMAXP */
+/* DCPMAXP */
+#define DEVSEL_MASK (0xF << 12) /* Device Select */
+#define DCP_MAXP_MASK (0x7F)
+#define PIPE_MAXP_MASK (0x7FF)
+
+/* PIPEBUF */
+#define BUFSIZE_SHIFT 10
+#define BUFSIZE_MASK (0x1F << BUFSIZE_SHIFT)
+#define BUFNMB_MASK (0xFF)
+
+/* PIPEnCTR */
+/* DCPCTR */
+#define BSTS (1 << 15) /* Buffer Status */
+#define CSSTS (1 << 12) /* CSSTS Status */
+#define SQCLR (1 << 8) /* Toggle Bit Clear */
+#define ACLRM (1 << 9) /* Buffer Auto-Clear Mode */
+#define PBUSY (1 << 5) /* Pipe Busy */
+#define PID_MASK (0x3) /* Response PID */
+#define PID_NAK 0
+#define PID_BUF 1
+#define PID_STALL10 2
+#define PID_STALL11 3
+
+#define CCPL (1 << 2) /* Control Transfer End Enable */
+
+/* FRMNUM */
+#define FRNM_MASK (0x7FF)
+
+/*
+ * struct
+ */
+struct usbhs_priv {
+
+ void __iomem *base;
+ unsigned int irq;
+
+ struct renesas_usbhs_platform_callback *pfunc;
+ struct renesas_usbhs_driver_param *dparam;
+
+ struct work_struct notify_hotplug_work;
+ struct platform_device *pdev;
+
+ spinlock_t lock;
+
+ /*
+ * module control
+ */
+ struct usbhs_mod_info mod_info;
+
+ /*
+ * pipe control
+ */
+ struct usbhs_pipe_info pipe_info;
+};
+
+/*
+ * common
+ */
+u16 usbhs_read(struct usbhs_priv *priv, u32 reg);
+void usbhs_write(struct usbhs_priv *priv, u32 reg, u16 data);
+void usbhs_bset(struct usbhs_priv *priv, u32 reg, u16 mask, u16 data);
+
+/*
+ * sysconfig
+ */
+void usbhs_sys_clock_ctrl(struct usbhs_priv *priv, int enable);
+void usbhs_sys_hispeed_ctrl(struct usbhs_priv *priv, int enable);
+void usbhs_sys_usb_ctrl(struct usbhs_priv *priv, int enable);
+void usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable);
+void usbhs_sys_function_ctrl(struct usbhs_priv *priv, int enable);
+
+/*
+ * frame
+ */
+int usbhs_frame_get_num(struct usbhs_priv *priv);
+
+/*
+ * data
+ */
+#define usbhs_get_dparam(priv, param) (priv->dparam->param)
+#define usbhs_priv_to_pdev(priv) (priv->pdev)
+#define usbhs_priv_to_dev(priv) (&priv->pdev->dev)
+#define usbhs_priv_to_lock(priv) (&priv->lock)
+
+#endif /* RENESAS_USB_DRIVER_H */
diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c
new file mode 100644
index 00000000000..73604a1d684
--- /dev/null
+++ b/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);
+}
diff --git a/drivers/usb/renesas_usbhs/mod.h b/drivers/usb/renesas_usbhs/mod.h
new file mode 100644
index 00000000000..8644191e164
--- /dev/null
+++ b/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 */
diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c
new file mode 100644
index 00000000000..9a5ac02077b
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/mod_gadget.c
@@ -0,0 +1,1341 @@
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include "common.h"
+
+/*
+ * struct
+ */
+struct usbhsg_request {
+ struct usb_request req;
+ struct list_head node;
+};
+
+#define EP_NAME_SIZE 8
+struct usbhsg_gpriv;
+struct usbhsg_pipe_handle;
+struct usbhsg_uep {
+ struct usb_ep ep;
+ struct usbhs_pipe *pipe;
+ struct list_head list;
+
+ char ep_name[EP_NAME_SIZE];
+
+ struct usbhsg_gpriv *gpriv;
+ struct usbhsg_pipe_handle *handler;
+};
+
+struct usbhsg_gpriv {
+ struct usb_gadget gadget;
+ struct usbhs_mod mod;
+
+ struct usbhsg_uep *uep;
+ int uep_size;
+
+ struct usb_gadget_driver *driver;
+
+ u32 status;
+#define USBHSG_STATUS_STARTED (1 << 0)
+#define USBHSG_STATUS_REGISTERD (1 << 1)
+#define USBHSG_STATUS_WEDGE (1 << 2)
+};
+
+struct usbhsg_pipe_handle {
+ int (*prepare)(struct usbhsg_uep *uep, struct usbhsg_request *ureq);
+ int (*try_run)(struct usbhsg_uep *uep, struct usbhsg_request *ureq);
+ void (*irq_mask)(struct usbhsg_uep *uep, int enable);
+};
+
+struct usbhsg_recip_handle {
+ char *name;
+ int (*device)(struct usbhs_priv *priv, struct usbhsg_uep *uep,
+ struct usb_ctrlrequest *ctrl);
+ int (*interface)(struct usbhs_priv *priv, struct usbhsg_uep *uep,
+ struct usb_ctrlrequest *ctrl);
+ int (*endpoint)(struct usbhs_priv *priv, struct usbhsg_uep *uep,
+ struct usb_ctrlrequest *ctrl);
+};
+
+/*
+ * macro
+ */
+#define usbhsg_priv_to_gpriv(priv) \
+ container_of( \
+ usbhs_mod_get(priv, USBHS_GADGET), \
+ struct usbhsg_gpriv, mod)
+
+#define __usbhsg_for_each_uep(start, pos, g, i) \
+ for (i = start, pos = (g)->uep; \
+ i < (g)->uep_size; \
+ i++, pos = (g)->uep + i)
+
+#define usbhsg_for_each_uep(pos, gpriv, i) \
+ __usbhsg_for_each_uep(1, pos, gpriv, i)
+
+#define usbhsg_for_each_uep_with_dcp(pos, gpriv, i) \
+ __usbhsg_for_each_uep(0, pos, gpriv, i)
+
+#define usbhsg_gadget_to_gpriv(g)\
+ container_of(g, struct usbhsg_gpriv, gadget)
+
+#define usbhsg_req_to_ureq(r)\
+ container_of(r, struct usbhsg_request, req)
+
+#define usbhsg_ep_to_uep(e) container_of(e, struct usbhsg_uep, ep)
+#define usbhsg_gpriv_to_lock(gp) usbhs_priv_to_lock((gp)->mod.priv)
+#define usbhsg_gpriv_to_dev(gp) usbhs_priv_to_dev((gp)->mod.priv)
+#define usbhsg_gpriv_to_priv(gp) ((gp)->mod.priv)
+#define usbhsg_gpriv_to_dcp(gp) ((gp)->uep)
+#define usbhsg_gpriv_to_nth_uep(gp, i) ((gp)->uep + i)
+#define usbhsg_uep_to_gpriv(u) ((u)->gpriv)
+#define usbhsg_uep_to_pipe(u) ((u)->pipe)
+#define usbhsg_pipe_to_uep(p) ((p)->mod_private)
+#define usbhsg_is_dcp(u) ((u) == usbhsg_gpriv_to_dcp((u)->gpriv))
+
+#define usbhsg_is_not_connected(gp) ((gp)->gadget.speed == USB_SPEED_UNKNOWN)
+
+/* status */
+#define usbhsg_status_init(gp) do {(gp)->status = 0; } while (0)
+#define usbhsg_status_set(gp, b) (gp->status |= b)
+#define usbhsg_status_clr(gp, b) (gp->status &= ~b)
+#define usbhsg_status_has(gp, b) (gp->status & b)
+
+/*
+ * list push/pop
+ */
+static void usbhsg_queue_push(struct usbhsg_uep *uep,
+ struct usbhsg_request *ureq)
+{
+ struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+ struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+ struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+
+ /*
+ ********* assume under spin lock *********
+ */
+ list_del_init(&ureq->node);
+ list_add_tail(&ureq->node, &uep->list);
+ ureq->req.actual = 0;
+ ureq->req.status = -EINPROGRESS;
+
+ dev_dbg(dev, "pipe %d : queue push (%d)\n",
+ usbhs_pipe_number(pipe),
+ ureq->req.length);
+}
+
+static struct usbhsg_request *usbhsg_queue_get(struct usbhsg_uep *uep)
+{
+ /*
+ ********* assume under spin lock *********
+ */
+ if (list_empty(&uep->list))
+ return NULL;
+
+ return list_entry(uep->list.next, struct usbhsg_request, node);
+}
+
+#define usbhsg_queue_prepare(uep) __usbhsg_queue_handler(uep, 1);
+#define usbhsg_queue_handle(uep) __usbhsg_queue_handler(uep, 0);
+static int __usbhsg_queue_handler(struct usbhsg_uep *uep, int prepare)
+{
+ struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+ struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+ struct usbhsg_request *ureq;
+ spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv);
+ unsigned long flags;
+ int is_locked;
+ int ret = 0;
+
+ if (!uep->handler) {
+ dev_err(dev, "no handler function\n");
+ return -EIO;
+ }
+
+ /*
+ * CAUTION [*queue handler*]
+ *
+ * This function will be called for start/restart queue operation.
+ * OTOH the most much worry for USB driver is spinlock nest.
+ * Specially it are
+ * - usb_ep_ops :: queue
+ * - usb_request :: complete
+ *
+ * But the caller of this function need not care about spinlock.
+ * This function is using spin_trylock_irqsave for it.
+ * if "is_locked" is 1, this mean this function lock it.
+ * but if it is 0, this mean it is already under spin lock.
+ * see also
+ * CAUTION [*endpoint queue*]
+ * CAUTION [*request complete*]
+ */
+
+ /****************** spin try lock *******************/
+ is_locked = spin_trylock_irqsave(lock, flags);
+ ureq = usbhsg_queue_get(uep);
+ if (ureq) {
+ if (prepare)
+ ret = uep->handler->prepare(uep, ureq);
+ else
+ ret = uep->handler->try_run(uep, ureq);
+ }
+ if (is_locked)
+ spin_unlock_irqrestore(lock, flags);
+ /******************** spin unlock ******************/
+
+ return ret;
+}
+
+static void usbhsg_queue_pop(struct usbhsg_uep *uep,
+ struct usbhsg_request *ureq,
+ int status)
+{
+ struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+ struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+ struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+
+ /*
+ ********* assume under spin lock *********
+ */
+
+ /*
+ * CAUTION [*request complete*]
+ *
+ * There is a possibility not to be called in correct order
+ * if "complete" is called without spinlock.
+ *
+ * So, this function assume it is under spinlock,
+ * and call usb_request :: complete.
+ *
+ * But this "complete" will push next usb_request.
+ * It mean "usb_ep_ops :: queue" which is using spinlock is called
+ * under spinlock.
+ *
+ * To avoid dead-lock, this driver is using spin_trylock.
+ * CAUTION [*endpoint queue*]
+ * CAUTION [*queue handler*]
+ */
+
+ dev_dbg(dev, "pipe %d : queue pop\n", usbhs_pipe_number(pipe));
+
+ list_del_init(&ureq->node);
+
+ ureq->req.status = status;
+ ureq->req.complete(&uep->ep, &ureq->req);
+
+ /* more request ? */
+ if (0 == status)
+ usbhsg_queue_prepare(uep);
+}
+
+/*
+ * irq enable/disable function
+ */
+#define usbhsg_irq_callback_ctrl(uep, status, enable) \
+ ({ \
+ struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); \
+ struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); \
+ struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv); \
+ struct usbhs_mod *mod = usbhs_mod_get_current(priv); \
+ if (!mod) \
+ return; \
+ if (enable) \
+ mod->irq_##status |= (1 << usbhs_pipe_number(pipe)); \
+ else \
+ mod->irq_##status &= ~(1 << usbhs_pipe_number(pipe)); \
+ usbhs_irq_callback_update(priv, mod); \
+ })
+
+static void usbhsg_irq_empty_ctrl(struct usbhsg_uep *uep, int enable)
+{
+ usbhsg_irq_callback_ctrl(uep, bempsts, enable);
+}
+
+static void usbhsg_irq_ready_ctrl(struct usbhsg_uep *uep, int enable)
+{
+ usbhsg_irq_callback_ctrl(uep, brdysts, enable);
+}
+
+/*
+ * handler function
+ */
+static int usbhsg_try_run_ctrl_stage_end(struct usbhsg_uep *uep,
+ struct usbhsg_request *ureq)
+{
+ struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+
+ /*
+ ********* assume under spin lock *********
+ */
+
+ usbhs_dcp_control_transfer_done(pipe);
+ usbhsg_queue_pop(uep, ureq, 0);
+
+ return 0;
+}
+
+static int usbhsg_try_run_send_packet(struct usbhsg_uep *uep,
+ struct usbhsg_request *ureq)
+{
+ struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+ struct usb_request *req = &ureq->req;
+ struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+ struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+ void *buf;
+ int remainder, send;
+ int is_done = 0;
+ int enable;
+ int maxp;
+
+ /*
+ ********* assume under spin lock *********
+ */
+
+ maxp = usbhs_pipe_get_maxpacket(pipe);
+ buf = req->buf + req->actual;
+ remainder = req->length - req->actual;
+
+ send = usbhs_fifo_write(pipe, buf, remainder);
+
+ /*
+ * send < 0 : pipe busy
+ * send = 0 : send zero packet
+ * send > 0 : send data
+ *
+ * send <= max_packet
+ */
+ if (send > 0)
+ req->actual += send;
+
+ /* send all packet ? */
+ if (send < remainder)
+ is_done = 0; /* there are remainder data */
+ else if (send < maxp)
+ is_done = 1; /* short packet */
+ else
+ is_done = !req->zero; /* send zero packet ? */
+
+ dev_dbg(dev, " send %d (%d/ %d/ %d/ %d)\n",
+ usbhs_pipe_number(pipe),
+ remainder, send, is_done, req->zero);
+
+ /*
+ * enable interrupt and send again in irq handler
+ * if it still have remainder data which should be sent.
+ */
+ enable = !is_done;
+ uep->handler->irq_mask(uep, enable);
+
+ /*
+ * usbhs_fifo_enable execute
+ * - after callback_update,
+ * - before queue_pop / stage_end
+ */
+ usbhs_fifo_enable(pipe);
+
+ /*
+ * all data were sent ?
+ */
+ if (is_done) {
+ /* it care below call in
+ "function mode" */
+ if (usbhsg_is_dcp(uep))
+ usbhs_dcp_control_transfer_done(pipe);
+
+ usbhsg_queue_pop(uep, ureq, 0);
+ }
+
+ return 0;
+}
+
+static int usbhsg_prepare_send_packet(struct usbhsg_uep *uep,
+ struct usbhsg_request *ureq)
+{
+ struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+
+ /*
+ ********* assume under spin lock *********
+ */
+
+ usbhs_fifo_prepare_write(pipe);
+ usbhsg_try_run_send_packet(uep, ureq);
+
+ return 0;
+}
+
+static int usbhsg_try_run_receive_packet(struct usbhsg_uep *uep,
+ struct usbhsg_request *ureq)
+{
+ struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+ struct usb_request *req = &ureq->req;
+ struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+ struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+ void *buf;
+ int maxp;
+ int remainder, recv;
+ int is_done = 0;
+
+ /*
+ ********* assume under spin lock *********
+ */
+
+ maxp = usbhs_pipe_get_maxpacket(pipe);
+ buf = req->buf + req->actual;
+ remainder = req->length - req->actual;
+
+ recv = usbhs_fifo_read(pipe, buf, remainder);
+ /*
+ * recv < 0 : pipe busy
+ * recv >= 0 : receive data
+ *
+ * recv <= max_packet
+ */
+ if (recv < 0)
+ return -EBUSY;
+
+ /* update parameters */
+ req->actual += recv;
+
+ if ((recv == remainder) || /* receive all data */
+ (recv < maxp)) /* short packet */
+ is_done = 1;
+
+ dev_dbg(dev, " recv %d (%d/ %d/ %d/ %d)\n",
+ usbhs_pipe_number(pipe),
+ remainder, recv, is_done, req->zero);
+
+ /* read all data ? */
+ if (is_done) {
+ int disable = 0;
+
+ uep->handler->irq_mask(uep, disable);
+ usbhs_fifo_disable(pipe);
+ usbhsg_queue_pop(uep, ureq, 0);
+ }
+
+ return 0;
+}
+
+static int usbhsg_prepare_receive_packet(struct usbhsg_uep *uep,
+ struct usbhsg_request *ureq)
+{
+ struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+ int enable = 1;
+ int ret;
+
+ /*
+ ********* assume under spin lock *********
+ */
+
+ ret = usbhs_fifo_prepare_read(pipe);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * data will be read in interrupt handler
+ */
+ uep->handler->irq_mask(uep, enable);
+
+ return ret;
+}
+
+static struct usbhsg_pipe_handle usbhsg_handler_send_by_empty = {
+ .prepare = usbhsg_prepare_send_packet,
+ .try_run = usbhsg_try_run_send_packet,
+ .irq_mask = usbhsg_irq_empty_ctrl,
+};
+
+static struct usbhsg_pipe_handle usbhsg_handler_send_by_ready = {
+ .prepare = usbhsg_prepare_send_packet,
+ .try_run = usbhsg_try_run_send_packet,
+ .irq_mask = usbhsg_irq_ready_ctrl,
+};
+
+static struct usbhsg_pipe_handle usbhsg_handler_recv_by_ready = {
+ .prepare = usbhsg_prepare_receive_packet,
+ .try_run = usbhsg_try_run_receive_packet,
+ .irq_mask = usbhsg_irq_ready_ctrl,
+};
+
+static struct usbhsg_pipe_handle usbhsg_handler_ctrl_stage_end = {
+ .prepare = usbhsg_try_run_ctrl_stage_end,
+ .try_run = usbhsg_try_run_ctrl_stage_end,
+};
+
+/*
+ * DCP pipe can NOT use "ready interrupt" for "send"
+ * it should use "empty" interrupt.
+ * see
+ * "Operation" - "Interrupt Function" - "BRDY Interrupt"
+ *
+ * on the other hand, normal pipe can use "ready interrupt" for "send"
+ * even though it is single/double buffer
+ */
+#define usbhsg_handler_send_ctrl usbhsg_handler_send_by_empty
+#define usbhsg_handler_recv_ctrl usbhsg_handler_recv_by_ready
+
+#define usbhsg_handler_send_packet usbhsg_handler_send_by_ready
+#define usbhsg_handler_recv_packet usbhsg_handler_recv_by_ready
+
+/*
+ * USB_TYPE_STANDARD / clear feature functions
+ */
+static int usbhsg_recip_handler_std_control_done(struct usbhs_priv *priv,
+ struct usbhsg_uep *uep,
+ struct usb_ctrlrequest *ctrl)
+{
+ struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+ struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv);
+ struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(dcp);
+
+ usbhs_dcp_control_transfer_done(pipe);
+
+ return 0;
+}
+
+static int usbhsg_recip_handler_std_clear_endpoint(struct usbhs_priv *priv,
+ struct usbhsg_uep *uep,
+ struct usb_ctrlrequest *ctrl)
+{
+ struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+ struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+
+ if (!usbhsg_status_has(gpriv, USBHSG_STATUS_WEDGE)) {
+ usbhs_fifo_disable(pipe);
+ usbhs_pipe_clear_sequence(pipe);
+ usbhs_fifo_enable(pipe);
+ }
+
+ usbhsg_recip_handler_std_control_done(priv, uep, ctrl);
+
+ usbhsg_queue_prepare(uep);
+
+ return 0;
+}
+
+struct usbhsg_recip_handle req_clear_feature = {
+ .name = "clear feature",
+ .device = usbhsg_recip_handler_std_control_done,
+ .interface = usbhsg_recip_handler_std_control_done,
+ .endpoint = usbhsg_recip_handler_std_clear_endpoint,
+};
+
+/*
+ * USB_TYPE handler
+ */
+static int usbhsg_recip_run_handle(struct usbhs_priv *priv,
+ struct usbhsg_recip_handle *handler,
+ struct usb_ctrlrequest *ctrl)
+{
+ struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+ struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+ struct usbhsg_uep *uep;
+ int recip = ctrl->bRequestType & USB_RECIP_MASK;
+ int nth = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK;
+ int ret;
+ int (*func)(struct usbhs_priv *priv, struct usbhsg_uep *uep,
+ struct usb_ctrlrequest *ctrl);
+ char *msg;
+
+ uep = usbhsg_gpriv_to_nth_uep(gpriv, nth);
+
+ switch (recip) {
+ case USB_RECIP_DEVICE:
+ msg = "DEVICE";
+ func = handler->device;
+ break;
+ case USB_RECIP_INTERFACE:
+ msg = "INTERFACE";
+ func = handler->interface;
+ break;
+ case USB_RECIP_ENDPOINT:
+ msg = "ENDPOINT";
+ func = handler->endpoint;
+ break;
+ default:
+ dev_warn(dev, "unsupported RECIP(%d)\n", recip);
+ func = NULL;
+ ret = -EINVAL;
+ }
+
+ if (func) {
+ dev_dbg(dev, "%s (pipe %d :%s)\n", handler->name, nth, msg);
+ ret = func(priv, uep, ctrl);
+ }
+
+ return ret;
+}
+
+/*
+ * irq functions
+ *
+ * it will be called from usbhs_interrupt
+ */
+static int usbhsg_irq_dev_state(struct usbhs_priv *priv,
+ struct usbhs_irq_state *irq_state)
+{
+ struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+ struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+
+ gpriv->gadget.speed = usbhs_status_get_usb_speed(irq_state);
+
+ dev_dbg(dev, "state = %x : speed : %d\n",
+ usbhs_status_get_device_state(irq_state),
+ gpriv->gadget.speed);
+
+ return 0;
+}
+
+static int usbhsg_irq_ctrl_stage(struct usbhs_priv *priv,
+ struct usbhs_irq_state *irq_state)
+{
+ struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+ struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv);
+ struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(dcp);
+ struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+ struct usb_ctrlrequest ctrl;
+ struct usbhsg_recip_handle *recip_handler = NULL;
+ int stage = usbhs_status_get_ctrl_stage(irq_state);
+ int ret = 0;
+
+ dev_dbg(dev, "stage = %d\n", stage);
+
+ /*
+ * see Manual
+ *
+ * "Operation"
+ * - "Interrupt Function"
+ * - "Control Transfer Stage Transition Interrupt"
+ * - Fig. "Control Transfer Stage Transitions"
+ */
+
+ switch (stage) {
+ case READ_DATA_STAGE:
+ dcp->handler = &usbhsg_handler_send_ctrl;
+ break;
+ case WRITE_DATA_STAGE:
+ dcp->handler = &usbhsg_handler_recv_ctrl;
+ break;
+ case NODATA_STATUS_STAGE:
+ dcp->handler = &usbhsg_handler_ctrl_stage_end;
+ break;
+ default:
+ return ret;
+ }
+
+ /*
+ * get usb request
+ */
+ usbhs_usbreq_get_val(priv, &ctrl);
+
+ switch (ctrl.bRequestType & USB_TYPE_MASK) {
+ case USB_TYPE_STANDARD:
+ switch (ctrl.bRequest) {
+ case USB_REQ_CLEAR_FEATURE:
+ recip_handler = &req_clear_feature;
+ break;
+ }
+ }
+
+ /*
+ * setup stage / run recip
+ */
+ if (recip_handler)
+ ret = usbhsg_recip_run_handle(priv, recip_handler, &ctrl);
+ else
+ ret = gpriv->driver->setup(&gpriv->gadget, &ctrl);
+
+ if (ret < 0)
+ usbhs_fifo_stall(pipe);
+
+ return ret;
+}
+
+static int usbhsg_irq_empty(struct usbhs_priv *priv,
+ struct usbhs_irq_state *irq_state)
+{
+ struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+ struct usbhsg_uep *uep;
+ struct usbhs_pipe *pipe;
+ struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+ int i, ret;
+
+ if (!irq_state->bempsts) {
+ dev_err(dev, "debug %s !!\n", __func__);
+ return -EIO;
+ }
+
+ dev_dbg(dev, "irq empty [0x%04x]\n", irq_state->bempsts);
+
+ /*
+ * search interrupted "pipe"
+ * not "uep".
+ */
+ usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
+ if (!(irq_state->bempsts & (1 << i)))
+ continue;
+
+ uep = usbhsg_pipe_to_uep(pipe);
+ ret = usbhsg_queue_handle(uep);
+ if (ret < 0)
+ dev_err(dev, "send error %d : %d\n", i, ret);
+ }
+
+ return 0;
+}
+
+static int usbhsg_irq_ready(struct usbhs_priv *priv,
+ struct usbhs_irq_state *irq_state)
+{
+ struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+ struct usbhsg_uep *uep;
+ struct usbhs_pipe *pipe;
+ struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+ int i, ret;
+
+ if (!irq_state->brdysts) {
+ dev_err(dev, "debug %s !!\n", __func__);
+ return -EIO;
+ }
+
+ dev_dbg(dev, "irq ready [0x%04x]\n", irq_state->brdysts);
+
+ /*
+ * search interrupted "pipe"
+ * not "uep".
+ */
+ usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
+ if (!(irq_state->brdysts & (1 << i)))
+ continue;
+
+ uep = usbhsg_pipe_to_uep(pipe);
+ ret = usbhsg_queue_handle(uep);
+ if (ret < 0)
+ dev_err(dev, "receive error %d : %d\n", i, ret);
+ }
+
+ return 0;
+}
+
+/*
+ *
+ * usb_dcp_ops
+ *
+ */
+static int usbhsg_dcp_enable(struct usbhsg_uep *uep)
+{
+ struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+ struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
+ struct usbhs_pipe *pipe;
+
+ /*
+ ********* assume under spin lock *********
+ */
+
+ pipe = usbhs_dcp_malloc(priv);
+ if (!pipe)
+ return -EIO;
+
+ uep->pipe = pipe;
+ uep->pipe->mod_private = uep;
+ INIT_LIST_HEAD(&uep->list);
+
+ return 0;
+}
+
+#define usbhsg_dcp_disable usbhsg_pipe_disable
+static int usbhsg_pipe_disable(struct usbhsg_uep *uep)
+{
+ struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+ struct usbhsg_request *ureq;
+ int disable = 0;
+
+ /*
+ ********* assume under spin lock *********
+ */
+
+ usbhs_fifo_disable(pipe);
+
+ /*
+ * disable pipe irq
+ */
+ usbhsg_irq_empty_ctrl(uep, disable);
+ usbhsg_irq_ready_ctrl(uep, disable);
+
+ while (1) {
+ ureq = usbhsg_queue_get(uep);
+ if (!ureq)
+ break;
+
+ usbhsg_queue_pop(uep, ureq, -ECONNRESET);
+ }
+
+ uep->pipe->mod_private = NULL;
+ uep->pipe = NULL;
+
+ return 0;
+}
+
+/*
+ *
+ * usb_ep_ops
+ *
+ */
+static int usbhsg_ep_enable(struct usb_ep *ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep);
+ struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+ struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
+ struct usbhs_pipe *pipe;
+ spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv);
+ unsigned long flags;
+ int ret = -EIO;
+
+ /******************** spin lock ********************/
+ spin_lock_irqsave(lock, flags);
+
+ pipe = usbhs_pipe_malloc(priv, desc);
+ if (pipe) {
+ uep->pipe = pipe;
+ pipe->mod_private = uep;
+ INIT_LIST_HEAD(&uep->list);
+
+ if (usb_endpoint_dir_in(desc))
+ uep->handler = &usbhsg_handler_send_packet;
+ else
+ uep->handler = &usbhsg_handler_recv_packet;
+
+ ret = 0;
+ }
+ spin_unlock_irqrestore(lock, flags);
+ /******************** spin unlock ******************/
+
+ return ret;
+}
+
+static int usbhsg_ep_disable(struct usb_ep *ep)
+{
+ struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep);
+ struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+ spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv);
+ unsigned long flags;
+ int ret;
+
+ /******************** spin lock ********************/
+ spin_lock_irqsave(lock, flags);
+ ret = usbhsg_pipe_disable(uep);
+ spin_unlock_irqrestore(lock, flags);
+ /******************** spin unlock ******************/
+
+ return ret;
+}
+
+static struct usb_request *usbhsg_ep_alloc_request(struct usb_ep *ep,
+ gfp_t gfp_flags)
+{
+ struct usbhsg_request *ureq;
+
+ ureq = kzalloc(sizeof *ureq, gfp_flags);
+ if (!ureq)
+ return NULL;
+
+ INIT_LIST_HEAD(&ureq->node);
+ return &ureq->req;
+}
+
+static void usbhsg_ep_free_request(struct usb_ep *ep,
+ struct usb_request *req)
+{
+ struct usbhsg_request *ureq = usbhsg_req_to_ureq(req);
+
+ WARN_ON(!list_empty(&ureq->node));
+ kfree(ureq);
+}
+
+static int usbhsg_ep_queue(struct usb_ep *ep, struct usb_request *req,
+ gfp_t gfp_flags)
+{
+ struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep);
+ struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+ struct usbhsg_request *ureq = usbhsg_req_to_ureq(req);
+ struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+ spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv);
+ unsigned long flags;
+ int ret = 0;
+ int is_locked;
+
+ /*
+ * CAUTION [*endpoint queue*]
+ *
+ * This function will be called from usb_request :: complete
+ * or usb driver timing.
+ * If this function is called from usb_request :: complete,
+ * it is already under spinlock on this driver.
+ * but it is called frm usb driver, this function should call spinlock.
+ *
+ * This function is using spin_trylock_irqsave to solve this issue.
+ * if "is_locked" is 1, this mean this function lock it.
+ * but if it is 0, this mean it is already under spin lock.
+ * see also
+ * CAUTION [*queue handler*]
+ * CAUTION [*request complete*]
+ */
+
+ /******************** spin lock ********************/
+ is_locked = spin_trylock_irqsave(lock, flags);
+
+ /* param check */
+ if (usbhsg_is_not_connected(gpriv) ||
+ unlikely(!gpriv->driver) ||
+ unlikely(!pipe))
+ ret = -ESHUTDOWN;
+ else
+ usbhsg_queue_push(uep, ureq);
+
+ if (is_locked)
+ spin_unlock_irqrestore(lock, flags);
+ /******************** spin unlock ******************/
+
+ usbhsg_queue_prepare(uep);
+
+ return ret;
+}
+
+static int usbhsg_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
+{
+ struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep);
+ struct usbhsg_request *ureq = usbhsg_req_to_ureq(req);
+ struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+ spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv);
+ unsigned long flags;
+ int is_locked;
+
+ /*
+ * see
+ * CAUTION [*queue handler*]
+ * CAUTION [*endpoint queue*]
+ * CAUTION [*request complete*]
+ */
+
+ /******************** spin lock ********************/
+ is_locked = spin_trylock_irqsave(lock, flags);
+
+ usbhsg_queue_pop(uep, ureq, -ECONNRESET);
+
+ if (is_locked)
+ spin_unlock_irqrestore(lock, flags);
+ /******************** spin unlock ******************/
+
+ return 0;
+}
+
+static int __usbhsg_ep_set_halt_wedge(struct usb_ep *ep, int halt, int wedge)
+{
+ struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep);
+ struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+ struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+ struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+ spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv);
+ unsigned long flags;
+ int ret = -EAGAIN;
+ int is_locked;
+
+ /*
+ * see
+ * CAUTION [*queue handler*]
+ * CAUTION [*endpoint queue*]
+ * CAUTION [*request complete*]
+ */
+
+ /******************** spin lock ********************/
+ is_locked = spin_trylock_irqsave(lock, flags);
+ if (!usbhsg_queue_get(uep)) {
+
+ dev_dbg(dev, "set halt %d (pipe %d)\n",
+ halt, usbhs_pipe_number(pipe));
+
+ if (halt)
+ usbhs_fifo_stall(pipe);
+ else
+ usbhs_fifo_disable(pipe);
+
+ if (halt && wedge)
+ usbhsg_status_set(gpriv, USBHSG_STATUS_WEDGE);
+ else
+ usbhsg_status_clr(gpriv, USBHSG_STATUS_WEDGE);
+
+ ret = 0;
+ }
+
+ if (is_locked)
+ spin_unlock_irqrestore(lock, flags);
+ /******************** spin unlock ******************/
+
+ return ret;
+}
+
+static int usbhsg_ep_set_halt(struct usb_ep *ep, int value)
+{
+ return __usbhsg_ep_set_halt_wedge(ep, value, 0);
+}
+
+static int usbhsg_ep_set_wedge(struct usb_ep *ep)
+{
+ return __usbhsg_ep_set_halt_wedge(ep, 1, 1);
+}
+
+static struct usb_ep_ops usbhsg_ep_ops = {
+ .enable = usbhsg_ep_enable,
+ .disable = usbhsg_ep_disable,
+
+ .alloc_request = usbhsg_ep_alloc_request,
+ .free_request = usbhsg_ep_free_request,
+
+ .queue = usbhsg_ep_queue,
+ .dequeue = usbhsg_ep_dequeue,
+
+ .set_halt = usbhsg_ep_set_halt,
+ .set_wedge = usbhsg_ep_set_wedge,
+};
+
+/*
+ * usb module start/end
+ */
+static int usbhsg_try_start(struct usbhs_priv *priv, u32 status)
+{
+ struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+ struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv);
+ struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+ struct device *dev = usbhs_priv_to_dev(priv);
+ spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv);
+ unsigned long flags;
+
+ /******************** spin lock ********************/
+ spin_lock_irqsave(lock, flags);
+
+ /*
+ * enable interrupt and systems if ready
+ */
+ usbhsg_status_set(gpriv, status);
+ if (!(usbhsg_status_has(gpriv, USBHSG_STATUS_STARTED) &&
+ usbhsg_status_has(gpriv, USBHSG_STATUS_REGISTERD)))
+ goto usbhsg_try_start_unlock;
+
+ dev_dbg(dev, "start gadget\n");
+
+ /*
+ * pipe initialize and enable DCP
+ */
+ usbhs_pipe_init(priv);
+ usbhsg_dcp_enable(dcp);
+
+ /*
+ * system config enble
+ * - HI speed
+ * - function
+ * - usb module
+ */
+ usbhs_sys_hispeed_ctrl(priv, 1);
+ usbhs_sys_function_ctrl(priv, 1);
+ usbhs_sys_usb_ctrl(priv, 1);
+
+ /*
+ * enable irq callback
+ */
+ mod->irq_dev_state = usbhsg_irq_dev_state;
+ mod->irq_ctrl_stage = usbhsg_irq_ctrl_stage;
+ mod->irq_empty = usbhsg_irq_empty;
+ mod->irq_ready = usbhsg_irq_ready;
+ mod->irq_bempsts = 0;
+ mod->irq_brdysts = 0;
+ usbhs_irq_callback_update(priv, mod);
+
+usbhsg_try_start_unlock:
+ spin_unlock_irqrestore(lock, flags);
+ /******************** spin unlock ********************/
+
+ return 0;
+}
+
+static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status)
+{
+ struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+ struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+ struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv);
+ struct device *dev = usbhs_priv_to_dev(priv);
+ spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv);
+ unsigned long flags;
+
+ /******************** spin lock ********************/
+ spin_lock_irqsave(lock, flags);
+
+ /*
+ * disable interrupt and systems if 1st try
+ */
+ usbhsg_status_clr(gpriv, status);
+ if (!usbhsg_status_has(gpriv, USBHSG_STATUS_STARTED) &&
+ !usbhsg_status_has(gpriv, USBHSG_STATUS_REGISTERD))
+ goto usbhsg_try_stop_unlock;
+
+ /* disable all irq */
+ mod->irq_dev_state = NULL;
+ mod->irq_ctrl_stage = NULL;
+ mod->irq_empty = NULL;
+ mod->irq_ready = NULL;
+ mod->irq_bempsts = 0;
+ mod->irq_brdysts = 0;
+ usbhs_irq_callback_update(priv, mod);
+
+ usbhsg_dcp_disable(dcp);
+
+ gpriv->gadget.speed = USB_SPEED_UNKNOWN;
+
+ /* disable sys */
+ usbhs_sys_hispeed_ctrl(priv, 0);
+ usbhs_sys_function_ctrl(priv, 0);
+ usbhs_sys_usb_ctrl(priv, 0);
+
+ spin_unlock_irqrestore(lock, flags);
+ /******************** spin unlock ********************/
+
+ if (gpriv->driver &&
+ gpriv->driver->disconnect)
+ gpriv->driver->disconnect(&gpriv->gadget);
+
+ dev_dbg(dev, "stop gadget\n");
+
+ return 0;
+
+usbhsg_try_stop_unlock:
+ spin_unlock_irqrestore(lock, flags);
+
+ return 0;
+}
+
+/*
+ *
+ * linux usb function
+ *
+ */
+struct usbhsg_gpriv *the_controller;
+int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+ int (*bind)(struct usb_gadget *))
+{
+ struct usbhsg_gpriv *gpriv = the_controller;
+ struct usbhs_priv *priv;
+ struct device *dev;
+ int ret;
+
+ if (!bind ||
+ !driver ||
+ !driver->setup ||
+ driver->speed != USB_SPEED_HIGH)
+ return -EINVAL;
+ if (!gpriv)
+ return -ENODEV;
+ if (gpriv->driver)
+ return -EBUSY;
+
+ dev = usbhsg_gpriv_to_dev(gpriv);
+ priv = usbhsg_gpriv_to_priv(gpriv);
+
+ /* first hook up the driver ... */
+ gpriv->driver = driver;
+ gpriv->gadget.dev.driver = &driver->driver;
+
+ ret = device_add(&gpriv->gadget.dev);
+ if (ret) {
+ dev_err(dev, "device_add error %d\n", ret);
+ goto add_fail;
+ }
+
+ ret = bind(&gpriv->gadget);
+ if (ret) {
+ dev_err(dev, "bind to driver %s error %d\n",
+ driver->driver.name, ret);
+ goto bind_fail;
+ }
+
+ dev_dbg(dev, "bind %s\n", driver->driver.name);
+
+ return usbhsg_try_start(priv, USBHSG_STATUS_REGISTERD);
+
+bind_fail:
+ device_del(&gpriv->gadget.dev);
+add_fail:
+ gpriv->driver = NULL;
+ gpriv->gadget.dev.driver = NULL;
+
+ return ret;
+}
+EXPORT_SYMBOL(usb_gadget_probe_driver);
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ struct usbhsg_gpriv *gpriv = the_controller;
+ struct usbhs_priv *priv;
+ struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+
+ if (!gpriv)
+ return -ENODEV;
+
+ if (!driver ||
+ !driver->unbind ||
+ driver != gpriv->driver)
+ return -EINVAL;
+
+ dev = usbhsg_gpriv_to_dev(gpriv);
+ priv = usbhsg_gpriv_to_priv(gpriv);
+
+ usbhsg_try_stop(priv, USBHSG_STATUS_REGISTERD);
+ device_del(&gpriv->gadget.dev);
+ gpriv->driver = NULL;
+
+ if (driver->disconnect)
+ driver->disconnect(&gpriv->gadget);
+
+ driver->unbind(&gpriv->gadget);
+ dev_dbg(dev, "unbind %s\n", driver->driver.name);
+
+ return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+/*
+ * usb gadget ops
+ */
+static int usbhsg_get_frame(struct usb_gadget *gadget)
+{
+ struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget);
+ struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
+
+ return usbhs_frame_get_num(priv);
+}
+
+static struct usb_gadget_ops usbhsg_gadget_ops = {
+ .get_frame = usbhsg_get_frame,
+};
+
+static int usbhsg_start(struct usbhs_priv *priv)
+{
+ return usbhsg_try_start(priv, USBHSG_STATUS_STARTED);
+}
+
+static int usbhsg_stop(struct usbhs_priv *priv)
+{
+ return usbhsg_try_stop(priv, USBHSG_STATUS_STARTED);
+}
+
+int __devinit usbhs_mod_gadget_probe(struct usbhs_priv *priv)
+{
+ struct usbhsg_gpriv *gpriv;
+ struct usbhsg_uep *uep;
+ struct device *dev = usbhs_priv_to_dev(priv);
+ int pipe_size = usbhs_get_dparam(priv, pipe_size);
+ int i;
+
+ gpriv = kzalloc(sizeof(struct usbhsg_gpriv), GFP_KERNEL);
+ if (!gpriv) {
+ dev_err(dev, "Could not allocate gadget priv\n");
+ return -ENOMEM;
+ }
+
+ uep = kzalloc(sizeof(struct usbhsg_uep) * pipe_size, GFP_KERNEL);
+ if (!uep) {
+ dev_err(dev, "Could not allocate ep\n");
+ goto usbhs_mod_gadget_probe_err_gpriv;
+ }
+
+ /*
+ * CAUTION
+ *
+ * There is no guarantee that it is possible to access usb module here.
+ * Don't accesses to it.
+ * The accesse will be enable after "usbhsg_start"
+ */
+
+ /*
+ * register itself
+ */
+ usbhs_mod_register(priv, &gpriv->mod, USBHS_GADGET);
+
+ /* init gpriv */
+ gpriv->mod.name = "gadget";
+ gpriv->mod.start = usbhsg_start;
+ gpriv->mod.stop = usbhsg_stop;
+ gpriv->uep = uep;
+ gpriv->uep_size = pipe_size;
+ usbhsg_status_init(gpriv);
+
+ /*
+ * init gadget
+ */
+ device_initialize(&gpriv->gadget.dev);
+ dev_set_name(&gpriv->gadget.dev, "gadget");
+ gpriv->gadget.dev.parent = dev;
+ gpriv->gadget.name = "renesas_usbhs_udc";
+ gpriv->gadget.ops = &usbhsg_gadget_ops;
+ gpriv->gadget.is_dualspeed = 1;
+
+ INIT_LIST_HEAD(&gpriv->gadget.ep_list);
+
+ /*
+ * init usb_ep
+ */
+ usbhsg_for_each_uep_with_dcp(uep, gpriv, i) {
+ uep->gpriv = gpriv;
+ snprintf(uep->ep_name, EP_NAME_SIZE, "ep%d", i);
+
+ uep->ep.name = uep->ep_name;
+ uep->ep.ops = &usbhsg_ep_ops;
+ INIT_LIST_HEAD(&uep->ep.ep_list);
+ INIT_LIST_HEAD(&uep->list);
+
+ /* init DCP */
+ if (usbhsg_is_dcp(uep)) {
+ gpriv->gadget.ep0 = &uep->ep;
+ uep->ep.maxpacket = 64;
+ }
+ /* init normal pipe */
+ else {
+ uep->ep.maxpacket = 512;
+ list_add_tail(&uep->ep.ep_list, &gpriv->gadget.ep_list);
+ }
+ }
+
+ the_controller = gpriv;
+
+ dev_info(dev, "gadget probed\n");
+
+ return 0;
+
+usbhs_mod_gadget_probe_err_gpriv:
+ kfree(gpriv);
+
+ return -ENOMEM;
+}
+
+void __devexit usbhs_mod_gadget_remove(struct usbhs_priv *priv)
+{
+ struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+
+ kfree(gpriv);
+}
diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c
new file mode 100644
index 00000000000..b7a9137f599
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/pipe.c
@@ -0,0 +1,880 @@
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include "./common.h"
+#include "./pipe.h"
+
+/*
+ * macros
+ */
+#define usbhsp_priv_to_pipeinfo(pr) (&(pr)->pipe_info)
+#define usbhsp_pipe_to_priv(p) ((p)->priv)
+
+#define usbhsp_addr_offset(p) ((usbhs_pipe_number(p) - 1) * 2)
+
+#define usbhsp_is_dcp(p) ((p)->priv->pipe_info.pipe == (p))
+
+#define usbhsp_flags_set(p, f) ((p)->flags |= USBHS_PIPE_FLAGS_##f)
+#define usbhsp_flags_clr(p, f) ((p)->flags &= ~USBHS_PIPE_FLAGS_##f)
+#define usbhsp_flags_has(p, f) ((p)->flags & USBHS_PIPE_FLAGS_##f)
+#define usbhsp_flags_init(p) do {(p)->flags = 0; } while (0)
+
+#define usbhsp_type(p) ((p)->pipe_type)
+#define usbhsp_type_is(p, t) ((p)->pipe_type == t)
+
+/*
+ * for debug
+ */
+static char *usbhsp_pipe_name[] = {
+ [USB_ENDPOINT_XFER_CONTROL] = "DCP",
+ [USB_ENDPOINT_XFER_BULK] = "BULK",
+ [USB_ENDPOINT_XFER_INT] = "INT",
+ [USB_ENDPOINT_XFER_ISOC] = "ISO",
+};
+
+/*
+ * usb request functions
+ */
+void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req)
+{
+ u16 val;
+
+ val = usbhs_read(priv, USBREQ);
+ req->bRequest = (val >> 8) & 0xFF;
+ req->bRequestType = (val >> 0) & 0xFF;
+
+ req->wValue = usbhs_read(priv, USBVAL);
+ req->wIndex = usbhs_read(priv, USBINDX);
+ req->wLength = usbhs_read(priv, USBLENG);
+}
+
+void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req)
+{
+ usbhs_write(priv, USBREQ, (req->bRequest << 8) | req->bRequestType);
+ usbhs_write(priv, USBVAL, req->wValue);
+ usbhs_write(priv, USBINDX, req->wIndex);
+ usbhs_write(priv, USBLENG, req->wLength);
+}
+
+/*
+ * DCPCTR/PIPEnCTR functions
+ */
+static void usbhsp_pipectrl_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
+{
+ struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+ int offset = usbhsp_addr_offset(pipe);
+
+ if (usbhsp_is_dcp(pipe))
+ usbhs_bset(priv, DCPCTR, mask, val);
+ else
+ usbhs_bset(priv, PIPEnCTR + offset, mask, val);
+}
+
+static u16 usbhsp_pipectrl_get(struct usbhs_pipe *pipe)
+{
+ struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+ int offset = usbhsp_addr_offset(pipe);
+
+ if (usbhsp_is_dcp(pipe))
+ return usbhs_read(priv, DCPCTR);
+ else
+ return usbhs_read(priv, PIPEnCTR + offset);
+}
+
+/*
+ * DCP/PIPE functions
+ */
+static void __usbhsp_pipe_xxx_set(struct usbhs_pipe *pipe,
+ u16 dcp_reg, u16 pipe_reg,
+ u16 mask, u16 val)
+{
+ struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+
+ if (usbhsp_is_dcp(pipe))
+ usbhs_bset(priv, dcp_reg, mask, val);
+ else
+ usbhs_bset(priv, pipe_reg, mask, val);
+}
+
+static u16 __usbhsp_pipe_xxx_get(struct usbhs_pipe *pipe,
+ u16 dcp_reg, u16 pipe_reg)
+{
+ struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+
+ if (usbhsp_is_dcp(pipe))
+ return usbhs_read(priv, dcp_reg);
+ else
+ return usbhs_read(priv, pipe_reg);
+}
+
+/*
+ * DCPCFG/PIPECFG functions
+ */
+static void usbhsp_pipe_cfg_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
+{
+ __usbhsp_pipe_xxx_set(pipe, DCPCFG, PIPECFG, mask, val);
+}
+
+/*
+ * PIPEBUF
+ */
+static void usbhsp_pipe_buf_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
+{
+ if (usbhsp_is_dcp(pipe))
+ return;
+
+ __usbhsp_pipe_xxx_set(pipe, 0, PIPEBUF, mask, val);
+}
+
+/*
+ * DCPMAXP/PIPEMAXP
+ */
+static void usbhsp_pipe_maxp_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
+{
+ __usbhsp_pipe_xxx_set(pipe, DCPMAXP, PIPEMAXP, mask, val);
+}
+
+static u16 usbhsp_pipe_maxp_get(struct usbhs_pipe *pipe)
+{
+ return __usbhsp_pipe_xxx_get(pipe, DCPMAXP, PIPEMAXP);
+}
+
+/*
+ * pipe control functions
+ */
+static void usbhsp_pipe_select(struct usbhs_pipe *pipe)
+{
+ struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+
+ /*
+ * On pipe, this is necessary before
+ * accesses to below registers.
+ *
+ * PIPESEL : usbhsp_pipe_select
+ * PIPECFG : usbhsp_pipe_cfg_xxx
+ * PIPEBUF : usbhsp_pipe_buf_xxx
+ * PIPEMAXP : usbhsp_pipe_maxp_xxx
+ * PIPEPERI
+ */
+
+ /*
+ * if pipe is dcp, no pipe is selected.
+ * it is no problem, because dcp have its register
+ */
+ usbhs_write(priv, PIPESEL, 0xF & usbhs_pipe_number(pipe));
+}
+
+static int usbhsp_pipe_barrier(struct usbhs_pipe *pipe)
+{
+ struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+ struct device *dev = usbhs_priv_to_dev(priv);
+ int timeout = 1024;
+ u16 val;
+
+ /*
+ * make sure....
+ *
+ * Modify these bits when CSSTS = 0, PID = NAK, and no pipe number is
+ * specified by the CURPIPE bits.
+ * When changing the setting of this bit after changing
+ * the PID bits for the selected pipe from BUF to NAK,
+ * check that CSSTS = 0 and PBUSY = 0.
+ */
+
+ /*
+ * CURPIPE bit = 0
+ *
+ * see also
+ * "Operation"
+ * - "Pipe Control"
+ * - "Pipe Control Registers Switching Procedure"
+ */
+ usbhs_write(priv, CFIFOSEL, 0);
+
+ do {
+ val = usbhsp_pipectrl_get(pipe);
+ val &= CSSTS | PID_MASK;
+ if (!val)
+ return 0;
+
+ udelay(10);
+
+ } while (timeout--);
+
+ /*
+ * force NAK
+ */
+ timeout = 1024;
+ usbhs_fifo_disable(pipe);
+ do {
+ val = usbhsp_pipectrl_get(pipe);
+ val &= PBUSY;
+ if (!val)
+ return 0;
+
+ } while (timeout--);
+
+ dev_err(dev, "pipe barrier failed\n");
+
+ return -EBUSY;
+}
+
+static int usbhsp_pipe_is_accessible(struct usbhs_pipe *pipe)
+{
+ u16 val;
+
+ val = usbhsp_pipectrl_get(pipe);
+ if (val & BSTS)
+ return 0;
+
+ return -EBUSY;
+}
+
+/*
+ * PID ctrl
+ */
+static void __usbhsp_pid_try_nak_if_stall(struct usbhs_pipe *pipe)
+{
+ u16 pid = usbhsp_pipectrl_get(pipe);
+
+ pid &= PID_MASK;
+
+ /*
+ * see
+ * "Pipe n Control Register" - "PID"
+ */
+ switch (pid) {
+ case PID_STALL11:
+ usbhsp_pipectrl_set(pipe, PID_MASK, PID_STALL10);
+ /* fall-through */
+ case PID_STALL10:
+ usbhsp_pipectrl_set(pipe, PID_MASK, PID_NAK);
+ }
+}
+
+void usbhs_fifo_disable(struct usbhs_pipe *pipe)
+{
+ /* see "Pipe n Control Register" - "PID" */
+ __usbhsp_pid_try_nak_if_stall(pipe);
+
+ usbhsp_pipectrl_set(pipe, PID_MASK, PID_NAK);
+}
+
+void usbhs_fifo_enable(struct usbhs_pipe *pipe)
+{
+ /* see "Pipe n Control Register" - "PID" */
+ __usbhsp_pid_try_nak_if_stall(pipe);
+
+ usbhsp_pipectrl_set(pipe, PID_MASK, PID_BUF);
+}
+
+void usbhs_fifo_stall(struct usbhs_pipe *pipe)
+{
+ u16 pid = usbhsp_pipectrl_get(pipe);
+
+ pid &= PID_MASK;
+
+ /*
+ * see
+ * "Pipe n Control Register" - "PID"
+ */
+ switch (pid) {
+ case PID_NAK:
+ usbhsp_pipectrl_set(pipe, PID_MASK, PID_STALL10);
+ break;
+ case PID_BUF:
+ usbhsp_pipectrl_set(pipe, PID_MASK, PID_STALL11);
+ break;
+ }
+}
+
+/*
+ * CFIFO ctrl
+ */
+void usbhs_fifo_send_terminator(struct usbhs_pipe *pipe)
+{
+ struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+
+ usbhs_bset(priv, CFIFOCTR, BVAL, BVAL);
+}
+
+static void usbhsp_fifo_clear(struct usbhs_pipe *pipe)
+{
+ struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+
+ usbhs_write(priv, CFIFOCTR, BCLR);
+}
+
+static int usbhsp_fifo_barrier(struct usbhs_priv *priv)
+{
+ int timeout = 1024;
+
+ do {
+ /* The FIFO port is accessible */
+ if (usbhs_read(priv, CFIFOCTR) & FRDY)
+ return 0;
+
+ udelay(10);
+ } while (timeout--);
+
+ return -EBUSY;
+}
+
+static int usbhsp_fifo_rcv_len(struct usbhs_priv *priv)
+{
+ return usbhs_read(priv, CFIFOCTR) & DTLN_MASK;
+}
+
+static int usbhsp_fifo_select(struct usbhs_pipe *pipe, int write)
+{
+ struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+ struct device *dev = usbhs_priv_to_dev(priv);
+ int timeout = 1024;
+ u16 mask = ((1 << 5) | 0xF); /* mask of ISEL | CURPIPE */
+ u16 base = usbhs_pipe_number(pipe); /* CURPIPE */
+
+ if (usbhsp_is_dcp(pipe))
+ base |= (1 == write) << 5; /* ISEL */
+
+ /* "base" will be used below */
+ usbhs_write(priv, CFIFOSEL, base | MBW_32);
+
+ /* check ISEL and CURPIPE value */
+ while (timeout--) {
+ if (base == (mask & usbhs_read(priv, CFIFOSEL)))
+ return 0;
+ udelay(10);
+ }
+
+ dev_err(dev, "fifo select error\n");
+
+ return -EIO;
+}
+
+int usbhs_fifo_prepare_write(struct usbhs_pipe *pipe)
+{
+ int ret;
+
+ ret = usbhsp_fifo_select(pipe, 1);
+ if (ret < 0)
+ return ret;
+
+ usbhsp_fifo_clear(pipe);
+
+ return ret;
+}
+
+int usbhs_fifo_write(struct usbhs_pipe *pipe, u8 *buf, int len)
+{
+ struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+ void __iomem *addr = priv->base + CFIFO;
+ int maxp = usbhs_pipe_get_maxpacket(pipe);
+ int total_len;
+ int i, ret;
+
+ ret = usbhsp_pipe_is_accessible(pipe);
+ if (ret < 0)
+ return ret;
+
+ ret = usbhs_fifo_prepare_write(pipe);
+ if (ret < 0)
+ return ret;
+
+ ret = usbhsp_fifo_barrier(priv);
+ if (ret < 0)
+ return ret;
+
+ len = min(len, maxp);
+ total_len = len;
+
+ /*
+ * FIXME
+ *
+ * 32-bit access only
+ */
+ if (len >= 4 &&
+ !((unsigned long)buf & 0x03)) {
+ iowrite32_rep(addr, buf, len / 4);
+ len %= 4;
+ buf += total_len - len;
+ }
+
+ /* the rest operation */
+ for (i = 0; i < len; i++)
+ iowrite8(buf[i], addr + (0x03 - (i & 0x03)));
+
+ if (total_len < maxp)
+ usbhs_fifo_send_terminator(pipe);
+
+ return total_len;
+}
+
+int usbhs_fifo_prepare_read(struct usbhs_pipe *pipe)
+{
+ int ret;
+
+ /*
+ * select pipe and enable it to prepare packet receive
+ */
+ ret = usbhsp_fifo_select(pipe, 0);
+ if (ret < 0)
+ return ret;
+
+ usbhs_fifo_enable(pipe);
+
+ return ret;
+}
+
+int usbhs_fifo_read(struct usbhs_pipe *pipe, u8 *buf, int len)
+{
+ struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+ void __iomem *addr = priv->base + CFIFO;
+ int rcv_len;
+ int i, ret;
+ int total_len;
+ u32 data = 0;
+
+ ret = usbhsp_fifo_select(pipe, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = usbhsp_fifo_barrier(priv);
+ if (ret < 0)
+ return ret;
+
+ rcv_len = usbhsp_fifo_rcv_len(priv);
+
+ /*
+ * Buffer clear if Zero-Length packet
+ *
+ * see
+ * "Operation" - "FIFO Buffer Memory" - "FIFO Port Function"
+ */
+ if (0 == rcv_len) {
+ usbhsp_fifo_clear(pipe);
+ return 0;
+ }
+
+ len = min(rcv_len, len);
+ total_len = len;
+
+ /*
+ * FIXME
+ *
+ * 32-bit access only
+ */
+ if (len >= 4 &&
+ !((unsigned long)buf & 0x03)) {
+ ioread32_rep(addr, buf, len / 4);
+ len %= 4;
+ buf += rcv_len - len;
+ }
+
+ /* the rest operation */
+ for (i = 0; i < len; i++) {
+ if (!(i & 0x03))
+ data = ioread32(addr);
+
+ buf[i] = (data >> ((i & 0x03) * 8)) & 0xff;
+ }
+
+ return total_len;
+}
+
+/*
+ * pipe setup
+ */
+static int usbhsp_possible_double_buffer(struct usbhs_pipe *pipe)
+{
+ /*
+ * only ISO / BULK pipe can use double buffer
+ */
+ if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK) ||
+ usbhsp_type_is(pipe, USB_ENDPOINT_XFER_ISOC))
+ return 1;
+
+ return 0;
+}
+
+static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe,
+ const struct usb_endpoint_descriptor *desc,
+ int is_host)
+{
+ u16 type = 0;
+ u16 bfre = 0;
+ u16 dblb = 0;
+ u16 cntmd = 0;
+ u16 dir = 0;
+ u16 epnum = 0;
+ u16 shtnak = 0;
+ u16 type_array[] = {
+ [USB_ENDPOINT_XFER_BULK] = TYPE_BULK,
+ [USB_ENDPOINT_XFER_INT] = TYPE_INT,
+ [USB_ENDPOINT_XFER_ISOC] = TYPE_ISO,
+ };
+ int is_double = usbhsp_possible_double_buffer(pipe);
+
+ if (usbhsp_is_dcp(pipe))
+ return -EINVAL;
+
+ /*
+ * PIPECFG
+ *
+ * see
+ * - "Register Descriptions" - "PIPECFG" register
+ * - "Features" - "Pipe configuration"
+ * - "Operation" - "Pipe Control"
+ */
+
+ /* TYPE */
+ type = type_array[usbhsp_type(pipe)];
+
+ /* BFRE */
+ if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_ISOC) ||
+ usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK))
+ bfre = 0; /* FIXME */
+
+ /* DBLB */
+ if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_ISOC) ||
+ usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK))
+ dblb = (is_double) ? DBLB : 0;
+
+ /* CNTMD */
+ if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK))
+ cntmd = 0; /* FIXME */
+
+ /* DIR */
+ if (usb_endpoint_dir_in(desc))
+ usbhsp_flags_set(pipe, IS_DIR_IN);
+
+ if ((is_host && usb_endpoint_dir_out(desc)) ||
+ (!is_host && usb_endpoint_dir_in(desc)))
+ dir |= DIR_OUT;
+
+ /* SHTNAK */
+ if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK) &&
+ !dir)
+ shtnak = SHTNAK;
+
+ /* EPNUM */
+ epnum = 0xF & usb_endpoint_num(desc);
+
+ return type |
+ bfre |
+ dblb |
+ cntmd |
+ dir |
+ shtnak |
+ epnum;
+}
+
+static u16 usbhsp_setup_pipemaxp(struct usbhs_pipe *pipe,
+ const struct usb_endpoint_descriptor *desc,
+ int is_host)
+{
+ /* host should set DEVSEL */
+
+ /* reutn MXPS */
+ return PIPE_MAXP_MASK & le16_to_cpu(desc->wMaxPacketSize);
+}
+
+static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe,
+ const struct usb_endpoint_descriptor *desc,
+ int is_host)
+{
+ struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+ struct usbhs_pipe_info *info = usbhsp_priv_to_pipeinfo(priv);
+ struct device *dev = usbhs_priv_to_dev(priv);
+ int pipe_num = usbhs_pipe_number(pipe);
+ int is_double = usbhsp_possible_double_buffer(pipe);
+ u16 buff_size;
+ u16 bufnmb;
+ u16 bufnmb_cnt;
+
+ /*
+ * PIPEBUF
+ *
+ * see
+ * - "Register Descriptions" - "PIPEBUF" register
+ * - "Features" - "Pipe configuration"
+ * - "Operation" - "FIFO Buffer Memory"
+ * - "Operation" - "Pipe Control"
+ *
+ * ex) if pipe6 - pipe9 are USB_ENDPOINT_XFER_INT (SH7724)
+ *
+ * BUFNMB: PIPE
+ * 0: pipe0 (DCP 256byte)
+ * 1: -
+ * 2: -
+ * 3: -
+ * 4: pipe6 (INT 64byte)
+ * 5: pipe7 (INT 64byte)
+ * 6: pipe8 (INT 64byte)
+ * 7: pipe9 (INT 64byte)
+ * 8 - xx: free (for BULK, ISOC)
+ */
+
+ /*
+ * FIXME
+ *
+ * it doesn't have good buffer allocator
+ *
+ * DCP : 256 byte
+ * BULK: 512 byte
+ * INT : 64 byte
+ * ISOC: 512 byte
+ */
+ if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_CONTROL))
+ buff_size = 256;
+ else if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_INT))
+ buff_size = 64;
+ else
+ buff_size = 512;
+
+ /* change buff_size to register value */
+ bufnmb_cnt = (buff_size / 64) - 1;
+
+ /* BUFNMB has been reserved for INT pipe
+ * see above */
+ if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_INT)) {
+ bufnmb = pipe_num - 2;
+ } else {
+ bufnmb = info->bufnmb_last;
+ info->bufnmb_last += bufnmb_cnt + 1;
+
+ /*
+ * double buffer
+ */
+ if (is_double)
+ info->bufnmb_last += bufnmb_cnt + 1;
+ }
+
+ dev_dbg(dev, "pipe : %d : buff_size 0x%x: bufnmb 0x%x\n",
+ pipe_num, buff_size, bufnmb);
+
+ return (0x1f & bufnmb_cnt) << 10 |
+ (0xff & bufnmb) << 0;
+}
+
+/*
+ * pipe control
+ */
+int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe)
+{
+ u16 mask = usbhsp_is_dcp(pipe) ? DCP_MAXP_MASK : PIPE_MAXP_MASK;
+
+ usbhsp_pipe_select(pipe);
+
+ return (int)(usbhsp_pipe_maxp_get(pipe) & mask);
+}
+
+int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe)
+{
+ return usbhsp_flags_has(pipe, IS_DIR_IN);
+}
+
+void usbhs_pipe_clear_sequence(struct usbhs_pipe *pipe)
+{
+ usbhsp_pipectrl_set(pipe, SQCLR, SQCLR);
+}
+
+static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type)
+{
+ struct usbhs_pipe *pos, *pipe;
+ int i;
+
+ /*
+ * find target pipe
+ */
+ pipe = NULL;
+ usbhs_for_each_pipe_with_dcp(pos, priv, i) {
+ if (!usbhsp_type_is(pos, type))
+ continue;
+ if (usbhsp_flags_has(pos, IS_USED))
+ continue;
+
+ pipe = pos;
+ break;
+ }
+
+ if (!pipe)
+ return NULL;
+
+ /*
+ * initialize pipe flags
+ */
+ usbhsp_flags_init(pipe);
+ usbhsp_flags_set(pipe, IS_USED);
+
+ return pipe;
+}
+
+void usbhs_pipe_init(struct usbhs_priv *priv)
+{
+ struct usbhs_pipe_info *info = usbhsp_priv_to_pipeinfo(priv);
+ struct usbhs_pipe *pipe;
+ int i;
+
+ /*
+ * FIXME
+ *
+ * driver needs good allocator.
+ *
+ * find first free buffer area (BULK, ISOC)
+ * (DCP, INT area is fixed)
+ *
+ * buffer number 0 - 3 have been reserved for DCP
+ * see
+ * usbhsp_to_bufnmb
+ */
+ info->bufnmb_last = 4;
+ usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
+ if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_INT))
+ info->bufnmb_last++;
+
+ usbhsp_flags_init(pipe);
+ pipe->mod_private = NULL;
+ }
+}
+
+struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct device *dev = usbhs_priv_to_dev(priv);
+ struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+ struct usbhs_pipe *pipe;
+ int is_host = usbhs_mod_is_host(priv, mod);
+ int ret;
+ u16 pipecfg, pipebuf, pipemaxp;
+
+ pipe = usbhsp_get_pipe(priv, usb_endpoint_type(desc));
+ if (!pipe)
+ return NULL;
+
+ usbhs_fifo_disable(pipe);
+
+ /* make sure pipe is not busy */
+ ret = usbhsp_pipe_barrier(pipe);
+ if (ret < 0) {
+ dev_err(dev, "pipe setup failed %d\n", usbhs_pipe_number(pipe));
+ return NULL;
+ }
+
+ pipecfg = usbhsp_setup_pipecfg(pipe, desc, is_host);
+ pipebuf = usbhsp_setup_pipebuff(pipe, desc, is_host);
+ pipemaxp = usbhsp_setup_pipemaxp(pipe, desc, is_host);
+
+ /* buffer clear
+ * see PIPECFG :: BFRE */
+ usbhsp_pipectrl_set(pipe, ACLRM, ACLRM);
+ usbhsp_pipectrl_set(pipe, ACLRM, 0);
+
+ usbhsp_pipe_select(pipe);
+ usbhsp_pipe_cfg_set(pipe, 0xFFFF, pipecfg);
+ usbhsp_pipe_buf_set(pipe, 0xFFFF, pipebuf);
+ usbhsp_pipe_maxp_set(pipe, 0xFFFF, pipemaxp);
+
+ usbhs_pipe_clear_sequence(pipe);
+
+ dev_dbg(dev, "enable pipe %d : %s (%s)\n",
+ usbhs_pipe_number(pipe),
+ usbhsp_pipe_name[usb_endpoint_type(desc)],
+ usbhs_pipe_is_dir_in(pipe) ? "in" : "out");
+
+ return pipe;
+}
+
+/*
+ * dcp control
+ */
+struct usbhs_pipe *usbhs_dcp_malloc(struct usbhs_priv *priv)
+{
+ struct usbhs_pipe *pipe;
+
+ pipe = usbhsp_get_pipe(priv, USB_ENDPOINT_XFER_CONTROL);
+ if (!pipe)
+ return NULL;
+
+ /*
+ * dcpcfg : default
+ * dcpmaxp : default
+ * pipebuf : nothing to do
+ */
+
+ usbhsp_pipe_select(pipe);
+ usbhs_pipe_clear_sequence(pipe);
+
+ return pipe;
+}
+
+void usbhs_dcp_control_transfer_done(struct usbhs_pipe *pipe)
+{
+ WARN_ON(!usbhsp_is_dcp(pipe));
+
+ usbhs_fifo_enable(pipe);
+ usbhsp_pipectrl_set(pipe, CCPL, CCPL);
+}
+
+
+/*
+ * pipe module function
+ */
+int usbhs_pipe_probe(struct usbhs_priv *priv)
+{
+ struct usbhs_pipe_info *info = usbhsp_priv_to_pipeinfo(priv);
+ struct usbhs_pipe *pipe;
+ struct device *dev = usbhs_priv_to_dev(priv);
+ u32 *pipe_type = usbhs_get_dparam(priv, pipe_type);
+ int pipe_size = usbhs_get_dparam(priv, pipe_size);
+ int i;
+
+ /* This driver expects 1st pipe is DCP */
+ if (pipe_type[0] != USB_ENDPOINT_XFER_CONTROL) {
+ dev_err(dev, "1st PIPE is not DCP\n");
+ return -EINVAL;
+ }
+
+ info->pipe = kzalloc(sizeof(struct usbhs_pipe) * pipe_size, GFP_KERNEL);
+ if (!info->pipe) {
+ dev_err(dev, "Could not allocate pipe\n");
+ return -ENOMEM;
+ }
+
+ info->size = pipe_size;
+
+ /*
+ * init pipe
+ */
+ usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
+ pipe->priv = priv;
+ usbhsp_type(pipe) = pipe_type[i] & USB_ENDPOINT_XFERTYPE_MASK;
+
+ dev_dbg(dev, "pipe %x\t: %s\n",
+ i, usbhsp_pipe_name[pipe_type[i]]);
+ }
+
+ return 0;
+}
+
+void usbhs_pipe_remove(struct usbhs_priv *priv)
+{
+ struct usbhs_pipe_info *info = usbhsp_priv_to_pipeinfo(priv);
+
+ kfree(info->pipe);
+}
diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h
new file mode 100644
index 00000000000..4a60dcef967
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/pipe.h
@@ -0,0 +1,105 @@
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#ifndef RENESAS_USB_PIPE_H
+#define RENESAS_USB_PIPE_H
+
+#include "./common.h"
+
+/*
+ * struct
+ */
+struct usbhs_pipe {
+ u32 pipe_type; /* USB_ENDPOINT_XFER_xxx */
+
+ struct usbhs_priv *priv;
+
+ u32 flags;
+#define USBHS_PIPE_FLAGS_IS_USED (1 << 0)
+#define USBHS_PIPE_FLAGS_IS_DIR_IN (1 << 1)
+
+ void *mod_private;
+};
+
+struct usbhs_pipe_info {
+ struct usbhs_pipe *pipe;
+ int size; /* array size of "pipe" */
+ int bufnmb_last; /* FIXME : driver needs good allocator */
+};
+
+/*
+ * pipe list
+ */
+#define __usbhs_for_each_pipe(start, pos, info, i) \
+ for (i = start, pos = (info)->pipe; \
+ i < (info)->size; \
+ i++, pos = (info)->pipe + i)
+
+#define usbhs_for_each_pipe(pos, priv, i) \
+ __usbhs_for_each_pipe(1, pos, &((priv)->pipe_info), i)
+
+#define usbhs_for_each_pipe_with_dcp(pos, priv, i) \
+ __usbhs_for_each_pipe(0, pos, &((priv)->pipe_info), i)
+
+/*
+ * pipe module probe / remove
+ */
+int usbhs_pipe_probe(struct usbhs_priv *priv);
+void usbhs_pipe_remove(struct usbhs_priv *priv);
+
+/*
+ * cfifo
+ */
+int usbhs_fifo_write(struct usbhs_pipe *pipe, u8 *buf, int len);
+int usbhs_fifo_read(struct usbhs_pipe *pipe, u8 *buf, int len);
+int usbhs_fifo_prepare_write(struct usbhs_pipe *pipe);
+int usbhs_fifo_prepare_read(struct usbhs_pipe *pipe);
+
+void usbhs_fifo_enable(struct usbhs_pipe *pipe);
+void usbhs_fifo_disable(struct usbhs_pipe *pipe);
+void usbhs_fifo_stall(struct usbhs_pipe *pipe);
+
+void usbhs_fifo_send_terminator(struct usbhs_pipe *pipe);
+
+
+/*
+ * usb request
+ */
+void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req);
+void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req);
+
+/*
+ * pipe control
+ */
+struct usbhs_pipe
+*usbhs_pipe_malloc(struct usbhs_priv *priv,
+ const struct usb_endpoint_descriptor *desc);
+
+int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe);
+void usbhs_pipe_init(struct usbhs_priv *priv);
+int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe);
+void usbhs_pipe_clear_sequence(struct usbhs_pipe *pipe);
+
+#define usbhs_pipe_number(p) (((u32)(p) - (u32)(p)->priv->pipe_info.pipe) / \
+ sizeof(struct usbhs_pipe))
+
+/*
+ * dcp control
+ */
+struct usbhs_pipe *usbhs_dcp_malloc(struct usbhs_priv *priv);
+void usbhs_dcp_control_transfer_done(struct usbhs_pipe *pipe);
+
+#endif /* RENESAS_USB_PIPE_H */
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index c2b29761fa9..b71e309116a 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -527,15 +527,6 @@ config USB_SERIAL_SAFE_PADDED
bool "USB Secure Encapsulated Driver - Padded"
depends on USB_SERIAL_SAFE
-config USB_SERIAL_SAMBA
- tristate "USB Atmel SAM Boot Assistant (SAM-BA) driver"
- help
- Say Y here if you want to access the SAM-BA boot application of an
- Atmel AT91SAM device.
-
- To compile this driver as a module, choose M here: the
- module will be called sam-ba.
-
config USB_SERIAL_SIEMENS_MPI
tristate "USB Siemens MPI driver"
help
diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile
index 9a2117f2b06..9e536eefb32 100644
--- a/drivers/usb/serial/Makefile
+++ b/drivers/usb/serial/Makefile
@@ -48,7 +48,6 @@ obj-$(CONFIG_USB_SERIAL_PL2303) += pl2303.o
obj-$(CONFIG_USB_SERIAL_QCAUX) += qcaux.o
obj-$(CONFIG_USB_SERIAL_QUALCOMM) += qcserial.o
obj-$(CONFIG_USB_SERIAL_SAFE) += safe_serial.o
-obj-$(CONFIG_USB_SERIAL_SAMBA) += sam-ba.o
obj-$(CONFIG_USB_SERIAL_SIEMENS_MPI) += siemens_mpi.o
obj-$(CONFIG_USB_SERIAL_SIERRAWIRELESS) += sierra.o
obj-$(CONFIG_USB_SERIAL_SPCP8X5) += spcp8x5.o
diff --git a/drivers/usb/serial/sam-ba.c b/drivers/usb/serial/sam-ba.c
deleted file mode 100644
index e3bba64afc5..00000000000
--- a/drivers/usb/serial/sam-ba.c
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Atmel SAM Boot Assistant (SAM-BA) driver
- *
- * Copyright (C) 2010 Johan Hovold <jhovold@gmail.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version
- * 2 as published by the Free Software Foundation.
- */
-
-#include <linux/kernel.h>
-#include <linux/tty.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/usb.h>
-#include <linux/usb/serial.h>
-
-
-#define DRIVER_VERSION "v1.0"
-#define DRIVER_AUTHOR "Johan Hovold <jhovold@gmail.com>"
-#define DRIVER_DESC "Atmel SAM Boot Assistant (SAM-BA) driver"
-
-#define SAMBA_VENDOR_ID 0x3eb
-#define SAMBA_PRODUCT_ID 0x6124
-
-
-static int debug;
-
-static const struct usb_device_id id_table[] = {
- /*
- * NOTE: Only match the CDC Data interface.
- */
- { USB_DEVICE_AND_INTERFACE_INFO(SAMBA_VENDOR_ID, SAMBA_PRODUCT_ID,
- USB_CLASS_CDC_DATA, 0, 0) },
- { }
-};
-MODULE_DEVICE_TABLE(usb, id_table);
-
-static struct usb_driver samba_driver = {
- .name = "sam-ba",
- .probe = usb_serial_probe,
- .disconnect = usb_serial_disconnect,
- .id_table = id_table,
- .no_dynamic_id = 1,
-};
-
-
-/*
- * NOTE: The SAM-BA firmware cannot handle merged write requests so we cannot
- * use the generic write implementation (which uses the port write fifo).
- */
-static int samba_write(struct tty_struct *tty, struct usb_serial_port *port,
- const unsigned char *buf, int count)
-{
- struct urb *urb;
- unsigned long flags;
- int result;
- int i;
-
- if (!count)
- return 0;
-
- count = min_t(int, count, port->bulk_out_size);
-
- spin_lock_irqsave(&port->lock, flags);
- if (!port->write_urbs_free) {
- spin_unlock_irqrestore(&port->lock, flags);
- return 0;
- }
- i = find_first_bit(&port->write_urbs_free,
- ARRAY_SIZE(port->write_urbs));
- __clear_bit(i, &port->write_urbs_free);
- port->tx_bytes += count;
- spin_unlock_irqrestore(&port->lock, flags);
-
- urb = port->write_urbs[i];
- memcpy(urb->transfer_buffer, buf, count);
- urb->transfer_buffer_length = count;
- usb_serial_debug_data(debug, &port->dev, __func__, count,
- urb->transfer_buffer);
- result = usb_submit_urb(urb, GFP_ATOMIC);
- if (result) {
- dev_err(&port->dev, "%s - error submitting urb: %d\n",
- __func__, result);
- spin_lock_irqsave(&port->lock, flags);
- __set_bit(i, &port->write_urbs_free);
- port->tx_bytes -= count;
- spin_unlock_irqrestore(&port->lock, flags);
-
- return result;
- }
-
- return count;
-}
-
-static int samba_write_room(struct tty_struct *tty)
-{
- struct usb_serial_port *port = tty->driver_data;
- unsigned long flags;
- unsigned long free;
- int count;
- int room;
-
- spin_lock_irqsave(&port->lock, flags);
- free = port->write_urbs_free;
- spin_unlock_irqrestore(&port->lock, flags);
-
- count = hweight_long(free);
- room = count * port->bulk_out_size;
-
- dbg("%s - returns %d", __func__, room);
-
- return room;
-}
-
-static int samba_chars_in_buffer(struct tty_struct *tty)
-{
- struct usb_serial_port *port = tty->driver_data;
- unsigned long flags;
- int chars;
-
- spin_lock_irqsave(&port->lock, flags);
- chars = port->tx_bytes;
- spin_unlock_irqrestore(&port->lock, flags);
-
- dbg("%s - returns %d", __func__, chars);
-
- return chars;
-}
-
-static void samba_write_bulk_callback(struct urb *urb)
-{
- struct usb_serial_port *port = urb->context;
- unsigned long flags;
- int i;
-
- dbg("%s - port %d", __func__, port->number);
-
- for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) {
- if (port->write_urbs[i] == urb)
- break;
- }
- spin_lock_irqsave(&port->lock, flags);
- __set_bit(i, &port->write_urbs_free);
- port->tx_bytes -= urb->transfer_buffer_length;
- spin_unlock_irqrestore(&port->lock, flags);
-
- if (urb->status)
- dbg("%s - non-zero urb status: %d", __func__, urb->status);
-
- usb_serial_port_softint(port);
-}
-
-static struct usb_serial_driver samba_device = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "sam-ba",
- },
- .usb_driver = &samba_driver,
- .id_table = id_table,
- .num_ports = 1,
- .bulk_in_size = 512,
- .bulk_out_size = 2048,
- .write = samba_write,
- .write_room = samba_write_room,
- .chars_in_buffer = samba_chars_in_buffer,
- .write_bulk_callback = samba_write_bulk_callback,
- .throttle = usb_serial_generic_throttle,
- .unthrottle = usb_serial_generic_unthrottle,
-};
-
-static int __init samba_init(void)
-{
- int retval;
-
- retval = usb_serial_register(&samba_device);
- if (retval)
- return retval;
-
- retval = usb_register(&samba_driver);
- if (retval) {
- usb_serial_deregister(&samba_device);
- return retval;
- }
-
- printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ": "
- DRIVER_DESC "\n");
- return 0;
-}
-
-static void __exit samba_exit(void)
-{
- usb_deregister(&samba_driver);
- usb_serial_deregister(&samba_device);
-}
-
-module_init(samba_init);
-module_exit(samba_exit);
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_VERSION(DRIVER_VERSION);
-MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Enable verbose debugging messages");
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index e538172c0f6..dd1571db55e 100644
--- a/include/linux/usb/gadget.h
+++ b/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 */
diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h
new file mode 100644
index 00000000000..565bca3aa44
--- /dev/null
+++ b/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 */