diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-23 12:33:02 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-05-23 12:33:02 -0700 |
commit | c44dead70a841d90ddc01968012f323c33217c9e (patch) | |
tree | 85489ebe9b9a3413cd8ee197ffb40c8aa8d97e63 | |
parent | 99dff5856220a02b8711f2e8746413ea6e53ccf6 (diff) | |
parent | d5f6db9e1aff6ccf1876224f152c0268b0c8a992 (diff) | |
download | kernel_samsung_aries-c44dead70a841d90ddc01968012f323c33217c9e.zip kernel_samsung_aries-c44dead70a841d90ddc01968012f323c33217c9e.tar.gz kernel_samsung_aries-c44dead70a841d90ddc01968012f323c33217c9e.tar.bz2 |
Merge branch 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6
* 'usb-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6: (205 commits)
USB: EHCI: Remove SPARC_LEON {read,write}_be definitions from ehci.h
USB: UHCI: Support big endian GRUSBHC HC
sparc: add {read,write}*_be routines
USB: UHCI: Add support for big endian descriptors
USB: UHCI: Use ACCESS_ONCE rather than using a full compiler barrier
USB: UHCI: Add support for big endian mmio
usb-storage: Correct adjust_quirks to include latest flags
usb/isp1760: Fix possible unlink problems
usb/isp1760: Move function isp1760_endpoint_disable() within file.
USB: remove remaining usages of hcd->state from usbcore and fix regression
usb: musb: ux500: add configuration and build options for ux500 dma
usb: musb: ux500: add dma glue layer for ux500
usb: musb: ux500: add dma name for ux500
usb: musb: ux500: add ux500 specific code for gadget side
usb: musb: fix compile error
usb-storage: fix up the unusual_realtek device list
USB: gadget: f_audio: Fix invalid dereference of initdata
EHCI: don't rescan interrupt QHs needlessly
OHCI: fix regression caused by nVidia shutdown workaround
USB: OTG: msm: Free VCCCX regulator even if we can't set the voltage
...
184 files changed, 14673 insertions, 3402 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index c603ef7..7c6624e 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -2585,6 +2585,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted. bytes of sense data); c = FIX_CAPACITY (decrease the reported device capacity by one sector); + d = NO_READ_DISC_INFO (don't use + READ_DISC_INFO command); + e = NO_READ_CAPACITY_16 (don't use + READ_CAPACITY_16 command); h = CAPACITY_HEURISTICS (decrease the reported device capacity by one sector if the number is odd); diff --git a/Documentation/usb/callbacks.txt b/Documentation/usb/callbacks.txt index bfb36b3..9e85846 100644 --- a/Documentation/usb/callbacks.txt +++ b/Documentation/usb/callbacks.txt @@ -95,9 +95,11 @@ pre_reset int (*pre_reset)(struct usb_interface *intf); -Another driver or user space is triggering a reset on the device which -contains the interface passed as an argument. Cease IO and save any -device state you need to restore. +A driver or user space is triggering a reset on the device which +contains the interface passed as an argument. Cease IO, wait for all +outstanding URBs to complete, and save any device state you need to +restore. No more URBs may be submitted until the post_reset method +is called. If you need to allocate memory here, use GFP_NOIO or GFP_ATOMIC, if you are in atomic context. diff --git a/Documentation/usb/linux-cdc-acm.inf b/Documentation/usb/linux-cdc-acm.inf index 612e722..37a02ce 100644 --- a/Documentation/usb/linux-cdc-acm.inf +++ b/Documentation/usb/linux-cdc-acm.inf @@ -90,10 +90,10 @@ ServiceBinary=%12%\USBSER.sys [SourceDisksFiles] [SourceDisksNames] [DeviceList] -%DESCRIPTION%=DriverInstall, USB\VID_0525&PID_A4A7, USB\VID_0525&PID_A4AB&MI_02 +%DESCRIPTION%=DriverInstall, USB\VID_0525&PID_A4A7, USB\VID_1D6B&PID_0104&MI_02 [DeviceList.NTamd64] -%DESCRIPTION%=DriverInstall, USB\VID_0525&PID_A4A7, USB\VID_0525&PID_A4AB&MI_02 +%DESCRIPTION%=DriverInstall, USB\VID_0525&PID_A4A7, USB\VID_1D6B&PID_0104&MI_02 ;------------------------------------------------------------------------------ diff --git a/Documentation/usb/linux.inf b/Documentation/usb/linux.inf index 4dee958..4ffa715b0 100644 --- a/Documentation/usb/linux.inf +++ b/Documentation/usb/linux.inf @@ -18,15 +18,15 @@ DriverVer = 06/21/2006,6.0.6000.16384 ; Decoration for x86 architecture [LinuxDevices.NTx86] -%LinuxDevice% = RNDIS.NT.5.1, USB\VID_0525&PID_a4a2, USB\VID_0525&PID_a4ab&MI_00 +%LinuxDevice% = RNDIS.NT.5.1, USB\VID_0525&PID_a4a2, USB\VID_1d6b&PID_0104&MI_00 ; Decoration for x64 architecture [LinuxDevices.NTamd64] -%LinuxDevice% = RNDIS.NT.5.1, USB\VID_0525&PID_a4a2, USB\VID_0525&PID_a4ab&MI_00 +%LinuxDevice% = RNDIS.NT.5.1, USB\VID_0525&PID_a4a2, USB\VID_1d6b&PID_0104&MI_00 ; Decoration for ia64 architecture [LinuxDevices.NTia64] -%LinuxDevice% = RNDIS.NT.5.1, USB\VID_0525&PID_a4a2, USB\VID_0525&PID_a4ab&MI_00 +%LinuxDevice% = RNDIS.NT.5.1, USB\VID_0525&PID_a4a2, USB\VID_1d6b&PID_0104&MI_00 ;@@@ This is the common setting for setup [ControlFlags] diff --git a/MAINTAINERS b/MAINTAINERS index a301ffa..0b41524 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4253,7 +4253,7 @@ F: include/linux/isicom.h MUSB MULTIPOINT HIGH SPEED DUAL-ROLE CONTROLLER M: Felipe Balbi <balbi@ti.com> L: linux-usb@vger.kernel.org -T: git git://gitorious.org/usb/usb.git +T: git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git S: Maintained F: drivers/usb/musb/ @@ -4588,6 +4588,7 @@ M: Felipe Balbi <balbi@ti.com> M: David Brownell <dbrownell@users.sourceforge.net> L: linux-usb@vger.kernel.org L: linux-omap@vger.kernel.org +T: git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git S: Maintained F: drivers/usb/*/*omap* F: arch/arm/*omap*/usb* diff --git a/arch/arm/mach-exynos4/Kconfig b/arch/arm/mach-exynos4/Kconfig index e849f67..8051962 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 9be104f..7778975 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 7930113..08813a6 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 6330b73..0009e77 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 62b0014..a964337 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 0000000..703118d --- /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 b79ad01..bb5d12f 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 0000000..0883c1b --- /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/mach-s3c2410/include/mach/map.h b/arch/arm/mach-s3c2410/include/mach/map.h index 25bbf5a..425552d 100644 --- a/arch/arm/mach-s3c2410/include/mach/map.h +++ b/arch/arm/mach-s3c2410/include/mach/map.h @@ -21,6 +21,10 @@ /* USB host controller */ #define S3C2410_PA_USBHOST (0x49000000) +/* S3C2416/S3C2443/S3C2450 High-Speed USB Gadget */ +#define S3C2416_PA_HSUDC (0x49800000) +#define S3C2416_SZ_HSUDC (SZ_4K) + /* DMA controller */ #define S3C2410_PA_DMA (0x4B000000) #define S3C24XX_SZ_DMA SZ_1M diff --git a/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h b/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h index 44494a5..5e06c72 100644 --- a/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h +++ b/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h @@ -37,6 +37,10 @@ #define S3C2443_SYSID S3C2443_CLKREG(0x5C) #define S3C2443_PWRCFG S3C2443_CLKREG(0x60) #define S3C2443_RSTCON S3C2443_CLKREG(0x64) +#define S3C2443_PHYCTRL S3C2443_CLKREG(0x80) +#define S3C2443_PHYPWR S3C2443_CLKREG(0x84) +#define S3C2443_URSTCON S3C2443_CLKREG(0x88) +#define S3C2443_UCLKCON S3C2443_CLKREG(0x8C) #define S3C2443_SWRST_RESET (0x533c2443) @@ -121,6 +125,27 @@ #define S3C2443_PWRCFG_SLEEP (1<<15) +#define S3C2443_PWRCFG_USBPHY (1 << 4) + +#define S3C2443_URSTCON_FUNCRST (1 << 2) +#define S3C2443_URSTCON_PHYRST (1 << 0) + +#define S3C2443_PHYCTRL_CLKSEL (1 << 3) +#define S3C2443_PHYCTRL_EXTCLK (1 << 2) +#define S3C2443_PHYCTRL_PLLSEL (1 << 1) +#define S3C2443_PHYCTRL_DSPORT (1 << 0) + +#define S3C2443_PHYPWR_COMMON_ON (1 << 31) +#define S3C2443_PHYPWR_ANALOG_PD (1 << 4) +#define S3C2443_PHYPWR_PLL_REFCLK (1 << 3) +#define S3C2443_PHYPWR_XO_ON (1 << 2) +#define S3C2443_PHYPWR_PLL_PWRDN (1 << 1) +#define S3C2443_PHYPWR_FSUSPEND (1 << 0) + +#define S3C2443_UCLKCON_DETECT_VBUS (1 << 31) +#define S3C2443_UCLKCON_FUNC_CLKEN (1 << 2) +#define S3C2443_UCLKCON_TCLKEN (1 << 0) + #include <asm/div64.h> static inline unsigned int diff --git a/arch/arm/mach-s3c2416/mach-smdk2416.c b/arch/arm/mach-s3c2416/mach-smdk2416.c index 3f83177..ac27ebb 100644 --- a/arch/arm/mach-s3c2416/mach-smdk2416.c +++ b/arch/arm/mach-s3c2416/mach-smdk2416.c @@ -23,6 +23,7 @@ #include <linux/mtd/partitions.h> #include <linux/gpio.h> #include <linux/fb.h> +#include <linux/delay.h> #include <asm/mach/arch.h> #include <asm/mach/map.h> @@ -35,6 +36,7 @@ #include <plat/regs-serial.h> #include <mach/regs-gpio.h> #include <mach/regs-lcd.h> +#include <mach/regs-s3c2443-clock.h> #include <mach/idle.h> #include <mach/leds-gpio.h> @@ -47,6 +49,7 @@ #include <plat/cpu.h> #include <plat/nand.h> #include <plat/sdhci.h> +#include <plat/udc.h> #include <plat/regs-fb-v4.h> #include <plat/fb.h> @@ -121,6 +124,27 @@ static struct s3c2410_uartcfg smdk2416_uartcfgs[] __initdata = { } }; +void smdk2416_hsudc_gpio_init(void) +{ + s3c_gpio_setpull(S3C2410_GPH(14), S3C_GPIO_PULL_UP); + s3c_gpio_setpull(S3C2410_GPF(2), S3C_GPIO_PULL_NONE); + s3c_gpio_cfgpin(S3C2410_GPH(14), S3C_GPIO_SFN(1)); + s3c2410_modify_misccr(S3C2416_MISCCR_SEL_SUSPND, 0); +} + +void smdk2416_hsudc_gpio_uninit(void) +{ + s3c2410_modify_misccr(S3C2416_MISCCR_SEL_SUSPND, 1); + s3c_gpio_setpull(S3C2410_GPH(14), S3C_GPIO_PULL_NONE); + s3c_gpio_cfgpin(S3C2410_GPH(14), S3C_GPIO_SFN(0)); +} + +struct s3c24xx_hsudc_platdata smdk2416_hsudc_platdata = { + .epnum = 9, + .gpio_init = smdk2416_hsudc_gpio_init, + .gpio_uninit = smdk2416_hsudc_gpio_uninit, +}; + struct s3c_fb_pd_win smdk2416_fb_win[] = { [0] = { /* think this is the same as the smdk6410 */ @@ -186,6 +210,7 @@ static struct platform_device *smdk2416_devices[] __initdata = { &s3c_device_i2c0, &s3c_device_hsmmc0, &s3c_device_hsmmc1, + &s3c_device_usb_hsudc, }; static void __init smdk2416_map_io(void) @@ -203,6 +228,8 @@ static void __init smdk2416_machine_init(void) s3c_sdhci0_set_platdata(&smdk2416_hsmmc0_pdata); s3c_sdhci1_set_platdata(&smdk2416_hsmmc1_pdata); + s3c24xx_hsudc_set_platdata(&smdk2416_hsudc_platdata); + gpio_request(S3C2410_GPB(4), "USBHost Power"); gpio_direction_output(S3C2410_GPB(4), 1); diff --git a/arch/arm/plat-s3c24xx/devs.c b/arch/arm/plat-s3c24xx/devs.c index 268f3ed..7366799 100644 --- a/arch/arm/plat-s3c24xx/devs.c +++ b/arch/arm/plat-s3c24xx/devs.c @@ -22,6 +22,7 @@ #include <linux/io.h> #include <linux/slab.h> #include <linux/string.h> +#include <linux/dma-mapping.h> #include <asm/mach/arch.h> #include <asm/mach/map.h> @@ -233,6 +234,46 @@ void __init s3c24xx_udc_set_platdata(struct s3c2410_udc_mach_info *pd) } } +/* USB High Speed 2.0 Device (Gadget) */ +static struct resource s3c_hsudc_resource[] = { + [0] = { + .start = S3C2416_PA_HSUDC, + .end = S3C2416_PA_HSUDC + S3C2416_SZ_HSUDC - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = IRQ_USBD, + .end = IRQ_USBD, + .flags = IORESOURCE_IRQ, + } +}; + +static u64 s3c_hsudc_dmamask = DMA_BIT_MASK(32); + +struct platform_device s3c_device_usb_hsudc = { + .name = "s3c-hsudc", + .id = -1, + .num_resources = ARRAY_SIZE(s3c_hsudc_resource), + .resource = s3c_hsudc_resource, + .dev = { + .dma_mask = &s3c_hsudc_dmamask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +void __init s3c24xx_hsudc_set_platdata(struct s3c24xx_hsudc_platdata *pd) +{ + struct s3c24xx_hsudc_platdata *npd; + + npd = kmalloc(sizeof(*npd), GFP_KERNEL); + if (npd) { + memcpy(npd, pd, sizeof(*npd)); + s3c_device_usb_hsudc.dev.platform_data = npd; + } else { + printk(KERN_ERR "no memory for udc platform data\n"); + } +} + /* IIS */ static struct resource s3c_iis_resource[] = { diff --git a/arch/arm/plat-s3c24xx/include/plat/udc.h b/arch/arm/plat-s3c24xx/include/plat/udc.h index 80457c6..f6388424 100644 --- a/arch/arm/plat-s3c24xx/include/plat/udc.h +++ b/arch/arm/plat-s3c24xx/include/plat/udc.h @@ -37,4 +37,21 @@ struct s3c2410_udc_mach_info { extern void __init s3c24xx_udc_set_platdata(struct s3c2410_udc_mach_info *); +/** + * s3c24xx_hsudc_platdata - Platform data for USB High-Speed gadget controller. + * @epnum: Number of endpoints to be instantiated by the controller driver. + * @gpio_init: Platform specific USB related GPIO initialization. + * @gpio_uninit: Platform specific USB releted GPIO uninitialzation. + * + * Representation of platform data for the S3C24XX USB 2.0 High Speed gadget + * controllers. + */ +struct s3c24xx_hsudc_platdata { + unsigned int epnum; + void (*gpio_init)(void); + void (*gpio_uninit)(void); +}; + +extern void __init s3c24xx_hsudc_set_platdata(struct s3c24xx_hsudc_platdata *pd); + #endif /* __ASM_ARM_ARCH_UDC_H */ diff --git a/arch/arm/plat-s5p/Kconfig b/arch/arm/plat-s5p/Kconfig index 8492297..6751bcf 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 42afff7..e234cc4 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 0000000..94080ff --- /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 0000000..6ae6810 --- /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 d973d39..a6c3d32 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 0000000..6dd6bcf --- /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 f0da6b7..39818d8 100644 --- a/arch/arm/plat-samsung/include/plat/devs.h +++ b/arch/arm/plat-samsung/include/plat/devs.h @@ -88,6 +88,7 @@ extern struct platform_device s3c64xx_device_onenand1; extern struct platform_device s5p_device_onenand; extern struct platform_device s3c_device_usbgadget; +extern struct platform_device s3c_device_usb_hsudc; extern struct platform_device s3c_device_usb_hsotg; extern struct platform_device s5pv210_device_ac97; @@ -142,6 +143,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 b058282..4770741 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 a29527f..145aead 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/hid/hid-core.c b/drivers/hid/hid-core.c index 4140fd2..c957c4b 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1775,19 +1775,37 @@ static const struct hid_device_id hid_ignore_list[] = { { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_GPEN_560) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_KYE, 0x0058) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY2) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY2) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYVOLTAGE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYCURRENT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYTIME) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYTEMPERATURE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYPH) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP) }, - { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY1) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIC) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIB) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY2) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_VIDEOCOM) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOTOR) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_COM3LAB) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_TELEPORT) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL) }, { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOSTANALYSER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOSTANALYSER2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_ABSESP) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_AUTODATABUS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MCT) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_HYBRID) }, + { HID_USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_HEATCONTROL) }, { HID_USB_DEVICE(USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1024LS) }, { HID_USB_DEVICE(USB_VENDOR_ID_MCC, USB_DEVICE_ID_MCC_PMD1208LS) }, { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICKIT1) }, diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index e715c43..0b374a6 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -380,19 +380,38 @@ #define USB_VENDOR_ID_LD 0x0f11 #define USB_DEVICE_ID_LD_CASSY 0x1000 +#define USB_DEVICE_ID_LD_CASSY2 0x1001 #define USB_DEVICE_ID_LD_POCKETCASSY 0x1010 +#define USB_DEVICE_ID_LD_POCKETCASSY2 0x1011 #define USB_DEVICE_ID_LD_MOBILECASSY 0x1020 +#define USB_DEVICE_ID_LD_MOBILECASSY2 0x1021 +#define USB_DEVICE_ID_LD_MICROCASSYVOLTAGE 0x1031 +#define USB_DEVICE_ID_LD_MICROCASSYCURRENT 0x1032 +#define USB_DEVICE_ID_LD_MICROCASSYTIME 0x1033 +#define USB_DEVICE_ID_LD_MICROCASSYTEMPERATURE 0x1035 +#define USB_DEVICE_ID_LD_MICROCASSYPH 0x1038 #define USB_DEVICE_ID_LD_JWM 0x1080 #define USB_DEVICE_ID_LD_DMMP 0x1081 #define USB_DEVICE_ID_LD_UMIP 0x1090 -#define USB_DEVICE_ID_LD_XRAY1 0x1100 +#define USB_DEVICE_ID_LD_UMIC 0x10A0 +#define USB_DEVICE_ID_LD_UMIB 0x10B0 +#define USB_DEVICE_ID_LD_XRAY 0x1100 #define USB_DEVICE_ID_LD_XRAY2 0x1101 +#define USB_DEVICE_ID_LD_XRAYCT 0x1110 #define USB_DEVICE_ID_LD_VIDEOCOM 0x1200 +#define USB_DEVICE_ID_LD_MOTOR 0x1210 #define USB_DEVICE_ID_LD_COM3LAB 0x2000 #define USB_DEVICE_ID_LD_TELEPORT 0x2010 #define USB_DEVICE_ID_LD_NETWORKANALYSER 0x2020 #define USB_DEVICE_ID_LD_POWERCONTROL 0x2030 #define USB_DEVICE_ID_LD_MACHINETEST 0x2040 +#define USB_DEVICE_ID_LD_MOSTANALYSER 0x2050 +#define USB_DEVICE_ID_LD_MOSTANALYSER2 0x2051 +#define USB_DEVICE_ID_LD_ABSESP 0x2060 +#define USB_DEVICE_ID_LD_AUTODATABUS 0x2070 +#define USB_DEVICE_ID_LD_MCT 0x2080 +#define USB_DEVICE_ID_LD_HYBRID 0x2090 +#define USB_DEVICE_ID_LD_HEATCONTROL 0x20A0 #define USB_VENDOR_ID_LOGITECH 0x046d #define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 006489d..48f1781 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -65,8 +65,10 @@ 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 y if SPARC_LEON default PCI # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. @@ -116,6 +118,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 239f050..30ddf8d 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_USB_R8A66597_HCD) += host/ obj-$(CONFIG_USB_HWA_HCD) += host/ obj-$(CONFIG_USB_ISP1760_HCD) += host/ obj-$(CONFIG_USB_IMX21_HCD) += host/ +obj-$(CONFIG_USB_FSL_MPH_DR_OF) += host/ obj-$(CONFIG_USB_C67X00_HCD) += c67x00/ @@ -45,3 +46,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 e057e53..395a347 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 */ @@ -946,7 +853,7 @@ static int acm_probe(struct usb_interface *intf, u8 ac_management_function = 0; u8 call_management_function = 0; int call_interface_num = -1; - int data_interface_num; + int data_interface_num = -1; unsigned long quirks; int num_rx_buf; int i; @@ -1030,7 +937,11 @@ next_desc: if (!union_header) { if (call_interface_num > 0) { dev_dbg(&intf->dev, "No union descriptor, using call management descriptor\n"); - data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num)); + /* quirks for Droids MuIn LCD */ + if (quirks & NO_DATA_INTERFACE) + data_interface = usb_ifnum_to_if(usb_dev, 0); + else + data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = call_interface_num)); control_interface = intf; } else { if (intf->cur_altsetting->desc.bNumEndpoints != 3) { @@ -1133,7 +1044,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 +1054,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 +1073,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 +1086,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 +1167,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 +1219,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 +1240,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 +1308,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 +1372,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: @@ -1622,6 +1539,11 @@ static const struct usb_device_id acm_ids[] = { .driver_info = NOT_A_MODEM, }, + /* Support for Droids MuIn LCD */ + { USB_DEVICE(0x04d8, 0x000b), + .driver_info = NO_DATA_INTERFACE, + }, + /* control interfaces without any protocol set */ { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM, USB_CDC_PROTO_NONE) }, @@ -1716,8 +1638,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 b4ea54d..ca7937f 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 */ }; @@ -137,3 +126,4 @@ struct acm { #define SINGLE_RX_URB 2 #define NO_CAP_LINE 4 #define NOT_A_MODEM 8 +#define NO_DATA_INTERFACE 16 diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c index a97c018..2b9ff51 100644 --- a/drivers/usb/class/cdc-wdm.c +++ b/drivers/usb/class/cdc-wdm.c @@ -542,6 +542,8 @@ static int wdm_open(struct inode *inode, struct file *file) mutex_lock(&desc->lock); if (!desc->count++) { + desc->werr = 0; + desc->rerr = 0; rv = usb_submit_urb(desc->validity, GFP_KERNEL); if (rv < 0) { desc->count--; @@ -853,6 +855,18 @@ static int wdm_pre_reset(struct usb_interface *intf) struct wdm_device *desc = usb_get_intfdata(intf); mutex_lock(&desc->lock); + kill_urbs(desc); + + /* + * we notify everybody using poll of + * an exceptional situation + * must be done before recovery lest a spontaneous + * message from the device is lost + */ + spin_lock_irq(&desc->iuspin); + desc->rerr = -EINTR; + spin_unlock_irq(&desc->iuspin); + wake_up_all(&desc->wait); return 0; } diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 83126b0..c962608 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -129,7 +129,7 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno, max_tx = ep->desc.wMaxPacketSize * (desc->bMaxBurst + 1); else max_tx = 999999; - if (desc->wBytesPerInterval > max_tx) { + if (le16_to_cpu(desc->wBytesPerInterval) > max_tx) { dev_warn(ddev, "%s endpoint with wBytesPerInterval of %d in " "config %d interface %d altsetting %d ep %d: " "setting to %d\n", diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 96fdfb8..0149c09 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 cf6a542..99458c8 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/core/hcd.c b/drivers/usb/core/hcd.c index 77a7fae..ace9f84 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -986,7 +986,7 @@ static int register_root_hub(struct usb_hcd *hcd) spin_unlock_irq (&hcd_root_hub_lock); /* Did the HC die before the root hub was registered? */ - if (HCD_DEAD(hcd) || hcd->state == HC_STATE_HALT) + if (HCD_DEAD(hcd)) usb_hc_died (hcd); /* This time clean up */ } @@ -2128,9 +2128,6 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd) set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); if (hcd->shared_hcd) set_bit(HCD_FLAG_SAW_IRQ, &hcd->shared_hcd->flags); - - if (unlikely(hcd->state == HC_STATE_HALT)) - usb_hc_died(hcd); rc = IRQ_HANDLED; } @@ -2407,6 +2404,7 @@ int usb_add_hcd(struct usb_hcd *hcd, rhdev->speed = USB_SPEED_SUPER; break; default: + retval = -EINVAL; goto err_set_rh_speed; } diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 93720bd..79a58c3 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -379,15 +379,6 @@ static int hub_port_status(struct usb_hub *hub, int port1, *status = le16_to_cpu(hub->status->port.wPortStatus); *change = le16_to_cpu(hub->status->port.wPortChange); - if ((hub->hdev->parent != NULL) && - hub_is_superspeed(hub->hdev)) { - /* Translate the USB 3 port status */ - u16 tmp = *status & USB_SS_PORT_STAT_MASK; - if (*status & USB_SS_PORT_STAT_POWER) - tmp |= USB_PORT_STAT_POWER; - *status = tmp; - } - ret = 0; } mutex_unlock(&hub->status_mutex); @@ -2160,11 +2151,76 @@ static int hub_port_reset(struct usb_hub *hub, int port1, return status; } +/* Warm reset a USB3 protocol port */ +static int hub_port_warm_reset(struct usb_hub *hub, int port) +{ + int ret; + u16 portstatus, portchange; + + if (!hub_is_superspeed(hub->hdev)) { + dev_err(hub->intfdev, "only USB3 hub support warm reset\n"); + return -EINVAL; + } + + /* Warm reset the port */ + ret = set_port_feature(hub->hdev, + port, USB_PORT_FEAT_BH_PORT_RESET); + if (ret) { + dev_err(hub->intfdev, "cannot warm reset port %d\n", port); + return ret; + } + + msleep(20); + ret = hub_port_status(hub, port, &portstatus, &portchange); + + if (portchange & USB_PORT_STAT_C_RESET) + clear_port_feature(hub->hdev, port, USB_PORT_FEAT_C_RESET); + + if (portchange & USB_PORT_STAT_C_BH_RESET) + clear_port_feature(hub->hdev, port, + USB_PORT_FEAT_C_BH_PORT_RESET); + + if (portchange & USB_PORT_STAT_C_LINK_STATE) + clear_port_feature(hub->hdev, port, + USB_PORT_FEAT_C_PORT_LINK_STATE); + + return ret; +} + +/* Check if a port is power on */ +static int port_is_power_on(struct usb_hub *hub, unsigned portstatus) +{ + int ret = 0; + + if (hub_is_superspeed(hub->hdev)) { + if (portstatus & USB_SS_PORT_STAT_POWER) + ret = 1; + } else { + if (portstatus & USB_PORT_STAT_POWER) + ret = 1; + } + + return ret; +} + #ifdef CONFIG_PM -#define MASK_BITS (USB_PORT_STAT_POWER | USB_PORT_STAT_CONNECTION | \ - USB_PORT_STAT_SUSPEND) -#define WANT_BITS (USB_PORT_STAT_POWER | USB_PORT_STAT_CONNECTION) +/* Check if a port is suspended(USB2.0 port) or in U3 state(USB3.0 port) */ +static int port_is_suspended(struct usb_hub *hub, unsigned portstatus) +{ + int ret = 0; + + if (hub_is_superspeed(hub->hdev)) { + if ((portstatus & USB_PORT_STAT_LINK_STATE) + == USB_SS_PORT_LS_U3) + ret = 1; + } else { + if (portstatus & USB_PORT_STAT_SUSPEND) + ret = 1; + } + + return ret; +} /* Determine whether the device on a port is ready for a normal resume, * is ready for a reset-resume, or should be disconnected. @@ -2174,7 +2230,9 @@ static int check_port_resume_type(struct usb_device *udev, int status, unsigned portchange, unsigned portstatus) { /* Is the device still present? */ - if (status || (portstatus & MASK_BITS) != WANT_BITS) { + if (status || port_is_suspended(hub, portstatus) || + !port_is_power_on(hub, portstatus) || + !(portstatus & USB_PORT_STAT_CONNECTION)) { if (status >= 0) status = -ENODEV; } @@ -2285,14 +2343,10 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) } /* see 7.1.7.6 */ - /* Clear PORT_POWER if it's a USB3.0 device connected to USB 3.0 - * external hub. - * FIXME: this is a temporary workaround to make the system able - * to suspend/resume. - */ - if ((hub->hdev->parent != NULL) && hub_is_superspeed(hub->hdev)) - status = clear_port_feature(hub->hdev, port1, - USB_PORT_FEAT_POWER); + if (hub_is_superspeed(hub->hdev)) + status = set_port_feature(hub->hdev, + port1 | (USB_SS_PORT_LS_U3 << 3), + USB_PORT_FEAT_LINK_STATE); else status = set_port_feature(hub->hdev, port1, USB_PORT_FEAT_SUSPEND); @@ -2439,7 +2493,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) /* Skip the initial Clear-Suspend step for a remote wakeup */ status = hub_port_status(hub, port1, &portstatus, &portchange); - if (status == 0 && !(portstatus & USB_PORT_STAT_SUSPEND)) + if (status == 0 && !port_is_suspended(hub, portstatus)) goto SuspendCleared; // dev_dbg(hub->intfdev, "resume port %d\n", port1); @@ -2447,8 +2501,13 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) set_bit(port1, hub->busy_bits); /* see 7.1.7.7; affects power usage, but not budgeting */ - status = clear_port_feature(hub->hdev, - port1, USB_PORT_FEAT_SUSPEND); + if (hub_is_superspeed(hub->hdev)) + status = set_port_feature(hub->hdev, + port1 | (USB_SS_PORT_LS_U0 << 3), + USB_PORT_FEAT_LINK_STATE); + else + status = clear_port_feature(hub->hdev, + port1, USB_PORT_FEAT_SUSPEND); if (status) { dev_dbg(hub->intfdev, "can't resume port %d, status %d\n", port1, status); @@ -2470,9 +2529,15 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) SuspendCleared: if (status == 0) { - if (portchange & USB_PORT_STAT_C_SUSPEND) - clear_port_feature(hub->hdev, port1, - USB_PORT_FEAT_C_SUSPEND); + if (hub_is_superspeed(hub->hdev)) { + if (portchange & USB_PORT_STAT_C_LINK_STATE) + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_PORT_LINK_STATE); + } else { + if (portchange & USB_PORT_STAT_C_SUSPEND) + clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_SUSPEND); + } } clear_bit(port1, hub->busy_bits); @@ -3147,7 +3212,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, /* maybe switch power back on (e.g. root hub was reset) */ if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2 - && !(portstatus & USB_PORT_STAT_POWER)) + && !port_is_power_on(hub, portstatus)) set_port_feature(hdev, port1, USB_PORT_FEAT_POWER); if (portstatus & USB_PORT_STAT_ENABLE) @@ -3490,6 +3555,16 @@ static void hub_events(void) USB_PORT_FEAT_C_PORT_CONFIG_ERROR); } + /* Warm reset a USB3 protocol port if it's in + * SS.Inactive state. + */ + if (hub_is_superspeed(hub->hdev) && + (portstatus & USB_PORT_STAT_LINK_STATE) + == USB_SS_PORT_LS_SS_INACTIVE) { + dev_dbg(hub_dev, "warm reset port %d\n", i); + hub_port_warm_reset(hub, i); + } + if (connect_change) hub_port_connect_change(hub, i, portstatus, portchange); diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 6781c36..cf05b97 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -842,22 +842,19 @@ const struct attribute_group *usb_interface_groups[] = { NULL }; -int usb_create_sysfs_intf_files(struct usb_interface *intf) +void usb_create_sysfs_intf_files(struct usb_interface *intf) { struct usb_device *udev = interface_to_usbdev(intf); struct usb_host_interface *alt = intf->cur_altsetting; - int retval; if (intf->sysfs_files_created || intf->unregistering) - return 0; + return; - if (alt->string == NULL && - !(udev->quirks & USB_QUIRK_CONFIG_INTF_STRINGS)) + if (!alt->string && !(udev->quirks & USB_QUIRK_CONFIG_INTF_STRINGS)) alt->string = usb_cache_string(udev, alt->desc.iInterface); - if (alt->string) - retval = device_create_file(&intf->dev, &dev_attr_interface); + if (alt->string && device_create_file(&intf->dev, &dev_attr_interface)) + ; /* We don't actually care if the function fails. */ intf->sysfs_files_created = 1; - return 0; } void usb_remove_sysfs_intf_files(struct usb_interface *intf) diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index d9d4b16..8706fc9 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -953,8 +953,7 @@ static int usb_bus_notify(struct notifier_block *nb, unsigned long action, if (dev->type == &usb_device_type) (void) usb_create_sysfs_dev_files(to_usb_device(dev)); else if (dev->type == &usb_if_device_type) - (void) usb_create_sysfs_intf_files( - to_usb_interface(dev)); + usb_create_sysfs_intf_files(to_usb_interface(dev)); break; case BUS_NOTIFY_DEL_DEVICE: diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index d450b74..d44d4b7 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -4,7 +4,7 @@ extern int usb_create_sysfs_dev_files(struct usb_device *dev); extern void usb_remove_sysfs_dev_files(struct usb_device *dev); -extern int usb_create_sysfs_intf_files(struct usb_interface *intf); +extern void usb_create_sysfs_intf_files(struct usb_interface *intf); extern void usb_remove_sysfs_intf_files(struct usb_interface *intf); extern int usb_create_ep_devs(struct device *parent, struct usb_host_endpoint *endpoint, diff --git a/drivers/usb/early/ehci-dbgp.c b/drivers/usb/early/ehci-dbgp.c index a6a350f..1fc8f12 100644 --- a/drivers/usb/early/ehci-dbgp.c +++ b/drivers/usb/early/ehci-dbgp.c @@ -102,6 +102,9 @@ static struct kgdb_io kgdbdbgp_io_ops; #define dbgp_kgdb_mode (0) #endif +/* Local version of HC_LENGTH macro as ehci struct is not available here */ +#define EARLY_HC_LENGTH(p) (0x00ff & (p)) /* bits 7 : 0 */ + /* * USB Packet IDs (PIDs) */ @@ -892,7 +895,7 @@ int __init early_dbgp_init(char *s) dbgp_printk("ehci_bar: %p\n", ehci_bar); ehci_caps = ehci_bar; - ehci_regs = ehci_bar + HC_LENGTH(readl(&ehci_caps->hc_capbase)); + ehci_regs = ehci_bar + EARLY_HC_LENGTH(readl(&ehci_caps->hc_capbase)); ehci_debug = ehci_bar + offset; ehci_dev.bus = bus; ehci_dev.slot = slot; diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index bc5123c..58456d1 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) @@ -338,6 +356,23 @@ config USB_S3C2410_DEBUG boolean "S3C2410 udc debug messages" depends on USB_GADGET_S3C2410 +config USB_GADGET_S3C_HSUDC + boolean "S3C2416, S3C2443 and S3C2450 USB Device Controller" + depends on ARCH_S3C2410 + select USB_GADGET_DUALSPEED + help + Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC + integrated with dual speed USB 2.0 device controller. It has + 8 endpoints, as well as endpoint zero. + + This driver has been tested on S3C2416 and S3C2450 processors. + +config USB_S3C_HSUDC + tristate + depends on USB_GADGET_S3C_HSUDC + default USB_GADGET + select USB_GADGET_SELECTED + config USB_GADGET_PXA_U2O boolean "PXA9xx Processor USB2.0 controller" select USB_GADGET_DUALSPEED diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 1ea15ee..4fe92b1 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -22,6 +22,7 @@ obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o obj-$(CONFIG_USB_CI13XXX_PCI) += ci13xxx_pci.o obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o +obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o obj-$(CONFIG_USB_EG20T) += pch_udc.o obj-$(CONFIG_USB_PXA_U2O) += mv_udc.o diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c index 9b7cdb1..41dc093 100644 --- a/drivers/usb/gadget/at91_udc.c +++ b/drivers/usb/gadget/at91_udc.c @@ -1767,7 +1767,7 @@ static int __init at91udc_probe(struct platform_device *pdev) } /* newer chips have more FIFO memory than rm9200 */ - if (cpu_is_at91sam9260()) { + if (cpu_is_at91sam9260() || cpu_is_at91sam9g20()) { udc->ep[0].maxpacket = 64; udc->ep[3].maxpacket = 64; udc->ep[4].maxpacket = 512; diff --git a/drivers/usb/gadget/ci13xxx_udc.c b/drivers/usb/gadget/ci13xxx_udc.c index e09178b..baaf87e 100644 --- a/drivers/usb/gadget/ci13xxx_udc.c +++ b/drivers/usb/gadget/ci13xxx_udc.c @@ -310,7 +310,7 @@ static int hw_device_reset(struct ci13xxx *udc) udc->udc_driver->notify_event(udc, CI13XXX_CONTROLLER_RESET_EVENT); - if (udc->udc_driver->flags && CI13XXX_DISABLE_STREAMING) + if (udc->udc_driver->flags & CI13XXX_DISABLE_STREAMING) hw_cwrite(CAP_USBMODE, USBMODE_SDIS, USBMODE_SDIS); /* USBMODE should be configured step by step */ @@ -1634,8 +1634,6 @@ static int _gadget_stop_activity(struct usb_gadget *gadget) gadget_for_each_ep(ep, gadget) { usb_ep_disable(ep); } - usb_ep_disable(&udc->ep0out.ep); - usb_ep_disable(&udc->ep0in.ep); if (udc->status != NULL) { usb_ep_free_request(&udc->ep0in.ep, udc->status); @@ -1678,18 +1676,10 @@ __acquires(udc->lock) if (retval) goto done; - retval = usb_ep_enable(&udc->ep0out.ep, &ctrl_endpt_out_desc); - if (retval) - goto done; + udc->status = usb_ep_alloc_request(&udc->ep0in.ep, GFP_ATOMIC); + if (udc->status == NULL) + retval = -ENOMEM; - retval = usb_ep_enable(&udc->ep0in.ep, &ctrl_endpt_in_desc); - if (!retval) { - udc->status = usb_ep_alloc_request(&udc->ep0in.ep, GFP_ATOMIC); - if (udc->status == NULL) { - usb_ep_disable(&udc->ep0out.ep); - retval = -ENOMEM; - } - } spin_lock(udc->lock); done: @@ -1843,7 +1833,8 @@ __releases(mEp->lock) __acquires(mEp->lock) { struct ci13xxx_req *mReq, *mReqTemp; - int retval; + struct ci13xxx_ep *mEpTemp = mEp; + int uninitialized_var(retval); trace("%p", mEp); @@ -1859,12 +1850,15 @@ __acquires(mEp->lock) dbg_done(_usb_addr(mEp), mReq->ptr->token, retval); if (mReq->req.complete != NULL) { spin_unlock(mEp->lock); - mReq->req.complete(&mEp->ep, &mReq->req); + if ((mEp->type == USB_ENDPOINT_XFER_CONTROL) && + mReq->req.length) + mEpTemp = &_udc->ep0in; + mReq->req.complete(&mEpTemp->ep, &mReq->req); spin_lock(mEp->lock); } } - if (retval == EBUSY) + if (retval == -EBUSY) retval = 0; if (retval < 0) dbg_event(_usb_addr(mEp), "DONE", retval); @@ -1894,7 +1888,7 @@ __acquires(udc->lock) for (i = 0; i < hw_ep_max; i++) { struct ci13xxx_ep *mEp = &udc->ci13xxx_ep[i]; - int type, num, err = -EINVAL; + int type, num, dir, err = -EINVAL; struct usb_ctrlrequest req; if (mEp->desc == NULL) @@ -1952,7 +1946,10 @@ __acquires(udc->lock) if (req.wLength != 0) break; num = le16_to_cpu(req.wIndex); + dir = num & USB_ENDPOINT_DIR_MASK; num &= USB_ENDPOINT_NUMBER_MASK; + if (dir) /* TX */ + num += hw_ep_max/2; if (!udc->ci13xxx_ep[num].wedge) { spin_unlock(udc->lock); err = usb_ep_clear_halt( @@ -2001,7 +1998,10 @@ __acquires(udc->lock) if (req.wLength != 0) break; num = le16_to_cpu(req.wIndex); + dir = num & USB_ENDPOINT_DIR_MASK; num &= USB_ENDPOINT_NUMBER_MASK; + if (dir) /* TX */ + num += hw_ep_max/2; spin_unlock(udc->lock); err = usb_ep_set_halt(&udc->ci13xxx_ep[num].ep); @@ -2110,7 +2110,12 @@ static int ep_enable(struct usb_ep *ep, (mEp->ep.maxpacket << ffs_nr(QH_MAX_PKT)) & QH_MAX_PKT; mEp->qh.ptr->td.next |= TD_TERMINATE; /* needed? */ - retval |= hw_ep_enable(mEp->num, mEp->dir, mEp->type); + /* + * Enable endpoints in the HW other than ep0 as ep0 + * is always enabled + */ + if (mEp->num) + retval |= hw_ep_enable(mEp->num, mEp->dir, mEp->type); spin_unlock_irqrestore(mEp->lock, flags); return retval; @@ -2242,11 +2247,15 @@ static int ep_queue(struct usb_ep *ep, struct usb_request *req, spin_lock_irqsave(mEp->lock, flags); - if (mEp->type == USB_ENDPOINT_XFER_CONTROL && - !list_empty(&mEp->qh.queue)) { - _ep_nuke(mEp); - retval = -EOVERFLOW; - warn("endpoint ctrl %X nuked", _usb_addr(mEp)); + if (mEp->type == USB_ENDPOINT_XFER_CONTROL) { + if (req->length) + mEp = (_udc->ep0_dir == RX) ? + &_udc->ep0out : &_udc->ep0in; + if (!list_empty(&mEp->qh.queue)) { + _ep_nuke(mEp); + retval = -EOVERFLOW; + warn("endpoint ctrl %X nuked", _usb_addr(mEp)); + } } /* first nuke then test link, e.g. previous status has not sent */ @@ -2497,6 +2506,15 @@ out: return ret; } +static int ci13xxx_vbus_draw(struct usb_gadget *_gadget, unsigned mA) +{ + struct ci13xxx *udc = container_of(_gadget, struct ci13xxx, gadget); + + if (udc->transceiver) + return otg_set_power(udc->transceiver, mA); + return -ENOTSUPP; +} + /** * Device operations part of the API to the USB controller hardware, * which don't involve endpoints (or i/o) @@ -2505,6 +2523,7 @@ out: static const struct usb_gadget_ops usb_gadget_ops = { .vbus_session = ci13xxx_vbus_session, .wakeup = ci13xxx_wakeup, + .vbus_draw = ci13xxx_vbus_draw, }; /** @@ -2595,6 +2614,14 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, } if (retval) goto done; + spin_unlock_irqrestore(udc->lock, flags); + retval = usb_ep_enable(&udc->ep0out.ep, &ctrl_endpt_out_desc); + if (retval) + return retval; + retval = usb_ep_enable(&udc->ep0in.ep, &ctrl_endpt_in_desc); + if (retval) + return retval; + spin_lock_irqsave(udc->lock, flags); udc->gadget.ep0 = &udc->ep0in.ep; /* bind gadget */ diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index 82314ed..5cbb1a4 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -461,12 +461,23 @@ static int set_config(struct usb_composite_dev *cdev, reset_config(cdev); goto done; } + + if (result == USB_GADGET_DELAYED_STATUS) { + DBG(cdev, + "%s: interface %d (%s) requested delayed status\n", + __func__, tmp, f->name); + cdev->delayed_status++; + DBG(cdev, "delayed_status count %d\n", + cdev->delayed_status); + } } /* when we return, be sure our power usage is valid */ power = c->bMaxPower ? (2 * c->bMaxPower) : CONFIG_USB_GADGET_VBUS_DRAW; done: usb_gadget_vbus_draw(gadget, power); + if (result >= 0 && cdev->delayed_status) + result = USB_GADGET_DELAYED_STATUS; return result; } @@ -895,6 +906,14 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) if (w_value && !f->set_alt) break; value = f->set_alt(f, w_index, w_value); + if (value == USB_GADGET_DELAYED_STATUS) { + DBG(cdev, + "%s: interface %d (%s) requested delayed status\n", + __func__, intf, f->name); + cdev->delayed_status++; + DBG(cdev, "delayed_status count %d\n", + cdev->delayed_status); + } break; case USB_REQ_GET_INTERFACE: if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) @@ -958,7 +977,7 @@ unknown: } /* respond with data transfer before status phase? */ - if (value >= 0) { + if (value >= 0 && value != USB_GADGET_DELAYED_STATUS) { req->length = value; req->zero = value < w_length; value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC); @@ -967,6 +986,10 @@ unknown: req->status = 0; composite_setup_complete(gadget->ep0, req); } + } else if (value == USB_GADGET_DELAYED_STATUS && w_length != 0) { + WARN(cdev, + "%s: Delayed status not supported for w_length != 0", + __func__); } done: @@ -1289,3 +1312,40 @@ void usb_composite_unregister(struct usb_composite_driver *driver) return; usb_gadget_unregister_driver(&composite_driver); } + +/** + * usb_composite_setup_continue() - Continue with the control transfer + * @cdev: the composite device who's control transfer was kept waiting + * + * This function must be called by the USB function driver to continue + * with the control transfer's data/status stage in case it had requested to + * delay the data/status stages. A USB function's setup handler (e.g. set_alt()) + * can request the composite framework to delay the setup request's data/status + * stages by returning USB_GADGET_DELAYED_STATUS. + */ +void usb_composite_setup_continue(struct usb_composite_dev *cdev) +{ + int value; + struct usb_request *req = cdev->req; + unsigned long flags; + + DBG(cdev, "%s\n", __func__); + spin_lock_irqsave(&cdev->lock, flags); + + if (cdev->delayed_status == 0) { + WARN(cdev, "%s: Unexpected call\n", __func__); + + } else if (--cdev->delayed_status == 0) { + DBG(cdev, "%s: Completing delayed status\n", __func__); + req->length = 0; + value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); + if (value < 0) { + DBG(cdev, "ep_queue --> %d\n", value); + req->status = 0; + composite_setup_complete(cdev->gadget->ep0, req); + } + } + + spin_unlock_irqrestore(&cdev->lock, flags); +} + diff --git a/drivers/usb/gadget/dbgp.c b/drivers/usb/gadget/dbgp.c index e5ac8a3..dbe92ee 100644 --- a/drivers/usb/gadget/dbgp.c +++ b/drivers/usb/gadget/dbgp.c @@ -261,8 +261,8 @@ static int __init dbgp_configure_endpoints(struct usb_gadget *gadget) o_desc.wMaxPacketSize = __constant_cpu_to_le16(USB_DEBUG_MAX_PACKET_SIZE); - dbg_desc.bDebugInEndpoint = i_desc.bEndpointAddress & 0x7f; - dbg_desc.bDebugOutEndpoint = o_desc.bEndpointAddress & 0x7f; + dbg_desc.bDebugInEndpoint = i_desc.bEndpointAddress; + dbg_desc.bDebugOutEndpoint = o_desc.bEndpointAddress; #ifdef CONFIG_USB_G_DBGP_SERIAL dbgp.serial->in = dbgp.i_ep; @@ -312,6 +312,7 @@ static int __init dbgp_bind(struct usb_gadget *gadget) dbgp.req->length = DBGP_REQ_EP0_LEN; gadget->ep0->driver_data = gadget; + device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; #ifdef CONFIG_USB_G_DBGP_SERIAL dbgp.serial = kzalloc(sizeof(struct gserial), GFP_KERNEL); @@ -350,9 +351,9 @@ static int dbgp_setup(struct usb_gadget *gadget, u8 request = ctrl->bRequest; u16 value = le16_to_cpu(ctrl->wValue); u16 length = le16_to_cpu(ctrl->wLength); - int err = 0; - void *data; - u16 len; + int err = -EOPNOTSUPP; + void *data = NULL; + u16 len = 0; gadget->ep0->driver_data = gadget; @@ -371,10 +372,9 @@ static int dbgp_setup(struct usb_gadget *gadget, default: goto fail; } + err = 0; } else if (request == USB_REQ_SET_FEATURE && value == USB_DEVICE_DEBUG_MODE) { - len = 0; - data = NULL; dev_dbg(&dbgp.gadget->dev, "setup: feat debug\n"); #ifdef CONFIG_USB_G_DBGP_PRINTK err = dbgp_enable_ep(); diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c index 3214ca3..61ff927 100644 --- a/drivers/usb/gadget/dummy_hcd.c +++ b/drivers/usb/gadget/dummy_hcd.c @@ -892,10 +892,11 @@ static int dummy_udc_probe (struct platform_device *pdev) return rc; } - platform_set_drvdata (pdev, dum); rc = device_create_file (&dum->gadget.dev, &dev_attr_function); if (rc < 0) device_unregister (&dum->gadget.dev); + else + platform_set_drvdata(pdev, dum); return rc; } @@ -1995,11 +1996,29 @@ static int __init init (void) retval = platform_device_add(the_hcd_pdev); if (retval < 0) goto err_add_hcd; + if (!the_controller) { + /* + * The hcd was added successfully but its probe function failed + * for some reason. + */ + retval = -EINVAL; + goto err_add_udc; + } retval = platform_device_add(the_udc_pdev); if (retval < 0) goto err_add_udc; + if (!platform_get_drvdata(the_udc_pdev)) { + /* + * The udc was added successfully but its probe function failed + * for some reason. + */ + retval = -EINVAL; + goto err_probe_udc; + } return retval; +err_probe_udc: + platform_device_del(the_udc_pdev); err_add_udc: platform_device_del(the_hcd_pdev); err_add_hcd: diff --git a/drivers/usb/gadget/f_audio.c b/drivers/usb/gadget/f_audio.c index 0111f8a..8ee330a 100644 --- a/drivers/usb/gadget/f_audio.c +++ b/drivers/usb/gadget/f_audio.c @@ -177,7 +177,7 @@ static struct uac_format_type_i_discrete_descriptor_1 as_type_i_desc = { }; /* Standard ISO OUT Endpoint Descriptor */ -static struct usb_endpoint_descriptor as_out_ep_desc __initdata = { +static struct usb_endpoint_descriptor as_out_ep_desc = { .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT, diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index 6d8e533..efb58f9 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -347,6 +347,7 @@ struct fsg_operations { /* Data shared by all the FSG instances. */ struct fsg_common { struct usb_gadget *gadget; + struct usb_composite_dev *cdev; struct fsg_dev *fsg, *new_fsg; wait_queue_head_t fsg_wait; @@ -613,6 +614,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 +1590,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 +1677,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 +1689,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; @@ -1910,7 +1884,7 @@ static int check_command(struct fsg_common *common, int cmnd_size, common->lun, lun); /* Check the LUN */ - if (common->lun >= 0 && common->lun < common->nluns) { + if (common->lun < common->nluns) { curlun = &common->luns[common->lun]; common->curlun = curlun; if (common->cmnd[0] != REQUEST_SENSE) { @@ -2468,7 +2442,7 @@ static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) struct fsg_dev *fsg = fsg_from_func(f); fsg->common->new_fsg = fsg; raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE); - return 0; + return USB_GADGET_DELAYED_STATUS; } static void fsg_disable(struct usb_function *f) @@ -2604,6 +2578,8 @@ static void handle_exception(struct fsg_common *common) case FSG_STATE_CONFIG_CHANGE: do_set_interface(common, common->new_fsg); + if (common->new_fsg) + usb_composite_setup_continue(common->cdev); break; case FSG_STATE_EXIT: @@ -2774,6 +2750,7 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common, common->gadget = gadget; common->ep0 = gadget->ep0; common->ep0req = cdev->req; + common->cdev = cdev; /* Maybe allocate device-global string IDs, and patch descriptors */ if (fsg_strings[FSG_STRING_INTERFACE].id == 0) { @@ -2800,6 +2777,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/f_rndis.c b/drivers/usb/gadget/f_rndis.c index 882484a..fa12ec8 100644 --- a/drivers/usb/gadget/f_rndis.c +++ b/drivers/usb/gadget/f_rndis.c @@ -420,8 +420,7 @@ rndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) */ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8) | USB_CDC_SEND_ENCAPSULATED_COMMAND: - if (w_length > req->length || w_value - || w_index != rndis->ctrl_id) + if (w_value || w_index != rndis->ctrl_id) goto invalid; /* read the request; process it later */ value = w_length; diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c index a6eacb5..0360f56 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; @@ -2314,7 +2285,7 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size, fsg->lun = lun; // Use LUN from the command /* Check the LUN */ - if (fsg->lun >= 0 && fsg->lun < fsg->nluns) { + if (fsg->lun < fsg->nluns) { fsg->curlun = curlun = &fsg->luns[fsg->lun]; if (fsg->cmnd[0] != REQUEST_SENSE) { curlun->sense_data = SS_NO_SENSE; diff --git a/drivers/usb/gadget/fsl_qe_udc.h b/drivers/usb/gadget/fsl_qe_udc.h index e35e24f..1da5fb0 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/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c index 07499c1..2cd9a60 100644 --- a/drivers/usb/gadget/fsl_udc_core.c +++ b/drivers/usb/gadget/fsl_udc_core.c @@ -1,12 +1,13 @@ /* - * Copyright (C) 2004-2007 Freescale Semicondutor, Inc. All rights reserved. + * Copyright (C) 2004-2007,2011 Freescale Semiconductor, Inc. + * All rights reserved. * * Author: Li Yang <leoli@freescale.com> * Jiang Bo <tanya.jiang@freescale.com> * * Description: * Freescale high-speed USB SOC DR module device controller driver. - * This can be found on MPC8349E/MPC8313E cpus. + * This can be found on MPC8349E/MPC8313E/MPC5121E cpus. * The driver is previously named as mpc_udc. Based on bare board * code from Dave Liu and Shlomi Gridish. * @@ -45,6 +46,7 @@ #include <asm/system.h> #include <asm/unaligned.h> #include <asm/dma.h> +#include <asm/cacheflush.h> #include "fsl_usb2_udc.h" @@ -77,12 +79,64 @@ fsl_ep0_desc = { static void fsl_ep_fifo_flush(struct usb_ep *_ep); #ifdef CONFIG_PPC32 -#define fsl_readl(addr) in_le32(addr) -#define fsl_writel(val32, addr) out_le32(addr, val32) -#else +/* + * On some SoCs, the USB controller registers can be big or little endian, + * depending on the version of the chip. In order to be able to run the + * same kernel binary on 2 different versions of an SoC, the BE/LE decision + * must be made at run time. _fsl_readl and fsl_writel are pointers to the + * BE or LE readl() and writel() functions, and fsl_readl() and fsl_writel() + * call through those pointers. Platform code for SoCs that have BE USB + * registers should set pdata->big_endian_mmio flag. + * + * This also applies to controller-to-cpu accessors for the USB descriptors, + * since their endianness is also SoC dependant. Platform code for SoCs that + * have BE USB descriptors should set pdata->big_endian_desc flag. + */ +static u32 _fsl_readl_be(const unsigned __iomem *p) +{ + return in_be32(p); +} + +static u32 _fsl_readl_le(const unsigned __iomem *p) +{ + return in_le32(p); +} + +static void _fsl_writel_be(u32 v, unsigned __iomem *p) +{ + out_be32(p, v); +} + +static void _fsl_writel_le(u32 v, unsigned __iomem *p) +{ + out_le32(p, v); +} + +static u32 (*_fsl_readl)(const unsigned __iomem *p); +static void (*_fsl_writel)(u32 v, unsigned __iomem *p); + +#define fsl_readl(p) (*_fsl_readl)((p)) +#define fsl_writel(v, p) (*_fsl_writel)((v), (p)) + +static inline u32 cpu_to_hc32(const u32 x) +{ + return udc_controller->pdata->big_endian_desc + ? (__force u32)cpu_to_be32(x) + : (__force u32)cpu_to_le32(x); +} + +static inline u32 hc32_to_cpu(const u32 x) +{ + return udc_controller->pdata->big_endian_desc + ? be32_to_cpu((__force __be32)x) + : le32_to_cpu((__force __le32)x); +} +#else /* !CONFIG_PPC32 */ #define fsl_readl(addr) readl(addr) #define fsl_writel(val32, addr) writel(val32, addr) -#endif +#define cpu_to_hc32(x) cpu_to_le32(x) +#define hc32_to_cpu(x) le32_to_cpu(x) +#endif /* CONFIG_PPC32 */ /******************************************************************** * Internal Used Function @@ -177,7 +231,8 @@ static void nuke(struct fsl_ep *ep, int status) static int dr_controller_setup(struct fsl_udc *udc) { - unsigned int tmp, portctrl; + unsigned int tmp, portctrl, ep_num; + unsigned int max_no_of_ep; #ifndef CONFIG_ARCH_MXC unsigned int ctrl; #endif @@ -226,9 +281,12 @@ static int dr_controller_setup(struct fsl_udc *udc) /* Set the controller as device mode */ tmp = fsl_readl(&dr_regs->usbmode); + tmp &= ~USB_MODE_CTRL_MODE_MASK; /* clear mode bits */ tmp |= USB_MODE_CTRL_MODE_DEVICE; /* Disable Setup Lockout */ tmp |= USB_MODE_SETUP_LOCK_OFF; + if (udc->pdata->es) + tmp |= USB_MODE_ES; fsl_writel(tmp, &dr_regs->usbmode); /* Clear the setup status */ @@ -242,22 +300,34 @@ static int dr_controller_setup(struct fsl_udc *udc) udc->ep_qh, (int)tmp, fsl_readl(&dr_regs->endpointlistaddr)); + max_no_of_ep = (0x0000001F & fsl_readl(&dr_regs->dccparams)); + for (ep_num = 1; ep_num < max_no_of_ep; ep_num++) { + tmp = fsl_readl(&dr_regs->endptctrl[ep_num]); + tmp &= ~(EPCTRL_TX_TYPE | EPCTRL_RX_TYPE); + tmp |= (EPCTRL_EP_TYPE_BULK << EPCTRL_TX_EP_TYPE_SHIFT) + | (EPCTRL_EP_TYPE_BULK << EPCTRL_RX_EP_TYPE_SHIFT); + fsl_writel(tmp, &dr_regs->endptctrl[ep_num]); + } /* Config control enable i/o output, cpu endian register */ #ifndef CONFIG_ARCH_MXC - ctrl = __raw_readl(&usb_sys_regs->control); - ctrl |= USB_CTRL_IOENB; - __raw_writel(ctrl, &usb_sys_regs->control); + if (udc->pdata->have_sysif_regs) { + ctrl = __raw_readl(&usb_sys_regs->control); + ctrl |= USB_CTRL_IOENB; + __raw_writel(ctrl, &usb_sys_regs->control); + } #endif #if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE) /* Turn on cache snooping hardware, since some PowerPC platforms * wholly rely on hardware to deal with cache coherent. */ - /* Setup Snooping for all the 4GB space */ - tmp = SNOOP_SIZE_2GB; /* starts from 0x0, size 2G */ - __raw_writel(tmp, &usb_sys_regs->snoop1); - tmp |= 0x80000000; /* starts from 0x8000000, size 2G */ - __raw_writel(tmp, &usb_sys_regs->snoop2); + if (udc->pdata->have_sysif_regs) { + /* Setup Snooping for all the 4GB space */ + tmp = SNOOP_SIZE_2GB; /* starts from 0x0, size 2G */ + __raw_writel(tmp, &usb_sys_regs->snoop1); + tmp |= 0x80000000; /* starts from 0x8000000, size 2G */ + __raw_writel(tmp, &usb_sys_regs->snoop2); + } #endif return 0; @@ -293,6 +363,19 @@ static void dr_controller_stop(struct fsl_udc *udc) { unsigned int tmp; + pr_debug("%s\n", __func__); + + /* if we're in OTG mode, and the Host is currently using the port, + * stop now and don't rip the controller out from under the + * ehci driver + */ + if (udc->gadget.is_otg) { + if (!(fsl_readl(&dr_regs->otgsc) & OTGSC_STS_USB_ID)) { + pr_debug("udc: Leaving early\n"); + return; + } + } + /* disable all INTR */ fsl_writel(0, &dr_regs->usbintr); @@ -318,12 +401,14 @@ static void dr_ep_setup(unsigned char ep_num, unsigned char dir, if (ep_num) tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST; tmp_epctrl |= EPCTRL_TX_ENABLE; + tmp_epctrl &= ~EPCTRL_TX_TYPE; tmp_epctrl |= ((unsigned int)(ep_type) << EPCTRL_TX_EP_TYPE_SHIFT); } else { if (ep_num) tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST; tmp_epctrl |= EPCTRL_RX_ENABLE; + tmp_epctrl &= ~EPCTRL_RX_TYPE; tmp_epctrl |= ((unsigned int)(ep_type) << EPCTRL_RX_EP_TYPE_SHIFT); } @@ -409,7 +494,7 @@ static void struct_ep_qh_setup(struct fsl_udc *udc, unsigned char ep_num, if (zlt) tmp |= EP_QUEUE_HEAD_ZLT_SEL; - p_QH->max_pkt_length = cpu_to_le32(tmp); + p_QH->max_pkt_length = cpu_to_hc32(tmp); p_QH->next_dtd_ptr = 1; p_QH->size_ioc_int_sts = 0; } @@ -546,10 +631,13 @@ static int fsl_ep_disable(struct usb_ep *_ep) /* disable ep on controller */ ep_num = ep_index(ep); epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]); - if (ep_is_in(ep)) - epctrl &= ~EPCTRL_TX_ENABLE; - else - epctrl &= ~EPCTRL_RX_ENABLE; + if (ep_is_in(ep)) { + epctrl &= ~(EPCTRL_TX_ENABLE | EPCTRL_TX_TYPE); + epctrl |= EPCTRL_EP_TYPE_BULK << EPCTRL_TX_EP_TYPE_SHIFT; + } else { + epctrl &= ~(EPCTRL_RX_ENABLE | EPCTRL_TX_TYPE); + epctrl |= EPCTRL_EP_TYPE_BULK << EPCTRL_RX_EP_TYPE_SHIFT; + } fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]); udc = (struct fsl_udc *)ep->udc; @@ -616,7 +704,7 @@ static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req) struct fsl_req *lastreq; lastreq = list_entry(ep->queue.prev, struct fsl_req, queue); lastreq->tail->next_td_ptr = - cpu_to_le32(req->head->td_dma & DTD_ADDR_MASK); + cpu_to_hc32(req->head->td_dma & DTD_ADDR_MASK); /* Read prime bit, if 1 goto done */ if (fsl_readl(&dr_regs->endpointprime) & bitmask) goto out; @@ -641,10 +729,10 @@ static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req) /* Write dQH next pointer and terminate bit to 0 */ temp = req->head->td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK; - dQH->next_dtd_ptr = cpu_to_le32(temp); + dQH->next_dtd_ptr = cpu_to_hc32(temp); /* Clear active and halt bit */ - temp = cpu_to_le32(~(EP_QUEUE_HEAD_STATUS_ACTIVE + temp = cpu_to_hc32(~(EP_QUEUE_HEAD_STATUS_ACTIVE | EP_QUEUE_HEAD_STATUS_HALT)); dQH->size_ioc_int_sts &= temp; @@ -682,17 +770,17 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length, dtd->td_dma = *dma; /* Clear reserved field */ - swap_temp = cpu_to_le32(dtd->size_ioc_sts); + swap_temp = hc32_to_cpu(dtd->size_ioc_sts); swap_temp &= ~DTD_RESERVED_FIELDS; - dtd->size_ioc_sts = cpu_to_le32(swap_temp); + dtd->size_ioc_sts = cpu_to_hc32(swap_temp); /* Init all of buffer page pointers */ swap_temp = (u32) (req->req.dma + req->req.actual); - dtd->buff_ptr0 = cpu_to_le32(swap_temp); - dtd->buff_ptr1 = cpu_to_le32(swap_temp + 0x1000); - dtd->buff_ptr2 = cpu_to_le32(swap_temp + 0x2000); - dtd->buff_ptr3 = cpu_to_le32(swap_temp + 0x3000); - dtd->buff_ptr4 = cpu_to_le32(swap_temp + 0x4000); + dtd->buff_ptr0 = cpu_to_hc32(swap_temp); + dtd->buff_ptr1 = cpu_to_hc32(swap_temp + 0x1000); + dtd->buff_ptr2 = cpu_to_hc32(swap_temp + 0x2000); + dtd->buff_ptr3 = cpu_to_hc32(swap_temp + 0x3000); + dtd->buff_ptr4 = cpu_to_hc32(swap_temp + 0x4000); req->req.actual += *length; @@ -716,7 +804,7 @@ static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length, if (*is_last && !req->req.no_interrupt) swap_temp |= DTD_IOC; - dtd->size_ioc_sts = cpu_to_le32(swap_temp); + dtd->size_ioc_sts = cpu_to_hc32(swap_temp); mb(); @@ -743,7 +831,7 @@ static int fsl_req_to_dtd(struct fsl_req *req) is_first = 0; req->head = dtd; } else { - last_dtd->next_td_ptr = cpu_to_le32(dma); + last_dtd->next_td_ptr = cpu_to_hc32(dma); last_dtd->next_td_virt = dtd; } last_dtd = dtd; @@ -751,7 +839,7 @@ static int fsl_req_to_dtd(struct fsl_req *req) req->dtd_count++; } while (!is_last); - dtd->next_td_ptr = cpu_to_le32(DTD_NEXT_TERMINATE); + dtd->next_td_ptr = cpu_to_hc32(DTD_NEXT_TERMINATE); req->tail = dtd; @@ -962,6 +1050,36 @@ out: return status; } +static int fsl_ep_fifo_status(struct usb_ep *_ep) +{ + struct fsl_ep *ep; + struct fsl_udc *udc; + int size = 0; + u32 bitmask; + struct ep_queue_head *d_qh; + + ep = container_of(_ep, struct fsl_ep, ep); + if (!_ep || (!ep->desc && ep_index(ep) != 0)) + return -ENODEV; + + udc = (struct fsl_udc *)ep->udc; + + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + d_qh = &ep->udc->ep_qh[ep_index(ep) * 2 + ep_is_in(ep)]; + + bitmask = (ep_is_in(ep)) ? (1 << (ep_index(ep) + 16)) : + (1 << (ep_index(ep))); + + if (fsl_readl(&dr_regs->endptstatus) & bitmask) + size = (d_qh->size_ioc_int_sts & DTD_PACKET_SIZE) + >> DTD_LENGTH_BIT_POS; + + pr_debug("%s %u\n", __func__, size); + return size; +} + static void fsl_ep_fifo_flush(struct usb_ep *_ep) { struct fsl_ep *ep; @@ -1014,6 +1132,7 @@ static struct usb_ep_ops fsl_ep_ops = { .dequeue = fsl_ep_dequeue, .set_halt = fsl_ep_set_halt, + .fifo_status = fsl_ep_fifo_status, .fifo_flush = fsl_ep_fifo_flush, /* flush fifo */ }; @@ -1228,6 +1347,10 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value, req = udc->status_req; /* Fill in the reqest structure */ *((u16 *) req->req.buf) = cpu_to_le16(tmp); + + /* flush cache for the req buffer */ + flush_dcache_range((u32)req->req.buf, (u32)req->req.buf + 8); + req->ep = ep; req->req.length = 2; req->req.status = -EINPROGRESS; @@ -1280,6 +1403,7 @@ static void setup_received_irq(struct fsl_udc *udc, /* Status phase from udc */ { int rc = -EOPNOTSUPP; + u16 ptc = 0; if ((setup->bRequestType & (USB_RECIP_MASK | USB_TYPE_MASK)) == (USB_RECIP_ENDPOINT | USB_TYPE_STANDARD)) { @@ -1301,17 +1425,19 @@ static void setup_received_irq(struct fsl_udc *udc, | USB_TYPE_STANDARD)) { /* Note: The driver has not include OTG support yet. * This will be set when OTG support is added */ - if (!gadget_is_otg(&udc->gadget)) - break; - else if (setup->bRequest == USB_DEVICE_B_HNP_ENABLE) - udc->gadget.b_hnp_enable = 1; - else if (setup->bRequest == USB_DEVICE_A_HNP_SUPPORT) - udc->gadget.a_hnp_support = 1; - else if (setup->bRequest == - USB_DEVICE_A_ALT_HNP_SUPPORT) - udc->gadget.a_alt_hnp_support = 1; - else - break; + if (wValue == USB_DEVICE_TEST_MODE) + ptc = wIndex >> 8; + else if (gadget_is_otg(&udc->gadget)) { + if (setup->bRequest == + USB_DEVICE_B_HNP_ENABLE) + udc->gadget.b_hnp_enable = 1; + else if (setup->bRequest == + USB_DEVICE_A_HNP_SUPPORT) + udc->gadget.a_hnp_support = 1; + else if (setup->bRequest == + USB_DEVICE_A_ALT_HNP_SUPPORT) + udc->gadget.a_alt_hnp_support = 1; + } rc = 0; } else break; @@ -1320,6 +1446,15 @@ static void setup_received_irq(struct fsl_udc *udc, if (ep0_prime_status(udc, EP_DIR_IN)) ep0stall(udc); } + if (ptc) { + u32 tmp; + + mdelay(10); + tmp = fsl_readl(&dr_regs->portsc1) | (ptc << 16); + fsl_writel(tmp, &dr_regs->portsc1); + printk(KERN_INFO "udc: switch to test mode %d.\n", ptc); + } + return; } @@ -1394,6 +1529,7 @@ static void tripwire_handler(struct fsl_udc *udc, u8 ep_num, u8 *buffer_ptr) { u32 temp; struct ep_queue_head *qh; + struct fsl_usb2_platform_data *pdata = udc->pdata; qh = &udc->ep_qh[ep_num * 2 + EP_DIR_OUT]; @@ -1408,7 +1544,16 @@ static void tripwire_handler(struct fsl_udc *udc, u8 ep_num, u8 *buffer_ptr) fsl_writel(temp | USB_CMD_SUTW, &dr_regs->usbcmd); /* Copy the setup packet to local buffer */ - memcpy(buffer_ptr, (u8 *) qh->setup_buffer, 8); + if (pdata->le_setup_buf) { + u32 *p = (u32 *)buffer_ptr; + u32 *s = (u32 *)qh->setup_buffer; + + /* Convert little endian setup buffer to CPU endian */ + *p++ = le32_to_cpu(*s++); + *p = le32_to_cpu(*s); + } else { + memcpy(buffer_ptr, (u8 *) qh->setup_buffer, 8); + } } while (!(fsl_readl(&dr_regs->usbcmd) & USB_CMD_SUTW)); /* Clear Setup Tripwire */ @@ -1432,19 +1577,19 @@ static int process_ep_req(struct fsl_udc *udc, int pipe, actual = curr_req->req.length; for (j = 0; j < curr_req->dtd_count; j++) { - remaining_length = (le32_to_cpu(curr_td->size_ioc_sts) + remaining_length = (hc32_to_cpu(curr_td->size_ioc_sts) & DTD_PACKET_SIZE) >> DTD_LENGTH_BIT_POS; actual -= remaining_length; - if ((errors = le32_to_cpu(curr_td->size_ioc_sts) & - DTD_ERROR_MASK)) { + errors = hc32_to_cpu(curr_td->size_ioc_sts); + if (errors & DTD_ERROR_MASK) { if (errors & DTD_STATUS_HALTED) { ERR("dTD error %08x QH=%d\n", errors, pipe); /* Clear the errors and Halt condition */ - tmp = le32_to_cpu(curr_qh->size_ioc_int_sts); + tmp = hc32_to_cpu(curr_qh->size_ioc_int_sts); tmp &= ~errors; - curr_qh->size_ioc_int_sts = cpu_to_le32(tmp); + curr_qh->size_ioc_int_sts = cpu_to_hc32(tmp); status = -EPIPE; /* FIXME: continue with next queued TD? */ @@ -1462,7 +1607,7 @@ static int process_ep_req(struct fsl_udc *udc, int pipe, ERR("Unknown error has occurred (0x%x)!\n", errors); - } else if (le32_to_cpu(curr_td->size_ioc_sts) + } else if (hc32_to_cpu(curr_td->size_ioc_sts) & DTD_STATUS_ACTIVE) { VDBG("Request not complete"); status = REQ_UNCOMPLETE; @@ -1551,6 +1696,9 @@ static void port_change_irq(struct fsl_udc *udc) { u32 speed; + if (udc->bus_reset) + udc->bus_reset = 0; + /* Bus resetting is finished */ if (!(fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET)) { /* Get the speed */ @@ -1658,6 +1806,8 @@ static void reset_irq(struct fsl_udc *udc) if (fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET) { VDBG("Bus reset"); + /* Bus is reseting */ + udc->bus_reset = 1; /* Reset all the queues, include XD, dTD, EP queue * head and TR Queue */ reset_queues(udc); @@ -1735,6 +1885,7 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc) /* Reset Received */ if (irq_src & USB_STS_RESET) { + VDBG("reset int"); reset_irq(udc); status = IRQ_HANDLED; } @@ -1792,11 +1943,30 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, goto out; } - /* Enable DR IRQ reg and Set usbcmd reg Run bit */ - dr_controller_run(udc_controller); - udc_controller->usb_state = USB_STATE_ATTACHED; - udc_controller->ep0_state = WAIT_FOR_SETUP; - udc_controller->ep0_dir = 0; + if (udc_controller->transceiver) { + /* Suspend the controller until OTG enable it */ + udc_controller->stopped = 1; + printk(KERN_INFO "Suspend udc for OTG auto detect\n"); + + /* connect to bus through transceiver */ + if (udc_controller->transceiver) { + retval = otg_set_peripheral(udc_controller->transceiver, + &udc_controller->gadget); + if (retval < 0) { + ERR("can't bind to transceiver\n"); + driver->unbind(&udc_controller->gadget); + udc_controller->gadget.dev.driver = 0; + udc_controller->driver = 0; + return retval; + } + } + } else { + /* Enable DR IRQ reg and set USBCMD reg Run bit */ + dr_controller_run(udc_controller); + udc_controller->usb_state = USB_STATE_ATTACHED; + udc_controller->ep0_state = WAIT_FOR_SETUP; + udc_controller->ep0_dir = 0; + } printk(KERN_INFO "%s: bind to driver %s\n", udc_controller->gadget.name, driver->driver.name); @@ -2044,16 +2214,18 @@ static int fsl_proc_read(char *page, char **start, off_t off, int count, next += t; #ifndef CONFIG_ARCH_MXC - tmp_reg = usb_sys_regs->snoop1; - t = scnprintf(next, size, "Snoop1 Reg : = [0x%x]\n\n", tmp_reg); - size -= t; - next += t; + if (udc->pdata->have_sysif_regs) { + tmp_reg = usb_sys_regs->snoop1; + t = scnprintf(next, size, "Snoop1 Reg : = [0x%x]\n\n", tmp_reg); + size -= t; + next += t; - tmp_reg = usb_sys_regs->control; - t = scnprintf(next, size, "General Control Reg : = [0x%x]\n\n", - tmp_reg); - size -= t; - next += t; + tmp_reg = usb_sys_regs->control; + t = scnprintf(next, size, "General Control Reg : = [0x%x]\n\n", + tmp_reg); + size -= t; + next += t; + } #endif /* ------fsl_udc, fsl_ep, fsl_request structure information ----- */ @@ -2233,6 +2405,7 @@ static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index, */ static int __init fsl_udc_probe(struct platform_device *pdev) { + struct fsl_usb2_platform_data *pdata; struct resource *res; int ret = -ENODEV; unsigned int i; @@ -2249,20 +2422,35 @@ static int __init fsl_udc_probe(struct platform_device *pdev) return -ENOMEM; } + pdata = pdev->dev.platform_data; + udc_controller->pdata = pdata; spin_lock_init(&udc_controller->lock); udc_controller->stopped = 1; +#ifdef CONFIG_USB_OTG + if (pdata->operating_mode == FSL_USB2_DR_OTG) { + udc_controller->transceiver = otg_get_transceiver(); + if (!udc_controller->transceiver) { + ERR("Can't find OTG driver!\n"); + ret = -ENODEV; + goto err_kfree; + } + } +#endif + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { ret = -ENXIO; goto err_kfree; } - if (!request_mem_region(res->start, res->end - res->start + 1, - driver_name)) { - ERR("request mem region for %s failed\n", pdev->name); - ret = -EBUSY; - goto err_kfree; + if (pdata->operating_mode == FSL_USB2_DR_DEVICE) { + if (!request_mem_region(res->start, res->end - res->start + 1, + driver_name)) { + ERR("request mem region for %s failed\n", pdev->name); + ret = -EBUSY; + goto err_kfree; + } } dr_regs = ioremap(res->start, resource_size(res)); @@ -2271,9 +2459,29 @@ static int __init fsl_udc_probe(struct platform_device *pdev) goto err_release_mem_region; } + pdata->regs = (void *)dr_regs; + + /* + * do platform specific init: check the clock, grab/config pins, etc. + */ + if (pdata->init && pdata->init(pdev)) { + ret = -ENODEV; + goto err_iounmap_noclk; + } + + /* Set accessors only after pdata->init() ! */ + if (pdata->big_endian_mmio) { + _fsl_readl = _fsl_readl_be; + _fsl_writel = _fsl_writel_be; + } else { + _fsl_readl = _fsl_readl_le; + _fsl_writel = _fsl_writel_le; + } + #ifndef CONFIG_ARCH_MXC - usb_sys_regs = (struct usb_sys_interface *) - ((u32)dr_regs + USB_DR_SYS_OFFSET); + if (pdata->have_sysif_regs) + usb_sys_regs = (struct usb_sys_interface *) + ((u32)dr_regs + USB_DR_SYS_OFFSET); #endif /* Initialize USB clocks */ @@ -2313,9 +2521,11 @@ static int __init fsl_udc_probe(struct platform_device *pdev) goto err_free_irq; } - /* initialize usb hw reg except for regs for EP, - * leave usbintr reg untouched */ - dr_controller_setup(udc_controller); + if (!udc_controller->transceiver) { + /* initialize usb hw reg except for regs for EP, + * leave usbintr reg untouched */ + dr_controller_setup(udc_controller); + } fsl_udc_clk_finalize(pdev); @@ -2335,6 +2545,9 @@ static int __init fsl_udc_probe(struct platform_device *pdev) if (ret < 0) goto err_free_irq; + if (udc_controller->transceiver) + udc_controller->gadget.is_otg = 1; + /* setup QH and epctrl for ep0 */ ep0_setup(udc_controller); @@ -2373,11 +2586,14 @@ err_unregister: err_free_irq: free_irq(udc_controller->irq, udc_controller); err_iounmap: + if (pdata->exit) + pdata->exit(pdev); fsl_udc_clk_release(); err_iounmap_noclk: iounmap(dr_regs); err_release_mem_region: - release_mem_region(res->start, res->end - res->start + 1); + if (pdata->operating_mode == FSL_USB2_DR_DEVICE) + release_mem_region(res->start, res->end - res->start + 1); err_kfree: kfree(udc_controller); udc_controller = NULL; @@ -2390,6 +2606,7 @@ err_kfree: static int __exit fsl_udc_remove(struct platform_device *pdev) { struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; DECLARE_COMPLETION(done); @@ -2410,12 +2627,20 @@ static int __exit fsl_udc_remove(struct platform_device *pdev) dma_pool_destroy(udc_controller->td_pool); free_irq(udc_controller->irq, udc_controller); iounmap(dr_regs); - release_mem_region(res->start, res->end - res->start + 1); + if (pdata->operating_mode == FSL_USB2_DR_DEVICE) + release_mem_region(res->start, res->end - res->start + 1); device_unregister(&udc_controller->gadget.dev); /* free udc --wait for the release() finished */ wait_for_completion(&done); + /* + * do platform specific un-initialization: + * release iomux pins, etc. + */ + if (pdata->exit) + pdata->exit(pdev); + return 0; } @@ -2446,6 +2671,62 @@ static int fsl_udc_resume(struct platform_device *pdev) return 0; } +static int fsl_udc_otg_suspend(struct device *dev, pm_message_t state) +{ + struct fsl_udc *udc = udc_controller; + u32 mode, usbcmd; + + mode = fsl_readl(&dr_regs->usbmode) & USB_MODE_CTRL_MODE_MASK; + + pr_debug("%s(): mode 0x%x stopped %d\n", __func__, mode, udc->stopped); + + /* + * If the controller is already stopped, then this must be a + * PM suspend. Remember this fact, so that we will leave the + * controller stopped at PM resume time. + */ + if (udc->stopped) { + pr_debug("gadget already stopped, leaving early\n"); + udc->already_stopped = 1; + return 0; + } + + if (mode != USB_MODE_CTRL_MODE_DEVICE) { + pr_debug("gadget not in device mode, leaving early\n"); + return 0; + } + + /* stop the controller */ + usbcmd = fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP; + fsl_writel(usbcmd, &dr_regs->usbcmd); + + udc->stopped = 1; + + pr_info("USB Gadget suspended\n"); + + return 0; +} + +static int fsl_udc_otg_resume(struct device *dev) +{ + pr_debug("%s(): stopped %d already_stopped %d\n", __func__, + udc_controller->stopped, udc_controller->already_stopped); + + /* + * If the controller was stopped at suspend time, then + * don't resume it now. + */ + if (udc_controller->already_stopped) { + udc_controller->already_stopped = 0; + pr_debug("gadget was already stopped, leaving early\n"); + return 0; + } + + pr_info("USB Gadget resume\n"); + + return fsl_udc_resume(NULL); +} + /*------------------------------------------------------------------------- Register entry point for the peripheral controller driver --------------------------------------------------------------------------*/ @@ -2458,6 +2739,9 @@ static struct platform_driver udc_driver = { .driver = { .name = (char *)driver_name, .owner = THIS_MODULE, + /* udc suspend/resume called from OTG driver */ + .suspend = fsl_udc_otg_suspend, + .resume = fsl_udc_otg_resume, }, }; diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h index e88cce5..1d51be8 100644 --- a/drivers/usb/gadget/fsl_usb2_udc.h +++ b/drivers/usb/gadget/fsl_usb2_udc.h @@ -275,7 +275,9 @@ struct usb_sys_interface { #define USB_MODE_CTRL_MODE_IDLE 0x00000000 #define USB_MODE_CTRL_MODE_DEVICE 0x00000002 #define USB_MODE_CTRL_MODE_HOST 0x00000003 +#define USB_MODE_CTRL_MODE_MASK 0x00000003 #define USB_MODE_CTRL_MODE_RSV 0x00000001 +#define USB_MODE_ES 0x00000004 /* Endian Select */ #define USB_MODE_SETUP_LOCK_OFF 0x00000008 #define USB_MODE_STREAM_DISABLE 0x00000010 /* Endpoint Flush Register */ @@ -461,6 +463,7 @@ struct fsl_ep { struct fsl_udc { struct usb_gadget gadget; struct usb_gadget_driver *driver; + struct fsl_usb2_platform_data *pdata; struct completion *done; /* to make sure release() is done */ struct fsl_ep *eps; unsigned int max_ep; @@ -473,6 +476,8 @@ struct fsl_udc { unsigned vbus_active:1; unsigned stopped:1; unsigned remote_wakeup:1; + unsigned already_stopped:1; + unsigned big_endian_desc:1; struct ep_queue_head *ep_qh; /* Endpoints Queue-Head */ struct fsl_req *status_req; /* ep0 status request */ @@ -483,6 +488,7 @@ struct fsl_udc { dma_addr_t ep_qh_dma; /* dma address of QH */ u32 max_pipes; /* Device max pipes */ + u32 bus_reset; /* Device is bus resetting */ u32 resume_state; /* USB state to resume */ u32 usb_state; /* USB current state */ u32 ep0_state; /* Endpoint zero state */ diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index e896f63..bcdac7c 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -136,6 +136,12 @@ #define gadget_is_s3c_hsotg(g) 0 #endif +#ifdef CONFIG_USB_S3C_HSUDC +#define gadget_is_s3c_hsudc(g) (!strcmp("s3c-hsudc", (g)->name)) +#else +#define gadget_is_s3c_hsudc(g) 0 +#endif + #ifdef CONFIG_USB_GADGET_EG20T #define gadget_is_pch(g) (!strcmp("pch_udc", (g)->name)) #else @@ -148,6 +154,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 +219,11 @@ 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; + else if (gadget_is_s3c_hsudc(gadget)) + return 0x30; + return -ENOENT; } diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c index c3f2bd4..271ef94 100644 --- a/drivers/usb/gadget/printer.c +++ b/drivers/usb/gadget/printer.c @@ -1189,6 +1189,8 @@ printer_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) else if (gadget->a_alt_hnp_support) DBG(dev, "HNP needs a different root port\n"); value = printer_set_config(dev, wValue); + if (!value) + value = set_interface(dev, PRINTER_INTERFACE); break; case USB_REQ_GET_CONFIGURATION: if (ctrl->bRequestType != USB_DIR_IN) diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c index 0912679..acb9cc4 100644 --- a/drivers/usb/gadget/s3c-hsotg.c +++ b/drivers/usb/gadget/s3c-hsotg.c @@ -1,5 +1,8 @@ /* linux/drivers/usb/gadget/s3c-hsotg.c * + * Copyright (c) 2011 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * * Copyright 2008 Openmoko, Inc. * Copyright 2008 Simtec Electronics * Ben Dooks <ben@simtec.co.uk> @@ -613,11 +616,10 @@ static unsigned get_ep_limit(struct s3c_hsotg_ep *hs_ep) maxpkt = S3C_DxEPTSIZ_PktCnt_LIMIT + 1; } else { maxsize = 64+64; - if (hs_ep->dir_in) { + if (hs_ep->dir_in) maxpkt = S3C_DIEPTSIZ0_PktCnt_LIMIT + 1; - } else { + else maxpkt = 2; - } } /* we made the constant loading easier above by using +1 */ @@ -679,6 +681,14 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg, __func__, readl(hsotg->regs + epctrl_reg), index, hs_ep->dir_in ? "in" : "out"); + /* If endpoint is stalled, we will restart request later */ + ctrl = readl(hsotg->regs + epctrl_reg); + + if (ctrl & S3C_DxEPCTL_Stall) { + dev_warn(hsotg->dev, "%s: ep%d is stalled\n", __func__, index); + return; + } + length = ureq->length - ureq->actual; if (0) @@ -731,18 +741,6 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg, /* write size / packets */ writel(epsize, hsotg->regs + epsize_reg); - ctrl = readl(hsotg->regs + epctrl_reg); - - if (ctrl & S3C_DxEPCTL_Stall) { - dev_warn(hsotg->dev, "%s: ep%d is stalled\n", __func__, index); - - /* not sure what we can do here, if it is EP0 then we should - * get this cleared once the endpoint has transmitted the - * STALL packet, otherwise it needs to be cleared by the - * host. - */ - } - if (using_dma(hsotg)) { unsigned int dma_reg; @@ -1048,6 +1046,20 @@ static int s3c_hsotg_process_req_status(struct s3c_hsotg *hsotg, static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value); /** + * get_ep_head - return the first request on the endpoint + * @hs_ep: The controller endpoint to get + * + * Get the first request on the endpoint. + */ +static struct s3c_hsotg_req *get_ep_head(struct s3c_hsotg_ep *hs_ep) +{ + if (list_empty(&hs_ep->queue)) + return NULL; + + return list_first_entry(&hs_ep->queue, struct s3c_hsotg_req, queue); +} + +/** * s3c_hsotg_process_req_featire - process request {SET,CLEAR}_FEATURE * @hsotg: The device state * @ctrl: USB control request @@ -1055,8 +1067,12 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value); static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg, struct usb_ctrlrequest *ctrl) { + struct s3c_hsotg_ep *ep0 = &hsotg->eps[0]; + struct s3c_hsotg_req *hs_req; + bool restart; bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE); struct s3c_hsotg_ep *ep; + int ret; dev_dbg(hsotg->dev, "%s: %s_FEATURE\n", __func__, set ? "SET" : "CLEAR"); @@ -1072,6 +1088,36 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg, switch (le16_to_cpu(ctrl->wValue)) { case USB_ENDPOINT_HALT: s3c_hsotg_ep_sethalt(&ep->ep, set); + + ret = s3c_hsotg_send_reply(hsotg, ep0, NULL, 0); + if (ret) { + dev_err(hsotg->dev, + "%s: failed to send reply\n", __func__); + return ret; + } + + if (!set) { + /* + * If we have request in progress, + * then complete it + */ + if (ep->req) { + hs_req = ep->req; + ep->req = NULL; + list_del_init(&hs_req->queue); + hs_req->req.complete(&ep->ep, + &hs_req->req); + } + + /* If we have pending request, then start it */ + restart = !list_empty(&ep->queue); + if (restart) { + hs_req = get_ep_head(ep); + s3c_hsotg_start_req(hsotg, ep, + hs_req, false); + } + } + break; default: @@ -1148,14 +1194,6 @@ static void s3c_hsotg_process_control(struct s3c_hsotg *hsotg, dev_dbg(hsotg->dev, "driver->setup() ret %d\n", ret); } - if (ret > 0) { - if (!ep0->dir_in) { - /* need to generate zlp in reply or take data */ - /* todo - deal with any data we might be sent? */ - ret = s3c_hsotg_send_reply(hsotg, ep0, NULL, 0); - } - } - /* the request is either unhandlable, or is not formatted correctly * so respond with a STALL for the status stage to indicate failure. */ @@ -1247,20 +1285,6 @@ static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg) } /** - * get_ep_head - return the first request on the endpoint - * @hs_ep: The controller endpoint to get - * - * Get the first request on the endpoint. -*/ -static struct s3c_hsotg_req *get_ep_head(struct s3c_hsotg_ep *hs_ep) -{ - if (list_empty(&hs_ep->queue)) - return NULL; - - return list_first_entry(&hs_ep->queue, struct s3c_hsotg_req, queue); -} - -/** * s3c_hsotg_complete_request - complete a request given to us * @hsotg: The device state. * @hs_ep: The endpoint the request was on. @@ -1683,6 +1707,37 @@ bad_mps: dev_err(hsotg->dev, "ep%d: bad mps of %d\n", ep, mps); } +/** + * s3c_hsotg_txfifo_flush - flush Tx FIFO + * @hsotg: The driver state + * @idx: The index for the endpoint (0..15) + */ +static void s3c_hsotg_txfifo_flush(struct s3c_hsotg *hsotg, unsigned int idx) +{ + int timeout; + int val; + + writel(S3C_GRSTCTL_TxFNum(idx) | S3C_GRSTCTL_TxFFlsh, + hsotg->regs + S3C_GRSTCTL); + + /* wait until the fifo is flushed */ + timeout = 100; + + while (1) { + val = readl(hsotg->regs + S3C_GRSTCTL); + + if ((val & (S3C_GRSTCTL_TxFFlsh)) == 0) + break; + + if (--timeout == 0) { + dev_err(hsotg->dev, + "%s: timeout flushing fifo (GRSTCTL=%08x)\n", + __func__, val); + } + + udelay(1); + } +} /** * s3c_hsotg_trytx - check to see if anything needs transmitting @@ -1775,10 +1830,12 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, u32 epctl_reg = dir_in ? S3C_DIEPCTL(idx) : S3C_DOEPCTL(idx); u32 epsiz_reg = dir_in ? S3C_DIEPTSIZ(idx) : S3C_DOEPTSIZ(idx); u32 ints; - u32 clear = 0; ints = readl(hsotg->regs + epint_reg); + /* Clear endpoint interrupts */ + writel(ints, hsotg->regs + epint_reg); + dev_dbg(hsotg->dev, "%s: ep%d(%s) DxEPINT=0x%08x\n", __func__, idx, dir_in ? "in" : "out", ints); @@ -1801,19 +1858,28 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, s3c_hsotg_handle_outdone(hsotg, idx, false); } - - clear |= S3C_DxEPINT_XferCompl; } if (ints & S3C_DxEPINT_EPDisbld) { dev_dbg(hsotg->dev, "%s: EPDisbld\n", __func__); - clear |= S3C_DxEPINT_EPDisbld; + + if (dir_in) { + int epctl = readl(hsotg->regs + epctl_reg); + + s3c_hsotg_txfifo_flush(hsotg, idx); + + if ((epctl & S3C_DxEPCTL_Stall) && + (epctl & S3C_DxEPCTL_EPType_Bulk)) { + int dctl = readl(hsotg->regs + S3C_DCTL); + + dctl |= S3C_DCTL_CGNPInNAK; + writel(dctl, hsotg->regs + S3C_DCTL); + } + } } - if (ints & S3C_DxEPINT_AHBErr) { + if (ints & S3C_DxEPINT_AHBErr) dev_dbg(hsotg->dev, "%s: AHBErr\n", __func__); - clear |= S3C_DxEPINT_AHBErr; - } if (ints & S3C_DxEPINT_Setup) { /* Setup or Timeout */ dev_dbg(hsotg->dev, "%s: Setup/Timeout\n", __func__); @@ -1829,14 +1895,10 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, else s3c_hsotg_handle_outdone(hsotg, 0, true); } - - clear |= S3C_DxEPINT_Setup; } - if (ints & S3C_DxEPINT_Back2BackSetup) { + if (ints & S3C_DxEPINT_Back2BackSetup) dev_dbg(hsotg->dev, "%s: B2BSetup/INEPNakEff\n", __func__); - clear |= S3C_DxEPINT_Back2BackSetup; - } if (dir_in) { /* not sure if this is important, but we'll clear it anyway @@ -1844,14 +1906,12 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, if (ints & S3C_DIEPMSK_INTknTXFEmpMsk) { dev_dbg(hsotg->dev, "%s: ep%d: INTknTXFEmpMsk\n", __func__, idx); - clear |= S3C_DIEPMSK_INTknTXFEmpMsk; } /* this probably means something bad is happening */ if (ints & S3C_DIEPMSK_INTknEPMisMsk) { dev_warn(hsotg->dev, "%s: ep%d: INTknEP\n", __func__, idx); - clear |= S3C_DIEPMSK_INTknEPMisMsk; } /* FIFO has space or is empty (see GAHBCFG) */ @@ -1860,11 +1920,8 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, dev_dbg(hsotg->dev, "%s: ep%d: TxFIFOEmpty\n", __func__, idx); s3c_hsotg_trytx(hsotg, hs_ep); - clear |= S3C_DIEPMSK_TxFIFOEmpty; } } - - writel(clear, hsotg->regs + epint_reg); } /** @@ -2056,7 +2113,6 @@ irq_retry: dev_info(hsotg->dev, "OTGInt: %08x\n", otgint); writel(otgint, hsotg->regs + S3C_GOTGINT); - writel(S3C_GINTSTS_OTGInt, hsotg->regs + S3C_GINTSTS); } if (gintsts & S3C_GINTSTS_DisconnInt) { @@ -2072,8 +2128,9 @@ irq_retry: } if (gintsts & S3C_GINTSTS_EnumDone) { - s3c_hsotg_irq_enumdone(hsotg); writel(S3C_GINTSTS_EnumDone, hsotg->regs + S3C_GINTSTS); + + s3c_hsotg_irq_enumdone(hsotg); } if (gintsts & S3C_GINTSTS_ConIDStsChng) { @@ -2101,10 +2158,6 @@ irq_retry: if (daint_in & 1) s3c_hsotg_epint(hsotg, ep, 1); } - - writel(daint, hsotg->regs + S3C_DAINT); - writel(gintsts & (S3C_GINTSTS_OEPInt | S3C_GINTSTS_IEPInt), - hsotg->regs + S3C_GINTSTS); } if (gintsts & S3C_GINTSTS_USBRst) { @@ -2112,6 +2165,8 @@ irq_retry: dev_dbg(hsotg->dev, "GNPTXSTS=%08x\n", readl(hsotg->regs + S3C_GNPTXSTS)); + writel(S3C_GINTSTS_USBRst, hsotg->regs + S3C_GINTSTS); + kill_all_requests(hsotg, &hsotg->eps[0], -ECONNRESET, true); /* it seems after a reset we can end up with a situation @@ -2123,8 +2178,6 @@ irq_retry: s3c_hsotg_init_fifo(hsotg); s3c_hsotg_enqueue_setup(hsotg); - - writel(S3C_GINTSTS_USBRst, hsotg->regs + S3C_GINTSTS); } /* check both FIFOs */ @@ -2138,8 +2191,6 @@ irq_retry: s3c_hsotg_disable_gsint(hsotg, S3C_GINTSTS_NPTxFEmp); s3c_hsotg_irq_fifoempty(hsotg, false); - - writel(S3C_GINTSTS_NPTxFEmp, hsotg->regs + S3C_GINTSTS); } if (gintsts & S3C_GINTSTS_PTxFEmp) { @@ -2149,8 +2200,6 @@ irq_retry: s3c_hsotg_disable_gsint(hsotg, S3C_GINTSTS_PTxFEmp); s3c_hsotg_irq_fifoempty(hsotg, true); - - writel(S3C_GINTSTS_PTxFEmp, hsotg->regs + S3C_GINTSTS); } if (gintsts & S3C_GINTSTS_RxFLvl) { @@ -2159,7 +2208,6 @@ irq_retry: * set. */ s3c_hsotg_handle_rx(hsotg); - writel(S3C_GINTSTS_RxFLvl, hsotg->regs + S3C_GINTSTS); } if (gintsts & S3C_GINTSTS_ModeMis) { @@ -2193,19 +2241,17 @@ irq_retry: if (gintsts & S3C_GINTSTS_GOUTNakEff) { dev_info(hsotg->dev, "GOUTNakEff triggered\n"); - s3c_hsotg_dump(hsotg); - writel(S3C_DCTL_CGOUTNak, hsotg->regs + S3C_DCTL); - writel(S3C_GINTSTS_GOUTNakEff, hsotg->regs + S3C_GINTSTS); + + s3c_hsotg_dump(hsotg); } if (gintsts & S3C_GINTSTS_GINNakEff) { dev_info(hsotg->dev, "GINNakEff triggered\n"); - s3c_hsotg_dump(hsotg); - writel(S3C_DCTL_CGNPInNAK, hsotg->regs + S3C_DCTL); - writel(S3C_GINTSTS_GINNakEff, hsotg->regs + S3C_GINTSTS); + + s3c_hsotg_dump(hsotg); } /* if we've had fifo events, we should try and go around the @@ -2403,11 +2449,6 @@ static int s3c_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req) dev_info(hs->dev, "ep_dequeue(%p,%p)\n", ep, req); - if (hs_req == hs_ep->req) { - dev_dbg(hs->dev, "%s: already in progress\n", __func__); - return -EINPROGRESS; - } - spin_lock_irqsave(&hs_ep->lock, flags); if (!on_list(hs_ep, hs_req)) { @@ -2429,6 +2470,7 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value) unsigned long irqflags; u32 epreg; u32 epctl; + u32 xfertype; dev_info(hs->dev, "%s(ep %p %s, %d)\n", __func__, ep, ep->name, value); @@ -2439,10 +2481,17 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value) epreg = S3C_DIEPCTL(index); epctl = readl(hs->regs + epreg); - if (value) - epctl |= S3C_DxEPCTL_Stall; - else + if (value) { + epctl |= S3C_DxEPCTL_Stall + S3C_DxEPCTL_SNAK; + if (epctl & S3C_DxEPCTL_EPEna) + epctl |= S3C_DxEPCTL_EPDis; + } else { epctl &= ~S3C_DxEPCTL_Stall; + xfertype = epctl & S3C_DxEPCTL_EPType_MASK; + if (xfertype == S3C_DxEPCTL_EPType_Bulk || + xfertype == S3C_DxEPCTL_EPType_Intterupt) + epctl |= S3C_DxEPCTL_SetD0PID; + } writel(epctl, hs->regs + epreg); @@ -2451,8 +2500,13 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value) if (value) epctl |= S3C_DxEPCTL_Stall; - else + else { epctl &= ~S3C_DxEPCTL_Stall; + xfertype = epctl & S3C_DxEPCTL_EPType_MASK; + if (xfertype == S3C_DxEPCTL_EPType_Bulk || + xfertype == S3C_DxEPCTL_EPType_Intterupt) + epctl |= S3C_DxEPCTL_SetD0PID; + } writel(epctl, hs->regs + epreg); @@ -2491,9 +2545,9 @@ static int s3c_hsotg_corereset(struct s3c_hsotg *hsotg) timeout = 1000; do { grstctl = readl(hsotg->regs + S3C_GRSTCTL); - } while (!(grstctl & S3C_GRSTCTL_CSftRst) && timeout-- > 0); + } while ((grstctl & S3C_GRSTCTL_CSftRst) && timeout-- > 0); - if (!(grstctl & S3C_GRSTCTL_CSftRst)) { + if (grstctl & S3C_GRSTCTL_CSftRst) { dev_err(hsotg->dev, "Failed to get CSftRst asserted\n"); return -EINVAL; } @@ -2510,13 +2564,10 @@ static int s3c_hsotg_corereset(struct s3c_hsotg *hsotg) return -ETIMEDOUT; } - if (grstctl & S3C_GRSTCTL_CSftRst) - continue; - if (!(grstctl & S3C_GRSTCTL_AHBIdle)) continue; - break; /* reset done */ + break; /* reset done */ } dev_dbg(hsotg->dev, "reset successful\n"); @@ -2588,6 +2639,12 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, writel(1 << 18 | S3C_DCFG_DevSpd_HS, hsotg->regs + S3C_DCFG); + /* Clear any pending OTG interrupts */ + writel(0xffffffff, hsotg->regs + S3C_GOTGINT); + + /* Clear any pending interrupts */ + writel(0xffffffff, hsotg->regs + S3C_GINTSTS); + writel(S3C_GINTSTS_DisconnInt | S3C_GINTSTS_SessReqInt | S3C_GINTSTS_ConIDStsChng | S3C_GINTSTS_USBRst | S3C_GINTSTS_EnumDone | S3C_GINTSTS_OTGInt | @@ -3261,7 +3318,7 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev) hsotg->clk = clk_get(&pdev->dev, "otg"); if (IS_ERR(hsotg->clk)) { dev_err(dev, "cannot get otg clock\n"); - ret = -EINVAL; + ret = PTR_ERR(hsotg->clk); goto err_mem; } diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c new file mode 100644 index 0000000..cfe3cf5 --- /dev/null +++ b/drivers/usb/gadget/s3c-hsudc.c @@ -0,0 +1,1349 @@ +/* linux/drivers/usb/gadget/s3c-hsudc.c + * + * Copyright (c) 2010 Samsung Electronics Co., Ltd. + * http://www.samsung.com/ + * + * S3C24XX USB 2.0 High-speed USB controller gadget driver + * + * The S3C24XX USB 2.0 high-speed USB controller supports upto 9 endpoints. + * Each endpoint can be configured as either in or out endpoint. Endpoints + * can be configured for Bulk or Interrupt transfer mode. + * + * 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/module.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/clk.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#include <mach/regs-s3c2443-clock.h> +#include <plat/udc.h> + +#define S3C_HSUDC_REG(x) (x) + +/* Non-Indexed Registers */ +#define S3C_IR S3C_HSUDC_REG(0x00) /* Index Register */ +#define S3C_EIR S3C_HSUDC_REG(0x04) /* EP Intr Status */ +#define S3C_EIR_EP0 (1<<0) +#define S3C_EIER S3C_HSUDC_REG(0x08) /* EP Intr Enable */ +#define S3C_FAR S3C_HSUDC_REG(0x0c) /* Gadget Address */ +#define S3C_FNR S3C_HSUDC_REG(0x10) /* Frame Number */ +#define S3C_EDR S3C_HSUDC_REG(0x14) /* EP Direction */ +#define S3C_TR S3C_HSUDC_REG(0x18) /* Test Register */ +#define S3C_SSR S3C_HSUDC_REG(0x1c) /* System Status */ +#define S3C_SSR_DTZIEN_EN (0xff8f) +#define S3C_SSR_ERR (0xff80) +#define S3C_SSR_VBUSON (1 << 8) +#define S3C_SSR_HSP (1 << 4) +#define S3C_SSR_SDE (1 << 3) +#define S3C_SSR_RESUME (1 << 2) +#define S3C_SSR_SUSPEND (1 << 1) +#define S3C_SSR_RESET (1 << 0) +#define S3C_SCR S3C_HSUDC_REG(0x20) /* System Control */ +#define S3C_SCR_DTZIEN_EN (1 << 14) +#define S3C_SCR_RRD_EN (1 << 5) +#define S3C_SCR_SUS_EN (1 << 1) +#define S3C_SCR_RST_EN (1 << 0) +#define S3C_EP0SR S3C_HSUDC_REG(0x24) /* EP0 Status */ +#define S3C_EP0SR_EP0_LWO (1 << 6) +#define S3C_EP0SR_STALL (1 << 4) +#define S3C_EP0SR_TX_SUCCESS (1 << 1) +#define S3C_EP0SR_RX_SUCCESS (1 << 0) +#define S3C_EP0CR S3C_HSUDC_REG(0x28) /* EP0 Control */ +#define S3C_BR(_x) S3C_HSUDC_REG(0x60 + (_x * 4)) + +/* Indexed Registers */ +#define S3C_ESR S3C_HSUDC_REG(0x2c) /* EPn Status */ +#define S3C_ESR_FLUSH (1 << 6) +#define S3C_ESR_STALL (1 << 5) +#define S3C_ESR_LWO (1 << 4) +#define S3C_ESR_PSIF_ONE (1 << 2) +#define S3C_ESR_PSIF_TWO (2 << 2) +#define S3C_ESR_TX_SUCCESS (1 << 1) +#define S3C_ESR_RX_SUCCESS (1 << 0) +#define S3C_ECR S3C_HSUDC_REG(0x30) /* EPn Control */ +#define S3C_ECR_DUEN (1 << 7) +#define S3C_ECR_FLUSH (1 << 6) +#define S3C_ECR_STALL (1 << 1) +#define S3C_ECR_IEMS (1 << 0) +#define S3C_BRCR S3C_HSUDC_REG(0x34) /* Read Count */ +#define S3C_BWCR S3C_HSUDC_REG(0x38) /* Write Count */ +#define S3C_MPR S3C_HSUDC_REG(0x3c) /* Max Pkt Size */ + +#define WAIT_FOR_SETUP (0) +#define DATA_STATE_XMIT (1) +#define DATA_STATE_RECV (2) + +/** + * struct s3c_hsudc_ep - Endpoint representation used by driver. + * @ep: USB gadget layer representation of device endpoint. + * @name: Endpoint name (as required by ep autoconfiguration). + * @dev: Reference to the device controller to which this EP belongs. + * @desc: Endpoint descriptor obtained from the gadget driver. + * @queue: Transfer request queue for the endpoint. + * @stopped: Maintains state of endpoint, set if EP is halted. + * @bEndpointAddress: EP address (including direction bit). + * @fifo: Base address of EP FIFO. + */ +struct s3c_hsudc_ep { + struct usb_ep ep; + char name[20]; + struct s3c_hsudc *dev; + const struct usb_endpoint_descriptor *desc; + struct list_head queue; + u8 stopped; + u8 wedge; + u8 bEndpointAddress; + void __iomem *fifo; +}; + +/** + * struct s3c_hsudc_req - Driver encapsulation of USB gadget transfer request. + * @req: Reference to USB gadget transfer request. + * @queue: Used for inserting this request to the endpoint request queue. + */ +struct s3c_hsudc_req { + struct usb_request req; + struct list_head queue; +}; + +/** + * struct s3c_hsudc - Driver's abstraction of the device controller. + * @gadget: Instance of usb_gadget which is referenced by gadget driver. + * @driver: Reference to currenty active gadget driver. + * @dev: The device reference used by probe function. + * @lock: Lock to synchronize the usage of Endpoints (EP's are indexed). + * @regs: Remapped base address of controller's register space. + * @mem_rsrc: Device memory resource used for remapping device register space. + * irq: IRQ number used by the controller. + * uclk: Reference to the controller clock. + * ep0state: Current state of EP0. + * ep: List of endpoints supported by the controller. + */ +struct s3c_hsudc { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + struct device *dev; + struct s3c24xx_hsudc_platdata *pd; + spinlock_t lock; + void __iomem *regs; + struct resource *mem_rsrc; + int irq; + struct clk *uclk; + int ep0state; + struct s3c_hsudc_ep ep[]; +}; + +#define ep_maxpacket(_ep) ((_ep)->ep.maxpacket) +#define ep_is_in(_ep) ((_ep)->bEndpointAddress & USB_DIR_IN) +#define ep_index(_ep) ((_ep)->bEndpointAddress & \ + USB_ENDPOINT_NUMBER_MASK) + +static struct s3c_hsudc *the_controller; +static const char driver_name[] = "s3c-udc"; +static const char ep0name[] = "ep0-control"; + +static inline struct s3c_hsudc_req *our_req(struct usb_request *req) +{ + return container_of(req, struct s3c_hsudc_req, req); +} + +static inline struct s3c_hsudc_ep *our_ep(struct usb_ep *ep) +{ + return container_of(ep, struct s3c_hsudc_ep, ep); +} + +static inline struct s3c_hsudc *to_hsudc(struct usb_gadget *gadget) +{ + return container_of(gadget, struct s3c_hsudc, gadget); +} + +static inline void set_index(struct s3c_hsudc *hsudc, int ep_addr) +{ + ep_addr &= USB_ENDPOINT_NUMBER_MASK; + writel(ep_addr, hsudc->regs + S3C_IR); +} + +static inline void __orr32(void __iomem *ptr, u32 val) +{ + writel(readl(ptr) | val, ptr); +} + +static void s3c_hsudc_init_phy(void) +{ + u32 cfg; + + cfg = readl(S3C2443_PWRCFG) | S3C2443_PWRCFG_USBPHY; + writel(cfg, S3C2443_PWRCFG); + + cfg = readl(S3C2443_URSTCON); + cfg |= (S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST); + writel(cfg, S3C2443_URSTCON); + mdelay(1); + + cfg = readl(S3C2443_URSTCON); + cfg &= ~(S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST); + writel(cfg, S3C2443_URSTCON); + + cfg = readl(S3C2443_PHYCTRL); + cfg &= ~(S3C2443_PHYCTRL_CLKSEL | S3C2443_PHYCTRL_DSPORT); + cfg |= (S3C2443_PHYCTRL_EXTCLK | S3C2443_PHYCTRL_PLLSEL); + writel(cfg, S3C2443_PHYCTRL); + + cfg = readl(S3C2443_PHYPWR); + cfg &= ~(S3C2443_PHYPWR_FSUSPEND | S3C2443_PHYPWR_PLL_PWRDN | + S3C2443_PHYPWR_XO_ON | S3C2443_PHYPWR_PLL_REFCLK | + S3C2443_PHYPWR_ANALOG_PD); + cfg |= S3C2443_PHYPWR_COMMON_ON; + writel(cfg, S3C2443_PHYPWR); + + cfg = readl(S3C2443_UCLKCON); + cfg |= (S3C2443_UCLKCON_DETECT_VBUS | S3C2443_UCLKCON_FUNC_CLKEN | + S3C2443_UCLKCON_TCLKEN); + writel(cfg, S3C2443_UCLKCON); +} + +static void s3c_hsudc_uninit_phy(void) +{ + u32 cfg; + + cfg = readl(S3C2443_PWRCFG) & ~S3C2443_PWRCFG_USBPHY; + writel(cfg, S3C2443_PWRCFG); + + writel(S3C2443_PHYPWR_FSUSPEND, S3C2443_PHYPWR); + + cfg = readl(S3C2443_UCLKCON) & ~S3C2443_UCLKCON_FUNC_CLKEN; + writel(cfg, S3C2443_UCLKCON); +} + +/** + * s3c_hsudc_complete_request - Complete a transfer request. + * @hsep: Endpoint to which the request belongs. + * @hsreq: Transfer request to be completed. + * @status: Transfer completion status for the transfer request. + */ +static void s3c_hsudc_complete_request(struct s3c_hsudc_ep *hsep, + struct s3c_hsudc_req *hsreq, int status) +{ + unsigned int stopped = hsep->stopped; + struct s3c_hsudc *hsudc = hsep->dev; + + list_del_init(&hsreq->queue); + hsreq->req.status = status; + + if (!ep_index(hsep)) { + hsudc->ep0state = WAIT_FOR_SETUP; + hsep->bEndpointAddress &= ~USB_DIR_IN; + } + + hsep->stopped = 1; + spin_unlock(&hsudc->lock); + if (hsreq->req.complete != NULL) + hsreq->req.complete(&hsep->ep, &hsreq->req); + spin_lock(&hsudc->lock); + hsep->stopped = stopped; +} + +/** + * s3c_hsudc_nuke_ep - Terminate all requests queued for a endpoint. + * @hsep: Endpoint for which queued requests have to be terminated. + * @status: Transfer completion status for the transfer request. + */ +static void s3c_hsudc_nuke_ep(struct s3c_hsudc_ep *hsep, int status) +{ + struct s3c_hsudc_req *hsreq; + + while (!list_empty(&hsep->queue)) { + hsreq = list_entry(hsep->queue.next, + struct s3c_hsudc_req, queue); + s3c_hsudc_complete_request(hsep, hsreq, status); + } +} + +/** + * s3c_hsudc_stop_activity - Stop activity on all endpoints. + * @hsudc: Device controller for which EP activity is to be stopped. + * @driver: Reference to the gadget driver which is currently active. + * + * All the endpoints are stopped and any pending transfer requests if any on + * the endpoint are terminated. + */ +static void s3c_hsudc_stop_activity(struct s3c_hsudc *hsudc, + struct usb_gadget_driver *driver) +{ + struct s3c_hsudc_ep *hsep; + int epnum; + + hsudc->gadget.speed = USB_SPEED_UNKNOWN; + + for (epnum = 0; epnum < hsudc->pd->epnum; epnum++) { + hsep = &hsudc->ep[epnum]; + hsep->stopped = 1; + s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN); + } + + spin_unlock(&hsudc->lock); + driver->disconnect(&hsudc->gadget); + spin_lock(&hsudc->lock); +} + +/** + * s3c_hsudc_read_setup_pkt - Read the received setup packet from EP0 fifo. + * @hsudc: Device controller from which setup packet is to be read. + * @buf: The buffer into which the setup packet is read. + * + * The setup packet received in the EP0 fifo is read and stored into a + * given buffer address. + */ + +static void s3c_hsudc_read_setup_pkt(struct s3c_hsudc *hsudc, u16 *buf) +{ + int count; + + count = readl(hsudc->regs + S3C_BRCR); + while (count--) + *buf++ = (u16)readl(hsudc->regs + S3C_BR(0)); + + writel(S3C_EP0SR_RX_SUCCESS, hsudc->regs + S3C_EP0SR); +} + +/** + * s3c_hsudc_write_fifo - Write next chunk of transfer data to EP fifo. + * @hsep: Endpoint to which the data is to be written. + * @hsreq: Transfer request from which the next chunk of data is written. + * + * Write the next chunk of data from a transfer request to the endpoint FIFO. + * If the transfer request completes, 1 is returned, otherwise 0 is returned. + */ +static int s3c_hsudc_write_fifo(struct s3c_hsudc_ep *hsep, + struct s3c_hsudc_req *hsreq) +{ + u16 *buf; + u32 max = ep_maxpacket(hsep); + u32 count, length; + bool is_last; + void __iomem *fifo = hsep->fifo; + + buf = hsreq->req.buf + hsreq->req.actual; + prefetch(buf); + + length = hsreq->req.length - hsreq->req.actual; + length = min(length, max); + hsreq->req.actual += length; + + writel(length, hsep->dev->regs + S3C_BWCR); + for (count = 0; count < length; count += 2) + writel(*buf++, fifo); + + if (count != max) { + is_last = true; + } else { + if (hsreq->req.length != hsreq->req.actual || hsreq->req.zero) + is_last = false; + else + is_last = true; + } + + if (is_last) { + s3c_hsudc_complete_request(hsep, hsreq, 0); + return 1; + } + + return 0; +} + +/** + * s3c_hsudc_read_fifo - Read the next chunk of data from EP fifo. + * @hsep: Endpoint from which the data is to be read. + * @hsreq: Transfer request to which the next chunk of data read is written. + * + * Read the next chunk of data from the endpoint FIFO and a write it to the + * transfer request buffer. If the transfer request completes, 1 is returned, + * otherwise 0 is returned. + */ +static int s3c_hsudc_read_fifo(struct s3c_hsudc_ep *hsep, + struct s3c_hsudc_req *hsreq) +{ + struct s3c_hsudc *hsudc = hsep->dev; + u32 csr, offset; + u16 *buf, word; + u32 buflen, rcnt, rlen; + void __iomem *fifo = hsep->fifo; + u32 is_short = 0; + + offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR; + csr = readl(hsudc->regs + offset); + if (!(csr & S3C_ESR_RX_SUCCESS)) + return -EINVAL; + + buf = hsreq->req.buf + hsreq->req.actual; + prefetchw(buf); + buflen = hsreq->req.length - hsreq->req.actual; + + rcnt = readl(hsudc->regs + S3C_BRCR); + rlen = (csr & S3C_ESR_LWO) ? (rcnt * 2 - 1) : (rcnt * 2); + + hsreq->req.actual += min(rlen, buflen); + is_short = (rlen < hsep->ep.maxpacket); + + while (rcnt-- != 0) { + word = (u16)readl(fifo); + if (buflen) { + *buf++ = word; + buflen--; + } else { + hsreq->req.status = -EOVERFLOW; + } + } + + writel(S3C_ESR_RX_SUCCESS, hsudc->regs + offset); + + if (is_short || hsreq->req.actual == hsreq->req.length) { + s3c_hsudc_complete_request(hsep, hsreq, 0); + return 1; + } + + return 0; +} + +/** + * s3c_hsudc_epin_intr - Handle in-endpoint interrupt. + * @hsudc - Device controller for which the interrupt is to be handled. + * @ep_idx - Endpoint number on which an interrupt is pending. + * + * Handles interrupt for a in-endpoint. The interrupts that are handled are + * stall and data transmit complete interrupt. + */ +static void s3c_hsudc_epin_intr(struct s3c_hsudc *hsudc, u32 ep_idx) +{ + struct s3c_hsudc_ep *hsep = &hsudc->ep[ep_idx]; + struct s3c_hsudc_req *hsreq; + u32 csr; + + csr = readl((u32)hsudc->regs + S3C_ESR); + if (csr & S3C_ESR_STALL) { + writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR); + return; + } + + if (csr & S3C_ESR_TX_SUCCESS) { + writel(S3C_ESR_TX_SUCCESS, hsudc->regs + S3C_ESR); + if (list_empty(&hsep->queue)) + return; + + hsreq = list_entry(hsep->queue.next, + struct s3c_hsudc_req, queue); + if ((s3c_hsudc_write_fifo(hsep, hsreq) == 0) && + (csr & S3C_ESR_PSIF_TWO)) + s3c_hsudc_write_fifo(hsep, hsreq); + } +} + +/** + * s3c_hsudc_epout_intr - Handle out-endpoint interrupt. + * @hsudc - Device controller for which the interrupt is to be handled. + * @ep_idx - Endpoint number on which an interrupt is pending. + * + * Handles interrupt for a out-endpoint. The interrupts that are handled are + * stall, flush and data ready interrupt. + */ +static void s3c_hsudc_epout_intr(struct s3c_hsudc *hsudc, u32 ep_idx) +{ + struct s3c_hsudc_ep *hsep = &hsudc->ep[ep_idx]; + struct s3c_hsudc_req *hsreq; + u32 csr; + + csr = readl((u32)hsudc->regs + S3C_ESR); + if (csr & S3C_ESR_STALL) { + writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR); + return; + } + + if (csr & S3C_ESR_FLUSH) { + __orr32(hsudc->regs + S3C_ECR, S3C_ECR_FLUSH); + return; + } + + if (csr & S3C_ESR_RX_SUCCESS) { + if (list_empty(&hsep->queue)) + return; + + hsreq = list_entry(hsep->queue.next, + struct s3c_hsudc_req, queue); + if (((s3c_hsudc_read_fifo(hsep, hsreq)) == 0) && + (csr & S3C_ESR_PSIF_TWO)) + s3c_hsudc_read_fifo(hsep, hsreq); + } +} + +/** s3c_hsudc_set_halt - Set or clear a endpoint halt. + * @_ep: Endpoint on which halt has to be set or cleared. + * @value: 1 for setting halt on endpoint, 0 to clear halt. + * + * Set or clear endpoint halt. If halt is set, the endpoint is stopped. + * If halt is cleared, for in-endpoints, if there are any pending + * transfer requests, transfers are started. + */ +static int s3c_hsudc_set_halt(struct usb_ep *_ep, int value) +{ + struct s3c_hsudc_ep *hsep = our_ep(_ep); + struct s3c_hsudc *hsudc = hsep->dev; + struct s3c_hsudc_req *hsreq; + unsigned long irqflags; + u32 ecr; + u32 offset; + + if (value && ep_is_in(hsep) && !list_empty(&hsep->queue)) + return -EAGAIN; + + spin_lock_irqsave(&hsudc->lock, irqflags); + set_index(hsudc, ep_index(hsep)); + offset = (ep_index(hsep)) ? S3C_ECR : S3C_EP0CR; + ecr = readl(hsudc->regs + offset); + + if (value) { + ecr |= S3C_ECR_STALL; + if (ep_index(hsep)) + ecr |= S3C_ECR_FLUSH; + hsep->stopped = 1; + } else { + ecr &= ~S3C_ECR_STALL; + hsep->stopped = hsep->wedge = 0; + } + writel(ecr, hsudc->regs + offset); + + if (ep_is_in(hsep) && !list_empty(&hsep->queue) && !value) { + hsreq = list_entry(hsep->queue.next, + struct s3c_hsudc_req, queue); + if (hsreq) + s3c_hsudc_write_fifo(hsep, hsreq); + } + + spin_unlock_irqrestore(&hsudc->lock, irqflags); + return 0; +} + +/** s3c_hsudc_set_wedge - Sets the halt feature with the clear requests ignored + * @_ep: Endpoint on which wedge has to be set. + * + * Sets the halt feature with the clear requests ignored. + */ +static int s3c_hsudc_set_wedge(struct usb_ep *_ep) +{ + struct s3c_hsudc_ep *hsep = our_ep(_ep); + + if (!hsep) + return -EINVAL; + + hsep->wedge = 1; + return usb_ep_set_halt(_ep); +} + +/** s3c_hsudc_handle_reqfeat - Handle set feature or clear feature requests. + * @_ep: Device controller on which the set/clear feature needs to be handled. + * @ctrl: Control request as received on the endpoint 0. + * + * Handle set feature or clear feature control requests on the control endpoint. + */ +static int s3c_hsudc_handle_reqfeat(struct s3c_hsudc *hsudc, + struct usb_ctrlrequest *ctrl) +{ + struct s3c_hsudc_ep *hsep; + bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE); + u8 ep_num = ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK; + + if (ctrl->bRequestType == USB_RECIP_ENDPOINT) { + hsep = &hsudc->ep[ep_num]; + switch (le16_to_cpu(ctrl->wValue)) { + case USB_ENDPOINT_HALT: + if (set || (!set && !hsep->wedge)) + s3c_hsudc_set_halt(&hsep->ep, set); + return 0; + } + } + + return -ENOENT; +} + +/** + * s3c_hsudc_process_req_status - Handle get status control request. + * @hsudc: Device controller on which get status request has be handled. + * @ctrl: Control request as received on the endpoint 0. + * + * Handle get status control request received on control endpoint. + */ +static void s3c_hsudc_process_req_status(struct s3c_hsudc *hsudc, + struct usb_ctrlrequest *ctrl) +{ + struct s3c_hsudc_ep *hsep0 = &hsudc->ep[0]; + struct s3c_hsudc_req hsreq; + struct s3c_hsudc_ep *hsep; + __le16 reply; + u8 epnum; + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + reply = cpu_to_le16(0); + break; + + case USB_RECIP_INTERFACE: + reply = cpu_to_le16(0); + break; + + case USB_RECIP_ENDPOINT: + epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK; + hsep = &hsudc->ep[epnum]; + reply = cpu_to_le16(hsep->stopped ? 1 : 0); + break; + } + + INIT_LIST_HEAD(&hsreq.queue); + hsreq.req.length = 2; + hsreq.req.buf = &reply; + hsreq.req.actual = 0; + hsreq.req.complete = NULL; + s3c_hsudc_write_fifo(hsep0, &hsreq); +} + +/** + * s3c_hsudc_process_setup - Process control request received on endpoint 0. + * @hsudc: Device controller on which control request has been received. + * + * Read the control request received on endpoint 0, decode it and handle + * the request. + */ +static void s3c_hsudc_process_setup(struct s3c_hsudc *hsudc) +{ + struct s3c_hsudc_ep *hsep = &hsudc->ep[0]; + struct usb_ctrlrequest ctrl = {0}; + int ret; + + s3c_hsudc_nuke_ep(hsep, -EPROTO); + s3c_hsudc_read_setup_pkt(hsudc, (u16 *)&ctrl); + + if (ctrl.bRequestType & USB_DIR_IN) { + hsep->bEndpointAddress |= USB_DIR_IN; + hsudc->ep0state = DATA_STATE_XMIT; + } else { + hsep->bEndpointAddress &= ~USB_DIR_IN; + hsudc->ep0state = DATA_STATE_RECV; + } + + switch (ctrl.bRequest) { + case USB_REQ_SET_ADDRESS: + if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) + break; + hsudc->ep0state = WAIT_FOR_SETUP; + return; + + case USB_REQ_GET_STATUS: + if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) + break; + s3c_hsudc_process_req_status(hsudc, &ctrl); + return; + + case USB_REQ_SET_FEATURE: + case USB_REQ_CLEAR_FEATURE: + if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD) + break; + s3c_hsudc_handle_reqfeat(hsudc, &ctrl); + hsudc->ep0state = WAIT_FOR_SETUP; + return; + } + + if (hsudc->driver) { + spin_unlock(&hsudc->lock); + ret = hsudc->driver->setup(&hsudc->gadget, &ctrl); + spin_lock(&hsudc->lock); + + if (ctrl.bRequest == USB_REQ_SET_CONFIGURATION) { + hsep->bEndpointAddress &= ~USB_DIR_IN; + hsudc->ep0state = WAIT_FOR_SETUP; + } + + if (ret < 0) { + dev_err(hsudc->dev, "setup failed, returned %d\n", + ret); + s3c_hsudc_set_halt(&hsep->ep, 1); + hsudc->ep0state = WAIT_FOR_SETUP; + hsep->bEndpointAddress &= ~USB_DIR_IN; + } + } +} + +/** s3c_hsudc_handle_ep0_intr - Handle endpoint 0 interrupt. + * @hsudc: Device controller on which endpoint 0 interrupt has occured. + * + * Handle endpoint 0 interrupt when it occurs. EP0 interrupt could occur + * when a stall handshake is sent to host or data is sent/received on + * endpoint 0. + */ +static void s3c_hsudc_handle_ep0_intr(struct s3c_hsudc *hsudc) +{ + struct s3c_hsudc_ep *hsep = &hsudc->ep[0]; + struct s3c_hsudc_req *hsreq; + u32 csr = readl(hsudc->regs + S3C_EP0SR); + u32 ecr; + + if (csr & S3C_EP0SR_STALL) { + ecr = readl(hsudc->regs + S3C_EP0CR); + ecr &= ~(S3C_ECR_STALL | S3C_ECR_FLUSH); + writel(ecr, hsudc->regs + S3C_EP0CR); + + writel(S3C_EP0SR_STALL, hsudc->regs + S3C_EP0SR); + hsep->stopped = 0; + + s3c_hsudc_nuke_ep(hsep, -ECONNABORTED); + hsudc->ep0state = WAIT_FOR_SETUP; + hsep->bEndpointAddress &= ~USB_DIR_IN; + return; + } + + if (csr & S3C_EP0SR_TX_SUCCESS) { + writel(S3C_EP0SR_TX_SUCCESS, hsudc->regs + S3C_EP0SR); + if (ep_is_in(hsep)) { + if (list_empty(&hsep->queue)) + return; + + hsreq = list_entry(hsep->queue.next, + struct s3c_hsudc_req, queue); + s3c_hsudc_write_fifo(hsep, hsreq); + } + } + + if (csr & S3C_EP0SR_RX_SUCCESS) { + if (hsudc->ep0state == WAIT_FOR_SETUP) + s3c_hsudc_process_setup(hsudc); + else { + if (!ep_is_in(hsep)) { + if (list_empty(&hsep->queue)) + return; + hsreq = list_entry(hsep->queue.next, + struct s3c_hsudc_req, queue); + s3c_hsudc_read_fifo(hsep, hsreq); + } + } + } +} + +/** + * s3c_hsudc_ep_enable - Enable a endpoint. + * @_ep: The endpoint to be enabled. + * @desc: Endpoint descriptor. + * + * Enables a endpoint when called from the gadget driver. Endpoint stall if + * any is cleared, transfer type is configured and endpoint interrupt is + * enabled. + */ +static int s3c_hsudc_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct s3c_hsudc_ep *hsep; + struct s3c_hsudc *hsudc; + unsigned long flags; + u32 ecr = 0; + + hsep = container_of(_ep, struct s3c_hsudc_ep, ep); + if (!_ep || !desc || hsep->desc || _ep->name == ep0name + || desc->bDescriptorType != USB_DT_ENDPOINT + || hsep->bEndpointAddress != desc->bEndpointAddress + || ep_maxpacket(hsep) < le16_to_cpu(desc->wMaxPacketSize)) + return -EINVAL; + + if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK + && le16_to_cpu(desc->wMaxPacketSize) != ep_maxpacket(hsep)) + || !desc->wMaxPacketSize) + return -ERANGE; + + hsudc = hsep->dev; + if (!hsudc->driver || hsudc->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + spin_lock_irqsave(&hsudc->lock, flags); + + set_index(hsudc, hsep->bEndpointAddress); + ecr |= ((usb_endpoint_xfer_int(desc)) ? S3C_ECR_IEMS : S3C_ECR_DUEN); + writel(ecr, hsudc->regs + S3C_ECR); + + hsep->stopped = hsep->wedge = 0; + hsep->desc = desc; + hsep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + + s3c_hsudc_set_halt(_ep, 0); + __set_bit(ep_index(hsep), hsudc->regs + S3C_EIER); + + spin_unlock_irqrestore(&hsudc->lock, flags); + return 0; +} + +/** + * s3c_hsudc_ep_disable - Disable a endpoint. + * @_ep: The endpoint to be disabled. + * @desc: Endpoint descriptor. + * + * Disables a endpoint when called from the gadget driver. + */ +static int s3c_hsudc_ep_disable(struct usb_ep *_ep) +{ + struct s3c_hsudc_ep *hsep = our_ep(_ep); + struct s3c_hsudc *hsudc = hsep->dev; + unsigned long flags; + + if (!_ep || !hsep->desc) + return -EINVAL; + + spin_lock_irqsave(&hsudc->lock, flags); + + set_index(hsudc, hsep->bEndpointAddress); + __clear_bit(ep_index(hsep), hsudc->regs + S3C_EIER); + + s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN); + + hsep->desc = 0; + hsep->stopped = 1; + + spin_unlock_irqrestore(&hsudc->lock, flags); + return 0; +} + +/** + * s3c_hsudc_alloc_request - Allocate a new request. + * @_ep: Endpoint for which request is allocated (not used). + * @gfp_flags: Flags used for the allocation. + * + * Allocates a single transfer request structure when called from gadget driver. + */ +static struct usb_request *s3c_hsudc_alloc_request(struct usb_ep *_ep, + gfp_t gfp_flags) +{ + struct s3c_hsudc_req *hsreq; + + hsreq = kzalloc(sizeof *hsreq, gfp_flags); + if (!hsreq) + return 0; + + INIT_LIST_HEAD(&hsreq->queue); + return &hsreq->req; +} + +/** + * s3c_hsudc_free_request - Deallocate a request. + * @ep: Endpoint for which request is deallocated (not used). + * @_req: Request to be deallocated. + * + * Allocates a single transfer request structure when called from gadget driver. + */ +static void s3c_hsudc_free_request(struct usb_ep *ep, struct usb_request *_req) +{ + struct s3c_hsudc_req *hsreq; + + hsreq = container_of(_req, struct s3c_hsudc_req, req); + WARN_ON(!list_empty(&hsreq->queue)); + kfree(hsreq); +} + +/** + * s3c_hsudc_queue - Queue a transfer request for the endpoint. + * @_ep: Endpoint for which the request is queued. + * @_req: Request to be queued. + * @gfp_flags: Not used. + * + * Start or enqueue a request for a endpoint when called from gadget driver. + */ +static int s3c_hsudc_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct s3c_hsudc_req *hsreq; + struct s3c_hsudc_ep *hsep; + struct s3c_hsudc *hsudc; + unsigned long flags; + u32 offset; + u32 csr; + + hsreq = container_of(_req, struct s3c_hsudc_req, req); + if ((!_req || !_req->complete || !_req->buf || + !list_empty(&hsreq->queue))) + return -EINVAL; + + hsep = container_of(_ep, struct s3c_hsudc_ep, ep); + hsudc = hsep->dev; + if (!hsudc->driver || hsudc->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + spin_lock_irqsave(&hsudc->lock, flags); + set_index(hsudc, hsep->bEndpointAddress); + + _req->status = -EINPROGRESS; + _req->actual = 0; + + if (!ep_index(hsep) && _req->length == 0) { + hsudc->ep0state = WAIT_FOR_SETUP; + s3c_hsudc_complete_request(hsep, hsreq, 0); + spin_unlock_irqrestore(&hsudc->lock, flags); + return 0; + } + + if (list_empty(&hsep->queue) && !hsep->stopped) { + offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR; + if (ep_is_in(hsep)) { + csr = readl((u32)hsudc->regs + offset); + if (!(csr & S3C_ESR_TX_SUCCESS) && + (s3c_hsudc_write_fifo(hsep, hsreq) == 1)) + hsreq = 0; + } else { + csr = readl((u32)hsudc->regs + offset); + if ((csr & S3C_ESR_RX_SUCCESS) + && (s3c_hsudc_read_fifo(hsep, hsreq) == 1)) + hsreq = 0; + } + } + + if (hsreq != 0) + list_add_tail(&hsreq->queue, &hsep->queue); + + spin_unlock_irqrestore(&hsudc->lock, flags); + return 0; +} + +/** + * s3c_hsudc_dequeue - Dequeue a transfer request from an endpoint. + * @_ep: Endpoint from which the request is dequeued. + * @_req: Request to be dequeued. + * + * Dequeue a request from a endpoint when called from gadget driver. + */ +static int s3c_hsudc_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct s3c_hsudc_ep *hsep = our_ep(_ep); + struct s3c_hsudc *hsudc = hsep->dev; + struct s3c_hsudc_req *hsreq; + unsigned long flags; + + hsep = container_of(_ep, struct s3c_hsudc_ep, ep); + if (!_ep || hsep->ep.name == ep0name) + return -EINVAL; + + spin_lock_irqsave(&hsudc->lock, flags); + + list_for_each_entry(hsreq, &hsep->queue, queue) { + if (&hsreq->req == _req) + break; + } + if (&hsreq->req != _req) { + spin_unlock_irqrestore(&hsudc->lock, flags); + return -EINVAL; + } + + set_index(hsudc, hsep->bEndpointAddress); + s3c_hsudc_complete_request(hsep, hsreq, -ECONNRESET); + + spin_unlock_irqrestore(&hsudc->lock, flags); + return 0; +} + +static struct usb_ep_ops s3c_hsudc_ep_ops = { + .enable = s3c_hsudc_ep_enable, + .disable = s3c_hsudc_ep_disable, + .alloc_request = s3c_hsudc_alloc_request, + .free_request = s3c_hsudc_free_request, + .queue = s3c_hsudc_queue, + .dequeue = s3c_hsudc_dequeue, + .set_halt = s3c_hsudc_set_halt, + .set_wedge = s3c_hsudc_set_wedge, +}; + +/** + * s3c_hsudc_initep - Initialize a endpoint to default state. + * @hsudc - Reference to the device controller. + * @hsep - Endpoint to be initialized. + * @epnum - Address to be assigned to the endpoint. + * + * Initialize a endpoint with default configuration. + */ +static void s3c_hsudc_initep(struct s3c_hsudc *hsudc, + struct s3c_hsudc_ep *hsep, int epnum) +{ + char *dir; + + if ((epnum % 2) == 0) { + dir = "out"; + } else { + dir = "in"; + hsep->bEndpointAddress = USB_DIR_IN; + } + + hsep->bEndpointAddress |= epnum; + if (epnum) + snprintf(hsep->name, sizeof(hsep->name), "ep%d%s", epnum, dir); + else + snprintf(hsep->name, sizeof(hsep->name), "%s", ep0name); + + INIT_LIST_HEAD(&hsep->queue); + INIT_LIST_HEAD(&hsep->ep.ep_list); + if (epnum) + list_add_tail(&hsep->ep.ep_list, &hsudc->gadget.ep_list); + + hsep->dev = hsudc; + hsep->ep.name = hsep->name; + hsep->ep.maxpacket = epnum ? 512 : 64; + hsep->ep.ops = &s3c_hsudc_ep_ops; + hsep->fifo = hsudc->regs + S3C_BR(epnum); + hsep->desc = 0; + hsep->stopped = 0; + hsep->wedge = 0; + + set_index(hsudc, epnum); + writel(hsep->ep.maxpacket, hsudc->regs + S3C_MPR); +} + +/** + * s3c_hsudc_setup_ep - Configure all endpoints to default state. + * @hsudc: Reference to device controller. + * + * Configures all endpoints to default state. + */ +static void s3c_hsudc_setup_ep(struct s3c_hsudc *hsudc) +{ + int epnum; + + hsudc->ep0state = WAIT_FOR_SETUP; + INIT_LIST_HEAD(&hsudc->gadget.ep_list); + for (epnum = 0; epnum < hsudc->pd->epnum; epnum++) + s3c_hsudc_initep(hsudc, &hsudc->ep[epnum], epnum); +} + +/** + * s3c_hsudc_reconfig - Reconfigure the device controller to default state. + * @hsudc: Reference to device controller. + * + * Reconfigures the device controller registers to a default state. + */ +static void s3c_hsudc_reconfig(struct s3c_hsudc *hsudc) +{ + writel(0xAA, hsudc->regs + S3C_EDR); + writel(1, hsudc->regs + S3C_EIER); + writel(0, hsudc->regs + S3C_TR); + writel(S3C_SCR_DTZIEN_EN | S3C_SCR_RRD_EN | S3C_SCR_SUS_EN | + S3C_SCR_RST_EN, hsudc->regs + S3C_SCR); + writel(0, hsudc->regs + S3C_EP0CR); + + s3c_hsudc_setup_ep(hsudc); +} + +/** + * s3c_hsudc_irq - Interrupt handler for device controller. + * @irq: Not used. + * @_dev: Reference to the device controller. + * + * Interrupt handler for the device controller. This handler handles controller + * interrupts and endpoint interrupts. + */ +static irqreturn_t s3c_hsudc_irq(int irq, void *_dev) +{ + struct s3c_hsudc *hsudc = _dev; + struct s3c_hsudc_ep *hsep; + u32 ep_intr; + u32 sys_status; + u32 ep_idx; + + spin_lock(&hsudc->lock); + + sys_status = readl(hsudc->regs + S3C_SSR); + ep_intr = readl(hsudc->regs + S3C_EIR) & 0x3FF; + + if (!ep_intr && !(sys_status & S3C_SSR_DTZIEN_EN)) { + spin_unlock(&hsudc->lock); + return IRQ_HANDLED; + } + + if (sys_status) { + if (sys_status & S3C_SSR_VBUSON) + writel(S3C_SSR_VBUSON, hsudc->regs + S3C_SSR); + + if (sys_status & S3C_SSR_ERR) + writel(S3C_SSR_ERR, hsudc->regs + S3C_SSR); + + if (sys_status & S3C_SSR_SDE) { + writel(S3C_SSR_SDE, hsudc->regs + S3C_SSR); + hsudc->gadget.speed = (sys_status & S3C_SSR_HSP) ? + USB_SPEED_HIGH : USB_SPEED_FULL; + } + + if (sys_status & S3C_SSR_SUSPEND) { + writel(S3C_SSR_SUSPEND, hsudc->regs + S3C_SSR); + if (hsudc->gadget.speed != USB_SPEED_UNKNOWN + && hsudc->driver && hsudc->driver->suspend) + hsudc->driver->suspend(&hsudc->gadget); + } + + if (sys_status & S3C_SSR_RESUME) { + writel(S3C_SSR_RESUME, hsudc->regs + S3C_SSR); + if (hsudc->gadget.speed != USB_SPEED_UNKNOWN + && hsudc->driver && hsudc->driver->resume) + hsudc->driver->resume(&hsudc->gadget); + } + + if (sys_status & S3C_SSR_RESET) { + writel(S3C_SSR_RESET, hsudc->regs + S3C_SSR); + for (ep_idx = 0; ep_idx < hsudc->pd->epnum; ep_idx++) { + hsep = &hsudc->ep[ep_idx]; + hsep->stopped = 1; + s3c_hsudc_nuke_ep(hsep, -ECONNRESET); + } + s3c_hsudc_reconfig(hsudc); + hsudc->ep0state = WAIT_FOR_SETUP; + } + } + + if (ep_intr & S3C_EIR_EP0) { + writel(S3C_EIR_EP0, hsudc->regs + S3C_EIR); + set_index(hsudc, 0); + s3c_hsudc_handle_ep0_intr(hsudc); + } + + ep_intr >>= 1; + ep_idx = 1; + while (ep_intr) { + if (ep_intr & 1) { + hsep = &hsudc->ep[ep_idx]; + set_index(hsudc, ep_idx); + writel(1 << ep_idx, hsudc->regs + S3C_EIR); + if (ep_is_in(hsep)) + s3c_hsudc_epin_intr(hsudc, ep_idx); + else + s3c_hsudc_epout_intr(hsudc, ep_idx); + } + ep_intr >>= 1; + ep_idx++; + } + + spin_unlock(&hsudc->lock); + return IRQ_HANDLED; +} + +int usb_gadget_probe_driver(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) +{ + struct s3c_hsudc *hsudc = the_controller; + int ret; + + if (!driver + || (driver->speed != USB_SPEED_FULL && + driver->speed != USB_SPEED_HIGH) + || !bind + || !driver->unbind || !driver->disconnect || !driver->setup) + return -EINVAL; + + if (!hsudc) + return -ENODEV; + + if (hsudc->driver) + return -EBUSY; + + hsudc->driver = driver; + hsudc->gadget.dev.driver = &driver->driver; + hsudc->gadget.speed = USB_SPEED_UNKNOWN; + ret = device_add(&hsudc->gadget.dev); + if (ret) { + dev_err(hsudc->dev, "failed to probe gadget device"); + return ret; + } + + ret = bind(&hsudc->gadget); + if (ret) { + dev_err(hsudc->dev, "%s: bind failed\n", hsudc->gadget.name); + device_del(&hsudc->gadget.dev); + + hsudc->driver = NULL; + hsudc->gadget.dev.driver = NULL; + return ret; + } + + enable_irq(hsudc->irq); + dev_info(hsudc->dev, "bound driver %s\n", driver->driver.name); + + s3c_hsudc_reconfig(hsudc); + s3c_hsudc_init_phy(); + if (hsudc->pd->gpio_init) + hsudc->pd->gpio_init(); + + return 0; +} +EXPORT_SYMBOL(usb_gadget_probe_driver); + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct s3c_hsudc *hsudc = the_controller; + unsigned long flags; + + if (!hsudc) + return -ENODEV; + + if (!driver || driver != hsudc->driver || !driver->unbind) + return -EINVAL; + + spin_lock_irqsave(&hsudc->lock, flags); + hsudc->driver = 0; + s3c_hsudc_uninit_phy(); + if (hsudc->pd->gpio_uninit) + hsudc->pd->gpio_uninit(); + s3c_hsudc_stop_activity(hsudc, driver); + spin_unlock_irqrestore(&hsudc->lock, flags); + + driver->unbind(&hsudc->gadget); + device_del(&hsudc->gadget.dev); + disable_irq(hsudc->irq); + + dev_info(hsudc->dev, "unregistered gadget driver '%s'\n", + driver->driver.name); + return 0; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + +static inline u32 s3c_hsudc_read_frameno(struct s3c_hsudc *hsudc) +{ + return readl(hsudc->regs + S3C_FNR) & 0x3FF; +} + +static int s3c_hsudc_gadget_getframe(struct usb_gadget *gadget) +{ + return s3c_hsudc_read_frameno(to_hsudc(gadget)); +} + +static struct usb_gadget_ops s3c_hsudc_gadget_ops = { + .get_frame = s3c_hsudc_gadget_getframe, +}; + +static int s3c_hsudc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct s3c_hsudc *hsudc; + struct s3c24xx_hsudc_platdata *pd = pdev->dev.platform_data; + int ret; + + hsudc = kzalloc(sizeof(struct s3c_hsudc) + + sizeof(struct s3c_hsudc_ep) * pd->epnum, + GFP_KERNEL); + if (!hsudc) { + dev_err(dev, "cannot allocate memory\n"); + return -ENOMEM; + } + + the_controller = hsudc; + platform_set_drvdata(pdev, dev); + hsudc->dev = dev; + hsudc->pd = pdev->dev.platform_data; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "unable to obtain driver resource data\n"); + ret = -ENODEV; + goto err_res; + } + + hsudc->mem_rsrc = request_mem_region(res->start, resource_size(res), + dev_name(&pdev->dev)); + if (!hsudc->mem_rsrc) { + dev_err(dev, "failed to reserve register area\n"); + ret = -ENODEV; + goto err_res; + } + + hsudc->regs = ioremap(res->start, resource_size(res)); + if (!hsudc->regs) { + dev_err(dev, "error mapping device register area\n"); + ret = -EBUSY; + goto err_remap; + } + + ret = platform_get_irq(pdev, 0); + if (ret < 0) { + dev_err(dev, "unable to obtain IRQ number\n"); + goto err_irq; + } + hsudc->irq = ret; + + ret = request_irq(hsudc->irq, s3c_hsudc_irq, 0, driver_name, hsudc); + if (ret < 0) { + dev_err(dev, "irq request failed\n"); + goto err_irq; + } + + spin_lock_init(&hsudc->lock); + + device_initialize(&hsudc->gadget.dev); + dev_set_name(&hsudc->gadget.dev, "gadget"); + + hsudc->gadget.is_dualspeed = 1; + hsudc->gadget.ops = &s3c_hsudc_gadget_ops; + hsudc->gadget.name = dev_name(dev); + hsudc->gadget.dev.parent = dev; + hsudc->gadget.dev.dma_mask = dev->dma_mask; + hsudc->gadget.ep0 = &hsudc->ep[0].ep; + + hsudc->gadget.is_otg = 0; + hsudc->gadget.is_a_peripheral = 0; + + s3c_hsudc_setup_ep(hsudc); + + hsudc->uclk = clk_get(&pdev->dev, "usb-device"); + if (IS_ERR(hsudc->uclk)) { + dev_err(dev, "failed to find usb-device clock source\n"); + return PTR_ERR(hsudc->uclk); + } + clk_enable(hsudc->uclk); + + local_irq_disable(); + + disable_irq(hsudc->irq); + local_irq_enable(); + return 0; + +err_irq: + iounmap(hsudc->regs); + +err_remap: + release_resource(hsudc->mem_rsrc); + kfree(hsudc->mem_rsrc); + +err_res: + kfree(hsudc); + return ret; +} + +static struct platform_driver s3c_hsudc_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "s3c-hsudc", + }, + .probe = s3c_hsudc_probe, +}; + +static int __init s3c_hsudc_modinit(void) +{ + return platform_driver_register(&s3c_hsudc_driver); +} + +static void __exit s3c_hsudc_modexit(void) +{ + platform_driver_unregister(&s3c_hsudc_driver); +} + +module_init(s3c_hsudc_modinit); +module_exit(s3c_hsudc_modexit); + +MODULE_DESCRIPTION("Samsung S3C24XX USB high-speed controller driver"); +MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c index b015561..1fa4f70 100644 --- a/drivers/usb/gadget/storage_common.c +++ b/drivers/usb/gadget/storage_common.c @@ -708,13 +708,14 @@ static ssize_t fsg_show_file(struct device *dev, struct device_attribute *attr, static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - ssize_t rc = count; + ssize_t rc; 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 @@ -728,6 +729,7 @@ static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr, curlun->ro = ro; curlun->initially_ro = ro; LDBG(curlun, "read-only status set to %d\n", curlun->ro); + rc = count; } up_read(filesem); return rc; @@ -738,10 +740,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 e0e0787..ab085f1 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -106,13 +106,13 @@ config USB_EHCI_BIG_ENDIAN_MMIO depends on USB_EHCI_HCD && (PPC_CELLEB || PPC_PS3 || 440EPX || \ ARCH_IXP4XX || XPS_USB_HCD_XILINX || \ PPC_MPC512x || CPU_CAVIUM_OCTEON || \ - PMC_MSP) + PMC_MSP || SPARC_LEON) default y config USB_EHCI_BIG_ENDIAN_DESC bool depends on USB_EHCI_HCD && (440EPX || ARCH_IXP4XX || XPS_USB_HCD_XILINX || \ - PPC_MPC512x || PMC_MSP) + PPC_MPC512x || PMC_MSP || SPARC_LEON) default y config XPS_USB_HCD_XILINX @@ -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) @@ -373,7 +396,7 @@ config USB_OHCI_LITTLE_ENDIAN config USB_UHCI_HCD tristate "UHCI HCD (most Intel and VIA) support" - depends on USB && PCI + depends on USB && (PCI || SPARC_LEON) ---help--- The Universal Host Controller Interface is a standard by Intel for accessing the USB hardware in the PC (which is also called the USB @@ -382,11 +405,27 @@ config USB_UHCI_HCD with Intel PCI chipsets (like intel 430TX, 440FX, 440LX, 440BX, i810, i820) conform to this standard. Also all VIA PCI chipsets (like VIA VP2, VP3, MVP3, Apollo Pro, Apollo Pro II or Apollo Pro - 133). If unsure, say Y. + 133) and LEON/GRLIB SoCs with the GRUSBHC controller. + If unsure, say Y. To compile this driver as a module, choose M here: the module will be called uhci-hcd. +config USB_UHCI_SUPPORT_NON_PCI_HC + bool + depends on USB_UHCI_HCD + default y if SPARC_LEON + +config USB_UHCI_BIG_ENDIAN_MMIO + bool + depends on USB_UHCI_SUPPORT_NON_PCI_HC && SPARC_LEON + default y + +config USB_UHCI_BIG_ENDIAN_DESC + bool + depends on USB_UHCI_SUPPORT_NON_PCI_HC && SPARC_LEON + default y + config USB_FHCI_HCD tristate "Freescale QE USB Host Controller support" depends on USB && OF_GPIO && QE_GPIO && QUICC_ENGINE @@ -444,6 +483,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 0000000..98cc8a1 --- /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 hclength; + int ret; + + id = platform_get_device_id(pdev); + if (!id) { + dev_err(hcd->self.controller, "missing device id\n"); + return -EINVAL; + } + + hclength = HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); + switch (id->driver_data) { + case EHCI_ATH79_IP_V1: + ehci->has_synopsys_hc_bug = 1; + + ehci->caps = hcd->regs; + ehci->regs = hcd->regs + hclength; + break; + + case EHCI_ATH79_IP_V2: + hcd->has_tt = 1; + + ehci->caps = hcd->regs + 0x100; + ehci->regs = hcd->regs + 0x100 + hclength; + 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-atmel.c b/drivers/usb/host/ehci-atmel.c index b2ed55c..a5a3ef1 100644 --- a/drivers/usb/host/ehci-atmel.c +++ b/drivers/usb/host/ehci-atmel.c @@ -56,7 +56,7 @@ static int ehci_atmel_setup(struct usb_hcd *hcd) /* registers start at offset 0x0 */ ehci->caps = hcd->regs; ehci->regs = hcd->regs + - HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); dbg_hcs_params(ehci, "reset"); dbg_hcc_params(ehci, "reset"); diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c index a869e3c..42ae574 100644 --- a/drivers/usb/host/ehci-au1xxx.c +++ b/drivers/usb/host/ehci-au1xxx.c @@ -175,7 +175,8 @@ static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev) ehci = hcd_to_ehci(hcd); ehci->caps = hcd->regs; - ehci->regs = hcd->regs + HC_LENGTH(readl(&ehci->caps->hc_capbase)); + ehci->regs = hcd->regs + + HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase)); /* cache this readonly data; minimize chip reads */ ehci->hcs_params = readl(&ehci->caps->hcs_params); @@ -215,10 +216,7 @@ static int ehci_hcd_au1xxx_drv_suspend(struct device *dev) struct usb_hcd *hcd = dev_get_drvdata(dev); struct ehci_hcd *ehci = hcd_to_ehci(hcd); unsigned long flags; - int rc; - - return 0; - rc = 0; + int rc = 0; if (time_before(jiffies, ehci->next_statechange)) msleep(10); @@ -233,13 +231,13 @@ static int ehci_hcd_au1xxx_drv_suspend(struct device *dev) (void)ehci_readl(ehci, &ehci->regs->intr_enable); clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - - au1xxx_stop_ehc(); spin_unlock_irqrestore(&ehci->lock, flags); // could save FLADJ in case of Vaux power loss // ... we'd only use it to handle clock skew + au1xxx_stop_ehc(); + return rc; } diff --git a/drivers/usb/host/ehci-cns3xxx.c b/drivers/usb/host/ehci-cns3xxx.c index 708a05b..d41745c 100644 --- a/drivers/usb/host/ehci-cns3xxx.c +++ b/drivers/usb/host/ehci-cns3xxx.c @@ -34,7 +34,7 @@ static int cns3xxx_ehci_init(struct usb_hcd *hcd) ehci->caps = hcd->regs; ehci->regs = hcd->regs - + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); hcd->has_tt = 0; diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 693c29b..40a844c 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -726,7 +726,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) } /* Capability Registers */ - i = HC_VERSION(ehci_readl(ehci, &ehci->caps->hc_capbase)); + i = HC_VERSION(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); temp = scnprintf (next, size, "bus %s, device %s\n" "%s\n" diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index 5c761df..f380bf9 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -117,6 +117,9 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, pdata->regs = hcd->regs; + if (pdata->power_budget) + hcd->power_budget = pdata->power_budget; + /* * do platform specific init: check the clock, grab/config pins, etc. */ @@ -134,6 +137,30 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED); if (retval != 0) goto err4; + +#ifdef CONFIG_USB_OTG + if (pdata->operating_mode == FSL_USB2_DR_OTG) { + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + + ehci->transceiver = otg_get_transceiver(); + dev_dbg(&pdev->dev, "hcd=0x%p ehci=0x%p, transceiver=0x%p\n", + hcd, ehci, ehci->transceiver); + + if (ehci->transceiver) { + retval = otg_set_host(ehci->transceiver, + &ehci_to_hcd(ehci)->self); + if (retval) { + if (ehci->transceiver) + put_device(ehci->transceiver->dev); + goto err4; + } + } else { + dev_err(&pdev->dev, "can't find transceiver\n"); + retval = -ENODEV; + goto err4; + } + } +#endif return retval; err4: @@ -164,6 +191,12 @@ static void usb_hcd_fsl_remove(struct usb_hcd *hcd, struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + + if (ehci->transceiver) { + otg_set_host(ehci->transceiver, NULL); + put_device(ehci->transceiver->dev); + } usb_remove_hcd(hcd); @@ -291,7 +324,7 @@ static int ehci_fsl_setup(struct usb_hcd *hcd) /* EHCI registers start at offset 0x100 */ ehci->caps = hcd->regs + 0x100; ehci->regs = hcd->regs + 0x100 + - HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); dbg_hcs_params(ehci, "reset"); dbg_hcc_params(ehci, "reset"); @@ -328,6 +361,149 @@ struct ehci_fsl { #ifdef CONFIG_PM +#ifdef CONFIG_PPC_MPC512x +static int ehci_fsl_mpc512x_drv_suspend(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct fsl_usb2_platform_data *pdata = dev->platform_data; + u32 tmp; + +#ifdef DEBUG + u32 mode = ehci_readl(ehci, hcd->regs + FSL_SOC_USB_USBMODE); + mode &= USBMODE_CM_MASK; + tmp = ehci_readl(ehci, hcd->regs + 0x140); /* usbcmd */ + + dev_dbg(dev, "suspend=%d already_suspended=%d " + "mode=%d usbcmd %08x\n", pdata->suspended, + pdata->already_suspended, mode, tmp); +#endif + + /* + * If the controller is already suspended, then this must be a + * PM suspend. Remember this fact, so that we will leave the + * controller suspended at PM resume time. + */ + if (pdata->suspended) { + dev_dbg(dev, "already suspended, leaving early\n"); + pdata->already_suspended = 1; + return 0; + } + + dev_dbg(dev, "suspending...\n"); + + hcd->state = HC_STATE_SUSPENDED; + dev->power.power_state = PMSG_SUSPEND; + + /* ignore non-host interrupts */ + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + + /* stop the controller */ + tmp = ehci_readl(ehci, &ehci->regs->command); + tmp &= ~CMD_RUN; + ehci_writel(ehci, tmp, &ehci->regs->command); + + /* save EHCI registers */ + pdata->pm_command = ehci_readl(ehci, &ehci->regs->command); + pdata->pm_command &= ~CMD_RUN; + pdata->pm_status = ehci_readl(ehci, &ehci->regs->status); + pdata->pm_intr_enable = ehci_readl(ehci, &ehci->regs->intr_enable); + pdata->pm_frame_index = ehci_readl(ehci, &ehci->regs->frame_index); + pdata->pm_segment = ehci_readl(ehci, &ehci->regs->segment); + pdata->pm_frame_list = ehci_readl(ehci, &ehci->regs->frame_list); + pdata->pm_async_next = ehci_readl(ehci, &ehci->regs->async_next); + pdata->pm_configured_flag = + ehci_readl(ehci, &ehci->regs->configured_flag); + pdata->pm_portsc = ehci_readl(ehci, &ehci->regs->port_status[0]); + pdata->pm_usbgenctrl = ehci_readl(ehci, + hcd->regs + FSL_SOC_USB_USBGENCTRL); + + /* clear the W1C bits */ + pdata->pm_portsc &= cpu_to_hc32(ehci, ~PORT_RWC_BITS); + + pdata->suspended = 1; + + /* clear PP to cut power to the port */ + tmp = ehci_readl(ehci, &ehci->regs->port_status[0]); + tmp &= ~PORT_POWER; + ehci_writel(ehci, tmp, &ehci->regs->port_status[0]); + + return 0; +} + +static int ehci_fsl_mpc512x_drv_resume(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct fsl_usb2_platform_data *pdata = dev->platform_data; + u32 tmp; + + dev_dbg(dev, "suspend=%d already_suspended=%d\n", + pdata->suspended, pdata->already_suspended); + + /* + * If the controller was already suspended at suspend time, + * then don't resume it now. + */ + if (pdata->already_suspended) { + dev_dbg(dev, "already suspended, leaving early\n"); + pdata->already_suspended = 0; + return 0; + } + + if (!pdata->suspended) { + dev_dbg(dev, "not suspended, leaving early\n"); + return 0; + } + + pdata->suspended = 0; + + dev_dbg(dev, "resuming...\n"); + + /* set host mode */ + tmp = USBMODE_CM_HOST | (pdata->es ? USBMODE_ES : 0); + ehci_writel(ehci, tmp, hcd->regs + FSL_SOC_USB_USBMODE); + + ehci_writel(ehci, pdata->pm_usbgenctrl, + hcd->regs + FSL_SOC_USB_USBGENCTRL); + ehci_writel(ehci, ISIPHYCTRL_PXE | ISIPHYCTRL_PHYE, + hcd->regs + FSL_SOC_USB_ISIPHYCTRL); + + /* restore EHCI registers */ + ehci_writel(ehci, pdata->pm_command, &ehci->regs->command); + ehci_writel(ehci, pdata->pm_intr_enable, &ehci->regs->intr_enable); + ehci_writel(ehci, pdata->pm_frame_index, &ehci->regs->frame_index); + ehci_writel(ehci, pdata->pm_segment, &ehci->regs->segment); + ehci_writel(ehci, pdata->pm_frame_list, &ehci->regs->frame_list); + ehci_writel(ehci, pdata->pm_async_next, &ehci->regs->async_next); + ehci_writel(ehci, pdata->pm_configured_flag, + &ehci->regs->configured_flag); + ehci_writel(ehci, pdata->pm_portsc, &ehci->regs->port_status[0]); + + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + hcd->state = HC_STATE_RUNNING; + dev->power.power_state = PMSG_ON; + + tmp = ehci_readl(ehci, &ehci->regs->command); + tmp |= CMD_RUN; + ehci_writel(ehci, tmp, &ehci->regs->command); + + usb_hcd_resume_root_hub(hcd); + + return 0; +} +#else +static inline int ehci_fsl_mpc512x_drv_suspend(struct device *dev) +{ + return 0; +} + +static inline int ehci_fsl_mpc512x_drv_resume(struct device *dev) +{ + return 0; +} +#endif /* CONFIG_PPC_MPC512x */ + static struct ehci_fsl *hcd_to_ehci_fsl(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); @@ -341,6 +517,11 @@ static int ehci_fsl_drv_suspend(struct device *dev) struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd); void __iomem *non_ehci = hcd->regs; + if (of_device_is_compatible(dev->parent->of_node, + "fsl,mpc5121-usb2-dr")) { + return ehci_fsl_mpc512x_drv_suspend(dev); + } + ehci_prepare_ports_for_controller_suspend(hcd_to_ehci(hcd), device_may_wakeup(dev)); if (!fsl_deep_sleep()) @@ -357,6 +538,11 @@ static int ehci_fsl_drv_resume(struct device *dev) struct ehci_hcd *ehci = hcd_to_ehci(hcd); void __iomem *non_ehci = hcd->regs; + if (of_device_is_compatible(dev->parent->of_node, + "fsl,mpc5121-usb2-dr")) { + return ehci_fsl_mpc512x_drv_resume(dev); + } + ehci_prepare_ports_for_controller_resume(ehci); if (!fsl_deep_sleep()) return 0; @@ -391,6 +577,38 @@ static struct dev_pm_ops ehci_fsl_pm_ops = { #define EHCI_FSL_PM_OPS NULL #endif /* CONFIG_PM */ +#ifdef CONFIG_USB_OTG +static int ehci_start_port_reset(struct usb_hcd *hcd, unsigned port) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + u32 status; + + if (!port) + return -EINVAL; + + port--; + + /* start port reset before HNP protocol time out */ + status = readl(&ehci->regs->port_status[port]); + if (!(status & PORT_CONNECT)) + return -ENODEV; + + /* khubd will finish the reset later */ + if (ehci_is_TDI(ehci)) { + writel(PORT_RESET | + (status & ~(PORT_CSC | PORT_PEC | PORT_OCC)), + &ehci->regs->port_status[port]); + } else { + writel(PORT_RESET, &ehci->regs->port_status[port]); + } + + return 0; +} +#else +#define ehci_start_port_reset NULL +#endif /* CONFIG_USB_OTG */ + + static const struct hc_driver ehci_fsl_hc_driver = { .description = hcd_name, .product_desc = "Freescale On-Chip EHCI Host Controller", @@ -430,6 +648,7 @@ static const struct hc_driver ehci_fsl_hc_driver = { .hub_control = ehci_hub_control, .bus_suspend = ehci_bus_suspend, .bus_resume = ehci_bus_resume, + .start_port_reset = ehci_start_port_reset, .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, diff --git a/drivers/usb/host/ehci-fsl.h b/drivers/usb/host/ehci-fsl.h index 3fabed3..4918062 100644 --- a/drivers/usb/host/ehci-fsl.h +++ b/drivers/usb/host/ehci-fsl.h @@ -27,6 +27,10 @@ #define PORT_PTS_SERIAL (3<<30) #define PORT_PTS_PTW (1<<28) #define FSL_SOC_USB_PORTSC2 0x188 +#define FSL_SOC_USB_USBMODE 0x1a8 +#define USBMODE_CM_MASK (3 << 0) /* controller mode mask */ +#define USBMODE_CM_HOST (3 << 0) /* controller mode: host */ +#define USBMODE_ES (1 << 2) /* (Big) Endian Select */ #define FSL_SOC_USB_USBGENCTRL 0x200 #define USBGENCTRL_PPP (1 << 3) diff --git a/drivers/usb/host/ehci-grlib.c b/drivers/usb/host/ehci-grlib.c new file mode 100644 index 0000000..93b230d --- /dev/null +++ b/drivers/usb/host/ehci-grlib.c @@ -0,0 +1,242 @@ +/* + * Driver for Aeroflex Gaisler GRLIB GRUSBHC EHCI host controller + * + * GRUSBHC is typically found on LEON/GRLIB SoCs + * + * (c) Jan Andersson <jan@gaisler.com> + * + * Based on ehci-ppc-of.c which is: + * (c) Valentine Barshak <vbarshak@ru.mvista.com> + * and in turn based on "ehci-ppc-soc.c" by Stefan Roese <sr@denx.de> + * and "ohci-ppc-of.c" by Sylvain Munaut <tnt@246tNt.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include <linux/signal.h> + +#include <linux/of_irq.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> + +#define GRUSBHC_HCIVERSION 0x0100 /* Known value of cap. reg. HCIVERSION */ + +/* called during probe() after chip reset completes */ +static int ehci_grlib_setup(struct usb_hcd *hcd) +{ + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + int retval; + + retval = ehci_halt(ehci); + if (retval) + return retval; + + retval = ehci_init(hcd); + if (retval) + return retval; + + ehci->sbrn = 0x20; + ehci_port_power(ehci, 1); + + return ehci_reset(ehci); +} + + +static const struct hc_driver ehci_grlib_hc_driver = { + .description = hcd_name, + .product_desc = "GRLIB GRUSBHC EHCI", + .hcd_priv_size = sizeof(struct ehci_hcd), + + /* + * generic hardware linkage + */ + .irq = ehci_irq, + .flags = HCD_MEMORY | HCD_USB2, + + /* + * basic lifecycle operations + */ + .reset = ehci_grlib_setup, + .start = ehci_run, + .stop = ehci_stop, + .shutdown = ehci_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ehci_urb_enqueue, + .urb_dequeue = ehci_urb_dequeue, + .endpoint_disable = ehci_endpoint_disable, + .endpoint_reset = ehci_endpoint_reset, + + /* + * scheduling support + */ + .get_frame_number = ehci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ehci_hub_status_data, + .hub_control = ehci_hub_control, +#ifdef CONFIG_PM + .bus_suspend = ehci_bus_suspend, + .bus_resume = ehci_bus_resume, +#endif + .relinquish_port = ehci_relinquish_port, + .port_handed_over = ehci_port_handed_over, + + .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, +}; + + +static int __devinit ehci_hcd_grlib_probe(struct platform_device *op) +{ + struct device_node *dn = op->dev.of_node; + struct usb_hcd *hcd; + struct ehci_hcd *ehci = NULL; + struct resource res; + u32 hc_capbase; + int irq; + int rv; + + if (usb_disabled()) + return -ENODEV; + + dev_dbg(&op->dev, "initializing GRUSBHC EHCI USB Controller\n"); + + rv = of_address_to_resource(dn, 0, &res); + if (rv) + return rv; + + /* usb_create_hcd requires dma_mask != NULL */ + op->dev.dma_mask = &op->dev.coherent_dma_mask; + hcd = usb_create_hcd(&ehci_grlib_hc_driver, &op->dev, + "GRUSBHC EHCI USB"); + 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)) { + printk(KERN_ERR "%s: request_mem_region failed\n", __FILE__); + rv = -EBUSY; + goto err_rmr; + } + + irq = irq_of_parse_and_map(dn, 0); + if (irq == NO_IRQ) { + printk(KERN_ERR "%s: irq_of_parse_and_map failed\n", __FILE__); + rv = -EBUSY; + goto err_irq; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + printk(KERN_ERR "%s: ioremap failed\n", __FILE__); + rv = -ENOMEM; + goto err_ioremap; + } + + ehci = hcd_to_ehci(hcd); + + ehci->caps = hcd->regs; + + /* determine endianness of this implementation */ + hc_capbase = ehci_readl(ehci, &ehci->caps->hc_capbase); + if (HC_VERSION(ehci, hc_capbase) != GRUSBHC_HCIVERSION) { + ehci->big_endian_mmio = 1; + ehci->big_endian_desc = 1; + ehci->big_endian_capbase = 1; + } + + ehci->regs = hcd->regs + + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); + + /* cache this readonly data; minimize chip reads */ + ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); + + rv = usb_add_hcd(hcd, irq, 0); + if (rv) + goto err_ehci; + + return 0; + +err_ehci: + iounmap(hcd->regs); +err_ioremap: + irq_dispose_mapping(irq); +err_irq: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err_rmr: + usb_put_hcd(hcd); + + return rv; +} + + +static int ehci_hcd_grlib_remove(struct platform_device *op) +{ + struct usb_hcd *hcd = dev_get_drvdata(&op->dev); + + dev_set_drvdata(&op->dev, NULL); + + dev_dbg(&op->dev, "stopping GRLIB GRUSBHC EHCI USB Controller\n"); + + usb_remove_hcd(hcd); + + iounmap(hcd->regs); + irq_dispose_mapping(hcd->irq); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + + usb_put_hcd(hcd); + + return 0; +} + + +static void ehci_hcd_grlib_shutdown(struct platform_device *op) +{ + struct usb_hcd *hcd = dev_get_drvdata(&op->dev); + + if (hcd->driver->shutdown) + hcd->driver->shutdown(hcd); +} + + +static const struct of_device_id ehci_hcd_grlib_of_match[] = { + { + .name = "GAISLER_EHCI", + }, + { + .name = "01_026", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, ehci_hcd_grlib_of_match); + + +static struct platform_driver ehci_grlib_driver = { + .probe = ehci_hcd_grlib_probe, + .remove = ehci_hcd_grlib_remove, + .shutdown = ehci_hcd_grlib_shutdown, + .driver = { + .name = "grlib-ehci", + .owner = THIS_MODULE, + .of_match_table = ehci_hcd_grlib_of_match, + }, +}; diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 78561d1..b435ed6 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -739,7 +739,7 @@ static int ehci_run (struct usb_hcd *hcd) up_write(&ehci_cf_port_reset_rwsem); ehci->last_periodic_enable = ktime_get_real(); - temp = HC_VERSION(ehci_readl(ehci, &ehci->caps->hc_capbase)); + temp = HC_VERSION(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); ehci_info (ehci, "USB %x.%x started, EHCI %x.%02x%s\n", ((ehci->sbrn & 0xf0)>>4), (ehci->sbrn & 0x0f), @@ -777,8 +777,9 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) goto dead; } + /* Shared IRQ? */ masked_status = status & INTR_MASK; - if (!masked_status) { /* irq sharing? */ + if (!masked_status || unlikely(hcd->state == HC_STATE_HALT)) { spin_unlock(&ehci->lock); return IRQ_NONE; } @@ -873,6 +874,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) dead: ehci_reset(ehci); ehci_writel(ehci, 0, &ehci->regs->configured_flag); + usb_hc_died(hcd); /* generic layer kills/unlinks all urbs, then * uses ehci_stop to clean up the rest */ @@ -1265,6 +1267,21 @@ 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 + +#ifdef CONFIG_SPARC_LEON +#include "ehci-grlib.c" +#define PLATFORM_DRIVER ehci_grlib_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 d05ea03..ea6184b 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -27,6 +27,7 @@ */ /*-------------------------------------------------------------------------*/ +#include <linux/usb/otg.h> #define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E) @@ -127,7 +128,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; @@ -801,6 +802,13 @@ static int ehci_hub_control ( goto error; if (ehci->no_selective_suspend) break; +#ifdef CONFIG_USB_OTG + if ((hcd->self.otg_port == (wIndex + 1)) + && hcd->self.b_hnp_enable) { + otg_start_hnp(ehci->transceiver); + break; + } +#endif if (!(temp & PORT_SUSPEND)) break; if ((temp & PORT_PE) == 0) diff --git a/drivers/usb/host/ehci-ixp4xx.c b/drivers/usb/host/ehci-ixp4xx.c index 89b7c70..50e600d 100644 --- a/drivers/usb/host/ehci-ixp4xx.c +++ b/drivers/usb/host/ehci-ixp4xx.c @@ -23,7 +23,7 @@ static int ixp4xx_ehci_init(struct usb_hcd *hcd) ehci->caps = hcd->regs + 0x100; ehci->regs = hcd->regs + 0x100 - + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); hcd->has_tt = 1; diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index 9ce1b0b..b5a0bf6 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -41,7 +41,7 @@ static int ehci_msm_reset(struct usb_hcd *hcd) ehci->caps = USB_CAPLENGTH; ehci->regs = USB_CAPLENGTH + - HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); dbg_hcs_params(ehci, "reset"); dbg_hcc_params(ehci, "reset"); diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c index 25c8c10..0c058be 100644 --- a/drivers/usb/host/ehci-mxc.c +++ b/drivers/usb/host/ehci-mxc.c @@ -208,7 +208,7 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev) /* EHCI registers start at offset 0x100 */ ehci->caps = hcd->regs + 0x100; ehci->regs = hcd->regs + 0x100 + - HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); /* set up the PORTSCx register */ ehci_writel(ehci, pdata->portsc, &ehci->regs->port_status[0]); diff --git a/drivers/usb/host/ehci-octeon.c b/drivers/usb/host/ehci-octeon.c index a31a031..ff55757 100644 --- a/drivers/usb/host/ehci-octeon.c +++ b/drivers/usb/host/ehci-octeon.c @@ -151,7 +151,7 @@ static int ehci_octeon_drv_probe(struct platform_device *pdev) ehci->caps = hcd->regs; ehci->regs = hcd->regs + - HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); /* cache this readonly data; minimize chip reads */ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c index 627f3a6..55a57c2 100644 --- a/drivers/usb/host/ehci-omap.c +++ b/drivers/usb/host/ehci-omap.c @@ -208,7 +208,7 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev) /* we know this is the memory we want, no need to ioremap again */ omap_ehci->caps = hcd->regs; omap_ehci->regs = hcd->regs - + HC_LENGTH(readl(&omap_ehci->caps->hc_capbase)); + + HC_LENGTH(ehci, readl(&omap_ehci->caps->hc_capbase)); dbg_hcs_params(omap_ehci, "reset"); dbg_hcc_params(omap_ehci, "reset"); diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 281e094..395bdb0 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -251,7 +251,7 @@ static int __devinit ehci_orion_drv_probe(struct platform_device *pdev) ehci = hcd_to_ehci(hcd); ehci->caps = hcd->regs + 0x100; ehci->regs = hcd->regs + 0x100 + - HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); hcd->has_tt = 1; ehci->sbrn = 0x20; diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index d5eaea7..660b80a 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -70,7 +70,7 @@ static int ehci_pci_setup(struct usb_hcd *hcd) ehci->caps = hcd->regs; ehci->regs = hcd->regs + - HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); dbg_hcs_params(ehci, "reset"); dbg_hcc_params(ehci, "reset"); diff --git a/drivers/usb/host/ehci-pmcmsp.c b/drivers/usb/host/ehci-pmcmsp.c index a216864..cd69099 100644 --- a/drivers/usb/host/ehci-pmcmsp.c +++ b/drivers/usb/host/ehci-pmcmsp.c @@ -83,7 +83,7 @@ static int ehci_msp_setup(struct usb_hcd *hcd) ehci->caps = hcd->regs; ehci->regs = hcd->regs + - HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); dbg_hcs_params(ehci, "reset"); dbg_hcc_params(ehci, "reset"); diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c index 1f09f25..8552db6 100644 --- a/drivers/usb/host/ehci-ppc-of.c +++ b/drivers/usb/host/ehci-ppc-of.c @@ -179,7 +179,7 @@ static int __devinit ehci_hcd_ppc_of_probe(struct platform_device *op) ehci->caps = hcd->regs; ehci->regs = hcd->regs + - HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); /* cache this readonly data; minimize chip reads */ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c index 1dee33b..64626a7 100644 --- a/drivers/usb/host/ehci-ps3.c +++ b/drivers/usb/host/ehci-ps3.c @@ -29,7 +29,7 @@ static int ps3_ehci_hc_reset(struct usb_hcd *hcd) ehci->big_endian_mmio = 1; ehci->caps = hcd->regs; - ehci->regs = hcd->regs + HC_LENGTH(ehci_readl(ehci, + ehci->regs = hcd->regs + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); dbg_hcs_params(ehci, "reset"); diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 42abd0f..5d6bc62 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -826,6 +826,7 @@ qh_make ( is_input, 0, hb_mult(maxp) * max_packet(maxp))); qh->start = NO_FRAME; + qh->stamp = ehci->periodic_stamp; if (urb->dev->speed == USB_SPEED_HIGH) { qh->c_usecs = 0; @@ -1183,6 +1184,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 0000000..e3374c8 --- /dev/null +++ b/drivers/usb/host/ehci-s5p.c @@ -0,0 +1,202 @@ +/* + * 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 __devinit 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(ehci, 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 __devexit 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 = __devexit_p(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-sched.c b/drivers/usb/host/ehci-sched.c index 1543c83..6c9fbe3 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -471,8 +471,10 @@ static int enable_periodic (struct ehci_hcd *ehci) */ status = handshake_on_error_set_halt(ehci, &ehci->regs->status, STS_PSS, 0, 9 * 125); - if (status) + if (status) { + usb_hc_died(ehci_to_hcd(ehci)); return status; + } cmd = ehci_readl(ehci, &ehci->regs->command) | CMD_PSE; ehci_writel(ehci, cmd, &ehci->regs->command); @@ -510,8 +512,10 @@ static int disable_periodic (struct ehci_hcd *ehci) */ status = handshake_on_error_set_halt(ehci, &ehci->regs->status, STS_PSS, STS_PSS, 9 * 125); - if (status) + if (status) { + usb_hc_died(ehci_to_hcd(ehci)); return status; + } cmd = ehci_readl(ehci, &ehci->regs->command) & ~CMD_PSE; ehci_writel(ehci, cmd, &ehci->regs->command); @@ -2287,6 +2291,7 @@ scan_periodic (struct ehci_hcd *ehci) } clock &= mod - 1; clock_frame = clock >> 3; + ++ehci->periodic_stamp; for (;;) { union ehci_shadow q, *q_p; @@ -2315,10 +2320,14 @@ restart: temp.qh = qh_get (q.qh); type = Q_NEXT_TYPE(ehci, q.qh->hw->hw_next); q = q.qh->qh_next; - modified = qh_completions (ehci, temp.qh); - if (unlikely(list_empty(&temp.qh->qtd_list) || - temp.qh->needs_rescan)) - intr_deschedule (ehci, temp.qh); + if (temp.qh->stamp != ehci->periodic_stamp) { + modified = qh_completions(ehci, temp.qh); + if (!modified) + temp.qh->stamp = ehci->periodic_stamp; + if (unlikely(list_empty(&temp.qh->qtd_list) || + temp.qh->needs_rescan)) + intr_deschedule(ehci, temp.qh); + } qh_put (temp.qh); break; case Q_TYPE_FSTN: @@ -2460,6 +2469,7 @@ restart: if (ehci->clock_frame != clock_frame) { free_cached_lists(ehci); ehci->clock_frame = clock_frame; + ++ehci->periodic_stamp; } } else { now_uframe++; diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c index 595f70f..86a95bb 100644 --- a/drivers/usb/host/ehci-sh.c +++ b/drivers/usb/host/ehci-sh.c @@ -23,7 +23,7 @@ static int ehci_sh_reset(struct usb_hcd *hcd) int ret; ehci->caps = hcd->regs; - ehci->regs = hcd->regs + HC_LENGTH(ehci_readl(ehci, + ehci->regs = hcd->regs + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); dbg_hcs_params(ehci, "reset"); diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c index 75c0087..dbf1e4e 100644 --- a/drivers/usb/host/ehci-spear.c +++ b/drivers/usb/host/ehci-spear.c @@ -38,7 +38,7 @@ static int ehci_spear_setup(struct usb_hcd *hcd) /* registers start at offset 0x0 */ ehci->caps = hcd->regs; - ehci->regs = hcd->regs + HC_LENGTH(ehci_readl(ehci, + ehci->regs = hcd->regs + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); /* cache this readonly data; minimize chip reads */ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index a516af2..02b2bfd 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -58,6 +58,71 @@ static void tegra_ehci_power_down(struct usb_hcd *hcd) clk_disable(tegra->emc_clk); } +static int tegra_ehci_internal_port_reset( + struct ehci_hcd *ehci, + u32 __iomem *portsc_reg +) +{ + u32 temp; + unsigned long flags; + int retval = 0; + int i, tries; + u32 saved_usbintr; + + spin_lock_irqsave(&ehci->lock, flags); + saved_usbintr = ehci_readl(ehci, &ehci->regs->intr_enable); + /* disable USB interrupt */ + ehci_writel(ehci, 0, &ehci->regs->intr_enable); + spin_unlock_irqrestore(&ehci->lock, flags); + + /* + * Here we have to do Port Reset at most twice for + * Port Enable bit to be set. + */ + for (i = 0; i < 2; i++) { + temp = ehci_readl(ehci, portsc_reg); + temp |= PORT_RESET; + ehci_writel(ehci, temp, portsc_reg); + mdelay(10); + temp &= ~PORT_RESET; + ehci_writel(ehci, temp, portsc_reg); + mdelay(1); + tries = 100; + do { + mdelay(1); + /* + * Up to this point, Port Enable bit is + * expected to be set after 2 ms waiting. + * USB1 usually takes extra 45 ms, for safety, + * we take 100 ms as timeout. + */ + temp = ehci_readl(ehci, portsc_reg); + } while (!(temp & PORT_PE) && tries--); + if (temp & PORT_PE) + break; + } + if (i == 2) + retval = -ETIMEDOUT; + + /* + * Clear Connect Status Change bit if it's set. + * We can't clear PORT_PEC. It will also cause PORT_PE to be cleared. + */ + if (temp & PORT_CSC) + ehci_writel(ehci, PORT_CSC, portsc_reg); + + /* + * Write to clear any interrupt status bits that might be set + * during port reset. + */ + temp = ehci_readl(ehci, &ehci->regs->status); + ehci_writel(ehci, temp, &ehci->regs->status); + + /* restore original interrupt enable bits */ + ehci_writel(ehci, saved_usbintr, &ehci->regs->intr_enable); + return retval; +} + static int tegra_ehci_hub_control( struct usb_hcd *hcd, u16 typeReq, @@ -121,6 +186,13 @@ static int tegra_ehci_hub_control( goto done; } + /* For USB1 port we need to issue Port Reset twice internally */ + if (tegra->phy->instance == 0 && + (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_RESET)) { + spin_unlock_irqrestore(&ehci->lock, flags); + return tegra_ehci_internal_port_reset(ehci, status_reg); + } + /* * Tegra host controller will time the resume operation to clear the bit * when the port control state switches to HS or FS Idle. This behavior @@ -328,7 +400,7 @@ static int tegra_ehci_setup(struct usb_hcd *hcd) /* EHCI registers start at offset 0x100 */ ehci->caps = hcd->regs + 0x100; ehci->regs = hcd->regs + 0x100 + - HC_LENGTH(readl(&ehci->caps->hc_capbase)); + HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase)); dbg_hcs_params(ehci, "reset"); dbg_hcc_params(ehci, "reset"); diff --git a/drivers/usb/host/ehci-vt8500.c b/drivers/usb/host/ehci-vt8500.c index 2016806..47d7496 100644 --- a/drivers/usb/host/ehci-vt8500.c +++ b/drivers/usb/host/ehci-vt8500.c @@ -121,7 +121,8 @@ static int vt8500_ehci_drv_probe(struct platform_device *pdev) ehci = hcd_to_ehci(hcd); ehci->caps = hcd->regs; - ehci->regs = hcd->regs + HC_LENGTH(readl(&ehci->caps->hc_capbase)); + ehci->regs = hcd->regs + + HC_LENGTH(ehci, readl(&ehci->caps->hc_capbase)); dbg_hcs_params(ehci, "reset"); dbg_hcc_params(ehci, "reset"); diff --git a/drivers/usb/host/ehci-w90x900.c b/drivers/usb/host/ehci-w90x900.c index 6bc3580..52a027a 100644 --- a/drivers/usb/host/ehci-w90x900.c +++ b/drivers/usb/host/ehci-w90x900.c @@ -57,7 +57,7 @@ static int __devinit usb_w90x900_probe(const struct hc_driver *driver, ehci = hcd_to_ehci(hcd); ehci->caps = hcd->regs; ehci->regs = hcd->regs + - HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); /* enable PHY 0,1,the regs only apply to w90p910 * 0xA4,0xA8 were offsets of PHY0 and PHY1 controller of diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c index effc58d..a64d6d6 100644 --- a/drivers/usb/host/ehci-xilinx-of.c +++ b/drivers/usb/host/ehci-xilinx-of.c @@ -220,7 +220,7 @@ static int __devinit ehci_hcd_xilinx_of_probe(struct platform_device *op) */ ehci->caps = hcd->regs + 0x100; ehci->regs = hcd->regs + 0x100 + - HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); + HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase)); /* cache this readonly data; minimize chip reads */ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 333ddc1..bd6ff48 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -118,6 +118,7 @@ struct ehci_hcd { /* one per controller */ struct timer_list watchdog; unsigned long actions; unsigned stamp; + unsigned periodic_stamp; unsigned random_frame; unsigned long next_statechange; ktime_t last_periodic_enable; @@ -128,12 +129,14 @@ struct ehci_hcd { /* one per controller */ unsigned has_fsl_port_bug:1; /* FreeScale */ unsigned big_endian_mmio:1; unsigned big_endian_desc:1; + unsigned big_endian_capbase:1; unsigned has_amcc_usb23:1; unsigned need_io_watchdog:1; unsigned broken_periodic:1; 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) @@ -160,6 +163,10 @@ struct ehci_hcd { /* one per controller */ #ifdef DEBUG struct dentry *debug_dir; #endif + /* + * OTG controllers and transceivers need software interaction + */ + struct otg_transceiver *transceiver; }; /* convert between an HCD pointer and the corresponding EHCI_HCD */ @@ -600,12 +607,18 @@ ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc) * This attempts to support either format at compile time without a * runtime penalty, or both formats with the additional overhead * of checking a flag bit. + * + * ehci_big_endian_capbase is a special quirk for controllers that + * implement the HC capability registers as separate registers and not + * as fields of a 32-bit register. */ #ifdef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO #define ehci_big_endian_mmio(e) ((e)->big_endian_mmio) +#define ehci_big_endian_capbase(e) ((e)->big_endian_capbase) #else #define ehci_big_endian_mmio(e) 0 +#define ehci_big_endian_capbase(e) 0 #endif /* diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index c0e22f2..baae4cc 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -612,6 +612,7 @@ static irqreturn_t isp116x_irq(struct usb_hcd *hcd) /* IRQ's are off, we do no DMA, perfectly ready to die ... */ hcd->state = HC_STATE_HALT; + usb_hc_died(hcd); ret = IRQ_HANDLED; goto done; } diff --git a/drivers/usb/host/isp1760-hcd.c b/drivers/usb/host/isp1760-hcd.c index 7b2e69a..c9e6e45 100644 --- a/drivers/usb/host/isp1760-hcd.c +++ b/drivers/usb/host/isp1760-hcd.c @@ -8,6 +8,8 @@ * * (c) 2007 Sebastian Siewior <bigeasy@linutronix.de> * + * (c) 2011 Arvid Brodin <arvid.brodin@enea.com> + * */ #include <linux/module.h> #include <linux/kernel.h> @@ -26,14 +28,18 @@ static struct kmem_cache *qtd_cachep; static struct kmem_cache *qh_cachep; +static struct kmem_cache *urb_listitem_cachep; struct isp1760_hcd { u32 hcs_params; spinlock_t lock; - struct inter_packet_info atl_ints[32]; - struct inter_packet_info int_ints[32]; + struct slotinfo atl_slots[32]; + int atl_done_map; + struct slotinfo int_slots[32]; + int int_done_map; struct memory_chunk memory_pool[BLOCKS]; - u32 atl_queued; + struct list_head controlqhs, bulkqhs, interruptqhs; + int active_ptds; /* periodic schedule support */ #define DEFAULT_I_TDPS 1024 @@ -85,18 +91,34 @@ struct isp1760_qtd { struct list_head qtd_list; struct urb *urb; size_t length; - - /* isp special*/ + size_t actual_length; + + /* QTD_ENQUEUED: waiting for transfer (inactive) */ + /* QTD_PAYLOAD_ALLOC: chip mem has been allocated for payload */ + /* QTD_XFER_STARTED: valid ptd has been written to isp176x - only + interrupt handler may touch this qtd! */ + /* QTD_XFER_COMPLETE: payload has been transferred successfully */ + /* QTD_RETIRE: transfer error/abort qtd */ +#define QTD_ENQUEUED 0 +#define QTD_PAYLOAD_ALLOC 1 +#define QTD_XFER_STARTED 2 +#define QTD_XFER_COMPLETE 3 +#define QTD_RETIRE 4 u32 status; -#define URB_ENQUEUED (1 << 1) }; +/* Queue head, one for each active endpoint */ struct isp1760_qh { - /* first part defined by EHCI spec */ + struct list_head qh_list; struct list_head qtd_list; - u32 toggle; u32 ping; + int slot; +}; + +struct urb_listitem { + struct list_head urb_list; + struct urb *urb; }; /* @@ -272,7 +294,7 @@ static void init_memory(struct isp1760_hcd *priv) payload_addr += priv->memory_pool[curr + i].size; } - BUG_ON(payload_addr - priv->memory_pool[0].start > PAYLOAD_AREA_SIZE); + WARN_ON(payload_addr - priv->memory_pool[0].start > PAYLOAD_AREA_SIZE); } static void alloc_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd) @@ -280,7 +302,7 @@ static void alloc_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd) struct isp1760_hcd *priv = hcd_to_priv(hcd); int i; - BUG_ON(qtd->payload_addr); + WARN_ON(qtd->payload_addr); if (!qtd->length) return; @@ -293,19 +315,6 @@ static void alloc_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd) return; } } - - dev_err(hcd->self.controller, - "%s: Cannot allocate %zu bytes of memory\n" - "Current memory map:\n", - __func__, qtd->length); - for (i = 0; i < BLOCKS; i++) { - dev_err(hcd->self.controller, "Pool %2d size %4d status: %d\n", - i, priv->memory_pool[i].size, - priv->memory_pool[i].free); - } - /* XXX maybe -ENOMEM could be possible */ - BUG(); - return; } static void free_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd) @@ -318,7 +327,7 @@ static void free_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd) for (i = 0; i < BLOCKS; i++) { if (priv->memory_pool[i].start == qtd->payload_addr) { - BUG_ON(priv->memory_pool[i].free); + WARN_ON(priv->memory_pool[i].free); priv->memory_pool[i].free = 1; qtd->payload_addr = 0; return; @@ -327,19 +336,8 @@ static void free_mem(struct usb_hcd *hcd, struct isp1760_qtd *qtd) dev_err(hcd->self.controller, "%s: Invalid pointer: %08x\n", __func__, qtd->payload_addr); - BUG(); -} - -static void isp1760_init_regs(struct usb_hcd *hcd) -{ - reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, 0); - reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE); - reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE); - reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE); - - reg_write32(hcd->regs, HC_ATL_PTD_DONEMAP_REG, ~NO_TRANSFER_ACTIVE); - reg_write32(hcd->regs, HC_INT_PTD_DONEMAP_REG, ~NO_TRANSFER_ACTIVE); - reg_write32(hcd->regs, HC_ISO_PTD_DONEMAP_REG, ~NO_TRANSFER_ACTIVE); + WARN_ON(1); + qtd->payload_addr = 0; } static int handshake(struct usb_hcd *hcd, u32 reg, @@ -377,31 +375,27 @@ static int ehci_reset(struct usb_hcd *hcd) return retval; } -static void qh_destroy(struct isp1760_qh *qh) -{ - BUG_ON(!list_empty(&qh->qtd_list)); - kmem_cache_free(qh_cachep, qh); -} - -static struct isp1760_qh *isp1760_qh_alloc(gfp_t flags) +static struct isp1760_qh *qh_alloc(gfp_t flags) { struct isp1760_qh *qh; qh = kmem_cache_zalloc(qh_cachep, flags); if (!qh) - return qh; + return NULL; + INIT_LIST_HEAD(&qh->qh_list); INIT_LIST_HEAD(&qh->qtd_list); + qh->slot = -1; + return qh; } -/* magic numbers that can affect system performance */ -#define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ -#define EHCI_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ -#define EHCI_TUNE_RL_TT 0 -#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ -#define EHCI_TUNE_MULT_TT 1 -#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */ +static void qh_free(struct isp1760_qh *qh) +{ + WARN_ON(!list_empty(&qh->qtd_list)); + WARN_ON(qh->slot > -1); + kmem_cache_free(qh_cachep, qh); +} /* one-time init, only for memory state */ static int priv_init(struct usb_hcd *hcd) @@ -411,6 +405,10 @@ static int priv_init(struct usb_hcd *hcd) spin_lock_init(&priv->lock); + INIT_LIST_HEAD(&priv->interruptqhs); + INIT_LIST_HEAD(&priv->controlqhs); + INIT_LIST_HEAD(&priv->bulkqhs); + /* * hw default: 1K periodic list heads, one per frame. * periodic_size can shrink by USBCMD update if hcc_params allows. @@ -468,7 +466,10 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) } /* pre reset */ - isp1760_init_regs(hcd); + reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, 0); + reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE); + reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE); + reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE); /* reset */ reg_write32(hcd->regs, HC_RESET_REG, SW_RESET_RESET_ALL); @@ -488,12 +489,15 @@ static int isp1760_hc_setup(struct usb_hcd *hcd) 16 : 32, (priv->devflags & ISP1760_FLAG_ANALOG_OC) ? "analog" : "digital"); + /* This is weird: at the first plug-in of a device there seems to be + one packet queued that never gets returned? */ + priv->active_ptds = -1; + /* ATL reset */ reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode | ALL_ATX_RESET); mdelay(10); reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode); - reg_write32(hcd->regs, HC_INTERRUPT_REG, INTERRUPT_ENABLE_MASK); reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, INTERRUPT_ENABLE_MASK); /* @@ -516,14 +520,21 @@ static void isp1760_init_maps(struct usb_hcd *hcd) reg_write32(hcd->regs, HC_ATL_PTD_LASTPTD_REG, 0x80000000); reg_write32(hcd->regs, HC_INT_PTD_LASTPTD_REG, 0x80000000); reg_write32(hcd->regs, HC_ISO_PTD_LASTPTD_REG, 0x00000001); + + reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, 0xffffffff); + reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, 0xffffffff); + reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, 0xffffffff); + + reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, + ATL_BUF_FILL | INT_BUF_FILL); } static void isp1760_enable_interrupts(struct usb_hcd *hcd) { reg_write32(hcd->regs, HC_ATL_IRQ_MASK_AND_REG, 0); - reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, 0); + reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, 0xffffffff); reg_write32(hcd->regs, HC_INT_IRQ_MASK_AND_REG, 0); - reg_write32(hcd->regs, HC_INT_IRQ_MASK_OR_REG, 0); + reg_write32(hcd->regs, HC_INT_IRQ_MASK_OR_REG, 0xffffffff); reg_write32(hcd->regs, HC_ISO_IRQ_MASK_AND_REG, 0); reg_write32(hcd->regs, HC_ISO_IRQ_MASK_OR_REG, 0xffffffff); /* step 23 passed */ @@ -548,8 +559,7 @@ static int isp1760_run(struct usb_hcd *hcd) command |= CMD_RUN; reg_write32(hcd->regs, HC_USBCMD, command); - retval = handshake(hcd, HC_USBCMD, CMD_RUN, CMD_RUN, - 250 * 1000); + retval = handshake(hcd, HC_USBCMD, CMD_RUN, CMD_RUN, 250 * 1000); if (retval) return retval; @@ -598,12 +608,19 @@ static int last_qtd_of_urb(struct isp1760_qtd *qtd, struct isp1760_qh *qh) return (qtd->urb != urb); } -static void transform_into_atl(struct isp1760_qh *qh, +/* magic numbers that can affect system performance */ +#define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ +#define EHCI_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ +#define EHCI_TUNE_RL_TT 0 +#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ +#define EHCI_TUNE_MULT_TT 1 +#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */ + +static void create_ptd_atl(struct isp1760_qh *qh, struct isp1760_qtd *qtd, struct ptd *ptd) { u32 maxpacket; u32 multi; - u32 pid_code; u32 rl = RL_COUNTER; u32 nak = NAK_COUNTER; @@ -616,67 +633,62 @@ static void transform_into_atl(struct isp1760_qh *qh, maxpacket &= 0x7ff; /* DW0 */ - ptd->dw0 = PTD_VALID; - ptd->dw0 |= PTD_LENGTH(qtd->length); - ptd->dw0 |= PTD_MAXPACKET(maxpacket); - ptd->dw0 |= PTD_ENDPOINT(usb_pipeendpoint(qtd->urb->pipe)); + ptd->dw0 = DW0_VALID_BIT; + ptd->dw0 |= TO_DW0_LENGTH(qtd->length); + ptd->dw0 |= TO_DW0_MAXPACKET(maxpacket); + ptd->dw0 |= TO_DW0_ENDPOINT(usb_pipeendpoint(qtd->urb->pipe)); /* DW1 */ ptd->dw1 = usb_pipeendpoint(qtd->urb->pipe) >> 1; - ptd->dw1 |= PTD_DEVICE_ADDR(usb_pipedevice(qtd->urb->pipe)); - - pid_code = qtd->packet_type; - ptd->dw1 |= PTD_PID_TOKEN(pid_code); + ptd->dw1 |= TO_DW1_DEVICE_ADDR(usb_pipedevice(qtd->urb->pipe)); + ptd->dw1 |= TO_DW1_PID_TOKEN(qtd->packet_type); if (usb_pipebulk(qtd->urb->pipe)) - ptd->dw1 |= PTD_TRANS_BULK; + ptd->dw1 |= DW1_TRANS_BULK; else if (usb_pipeint(qtd->urb->pipe)) - ptd->dw1 |= PTD_TRANS_INT; + ptd->dw1 |= DW1_TRANS_INT; if (qtd->urb->dev->speed != USB_SPEED_HIGH) { /* split transaction */ - ptd->dw1 |= PTD_TRANS_SPLIT; + ptd->dw1 |= DW1_TRANS_SPLIT; if (qtd->urb->dev->speed == USB_SPEED_LOW) - ptd->dw1 |= PTD_SE_USB_LOSPEED; + ptd->dw1 |= DW1_SE_USB_LOSPEED; - ptd->dw1 |= PTD_PORT_NUM(qtd->urb->dev->ttport); - ptd->dw1 |= PTD_HUB_NUM(qtd->urb->dev->tt->hub->devnum); + ptd->dw1 |= TO_DW1_PORT_NUM(qtd->urb->dev->ttport); + ptd->dw1 |= TO_DW1_HUB_NUM(qtd->urb->dev->tt->hub->devnum); /* SE bit for Split INT transfers */ if (usb_pipeint(qtd->urb->pipe) && (qtd->urb->dev->speed == USB_SPEED_LOW)) ptd->dw1 |= 2 << 16; - ptd->dw3 = 0; rl = 0; nak = 0; } else { - ptd->dw0 |= PTD_MULTI(multi); + ptd->dw0 |= TO_DW0_MULTI(multi); if (usb_pipecontrol(qtd->urb->pipe) || usb_pipebulk(qtd->urb->pipe)) - ptd->dw3 = qh->ping; - else - ptd->dw3 = 0; + ptd->dw3 |= TO_DW3_PING(qh->ping); } /* DW2 */ ptd->dw2 = 0; - ptd->dw2 |= PTD_DATA_START_ADDR(base_to_chip(qtd->payload_addr)); - ptd->dw2 |= PTD_RL_CNT(rl); - ptd->dw3 |= PTD_NAC_CNT(nak); + ptd->dw2 |= TO_DW2_DATA_START_ADDR(base_to_chip(qtd->payload_addr)); + ptd->dw2 |= TO_DW2_RL(rl); /* DW3 */ - ptd->dw3 |= qh->toggle; + ptd->dw3 |= TO_DW3_NAKCOUNT(nak); + ptd->dw3 |= TO_DW3_DATA_TOGGLE(qh->toggle); if (usb_pipecontrol(qtd->urb->pipe)) { if (qtd->data_buffer == qtd->urb->setup_packet) - ptd->dw3 &= ~PTD_DATA_TOGGLE(1); + ptd->dw3 &= ~TO_DW3_DATA_TOGGLE(1); else if (last_qtd_of_urb(qtd, qh)) - ptd->dw3 |= PTD_DATA_TOGGLE(1); + ptd->dw3 |= TO_DW3_DATA_TOGGLE(1); } - ptd->dw3 |= PTD_ACTIVE; + ptd->dw3 |= DW3_ACTIVE_BIT; /* Cerr */ - ptd->dw3 |= PTD_CERR(ERR_COUNTER); + ptd->dw3 |= TO_DW3_CERR(ERR_COUNTER); } static void transform_add_int(struct isp1760_qh *qh, @@ -731,197 +743,13 @@ static void transform_add_int(struct isp1760_qh *qh, ptd->dw4 = usof; } -static void transform_into_int(struct isp1760_qh *qh, +static void create_ptd_int(struct isp1760_qh *qh, struct isp1760_qtd *qtd, struct ptd *ptd) { - transform_into_atl(qh, qtd, ptd); + create_ptd_atl(qh, qtd, ptd); transform_add_int(qh, qtd, ptd); } -static int qtd_fill(struct isp1760_qtd *qtd, void *databuffer, size_t len, - u32 token) -{ - int count; - - qtd->data_buffer = databuffer; - qtd->packet_type = GET_QTD_TOKEN_TYPE(token); - - if (len > MAX_PAYLOAD_SIZE) - count = MAX_PAYLOAD_SIZE; - else - count = len; - - qtd->length = count; - return count; -} - -static int check_error(struct usb_hcd *hcd, struct ptd *ptd) -{ - int error = 0; - - if (ptd->dw3 & DW3_HALT_BIT) { - error = -EPIPE; - - if (ptd->dw3 & DW3_ERROR_BIT) - pr_err("error bit is set in DW3\n"); - } - - if (ptd->dw3 & DW3_QTD_ACTIVE) { - dev_err(hcd->self.controller, "Transfer active bit is set DW3\n" - "nak counter: %d, rl: %d\n", - (ptd->dw3 >> 19) & 0xf, (ptd->dw2 >> 25) & 0xf); - } - - return error; -} - -static void check_int_err_status(struct usb_hcd *hcd, u32 dw4) -{ - u32 i; - - dw4 >>= 8; - - for (i = 0; i < 8; i++) { - switch (dw4 & 0x7) { - case INT_UNDERRUN: - dev_err(hcd->self.controller, "Underrun (%d)\n", i); - break; - - case INT_EXACT: - dev_err(hcd->self.controller, - "Transaction error (%d)\n", i); - break; - - case INT_BABBLE: - dev_err(hcd->self.controller, "Babble error (%d)\n", i); - break; - } - dw4 >>= 3; - } -} - -static void enqueue_one_qtd(struct usb_hcd *hcd, struct isp1760_qtd *qtd) -{ - if (qtd->length && (qtd->length <= MAX_PAYLOAD_SIZE)) { - switch (qtd->packet_type) { - case IN_PID: - break; - case OUT_PID: - case SETUP_PID: - mem_writes8(hcd->regs, qtd->payload_addr, - qtd->data_buffer, qtd->length); - } - } -} - -static void enqueue_one_atl_qtd(struct usb_hcd *hcd, struct isp1760_qh *qh, - u32 slot, struct isp1760_qtd *qtd) -{ - struct isp1760_hcd *priv = hcd_to_priv(hcd); - struct ptd ptd; - - alloc_mem(hcd, qtd); - transform_into_atl(qh, qtd, &ptd); - ptd_write(hcd->regs, ATL_PTD_OFFSET, slot, &ptd); - enqueue_one_qtd(hcd, qtd); - - priv->atl_ints[slot].qh = qh; - priv->atl_ints[slot].qtd = qtd; - qtd->status |= URB_ENQUEUED; - qtd->status |= slot << 16; -} - -static void enqueue_one_int_qtd(struct usb_hcd *hcd, struct isp1760_qh *qh, - u32 slot, struct isp1760_qtd *qtd) -{ - struct isp1760_hcd *priv = hcd_to_priv(hcd); - struct ptd ptd; - - alloc_mem(hcd, qtd); - transform_into_int(qh, qtd, &ptd); - ptd_write(hcd->regs, INT_PTD_OFFSET, slot, &ptd); - enqueue_one_qtd(hcd, qtd); - - priv->int_ints[slot].qh = qh; - priv->int_ints[slot].qtd = qtd; - qtd->status |= URB_ENQUEUED; - qtd->status |= slot << 16; -} - -static void enqueue_an_ATL_packet(struct usb_hcd *hcd, struct isp1760_qh *qh, - struct isp1760_qtd *qtd) -{ - struct isp1760_hcd *priv = hcd_to_priv(hcd); - u32 skip_map, or_map; - u32 slot; - u32 buffstatus; - - /* - * When this function is called from the interrupt handler to enqueue - * a follow-up packet, the SKIP register gets written and read back - * almost immediately. With ISP1761, this register requires a delay of - * 195ns between a write and subsequent read (see section 15.1.1.3). - */ - mmiowb(); - ndelay(195); - skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG); - - BUG_ON(!skip_map); - slot = __ffs(skip_map); - - enqueue_one_atl_qtd(hcd, qh, slot, qtd); - - or_map = reg_read32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG); - or_map |= (1 << slot); - reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, or_map); - - skip_map &= ~(1 << slot); - reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, skip_map); - - priv->atl_queued++; - if (priv->atl_queued == 2) - reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, - INTERRUPT_ENABLE_SOT_MASK); - - buffstatus = reg_read32(hcd->regs, HC_BUFFER_STATUS_REG); - buffstatus |= ATL_BUFFER; - reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, buffstatus); -} - -static void enqueue_an_INT_packet(struct usb_hcd *hcd, struct isp1760_qh *qh, - struct isp1760_qtd *qtd) -{ - u32 skip_map, or_map; - u32 slot; - u32 buffstatus; - - /* - * When this function is called from the interrupt handler to enqueue - * a follow-up packet, the SKIP register gets written and read back - * almost immediately. With ISP1761, this register requires a delay of - * 195ns between a write and subsequent read (see section 15.1.1.3). - */ - mmiowb(); - ndelay(195); - skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG); - - BUG_ON(!skip_map); - slot = __ffs(skip_map); - - enqueue_one_int_qtd(hcd, qh, slot, qtd); - - or_map = reg_read32(hcd->regs, HC_INT_IRQ_MASK_OR_REG); - or_map |= (1 << slot); - reg_write32(hcd->regs, HC_INT_IRQ_MASK_OR_REG, or_map); - - skip_map &= ~(1 << slot); - reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, skip_map); - - buffstatus = reg_read32(hcd->regs, HC_BUFFER_STATUS_REG); - buffstatus |= INT_BUFFER; - reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, buffstatus); -} - static void isp1760_urb_done(struct usb_hcd *hcd, struct urb *urb) __releases(priv->lock) __acquires(priv->lock) @@ -948,557 +776,654 @@ __acquires(priv->lock) spin_lock(&priv->lock); } -static void isp1760_qtd_free(struct isp1760_qtd *qtd) +static struct isp1760_qtd *qtd_alloc(gfp_t flags, struct urb *urb, + u8 packet_type) { - BUG_ON(qtd->payload_addr); - kmem_cache_free(qtd_cachep, qtd); + struct isp1760_qtd *qtd; + + qtd = kmem_cache_zalloc(qtd_cachep, flags); + if (!qtd) + return NULL; + + INIT_LIST_HEAD(&qtd->qtd_list); + qtd->urb = urb; + qtd->packet_type = packet_type; + qtd->status = QTD_ENQUEUED; + qtd->actual_length = 0; + + return qtd; } -static struct isp1760_qtd *clean_this_qtd(struct isp1760_qtd *qtd, - struct isp1760_qh *qh) +static void qtd_free(struct isp1760_qtd *qtd) { - struct isp1760_qtd *tmp_qtd; - - if (list_is_last(&qtd->qtd_list, &qh->qtd_list)) - tmp_qtd = NULL; - else - tmp_qtd = list_entry(qtd->qtd_list.next, struct isp1760_qtd, - qtd_list); - list_del(&qtd->qtd_list); - isp1760_qtd_free(qtd); - return tmp_qtd; + WARN_ON(qtd->payload_addr); + kmem_cache_free(qtd_cachep, qtd); } -/* - * Remove this QTD from the QH list and free its memory. If this QTD - * isn't the last one than remove also his successor(s). - * Returns the QTD which is part of an new URB and should be enqueued. - */ -static struct isp1760_qtd *clean_up_qtdlist(struct isp1760_qtd *qtd, - struct isp1760_qh *qh) +static void start_bus_transfer(struct usb_hcd *hcd, u32 ptd_offset, int slot, + struct slotinfo *slots, struct isp1760_qtd *qtd, + struct isp1760_qh *qh, struct ptd *ptd) { - struct urb *urb; + struct isp1760_hcd *priv = hcd_to_priv(hcd); + int skip_map; + + WARN_ON((slot < 0) || (slot > 31)); + WARN_ON(qtd->length && !qtd->payload_addr); + WARN_ON(slots[slot].qtd); + WARN_ON(slots[slot].qh); + WARN_ON(qtd->status != QTD_PAYLOAD_ALLOC); + + slots[slot].qtd = qtd; + slots[slot].qh = qh; + qh->slot = slot; + qtd->status = QTD_XFER_STARTED; /* Set this before writing ptd, since + interrupt routine may preempt and expects this value. */ + ptd_write(hcd->regs, ptd_offset, slot, ptd); + priv->active_ptds++; + + /* Make sure done map has not triggered from some unlinked transfer */ + if (ptd_offset == ATL_PTD_OFFSET) { + priv->atl_done_map |= reg_read32(hcd->regs, + HC_ATL_PTD_DONEMAP_REG); + priv->atl_done_map &= ~(1 << qh->slot); - urb = qtd->urb; - do { - qtd = clean_this_qtd(qtd, qh); - } while (qtd && (qtd->urb == urb)); + skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG); + skip_map &= ~(1 << qh->slot); + reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, skip_map); + } else { + priv->int_done_map |= reg_read32(hcd->regs, + HC_INT_PTD_DONEMAP_REG); + priv->int_done_map &= ~(1 << qh->slot); - return qtd; + skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG); + skip_map &= ~(1 << qh->slot); + reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, skip_map); + } } -static void do_atl_int(struct usb_hcd *hcd) +static int is_short_bulk(struct isp1760_qtd *qtd) { - struct isp1760_hcd *priv = hcd_to_priv(hcd); - u32 done_map, skip_map; - struct ptd ptd; - struct urb *urb; - u32 slot; - u32 length; - u32 or_map; - u32 status = -EINVAL; - int error; - struct isp1760_qtd *qtd; - struct isp1760_qh *qh; - u32 rl; - u32 nakcount; + return (usb_pipebulk(qtd->urb->pipe) && + (qtd->actual_length < qtd->length)); +} - done_map = reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG); - skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG); +static void collect_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh, + struct list_head *urb_list) +{ + int last_qtd; + struct isp1760_qtd *qtd, *qtd_next; + struct urb_listitem *urb_listitem; - or_map = reg_read32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG); - or_map &= ~done_map; - reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, or_map); + list_for_each_entry_safe(qtd, qtd_next, &qh->qtd_list, qtd_list) { + if (qtd->status < QTD_XFER_COMPLETE) + break; - while (done_map) { - status = 0; - priv->atl_queued--; + if (list_is_last(&qtd->qtd_list, &qh->qtd_list)) + last_qtd = 1; + else + last_qtd = qtd->urb != qtd_next->urb; + + if ((!last_qtd) && (qtd->status == QTD_RETIRE)) + qtd_next->status = QTD_RETIRE; + + if (qtd->status == QTD_XFER_COMPLETE) { + if (qtd->actual_length) { + switch (qtd->packet_type) { + case IN_PID: + mem_reads8(hcd->regs, qtd->payload_addr, + qtd->data_buffer, + qtd->actual_length); + /* Fall through (?) */ + case OUT_PID: + qtd->urb->actual_length += + qtd->actual_length; + /* Fall through ... */ + case SETUP_PID: + break; + } + } - slot = __ffs(done_map); - done_map &= ~(1 << slot); - skip_map |= (1 << slot); + if (is_short_bulk(qtd)) { + if (qtd->urb->transfer_flags & URB_SHORT_NOT_OK) + qtd->urb->status = -EREMOTEIO; + if (!last_qtd) + qtd_next->status = QTD_RETIRE; + } + } - qtd = priv->atl_ints[slot].qtd; - qh = priv->atl_ints[slot].qh; + if (qtd->payload_addr) + free_mem(hcd, qtd); - if (!qh) { - dev_err(hcd->self.controller, "qh is 0\n"); - continue; + if (last_qtd) { + if ((qtd->status == QTD_RETIRE) && + (qtd->urb->status == -EINPROGRESS)) + qtd->urb->status = -EPIPE; + /* Defer calling of urb_done() since it releases lock */ + urb_listitem = kmem_cache_zalloc(urb_listitem_cachep, + GFP_ATOMIC); + if (unlikely(!urb_listitem)) + break; + urb_listitem->urb = qtd->urb; + list_add_tail(&urb_listitem->urb_list, urb_list); } - ptd_read(hcd->regs, ATL_PTD_OFFSET, slot, &ptd); - rl = (ptd.dw2 >> 25) & 0x0f; - nakcount = (ptd.dw3 >> 19) & 0xf; - - /* Transfer Error, *but* active and no HALT -> reload */ - if ((ptd.dw3 & DW3_ERROR_BIT) && (ptd.dw3 & DW3_QTD_ACTIVE) && - !(ptd.dw3 & DW3_HALT_BIT)) { - - /* according to ppriv code, we have to - * reload this one if trasfered bytes != requested bytes - * else act like everything went smooth.. - * XXX This just doesn't feel right and hasn't - * triggered so far. - */ + list_del(&qtd->qtd_list); + qtd_free(qtd); + } +} - length = PTD_XFERRED_LENGTH(ptd.dw3); - dev_err(hcd->self.controller, - "Should reload now... transferred %d " - "of %zu\n", length, qtd->length); - BUG(); - } +#define ENQUEUE_DEPTH 2 +static void enqueue_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh) +{ + struct isp1760_hcd *priv = hcd_to_priv(hcd); + int ptd_offset; + struct slotinfo *slots; + int curr_slot, free_slot; + int n; + struct ptd ptd; + struct isp1760_qtd *qtd; - if (!nakcount && (ptd.dw3 & DW3_QTD_ACTIVE)) { - u32 buffstatus; + if (unlikely(list_empty(&qh->qtd_list))) { + WARN_ON(1); + return; + } - /* - * NAKs are handled in HW by the chip. Usually if the - * device is not able to send data fast enough. - * This happens mostly on slower hardware. - */ + if (usb_pipeint(list_entry(qh->qtd_list.next, struct isp1760_qtd, + qtd_list)->urb->pipe)) { + ptd_offset = INT_PTD_OFFSET; + slots = priv->int_slots; + } else { + ptd_offset = ATL_PTD_OFFSET; + slots = priv->atl_slots; + } - /* RL counter = ERR counter */ - ptd.dw3 &= ~(0xf << 19); - ptd.dw3 |= rl << 19; - ptd.dw3 &= ~(3 << (55 - 32)); - ptd.dw3 |= ERR_COUNTER << (55 - 32); - - /* - * It is not needed to write skip map back because it - * is unchanged. Just make sure that this entry is - * unskipped once it gets written to the HW. - */ - skip_map &= ~(1 << slot); - or_map = reg_read32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG); - or_map |= 1 << slot; - reg_write32(hcd->regs, HC_ATL_IRQ_MASK_OR_REG, or_map); + free_slot = -1; + for (curr_slot = 0; curr_slot < 32; curr_slot++) { + if ((free_slot == -1) && (slots[curr_slot].qtd == NULL)) + free_slot = curr_slot; + if (slots[curr_slot].qh == qh) + break; + } - ptd.dw0 |= PTD_VALID; - ptd_write(hcd->regs, ATL_PTD_OFFSET, slot, &ptd); + n = 0; + list_for_each_entry(qtd, &qh->qtd_list, qtd_list) { + if (qtd->status == QTD_ENQUEUED) { + WARN_ON(qtd->payload_addr); + alloc_mem(hcd, qtd); + if ((qtd->length) && (!qtd->payload_addr)) + break; - priv->atl_queued++; - if (priv->atl_queued == 2) - reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, - INTERRUPT_ENABLE_SOT_MASK); + if ((qtd->length) && + ((qtd->packet_type == SETUP_PID) || + (qtd->packet_type == OUT_PID))) { + mem_writes8(hcd->regs, qtd->payload_addr, + qtd->data_buffer, qtd->length); + } - buffstatus = reg_read32(hcd->regs, - HC_BUFFER_STATUS_REG); - buffstatus |= ATL_BUFFER; - reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, - buffstatus); - continue; + qtd->status = QTD_PAYLOAD_ALLOC; } - error = check_error(hcd, &ptd); - if (error) { - status = error; - priv->atl_ints[slot].qh->toggle = 0; - priv->atl_ints[slot].qh->ping = 0; - qtd->urb->status = -EPIPE; - -#if 0 - printk(KERN_ERR "Error in %s().\n", __func__); - printk(KERN_ERR "IN dw0: %08x dw1: %08x dw2: %08x " - "dw3: %08x dw4: %08x dw5: %08x dw6: " - "%08x dw7: %08x\n", - ptd.dw0, ptd.dw1, ptd.dw2, ptd.dw3, - ptd.dw4, ptd.dw5, ptd.dw6, ptd.dw7); -#endif - } else { - priv->atl_ints[slot].qh->toggle = ptd.dw3 & (1 << 25); - priv->atl_ints[slot].qh->ping = ptd.dw3 & (1 << 26); - } + if (qtd->status == QTD_PAYLOAD_ALLOC) { +/* + if ((curr_slot > 31) && (free_slot == -1)) + dev_dbg(hcd->self.controller, "%s: No slot " + "available for transfer\n", __func__); +*/ + /* Start xfer for this endpoint if not already done */ + if ((curr_slot > 31) && (free_slot > -1)) { + if (usb_pipeint(qtd->urb->pipe)) + create_ptd_int(qh, qtd, &ptd); + else + create_ptd_atl(qh, qtd, &ptd); + + start_bus_transfer(hcd, ptd_offset, free_slot, + slots, qtd, qh, &ptd); + curr_slot = free_slot; + } - length = PTD_XFERRED_LENGTH(ptd.dw3); - if (length) { - switch (DW1_GET_PID(ptd.dw1)) { - case IN_PID: - mem_reads8(hcd->regs, qtd->payload_addr, - qtd->data_buffer, length); + n++; + if (n >= ENQUEUE_DEPTH) + break; + } + } +} - case OUT_PID: +void schedule_ptds(struct usb_hcd *hcd) +{ + struct isp1760_hcd *priv; + struct isp1760_qh *qh, *qh_next; + struct list_head *ep_queue; + struct usb_host_endpoint *ep; + LIST_HEAD(urb_list); + struct urb_listitem *urb_listitem, *urb_listitem_next; + + if (!hcd) { + WARN_ON(1); + return; + } - qtd->urb->actual_length += length; + priv = hcd_to_priv(hcd); - case SETUP_PID: - break; + /* + * check finished/retired xfers, transfer payloads, call urb_done() + */ + ep_queue = &priv->interruptqhs; + while (ep_queue) { + list_for_each_entry_safe(qh, qh_next, ep_queue, qh_list) { + ep = list_entry(qh->qtd_list.next, struct isp1760_qtd, + qtd_list)->urb->ep; + collect_qtds(hcd, qh, &urb_list); + if (list_empty(&qh->qtd_list)) { + list_del(&qh->qh_list); + if (ep->hcpriv == NULL) { + /* Endpoint has been disabled, so we + can free the associated queue head. */ + qh_free(qh); + } } } - priv->atl_ints[slot].qtd = NULL; - priv->atl_ints[slot].qh = NULL; - - free_mem(hcd, qtd); + if (ep_queue == &priv->interruptqhs) + ep_queue = &priv->controlqhs; + else if (ep_queue == &priv->controlqhs) + ep_queue = &priv->bulkqhs; + else + ep_queue = NULL; + } - reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, skip_map); + list_for_each_entry_safe(urb_listitem, urb_listitem_next, &urb_list, + urb_list) { + isp1760_urb_done(hcd, urb_listitem->urb); + kmem_cache_free(urb_listitem_cachep, urb_listitem); + } - if (qtd->urb->status == -EPIPE) { - /* HALT was received */ + /* + * Schedule packets for transfer. + * + * According to USB2.0 specification: + * + * 1st prio: interrupt xfers, up to 80 % of bandwidth + * 2nd prio: control xfers + * 3rd prio: bulk xfers + * + * ... but let's use a simpler scheme here (mostly because ISP1761 doc + * is very unclear on how to prioritize traffic): + * + * 1) Enqueue any queued control transfers, as long as payload chip mem + * and PTD ATL slots are available. + * 2) Enqueue any queued INT transfers, as long as payload chip mem + * and PTD INT slots are available. + * 3) Enqueue any queued bulk transfers, as long as payload chip mem + * and PTD ATL slots are available. + * + * Use double buffering (ENQUEUE_DEPTH==2) as a compromise between + * conservation of chip mem and performance. + * + * I'm sure this scheme could be improved upon! + */ + ep_queue = &priv->controlqhs; + while (ep_queue) { + list_for_each_entry_safe(qh, qh_next, ep_queue, qh_list) + enqueue_qtds(hcd, qh); + + if (ep_queue == &priv->controlqhs) + ep_queue = &priv->interruptqhs; + else if (ep_queue == &priv->interruptqhs) + ep_queue = &priv->bulkqhs; + else + ep_queue = NULL; + } +} - urb = qtd->urb; - qtd = clean_up_qtdlist(qtd, qh); - isp1760_urb_done(hcd, urb); +#define PTD_STATE_QTD_DONE 1 +#define PTD_STATE_QTD_RELOAD 2 +#define PTD_STATE_URB_RETIRE 3 - } else if (usb_pipebulk(qtd->urb->pipe) && - (length < qtd->length)) { - /* short BULK received */ +static int check_int_transfer(struct usb_hcd *hcd, struct ptd *ptd, + struct urb *urb) +{ + __dw dw4; + int i; - if (qtd->urb->transfer_flags & URB_SHORT_NOT_OK) { - qtd->urb->status = -EREMOTEIO; - dev_dbg(hcd->self.controller, - "short bulk, %d instead %zu " - "with URB_SHORT_NOT_OK flag.\n", - length, qtd->length); - } + dw4 = ptd->dw4; + dw4 >>= 8; - if (qtd->urb->status == -EINPROGRESS) - qtd->urb->status = 0; + /* FIXME: ISP1761 datasheet does not say what to do with these. Do we + need to handle these errors? Is it done in hardware? */ - urb = qtd->urb; - qtd = clean_up_qtdlist(qtd, qh); - isp1760_urb_done(hcd, urb); + if (ptd->dw3 & DW3_HALT_BIT) { - } else if (last_qtd_of_urb(qtd, qh)) { - /* that was the last qtd of that URB */ + urb->status = -EPROTO; /* Default unknown error */ - if (qtd->urb->status == -EINPROGRESS) - qtd->urb->status = 0; + for (i = 0; i < 8; i++) { + switch (dw4 & 0x7) { + case INT_UNDERRUN: + dev_dbg(hcd->self.controller, "%s: underrun " + "during uFrame %d\n", + __func__, i); + urb->status = -ECOMM; /* Could not write data */ + break; + case INT_EXACT: + dev_dbg(hcd->self.controller, "%s: transaction " + "error during uFrame %d\n", + __func__, i); + urb->status = -EPROTO; /* timeout, bad CRC, PID + error etc. */ + break; + case INT_BABBLE: + dev_dbg(hcd->self.controller, "%s: babble " + "error during uFrame %d\n", + __func__, i); + urb->status = -EOVERFLOW; + break; + } + dw4 >>= 3; + } - urb = qtd->urb; - qtd = clean_up_qtdlist(qtd, qh); - isp1760_urb_done(hcd, urb); + return PTD_STATE_URB_RETIRE; + } - } else { - /* next QTD of this URB */ + return PTD_STATE_QTD_DONE; +} - qtd = clean_this_qtd(qtd, qh); - BUG_ON(!qtd); - } +static int check_atl_transfer(struct usb_hcd *hcd, struct ptd *ptd, + struct urb *urb) +{ + WARN_ON(!ptd); + if (ptd->dw3 & DW3_HALT_BIT) { + if (ptd->dw3 & DW3_BABBLE_BIT) + urb->status = -EOVERFLOW; + else if (FROM_DW3_CERR(ptd->dw3)) + urb->status = -EPIPE; /* Stall */ + else if (ptd->dw3 & DW3_ERROR_BIT) + urb->status = -EPROTO; /* XactErr */ + else + urb->status = -EPROTO; /* Unknown */ +/* + dev_dbg(hcd->self.controller, "%s: ptd error:\n" + " dw0: %08x dw1: %08x dw2: %08x dw3: %08x\n" + " dw4: %08x dw5: %08x dw6: %08x dw7: %08x\n", + __func__, + ptd->dw0, ptd->dw1, ptd->dw2, ptd->dw3, + ptd->dw4, ptd->dw5, ptd->dw6, ptd->dw7); +*/ + return PTD_STATE_URB_RETIRE; + } - if (qtd) - enqueue_an_ATL_packet(hcd, qh, qtd); + if ((ptd->dw3 & DW3_ERROR_BIT) && (ptd->dw3 & DW3_ACTIVE_BIT)) { + /* Transfer Error, *but* active and no HALT -> reload */ + dev_dbg(hcd->self.controller, "PID error; reloading ptd\n"); + return PTD_STATE_QTD_RELOAD; + } - skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG); + if (!FROM_DW3_NAKCOUNT(ptd->dw3) && (ptd->dw3 & DW3_ACTIVE_BIT)) { + /* + * NAKs are handled in HW by the chip. Usually if the + * device is not able to send data fast enough. + * This happens mostly on slower hardware. + */ + return PTD_STATE_QTD_RELOAD; } - if (priv->atl_queued <= 1) - reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, - INTERRUPT_ENABLE_MASK); + + return PTD_STATE_QTD_DONE; } -static void do_intl_int(struct usb_hcd *hcd) +static irqreturn_t isp1760_irq(struct usb_hcd *hcd) { struct isp1760_hcd *priv = hcd_to_priv(hcd); - u32 done_map, skip_map; + u32 imask; + irqreturn_t irqret = IRQ_NONE; struct ptd ptd; - struct urb *urb; - u32 length; - u32 or_map; - int error; - u32 slot; - struct isp1760_qtd *qtd; struct isp1760_qh *qh; + int slot; + int state; + struct slotinfo *slots; + u32 ptd_offset; + struct isp1760_qtd *qtd; + int modified; + static int last_active_ptds; + int int_skip_map, atl_skip_map; - done_map = reg_read32(hcd->regs, HC_INT_PTD_DONEMAP_REG); - skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG); - - or_map = reg_read32(hcd->regs, HC_INT_IRQ_MASK_OR_REG); - or_map &= ~done_map; - reg_write32(hcd->regs, HC_INT_IRQ_MASK_OR_REG, or_map); - - while (done_map) { - slot = __ffs(done_map); - done_map &= ~(1 << slot); - skip_map |= (1 << slot); - - qtd = priv->int_ints[slot].qtd; - qh = priv->int_ints[slot].qh; - - if (!qh) { - dev_err(hcd->self.controller, "(INT) qh is 0\n"); - continue; - } + spin_lock(&priv->lock); - ptd_read(hcd->regs, INT_PTD_OFFSET, slot, &ptd); - check_int_err_status(hcd, ptd.dw4); - - error = check_error(hcd, &ptd); - if (error) { -#if 0 - printk(KERN_ERR "Error in %s().\n", __func__); - printk(KERN_ERR "IN dw0: %08x dw1: %08x dw2: %08x " - "dw3: %08x dw4: %08x dw5: %08x dw6: " - "%08x dw7: %08x\n", - ptd.dw0, ptd.dw1, ptd.dw2, ptd.dw3, - ptd.dw4, ptd.dw5, ptd.dw6, ptd.dw7); -#endif - qtd->urb->status = -EPIPE; - priv->int_ints[slot].qh->toggle = 0; - priv->int_ints[slot].qh->ping = 0; + if (!(hcd->state & HC_STATE_RUNNING)) + goto leave; + imask = reg_read32(hcd->regs, HC_INTERRUPT_REG); + if (unlikely(!imask)) + goto leave; + reg_write32(hcd->regs, HC_INTERRUPT_REG, imask); /* Clear */ + + int_skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG); + atl_skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG); + priv->int_done_map |= reg_read32(hcd->regs, HC_INT_PTD_DONEMAP_REG); + priv->atl_done_map |= reg_read32(hcd->regs, HC_ATL_PTD_DONEMAP_REG); + priv->int_done_map &= ~int_skip_map; + priv->atl_done_map &= ~atl_skip_map; + + modified = priv->int_done_map | priv->atl_done_map; + + while (priv->int_done_map || priv->atl_done_map) { + if (priv->int_done_map) { + /* INT ptd */ + slot = __ffs(priv->int_done_map); + priv->int_done_map &= ~(1 << slot); + slots = priv->int_slots; + /* This should not trigger, and could be removed if + noone have any problems with it triggering: */ + if (!slots[slot].qh) { + WARN_ON(1); + continue; + } + ptd_offset = INT_PTD_OFFSET; + ptd_read(hcd->regs, INT_PTD_OFFSET, slot, &ptd); + state = check_int_transfer(hcd, &ptd, + slots[slot].qtd->urb); } else { - priv->int_ints[slot].qh->toggle = ptd.dw3 & (1 << 25); - priv->int_ints[slot].qh->ping = ptd.dw3 & (1 << 26); - } - - if (qtd->urb->dev->speed != USB_SPEED_HIGH) - length = PTD_XFERRED_LENGTH_LO(ptd.dw3); - else - length = PTD_XFERRED_LENGTH(ptd.dw3); - - if (length) { - switch (DW1_GET_PID(ptd.dw1)) { - case IN_PID: - mem_reads8(hcd->regs, qtd->payload_addr, - qtd->data_buffer, length); - case OUT_PID: - - qtd->urb->actual_length += length; - - case SETUP_PID: - break; + /* ATL ptd */ + slot = __ffs(priv->atl_done_map); + priv->atl_done_map &= ~(1 << slot); + slots = priv->atl_slots; + /* This should not trigger, and could be removed if + noone have any problems with it triggering: */ + if (!slots[slot].qh) { + WARN_ON(1); + continue; } + ptd_offset = ATL_PTD_OFFSET; + ptd_read(hcd->regs, ATL_PTD_OFFSET, slot, &ptd); + state = check_atl_transfer(hcd, &ptd, + slots[slot].qtd->urb); } - priv->int_ints[slot].qtd = NULL; - priv->int_ints[slot].qh = NULL; - - reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, skip_map); - free_mem(hcd, qtd); - - if (qtd->urb->status == -EPIPE) { - /* HALT received */ - - urb = qtd->urb; - qtd = clean_up_qtdlist(qtd, qh); - isp1760_urb_done(hcd, urb); - - } else if (last_qtd_of_urb(qtd, qh)) { - - if (qtd->urb->status == -EINPROGRESS) - qtd->urb->status = 0; + qtd = slots[slot].qtd; + slots[slot].qtd = NULL; + qh = slots[slot].qh; + slots[slot].qh = NULL; + priv->active_ptds--; + qh->slot = -1; + + WARN_ON(qtd->status != QTD_XFER_STARTED); + + switch (state) { + case PTD_STATE_QTD_DONE: + if ((usb_pipeint(qtd->urb->pipe)) && + (qtd->urb->dev->speed != USB_SPEED_HIGH)) + qtd->actual_length = + FROM_DW3_SCS_NRBYTESTRANSFERRED(ptd.dw3); + else + qtd->actual_length = + FROM_DW3_NRBYTESTRANSFERRED(ptd.dw3); + + qtd->status = QTD_XFER_COMPLETE; + if (list_is_last(&qtd->qtd_list, &qh->qtd_list) || + is_short_bulk(qtd)) + qtd = NULL; + else + qtd = list_entry(qtd->qtd_list.next, + typeof(*qtd), qtd_list); + + qh->toggle = FROM_DW3_DATA_TOGGLE(ptd.dw3); + qh->ping = FROM_DW3_PING(ptd.dw3); + break; - urb = qtd->urb; - qtd = clean_up_qtdlist(qtd, qh); - isp1760_urb_done(hcd, urb); + case PTD_STATE_QTD_RELOAD: /* QTD_RETRY, for atls only */ + qtd->status = QTD_PAYLOAD_ALLOC; + ptd.dw0 |= DW0_VALID_BIT; + /* RL counter = ERR counter */ + ptd.dw3 &= ~TO_DW3_NAKCOUNT(0xf); + ptd.dw3 |= TO_DW3_NAKCOUNT(FROM_DW2_RL(ptd.dw2)); + ptd.dw3 &= ~TO_DW3_CERR(3); + ptd.dw3 |= TO_DW3_CERR(ERR_COUNTER); + qh->toggle = FROM_DW3_DATA_TOGGLE(ptd.dw3); + qh->ping = FROM_DW3_PING(ptd.dw3); + break; - } else { - /* next QTD of this URB */ + case PTD_STATE_URB_RETIRE: + qtd->status = QTD_RETIRE; + qtd = NULL; + qh->toggle = 0; + qh->ping = 0; + break; - qtd = clean_this_qtd(qtd, qh); - BUG_ON(!qtd); + default: + WARN_ON(1); + continue; } - if (qtd) - enqueue_an_INT_packet(hcd, qh, qtd); + if (qtd && (qtd->status == QTD_PAYLOAD_ALLOC)) { + if (slots == priv->int_slots) { + if (state == PTD_STATE_QTD_RELOAD) + dev_err(hcd->self.controller, + "%s: PTD_STATE_QTD_RELOAD on " + "interrupt packet\n", __func__); + if (state != PTD_STATE_QTD_RELOAD) + create_ptd_int(qh, qtd, &ptd); + } else { + if (state != PTD_STATE_QTD_RELOAD) + create_ptd_atl(qh, qtd, &ptd); + } - skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG); + start_bus_transfer(hcd, ptd_offset, slot, slots, qtd, + qh, &ptd); + } } -} -static struct isp1760_qh *qh_make(struct usb_hcd *hcd, struct urb *urb, - gfp_t flags) -{ - struct isp1760_qh *qh; - int is_input, type; + if (modified) + schedule_ptds(hcd); - qh = isp1760_qh_alloc(flags); - if (!qh) - return qh; - - /* - * init endpoint/device data for this QH - */ - is_input = usb_pipein(urb->pipe); - type = usb_pipetype(urb->pipe); + /* ISP1760 Errata 2 explains that interrupts may be missed (or not + happen?) if two USB devices are running simultaneously. Perhaps + this happens when a PTD is finished during interrupt handling; + enable SOF interrupts if PTDs are still scheduled when exiting this + interrupt handler, just to be safe. */ - if (!usb_pipecontrol(urb->pipe)) - usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), !is_input, - 1); - return qh; -} - -/* - * For control/bulk/interrupt, return QH with these TDs appended. - * Allocates and initializes the QH if necessary. - * Returns null if it can't allocate a QH it needs to. - * If the QH has TDs (urbs) already, that's great. - */ -static struct isp1760_qh *qh_append_tds(struct usb_hcd *hcd, - struct urb *urb, struct list_head *qtd_list, int epnum, - void **ptr) -{ - struct isp1760_qh *qh; - - qh = (struct isp1760_qh *)*ptr; - if (!qh) { - /* can't sleep here, we have priv->lock... */ - qh = qh_make(hcd, urb, GFP_ATOMIC); - if (!qh) - return qh; - *ptr = qh; + if (priv->active_ptds != last_active_ptds) { + if (priv->active_ptds > 0) + reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, + INTERRUPT_ENABLE_SOT_MASK); + else + reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, + INTERRUPT_ENABLE_MASK); + last_active_ptds = priv->active_ptds; } - list_splice(qtd_list, qh->qtd_list.prev); + irqret = IRQ_HANDLED; +leave: + spin_unlock(&priv->lock); - return qh; + return irqret; } -static void qtd_list_free(struct urb *urb, struct list_head *qtd_list) +static int qtd_fill(struct isp1760_qtd *qtd, void *databuffer, size_t len) { - struct list_head *entry, *temp; + qtd->data_buffer = databuffer; - list_for_each_safe(entry, temp, qtd_list) { - struct isp1760_qtd *qtd; + if (len > MAX_PAYLOAD_SIZE) + len = MAX_PAYLOAD_SIZE; + qtd->length = len; - qtd = list_entry(entry, struct isp1760_qtd, qtd_list); - list_del(&qtd->qtd_list); - isp1760_qtd_free(qtd); - } + return qtd->length; } -static int isp1760_prepare_enqueue(struct usb_hcd *hcd, struct urb *urb, - struct list_head *qtd_list, gfp_t mem_flags, packet_enqueue *p) +static void qtd_list_free(struct list_head *qtd_list) { - struct isp1760_hcd *priv = hcd_to_priv(hcd); - struct isp1760_qtd *qtd; - int epnum; - unsigned long flags; - struct isp1760_qh *qh = NULL; - int rc; - int qh_busy; - - qtd = list_entry(qtd_list->next, struct isp1760_qtd, qtd_list); - epnum = urb->ep->desc.bEndpointAddress; - - spin_lock_irqsave(&priv->lock, flags); - if (!HCD_HW_ACCESSIBLE(hcd)) { - rc = -ESHUTDOWN; - goto done; - } - rc = usb_hcd_link_urb_to_ep(hcd, urb); - if (rc) - goto done; - - qh = urb->ep->hcpriv; - if (qh) - qh_busy = !list_empty(&qh->qtd_list); - else - qh_busy = 0; + struct isp1760_qtd *qtd, *qtd_next; - qh = qh_append_tds(hcd, urb, qtd_list, epnum, &urb->ep->hcpriv); - if (!qh) { - usb_hcd_unlink_urb_from_ep(hcd, urb); - rc = -ENOMEM; - goto done; + list_for_each_entry_safe(qtd, qtd_next, qtd_list, qtd_list) { + list_del(&qtd->qtd_list); + qtd_free(qtd); } - - if (!qh_busy) - p(hcd, qh, qtd); - -done: - spin_unlock_irqrestore(&priv->lock, flags); - if (!qh) - qtd_list_free(urb, qtd_list); - return rc; -} - -static struct isp1760_qtd *isp1760_qtd_alloc(gfp_t flags) -{ - struct isp1760_qtd *qtd; - - qtd = kmem_cache_zalloc(qtd_cachep, flags); - if (qtd) - INIT_LIST_HEAD(&qtd->qtd_list); - - return qtd; } /* - * create a list of filled qtds for this URB; won't link into qh. + * Packetize urb->transfer_buffer into list of packets of size wMaxPacketSize. + * Also calculate the PID type (SETUP/IN/OUT) for each packet. */ #define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff) -static struct list_head *qh_urb_transaction(struct usb_hcd *hcd, +static void packetize_urb(struct usb_hcd *hcd, struct urb *urb, struct list_head *head, gfp_t flags) { struct isp1760_qtd *qtd; void *buf; - int len, maxpacket; - int is_input; - u32 token; + int len, maxpacketsize; + u8 packet_type; /* * URBs map to sequences of QTDs: one logical transaction */ - qtd = isp1760_qtd_alloc(flags); - if (!qtd) - return NULL; - list_add_tail(&qtd->qtd_list, head); - qtd->urb = urb; - urb->status = -EINPROGRESS; + if (!urb->transfer_buffer && urb->transfer_buffer_length) { + /* XXX This looks like usb storage / SCSI bug */ + dev_err(hcd->self.controller, + "buf is null, dma is %08lx len is %d\n", + (long unsigned)urb->transfer_dma, + urb->transfer_buffer_length); + WARN_ON(1); + } - token = 0; - /* for split transactions, SplitXState initialized to zero */ + if (usb_pipein(urb->pipe)) + packet_type = IN_PID; + else + packet_type = OUT_PID; - len = urb->transfer_buffer_length; - is_input = usb_pipein(urb->pipe); if (usb_pipecontrol(urb->pipe)) { - /* SETUP pid */ - qtd_fill(qtd, urb->setup_packet, - sizeof(struct usb_ctrlrequest), - token | SETUP_PID); - - /* ... and always at least one more pid */ - qtd = isp1760_qtd_alloc(flags); + qtd = qtd_alloc(flags, urb, SETUP_PID); if (!qtd) goto cleanup; - qtd->urb = urb; + qtd_fill(qtd, urb->setup_packet, sizeof(struct usb_ctrlrequest)); list_add_tail(&qtd->qtd_list, head); /* for zero length DATA stages, STATUS is always IN */ - if (len == 0) - token |= IN_PID; + if (urb->transfer_buffer_length == 0) + packet_type = IN_PID; } - /* - * data transfer stage: buffer setup - */ - buf = urb->transfer_buffer; - - if (is_input) - token |= IN_PID; - else - token |= OUT_PID; - - maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe, !is_input)); + maxpacketsize = max_packet(usb_maxpacket(urb->dev, urb->pipe, + usb_pipeout(urb->pipe))); /* * buffer gets wrapped in one or more qtds; * last one may be "short" (including zero len) * and may serve as a control status ack */ + buf = urb->transfer_buffer; + len = urb->transfer_buffer_length; + for (;;) { int this_qtd_len; - if (!buf && len) { - /* XXX This looks like usb storage / SCSI bug */ - dev_err(hcd->self.controller, "buf is null, dma is %08lx len is %d\n", - (long unsigned)urb->transfer_dma, len); - WARN_ON(1); - } + qtd = qtd_alloc(flags, urb, packet_type); + if (!qtd) + goto cleanup; + this_qtd_len = qtd_fill(qtd, buf, len); + list_add_tail(&qtd->qtd_list, head); - this_qtd_len = qtd_fill(qtd, buf, len, token); len -= this_qtd_len; buf += this_qtd_len; if (len <= 0) break; - - qtd = isp1760_qtd_alloc(flags); - if (!qtd) - goto cleanup; - qtd->urb = urb; - list_add_tail(&qtd->qtd_list, head); } /* @@ -1510,184 +1435,204 @@ static struct list_head *qh_urb_transaction(struct usb_hcd *hcd, if (usb_pipecontrol(urb->pipe)) { one_more = 1; - /* "in" <--> "out" */ - token ^= IN_PID; + if (packet_type == IN_PID) + packet_type = OUT_PID; + else + packet_type = IN_PID; } else if (usb_pipebulk(urb->pipe) && (urb->transfer_flags & URB_ZERO_PACKET) - && !(urb->transfer_buffer_length % maxpacket)) { + && !(urb->transfer_buffer_length % + maxpacketsize)) { one_more = 1; } if (one_more) { - qtd = isp1760_qtd_alloc(flags); + qtd = qtd_alloc(flags, urb, packet_type); if (!qtd) goto cleanup; - qtd->urb = urb; - list_add_tail(&qtd->qtd_list, head); /* never any data in such packets */ - qtd_fill(qtd, NULL, 0, token); + qtd_fill(qtd, NULL, 0); + list_add_tail(&qtd->qtd_list, head); } } - qtd->status = 0; - return head; + return; cleanup: - qtd_list_free(urb, head); - return NULL; + qtd_list_free(head); } static int isp1760_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) { - struct list_head qtd_list; - packet_enqueue *pe; - - INIT_LIST_HEAD(&qtd_list); + struct isp1760_hcd *priv = hcd_to_priv(hcd); + struct list_head *ep_queue; + struct isp1760_qh *qh, *qhit; + unsigned long spinflags; + LIST_HEAD(new_qtds); + int retval; + int qh_in_queue; switch (usb_pipetype(urb->pipe)) { case PIPE_CONTROL: + ep_queue = &priv->controlqhs; + break; case PIPE_BULK: - if (!qh_urb_transaction(hcd, urb, &qtd_list, mem_flags)) - return -ENOMEM; - pe = enqueue_an_ATL_packet; + ep_queue = &priv->bulkqhs; break; - case PIPE_INTERRUPT: - if (!qh_urb_transaction(hcd, urb, &qtd_list, mem_flags)) - return -ENOMEM; - pe = enqueue_an_INT_packet; + if (urb->interval < 0) + return -EINVAL; + /* FIXME: Check bandwidth */ + ep_queue = &priv->interruptqhs; break; - case PIPE_ISOCHRONOUS: - dev_err(hcd->self.controller, "PIPE_ISOCHRONOUS ain't supported\n"); + dev_err(hcd->self.controller, "%s: isochronous USB packets " + "not yet supported\n", + __func__); + return -EPIPE; default: + dev_err(hcd->self.controller, "%s: unknown pipe type\n", + __func__); return -EPIPE; } - return isp1760_prepare_enqueue(hcd, urb, &qtd_list, mem_flags, pe); -} - -static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) -{ - struct isp1760_hcd *priv = hcd_to_priv(hcd); - struct inter_packet_info *ints; - u32 i; - u32 reg_base, or_reg, skip_reg; - unsigned long flags; - struct ptd ptd; - packet_enqueue *pe; + if (usb_pipein(urb->pipe)) + urb->actual_length = 0; - switch (usb_pipetype(urb->pipe)) { - case PIPE_ISOCHRONOUS: - return -EPIPE; - break; + packetize_urb(hcd, urb, &new_qtds, mem_flags); + if (list_empty(&new_qtds)) + return -ENOMEM; + urb->hcpriv = NULL; /* Used to signal unlink to interrupt handler */ - case PIPE_INTERRUPT: - ints = priv->int_ints; - reg_base = INT_PTD_OFFSET; - or_reg = HC_INT_IRQ_MASK_OR_REG; - skip_reg = HC_INT_PTD_SKIPMAP_REG; - pe = enqueue_an_INT_packet; - break; + retval = 0; + spin_lock_irqsave(&priv->lock, spinflags); - default: - ints = priv->atl_ints; - reg_base = ATL_PTD_OFFSET; - or_reg = HC_ATL_IRQ_MASK_OR_REG; - skip_reg = HC_ATL_PTD_SKIPMAP_REG; - pe = enqueue_an_ATL_packet; - break; + if (!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)) { + retval = -ESHUTDOWN; + goto out; } + retval = usb_hcd_link_urb_to_ep(hcd, urb); + if (retval) + goto out; - memset(&ptd, 0, sizeof(ptd)); - spin_lock_irqsave(&priv->lock, flags); - - for (i = 0; i < 32; i++) { - if (!ints[i].qh) - continue; - BUG_ON(!ints[i].qtd); + qh = urb->ep->hcpriv; + if (qh) { + qh_in_queue = 0; + list_for_each_entry(qhit, ep_queue, qh_list) { + if (qhit == qh) { + qh_in_queue = 1; + break; + } + } + if (!qh_in_queue) + list_add_tail(&qh->qh_list, ep_queue); + } else { + qh = qh_alloc(GFP_ATOMIC); + if (!qh) { + retval = -ENOMEM; + goto out; + } + list_add_tail(&qh->qh_list, ep_queue); + urb->ep->hcpriv = qh; + } - if (ints[i].qtd->urb == urb) { - u32 skip_map; - u32 or_map; - struct isp1760_qtd *qtd; - struct isp1760_qh *qh; + list_splice_tail(&new_qtds, &qh->qtd_list); + schedule_ptds(hcd); - skip_map = reg_read32(hcd->regs, skip_reg); - skip_map |= 1 << i; - reg_write32(hcd->regs, skip_reg, skip_map); +out: + spin_unlock_irqrestore(&priv->lock, spinflags); + return retval; +} - or_map = reg_read32(hcd->regs, or_reg); - or_map &= ~(1 << i); - reg_write32(hcd->regs, or_reg, or_map); +static void kill_transfer(struct usb_hcd *hcd, struct urb *urb, + struct isp1760_qh *qh) +{ + struct isp1760_hcd *priv = hcd_to_priv(hcd); + int skip_map; - ptd_write(hcd->regs, reg_base, i, &ptd); + WARN_ON(qh->slot == -1); - qtd = ints[i].qtd; - qh = ints[i].qh; + /* We need to forcefully reclaim the slot since some transfers never + return, e.g. interrupt transfers and NAKed bulk transfers. */ + if (usb_pipebulk(urb->pipe)) { + skip_map = reg_read32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG); + skip_map |= (1 << qh->slot); + reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, skip_map); + priv->atl_slots[qh->slot].qh = NULL; + priv->atl_slots[qh->slot].qtd = NULL; + } else { + skip_map = reg_read32(hcd->regs, HC_INT_PTD_SKIPMAP_REG); + skip_map |= (1 << qh->slot); + reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, skip_map); + priv->int_slots[qh->slot].qh = NULL; + priv->int_slots[qh->slot].qtd = NULL; + } - free_mem(hcd, qtd); - qtd = clean_up_qtdlist(qtd, qh); + qh->slot = -1; + priv->active_ptds--; +} - ints[i].qh = NULL; - ints[i].qtd = NULL; +static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, + int status) +{ + struct isp1760_hcd *priv = hcd_to_priv(hcd); + unsigned long spinflags; + struct isp1760_qh *qh; + struct isp1760_qtd *qtd; + int retval = 0; - urb->status = status; - isp1760_urb_done(hcd, urb); - if (qtd) - pe(hcd, qh, qtd); - break; + spin_lock_irqsave(&priv->lock, spinflags); - } else { - struct isp1760_qtd *qtd; - - list_for_each_entry(qtd, &ints[i].qtd->qtd_list, - qtd_list) { - if (qtd->urb == urb) { - clean_up_qtdlist(qtd, ints[i].qh); - isp1760_urb_done(hcd, urb); - qtd = NULL; - break; - } - } + qh = urb->ep->hcpriv; + if (!qh) { + retval = -EINVAL; + goto out; + } - /* We found the urb before the last slot */ - if (!qtd) - break; + list_for_each_entry(qtd, &qh->qtd_list, qtd_list) + if (qtd->urb == urb) { + if (qtd->status == QTD_XFER_STARTED) + kill_transfer(hcd, urb, qh); + qtd->status = QTD_RETIRE; } - } - spin_unlock_irqrestore(&priv->lock, flags); - return 0; + urb->status = status; + schedule_ptds(hcd); + +out: + spin_unlock_irqrestore(&priv->lock, spinflags); + return retval; } -static irqreturn_t isp1760_irq(struct usb_hcd *hcd) +static void isp1760_endpoint_disable(struct usb_hcd *hcd, + struct usb_host_endpoint *ep) { struct isp1760_hcd *priv = hcd_to_priv(hcd); - u32 imask; - irqreturn_t irqret = IRQ_NONE; + unsigned long spinflags; + struct isp1760_qh *qh; + struct isp1760_qtd *qtd; - spin_lock(&priv->lock); + spin_lock_irqsave(&priv->lock, spinflags); - if (!(hcd->state & HC_STATE_RUNNING)) - goto leave; + qh = ep->hcpriv; + if (!qh) + goto out; - imask = reg_read32(hcd->regs, HC_INTERRUPT_REG); - if (unlikely(!imask)) - goto leave; + list_for_each_entry(qtd, &qh->qtd_list, qtd_list) { + if (qtd->status == QTD_XFER_STARTED) + kill_transfer(hcd, qtd->urb, qh); + qtd->status = QTD_RETIRE; + qtd->urb->status = -ECONNRESET; + } - reg_write32(hcd->regs, HC_INTERRUPT_REG, imask); - if (imask & (HC_ATL_INT | HC_SOT_INT)) - do_atl_int(hcd); + ep->hcpriv = NULL; + /* Cannot free qh here since it will be parsed by schedule_ptds() */ - if (imask & HC_INTL_INT) - do_intl_int(hcd); + schedule_ptds(hcd); - irqret = IRQ_HANDLED; -leave: - spin_unlock(&priv->lock); - return irqret; +out: + spin_unlock_irqrestore(&priv->lock, spinflags); } static int isp1760_hub_status_data(struct usb_hcd *hcd, char *buf) @@ -1778,7 +1723,7 @@ static int check_reset_complete(struct usb_hcd *hcd, int index, /* if reset finished and it's still not enabled -- handoff */ if (!(port_status & PORT_PE)) { - dev_err(hcd->self.controller, + dev_info(hcd->self.controller, "port %d full speed --> companion\n", index + 1); @@ -1787,7 +1732,7 @@ static int check_reset_complete(struct usb_hcd *hcd, int index, reg_write32(hcd->regs, HC_PORTSC1, port_status); } else - dev_err(hcd->self.controller, "port %d high speed\n", + dev_info(hcd->self.controller, "port %d high speed\n", index + 1); return port_status; @@ -2059,51 +2004,6 @@ error: return retval; } -static void isp1760_endpoint_disable(struct usb_hcd *hcd, - struct usb_host_endpoint *ep) -{ - struct isp1760_hcd *priv = hcd_to_priv(hcd); - struct isp1760_qh *qh; - struct isp1760_qtd *qtd; - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - qh = ep->hcpriv; - if (!qh) - goto out; - - ep->hcpriv = NULL; - do { - /* more than entry might get removed */ - if (list_empty(&qh->qtd_list)) - break; - - qtd = list_first_entry(&qh->qtd_list, struct isp1760_qtd, - qtd_list); - - if (qtd->status & URB_ENQUEUED) { - spin_unlock_irqrestore(&priv->lock, flags); - isp1760_urb_dequeue(hcd, qtd->urb, -ECONNRESET); - spin_lock_irqsave(&priv->lock, flags); - } else { - struct urb *urb; - - urb = qtd->urb; - clean_up_qtdlist(qtd, qh); - urb->status = -ECONNRESET; - isp1760_urb_done(hcd, urb); - } - } while (1); - - qh_destroy(qh); - /* remove requests and leak them. - * ATL are pretty fast done, INT could take a while... - * The latter shoule be removed - */ -out: - spin_unlock_irqrestore(&priv->lock, flags); -} - static int isp1760_get_frame(struct usb_hcd *hcd) { struct isp1760_hcd *priv = hcd_to_priv(hcd); @@ -2165,6 +2065,13 @@ static const struct hc_driver isp1760_hc_driver = { int __init init_kmem_once(void) { + urb_listitem_cachep = kmem_cache_create("isp1760 urb_listitem", + sizeof(struct urb_listitem), 0, SLAB_TEMPORARY | + SLAB_MEM_SPREAD, NULL); + + if (!urb_listitem_cachep) + return -ENOMEM; + qtd_cachep = kmem_cache_create("isp1760_qtd", sizeof(struct isp1760_qtd), 0, SLAB_TEMPORARY | SLAB_MEM_SPREAD, NULL); @@ -2187,6 +2094,7 @@ void deinit_kmem_cache(void) { kmem_cache_destroy(qtd_cachep); kmem_cache_destroy(qh_cachep); + kmem_cache_destroy(urb_listitem_cachep); } struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len, diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h index 8705076..014a7df 100644 --- a/drivers/usb/host/isp1760-hcd.h +++ b/drivers/usb/host/isp1760-hcd.h @@ -49,10 +49,9 @@ void deinit_kmem_cache(void); #define SW_RESET_RESET_ALL (1 << 0) #define HC_BUFFER_STATUS_REG 0x334 -#define ATL_BUFFER 0x1 -#define INT_BUFFER 0x2 -#define ISO_BUFFER 0x4 -#define BUFFER_MAP 0x7 +#define ISO_BUF_FILL (1 << 2) +#define INT_BUF_FILL (1 << 1) +#define ATL_BUF_FILL (1 << 0) #define HC_MEMORY_REG 0x33c #define ISP_BANK(x) ((x) << 16) @@ -68,14 +67,13 @@ void deinit_kmem_cache(void); #define HC_INTERRUPT_REG 0x310 #define HC_INTERRUPT_ENABLE 0x314 -#define INTERRUPT_ENABLE_MASK (HC_INTL_INT | HC_ATL_INT | HC_EOT_INT) -#define INTERRUPT_ENABLE_SOT_MASK (HC_INTL_INT | HC_SOT_INT | HC_EOT_INT) - #define HC_ISO_INT (1 << 9) #define HC_ATL_INT (1 << 8) #define HC_INTL_INT (1 << 7) #define HC_EOT_INT (1 << 3) #define HC_SOT_INT (1 << 1) +#define INTERRUPT_ENABLE_MASK (HC_INTL_INT | HC_ATL_INT) +#define INTERRUPT_ENABLE_SOT_MASK (HC_SOT_INT) #define HC_ISO_IRQ_MASK_OR_REG 0x318 #define HC_INT_IRQ_MASK_OR_REG 0x31C @@ -106,7 +104,7 @@ struct ptd { #define ATL_PTD_OFFSET 0x0c00 #define PAYLOAD_OFFSET 0x1000 -struct inter_packet_info { +struct slotinfo { struct isp1760_qh *qh; struct isp1760_qtd *qtd; }; @@ -156,54 +154,52 @@ struct memory_chunk { /* ATL */ /* DW0 */ -#define PTD_VALID 1 -#define PTD_LENGTH(x) (((u32) x) << 3) -#define PTD_MAXPACKET(x) (((u32) x) << 18) -#define PTD_MULTI(x) (((u32) x) << 29) -#define PTD_ENDPOINT(x) (((u32) x) << 31) +#define DW0_VALID_BIT 1 +#define FROM_DW0_VALID(x) ((x) & 0x01) +#define TO_DW0_LENGTH(x) (((u32) x) << 3) +#define TO_DW0_MAXPACKET(x) (((u32) x) << 18) +#define TO_DW0_MULTI(x) (((u32) x) << 29) +#define TO_DW0_ENDPOINT(x) (((u32) x) << 31) /* DW1 */ -#define PTD_DEVICE_ADDR(x) (((u32) x) << 3) -#define PTD_PID_TOKEN(x) (((u32) x) << 10) -#define PTD_TRANS_BULK ((u32) 2 << 12) -#define PTD_TRANS_INT ((u32) 3 << 12) -#define PTD_TRANS_SPLIT ((u32) 1 << 14) -#define PTD_SE_USB_LOSPEED ((u32) 2 << 16) -#define PTD_PORT_NUM(x) (((u32) x) << 18) -#define PTD_HUB_NUM(x) (((u32) x) << 25) -#define PTD_PING(x) (((u32) x) << 26) +#define TO_DW1_DEVICE_ADDR(x) (((u32) x) << 3) +#define TO_DW1_PID_TOKEN(x) (((u32) x) << 10) +#define DW1_TRANS_BULK ((u32) 2 << 12) +#define DW1_TRANS_INT ((u32) 3 << 12) +#define DW1_TRANS_SPLIT ((u32) 1 << 14) +#define DW1_SE_USB_LOSPEED ((u32) 2 << 16) +#define TO_DW1_PORT_NUM(x) (((u32) x) << 18) +#define TO_DW1_HUB_NUM(x) (((u32) x) << 25) /* DW2 */ -#define PTD_RL_CNT(x) (((u32) x) << 25) -#define PTD_DATA_START_ADDR(x) (((u32) x) << 8) -#define BASE_ADDR 0x1000 +#define TO_DW2_DATA_START_ADDR(x) (((u32) x) << 8) +#define TO_DW2_RL(x) ((x) << 25) +#define FROM_DW2_RL(x) (((x) >> 25) & 0xf) /* DW3 */ -#define PTD_CERR(x) (((u32) x) << 23) -#define PTD_NAC_CNT(x) (((u32) x) << 19) -#define PTD_ACTIVE ((u32) 1 << 31) -#define PTD_DATA_TOGGLE(x) (((u32) x) << 25) - -#define DW3_HALT_BIT (1 << 30) +#define FROM_DW3_NRBYTESTRANSFERRED(x) ((x) & 0x7fff) +#define FROM_DW3_SCS_NRBYTESTRANSFERRED(x) ((x) & 0x07ff) +#define TO_DW3_NAKCOUNT(x) ((x) << 19) +#define FROM_DW3_NAKCOUNT(x) (((x) >> 19) & 0xf) +#define TO_DW3_CERR(x) ((x) << 23) +#define FROM_DW3_CERR(x) (((x) >> 23) & 0x3) +#define TO_DW3_DATA_TOGGLE(x) ((x) << 25) +#define FROM_DW3_DATA_TOGGLE(x) (((x) >> 25) & 0x1) +#define TO_DW3_PING(x) ((x) << 26) +#define FROM_DW3_PING(x) (((x) >> 26) & 0x1) #define DW3_ERROR_BIT (1 << 28) -#define DW3_QTD_ACTIVE (1 << 31) +#define DW3_BABBLE_BIT (1 << 29) +#define DW3_HALT_BIT (1 << 30) +#define DW3_ACTIVE_BIT (1 << 31) #define INT_UNDERRUN (1 << 2) #define INT_BABBLE (1 << 1) #define INT_EXACT (1 << 0) -#define DW1_GET_PID(x) (((x) >> 10) & 0x3) -#define PTD_XFERRED_LENGTH(x) ((x) & 0x7fff) -#define PTD_XFERRED_LENGTH_LO(x) ((x) & 0x7ff) - #define SETUP_PID (2) #define IN_PID (1) #define OUT_PID (0) -#define GET_QTD_TOKEN_TYPE(x) ((x) & 0x3) - -#define DATA_TOGGLE (1 << 31) -#define GET_DATA_TOGGLE(x) ((x) >> 31) /* Errata 1 */ #define RL_COUNTER (0) #define NAK_COUNTER (0) #define ERR_COUNTER (2) -#endif +#endif /* _ISP1760_HCD_H_ */ diff --git a/drivers/usb/host/octeon2-common.c b/drivers/usb/host/octeon2-common.c index 72d672c..d9df423 100644 --- a/drivers/usb/host/octeon2-common.c +++ b/drivers/usb/host/octeon2-common.c @@ -3,18 +3,19 @@ * License. See the file "COPYING" in the main directory of this archive * for more details. * - * Copyright (C) 2010 Cavium Networks + * Copyright (C) 2010, 2011 Cavium Networks */ #include <linux/module.h> +#include <linux/mutex.h> #include <linux/delay.h> -#include <asm/atomic.h> - #include <asm/octeon/octeon.h> #include <asm/octeon/cvmx-uctlx-defs.h> -static atomic_t octeon2_usb_clock_start_cnt = ATOMIC_INIT(0); +static DEFINE_MUTEX(octeon2_usb_clocks_mutex); + +static int octeon2_usb_clock_start_cnt; void octeon2_usb_clocks_start(void) { @@ -26,8 +27,12 @@ void octeon2_usb_clocks_start(void) int i; unsigned long io_clk_64_to_ns; - if (atomic_inc_return(&octeon2_usb_clock_start_cnt) != 1) - return; + + mutex_lock(&octeon2_usb_clocks_mutex); + + octeon2_usb_clock_start_cnt++; + if (octeon2_usb_clock_start_cnt != 1) + goto exit; io_clk_64_to_ns = 64000000000ull / octeon_get_io_clock_rate(); @@ -43,6 +48,13 @@ void octeon2_usb_clocks_start(void) /* Step 3: Configure the reference clock, PHY, and HCLK */ clk_rst_ctl.u64 = cvmx_read_csr(CVMX_UCTLX_CLK_RST_CTL(0)); + + /* + * If the UCTL looks like it has already been started, skip + * the initialization, otherwise bus errors are obtained. + */ + if (clk_rst_ctl.s.hrst) + goto end_clock; /* 3a */ clk_rst_ctl.s.p_por = 1; clk_rst_ctl.s.hrst = 0; @@ -158,28 +170,31 @@ void octeon2_usb_clocks_start(void) clk_rst_ctl.s.hrst = 1; cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64); +end_clock: /* Now we can set some other registers. */ for (i = 0; i <= 1; i++) { port_ctl_status.u64 = cvmx_read_csr(CVMX_UCTLX_UPHY_PORTX_CTL_STATUS(i, 0)); - /* Set txvreftune to 15 to obtain complient 'eye' diagram. */ + /* Set txvreftune to 15 to obtain compliant 'eye' diagram. */ port_ctl_status.s.txvreftune = 15; + port_ctl_status.s.txrisetune = 1; + port_ctl_status.s.txpreemphasistune = 1; cvmx_write_csr(CVMX_UCTLX_UPHY_PORTX_CTL_STATUS(i, 0), port_ctl_status.u64); } + + /* Set uSOF cycle period to 60,000 bits. */ + cvmx_write_csr(CVMX_UCTLX_EHCI_FLA(0), 0x20ull); +exit: + mutex_unlock(&octeon2_usb_clocks_mutex); } EXPORT_SYMBOL(octeon2_usb_clocks_start); void octeon2_usb_clocks_stop(void) { - union cvmx_uctlx_if_ena if_ena; - - if (atomic_dec_return(&octeon2_usb_clock_start_cnt) != 0) - return; - - if_ena.u64 = 0; - if_ena.s.en = 0; - cvmx_write_csr(CVMX_UCTLX_IF_ENA(0), if_ena.u64); + mutex_lock(&octeon2_usb_clocks_mutex); + octeon2_usb_clock_start_cnt--; + mutex_unlock(&octeon2_usb_clocks_mutex); } EXPORT_SYMBOL(octeon2_usb_clocks_stop); diff --git a/drivers/usb/host/ohci-ath79.c b/drivers/usb/host/ohci-ath79.c new file mode 100644 index 0000000..ffea3e7 --- /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 d557235..9aa10bd 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -764,6 +764,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) if (ints == ~(u32)0) { disable (ohci); ohci_dbg (ohci, "device removed!\n"); + usb_hc_died(hcd); return IRQ_HANDLED; } @@ -771,7 +772,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) ints &= ohci_readl(ohci, ®s->intrenable); /* interrupt for some other device? */ - if (ints == 0) + if (ints == 0 || unlikely(hcd->state == HC_STATE_HALT)) return IRQ_NOTMINE; if (ints & OHCI_INTR_UE) { @@ -788,6 +789,7 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd) } else { disable (ohci); ohci_err (ohci, "OHCI Unrecoverable Error, disabled\n"); + usb_hc_died(hcd); } ohci_dump (ohci, 1); @@ -1105,6 +1107,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/ohci-pci.c b/drivers/usb/host/ohci-pci.c index d84d6f0..ad8166c 100644 --- a/drivers/usb/host/ohci-pci.c +++ b/drivers/usb/host/ohci-pci.c @@ -181,10 +181,18 @@ static int ohci_quirk_amd700(struct usb_hcd *hcd) */ static int ohci_quirk_nvidia_shutdown(struct usb_hcd *hcd) { + struct pci_dev *pdev = to_pci_dev(hcd->self.controller); struct ohci_hcd *ohci = hcd_to_ohci(hcd); - ohci->flags |= OHCI_QUIRK_SHUTDOWN; - ohci_dbg(ohci, "enabled nVidia shutdown quirk\n"); + /* Evidently nVidia fixed their later hardware; this is a guess at + * the changeover point. + */ +#define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_USB 0x026d + + if (pdev->device < PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_USB) { + ohci->flags |= OHCI_QUIRK_SHUTDOWN; + ohci_dbg(ohci, "enabled nVidia shutdown quirk\n"); + } return 0; } diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c index a68af2d..7c9a4d5 100644 --- a/drivers/usb/host/ohci-s3c2410.c +++ b/drivers/usb/host/ohci-s3c2410.c @@ -56,9 +56,8 @@ static void s3c2410_start_hc(struct platform_device *dev, struct usb_hcd *hcd) info->hcd = hcd; info->report_oc = s3c2410_hcd_oc; - if (info->enable_oc != NULL) { + if (info->enable_oc != NULL) (info->enable_oc)(info, 1); - } } } @@ -72,9 +71,8 @@ static void s3c2410_stop_hc(struct platform_device *dev) info->report_oc = NULL; info->hcd = NULL; - if (info->enable_oc != NULL) { + if (info->enable_oc != NULL) (info->enable_oc)(info, 0); - } } clk_disable(clk); @@ -88,14 +86,14 @@ static void s3c2410_stop_hc(struct platform_device *dev) */ static int -ohci_s3c2410_hub_status_data (struct usb_hcd *hcd, char *buf) +ohci_s3c2410_hub_status_data(struct usb_hcd *hcd, char *buf) { struct s3c2410_hcd_info *info = to_s3c2410_info(hcd); struct s3c2410_hcd_port *port; int orig; int portno; - orig = ohci_hub_status_data (hcd, buf); + orig = ohci_hub_status_data(hcd, buf); if (info == NULL) return orig; @@ -145,7 +143,7 @@ static void s3c2410_usb_set_power(struct s3c2410_hcd_info *info, * request. */ -static int ohci_s3c2410_hub_control ( +static int ohci_s3c2410_hub_control( struct usb_hcd *hcd, u16 typeReq, u16 wValue, @@ -199,9 +197,8 @@ static int ohci_s3c2410_hub_control ( dev_dbg(hcd->self.controller, "ClearPortFeature: OVER_CURRENT\n"); - if (valid_port(wIndex)) { + if (valid_port(wIndex)) info->port[wIndex-1].oc_status = 0; - } goto out; @@ -242,8 +239,11 @@ static int ohci_s3c2410_hub_control ( desc->wHubCharacteristics |= cpu_to_le16(0x0001); if (info->enable_oc) { - desc->wHubCharacteristics &= ~cpu_to_le16(HUB_CHAR_OCPM); - desc->wHubCharacteristics |= cpu_to_le16(0x0008|0x0001); + desc->wHubCharacteristics &= ~cpu_to_le16( + HUB_CHAR_OCPM); + desc->wHubCharacteristics |= cpu_to_le16( + 0x0008 | + 0x0001); } dev_dbg(hcd->self.controller, "wHubCharacteristics after 0x%04x\n", @@ -257,13 +257,11 @@ static int ohci_s3c2410_hub_control ( dev_dbg(hcd->self.controller, "GetPortStatus(%d)\n", wIndex); if (valid_port(wIndex)) { - if (info->port[wIndex-1].oc_changed) { + if (info->port[wIndex-1].oc_changed) *data |= cpu_to_le32(RH_PS_OCIC); - } - if (info->port[wIndex-1].oc_status) { + if (info->port[wIndex-1].oc_status) *data |= cpu_to_le32(RH_PS_POCI); - } } } @@ -321,7 +319,7 @@ static void s3c2410_hcd_oc(struct s3c2410_hcd_info *info, int port_oc) */ static void -usb_hcd_s3c2410_remove (struct usb_hcd *hcd, struct platform_device *dev) +usb_hcd_s3c2410_remove(struct usb_hcd *hcd, struct platform_device *dev) { usb_remove_hcd(hcd); s3c2410_stop_hc(dev); @@ -339,7 +337,7 @@ usb_hcd_s3c2410_remove (struct usb_hcd *hcd, struct platform_device *dev) * through the hotplug entry's driver_data. * */ -static int usb_hcd_s3c2410_probe (const struct hc_driver *driver, +static int usb_hcd_s3c2410_probe(const struct hc_driver *driver, struct platform_device *dev) { struct usb_hcd *hcd = NULL; @@ -353,7 +351,7 @@ static int usb_hcd_s3c2410_probe (const struct hc_driver *driver, return -ENOMEM; hcd->rsrc_start = dev->resource[0].start; - hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1; + hcd->rsrc_len = resource_size(&dev->resource[0]); if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { dev_err(&dev->dev, "request_mem_region failed\n"); @@ -364,14 +362,14 @@ static int usb_hcd_s3c2410_probe (const struct hc_driver *driver, clk = clk_get(&dev->dev, "usb-host"); if (IS_ERR(clk)) { dev_err(&dev->dev, "cannot get usb-host clock\n"); - retval = -ENOENT; + retval = PTR_ERR(clk); goto err_mem; } usb_clk = clk_get(&dev->dev, "usb-bus-host"); if (IS_ERR(usb_clk)) { dev_err(&dev->dev, "cannot get usb-bus-host clock\n"); - retval = -ENOENT; + retval = PTR_ERR(usb_clk); goto err_clk; } @@ -411,17 +409,19 @@ static int usb_hcd_s3c2410_probe (const struct hc_driver *driver, /*-------------------------------------------------------------------------*/ static int -ohci_s3c2410_start (struct usb_hcd *hcd) +ohci_s3c2410_start(struct usb_hcd *hcd) { - struct ohci_hcd *ohci = hcd_to_ohci (hcd); + struct ohci_hcd *ohci = hcd_to_ohci(hcd); int ret; - if ((ret = ohci_init(ohci)) < 0) + ret = ohci_init(ohci); + if (ret < 0) return ret; - if ((ret = ohci_run (ohci)) < 0) { - err ("can't start %s", hcd->self.bus_name); - ohci_stop (hcd); + ret = ohci_run(ohci); + if (ret < 0) { + err("can't start %s", hcd->self.bus_name); + ohci_stop(hcd); return ret; } @@ -473,12 +473,12 @@ static const struct hc_driver ohci_s3c2410_hc_driver = { /* device driver */ -static int ohci_hcd_s3c2410_drv_probe(struct platform_device *pdev) +static int __devinit ohci_hcd_s3c2410_drv_probe(struct platform_device *pdev) { return usb_hcd_s3c2410_probe(&ohci_s3c2410_hc_driver, pdev); } -static int ohci_hcd_s3c2410_drv_remove(struct platform_device *pdev) +static int __devexit ohci_hcd_s3c2410_drv_remove(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); @@ -488,7 +488,7 @@ static int ohci_hcd_s3c2410_drv_remove(struct platform_device *pdev) static struct platform_driver ohci_hcd_s3c2410_driver = { .probe = ohci_hcd_s3c2410_drv_probe, - .remove = ohci_hcd_s3c2410_drv_remove, + .remove = __devexit_p(ohci_hcd_s3c2410_drv_remove), .shutdown = usb_hcd_platform_shutdown, /*.suspend = ohci_hcd_s3c2410_drv_suspend, */ /*.resume = ohci_hcd_s3c2410_drv_resume, */ diff --git a/drivers/usb/host/oxu210hp-hcd.c b/drivers/usb/host/oxu210hp-hcd.c index 4a771f6..5fbe997 100644 --- a/drivers/usb/host/oxu210hp-hcd.c +++ b/drivers/usb/host/oxu210hp-hcd.c @@ -1884,6 +1884,7 @@ static int enable_periodic(struct oxu_hcd *oxu) status = handshake(oxu, &oxu->regs->status, STS_PSS, 0, 9 * 125); if (status != 0) { oxu_to_hcd(oxu)->state = HC_STATE_HALT; + usb_hc_died(oxu_to_hcd(oxu)); return status; } @@ -1909,6 +1910,7 @@ static int disable_periodic(struct oxu_hcd *oxu) status = handshake(oxu, &oxu->regs->status, STS_PSS, STS_PSS, 9 * 125); if (status != 0) { oxu_to_hcd(oxu)->state = HC_STATE_HALT; + usb_hc_died(oxu_to_hcd(oxu)); return status; } @@ -2449,8 +2451,9 @@ static irqreturn_t oxu210_hcd_irq(struct usb_hcd *hcd) goto dead; } + /* Shared IRQ? */ status &= INTR_MASK; - if (!status) { /* irq sharing? */ + if (!status || unlikely(hcd->state == HC_STATE_HALT)) { spin_unlock(&oxu->lock); return IRQ_NONE; } @@ -2516,6 +2519,7 @@ static irqreturn_t oxu210_hcd_irq(struct usb_hcd *hcd) dead: ehci_reset(oxu); writel(0, &oxu->regs->configured_flag); + usb_hc_died(hcd); /* generic layer kills/unlinks all urbs, then * uses oxu_stop to clean up the rest */ diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c index 9b166d7..f16c59d 100644 --- a/drivers/usb/host/pci-quirks.c +++ b/drivers/usb/host/pci-quirks.c @@ -14,6 +14,7 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/acpi.h> +#include <linux/dmi.h> #include "pci-quirks.h" #include "xhci-ext-caps.h" @@ -503,14 +504,84 @@ static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev) iounmap(base); } +static void __devinit ehci_bios_handoff(struct pci_dev *pdev, + void __iomem *op_reg_base, + u32 cap, u8 offset) +{ + int try_handoff = 1, tried_handoff = 0; + + /* The Pegatron Lucid (ExoPC) tablet sporadically waits for 90 + * seconds trying the handoff on its unused controller. Skip + * it. */ + if (pdev->vendor == 0x8086 && pdev->device == 0x283a) { + const char *dmi_bn = dmi_get_system_info(DMI_BOARD_NAME); + const char *dmi_bv = dmi_get_system_info(DMI_BIOS_VERSION); + if (dmi_bn && !strcmp(dmi_bn, "EXOPG06411") && + dmi_bv && !strcmp(dmi_bv, "Lucid-CE-133")) + try_handoff = 0; + } + + if (try_handoff && (cap & EHCI_USBLEGSUP_BIOS)) { + dev_dbg(&pdev->dev, "EHCI: BIOS handoff\n"); + +#if 0 +/* aleksey_gorelov@phoenix.com reports that some systems need SMI forced on, + * but that seems dubious in general (the BIOS left it off intentionally) + * and is known to prevent some systems from booting. so we won't do this + * unless maybe we can determine when we're on a system that needs SMI forced. + */ + /* BIOS workaround (?): be sure the pre-Linux code + * receives the SMI + */ + pci_read_config_dword(pdev, offset + EHCI_USBLEGCTLSTS, &val); + pci_write_config_dword(pdev, offset + EHCI_USBLEGCTLSTS, + val | EHCI_USBLEGCTLSTS_SOOE); +#endif + + /* some systems get upset if this semaphore is + * set for any other reason than forcing a BIOS + * handoff.. + */ + pci_write_config_byte(pdev, offset + 3, 1); + } + + /* if boot firmware now owns EHCI, spin till it hands it over. */ + if (try_handoff) { + int msec = 1000; + while ((cap & EHCI_USBLEGSUP_BIOS) && (msec > 0)) { + tried_handoff = 1; + msleep(10); + msec -= 10; + pci_read_config_dword(pdev, offset, &cap); + } + } + + if (cap & EHCI_USBLEGSUP_BIOS) { + /* well, possibly buggy BIOS... try to shut it down, + * and hope nothing goes too wrong + */ + if (try_handoff) + dev_warn(&pdev->dev, "EHCI: BIOS handoff failed" + " (BIOS bug?) %08x\n", cap); + pci_write_config_byte(pdev, offset + 2, 0); + } + + /* just in case, always disable EHCI SMIs */ + pci_write_config_dword(pdev, offset + EHCI_USBLEGCTLSTS, 0); + + /* If the BIOS ever owned the controller then we can't expect + * any power sessions to remain intact. + */ + if (tried_handoff) + writel(0, op_reg_base + EHCI_CONFIGFLAG); +} + static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev) { - int wait_time, delta; void __iomem *base, *op_reg_base; - u32 hcc_params, val; + u32 hcc_params, cap, val; u8 offset, cap_length; - int count = 256/4; - int tried_handoff = 0; + int wait_time, delta, count = 256/4; if (!mmio_resource_enabled(pdev, 0)) return; @@ -529,77 +600,17 @@ static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev) hcc_params = readl(base + EHCI_HCC_PARAMS); offset = (hcc_params >> 8) & 0xff; while (offset && --count) { - u32 cap; - int msec; - pci_read_config_dword(pdev, offset, &cap); - switch (cap & 0xff) { - case 1: /* BIOS/SMM/... handoff support */ - if ((cap & EHCI_USBLEGSUP_BIOS)) { - dev_dbg(&pdev->dev, "EHCI: BIOS handoff\n"); -#if 0 -/* aleksey_gorelov@phoenix.com reports that some systems need SMI forced on, - * but that seems dubious in general (the BIOS left it off intentionally) - * and is known to prevent some systems from booting. so we won't do this - * unless maybe we can determine when we're on a system that needs SMI forced. - */ - /* BIOS workaround (?): be sure the - * pre-Linux code receives the SMI - */ - pci_read_config_dword(pdev, - offset + EHCI_USBLEGCTLSTS, - &val); - pci_write_config_dword(pdev, - offset + EHCI_USBLEGCTLSTS, - val | EHCI_USBLEGCTLSTS_SOOE); -#endif - - /* some systems get upset if this semaphore is - * set for any other reason than forcing a BIOS - * handoff.. - */ - pci_write_config_byte(pdev, offset + 3, 1); - } - - /* if boot firmware now owns EHCI, spin till - * it hands it over. - */ - msec = 1000; - while ((cap & EHCI_USBLEGSUP_BIOS) && (msec > 0)) { - tried_handoff = 1; - msleep(10); - msec -= 10; - pci_read_config_dword(pdev, offset, &cap); - } - - if (cap & EHCI_USBLEGSUP_BIOS) { - /* well, possibly buggy BIOS... try to shut - * it down, and hope nothing goes too wrong - */ - dev_warn(&pdev->dev, "EHCI: BIOS handoff failed" - " (BIOS bug?) %08x\n", cap); - pci_write_config_byte(pdev, offset + 2, 0); - } - - /* just in case, always disable EHCI SMIs */ - pci_write_config_dword(pdev, - offset + EHCI_USBLEGCTLSTS, - 0); - - /* If the BIOS ever owned the controller then we - * can't expect any power sessions to remain intact. - */ - if (tried_handoff) - writel(0, op_reg_base + EHCI_CONFIGFLAG); + switch (cap & 0xff) { + case 1: + ehci_bios_handoff(pdev, op_reg_base, cap, offset); break; - case 0: /* illegal reserved capability */ - cap = 0; - /* FALLTHROUGH */ + case 0: /* Illegal reserved cap, set cap=0 so we exit */ + cap = 0; /* then fallthrough... */ default: dev_warn(&pdev->dev, "EHCI: unrecognized capability " - "%02x\n", cap & 0xff); - break; + "%02x\n", cap & 0xff); } offset = (cap >> 8) & 0xff; } diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c index fafccc2..1a99624 100644 --- a/drivers/usb/host/sl811-hcd.c +++ b/drivers/usb/host/sl811-hcd.c @@ -72,12 +72,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 @@ -808,7 +802,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 b478593..533d12c 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-debug.c b/drivers/usb/host/uhci-debug.c index ee60cd3..fc0b0da 100644 --- a/drivers/usb/host/uhci-debug.c +++ b/drivers/usb/host/uhci-debug.c @@ -37,7 +37,8 @@ static void lprintk(char *buf) } } -static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space) +static int uhci_show_td(struct uhci_hcd *uhci, struct uhci_td *td, char *buf, + int len, int space) { char *out = buf; char *spid; @@ -47,8 +48,9 @@ static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space) if (len < 160) return 0; - status = td_status(td); - out += sprintf(out, "%*s[%p] link (%08x) ", space, "", td, le32_to_cpu(td->link)); + status = td_status(uhci, td); + out += sprintf(out, "%*s[%p] link (%08x) ", space, "", td, + hc32_to_cpu(uhci, td->link)); out += sprintf(out, "e%d %s%s%s%s%s%s%s%s%s%sLength=%x ", ((status >> 27) & 3), (status & TD_CTRL_SPD) ? "SPD " : "", @@ -63,7 +65,7 @@ static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space) (status & TD_CTRL_BITSTUFF) ? "BitStuff " : "", status & 0x7ff); - token = td_token(td); + token = td_token(uhci, td); switch (uhci_packetid(token)) { case USB_PID_SETUP: spid = "SETUP"; @@ -86,12 +88,13 @@ static int uhci_show_td(struct uhci_td *td, char *buf, int len, int space) (token >> 8) & 127, (token & 0xff), spid); - out += sprintf(out, "(buf=%08x)\n", le32_to_cpu(td->buffer)); + out += sprintf(out, "(buf=%08x)\n", hc32_to_cpu(uhci, td->buffer)); return out - buf; } -static int uhci_show_urbp(struct urb_priv *urbp, char *buf, int len, int space) +static int uhci_show_urbp(struct uhci_hcd *uhci, struct urb_priv *urbp, + char *buf, int len, int space) { char *out = buf; struct uhci_td *td; @@ -130,9 +133,10 @@ static int uhci_show_urbp(struct urb_priv *urbp, char *buf, int len, int space) if (urbp->qh->type != USB_ENDPOINT_XFER_ISOC && (++i <= 10 || debug > 2)) { out += sprintf(out, "%*s%d: ", space + 2, "", i); - out += uhci_show_td(td, out, len - (out - buf), 0); + out += uhci_show_td(uhci, td, out, + len - (out - buf), 0); } else { - if (td_status(td) & TD_CTRL_ACTIVE) + if (td_status(uhci, td) & TD_CTRL_ACTIVE) ++nactive; else ++ninactive; @@ -151,7 +155,7 @@ static int uhci_show_qh(struct uhci_hcd *uhci, { char *out = buf; int i, nurbs; - __le32 element = qh_element(qh); + __hc32 element = qh_element(qh); char *qtype; /* Try to make sure there's enough memory */ @@ -168,7 +172,8 @@ static int uhci_show_qh(struct uhci_hcd *uhci, out += sprintf(out, "%*s[%p] %s QH link (%08x) element (%08x)\n", space, "", qh, qtype, - le32_to_cpu(qh->link), le32_to_cpu(element)); + hc32_to_cpu(uhci, qh->link), + hc32_to_cpu(uhci, element)); if (qh->type == USB_ENDPOINT_XFER_ISOC) out += sprintf(out, "%*s period %d phase %d load %d us, " "frame %x desc [%p]\n", @@ -178,22 +183,22 @@ static int uhci_show_qh(struct uhci_hcd *uhci, out += sprintf(out, "%*s period %d phase %d load %d us\n", space, "", qh->period, qh->phase, qh->load); - if (element & UHCI_PTR_QH) + if (element & UHCI_PTR_QH(uhci)) out += sprintf(out, "%*s Element points to QH (bug?)\n", space, ""); - if (element & UHCI_PTR_DEPTH) + if (element & UHCI_PTR_DEPTH(uhci)) out += sprintf(out, "%*s Depth traverse\n", space, ""); - if (element & cpu_to_le32(8)) + if (element & cpu_to_hc32(uhci, 8)) out += sprintf(out, "%*s Bit 3 set (bug?)\n", space, ""); - if (!(element & ~(UHCI_PTR_QH | UHCI_PTR_DEPTH))) + if (!(element & ~(UHCI_PTR_QH(uhci) | UHCI_PTR_DEPTH(uhci)))) out += sprintf(out, "%*s Element is NULL (bug?)\n", space, ""); if (list_empty(&qh->queue)) { out += sprintf(out, "%*s queue is empty\n", space, ""); if (qh == uhci->skel_async_qh) - out += uhci_show_td(uhci->term_td, out, + out += uhci_show_td(uhci, uhci->term_td, out, len - (out - buf), 0); } else { struct urb_priv *urbp = list_entry(qh->queue.next, @@ -201,13 +206,13 @@ static int uhci_show_qh(struct uhci_hcd *uhci, struct uhci_td *td = list_entry(urbp->td_list.next, struct uhci_td, list); - if (element != LINK_TO_TD(td)) + if (element != LINK_TO_TD(uhci, td)) out += sprintf(out, "%*s Element != First TD\n", space, ""); i = nurbs = 0; list_for_each_entry(urbp, &qh->queue, node) { if (++i <= 10) - out += uhci_show_urbp(urbp, out, + out += uhci_show_urbp(uhci, urbp, out, len - (out - buf), space + 2); else ++nurbs; @@ -219,7 +224,8 @@ static int uhci_show_qh(struct uhci_hcd *uhci, if (qh->dummy_td) { out += sprintf(out, "%*s Dummy TD\n", space, ""); - out += uhci_show_td(qh->dummy_td, out, len - (out - buf), 0); + out += uhci_show_td(uhci, qh->dummy_td, out, + len - (out - buf), 0); } return out - buf; @@ -285,7 +291,6 @@ static int uhci_show_root_hub_state(struct uhci_hcd *uhci, char *buf, int len) static int uhci_show_status(struct uhci_hcd *uhci, char *buf, int len) { char *out = buf; - unsigned long io_addr = uhci->io_addr; unsigned short usbcmd, usbstat, usbint, usbfrnum; unsigned int flbaseadd; unsigned char sof; @@ -295,14 +300,14 @@ static int uhci_show_status(struct uhci_hcd *uhci, char *buf, int len) if (len < 80 * 9) return 0; - usbcmd = inw(io_addr + 0); - usbstat = inw(io_addr + 2); - usbint = inw(io_addr + 4); - usbfrnum = inw(io_addr + 6); - flbaseadd = inl(io_addr + 8); - sof = inb(io_addr + 12); - portsc1 = inw(io_addr + 16); - portsc2 = inw(io_addr + 18); + usbcmd = uhci_readw(uhci, 0); + usbstat = uhci_readw(uhci, 2); + usbint = uhci_readw(uhci, 4); + usbfrnum = uhci_readw(uhci, 6); + flbaseadd = uhci_readl(uhci, 8); + sof = uhci_readb(uhci, 12); + portsc1 = uhci_readw(uhci, 16); + portsc2 = uhci_readw(uhci, 18); out += sprintf(out, " usbcmd = %04x %s%s%s%s%s%s%s%s\n", usbcmd, @@ -347,8 +352,8 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len) struct uhci_td *td; struct list_head *tmp, *head; int nframes, nerrs; - __le32 link; - __le32 fsbr_link; + __hc32 link; + __hc32 fsbr_link; static const char * const qh_names[] = { "unlink", "iso", "int128", "int64", "int32", "int16", @@ -376,7 +381,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len) nframes = 10; nerrs = 0; for (i = 0; i < UHCI_NUMFRAMES; ++i) { - __le32 qh_dma; + __hc32 qh_dma; j = 0; td = uhci->frame_cpu[i]; @@ -386,7 +391,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len) if (nframes > 0) { out += sprintf(out, "- Frame %d -> (%08x)\n", - i, le32_to_cpu(link)); + i, hc32_to_cpu(uhci, link)); j = 1; } @@ -395,7 +400,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len) do { td = list_entry(tmp, struct uhci_td, fl_list); tmp = tmp->next; - if (link != LINK_TO_TD(td)) { + if (link != LINK_TO_TD(uhci, td)) { if (nframes > 0) out += sprintf(out, " link does " "not match list entry!\n"); @@ -403,7 +408,7 @@ static int uhci_sprint_schedule(struct uhci_hcd *uhci, char *buf, int len) ++nerrs; } if (nframes > 0) - out += uhci_show_td(td, out, + out += uhci_show_td(uhci, td, out, len - (out - buf), 4); link = td->link; } while (tmp != head); @@ -415,11 +420,12 @@ check_link: if (!j) { out += sprintf(out, "- Frame %d -> (%08x)\n", - i, le32_to_cpu(link)); + i, hc32_to_cpu(uhci, link)); j = 1; } out += sprintf(out, " link does not match " - "QH (%08x)!\n", le32_to_cpu(qh_dma)); + "QH (%08x)!\n", + hc32_to_cpu(uhci, qh_dma)); } else ++nerrs; } @@ -440,11 +446,11 @@ check_link: /* Last QH is the Terminating QH, it's different */ if (i == SKEL_TERM) { - if (qh_element(qh) != LINK_TO_TD(uhci->term_td)) + if (qh_element(qh) != LINK_TO_TD(uhci, uhci->term_td)) out += sprintf(out, " skel_term_qh element is not set to term_td!\n"); link = fsbr_link; if (!link) - link = LINK_TO_QH(uhci->skel_term_qh); + link = LINK_TO_QH(uhci, uhci->skel_term_qh); goto check_qh_link; } @@ -458,20 +464,20 @@ check_link: out += uhci_show_qh(uhci, qh, out, len - (out - buf), 4); if (!fsbr_link && qh->skel >= SKEL_FSBR) - fsbr_link = LINK_TO_QH(qh); + fsbr_link = LINK_TO_QH(uhci, qh); } if ((cnt -= 10) > 0) out += sprintf(out, " Skipped %d QHs\n", cnt); - link = UHCI_PTR_TERM; + link = UHCI_PTR_TERM(uhci); if (i <= SKEL_ISO) ; else if (i < SKEL_ASYNC) - link = LINK_TO_QH(uhci->skel_async_qh); + link = LINK_TO_QH(uhci, uhci->skel_async_qh); else if (!uhci->fsbr_is_on) ; else - link = LINK_TO_QH(uhci->skel_term_qh); + link = LINK_TO_QH(uhci, uhci->skel_term_qh); check_qh_link: if (qh->link != link) out += sprintf(out, " last QH not linked to next skeleton!\n"); diff --git a/drivers/usb/host/uhci-grlib.c b/drivers/usb/host/uhci-grlib.c new file mode 100644 index 0000000..d01c1e2 --- /dev/null +++ b/drivers/usb/host/uhci-grlib.c @@ -0,0 +1,208 @@ +/* + * UHCI HCD (Host Controller Driver) for GRLIB GRUSBHC + * + * Copyright (c) 2011 Jan Andersson <jan@gaisler.com> + * + * This file is based on UHCI PCI HCD: + * (C) Copyright 1999 Linus Torvalds + * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com + * (C) Copyright 1999 Randy Dunlap + * (C) Copyright 1999 Georg Acher, acher@in.tum.de + * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de + * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch + * (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at + * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface + * support from usb-ohci.c by Adam Richter, adam@yggdrasil.com). + * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c) + * (C) Copyright 2004-2007 Alan Stern, stern@rowland.harvard.edu + */ + +#include <linux/of_irq.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> + +static int uhci_grlib_init(struct usb_hcd *hcd) +{ + struct uhci_hcd *uhci = hcd_to_uhci(hcd); + + /* + * Probe to determine the endianness of the controller. + * We know that bit 7 of the PORTSC1 register is always set + * and bit 15 is always clear. If uhci_readw() yields a value + * with bit 7 (0x80) turned on then the current little-endian + * setting is correct. Otherwise we assume the value was + * byte-swapped; hence the register interface and presumably + * also the descriptors are big-endian. + */ + if (!(uhci_readw(uhci, USBPORTSC1) & 0x80)) { + uhci->big_endian_mmio = 1; + uhci->big_endian_desc = 1; + } + + uhci->rh_numports = uhci_count_ports(hcd); + + /* Set up pointers to to generic functions */ + uhci->reset_hc = uhci_generic_reset_hc; + uhci->check_and_reset_hc = uhci_generic_check_and_reset_hc; + /* No special actions need to be taken for the functions below */ + uhci->configure_hc = NULL; + uhci->resume_detect_interrupts_are_broken = NULL; + uhci->global_suspend_mode_is_broken = NULL; + + /* Reset if the controller isn't already safely quiescent. */ + check_and_reset_hc(uhci); + return 0; +} + +static const struct hc_driver uhci_grlib_hc_driver = { + .description = hcd_name, + .product_desc = "GRLIB GRUSBHC UHCI Host Controller", + .hcd_priv_size = sizeof(struct uhci_hcd), + + /* Generic hardware linkage */ + .irq = uhci_irq, + .flags = HCD_MEMORY | HCD_USB11, + + /* Basic lifecycle operations */ + .reset = uhci_grlib_init, + .start = uhci_start, +#ifdef CONFIG_PM + .pci_suspend = NULL, + .pci_resume = NULL, + .bus_suspend = uhci_rh_suspend, + .bus_resume = uhci_rh_resume, +#endif + .stop = uhci_stop, + + .urb_enqueue = uhci_urb_enqueue, + .urb_dequeue = uhci_urb_dequeue, + + .endpoint_disable = uhci_hcd_endpoint_disable, + .get_frame_number = uhci_hcd_get_frame_number, + + .hub_status_data = uhci_hub_status_data, + .hub_control = uhci_hub_control, +}; + + +static int __devinit uhci_hcd_grlib_probe(struct platform_device *op) +{ + struct device_node *dn = op->dev.of_node; + struct usb_hcd *hcd; + struct uhci_hcd *uhci = NULL; + struct resource res; + int irq; + int rv; + + if (usb_disabled()) + return -ENODEV; + + dev_dbg(&op->dev, "initializing GRUSBHC UHCI USB Controller\n"); + + rv = of_address_to_resource(dn, 0, &res); + if (rv) + return rv; + + /* usb_create_hcd requires dma_mask != NULL */ + op->dev.dma_mask = &op->dev.coherent_dma_mask; + hcd = usb_create_hcd(&uhci_grlib_hc_driver, &op->dev, + "GRUSBHC UHCI USB"); + 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)) { + printk(KERN_ERR "%s: request_mem_region failed\n", __FILE__); + rv = -EBUSY; + goto err_rmr; + } + + irq = irq_of_parse_and_map(dn, 0); + if (irq == NO_IRQ) { + printk(KERN_ERR "%s: irq_of_parse_and_map failed\n", __FILE__); + rv = -EBUSY; + goto err_irq; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + printk(KERN_ERR "%s: ioremap failed\n", __FILE__); + rv = -ENOMEM; + goto err_ioremap; + } + + uhci = hcd_to_uhci(hcd); + + uhci->regs = hcd->regs; + + rv = usb_add_hcd(hcd, irq, 0); + if (rv) + goto err_uhci; + + return 0; + +err_uhci: + iounmap(hcd->regs); +err_ioremap: + irq_dispose_mapping(irq); +err_irq: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err_rmr: + usb_put_hcd(hcd); + + return rv; +} + +static int uhci_hcd_grlib_remove(struct platform_device *op) +{ + struct usb_hcd *hcd = dev_get_drvdata(&op->dev); + + dev_set_drvdata(&op->dev, NULL); + + dev_dbg(&op->dev, "stopping GRLIB GRUSBHC UHCI USB Controller\n"); + + usb_remove_hcd(hcd); + + iounmap(hcd->regs); + irq_dispose_mapping(hcd->irq); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + + usb_put_hcd(hcd); + + return 0; +} + +/* Make sure the controller is quiescent and that we're not using it + * any more. This is mainly for the benefit of programs which, like kexec, + * expect the hardware to be idle: not doing DMA or generating IRQs. + * + * This routine may be called in a damaged or failing kernel. Hence we + * do not acquire the spinlock before shutting down the controller. + */ +static void uhci_hcd_grlib_shutdown(struct platform_device *op) +{ + struct usb_hcd *hcd = dev_get_drvdata(&op->dev); + + uhci_hc_died(hcd_to_uhci(hcd)); +} + +static const struct of_device_id uhci_hcd_grlib_of_match[] = { + { .name = "GAISLER_UHCI", }, + { .name = "01_027", }, + {}, +}; +MODULE_DEVICE_TABLE(of, uhci_hcd_grlib_of_match); + + +static struct platform_driver uhci_grlib_driver = { + .probe = uhci_hcd_grlib_probe, + .remove = uhci_hcd_grlib_remove, + .shutdown = uhci_hcd_grlib_shutdown, + .driver = { + .name = "grlib-uhci", + .owner = THIS_MODULE, + .of_match_table = uhci_hcd_grlib_of_match, + }, +}; diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 448b9d1..fba99b1 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -48,7 +48,6 @@ #include <asm/system.h> #include "uhci-hcd.h" -#include "pci-quirks.h" /* * Version Information @@ -94,7 +93,7 @@ static void uhci_get_current_frame_number(struct uhci_hcd *uhci); /* * Calculate the link pointer DMA value for the first Skeleton QH in a frame. */ -static __le32 uhci_frame_skel_link(struct uhci_hcd *uhci, int frame) +static __hc32 uhci_frame_skel_link(struct uhci_hcd *uhci, int frame) { int skelnum; @@ -116,7 +115,7 @@ static __le32 uhci_frame_skel_link(struct uhci_hcd *uhci, int frame) skelnum = 8 - (int) __ffs(frame | UHCI_NUMFRAMES); if (skelnum <= 1) skelnum = 9; - return LINK_TO_QH(uhci->skelqh[skelnum]); + return LINK_TO_QH(uhci, uhci->skelqh[skelnum]); } #include "uhci-debug.c" @@ -135,15 +134,12 @@ static void finish_reset(struct uhci_hcd *uhci) * We have to clear them by hand. */ for (port = 0; port < uhci->rh_numports; ++port) - outw(0, uhci->io_addr + USBPORTSC1 + (port * 2)); + uhci_writew(uhci, 0, USBPORTSC1 + (port * 2)); 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 */ } /* @@ -153,7 +149,7 @@ static void finish_reset(struct uhci_hcd *uhci) static void uhci_hc_died(struct uhci_hcd *uhci) { uhci_get_current_frame_number(uhci); - uhci_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr); + uhci->reset_hc(uhci); finish_reset(uhci); uhci->dead = 1; @@ -168,97 +164,118 @@ static void uhci_hc_died(struct uhci_hcd *uhci) */ static void check_and_reset_hc(struct uhci_hcd *uhci) { - if (uhci_check_and_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr)) + if (uhci->check_and_reset_hc(uhci)) finish_reset(uhci); } +#if defined(CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC) +/* + * The two functions below are generic reset functions that are used on systems + * that do not have keyboard and mouse legacy support. We assume that we are + * running on such a system if CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC is defined. + */ + +/* + * Make sure the controller is completely inactive, unable to + * generate interrupts or do DMA. + */ +static void uhci_generic_reset_hc(struct uhci_hcd *uhci) +{ + /* Reset the HC - this will force us to get a + * new notification of any already connected + * ports due to the virtual disconnect that it + * implies. + */ + uhci_writew(uhci, USBCMD_HCRESET, USBCMD); + mb(); + udelay(5); + if (uhci_readw(uhci, USBCMD) & USBCMD_HCRESET) + dev_warn(uhci_dev(uhci), "HCRESET not completed yet!\n"); + + /* Just to be safe, disable interrupt requests and + * make sure the controller is stopped. + */ + uhci_writew(uhci, 0, USBINTR); + uhci_writew(uhci, 0, USBCMD); +} + +/* + * Initialize a controller that was newly discovered or has just been + * resumed. In either case we can't be sure of its previous state. + * + * Returns: 1 if the controller was reset, 0 otherwise. + */ +static int uhci_generic_check_and_reset_hc(struct uhci_hcd *uhci) +{ + unsigned int cmd, intr; + + /* + * When restarting a suspended controller, we expect all the + * settings to be the same as we left them: + * + * Controller is stopped and configured with EGSM set; + * No interrupts enabled except possibly Resume Detect. + * + * If any of these conditions are violated we do a complete reset. + */ + + cmd = uhci_readw(uhci, USBCMD); + if ((cmd & USBCMD_RS) || !(cmd & USBCMD_CF) || !(cmd & USBCMD_EGSM)) { + dev_dbg(uhci_dev(uhci), "%s: cmd = 0x%04x\n", + __func__, cmd); + goto reset_needed; + } + + intr = uhci_readw(uhci, USBINTR); + if (intr & (~USBINTR_RESUME)) { + dev_dbg(uhci_dev(uhci), "%s: intr = 0x%04x\n", + __func__, intr); + goto reset_needed; + } + return 0; + +reset_needed: + dev_dbg(uhci_dev(uhci), "Performing full reset\n"); + uhci_generic_reset_hc(uhci); + return 1; +} +#endif /* CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC */ + /* * Store the basic register settings needed by the controller. */ static void configure_hc(struct uhci_hcd *uhci) { - struct pci_dev *pdev = to_pci_dev(uhci_dev(uhci)); - /* Set the frame length to the default: 1 ms exactly */ - outb(USBSOF_DEFAULT, uhci->io_addr + USBSOF); + uhci_writeb(uhci, USBSOF_DEFAULT, USBSOF); /* Store the frame list base address */ - outl(uhci->frame_dma_handle, uhci->io_addr + USBFLBASEADD); + uhci_writel(uhci, uhci->frame_dma_handle, USBFLBASEADD); /* Set the current frame number */ - 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); + uhci_writew(uhci, uhci->frame_number & UHCI_MAX_SOF_NUMBER, + USBFRNUM); - /* Disable platform-specific non-PME# wakeup */ - if (pdev->vendor == PCI_VENDOR_ID_INTEL) - pci_write_config_byte(pdev, USBRES_INTEL, 0); + /* perform any arch/bus specific configuration */ + if (uhci->configure_hc) + uhci->configure_hc(uhci); } - static int resume_detect_interrupts_are_broken(struct uhci_hcd *uhci) { - int port; - /* If we have to ignore overcurrent events then almost by definition * we can't depend on resume-detect interrupts. */ if (ignore_oc) return 1; - switch (to_pci_dev(uhci_dev(uhci))->vendor) { - default: - break; - - case PCI_VENDOR_ID_GENESYS: - /* Genesys Logic's GL880S controllers don't generate - * resume-detect interrupts. - */ - return 1; - - case PCI_VENDOR_ID_INTEL: - /* Some of Intel's USB controllers have a bug that causes - * resume-detect interrupts if any port has an over-current - * condition. To make matters worse, some motherboards - * hardwire unused USB ports' over-current inputs active! - * To prevent problems, we will not enable resume-detect - * interrupts if any ports are OC. - */ - for (port = 0; port < uhci->rh_numports; ++port) { - if (inw(uhci->io_addr + USBPORTSC1 + port * 2) & - USBPORTSC_OC) - return 1; - } - break; - } - return 0; + return uhci->resume_detect_interrupts_are_broken ? + uhci->resume_detect_interrupts_are_broken(uhci) : 0; } static int global_suspend_mode_is_broken(struct uhci_hcd *uhci) { - int port; - const char *sys_info; - static char bad_Asus_board[] = "A7V8X"; - - /* One of Asus's motherboards has a bug which causes it to - * wake up immediately from suspend-to-RAM if any of the ports - * are connected. In such cases we will not set EGSM. - */ - sys_info = dmi_get_system_info(DMI_BOARD_NAME); - if (sys_info && !strcmp(sys_info, bad_Asus_board)) { - for (port = 0; port < uhci->rh_numports; ++port) { - if (inw(uhci->io_addr + USBPORTSC1 + port * 2) & - USBPORTSC_CCS) - return 1; - } - } - - return 0; + return uhci->global_suspend_mode_is_broken ? + uhci->global_suspend_mode_is_broken(uhci) : 0; } static void suspend_rh(struct uhci_hcd *uhci, enum uhci_rh_state new_state) @@ -321,8 +338,8 @@ __acquires(uhci->lock) !int_enable) uhci->RD_enable = int_enable = 0; - outw(int_enable, uhci->io_addr + USBINTR); - outw(egsm_enable | USBCMD_CF, uhci->io_addr + USBCMD); + uhci_writew(uhci, int_enable, USBINTR); + uhci_writew(uhci, egsm_enable | USBCMD_CF, USBCMD); mb(); udelay(5); @@ -331,7 +348,7 @@ __acquires(uhci->lock) * controller should stop after a few microseconds. Otherwise * we will give the controller one frame to stop. */ - if (!auto_stop && !(inw(uhci->io_addr + USBSTS) & USBSTS_HCH)) { + if (!auto_stop && !(uhci_readw(uhci, USBSTS) & USBSTS_HCH)) { uhci->rh_state = UHCI_RH_SUSPENDING; spin_unlock_irq(&uhci->lock); msleep(1); @@ -339,7 +356,7 @@ __acquires(uhci->lock) if (uhci->dead) return; } - if (!(inw(uhci->io_addr + USBSTS) & USBSTS_HCH)) + if (!(uhci_readw(uhci, USBSTS) & USBSTS_HCH)) dev_warn(uhci_dev(uhci), "Controller not stopped yet!\n"); uhci_get_current_frame_number(uhci); @@ -361,15 +378,14 @@ __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. * All interrupts are enabled, even though RESUME won't do anything. */ - outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, uhci->io_addr + USBCMD); - outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, - uhci->io_addr + USBINTR); + uhci_writew(uhci, USBCMD_RS | USBCMD_CF | USBCMD_MAXP, USBCMD); + uhci_writew(uhci, USBINTR_TIMEOUT | USBINTR_RESUME | + USBINTR_IOC | USBINTR_SP, USBINTR); mb(); uhci->rh_state = UHCI_RH_RUNNING; set_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags); @@ -392,9 +408,9 @@ __acquires(uhci->lock) unsigned egsm; /* Keep EGSM on if it was set before */ - egsm = inw(uhci->io_addr + USBCMD) & USBCMD_EGSM; + egsm = uhci_readw(uhci, USBCMD) & USBCMD_EGSM; uhci->rh_state = UHCI_RH_RESUMING; - outw(USBCMD_FGR | USBCMD_CF | egsm, uhci->io_addr + USBCMD); + uhci_writew(uhci, USBCMD_FGR | USBCMD_CF | egsm, USBCMD); spin_unlock_irq(&uhci->lock); msleep(20); spin_lock_irq(&uhci->lock); @@ -402,10 +418,10 @@ __acquires(uhci->lock) return; /* End Global Resume and wait for EOP to be sent */ - outw(USBCMD_CF, uhci->io_addr + USBCMD); + uhci_writew(uhci, USBCMD_CF, USBCMD); mb(); udelay(4); - if (inw(uhci->io_addr + USBCMD) & USBCMD_FGR) + if (uhci_readw(uhci, USBCMD) & USBCMD_FGR) dev_warn(uhci_dev(uhci), "FGR not stopped yet!\n"); } @@ -425,10 +441,10 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd) * interrupt cause. Contrary to the UHCI specification, the * "HC Halted" status bit is persistent: it is RO, not R/WC. */ - status = inw(uhci->io_addr + USBSTS); + status = uhci_readw(uhci, USBSTS); if (!(status & ~USBSTS_HCH)) /* shared interrupt, not mine */ return IRQ_NONE; - outw(status, uhci->io_addr + USBSTS); /* Clear it */ + uhci_writew(uhci, status, USBSTS); /* Clear it */ if (status & ~(USBSTS_USBINT | USBSTS_ERROR | USBSTS_RD)) { if (status & USBSTS_HSE) @@ -450,6 +466,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 */ @@ -483,7 +500,7 @@ static void uhci_get_current_frame_number(struct uhci_hcd *uhci) if (!uhci->is_stopped) { unsigned delta; - delta = (inw(uhci->io_addr + USBFRNUM) - uhci->frame_number) & + delta = (uhci_readw(uhci, USBFRNUM) - uhci->frame_number) & (UHCI_NUMFRAMES - 1); uhci->frame_number += delta; } @@ -520,61 +537,6 @@ static void release_uhci(struct uhci_hcd *uhci) uhci->frame, uhci->frame_dma_handle); } -static int uhci_init(struct usb_hcd *hcd) -{ - struct uhci_hcd *uhci = hcd_to_uhci(hcd); - unsigned io_size = (unsigned) hcd->rsrc_len; - int port; - - uhci->io_addr = (unsigned long) hcd->rsrc_start; - - /* The UHCI spec says devices must have 2 ports, and goes on to say - * they may have more but gives no way to determine how many there - * are. However according to the UHCI spec, Bit 7 of the port - * status and control register is always set to 1. So we try to - * use this to our advantage. Another common failure mode when - * a nonexistent register is addressed is to return all ones, so - * we test for that also. - */ - for (port = 0; port < (io_size - USBPORTSC1) / 2; port++) { - unsigned int portstatus; - - portstatus = inw(uhci->io_addr + USBPORTSC1 + (port * 2)); - if (!(portstatus & 0x0080) || portstatus == 0xffff) - break; - } - if (debug) - dev_info(uhci_dev(uhci), "detected %d ports\n", port); - - /* Anything greater than 7 is weird so we'll ignore it. */ - if (port > UHCI_RH_MAXCHILD) { - dev_info(uhci_dev(uhci), "port count misdetected? " - "forcing to 2 ports\n"); - port = 2; - } - uhci->rh_numports = port; - - /* Kick BIOS off this hardware and reset if the controller - * isn't already safely quiescent. - */ - check_and_reset_hc(uhci); - return 0; -} - -/* Make sure the controller is quiescent and that we're not using it - * any more. This is mainly for the benefit of programs which, like kexec, - * expect the hardware to be idle: not doing DMA or generating IRQs. - * - * This routine may be called in a damaged or failing kernel. Hence we - * do not acquire the spinlock before shutting down the controller. - */ -static void uhci_shutdown(struct pci_dev *pdev) -{ - struct usb_hcd *hcd = pci_get_drvdata(pdev); - - uhci_hc_died(hcd_to_uhci(hcd)); -} - /* * Allocate a frame list, and then setup the skeleton * @@ -669,16 +631,16 @@ static int uhci_start(struct usb_hcd *hcd) * 8 Interrupt queues; link all higher int queues to int1 = async */ for (i = SKEL_ISO + 1; i < SKEL_ASYNC; ++i) - uhci->skelqh[i]->link = LINK_TO_QH(uhci->skel_async_qh); - uhci->skel_async_qh->link = UHCI_PTR_TERM; - uhci->skel_term_qh->link = LINK_TO_QH(uhci->skel_term_qh); + uhci->skelqh[i]->link = LINK_TO_QH(uhci, uhci->skel_async_qh); + uhci->skel_async_qh->link = UHCI_PTR_TERM(uhci); + uhci->skel_term_qh->link = LINK_TO_QH(uhci, uhci->skel_term_qh); /* This dummy TD is to work around a bug in Intel PIIX controllers */ - uhci_fill_td(uhci->term_td, 0, uhci_explen(0) | + uhci_fill_td(uhci, uhci->term_td, 0, uhci_explen(0) | (0x7f << TD_TOKEN_DEVADDR_SHIFT) | USB_PID_IN, 0); - uhci->term_td->link = UHCI_PTR_TERM; + uhci->term_td->link = UHCI_PTR_TERM(uhci); uhci->skel_async_qh->element = uhci->skel_term_qh->element = - LINK_TO_TD(uhci->term_td); + LINK_TO_TD(uhci, uhci->term_td); /* * Fill the frame list: make all entries point to the proper @@ -791,86 +753,6 @@ static int uhci_rh_resume(struct usb_hcd *hcd) return rc; } -static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) -{ - struct uhci_hcd *uhci = hcd_to_uhci(hcd); - struct pci_dev *pdev = to_pci_dev(uhci_dev(uhci)); - int rc = 0; - - dev_dbg(uhci_dev(uhci), "%s\n", __func__); - - spin_lock_irq(&uhci->lock); - if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead) - goto done_okay; /* Already suspended or dead */ - - if (uhci->rh_state > UHCI_RH_SUSPENDED) { - dev_warn(uhci_dev(uhci), "Root hub isn't suspended!\n"); - rc = -EBUSY; - goto done; - }; - - /* All PCI host controllers are required to disable IRQ generation - * at the source, so we must turn off PIRQ. - */ - pci_write_config_word(pdev, USBLEGSUP, 0); - clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); - - /* Enable platform-specific non-PME# wakeup */ - if (do_wakeup) { - if (pdev->vendor == PCI_VENDOR_ID_INTEL) - pci_write_config_byte(pdev, USBRES_INTEL, - USBPORT1EN | USBPORT2EN); - } - -done_okay: - clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); -done: - spin_unlock_irq(&uhci->lock); - return rc; -} - -static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated) -{ - struct uhci_hcd *uhci = hcd_to_uhci(hcd); - - dev_dbg(uhci_dev(uhci), "%s\n", __func__); - - /* Since we aren't in D3 any more, it's safe to set this flag - * even if the controller was dead. - */ - set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); - - spin_lock_irq(&uhci->lock); - - /* Make sure resume from hibernation re-enumerates everything */ - if (hibernated) - uhci_hc_died(uhci); - - /* The firmware or a boot kernel 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 */ - configure_hc(uhci); - - /* Tell the core if the controller had to be reset */ - if (uhci->rh_state == UHCI_RH_RESET) - usb_root_hub_lost_power(hcd->self.root_hub); - - spin_unlock_irq(&uhci->lock); - - /* If interrupts don't work and remote wakeup is enabled then - * the suspended root hub needs to be polled. - */ - if (!uhci->RD_enable && hcd->self.root_hub->do_remote_wakeup) - set_bit(HCD_FLAG_POLL_RH, &hcd->flags); - - /* Does the root hub have a port wakeup pending? */ - usb_hcd_poll_rh_status(hcd); - return 0; -} #endif /* Wait until a particular device/endpoint's QH is idle, and free it */ @@ -908,67 +790,62 @@ static int uhci_hcd_get_frame_number(struct usb_hcd *hcd) /* Minimize latency by avoiding the spinlock */ frame_number = uhci->frame_number; barrier(); - delta = (inw(uhci->io_addr + USBFRNUM) - frame_number) & + delta = (uhci_readw(uhci, USBFRNUM) - frame_number) & (UHCI_NUMFRAMES - 1); return frame_number + delta; } -static const char hcd_name[] = "uhci_hcd"; - -static const struct hc_driver uhci_driver = { - .description = hcd_name, - .product_desc = "UHCI Host Controller", - .hcd_priv_size = sizeof(struct uhci_hcd), - - /* Generic hardware linkage */ - .irq = uhci_irq, - .flags = HCD_USB11, - - /* Basic lifecycle operations */ - .reset = uhci_init, - .start = uhci_start, -#ifdef CONFIG_PM - .pci_suspend = uhci_pci_suspend, - .pci_resume = uhci_pci_resume, - .bus_suspend = uhci_rh_suspend, - .bus_resume = uhci_rh_resume, -#endif - .stop = uhci_stop, +/* Determines number of ports on controller */ +static int uhci_count_ports(struct usb_hcd *hcd) +{ + struct uhci_hcd *uhci = hcd_to_uhci(hcd); + unsigned io_size = (unsigned) hcd->rsrc_len; + int port; - .urb_enqueue = uhci_urb_enqueue, - .urb_dequeue = uhci_urb_dequeue, + /* The UHCI spec says devices must have 2 ports, and goes on to say + * they may have more but gives no way to determine how many there + * are. However according to the UHCI spec, Bit 7 of the port + * status and control register is always set to 1. So we try to + * use this to our advantage. Another common failure mode when + * a nonexistent register is addressed is to return all ones, so + * we test for that also. + */ + for (port = 0; port < (io_size - USBPORTSC1) / 2; port++) { + unsigned int portstatus; - .endpoint_disable = uhci_hcd_endpoint_disable, - .get_frame_number = uhci_hcd_get_frame_number, + portstatus = uhci_readw(uhci, USBPORTSC1 + (port * 2)); + if (!(portstatus & 0x0080) || portstatus == 0xffff) + break; + } + if (debug) + dev_info(uhci_dev(uhci), "detected %d ports\n", port); - .hub_status_data = uhci_hub_status_data, - .hub_control = uhci_hub_control, -}; + /* Anything greater than 7 is weird so we'll ignore it. */ + if (port > UHCI_RH_MAXCHILD) { + dev_info(uhci_dev(uhci), "port count misdetected? " + "forcing to 2 ports\n"); + port = 2; + } -static const struct pci_device_id uhci_pci_ids[] = { { - /* handle any USB UHCI controller */ - PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_UHCI, ~0), - .driver_data = (unsigned long) &uhci_driver, - }, { /* end: all zeroes */ } -}; + return port; +} -MODULE_DEVICE_TABLE(pci, uhci_pci_ids); +static const char hcd_name[] = "uhci_hcd"; -static struct pci_driver uhci_pci_driver = { - .name = (char *)hcd_name, - .id_table = uhci_pci_ids, +#ifdef CONFIG_PCI +#include "uhci-pci.c" +#define PCI_DRIVER uhci_pci_driver +#endif - .probe = usb_hcd_pci_probe, - .remove = usb_hcd_pci_remove, - .shutdown = uhci_shutdown, +#ifdef CONFIG_SPARC_LEON +#include "uhci-grlib.c" +#define PLATFORM_DRIVER uhci_grlib_driver +#endif -#ifdef CONFIG_PM_SLEEP - .driver = { - .pm = &usb_hcd_pci_pm_ops - }, +#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) +#error "missing bus glue for uhci-hcd" #endif -}; - + static int __init uhci_hcd_init(void) { int retval = -ENOMEM; @@ -994,13 +871,27 @@ static int __init uhci_hcd_init(void) if (!uhci_up_cachep) goto up_failed; - retval = pci_register_driver(&uhci_pci_driver); - if (retval) - goto init_failed; +#ifdef PLATFORM_DRIVER + retval = platform_driver_register(&PLATFORM_DRIVER); + if (retval < 0) + goto clean0; +#endif + +#ifdef PCI_DRIVER + retval = pci_register_driver(&PCI_DRIVER); + if (retval < 0) + goto clean1; +#endif return 0; -init_failed: +#ifdef PCI_DRIVER +clean1: +#endif +#ifdef PLATFORM_DRIVER + platform_driver_unregister(&PLATFORM_DRIVER); +clean0: +#endif kmem_cache_destroy(uhci_up_cachep); up_failed: @@ -1017,7 +908,12 @@ errbuf_failed: static void __exit uhci_hcd_cleanup(void) { - pci_unregister_driver(&uhci_pci_driver); +#ifdef PLATFORM_DRIVER + platform_driver_unregister(&PLATFORM_DRIVER); +#endif +#ifdef PCI_DRIVER + pci_unregister_driver(&PCI_DRIVER); +#endif kmem_cache_destroy(uhci_up_cachep); debugfs_remove(uhci_debugfs_root); kfree(errbuf); diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index 49bf279..7af2b70 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -78,11 +78,11 @@ #define USBPORT1EN 0x01 #define USBPORT2EN 0x02 -#define UHCI_PTR_BITS cpu_to_le32(0x000F) -#define UHCI_PTR_TERM cpu_to_le32(0x0001) -#define UHCI_PTR_QH cpu_to_le32(0x0002) -#define UHCI_PTR_DEPTH cpu_to_le32(0x0004) -#define UHCI_PTR_BREADTH cpu_to_le32(0x0000) +#define UHCI_PTR_BITS(uhci) cpu_to_hc32((uhci), 0x000F) +#define UHCI_PTR_TERM(uhci) cpu_to_hc32((uhci), 0x0001) +#define UHCI_PTR_QH(uhci) cpu_to_hc32((uhci), 0x0002) +#define UHCI_PTR_DEPTH(uhci) cpu_to_hc32((uhci), 0x0004) +#define UHCI_PTR_BREADTH(uhci) cpu_to_hc32((uhci), 0x0000) #define UHCI_NUMFRAMES 1024 /* in the frame list [array] */ #define UHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */ @@ -99,6 +99,22 @@ /* + * __hc32 and __hc16 are "Host Controller" types, they may be equivalent to + * __leXX (normally) or __beXX (given UHCI_BIG_ENDIAN_DESC), depending on + * the host controller implementation. + * + * To facilitate the strongest possible byte-order checking from "sparse" + * and so on, we use __leXX unless that's not practical. + */ +#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_DESC +typedef __u32 __bitwise __hc32; +typedef __u16 __bitwise __hc16; +#else +#define __hc32 __le32 +#define __hc16 __le16 +#endif + +/* * Queue Headers */ @@ -130,8 +146,8 @@ struct uhci_qh { /* Hardware fields */ - __le32 link; /* Next QH in the schedule */ - __le32 element; /* Queue element (TD) pointer */ + __hc32 link; /* Next QH in the schedule */ + __hc32 element; /* Queue element (TD) pointer */ /* Software fields */ dma_addr_t dma_handle; @@ -168,14 +184,10 @@ struct uhci_qh { * We need a special accessor for the element pointer because it is * subject to asynchronous updates by the controller. */ -static inline __le32 qh_element(struct uhci_qh *qh) { - __le32 element = qh->element; +#define qh_element(qh) ACCESS_ONCE((qh)->element) - barrier(); - return element; -} - -#define LINK_TO_QH(qh) (UHCI_PTR_QH | cpu_to_le32((qh)->dma_handle)) +#define LINK_TO_QH(uhci, qh) (UHCI_PTR_QH((uhci)) | \ + cpu_to_hc32((uhci), (qh)->dma_handle)) /* @@ -212,7 +224,7 @@ static inline __le32 qh_element(struct uhci_qh *qh) { /* * for TD <info>: (a.k.a. Token) */ -#define td_token(td) le32_to_cpu((td)->token) +#define td_token(uhci, td) hc32_to_cpu((uhci), (td)->token) #define TD_TOKEN_DEVADDR_SHIFT 8 #define TD_TOKEN_TOGGLE_SHIFT 19 #define TD_TOKEN_TOGGLE (1 << 19) @@ -245,10 +257,10 @@ static inline __le32 qh_element(struct uhci_qh *qh) { */ struct uhci_td { /* Hardware fields */ - __le32 link; - __le32 status; - __le32 token; - __le32 buffer; + __hc32 link; + __hc32 status; + __hc32 token; + __hc32 buffer; /* Software fields */ dma_addr_t dma_handle; @@ -263,14 +275,10 @@ struct uhci_td { * We need a special accessor for the control/status word because it is * subject to asynchronous updates by the controller. */ -static inline u32 td_status(struct uhci_td *td) { - __le32 status = td->status; +#define td_status(uhci, td) hc32_to_cpu((uhci), \ + ACCESS_ONCE((td)->status)) - barrier(); - return le32_to_cpu(status); -} - -#define LINK_TO_TD(td) (cpu_to_le32((td)->dma_handle)) +#define LINK_TO_TD(uhci, td) (cpu_to_hc32((uhci), (td)->dma_handle)) /* @@ -380,6 +388,9 @@ struct uhci_hcd { /* Grabbed from PCI */ unsigned long io_addr; + /* Used when registers are memory mapped */ + void __iomem *regs; + struct dma_pool *qh_pool; struct dma_pool *td_pool; @@ -390,7 +401,7 @@ struct uhci_hcd { spinlock_t lock; dma_addr_t frame_dma_handle; /* Hardware frame list */ - __le32 *frame; + __hc32 *frame; void **frame_cpu; /* CPU's frame list */ enum uhci_rh_state rh_state; @@ -415,6 +426,12 @@ struct uhci_hcd { struct timer_list fsbr_timer; /* For turning off FBSR */ + /* Silicon quirks */ + unsigned int oc_low:1; /* OverCurrent bit active low */ + unsigned int wait_for_hp:1; /* Wait for HP port reset */ + unsigned int big_endian_mmio:1; /* Big endian registers */ + unsigned int big_endian_desc:1; /* Big endian descriptors */ + /* Support for port suspend/resume/reset */ unsigned long port_c_suspend; /* Bit-arrays of ports */ unsigned long resuming_ports; @@ -429,6 +446,16 @@ struct uhci_hcd { int total_load; /* Sum of array values */ short load[MAX_PHASE]; /* Periodic allocations */ + + /* Reset host controller */ + void (*reset_hc) (struct uhci_hcd *uhci); + int (*check_and_reset_hc) (struct uhci_hcd *uhci); + /* configure_hc should perform arch specific settings, if needed */ + void (*configure_hc) (struct uhci_hcd *uhci); + /* Check for broken resume detect interrupts */ + int (*resume_detect_interrupts_are_broken) (struct uhci_hcd *uhci); + /* Check for broken global suspend */ + int (*global_suspend_mode_is_broken) (struct uhci_hcd *uhci); }; /* Convert between a usb_hcd pointer and the corresponding uhci_hcd */ @@ -467,4 +494,171 @@ struct urb_priv { #define PCI_VENDOR_ID_GENESYS 0x17a0 #define PCI_DEVICE_ID_GL880S_UHCI 0x8083 +/* + * Functions used to access controller registers. The UCHI spec says that host + * controller I/O registers are mapped into PCI I/O space. For non-PCI hosts + * we use memory mapped registers. + */ + +#ifndef CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC +/* Support PCI only */ +static inline u32 uhci_readl(const struct uhci_hcd *uhci, int reg) +{ + return inl(uhci->io_addr + reg); +} + +static inline void uhci_writel(const struct uhci_hcd *uhci, u32 val, int reg) +{ + outl(val, uhci->io_addr + reg); +} + +static inline u16 uhci_readw(const struct uhci_hcd *uhci, int reg) +{ + return inw(uhci->io_addr + reg); +} + +static inline void uhci_writew(const struct uhci_hcd *uhci, u16 val, int reg) +{ + outw(val, uhci->io_addr + reg); +} + +static inline u8 uhci_readb(const struct uhci_hcd *uhci, int reg) +{ + return inb(uhci->io_addr + reg); +} + +static inline void uhci_writeb(const struct uhci_hcd *uhci, u8 val, int reg) +{ + outb(val, uhci->io_addr + reg); +} + +#else +/* Support non-PCI host controllers */ +#ifdef CONFIG_PCI +/* Support PCI and non-PCI host controllers */ +#define uhci_has_pci_registers(u) ((u)->io_addr != 0) +#else +/* Support non-PCI host controllers only */ +#define uhci_has_pci_registers(u) 0 +#endif + +#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO +/* Support (non-PCI) big endian host controllers */ +#define uhci_big_endian_mmio(u) ((u)->big_endian_mmio) +#else +#define uhci_big_endian_mmio(u) 0 +#endif + +static inline u32 uhci_readl(const struct uhci_hcd *uhci, int reg) +{ + if (uhci_has_pci_registers(uhci)) + return inl(uhci->io_addr + reg); +#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO + else if (uhci_big_endian_mmio(uhci)) + return readl_be(uhci->regs + reg); +#endif + else + return readl(uhci->regs + reg); +} + +static inline void uhci_writel(const struct uhci_hcd *uhci, u32 val, int reg) +{ + if (uhci_has_pci_registers(uhci)) + outl(val, uhci->io_addr + reg); +#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO + else if (uhci_big_endian_mmio(uhci)) + writel_be(val, uhci->regs + reg); +#endif + else + writel(val, uhci->regs + reg); +} + +static inline u16 uhci_readw(const struct uhci_hcd *uhci, int reg) +{ + if (uhci_has_pci_registers(uhci)) + return inw(uhci->io_addr + reg); +#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO + else if (uhci_big_endian_mmio(uhci)) + return readw_be(uhci->regs + reg); +#endif + else + return readw(uhci->regs + reg); +} + +static inline void uhci_writew(const struct uhci_hcd *uhci, u16 val, int reg) +{ + if (uhci_has_pci_registers(uhci)) + outw(val, uhci->io_addr + reg); +#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO + else if (uhci_big_endian_mmio(uhci)) + writew_be(val, uhci->regs + reg); +#endif + else + writew(val, uhci->regs + reg); +} + +static inline u8 uhci_readb(const struct uhci_hcd *uhci, int reg) +{ + if (uhci_has_pci_registers(uhci)) + return inb(uhci->io_addr + reg); +#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO + else if (uhci_big_endian_mmio(uhci)) + return readb_be(uhci->regs + reg); +#endif + else + return readb(uhci->regs + reg); +} + +static inline void uhci_writeb(const struct uhci_hcd *uhci, u8 val, int reg) +{ + if (uhci_has_pci_registers(uhci)) + outb(val, uhci->io_addr + reg); +#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_MMIO + else if (uhci_big_endian_mmio(uhci)) + writeb_be(val, uhci->regs + reg); +#endif + else + writeb(val, uhci->regs + reg); +} +#endif /* CONFIG_USB_UHCI_SUPPORT_NON_PCI_HC */ + +/* + * The GRLIB GRUSBHC controller can use big endian format for its descriptors. + * + * UHCI controllers accessed through PCI work normally (little-endian + * everywhere), so we don't bother supporting a BE-only mode. + */ +#ifdef CONFIG_USB_UHCI_BIG_ENDIAN_DESC +#define uhci_big_endian_desc(u) ((u)->big_endian_desc) + +/* cpu to uhci */ +static inline __hc32 cpu_to_hc32(const struct uhci_hcd *uhci, const u32 x) +{ + return uhci_big_endian_desc(uhci) + ? (__force __hc32)cpu_to_be32(x) + : (__force __hc32)cpu_to_le32(x); +} + +/* uhci to cpu */ +static inline u32 hc32_to_cpu(const struct uhci_hcd *uhci, const __hc32 x) +{ + return uhci_big_endian_desc(uhci) + ? be32_to_cpu((__force __be32)x) + : le32_to_cpu((__force __le32)x); +} + +#else +/* cpu to uhci */ +static inline __hc32 cpu_to_hc32(const struct uhci_hcd *uhci, const u32 x) +{ + return cpu_to_le32(x); +} + +/* uhci to cpu */ +static inline u32 hc32_to_cpu(const struct uhci_hcd *uhci, const __hc32 x) +{ + return le32_to_cpu(x); +} +#endif + #endif diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c index 6d59c0f..045cde4 100644 --- a/drivers/usb/host/uhci-hub.c +++ b/drivers/usb/host/uhci-hub.c @@ -44,7 +44,7 @@ static int any_ports_active(struct uhci_hcd *uhci) int port; for (port = 0; port < uhci->rh_numports; ++port) { - if ((inw(uhci->io_addr + USBPORTSC1 + port * 2) & + if ((uhci_readw(uhci, USBPORTSC1 + port * 2) & (USBPORTSC_CCS | RWC_BITS)) || test_bit(port, &uhci->port_c_suspend)) return 1; @@ -68,7 +68,7 @@ static inline int get_hub_status_data(struct uhci_hcd *uhci, char *buf) *buf = 0; for (port = 0; port < uhci->rh_numports; ++port) { - if ((inw(uhci->io_addr + USBPORTSC1 + port * 2) & mask) || + if ((uhci_readw(uhci, USBPORTSC1 + port * 2) & mask) || test_bit(port, &uhci->port_c_suspend)) *buf |= (1 << (port + 1)); } @@ -78,17 +78,17 @@ static inline int get_hub_status_data(struct uhci_hcd *uhci, char *buf) #define OK(x) len = (x); break #define CLR_RH_PORTSTAT(x) \ - status = inw(port_addr); \ + status = uhci_readw(uhci, port_addr); \ status &= ~(RWC_BITS|WZ_BITS); \ status &= ~(x); \ status |= RWC_BITS & (x); \ - outw(status, port_addr) + uhci_writew(uhci, status, port_addr) #define SET_RH_PORTSTAT(x) \ - status = inw(port_addr); \ + status = uhci_readw(uhci, port_addr); \ status |= (x); \ status &= ~(RWC_BITS|WZ_BITS); \ - outw(status, port_addr) + uhci_writew(uhci, status, port_addr) /* UHCI controllers don't automatically stop resume signalling after 20 msec, * so we have to poll and check timeouts in order to take care of it. @@ -99,7 +99,7 @@ static void uhci_finish_suspend(struct uhci_hcd *uhci, int port, int status; int i; - if (inw(port_addr) & SUSPEND_BITS) { + if (uhci_readw(uhci, port_addr) & SUSPEND_BITS) { CLR_RH_PORTSTAT(SUSPEND_BITS); if (test_bit(port, &uhci->resuming_ports)) set_bit(port, &uhci->port_c_suspend); @@ -110,7 +110,7 @@ static void uhci_finish_suspend(struct uhci_hcd *uhci, int port, * Experiments show that some controllers take longer, so * we'll poll for completion. */ for (i = 0; i < 10; ++i) { - if (!(inw(port_addr) & SUSPEND_BITS)) + if (!(uhci_readw(uhci, port_addr) & SUSPEND_BITS)) break; udelay(1); } @@ -121,12 +121,12 @@ static void uhci_finish_suspend(struct uhci_hcd *uhci, int port, /* Wait for the UHCI controller in HP's iLO2 server management chip. * It can take up to 250 us to finish a reset and set the CSC bit. */ -static void wait_for_HP(unsigned long port_addr) +static void wait_for_HP(struct uhci_hcd *uhci, unsigned long port_addr) { int i; for (i = 10; i < 250; i += 10) { - if (inw(port_addr) & USBPORTSC_CSC) + if (uhci_readw(uhci, port_addr) & USBPORTSC_CSC) return; udelay(10); } @@ -140,8 +140,8 @@ static void uhci_check_ports(struct uhci_hcd *uhci) int status; for (port = 0; port < uhci->rh_numports; ++port) { - port_addr = uhci->io_addr + USBPORTSC1 + 2 * port; - status = inw(port_addr); + port_addr = USBPORTSC1 + 2 * port; + status = uhci_readw(uhci, port_addr); if (unlikely(status & USBPORTSC_PR)) { if (time_after_eq(jiffies, uhci->ports_timeout)) { CLR_RH_PORTSTAT(USBPORTSC_PR); @@ -149,9 +149,8 @@ static void uhci_check_ports(struct uhci_hcd *uhci) /* HP's server management chip requires * a longer delay. */ - if (to_pci_dev(uhci_dev(uhci))->vendor == - PCI_VENDOR_ID_HP) - wait_for_HP(port_addr); + if (uhci->wait_for_hp) + wait_for_HP(uhci, port_addr); /* If the port was enabled before, turning * reset on caused a port enable change. @@ -242,7 +241,7 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, struct uhci_hcd *uhci = hcd_to_uhci(hcd); int status, lstatus, retval = 0, len = 0; unsigned int port = wIndex - 1; - unsigned long port_addr = uhci->io_addr + USBPORTSC1 + 2 * port; + unsigned long port_addr = USBPORTSC1 + 2 * port; u16 wPortChange, wPortStatus; unsigned long flags; @@ -260,14 +259,13 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, goto err; uhci_check_ports(uhci); - status = inw(port_addr); + status = uhci_readw(uhci, port_addr); /* Intel controllers report the OverCurrent bit active on. * VIA controllers report it active off, so we'll adjust the * bit value. (It's not standardized in the UHCI spec.) */ - if (to_pci_dev(hcd->self.controller)->vendor == - PCI_VENDOR_ID_VIA) + if (uhci->oc_low) status ^= USBPORTSC_OC; /* UHCI doesn't support C_RESET (always false) */ @@ -358,7 +356,7 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, CLR_RH_PORTSTAT(USBPORTSC_PEC); OK(0); case USB_PORT_FEAT_SUSPEND: - if (!(inw(port_addr) & USBPORTSC_SUSP)) { + if (!(uhci_readw(uhci, port_addr) & USBPORTSC_SUSP)) { /* Make certain the port isn't suspended */ uhci_finish_suspend(uhci, port, port_addr); @@ -370,7 +368,8 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, * if the port is disabled. When this happens * just skip the Resume signalling. */ - if (!(inw(port_addr) & USBPORTSC_RD)) + if (!(uhci_readw(uhci, port_addr) & + USBPORTSC_RD)) uhci_finish_suspend(uhci, port, port_addr); else diff --git a/drivers/usb/host/uhci-pci.c b/drivers/usb/host/uhci-pci.c new file mode 100644 index 0000000..c300bd2f7 --- /dev/null +++ b/drivers/usb/host/uhci-pci.c @@ -0,0 +1,301 @@ +/* + * UHCI HCD (Host Controller Driver) PCI Bus Glue. + * + * Extracted from uhci-hcd.c: + * Maintainer: Alan Stern <stern@rowland.harvard.edu> + * + * (C) Copyright 1999 Linus Torvalds + * (C) Copyright 1999-2002 Johannes Erdfelt, johannes@erdfelt.com + * (C) Copyright 1999 Randy Dunlap + * (C) Copyright 1999 Georg Acher, acher@in.tum.de + * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de + * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch + * (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at + * (C) Copyright 2000 Yggdrasil Computing, Inc. (port of new PCI interface + * support from usb-ohci.c by Adam Richter, adam@yggdrasil.com). + * (C) Copyright 1999 Gregory P. Smith (from usb-ohci.c) + * (C) Copyright 2004-2007 Alan Stern, stern@rowland.harvard.edu + */ + +#include "pci-quirks.h" + +/* + * Make sure the controller is completely inactive, unable to + * generate interrupts or do DMA. + */ +static void uhci_pci_reset_hc(struct uhci_hcd *uhci) +{ + uhci_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr); +} + +/* + * Initialize a controller that was newly discovered or has just been + * resumed. In either case we can't be sure of its previous state. + * + * Returns: 1 if the controller was reset, 0 otherwise. + */ +static int uhci_pci_check_and_reset_hc(struct uhci_hcd *uhci) +{ + return uhci_check_and_reset_hc(to_pci_dev(uhci_dev(uhci)), + uhci->io_addr); +} + +/* + * Store the basic register settings needed by the controller. + * This function is called at the end of configure_hc in uhci-hcd.c. + */ +static void uhci_pci_configure_hc(struct uhci_hcd *uhci) +{ + struct pci_dev *pdev = to_pci_dev(uhci_dev(uhci)); + + /* Enable PIRQ */ + pci_write_config_word(pdev, USBLEGSUP, USBLEGSUP_DEFAULT); + + /* Disable platform-specific non-PME# wakeup */ + if (pdev->vendor == PCI_VENDOR_ID_INTEL) + pci_write_config_byte(pdev, USBRES_INTEL, 0); +} + +static int uhci_pci_resume_detect_interrupts_are_broken(struct uhci_hcd *uhci) +{ + int port; + + switch (to_pci_dev(uhci_dev(uhci))->vendor) { + default: + break; + + case PCI_VENDOR_ID_GENESYS: + /* Genesys Logic's GL880S controllers don't generate + * resume-detect interrupts. + */ + return 1; + + case PCI_VENDOR_ID_INTEL: + /* Some of Intel's USB controllers have a bug that causes + * resume-detect interrupts if any port has an over-current + * condition. To make matters worse, some motherboards + * hardwire unused USB ports' over-current inputs active! + * To prevent problems, we will not enable resume-detect + * interrupts if any ports are OC. + */ + for (port = 0; port < uhci->rh_numports; ++port) { + if (inw(uhci->io_addr + USBPORTSC1 + port * 2) & + USBPORTSC_OC) + return 1; + } + break; + } + return 0; +} + +static int uhci_pci_global_suspend_mode_is_broken(struct uhci_hcd *uhci) +{ + int port; + const char *sys_info; + static const char bad_Asus_board[] = "A7V8X"; + + /* One of Asus's motherboards has a bug which causes it to + * wake up immediately from suspend-to-RAM if any of the ports + * are connected. In such cases we will not set EGSM. + */ + sys_info = dmi_get_system_info(DMI_BOARD_NAME); + if (sys_info && !strcmp(sys_info, bad_Asus_board)) { + for (port = 0; port < uhci->rh_numports; ++port) { + if (inw(uhci->io_addr + USBPORTSC1 + port * 2) & + USBPORTSC_CCS) + return 1; + } + } + + return 0; +} + +static int uhci_pci_init(struct usb_hcd *hcd) +{ + struct uhci_hcd *uhci = hcd_to_uhci(hcd); + + uhci->io_addr = (unsigned long) hcd->rsrc_start; + + uhci->rh_numports = uhci_count_ports(hcd); + + /* Intel controllers report the OverCurrent bit active on. + * VIA controllers report it active off, so we'll adjust the + * bit value. (It's not standardized in the UHCI spec.) + */ + if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_VIA) + uhci->oc_low = 1; + + /* HP's server management chip requires a longer port reset delay. */ + if (to_pci_dev(uhci_dev(uhci))->vendor == PCI_VENDOR_ID_HP) + uhci->wait_for_hp = 1; + + /* Set up pointers to PCI-specific functions */ + uhci->reset_hc = uhci_pci_reset_hc; + uhci->check_and_reset_hc = uhci_pci_check_and_reset_hc; + uhci->configure_hc = uhci_pci_configure_hc; + uhci->resume_detect_interrupts_are_broken = + uhci_pci_resume_detect_interrupts_are_broken; + uhci->global_suspend_mode_is_broken = + uhci_pci_global_suspend_mode_is_broken; + + + /* Kick BIOS off this hardware and reset if the controller + * isn't already safely quiescent. + */ + check_and_reset_hc(uhci); + return 0; +} + +/* Make sure the controller is quiescent and that we're not using it + * any more. This is mainly for the benefit of programs which, like kexec, + * expect the hardware to be idle: not doing DMA or generating IRQs. + * + * This routine may be called in a damaged or failing kernel. Hence we + * do not acquire the spinlock before shutting down the controller. + */ +static void uhci_shutdown(struct pci_dev *pdev) +{ + struct usb_hcd *hcd = pci_get_drvdata(pdev); + + uhci_hc_died(hcd_to_uhci(hcd)); +} + +#ifdef CONFIG_PM + +static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup) +{ + struct uhci_hcd *uhci = hcd_to_uhci(hcd); + struct pci_dev *pdev = to_pci_dev(uhci_dev(uhci)); + int rc = 0; + + dev_dbg(uhci_dev(uhci), "%s\n", __func__); + + spin_lock_irq(&uhci->lock); + if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead) + goto done_okay; /* Already suspended or dead */ + + if (uhci->rh_state > UHCI_RH_SUSPENDED) { + dev_warn(uhci_dev(uhci), "Root hub isn't suspended!\n"); + rc = -EBUSY; + goto done; + }; + + /* All PCI host controllers are required to disable IRQ generation + * at the source, so we must turn off PIRQ. + */ + pci_write_config_word(pdev, USBLEGSUP, 0); + clear_bit(HCD_FLAG_POLL_RH, &hcd->flags); + + /* Enable platform-specific non-PME# wakeup */ + if (do_wakeup) { + if (pdev->vendor == PCI_VENDOR_ID_INTEL) + pci_write_config_byte(pdev, USBRES_INTEL, + USBPORT1EN | USBPORT2EN); + } + +done_okay: + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); +done: + spin_unlock_irq(&uhci->lock); + return rc; +} + +static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated) +{ + struct uhci_hcd *uhci = hcd_to_uhci(hcd); + + dev_dbg(uhci_dev(uhci), "%s\n", __func__); + + /* Since we aren't in D3 any more, it's safe to set this flag + * even if the controller was dead. + */ + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + + spin_lock_irq(&uhci->lock); + + /* Make sure resume from hibernation re-enumerates everything */ + if (hibernated) { + uhci->reset_hc(uhci); + finish_reset(uhci); + } + + /* The firmware may have changed the controller settings during + * a system wakeup. Check it and reconfigure to avoid problems. + */ + else { + check_and_reset_hc(uhci); + } + configure_hc(uhci); + + /* Tell the core if the controller had to be reset */ + if (uhci->rh_state == UHCI_RH_RESET) + usb_root_hub_lost_power(hcd->self.root_hub); + + spin_unlock_irq(&uhci->lock); + + /* If interrupts don't work and remote wakeup is enabled then + * the suspended root hub needs to be polled. + */ + if (!uhci->RD_enable && hcd->self.root_hub->do_remote_wakeup) + set_bit(HCD_FLAG_POLL_RH, &hcd->flags); + + /* Does the root hub have a port wakeup pending? */ + usb_hcd_poll_rh_status(hcd); + return 0; +} + +#endif + +static const struct hc_driver uhci_driver = { + .description = hcd_name, + .product_desc = "UHCI Host Controller", + .hcd_priv_size = sizeof(struct uhci_hcd), + + /* Generic hardware linkage */ + .irq = uhci_irq, + .flags = HCD_USB11, + + /* Basic lifecycle operations */ + .reset = uhci_pci_init, + .start = uhci_start, +#ifdef CONFIG_PM + .pci_suspend = uhci_pci_suspend, + .pci_resume = uhci_pci_resume, + .bus_suspend = uhci_rh_suspend, + .bus_resume = uhci_rh_resume, +#endif + .stop = uhci_stop, + + .urb_enqueue = uhci_urb_enqueue, + .urb_dequeue = uhci_urb_dequeue, + + .endpoint_disable = uhci_hcd_endpoint_disable, + .get_frame_number = uhci_hcd_get_frame_number, + + .hub_status_data = uhci_hub_status_data, + .hub_control = uhci_hub_control, +}; + +static DEFINE_PCI_DEVICE_TABLE(uhci_pci_ids) = { { + /* handle any USB UHCI controller */ + PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_UHCI, ~0), + .driver_data = (unsigned long) &uhci_driver, + }, { /* end: all zeroes */ } +}; + +MODULE_DEVICE_TABLE(pci, uhci_pci_ids); + +static struct pci_driver uhci_pci_driver = { + .name = (char *)hcd_name, + .id_table = uhci_pci_ids, + + .probe = usb_hcd_pci_probe, + .remove = usb_hcd_pci_remove, + .shutdown = uhci_shutdown, + +#ifdef CONFIG_PM_SLEEP + .driver = { + .pm = &usb_hcd_pci_pm_ops + }, +#endif +}; diff --git a/drivers/usb/host/uhci-q.c b/drivers/usb/host/uhci-q.c index af77abb..84ed28b 100644 --- a/drivers/usb/host/uhci-q.c +++ b/drivers/usb/host/uhci-q.c @@ -29,12 +29,12 @@ static void uhci_set_next_interrupt(struct uhci_hcd *uhci) { if (uhci->is_stopped) mod_timer(&uhci_to_hcd(uhci)->rh_timer, jiffies); - uhci->term_td->status |= cpu_to_le32(TD_CTRL_IOC); + uhci->term_td->status |= cpu_to_hc32(uhci, TD_CTRL_IOC); } static inline void uhci_clear_next_interrupt(struct uhci_hcd *uhci) { - uhci->term_td->status &= ~cpu_to_le32(TD_CTRL_IOC); + uhci->term_td->status &= ~cpu_to_hc32(uhci, TD_CTRL_IOC); } @@ -53,7 +53,7 @@ static void uhci_fsbr_on(struct uhci_hcd *uhci) uhci->fsbr_is_on = 1; lqh = list_entry(uhci->skel_async_qh->node.prev, struct uhci_qh, node); - lqh->link = LINK_TO_QH(uhci->skel_term_qh); + lqh->link = LINK_TO_QH(uhci, uhci->skel_term_qh); } static void uhci_fsbr_off(struct uhci_hcd *uhci) @@ -65,7 +65,7 @@ static void uhci_fsbr_off(struct uhci_hcd *uhci) uhci->fsbr_is_on = 0; lqh = list_entry(uhci->skel_async_qh->node.prev, struct uhci_qh, node); - lqh->link = UHCI_PTR_TERM; + lqh->link = UHCI_PTR_TERM(uhci); } static void uhci_add_fsbr(struct uhci_hcd *uhci, struct urb *urb) @@ -131,12 +131,12 @@ static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td) dma_pool_free(uhci->td_pool, td, td->dma_handle); } -static inline void uhci_fill_td(struct uhci_td *td, u32 status, - u32 token, u32 buffer) +static inline void uhci_fill_td(struct uhci_hcd *uhci, struct uhci_td *td, + u32 status, u32 token, u32 buffer) { - td->status = cpu_to_le32(status); - td->token = cpu_to_le32(token); - td->buffer = cpu_to_le32(buffer); + td->status = cpu_to_hc32(uhci, status); + td->token = cpu_to_hc32(uhci, token); + td->buffer = cpu_to_hc32(uhci, buffer); } static void uhci_add_td_to_urbp(struct uhci_td *td, struct urb_priv *urbp) @@ -170,11 +170,11 @@ static inline void uhci_insert_td_in_frame_list(struct uhci_hcd *uhci, td->link = ltd->link; wmb(); - ltd->link = LINK_TO_TD(td); + ltd->link = LINK_TO_TD(uhci, td); } else { td->link = uhci->frame[framenum]; wmb(); - uhci->frame[framenum] = LINK_TO_TD(td); + uhci->frame[framenum] = LINK_TO_TD(uhci, td); uhci->frame_cpu[framenum] = td; } } @@ -198,7 +198,7 @@ static inline void uhci_remove_td_from_frame_list(struct uhci_hcd *uhci, ntd = list_entry(td->fl_list.next, struct uhci_td, fl_list); - uhci->frame[td->frame] = LINK_TO_TD(ntd); + uhci->frame[td->frame] = LINK_TO_TD(uhci, ntd); uhci->frame_cpu[td->frame] = ntd; } } else { @@ -255,8 +255,8 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci, memset(qh, 0, sizeof(*qh)); qh->dma_handle = dma_handle; - qh->element = UHCI_PTR_TERM; - qh->link = UHCI_PTR_TERM; + qh->element = UHCI_PTR_TERM(uhci); + qh->link = UHCI_PTR_TERM(uhci); INIT_LIST_HEAD(&qh->queue); INIT_LIST_HEAD(&qh->node); @@ -348,9 +348,9 @@ static int uhci_cleanup_queue(struct uhci_hcd *uhci, struct uhci_qh *qh, /* If the QH element pointer is UHCI_PTR_TERM then then currently * executing URB has already been unlinked, so this one isn't it. */ - if (qh_element(qh) == UHCI_PTR_TERM) + if (qh_element(qh) == UHCI_PTR_TERM(uhci)) goto done; - qh->element = UHCI_PTR_TERM; + qh->element = UHCI_PTR_TERM(uhci); /* Control pipes don't have to worry about toggles */ if (qh->type == USB_ENDPOINT_XFER_CONTROL) @@ -360,7 +360,7 @@ static int uhci_cleanup_queue(struct uhci_hcd *uhci, struct uhci_qh *qh, WARN_ON(list_empty(&urbp->td_list)); td = list_entry(urbp->td_list.next, struct uhci_td, list); qh->needs_fixup = 1; - qh->initial_toggle = uhci_toggle(td_token(td)); + qh->initial_toggle = uhci_toggle(td_token(uhci, td)); done: return ret; @@ -370,7 +370,8 @@ done: * Fix up the data toggles for URBs in a queue, when one of them * terminates early (short transfer, error, or dequeued). */ -static void uhci_fixup_toggles(struct uhci_qh *qh, int skip_first) +static void uhci_fixup_toggles(struct uhci_hcd *uhci, struct uhci_qh *qh, + int skip_first) { struct urb_priv *urbp = NULL; struct uhci_td *td; @@ -384,7 +385,7 @@ static void uhci_fixup_toggles(struct uhci_qh *qh, int skip_first) /* When starting with the first URB, if the QH element pointer is * still valid then we know the URB's toggles are okay. */ - else if (qh_element(qh) != UHCI_PTR_TERM) + else if (qh_element(qh) != UHCI_PTR_TERM(uhci)) toggle = 2; /* Fix up the toggle for the URBs in the queue. Normally this @@ -396,15 +397,15 @@ static void uhci_fixup_toggles(struct uhci_qh *qh, int skip_first) /* If the first TD has the right toggle value, we don't * need to change any toggles in this URB */ td = list_entry(urbp->td_list.next, struct uhci_td, list); - if (toggle > 1 || uhci_toggle(td_token(td)) == toggle) { + if (toggle > 1 || uhci_toggle(td_token(uhci, td)) == toggle) { td = list_entry(urbp->td_list.prev, struct uhci_td, list); - toggle = uhci_toggle(td_token(td)) ^ 1; + toggle = uhci_toggle(td_token(uhci, td)) ^ 1; /* Otherwise all the toggles in the URB have to be switched */ } else { list_for_each_entry(td, &urbp->td_list, list) { - td->token ^= cpu_to_le32( + td->token ^= cpu_to_hc32(uhci, TD_TOKEN_TOGGLE); toggle ^= 1; } @@ -441,7 +442,7 @@ static void link_interrupt(struct uhci_hcd *uhci, struct uhci_qh *qh) pqh = list_entry(qh->node.prev, struct uhci_qh, node); qh->link = pqh->link; wmb(); - pqh->link = LINK_TO_QH(qh); + pqh->link = LINK_TO_QH(uhci, qh); } /* @@ -451,7 +452,7 @@ static void link_interrupt(struct uhci_hcd *uhci, struct uhci_qh *qh) static void link_async(struct uhci_hcd *uhci, struct uhci_qh *qh) { struct uhci_qh *pqh; - __le32 link_to_new_qh; + __hc32 link_to_new_qh; /* Find the predecessor QH for our new one and insert it in the list. * The list of QHs is expected to be short, so linear search won't @@ -465,7 +466,7 @@ static void link_async(struct uhci_hcd *uhci, struct uhci_qh *qh) /* Link it into the schedule */ qh->link = pqh->link; wmb(); - link_to_new_qh = LINK_TO_QH(qh); + link_to_new_qh = LINK_TO_QH(uhci, qh); pqh->link = link_to_new_qh; /* If this is now the first FSBR QH, link the terminating skeleton @@ -483,13 +484,13 @@ static void uhci_activate_qh(struct uhci_hcd *uhci, struct uhci_qh *qh) /* Set the element pointer if it isn't set already. * This isn't needed for Isochronous queues, but it doesn't hurt. */ - if (qh_element(qh) == UHCI_PTR_TERM) { + if (qh_element(qh) == UHCI_PTR_TERM(uhci)) { struct urb_priv *urbp = list_entry(qh->queue.next, struct urb_priv, node); struct uhci_td *td = list_entry(urbp->td_list.next, struct uhci_td, list); - qh->element = LINK_TO_TD(td); + qh->element = LINK_TO_TD(uhci, td); } /* Treat the queue as if it has just advanced */ @@ -533,7 +534,7 @@ static void unlink_interrupt(struct uhci_hcd *uhci, struct uhci_qh *qh) static void unlink_async(struct uhci_hcd *uhci, struct uhci_qh *qh) { struct uhci_qh *pqh; - __le32 link_to_next_qh = qh->link; + __hc32 link_to_next_qh = qh->link; pqh = list_entry(qh->node.prev, struct uhci_qh, node); pqh->link = link_to_next_qh; @@ -757,8 +758,8 @@ static void uhci_free_urb_priv(struct uhci_hcd *uhci, /* * Map status to standard result codes * - * <status> is (td_status(td) & 0xF60000), a.k.a. - * uhci_status_bits(td_status(td)). + * <status> is (td_status(uhci, td) & 0xF60000), a.k.a. + * uhci_status_bits(td_status(uhci, td)). * Note: <status> does not include the TD_CTRL_NAK bit. * <dir_out> is True for output TDs and False for input TDs. */ @@ -794,7 +795,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, int maxsze = le16_to_cpu(qh->hep->desc.wMaxPacketSize); int len = urb->transfer_buffer_length; dma_addr_t data = urb->transfer_dma; - __le32 *plink; + __hc32 *plink; struct urb_priv *urbp = urb->hcpriv; int skel; @@ -811,7 +812,7 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, */ td = qh->dummy_td; uhci_add_td_to_urbp(td, urbp); - uhci_fill_td(td, status, destination | uhci_explen(8), + uhci_fill_td(uhci, td, status, destination | uhci_explen(8), urb->setup_dma); plink = &td->link; status |= TD_CTRL_ACTIVE; @@ -844,14 +845,14 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, td = uhci_alloc_td(uhci); if (!td) goto nomem; - *plink = LINK_TO_TD(td); + *plink = LINK_TO_TD(uhci, td); /* Alternate Data0/1 (start with Data1) */ destination ^= TD_TOKEN_TOGGLE; uhci_add_td_to_urbp(td, urbp); - uhci_fill_td(td, status, destination | uhci_explen(pktsze), - data); + uhci_fill_td(uhci, td, status, + destination | uhci_explen(pktsze), data); plink = &td->link; data += pktsze; @@ -864,14 +865,14 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, td = uhci_alloc_td(uhci); if (!td) goto nomem; - *plink = LINK_TO_TD(td); + *plink = LINK_TO_TD(uhci, td); /* Change direction for the status transaction */ destination ^= (USB_PID_IN ^ USB_PID_OUT); destination |= TD_TOKEN_TOGGLE; /* End in Data1 */ uhci_add_td_to_urbp(td, urbp); - uhci_fill_td(td, status | TD_CTRL_IOC, + uhci_fill_td(uhci, td, status | TD_CTRL_IOC, destination | uhci_explen(0), 0); plink = &td->link; @@ -881,11 +882,11 @@ static int uhci_submit_control(struct uhci_hcd *uhci, struct urb *urb, td = uhci_alloc_td(uhci); if (!td) goto nomem; - *plink = LINK_TO_TD(td); + *plink = LINK_TO_TD(uhci, td); - uhci_fill_td(td, 0, USB_PID_OUT | uhci_explen(0), 0); + uhci_fill_td(uhci, td, 0, USB_PID_OUT | uhci_explen(0), 0); wmb(); - qh->dummy_td->status |= cpu_to_le32(TD_CTRL_ACTIVE); + qh->dummy_td->status |= cpu_to_hc32(uhci, TD_CTRL_ACTIVE); qh->dummy_td = td; /* Low-speed transfers get a different queue, and won't hog the bus. @@ -921,7 +922,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, int len = urb->transfer_buffer_length; int this_sg_len; dma_addr_t data; - __le32 *plink; + __hc32 *plink; struct urb_priv *urbp = urb->hcpriv; unsigned int toggle; struct scatterlist *sg; @@ -974,10 +975,10 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, td = uhci_alloc_td(uhci); if (!td) goto nomem; - *plink = LINK_TO_TD(td); + *plink = LINK_TO_TD(uhci, td); } uhci_add_td_to_urbp(td, urbp); - uhci_fill_td(td, status, + uhci_fill_td(uhci, td, status, destination | uhci_explen(pktsze) | (toggle << TD_TOKEN_TOGGLE_SHIFT), data); @@ -1010,10 +1011,10 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, td = uhci_alloc_td(uhci); if (!td) goto nomem; - *plink = LINK_TO_TD(td); + *plink = LINK_TO_TD(uhci, td); uhci_add_td_to_urbp(td, urbp); - uhci_fill_td(td, status, + uhci_fill_td(uhci, td, status, destination | uhci_explen(0) | (toggle << TD_TOKEN_TOGGLE_SHIFT), data); @@ -1028,7 +1029,7 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, * fast side but not enough to justify delaying an interrupt * more than 2 or 3 URBs, so we will ignore the URB_NO_INTERRUPT * flag setting. */ - td->status |= cpu_to_le32(TD_CTRL_IOC); + td->status |= cpu_to_hc32(uhci, TD_CTRL_IOC); /* * Build the new dummy TD and activate the old one @@ -1036,11 +1037,11 @@ static int uhci_submit_common(struct uhci_hcd *uhci, struct urb *urb, td = uhci_alloc_td(uhci); if (!td) goto nomem; - *plink = LINK_TO_TD(td); + *plink = LINK_TO_TD(uhci, td); - uhci_fill_td(td, 0, USB_PID_OUT | uhci_explen(0), 0); + uhci_fill_td(uhci, td, 0, USB_PID_OUT | uhci_explen(0), 0); wmb(); - qh->dummy_td->status |= cpu_to_le32(TD_CTRL_ACTIVE); + qh->dummy_td->status |= cpu_to_hc32(uhci, TD_CTRL_ACTIVE); qh->dummy_td = td; usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), @@ -1133,7 +1134,7 @@ static int uhci_fixup_short_transfer(struct uhci_hcd *uhci, * the queue at the status stage transaction, which is * the last TD. */ WARN_ON(list_empty(&urbp->td_list)); - qh->element = LINK_TO_TD(td); + qh->element = LINK_TO_TD(uhci, td); tmp = td->list.prev; ret = -EINPROGRESS; @@ -1142,8 +1143,9 @@ static int uhci_fixup_short_transfer(struct uhci_hcd *uhci, /* When a bulk/interrupt transfer is short, we have to * fix up the toggles of the following URBs on the queue * before restarting the queue at the next URB. */ - qh->initial_toggle = uhci_toggle(td_token(qh->post_td)) ^ 1; - uhci_fixup_toggles(qh, 1); + qh->initial_toggle = + uhci_toggle(td_token(uhci, qh->post_td)) ^ 1; + uhci_fixup_toggles(uhci, qh, 1); if (list_empty(&urbp->td_list)) td = qh->post_td; @@ -1178,7 +1180,7 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb) unsigned int ctrlstat; int len; - ctrlstat = td_status(td); + ctrlstat = td_status(uhci, td); status = uhci_status_bits(ctrlstat); if (status & TD_CTRL_ACTIVE) return -EINPROGRESS; @@ -1188,7 +1190,7 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb) if (status) { ret = uhci_map_status(status, - uhci_packetout(td_token(td))); + uhci_packetout(td_token(uhci, td))); if ((debug == 1 && ret != -EPIPE) || debug > 1) { /* Some debugging code */ dev_dbg(&urb->dev->dev, @@ -1204,7 +1206,7 @@ static int uhci_result_common(struct uhci_hcd *uhci, struct urb *urb) } /* Did we receive a short packet? */ - } else if (len < uhci_expected_length(td_token(td))) { + } else if (len < uhci_expected_length(td_token(uhci, td))) { /* For control transfers, go to the status TD if * this isn't already the last data TD */ @@ -1236,10 +1238,10 @@ err: if (ret < 0) { /* Note that the queue has stopped and save * the next toggle value */ - qh->element = UHCI_PTR_TERM; + qh->element = UHCI_PTR_TERM(uhci); qh->is_stopped = 1; qh->needs_fixup = (qh->type != USB_ENDPOINT_XFER_CONTROL); - qh->initial_toggle = uhci_toggle(td_token(td)) ^ + qh->initial_toggle = uhci_toggle(td_token(uhci, td)) ^ (ret == -EREMOTEIO); } else /* Short packet received */ @@ -1335,14 +1337,14 @@ static int uhci_submit_isochronous(struct uhci_hcd *uhci, struct urb *urb, return -ENOMEM; uhci_add_td_to_urbp(td, urbp); - uhci_fill_td(td, status, destination | + uhci_fill_td(uhci, td, status, destination | uhci_explen(urb->iso_frame_desc[i].length), urb->transfer_dma + urb->iso_frame_desc[i].offset); } /* Set the interrupt-on-completion flag on the last packet. */ - td->status |= cpu_to_le32(TD_CTRL_IOC); + td->status |= cpu_to_hc32(uhci, TD_CTRL_IOC); /* Add the TDs to the frame list */ frame = urb->start_frame; @@ -1378,7 +1380,7 @@ static int uhci_result_isochronous(struct uhci_hcd *uhci, struct urb *urb) uhci_remove_tds_from_frame(uhci, qh->iso_frame); - ctrlstat = td_status(td); + ctrlstat = td_status(uhci, td); if (ctrlstat & TD_CTRL_ACTIVE) { status = -EXDEV; /* TD was added too late? */ } else { @@ -1629,7 +1631,7 @@ restart: * queue, the QH can now be re-activated. */ if (!list_empty(&qh->queue)) { if (qh->needs_fixup) - uhci_fixup_toggles(qh, 0); + uhci_fixup_toggles(uhci, qh, 0); /* If the first URB on the queue wants FSBR but its time * limit has expired, set the next TD to interrupt on @@ -1639,7 +1641,7 @@ restart: struct uhci_td *td = list_entry(urbp->td_list.next, struct uhci_td, list); - td->status |= __cpu_to_le32(TD_CTRL_IOC); + td->status |= cpu_to_hc32(uhci, TD_CTRL_IOC); } uhci_activate_qh(uhci, qh); @@ -1686,7 +1688,7 @@ static int uhci_advance_check(struct uhci_hcd *uhci, struct uhci_qh *qh) } else { urbp = list_entry(qh->queue.next, struct urb_priv, node); td = list_entry(urbp->td_list.next, struct uhci_td, list); - status = td_status(td); + status = td_status(uhci, td); if (!(status & TD_CTRL_ACTIVE)) { /* We're okay, the queue has advanced */ @@ -1704,7 +1706,8 @@ static int uhci_advance_check(struct uhci_hcd *uhci, struct uhci_qh *qh) if (time_after(jiffies, qh->advance_jiffies + QH_WAIT_TIMEOUT)) { /* Detect the Intel bug and work around it */ - if (qh->post_td && qh_element(qh) == LINK_TO_TD(qh->post_td)) { + if (qh->post_td && qh_element(qh) == + LINK_TO_TD(uhci, qh->post_td)) { qh->element = qh->post_td->link; qh->advance_jiffies = jiffies; ret = 1; diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c index 0231814..2e04861 100644 --- a/drivers/usb/host/xhci-dbg.c +++ b/drivers/usb/host/xhci-dbg.c @@ -147,7 +147,7 @@ static void xhci_print_op_regs(struct xhci_hcd *xhci) static void xhci_print_ports(struct xhci_hcd *xhci) { - u32 __iomem *addr; + __le32 __iomem *addr; int i, j; int ports; char *names[NUM_PORT_REGS] = { @@ -253,27 +253,27 @@ void xhci_print_trb_offsets(struct xhci_hcd *xhci, union xhci_trb *trb) void xhci_debug_trb(struct xhci_hcd *xhci, union xhci_trb *trb) { u64 address; - u32 type = xhci_readl(xhci, &trb->link.control) & TRB_TYPE_BITMASK; + u32 type = le32_to_cpu(trb->link.control) & TRB_TYPE_BITMASK; switch (type) { case TRB_TYPE(TRB_LINK): xhci_dbg(xhci, "Link TRB:\n"); xhci_print_trb_offsets(xhci, trb); - address = trb->link.segment_ptr; + address = le64_to_cpu(trb->link.segment_ptr); xhci_dbg(xhci, "Next ring segment DMA address = 0x%llx\n", address); xhci_dbg(xhci, "Interrupter target = 0x%x\n", - GET_INTR_TARGET(trb->link.intr_target)); + GET_INTR_TARGET(le32_to_cpu(trb->link.intr_target))); xhci_dbg(xhci, "Cycle bit = %u\n", - (unsigned int) (trb->link.control & TRB_CYCLE)); + (unsigned int) (le32_to_cpu(trb->link.control) & TRB_CYCLE)); xhci_dbg(xhci, "Toggle cycle bit = %u\n", - (unsigned int) (trb->link.control & LINK_TOGGLE)); + (unsigned int) (le32_to_cpu(trb->link.control) & LINK_TOGGLE)); xhci_dbg(xhci, "No Snoop bit = %u\n", - (unsigned int) (trb->link.control & TRB_NO_SNOOP)); + (unsigned int) (le32_to_cpu(trb->link.control) & TRB_NO_SNOOP)); break; case TRB_TYPE(TRB_TRANSFER): - address = trb->trans_event.buffer; + address = le64_to_cpu(trb->trans_event.buffer); /* * FIXME: look at flags to figure out if it's an address or if * the data is directly in the buffer field. @@ -281,11 +281,12 @@ void xhci_debug_trb(struct xhci_hcd *xhci, union xhci_trb *trb) xhci_dbg(xhci, "DMA address or buffer contents= %llu\n", address); break; case TRB_TYPE(TRB_COMPLETION): - address = trb->event_cmd.cmd_trb; + address = le64_to_cpu(trb->event_cmd.cmd_trb); xhci_dbg(xhci, "Command TRB pointer = %llu\n", address); xhci_dbg(xhci, "Completion status = %u\n", - (unsigned int) GET_COMP_CODE(trb->event_cmd.status)); - xhci_dbg(xhci, "Flags = 0x%x\n", (unsigned int) trb->event_cmd.flags); + (unsigned int) GET_COMP_CODE(le32_to_cpu(trb->event_cmd.status))); + xhci_dbg(xhci, "Flags = 0x%x\n", + (unsigned int) le32_to_cpu(trb->event_cmd.flags)); break; default: xhci_dbg(xhci, "Unknown TRB with TRB type ID %u\n", @@ -311,16 +312,16 @@ void xhci_debug_trb(struct xhci_hcd *xhci, union xhci_trb *trb) void xhci_debug_segment(struct xhci_hcd *xhci, struct xhci_segment *seg) { int i; - u32 addr = (u32) seg->dma; + u64 addr = seg->dma; union xhci_trb *trb = seg->trbs; for (i = 0; i < TRBS_PER_SEGMENT; ++i) { trb = &seg->trbs[i]; - xhci_dbg(xhci, "@%08x %08x %08x %08x %08x\n", addr, - lower_32_bits(trb->link.segment_ptr), - upper_32_bits(trb->link.segment_ptr), - (unsigned int) trb->link.intr_target, - (unsigned int) trb->link.control); + xhci_dbg(xhci, "@%016llx %08x %08x %08x %08x\n", addr, + (u32)lower_32_bits(le64_to_cpu(trb->link.segment_ptr)), + (u32)upper_32_bits(le64_to_cpu(trb->link.segment_ptr)), + (unsigned int) le32_to_cpu(trb->link.intr_target), + (unsigned int) le32_to_cpu(trb->link.control)); addr += sizeof(*trb); } } @@ -391,18 +392,18 @@ void xhci_dbg_ep_rings(struct xhci_hcd *xhci, void xhci_dbg_erst(struct xhci_hcd *xhci, struct xhci_erst *erst) { - u32 addr = (u32) erst->erst_dma_addr; + u64 addr = erst->erst_dma_addr; int i; struct xhci_erst_entry *entry; for (i = 0; i < erst->num_entries; ++i) { entry = &erst->entries[i]; - xhci_dbg(xhci, "@%08x %08x %08x %08x %08x\n", - (unsigned int) addr, - lower_32_bits(entry->seg_addr), - upper_32_bits(entry->seg_addr), - (unsigned int) entry->seg_size, - (unsigned int) entry->rsvd); + xhci_dbg(xhci, "@%016llx %08x %08x %08x %08x\n", + addr, + lower_32_bits(le64_to_cpu(entry->seg_addr)), + upper_32_bits(le64_to_cpu(entry->seg_addr)), + (unsigned int) le32_to_cpu(entry->seg_size), + (unsigned int) le32_to_cpu(entry->rsvd)); addr += sizeof(*entry); } } @@ -436,7 +437,7 @@ char *xhci_get_slot_state(struct xhci_hcd *xhci, { struct xhci_slot_ctx *slot_ctx = xhci_get_slot_ctx(xhci, ctx); - switch (GET_SLOT_STATE(slot_ctx->dev_state)) { + switch (GET_SLOT_STATE(le32_to_cpu(slot_ctx->dev_state))) { case 0: return "enabled/disabled"; case 1: diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 73f75d2..0be788c 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -50,7 +50,7 @@ static void xhci_common_hub_descriptor(struct xhci_hcd *xhci, temp |= 0x0008; /* Bits 6:5 - no TTs in root ports */ /* Bit 7 - no port indicators */ - desc->wHubCharacteristics = (__force __u16) cpu_to_le16(temp); + desc->wHubCharacteristics = cpu_to_le16(temp); } /* Fill in the USB 2.0 roothub descriptor */ @@ -314,7 +314,7 @@ void xhci_ring_device(struct xhci_hcd *xhci, int slot_id) } static void xhci_disable_port(struct usb_hcd *hcd, struct xhci_hcd *xhci, - u16 wIndex, u32 __iomem *addr, u32 port_status) + u16 wIndex, __le32 __iomem *addr, u32 port_status) { /* Don't allow the USB core to disable SuperSpeed ports. */ if (hcd->speed == HCD_USB3) { @@ -331,7 +331,7 @@ static void xhci_disable_port(struct usb_hcd *hcd, struct xhci_hcd *xhci, } static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, - u16 wIndex, u32 __iomem *addr, u32 port_status) + u16 wIndex, __le32 __iomem *addr, u32 port_status) { char *port_change_bit; u32 status; @@ -341,6 +341,10 @@ static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, status = PORT_RC; port_change_bit = "reset"; break; + case USB_PORT_FEAT_C_BH_PORT_RESET: + status = PORT_WRC; + port_change_bit = "warm(BH) reset"; + break; case USB_PORT_FEAT_C_CONNECTION: status = PORT_CSC; port_change_bit = "connect"; @@ -357,6 +361,10 @@ static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, status = PORT_PLC; port_change_bit = "suspend/resume"; break; + case USB_PORT_FEAT_C_PORT_LINK_STATE: + status = PORT_PLC; + port_change_bit = "link state"; + break; default: /* Should never happen */ return; @@ -368,25 +376,36 @@ static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue, port_change_bit, wIndex, port_status); } +static int xhci_get_ports(struct usb_hcd *hcd, __le32 __iomem ***port_array) +{ + int max_ports; + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + + if (hcd->speed == HCD_USB3) { + max_ports = xhci->num_usb3_ports; + *port_array = xhci->usb3_ports; + } else { + max_ports = xhci->num_usb2_ports; + *port_array = xhci->usb2_ports; + } + + return max_ports; +} + int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); - int ports; + int max_ports; unsigned long flags; u32 temp, temp1, status; int retval = 0; - u32 __iomem **port_array; + __le32 __iomem **port_array; int slot_id; struct xhci_bus_state *bus_state; + u16 link_state = 0; - if (hcd->speed == HCD_USB3) { - ports = xhci->num_usb3_ports; - port_array = xhci->usb3_ports; - } else { - ports = xhci->num_usb2_ports; - port_array = xhci->usb2_ports; - } + max_ports = xhci_get_ports(hcd, &port_array); bus_state = &xhci->bus_state[hcd_index(hcd)]; spin_lock_irqsave(&xhci->lock, flags); @@ -411,7 +430,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, (struct usb_hub_descriptor *) buf); break; case GetPortStatus: - if (!wIndex || wIndex > ports) + if (!wIndex || wIndex > max_ports) goto error; wIndex--; status = 0; @@ -422,9 +441,6 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, } xhci_dbg(xhci, "get port status, actual port %d status = 0x%x\n", wIndex, temp); - /* FIXME - should we return a port status value like the USB - * 3.0 external hubs do? - */ /* wPortChange bits */ if (temp & PORT_CSC) status |= USB_PORT_STAT_C_CONNECTION << 16; @@ -432,13 +448,21 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, status |= USB_PORT_STAT_C_ENABLE << 16; if ((temp & PORT_OCC)) status |= USB_PORT_STAT_C_OVERCURRENT << 16; - /* - * FIXME ignoring reset and USB 2.1/3.0 specific - * changes - */ - if ((temp & PORT_PLS_MASK) == XDEV_U3 - && (temp & PORT_POWER)) - status |= 1 << USB_PORT_FEAT_SUSPEND; + if ((temp & PORT_RC)) + status |= USB_PORT_STAT_C_RESET << 16; + /* USB3.0 only */ + if (hcd->speed == HCD_USB3) { + if ((temp & PORT_PLC)) + status |= USB_PORT_STAT_C_LINK_STATE << 16; + if ((temp & PORT_WRC)) + status |= USB_PORT_STAT_C_BH_RESET << 16; + } + + if (hcd->speed != HCD_USB3) { + if ((temp & PORT_PLS_MASK) == XDEV_U3 + && (temp & PORT_POWER)) + status |= USB_PORT_STAT_SUSPEND; + } if ((temp & PORT_PLS_MASK) == XDEV_RESUME) { if ((temp & PORT_RESET) || !(temp & PORT_PE)) goto error; @@ -469,7 +493,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, && (temp & PORT_POWER) && (bus_state->suspended_ports & (1 << wIndex))) { bus_state->suspended_ports &= ~(1 << wIndex); - bus_state->port_c_suspend |= 1 << wIndex; + if (hcd->speed != HCD_USB3) + bus_state->port_c_suspend |= 1 << wIndex; } if (temp & PORT_CONNECT) { status |= USB_PORT_STAT_CONNECTION; @@ -481,16 +506,30 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, status |= USB_PORT_STAT_OVERCURRENT; if (temp & PORT_RESET) status |= USB_PORT_STAT_RESET; - if (temp & PORT_POWER) - status |= USB_PORT_STAT_POWER; + if (temp & PORT_POWER) { + if (hcd->speed == HCD_USB3) + status |= USB_SS_PORT_STAT_POWER; + else + status |= USB_PORT_STAT_POWER; + } + /* Port Link State */ + if (hcd->speed == HCD_USB3) { + /* resume state is a xHCI internal state. + * Do not report it to usb core. + */ + if ((temp & PORT_PLS_MASK) != XDEV_RESUME) + status |= (temp & PORT_PLS_MASK); + } if (bus_state->port_c_suspend & (1 << wIndex)) status |= 1 << USB_PORT_FEAT_C_SUSPEND; xhci_dbg(xhci, "Get port status returned 0x%x\n", status); put_unaligned(cpu_to_le32(status), (__le32 *) buf); break; case SetPortFeature: + if (wValue == USB_PORT_FEAT_LINK_STATE) + link_state = (wIndex & 0xff00) >> 3; wIndex &= 0xff; - if (!wIndex || wIndex > ports) + if (!wIndex || wIndex > max_ports) goto error; wIndex--; temp = xhci_readl(xhci, port_array[wIndex]); @@ -537,6 +576,44 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp = xhci_readl(xhci, port_array[wIndex]); bus_state->suspended_ports |= 1 << wIndex; break; + case USB_PORT_FEAT_LINK_STATE: + temp = xhci_readl(xhci, port_array[wIndex]); + /* Software should not attempt to set + * port link state above '5' (Rx.Detect) and the port + * must be enabled. + */ + if ((temp & PORT_PE) == 0 || + (link_state > USB_SS_PORT_LS_RX_DETECT)) { + xhci_warn(xhci, "Cannot set link state.\n"); + goto error; + } + + if (link_state == USB_SS_PORT_LS_U3) { + slot_id = xhci_find_slot_id_by_port(hcd, xhci, + wIndex + 1); + if (slot_id) { + /* unlock to execute stop endpoint + * commands */ + spin_unlock_irqrestore(&xhci->lock, + flags); + xhci_stop_device(xhci, slot_id, 1); + spin_lock_irqsave(&xhci->lock, flags); + } + } + + temp = xhci_port_state_to_neutral(temp); + temp &= ~PORT_PLS_MASK; + temp |= PORT_LINK_STROBE | link_state; + xhci_writel(xhci, temp, port_array[wIndex]); + + spin_unlock_irqrestore(&xhci->lock, flags); + msleep(20); /* wait device to enter */ + spin_lock_irqsave(&xhci->lock, flags); + + temp = xhci_readl(xhci, port_array[wIndex]); + if (link_state == USB_SS_PORT_LS_U3) + bus_state->suspended_ports |= 1 << wIndex; + break; case USB_PORT_FEAT_POWER: /* * Turn on ports, even if there isn't per-port switching. @@ -557,6 +634,12 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp = xhci_readl(xhci, port_array[wIndex]); xhci_dbg(xhci, "set port reset, actual port %d status = 0x%x\n", wIndex, temp); break; + case USB_PORT_FEAT_BH_PORT_RESET: + temp |= PORT_WR; + xhci_writel(xhci, temp, port_array[wIndex]); + + temp = xhci_readl(xhci, port_array[wIndex]); + break; default: goto error; } @@ -564,7 +647,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp = xhci_readl(xhci, port_array[wIndex]); break; case ClearPortFeature: - if (!wIndex || wIndex > ports) + if (!wIndex || wIndex > max_ports) goto error; wIndex--; temp = xhci_readl(xhci, port_array[wIndex]); @@ -584,35 +667,27 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, if (temp & XDEV_U3) { if ((temp & PORT_PE) == 0) goto error; - if (DEV_SUPERSPEED(temp)) { - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | XDEV_U0; - xhci_writel(xhci, temp, - port_array[wIndex]); - xhci_readl(xhci, port_array[wIndex]); - } else { - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | XDEV_RESUME; - xhci_writel(xhci, temp, - port_array[wIndex]); - spin_unlock_irqrestore(&xhci->lock, - flags); - msleep(20); - spin_lock_irqsave(&xhci->lock, flags); + temp = xhci_port_state_to_neutral(temp); + temp &= ~PORT_PLS_MASK; + temp |= PORT_LINK_STROBE | XDEV_RESUME; + xhci_writel(xhci, temp, + port_array[wIndex]); - temp = xhci_readl(xhci, - port_array[wIndex]); - temp = xhci_port_state_to_neutral(temp); - temp &= ~PORT_PLS_MASK; - temp |= PORT_LINK_STROBE | XDEV_U0; - xhci_writel(xhci, temp, - port_array[wIndex]); - } - bus_state->port_c_suspend |= 1 << wIndex; + spin_unlock_irqrestore(&xhci->lock, + flags); + msleep(20); + spin_lock_irqsave(&xhci->lock, flags); + + temp = xhci_readl(xhci, + port_array[wIndex]); + temp = xhci_port_state_to_neutral(temp); + temp &= ~PORT_PLS_MASK; + temp |= PORT_LINK_STROBE | XDEV_U0; + xhci_writel(xhci, temp, + port_array[wIndex]); } + bus_state->port_c_suspend |= 1 << wIndex; slot_id = xhci_find_slot_id_by_port(hcd, xhci, wIndex + 1); @@ -625,9 +700,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case USB_PORT_FEAT_C_SUSPEND: bus_state->port_c_suspend &= ~(1 << wIndex); case USB_PORT_FEAT_C_RESET: + case USB_PORT_FEAT_C_BH_PORT_RESET: case USB_PORT_FEAT_C_CONNECTION: case USB_PORT_FEAT_C_OVER_CURRENT: case USB_PORT_FEAT_C_ENABLE: + case USB_PORT_FEAT_C_PORT_LINK_STATE: xhci_clear_port_change_bit(xhci, wValue, wIndex, port_array[wIndex], temp); break; @@ -663,29 +740,23 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf) u32 mask; int i, retval; struct xhci_hcd *xhci = hcd_to_xhci(hcd); - int ports; - u32 __iomem **port_array; + int max_ports; + __le32 __iomem **port_array; struct xhci_bus_state *bus_state; - if (hcd->speed == HCD_USB3) { - ports = xhci->num_usb3_ports; - port_array = xhci->usb3_ports; - } else { - ports = xhci->num_usb2_ports; - port_array = xhci->usb2_ports; - } + max_ports = xhci_get_ports(hcd, &port_array); bus_state = &xhci->bus_state[hcd_index(hcd)]; /* Initial status is no changes */ - retval = (ports + 8) / 8; + retval = (max_ports + 8) / 8; memset(buf, 0, retval); status = 0; - mask = PORT_CSC | PORT_PEC | PORT_OCC; + mask = PORT_CSC | PORT_PEC | PORT_OCC | PORT_PLC; spin_lock_irqsave(&xhci->lock, flags); /* For each port, did anything change? If so, set that bit in buf. */ - for (i = 0; i < ports; i++) { + for (i = 0; i < max_ports; i++) { temp = xhci_readl(xhci, port_array[i]); if (temp == 0xffffffff) { retval = -ENODEV; @@ -709,19 +780,11 @@ int xhci_bus_suspend(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); int max_ports, port_index; - u32 __iomem **port_array; + __le32 __iomem **port_array; struct xhci_bus_state *bus_state; unsigned long flags; - if (hcd->speed == HCD_USB3) { - max_ports = xhci->num_usb3_ports; - port_array = xhci->usb3_ports; - xhci_dbg(xhci, "suspend USB 3.0 root hub\n"); - } else { - max_ports = xhci->num_usb2_ports; - port_array = xhci->usb2_ports; - xhci_dbg(xhci, "suspend USB 2.0 root hub\n"); - } + max_ports = xhci_get_ports(hcd, &port_array); bus_state = &xhci->bus_state[hcd_index(hcd)]; spin_lock_irqsave(&xhci->lock, flags); @@ -779,7 +842,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd) if (hcd->speed != HCD_USB3) { /* enable remote wake up for USB 2.0 */ - u32 __iomem *addr; + __le32 __iomem *addr; u32 tmp; /* Add one to the port status register address to get @@ -801,20 +864,12 @@ int xhci_bus_resume(struct usb_hcd *hcd) { struct xhci_hcd *xhci = hcd_to_xhci(hcd); int max_ports, port_index; - u32 __iomem **port_array; + __le32 __iomem **port_array; struct xhci_bus_state *bus_state; u32 temp; unsigned long flags; - if (hcd->speed == HCD_USB3) { - max_ports = xhci->num_usb3_ports; - port_array = xhci->usb3_ports; - xhci_dbg(xhci, "resume USB 3.0 root hub\n"); - } else { - max_ports = xhci->num_usb2_ports; - port_array = xhci->usb2_ports; - xhci_dbg(xhci, "resume USB 2.0 root hub\n"); - } + max_ports = xhci_get_ports(hcd, &port_array); bus_state = &xhci->bus_state[hcd_index(hcd)]; if (time_before(jiffies, bus_state->next_statechange)) @@ -890,7 +945,7 @@ int xhci_bus_resume(struct usb_hcd *hcd) if (hcd->speed != HCD_USB3) { /* disable remote wake up for USB 2.0 */ - u32 __iomem *addr; + __le32 __iomem *addr; u32 tmp; /* Add one to the port status register address to get diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 627f343..26caba4 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -89,16 +89,17 @@ static void xhci_link_segments(struct xhci_hcd *xhci, struct xhci_segment *prev, return; prev->next = next; if (link_trbs) { - prev->trbs[TRBS_PER_SEGMENT-1].link.segment_ptr = next->dma; + prev->trbs[TRBS_PER_SEGMENT-1].link. + segment_ptr = cpu_to_le64(next->dma); /* Set the last TRB in the segment to have a TRB type ID of Link TRB */ - val = prev->trbs[TRBS_PER_SEGMENT-1].link.control; + val = le32_to_cpu(prev->trbs[TRBS_PER_SEGMENT-1].link.control); val &= ~TRB_TYPE_BITMASK; val |= TRB_TYPE(TRB_LINK); /* Always set the chain bit with 0.95 hardware */ if (xhci_link_trb_quirk(xhci)) val |= TRB_CHAIN; - prev->trbs[TRBS_PER_SEGMENT-1].link.control = val; + prev->trbs[TRBS_PER_SEGMENT-1].link.control = cpu_to_le32(val); } xhci_dbg(xhci, "Linking segment 0x%llx to segment 0x%llx (DMA)\n", (unsigned long long)prev->dma, @@ -186,7 +187,8 @@ static struct xhci_ring *xhci_ring_alloc(struct xhci_hcd *xhci, if (link_trbs) { /* See section 4.9.2.1 and 6.4.4.1 */ - prev->trbs[TRBS_PER_SEGMENT-1].link.control |= (LINK_TOGGLE); + prev->trbs[TRBS_PER_SEGMENT-1].link. + control |= cpu_to_le32(LINK_TOGGLE); xhci_dbg(xhci, "Wrote link toggle flag to" " segment %p (virtual), 0x%llx (DMA)\n", prev, (unsigned long long)prev->dma); @@ -207,14 +209,13 @@ void xhci_free_or_cache_endpoint_ring(struct xhci_hcd *xhci, rings_cached = virt_dev->num_rings_cached; if (rings_cached < XHCI_MAX_RINGS_CACHED) { - virt_dev->num_rings_cached++; - rings_cached = virt_dev->num_rings_cached; virt_dev->ring_cache[rings_cached] = virt_dev->eps[ep_index].ring; + virt_dev->num_rings_cached++; xhci_dbg(xhci, "Cached old ring, " "%d ring%s cached\n", - rings_cached, - (rings_cached > 1) ? "s" : ""); + virt_dev->num_rings_cached, + (virt_dev->num_rings_cached > 1) ? "s" : ""); } else { xhci_ring_free(xhci, virt_dev->eps[ep_index].ring); xhci_dbg(xhci, "Ring cache full (%d rings), " @@ -548,7 +549,8 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci, addr = cur_ring->first_seg->dma | SCT_FOR_CTX(SCT_PRI_TR) | cur_ring->cycle_state; - stream_info->stream_ctx_array[cur_stream].stream_ring = addr; + stream_info->stream_ctx_array[cur_stream]. + stream_ring = cpu_to_le64(addr); xhci_dbg(xhci, "Setting stream %d ring ptr to 0x%08llx\n", cur_stream, (unsigned long long) addr); @@ -614,10 +616,10 @@ void xhci_setup_streams_ep_input_ctx(struct xhci_hcd *xhci, max_primary_streams = fls(stream_info->num_stream_ctxs) - 2; xhci_dbg(xhci, "Setting number of stream ctx array entries to %u\n", 1 << (max_primary_streams + 1)); - ep_ctx->ep_info &= ~EP_MAXPSTREAMS_MASK; - ep_ctx->ep_info |= EP_MAXPSTREAMS(max_primary_streams); - ep_ctx->ep_info |= EP_HAS_LSA; - ep_ctx->deq = stream_info->ctx_array_dma; + ep_ctx->ep_info &= cpu_to_le32(~EP_MAXPSTREAMS_MASK); + ep_ctx->ep_info |= cpu_to_le32(EP_MAXPSTREAMS(max_primary_streams) + | EP_HAS_LSA); + ep_ctx->deq = cpu_to_le64(stream_info->ctx_array_dma); } /* @@ -630,10 +632,9 @@ void xhci_setup_no_streams_ep_input_ctx(struct xhci_hcd *xhci, struct xhci_virt_ep *ep) { dma_addr_t addr; - ep_ctx->ep_info &= ~EP_MAXPSTREAMS_MASK; - ep_ctx->ep_info &= ~EP_HAS_LSA; + ep_ctx->ep_info &= cpu_to_le32(~(EP_MAXPSTREAMS_MASK | EP_HAS_LSA)); addr = xhci_trb_virt_to_dma(ep->ring->deq_seg, ep->ring->dequeue); - ep_ctx->deq = addr | ep->ring->cycle_state; + ep_ctx->deq = cpu_to_le64(addr | ep->ring->cycle_state); } /* Frees all stream contexts associated with the endpoint, @@ -781,11 +782,11 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id, dev->udev = udev; /* Point to output device context in dcbaa. */ - xhci->dcbaa->dev_context_ptrs[slot_id] = dev->out_ctx->dma; + xhci->dcbaa->dev_context_ptrs[slot_id] = cpu_to_le64(dev->out_ctx->dma); xhci_dbg(xhci, "Set slot id %d dcbaa entry %p to 0x%llx\n", - slot_id, - &xhci->dcbaa->dev_context_ptrs[slot_id], - (unsigned long long) xhci->dcbaa->dev_context_ptrs[slot_id]); + slot_id, + &xhci->dcbaa->dev_context_ptrs[slot_id], + (unsigned long long) le64_to_cpu(xhci->dcbaa->dev_context_ptrs[slot_id])); return 1; fail: @@ -810,8 +811,9 @@ void xhci_copy_ep0_dequeue_into_input_ctx(struct xhci_hcd *xhci, * configured device has reset, so all control transfers should have * been completed or cancelled before the reset. */ - ep0_ctx->deq = xhci_trb_virt_to_dma(ep_ring->enq_seg, ep_ring->enqueue); - ep0_ctx->deq |= ep_ring->cycle_state; + ep0_ctx->deq = cpu_to_le64(xhci_trb_virt_to_dma(ep_ring->enq_seg, + ep_ring->enqueue) + | ep_ring->cycle_state); } /* @@ -885,24 +887,22 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud slot_ctx = xhci_get_slot_ctx(xhci, dev->in_ctx); /* 2) New slot context and endpoint 0 context are valid*/ - ctrl_ctx->add_flags = SLOT_FLAG | EP0_FLAG; + ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG | EP0_FLAG); /* 3) Only the control endpoint is valid - one endpoint context */ - slot_ctx->dev_info |= LAST_CTX(1); - - slot_ctx->dev_info |= (u32) udev->route; + slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(1) | (u32) udev->route); switch (udev->speed) { case USB_SPEED_SUPER: - slot_ctx->dev_info |= (u32) SLOT_SPEED_SS; + slot_ctx->dev_info |= cpu_to_le32((u32) SLOT_SPEED_SS); break; case USB_SPEED_HIGH: - slot_ctx->dev_info |= (u32) SLOT_SPEED_HS; + slot_ctx->dev_info |= cpu_to_le32((u32) SLOT_SPEED_HS); break; case USB_SPEED_FULL: - slot_ctx->dev_info |= (u32) SLOT_SPEED_FS; + slot_ctx->dev_info |= cpu_to_le32((u32) SLOT_SPEED_FS); break; case USB_SPEED_LOW: - slot_ctx->dev_info |= (u32) SLOT_SPEED_LS; + slot_ctx->dev_info |= cpu_to_le32((u32) SLOT_SPEED_LS); break; case USB_SPEED_WIRELESS: xhci_dbg(xhci, "FIXME xHCI doesn't support wireless speeds\n"); @@ -916,7 +916,7 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud port_num = xhci_find_real_port_number(xhci, udev); if (!port_num) return -EINVAL; - slot_ctx->dev_info2 |= (u32) ROOT_HUB_PORT(port_num); + slot_ctx->dev_info2 |= cpu_to_le32((u32) ROOT_HUB_PORT(port_num)); /* Set the port number in the virtual_device to the faked port number */ for (top_dev = udev; top_dev->parent && top_dev->parent->parent; top_dev = top_dev->parent) @@ -927,31 +927,31 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud /* Is this a LS/FS device under an external HS hub? */ if (udev->tt && udev->tt->hub->parent) { - slot_ctx->tt_info = udev->tt->hub->slot_id; - slot_ctx->tt_info |= udev->ttport << 8; + slot_ctx->tt_info = cpu_to_le32(udev->tt->hub->slot_id | + (udev->ttport << 8)); if (udev->tt->multi) - slot_ctx->dev_info |= DEV_MTT; + slot_ctx->dev_info |= cpu_to_le32(DEV_MTT); } xhci_dbg(xhci, "udev->tt = %p\n", udev->tt); xhci_dbg(xhci, "udev->ttport = 0x%x\n", udev->ttport); /* Step 4 - ring already allocated */ /* Step 5 */ - ep0_ctx->ep_info2 = EP_TYPE(CTRL_EP); + ep0_ctx->ep_info2 = cpu_to_le32(EP_TYPE(CTRL_EP)); /* * XXX: Not sure about wireless USB devices. */ switch (udev->speed) { case USB_SPEED_SUPER: - ep0_ctx->ep_info2 |= MAX_PACKET(512); + ep0_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(512)); break; case USB_SPEED_HIGH: /* USB core guesses at a 64-byte max packet first for FS devices */ case USB_SPEED_FULL: - ep0_ctx->ep_info2 |= MAX_PACKET(64); + ep0_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(64)); break; case USB_SPEED_LOW: - ep0_ctx->ep_info2 |= MAX_PACKET(8); + ep0_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(8)); break; case USB_SPEED_WIRELESS: xhci_dbg(xhci, "FIXME xHCI doesn't support wireless speeds\n"); @@ -962,12 +962,10 @@ int xhci_setup_addressable_virt_dev(struct xhci_hcd *xhci, struct usb_device *ud BUG(); } /* EP 0 can handle "burst" sizes of 1, so Max Burst Size field is 0 */ - ep0_ctx->ep_info2 |= MAX_BURST(0); - ep0_ctx->ep_info2 |= ERROR_COUNT(3); + ep0_ctx->ep_info2 |= cpu_to_le32(MAX_BURST(0) | ERROR_COUNT(3)); - ep0_ctx->deq = - dev->eps[0].ring->first_seg->dma; - ep0_ctx->deq |= dev->eps[0].ring->cycle_state; + ep0_ctx->deq = cpu_to_le64(dev->eps[0].ring->first_seg->dma | + dev->eps[0].ring->cycle_state); /* Steps 7 and 8 were done in xhci_alloc_virt_device() */ @@ -1046,12 +1044,12 @@ static unsigned int xhci_get_endpoint_interval(struct usb_device *udev, break; case USB_SPEED_FULL: - if (usb_endpoint_xfer_int(&ep->desc)) { + if (usb_endpoint_xfer_isoc(&ep->desc)) { interval = xhci_parse_exponent_interval(udev, ep); break; } /* - * Fall through for isochronous endpoint interval decoding + * Fall through for interrupt endpoint interval decoding * since it uses the same rules as low speed interrupt * endpoints. */ @@ -1131,10 +1129,10 @@ static u32 xhci_get_max_esit_payload(struct xhci_hcd *xhci, return 0; if (udev->speed == USB_SPEED_SUPER) - return ep->ss_ep_comp.wBytesPerInterval; + return le16_to_cpu(ep->ss_ep_comp.wBytesPerInterval); - max_packet = GET_MAX_PACKET(ep->desc.wMaxPacketSize); - max_burst = (ep->desc.wMaxPacketSize & 0x1800) >> 11; + max_packet = GET_MAX_PACKET(le16_to_cpu(ep->desc.wMaxPacketSize)); + max_burst = (le16_to_cpu(ep->desc.wMaxPacketSize) & 0x1800) >> 11; /* A 0 in max burst means 1 transfer per ESIT */ return max_packet * (max_burst + 1); } @@ -1183,33 +1181,33 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, } virt_dev->eps[ep_index].skip = false; ep_ring = virt_dev->eps[ep_index].new_ring; - ep_ctx->deq = ep_ring->first_seg->dma | ep_ring->cycle_state; + ep_ctx->deq = cpu_to_le64(ep_ring->first_seg->dma | ep_ring->cycle_state); - ep_ctx->ep_info = xhci_get_endpoint_interval(udev, ep); - ep_ctx->ep_info |= EP_MULT(xhci_get_endpoint_mult(udev, ep)); + ep_ctx->ep_info = cpu_to_le32(xhci_get_endpoint_interval(udev, ep) + | EP_MULT(xhci_get_endpoint_mult(udev, ep))); /* FIXME dig Mult and streams info out of ep companion desc */ /* Allow 3 retries for everything but isoc; - * error count = 0 means infinite retries. + * CErr shall be set to 0 for Isoch endpoints. */ if (!usb_endpoint_xfer_isoc(&ep->desc)) - ep_ctx->ep_info2 = ERROR_COUNT(3); + ep_ctx->ep_info2 = cpu_to_le32(ERROR_COUNT(3)); else - ep_ctx->ep_info2 = ERROR_COUNT(1); + ep_ctx->ep_info2 = cpu_to_le32(ERROR_COUNT(0)); - ep_ctx->ep_info2 |= xhci_get_endpoint_type(udev, ep); + ep_ctx->ep_info2 |= cpu_to_le32(xhci_get_endpoint_type(udev, ep)); /* Set the max packet size and max burst */ switch (udev->speed) { case USB_SPEED_SUPER: - max_packet = ep->desc.wMaxPacketSize; - ep_ctx->ep_info2 |= MAX_PACKET(max_packet); + max_packet = le16_to_cpu(ep->desc.wMaxPacketSize); + ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet)); /* dig out max burst from ep companion desc */ max_packet = ep->ss_ep_comp.bMaxBurst; if (!max_packet) xhci_warn(xhci, "WARN no SS endpoint bMaxBurst\n"); - ep_ctx->ep_info2 |= MAX_BURST(max_packet); + ep_ctx->ep_info2 |= cpu_to_le32(MAX_BURST(max_packet)); break; case USB_SPEED_HIGH: /* bits 11:12 specify the number of additional transaction @@ -1217,20 +1215,21 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, */ if (usb_endpoint_xfer_isoc(&ep->desc) || usb_endpoint_xfer_int(&ep->desc)) { - max_burst = (ep->desc.wMaxPacketSize & 0x1800) >> 11; - ep_ctx->ep_info2 |= MAX_BURST(max_burst); + max_burst = (le16_to_cpu(ep->desc.wMaxPacketSize) + & 0x1800) >> 11; + ep_ctx->ep_info2 |= cpu_to_le32(MAX_BURST(max_burst)); } /* Fall through */ case USB_SPEED_FULL: case USB_SPEED_LOW: - max_packet = GET_MAX_PACKET(ep->desc.wMaxPacketSize); - ep_ctx->ep_info2 |= MAX_PACKET(max_packet); + max_packet = GET_MAX_PACKET(le16_to_cpu(ep->desc.wMaxPacketSize)); + ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet)); break; default: BUG(); } max_esit_payload = xhci_get_max_esit_payload(xhci, udev, ep); - ep_ctx->tx_info = MAX_ESIT_PAYLOAD_FOR_EP(max_esit_payload); + ep_ctx->tx_info = cpu_to_le32(MAX_ESIT_PAYLOAD_FOR_EP(max_esit_payload)); /* * XXX no idea how to calculate the average TRB buffer length for bulk @@ -1246,8 +1245,15 @@ int xhci_endpoint_init(struct xhci_hcd *xhci, * including link TRBs, No-op TRBs, and Event data TRBs. Since we don't * use Event Data TRBs, and we don't chain in a link TRB on short * transfers, we're basically dividing by 1. + * + * xHCI 1.0 specification indicates that the Average TRB Length should + * be set to 8 for control endpoints. */ - ep_ctx->tx_info |= AVG_TRB_LENGTH_FOR_EP(max_esit_payload); + if (usb_endpoint_xfer_control(&ep->desc) && xhci->hci_version == 0x100) + ep_ctx->tx_info |= cpu_to_le32(AVG_TRB_LENGTH_FOR_EP(8)); + else + ep_ctx->tx_info |= + cpu_to_le32(AVG_TRB_LENGTH_FOR_EP(max_esit_payload)); /* FIXME Debug endpoint context */ return 0; @@ -1347,7 +1353,7 @@ static int scratchpad_alloc(struct xhci_hcd *xhci, gfp_t flags) if (!xhci->scratchpad->sp_dma_buffers) goto fail_sp4; - xhci->dcbaa->dev_context_ptrs[0] = xhci->scratchpad->sp_dma; + xhci->dcbaa->dev_context_ptrs[0] = cpu_to_le64(xhci->scratchpad->sp_dma); for (i = 0; i < num_sp; i++) { dma_addr_t dma; void *buf = pci_alloc_consistent(to_pci_dev(dev), @@ -1724,7 +1730,7 @@ static void xhci_set_hc_event_deq(struct xhci_hcd *xhci) } static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, - u32 __iomem *addr, u8 major_revision) + __le32 __iomem *addr, u8 major_revision) { u32 temp, port_offset, port_count; int i; @@ -1789,7 +1795,7 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, */ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags) { - u32 __iomem *addr; + __le32 __iomem *addr; u32 offset; unsigned int num_ports; int i, port_index; @@ -2042,8 +2048,8 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags) /* set ring base address and size for each segment table entry */ for (val = 0, seg = xhci->event_ring->first_seg; val < ERST_NUM_SEGS; val++) { struct xhci_erst_entry *entry = &xhci->erst.entries[val]; - entry->seg_addr = seg->dma; - entry->seg_size = TRBS_PER_SEGMENT; + entry->seg_addr = cpu_to_le64(seg->dma); + entry->seg_size = cpu_to_le32(TRBS_PER_SEGMENT); entry->rsvd = 0; seg = seg->next; } diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index a10494c..cbc4d49 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -21,6 +21,7 @@ */ #include <linux/pci.h> +#include <linux/slab.h> #include "xhci.h" diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 7437386..237a765 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -100,7 +100,7 @@ static bool last_trb_on_last_seg(struct xhci_hcd *xhci, struct xhci_ring *ring, return (trb == &seg->trbs[TRBS_PER_SEGMENT]) && (seg->next == xhci->event_ring->first_seg); else - return trb->link.control & LINK_TOGGLE; + return le32_to_cpu(trb->link.control) & LINK_TOGGLE; } /* Is this TRB a link TRB or was the last TRB the last TRB in this event ring @@ -113,13 +113,15 @@ static int last_trb(struct xhci_hcd *xhci, struct xhci_ring *ring, if (ring == xhci->event_ring) return trb == &seg->trbs[TRBS_PER_SEGMENT]; else - return (trb->link.control & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK); + return (le32_to_cpu(trb->link.control) & TRB_TYPE_BITMASK) + == TRB_TYPE(TRB_LINK); } static int enqueue_is_link_trb(struct xhci_ring *ring) { struct xhci_link_trb *link = &ring->enqueue->link; - return ((link->control & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK)); + return ((le32_to_cpu(link->control) & TRB_TYPE_BITMASK) == + TRB_TYPE(TRB_LINK)); } /* Updates trb to point to the next TRB in the ring, and updates seg if the next @@ -197,7 +199,7 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, union xhci_trb *next; unsigned long long addr; - chain = ring->enqueue->generic.field[3] & TRB_CHAIN; + chain = le32_to_cpu(ring->enqueue->generic.field[3]) & TRB_CHAIN; next = ++(ring->enqueue); ring->enq_updates++; @@ -223,12 +225,14 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhci_ring *ring, * (which may mean the chain bit is cleared). */ if (!xhci_link_trb_quirk(xhci)) { - next->link.control &= ~TRB_CHAIN; - next->link.control |= chain; + next->link.control &= + cpu_to_le32(~TRB_CHAIN); + next->link.control |= + cpu_to_le32(chain); } /* Give this link TRB to the hardware */ wmb(); - next->link.control ^= TRB_CYCLE; + next->link.control ^= cpu_to_le32(TRB_CYCLE); } /* Toggle the cycle bit after the last ring segment. */ if (last_trb_on_last_seg(xhci, ring, ring->enq_seg, next)) { @@ -319,7 +323,7 @@ void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int ep_index, unsigned int stream_id) { - __u32 __iomem *db_addr = &xhci->dba->doorbell[slot_id]; + __le32 __iomem *db_addr = &xhci->dba->doorbell[slot_id]; struct xhci_virt_ep *ep = &xhci->devs[slot_id]->eps[ep_index]; unsigned int ep_state = ep->ep_state; @@ -380,7 +384,7 @@ static struct xhci_segment *find_trb_seg( while (cur_seg->trbs > trb || &cur_seg->trbs[TRBS_PER_SEGMENT - 1] < trb) { generic_trb = &cur_seg->trbs[TRBS_PER_SEGMENT - 1].generic; - if (generic_trb->field[3] & LINK_TOGGLE) + if (le32_to_cpu(generic_trb->field[3]) & LINK_TOGGLE) *cycle_state ^= 0x1; cur_seg = cur_seg->next; if (cur_seg == start_seg) @@ -447,6 +451,10 @@ static struct xhci_ring *xhci_urb_to_transfer_ring(struct xhci_hcd *xhci, * any link TRBs with the toggle cycle bit set. * - Finally we move the dequeue state one TRB further, toggling the cycle bit * if we've moved it past a link TRB with the toggle cycle bit set. + * + * Some of the uses of xhci_generic_trb are grotty, but if they're done + * with correct __le32 accesses they should work fine. Only users of this are + * in here. */ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, unsigned int slot_id, unsigned int ep_index, @@ -480,7 +488,7 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, /* Dig out the cycle state saved by the xHC during the stop ep cmd */ xhci_dbg(xhci, "Finding endpoint context\n"); ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index); - state->new_cycle_state = 0x1 & ep_ctx->deq; + state->new_cycle_state = 0x1 & le64_to_cpu(ep_ctx->deq); state->new_deq_ptr = cur_td->last_trb; xhci_dbg(xhci, "Finding segment containing last TRB in TD.\n"); @@ -493,8 +501,8 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, } trb = &state->new_deq_ptr->generic; - if ((trb->field[3] & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK) && - (trb->field[3] & LINK_TOGGLE)) + if ((le32_to_cpu(trb->field[3]) & TRB_TYPE_BITMASK) == + TRB_TYPE(TRB_LINK) && (le32_to_cpu(trb->field[3]) & LINK_TOGGLE)) state->new_cycle_state ^= 0x1; next_trb(xhci, ep_ring, &state->new_deq_seg, &state->new_deq_ptr); @@ -529,12 +537,12 @@ static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, for (cur_seg = cur_td->start_seg, cur_trb = cur_td->first_trb; true; next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) { - if ((cur_trb->generic.field[3] & TRB_TYPE_BITMASK) == - TRB_TYPE(TRB_LINK)) { + if ((le32_to_cpu(cur_trb->generic.field[3]) & TRB_TYPE_BITMASK) + == TRB_TYPE(TRB_LINK)) { /* Unchain any chained Link TRBs, but * leave the pointers intact. */ - cur_trb->generic.field[3] &= ~TRB_CHAIN; + cur_trb->generic.field[3] &= cpu_to_le32(~TRB_CHAIN); xhci_dbg(xhci, "Cancel (unchain) link TRB\n"); xhci_dbg(xhci, "Address = %p (0x%llx dma); " "in seg %p (0x%llx dma)\n", @@ -547,8 +555,9 @@ static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, cur_trb->generic.field[1] = 0; cur_trb->generic.field[2] = 0; /* Preserve only the cycle bit of this TRB */ - cur_trb->generic.field[3] &= TRB_CYCLE; - cur_trb->generic.field[3] |= TRB_TYPE(TRB_TR_NOOP); + cur_trb->generic.field[3] &= cpu_to_le32(TRB_CYCLE); + cur_trb->generic.field[3] |= cpu_to_le32( + TRB_TYPE(TRB_TR_NOOP)); xhci_dbg(xhci, "Cancel TRB %p (0x%llx dma) " "in seg %p (0x%llx dma)\n", cur_trb, @@ -662,9 +671,9 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci, struct xhci_dequeue_state deq_state; if (unlikely(TRB_TO_SUSPEND_PORT( - xhci->cmd_ring->dequeue->generic.field[3]))) { + le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3])))) { slot_id = TRB_TO_SLOT_ID( - xhci->cmd_ring->dequeue->generic.field[3]); + le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3])); virt_dev = xhci->devs[slot_id]; if (virt_dev) handle_cmd_in_cmd_wait_list(xhci, virt_dev, @@ -677,8 +686,8 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci, } memset(&deq_state, 0, sizeof(deq_state)); - slot_id = TRB_TO_SLOT_ID(trb->generic.field[3]); - ep_index = TRB_TO_EP_INDEX(trb->generic.field[3]); + slot_id = TRB_TO_SLOT_ID(le32_to_cpu(trb->generic.field[3])); + ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3])); ep = &xhci->devs[slot_id]->eps[ep_index]; if (list_empty(&ep->cancelled_td_list)) { @@ -910,9 +919,9 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci, struct xhci_ep_ctx *ep_ctx; struct xhci_slot_ctx *slot_ctx; - slot_id = TRB_TO_SLOT_ID(trb->generic.field[3]); - ep_index = TRB_TO_EP_INDEX(trb->generic.field[3]); - stream_id = TRB_TO_STREAM_ID(trb->generic.field[2]); + slot_id = TRB_TO_SLOT_ID(le32_to_cpu(trb->generic.field[3])); + ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3])); + stream_id = TRB_TO_STREAM_ID(le32_to_cpu(trb->generic.field[2])); dev = xhci->devs[slot_id]; ep_ring = xhci_stream_id_to_ring(dev, ep_index, stream_id); @@ -928,11 +937,11 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci, ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index); slot_ctx = xhci_get_slot_ctx(xhci, dev->out_ctx); - if (GET_COMP_CODE(event->status) != COMP_SUCCESS) { + if (GET_COMP_CODE(le32_to_cpu(event->status)) != COMP_SUCCESS) { unsigned int ep_state; unsigned int slot_state; - switch (GET_COMP_CODE(event->status)) { + switch (GET_COMP_CODE(le32_to_cpu(event->status))) { case COMP_TRB_ERR: xhci_warn(xhci, "WARN Set TR Deq Ptr cmd invalid because " "of stream ID configuration\n"); @@ -940,9 +949,9 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci, case COMP_CTX_STATE: xhci_warn(xhci, "WARN Set TR Deq Ptr cmd failed due " "to incorrect slot or ep state.\n"); - ep_state = ep_ctx->ep_info; + ep_state = le32_to_cpu(ep_ctx->ep_info); ep_state &= EP_STATE_MASK; - slot_state = slot_ctx->dev_state; + slot_state = le32_to_cpu(slot_ctx->dev_state); slot_state = GET_SLOT_STATE(slot_state); xhci_dbg(xhci, "Slot state = %u, EP state = %u\n", slot_state, ep_state); @@ -954,7 +963,7 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci, default: xhci_warn(xhci, "WARN Set TR Deq Ptr cmd with unknown " "completion code of %u.\n", - GET_COMP_CODE(event->status)); + GET_COMP_CODE(le32_to_cpu(event->status))); break; } /* OK what do we do now? The endpoint state is hosed, and we @@ -965,10 +974,10 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci, */ } else { xhci_dbg(xhci, "Successful Set TR Deq Ptr cmd, deq = @%08llx\n", - ep_ctx->deq); + le64_to_cpu(ep_ctx->deq)); if (xhci_trb_virt_to_dma(dev->eps[ep_index].queued_deq_seg, - dev->eps[ep_index].queued_deq_ptr) == - (ep_ctx->deq & ~(EP_CTX_CYCLE_MASK))) { + dev->eps[ep_index].queued_deq_ptr) == + (le64_to_cpu(ep_ctx->deq) & ~(EP_CTX_CYCLE_MASK))) { /* Update the ring's dequeue segment and dequeue pointer * to reflect the new position. */ @@ -997,13 +1006,13 @@ static void handle_reset_ep_completion(struct xhci_hcd *xhci, int slot_id; unsigned int ep_index; - slot_id = TRB_TO_SLOT_ID(trb->generic.field[3]); - ep_index = TRB_TO_EP_INDEX(trb->generic.field[3]); + slot_id = TRB_TO_SLOT_ID(le32_to_cpu(trb->generic.field[3])); + ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3])); /* This command will only fail if the endpoint wasn't halted, * but we don't care. */ xhci_dbg(xhci, "Ignoring reset ep completion code of %u\n", - (unsigned int) GET_COMP_CODE(event->status)); + (unsigned int) GET_COMP_CODE(le32_to_cpu(event->status))); /* HW with the reset endpoint quirk needs to have a configure endpoint * command complete before the endpoint can be used. Queue that here @@ -1040,8 +1049,7 @@ static int handle_cmd_in_cmd_wait_list(struct xhci_hcd *xhci, if (xhci->cmd_ring->dequeue != command->command_trb) return 0; - command->status = - GET_COMP_CODE(event->status); + command->status = GET_COMP_CODE(le32_to_cpu(event->status)); list_del(&command->cmd_list); if (command->completion) complete(command->completion); @@ -1053,7 +1061,7 @@ static int handle_cmd_in_cmd_wait_list(struct xhci_hcd *xhci, static void handle_cmd_completion(struct xhci_hcd *xhci, struct xhci_event_cmd *event) { - int slot_id = TRB_TO_SLOT_ID(event->flags); + int slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags)); u64 cmd_dma; dma_addr_t cmd_dequeue_dma; struct xhci_input_control_ctx *ctrl_ctx; @@ -1062,7 +1070,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, struct xhci_ring *ep_ring; unsigned int ep_state; - cmd_dma = event->cmd_trb; + cmd_dma = le64_to_cpu(event->cmd_trb); cmd_dequeue_dma = xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg, xhci->cmd_ring->dequeue); /* Is the command ring deq ptr out of sync with the deq seg ptr? */ @@ -1075,9 +1083,10 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, xhci->error_bitmask |= 1 << 5; return; } - switch (xhci->cmd_ring->dequeue->generic.field[3] & TRB_TYPE_BITMASK) { + switch (le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3]) + & TRB_TYPE_BITMASK) { case TRB_TYPE(TRB_ENABLE_SLOT): - if (GET_COMP_CODE(event->status) == COMP_SUCCESS) + if (GET_COMP_CODE(le32_to_cpu(event->status)) == COMP_SUCCESS) xhci->slot_id = slot_id; else xhci->slot_id = 0; @@ -1102,7 +1111,7 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, ctrl_ctx = xhci_get_input_control_ctx(xhci, virt_dev->in_ctx); /* Input ctx add_flags are the endpoint index plus one */ - ep_index = xhci_last_valid_endpoint(ctrl_ctx->add_flags) - 1; + ep_index = xhci_last_valid_endpoint(le32_to_cpu(ctrl_ctx->add_flags)) - 1; /* A usb_set_interface() call directly after clearing a halted * condition may race on this quirky hardware. Not worth * worrying about, since this is prototype hardware. Not sure @@ -1111,8 +1120,8 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, */ if (xhci->quirks & XHCI_RESET_EP_QUIRK && ep_index != (unsigned int) -1 && - ctrl_ctx->add_flags - SLOT_FLAG == - ctrl_ctx->drop_flags) { + le32_to_cpu(ctrl_ctx->add_flags) - SLOT_FLAG == + le32_to_cpu(ctrl_ctx->drop_flags)) { ep_ring = xhci->devs[slot_id]->eps[ep_index].ring; ep_state = xhci->devs[slot_id]->eps[ep_index].ep_state; if (!(ep_state & EP_HALTED)) @@ -1129,18 +1138,18 @@ static void handle_cmd_completion(struct xhci_hcd *xhci, bandwidth_change: xhci_dbg(xhci, "Completed config ep cmd\n"); xhci->devs[slot_id]->cmd_status = - GET_COMP_CODE(event->status); + GET_COMP_CODE(le32_to_cpu(event->status)); complete(&xhci->devs[slot_id]->cmd_completion); break; case TRB_TYPE(TRB_EVAL_CONTEXT): virt_dev = xhci->devs[slot_id]; if (handle_cmd_in_cmd_wait_list(xhci, virt_dev, event)) break; - xhci->devs[slot_id]->cmd_status = GET_COMP_CODE(event->status); + xhci->devs[slot_id]->cmd_status = GET_COMP_CODE(le32_to_cpu(event->status)); complete(&xhci->devs[slot_id]->cmd_completion); break; case TRB_TYPE(TRB_ADDR_DEV): - xhci->devs[slot_id]->cmd_status = GET_COMP_CODE(event->status); + xhci->devs[slot_id]->cmd_status = GET_COMP_CODE(le32_to_cpu(event->status)); complete(&xhci->addr_dev); break; case TRB_TYPE(TRB_STOP_RING): @@ -1157,7 +1166,7 @@ bandwidth_change: case TRB_TYPE(TRB_RESET_DEV): xhci_dbg(xhci, "Completed reset device command.\n"); slot_id = TRB_TO_SLOT_ID( - xhci->cmd_ring->dequeue->generic.field[3]); + le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3])); virt_dev = xhci->devs[slot_id]; if (virt_dev) handle_cmd_in_cmd_wait_list(xhci, virt_dev, event); @@ -1171,8 +1180,8 @@ bandwidth_change: break; } xhci_dbg(xhci, "NEC firmware version %2x.%02x\n", - NEC_FW_MAJOR(event->status), - NEC_FW_MINOR(event->status)); + NEC_FW_MAJOR(le32_to_cpu(event->status)), + NEC_FW_MINOR(le32_to_cpu(event->status))); break; default: /* Skip over unknown commands on the event ring */ @@ -1187,7 +1196,7 @@ static void handle_vendor_event(struct xhci_hcd *xhci, { u32 trb_type; - trb_type = TRB_FIELD_TO_TYPE(event->generic.field[3]); + trb_type = TRB_FIELD_TO_TYPE(le32_to_cpu(event->generic.field[3])); xhci_dbg(xhci, "Vendor specific event TRB type = %u\n", trb_type); if (trb_type == TRB_NEC_CMD_COMP && (xhci->quirks & XHCI_NEC_HOST)) handle_cmd_completion(xhci, &event->event_cmd); @@ -1241,15 +1250,15 @@ static void handle_port_status(struct xhci_hcd *xhci, unsigned int faked_port_index; u8 major_revision; struct xhci_bus_state *bus_state; - u32 __iomem **port_array; + __le32 __iomem **port_array; bool bogus_port_status = false; /* Port status change events always have a successful completion code */ - if (GET_COMP_CODE(event->generic.field[2]) != COMP_SUCCESS) { + if (GET_COMP_CODE(le32_to_cpu(event->generic.field[2])) != COMP_SUCCESS) { xhci_warn(xhci, "WARN: xHC returned failed port status event\n"); xhci->error_bitmask |= 1 << 8; } - port_id = GET_PORT_ID(event->generic.field[0]); + port_id = GET_PORT_ID(le32_to_cpu(event->generic.field[0])); xhci_dbg(xhci, "Port Status Change Event for port %d\n", port_id); max_ports = HCS_MAX_PORTS(xhci->hcs_params1); @@ -1456,7 +1465,7 @@ static int xhci_requires_manual_halt_cleanup(struct xhci_hcd *xhci, * endpoint anyway. Check if a babble halted the * endpoint. */ - if ((ep_ctx->ep_info & EP_STATE_MASK) == EP_STATE_HALTED) + if ((le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK) == EP_STATE_HALTED) return 1; return 0; @@ -1494,12 +1503,12 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td, struct urb_priv *urb_priv; u32 trb_comp_code; - slot_id = TRB_TO_SLOT_ID(event->flags); + slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags)); xdev = xhci->devs[slot_id]; - ep_index = TRB_TO_EP_ID(event->flags) - 1; - ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer); + ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1; + ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer)); ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index); - trb_comp_code = GET_COMP_CODE(event->transfer_len); + trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len)); if (skip) goto td_cleanup; @@ -1602,12 +1611,12 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, struct xhci_ep_ctx *ep_ctx; u32 trb_comp_code; - slot_id = TRB_TO_SLOT_ID(event->flags); + slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags)); xdev = xhci->devs[slot_id]; - ep_index = TRB_TO_EP_ID(event->flags) - 1; - ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer); + ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1; + ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer)); ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index); - trb_comp_code = GET_COMP_CODE(event->transfer_len); + trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len)); xhci_debug_trb(xhci, xhci->event_ring->dequeue); switch (trb_comp_code) { @@ -1632,6 +1641,9 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, else *status = 0; break; + case COMP_STOP_INVAL: + case COMP_STOP: + return finish_td(xhci, td, event_trb, event, ep, status, false); default: if (!xhci_requires_manual_halt_cleanup(xhci, ep_ctx, trb_comp_code)) @@ -1646,7 +1658,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, event_trb != td->last_trb) td->urb->actual_length = td->urb->transfer_buffer_length - - TRB_LEN(event->transfer_len); + - TRB_LEN(le32_to_cpu(event->transfer_len)); else td->urb->actual_length = 0; @@ -1676,15 +1688,12 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, } } else { /* Maybe the event was for the data stage? */ - if (trb_comp_code != COMP_STOP_INVAL) { - /* We didn't stop on a link TRB in the middle */ - td->urb->actual_length = - td->urb->transfer_buffer_length - - TRB_LEN(event->transfer_len); - xhci_dbg(xhci, "Waiting for status " - "stage event\n"); - return 0; - } + td->urb->actual_length = + td->urb->transfer_buffer_length - + TRB_LEN(le32_to_cpu(event->transfer_len)); + xhci_dbg(xhci, "Waiting for status " + "stage event\n"); + return 0; } } @@ -1708,8 +1717,8 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, u32 trb_comp_code; bool skip_td = false; - ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer); - trb_comp_code = GET_COMP_CODE(event->transfer_len); + ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer)); + trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len)); urb_priv = td->urb->hcpriv; idx = urb_priv->td_cnt; frame = &td->urb->iso_frame_desc[idx]; @@ -1752,15 +1761,14 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, for (cur_trb = ep_ring->dequeue, cur_seg = ep_ring->deq_seg; cur_trb != event_trb; next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) { - if ((cur_trb->generic.field[3] & + if ((le32_to_cpu(cur_trb->generic.field[3]) & TRB_TYPE_BITMASK) != TRB_TYPE(TRB_TR_NOOP) && - (cur_trb->generic.field[3] & + (le32_to_cpu(cur_trb->generic.field[3]) & TRB_TYPE_BITMASK) != TRB_TYPE(TRB_LINK)) - len += - TRB_LEN(cur_trb->generic.field[2]); + len += TRB_LEN(le32_to_cpu(cur_trb->generic.field[2])); } - len += TRB_LEN(cur_trb->generic.field[2]) - - TRB_LEN(event->transfer_len); + len += TRB_LEN(le32_to_cpu(cur_trb->generic.field[2])) - + TRB_LEN(le32_to_cpu(event->transfer_len)); if (trb_comp_code != COMP_STOP_INVAL) { frame->actual_length = len; @@ -1815,8 +1823,8 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td, struct xhci_segment *cur_seg; u32 trb_comp_code; - ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer); - trb_comp_code = GET_COMP_CODE(event->transfer_len); + ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer)); + trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len)); switch (trb_comp_code) { case COMP_SUCCESS: @@ -1852,18 +1860,18 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td, "%d bytes untransferred\n", td->urb->ep->desc.bEndpointAddress, td->urb->transfer_buffer_length, - TRB_LEN(event->transfer_len)); + TRB_LEN(le32_to_cpu(event->transfer_len))); /* Fast path - was this the last TRB in the TD for this URB? */ if (event_trb == td->last_trb) { - if (TRB_LEN(event->transfer_len) != 0) { + if (TRB_LEN(le32_to_cpu(event->transfer_len)) != 0) { td->urb->actual_length = td->urb->transfer_buffer_length - - TRB_LEN(event->transfer_len); + TRB_LEN(le32_to_cpu(event->transfer_len)); if (td->urb->transfer_buffer_length < td->urb->actual_length) { xhci_warn(xhci, "HC gave bad length " "of %d bytes left\n", - TRB_LEN(event->transfer_len)); + TRB_LEN(le32_to_cpu(event->transfer_len))); td->urb->actual_length = 0; if (td->urb->transfer_flags & URB_SHORT_NOT_OK) *status = -EREMOTEIO; @@ -1894,20 +1902,20 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td, for (cur_trb = ep_ring->dequeue, cur_seg = ep_ring->deq_seg; cur_trb != event_trb; next_trb(xhci, ep_ring, &cur_seg, &cur_trb)) { - if ((cur_trb->generic.field[3] & + if ((le32_to_cpu(cur_trb->generic.field[3]) & TRB_TYPE_BITMASK) != TRB_TYPE(TRB_TR_NOOP) && - (cur_trb->generic.field[3] & + (le32_to_cpu(cur_trb->generic.field[3]) & TRB_TYPE_BITMASK) != TRB_TYPE(TRB_LINK)) td->urb->actual_length += - TRB_LEN(cur_trb->generic.field[2]); + TRB_LEN(le32_to_cpu(cur_trb->generic.field[2])); } /* If the ring didn't stop on a Link or No-op TRB, add * in the actual bytes transferred from the Normal TRB */ if (trb_comp_code != COMP_STOP_INVAL) td->urb->actual_length += - TRB_LEN(cur_trb->generic.field[2]) - - TRB_LEN(event->transfer_len); + TRB_LEN(le32_to_cpu(cur_trb->generic.field[2])) - + TRB_LEN(le32_to_cpu(event->transfer_len)); } return finish_td(xhci, td, event_trb, event, ep, status, false); @@ -1937,7 +1945,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, u32 trb_comp_code; int ret = 0; - slot_id = TRB_TO_SLOT_ID(event->flags); + slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags)); xdev = xhci->devs[slot_id]; if (!xdev) { xhci_err(xhci, "ERROR Transfer event pointed to bad slot\n"); @@ -1945,20 +1953,21 @@ static int handle_tx_event(struct xhci_hcd *xhci, } /* Endpoint ID is 1 based, our index is zero based */ - ep_index = TRB_TO_EP_ID(event->flags) - 1; + ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1; xhci_dbg(xhci, "%s - ep index = %d\n", __func__, ep_index); ep = &xdev->eps[ep_index]; - ep_ring = xhci_dma_to_transfer_ring(ep, event->buffer); + ep_ring = xhci_dma_to_transfer_ring(ep, le64_to_cpu(event->buffer)); ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index); if (!ep_ring || - (ep_ctx->ep_info & EP_STATE_MASK) == EP_STATE_DISABLED) { + (le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK) == + EP_STATE_DISABLED) { xhci_err(xhci, "ERROR Transfer event for disabled endpoint " "or incorrect stream ring\n"); return -ENODEV; } - event_dma = event->buffer; - trb_comp_code = GET_COMP_CODE(event->transfer_len); + event_dma = le64_to_cpu(event->buffer); + trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len)); /* Look for common error cases */ switch (trb_comp_code) { /* Skip codes that require special handling depending on @@ -2011,14 +2020,16 @@ static int handle_tx_event(struct xhci_hcd *xhci, if (!list_empty(&ep_ring->td_list)) xhci_dbg(xhci, "Underrun Event for slot %d ep %d " "still with TDs queued?\n", - TRB_TO_SLOT_ID(event->flags), ep_index); + TRB_TO_SLOT_ID(le32_to_cpu(event->flags)), + ep_index); goto cleanup; case COMP_OVERRUN: xhci_dbg(xhci, "overrun event on endpoint\n"); if (!list_empty(&ep_ring->td_list)) xhci_dbg(xhci, "Overrun Event for slot %d ep %d " "still with TDs queued?\n", - TRB_TO_SLOT_ID(event->flags), ep_index); + TRB_TO_SLOT_ID(le32_to_cpu(event->flags)), + ep_index); goto cleanup; case COMP_MISSED_INT: /* @@ -2047,9 +2058,11 @@ static int handle_tx_event(struct xhci_hcd *xhci, if (list_empty(&ep_ring->td_list)) { xhci_warn(xhci, "WARN Event TRB for slot %d ep %d " "with no TDs queued?\n", - TRB_TO_SLOT_ID(event->flags), ep_index); + TRB_TO_SLOT_ID(le32_to_cpu(event->flags)), + ep_index); xhci_dbg(xhci, "Event TRB with TRB type ID %u\n", - (unsigned int) (event->flags & TRB_TYPE_BITMASK)>>10); + (unsigned int) (le32_to_cpu(event->flags) + & TRB_TYPE_BITMASK)>>10); xhci_print_trb_offsets(xhci, (union xhci_trb *) event); if (ep->skip) { ep->skip = false; @@ -2092,7 +2105,8 @@ static int handle_tx_event(struct xhci_hcd *xhci, * corresponding TD has been cancelled. Just ignore * the TD. */ - if ((event_trb->generic.field[3] & TRB_TYPE_BITMASK) + if ((le32_to_cpu(event_trb->generic.field[3]) + & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_TR_NOOP)) { xhci_dbg(xhci, "event_trb is a no-op TRB. Skip it\n"); @@ -2157,8 +2171,10 @@ cleanup: /* * This function handles all OS-owned events on the event ring. It may drop * xhci->lock between event processing (e.g. to pass up port status changes). + * Returns >0 for "possibly more events to process" (caller should call again), + * otherwise 0 if done. In future, <0 returns should indicate error code. */ -static void xhci_handle_event(struct xhci_hcd *xhci) +static int xhci_handle_event(struct xhci_hcd *xhci) { union xhci_trb *event; int update_ptrs = 1; @@ -2167,20 +2183,25 @@ static void xhci_handle_event(struct xhci_hcd *xhci) xhci_dbg(xhci, "In %s\n", __func__); if (!xhci->event_ring || !xhci->event_ring->dequeue) { xhci->error_bitmask |= 1 << 1; - return; + return 0; } event = xhci->event_ring->dequeue; /* Does the HC or OS own the TRB? */ - if ((event->event_cmd.flags & TRB_CYCLE) != - xhci->event_ring->cycle_state) { + if ((le32_to_cpu(event->event_cmd.flags) & TRB_CYCLE) != + xhci->event_ring->cycle_state) { xhci->error_bitmask |= 1 << 2; - return; + return 0; } xhci_dbg(xhci, "%s - OS owns TRB\n", __func__); + /* + * Barrier between reading the TRB_CYCLE (valid) flag above and any + * speculative reads of the event's flags/data below. + */ + rmb(); /* FIXME: Handle more event types. */ - switch ((event->event_cmd.flags & TRB_TYPE_BITMASK)) { + switch ((le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK)) { case TRB_TYPE(TRB_COMPLETION): xhci_dbg(xhci, "%s - calling handle_cmd_completion\n", __func__); handle_cmd_completion(xhci, &event->event_cmd); @@ -2202,7 +2223,8 @@ static void xhci_handle_event(struct xhci_hcd *xhci) update_ptrs = 0; break; default: - if ((event->event_cmd.flags & TRB_TYPE_BITMASK) >= TRB_TYPE(48)) + if ((le32_to_cpu(event->event_cmd.flags) & TRB_TYPE_BITMASK) >= + TRB_TYPE(48)) handle_vendor_event(xhci, event); else xhci->error_bitmask |= 1 << 3; @@ -2213,15 +2235,17 @@ static void xhci_handle_event(struct xhci_hcd *xhci) if (xhci->xhc_state & XHCI_STATE_DYING) { xhci_dbg(xhci, "xHCI host dying, returning from " "event handler.\n"); - return; + return 0; } if (update_ptrs) /* Update SW event ring dequeue pointer */ inc_deq(xhci, xhci->event_ring, true); - /* Are there more items on the event ring? */ - xhci_handle_event(xhci); + /* Are there more items on the event ring? Caller will call us again to + * check. + */ + return 1; } /* @@ -2252,12 +2276,12 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd) xhci_dbg(xhci, "op reg status = %08x\n", status); xhci_dbg(xhci, "Event ring dequeue ptr:\n"); xhci_dbg(xhci, "@%llx %08x %08x %08x %08x\n", - (unsigned long long) - xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, trb), - lower_32_bits(trb->link.segment_ptr), - upper_32_bits(trb->link.segment_ptr), - (unsigned int) trb->link.intr_target, - (unsigned int) trb->link.control); + (unsigned long long) + xhci_trb_virt_to_dma(xhci->event_ring->deq_seg, trb), + lower_32_bits(le64_to_cpu(trb->link.segment_ptr)), + upper_32_bits(le64_to_cpu(trb->link.segment_ptr)), + (unsigned int) le32_to_cpu(trb->link.intr_target), + (unsigned int) le32_to_cpu(trb->link.control)); if (status & STS_FATAL) { xhci_warn(xhci, "WARNING: Host System Error\n"); @@ -2303,7 +2327,7 @@ hw_died: /* FIXME this should be a delayed service routine * that clears the EHB. */ - xhci_handle_event(xhci); + while (xhci_handle_event(xhci) > 0) {} temp_64 = xhci_read_64(xhci, &xhci->ir_set->erst_dequeue); /* If necessary, update the HW's version of the event ring deq ptr. */ @@ -2358,10 +2382,10 @@ static void queue_trb(struct xhci_hcd *xhci, struct xhci_ring *ring, struct xhci_generic_trb *trb; trb = &ring->enqueue->generic; - trb->field[0] = field1; - trb->field[1] = field2; - trb->field[2] = field3; - trb->field[3] = field4; + trb->field[0] = cpu_to_le32(field1); + trb->field[1] = cpu_to_le32(field2); + trb->field[2] = cpu_to_le32(field3); + trb->field[3] = cpu_to_le32(field4); inc_enq(xhci, ring, consumer, more_trbs_coming); } @@ -2414,17 +2438,16 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, next = ring->enqueue; while (last_trb(xhci, ring, ring->enq_seg, next)) { - /* If we're not dealing with 0.95 hardware, * clear the chain bit. */ if (!xhci_link_trb_quirk(xhci)) - next->link.control &= ~TRB_CHAIN; + next->link.control &= cpu_to_le32(~TRB_CHAIN); else - next->link.control |= TRB_CHAIN; + next->link.control |= cpu_to_le32(TRB_CHAIN); wmb(); - next->link.control ^= (u32) TRB_CYCLE; + next->link.control ^= cpu_to_le32((u32) TRB_CYCLE); /* Toggle the cycle bit after the last ring segment. */ if (last_trb_on_last_seg(xhci, ring, ring->enq_seg, next)) { @@ -2467,8 +2490,8 @@ static int prepare_transfer(struct xhci_hcd *xhci, } ret = prepare_ring(xhci, ep_ring, - ep_ctx->ep_info & EP_STATE_MASK, - num_trbs, mem_flags); + le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK, + num_trbs, mem_flags); if (ret) return ret; @@ -2570,9 +2593,9 @@ static void giveback_first_trb(struct xhci_hcd *xhci, int slot_id, */ wmb(); if (start_cycle) - start_trb->field[3] |= start_cycle; + start_trb->field[3] |= cpu_to_le32(start_cycle); else - start_trb->field[3] &= ~0x1; + start_trb->field[3] &= cpu_to_le32(~TRB_CYCLE); xhci_ring_ep_doorbell(xhci, slot_id, ep_index, stream_id); } @@ -2590,7 +2613,7 @@ int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags, int xhci_interval; int ep_interval; - xhci_interval = EP_INTERVAL_TO_UFRAMES(ep_ctx->ep_info); + xhci_interval = EP_INTERVAL_TO_UFRAMES(le32_to_cpu(ep_ctx->ep_info)); ep_interval = urb->interval; /* Convert to microframes */ if (urb->dev->speed == USB_SPEED_LOW || @@ -2632,6 +2655,35 @@ static u32 xhci_td_remainder(unsigned int remainder) return (remainder >> 10) << 17; } +/* + * For xHCI 1.0 host controllers, TD size is the number of packets remaining in + * the TD (*not* including this TRB). + * + * Total TD packet count = total_packet_count = + * roundup(TD size in bytes / wMaxPacketSize) + * + * Packets transferred up to and including this TRB = packets_transferred = + * rounddown(total bytes transferred including this TRB / wMaxPacketSize) + * + * TD size = total_packet_count - packets_transferred + * + * It must fit in bits 21:17, so it can't be bigger than 31. + */ + +static u32 xhci_v1_0_td_remainder(int running_total, int trb_buff_len, + unsigned int total_packet_count, struct urb *urb) +{ + int packets_transferred; + + /* All the TRB queueing functions don't count the current TRB in + * running_total. + */ + packets_transferred = (running_total + trb_buff_len) / + le16_to_cpu(urb->ep->desc.wMaxPacketSize); + + return xhci_td_remainder(total_packet_count - packets_transferred); +} + static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb, int slot_id, unsigned int ep_index) { @@ -2642,6 +2694,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct scatterlist *sg; int num_sgs; int trb_buff_len, this_sg_len, running_total; + unsigned int total_packet_count; bool first_trb; u64 addr; bool more_trbs_coming; @@ -2655,6 +2708,8 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, num_trbs = count_sg_trbs_needed(xhci, urb); num_sgs = urb->num_sgs; + total_packet_count = roundup(urb->transfer_buffer_length, + le16_to_cpu(urb->ep->desc.wMaxPacketSize)); trb_buff_len = prepare_transfer(xhci, xhci->devs[slot_id], ep_index, urb->stream_id, @@ -2718,6 +2773,11 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, td->last_trb = ep_ring->enqueue; field |= TRB_IOC; } + + /* Only set interrupt on short packet for IN endpoints */ + if (usb_urb_dir_in(urb)) + field |= TRB_ISP; + xhci_dbg(xhci, " sg entry: dma = %#x, len = %#x (%d), " "64KB boundary at %#x, end dma = %#x\n", (unsigned int) addr, trb_buff_len, trb_buff_len, @@ -2730,11 +2790,20 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, (unsigned int) (addr + TRB_MAX_BUFF_SIZE) & ~(TRB_MAX_BUFF_SIZE - 1), (unsigned int) addr + trb_buff_len); } - remainder = xhci_td_remainder(urb->transfer_buffer_length - - running_total) ; + + /* Set the TRB length, TD size, and interrupter fields. */ + if (xhci->hci_version < 0x100) { + remainder = xhci_td_remainder( + urb->transfer_buffer_length - + running_total); + } else { + remainder = xhci_v1_0_td_remainder(running_total, + trb_buff_len, total_packet_count, urb); + } length_field = TRB_LEN(trb_buff_len) | remainder | TRB_INTR_TARGET(0); + if (num_trbs > 1) more_trbs_coming = true; else @@ -2743,12 +2812,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, lower_32_bits(addr), upper_32_bits(addr), length_field, - /* We always want to know if the TRB was short, - * or we won't get an event when it completes. - * (Unless we use event data TRBs, which are a - * waste of space and HC resources.) - */ - field | TRB_ISP | TRB_TYPE(TRB_NORMAL)); + field | TRB_TYPE(TRB_NORMAL)); --num_trbs; running_total += trb_buff_len; @@ -2796,6 +2860,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, u32 field, length_field; int running_total, trb_buff_len, ret; + unsigned int total_packet_count; u64 addr; if (urb->num_sgs) @@ -2850,6 +2915,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, start_cycle = ep_ring->cycle_state; running_total = 0; + total_packet_count = roundup(urb->transfer_buffer_length, + le16_to_cpu(urb->ep->desc.wMaxPacketSize)); /* How much data is in the first TRB? */ addr = (u64) urb->transfer_dma; trb_buff_len = TRB_MAX_BUFF_SIZE - @@ -2882,11 +2949,24 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, td->last_trb = ep_ring->enqueue; field |= TRB_IOC; } - remainder = xhci_td_remainder(urb->transfer_buffer_length - - running_total); + + /* Only set interrupt on short packet for IN endpoints */ + if (usb_urb_dir_in(urb)) + field |= TRB_ISP; + + /* Set the TRB length, TD size, and interrupter fields. */ + if (xhci->hci_version < 0x100) { + remainder = xhci_td_remainder( + urb->transfer_buffer_length - + running_total); + } else { + remainder = xhci_v1_0_td_remainder(running_total, + trb_buff_len, total_packet_count, urb); + } length_field = TRB_LEN(trb_buff_len) | remainder | TRB_INTR_TARGET(0); + if (num_trbs > 1) more_trbs_coming = true; else @@ -2895,12 +2975,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, lower_32_bits(addr), upper_32_bits(addr), length_field, - /* We always want to know if the TRB was short, - * or we won't get an event when it completes. - * (Unless we use event data TRBs, which are a - * waste of space and HC resources.) - */ - field | TRB_ISP | TRB_TYPE(TRB_NORMAL)); + field | TRB_TYPE(TRB_NORMAL)); --num_trbs; running_total += trb_buff_len; @@ -2978,16 +3053,31 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, field |= TRB_IDT | TRB_TYPE(TRB_SETUP); if (start_cycle == 0) field |= 0x1; + + /* xHCI 1.0 6.4.1.2.1: Transfer Type field */ + if (xhci->hci_version == 0x100) { + if (urb->transfer_buffer_length > 0) { + if (setup->bRequestType & USB_DIR_IN) + field |= TRB_TX_TYPE(TRB_DATA_IN); + else + field |= TRB_TX_TYPE(TRB_DATA_OUT); + } + } + queue_trb(xhci, ep_ring, false, true, - /* FIXME endianness is probably going to bite my ass here. */ - setup->bRequestType | setup->bRequest << 8 | setup->wValue << 16, - setup->wIndex | setup->wLength << 16, - TRB_LEN(8) | TRB_INTR_TARGET(0), - /* Immediate data in pointer */ - field); + setup->bRequestType | setup->bRequest << 8 | le16_to_cpu(setup->wValue) << 16, + le16_to_cpu(setup->wIndex) | le16_to_cpu(setup->wLength) << 16, + TRB_LEN(8) | TRB_INTR_TARGET(0), + /* Immediate data in pointer */ + field); /* If there's data, queue data TRBs */ - field = 0; + /* Only set interrupt on short packet for IN endpoints */ + if (usb_urb_dir_in(urb)) + field = TRB_ISP | TRB_TYPE(TRB_DATA); + else + field = TRB_TYPE(TRB_DATA); + length_field = TRB_LEN(urb->transfer_buffer_length) | xhci_td_remainder(urb->transfer_buffer_length) | TRB_INTR_TARGET(0); @@ -2998,8 +3088,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, lower_32_bits(urb->transfer_dma), upper_32_bits(urb->transfer_dma), length_field, - /* Event on short tx */ - field | TRB_ISP | TRB_TYPE(TRB_DATA) | ep_ring->cycle_state); + field | ep_ring->cycle_state); } /* Save the DMA address of the last TRB in the TD */ @@ -3045,6 +3134,63 @@ static int count_isoc_trbs_needed(struct xhci_hcd *xhci, return num_trbs; } +/* + * The transfer burst count field of the isochronous TRB defines the number of + * bursts that are required to move all packets in this TD. Only SuperSpeed + * devices can burst up to bMaxBurst number of packets per service interval. + * This field is zero based, meaning a value of zero in the field means one + * burst. Basically, for everything but SuperSpeed devices, this field will be + * zero. Only xHCI 1.0 host controllers support this field. + */ +static unsigned int xhci_get_burst_count(struct xhci_hcd *xhci, + struct usb_device *udev, + struct urb *urb, unsigned int total_packet_count) +{ + unsigned int max_burst; + + if (xhci->hci_version < 0x100 || udev->speed != USB_SPEED_SUPER) + return 0; + + max_burst = urb->ep->ss_ep_comp.bMaxBurst; + return roundup(total_packet_count, max_burst + 1) - 1; +} + +/* + * Returns the number of packets in the last "burst" of packets. This field is + * valid for all speeds of devices. USB 2.0 devices can only do one "burst", so + * the last burst packet count is equal to the total number of packets in the + * TD. SuperSpeed endpoints can have up to 3 bursts. All but the last burst + * must contain (bMaxBurst + 1) number of packets, but the last burst can + * contain 1 to (bMaxBurst + 1) packets. + */ +static unsigned int xhci_get_last_burst_packet_count(struct xhci_hcd *xhci, + struct usb_device *udev, + struct urb *urb, unsigned int total_packet_count) +{ + unsigned int max_burst; + unsigned int residue; + + if (xhci->hci_version < 0x100) + return 0; + + switch (udev->speed) { + case USB_SPEED_SUPER: + /* bMaxBurst is zero based: 0 means 1 packet per burst */ + max_burst = urb->ep->ss_ep_comp.bMaxBurst; + residue = total_packet_count % (max_burst + 1); + /* If residue is zero, the last burst contains (max_burst + 1) + * number of packets, but the TLBPC field is zero-based. + */ + if (residue == 0) + return max_burst; + return residue - 1; + default: + if (total_packet_count == 0) + return 0; + return total_packet_count - 1; + } +} + /* This is for isoc transfer */ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb, int slot_id, unsigned int ep_index) @@ -3085,12 +3231,22 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, /* Queue the first TRB, even if it's zero-length */ for (i = 0; i < num_tds; i++) { - first_trb = true; + unsigned int total_packet_count; + unsigned int burst_count; + unsigned int residue; + first_trb = true; running_total = 0; addr = start_addr + urb->iso_frame_desc[i].offset; td_len = urb->iso_frame_desc[i].length; td_remain_len = td_len; + /* FIXME: Ignoring zero-length packets, can those happen? */ + total_packet_count = roundup(td_len, + le16_to_cpu(urb->ep->desc.wMaxPacketSize)); + burst_count = xhci_get_burst_count(xhci, urb->dev, urb, + total_packet_count); + residue = xhci_get_last_burst_packet_count(xhci, + urb->dev, urb, total_packet_count); trbs_per_td = count_isoc_trbs_needed(xhci, urb, i); @@ -3104,7 +3260,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, for (j = 0; j < trbs_per_td; j++) { u32 remainder = 0; - field = 0; + field = TRB_TBC(burst_count) | TRB_TLBPC(residue); if (first_trb) { /* Queue the isoc TRB */ @@ -3123,6 +3279,10 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, field |= ep_ring->cycle_state; } + /* Only set interrupt on short packet for IN EPs */ + if (usb_urb_dir_in(urb)) + field |= TRB_ISP; + /* Chain all the TRBs together; clear the chain bit in * the last TRB to indicate it's the last TRB in the * chain. @@ -3133,6 +3293,11 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, } else { td->last_trb = ep_ring->enqueue; field |= TRB_IOC; + if (xhci->hci_version == 0x100) { + /* Set BEI bit except for the last td */ + if (i < num_tds - 1) + field |= TRB_BEI; + } more_trbs_coming = false; } @@ -3142,20 +3307,24 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, if (trb_buff_len > td_remain_len) trb_buff_len = td_remain_len; - remainder = xhci_td_remainder(td_len - running_total); + /* Set the TRB length, TD size, & interrupter fields. */ + if (xhci->hci_version < 0x100) { + remainder = xhci_td_remainder( + td_len - running_total); + } else { + remainder = xhci_v1_0_td_remainder( + running_total, trb_buff_len, + total_packet_count, urb); + } length_field = TRB_LEN(trb_buff_len) | remainder | TRB_INTR_TARGET(0); + queue_trb(xhci, ep_ring, false, more_trbs_coming, lower_32_bits(addr), upper_32_bits(addr), length_field, - /* We always want to know if the TRB was short, - * or we won't get an event when it completes. - * (Unless we use event data TRBs, which are a - * waste of space and HC resources.) - */ - field | TRB_ISP); + field); running_total += trb_buff_len; addr += trb_buff_len; @@ -3211,8 +3380,8 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, /* Check the ring to guarantee there is enough room for the whole urb. * Do not insert any td of the urb to the ring if the check failed. */ - ret = prepare_ring(xhci, ep_ring, ep_ctx->ep_info & EP_STATE_MASK, - num_trbs, mem_flags); + ret = prepare_ring(xhci, ep_ring, le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK, + num_trbs, mem_flags); if (ret) return ret; @@ -3224,7 +3393,7 @@ int xhci_queue_isoc_tx_prepare(struct xhci_hcd *xhci, gfp_t mem_flags, urb->dev->speed == USB_SPEED_FULL) urb->start_frame >>= 3; - xhci_interval = EP_INTERVAL_TO_UFRAMES(ep_ctx->ep_info); + xhci_interval = EP_INTERVAL_TO_UFRAMES(le32_to_cpu(ep_ctx->ep_info)); ep_interval = urb->interval; /* Convert to microframes */ if (urb->dev->speed == USB_SPEED_LOW || diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 81b976e..8f2a56e 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -973,8 +973,8 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, out_ctx = xhci->devs[slot_id]->out_ctx; ep_ctx = xhci_get_ep_ctx(xhci, out_ctx, ep_index); - hw_max_packet_size = MAX_PACKET_DECODED(ep_ctx->ep_info2); - max_packet_size = urb->dev->ep0.desc.wMaxPacketSize; + hw_max_packet_size = MAX_PACKET_DECODED(le32_to_cpu(ep_ctx->ep_info2)); + max_packet_size = le16_to_cpu(urb->dev->ep0.desc.wMaxPacketSize); if (hw_max_packet_size != max_packet_size) { xhci_dbg(xhci, "Max Packet Size for ep 0 changed.\n"); xhci_dbg(xhci, "Max packet size in usb_device = %d\n", @@ -988,15 +988,15 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, xhci->devs[slot_id]->out_ctx, ep_index); in_ctx = xhci->devs[slot_id]->in_ctx; ep_ctx = xhci_get_ep_ctx(xhci, in_ctx, ep_index); - ep_ctx->ep_info2 &= ~MAX_PACKET_MASK; - ep_ctx->ep_info2 |= MAX_PACKET(max_packet_size); + ep_ctx->ep_info2 &= cpu_to_le32(~MAX_PACKET_MASK); + ep_ctx->ep_info2 |= cpu_to_le32(MAX_PACKET(max_packet_size)); /* Set up the input context flags for the command */ /* FIXME: This won't work if a non-default control endpoint * changes max packet sizes. */ ctrl_ctx = xhci_get_input_control_ctx(xhci, in_ctx); - ctrl_ctx->add_flags = EP0_FLAG; + ctrl_ctx->add_flags = cpu_to_le32(EP0_FLAG); ctrl_ctx->drop_flags = 0; xhci_dbg(xhci, "Slot %d input context\n", slot_id); @@ -1010,7 +1010,7 @@ static int xhci_check_maxpacket(struct xhci_hcd *xhci, unsigned int slot_id, /* Clean up the input context for later use by bandwidth * functions. */ - ctrl_ctx->add_flags = SLOT_FLAG; + ctrl_ctx->add_flags = cpu_to_le32(SLOT_FLAG); } return ret; } @@ -1331,27 +1331,30 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, /* If the HC already knows the endpoint is disabled, * or the HCD has noted it is disabled, ignore this request */ - if ((ep_ctx->ep_info & EP_STATE_MASK) == EP_STATE_DISABLED || - ctrl_ctx->drop_flags & xhci_get_endpoint_flag(&ep->desc)) { + if ((le32_to_cpu(ep_ctx->ep_info) & EP_STATE_MASK) == + EP_STATE_DISABLED || + le32_to_cpu(ctrl_ctx->drop_flags) & + xhci_get_endpoint_flag(&ep->desc)) { xhci_warn(xhci, "xHCI %s called with disabled ep %p\n", __func__, ep); return 0; } - ctrl_ctx->drop_flags |= drop_flag; - new_drop_flags = ctrl_ctx->drop_flags; + ctrl_ctx->drop_flags |= cpu_to_le32(drop_flag); + new_drop_flags = le32_to_cpu(ctrl_ctx->drop_flags); - ctrl_ctx->add_flags &= ~drop_flag; - new_add_flags = ctrl_ctx->add_flags; + ctrl_ctx->add_flags &= cpu_to_le32(~drop_flag); + new_add_flags = le32_to_cpu(ctrl_ctx->add_flags); - last_ctx = xhci_last_valid_endpoint(ctrl_ctx->add_flags); + last_ctx = xhci_last_valid_endpoint(le32_to_cpu(ctrl_ctx->add_flags)); slot_ctx = xhci_get_slot_ctx(xhci, in_ctx); /* Update the last valid endpoint context, if we deleted the last one */ - if ((slot_ctx->dev_info & LAST_CTX_MASK) > LAST_CTX(last_ctx)) { - slot_ctx->dev_info &= ~LAST_CTX_MASK; - slot_ctx->dev_info |= LAST_CTX(last_ctx); + if ((le32_to_cpu(slot_ctx->dev_info) & LAST_CTX_MASK) > + LAST_CTX(last_ctx)) { + slot_ctx->dev_info &= cpu_to_le32(~LAST_CTX_MASK); + slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(last_ctx)); } - new_slot_info = slot_ctx->dev_info; + new_slot_info = le32_to_cpu(slot_ctx->dev_info); xhci_endpoint_zero(xhci, xhci->devs[udev->slot_id], ep); @@ -1419,7 +1422,8 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, /* If the HCD has already noted the endpoint is enabled, * ignore this request. */ - if (ctrl_ctx->add_flags & xhci_get_endpoint_flag(&ep->desc)) { + if (le32_to_cpu(ctrl_ctx->add_flags) & + xhci_get_endpoint_flag(&ep->desc)) { xhci_warn(xhci, "xHCI %s called with enabled ep %p\n", __func__, ep); return 0; @@ -1437,8 +1441,8 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, return -ENOMEM; } - ctrl_ctx->add_flags |= added_ctxs; - new_add_flags = ctrl_ctx->add_flags; + ctrl_ctx->add_flags |= cpu_to_le32(added_ctxs); + new_add_flags = le32_to_cpu(ctrl_ctx->add_flags); /* If xhci_endpoint_disable() was called for this endpoint, but the * xHC hasn't been notified yet through the check_bandwidth() call, @@ -1446,15 +1450,16 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, * descriptors. We must drop and re-add this endpoint, so we leave the * drop flags alone. */ - new_drop_flags = ctrl_ctx->drop_flags; + new_drop_flags = le32_to_cpu(ctrl_ctx->drop_flags); slot_ctx = xhci_get_slot_ctx(xhci, in_ctx); /* Update the last valid endpoint context, if we just added one past */ - if ((slot_ctx->dev_info & LAST_CTX_MASK) < LAST_CTX(last_ctx)) { - slot_ctx->dev_info &= ~LAST_CTX_MASK; - slot_ctx->dev_info |= LAST_CTX(last_ctx); + if ((le32_to_cpu(slot_ctx->dev_info) & LAST_CTX_MASK) < + LAST_CTX(last_ctx)) { + slot_ctx->dev_info &= cpu_to_le32(~LAST_CTX_MASK); + slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(last_ctx)); } - new_slot_info = slot_ctx->dev_info; + new_slot_info = le32_to_cpu(slot_ctx->dev_info); /* Store the usb_device pointer for later use */ ep->hcpriv = udev; @@ -1484,9 +1489,9 @@ static void xhci_zero_in_ctx(struct xhci_hcd *xhci, struct xhci_virt_device *vir ctrl_ctx->drop_flags = 0; ctrl_ctx->add_flags = 0; slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx); - slot_ctx->dev_info &= ~LAST_CTX_MASK; + slot_ctx->dev_info &= cpu_to_le32(~LAST_CTX_MASK); /* Endpoint 0 is always valid */ - slot_ctx->dev_info |= LAST_CTX(1); + slot_ctx->dev_info |= cpu_to_le32(LAST_CTX(1)); for (i = 1; i < 31; ++i) { ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->in_ctx, i); ep_ctx->ep_info = 0; @@ -1497,7 +1502,7 @@ static void xhci_zero_in_ctx(struct xhci_hcd *xhci, struct xhci_virt_device *vir } static int xhci_configure_endpoint_result(struct xhci_hcd *xhci, - struct usb_device *udev, int *cmd_status) + struct usb_device *udev, u32 *cmd_status) { int ret; @@ -1535,7 +1540,7 @@ static int xhci_configure_endpoint_result(struct xhci_hcd *xhci, } static int xhci_evaluate_context_result(struct xhci_hcd *xhci, - struct usb_device *udev, int *cmd_status) + struct usb_device *udev, u32 *cmd_status) { int ret; struct xhci_virt_device *virt_dev = xhci->devs[udev->slot_id]; @@ -1555,6 +1560,11 @@ static int xhci_evaluate_context_result(struct xhci_hcd *xhci, xhci_dbg_ctx(xhci, virt_dev->out_ctx, 1); ret = -EINVAL; break; + case COMP_MEL_ERR: + /* Max Exit Latency too large error */ + dev_warn(&udev->dev, "WARN: Max Exit Latency too large\n"); + ret = -EINVAL; + break; case COMP_SUCCESS: dev_dbg(&udev->dev, "Successful evaluate context command\n"); ret = 0; @@ -1581,7 +1591,7 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci, unsigned long flags; struct xhci_container_ctx *in_ctx; struct completion *cmd_completion; - int *cmd_status; + u32 *cmd_status; struct xhci_virt_device *virt_dev; spin_lock_irqsave(&xhci->lock, flags); @@ -1595,8 +1605,8 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci, /* Enqueue pointer can be left pointing to the link TRB, * we must handle that */ - if ((command->command_trb->link.control & TRB_TYPE_BITMASK) - == TRB_TYPE(TRB_LINK)) + if ((le32_to_cpu(command->command_trb->link.control) + & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK)) command->command_trb = xhci->cmd_ring->enq_seg->next->trbs; @@ -1672,14 +1682,13 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) /* See section 4.6.6 - A0 = 1; A1 = D0 = D1 = 0 */ ctrl_ctx = xhci_get_input_control_ctx(xhci, virt_dev->in_ctx); - ctrl_ctx->add_flags |= SLOT_FLAG; - ctrl_ctx->add_flags &= ~EP0_FLAG; - ctrl_ctx->drop_flags &= ~SLOT_FLAG; - ctrl_ctx->drop_flags &= ~EP0_FLAG; + ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG); + ctrl_ctx->add_flags &= cpu_to_le32(~EP0_FLAG); + ctrl_ctx->drop_flags &= cpu_to_le32(~(SLOT_FLAG | EP0_FLAG)); xhci_dbg(xhci, "New Input Control Context:\n"); slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx); xhci_dbg_ctx(xhci, virt_dev->in_ctx, - LAST_CTX_TO_EP_NUM(slot_ctx->dev_info)); + LAST_CTX_TO_EP_NUM(le32_to_cpu(slot_ctx->dev_info))); ret = xhci_configure_endpoint(xhci, udev, NULL, false, false); @@ -1690,10 +1699,19 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) xhci_dbg(xhci, "Output context after successful config ep cmd:\n"); xhci_dbg_ctx(xhci, virt_dev->out_ctx, - LAST_CTX_TO_EP_NUM(slot_ctx->dev_info)); + LAST_CTX_TO_EP_NUM(le32_to_cpu(slot_ctx->dev_info))); + /* Free any rings that were dropped, but not changed. */ + for (i = 1; i < 31; ++i) { + if ((ctrl_ctx->drop_flags & (1 << (i + 1))) && + !(ctrl_ctx->add_flags & (1 << (i + 1)))) + xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i); + } xhci_zero_in_ctx(xhci, virt_dev); - /* Install new rings and free or cache any old rings */ + /* + * Install any rings for completely new endpoints or changed endpoints, + * and free or cache any old rings from changed endpoints. + */ for (i = 1; i < 31; ++i) { if (!virt_dev->eps[i].new_ring) continue; @@ -1740,10 +1758,10 @@ static void xhci_setup_input_ctx_for_config_ep(struct xhci_hcd *xhci, { struct xhci_input_control_ctx *ctrl_ctx; ctrl_ctx = xhci_get_input_control_ctx(xhci, in_ctx); - ctrl_ctx->add_flags = add_flags; - ctrl_ctx->drop_flags = drop_flags; + ctrl_ctx->add_flags = cpu_to_le32(add_flags); + ctrl_ctx->drop_flags = cpu_to_le32(drop_flags); xhci_slot_copy(xhci, in_ctx, out_ctx); - ctrl_ctx->add_flags |= SLOT_FLAG; + ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG); xhci_dbg(xhci, "Input Context:\n"); xhci_dbg_ctx(xhci, in_ctx, xhci_last_valid_endpoint(add_flags)); @@ -1772,7 +1790,7 @@ static void xhci_setup_input_ctx_for_quirk(struct xhci_hcd *xhci, deq_state->new_deq_ptr); return; } - ep_ctx->deq = addr | deq_state->new_cycle_state; + ep_ctx->deq = cpu_to_le64(addr | deq_state->new_cycle_state); added_ctxs = xhci_get_endpoint_flag_from_index(ep_index); xhci_setup_input_ctx_for_config_ep(xhci, xhci->devs[slot_id]->in_ctx, @@ -2327,8 +2345,8 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev) /* Enqueue pointer can be left pointing to the link TRB, * we must handle that */ - if ((reset_device_cmd->command_trb->link.control & TRB_TYPE_BITMASK) - == TRB_TYPE(TRB_LINK)) + if ((le32_to_cpu(reset_device_cmd->command_trb->link.control) + & TRB_TYPE_BITMASK) == TRB_TYPE(TRB_LINK)) reset_device_cmd->command_trb = xhci->cmd_ring->enq_seg->next->trbs; @@ -2542,6 +2560,17 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) virt_dev = xhci->devs[udev->slot_id]; + if (WARN_ON(!virt_dev)) { + /* + * In plug/unplug torture test with an NEC controller, + * a zero-dereference was observed once due to virt_dev = 0. + * Print useful debug rather than crash if it is observed again! + */ + xhci_warn(xhci, "Virt dev invalid for slot_id 0x%x!\n", + udev->slot_id); + return -EINVAL; + } + slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->in_ctx); /* * If this is the first Set Address since device plug-in or @@ -2609,10 +2638,10 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) temp_64 = xhci_read_64(xhci, &xhci->op_regs->dcbaa_ptr); xhci_dbg(xhci, "Op regs DCBAA ptr = %#016llx\n", temp_64); xhci_dbg(xhci, "Slot ID %d dcbaa entry @%p = %#016llx\n", - udev->slot_id, - &xhci->dcbaa->dev_context_ptrs[udev->slot_id], - (unsigned long long) - xhci->dcbaa->dev_context_ptrs[udev->slot_id]); + udev->slot_id, + &xhci->dcbaa->dev_context_ptrs[udev->slot_id], + (unsigned long long) + le64_to_cpu(xhci->dcbaa->dev_context_ptrs[udev->slot_id])); xhci_dbg(xhci, "Output Context DMA address = %#08llx\n", (unsigned long long)virt_dev->out_ctx->dma); xhci_dbg(xhci, "Slot ID %d Input Context:\n", udev->slot_id); @@ -2626,7 +2655,8 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev) slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->out_ctx); /* Use kernel assigned address for devices; store xHC assigned * address locally. */ - virt_dev->address = (slot_ctx->dev_state & DEV_ADDR_MASK) + 1; + virt_dev->address = (le32_to_cpu(slot_ctx->dev_state) & DEV_ADDR_MASK) + + 1; /* Zero the input context control for later use */ ctrl_ctx = xhci_get_input_control_ctx(xhci, virt_dev->in_ctx); ctrl_ctx->add_flags = 0; @@ -2670,24 +2700,29 @@ int xhci_update_hub_device(struct usb_hcd *hcd, struct usb_device *hdev, spin_lock_irqsave(&xhci->lock, flags); xhci_slot_copy(xhci, config_cmd->in_ctx, vdev->out_ctx); ctrl_ctx = xhci_get_input_control_ctx(xhci, config_cmd->in_ctx); - ctrl_ctx->add_flags |= SLOT_FLAG; + ctrl_ctx->add_flags |= cpu_to_le32(SLOT_FLAG); slot_ctx = xhci_get_slot_ctx(xhci, config_cmd->in_ctx); - slot_ctx->dev_info |= DEV_HUB; + slot_ctx->dev_info |= cpu_to_le32(DEV_HUB); if (tt->multi) - slot_ctx->dev_info |= DEV_MTT; + slot_ctx->dev_info |= cpu_to_le32(DEV_MTT); if (xhci->hci_version > 0x95) { xhci_dbg(xhci, "xHCI version %x needs hub " "TT think time and number of ports\n", (unsigned int) xhci->hci_version); - slot_ctx->dev_info2 |= XHCI_MAX_PORTS(hdev->maxchild); + slot_ctx->dev_info2 |= cpu_to_le32(XHCI_MAX_PORTS(hdev->maxchild)); /* Set TT think time - convert from ns to FS bit times. * 0 = 8 FS bit times, 1 = 16 FS bit times, * 2 = 24 FS bit times, 3 = 32 FS bit times. + * + * xHCI 1.0: this field shall be 0 if the device is not a + * High-spped hub. */ think_time = tt->think_time; if (think_time != 0) think_time = (think_time / 666) - 1; - slot_ctx->tt_info |= TT_THINK_TIME(think_time); + if (xhci->hci_version < 0x100 || hdev->speed == USB_SPEED_HIGH) + slot_ctx->tt_info |= + cpu_to_le32(TT_THINK_TIME(think_time)); } else { xhci_dbg(xhci, "xHCI version %x doesn't need hub " "TT think time or number of ports\n", diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index ba1be6b..e12db7cf 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -57,13 +57,13 @@ * @run_regs_off: RTSOFF - Runtime register space offset */ struct xhci_cap_regs { - u32 hc_capbase; - u32 hcs_params1; - u32 hcs_params2; - u32 hcs_params3; - u32 hcc_params; - u32 db_off; - u32 run_regs_off; + __le32 hc_capbase; + __le32 hcs_params1; + __le32 hcs_params2; + __le32 hcs_params3; + __le32 hcc_params; + __le32 db_off; + __le32 run_regs_off; /* Reserved up to (CAPLENGTH - 0x1C) */ }; @@ -155,26 +155,26 @@ struct xhci_cap_regs { * devices. */ struct xhci_op_regs { - u32 command; - u32 status; - u32 page_size; - u32 reserved1; - u32 reserved2; - u32 dev_notification; - u64 cmd_ring; + __le32 command; + __le32 status; + __le32 page_size; + __le32 reserved1; + __le32 reserved2; + __le32 dev_notification; + __le64 cmd_ring; /* rsvd: offset 0x20-2F */ - u32 reserved3[4]; - u64 dcbaa_ptr; - u32 config_reg; + __le32 reserved3[4]; + __le64 dcbaa_ptr; + __le32 config_reg; /* rsvd: offset 0x3C-3FF */ - u32 reserved4[241]; + __le32 reserved4[241]; /* port 1 registers, which serve as a base address for other ports */ - u32 port_status_base; - u32 port_power_base; - u32 port_link_base; - u32 reserved5; + __le32 port_status_base; + __le32 port_power_base; + __le32 port_link_base; + __le32 reserved5; /* registers for ports 2-255 */ - u32 reserved6[NUM_PORT_REGS*254]; + __le32 reserved6[NUM_PORT_REGS*254]; }; /* USBCMD - USB command - command bitmasks */ @@ -382,12 +382,12 @@ struct xhci_op_regs { * updates the dequeue pointer. */ struct xhci_intr_reg { - u32 irq_pending; - u32 irq_control; - u32 erst_size; - u32 rsvd; - u64 erst_base; - u64 erst_dequeue; + __le32 irq_pending; + __le32 irq_control; + __le32 erst_size; + __le32 rsvd; + __le64 erst_base; + __le64 erst_dequeue; }; /* irq_pending bitmasks */ @@ -432,8 +432,8 @@ struct xhci_intr_reg { * or larger accesses" */ struct xhci_run_regs { - u32 microframe_index; - u32 rsvd[7]; + __le32 microframe_index; + __le32 rsvd[7]; struct xhci_intr_reg ir_set[128]; }; @@ -447,7 +447,7 @@ struct xhci_run_regs { * Section 5.6 */ struct xhci_doorbell_array { - u32 doorbell[256]; + __le32 doorbell[256]; }; #define DB_VALUE(ep, stream) ((((ep) + 1) & 0xff) | ((stream) << 16)) @@ -504,12 +504,12 @@ struct xhci_container_ctx { * reserved at the end of the slot context for HC internal use. */ struct xhci_slot_ctx { - u32 dev_info; - u32 dev_info2; - u32 tt_info; - u32 dev_state; + __le32 dev_info; + __le32 dev_info2; + __le32 tt_info; + __le32 dev_state; /* offset 0x10 to 0x1f reserved for HC internal use */ - u32 reserved[4]; + __le32 reserved[4]; }; /* dev_info bitmasks */ @@ -580,12 +580,12 @@ struct xhci_slot_ctx { * reserved at the end of the endpoint context for HC internal use. */ struct xhci_ep_ctx { - u32 ep_info; - u32 ep_info2; - u64 deq; - u32 tx_info; + __le32 ep_info; + __le32 ep_info2; + __le64 deq; + __le32 tx_info; /* offset 0x14 - 0x1f reserved for HC internal use */ - u32 reserved[3]; + __le32 reserved[3]; }; /* ep_info bitmasks */ @@ -660,9 +660,9 @@ struct xhci_ep_ctx { * @add_context: set the bit of the endpoint context you want to enable */ struct xhci_input_control_ctx { - u32 drop_flags; - u32 add_flags; - u32 rsvd2[6]; + __le32 drop_flags; + __le32 add_flags; + __le32 rsvd2[6]; }; /* Represents everything that is needed to issue a command on the command ring. @@ -688,9 +688,9 @@ struct xhci_command { struct xhci_stream_ctx { /* 64-bit stream ring address, cycle state, and stream type */ - u64 stream_ring; + __le64 stream_ring; /* offset 0x14 - 0x1f reserved for HC internal use */ - u32 reserved[2]; + __le32 reserved[2]; }; /* Stream Context Types (section 6.4.1) - bits 3:1 of stream ctx deq ptr */ @@ -803,7 +803,7 @@ struct xhci_virt_device { */ struct xhci_device_context_array { /* 64-bit device addresses; we only write 32-bit addresses */ - u64 dev_context_ptrs[MAX_HC_SLOTS]; + __le64 dev_context_ptrs[MAX_HC_SLOTS]; /* private xHCD pointers */ dma_addr_t dma; }; @@ -816,10 +816,10 @@ struct xhci_device_context_array { struct xhci_transfer_event { /* 64-bit buffer address, or immediate data */ - u64 buffer; - u32 transfer_len; + __le64 buffer; + __le32 transfer_len; /* This field is interpreted differently based on the type of TRB */ - u32 flags; + __le32 flags; }; /** Transfer Event bit fields **/ @@ -881,7 +881,9 @@ struct xhci_transfer_event { #define COMP_STOP_INVAL 27 /* Control Abort Error - Debug Capability - control pipe aborted */ #define COMP_DBG_ABORT 28 -/* TRB type 29 and 30 reserved */ +/* Max Exit Latency Too Large Error */ +#define COMP_MEL_ERR 29 +/* TRB type 30 reserved */ /* Isoc Buffer Overrun - an isoc IN ep sent more data than could fit in TD */ #define COMP_BUFF_OVER 31 /* Event Lost Error - xHC has an "internal event overrun condition" */ @@ -898,9 +900,9 @@ struct xhci_transfer_event { struct xhci_link_trb { /* 64-bit segment pointer*/ - u64 segment_ptr; - u32 intr_target; - u32 control; + __le64 segment_ptr; + __le32 intr_target; + __le32 control; }; /* control bitfields */ @@ -909,9 +911,9 @@ struct xhci_link_trb { /* Command completion event TRB */ struct xhci_event_cmd { /* Pointer to command TRB, or the value passed by the event data trb */ - u64 cmd_trb; - u32 status; - u32 flags; + __le64 cmd_trb; + __le32 status; + __le32 flags; }; /* flags bitmasks */ @@ -943,6 +945,8 @@ struct xhci_event_cmd { /* Interrupter Target - which MSI-X vector to target the completion event at */ #define TRB_INTR_TARGET(p) (((p) & 0x3ff) << 22) #define GET_INTR_TARGET(p) (((p) >> 22) & 0x3ff) +#define TRB_TBC(p) (((p) & 0x3) << 7) +#define TRB_TLBPC(p) (((p) & 0xf) << 16) /* Cycle bit - indicates TRB ownership by HC or HCD */ #define TRB_CYCLE (1<<0) @@ -962,15 +966,20 @@ struct xhci_event_cmd { /* The buffer pointer contains immediate data */ #define TRB_IDT (1<<6) +/* Block Event Interrupt */ +#define TRB_BEI (1<<9) /* Control transfer TRB specific fields */ #define TRB_DIR_IN (1<<16) +#define TRB_TX_TYPE(p) ((p) << 16) +#define TRB_DATA_OUT 2 +#define TRB_DATA_IN 3 /* Isochronous TRB specific fields */ #define TRB_SIA (1<<31) struct xhci_generic_trb { - u32 field[4]; + __le32 field[4]; }; union xhci_trb { @@ -1118,10 +1127,10 @@ struct xhci_ring { struct xhci_erst_entry { /* 64-bit event ring segment address */ - u64 seg_addr; - u32 seg_size; + __le64 seg_addr; + __le32 seg_size; /* Set to zero */ - u32 rsvd; + __le32 rsvd; }; struct xhci_erst { @@ -1286,10 +1295,10 @@ struct xhci_hcd { /* Is each xHCI roothub port a USB 3.0, USB 2.0, or USB 1.1 port? */ u8 *port_array; /* Array of pointers to USB 3.0 PORTSC registers */ - u32 __iomem **usb3_ports; + __le32 __iomem **usb3_ports; unsigned int num_usb3_ports; /* Array of pointers to USB 2.0 PORTSC registers */ - u32 __iomem **usb2_ports; + __le32 __iomem **usb2_ports; unsigned int num_usb2_ports; }; @@ -1322,12 +1331,12 @@ static inline struct usb_hcd *xhci_to_hcd(struct xhci_hcd *xhci) /* TODO: copied from ehci.h - can be refactored? */ /* xHCI spec says all registers are little endian */ static inline unsigned int xhci_readl(const struct xhci_hcd *xhci, - __u32 __iomem *regs) + __le32 __iomem *regs) { return readl(regs); } static inline void xhci_writel(struct xhci_hcd *xhci, - const unsigned int val, __u32 __iomem *regs) + const unsigned int val, __le32 __iomem *regs) { xhci_dbg(xhci, "`MEM_WRITE_DWORD(3'b000, 32'h%p, 32'h%0x, 4'hf);\n", @@ -1345,7 +1354,7 @@ static inline void xhci_writel(struct xhci_hcd *xhci, * the high dword, and write order is irrelevant. */ static inline u64 xhci_read_64(const struct xhci_hcd *xhci, - __u64 __iomem *regs) + __le64 __iomem *regs) { __u32 __iomem *ptr = (__u32 __iomem *) regs; u64 val_lo = readl(ptr); @@ -1353,7 +1362,7 @@ static inline u64 xhci_read_64(const struct xhci_hcd *xhci, return val_lo + (val_hi << 32); } static inline void xhci_write_64(struct xhci_hcd *xhci, - const u64 val, __u64 __iomem *regs) + const u64 val, __le64 __iomem *regs) { __u32 __iomem *ptr = (__u32 __iomem *) regs; u32 val_lo = lower_32_bits(val); diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c index 7839c98..b16bd3c 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/misc/ldusb.c b/drivers/usb/misc/ldusb.c index eefb827..cb40962 100644 --- a/drivers/usb/misc/ldusb.c +++ b/drivers/usb/misc/ldusb.c @@ -20,11 +20,6 @@ * Derived from Lego USB Tower driver * Copyright (C) 2003 David Glance <advidgsf@sourceforge.net> * 2001-2004 Juergen Stuber <starblue@users.sourceforge.net> - * - * V0.1 (mh) Initial version - * V0.11 (mh) Added raw support for HID 1.0 devices (no interrupt out endpoint) - * V0.12 (mh) Added kmalloc check for string buffer - * V0.13 (mh) Added support for LD X-Ray and Machine Test System */ #include <linux/kernel.h> @@ -41,20 +36,39 @@ /* Define these values to match your devices */ #define USB_VENDOR_ID_LD 0x0f11 /* USB Vendor ID of LD Didactic GmbH */ -#define USB_DEVICE_ID_LD_CASSY 0x1000 /* USB Product ID of CASSY-S */ +#define USB_DEVICE_ID_LD_CASSY 0x1000 /* USB Product ID of CASSY-S modules with 8 bytes endpoint size */ +#define USB_DEVICE_ID_LD_CASSY2 0x1001 /* USB Product ID of CASSY-S modules with 64 bytes endpoint size */ #define USB_DEVICE_ID_LD_POCKETCASSY 0x1010 /* USB Product ID of Pocket-CASSY */ +#define USB_DEVICE_ID_LD_POCKETCASSY2 0x1011 /* USB Product ID of Pocket-CASSY 2 (reserved) */ #define USB_DEVICE_ID_LD_MOBILECASSY 0x1020 /* USB Product ID of Mobile-CASSY */ +#define USB_DEVICE_ID_LD_MOBILECASSY2 0x1021 /* USB Product ID of Mobile-CASSY 2 (reserved) */ +#define USB_DEVICE_ID_LD_MICROCASSYVOLTAGE 0x1031 /* USB Product ID of Micro-CASSY Voltage */ +#define USB_DEVICE_ID_LD_MICROCASSYCURRENT 0x1032 /* USB Product ID of Micro-CASSY Current */ +#define USB_DEVICE_ID_LD_MICROCASSYTIME 0x1033 /* USB Product ID of Micro-CASSY Time (reserved) */ +#define USB_DEVICE_ID_LD_MICROCASSYTEMPERATURE 0x1035 /* USB Product ID of Micro-CASSY Temperature */ +#define USB_DEVICE_ID_LD_MICROCASSYPH 0x1038 /* USB Product ID of Micro-CASSY pH */ #define USB_DEVICE_ID_LD_JWM 0x1080 /* USB Product ID of Joule and Wattmeter */ #define USB_DEVICE_ID_LD_DMMP 0x1081 /* USB Product ID of Digital Multimeter P (reserved) */ #define USB_DEVICE_ID_LD_UMIP 0x1090 /* USB Product ID of UMI P */ -#define USB_DEVICE_ID_LD_XRAY1 0x1100 /* USB Product ID of X-Ray Apparatus */ -#define USB_DEVICE_ID_LD_XRAY2 0x1101 /* USB Product ID of X-Ray Apparatus */ +#define USB_DEVICE_ID_LD_UMIC 0x10A0 /* USB Product ID of UMI C */ +#define USB_DEVICE_ID_LD_UMIB 0x10B0 /* USB Product ID of UMI B */ +#define USB_DEVICE_ID_LD_XRAY 0x1100 /* USB Product ID of X-Ray Apparatus 55481 */ +#define USB_DEVICE_ID_LD_XRAY2 0x1101 /* USB Product ID of X-Ray Apparatus 554800 */ +#define USB_DEVICE_ID_LD_XRAYCT 0x1110 /* USB Product ID of X-Ray Apparatus CT 554821*/ #define USB_DEVICE_ID_LD_VIDEOCOM 0x1200 /* USB Product ID of VideoCom */ +#define USB_DEVICE_ID_LD_MOTOR 0x1210 /* USB Product ID of Motor (reserved) */ #define USB_DEVICE_ID_LD_COM3LAB 0x2000 /* USB Product ID of COM3LAB */ #define USB_DEVICE_ID_LD_TELEPORT 0x2010 /* USB Product ID of Terminal Adapter */ #define USB_DEVICE_ID_LD_NETWORKANALYSER 0x2020 /* USB Product ID of Network Analyser */ #define USB_DEVICE_ID_LD_POWERCONTROL 0x2030 /* USB Product ID of Converter Control Unit */ #define USB_DEVICE_ID_LD_MACHINETEST 0x2040 /* USB Product ID of Machine Test System */ +#define USB_DEVICE_ID_LD_MOSTANALYSER 0x2050 /* USB Product ID of MOST Protocol Analyser */ +#define USB_DEVICE_ID_LD_MOSTANALYSER2 0x2051 /* USB Product ID of MOST Protocol Analyser 2 */ +#define USB_DEVICE_ID_LD_ABSESP 0x2060 /* USB Product ID of ABS ESP */ +#define USB_DEVICE_ID_LD_AUTODATABUS 0x2070 /* USB Product ID of Automotive Data Buses */ +#define USB_DEVICE_ID_LD_MCT 0x2080 /* USB Product ID of Microcontroller technique */ +#define USB_DEVICE_ID_LD_HYBRID 0x2090 /* USB Product ID of Automotive Hybrid */ +#define USB_DEVICE_ID_LD_HEATCONTROL 0x20A0 /* USB Product ID of Heat control */ #define USB_VENDOR_ID_VERNIER 0x08f7 #define USB_DEVICE_ID_VERNIER_GOTEMP 0x0002 @@ -71,19 +85,37 @@ /* table of devices that work with this driver */ static const struct usb_device_id ld_usb_table[] = { { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_CASSY2) }, { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POCKETCASSY2) }, { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOBILECASSY2) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYVOLTAGE) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYCURRENT) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYTIME) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYTEMPERATURE) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MICROCASSYPH) }, { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_JWM) }, { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_DMMP) }, { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIP) }, - { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY1) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIC) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_UMIB) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY) }, { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_XRAY2) }, { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_VIDEOCOM) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOTOR) }, { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_COM3LAB) }, { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_TELEPORT) }, { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_NETWORKANALYSER) }, { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_POWERCONTROL) }, { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MACHINETEST) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOSTANALYSER) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MOSTANALYSER2) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_ABSESP) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_AUTODATABUS) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_MCT) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_HYBRID) }, + { USB_DEVICE(USB_VENDOR_ID_LD, USB_DEVICE_ID_LD_HEATCONTROL) }, { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) }, { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) }, { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS) }, @@ -91,7 +123,7 @@ static const struct usb_device_id ld_usb_table[] = { { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, ld_usb_table); -MODULE_VERSION("V0.13"); +MODULE_VERSION("V0.14"); MODULE_AUTHOR("Michael Hund <mhund@ld-didactic.de>"); MODULE_DESCRIPTION("LD USB Driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index ff9a01f..bb10846 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -268,9 +268,9 @@ static inline void simple_fill_buf(struct urb *urb) } } -static inline unsigned buffer_offset(void *buf) +static inline unsigned long buffer_offset(void *buf) { - return (unsigned)buf & (ARCH_KMALLOC_MINALIGN - 1); + return (unsigned long)buf & (ARCH_KMALLOC_MINALIGN - 1); } static int check_guard_bytes(struct usbtest_dev *tdev, struct urb *urb) @@ -329,7 +329,7 @@ static int simple_check_buf(struct usbtest_dev *tdev, struct urb *urb) static void simple_free_urb(struct urb *urb) { - unsigned offset = buffer_offset(urb->transfer_buffer); + unsigned long offset = buffer_offset(urb->transfer_buffer); if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) usb_free_coherent( @@ -1030,6 +1030,8 @@ test_ctrl_queue(struct usbtest_dev *dev, struct usbtest_param *param) req.wValue = cpu_to_le16((USB_DT_DEVICE << 8) | 0); /* device descriptor size == 18 bytes */ len = udev->descriptor.bMaxPacketSize0; + if (udev->speed == USB_SPEED_SUPER) + len = 512; switch (len) { case 8: len = 24; @@ -1195,6 +1197,104 @@ static int unlink_simple(struct usbtest_dev *dev, int pipe, int len) /*-------------------------------------------------------------------------*/ +struct queued_ctx { + struct completion complete; + atomic_t pending; + unsigned num; + int status; + struct urb **urbs; +}; + +static void unlink_queued_callback(struct urb *urb) +{ + int status = urb->status; + struct queued_ctx *ctx = urb->context; + + if (ctx->status) + goto done; + if (urb == ctx->urbs[ctx->num - 4] || urb == ctx->urbs[ctx->num - 2]) { + if (status == -ECONNRESET) + goto done; + /* What error should we report if the URB completed normally? */ + } + if (status != 0) + ctx->status = status; + + done: + if (atomic_dec_and_test(&ctx->pending)) + complete(&ctx->complete); +} + +static int unlink_queued(struct usbtest_dev *dev, int pipe, unsigned num, + unsigned size) +{ + struct queued_ctx ctx; + struct usb_device *udev = testdev_to_usbdev(dev); + void *buf; + dma_addr_t buf_dma; + int i; + int retval = -ENOMEM; + + init_completion(&ctx.complete); + atomic_set(&ctx.pending, 1); /* One more than the actual value */ + ctx.num = num; + ctx.status = 0; + + buf = usb_alloc_coherent(udev, size, GFP_KERNEL, &buf_dma); + if (!buf) + return retval; + memset(buf, 0, size); + + /* Allocate and init the urbs we'll queue */ + ctx.urbs = kcalloc(num, sizeof(struct urb *), GFP_KERNEL); + if (!ctx.urbs) + goto free_buf; + for (i = 0; i < num; i++) { + ctx.urbs[i] = usb_alloc_urb(0, GFP_KERNEL); + if (!ctx.urbs[i]) + goto free_urbs; + usb_fill_bulk_urb(ctx.urbs[i], udev, pipe, buf, size, + unlink_queued_callback, &ctx); + ctx.urbs[i]->transfer_dma = buf_dma; + ctx.urbs[i]->transfer_flags = URB_NO_TRANSFER_DMA_MAP; + } + + /* Submit all the URBs and then unlink URBs num - 4 and num - 2. */ + for (i = 0; i < num; i++) { + atomic_inc(&ctx.pending); + retval = usb_submit_urb(ctx.urbs[i], GFP_KERNEL); + if (retval != 0) { + dev_err(&dev->intf->dev, "submit urbs[%d] fail %d\n", + i, retval); + atomic_dec(&ctx.pending); + ctx.status = retval; + break; + } + } + if (i == num) { + usb_unlink_urb(ctx.urbs[num - 4]); + usb_unlink_urb(ctx.urbs[num - 2]); + } else { + while (--i >= 0) + usb_unlink_urb(ctx.urbs[i]); + } + + if (atomic_dec_and_test(&ctx.pending)) /* The extra count */ + complete(&ctx.complete); + wait_for_completion(&ctx.complete); + retval = ctx.status; + + free_urbs: + for (i = 0; i < num; i++) + usb_free_urb(ctx.urbs[i]); + kfree(ctx.urbs); + free_buf: + usb_free_coherent(udev, size, buf, buf_dma); + return retval; +} + +/*-------------------------------------------------------------------------*/ + static int verify_not_halted(struct usbtest_dev *tdev, int ep, struct urb *urb) { int retval; @@ -1970,8 +2070,6 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) dev->in_iso_pipe, dev->iso_in, 0); break; - /* FIXME unlink from queue (ring with N urbs) */ - /* FIXME scatterlist cancel (needs helper thread) */ /* Tests for bulk I/O using DMA mapping by core and odd address */ @@ -2064,6 +2162,26 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) dev->in_iso_pipe, dev->iso_in, 1); break; + /* unlink URBs from a bulk-OUT queue */ + case 24: + if (dev->out_pipe == 0 || !param->length || param->sglen < 4) + break; + retval = 0; + dev_info(&intf->dev, "TEST 17: unlink from %d queues of " + "%d %d-byte writes\n", + param->iterations, param->sglen, param->length); + for (i = param->iterations; retval == 0 && i > 0; --i) { + retval = unlink_queued(dev, dev->out_pipe, + param->sglen, param->length); + if (retval) { + dev_err(&intf->dev, + "unlink queued writes failed %d, " + "iterations left %d\n", retval, i); + break; + } + } + break; + } do_gettimeofday(¶m->duration); param->duration.tv_sec -= start.tv_sec; @@ -2192,6 +2310,9 @@ usbtest_probe(struct usb_interface *intf, const struct usb_device_id *id) case USB_SPEED_HIGH: tmp = "high"; break; + case USB_SPEED_SUPER: + tmp = "super"; + break; default: tmp = "unknown"; break; diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 74073b3..1309348 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -14,7 +14,7 @@ config USB_MUSB_HDRC select TWL4030_USB if MACH_OMAP_3430SDP select TWL6030_USB if MACH_OMAP_4430SDP || MACH_OMAP4_PANDA select USB_OTG_UTILS - bool 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)' + tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)' help Say Y here if your system has a dual role high speed USB controller based on the Mentor Graphics silicon IP. Then @@ -30,39 +30,39 @@ config USB_MUSB_HDRC If you do not know what this is, please say N. -# To compile this driver as a module, choose M here; the -# module will be called "musb-hdrc". + To compile this driver as a module, choose M here; the + module will be called "musb-hdrc". choice prompt "Platform Glue Layer" depends on USB_MUSB_HDRC config USB_MUSB_DAVINCI - bool "DaVinci" + tristate "DaVinci" depends on ARCH_DAVINCI_DMx config USB_MUSB_DA8XX - bool "DA8xx/OMAP-L1x" + tristate "DA8xx/OMAP-L1x" depends on ARCH_DAVINCI_DA8XX config USB_MUSB_TUSB6010 - bool "TUSB6010" + tristate "TUSB6010" depends on ARCH_OMAP config USB_MUSB_OMAP2PLUS - bool "OMAP2430 and onwards" + tristate "OMAP2430 and onwards" depends on ARCH_OMAP2PLUS config USB_MUSB_AM35X - bool "AM35x" + tristate "AM35x" depends on ARCH_OMAP config USB_MUSB_BLACKFIN - bool "Blackfin" + tristate "Blackfin" depends on (BF54x && !BF544) || (BF52x && ! BF522 && !BF523) config USB_MUSB_UX500 - bool "U8500 and U5500" + tristate "U8500 and U5500" depends on (ARCH_U8500 && AB8500_USB) || (ARCH_U5500) endchoice @@ -153,6 +153,13 @@ config MUSB_PIO_ONLY you can still disable it at run time using the "use_dma=n" module parameter. +config USB_UX500_DMA + bool + depends on USB_MUSB_HDRC && !MUSB_PIO_ONLY + default USB_MUSB_UX500 + help + Enable DMA transfers on UX500 platforms. + config USB_INVENTRA_DMA bool depends on USB_MUSB_HDRC && !MUSB_PIO_ONLY @@ -176,11 +183,3 @@ config USB_TUSB_OMAP_DMA help Enable DMA transfers on TUSB 6010 when OMAP DMA is available. -config USB_MUSB_DEBUG - depends on USB_MUSB_HDRC - bool "Enable debugging messages" - default n - help - This enables musb debugging. To set the logging level use the debug - module parameter. Starting at level 3, per-transfer (urb, usb_request, - packet, or dma transfer) tracing may kick in. diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile index 74df528..c4d228b 100644 --- a/drivers/usb/musb/Makefile +++ b/drivers/usb/musb/Makefile @@ -2,8 +2,6 @@ # for USB OTG silicon based on Mentor Graphics INVENTRA designs # -ccflags-$(CONFIG_USB_MUSB_DEBUG) := -DDEBUG - obj-$(CONFIG_USB_MUSB_HDRC) += musb_hdrc.o musb_hdrc-y := musb_core.o @@ -39,6 +37,11 @@ ifneq ($(CONFIG_MUSB_PIO_ONLY),y) ifeq ($(CONFIG_USB_TUSB_OMAP_DMA),y) musb_hdrc-y += tusb6010_omap.o + else + ifeq ($(CONFIG_USB_UX500_DMA),y) + musb_hdrc-y += ux500_dma.o + + endif endif endif endif diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index d5a3da3..23ac28f 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -151,7 +151,8 @@ static void otg_timer(unsigned long _musb) * status change events (from the transceiver) otherwise. */ devctl = musb_readb(mregs, MUSB_DEVCTL); - DBG(7, "Poll devctl %02x (%s)\n", devctl, otg_state_string(musb)); + dev_dbg(musb->controller, "Poll devctl %02x (%s)\n", devctl, + otg_state_string(musb->xceiv->state)); spin_lock_irqsave(&musb->lock, flags); switch (musb->xceiv->state) { @@ -202,20 +203,22 @@ static void am35x_musb_try_idle(struct musb *musb, unsigned long timeout) /* Never idle if active, or when VBUS timeout is not set as host */ if (musb->is_active || (musb->a_wait_bcon == 0 && musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) { - DBG(4, "%s active, deleting timer\n", otg_state_string(musb)); + dev_dbg(musb->controller, "%s active, deleting timer\n", + otg_state_string(musb->xceiv->state)); del_timer(&otg_workaround); last_timer = jiffies; return; } if (time_after(last_timer, timeout) && timer_pending(&otg_workaround)) { - DBG(4, "Longer idle timer already pending, ignoring...\n"); + dev_dbg(musb->controller, "Longer idle timer already pending, ignoring...\n"); return; } last_timer = timeout; - DBG(4, "%s inactive, starting idle timer for %u ms\n", - otg_state_string(musb), jiffies_to_msecs(timeout - jiffies)); + dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n", + otg_state_string(musb->xceiv->state), + jiffies_to_msecs(timeout - jiffies)); mod_timer(&otg_workaround, timeout); } @@ -302,9 +305,9 @@ static irqreturn_t am35x_musb_interrupt(int irq, void *hci) } /* NOTE: this must complete power-on within 100 ms. */ - DBG(2, "VBUS %s (%s)%s, devctl %02x\n", + dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n", drvvbus ? "on" : "off", - otg_state_string(musb), + otg_state_string(musb->xceiv->state), err ? " ERROR" : "", devctl); ret = IRQ_HANDLED; diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 8e2a1ff..ae8c396 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -35,6 +35,7 @@ struct bfin_glue { */ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src) { + struct musb *musb = hw_ep->musb; void __iomem *fifo = hw_ep->fifo; void __iomem *epio = hw_ep->regs; u8 epnum = hw_ep->epnum; @@ -43,7 +44,7 @@ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src) musb_writew(epio, MUSB_TXCOUNT, len); - DBG(4, "TX ep%d fifo %p count %d buf %p, epio %p\n", + dev_dbg(musb->controller, "TX ep%d fifo %p count %d buf %p, epio %p\n", hw_ep->epnum, fifo, len, src, epio); dump_fifo_data(src, len); @@ -98,6 +99,7 @@ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src) */ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) { + struct musb *musb = hw_ep->musb; void __iomem *fifo = hw_ep->fifo; u8 epnum = hw_ep->epnum; @@ -154,7 +156,7 @@ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) *(dst + len - 1) = (u8)inw((unsigned long)fifo + 4); } } - DBG(4, "%cX ep%d fifo %p count %d buf %p\n", + dev_dbg(musb->controller, "%cX ep%d fifo %p count %d buf %p\n", 'R', hw_ep->epnum, fifo, len, dst); dump_fifo_data(dst, len); @@ -279,12 +281,14 @@ static void musb_conn_timer_handler(unsigned long _musb) } break; default: - DBG(1, "%s state not handled\n", otg_state_string(musb)); + dev_dbg(musb->controller, "%s state not handled\n", + otg_state_string(musb->xceiv->state)); break; } spin_unlock_irqrestore(&musb->lock, flags); - DBG(4, "state is %s\n", otg_state_string(musb)); + dev_dbg(musb->controller, "state is %s\n", + otg_state_string(musb->xceiv->state)); } static void bfin_musb_enable(struct musb *musb) @@ -306,9 +310,9 @@ static void bfin_musb_set_vbus(struct musb *musb, int is_on) value = !value; gpio_set_value(musb->config->gpio_vrsel, value); - DBG(1, "VBUS %s, devctl %02x " + dev_dbg(musb->controller, "VBUS %s, devctl %02x " /* otg %3x conf %08x prcm %08x */ "\n", - otg_state_string(musb), + otg_state_string(musb->xceiv->state), musb_readb(musb->mregs, MUSB_DEVCTL)); } diff --git a/drivers/usb/musb/cppi_dma.c b/drivers/usb/musb/cppi_dma.c index ab434fb..149f3f3 100644 --- a/drivers/usb/musb/cppi_dma.c +++ b/drivers/usb/musb/cppi_dma.c @@ -236,7 +236,7 @@ static int cppi_controller_stop(struct dma_controller *c) musb_writel(tibase, DAVINCI_RXCPPI_INTCLR_REG, DAVINCI_DMA_ALL_CHANNELS_ENABLE); - DBG(1, "Tearing down RX and TX Channels\n"); + dev_dbg(musb->controller, "Tearing down RX and TX Channels\n"); for (i = 0; i < ARRAY_SIZE(controller->tx); i++) { /* FIXME restructure of txdma to use bds like rxdma */ controller->tx[i].last_processed = NULL; @@ -301,13 +301,13 @@ cppi_channel_allocate(struct dma_controller *c, */ if (transmit) { if (index >= ARRAY_SIZE(controller->tx)) { - DBG(1, "no %cX%d CPPI channel\n", 'T', index); + dev_dbg(musb->controller, "no %cX%d CPPI channel\n", 'T', index); return NULL; } cppi_ch = controller->tx + index; } else { if (index >= ARRAY_SIZE(controller->rx)) { - DBG(1, "no %cX%d CPPI channel\n", 'R', index); + dev_dbg(musb->controller, "no %cX%d CPPI channel\n", 'R', index); return NULL; } cppi_ch = controller->rx + index; @@ -318,13 +318,13 @@ cppi_channel_allocate(struct dma_controller *c, * with the other DMA engine too */ if (cppi_ch->hw_ep) - DBG(1, "re-allocating DMA%d %cX channel %p\n", + dev_dbg(musb->controller, "re-allocating DMA%d %cX channel %p\n", index, transmit ? 'T' : 'R', cppi_ch); cppi_ch->hw_ep = ep; cppi_ch->channel.status = MUSB_DMA_STATUS_FREE; cppi_ch->channel.max_len = 0x7fffffff; - DBG(4, "Allocate CPPI%d %cX\n", index, transmit ? 'T' : 'R'); + dev_dbg(musb->controller, "Allocate CPPI%d %cX\n", index, transmit ? 'T' : 'R'); return &cppi_ch->channel; } @@ -339,7 +339,7 @@ static void cppi_channel_release(struct dma_channel *channel) c = container_of(channel, struct cppi_channel, channel); tibase = c->controller->tibase; if (!c->hw_ep) - DBG(1, "releasing idle DMA channel %p\n", c); + dev_dbg(musb->controller, "releasing idle DMA channel %p\n", c); else if (!c->transmit) core_rxirq_enable(tibase, c->index + 1); @@ -597,7 +597,7 @@ cppi_next_tx_segment(struct musb *musb, struct cppi_channel *tx) length = min(n_bds * maxpacket, length); } - DBG(4, "TX DMA%d, pktSz %d %s bds %d dma 0x%llx len %u\n", + dev_dbg(musb->controller, "TX DMA%d, pktSz %d %s bds %d dma 0x%llx len %u\n", tx->index, maxpacket, rndis ? "rndis" : "transparent", @@ -654,7 +654,7 @@ cppi_next_tx_segment(struct musb *musb, struct cppi_channel *tx) bd->hw_options |= CPPI_ZERO_SET; } - DBG(5, "TXBD %p: nxt %08x buf %08x len %04x opt %08x\n", + dev_dbg(musb->controller, "TXBD %p: nxt %08x buf %08x len %04x opt %08x\n", bd, bd->hw_next, bd->hw_bufp, bd->hw_off_len, bd->hw_options); @@ -819,7 +819,7 @@ cppi_next_rx_segment(struct musb *musb, struct cppi_channel *rx, int onepacket) length = min(n_bds * maxpacket, length); - DBG(4, "RX DMA%d seg, maxp %d %s bds %d (cnt %d) " + dev_dbg(musb->controller, "RX DMA%d seg, maxp %d %s bds %d (cnt %d) " "dma 0x%llx len %u %u/%u\n", rx->index, maxpacket, onepacket @@ -936,7 +936,7 @@ cppi_next_rx_segment(struct musb *musb, struct cppi_channel *rx, int onepacket) DAVINCI_RXCPPI_BUFCNT0_REG + (rx->index * 4)) & 0xffff; if (i < (2 + n_bds)) { - DBG(2, "bufcnt%d underrun - %d (for %d)\n", + dev_dbg(musb->controller, "bufcnt%d underrun - %d (for %d)\n", rx->index, i, n_bds); musb_writel(tibase, DAVINCI_RXCPPI_BUFCNT0_REG + (rx->index * 4), @@ -985,7 +985,7 @@ static int cppi_channel_program(struct dma_channel *ch, /* WARN_ON(1); */ break; case MUSB_DMA_STATUS_UNKNOWN: - DBG(1, "%cX DMA%d not allocated!\n", + dev_dbg(musb->controller, "%cX DMA%d not allocated!\n", cppi_ch->transmit ? 'T' : 'R', cppi_ch->index); /* FALLTHROUGH */ @@ -1040,7 +1040,7 @@ static bool cppi_rx_scan(struct cppi *cppi, unsigned ch) if (!completed && (bd->hw_options & CPPI_OWN_SET)) break; - DBG(5, "C/RXBD %llx: nxt %08x buf %08x " + dev_dbg(musb->controller, "C/RXBD %llx: nxt %08x buf %08x " "off.len %08x opt.len %08x (%d)\n", (unsigned long long)bd->dma, bd->hw_next, bd->hw_bufp, bd->hw_off_len, bd->hw_options, @@ -1062,7 +1062,7 @@ static bool cppi_rx_scan(struct cppi *cppi, unsigned ch) * CPPI ignores those BDs even though OWN is still set. */ completed = true; - DBG(3, "rx short %d/%d (%d)\n", + dev_dbg(musb->controller, "rx short %d/%d (%d)\n", len, bd->buflen, rx->channel.actual_len); } @@ -1112,7 +1112,7 @@ static bool cppi_rx_scan(struct cppi *cppi, unsigned ch) musb_ep_select(cppi->mregs, rx->index + 1); csr = musb_readw(regs, MUSB_RXCSR); if (csr & MUSB_RXCSR_DMAENAB) { - DBG(4, "list%d %p/%p, last %llx%s, csr %04x\n", + dev_dbg(musb->controller, "list%d %p/%p, last %llx%s, csr %04x\n", rx->index, rx->head, rx->tail, rx->last_processed @@ -1175,7 +1175,7 @@ irqreturn_t cppi_interrupt(int irq, void *dev_id) return IRQ_NONE; } - DBG(4, "CPPI IRQ Tx%x Rx%x\n", tx, rx); + dev_dbg(musb->controller, "CPPI IRQ Tx%x Rx%x\n", tx, rx); /* process TX channels */ for (index = 0; tx; tx = tx >> 1, index++) { @@ -1203,7 +1203,7 @@ irqreturn_t cppi_interrupt(int irq, void *dev_id) * that needs to be acknowledged. */ if (NULL == bd) { - DBG(1, "null BD\n"); + dev_dbg(musb->controller, "null BD\n"); musb_writel(&tx_ram->tx_complete, 0, 0); continue; } @@ -1218,7 +1218,7 @@ irqreturn_t cppi_interrupt(int irq, void *dev_id) if (bd->hw_options & CPPI_OWN_SET) break; - DBG(5, "C/TXBD %p n %x b %x off %x opt %x\n", + dev_dbg(musb->controller, "C/TXBD %p n %x b %x off %x opt %x\n", bd, bd->hw_next, bd->hw_bufp, bd->hw_off_len, bd->hw_options); diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 69a0da3..662ed34 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -199,7 +199,8 @@ static void otg_timer(unsigned long _musb) * status change events (from the transceiver) otherwise. */ devctl = musb_readb(mregs, MUSB_DEVCTL); - DBG(7, "Poll devctl %02x (%s)\n", devctl, otg_state_string(musb)); + dev_dbg(musb->controller, "Poll devctl %02x (%s)\n", devctl, + otg_state_string(musb->xceiv->state)); spin_lock_irqsave(&musb->lock, flags); switch (musb->xceiv->state) { @@ -273,20 +274,22 @@ static void da8xx_musb_try_idle(struct musb *musb, unsigned long timeout) /* Never idle if active, or when VBUS timeout is not set as host */ if (musb->is_active || (musb->a_wait_bcon == 0 && musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) { - DBG(4, "%s active, deleting timer\n", otg_state_string(musb)); + dev_dbg(musb->controller, "%s active, deleting timer\n", + otg_state_string(musb->xceiv->state)); del_timer(&otg_workaround); last_timer = jiffies; return; } if (time_after(last_timer, timeout) && timer_pending(&otg_workaround)) { - DBG(4, "Longer idle timer already pending, ignoring...\n"); + dev_dbg(musb->controller, "Longer idle timer already pending, ignoring...\n"); return; } last_timer = timeout; - DBG(4, "%s inactive, starting idle timer for %u ms\n", - otg_state_string(musb), jiffies_to_msecs(timeout - jiffies)); + dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n", + otg_state_string(musb->xceiv->state), + jiffies_to_msecs(timeout - jiffies)); mod_timer(&otg_workaround, timeout); } @@ -311,7 +314,7 @@ static irqreturn_t da8xx_musb_interrupt(int irq, void *hci) goto eoi; musb_writel(reg_base, DA8XX_USB_INTR_SRC_CLEAR_REG, status); - DBG(4, "USB IRQ %08x\n", status); + dev_dbg(musb->controller, "USB IRQ %08x\n", status); musb->int_rx = (status & DA8XX_INTR_RX_MASK) >> DA8XX_INTR_RX_SHIFT; musb->int_tx = (status & DA8XX_INTR_TX_MASK) >> DA8XX_INTR_TX_SHIFT; @@ -363,9 +366,9 @@ static irqreturn_t da8xx_musb_interrupt(int irq, void *hci) portstate(musb->port1_status &= ~USB_PORT_STAT_POWER); } - DBG(2, "VBUS %s (%s)%s, devctl %02x\n", + dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n", drvvbus ? "on" : "off", - otg_state_string(musb), + otg_state_string(musb->xceiv->state), err ? " ERROR" : "", devctl); ret = IRQ_HANDLED; @@ -410,7 +413,7 @@ static int da8xx_musb_set_mode(struct musb *musb, u8 musb_mode) break; #endif default: - DBG(2, "Trying to set unsupported mode %u\n", musb_mode); + dev_dbg(musb->controller, "Trying to set unsupported mode %u\n", musb_mode); } __raw_writel(cfgchip2, CFGCHIP2); diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index e6de097..2a2adf6 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -220,7 +220,8 @@ static void otg_timer(unsigned long _musb) * status change events (from the transceiver) otherwise. */ devctl = musb_readb(mregs, MUSB_DEVCTL); - DBG(7, "poll devctl %02x (%s)\n", devctl, otg_state_string(musb)); + dev_dbg(musb->controller, "poll devctl %02x (%s)\n", devctl, + otg_state_string(musb->xceiv->state)); spin_lock_irqsave(&musb->lock, flags); switch (musb->xceiv->state) { @@ -297,7 +298,7 @@ static irqreturn_t davinci_musb_interrupt(int irq, void *__hci) /* ack and handle non-CPPI interrupts */ tmp = musb_readl(tibase, DAVINCI_USB_INT_SRC_MASKED_REG); musb_writel(tibase, DAVINCI_USB_INT_SRC_CLR_REG, tmp); - DBG(4, "IRQ %08x\n", tmp); + dev_dbg(musb->controller, "IRQ %08x\n", tmp); musb->int_rx = (tmp & DAVINCI_USB_RXINT_MASK) >> DAVINCI_USB_RXINT_SHIFT; @@ -354,9 +355,9 @@ static irqreturn_t davinci_musb_interrupt(int irq, void *__hci) * (OTG_TIME_A_WAIT_VRISE) but we don't check for that. */ davinci_musb_source_power(musb, drvvbus, 0); - DBG(2, "VBUS %s (%s)%s, devctl %02x\n", + dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n", drvvbus ? "on" : "off", - otg_state_string(musb), + otg_state_string(musb->xceiv->state), err ? " ERROR" : "", devctl); retval = IRQ_HANDLED; @@ -484,7 +485,7 @@ static int davinci_musb_exit(struct musb *musb) break; if ((devctl & MUSB_DEVCTL_VBUS) != warn) { warn = devctl & MUSB_DEVCTL_VBUS; - DBG(1, "VBUS %d\n", + dev_dbg(musb->controller, "VBUS %d\n", warn >> MUSB_DEVCTL_VBUS_SHIFT); } msleep(1000); @@ -493,7 +494,7 @@ static int davinci_musb_exit(struct musb *musb) /* in OTG mode, another host might be connected */ if (devctl & MUSB_DEVCTL_VBUS) - DBG(1, "VBUS off timeout (devctl %02x)\n", devctl); + dev_dbg(musb->controller, "VBUS off timeout (devctl %02x)\n", devctl); } phy_off(); diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index f10ff00..ab8e100 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -104,10 +104,6 @@ #define TA_WAIT_BCON(m) max_t(int, (m)->a_wait_bcon, OTG_TIME_A_WAIT_BCON) -unsigned musb_debug; -module_param_named(debug, musb_debug, uint, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Debug message level. Default = 0"); - #define DRIVER_AUTHOR "Mentor Graphics, Texas Instruments, Nokia" #define DRIVER_DESC "Inventra Dual-Role USB Controller Driver" @@ -157,10 +153,8 @@ static int musb_ulpi_read(struct otg_transceiver *otg, u32 offset) while (!(musb_readb(addr, MUSB_ULPI_REG_CONTROL) & MUSB_ULPI_REG_CMPLT)) { i++; - if (i == 10000) { - DBG(3, "ULPI read timed out\n"); + if (i == 10000) return -ETIMEDOUT; - } } r = musb_readb(addr, MUSB_ULPI_REG_CONTROL); @@ -190,10 +184,8 @@ static int musb_ulpi_write(struct otg_transceiver *otg, while (!(musb_readb(addr, MUSB_ULPI_REG_CONTROL) & MUSB_ULPI_REG_CMPLT)) { i++; - if (i == 10000) { - DBG(3, "ULPI write timed out\n"); + if (i == 10000) return -ETIMEDOUT; - } } r = musb_readb(addr, MUSB_ULPI_REG_CONTROL); @@ -221,11 +213,12 @@ static struct otg_io_access_ops musb_ulpi_access = { */ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src) { + struct musb *musb = hw_ep->musb; void __iomem *fifo = hw_ep->fifo; prefetch((u8 *)src); - DBG(4, "%cX ep%d fifo %p count %d buf %p\n", + dev_dbg(musb->controller, "%cX ep%d fifo %p count %d buf %p\n", 'T', hw_ep->epnum, fifo, len, src); /* we can't assume unaligned reads work */ @@ -262,9 +255,10 @@ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src) */ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) { + struct musb *musb = hw_ep->musb; void __iomem *fifo = hw_ep->fifo; - DBG(4, "%cX ep%d fifo %p count %d buf %p\n", + dev_dbg(musb->controller, "%cX ep%d fifo %p count %d buf %p\n", 'R', hw_ep->epnum, fifo, len, dst); /* we can't assume unaligned writes work */ @@ -333,26 +327,6 @@ void musb_load_testpacket(struct musb *musb) /*-------------------------------------------------------------------------*/ -const char *otg_state_string(struct musb *musb) -{ - switch (musb->xceiv->state) { - case OTG_STATE_A_IDLE: return "a_idle"; - case OTG_STATE_A_WAIT_VRISE: return "a_wait_vrise"; - case OTG_STATE_A_WAIT_BCON: return "a_wait_bcon"; - case OTG_STATE_A_HOST: return "a_host"; - case OTG_STATE_A_SUSPEND: return "a_suspend"; - case OTG_STATE_A_PERIPHERAL: return "a_peripheral"; - case OTG_STATE_A_WAIT_VFALL: return "a_wait_vfall"; - case OTG_STATE_A_VBUS_ERR: return "a_vbus_err"; - case OTG_STATE_B_IDLE: return "b_idle"; - case OTG_STATE_B_SRP_INIT: return "b_srp_init"; - case OTG_STATE_B_PERIPHERAL: return "b_peripheral"; - case OTG_STATE_B_WAIT_ACON: return "b_wait_acon"; - case OTG_STATE_B_HOST: return "b_host"; - default: return "UNDEFINED"; - } -} - #ifdef CONFIG_USB_MUSB_OTG /* @@ -366,19 +340,21 @@ void musb_otg_timer_func(unsigned long data) spin_lock_irqsave(&musb->lock, flags); switch (musb->xceiv->state) { case OTG_STATE_B_WAIT_ACON: - DBG(1, "HNP: b_wait_acon timeout; back to b_peripheral\n"); + dev_dbg(musb->controller, "HNP: b_wait_acon timeout; back to b_peripheral\n"); musb_g_disconnect(musb); musb->xceiv->state = OTG_STATE_B_PERIPHERAL; musb->is_active = 0; break; case OTG_STATE_A_SUSPEND: case OTG_STATE_A_WAIT_BCON: - DBG(1, "HNP: %s timeout\n", otg_state_string(musb)); + dev_dbg(musb->controller, "HNP: %s timeout\n", + otg_state_string(musb->xceiv->state)); musb_platform_set_vbus(musb, 0); musb->xceiv->state = OTG_STATE_A_WAIT_VFALL; break; default: - DBG(1, "HNP: Unhandled mode %s\n", otg_state_string(musb)); + dev_dbg(musb->controller, "HNP: Unhandled mode %s\n", + otg_state_string(musb->xceiv->state)); } musb->ignore_disconnect = 0; spin_unlock_irqrestore(&musb->lock, flags); @@ -393,15 +369,16 @@ void musb_hnp_stop(struct musb *musb) void __iomem *mbase = musb->mregs; u8 reg; - DBG(1, "HNP: stop from %s\n", otg_state_string(musb)); + dev_dbg(musb->controller, "HNP: stop from %s\n", otg_state_string(musb->xceiv->state)); switch (musb->xceiv->state) { case OTG_STATE_A_PERIPHERAL: musb_g_disconnect(musb); - DBG(1, "HNP: back to %s\n", otg_state_string(musb)); + dev_dbg(musb->controller, "HNP: back to %s\n", + otg_state_string(musb->xceiv->state)); break; case OTG_STATE_B_HOST: - DBG(1, "HNP: Disabling HR\n"); + dev_dbg(musb->controller, "HNP: Disabling HR\n"); hcd->self.is_b_host = 0; musb->xceiv->state = OTG_STATE_B_PERIPHERAL; MUSB_DEV_MODE(musb); @@ -411,8 +388,8 @@ void musb_hnp_stop(struct musb *musb) /* REVISIT: Start SESSION_REQUEST here? */ break; default: - DBG(1, "HNP: Stopping in unknown state %s\n", - otg_state_string(musb)); + dev_dbg(musb->controller, "HNP: Stopping in unknown state %s\n", + otg_state_string(musb->xceiv->state)); } /* @@ -442,7 +419,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, { irqreturn_t handled = IRQ_NONE; - DBG(3, "<== Power=%02x, DevCtl=%02x, int_usb=0x%x\n", power, devctl, + dev_dbg(musb->controller, "<== Power=%02x, DevCtl=%02x, int_usb=0x%x\n", power, devctl, int_usb); /* in host mode, the peripheral may issue remote wakeup. @@ -451,7 +428,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, */ if (int_usb & MUSB_INTR_RESUME) { handled = IRQ_HANDLED; - DBG(3, "RESUME (%s)\n", otg_state_string(musb)); + dev_dbg(musb->controller, "RESUME (%s)\n", otg_state_string(musb->xceiv->state)); if (devctl & MUSB_DEVCTL_HM) { #ifdef CONFIG_USB_MUSB_HDRC_HCD @@ -466,7 +443,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, if (power & MUSB_POWER_SUSPENDM) { /* spurious */ musb->int_usb &= ~MUSB_INTR_SUSPEND; - DBG(2, "Spurious SUSPENDM\n"); + dev_dbg(musb->controller, "Spurious SUSPENDM\n"); break; } @@ -492,7 +469,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, default: WARNING("bogus %s RESUME (%s)\n", "host", - otg_state_string(musb)); + otg_state_string(musb->xceiv->state)); } #endif } else { @@ -526,7 +503,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, default: WARNING("bogus %s RESUME (%s)\n", "peripheral", - otg_state_string(musb)); + otg_state_string(musb->xceiv->state)); } } } @@ -538,11 +515,12 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS && (devctl & MUSB_DEVCTL_BDEVICE)) { - DBG(3, "SessReq while on B state\n"); + dev_dbg(musb->controller, "SessReq while on B state\n"); return IRQ_HANDLED; } - DBG(1, "SESSION_REQUEST (%s)\n", otg_state_string(musb)); + dev_dbg(musb->controller, "SESSION_REQUEST (%s)\n", + otg_state_string(musb->xceiv->state)); /* IRQ arrives from ID pin sense or (later, if VBUS power * is removed) SRP. responses are time critical: @@ -606,8 +584,8 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, break; } - DBG(1, "VBUS_ERROR in %s (%02x, %s), retry #%d, port1 %08x\n", - otg_state_string(musb), + dev_dbg(musb->controller, "VBUS_ERROR in %s (%02x, %s), retry #%d, port1 %08x\n", + otg_state_string(musb->xceiv->state), devctl, ({ char *s; switch (devctl & MUSB_DEVCTL_VBUS) { @@ -632,8 +610,8 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, #endif if (int_usb & MUSB_INTR_SUSPEND) { - DBG(1, "SUSPEND (%s) devctl %02x power %02x\n", - otg_state_string(musb), devctl, power); + dev_dbg(musb->controller, "SUSPEND (%s) devctl %02x power %02x\n", + otg_state_string(musb->xceiv->state), devctl, power); handled = IRQ_HANDLED; switch (musb->xceiv->state) { @@ -665,7 +643,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, if (musb->is_active) { #ifdef CONFIG_USB_MUSB_OTG musb->xceiv->state = OTG_STATE_B_WAIT_ACON; - DBG(1, "HNP: Setting timer for b_ase0_brst\n"); + dev_dbg(musb->controller, "HNP: Setting timer for b_ase0_brst\n"); mod_timer(&musb->otg_timer, jiffies + msecs_to_jiffies( OTG_TIME_B_ASE0_BRST)); @@ -684,7 +662,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, break; case OTG_STATE_B_HOST: /* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */ - DBG(1, "REVISIT: SUSPEND as B_HOST\n"); + dev_dbg(musb->controller, "REVISIT: SUSPEND as B_HOST\n"); break; default: /* "should not happen" */ @@ -727,14 +705,14 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb, switch (musb->xceiv->state) { case OTG_STATE_B_PERIPHERAL: if (int_usb & MUSB_INTR_SUSPEND) { - DBG(1, "HNP: SUSPEND+CONNECT, now b_host\n"); + dev_dbg(musb->controller, "HNP: SUSPEND+CONNECT, now b_host\n"); int_usb &= ~MUSB_INTR_SUSPEND; goto b_host; } else - DBG(1, "CONNECT as b_peripheral???\n"); + dev_dbg(musb->controller, "CONNECT as b_peripheral???\n"); break; case OTG_STATE_B_WAIT_ACON: - DBG(1, "HNP: CONNECT, now b_host\n"); + dev_dbg(musb->controller, "HNP: CONNECT, now b_host\n"); b_host: musb->xceiv->state = OTG_STATE_B_HOST; hcd->self.is_b_host = 1; @@ -757,14 +735,14 @@ b_host: else usb_hcd_resume_root_hub(hcd); - DBG(1, "CONNECT (%s) devctl %02x\n", - otg_state_string(musb), devctl); + dev_dbg(musb->controller, "CONNECT (%s) devctl %02x\n", + otg_state_string(musb->xceiv->state), devctl); } #endif /* CONFIG_USB_MUSB_HDRC_HCD */ if ((int_usb & MUSB_INTR_DISCONNECT) && !musb->ignore_disconnect) { - DBG(1, "DISCONNECT (%s) as %s, devctl %02x\n", - otg_state_string(musb), + dev_dbg(musb->controller, "DISCONNECT (%s) as %s, devctl %02x\n", + otg_state_string(musb->xceiv->state), MUSB_MODE(musb), devctl); handled = IRQ_HANDLED; @@ -807,7 +785,7 @@ b_host: #endif /* GADGET */ default: WARNING("unhandled DISCONNECT transition (%s)\n", - otg_state_string(musb)); + otg_state_string(musb->xceiv->state)); break; } } @@ -826,13 +804,14 @@ b_host: * stop the session. */ if (devctl & (MUSB_DEVCTL_FSDEV | MUSB_DEVCTL_LSDEV)) - DBG(1, "BABBLE devctl: %02x\n", devctl); + dev_dbg(musb->controller, "BABBLE devctl: %02x\n", devctl); else { ERR("Stopping host session -- babble\n"); musb_writeb(musb->mregs, MUSB_DEVCTL, 0); } } else if (is_peripheral_capable()) { - DBG(1, "BUS RESET as %s\n", otg_state_string(musb)); + dev_dbg(musb->controller, "BUS RESET as %s\n", + otg_state_string(musb->xceiv->state)); switch (musb->xceiv->state) { #ifdef CONFIG_USB_OTG case OTG_STATE_A_SUSPEND: @@ -845,9 +824,9 @@ b_host: /* FALLTHROUGH */ case OTG_STATE_A_WAIT_BCON: /* OPT TD.4.7-900ms */ /* never use invalid T(a_wait_bcon) */ - DBG(1, "HNP: in %s, %d msec timeout\n", - otg_state_string(musb), - TA_WAIT_BCON(musb)); + dev_dbg(musb->controller, "HNP: in %s, %d msec timeout\n", + otg_state_string(musb->xceiv->state), + TA_WAIT_BCON(musb)); mod_timer(&musb->otg_timer, jiffies + msecs_to_jiffies(TA_WAIT_BCON(musb))); break; @@ -857,8 +836,8 @@ b_host: musb_g_reset(musb); break; case OTG_STATE_B_WAIT_ACON: - DBG(1, "HNP: RESET (%s), to b_peripheral\n", - otg_state_string(musb)); + dev_dbg(musb->controller, "HNP: RESET (%s), to b_peripheral\n", + otg_state_string(musb->xceiv->state)); musb->xceiv->state = OTG_STATE_B_PERIPHERAL; musb_g_reset(musb); break; @@ -870,8 +849,8 @@ b_host: musb_g_reset(musb); break; default: - DBG(1, "Unhandled BUS RESET as %s\n", - otg_state_string(musb)); + dev_dbg(musb->controller, "Unhandled BUS RESET as %s\n", + otg_state_string(musb->xceiv->state)); } } } @@ -894,7 +873,7 @@ b_host: u8 epnum; u16 frame; - DBG(6, "START_OF_FRAME\n"); + dev_dbg(musb->controller, "START_OF_FRAME\n"); handled = IRQ_HANDLED; /* start any periodic Tx transfers waiting for current frame */ @@ -936,7 +915,7 @@ void musb_start(struct musb *musb) void __iomem *regs = musb->mregs; u8 devctl = musb_readb(regs, MUSB_DEVCTL); - DBG(2, "<== devctl %02x\n", devctl); + dev_dbg(musb->controller, "<== devctl %02x\n", devctl); /* Set INT enable registers, enable interrupts */ musb_writew(regs, MUSB_INTRTXE, musb->epmask); @@ -1013,7 +992,7 @@ void musb_stop(struct musb *musb) /* stop IRQs, timers, ... */ musb_platform_disable(musb); musb_generic_disable(musb); - DBG(3, "HDRC disabled\n"); + dev_dbg(musb->controller, "HDRC disabled\n"); /* FIXME * - mark host and/or peripheral drivers unusable/inactive @@ -1359,7 +1338,7 @@ static int __init ep_config_from_hw(struct musb *musb) void *mbase = musb->mregs; int ret = 0; - DBG(2, "<== static silicon ep config\n"); + dev_dbg(musb->controller, "<== static silicon ep config\n"); /* FIXME pick up ep0 maxpacket size */ @@ -1506,7 +1485,7 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb) #endif if (hw_ep->max_packet_sz_tx) { - DBG(1, + dev_dbg(musb->controller, "%s: hw_ep %d%s, %smax %d\n", musb_driver_name, i, hw_ep->is_shared_fifo ? "shared" : "tx", @@ -1515,7 +1494,7 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb) hw_ep->max_packet_sz_tx); } if (hw_ep->max_packet_sz_rx && !hw_ep->is_shared_fifo) { - DBG(1, + dev_dbg(musb->controller, "%s: hw_ep %d%s, %smax %d\n", musb_driver_name, i, "rx", @@ -1524,7 +1503,7 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb) hw_ep->max_packet_sz_rx); } if (!(hw_ep->max_packet_sz_tx || hw_ep->max_packet_sz_rx)) - DBG(1, "hw_ep %d not configured\n", i); + dev_dbg(musb->controller, "hw_ep %d not configured\n", i); } return 0; @@ -1577,14 +1556,14 @@ irqreturn_t musb_interrupt(struct musb *musb) devctl = musb_readb(musb->mregs, MUSB_DEVCTL); power = musb_readb(musb->mregs, MUSB_POWER); - DBG(4, "** IRQ %s usb%04x tx%04x rx%04x\n", + dev_dbg(musb->controller, "** IRQ %s usb%04x tx%04x rx%04x\n", (devctl & MUSB_DEVCTL_HM) ? "host" : "peripheral", musb->int_usb, musb->int_tx, musb->int_rx); #ifdef CONFIG_USB_GADGET_MUSB_HDRC if (is_otg_enabled(musb) || is_peripheral_enabled(musb)) if (!musb->gadget_driver) { - DBG(5, "No gadget driver loaded\n"); + dev_dbg(musb->controller, "No gadget driver loaded\n"); return IRQ_HANDLED; } #endif @@ -1649,7 +1628,7 @@ irqreturn_t musb_interrupt(struct musb *musb) return retval; } - +EXPORT_SYMBOL_GPL(musb_interrupt); #ifndef CONFIG_MUSB_PIO_ONLY static int __initdata use_dma = 1; @@ -1713,7 +1692,7 @@ musb_mode_show(struct device *dev, struct device_attribute *attr, char *buf) int ret = -EINVAL; spin_lock_irqsave(&musb->lock, flags); - ret = sprintf(buf, "%s\n", otg_state_string(musb)); + ret = sprintf(buf, "%s\n", otg_state_string(musb->xceiv->state)); spin_unlock_irqrestore(&musb->lock, flags); return ret; @@ -2075,7 +2054,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) status = usb_add_hcd(musb_to_hcd(musb), -1, 0); hcd->self.uses_pio_for_control = 1; - DBG(1, "%s mode, status %d, devctl %02x %c\n", + dev_dbg(musb->controller, "%s mode, status %d, devctl %02x %c\n", "HOST", status, musb_readb(musb->mregs, MUSB_DEVCTL), (musb_readb(musb->mregs, MUSB_DEVCTL) @@ -2089,7 +2068,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) status = musb_gadget_setup(musb); - DBG(1, "%s mode, status %d, dev%02x\n", + dev_dbg(musb->controller, "%s mode, status %d, dev%02x\n", is_otg_enabled(musb) ? "OTG" : "PERIPHERAL", status, musb_readb(musb->mregs, MUSB_DEVCTL)); @@ -2460,6 +2439,8 @@ static int __init musb_init(void) "musb-dma" #elif defined(CONFIG_USB_TUSB_OMAP_DMA) "tusb-omap-dma" +#elif defined(CONFIG_USB_UX500_DMA) + "ux500-dma" #else "?dma?" #endif @@ -2471,8 +2452,8 @@ static int __init musb_init(void) #elif defined(CONFIG_USB_MUSB_HDRC_HCD) "host" #endif - ", debug=%d\n", - musb_driver_name, musb_debug); + , + musb_driver_name); return platform_driver_probe(&musb_driver, musb_probe); } diff --git a/drivers/usb/musb/musb_debug.h b/drivers/usb/musb/musb_debug.h index 94f6973..742eada 100644 --- a/drivers/usb/musb/musb_debug.h +++ b/drivers/usb/musb/musb_debug.h @@ -42,20 +42,6 @@ #define INFO(fmt, args...) yprintk(KERN_INFO, fmt, ## args) #define ERR(fmt, args...) yprintk(KERN_ERR, fmt, ## args) -#define DBG(level, format, args...) do { \ - if (_dbg_level(level)) \ - pr_debug("%s %d: " format, __func__, __LINE__, ## args); \ - } while (0) - -extern unsigned musb_debug; - -static inline int _dbg_level(unsigned l) -{ - return musb_debug >= l; -} - -extern const char *otg_state_string(struct musb *); - #ifdef CONFIG_DEBUG_FS extern int musb_init_debugfs(struct musb *musb); extern void musb_exit_debugfs(struct musb *musb); diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index f47c201..0a50a35 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -147,7 +147,8 @@ static inline void unmap_dma_buffer(struct musb_request *request, return; if (request->request.dma == DMA_ADDR_INVALID) { - DBG(20, "not unmapping a never mapped buffer\n"); + dev_vdbg(musb->controller, + "not unmapping a never mapped buffer\n"); return; } if (request->map_state == MUSB_MAPPED) { @@ -198,11 +199,11 @@ __acquires(ep->musb->lock) spin_unlock(&musb->lock); unmap_dma_buffer(req, musb); if (request->status == 0) - DBG(5, "%s done request %p, %d/%d\n", + dev_dbg(musb->controller, "%s done request %p, %d/%d\n", ep->end_point.name, request, req->request.actual, req->request.length); else - DBG(2, "%s request %p, %d/%d fault %d\n", + dev_dbg(musb->controller, "%s request %p, %d/%d fault %d\n", ep->end_point.name, request, req->request.actual, req->request.length, request->status); @@ -219,6 +220,7 @@ __acquires(ep->musb->lock) */ static void nuke(struct musb_ep *ep, const int status) { + struct musb *musb = ep->musb; struct musb_request *req = NULL; void __iomem *epio = ep->musb->endpoints[ep->current_epnum].regs; @@ -246,7 +248,8 @@ static void nuke(struct musb_ep *ep, const int status) } value = c->channel_abort(ep->dma); - DBG(value ? 1 : 6, "%s: abort DMA --> %d\n", ep->name, value); + dev_dbg(musb->controller, "%s: abort DMA --> %d\n", + ep->name, value); c->channel_release(ep->dma); ep->dma = NULL; } @@ -329,7 +332,7 @@ static void txstate(struct musb *musb, struct musb_request *req) /* we shouldn't get here while DMA is active ... but we do ... */ if (dma_channel_status(musb_ep->dma) == MUSB_DMA_STATUS_BUSY) { - DBG(4, "dma pending...\n"); + dev_dbg(musb->controller, "dma pending...\n"); return; } @@ -341,18 +344,18 @@ static void txstate(struct musb *musb, struct musb_request *req) (int)(request->length - request->actual)); if (csr & MUSB_TXCSR_TXPKTRDY) { - DBG(5, "%s old packet still ready , txcsr %03x\n", + dev_dbg(musb->controller, "%s old packet still ready , txcsr %03x\n", musb_ep->end_point.name, csr); return; } if (csr & MUSB_TXCSR_P_SENDSTALL) { - DBG(5, "%s stalling, txcsr %03x\n", + dev_dbg(musb->controller, "%s stalling, txcsr %03x\n", musb_ep->end_point.name, csr); return; } - DBG(4, "hw_ep%d, maxpacket %d, fifo count %d, txcsr %03x\n", + dev_dbg(musb->controller, "hw_ep%d, maxpacket %d, fifo count %d, txcsr %03x\n", epnum, musb_ep->packet_sz, fifo_count, csr); @@ -369,7 +372,7 @@ static void txstate(struct musb *musb, struct musb_request *req) /* MUSB_TXCSR_P_ISO is still set correctly */ -#ifdef CONFIG_USB_INVENTRA_DMA +#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) { if (request_size < musb_ep->packet_sz) musb_ep->dma->desired_mode = 0; @@ -469,7 +472,7 @@ static void txstate(struct musb *musb, struct musb_request *req) } /* host may already have the data when this message shows... */ - DBG(3, "%s TX/IN %s len %d/%d, txcsr %04x, fifo %d/%d\n", + dev_dbg(musb->controller, "%s TX/IN %s len %d/%d, txcsr %04x, fifo %d/%d\n", musb_ep->end_point.name, use_dma ? "dma" : "pio", request->actual, request->length, musb_readw(epio, MUSB_TXCSR), @@ -496,7 +499,7 @@ void musb_g_tx(struct musb *musb, u8 epnum) request = &req->request; csr = musb_readw(epio, MUSB_TXCSR); - DBG(4, "<== %s, txcsr %04x\n", musb_ep->end_point.name, csr); + dev_dbg(musb->controller, "<== %s, txcsr %04x\n", musb_ep->end_point.name, csr); dma = is_dma_capable() ? musb_ep->dma : NULL; @@ -516,7 +519,8 @@ void musb_g_tx(struct musb *musb, u8 epnum) csr |= MUSB_TXCSR_P_WZC_BITS; csr &= ~(MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_TXPKTRDY); musb_writew(epio, MUSB_TXCSR, csr); - DBG(20, "underrun on ep%d, req %p\n", epnum, request); + dev_vdbg(musb->controller, "underrun on ep%d, req %p\n", + epnum, request); } if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { @@ -524,7 +528,7 @@ void musb_g_tx(struct musb *musb, u8 epnum) * SHOULD NOT HAPPEN... has with CPPI though, after * changing SENDSTALL (and other cases); harmless? */ - DBG(5, "%s dma still busy?\n", musb_ep->end_point.name); + dev_dbg(musb->controller, "%s dma still busy?\n", musb_ep->end_point.name); return; } @@ -540,7 +544,7 @@ void musb_g_tx(struct musb *musb, u8 epnum) /* Ensure writebuffer is empty. */ csr = musb_readw(epio, MUSB_TXCSR); request->actual += musb_ep->dma->actual_len; - DBG(4, "TXCSR%d %04x, DMA off, len %zu, req %p\n", + dev_dbg(musb->controller, "TXCSR%d %04x, DMA off, len %zu, req %p\n", epnum, csr, musb_ep->dma->actual_len, request); } @@ -551,7 +555,7 @@ void musb_g_tx(struct musb *musb, u8 epnum) if ((request->zero && request->length && (request->length % musb_ep->packet_sz == 0) && (request->actual == request->length)) -#ifdef CONFIG_USB_INVENTRA_DMA +#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) || (is_dma && (!dma->desired_mode || (request->actual & (musb_ep->packet_sz - 1)))) @@ -564,7 +568,7 @@ void musb_g_tx(struct musb *musb, u8 epnum) if (csr & MUSB_TXCSR_TXPKTRDY) return; - DBG(4, "sending zero pkt\n"); + dev_dbg(musb->controller, "sending zero pkt\n"); musb_writew(epio, MUSB_TXCSR, MUSB_TXCSR_MODE | MUSB_TXCSR_TXPKTRDY); request->zero = 0; @@ -574,7 +578,7 @@ void musb_g_tx(struct musb *musb, u8 epnum) musb_g_giveback(musb_ep, request, 0); req = musb_ep->desc ? next_request(musb_ep) : NULL; if (!req) { - DBG(4, "%s idle now\n", + dev_dbg(musb->controller, "%s idle now\n", musb_ep->end_point.name); return; } @@ -640,12 +644,12 @@ static void rxstate(struct musb *musb, struct musb_request *req) /* We shouldn't get here while DMA is active, but we do... */ if (dma_channel_status(musb_ep->dma) == MUSB_DMA_STATUS_BUSY) { - DBG(4, "DMA pending...\n"); + dev_dbg(musb->controller, "DMA pending...\n"); return; } if (csr & MUSB_RXCSR_P_SENDSTALL) { - DBG(5, "%s stalling, RXCSR %04x\n", + dev_dbg(musb->controller, "%s stalling, RXCSR %04x\n", musb_ep->end_point.name, csr); return; } @@ -754,10 +758,57 @@ static void rxstate(struct musb *musb, struct musb_request *req) if (use_dma) return; } +#elif defined(CONFIG_USB_UX500_DMA) + if ((is_buffer_mapped(req)) && + (request->actual < request->length)) { + + struct dma_controller *c; + struct dma_channel *channel; + int transfer_size = 0; + + c = musb->dma_controller; + channel = musb_ep->dma; + + /* In case first packet is short */ + if (len < musb_ep->packet_sz) + transfer_size = len; + else if (request->short_not_ok) + transfer_size = min(request->length - + request->actual, + channel->max_len); + else + transfer_size = min(request->length - + request->actual, + (unsigned)len); + + csr &= ~MUSB_RXCSR_DMAMODE; + csr |= (MUSB_RXCSR_DMAENAB | + MUSB_RXCSR_AUTOCLEAR); + + musb_writew(epio, MUSB_RXCSR, csr); + + if (transfer_size <= musb_ep->packet_sz) { + musb_ep->dma->desired_mode = 0; + } else { + musb_ep->dma->desired_mode = 1; + /* Mode must be set after DMAENAB */ + csr |= MUSB_RXCSR_DMAMODE; + musb_writew(epio, MUSB_RXCSR, csr); + } + + if (c->channel_program(channel, + musb_ep->packet_sz, + channel->desired_mode, + request->dma + + request->actual, + transfer_size)) + + return; + } #endif /* Mentor's DMA */ fifo_count = request->length - request->actual; - DBG(3, "%s OUT/RX pio fifo %d/%d, maxpacket %d\n", + dev_dbg(musb->controller, "%s OUT/RX pio fifo %d/%d, maxpacket %d\n", musb_ep->end_point.name, len, fifo_count, musb_ep->packet_sz); @@ -846,7 +897,7 @@ void musb_g_rx(struct musb *musb, u8 epnum) csr = musb_readw(epio, MUSB_RXCSR); dma = is_dma_capable() ? musb_ep->dma : NULL; - DBG(4, "<== %s, rxcsr %04x%s %p\n", musb_ep->end_point.name, + dev_dbg(musb->controller, "<== %s, rxcsr %04x%s %p\n", musb_ep->end_point.name, csr, dma ? " (dma)" : "", request); if (csr & MUSB_RXCSR_P_SENTSTALL) { @@ -861,19 +912,18 @@ void musb_g_rx(struct musb *musb, u8 epnum) csr &= ~MUSB_RXCSR_P_OVERRUN; musb_writew(epio, MUSB_RXCSR, csr); - DBG(3, "%s iso overrun on %p\n", musb_ep->name, request); + dev_dbg(musb->controller, "%s iso overrun on %p\n", musb_ep->name, request); if (request->status == -EINPROGRESS) request->status = -EOVERFLOW; } if (csr & MUSB_RXCSR_INCOMPRX) { /* REVISIT not necessarily an error */ - DBG(4, "%s, incomprx\n", musb_ep->end_point.name); + dev_dbg(musb->controller, "%s, incomprx\n", musb_ep->end_point.name); } if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { /* "should not happen"; likely RXPKTRDY pending for DMA */ - DBG((csr & MUSB_RXCSR_DMAENAB) ? 4 : 1, - "%s busy, csr %04x\n", + dev_dbg(musb->controller, "%s busy, csr %04x\n", musb_ep->end_point.name, csr); return; } @@ -887,12 +937,13 @@ void musb_g_rx(struct musb *musb, u8 epnum) request->actual += musb_ep->dma->actual_len; - DBG(4, "RXCSR%d %04x, dma off, %04x, len %zu, req %p\n", + dev_dbg(musb->controller, "RXCSR%d %04x, dma off, %04x, len %zu, req %p\n", epnum, csr, musb_readw(epio, MUSB_RXCSR), musb_ep->dma->actual_len, request); -#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_TUSB_OMAP_DMA) +#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_TUSB_OMAP_DMA) || \ + defined(CONFIG_USB_UX500_DMA) /* Autoclear doesn't clear RxPktRdy for short packets */ if ((dma->desired_mode == 0 && !hw_ep->rx_double_buffered) || (dma->actual_len @@ -922,7 +973,8 @@ void musb_g_rx(struct musb *musb, u8 epnum) if (!req) return; } -#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_TUSB_OMAP_DMA) +#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_TUSB_OMAP_DMA) || \ + defined(CONFIG_USB_UX500_DMA) exit: #endif /* Analyze request */ @@ -978,7 +1030,7 @@ static int musb_gadget_enable(struct usb_ep *ep, ok = musb->hb_iso_rx; if (!ok) { - DBG(4, "no support for high bandwidth ISO\n"); + dev_dbg(musb->controller, "no support for high bandwidth ISO\n"); goto fail; } musb_ep->hb_mult = (tmp >> 11) & 3; @@ -1002,7 +1054,7 @@ static int musb_gadget_enable(struct usb_ep *ep, goto fail; if (tmp > hw_ep->max_packet_sz_tx) { - DBG(4, "packet size beyond hardware FIFO size\n"); + dev_dbg(musb->controller, "packet size beyond hardware FIFO size\n"); goto fail; } @@ -1042,7 +1094,7 @@ static int musb_gadget_enable(struct usb_ep *ep, goto fail; if (tmp > hw_ep->max_packet_sz_rx) { - DBG(4, "packet size beyond hardware FIFO size\n"); + dev_dbg(musb->controller, "packet size beyond hardware FIFO size\n"); goto fail; } @@ -1155,7 +1207,7 @@ static int musb_gadget_disable(struct usb_ep *ep) spin_unlock_irqrestore(&(musb->lock), flags); - DBG(2, "%s\n", musb_ep->end_point.name); + dev_dbg(musb->controller, "%s\n", musb_ep->end_point.name); return status; } @@ -1167,11 +1219,12 @@ static int musb_gadget_disable(struct usb_ep *ep) struct usb_request *musb_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) { struct musb_ep *musb_ep = to_musb_ep(ep); + struct musb *musb = musb_ep->musb; struct musb_request *request = NULL; request = kzalloc(sizeof *request, gfp_flags); if (!request) { - DBG(4, "not enough memory\n"); + dev_dbg(musb->controller, "not enough memory\n"); return NULL; } @@ -1205,7 +1258,7 @@ struct free_record { */ void musb_ep_restart(struct musb *musb, struct musb_request *req) { - DBG(3, "<== %s request %p len %u on hw_ep%d\n", + dev_dbg(musb->controller, "<== %s request %p len %u on hw_ep%d\n", req->tx ? "TX/IN" : "RX/OUT", &req->request, req->request.length, req->epnum); @@ -1239,7 +1292,7 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req, if (request->ep != musb_ep) return -EINVAL; - DBG(4, "<== to %s request=%p\n", ep->name, req); + dev_dbg(musb->controller, "<== to %s request=%p\n", ep->name, req); /* request is mine now... */ request->request.actual = 0; @@ -1253,7 +1306,7 @@ static int musb_gadget_queue(struct usb_ep *ep, struct usb_request *req, /* don't queue if the ep is down */ if (!musb_ep->desc) { - DBG(4, "req %p queued to %s while ep %s\n", + dev_dbg(musb->controller, "req %p queued to %s while ep %s\n", req, ep->name, "disabled"); status = -ESHUTDOWN; goto cleanup; @@ -1290,7 +1343,7 @@ static int musb_gadget_dequeue(struct usb_ep *ep, struct usb_request *request) break; } if (r != req) { - DBG(3, "request %p not queued to %s\n", request, ep->name); + dev_dbg(musb->controller, "request %p not queued to %s\n", request, ep->name); status = -EINVAL; goto done; } @@ -1356,7 +1409,7 @@ static int musb_gadget_set_halt(struct usb_ep *ep, int value) request = next_request(musb_ep); if (value) { if (request) { - DBG(3, "request in progress, cannot halt %s\n", + dev_dbg(musb->controller, "request in progress, cannot halt %s\n", ep->name); status = -EAGAIN; goto done; @@ -1365,7 +1418,7 @@ static int musb_gadget_set_halt(struct usb_ep *ep, int value) if (musb_ep->is_in) { csr = musb_readw(epio, MUSB_TXCSR); if (csr & MUSB_TXCSR_FIFONOTEMPTY) { - DBG(3, "FIFO busy, cannot halt %s\n", ep->name); + dev_dbg(musb->controller, "FIFO busy, cannot halt %s\n", ep->name); status = -EAGAIN; goto done; } @@ -1374,7 +1427,7 @@ static int musb_gadget_set_halt(struct usb_ep *ep, int value) musb_ep->wedged = 0; /* set/clear the stall and toggle bits */ - DBG(2, "%s: %s stall\n", ep->name, value ? "set" : "clear"); + dev_dbg(musb->controller, "%s: %s stall\n", ep->name, value ? "set" : "clear"); if (musb_ep->is_in) { csr = musb_readw(epio, MUSB_TXCSR); csr |= MUSB_TXCSR_P_WZC_BITS @@ -1401,7 +1454,7 @@ static int musb_gadget_set_halt(struct usb_ep *ep, int value) /* maybe start the first request in the queue */ if (!musb_ep->busy && !value && request) { - DBG(3, "restarting the request\n"); + dev_dbg(musb->controller, "restarting the request\n"); musb_ep_restart(musb, request); } @@ -1532,7 +1585,7 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget) case OTG_STATE_B_IDLE: /* Start SRP ... OTG not required. */ devctl = musb_readb(mregs, MUSB_DEVCTL); - DBG(2, "Sending SRP: devctl: %02x\n", devctl); + dev_dbg(musb->controller, "Sending SRP: devctl: %02x\n", devctl); devctl |= MUSB_DEVCTL_SESSION; musb_writeb(mregs, MUSB_DEVCTL, devctl); devctl = musb_readb(mregs, MUSB_DEVCTL); @@ -1549,6 +1602,10 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget) break; } + spin_unlock_irqrestore(&musb->lock, flags); + otg_start_srp(musb->xceiv); + spin_lock_irqsave(&musb->lock, flags); + /* Block idling for at least 1s */ musb_platform_try_idle(musb, jiffies + msecs_to_jiffies(1 * HZ)); @@ -1556,7 +1613,8 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget) status = 0; goto done; default: - DBG(2, "Unhandled wake: %s\n", otg_state_string(musb)); + dev_dbg(musb->controller, "Unhandled wake: %s\n", + otg_state_string(musb->xceiv->state)); goto done; } @@ -1565,7 +1623,7 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget) power = musb_readb(mregs, MUSB_POWER); power |= MUSB_POWER_RESUME; musb_writeb(mregs, MUSB_POWER, power); - DBG(2, "issue wakeup\n"); + dev_dbg(musb->controller, "issue wakeup\n"); /* FIXME do this next chunk in a timer callback, no udelay */ mdelay(2); @@ -1599,7 +1657,7 @@ static void musb_pullup(struct musb *musb, int is_on) /* FIXME if on, HdrcStart; if off, HdrcStop */ - DBG(3, "gadget %s D+ pullup %s\n", + dev_dbg(musb->controller, "gadget %s D+ pullup %s\n", musb->gadget_driver->function, is_on ? "on" : "off"); musb_writeb(musb->mregs, MUSB_POWER, power); } @@ -1607,7 +1665,7 @@ static void musb_pullup(struct musb *musb, int is_on) #if 0 static int musb_gadget_vbus_session(struct usb_gadget *gadget, int is_active) { - DBG(2, "<= %s =>\n", __func__); + dev_dbg(musb->controller, "<= %s =>\n", __func__); /* * FIXME iff driver's softconnect flag is set (as it is during probe, @@ -1816,17 +1874,17 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, /* driver must be initialized to support peripheral mode */ if (!musb) { - DBG(1, "no dev??\n"); + dev_dbg(musb->controller, "no dev??\n"); retval = -ENODEV; goto err0; } pm_runtime_get_sync(musb->controller); - DBG(3, "registering driver %s\n", driver->function); + dev_dbg(musb->controller, "registering driver %s\n", driver->function); if (musb->gadget_driver) { - DBG(1, "%s is already bound to %s\n", + dev_dbg(musb->controller, "%s is already bound to %s\n", musb_driver_name, musb->gadget_driver->driver.name); retval = -EBUSY; @@ -1842,7 +1900,7 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, retval = bind(&musb->g); if (retval) { - DBG(3, "bind to driver %s failed --> %d\n", + dev_dbg(musb->controller, "bind to driver %s failed --> %d\n", driver->driver.name, retval); goto err1; } @@ -1870,7 +1928,7 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, if (is_otg_enabled(musb)) { struct usb_hcd *hcd = musb_to_hcd(musb); - DBG(3, "OTG startup...\n"); + dev_dbg(musb->controller, "OTG startup...\n"); /* REVISIT: funcall to other code, which also * handles power budgeting ... this way also @@ -1878,7 +1936,7 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver, */ retval = usb_add_hcd(musb_to_hcd(musb), -1, 0); if (retval < 0) { - DBG(1, "add_hcd failed, %d\n", retval); + dev_dbg(musb->controller, "add_hcd failed, %d\n", retval); goto err2; } @@ -1985,7 +2043,7 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) stop_activity(musb, driver); otg_set_peripheral(musb->xceiv, NULL); - DBG(3, "unregistering driver %s\n", driver->function); + dev_dbg(musb->controller, "unregistering driver %s\n", driver->function); spin_unlock_irqrestore(&musb->lock, flags); driver->unbind(&musb->g); @@ -2037,7 +2095,7 @@ void musb_g_resume(struct musb *musb) break; default: WARNING("unhandled RESUME transition (%s)\n", - otg_state_string(musb)); + otg_state_string(musb->xceiv->state)); } } @@ -2047,7 +2105,7 @@ void musb_g_suspend(struct musb *musb) u8 devctl; devctl = musb_readb(musb->mregs, MUSB_DEVCTL); - DBG(3, "devctl %02x\n", devctl); + dev_dbg(musb->controller, "devctl %02x\n", devctl); switch (musb->xceiv->state) { case OTG_STATE_B_IDLE: @@ -2067,7 +2125,7 @@ void musb_g_suspend(struct musb *musb) * A_PERIPHERAL may need care too */ WARNING("unhandled SUSPEND transition (%s)\n", - otg_state_string(musb)); + otg_state_string(musb->xceiv->state)); } } @@ -2083,7 +2141,7 @@ void musb_g_disconnect(struct musb *musb) void __iomem *mregs = musb->mregs; u8 devctl = musb_readb(mregs, MUSB_DEVCTL); - DBG(3, "devctl %02x\n", devctl); + dev_dbg(musb->controller, "devctl %02x\n", devctl); /* clear HR */ musb_writeb(mregs, MUSB_DEVCTL, devctl & MUSB_DEVCTL_SESSION); @@ -2101,8 +2159,8 @@ void musb_g_disconnect(struct musb *musb) switch (musb->xceiv->state) { default: #ifdef CONFIG_USB_MUSB_OTG - DBG(2, "Unhandled disconnect %s, setting a_idle\n", - otg_state_string(musb)); + dev_dbg(musb->controller, "Unhandled disconnect %s, setting a_idle\n", + otg_state_string(musb->xceiv->state)); musb->xceiv->state = OTG_STATE_A_IDLE; MUSB_HST_MODE(musb); break; @@ -2132,7 +2190,7 @@ __acquires(musb->lock) u8 devctl = musb_readb(mbase, MUSB_DEVCTL); u8 power; - DBG(3, "<== %s addr=%x driver '%s'\n", + dev_dbg(musb->controller, "<== %s addr=%x driver '%s'\n", (devctl & MUSB_DEVCTL_BDEVICE) ? "B-Device" : "A-Device", musb_readb(mbase, MUSB_FADDR), diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c index 75a542e..b2faff2 100644 --- a/drivers/usb/musb/musb_gadget_ep0.c +++ b/drivers/usb/musb/musb_gadget_ep0.c @@ -209,7 +209,7 @@ static inline void musb_try_b_hnp_enable(struct musb *musb) void __iomem *mbase = musb->mregs; u8 devctl; - DBG(1, "HNP: Setting HR\n"); + dev_dbg(musb->controller, "HNP: Setting HR\n"); devctl = musb_readb(mbase, MUSB_DEVCTL); musb_writeb(mbase, MUSB_DEVCTL, devctl | MUSB_DEVCTL_HR); } @@ -306,7 +306,7 @@ __acquires(musb->lock) /* Maybe start the first request in the queue */ request = next_request(musb_ep); if (!musb_ep->busy && request) { - DBG(3, "restarting the request\n"); + dev_dbg(musb->controller, "restarting the request\n"); musb_ep_restart(musb, request); } @@ -553,7 +553,7 @@ static void ep0_txstate(struct musb *musb) if (!req) { /* WARN_ON(1); */ - DBG(2, "odd; csr0 %04x\n", musb_readw(regs, MUSB_CSR0)); + dev_dbg(musb->controller, "odd; csr0 %04x\n", musb_readw(regs, MUSB_CSR0)); return; } @@ -610,7 +610,7 @@ musb_read_setup(struct musb *musb, struct usb_ctrlrequest *req) /* NOTE: earlier 2.6 versions changed setup packets to host * order, but now USB packets always stay in USB byte order. */ - DBG(3, "SETUP req%02x.%02x v%04x i%04x l%d\n", + dev_dbg(musb->controller, "SETUP req%02x.%02x v%04x i%04x l%d\n", req->bRequestType, req->bRequest, le16_to_cpu(req->wValue), @@ -678,7 +678,7 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb) csr = musb_readw(regs, MUSB_CSR0); len = musb_readb(regs, MUSB_COUNT0); - DBG(4, "csr %04x, count %d, myaddr %d, ep0stage %s\n", + dev_dbg(musb->controller, "csr %04x, count %d, myaddr %d, ep0stage %s\n", csr, len, musb_readb(mbase, MUSB_FADDR), decode_ep0stage(musb->ep0_state)); @@ -749,7 +749,7 @@ irqreturn_t musb_g_ep0_irq(struct musb *musb) /* enter test mode if needed (exit by reset) */ else if (musb->test_mode) { - DBG(1, "entering TESTMODE\n"); + dev_dbg(musb->controller, "entering TESTMODE\n"); if (MUSB_TEST_PACKET == musb->test_mode_nr) musb_load_testpacket(musb); @@ -861,7 +861,7 @@ setup: break; } - DBG(3, "handled %d, csr %04x, ep0stage %s\n", + dev_dbg(musb->controller, "handled %d, csr %04x, ep0stage %s\n", handled, csr, decode_ep0stage(musb->ep0_state)); @@ -878,7 +878,7 @@ setup: if (handled < 0) { musb_ep_select(mbase, 0); stall: - DBG(3, "stall (%d)\n", handled); + dev_dbg(musb->controller, "stall (%d)\n", handled); musb->ackpend |= MUSB_CSR0_P_SENDSTALL; musb->ep0_state = MUSB_EP0_STAGE_IDLE; finish: @@ -958,7 +958,7 @@ musb_g_ep0_queue(struct usb_ep *e, struct usb_request *r, gfp_t gfp_flags) status = 0; break; default: - DBG(1, "ep0 request queued in state %d\n", + dev_dbg(musb->controller, "ep0 request queued in state %d\n", musb->ep0_state); status = -EINVAL; goto cleanup; @@ -967,7 +967,7 @@ musb_g_ep0_queue(struct usb_ep *e, struct usb_request *r, gfp_t gfp_flags) /* add request to the list */ list_add_tail(&req->list, &ep->req_list); - DBG(3, "queue to %s (%s), length=%d\n", + dev_dbg(musb->controller, "queue to %s (%s), length=%d\n", ep->name, ep->is_in ? "IN/TX" : "OUT/RX", req->request.length); @@ -1060,7 +1060,7 @@ static int musb_g_ep0_halt(struct usb_ep *e, int value) musb->ackpend = 0; break; default: - DBG(1, "ep0 can't halt in state %d\n", musb->ep0_state); + dev_dbg(musb->controller, "ep0 can't halt in state %d\n", musb->ep0_state); status = -EINVAL; } diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 5eef4a8..7295e31 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -106,6 +106,7 @@ static void musb_ep_program(struct musb *musb, u8 epnum, */ static void musb_h_tx_flush_fifo(struct musb_hw_ep *ep) { + struct musb *musb = ep->musb; void __iomem *epio = ep->regs; u16 csr; u16 lastcsr = 0; @@ -114,7 +115,7 @@ static void musb_h_tx_flush_fifo(struct musb_hw_ep *ep) csr = musb_readw(epio, MUSB_TXCSR); while (csr & MUSB_TXCSR_FIFONOTEMPTY) { if (csr != lastcsr) - DBG(3, "Host TX FIFONOTEMPTY csr: %02x\n", csr); + dev_dbg(musb->controller, "Host TX FIFONOTEMPTY csr: %02x\n", csr); lastcsr = csr; csr |= MUSB_TXCSR_FLUSHFIFO; musb_writew(epio, MUSB_TXCSR, csr); @@ -240,7 +241,7 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh) len = urb->transfer_buffer_length - urb->actual_length; } - DBG(4, "qh %p urb %p dev%d ep%d%s%s, hw_ep %d, %p/%d\n", + dev_dbg(musb->controller, "qh %p urb %p dev%d ep%d%s%s, hw_ep %d, %p/%d\n", qh, urb, address, qh->epnum, is_in ? "in" : "out", ({char *s; switch (qh->type) { @@ -263,7 +264,7 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh) switch (qh->type) { case USB_ENDPOINT_XFER_ISOC: case USB_ENDPOINT_XFER_INT: - DBG(3, "check whether there's still time for periodic Tx\n"); + dev_dbg(musb->controller, "check whether there's still time for periodic Tx\n"); frame = musb_readw(mbase, MUSB_FRAME); /* FIXME this doesn't implement that scheduling policy ... * or handle framecounter wrapping @@ -278,7 +279,7 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh) } else { qh->frame = urb->start_frame; /* enable SOF interrupt so we can count down */ - DBG(1, "SOF for %d\n", epnum); + dev_dbg(musb->controller, "SOF for %d\n", epnum); #if 1 /* ifndef CONFIG_ARCH_DAVINCI */ musb_writeb(mbase, MUSB_INTRUSBE, 0xff); #endif @@ -286,7 +287,7 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh) break; default: start: - DBG(4, "Start TX%d %s\n", epnum, + dev_dbg(musb->controller, "Start TX%d %s\n", epnum, hw_ep->tx_channel ? "dma" : "pio"); if (!hw_ep->tx_channel) @@ -301,21 +302,7 @@ static void musb_giveback(struct musb *musb, struct urb *urb, int status) __releases(musb->lock) __acquires(musb->lock) { - DBG(({ int level; switch (status) { - case 0: - level = 4; - break; - /* common/boring faults */ - case -EREMOTEIO: - case -ESHUTDOWN: - case -ECONNRESET: - case -EPIPE: - level = 3; - break; - default: - level = 2; - break; - }; level; }), + dev_dbg(musb->controller, "complete %p %pF (%d), dev%d ep%d%s, %d/%d\n", urb, urb->complete, status, usb_pipedevice(urb->pipe), @@ -426,7 +413,7 @@ static void musb_advance_schedule(struct musb *musb, struct urb *urb, } if (qh != NULL && qh->is_ready) { - DBG(4, "... next ep%d %cX urb %p\n", + dev_dbg(musb->controller, "... next ep%d %cX urb %p\n", hw_ep->epnum, is_in ? 'R' : 'T', next_urb(qh)); musb_start_urb(musb, is_in, qh); } @@ -471,7 +458,7 @@ musb_host_packet_rx(struct musb *musb, struct urb *urb, u8 epnum, u8 iso_err) /* musb_ep_select(mbase, epnum); */ rx_count = musb_readw(epio, MUSB_RXCOUNT); - DBG(3, "RX%d count %d, buffer %p len %d/%d\n", epnum, rx_count, + dev_dbg(musb->controller, "RX%d count %d, buffer %p len %d/%d\n", epnum, rx_count, urb->transfer_buffer, qh->offset, urb->transfer_buffer_length); @@ -493,7 +480,7 @@ musb_host_packet_rx(struct musb *musb, struct urb *urb, u8 epnum, u8 iso_err) status = -EOVERFLOW; urb->error_count++; } - DBG(2, "** OVERFLOW %d into %d\n", rx_count, length); + dev_dbg(musb->controller, "** OVERFLOW %d into %d\n", rx_count, length); do_flush = 1; } else length = rx_count; @@ -511,7 +498,7 @@ musb_host_packet_rx(struct musb *musb, struct urb *urb, u8 epnum, u8 iso_err) if (rx_count > length) { if (urb->status == -EINPROGRESS) urb->status = -EOVERFLOW; - DBG(2, "** OVERFLOW %d into %d\n", rx_count, length); + dev_dbg(musb->controller, "** OVERFLOW %d into %d\n", rx_count, length); do_flush = 1; } else length = rx_count; @@ -697,7 +684,7 @@ static void musb_ep_program(struct musb *musb, u8 epnum, struct musb_qh *qh = musb_ep_get_qh(hw_ep, !is_out); u16 packet_sz = qh->maxpacket; - DBG(3, "%s hw%d urb %p spd%d dev%d ep%d%s " + dev_dbg(musb->controller, "%s hw%d urb %p spd%d dev%d ep%d%s " "h_addr%02x h_port%02x bytes %d\n", is_out ? "-->" : "<--", epnum, urb, urb->dev->speed, @@ -850,37 +837,32 @@ static void musb_ep_program(struct musb *musb, u8 epnum, /* kick things off */ if ((is_cppi_enabled() || tusb_dma_omap()) && dma_channel) { - /* candidate for DMA */ - if (dma_channel) { - dma_channel->actual_len = 0L; - qh->segsize = len; - - /* AUTOREQ is in a DMA register */ - musb_writew(hw_ep->regs, MUSB_RXCSR, csr); - csr = musb_readw(hw_ep->regs, - MUSB_RXCSR); - - /* unless caller treats short rx transfers as - * errors, we dare not queue multiple transfers. - */ - dma_ok = dma_controller->channel_program( - dma_channel, packet_sz, - !(urb->transfer_flags - & URB_SHORT_NOT_OK), - urb->transfer_dma + offset, - qh->segsize); - if (!dma_ok) { - dma_controller->channel_release( - dma_channel); - hw_ep->rx_channel = NULL; - dma_channel = NULL; - } else - csr |= MUSB_RXCSR_DMAENAB; - } + /* Candidate for DMA */ + dma_channel->actual_len = 0L; + qh->segsize = len; + + /* AUTOREQ is in a DMA register */ + musb_writew(hw_ep->regs, MUSB_RXCSR, csr); + csr = musb_readw(hw_ep->regs, MUSB_RXCSR); + + /* + * Unless caller treats short RX transfers as + * errors, we dare not queue multiple transfers. + */ + dma_ok = dma_controller->channel_program(dma_channel, + packet_sz, !(urb->transfer_flags & + URB_SHORT_NOT_OK), + urb->transfer_dma + offset, + qh->segsize); + if (!dma_ok) { + dma_controller->channel_release(dma_channel); + hw_ep->rx_channel = dma_channel = NULL; + } else + csr |= MUSB_RXCSR_DMAENAB; } csr |= MUSB_RXCSR_H_REQPKT; - DBG(7, "RXCSR%d := %04x\n", epnum, csr); + dev_dbg(musb->controller, "RXCSR%d := %04x\n", epnum, csr); musb_writew(hw_ep->regs, MUSB_RXCSR, csr); csr = musb_readw(hw_ep->regs, MUSB_RXCSR); } @@ -923,15 +905,15 @@ static bool musb_h_ep0_continue(struct musb *musb, u16 len, struct urb *urb) request = (struct usb_ctrlrequest *) urb->setup_packet; if (!request->wLength) { - DBG(4, "start no-DATA\n"); + dev_dbg(musb->controller, "start no-DATA\n"); break; } else if (request->bRequestType & USB_DIR_IN) { - DBG(4, "start IN-DATA\n"); + dev_dbg(musb->controller, "start IN-DATA\n"); musb->ep0_stage = MUSB_EP0_IN; more = true; break; } else { - DBG(4, "start OUT-DATA\n"); + dev_dbg(musb->controller, "start OUT-DATA\n"); musb->ep0_stage = MUSB_EP0_OUT; more = true; } @@ -943,7 +925,7 @@ static bool musb_h_ep0_continue(struct musb *musb, u16 len, struct urb *urb) if (fifo_count) { fifo_dest = (u8 *) (urb->transfer_buffer + urb->actual_length); - DBG(3, "Sending %d byte%s to ep0 fifo %p\n", + dev_dbg(musb->controller, "Sending %d byte%s to ep0 fifo %p\n", fifo_count, (fifo_count == 1) ? "" : "s", fifo_dest); @@ -988,7 +970,7 @@ irqreturn_t musb_h_ep0_irq(struct musb *musb) ? musb_readb(epio, MUSB_COUNT0) : 0; - DBG(4, "<== csr0 %04x, qh %p, count %d, urb %p, stage %d\n", + dev_dbg(musb->controller, "<== csr0 %04x, qh %p, count %d, urb %p, stage %d\n", csr, qh, len, urb, musb->ep0_stage); /* if we just did status stage, we are done */ @@ -999,15 +981,15 @@ irqreturn_t musb_h_ep0_irq(struct musb *musb) /* prepare status */ if (csr & MUSB_CSR0_H_RXSTALL) { - DBG(6, "STALLING ENDPOINT\n"); + dev_dbg(musb->controller, "STALLING ENDPOINT\n"); status = -EPIPE; } else if (csr & MUSB_CSR0_H_ERROR) { - DBG(2, "no response, csr0 %04x\n", csr); + dev_dbg(musb->controller, "no response, csr0 %04x\n", csr); status = -EPROTO; } else if (csr & MUSB_CSR0_H_NAKTIMEOUT) { - DBG(2, "control NAK timeout\n"); + dev_dbg(musb->controller, "control NAK timeout\n"); /* NOTE: this code path would be a good place to PAUSE a * control transfer, if another one is queued, so that @@ -1022,7 +1004,7 @@ irqreturn_t musb_h_ep0_irq(struct musb *musb) } if (status) { - DBG(6, "aborting\n"); + dev_dbg(musb->controller, "aborting\n"); retval = IRQ_HANDLED; if (urb) urb->status = status; @@ -1072,7 +1054,7 @@ irqreturn_t musb_h_ep0_irq(struct musb *musb) /* flag status stage */ musb->ep0_stage = MUSB_EP0_STATUS; - DBG(5, "ep0 STATUS, csr %04x\n", csr); + dev_dbg(musb->controller, "ep0 STATUS, csr %04x\n", csr); } musb_writew(epio, MUSB_CSR0, csr); @@ -1126,31 +1108,31 @@ void musb_host_tx(struct musb *musb, u8 epnum) /* with CPPI, DMA sometimes triggers "extra" irqs */ if (!urb) { - DBG(4, "extra TX%d ready, csr %04x\n", epnum, tx_csr); + dev_dbg(musb->controller, "extra TX%d ready, csr %04x\n", epnum, tx_csr); return; } pipe = urb->pipe; dma = is_dma_capable() ? hw_ep->tx_channel : NULL; - DBG(4, "OUT/TX%d end, csr %04x%s\n", epnum, tx_csr, + dev_dbg(musb->controller, "OUT/TX%d end, csr %04x%s\n", epnum, tx_csr, dma ? ", dma" : ""); /* check for errors */ if (tx_csr & MUSB_TXCSR_H_RXSTALL) { /* dma was disabled, fifo flushed */ - DBG(3, "TX end %d stall\n", epnum); + dev_dbg(musb->controller, "TX end %d stall\n", epnum); /* stall; record URB status */ status = -EPIPE; } else if (tx_csr & MUSB_TXCSR_H_ERROR) { /* (NON-ISO) dma was disabled, fifo flushed */ - DBG(3, "TX 3strikes on ep=%d\n", epnum); + dev_dbg(musb->controller, "TX 3strikes on ep=%d\n", epnum); status = -ETIMEDOUT; } else if (tx_csr & MUSB_TXCSR_H_NAKTIMEOUT) { - DBG(6, "TX end=%d device not responding\n", epnum); + dev_dbg(musb->controller, "TX end=%d device not responding\n", epnum); /* NOTE: this code path would be a good place to PAUSE a * transfer, if there's some other (nonperiodic) tx urb @@ -1195,7 +1177,7 @@ void musb_host_tx(struct musb *musb, u8 epnum) /* second cppi case */ if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { - DBG(4, "extra TX%d ready, csr %04x\n", epnum, tx_csr); + dev_dbg(musb->controller, "extra TX%d ready, csr %04x\n", epnum, tx_csr); return; } @@ -1254,7 +1236,7 @@ void musb_host_tx(struct musb *musb, u8 epnum) * FIFO mode too... */ if (tx_csr & (MUSB_TXCSR_FIFONOTEMPTY | MUSB_TXCSR_TXPKTRDY)) { - DBG(2, "DMA complete but packet still in FIFO, " + dev_dbg(musb->controller, "DMA complete but packet still in FIFO, " "CSR %04x\n", tx_csr); return; } @@ -1321,7 +1303,7 @@ void musb_host_tx(struct musb *musb, u8 epnum) return; } } else if (tx_csr & MUSB_TXCSR_DMAENAB) { - DBG(1, "not complete, but DMA enabled?\n"); + dev_dbg(musb->controller, "not complete, but DMA enabled?\n"); return; } @@ -1462,7 +1444,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) * usbtest #11 (unlinks) triggers it regularly, sometimes * with fifo full. (Only with DMA??) */ - DBG(3, "BOGUS RX%d ready, csr %04x, count %d\n", epnum, val, + dev_dbg(musb->controller, "BOGUS RX%d ready, csr %04x, count %d\n", epnum, val, musb_readw(epio, MUSB_RXCOUNT)); musb_h_flush_rxfifo(hw_ep, MUSB_RXCSR_CLRDATATOG); return; @@ -1470,20 +1452,20 @@ void musb_host_rx(struct musb *musb, u8 epnum) pipe = urb->pipe; - DBG(5, "<== hw %d rxcsr %04x, urb actual %d (+dma %zu)\n", + dev_dbg(musb->controller, "<== hw %d rxcsr %04x, urb actual %d (+dma %zu)\n", epnum, rx_csr, urb->actual_length, dma ? dma->actual_len : 0); /* check for errors, concurrent stall & unlink is not really * handled yet! */ if (rx_csr & MUSB_RXCSR_H_RXSTALL) { - DBG(3, "RX end %d STALL\n", epnum); + dev_dbg(musb->controller, "RX end %d STALL\n", epnum); /* stall; record URB status */ status = -EPIPE; } else if (rx_csr & MUSB_RXCSR_H_ERROR) { - DBG(3, "end %d RX proto error\n", epnum); + dev_dbg(musb->controller, "end %d RX proto error\n", epnum); status = -EPROTO; musb_writeb(epio, MUSB_RXINTERVAL, 0); @@ -1491,7 +1473,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) } else if (rx_csr & MUSB_RXCSR_DATAERROR) { if (USB_ENDPOINT_XFER_ISOC != qh->type) { - DBG(6, "RX end %d NAK timeout\n", epnum); + dev_dbg(musb->controller, "RX end %d NAK timeout\n", epnum); /* NOTE: NAKing is *NOT* an error, so we want to * continue. Except ... if there's a request for @@ -1514,12 +1496,12 @@ void musb_host_rx(struct musb *musb, u8 epnum) goto finish; } else { - DBG(4, "RX end %d ISO data error\n", epnum); + dev_dbg(musb->controller, "RX end %d ISO data error\n", epnum); /* packet error reported later */ iso_err = true; } } else if (rx_csr & MUSB_RXCSR_INCOMPRX) { - DBG(3, "end %d high bandwidth incomplete ISO packet RX\n", + dev_dbg(musb->controller, "end %d high bandwidth incomplete ISO packet RX\n", epnum); status = -EPROTO; } @@ -1565,7 +1547,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) done = true; } - DBG(2, "RXCSR%d %04x, reqpkt, len %zu%s\n", epnum, rx_csr, + dev_dbg(musb->controller, "RXCSR%d %04x, reqpkt, len %zu%s\n", epnum, rx_csr, xfer_len, dma ? ", dma" : ""); rx_csr &= ~MUSB_RXCSR_H_REQPKT; @@ -1615,7 +1597,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) MUSB_RXCSR_H_WZC_BITS | val); } - DBG(4, "ep %d dma %s, rxcsr %04x, rxcount %d\n", epnum, + dev_dbg(musb->controller, "ep %d dma %s, rxcsr %04x, rxcount %d\n", epnum, done ? "off" : "reset", musb_readw(epio, MUSB_RXCSR), musb_readw(epio, MUSB_RXCOUNT)); @@ -1648,7 +1630,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) rx_count = musb_readw(epio, MUSB_RXCOUNT); - DBG(2, "RX%d count %d, buffer 0x%x len %d/%d\n", + dev_dbg(musb->controller, "RX%d count %d, buffer 0x%x len %d/%d\n", epnum, rx_count, urb->transfer_dma + urb->actual_length, @@ -1672,7 +1654,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) d_status = -EOVERFLOW; urb->error_count++; } - DBG(2, "** OVERFLOW %d into %d\n",\ + dev_dbg(musb->controller, "** OVERFLOW %d into %d\n",\ rx_count, d->length); length = d->length; @@ -1760,7 +1742,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) usb_hcd_unmap_urb_for_dma(musb_to_hcd(musb), urb); done = musb_host_packet_rx(musb, urb, epnum, iso_err); - DBG(6, "read %spacket\n", done ? "last " : ""); + dev_dbg(musb->controller, "read %spacket\n", done ? "last " : ""); } } @@ -1881,7 +1863,7 @@ static int musb_schedule( idle = 1; qh->mux = 0; hw_ep = musb->endpoints + best_end; - DBG(4, "qh %p periodic slot %d\n", qh, best_end); + dev_dbg(musb->controller, "qh %p periodic slot %d\n", qh, best_end); success: if (head) { idle = list_empty(head); @@ -2087,6 +2069,7 @@ done: static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh) { struct musb_hw_ep *ep = qh->hw_ep; + struct musb *musb = ep->musb; void __iomem *epio = ep->regs; unsigned hw_end = ep->epnum; void __iomem *regs = ep->musb->mregs; @@ -2102,7 +2085,7 @@ static int musb_cleanup_urb(struct urb *urb, struct musb_qh *qh) dma = is_in ? ep->rx_channel : ep->tx_channel; if (dma) { status = ep->musb->dma_controller->channel_abort(dma); - DBG(status ? 1 : 3, + dev_dbg(musb->controller, "abort %cX%d DMA for urb %p --> %d\n", is_in ? 'R' : 'T', ep->epnum, urb, status); @@ -2149,7 +2132,7 @@ static int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) int is_in = usb_pipein(urb->pipe); int ret; - DBG(4, "urb=%p, dev%d ep%d%s\n", urb, + dev_dbg(musb->controller, "urb=%p, dev%d ep%d%s\n", urb, usb_pipedevice(urb->pipe), usb_pipeendpoint(urb->pipe), is_in ? "in" : "out"); @@ -2304,7 +2287,7 @@ static int musb_bus_suspend(struct usb_hcd *hcd) if (musb->is_active) { WARNING("trying to suspend as %s while active\n", - otg_state_string(musb)); + otg_state_string(musb->xceiv->state)); return -EBUSY; } else return 0; diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index 489104a..2d80a57 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -74,7 +74,7 @@ static void musb_port_suspend(struct musb *musb, bool do_suspend) break; } - DBG(3, "Root port suspended, power %02x\n", power); + dev_dbg(musb->controller, "Root port suspended, power %02x\n", power); musb->port1_status |= USB_PORT_STAT_SUSPEND; switch (musb->xceiv->state) { @@ -97,15 +97,15 @@ static void musb_port_suspend(struct musb *musb, bool do_suspend) break; #endif default: - DBG(1, "bogus rh suspend? %s\n", - otg_state_string(musb)); + dev_dbg(musb->controller, "bogus rh suspend? %s\n", + otg_state_string(musb->xceiv->state)); } } else if (power & MUSB_POWER_SUSPENDM) { power &= ~MUSB_POWER_SUSPENDM; power |= MUSB_POWER_RESUME; musb_writeb(mbase, MUSB_POWER, power); - DBG(3, "Root port resuming, power %02x\n", power); + dev_dbg(musb->controller, "Root port resuming, power %02x\n", power); /* later, GetPortStatus will stop RESUME signaling */ musb->port1_status |= MUSB_PORT_STAT_RESUME; @@ -120,7 +120,7 @@ static void musb_port_reset(struct musb *musb, bool do_reset) #ifdef CONFIG_USB_MUSB_OTG if (musb->xceiv->state == OTG_STATE_B_IDLE) { - DBG(2, "HNP: Returning from HNP; no hub reset from b_idle\n"); + dev_dbg(musb->controller, "HNP: Returning from HNP; no hub reset from b_idle\n"); musb->port1_status &= ~USB_PORT_STAT_RESET; return; } @@ -159,7 +159,7 @@ static void musb_port_reset(struct musb *musb, bool do_reset) musb->port1_status &= ~USB_PORT_STAT_ENABLE; musb->rh_timer = jiffies + msecs_to_jiffies(50); } else { - DBG(4, "root port reset stopped\n"); + dev_dbg(musb->controller, "root port reset stopped\n"); musb_writeb(mbase, MUSB_POWER, power & ~MUSB_POWER_RESET); @@ -167,7 +167,7 @@ static void musb_port_reset(struct musb *musb, bool do_reset) power = musb_readb(mbase, MUSB_POWER); if (power & MUSB_POWER_HSMODE) { - DBG(4, "high-speed device connected\n"); + dev_dbg(musb->controller, "high-speed device connected\n"); musb->port1_status |= USB_PORT_STAT_HIGH_SPEED; } @@ -208,7 +208,8 @@ void musb_root_disconnect(struct musb *musb) musb->xceiv->state = OTG_STATE_B_IDLE; break; default: - DBG(1, "host disconnect (%s)\n", otg_state_string(musb)); + dev_dbg(musb->controller, "host disconnect (%s)\n", + otg_state_string(musb->xceiv->state)); } } @@ -287,7 +288,7 @@ int musb_hub_control( default: goto error; } - DBG(5, "clear feature %d\n", wValue); + dev_dbg(musb->controller, "clear feature %d\n", wValue); musb->port1_status &= ~(1 << wValue); break; case GetHubDescriptor: @@ -329,7 +330,7 @@ int musb_hub_control( power = musb_readb(musb->mregs, MUSB_POWER); power &= ~MUSB_POWER_RESUME; - DBG(4, "root port resume stopped, power %02x\n", + dev_dbg(musb->controller, "root port resume stopped, power %02x\n", power); musb_writeb(musb->mregs, MUSB_POWER, power); @@ -352,7 +353,7 @@ int musb_hub_control( (__le32 *) buf); /* port change status is more interesting */ - DBG(get_unaligned((u16 *)(buf+2)) ? 2 : 5, "port status %08x\n", + dev_dbg(musb->controller, "port status %08x\n", musb->port1_status); break; case SetPortFeature: @@ -423,7 +424,7 @@ int musb_hub_control( default: goto error; } - DBG(5, "set feature %d\n", wValue); + dev_dbg(musb->controller, "set feature %d\n", wValue); musb->port1_status |= 1 << wValue; break; diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c index d281792..f70c5a5 100644 --- a/drivers/usb/musb/musbhsdma.c +++ b/drivers/usb/musb/musbhsdma.c @@ -122,11 +122,12 @@ static void configure_channel(struct dma_channel *channel, { struct musb_dma_channel *musb_channel = channel->private_data; struct musb_dma_controller *controller = musb_channel->controller; + struct musb *musb = controller->private_data; void __iomem *mbase = controller->base; u8 bchannel = musb_channel->idx; u16 csr = 0; - DBG(4, "%p, pkt_sz %d, addr 0x%x, len %d, mode %d\n", + dev_dbg(musb->controller, "%p, pkt_sz %d, addr 0x%x, len %d, mode %d\n", channel, packet_sz, dma_addr, len, mode); if (mode) { @@ -161,7 +162,7 @@ static int dma_channel_program(struct dma_channel *channel, struct musb_dma_controller *controller = musb_channel->controller; struct musb *musb = controller->private_data; - DBG(2, "ep%d-%s pkt_sz %d, dma_addr 0x%x length %d, mode %d\n", + dev_dbg(musb->controller, "ep%d-%s pkt_sz %d, dma_addr 0x%x length %d, mode %d\n", musb_channel->epnum, musb_channel->transmit ? "Tx" : "Rx", packet_sz, dma_addr, len, mode); @@ -274,7 +275,7 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data) #endif if (!int_hsdma) { - DBG(2, "spurious DMA irq\n"); + dev_dbg(musb->controller, "spurious DMA irq\n"); for (bchannel = 0; bchannel < MUSB_HSDMA_CHANNELS; bchannel++) { musb_channel = (struct musb_dma_channel *) @@ -288,7 +289,7 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data) } } - DBG(2, "int_hsdma = 0x%x\n", int_hsdma); + dev_dbg(musb->controller, "int_hsdma = 0x%x\n", int_hsdma); if (!int_hsdma) goto done; @@ -315,7 +316,7 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data) channel->actual_len = addr - musb_channel->start_addr; - DBG(2, "ch %p, 0x%x -> 0x%x (%zu / %d) %s\n", + dev_dbg(musb->controller, "ch %p, 0x%x -> 0x%x (%zu / %d) %s\n", channel, musb_channel->start_addr, addr, channel->actual_len, musb_channel->len, diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index e9e60b6..c5d4c44 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -76,7 +76,7 @@ static void musb_do_idle(unsigned long _musb) if (musb->port1_status & MUSB_PORT_STAT_RESUME) { power = musb_readb(musb->mregs, MUSB_POWER); power &= ~MUSB_POWER_RESUME; - DBG(1, "root port resume stopped, power %02x\n", power); + dev_dbg(musb->controller, "root port resume stopped, power %02x\n", power); musb_writeb(musb->mregs, MUSB_POWER, power); musb->is_active = 1; musb->port1_status &= ~(USB_PORT_STAT_SUSPEND @@ -114,7 +114,8 @@ static void omap2430_musb_try_idle(struct musb *musb, unsigned long timeout) /* Never idle if active, or when VBUS timeout is not set as host */ if (musb->is_active || ((musb->a_wait_bcon == 0) && (musb->xceiv->state == OTG_STATE_A_WAIT_BCON))) { - DBG(4, "%s active, deleting timer\n", otg_state_string(musb)); + dev_dbg(musb->controller, "%s active, deleting timer\n", + otg_state_string(musb->xceiv->state)); del_timer(&musb_idle_timer); last_timer = jiffies; return; @@ -124,14 +125,14 @@ static void omap2430_musb_try_idle(struct musb *musb, unsigned long timeout) if (!timer_pending(&musb_idle_timer)) last_timer = timeout; else { - DBG(4, "Longer idle timer already pending, ignoring\n"); + dev_dbg(musb->controller, "Longer idle timer already pending, ignoring\n"); return; } } last_timer = timeout; - DBG(4, "%s inactive, for idle timer for %lu ms\n", - otg_state_string(musb), + dev_dbg(musb->controller, "%s inactive, for idle timer for %lu ms\n", + otg_state_string(musb->xceiv->state), (unsigned long)jiffies_to_msecs(timeout - jiffies)); mod_timer(&musb_idle_timer, timeout); } @@ -193,9 +194,9 @@ static void omap2430_musb_set_vbus(struct musb *musb, int is_on) } musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); - DBG(1, "VBUS %s, devctl %02x " + dev_dbg(musb->controller, "VBUS %s, devctl %02x " /* otg %3x conf %08x prcm %08x */ "\n", - otg_state_string(musb), + otg_state_string(musb->xceiv->state), musb_readb(musb->mregs, MUSB_DEVCTL)); } @@ -239,7 +240,7 @@ static int musb_otg_notifications(struct notifier_block *nb, switch (event) { case USB_EVENT_ID: - DBG(4, "ID GND\n"); + dev_dbg(musb->controller, "ID GND\n"); if (is_otg_enabled(musb)) { #ifdef CONFIG_USB_GADGET_MUSB_HDRC @@ -257,7 +258,7 @@ static int musb_otg_notifications(struct notifier_block *nb, break; case USB_EVENT_VBUS: - DBG(4, "VBUS Connect\n"); + dev_dbg(musb->controller, "VBUS Connect\n"); #ifdef CONFIG_USB_GADGET_MUSB_HDRC if (musb->gadget_driver) @@ -267,7 +268,7 @@ static int musb_otg_notifications(struct notifier_block *nb, break; case USB_EVENT_NONE: - DBG(4, "VBUS Disconnect\n"); + dev_dbg(musb->controller, "VBUS Disconnect\n"); #ifdef CONFIG_USB_GADGET_MUSB_HDRC if (is_otg_enabled(musb) || is_peripheral_enabled(musb)) @@ -285,7 +286,7 @@ static int musb_otg_notifications(struct notifier_block *nb, otg_shutdown(musb->xceiv); break; default: - DBG(4, "ID float\n"); + dev_dbg(musb->controller, "ID float\n"); return NOTIFY_DONE; } @@ -339,7 +340,7 @@ static int omap2430_musb_init(struct musb *musb) status = otg_register_notifier(musb->xceiv, &musb->nb); if (status) - DBG(1, "notification register failed\n"); + dev_dbg(musb->controller, "notification register failed\n"); setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb); diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index c47aac4..b410357 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -106,7 +106,7 @@ static void tusb_wbus_quirk(struct musb *musb, int enabled) tmp = phy_otg_ena & ~WBUS_QUIRK_MASK; tmp |= TUSB_PHY_OTG_CTRL_WRPROTECT | TUSB_PHY_OTG_CTRL_TESTM2; musb_writel(tbase, TUSB_PHY_OTG_CTRL_ENABLE, tmp); - DBG(2, "Enabled tusb wbus quirk ctrl %08x ena %08x\n", + dev_dbg(musb->controller, "Enabled tusb wbus quirk ctrl %08x ena %08x\n", musb_readl(tbase, TUSB_PHY_OTG_CTRL), musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE)); } else if (musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE) @@ -115,7 +115,7 @@ static void tusb_wbus_quirk(struct musb *musb, int enabled) musb_writel(tbase, TUSB_PHY_OTG_CTRL, tmp); tmp = TUSB_PHY_OTG_CTRL_WRPROTECT | phy_otg_ena; musb_writel(tbase, TUSB_PHY_OTG_CTRL_ENABLE, tmp); - DBG(2, "Disabled tusb wbus quirk ctrl %08x ena %08x\n", + dev_dbg(musb->controller, "Disabled tusb wbus quirk ctrl %08x ena %08x\n", musb_readl(tbase, TUSB_PHY_OTG_CTRL), musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE)); phy_otg_ctrl = 0; @@ -172,13 +172,14 @@ static inline void tusb_fifo_read_unaligned(void __iomem *fifo, void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf) { + struct musb *musb = hw_ep->musb; void __iomem *ep_conf = hw_ep->conf; void __iomem *fifo = hw_ep->fifo; u8 epnum = hw_ep->epnum; prefetch(buf); - DBG(4, "%cX ep%d fifo %p count %d buf %p\n", + dev_dbg(musb->controller, "%cX ep%d fifo %p count %d buf %p\n", 'T', epnum, fifo, len, buf); if (epnum) @@ -221,11 +222,12 @@ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf) void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *buf) { + struct musb *musb = hw_ep->musb; void __iomem *ep_conf = hw_ep->conf; void __iomem *fifo = hw_ep->fifo; u8 epnum = hw_ep->epnum; - DBG(4, "%cX ep%d fifo %p count %d buf %p\n", + dev_dbg(musb->controller, "%cX ep%d fifo %p count %d buf %p\n", 'R', epnum, fifo, len, buf); if (epnum) @@ -304,7 +306,7 @@ static int tusb_draw_power(struct otg_transceiver *x, unsigned mA) } musb_writel(tbase, TUSB_PRCM_MNGMT, reg); - DBG(2, "draw max %d mA VBUS\n", mA); + dev_dbg(musb->controller, "draw max %d mA VBUS\n", mA); return 0; } @@ -374,7 +376,7 @@ static void tusb_allow_idle(struct musb *musb, u32 wakeup_enables) reg |= TUSB_PRCM_MNGMT_PM_IDLE | TUSB_PRCM_MNGMT_DEV_IDLE; musb_writel(tbase, TUSB_PRCM_MNGMT, reg); - DBG(6, "idle, wake on %02x\n", wakeup_enables); + dev_dbg(musb->controller, "idle, wake on %02x\n", wakeup_enables); } /* @@ -421,8 +423,8 @@ static void musb_do_idle(unsigned long _musb) if ((musb->a_wait_bcon != 0) && (musb->idle_timeout == 0 || time_after(jiffies, musb->idle_timeout))) { - DBG(4, "Nothing connected %s, turning off VBUS\n", - otg_state_string(musb)); + dev_dbg(musb->controller, "Nothing connected %s, turning off VBUS\n", + otg_state_string(musb->xceiv->state)); } /* FALLTHROUGH */ case OTG_STATE_A_IDLE: @@ -481,7 +483,8 @@ static void tusb_musb_try_idle(struct musb *musb, unsigned long timeout) /* Never idle if active, or when VBUS timeout is not set as host */ if (musb->is_active || ((musb->a_wait_bcon == 0) && (musb->xceiv->state == OTG_STATE_A_WAIT_BCON))) { - DBG(4, "%s active, deleting timer\n", otg_state_string(musb)); + dev_dbg(musb->controller, "%s active, deleting timer\n", + otg_state_string(musb->xceiv->state)); del_timer(&musb_idle_timer); last_timer = jiffies; return; @@ -491,14 +494,14 @@ static void tusb_musb_try_idle(struct musb *musb, unsigned long timeout) if (!timer_pending(&musb_idle_timer)) last_timer = timeout; else { - DBG(4, "Longer idle timer already pending, ignoring\n"); + dev_dbg(musb->controller, "Longer idle timer already pending, ignoring\n"); return; } } last_timer = timeout; - DBG(4, "%s inactive, for idle timer for %lu ms\n", - otg_state_string(musb), + dev_dbg(musb->controller, "%s inactive, for idle timer for %lu ms\n", + otg_state_string(musb->xceiv->state), (unsigned long)jiffies_to_msecs(timeout - jiffies)); mod_timer(&musb_idle_timer, timeout); } @@ -572,8 +575,8 @@ static void tusb_musb_set_vbus(struct musb *musb, int is_on) musb_writel(tbase, TUSB_DEV_CONF, conf); musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); - DBG(1, "VBUS %s, devctl %02x otg %3x conf %08x prcm %08x\n", - otg_state_string(musb), + dev_dbg(musb->controller, "VBUS %s, devctl %02x otg %3x conf %08x prcm %08x\n", + otg_state_string(musb->xceiv->state), musb_readb(musb->mregs, MUSB_DEVCTL), musb_readl(tbase, TUSB_DEV_OTG_STAT), conf, prcm); @@ -633,7 +636,7 @@ static int tusb_musb_set_mode(struct musb *musb, u8 musb_mode) #endif default: - DBG(2, "Trying to set mode %i\n", musb_mode); + dev_dbg(musb->controller, "Trying to set mode %i\n", musb_mode); return -EINVAL; } @@ -666,7 +669,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) default_a = !(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS); else default_a = is_host_enabled(musb); - DBG(2, "Default-%c\n", default_a ? 'A' : 'B'); + dev_dbg(musb->controller, "Default-%c\n", default_a ? 'A' : 'B'); musb->xceiv->default_a = default_a; tusb_musb_set_vbus(musb, default_a); @@ -693,7 +696,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) #endif if (otg_stat & TUSB_DEV_OTG_STAT_SESS_END) { - DBG(1, "Forcing disconnect (no interrupt)\n"); + dev_dbg(musb->controller, "Forcing disconnect (no interrupt)\n"); if (musb->xceiv->state != OTG_STATE_B_IDLE) { /* INTR_DISCONNECT can hide... */ musb->xceiv->state = OTG_STATE_B_IDLE; @@ -701,18 +704,18 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) } musb->is_active = 0; } - DBG(2, "vbus change, %s, otg %03x\n", - otg_state_string(musb), otg_stat); + dev_dbg(musb->controller, "vbus change, %s, otg %03x\n", + otg_state_string(musb->xceiv->state), otg_stat); idle_timeout = jiffies + (1 * HZ); schedule_work(&musb->irq_work); } else /* A-dev state machine */ { - DBG(2, "vbus change, %s, otg %03x\n", - otg_state_string(musb), otg_stat); + dev_dbg(musb->controller, "vbus change, %s, otg %03x\n", + otg_state_string(musb->xceiv->state), otg_stat); switch (musb->xceiv->state) { case OTG_STATE_A_IDLE: - DBG(2, "Got SRP, turning on VBUS\n"); + dev_dbg(musb->controller, "Got SRP, turning on VBUS\n"); musb_platform_set_vbus(musb, 1); /* CONNECT can wake if a_wait_bcon is set */ @@ -756,7 +759,8 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) if (int_src & TUSB_INT_SRC_OTG_TIMEOUT) { u8 devctl; - DBG(4, "%s timer, %03x\n", otg_state_string(musb), otg_stat); + dev_dbg(musb->controller, "%s timer, %03x\n", + otg_state_string(musb->xceiv->state), otg_stat); switch (musb->xceiv->state) { case OTG_STATE_A_WAIT_VRISE: @@ -767,7 +771,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase) if (otg_stat & TUSB_DEV_OTG_STAT_VBUS_VALID) { if ((devctl & MUSB_DEVCTL_VBUS) != MUSB_DEVCTL_VBUS) { - DBG(2, "devctl %02x\n", devctl); + dev_dbg(musb->controller, "devctl %02x\n", devctl); break; } musb->xceiv->state = OTG_STATE_A_WAIT_BCON; @@ -812,7 +816,7 @@ static irqreturn_t tusb_musb_interrupt(int irq, void *__hci) musb_writel(tbase, TUSB_INT_MASK, ~TUSB_INT_MASK_RESERVED_BITS); int_src = musb_readl(tbase, TUSB_INT_SRC) & ~TUSB_INT_SRC_RESERVED_BITS; - DBG(3, "TUSB IRQ %08x\n", int_src); + dev_dbg(musb->controller, "TUSB IRQ %08x\n", int_src); musb->int_usb = (u8) int_src; @@ -833,7 +837,7 @@ static irqreturn_t tusb_musb_interrupt(int irq, void *__hci) reg = musb_readl(tbase, TUSB_SCRATCH_PAD); if (reg == i) break; - DBG(6, "TUSB NOR not ready\n"); + dev_dbg(musb->controller, "TUSB NOR not ready\n"); } /* work around issue 13 (2nd half) */ @@ -845,7 +849,7 @@ static irqreturn_t tusb_musb_interrupt(int irq, void *__hci) musb->is_active = 1; schedule_work(&musb->irq_work); } - DBG(3, "wake %sactive %02x\n", + dev_dbg(musb->controller, "wake %sactive %02x\n", musb->is_active ? "" : "in", reg); /* REVISIT host side TUSB_PRCM_WHOSTDISCON, TUSB_PRCM_WBUS */ @@ -867,7 +871,7 @@ static irqreturn_t tusb_musb_interrupt(int irq, void *__hci) u32 dma_src = musb_readl(tbase, TUSB_DMA_INT_SRC); u32 real_dma_src = musb_readl(tbase, TUSB_DMA_INT_MASK); - DBG(3, "DMA IRQ %08x\n", dma_src); + dev_dbg(musb->controller, "DMA IRQ %08x\n", dma_src); real_dma_src = ~real_dma_src & dma_src; if (tusb_dma_omap() && real_dma_src) { int tx_source = (real_dma_src & 0xffff); @@ -875,7 +879,7 @@ static irqreturn_t tusb_musb_interrupt(int irq, void *__hci) for (i = 1; i <= 15; i++) { if (tx_source & (1 << i)) { - DBG(3, "completing ep%i %s\n", i, "tx"); + dev_dbg(musb->controller, "completing ep%i %s\n", i, "tx"); musb_dma_completion(musb, i, 1); } } diff --git a/drivers/usb/musb/tusb6010_omap.c b/drivers/usb/musb/tusb6010_omap.c index 99cb541..c784e6c 100644 --- a/drivers/usb/musb/tusb6010_omap.c +++ b/drivers/usb/musb/tusb6010_omap.c @@ -65,7 +65,7 @@ static int tusb_omap_dma_start(struct dma_controller *c) tusb_dma = container_of(c, struct tusb_omap_dma, controller); - /* DBG(3, "ep%i ch: %i\n", chdat->epnum, chdat->ch); */ + /* dev_dbg(musb->controller, "ep%i ch: %i\n", chdat->epnum, chdat->ch); */ return 0; } @@ -76,7 +76,7 @@ static int tusb_omap_dma_stop(struct dma_controller *c) tusb_dma = container_of(c, struct tusb_omap_dma, controller); - /* DBG(3, "ep%i ch: %i\n", chdat->epnum, chdat->ch); */ + /* dev_dbg(musb->controller, "ep%i ch: %i\n", chdat->epnum, chdat->ch); */ return 0; } @@ -89,7 +89,7 @@ static inline int tusb_omap_use_shared_dmareq(struct tusb_omap_dma_ch *chdat) u32 reg = musb_readl(chdat->tbase, TUSB_DMA_EP_MAP); if (reg != 0) { - DBG(3, "ep%i dmareq0 is busy for ep%i\n", + dev_dbg(musb->controller, "ep%i dmareq0 is busy for ep%i\n", chdat->epnum, reg & 0xf); return -EAGAIN; } @@ -143,7 +143,7 @@ static void tusb_omap_dma_cb(int lch, u16 ch_status, void *data) if (ch_status != OMAP_DMA_BLOCK_IRQ) printk(KERN_ERR "TUSB DMA error status: %i\n", ch_status); - DBG(3, "ep%i %s dma callback ch: %i status: %x\n", + dev_dbg(musb->controller, "ep%i %s dma callback ch: %i status: %x\n", chdat->epnum, chdat->tx ? "tx" : "rx", ch, ch_status); @@ -156,7 +156,7 @@ static void tusb_omap_dma_cb(int lch, u16 ch_status, void *data) /* HW issue #10: XFR_SIZE may get corrupt on DMA (both async & sync) */ if (unlikely(remaining > chdat->transfer_len)) { - DBG(2, "Corrupt %s dma ch%i XFR_SIZE: 0x%08lx\n", + dev_dbg(musb->controller, "Corrupt %s dma ch%i XFR_SIZE: 0x%08lx\n", chdat->tx ? "tx" : "rx", chdat->ch, remaining); remaining = 0; @@ -165,13 +165,13 @@ static void tusb_omap_dma_cb(int lch, u16 ch_status, void *data) channel->actual_len = chdat->transfer_len - remaining; pio = chdat->len - channel->actual_len; - DBG(3, "DMA remaining %lu/%u\n", remaining, chdat->transfer_len); + dev_dbg(musb->controller, "DMA remaining %lu/%u\n", remaining, chdat->transfer_len); /* Transfer remaining 1 - 31 bytes */ if (pio > 0 && pio < 32) { u8 *buf; - DBG(3, "Using PIO for remaining %lu bytes\n", pio); + dev_dbg(musb->controller, "Using PIO for remaining %lu bytes\n", pio); buf = phys_to_virt((u32)chdat->dma_addr) + chdat->transfer_len; if (chdat->tx) { dma_unmap_single(dev, chdat->dma_addr, @@ -209,7 +209,7 @@ static void tusb_omap_dma_cb(int lch, u16 ch_status, void *data) u16 csr; if (chdat->tx) { - DBG(3, "terminating short tx packet\n"); + dev_dbg(musb->controller, "terminating short tx packet\n"); musb_ep_select(mbase, chdat->epnum); csr = musb_readw(hw_ep->regs, MUSB_TXCSR); csr |= MUSB_TXCSR_MODE | MUSB_TXCSR_TXPKTRDY @@ -264,7 +264,7 @@ static int tusb_omap_dma_program(struct dma_channel *channel, u16 packet_sz, dma_remaining = TUSB_EP_CONFIG_XFR_SIZE(dma_remaining); if (dma_remaining) { - DBG(2, "Busy %s dma ch%i, not using: %08x\n", + dev_dbg(musb->controller, "Busy %s dma ch%i, not using: %08x\n", chdat->tx ? "tx" : "rx", chdat->ch, dma_remaining); return false; @@ -283,7 +283,7 @@ static int tusb_omap_dma_program(struct dma_channel *channel, u16 packet_sz, sync_dev = chdat->sync_dev; } else { if (tusb_omap_use_shared_dmareq(chdat) != 0) { - DBG(3, "could not get dma for ep%i\n", chdat->epnum); + dev_dbg(musb->controller, "could not get dma for ep%i\n", chdat->epnum); return false; } if (tusb_dma->ch < 0) { @@ -326,7 +326,7 @@ static int tusb_omap_dma_program(struct dma_channel *channel, u16 packet_sz, dma_params.frame_count = chdat->transfer_len / 32; /* Burst sz frame */ - DBG(3, "ep%i %s dma ch%i dma: %08x len: %u(%u) packet_sz: %i(%i)\n", + dev_dbg(musb->controller, "ep%i %s dma ch%i dma: %08x len: %u(%u) packet_sz: %i(%i)\n", chdat->epnum, chdat->tx ? "tx" : "rx", ch, dma_addr, chdat->transfer_len, len, chdat->transfer_packet_sz, packet_sz); @@ -370,7 +370,7 @@ static int tusb_omap_dma_program(struct dma_channel *channel, u16 packet_sz, dst_burst = OMAP_DMA_DATA_BURST_16; /* 16x32 write */ } - DBG(3, "ep%i %s using %i-bit %s dma from 0x%08lx to 0x%08lx\n", + dev_dbg(musb->controller, "ep%i %s using %i-bit %s dma from 0x%08lx to 0x%08lx\n", chdat->epnum, chdat->tx ? "tx" : "rx", (dma_params.data_type == OMAP_DMA_DATA_TYPE_S32) ? 32 : 16, ((dma_addr & 0x3) == 0) ? "sync" : "async", @@ -525,7 +525,7 @@ tusb_omap_dma_allocate(struct dma_controller *c, /* REVISIT: Why does dmareq5 not work? */ if (hw_ep->epnum == 0) { - DBG(3, "Not allowing DMA for ep0 %s\n", tx ? "tx" : "rx"); + dev_dbg(musb->controller, "Not allowing DMA for ep0 %s\n", tx ? "tx" : "rx"); return NULL; } @@ -585,7 +585,7 @@ tusb_omap_dma_allocate(struct dma_controller *c, chdat->ch = -1; } - DBG(3, "ep%i %s dma: %s dma%i dmareq%i sync%i\n", + dev_dbg(musb->controller, "ep%i %s dma: %s dma%i dmareq%i sync%i\n", chdat->epnum, chdat->tx ? "tx" : "rx", chdat->ch >= 0 ? "dedicated" : "shared", @@ -598,7 +598,7 @@ tusb_omap_dma_allocate(struct dma_controller *c, free_dmareq: tusb_omap_dma_free_dmareq(chdat); - DBG(3, "ep%i: Could not get a DMA channel\n", chdat->epnum); + dev_dbg(musb->controller, "ep%i: Could not get a DMA channel\n", chdat->epnum); channel->status = MUSB_DMA_STATUS_UNKNOWN; return NULL; @@ -611,7 +611,7 @@ static void tusb_omap_dma_release(struct dma_channel *channel) void __iomem *tbase = musb->ctrl_base; u32 reg; - DBG(3, "ep%i ch%i\n", chdat->epnum, chdat->ch); + dev_dbg(musb->controller, "ep%i ch%i\n", chdat->epnum, chdat->ch); reg = musb_readl(tbase, TUSB_DMA_INT_MASK); if (chdat->tx) diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c new file mode 100644 index 0000000..cecace4 --- /dev/null +++ b/drivers/usb/musb/ux500_dma.c @@ -0,0 +1,422 @@ +/* + * drivers/usb/musb/ux500_dma.c + * + * U8500 and U5500 DMA support code + * + * Copyright (C) 2009 STMicroelectronics + * Copyright (C) 2011 ST-Ericsson SA + * Authors: + * Mian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com> + * Praveena Nadahally <praveen.nadahally@stericsson.com> + * Rajaram Regupathy <ragupathy.rajaram@stericsson.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/dmaengine.h> +#include <linux/pfn.h> +#include <mach/usb.h> +#include "musb_core.h" + +struct ux500_dma_channel { + struct dma_channel channel; + struct ux500_dma_controller *controller; + struct musb_hw_ep *hw_ep; + struct work_struct channel_work; + struct dma_chan *dma_chan; + unsigned int cur_len; + dma_cookie_t cookie; + u8 ch_num; + u8 is_tx; + u8 is_allocated; +}; + +struct ux500_dma_controller { + struct dma_controller controller; + struct ux500_dma_channel rx_channel[UX500_MUSB_DMA_NUM_RX_CHANNELS]; + struct ux500_dma_channel tx_channel[UX500_MUSB_DMA_NUM_TX_CHANNELS]; + u32 num_rx_channels; + u32 num_tx_channels; + void *private_data; + dma_addr_t phy_base; +}; + +/* Work function invoked from DMA callback to handle tx transfers. */ +static void ux500_tx_work(struct work_struct *data) +{ + struct ux500_dma_channel *ux500_channel = container_of(data, + struct ux500_dma_channel, channel_work); + struct musb_hw_ep *hw_ep = ux500_channel->hw_ep; + struct musb *musb = hw_ep->musb; + unsigned long flags; + + DBG(4, "DMA tx transfer done on hw_ep=%d\n", hw_ep->epnum); + + spin_lock_irqsave(&musb->lock, flags); + ux500_channel->channel.actual_len = ux500_channel->cur_len; + ux500_channel->channel.status = MUSB_DMA_STATUS_FREE; + musb_dma_completion(musb, hw_ep->epnum, + ux500_channel->is_tx); + spin_unlock_irqrestore(&musb->lock, flags); +} + +/* Work function invoked from DMA callback to handle rx transfers. */ +static void ux500_rx_work(struct work_struct *data) +{ + struct ux500_dma_channel *ux500_channel = container_of(data, + struct ux500_dma_channel, channel_work); + struct musb_hw_ep *hw_ep = ux500_channel->hw_ep; + struct musb *musb = hw_ep->musb; + unsigned long flags; + + DBG(4, "DMA rx transfer done on hw_ep=%d\n", hw_ep->epnum); + + spin_lock_irqsave(&musb->lock, flags); + ux500_channel->channel.actual_len = ux500_channel->cur_len; + ux500_channel->channel.status = MUSB_DMA_STATUS_FREE; + musb_dma_completion(musb, hw_ep->epnum, + ux500_channel->is_tx); + spin_unlock_irqrestore(&musb->lock, flags); +} + +void ux500_dma_callback(void *private_data) +{ + struct dma_channel *channel = (struct dma_channel *)private_data; + struct ux500_dma_channel *ux500_channel = channel->private_data; + + schedule_work(&ux500_channel->channel_work); +} + +static bool ux500_configure_channel(struct dma_channel *channel, + u16 packet_sz, u8 mode, + dma_addr_t dma_addr, u32 len) +{ + struct ux500_dma_channel *ux500_channel = channel->private_data; + struct musb_hw_ep *hw_ep = ux500_channel->hw_ep; + struct dma_chan *dma_chan = ux500_channel->dma_chan; + struct dma_async_tx_descriptor *dma_desc; + enum dma_data_direction direction; + struct scatterlist sg; + struct dma_slave_config slave_conf; + enum dma_slave_buswidth addr_width; + dma_addr_t usb_fifo_addr = (MUSB_FIFO_OFFSET(hw_ep->epnum) + + ux500_channel->controller->phy_base); + + DBG(4, "packet_sz=%d, mode=%d, dma_addr=0x%x, len=%d is_tx=%d\n", + packet_sz, mode, dma_addr, len, ux500_channel->is_tx); + + ux500_channel->cur_len = len; + + sg_init_table(&sg, 1); + sg_set_page(&sg, pfn_to_page(PFN_DOWN(dma_addr)), len, + offset_in_page(dma_addr)); + sg_dma_address(&sg) = dma_addr; + sg_dma_len(&sg) = len; + + direction = ux500_channel->is_tx ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + addr_width = (len & 0x3) ? DMA_SLAVE_BUSWIDTH_1_BYTE : + DMA_SLAVE_BUSWIDTH_4_BYTES; + + slave_conf.direction = direction; + if (direction == DMA_FROM_DEVICE) { + slave_conf.src_addr = usb_fifo_addr; + slave_conf.src_addr_width = addr_width; + slave_conf.src_maxburst = 16; + } else { + slave_conf.dst_addr = usb_fifo_addr; + slave_conf.dst_addr_width = addr_width; + slave_conf.dst_maxburst = 16; + } + dma_chan->device->device_control(dma_chan, DMA_SLAVE_CONFIG, + (unsigned long) &slave_conf); + + dma_desc = dma_chan->device-> + device_prep_slave_sg(dma_chan, &sg, 1, direction, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); + if (!dma_desc) + return false; + + dma_desc->callback = ux500_dma_callback; + dma_desc->callback_param = channel; + ux500_channel->cookie = dma_desc->tx_submit(dma_desc); + + dma_async_issue_pending(dma_chan); + + return true; +} + +static struct dma_channel *ux500_dma_channel_allocate(struct dma_controller *c, + struct musb_hw_ep *hw_ep, u8 is_tx) +{ + struct ux500_dma_controller *controller = container_of(c, + struct ux500_dma_controller, controller); + struct ux500_dma_channel *ux500_channel = NULL; + u8 ch_num = hw_ep->epnum - 1; + u32 max_ch; + + /* Max 8 DMA channels (0 - 7). Each DMA channel can only be allocated + * to specified hw_ep. For example DMA channel 0 can only be allocated + * to hw_ep 1 and 9. + */ + if (ch_num > 7) + ch_num -= 8; + + max_ch = is_tx ? controller->num_tx_channels : + controller->num_rx_channels; + + if (ch_num >= max_ch) + return NULL; + + ux500_channel = is_tx ? &(controller->tx_channel[ch_num]) : + &(controller->rx_channel[ch_num]) ; + + /* Check if channel is already used. */ + if (ux500_channel->is_allocated) + return NULL; + + ux500_channel->hw_ep = hw_ep; + ux500_channel->is_allocated = 1; + + DBG(7, "hw_ep=%d, is_tx=0x%x, channel=%d\n", + hw_ep->epnum, is_tx, ch_num); + + return &(ux500_channel->channel); +} + +static void ux500_dma_channel_release(struct dma_channel *channel) +{ + struct ux500_dma_channel *ux500_channel = channel->private_data; + + DBG(7, "channel=%d\n", ux500_channel->ch_num); + + if (ux500_channel->is_allocated) { + ux500_channel->is_allocated = 0; + channel->status = MUSB_DMA_STATUS_FREE; + channel->actual_len = 0; + } +} + +static int ux500_dma_is_compatible(struct dma_channel *channel, + u16 maxpacket, void *buf, u32 length) +{ + if ((maxpacket & 0x3) || + ((int)buf & 0x3) || + (length < 512) || + (length & 0x3)) + return false; + else + return true; +} + +static int ux500_dma_channel_program(struct dma_channel *channel, + u16 packet_sz, u8 mode, + dma_addr_t dma_addr, u32 len) +{ + int ret; + + BUG_ON(channel->status == MUSB_DMA_STATUS_UNKNOWN || + channel->status == MUSB_DMA_STATUS_BUSY); + + if (!ux500_dma_is_compatible(channel, packet_sz, (void *)dma_addr, len)) + return false; + + channel->status = MUSB_DMA_STATUS_BUSY; + channel->actual_len = 0; + ret = ux500_configure_channel(channel, packet_sz, mode, dma_addr, len); + if (!ret) + channel->status = MUSB_DMA_STATUS_FREE; + + return ret; +} + +static int ux500_dma_channel_abort(struct dma_channel *channel) +{ + struct ux500_dma_channel *ux500_channel = channel->private_data; + struct ux500_dma_controller *controller = ux500_channel->controller; + struct musb *musb = controller->private_data; + void __iomem *epio = musb->endpoints[ux500_channel->hw_ep->epnum].regs; + u16 csr; + + DBG(4, "channel=%d, is_tx=%d\n", ux500_channel->ch_num, + ux500_channel->is_tx); + + if (channel->status == MUSB_DMA_STATUS_BUSY) { + if (ux500_channel->is_tx) { + csr = musb_readw(epio, MUSB_TXCSR); + csr &= ~(MUSB_TXCSR_AUTOSET | + MUSB_TXCSR_DMAENAB | + MUSB_TXCSR_DMAMODE); + musb_writew(epio, MUSB_TXCSR, csr); + } else { + csr = musb_readw(epio, MUSB_RXCSR); + csr &= ~(MUSB_RXCSR_AUTOCLEAR | + MUSB_RXCSR_DMAENAB | + MUSB_RXCSR_DMAMODE); + musb_writew(epio, MUSB_RXCSR, csr); + } + + ux500_channel->dma_chan->device-> + device_control(ux500_channel->dma_chan, + DMA_TERMINATE_ALL, 0); + channel->status = MUSB_DMA_STATUS_FREE; + } + return 0; +} + +static int ux500_dma_controller_stop(struct dma_controller *c) +{ + struct ux500_dma_controller *controller = container_of(c, + struct ux500_dma_controller, controller); + struct ux500_dma_channel *ux500_channel; + struct dma_channel *channel; + u8 ch_num; + + for (ch_num = 0; ch_num < controller->num_rx_channels; ch_num++) { + channel = &controller->rx_channel[ch_num].channel; + ux500_channel = channel->private_data; + + ux500_dma_channel_release(channel); + + if (ux500_channel->dma_chan) + dma_release_channel(ux500_channel->dma_chan); + } + + for (ch_num = 0; ch_num < controller->num_tx_channels; ch_num++) { + channel = &controller->tx_channel[ch_num].channel; + ux500_channel = channel->private_data; + + ux500_dma_channel_release(channel); + + if (ux500_channel->dma_chan) + dma_release_channel(ux500_channel->dma_chan); + } + + return 0; +} + +static int ux500_dma_controller_start(struct dma_controller *c) +{ + struct ux500_dma_controller *controller = container_of(c, + struct ux500_dma_controller, controller); + struct ux500_dma_channel *ux500_channel = NULL; + struct musb *musb = controller->private_data; + struct device *dev = musb->controller; + struct musb_hdrc_platform_data *plat = dev->platform_data; + struct ux500_musb_board_data *data = plat->board_data; + struct dma_channel *dma_channel = NULL; + u32 ch_num; + u8 dir; + u8 is_tx = 0; + + void **param_array; + struct ux500_dma_channel *channel_array; + u32 ch_count; + void (*musb_channel_work)(struct work_struct *); + dma_cap_mask_t mask; + + if ((data->num_rx_channels > UX500_MUSB_DMA_NUM_RX_CHANNELS) || + (data->num_tx_channels > UX500_MUSB_DMA_NUM_TX_CHANNELS)) + return -EINVAL; + + controller->num_rx_channels = data->num_rx_channels; + controller->num_tx_channels = data->num_tx_channels; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + /* Prepare the loop for RX channels */ + channel_array = controller->rx_channel; + ch_count = data->num_rx_channels; + param_array = data->dma_rx_param_array; + musb_channel_work = ux500_rx_work; + + for (dir = 0; dir < 2; dir++) { + for (ch_num = 0; ch_num < ch_count; ch_num++) { + ux500_channel = &channel_array[ch_num]; + ux500_channel->controller = controller; + ux500_channel->ch_num = ch_num; + ux500_channel->is_tx = is_tx; + + dma_channel = &(ux500_channel->channel); + dma_channel->private_data = ux500_channel; + dma_channel->status = MUSB_DMA_STATUS_FREE; + dma_channel->max_len = SZ_16M; + + ux500_channel->dma_chan = dma_request_channel(mask, + data->dma_filter, + param_array[ch_num]); + if (!ux500_channel->dma_chan) { + ERR("Dma pipe allocation error dir=%d ch=%d\n", + dir, ch_num); + + /* Release already allocated channels */ + ux500_dma_controller_stop(c); + + return -EBUSY; + } + + INIT_WORK(&ux500_channel->channel_work, + musb_channel_work); + } + + /* Prepare the loop for TX channels */ + channel_array = controller->tx_channel; + ch_count = data->num_tx_channels; + param_array = data->dma_tx_param_array; + musb_channel_work = ux500_tx_work; + is_tx = 1; + } + + return 0; +} + +void dma_controller_destroy(struct dma_controller *c) +{ + struct ux500_dma_controller *controller = container_of(c, + struct ux500_dma_controller, controller); + + kfree(controller); +} + +struct dma_controller *__init +dma_controller_create(struct musb *musb, void __iomem *base) +{ + struct ux500_dma_controller *controller; + struct platform_device *pdev = to_platform_device(musb->controller); + struct resource *iomem; + + controller = kzalloc(sizeof(*controller), GFP_KERNEL); + if (!controller) + return NULL; + + controller->private_data = musb; + + /* Save physical address for DMA controller. */ + iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + controller->phy_base = (dma_addr_t) iomem->start; + + controller->controller.start = ux500_dma_controller_start; + controller->controller.stop = ux500_dma_controller_stop; + controller->controller.channel_alloc = ux500_dma_channel_allocate; + controller->controller.channel_release = ux500_dma_channel_release; + controller->controller.channel_program = ux500_dma_channel_program; + controller->controller.channel_abort = ux500_dma_channel_abort; + controller->controller.is_compatible = ux500_dma_is_compatible; + + return &controller->controller; +} diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig index daf3e5f..c66481a 100644 --- a/drivers/usb/otg/Kconfig +++ b/drivers/usb/otg/Kconfig @@ -122,4 +122,12 @@ config AB8500_USB This transceiver supports high and full speed devices plus, in host mode, low speed. +config FSL_USB2_OTG + bool "Freescale USB OTG Transceiver Driver" + depends on USB_EHCI_FSL && USB_GADGET_FSL_USB2 + select USB_OTG + select USB_OTG_UTILS + help + Enable this to support Freescale USB OTG transceiver. + endif # USB || OTG diff --git a/drivers/usb/otg/Makefile b/drivers/usb/otg/Makefile index e22d917..566655c 100644 --- a/drivers/usb/otg/Makefile +++ b/drivers/usb/otg/Makefile @@ -19,3 +19,5 @@ obj-$(CONFIG_USB_ULPI) += ulpi.o obj-$(CONFIG_USB_ULPI_VIEWPORT) += ulpi_viewport.o obj-$(CONFIG_USB_MSM_OTG) += msm_otg.o obj-$(CONFIG_AB8500_USB) += ab8500-usb.o +fsl_usb2_otg-objs := fsl_otg.o otg_fsm.o +obj-$(CONFIG_FSL_USB2_OTG) += fsl_usb2_otg.o diff --git a/drivers/usb/otg/fsl_otg.c b/drivers/usb/otg/fsl_otg.c new file mode 100644 index 0000000..0f420b2 --- /dev/null +++ b/drivers/usb/otg/fsl_otg.c @@ -0,0 +1,1169 @@ +/* + * Copyright (C) 2007,2008 Freescale semiconductor, Inc. + * + * Author: Li Yang <LeoLi@freescale.com> + * Jerry Huang <Chang-Ming.Huang@freescale.com> + * + * Initialization based on code from Shlomi Gridish. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/proc_fs.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/timer.h> +#include <linux/usb.h> +#include <linux/device.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/workqueue.h> +#include <linux/time.h> +#include <linux/fsl_devices.h> +#include <linux/platform_device.h> +#include <linux/uaccess.h> + +#include <asm/unaligned.h> + +#include "fsl_otg.h" + +#define DRIVER_VERSION "Rev. 1.55" +#define DRIVER_AUTHOR "Jerry Huang/Li Yang" +#define DRIVER_DESC "Freescale USB OTG Transceiver Driver" +#define DRIVER_INFO DRIVER_DESC " " DRIVER_VERSION + +static const char driver_name[] = "fsl-usb2-otg"; + +const pm_message_t otg_suspend_state = { + .event = 1, +}; + +#define HA_DATA_PULSE + +static struct usb_dr_mmap *usb_dr_regs; +static struct fsl_otg *fsl_otg_dev; +static int srp_wait_done; + +/* FSM timers */ +struct fsl_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, *a_aidl_bdis_tmr, + *b_ase0_brst_tmr, *b_se0_srp_tmr; + +/* Driver specific timers */ +struct fsl_otg_timer *b_data_pulse_tmr, *b_vbus_pulse_tmr, *b_srp_fail_tmr, + *b_srp_wait_tmr, *a_wait_enum_tmr; + +static struct list_head active_timers; + +static struct fsl_otg_config fsl_otg_initdata = { + .otg_port = 1, +}; + +#ifdef CONFIG_PPC32 +static u32 _fsl_readl_be(const unsigned __iomem *p) +{ + return in_be32(p); +} + +static u32 _fsl_readl_le(const unsigned __iomem *p) +{ + return in_le32(p); +} + +static void _fsl_writel_be(u32 v, unsigned __iomem *p) +{ + out_be32(p, v); +} + +static void _fsl_writel_le(u32 v, unsigned __iomem *p) +{ + out_le32(p, v); +} + +static u32 (*_fsl_readl)(const unsigned __iomem *p); +static void (*_fsl_writel)(u32 v, unsigned __iomem *p); + +#define fsl_readl(p) (*_fsl_readl)((p)) +#define fsl_writel(v, p) (*_fsl_writel)((v), (p)) + +#else +#define fsl_readl(addr) readl(addr) +#define fsl_writel(val, addr) writel(val, addr) +#endif /* CONFIG_PPC32 */ + +/* Routines to access transceiver ULPI registers */ +u8 view_ulpi(u8 addr) +{ + u32 temp; + + temp = 0x40000000 | (addr << 16); + fsl_writel(temp, &usb_dr_regs->ulpiview); + udelay(1000); + while (temp & 0x40) + temp = fsl_readl(&usb_dr_regs->ulpiview); + return (le32_to_cpu(temp) & 0x0000ff00) >> 8; +} + +int write_ulpi(u8 addr, u8 data) +{ + u32 temp; + + temp = 0x60000000 | (addr << 16) | data; + fsl_writel(temp, &usb_dr_regs->ulpiview); + return 0; +} + +/* -------------------------------------------------------------*/ +/* Operations that will be called from OTG Finite State Machine */ + +/* Charge vbus for vbus pulsing in SRP */ +void fsl_otg_chrg_vbus(int on) +{ + u32 tmp; + + tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; + + if (on) + /* stop discharging, start charging */ + tmp = (tmp & ~OTGSC_CTRL_VBUS_DISCHARGE) | + OTGSC_CTRL_VBUS_CHARGE; + else + /* stop charging */ + tmp &= ~OTGSC_CTRL_VBUS_CHARGE; + + fsl_writel(tmp, &usb_dr_regs->otgsc); +} + +/* Discharge vbus through a resistor to ground */ +void fsl_otg_dischrg_vbus(int on) +{ + u32 tmp; + + tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; + + if (on) + /* stop charging, start discharging */ + tmp = (tmp & ~OTGSC_CTRL_VBUS_CHARGE) | + OTGSC_CTRL_VBUS_DISCHARGE; + else + /* stop discharging */ + tmp &= ~OTGSC_CTRL_VBUS_DISCHARGE; + + fsl_writel(tmp, &usb_dr_regs->otgsc); +} + +/* A-device driver vbus, controlled through PP bit in PORTSC */ +void fsl_otg_drv_vbus(int on) +{ + u32 tmp; + + if (on) { + tmp = fsl_readl(&usb_dr_regs->portsc) & ~PORTSC_W1C_BITS; + fsl_writel(tmp | PORTSC_PORT_POWER, &usb_dr_regs->portsc); + } else { + tmp = fsl_readl(&usb_dr_regs->portsc) & + ~PORTSC_W1C_BITS & ~PORTSC_PORT_POWER; + fsl_writel(tmp, &usb_dr_regs->portsc); + } +} + +/* + * Pull-up D+, signalling connect by periperal. Also used in + * data-line pulsing in SRP + */ +void fsl_otg_loc_conn(int on) +{ + u32 tmp; + + tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; + + if (on) + tmp |= OTGSC_CTRL_DATA_PULSING; + else + tmp &= ~OTGSC_CTRL_DATA_PULSING; + + fsl_writel(tmp, &usb_dr_regs->otgsc); +} + +/* + * Generate SOF by host. This is controlled through suspend/resume the + * port. In host mode, controller will automatically send SOF. + * Suspend will block the data on the port. + */ +void fsl_otg_loc_sof(int on) +{ + u32 tmp; + + tmp = fsl_readl(&fsl_otg_dev->dr_mem_map->portsc) & ~PORTSC_W1C_BITS; + if (on) + tmp |= PORTSC_PORT_FORCE_RESUME; + else + tmp |= PORTSC_PORT_SUSPEND; + + fsl_writel(tmp, &fsl_otg_dev->dr_mem_map->portsc); + +} + +/* Start SRP pulsing by data-line pulsing, followed with v-bus pulsing. */ +void fsl_otg_start_pulse(void) +{ + u32 tmp; + + srp_wait_done = 0; +#ifdef HA_DATA_PULSE + tmp = fsl_readl(&usb_dr_regs->otgsc) & ~OTGSC_INTSTS_MASK; + tmp |= OTGSC_HA_DATA_PULSE; + fsl_writel(tmp, &usb_dr_regs->otgsc); +#else + fsl_otg_loc_conn(1); +#endif + + fsl_otg_add_timer(b_data_pulse_tmr); +} + +void b_data_pulse_end(unsigned long foo) +{ +#ifdef HA_DATA_PULSE +#else + fsl_otg_loc_conn(0); +#endif + + /* Do VBUS pulse after data pulse */ + fsl_otg_pulse_vbus(); +} + +void fsl_otg_pulse_vbus(void) +{ + srp_wait_done = 0; + fsl_otg_chrg_vbus(1); + /* start the timer to end vbus charge */ + fsl_otg_add_timer(b_vbus_pulse_tmr); +} + +void b_vbus_pulse_end(unsigned long foo) +{ + fsl_otg_chrg_vbus(0); + + /* + * As USB3300 using the same a_sess_vld and b_sess_vld voltage + * we need to discharge the bus for a while to distinguish + * residual voltage of vbus pulsing and A device pull up + */ + fsl_otg_dischrg_vbus(1); + fsl_otg_add_timer(b_srp_wait_tmr); +} + +void b_srp_end(unsigned long foo) +{ + fsl_otg_dischrg_vbus(0); + srp_wait_done = 1; + + if ((fsl_otg_dev->otg.state == OTG_STATE_B_SRP_INIT) && + fsl_otg_dev->fsm.b_sess_vld) + fsl_otg_dev->fsm.b_srp_done = 1; +} + +/* + * Workaround for a_host suspending too fast. When a_bus_req=0, + * a_host will start by SRP. It needs to set b_hnp_enable before + * actually suspending to start HNP + */ +void a_wait_enum(unsigned long foo) +{ + VDBG("a_wait_enum timeout\n"); + if (!fsl_otg_dev->otg.host->b_hnp_enable) + fsl_otg_add_timer(a_wait_enum_tmr); + else + otg_statemachine(&fsl_otg_dev->fsm); +} + +/* The timeout callback function to set time out bit */ +void set_tmout(unsigned long indicator) +{ + *(int *)indicator = 1; +} + +/* Initialize timers */ +int fsl_otg_init_timers(struct otg_fsm *fsm) +{ + /* FSM used timers */ + a_wait_vrise_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_VRISE, + (unsigned long)&fsm->a_wait_vrise_tmout); + if (!a_wait_vrise_tmr) + return -ENOMEM; + + a_wait_bcon_tmr = otg_timer_initializer(&set_tmout, TA_WAIT_BCON, + (unsigned long)&fsm->a_wait_bcon_tmout); + if (!a_wait_bcon_tmr) + return -ENOMEM; + + a_aidl_bdis_tmr = otg_timer_initializer(&set_tmout, TA_AIDL_BDIS, + (unsigned long)&fsm->a_aidl_bdis_tmout); + if (!a_aidl_bdis_tmr) + return -ENOMEM; + + b_ase0_brst_tmr = otg_timer_initializer(&set_tmout, TB_ASE0_BRST, + (unsigned long)&fsm->b_ase0_brst_tmout); + if (!b_ase0_brst_tmr) + return -ENOMEM; + + b_se0_srp_tmr = otg_timer_initializer(&set_tmout, TB_SE0_SRP, + (unsigned long)&fsm->b_se0_srp); + if (!b_se0_srp_tmr) + return -ENOMEM; + + b_srp_fail_tmr = otg_timer_initializer(&set_tmout, TB_SRP_FAIL, + (unsigned long)&fsm->b_srp_done); + if (!b_srp_fail_tmr) + return -ENOMEM; + + a_wait_enum_tmr = otg_timer_initializer(&a_wait_enum, 10, + (unsigned long)&fsm); + if (!a_wait_enum_tmr) + return -ENOMEM; + + /* device driver used timers */ + b_srp_wait_tmr = otg_timer_initializer(&b_srp_end, TB_SRP_WAIT, 0); + if (!b_srp_wait_tmr) + return -ENOMEM; + + b_data_pulse_tmr = otg_timer_initializer(&b_data_pulse_end, + TB_DATA_PLS, 0); + if (!b_data_pulse_tmr) + return -ENOMEM; + + b_vbus_pulse_tmr = otg_timer_initializer(&b_vbus_pulse_end, + TB_VBUS_PLS, 0); + if (!b_vbus_pulse_tmr) + return -ENOMEM; + + return 0; +} + +/* Uninitialize timers */ +void fsl_otg_uninit_timers(void) +{ + /* FSM used timers */ + if (a_wait_vrise_tmr != NULL) + kfree(a_wait_vrise_tmr); + if (a_wait_bcon_tmr != NULL) + kfree(a_wait_bcon_tmr); + if (a_aidl_bdis_tmr != NULL) + kfree(a_aidl_bdis_tmr); + if (b_ase0_brst_tmr != NULL) + kfree(b_ase0_brst_tmr); + if (b_se0_srp_tmr != NULL) + kfree(b_se0_srp_tmr); + if (b_srp_fail_tmr != NULL) + kfree(b_srp_fail_tmr); + if (a_wait_enum_tmr != NULL) + kfree(a_wait_enum_tmr); + + /* device driver used timers */ + if (b_srp_wait_tmr != NULL) + kfree(b_srp_wait_tmr); + if (b_data_pulse_tmr != NULL) + kfree(b_data_pulse_tmr); + if (b_vbus_pulse_tmr != NULL) + kfree(b_vbus_pulse_tmr); +} + +/* Add timer to timer list */ +void fsl_otg_add_timer(void *gtimer) +{ + struct fsl_otg_timer *timer = gtimer; + struct fsl_otg_timer *tmp_timer; + + /* + * Check if the timer is already in the active list, + * if so update timer count + */ + list_for_each_entry(tmp_timer, &active_timers, list) + if (tmp_timer == timer) { + timer->count = timer->expires; + return; + } + timer->count = timer->expires; + list_add_tail(&timer->list, &active_timers); +} + +/* Remove timer from the timer list; clear timeout status */ +void fsl_otg_del_timer(void *gtimer) +{ + struct fsl_otg_timer *timer = gtimer; + struct fsl_otg_timer *tmp_timer, *del_tmp; + + list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) + if (tmp_timer == timer) + list_del(&timer->list); +} + +/* + * Reduce timer count by 1, and find timeout conditions. + * Called by fsl_otg 1ms timer interrupt + */ +int fsl_otg_tick_timer(void) +{ + struct fsl_otg_timer *tmp_timer, *del_tmp; + int expired = 0; + + list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) { + tmp_timer->count--; + /* check if timer expires */ + if (!tmp_timer->count) { + list_del(&tmp_timer->list); + tmp_timer->function(tmp_timer->data); + expired = 1; + } + } + + return expired; +} + +/* Reset controller, not reset the bus */ +void otg_reset_controller(void) +{ + u32 command; + + command = fsl_readl(&usb_dr_regs->usbcmd); + command |= (1 << 1); + fsl_writel(command, &usb_dr_regs->usbcmd); + while (fsl_readl(&usb_dr_regs->usbcmd) & (1 << 1)) + ; +} + +/* Call suspend/resume routines in host driver */ +int fsl_otg_start_host(struct otg_fsm *fsm, int on) +{ + struct otg_transceiver *xceiv = fsm->transceiver; + struct device *dev; + struct fsl_otg *otg_dev = container_of(xceiv, struct fsl_otg, otg); + u32 retval = 0; + + if (!xceiv->host) + return -ENODEV; + dev = xceiv->host->controller; + + /* + * Update a_vbus_vld state as a_vbus_vld int is disabled + * in device mode + */ + fsm->a_vbus_vld = + !!(fsl_readl(&usb_dr_regs->otgsc) & OTGSC_STS_A_VBUS_VALID); + if (on) { + /* start fsl usb host controller */ + if (otg_dev->host_working) + goto end; + else { + otg_reset_controller(); + VDBG("host on......\n"); + if (dev->driver->pm && dev->driver->pm->resume) { + retval = dev->driver->pm->resume(dev); + if (fsm->id) { + /* default-b */ + fsl_otg_drv_vbus(1); + /* + * Workaround: b_host can't driver + * vbus, but PP in PORTSC needs to + * be 1 for host to work. + * So we set drv_vbus bit in + * transceiver to 0 thru ULPI. + */ + write_ulpi(0x0c, 0x20); + } + } + + otg_dev->host_working = 1; + } + } else { + /* stop fsl usb host controller */ + if (!otg_dev->host_working) + goto end; + else { + VDBG("host off......\n"); + if (dev && dev->driver) { + if (dev->driver->pm && dev->driver->pm->suspend) + retval = dev->driver->pm->suspend(dev); + if (fsm->id) + /* default-b */ + fsl_otg_drv_vbus(0); + } + otg_dev->host_working = 0; + } + } +end: + return retval; +} + +/* + * Call suspend and resume function in udc driver + * to stop and start udc driver. + */ +int fsl_otg_start_gadget(struct otg_fsm *fsm, int on) +{ + struct otg_transceiver *xceiv = fsm->transceiver; + struct device *dev; + + if (!xceiv->gadget || !xceiv->gadget->dev.parent) + return -ENODEV; + + VDBG("gadget %s\n", on ? "on" : "off"); + dev = xceiv->gadget->dev.parent; + + if (on) { + if (dev->driver->resume) + dev->driver->resume(dev); + } else { + if (dev->driver->suspend) + dev->driver->suspend(dev, otg_suspend_state); + } + + return 0; +} + +/* + * Called by initialization code of host driver. Register host controller + * to the OTG. Suspend host for OTG role detection. + */ +static int fsl_otg_set_host(struct otg_transceiver *otg_p, struct usb_bus *host) +{ + struct fsl_otg *otg_dev = container_of(otg_p, struct fsl_otg, otg); + + if (!otg_p || otg_dev != fsl_otg_dev) + return -ENODEV; + + otg_p->host = host; + + otg_dev->fsm.a_bus_drop = 0; + otg_dev->fsm.a_bus_req = 1; + + if (host) { + VDBG("host off......\n"); + + otg_p->host->otg_port = fsl_otg_initdata.otg_port; + otg_p->host->is_b_host = otg_dev->fsm.id; + /* + * must leave time for khubd to finish its thing + * before yanking the host driver out from under it, + * so suspend the host after a short delay. + */ + otg_dev->host_working = 1; + schedule_delayed_work(&otg_dev->otg_event, 100); + return 0; + } else { + /* host driver going away */ + if (!(fsl_readl(&otg_dev->dr_mem_map->otgsc) & + OTGSC_STS_USB_ID)) { + /* Mini-A cable connected */ + struct otg_fsm *fsm = &otg_dev->fsm; + + otg_p->state = OTG_STATE_UNDEFINED; + fsm->protocol = PROTO_UNDEF; + } + } + + otg_dev->host_working = 0; + + otg_statemachine(&otg_dev->fsm); + + return 0; +} + +/* Called by initialization code of udc. Register udc to OTG. */ +static int fsl_otg_set_peripheral(struct otg_transceiver *otg_p, + struct usb_gadget *gadget) +{ + struct fsl_otg *otg_dev = container_of(otg_p, struct fsl_otg, otg); + + VDBG("otg_dev 0x%x\n", (int)otg_dev); + VDBG("fsl_otg_dev 0x%x\n", (int)fsl_otg_dev); + + if (!otg_p || otg_dev != fsl_otg_dev) + return -ENODEV; + + if (!gadget) { + if (!otg_dev->otg.default_a) + otg_p->gadget->ops->vbus_draw(otg_p->gadget, 0); + usb_gadget_vbus_disconnect(otg_dev->otg.gadget); + otg_dev->otg.gadget = 0; + otg_dev->fsm.b_bus_req = 0; + otg_statemachine(&otg_dev->fsm); + return 0; + } + + otg_p->gadget = gadget; + otg_p->gadget->is_a_peripheral = !otg_dev->fsm.id; + + otg_dev->fsm.b_bus_req = 1; + + /* start the gadget right away if the ID pin says Mini-B */ + DBG("ID pin=%d\n", otg_dev->fsm.id); + if (otg_dev->fsm.id == 1) { + fsl_otg_start_host(&otg_dev->fsm, 0); + otg_drv_vbus(&otg_dev->fsm, 0); + fsl_otg_start_gadget(&otg_dev->fsm, 1); + } + + return 0; +} + +/* Set OTG port power, only for B-device */ +static int fsl_otg_set_power(struct otg_transceiver *otg_p, unsigned mA) +{ + if (!fsl_otg_dev) + return -ENODEV; + if (otg_p->state == OTG_STATE_B_PERIPHERAL) + pr_info("FSL OTG: Draw %d mA\n", mA); + + return 0; +} + +/* + * Delayed pin detect interrupt processing. + * + * When the Mini-A cable is disconnected from the board, + * the pin-detect interrupt happens before the disconnnect + * interrupts for the connected device(s). In order to + * process the disconnect interrupt(s) prior to switching + * roles, the pin-detect interrupts are delayed, and handled + * by this routine. + */ +static void fsl_otg_event(struct work_struct *work) +{ + struct fsl_otg *og = container_of(work, struct fsl_otg, otg_event.work); + struct otg_fsm *fsm = &og->fsm; + + if (fsm->id) { /* switch to gadget */ + fsl_otg_start_host(fsm, 0); + otg_drv_vbus(fsm, 0); + fsl_otg_start_gadget(fsm, 1); + } +} + +/* B-device start SRP */ +static int fsl_otg_start_srp(struct otg_transceiver *otg_p) +{ + struct fsl_otg *otg_dev = container_of(otg_p, struct fsl_otg, otg); + + if (!otg_p || otg_dev != fsl_otg_dev + || otg_p->state != OTG_STATE_B_IDLE) + return -ENODEV; + + otg_dev->fsm.b_bus_req = 1; + otg_statemachine(&otg_dev->fsm); + + return 0; +} + +/* A_host suspend will call this function to start hnp */ +static int fsl_otg_start_hnp(struct otg_transceiver *otg_p) +{ + struct fsl_otg *otg_dev = container_of(otg_p, struct fsl_otg, otg); + + if (!otg_p || otg_dev != fsl_otg_dev) + return -ENODEV; + + DBG("start_hnp...n"); + + /* clear a_bus_req to enter a_suspend state */ + otg_dev->fsm.a_bus_req = 0; + otg_statemachine(&otg_dev->fsm); + + return 0; +} + +/* + * Interrupt handler. OTG/host/peripheral share the same int line. + * OTG driver clears OTGSC interrupts and leaves USB interrupts + * intact. It needs to have knowledge of some USB interrupts + * such as port change. + */ +irqreturn_t fsl_otg_isr(int irq, void *dev_id) +{ + struct otg_fsm *fsm = &((struct fsl_otg *)dev_id)->fsm; + struct otg_transceiver *otg = &((struct fsl_otg *)dev_id)->otg; + u32 otg_int_src, otg_sc; + + otg_sc = fsl_readl(&usb_dr_regs->otgsc); + otg_int_src = otg_sc & OTGSC_INTSTS_MASK & (otg_sc >> 8); + + /* Only clear otg interrupts */ + fsl_writel(otg_sc, &usb_dr_regs->otgsc); + + /*FIXME: ID change not generate when init to 0 */ + fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0; + otg->default_a = (fsm->id == 0); + + /* process OTG interrupts */ + if (otg_int_src) { + if (otg_int_src & OTGSC_INTSTS_USB_ID) { + fsm->id = (otg_sc & OTGSC_STS_USB_ID) ? 1 : 0; + otg->default_a = (fsm->id == 0); + /* clear conn information */ + if (fsm->id) + fsm->b_conn = 0; + else + fsm->a_conn = 0; + + if (otg->host) + otg->host->is_b_host = fsm->id; + if (otg->gadget) + otg->gadget->is_a_peripheral = !fsm->id; + VDBG("ID int (ID is %d)\n", fsm->id); + + if (fsm->id) { /* switch to gadget */ + schedule_delayed_work( + &((struct fsl_otg *)dev_id)->otg_event, + 100); + } else { /* switch to host */ + cancel_delayed_work(& + ((struct fsl_otg *)dev_id)-> + otg_event); + fsl_otg_start_gadget(fsm, 0); + otg_drv_vbus(fsm, 1); + fsl_otg_start_host(fsm, 1); + } + return IRQ_HANDLED; + } + } + return IRQ_NONE; +} + +static struct otg_fsm_ops fsl_otg_ops = { + .chrg_vbus = fsl_otg_chrg_vbus, + .drv_vbus = fsl_otg_drv_vbus, + .loc_conn = fsl_otg_loc_conn, + .loc_sof = fsl_otg_loc_sof, + .start_pulse = fsl_otg_start_pulse, + + .add_timer = fsl_otg_add_timer, + .del_timer = fsl_otg_del_timer, + + .start_host = fsl_otg_start_host, + .start_gadget = fsl_otg_start_gadget, +}; + +/* Initialize the global variable fsl_otg_dev and request IRQ for OTG */ +static int fsl_otg_conf(struct platform_device *pdev) +{ + struct fsl_otg *fsl_otg_tc; + int status; + + if (fsl_otg_dev) + return 0; + + /* allocate space to fsl otg device */ + fsl_otg_tc = kzalloc(sizeof(struct fsl_otg), GFP_KERNEL); + if (!fsl_otg_tc) + return -ENOMEM; + + INIT_DELAYED_WORK(&fsl_otg_tc->otg_event, fsl_otg_event); + + INIT_LIST_HEAD(&active_timers); + status = fsl_otg_init_timers(&fsl_otg_tc->fsm); + if (status) { + pr_info("Couldn't init OTG timers\n"); + goto err; + } + spin_lock_init(&fsl_otg_tc->fsm.lock); + + /* Set OTG state machine operations */ + fsl_otg_tc->fsm.ops = &fsl_otg_ops; + + /* initialize the otg structure */ + fsl_otg_tc->otg.label = DRIVER_DESC; + fsl_otg_tc->otg.set_host = fsl_otg_set_host; + fsl_otg_tc->otg.set_peripheral = fsl_otg_set_peripheral; + fsl_otg_tc->otg.set_power = fsl_otg_set_power; + fsl_otg_tc->otg.start_hnp = fsl_otg_start_hnp; + fsl_otg_tc->otg.start_srp = fsl_otg_start_srp; + + fsl_otg_dev = fsl_otg_tc; + + /* Store the otg transceiver */ + status = otg_set_transceiver(&fsl_otg_tc->otg); + if (status) { + pr_warn(FSL_OTG_NAME ": unable to register OTG transceiver.\n"); + goto err; + } + + return 0; +err: + fsl_otg_uninit_timers(); + kfree(fsl_otg_tc); + return status; +} + +/* OTG Initialization */ +int usb_otg_start(struct platform_device *pdev) +{ + struct fsl_otg *p_otg; + struct otg_transceiver *otg_trans = otg_get_transceiver(); + struct otg_fsm *fsm; + int status; + struct resource *res; + u32 temp; + struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; + + p_otg = container_of(otg_trans, struct fsl_otg, otg); + fsm = &p_otg->fsm; + + /* Initialize the state machine structure with default values */ + SET_OTG_STATE(otg_trans, OTG_STATE_UNDEFINED); + fsm->transceiver = &p_otg->otg; + + /* We don't require predefined MEM/IRQ resource index */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENXIO; + + /* We don't request_mem_region here to enable resource sharing + * with host/device */ + + usb_dr_regs = ioremap(res->start, sizeof(struct usb_dr_mmap)); + p_otg->dr_mem_map = (struct usb_dr_mmap *)usb_dr_regs; + pdata->regs = (void *)usb_dr_regs; + + if (pdata->init && pdata->init(pdev) != 0) + return -EINVAL; + + if (pdata->big_endian_mmio) { + _fsl_readl = _fsl_readl_be; + _fsl_writel = _fsl_writel_be; + } else { + _fsl_readl = _fsl_readl_le; + _fsl_writel = _fsl_writel_le; + } + + /* request irq */ + p_otg->irq = platform_get_irq(pdev, 0); + status = request_irq(p_otg->irq, fsl_otg_isr, + IRQF_SHARED, driver_name, p_otg); + if (status) { + dev_dbg(p_otg->otg.dev, "can't get IRQ %d, error %d\n", + p_otg->irq, status); + iounmap(p_otg->dr_mem_map); + kfree(p_otg); + return status; + } + + /* stop the controller */ + temp = fsl_readl(&p_otg->dr_mem_map->usbcmd); + temp &= ~USB_CMD_RUN_STOP; + fsl_writel(temp, &p_otg->dr_mem_map->usbcmd); + + /* reset the controller */ + temp = fsl_readl(&p_otg->dr_mem_map->usbcmd); + temp |= USB_CMD_CTRL_RESET; + fsl_writel(temp, &p_otg->dr_mem_map->usbcmd); + + /* wait reset completed */ + while (fsl_readl(&p_otg->dr_mem_map->usbcmd) & USB_CMD_CTRL_RESET) + ; + + /* configure the VBUSHS as IDLE(both host and device) */ + temp = USB_MODE_STREAM_DISABLE | (pdata->es ? USB_MODE_ES : 0); + fsl_writel(temp, &p_otg->dr_mem_map->usbmode); + + /* configure PHY interface */ + temp = fsl_readl(&p_otg->dr_mem_map->portsc); + temp &= ~(PORTSC_PHY_TYPE_SEL | PORTSC_PTW); + switch (pdata->phy_mode) { + case FSL_USB2_PHY_ULPI: + temp |= PORTSC_PTS_ULPI; + break; + case FSL_USB2_PHY_UTMI_WIDE: + temp |= PORTSC_PTW_16BIT; + /* fall through */ + case FSL_USB2_PHY_UTMI: + temp |= PORTSC_PTS_UTMI; + /* fall through */ + default: + break; + } + fsl_writel(temp, &p_otg->dr_mem_map->portsc); + + if (pdata->have_sysif_regs) { + /* configure control enable IO output, big endian register */ + temp = __raw_readl(&p_otg->dr_mem_map->control); + temp |= USB_CTRL_IOENB; + __raw_writel(temp, &p_otg->dr_mem_map->control); + } + + /* disable all interrupt and clear all OTGSC status */ + temp = fsl_readl(&p_otg->dr_mem_map->otgsc); + temp &= ~OTGSC_INTERRUPT_ENABLE_BITS_MASK; + temp |= OTGSC_INTERRUPT_STATUS_BITS_MASK | OTGSC_CTRL_VBUS_DISCHARGE; + fsl_writel(temp, &p_otg->dr_mem_map->otgsc); + + /* + * The identification (id) input is FALSE when a Mini-A plug is inserted + * in the devices Mini-AB receptacle. Otherwise, this input is TRUE. + * Also: record initial state of ID pin + */ + if (fsl_readl(&p_otg->dr_mem_map->otgsc) & OTGSC_STS_USB_ID) { + p_otg->otg.state = OTG_STATE_UNDEFINED; + p_otg->fsm.id = 1; + } else { + p_otg->otg.state = OTG_STATE_A_IDLE; + p_otg->fsm.id = 0; + } + + DBG("initial ID pin=%d\n", p_otg->fsm.id); + + /* enable OTG ID pin interrupt */ + temp = fsl_readl(&p_otg->dr_mem_map->otgsc); + temp |= OTGSC_INTR_USB_ID_EN; + temp &= ~(OTGSC_CTRL_VBUS_DISCHARGE | OTGSC_INTR_1MS_TIMER_EN); + fsl_writel(temp, &p_otg->dr_mem_map->otgsc); + + return 0; +} + +/* + * state file in sysfs + */ +static int show_fsl_usb2_otg_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct otg_fsm *fsm = &fsl_otg_dev->fsm; + char *next = buf; + unsigned size = PAGE_SIZE; + unsigned long flags; + int t; + + spin_lock_irqsave(&fsm->lock, flags); + + /* basic driver infomation */ + t = scnprintf(next, size, + DRIVER_DESC "\n" "fsl_usb2_otg version: %s\n\n", + DRIVER_VERSION); + size -= t; + next += t; + + /* Registers */ + t = scnprintf(next, size, + "OTGSC: 0x%08x\n" + "PORTSC: 0x%08x\n" + "USBMODE: 0x%08x\n" + "USBCMD: 0x%08x\n" + "USBSTS: 0x%08x\n" + "USBINTR: 0x%08x\n", + fsl_readl(&usb_dr_regs->otgsc), + fsl_readl(&usb_dr_regs->portsc), + fsl_readl(&usb_dr_regs->usbmode), + fsl_readl(&usb_dr_regs->usbcmd), + fsl_readl(&usb_dr_regs->usbsts), + fsl_readl(&usb_dr_regs->usbintr)); + size -= t; + next += t; + + /* State */ + t = scnprintf(next, size, + "OTG state: %s\n\n", + otg_state_string(fsl_otg_dev->otg.state)); + size -= t; + next += t; + + /* State Machine Variables */ + t = scnprintf(next, size, + "a_bus_req: %d\n" + "b_bus_req: %d\n" + "a_bus_resume: %d\n" + "a_bus_suspend: %d\n" + "a_conn: %d\n" + "a_sess_vld: %d\n" + "a_srp_det: %d\n" + "a_vbus_vld: %d\n" + "b_bus_resume: %d\n" + "b_bus_suspend: %d\n" + "b_conn: %d\n" + "b_se0_srp: %d\n" + "b_sess_end: %d\n" + "b_sess_vld: %d\n" + "id: %d\n", + fsm->a_bus_req, + fsm->b_bus_req, + fsm->a_bus_resume, + fsm->a_bus_suspend, + fsm->a_conn, + fsm->a_sess_vld, + fsm->a_srp_det, + fsm->a_vbus_vld, + fsm->b_bus_resume, + fsm->b_bus_suspend, + fsm->b_conn, + fsm->b_se0_srp, + fsm->b_sess_end, + fsm->b_sess_vld, + fsm->id); + size -= t; + next += t; + + spin_unlock_irqrestore(&fsm->lock, flags); + + return PAGE_SIZE - size; +} + +static DEVICE_ATTR(fsl_usb2_otg_state, S_IRUGO, show_fsl_usb2_otg_state, NULL); + + +/* Char driver interface to control some OTG input */ + +/* + * Handle some ioctl command, such as get otg + * status and set host suspend + */ +static long fsl_otg_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + u32 retval = 0; + + switch (cmd) { + case GET_OTG_STATUS: + retval = fsl_otg_dev->host_working; + break; + + case SET_A_SUSPEND_REQ: + fsl_otg_dev->fsm.a_suspend_req = arg; + break; + + case SET_A_BUS_DROP: + fsl_otg_dev->fsm.a_bus_drop = arg; + break; + + case SET_A_BUS_REQ: + fsl_otg_dev->fsm.a_bus_req = arg; + break; + + case SET_B_BUS_REQ: + fsl_otg_dev->fsm.b_bus_req = arg; + break; + + default: + break; + } + + otg_statemachine(&fsl_otg_dev->fsm); + + return retval; +} + +static int fsl_otg_open(struct inode *inode, struct file *file) +{ + return 0; +} + +static int fsl_otg_release(struct inode *inode, struct file *file) +{ + return 0; +} + +static const struct file_operations otg_fops = { + .owner = THIS_MODULE, + .llseek = NULL, + .read = NULL, + .write = NULL, + .unlocked_ioctl = fsl_otg_ioctl, + .open = fsl_otg_open, + .release = fsl_otg_release, +}; + +static int __devinit fsl_otg_probe(struct platform_device *pdev) +{ + int ret; + + if (!pdev->dev.platform_data) + return -ENODEV; + + /* configure the OTG */ + ret = fsl_otg_conf(pdev); + if (ret) { + dev_err(&pdev->dev, "Couldn't configure OTG module\n"); + return ret; + } + + /* start OTG */ + ret = usb_otg_start(pdev); + if (ret) { + dev_err(&pdev->dev, "Can't init FSL OTG device\n"); + return ret; + } + + ret = register_chrdev(FSL_OTG_MAJOR, FSL_OTG_NAME, &otg_fops); + if (ret) { + dev_err(&pdev->dev, "unable to register FSL OTG device\n"); + return ret; + } + + ret = device_create_file(&pdev->dev, &dev_attr_fsl_usb2_otg_state); + if (ret) + dev_warn(&pdev->dev, "Can't register sysfs attribute\n"); + + return ret; +} + +static int __devexit fsl_otg_remove(struct platform_device *pdev) +{ + struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data; + + otg_set_transceiver(NULL); + free_irq(fsl_otg_dev->irq, fsl_otg_dev); + + iounmap((void *)usb_dr_regs); + + fsl_otg_uninit_timers(); + kfree(fsl_otg_dev); + + device_remove_file(&pdev->dev, &dev_attr_fsl_usb2_otg_state); + + unregister_chrdev(FSL_OTG_MAJOR, FSL_OTG_NAME); + + if (pdata->exit) + pdata->exit(pdev); + + return 0; +} + +struct platform_driver fsl_otg_driver = { + .probe = fsl_otg_probe, + .remove = __devexit_p(fsl_otg_remove), + .driver = { + .name = driver_name, + .owner = THIS_MODULE, + }, +}; + +static int __init fsl_usb_otg_init(void) +{ + pr_info(DRIVER_INFO "\n"); + return platform_driver_register(&fsl_otg_driver); +} +module_init(fsl_usb_otg_init); + +static void __exit fsl_usb_otg_exit(void) +{ + platform_driver_unregister(&fsl_otg_driver); +} +module_exit(fsl_usb_otg_exit); + +MODULE_DESCRIPTION(DRIVER_INFO); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/otg/fsl_otg.h b/drivers/usb/otg/fsl_otg.h new file mode 100644 index 0000000..3f8ef73 --- /dev/null +++ b/drivers/usb/otg/fsl_otg.h @@ -0,0 +1,406 @@ +/* Copyright (C) 2007,2008 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "otg_fsm.h" +#include <linux/usb/otg.h> +#include <linux/ioctl.h> + +/* USB Command Register Bit Masks */ +#define USB_CMD_RUN_STOP (0x1<<0) +#define USB_CMD_CTRL_RESET (0x1<<1) +#define USB_CMD_PERIODIC_SCHEDULE_EN (0x1<<4) +#define USB_CMD_ASYNC_SCHEDULE_EN (0x1<<5) +#define USB_CMD_INT_AA_DOORBELL (0x1<<6) +#define USB_CMD_ASP (0x3<<8) +#define USB_CMD_ASYNC_SCH_PARK_EN (0x1<<11) +#define USB_CMD_SUTW (0x1<<13) +#define USB_CMD_ATDTW (0x1<<14) +#define USB_CMD_ITC (0xFF<<16) + +/* bit 15,3,2 are frame list size */ +#define USB_CMD_FRAME_SIZE_1024 (0x0<<15 | 0x0<<2) +#define USB_CMD_FRAME_SIZE_512 (0x0<<15 | 0x1<<2) +#define USB_CMD_FRAME_SIZE_256 (0x0<<15 | 0x2<<2) +#define USB_CMD_FRAME_SIZE_128 (0x0<<15 | 0x3<<2) +#define USB_CMD_FRAME_SIZE_64 (0x1<<15 | 0x0<<2) +#define USB_CMD_FRAME_SIZE_32 (0x1<<15 | 0x1<<2) +#define USB_CMD_FRAME_SIZE_16 (0x1<<15 | 0x2<<2) +#define USB_CMD_FRAME_SIZE_8 (0x1<<15 | 0x3<<2) + +/* bit 9-8 are async schedule park mode count */ +#define USB_CMD_ASP_00 (0x0<<8) +#define USB_CMD_ASP_01 (0x1<<8) +#define USB_CMD_ASP_10 (0x2<<8) +#define USB_CMD_ASP_11 (0x3<<8) +#define USB_CMD_ASP_BIT_POS (8) + +/* bit 23-16 are interrupt threshold control */ +#define USB_CMD_ITC_NO_THRESHOLD (0x00<<16) +#define USB_CMD_ITC_1_MICRO_FRM (0x01<<16) +#define USB_CMD_ITC_2_MICRO_FRM (0x02<<16) +#define USB_CMD_ITC_4_MICRO_FRM (0x04<<16) +#define USB_CMD_ITC_8_MICRO_FRM (0x08<<16) +#define USB_CMD_ITC_16_MICRO_FRM (0x10<<16) +#define USB_CMD_ITC_32_MICRO_FRM (0x20<<16) +#define USB_CMD_ITC_64_MICRO_FRM (0x40<<16) +#define USB_CMD_ITC_BIT_POS (16) + +/* USB Status Register Bit Masks */ +#define USB_STS_INT (0x1<<0) +#define USB_STS_ERR (0x1<<1) +#define USB_STS_PORT_CHANGE (0x1<<2) +#define USB_STS_FRM_LST_ROLL (0x1<<3) +#define USB_STS_SYS_ERR (0x1<<4) +#define USB_STS_IAA (0x1<<5) +#define USB_STS_RESET_RECEIVED (0x1<<6) +#define USB_STS_SOF (0x1<<7) +#define USB_STS_DCSUSPEND (0x1<<8) +#define USB_STS_HC_HALTED (0x1<<12) +#define USB_STS_RCL (0x1<<13) +#define USB_STS_PERIODIC_SCHEDULE (0x1<<14) +#define USB_STS_ASYNC_SCHEDULE (0x1<<15) + +/* USB Interrupt Enable Register Bit Masks */ +#define USB_INTR_INT_EN (0x1<<0) +#define USB_INTR_ERR_INT_EN (0x1<<1) +#define USB_INTR_PC_DETECT_EN (0x1<<2) +#define USB_INTR_FRM_LST_ROLL_EN (0x1<<3) +#define USB_INTR_SYS_ERR_EN (0x1<<4) +#define USB_INTR_ASYN_ADV_EN (0x1<<5) +#define USB_INTR_RESET_EN (0x1<<6) +#define USB_INTR_SOF_EN (0x1<<7) +#define USB_INTR_DEVICE_SUSPEND (0x1<<8) + +/* Device Address bit masks */ +#define USB_DEVICE_ADDRESS_MASK (0x7F<<25) +#define USB_DEVICE_ADDRESS_BIT_POS (25) +/* PORTSC Register Bit Masks,Only one PORT in OTG mode*/ +#define PORTSC_CURRENT_CONNECT_STATUS (0x1<<0) +#define PORTSC_CONNECT_STATUS_CHANGE (0x1<<1) +#define PORTSC_PORT_ENABLE (0x1<<2) +#define PORTSC_PORT_EN_DIS_CHANGE (0x1<<3) +#define PORTSC_OVER_CURRENT_ACT (0x1<<4) +#define PORTSC_OVER_CUURENT_CHG (0x1<<5) +#define PORTSC_PORT_FORCE_RESUME (0x1<<6) +#define PORTSC_PORT_SUSPEND (0x1<<7) +#define PORTSC_PORT_RESET (0x1<<8) +#define PORTSC_LINE_STATUS_BITS (0x3<<10) +#define PORTSC_PORT_POWER (0x1<<12) +#define PORTSC_PORT_INDICTOR_CTRL (0x3<<14) +#define PORTSC_PORT_TEST_CTRL (0xF<<16) +#define PORTSC_WAKE_ON_CONNECT_EN (0x1<<20) +#define PORTSC_WAKE_ON_CONNECT_DIS (0x1<<21) +#define PORTSC_WAKE_ON_OVER_CURRENT (0x1<<22) +#define PORTSC_PHY_LOW_POWER_SPD (0x1<<23) +#define PORTSC_PORT_FORCE_FULL_SPEED (0x1<<24) +#define PORTSC_PORT_SPEED_MASK (0x3<<26) +#define PORTSC_TRANSCEIVER_WIDTH (0x1<<28) +#define PORTSC_PHY_TYPE_SEL (0x3<<30) +/* bit 11-10 are line status */ +#define PORTSC_LINE_STATUS_SE0 (0x0<<10) +#define PORTSC_LINE_STATUS_JSTATE (0x1<<10) +#define PORTSC_LINE_STATUS_KSTATE (0x2<<10) +#define PORTSC_LINE_STATUS_UNDEF (0x3<<10) +#define PORTSC_LINE_STATUS_BIT_POS (10) + +/* bit 15-14 are port indicator control */ +#define PORTSC_PIC_OFF (0x0<<14) +#define PORTSC_PIC_AMBER (0x1<<14) +#define PORTSC_PIC_GREEN (0x2<<14) +#define PORTSC_PIC_UNDEF (0x3<<14) +#define PORTSC_PIC_BIT_POS (14) + +/* bit 19-16 are port test control */ +#define PORTSC_PTC_DISABLE (0x0<<16) +#define PORTSC_PTC_JSTATE (0x1<<16) +#define PORTSC_PTC_KSTATE (0x2<<16) +#define PORTSC_PTC_SEQNAK (0x3<<16) +#define PORTSC_PTC_PACKET (0x4<<16) +#define PORTSC_PTC_FORCE_EN (0x5<<16) +#define PORTSC_PTC_BIT_POS (16) + +/* bit 27-26 are port speed */ +#define PORTSC_PORT_SPEED_FULL (0x0<<26) +#define PORTSC_PORT_SPEED_LOW (0x1<<26) +#define PORTSC_PORT_SPEED_HIGH (0x2<<26) +#define PORTSC_PORT_SPEED_UNDEF (0x3<<26) +#define PORTSC_SPEED_BIT_POS (26) + +/* bit 28 is parallel transceiver width for UTMI interface */ +#define PORTSC_PTW (0x1<<28) +#define PORTSC_PTW_8BIT (0x0<<28) +#define PORTSC_PTW_16BIT (0x1<<28) + +/* bit 31-30 are port transceiver select */ +#define PORTSC_PTS_UTMI (0x0<<30) +#define PORTSC_PTS_ULPI (0x2<<30) +#define PORTSC_PTS_FSLS_SERIAL (0x3<<30) +#define PORTSC_PTS_BIT_POS (30) + +#define PORTSC_W1C_BITS \ + (PORTSC_CONNECT_STATUS_CHANGE | \ + PORTSC_PORT_EN_DIS_CHANGE | \ + PORTSC_OVER_CUURENT_CHG) + +/* OTG Status Control Register Bit Masks */ +#define OTGSC_CTRL_VBUS_DISCHARGE (0x1<<0) +#define OTGSC_CTRL_VBUS_CHARGE (0x1<<1) +#define OTGSC_CTRL_OTG_TERMINATION (0x1<<3) +#define OTGSC_CTRL_DATA_PULSING (0x1<<4) +#define OTGSC_CTRL_ID_PULL_EN (0x1<<5) +#define OTGSC_HA_DATA_PULSE (0x1<<6) +#define OTGSC_HA_BA (0x1<<7) +#define OTGSC_STS_USB_ID (0x1<<8) +#define OTGSC_STS_A_VBUS_VALID (0x1<<9) +#define OTGSC_STS_A_SESSION_VALID (0x1<<10) +#define OTGSC_STS_B_SESSION_VALID (0x1<<11) +#define OTGSC_STS_B_SESSION_END (0x1<<12) +#define OTGSC_STS_1MS_TOGGLE (0x1<<13) +#define OTGSC_STS_DATA_PULSING (0x1<<14) +#define OTGSC_INTSTS_USB_ID (0x1<<16) +#define OTGSC_INTSTS_A_VBUS_VALID (0x1<<17) +#define OTGSC_INTSTS_A_SESSION_VALID (0x1<<18) +#define OTGSC_INTSTS_B_SESSION_VALID (0x1<<19) +#define OTGSC_INTSTS_B_SESSION_END (0x1<<20) +#define OTGSC_INTSTS_1MS (0x1<<21) +#define OTGSC_INTSTS_DATA_PULSING (0x1<<22) +#define OTGSC_INTR_USB_ID_EN (0x1<<24) +#define OTGSC_INTR_A_VBUS_VALID_EN (0x1<<25) +#define OTGSC_INTR_A_SESSION_VALID_EN (0x1<<26) +#define OTGSC_INTR_B_SESSION_VALID_EN (0x1<<27) +#define OTGSC_INTR_B_SESSION_END_EN (0x1<<28) +#define OTGSC_INTR_1MS_TIMER_EN (0x1<<29) +#define OTGSC_INTR_DATA_PULSING_EN (0x1<<30) +#define OTGSC_INTSTS_MASK (0x00ff0000) + +/* USB MODE Register Bit Masks */ +#define USB_MODE_CTRL_MODE_IDLE (0x0<<0) +#define USB_MODE_CTRL_MODE_DEVICE (0x2<<0) +#define USB_MODE_CTRL_MODE_HOST (0x3<<0) +#define USB_MODE_CTRL_MODE_RSV (0x1<<0) +#define USB_MODE_SETUP_LOCK_OFF (0x1<<3) +#define USB_MODE_STREAM_DISABLE (0x1<<4) +#define USB_MODE_ES (0x1<<2) /* Endian Select */ + +/* control Register Bit Masks */ +#define USB_CTRL_IOENB (0x1<<2) +#define USB_CTRL_ULPI_INT0EN (0x1<<0) + +/* BCSR5 */ +#define BCSR5_INT_USB (0x02) + +/* USB module clk cfg */ +#define SCCR_OFFS (0xA08) +#define SCCR_USB_CLK_DISABLE (0x00000000) /* USB clk disable */ +#define SCCR_USB_MPHCM_11 (0x00c00000) +#define SCCR_USB_MPHCM_01 (0x00400000) +#define SCCR_USB_MPHCM_10 (0x00800000) +#define SCCR_USB_DRCM_11 (0x00300000) +#define SCCR_USB_DRCM_01 (0x00100000) +#define SCCR_USB_DRCM_10 (0x00200000) + +#define SICRL_OFFS (0x114) +#define SICRL_USB0 (0x40000000) +#define SICRL_USB1 (0x20000000) + +#define SICRH_OFFS (0x118) +#define SICRH_USB_UTMI (0x00020000) + +/* OTG interrupt enable bit masks */ +#define OTGSC_INTERRUPT_ENABLE_BITS_MASK \ + (OTGSC_INTR_USB_ID_EN | \ + OTGSC_INTR_1MS_TIMER_EN | \ + OTGSC_INTR_A_VBUS_VALID_EN | \ + OTGSC_INTR_A_SESSION_VALID_EN | \ + OTGSC_INTR_B_SESSION_VALID_EN | \ + OTGSC_INTR_B_SESSION_END_EN | \ + OTGSC_INTR_DATA_PULSING_EN) + +/* OTG interrupt status bit masks */ +#define OTGSC_INTERRUPT_STATUS_BITS_MASK \ + (OTGSC_INTSTS_USB_ID | \ + OTGSC_INTR_1MS_TIMER_EN | \ + OTGSC_INTSTS_A_VBUS_VALID | \ + OTGSC_INTSTS_A_SESSION_VALID | \ + OTGSC_INTSTS_B_SESSION_VALID | \ + OTGSC_INTSTS_B_SESSION_END | \ + OTGSC_INTSTS_DATA_PULSING) + +/* + * A-DEVICE timing constants + */ + +/* Wait for VBUS Rise */ +#define TA_WAIT_VRISE (100) /* a_wait_vrise 100 ms, section: 6.6.5.1 */ + +/* Wait for B-Connect */ +#define TA_WAIT_BCON (10000) /* a_wait_bcon > 1 sec, section: 6.6.5.2 + * This is only used to get out of + * OTG_STATE_A_WAIT_BCON state if there was + * no connection for these many milliseconds + */ + +/* A-Idle to B-Disconnect */ +/* It is necessary for this timer to be more than 750 ms because of a bug in OPT + * test 5.4 in which B OPT disconnects after 750 ms instead of 75ms as stated + * in the test description + */ +#define TA_AIDL_BDIS (5000) /* a_suspend minimum 200 ms, section: 6.6.5.3 */ + +/* B-Idle to A-Disconnect */ +#define TA_BIDL_ADIS (12) /* 3 to 200 ms */ + +/* B-device timing constants */ + + +/* Data-Line Pulse Time*/ +#define TB_DATA_PLS (10) /* b_srp_init,continue 5~10ms, section:5.3.3 */ +#define TB_DATA_PLS_MIN (5) /* minimum 5 ms */ +#define TB_DATA_PLS_MAX (10) /* maximum 10 ms */ + +/* SRP Initiate Time */ +#define TB_SRP_INIT (100) /* b_srp_init,maximum 100 ms, section:5.3.8 */ + +/* SRP Fail Time */ +#define TB_SRP_FAIL (7000) /* b_srp_init,Fail time 5~30s, section:6.8.2.2*/ + +/* SRP result wait time */ +#define TB_SRP_WAIT (60) + +/* VBus time */ +#define TB_VBUS_PLS (30) /* time to keep vbus pulsing asserted */ + +/* Discharge time */ +/* This time should be less than 10ms. It varies from system to system. */ +#define TB_VBUS_DSCHRG (8) + +/* A-SE0 to B-Reset */ +#define TB_ASE0_BRST (20) /* b_wait_acon, mini 3.125 ms,section:6.8.2.4 */ + +/* A bus suspend timer before we can switch to b_wait_aconn */ +#define TB_A_SUSPEND (7) +#define TB_BUS_RESUME (12) + +/* SE0 Time Before SRP */ +#define TB_SE0_SRP (2) /* b_idle,minimum 2 ms, section:5.3.2 */ + +#define SET_OTG_STATE(otg_ptr, newstate) ((otg_ptr)->state = newstate) + +struct usb_dr_mmap { + /* Capability register */ + u8 res1[256]; + u16 caplength; /* Capability Register Length */ + u16 hciversion; /* Host Controller Interface Version */ + u32 hcsparams; /* Host Controller Structual Parameters */ + u32 hccparams; /* Host Controller Capability Parameters */ + u8 res2[20]; + u32 dciversion; /* Device Controller Interface Version */ + u32 dccparams; /* Device Controller Capability Parameters */ + u8 res3[24]; + /* Operation register */ + u32 usbcmd; /* USB Command Register */ + u32 usbsts; /* USB Status Register */ + u32 usbintr; /* USB Interrupt Enable Register */ + u32 frindex; /* Frame Index Register */ + u8 res4[4]; + u32 deviceaddr; /* Device Address */ + u32 endpointlistaddr; /* Endpoint List Address Register */ + u8 res5[4]; + u32 burstsize; /* Master Interface Data Burst Size Register */ + u32 txttfilltuning; /* Transmit FIFO Tuning Controls Register */ + u8 res6[8]; + u32 ulpiview; /* ULPI register access */ + u8 res7[12]; + u32 configflag; /* Configure Flag Register */ + u32 portsc; /* Port 1 Status and Control Register */ + u8 res8[28]; + u32 otgsc; /* On-The-Go Status and Control */ + u32 usbmode; /* USB Mode Register */ + u32 endptsetupstat; /* Endpoint Setup Status Register */ + u32 endpointprime; /* Endpoint Initialization Register */ + u32 endptflush; /* Endpoint Flush Register */ + u32 endptstatus; /* Endpoint Status Register */ + u32 endptcomplete; /* Endpoint Complete Register */ + u32 endptctrl[6]; /* Endpoint Control Registers */ + u8 res9[552]; + u32 snoop1; + u32 snoop2; + u32 age_cnt_thresh; /* Age Count Threshold Register */ + u32 pri_ctrl; /* Priority Control Register */ + u32 si_ctrl; /* System Interface Control Register */ + u8 res10[236]; + u32 control; /* General Purpose Control Register */ +}; + +struct fsl_otg_timer { + unsigned long expires; /* Number of count increase to timeout */ + unsigned long count; /* Tick counter */ + void (*function)(unsigned long); /* Timeout function */ + unsigned long data; /* Data passed to function */ + struct list_head list; +}; + +inline struct fsl_otg_timer *otg_timer_initializer +(void (*function)(unsigned long), unsigned long expires, unsigned long data) +{ + struct fsl_otg_timer *timer; + + timer = kmalloc(sizeof(struct fsl_otg_timer), GFP_KERNEL); + if (!timer) + return NULL; + timer->function = function; + timer->expires = expires; + timer->data = data; + return timer; +} + +struct fsl_otg { + struct otg_transceiver otg; + struct otg_fsm fsm; + struct usb_dr_mmap *dr_mem_map; + struct delayed_work otg_event; + + /* used for usb host */ + struct work_struct work_wq; + u8 host_working; + + int irq; +}; + +struct fsl_otg_config { + u8 otg_port; +}; + +/* For SRP and HNP handle */ +#define FSL_OTG_MAJOR 240 +#define FSL_OTG_NAME "fsl-usb2-otg" +/* Command to OTG driver ioctl */ +#define OTG_IOCTL_MAGIC FSL_OTG_MAJOR +/* if otg work as host, it should return 1, otherwise return 0 */ +#define GET_OTG_STATUS _IOR(OTG_IOCTL_MAGIC, 1, int) +#define SET_A_SUSPEND_REQ _IOW(OTG_IOCTL_MAGIC, 2, int) +#define SET_A_BUS_DROP _IOW(OTG_IOCTL_MAGIC, 3, int) +#define SET_A_BUS_REQ _IOW(OTG_IOCTL_MAGIC, 4, int) +#define SET_B_BUS_REQ _IOW(OTG_IOCTL_MAGIC, 5, int) +#define GET_A_SUSPEND_REQ _IOR(OTG_IOCTL_MAGIC, 6, int) +#define GET_A_BUS_DROP _IOR(OTG_IOCTL_MAGIC, 7, int) +#define GET_A_BUS_REQ _IOR(OTG_IOCTL_MAGIC, 8, int) +#define GET_B_BUS_REQ _IOR(OTG_IOCTL_MAGIC, 9, int) + +void fsl_otg_add_timer(void *timer); +void fsl_otg_del_timer(void *timer); +void fsl_otg_pulse_vbus(void); diff --git a/drivers/usb/otg/gpio_vbus.c b/drivers/usb/otg/gpio_vbus.c index 221c444..52733d9 100644 --- a/drivers/usb/otg/gpio_vbus.c +++ b/drivers/usb/otg/gpio_vbus.c @@ -279,6 +279,13 @@ static int __init gpio_vbus_probe(struct platform_device *pdev) } INIT_WORK(&gpio_vbus->work, gpio_vbus_work); + gpio_vbus->vbus_draw = regulator_get(&pdev->dev, "vbus_draw"); + if (IS_ERR(gpio_vbus->vbus_draw)) { + dev_dbg(&pdev->dev, "can't get vbus_draw regulator, err: %ld\n", + PTR_ERR(gpio_vbus->vbus_draw)); + gpio_vbus->vbus_draw = NULL; + } + /* only active when a gadget is registered */ err = otg_set_transceiver(&gpio_vbus->otg); if (err) { @@ -287,13 +294,6 @@ static int __init gpio_vbus_probe(struct platform_device *pdev) goto err_otg; } - gpio_vbus->vbus_draw = regulator_get(&pdev->dev, "vbus_draw"); - if (IS_ERR(gpio_vbus->vbus_draw)) { - dev_dbg(&pdev->dev, "can't get vbus_draw regulator, err: %ld\n", - PTR_ERR(gpio_vbus->vbus_draw)); - gpio_vbus->vbus_draw = NULL; - } - return 0; err_otg: free_irq(irq, &pdev->dev); diff --git a/drivers/usb/otg/isp1301_omap.c b/drivers/usb/otg/isp1301_omap.c index e25700f..8c28225 100644 --- a/drivers/usb/otg/isp1301_omap.c +++ b/drivers/usb/otg/isp1301_omap.c @@ -234,29 +234,9 @@ isp1301_clear_bits(struct isp1301 *isp, u8 reg, u8 bits) /*-------------------------------------------------------------------------*/ -static const char *state_string(enum usb_otg_state state) -{ - switch (state) { - case OTG_STATE_A_IDLE: return "a_idle"; - case OTG_STATE_A_WAIT_VRISE: return "a_wait_vrise"; - case OTG_STATE_A_WAIT_BCON: return "a_wait_bcon"; - case OTG_STATE_A_HOST: return "a_host"; - case OTG_STATE_A_SUSPEND: return "a_suspend"; - case OTG_STATE_A_PERIPHERAL: return "a_peripheral"; - case OTG_STATE_A_WAIT_VFALL: return "a_wait_vfall"; - case OTG_STATE_A_VBUS_ERR: return "a_vbus_err"; - case OTG_STATE_B_IDLE: return "b_idle"; - case OTG_STATE_B_SRP_INIT: return "b_srp_init"; - case OTG_STATE_B_PERIPHERAL: return "b_peripheral"; - case OTG_STATE_B_WAIT_ACON: return "b_wait_acon"; - case OTG_STATE_B_HOST: return "b_host"; - default: return "UNDEFINED"; - } -} - static inline const char *state_name(struct isp1301 *isp) { - return state_string(isp->otg.state); + return otg_state_string(isp->otg.state); } /*-------------------------------------------------------------------------*/ @@ -501,7 +481,7 @@ static void check_state(struct isp1301 *isp, const char *tag) if (isp->otg.state == state && !extra) return; pr_debug("otg: %s FSM %s/%02x, %s, %06x\n", tag, - state_string(state), fsm, state_name(isp), + otg_state_string(state), fsm, state_name(isp), omap_readl(OTG_CTRL)); } @@ -1095,7 +1075,7 @@ static void isp_update_otg(struct isp1301 *isp, u8 stat) if (state != isp->otg.state) pr_debug(" isp, %s -> %s\n", - state_string(state), state_name(isp)); + otg_state_string(state), state_name(isp)); #ifdef CONFIG_USB_OTG /* update the OTG controller state to match the isp1301; may diff --git a/drivers/usb/otg/langwell_otg.c b/drivers/usb/otg/langwell_otg.c index e973ff1..f08f784 100644 --- a/drivers/usb/otg/langwell_otg.c +++ b/drivers/usb/otg/langwell_otg.c @@ -82,40 +82,6 @@ static struct pci_driver otg_pci_driver = { .resume = langwell_otg_resume, }; -static const char *state_string(enum usb_otg_state state) -{ - switch (state) { - case OTG_STATE_A_IDLE: - return "a_idle"; - case OTG_STATE_A_WAIT_VRISE: - return "a_wait_vrise"; - case OTG_STATE_A_WAIT_BCON: - return "a_wait_bcon"; - case OTG_STATE_A_HOST: - return "a_host"; - case OTG_STATE_A_SUSPEND: - return "a_suspend"; - case OTG_STATE_A_PERIPHERAL: - return "a_peripheral"; - case OTG_STATE_A_WAIT_VFALL: - return "a_wait_vfall"; - case OTG_STATE_A_VBUS_ERR: - return "a_vbus_err"; - case OTG_STATE_B_IDLE: - return "b_idle"; - case OTG_STATE_B_SRP_INIT: - return "b_srp_init"; - case OTG_STATE_B_PERIPHERAL: - return "b_peripheral"; - case OTG_STATE_B_WAIT_ACON: - return "b_wait_acon"; - case OTG_STATE_B_HOST: - return "b_host"; - default: - return "UNDEFINED"; - } -} - /* HSM timers */ static inline struct langwell_otg_timer *otg_timer_initializer (void (*function)(unsigned long), unsigned long expires, unsigned long data) @@ -968,7 +934,7 @@ static void langwell_otg_work(struct work_struct *work) pdev = to_pci_dev(lnw->dev); dev_dbg(lnw->dev, "%s: old state = %s\n", __func__, - state_string(iotg->otg.state)); + otg_state_string(iotg->otg.state)); switch (iotg->otg.state) { case OTG_STATE_UNDEFINED: @@ -1703,7 +1669,7 @@ static void langwell_otg_work(struct work_struct *work) } dev_dbg(lnw->dev, "%s: new state = %s\n", __func__, - state_string(iotg->otg.state)); + otg_state_string(iotg->otg.state)); } static ssize_t @@ -1789,7 +1755,7 @@ show_hsm(struct device *_dev, struct device_attribute *attr, char *buf) "b_bus_req = \t%d\n" "b_bus_suspend_tmout = \t%d\n" "b_bus_suspend_vld = \t%d\n", - state_string(iotg->otg.state), + otg_state_string(iotg->otg.state), iotg->hsm.a_bus_resume, iotg->hsm.a_bus_suspend, iotg->hsm.a_conn, diff --git a/drivers/usb/otg/msm_otg.c b/drivers/usb/otg/msm_otg.c index 2965986..b276f8f 100644 --- a/drivers/usb/otg/msm_otg.c +++ b/drivers/usb/otg/msm_otg.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. +/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -38,6 +38,7 @@ #include <linux/usb/hcd.h> #include <linux/usb/msm_hsusb.h> #include <linux/usb/msm_hsusb_hw.h> +#include <linux/regulator/consumer.h> #include <mach/clk.h> @@ -45,6 +46,195 @@ #define DRIVER_NAME "msm_otg" #define ULPI_IO_TIMEOUT_USEC (10 * 1000) + +#define USB_PHY_3P3_VOL_MIN 3050000 /* uV */ +#define USB_PHY_3P3_VOL_MAX 3300000 /* uV */ +#define USB_PHY_3P3_HPM_LOAD 50000 /* uA */ +#define USB_PHY_3P3_LPM_LOAD 4000 /* uA */ + +#define USB_PHY_1P8_VOL_MIN 1800000 /* uV */ +#define USB_PHY_1P8_VOL_MAX 1800000 /* uV */ +#define USB_PHY_1P8_HPM_LOAD 50000 /* uA */ +#define USB_PHY_1P8_LPM_LOAD 4000 /* uA */ + +#define USB_PHY_VDD_DIG_VOL_MIN 1000000 /* uV */ +#define USB_PHY_VDD_DIG_VOL_MAX 1320000 /* uV */ + +static struct regulator *hsusb_3p3; +static struct regulator *hsusb_1p8; +static struct regulator *hsusb_vddcx; + +static int msm_hsusb_init_vddcx(struct msm_otg *motg, int init) +{ + int ret = 0; + + if (init) { + hsusb_vddcx = regulator_get(motg->otg.dev, "HSUSB_VDDCX"); + if (IS_ERR(hsusb_vddcx)) { + dev_err(motg->otg.dev, "unable to get hsusb vddcx\n"); + return PTR_ERR(hsusb_vddcx); + } + + ret = regulator_set_voltage(hsusb_vddcx, + USB_PHY_VDD_DIG_VOL_MIN, + USB_PHY_VDD_DIG_VOL_MAX); + if (ret) { + dev_err(motg->otg.dev, "unable to set the voltage " + "for hsusb vddcx\n"); + regulator_put(hsusb_vddcx); + return ret; + } + + ret = regulator_enable(hsusb_vddcx); + if (ret) { + dev_err(motg->otg.dev, "unable to enable hsusb vddcx\n"); + regulator_put(hsusb_vddcx); + } + } else { + ret = regulator_set_voltage(hsusb_vddcx, 0, + USB_PHY_VDD_DIG_VOL_MAX); + if (ret) + dev_err(motg->otg.dev, "unable to set the voltage " + "for hsusb vddcx\n"); + ret = regulator_disable(hsusb_vddcx); + if (ret) + dev_err(motg->otg.dev, "unable to disable hsusb vddcx\n"); + + regulator_put(hsusb_vddcx); + } + + return ret; +} + +static int msm_hsusb_ldo_init(struct msm_otg *motg, int init) +{ + int rc = 0; + + if (init) { + hsusb_3p3 = regulator_get(motg->otg.dev, "HSUSB_3p3"); + if (IS_ERR(hsusb_3p3)) { + dev_err(motg->otg.dev, "unable to get hsusb 3p3\n"); + return PTR_ERR(hsusb_3p3); + } + + rc = regulator_set_voltage(hsusb_3p3, USB_PHY_3P3_VOL_MIN, + USB_PHY_3P3_VOL_MAX); + if (rc) { + dev_err(motg->otg.dev, "unable to set voltage level " + "for hsusb 3p3\n"); + goto put_3p3; + } + rc = regulator_enable(hsusb_3p3); + if (rc) { + dev_err(motg->otg.dev, "unable to enable the hsusb 3p3\n"); + goto put_3p3; + } + hsusb_1p8 = regulator_get(motg->otg.dev, "HSUSB_1p8"); + if (IS_ERR(hsusb_1p8)) { + dev_err(motg->otg.dev, "unable to get hsusb 1p8\n"); + rc = PTR_ERR(hsusb_1p8); + goto disable_3p3; + } + rc = regulator_set_voltage(hsusb_1p8, USB_PHY_1P8_VOL_MIN, + USB_PHY_1P8_VOL_MAX); + if (rc) { + dev_err(motg->otg.dev, "unable to set voltage level " + "for hsusb 1p8\n"); + goto put_1p8; + } + rc = regulator_enable(hsusb_1p8); + if (rc) { + dev_err(motg->otg.dev, "unable to enable the hsusb 1p8\n"); + goto put_1p8; + } + + return 0; + } + + regulator_disable(hsusb_1p8); +put_1p8: + regulator_put(hsusb_1p8); +disable_3p3: + regulator_disable(hsusb_3p3); +put_3p3: + regulator_put(hsusb_3p3); + return rc; +} + +#ifdef CONFIG_PM_SLEEP +#define USB_PHY_SUSP_DIG_VOL 500000 +static int msm_hsusb_config_vddcx(int high) +{ + int max_vol = USB_PHY_VDD_DIG_VOL_MAX; + int min_vol; + int ret; + + if (high) + min_vol = USB_PHY_VDD_DIG_VOL_MIN; + else + min_vol = USB_PHY_SUSP_DIG_VOL; + + ret = regulator_set_voltage(hsusb_vddcx, min_vol, max_vol); + if (ret) { + pr_err("%s: unable to set the voltage for regulator " + "HSUSB_VDDCX\n", __func__); + return ret; + } + + pr_debug("%s: min_vol:%d max_vol:%d\n", __func__, min_vol, max_vol); + + return ret; +} +#endif + +static int msm_hsusb_ldo_set_mode(int on) +{ + int ret = 0; + + if (!hsusb_1p8 || IS_ERR(hsusb_1p8)) { + pr_err("%s: HSUSB_1p8 is not initialized\n", __func__); + return -ENODEV; + } + + if (!hsusb_3p3 || IS_ERR(hsusb_3p3)) { + pr_err("%s: HSUSB_3p3 is not initialized\n", __func__); + return -ENODEV; + } + + if (on) { + ret = regulator_set_optimum_mode(hsusb_1p8, + USB_PHY_1P8_HPM_LOAD); + if (ret < 0) { + pr_err("%s: Unable to set HPM of the regulator " + "HSUSB_1p8\n", __func__); + return ret; + } + ret = regulator_set_optimum_mode(hsusb_3p3, + USB_PHY_3P3_HPM_LOAD); + if (ret < 0) { + pr_err("%s: Unable to set HPM of the regulator " + "HSUSB_3p3\n", __func__); + regulator_set_optimum_mode(hsusb_1p8, + USB_PHY_1P8_LPM_LOAD); + return ret; + } + } else { + ret = regulator_set_optimum_mode(hsusb_1p8, + USB_PHY_1P8_LPM_LOAD); + if (ret < 0) + pr_err("%s: Unable to set LPM of the regulator " + "HSUSB_1p8\n", __func__); + ret = regulator_set_optimum_mode(hsusb_3p3, + USB_PHY_3P3_LPM_LOAD); + if (ret < 0) + pr_err("%s: Unable to set LPM of the regulator " + "HSUSB_3p3\n", __func__); + } + + pr_debug("reg (%s)\n", on ? "HPM" : "LPM"); + return ret < 0 ? ret : 0; +} + static int ulpi_read(struct otg_transceiver *otg, u32 reg) { struct msm_otg *motg = container_of(otg, struct msm_otg, otg); @@ -268,27 +458,28 @@ static int msm_otg_suspend(struct msm_otg *motg) disable_irq(motg->irq); /* + * Chipidea 45-nm PHY suspend sequence: + * * Interrupt Latch Register auto-clear feature is not present * in all PHY versions. Latch register is clear on read type. * Clear latch register to avoid spurious wakeup from * low power mode (LPM). - */ - ulpi_read(otg, 0x14); - - /* + * * PHY comparators are disabled when PHY enters into low power * mode (LPM). Keep PHY comparators ON in LPM only when we expect * VBUS/Id notifications from USB PHY. Otherwise turn off USB * PHY comparators. This save significant amount of power. - */ - if (pdata->otg_control == OTG_PHY_CONTROL) - ulpi_write(otg, 0x01, 0x30); - - /* + * * PLL is not turned off when PHY enters into low power mode (LPM). * Disable PLL for maximum power savings. */ - ulpi_write(otg, 0x08, 0x09); + + if (motg->pdata->phy_type == CI_45NM_INTEGRATED_PHY) { + ulpi_read(otg, 0x14); + if (pdata->otg_control == OTG_PHY_CONTROL) + ulpi_write(otg, 0x01, 0x30); + ulpi_write(otg, 0x08, 0x09); + } /* * PHY may take some time or even fail to enter into low power @@ -319,11 +510,24 @@ static int msm_otg_suspend(struct msm_otg *motg) */ writel(readl(USB_USBCMD) | ASYNC_INTR_CTRL | ULPI_STP_CTRL, USB_USBCMD); + if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && + motg->pdata->otg_control == OTG_PMIC_CONTROL) + writel(readl(USB_PHY_CTRL) | PHY_RETEN, USB_PHY_CTRL); + clk_disable(motg->pclk); clk_disable(motg->clk); if (motg->core_clk) clk_disable(motg->core_clk); + if (!IS_ERR(motg->pclk_src)) + clk_disable(motg->pclk_src); + + if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && + motg->pdata->otg_control == OTG_PMIC_CONTROL) { + msm_hsusb_ldo_set_mode(0); + msm_hsusb_config_vddcx(0); + } + if (device_may_wakeup(otg->dev)) enable_irq_wake(motg->irq); if (bus) @@ -347,11 +551,21 @@ static int msm_otg_resume(struct msm_otg *motg) if (!atomic_read(&motg->in_lpm)) return 0; + if (!IS_ERR(motg->pclk_src)) + clk_enable(motg->pclk_src); + clk_enable(motg->pclk); clk_enable(motg->clk); if (motg->core_clk) clk_enable(motg->core_clk); + if (motg->pdata->phy_type == SNPS_28NM_INTEGRATED_PHY && + motg->pdata->otg_control == OTG_PMIC_CONTROL) { + msm_hsusb_ldo_set_mode(1); + msm_hsusb_config_vddcx(1); + writel(readl(USB_PHY_CTRL) & ~PHY_RETEN, USB_PHY_CTRL); + } + temp = readl(USB_USBCMD); temp &= ~ASYNC_INTR_CTRL; temp &= ~ULPI_STP_CTRL; @@ -389,20 +603,47 @@ skip_phy_resume: if (bus) set_bit(HCD_FLAG_HW_ACCESSIBLE, &(bus_to_hcd(bus))->flags); + atomic_set(&motg->in_lpm, 0); + if (motg->async_int) { motg->async_int = 0; pm_runtime_put(otg->dev); enable_irq(motg->irq); } - atomic_set(&motg->in_lpm, 0); - dev_info(otg->dev, "USB exited from low power mode\n"); return 0; } #endif +static void msm_otg_notify_charger(struct msm_otg *motg, unsigned mA) +{ + if (motg->cur_power == mA) + return; + + /* TODO: Notify PMIC about available current */ + dev_info(motg->otg.dev, "Avail curr from USB = %u\n", mA); + motg->cur_power = mA; +} + +static int msm_otg_set_power(struct otg_transceiver *otg, unsigned mA) +{ + struct msm_otg *motg = container_of(otg, struct msm_otg, otg); + + /* + * Gadget driver uses set_power method to notify about the + * available current based on suspend/configured states. + * + * IDEV_CHG can be drawn irrespective of suspend/un-configured + * states when CDP/ACA is connected. + */ + if (motg->chg_type == USB_SDP_CHARGER) + msm_otg_notify_charger(motg, mA); + + return 0; +} + static void msm_otg_start_host(struct otg_transceiver *otg, int on) { struct msm_otg *motg = container_of(otg, struct msm_otg, otg); @@ -557,6 +798,306 @@ static int msm_otg_set_peripheral(struct otg_transceiver *otg, return 0; } +static bool msm_chg_check_secondary_det(struct msm_otg *motg) +{ + struct otg_transceiver *otg = &motg->otg; + u32 chg_det; + bool ret = false; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(otg, 0x34); + ret = chg_det & (1 << 4); + break; + case SNPS_28NM_INTEGRATED_PHY: + chg_det = ulpi_read(otg, 0x87); + ret = chg_det & 1; + break; + default: + break; + } + return ret; +} + +static void msm_chg_enable_secondary_det(struct msm_otg *motg) +{ + struct otg_transceiver *otg = &motg->otg; + u32 chg_det; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(otg, 0x34); + /* Turn off charger block */ + chg_det |= ~(1 << 1); + ulpi_write(otg, chg_det, 0x34); + udelay(20); + /* control chg block via ULPI */ + chg_det &= ~(1 << 3); + ulpi_write(otg, chg_det, 0x34); + /* put it in host mode for enabling D- source */ + chg_det &= ~(1 << 2); + ulpi_write(otg, chg_det, 0x34); + /* Turn on chg detect block */ + chg_det &= ~(1 << 1); + ulpi_write(otg, chg_det, 0x34); + udelay(20); + /* enable chg detection */ + chg_det &= ~(1 << 0); + ulpi_write(otg, chg_det, 0x34); + break; + case SNPS_28NM_INTEGRATED_PHY: + /* + * Configure DM as current source, DP as current sink + * and enable battery charging comparators. + */ + ulpi_write(otg, 0x8, 0x85); + ulpi_write(otg, 0x2, 0x85); + ulpi_write(otg, 0x1, 0x85); + break; + default: + break; + } +} + +static bool msm_chg_check_primary_det(struct msm_otg *motg) +{ + struct otg_transceiver *otg = &motg->otg; + u32 chg_det; + bool ret = false; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(otg, 0x34); + ret = chg_det & (1 << 4); + break; + case SNPS_28NM_INTEGRATED_PHY: + chg_det = ulpi_read(otg, 0x87); + ret = chg_det & 1; + break; + default: + break; + } + return ret; +} + +static void msm_chg_enable_primary_det(struct msm_otg *motg) +{ + struct otg_transceiver *otg = &motg->otg; + u32 chg_det; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(otg, 0x34); + /* enable chg detection */ + chg_det &= ~(1 << 0); + ulpi_write(otg, chg_det, 0x34); + break; + case SNPS_28NM_INTEGRATED_PHY: + /* + * Configure DP as current source, DM as current sink + * and enable battery charging comparators. + */ + ulpi_write(otg, 0x2, 0x85); + ulpi_write(otg, 0x1, 0x85); + break; + default: + break; + } +} + +static bool msm_chg_check_dcd(struct msm_otg *motg) +{ + struct otg_transceiver *otg = &motg->otg; + u32 line_state; + bool ret = false; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + line_state = ulpi_read(otg, 0x15); + ret = !(line_state & 1); + break; + case SNPS_28NM_INTEGRATED_PHY: + line_state = ulpi_read(otg, 0x87); + ret = line_state & 2; + break; + default: + break; + } + return ret; +} + +static void msm_chg_disable_dcd(struct msm_otg *motg) +{ + struct otg_transceiver *otg = &motg->otg; + u32 chg_det; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(otg, 0x34); + chg_det &= ~(1 << 5); + ulpi_write(otg, chg_det, 0x34); + break; + case SNPS_28NM_INTEGRATED_PHY: + ulpi_write(otg, 0x10, 0x86); + break; + default: + break; + } +} + +static void msm_chg_enable_dcd(struct msm_otg *motg) +{ + struct otg_transceiver *otg = &motg->otg; + u32 chg_det; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(otg, 0x34); + /* Turn on D+ current source */ + chg_det |= (1 << 5); + ulpi_write(otg, chg_det, 0x34); + break; + case SNPS_28NM_INTEGRATED_PHY: + /* Data contact detection enable */ + ulpi_write(otg, 0x10, 0x85); + break; + default: + break; + } +} + +static void msm_chg_block_on(struct msm_otg *motg) +{ + struct otg_transceiver *otg = &motg->otg; + u32 func_ctrl, chg_det; + + /* put the controller in non-driving mode */ + func_ctrl = ulpi_read(otg, ULPI_FUNC_CTRL); + func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK; + func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING; + ulpi_write(otg, func_ctrl, ULPI_FUNC_CTRL); + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(otg, 0x34); + /* control chg block via ULPI */ + chg_det &= ~(1 << 3); + ulpi_write(otg, chg_det, 0x34); + /* Turn on chg detect block */ + chg_det &= ~(1 << 1); + ulpi_write(otg, chg_det, 0x34); + udelay(20); + break; + case SNPS_28NM_INTEGRATED_PHY: + /* Clear charger detecting control bits */ + ulpi_write(otg, 0x3F, 0x86); + /* Clear alt interrupt latch and enable bits */ + ulpi_write(otg, 0x1F, 0x92); + ulpi_write(otg, 0x1F, 0x95); + udelay(100); + break; + default: + break; + } +} + +static void msm_chg_block_off(struct msm_otg *motg) +{ + struct otg_transceiver *otg = &motg->otg; + u32 func_ctrl, chg_det; + + switch (motg->pdata->phy_type) { + case CI_45NM_INTEGRATED_PHY: + chg_det = ulpi_read(otg, 0x34); + /* Turn off charger block */ + chg_det |= ~(1 << 1); + ulpi_write(otg, chg_det, 0x34); + break; + case SNPS_28NM_INTEGRATED_PHY: + /* Clear charger detecting control bits */ + ulpi_write(otg, 0x3F, 0x86); + /* Clear alt interrupt latch and enable bits */ + ulpi_write(otg, 0x1F, 0x92); + ulpi_write(otg, 0x1F, 0x95); + break; + default: + break; + } + + /* put the controller in normal mode */ + func_ctrl = ulpi_read(otg, ULPI_FUNC_CTRL); + func_ctrl &= ~ULPI_FUNC_CTRL_OPMODE_MASK; + func_ctrl |= ULPI_FUNC_CTRL_OPMODE_NORMAL; + ulpi_write(otg, func_ctrl, ULPI_FUNC_CTRL); +} + +#define MSM_CHG_DCD_POLL_TIME (100 * HZ/1000) /* 100 msec */ +#define MSM_CHG_DCD_MAX_RETRIES 6 /* Tdcd_tmout = 6 * 100 msec */ +#define MSM_CHG_PRIMARY_DET_TIME (40 * HZ/1000) /* TVDPSRC_ON */ +#define MSM_CHG_SECONDARY_DET_TIME (40 * HZ/1000) /* TVDMSRC_ON */ +static void msm_chg_detect_work(struct work_struct *w) +{ + struct msm_otg *motg = container_of(w, struct msm_otg, chg_work.work); + struct otg_transceiver *otg = &motg->otg; + bool is_dcd, tmout, vout; + unsigned long delay; + + dev_dbg(otg->dev, "chg detection work\n"); + switch (motg->chg_state) { + case USB_CHG_STATE_UNDEFINED: + pm_runtime_get_sync(otg->dev); + msm_chg_block_on(motg); + msm_chg_enable_dcd(motg); + motg->chg_state = USB_CHG_STATE_WAIT_FOR_DCD; + motg->dcd_retries = 0; + delay = MSM_CHG_DCD_POLL_TIME; + break; + case USB_CHG_STATE_WAIT_FOR_DCD: + is_dcd = msm_chg_check_dcd(motg); + tmout = ++motg->dcd_retries == MSM_CHG_DCD_MAX_RETRIES; + if (is_dcd || tmout) { + msm_chg_disable_dcd(motg); + msm_chg_enable_primary_det(motg); + delay = MSM_CHG_PRIMARY_DET_TIME; + motg->chg_state = USB_CHG_STATE_DCD_DONE; + } else { + delay = MSM_CHG_DCD_POLL_TIME; + } + break; + case USB_CHG_STATE_DCD_DONE: + vout = msm_chg_check_primary_det(motg); + if (vout) { + msm_chg_enable_secondary_det(motg); + delay = MSM_CHG_SECONDARY_DET_TIME; + motg->chg_state = USB_CHG_STATE_PRIMARY_DONE; + } else { + motg->chg_type = USB_SDP_CHARGER; + motg->chg_state = USB_CHG_STATE_DETECTED; + delay = 0; + } + break; + case USB_CHG_STATE_PRIMARY_DONE: + vout = msm_chg_check_secondary_det(motg); + if (vout) + motg->chg_type = USB_DCP_CHARGER; + else + motg->chg_type = USB_CDP_CHARGER; + motg->chg_state = USB_CHG_STATE_SECONDARY_DONE; + /* fall through */ + case USB_CHG_STATE_SECONDARY_DONE: + motg->chg_state = USB_CHG_STATE_DETECTED; + case USB_CHG_STATE_DETECTED: + msm_chg_block_off(motg); + dev_dbg(otg->dev, "charger = %d\n", motg->chg_type); + schedule_work(&motg->sm_work); + return; + default: + return; + } + + schedule_delayed_work(&motg->chg_work, delay); +} + /* * We support OTG, Peripheral only and Host only configurations. In case * of OTG, mode switch (host-->peripheral/peripheral-->host) can happen @@ -627,9 +1168,48 @@ static void msm_otg_sm_work(struct work_struct *w) writel(readl(USB_OTGSC) & ~OTGSC_BSVIE, USB_OTGSC); msm_otg_start_host(otg, 1); otg->state = OTG_STATE_A_HOST; - } else if (test_bit(B_SESS_VLD, &motg->inputs) && otg->gadget) { - msm_otg_start_peripheral(otg, 1); - otg->state = OTG_STATE_B_PERIPHERAL; + } else if (test_bit(B_SESS_VLD, &motg->inputs)) { + switch (motg->chg_state) { + case USB_CHG_STATE_UNDEFINED: + msm_chg_detect_work(&motg->chg_work.work); + break; + case USB_CHG_STATE_DETECTED: + switch (motg->chg_type) { + case USB_DCP_CHARGER: + msm_otg_notify_charger(motg, + IDEV_CHG_MAX); + break; + case USB_CDP_CHARGER: + msm_otg_notify_charger(motg, + IDEV_CHG_MAX); + msm_otg_start_peripheral(otg, 1); + otg->state = OTG_STATE_B_PERIPHERAL; + break; + case USB_SDP_CHARGER: + msm_otg_notify_charger(motg, IUNIT); + msm_otg_start_peripheral(otg, 1); + otg->state = OTG_STATE_B_PERIPHERAL; + break; + default: + break; + } + break; + default: + break; + } + } else { + /* + * If charger detection work is pending, decrement + * the pm usage counter to balance with the one that + * is incremented in charger detection work. + */ + if (cancel_delayed_work_sync(&motg->chg_work)) { + pm_runtime_put_sync(otg->dev); + msm_otg_reset(otg); + } + msm_otg_notify_charger(motg, 0); + motg->chg_state = USB_CHG_STATE_UNDEFINED; + motg->chg_type = USB_INVALID_CHARGER; } pm_runtime_put_sync(otg->dev); break; @@ -637,7 +1217,10 @@ static void msm_otg_sm_work(struct work_struct *w) dev_dbg(otg->dev, "OTG_STATE_B_PERIPHERAL state\n"); if (!test_bit(B_SESS_VLD, &motg->inputs) || !test_bit(ID, &motg->inputs)) { + msm_otg_notify_charger(motg, 0); msm_otg_start_peripheral(otg, 0); + motg->chg_state = USB_CHG_STATE_UNDEFINED; + motg->chg_type = USB_INVALID_CHARGER; otg->state = OTG_STATE_B_IDLE; msm_otg_reset(otg); schedule_work(w); @@ -862,12 +1445,31 @@ static int __init msm_otg_probe(struct platform_device *pdev) ret = PTR_ERR(motg->clk); goto put_phy_reset_clk; } + clk_set_rate(motg->clk, 60000000); + + /* + * If USB Core is running its protocol engine based on CORE CLK, + * CORE CLK must be running at >55Mhz for correct HSUSB + * operation and USB core cannot tolerate frequency changes on + * CORE CLK. For such USB cores, vote for maximum clk frequency + * on pclk source + */ + if (motg->pdata->pclk_src_name) { + motg->pclk_src = clk_get(&pdev->dev, + motg->pdata->pclk_src_name); + if (IS_ERR(motg->pclk_src)) + goto put_clk; + clk_set_rate(motg->pclk_src, INT_MAX); + clk_enable(motg->pclk_src); + } else + motg->pclk_src = ERR_PTR(-ENOENT); + motg->pclk = clk_get(&pdev->dev, "usb_hs_pclk"); if (IS_ERR(motg->pclk)) { dev_err(&pdev->dev, "failed to get usb_hs_pclk\n"); ret = PTR_ERR(motg->pclk); - goto put_clk; + goto put_pclk_src; } /* @@ -903,6 +1505,24 @@ static int __init msm_otg_probe(struct platform_device *pdev) clk_enable(motg->clk); clk_enable(motg->pclk); + + ret = msm_hsusb_init_vddcx(motg, 1); + if (ret) { + dev_err(&pdev->dev, "hsusb vddcx configuration failed\n"); + goto free_regs; + } + + ret = msm_hsusb_ldo_init(motg, 1); + if (ret) { + dev_err(&pdev->dev, "hsusb vreg configuration failed\n"); + goto vddcx_exit; + } + ret = msm_hsusb_ldo_set_mode(1); + if (ret) { + dev_err(&pdev->dev, "hsusb vreg enable failed\n"); + goto ldo_exit; + } + if (motg->core_clk) clk_enable(motg->core_clk); @@ -910,6 +1530,7 @@ static int __init msm_otg_probe(struct platform_device *pdev) writel(0, USB_OTGSC); INIT_WORK(&motg->sm_work, msm_otg_sm_work); + INIT_DELAYED_WORK(&motg->chg_work, msm_chg_detect_work); ret = request_irq(motg->irq, msm_otg_irq, IRQF_SHARED, "msm_otg", motg); if (ret) { @@ -920,6 +1541,7 @@ static int __init msm_otg_probe(struct platform_device *pdev) otg->init = msm_otg_reset; otg->set_host = msm_otg_set_host; otg->set_peripheral = msm_otg_set_peripheral; + otg->set_power = msm_otg_set_power; otg->io_ops = &msm_otg_io_ops; @@ -949,12 +1571,21 @@ free_irq: disable_clks: clk_disable(motg->pclk); clk_disable(motg->clk); +ldo_exit: + msm_hsusb_ldo_init(motg, 0); +vddcx_exit: + msm_hsusb_init_vddcx(motg, 0); free_regs: iounmap(motg->regs); put_core_clk: if (motg->core_clk) clk_put(motg->core_clk); clk_put(motg->pclk); +put_pclk_src: + if (!IS_ERR(motg->pclk_src)) { + clk_disable(motg->pclk_src); + clk_put(motg->pclk_src); + } put_clk: clk_put(motg->clk); put_phy_reset_clk: @@ -974,6 +1605,7 @@ static int __devexit msm_otg_remove(struct platform_device *pdev) return -EBUSY; msm_otg_debugfs_cleanup(); + cancel_delayed_work_sync(&motg->chg_work); cancel_work_sync(&motg->sm_work); pm_runtime_resume(&pdev->dev); @@ -1004,6 +1636,11 @@ static int __devexit msm_otg_remove(struct platform_device *pdev) clk_disable(motg->clk); if (motg->core_clk) clk_disable(motg->core_clk); + if (!IS_ERR(motg->pclk_src)) { + clk_disable(motg->pclk_src); + clk_put(motg->pclk_src); + } + msm_hsusb_ldo_init(motg, 0); iounmap(motg->regs); pm_runtime_set_suspended(&pdev->dev); diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c index 0a43a7d..fb7adef 100644 --- a/drivers/usb/otg/otg.c +++ b/drivers/usb/otg/otg.c @@ -64,3 +64,38 @@ int otg_set_transceiver(struct otg_transceiver *x) return 0; } EXPORT_SYMBOL(otg_set_transceiver); + +const char *otg_state_string(enum usb_otg_state state) +{ + switch (state) { + case OTG_STATE_A_IDLE: + return "a_idle"; + case OTG_STATE_A_WAIT_VRISE: + return "a_wait_vrise"; + case OTG_STATE_A_WAIT_BCON: + return "a_wait_bcon"; + case OTG_STATE_A_HOST: + return "a_host"; + case OTG_STATE_A_SUSPEND: + return "a_suspend"; + case OTG_STATE_A_PERIPHERAL: + return "a_peripheral"; + case OTG_STATE_A_WAIT_VFALL: + return "a_wait_vfall"; + case OTG_STATE_A_VBUS_ERR: + return "a_vbus_err"; + case OTG_STATE_B_IDLE: + return "b_idle"; + case OTG_STATE_B_SRP_INIT: + return "b_srp_init"; + case OTG_STATE_B_PERIPHERAL: + return "b_peripheral"; + case OTG_STATE_B_WAIT_ACON: + return "b_wait_acon"; + case OTG_STATE_B_HOST: + return "b_host"; + default: + return "UNDEFINED"; + } +} +EXPORT_SYMBOL(otg_state_string); diff --git a/drivers/usb/otg/otg_fsm.c b/drivers/usb/otg/otg_fsm.c new file mode 100644 index 0000000..b0cc422 --- /dev/null +++ b/drivers/usb/otg/otg_fsm.c @@ -0,0 +1,349 @@ +/* + * OTG Finite State Machine from OTG spec + * + * Copyright (C) 2007,2008 Freescale Semiconductor, Inc. + * + * Author: Li Yang <LeoLi@freescale.com> + * Jerry Huang <Chang-Ming.Huang@freescale.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/spinlock.h> +#include <linux/delay.h> +#include <linux/usb.h> +#include <linux/usb/gadget.h> +#include <linux/usb/otg.h> +#include <linux/types.h> + +#include "otg_fsm.h" + +/* Change USB protocol when there is a protocol change */ +static int otg_set_protocol(struct otg_fsm *fsm, int protocol) +{ + int ret = 0; + + if (fsm->protocol != protocol) { + VDBG("Changing role fsm->protocol= %d; new protocol= %d\n", + fsm->protocol, protocol); + /* stop old protocol */ + if (fsm->protocol == PROTO_HOST) + ret = fsm->ops->start_host(fsm, 0); + else if (fsm->protocol == PROTO_GADGET) + ret = fsm->ops->start_gadget(fsm, 0); + if (ret) + return ret; + + /* start new protocol */ + if (protocol == PROTO_HOST) + ret = fsm->ops->start_host(fsm, 1); + else if (protocol == PROTO_GADGET) + ret = fsm->ops->start_gadget(fsm, 1); + if (ret) + return ret; + + fsm->protocol = protocol; + return 0; + } + + return 0; +} + +static int state_changed; + +/* Called when leaving a state. Do state clean up jobs here */ +void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state) +{ + switch (old_state) { + case OTG_STATE_B_IDLE: + otg_del_timer(fsm, b_se0_srp_tmr); + fsm->b_se0_srp = 0; + break; + case OTG_STATE_B_SRP_INIT: + fsm->b_srp_done = 0; + break; + case OTG_STATE_B_PERIPHERAL: + break; + case OTG_STATE_B_WAIT_ACON: + otg_del_timer(fsm, b_ase0_brst_tmr); + fsm->b_ase0_brst_tmout = 0; + break; + case OTG_STATE_B_HOST: + break; + case OTG_STATE_A_IDLE: + break; + case OTG_STATE_A_WAIT_VRISE: + otg_del_timer(fsm, a_wait_vrise_tmr); + fsm->a_wait_vrise_tmout = 0; + break; + case OTG_STATE_A_WAIT_BCON: + otg_del_timer(fsm, a_wait_bcon_tmr); + fsm->a_wait_bcon_tmout = 0; + break; + case OTG_STATE_A_HOST: + otg_del_timer(fsm, a_wait_enum_tmr); + break; + case OTG_STATE_A_SUSPEND: + otg_del_timer(fsm, a_aidl_bdis_tmr); + fsm->a_aidl_bdis_tmout = 0; + fsm->a_suspend_req = 0; + break; + case OTG_STATE_A_PERIPHERAL: + break; + case OTG_STATE_A_WAIT_VFALL: + otg_del_timer(fsm, a_wait_vrise_tmr); + break; + case OTG_STATE_A_VBUS_ERR: + break; + default: + break; + } +} + +/* Called when entering a state */ +int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) +{ + state_changed = 1; + if (fsm->transceiver->state == new_state) + return 0; + VDBG("Set state: %s\n", otg_state_string(new_state)); + otg_leave_state(fsm, fsm->transceiver->state); + switch (new_state) { + case OTG_STATE_B_IDLE: + otg_drv_vbus(fsm, 0); + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_UNDEF); + otg_add_timer(fsm, b_se0_srp_tmr); + break; + case OTG_STATE_B_SRP_INIT: + otg_start_pulse(fsm); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_UNDEF); + otg_add_timer(fsm, b_srp_fail_tmr); + break; + case OTG_STATE_B_PERIPHERAL: + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 1); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_GADGET); + break; + case OTG_STATE_B_WAIT_ACON: + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + otg_add_timer(fsm, b_ase0_brst_tmr); + fsm->a_bus_suspend = 0; + break; + case OTG_STATE_B_HOST: + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 1); + otg_set_protocol(fsm, PROTO_HOST); + usb_bus_start_enum(fsm->transceiver->host, + fsm->transceiver->host->otg_port); + break; + case OTG_STATE_A_IDLE: + otg_drv_vbus(fsm, 0); + otg_chrg_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + break; + case OTG_STATE_A_WAIT_VRISE: + otg_drv_vbus(fsm, 1); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + otg_add_timer(fsm, a_wait_vrise_tmr); + break; + case OTG_STATE_A_WAIT_BCON: + otg_drv_vbus(fsm, 1); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + otg_add_timer(fsm, a_wait_bcon_tmr); + break; + case OTG_STATE_A_HOST: + otg_drv_vbus(fsm, 1); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 1); + otg_set_protocol(fsm, PROTO_HOST); + /* + * When HNP is triggered while a_bus_req = 0, a_host will + * suspend too fast to complete a_set_b_hnp_en + */ + if (!fsm->a_bus_req || fsm->a_suspend_req) + otg_add_timer(fsm, a_wait_enum_tmr); + break; + case OTG_STATE_A_SUSPEND: + otg_drv_vbus(fsm, 1); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + otg_add_timer(fsm, a_aidl_bdis_tmr); + + break; + case OTG_STATE_A_PERIPHERAL: + otg_loc_conn(fsm, 1); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_GADGET); + otg_drv_vbus(fsm, 1); + break; + case OTG_STATE_A_WAIT_VFALL: + otg_drv_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_HOST); + break; + case OTG_STATE_A_VBUS_ERR: + otg_drv_vbus(fsm, 0); + otg_loc_conn(fsm, 0); + otg_loc_sof(fsm, 0); + otg_set_protocol(fsm, PROTO_UNDEF); + break; + default: + break; + } + + fsm->transceiver->state = new_state; + return 0; +} + +/* State change judgement */ +int otg_statemachine(struct otg_fsm *fsm) +{ + enum usb_otg_state state; + unsigned long flags; + + spin_lock_irqsave(&fsm->lock, flags); + + state = fsm->transceiver->state; + state_changed = 0; + /* State machine state change judgement */ + + switch (state) { + case OTG_STATE_UNDEFINED: + VDBG("fsm->id = %d\n", fsm->id); + if (fsm->id) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else + otg_set_state(fsm, OTG_STATE_A_IDLE); + break; + case OTG_STATE_B_IDLE: + if (!fsm->id) + otg_set_state(fsm, OTG_STATE_A_IDLE); + else if (fsm->b_sess_vld && fsm->transceiver->gadget) + otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); + else if (fsm->b_bus_req && fsm->b_sess_end && fsm->b_se0_srp) + otg_set_state(fsm, OTG_STATE_B_SRP_INIT); + break; + case OTG_STATE_B_SRP_INIT: + if (!fsm->id || fsm->b_srp_done) + otg_set_state(fsm, OTG_STATE_B_IDLE); + break; + case OTG_STATE_B_PERIPHERAL: + if (!fsm->id || !fsm->b_sess_vld) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else if (fsm->b_bus_req && fsm->transceiver-> + gadget->b_hnp_enable && fsm->a_bus_suspend) + otg_set_state(fsm, OTG_STATE_B_WAIT_ACON); + break; + case OTG_STATE_B_WAIT_ACON: + if (fsm->a_conn) + otg_set_state(fsm, OTG_STATE_B_HOST); + else if (!fsm->id || !fsm->b_sess_vld) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else if (fsm->a_bus_resume || fsm->b_ase0_brst_tmout) { + fsm->b_ase0_brst_tmout = 0; + otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); + } + break; + case OTG_STATE_B_HOST: + if (!fsm->id || !fsm->b_sess_vld) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else if (!fsm->b_bus_req || !fsm->a_conn) + otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); + break; + case OTG_STATE_A_IDLE: + if (fsm->id) + otg_set_state(fsm, OTG_STATE_B_IDLE); + else if (!fsm->a_bus_drop && (fsm->a_bus_req || fsm->a_srp_det)) + otg_set_state(fsm, OTG_STATE_A_WAIT_VRISE); + break; + case OTG_STATE_A_WAIT_VRISE: + if (fsm->id || fsm->a_bus_drop || fsm->a_vbus_vld || + fsm->a_wait_vrise_tmout) { + otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); + } + break; + case OTG_STATE_A_WAIT_BCON: + if (!fsm->a_vbus_vld) + otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); + else if (fsm->b_conn) + otg_set_state(fsm, OTG_STATE_A_HOST); + else if (fsm->id | fsm->a_bus_drop | fsm->a_wait_bcon_tmout) + otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); + break; + case OTG_STATE_A_HOST: + if ((!fsm->a_bus_req || fsm->a_suspend_req) && + fsm->transceiver->host->b_hnp_enable) + otg_set_state(fsm, OTG_STATE_A_SUSPEND); + else if (fsm->id || !fsm->b_conn || fsm->a_bus_drop) + otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); + else if (!fsm->a_vbus_vld) + otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); + break; + case OTG_STATE_A_SUSPEND: + if (!fsm->b_conn && fsm->transceiver->host->b_hnp_enable) + otg_set_state(fsm, OTG_STATE_A_PERIPHERAL); + else if (!fsm->b_conn && !fsm->transceiver->host->b_hnp_enable) + otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); + else if (fsm->a_bus_req || fsm->b_bus_resume) + otg_set_state(fsm, OTG_STATE_A_HOST); + else if (fsm->id || fsm->a_bus_drop || fsm->a_aidl_bdis_tmout) + otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); + else if (!fsm->a_vbus_vld) + otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); + break; + case OTG_STATE_A_PERIPHERAL: + if (fsm->id || fsm->a_bus_drop) + otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); + else if (fsm->b_bus_suspend) + otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); + else if (!fsm->a_vbus_vld) + otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); + break; + case OTG_STATE_A_WAIT_VFALL: + if (fsm->id || fsm->a_bus_req || (!fsm->a_sess_vld && + !fsm->b_conn)) + otg_set_state(fsm, OTG_STATE_A_IDLE); + break; + case OTG_STATE_A_VBUS_ERR: + if (fsm->id || fsm->a_bus_drop || fsm->a_clr_err) + otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); + break; + default: + break; + } + spin_unlock_irqrestore(&fsm->lock, flags); + + VDBG("quit statemachine, changed = %d\n", state_changed); + return state_changed; +} diff --git a/drivers/usb/otg/otg_fsm.h b/drivers/usb/otg/otg_fsm.h new file mode 100644 index 0000000..0cecf1d --- /dev/null +++ b/drivers/usb/otg/otg_fsm.h @@ -0,0 +1,154 @@ +/* Copyright (C) 2007,2008 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#undef DEBUG +#undef VERBOSE + +#ifdef DEBUG +#define DBG(fmt, args...) printk(KERN_DEBUG "[%s] " fmt , \ + __func__, ## args) +#else +#define DBG(fmt, args...) do {} while (0) +#endif + +#ifdef VERBOSE +#define VDBG DBG +#else +#define VDBG(stuff...) do {} while (0) +#endif + +#ifdef VERBOSE +#define MPC_LOC printk("Current Location [%s]:[%d]\n", __FILE__, __LINE__) +#else +#define MPC_LOC do {} while (0) +#endif + +#define PROTO_UNDEF (0) +#define PROTO_HOST (1) +#define PROTO_GADGET (2) + +/* OTG state machine according to the OTG spec */ +struct otg_fsm { + /* Input */ + int a_bus_resume; + int a_bus_suspend; + int a_conn; + int a_sess_vld; + int a_srp_det; + int a_vbus_vld; + int b_bus_resume; + int b_bus_suspend; + int b_conn; + int b_se0_srp; + int b_sess_end; + int b_sess_vld; + int id; + + /* Internal variables */ + int a_set_b_hnp_en; + int b_srp_done; + int b_hnp_enable; + + /* Timeout indicator for timers */ + int a_wait_vrise_tmout; + int a_wait_bcon_tmout; + int a_aidl_bdis_tmout; + int b_ase0_brst_tmout; + + /* Informative variables */ + int a_bus_drop; + int a_bus_req; + int a_clr_err; + int a_suspend_req; + int b_bus_req; + + /* Output */ + int drv_vbus; + int loc_conn; + int loc_sof; + + struct otg_fsm_ops *ops; + struct otg_transceiver *transceiver; + + /* Current usb protocol used: 0:undefine; 1:host; 2:client */ + int protocol; + spinlock_t lock; +}; + +struct otg_fsm_ops { + void (*chrg_vbus)(int on); + void (*drv_vbus)(int on); + void (*loc_conn)(int on); + void (*loc_sof)(int on); + void (*start_pulse)(void); + void (*add_timer)(void *timer); + void (*del_timer)(void *timer); + int (*start_host)(struct otg_fsm *fsm, int on); + int (*start_gadget)(struct otg_fsm *fsm, int on); +}; + + +static inline void otg_chrg_vbus(struct otg_fsm *fsm, int on) +{ + fsm->ops->chrg_vbus(on); +} + +static inline void otg_drv_vbus(struct otg_fsm *fsm, int on) +{ + if (fsm->drv_vbus != on) { + fsm->drv_vbus = on; + fsm->ops->drv_vbus(on); + } +} + +static inline void otg_loc_conn(struct otg_fsm *fsm, int on) +{ + if (fsm->loc_conn != on) { + fsm->loc_conn = on; + fsm->ops->loc_conn(on); + } +} + +static inline void otg_loc_sof(struct otg_fsm *fsm, int on) +{ + if (fsm->loc_sof != on) { + fsm->loc_sof = on; + fsm->ops->loc_sof(on); + } +} + +static inline void otg_start_pulse(struct otg_fsm *fsm) +{ + fsm->ops->start_pulse(); +} + +static inline void otg_add_timer(struct otg_fsm *fsm, void *timer) +{ + fsm->ops->add_timer(timer); +} + +static inline void otg_del_timer(struct otg_fsm *fsm, void *timer) +{ + fsm->ops->del_timer(timer); +} + +int otg_statemachine(struct otg_fsm *fsm); + +/* Defined by device specific driver, for different timer implementation */ +extern struct fsl_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, + *a_aidl_bdis_tmr, *b_ase0_brst_tmr, *b_se0_srp_tmr, *b_srp_fail_tmr, + *a_wait_enum_tmr; diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c index e01b073..efeb4d1 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 8a91b4b..3f2e070 100644 --- a/drivers/usb/otg/twl6030-usb.c +++ b/drivers/usb/otg/twl6030-usb.c @@ -31,6 +31,7 @@ #include <linux/err.h> #include <linux/notifier.h> #include <linux/slab.h> +#include <linux/delay.h> /* usb register definitions */ #define USB_VENDOR_ID_LSB 0x00 @@ -101,7 +102,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) /*-------------------------------------------------------------------------*/ @@ -188,6 +189,19 @@ static int twl6030_phy_suspend(struct otg_transceiver *x, int suspend) return 0; } +static int twl6030_start_srp(struct otg_transceiver *x) +{ + struct twl6030_usb *twl = xceiv_to_twl(x); + + twl6030_writeb(twl, TWL_MODULE_USB, 0x24, USB_VBUS_CTRL_SET); + twl6030_writeb(twl, TWL_MODULE_USB, 0x84, USB_VBUS_CTRL_SET); + + mdelay(100); + twl6030_writeb(twl, TWL_MODULE_USB, 0xa0, USB_VBUS_CTRL_CLR); + + return 0; +} + static int twl6030_usb_ldo_init(struct twl6030_usb *twl) { @@ -403,6 +417,7 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev) twl->otg.init = twl6030_phy_init; twl->otg.shutdown = twl6030_phy_shutdown; twl->otg.set_suspend = twl6030_phy_suspend; + twl->otg.start_srp = twl6030_start_srp; /* init spinlock for workqueue */ spin_lock_init(&twl->lock); diff --git a/drivers/usb/renesas_usbhs/Kconfig b/drivers/usb/renesas_usbhs/Kconfig new file mode 100644 index 0000000..b2e6491 --- /dev/null +++ b/drivers/usb/renesas_usbhs/Kconfig @@ -0,0 +1,16 @@ +# +# Renesas USB Controller Drivers +# + +config USB_RENESAS_USBHS + tristate 'Renesas USBHS controller' + depends on SUPERH || ARCH_SHMOBILE + 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 0000000..b8798ad --- /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 0000000..f3664d6 --- /dev/null +++ b/drivers/usb/renesas_usbhs/common.c @@ -0,0 +1,437 @@ +/* + * 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" + +#define USBHSF_RUNTIME_PWCTRL (1 << 0) + +/* status */ +#define usbhsc_flags_init(p) do {(p)->flags = 0; } while (0) +#define usbhsc_flags_set(p, b) ((p)->flags |= (b)) +#define usbhsc_flags_clr(p, b) ((p)->flags &= ~(b)) +#define usbhsc_flags_has(p, b) ((p)->flags & (b)) + +/* + * 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); +} + +struct usbhs_priv *usbhs_pdev_to_priv(struct platform_device *pdev) +{ + return dev_get_drvdata(&pdev->dev); +} + +/* + * 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 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, +}; + +/* + * power control + */ +static void usbhsc_power_ctrl(struct usbhs_priv *priv, int enable) +{ + struct device *dev = usbhs_priv_to_dev(priv); + + if (enable) { + /* enable PM */ + pm_runtime_get_sync(dev); + + /* USB on */ + usbhs_sys_clock_ctrl(priv, enable); + usbhsc_bus_ctrl(priv, enable); + } else { + /* USB off */ + usbhsc_bus_ctrl(priv, enable); + usbhs_sys_clock_ctrl(priv, enable); + + /* disable PM */ + pm_runtime_put_sync(dev); + } +} + +/* + * notify hotplug + */ +static void usbhsc_notify_hotplug(struct work_struct *work) +{ + struct usbhs_priv *priv = container_of(work, + struct usbhs_priv, + notify_hotplug_work.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__); + + /* power on */ + if (usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) + usbhsc_power_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); + + /* power off */ + if (usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) + usbhsc_power_ctrl(priv, enable); + + usbhs_mod_change(priv, -1); + + /* reset phy for next connection */ + usbhs_platform_call(priv, phy_reset, pdev); + } +} + +int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev) +{ + struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); + int delay = usbhs_get_dparam(priv, detection_delay); + + /* + * This functions will be called in interrupt. + * To make sure safety context, + * use workqueue for usbhs_notify_hotplug + */ + schedule_delayed_work(&priv->notify_hotplug_work, delay); + 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) { + 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); + } + + /* FIXME */ + /* runtime power control ? */ + if (priv->pfunc->get_vbus) + usbhsc_flags_set(priv, USBHSF_RUNTIME_PWCTRL); + + /* + * priv settings + */ + priv->irq = irq; + priv->pdev = pdev; + INIT_DELAYED_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_iounmap; + + ret = usbhs_mod_probe(priv); + if (ret < 0) + goto probe_end_pipe_exit; + + /* 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_mod_exit; + } + + /* reset phy for connection */ + usbhs_platform_call(priv, phy_reset, pdev); + + /* power control */ + pm_runtime_enable(&pdev->dev); + if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) { + usbhsc_power_ctrl(priv, 1); + usbhs_mod_autonomy_mode(priv); + } + + /* + * manual call notify_hotplug for cold plug + */ + 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_mod_exit: + usbhs_mod_remove(priv); +probe_end_pipe_exit: + usbhs_pipe_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 = usbhs_pdev_to_priv(pdev); + struct renesas_usbhs_platform_info *info = pdev->dev.platform_data; + struct renesas_usbhs_driver_callback *dfunc = &info->driver_callback; + + dev_dbg(&pdev->dev, "usb remove\n"); + + dfunc->notify_hotplug = NULL; + + /* power off */ + if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) + usbhsc_power_ctrl(priv, 0); + + pm_runtime_disable(&pdev->dev); + + usbhs_platform_call(priv, hardware_exit, pdev); + usbhs_mod_remove(priv); + usbhs_pipe_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 0000000..0aadcb4 --- /dev/null +++ b/drivers/usb/renesas_usbhs/common.h @@ -0,0 +1,230 @@ +/* + * 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 VBINT (1 << 15) /* VBUS0_0 and VBUS1_0 Interrupt Status */ +#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 delayed_work notify_hotplug_work; + struct platform_device *pdev; + + spinlock_t lock; + + u32 flags; + + /* + * 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); + +int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev); +/* + * 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 + */ +struct usbhs_priv *usbhs_pdev_to_priv(struct platform_device *pdev); +#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 0000000..a577f8f --- /dev/null +++ b/drivers/usb/renesas_usbhs/mod.c @@ -0,0 +1,328 @@ +/* + * 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) +#define usbhs_mod_info_call(priv, func, param...) \ +({ \ + struct usbhs_mod_info *info; \ + info = usbhs_priv_to_modinfo(priv); \ + !info->func ? 0 : \ + info->func(param); \ +}) + +/* + * autonomy + * + * these functions are used if platform doesn't have external phy. + * -> there is no "notify_hotplug" callback from platform + * -> call "notify_hotplug" by itself + * -> use own interrupt to connect/disconnect + * -> it mean module clock is always ON + * ~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +static int usbhsm_autonomy_get_vbus(struct platform_device *pdev) +{ + struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); + + return VBSTS & usbhs_read(priv, INTSTS0); +} + +static int usbhsm_autonomy_irq_vbus(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state) +{ + struct platform_device *pdev = usbhs_priv_to_pdev(priv); + + return usbhsc_drvcllbck_notify_hotplug(pdev); +} + +void usbhs_mod_autonomy_mode(struct usbhs_priv *priv) +{ + struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); + + info->irq_vbus = usbhsm_autonomy_irq_vbus; + priv->pfunc->get_vbus = usbhsm_autonomy_get_vbus; + + usbhs_irq_callback_update(priv, NULL); +} + +/* + * 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->dvstctr = usbhs_read(priv, DVSTCTR); + + /* mask */ + if (mod) { + state->brdysts = usbhs_read(priv, BRDYSTS); + state->nrdysts = usbhs_read(priv, NRDYSTS); + state->bempsts = usbhs_read(priv, BEMPSTS); + + 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 & VBINT) + usbhs_mod_info_call(priv, irq_vbus, priv, &irq_state); + + 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; + struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv); + + 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 (info->irq_vbus) + intenb0 |= VBSE; + + if (mod) { + 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 0000000..5c845a2 --- /dev/null +++ b/drivers/usb/renesas_usbhs/mod.h @@ -0,0 +1,137 @@ +/* + * 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 */ + + /* + * INTSTS0 :: VBINT + * + * This function will be used as autonomy mode + * when platform cannot call notify_hotplug. + * + * This callback cannot be member of "struct usbhs_mod" + * because it will be used even though + * host/gadget has not been selected. + */ + int (*irq_vbus)(struct usbhs_priv *priv, + struct usbhs_irq_state *irq_state); +}; + +/* + * 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); + +void usbhs_mod_autonomy_mode(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 0000000..206cfab --- /dev/null +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -0,0 +1,1384 @@ +/* + * 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) + +/* + * usbhsg_trylock + * + * This driver don't use spin_try_lock + * to avoid warning of CONFIG_DEBUG_SPINLOCK + */ +static spinlock_t *usbhsg_trylock(struct usbhsg_gpriv *gpriv, + unsigned long *flags) +{ + spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv); + + /* check spin lock status + * to avoid deadlock/nest */ + if (spin_is_locked(lock)) + return NULL; + + spin_lock_irqsave(lock, *flags); + + return lock; +} + +static void usbhsg_unlock(spinlock_t *lock, unsigned long *flags) +{ + if (!lock) + return; + + spin_unlock_irqrestore(lock, *flags); +} + +/* + * 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; + unsigned long flags; + 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 usbhsg_trylock 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 *******************/ + lock = usbhsg_trylock(gpriv, &flags); + + ureq = usbhsg_queue_get(uep); + if (ureq) { + if (prepare) + ret = uep->handler->prepare(uep, ureq); + else + ret = uep->handler->try_run(uep, ureq); + } + usbhsg_unlock(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 usbhsg_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); + if (!usbhsg_uep_to_pipe(uep)) { + dev_err(dev, "wrong recip request\n"); + return -EINVAL; + } + + 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); + } + + return 0; +} + +static void usbhsg_uep_init(struct usbhsg_gpriv *gpriv) +{ + int i; + struct usbhsg_uep *uep; + + usbhsg_for_each_uep_with_dcp(uep, gpriv, i) + uep->pipe = NULL; +} + +/* + * + * 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; + unsigned long flags; + int ret = -EIO; + + /* + * if it already have pipe, + * nothing to do + */ + if (uep->pipe) + return 0; + + /******************** spin lock ********************/ + lock = usbhsg_trylock(gpriv, &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; + } + + usbhsg_unlock(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; + unsigned long flags; + int ret; + + /******************** spin lock ********************/ + lock = usbhsg_trylock(gpriv, &flags); + + ret = usbhsg_pipe_disable(uep); + + usbhsg_unlock(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; + unsigned long flags; + int ret = 0; + + /* + * 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 usbshg_trylock 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 ********************/ + lock = usbhsg_trylock(gpriv, &flags); + + /* param check */ + if (usbhsg_is_not_connected(gpriv) || + unlikely(!gpriv->driver) || + unlikely(!pipe)) + ret = -ESHUTDOWN; + else + usbhsg_queue_push(uep, ureq); + + usbhsg_unlock(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; + unsigned long flags; + + /* + * see + * CAUTION [*queue handler*] + * CAUTION [*endpoint queue*] + * CAUTION [*request complete*] + */ + + /******************** spin lock ********************/ + lock = usbhsg_trylock(gpriv, &flags); + + usbhsg_queue_pop(uep, ureq, -ECONNRESET); + + usbhsg_unlock(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; + unsigned long flags; + int ret = -EAGAIN; + + /* + * see + * CAUTION [*queue handler*] + * CAUTION [*endpoint queue*] + * CAUTION [*request complete*] + */ + + /******************** spin lock ********************/ + lock = usbhsg_trylock(gpriv, &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; + } + + usbhsg_unlock(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; + unsigned long flags; + + /******************** spin lock ********************/ + lock = usbhsg_trylock(gpriv, &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_uep_init(gpriv); + 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: + usbhsg_unlock(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; + unsigned long flags; + + /******************** spin lock ********************/ + lock = usbhsg_trylock(gpriv, &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); + + usbhsg_unlock(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: + usbhsg_unlock(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 0000000..bc4521c --- /dev/null +++ b/drivers/usb/renesas_usbhs/pipe.c @@ -0,0 +1,874 @@ +/* + * 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); + 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); + usbhs_fifo_disable(pipe); + + do { + val = usbhsp_pipectrl_get(pipe); + val &= CSSTS | PID_MASK; + if (!val) + return 0; + + udelay(10); + + } while (timeout--); + + 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) +{ + int timeout = 1024; + u16 val; + + /* see "Pipe n Control Register" - "PID" */ + __usbhsp_pid_try_nak_if_stall(pipe); + + usbhsp_pipectrl_set(pipe, PID_MASK, PID_NAK); + + do { + val = usbhsp_pipectrl_get(pipe); + val &= PBUSY; + if (!val) + break; + + udelay(10); + } while (timeout--); +} + +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) +{ + return usbhsp_fifo_select(pipe, 1); +} + +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 = usbhsp_fifo_select(pipe, 1); + 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; + + usbhsp_fifo_clear(pipe); + } +} + +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) { + dev_err(dev, "can't get pipe (%s)\n", + usbhsp_pipe_name[usb_endpoint_type(desc)]); + 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 0000000..1cca9b7 --- /dev/null +++ b/drivers/usb/renesas_usbhs/pipe.h @@ -0,0 +1,104 @@ +/* + * 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) (int)((p) - (p)->priv->pipe_info.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 c2b2976..b71e309 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 9a2117f..9e536ee 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/cp210x.c b/drivers/usb/serial/cp210x.c index 0f11afd..fd67cc5 100644 --- a/drivers/usb/serial/cp210x.c +++ b/drivers/usb/serial/cp210x.c @@ -102,7 +102,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x10C4, 0x8218) }, /* Lipowsky Industrie Elektronik GmbH, HARP-1 */ { USB_DEVICE(0x10C4, 0x822B) }, /* Modem EDGE(GSM) Comander 2 */ { USB_DEVICE(0x10C4, 0x826B) }, /* Cygnal Integrated Products, Inc., Fasttrax GPS demonstration module */ - { USB_DEVICE(0x10C4, 0x8293) }, /* Telegesys ETRX2USB */ + { USB_DEVICE(0x10C4, 0x8293) }, /* Telegesis ETRX2USB */ { USB_DEVICE(0x10C4, 0x82F9) }, /* Procyon AVS */ { USB_DEVICE(0x10C4, 0x8341) }, /* Siemens MC35PU GPRS Modem */ { USB_DEVICE(0x10C4, 0x8382) }, /* Cygnal Integrated Products, Inc. */ @@ -112,6 +112,10 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x10C4, 0x8418) }, /* IRZ Automation Teleport SG-10 GSM/GPRS Modem */ { USB_DEVICE(0x10C4, 0x846E) }, /* BEI USB Sensor Interface (VCP) */ { USB_DEVICE(0x10C4, 0x8477) }, /* Balluff RFID */ + { USB_DEVICE(0x10C4, 0x85EA) }, /* AC-Services IBUS-IF */ + { USB_DEVICE(0x10C4, 0x85EB) }, /* AC-Services CIS-IBUS */ + { USB_DEVICE(0x10C4, 0x8664) }, /* AC-Services CAN-IF */ + { USB_DEVICE(0x10C4, 0x8665) }, /* AC-Services OBD-IF */ { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */ { USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */ { USB_DEVICE(0x10C4, 0xEA71) }, /* Infinity GPS-MIC-1 Radio Monophone */ diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 4de6ef0..e8dbde5 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -566,6 +566,7 @@ static struct usb_device_id id_table_combined [] = { { USB_DEVICE(FTDI_VID, FTDI_IBS_APP70_PID) }, { USB_DEVICE(FTDI_VID, FTDI_IBS_PEDO_PID) }, { USB_DEVICE(FTDI_VID, FTDI_IBS_PROD_PID) }, + { USB_DEVICE(FTDI_VID, FTDI_TAVIR_STK500_PID) }, /* * ELV devices: */ diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h index efffc23..1d946cd 100644 --- a/drivers/usb/serial/ftdi_sio_ids.h +++ b/drivers/usb/serial/ftdi_sio_ids.h @@ -491,6 +491,11 @@ /* www.canusb.com Lawicel CANUSB device (FTDI_VID) */ #define FTDI_CANUSB_PID 0xFFA8 /* Product Id */ +/* + * TavIR AVR product ids (FTDI_VID) + */ +#define FTDI_TAVIR_STK500_PID 0xFA33 /* STK500 AVR programmer */ + /********************************/ diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index 26710b1..b0a7a9e 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -1,7 +1,7 @@ /* * Garmin GPS driver * - * Copyright (C) 2006-2009 Hermann Kneissel herkne@users.sourceforge.net + * Copyright (C) 2006-2011 Hermann Kneissel herkne@gmx.de * * The latest version of the driver can be found at * http://sourceforge.net/projects/garmin-gps/ @@ -51,7 +51,7 @@ static int debug; */ #define VERSION_MAJOR 0 -#define VERSION_MINOR 33 +#define VERSION_MINOR 36 #define _STR(s) #s #define _DRIVER_VERSION(a, b) "v" _STR(a) "." _STR(b) @@ -410,6 +410,7 @@ static int gsp_send_ack(struct garmin_data *garmin_data_p, __u8 pkt_id) */ static int gsp_rec_packet(struct garmin_data *garmin_data_p, int count) { + unsigned long flags; const __u8 *recpkt = garmin_data_p->inbuffer+GSP_INITIAL_OFFSET; __le32 *usbdata = (__le32 *) garmin_data_p->inbuffer; @@ -458,7 +459,9 @@ static int gsp_rec_packet(struct garmin_data *garmin_data_p, int count) /* if this was an abort-transfer command, flush all queued data. */ if (isAbortTrfCmnd(garmin_data_p->inbuffer)) { + spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->flags |= FLAGS_DROP_DATA; + spin_unlock_irqrestore(&garmin_data_p->lock, flags); pkt_clear(garmin_data_p); } @@ -943,7 +946,7 @@ static int garmin_open(struct tty_struct *tty, struct usb_serial_port *port) spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->mode = initial_mode; garmin_data_p->count = 0; - garmin_data_p->flags = 0; + garmin_data_p->flags &= FLAGS_SESSION_REPLY1_SEEN; spin_unlock_irqrestore(&garmin_data_p->lock, flags); /* shutdown any bulk reads that might be going on */ @@ -1178,7 +1181,8 @@ static int garmin_write_room(struct tty_struct *tty) static void garmin_read_process(struct garmin_data *garmin_data_p, - unsigned char *data, unsigned data_length) + unsigned char *data, unsigned data_length, + int bulk_data) { unsigned long flags; @@ -1193,7 +1197,8 @@ static void garmin_read_process(struct garmin_data *garmin_data_p, send it directly to the tty port */ if (garmin_data_p->flags & FLAGS_QUEUING) { pkt_add(garmin_data_p, data, data_length); - } else if (getLayerId(data) == GARMIN_LAYERID_APPL) { + } else if (bulk_data || + getLayerId(data) == GARMIN_LAYERID_APPL) { spin_lock_irqsave(&garmin_data_p->lock, flags); garmin_data_p->flags |= APP_RESP_SEEN; @@ -1237,7 +1242,7 @@ static void garmin_read_bulk_callback(struct urb *urb) usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length, data); - garmin_read_process(garmin_data_p, data, urb->actual_length); + garmin_read_process(garmin_data_p, data, urb->actual_length, 1); if (urb->actual_length == 0 && 0 != (garmin_data_p->flags & FLAGS_BULK_IN_RESTART)) { @@ -1346,7 +1351,7 @@ static void garmin_read_int_callback(struct urb *urb) __func__, garmin_data_p->serial_num); } - garmin_read_process(garmin_data_p, data, urb->actual_length); + garmin_read_process(garmin_data_p, data, urb->actual_length, 0); port->interrupt_in_urb->dev = port->serial->dev; retval = usb_submit_urb(urb, GFP_ATOMIC); @@ -1461,6 +1466,7 @@ static int garmin_attach(struct usb_serial *serial) garmin_data_p->timer.function = timeout_handler; garmin_data_p->port = port; garmin_data_p->state = 0; + garmin_data_p->flags = 0; garmin_data_p->count = 0; usb_set_serial_port_data(port, garmin_data_p); diff --git a/drivers/usb/serial/moto_modem.c b/drivers/usb/serial/moto_modem.c index 653465f..e2bfecc 100644 --- a/drivers/usb/serial/moto_modem.c +++ b/drivers/usb/serial/moto_modem.c @@ -25,6 +25,7 @@ static const struct usb_device_id id_table[] = { { USB_DEVICE(0x05c6, 0x3197) }, /* unknown Motorola phone */ { USB_DEVICE(0x0c44, 0x0022) }, /* unknown Mororola phone */ { USB_DEVICE(0x22b8, 0x2a64) }, /* Motorola KRZR K1m */ + { USB_DEVICE(0x22b8, 0x2c84) }, /* Motorola VE240 phone */ { USB_DEVICE(0x22b8, 0x2c64) }, /* Motorola V950 phone */ { }, }; diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index 1b5633f..96423f3 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -289,8 +289,11 @@ static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port, /* The conncected devices do not have a bulk write endpoint, * to transmit data to de barcode device the control endpoint is used */ dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_NOIO); - if (!dr) - return -ENOMEM; + if (!dr) { + dev_err(&port->dev, "out of memory\n"); + count = -ENOMEM; + goto error; + } dr->bRequestType = USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT; dr->bRequest = 0x01; diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index d77ff04..318dd00 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -149,6 +149,7 @@ static void option_instat_callback(struct urb *urb); #define HUAWEI_PRODUCT_K3765 0x1465 #define HUAWEI_PRODUCT_E14AC 0x14AC #define HUAWEI_PRODUCT_ETS1220 0x1803 +#define HUAWEI_PRODUCT_E353 0x1506 #define QUANTA_VENDOR_ID 0x0408 #define QUANTA_PRODUCT_Q101 0xEA02 @@ -532,6 +533,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3765, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_ETS1220, 0xff, 0xff, 0xff) }, { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E14AC, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x01, 0x01) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V640) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V620) }, { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V740) }, @@ -972,7 +974,7 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(OLIVETTI_VENDOR_ID, OLIVETTI_PRODUCT_OLICARD100) }, { USB_DEVICE(CELOT_VENDOR_ID, CELOT_PRODUCT_CT680M) }, /* CT-650 CDMA 450 1xEVDO modem */ { USB_DEVICE(ONDA_VENDOR_ID, ONDA_MT825UP) }, /* ONDA MT825UP modem */ - { USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_GT_B3730, USB_CLASS_CDC_DATA, 0x00, 0x00) }, /* Samsung GT-B3730/GT-B3710 LTE USB modem.*/ + { USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_GT_B3730, USB_CLASS_CDC_DATA, 0x00, 0x00) }, /* Samsung GT-B3730 LTE USB modem.*/ { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); @@ -1109,6 +1111,12 @@ static int option_probe(struct usb_serial *serial, serial->interface->cur_altsetting->desc.bInterfaceNumber == 1) return -ENODEV; + /* Don't bind network interface on Samsung GT-B3730, it is handled by a separate module */ + if (serial->dev->descriptor.idVendor == SAMSUNG_VENDOR_ID && + serial->dev->descriptor.idProduct == SAMSUNG_PRODUCT_GT_B3730 && + serial->interface->cur_altsetting->desc.bInterfaceClass != USB_CLASS_CDC_DATA) + return -ENODEV; + data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL); if (!data) diff --git a/drivers/usb/serial/sam-ba.c b/drivers/usb/serial/sam-ba.c deleted file mode 100644 index e3bba64..0000000 --- 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/drivers/usb/storage/unusual_realtek.h b/drivers/usb/storage/unusual_realtek.h index 3236e03..e41f50c 100644 --- a/drivers/usb/storage/unusual_realtek.h +++ b/drivers/usb/storage/unusual_realtek.h @@ -23,19 +23,19 @@ #if defined(CONFIG_USB_STORAGE_REALTEK) || \ defined(CONFIG_USB_STORAGE_REALTEK_MODULE) -UNUSUAL_DEV(0x0bda, 0x0159, 0x0000, 0x9999, +UNUSUAL_DEV(0x0bda, 0x0138, 0x0000, 0x9999, "Realtek", "USB Card Reader", - USB_SC_SCSI, USB_PR_BULK, init_realtek_cr, 0), + USB_SC_DEVICE, USB_PR_DEVICE, init_realtek_cr, 0), UNUSUAL_DEV(0x0bda, 0x0158, 0x0000, 0x9999, "Realtek", "USB Card Reader", - USB_SC_SCSI, USB_PR_BULK, init_realtek_cr, 0), + USB_SC_DEVICE, USB_PR_DEVICE, init_realtek_cr, 0), -UNUSUAL_DEV(0x0bda, 0x0138, 0x0000, 0x9999, +UNUSUAL_DEV(0x0bda, 0x0159, 0x0000, 0x9999, "Realtek", "USB Card Reader", - USB_SC_SCSI, USB_PR_BULK, init_realtek_cr, 0), + USB_SC_DEVICE, USB_PR_DEVICE, init_realtek_cr, 0), #endif /* defined(CONFIG_USB_STORAGE_REALTEK) || ... */ diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 4219c19..5ee7ac4 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -439,7 +439,8 @@ static void adjust_quirks(struct us_data *us) US_FL_CAPACITY_HEURISTICS | US_FL_IGNORE_DEVICE | US_FL_NOT_LOCKABLE | US_FL_MAX_SECTORS_64 | US_FL_CAPACITY_OK | US_FL_IGNORE_RESIDUE | - US_FL_SINGLE_LUN | US_FL_NO_WP_DETECT); + US_FL_SINGLE_LUN | US_FL_NO_WP_DETECT | + US_FL_NO_READ_DISC_INFO | US_FL_NO_READ_CAPACITY_16); p = quirks; while (*p) { @@ -471,6 +472,12 @@ static void adjust_quirks(struct us_data *us) case 'c': f |= US_FL_FIX_CAPACITY; break; + case 'd': + f |= US_FL_NO_READ_DISC_INFO; + break; + case 'e': + f |= US_FL_NO_READ_CAPACITY_16; + break; case 'h': f |= US_FL_CAPACITY_HEURISTICS; break; diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h index 4eb56ed..fffdf00 100644 --- a/include/linux/fsl_devices.h +++ b/include/linux/fsl_devices.h @@ -72,6 +72,7 @@ struct fsl_usb2_platform_data { void (*exit)(struct platform_device *); void __iomem *regs; /* ioremap'd register base */ struct clk *clk; + unsigned power_budget; /* hcd->power_budget */ unsigned big_endian_mmio:1; unsigned big_endian_desc:1; unsigned es:1; /* need USBMODE:ES */ @@ -79,6 +80,21 @@ struct fsl_usb2_platform_data { unsigned have_sysif_regs:1; unsigned invert_drvvbus:1; unsigned invert_pwr_fault:1; + + unsigned suspended:1; + unsigned already_suspended:1; + + /* register save area for suspend/resume */ + u32 pm_command; + u32 pm_status; + u32 pm_intr_enable; + u32 pm_frame_index; + u32 pm_segment; + u32 pm_frame_list; + u32 pm_async_next; + u32 pm_configured_flag; + u32 pm_portsc; + u32 pm_usbgenctrl; }; /* Flags in fsl_usb2_mph_platform_data */ diff --git a/include/linux/usb.h b/include/linux/usb.h index 65f78ca..73c7df4 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -806,8 +806,10 @@ struct usbdrv_wrap { * @resume: Called when the device is being resumed by the system. * @reset_resume: Called when the suspended device has been reset instead * of being resumed. - * @pre_reset: Called by usb_reset_device() when the device - * is about to be reset. + * @pre_reset: Called by usb_reset_device() when the device is about to be + * reset. This routine must not return until the driver has no active + * URBs for the device, and no more URBs may be submitted until the + * post_reset method is called. * @post_reset: Called by usb_reset_device() after the device * has been reset * @id_table: USB drivers use ID table to support hotplugging. diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index b72f305..0fd3fbd 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -579,7 +579,7 @@ struct usb_ss_ep_comp_descriptor { __u8 bMaxBurst; __u8 bmAttributes; - __u16 wBytesPerInterval; + __le16 wBytesPerInterval; } __attribute__ ((packed)); #define USB_DT_SS_EP_COMP_SIZE 6 diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 882a084..b78cba4 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -37,6 +37,14 @@ #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> +/* + * USB function drivers should return USB_GADGET_DELAYED_STATUS if they + * wish to delay the data/status stages of the control transfer till they + * are ready. The control transfer will then be kept from completing till + * all the function drivers that requested for USB_GADGET_DELAYED_STAUS + * invoke usb_composite_setup_continue(). + */ +#define USB_GADGET_DELAYED_STATUS 0x7fff /* Impossibly large value */ struct usb_configuration; @@ -285,6 +293,7 @@ struct usb_composite_driver { extern int usb_composite_probe(struct usb_composite_driver *driver, int (*bind)(struct usb_composite_dev *cdev)); extern void usb_composite_unregister(struct usb_composite_driver *driver); +extern void usb_composite_setup_continue(struct usb_composite_dev *cdev); /** @@ -342,7 +351,12 @@ struct usb_composite_dev { */ unsigned deactivations; - /* protects at least deactivation count */ + /* the composite driver won't complete the control transfer's + * data/status stages till delayed_status is zero. + */ + int delayed_status; + + /* protects deactivations and delayed_status counts*/ spinlock_t lock; }; diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h index e49dfd4..7cc95ee 100644 --- a/include/linux/usb/ehci_def.h +++ b/include/linux/usb/ehci_def.h @@ -25,10 +25,15 @@ struct ehci_caps { /* these fields are specified as 8 and 16 bit registers, * but some hosts can't perform 8 or 16 bit PCI accesses. + * some hosts treat caplength and hciversion as parts of a 32-bit + * register, others treat them as two separate registers, this + * affects the memory map for big endian controllers. */ u32 hc_capbase; -#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */ -#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */ +#define HC_LENGTH(ehci, p) (0x00ff&((p) >> /* bits 7:0 / offset 00h */ \ + (ehci_big_endian_capbase(ehci) ? 24 : 0))) +#define HC_VERSION(ehci, p) (0xffff&((p) >> /* bits 31:16 / offset 02h */ \ + (ehci_big_endian_capbase(ehci) ? 0 : 16))) u32 hcs_params; /* HCSPARAMS - offset 0x4 */ #define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */ #define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */ @@ -52,7 +57,7 @@ struct ehci_caps { #define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/ #define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */ u8 portroute[8]; /* nibbles for routing - offset 0xC */ -} __attribute__ ((packed)); +}; /* Section 2.3 Host Controller Operational Registers */ @@ -150,7 +155,7 @@ struct ehci_regs { #define PORT_CSC (1<<1) /* connect status change */ #define PORT_CONNECT (1<<0) /* device connected */ #define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) -} __attribute__ ((packed)); +}; #define USBMODE 0x68 /* USB Device mode */ #define USBMODE_SDIS (1<<3) /* Stream disable */ @@ -194,7 +199,7 @@ struct ehci_dbg_port { u32 data47; u32 address; #define DBGP_EPADDR(dev, ep) (((dev)<<8)|(ep)) -} __attribute__ ((packed)); +}; #ifdef CONFIG_EARLY_PRINTK_DBGP #include <linux/init.h> diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index e538172..dd1571d 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/msm_hsusb.h b/include/linux/usb/msm_hsusb.h index 3657403..00311fe 100644 --- a/include/linux/usb/msm_hsusb.h +++ b/include/linux/usb/msm_hsusb.h @@ -2,7 +2,7 @@ * * Copyright (C) 2008 Google, Inc. * Author: Brian Swetland <swetland@google.com> - * Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. + * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -54,6 +54,64 @@ enum otg_control_type { }; /** + * PHY used in + * + * INVALID_PHY Unsupported PHY + * CI_45NM_INTEGRATED_PHY Chipidea 45nm integrated PHY + * SNPS_28NM_INTEGRATED_PHY Synopsis 28nm integrated PHY + * + */ +enum msm_usb_phy_type { + INVALID_PHY = 0, + CI_45NM_INTEGRATED_PHY, + SNPS_28NM_INTEGRATED_PHY, +}; + +#define IDEV_CHG_MAX 1500 +#define IUNIT 100 + +/** + * Different states involved in USB charger detection. + * + * USB_CHG_STATE_UNDEFINED USB charger is not connected or detection + * process is not yet started. + * USB_CHG_STATE_WAIT_FOR_DCD Waiting for Data pins contact. + * USB_CHG_STATE_DCD_DONE Data pin contact is detected. + * USB_CHG_STATE_PRIMARY_DONE Primary detection is completed (Detects + * between SDP and DCP/CDP). + * USB_CHG_STATE_SECONDARY_DONE Secondary detection is completed (Detects + * between DCP and CDP). + * USB_CHG_STATE_DETECTED USB charger type is determined. + * + */ +enum usb_chg_state { + USB_CHG_STATE_UNDEFINED = 0, + USB_CHG_STATE_WAIT_FOR_DCD, + USB_CHG_STATE_DCD_DONE, + USB_CHG_STATE_PRIMARY_DONE, + USB_CHG_STATE_SECONDARY_DONE, + USB_CHG_STATE_DETECTED, +}; + +/** + * USB charger types + * + * USB_INVALID_CHARGER Invalid USB charger. + * USB_SDP_CHARGER Standard downstream port. Refers to a downstream port + * on USB2.0 compliant host/hub. + * USB_DCP_CHARGER Dedicated charger port (AC charger/ Wall charger). + * USB_CDP_CHARGER Charging downstream port. Enumeration can happen and + * IDEV_CHG_MAX can be drawn irrespective of USB state. + * + */ +enum usb_chg_type { + USB_INVALID_CHARGER = 0, + USB_SDP_CHARGER, + USB_DCP_CHARGER, + USB_CDP_CHARGER, +}; + +/** * struct msm_otg_platform_data - platform device data * for msm_otg driver. * @phy_init_seq: PHY configuration sequence. val, reg pairs @@ -64,7 +122,8 @@ enum otg_control_type { * @otg_control: OTG switch controlled by user/Id pin * @default_mode: Default operational mode. Applicable only if * OTG switch is controller by user. - * + * @pclk_src_name: pclk is derived from ebi1_usb_clk in case of 7x27 and 8k + * dfab_usb_hs_clk in case of 8660 and 8960. */ struct msm_otg_platform_data { int *phy_init_seq; @@ -73,7 +132,9 @@ struct msm_otg_platform_data { enum usb_mode_type mode; enum otg_control_type otg_control; enum usb_mode_type default_mode; + enum msm_usb_phy_type phy_type; void (*setup_gpio)(enum usb_otg_state state); + char *pclk_src_name; }; /** @@ -83,6 +144,7 @@ struct msm_otg_platform_data { * @irq: IRQ number assigned for HSUSB controller. * @clk: clock struct of usb_hs_clk. * @pclk: clock struct of usb_hs_pclk. + * @pclk_src: pclk source for voting. * @phy_reset_clk: clock struct of usb_phy_clk. * @core_clk: clock struct of usb_hs_core_clk. * @regs: ioremapped register base address. @@ -90,7 +152,12 @@ struct msm_otg_platform_data { * @sm_work: OTG state machine work. * @in_lpm: indicates low power mode (LPM) state. * @async_int: Async interrupt arrived. - * + * @cur_power: The amount of mA available from downstream port. + * @chg_work: Charger detection work. + * @chg_state: The state of charger detection process. + * @chg_type: The type of charger attached. + * @dcd_retires: The retry count used to track Data contact + * detection process. */ struct msm_otg { struct otg_transceiver otg; @@ -98,6 +165,7 @@ struct msm_otg { int irq; struct clk *clk; struct clk *pclk; + struct clk *pclk_src; struct clk *phy_reset_clk; struct clk *core_clk; void __iomem *regs; @@ -107,6 +175,11 @@ struct msm_otg { struct work_struct sm_work; atomic_t in_lpm; int async_int; + unsigned cur_power; + struct delayed_work chg_work; + enum usb_chg_state chg_state; + enum usb_chg_type chg_type; + u8 dcd_retries; }; #endif diff --git a/include/linux/usb/msm_hsusb_hw.h b/include/linux/usb/msm_hsusb_hw.h index 7d1babb..6e97a2d 100644 --- a/include/linux/usb/msm_hsusb_hw.h +++ b/include/linux/usb/msm_hsusb_hw.h @@ -24,6 +24,7 @@ #define USB_PORTSC (MSM_USB_BASE + 0x0184) #define USB_OTGSC (MSM_USB_BASE + 0x01A4) #define USB_USBMODE (MSM_USB_BASE + 0x01A8) +#define USB_PHY_CTRL (MSM_USB_BASE + 0x0240) #define USBCMD_RESET 2 #define USB_USBINTR (MSM_USB_BASE + 0x0148) @@ -42,6 +43,7 @@ #define ASYNC_INTR_CTRL (1 << 29) /* Enable async interrupt */ #define ULPI_STP_CTRL (1 << 30) /* Block communication with PHY */ +#define PHY_RETEN (1 << 1) /* PHY retention enable/disable */ /* OTG definitions */ #define OTGSC_INTSTS_MASK (0x7f << 16) diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index 6e40718..d87f44f 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -168,6 +168,7 @@ otg_shutdown(struct otg_transceiver *otg) #ifdef CONFIG_USB_OTG_UTILS extern struct otg_transceiver *otg_get_transceiver(void); extern void otg_put_transceiver(struct otg_transceiver *); +extern const char *otg_state_string(enum usb_otg_state state); #else static inline struct otg_transceiver *otg_get_transceiver(void) { @@ -177,6 +178,11 @@ static inline struct otg_transceiver *otg_get_transceiver(void) static inline void otg_put_transceiver(struct otg_transceiver *x) { } + +static inline const char *otg_state_string(enum usb_otg_state state) +{ + return NULL; +} #endif /* Context: can sleep */ diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h new file mode 100644 index 0000000..3a7f1d9 --- /dev/null +++ b/include/linux/usb/renesas_usbhs.h @@ -0,0 +1,156 @@ +/* + * 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: + * + * delay time from notify_hotplug callback + */ + int detection_delay; +}; + +/* + * 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) \ + dc->notify_hotplug(pdev); \ + }) +#endif /* RENESAS_USB_H */ |