mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-24 09:50:04 +00:00
Merge 2.6.39-rc4 into usb-next
This is needed to help resolve some xhci issues and other minor differences. Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
commit
50ee9339c7
58 changed files with 4790 additions and 671 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -33,6 +33,9 @@
|
|||
#define S5P_EINT_WAKEUP_MASK S5P_PMUREG(0x0604)
|
||||
#define S5P_WAKEUP_MASK S5P_PMUREG(0x0608)
|
||||
|
||||
#define S5P_USBHOST_PHY_CONTROL S5P_PMUREG(0x0708)
|
||||
#define S5P_USBHOST_PHY_ENABLE (1 << 0)
|
||||
|
||||
#define S5P_MIPI_DPHY_CONTROL(n) S5P_PMUREG(0x0710 + (n) * 4)
|
||||
#define S5P_MIPI_DPHY_ENABLE (1 << 0)
|
||||
#define S5P_MIPI_DPHY_SRESETN (1 << 1)
|
||||
|
|
64
arch/arm/mach-exynos4/include/mach/regs-usb-phy.h
Normal file
64
arch/arm/mach-exynos4/include/mach/regs-usb-phy.h
Normal file
|
@ -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 */
|
|
@ -30,6 +30,8 @@
|
|||
#include <plat/cpu.h>
|
||||
#include <plat/devs.h>
|
||||
#include <plat/sdhci.h>
|
||||
#include <plat/ehci.h>
|
||||
#include <plat/clock.h>
|
||||
|
||||
#include <mach/map.h>
|
||||
|
||||
|
@ -262,6 +264,16 @@ static struct i2c_board_info i2c5_devs[] __initdata = {
|
|||
/* max8997, To be updated */
|
||||
};
|
||||
|
||||
/* USB EHCI */
|
||||
static struct s5p_ehci_platdata nuri_ehci_pdata;
|
||||
|
||||
static void __init nuri_ehci_init(void)
|
||||
{
|
||||
struct s5p_ehci_platdata *pdata = &nuri_ehci_pdata;
|
||||
|
||||
s5p_ehci_set_platdata(pdata);
|
||||
}
|
||||
|
||||
static struct platform_device *nuri_devices[] __initdata = {
|
||||
/* Samsung Platform Devices */
|
||||
&emmc_fixed_voltage,
|
||||
|
@ -270,6 +282,7 @@ static struct platform_device *nuri_devices[] __initdata = {
|
|||
&s3c_device_hsmmc3,
|
||||
&s3c_device_wdt,
|
||||
&s3c_device_timer[0],
|
||||
&s5p_device_ehci,
|
||||
|
||||
/* NURI Devices */
|
||||
&nuri_gpio_keys,
|
||||
|
@ -291,6 +304,9 @@ static void __init nuri_machine_init(void)
|
|||
i2c_register_board_info(1, i2c1_devs, ARRAY_SIZE(i2c1_devs));
|
||||
i2c_register_board_info(5, i2c5_devs, ARRAY_SIZE(i2c5_devs));
|
||||
|
||||
nuri_ehci_init();
|
||||
clk_xusbxti.rate = 24000000;
|
||||
|
||||
/* Last */
|
||||
platform_add_devices(nuri_devices, ARRAY_SIZE(nuri_devices));
|
||||
}
|
||||
|
|
136
arch/arm/mach-exynos4/usb-phy.c
Normal file
136
arch/arm/mach-exynos4/usb-phy.c
Normal file
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -33,4 +33,5 @@ obj-$(CONFIG_S5P_DEV_FIMC3) += dev-fimc3.o
|
|||
obj-$(CONFIG_S5P_DEV_ONENAND) += dev-onenand.o
|
||||
obj-$(CONFIG_S5P_DEV_CSIS0) += dev-csis0.o
|
||||
obj-$(CONFIG_S5P_DEV_CSIS1) += dev-csis1.o
|
||||
obj-$(CONFIG_S5P_DEV_USB_EHCI) += dev-ehci.o
|
||||
obj-$(CONFIG_S5P_SETUP_MIPIPHY) += setup-mipiphy.o
|
||||
|
|
57
arch/arm/plat-s5p/dev-ehci.c
Normal file
57
arch/arm/plat-s5p/dev-ehci.c
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Samsung Electronics Co.Ltd
|
||||
* Author: Joonyoung Shim <jy0922.shim@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <mach/irqs.h>
|
||||
#include <mach/map.h>
|
||||
#include <plat/devs.h>
|
||||
#include <plat/ehci.h>
|
||||
#include <plat/usb-phy.h>
|
||||
|
||||
/* USB EHCI Host Controller registration */
|
||||
static struct resource s5p_ehci_resource[] = {
|
||||
[0] = {
|
||||
.start = S5P_PA_EHCI,
|
||||
.end = S5P_PA_EHCI + SZ_256 - 1,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
[1] = {
|
||||
.start = IRQ_USB_HOST,
|
||||
.end = IRQ_USB_HOST,
|
||||
.flags = IORESOURCE_IRQ,
|
||||
}
|
||||
};
|
||||
|
||||
static u64 s5p_device_ehci_dmamask = 0xffffffffUL;
|
||||
|
||||
struct platform_device s5p_device_ehci = {
|
||||
.name = "s5p-ehci",
|
||||
.id = -1,
|
||||
.num_resources = ARRAY_SIZE(s5p_ehci_resource),
|
||||
.resource = s5p_ehci_resource,
|
||||
.dev = {
|
||||
.dma_mask = &s5p_device_ehci_dmamask,
|
||||
.coherent_dma_mask = 0xffffffffUL
|
||||
}
|
||||
};
|
||||
|
||||
void __init s5p_ehci_set_platdata(struct s5p_ehci_platdata *pd)
|
||||
{
|
||||
struct s5p_ehci_platdata *npd;
|
||||
|
||||
npd = s3c_set_platdata(pd, sizeof(struct s5p_ehci_platdata),
|
||||
&s5p_device_ehci);
|
||||
|
||||
if (!npd->phy_init)
|
||||
npd->phy_init = s5p_usb_phy_init;
|
||||
if (!npd->phy_exit)
|
||||
npd->phy_exit = s5p_usb_phy_exit;
|
||||
}
|
21
arch/arm/plat-s5p/include/plat/ehci.h
Normal file
21
arch/arm/plat-s5p/include/plat/ehci.h
Normal file
|
@ -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 */
|
|
@ -39,7 +39,7 @@
|
|||
#define S5P_VA_TWD S5P_VA_COREPERI(0x600)
|
||||
#define S5P_VA_GIC_DIST S5P_VA_COREPERI(0x1000)
|
||||
|
||||
#define S3C_VA_USB_HSPHY S3C_ADDR(0x02900000)
|
||||
#define S5P_VA_USB_HSPHY S3C_ADDR(0x02900000)
|
||||
|
||||
#define VA_VIC(x) (S3C_VA_IRQ + ((x) * 0x10000))
|
||||
#define VA_VIC0 VA_VIC(0)
|
||||
|
|
22
arch/arm/plat-s5p/include/plat/usb-phy.h
Normal file
22
arch/arm/plat-s5p/include/plat/usb-phy.h
Normal file
|
@ -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 */
|
|
@ -142,6 +142,8 @@ extern struct platform_device s5p_device_fimc3;
|
|||
extern struct platform_device s5p_device_mipi_csis0;
|
||||
extern struct platform_device s5p_device_mipi_csis1;
|
||||
|
||||
extern struct platform_device s5p_device_ehci;
|
||||
|
||||
extern struct platform_device exynos4_device_sysmmu;
|
||||
|
||||
/* s3c2440 specific devices */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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/
|
||||
|
|
|
@ -65,6 +65,7 @@ config USB_ARCH_HAS_EHCI
|
|||
default y if ARCH_CNS3XXX
|
||||
default y if ARCH_VT8500
|
||||
default y if PLAT_SPEAR
|
||||
default y if PLAT_S5P
|
||||
default y if ARCH_MSM
|
||||
default y if MICROBLAZE
|
||||
default PCI
|
||||
|
@ -116,6 +117,8 @@ source "drivers/usb/host/Kconfig"
|
|||
|
||||
source "drivers/usb/musb/Kconfig"
|
||||
|
||||
source "drivers/usb/renesas_usbhs/Kconfig"
|
||||
|
||||
source "drivers/usb/class/Kconfig"
|
||||
|
||||
source "drivers/usb/storage/Kconfig"
|
||||
|
|
|
@ -45,3 +45,8 @@ obj-$(CONFIG_EARLY_PRINTK_DBGP) += early/
|
|||
|
||||
obj-$(CONFIG_USB_ATM) += atm/
|
||||
obj-$(CONFIG_USB_SPEEDTOUCH) += atm/
|
||||
|
||||
obj-$(CONFIG_USB_MUSB_HDRC) += musb/
|
||||
obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs/
|
||||
obj-$(CONFIG_USB_OTG_UTILS) += otg/
|
||||
obj-$(CONFIG_USB_GADGET) += gadget/
|
||||
|
|
|
@ -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;
|
||||
|
||||
dbg("Entering acm_read_bulk with status %d", status);
|
||||
if (!test_and_clear_bit(index, &acm->read_urbs_free))
|
||||
return 0;
|
||||
|
||||
if (!ACM_READY(acm)) {
|
||||
dev_dbg(&acm->data->dev, "Aborting, acm not ready");
|
||||
dev_vdbg(&acm->data->dev, "%s - urb %d\n", __func__, index);
|
||||
|
||||
res = usb_submit_urb(acm->read_urbs[index], mem_flags);
|
||||
if (res) {
|
||||
if (res != -EPERM) {
|
||||
dev_err(&acm->data->dev,
|
||||
"%s - usb_submit_urb failed: %d\n",
|
||||
__func__, res);
|
||||
}
|
||||
set_bit(index, &acm->read_urbs_free);
|
||||
return res;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int acm_submit_read_urbs(struct acm *acm, gfp_t mem_flags)
|
||||
{
|
||||
int res;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < acm->rx_buflimit; ++i) {
|
||||
res = acm_submit_read_urb(acm, i, mem_flags);
|
||||
if (res)
|
||||
return res;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void acm_process_read_urb(struct acm *acm, struct urb *urb)
|
||||
{
|
||||
struct tty_struct *tty;
|
||||
|
||||
if (!urb->actual_length)
|
||||
return;
|
||||
|
||||
tty = tty_port_tty_get(&acm->port);
|
||||
if (!tty)
|
||||
return;
|
||||
|
||||
tty_insert_flip_string(tty, urb->transfer_buffer, urb->actual_length);
|
||||
tty_flip_buffer_push(tty);
|
||||
|
||||
tty_kref_put(tty);
|
||||
}
|
||||
|
||||
static void acm_read_bulk_callback(struct urb *urb)
|
||||
{
|
||||
struct acm_rb *rb = urb->context;
|
||||
struct acm *acm = rb->instance;
|
||||
unsigned long flags;
|
||||
|
||||
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);
|
||||
|
||||
if (!acm->dev) {
|
||||
dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__);
|
||||
return;
|
||||
}
|
||||
usb_mark_last_busy(acm->dev);
|
||||
|
||||
if (status)
|
||||
dev_dbg(&acm->data->dev, "bulk rx status %d\n", status);
|
||||
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);
|
||||
|
||||
buf = rcv->buffer;
|
||||
buf->size = urb->actual_length;
|
||||
|
||||
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);
|
||||
/* throttle device if requested by tty */
|
||||
spin_lock_irqsave(&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 {
|
||||
/* 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 */
|
||||
}
|
||||
if (likely(!acm->susp_count))
|
||||
tasklet_schedule(&acm->urb_task);
|
||||
}
|
||||
|
||||
static void acm_rx_tasklet(unsigned long _acm)
|
||||
{
|
||||
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");
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
|
||||
buf = list_entry(acm->spare_read_bufs.next,
|
||||
struct acm_rb, list);
|
||||
list_del(&buf->list);
|
||||
|
||||
rcv->buffer = buf;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
spin_lock_irqsave(&acm->read_lock, flags);
|
||||
acm->processing = 0;
|
||||
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 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
|
|||
|
||||
static void acm_tty_unregister(struct acm *acm)
|
||||
{
|
||||
int i, nr;
|
||||
int i;
|
||||
|
||||
nr = acm->rx_buflimit;
|
||||
tty_unregister_device(acm_tty_driver, acm->minor);
|
||||
usb_put_intf(acm->control);
|
||||
acm_table[acm->minor] = NULL;
|
||||
usb_free_urb(acm->ctrlurb);
|
||||
for (i = 0; i < ACM_NW; i++)
|
||||
usb_free_urb(acm->wb[i].urb);
|
||||
for (i = 0; i < nr; i++)
|
||||
usb_free_urb(acm->ru[i].urb);
|
||||
for (i = 0; i < acm->rx_buflimit; i++)
|
||||
usb_free_urb(acm->read_urbs[i]);
|
||||
kfree(acm->country_codes);
|
||||
kfree(acm);
|
||||
}
|
||||
|
||||
static int acm_tty_chars_in_buffer(struct tty_struct *tty);
|
||||
|
||||
static void acm_port_down(struct acm *acm)
|
||||
{
|
||||
int i, nr = acm->rx_buflimit;
|
||||
int i;
|
||||
|
||||
mutex_lock(&open_mutex);
|
||||
if (acm->dev) {
|
||||
usb_autopm_get_interface(acm->control);
|
||||
|
@ -649,10 +546,8 @@ static void acm_port_down(struct acm *acm)
|
|||
usb_kill_urb(acm->ctrlurb);
|
||||
for (i = 0; i < ACM_NW; i++)
|
||||
usb_kill_urb(acm->wb[i].urb);
|
||||
tasklet_disable(&acm->urb_task);
|
||||
for (i = 0; i < nr; i++)
|
||||
usb_kill_urb(acm->ru[i].urb);
|
||||
tasklet_enable(&acm->urb_task);
|
||||
for (i = 0; i < acm->rx_buflimit; i++)
|
||||
usb_kill_urb(acm->read_urbs[i]);
|
||||
acm->control->needs_remote_wakeup = 0;
|
||||
usb_autopm_put_interface(acm->control);
|
||||
}
|
||||
|
@ -698,13 +593,13 @@ static int acm_tty_write(struct tty_struct *tty,
|
|||
int wbn;
|
||||
struct acm_wb *wb;
|
||||
|
||||
dbg("Entering acm_tty_write to write %d bytes,", count);
|
||||
|
||||
if (!ACM_READY(acm))
|
||||
return -EINVAL;
|
||||
if (!count)
|
||||
return 0;
|
||||
|
||||
dev_vdbg(&acm->data->dev, "%s - count %d\n", __func__, count);
|
||||
|
||||
spin_lock_irqsave(&acm->write_lock, flags);
|
||||
wbn = acm_wb_alloc(acm);
|
||||
if (wbn < 0) {
|
||||
|
@ -714,7 +609,7 @@ static int acm_tty_write(struct tty_struct *tty,
|
|||
wb = &acm->wb[wbn];
|
||||
|
||||
count = (count > acm->writesize) ? acm->writesize : count;
|
||||
dbg("Get %d bytes...", count);
|
||||
dev_vdbg(&acm->data->dev, "%s - write %d\n", __func__, count);
|
||||
memcpy(wb->buf, buf, count);
|
||||
wb->len = count;
|
||||
spin_unlock_irqrestore(&acm->write_lock, flags);
|
||||
|
@ -751,22 +646,31 @@ static int acm_tty_chars_in_buffer(struct tty_struct *tty)
|
|||
static void acm_tty_throttle(struct tty_struct *tty)
|
||||
{
|
||||
struct acm *acm = tty->driver_data;
|
||||
|
||||
if (!ACM_READY(acm))
|
||||
return;
|
||||
spin_lock_bh(&acm->throttle_lock);
|
||||
acm->throttle = 1;
|
||||
spin_unlock_bh(&acm->throttle_lock);
|
||||
|
||||
spin_lock_irq(&acm->read_lock);
|
||||
acm->throttle_req = 1;
|
||||
spin_unlock_irq(&acm->read_lock);
|
||||
}
|
||||
|
||||
static void acm_tty_unthrottle(struct tty_struct *tty)
|
||||
{
|
||||
struct acm *acm = tty->driver_data;
|
||||
unsigned int was_throttled;
|
||||
|
||||
if (!ACM_READY(acm))
|
||||
return;
|
||||
spin_lock_bh(&acm->throttle_lock);
|
||||
acm->throttle = 0;
|
||||
spin_unlock_bh(&acm->throttle_lock);
|
||||
tasklet_schedule(&acm->urb_task);
|
||||
|
||||
spin_lock_irq(&acm->read_lock);
|
||||
was_throttled = acm->throttled;
|
||||
acm->throttled = 0;
|
||||
acm->throttle_req = 0;
|
||||
spin_unlock_irq(&acm->read_lock);
|
||||
|
||||
if (was_throttled)
|
||||
acm_submit_read_urbs(acm, GFP_KERNEL);
|
||||
}
|
||||
|
||||
static int acm_tty_break_ctl(struct tty_struct *tty, int state)
|
||||
|
@ -777,7 +681,8 @@ static int acm_tty_break_ctl(struct tty_struct *tty, int state)
|
|||
return -EINVAL;
|
||||
retval = acm_send_break(acm, state ? 0xffff : 0);
|
||||
if (retval < 0)
|
||||
dbg("send break failed");
|
||||
dev_dbg(&acm->control->dev, "%s - send break failed\n",
|
||||
__func__);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
@ -872,7 +777,9 @@ static void acm_tty_set_termios(struct tty_struct *tty,
|
|||
|
||||
if (memcmp(&acm->line, &newline, sizeof newline)) {
|
||||
memcpy(&acm->line, &newline, sizeof newline);
|
||||
dbg("set line: %d %d %d %d", le32_to_cpu(newline.dwDTERate),
|
||||
dev_dbg(&acm->control->dev, "%s - set line: %d %d %d %d\n",
|
||||
__func__,
|
||||
le32_to_cpu(newline.dwDTERate),
|
||||
newline.bCharFormat, newline.bParityType,
|
||||
newline.bDataBits);
|
||||
acm_set_line(acm, &acm->line);
|
||||
|
@ -897,11 +804,11 @@ static void acm_write_buffers_free(struct acm *acm)
|
|||
static void acm_read_buffers_free(struct acm *acm)
|
||||
{
|
||||
struct usb_device *usb_dev = interface_to_usbdev(acm->control);
|
||||
int i, n = acm->rx_buflimit;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
for (i = 0; i < acm->rx_buflimit; i++)
|
||||
usb_free_coherent(usb_dev, acm->readsize,
|
||||
acm->rb[i].base, acm->rb[i].dma);
|
||||
acm->read_buffers[i].base, acm->read_buffers[i].dma);
|
||||
}
|
||||
|
||||
/* Little helper: write buffers allocate */
|
||||
|
@ -1133,7 +1040,7 @@ static int acm_probe(struct usb_interface *intf,
|
|||
epwrite = t;
|
||||
}
|
||||
made_compressed_probe:
|
||||
dbg("interfaces are valid");
|
||||
dev_dbg(&intf->dev, "interfaces are valid\n");
|
||||
for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++);
|
||||
|
||||
if (minor == ACM_TTY_MINORS) {
|
||||
|
@ -1143,7 +1050,7 @@ static int acm_probe(struct usb_interface *intf,
|
|||
|
||||
acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
|
||||
if (acm == NULL) {
|
||||
dev_dbg(&intf->dev, "out of memory (acm kzalloc)\n");
|
||||
dev_err(&intf->dev, "out of memory (acm kzalloc)\n");
|
||||
goto alloc_fail;
|
||||
}
|
||||
|
||||
|
@ -1162,11 +1069,7 @@ static int acm_probe(struct usb_interface *intf,
|
|||
acm->ctrlsize = ctrlsize;
|
||||
acm->readsize = readsize;
|
||||
acm->rx_buflimit = num_rx_buf;
|
||||
acm->urb_task.func = acm_rx_tasklet;
|
||||
acm->urb_task.data = (unsigned long) acm;
|
||||
INIT_WORK(&acm->work, acm_softint);
|
||||
init_waitqueue_head(&acm->drain_wait);
|
||||
spin_lock_init(&acm->throttle_lock);
|
||||
spin_lock_init(&acm->write_lock);
|
||||
spin_lock_init(&acm->read_lock);
|
||||
mutex_init(&acm->mutex);
|
||||
|
@ -1179,53 +1082,69 @@ static int acm_probe(struct usb_interface *intf,
|
|||
|
||||
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,
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||
urb->transfer_dma = rb->dma;
|
||||
if (acm->is_int_ep) {
|
||||
usb_fill_int_urb(urb, acm->dev,
|
||||
acm->rx_endpoint,
|
||||
rb->base,
|
||||
acm->readsize,
|
||||
acm_read_bulk_callback, rb,
|
||||
acm->bInterval);
|
||||
} else {
|
||||
usb_fill_bulk_urb(urb, acm->dev,
|
||||
acm->rx_endpoint,
|
||||
rb->base,
|
||||
acm->readsize,
|
||||
acm_read_bulk_callback, rb);
|
||||
}
|
||||
|
||||
acm->read_urbs[i] = urb;
|
||||
__set_bit(i, &acm->read_urbs_free);
|
||||
}
|
||||
for (i = 0; i < ACM_NW; i++) {
|
||||
struct acm_wb *snd = &(acm->wb[i]);
|
||||
|
||||
snd->urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (snd->urb == NULL) {
|
||||
dev_dbg(&intf->dev,
|
||||
"out of memory (write urbs usb_alloc_urb)");
|
||||
goto alloc_fail8;
|
||||
dev_err(&intf->dev,
|
||||
"out of memory (write urbs usb_alloc_urb)\n");
|
||||
goto alloc_fail7;
|
||||
}
|
||||
|
||||
if (usb_endpoint_xfer_int(epwrite))
|
||||
|
@ -1244,7 +1163,7 @@ static int acm_probe(struct usb_interface *intf,
|
|||
|
||||
i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
|
||||
if (i < 0)
|
||||
goto alloc_fail8;
|
||||
goto alloc_fail7;
|
||||
|
||||
if (cfd) { /* export the country data */
|
||||
acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
|
||||
|
@ -1296,14 +1215,13 @@ static int acm_probe(struct usb_interface *intf,
|
|||
acm_table[minor] = acm;
|
||||
|
||||
return 0;
|
||||
alloc_fail8:
|
||||
alloc_fail7:
|
||||
for (i = 0; i < ACM_NW; i++)
|
||||
usb_free_urb(acm->wb[i].urb);
|
||||
alloc_fail7:
|
||||
acm_read_buffers_free(acm);
|
||||
alloc_fail6:
|
||||
for (i = 0; i < num_rx_buf; i++)
|
||||
usb_free_urb(acm->ru[i].urb);
|
||||
usb_free_urb(acm->read_urbs[i]);
|
||||
acm_read_buffers_free(acm);
|
||||
usb_free_urb(acm->ctrlurb);
|
||||
alloc_fail5:
|
||||
acm_write_buffers_free(acm);
|
||||
|
@ -1318,17 +1236,14 @@ static int acm_probe(struct usb_interface *intf,
|
|||
static void stop_data_traffic(struct acm *acm)
|
||||
{
|
||||
int i;
|
||||
dbg("Entering stop_data_traffic");
|
||||
|
||||
tasklet_disable(&acm->urb_task);
|
||||
dev_dbg(&acm->control->dev, "%s\n", __func__);
|
||||
|
||||
usb_kill_urb(acm->ctrlurb);
|
||||
for (i = 0; i < ACM_NW; i++)
|
||||
usb_kill_urb(acm->wb[i].urb);
|
||||
for (i = 0; i < acm->rx_buflimit; i++)
|
||||
usb_kill_urb(acm->ru[i].urb);
|
||||
|
||||
tasklet_enable(&acm->urb_task);
|
||||
usb_kill_urb(acm->read_urbs[i]);
|
||||
|
||||
cancel_work_sync(&acm->work);
|
||||
}
|
||||
|
@ -1389,11 +1304,9 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)
|
|||
if (message.event & PM_EVENT_AUTO) {
|
||||
int b;
|
||||
|
||||
spin_lock_irq(&acm->read_lock);
|
||||
spin_lock(&acm->write_lock);
|
||||
b = acm->processing + acm->transmitting;
|
||||
spin_unlock(&acm->write_lock);
|
||||
spin_unlock_irq(&acm->read_lock);
|
||||
spin_lock_irq(&acm->write_lock);
|
||||
b = acm->transmitting;
|
||||
spin_unlock_irq(&acm->write_lock);
|
||||
if (b)
|
||||
return -EBUSY;
|
||||
}
|
||||
|
@ -1455,7 +1368,7 @@ static int acm_resume(struct usb_interface *intf)
|
|||
if (rv < 0)
|
||||
goto err_out;
|
||||
|
||||
tasklet_schedule(&acm->urb_task);
|
||||
rv = acm_submit_read_urbs(acm, GFP_NOIO);
|
||||
}
|
||||
|
||||
err_out:
|
||||
|
@ -1716,8 +1629,7 @@ static int __init acm_init(void)
|
|||
return retval;
|
||||
}
|
||||
|
||||
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
|
||||
DRIVER_DESC "\n");
|
||||
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
};
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -613,6 +613,11 @@ static int fsg_setup(struct usb_function *f,
|
|||
if (!fsg_is_set(fsg->common))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
++fsg->common->ep0_req_tag; /* Record arrival of a new request */
|
||||
req->context = NULL;
|
||||
req->length = 0;
|
||||
dump_msg(fsg, "ep0-setup", (u8 *) ctrl, sizeof(*ctrl));
|
||||
|
||||
switch (ctrl->bRequest) {
|
||||
|
||||
case USB_BULK_RESET_REQUEST:
|
||||
|
@ -1584,37 +1589,6 @@ static int wedge_bulk_in_endpoint(struct fsg_dev *fsg)
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int pad_with_zeros(struct fsg_dev *fsg)
|
||||
{
|
||||
struct fsg_buffhd *bh = fsg->common->next_buffhd_to_fill;
|
||||
u32 nkeep = bh->inreq->length;
|
||||
u32 nsend;
|
||||
int rc;
|
||||
|
||||
bh->state = BUF_STATE_EMPTY; /* For the first iteration */
|
||||
fsg->common->usb_amount_left = nkeep + fsg->common->residue;
|
||||
while (fsg->common->usb_amount_left > 0) {
|
||||
|
||||
/* Wait for the next buffer to be free */
|
||||
while (bh->state != BUF_STATE_EMPTY) {
|
||||
rc = sleep_thread(fsg->common);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
nsend = min(fsg->common->usb_amount_left, FSG_BUFLEN);
|
||||
memset(bh->buf + nkeep, 0, nsend - nkeep);
|
||||
bh->inreq->length = nsend;
|
||||
bh->inreq->zero = 0;
|
||||
start_transfer(fsg, fsg->bulk_in, bh->inreq,
|
||||
&bh->inreq_busy, &bh->state);
|
||||
bh = fsg->common->next_buffhd_to_fill = bh->next;
|
||||
fsg->common->usb_amount_left -= nsend;
|
||||
nkeep = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int throw_away_data(struct fsg_common *common)
|
||||
{
|
||||
struct fsg_buffhd *bh;
|
||||
|
@ -1702,6 +1676,10 @@ static int finish_reply(struct fsg_common *common)
|
|||
if (common->data_size == 0) {
|
||||
/* Nothing to send */
|
||||
|
||||
/* Don't know what to do if common->fsg is NULL */
|
||||
} else if (!fsg_is_set(common)) {
|
||||
rc = -EIO;
|
||||
|
||||
/* If there's no residue, simply send the last buffer */
|
||||
} else if (common->residue == 0) {
|
||||
bh->inreq->zero = 0;
|
||||
|
@ -1710,24 +1688,19 @@ static int finish_reply(struct fsg_common *common)
|
|||
common->next_buffhd_to_fill = bh->next;
|
||||
|
||||
/*
|
||||
* For Bulk-only, if we're allowed to stall then send the
|
||||
* short packet and halt the bulk-in endpoint. If we can't
|
||||
* stall, pad out the remaining data with 0's.
|
||||
* For Bulk-only, mark the end of the data with a short
|
||||
* packet. If we are allowed to stall, halt the bulk-in
|
||||
* endpoint. (Note: This violates the Bulk-Only Transport
|
||||
* specification, which requires us to pad the data if we
|
||||
* don't halt the endpoint. Presumably nobody will mind.)
|
||||
*/
|
||||
} else if (common->can_stall) {
|
||||
} else {
|
||||
bh->inreq->zero = 1;
|
||||
if (!start_in_transfer(common, bh))
|
||||
/* Don't know what to do if
|
||||
* common->fsg is NULL */
|
||||
rc = -EIO;
|
||||
common->next_buffhd_to_fill = bh->next;
|
||||
if (common->fsg)
|
||||
if (common->can_stall)
|
||||
rc = halt_bulk_in_endpoint(common->fsg);
|
||||
} else if (fsg_is_set(common)) {
|
||||
rc = pad_with_zeros(common->fsg);
|
||||
} else {
|
||||
/* Don't know what to do if common->fsg is NULL */
|
||||
rc = -EIO;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -2800,6 +2773,7 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
|
|||
for (i = 0, lcfg = cfg->luns; i < nluns; ++i, ++curlun, ++lcfg) {
|
||||
curlun->cdrom = !!lcfg->cdrom;
|
||||
curlun->ro = lcfg->cdrom || lcfg->ro;
|
||||
curlun->initially_ro = curlun->ro;
|
||||
curlun->removable = lcfg->removable;
|
||||
curlun->dev.release = fsg_lun_release;
|
||||
curlun->dev.parent = &gadget->dev;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -148,6 +148,12 @@
|
|||
#define gadget_is_ci13xxx_msm(g) 0
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_GADGET_RENESAS_USBHS
|
||||
#define gadget_is_renesas_usbhs(g) (!strcmp("renesas_usbhs_udc", (g)->name))
|
||||
#else
|
||||
#define gadget_is_renesas_usbhs(g) 0
|
||||
#endif
|
||||
|
||||
/**
|
||||
* usb_gadget_controller_number - support bcdDevice id convention
|
||||
* @gadget: the controller being driven
|
||||
|
@ -207,6 +213,9 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
|
|||
return 0x27;
|
||||
else if (gadget_is_ci13xxx_msm(gadget))
|
||||
return 0x28;
|
||||
else if (gadget_is_renesas_usbhs(gadget))
|
||||
return 0x29;
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
|
|
|
@ -711,10 +711,11 @@ static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr,
|
|||
ssize_t rc = count;
|
||||
struct fsg_lun *curlun = fsg_lun_from_dev(dev);
|
||||
struct rw_semaphore *filesem = dev_get_drvdata(dev);
|
||||
unsigned long ro;
|
||||
unsigned ro;
|
||||
|
||||
if (strict_strtoul(buf, 2, &ro))
|
||||
return -EINVAL;
|
||||
rc = kstrtouint(buf, 2, &ro);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/*
|
||||
* Allow the write-enable status to change only while the
|
||||
|
@ -738,10 +739,12 @@ static ssize_t fsg_store_nofua(struct device *dev,
|
|||
const char *buf, size_t count)
|
||||
{
|
||||
struct fsg_lun *curlun = fsg_lun_from_dev(dev);
|
||||
unsigned long nofua;
|
||||
unsigned nofua;
|
||||
int ret;
|
||||
|
||||
if (strict_strtoul(buf, 2, &nofua))
|
||||
return -EINVAL;
|
||||
ret = kstrtouint(buf, 2, &nofua);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Sync data when switching from async mode to sync */
|
||||
if (!nofua && curlun->nofua)
|
||||
|
|
|
@ -188,6 +188,12 @@ config USB_EHCI_SH
|
|||
Enables support for the on-chip EHCI controller on the SuperH.
|
||||
If you use the PCI EHCI controller, this option is not necessary.
|
||||
|
||||
config USB_EHCI_S5P
|
||||
boolean "S5P EHCI support"
|
||||
depends on USB_EHCI_HCD && PLAT_S5P
|
||||
help
|
||||
Enable support for the S5P SOC's on-chip EHCI controller.
|
||||
|
||||
config USB_W90X900_EHCI
|
||||
bool "W90X900(W90P910) EHCI support"
|
||||
depends on USB_EHCI_HCD && ARCH_W90X900
|
||||
|
@ -202,6 +208,15 @@ config USB_CNS3XXX_EHCI
|
|||
It is needed for high-speed (480Mbit/sec) USB 2.0 device
|
||||
support.
|
||||
|
||||
config USB_EHCI_ATH79
|
||||
bool "EHCI support for AR7XXX/AR9XXX SoCs"
|
||||
depends on USB_EHCI_HCD && (SOC_AR71XX || SOC_AR724X || SOC_AR913X)
|
||||
select USB_EHCI_ROOT_HUB_TT
|
||||
default y
|
||||
---help---
|
||||
Enables support for the built-in EHCI controller present
|
||||
on the Atheros AR7XXX/AR9XXX SoCs.
|
||||
|
||||
config USB_OXU210HP_HCD
|
||||
tristate "OXU210HP HCD support"
|
||||
depends on USB
|
||||
|
@ -287,6 +302,14 @@ config USB_OHCI_HCD_OMAP3
|
|||
Enables support for the on-chip OHCI controller on
|
||||
OMAP3 and later chips.
|
||||
|
||||
config USB_OHCI_ATH79
|
||||
bool "USB OHCI support for the Atheros AR71XX/AR7240 SoCs"
|
||||
depends on USB_OHCI_HCD && (SOC_AR71XX || SOC_AR724X)
|
||||
default y
|
||||
help
|
||||
Enables support for the built-in OHCI controller present on the
|
||||
Atheros AR71XX/AR7240 SoCs.
|
||||
|
||||
config USB_OHCI_HCD_PPC_SOC
|
||||
bool "OHCI support for on-chip PPC USB controller"
|
||||
depends on USB_OHCI_HCD && (STB03xxx || PPC_MPC52xx)
|
||||
|
@ -444,6 +467,16 @@ config USB_SL811_HCD
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called sl811-hcd.
|
||||
|
||||
config USB_SL811_HCD_ISO
|
||||
bool "partial ISO support"
|
||||
depends on USB_SL811_HCD
|
||||
help
|
||||
The driver doesn't support iso_frame_desc (yet), but for some simple
|
||||
devices that just queue one ISO frame per URB, then ISO transfers
|
||||
"should" work using the normal urb status fields.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config USB_SL811_CS
|
||||
tristate "CF/PCMCIA support for SL811HS HCD"
|
||||
depends on USB_SL811_HCD && PCMCIA
|
||||
|
|
202
drivers/usb/host/ehci-ath79.c
Normal file
202
drivers/usb/host/ehci-ath79.c
Normal file
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* Bus Glue for Atheros AR7XXX/AR9XXX built-in EHCI controller.
|
||||
*
|
||||
* Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
|
||||
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
|
||||
*
|
||||
* Parts of this file are based on Atheros' 2.6.15 BSP
|
||||
* Copyright (C) 2007 Atheros Communications, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published
|
||||
* by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
enum {
|
||||
EHCI_ATH79_IP_V1 = 0,
|
||||
EHCI_ATH79_IP_V2,
|
||||
};
|
||||
|
||||
static const struct platform_device_id ehci_ath79_id_table[] = {
|
||||
{
|
||||
.name = "ar71xx-ehci",
|
||||
.driver_data = EHCI_ATH79_IP_V1,
|
||||
},
|
||||
{
|
||||
.name = "ar724x-ehci",
|
||||
.driver_data = EHCI_ATH79_IP_V2,
|
||||
},
|
||||
{
|
||||
.name = "ar913x-ehci",
|
||||
.driver_data = EHCI_ATH79_IP_V2,
|
||||
},
|
||||
{
|
||||
/* terminating entry */
|
||||
},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(platform, ehci_ath79_id_table);
|
||||
|
||||
static int ehci_ath79_init(struct usb_hcd *hcd)
|
||||
{
|
||||
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
|
||||
struct platform_device *pdev = to_platform_device(hcd->self.controller);
|
||||
const struct platform_device_id *id;
|
||||
int ret;
|
||||
|
||||
id = platform_get_device_id(pdev);
|
||||
if (!id) {
|
||||
dev_err(hcd->self.controller, "missing device id\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (id->driver_data) {
|
||||
case EHCI_ATH79_IP_V1:
|
||||
ehci->has_synopsys_hc_bug = 1;
|
||||
|
||||
ehci->caps = hcd->regs;
|
||||
ehci->regs = hcd->regs +
|
||||
HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
|
||||
break;
|
||||
|
||||
case EHCI_ATH79_IP_V2:
|
||||
hcd->has_tt = 1;
|
||||
|
||||
ehci->caps = hcd->regs + 0x100;
|
||||
ehci->regs = hcd->regs + 0x100 +
|
||||
HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
|
||||
break;
|
||||
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
dbg_hcs_params(ehci, "reset");
|
||||
dbg_hcc_params(ehci, "reset");
|
||||
ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
|
||||
ehci->sbrn = 0x20;
|
||||
|
||||
ehci_reset(ehci);
|
||||
|
||||
ret = ehci_init(hcd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ehci_port_power(ehci, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct hc_driver ehci_ath79_hc_driver = {
|
||||
.description = hcd_name,
|
||||
.product_desc = "Atheros built-in EHCI controller",
|
||||
.hcd_priv_size = sizeof(struct ehci_hcd),
|
||||
.irq = ehci_irq,
|
||||
.flags = HCD_MEMORY | HCD_USB2,
|
||||
|
||||
.reset = ehci_ath79_init,
|
||||
.start = ehci_run,
|
||||
.stop = ehci_stop,
|
||||
.shutdown = ehci_shutdown,
|
||||
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
.get_frame_number = ehci_get_frame,
|
||||
|
||||
.hub_status_data = ehci_hub_status_data,
|
||||
.hub_control = ehci_hub_control,
|
||||
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
|
||||
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
|
||||
};
|
||||
|
||||
static int ehci_ath79_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_hcd *hcd;
|
||||
struct resource *res;
|
||||
int irq;
|
||||
int ret;
|
||||
|
||||
if (usb_disabled())
|
||||
return -ENODEV;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (!res) {
|
||||
dev_dbg(&pdev->dev, "no IRQ specified\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
irq = res->start;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_dbg(&pdev->dev, "no base address specified\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
hcd = usb_create_hcd(&ehci_ath79_hc_driver, &pdev->dev,
|
||||
dev_name(&pdev->dev));
|
||||
if (!hcd)
|
||||
return -ENOMEM;
|
||||
|
||||
hcd->rsrc_start = res->start;
|
||||
hcd->rsrc_len = res->end - res->start + 1;
|
||||
|
||||
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
|
||||
dev_dbg(&pdev->dev, "controller already in use\n");
|
||||
ret = -EBUSY;
|
||||
goto err_put_hcd;
|
||||
}
|
||||
|
||||
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
|
||||
if (!hcd->regs) {
|
||||
dev_dbg(&pdev->dev, "error mapping memory\n");
|
||||
ret = -EFAULT;
|
||||
goto err_release_region;
|
||||
}
|
||||
|
||||
ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
|
||||
if (ret)
|
||||
goto err_iounmap;
|
||||
|
||||
return 0;
|
||||
|
||||
err_iounmap:
|
||||
iounmap(hcd->regs);
|
||||
|
||||
err_release_region:
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
err_put_hcd:
|
||||
usb_put_hcd(hcd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ehci_ath79_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct usb_hcd *hcd = platform_get_drvdata(pdev);
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
iounmap(hcd->regs);
|
||||
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
|
||||
usb_put_hcd(hcd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ehci_ath79_driver = {
|
||||
.probe = ehci_ath79_probe,
|
||||
.remove = ehci_ath79_remove,
|
||||
.id_table = ehci_ath79_id_table,
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "ath79-ehci",
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_ALIAS(PLATFORM_MODULE_PREFIX "ath79-ehci");
|
|
@ -1265,6 +1265,16 @@ MODULE_LICENSE ("GPL");
|
|||
#define PLATFORM_DRIVER tegra_ehci_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_EHCI_S5P
|
||||
#include "ehci-s5p.c"
|
||||
#define PLATFORM_DRIVER s5p_ehci_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_EHCI_ATH79
|
||||
#include "ehci-ath79.c"
|
||||
#define PLATFORM_DRIVER ehci_ath79_driver
|
||||
#endif
|
||||
|
||||
#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
|
||||
!defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \
|
||||
!defined(XILINX_OF_PLATFORM_DRIVER)
|
||||
|
|
|
@ -127,7 +127,7 @@ static int ehci_port_change(struct ehci_hcd *ehci)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
|
||||
static __maybe_unused void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci,
|
||||
bool suspending, bool do_wakeup)
|
||||
{
|
||||
int port;
|
||||
|
|
|
@ -1183,6 +1183,10 @@ static void end_unlink_async (struct ehci_hcd *ehci)
|
|||
ehci->reclaim = NULL;
|
||||
start_unlink_async (ehci, next);
|
||||
}
|
||||
|
||||
if (ehci->has_synopsys_hc_bug)
|
||||
ehci_writel(ehci, (u32) ehci->async->qh_dma,
|
||||
&ehci->regs->async_next);
|
||||
}
|
||||
|
||||
/* makes sure the async qh will become idle */
|
||||
|
|
201
drivers/usb/host/ehci-s5p.c
Normal file
201
drivers/usb/host/ehci-s5p.c
Normal file
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* SAMSUNG S5P USB HOST EHCI Controller
|
||||
*
|
||||
* Copyright (C) 2011 Samsung Electronics Co.Ltd
|
||||
* Author: Jingoo Han <jg1.han@samsung.com>
|
||||
* Author: Joonyoung Shim <jy0922.shim@samsung.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <mach/regs-pmu.h>
|
||||
#include <plat/cpu.h>
|
||||
#include <plat/ehci.h>
|
||||
#include <plat/usb-phy.h>
|
||||
|
||||
struct s5p_ehci_hcd {
|
||||
struct device *dev;
|
||||
struct usb_hcd *hcd;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static const struct hc_driver s5p_ehci_hc_driver = {
|
||||
.description = hcd_name,
|
||||
.product_desc = "S5P EHCI Host Controller",
|
||||
.hcd_priv_size = sizeof(struct ehci_hcd),
|
||||
|
||||
.irq = ehci_irq,
|
||||
.flags = HCD_MEMORY | HCD_USB2,
|
||||
|
||||
.reset = ehci_init,
|
||||
.start = ehci_run,
|
||||
.stop = ehci_stop,
|
||||
.shutdown = ehci_shutdown,
|
||||
|
||||
.get_frame_number = ehci_get_frame,
|
||||
|
||||
.urb_enqueue = ehci_urb_enqueue,
|
||||
.urb_dequeue = ehci_urb_dequeue,
|
||||
.endpoint_disable = ehci_endpoint_disable,
|
||||
.endpoint_reset = ehci_endpoint_reset,
|
||||
|
||||
.hub_status_data = ehci_hub_status_data,
|
||||
.hub_control = ehci_hub_control,
|
||||
.bus_suspend = ehci_bus_suspend,
|
||||
.bus_resume = ehci_bus_resume,
|
||||
|
||||
.relinquish_port = ehci_relinquish_port,
|
||||
.port_handed_over = ehci_port_handed_over,
|
||||
|
||||
.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
|
||||
};
|
||||
|
||||
static int s5p_ehci_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct s5p_ehci_platdata *pdata;
|
||||
struct s5p_ehci_hcd *s5p_ehci;
|
||||
struct usb_hcd *hcd;
|
||||
struct ehci_hcd *ehci;
|
||||
struct resource *res;
|
||||
int irq;
|
||||
int err;
|
||||
|
||||
pdata = pdev->dev.platform_data;
|
||||
if (!pdata) {
|
||||
dev_err(&pdev->dev, "No platform data defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
s5p_ehci = kzalloc(sizeof(struct s5p_ehci_hcd), GFP_KERNEL);
|
||||
if (!s5p_ehci)
|
||||
return -ENOMEM;
|
||||
|
||||
s5p_ehci->dev = &pdev->dev;
|
||||
|
||||
hcd = usb_create_hcd(&s5p_ehci_hc_driver, &pdev->dev,
|
||||
dev_name(&pdev->dev));
|
||||
if (!hcd) {
|
||||
dev_err(&pdev->dev, "Unable to create HCD\n");
|
||||
err = -ENOMEM;
|
||||
goto fail_hcd;
|
||||
}
|
||||
|
||||
s5p_ehci->clk = clk_get(&pdev->dev, "usbhost");
|
||||
|
||||
if (IS_ERR(s5p_ehci->clk)) {
|
||||
dev_err(&pdev->dev, "Failed to get usbhost clock\n");
|
||||
err = PTR_ERR(s5p_ehci->clk);
|
||||
goto fail_clk;
|
||||
}
|
||||
|
||||
err = clk_enable(s5p_ehci->clk);
|
||||
if (err)
|
||||
goto fail_clken;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get I/O memory\n");
|
||||
err = -ENXIO;
|
||||
goto fail_io;
|
||||
}
|
||||
|
||||
hcd->rsrc_start = res->start;
|
||||
hcd->rsrc_len = resource_size(res);
|
||||
hcd->regs = ioremap(res->start, resource_size(res));
|
||||
if (!hcd->regs) {
|
||||
dev_err(&pdev->dev, "Failed to remap I/O memory\n");
|
||||
err = -ENOMEM;
|
||||
goto fail_io;
|
||||
}
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (!irq) {
|
||||
dev_err(&pdev->dev, "Failed to get IRQ\n");
|
||||
err = -ENODEV;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (pdata->phy_init)
|
||||
pdata->phy_init(pdev, S5P_USB_PHY_HOST);
|
||||
|
||||
ehci = hcd_to_ehci(hcd);
|
||||
ehci->caps = hcd->regs;
|
||||
ehci->regs = hcd->regs + HC_LENGTH(readl(&ehci->caps->hc_capbase));
|
||||
|
||||
dbg_hcs_params(ehci, "reset");
|
||||
dbg_hcc_params(ehci, "reset");
|
||||
|
||||
/* cache this readonly data; minimize chip reads */
|
||||
ehci->hcs_params = readl(&ehci->caps->hcs_params);
|
||||
|
||||
err = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "Failed to add USB HCD\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, s5p_ehci);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
iounmap(hcd->regs);
|
||||
fail_io:
|
||||
clk_disable(s5p_ehci->clk);
|
||||
fail_clken:
|
||||
clk_put(s5p_ehci->clk);
|
||||
fail_clk:
|
||||
usb_put_hcd(hcd);
|
||||
fail_hcd:
|
||||
kfree(s5p_ehci);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int s5p_ehci_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct s5p_ehci_platdata *pdata = pdev->dev.platform_data;
|
||||
struct s5p_ehci_hcd *s5p_ehci = platform_get_drvdata(pdev);
|
||||
struct usb_hcd *hcd = s5p_ehci->hcd;
|
||||
|
||||
usb_remove_hcd(hcd);
|
||||
|
||||
if (pdata && pdata->phy_exit)
|
||||
pdata->phy_exit(pdev, S5P_USB_PHY_HOST);
|
||||
|
||||
iounmap(hcd->regs);
|
||||
|
||||
clk_disable(s5p_ehci->clk);
|
||||
clk_put(s5p_ehci->clk);
|
||||
|
||||
usb_put_hcd(hcd);
|
||||
kfree(s5p_ehci);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void s5p_ehci_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
struct s5p_ehci_hcd *s5p_ehci = platform_get_drvdata(pdev);
|
||||
struct usb_hcd *hcd = s5p_ehci->hcd;
|
||||
|
||||
if (hcd->driver->shutdown)
|
||||
hcd->driver->shutdown(hcd);
|
||||
}
|
||||
|
||||
static struct platform_driver s5p_ehci_driver = {
|
||||
.probe = s5p_ehci_probe,
|
||||
.remove = s5p_ehci_remove,
|
||||
.shutdown = s5p_ehci_shutdown,
|
||||
.driver = {
|
||||
.name = "s5p-ehci",
|
||||
.owner = THIS_MODULE,
|
||||
}
|
||||
};
|
||||
|
||||
MODULE_ALIAS("platform:s5p-ehci");
|
|
@ -134,6 +134,7 @@ struct ehci_hcd { /* one per controller */
|
|||
unsigned amd_pll_fix:1;
|
||||
unsigned fs_i_thresh:1; /* Intel iso scheduling */
|
||||
unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/
|
||||
unsigned has_synopsys_hc_bug:1; /* Synopsys HC */
|
||||
|
||||
/* required for usb32 quirk */
|
||||
#define OHCI_CTRL_HCFS (3 << 6)
|
||||
|
|
151
drivers/usb/host/ohci-ath79.c
Normal file
151
drivers/usb/host/ohci-ath79.c
Normal file
|
@ -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");
|
|
@ -1105,6 +1105,11 @@ MODULE_LICENSE ("GPL");
|
|||
#define PLATFORM_DRIVER ohci_hcd_cns3xxx_driver
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_USB_OHCI_ATH79
|
||||
#include "ohci-ath79.c"
|
||||
#define PLATFORM_DRIVER ohci_hcd_ath79_driver
|
||||
#endif
|
||||
|
||||
#if !defined(PCI_DRIVER) && \
|
||||
!defined(PLATFORM_DRIVER) && \
|
||||
!defined(OMAP1_PLATFORM_DRIVER) && \
|
||||
|
|
|
@ -71,12 +71,6 @@ MODULE_ALIAS("platform:sl811-hcd");
|
|||
/* for now, use only one transfer register bank */
|
||||
#undef USE_B
|
||||
|
||||
/* this doesn't understand urb->iso_frame_desc[], but if you had a driver
|
||||
* that just queued one ISO frame per URB then iso transfers "should" work
|
||||
* using the normal urb status fields.
|
||||
*/
|
||||
#define DISABLE_ISO
|
||||
|
||||
// #define QUIRK2
|
||||
#define QUIRK3
|
||||
|
||||
|
@ -807,7 +801,7 @@ static int sl811h_urb_enqueue(
|
|||
int retval;
|
||||
struct usb_host_endpoint *hep = urb->ep;
|
||||
|
||||
#ifdef DISABLE_ISO
|
||||
#ifndef CONFIG_USB_SL811_HCD_ISO
|
||||
if (type == PIPE_ISOCHRONOUS)
|
||||
return -ENOSPC;
|
||||
#endif
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -139,10 +139,7 @@ static void finish_reset(struct uhci_hcd *uhci)
|
|||
uhci->port_c_suspend = uhci->resuming_ports = 0;
|
||||
uhci->rh_state = UHCI_RH_RESET;
|
||||
uhci->is_stopped = UHCI_IS_STOPPED;
|
||||
uhci_to_hcd(uhci)->state = HC_STATE_HALT;
|
||||
clear_bit(HCD_FLAG_POLL_RH, &uhci_to_hcd(uhci)->flags);
|
||||
|
||||
uhci->dead = 0; /* Full reset resurrects the controller */
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -188,10 +185,6 @@ static void configure_hc(struct uhci_hcd *uhci)
|
|||
outw(uhci->frame_number & UHCI_MAX_SOF_NUMBER,
|
||||
uhci->io_addr + USBFRNUM);
|
||||
|
||||
/* Mark controller as not halted before we enable interrupts */
|
||||
uhci_to_hcd(uhci)->state = HC_STATE_SUSPENDED;
|
||||
mb();
|
||||
|
||||
/* Enable PIRQ */
|
||||
pci_write_config_word(pdev, USBLEGSUP, USBLEGSUP_DEFAULT);
|
||||
|
||||
|
@ -360,7 +353,6 @@ __acquires(uhci->lock)
|
|||
|
||||
static void start_rh(struct uhci_hcd *uhci)
|
||||
{
|
||||
uhci_to_hcd(uhci)->state = HC_STATE_RUNNING;
|
||||
uhci->is_stopped = 0;
|
||||
|
||||
/* Mark it configured and running with a 64-byte max packet.
|
||||
|
@ -449,6 +441,7 @@ static irqreturn_t uhci_irq(struct usb_hcd *hcd)
|
|||
lprintk(errbuf);
|
||||
}
|
||||
uhci_hc_died(uhci);
|
||||
usb_hc_died(hcd);
|
||||
|
||||
/* Force a callback in case there are
|
||||
* pending unlinks */
|
||||
|
@ -842,16 +835,17 @@ static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated)
|
|||
spin_lock_irq(&uhci->lock);
|
||||
|
||||
/* Make sure resume from hibernation re-enumerates everything */
|
||||
if (hibernated)
|
||||
uhci_hc_died(uhci);
|
||||
if (hibernated) {
|
||||
uhci_reset_hc(to_pci_dev(uhci_dev(uhci)), uhci->io_addr);
|
||||
finish_reset(uhci);
|
||||
}
|
||||
|
||||
/* The firmware or a boot kernel may have changed the controller
|
||||
* settings during a system wakeup. Check it and reconfigure
|
||||
* to avoid problems.
|
||||
/* The firmware may have changed the controller settings during
|
||||
* a system wakeup. Check it and reconfigure to avoid problems.
|
||||
*/
|
||||
check_and_reset_hc(uhci);
|
||||
|
||||
/* If the controller was dead before, it's back alive now */
|
||||
else {
|
||||
check_and_reset_hc(uhci);
|
||||
}
|
||||
configure_hc(uhci);
|
||||
|
||||
/* Tell the core if the controller had to be reset */
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -101,7 +101,7 @@ struct twl6030_usb {
|
|||
bool irq_enabled;
|
||||
};
|
||||
|
||||
#define xceiv_to_twl(x) container_of((x), struct twl6030_usb, otg);
|
||||
#define xceiv_to_twl(x) container_of((x), struct twl6030_usb, otg)
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
|
15
drivers/usb/renesas_usbhs/Kconfig
Normal file
15
drivers/usb/renesas_usbhs/Kconfig
Normal file
|
@ -0,0 +1,15 @@
|
|||
#
|
||||
# Renesas USB Controller Drivers
|
||||
#
|
||||
|
||||
config USB_RENESAS_USBHS
|
||||
tristate 'Renesas USBHS controller'
|
||||
default n
|
||||
help
|
||||
Renesas USBHS is a discrete USB host and peripheral controller chip
|
||||
that supports both full and high speed USB 2.0 data transfers.
|
||||
It has nine or more configurable endpoints, and endpoint zero.
|
||||
|
||||
Say "y" to link the driver statically, or "m" to build a
|
||||
dynamically linked module called "renesas_usbhs" and force all
|
||||
gadget drivers to also be dynamically linked.
|
9
drivers/usb/renesas_usbhs/Makefile
Normal file
9
drivers/usb/renesas_usbhs/Makefile
Normal file
|
@ -0,0 +1,9 @@
|
|||
#
|
||||
# for Renesas USB
|
||||
#
|
||||
|
||||
obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs.o
|
||||
|
||||
renesas_usbhs-y := common.o mod.o pipe.o
|
||||
|
||||
renesas_usbhs-$(CONFIG_USB_RENESAS_USBHS_UDC) += mod_gadget.o
|
394
drivers/usb/renesas_usbhs/common.c
Normal file
394
drivers/usb/renesas_usbhs/common.c
Normal file
|
@ -0,0 +1,394 @@
|
|||
/*
|
||||
* Renesas USB driver
|
||||
*
|
||||
* Copyright (C) 2011 Renesas Solutions Corp.
|
||||
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include "./common.h"
|
||||
|
||||
/*
|
||||
* platform call back
|
||||
*
|
||||
* renesas usb support platform callback function.
|
||||
* Below macro call it.
|
||||
* if platform doesn't have callback, it return 0 (no error)
|
||||
*/
|
||||
#define usbhs_platform_call(priv, func, args...)\
|
||||
(!(priv) ? -ENODEV : \
|
||||
!((priv)->pfunc->func) ? 0 : \
|
||||
(priv)->pfunc->func(args))
|
||||
|
||||
/*
|
||||
* common functions
|
||||
*/
|
||||
u16 usbhs_read(struct usbhs_priv *priv, u32 reg)
|
||||
{
|
||||
return ioread16(priv->base + reg);
|
||||
}
|
||||
|
||||
void usbhs_write(struct usbhs_priv *priv, u32 reg, u16 data)
|
||||
{
|
||||
iowrite16(data, priv->base + reg);
|
||||
}
|
||||
|
||||
void usbhs_bset(struct usbhs_priv *priv, u32 reg, u16 mask, u16 data)
|
||||
{
|
||||
u16 val = usbhs_read(priv, reg);
|
||||
|
||||
val &= ~mask;
|
||||
val |= data & mask;
|
||||
|
||||
usbhs_write(priv, reg, val);
|
||||
}
|
||||
|
||||
/*
|
||||
* syscfg functions
|
||||
*/
|
||||
void usbhs_sys_clock_ctrl(struct usbhs_priv *priv, int enable)
|
||||
{
|
||||
usbhs_bset(priv, SYSCFG, SCKE, enable ? SCKE : 0);
|
||||
}
|
||||
|
||||
void usbhs_sys_hispeed_ctrl(struct usbhs_priv *priv, int enable)
|
||||
{
|
||||
usbhs_bset(priv, SYSCFG, HSE, enable ? HSE : 0);
|
||||
}
|
||||
|
||||
void usbhs_sys_usb_ctrl(struct usbhs_priv *priv, int enable)
|
||||
{
|
||||
usbhs_bset(priv, SYSCFG, USBE, enable ? USBE : 0);
|
||||
}
|
||||
|
||||
void usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable)
|
||||
{
|
||||
u16 mask = DCFM | DRPD | DPRPU;
|
||||
u16 val = DCFM | DRPD;
|
||||
|
||||
/*
|
||||
* if enable
|
||||
*
|
||||
* - select Host mode
|
||||
* - D+ Line/D- Line Pull-down
|
||||
*/
|
||||
usbhs_bset(priv, SYSCFG, mask, enable ? val : 0);
|
||||
}
|
||||
|
||||
void usbhs_sys_function_ctrl(struct usbhs_priv *priv, int enable)
|
||||
{
|
||||
u16 mask = DCFM | DRPD | DPRPU;
|
||||
u16 val = DPRPU;
|
||||
|
||||
/*
|
||||
* if enable
|
||||
*
|
||||
* - select Function mode
|
||||
* - D+ Line Pull-up
|
||||
*/
|
||||
usbhs_bset(priv, SYSCFG, mask, enable ? val : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* frame functions
|
||||
*/
|
||||
int usbhs_frame_get_num(struct usbhs_priv *priv)
|
||||
{
|
||||
return usbhs_read(priv, FRMNUM) & FRNM_MASK;
|
||||
}
|
||||
|
||||
/*
|
||||
* local functions
|
||||
*/
|
||||
static struct usbhs_priv *usbhsc_pdev_to_priv(struct platform_device *pdev)
|
||||
{
|
||||
return dev_get_drvdata(&pdev->dev);
|
||||
}
|
||||
|
||||
static void usbhsc_bus_ctrl(struct usbhs_priv *priv, int enable)
|
||||
{
|
||||
int wait = usbhs_get_dparam(priv, buswait_bwait);
|
||||
u16 data = 0;
|
||||
|
||||
if (enable) {
|
||||
/* set bus wait if platform have */
|
||||
if (wait)
|
||||
usbhs_bset(priv, BUSWAIT, 0x000F, wait);
|
||||
}
|
||||
usbhs_write(priv, DVSTCTR, data);
|
||||
}
|
||||
|
||||
/*
|
||||
* platform default param
|
||||
*/
|
||||
static u32 usbhsc_default_pipe_type[] = {
|
||||
USB_ENDPOINT_XFER_CONTROL,
|
||||
USB_ENDPOINT_XFER_ISOC,
|
||||
USB_ENDPOINT_XFER_ISOC,
|
||||
USB_ENDPOINT_XFER_BULK,
|
||||
USB_ENDPOINT_XFER_BULK,
|
||||
USB_ENDPOINT_XFER_BULK,
|
||||
USB_ENDPOINT_XFER_INT,
|
||||
USB_ENDPOINT_XFER_INT,
|
||||
USB_ENDPOINT_XFER_INT,
|
||||
USB_ENDPOINT_XFER_INT,
|
||||
};
|
||||
|
||||
/*
|
||||
* driver callback functions
|
||||
*/
|
||||
static void usbhsc_notify_hotplug(struct work_struct *work)
|
||||
{
|
||||
struct usbhs_priv *priv = container_of(work,
|
||||
struct usbhs_priv,
|
||||
notify_hotplug_work);
|
||||
struct platform_device *pdev = usbhs_priv_to_pdev(priv);
|
||||
struct usbhs_mod *mod = usbhs_mod_get_current(priv);
|
||||
int id;
|
||||
int enable;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* get vbus status from platform
|
||||
*/
|
||||
enable = usbhs_platform_call(priv, get_vbus, pdev);
|
||||
|
||||
/*
|
||||
* get id from platform
|
||||
*/
|
||||
id = usbhs_platform_call(priv, get_id, pdev);
|
||||
|
||||
if (enable && !mod) {
|
||||
ret = usbhs_mod_change(priv, id);
|
||||
if (ret < 0)
|
||||
return;
|
||||
|
||||
dev_dbg(&pdev->dev, "%s enable\n", __func__);
|
||||
|
||||
/* enable PM */
|
||||
pm_runtime_get_sync(&pdev->dev);
|
||||
|
||||
/* USB on */
|
||||
usbhs_sys_clock_ctrl(priv, enable);
|
||||
usbhsc_bus_ctrl(priv, enable);
|
||||
|
||||
/* module start */
|
||||
usbhs_mod_call(priv, start, priv);
|
||||
|
||||
} else if (!enable && mod) {
|
||||
dev_dbg(&pdev->dev, "%s disable\n", __func__);
|
||||
|
||||
/* module stop */
|
||||
usbhs_mod_call(priv, stop, priv);
|
||||
|
||||
/* USB off */
|
||||
usbhsc_bus_ctrl(priv, enable);
|
||||
usbhs_sys_clock_ctrl(priv, enable);
|
||||
|
||||
/* disable PM */
|
||||
pm_runtime_put_sync(&pdev->dev);
|
||||
|
||||
usbhs_mod_change(priv, -1);
|
||||
|
||||
/* reset phy for next connection */
|
||||
usbhs_platform_call(priv, phy_reset, pdev);
|
||||
}
|
||||
}
|
||||
|
||||
static int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev)
|
||||
{
|
||||
struct usbhs_priv *priv = usbhsc_pdev_to_priv(pdev);
|
||||
|
||||
/*
|
||||
* This functions will be called in interrupt.
|
||||
* To make sure safety context,
|
||||
* use workqueue for usbhs_notify_hotplug
|
||||
*/
|
||||
schedule_work(&priv->notify_hotplug_work);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* platform functions
|
||||
*/
|
||||
static int __devinit usbhs_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct renesas_usbhs_platform_info *info = pdev->dev.platform_data;
|
||||
struct renesas_usbhs_driver_callback *dfunc;
|
||||
struct usbhs_priv *priv;
|
||||
struct resource *res;
|
||||
unsigned int irq;
|
||||
int ret;
|
||||
|
||||
/* check platform information */
|
||||
if (!info ||
|
||||
!info->platform_callback.get_id ||
|
||||
!info->platform_callback.get_vbus) {
|
||||
dev_err(&pdev->dev, "no platform information\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* platform data */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (!res || (int)irq <= 0) {
|
||||
dev_err(&pdev->dev, "Not enough Renesas USB platform resources.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* usb private data */
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
dev_err(&pdev->dev, "Could not allocate priv\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv->base = ioremap_nocache(res->start, resource_size(res));
|
||||
if (!priv->base) {
|
||||
dev_err(&pdev->dev, "ioremap error.\n");
|
||||
ret = -ENOMEM;
|
||||
goto probe_end_kfree;
|
||||
}
|
||||
|
||||
/*
|
||||
* care platform info
|
||||
*/
|
||||
priv->pfunc = &info->platform_callback;
|
||||
priv->dparam = &info->driver_param;
|
||||
|
||||
/* set driver callback functions for platform */
|
||||
dfunc = &info->driver_callback;
|
||||
dfunc->notify_hotplug = usbhsc_drvcllbck_notify_hotplug;
|
||||
|
||||
/* set default param if platform doesn't have */
|
||||
if (!priv->dparam->pipe_type) {
|
||||
priv->dparam->pipe_type = usbhsc_default_pipe_type;
|
||||
priv->dparam->pipe_size = ARRAY_SIZE(usbhsc_default_pipe_type);
|
||||
}
|
||||
|
||||
/*
|
||||
* priv settings
|
||||
*/
|
||||
priv->irq = irq;
|
||||
priv->pdev = pdev;
|
||||
INIT_WORK(&priv->notify_hotplug_work, usbhsc_notify_hotplug);
|
||||
spin_lock_init(usbhs_priv_to_lock(priv));
|
||||
|
||||
/* call pipe and module init */
|
||||
ret = usbhs_pipe_probe(priv);
|
||||
if (ret < 0)
|
||||
goto probe_end_mod_exit;
|
||||
|
||||
ret = usbhs_mod_probe(priv);
|
||||
if (ret < 0)
|
||||
goto probe_end_iounmap;
|
||||
|
||||
/* dev_set_drvdata should be called after usbhs_mod_init */
|
||||
dev_set_drvdata(&pdev->dev, priv);
|
||||
|
||||
/*
|
||||
* deviece reset here because
|
||||
* USB device might be used in boot loader.
|
||||
*/
|
||||
usbhs_sys_clock_ctrl(priv, 0);
|
||||
|
||||
/*
|
||||
* platform call
|
||||
*
|
||||
* USB phy setup might depend on CPU/Board.
|
||||
* If platform has its callback functions,
|
||||
* call it here.
|
||||
*/
|
||||
ret = usbhs_platform_call(priv, hardware_init, pdev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "platform prove failed.\n");
|
||||
goto probe_end_pipe_exit;
|
||||
}
|
||||
|
||||
/* reset phy for connection */
|
||||
usbhs_platform_call(priv, phy_reset, pdev);
|
||||
|
||||
/*
|
||||
* manual call notify_hotplug for cold plug
|
||||
*/
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
ret = usbhsc_drvcllbck_notify_hotplug(pdev);
|
||||
if (ret < 0)
|
||||
goto probe_end_call_remove;
|
||||
|
||||
dev_info(&pdev->dev, "probed\n");
|
||||
|
||||
return ret;
|
||||
|
||||
probe_end_call_remove:
|
||||
usbhs_platform_call(priv, hardware_exit, pdev);
|
||||
probe_end_pipe_exit:
|
||||
usbhs_pipe_remove(priv);
|
||||
probe_end_mod_exit:
|
||||
usbhs_mod_remove(priv);
|
||||
probe_end_iounmap:
|
||||
iounmap(priv->base);
|
||||
probe_end_kfree:
|
||||
kfree(priv);
|
||||
|
||||
dev_info(&pdev->dev, "probe failed\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit usbhs_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct usbhs_priv *priv = usbhsc_pdev_to_priv(pdev);
|
||||
|
||||
dev_dbg(&pdev->dev, "usb remove\n");
|
||||
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
|
||||
usbhsc_bus_ctrl(priv, 0);
|
||||
|
||||
usbhs_platform_call(priv, hardware_exit, pdev);
|
||||
usbhs_pipe_remove(priv);
|
||||
usbhs_mod_remove(priv);
|
||||
iounmap(priv->base);
|
||||
kfree(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver renesas_usbhs_driver = {
|
||||
.driver = {
|
||||
.name = "renesas_usbhs",
|
||||
},
|
||||
.probe = usbhs_probe,
|
||||
.remove = __devexit_p(usbhs_remove),
|
||||
};
|
||||
|
||||
static int __init usbhs_init(void)
|
||||
{
|
||||
return platform_driver_register(&renesas_usbhs_driver);
|
||||
}
|
||||
|
||||
static void __exit usbhs_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&renesas_usbhs_driver);
|
||||
}
|
||||
|
||||
module_init(usbhs_init);
|
||||
module_exit(usbhs_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Renesas USB driver");
|
||||
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
|
225
drivers/usb/renesas_usbhs/common.h
Normal file
225
drivers/usb/renesas_usbhs/common.h
Normal file
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
* Renesas USB driver
|
||||
*
|
||||
* Copyright (C) 2011 Renesas Solutions Corp.
|
||||
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#ifndef RENESAS_USB_DRIVER_H
|
||||
#define RENESAS_USB_DRIVER_H
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/usb/renesas_usbhs.h>
|
||||
|
||||
struct usbhs_priv;
|
||||
|
||||
#include "./mod.h"
|
||||
#include "./pipe.h"
|
||||
|
||||
/*
|
||||
*
|
||||
* register define
|
||||
*
|
||||
*/
|
||||
#define SYSCFG 0x0000
|
||||
#define BUSWAIT 0x0002
|
||||
#define DVSTCTR 0x0008
|
||||
#define CFIFO 0x0014
|
||||
#define CFIFOSEL 0x0020
|
||||
#define CFIFOCTR 0x0022
|
||||
#define INTENB0 0x0030
|
||||
#define INTENB1 0x0032
|
||||
#define BRDYENB 0x0036
|
||||
#define NRDYENB 0x0038
|
||||
#define BEMPENB 0x003A
|
||||
#define INTSTS0 0x0040
|
||||
#define INTSTS1 0x0042
|
||||
#define BRDYSTS 0x0046
|
||||
#define NRDYSTS 0x0048
|
||||
#define BEMPSTS 0x004A
|
||||
#define FRMNUM 0x004C
|
||||
#define USBREQ 0x0054 /* USB request type register */
|
||||
#define USBVAL 0x0056 /* USB request value register */
|
||||
#define USBINDX 0x0058 /* USB request index register */
|
||||
#define USBLENG 0x005A /* USB request length register */
|
||||
#define DCPCFG 0x005C
|
||||
#define DCPMAXP 0x005E
|
||||
#define DCPCTR 0x0060
|
||||
#define PIPESEL 0x0064
|
||||
#define PIPECFG 0x0068
|
||||
#define PIPEBUF 0x006A
|
||||
#define PIPEMAXP 0x006C
|
||||
#define PIPEPERI 0x006E
|
||||
#define PIPEnCTR 0x0070
|
||||
|
||||
/* SYSCFG */
|
||||
#define SCKE (1 << 10) /* USB Module Clock Enable */
|
||||
#define HSE (1 << 7) /* High-Speed Operation Enable */
|
||||
#define DCFM (1 << 6) /* Controller Function Select */
|
||||
#define DRPD (1 << 5) /* D+ Line/D- Line Resistance Control */
|
||||
#define DPRPU (1 << 4) /* D+ Line Resistance Control */
|
||||
#define USBE (1 << 0) /* USB Module Operation Enable */
|
||||
|
||||
/* DVSTCTR */
|
||||
#define EXTLP (1 << 10) /* Controls the EXTLP pin output state */
|
||||
#define PWEN (1 << 9) /* Controls the PWEN pin output state */
|
||||
#define RHST (0x7) /* Reset Handshake */
|
||||
#define RHST_LOW_SPEED 1 /* Low-speed connection */
|
||||
#define RHST_FULL_SPEED 2 /* Full-speed connection */
|
||||
#define RHST_HIGH_SPEED 3 /* High-speed connection */
|
||||
|
||||
/* CFIFOSEL */
|
||||
#define MBW_32 (0x2 << 10) /* CFIFO Port Access Bit Width */
|
||||
|
||||
/* CFIFOCTR */
|
||||
#define BVAL (1 << 15) /* Buffer Memory Enable Flag */
|
||||
#define BCLR (1 << 14) /* CPU buffer clear */
|
||||
#define FRDY (1 << 13) /* FIFO Port Ready */
|
||||
#define DTLN_MASK (0x0FFF) /* Receive Data Length */
|
||||
|
||||
/* INTENB0 */
|
||||
#define VBSE (1 << 15) /* Enable IRQ VBUS_0 and VBUSIN_0 */
|
||||
#define RSME (1 << 14) /* Enable IRQ Resume */
|
||||
#define SOFE (1 << 13) /* Enable IRQ Frame Number Update */
|
||||
#define DVSE (1 << 12) /* Enable IRQ Device State Transition */
|
||||
#define CTRE (1 << 11) /* Enable IRQ Control Stage Transition */
|
||||
#define BEMPE (1 << 10) /* Enable IRQ Buffer Empty */
|
||||
#define NRDYE (1 << 9) /* Enable IRQ Buffer Not Ready Response */
|
||||
#define BRDYE (1 << 8) /* Enable IRQ Buffer Ready */
|
||||
|
||||
/* INTENB1 */
|
||||
#define BCHGE (1 << 14) /* USB Bus Change Interrupt Enable */
|
||||
#define DTCHE (1 << 12) /* Disconnection Detect Interrupt Enable */
|
||||
#define ATTCHE (1 << 11) /* Connection Detect Interrupt Enable */
|
||||
#define EOFERRE (1 << 6) /* EOF Error Detect Interrupt Enable */
|
||||
#define SIGNE (1 << 5) /* Setup Transaction Error Interrupt Enable */
|
||||
#define SACKE (1 << 4) /* Setup Transaction ACK Interrupt Enable */
|
||||
|
||||
/* INTSTS0 */
|
||||
#define DVST (1 << 12) /* Device State Transition Interrupt Status */
|
||||
#define CTRT (1 << 11) /* Control Stage Interrupt Status */
|
||||
#define BEMP (1 << 10) /* Buffer Empty Interrupt Status */
|
||||
#define BRDY (1 << 8) /* Buffer Ready Interrupt Status */
|
||||
#define VBSTS (1 << 7) /* VBUS_0 and VBUSIN_0 Input Status */
|
||||
#define VALID (1 << 3) /* USB Request Receive */
|
||||
|
||||
#define DVSQ_MASK (0x3 << 4) /* Device State */
|
||||
#define POWER_STATE (0 << 4)
|
||||
#define DEFAULT_STATE (1 << 4)
|
||||
#define ADDRESS_STATE (2 << 4)
|
||||
#define CONFIGURATION_STATE (3 << 4)
|
||||
|
||||
#define CTSQ_MASK (0x7) /* Control Transfer Stage */
|
||||
#define IDLE_SETUP_STAGE 0 /* Idle stage or setup stage */
|
||||
#define READ_DATA_STAGE 1 /* Control read data stage */
|
||||
#define READ_STATUS_STAGE 2 /* Control read status stage */
|
||||
#define WRITE_DATA_STAGE 3 /* Control write data stage */
|
||||
#define WRITE_STATUS_STAGE 4 /* Control write status stage */
|
||||
#define NODATA_STATUS_STAGE 5 /* Control write NoData status stage */
|
||||
#define SEQUENCE_ERROR 6 /* Control transfer sequence error */
|
||||
|
||||
/* PIPECFG */
|
||||
/* DCPCFG */
|
||||
#define TYPE_NONE (0 << 14) /* Transfer Type */
|
||||
#define TYPE_BULK (1 << 14)
|
||||
#define TYPE_INT (2 << 14)
|
||||
#define TYPE_ISO (3 << 14)
|
||||
#define DBLB (1 << 9) /* Double Buffer Mode */
|
||||
#define SHTNAK (1 << 7) /* Pipe Disable in Transfer End */
|
||||
#define DIR_OUT (1 << 4) /* Transfer Direction */
|
||||
|
||||
/* PIPEMAXP */
|
||||
/* DCPMAXP */
|
||||
#define DEVSEL_MASK (0xF << 12) /* Device Select */
|
||||
#define DCP_MAXP_MASK (0x7F)
|
||||
#define PIPE_MAXP_MASK (0x7FF)
|
||||
|
||||
/* PIPEBUF */
|
||||
#define BUFSIZE_SHIFT 10
|
||||
#define BUFSIZE_MASK (0x1F << BUFSIZE_SHIFT)
|
||||
#define BUFNMB_MASK (0xFF)
|
||||
|
||||
/* PIPEnCTR */
|
||||
/* DCPCTR */
|
||||
#define BSTS (1 << 15) /* Buffer Status */
|
||||
#define CSSTS (1 << 12) /* CSSTS Status */
|
||||
#define SQCLR (1 << 8) /* Toggle Bit Clear */
|
||||
#define ACLRM (1 << 9) /* Buffer Auto-Clear Mode */
|
||||
#define PBUSY (1 << 5) /* Pipe Busy */
|
||||
#define PID_MASK (0x3) /* Response PID */
|
||||
#define PID_NAK 0
|
||||
#define PID_BUF 1
|
||||
#define PID_STALL10 2
|
||||
#define PID_STALL11 3
|
||||
|
||||
#define CCPL (1 << 2) /* Control Transfer End Enable */
|
||||
|
||||
/* FRMNUM */
|
||||
#define FRNM_MASK (0x7FF)
|
||||
|
||||
/*
|
||||
* struct
|
||||
*/
|
||||
struct usbhs_priv {
|
||||
|
||||
void __iomem *base;
|
||||
unsigned int irq;
|
||||
|
||||
struct renesas_usbhs_platform_callback *pfunc;
|
||||
struct renesas_usbhs_driver_param *dparam;
|
||||
|
||||
struct work_struct notify_hotplug_work;
|
||||
struct platform_device *pdev;
|
||||
|
||||
spinlock_t lock;
|
||||
|
||||
/*
|
||||
* module control
|
||||
*/
|
||||
struct usbhs_mod_info mod_info;
|
||||
|
||||
/*
|
||||
* pipe control
|
||||
*/
|
||||
struct usbhs_pipe_info pipe_info;
|
||||
};
|
||||
|
||||
/*
|
||||
* common
|
||||
*/
|
||||
u16 usbhs_read(struct usbhs_priv *priv, u32 reg);
|
||||
void usbhs_write(struct usbhs_priv *priv, u32 reg, u16 data);
|
||||
void usbhs_bset(struct usbhs_priv *priv, u32 reg, u16 mask, u16 data);
|
||||
|
||||
/*
|
||||
* sysconfig
|
||||
*/
|
||||
void usbhs_sys_clock_ctrl(struct usbhs_priv *priv, int enable);
|
||||
void usbhs_sys_hispeed_ctrl(struct usbhs_priv *priv, int enable);
|
||||
void usbhs_sys_usb_ctrl(struct usbhs_priv *priv, int enable);
|
||||
void usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable);
|
||||
void usbhs_sys_function_ctrl(struct usbhs_priv *priv, int enable);
|
||||
|
||||
/*
|
||||
* frame
|
||||
*/
|
||||
int usbhs_frame_get_num(struct usbhs_priv *priv);
|
||||
|
||||
/*
|
||||
* data
|
||||
*/
|
||||
#define usbhs_get_dparam(priv, param) (priv->dparam->param)
|
||||
#define usbhs_priv_to_pdev(priv) (priv->pdev)
|
||||
#define usbhs_priv_to_dev(priv) (&priv->pdev->dev)
|
||||
#define usbhs_priv_to_lock(priv) (&priv->lock)
|
||||
|
||||
#endif /* RENESAS_USB_DRIVER_H */
|
276
drivers/usb/renesas_usbhs/mod.c
Normal file
276
drivers/usb/renesas_usbhs/mod.c
Normal file
|
@ -0,0 +1,276 @@
|
|||
/*
|
||||
* Renesas USB driver
|
||||
*
|
||||
* Copyright (C) 2011 Renesas Solutions Corp.
|
||||
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#include "./common.h"
|
||||
#include "./mod.h"
|
||||
|
||||
#define usbhs_priv_to_modinfo(priv) (&priv->mod_info)
|
||||
|
||||
/*
|
||||
* host / gadget functions
|
||||
*
|
||||
* renesas_usbhs host/gadget can register itself by below functions.
|
||||
* these functions are called when probe
|
||||
*
|
||||
*/
|
||||
void usbhs_mod_register(struct usbhs_priv *priv, struct usbhs_mod *mod, int id)
|
||||
{
|
||||
struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
|
||||
|
||||
info->mod[id] = mod;
|
||||
mod->priv = priv;
|
||||
}
|
||||
|
||||
struct usbhs_mod *usbhs_mod_get(struct usbhs_priv *priv, int id)
|
||||
{
|
||||
struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
|
||||
struct usbhs_mod *ret = NULL;
|
||||
|
||||
switch (id) {
|
||||
case USBHS_HOST:
|
||||
case USBHS_GADGET:
|
||||
ret = info->mod[id];
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usbhs_mod_is_host(struct usbhs_priv *priv, struct usbhs_mod *mod)
|
||||
{
|
||||
struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
|
||||
|
||||
if (!mod)
|
||||
return -EINVAL;
|
||||
|
||||
return info->mod[USBHS_HOST] == mod;
|
||||
}
|
||||
|
||||
struct usbhs_mod *usbhs_mod_get_current(struct usbhs_priv *priv)
|
||||
{
|
||||
struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
|
||||
|
||||
return info->curt;
|
||||
}
|
||||
|
||||
int usbhs_mod_change(struct usbhs_priv *priv, int id)
|
||||
{
|
||||
struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
|
||||
struct usbhs_mod *mod = NULL;
|
||||
int ret = 0;
|
||||
|
||||
/* id < 0 mean no current */
|
||||
switch (id) {
|
||||
case USBHS_HOST:
|
||||
case USBHS_GADGET:
|
||||
mod = info->mod[id];
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
info->curt = mod;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t usbhs_interrupt(int irq, void *data);
|
||||
int usbhs_mod_probe(struct usbhs_priv *priv)
|
||||
{
|
||||
struct device *dev = usbhs_priv_to_dev(priv);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* install host/gadget driver
|
||||
*/
|
||||
ret = usbhs_mod_gadget_probe(priv);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* irq settings */
|
||||
ret = request_irq(priv->irq, usbhs_interrupt,
|
||||
IRQF_DISABLED, dev_name(dev), priv);
|
||||
if (ret) {
|
||||
dev_err(dev, "irq request err\n");
|
||||
goto mod_init_gadget_err;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
mod_init_gadget_err:
|
||||
usbhs_mod_gadget_remove(priv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void usbhs_mod_remove(struct usbhs_priv *priv)
|
||||
{
|
||||
usbhs_mod_gadget_remove(priv);
|
||||
free_irq(priv->irq, priv);
|
||||
}
|
||||
|
||||
/*
|
||||
* status functions
|
||||
*/
|
||||
int usbhs_status_get_usb_speed(struct usbhs_irq_state *irq_state)
|
||||
{
|
||||
switch (irq_state->dvstctr & RHST) {
|
||||
case RHST_LOW_SPEED:
|
||||
return USB_SPEED_LOW;
|
||||
case RHST_FULL_SPEED:
|
||||
return USB_SPEED_FULL;
|
||||
case RHST_HIGH_SPEED:
|
||||
return USB_SPEED_HIGH;
|
||||
}
|
||||
|
||||
return USB_SPEED_UNKNOWN;
|
||||
}
|
||||
|
||||
int usbhs_status_get_device_state(struct usbhs_irq_state *irq_state)
|
||||
{
|
||||
int state = irq_state->intsts0 & DVSQ_MASK;
|
||||
|
||||
switch (state) {
|
||||
case POWER_STATE:
|
||||
case DEFAULT_STATE:
|
||||
case ADDRESS_STATE:
|
||||
case CONFIGURATION_STATE:
|
||||
return state;
|
||||
}
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int usbhs_status_get_ctrl_stage(struct usbhs_irq_state *irq_state)
|
||||
{
|
||||
/*
|
||||
* return value
|
||||
*
|
||||
* IDLE_SETUP_STAGE
|
||||
* READ_DATA_STAGE
|
||||
* READ_STATUS_STAGE
|
||||
* WRITE_DATA_STAGE
|
||||
* WRITE_STATUS_STAGE
|
||||
* NODATA_STATUS_STAGE
|
||||
* SEQUENCE_ERROR
|
||||
*/
|
||||
return (int)irq_state->intsts0 & CTSQ_MASK;
|
||||
}
|
||||
|
||||
static void usbhs_status_get_each_irq(struct usbhs_priv *priv,
|
||||
struct usbhs_irq_state *state)
|
||||
{
|
||||
struct usbhs_mod *mod = usbhs_mod_get_current(priv);
|
||||
|
||||
state->intsts0 = usbhs_read(priv, INTSTS0);
|
||||
state->intsts1 = usbhs_read(priv, INTSTS1);
|
||||
|
||||
state->brdysts = usbhs_read(priv, BRDYSTS);
|
||||
state->nrdysts = usbhs_read(priv, NRDYSTS);
|
||||
state->bempsts = usbhs_read(priv, BEMPSTS);
|
||||
|
||||
state->dvstctr = usbhs_read(priv, DVSTCTR);
|
||||
|
||||
/* mask */
|
||||
state->bempsts &= mod->irq_bempsts;
|
||||
state->brdysts &= mod->irq_brdysts;
|
||||
}
|
||||
|
||||
/*
|
||||
* interrupt
|
||||
*/
|
||||
#define INTSTS0_MAGIC 0xF800 /* acknowledge magical interrupt sources */
|
||||
#define INTSTS1_MAGIC 0xA870 /* acknowledge magical interrupt sources */
|
||||
static irqreturn_t usbhs_interrupt(int irq, void *data)
|
||||
{
|
||||
struct usbhs_priv *priv = data;
|
||||
struct usbhs_irq_state irq_state;
|
||||
|
||||
usbhs_status_get_each_irq(priv, &irq_state);
|
||||
|
||||
/*
|
||||
* clear interrupt
|
||||
*
|
||||
* The hardware is _very_ picky to clear interrupt bit.
|
||||
* Especially INTSTS0_MAGIC, INTSTS1_MAGIC value.
|
||||
*
|
||||
* see
|
||||
* "Operation"
|
||||
* - "Control Transfer (DCP)"
|
||||
* - Function :: VALID bit should 0
|
||||
*/
|
||||
usbhs_write(priv, INTSTS0, ~irq_state.intsts0 & INTSTS0_MAGIC);
|
||||
usbhs_write(priv, INTSTS1, ~irq_state.intsts1 & INTSTS1_MAGIC);
|
||||
|
||||
usbhs_write(priv, BRDYSTS, 0);
|
||||
usbhs_write(priv, NRDYSTS, 0);
|
||||
usbhs_write(priv, BEMPSTS, 0);
|
||||
|
||||
/*
|
||||
* call irq callback functions
|
||||
* see also
|
||||
* usbhs_irq_setting_update
|
||||
*/
|
||||
if (irq_state.intsts0 & DVST)
|
||||
usbhs_mod_call(priv, irq_dev_state, priv, &irq_state);
|
||||
|
||||
if (irq_state.intsts0 & CTRT)
|
||||
usbhs_mod_call(priv, irq_ctrl_stage, priv, &irq_state);
|
||||
|
||||
if (irq_state.intsts0 & BEMP)
|
||||
usbhs_mod_call(priv, irq_empty, priv, &irq_state);
|
||||
|
||||
if (irq_state.intsts0 & BRDY)
|
||||
usbhs_mod_call(priv, irq_ready, priv, &irq_state);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod)
|
||||
{
|
||||
u16 intenb0 = 0;
|
||||
|
||||
usbhs_write(priv, INTENB0, 0);
|
||||
|
||||
usbhs_write(priv, BEMPENB, 0);
|
||||
usbhs_write(priv, BRDYENB, 0);
|
||||
|
||||
/*
|
||||
* see also
|
||||
* usbhs_interrupt
|
||||
*/
|
||||
|
||||
/*
|
||||
* it don't enable DVSE (intenb0) here
|
||||
* but "mod->irq_dev_state" will be called.
|
||||
*/
|
||||
|
||||
if (mod->irq_ctrl_stage)
|
||||
intenb0 |= CTRE;
|
||||
|
||||
if (mod->irq_empty && mod->irq_bempsts) {
|
||||
usbhs_write(priv, BEMPENB, mod->irq_bempsts);
|
||||
intenb0 |= BEMPE;
|
||||
}
|
||||
|
||||
if (mod->irq_ready && mod->irq_brdysts) {
|
||||
usbhs_write(priv, BRDYENB, mod->irq_brdysts);
|
||||
intenb0 |= BRDYE;
|
||||
}
|
||||
|
||||
usbhs_write(priv, INTENB0, intenb0);
|
||||
}
|
122
drivers/usb/renesas_usbhs/mod.h
Normal file
122
drivers/usb/renesas_usbhs/mod.h
Normal file
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Renesas USB driver
|
||||
*
|
||||
* Copyright (C) 2011 Renesas Solutions Corp.
|
||||
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#ifndef RENESAS_USB_MOD_H
|
||||
#define RENESAS_USB_MOD_H
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/usb/renesas_usbhs.h>
|
||||
#include "./common.h"
|
||||
|
||||
/*
|
||||
* struct
|
||||
*/
|
||||
struct usbhs_irq_state {
|
||||
u16 intsts0;
|
||||
u16 intsts1;
|
||||
u16 brdysts;
|
||||
u16 nrdysts;
|
||||
u16 bempsts;
|
||||
u16 dvstctr;
|
||||
};
|
||||
|
||||
struct usbhs_mod {
|
||||
char *name;
|
||||
|
||||
/*
|
||||
* entry point from common.c
|
||||
*/
|
||||
int (*start)(struct usbhs_priv *priv);
|
||||
int (*stop)(struct usbhs_priv *priv);
|
||||
|
||||
/* INTSTS0 :: DVST (DVSQ) */
|
||||
int (*irq_dev_state)(struct usbhs_priv *priv,
|
||||
struct usbhs_irq_state *irq_state);
|
||||
|
||||
/* INTSTS0 :: CTRT (CTSQ) */
|
||||
int (*irq_ctrl_stage)(struct usbhs_priv *priv,
|
||||
struct usbhs_irq_state *irq_state);
|
||||
|
||||
/* INTSTS0 :: BEMP */
|
||||
/* BEMPSTS */
|
||||
int (*irq_empty)(struct usbhs_priv *priv,
|
||||
struct usbhs_irq_state *irq_state);
|
||||
u16 irq_bempsts;
|
||||
|
||||
/* INTSTS0 :: BRDY */
|
||||
/* BRDYSTS */
|
||||
int (*irq_ready)(struct usbhs_priv *priv,
|
||||
struct usbhs_irq_state *irq_state);
|
||||
u16 irq_brdysts;
|
||||
|
||||
struct usbhs_priv *priv;
|
||||
};
|
||||
|
||||
struct usbhs_mod_info {
|
||||
struct usbhs_mod *mod[USBHS_MAX];
|
||||
struct usbhs_mod *curt; /* current mod */
|
||||
};
|
||||
|
||||
/*
|
||||
* for host/gadget module
|
||||
*/
|
||||
struct usbhs_mod *usbhs_mod_get(struct usbhs_priv *priv, int id);
|
||||
struct usbhs_mod *usbhs_mod_get_current(struct usbhs_priv *priv);
|
||||
void usbhs_mod_register(struct usbhs_priv *priv, struct usbhs_mod *usb, int id);
|
||||
int usbhs_mod_is_host(struct usbhs_priv *priv, struct usbhs_mod *mod);
|
||||
int usbhs_mod_change(struct usbhs_priv *priv, int id);
|
||||
int usbhs_mod_probe(struct usbhs_priv *priv);
|
||||
void usbhs_mod_remove(struct usbhs_priv *priv);
|
||||
|
||||
/*
|
||||
* status functions
|
||||
*/
|
||||
int usbhs_status_get_usb_speed(struct usbhs_irq_state *irq_state);
|
||||
int usbhs_status_get_device_state(struct usbhs_irq_state *irq_state);
|
||||
int usbhs_status_get_ctrl_stage(struct usbhs_irq_state *irq_state);
|
||||
|
||||
/*
|
||||
* callback functions
|
||||
*/
|
||||
void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod);
|
||||
|
||||
|
||||
#define usbhs_mod_call(priv, func, param...) \
|
||||
({ \
|
||||
struct usbhs_mod *mod; \
|
||||
mod = usbhs_mod_get_current(priv); \
|
||||
!mod ? -ENODEV : \
|
||||
!mod->func ? 0 : \
|
||||
mod->func(param); \
|
||||
})
|
||||
|
||||
/*
|
||||
* gadget control
|
||||
*/
|
||||
#ifdef CONFIG_USB_RENESAS_USBHS_UDC
|
||||
extern int __devinit usbhs_mod_gadget_probe(struct usbhs_priv *priv);
|
||||
extern void __devexit usbhs_mod_gadget_remove(struct usbhs_priv *priv);
|
||||
#else
|
||||
static inline int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline void usbhs_mod_gadget_remove(struct usbhs_priv *priv)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* RENESAS_USB_MOD_H */
|
1341
drivers/usb/renesas_usbhs/mod_gadget.c
Normal file
1341
drivers/usb/renesas_usbhs/mod_gadget.c
Normal file
File diff suppressed because it is too large
Load diff
880
drivers/usb/renesas_usbhs/pipe.c
Normal file
880
drivers/usb/renesas_usbhs/pipe.c
Normal file
|
@ -0,0 +1,880 @@
|
|||
/*
|
||||
* Renesas USB driver
|
||||
*
|
||||
* Copyright (C) 2011 Renesas Solutions Corp.
|
||||
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/slab.h>
|
||||
#include "./common.h"
|
||||
#include "./pipe.h"
|
||||
|
||||
/*
|
||||
* macros
|
||||
*/
|
||||
#define usbhsp_priv_to_pipeinfo(pr) (&(pr)->pipe_info)
|
||||
#define usbhsp_pipe_to_priv(p) ((p)->priv)
|
||||
|
||||
#define usbhsp_addr_offset(p) ((usbhs_pipe_number(p) - 1) * 2)
|
||||
|
||||
#define usbhsp_is_dcp(p) ((p)->priv->pipe_info.pipe == (p))
|
||||
|
||||
#define usbhsp_flags_set(p, f) ((p)->flags |= USBHS_PIPE_FLAGS_##f)
|
||||
#define usbhsp_flags_clr(p, f) ((p)->flags &= ~USBHS_PIPE_FLAGS_##f)
|
||||
#define usbhsp_flags_has(p, f) ((p)->flags & USBHS_PIPE_FLAGS_##f)
|
||||
#define usbhsp_flags_init(p) do {(p)->flags = 0; } while (0)
|
||||
|
||||
#define usbhsp_type(p) ((p)->pipe_type)
|
||||
#define usbhsp_type_is(p, t) ((p)->pipe_type == t)
|
||||
|
||||
/*
|
||||
* for debug
|
||||
*/
|
||||
static char *usbhsp_pipe_name[] = {
|
||||
[USB_ENDPOINT_XFER_CONTROL] = "DCP",
|
||||
[USB_ENDPOINT_XFER_BULK] = "BULK",
|
||||
[USB_ENDPOINT_XFER_INT] = "INT",
|
||||
[USB_ENDPOINT_XFER_ISOC] = "ISO",
|
||||
};
|
||||
|
||||
/*
|
||||
* usb request functions
|
||||
*/
|
||||
void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req)
|
||||
{
|
||||
u16 val;
|
||||
|
||||
val = usbhs_read(priv, USBREQ);
|
||||
req->bRequest = (val >> 8) & 0xFF;
|
||||
req->bRequestType = (val >> 0) & 0xFF;
|
||||
|
||||
req->wValue = usbhs_read(priv, USBVAL);
|
||||
req->wIndex = usbhs_read(priv, USBINDX);
|
||||
req->wLength = usbhs_read(priv, USBLENG);
|
||||
}
|
||||
|
||||
void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req)
|
||||
{
|
||||
usbhs_write(priv, USBREQ, (req->bRequest << 8) | req->bRequestType);
|
||||
usbhs_write(priv, USBVAL, req->wValue);
|
||||
usbhs_write(priv, USBINDX, req->wIndex);
|
||||
usbhs_write(priv, USBLENG, req->wLength);
|
||||
}
|
||||
|
||||
/*
|
||||
* DCPCTR/PIPEnCTR functions
|
||||
*/
|
||||
static void usbhsp_pipectrl_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
|
||||
{
|
||||
struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
|
||||
int offset = usbhsp_addr_offset(pipe);
|
||||
|
||||
if (usbhsp_is_dcp(pipe))
|
||||
usbhs_bset(priv, DCPCTR, mask, val);
|
||||
else
|
||||
usbhs_bset(priv, PIPEnCTR + offset, mask, val);
|
||||
}
|
||||
|
||||
static u16 usbhsp_pipectrl_get(struct usbhs_pipe *pipe)
|
||||
{
|
||||
struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
|
||||
int offset = usbhsp_addr_offset(pipe);
|
||||
|
||||
if (usbhsp_is_dcp(pipe))
|
||||
return usbhs_read(priv, DCPCTR);
|
||||
else
|
||||
return usbhs_read(priv, PIPEnCTR + offset);
|
||||
}
|
||||
|
||||
/*
|
||||
* DCP/PIPE functions
|
||||
*/
|
||||
static void __usbhsp_pipe_xxx_set(struct usbhs_pipe *pipe,
|
||||
u16 dcp_reg, u16 pipe_reg,
|
||||
u16 mask, u16 val)
|
||||
{
|
||||
struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
|
||||
|
||||
if (usbhsp_is_dcp(pipe))
|
||||
usbhs_bset(priv, dcp_reg, mask, val);
|
||||
else
|
||||
usbhs_bset(priv, pipe_reg, mask, val);
|
||||
}
|
||||
|
||||
static u16 __usbhsp_pipe_xxx_get(struct usbhs_pipe *pipe,
|
||||
u16 dcp_reg, u16 pipe_reg)
|
||||
{
|
||||
struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
|
||||
|
||||
if (usbhsp_is_dcp(pipe))
|
||||
return usbhs_read(priv, dcp_reg);
|
||||
else
|
||||
return usbhs_read(priv, pipe_reg);
|
||||
}
|
||||
|
||||
/*
|
||||
* DCPCFG/PIPECFG functions
|
||||
*/
|
||||
static void usbhsp_pipe_cfg_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
|
||||
{
|
||||
__usbhsp_pipe_xxx_set(pipe, DCPCFG, PIPECFG, mask, val);
|
||||
}
|
||||
|
||||
/*
|
||||
* PIPEBUF
|
||||
*/
|
||||
static void usbhsp_pipe_buf_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
|
||||
{
|
||||
if (usbhsp_is_dcp(pipe))
|
||||
return;
|
||||
|
||||
__usbhsp_pipe_xxx_set(pipe, 0, PIPEBUF, mask, val);
|
||||
}
|
||||
|
||||
/*
|
||||
* DCPMAXP/PIPEMAXP
|
||||
*/
|
||||
static void usbhsp_pipe_maxp_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
|
||||
{
|
||||
__usbhsp_pipe_xxx_set(pipe, DCPMAXP, PIPEMAXP, mask, val);
|
||||
}
|
||||
|
||||
static u16 usbhsp_pipe_maxp_get(struct usbhs_pipe *pipe)
|
||||
{
|
||||
return __usbhsp_pipe_xxx_get(pipe, DCPMAXP, PIPEMAXP);
|
||||
}
|
||||
|
||||
/*
|
||||
* pipe control functions
|
||||
*/
|
||||
static void usbhsp_pipe_select(struct usbhs_pipe *pipe)
|
||||
{
|
||||
struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
|
||||
|
||||
/*
|
||||
* On pipe, this is necessary before
|
||||
* accesses to below registers.
|
||||
*
|
||||
* PIPESEL : usbhsp_pipe_select
|
||||
* PIPECFG : usbhsp_pipe_cfg_xxx
|
||||
* PIPEBUF : usbhsp_pipe_buf_xxx
|
||||
* PIPEMAXP : usbhsp_pipe_maxp_xxx
|
||||
* PIPEPERI
|
||||
*/
|
||||
|
||||
/*
|
||||
* if pipe is dcp, no pipe is selected.
|
||||
* it is no problem, because dcp have its register
|
||||
*/
|
||||
usbhs_write(priv, PIPESEL, 0xF & usbhs_pipe_number(pipe));
|
||||
}
|
||||
|
||||
static int usbhsp_pipe_barrier(struct usbhs_pipe *pipe)
|
||||
{
|
||||
struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
|
||||
struct device *dev = usbhs_priv_to_dev(priv);
|
||||
int timeout = 1024;
|
||||
u16 val;
|
||||
|
||||
/*
|
||||
* make sure....
|
||||
*
|
||||
* Modify these bits when CSSTS = 0, PID = NAK, and no pipe number is
|
||||
* specified by the CURPIPE bits.
|
||||
* When changing the setting of this bit after changing
|
||||
* the PID bits for the selected pipe from BUF to NAK,
|
||||
* check that CSSTS = 0 and PBUSY = 0.
|
||||
*/
|
||||
|
||||
/*
|
||||
* CURPIPE bit = 0
|
||||
*
|
||||
* see also
|
||||
* "Operation"
|
||||
* - "Pipe Control"
|
||||
* - "Pipe Control Registers Switching Procedure"
|
||||
*/
|
||||
usbhs_write(priv, CFIFOSEL, 0);
|
||||
|
||||
do {
|
||||
val = usbhsp_pipectrl_get(pipe);
|
||||
val &= CSSTS | PID_MASK;
|
||||
if (!val)
|
||||
return 0;
|
||||
|
||||
udelay(10);
|
||||
|
||||
} while (timeout--);
|
||||
|
||||
/*
|
||||
* force NAK
|
||||
*/
|
||||
timeout = 1024;
|
||||
usbhs_fifo_disable(pipe);
|
||||
do {
|
||||
val = usbhsp_pipectrl_get(pipe);
|
||||
val &= PBUSY;
|
||||
if (!val)
|
||||
return 0;
|
||||
|
||||
} while (timeout--);
|
||||
|
||||
dev_err(dev, "pipe barrier failed\n");
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static int usbhsp_pipe_is_accessible(struct usbhs_pipe *pipe)
|
||||
{
|
||||
u16 val;
|
||||
|
||||
val = usbhsp_pipectrl_get(pipe);
|
||||
if (val & BSTS)
|
||||
return 0;
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
/*
|
||||
* PID ctrl
|
||||
*/
|
||||
static void __usbhsp_pid_try_nak_if_stall(struct usbhs_pipe *pipe)
|
||||
{
|
||||
u16 pid = usbhsp_pipectrl_get(pipe);
|
||||
|
||||
pid &= PID_MASK;
|
||||
|
||||
/*
|
||||
* see
|
||||
* "Pipe n Control Register" - "PID"
|
||||
*/
|
||||
switch (pid) {
|
||||
case PID_STALL11:
|
||||
usbhsp_pipectrl_set(pipe, PID_MASK, PID_STALL10);
|
||||
/* fall-through */
|
||||
case PID_STALL10:
|
||||
usbhsp_pipectrl_set(pipe, PID_MASK, PID_NAK);
|
||||
}
|
||||
}
|
||||
|
||||
void usbhs_fifo_disable(struct usbhs_pipe *pipe)
|
||||
{
|
||||
/* see "Pipe n Control Register" - "PID" */
|
||||
__usbhsp_pid_try_nak_if_stall(pipe);
|
||||
|
||||
usbhsp_pipectrl_set(pipe, PID_MASK, PID_NAK);
|
||||
}
|
||||
|
||||
void usbhs_fifo_enable(struct usbhs_pipe *pipe)
|
||||
{
|
||||
/* see "Pipe n Control Register" - "PID" */
|
||||
__usbhsp_pid_try_nak_if_stall(pipe);
|
||||
|
||||
usbhsp_pipectrl_set(pipe, PID_MASK, PID_BUF);
|
||||
}
|
||||
|
||||
void usbhs_fifo_stall(struct usbhs_pipe *pipe)
|
||||
{
|
||||
u16 pid = usbhsp_pipectrl_get(pipe);
|
||||
|
||||
pid &= PID_MASK;
|
||||
|
||||
/*
|
||||
* see
|
||||
* "Pipe n Control Register" - "PID"
|
||||
*/
|
||||
switch (pid) {
|
||||
case PID_NAK:
|
||||
usbhsp_pipectrl_set(pipe, PID_MASK, PID_STALL10);
|
||||
break;
|
||||
case PID_BUF:
|
||||
usbhsp_pipectrl_set(pipe, PID_MASK, PID_STALL11);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* CFIFO ctrl
|
||||
*/
|
||||
void usbhs_fifo_send_terminator(struct usbhs_pipe *pipe)
|
||||
{
|
||||
struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
|
||||
|
||||
usbhs_bset(priv, CFIFOCTR, BVAL, BVAL);
|
||||
}
|
||||
|
||||
static void usbhsp_fifo_clear(struct usbhs_pipe *pipe)
|
||||
{
|
||||
struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
|
||||
|
||||
usbhs_write(priv, CFIFOCTR, BCLR);
|
||||
}
|
||||
|
||||
static int usbhsp_fifo_barrier(struct usbhs_priv *priv)
|
||||
{
|
||||
int timeout = 1024;
|
||||
|
||||
do {
|
||||
/* The FIFO port is accessible */
|
||||
if (usbhs_read(priv, CFIFOCTR) & FRDY)
|
||||
return 0;
|
||||
|
||||
udelay(10);
|
||||
} while (timeout--);
|
||||
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static int usbhsp_fifo_rcv_len(struct usbhs_priv *priv)
|
||||
{
|
||||
return usbhs_read(priv, CFIFOCTR) & DTLN_MASK;
|
||||
}
|
||||
|
||||
static int usbhsp_fifo_select(struct usbhs_pipe *pipe, int write)
|
||||
{
|
||||
struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
|
||||
struct device *dev = usbhs_priv_to_dev(priv);
|
||||
int timeout = 1024;
|
||||
u16 mask = ((1 << 5) | 0xF); /* mask of ISEL | CURPIPE */
|
||||
u16 base = usbhs_pipe_number(pipe); /* CURPIPE */
|
||||
|
||||
if (usbhsp_is_dcp(pipe))
|
||||
base |= (1 == write) << 5; /* ISEL */
|
||||
|
||||
/* "base" will be used below */
|
||||
usbhs_write(priv, CFIFOSEL, base | MBW_32);
|
||||
|
||||
/* check ISEL and CURPIPE value */
|
||||
while (timeout--) {
|
||||
if (base == (mask & usbhs_read(priv, CFIFOSEL)))
|
||||
return 0;
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
dev_err(dev, "fifo select error\n");
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
int usbhs_fifo_prepare_write(struct usbhs_pipe *pipe)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = usbhsp_fifo_select(pipe, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
usbhsp_fifo_clear(pipe);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usbhs_fifo_write(struct usbhs_pipe *pipe, u8 *buf, int len)
|
||||
{
|
||||
struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
|
||||
void __iomem *addr = priv->base + CFIFO;
|
||||
int maxp = usbhs_pipe_get_maxpacket(pipe);
|
||||
int total_len;
|
||||
int i, ret;
|
||||
|
||||
ret = usbhsp_pipe_is_accessible(pipe);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = usbhs_fifo_prepare_write(pipe);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = usbhsp_fifo_barrier(priv);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
len = min(len, maxp);
|
||||
total_len = len;
|
||||
|
||||
/*
|
||||
* FIXME
|
||||
*
|
||||
* 32-bit access only
|
||||
*/
|
||||
if (len >= 4 &&
|
||||
!((unsigned long)buf & 0x03)) {
|
||||
iowrite32_rep(addr, buf, len / 4);
|
||||
len %= 4;
|
||||
buf += total_len - len;
|
||||
}
|
||||
|
||||
/* the rest operation */
|
||||
for (i = 0; i < len; i++)
|
||||
iowrite8(buf[i], addr + (0x03 - (i & 0x03)));
|
||||
|
||||
if (total_len < maxp)
|
||||
usbhs_fifo_send_terminator(pipe);
|
||||
|
||||
return total_len;
|
||||
}
|
||||
|
||||
int usbhs_fifo_prepare_read(struct usbhs_pipe *pipe)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* select pipe and enable it to prepare packet receive
|
||||
*/
|
||||
ret = usbhsp_fifo_select(pipe, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
usbhs_fifo_enable(pipe);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usbhs_fifo_read(struct usbhs_pipe *pipe, u8 *buf, int len)
|
||||
{
|
||||
struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
|
||||
void __iomem *addr = priv->base + CFIFO;
|
||||
int rcv_len;
|
||||
int i, ret;
|
||||
int total_len;
|
||||
u32 data = 0;
|
||||
|
||||
ret = usbhsp_fifo_select(pipe, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = usbhsp_fifo_barrier(priv);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
rcv_len = usbhsp_fifo_rcv_len(priv);
|
||||
|
||||
/*
|
||||
* Buffer clear if Zero-Length packet
|
||||
*
|
||||
* see
|
||||
* "Operation" - "FIFO Buffer Memory" - "FIFO Port Function"
|
||||
*/
|
||||
if (0 == rcv_len) {
|
||||
usbhsp_fifo_clear(pipe);
|
||||
return 0;
|
||||
}
|
||||
|
||||
len = min(rcv_len, len);
|
||||
total_len = len;
|
||||
|
||||
/*
|
||||
* FIXME
|
||||
*
|
||||
* 32-bit access only
|
||||
*/
|
||||
if (len >= 4 &&
|
||||
!((unsigned long)buf & 0x03)) {
|
||||
ioread32_rep(addr, buf, len / 4);
|
||||
len %= 4;
|
||||
buf += rcv_len - len;
|
||||
}
|
||||
|
||||
/* the rest operation */
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!(i & 0x03))
|
||||
data = ioread32(addr);
|
||||
|
||||
buf[i] = (data >> ((i & 0x03) * 8)) & 0xff;
|
||||
}
|
||||
|
||||
return total_len;
|
||||
}
|
||||
|
||||
/*
|
||||
* pipe setup
|
||||
*/
|
||||
static int usbhsp_possible_double_buffer(struct usbhs_pipe *pipe)
|
||||
{
|
||||
/*
|
||||
* only ISO / BULK pipe can use double buffer
|
||||
*/
|
||||
if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK) ||
|
||||
usbhsp_type_is(pipe, USB_ENDPOINT_XFER_ISOC))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe,
|
||||
const struct usb_endpoint_descriptor *desc,
|
||||
int is_host)
|
||||
{
|
||||
u16 type = 0;
|
||||
u16 bfre = 0;
|
||||
u16 dblb = 0;
|
||||
u16 cntmd = 0;
|
||||
u16 dir = 0;
|
||||
u16 epnum = 0;
|
||||
u16 shtnak = 0;
|
||||
u16 type_array[] = {
|
||||
[USB_ENDPOINT_XFER_BULK] = TYPE_BULK,
|
||||
[USB_ENDPOINT_XFER_INT] = TYPE_INT,
|
||||
[USB_ENDPOINT_XFER_ISOC] = TYPE_ISO,
|
||||
};
|
||||
int is_double = usbhsp_possible_double_buffer(pipe);
|
||||
|
||||
if (usbhsp_is_dcp(pipe))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* PIPECFG
|
||||
*
|
||||
* see
|
||||
* - "Register Descriptions" - "PIPECFG" register
|
||||
* - "Features" - "Pipe configuration"
|
||||
* - "Operation" - "Pipe Control"
|
||||
*/
|
||||
|
||||
/* TYPE */
|
||||
type = type_array[usbhsp_type(pipe)];
|
||||
|
||||
/* BFRE */
|
||||
if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_ISOC) ||
|
||||
usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK))
|
||||
bfre = 0; /* FIXME */
|
||||
|
||||
/* DBLB */
|
||||
if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_ISOC) ||
|
||||
usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK))
|
||||
dblb = (is_double) ? DBLB : 0;
|
||||
|
||||
/* CNTMD */
|
||||
if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK))
|
||||
cntmd = 0; /* FIXME */
|
||||
|
||||
/* DIR */
|
||||
if (usb_endpoint_dir_in(desc))
|
||||
usbhsp_flags_set(pipe, IS_DIR_IN);
|
||||
|
||||
if ((is_host && usb_endpoint_dir_out(desc)) ||
|
||||
(!is_host && usb_endpoint_dir_in(desc)))
|
||||
dir |= DIR_OUT;
|
||||
|
||||
/* SHTNAK */
|
||||
if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK) &&
|
||||
!dir)
|
||||
shtnak = SHTNAK;
|
||||
|
||||
/* EPNUM */
|
||||
epnum = 0xF & usb_endpoint_num(desc);
|
||||
|
||||
return type |
|
||||
bfre |
|
||||
dblb |
|
||||
cntmd |
|
||||
dir |
|
||||
shtnak |
|
||||
epnum;
|
||||
}
|
||||
|
||||
static u16 usbhsp_setup_pipemaxp(struct usbhs_pipe *pipe,
|
||||
const struct usb_endpoint_descriptor *desc,
|
||||
int is_host)
|
||||
{
|
||||
/* host should set DEVSEL */
|
||||
|
||||
/* reutn MXPS */
|
||||
return PIPE_MAXP_MASK & le16_to_cpu(desc->wMaxPacketSize);
|
||||
}
|
||||
|
||||
static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe,
|
||||
const struct usb_endpoint_descriptor *desc,
|
||||
int is_host)
|
||||
{
|
||||
struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
|
||||
struct usbhs_pipe_info *info = usbhsp_priv_to_pipeinfo(priv);
|
||||
struct device *dev = usbhs_priv_to_dev(priv);
|
||||
int pipe_num = usbhs_pipe_number(pipe);
|
||||
int is_double = usbhsp_possible_double_buffer(pipe);
|
||||
u16 buff_size;
|
||||
u16 bufnmb;
|
||||
u16 bufnmb_cnt;
|
||||
|
||||
/*
|
||||
* PIPEBUF
|
||||
*
|
||||
* see
|
||||
* - "Register Descriptions" - "PIPEBUF" register
|
||||
* - "Features" - "Pipe configuration"
|
||||
* - "Operation" - "FIFO Buffer Memory"
|
||||
* - "Operation" - "Pipe Control"
|
||||
*
|
||||
* ex) if pipe6 - pipe9 are USB_ENDPOINT_XFER_INT (SH7724)
|
||||
*
|
||||
* BUFNMB: PIPE
|
||||
* 0: pipe0 (DCP 256byte)
|
||||
* 1: -
|
||||
* 2: -
|
||||
* 3: -
|
||||
* 4: pipe6 (INT 64byte)
|
||||
* 5: pipe7 (INT 64byte)
|
||||
* 6: pipe8 (INT 64byte)
|
||||
* 7: pipe9 (INT 64byte)
|
||||
* 8 - xx: free (for BULK, ISOC)
|
||||
*/
|
||||
|
||||
/*
|
||||
* FIXME
|
||||
*
|
||||
* it doesn't have good buffer allocator
|
||||
*
|
||||
* DCP : 256 byte
|
||||
* BULK: 512 byte
|
||||
* INT : 64 byte
|
||||
* ISOC: 512 byte
|
||||
*/
|
||||
if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_CONTROL))
|
||||
buff_size = 256;
|
||||
else if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_INT))
|
||||
buff_size = 64;
|
||||
else
|
||||
buff_size = 512;
|
||||
|
||||
/* change buff_size to register value */
|
||||
bufnmb_cnt = (buff_size / 64) - 1;
|
||||
|
||||
/* BUFNMB has been reserved for INT pipe
|
||||
* see above */
|
||||
if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_INT)) {
|
||||
bufnmb = pipe_num - 2;
|
||||
} else {
|
||||
bufnmb = info->bufnmb_last;
|
||||
info->bufnmb_last += bufnmb_cnt + 1;
|
||||
|
||||
/*
|
||||
* double buffer
|
||||
*/
|
||||
if (is_double)
|
||||
info->bufnmb_last += bufnmb_cnt + 1;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "pipe : %d : buff_size 0x%x: bufnmb 0x%x\n",
|
||||
pipe_num, buff_size, bufnmb);
|
||||
|
||||
return (0x1f & bufnmb_cnt) << 10 |
|
||||
(0xff & bufnmb) << 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* pipe control
|
||||
*/
|
||||
int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe)
|
||||
{
|
||||
u16 mask = usbhsp_is_dcp(pipe) ? DCP_MAXP_MASK : PIPE_MAXP_MASK;
|
||||
|
||||
usbhsp_pipe_select(pipe);
|
||||
|
||||
return (int)(usbhsp_pipe_maxp_get(pipe) & mask);
|
||||
}
|
||||
|
||||
int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe)
|
||||
{
|
||||
return usbhsp_flags_has(pipe, IS_DIR_IN);
|
||||
}
|
||||
|
||||
void usbhs_pipe_clear_sequence(struct usbhs_pipe *pipe)
|
||||
{
|
||||
usbhsp_pipectrl_set(pipe, SQCLR, SQCLR);
|
||||
}
|
||||
|
||||
static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type)
|
||||
{
|
||||
struct usbhs_pipe *pos, *pipe;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* find target pipe
|
||||
*/
|
||||
pipe = NULL;
|
||||
usbhs_for_each_pipe_with_dcp(pos, priv, i) {
|
||||
if (!usbhsp_type_is(pos, type))
|
||||
continue;
|
||||
if (usbhsp_flags_has(pos, IS_USED))
|
||||
continue;
|
||||
|
||||
pipe = pos;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!pipe)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* initialize pipe flags
|
||||
*/
|
||||
usbhsp_flags_init(pipe);
|
||||
usbhsp_flags_set(pipe, IS_USED);
|
||||
|
||||
return pipe;
|
||||
}
|
||||
|
||||
void usbhs_pipe_init(struct usbhs_priv *priv)
|
||||
{
|
||||
struct usbhs_pipe_info *info = usbhsp_priv_to_pipeinfo(priv);
|
||||
struct usbhs_pipe *pipe;
|
||||
int i;
|
||||
|
||||
/*
|
||||
* FIXME
|
||||
*
|
||||
* driver needs good allocator.
|
||||
*
|
||||
* find first free buffer area (BULK, ISOC)
|
||||
* (DCP, INT area is fixed)
|
||||
*
|
||||
* buffer number 0 - 3 have been reserved for DCP
|
||||
* see
|
||||
* usbhsp_to_bufnmb
|
||||
*/
|
||||
info->bufnmb_last = 4;
|
||||
usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
|
||||
if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_INT))
|
||||
info->bufnmb_last++;
|
||||
|
||||
usbhsp_flags_init(pipe);
|
||||
pipe->mod_private = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv,
|
||||
const struct usb_endpoint_descriptor *desc)
|
||||
{
|
||||
struct device *dev = usbhs_priv_to_dev(priv);
|
||||
struct usbhs_mod *mod = usbhs_mod_get_current(priv);
|
||||
struct usbhs_pipe *pipe;
|
||||
int is_host = usbhs_mod_is_host(priv, mod);
|
||||
int ret;
|
||||
u16 pipecfg, pipebuf, pipemaxp;
|
||||
|
||||
pipe = usbhsp_get_pipe(priv, usb_endpoint_type(desc));
|
||||
if (!pipe)
|
||||
return NULL;
|
||||
|
||||
usbhs_fifo_disable(pipe);
|
||||
|
||||
/* make sure pipe is not busy */
|
||||
ret = usbhsp_pipe_barrier(pipe);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "pipe setup failed %d\n", usbhs_pipe_number(pipe));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
pipecfg = usbhsp_setup_pipecfg(pipe, desc, is_host);
|
||||
pipebuf = usbhsp_setup_pipebuff(pipe, desc, is_host);
|
||||
pipemaxp = usbhsp_setup_pipemaxp(pipe, desc, is_host);
|
||||
|
||||
/* buffer clear
|
||||
* see PIPECFG :: BFRE */
|
||||
usbhsp_pipectrl_set(pipe, ACLRM, ACLRM);
|
||||
usbhsp_pipectrl_set(pipe, ACLRM, 0);
|
||||
|
||||
usbhsp_pipe_select(pipe);
|
||||
usbhsp_pipe_cfg_set(pipe, 0xFFFF, pipecfg);
|
||||
usbhsp_pipe_buf_set(pipe, 0xFFFF, pipebuf);
|
||||
usbhsp_pipe_maxp_set(pipe, 0xFFFF, pipemaxp);
|
||||
|
||||
usbhs_pipe_clear_sequence(pipe);
|
||||
|
||||
dev_dbg(dev, "enable pipe %d : %s (%s)\n",
|
||||
usbhs_pipe_number(pipe),
|
||||
usbhsp_pipe_name[usb_endpoint_type(desc)],
|
||||
usbhs_pipe_is_dir_in(pipe) ? "in" : "out");
|
||||
|
||||
return pipe;
|
||||
}
|
||||
|
||||
/*
|
||||
* dcp control
|
||||
*/
|
||||
struct usbhs_pipe *usbhs_dcp_malloc(struct usbhs_priv *priv)
|
||||
{
|
||||
struct usbhs_pipe *pipe;
|
||||
|
||||
pipe = usbhsp_get_pipe(priv, USB_ENDPOINT_XFER_CONTROL);
|
||||
if (!pipe)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* dcpcfg : default
|
||||
* dcpmaxp : default
|
||||
* pipebuf : nothing to do
|
||||
*/
|
||||
|
||||
usbhsp_pipe_select(pipe);
|
||||
usbhs_pipe_clear_sequence(pipe);
|
||||
|
||||
return pipe;
|
||||
}
|
||||
|
||||
void usbhs_dcp_control_transfer_done(struct usbhs_pipe *pipe)
|
||||
{
|
||||
WARN_ON(!usbhsp_is_dcp(pipe));
|
||||
|
||||
usbhs_fifo_enable(pipe);
|
||||
usbhsp_pipectrl_set(pipe, CCPL, CCPL);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* pipe module function
|
||||
*/
|
||||
int usbhs_pipe_probe(struct usbhs_priv *priv)
|
||||
{
|
||||
struct usbhs_pipe_info *info = usbhsp_priv_to_pipeinfo(priv);
|
||||
struct usbhs_pipe *pipe;
|
||||
struct device *dev = usbhs_priv_to_dev(priv);
|
||||
u32 *pipe_type = usbhs_get_dparam(priv, pipe_type);
|
||||
int pipe_size = usbhs_get_dparam(priv, pipe_size);
|
||||
int i;
|
||||
|
||||
/* This driver expects 1st pipe is DCP */
|
||||
if (pipe_type[0] != USB_ENDPOINT_XFER_CONTROL) {
|
||||
dev_err(dev, "1st PIPE is not DCP\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
info->pipe = kzalloc(sizeof(struct usbhs_pipe) * pipe_size, GFP_KERNEL);
|
||||
if (!info->pipe) {
|
||||
dev_err(dev, "Could not allocate pipe\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
info->size = pipe_size;
|
||||
|
||||
/*
|
||||
* init pipe
|
||||
*/
|
||||
usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
|
||||
pipe->priv = priv;
|
||||
usbhsp_type(pipe) = pipe_type[i] & USB_ENDPOINT_XFERTYPE_MASK;
|
||||
|
||||
dev_dbg(dev, "pipe %x\t: %s\n",
|
||||
i, usbhsp_pipe_name[pipe_type[i]]);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usbhs_pipe_remove(struct usbhs_priv *priv)
|
||||
{
|
||||
struct usbhs_pipe_info *info = usbhsp_priv_to_pipeinfo(priv);
|
||||
|
||||
kfree(info->pipe);
|
||||
}
|
105
drivers/usb/renesas_usbhs/pipe.h
Normal file
105
drivers/usb/renesas_usbhs/pipe.h
Normal file
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Renesas USB driver
|
||||
*
|
||||
* Copyright (C) 2011 Renesas Solutions Corp.
|
||||
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#ifndef RENESAS_USB_PIPE_H
|
||||
#define RENESAS_USB_PIPE_H
|
||||
|
||||
#include "./common.h"
|
||||
|
||||
/*
|
||||
* struct
|
||||
*/
|
||||
struct usbhs_pipe {
|
||||
u32 pipe_type; /* USB_ENDPOINT_XFER_xxx */
|
||||
|
||||
struct usbhs_priv *priv;
|
||||
|
||||
u32 flags;
|
||||
#define USBHS_PIPE_FLAGS_IS_USED (1 << 0)
|
||||
#define USBHS_PIPE_FLAGS_IS_DIR_IN (1 << 1)
|
||||
|
||||
void *mod_private;
|
||||
};
|
||||
|
||||
struct usbhs_pipe_info {
|
||||
struct usbhs_pipe *pipe;
|
||||
int size; /* array size of "pipe" */
|
||||
int bufnmb_last; /* FIXME : driver needs good allocator */
|
||||
};
|
||||
|
||||
/*
|
||||
* pipe list
|
||||
*/
|
||||
#define __usbhs_for_each_pipe(start, pos, info, i) \
|
||||
for (i = start, pos = (info)->pipe; \
|
||||
i < (info)->size; \
|
||||
i++, pos = (info)->pipe + i)
|
||||
|
||||
#define usbhs_for_each_pipe(pos, priv, i) \
|
||||
__usbhs_for_each_pipe(1, pos, &((priv)->pipe_info), i)
|
||||
|
||||
#define usbhs_for_each_pipe_with_dcp(pos, priv, i) \
|
||||
__usbhs_for_each_pipe(0, pos, &((priv)->pipe_info), i)
|
||||
|
||||
/*
|
||||
* pipe module probe / remove
|
||||
*/
|
||||
int usbhs_pipe_probe(struct usbhs_priv *priv);
|
||||
void usbhs_pipe_remove(struct usbhs_priv *priv);
|
||||
|
||||
/*
|
||||
* cfifo
|
||||
*/
|
||||
int usbhs_fifo_write(struct usbhs_pipe *pipe, u8 *buf, int len);
|
||||
int usbhs_fifo_read(struct usbhs_pipe *pipe, u8 *buf, int len);
|
||||
int usbhs_fifo_prepare_write(struct usbhs_pipe *pipe);
|
||||
int usbhs_fifo_prepare_read(struct usbhs_pipe *pipe);
|
||||
|
||||
void usbhs_fifo_enable(struct usbhs_pipe *pipe);
|
||||
void usbhs_fifo_disable(struct usbhs_pipe *pipe);
|
||||
void usbhs_fifo_stall(struct usbhs_pipe *pipe);
|
||||
|
||||
void usbhs_fifo_send_terminator(struct usbhs_pipe *pipe);
|
||||
|
||||
|
||||
/*
|
||||
* usb request
|
||||
*/
|
||||
void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req);
|
||||
void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req);
|
||||
|
||||
/*
|
||||
* pipe control
|
||||
*/
|
||||
struct usbhs_pipe
|
||||
*usbhs_pipe_malloc(struct usbhs_priv *priv,
|
||||
const struct usb_endpoint_descriptor *desc);
|
||||
|
||||
int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe);
|
||||
void usbhs_pipe_init(struct usbhs_priv *priv);
|
||||
int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe);
|
||||
void usbhs_pipe_clear_sequence(struct usbhs_pipe *pipe);
|
||||
|
||||
#define usbhs_pipe_number(p) (((u32)(p) - (u32)(p)->priv->pipe_info.pipe) / \
|
||||
sizeof(struct usbhs_pipe))
|
||||
|
||||
/*
|
||||
* dcp control
|
||||
*/
|
||||
struct usbhs_pipe *usbhs_dcp_malloc(struct usbhs_priv *priv);
|
||||
void usbhs_dcp_control_transfer_done(struct usbhs_pipe *pipe);
|
||||
|
||||
#endif /* RENESAS_USB_PIPE_H */
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
|
@ -890,8 +890,8 @@ static inline void usb_free_descriptors(struct usb_descriptor_header **v)
|
|||
/* utility wrapping a simple endpoint selection policy */
|
||||
|
||||
extern struct usb_ep *usb_ep_autoconfig(struct usb_gadget *,
|
||||
struct usb_endpoint_descriptor *) __devinit;
|
||||
struct usb_endpoint_descriptor *);
|
||||
|
||||
extern void usb_ep_autoconfig_reset(struct usb_gadget *) __devinit;
|
||||
extern void usb_ep_autoconfig_reset(struct usb_gadget *);
|
||||
|
||||
#endif /* __LINUX_USB_GADGET_H */
|
||||
|
|
149
include/linux/usb/renesas_usbhs.h
Normal file
149
include/linux/usb/renesas_usbhs.h
Normal file
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* Renesas USB
|
||||
*
|
||||
* Copyright (C) 2011 Renesas Solutions Corp.
|
||||
* Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
#ifndef RENESAS_USB_H
|
||||
#define RENESAS_USB_H
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/usb/ch9.h>
|
||||
|
||||
/*
|
||||
* module type
|
||||
*
|
||||
* it will be return value from get_id
|
||||
*/
|
||||
enum {
|
||||
USBHS_HOST = 0,
|
||||
USBHS_GADGET,
|
||||
USBHS_MAX,
|
||||
};
|
||||
|
||||
/*
|
||||
* callback functions table for driver
|
||||
*
|
||||
* These functions are called from platform for driver.
|
||||
* Callback function's pointer will be set before
|
||||
* renesas_usbhs_platform_callback :: hardware_init was called
|
||||
*/
|
||||
struct renesas_usbhs_driver_callback {
|
||||
int (*notify_hotplug)(struct platform_device *pdev);
|
||||
};
|
||||
|
||||
/*
|
||||
* callback functions for platform
|
||||
*
|
||||
* These functions are called from driver for platform
|
||||
*/
|
||||
struct renesas_usbhs_platform_callback {
|
||||
|
||||
/*
|
||||
* option:
|
||||
*
|
||||
* Hardware init function for platform.
|
||||
* it is called when driver was probed.
|
||||
*/
|
||||
int (*hardware_init)(struct platform_device *pdev);
|
||||
|
||||
/*
|
||||
* option:
|
||||
*
|
||||
* Hardware exit function for platform.
|
||||
* it is called when driver was removed
|
||||
*/
|
||||
void (*hardware_exit)(struct platform_device *pdev);
|
||||
|
||||
/*
|
||||
* option:
|
||||
*
|
||||
* Phy reset for platform
|
||||
*/
|
||||
void (*phy_reset)(struct platform_device *pdev);
|
||||
|
||||
/*
|
||||
* get USB ID function
|
||||
* - USBHS_HOST
|
||||
* - USBHS_GADGET
|
||||
*/
|
||||
int (*get_id)(struct platform_device *pdev);
|
||||
|
||||
/*
|
||||
* get VBUS status function.
|
||||
*/
|
||||
int (*get_vbus)(struct platform_device *pdev);
|
||||
};
|
||||
|
||||
/*
|
||||
* parameters for renesas usbhs
|
||||
*
|
||||
* some register needs USB chip specific parameters.
|
||||
* This struct show it to driver
|
||||
*/
|
||||
struct renesas_usbhs_driver_param {
|
||||
/*
|
||||
* pipe settings
|
||||
*/
|
||||
u32 *pipe_type; /* array of USB_ENDPOINT_XFER_xxx (from ep0) */
|
||||
int pipe_size; /* pipe_type array size */
|
||||
|
||||
/*
|
||||
* option:
|
||||
*
|
||||
* for BUSWAIT :: BWAIT
|
||||
* */
|
||||
int buswait_bwait;
|
||||
};
|
||||
|
||||
/*
|
||||
* option:
|
||||
*
|
||||
* platform information for renesas_usbhs driver.
|
||||
*/
|
||||
struct renesas_usbhs_platform_info {
|
||||
/*
|
||||
* option:
|
||||
*
|
||||
* platform set these functions before
|
||||
* call platform_add_devices if needed
|
||||
*/
|
||||
struct renesas_usbhs_platform_callback platform_callback;
|
||||
|
||||
/*
|
||||
* driver set these callback functions pointer.
|
||||
* platform can use it on callback functions
|
||||
*/
|
||||
struct renesas_usbhs_driver_callback driver_callback;
|
||||
|
||||
/*
|
||||
* option:
|
||||
*
|
||||
* driver use these param for some register
|
||||
*/
|
||||
struct renesas_usbhs_driver_param driver_param;
|
||||
};
|
||||
|
||||
/*
|
||||
* macro for platform
|
||||
*/
|
||||
#define renesas_usbhs_get_info(pdev)\
|
||||
((struct renesas_usbhs_platform_info *)(pdev)->dev.platform_data)
|
||||
|
||||
#define renesas_usbhs_call_notify_hotplug(pdev) \
|
||||
({ \
|
||||
struct renesas_usbhs_driver_callback *dc; \
|
||||
dc = &(renesas_usbhs_get_info(pdev)->driver_callback); \
|
||||
if (dc) \
|
||||
dc->notify_hotplug(pdev); \
|
||||
})
|
||||
#endif /* RENESAS_USB_H */
|
Loading…
Reference in a new issue