diff options
144 files changed, 5964 insertions, 10544 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb index 864637f..3a4abfc 100644 --- a/Documentation/ABI/testing/sysfs-bus-usb +++ b/Documentation/ABI/testing/sysfs-bus-usb @@ -1,3 +1,23 @@ +What: /sys/bus/usb/devices/INTERFACE/authorized +Date: August 2015 +Description: + This allows to authorize (1) or deauthorize (0) + individual interfaces instead a whole device + in contrast to the device authorization. + If a deauthorized interface will be authorized + so the driver probing must be triggered manually + by writing INTERFACE to /sys/bus/usb/drivers_probe + This allows to avoid side-effects with drivers + that need multiple interfaces. + A deauthorized interface cannot be probed or claimed. + +What: /sys/bus/usb/devices/usbX/interface_authorized_default +Date: August 2015 +Description: + This is used as value that determines if interfaces + would be authorized by default. + The value can be 1 or 0. It's by default 1. + What: /sys/bus/usb/device/.../authorized Date: July 2008 KernelVersion: 2.6.26 diff --git a/Documentation/devicetree/bindings/phy/brcm,cygnus-pcie-phy.txt b/Documentation/devicetree/bindings/phy/brcm,cygnus-pcie-phy.txt new file mode 100644 index 0000000..761c4bc --- /dev/null +++ b/Documentation/devicetree/bindings/phy/brcm,cygnus-pcie-phy.txt @@ -0,0 +1,47 @@ +Broadcom Cygnus PCIe PHY + +Required properties: +- compatible: must be "brcm,cygnus-pcie-phy" +- reg: base address and length of the PCIe PHY block +- #address-cells: must be 1 +- #size-cells: must be 0 + +Each PCIe PHY should be represented by a child node + +Required properties For the child node: +- reg: the PHY ID +0 - PCIe RC 0 +1 - PCIe RC 1 +- #phy-cells: must be 0 + +Example: + pcie_phy: phy@0301d0a0 { + compatible = "brcm,cygnus-pcie-phy"; + reg = <0x0301d0a0 0x14>; + + pcie0_phy: phy@0 { + reg = <0>; + #phy-cells = <0>; + }; + + pcie1_phy: phy@1 { + reg = <1>; + #phy-cells = <0>; + }; + }; + + /* users of the PCIe phy */ + + pcie0: pcie@18012000 { + ... + ... + phys = <&pcie0_phy>; + phy-names = "pcie-phy"; + }; + + pcie1: pcie@18013000 { + ... + ... + phys = <pcie1_phy>; + phy-names = "pcie-phy"; + }; diff --git a/Documentation/devicetree/bindings/phy/phy-mt65xx-usb.txt b/Documentation/devicetree/bindings/phy/phy-mt65xx-usb.txt new file mode 100644 index 0000000..00100cf --- /dev/null +++ b/Documentation/devicetree/bindings/phy/phy-mt65xx-usb.txt @@ -0,0 +1,68 @@ +mt65xx USB3.0 PHY binding +-------------------------- + +This binding describes a usb3.0 phy for mt65xx platforms of Medaitek SoC. + +Required properties (controller (parent) node): + - compatible : should be "mediatek,mt8173-u3phy" + - reg : offset and length of register for phy, exclude port's + register. + - clocks : a list of phandle + clock-specifier pairs, one for each + entry in clock-names + - clock-names : must contain + "u3phya_ref": for reference clock of usb3.0 analog phy. + +Required nodes : a sub-node is required for each port the controller + provides. Address range information including the usual + 'reg' property is used inside these nodes to describe + the controller's topology. + +Required properties (port (child) node): +- reg : address and length of the register set for the port. +- #phy-cells : should be 1 (See second example) + cell after port phandle is phy type from: + - PHY_TYPE_USB2 + - PHY_TYPE_USB3 + +Example: + +u3phy: usb-phy@11290000 { + compatible = "mediatek,mt8173-u3phy"; + reg = <0 0x11290000 0 0x800>; + clocks = <&apmixedsys CLK_APMIXED_REF2USB_TX>; + clock-names = "u3phya_ref"; + #address-cells = <2>; + #size-cells = <2>; + ranges; + status = "okay"; + + phy_port0: port@11290800 { + reg = <0 0x11290800 0 0x800>; + #phy-cells = <1>; + status = "okay"; + }; + + phy_port1: port@11291000 { + reg = <0 0x11291000 0 0x800>; + #phy-cells = <1>; + status = "okay"; + }; +}; + +Specifying phy control of devices +--------------------------------- + +Device nodes should specify the configuration required in their "phys" +property, containing a phandle to the phy port node and a device type; +phy-names for each port are optional. + +Example: + +#include <dt-bindings/phy/phy.h> + +usb30: usb@11270000 { + ... + phys = <&phy_port0 PHY_TYPE_USB3>; + phy-names = "usb3-0"; + ... +}; diff --git a/Documentation/devicetree/bindings/phy/samsung-phy.txt b/Documentation/devicetree/bindings/phy/samsung-phy.txt index 60c6f2a..0289d3b 100644 --- a/Documentation/devicetree/bindings/phy/samsung-phy.txt +++ b/Documentation/devicetree/bindings/phy/samsung-phy.txt @@ -44,6 +44,9 @@ Required properties: - the "ref" clock is used to get the rate of the clock provided to the PHY module +Optional properties: +- vbus-supply: power-supply phandle for vbus power source + The first phandle argument in the PHY specifier identifies the PHY, its meaning is compatible dependent. For the currently supported SoCs (Exynos 4210 and Exynos 4212) it is as follows: diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt index a057b75..781296b 100644 --- a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt +++ b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt @@ -27,10 +27,6 @@ Optional properties: - vbus-supply: reference to the VBUS regulator - maximum-speed: limit the maximum connection speed to "full-speed". - tpl-support: TPL (Targeted Peripheral List) feature for targeted hosts -- fsl,usbmisc: (FSL only) phandler of non-core register device, with one - argument that indicate usb controller index -- disable-over-current: (FSL only) disable over current detect -- external-vbus-divider: (FSL only) enables off-chip resistor divider for Vbus - itc-setting: interrupt threshold control register control, the setting should be aligned with ITC bits at register USBCMD. - ahb-burst-config: it is vendor dependent, the required value should be @@ -41,11 +37,28 @@ Optional properties: - tx-burst-size-dword: it is vendor dependent, the tx burst size in dword (4 bytes), This register represents the maximum length of a the burst in 32-bit words while moving data from system memory to the USB - bus, changing this value takes effect only the SBUSCFG.AHBBRST is 0. + bus, the value of this property will only take effect if property + "ahb-burst-config" is set to 0, if this property is missing the reset + default of the hardware implementation will be used. - rx-burst-size-dword: it is vendor dependent, the rx burst size in dword (4 bytes), This register represents the maximum length of a the burst in 32-bit words while moving data from the USB bus to system memory, - changing this value takes effect only the SBUSCFG.AHBBRST is 0. + the value of this property will only take effect if property + "ahb-burst-config" is set to 0, if this property is missing the reset + default of the hardware implementation will be used. +- extcon: phandles to external connector devices. First phandle should point to + external connector, which provide "USB" cable events, the second should point + to external connector device, which provide "USB-HOST" cable events. If one + of the external connector devices is not required, empty <0> phandle should + be specified. +- phy-clkgate-delay-us: the delay time (us) between putting the PHY into + low power mode and gating the PHY clock. + +i.mx specific properties +- fsl,usbmisc: phandler of non-core register device, with one + argument that indicate usb controller index +- disable-over-current: disable over current detect +- external-vbus-divider: enables off-chip resistor divider for Vbus Example: @@ -62,4 +75,6 @@ Example: ahb-burst-config = <0x0>; tx-burst-size-dword = <0x10>; /* 64 bytes */ rx-burst-size-dword = <0x10>; + extcon = <0>, <&usb_id>; + phy-clkgate-delay-us = <400>; }; diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt index 0815eac..9ff48e0 100644 --- a/Documentation/devicetree/bindings/usb/dwc3.txt +++ b/Documentation/devicetree/bindings/usb/dwc3.txt @@ -35,11 +35,16 @@ Optional properties: LTSSM during USB3 Compliance mode. - snps,dis_u3_susphy_quirk: when set core will disable USB3 suspend phy. - snps,dis_u2_susphy_quirk: when set core will disable USB2 suspend phy. + - snps,dis_enblslpm_quirk: when set clears the enblslpm in GUSB2PHYCFG, + disabling the suspend signal to the PHY. - snps,is-utmi-l1-suspend: true when DWC3 asserts output signal utmi_l1_suspend_n, false when asserts utmi_sleep_n - snps,hird-threshold: HIRD threshold - snps,hsphy_interface: High-Speed PHY interface selection between "utmi" for UTMI+ and "ulpi" for ULPI when the DWC_USB3_HSPHY_INTERFACE has value 3. + - snps,quirk-frame-length-adjustment: Value for GFLADJ_30MHZ field of GFLADJ + register for post-silicon frame length adjustment when the + fladj_30mhz_sdbnd signal is invalid or incorrect. This is usually a subnode to DWC3 glue to which it is connected. diff --git a/Documentation/usb/authorization.txt b/Documentation/usb/authorization.txt index c069b68..c7e985f 100644 --- a/Documentation/usb/authorization.txt +++ b/Documentation/usb/authorization.txt @@ -90,3 +90,34 @@ etc, but you get the idea. Anybody with access to a device gadget kit can fake descriptors and device info. Don't trust that. You are welcome. + +Interface authorization +----------------------- +There is a similar approach to allow or deny specific USB interfaces. +That allows to block only a subset of an USB device. + +Authorize an interface: +$ echo 1 > /sys/bus/usb/devices/INTERFACE/authorized + +Deauthorize an interface: +$ echo 0 > /sys/bus/usb/devices/INTERFACE/authorized + +The default value for new interfaces +on a particular USB bus can be changed, too. + +Allow interfaces per default: +$ echo 1 > /sys/bus/usb/devices/usbX/interface_authorized_default + +Deny interfaces per default: +$ echo 0 > /sys/bus/usb/devices/usbX/interface_authorized_default + +Per default the interface_authorized_default bit is 1. +So all interfaces would authorized per default. + +Note: +If a deauthorized interface will be authorized so the driver probing must +be triggered manually by writing INTERFACE to /sys/bus/usb/drivers_probe + +For drivers that need multiple interfaces all needed interfaces should be +authroized first. After that the drivers should be probed. +This avoids side effects. diff --git a/MAINTAINERS b/MAINTAINERS index ed6dccf..94a97bc 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1300,6 +1300,13 @@ F: arch/arm/mach-mediatek/ N: mtk K: mediatek +ARM/Mediatek USB3 PHY DRIVER +M: Chunfeng Yun <chunfeng.yun@mediatek.com> +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +L: linux-mediatek@lists.infradead.org (moderated for non-subscribers) +S: Maintained +F: drivers/phy/phy-mt65xx-usb3.c + ARM/MICREL KS8695 ARCHITECTURE M: Greg Ungerer <gerg@uclinux.org> L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) diff --git a/arch/arm/mach-s3c64xx/mach-crag6410.c b/arch/arm/mach-s3c64xx/mach-crag6410.c index 65c426b..14bd9ae 100644 --- a/arch/arm/mach-s3c64xx/mach-crag6410.c +++ b/arch/arm/mach-s3c64xx/mach-crag6410.c @@ -809,7 +809,7 @@ static const struct gpio_led_platform_data gpio_leds_pdata = { .num_leds = ARRAY_SIZE(gpio_leds), }; -static struct s3c_hsotg_plat crag6410_hsotg_pdata; +static struct dwc2_hsotg_plat crag6410_hsotg_pdata; static void __init crag6410_machine_init(void) { @@ -835,7 +835,7 @@ static void __init crag6410_machine_init(void) s3c_i2c0_set_platdata(&i2c0_pdata); s3c_i2c1_set_platdata(&i2c1_pdata); s3c_fb_set_platdata(&crag6410_lcd_pdata); - s3c_hsotg_set_platdata(&crag6410_hsotg_pdata); + dwc2_hsotg_set_platdata(&crag6410_hsotg_pdata); i2c_register_board_info(0, i2c_devs0, ARRAY_SIZE(i2c_devs0)); i2c_register_board_info(1, i2c_devs1, ARRAY_SIZE(i2c_devs1)); diff --git a/arch/arm/mach-s3c64xx/mach-smartq.c b/arch/arm/mach-s3c64xx/mach-smartq.c index b3d1353..719843d 100644 --- a/arch/arm/mach-s3c64xx/mach-smartq.c +++ b/arch/arm/mach-s3c64xx/mach-smartq.c @@ -189,7 +189,7 @@ static struct s3c_hwmon_pdata smartq_hwmon_pdata __initdata = { }, }; -static struct s3c_hsotg_plat smartq_hsotg_pdata; +static struct dwc2_hsotg_plat smartq_hsotg_pdata; static int __init smartq_lcd_setup_gpio(void) { @@ -382,7 +382,7 @@ void __init smartq_map_io(void) void __init smartq_machine_init(void) { s3c_i2c0_set_platdata(NULL); - s3c_hsotg_set_platdata(&smartq_hsotg_pdata); + dwc2_hsotg_set_platdata(&smartq_hsotg_pdata); s3c_hwmon_set_platdata(&smartq_hwmon_pdata); s3c_sdhci1_set_platdata(&smartq_internal_hsmmc_pdata); s3c_sdhci2_set_platdata(&smartq_internal_hsmmc_pdata); diff --git a/arch/arm/mach-s3c64xx/mach-smdk6410.c b/arch/arm/mach-s3c64xx/mach-smdk6410.c index d590b88..286c9bd 100644 --- a/arch/arm/mach-s3c64xx/mach-smdk6410.c +++ b/arch/arm/mach-s3c64xx/mach-smdk6410.c @@ -628,7 +628,7 @@ static struct platform_pwm_backlight_data smdk6410_bl_data = { .enable_gpio = -1, }; -static struct s3c_hsotg_plat smdk6410_hsotg_pdata; +static struct dwc2_hsotg_plat smdk6410_hsotg_pdata; static void __init smdk6410_map_io(void) { @@ -659,7 +659,7 @@ static void __init smdk6410_machine_init(void) s3c_i2c0_set_platdata(NULL); s3c_i2c1_set_platdata(NULL); s3c_fb_set_platdata(&smdk6410_lcd_pdata); - s3c_hsotg_set_platdata(&smdk6410_hsotg_pdata); + dwc2_hsotg_set_platdata(&smdk6410_hsotg_pdata); samsung_keypad_set_platdata(&smdk6410_keypad_data); diff --git a/arch/arm/plat-samsung/devs.c b/arch/arm/plat-samsung/devs.c index 83c7d15..8207462 100644 --- a/arch/arm/plat-samsung/devs.c +++ b/arch/arm/plat-samsung/devs.c @@ -1042,11 +1042,11 @@ struct platform_device s3c_device_usb_hsotg = { }, }; -void __init s3c_hsotg_set_platdata(struct s3c_hsotg_plat *pd) +void __init dwc2_hsotg_set_platdata(struct dwc2_hsotg_plat *pd) { - struct s3c_hsotg_plat *npd; + struct dwc2_hsotg_plat *npd; - npd = s3c_set_platdata(pd, sizeof(struct s3c_hsotg_plat), + npd = s3c_set_platdata(pd, sizeof(struct dwc2_hsotg_plat), &s3c_device_usb_hsotg); if (!npd->phy_init) diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 47da573..7eb5859d 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -206,6 +206,15 @@ config PHY_HIX5HD2_SATA help Support for SATA PHY on Hisilicon hix5hd2 Soc. +config PHY_MT65XX_USB3 + tristate "Mediatek USB3.0 PHY Driver" + depends on ARCH_MEDIATEK && OF + select GENERIC_PHY + help + Say 'Y' here to add support for Mediatek USB3.0 PHY driver + for mt65xx SoCs. it supports two usb2.0 ports and + one usb3.0 port. + config PHY_SUN4I_USB tristate "Allwinner sunxi SoC USB PHY driver" depends on ARCH_SUNXI && HAS_IOMEM && OF @@ -371,4 +380,13 @@ config PHY_BRCMSTB_SATA Enable this to support the SATA3 PHY on 28nm Broadcom STB SoCs. Likely useful only with CONFIG_SATA_BRCMSTB enabled. +config PHY_CYGNUS_PCIE + tristate "Broadcom Cygnus PCIe PHY driver" + depends on OF && (ARCH_BCM_CYGNUS || COMPILE_TEST) + select GENERIC_PHY + default ARCH_BCM_CYGNUS + help + Enable this to support the Broadcom Cygnus PCIe PHY. + If unsure, say N. + endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index a5b18c1..075db1a 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.o obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o +obj-$(CONFIG_PHY_MT65XX_USB3) += phy-mt65xx-usb3.o obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o obj-$(CONFIG_PHY_SUN9I_USB) += phy-sun9i-usb.o obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-exynos-usb2.o @@ -46,3 +47,4 @@ obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-14nm.o obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o obj-$(CONFIG_PHY_BRCMSTB_SATA) += phy-brcmstb-sata.o obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o +obj-$(CONFIG_PHY_CYGNUS_PCIE) += phy-bcm-cygnus-pcie.o diff --git a/drivers/phy/phy-bcm-cygnus-pcie.c b/drivers/phy/phy-bcm-cygnus-pcie.c new file mode 100644 index 0000000..7ad72b7 --- /dev/null +++ b/drivers/phy/phy-bcm-cygnus-pcie.c @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2015 Broadcom Corporation + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> + +#define PCIE_CFG_OFFSET 0x00 +#define PCIE1_PHY_IDDQ_SHIFT 10 +#define PCIE0_PHY_IDDQ_SHIFT 2 + +enum cygnus_pcie_phy_id { + CYGNUS_PHY_PCIE0 = 0, + CYGNUS_PHY_PCIE1, + MAX_NUM_PHYS, +}; + +struct cygnus_pcie_phy_core; + +/** + * struct cygnus_pcie_phy - Cygnus PCIe PHY device + * @core: pointer to the Cygnus PCIe PHY core control + * @id: internal ID to identify the Cygnus PCIe PHY + * @phy: pointer to the kernel PHY device + */ +struct cygnus_pcie_phy { + struct cygnus_pcie_phy_core *core; + enum cygnus_pcie_phy_id id; + struct phy *phy; +}; + +/** + * struct cygnus_pcie_phy_core - Cygnus PCIe PHY core control + * @dev: pointer to device + * @base: base register + * @lock: mutex to protect access to individual PHYs + * @phys: pointer to Cygnus PHY device + */ +struct cygnus_pcie_phy_core { + struct device *dev; + void __iomem *base; + struct mutex lock; + struct cygnus_pcie_phy phys[MAX_NUM_PHYS]; +}; + +static int cygnus_pcie_power_config(struct cygnus_pcie_phy *phy, bool enable) +{ + struct cygnus_pcie_phy_core *core = phy->core; + unsigned shift; + u32 val; + + mutex_lock(&core->lock); + + switch (phy->id) { + case CYGNUS_PHY_PCIE0: + shift = PCIE0_PHY_IDDQ_SHIFT; + break; + + case CYGNUS_PHY_PCIE1: + shift = PCIE1_PHY_IDDQ_SHIFT; + break; + + default: + mutex_unlock(&core->lock); + dev_err(core->dev, "PCIe PHY %d invalid\n", phy->id); + return -EINVAL; + } + + if (enable) { + val = readl(core->base + PCIE_CFG_OFFSET); + val &= ~BIT(shift); + writel(val, core->base + PCIE_CFG_OFFSET); + /* + * Wait 50 ms for the PCIe Serdes to stabilize after the analog + * front end is brought up + */ + msleep(50); + } else { + val = readl(core->base + PCIE_CFG_OFFSET); + val |= BIT(shift); + writel(val, core->base + PCIE_CFG_OFFSET); + } + + mutex_unlock(&core->lock); + dev_dbg(core->dev, "PCIe PHY %d %s\n", phy->id, + enable ? "enabled" : "disabled"); + return 0; +} + +static int cygnus_pcie_phy_power_on(struct phy *p) +{ + struct cygnus_pcie_phy *phy = phy_get_drvdata(p); + + return cygnus_pcie_power_config(phy, true); +} + +static int cygnus_pcie_phy_power_off(struct phy *p) +{ + struct cygnus_pcie_phy *phy = phy_get_drvdata(p); + + return cygnus_pcie_power_config(phy, false); +} + +static struct phy_ops cygnus_pcie_phy_ops = { + .power_on = cygnus_pcie_phy_power_on, + .power_off = cygnus_pcie_phy_power_off, + .owner = THIS_MODULE, +}; + +static int cygnus_pcie_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node, *child; + struct cygnus_pcie_phy_core *core; + struct phy_provider *provider; + struct resource *res; + unsigned cnt = 0; + + if (of_get_child_count(node) == 0) { + dev_err(dev, "PHY no child node\n"); + return -ENODEV; + } + + core = devm_kzalloc(dev, sizeof(*core), GFP_KERNEL); + if (!core) + return -ENOMEM; + + core->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + core->base = devm_ioremap_resource(dev, res); + if (IS_ERR(core->base)) + return PTR_ERR(core->base); + + mutex_init(&core->lock); + + for_each_available_child_of_node(node, child) { + unsigned int id; + struct cygnus_pcie_phy *p; + + if (of_property_read_u32(child, "reg", &id)) { + dev_err(dev, "missing reg property for %s\n", + child->name); + return -EINVAL; + } + + if (id >= MAX_NUM_PHYS) { + dev_err(dev, "invalid PHY id: %u\n", id); + return -EINVAL; + } + + if (core->phys[id].phy) { + dev_err(dev, "duplicated PHY id: %u\n", id); + return -EINVAL; + } + + p = &core->phys[id]; + p->phy = devm_phy_create(dev, child, &cygnus_pcie_phy_ops); + if (IS_ERR(p->phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(p->phy); + } + + p->core = core; + p->id = id; + phy_set_drvdata(p->phy, p); + cnt++; + } + + dev_set_drvdata(dev, core); + + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(provider)) { + dev_err(dev, "failed to register PHY provider\n"); + return PTR_ERR(provider); + } + + dev_dbg(dev, "registered %u PCIe PHY(s)\n", cnt); + + return 0; +} + +static const struct of_device_id cygnus_pcie_phy_match_table[] = { + { .compatible = "brcm,cygnus-pcie-phy" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, cygnus_pcie_phy_match_table); + +static struct platform_driver cygnus_pcie_phy_driver = { + .driver = { + .name = "cygnus-pcie-phy", + .of_match_table = cygnus_pcie_phy_match_table, + }, + .probe = cygnus_pcie_phy_probe, +}; +module_platform_driver(cygnus_pcie_phy_driver); + +MODULE_AUTHOR("Ray Jui <rjui@broadcom.com>"); +MODULE_DESCRIPTION("Broadcom Cygnus PCIe PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-mt65xx-usb3.c b/drivers/phy/phy-mt65xx-usb3.c new file mode 100644 index 0000000..f30b28b --- /dev/null +++ b/drivers/phy/phy-mt65xx-usb3.c @@ -0,0 +1,506 @@ +/* + * Copyright (c) 2015 MediaTek Inc. + * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include <dt-bindings/phy/phy.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> + +/* + * for sifslv2 register, but exclude port's; + * relative to USB3_SIF2_BASE base address + */ +#define SSUSB_SIFSLV_SPLLC 0x0000 + +/* offsets of sub-segment in each port registers */ +#define SSUSB_SIFSLV_U2PHY_COM_BASE 0x0000 +#define SSUSB_SIFSLV_U3PHYD_BASE 0x0100 +#define SSUSB_USB30_PHYA_SIV_B_BASE 0x0300 +#define SSUSB_SIFSLV_U3PHYA_DA_BASE 0x0400 + +#define U3P_USBPHYACR0 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0000) +#define PA0_RG_U2PLL_FORCE_ON BIT(15) + +#define U3P_USBPHYACR2 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0008) +#define PA2_RG_SIF_U2PLL_FORCE_EN BIT(18) + +#define U3P_USBPHYACR5 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0014) +#define PA5_RG_U2_HSTX_SRCTRL GENMASK(14, 12) +#define PA5_RG_U2_HSTX_SRCTRL_VAL(x) ((0x7 & (x)) << 12) +#define PA5_RG_U2_HS_100U_U3_EN BIT(11) + +#define U3P_USBPHYACR6 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0018) +#define PA6_RG_U2_ISO_EN BIT(31) +#define PA6_RG_U2_BC11_SW_EN BIT(23) +#define PA6_RG_U2_OTG_VBUSCMP_EN BIT(20) + +#define U3P_U2PHYACR4 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0020) +#define P2C_RG_USB20_GPIO_CTL BIT(9) +#define P2C_USB20_GPIO_MODE BIT(8) +#define P2C_U2_GPIO_CTR_MSK (P2C_RG_USB20_GPIO_CTL | P2C_USB20_GPIO_MODE) + +#define U3D_U2PHYDCR0 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0060) +#define P2C_RG_SIF_U2PLL_FORCE_ON BIT(24) + +#define U3P_U2PHYDTM0 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x0068) +#define P2C_FORCE_UART_EN BIT(26) +#define P2C_FORCE_DATAIN BIT(23) +#define P2C_FORCE_DM_PULLDOWN BIT(21) +#define P2C_FORCE_DP_PULLDOWN BIT(20) +#define P2C_FORCE_XCVRSEL BIT(19) +#define P2C_FORCE_SUSPENDM BIT(18) +#define P2C_FORCE_TERMSEL BIT(17) +#define P2C_RG_DATAIN GENMASK(13, 10) +#define P2C_RG_DATAIN_VAL(x) ((0xf & (x)) << 10) +#define P2C_RG_DMPULLDOWN BIT(7) +#define P2C_RG_DPPULLDOWN BIT(6) +#define P2C_RG_XCVRSEL GENMASK(5, 4) +#define P2C_RG_XCVRSEL_VAL(x) ((0x3 & (x)) << 4) +#define P2C_RG_SUSPENDM BIT(3) +#define P2C_RG_TERMSEL BIT(2) +#define P2C_DTM0_PART_MASK \ + (P2C_FORCE_DATAIN | P2C_FORCE_DM_PULLDOWN | \ + P2C_FORCE_DP_PULLDOWN | P2C_FORCE_XCVRSEL | \ + P2C_FORCE_TERMSEL | P2C_RG_DMPULLDOWN | \ + P2C_RG_DPPULLDOWN | P2C_RG_TERMSEL) + +#define U3P_U2PHYDTM1 (SSUSB_SIFSLV_U2PHY_COM_BASE + 0x006C) +#define P2C_RG_UART_EN BIT(16) +#define P2C_RG_VBUSVALID BIT(5) +#define P2C_RG_SESSEND BIT(4) +#define P2C_RG_AVALID BIT(2) + +#define U3P_U3_PHYA_REG0 (SSUSB_USB30_PHYA_SIV_B_BASE + 0x0000) +#define P3A_RG_U3_VUSB10_ON BIT(5) + +#define U3P_U3_PHYA_REG6 (SSUSB_USB30_PHYA_SIV_B_BASE + 0x0018) +#define P3A_RG_TX_EIDLE_CM GENMASK(31, 28) +#define P3A_RG_TX_EIDLE_CM_VAL(x) ((0xf & (x)) << 28) + +#define U3P_U3_PHYA_REG9 (SSUSB_USB30_PHYA_SIV_B_BASE + 0x0024) +#define P3A_RG_RX_DAC_MUX GENMASK(5, 1) +#define P3A_RG_RX_DAC_MUX_VAL(x) ((0x1f & (x)) << 1) + +#define U3P_U3PHYA_DA_REG0 (SSUSB_SIFSLV_U3PHYA_DA_BASE + 0x0000) +#define P3A_RG_XTAL_EXT_EN_U3 GENMASK(11, 10) +#define P3A_RG_XTAL_EXT_EN_U3_VAL(x) ((0x3 & (x)) << 10) + +#define U3P_PHYD_CDR1 (SSUSB_SIFSLV_U3PHYD_BASE + 0x005c) +#define P3D_RG_CDR_BIR_LTD1 GENMASK(28, 24) +#define P3D_RG_CDR_BIR_LTD1_VAL(x) ((0x1f & (x)) << 24) +#define P3D_RG_CDR_BIR_LTD0 GENMASK(12, 8) +#define P3D_RG_CDR_BIR_LTD0_VAL(x) ((0x1f & (x)) << 8) + +#define U3P_XTALCTL3 (SSUSB_SIFSLV_SPLLC + 0x0018) +#define XC3_RG_U3_XTAL_RX_PWD BIT(9) +#define XC3_RG_U3_FRC_XTAL_RX_PWD BIT(8) + +struct mt65xx_phy_instance { + struct phy *phy; + void __iomem *port_base; + u32 index; + u8 type; +}; + +struct mt65xx_u3phy { + struct device *dev; + void __iomem *sif_base; /* include sif2, but exclude port's */ + struct clk *u3phya_ref; /* reference clock of usb3 anolog phy */ + struct mt65xx_phy_instance **phys; + int nphys; +}; + +static void phy_instance_init(struct mt65xx_u3phy *u3phy, + struct mt65xx_phy_instance *instance) +{ + void __iomem *port_base = instance->port_base; + u32 index = instance->index; + u32 tmp; + + /* switch to USB function. (system register, force ip into usb mode) */ + tmp = readl(port_base + U3P_U2PHYDTM0); + tmp &= ~P2C_FORCE_UART_EN; + tmp |= P2C_RG_XCVRSEL_VAL(1) | P2C_RG_DATAIN_VAL(0); + writel(tmp, port_base + U3P_U2PHYDTM0); + + tmp = readl(port_base + U3P_U2PHYDTM1); + tmp &= ~P2C_RG_UART_EN; + writel(tmp, port_base + U3P_U2PHYDTM1); + + if (!index) { + tmp = readl(port_base + U3P_U2PHYACR4); + tmp &= ~P2C_U2_GPIO_CTR_MSK; + writel(tmp, port_base + U3P_U2PHYACR4); + + tmp = readl(port_base + U3P_USBPHYACR2); + tmp |= PA2_RG_SIF_U2PLL_FORCE_EN; + writel(tmp, port_base + U3P_USBPHYACR2); + + tmp = readl(port_base + U3D_U2PHYDCR0); + tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON; + writel(tmp, port_base + U3D_U2PHYDCR0); + } else { + tmp = readl(port_base + U3D_U2PHYDCR0); + tmp |= P2C_RG_SIF_U2PLL_FORCE_ON; + writel(tmp, port_base + U3D_U2PHYDCR0); + + tmp = readl(port_base + U3P_U2PHYDTM0); + tmp |= P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM; + writel(tmp, port_base + U3P_U2PHYDTM0); + } + + /* DP/DM BC1.1 path Disable */ + tmp = readl(port_base + U3P_USBPHYACR6); + tmp &= ~PA6_RG_U2_BC11_SW_EN; + writel(tmp, port_base + U3P_USBPHYACR6); + + tmp = readl(port_base + U3P_U3PHYA_DA_REG0); + tmp &= ~P3A_RG_XTAL_EXT_EN_U3; + tmp |= P3A_RG_XTAL_EXT_EN_U3_VAL(2); + writel(tmp, port_base + U3P_U3PHYA_DA_REG0); + + tmp = readl(port_base + U3P_U3_PHYA_REG9); + tmp &= ~P3A_RG_RX_DAC_MUX; + tmp |= P3A_RG_RX_DAC_MUX_VAL(4); + writel(tmp, port_base + U3P_U3_PHYA_REG9); + + tmp = readl(port_base + U3P_U3_PHYA_REG6); + tmp &= ~P3A_RG_TX_EIDLE_CM; + tmp |= P3A_RG_TX_EIDLE_CM_VAL(0xe); + writel(tmp, port_base + U3P_U3_PHYA_REG6); + + tmp = readl(port_base + U3P_PHYD_CDR1); + tmp &= ~(P3D_RG_CDR_BIR_LTD0 | P3D_RG_CDR_BIR_LTD1); + tmp |= P3D_RG_CDR_BIR_LTD0_VAL(0xc) | P3D_RG_CDR_BIR_LTD1_VAL(0x3); + writel(tmp, port_base + U3P_PHYD_CDR1); + + dev_dbg(u3phy->dev, "%s(%d)\n", __func__, index); +} + +static void phy_instance_power_on(struct mt65xx_u3phy *u3phy, + struct mt65xx_phy_instance *instance) +{ + void __iomem *port_base = instance->port_base; + u32 index = instance->index; + u32 tmp; + + if (!index) { + /* Set RG_SSUSB_VUSB10_ON as 1 after VUSB10 ready */ + tmp = readl(port_base + U3P_U3_PHYA_REG0); + tmp |= P3A_RG_U3_VUSB10_ON; + writel(tmp, port_base + U3P_U3_PHYA_REG0); + } + + /* (force_suspendm=0) (let suspendm=1, enable usb 480MHz pll) */ + tmp = readl(port_base + U3P_U2PHYDTM0); + tmp &= ~(P2C_FORCE_SUSPENDM | P2C_RG_XCVRSEL); + tmp &= ~(P2C_RG_DATAIN | P2C_DTM0_PART_MASK); + writel(tmp, port_base + U3P_U2PHYDTM0); + + /* OTG Enable */ + tmp = readl(port_base + U3P_USBPHYACR6); + tmp |= PA6_RG_U2_OTG_VBUSCMP_EN; + writel(tmp, port_base + U3P_USBPHYACR6); + + if (!index) { + tmp = readl(u3phy->sif_base + U3P_XTALCTL3); + tmp |= XC3_RG_U3_XTAL_RX_PWD | XC3_RG_U3_FRC_XTAL_RX_PWD; + writel(tmp, u3phy->sif_base + U3P_XTALCTL3); + + /* [mt8173]disable Change 100uA current from SSUSB */ + tmp = readl(port_base + U3P_USBPHYACR5); + tmp &= ~PA5_RG_U2_HS_100U_U3_EN; + writel(tmp, port_base + U3P_USBPHYACR5); + } + + tmp = readl(port_base + U3P_U2PHYDTM1); + tmp |= P2C_RG_VBUSVALID | P2C_RG_AVALID; + tmp &= ~P2C_RG_SESSEND; + writel(tmp, port_base + U3P_U2PHYDTM1); + + /* USB 2.0 slew rate calibration */ + tmp = readl(port_base + U3P_USBPHYACR5); + tmp &= ~PA5_RG_U2_HSTX_SRCTRL; + tmp |= PA5_RG_U2_HSTX_SRCTRL_VAL(4); + writel(tmp, port_base + U3P_USBPHYACR5); + + if (index) { + tmp = readl(port_base + U3D_U2PHYDCR0); + tmp |= P2C_RG_SIF_U2PLL_FORCE_ON; + writel(tmp, port_base + U3D_U2PHYDCR0); + + tmp = readl(port_base + U3P_U2PHYDTM0); + tmp |= P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM; + writel(tmp, port_base + U3P_U2PHYDTM0); + } + dev_dbg(u3phy->dev, "%s(%d)\n", __func__, index); +} + +static void phy_instance_power_off(struct mt65xx_u3phy *u3phy, + struct mt65xx_phy_instance *instance) +{ + void __iomem *port_base = instance->port_base; + u32 index = instance->index; + u32 tmp; + + tmp = readl(port_base + U3P_U2PHYDTM0); + tmp &= ~(P2C_RG_XCVRSEL | P2C_RG_DATAIN); + tmp |= P2C_FORCE_SUSPENDM; + writel(tmp, port_base + U3P_U2PHYDTM0); + + /* OTG Disable */ + tmp = readl(port_base + U3P_USBPHYACR6); + tmp &= ~PA6_RG_U2_OTG_VBUSCMP_EN; + writel(tmp, port_base + U3P_USBPHYACR6); + + if (!index) { + /* (also disable)Change 100uA current switch to USB2.0 */ + tmp = readl(port_base + U3P_USBPHYACR5); + tmp &= ~PA5_RG_U2_HS_100U_U3_EN; + writel(tmp, port_base + U3P_USBPHYACR5); + } + + /* let suspendm=0, set utmi into analog power down */ + tmp = readl(port_base + U3P_U2PHYDTM0); + tmp &= ~P2C_RG_SUSPENDM; + writel(tmp, port_base + U3P_U2PHYDTM0); + udelay(1); + + tmp = readl(port_base + U3P_U2PHYDTM1); + tmp &= ~(P2C_RG_VBUSVALID | P2C_RG_AVALID); + tmp |= P2C_RG_SESSEND; + writel(tmp, port_base + U3P_U2PHYDTM1); + + if (!index) { + tmp = readl(port_base + U3P_U3_PHYA_REG0); + tmp &= ~P3A_RG_U3_VUSB10_ON; + writel(tmp, port_base + U3P_U3_PHYA_REG0); + } else { + tmp = readl(port_base + U3D_U2PHYDCR0); + tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON; + writel(tmp, port_base + U3D_U2PHYDCR0); + } + + dev_dbg(u3phy->dev, "%s(%d)\n", __func__, index); +} + +static void phy_instance_exit(struct mt65xx_u3phy *u3phy, + struct mt65xx_phy_instance *instance) +{ + void __iomem *port_base = instance->port_base; + u32 index = instance->index; + u32 tmp; + + if (index) { + tmp = readl(port_base + U3D_U2PHYDCR0); + tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON; + writel(tmp, port_base + U3D_U2PHYDCR0); + + tmp = readl(port_base + U3P_U2PHYDTM0); + tmp &= ~P2C_FORCE_SUSPENDM; + writel(tmp, port_base + U3P_U2PHYDTM0); + } +} + +static int mt65xx_phy_init(struct phy *phy) +{ + struct mt65xx_phy_instance *instance = phy_get_drvdata(phy); + struct mt65xx_u3phy *u3phy = dev_get_drvdata(phy->dev.parent); + int ret; + + ret = clk_prepare_enable(u3phy->u3phya_ref); + if (ret) { + dev_err(u3phy->dev, "failed to enable u3phya_ref\n"); + return ret; + } + + phy_instance_init(u3phy, instance); + return 0; +} + +static int mt65xx_phy_power_on(struct phy *phy) +{ + struct mt65xx_phy_instance *instance = phy_get_drvdata(phy); + struct mt65xx_u3phy *u3phy = dev_get_drvdata(phy->dev.parent); + + phy_instance_power_on(u3phy, instance); + return 0; +} + +static int mt65xx_phy_power_off(struct phy *phy) +{ + struct mt65xx_phy_instance *instance = phy_get_drvdata(phy); + struct mt65xx_u3phy *u3phy = dev_get_drvdata(phy->dev.parent); + + phy_instance_power_off(u3phy, instance); + return 0; +} + +static int mt65xx_phy_exit(struct phy *phy) +{ + struct mt65xx_phy_instance *instance = phy_get_drvdata(phy); + struct mt65xx_u3phy *u3phy = dev_get_drvdata(phy->dev.parent); + + phy_instance_exit(u3phy, instance); + clk_disable_unprepare(u3phy->u3phya_ref); + return 0; +} + +static struct phy *mt65xx_phy_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct mt65xx_u3phy *u3phy = dev_get_drvdata(dev); + struct mt65xx_phy_instance *instance = NULL; + struct device_node *phy_np = args->np; + int index; + + + if (args->args_count != 1) { + dev_err(dev, "invalid number of cells in 'phy' property\n"); + return ERR_PTR(-EINVAL); + } + + for (index = 0; index < u3phy->nphys; index++) + if (phy_np == u3phy->phys[index]->phy->dev.of_node) { + instance = u3phy->phys[index]; + break; + } + + if (!instance) { + dev_err(dev, "failed to find appropriate phy\n"); + return ERR_PTR(-EINVAL); + } + + instance->type = args->args[0]; + + if (!(instance->type == PHY_TYPE_USB2 || + instance->type == PHY_TYPE_USB3)) { + dev_err(dev, "unsupported device type: %d\n", instance->type); + return ERR_PTR(-EINVAL); + } + + return instance->phy; +} + +static struct phy_ops mt65xx_u3phy_ops = { + .init = mt65xx_phy_init, + .exit = mt65xx_phy_exit, + .power_on = mt65xx_phy_power_on, + .power_off = mt65xx_phy_power_off, + .owner = THIS_MODULE, +}; + +static int mt65xx_u3phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *child_np; + struct phy_provider *provider; + struct resource *sif_res; + struct mt65xx_u3phy *u3phy; + struct resource res; + int port; + + u3phy = devm_kzalloc(dev, sizeof(*u3phy), GFP_KERNEL); + if (!u3phy) + return -ENOMEM; + + u3phy->nphys = of_get_child_count(np); + u3phy->phys = devm_kcalloc(dev, u3phy->nphys, + sizeof(*u3phy->phys), GFP_KERNEL); + if (!u3phy->phys) + return -ENOMEM; + + u3phy->dev = dev; + platform_set_drvdata(pdev, u3phy); + + sif_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + u3phy->sif_base = devm_ioremap_resource(dev, sif_res); + if (IS_ERR(u3phy->sif_base)) { + dev_err(dev, "failed to remap sif regs\n"); + return PTR_ERR(u3phy->sif_base); + } + + u3phy->u3phya_ref = devm_clk_get(dev, "u3phya_ref"); + if (IS_ERR(u3phy->u3phya_ref)) { + dev_err(dev, "error to get u3phya_ref\n"); + return PTR_ERR(u3phy->u3phya_ref); + } + + port = 0; + for_each_child_of_node(np, child_np) { + struct mt65xx_phy_instance *instance; + struct phy *phy; + int retval; + + instance = devm_kzalloc(dev, sizeof(*instance), GFP_KERNEL); + if (!instance) + return -ENOMEM; + + u3phy->phys[port] = instance; + + phy = devm_phy_create(dev, child_np, &mt65xx_u3phy_ops); + if (IS_ERR(phy)) { + dev_err(dev, "failed to create phy\n"); + return PTR_ERR(phy); + } + + retval = of_address_to_resource(child_np, 0, &res); + if (retval) { + dev_err(dev, "failed to get address resource(id-%d)\n", + port); + return retval; + } + + instance->port_base = devm_ioremap_resource(&phy->dev, &res); + if (IS_ERR(instance->port_base)) { + dev_err(dev, "failed to remap phy regs\n"); + return PTR_ERR(instance->port_base); + } + + instance->phy = phy; + instance->index = port; + phy_set_drvdata(phy, instance); + port++; + } + + provider = devm_of_phy_provider_register(dev, mt65xx_phy_xlate); + + return PTR_ERR_OR_ZERO(provider); +} + +static const struct of_device_id mt65xx_u3phy_id_table[] = { + { .compatible = "mediatek,mt8173-u3phy", }, + { }, +}; +MODULE_DEVICE_TABLE(of, mt65xx_u3phy_id_table); + +static struct platform_driver mt65xx_u3phy_driver = { + .probe = mt65xx_u3phy_probe, + .driver = { + .name = "mt65xx-u3phy", + .of_match_table = mt65xx_u3phy_id_table, + }, +}; + +module_platform_driver(mt65xx_u3phy_driver); + +MODULE_AUTHOR("Chunfeng Yun <chunfeng.yun@mediatek.com>"); +MODULE_DESCRIPTION("mt65xx USB PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-samsung-usb2.c b/drivers/phy/phy-samsung-usb2.c index f278a9c..1d22d93 100644 --- a/drivers/phy/phy-samsung-usb2.c +++ b/drivers/phy/phy-samsung-usb2.c @@ -27,6 +27,13 @@ static int samsung_usb2_phy_power_on(struct phy *phy) dev_dbg(drv->dev, "Request to power_on \"%s\" usb phy\n", inst->cfg->label); + + if (drv->vbus) { + ret = regulator_enable(drv->vbus); + if (ret) + goto err_regulator; + } + ret = clk_prepare_enable(drv->clk); if (ret) goto err_main_clk; @@ -48,6 +55,9 @@ err_power_on: err_instance_clk: clk_disable_unprepare(drv->clk); err_main_clk: + if (drv->vbus) + regulator_disable(drv->vbus); +err_regulator: return ret; } @@ -55,7 +65,7 @@ static int samsung_usb2_phy_power_off(struct phy *phy) { struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy); struct samsung_usb2_phy_driver *drv = inst->drv; - int ret; + int ret = 0; dev_dbg(drv->dev, "Request to power_off \"%s\" usb phy\n", inst->cfg->label); @@ -68,7 +78,10 @@ static int samsung_usb2_phy_power_off(struct phy *phy) } clk_disable_unprepare(drv->ref_clk); clk_disable_unprepare(drv->clk); - return 0; + if (drv->vbus) + ret = regulator_disable(drv->vbus); + + return ret; } static const struct phy_ops samsung_usb2_phy_ops = { @@ -203,6 +216,14 @@ static int samsung_usb2_phy_probe(struct platform_device *pdev) return ret; } + drv->vbus = devm_regulator_get(dev, "vbus"); + if (IS_ERR(drv->vbus)) { + ret = PTR_ERR(drv->vbus); + if (ret == -EPROBE_DEFER) + return ret; + drv->vbus = NULL; + } + for (i = 0; i < drv->cfg->num_phys; i++) { char *label = drv->cfg->phys[i].label; struct samsung_usb2_phy_instance *p = &drv->instances[i]; diff --git a/drivers/phy/phy-samsung-usb2.h b/drivers/phy/phy-samsung-usb2.h index 44bead9..6563e7c 100644 --- a/drivers/phy/phy-samsung-usb2.h +++ b/drivers/phy/phy-samsung-usb2.h @@ -17,6 +17,7 @@ #include <linux/device.h> #include <linux/regmap.h> #include <linux/spinlock.h> +#include <linux/regulator/consumer.h> #define KHZ 1000 #define MHZ (KHZ * KHZ) @@ -37,6 +38,7 @@ struct samsung_usb2_phy_driver { const struct samsung_usb2_phy_config *cfg; struct clk *clk; struct clk *ref_clk; + struct regulator *vbus; unsigned long ref_rate; u32 ref_reg_val; struct device *dev; diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c index 731b395..b12964b 100644 --- a/drivers/phy/phy-sun4i-usb.c +++ b/drivers/phy/phy-sun4i-usb.c @@ -551,19 +551,15 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) if (IS_ERR(data->base)) return PTR_ERR(data->base); - data->id_det_gpio = devm_gpiod_get(dev, "usb0_id_det", GPIOD_IN); - if (IS_ERR(data->id_det_gpio)) { - if (PTR_ERR(data->id_det_gpio) == -EPROBE_DEFER) - return -EPROBE_DEFER; - data->id_det_gpio = NULL; - } - - data->vbus_det_gpio = devm_gpiod_get(dev, "usb0_vbus_det", GPIOD_IN); - if (IS_ERR(data->vbus_det_gpio)) { - if (PTR_ERR(data->vbus_det_gpio) == -EPROBE_DEFER) - return -EPROBE_DEFER; - data->vbus_det_gpio = NULL; - } + data->id_det_gpio = devm_gpiod_get_optional(dev, "usb0_id_det", + GPIOD_IN); + if (IS_ERR(data->id_det_gpio)) + return PTR_ERR(data->id_det_gpio); + + data->vbus_det_gpio = devm_gpiod_get_optional(dev, "usb0_vbus_det", + GPIOD_IN); + if (IS_ERR(data->vbus_det_gpio)) + return PTR_ERR(data->vbus_det_gpio); if (of_find_property(np, "usb0_vbus_power-supply", NULL)) { data->vbus_power_supply = devm_power_supply_get_by_phandle(dev, diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index d8926c6..d5c57f1 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -27,7 +27,6 @@ obj-$(CONFIG_USB_R8A66597_HCD) += host/ obj-$(CONFIG_USB_HWA_HCD) += host/ obj-$(CONFIG_USB_IMX21_HCD) += host/ obj-$(CONFIG_USB_FSL_MPH_DR_OF) += host/ -obj-$(CONFIG_USB_FUSBH200_HCD) += host/ obj-$(CONFIG_USB_FOTG210_HCD) += host/ obj-$(CONFIG_USB_MAX3421_HCD) += host/ diff --git a/drivers/usb/chipidea/Kconfig b/drivers/usb/chipidea/Kconfig index 5ce3f1d..5619b8c 100644 --- a/drivers/usb/chipidea/Kconfig +++ b/drivers/usb/chipidea/Kconfig @@ -1,6 +1,7 @@ config USB_CHIPIDEA tristate "ChipIdea Highspeed Dual Role Controller" depends on ((USB_EHCI_HCD && USB_GADGET) || (USB_EHCI_HCD && !USB_GADGET) || (!USB_EHCI_HCD && USB_GADGET)) && HAS_DMA + select EXTCON help Say Y here if your system has a dual role high speed USB controller based on ChipIdea silicon IP. Currently, only the diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index dcc50c87..6ccbf60 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -56,12 +56,23 @@ static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data = { CI_HDRC_DISABLE_HOST_STREAMING, }; +static const struct ci_hdrc_imx_platform_flag imx6ul_usb_data = { + .flags = CI_HDRC_SUPPORTS_RUNTIME_PM | + CI_HDRC_TURN_VBUS_EARLY_ON, +}; + +static const struct ci_hdrc_imx_platform_flag imx7d_usb_data = { + .flags = CI_HDRC_SUPPORTS_RUNTIME_PM, +}; + static const struct of_device_id ci_hdrc_imx_dt_ids[] = { { .compatible = "fsl,imx28-usb", .data = &imx28_usb_data}, { .compatible = "fsl,imx27-usb", .data = &imx27_usb_data}, { .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data}, { .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data}, { .compatible = "fsl,imx6sx-usb", .data = &imx6sx_usb_data}, + { .compatible = "fsl,imx6ul-usb", .data = &imx6ul_usb_data}, + { .compatible = "fsl,imx7d-usb", .data = &imx7d_usb_data}, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids); diff --git a/drivers/usb/chipidea/ci_hdrc_pci.c b/drivers/usb/chipidea/ci_hdrc_pci.c index 773d150..b59195e 100644 --- a/drivers/usb/chipidea/ci_hdrc_pci.c +++ b/drivers/usb/chipidea/ci_hdrc_pci.c @@ -142,16 +142,16 @@ static const struct pci_device_id ci_hdrc_pci_id_table[] = { .driver_data = (kernel_ulong_t)&pci_platdata, }, { - PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0811), + PCI_VDEVICE(INTEL, 0x0811), .driver_data = (kernel_ulong_t)&langwell_pci_platdata, }, { - PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0829), + PCI_VDEVICE(INTEL, 0x0829), .driver_data = (kernel_ulong_t)&penwell_pci_platdata, }, { /* Intel Clovertrail */ - PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xe006), + PCI_VDEVICE(INTEL, 0xe006), .driver_data = (kernel_ulong_t)&penwell_pci_platdata, }, { 0 } /* end: all zeroes */ diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 3feebf7..965d0e2 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -47,6 +47,7 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/dma-mapping.h> +#include <linux/extcon.h> #include <linux/phy/phy.h> #include <linux/platform_device.h> #include <linux/module.h> @@ -602,16 +603,52 @@ static irqreturn_t ci_irq(int irq, void *data) return ret; } +static int ci_vbus_notifier(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct ci_hdrc_cable *vbus = container_of(nb, struct ci_hdrc_cable, nb); + struct ci_hdrc *ci = vbus->ci; + + if (event) + vbus->state = true; + else + vbus->state = false; + + vbus->changed = true; + + ci_irq(ci->irq, ci); + return NOTIFY_DONE; +} + +static int ci_id_notifier(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct ci_hdrc_cable *id = container_of(nb, struct ci_hdrc_cable, nb); + struct ci_hdrc *ci = id->ci; + + if (event) + id->state = false; + else + id->state = true; + + id->changed = true; + + ci_irq(ci->irq, ci); + return NOTIFY_DONE; +} + static int ci_get_platdata(struct device *dev, struct ci_hdrc_platform_data *platdata) { + struct extcon_dev *ext_vbus, *ext_id; + struct ci_hdrc_cable *cable; int ret; if (!platdata->phy_mode) platdata->phy_mode = of_usb_get_phy_mode(dev->of_node); if (!platdata->dr_mode) - platdata->dr_mode = of_usb_get_dr_mode(dev->of_node); + platdata->dr_mode = usb_get_dr_mode(dev); if (platdata->dr_mode == USB_DR_MODE_UNKNOWN) platdata->dr_mode = USB_DR_MODE_OTG; @@ -648,9 +685,13 @@ static int ci_get_platdata(struct device *dev, return ret; } - if (of_usb_get_maximum_speed(dev->of_node) == USB_SPEED_FULL) + if (usb_get_maximum_speed(dev) == USB_SPEED_FULL) platdata->flags |= CI_HDRC_FORCE_FULLSPEED; + if (of_find_property(dev->of_node, "phy-clkgate-delay-us", NULL)) + of_property_read_u32(dev->of_node, "phy-clkgate-delay-us", + &platdata->phy_clkgate_delay_us); + platdata->itc_setting = 1; if (of_find_property(dev->of_node, "itc-setting", NULL)) { ret = of_property_read_u32(dev->of_node, "itc-setting", @@ -695,9 +736,91 @@ static int ci_get_platdata(struct device *dev, platdata->flags |= CI_HDRC_OVERRIDE_RX_BURST; } + ext_id = ERR_PTR(-ENODEV); + ext_vbus = ERR_PTR(-ENODEV); + if (of_property_read_bool(dev->of_node, "extcon")) { + /* Each one of them is not mandatory */ + ext_vbus = extcon_get_edev_by_phandle(dev, 0); + if (IS_ERR(ext_vbus) && PTR_ERR(ext_vbus) != -ENODEV) + return PTR_ERR(ext_vbus); + + ext_id = extcon_get_edev_by_phandle(dev, 1); + if (IS_ERR(ext_id) && PTR_ERR(ext_id) != -ENODEV) + return PTR_ERR(ext_id); + } + + cable = &platdata->vbus_extcon; + cable->nb.notifier_call = ci_vbus_notifier; + cable->edev = ext_vbus; + + if (!IS_ERR(ext_vbus)) { + ret = extcon_get_cable_state_(cable->edev, EXTCON_USB); + if (ret) + cable->state = true; + else + cable->state = false; + } + + cable = &platdata->id_extcon; + cable->nb.notifier_call = ci_id_notifier; + cable->edev = ext_id; + + if (!IS_ERR(ext_id)) { + ret = extcon_get_cable_state_(cable->edev, EXTCON_USB_HOST); + if (ret) + cable->state = false; + else + cable->state = true; + } + return 0; +} + +static int ci_extcon_register(struct ci_hdrc *ci) +{ + struct ci_hdrc_cable *id, *vbus; + int ret; + + id = &ci->platdata->id_extcon; + id->ci = ci; + if (!IS_ERR(id->edev)) { + ret = extcon_register_notifier(id->edev, EXTCON_USB_HOST, + &id->nb); + if (ret < 0) { + dev_err(ci->dev, "register ID failed\n"); + return ret; + } + } + + vbus = &ci->platdata->vbus_extcon; + vbus->ci = ci; + if (!IS_ERR(vbus->edev)) { + ret = extcon_register_notifier(vbus->edev, EXTCON_USB, + &vbus->nb); + if (ret < 0) { + extcon_unregister_notifier(id->edev, EXTCON_USB_HOST, + &id->nb); + dev_err(ci->dev, "register VBUS failed\n"); + return ret; + } + } + return 0; } +static void ci_extcon_unregister(struct ci_hdrc *ci) +{ + struct ci_hdrc_cable *cable; + + cable = &ci->platdata->id_extcon; + if (!IS_ERR(cable->edev)) + extcon_unregister_notifier(cable->edev, EXTCON_USB_HOST, + &cable->nb); + + cable = &ci->platdata->vbus_extcon; + if (!IS_ERR(cable->edev)) + extcon_unregister_notifier(cable->edev, EXTCON_USB, &cable->nb); +} + static DEFINE_IDA(ci_ida); struct platform_device *ci_hdrc_add_device(struct device *dev, @@ -921,6 +1044,10 @@ static int ci_hdrc_probe(struct platform_device *pdev) if (ret) goto stop; + ret = ci_extcon_register(ci); + if (ret) + goto stop; + if (ci->supports_runtime_pm) { pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); @@ -938,6 +1065,7 @@ static int ci_hdrc_probe(struct platform_device *pdev) if (!ret) return 0; + ci_extcon_unregister(ci); stop: ci_role_destroy(ci); deinit_phy: @@ -957,6 +1085,7 @@ static int ci_hdrc_remove(struct platform_device *pdev) } dbg_remove_files(ci); + ci_extcon_unregister(ci); ci_role_destroy(ci); ci_hdrc_enter_lpm(ci, true); ci_usb_phy_exit(ci); @@ -996,6 +1125,9 @@ static void ci_controller_suspend(struct ci_hdrc *ci) { disable_irq(ci->irq); ci_hdrc_enter_lpm(ci, true); + if (ci->platdata->phy_clkgate_delay_us) + usleep_range(ci->platdata->phy_clkgate_delay_us, + ci->platdata->phy_clkgate_delay_us + 50); usb_phy_set_suspend(ci->usb_phy, 1); ci->in_lpm = true; enable_irq(ci->irq); diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c index ad6c87a..45f86da 100644 --- a/drivers/usb/chipidea/otg.c +++ b/drivers/usb/chipidea/otg.c @@ -30,7 +30,44 @@ */ u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask) { - return hw_read(ci, OP_OTGSC, mask); + struct ci_hdrc_cable *cable; + u32 val = hw_read(ci, OP_OTGSC, mask); + + /* + * If using extcon framework for VBUS and/or ID signal + * detection overwrite OTGSC register value + */ + cable = &ci->platdata->vbus_extcon; + if (!IS_ERR(cable->edev)) { + if (cable->changed) + val |= OTGSC_BSVIS; + else + val &= ~OTGSC_BSVIS; + + cable->changed = false; + + if (cable->state) + val |= OTGSC_BSV; + else + val &= ~OTGSC_BSV; + } + + cable = &ci->platdata->id_extcon; + if (!IS_ERR(cable->edev)) { + if (cable->changed) + val |= OTGSC_IDIS; + else + val &= ~OTGSC_IDIS; + + cable->changed = false; + + if (cable->state) + val |= OTGSC_ID; + else + val &= ~OTGSC_ID; + } + + return val; } /** @@ -77,9 +114,12 @@ static void ci_handle_id_switch(struct ci_hdrc *ci) ci_role(ci)->name, ci->roles[role]->name); ci_role_stop(ci); - /* wait vbus lower than OTGSC_BSV */ - hw_wait_reg(ci, OP_OTGSC, OTGSC_BSV, 0, - CI_VBUS_STABLE_TIMEOUT_MS); + + if (role == CI_ROLE_GADGET) + /* wait vbus lower than OTGSC_BSV */ + hw_wait_reg(ci, OP_OTGSC, OTGSC_BSV, 0, + CI_VBUS_STABLE_TIMEOUT_MS); + ci_role_start(ci, role); } } diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index 5ddab30..fcea4eb 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -72,6 +72,14 @@ #define VF610_OVER_CUR_DIS BIT(7) +#define MX7D_USBNC_USB_CTRL2 0x4 +#define MX7D_USB_VBUS_WAKEUP_SOURCE_MASK 0x3 +#define MX7D_USB_VBUS_WAKEUP_SOURCE(v) (v << 0) +#define MX7D_USB_VBUS_WAKEUP_SOURCE_VBUS MX7D_USB_VBUS_WAKEUP_SOURCE(0) +#define MX7D_USB_VBUS_WAKEUP_SOURCE_AVALID MX7D_USB_VBUS_WAKEUP_SOURCE(1) +#define MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID MX7D_USB_VBUS_WAKEUP_SOURCE(2) +#define MX7D_USB_VBUS_WAKEUP_SOURCE_SESS_END MX7D_USB_VBUS_WAKEUP_SOURCE(3) + struct usbmisc_ops { /* It's called once when probe a usb device */ int (*init)(struct imx_usbmisc_data *data); @@ -324,6 +332,55 @@ static int usbmisc_vf610_init(struct imx_usbmisc_data *data) return 0; } +static int usbmisc_imx7d_set_wakeup + (struct imx_usbmisc_data *data, bool enabled) +{ + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + unsigned long flags; + u32 val; + u32 wakeup_setting = (MX6_BM_WAKEUP_ENABLE | + MX6_BM_VBUS_WAKEUP | MX6_BM_ID_WAKEUP); + + spin_lock_irqsave(&usbmisc->lock, flags); + val = readl(usbmisc->base); + if (enabled) { + writel(val | wakeup_setting, usbmisc->base); + } else { + if (val & MX6_BM_WAKEUP_INTR) + dev_dbg(data->dev, "wakeup int\n"); + writel(val & ~wakeup_setting, usbmisc->base); + } + spin_unlock_irqrestore(&usbmisc->lock, flags); + + return 0; +} + +static int usbmisc_imx7d_init(struct imx_usbmisc_data *data) +{ + struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev); + unsigned long flags; + u32 reg; + + if (data->index >= 1) + return -EINVAL; + + spin_lock_irqsave(&usbmisc->lock, flags); + if (data->disable_oc) { + reg = readl(usbmisc->base); + writel(reg | MX6_BM_OVER_CUR_DIS, usbmisc->base); + } + + reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2); + reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK; + writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID, + usbmisc->base + MX7D_USBNC_USB_CTRL2); + spin_unlock_irqrestore(&usbmisc->lock, flags); + + usbmisc_imx7d_set_wakeup(data, false); + + return 0; +} + static const struct usbmisc_ops imx25_usbmisc_ops = { .init = usbmisc_imx25_init, .post = usbmisc_imx25_post, @@ -351,6 +408,11 @@ static const struct usbmisc_ops imx6sx_usbmisc_ops = { .init = usbmisc_imx6sx_init, }; +static const struct usbmisc_ops imx7d_usbmisc_ops = { + .init = usbmisc_imx7d_init, + .set_wakeup = usbmisc_imx7d_set_wakeup, +}; + int imx_usbmisc_init(struct imx_usbmisc_data *data) { struct imx_usbmisc *usbmisc; @@ -426,6 +488,10 @@ static const struct of_device_id usbmisc_imx_dt_ids[] = { .compatible = "fsl,imx6sx-usbmisc", .data = &imx6sx_usbmisc_ops, }, + { + .compatible = "fsl,imx6ul-usbmisc", + .data = &imx6sx_usbmisc_ops, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids); diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c index 9e39286..673d530 100644 --- a/drivers/usb/common/common.c +++ b/drivers/usb/common/common.c @@ -60,6 +60,24 @@ const char *usb_speed_string(enum usb_device_speed speed) } EXPORT_SYMBOL_GPL(usb_speed_string); +enum usb_device_speed usb_get_maximum_speed(struct device *dev) +{ + const char *maximum_speed; + int err; + int i; + + err = device_property_read_string(dev, "maximum-speed", &maximum_speed); + if (err < 0) + return USB_SPEED_UNKNOWN; + + for (i = 0; i < ARRAY_SIZE(speed_names); i++) + if (strcmp(maximum_speed, speed_names[i]) == 0) + return i; + + return USB_SPEED_UNKNOWN; +} +EXPORT_SYMBOL_GPL(usb_get_maximum_speed); + const char *usb_state_string(enum usb_device_state state) { static const char *const names[] = { @@ -81,7 +99,6 @@ const char *usb_state_string(enum usb_device_state state) } EXPORT_SYMBOL_GPL(usb_state_string); -#ifdef CONFIG_OF static const char *const usb_dr_modes[] = { [USB_DR_MODE_UNKNOWN] = "", [USB_DR_MODE_HOST] = "host", @@ -89,19 +106,12 @@ static const char *const usb_dr_modes[] = { [USB_DR_MODE_OTG] = "otg", }; -/** - * of_usb_get_dr_mode - Get dual role mode for given device_node - * @np: Pointer to the given device_node - * - * The function gets phy interface string from property 'dr_mode', - * and returns the correspondig enum usb_dr_mode - */ -enum usb_dr_mode of_usb_get_dr_mode(struct device_node *np) +enum usb_dr_mode usb_get_dr_mode(struct device *dev) { const char *dr_mode; int err, i; - err = of_property_read_string(np, "dr_mode", &dr_mode); + err = device_property_read_string(dev, "dr_mode", &dr_mode); if (err < 0) return USB_DR_MODE_UNKNOWN; @@ -111,34 +121,9 @@ enum usb_dr_mode of_usb_get_dr_mode(struct device_node *np) return USB_DR_MODE_UNKNOWN; } -EXPORT_SYMBOL_GPL(of_usb_get_dr_mode); - -/** - * of_usb_get_maximum_speed - Get maximum requested speed for a given USB - * controller. - * @np: Pointer to the given device_node - * - * The function gets the maximum speed string from property "maximum-speed", - * and returns the corresponding enum usb_device_speed. - */ -enum usb_device_speed of_usb_get_maximum_speed(struct device_node *np) -{ - const char *maximum_speed; - int err; - int i; - - err = of_property_read_string(np, "maximum-speed", &maximum_speed); - if (err < 0) - return USB_SPEED_UNKNOWN; - - for (i = 0; i < ARRAY_SIZE(speed_names); i++) - if (strcmp(maximum_speed, speed_names[i]) == 0) - return i; - - return USB_SPEED_UNKNOWN; -} -EXPORT_SYMBOL_GPL(of_usb_get_maximum_speed); +EXPORT_SYMBOL_GPL(usb_get_dr_mode); +#ifdef CONFIG_OF /** * of_usb_host_tpl_support - to get if Targeted Peripheral List is supported * for given targeted hosts (non-PC hosts) diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index b9ddf0c..7caff02 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -853,6 +853,10 @@ int usb_get_bos_descriptor(struct usb_device *dev) dev->bos->ss_cap = (struct usb_ss_cap_descriptor *)buffer; break; + case USB_SSP_CAP_TYPE: + dev->bos->ssp_cap = + (struct usb_ssp_cap_descriptor *)buffer; + break; case CONTAINER_ID_TYPE: dev->bos->ss_id = (struct usb_ss_container_id_descriptor *)buffer; diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 6b5063e..56593a9 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -296,6 +296,10 @@ static int usb_probe_interface(struct device *dev) if (udev->authorized == 0) { dev_err(&intf->dev, "Device is not authorized for usage\n"); return error; + } else if (intf->authorized == 0) { + dev_err(&intf->dev, "Interface %d is not authorized for usage\n", + intf->altsetting->desc.bInterfaceNumber); + return error; } id = usb_match_dynamic_id(intf, driver); @@ -417,12 +421,10 @@ static int usb_unbind_interface(struct device *dev) if (ep->streams == 0) continue; if (j == 0) { - eps = kmalloc(USB_MAXENDPOINTS * sizeof(void *), + eps = kmalloc_array(USB_MAXENDPOINTS, sizeof(void *), GFP_KERNEL); - if (!eps) { - dev_warn(dev, "oom, leaking streams\n"); + if (!eps) break; - } } eps[j++] = ep; } @@ -508,6 +510,10 @@ int usb_driver_claim_interface(struct usb_driver *driver, if (dev->driver) return -EBUSY; + /* reject claim if interface is not authorized */ + if (!iface->authorized) + return -ENODEV; + udev = interface_to_usbdev(iface); dev->driver = &driver->drvwrap.driver; diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 4d64e5c4..1c102d6 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -131,7 +131,7 @@ static inline int is_root_hub(struct usb_device *udev) /* usb 3.0 root hub device descriptor */ static const u8 usb3_rh_dev_descriptor[18] = { 0x12, /* __u8 bLength; */ - 0x01, /* __u8 bDescriptorType; Device */ + USB_DT_DEVICE, /* __u8 bDescriptorType; Device */ 0x00, 0x03, /* __le16 bcdUSB; v3.0 */ 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ @@ -152,7 +152,7 @@ static const u8 usb3_rh_dev_descriptor[18] = { /* usb 2.5 (wireless USB 1.0) root hub device descriptor */ static const u8 usb25_rh_dev_descriptor[18] = { 0x12, /* __u8 bLength; */ - 0x01, /* __u8 bDescriptorType; Device */ + USB_DT_DEVICE, /* __u8 bDescriptorType; Device */ 0x50, 0x02, /* __le16 bcdUSB; v2.5 */ 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ @@ -173,7 +173,7 @@ static const u8 usb25_rh_dev_descriptor[18] = { /* usb 2.0 root hub device descriptor */ static const u8 usb2_rh_dev_descriptor[18] = { 0x12, /* __u8 bLength; */ - 0x01, /* __u8 bDescriptorType; Device */ + USB_DT_DEVICE, /* __u8 bDescriptorType; Device */ 0x00, 0x02, /* __le16 bcdUSB; v2.0 */ 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ @@ -196,7 +196,7 @@ static const u8 usb2_rh_dev_descriptor[18] = { /* usb 1.1 root hub device descriptor */ static const u8 usb11_rh_dev_descriptor[18] = { 0x12, /* __u8 bLength; */ - 0x01, /* __u8 bDescriptorType; Device */ + USB_DT_DEVICE, /* __u8 bDescriptorType; Device */ 0x10, 0x01, /* __le16 bcdUSB; v1.1 */ 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ @@ -223,7 +223,7 @@ static const u8 fs_rh_config_descriptor[] = { /* one configuration */ 0x09, /* __u8 bLength; */ - 0x02, /* __u8 bDescriptorType; Configuration */ + USB_DT_CONFIG, /* __u8 bDescriptorType; Configuration */ 0x19, 0x00, /* __le16 wTotalLength; */ 0x01, /* __u8 bNumInterfaces; (1) */ 0x01, /* __u8 bConfigurationValue; */ @@ -248,7 +248,7 @@ static const u8 fs_rh_config_descriptor[] = { /* one interface */ 0x09, /* __u8 if_bLength; */ - 0x04, /* __u8 if_bDescriptorType; Interface */ + USB_DT_INTERFACE, /* __u8 if_bDescriptorType; Interface */ 0x00, /* __u8 if_bInterfaceNumber; */ 0x00, /* __u8 if_bAlternateSetting; */ 0x01, /* __u8 if_bNumEndpoints; */ @@ -259,7 +259,7 @@ static const u8 fs_rh_config_descriptor[] = { /* one endpoint (status change endpoint) */ 0x07, /* __u8 ep_bLength; */ - 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + USB_DT_ENDPOINT, /* __u8 ep_bDescriptorType; Endpoint */ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ 0x03, /* __u8 ep_bmAttributes; Interrupt */ 0x02, 0x00, /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */ @@ -270,7 +270,7 @@ static const u8 hs_rh_config_descriptor[] = { /* one configuration */ 0x09, /* __u8 bLength; */ - 0x02, /* __u8 bDescriptorType; Configuration */ + USB_DT_CONFIG, /* __u8 bDescriptorType; Configuration */ 0x19, 0x00, /* __le16 wTotalLength; */ 0x01, /* __u8 bNumInterfaces; (1) */ 0x01, /* __u8 bConfigurationValue; */ @@ -295,7 +295,7 @@ static const u8 hs_rh_config_descriptor[] = { /* one interface */ 0x09, /* __u8 if_bLength; */ - 0x04, /* __u8 if_bDescriptorType; Interface */ + USB_DT_INTERFACE, /* __u8 if_bDescriptorType; Interface */ 0x00, /* __u8 if_bInterfaceNumber; */ 0x00, /* __u8 if_bAlternateSetting; */ 0x01, /* __u8 if_bNumEndpoints; */ @@ -306,7 +306,7 @@ static const u8 hs_rh_config_descriptor[] = { /* one endpoint (status change endpoint) */ 0x07, /* __u8 ep_bLength; */ - 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + USB_DT_ENDPOINT, /* __u8 ep_bDescriptorType; Endpoint */ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ 0x03, /* __u8 ep_bmAttributes; Interrupt */ /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) @@ -318,7 +318,7 @@ static const u8 hs_rh_config_descriptor[] = { static const u8 ss_rh_config_descriptor[] = { /* one configuration */ 0x09, /* __u8 bLength; */ - 0x02, /* __u8 bDescriptorType; Configuration */ + USB_DT_CONFIG, /* __u8 bDescriptorType; Configuration */ 0x1f, 0x00, /* __le16 wTotalLength; */ 0x01, /* __u8 bNumInterfaces; (1) */ 0x01, /* __u8 bConfigurationValue; */ @@ -332,7 +332,7 @@ static const u8 ss_rh_config_descriptor[] = { /* one interface */ 0x09, /* __u8 if_bLength; */ - 0x04, /* __u8 if_bDescriptorType; Interface */ + USB_DT_INTERFACE, /* __u8 if_bDescriptorType; Interface */ 0x00, /* __u8 if_bInterfaceNumber; */ 0x00, /* __u8 if_bAlternateSetting; */ 0x01, /* __u8 if_bNumEndpoints; */ @@ -343,7 +343,7 @@ static const u8 ss_rh_config_descriptor[] = { /* one endpoint (status change endpoint) */ 0x07, /* __u8 ep_bLength; */ - 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + USB_DT_ENDPOINT, /* __u8 ep_bDescriptorType; Endpoint */ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ 0x03, /* __u8 ep_bmAttributes; Interrupt */ /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) @@ -353,7 +353,8 @@ static const u8 ss_rh_config_descriptor[] = { /* one SuperSpeed endpoint companion descriptor */ 0x06, /* __u8 ss_bLength */ - 0x30, /* __u8 ss_bDescriptorType; SuperSpeed EP Companion */ + USB_DT_SS_ENDPOINT_COMP, /* __u8 ss_bDescriptorType; SuperSpeed EP */ + /* Companion */ 0x00, /* __u8 ss_bMaxBurst; allows 1 TX between ACKs */ 0x00, /* __u8 ss_bmAttributes; 1 packet per service interval */ 0x02, 0x00 /* __le16 ss_wBytesPerInterval; 15 bits for max 15 ports */ @@ -555,6 +556,7 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) switch (wValue & 0xff00) { case USB_DT_DEVICE << 8: switch (hcd->speed) { + case HCD_USB31: case HCD_USB3: bufp = usb3_rh_dev_descriptor; break; @@ -576,6 +578,7 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) break; case USB_DT_CONFIG << 8: switch (hcd->speed) { + case HCD_USB31: case HCD_USB3: bufp = ss_rh_config_descriptor; len = sizeof ss_rh_config_descriptor; @@ -854,10 +857,10 @@ static ssize_t authorized_default_show(struct device *dev, { struct usb_device *rh_usb_dev = to_usb_device(dev); struct usb_bus *usb_bus = rh_usb_dev->bus; - struct usb_hcd *usb_hcd; + struct usb_hcd *hcd; - usb_hcd = bus_to_hcd(usb_bus); - return snprintf(buf, PAGE_SIZE, "%u\n", usb_hcd->authorized_default); + hcd = bus_to_hcd(usb_bus); + return snprintf(buf, PAGE_SIZE, "%u\n", !!HCD_DEV_AUTHORIZED(hcd)); } static ssize_t authorized_default_store(struct device *dev, @@ -868,12 +871,16 @@ static ssize_t authorized_default_store(struct device *dev, unsigned val; struct usb_device *rh_usb_dev = to_usb_device(dev); struct usb_bus *usb_bus = rh_usb_dev->bus; - struct usb_hcd *usb_hcd; + struct usb_hcd *hcd; - usb_hcd = bus_to_hcd(usb_bus); + hcd = bus_to_hcd(usb_bus); result = sscanf(buf, "%u\n", &val); if (result == 1) { - usb_hcd->authorized_default = val ? 1 : 0; + if (val) + set_bit(HCD_FLAG_DEV_AUTHORIZED, &hcd->flags); + else + clear_bit(HCD_FLAG_DEV_AUTHORIZED, &hcd->flags); + result = size; } else { result = -EINVAL; @@ -882,9 +889,53 @@ static ssize_t authorized_default_store(struct device *dev, } static DEVICE_ATTR_RW(authorized_default); +/* + * interface_authorized_default_show - show default authorization status + * for USB interfaces + * + * note: interface_authorized_default is the default value + * for initializing the authorized attribute of interfaces + */ +static ssize_t interface_authorized_default_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_device *usb_dev = to_usb_device(dev); + struct usb_hcd *hcd = bus_to_hcd(usb_dev->bus); + + return sprintf(buf, "%u\n", !!HCD_INTF_AUTHORIZED(hcd)); +} + +/* + * interface_authorized_default_store - store default authorization status + * for USB interfaces + * + * note: interface_authorized_default is the default value + * for initializing the authorized attribute of interfaces + */ +static ssize_t interface_authorized_default_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_device *usb_dev = to_usb_device(dev); + struct usb_hcd *hcd = bus_to_hcd(usb_dev->bus); + int rc = count; + bool val; + + if (strtobool(buf, &val) != 0) + return -EINVAL; + + if (val) + set_bit(HCD_FLAG_INTF_AUTHORIZED, &hcd->flags); + else + clear_bit(HCD_FLAG_INTF_AUTHORIZED, &hcd->flags); + + return rc; +} +static DEVICE_ATTR_RW(interface_authorized_default); + /* Group all the USB bus attributes */ static struct attribute *usb_bus_attrs[] = { &dev_attr_authorized_default.attr, + &dev_attr_interface_authorized_default.attr, NULL, }; @@ -2676,12 +2727,22 @@ int usb_add_hcd(struct usb_hcd *hcd, dev_info(hcd->self.controller, "%s\n", hcd->product_desc); /* Keep old behaviour if authorized_default is not in [0, 1]. */ - if (authorized_default < 0 || authorized_default > 1) - hcd->authorized_default = hcd->wireless ? 0 : 1; - else - hcd->authorized_default = authorized_default; + if (authorized_default < 0 || authorized_default > 1) { + if (hcd->wireless) + clear_bit(HCD_FLAG_DEV_AUTHORIZED, &hcd->flags); + else + set_bit(HCD_FLAG_DEV_AUTHORIZED, &hcd->flags); + } else { + if (authorized_default) + set_bit(HCD_FLAG_DEV_AUTHORIZED, &hcd->flags); + else + clear_bit(HCD_FLAG_DEV_AUTHORIZED, &hcd->flags); + } set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + /* per default all interfaces are authorized */ + set_bit(HCD_FLAG_INTF_AUTHORIZED, &hcd->flags); + /* HC is in reset state, but accessible. Now do the one-time init, * bottom up so that hcds can customize the root hubs before hub_wq * starts talking to them. (Note, bus id is assigned early too.) @@ -2717,6 +2778,7 @@ int usb_add_hcd(struct usb_hcd *hcd, rhdev->speed = USB_SPEED_WIRELESS; break; case HCD_USB3: + case HCD_USB31: rhdev->speed = USB_SPEED_SUPER; break; default: diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 431839b..bdeadc1 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1070,7 +1070,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) * for HUB_POST_RESET, but it's easier not to. */ if (type == HUB_INIT) { - unsigned delay = hub_power_on_good_delay(hub); + delay = hub_power_on_good_delay(hub); hub_power_on(hub, false); INIT_DELAYED_WORK(&hub->init_work, hub_init_func2); @@ -1404,7 +1404,6 @@ static int hub_configure(struct usb_hub *hub, /* FIXME for USB 3.0, skip for now */ if ((wHubCharacteristics & HUB_CHAR_COMPOUND) && !(hub_is_superspeed(hdev))) { - int i; char portstr[USB_MAXCHILDREN + 1]; for (i = 0; i < maxchild; i++) @@ -2240,39 +2239,49 @@ static int usb_enumerate_device_otg(struct usb_device *udev) && udev->parent == udev->bus->root_hub) { struct usb_otg_descriptor *desc = NULL; struct usb_bus *bus = udev->bus; + unsigned port1 = udev->portnum; /* descriptor may appear anywhere in config */ - if (__usb_get_extra_descriptor(udev->rawdescriptors[0], - le16_to_cpu(udev->config[0].desc.wTotalLength), - USB_DT_OTG, (void **) &desc) == 0) { - if (desc->bmAttributes & USB_OTG_HNP) { - unsigned port1 = udev->portnum; + err = __usb_get_extra_descriptor(udev->rawdescriptors[0], + le16_to_cpu(udev->config[0].desc.wTotalLength), + USB_DT_OTG, (void **) &desc); + if (err || !(desc->bmAttributes & USB_OTG_HNP)) + return 0; - dev_info(&udev->dev, - "Dual-Role OTG device on %sHNP port\n", - (port1 == bus->otg_port) - ? "" : "non-"); - - /* enable HNP before suspend, it's simpler */ - if (port1 == bus->otg_port) - bus->b_hnp_enable = 1; - err = usb_control_msg(udev, - usb_sndctrlpipe(udev, 0), - USB_REQ_SET_FEATURE, 0, - bus->b_hnp_enable - ? USB_DEVICE_B_HNP_ENABLE - : USB_DEVICE_A_ALT_HNP_SUPPORT, - 0, NULL, 0, USB_CTRL_SET_TIMEOUT); - if (err < 0) { - /* OTG MESSAGE: report errors here, - * customize to match your product. - */ - dev_info(&udev->dev, - "can't set HNP mode: %d\n", - err); - bus->b_hnp_enable = 0; - } + dev_info(&udev->dev, "Dual-Role OTG device on %sHNP port\n", + (port1 == bus->otg_port) ? "" : "non-"); + + /* enable HNP before suspend, it's simpler */ + if (port1 == bus->otg_port) { + bus->b_hnp_enable = 1; + err = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + USB_REQ_SET_FEATURE, 0, + USB_DEVICE_B_HNP_ENABLE, + 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); + if (err < 0) { + /* + * OTG MESSAGE: report errors here, + * customize to match your product. + */ + dev_err(&udev->dev, "can't set HNP mode: %d\n", + err); + bus->b_hnp_enable = 0; } + } else if (desc->bLength == sizeof + (struct usb_otg_descriptor)) { + /* Set a_alt_hnp_support for legacy otg device */ + err = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + USB_REQ_SET_FEATURE, 0, + USB_DEVICE_A_ALT_HNP_SUPPORT, + 0, NULL, 0, + USB_CTRL_SET_TIMEOUT); + if (err < 0) + dev_err(&udev->dev, + "set a_alt_hnp_support failed: %d\n", + err); } } #endif @@ -4222,7 +4231,7 @@ static int hub_enable_device(struct usb_device *udev) * but it is still necessary to lock the port. */ static int -hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, +hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, int retry_counter) { struct usb_device *hdev = hub->hdev; @@ -4526,7 +4535,7 @@ fail: } static void -check_highspeed (struct usb_hub *hub, struct usb_device *udev, int port1) +check_highspeed(struct usb_hub *hub, struct usb_device *udev, int port1) { struct usb_qualifier_descriptor *qual; int status; @@ -4534,11 +4543,11 @@ check_highspeed (struct usb_hub *hub, struct usb_device *udev, int port1) if (udev->quirks & USB_QUIRK_DEVICE_QUALIFIER) return; - qual = kmalloc (sizeof *qual, GFP_KERNEL); + qual = kmalloc(sizeof *qual, GFP_KERNEL); if (qual == NULL) return; - status = usb_get_descriptor (udev, USB_DT_DEVICE_QUALIFIER, 0, + status = usb_get_descriptor(udev, USB_DT_DEVICE_QUALIFIER, 0, qual, sizeof *qual); if (status == sizeof *qual) { dev_info(&udev->dev, "not running at top speed; " @@ -4554,7 +4563,7 @@ check_highspeed (struct usb_hub *hub, struct usb_device *udev, int port1) } static unsigned -hub_power_remaining (struct usb_hub *hub) +hub_power_remaining(struct usb_hub *hub) { struct usb_device *hdev = hub->hdev; int remaining; @@ -4741,7 +4750,7 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus, if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200 && udev->speed == USB_SPEED_FULL && highspeed_hubs != 0) - check_highspeed (hub, udev, port1); + check_highspeed(hub, udev, port1); /* Store the parent's children[] pointer. At this point * udev becomes globally accessible, although presumably @@ -5115,7 +5124,7 @@ static const struct usb_device_id hub_id_table[] = { { } /* Terminating entry */ }; -MODULE_DEVICE_TABLE (usb, hub_id_table); +MODULE_DEVICE_TABLE(usb, hub_id_table); static struct usb_driver hub_driver = { .name = "hub", @@ -5227,7 +5236,7 @@ static int descriptors_changed(struct usb_device *udev, changed = 1; break; } - if (memcmp (buf, udev->rawdescriptors[index], old_length) + if (memcmp(buf, udev->rawdescriptors[index], old_length) != 0) { dev_dbg(&udev->dev, "config index %d changed (#%d)\n", index, diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index f368d20..8e641b5 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1387,8 +1387,6 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) * new altsetting. */ if (manual) { - int i; - for (i = 0; i < alt->desc.bNumEndpoints; i++) { epaddr = alt->endpoint[i].desc.bEndpointAddress; pipe = __create_pipe(dev, @@ -1555,6 +1553,44 @@ static void usb_release_interface(struct device *dev) kfree(intf); } +/* + * usb_deauthorize_interface - deauthorize an USB interface + * + * @intf: USB interface structure + */ +void usb_deauthorize_interface(struct usb_interface *intf) +{ + struct device *dev = &intf->dev; + + device_lock(dev->parent); + + if (intf->authorized) { + device_lock(dev); + intf->authorized = 0; + device_unlock(dev); + + usb_forced_unbind_intf(intf); + } + + device_unlock(dev->parent); +} + +/* + * usb_authorize_interface - authorize an USB interface + * + * @intf: USB interface structure + */ +void usb_authorize_interface(struct usb_interface *intf) +{ + struct device *dev = &intf->dev; + + if (!intf->authorized) { + device_lock(dev); + intf->authorized = 1; /* authorize interface */ + device_unlock(dev); + } +} + static int usb_if_uevent(struct device *dev, struct kobj_uevent_env *env) { struct usb_device *usb_dev; @@ -1807,6 +1843,7 @@ free_interfaces: intfc = cp->intf_cache[i]; intf->altsetting = intfc->altsetting; intf->num_altsetting = intfc->num_altsetting; + intf->authorized = !!HCD_INTF_AUTHORIZED(hcd); kref_get(&intfc->ref); alt = usb_altnum_to_altsetting(intf, 0); diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index cfc68c1..d9ec2de6c 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -957,6 +957,41 @@ static ssize_t supports_autosuspend_show(struct device *dev, } static DEVICE_ATTR_RO(supports_autosuspend); +/* + * interface_authorized_show - show authorization status of an USB interface + * 1 is authorized, 0 is deauthorized + */ +static ssize_t interface_authorized_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + + return sprintf(buf, "%u\n", intf->authorized); +} + +/* + * interface_authorized_store - authorize or deauthorize an USB interface + */ +static ssize_t interface_authorized_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + bool val; + + if (strtobool(buf, &val) != 0) + return -EINVAL; + + if (val) + usb_authorize_interface(intf); + else + usb_deauthorize_interface(intf); + + return count; +} +static struct device_attribute dev_attr_interface_authorized = + __ATTR(authorized, S_IRUGO | S_IWUSR, + interface_authorized_show, interface_authorized_store); + static struct attribute *intf_attrs[] = { &dev_attr_bInterfaceNumber.attr, &dev_attr_bAlternateSetting.attr, @@ -966,6 +1001,7 @@ static struct attribute *intf_attrs[] = { &dev_attr_bInterfaceProtocol.attr, &dev_attr_modalias.attr, &dev_attr_supports_autosuspend.attr, + &dev_attr_interface_authorized.attr, NULL, }; static struct attribute_group intf_attr_grp = { diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index c9e8ee8..3d27477 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -129,9 +129,8 @@ void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor) list_add_tail(&urb->anchor_list, &anchor->urb_list); urb->anchor = anchor; - if (unlikely(anchor->poisoned)) { + if (unlikely(anchor->poisoned)) atomic_inc(&urb->reject); - } spin_unlock_irqrestore(&anchor->lock, flags); } diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 8d5b2f4..f8bbd0b 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -510,7 +510,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent, if (root_hub) /* Root hub always ok [and always wired] */ dev->authorized = 1; else { - dev->authorized = usb_hcd->authorized_default; + dev->authorized = !!HCD_DEV_AUTHORIZED(usb_hcd); dev->wusb = usb_bus_is_wusb(bus) ? 1 : 0; } return dev; diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 457255a..05b5e17 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -27,6 +27,8 @@ extern void usb_release_interface_cache(struct kref *ref); extern void usb_disable_device(struct usb_device *dev, int skip_ep0); extern int usb_deauthorize_device(struct usb_device *); extern int usb_authorize_device(struct usb_device *); +extern void usb_deauthorize_interface(struct usb_interface *); +extern void usb_authorize_interface(struct usb_interface *); extern void usb_detect_quirks(struct usb_device *udev); extern void usb_detect_interface_quirks(struct usb_device *udev); extern int usb_remove_device(struct usb_device *udev); diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index b00fe95..ef73e49 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -73,13 +73,13 @@ static int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg) /* Backup Host regs */ hr = &hsotg->hr_backup; - hr->hcfg = readl(hsotg->regs + HCFG); - hr->haintmsk = readl(hsotg->regs + HAINTMSK); + hr->hcfg = dwc2_readl(hsotg->regs + HCFG); + hr->haintmsk = dwc2_readl(hsotg->regs + HAINTMSK); for (i = 0; i < hsotg->core_params->host_channels; ++i) - hr->hcintmsk[i] = readl(hsotg->regs + HCINTMSK(i)); + hr->hcintmsk[i] = dwc2_readl(hsotg->regs + HCINTMSK(i)); - hr->hprt0 = readl(hsotg->regs + HPRT0); - hr->hfir = readl(hsotg->regs + HFIR); + hr->hprt0 = dwc2_read_hprt0(hsotg); + hr->hfir = dwc2_readl(hsotg->regs + HFIR); hr->valid = true; return 0; @@ -108,14 +108,15 @@ static int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg) } hr->valid = false; - writel(hr->hcfg, hsotg->regs + HCFG); - writel(hr->haintmsk, hsotg->regs + HAINTMSK); + dwc2_writel(hr->hcfg, hsotg->regs + HCFG); + dwc2_writel(hr->haintmsk, hsotg->regs + HAINTMSK); for (i = 0; i < hsotg->core_params->host_channels; ++i) - writel(hr->hcintmsk[i], hsotg->regs + HCINTMSK(i)); + dwc2_writel(hr->hcintmsk[i], hsotg->regs + HCINTMSK(i)); - writel(hr->hprt0, hsotg->regs + HPRT0); - writel(hr->hfir, hsotg->regs + HFIR); + dwc2_writel(hr->hprt0, hsotg->regs + HPRT0); + dwc2_writel(hr->hfir, hsotg->regs + HFIR); + hsotg->frame_number = 0; return 0; } @@ -146,15 +147,15 @@ static int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg) /* Backup dev regs */ dr = &hsotg->dr_backup; - dr->dcfg = readl(hsotg->regs + DCFG); - dr->dctl = readl(hsotg->regs + DCTL); - dr->daintmsk = readl(hsotg->regs + DAINTMSK); - dr->diepmsk = readl(hsotg->regs + DIEPMSK); - dr->doepmsk = readl(hsotg->regs + DOEPMSK); + dr->dcfg = dwc2_readl(hsotg->regs + DCFG); + dr->dctl = dwc2_readl(hsotg->regs + DCTL); + dr->daintmsk = dwc2_readl(hsotg->regs + DAINTMSK); + dr->diepmsk = dwc2_readl(hsotg->regs + DIEPMSK); + dr->doepmsk = dwc2_readl(hsotg->regs + DOEPMSK); for (i = 0; i < hsotg->num_of_eps; i++) { /* Backup IN EPs */ - dr->diepctl[i] = readl(hsotg->regs + DIEPCTL(i)); + dr->diepctl[i] = dwc2_readl(hsotg->regs + DIEPCTL(i)); /* Ensure DATA PID is correctly configured */ if (dr->diepctl[i] & DXEPCTL_DPID) @@ -162,11 +163,11 @@ static int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg) else dr->diepctl[i] |= DXEPCTL_SETD0PID; - dr->dieptsiz[i] = readl(hsotg->regs + DIEPTSIZ(i)); - dr->diepdma[i] = readl(hsotg->regs + DIEPDMA(i)); + dr->dieptsiz[i] = dwc2_readl(hsotg->regs + DIEPTSIZ(i)); + dr->diepdma[i] = dwc2_readl(hsotg->regs + DIEPDMA(i)); /* Backup OUT EPs */ - dr->doepctl[i] = readl(hsotg->regs + DOEPCTL(i)); + dr->doepctl[i] = dwc2_readl(hsotg->regs + DOEPCTL(i)); /* Ensure DATA PID is correctly configured */ if (dr->doepctl[i] & DXEPCTL_DPID) @@ -174,8 +175,8 @@ static int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg) else dr->doepctl[i] |= DXEPCTL_SETD0PID; - dr->doeptsiz[i] = readl(hsotg->regs + DOEPTSIZ(i)); - dr->doepdma[i] = readl(hsotg->regs + DOEPDMA(i)); + dr->doeptsiz[i] = dwc2_readl(hsotg->regs + DOEPTSIZ(i)); + dr->doepdma[i] = dwc2_readl(hsotg->regs + DOEPDMA(i)); } dr->valid = true; return 0; @@ -205,28 +206,28 @@ static int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg) } dr->valid = false; - writel(dr->dcfg, hsotg->regs + DCFG); - writel(dr->dctl, hsotg->regs + DCTL); - writel(dr->daintmsk, hsotg->regs + DAINTMSK); - writel(dr->diepmsk, hsotg->regs + DIEPMSK); - writel(dr->doepmsk, hsotg->regs + DOEPMSK); + dwc2_writel(dr->dcfg, hsotg->regs + DCFG); + dwc2_writel(dr->dctl, hsotg->regs + DCTL); + dwc2_writel(dr->daintmsk, hsotg->regs + DAINTMSK); + dwc2_writel(dr->diepmsk, hsotg->regs + DIEPMSK); + dwc2_writel(dr->doepmsk, hsotg->regs + DOEPMSK); for (i = 0; i < hsotg->num_of_eps; i++) { /* Restore IN EPs */ - writel(dr->diepctl[i], hsotg->regs + DIEPCTL(i)); - writel(dr->dieptsiz[i], hsotg->regs + DIEPTSIZ(i)); - writel(dr->diepdma[i], hsotg->regs + DIEPDMA(i)); + dwc2_writel(dr->diepctl[i], hsotg->regs + DIEPCTL(i)); + dwc2_writel(dr->dieptsiz[i], hsotg->regs + DIEPTSIZ(i)); + dwc2_writel(dr->diepdma[i], hsotg->regs + DIEPDMA(i)); /* Restore OUT EPs */ - writel(dr->doepctl[i], hsotg->regs + DOEPCTL(i)); - writel(dr->doeptsiz[i], hsotg->regs + DOEPTSIZ(i)); - writel(dr->doepdma[i], hsotg->regs + DOEPDMA(i)); + dwc2_writel(dr->doepctl[i], hsotg->regs + DOEPCTL(i)); + dwc2_writel(dr->doeptsiz[i], hsotg->regs + DOEPTSIZ(i)); + dwc2_writel(dr->doepdma[i], hsotg->regs + DOEPDMA(i)); } /* Set the Power-On Programming done bit */ - dctl = readl(hsotg->regs + DCTL); + dctl = dwc2_readl(hsotg->regs + DCTL); dctl |= DCTL_PWRONPRGDONE; - writel(dctl, hsotg->regs + DCTL); + dwc2_writel(dctl, hsotg->regs + DCTL); return 0; } @@ -253,16 +254,16 @@ static int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg) /* Backup global regs */ gr = &hsotg->gr_backup; - gr->gotgctl = readl(hsotg->regs + GOTGCTL); - gr->gintmsk = readl(hsotg->regs + GINTMSK); - gr->gahbcfg = readl(hsotg->regs + GAHBCFG); - gr->gusbcfg = readl(hsotg->regs + GUSBCFG); - gr->grxfsiz = readl(hsotg->regs + GRXFSIZ); - gr->gnptxfsiz = readl(hsotg->regs + GNPTXFSIZ); - gr->hptxfsiz = readl(hsotg->regs + HPTXFSIZ); - gr->gdfifocfg = readl(hsotg->regs + GDFIFOCFG); + gr->gotgctl = dwc2_readl(hsotg->regs + GOTGCTL); + gr->gintmsk = dwc2_readl(hsotg->regs + GINTMSK); + gr->gahbcfg = dwc2_readl(hsotg->regs + GAHBCFG); + gr->gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + gr->grxfsiz = dwc2_readl(hsotg->regs + GRXFSIZ); + gr->gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ); + gr->hptxfsiz = dwc2_readl(hsotg->regs + HPTXFSIZ); + gr->gdfifocfg = dwc2_readl(hsotg->regs + GDFIFOCFG); for (i = 0; i < MAX_EPS_CHANNELS; i++) - gr->dtxfsiz[i] = readl(hsotg->regs + DPTXFSIZN(i)); + gr->dtxfsiz[i] = dwc2_readl(hsotg->regs + DPTXFSIZN(i)); gr->valid = true; return 0; @@ -291,17 +292,17 @@ static int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg) } gr->valid = false; - writel(0xffffffff, hsotg->regs + GINTSTS); - writel(gr->gotgctl, hsotg->regs + GOTGCTL); - writel(gr->gintmsk, hsotg->regs + GINTMSK); - writel(gr->gusbcfg, hsotg->regs + GUSBCFG); - writel(gr->gahbcfg, hsotg->regs + GAHBCFG); - writel(gr->grxfsiz, hsotg->regs + GRXFSIZ); - writel(gr->gnptxfsiz, hsotg->regs + GNPTXFSIZ); - writel(gr->hptxfsiz, hsotg->regs + HPTXFSIZ); - writel(gr->gdfifocfg, hsotg->regs + GDFIFOCFG); + dwc2_writel(0xffffffff, hsotg->regs + GINTSTS); + dwc2_writel(gr->gotgctl, hsotg->regs + GOTGCTL); + dwc2_writel(gr->gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(gr->gusbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(gr->gahbcfg, hsotg->regs + GAHBCFG); + dwc2_writel(gr->grxfsiz, hsotg->regs + GRXFSIZ); + dwc2_writel(gr->gnptxfsiz, hsotg->regs + GNPTXFSIZ); + dwc2_writel(gr->hptxfsiz, hsotg->regs + HPTXFSIZ); + dwc2_writel(gr->gdfifocfg, hsotg->regs + GDFIFOCFG); for (i = 0; i < MAX_EPS_CHANNELS; i++) - writel(gr->dtxfsiz[i], hsotg->regs + DPTXFSIZN(i)); + dwc2_writel(gr->dtxfsiz[i], hsotg->regs + DPTXFSIZN(i)); return 0; } @@ -320,17 +321,17 @@ int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore) if (!hsotg->core_params->hibernation) return -ENOTSUPP; - pcgcctl = readl(hsotg->regs + PCGCTL); + pcgcctl = dwc2_readl(hsotg->regs + PCGCTL); pcgcctl &= ~PCGCTL_STOPPCLK; - writel(pcgcctl, hsotg->regs + PCGCTL); + dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); - pcgcctl = readl(hsotg->regs + PCGCTL); + pcgcctl = dwc2_readl(hsotg->regs + PCGCTL); pcgcctl &= ~PCGCTL_PWRCLMP; - writel(pcgcctl, hsotg->regs + PCGCTL); + dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); - pcgcctl = readl(hsotg->regs + PCGCTL); + pcgcctl = dwc2_readl(hsotg->regs + PCGCTL); pcgcctl &= ~PCGCTL_RSTPDWNMODULE; - writel(pcgcctl, hsotg->regs + PCGCTL); + dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); udelay(100); if (restore) { @@ -397,19 +398,25 @@ int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg) } } + /* + * Clear any pending interrupts since dwc2 will not be able to + * clear them after entering hibernation. + */ + dwc2_writel(0xffffffff, hsotg->regs + GINTSTS); + /* Put the controller in low power state */ - pcgcctl = readl(hsotg->regs + PCGCTL); + pcgcctl = dwc2_readl(hsotg->regs + PCGCTL); pcgcctl |= PCGCTL_PWRCLMP; - writel(pcgcctl, hsotg->regs + PCGCTL); + dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); ndelay(20); pcgcctl |= PCGCTL_RSTPDWNMODULE; - writel(pcgcctl, hsotg->regs + PCGCTL); + dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); ndelay(20); pcgcctl |= PCGCTL_STOPPCLK; - writel(pcgcctl, hsotg->regs + PCGCTL); + dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); return ret; } @@ -425,10 +432,10 @@ static void dwc2_enable_common_interrupts(struct dwc2_hsotg *hsotg) u32 intmsk; /* Clear any pending OTG Interrupts */ - writel(0xffffffff, hsotg->regs + GOTGINT); + dwc2_writel(0xffffffff, hsotg->regs + GOTGINT); /* Clear any pending interrupts */ - writel(0xffffffff, hsotg->regs + GINTSTS); + dwc2_writel(0xffffffff, hsotg->regs + GINTSTS); /* Enable the interrupts in the GINTMSK */ intmsk = GINTSTS_MODEMIS | GINTSTS_OTGINT; @@ -441,7 +448,7 @@ static void dwc2_enable_common_interrupts(struct dwc2_hsotg *hsotg) intmsk |= GINTSTS_WKUPINT | GINTSTS_USBSUSP | GINTSTS_SESSREQINT; - writel(intmsk, hsotg->regs + GINTMSK); + dwc2_writel(intmsk, hsotg->regs + GINTMSK); } /* @@ -464,10 +471,10 @@ static void dwc2_init_fs_ls_pclk_sel(struct dwc2_hsotg *hsotg) } dev_dbg(hsotg->dev, "Initializing HCFG.FSLSPClkSel to %08x\n", val); - hcfg = readl(hsotg->regs + HCFG); + hcfg = dwc2_readl(hsotg->regs + HCFG); hcfg &= ~HCFG_FSLSPCLKSEL_MASK; hcfg |= val << HCFG_FSLSPCLKSEL_SHIFT; - writel(hcfg, hsotg->regs + HCFG); + dwc2_writel(hcfg, hsotg->regs + HCFG); } /* @@ -485,7 +492,7 @@ static int dwc2_core_reset(struct dwc2_hsotg *hsotg) /* Wait for AHB master IDLE state */ do { usleep_range(20000, 40000); - greset = readl(hsotg->regs + GRSTCTL); + greset = dwc2_readl(hsotg->regs + GRSTCTL); if (++count > 50) { dev_warn(hsotg->dev, "%s() HANG! AHB Idle GRSTCTL=%0x\n", @@ -497,10 +504,10 @@ static int dwc2_core_reset(struct dwc2_hsotg *hsotg) /* Core Soft Reset */ count = 0; greset |= GRSTCTL_CSFTRST; - writel(greset, hsotg->regs + GRSTCTL); + dwc2_writel(greset, hsotg->regs + GRSTCTL); do { usleep_range(20000, 40000); - greset = readl(hsotg->regs + GRSTCTL); + greset = dwc2_readl(hsotg->regs + GRSTCTL); if (++count > 50) { dev_warn(hsotg->dev, "%s() HANG! Soft Reset GRSTCTL=%0x\n", @@ -510,20 +517,20 @@ static int dwc2_core_reset(struct dwc2_hsotg *hsotg) } while (greset & GRSTCTL_CSFTRST); if (hsotg->dr_mode == USB_DR_MODE_HOST) { - gusbcfg = readl(hsotg->regs + GUSBCFG); + gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG); gusbcfg &= ~GUSBCFG_FORCEDEVMODE; gusbcfg |= GUSBCFG_FORCEHOSTMODE; - writel(gusbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG); } else if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) { - gusbcfg = readl(hsotg->regs + GUSBCFG); + gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG); gusbcfg &= ~GUSBCFG_FORCEHOSTMODE; gusbcfg |= GUSBCFG_FORCEDEVMODE; - writel(gusbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG); } else if (hsotg->dr_mode == USB_DR_MODE_OTG) { - gusbcfg = readl(hsotg->regs + GUSBCFG); + gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG); gusbcfg &= ~GUSBCFG_FORCEHOSTMODE; gusbcfg &= ~GUSBCFG_FORCEDEVMODE; - writel(gusbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG); } /* @@ -546,9 +553,9 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) */ if (select_phy) { dev_dbg(hsotg->dev, "FS PHY selected\n"); - usbcfg = readl(hsotg->regs + GUSBCFG); + usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); usbcfg |= GUSBCFG_PHYSEL; - writel(usbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); /* Reset after a PHY select */ retval = dwc2_core_reset(hsotg); @@ -571,18 +578,18 @@ static int dwc2_fs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) dev_dbg(hsotg->dev, "FS PHY enabling I2C\n"); /* Program GUSBCFG.OtgUtmiFsSel to I2C */ - usbcfg = readl(hsotg->regs + GUSBCFG); + usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); usbcfg |= GUSBCFG_OTG_UTMI_FS_SEL; - writel(usbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); /* Program GI2CCTL.I2CEn */ - i2cctl = readl(hsotg->regs + GI2CCTL); + i2cctl = dwc2_readl(hsotg->regs + GI2CCTL); i2cctl &= ~GI2CCTL_I2CDEVADDR_MASK; i2cctl |= 1 << GI2CCTL_I2CDEVADDR_SHIFT; i2cctl &= ~GI2CCTL_I2CEN; - writel(i2cctl, hsotg->regs + GI2CCTL); + dwc2_writel(i2cctl, hsotg->regs + GI2CCTL); i2cctl |= GI2CCTL_I2CEN; - writel(i2cctl, hsotg->regs + GI2CCTL); + dwc2_writel(i2cctl, hsotg->regs + GI2CCTL); } return retval; @@ -596,7 +603,7 @@ static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) if (!select_phy) return 0; - usbcfg = readl(hsotg->regs + GUSBCFG); + usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); /* * HS PHY parameters. These parameters are preserved during soft reset @@ -624,7 +631,7 @@ static int dwc2_hs_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) break; } - writel(usbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); /* Reset after setting the PHY parameters */ retval = dwc2_core_reset(hsotg); @@ -659,15 +666,15 @@ static int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED && hsotg->core_params->ulpi_fs_ls > 0) { dev_dbg(hsotg->dev, "Setting ULPI FSLS\n"); - usbcfg = readl(hsotg->regs + GUSBCFG); + usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); usbcfg |= GUSBCFG_ULPI_FS_LS; usbcfg |= GUSBCFG_ULPI_CLK_SUSP_M; - writel(usbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); } else { - usbcfg = readl(hsotg->regs + GUSBCFG); + usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); usbcfg &= ~GUSBCFG_ULPI_FS_LS; usbcfg &= ~GUSBCFG_ULPI_CLK_SUSP_M; - writel(usbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); } return retval; @@ -675,7 +682,7 @@ static int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy) static int dwc2_gahbcfg_init(struct dwc2_hsotg *hsotg) { - u32 ahbcfg = readl(hsotg->regs + GAHBCFG); + u32 ahbcfg = dwc2_readl(hsotg->regs + GAHBCFG); switch (hsotg->hw_params.arch) { case GHWCFG2_EXT_DMA_ARCH: @@ -714,7 +721,7 @@ static int dwc2_gahbcfg_init(struct dwc2_hsotg *hsotg) if (hsotg->core_params->dma_enable > 0) ahbcfg |= GAHBCFG_DMA_EN; - writel(ahbcfg, hsotg->regs + GAHBCFG); + dwc2_writel(ahbcfg, hsotg->regs + GAHBCFG); return 0; } @@ -723,7 +730,7 @@ static void dwc2_gusbcfg_init(struct dwc2_hsotg *hsotg) { u32 usbcfg; - usbcfg = readl(hsotg->regs + GUSBCFG); + usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); usbcfg &= ~(GUSBCFG_HNPCAP | GUSBCFG_SRPCAP); switch (hsotg->hw_params.op_mode) { @@ -751,7 +758,7 @@ static void dwc2_gusbcfg_init(struct dwc2_hsotg *hsotg) break; } - writel(usbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); } /** @@ -769,7 +776,7 @@ int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy, int irq) dev_dbg(hsotg->dev, "%s(%p)\n", __func__, hsotg); - usbcfg = readl(hsotg->regs + GUSBCFG); + usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); /* Set ULPI External VBUS bit if needed */ usbcfg &= ~GUSBCFG_ULPI_EXT_VBUS_DRV; @@ -782,7 +789,7 @@ int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy, int irq) if (hsotg->core_params->ts_dline > 0) usbcfg |= GUSBCFG_TERMSELDLPULSE; - writel(usbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); /* Reset the Controller */ retval = dwc2_core_reset(hsotg); @@ -808,11 +815,11 @@ int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy, int irq) dwc2_gusbcfg_init(hsotg); /* Program the GOTGCTL register */ - otgctl = readl(hsotg->regs + GOTGCTL); + otgctl = dwc2_readl(hsotg->regs + GOTGCTL); otgctl &= ~GOTGCTL_OTGVER; if (hsotg->core_params->otg_ver > 0) otgctl |= GOTGCTL_OTGVER; - writel(otgctl, hsotg->regs + GOTGCTL); + dwc2_writel(otgctl, hsotg->regs + GOTGCTL); dev_dbg(hsotg->dev, "OTG VER PARAM: %d\n", hsotg->core_params->otg_ver); /* Clear the SRP success bit for FS-I2c */ @@ -848,16 +855,16 @@ void dwc2_enable_host_interrupts(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, "%s()\n", __func__); /* Disable all interrupts */ - writel(0, hsotg->regs + GINTMSK); - writel(0, hsotg->regs + HAINTMSK); + dwc2_writel(0, hsotg->regs + GINTMSK); + dwc2_writel(0, hsotg->regs + HAINTMSK); /* Enable the common interrupts */ dwc2_enable_common_interrupts(hsotg); /* Enable host mode interrupts without disturbing common interrupts */ - intmsk = readl(hsotg->regs + GINTMSK); + intmsk = dwc2_readl(hsotg->regs + GINTMSK); intmsk |= GINTSTS_DISCONNINT | GINTSTS_PRTINT | GINTSTS_HCHINT; - writel(intmsk, hsotg->regs + GINTMSK); + dwc2_writel(intmsk, hsotg->regs + GINTMSK); } /** @@ -867,12 +874,12 @@ void dwc2_enable_host_interrupts(struct dwc2_hsotg *hsotg) */ void dwc2_disable_host_interrupts(struct dwc2_hsotg *hsotg) { - u32 intmsk = readl(hsotg->regs + GINTMSK); + u32 intmsk = dwc2_readl(hsotg->regs + GINTMSK); /* Disable host mode interrupts without disturbing common interrupts */ intmsk &= ~(GINTSTS_SOF | GINTSTS_PRTINT | GINTSTS_HCHINT | - GINTSTS_PTXFEMP | GINTSTS_NPTXFEMP); - writel(intmsk, hsotg->regs + GINTMSK); + GINTSTS_PTXFEMP | GINTSTS_NPTXFEMP | GINTSTS_DISCONNINT); + dwc2_writel(intmsk, hsotg->regs + GINTMSK); } /* @@ -952,36 +959,37 @@ static void dwc2_config_fifos(struct dwc2_hsotg *hsotg) dwc2_calculate_dynamic_fifo(hsotg); /* Rx FIFO */ - grxfsiz = readl(hsotg->regs + GRXFSIZ); + grxfsiz = dwc2_readl(hsotg->regs + GRXFSIZ); dev_dbg(hsotg->dev, "initial grxfsiz=%08x\n", grxfsiz); grxfsiz &= ~GRXFSIZ_DEPTH_MASK; grxfsiz |= params->host_rx_fifo_size << GRXFSIZ_DEPTH_SHIFT & GRXFSIZ_DEPTH_MASK; - writel(grxfsiz, hsotg->regs + GRXFSIZ); - dev_dbg(hsotg->dev, "new grxfsiz=%08x\n", readl(hsotg->regs + GRXFSIZ)); + dwc2_writel(grxfsiz, hsotg->regs + GRXFSIZ); + dev_dbg(hsotg->dev, "new grxfsiz=%08x\n", + dwc2_readl(hsotg->regs + GRXFSIZ)); /* Non-periodic Tx FIFO */ dev_dbg(hsotg->dev, "initial gnptxfsiz=%08x\n", - readl(hsotg->regs + GNPTXFSIZ)); + dwc2_readl(hsotg->regs + GNPTXFSIZ)); nptxfsiz = params->host_nperio_tx_fifo_size << FIFOSIZE_DEPTH_SHIFT & FIFOSIZE_DEPTH_MASK; nptxfsiz |= params->host_rx_fifo_size << FIFOSIZE_STARTADDR_SHIFT & FIFOSIZE_STARTADDR_MASK; - writel(nptxfsiz, hsotg->regs + GNPTXFSIZ); + dwc2_writel(nptxfsiz, hsotg->regs + GNPTXFSIZ); dev_dbg(hsotg->dev, "new gnptxfsiz=%08x\n", - readl(hsotg->regs + GNPTXFSIZ)); + dwc2_readl(hsotg->regs + GNPTXFSIZ)); /* Periodic Tx FIFO */ dev_dbg(hsotg->dev, "initial hptxfsiz=%08x\n", - readl(hsotg->regs + HPTXFSIZ)); + dwc2_readl(hsotg->regs + HPTXFSIZ)); hptxfsiz = params->host_perio_tx_fifo_size << FIFOSIZE_DEPTH_SHIFT & FIFOSIZE_DEPTH_MASK; hptxfsiz |= (params->host_rx_fifo_size + params->host_nperio_tx_fifo_size) << FIFOSIZE_STARTADDR_SHIFT & FIFOSIZE_STARTADDR_MASK; - writel(hptxfsiz, hsotg->regs + HPTXFSIZ); + dwc2_writel(hptxfsiz, hsotg->regs + HPTXFSIZ); dev_dbg(hsotg->dev, "new hptxfsiz=%08x\n", - readl(hsotg->regs + HPTXFSIZ)); + dwc2_readl(hsotg->regs + HPTXFSIZ)); if (hsotg->core_params->en_multiple_tx_fifo > 0 && hsotg->hw_params.snpsid <= DWC2_CORE_REV_2_94a) { @@ -989,14 +997,14 @@ static void dwc2_config_fifos(struct dwc2_hsotg *hsotg) * Global DFIFOCFG calculation for Host mode - * include RxFIFO, NPTXFIFO and HPTXFIFO */ - dfifocfg = readl(hsotg->regs + GDFIFOCFG); + dfifocfg = dwc2_readl(hsotg->regs + GDFIFOCFG); dfifocfg &= ~GDFIFOCFG_EPINFOBASE_MASK; dfifocfg |= (params->host_rx_fifo_size + params->host_nperio_tx_fifo_size + params->host_perio_tx_fifo_size) << GDFIFOCFG_EPINFOBASE_SHIFT & GDFIFOCFG_EPINFOBASE_MASK; - writel(dfifocfg, hsotg->regs + GDFIFOCFG); + dwc2_writel(dfifocfg, hsotg->regs + GDFIFOCFG); } } @@ -1017,14 +1025,14 @@ void dwc2_core_host_init(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, "%s(%p)\n", __func__, hsotg); /* Restart the Phy Clock */ - writel(0, hsotg->regs + PCGCTL); + dwc2_writel(0, hsotg->regs + PCGCTL); /* Initialize Host Configuration Register */ dwc2_init_fs_ls_pclk_sel(hsotg); if (hsotg->core_params->speed == DWC2_SPEED_PARAM_FULL) { - hcfg = readl(hsotg->regs + HCFG); + hcfg = dwc2_readl(hsotg->regs + HCFG); hcfg |= HCFG_FSLSSUPP; - writel(hcfg, hsotg->regs + HCFG); + dwc2_writel(hcfg, hsotg->regs + HCFG); } /* @@ -1033,9 +1041,9 @@ void dwc2_core_host_init(struct dwc2_hsotg *hsotg) * and its value must not be changed during runtime. */ if (hsotg->core_params->reload_ctl > 0) { - hfir = readl(hsotg->regs + HFIR); + hfir = dwc2_readl(hsotg->regs + HFIR); hfir |= HFIR_RLDCTRL; - writel(hfir, hsotg->regs + HFIR); + dwc2_writel(hfir, hsotg->regs + HFIR); } if (hsotg->core_params->dma_desc_enable > 0) { @@ -1051,9 +1059,9 @@ void dwc2_core_host_init(struct dwc2_hsotg *hsotg) "falling back to buffer DMA mode.\n"); hsotg->core_params->dma_desc_enable = 0; } else { - hcfg = readl(hsotg->regs + HCFG); + hcfg = dwc2_readl(hsotg->regs + HCFG); hcfg |= HCFG_DESCDMA; - writel(hcfg, hsotg->regs + HCFG); + dwc2_writel(hcfg, hsotg->regs + HCFG); } } @@ -1062,18 +1070,18 @@ void dwc2_core_host_init(struct dwc2_hsotg *hsotg) /* TODO - check this */ /* Clear Host Set HNP Enable in the OTG Control Register */ - otgctl = readl(hsotg->regs + GOTGCTL); + otgctl = dwc2_readl(hsotg->regs + GOTGCTL); otgctl &= ~GOTGCTL_HSTSETHNPEN; - writel(otgctl, hsotg->regs + GOTGCTL); + dwc2_writel(otgctl, hsotg->regs + GOTGCTL); /* Make sure the FIFOs are flushed */ dwc2_flush_tx_fifo(hsotg, 0x10 /* all TX FIFOs */); dwc2_flush_rx_fifo(hsotg); /* Clear Host Set HNP Enable in the OTG Control Register */ - otgctl = readl(hsotg->regs + GOTGCTL); + otgctl = dwc2_readl(hsotg->regs + GOTGCTL); otgctl &= ~GOTGCTL_HSTSETHNPEN; - writel(otgctl, hsotg->regs + GOTGCTL); + dwc2_writel(otgctl, hsotg->regs + GOTGCTL); if (hsotg->core_params->dma_desc_enable <= 0) { int num_channels, i; @@ -1082,25 +1090,25 @@ void dwc2_core_host_init(struct dwc2_hsotg *hsotg) /* Flush out any leftover queued requests */ num_channels = hsotg->core_params->host_channels; for (i = 0; i < num_channels; i++) { - hcchar = readl(hsotg->regs + HCCHAR(i)); + hcchar = dwc2_readl(hsotg->regs + HCCHAR(i)); hcchar &= ~HCCHAR_CHENA; hcchar |= HCCHAR_CHDIS; hcchar &= ~HCCHAR_EPDIR; - writel(hcchar, hsotg->regs + HCCHAR(i)); + dwc2_writel(hcchar, hsotg->regs + HCCHAR(i)); } /* Halt all channels to put them into a known state */ for (i = 0; i < num_channels; i++) { int count = 0; - hcchar = readl(hsotg->regs + HCCHAR(i)); + hcchar = dwc2_readl(hsotg->regs + HCCHAR(i)); hcchar |= HCCHAR_CHENA | HCCHAR_CHDIS; hcchar &= ~HCCHAR_EPDIR; - writel(hcchar, hsotg->regs + HCCHAR(i)); + dwc2_writel(hcchar, hsotg->regs + HCCHAR(i)); dev_dbg(hsotg->dev, "%s: Halt channel %d\n", __func__, i); do { - hcchar = readl(hsotg->regs + HCCHAR(i)); + hcchar = dwc2_readl(hsotg->regs + HCCHAR(i)); if (++count > 1000) { dev_err(hsotg->dev, "Unable to clear enable on channel %d\n", @@ -1121,7 +1129,7 @@ void dwc2_core_host_init(struct dwc2_hsotg *hsotg) !!(hprt0 & HPRT0_PWR)); if (!(hprt0 & HPRT0_PWR)) { hprt0 |= HPRT0_PWR; - writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hprt0, hsotg->regs + HPRT0); } } @@ -1201,7 +1209,7 @@ static void dwc2_hc_enable_slave_ints(struct dwc2_hsotg *hsotg, break; } - writel(hcintmsk, hsotg->regs + HCINTMSK(chan->hc_num)); + dwc2_writel(hcintmsk, hsotg->regs + HCINTMSK(chan->hc_num)); if (dbg_hc(chan)) dev_vdbg(hsotg->dev, "set HCINTMSK to %08x\n", hcintmsk); } @@ -1238,7 +1246,7 @@ static void dwc2_hc_enable_dma_ints(struct dwc2_hsotg *hsotg, } } - writel(hcintmsk, hsotg->regs + HCINTMSK(chan->hc_num)); + dwc2_writel(hcintmsk, hsotg->regs + HCINTMSK(chan->hc_num)); if (dbg_hc(chan)) dev_vdbg(hsotg->dev, "set HCINTMSK to %08x\n", hcintmsk); } @@ -1259,16 +1267,16 @@ static void dwc2_hc_enable_ints(struct dwc2_hsotg *hsotg, } /* Enable the top level host channel interrupt */ - intmsk = readl(hsotg->regs + HAINTMSK); + intmsk = dwc2_readl(hsotg->regs + HAINTMSK); intmsk |= 1 << chan->hc_num; - writel(intmsk, hsotg->regs + HAINTMSK); + dwc2_writel(intmsk, hsotg->regs + HAINTMSK); if (dbg_hc(chan)) dev_vdbg(hsotg->dev, "set HAINTMSK to %08x\n", intmsk); /* Make sure host channel interrupts are enabled */ - intmsk = readl(hsotg->regs + GINTMSK); + intmsk = dwc2_readl(hsotg->regs + GINTMSK); intmsk |= GINTSTS_HCHINT; - writel(intmsk, hsotg->regs + GINTMSK); + dwc2_writel(intmsk, hsotg->regs + GINTMSK); if (dbg_hc(chan)) dev_vdbg(hsotg->dev, "set GINTMSK to %08x\n", intmsk); } @@ -1297,7 +1305,7 @@ void dwc2_hc_init(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan) /* Clear old interrupt conditions for this host channel */ hcintmsk = 0xffffffff; hcintmsk &= ~HCINTMSK_RESERVED14_31; - writel(hcintmsk, hsotg->regs + HCINT(hc_num)); + dwc2_writel(hcintmsk, hsotg->regs + HCINT(hc_num)); /* Enable channel interrupts required for this transfer */ dwc2_hc_enable_ints(hsotg, chan); @@ -1314,7 +1322,7 @@ void dwc2_hc_init(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan) hcchar |= HCCHAR_LSPDDEV; hcchar |= chan->ep_type << HCCHAR_EPTYPE_SHIFT & HCCHAR_EPTYPE_MASK; hcchar |= chan->max_packet << HCCHAR_MPS_SHIFT & HCCHAR_MPS_MASK; - writel(hcchar, hsotg->regs + HCCHAR(hc_num)); + dwc2_writel(hcchar, hsotg->regs + HCCHAR(hc_num)); if (dbg_hc(chan)) { dev_vdbg(hsotg->dev, "set HCCHAR(%d) to %08x\n", hc_num, hcchar); @@ -1368,7 +1376,7 @@ void dwc2_hc_init(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan) } } - writel(hcsplt, hsotg->regs + HCSPLT(hc_num)); + dwc2_writel(hcsplt, hsotg->regs + HCSPLT(hc_num)); } /** @@ -1420,14 +1428,14 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan, u32 hcintmsk = HCINTMSK_CHHLTD; dev_vdbg(hsotg->dev, "dequeue/error\n"); - writel(hcintmsk, hsotg->regs + HCINTMSK(chan->hc_num)); + dwc2_writel(hcintmsk, hsotg->regs + HCINTMSK(chan->hc_num)); /* * Make sure no other interrupts besides halt are currently * pending. Handling another interrupt could cause a crash due * to the QTD and QH state. */ - writel(~hcintmsk, hsotg->regs + HCINT(chan->hc_num)); + dwc2_writel(~hcintmsk, hsotg->regs + HCINT(chan->hc_num)); /* * Make sure the halt status is set to URB_DEQUEUE or AHB_ERR @@ -1436,7 +1444,7 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan, */ chan->halt_status = halt_status; - hcchar = readl(hsotg->regs + HCCHAR(chan->hc_num)); + hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num)); if (!(hcchar & HCCHAR_CHENA)) { /* * The channel is either already halted or it hasn't @@ -1464,7 +1472,7 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan, return; } - hcchar = readl(hsotg->regs + HCCHAR(chan->hc_num)); + hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num)); /* No need to set the bit in DDMA for disabling the channel */ /* TODO check it everywhere channel is disabled */ @@ -1487,7 +1495,7 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan, if (chan->ep_type == USB_ENDPOINT_XFER_CONTROL || chan->ep_type == USB_ENDPOINT_XFER_BULK) { dev_vdbg(hsotg->dev, "control/bulk\n"); - nptxsts = readl(hsotg->regs + GNPTXSTS); + nptxsts = dwc2_readl(hsotg->regs + GNPTXSTS); if ((nptxsts & TXSTS_QSPCAVAIL_MASK) == 0) { dev_vdbg(hsotg->dev, "Disabling channel\n"); hcchar &= ~HCCHAR_CHENA; @@ -1495,7 +1503,7 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan, } else { if (dbg_perio()) dev_vdbg(hsotg->dev, "isoc/intr\n"); - hptxsts = readl(hsotg->regs + HPTXSTS); + hptxsts = dwc2_readl(hsotg->regs + HPTXSTS); if ((hptxsts & TXSTS_QSPCAVAIL_MASK) == 0 || hsotg->queuing_high_bandwidth) { if (dbg_perio()) @@ -1508,7 +1516,7 @@ void dwc2_hc_halt(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan, dev_vdbg(hsotg->dev, "DMA enabled\n"); } - writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); + dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); chan->halt_status = halt_status; if (hcchar & HCCHAR_CHENA) { @@ -1555,10 +1563,10 @@ void dwc2_hc_cleanup(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan) * Clear channel interrupt enables and any unhandled channel interrupt * conditions */ - writel(0, hsotg->regs + HCINTMSK(chan->hc_num)); + dwc2_writel(0, hsotg->regs + HCINTMSK(chan->hc_num)); hcintmsk = 0xffffffff; hcintmsk &= ~HCINTMSK_RESERVED14_31; - writel(hcintmsk, hsotg->regs + HCINT(chan->hc_num)); + dwc2_writel(hcintmsk, hsotg->regs + HCINT(chan->hc_num)); } /** @@ -1644,13 +1652,13 @@ static void dwc2_hc_write_packet(struct dwc2_hsotg *hsotg, if (((unsigned long)data_buf & 0x3) == 0) { /* xfer_buf is DWORD aligned */ for (i = 0; i < dword_count; i++, data_buf++) - writel(*data_buf, data_fifo); + dwc2_writel(*data_buf, data_fifo); } else { /* xfer_buf is not DWORD aligned */ for (i = 0; i < dword_count; i++, data_buf++) { u32 data = data_buf[0] | data_buf[1] << 8 | data_buf[2] << 16 | data_buf[3] << 24; - writel(data, data_fifo); + dwc2_writel(data, data_fifo); } } @@ -1803,7 +1811,7 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg, hctsiz |= num_packets << TSIZ_PKTCNT_SHIFT & TSIZ_PKTCNT_MASK; hctsiz |= chan->data_pid_start << TSIZ_SC_MC_PID_SHIFT & TSIZ_SC_MC_PID_MASK; - writel(hctsiz, hsotg->regs + HCTSIZ(chan->hc_num)); + dwc2_writel(hctsiz, hsotg->regs + HCTSIZ(chan->hc_num)); if (dbg_hc(chan)) { dev_vdbg(hsotg->dev, "Wrote %08x to HCTSIZ(%d)\n", hctsiz, chan->hc_num); @@ -1831,7 +1839,7 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg, } else { dma_addr = chan->xfer_dma; } - writel((u32)dma_addr, hsotg->regs + HCDMA(chan->hc_num)); + dwc2_writel((u32)dma_addr, hsotg->regs + HCDMA(chan->hc_num)); if (dbg_hc(chan)) dev_vdbg(hsotg->dev, "Wrote %08lx to HCDMA(%d)\n", (unsigned long)dma_addr, chan->hc_num); @@ -1839,13 +1847,13 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg, /* Start the split */ if (chan->do_split) { - u32 hcsplt = readl(hsotg->regs + HCSPLT(chan->hc_num)); + u32 hcsplt = dwc2_readl(hsotg->regs + HCSPLT(chan->hc_num)); hcsplt |= HCSPLT_SPLTENA; - writel(hcsplt, hsotg->regs + HCSPLT(chan->hc_num)); + dwc2_writel(hcsplt, hsotg->regs + HCSPLT(chan->hc_num)); } - hcchar = readl(hsotg->regs + HCCHAR(chan->hc_num)); + hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num)); hcchar &= ~HCCHAR_MULTICNT_MASK; hcchar |= chan->multi_count << HCCHAR_MULTICNT_SHIFT & HCCHAR_MULTICNT_MASK; @@ -1865,7 +1873,7 @@ void dwc2_hc_start_transfer(struct dwc2_hsotg *hsotg, (hcchar & HCCHAR_MULTICNT_MASK) >> HCCHAR_MULTICNT_SHIFT); - writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); + dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); if (dbg_hc(chan)) dev_vdbg(hsotg->dev, "Wrote %08x to HCCHAR(%d)\n", hcchar, chan->hc_num); @@ -1924,18 +1932,18 @@ void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg, dev_vdbg(hsotg->dev, " NTD: %d\n", chan->ntd - 1); } - writel(hctsiz, hsotg->regs + HCTSIZ(chan->hc_num)); + dwc2_writel(hctsiz, hsotg->regs + HCTSIZ(chan->hc_num)); hc_dma = (u32)chan->desc_list_addr & HCDMA_DMA_ADDR_MASK; /* Always start from first descriptor */ hc_dma &= ~HCDMA_CTD_MASK; - writel(hc_dma, hsotg->regs + HCDMA(chan->hc_num)); + dwc2_writel(hc_dma, hsotg->regs + HCDMA(chan->hc_num)); if (dbg_hc(chan)) dev_vdbg(hsotg->dev, "Wrote %08x to HCDMA(%d)\n", hc_dma, chan->hc_num); - hcchar = readl(hsotg->regs + HCCHAR(chan->hc_num)); + hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num)); hcchar &= ~HCCHAR_MULTICNT_MASK; hcchar |= chan->multi_count << HCCHAR_MULTICNT_SHIFT & HCCHAR_MULTICNT_MASK; @@ -1954,7 +1962,7 @@ void dwc2_hc_start_transfer_ddma(struct dwc2_hsotg *hsotg, (hcchar & HCCHAR_MULTICNT_MASK) >> HCCHAR_MULTICNT_SHIFT); - writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); + dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); if (dbg_hc(chan)) dev_vdbg(hsotg->dev, "Wrote %08x to HCCHAR(%d)\n", hcchar, chan->hc_num); @@ -2011,7 +2019,7 @@ int dwc2_hc_continue_transfer(struct dwc2_hsotg *hsotg, * transfer completes, the extra requests for the channel will * be flushed. */ - u32 hcchar = readl(hsotg->regs + HCCHAR(chan->hc_num)); + u32 hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num)); dwc2_hc_set_even_odd_frame(hsotg, chan, &hcchar); hcchar |= HCCHAR_CHENA; @@ -2019,7 +2027,7 @@ int dwc2_hc_continue_transfer(struct dwc2_hsotg *hsotg, if (dbg_hc(chan)) dev_vdbg(hsotg->dev, " IN xfer: hcchar = 0x%08x\n", hcchar); - writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); + dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); chan->requests++; return 1; } @@ -2029,8 +2037,8 @@ int dwc2_hc_continue_transfer(struct dwc2_hsotg *hsotg, if (chan->xfer_count < chan->xfer_len) { if (chan->ep_type == USB_ENDPOINT_XFER_INT || chan->ep_type == USB_ENDPOINT_XFER_ISOC) { - u32 hcchar = readl(hsotg->regs + - HCCHAR(chan->hc_num)); + u32 hcchar = dwc2_readl(hsotg->regs + + HCCHAR(chan->hc_num)); dwc2_hc_set_even_odd_frame(hsotg, chan, &hcchar); @@ -2066,12 +2074,12 @@ void dwc2_hc_do_ping(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan) hctsiz = TSIZ_DOPNG; hctsiz |= 1 << TSIZ_PKTCNT_SHIFT; - writel(hctsiz, hsotg->regs + HCTSIZ(chan->hc_num)); + dwc2_writel(hctsiz, hsotg->regs + HCTSIZ(chan->hc_num)); - hcchar = readl(hsotg->regs + HCCHAR(chan->hc_num)); + hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num)); hcchar |= HCCHAR_CHENA; hcchar &= ~HCCHAR_CHDIS; - writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); + dwc2_writel(hcchar, hsotg->regs + HCCHAR(chan->hc_num)); } /** @@ -2090,8 +2098,8 @@ u32 dwc2_calc_frame_interval(struct dwc2_hsotg *hsotg) u32 hprt0; int clock = 60; /* default value */ - usbcfg = readl(hsotg->regs + GUSBCFG); - hprt0 = readl(hsotg->regs + HPRT0); + usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); + hprt0 = dwc2_readl(hsotg->regs + HPRT0); if (!(usbcfg & GUSBCFG_PHYSEL) && (usbcfg & GUSBCFG_ULPI_UTMI_SEL) && !(usbcfg & GUSBCFG_PHYIF16)) @@ -2147,7 +2155,7 @@ void dwc2_read_packet(struct dwc2_hsotg *hsotg, u8 *dest, u16 bytes) dev_vdbg(hsotg->dev, "%s(%p,%p,%d)\n", __func__, hsotg, dest, bytes); for (i = 0; i < word_count; i++, data_buf++) - *data_buf = readl(fifo); + *data_buf = dwc2_readl(fifo); } /** @@ -2167,56 +2175,56 @@ void dwc2_dump_host_registers(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, "Host Global Registers\n"); addr = hsotg->regs + HCFG; dev_dbg(hsotg->dev, "HCFG @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + HFIR; dev_dbg(hsotg->dev, "HFIR @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + HFNUM; dev_dbg(hsotg->dev, "HFNUM @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + HPTXSTS; dev_dbg(hsotg->dev, "HPTXSTS @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + HAINT; dev_dbg(hsotg->dev, "HAINT @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + HAINTMSK; dev_dbg(hsotg->dev, "HAINTMSK @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); if (hsotg->core_params->dma_desc_enable > 0) { addr = hsotg->regs + HFLBADDR; dev_dbg(hsotg->dev, "HFLBADDR @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); } addr = hsotg->regs + HPRT0; dev_dbg(hsotg->dev, "HPRT0 @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); for (i = 0; i < hsotg->core_params->host_channels; i++) { dev_dbg(hsotg->dev, "Host Channel %d Specific Registers\n", i); addr = hsotg->regs + HCCHAR(i); dev_dbg(hsotg->dev, "HCCHAR @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + HCSPLT(i); dev_dbg(hsotg->dev, "HCSPLT @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + HCINT(i); dev_dbg(hsotg->dev, "HCINT @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + HCINTMSK(i); dev_dbg(hsotg->dev, "HCINTMSK @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + HCTSIZ(i); dev_dbg(hsotg->dev, "HCTSIZ @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + HCDMA(i); dev_dbg(hsotg->dev, "HCDMA @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); if (hsotg->core_params->dma_desc_enable > 0) { addr = hsotg->regs + HCDMAB(i); dev_dbg(hsotg->dev, "HCDMAB @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); } } #endif @@ -2238,80 +2246,80 @@ void dwc2_dump_global_registers(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, "Core Global Registers\n"); addr = hsotg->regs + GOTGCTL; dev_dbg(hsotg->dev, "GOTGCTL @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + GOTGINT; dev_dbg(hsotg->dev, "GOTGINT @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + GAHBCFG; dev_dbg(hsotg->dev, "GAHBCFG @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + GUSBCFG; dev_dbg(hsotg->dev, "GUSBCFG @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + GRSTCTL; dev_dbg(hsotg->dev, "GRSTCTL @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + GINTSTS; dev_dbg(hsotg->dev, "GINTSTS @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + GINTMSK; dev_dbg(hsotg->dev, "GINTMSK @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + GRXSTSR; dev_dbg(hsotg->dev, "GRXSTSR @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + GRXFSIZ; dev_dbg(hsotg->dev, "GRXFSIZ @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + GNPTXFSIZ; dev_dbg(hsotg->dev, "GNPTXFSIZ @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + GNPTXSTS; dev_dbg(hsotg->dev, "GNPTXSTS @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + GI2CCTL; dev_dbg(hsotg->dev, "GI2CCTL @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + GPVNDCTL; dev_dbg(hsotg->dev, "GPVNDCTL @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + GGPIO; dev_dbg(hsotg->dev, "GGPIO @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + GUID; dev_dbg(hsotg->dev, "GUID @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + GSNPSID; dev_dbg(hsotg->dev, "GSNPSID @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + GHWCFG1; dev_dbg(hsotg->dev, "GHWCFG1 @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + GHWCFG2; dev_dbg(hsotg->dev, "GHWCFG2 @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + GHWCFG3; dev_dbg(hsotg->dev, "GHWCFG3 @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + GHWCFG4; dev_dbg(hsotg->dev, "GHWCFG4 @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + GLPMCFG; dev_dbg(hsotg->dev, "GLPMCFG @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + GPWRDN; dev_dbg(hsotg->dev, "GPWRDN @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + GDFIFOCFG; dev_dbg(hsotg->dev, "GDFIFOCFG @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + HPTXFSIZ; dev_dbg(hsotg->dev, "HPTXFSIZ @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); addr = hsotg->regs + PCGCTL; dev_dbg(hsotg->dev, "PCGCTL @0x%08lX : 0x%08X\n", - (unsigned long)addr, readl(addr)); + (unsigned long)addr, dwc2_readl(addr)); #endif } @@ -2330,15 +2338,15 @@ void dwc2_flush_tx_fifo(struct dwc2_hsotg *hsotg, const int num) greset = GRSTCTL_TXFFLSH; greset |= num << GRSTCTL_TXFNUM_SHIFT & GRSTCTL_TXFNUM_MASK; - writel(greset, hsotg->regs + GRSTCTL); + dwc2_writel(greset, hsotg->regs + GRSTCTL); do { - greset = readl(hsotg->regs + GRSTCTL); + greset = dwc2_readl(hsotg->regs + GRSTCTL); if (++count > 10000) { dev_warn(hsotg->dev, "%s() HANG! GRSTCTL=%0x GNPTXSTS=0x%08x\n", __func__, greset, - readl(hsotg->regs + GNPTXSTS)); + dwc2_readl(hsotg->regs + GNPTXSTS)); break; } udelay(1); @@ -2361,10 +2369,10 @@ void dwc2_flush_rx_fifo(struct dwc2_hsotg *hsotg) dev_vdbg(hsotg->dev, "%s()\n", __func__); greset = GRSTCTL_RXFFLSH; - writel(greset, hsotg->regs + GRSTCTL); + dwc2_writel(greset, hsotg->regs + GRSTCTL); do { - greset = readl(hsotg->regs + GRSTCTL); + greset = dwc2_readl(hsotg->regs + GRSTCTL); if (++count > 10000) { dev_warn(hsotg->dev, "%s() HANG! GRSTCTL=%0x\n", __func__, greset); @@ -3062,7 +3070,7 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) * 0x45f42xxx or 0x45f43xxx, which corresponds to either "OT2" or "OT3", * as in "OTG version 2.xx" or "OTG version 3.xx". */ - hw->snpsid = readl(hsotg->regs + GSNPSID); + hw->snpsid = dwc2_readl(hsotg->regs + GSNPSID); if ((hw->snpsid & 0xfffff000) != 0x4f542000 && (hw->snpsid & 0xfffff000) != 0x4f543000) { dev_err(hsotg->dev, "Bad value for GSNPSID: 0x%08x\n", @@ -3074,11 +3082,11 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf, hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid); - hwcfg1 = readl(hsotg->regs + GHWCFG1); - hwcfg2 = readl(hsotg->regs + GHWCFG2); - hwcfg3 = readl(hsotg->regs + GHWCFG3); - hwcfg4 = readl(hsotg->regs + GHWCFG4); - grxfsiz = readl(hsotg->regs + GRXFSIZ); + hwcfg1 = dwc2_readl(hsotg->regs + GHWCFG1); + hwcfg2 = dwc2_readl(hsotg->regs + GHWCFG2); + hwcfg3 = dwc2_readl(hsotg->regs + GHWCFG3); + hwcfg4 = dwc2_readl(hsotg->regs + GHWCFG4); + grxfsiz = dwc2_readl(hsotg->regs + GRXFSIZ); dev_dbg(hsotg->dev, "hwcfg1=%08x\n", hwcfg1); dev_dbg(hsotg->dev, "hwcfg2=%08x\n", hwcfg2); @@ -3087,18 +3095,18 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, "grxfsiz=%08x\n", grxfsiz); /* Force host mode to get HPTXFSIZ / GNPTXFSIZ exact power on value */ - gusbcfg = readl(hsotg->regs + GUSBCFG); + gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG); gusbcfg |= GUSBCFG_FORCEHOSTMODE; - writel(gusbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG); usleep_range(100000, 150000); - gnptxfsiz = readl(hsotg->regs + GNPTXFSIZ); - hptxfsiz = readl(hsotg->regs + HPTXFSIZ); + gnptxfsiz = dwc2_readl(hsotg->regs + GNPTXFSIZ); + hptxfsiz = dwc2_readl(hsotg->regs + HPTXFSIZ); dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz); dev_dbg(hsotg->dev, "hptxfsiz=%08x\n", hptxfsiz); - gusbcfg = readl(hsotg->regs + GUSBCFG); + gusbcfg = dwc2_readl(hsotg->regs + GUSBCFG); gusbcfg &= ~GUSBCFG_FORCEHOSTMODE; - writel(gusbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(gusbcfg, hsotg->regs + GUSBCFG); usleep_range(100000, 150000); /* hwcfg2 */ @@ -3233,7 +3241,7 @@ u16 dwc2_get_otg_version(struct dwc2_hsotg *hsotg) bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg) { - if (readl(hsotg->regs + GSNPSID) == 0xffffffff) + if (dwc2_readl(hsotg->regs + GSNPSID) == 0xffffffff) return false; else return true; @@ -3247,10 +3255,10 @@ bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg) */ void dwc2_enable_global_interrupts(struct dwc2_hsotg *hsotg) { - u32 ahbcfg = readl(hsotg->regs + GAHBCFG); + u32 ahbcfg = dwc2_readl(hsotg->regs + GAHBCFG); ahbcfg |= GAHBCFG_GLBL_INTR_EN; - writel(ahbcfg, hsotg->regs + GAHBCFG); + dwc2_writel(ahbcfg, hsotg->regs + GAHBCFG); } /** @@ -3261,10 +3269,10 @@ void dwc2_enable_global_interrupts(struct dwc2_hsotg *hsotg) */ void dwc2_disable_global_interrupts(struct dwc2_hsotg *hsotg) { - u32 ahbcfg = readl(hsotg->regs + GAHBCFG); + u32 ahbcfg = dwc2_readl(hsotg->regs + GAHBCFG); ahbcfg &= ~GAHBCFG_GLBL_INTR_EN; - writel(ahbcfg, hsotg->regs + GAHBCFG); + dwc2_writel(ahbcfg, hsotg->regs + GAHBCFG); } MODULE_DESCRIPTION("DESIGNWARE HS OTG Core"); diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 0ed87620..a66d3cb 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -44,22 +44,38 @@ #include <linux/usb/phy.h> #include "hw.h" -#ifdef DWC2_LOG_WRITES -static inline void do_write(u32 value, void *addr) +static inline u32 dwc2_readl(const void __iomem *addr) { - writel(value, addr); - pr_info("INFO:: wrote %08x to %p\n", value, addr); + u32 value = __raw_readl(addr); + + /* In order to preserve endianness __raw_* operation is used. Therefore + * a barrier is needed to ensure IO access is not re-ordered across + * reads or writes + */ + mb(); + return value; } -#undef writel -#define writel(v, a) do_write(v, a) +static inline void dwc2_writel(u32 value, void __iomem *addr) +{ + __raw_writel(value, addr); + + /* + * In order to preserve endianness __raw_* operation is used. Therefore + * a barrier is needed to ensure IO access is not re-ordered across + * reads or writes + */ + mb(); +#ifdef DWC2_LOG_WRITES + pr_info("INFO:: wrote %08x to %p\n", value, addr); #endif +} /* Maximum number of Endpoints/HostChannels */ #define MAX_EPS_CHANNELS 16 -/* s3c-hsotg declarations */ -static const char * const s3c_hsotg_supply_names[] = { +/* dwc2-hsotg declarations */ +static const char * const dwc2_hsotg_supply_names[] = { "vusb_d", /* digital USB supply, 1.2V */ "vusb_a", /* analog USB supply, 1.1V */ }; @@ -85,10 +101,10 @@ static const char * const s3c_hsotg_supply_names[] = { #define EP0_MPS_LIMIT 64 struct dwc2_hsotg; -struct s3c_hsotg_req; +struct dwc2_hsotg_req; /** - * struct s3c_hsotg_ep - driver endpoint definition. + * struct dwc2_hsotg_ep - driver endpoint definition. * @ep: The gadget layer representation of the endpoint. * @name: The driver generated name for the endpoint. * @queue: Queue of requests for this endpoint. @@ -127,11 +143,11 @@ struct s3c_hsotg_req; * as in shared-fifo mode periodic in acts like a single-frame packet * buffer than a fifo) */ -struct s3c_hsotg_ep { +struct dwc2_hsotg_ep { struct usb_ep ep; struct list_head queue; struct dwc2_hsotg *parent; - struct s3c_hsotg_req *req; + struct dwc2_hsotg_req *req; struct dentry *debugfs; unsigned long total_data; @@ -150,17 +166,18 @@ struct s3c_hsotg_ep { unsigned int periodic:1; unsigned int isochronous:1; unsigned int send_zlp:1; + unsigned int has_correct_parity:1; char name[10]; }; /** - * struct s3c_hsotg_req - data transfer request + * struct dwc2_hsotg_req - data transfer request * @req: The USB gadget request * @queue: The list of requests for the endpoint this is queued for. * @saved_req_buf: variable to save req.buf when bounce buffers are used. */ -struct s3c_hsotg_req { +struct dwc2_hsotg_req { struct usb_request req; struct list_head queue; void *saved_req_buf; @@ -562,6 +579,15 @@ struct dwc2_hregs_backup { * - USB_DR_MODE_PERIPHERAL * - USB_DR_MODE_HOST * - USB_DR_MODE_OTG + * @hcd_enabled Host mode sub-driver initialization indicator. + * @gadget_enabled Peripheral mode sub-driver initialization indicator. + * @ll_hw_enabled Status of low-level hardware resources. + * @phy: The otg phy transceiver structure for phy control. + * @uphy: The otg phy transceiver structure for old USB phy control. + * @plat: The platform specific configuration data. This can be removed once + * all SoCs support usb transceiver. + * @supplies: Definition of USB power supplies + * @phyif: PHY interface width * @lock: Spinlock that protects all the driver data structures * @priv: Stores a pointer to the struct usb_hcd * @queuing_high_bandwidth: True if multiple packets of a high-bandwidth @@ -654,12 +680,6 @@ struct dwc2_hregs_backup { * These are for peripheral mode: * * @driver: USB gadget driver - * @phy: The otg phy transceiver structure for phy control. - * @uphy: The otg phy transceiver structure for old USB phy control. - * @plat: The platform specific configuration data. This can be removed once - * all SoCs support usb transceiver. - * @supplies: Definition of USB power supplies - * @phyif: PHY interface width * @dedicated_fifos: Set if the hardware has dedicated IN-EP fifos. * @num_of_eps: Number of available EPs (excluding EP0) * @debug_root: Root directrory for debugfs. @@ -672,7 +692,6 @@ struct dwc2_hregs_backup { * @ctrl_req: Request for EP0 control packets. * @ep0_state: EP0 control transfers state * @test_mode: USB test mode requested by the host - * @last_rst: Time of last reset * @eps: The endpoints being supplied to the gadget framework * @g_using_dma: Indicate if dma usage is enabled * @g_rx_fifo_sz: Contains rx fifo size value @@ -690,13 +709,15 @@ struct dwc2_hsotg { enum usb_dr_mode dr_mode; unsigned int hcd_enabled:1; unsigned int gadget_enabled:1; + unsigned int ll_hw_enabled:1; struct phy *phy; struct usb_phy *uphy; - struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsotg_supply_names)]; + struct dwc2_hsotg_plat *plat; + struct regulator_bulk_data supplies[ARRAY_SIZE(dwc2_hsotg_supply_names)]; + u32 phyif; spinlock_t lock; - struct mutex init_mutex; void *priv; int irq; struct clk *clk; @@ -748,6 +769,7 @@ struct dwc2_hsotg { u16 frame_usecs[8]; u16 frame_number; u16 periodic_qh_count; + bool bus_suspended; #ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS #define FRAME_NUM_ARRAY_SIZE 1000 @@ -796,9 +818,6 @@ struct dwc2_hsotg { #if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) /* Gadget structures */ struct usb_gadget_driver *driver; - struct s3c_hsotg_plat *plat; - - u32 phyif; int fifo_mem; unsigned int dedicated_fifos:1; unsigned char num_of_eps; @@ -814,9 +833,8 @@ struct dwc2_hsotg { struct usb_gadget gadget; unsigned int enabled:1; unsigned int connected:1; - unsigned long last_rst; - struct s3c_hsotg_ep *eps_in[MAX_EPS_CHANNELS]; - struct s3c_hsotg_ep *eps_out[MAX_EPS_CHANNELS]; + struct dwc2_hsotg_ep *eps_in[MAX_EPS_CHANNELS]; + struct dwc2_hsotg_ep *eps_out[MAX_EPS_CHANNELS]; u32 g_using_dma; u32 g_rx_fifo_sz; u32 g_np_g_tx_fifo_sz; @@ -1088,7 +1106,8 @@ extern void dwc2_set_all_params(struct dwc2_core_params *params, int value); extern int dwc2_get_hwparams(struct dwc2_hsotg *hsotg); - +extern int dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg); +extern int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg); /* * Dump core registers and SPRAM @@ -1104,30 +1123,30 @@ extern u16 dwc2_get_otg_version(struct dwc2_hsotg *hsotg); /* Gadget defines */ #if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) -extern int s3c_hsotg_remove(struct dwc2_hsotg *hsotg); -extern int s3c_hsotg_suspend(struct dwc2_hsotg *dwc2); -extern int s3c_hsotg_resume(struct dwc2_hsotg *dwc2); +extern int dwc2_hsotg_remove(struct dwc2_hsotg *hsotg); +extern int dwc2_hsotg_suspend(struct dwc2_hsotg *dwc2); +extern int dwc2_hsotg_resume(struct dwc2_hsotg *dwc2); extern int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq); -extern void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2, +extern void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2, bool reset); -extern void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg); -extern void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2); -extern int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode); +extern void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg); +extern void dwc2_hsotg_disconnect(struct dwc2_hsotg *dwc2); +extern int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode); #define dwc2_is_device_connected(hsotg) (hsotg->connected) #else -static inline int s3c_hsotg_remove(struct dwc2_hsotg *dwc2) +static inline int dwc2_hsotg_remove(struct dwc2_hsotg *dwc2) { return 0; } -static inline int s3c_hsotg_suspend(struct dwc2_hsotg *dwc2) +static inline int dwc2_hsotg_suspend(struct dwc2_hsotg *dwc2) { return 0; } -static inline int s3c_hsotg_resume(struct dwc2_hsotg *dwc2) +static inline int dwc2_hsotg_resume(struct dwc2_hsotg *dwc2) { return 0; } static inline int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) { return 0; } -static inline void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2, +static inline void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2, bool reset) {} -static inline void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg) {} -static inline void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2) {} -static inline int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, +static inline void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg) {} +static inline void dwc2_hsotg_disconnect(struct dwc2_hsotg *dwc2) {} +static inline int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode) { return 0; } #define dwc2_is_device_connected(hsotg) (0) diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 927be1e..27daa42 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -80,15 +80,15 @@ static const char *dwc2_op_state_str(struct dwc2_hsotg *hsotg) */ static void dwc2_handle_usb_port_intr(struct dwc2_hsotg *hsotg) { - u32 hprt0 = readl(hsotg->regs + HPRT0); + u32 hprt0 = dwc2_readl(hsotg->regs + HPRT0); if (hprt0 & HPRT0_ENACHG) { hprt0 &= ~HPRT0_ENA; - writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hprt0, hsotg->regs + HPRT0); } /* Clear interrupt */ - writel(GINTSTS_PRTINT, hsotg->regs + GINTSTS); + dwc2_writel(GINTSTS_PRTINT, hsotg->regs + GINTSTS); } /** @@ -102,7 +102,7 @@ static void dwc2_handle_mode_mismatch_intr(struct dwc2_hsotg *hsotg) dwc2_is_host_mode(hsotg) ? "Host" : "Device"); /* Clear interrupt */ - writel(GINTSTS_MODEMIS, hsotg->regs + GINTSTS); + dwc2_writel(GINTSTS_MODEMIS, hsotg->regs + GINTSTS); } /** @@ -117,8 +117,8 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg) u32 gotgctl; u32 gintmsk; - gotgint = readl(hsotg->regs + GOTGINT); - gotgctl = readl(hsotg->regs + GOTGCTL); + gotgint = dwc2_readl(hsotg->regs + GOTGINT); + gotgctl = dwc2_readl(hsotg->regs + GOTGCTL); dev_dbg(hsotg->dev, "++OTG Interrupt gotgint=%0x [%s]\n", gotgint, dwc2_op_state_str(hsotg)); @@ -126,10 +126,10 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, " ++OTG Interrupt: Session End Detected++ (%s)\n", dwc2_op_state_str(hsotg)); - gotgctl = readl(hsotg->regs + GOTGCTL); + gotgctl = dwc2_readl(hsotg->regs + GOTGCTL); if (dwc2_is_device_mode(hsotg)) - s3c_hsotg_disconnect(hsotg); + dwc2_hsotg_disconnect(hsotg); if (hsotg->op_state == OTG_STATE_B_HOST) { hsotg->op_state = OTG_STATE_B_PERIPHERAL; @@ -152,15 +152,15 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg) hsotg->lx_state = DWC2_L0; } - gotgctl = readl(hsotg->regs + GOTGCTL); + gotgctl = dwc2_readl(hsotg->regs + GOTGCTL); gotgctl &= ~GOTGCTL_DEVHNPEN; - writel(gotgctl, hsotg->regs + GOTGCTL); + dwc2_writel(gotgctl, hsotg->regs + GOTGCTL); } if (gotgint & GOTGINT_SES_REQ_SUC_STS_CHNG) { dev_dbg(hsotg->dev, " ++OTG Interrupt: Session Request Success Status Change++\n"); - gotgctl = readl(hsotg->regs + GOTGCTL); + gotgctl = dwc2_readl(hsotg->regs + GOTGCTL); if (gotgctl & GOTGCTL_SESREQSCS) { if (hsotg->core_params->phy_type == DWC2_PHY_TYPE_PARAM_FS @@ -168,9 +168,9 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg) hsotg->srp_success = 1; } else { /* Clear Session Request */ - gotgctl = readl(hsotg->regs + GOTGCTL); + gotgctl = dwc2_readl(hsotg->regs + GOTGCTL); gotgctl &= ~GOTGCTL_SESREQ; - writel(gotgctl, hsotg->regs + GOTGCTL); + dwc2_writel(gotgctl, hsotg->regs + GOTGCTL); } } } @@ -180,7 +180,7 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg) * Print statements during the HNP interrupt handling * can cause it to fail */ - gotgctl = readl(hsotg->regs + GOTGCTL); + gotgctl = dwc2_readl(hsotg->regs + GOTGCTL); /* * WA for 3.00a- HW is not setting cur_mode, even sometimes * this does not help @@ -200,9 +200,9 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg) * interrupt does not get handled and Linux * complains loudly. */ - gintmsk = readl(hsotg->regs + GINTMSK); + gintmsk = dwc2_readl(hsotg->regs + GINTMSK); gintmsk &= ~GINTSTS_SOF; - writel(gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(gintmsk, hsotg->regs + GINTMSK); /* * Call callback function with spin lock @@ -216,9 +216,9 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg) hsotg->op_state = OTG_STATE_B_HOST; } } else { - gotgctl = readl(hsotg->regs + GOTGCTL); + gotgctl = dwc2_readl(hsotg->regs + GOTGCTL); gotgctl &= ~(GOTGCTL_HNPREQ | GOTGCTL_DEVHNPEN); - writel(gotgctl, hsotg->regs + GOTGCTL); + dwc2_writel(gotgctl, hsotg->regs + GOTGCTL); dev_dbg(hsotg->dev, "HNP Failed\n"); dev_err(hsotg->dev, "Device Not Connected/Responding\n"); @@ -244,9 +244,9 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg) hsotg->op_state = OTG_STATE_A_PERIPHERAL; } else { /* Need to disable SOF interrupt immediately */ - gintmsk = readl(hsotg->regs + GINTMSK); + gintmsk = dwc2_readl(hsotg->regs + GINTMSK); gintmsk &= ~GINTSTS_SOF; - writel(gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(gintmsk, hsotg->regs + GINTMSK); spin_unlock(&hsotg->lock); dwc2_hcd_start(hsotg); spin_lock(&hsotg->lock); @@ -261,7 +261,7 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, " ++OTG Interrupt: Debounce Done++\n"); /* Clear GOTGINT */ - writel(gotgint, hsotg->regs + GOTGINT); + dwc2_writel(gotgint, hsotg->regs + GOTGINT); } /** @@ -276,11 +276,11 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg) */ static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg) { - u32 gintmsk = readl(hsotg->regs + GINTMSK); + u32 gintmsk = dwc2_readl(hsotg->regs + GINTMSK); /* Need to disable SOF interrupt immediately */ gintmsk &= ~GINTSTS_SOF; - writel(gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(gintmsk, hsotg->regs + GINTMSK); dev_dbg(hsotg->dev, " ++Connector ID Status Change Interrupt++ (%s)\n", dwc2_is_host_mode(hsotg) ? "Host" : "Device"); @@ -297,7 +297,7 @@ static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg) } /* Clear interrupt */ - writel(GINTSTS_CONIDSTSCHNG, hsotg->regs + GINTSTS); + dwc2_writel(GINTSTS_CONIDSTSCHNG, hsotg->regs + GINTSTS); } /** @@ -313,16 +313,28 @@ static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg) */ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg) { - dev_dbg(hsotg->dev, "++Session Request Interrupt++\n"); + int ret; + + dev_dbg(hsotg->dev, "Session request interrupt - lx_state=%d\n", + hsotg->lx_state); /* Clear interrupt */ - writel(GINTSTS_SESSREQINT, hsotg->regs + GINTSTS); + dwc2_writel(GINTSTS_SESSREQINT, hsotg->regs + GINTSTS); - /* - * Report disconnect if there is any previous session established - */ - if (dwc2_is_device_mode(hsotg)) - s3c_hsotg_disconnect(hsotg); + if (dwc2_is_device_mode(hsotg)) { + if (hsotg->lx_state == DWC2_L2) { + ret = dwc2_exit_hibernation(hsotg, true); + if (ret && (ret != -ENOTSUPP)) + dev_err(hsotg->dev, + "exit hibernation failed\n"); + } + + /* + * Report disconnect if there is any previous session + * established + */ + dwc2_hsotg_disconnect(hsotg); + } } /* @@ -339,13 +351,14 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state); if (dwc2_is_device_mode(hsotg)) { - dev_dbg(hsotg->dev, "DSTS=0x%0x\n", readl(hsotg->regs + DSTS)); + dev_dbg(hsotg->dev, "DSTS=0x%0x\n", + dwc2_readl(hsotg->regs + DSTS)); if (hsotg->lx_state == DWC2_L2) { - u32 dctl = readl(hsotg->regs + DCTL); + u32 dctl = dwc2_readl(hsotg->regs + DCTL); /* Clear Remote Wakeup Signaling */ dctl &= ~DCTL_RMTWKUPSIG; - writel(dctl, hsotg->regs + DCTL); + dwc2_writel(dctl, hsotg->regs + DCTL); ret = dwc2_exit_hibernation(hsotg, true); if (ret && (ret != -ENOTSUPP)) dev_err(hsotg->dev, "exit hibernation failed\n"); @@ -355,12 +368,16 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) /* Change to L0 state */ hsotg->lx_state = DWC2_L0; } else { + if (hsotg->core_params->hibernation) { + dwc2_writel(GINTSTS_WKUPINT, hsotg->regs + GINTSTS); + return; + } if (hsotg->lx_state != DWC2_L1) { - u32 pcgcctl = readl(hsotg->regs + PCGCTL); + u32 pcgcctl = dwc2_readl(hsotg->regs + PCGCTL); /* Restart the Phy Clock */ pcgcctl &= ~PCGCTL_STOPPCLK; - writel(pcgcctl, hsotg->regs + PCGCTL); + dwc2_writel(pcgcctl, hsotg->regs + PCGCTL); mod_timer(&hsotg->wkp_timer, jiffies + msecs_to_jiffies(71)); } else { @@ -370,7 +387,7 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) } /* Clear interrupt */ - writel(GINTSTS_WKUPINT, hsotg->regs + GINTSTS); + dwc2_writel(GINTSTS_WKUPINT, hsotg->regs + GINTSTS); } /* @@ -386,10 +403,7 @@ static void dwc2_handle_disconnect_intr(struct dwc2_hsotg *hsotg) if (hsotg->op_state == OTG_STATE_A_HOST) dwc2_hcd_disconnect(hsotg); - /* Change to L3 (OFF) state */ - hsotg->lx_state = DWC2_L3; - - writel(GINTSTS_DISCONNINT, hsotg->regs + GINTSTS); + dwc2_writel(GINTSTS_DISCONNINT, hsotg->regs + GINTSTS); } /* @@ -412,7 +426,7 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) * Check the Device status register to determine if the Suspend * state is active */ - dsts = readl(hsotg->regs + DSTS); + dsts = dwc2_readl(hsotg->regs + DSTS); dev_dbg(hsotg->dev, "DSTS=0x%0x\n", dsts); dev_dbg(hsotg->dev, "DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d\n", @@ -465,7 +479,7 @@ skip_power_saving: clear_int: /* Clear interrupt */ - writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS); + dwc2_writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS); } #define GINTMSK_COMMON (GINTSTS_WKUPINT | GINTSTS_SESSREQINT | \ @@ -483,9 +497,9 @@ static u32 dwc2_read_common_intr(struct dwc2_hsotg *hsotg) u32 gahbcfg; u32 gintmsk_common = GINTMSK_COMMON; - gintsts = readl(hsotg->regs + GINTSTS); - gintmsk = readl(hsotg->regs + GINTMSK); - gahbcfg = readl(hsotg->regs + GAHBCFG); + gintsts = dwc2_readl(hsotg->regs + GINTSTS); + gintmsk = dwc2_readl(hsotg->regs + GINTMSK); + gahbcfg = dwc2_readl(hsotg->regs + GAHBCFG); /* If any common interrupts set */ if (gintsts & gintmsk_common) diff --git a/drivers/usb/dwc2/debugfs.c b/drivers/usb/dwc2/debugfs.c index ef2ee3d..55d91f2 100644 --- a/drivers/usb/dwc2/debugfs.c +++ b/drivers/usb/dwc2/debugfs.c @@ -57,7 +57,7 @@ static ssize_t testmode_write(struct file *file, const char __user *ubuf, size_t testmode = 0; spin_lock_irqsave(&hsotg->lock, flags); - s3c_hsotg_set_test_mode(hsotg, testmode); + dwc2_hsotg_set_test_mode(hsotg, testmode); spin_unlock_irqrestore(&hsotg->lock, flags); return count; } @@ -76,7 +76,7 @@ static int testmode_show(struct seq_file *s, void *unused) int dctl; spin_lock_irqsave(&hsotg->lock, flags); - dctl = readl(hsotg->regs + DCTL); + dctl = dwc2_readl(hsotg->regs + DCTL); dctl &= DCTL_TSTCTL_MASK; dctl >>= DCTL_TSTCTL_SHIFT; spin_unlock_irqrestore(&hsotg->lock, flags); @@ -137,38 +137,38 @@ static int state_show(struct seq_file *seq, void *v) int idx; seq_printf(seq, "DCFG=0x%08x, DCTL=0x%08x, DSTS=0x%08x\n", - readl(regs + DCFG), - readl(regs + DCTL), - readl(regs + DSTS)); + dwc2_readl(regs + DCFG), + dwc2_readl(regs + DCTL), + dwc2_readl(regs + DSTS)); seq_printf(seq, "DIEPMSK=0x%08x, DOEPMASK=0x%08x\n", - readl(regs + DIEPMSK), readl(regs + DOEPMSK)); + dwc2_readl(regs + DIEPMSK), dwc2_readl(regs + DOEPMSK)); seq_printf(seq, "GINTMSK=0x%08x, GINTSTS=0x%08x\n", - readl(regs + GINTMSK), - readl(regs + GINTSTS)); + dwc2_readl(regs + GINTMSK), + dwc2_readl(regs + GINTSTS)); seq_printf(seq, "DAINTMSK=0x%08x, DAINT=0x%08x\n", - readl(regs + DAINTMSK), - readl(regs + DAINT)); + dwc2_readl(regs + DAINTMSK), + dwc2_readl(regs + DAINT)); seq_printf(seq, "GNPTXSTS=0x%08x, GRXSTSR=%08x\n", - readl(regs + GNPTXSTS), - readl(regs + GRXSTSR)); + dwc2_readl(regs + GNPTXSTS), + dwc2_readl(regs + GRXSTSR)); seq_puts(seq, "\nEndpoint status:\n"); for (idx = 0; idx < hsotg->num_of_eps; idx++) { u32 in, out; - in = readl(regs + DIEPCTL(idx)); - out = readl(regs + DOEPCTL(idx)); + in = dwc2_readl(regs + DIEPCTL(idx)); + out = dwc2_readl(regs + DOEPCTL(idx)); seq_printf(seq, "ep%d: DIEPCTL=0x%08x, DOEPCTL=0x%08x", idx, in, out); - in = readl(regs + DIEPTSIZ(idx)); - out = readl(regs + DOEPTSIZ(idx)); + in = dwc2_readl(regs + DIEPTSIZ(idx)); + out = dwc2_readl(regs + DOEPTSIZ(idx)); seq_printf(seq, ", DIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x", in, out); @@ -208,9 +208,9 @@ static int fifo_show(struct seq_file *seq, void *v) int idx; seq_puts(seq, "Non-periodic FIFOs:\n"); - seq_printf(seq, "RXFIFO: Size %d\n", readl(regs + GRXFSIZ)); + seq_printf(seq, "RXFIFO: Size %d\n", dwc2_readl(regs + GRXFSIZ)); - val = readl(regs + GNPTXFSIZ); + val = dwc2_readl(regs + GNPTXFSIZ); seq_printf(seq, "NPTXFIFO: Size %d, Start 0x%08x\n", val >> FIFOSIZE_DEPTH_SHIFT, val & FIFOSIZE_DEPTH_MASK); @@ -218,7 +218,7 @@ static int fifo_show(struct seq_file *seq, void *v) seq_puts(seq, "\nPeriodic TXFIFOs:\n"); for (idx = 1; idx < hsotg->num_of_eps; idx++) { - val = readl(regs + DPTXFSIZN(idx)); + val = dwc2_readl(regs + DPTXFSIZN(idx)); seq_printf(seq, "\tDPTXFIFO%2d: Size %d, Start 0x%08x\n", idx, val >> FIFOSIZE_DEPTH_SHIFT, @@ -256,9 +256,9 @@ static const char *decode_direction(int is_in) */ static int ep_show(struct seq_file *seq, void *v) { - struct s3c_hsotg_ep *ep = seq->private; + struct dwc2_hsotg_ep *ep = seq->private; struct dwc2_hsotg *hsotg = ep->parent; - struct s3c_hsotg_req *req; + struct dwc2_hsotg_req *req; void __iomem *regs = hsotg->regs; int index = ep->index; int show_limit = 15; @@ -270,20 +270,20 @@ static int ep_show(struct seq_file *seq, void *v) /* first show the register state */ seq_printf(seq, "\tDIEPCTL=0x%08x, DOEPCTL=0x%08x\n", - readl(regs + DIEPCTL(index)), - readl(regs + DOEPCTL(index))); + dwc2_readl(regs + DIEPCTL(index)), + dwc2_readl(regs + DOEPCTL(index))); seq_printf(seq, "\tDIEPDMA=0x%08x, DOEPDMA=0x%08x\n", - readl(regs + DIEPDMA(index)), - readl(regs + DOEPDMA(index))); + dwc2_readl(regs + DIEPDMA(index)), + dwc2_readl(regs + DOEPDMA(index))); seq_printf(seq, "\tDIEPINT=0x%08x, DOEPINT=0x%08x\n", - readl(regs + DIEPINT(index)), - readl(regs + DOEPINT(index))); + dwc2_readl(regs + DIEPINT(index)), + dwc2_readl(regs + DOEPINT(index))); seq_printf(seq, "\tDIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x\n", - readl(regs + DIEPTSIZ(index)), - readl(regs + DOEPTSIZ(index))); + dwc2_readl(regs + DIEPTSIZ(index)), + dwc2_readl(regs + DOEPTSIZ(index))); seq_puts(seq, "\n"); seq_printf(seq, "mps %d\n", ep->ep.maxpacket); @@ -326,7 +326,7 @@ static const struct file_operations ep_fops = { }; /** - * s3c_hsotg_create_debug - create debugfs directory and files + * dwc2_hsotg_create_debug - create debugfs directory and files * @hsotg: The driver state * * Create the debugfs files to allow the user to get information @@ -334,7 +334,7 @@ static const struct file_operations ep_fops = { * with the same name as the device itself, in case we end up * with multiple blocks in future systems. */ -static void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg) +static void dwc2_hsotg_create_debug(struct dwc2_hsotg *hsotg) { struct dentry *root; struct dentry *file; @@ -360,7 +360,7 @@ static void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg) /* Create one file for each out endpoint */ for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) { - struct s3c_hsotg_ep *ep; + struct dwc2_hsotg_ep *ep; ep = hsotg->eps_out[epidx]; if (ep) { @@ -373,7 +373,7 @@ static void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg) } /* Create one file for each in endpoint. EP0 is handled with out eps */ for (epidx = 1; epidx < hsotg->num_of_eps; epidx++) { - struct s3c_hsotg_ep *ep; + struct dwc2_hsotg_ep *ep; ep = hsotg->eps_in[epidx]; if (ep) { @@ -386,10 +386,10 @@ static void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg) } } #else -static inline void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg) {} +static inline void dwc2_hsotg_create_debug(struct dwc2_hsotg *hsotg) {} #endif -/* s3c_hsotg_delete_debug is removed as cleanup in done in dwc2_debugfs_exit */ +/* dwc2_hsotg_delete_debug is removed as cleanup in done in dwc2_debugfs_exit */ #define dump_register(nm) \ { \ @@ -737,7 +737,7 @@ int dwc2_debugfs_init(struct dwc2_hsotg *hsotg) } /* Add gadget debugfs nodes */ - s3c_hsotg_create_debug(hsotg); + dwc2_hsotg_create_debug(hsotg); hsotg->regset = devm_kzalloc(hsotg->dev, sizeof(*hsotg->regset), GFP_KERNEL); diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 3ee5b4c..0abf73c 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -25,28 +25,24 @@ #include <linux/delay.h> #include <linux/io.h> #include <linux/slab.h> -#include <linux/clk.h> -#include <linux/regulator/consumer.h> #include <linux/of_platform.h> -#include <linux/phy/phy.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include <linux/usb/phy.h> -#include <linux/platform_data/s3c-hsotg.h> #include "core.h" #include "hw.h" /* conversion functions */ -static inline struct s3c_hsotg_req *our_req(struct usb_request *req) +static inline struct dwc2_hsotg_req *our_req(struct usb_request *req) { - return container_of(req, struct s3c_hsotg_req, req); + return container_of(req, struct dwc2_hsotg_req, req); } -static inline struct s3c_hsotg_ep *our_ep(struct usb_ep *ep) +static inline struct dwc2_hsotg_ep *our_ep(struct usb_ep *ep) { - return container_of(ep, struct s3c_hsotg_ep, ep); + return container_of(ep, struct dwc2_hsotg_ep, ep); } static inline struct dwc2_hsotg *to_hsotg(struct usb_gadget *gadget) @@ -56,15 +52,15 @@ static inline struct dwc2_hsotg *to_hsotg(struct usb_gadget *gadget) static inline void __orr32(void __iomem *ptr, u32 val) { - writel(readl(ptr) | val, ptr); + dwc2_writel(dwc2_readl(ptr) | val, ptr); } static inline void __bic32(void __iomem *ptr, u32 val) { - writel(readl(ptr) & ~val, ptr); + dwc2_writel(dwc2_readl(ptr) & ~val, ptr); } -static inline struct s3c_hsotg_ep *index_to_ep(struct dwc2_hsotg *hsotg, +static inline struct dwc2_hsotg_ep *index_to_ep(struct dwc2_hsotg *hsotg, u32 ep_index, u32 dir_in) { if (dir_in) @@ -74,7 +70,7 @@ static inline struct s3c_hsotg_ep *index_to_ep(struct dwc2_hsotg *hsotg, } /* forward declaration of functions */ -static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg); +static void dwc2_hsotg_dump(struct dwc2_hsotg *hsotg); /** * using_dma - return the DMA status of the driver. @@ -101,41 +97,41 @@ static inline bool using_dma(struct dwc2_hsotg *hsotg) } /** - * s3c_hsotg_en_gsint - enable one or more of the general interrupt + * dwc2_hsotg_en_gsint - enable one or more of the general interrupt * @hsotg: The device state * @ints: A bitmask of the interrupts to enable */ -static void s3c_hsotg_en_gsint(struct dwc2_hsotg *hsotg, u32 ints) +static void dwc2_hsotg_en_gsint(struct dwc2_hsotg *hsotg, u32 ints) { - u32 gsintmsk = readl(hsotg->regs + GINTMSK); + u32 gsintmsk = dwc2_readl(hsotg->regs + GINTMSK); u32 new_gsintmsk; new_gsintmsk = gsintmsk | ints; if (new_gsintmsk != gsintmsk) { dev_dbg(hsotg->dev, "gsintmsk now 0x%08x\n", new_gsintmsk); - writel(new_gsintmsk, hsotg->regs + GINTMSK); + dwc2_writel(new_gsintmsk, hsotg->regs + GINTMSK); } } /** - * s3c_hsotg_disable_gsint - disable one or more of the general interrupt + * dwc2_hsotg_disable_gsint - disable one or more of the general interrupt * @hsotg: The device state * @ints: A bitmask of the interrupts to enable */ -static void s3c_hsotg_disable_gsint(struct dwc2_hsotg *hsotg, u32 ints) +static void dwc2_hsotg_disable_gsint(struct dwc2_hsotg *hsotg, u32 ints) { - u32 gsintmsk = readl(hsotg->regs + GINTMSK); + u32 gsintmsk = dwc2_readl(hsotg->regs + GINTMSK); u32 new_gsintmsk; new_gsintmsk = gsintmsk & ~ints; if (new_gsintmsk != gsintmsk) - writel(new_gsintmsk, hsotg->regs + GINTMSK); + dwc2_writel(new_gsintmsk, hsotg->regs + GINTMSK); } /** - * s3c_hsotg_ctrl_epint - enable/disable an endpoint irq + * dwc2_hsotg_ctrl_epint - enable/disable an endpoint irq * @hsotg: The device state * @ep: The endpoint index * @dir_in: True if direction is in. @@ -144,7 +140,7 @@ static void s3c_hsotg_disable_gsint(struct dwc2_hsotg *hsotg, u32 ints) * Set or clear the mask for an individual endpoint's interrupt * request. */ -static void s3c_hsotg_ctrl_epint(struct dwc2_hsotg *hsotg, +static void dwc2_hsotg_ctrl_epint(struct dwc2_hsotg *hsotg, unsigned int ep, unsigned int dir_in, unsigned int en) { @@ -156,20 +152,20 @@ static void s3c_hsotg_ctrl_epint(struct dwc2_hsotg *hsotg, bit <<= 16; local_irq_save(flags); - daint = readl(hsotg->regs + DAINTMSK); + daint = dwc2_readl(hsotg->regs + DAINTMSK); if (en) daint |= bit; else daint &= ~bit; - writel(daint, hsotg->regs + DAINTMSK); + dwc2_writel(daint, hsotg->regs + DAINTMSK); local_irq_restore(flags); } /** - * s3c_hsotg_init_fifo - initialise non-periodic FIFOs + * dwc2_hsotg_init_fifo - initialise non-periodic FIFOs * @hsotg: The device instance. */ -static void s3c_hsotg_init_fifo(struct dwc2_hsotg *hsotg) +static void dwc2_hsotg_init_fifo(struct dwc2_hsotg *hsotg) { unsigned int ep; unsigned int addr; @@ -181,8 +177,8 @@ static void s3c_hsotg_init_fifo(struct dwc2_hsotg *hsotg) hsotg->fifo_map = 0; /* set RX/NPTX FIFO sizes */ - writel(hsotg->g_rx_fifo_sz, hsotg->regs + GRXFSIZ); - writel((hsotg->g_rx_fifo_sz << FIFOSIZE_STARTADDR_SHIFT) | + dwc2_writel(hsotg->g_rx_fifo_sz, hsotg->regs + GRXFSIZ); + dwc2_writel((hsotg->g_rx_fifo_sz << FIFOSIZE_STARTADDR_SHIFT) | (hsotg->g_np_g_tx_fifo_sz << FIFOSIZE_DEPTH_SHIFT), hsotg->regs + GNPTXFSIZ); @@ -210,7 +206,7 @@ static void s3c_hsotg_init_fifo(struct dwc2_hsotg *hsotg) "insufficient fifo memory"); addr += hsotg->g_tx_fifo_sz[ep]; - writel(val, hsotg->regs + DPTXFSIZN(ep)); + dwc2_writel(val, hsotg->regs + DPTXFSIZN(ep)); } /* @@ -218,13 +214,13 @@ static void s3c_hsotg_init_fifo(struct dwc2_hsotg *hsotg) * all fifos are flushed before continuing */ - writel(GRSTCTL_TXFNUM(0x10) | GRSTCTL_TXFFLSH | + dwc2_writel(GRSTCTL_TXFNUM(0x10) | GRSTCTL_TXFFLSH | GRSTCTL_RXFFLSH, hsotg->regs + GRSTCTL); /* wait until the fifos are both flushed */ timeout = 100; while (1) { - val = readl(hsotg->regs + GRSTCTL); + val = dwc2_readl(hsotg->regs + GRSTCTL); if ((val & (GRSTCTL_TXFFLSH | GRSTCTL_RXFFLSH)) == 0) break; @@ -248,12 +244,12 @@ static void s3c_hsotg_init_fifo(struct dwc2_hsotg *hsotg) * * Allocate a new USB request structure appropriate for the specified endpoint */ -static struct usb_request *s3c_hsotg_ep_alloc_request(struct usb_ep *ep, +static struct usb_request *dwc2_hsotg_ep_alloc_request(struct usb_ep *ep, gfp_t flags) { - struct s3c_hsotg_req *req; + struct dwc2_hsotg_req *req; - req = kzalloc(sizeof(struct s3c_hsotg_req), flags); + req = kzalloc(sizeof(struct dwc2_hsotg_req), flags); if (!req) return NULL; @@ -269,23 +265,23 @@ static struct usb_request *s3c_hsotg_ep_alloc_request(struct usb_ep *ep, * Returns true if the endpoint is in periodic mode, meaning it is being * used for an Interrupt or ISO transfer. */ -static inline int is_ep_periodic(struct s3c_hsotg_ep *hs_ep) +static inline int is_ep_periodic(struct dwc2_hsotg_ep *hs_ep) { return hs_ep->periodic; } /** - * s3c_hsotg_unmap_dma - unmap the DMA memory being used for the request + * dwc2_hsotg_unmap_dma - unmap the DMA memory being used for the request * @hsotg: The device state. * @hs_ep: The endpoint for the request * @hs_req: The request being processed. * - * This is the reverse of s3c_hsotg_map_dma(), called for the completion + * This is the reverse of dwc2_hsotg_map_dma(), called for the completion * of a request to ensure the buffer is ready for access by the caller. */ -static void s3c_hsotg_unmap_dma(struct dwc2_hsotg *hsotg, - struct s3c_hsotg_ep *hs_ep, - struct s3c_hsotg_req *hs_req) +static void dwc2_hsotg_unmap_dma(struct dwc2_hsotg *hsotg, + struct dwc2_hsotg_ep *hs_ep, + struct dwc2_hsotg_req *hs_req) { struct usb_request *req = &hs_req->req; @@ -297,7 +293,7 @@ static void s3c_hsotg_unmap_dma(struct dwc2_hsotg *hsotg, } /** - * s3c_hsotg_write_fifo - write packet Data to the TxFIFO + * dwc2_hsotg_write_fifo - write packet Data to the TxFIFO * @hsotg: The controller state. * @hs_ep: The endpoint we're going to write for. * @hs_req: The request to write data for. @@ -312,12 +308,12 @@ static void s3c_hsotg_unmap_dma(struct dwc2_hsotg *hsotg, * * This routine is only needed for PIO */ -static int s3c_hsotg_write_fifo(struct dwc2_hsotg *hsotg, - struct s3c_hsotg_ep *hs_ep, - struct s3c_hsotg_req *hs_req) +static int dwc2_hsotg_write_fifo(struct dwc2_hsotg *hsotg, + struct dwc2_hsotg_ep *hs_ep, + struct dwc2_hsotg_req *hs_req) { bool periodic = is_ep_periodic(hs_ep); - u32 gnptxsts = readl(hsotg->regs + GNPTXSTS); + u32 gnptxsts = dwc2_readl(hsotg->regs + GNPTXSTS); int buf_pos = hs_req->req.actual; int to_write = hs_ep->size_loaded; void *data; @@ -332,7 +328,7 @@ static int s3c_hsotg_write_fifo(struct dwc2_hsotg *hsotg, return 0; if (periodic && !hsotg->dedicated_fifos) { - u32 epsize = readl(hsotg->regs + DIEPTSIZ(hs_ep->index)); + u32 epsize = dwc2_readl(hsotg->regs + DIEPTSIZ(hs_ep->index)); int size_left; int size_done; @@ -348,7 +344,7 @@ static int s3c_hsotg_write_fifo(struct dwc2_hsotg *hsotg, * previous data has been completely sent. */ if (hs_ep->fifo_load != 0) { - s3c_hsotg_en_gsint(hsotg, GINTSTS_PTXFEMP); + dwc2_hsotg_en_gsint(hsotg, GINTSTS_PTXFEMP); return -ENOSPC; } @@ -369,11 +365,11 @@ static int s3c_hsotg_write_fifo(struct dwc2_hsotg *hsotg, __func__, can_write); if (can_write <= 0) { - s3c_hsotg_en_gsint(hsotg, GINTSTS_PTXFEMP); + dwc2_hsotg_en_gsint(hsotg, GINTSTS_PTXFEMP); return -ENOSPC; } } else if (hsotg->dedicated_fifos && hs_ep->index != 0) { - can_write = readl(hsotg->regs + DTXFSTS(hs_ep->index)); + can_write = dwc2_readl(hsotg->regs + DTXFSTS(hs_ep->index)); can_write &= 0xffff; can_write *= 4; @@ -383,7 +379,7 @@ static int s3c_hsotg_write_fifo(struct dwc2_hsotg *hsotg, "%s: no queue slots available (0x%08x)\n", __func__, gnptxsts); - s3c_hsotg_en_gsint(hsotg, GINTSTS_NPTXFEMP); + dwc2_hsotg_en_gsint(hsotg, GINTSTS_NPTXFEMP); return -ENOSPC; } @@ -414,7 +410,7 @@ static int s3c_hsotg_write_fifo(struct dwc2_hsotg *hsotg, /* it's needed only when we do not use dedicated fifos */ if (!hsotg->dedicated_fifos) - s3c_hsotg_en_gsint(hsotg, + dwc2_hsotg_en_gsint(hsotg, periodic ? GINTSTS_PTXFEMP : GINTSTS_NPTXFEMP); } @@ -443,7 +439,7 @@ static int s3c_hsotg_write_fifo(struct dwc2_hsotg *hsotg, /* it's needed only when we do not use dedicated fifos */ if (!hsotg->dedicated_fifos) - s3c_hsotg_en_gsint(hsotg, + dwc2_hsotg_en_gsint(hsotg, periodic ? GINTSTS_PTXFEMP : GINTSTS_NPTXFEMP); } @@ -475,7 +471,7 @@ static int s3c_hsotg_write_fifo(struct dwc2_hsotg *hsotg, * Return the maximum data that can be queued in one go on a given endpoint * so that transfers that are too long can be split. */ -static unsigned get_ep_limit(struct s3c_hsotg_ep *hs_ep) +static unsigned get_ep_limit(struct dwc2_hsotg_ep *hs_ep) { int index = hs_ep->index; unsigned maxsize; @@ -508,7 +504,7 @@ static unsigned get_ep_limit(struct s3c_hsotg_ep *hs_ep) } /** - * s3c_hsotg_start_req - start a USB request from an endpoint's queue + * dwc2_hsotg_start_req - start a USB request from an endpoint's queue * @hsotg: The controller state. * @hs_ep: The endpoint to process a request for * @hs_req: The request to start. @@ -517,9 +513,9 @@ static unsigned get_ep_limit(struct s3c_hsotg_ep *hs_ep) * Start the given request running by setting the endpoint registers * appropriately, and writing any data to the FIFOs. */ -static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg, - struct s3c_hsotg_ep *hs_ep, - struct s3c_hsotg_req *hs_req, +static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg, + struct dwc2_hsotg_ep *hs_ep, + struct dwc2_hsotg_req *hs_req, bool continuing) { struct usb_request *ureq = &hs_req->req; @@ -550,13 +546,13 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg, epsize_reg = dir_in ? DIEPTSIZ(index) : DOEPTSIZ(index); dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x, ep %d, dir %s\n", - __func__, readl(hsotg->regs + epctrl_reg), index, + __func__, dwc2_readl(hsotg->regs + epctrl_reg), index, hs_ep->dir_in ? "in" : "out"); /* If endpoint is stalled, we will restart request later */ - ctrl = readl(hsotg->regs + epctrl_reg); + ctrl = dwc2_readl(hsotg->regs + epctrl_reg); - if (ctrl & DXEPCTL_STALL) { + if (index && ctrl & DXEPCTL_STALL) { dev_warn(hsotg->dev, "%s: ep%d is stalled\n", __func__, index); return; } @@ -618,18 +614,18 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg, hs_ep->req = hs_req; /* write size / packets */ - writel(epsize, hsotg->regs + epsize_reg); + dwc2_writel(epsize, hsotg->regs + epsize_reg); if (using_dma(hsotg) && !continuing) { unsigned int dma_reg; /* * write DMA address to control register, buffer already - * synced by s3c_hsotg_ep_queue(). + * synced by dwc2_hsotg_ep_queue(). */ dma_reg = dir_in ? DIEPDMA(index) : DOEPDMA(index); - writel(ureq->dma, hsotg->regs + dma_reg); + dwc2_writel(ureq->dma, hsotg->regs + dma_reg); dev_dbg(hsotg->dev, "%s: %pad => 0x%08x\n", __func__, &ureq->dma, dma_reg); @@ -645,7 +641,7 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg, ctrl |= DXEPCTL_CNAK; /* clear NAK set by core */ dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", __func__, ctrl); - writel(ctrl, hsotg->regs + epctrl_reg); + dwc2_writel(ctrl, hsotg->regs + epctrl_reg); /* * set these, it seems that DMA support increments past the end @@ -659,7 +655,7 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg, /* set these anyway, we may need them for non-periodic in */ hs_ep->fifo_load = 0; - s3c_hsotg_write_fifo(hsotg, hs_ep, hs_req); + dwc2_hsotg_write_fifo(hsotg, hs_ep, hs_req); } /* @@ -667,7 +663,7 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg, * to debugging to see what is going on. */ if (dir_in) - writel(DIEPMSK_INTKNTXFEMPMSK, + dwc2_writel(DIEPMSK_INTKNTXFEMPMSK, hsotg->regs + DIEPINT(index)); /* @@ -676,20 +672,20 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg, */ /* check ep is enabled */ - if (!(readl(hsotg->regs + epctrl_reg) & DXEPCTL_EPENA)) + if (!(dwc2_readl(hsotg->regs + epctrl_reg) & DXEPCTL_EPENA)) dev_dbg(hsotg->dev, "ep%d: failed to become enabled (DXEPCTL=0x%08x)?\n", - index, readl(hsotg->regs + epctrl_reg)); + index, dwc2_readl(hsotg->regs + epctrl_reg)); dev_dbg(hsotg->dev, "%s: DXEPCTL=0x%08x\n", - __func__, readl(hsotg->regs + epctrl_reg)); + __func__, dwc2_readl(hsotg->regs + epctrl_reg)); /* enable ep interrupts */ - s3c_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 1); + dwc2_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 1); } /** - * s3c_hsotg_map_dma - map the DMA memory being used for the request + * dwc2_hsotg_map_dma - map the DMA memory being used for the request * @hsotg: The device state. * @hs_ep: The endpoint the request is on. * @req: The request being processed. @@ -700,11 +696,11 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg, * DMA memory, then we map the memory and mark our request to allow us to * cleanup on completion. */ -static int s3c_hsotg_map_dma(struct dwc2_hsotg *hsotg, - struct s3c_hsotg_ep *hs_ep, +static int dwc2_hsotg_map_dma(struct dwc2_hsotg *hsotg, + struct dwc2_hsotg_ep *hs_ep, struct usb_request *req) { - struct s3c_hsotg_req *hs_req = our_req(req); + struct dwc2_hsotg_req *hs_req = our_req(req); int ret; /* if the length is zero, ignore the DMA data */ @@ -724,8 +720,8 @@ dma_error: return -EIO; } -static int s3c_hsotg_handle_unaligned_buf_start(struct dwc2_hsotg *hsotg, - struct s3c_hsotg_ep *hs_ep, struct s3c_hsotg_req *hs_req) +static int dwc2_hsotg_handle_unaligned_buf_start(struct dwc2_hsotg *hsotg, + struct dwc2_hsotg_ep *hs_ep, struct dwc2_hsotg_req *hs_req) { void *req_buf = hs_req->req.buf; @@ -755,8 +751,8 @@ static int s3c_hsotg_handle_unaligned_buf_start(struct dwc2_hsotg *hsotg, return 0; } -static void s3c_hsotg_handle_unaligned_buf_complete(struct dwc2_hsotg *hsotg, - struct s3c_hsotg_ep *hs_ep, struct s3c_hsotg_req *hs_req) +static void dwc2_hsotg_handle_unaligned_buf_complete(struct dwc2_hsotg *hsotg, + struct dwc2_hsotg_ep *hs_ep, struct dwc2_hsotg_req *hs_req) { /* If dma is not being used or buffer was aligned */ if (!using_dma(hsotg) || !hs_req->saved_req_buf) @@ -777,11 +773,11 @@ static void s3c_hsotg_handle_unaligned_buf_complete(struct dwc2_hsotg *hsotg, hs_req->saved_req_buf = NULL; } -static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, +static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags) { - struct s3c_hsotg_req *hs_req = our_req(req); - struct s3c_hsotg_ep *hs_ep = our_ep(ep); + struct dwc2_hsotg_req *hs_req = our_req(req); + struct dwc2_hsotg_ep *hs_ep = our_ep(ep); struct dwc2_hsotg *hs = hs_ep->parent; bool first; int ret; @@ -802,13 +798,13 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, req->actual = 0; req->status = -EINPROGRESS; - ret = s3c_hsotg_handle_unaligned_buf_start(hs, hs_ep, hs_req); + ret = dwc2_hsotg_handle_unaligned_buf_start(hs, hs_ep, hs_req); if (ret) return ret; /* if we're using DMA, sync the buffers as necessary */ if (using_dma(hs)) { - ret = s3c_hsotg_map_dma(hs, hs_ep, req); + ret = dwc2_hsotg_map_dma(hs, hs_ep, req); if (ret) return ret; } @@ -817,51 +813,51 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, list_add_tail(&hs_req->queue, &hs_ep->queue); if (first) - s3c_hsotg_start_req(hs, hs_ep, hs_req, false); + dwc2_hsotg_start_req(hs, hs_ep, hs_req, false); return 0; } -static int s3c_hsotg_ep_queue_lock(struct usb_ep *ep, struct usb_request *req, +static int dwc2_hsotg_ep_queue_lock(struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags) { - struct s3c_hsotg_ep *hs_ep = our_ep(ep); + struct dwc2_hsotg_ep *hs_ep = our_ep(ep); struct dwc2_hsotg *hs = hs_ep->parent; unsigned long flags = 0; int ret = 0; spin_lock_irqsave(&hs->lock, flags); - ret = s3c_hsotg_ep_queue(ep, req, gfp_flags); + ret = dwc2_hsotg_ep_queue(ep, req, gfp_flags); spin_unlock_irqrestore(&hs->lock, flags); return ret; } -static void s3c_hsotg_ep_free_request(struct usb_ep *ep, +static void dwc2_hsotg_ep_free_request(struct usb_ep *ep, struct usb_request *req) { - struct s3c_hsotg_req *hs_req = our_req(req); + struct dwc2_hsotg_req *hs_req = our_req(req); kfree(hs_req); } /** - * s3c_hsotg_complete_oursetup - setup completion callback + * dwc2_hsotg_complete_oursetup - setup completion callback * @ep: The endpoint the request was on. * @req: The request completed. * * Called on completion of any requests the driver itself * submitted that need cleaning up. */ -static void s3c_hsotg_complete_oursetup(struct usb_ep *ep, +static void dwc2_hsotg_complete_oursetup(struct usb_ep *ep, struct usb_request *req) { - struct s3c_hsotg_ep *hs_ep = our_ep(ep); + struct dwc2_hsotg_ep *hs_ep = our_ep(ep); struct dwc2_hsotg *hsotg = hs_ep->parent; dev_dbg(hsotg->dev, "%s: ep %p, req %p\n", __func__, ep, req); - s3c_hsotg_ep_free_request(ep, req); + dwc2_hsotg_ep_free_request(ep, req); } /** @@ -872,10 +868,10 @@ static void s3c_hsotg_complete_oursetup(struct usb_ep *ep, * Convert the given wIndex into a pointer to an driver endpoint * structure, or return NULL if it is not a valid endpoint. */ -static struct s3c_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg, +static struct dwc2_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg, u32 windex) { - struct s3c_hsotg_ep *ep; + struct dwc2_hsotg_ep *ep; int dir = (windex & USB_DIR_IN) ? 1 : 0; int idx = windex & 0x7F; @@ -894,14 +890,14 @@ static struct s3c_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg, } /** - * s3c_hsotg_set_test_mode - Enable usb Test Modes + * dwc2_hsotg_set_test_mode - Enable usb Test Modes * @hsotg: The driver state. * @testmode: requested usb test mode * Enable usb Test Mode requested by the Host. */ -int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode) +int dwc2_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode) { - int dctl = readl(hsotg->regs + DCTL); + int dctl = dwc2_readl(hsotg->regs + DCTL); dctl &= ~DCTL_TSTCTL_MASK; switch (testmode) { @@ -915,12 +911,12 @@ int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode) default: return -EINVAL; } - writel(dctl, hsotg->regs + DCTL); + dwc2_writel(dctl, hsotg->regs + DCTL); return 0; } /** - * s3c_hsotg_send_reply - send reply to control request + * dwc2_hsotg_send_reply - send reply to control request * @hsotg: The device state * @ep: Endpoint 0 * @buff: Buffer for request @@ -929,8 +925,8 @@ int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode) * Create a request and queue it on the given endpoint. This is useful as * an internal method of sending replies to certain control requests, etc. */ -static int s3c_hsotg_send_reply(struct dwc2_hsotg *hsotg, - struct s3c_hsotg_ep *ep, +static int dwc2_hsotg_send_reply(struct dwc2_hsotg *hsotg, + struct dwc2_hsotg_ep *ep, void *buff, int length) { @@ -939,7 +935,7 @@ static int s3c_hsotg_send_reply(struct dwc2_hsotg *hsotg, dev_dbg(hsotg->dev, "%s: buff %p, len %d\n", __func__, buff, length); - req = s3c_hsotg_ep_alloc_request(&ep->ep, GFP_ATOMIC); + req = dwc2_hsotg_ep_alloc_request(&ep->ep, GFP_ATOMIC); hsotg->ep0_reply = req; if (!req) { dev_warn(hsotg->dev, "%s: cannot alloc req\n", __func__); @@ -953,12 +949,12 @@ static int s3c_hsotg_send_reply(struct dwc2_hsotg *hsotg, * STATUS stage. */ req->zero = 0; - req->complete = s3c_hsotg_complete_oursetup; + req->complete = dwc2_hsotg_complete_oursetup; if (length) memcpy(req->buf, buff, length); - ret = s3c_hsotg_ep_queue(&ep->ep, req, GFP_ATOMIC); + ret = dwc2_hsotg_ep_queue(&ep->ep, req, GFP_ATOMIC); if (ret) { dev_warn(hsotg->dev, "%s: cannot queue req\n", __func__); return ret; @@ -968,15 +964,15 @@ static int s3c_hsotg_send_reply(struct dwc2_hsotg *hsotg, } /** - * s3c_hsotg_process_req_status - process request GET_STATUS + * dwc2_hsotg_process_req_status - process request GET_STATUS * @hsotg: The device state * @ctrl: USB control request */ -static int s3c_hsotg_process_req_status(struct dwc2_hsotg *hsotg, +static int dwc2_hsotg_process_req_status(struct dwc2_hsotg *hsotg, struct usb_ctrlrequest *ctrl) { - struct s3c_hsotg_ep *ep0 = hsotg->eps_out[0]; - struct s3c_hsotg_ep *ep; + struct dwc2_hsotg_ep *ep0 = hsotg->eps_out[0]; + struct dwc2_hsotg_ep *ep; __le16 reply; int ret; @@ -1013,7 +1009,7 @@ static int s3c_hsotg_process_req_status(struct dwc2_hsotg *hsotg, if (le16_to_cpu(ctrl->wLength) != 2) return -EINVAL; - ret = s3c_hsotg_send_reply(hsotg, ep0, &reply, 2); + ret = dwc2_hsotg_send_reply(hsotg, ep0, &reply, 2); if (ret) { dev_err(hsotg->dev, "%s: failed to send reply\n", __func__); return ret; @@ -1022,7 +1018,7 @@ static int s3c_hsotg_process_req_status(struct dwc2_hsotg *hsotg, return 1; } -static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value); +static int dwc2_hsotg_ep_sethalt(struct usb_ep *ep, int value); /** * get_ep_head - return the first request on the endpoint @@ -1030,27 +1026,27 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value); * * Get the first request on the endpoint. */ -static struct s3c_hsotg_req *get_ep_head(struct s3c_hsotg_ep *hs_ep) +static struct dwc2_hsotg_req *get_ep_head(struct dwc2_hsotg_ep *hs_ep) { if (list_empty(&hs_ep->queue)) return NULL; - return list_first_entry(&hs_ep->queue, struct s3c_hsotg_req, queue); + return list_first_entry(&hs_ep->queue, struct dwc2_hsotg_req, queue); } /** - * s3c_hsotg_process_req_feature - process request {SET,CLEAR}_FEATURE + * dwc2_hsotg_process_req_feature - process request {SET,CLEAR}_FEATURE * @hsotg: The device state * @ctrl: USB control request */ -static int s3c_hsotg_process_req_feature(struct dwc2_hsotg *hsotg, +static int dwc2_hsotg_process_req_feature(struct dwc2_hsotg *hsotg, struct usb_ctrlrequest *ctrl) { - struct s3c_hsotg_ep *ep0 = hsotg->eps_out[0]; - struct s3c_hsotg_req *hs_req; + struct dwc2_hsotg_ep *ep0 = hsotg->eps_out[0]; + struct dwc2_hsotg_req *hs_req; bool restart; bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE); - struct s3c_hsotg_ep *ep; + struct dwc2_hsotg_ep *ep; int ret; bool halted; u32 recip; @@ -1074,7 +1070,7 @@ static int s3c_hsotg_process_req_feature(struct dwc2_hsotg *hsotg, return -EINVAL; hsotg->test_mode = wIndex >> 8; - ret = s3c_hsotg_send_reply(hsotg, ep0, NULL, 0); + ret = dwc2_hsotg_send_reply(hsotg, ep0, NULL, 0); if (ret) { dev_err(hsotg->dev, "%s: failed to send reply\n", __func__); @@ -1098,9 +1094,9 @@ static int s3c_hsotg_process_req_feature(struct dwc2_hsotg *hsotg, case USB_ENDPOINT_HALT: halted = ep->halted; - s3c_hsotg_ep_sethalt(&ep->ep, set); + dwc2_hsotg_ep_sethalt(&ep->ep, set); - ret = s3c_hsotg_send_reply(hsotg, ep0, NULL, 0); + ret = dwc2_hsotg_send_reply(hsotg, ep0, NULL, 0); if (ret) { dev_err(hsotg->dev, "%s: failed to send reply\n", __func__); @@ -1134,7 +1130,7 @@ static int s3c_hsotg_process_req_feature(struct dwc2_hsotg *hsotg, restart = !list_empty(&ep->queue); if (restart) { hs_req = get_ep_head(ep); - s3c_hsotg_start_req(hsotg, ep, + dwc2_hsotg_start_req(hsotg, ep, hs_req, false); } } @@ -1152,17 +1148,17 @@ static int s3c_hsotg_process_req_feature(struct dwc2_hsotg *hsotg, return 1; } -static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg); +static void dwc2_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg); /** - * s3c_hsotg_stall_ep0 - stall ep0 + * dwc2_hsotg_stall_ep0 - stall ep0 * @hsotg: The device state * * Set stall for ep0 as response for setup request. */ -static void s3c_hsotg_stall_ep0(struct dwc2_hsotg *hsotg) +static void dwc2_hsotg_stall_ep0(struct dwc2_hsotg *hsotg) { - struct s3c_hsotg_ep *ep0 = hsotg->eps_out[0]; + struct dwc2_hsotg_ep *ep0 = hsotg->eps_out[0]; u32 reg; u32 ctrl; @@ -1174,24 +1170,24 @@ static void s3c_hsotg_stall_ep0(struct dwc2_hsotg *hsotg) * taken effect, so no need to clear later. */ - ctrl = readl(hsotg->regs + reg); + ctrl = dwc2_readl(hsotg->regs + reg); ctrl |= DXEPCTL_STALL; ctrl |= DXEPCTL_CNAK; - writel(ctrl, hsotg->regs + reg); + dwc2_writel(ctrl, hsotg->regs + reg); dev_dbg(hsotg->dev, "written DXEPCTL=0x%08x to %08x (DXEPCTL=0x%08x)\n", - ctrl, reg, readl(hsotg->regs + reg)); + ctrl, reg, dwc2_readl(hsotg->regs + reg)); /* * complete won't be called, so we enqueue * setup request here */ - s3c_hsotg_enqueue_setup(hsotg); + dwc2_hsotg_enqueue_setup(hsotg); } /** - * s3c_hsotg_process_control - process a control request + * dwc2_hsotg_process_control - process a control request * @hsotg: The device state * @ctrl: The control request received * @@ -1199,16 +1195,17 @@ static void s3c_hsotg_stall_ep0(struct dwc2_hsotg *hsotg) * needs to work out what to do next (and whether to pass it on to the * gadget driver). */ -static void s3c_hsotg_process_control(struct dwc2_hsotg *hsotg, +static void dwc2_hsotg_process_control(struct dwc2_hsotg *hsotg, struct usb_ctrlrequest *ctrl) { - struct s3c_hsotg_ep *ep0 = hsotg->eps_out[0]; + struct dwc2_hsotg_ep *ep0 = hsotg->eps_out[0]; int ret = 0; u32 dcfg; - dev_dbg(hsotg->dev, "ctrl Req=%02x, Type=%02x, V=%04x, L=%04x\n", - ctrl->bRequest, ctrl->bRequestType, - ctrl->wValue, ctrl->wLength); + dev_dbg(hsotg->dev, + "ctrl Type=%02x, Req=%02x, V=%04x, I=%04x, L=%04x\n", + ctrl->bRequestType, ctrl->bRequest, ctrl->wValue, + ctrl->wIndex, ctrl->wLength); if (ctrl->wLength == 0) { ep0->dir_in = 1; @@ -1225,24 +1222,24 @@ static void s3c_hsotg_process_control(struct dwc2_hsotg *hsotg, switch (ctrl->bRequest) { case USB_REQ_SET_ADDRESS: hsotg->connected = 1; - dcfg = readl(hsotg->regs + DCFG); + dcfg = dwc2_readl(hsotg->regs + DCFG); dcfg &= ~DCFG_DEVADDR_MASK; dcfg |= (le16_to_cpu(ctrl->wValue) << DCFG_DEVADDR_SHIFT) & DCFG_DEVADDR_MASK; - writel(dcfg, hsotg->regs + DCFG); + dwc2_writel(dcfg, hsotg->regs + DCFG); dev_info(hsotg->dev, "new address %d\n", ctrl->wValue); - ret = s3c_hsotg_send_reply(hsotg, ep0, NULL, 0); + ret = dwc2_hsotg_send_reply(hsotg, ep0, NULL, 0); return; case USB_REQ_GET_STATUS: - ret = s3c_hsotg_process_req_status(hsotg, ctrl); + ret = dwc2_hsotg_process_req_status(hsotg, ctrl); break; case USB_REQ_CLEAR_FEATURE: case USB_REQ_SET_FEATURE: - ret = s3c_hsotg_process_req_feature(hsotg, ctrl); + ret = dwc2_hsotg_process_req_feature(hsotg, ctrl); break; } } @@ -1263,21 +1260,21 @@ static void s3c_hsotg_process_control(struct dwc2_hsotg *hsotg, */ if (ret < 0) - s3c_hsotg_stall_ep0(hsotg); + dwc2_hsotg_stall_ep0(hsotg); } /** - * s3c_hsotg_complete_setup - completion of a setup transfer + * dwc2_hsotg_complete_setup - completion of a setup transfer * @ep: The endpoint the request was on. * @req: The request completed. * * Called on completion of any requests the driver itself submitted for * EP0 setup packets */ -static void s3c_hsotg_complete_setup(struct usb_ep *ep, +static void dwc2_hsotg_complete_setup(struct usb_ep *ep, struct usb_request *req) { - struct s3c_hsotg_ep *hs_ep = our_ep(ep); + struct dwc2_hsotg_ep *hs_ep = our_ep(ep); struct dwc2_hsotg *hsotg = hs_ep->parent; if (req->status < 0) { @@ -1287,23 +1284,23 @@ static void s3c_hsotg_complete_setup(struct usb_ep *ep, spin_lock(&hsotg->lock); if (req->actual == 0) - s3c_hsotg_enqueue_setup(hsotg); + dwc2_hsotg_enqueue_setup(hsotg); else - s3c_hsotg_process_control(hsotg, req->buf); + dwc2_hsotg_process_control(hsotg, req->buf); spin_unlock(&hsotg->lock); } /** - * s3c_hsotg_enqueue_setup - start a request for EP0 packets + * dwc2_hsotg_enqueue_setup - start a request for EP0 packets * @hsotg: The device state. * * Enqueue a request on EP0 if necessary to received any SETUP packets * received from the host. */ -static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg) +static void dwc2_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg) { struct usb_request *req = hsotg->ctrl_req; - struct s3c_hsotg_req *hs_req = our_req(req); + struct dwc2_hsotg_req *hs_req = our_req(req); int ret; dev_dbg(hsotg->dev, "%s: queueing setup request\n", __func__); @@ -1311,7 +1308,7 @@ static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg) req->zero = 0; req->length = 8; req->buf = hsotg->ctrl_buff; - req->complete = s3c_hsotg_complete_setup; + req->complete = dwc2_hsotg_complete_setup; if (!list_empty(&hs_req->queue)) { dev_dbg(hsotg->dev, "%s already queued???\n", __func__); @@ -1322,7 +1319,7 @@ static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg) hsotg->eps_out[0]->send_zlp = 0; hsotg->ep0_state = DWC2_EP0_SETUP; - ret = s3c_hsotg_ep_queue(&hsotg->eps_out[0]->ep, req, GFP_ATOMIC); + ret = dwc2_hsotg_ep_queue(&hsotg->eps_out[0]->ep, req, GFP_ATOMIC); if (ret < 0) { dev_err(hsotg->dev, "%s: failed queue (%d)\n", __func__, ret); /* @@ -1332,8 +1329,8 @@ static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg) } } -static void s3c_hsotg_program_zlp(struct dwc2_hsotg *hsotg, - struct s3c_hsotg_ep *hs_ep) +static void dwc2_hsotg_program_zlp(struct dwc2_hsotg *hsotg, + struct dwc2_hsotg_ep *hs_ep) { u32 ctrl; u8 index = hs_ep->index; @@ -1347,19 +1344,19 @@ static void s3c_hsotg_program_zlp(struct dwc2_hsotg *hsotg, dev_dbg(hsotg->dev, "Receiving zero-length packet on ep%d\n", index); - writel(DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) | - DXEPTSIZ_XFERSIZE(0), hsotg->regs + - epsiz_reg); + dwc2_writel(DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) | + DXEPTSIZ_XFERSIZE(0), hsotg->regs + + epsiz_reg); - ctrl = readl(hsotg->regs + epctl_reg); + ctrl = dwc2_readl(hsotg->regs + epctl_reg); ctrl |= DXEPCTL_CNAK; /* clear NAK set by core */ ctrl |= DXEPCTL_EPENA; /* ensure ep enabled */ ctrl |= DXEPCTL_USBACTEP; - writel(ctrl, hsotg->regs + epctl_reg); + dwc2_writel(ctrl, hsotg->regs + epctl_reg); } /** - * s3c_hsotg_complete_request - complete a request given to us + * dwc2_hsotg_complete_request - complete a request given to us * @hsotg: The device state. * @hs_ep: The endpoint the request was on. * @hs_req: The request to complete. @@ -1371,9 +1368,9 @@ static void s3c_hsotg_program_zlp(struct dwc2_hsotg *hsotg, * * Note, expects the ep to already be locked as appropriate. */ -static void s3c_hsotg_complete_request(struct dwc2_hsotg *hsotg, - struct s3c_hsotg_ep *hs_ep, - struct s3c_hsotg_req *hs_req, +static void dwc2_hsotg_complete_request(struct dwc2_hsotg *hsotg, + struct dwc2_hsotg_ep *hs_ep, + struct dwc2_hsotg_req *hs_req, int result) { bool restart; @@ -1394,14 +1391,14 @@ static void s3c_hsotg_complete_request(struct dwc2_hsotg *hsotg, if (hs_req->req.status == -EINPROGRESS) hs_req->req.status = result; - s3c_hsotg_handle_unaligned_buf_complete(hsotg, hs_ep, hs_req); + if (using_dma(hsotg)) + dwc2_hsotg_unmap_dma(hsotg, hs_ep, hs_req); + + dwc2_hsotg_handle_unaligned_buf_complete(hsotg, hs_ep, hs_req); hs_ep->req = NULL; list_del_init(&hs_req->queue); - if (using_dma(hsotg)) - s3c_hsotg_unmap_dma(hsotg, hs_ep, hs_req); - /* * call the complete request with the locks off, just in case the * request tries to queue more work for this endpoint. @@ -1423,13 +1420,13 @@ static void s3c_hsotg_complete_request(struct dwc2_hsotg *hsotg, restart = !list_empty(&hs_ep->queue); if (restart) { hs_req = get_ep_head(hs_ep); - s3c_hsotg_start_req(hsotg, hs_ep, hs_req, false); + dwc2_hsotg_start_req(hsotg, hs_ep, hs_req, false); } } } /** - * s3c_hsotg_rx_data - receive data from the FIFO for an endpoint + * dwc2_hsotg_rx_data - receive data from the FIFO for an endpoint * @hsotg: The device state. * @ep_idx: The endpoint index for the data * @size: The size of data in the fifo, in bytes @@ -1438,10 +1435,10 @@ static void s3c_hsotg_complete_request(struct dwc2_hsotg *hsotg, * endpoint, so sort out whether we need to read the data into a request * that has been made for that endpoint. */ -static void s3c_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size) +static void dwc2_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size) { - struct s3c_hsotg_ep *hs_ep = hsotg->eps_out[ep_idx]; - struct s3c_hsotg_req *hs_req = hs_ep->req; + struct dwc2_hsotg_ep *hs_ep = hsotg->eps_out[ep_idx]; + struct dwc2_hsotg_req *hs_req = hs_ep->req; void __iomem *fifo = hsotg->regs + EPFIFO(ep_idx); int to_read; int max_req; @@ -1449,7 +1446,7 @@ static void s3c_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size) if (!hs_req) { - u32 epctl = readl(hsotg->regs + DOEPCTL(ep_idx)); + u32 epctl = dwc2_readl(hsotg->regs + DOEPCTL(ep_idx)); int ptr; dev_dbg(hsotg->dev, @@ -1458,7 +1455,7 @@ static void s3c_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size) /* dump the data from the FIFO, we've nothing we can do */ for (ptr = 0; ptr < size; ptr += 4) - (void)readl(fifo); + (void)dwc2_readl(fifo); return; } @@ -1492,7 +1489,7 @@ static void s3c_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size) } /** - * s3c_hsotg_ep0_zlp - send/receive zero-length packet on control endpoint + * dwc2_hsotg_ep0_zlp - send/receive zero-length packet on control endpoint * @hsotg: The device instance * @dir_in: If IN zlp * @@ -1503,17 +1500,30 @@ static void s3c_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size) * currently believed that we do not need to wait for any space in * the TxFIFO. */ -static void s3c_hsotg_ep0_zlp(struct dwc2_hsotg *hsotg, bool dir_in) +static void dwc2_hsotg_ep0_zlp(struct dwc2_hsotg *hsotg, bool dir_in) { /* eps_out[0] is used in both directions */ hsotg->eps_out[0]->dir_in = dir_in; hsotg->ep0_state = dir_in ? DWC2_EP0_STATUS_IN : DWC2_EP0_STATUS_OUT; - s3c_hsotg_program_zlp(hsotg, hsotg->eps_out[0]); + dwc2_hsotg_program_zlp(hsotg, hsotg->eps_out[0]); +} + +static void dwc2_hsotg_change_ep_iso_parity(struct dwc2_hsotg *hsotg, + u32 epctl_reg) +{ + u32 ctrl; + + ctrl = dwc2_readl(hsotg->regs + epctl_reg); + if (ctrl & DXEPCTL_EOFRNUM) + ctrl |= DXEPCTL_SETEVENFR; + else + ctrl |= DXEPCTL_SETODDFR; + dwc2_writel(ctrl, hsotg->regs + epctl_reg); } /** - * s3c_hsotg_handle_outdone - handle receiving OutDone/SetupDone from RXFIFO + * dwc2_hsotg_handle_outdone - handle receiving OutDone/SetupDone from RXFIFO * @hsotg: The device instance * @epnum: The endpoint received from * @@ -1521,11 +1531,11 @@ static void s3c_hsotg_ep0_zlp(struct dwc2_hsotg *hsotg, bool dir_in) * transfer for an OUT endpoint has been completed, either by a short * packet or by the finish of a transfer. */ -static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum) +static void dwc2_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum) { - u32 epsize = readl(hsotg->regs + DOEPTSIZ(epnum)); - struct s3c_hsotg_ep *hs_ep = hsotg->eps_out[epnum]; - struct s3c_hsotg_req *hs_req = hs_ep->req; + u32 epsize = dwc2_readl(hsotg->regs + DOEPTSIZ(epnum)); + struct dwc2_hsotg_ep *hs_ep = hsotg->eps_out[epnum]; + struct dwc2_hsotg_req *hs_req = hs_ep->req; struct usb_request *req = &hs_req->req; unsigned size_left = DXEPTSIZ_XFERSIZE_GET(epsize); int result = 0; @@ -1537,8 +1547,8 @@ static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum) if (epnum == 0 && hsotg->ep0_state == DWC2_EP0_STATUS_OUT) { dev_dbg(hsotg->dev, "zlp packet received\n"); - s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0); - s3c_hsotg_enqueue_setup(hsotg); + dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, 0); + dwc2_hsotg_enqueue_setup(hsotg); return; } @@ -1562,7 +1572,7 @@ static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum) /* if there is more request to do, schedule new transfer */ if (req->actual < req->length && size_left == 0) { - s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true); + dwc2_hsotg_start_req(hsotg, hs_ep, hs_req, true); return; } @@ -1578,24 +1588,34 @@ static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum) if (epnum == 0 && hsotg->ep0_state == DWC2_EP0_DATA_OUT) { /* Move to STATUS IN */ - s3c_hsotg_ep0_zlp(hsotg, true); + dwc2_hsotg_ep0_zlp(hsotg, true); return; } - s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, result); + /* + * Slave mode OUT transfers do not go through XferComplete so + * adjust the ISOC parity here. + */ + if (!using_dma(hsotg)) { + hs_ep->has_correct_parity = 1; + if (hs_ep->isochronous && hs_ep->interval == 1) + dwc2_hsotg_change_ep_iso_parity(hsotg, DOEPCTL(epnum)); + } + + dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, result); } /** - * s3c_hsotg_read_frameno - read current frame number + * dwc2_hsotg_read_frameno - read current frame number * @hsotg: The device instance * * Return the current frame number */ -static u32 s3c_hsotg_read_frameno(struct dwc2_hsotg *hsotg) +static u32 dwc2_hsotg_read_frameno(struct dwc2_hsotg *hsotg) { u32 dsts; - dsts = readl(hsotg->regs + DSTS); + dsts = dwc2_readl(hsotg->regs + DSTS); dsts &= DSTS_SOFFN_MASK; dsts >>= DSTS_SOFFN_SHIFT; @@ -1603,7 +1623,7 @@ static u32 s3c_hsotg_read_frameno(struct dwc2_hsotg *hsotg) } /** - * s3c_hsotg_handle_rx - RX FIFO has data + * dwc2_hsotg_handle_rx - RX FIFO has data * @hsotg: The device instance * * The IRQ handler has detected that the RX FIFO has some data in it @@ -1618,9 +1638,9 @@ static u32 s3c_hsotg_read_frameno(struct dwc2_hsotg *hsotg) * as the actual data should be sent to the memory directly and we turn * on the completion interrupts to get notifications of transfer completion. */ -static void s3c_hsotg_handle_rx(struct dwc2_hsotg *hsotg) +static void dwc2_hsotg_handle_rx(struct dwc2_hsotg *hsotg) { - u32 grxstsr = readl(hsotg->regs + GRXSTSP); + u32 grxstsr = dwc2_readl(hsotg->regs + GRXSTSP); u32 epnum, status, size; WARN_ON(using_dma(hsotg)); @@ -1641,55 +1661,55 @@ static void s3c_hsotg_handle_rx(struct dwc2_hsotg *hsotg) case GRXSTS_PKTSTS_OUTDONE: dev_dbg(hsotg->dev, "OutDone (Frame=0x%08x)\n", - s3c_hsotg_read_frameno(hsotg)); + dwc2_hsotg_read_frameno(hsotg)); if (!using_dma(hsotg)) - s3c_hsotg_handle_outdone(hsotg, epnum); + dwc2_hsotg_handle_outdone(hsotg, epnum); break; case GRXSTS_PKTSTS_SETUPDONE: dev_dbg(hsotg->dev, "SetupDone (Frame=0x%08x, DOPEPCTL=0x%08x)\n", - s3c_hsotg_read_frameno(hsotg), - readl(hsotg->regs + DOEPCTL(0))); + dwc2_hsotg_read_frameno(hsotg), + dwc2_readl(hsotg->regs + DOEPCTL(0))); /* - * Call s3c_hsotg_handle_outdone here if it was not called from + * Call dwc2_hsotg_handle_outdone here if it was not called from * GRXSTS_PKTSTS_OUTDONE. That is, if the core didn't * generate GRXSTS_PKTSTS_OUTDONE for setup packet. */ if (hsotg->ep0_state == DWC2_EP0_SETUP) - s3c_hsotg_handle_outdone(hsotg, epnum); + dwc2_hsotg_handle_outdone(hsotg, epnum); break; case GRXSTS_PKTSTS_OUTRX: - s3c_hsotg_rx_data(hsotg, epnum, size); + dwc2_hsotg_rx_data(hsotg, epnum, size); break; case GRXSTS_PKTSTS_SETUPRX: dev_dbg(hsotg->dev, "SetupRX (Frame=0x%08x, DOPEPCTL=0x%08x)\n", - s3c_hsotg_read_frameno(hsotg), - readl(hsotg->regs + DOEPCTL(0))); + dwc2_hsotg_read_frameno(hsotg), + dwc2_readl(hsotg->regs + DOEPCTL(0))); WARN_ON(hsotg->ep0_state != DWC2_EP0_SETUP); - s3c_hsotg_rx_data(hsotg, epnum, size); + dwc2_hsotg_rx_data(hsotg, epnum, size); break; default: dev_warn(hsotg->dev, "%s: unknown status %08x\n", __func__, grxstsr); - s3c_hsotg_dump(hsotg); + dwc2_hsotg_dump(hsotg); break; } } /** - * s3c_hsotg_ep0_mps - turn max packet size into register setting + * dwc2_hsotg_ep0_mps - turn max packet size into register setting * @mps: The maximum packet size in bytes. */ -static u32 s3c_hsotg_ep0_mps(unsigned int mps) +static u32 dwc2_hsotg_ep0_mps(unsigned int mps) { switch (mps) { case 64: @@ -1708,7 +1728,7 @@ static u32 s3c_hsotg_ep0_mps(unsigned int mps) } /** - * s3c_hsotg_set_ep_maxpacket - set endpoint's max-packet field + * dwc2_hsotg_set_ep_maxpacket - set endpoint's max-packet field * @hsotg: The driver state. * @ep: The index number of the endpoint * @mps: The maximum packet size in bytes @@ -1716,10 +1736,10 @@ static u32 s3c_hsotg_ep0_mps(unsigned int mps) * Configure the maximum packet size for the given endpoint, updating * the hardware control registers to reflect this. */ -static void s3c_hsotg_set_ep_maxpacket(struct dwc2_hsotg *hsotg, +static void dwc2_hsotg_set_ep_maxpacket(struct dwc2_hsotg *hsotg, unsigned int ep, unsigned int mps, unsigned int dir_in) { - struct s3c_hsotg_ep *hs_ep; + struct dwc2_hsotg_ep *hs_ep; void __iomem *regs = hsotg->regs; u32 mpsval; u32 mcval; @@ -1731,7 +1751,7 @@ static void s3c_hsotg_set_ep_maxpacket(struct dwc2_hsotg *hsotg, if (ep == 0) { /* EP0 is a special case */ - mpsval = s3c_hsotg_ep0_mps(mps); + mpsval = dwc2_hsotg_ep0_mps(mps); if (mpsval > 3) goto bad_mps; hs_ep->ep.maxpacket = mps; @@ -1748,15 +1768,15 @@ static void s3c_hsotg_set_ep_maxpacket(struct dwc2_hsotg *hsotg, } if (dir_in) { - reg = readl(regs + DIEPCTL(ep)); + reg = dwc2_readl(regs + DIEPCTL(ep)); reg &= ~DXEPCTL_MPS_MASK; reg |= mpsval; - writel(reg, regs + DIEPCTL(ep)); + dwc2_writel(reg, regs + DIEPCTL(ep)); } else { - reg = readl(regs + DOEPCTL(ep)); + reg = dwc2_readl(regs + DOEPCTL(ep)); reg &= ~DXEPCTL_MPS_MASK; reg |= mpsval; - writel(reg, regs + DOEPCTL(ep)); + dwc2_writel(reg, regs + DOEPCTL(ep)); } return; @@ -1766,23 +1786,23 @@ bad_mps: } /** - * s3c_hsotg_txfifo_flush - flush Tx FIFO + * dwc2_hsotg_txfifo_flush - flush Tx FIFO * @hsotg: The driver state * @idx: The index for the endpoint (0..15) */ -static void s3c_hsotg_txfifo_flush(struct dwc2_hsotg *hsotg, unsigned int idx) +static void dwc2_hsotg_txfifo_flush(struct dwc2_hsotg *hsotg, unsigned int idx) { int timeout; int val; - writel(GRSTCTL_TXFNUM(idx) | GRSTCTL_TXFFLSH, - hsotg->regs + GRSTCTL); + dwc2_writel(GRSTCTL_TXFNUM(idx) | GRSTCTL_TXFFLSH, + hsotg->regs + GRSTCTL); /* wait until the fifo is flushed */ timeout = 100; while (1) { - val = readl(hsotg->regs + GRSTCTL); + val = dwc2_readl(hsotg->regs + GRSTCTL); if ((val & (GRSTCTL_TXFFLSH)) == 0) break; @@ -1799,17 +1819,17 @@ static void s3c_hsotg_txfifo_flush(struct dwc2_hsotg *hsotg, unsigned int idx) } /** - * s3c_hsotg_trytx - check to see if anything needs transmitting + * dwc2_hsotg_trytx - check to see if anything needs transmitting * @hsotg: The driver state * @hs_ep: The driver endpoint to check. * * Check to see if there is a request that has data to send, and if so * make an attempt to write data into the FIFO. */ -static int s3c_hsotg_trytx(struct dwc2_hsotg *hsotg, - struct s3c_hsotg_ep *hs_ep) +static int dwc2_hsotg_trytx(struct dwc2_hsotg *hsotg, + struct dwc2_hsotg_ep *hs_ep) { - struct s3c_hsotg_req *hs_req = hs_ep->req; + struct dwc2_hsotg_req *hs_req = hs_ep->req; if (!hs_ep->dir_in || !hs_req) { /** @@ -1817,7 +1837,7 @@ static int s3c_hsotg_trytx(struct dwc2_hsotg *hsotg, * for endpoints, excepting ep0 */ if (hs_ep->index != 0) - s3c_hsotg_ctrl_epint(hsotg, hs_ep->index, + dwc2_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 0); return 0; } @@ -1825,25 +1845,25 @@ static int s3c_hsotg_trytx(struct dwc2_hsotg *hsotg, if (hs_req->req.actual < hs_req->req.length) { dev_dbg(hsotg->dev, "trying to write more for ep%d\n", hs_ep->index); - return s3c_hsotg_write_fifo(hsotg, hs_ep, hs_req); + return dwc2_hsotg_write_fifo(hsotg, hs_ep, hs_req); } return 0; } /** - * s3c_hsotg_complete_in - complete IN transfer + * dwc2_hsotg_complete_in - complete IN transfer * @hsotg: The device state. * @hs_ep: The endpoint that has just completed. * * An IN transfer has been completed, update the transfer's state and then * call the relevant completion routines. */ -static void s3c_hsotg_complete_in(struct dwc2_hsotg *hsotg, - struct s3c_hsotg_ep *hs_ep) +static void dwc2_hsotg_complete_in(struct dwc2_hsotg *hsotg, + struct dwc2_hsotg_ep *hs_ep) { - struct s3c_hsotg_req *hs_req = hs_ep->req; - u32 epsize = readl(hsotg->regs + DIEPTSIZ(hs_ep->index)); + struct dwc2_hsotg_req *hs_req = hs_ep->req; + u32 epsize = dwc2_readl(hsotg->regs + DIEPTSIZ(hs_ep->index)); int size_left, size_done; if (!hs_req) { @@ -1854,19 +1874,19 @@ static void s3c_hsotg_complete_in(struct dwc2_hsotg *hsotg, /* Finish ZLP handling for IN EP0 transactions */ if (hs_ep->index == 0 && hsotg->ep0_state == DWC2_EP0_STATUS_IN) { dev_dbg(hsotg->dev, "zlp packet sent\n"); - s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0); + dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, 0); if (hsotg->test_mode) { int ret; - ret = s3c_hsotg_set_test_mode(hsotg, hsotg->test_mode); + ret = dwc2_hsotg_set_test_mode(hsotg, hsotg->test_mode); if (ret < 0) { dev_dbg(hsotg->dev, "Invalid Test #%d\n", hsotg->test_mode); - s3c_hsotg_stall_ep0(hsotg); + dwc2_hsotg_stall_ep0(hsotg); return; } } - s3c_hsotg_enqueue_setup(hsotg); + dwc2_hsotg_enqueue_setup(hsotg); return; } @@ -1895,13 +1915,13 @@ static void s3c_hsotg_complete_in(struct dwc2_hsotg *hsotg, if (!size_left && hs_req->req.actual < hs_req->req.length) { dev_dbg(hsotg->dev, "%s trying more for req...\n", __func__); - s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true); + dwc2_hsotg_start_req(hsotg, hs_ep, hs_req, true); return; } /* Zlp for all endpoints, for ep0 only in DATA IN stage */ if (hs_ep->send_zlp) { - s3c_hsotg_program_zlp(hsotg, hs_ep); + dwc2_hsotg_program_zlp(hsotg, hs_ep); hs_ep->send_zlp = 0; /* transfer will be completed on next complete interrupt */ return; @@ -1909,36 +1929,36 @@ static void s3c_hsotg_complete_in(struct dwc2_hsotg *hsotg, if (hs_ep->index == 0 && hsotg->ep0_state == DWC2_EP0_DATA_IN) { /* Move to STATUS OUT */ - s3c_hsotg_ep0_zlp(hsotg, false); + dwc2_hsotg_ep0_zlp(hsotg, false); return; } - s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0); + dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, 0); } /** - * s3c_hsotg_epint - handle an in/out endpoint interrupt + * dwc2_hsotg_epint - handle an in/out endpoint interrupt * @hsotg: The driver state * @idx: The index for the endpoint (0..15) * @dir_in: Set if this is an IN endpoint * * Process and clear any interrupt pending for an individual endpoint */ -static void s3c_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx, +static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx, int dir_in) { - struct s3c_hsotg_ep *hs_ep = index_to_ep(hsotg, idx, dir_in); + struct dwc2_hsotg_ep *hs_ep = index_to_ep(hsotg, idx, dir_in); u32 epint_reg = dir_in ? DIEPINT(idx) : DOEPINT(idx); u32 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(idx); u32 epsiz_reg = dir_in ? DIEPTSIZ(idx) : DOEPTSIZ(idx); u32 ints; u32 ctrl; - ints = readl(hsotg->regs + epint_reg); - ctrl = readl(hsotg->regs + epctl_reg); + ints = dwc2_readl(hsotg->regs + epint_reg); + ctrl = dwc2_readl(hsotg->regs + epctl_reg); /* Clear endpoint interrupts */ - writel(ints, hsotg->regs + epint_reg); + dwc2_writel(ints, hsotg->regs + epint_reg); if (!hs_ep) { dev_err(hsotg->dev, "%s:Interrupt for unconfigured ep%d(%s)\n", @@ -1954,35 +1974,31 @@ static void s3c_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx, ints &= ~DXEPINT_XFERCOMPL; if (ints & DXEPINT_XFERCOMPL) { - if (hs_ep->isochronous && hs_ep->interval == 1) { - if (ctrl & DXEPCTL_EOFRNUM) - ctrl |= DXEPCTL_SETEVENFR; - else - ctrl |= DXEPCTL_SETODDFR; - writel(ctrl, hsotg->regs + epctl_reg); - } + hs_ep->has_correct_parity = 1; + if (hs_ep->isochronous && hs_ep->interval == 1) + dwc2_hsotg_change_ep_iso_parity(hsotg, epctl_reg); dev_dbg(hsotg->dev, "%s: XferCompl: DxEPCTL=0x%08x, DXEPTSIZ=%08x\n", - __func__, readl(hsotg->regs + epctl_reg), - readl(hsotg->regs + epsiz_reg)); + __func__, dwc2_readl(hsotg->regs + epctl_reg), + dwc2_readl(hsotg->regs + epsiz_reg)); /* * we get OutDone from the FIFO, so we only need to look * at completing IN requests here */ if (dir_in) { - s3c_hsotg_complete_in(hsotg, hs_ep); + dwc2_hsotg_complete_in(hsotg, hs_ep); if (idx == 0 && !hs_ep->req) - s3c_hsotg_enqueue_setup(hsotg); + dwc2_hsotg_enqueue_setup(hsotg); } else if (using_dma(hsotg)) { /* * We're using DMA, we need to fire an OutDone here * as we ignore the RXFIFO. */ - s3c_hsotg_handle_outdone(hsotg, idx); + dwc2_hsotg_handle_outdone(hsotg, idx); } } @@ -1990,16 +2006,16 @@ static void s3c_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx, dev_dbg(hsotg->dev, "%s: EPDisbld\n", __func__); if (dir_in) { - int epctl = readl(hsotg->regs + epctl_reg); + int epctl = dwc2_readl(hsotg->regs + epctl_reg); - s3c_hsotg_txfifo_flush(hsotg, hs_ep->fifo_index); + dwc2_hsotg_txfifo_flush(hsotg, hs_ep->fifo_index); if ((epctl & DXEPCTL_STALL) && (epctl & DXEPCTL_EPTYPE_BULK)) { - int dctl = readl(hsotg->regs + DCTL); + int dctl = dwc2_readl(hsotg->regs + DCTL); dctl |= DCTL_CGNPINNAK; - writel(dctl, hsotg->regs + DCTL); + dwc2_writel(dctl, hsotg->regs + DCTL); } } } @@ -2021,7 +2037,7 @@ static void s3c_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx, if (dir_in) WARN_ON_ONCE(1); else - s3c_hsotg_handle_outdone(hsotg, 0); + dwc2_hsotg_handle_outdone(hsotg, 0); } } @@ -2047,21 +2063,21 @@ static void s3c_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx, dev_dbg(hsotg->dev, "%s: ep%d: TxFIFOEmpty\n", __func__, idx); if (!using_dma(hsotg)) - s3c_hsotg_trytx(hsotg, hs_ep); + dwc2_hsotg_trytx(hsotg, hs_ep); } } } /** - * s3c_hsotg_irq_enumdone - Handle EnumDone interrupt (enumeration done) + * dwc2_hsotg_irq_enumdone - Handle EnumDone interrupt (enumeration done) * @hsotg: The device state. * * Handle updating the device settings after the enumeration phase has * been completed. */ -static void s3c_hsotg_irq_enumdone(struct dwc2_hsotg *hsotg) +static void dwc2_hsotg_irq_enumdone(struct dwc2_hsotg *hsotg) { - u32 dsts = readl(hsotg->regs + DSTS); + u32 dsts = dwc2_readl(hsotg->regs + DSTS); int ep0_mps = 0, ep_mps = 8; /* @@ -2113,23 +2129,23 @@ static void s3c_hsotg_irq_enumdone(struct dwc2_hsotg *hsotg) if (ep0_mps) { int i; /* Initialize ep0 for both in and out directions */ - s3c_hsotg_set_ep_maxpacket(hsotg, 0, ep0_mps, 1); - s3c_hsotg_set_ep_maxpacket(hsotg, 0, ep0_mps, 0); + dwc2_hsotg_set_ep_maxpacket(hsotg, 0, ep0_mps, 1); + dwc2_hsotg_set_ep_maxpacket(hsotg, 0, ep0_mps, 0); for (i = 1; i < hsotg->num_of_eps; i++) { if (hsotg->eps_in[i]) - s3c_hsotg_set_ep_maxpacket(hsotg, i, ep_mps, 1); + dwc2_hsotg_set_ep_maxpacket(hsotg, i, ep_mps, 1); if (hsotg->eps_out[i]) - s3c_hsotg_set_ep_maxpacket(hsotg, i, ep_mps, 0); + dwc2_hsotg_set_ep_maxpacket(hsotg, i, ep_mps, 0); } } /* ensure after enumeration our EP0 is active */ - s3c_hsotg_enqueue_setup(hsotg); + dwc2_hsotg_enqueue_setup(hsotg); dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n", - readl(hsotg->regs + DIEPCTL0), - readl(hsotg->regs + DOEPCTL0)); + dwc2_readl(hsotg->regs + DIEPCTL0), + dwc2_readl(hsotg->regs + DOEPCTL0)); } /** @@ -2142,34 +2158,34 @@ static void s3c_hsotg_irq_enumdone(struct dwc2_hsotg *hsotg) * completed with the given result code. */ static void kill_all_requests(struct dwc2_hsotg *hsotg, - struct s3c_hsotg_ep *ep, + struct dwc2_hsotg_ep *ep, int result) { - struct s3c_hsotg_req *req, *treq; + struct dwc2_hsotg_req *req, *treq; unsigned size; ep->req = NULL; list_for_each_entry_safe(req, treq, &ep->queue, queue) - s3c_hsotg_complete_request(hsotg, ep, req, + dwc2_hsotg_complete_request(hsotg, ep, req, result); if (!hsotg->dedicated_fifos) return; - size = (readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4; + size = (dwc2_readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4; if (size < ep->fifo_size) - s3c_hsotg_txfifo_flush(hsotg, ep->fifo_index); + dwc2_hsotg_txfifo_flush(hsotg, ep->fifo_index); } /** - * s3c_hsotg_disconnect - disconnect service + * dwc2_hsotg_disconnect - disconnect service * @hsotg: The device state. * * The device has been disconnected. Remove all current * transactions and signal the gadget driver that this * has happened. */ -void s3c_hsotg_disconnect(struct dwc2_hsotg *hsotg) +void dwc2_hsotg_disconnect(struct dwc2_hsotg *hsotg) { unsigned ep; @@ -2189,16 +2205,17 @@ void s3c_hsotg_disconnect(struct dwc2_hsotg *hsotg) } call_gadget(hsotg, disconnect); + hsotg->lx_state = DWC2_L3; } /** - * s3c_hsotg_irq_fifoempty - TX FIFO empty interrupt handler + * dwc2_hsotg_irq_fifoempty - TX FIFO empty interrupt handler * @hsotg: The device state: * @periodic: True if this is a periodic FIFO interrupt */ -static void s3c_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic) +static void dwc2_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic) { - struct s3c_hsotg_ep *ep; + struct dwc2_hsotg_ep *ep; int epno, ret; /* look through for any more data to transmit */ @@ -2215,7 +2232,7 @@ static void s3c_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic) (!periodic && ep->periodic)) continue; - ret = s3c_hsotg_trytx(hsotg, ep); + ret = dwc2_hsotg_trytx(hsotg, ep); if (ret < 0) break; } @@ -2227,12 +2244,12 @@ static void s3c_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic) GINTSTS_RXFLVL) /** - * s3c_hsotg_corereset - issue softreset to the core + * dwc2_hsotg_corereset - issue softreset to the core * @hsotg: The device state * * Issue a soft reset to the core, and await the core finishing it. */ -static int s3c_hsotg_corereset(struct dwc2_hsotg *hsotg) +static int dwc2_hsotg_corereset(struct dwc2_hsotg *hsotg) { int timeout; u32 grstctl; @@ -2240,11 +2257,11 @@ static int s3c_hsotg_corereset(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, "resetting core\n"); /* issue soft reset */ - writel(GRSTCTL_CSFTRST, hsotg->regs + GRSTCTL); + dwc2_writel(GRSTCTL_CSFTRST, hsotg->regs + GRSTCTL); timeout = 10000; do { - grstctl = readl(hsotg->regs + GRSTCTL); + grstctl = dwc2_readl(hsotg->regs + GRSTCTL); } while ((grstctl & GRSTCTL_CSFTRST) && timeout-- > 0); if (grstctl & GRSTCTL_CSFTRST) { @@ -2255,7 +2272,7 @@ static int s3c_hsotg_corereset(struct dwc2_hsotg *hsotg) timeout = 10000; while (1) { - u32 grstctl = readl(hsotg->regs + GRSTCTL); + u32 grstctl = dwc2_readl(hsotg->regs + GRSTCTL); if (timeout-- < 0) { dev_info(hsotg->dev, @@ -2275,18 +2292,23 @@ static int s3c_hsotg_corereset(struct dwc2_hsotg *hsotg) } /** - * s3c_hsotg_core_init - issue softreset to the core + * dwc2_hsotg_core_init - issue softreset to the core * @hsotg: The device state * * Issue a soft reset to the core, and await the core finishing it. */ -void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, +void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, bool is_usb_reset) { + u32 intmsk; u32 val; + /* Kill any ep0 requests as controller will be reinitialized */ + kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET); + if (!is_usb_reset) - s3c_hsotg_corereset(hsotg); + if (dwc2_hsotg_corereset(hsotg)) + return; /* * we must now enable ep0 ready for host detection and then @@ -2295,39 +2317,42 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, /* set the PLL on, remove the HNP/SRP and set the PHY */ val = (hsotg->phyif == GUSBCFG_PHYIF8) ? 9 : 5; - writel(hsotg->phyif | GUSBCFG_TOUTCAL(7) | + dwc2_writel(hsotg->phyif | GUSBCFG_TOUTCAL(7) | (val << GUSBCFG_USBTRDTIM_SHIFT), hsotg->regs + GUSBCFG); - s3c_hsotg_init_fifo(hsotg); + dwc2_hsotg_init_fifo(hsotg); if (!is_usb_reset) __orr32(hsotg->regs + DCTL, DCTL_SFTDISCON); - writel(DCFG_EPMISCNT(1) | DCFG_DEVSPD_HS, hsotg->regs + DCFG); + dwc2_writel(DCFG_EPMISCNT(1) | DCFG_DEVSPD_HS, hsotg->regs + DCFG); /* Clear any pending OTG interrupts */ - writel(0xffffffff, hsotg->regs + GOTGINT); + dwc2_writel(0xffffffff, hsotg->regs + GOTGINT); /* Clear any pending interrupts */ - writel(0xffffffff, hsotg->regs + GINTSTS); - - writel(GINTSTS_ERLYSUSP | GINTSTS_SESSREQINT | + dwc2_writel(0xffffffff, hsotg->regs + GINTSTS); + intmsk = GINTSTS_ERLYSUSP | GINTSTS_SESSREQINT | GINTSTS_GOUTNAKEFF | GINTSTS_GINNAKEFF | - GINTSTS_CONIDSTSCHNG | GINTSTS_USBRST | - GINTSTS_RESETDET | GINTSTS_ENUMDONE | - GINTSTS_OTGINT | GINTSTS_USBSUSP | - GINTSTS_WKUPINT, - hsotg->regs + GINTMSK); + GINTSTS_USBRST | GINTSTS_RESETDET | + GINTSTS_ENUMDONE | GINTSTS_OTGINT | + GINTSTS_USBSUSP | GINTSTS_WKUPINT | + GINTSTS_INCOMPL_SOIN | GINTSTS_INCOMPL_SOOUT; + + if (hsotg->core_params->external_id_pin_ctl <= 0) + intmsk |= GINTSTS_CONIDSTSCHNG; + + dwc2_writel(intmsk, hsotg->regs + GINTMSK); if (using_dma(hsotg)) - writel(GAHBCFG_GLBL_INTR_EN | GAHBCFG_DMA_EN | - (GAHBCFG_HBSTLEN_INCR4 << GAHBCFG_HBSTLEN_SHIFT), - hsotg->regs + GAHBCFG); + dwc2_writel(GAHBCFG_GLBL_INTR_EN | GAHBCFG_DMA_EN | + (GAHBCFG_HBSTLEN_INCR4 << GAHBCFG_HBSTLEN_SHIFT), + hsotg->regs + GAHBCFG); else - writel(((hsotg->dedicated_fifos) ? (GAHBCFG_NP_TXF_EMP_LVL | - GAHBCFG_P_TXF_EMP_LVL) : 0) | - GAHBCFG_GLBL_INTR_EN, - hsotg->regs + GAHBCFG); + dwc2_writel(((hsotg->dedicated_fifos) ? + (GAHBCFG_NP_TXF_EMP_LVL | + GAHBCFG_P_TXF_EMP_LVL) : 0) | + GAHBCFG_GLBL_INTR_EN, hsotg->regs + GAHBCFG); /* * If INTknTXFEmpMsk is enabled, it's important to disable ep interrupts @@ -2335,7 +2360,7 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, * interrupts. */ - writel(((hsotg->dedicated_fifos && !using_dma(hsotg)) ? + dwc2_writel(((hsotg->dedicated_fifos && !using_dma(hsotg)) ? DIEPMSK_TXFIFOEMPTY | DIEPMSK_INTKNTXFEMPMSK : 0) | DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK | DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK | @@ -2346,20 +2371,20 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, * don't need XferCompl, we get that from RXFIFO in slave mode. In * DMA mode we may need this. */ - writel((using_dma(hsotg) ? (DIEPMSK_XFERCOMPLMSK | + dwc2_writel((using_dma(hsotg) ? (DIEPMSK_XFERCOMPLMSK | DIEPMSK_TIMEOUTMSK) : 0) | DOEPMSK_EPDISBLDMSK | DOEPMSK_AHBERRMSK | DOEPMSK_SETUPMSK, hsotg->regs + DOEPMSK); - writel(0, hsotg->regs + DAINTMSK); + dwc2_writel(0, hsotg->regs + DAINTMSK); dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n", - readl(hsotg->regs + DIEPCTL0), - readl(hsotg->regs + DOEPCTL0)); + dwc2_readl(hsotg->regs + DIEPCTL0), + dwc2_readl(hsotg->regs + DOEPCTL0)); /* enable in and out endpoint interrupts */ - s3c_hsotg_en_gsint(hsotg, GINTSTS_OEPINT | GINTSTS_IEPINT); + dwc2_hsotg_en_gsint(hsotg, GINTSTS_OEPINT | GINTSTS_IEPINT); /* * Enable the RXFIFO when in slave mode, as this is how we collect @@ -2367,11 +2392,11 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, * things we cannot process, so do not use it. */ if (!using_dma(hsotg)) - s3c_hsotg_en_gsint(hsotg, GINTSTS_RXFLVL); + dwc2_hsotg_en_gsint(hsotg, GINTSTS_RXFLVL); /* Enable interrupts for EP0 in and out */ - s3c_hsotg_ctrl_epint(hsotg, 0, 0, 1); - s3c_hsotg_ctrl_epint(hsotg, 0, 1, 1); + dwc2_hsotg_ctrl_epint(hsotg, 0, 0, 1); + dwc2_hsotg_ctrl_epint(hsotg, 0, 1, 1); if (!is_usb_reset) { __orr32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE); @@ -2379,7 +2404,7 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, __bic32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE); } - dev_dbg(hsotg->dev, "DCTL=0x%08x\n", readl(hsotg->regs + DCTL)); + dev_dbg(hsotg->dev, "DCTL=0x%08x\n", dwc2_readl(hsotg->regs + DCTL)); /* * DxEPCTL_USBActEp says RO in manual, but seems to be set by @@ -2387,23 +2412,23 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, */ /* set to read 1 8byte packet */ - writel(DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) | + dwc2_writel(DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) | DXEPTSIZ_XFERSIZE(8), hsotg->regs + DOEPTSIZ0); - writel(s3c_hsotg_ep0_mps(hsotg->eps_out[0]->ep.maxpacket) | + dwc2_writel(dwc2_hsotg_ep0_mps(hsotg->eps_out[0]->ep.maxpacket) | DXEPCTL_CNAK | DXEPCTL_EPENA | DXEPCTL_USBACTEP, hsotg->regs + DOEPCTL0); /* enable, but don't activate EP0in */ - writel(s3c_hsotg_ep0_mps(hsotg->eps_out[0]->ep.maxpacket) | + dwc2_writel(dwc2_hsotg_ep0_mps(hsotg->eps_out[0]->ep.maxpacket) | DXEPCTL_USBACTEP, hsotg->regs + DIEPCTL0); - s3c_hsotg_enqueue_setup(hsotg); + dwc2_hsotg_enqueue_setup(hsotg); dev_dbg(hsotg->dev, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n", - readl(hsotg->regs + DIEPCTL0), - readl(hsotg->regs + DOEPCTL0)); + dwc2_readl(hsotg->regs + DIEPCTL0), + dwc2_readl(hsotg->regs + DOEPCTL0)); /* clear global NAKs */ val = DCTL_CGOUTNAK | DCTL_CGNPINNAK; @@ -2414,27 +2439,27 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, /* must be at-least 3ms to allow bus to see disconnect */ mdelay(3); - hsotg->last_rst = jiffies; + hsotg->lx_state = DWC2_L0; } -static void s3c_hsotg_core_disconnect(struct dwc2_hsotg *hsotg) +static void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg) { /* set the soft-disconnect bit */ __orr32(hsotg->regs + DCTL, DCTL_SFTDISCON); } -void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg) +void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg) { /* remove the soft-disconnect and let's go */ __bic32(hsotg->regs + DCTL, DCTL_SFTDISCON); } /** - * s3c_hsotg_irq - handle device interrupt + * dwc2_hsotg_irq - handle device interrupt * @irq: The IRQ number triggered * @pw: The pw value when registered the handler. */ -static irqreturn_t s3c_hsotg_irq(int irq, void *pw) +static irqreturn_t dwc2_hsotg_irq(int irq, void *pw) { struct dwc2_hsotg *hsotg = pw; int retry_count = 8; @@ -2443,49 +2468,18 @@ static irqreturn_t s3c_hsotg_irq(int irq, void *pw) spin_lock(&hsotg->lock); irq_retry: - gintsts = readl(hsotg->regs + GINTSTS); - gintmsk = readl(hsotg->regs + GINTMSK); + gintsts = dwc2_readl(hsotg->regs + GINTSTS); + gintmsk = dwc2_readl(hsotg->regs + GINTMSK); dev_dbg(hsotg->dev, "%s: %08x %08x (%08x) retry %d\n", __func__, gintsts, gintsts & gintmsk, gintmsk, retry_count); gintsts &= gintmsk; - if (gintsts & GINTSTS_ENUMDONE) { - writel(GINTSTS_ENUMDONE, hsotg->regs + GINTSTS); - - s3c_hsotg_irq_enumdone(hsotg); - } - - if (gintsts & (GINTSTS_OEPINT | GINTSTS_IEPINT)) { - u32 daint = readl(hsotg->regs + DAINT); - u32 daintmsk = readl(hsotg->regs + DAINTMSK); - u32 daint_out, daint_in; - int ep; - - daint &= daintmsk; - daint_out = daint >> DAINT_OUTEP_SHIFT; - daint_in = daint & ~(daint_out << DAINT_OUTEP_SHIFT); - - dev_dbg(hsotg->dev, "%s: daint=%08x\n", __func__, daint); - - for (ep = 0; ep < hsotg->num_of_eps && daint_out; - ep++, daint_out >>= 1) { - if (daint_out & 1) - s3c_hsotg_epint(hsotg, ep, 0); - } - - for (ep = 0; ep < hsotg->num_of_eps && daint_in; - ep++, daint_in >>= 1) { - if (daint_in & 1) - s3c_hsotg_epint(hsotg, ep, 1); - } - } - if (gintsts & GINTSTS_RESETDET) { dev_dbg(hsotg->dev, "%s: USBRstDet\n", __func__); - writel(GINTSTS_RESETDET, hsotg->regs + GINTSTS); + dwc2_writel(GINTSTS_RESETDET, hsotg->regs + GINTSTS); /* This event must be used only if controller is suspended */ if (hsotg->lx_state == DWC2_L2) { @@ -2496,27 +2490,50 @@ irq_retry: if (gintsts & (GINTSTS_USBRST | GINTSTS_RESETDET)) { - u32 usb_status = readl(hsotg->regs + GOTGCTL); + u32 usb_status = dwc2_readl(hsotg->regs + GOTGCTL); + u32 connected = hsotg->connected; dev_dbg(hsotg->dev, "%s: USBRst\n", __func__); dev_dbg(hsotg->dev, "GNPTXSTS=%08x\n", - readl(hsotg->regs + GNPTXSTS)); + dwc2_readl(hsotg->regs + GNPTXSTS)); - writel(GINTSTS_USBRST, hsotg->regs + GINTSTS); + dwc2_writel(GINTSTS_USBRST, hsotg->regs + GINTSTS); /* Report disconnection if it is not already done. */ - s3c_hsotg_disconnect(hsotg); + dwc2_hsotg_disconnect(hsotg); + + if (usb_status & GOTGCTL_BSESVLD && connected) + dwc2_hsotg_core_init_disconnected(hsotg, true); + } - if (usb_status & GOTGCTL_BSESVLD) { - if (time_after(jiffies, hsotg->last_rst + - msecs_to_jiffies(200))) { + if (gintsts & GINTSTS_ENUMDONE) { + dwc2_writel(GINTSTS_ENUMDONE, hsotg->regs + GINTSTS); - kill_all_requests(hsotg, hsotg->eps_out[0], - -ECONNRESET); + dwc2_hsotg_irq_enumdone(hsotg); + } - hsotg->lx_state = DWC2_L0; - s3c_hsotg_core_init_disconnected(hsotg, true); - } + if (gintsts & (GINTSTS_OEPINT | GINTSTS_IEPINT)) { + u32 daint = dwc2_readl(hsotg->regs + DAINT); + u32 daintmsk = dwc2_readl(hsotg->regs + DAINTMSK); + u32 daint_out, daint_in; + int ep; + + daint &= daintmsk; + daint_out = daint >> DAINT_OUTEP_SHIFT; + daint_in = daint & ~(daint_out << DAINT_OUTEP_SHIFT); + + dev_dbg(hsotg->dev, "%s: daint=%08x\n", __func__, daint); + + for (ep = 0; ep < hsotg->num_of_eps && daint_out; + ep++, daint_out >>= 1) { + if (daint_out & 1) + dwc2_hsotg_epint(hsotg, ep, 0); + } + + for (ep = 0; ep < hsotg->num_of_eps && daint_in; + ep++, daint_in >>= 1) { + if (daint_in & 1) + dwc2_hsotg_epint(hsotg, ep, 1); } } @@ -2531,8 +2548,8 @@ irq_retry: * it needs re-enabling */ - s3c_hsotg_disable_gsint(hsotg, GINTSTS_NPTXFEMP); - s3c_hsotg_irq_fifoempty(hsotg, false); + dwc2_hsotg_disable_gsint(hsotg, GINTSTS_NPTXFEMP); + dwc2_hsotg_irq_fifoempty(hsotg, false); } if (gintsts & GINTSTS_PTXFEMP) { @@ -2540,23 +2557,23 @@ irq_retry: /* See note in GINTSTS_NPTxFEmp */ - s3c_hsotg_disable_gsint(hsotg, GINTSTS_PTXFEMP); - s3c_hsotg_irq_fifoempty(hsotg, true); + dwc2_hsotg_disable_gsint(hsotg, GINTSTS_PTXFEMP); + dwc2_hsotg_irq_fifoempty(hsotg, true); } if (gintsts & GINTSTS_RXFLVL) { /* * note, since GINTSTS_RxFLvl doubles as FIFO-not-empty, - * we need to retry s3c_hsotg_handle_rx if this is still + * we need to retry dwc2_hsotg_handle_rx if this is still * set. */ - s3c_hsotg_handle_rx(hsotg); + dwc2_hsotg_handle_rx(hsotg); } if (gintsts & GINTSTS_ERLYSUSP) { dev_dbg(hsotg->dev, "GINTSTS_ErlySusp\n"); - writel(GINTSTS_ERLYSUSP, hsotg->regs + GINTSTS); + dwc2_writel(GINTSTS_ERLYSUSP, hsotg->regs + GINTSTS); } /* @@ -2568,17 +2585,51 @@ irq_retry: if (gintsts & GINTSTS_GOUTNAKEFF) { dev_info(hsotg->dev, "GOUTNakEff triggered\n"); - writel(DCTL_CGOUTNAK, hsotg->regs + DCTL); + dwc2_writel(DCTL_CGOUTNAK, hsotg->regs + DCTL); - s3c_hsotg_dump(hsotg); + dwc2_hsotg_dump(hsotg); } if (gintsts & GINTSTS_GINNAKEFF) { dev_info(hsotg->dev, "GINNakEff triggered\n"); - writel(DCTL_CGNPINNAK, hsotg->regs + DCTL); + dwc2_writel(DCTL_CGNPINNAK, hsotg->regs + DCTL); + + dwc2_hsotg_dump(hsotg); + } + + if (gintsts & GINTSTS_INCOMPL_SOIN) { + u32 idx, epctl_reg; + struct dwc2_hsotg_ep *hs_ep; + + dev_dbg(hsotg->dev, "%s: GINTSTS_INCOMPL_SOIN\n", __func__); + for (idx = 1; idx < hsotg->num_of_eps; idx++) { + hs_ep = hsotg->eps_in[idx]; + + if (!hs_ep->isochronous || hs_ep->has_correct_parity) + continue; + + epctl_reg = DIEPCTL(idx); + dwc2_hsotg_change_ep_iso_parity(hsotg, epctl_reg); + } + dwc2_writel(GINTSTS_INCOMPL_SOIN, hsotg->regs + GINTSTS); + } + + if (gintsts & GINTSTS_INCOMPL_SOOUT) { + u32 idx, epctl_reg; + struct dwc2_hsotg_ep *hs_ep; - s3c_hsotg_dump(hsotg); + dev_dbg(hsotg->dev, "%s: GINTSTS_INCOMPL_SOOUT\n", __func__); + for (idx = 1; idx < hsotg->num_of_eps; idx++) { + hs_ep = hsotg->eps_out[idx]; + + if (!hs_ep->isochronous || hs_ep->has_correct_parity) + continue; + + epctl_reg = DOEPCTL(idx); + dwc2_hsotg_change_ep_iso_parity(hsotg, epctl_reg); + } + dwc2_writel(GINTSTS_INCOMPL_SOOUT, hsotg->regs + GINTSTS); } /* @@ -2595,16 +2646,16 @@ irq_retry: } /** - * s3c_hsotg_ep_enable - enable the given endpoint + * dwc2_hsotg_ep_enable - enable the given endpoint * @ep: The USB endpint to configure * @desc: The USB endpoint descriptor to configure with. * * This is called from the USB gadget code's usb_ep_enable(). */ -static int s3c_hsotg_ep_enable(struct usb_ep *ep, +static int dwc2_hsotg_ep_enable(struct usb_ep *ep, const struct usb_endpoint_descriptor *desc) { - struct s3c_hsotg_ep *hs_ep = our_ep(ep); + struct dwc2_hsotg_ep *hs_ep = our_ep(ep); struct dwc2_hsotg *hsotg = hs_ep->parent; unsigned long flags; unsigned int index = hs_ep->index; @@ -2631,10 +2682,10 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, mps = usb_endpoint_maxp(desc); - /* note, we handle this here instead of s3c_hsotg_set_ep_maxpacket */ + /* note, we handle this here instead of dwc2_hsotg_set_ep_maxpacket */ epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index); - epctrl = readl(hsotg->regs + epctrl_reg); + epctrl = dwc2_readl(hsotg->regs + epctrl_reg); dev_dbg(hsotg->dev, "%s: read DxEPCTL=0x%08x from 0x%08x\n", __func__, epctrl, epctrl_reg); @@ -2660,13 +2711,14 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, epctrl |= DXEPCTL_SNAK; /* update the endpoint state */ - s3c_hsotg_set_ep_maxpacket(hsotg, hs_ep->index, mps, dir_in); + dwc2_hsotg_set_ep_maxpacket(hsotg, hs_ep->index, mps, dir_in); /* default, set to non-periodic */ hs_ep->isochronous = 0; hs_ep->periodic = 0; hs_ep->halted = 0; hs_ep->interval = desc->bInterval; + hs_ep->has_correct_parity = 0; if (hs_ep->interval > 1 && hs_ep->mc > 1) dev_err(hsotg->dev, "MC > 1 when interval is not 1\n"); @@ -2718,7 +2770,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, for (i = 1; i < hsotg->num_of_eps; ++i) { if (hsotg->fifo_map & (1<<i)) continue; - val = readl(hsotg->regs + DPTXFSIZN(i)); + val = dwc2_readl(hsotg->regs + DPTXFSIZN(i)); val = (val >> FIFOSIZE_DEPTH_SHIFT)*4; if (val < size) continue; @@ -2747,12 +2799,12 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, dev_dbg(hsotg->dev, "%s: write DxEPCTL=0x%08x\n", __func__, epctrl); - writel(epctrl, hsotg->regs + epctrl_reg); + dwc2_writel(epctrl, hsotg->regs + epctrl_reg); dev_dbg(hsotg->dev, "%s: read DxEPCTL=0x%08x\n", - __func__, readl(hsotg->regs + epctrl_reg)); + __func__, dwc2_readl(hsotg->regs + epctrl_reg)); /* enable the endpoint interrupt */ - s3c_hsotg_ctrl_epint(hsotg, index, dir_in, 1); + dwc2_hsotg_ctrl_epint(hsotg, index, dir_in, 1); error: spin_unlock_irqrestore(&hsotg->lock, flags); @@ -2760,12 +2812,12 @@ error: } /** - * s3c_hsotg_ep_disable - disable given endpoint + * dwc2_hsotg_ep_disable - disable given endpoint * @ep: The endpoint to disable. */ -static int s3c_hsotg_ep_disable(struct usb_ep *ep) +static int dwc2_hsotg_ep_disable(struct usb_ep *ep) { - struct s3c_hsotg_ep *hs_ep = our_ep(ep); + struct dwc2_hsotg_ep *hs_ep = our_ep(ep); struct dwc2_hsotg *hsotg = hs_ep->parent; int dir_in = hs_ep->dir_in; int index = hs_ep->index; @@ -2788,16 +2840,16 @@ static int s3c_hsotg_ep_disable(struct usb_ep *ep) hs_ep->fifo_index = 0; hs_ep->fifo_size = 0; - ctrl = readl(hsotg->regs + epctrl_reg); + ctrl = dwc2_readl(hsotg->regs + epctrl_reg); ctrl &= ~DXEPCTL_EPENA; ctrl &= ~DXEPCTL_USBACTEP; ctrl |= DXEPCTL_SNAK; dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", __func__, ctrl); - writel(ctrl, hsotg->regs + epctrl_reg); + dwc2_writel(ctrl, hsotg->regs + epctrl_reg); /* disable endpoint interrupts */ - s3c_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 0); + dwc2_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 0); /* terminate all requests with shutdown */ kill_all_requests(hsotg, hs_ep, -ESHUTDOWN); @@ -2811,9 +2863,9 @@ static int s3c_hsotg_ep_disable(struct usb_ep *ep) * @ep: The endpoint to check. * @test: The request to test if it is on the endpoint. */ -static bool on_list(struct s3c_hsotg_ep *ep, struct s3c_hsotg_req *test) +static bool on_list(struct dwc2_hsotg_ep *ep, struct dwc2_hsotg_req *test) { - struct s3c_hsotg_req *req, *treq; + struct dwc2_hsotg_req *req, *treq; list_for_each_entry_safe(req, treq, &ep->queue, queue) { if (req == test) @@ -2823,15 +2875,88 @@ static bool on_list(struct s3c_hsotg_ep *ep, struct s3c_hsotg_req *test) return false; } +static int dwc2_hsotg_wait_bit_set(struct dwc2_hsotg *hs_otg, u32 reg, + u32 bit, u32 timeout) +{ + u32 i; + + for (i = 0; i < timeout; i++) { + if (dwc2_readl(hs_otg->regs + reg) & bit) + return 0; + udelay(1); + } + + return -ETIMEDOUT; +} + +static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg, + struct dwc2_hsotg_ep *hs_ep) +{ + u32 epctrl_reg; + u32 epint_reg; + + epctrl_reg = hs_ep->dir_in ? DIEPCTL(hs_ep->index) : + DOEPCTL(hs_ep->index); + epint_reg = hs_ep->dir_in ? DIEPINT(hs_ep->index) : + DOEPINT(hs_ep->index); + + dev_dbg(hsotg->dev, "%s: stopping transfer on %s\n", __func__, + hs_ep->name); + if (hs_ep->dir_in) { + __orr32(hsotg->regs + epctrl_reg, DXEPCTL_SNAK); + /* Wait for Nak effect */ + if (dwc2_hsotg_wait_bit_set(hsotg, epint_reg, + DXEPINT_INEPNAKEFF, 100)) + dev_warn(hsotg->dev, + "%s: timeout DIEPINT.NAKEFF\n", __func__); + } else { + /* Clear any pending nak effect interrupt */ + dwc2_writel(GINTSTS_GINNAKEFF, hsotg->regs + GINTSTS); + + __orr32(hsotg->regs + DCTL, DCTL_SGNPINNAK); + + /* Wait for global nak to take effect */ + if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS, + GINTSTS_GINNAKEFF, 100)) + dev_warn(hsotg->dev, + "%s: timeout GINTSTS.GINNAKEFF\n", __func__); + } + + /* Disable ep */ + __orr32(hsotg->regs + epctrl_reg, DXEPCTL_EPDIS | DXEPCTL_SNAK); + + /* Wait for ep to be disabled */ + if (dwc2_hsotg_wait_bit_set(hsotg, epint_reg, DXEPINT_EPDISBLD, 100)) + dev_warn(hsotg->dev, + "%s: timeout DOEPCTL.EPDisable\n", __func__); + + if (hs_ep->dir_in) { + if (hsotg->dedicated_fifos) { + dwc2_writel(GRSTCTL_TXFNUM(hs_ep->fifo_index) | + GRSTCTL_TXFFLSH, hsotg->regs + GRSTCTL); + /* Wait for fifo flush */ + if (dwc2_hsotg_wait_bit_set(hsotg, GRSTCTL, + GRSTCTL_TXFFLSH, 100)) + dev_warn(hsotg->dev, + "%s: timeout flushing fifos\n", + __func__); + } + /* TODO: Flush shared tx fifo */ + } else { + /* Remove global NAKs */ + __bic32(hsotg->regs + DCTL, DCTL_SGNPINNAK); + } +} + /** - * s3c_hsotg_ep_dequeue - dequeue given endpoint + * dwc2_hsotg_ep_dequeue - dequeue given endpoint * @ep: The endpoint to dequeue. * @req: The request to be removed from a queue. */ -static int s3c_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req) +static int dwc2_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req) { - struct s3c_hsotg_req *hs_req = our_req(req); - struct s3c_hsotg_ep *hs_ep = our_ep(ep); + struct dwc2_hsotg_req *hs_req = our_req(req); + struct dwc2_hsotg_ep *hs_ep = our_ep(ep); struct dwc2_hsotg *hs = hs_ep->parent; unsigned long flags; @@ -2844,20 +2969,24 @@ static int s3c_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req) return -EINVAL; } - s3c_hsotg_complete_request(hs, hs_ep, hs_req, -ECONNRESET); + /* Dequeue already started request */ + if (req == &hs_ep->req->req) + dwc2_hsotg_ep_stop_xfr(hs, hs_ep); + + dwc2_hsotg_complete_request(hs, hs_ep, hs_req, -ECONNRESET); spin_unlock_irqrestore(&hs->lock, flags); return 0; } /** - * s3c_hsotg_ep_sethalt - set halt on a given endpoint + * dwc2_hsotg_ep_sethalt - set halt on a given endpoint * @ep: The endpoint to set halt. * @value: Set or unset the halt. */ -static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value) +static int dwc2_hsotg_ep_sethalt(struct usb_ep *ep, int value) { - struct s3c_hsotg_ep *hs_ep = our_ep(ep); + struct dwc2_hsotg_ep *hs_ep = our_ep(ep); struct dwc2_hsotg *hs = hs_ep->parent; int index = hs_ep->index; u32 epreg; @@ -2868,7 +2997,7 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value) if (index == 0) { if (value) - s3c_hsotg_stall_ep0(hs); + dwc2_hsotg_stall_ep0(hs); else dev_warn(hs->dev, "%s: can't clear halt on ep0\n", __func__); @@ -2877,7 +3006,7 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value) if (hs_ep->dir_in) { epreg = DIEPCTL(index); - epctl = readl(hs->regs + epreg); + epctl = dwc2_readl(hs->regs + epreg); if (value) { epctl |= DXEPCTL_STALL | DXEPCTL_SNAK; @@ -2890,11 +3019,11 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value) xfertype == DXEPCTL_EPTYPE_INTERRUPT) epctl |= DXEPCTL_SETD0PID; } - writel(epctl, hs->regs + epreg); + dwc2_writel(epctl, hs->regs + epreg); } else { epreg = DOEPCTL(index); - epctl = readl(hs->regs + epreg); + epctl = dwc2_readl(hs->regs + epreg); if (value) epctl |= DXEPCTL_STALL; @@ -2905,7 +3034,7 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value) xfertype == DXEPCTL_EPTYPE_INTERRUPT) epctl |= DXEPCTL_SETD0PID; } - writel(epctl, hs->regs + epreg); + dwc2_writel(epctl, hs->regs + epreg); } hs_ep->halted = value; @@ -2914,97 +3043,53 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value) } /** - * s3c_hsotg_ep_sethalt_lock - set halt on a given endpoint with lock held + * dwc2_hsotg_ep_sethalt_lock - set halt on a given endpoint with lock held * @ep: The endpoint to set halt. * @value: Set or unset the halt. */ -static int s3c_hsotg_ep_sethalt_lock(struct usb_ep *ep, int value) +static int dwc2_hsotg_ep_sethalt_lock(struct usb_ep *ep, int value) { - struct s3c_hsotg_ep *hs_ep = our_ep(ep); + struct dwc2_hsotg_ep *hs_ep = our_ep(ep); struct dwc2_hsotg *hs = hs_ep->parent; unsigned long flags = 0; int ret = 0; spin_lock_irqsave(&hs->lock, flags); - ret = s3c_hsotg_ep_sethalt(ep, value); + ret = dwc2_hsotg_ep_sethalt(ep, value); spin_unlock_irqrestore(&hs->lock, flags); return ret; } -static struct usb_ep_ops s3c_hsotg_ep_ops = { - .enable = s3c_hsotg_ep_enable, - .disable = s3c_hsotg_ep_disable, - .alloc_request = s3c_hsotg_ep_alloc_request, - .free_request = s3c_hsotg_ep_free_request, - .queue = s3c_hsotg_ep_queue_lock, - .dequeue = s3c_hsotg_ep_dequeue, - .set_halt = s3c_hsotg_ep_sethalt_lock, +static struct usb_ep_ops dwc2_hsotg_ep_ops = { + .enable = dwc2_hsotg_ep_enable, + .disable = dwc2_hsotg_ep_disable, + .alloc_request = dwc2_hsotg_ep_alloc_request, + .free_request = dwc2_hsotg_ep_free_request, + .queue = dwc2_hsotg_ep_queue_lock, + .dequeue = dwc2_hsotg_ep_dequeue, + .set_halt = dwc2_hsotg_ep_sethalt_lock, /* note, don't believe we have any call for the fifo routines */ }; /** - * s3c_hsotg_phy_enable - enable platform phy dev - * @hsotg: The driver state - * - * A wrapper for platform code responsible for controlling - * low-level USB code - */ -static void s3c_hsotg_phy_enable(struct dwc2_hsotg *hsotg) -{ - struct platform_device *pdev = to_platform_device(hsotg->dev); - - dev_dbg(hsotg->dev, "pdev 0x%p\n", pdev); - - if (hsotg->uphy) - usb_phy_init(hsotg->uphy); - else if (hsotg->plat && hsotg->plat->phy_init) - hsotg->plat->phy_init(pdev, hsotg->plat->phy_type); - else { - phy_init(hsotg->phy); - phy_power_on(hsotg->phy); - } -} - -/** - * s3c_hsotg_phy_disable - disable platform phy dev - * @hsotg: The driver state - * - * A wrapper for platform code responsible for controlling - * low-level USB code - */ -static void s3c_hsotg_phy_disable(struct dwc2_hsotg *hsotg) -{ - struct platform_device *pdev = to_platform_device(hsotg->dev); - - if (hsotg->uphy) - usb_phy_shutdown(hsotg->uphy); - else if (hsotg->plat && hsotg->plat->phy_exit) - hsotg->plat->phy_exit(pdev, hsotg->plat->phy_type); - else { - phy_power_off(hsotg->phy); - phy_exit(hsotg->phy); - } -} - -/** - * s3c_hsotg_init - initalize the usb core + * dwc2_hsotg_init - initalize the usb core * @hsotg: The driver state */ -static void s3c_hsotg_init(struct dwc2_hsotg *hsotg) +static void dwc2_hsotg_init(struct dwc2_hsotg *hsotg) { u32 trdtim; /* unmask subset of endpoint interrupts */ - writel(DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK | - DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK, - hsotg->regs + DIEPMSK); + dwc2_writel(DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK | + DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK, + hsotg->regs + DIEPMSK); - writel(DOEPMSK_SETUPMSK | DOEPMSK_AHBERRMSK | - DOEPMSK_EPDISBLDMSK | DOEPMSK_XFERCOMPLMSK, - hsotg->regs + DOEPMSK); + dwc2_writel(DOEPMSK_SETUPMSK | DOEPMSK_AHBERRMSK | + DOEPMSK_EPDISBLDMSK | DOEPMSK_XFERCOMPLMSK, + hsotg->regs + DOEPMSK); - writel(0, hsotg->regs + DAINTMSK); + dwc2_writel(0, hsotg->regs + DAINTMSK); /* Be in disconnected state until gadget is registered */ __orr32(hsotg->regs + DCTL, DCTL_SFTDISCON); @@ -3012,14 +3097,14 @@ static void s3c_hsotg_init(struct dwc2_hsotg *hsotg) /* setup fifos */ dev_dbg(hsotg->dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n", - readl(hsotg->regs + GRXFSIZ), - readl(hsotg->regs + GNPTXFSIZ)); + dwc2_readl(hsotg->regs + GRXFSIZ), + dwc2_readl(hsotg->regs + GNPTXFSIZ)); - s3c_hsotg_init_fifo(hsotg); + dwc2_hsotg_init_fifo(hsotg); /* set the PLL on, remove the HNP/SRP and set the PHY */ trdtim = (hsotg->phyif == GUSBCFG_PHYIF8) ? 9 : 5; - writel(hsotg->phyif | GUSBCFG_TOUTCAL(7) | + dwc2_writel(hsotg->phyif | GUSBCFG_TOUTCAL(7) | (trdtim << GUSBCFG_USBTRDTIM_SHIFT), hsotg->regs + GUSBCFG); @@ -3028,14 +3113,14 @@ static void s3c_hsotg_init(struct dwc2_hsotg *hsotg) } /** - * s3c_hsotg_udc_start - prepare the udc for work + * dwc2_hsotg_udc_start - prepare the udc for work * @gadget: The usb gadget state * @driver: The usb gadget driver * * Perform initialization to prepare udc device and driver * to work. */ -static int s3c_hsotg_udc_start(struct usb_gadget *gadget, +static int dwc2_hsotg_udc_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver) { struct dwc2_hsotg *hsotg = to_hsotg(gadget); @@ -3060,7 +3145,6 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget, return -EINVAL; } - mutex_lock(&hsotg->init_mutex); WARN_ON(hsotg->driver); driver->driver.bus = NULL; @@ -3068,45 +3152,38 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget, hsotg->gadget.dev.of_node = hsotg->dev->of_node; hsotg->gadget.speed = USB_SPEED_UNKNOWN; - clk_enable(hsotg->clk); - - ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies), - hsotg->supplies); - if (ret) { - dev_err(hsotg->dev, "failed to enable supplies: %d\n", ret); - goto err; + if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) { + ret = dwc2_lowlevel_hw_enable(hsotg); + if (ret) + goto err; } - s3c_hsotg_phy_enable(hsotg); if (!IS_ERR_OR_NULL(hsotg->uphy)) otg_set_peripheral(hsotg->uphy->otg, &hsotg->gadget); spin_lock_irqsave(&hsotg->lock, flags); - s3c_hsotg_init(hsotg); - s3c_hsotg_core_init_disconnected(hsotg, false); + dwc2_hsotg_init(hsotg); + dwc2_hsotg_core_init_disconnected(hsotg, false); hsotg->enabled = 0; spin_unlock_irqrestore(&hsotg->lock, flags); dev_info(hsotg->dev, "bound driver %s\n", driver->driver.name); - mutex_unlock(&hsotg->init_mutex); - return 0; err: - mutex_unlock(&hsotg->init_mutex); hsotg->driver = NULL; return ret; } /** - * s3c_hsotg_udc_stop - stop the udc + * dwc2_hsotg_udc_stop - stop the udc * @gadget: The usb gadget state * @driver: The usb gadget driver * * Stop udc hw block and stay tunned for future transmissions */ -static int s3c_hsotg_udc_stop(struct usb_gadget *gadget) +static int dwc2_hsotg_udc_stop(struct usb_gadget *gadget) { struct dwc2_hsotg *hsotg = to_hsotg(gadget); unsigned long flags = 0; @@ -3115,14 +3192,12 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget) if (!hsotg) return -ENODEV; - mutex_lock(&hsotg->init_mutex); - /* all endpoints should be shutdown */ for (ep = 1; ep < hsotg->num_of_eps; ep++) { if (hsotg->eps_in[ep]) - s3c_hsotg_ep_disable(&hsotg->eps_in[ep]->ep); + dwc2_hsotg_ep_disable(&hsotg->eps_in[ep]->ep); if (hsotg->eps_out[ep]) - s3c_hsotg_ep_disable(&hsotg->eps_out[ep]->ep); + dwc2_hsotg_ep_disable(&hsotg->eps_out[ep]->ep); } spin_lock_irqsave(&hsotg->lock, flags); @@ -3135,64 +3210,63 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget) if (!IS_ERR_OR_NULL(hsotg->uphy)) otg_set_peripheral(hsotg->uphy->otg, NULL); - s3c_hsotg_phy_disable(hsotg); - regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); - - clk_disable(hsotg->clk); - - mutex_unlock(&hsotg->init_mutex); + if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) + dwc2_lowlevel_hw_disable(hsotg); return 0; } /** - * s3c_hsotg_gadget_getframe - read the frame number + * dwc2_hsotg_gadget_getframe - read the frame number * @gadget: The usb gadget state * * Read the {micro} frame number */ -static int s3c_hsotg_gadget_getframe(struct usb_gadget *gadget) +static int dwc2_hsotg_gadget_getframe(struct usb_gadget *gadget) { - return s3c_hsotg_read_frameno(to_hsotg(gadget)); + return dwc2_hsotg_read_frameno(to_hsotg(gadget)); } /** - * s3c_hsotg_pullup - connect/disconnect the USB PHY + * dwc2_hsotg_pullup - connect/disconnect the USB PHY * @gadget: The usb gadget state * @is_on: Current state of the USB PHY * * Connect/Disconnect the USB PHY pullup */ -static int s3c_hsotg_pullup(struct usb_gadget *gadget, int is_on) +static int dwc2_hsotg_pullup(struct usb_gadget *gadget, int is_on) { struct dwc2_hsotg *hsotg = to_hsotg(gadget); unsigned long flags = 0; - dev_dbg(hsotg->dev, "%s: is_on: %d\n", __func__, is_on); + dev_dbg(hsotg->dev, "%s: is_on: %d op_state: %d\n", __func__, is_on, + hsotg->op_state); + + /* Don't modify pullup state while in host mode */ + if (hsotg->op_state != OTG_STATE_B_PERIPHERAL) { + hsotg->enabled = is_on; + return 0; + } - mutex_lock(&hsotg->init_mutex); spin_lock_irqsave(&hsotg->lock, flags); if (is_on) { - clk_enable(hsotg->clk); hsotg->enabled = 1; - s3c_hsotg_core_init_disconnected(hsotg, false); - s3c_hsotg_core_connect(hsotg); + dwc2_hsotg_core_init_disconnected(hsotg, false); + dwc2_hsotg_core_connect(hsotg); } else { - s3c_hsotg_core_disconnect(hsotg); - s3c_hsotg_disconnect(hsotg); + dwc2_hsotg_core_disconnect(hsotg); + dwc2_hsotg_disconnect(hsotg); hsotg->enabled = 0; - clk_disable(hsotg->clk); } hsotg->gadget.speed = USB_SPEED_UNKNOWN; spin_unlock_irqrestore(&hsotg->lock, flags); - mutex_unlock(&hsotg->init_mutex); return 0; } -static int s3c_hsotg_vbus_session(struct usb_gadget *gadget, int is_active) +static int dwc2_hsotg_vbus_session(struct usb_gadget *gadget, int is_active) { struct dwc2_hsotg *hsotg = to_hsotg(gadget); unsigned long flags; @@ -3200,23 +3274,22 @@ static int s3c_hsotg_vbus_session(struct usb_gadget *gadget, int is_active) dev_dbg(hsotg->dev, "%s: is_active: %d\n", __func__, is_active); spin_lock_irqsave(&hsotg->lock, flags); + /* + * If controller is hibernated, it must exit from hibernation + * before being initialized / de-initialized + */ + if (hsotg->lx_state == DWC2_L2) + dwc2_exit_hibernation(hsotg, false); + if (is_active) { - /* - * If controller is hibernated, it must exit from hibernation - * before being initialized - */ - if (hsotg->lx_state == DWC2_L2) { - dwc2_exit_hibernation(hsotg, false); - hsotg->lx_state = DWC2_L0; - } - /* Kill any ep0 requests as controller will be reinitialized */ - kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET); - s3c_hsotg_core_init_disconnected(hsotg, false); + hsotg->op_state = OTG_STATE_B_PERIPHERAL; + + dwc2_hsotg_core_init_disconnected(hsotg, false); if (hsotg->enabled) - s3c_hsotg_core_connect(hsotg); + dwc2_hsotg_core_connect(hsotg); } else { - s3c_hsotg_core_disconnect(hsotg); - s3c_hsotg_disconnect(hsotg); + dwc2_hsotg_core_disconnect(hsotg); + dwc2_hsotg_disconnect(hsotg); } spin_unlock_irqrestore(&hsotg->lock, flags); @@ -3224,13 +3297,13 @@ static int s3c_hsotg_vbus_session(struct usb_gadget *gadget, int is_active) } /** - * s3c_hsotg_vbus_draw - report bMaxPower field + * dwc2_hsotg_vbus_draw - report bMaxPower field * @gadget: The usb gadget state * @mA: Amount of current * * Report how much power the device may consume to the phy. */ -static int s3c_hsotg_vbus_draw(struct usb_gadget *gadget, unsigned mA) +static int dwc2_hsotg_vbus_draw(struct usb_gadget *gadget, unsigned mA) { struct dwc2_hsotg *hsotg = to_hsotg(gadget); @@ -3239,17 +3312,17 @@ static int s3c_hsotg_vbus_draw(struct usb_gadget *gadget, unsigned mA) return usb_phy_set_power(hsotg->uphy, mA); } -static const struct usb_gadget_ops s3c_hsotg_gadget_ops = { - .get_frame = s3c_hsotg_gadget_getframe, - .udc_start = s3c_hsotg_udc_start, - .udc_stop = s3c_hsotg_udc_stop, - .pullup = s3c_hsotg_pullup, - .vbus_session = s3c_hsotg_vbus_session, - .vbus_draw = s3c_hsotg_vbus_draw, +static const struct usb_gadget_ops dwc2_hsotg_gadget_ops = { + .get_frame = dwc2_hsotg_gadget_getframe, + .udc_start = dwc2_hsotg_udc_start, + .udc_stop = dwc2_hsotg_udc_stop, + .pullup = dwc2_hsotg_pullup, + .vbus_session = dwc2_hsotg_vbus_session, + .vbus_draw = dwc2_hsotg_vbus_draw, }; /** - * s3c_hsotg_initep - initialise a single endpoint + * dwc2_hsotg_initep - initialise a single endpoint * @hsotg: The device state. * @hs_ep: The endpoint to be initialised. * @epnum: The endpoint number @@ -3258,8 +3331,8 @@ static const struct usb_gadget_ops s3c_hsotg_gadget_ops = { * creation) to give to the gadget driver. Setup the endpoint name, any * direction information and other state that may be required. */ -static void s3c_hsotg_initep(struct dwc2_hsotg *hsotg, - struct s3c_hsotg_ep *hs_ep, +static void dwc2_hsotg_initep(struct dwc2_hsotg *hsotg, + struct dwc2_hsotg_ep *hs_ep, int epnum, bool dir_in) { @@ -3287,7 +3360,7 @@ static void s3c_hsotg_initep(struct dwc2_hsotg *hsotg, hs_ep->parent = hsotg; hs_ep->ep.name = hs_ep->name; usb_ep_set_maxpacket_limit(&hs_ep->ep, epnum ? 1024 : EP0_MPS_LIMIT); - hs_ep->ep.ops = &s3c_hsotg_ep_ops; + hs_ep->ep.ops = &dwc2_hsotg_ep_ops; if (epnum == 0) { hs_ep->ep.caps.type_control = true; @@ -3310,19 +3383,19 @@ static void s3c_hsotg_initep(struct dwc2_hsotg *hsotg, if (using_dma(hsotg)) { u32 next = DXEPCTL_NEXTEP((epnum + 1) % 15); if (dir_in) - writel(next, hsotg->regs + DIEPCTL(epnum)); + dwc2_writel(next, hsotg->regs + DIEPCTL(epnum)); else - writel(next, hsotg->regs + DOEPCTL(epnum)); + dwc2_writel(next, hsotg->regs + DOEPCTL(epnum)); } } /** - * s3c_hsotg_hw_cfg - read HW configuration registers + * dwc2_hsotg_hw_cfg - read HW configuration registers * @param: The device state * * Read the USB core HW configuration registers */ -static int s3c_hsotg_hw_cfg(struct dwc2_hsotg *hsotg) +static int dwc2_hsotg_hw_cfg(struct dwc2_hsotg *hsotg) { u32 cfg; u32 ep_type; @@ -3330,41 +3403,41 @@ static int s3c_hsotg_hw_cfg(struct dwc2_hsotg *hsotg) /* check hardware configuration */ - cfg = readl(hsotg->regs + GHWCFG2); + cfg = dwc2_readl(hsotg->regs + GHWCFG2); hsotg->num_of_eps = (cfg >> GHWCFG2_NUM_DEV_EP_SHIFT) & 0xF; /* Add ep0 */ hsotg->num_of_eps++; - hsotg->eps_in[0] = devm_kzalloc(hsotg->dev, sizeof(struct s3c_hsotg_ep), + hsotg->eps_in[0] = devm_kzalloc(hsotg->dev, sizeof(struct dwc2_hsotg_ep), GFP_KERNEL); if (!hsotg->eps_in[0]) return -ENOMEM; - /* Same s3c_hsotg_ep is used in both directions for ep0 */ + /* Same dwc2_hsotg_ep is used in both directions for ep0 */ hsotg->eps_out[0] = hsotg->eps_in[0]; - cfg = readl(hsotg->regs + GHWCFG1); + cfg = dwc2_readl(hsotg->regs + GHWCFG1); for (i = 1, cfg >>= 2; i < hsotg->num_of_eps; i++, cfg >>= 2) { ep_type = cfg & 3; /* Direction in or both */ if (!(ep_type & 2)) { hsotg->eps_in[i] = devm_kzalloc(hsotg->dev, - sizeof(struct s3c_hsotg_ep), GFP_KERNEL); + sizeof(struct dwc2_hsotg_ep), GFP_KERNEL); if (!hsotg->eps_in[i]) return -ENOMEM; } /* Direction out or both */ if (!(ep_type & 1)) { hsotg->eps_out[i] = devm_kzalloc(hsotg->dev, - sizeof(struct s3c_hsotg_ep), GFP_KERNEL); + sizeof(struct dwc2_hsotg_ep), GFP_KERNEL); if (!hsotg->eps_out[i]) return -ENOMEM; } } - cfg = readl(hsotg->regs + GHWCFG3); + cfg = dwc2_readl(hsotg->regs + GHWCFG3); hsotg->fifo_mem = (cfg >> GHWCFG3_DFIFO_DEPTH_SHIFT); - cfg = readl(hsotg->regs + GHWCFG4); + cfg = dwc2_readl(hsotg->regs + GHWCFG4); hsotg->dedicated_fifos = (cfg >> GHWCFG4_DED_FIFO_SHIFT) & 1; dev_info(hsotg->dev, "EPs: %d, %s fifos, %d entries in SPRAM\n", @@ -3375,10 +3448,10 @@ static int s3c_hsotg_hw_cfg(struct dwc2_hsotg *hsotg) } /** - * s3c_hsotg_dump - dump state of the udc + * dwc2_hsotg_dump - dump state of the udc * @param: The device state */ -static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg) +static void dwc2_hsotg_dump(struct dwc2_hsotg *hsotg) { #ifdef DEBUG struct device *dev = hsotg->dev; @@ -3387,19 +3460,19 @@ static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg) int idx; dev_info(dev, "DCFG=0x%08x, DCTL=0x%08x, DIEPMSK=%08x\n", - readl(regs + DCFG), readl(regs + DCTL), - readl(regs + DIEPMSK)); + dwc2_readl(regs + DCFG), dwc2_readl(regs + DCTL), + dwc2_readl(regs + DIEPMSK)); dev_info(dev, "GAHBCFG=0x%08x, GHWCFG1=0x%08x\n", - readl(regs + GAHBCFG), readl(regs + GHWCFG1)); + dwc2_readl(regs + GAHBCFG), dwc2_readl(regs + GHWCFG1)); dev_info(dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n", - readl(regs + GRXFSIZ), readl(regs + GNPTXFSIZ)); + dwc2_readl(regs + GRXFSIZ), dwc2_readl(regs + GNPTXFSIZ)); /* show periodic fifo settings */ for (idx = 1; idx < hsotg->num_of_eps; idx++) { - val = readl(regs + DPTXFSIZN(idx)); + val = dwc2_readl(regs + DPTXFSIZN(idx)); dev_info(dev, "DPTx[%d] FSize=%d, StAddr=0x%08x\n", idx, val >> FIFOSIZE_DEPTH_SHIFT, val & FIFOSIZE_STARTADDR_MASK); @@ -3408,26 +3481,26 @@ static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg) for (idx = 0; idx < hsotg->num_of_eps; idx++) { dev_info(dev, "ep%d-in: EPCTL=0x%08x, SIZ=0x%08x, DMA=0x%08x\n", idx, - readl(regs + DIEPCTL(idx)), - readl(regs + DIEPTSIZ(idx)), - readl(regs + DIEPDMA(idx))); + dwc2_readl(regs + DIEPCTL(idx)), + dwc2_readl(regs + DIEPTSIZ(idx)), + dwc2_readl(regs + DIEPDMA(idx))); - val = readl(regs + DOEPCTL(idx)); + val = dwc2_readl(regs + DOEPCTL(idx)); dev_info(dev, "ep%d-out: EPCTL=0x%08x, SIZ=0x%08x, DMA=0x%08x\n", - idx, readl(regs + DOEPCTL(idx)), - readl(regs + DOEPTSIZ(idx)), - readl(regs + DOEPDMA(idx))); + idx, dwc2_readl(regs + DOEPCTL(idx)), + dwc2_readl(regs + DOEPTSIZ(idx)), + dwc2_readl(regs + DOEPDMA(idx))); } dev_info(dev, "DVBUSDIS=0x%08x, DVBUSPULSE=%08x\n", - readl(regs + DVBUSDIS), readl(regs + DVBUSPULSE)); + dwc2_readl(regs + DVBUSDIS), dwc2_readl(regs + DVBUSPULSE)); #endif } #ifdef CONFIG_OF -static void s3c_hsotg_of_probe(struct dwc2_hsotg *hsotg) +static void dwc2_hsotg_of_probe(struct dwc2_hsotg *hsotg) { struct device_node *np = hsotg->dev->of_node; u32 len = 0; @@ -3468,7 +3541,7 @@ rx_fifo: &hsotg->g_np_g_tx_fifo_sz); } #else -static inline void s3c_hsotg_of_probe(struct dwc2_hsotg *hsotg) { } +static inline void dwc2_hsotg_of_probe(struct dwc2_hsotg *hsotg) { } #endif /** @@ -3479,23 +3552,17 @@ static inline void s3c_hsotg_of_probe(struct dwc2_hsotg *hsotg) { } int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) { struct device *dev = hsotg->dev; - struct s3c_hsotg_plat *plat = dev->platform_data; int epnum; int ret; int i; u32 p_tx_fifo[] = DWC2_G_P_LEGACY_TX_FIFO_SIZE; - /* Set default UTMI width */ - hsotg->phyif = GUSBCFG_PHYIF16; - - s3c_hsotg_of_probe(hsotg); - /* Initialize to legacy fifo configuration values */ hsotg->g_rx_fifo_sz = 2048; hsotg->g_np_g_tx_fifo_sz = 1024; memcpy(&hsotg->g_tx_fifo_sz[1], p_tx_fifo, sizeof(p_tx_fifo)); /* Device tree specific probe */ - s3c_hsotg_of_probe(hsotg); + dwc2_hsotg_of_probe(hsotg); /* Dump fifo information */ dev_dbg(dev, "NonPeriodic TXFIFO size: %d\n", hsotg->g_np_g_tx_fifo_sz); @@ -3503,70 +3570,14 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) for (i = 0; i < MAX_EPS_CHANNELS; i++) dev_dbg(dev, "Periodic TXFIFO%2d size: %d\n", i, hsotg->g_tx_fifo_sz[i]); - /* - * If platform probe couldn't find a generic PHY or an old style - * USB PHY, fall back to pdata - */ - if (IS_ERR_OR_NULL(hsotg->phy) && IS_ERR_OR_NULL(hsotg->uphy)) { - plat = dev_get_platdata(dev); - if (!plat) { - dev_err(dev, - "no platform data or transceiver defined\n"); - return -EPROBE_DEFER; - } - hsotg->plat = plat; - } else if (hsotg->phy) { - /* - * If using the generic PHY framework, check if the PHY bus - * width is 8-bit and set the phyif appropriately. - */ - if (phy_get_bus_width(hsotg->phy) == 8) - hsotg->phyif = GUSBCFG_PHYIF8; - } - - hsotg->clk = devm_clk_get(dev, "otg"); - if (IS_ERR(hsotg->clk)) { - hsotg->clk = NULL; - dev_dbg(dev, "cannot get otg clock\n"); - } hsotg->gadget.max_speed = USB_SPEED_HIGH; - hsotg->gadget.ops = &s3c_hsotg_gadget_ops; + hsotg->gadget.ops = &dwc2_hsotg_gadget_ops; hsotg->gadget.name = dev_name(dev); if (hsotg->dr_mode == USB_DR_MODE_OTG) hsotg->gadget.is_otg = 1; - - /* reset the system */ - - ret = clk_prepare_enable(hsotg->clk); - if (ret) { - dev_err(dev, "failed to enable otg clk\n"); - goto err_clk; - } - - - /* regulators */ - - for (i = 0; i < ARRAY_SIZE(hsotg->supplies); i++) - hsotg->supplies[i].supply = s3c_hsotg_supply_names[i]; - - ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(hsotg->supplies), - hsotg->supplies); - if (ret) { - dev_err(dev, "failed to request supplies: %d\n", ret); - goto err_clk; - } - - ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies), - hsotg->supplies); - - if (ret) { - dev_err(dev, "failed to enable supplies: %d\n", ret); - goto err_clk; - } - - /* usb phy enable */ - s3c_hsotg_phy_enable(hsotg); + else if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) + hsotg->op_state = OTG_STATE_B_PERIPHERAL; /* * Force Device mode before initialization. @@ -3581,14 +3592,14 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) */ msleep(25); - s3c_hsotg_corereset(hsotg); - ret = s3c_hsotg_hw_cfg(hsotg); + dwc2_hsotg_corereset(hsotg); + ret = dwc2_hsotg_hw_cfg(hsotg); if (ret) { dev_err(hsotg->dev, "Hardware configuration failed: %d\n", ret); - goto err_clk; + return ret; } - s3c_hsotg_init(hsotg); + dwc2_hsotg_init(hsotg); /* Switch back to default configuration */ __bic32(hsotg->regs + GUSBCFG, GUSBCFG_FORCEDEVMODE); @@ -3597,35 +3608,28 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) DWC2_CTRL_BUFF_SIZE, GFP_KERNEL); if (!hsotg->ctrl_buff) { dev_err(dev, "failed to allocate ctrl request buff\n"); - ret = -ENOMEM; - goto err_supplies; + return -ENOMEM; } hsotg->ep0_buff = devm_kzalloc(hsotg->dev, DWC2_CTRL_BUFF_SIZE, GFP_KERNEL); if (!hsotg->ep0_buff) { dev_err(dev, "failed to allocate ctrl reply buff\n"); - ret = -ENOMEM; - goto err_supplies; + return -ENOMEM; } - ret = devm_request_irq(hsotg->dev, irq, s3c_hsotg_irq, IRQF_SHARED, + ret = devm_request_irq(hsotg->dev, irq, dwc2_hsotg_irq, IRQF_SHARED, dev_name(hsotg->dev), hsotg); if (ret < 0) { - s3c_hsotg_phy_disable(hsotg); - clk_disable_unprepare(hsotg->clk); - regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), - hsotg->supplies); dev_err(dev, "cannot claim IRQ for gadget\n"); - goto err_supplies; + return ret; } /* hsotg->num_of_eps holds number of EPs other than ep0 */ if (hsotg->num_of_eps == 0) { dev_err(dev, "wrong number of EPs (zero)\n"); - ret = -EINVAL; - goto err_supplies; + return -EINVAL; } /* setup endpoint information */ @@ -3635,71 +3639,49 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) /* allocate EP0 request */ - hsotg->ctrl_req = s3c_hsotg_ep_alloc_request(&hsotg->eps_out[0]->ep, + hsotg->ctrl_req = dwc2_hsotg_ep_alloc_request(&hsotg->eps_out[0]->ep, GFP_KERNEL); if (!hsotg->ctrl_req) { dev_err(dev, "failed to allocate ctrl req\n"); - ret = -ENOMEM; - goto err_supplies; + return -ENOMEM; } /* initialise the endpoints now the core has been initialised */ for (epnum = 0; epnum < hsotg->num_of_eps; epnum++) { if (hsotg->eps_in[epnum]) - s3c_hsotg_initep(hsotg, hsotg->eps_in[epnum], + dwc2_hsotg_initep(hsotg, hsotg->eps_in[epnum], epnum, 1); if (hsotg->eps_out[epnum]) - s3c_hsotg_initep(hsotg, hsotg->eps_out[epnum], + dwc2_hsotg_initep(hsotg, hsotg->eps_out[epnum], epnum, 0); } - /* disable power and clock */ - s3c_hsotg_phy_disable(hsotg); - - ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), - hsotg->supplies); - if (ret) { - dev_err(dev, "failed to disable supplies: %d\n", ret); - goto err_supplies; - } - ret = usb_add_gadget_udc(dev, &hsotg->gadget); if (ret) - goto err_supplies; + return ret; - s3c_hsotg_dump(hsotg); + dwc2_hsotg_dump(hsotg); return 0; - -err_supplies: - s3c_hsotg_phy_disable(hsotg); -err_clk: - clk_disable_unprepare(hsotg->clk); - - return ret; } /** - * s3c_hsotg_remove - remove function for hsotg driver + * dwc2_hsotg_remove - remove function for hsotg driver * @pdev: The platform information for the driver */ -int s3c_hsotg_remove(struct dwc2_hsotg *hsotg) +int dwc2_hsotg_remove(struct dwc2_hsotg *hsotg) { usb_del_gadget_udc(&hsotg->gadget); - clk_disable_unprepare(hsotg->clk); return 0; } -int s3c_hsotg_suspend(struct dwc2_hsotg *hsotg) +int dwc2_hsotg_suspend(struct dwc2_hsotg *hsotg) { unsigned long flags; - int ret = 0; if (hsotg->lx_state != DWC2_L0) - return ret; - - mutex_lock(&hsotg->init_mutex); + return 0; if (hsotg->driver) { int ep; @@ -3709,57 +3691,39 @@ int s3c_hsotg_suspend(struct dwc2_hsotg *hsotg) spin_lock_irqsave(&hsotg->lock, flags); if (hsotg->enabled) - s3c_hsotg_core_disconnect(hsotg); - s3c_hsotg_disconnect(hsotg); + dwc2_hsotg_core_disconnect(hsotg); + dwc2_hsotg_disconnect(hsotg); hsotg->gadget.speed = USB_SPEED_UNKNOWN; spin_unlock_irqrestore(&hsotg->lock, flags); - s3c_hsotg_phy_disable(hsotg); - for (ep = 0; ep < hsotg->num_of_eps; ep++) { if (hsotg->eps_in[ep]) - s3c_hsotg_ep_disable(&hsotg->eps_in[ep]->ep); + dwc2_hsotg_ep_disable(&hsotg->eps_in[ep]->ep); if (hsotg->eps_out[ep]) - s3c_hsotg_ep_disable(&hsotg->eps_out[ep]->ep); + dwc2_hsotg_ep_disable(&hsotg->eps_out[ep]->ep); } - - ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), - hsotg->supplies); - clk_disable(hsotg->clk); } - mutex_unlock(&hsotg->init_mutex); - - return ret; + return 0; } -int s3c_hsotg_resume(struct dwc2_hsotg *hsotg) +int dwc2_hsotg_resume(struct dwc2_hsotg *hsotg) { unsigned long flags; - int ret = 0; if (hsotg->lx_state == DWC2_L2) - return ret; - - mutex_lock(&hsotg->init_mutex); + return 0; if (hsotg->driver) { dev_info(hsotg->dev, "resuming usb gadget %s\n", hsotg->driver->driver.name); - clk_enable(hsotg->clk); - ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies), - hsotg->supplies); - - s3c_hsotg_phy_enable(hsotg); - spin_lock_irqsave(&hsotg->lock, flags); - s3c_hsotg_core_init_disconnected(hsotg, false); + dwc2_hsotg_core_init_disconnected(hsotg, false); if (hsotg->enabled) - s3c_hsotg_core_connect(hsotg); + dwc2_hsotg_core_connect(hsotg); spin_unlock_irqrestore(&hsotg->lock, flags); } - mutex_unlock(&hsotg->init_mutex); - return ret; + return 0; } diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index f845c41..e79baf7 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -80,10 +80,10 @@ static void dwc2_dump_channel_info(struct dwc2_hsotg *hsotg, if (chan == NULL) return; - hcchar = readl(hsotg->regs + HCCHAR(chan->hc_num)); - hcsplt = readl(hsotg->regs + HCSPLT(chan->hc_num)); - hctsiz = readl(hsotg->regs + HCTSIZ(chan->hc_num)); - hc_dma = readl(hsotg->regs + HCDMA(chan->hc_num)); + hcchar = dwc2_readl(hsotg->regs + HCCHAR(chan->hc_num)); + hcsplt = dwc2_readl(hsotg->regs + HCSPLT(chan->hc_num)); + hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chan->hc_num)); + hc_dma = dwc2_readl(hsotg->regs + HCDMA(chan->hc_num)); dev_dbg(hsotg->dev, " Assigned to channel %p:\n", chan); dev_dbg(hsotg->dev, " hcchar 0x%08x, hcsplt 0x%08x\n", @@ -134,7 +134,7 @@ static void dwc2_kill_urbs_in_qh_list(struct dwc2_hsotg *hsotg, list_for_each_entry_safe(qh, qh_tmp, qh_list, qh_list_entry) { list_for_each_entry_safe(qtd, qtd_tmp, &qh->qtd_list, qtd_list_entry) { - dwc2_host_complete(hsotg, qtd, -ETIMEDOUT); + dwc2_host_complete(hsotg, qtd, -ECONNRESET); dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh); } } @@ -207,7 +207,7 @@ void dwc2_hcd_start(struct dwc2_hsotg *hsotg) */ hprt0 = dwc2_read_hprt0(hsotg); hprt0 |= HPRT0_RST; - writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hprt0, hsotg->regs + HPRT0); } queue_delayed_work(hsotg->wq_otg, &hsotg->start_work, @@ -228,11 +228,11 @@ static void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg) channel = hsotg->hc_ptr_array[i]; if (!list_empty(&channel->hc_list_entry)) continue; - hcchar = readl(hsotg->regs + HCCHAR(i)); + hcchar = dwc2_readl(hsotg->regs + HCCHAR(i)); if (hcchar & HCCHAR_CHENA) { hcchar &= ~(HCCHAR_CHENA | HCCHAR_EPDIR); hcchar |= HCCHAR_CHDIS; - writel(hcchar, hsotg->regs + HCCHAR(i)); + dwc2_writel(hcchar, hsotg->regs + HCCHAR(i)); } } } @@ -241,11 +241,11 @@ static void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg) channel = hsotg->hc_ptr_array[i]; if (!list_empty(&channel->hc_list_entry)) continue; - hcchar = readl(hsotg->regs + HCCHAR(i)); + hcchar = dwc2_readl(hsotg->regs + HCCHAR(i)); if (hcchar & HCCHAR_CHENA) { /* Halt the channel */ hcchar |= HCCHAR_CHDIS; - writel(hcchar, hsotg->regs + HCCHAR(i)); + dwc2_writel(hcchar, hsotg->regs + HCCHAR(i)); } dwc2_hc_cleanup(hsotg, channel); @@ -287,11 +287,11 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg) * interrupt mask and status bits and disabling subsequent host * channel interrupts. */ - intr = readl(hsotg->regs + GINTMSK); + intr = dwc2_readl(hsotg->regs + GINTMSK); intr &= ~(GINTSTS_NPTXFEMP | GINTSTS_PTXFEMP | GINTSTS_HCHINT); - writel(intr, hsotg->regs + GINTMSK); + dwc2_writel(intr, hsotg->regs + GINTMSK); intr = GINTSTS_NPTXFEMP | GINTSTS_PTXFEMP | GINTSTS_HCHINT; - writel(intr, hsotg->regs + GINTSTS); + dwc2_writel(intr, hsotg->regs + GINTSTS); /* * Turn off the vbus power only if the core has transitioned to device @@ -301,7 +301,7 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg) if (dwc2_is_device_mode(hsotg)) { if (hsotg->op_state != OTG_STATE_A_SUSPEND) { dev_dbg(hsotg->dev, "Disconnect: PortPower off\n"); - writel(0, hsotg->regs + HPRT0); + dwc2_writel(0, hsotg->regs + HPRT0); } dwc2_disable_host_interrupts(hsotg); @@ -354,7 +354,7 @@ void dwc2_hcd_stop(struct dwc2_hsotg *hsotg) /* Turn off the vbus power */ dev_dbg(hsotg->dev, "PortPower off\n"); - writel(0, hsotg->regs + HPRT0); + dwc2_writel(0, hsotg->regs + HPRT0); } /* Caller must hold driver lock */ @@ -378,7 +378,7 @@ static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg, if ((dev_speed == USB_SPEED_LOW) && (hsotg->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED) && (hsotg->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_UTMI)) { - u32 hprt0 = readl(hsotg->regs + HPRT0); + u32 hprt0 = dwc2_readl(hsotg->regs + HPRT0); u32 prtspd = (hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT; if (prtspd == HPRT0_SPD_FULL_SPEED) @@ -397,7 +397,7 @@ static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg, return retval; } - intr_mask = readl(hsotg->regs + GINTMSK); + intr_mask = dwc2_readl(hsotg->regs + GINTMSK); if (!(intr_mask & GINTSTS_SOF)) { enum dwc2_transaction_type tr_type; @@ -1070,7 +1070,7 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg) if (dbg_perio()) dev_vdbg(hsotg->dev, "Queue periodic transactions\n"); - tx_status = readl(hsotg->regs + HPTXSTS); + tx_status = dwc2_readl(hsotg->regs + HPTXSTS); qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT; fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >> @@ -1085,7 +1085,7 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg) qh_ptr = hsotg->periodic_sched_assigned.next; while (qh_ptr != &hsotg->periodic_sched_assigned) { - tx_status = readl(hsotg->regs + HPTXSTS); + tx_status = dwc2_readl(hsotg->regs + HPTXSTS); qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT; if (qspcavail == 0) { @@ -1145,7 +1145,7 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg) } if (hsotg->core_params->dma_enable <= 0) { - tx_status = readl(hsotg->regs + HPTXSTS); + tx_status = dwc2_readl(hsotg->regs + HPTXSTS); qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT; fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >> @@ -1168,9 +1168,9 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg) * level to ensure that new requests are loaded as * soon as possible.) */ - gintmsk = readl(hsotg->regs + GINTMSK); + gintmsk = dwc2_readl(hsotg->regs + GINTMSK); gintmsk |= GINTSTS_PTXFEMP; - writel(gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(gintmsk, hsotg->regs + GINTMSK); } else { /* * Disable the Tx FIFO empty interrupt since there are @@ -1179,9 +1179,9 @@ static void dwc2_process_periodic_channels(struct dwc2_hsotg *hsotg) * handlers to queue more transactions as transfer * states change. */ - gintmsk = readl(hsotg->regs + GINTMSK); + gintmsk = dwc2_readl(hsotg->regs + GINTMSK); gintmsk &= ~GINTSTS_PTXFEMP; - writel(gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(gintmsk, hsotg->regs + GINTMSK); } } } @@ -1210,7 +1210,7 @@ static void dwc2_process_non_periodic_channels(struct dwc2_hsotg *hsotg) dev_vdbg(hsotg->dev, "Queue non-periodic transactions\n"); - tx_status = readl(hsotg->regs + GNPTXSTS); + tx_status = dwc2_readl(hsotg->regs + GNPTXSTS); qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT; fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >> @@ -1233,7 +1233,7 @@ static void dwc2_process_non_periodic_channels(struct dwc2_hsotg *hsotg) * available in the request queue or the Tx FIFO */ do { - tx_status = readl(hsotg->regs + GNPTXSTS); + tx_status = dwc2_readl(hsotg->regs + GNPTXSTS); qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT; if (hsotg->core_params->dma_enable <= 0 && qspcavail == 0) { @@ -1270,7 +1270,7 @@ next: } while (hsotg->non_periodic_qh_ptr != orig_qh_ptr); if (hsotg->core_params->dma_enable <= 0) { - tx_status = readl(hsotg->regs + GNPTXSTS); + tx_status = dwc2_readl(hsotg->regs + GNPTXSTS); qspcavail = (tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT; fspcavail = (tx_status & TXSTS_FSPCAVAIL_MASK) >> @@ -1290,9 +1290,9 @@ next: * level to ensure that new requests are loaded as * soon as possible.) */ - gintmsk = readl(hsotg->regs + GINTMSK); + gintmsk = dwc2_readl(hsotg->regs + GINTMSK); gintmsk |= GINTSTS_NPTXFEMP; - writel(gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(gintmsk, hsotg->regs + GINTMSK); } else { /* * Disable the Tx FIFO empty interrupt since there are @@ -1301,9 +1301,9 @@ next: * handlers to queue more transactions as transfer * states change. */ - gintmsk = readl(hsotg->regs + GINTMSK); + gintmsk = dwc2_readl(hsotg->regs + GINTMSK); gintmsk &= ~GINTSTS_NPTXFEMP; - writel(gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(gintmsk, hsotg->regs + GINTMSK); } } } @@ -1341,10 +1341,10 @@ void dwc2_hcd_queue_transactions(struct dwc2_hsotg *hsotg, * Ensure NP Tx FIFO empty interrupt is disabled when * there are no non-periodic transfers to process */ - u32 gintmsk = readl(hsotg->regs + GINTMSK); + u32 gintmsk = dwc2_readl(hsotg->regs + GINTMSK); gintmsk &= ~GINTSTS_NPTXFEMP; - writel(gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(gintmsk, hsotg->regs + GINTMSK); } } } @@ -1355,10 +1355,11 @@ static void dwc2_conn_id_status_change(struct work_struct *work) wf_otg); u32 count = 0; u32 gotgctl; + unsigned long flags; dev_dbg(hsotg->dev, "%s()\n", __func__); - gotgctl = readl(hsotg->regs + GOTGCTL); + gotgctl = dwc2_readl(hsotg->regs + GOTGCTL); dev_dbg(hsotg->dev, "gotgctl=%0x\n", gotgctl); dev_dbg(hsotg->dev, "gotgctl.b.conidsts=%d\n", !!(gotgctl & GOTGCTL_CONID_B)); @@ -1382,8 +1383,10 @@ static void dwc2_conn_id_status_change(struct work_struct *work) hsotg->op_state = OTG_STATE_B_PERIPHERAL; dwc2_core_init(hsotg, false, -1); dwc2_enable_global_interrupts(hsotg); - s3c_hsotg_core_init_disconnected(hsotg, false); - s3c_hsotg_core_connect(hsotg); + spin_lock_irqsave(&hsotg->lock, flags); + dwc2_hsotg_core_init_disconnected(hsotg, false); + spin_unlock_irqrestore(&hsotg->lock, flags); + dwc2_hsotg_core_connect(hsotg); } else { /* A-Device connector (Host Mode) */ dev_dbg(hsotg->dev, "connId A\n"); @@ -1421,10 +1424,11 @@ static void dwc2_wakeup_detected(unsigned long data) hprt0 = dwc2_read_hprt0(hsotg); dev_dbg(hsotg->dev, "Resume: HPRT0=%0x\n", hprt0); hprt0 &= ~HPRT0_RES; - writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hprt0, hsotg->regs + HPRT0); dev_dbg(hsotg->dev, "Clear Resume: HPRT0=%0x\n", - readl(hsotg->regs + HPRT0)); + dwc2_readl(hsotg->regs + HPRT0)); + hsotg->bus_suspended = 0; dwc2_hcd_rem_wakeup(hsotg); /* Change to L0 state */ @@ -1451,30 +1455,35 @@ static void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) spin_lock_irqsave(&hsotg->lock, flags); if (windex == hsotg->otg_port && dwc2_host_is_b_hnp_enabled(hsotg)) { - gotgctl = readl(hsotg->regs + GOTGCTL); + gotgctl = dwc2_readl(hsotg->regs + GOTGCTL); gotgctl |= GOTGCTL_HSTSETHNPEN; - writel(gotgctl, hsotg->regs + GOTGCTL); + dwc2_writel(gotgctl, hsotg->regs + GOTGCTL); hsotg->op_state = OTG_STATE_A_SUSPEND; } hprt0 = dwc2_read_hprt0(hsotg); hprt0 |= HPRT0_SUSP; - writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hprt0, hsotg->regs + HPRT0); - /* Update lx_state */ - hsotg->lx_state = DWC2_L2; + hsotg->bus_suspended = 1; - /* Suspend the Phy Clock */ - pcgctl = readl(hsotg->regs + PCGCTL); - pcgctl |= PCGCTL_STOPPCLK; - writel(pcgctl, hsotg->regs + PCGCTL); - udelay(10); + /* + * If hibernation is supported, Phy clock will be suspended + * after registers are backuped. + */ + if (!hsotg->core_params->hibernation) { + /* Suspend the Phy Clock */ + pcgctl = dwc2_readl(hsotg->regs + PCGCTL); + pcgctl |= PCGCTL_STOPPCLK; + dwc2_writel(pcgctl, hsotg->regs + PCGCTL); + udelay(10); + } /* For HNP the bus must be suspended for at least 200ms */ if (dwc2_host_is_b_hnp_enabled(hsotg)) { - pcgctl = readl(hsotg->regs + PCGCTL); + pcgctl = dwc2_readl(hsotg->regs + PCGCTL); pcgctl &= ~PCGCTL_STOPPCLK; - writel(pcgctl, hsotg->regs + PCGCTL); + dwc2_writel(pcgctl, hsotg->regs + PCGCTL); spin_unlock_irqrestore(&hsotg->lock, flags); @@ -1484,6 +1493,44 @@ static void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex) } } +/* Must NOT be called with interrupt disabled or spinlock held */ +static void dwc2_port_resume(struct dwc2_hsotg *hsotg) +{ + unsigned long flags; + u32 hprt0; + u32 pcgctl; + + spin_lock_irqsave(&hsotg->lock, flags); + + /* + * If hibernation is supported, Phy clock is already resumed + * after registers restore. + */ + if (!hsotg->core_params->hibernation) { + pcgctl = dwc2_readl(hsotg->regs + PCGCTL); + pcgctl &= ~PCGCTL_STOPPCLK; + dwc2_writel(pcgctl, hsotg->regs + PCGCTL); + spin_unlock_irqrestore(&hsotg->lock, flags); + usleep_range(20000, 40000); + spin_lock_irqsave(&hsotg->lock, flags); + } + + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 |= HPRT0_RES; + hprt0 &= ~HPRT0_SUSP; + dwc2_writel(hprt0, hsotg->regs + HPRT0); + spin_unlock_irqrestore(&hsotg->lock, flags); + + msleep(USB_RESUME_TIMEOUT); + + spin_lock_irqsave(&hsotg->lock, flags); + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 &= ~(HPRT0_RES | HPRT0_SUSP); + dwc2_writel(hprt0, hsotg->regs + HPRT0); + hsotg->bus_suspended = 0; + spin_unlock_irqrestore(&hsotg->lock, flags); +} + /* Handles hub class-specific requests */ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, u16 wvalue, u16 windex, char *buf, u16 wlength) @@ -1523,23 +1570,15 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, "ClearPortFeature USB_PORT_FEAT_ENABLE\n"); hprt0 = dwc2_read_hprt0(hsotg); hprt0 |= HPRT0_ENA; - writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hprt0, hsotg->regs + HPRT0); break; case USB_PORT_FEAT_SUSPEND: dev_dbg(hsotg->dev, "ClearPortFeature USB_PORT_FEAT_SUSPEND\n"); - writel(0, hsotg->regs + PCGCTL); - usleep_range(20000, 40000); - - hprt0 = dwc2_read_hprt0(hsotg); - hprt0 |= HPRT0_RES; - writel(hprt0, hsotg->regs + HPRT0); - hprt0 &= ~HPRT0_SUSP; - msleep(USB_RESUME_TIMEOUT); - hprt0 &= ~HPRT0_RES; - writel(hprt0, hsotg->regs + HPRT0); + if (hsotg->bus_suspended) + dwc2_port_resume(hsotg); break; case USB_PORT_FEAT_POWER: @@ -1547,7 +1586,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, "ClearPortFeature USB_PORT_FEAT_POWER\n"); hprt0 = dwc2_read_hprt0(hsotg); hprt0 &= ~HPRT0_PWR; - writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hprt0, hsotg->regs + HPRT0); break; case USB_PORT_FEAT_INDICATOR: @@ -1668,7 +1707,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, break; } - hprt0 = readl(hsotg->regs + HPRT0); + hprt0 = dwc2_readl(hsotg->regs + HPRT0); dev_vdbg(hsotg->dev, " HPRT0: 0x%08x\n", hprt0); if (hprt0 & HPRT0_CONNSTS) @@ -1733,18 +1772,18 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, "SetPortFeature - USB_PORT_FEAT_POWER\n"); hprt0 = dwc2_read_hprt0(hsotg); hprt0 |= HPRT0_PWR; - writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hprt0, hsotg->regs + HPRT0); break; case USB_PORT_FEAT_RESET: hprt0 = dwc2_read_hprt0(hsotg); dev_dbg(hsotg->dev, "SetPortFeature - USB_PORT_FEAT_RESET\n"); - pcgctl = readl(hsotg->regs + PCGCTL); + pcgctl = dwc2_readl(hsotg->regs + PCGCTL); pcgctl &= ~(PCGCTL_ENBL_SLEEP_GATING | PCGCTL_STOPPCLK); - writel(pcgctl, hsotg->regs + PCGCTL); + dwc2_writel(pcgctl, hsotg->regs + PCGCTL); /* ??? Original driver does this */ - writel(0, hsotg->regs + PCGCTL); + dwc2_writel(0, hsotg->regs + PCGCTL); hprt0 = dwc2_read_hprt0(hsotg); /* Clear suspend bit if resetting from suspend state */ @@ -1759,13 +1798,13 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, hprt0 |= HPRT0_PWR | HPRT0_RST; dev_dbg(hsotg->dev, "In host mode, hprt0=%08x\n", hprt0); - writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hprt0, hsotg->regs + HPRT0); } /* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */ usleep_range(50000, 70000); hprt0 &= ~HPRT0_RST; - writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hprt0, hsotg->regs + HPRT0); hsotg->lx_state = DWC2_L0; /* Now back to On state */ break; @@ -1781,7 +1820,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, "SetPortFeature - USB_PORT_FEAT_TEST\n"); hprt0 &= ~HPRT0_TSTCTL_MASK; hprt0 |= (windex >> 8) << HPRT0_TSTCTL_SHIFT; - writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hprt0, hsotg->regs + HPRT0); break; default: @@ -1838,7 +1877,7 @@ static int dwc2_hcd_is_status_changed(struct dwc2_hsotg *hsotg, int port) int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg) { - u32 hfnum = readl(hsotg->regs + HFNUM); + u32 hfnum = dwc2_readl(hsotg->regs + HFNUM); #ifdef DWC2_DEBUG_SOF dev_vdbg(hsotg->dev, "DWC OTG HCD GET FRAME NUMBER %d\n", @@ -1941,11 +1980,11 @@ void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg) if (chan->xfer_started) { u32 hfnum, hcchar, hctsiz, hcint, hcintmsk; - hfnum = readl(hsotg->regs + HFNUM); - hcchar = readl(hsotg->regs + HCCHAR(i)); - hctsiz = readl(hsotg->regs + HCTSIZ(i)); - hcint = readl(hsotg->regs + HCINT(i)); - hcintmsk = readl(hsotg->regs + HCINTMSK(i)); + hfnum = dwc2_readl(hsotg->regs + HFNUM); + hcchar = dwc2_readl(hsotg->regs + HCCHAR(i)); + hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(i)); + hcint = dwc2_readl(hsotg->regs + HCINT(i)); + hcintmsk = dwc2_readl(hsotg->regs + HCINTMSK(i)); dev_dbg(hsotg->dev, " hfnum: 0x%08x\n", hfnum); dev_dbg(hsotg->dev, " hcchar: 0x%08x\n", hcchar); dev_dbg(hsotg->dev, " hctsiz: 0x%08x\n", hctsiz); @@ -1993,12 +2032,12 @@ void dwc2_hcd_dump_state(struct dwc2_hsotg *hsotg) dev_dbg(hsotg->dev, " periodic_channels: %d\n", hsotg->periodic_channels); dev_dbg(hsotg->dev, " periodic_usecs: %d\n", hsotg->periodic_usecs); - np_tx_status = readl(hsotg->regs + GNPTXSTS); + np_tx_status = dwc2_readl(hsotg->regs + GNPTXSTS); dev_dbg(hsotg->dev, " NP Tx Req Queue Space Avail: %d\n", (np_tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT); dev_dbg(hsotg->dev, " NP Tx FIFO Space Avail: %d\n", (np_tx_status & TXSTS_FSPCAVAIL_MASK) >> TXSTS_FSPCAVAIL_SHIFT); - p_tx_status = readl(hsotg->regs + HPTXSTS); + p_tx_status = dwc2_readl(hsotg->regs + HPTXSTS); dev_dbg(hsotg->dev, " P Tx Req Queue Space Avail: %d\n", (p_tx_status & TXSTS_QSPCAVAIL_MASK) >> TXSTS_QSPCAVAIL_SHIFT); dev_dbg(hsotg->dev, " P Tx FIFO Space Avail: %d\n", @@ -2194,11 +2233,6 @@ void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd, usb_pipein(urb->pipe) ? "IN" : "OUT", status, urb->actual_length); - if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS && dbg_perio()) { - for (i = 0; i < urb->number_of_packets; i++) - dev_vdbg(hsotg->dev, " ISO Desc %d status %d\n", - i, urb->iso_frame_desc[i].status); - } if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { urb->error_count = dwc2_hcd_urb_get_error_count(qtd->urb); @@ -2211,6 +2245,12 @@ void dwc2_host_complete(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd, } } + if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS && dbg_perio()) { + for (i = 0; i < urb->number_of_packets; i++) + dev_vdbg(hsotg->dev, " ISO Desc %d status %d\n", + i, urb->iso_frame_desc[i].status); + } + urb->status = status; if (!status) { if ((urb->transfer_flags & URB_SHORT_NOT_OK) && @@ -2262,7 +2302,7 @@ static void dwc2_hcd_reset_func(struct work_struct *work) dev_dbg(hsotg->dev, "USB RESET function called\n"); hprt0 = dwc2_read_hprt0(hsotg); hprt0 &= ~HPRT0_RST; - writel(hprt0, hsotg->regs + HPRT0); + dwc2_writel(hprt0, hsotg->regs + HPRT0); hsotg->flags.b.port_reset_change = 1; } @@ -2286,8 +2326,9 @@ static int _dwc2_hcd_start(struct usb_hcd *hcd) dev_dbg(hsotg->dev, "DWC OTG HCD START\n"); spin_lock_irqsave(&hsotg->lock, flags); - + hsotg->lx_state = DWC2_L0; hcd->state = HC_STATE_RUNNING; + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); if (dwc2_is_device_mode(hsotg)) { spin_unlock_irqrestore(&hsotg->lock, flags); @@ -2316,8 +2357,19 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd) struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); unsigned long flags; + /* Turn off all host-specific interrupts */ + dwc2_disable_host_interrupts(hsotg); + + /* Wait for interrupt processing to finish */ + synchronize_irq(hcd->irq); + spin_lock_irqsave(&hsotg->lock, flags); + /* Ensure hcd is disconnected */ + dwc2_hcd_disconnect(hsotg); dwc2_hcd_stop(hsotg); + hsotg->lx_state = DWC2_L3; + hcd->state = HC_STATE_HALT; + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); spin_unlock_irqrestore(&hsotg->lock, flags); usleep_range(1000, 3000); @@ -2326,17 +2378,125 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd) static int _dwc2_hcd_suspend(struct usb_hcd *hcd) { struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); + unsigned long flags; + int ret = 0; + u32 hprt0; + + spin_lock_irqsave(&hsotg->lock, flags); + + if (hsotg->lx_state != DWC2_L0) + goto unlock; + + if (!HCD_HW_ACCESSIBLE(hcd)) + goto unlock; + + if (!hsotg->core_params->hibernation) + goto skip_power_saving; + /* + * Drive USB suspend and disable port Power + * if usb bus is not suspended. + */ + if (!hsotg->bus_suspended) { + hprt0 = dwc2_read_hprt0(hsotg); + hprt0 |= HPRT0_SUSP; + hprt0 &= ~HPRT0_PWR; + dwc2_writel(hprt0, hsotg->regs + HPRT0); + } + + /* Enter hibernation */ + ret = dwc2_enter_hibernation(hsotg); + if (ret) { + if (ret != -ENOTSUPP) + dev_err(hsotg->dev, + "enter hibernation failed\n"); + goto skip_power_saving; + } + + /* Ask phy to be suspended */ + if (!IS_ERR_OR_NULL(hsotg->uphy)) { + spin_unlock_irqrestore(&hsotg->lock, flags); + usb_phy_set_suspend(hsotg->uphy, true); + spin_lock_irqsave(&hsotg->lock, flags); + } + + /* After entering hibernation, hardware is no more accessible */ + clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + +skip_power_saving: hsotg->lx_state = DWC2_L2; - return 0; +unlock: + spin_unlock_irqrestore(&hsotg->lock, flags); + + return ret; } static int _dwc2_hcd_resume(struct usb_hcd *hcd) { struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&hsotg->lock, flags); + + if (hsotg->lx_state != DWC2_L2) + goto unlock; + + if (!hsotg->core_params->hibernation) { + hsotg->lx_state = DWC2_L0; + goto unlock; + } + + /* + * Set HW accessible bit before powering on the controller + * since an interrupt may rise. + */ + set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags); + + /* + * Enable power if not already done. + * This must not be spinlocked since duration + * of this call is unknown. + */ + if (!IS_ERR_OR_NULL(hsotg->uphy)) { + spin_unlock_irqrestore(&hsotg->lock, flags); + usb_phy_set_suspend(hsotg->uphy, false); + spin_lock_irqsave(&hsotg->lock, flags); + } + + /* Exit hibernation */ + ret = dwc2_exit_hibernation(hsotg, true); + if (ret && (ret != -ENOTSUPP)) + dev_err(hsotg->dev, "exit hibernation failed\n"); hsotg->lx_state = DWC2_L0; - return 0; + + spin_unlock_irqrestore(&hsotg->lock, flags); + + if (hsotg->bus_suspended) { + spin_lock_irqsave(&hsotg->lock, flags); + hsotg->flags.b.port_suspend_change = 1; + spin_unlock_irqrestore(&hsotg->lock, flags); + dwc2_port_resume(hsotg); + } else { + /* Wait for controller to correctly update D+/D- level */ + usleep_range(3000, 5000); + + /* + * Clear Port Enable and Port Status changes. + * Enable Port Power. + */ + dwc2_writel(HPRT0_PWR | HPRT0_CONNDET | + HPRT0_ENACHG, hsotg->regs + HPRT0); + /* Wait for controller to detect Port Connect */ + usleep_range(5000, 7000); + } + + return ret; +unlock: + spin_unlock_irqrestore(&hsotg->lock, flags); + + return ret; } /* Returns the current frame number */ @@ -2790,17 +2950,17 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg) hsotg->status_buf = NULL; } - ahbcfg = readl(hsotg->regs + GAHBCFG); + ahbcfg = dwc2_readl(hsotg->regs + GAHBCFG); /* Disable all interrupts */ ahbcfg &= ~GAHBCFG_GLBL_INTR_EN; - writel(ahbcfg, hsotg->regs + GAHBCFG); - writel(0, hsotg->regs + GINTMSK); + dwc2_writel(ahbcfg, hsotg->regs + GAHBCFG); + dwc2_writel(0, hsotg->regs + GINTMSK); if (hsotg->hw_params.snpsid >= DWC2_CORE_REV_3_00a) { - dctl = readl(hsotg->regs + DCTL); + dctl = dwc2_readl(hsotg->regs + DCTL); dctl |= DCTL_SFTDISCON; - writel(dctl, hsotg->regs + DCTL); + dwc2_writel(dctl, hsotg->regs + DCTL); } if (hsotg->wq_otg) { @@ -2841,7 +3001,7 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq) retval = -ENOMEM; - hcfg = readl(hsotg->regs + HCFG); + hcfg = dwc2_readl(hsotg->regs + HCFG); dev_dbg(hsotg->dev, "hcfg=%08x\n", hcfg); #ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h index fc10549..f105bad 100644 --- a/drivers/usb/dwc2/hcd.h +++ b/drivers/usb/dwc2/hcd.h @@ -371,10 +371,10 @@ static inline struct usb_hcd *dwc2_hsotg_to_hcd(struct dwc2_hsotg *hsotg) */ static inline void disable_hc_int(struct dwc2_hsotg *hsotg, int chnum, u32 intr) { - u32 mask = readl(hsotg->regs + HCINTMSK(chnum)); + u32 mask = dwc2_readl(hsotg->regs + HCINTMSK(chnum)); mask &= ~intr; - writel(mask, hsotg->regs + HCINTMSK(chnum)); + dwc2_writel(mask, hsotg->regs + HCINTMSK(chnum)); } /* @@ -382,11 +382,11 @@ static inline void disable_hc_int(struct dwc2_hsotg *hsotg, int chnum, u32 intr) */ static inline int dwc2_is_host_mode(struct dwc2_hsotg *hsotg) { - return (readl(hsotg->regs + GINTSTS) & GINTSTS_CURMODE_HOST) != 0; + return (dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_CURMODE_HOST) != 0; } static inline int dwc2_is_device_mode(struct dwc2_hsotg *hsotg) { - return (readl(hsotg->regs + GINTSTS) & GINTSTS_CURMODE_HOST) == 0; + return (dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_CURMODE_HOST) == 0; } /* @@ -395,7 +395,7 @@ static inline int dwc2_is_device_mode(struct dwc2_hsotg *hsotg) */ static inline u32 dwc2_read_hprt0(struct dwc2_hsotg *hsotg) { - u32 hprt0 = readl(hsotg->regs + HPRT0); + u32 hprt0 = dwc2_readl(hsotg->regs + HPRT0); hprt0 &= ~(HPRT0_ENA | HPRT0_CONNDET | HPRT0_ENACHG | HPRT0_OVRCURRCHG); return hprt0; @@ -580,7 +580,8 @@ static inline u16 dwc2_micro_frame_num(u16 frame) */ static inline u32 dwc2_read_core_intr(struct dwc2_hsotg *hsotg) { - return readl(hsotg->regs + GINTSTS) & readl(hsotg->regs + GINTMSK); + return dwc2_readl(hsotg->regs + GINTSTS) & + dwc2_readl(hsotg->regs + GINTMSK); } static inline u32 dwc2_hcd_urb_get_status(struct dwc2_hcd_urb *dwc2_urb) @@ -732,7 +733,7 @@ do { \ qtd_list_entry); \ if (usb_pipeint(_qtd_->urb->pipe) && \ (_qh_)->start_split_frame != 0 && !_qtd_->complete_split) { \ - _hfnum_.d32 = readl((_hcd_)->regs + HFNUM); \ + _hfnum_.d32 = dwc2_readl((_hcd_)->regs + HFNUM); \ switch (_hfnum_.b.frnum & 0x7) { \ case 7: \ (_hcd_)->hfnum_7_samples_##_letter_++; \ diff --git a/drivers/usb/dwc2/hcd_ddma.c b/drivers/usb/dwc2/hcd_ddma.c index 3376177..78993ab 100644 --- a/drivers/usb/dwc2/hcd_ddma.c +++ b/drivers/usb/dwc2/hcd_ddma.c @@ -169,19 +169,19 @@ static void dwc2_per_sched_enable(struct dwc2_hsotg *hsotg, u32 fr_list_en) spin_lock_irqsave(&hsotg->lock, flags); - hcfg = readl(hsotg->regs + HCFG); + hcfg = dwc2_readl(hsotg->regs + HCFG); if (hcfg & HCFG_PERSCHEDENA) { /* already enabled */ spin_unlock_irqrestore(&hsotg->lock, flags); return; } - writel(hsotg->frame_list_dma, hsotg->regs + HFLBADDR); + dwc2_writel(hsotg->frame_list_dma, hsotg->regs + HFLBADDR); hcfg &= ~HCFG_FRLISTEN_MASK; hcfg |= fr_list_en | HCFG_PERSCHEDENA; dev_vdbg(hsotg->dev, "Enabling Periodic schedule\n"); - writel(hcfg, hsotg->regs + HCFG); + dwc2_writel(hcfg, hsotg->regs + HCFG); spin_unlock_irqrestore(&hsotg->lock, flags); } @@ -193,7 +193,7 @@ static void dwc2_per_sched_disable(struct dwc2_hsotg *hsotg) spin_lock_irqsave(&hsotg->lock, flags); - hcfg = readl(hsotg->regs + HCFG); + hcfg = dwc2_readl(hsotg->regs + HCFG); if (!(hcfg & HCFG_PERSCHEDENA)) { /* already disabled */ spin_unlock_irqrestore(&hsotg->lock, flags); @@ -202,7 +202,7 @@ static void dwc2_per_sched_disable(struct dwc2_hsotg *hsotg) hcfg &= ~HCFG_PERSCHEDENA; dev_vdbg(hsotg->dev, "Disabling Periodic schedule\n"); - writel(hcfg, hsotg->regs + HCFG); + dwc2_writel(hcfg, hsotg->regs + HCFG); spin_unlock_irqrestore(&hsotg->lock, flags); } diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c index 4cc95df..bda0b21 100644 --- a/drivers/usb/dwc2/hcd_intr.c +++ b/drivers/usb/dwc2/hcd_intr.c @@ -148,7 +148,7 @@ static void dwc2_sof_intr(struct dwc2_hsotg *hsotg) dwc2_hcd_queue_transactions(hsotg, tr_type); /* Clear interrupt */ - writel(GINTSTS_SOF, hsotg->regs + GINTSTS); + dwc2_writel(GINTSTS_SOF, hsotg->regs + GINTSTS); } /* @@ -164,7 +164,7 @@ static void dwc2_rx_fifo_level_intr(struct dwc2_hsotg *hsotg) if (dbg_perio()) dev_vdbg(hsotg->dev, "--RxFIFO Level Interrupt--\n"); - grxsts = readl(hsotg->regs + GRXSTSP); + grxsts = dwc2_readl(hsotg->regs + GRXSTSP); chnum = (grxsts & GRXSTS_HCHNUM_MASK) >> GRXSTS_HCHNUM_SHIFT; chan = hsotg->hc_ptr_array[chnum]; if (!chan) { @@ -247,11 +247,11 @@ static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0, dev_vdbg(hsotg->dev, "%s(%p)\n", __func__, hsotg); /* Every time when port enables calculate HFIR.FrInterval */ - hfir = readl(hsotg->regs + HFIR); + hfir = dwc2_readl(hsotg->regs + HFIR); hfir &= ~HFIR_FRINT_MASK; hfir |= dwc2_calc_frame_interval(hsotg) << HFIR_FRINT_SHIFT & HFIR_FRINT_MASK; - writel(hfir, hsotg->regs + HFIR); + dwc2_writel(hfir, hsotg->regs + HFIR); /* Check if we need to adjust the PHY clock speed for low power */ if (!params->host_support_fs_ls_low_power) { @@ -260,7 +260,7 @@ static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0, return; } - usbcfg = readl(hsotg->regs + GUSBCFG); + usbcfg = dwc2_readl(hsotg->regs + GUSBCFG); prtspd = (hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT; if (prtspd == HPRT0_SPD_LOW_SPEED || prtspd == HPRT0_SPD_FULL_SPEED) { @@ -268,11 +268,11 @@ static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0, if (!(usbcfg & GUSBCFG_PHY_LP_CLK_SEL)) { /* Set PHY low power clock select for FS/LS devices */ usbcfg |= GUSBCFG_PHY_LP_CLK_SEL; - writel(usbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); do_reset = 1; } - hcfg = readl(hsotg->regs + HCFG); + hcfg = dwc2_readl(hsotg->regs + HCFG); fslspclksel = (hcfg & HCFG_FSLSPCLKSEL_MASK) >> HCFG_FSLSPCLKSEL_SHIFT; @@ -286,7 +286,7 @@ static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0, fslspclksel = HCFG_FSLSPCLKSEL_6_MHZ; hcfg &= ~HCFG_FSLSPCLKSEL_MASK; hcfg |= fslspclksel << HCFG_FSLSPCLKSEL_SHIFT; - writel(hcfg, hsotg->regs + HCFG); + dwc2_writel(hcfg, hsotg->regs + HCFG); do_reset = 1; } } else { @@ -297,7 +297,7 @@ static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0, fslspclksel = HCFG_FSLSPCLKSEL_48_MHZ; hcfg &= ~HCFG_FSLSPCLKSEL_MASK; hcfg |= fslspclksel << HCFG_FSLSPCLKSEL_SHIFT; - writel(hcfg, hsotg->regs + HCFG); + dwc2_writel(hcfg, hsotg->regs + HCFG); do_reset = 1; } } @@ -305,7 +305,7 @@ static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0, /* Not low power */ if (usbcfg & GUSBCFG_PHY_LP_CLK_SEL) { usbcfg &= ~GUSBCFG_PHY_LP_CLK_SEL; - writel(usbcfg, hsotg->regs + GUSBCFG); + dwc2_writel(usbcfg, hsotg->regs + GUSBCFG); do_reset = 1; } } @@ -332,7 +332,7 @@ static void dwc2_port_intr(struct dwc2_hsotg *hsotg) dev_vdbg(hsotg->dev, "--Port Interrupt--\n"); - hprt0 = readl(hsotg->regs + HPRT0); + hprt0 = dwc2_readl(hsotg->regs + HPRT0); hprt0_modify = hprt0; /* @@ -388,7 +388,7 @@ static void dwc2_port_intr(struct dwc2_hsotg *hsotg) } /* Clear Port Interrupts */ - writel(hprt0_modify, hsotg->regs + HPRT0); + dwc2_writel(hprt0_modify, hsotg->regs + HPRT0); } /* @@ -408,7 +408,7 @@ static u32 dwc2_get_actual_xfer_length(struct dwc2_hsotg *hsotg, { u32 hctsiz, count, length; - hctsiz = readl(hsotg->regs + HCTSIZ(chnum)); + hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum)); if (halt_status == DWC2_HC_XFER_COMPLETE) { if (chan->ep_is_in) { @@ -491,7 +491,7 @@ static int dwc2_update_urb_state(struct dwc2_hsotg *hsotg, urb->status = 0; } - hctsiz = readl(hsotg->regs + HCTSIZ(chnum)); + hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum)); dev_vdbg(hsotg->dev, "DWC_otg: %s: %s, channel %d\n", __func__, (chan->ep_is_in ? "IN" : "OUT"), chnum); dev_vdbg(hsotg->dev, " chan->xfer_len %d\n", chan->xfer_len); @@ -514,7 +514,7 @@ void dwc2_hcd_save_data_toggle(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan, int chnum, struct dwc2_qtd *qtd) { - u32 hctsiz = readl(hsotg->regs + HCTSIZ(chnum)); + u32 hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum)); u32 pid = (hctsiz & TSIZ_SC_MC_PID_MASK) >> TSIZ_SC_MC_PID_SHIFT; if (chan->ep_type != USB_ENDPOINT_XFER_CONTROL) { @@ -771,9 +771,9 @@ cleanup: } } - haintmsk = readl(hsotg->regs + HAINTMSK); + haintmsk = dwc2_readl(hsotg->regs + HAINTMSK); haintmsk &= ~(1 << chan->hc_num); - writel(haintmsk, hsotg->regs + HAINTMSK); + dwc2_writel(haintmsk, hsotg->regs + HAINTMSK); /* Try to queue more transfers now that there's a free channel */ tr_type = dwc2_hcd_select_transactions(hsotg); @@ -820,9 +820,9 @@ static void dwc2_halt_channel(struct dwc2_hsotg *hsotg, * is enabled so that the non-periodic schedule will * be processed */ - gintmsk = readl(hsotg->regs + GINTMSK); + gintmsk = dwc2_readl(hsotg->regs + GINTMSK); gintmsk |= GINTSTS_NPTXFEMP; - writel(gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(gintmsk, hsotg->regs + GINTMSK); } else { dev_vdbg(hsotg->dev, "isoc/intr\n"); /* @@ -839,9 +839,9 @@ static void dwc2_halt_channel(struct dwc2_hsotg *hsotg, * enabled so that the periodic schedule will be * processed */ - gintmsk = readl(hsotg->regs + GINTMSK); + gintmsk = dwc2_readl(hsotg->regs + GINTMSK); gintmsk |= GINTSTS_PTXFEMP; - writel(gintmsk, hsotg->regs + GINTMSK); + dwc2_writel(gintmsk, hsotg->regs + GINTMSK); } } } @@ -906,7 +906,7 @@ static void dwc2_complete_periodic_xfer(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd, enum dwc2_halt_status halt_status) { - u32 hctsiz = readl(hsotg->regs + HCTSIZ(chnum)); + u32 hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum)); qtd->error_count = 0; @@ -1184,7 +1184,7 @@ static void dwc2_update_urb_state_abn(struct dwc2_hsotg *hsotg, urb->actual_length += xfer_length; - hctsiz = readl(hsotg->regs + HCTSIZ(chnum)); + hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum)); dev_vdbg(hsotg->dev, "DWC_otg: %s: %s, channel %d\n", __func__, (chan->ep_is_in ? "IN" : "OUT"), chnum); dev_vdbg(hsotg->dev, " chan->start_pkt_count %d\n", @@ -1505,10 +1505,10 @@ static void dwc2_hc_ahberr_intr(struct dwc2_hsotg *hsotg, dwc2_hc_handle_tt_clear(hsotg, chan, qtd); - hcchar = readl(hsotg->regs + HCCHAR(chnum)); - hcsplt = readl(hsotg->regs + HCSPLT(chnum)); - hctsiz = readl(hsotg->regs + HCTSIZ(chnum)); - hc_dma = readl(hsotg->regs + HCDMA(chnum)); + hcchar = dwc2_readl(hsotg->regs + HCCHAR(chnum)); + hcsplt = dwc2_readl(hsotg->regs + HCSPLT(chnum)); + hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum)); + hc_dma = dwc2_readl(hsotg->regs + HCDMA(chnum)); dev_err(hsotg->dev, "AHB ERROR, Channel %d\n", chnum); dev_err(hsotg->dev, " hcchar 0x%08x, hcsplt 0x%08x\n", hcchar, hcsplt); @@ -1721,10 +1721,10 @@ static bool dwc2_halt_status_ok(struct dwc2_hsotg *hsotg, * This code is here only as a check. This condition should * never happen. Ignore the halt if it does occur. */ - hcchar = readl(hsotg->regs + HCCHAR(chnum)); - hctsiz = readl(hsotg->regs + HCTSIZ(chnum)); - hcintmsk = readl(hsotg->regs + HCINTMSK(chnum)); - hcsplt = readl(hsotg->regs + HCSPLT(chnum)); + hcchar = dwc2_readl(hsotg->regs + HCCHAR(chnum)); + hctsiz = dwc2_readl(hsotg->regs + HCTSIZ(chnum)); + hcintmsk = dwc2_readl(hsotg->regs + HCINTMSK(chnum)); + hcsplt = dwc2_readl(hsotg->regs + HCSPLT(chnum)); dev_dbg(hsotg->dev, "%s: chan->halt_status DWC2_HC_XFER_NO_HALT_STATUS,\n", __func__); @@ -1748,7 +1748,7 @@ static bool dwc2_halt_status_ok(struct dwc2_hsotg *hsotg, * when the halt interrupt occurs. Halt the channel again if it does * occur. */ - hcchar = readl(hsotg->regs + HCCHAR(chnum)); + hcchar = dwc2_readl(hsotg->regs + HCCHAR(chnum)); if (hcchar & HCCHAR_CHDIS) { dev_warn(hsotg->dev, "%s: hcchar.chdis set unexpectedly, hcchar 0x%08x, trying to halt again\n", @@ -1808,7 +1808,7 @@ static void dwc2_hc_chhltd_intr_dma(struct dwc2_hsotg *hsotg, return; } - hcintmsk = readl(hsotg->regs + HCINTMSK(chnum)); + hcintmsk = dwc2_readl(hsotg->regs + HCINTMSK(chnum)); if (chan->hcint & HCINTMSK_XFERCOMPL) { /* @@ -1903,7 +1903,7 @@ static void dwc2_hc_chhltd_intr_dma(struct dwc2_hsotg *hsotg, dev_err(hsotg->dev, "hcint 0x%08x, intsts 0x%08x\n", chan->hcint, - readl(hsotg->regs + GINTSTS)); + dwc2_readl(hsotg->regs + GINTSTS)); goto error; } } @@ -1949,6 +1949,24 @@ static void dwc2_hc_chhltd_intr(struct dwc2_hsotg *hsotg, } } +/* + * Check if the given qtd is still the top of the list (and thus valid). + * + * If dwc2_hcd_qtd_unlink_and_free() has been called since we grabbed + * the qtd from the top of the list, this will return false (otherwise true). + */ +static bool dwc2_check_qtd_still_ok(struct dwc2_qtd *qtd, struct dwc2_qh *qh) +{ + struct dwc2_qtd *cur_head; + + if (qh == NULL) + return false; + + cur_head = list_first_entry(&qh->qtd_list, struct dwc2_qtd, + qtd_list_entry); + return (cur_head == qtd); +} + /* Handles interrupt for a specific Host Channel */ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum) { @@ -1958,11 +1976,11 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum) chan = hsotg->hc_ptr_array[chnum]; - hcint = readl(hsotg->regs + HCINT(chnum)); - hcintmsk = readl(hsotg->regs + HCINTMSK(chnum)); + hcint = dwc2_readl(hsotg->regs + HCINT(chnum)); + hcintmsk = dwc2_readl(hsotg->regs + HCINTMSK(chnum)); if (!chan) { dev_err(hsotg->dev, "## hc_ptr_array for channel is NULL ##\n"); - writel(hcint, hsotg->regs + HCINT(chnum)); + dwc2_writel(hcint, hsotg->regs + HCINT(chnum)); return; } @@ -1974,7 +1992,7 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum) hcint, hcintmsk, hcint & hcintmsk); } - writel(hcint, hsotg->regs + HCINT(chnum)); + dwc2_writel(hcint, hsotg->regs + HCINT(chnum)); chan->hcint = hcint; hcint &= hcintmsk; @@ -2031,27 +2049,59 @@ static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum) */ hcint &= ~HCINTMSK_NYET; } - if (hcint & HCINTMSK_CHHLTD) + + if (hcint & HCINTMSK_CHHLTD) { dwc2_hc_chhltd_intr(hsotg, chan, chnum, qtd); - if (hcint & HCINTMSK_AHBERR) + if (!dwc2_check_qtd_still_ok(qtd, chan->qh)) + goto exit; + } + if (hcint & HCINTMSK_AHBERR) { dwc2_hc_ahberr_intr(hsotg, chan, chnum, qtd); - if (hcint & HCINTMSK_STALL) + if (!dwc2_check_qtd_still_ok(qtd, chan->qh)) + goto exit; + } + if (hcint & HCINTMSK_STALL) { dwc2_hc_stall_intr(hsotg, chan, chnum, qtd); - if (hcint & HCINTMSK_NAK) + if (!dwc2_check_qtd_still_ok(qtd, chan->qh)) + goto exit; + } + if (hcint & HCINTMSK_NAK) { dwc2_hc_nak_intr(hsotg, chan, chnum, qtd); - if (hcint & HCINTMSK_ACK) + if (!dwc2_check_qtd_still_ok(qtd, chan->qh)) + goto exit; + } + if (hcint & HCINTMSK_ACK) { dwc2_hc_ack_intr(hsotg, chan, chnum, qtd); - if (hcint & HCINTMSK_NYET) + if (!dwc2_check_qtd_still_ok(qtd, chan->qh)) + goto exit; + } + if (hcint & HCINTMSK_NYET) { dwc2_hc_nyet_intr(hsotg, chan, chnum, qtd); - if (hcint & HCINTMSK_XACTERR) + if (!dwc2_check_qtd_still_ok(qtd, chan->qh)) + goto exit; + } + if (hcint & HCINTMSK_XACTERR) { dwc2_hc_xacterr_intr(hsotg, chan, chnum, qtd); - if (hcint & HCINTMSK_BBLERR) + if (!dwc2_check_qtd_still_ok(qtd, chan->qh)) + goto exit; + } + if (hcint & HCINTMSK_BBLERR) { dwc2_hc_babble_intr(hsotg, chan, chnum, qtd); - if (hcint & HCINTMSK_FRMOVRUN) + if (!dwc2_check_qtd_still_ok(qtd, chan->qh)) + goto exit; + } + if (hcint & HCINTMSK_FRMOVRUN) { dwc2_hc_frmovrun_intr(hsotg, chan, chnum, qtd); - if (hcint & HCINTMSK_DATATGLERR) + if (!dwc2_check_qtd_still_ok(qtd, chan->qh)) + goto exit; + } + if (hcint & HCINTMSK_DATATGLERR) { dwc2_hc_datatglerr_intr(hsotg, chan, chnum, qtd); + if (!dwc2_check_qtd_still_ok(qtd, chan->qh)) + goto exit; + } +exit: chan->hcint = 0; } @@ -2066,7 +2116,7 @@ static void dwc2_hc_intr(struct dwc2_hsotg *hsotg) u32 haint; int i; - haint = readl(hsotg->regs + HAINT); + haint = dwc2_readl(hsotg->regs + HAINT); if (dbg_perio()) { dev_vdbg(hsotg->dev, "%s()\n", __func__); @@ -2134,8 +2184,8 @@ irqreturn_t dwc2_handle_hcd_intr(struct dwc2_hsotg *hsotg) "DWC OTG HCD Finished Servicing Interrupts\n"); dev_vdbg(hsotg->dev, "DWC OTG HCD gintsts=0x%08x gintmsk=0x%08x\n", - readl(hsotg->regs + GINTSTS), - readl(hsotg->regs + GINTMSK)); + dwc2_readl(hsotg->regs + GINTSTS), + dwc2_readl(hsotg->regs + GINTMSK)); } } diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c index 3ad63d3..7d8d06c 100644 --- a/drivers/usb/dwc2/hcd_queue.c +++ b/drivers/usb/dwc2/hcd_queue.c @@ -106,6 +106,9 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, USB_SPEED_HIGH : dev_speed, qh->ep_is_in, qh->ep_type == USB_ENDPOINT_XFER_ISOC, bytecount)); + + /* Ensure frame_number corresponds to the reality */ + hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg); /* Start in a slightly future (micro)frame */ qh->sched_frame = dwc2_frame_num_inc(hsotg->frame_number, SCHEDULE_SLOP); @@ -115,7 +118,7 @@ static void dwc2_qh_init(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, if (qh->ep_type == USB_ENDPOINT_XFER_INT) qh->interval = 8; #endif - hprt = readl(hsotg->regs + HPRT0); + hprt = dwc2_readl(hsotg->regs + HPRT0); prtspd = (hprt & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT; if (prtspd == HPRT0_SPD_HIGH_SPEED && (dev_speed == USB_SPEED_LOW || @@ -583,6 +586,14 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) /* QH already in a schedule */ return 0; + if (!dwc2_frame_num_le(qh->sched_frame, hsotg->frame_number) && + !hsotg->frame_number) { + dev_dbg(hsotg->dev, + "reset frame number counter\n"); + qh->sched_frame = dwc2_frame_num_inc(hsotg->frame_number, + SCHEDULE_SLOP); + } + /* Add the new QH to the appropriate schedule */ if (dwc2_qh_is_non_per(qh)) { /* Always start in inactive schedule */ @@ -595,9 +606,9 @@ int dwc2_hcd_qh_add(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) if (status) return status; if (!hsotg->periodic_qh_count) { - intr_mask = readl(hsotg->regs + GINTMSK); + intr_mask = dwc2_readl(hsotg->regs + GINTMSK); intr_mask |= GINTSTS_SOF; - writel(intr_mask, hsotg->regs + GINTMSK); + dwc2_writel(intr_mask, hsotg->regs + GINTMSK); } hsotg->periodic_qh_count++; @@ -632,9 +643,9 @@ void dwc2_hcd_qh_unlink(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) dwc2_deschedule_periodic(hsotg, qh); hsotg->periodic_qh_count--; if (!hsotg->periodic_qh_count) { - intr_mask = readl(hsotg->regs + GINTMSK); + intr_mask = dwc2_readl(hsotg->regs + GINTMSK); intr_mask &= ~GINTSTS_SOF; - writel(intr_mask, hsotg->regs + GINTMSK); + dwc2_writel(intr_mask, hsotg->regs + GINTMSK); } } diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h index d0a5ed8..553f246 100644 --- a/drivers/usb/dwc2/hw.h +++ b/drivers/usb/dwc2/hw.h @@ -142,6 +142,7 @@ #define GINTSTS_RESETDET (1 << 23) #define GINTSTS_FET_SUSP (1 << 22) #define GINTSTS_INCOMPL_IP (1 << 21) +#define GINTSTS_INCOMPL_SOOUT (1 << 21) #define GINTSTS_INCOMPL_SOIN (1 << 20) #define GINTSTS_OEPINT (1 << 19) #define GINTSTS_IEPINT (1 << 18) diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 9093530..5859b0f 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -37,11 +37,14 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/slab.h> +#include <linux/clk.h> #include <linux/device.h> #include <linux/dma-mapping.h> #include <linux/of_device.h> #include <linux/mutex.h> #include <linux/platform_device.h> +#include <linux/phy/phy.h> +#include <linux/platform_data/s3c-hsotg.h> #include <linux/usb/of.h> @@ -111,6 +114,145 @@ static const struct dwc2_core_params params_rk3066 = { .hibernation = -1, }; +static int __dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg) +{ + struct platform_device *pdev = to_platform_device(hsotg->dev); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies), + hsotg->supplies); + if (ret) + return ret; + + ret = clk_prepare_enable(hsotg->clk); + if (ret) + return ret; + + if (hsotg->uphy) + ret = usb_phy_init(hsotg->uphy); + else if (hsotg->plat && hsotg->plat->phy_init) + ret = hsotg->plat->phy_init(pdev, hsotg->plat->phy_type); + else { + ret = phy_power_on(hsotg->phy); + if (ret == 0) + ret = phy_init(hsotg->phy); + } + + return ret; +} + +/** + * dwc2_lowlevel_hw_enable - enable platform lowlevel hw resources + * @hsotg: The driver state + * + * A wrapper for platform code responsible for controlling + * low-level USB platform resources (phy, clock, regulators) + */ +int dwc2_lowlevel_hw_enable(struct dwc2_hsotg *hsotg) +{ + int ret = __dwc2_lowlevel_hw_enable(hsotg); + + if (ret == 0) + hsotg->ll_hw_enabled = true; + return ret; +} + +static int __dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg) +{ + struct platform_device *pdev = to_platform_device(hsotg->dev); + int ret = 0; + + if (hsotg->uphy) + usb_phy_shutdown(hsotg->uphy); + else if (hsotg->plat && hsotg->plat->phy_exit) + ret = hsotg->plat->phy_exit(pdev, hsotg->plat->phy_type); + else { + ret = phy_exit(hsotg->phy); + if (ret == 0) + ret = phy_power_off(hsotg->phy); + } + if (ret) + return ret; + + clk_disable_unprepare(hsotg->clk); + + ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), + hsotg->supplies); + + return ret; +} + +/** + * dwc2_lowlevel_hw_disable - disable platform lowlevel hw resources + * @hsotg: The driver state + * + * A wrapper for platform code responsible for controlling + * low-level USB platform resources (phy, clock, regulators) + */ +int dwc2_lowlevel_hw_disable(struct dwc2_hsotg *hsotg) +{ + int ret = __dwc2_lowlevel_hw_disable(hsotg); + + if (ret == 0) + hsotg->ll_hw_enabled = false; + return ret; +} + +static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg) +{ + int i, ret; + + /* Set default UTMI width */ + hsotg->phyif = GUSBCFG_PHYIF16; + + /* + * Attempt to find a generic PHY, then look for an old style + * USB PHY and then fall back to pdata + */ + hsotg->phy = devm_phy_get(hsotg->dev, "usb2-phy"); + if (IS_ERR(hsotg->phy)) { + hsotg->phy = NULL; + hsotg->uphy = devm_usb_get_phy(hsotg->dev, USB_PHY_TYPE_USB2); + if (IS_ERR(hsotg->uphy)) + hsotg->uphy = NULL; + else + hsotg->plat = dev_get_platdata(hsotg->dev); + } + + if (hsotg->phy) { + /* + * If using the generic PHY framework, check if the PHY bus + * width is 8-bit and set the phyif appropriately. + */ + if (phy_get_bus_width(hsotg->phy) == 8) + hsotg->phyif = GUSBCFG_PHYIF8; + } + + if (!hsotg->phy && !hsotg->uphy && !hsotg->plat) { + dev_err(hsotg->dev, "no platform data or transceiver defined\n"); + return -EPROBE_DEFER; + } + + /* Clock */ + hsotg->clk = devm_clk_get(hsotg->dev, "otg"); + if (IS_ERR(hsotg->clk)) { + hsotg->clk = NULL; + dev_dbg(hsotg->dev, "cannot get otg clock\n"); + } + + /* Regulators */ + for (i = 0; i < ARRAY_SIZE(hsotg->supplies); i++) + hsotg->supplies[i].supply = dwc2_hsotg_supply_names[i]; + + ret = devm_regulator_bulk_get(hsotg->dev, ARRAY_SIZE(hsotg->supplies), + hsotg->supplies); + if (ret) { + dev_err(hsotg->dev, "failed to request supplies: %d\n", ret); + return ret; + } + return 0; +} + /** * dwc2_driver_remove() - Called when the DWC_otg core is unregistered with the * DWC_otg driver @@ -130,7 +272,10 @@ static int dwc2_driver_remove(struct platform_device *dev) if (hsotg->hcd_enabled) dwc2_hcd_remove(hsotg); if (hsotg->gadget_enabled) - s3c_hsotg_remove(hsotg); + dwc2_hsotg_remove(hsotg); + + if (hsotg->ll_hw_enabled) + dwc2_lowlevel_hw_disable(hsotg); return 0; } @@ -163,8 +308,6 @@ static int dwc2_driver_probe(struct platform_device *dev) struct dwc2_core_params defparams; struct dwc2_hsotg *hsotg; struct resource *res; - struct phy *phy; - struct usb_phy *uphy; int retval; int irq; @@ -220,34 +363,25 @@ static int dwc2_driver_probe(struct platform_device *dev) dev_dbg(&dev->dev, "mapped PA %08lx to VA %p\n", (unsigned long)res->start, hsotg->regs); - hsotg->dr_mode = of_usb_get_dr_mode(dev->dev.of_node); - - /* - * Attempt to find a generic PHY, then look for an old style - * USB PHY - */ - phy = devm_phy_get(&dev->dev, "usb2-phy"); - if (IS_ERR(phy)) { - hsotg->phy = NULL; - uphy = devm_usb_get_phy(&dev->dev, USB_PHY_TYPE_USB2); - if (IS_ERR(uphy)) - hsotg->uphy = NULL; - else - hsotg->uphy = uphy; - } else { - hsotg->phy = phy; - phy_power_on(hsotg->phy); - phy_init(hsotg->phy); + hsotg->dr_mode = usb_get_dr_mode(&dev->dev); + if (IS_ENABLED(CONFIG_USB_DWC2_HOST) && + hsotg->dr_mode != USB_DR_MODE_HOST) { + hsotg->dr_mode = USB_DR_MODE_HOST; + dev_warn(hsotg->dev, + "Configuration mismatch. Forcing host mode\n"); + } else if (IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) && + hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) { + hsotg->dr_mode = USB_DR_MODE_PERIPHERAL; + dev_warn(hsotg->dev, + "Configuration mismatch. Forcing peripheral mode\n"); } - spin_lock_init(&hsotg->lock); - mutex_init(&hsotg->init_mutex); - - /* Detect config values from hardware */ - retval = dwc2_get_hwparams(hsotg); + retval = dwc2_lowlevel_hw_init(hsotg); if (retval) return retval; + spin_lock_init(&hsotg->lock); + hsotg->core_params = devm_kzalloc(&dev->dev, sizeof(*hsotg->core_params), GFP_KERNEL); if (!hsotg->core_params) @@ -255,13 +389,22 @@ static int dwc2_driver_probe(struct platform_device *dev) dwc2_set_all_params(hsotg->core_params, -1); + retval = dwc2_lowlevel_hw_enable(hsotg); + if (retval) + return retval; + + /* Detect config values from hardware */ + retval = dwc2_get_hwparams(hsotg); + if (retval) + goto error; + /* Validate parameter values */ dwc2_set_parameters(hsotg, params); if (hsotg->dr_mode != USB_DR_MODE_HOST) { retval = dwc2_gadget_init(hsotg, irq); if (retval) - return retval; + goto error; hsotg->gadget_enabled = 1; } @@ -269,8 +412,8 @@ static int dwc2_driver_probe(struct platform_device *dev) retval = dwc2_hcd_init(hsotg, irq); if (retval) { if (hsotg->gadget_enabled) - s3c_hsotg_remove(hsotg); - return retval; + dwc2_hsotg_remove(hsotg); + goto error; } hsotg->hcd_enabled = 1; } @@ -279,6 +422,14 @@ static int dwc2_driver_probe(struct platform_device *dev) dwc2_debugfs_init(hsotg); + /* Gadget code manages lowlevel hw on its own */ + if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) + dwc2_lowlevel_hw_disable(hsotg); + + return 0; + +error: + dwc2_lowlevel_hw_disable(hsotg); return retval; } @@ -287,15 +438,12 @@ static int __maybe_unused dwc2_suspend(struct device *dev) struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev); int ret = 0; - if (dwc2_is_device_mode(dwc2)) { - ret = s3c_hsotg_suspend(dwc2); - } else { - if (dwc2->lx_state == DWC2_L0) - return 0; - phy_exit(dwc2->phy); - phy_power_off(dwc2->phy); + if (dwc2_is_device_mode(dwc2)) + dwc2_hsotg_suspend(dwc2); + + if (dwc2->ll_hw_enabled) + ret = __dwc2_lowlevel_hw_disable(dwc2); - } return ret; } @@ -304,13 +452,15 @@ static int __maybe_unused dwc2_resume(struct device *dev) struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev); int ret = 0; - if (dwc2_is_device_mode(dwc2)) { - ret = s3c_hsotg_resume(dwc2); - } else { - phy_power_on(dwc2->phy); - phy_init(dwc2->phy); - + if (dwc2->ll_hw_enabled) { + ret = __dwc2_lowlevel_hw_enable(dwc2); + if (ret) + return ret; } + + if (dwc2_is_device_mode(dwc2)) + ret = dwc2_hsotg_resume(dwc2); + return ret; } diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 064123e..22b47973 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -34,6 +34,7 @@ #include <linux/dma-mapping.h> #include <linux/of.h> #include <linux/acpi.h> +#include <linux/pinctrl/consumer.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> @@ -143,6 +144,32 @@ static int dwc3_soft_reset(struct dwc3 *dwc) return 0; } +/* + * dwc3_frame_length_adjustment - Adjusts frame length if required + * @dwc3: Pointer to our controller context structure + * @fladj: Value of GFLADJ_30MHZ to adjust frame length + */ +static void dwc3_frame_length_adjustment(struct dwc3 *dwc, u32 fladj) +{ + u32 reg; + u32 dft; + + if (dwc->revision < DWC3_REVISION_250A) + return; + + if (fladj == 0) + return; + + reg = dwc3_readl(dwc->regs, DWC3_GFLADJ); + dft = reg & DWC3_GFLADJ_30MHZ_MASK; + if (!dev_WARN_ONCE(dwc->dev, dft == fladj, + "request value same as default, ignoring\n")) { + reg &= ~DWC3_GFLADJ_30MHZ_MASK; + reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | fladj; + dwc3_writel(dwc->regs, DWC3_GFLADJ, reg); + } +} + /** * dwc3_free_one_event_buffer - Frees one event buffer * @dwc: Pointer to our controller context structure @@ -488,6 +515,9 @@ static int dwc3_phy_setup(struct dwc3 *dwc) if (dwc->dis_u2_susphy_quirk) reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; + if (dwc->dis_enblslpm_quirk) + reg &= ~DWC3_GUSB2PHYCFG_ENBLSLPM; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); return 0; @@ -507,12 +537,18 @@ static int dwc3_core_init(struct dwc3 *dwc) reg = dwc3_readl(dwc->regs, DWC3_GSNPSID); /* This should read as U3 followed by revision number */ - if ((reg & DWC3_GSNPSID_MASK) != 0x55330000) { + if ((reg & DWC3_GSNPSID_MASK) == 0x55330000) { + /* Detected DWC_usb3 IP */ + dwc->revision = reg; + } else if ((reg & DWC3_GSNPSID_MASK) == 0x33310000) { + /* Detected DWC_usb31 IP */ + dwc->revision = dwc3_readl(dwc->regs, DWC3_VER_NUMBER); + dwc->revision |= DWC3_REVISION_IS_DWC31; + } else { dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n"); ret = -ENODEV; goto err0; } - dwc->revision = reg; /* * Write Linux Version Code to our GUID register so it's easy to figure @@ -773,12 +809,12 @@ static int dwc3_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct dwc3_platform_data *pdata = dev_get_platdata(dev); - struct device_node *node = dev->of_node; struct resource *res; struct dwc3 *dwc; u8 lpm_nyet_threshold; u8 tx_de_emphasis; u8 hird_threshold; + u32 fladj = 0; int ret; @@ -842,51 +878,56 @@ static int dwc3_probe(struct platform_device *pdev) */ hird_threshold = 12; - if (node) { - dwc->maximum_speed = of_usb_get_maximum_speed(node); - dwc->has_lpm_erratum = of_property_read_bool(node, + dwc->maximum_speed = usb_get_maximum_speed(dev); + dwc->dr_mode = usb_get_dr_mode(dev); + + dwc->has_lpm_erratum = device_property_read_bool(dev, "snps,has-lpm-erratum"); - of_property_read_u8(node, "snps,lpm-nyet-threshold", + device_property_read_u8(dev, "snps,lpm-nyet-threshold", &lpm_nyet_threshold); - dwc->is_utmi_l1_suspend = of_property_read_bool(node, + dwc->is_utmi_l1_suspend = device_property_read_bool(dev, "snps,is-utmi-l1-suspend"); - of_property_read_u8(node, "snps,hird-threshold", + device_property_read_u8(dev, "snps,hird-threshold", &hird_threshold); - dwc->usb3_lpm_capable = of_property_read_bool(node, + dwc->usb3_lpm_capable = device_property_read_bool(dev, "snps,usb3_lpm_capable"); - dwc->needs_fifo_resize = of_property_read_bool(node, + dwc->needs_fifo_resize = device_property_read_bool(dev, "tx-fifo-resize"); - dwc->dr_mode = of_usb_get_dr_mode(node); - dwc->disable_scramble_quirk = of_property_read_bool(node, + dwc->disable_scramble_quirk = device_property_read_bool(dev, "snps,disable_scramble_quirk"); - dwc->u2exit_lfps_quirk = of_property_read_bool(node, + dwc->u2exit_lfps_quirk = device_property_read_bool(dev, "snps,u2exit_lfps_quirk"); - dwc->u2ss_inp3_quirk = of_property_read_bool(node, + dwc->u2ss_inp3_quirk = device_property_read_bool(dev, "snps,u2ss_inp3_quirk"); - dwc->req_p1p2p3_quirk = of_property_read_bool(node, + dwc->req_p1p2p3_quirk = device_property_read_bool(dev, "snps,req_p1p2p3_quirk"); - dwc->del_p1p2p3_quirk = of_property_read_bool(node, + dwc->del_p1p2p3_quirk = device_property_read_bool(dev, "snps,del_p1p2p3_quirk"); - dwc->del_phy_power_chg_quirk = of_property_read_bool(node, + dwc->del_phy_power_chg_quirk = device_property_read_bool(dev, "snps,del_phy_power_chg_quirk"); - dwc->lfps_filter_quirk = of_property_read_bool(node, + dwc->lfps_filter_quirk = device_property_read_bool(dev, "snps,lfps_filter_quirk"); - dwc->rx_detect_poll_quirk = of_property_read_bool(node, + dwc->rx_detect_poll_quirk = device_property_read_bool(dev, "snps,rx_detect_poll_quirk"); - dwc->dis_u3_susphy_quirk = of_property_read_bool(node, + dwc->dis_u3_susphy_quirk = device_property_read_bool(dev, "snps,dis_u3_susphy_quirk"); - dwc->dis_u2_susphy_quirk = of_property_read_bool(node, + dwc->dis_u2_susphy_quirk = device_property_read_bool(dev, "snps,dis_u2_susphy_quirk"); + dwc->dis_enblslpm_quirk = device_property_read_bool(dev, + "snps,dis_enblslpm_quirk"); - dwc->tx_de_emphasis_quirk = of_property_read_bool(node, + dwc->tx_de_emphasis_quirk = device_property_read_bool(dev, "snps,tx_de_emphasis_quirk"); - of_property_read_u8(node, "snps,tx_de_emphasis", + device_property_read_u8(dev, "snps,tx_de_emphasis", &tx_de_emphasis); - of_property_read_string(node, "snps,hsphy_interface", - &dwc->hsphy_interface); - } else if (pdata) { + device_property_read_string(dev, "snps,hsphy_interface", + &dwc->hsphy_interface); + device_property_read_u32(dev, "snps,quirk-frame-length-adjustment", + &fladj); + + if (pdata) { dwc->maximum_speed = pdata->maximum_speed; dwc->has_lpm_erratum = pdata->has_lpm_erratum; if (pdata->lpm_nyet_threshold) @@ -909,12 +950,14 @@ static int dwc3_probe(struct platform_device *pdev) dwc->rx_detect_poll_quirk = pdata->rx_detect_poll_quirk; dwc->dis_u3_susphy_quirk = pdata->dis_u3_susphy_quirk; dwc->dis_u2_susphy_quirk = pdata->dis_u2_susphy_quirk; + dwc->dis_enblslpm_quirk = pdata->dis_enblslpm_quirk; dwc->tx_de_emphasis_quirk = pdata->tx_de_emphasis_quirk; if (pdata->tx_de_emphasis) tx_de_emphasis = pdata->tx_de_emphasis; dwc->hsphy_interface = pdata->hsphy_interface; + fladj = pdata->fladj_value; } /* default to superspeed if no maximum_speed passed */ @@ -971,6 +1014,9 @@ static int dwc3_probe(struct platform_device *pdev) goto err1; } + /* Adjust Frame Length */ + dwc3_frame_length_adjustment(dwc, fladj); + usb_phy_set_suspend(dwc->usb2_phy, 0); usb_phy_set_suspend(dwc->usb3_phy, 0); ret = phy_power_on(dwc->usb2_generic_phy); @@ -1091,6 +1137,8 @@ static int dwc3_suspend(struct device *dev) phy_exit(dwc->usb2_generic_phy); phy_exit(dwc->usb3_generic_phy); + pinctrl_pm_select_sleep_state(dev); + return 0; } @@ -1100,6 +1148,8 @@ static int dwc3_resume(struct device *dev) unsigned long flags; int ret; + pinctrl_pm_select_default_state(dev); + usb_phy_init(dwc->usb3_phy); usb_phy_init(dwc->usb2_phy); ret = phy_init(dwc->usb2_generic_phy); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 0447788..36f1cb7 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -108,6 +108,9 @@ #define DWC3_GPRTBIMAP_FS0 0xc188 #define DWC3_GPRTBIMAP_FS1 0xc18c +#define DWC3_VER_NUMBER 0xc1a0 +#define DWC3_VER_TYPE 0xc1a4 + #define DWC3_GUSB2PHYCFG(n) (0xc200 + (n * 0x04)) #define DWC3_GUSB2I2CCTL(n) (0xc240 + (n * 0x04)) @@ -124,6 +127,7 @@ #define DWC3_GEVNTCOUNT(n) (0xc40c + (n * 0x10)) #define DWC3_GHWPARAMS8 0xc600 +#define DWC3_GFLADJ 0xc630 /* Device Registers */ #define DWC3_DCFG 0xc700 @@ -175,6 +179,7 @@ #define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31) #define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6) #define DWC3_GUSB2PHYCFG_ULPI_UTMI (1 << 4) +#define DWC3_GUSB2PHYCFG_ENBLSLPM (1 << 8) /* Global USB2 PHY Vendor Control Register */ #define DWC3_GUSB2PHYACC_NEWREGREQ (1 << 25) @@ -234,6 +239,10 @@ /* Global HWPARAMS6 Register */ #define DWC3_GHWPARAMS6_EN_FPGA (1 << 7) +/* Global Frame Length Adjustment Register */ +#define DWC3_GFLADJ_30MHZ_SDBND_SEL (1 << 7) +#define DWC3_GFLADJ_30MHZ_MASK 0x3f + /* Device Configuration Register */ #define DWC3_DCFG_DEVADDR(addr) ((addr) << 3) #define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f) @@ -712,6 +721,8 @@ struct dwc3_scratchpad_array { * @rx_detect_poll_quirk: set if we enable rx_detect to polling lfps quirk * @dis_u3_susphy_quirk: set if we disable usb3 suspend phy * @dis_u2_susphy_quirk: set if we disable usb2 suspend phy + * @dis_enblslpm_quirk: set if we clear enblslpm in GUSB2PHYCFG, + * disabling the suspend signal to the PHY. * @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk * @tx_de_emphasis: Tx de-emphasis value * 0 - -6dB de-emphasis @@ -766,6 +777,14 @@ struct dwc3 { u32 num_event_buffers; u32 u1u2; u32 maximum_speed; + + /* + * All 3.1 IP version constants are greater than the 3.0 IP + * version constants. This works for most version checks in + * dwc3. However, in the future, this may not apply as + * features may be developed on newer versions of the 3.0 IP + * that are not in the 3.1 IP. + */ u32 revision; #define DWC3_REVISION_173A 0x5533173a @@ -788,6 +807,13 @@ struct dwc3 { #define DWC3_REVISION_270A 0x5533270a #define DWC3_REVISION_280A 0x5533280a +/* + * NOTICE: we're using bit 31 as a "is usb 3.1" flag. This is really + * just so dwc31 revisions are always larger than dwc3. + */ +#define DWC3_REVISION_IS_DWC31 0x80000000 +#define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_USB31) + enum dwc3_ep0_next ep0_next_event; enum dwc3_ep0_state ep0state; enum dwc3_link_state link_state; @@ -841,6 +867,7 @@ struct dwc3 { unsigned rx_detect_poll_quirk:1; unsigned dis_u3_susphy_quirk:1; unsigned dis_u2_susphy_quirk:1; + unsigned dis_enblslpm_quirk:1; unsigned tx_de_emphasis_quirk:1; unsigned tx_de_emphasis:2; diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index f626179..77a622c 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -26,12 +26,14 @@ #include "platform_data.h" -#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd -#define PCI_DEVICE_ID_INTEL_BYT 0x0f37 -#define PCI_DEVICE_ID_INTEL_MRFLD 0x119e -#define PCI_DEVICE_ID_INTEL_BSW 0x22B7 -#define PCI_DEVICE_ID_INTEL_SPTLP 0x9d30 -#define PCI_DEVICE_ID_INTEL_SPTH 0xa130 +#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd +#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI 0xabce +#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB31 0xabcf +#define PCI_DEVICE_ID_INTEL_BYT 0x0f37 +#define PCI_DEVICE_ID_INTEL_MRFLD 0x119e +#define PCI_DEVICE_ID_INTEL_BSW 0x22b7 +#define PCI_DEVICE_ID_INTEL_SPTLP 0x9d30 +#define PCI_DEVICE_ID_INTEL_SPTH 0xa130 static const struct acpi_gpio_params reset_gpios = { 0, 0, false }; static const struct acpi_gpio_params cs_gpios = { 1, 0, false }; @@ -106,6 +108,22 @@ static int dwc3_pci_quirks(struct pci_dev *pdev) } } + if (pdev->vendor == PCI_VENDOR_ID_SYNOPSYS && + (pdev->device == PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 || + pdev->device == PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI || + pdev->device == PCI_DEVICE_ID_SYNOPSYS_HAPSUSB31)) { + + struct dwc3_platform_data pdata; + + memset(&pdata, 0, sizeof(pdata)); + pdata.usb3_lpm_capable = true; + pdata.has_lpm_erratum = true; + pdata.dis_enblslpm_quirk = true; + + return platform_device_add_data(pci_get_drvdata(pdev), &pdata, + sizeof(pdata)); + } + return 0; } @@ -154,6 +172,7 @@ static int dwc3_pci_probe(struct pci_dev *pci, goto err; dwc3->dev.parent = dev; + ACPI_COMPANION_SET(&dwc3->dev, ACPI_COMPANION(dev)); ret = platform_device_add(dwc3); if (ret) { @@ -178,6 +197,14 @@ static const struct pci_device_id dwc3_pci_id_table[] = { PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3), }, + { + PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, + PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI), + }, + { + PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS, + PCI_DEVICE_ID_SYNOPSYS_HAPSUSB31), + }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BSW), }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT), }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_MRFLD), }, diff --git a/drivers/usb/dwc3/dwc3-st.c b/drivers/usb/dwc3/dwc3-st.c index de4d52f..5c0adb9 100644 --- a/drivers/usb/dwc3/dwc3-st.c +++ b/drivers/usb/dwc3/dwc3-st.c @@ -195,6 +195,7 @@ static int st_dwc3_probe(struct platform_device *pdev) struct resource *res; struct device *dev = &pdev->dev; struct device_node *node = dev->of_node, *child; + struct platform_device *child_pdev; struct regmap *regmap; int ret; @@ -253,8 +254,6 @@ static int st_dwc3_probe(struct platform_device *pdev) goto undo_softreset; } - dwc3_data->dr_mode = of_usb_get_dr_mode(child); - /* Allocate and initialize the core */ ret = of_platform_populate(node, NULL, NULL, dev); if (ret) { @@ -262,6 +261,15 @@ static int st_dwc3_probe(struct platform_device *pdev) goto undo_softreset; } + child_pdev = of_find_device_by_node(child); + if (!child_pdev) { + dev_err(dev, "failed to find dwc3 core device\n"); + ret = -ENODEV; + goto undo_softreset; + } + + dwc3_data->dr_mode = usb_get_dr_mode(&child_pdev->dev); + /* * Configure the USB port as device or host according to the static * configuration passed from DT. diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 1e8bdf8..55ba447 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -948,7 +948,6 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param, dwc3_trace(trace_dwc3_gadget, "%s: endpoint busy", dep->name); return -EBUSY; } - dep->flags &= ~DWC3_EP_PENDING_REQUEST; /* * If we are getting here after a short-out-packet we don't enqueue any @@ -1050,6 +1049,8 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) req->direction = dep->direction; req->epnum = dep->number; + trace_dwc3_ep_queue(req); + /* * We only add to our list of requests now and * start consuming the list once we get XferNotReady @@ -1070,6 +1071,19 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) list_add_tail(&req->list, &dep->request_list); /* + * If there are no pending requests and the endpoint isn't already + * busy, we will just start the request straight away. + * + * This will save one IRQ (XFER_NOT_READY) and possibly make it a + * little bit faster. + */ + if (!usb_endpoint_xfer_isoc(dep->endpoint.desc) && + !(dep->flags & DWC3_EP_BUSY)) { + ret = __dwc3_gadget_kick_transfer(dep, 0, true); + goto out; + } + + /* * There are a few special cases: * * 1. XferNotReady with empty list of requests. We need to kick the @@ -1096,10 +1110,10 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) } ret = __dwc3_gadget_kick_transfer(dep, 0, true); - if (ret && ret != -EBUSY) - dev_dbg(dwc->dev, "%s: failed to kick transfers\n", - dep->name); - return ret; + if (!ret) + dep->flags &= ~DWC3_EP_PENDING_REQUEST; + + goto out; } /* @@ -1113,10 +1127,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) WARN_ON_ONCE(!dep->resource_index); ret = __dwc3_gadget_kick_transfer(dep, dep->resource_index, false); - if (ret && ret != -EBUSY) - dev_dbg(dwc->dev, "%s: failed to kick transfers\n", - dep->name); - return ret; + goto out; } /* @@ -1124,14 +1135,17 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) * right away, otherwise host will not know we have streams to be * handled. */ - if (dep->stream_capable) { + if (dep->stream_capable) ret = __dwc3_gadget_kick_transfer(dep, 0, true); - if (ret && ret != -EBUSY) - dev_dbg(dwc->dev, "%s: failed to kick transfers\n", - dep->name); - } - return 0; +out: + if (ret && ret != -EBUSY) + dev_dbg(dwc->dev, "%s: failed to kick transfers\n", + dep->name); + if (ret == -EBUSY) + ret = 0; + + return ret; } static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, @@ -1159,8 +1173,6 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request, goto out; } - trace_dwc3_ep_queue(req); - ret = __dwc3_gadget_ep_queue(dep, req); out: @@ -1872,27 +1884,32 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, unsigned int i; int ret; - req = next_request(&dep->req_queued); - if (!req) { - WARN_ON_ONCE(1); - return 1; - } - i = 0; do { - slot = req->start_slot + i; - if ((slot == DWC3_TRB_NUM - 1) && + req = next_request(&dep->req_queued); + if (!req) { + WARN_ON_ONCE(1); + return 1; + } + i = 0; + do { + slot = req->start_slot + i; + if ((slot == DWC3_TRB_NUM - 1) && usb_endpoint_xfer_isoc(dep->endpoint.desc)) - slot++; - slot %= DWC3_TRB_NUM; - trb = &dep->trb_pool[slot]; + slot++; + slot %= DWC3_TRB_NUM; + trb = &dep->trb_pool[slot]; + + ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb, + event, status); + if (ret) + break; + } while (++i < req->request.num_mapped_sgs); + + dwc3_gadget_giveback(dep, req, status); - ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb, - event, status); if (ret) break; - } while (++i < req->request.num_mapped_sgs); - - dwc3_gadget_giveback(dep, req, status); + } while (1); if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && list_empty(&dep->req_queued)) { @@ -1955,6 +1972,14 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc, dwc->u1u2 = 0; } + + if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) { + int ret; + + ret = __dwc3_gadget_kick_transfer(dep, 0, is_xfer_complete); + if (!ret || ret == -EBUSY) + return; + } } static void dwc3_endpoint_interrupt(struct dwc3 *dwc, @@ -1992,15 +2017,16 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { dwc3_gadget_start_isoc(dwc, dep, event); } else { + int active; int ret; + active = event->status & DEPEVT_STATUS_TRANSFER_ACTIVE; + dwc3_trace(trace_dwc3_gadget, "%s: reason %s", - dep->name, event->status & - DEPEVT_STATUS_TRANSFER_ACTIVE - ? "Transfer Active" + dep->name, active ? "Transfer Active" : "Transfer Not Active"); - ret = __dwc3_gadget_kick_transfer(dep, 0, 1); + ret = __dwc3_gadget_kick_transfer(dep, 0, !active); if (!ret || ret == -EBUSY) return; diff --git a/drivers/usb/dwc3/platform_data.h b/drivers/usb/dwc3/platform_data.h index d3614ec..2bb4d3a 100644 --- a/drivers/usb/dwc3/platform_data.h +++ b/drivers/usb/dwc3/platform_data.h @@ -42,9 +42,12 @@ struct dwc3_platform_data { unsigned rx_detect_poll_quirk:1; unsigned dis_u3_susphy_quirk:1; unsigned dis_u2_susphy_quirk:1; + unsigned dis_enblslpm_quirk:1; unsigned tx_de_emphasis_quirk:1; unsigned tx_de_emphasis:2; + u32 fladj_value; + const char *hsphy_interface; }; diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index bcf83c0..33834aa 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -113,7 +113,7 @@ config USB_GADGET_VBUS_DRAW config USB_GADGET_STORAGE_NUM_BUFFERS int "Number of storage pipeline buffers" - range 2 4 + range 2 32 default 2 help Usually 2 buffers are enough to establish a good buffering diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index b474499..8b14c2a 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -839,9 +839,7 @@ int usb_add_config(struct usb_composite_dev *cdev, } } - /* set_alt(), or next bind(), sets up - * ep->driver_data as needed. - */ + /* set_alt(), or next bind(), sets up ep->claimed as needed */ usb_ep_autoconfig_reset(cdev->gadget); done: @@ -1506,6 +1504,8 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) } else { cdev->desc.bcdUSB = cpu_to_le16(0x0210); } + } else { + cdev->desc.bcdUSB = cpu_to_le16(0x0200); } value = min(w_length, (u16) sizeof cdev->desc); diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index 6399c10..30fdab0 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -53,13 +53,13 @@ * the restrictions that may apply. Some combinations of driver * and hardware won't be able to autoconfigure. * - * On success, this returns an un-claimed usb_ep, and modifies the endpoint + * On success, this returns an claimed usb_ep, and modifies the endpoint * descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value * is initialized as if the endpoint were used at full speed and * the bmAttribute field in the ep companion descriptor is * updated with the assigned number of streams if it is * different from the original value. To prevent the endpoint - * from being returned by a later autoconfig call, claim it by + * from being returned by a later autoconfig call, claims it by * assigning ep->claimed to true. * * On failure, this returns a null endpoint descriptor. @@ -154,10 +154,10 @@ EXPORT_SYMBOL_GPL(usb_ep_autoconfig_ss); * USB controller, and it can't know all the restrictions that may apply. * Some combinations of driver and hardware won't be able to autoconfigure. * - * On success, this returns an un-claimed usb_ep, and modifies the endpoint + * On success, this returns an claimed usb_ep, and modifies the endpoint * descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value * is initialized as if the endpoint were used at full speed. To prevent - * the endpoint from being returned by a later autoconfig call, claim it + * the endpoint from being returned by a later autoconfig call, claims it * by assigning ep->claimed to true. * * On failure, this returns a null endpoint descriptor. @@ -172,6 +172,23 @@ struct usb_ep *usb_ep_autoconfig( EXPORT_SYMBOL_GPL(usb_ep_autoconfig); /** + * usb_ep_autoconfig_release - releases endpoint and set it to initial state + * @ep: endpoint which should be released + * + * This function can be used during function bind for endpoints obtained + * from usb_ep_autoconfig(). It unclaims endpoint claimed by + * usb_ep_autoconfig() to make it available for other functions. Endpoint + * which was released is no longer invalid and shouldn't be used in + * context of function which released it. + */ +void usb_ep_autoconfig_release(struct usb_ep *ep) +{ + ep->claimed = false; + ep->driver_data = NULL; +} +EXPORT_SYMBOL_GPL(usb_ep_autoconfig_release); + +/** * usb_ep_autoconfig_reset - reset endpoint autoconfig state * @gadget: device for which autoconfig state will be reset * diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c index be9df09..22e723d 100644 --- a/drivers/usb/gadget/function/f_acm.c +++ b/drivers/usb/gadget/function/f_acm.c @@ -428,21 +428,18 @@ static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) /* we know alt == 0, so this is an activation or a reset */ if (intf == acm->ctrl_id) { - if (acm->notify->driver_data) { - dev_vdbg(&cdev->gadget->dev, - "reset acm control interface %d\n", intf); - usb_ep_disable(acm->notify); - } + dev_vdbg(&cdev->gadget->dev, + "reset acm control interface %d\n", intf); + usb_ep_disable(acm->notify); if (!acm->notify->desc) if (config_ep_by_speed(cdev->gadget, f, acm->notify)) return -EINVAL; usb_ep_enable(acm->notify); - acm->notify->driver_data = acm; } else if (intf == acm->data_id) { - if (acm->port.in->driver_data) { + if (acm->notify->enabled) { dev_dbg(&cdev->gadget->dev, "reset acm ttyGS%d\n", acm->port_num); gserial_disconnect(&acm->port); @@ -475,7 +472,6 @@ static void acm_disable(struct usb_function *f) dev_dbg(&cdev->gadget->dev, "acm ttyGS%d deactivated\n", acm->port_num); gserial_disconnect(&acm->port); usb_ep_disable(acm->notify); - acm->notify->driver_data = NULL; } /*-------------------------------------------------------------------------*/ @@ -655,19 +651,16 @@ acm_bind(struct usb_configuration *c, struct usb_function *f) if (!ep) goto fail; acm->port.in = ep; - ep->driver_data = cdev; /* claim */ ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_out_desc); if (!ep) goto fail; acm->port.out = ep; - ep->driver_data = cdev; /* claim */ ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_notify_desc); if (!ep) goto fail; acm->notify = ep; - ep->driver_data = cdev; /* claim */ /* allocate notification */ acm->notify_req = gs_alloc_req(ep, @@ -709,14 +702,6 @@ fail: if (acm->notify_req) gs_free_req(acm->notify, acm->notify_req); - /* we might as well release our claims on endpoints */ - if (acm->notify) - acm->notify->driver_data = NULL; - if (acm->port.out) - acm->port.out->driver_data = NULL; - if (acm->port.in) - acm->port.in->driver_data = NULL; - ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status); return status; diff --git a/drivers/usb/gadget/function/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c index 7b7424f..4abca70 100644 --- a/drivers/usb/gadget/function/f_ecm.c +++ b/drivers/usb/gadget/function/f_ecm.c @@ -541,24 +541,21 @@ static int ecm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (alt != 0) goto fail; - if (ecm->notify->driver_data) { - VDBG(cdev, "reset ecm control %d\n", intf); - usb_ep_disable(ecm->notify); - } + VDBG(cdev, "reset ecm control %d\n", intf); + usb_ep_disable(ecm->notify); if (!(ecm->notify->desc)) { VDBG(cdev, "init ecm ctrl %d\n", intf); if (config_ep_by_speed(cdev->gadget, f, ecm->notify)) goto fail; } usb_ep_enable(ecm->notify); - ecm->notify->driver_data = ecm; /* Data interface has two altsettings, 0 and 1 */ } else if (intf == ecm->data_id) { if (alt > 1) goto fail; - if (ecm->port.in_ep->driver_data) { + if (ecm->port.in_ep->enabled) { DBG(cdev, "reset ecm\n"); gether_disconnect(&ecm->port); } @@ -618,7 +615,7 @@ static int ecm_get_alt(struct usb_function *f, unsigned intf) if (intf == ecm->ctrl_id) return 0; - return ecm->port.in_ep->driver_data ? 1 : 0; + return ecm->port.in_ep->enabled ? 1 : 0; } static void ecm_disable(struct usb_function *f) @@ -628,14 +625,11 @@ static void ecm_disable(struct usb_function *f) DBG(cdev, "ecm deactivated\n"); - if (ecm->port.in_ep->driver_data) + if (ecm->port.in_ep->enabled) gether_disconnect(&ecm->port); - if (ecm->notify->driver_data) { - usb_ep_disable(ecm->notify); - ecm->notify->driver_data = NULL; - ecm->notify->desc = NULL; - } + usb_ep_disable(ecm->notify); + ecm->notify->desc = NULL; } /*-------------------------------------------------------------------------*/ @@ -750,13 +744,11 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) if (!ep) goto fail; ecm->port.in_ep = ep; - ep->driver_data = cdev; /* claim */ ep = usb_ep_autoconfig(cdev->gadget, &fs_ecm_out_desc); if (!ep) goto fail; ecm->port.out_ep = ep; - ep->driver_data = cdev; /* claim */ /* NOTE: a status/notification endpoint is *OPTIONAL* but we * don't treat it that way. It's simpler, and some newer CDC @@ -766,7 +758,6 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f) if (!ep) goto fail; ecm->notify = ep; - ep->driver_data = cdev; /* claim */ status = -ENOMEM; @@ -820,14 +811,6 @@ fail: usb_ep_free_request(ecm->notify, ecm->notify_req); } - /* we might as well release our claims on endpoints */ - if (ecm->notify) - ecm->notify->driver_data = NULL; - if (ecm->port.out_ep) - ecm->port.out_ep->driver_data = NULL; - if (ecm->port.in_ep) - ecm->port.in_ep->driver_data = NULL; - ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); return status; diff --git a/drivers/usb/gadget/function/f_eem.c b/drivers/usb/gadget/function/f_eem.c index c9e90de..9a55757 100644 --- a/drivers/usb/gadget/function/f_eem.c +++ b/drivers/usb/gadget/function/f_eem.c @@ -195,11 +195,8 @@ static int eem_set_alt(struct usb_function *f, unsigned intf, unsigned alt) goto fail; if (intf == eem->ctrl_id) { - - if (eem->port.in_ep->driver_data) { - DBG(cdev, "reset eem\n"); - gether_disconnect(&eem->port); - } + DBG(cdev, "reset eem\n"); + gether_disconnect(&eem->port); if (!eem->port.in_ep->desc || !eem->port.out_ep->desc) { DBG(cdev, "init eem\n"); @@ -237,7 +234,7 @@ static void eem_disable(struct usb_function *f) DBG(cdev, "eem deactivated\n"); - if (eem->port.in_ep->driver_data) + if (eem->port.in_ep->enabled) gether_disconnect(&eem->port); } @@ -293,13 +290,11 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f) if (!ep) goto fail; eem->port.in_ep = ep; - ep->driver_data = cdev; /* claim */ ep = usb_ep_autoconfig(cdev->gadget, &eem_fs_out_desc); if (!ep) goto fail; eem->port.out_ep = ep; - ep->driver_data = cdev; /* claim */ status = -ENOMEM; @@ -325,11 +320,6 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f) return 0; fail: - if (eem->port.out_ep) - eem->port.out_ep->driver_data = NULL; - if (eem->port.in_ep) - eem->port.in_ep->driver_data = NULL; - ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); return status; diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index 6df9715..21fcf18 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -492,10 +492,7 @@ static void hidg_disable(struct usb_function *f) struct f_hidg_req_list *list, *next; usb_ep_disable(hidg->in_ep); - hidg->in_ep->driver_data = NULL; - usb_ep_disable(hidg->out_ep); - hidg->out_ep->driver_data = NULL; list_for_each_entry_safe(list, next, &hidg->completed_out_req, list) { list_del(&list->list); @@ -513,8 +510,7 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (hidg->in_ep != NULL) { /* restart endpoint */ - if (hidg->in_ep->driver_data != NULL) - usb_ep_disable(hidg->in_ep); + usb_ep_disable(hidg->in_ep); status = config_ep_by_speed(f->config->cdev->gadget, f, hidg->in_ep); @@ -533,8 +529,7 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (hidg->out_ep != NULL) { /* restart endpoint */ - if (hidg->out_ep->driver_data != NULL) - usb_ep_disable(hidg->out_ep); + usb_ep_disable(hidg->out_ep); status = config_ep_by_speed(f->config->cdev->gadget, f, hidg->out_ep); @@ -566,7 +561,6 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt) hidg->out_ep->name, status); } else { usb_ep_disable(hidg->out_ep); - hidg->out_ep->driver_data = NULL; status = -ENOMEM; goto fail; } @@ -614,13 +608,11 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f) ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_in_ep_desc); if (!ep) goto fail; - ep->driver_data = c->cdev; /* claim */ hidg->in_ep = ep; ep = usb_ep_autoconfig(c->cdev->gadget, &hidg_fs_out_ep_desc); if (!ep) goto fail; - ep->driver_data = c->cdev; /* claim */ hidg->out_ep = ep; /* preallocate request and buffer */ diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c index 6e2fe63..6b2102b 100644 --- a/drivers/usb/gadget/function/f_loopback.c +++ b/drivers/usb/gadget/function/f_loopback.c @@ -34,6 +34,9 @@ struct f_loopback { struct usb_ep *in_ep; struct usb_ep *out_ep; + + unsigned qlen; + unsigned buflen; }; static inline struct f_loopback *func_to_loop(struct usb_function *f) @@ -41,13 +44,10 @@ static inline struct f_loopback *func_to_loop(struct usb_function *f) return container_of(f, struct f_loopback, function); } -static unsigned qlen; -static unsigned buflen; - /*-------------------------------------------------------------------------*/ static struct usb_interface_descriptor loopback_intf = { - .bLength = sizeof loopback_intf, + .bLength = sizeof(loopback_intf), .bDescriptorType = USB_DT_INTERFACE, .bNumEndpoints = 2, @@ -195,12 +195,10 @@ autoconf_fail: f->name, cdev->gadget->name); return -ENODEV; } - loop->in_ep->driver_data = cdev; /* claim */ loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_loop_sink_desc); if (!loop->out_ep) goto autoconf_fail; - loop->out_ep->driver_data = cdev; /* claim */ /* support high speed hardware */ hs_loop_source_desc.bEndpointAddress = @@ -245,22 +243,38 @@ static void loopback_complete(struct usb_ep *ep, struct usb_request *req) int status = req->status; switch (status) { - case 0: /* normal completion? */ if (ep == loop->out_ep) { - req->zero = (req->actual < req->length); - req->length = req->actual; + /* + * We received some data from the host so let's + * queue it so host can read the from our in ep + */ + struct usb_request *in_req = req->context; + + in_req->zero = (req->actual < req->length); + in_req->length = req->actual; + ep = loop->in_ep; + req = in_req; + } else { + /* + * We have just looped back a bunch of data + * to host. Now let's wait for some more data. + */ + req = req->context; + ep = loop->out_ep; } - /* queue the buffer for some later OUT packet */ - req->length = buflen; + /* queue the buffer back to host or for next bunch of data */ status = usb_ep_queue(ep, req, GFP_ATOMIC); - if (status == 0) + if (status == 0) { return; + } else { + ERROR(cdev, "Unable to loop back buffer to %s: %d\n", + ep->name, status); + goto free_req; + } /* "should never get here" */ - /* FALLTHROUGH */ - default: ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name, status, req->actual, req->length); @@ -274,6 +288,10 @@ static void loopback_complete(struct usb_ep *ep, struct usb_request *req) case -ECONNABORTED: /* hardware forced ep reset */ case -ECONNRESET: /* request dequeued */ case -ESHUTDOWN: /* disconnect from host */ +free_req: + usb_ep_free_request(ep == loop->in_ep ? + loop->out_ep : loop->in_ep, + req->context); free_ep_req(ep, req); return; } @@ -290,53 +308,77 @@ static void disable_loopback(struct f_loopback *loop) static inline struct usb_request *lb_alloc_ep_req(struct usb_ep *ep, int len) { - return alloc_ep_req(ep, len, buflen); + struct f_loopback *loop = ep->driver_data; + + return alloc_ep_req(ep, len, loop->buflen); } -static int enable_endpoint(struct usb_composite_dev *cdev, struct f_loopback *loop, - struct usb_ep *ep) +static int alloc_requests(struct usb_composite_dev *cdev, + struct f_loopback *loop) { - struct usb_request *req; - unsigned i; - int result; - - /* - * one endpoint writes data back IN to the host while another endpoint - * just reads OUT packets - */ - result = config_ep_by_speed(cdev->gadget, &(loop->function), ep); - if (result) - goto fail0; - result = usb_ep_enable(ep); - if (result < 0) - goto fail0; - ep->driver_data = loop; + struct usb_request *in_req, *out_req; + int i; + int result = 0; /* * allocate a bunch of read buffers and queue them all at once. - * we buffer at most 'qlen' transfers; fewer if any need more - * than 'buflen' bytes each. + * we buffer at most 'qlen' transfers; We allocate buffers only + * for out transfer and reuse them in IN transfers to implement + * our loopback functionality */ - for (i = 0; i < qlen && result == 0; i++) { - req = lb_alloc_ep_req(ep, 0); - if (!req) - goto fail1; + for (i = 0; i < loop->qlen && result == 0; i++) { + result = -ENOMEM; + + in_req = usb_ep_alloc_request(loop->in_ep, GFP_KERNEL); + if (!in_req) + goto fail; + + out_req = lb_alloc_ep_req(loop->out_ep, 0); + if (!out_req) + goto fail_in; - req->complete = loopback_complete; - result = usb_ep_queue(ep, req, GFP_ATOMIC); + in_req->complete = loopback_complete; + out_req->complete = loopback_complete; + + in_req->buf = out_req->buf; + /* length will be set in complete routine */ + in_req->context = out_req; + out_req->context = in_req; + + result = usb_ep_queue(loop->out_ep, out_req, GFP_ATOMIC); if (result) { ERROR(cdev, "%s queue req --> %d\n", - ep->name, result); - goto fail1; + loop->out_ep->name, result); + goto fail_out; } } return 0; -fail1: - usb_ep_disable(ep); +fail_out: + free_ep_req(loop->out_ep, out_req); +fail_in: + usb_ep_free_request(loop->in_ep, in_req); +fail: + return result; +} + +static int enable_endpoint(struct usb_composite_dev *cdev, + struct f_loopback *loop, struct usb_ep *ep) +{ + int result; + + result = config_ep_by_speed(cdev->gadget, &(loop->function), ep); + if (result) + goto out; -fail0: + result = usb_ep_enable(ep); + if (result < 0) + goto out; + ep->driver_data = loop; + result = 0; + +out: return result; } @@ -347,13 +389,24 @@ enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop) result = enable_endpoint(cdev, loop, loop->in_ep); if (result) - return result; + goto out; result = enable_endpoint(cdev, loop, loop->out_ep); if (result) - return result; + goto disable_in; + + result = alloc_requests(cdev, loop); + if (result) + goto disable_out; DBG(cdev, "%s enabled\n", loop->function.name); + return 0; + +disable_out: + usb_ep_disable(loop->out_ep); +disable_in: + usb_ep_disable(loop->in_ep); +out: return result; } @@ -364,8 +417,7 @@ static int loopback_set_alt(struct usb_function *f, struct usb_composite_dev *cdev = f->config->cdev; /* we know alt is zero */ - if (loop->in_ep->driver_data) - disable_loopback(loop); + disable_loopback(loop); return enable_loopback(cdev, loop); } @@ -391,10 +443,10 @@ static struct usb_function *loopback_alloc(struct usb_function_instance *fi) lb_opts->refcnt++; mutex_unlock(&lb_opts->lock); - buflen = lb_opts->bulk_buflen; - qlen = lb_opts->qlen; - if (!qlen) - qlen = 32; + loop->buflen = lb_opts->bulk_buflen; + loop->qlen = lb_opts->qlen; + if (!loop->qlen) + loop->qlen = 32; loop->function.name = "loopback"; loop->function.bind = loopback_bind; @@ -434,7 +486,7 @@ static ssize_t f_lb_opts_qlen_show(struct f_lb_opts *opts, char *page) int result; mutex_lock(&opts->lock); - result = sprintf(page, "%d", opts->qlen); + result = sprintf(page, "%d\n", opts->qlen); mutex_unlock(&opts->lock); return result; @@ -473,7 +525,7 @@ static ssize_t f_lb_opts_bulk_buflen_show(struct f_lb_opts *opts, char *page) int result; mutex_lock(&opts->lock); - result = sprintf(page, "%d", opts->bulk_buflen); + result = sprintf(page, "%d\n", opts->bulk_buflen); mutex_unlock(&opts->lock); return result; diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index a6eb537..cd54e72 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -2258,12 +2258,10 @@ reset: /* Disable the endpoints */ if (fsg->bulk_in_enabled) { usb_ep_disable(fsg->bulk_in); - fsg->bulk_in->driver_data = NULL; fsg->bulk_in_enabled = 0; } if (fsg->bulk_out_enabled) { usb_ep_disable(fsg->bulk_out); - fsg->bulk_out->driver_data = NULL; fsg->bulk_out_enabled = 0; } @@ -2662,10 +2660,12 @@ EXPORT_SYMBOL_GPL(fsg_common_put); /* check if fsg_num_buffers is within a valid range */ static inline int fsg_num_buffers_validate(unsigned int fsg_num_buffers) { - if (fsg_num_buffers >= 2 && fsg_num_buffers <= 4) +#define FSG_MAX_NUM_BUFFERS 32 + + if (fsg_num_buffers >= 2 && fsg_num_buffers <= FSG_MAX_NUM_BUFFERS) return 0; pr_err("fsg_num_buffers %u is out of range (%d to %d)\n", - fsg_num_buffers, 2, 4); + fsg_num_buffers, 2, FSG_MAX_NUM_BUFFERS); return -EINVAL; } @@ -3070,13 +3070,11 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f) ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc); if (!ep) goto autoconf_fail; - ep->driver_data = fsg->common; /* claim the endpoint */ fsg->bulk_in = ep; ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc); if (!ep) goto autoconf_fail; - ep->driver_data = fsg->common; /* claim the endpoint */ fsg->bulk_out = ep; /* Assume endpoint addresses are the same for both speeds */ diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index a287a48..ce3c8a6 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -302,8 +302,7 @@ static int f_midi_start_ep(struct f_midi *midi, int err; struct usb_composite_dev *cdev = f->config->cdev; - if (ep->driver_data) - usb_ep_disable(ep); + usb_ep_disable(ep); err = config_ep_by_speed(midi->gadget, f, ep); if (err) { @@ -341,8 +340,7 @@ static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (err) return err; - if (midi->out_ep->driver_data) - usb_ep_disable(midi->out_ep); + usb_ep_disable(midi->out_ep); err = config_ep_by_speed(midi->gadget, f, midi->out_ep); if (err) { @@ -547,10 +545,16 @@ static void f_midi_transmit(struct f_midi *midi, struct usb_request *req) } } - if (req->length > 0) - usb_ep_queue(ep, req, GFP_ATOMIC); - else + if (req->length > 0) { + int err; + + err = usb_ep_queue(ep, req, GFP_ATOMIC); + if (err < 0) + ERROR(midi, "%s queue req: %d\n", + midi->in_ep->name, err); + } else { free_ep_req(ep, req); + } } static void f_midi_in_tasklet(unsigned long data) @@ -757,12 +761,10 @@ static int f_midi_bind(struct usb_configuration *c, struct usb_function *f) midi->in_ep = usb_ep_autoconfig(cdev->gadget, &bulk_in_desc); if (!midi->in_ep) goto fail; - midi->in_ep->driver_data = cdev; /* claim */ midi->out_ep = usb_ep_autoconfig(cdev->gadget, &bulk_out_desc); if (!midi->out_ep) goto fail; - midi->out_ep->driver_data = cdev; /* claim */ /* allocate temporary function list */ midi_function = kcalloc((MAX_PORTS * 4) + 9, sizeof(*midi_function), @@ -889,12 +891,6 @@ fail_f_midi: fail: f_midi_unregister_card(midi); fail_register: - /* we might as well release our claims on endpoints */ - if (midi->out_ep) - midi->out_ep->driver_data = NULL; - if (midi->in_ep) - midi->in_ep->driver_data = NULL; - ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); return status; diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c index 3f05c6bd..b6f7ed7 100644 --- a/drivers/usb/gadget/function/f_ncm.c +++ b/drivers/usb/gadget/function/f_ncm.c @@ -586,7 +586,7 @@ static void ncm_ep0out_complete(struct usb_ep *ep, struct usb_request *req) unsigned in_size; struct usb_function *f = req->context; struct f_ncm *ncm = func_to_ncm(f); - struct usb_composite_dev *cdev = ep->driver_data; + struct usb_composite_dev *cdev = f->config->cdev; req->context = NULL; if (req->status || req->actual != req->length) { @@ -803,10 +803,8 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (alt != 0) goto fail; - if (ncm->notify->driver_data) { - DBG(cdev, "reset ncm control %d\n", intf); - usb_ep_disable(ncm->notify); - } + DBG(cdev, "reset ncm control %d\n", intf); + usb_ep_disable(ncm->notify); if (!(ncm->notify->desc)) { DBG(cdev, "init ncm ctrl %d\n", intf); @@ -814,14 +812,13 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt) goto fail; } usb_ep_enable(ncm->notify); - ncm->notify->driver_data = ncm; /* Data interface has two altsettings, 0 and 1 */ } else if (intf == ncm->data_id) { if (alt > 1) goto fail; - if (ncm->port.in_ep->driver_data) { + if (ncm->port.in_ep->enabled) { DBG(cdev, "reset ncm\n"); ncm->timer_stopping = true; ncm->netdev = NULL; @@ -885,7 +882,7 @@ static int ncm_get_alt(struct usb_function *f, unsigned intf) if (intf == ncm->ctrl_id) return 0; - return ncm->port.in_ep->driver_data ? 1 : 0; + return ncm->port.in_ep->enabled ? 1 : 0; } static struct sk_buff *package_for_tx(struct f_ncm *ncm) @@ -1276,15 +1273,14 @@ static void ncm_disable(struct usb_function *f) DBG(cdev, "ncm deactivated\n"); - if (ncm->port.in_ep->driver_data) { + if (ncm->port.in_ep->enabled) { ncm->timer_stopping = true; ncm->netdev = NULL; gether_disconnect(&ncm->port); } - if (ncm->notify->driver_data) { + if (ncm->notify->enabled) { usb_ep_disable(ncm->notify); - ncm->notify->driver_data = NULL; ncm->notify->desc = NULL; } } @@ -1402,19 +1398,16 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f) if (!ep) goto fail; ncm->port.in_ep = ep; - ep->driver_data = cdev; /* claim */ ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_out_desc); if (!ep) goto fail; ncm->port.out_ep = ep; - ep->driver_data = cdev; /* claim */ ep = usb_ep_autoconfig(cdev->gadget, &fs_ncm_notify_desc); if (!ep) goto fail; ncm->notify = ep; - ep->driver_data = cdev; /* claim */ status = -ENOMEM; @@ -1468,14 +1461,6 @@ fail: usb_ep_free_request(ncm->notify, ncm->notify_req); } - /* we might as well release our claims on endpoints */ - if (ncm->notify) - ncm->notify->driver_data = NULL; - if (ncm->port.out_ep) - ncm->port.out_ep->driver_data = NULL; - if (ncm->port.in_ep) - ncm->port.in_ep->driver_data = NULL; - ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); return status; diff --git a/drivers/usb/gadget/function/f_obex.c b/drivers/usb/gadget/function/f_obex.c index 5460426..1c3d30a 100644 --- a/drivers/usb/gadget/function/f_obex.c +++ b/drivers/usb/gadget/function/f_obex.c @@ -206,7 +206,7 @@ static int obex_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (alt > 1) goto fail; - if (obex->port.in->driver_data) { + if (obex->port.in->enabled) { dev_dbg(&cdev->gadget->dev, "reset obex ttyGS%d\n", obex->port_num); gserial_disconnect(&obex->port); @@ -348,13 +348,11 @@ static int obex_bind(struct usb_configuration *c, struct usb_function *f) if (!ep) goto fail; obex->port.in = ep; - ep->driver_data = cdev; /* claim */ ep = usb_ep_autoconfig(cdev->gadget, &obex_fs_ep_out_desc); if (!ep) goto fail; obex->port.out = ep; - ep->driver_data = cdev; /* claim */ /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at @@ -378,12 +376,6 @@ static int obex_bind(struct usb_configuration *c, struct usb_function *f) return 0; fail: - /* we might as well release our claims on endpoints */ - if (obex->port.out) - obex->port.out->driver_data = NULL; - if (obex->port.in) - obex->port.in->driver_data = NULL; - ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status); return status; diff --git a/drivers/usb/gadget/function/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c index c0c3ef2..62a1987 100644 --- a/drivers/usb/gadget/function/f_phonet.c +++ b/drivers/usb/gadget/function/f_phonet.c @@ -418,7 +418,7 @@ static int pn_set_alt(struct usb_function *f, unsigned intf, unsigned alt) spin_lock(&port->lock); - if (fp->in_ep->driver_data) + if (fp->in_ep->enabled) __pn_reset(f); if (alt == 1) { @@ -530,13 +530,11 @@ static int pn_bind(struct usb_configuration *c, struct usb_function *f) if (!ep) goto err; fp->out_ep = ep; - ep->driver_data = fp; /* Claim */ ep = usb_ep_autoconfig(gadget, &pn_fs_source_desc); if (!ep) goto err; fp->in_ep = ep; - ep->driver_data = fp; /* Claim */ pn_hs_sink_desc.bEndpointAddress = pn_fs_sink_desc.bEndpointAddress; pn_hs_source_desc.bEndpointAddress = pn_fs_source_desc.bEndpointAddress; @@ -575,10 +573,6 @@ err_req: usb_ep_free_request(fp->out_ep, fp->out_reqv[i]); usb_free_all_descriptors(f); err: - if (fp->out_ep) - fp->out_ep->driver_data = NULL; - if (fp->in_ep) - fp->in_ep->driver_data = NULL; ERROR(cdev, "USB CDC Phonet: cannot autoconfigure\n"); return status; } diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c index 8e2b6be..7fb3209 100644 --- a/drivers/usb/gadget/function/f_printer.c +++ b/drivers/usb/gadget/function/f_printer.c @@ -1039,12 +1039,10 @@ autoconf_fail: cdev->gadget->name); return -ENODEV; } - in_ep->driver_data = in_ep; /* claim */ out_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_out_desc); if (!out_ep) goto autoconf_fail; - out_ep->driver_data = out_ep; /* claim */ /* assumes that all endpoints are dual-speed */ hs_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress; diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index 32985da..fd301ed 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -543,22 +543,20 @@ static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt) /* we know alt == 0 */ if (intf == rndis->ctrl_id) { - if (rndis->notify->driver_data) { - VDBG(cdev, "reset rndis control %d\n", intf); - usb_ep_disable(rndis->notify); - } + VDBG(cdev, "reset rndis control %d\n", intf); + usb_ep_disable(rndis->notify); + if (!rndis->notify->desc) { VDBG(cdev, "init rndis ctrl %d\n", intf); if (config_ep_by_speed(cdev->gadget, f, rndis->notify)) goto fail; } usb_ep_enable(rndis->notify); - rndis->notify->driver_data = rndis; } else if (intf == rndis->data_id) { struct net_device *net; - if (rndis->port.in_ep->driver_data) { + if (rndis->port.in_ep->enabled) { DBG(cdev, "reset rndis\n"); gether_disconnect(&rndis->port); } @@ -612,7 +610,7 @@ static void rndis_disable(struct usb_function *f) struct f_rndis *rndis = func_to_rndis(f); struct usb_composite_dev *cdev = f->config->cdev; - if (!rndis->notify->driver_data) + if (!rndis->notify->enabled) return; DBG(cdev, "rndis deactivated\n"); @@ -621,7 +619,6 @@ static void rndis_disable(struct usb_function *f) gether_disconnect(&rndis->port); usb_ep_disable(rndis->notify); - rndis->notify->driver_data = NULL; } /*-------------------------------------------------------------------------*/ @@ -745,13 +742,11 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) if (!ep) goto fail; rndis->port.in_ep = ep; - ep->driver_data = cdev; /* claim */ ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc); if (!ep) goto fail; rndis->port.out_ep = ep; - ep->driver_data = cdev; /* claim */ /* NOTE: a status/notification endpoint is, strictly speaking, * optional. We don't treat it that way though! It's simpler, @@ -761,7 +756,6 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) if (!ep) goto fail; rndis->notify = ep; - ep->driver_data = cdev; /* claim */ status = -ENOMEM; @@ -829,14 +823,6 @@ fail: usb_ep_free_request(rndis->notify, rndis->notify_req); } - /* we might as well release our claims on endpoints */ - if (rndis->notify) - rndis->notify->driver_data = NULL; - if (rndis->port.out_ep) - rndis->port.out_ep->driver_data = NULL; - if (rndis->port.in_ep) - rndis->port.in_ep->driver_data = NULL; - ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); return status; diff --git a/drivers/usb/gadget/function/f_serial.c b/drivers/usb/gadget/function/f_serial.c index 1d162e2..ba705e0 100644 --- a/drivers/usb/gadget/function/f_serial.c +++ b/drivers/usb/gadget/function/f_serial.c @@ -153,7 +153,7 @@ static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt) /* we know alt == 0, so this is an activation or a reset */ - if (gser->port.in->driver_data) { + if (gser->port.in->enabled) { dev_dbg(&cdev->gadget->dev, "reset generic ttyGS%d\n", gser->port_num); gserial_disconnect(&gser->port); @@ -219,13 +219,11 @@ static int gser_bind(struct usb_configuration *c, struct usb_function *f) if (!ep) goto fail; gser->port.in = ep; - ep->driver_data = cdev; /* claim */ ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_out_desc); if (!ep) goto fail; gser->port.out = ep; - ep->driver_data = cdev; /* claim */ /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at @@ -249,12 +247,6 @@ static int gser_bind(struct usb_configuration *c, struct usb_function *f) return 0; fail: - /* we might as well release our claims on endpoints */ - if (gser->port.out) - gser->port.out->driver_data = NULL; - if (gser->port.in) - gser->port.in->driver_data = NULL; - ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); return status; diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c index cbfaf86..d7646d3 100644 --- a/drivers/usb/gadget/function/f_sourcesink.c +++ b/drivers/usb/gadget/function/f_sourcesink.c @@ -50,6 +50,13 @@ struct f_sourcesink { struct usb_ep *iso_in_ep; struct usb_ep *iso_out_ep; int cur_alt; + + unsigned pattern; + unsigned isoc_interval; + unsigned isoc_maxpacket; + unsigned isoc_mult; + unsigned isoc_maxburst; + unsigned buflen; }; static inline struct f_sourcesink *func_to_ss(struct usb_function *f) @@ -57,13 +64,6 @@ static inline struct f_sourcesink *func_to_ss(struct usb_function *f) return container_of(f, struct f_sourcesink, function); } -static unsigned pattern; -static unsigned isoc_interval; -static unsigned isoc_maxpacket; -static unsigned isoc_mult; -static unsigned isoc_maxburst; -static unsigned buflen; - /*-------------------------------------------------------------------------*/ static struct usb_interface_descriptor source_sink_intf_alt0 = { @@ -298,7 +298,9 @@ static struct usb_gadget_strings *sourcesink_strings[] = { static inline struct usb_request *ss_alloc_ep_req(struct usb_ep *ep, int len) { - return alloc_ep_req(ep, len, buflen); + struct f_sourcesink *ss = ep->driver_data; + + return alloc_ep_req(ep, len, ss->buflen); } void free_ep_req(struct usb_ep *ep, struct usb_request *req) @@ -311,13 +313,9 @@ static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep) { int value; - if (ep->driver_data) { - value = usb_ep_disable(ep); - if (value < 0) - DBG(cdev, "disable %s --> %d\n", - ep->name, value); - ep->driver_data = NULL; - } + value = usb_ep_disable(ep); + if (value < 0) + DBG(cdev, "disable %s --> %d\n", ep->name, value); } void disable_endpoints(struct usb_composite_dev *cdev, @@ -355,42 +353,37 @@ autoconf_fail: f->name, cdev->gadget->name); return -ENODEV; } - ss->in_ep->driver_data = cdev; /* claim */ ss->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_sink_desc); if (!ss->out_ep) goto autoconf_fail; - ss->out_ep->driver_data = cdev; /* claim */ /* sanity check the isoc module parameters */ - if (isoc_interval < 1) - isoc_interval = 1; - if (isoc_interval > 16) - isoc_interval = 16; - if (isoc_mult > 2) - isoc_mult = 2; - if (isoc_maxburst > 15) - isoc_maxburst = 15; + if (ss->isoc_interval < 1) + ss->isoc_interval = 1; + if (ss->isoc_interval > 16) + ss->isoc_interval = 16; + if (ss->isoc_mult > 2) + ss->isoc_mult = 2; + if (ss->isoc_maxburst > 15) + ss->isoc_maxburst = 15; /* fill in the FS isoc descriptors from the module parameters */ - fs_iso_source_desc.wMaxPacketSize = isoc_maxpacket > 1023 ? - 1023 : isoc_maxpacket; - fs_iso_source_desc.bInterval = isoc_interval; - fs_iso_sink_desc.wMaxPacketSize = isoc_maxpacket > 1023 ? - 1023 : isoc_maxpacket; - fs_iso_sink_desc.bInterval = isoc_interval; + fs_iso_source_desc.wMaxPacketSize = ss->isoc_maxpacket > 1023 ? + 1023 : ss->isoc_maxpacket; + fs_iso_source_desc.bInterval = ss->isoc_interval; + fs_iso_sink_desc.wMaxPacketSize = ss->isoc_maxpacket > 1023 ? + 1023 : ss->isoc_maxpacket; + fs_iso_sink_desc.bInterval = ss->isoc_interval; /* allocate iso endpoints */ ss->iso_in_ep = usb_ep_autoconfig(cdev->gadget, &fs_iso_source_desc); if (!ss->iso_in_ep) goto no_iso; - ss->iso_in_ep->driver_data = cdev; /* claim */ ss->iso_out_ep = usb_ep_autoconfig(cdev->gadget, &fs_iso_sink_desc); - if (ss->iso_out_ep) { - ss->iso_out_ep->driver_data = cdev; /* claim */ - } else { - ss->iso_in_ep->driver_data = NULL; + if (!ss->iso_out_ep) { + usb_ep_autoconfig_release(ss->iso_in_ep); ss->iso_in_ep = NULL; no_iso: /* @@ -403,8 +396,8 @@ no_iso: ss_source_sink_descs[SS_ALT_IFC_1_OFFSET] = NULL; } - if (isoc_maxpacket > 1024) - isoc_maxpacket = 1024; + if (ss->isoc_maxpacket > 1024) + ss->isoc_maxpacket = 1024; /* support high speed hardware */ hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress; @@ -415,15 +408,15 @@ no_iso: * We assume that the user knows what they are doing and won't * give parameters that their UDC doesn't support. */ - hs_iso_source_desc.wMaxPacketSize = isoc_maxpacket; - hs_iso_source_desc.wMaxPacketSize |= isoc_mult << 11; - hs_iso_source_desc.bInterval = isoc_interval; + hs_iso_source_desc.wMaxPacketSize = ss->isoc_maxpacket; + hs_iso_source_desc.wMaxPacketSize |= ss->isoc_mult << 11; + hs_iso_source_desc.bInterval = ss->isoc_interval; hs_iso_source_desc.bEndpointAddress = fs_iso_source_desc.bEndpointAddress; - hs_iso_sink_desc.wMaxPacketSize = isoc_maxpacket; - hs_iso_sink_desc.wMaxPacketSize |= isoc_mult << 11; - hs_iso_sink_desc.bInterval = isoc_interval; + hs_iso_sink_desc.wMaxPacketSize = ss->isoc_maxpacket; + hs_iso_sink_desc.wMaxPacketSize |= ss->isoc_mult << 11; + hs_iso_sink_desc.bInterval = ss->isoc_interval; hs_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress; /* support super speed hardware */ @@ -437,21 +430,21 @@ no_iso: * We assume that the user knows what they are doing and won't * give parameters that their UDC doesn't support. */ - ss_iso_source_desc.wMaxPacketSize = isoc_maxpacket; - ss_iso_source_desc.bInterval = isoc_interval; - ss_iso_source_comp_desc.bmAttributes = isoc_mult; - ss_iso_source_comp_desc.bMaxBurst = isoc_maxburst; - ss_iso_source_comp_desc.wBytesPerInterval = - isoc_maxpacket * (isoc_mult + 1) * (isoc_maxburst + 1); + ss_iso_source_desc.wMaxPacketSize = ss->isoc_maxpacket; + ss_iso_source_desc.bInterval = ss->isoc_interval; + ss_iso_source_comp_desc.bmAttributes = ss->isoc_mult; + ss_iso_source_comp_desc.bMaxBurst = ss->isoc_maxburst; + ss_iso_source_comp_desc.wBytesPerInterval = ss->isoc_maxpacket * + (ss->isoc_mult + 1) * (ss->isoc_maxburst + 1); ss_iso_source_desc.bEndpointAddress = fs_iso_source_desc.bEndpointAddress; - ss_iso_sink_desc.wMaxPacketSize = isoc_maxpacket; - ss_iso_sink_desc.bInterval = isoc_interval; - ss_iso_sink_comp_desc.bmAttributes = isoc_mult; - ss_iso_sink_comp_desc.bMaxBurst = isoc_maxburst; - ss_iso_sink_comp_desc.wBytesPerInterval = - isoc_maxpacket * (isoc_mult + 1) * (isoc_maxburst + 1); + ss_iso_sink_desc.wMaxPacketSize = ss->isoc_maxpacket; + ss_iso_sink_desc.bInterval = ss->isoc_interval; + ss_iso_sink_comp_desc.bmAttributes = ss->isoc_mult; + ss_iso_sink_comp_desc.bMaxBurst = ss->isoc_maxburst; + ss_iso_sink_comp_desc.wBytesPerInterval = ss->isoc_maxpacket * + (ss->isoc_mult + 1) * (ss->isoc_maxburst + 1); ss_iso_sink_desc.bEndpointAddress = fs_iso_sink_desc.bEndpointAddress; ret = usb_assign_descriptors(f, fs_source_sink_descs, @@ -489,12 +482,13 @@ static int check_read_data(struct f_sourcesink *ss, struct usb_request *req) unsigned i; u8 *buf = req->buf; struct usb_composite_dev *cdev = ss->function.config->cdev; + int max_packet_size = le16_to_cpu(ss->out_ep->desc->wMaxPacketSize); - if (pattern == 2) + if (ss->pattern == 2) return 0; for (i = 0; i < req->actual; i++, buf++) { - switch (pattern) { + switch (ss->pattern) { /* all-zeroes has no synchronization issues */ case 0: @@ -510,7 +504,7 @@ static int check_read_data(struct f_sourcesink *ss, struct usb_request *req) * stutter for any reason, including buffer duplication...) */ case 1: - if (*buf == (u8)(i % 63)) + if (*buf == (u8)((i % max_packet_size) % 63)) continue; break; } @@ -525,14 +519,16 @@ static void reinit_write_data(struct usb_ep *ep, struct usb_request *req) { unsigned i; u8 *buf = req->buf; + int max_packet_size = le16_to_cpu(ep->desc->wMaxPacketSize); + struct f_sourcesink *ss = ep->driver_data; - switch (pattern) { + switch (ss->pattern) { case 0: memset(req->buf, 0, req->length); break; case 1: for (i = 0; i < req->length; i++) - *buf++ = (u8) (i % 63); + *buf++ = (u8) ((i % max_packet_size) % 63); break; case 2: break; @@ -556,7 +552,7 @@ static void source_sink_complete(struct usb_ep *ep, struct usb_request *req) case 0: /* normal completion? */ if (ep == ss->out_ep) { check_read_data(ss, req); - if (pattern != 2) + if (ss->pattern != 2) memset(req->buf, 0x55, req->length); } break; @@ -605,15 +601,16 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in, if (is_iso) { switch (speed) { case USB_SPEED_SUPER: - size = isoc_maxpacket * (isoc_mult + 1) * - (isoc_maxburst + 1); + size = ss->isoc_maxpacket * + (ss->isoc_mult + 1) * + (ss->isoc_maxburst + 1); break; case USB_SPEED_HIGH: - size = isoc_maxpacket * (isoc_mult + 1); + size = ss->isoc_maxpacket * (ss->isoc_mult + 1); break; default: - size = isoc_maxpacket > 1023 ? - 1023 : isoc_maxpacket; + size = ss->isoc_maxpacket > 1023 ? + 1023 : ss->isoc_maxpacket; break; } ep = is_in ? ss->iso_in_ep : ss->iso_out_ep; @@ -629,7 +626,7 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in, req->complete = source_sink_complete; if (is_in) reinit_write_data(ep, req); - else if (pattern != 2) + else if (ss->pattern != 2) memset(req->buf, 0x55, req->length); status = usb_ep_queue(ep, req, GFP_ATOMIC); @@ -683,7 +680,6 @@ enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss, fail: ep = ss->in_ep; usb_ep_disable(ep); - ep->driver_data = NULL; return result; } @@ -702,7 +698,6 @@ fail: fail2: ep = ss->out_ep; usb_ep_disable(ep); - ep->driver_data = NULL; goto fail; } @@ -724,10 +719,8 @@ fail2: if (result < 0) { fail3: ep = ss->iso_in_ep; - if (ep) { + if (ep) usb_ep_disable(ep); - ep->driver_data = NULL; - } goto fail2; } } @@ -746,7 +739,6 @@ fail3: result = source_sink_start_ep(ss, false, true, speed); if (result < 0) { usb_ep_disable(ep); - ep->driver_data = NULL; goto fail3; } } @@ -763,8 +755,7 @@ static int sourcesink_set_alt(struct usb_function *f, struct f_sourcesink *ss = func_to_ss(f); struct usb_composite_dev *cdev = f->config->cdev; - if (ss->in_ep->driver_data) - disable_source_sink(ss); + disable_source_sink(ss); return enable_source_sink(cdev, ss, alt); } @@ -872,12 +863,12 @@ static struct usb_function *source_sink_alloc_func( ss_opts->refcnt++; mutex_unlock(&ss_opts->lock); - pattern = ss_opts->pattern; - isoc_interval = ss_opts->isoc_interval; - isoc_maxpacket = ss_opts->isoc_maxpacket; - isoc_mult = ss_opts->isoc_mult; - isoc_maxburst = ss_opts->isoc_maxburst; - buflen = ss_opts->bulk_buflen; + ss->pattern = ss_opts->pattern; + ss->isoc_interval = ss_opts->isoc_interval; + ss->isoc_maxpacket = ss_opts->isoc_maxpacket; + ss->isoc_mult = ss_opts->isoc_mult; + ss->isoc_maxburst = ss_opts->isoc_maxburst; + ss->buflen = ss_opts->bulk_buflen; ss->function.name = "source/sink"; ss->function.bind = sourcesink_bind; @@ -919,7 +910,7 @@ static ssize_t f_ss_opts_pattern_show(struct f_ss_opts *opts, char *page) int result; mutex_lock(&opts->lock); - result = sprintf(page, "%u", opts->pattern); + result = sprintf(page, "%u\n", opts->pattern); mutex_unlock(&opts->lock); return result; @@ -963,7 +954,7 @@ static ssize_t f_ss_opts_isoc_interval_show(struct f_ss_opts *opts, char *page) int result; mutex_lock(&opts->lock); - result = sprintf(page, "%u", opts->isoc_interval); + result = sprintf(page, "%u\n", opts->isoc_interval); mutex_unlock(&opts->lock); return result; @@ -1007,7 +998,7 @@ static ssize_t f_ss_opts_isoc_maxpacket_show(struct f_ss_opts *opts, char *page) int result; mutex_lock(&opts->lock); - result = sprintf(page, "%u", opts->isoc_maxpacket); + result = sprintf(page, "%u\n", opts->isoc_maxpacket); mutex_unlock(&opts->lock); return result; @@ -1051,7 +1042,7 @@ static ssize_t f_ss_opts_isoc_mult_show(struct f_ss_opts *opts, char *page) int result; mutex_lock(&opts->lock); - result = sprintf(page, "%u", opts->isoc_mult); + result = sprintf(page, "%u\n", opts->isoc_mult); mutex_unlock(&opts->lock); return result; @@ -1095,7 +1086,7 @@ static ssize_t f_ss_opts_isoc_maxburst_show(struct f_ss_opts *opts, char *page) int result; mutex_lock(&opts->lock); - result = sprintf(page, "%u", opts->isoc_maxburst); + result = sprintf(page, "%u\n", opts->isoc_maxburst); mutex_unlock(&opts->lock); return result; @@ -1139,7 +1130,7 @@ static ssize_t f_ss_opts_bulk_buflen_show(struct f_ss_opts *opts, char *page) int result; mutex_lock(&opts->lock); - result = sprintf(page, "%u", opts->bulk_buflen); + result = sprintf(page, "%u\n", opts->bulk_buflen); mutex_unlock(&opts->lock); return result; diff --git a/drivers/usb/gadget/function/f_subset.c b/drivers/usb/gadget/function/f_subset.c index e3dfa67..2e66e62 100644 --- a/drivers/usb/gadget/function/f_subset.c +++ b/drivers/usb/gadget/function/f_subset.c @@ -262,7 +262,7 @@ static int geth_set_alt(struct usb_function *f, unsigned intf, unsigned alt) /* we know alt == 0, so this is an activation or a reset */ - if (geth->port.in_ep->driver_data) { + if (geth->port.in_ep->enabled) { DBG(cdev, "reset cdc subset\n"); gether_disconnect(&geth->port); } @@ -343,13 +343,11 @@ geth_bind(struct usb_configuration *c, struct usb_function *f) if (!ep) goto fail; geth->port.in_ep = ep; - ep->driver_data = cdev; /* claim */ ep = usb_ep_autoconfig(cdev->gadget, &fs_subset_out_desc); if (!ep) goto fail; geth->port.out_ep = ep; - ep->driver_data = cdev; /* claim */ /* support all relevant hardware speeds... we expect that when * hardware is dual speed, all bulk-capable endpoints work at @@ -380,12 +378,6 @@ geth_bind(struct usb_configuration *c, struct usb_function *f) return 0; fail: - /* we might as well release our claims on endpoints */ - if (geth->port.out_ep) - geth->port.out_ep->driver_data = NULL; - if (geth->port.in_ep) - geth->port.in_ep->driver_data = NULL; - ERROR(cdev, "%s: can't bind, err %d\n", f->name, status); return status; diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 7856b33..8ee7019 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -593,7 +593,6 @@ static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt) return err; usb_ep_enable(out_ep); - out_ep->driver_data = audio; audio->copy_buf = f_audio_buffer_alloc(audio_buf_size); if (IS_ERR(audio->copy_buf)) return -ENOMEM; @@ -718,7 +717,6 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f) goto fail; audio->out_ep = ep; audio->out_ep->desc = &as_out_ep_desc; - ep->driver_data = cdev; /* claim */ status = -ENOMEM; @@ -730,8 +728,6 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f) fail: gaudio_cleanup(&audio->card); - if (ep) - ep->driver_data = NULL; return status; } diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index f8de7ea..63336e2 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -1081,14 +1081,12 @@ afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); goto err; } - agdev->out_ep->driver_data = agdev; agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc); if (!agdev->in_ep) { dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); goto err; } - agdev->in_ep->driver_data = agdev; uac2->p_prm.uac2 = uac2; uac2->c_prm.uac2 = uac2; @@ -1132,10 +1130,6 @@ err_free_descs: err: kfree(agdev->uac2.p_prm.rbuf); kfree(agdev->uac2.c_prm.rbuf); - if (agdev->in_ep) - agdev->in_ep->driver_data = NULL; - if (agdev->out_ep) - agdev->out_ep->driver_data = NULL; return -EINVAL; } @@ -1583,11 +1577,6 @@ static void afunc_unbind(struct usb_configuration *c, struct usb_function *f) prm = &agdev->uac2.c_prm; kfree(prm->rbuf); usb_free_all_descriptors(f); - - if (agdev->in_ep) - agdev->in_ep->driver_data = NULL; - if (agdev->out_ep) - agdev->out_ep->driver_data = NULL; } static struct usb_function *afunc_alloc(struct usb_function_instance *fi) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index 743be34..29b41b5 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -280,7 +280,7 @@ uvc_function_get_alt(struct usb_function *f, unsigned interface) else if (interface != uvc->streaming_intf) return -EINVAL; else - return uvc->video.ep->driver_data ? 1 : 0; + return uvc->video.ep->enabled ? 1 : 0; } static int @@ -298,18 +298,14 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) if (alt) return -EINVAL; - if (uvc->control_ep->driver_data) { - INFO(cdev, "reset UVC Control\n"); - usb_ep_disable(uvc->control_ep); - uvc->control_ep->driver_data = NULL; - } + INFO(cdev, "reset UVC Control\n"); + usb_ep_disable(uvc->control_ep); if (!uvc->control_ep->desc) if (config_ep_by_speed(cdev->gadget, f, uvc->control_ep)) return -EINVAL; usb_ep_enable(uvc->control_ep); - uvc->control_ep->driver_data = uvc; if (uvc->state == UVC_STATE_DISCONNECTED) { memset(&v4l2_event, 0, sizeof(v4l2_event)); @@ -336,10 +332,8 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) if (uvc->state != UVC_STATE_STREAMING) return 0; - if (uvc->video.ep) { + if (uvc->video.ep) usb_ep_disable(uvc->video.ep); - uvc->video.ep->driver_data = NULL; - } memset(&v4l2_event, 0, sizeof(v4l2_event)); v4l2_event.type = UVC_EVENT_STREAMOFF; @@ -355,18 +349,14 @@ uvc_function_set_alt(struct usb_function *f, unsigned interface, unsigned alt) if (!uvc->video.ep) return -EINVAL; - if (uvc->video.ep->driver_data) { - INFO(cdev, "reset UVC\n"); - usb_ep_disable(uvc->video.ep); - uvc->video.ep->driver_data = NULL; - } + INFO(cdev, "reset UVC\n"); + usb_ep_disable(uvc->video.ep); ret = config_ep_by_speed(f->config->cdev->gadget, &(uvc->func), uvc->video.ep); if (ret) return ret; usb_ep_enable(uvc->video.ep); - uvc->video.ep->driver_data = uvc; memset(&v4l2_event, 0, sizeof(v4l2_event)); v4l2_event.type = UVC_EVENT_STREAMON; @@ -392,15 +382,8 @@ uvc_function_disable(struct usb_function *f) uvc->state = UVC_STATE_DISCONNECTED; - if (uvc->video.ep->driver_data) { - usb_ep_disable(uvc->video.ep); - uvc->video.ep->driver_data = NULL; - } - - if (uvc->control_ep->driver_data) { - usb_ep_disable(uvc->control_ep); - uvc->control_ep->driver_data = NULL; - } + usb_ep_disable(uvc->video.ep); + usb_ep_disable(uvc->control_ep); } /* -------------------------------------------------------------------------- @@ -651,7 +634,6 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) goto error; } uvc->control_ep = ep; - ep->driver_data = uvc; if (gadget_is_superspeed(c->cdev->gadget)) ep = usb_ep_autoconfig_ss(cdev->gadget, &uvc_ss_streaming_ep, @@ -666,7 +648,6 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) goto error; } uvc->video.ep = ep; - ep->driver_data = uvc; uvc_fs_streaming_ep.bEndpointAddress = uvc->video.ep->address; uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address; @@ -755,11 +736,6 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) error: v4l2_device_unregister(&uvc->v4l2_dev); - if (uvc->control_ep) - uvc->control_ep->driver_data = NULL; - if (uvc->video.ep) - uvc->video.ep->driver_data = NULL; - if (uvc->control_req) usb_ep_free_request(cdev->gadget->ep0, uvc->control_req); kfree(uvc->control_buf); @@ -886,8 +862,6 @@ static void uvc_unbind(struct usb_configuration *c, struct usb_function *f) video_unregister_device(&uvc->vdev); v4l2_device_unregister(&uvc->v4l2_dev); - uvc->control_ep->driver_data = NULL; - uvc->video.ep->driver_data = NULL; usb_ep_free_request(cdev->gadget->ep0, uvc->control_req); kfree(uvc->control_buf); diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index f1fd777..6554322 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -48,6 +48,11 @@ #define UETH__VERSION "29-May-2008" +/* Experiments show that both Linux and Windows hosts allow up to 16k + * frame sizes. Set the max size to 15k+52 to prevent allocating 32k + * blocks and still have efficient handling. */ +#define GETHER_MAX_ETH_FRAME_LEN 15412 + struct eth_dev { /* lock is held while accessing port_usb */ @@ -146,7 +151,7 @@ static int ueth_change_mtu(struct net_device *net, int new_mtu) spin_lock_irqsave(&dev->lock, flags); if (dev->port_usb) status = -EBUSY; - else if (new_mtu <= ETH_HLEN || new_mtu > ETH_FRAME_LEN) + else if (new_mtu <= ETH_HLEN || new_mtu > GETHER_MAX_ETH_FRAME_LEN) status = -ERANGE; else net->mtu = new_mtu; @@ -294,7 +299,7 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req) while (skb2) { if (status < 0 || ETH_HLEN > skb2->len - || skb2->len > VLAN_ETH_FRAME_LEN) { + || skb2->len > GETHER_MAX_ETH_FRAME_LEN) { dev->net->stats.rx_errors++; dev->net->stats.rx_length_errors++; DBG(dev, "rx length %d\n", skb2->len); @@ -1144,7 +1149,6 @@ void gether_disconnect(struct gether *link) spin_lock(&dev->req_lock); } spin_unlock(&dev->req_lock); - link->in_ep->driver_data = NULL; link->in_ep->desc = NULL; usb_ep_disable(link->out_ep); @@ -1159,7 +1163,6 @@ void gether_disconnect(struct gether *link) spin_lock(&dev->req_lock); } spin_unlock(&dev->req_lock); - link->out_ep->driver_data = NULL; link->out_ep->desc = NULL; /* finish forgetting about this USB link episode */ diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index 7ee0579..9cc6a13 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -876,7 +876,6 @@ static void gs_close(struct tty_struct *tty, struct file *file) else gs_buf_clear(&port->port_write_buf); - tty->driver_data = NULL; port->port.tty = NULL; port->openclose = false; @@ -1224,7 +1223,6 @@ int gserial_connect(struct gserial *gser, u8 port_num) fail_out: usb_ep_disable(gser->in); - gser->in->driver_data = NULL; return status; } EXPORT_SYMBOL_GPL(gserial_connect); @@ -1264,10 +1262,7 @@ void gserial_disconnect(struct gserial *gser) /* disable endpoints, aborting down any active I/O */ usb_ep_disable(gser->out); - gser->out->driver_data = NULL; - usb_ep_disable(gser->in); - gser->in->driver_data = NULL; /* finally, free any unused/unusable I/O buffers */ spin_lock_irqsave(&port->port_lock, flags); diff --git a/drivers/usb/gadget/legacy/dbgp.c b/drivers/usb/gadget/legacy/dbgp.c index 5231a32..99ca3da 100644 --- a/drivers/usb/gadget/legacy/dbgp.c +++ b/drivers/usb/gadget/legacy/dbgp.c @@ -79,10 +79,7 @@ static int dbgp_consume(char *buf, unsigned len) static void __disable_ep(struct usb_ep *ep) { - if (ep && ep->driver_data == dbgp.gadget) { - usb_ep_disable(ep); - ep->driver_data = NULL; - } + usb_ep_disable(ep); } static void dbgp_disable_ep(void) @@ -171,7 +168,6 @@ static int __enable_ep(struct usb_ep *ep, struct usb_endpoint_descriptor *desc) int err; ep->desc = desc; err = usb_ep_enable(ep); - ep->driver_data = dbgp.gadget; return err; } @@ -229,8 +225,6 @@ static void dbgp_unbind(struct usb_gadget *gadget) usb_ep_free_request(gadget->ep0, dbgp.req); dbgp.req = NULL; } - - gadget->ep0->driver_data = NULL; } #ifdef CONFIG_USB_G_DBGP_SERIAL @@ -249,18 +243,15 @@ static int dbgp_configure_endpoints(struct usb_gadget *gadget) goto fail_1; } - dbgp.i_ep->driver_data = gadget; i_desc.wMaxPacketSize = cpu_to_le16(USB_DEBUG_MAX_PACKET_SIZE); dbgp.o_ep = usb_ep_autoconfig(gadget, &o_desc); if (!dbgp.o_ep) { - dbgp.i_ep->driver_data = NULL; stp = 2; - goto fail_2; + goto fail_1; } - dbgp.o_ep->driver_data = gadget; o_desc.wMaxPacketSize = cpu_to_le16(USB_DEBUG_MAX_PACKET_SIZE); @@ -277,8 +268,6 @@ static int dbgp_configure_endpoints(struct usb_gadget *gadget) return 0; -fail_2: - dbgp.i_ep->driver_data = NULL; fail_1: dev_dbg(&dbgp.gadget->dev, "ep config: failure (%d)\n", stp); return -ENODEV; @@ -306,7 +295,6 @@ static int dbgp_bind(struct usb_gadget *gadget, } dbgp.req->length = DBGP_REQ_EP0_LEN; - gadget->ep0->driver_data = gadget; #ifdef CONFIG_USB_G_DBGP_SERIAL dbgp.serial = kzalloc(sizeof(struct gserial), GFP_KERNEL); @@ -356,8 +344,6 @@ static int dbgp_setup(struct usb_gadget *gadget, void *data = NULL; u16 len = 0; - gadget->ep0->driver_data = gadget; - if (request == USB_REQ_GET_DESCRIPTOR) { switch (value>>8) { case USB_DT_DEVICE: diff --git a/drivers/usb/gadget/legacy/tcm_usb_gadget.c b/drivers/usb/gadget/legacy/tcm_usb_gadget.c index c3c4808..778e42a 100644 --- a/drivers/usb/gadget/legacy/tcm_usb_gadget.c +++ b/drivers/usb/gadget/legacy/tcm_usb_gadget.c @@ -2018,14 +2018,6 @@ static struct usb_configuration usbg_config_driver = { .bmAttributes = USB_CONFIG_ATT_SELFPOWER, }; -static void give_back_ep(struct usb_ep **pep) -{ - struct usb_ep *ep = *pep; - if (!ep) - return; - ep->driver_data = NULL; -} - static int usbg_bind(struct usb_configuration *c, struct usb_function *f) { struct f_uas *fu = to_f_uas(f); @@ -2045,29 +2037,24 @@ static int usbg_bind(struct usb_configuration *c, struct usb_function *f) &uasp_bi_ep_comp_desc); if (!ep) goto ep_fail; - - ep->driver_data = fu; fu->ep_in = ep; ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_bo_desc, &uasp_bo_ep_comp_desc); if (!ep) goto ep_fail; - ep->driver_data = fu; fu->ep_out = ep; ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_status_desc, &uasp_status_in_ep_comp_desc); if (!ep) goto ep_fail; - ep->driver_data = fu; fu->ep_status = ep; ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_cmd_desc, &uasp_cmd_comp_desc); if (!ep) goto ep_fail; - ep->driver_data = fu; fu->ep_cmd = ep; /* Assume endpoint addresses are the same for both speeds */ @@ -2091,11 +2078,6 @@ static int usbg_bind(struct usb_configuration *c, struct usb_function *f) return 0; ep_fail: pr_err("Can't claim all required eps\n"); - - give_back_ep(&fu->ep_in); - give_back_ep(&fu->ep_out); - give_back_ep(&fu->ep_status); - give_back_ep(&fu->ep_cmd); return -ENOTSUPP; } diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig index 9a3a6b0..cdbff54 100644 --- a/drivers/usb/gadget/udc/Kconfig +++ b/drivers/usb/gadget/udc/Kconfig @@ -55,7 +55,7 @@ config USB_LPC32XX config USB_ATMEL_USBA tristate "Atmel USBA" - depends on AVR32 || ARCH_AT91 + depends on ((AVR32 && !OF) || ARCH_AT91) help USBA is the integrated high-speed USB Device controller on the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel. diff --git a/drivers/usb/gadget/udc/amd5536udc.c b/drivers/usb/gadget/udc/amd5536udc.c index 175ca93..cd87641 100644 --- a/drivers/usb/gadget/udc/amd5536udc.c +++ b/drivers/usb/gadget/udc/amd5536udc.c @@ -65,18 +65,10 @@ static void udc_tasklet_disconnect(unsigned long); static void empty_req_queue(struct udc_ep *); -static int udc_probe(struct udc *dev); -static void udc_basic_init(struct udc *dev); static void udc_setup_endpoints(struct udc *dev); static void udc_soft_reset(struct udc *dev); static struct udc_request *udc_alloc_bna_dummy(struct udc_ep *ep); static void udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq); -static int udc_free_dma_chain(struct udc *dev, struct udc_request *req); -static int udc_create_dma_chain(struct udc_ep *ep, struct udc_request *req, - unsigned long buf_len, gfp_t gfp_flags); -static int udc_remote_wakeup(struct udc *dev); -static int udc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id); -static void udc_pci_remove(struct pci_dev *pdev); /* description */ static const char mod_desc[] = UDC_MOD_DESCRIPTION; @@ -615,6 +607,30 @@ udc_alloc_request(struct usb_ep *usbep, gfp_t gfp) return &req->req; } +/* frees pci pool descriptors of a DMA chain */ +static int udc_free_dma_chain(struct udc *dev, struct udc_request *req) +{ + int ret_val = 0; + struct udc_data_dma *td; + struct udc_data_dma *td_last = NULL; + unsigned int i; + + DBG(dev, "free chain req = %p\n", req); + + /* do not free first desc., will be done by free for request */ + td_last = req->td_data; + td = phys_to_virt(td_last->next); + + for (i = 1; i < req->chain_len; i++) { + pci_pool_free(dev->data_requests, td, + (dma_addr_t)td_last->next); + td_last = td; + td = phys_to_virt(td_last->next); + } + + return ret_val; +} + /* Frees request packet, called by gadget driver */ static void udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq) @@ -789,6 +805,123 @@ udc_rxfifo_read(struct udc_ep *ep, struct udc_request *req) return finished; } +/* Creates or re-inits a DMA chain */ +static int udc_create_dma_chain( + struct udc_ep *ep, + struct udc_request *req, + unsigned long buf_len, gfp_t gfp_flags +) +{ + unsigned long bytes = req->req.length; + unsigned int i; + dma_addr_t dma_addr; + struct udc_data_dma *td = NULL; + struct udc_data_dma *last = NULL; + unsigned long txbytes; + unsigned create_new_chain = 0; + unsigned len; + + VDBG(ep->dev, "udc_create_dma_chain: bytes=%ld buf_len=%ld\n", + bytes, buf_len); + dma_addr = DMA_DONT_USE; + + /* unset L bit in first desc for OUT */ + if (!ep->in) + req->td_data->status &= AMD_CLEAR_BIT(UDC_DMA_IN_STS_L); + + /* alloc only new desc's if not already available */ + len = req->req.length / ep->ep.maxpacket; + if (req->req.length % ep->ep.maxpacket) + len++; + + if (len > req->chain_len) { + /* shorter chain already allocated before */ + if (req->chain_len > 1) + udc_free_dma_chain(ep->dev, req); + req->chain_len = len; + create_new_chain = 1; + } + + td = req->td_data; + /* gen. required number of descriptors and buffers */ + for (i = buf_len; i < bytes; i += buf_len) { + /* create or determine next desc. */ + if (create_new_chain) { + td = pci_pool_alloc(ep->dev->data_requests, + gfp_flags, &dma_addr); + if (!td) + return -ENOMEM; + + td->status = 0; + } else if (i == buf_len) { + /* first td */ + td = (struct udc_data_dma *)phys_to_virt( + req->td_data->next); + td->status = 0; + } else { + td = (struct udc_data_dma *)phys_to_virt(last->next); + td->status = 0; + } + + if (td) + td->bufptr = req->req.dma + i; /* assign buffer */ + else + break; + + /* short packet ? */ + if ((bytes - i) >= buf_len) { + txbytes = buf_len; + } else { + /* short packet */ + txbytes = bytes - i; + } + + /* link td and assign tx bytes */ + if (i == buf_len) { + if (create_new_chain) + req->td_data->next = dma_addr; + /* + * else + * req->td_data->next = virt_to_phys(td); + */ + /* write tx bytes */ + if (ep->in) { + /* first desc */ + req->td_data->status = + AMD_ADDBITS(req->td_data->status, + ep->ep.maxpacket, + UDC_DMA_IN_STS_TXBYTES); + /* second desc */ + td->status = AMD_ADDBITS(td->status, + txbytes, + UDC_DMA_IN_STS_TXBYTES); + } + } else { + if (create_new_chain) + last->next = dma_addr; + /* + * else + * last->next = virt_to_phys(td); + */ + if (ep->in) { + /* write tx bytes */ + td->status = AMD_ADDBITS(td->status, + txbytes, + UDC_DMA_IN_STS_TXBYTES); + } + } + last = td; + } + /* set last bit */ + if (td) { + td->status |= AMD_BIT(UDC_DMA_IN_STS_L); + /* last desc. points to itself */ + req->td_data_last = td; + } + + return 0; +} + /* create/re-init a DMA descriptor or a DMA descriptor chain */ static int prep_dma(struct udc_ep *ep, struct udc_request *req, gfp_t gfp) { @@ -913,32 +1046,6 @@ __acquires(ep->dev->lock) ep->halted = halted; } -/* frees pci pool descriptors of a DMA chain */ -static int udc_free_dma_chain(struct udc *dev, struct udc_request *req) -{ - - int ret_val = 0; - struct udc_data_dma *td; - struct udc_data_dma *td_last = NULL; - unsigned int i; - - DBG(dev, "free chain req = %p\n", req); - - /* do not free first desc., will be done by free for request */ - td_last = req->td_data; - td = phys_to_virt(td_last->next); - - for (i = 1; i < req->chain_len; i++) { - - pci_pool_free(dev->data_requests, td, - (dma_addr_t) td_last->next); - td_last = td; - td = phys_to_virt(td_last->next); - } - - return ret_val; -} - /* Iterates to the end of a DMA chain and returns last descriptor */ static struct udc_data_dma *udc_get_last_dma_desc(struct udc_request *req) { @@ -975,125 +1082,6 @@ static u32 udc_get_ppbdu_rxbytes(struct udc_request *req) } -/* Creates or re-inits a DMA chain */ -static int udc_create_dma_chain( - struct udc_ep *ep, - struct udc_request *req, - unsigned long buf_len, gfp_t gfp_flags -) -{ - unsigned long bytes = req->req.length; - unsigned int i; - dma_addr_t dma_addr; - struct udc_data_dma *td = NULL; - struct udc_data_dma *last = NULL; - unsigned long txbytes; - unsigned create_new_chain = 0; - unsigned len; - - VDBG(ep->dev, "udc_create_dma_chain: bytes=%ld buf_len=%ld\n", - bytes, buf_len); - dma_addr = DMA_DONT_USE; - - /* unset L bit in first desc for OUT */ - if (!ep->in) - req->td_data->status &= AMD_CLEAR_BIT(UDC_DMA_IN_STS_L); - - /* alloc only new desc's if not already available */ - len = req->req.length / ep->ep.maxpacket; - if (req->req.length % ep->ep.maxpacket) - len++; - - if (len > req->chain_len) { - /* shorter chain already allocated before */ - if (req->chain_len > 1) - udc_free_dma_chain(ep->dev, req); - req->chain_len = len; - create_new_chain = 1; - } - - td = req->td_data; - /* gen. required number of descriptors and buffers */ - for (i = buf_len; i < bytes; i += buf_len) { - /* create or determine next desc. */ - if (create_new_chain) { - - td = pci_pool_alloc(ep->dev->data_requests, - gfp_flags, &dma_addr); - if (!td) - return -ENOMEM; - - td->status = 0; - } else if (i == buf_len) { - /* first td */ - td = (struct udc_data_dma *) phys_to_virt( - req->td_data->next); - td->status = 0; - } else { - td = (struct udc_data_dma *) phys_to_virt(last->next); - td->status = 0; - } - - - if (td) - td->bufptr = req->req.dma + i; /* assign buffer */ - else - break; - - /* short packet ? */ - if ((bytes - i) >= buf_len) { - txbytes = buf_len; - } else { - /* short packet */ - txbytes = bytes - i; - } - - /* link td and assign tx bytes */ - if (i == buf_len) { - if (create_new_chain) - req->td_data->next = dma_addr; - /* - else - req->td_data->next = virt_to_phys(td); - */ - /* write tx bytes */ - if (ep->in) { - /* first desc */ - req->td_data->status = - AMD_ADDBITS(req->td_data->status, - ep->ep.maxpacket, - UDC_DMA_IN_STS_TXBYTES); - /* second desc */ - td->status = AMD_ADDBITS(td->status, - txbytes, - UDC_DMA_IN_STS_TXBYTES); - } - } else { - if (create_new_chain) - last->next = dma_addr; - /* - else - last->next = virt_to_phys(td); - */ - if (ep->in) { - /* write tx bytes */ - td->status = AMD_ADDBITS(td->status, - txbytes, - UDC_DMA_IN_STS_TXBYTES); - } - } - last = td; - } - /* set last bit */ - if (td) { - td->status |= AMD_BIT(UDC_DMA_IN_STS_L); - /* last desc. points to itself */ - req->td_data_last = td; - } - - return 0; -} - /* Enabling RX DMA */ static void udc_set_rde(struct udc *dev) { @@ -1453,6 +1441,26 @@ static int udc_get_frame(struct usb_gadget *gadget) return -EOPNOTSUPP; } +/* Initiates a remote wakeup */ +static int udc_remote_wakeup(struct udc *dev) +{ + unsigned long flags; + u32 tmp; + + DBG(dev, "UDC initiates remote wakeup\n"); + + spin_lock_irqsave(&dev->lock, flags); + + tmp = readl(&dev->regs->ctl); + tmp |= AMD_BIT(UDC_DEVCTL_RES); + writel(tmp, &dev->regs->ctl); + tmp &= AMD_CLEAR_BIT(UDC_DEVCTL_RES); + writel(tmp, &dev->regs->ctl); + + spin_unlock_irqrestore(&dev->lock, flags); + return 0; +} + /* Remote wakeup gadget interface */ static int udc_wakeup(struct usb_gadget *gadget) { @@ -1498,33 +1506,6 @@ static void make_ep_lists(struct udc *dev) dev->ep[UDC_EPOUT_IX].fifo_depth = UDC_RXFIFO_SIZE; } -/* init registers at driver load time */ -static int startup_registers(struct udc *dev) -{ - u32 tmp; - - /* init controller by soft reset */ - udc_soft_reset(dev); - - /* mask not needed interrupts */ - udc_mask_unused_interrupts(dev); - - /* put into initial config */ - udc_basic_init(dev); - /* link up all endpoints */ - udc_setup_endpoints(dev); - - /* program speed */ - tmp = readl(&dev->regs->cfg); - if (use_fullspeed) - tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_FS, UDC_DEVCFG_SPD); - else - tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_HS, UDC_DEVCFG_SPD); - writel(tmp, &dev->regs->cfg); - - return 0; -} - /* Inits UDC context */ static void udc_basic_init(struct udc *dev) { @@ -1563,6 +1544,33 @@ static void udc_basic_init(struct udc *dev) dev->data_ep_queued = 0; } +/* init registers at driver load time */ +static int startup_registers(struct udc *dev) +{ + u32 tmp; + + /* init controller by soft reset */ + udc_soft_reset(dev); + + /* mask not needed interrupts */ + udc_mask_unused_interrupts(dev); + + /* put into initial config */ + udc_basic_init(dev); + /* link up all endpoints */ + udc_setup_endpoints(dev); + + /* program speed */ + tmp = readl(&dev->regs->cfg); + if (use_fullspeed) + tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_FS, UDC_DEVCFG_SPD); + else + tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_HS, UDC_DEVCFG_SPD); + writel(tmp, &dev->regs->cfg); + + return 0; +} + /* Sets initial endpoint parameters */ static void udc_setup_endpoints(struct udc *dev) { @@ -2177,7 +2185,7 @@ static irqreturn_t udc_data_out_isr(struct udc *dev, int ep_ix) } /* DMA */ - } else if (!ep->cancel_transfer && req != NULL) { + } else if (!ep->cancel_transfer && req) { ret_val = IRQ_HANDLED; /* check for DMA done */ @@ -3107,6 +3115,17 @@ static void udc_remove(struct udc *dev) udc = NULL; } +/* free all the dma pools */ +static void free_dma_pools(struct udc *dev) +{ + dma_pool_free(dev->stp_requests, dev->ep[UDC_EP0OUT_IX].td, + dev->ep[UDC_EP0OUT_IX].td_phys); + dma_pool_free(dev->stp_requests, dev->ep[UDC_EP0OUT_IX].td_stp, + dev->ep[UDC_EP0OUT_IX].td_stp_dma); + dma_pool_destroy(dev->stp_requests); + dma_pool_destroy(dev->data_requests); +} + /* Reset all pci context */ static void udc_pci_remove(struct pci_dev *pdev) { @@ -3116,35 +3135,19 @@ static void udc_pci_remove(struct pci_dev *pdev) usb_del_gadget_udc(&udc->gadget); /* gadget driver must not be registered */ - BUG_ON(dev->driver != NULL); + if (WARN_ON(dev->driver)) + return; /* dma pool cleanup */ - if (dev->data_requests) - pci_pool_destroy(dev->data_requests); - - if (dev->stp_requests) { - /* cleanup DMA desc's for ep0in */ - pci_pool_free(dev->stp_requests, - dev->ep[UDC_EP0OUT_IX].td_stp, - dev->ep[UDC_EP0OUT_IX].td_stp_dma); - pci_pool_free(dev->stp_requests, - dev->ep[UDC_EP0OUT_IX].td, - dev->ep[UDC_EP0OUT_IX].td_phys); - - pci_pool_destroy(dev->stp_requests); - } + free_dma_pools(dev); /* reset controller */ writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg); - if (dev->irq_registered) - free_irq(pdev->irq, dev); - if (dev->virt_addr) - iounmap(dev->virt_addr); - if (dev->mem_region) - release_mem_region(pci_resource_start(pdev, 0), - pci_resource_len(pdev, 0)); - if (dev->active) - pci_disable_device(pdev); + free_irq(pdev->irq, dev); + iounmap(dev->virt_addr); + release_mem_region(pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0)); + pci_disable_device(pdev); udc_remove(dev); } @@ -3169,8 +3172,7 @@ static int init_dma_pools(struct udc *dev) sizeof(struct udc_data_dma), 0, 0); if (!dev->data_requests) { DBG(dev, "can't get request data pool\n"); - retval = -ENOMEM; - goto finished; + return -ENOMEM; } /* EP0 in dma regs = dev control regs */ @@ -3182,27 +3184,101 @@ static int init_dma_pools(struct udc *dev) if (!dev->stp_requests) { DBG(dev, "can't get stp request pool\n"); retval = -ENOMEM; - goto finished; + goto err_create_dma_pool; } /* setup */ td_stp = dma_pool_alloc(dev->stp_requests, GFP_KERNEL, &dev->ep[UDC_EP0OUT_IX].td_stp_dma); - if (td_stp == NULL) { + if (!td_stp) { retval = -ENOMEM; - goto finished; + goto err_alloc_dma; } dev->ep[UDC_EP0OUT_IX].td_stp = td_stp; /* data: 0 packets !? */ td_data = dma_pool_alloc(dev->stp_requests, GFP_KERNEL, &dev->ep[UDC_EP0OUT_IX].td_phys); - if (td_data == NULL) { + if (!td_data) { retval = -ENOMEM; - goto finished; + goto err_alloc_phys; } dev->ep[UDC_EP0OUT_IX].td = td_data; return 0; +err_alloc_phys: + dma_pool_free(dev->stp_requests, dev->ep[UDC_EP0OUT_IX].td_stp, + dev->ep[UDC_EP0OUT_IX].td_stp_dma); +err_alloc_dma: + dma_pool_destroy(dev->stp_requests); + dev->stp_requests = NULL; +err_create_dma_pool: + dma_pool_destroy(dev->data_requests); + dev->data_requests = NULL; + return retval; +} + +/* general probe */ +static int udc_probe(struct udc *dev) +{ + char tmp[128]; + u32 reg; + int retval; + + /* mark timer as not initialized */ + udc_timer.data = 0; + udc_pollstall_timer.data = 0; + + /* device struct setup */ + dev->gadget.ops = &udc_ops; + + dev_set_name(&dev->gadget.dev, "gadget"); + dev->gadget.name = name; + dev->gadget.max_speed = USB_SPEED_HIGH; + + /* init registers, interrupts, ... */ + startup_registers(dev); + + dev_info(&dev->pdev->dev, "%s\n", mod_desc); + + snprintf(tmp, sizeof(tmp), "%d", dev->irq); + dev_info(&dev->pdev->dev, + "irq %s, pci mem %08lx, chip rev %02x(Geode5536 %s)\n", + tmp, dev->phys_addr, dev->chiprev, + (dev->chiprev == UDC_HSA0_REV) ? "A0" : "B1"); + strcpy(tmp, UDC_DRIVER_VERSION_STRING); + if (dev->chiprev == UDC_HSA0_REV) { + dev_err(&dev->pdev->dev, "chip revision is A0; too old\n"); + retval = -ENODEV; + goto finished; + } + dev_info(&dev->pdev->dev, + "driver version: %s(for Geode5536 B1)\n", tmp); + udc = dev; + + retval = usb_add_gadget_udc_release(&udc->pdev->dev, &dev->gadget, + gadget_release); + if (retval) + goto finished; + + /* timer init */ + init_timer(&udc_timer); + udc_timer.function = udc_timer_function; + udc_timer.data = 1; + /* timer pollstall init */ + init_timer(&udc_pollstall_timer); + udc_pollstall_timer.function = udc_pollstall_timer_function; + udc_pollstall_timer.data = 1; + + /* set SD */ + reg = readl(&dev->regs->ctl); + reg |= AMD_BIT(UDC_DEVCTL_SD); + writel(reg, &dev->regs->ctl); + + /* print dev register info */ + print_regs(dev); + + return 0; + finished: return retval; } @@ -3234,7 +3310,6 @@ static int udc_pci_probe( retval = -ENODEV; goto err_pcidev; } - dev->active = 1; /* PCI resource allocation */ resource = pci_resource_start(pdev, 0); @@ -3245,10 +3320,9 @@ static int udc_pci_probe( retval = -EBUSY; goto err_memreg; } - dev->mem_region = 1; dev->virt_addr = ioremap_nocache(resource, len); - if (dev->virt_addr == NULL) { + if (!dev->virt_addr) { dev_dbg(&pdev->dev, "start address cannot be mapped\n"); retval = -EFAULT; goto err_ioremap; @@ -3276,7 +3350,6 @@ static int udc_pci_probe( retval = -EBUSY; goto err_irq; } - dev->irq_registered = 1; pci_set_drvdata(pdev, dev); @@ -3290,7 +3363,7 @@ static int udc_pci_probe( if (use_dma) { retval = init_dma_pools(dev); if (retval != 0) - goto finished; + goto err_dma; } dev->phys_addr = resource; @@ -3298,13 +3371,17 @@ static int udc_pci_probe( dev->pdev = pdev; /* general probing */ - if (udc_probe(dev) == 0) - return 0; - -finished: - udc_pci_remove(pdev); - return retval; + if (udc_probe(dev)) { + retval = -ENODEV; + goto err_probe; + } + return 0; +err_probe: + if (use_dma) + free_dma_pools(dev); +err_dma: + free_irq(pdev->irq, dev); err_irq: iounmap(dev->virt_addr); err_ioremap: @@ -3316,92 +3393,6 @@ err_pcidev: return retval; } -/* general probe */ -static int udc_probe(struct udc *dev) -{ - char tmp[128]; - u32 reg; - int retval; - - /* mark timer as not initialized */ - udc_timer.data = 0; - udc_pollstall_timer.data = 0; - - /* device struct setup */ - dev->gadget.ops = &udc_ops; - - dev_set_name(&dev->gadget.dev, "gadget"); - dev->gadget.name = name; - dev->gadget.max_speed = USB_SPEED_HIGH; - - /* init registers, interrupts, ... */ - startup_registers(dev); - - dev_info(&dev->pdev->dev, "%s\n", mod_desc); - - snprintf(tmp, sizeof tmp, "%d", dev->irq); - dev_info(&dev->pdev->dev, - "irq %s, pci mem %08lx, chip rev %02x(Geode5536 %s)\n", - tmp, dev->phys_addr, dev->chiprev, - (dev->chiprev == UDC_HSA0_REV) ? "A0" : "B1"); - strcpy(tmp, UDC_DRIVER_VERSION_STRING); - if (dev->chiprev == UDC_HSA0_REV) { - dev_err(&dev->pdev->dev, "chip revision is A0; too old\n"); - retval = -ENODEV; - goto finished; - } - dev_info(&dev->pdev->dev, - "driver version: %s(for Geode5536 B1)\n", tmp); - udc = dev; - - retval = usb_add_gadget_udc_release(&udc->pdev->dev, &dev->gadget, - gadget_release); - if (retval) - goto finished; - - /* timer init */ - init_timer(&udc_timer); - udc_timer.function = udc_timer_function; - udc_timer.data = 1; - /* timer pollstall init */ - init_timer(&udc_pollstall_timer); - udc_pollstall_timer.function = udc_pollstall_timer_function; - udc_pollstall_timer.data = 1; - - /* set SD */ - reg = readl(&dev->regs->ctl); - reg |= AMD_BIT(UDC_DEVCTL_SD); - writel(reg, &dev->regs->ctl); - - /* print dev register info */ - print_regs(dev); - - return 0; - -finished: - return retval; -} - -/* Initiates a remote wakeup */ -static int udc_remote_wakeup(struct udc *dev) -{ - unsigned long flags; - u32 tmp; - - DBG(dev, "UDC initiates remote wakeup\n"); - - spin_lock_irqsave(&dev->lock, flags); - - tmp = readl(&dev->regs->ctl); - tmp |= AMD_BIT(UDC_DEVCTL_RES); - writel(tmp, &dev->regs->ctl); - tmp &= AMD_CLEAR_BIT(UDC_DEVCTL_RES); - writel(tmp, &dev->regs->ctl); - - spin_unlock_irqrestore(&dev->lock, flags); - return 0; -} - /* PCI device parameters */ static const struct pci_device_id pci_id[] = { { diff --git a/drivers/usb/gadget/udc/amd5536udc.h b/drivers/usb/gadget/udc/amd5536udc.h index 6744d3b..4638d70 100644 --- a/drivers/usb/gadget/udc/amd5536udc.h +++ b/drivers/usb/gadget/udc/amd5536udc.h @@ -526,14 +526,11 @@ struct udc { struct udc_ep ep[UDC_EP_NUM]; struct usb_gadget_driver *driver; /* operational flags */ - unsigned active : 1, - stall_ep0in : 1, + unsigned stall_ep0in : 1, waiting_zlp_ack_ep0in : 1, set_cfg_not_acked : 1, - irq_registered : 1, data_ep_enabled : 1, data_ep_queued : 1, - mem_region : 1, sys_suspended : 1, connected; diff --git a/drivers/usb/gadget/udc/at91_udc.h b/drivers/usb/gadget/udc/at91_udc.h index 2679c8b..0a433e6 100644 --- a/drivers/usb/gadget/udc/at91_udc.h +++ b/drivers/usb/gadget/udc/at91_udc.h @@ -112,6 +112,14 @@ struct at91_udc_caps { void (*pullup)(struct at91_udc *udc, int is_on); }; +struct at91_udc_data { + int vbus_pin; /* high == host powering us */ + u8 vbus_active_low; /* vbus polarity */ + u8 vbus_polled; /* Use polling, not interrupt */ + int pullup_pin; /* active == D+ pulled up */ + u8 pullup_active_low; /* true == pullup_pin is active low */ +}; + /* * driver is non-SMP, and just blocks IRQs whenever it needs * access protection for chip registers or driver state diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index 27af0f0..dde4445 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -833,10 +833,10 @@ static const struct usb_ep_ops dummy_ep_ops = { /* there are both host and device side versions of this call ... */ static int dummy_g_get_frame(struct usb_gadget *_gadget) { - struct timeval tv; + struct timespec64 ts64; - do_gettimeofday(&tv); - return tv.tv_usec / 1000; + ktime_get_ts64(&ts64); + return ts64.tv_nsec / NSEC_PER_MSEC; } static int dummy_wakeup(struct usb_gadget *_gadget) diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index cf0ed42..6706aef 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -1913,7 +1913,7 @@ static void defect7374_disable_data_eps(struct net2280 *dev) for (i = 1; i < 5; i++) { ep = &dev->ep[i]; - writel(0, &ep->cfg->ep_cfg); + writel(i, &ep->cfg->ep_cfg); } /* CSROUT, CSRIN, PCIOUT, PCIIN, STATIN, RCIN */ diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c index e5f4c52..7a04157 100644 --- a/drivers/usb/gadget/udc/pch_udc.c +++ b/drivers/usb/gadget/udc/pch_udc.c @@ -330,7 +330,7 @@ struct pch_vbus_gpio_data { * @prot_stall: protcol stall requested * @irq_registered: irq registered with system * @mem_region: device memory mapped - * @registered: driver regsitered with system + * @registered: driver registered with system * @suspended: driver in suspended state * @connected: gadget driver associated * @vbus_session: required vbus_session state @@ -2747,18 +2747,18 @@ static void pch_udc_dev_isr(struct pch_udc_dev *dev, u32 dev_intr) if (dev_intr & UDC_DEVINT_US) { if (dev->driver && dev->driver->suspend) { - spin_lock(&dev->lock); - dev->driver->suspend(&dev->gadget); spin_unlock(&dev->lock); + dev->driver->suspend(&dev->gadget); + spin_lock(&dev->lock); } vbus = pch_vbus_gpio_get_value(dev); if ((dev->vbus_session == 0) && (vbus != 1)) { if (dev->driver && dev->driver->disconnect) { - spin_lock(&dev->lock); - dev->driver->disconnect(&dev->gadget); spin_unlock(&dev->lock); + dev->driver->disconnect(&dev->gadget); + spin_lock(&dev->lock); } pch_udc_reconnect(dev); } else if ((dev->vbus_session == 0) diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 079991e..3bb0887 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -348,16 +348,6 @@ config USB_ISP1362_HCD To compile this driver as a module, choose M here: the module will be called isp1362-hcd. -config USB_FUSBH200_HCD - tristate "FUSBH200 HCD support" - depends on USB - ---help--- - Faraday FUSBH200 is designed to meet USB2.0 EHCI specification - with minor modification. - - To compile this driver as a module, choose M here: the - module will be called fusbh200-hcd. - config USB_FOTG210_HCD tristate "FOTG210 HCD support" depends on USB diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 754efaa..e7558ab 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -28,9 +28,6 @@ ifneq ($(CONFIG_USB), ) obj-$(CONFIG_PCI) += pci-quirks.o endif -obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o -obj-$(CONFIG_USB_XHCI_PLATFORM) += xhci-plat-hcd.o - obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o obj-$(CONFIG_USB_EHCI_PCI) += ehci-pci.o obj-$(CONFIG_USB_EHCI_HCD_PLATFORM) += ehci-platform.o @@ -65,6 +62,8 @@ obj-$(CONFIG_USB_OHCI_HCD_PXA27X) += ohci-pxa27x.o obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o obj-$(CONFIG_USB_FHCI_HCD) += fhci.o obj-$(CONFIG_USB_XHCI_HCD) += xhci-hcd.o +obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o +obj-$(CONFIG_USB_XHCI_PLATFORM) += xhci-plat-hcd.o obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o @@ -75,6 +74,5 @@ obj-$(CONFIG_USB_FSL_MPH_DR_OF) += fsl-mph-dr-of.o obj-$(CONFIG_USB_EHCI_FSL) += ehci-fsl.o obj-$(CONFIG_USB_HCD_BCMA) += bcma-hcd.o obj-$(CONFIG_USB_HCD_SSB) += ssb-hcd.o -obj-$(CONFIG_USB_FUSBH200_HCD) += fusbh200-hcd.o obj-$(CONFIG_USB_FOTG210_HCD) += fotg210-hcd.o obj-$(CONFIG_USB_MAX3421_HCD) += max3421-hcd.o diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c index 275c92e..c4f84c8 100644 --- a/drivers/usb/host/ehci-msm.c +++ b/drivers/usb/host/ehci-msm.c @@ -80,12 +80,12 @@ static int ehci_msm_probe(struct platform_device *pdev) return -ENOMEM; } - hcd->irq = platform_get_irq(pdev, 0); - if (hcd->irq < 0) { + ret = platform_get_irq(pdev, 0); + if (ret < 0) { dev_err(&pdev->dev, "Unable to get IRQ resource\n"); - ret = hcd->irq; goto put_hcd; } + hcd->irq = ret; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index bfcbb9a..ee8d5fa 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -224,7 +224,8 @@ static int ehci_orion_drv_probe(struct platform_device *pdev) priv->phy = devm_phy_optional_get(&pdev->dev, "usb"); if (IS_ERR(priv->phy)) { err = PTR_ERR(priv->phy); - goto err_phy_get; + if (err != -ENOSYS) + goto err_phy_get; } else { err = phy_init(priv->phy); if (err) diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index 5c3c085..bd7082f2 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -19,6 +19,7 @@ * * Licensed under the GNU/GPL. See COPYING for details. */ +#include <linux/acpi.h> #include <linux/clk.h> #include <linux/dma-mapping.h> #include <linux/err.h> @@ -162,8 +163,10 @@ static int ehci_platform_probe(struct platform_device *dev) err = dma_coerce_mask_and_coherent(&dev->dev, pdata->dma_mask_64 ? DMA_BIT_MASK(64) : DMA_BIT_MASK(32)); - if (err) + if (err) { + dev_err(&dev->dev, "Error: DMA mask configuration failed\n"); return err; + } irq = platform_get_irq(dev, 0); if (irq < 0) { @@ -385,6 +388,12 @@ static const struct of_device_id vt8500_ehci_ids[] = { }; MODULE_DEVICE_TABLE(of, vt8500_ehci_ids); +static const struct acpi_device_id ehci_acpi_match[] = { + { "PNP0D20", 0 }, /* EHCI controller without debug */ + { } +}; +MODULE_DEVICE_TABLE(acpi, ehci_acpi_match); + static const struct platform_device_id ehci_platform_table[] = { { "ehci-platform", 0 }, { } @@ -403,6 +412,7 @@ static struct platform_driver ehci_platform_driver = { .name = "ehci-platform", .pm = &ehci_platform_pm_ops, .of_match_table = vt8500_ehci_ids, + .acpi_match_table = ACPI_PTR(ehci_acpi_match), } }; diff --git a/drivers/usb/host/ehci-spear.c b/drivers/usb/host/ehci-spear.c index 34e1474..3c4e525 100644 --- a/drivers/usb/host/ehci-spear.c +++ b/drivers/usb/host/ehci-spear.c @@ -149,6 +149,7 @@ static const struct of_device_id spear_ehci_id_table[] = { { .compatible = "st,spear600-ehci", }, { }, }; +MODULE_DEVICE_TABLE(of, spear_ehci_id_table); static struct platform_driver spear_ehci_hcd_driver = { .probe = spear_ehci_hcd_drv_probe, diff --git a/drivers/usb/host/fotg210-hcd.c b/drivers/usb/host/fotg210-hcd.c index 000ed80..787f4e3 100644 --- a/drivers/usb/host/fotg210-hcd.c +++ b/drivers/usb/host/fotg210-hcd.c @@ -1,5 +1,4 @@ -/* - * Faraday FOTG210 EHCI-like driver +/* Faraday FOTG210 EHCI-like driver * * Copyright (c) 2013 Faraday Technology Corporation * @@ -50,32 +49,29 @@ #include <asm/irq.h> #include <asm/unaligned.h> -/*-------------------------------------------------------------------------*/ #define DRIVER_AUTHOR "Yuan-Hsin Chen" #define DRIVER_DESC "FOTG210 Host Controller (EHCI) Driver" - -static const char hcd_name[] = "fotg210_hcd"; +static const char hcd_name[] = "fotg210_hcd"; #undef FOTG210_URB_TRACE - #define FOTG210_STATS /* magic numbers that can affect system performance */ -#define FOTG210_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ -#define FOTG210_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ -#define FOTG210_TUNE_RL_TT 0 -#define FOTG210_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ -#define FOTG210_TUNE_MULT_TT 1 -/* - * Some drivers think it's safe to schedule isochronous transfers more than - * 256 ms into the future (partly as a result of an old bug in the scheduling +#define FOTG210_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ +#define FOTG210_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ +#define FOTG210_TUNE_RL_TT 0 +#define FOTG210_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ +#define FOTG210_TUNE_MULT_TT 1 + +/* Some drivers think it's safe to schedule isochronous transfers more than 256 + * ms into the future (partly as a result of an old bug in the scheduling * code). In an attempt to avoid trouble, we will use a minimum scheduling * length of 512 frames instead of 256. */ -#define FOTG210_TUNE_FLS 1 /* (medium) 512-frame schedule */ +#define FOTG210_TUNE_FLS 1 /* (medium) 512-frame schedule */ /* Initial IRQ latency: faster than hw default */ -static int log2_irq_thresh; /* 0 to 6 */ +static int log2_irq_thresh; /* 0 to 6 */ module_param(log2_irq_thresh, int, S_IRUGO); MODULE_PARM_DESC(log2_irq_thresh, "log2 IRQ latency, 1-64 microframes"); @@ -89,66 +85,57 @@ static unsigned int hird; module_param(hird, int, S_IRUGO); MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us"); -#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT) +#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT) #include "fotg210.h" -/*-------------------------------------------------------------------------*/ - #define fotg210_dbg(fotg210, fmt, args...) \ - dev_dbg(fotg210_to_hcd(fotg210)->self.controller , fmt , ## args) + dev_dbg(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args) #define fotg210_err(fotg210, fmt, args...) \ - dev_err(fotg210_to_hcd(fotg210)->self.controller , fmt , ## args) + dev_err(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args) #define fotg210_info(fotg210, fmt, args...) \ - dev_info(fotg210_to_hcd(fotg210)->self.controller , fmt , ## args) + dev_info(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args) #define fotg210_warn(fotg210, fmt, args...) \ - dev_warn(fotg210_to_hcd(fotg210)->self.controller , fmt , ## args) + dev_warn(fotg210_to_hcd(fotg210)->self.controller, fmt, ## args) -/* check the values in the HCSPARAMS register - * (host controller _Structural_ parameters) - * see EHCI spec, Table 2-4 for each value +/* check the values in the HCSPARAMS register (host controller _Structural_ + * parameters) see EHCI spec, Table 2-4 for each value */ static void dbg_hcs_params(struct fotg210_hcd *fotg210, char *label) { - u32 params = fotg210_readl(fotg210, &fotg210->caps->hcs_params); + u32 params = fotg210_readl(fotg210, &fotg210->caps->hcs_params); - fotg210_dbg(fotg210, - "%s hcs_params 0x%x ports=%d\n", - label, params, - HCS_N_PORTS(params) - ); + fotg210_dbg(fotg210, "%s hcs_params 0x%x ports=%d\n", label, params, + HCS_N_PORTS(params)); } -/* check the values in the HCCPARAMS register - * (host controller _Capability_ parameters) - * see EHCI Spec, Table 2-5 for each value - * */ +/* check the values in the HCCPARAMS register (host controller _Capability_ + * parameters) see EHCI Spec, Table 2-5 for each value + */ static void dbg_hcc_params(struct fotg210_hcd *fotg210, char *label) { - u32 params = fotg210_readl(fotg210, &fotg210->caps->hcc_params); + u32 params = fotg210_readl(fotg210, &fotg210->caps->hcc_params); - fotg210_dbg(fotg210, - "%s hcc_params %04x uframes %s%s\n", - label, - params, - HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024", - HCC_CANPARK(params) ? " park" : ""); + fotg210_dbg(fotg210, "%s hcc_params %04x uframes %s%s\n", label, + params, + HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024", + HCC_CANPARK(params) ? " park" : ""); } static void __maybe_unused dbg_qtd(const char *label, struct fotg210_hcd *fotg210, struct fotg210_qtd *qtd) { fotg210_dbg(fotg210, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd, - hc32_to_cpup(fotg210, &qtd->hw_next), - hc32_to_cpup(fotg210, &qtd->hw_alt_next), - hc32_to_cpup(fotg210, &qtd->hw_token), - hc32_to_cpup(fotg210, &qtd->hw_buf[0])); + hc32_to_cpup(fotg210, &qtd->hw_next), + hc32_to_cpup(fotg210, &qtd->hw_alt_next), + hc32_to_cpup(fotg210, &qtd->hw_token), + hc32_to_cpup(fotg210, &qtd->hw_buf[0])); if (qtd->hw_buf[1]) fotg210_dbg(fotg210, " p1=%08x p2=%08x p3=%08x p4=%08x\n", - hc32_to_cpup(fotg210, &qtd->hw_buf[1]), - hc32_to_cpup(fotg210, &qtd->hw_buf[2]), - hc32_to_cpup(fotg210, &qtd->hw_buf[3]), - hc32_to_cpup(fotg210, &qtd->hw_buf[4])); + hc32_to_cpup(fotg210, &qtd->hw_buf[1]), + hc32_to_cpup(fotg210, &qtd->hw_buf[2]), + hc32_to_cpup(fotg210, &qtd->hw_buf[3]), + hc32_to_cpup(fotg210, &qtd->hw_buf[4])); } static void __maybe_unused @@ -156,101 +143,100 @@ dbg_qh(const char *label, struct fotg210_hcd *fotg210, struct fotg210_qh *qh) { struct fotg210_qh_hw *hw = qh->hw; - fotg210_dbg(fotg210, "%s qh %p n%08x info %x %x qtd %x\n", label, - qh, hw->hw_next, hw->hw_info1, hw->hw_info2, hw->hw_current); + fotg210_dbg(fotg210, "%s qh %p n%08x info %x %x qtd %x\n", label, qh, + hw->hw_next, hw->hw_info1, hw->hw_info2, + hw->hw_current); + dbg_qtd("overlay", fotg210, (struct fotg210_qtd *) &hw->hw_qtd_next); } static void __maybe_unused dbg_itd(const char *label, struct fotg210_hcd *fotg210, struct fotg210_itd *itd) { - fotg210_dbg(fotg210, "%s[%d] itd %p, next %08x, urb %p\n", - label, itd->frame, itd, hc32_to_cpu(fotg210, itd->hw_next), - itd->urb); + fotg210_dbg(fotg210, "%s[%d] itd %p, next %08x, urb %p\n", label, + itd->frame, itd, hc32_to_cpu(fotg210, itd->hw_next), + itd->urb); + fotg210_dbg(fotg210, - " trans: %08x %08x %08x %08x %08x %08x %08x %08x\n", - hc32_to_cpu(fotg210, itd->hw_transaction[0]), - hc32_to_cpu(fotg210, itd->hw_transaction[1]), - hc32_to_cpu(fotg210, itd->hw_transaction[2]), - hc32_to_cpu(fotg210, itd->hw_transaction[3]), - hc32_to_cpu(fotg210, itd->hw_transaction[4]), - hc32_to_cpu(fotg210, itd->hw_transaction[5]), - hc32_to_cpu(fotg210, itd->hw_transaction[6]), - hc32_to_cpu(fotg210, itd->hw_transaction[7])); + " trans: %08x %08x %08x %08x %08x %08x %08x %08x\n", + hc32_to_cpu(fotg210, itd->hw_transaction[0]), + hc32_to_cpu(fotg210, itd->hw_transaction[1]), + hc32_to_cpu(fotg210, itd->hw_transaction[2]), + hc32_to_cpu(fotg210, itd->hw_transaction[3]), + hc32_to_cpu(fotg210, itd->hw_transaction[4]), + hc32_to_cpu(fotg210, itd->hw_transaction[5]), + hc32_to_cpu(fotg210, itd->hw_transaction[6]), + hc32_to_cpu(fotg210, itd->hw_transaction[7])); + fotg210_dbg(fotg210, - " buf: %08x %08x %08x %08x %08x %08x %08x\n", - hc32_to_cpu(fotg210, itd->hw_bufp[0]), - hc32_to_cpu(fotg210, itd->hw_bufp[1]), - hc32_to_cpu(fotg210, itd->hw_bufp[2]), - hc32_to_cpu(fotg210, itd->hw_bufp[3]), - hc32_to_cpu(fotg210, itd->hw_bufp[4]), - hc32_to_cpu(fotg210, itd->hw_bufp[5]), - hc32_to_cpu(fotg210, itd->hw_bufp[6])); + " buf: %08x %08x %08x %08x %08x %08x %08x\n", + hc32_to_cpu(fotg210, itd->hw_bufp[0]), + hc32_to_cpu(fotg210, itd->hw_bufp[1]), + hc32_to_cpu(fotg210, itd->hw_bufp[2]), + hc32_to_cpu(fotg210, itd->hw_bufp[3]), + hc32_to_cpu(fotg210, itd->hw_bufp[4]), + hc32_to_cpu(fotg210, itd->hw_bufp[5]), + hc32_to_cpu(fotg210, itd->hw_bufp[6])); + fotg210_dbg(fotg210, " index: %d %d %d %d %d %d %d %d\n", - itd->index[0], itd->index[1], itd->index[2], - itd->index[3], itd->index[4], itd->index[5], - itd->index[6], itd->index[7]); + itd->index[0], itd->index[1], itd->index[2], + itd->index[3], itd->index[4], itd->index[5], + itd->index[6], itd->index[7]); } static int __maybe_unused dbg_status_buf(char *buf, unsigned len, const char *label, u32 status) { - return scnprintf(buf, len, - "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s", - label, label[0] ? " " : "", status, - (status & STS_ASS) ? " Async" : "", - (status & STS_PSS) ? " Periodic" : "", - (status & STS_RECL) ? " Recl" : "", - (status & STS_HALT) ? " Halt" : "", - (status & STS_IAA) ? " IAA" : "", - (status & STS_FATAL) ? " FATAL" : "", - (status & STS_FLR) ? " FLR" : "", - (status & STS_PCD) ? " PCD" : "", - (status & STS_ERR) ? " ERR" : "", - (status & STS_INT) ? " INT" : "" - ); + return scnprintf(buf, len, "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s", + label, label[0] ? " " : "", status, + (status & STS_ASS) ? " Async" : "", + (status & STS_PSS) ? " Periodic" : "", + (status & STS_RECL) ? " Recl" : "", + (status & STS_HALT) ? " Halt" : "", + (status & STS_IAA) ? " IAA" : "", + (status & STS_FATAL) ? " FATAL" : "", + (status & STS_FLR) ? " FLR" : "", + (status & STS_PCD) ? " PCD" : "", + (status & STS_ERR) ? " ERR" : "", + (status & STS_INT) ? " INT" : ""); } static int __maybe_unused dbg_intr_buf(char *buf, unsigned len, const char *label, u32 enable) { - return scnprintf(buf, len, - "%s%sintrenable %02x%s%s%s%s%s%s", - label, label[0] ? " " : "", enable, - (enable & STS_IAA) ? " IAA" : "", - (enable & STS_FATAL) ? " FATAL" : "", - (enable & STS_FLR) ? " FLR" : "", - (enable & STS_PCD) ? " PCD" : "", - (enable & STS_ERR) ? " ERR" : "", - (enable & STS_INT) ? " INT" : "" - ); + return scnprintf(buf, len, "%s%sintrenable %02x%s%s%s%s%s%s", + label, label[0] ? " " : "", enable, + (enable & STS_IAA) ? " IAA" : "", + (enable & STS_FATAL) ? " FATAL" : "", + (enable & STS_FLR) ? " FLR" : "", + (enable & STS_PCD) ? " PCD" : "", + (enable & STS_ERR) ? " ERR" : "", + (enable & STS_INT) ? " INT" : ""); } static const char *const fls_strings[] = { "1024", "512", "256", "??" }; -static int -dbg_command_buf(char *buf, unsigned len, const char *label, u32 command) +static int dbg_command_buf(char *buf, unsigned len, const char *label, + u32 command) { return scnprintf(buf, len, - "%s%scommand %07x %s=%d ithresh=%d%s%s%s " - "period=%s%s %s", - label, label[0] ? " " : "", command, - (command & CMD_PARK) ? " park" : "(park)", - CMD_PARK_CNT(command), - (command >> 16) & 0x3f, - (command & CMD_IAAD) ? " IAAD" : "", - (command & CMD_ASE) ? " Async" : "", - (command & CMD_PSE) ? " Periodic" : "", - fls_strings[(command >> 2) & 0x3], - (command & CMD_RESET) ? " Reset" : "", - (command & CMD_RUN) ? "RUN" : "HALT" - ); -} - -static char -*dbg_port_buf(char *buf, unsigned len, const char *label, int port, u32 status) -{ - char *sig; + "%s%scommand %07x %s=%d ithresh=%d%s%s%s period=%s%s %s", + label, label[0] ? " " : "", command, + (command & CMD_PARK) ? " park" : "(park)", + CMD_PARK_CNT(command), + (command >> 16) & 0x3f, + (command & CMD_IAAD) ? " IAAD" : "", + (command & CMD_ASE) ? " Async" : "", + (command & CMD_PSE) ? " Periodic" : "", + fls_strings[(command >> 2) & 0x3], + (command & CMD_RESET) ? " Reset" : "", + (command & CMD_RUN) ? "RUN" : "HALT"); +} + +static char *dbg_port_buf(char *buf, unsigned len, const char *label, int port, + u32 status) +{ + char *sig; /* signaling state */ switch (status & (3 << 10)) { @@ -268,44 +254,41 @@ static char break; } - scnprintf(buf, len, - "%s%sport:%d status %06x %d " - "sig=%s%s%s%s%s%s%s%s", - label, label[0] ? " " : "", port, status, - status>>25,/*device address */ - sig, - (status & PORT_RESET) ? " RESET" : "", - (status & PORT_SUSPEND) ? " SUSPEND" : "", - (status & PORT_RESUME) ? " RESUME" : "", - (status & PORT_PEC) ? " PEC" : "", - (status & PORT_PE) ? " PE" : "", - (status & PORT_CSC) ? " CSC" : "", - (status & PORT_CONNECT) ? " CONNECT" : ""); + scnprintf(buf, len, "%s%sport:%d status %06x %d sig=%s%s%s%s%s%s%s%s", + label, label[0] ? " " : "", port, status, + status >> 25, /*device address */ + sig, + (status & PORT_RESET) ? " RESET" : "", + (status & PORT_SUSPEND) ? " SUSPEND" : "", + (status & PORT_RESUME) ? " RESUME" : "", + (status & PORT_PEC) ? " PEC" : "", + (status & PORT_PE) ? " PE" : "", + (status & PORT_CSC) ? " CSC" : "", + (status & PORT_CONNECT) ? " CONNECT" : ""); + return buf; } /* functions have the "wrong" filename when they're output... */ -#define dbg_status(fotg210, label, status) { \ - char _buf[80]; \ - dbg_status_buf(_buf, sizeof(_buf), label, status); \ - fotg210_dbg(fotg210, "%s\n", _buf); \ +#define dbg_status(fotg210, label, status) { \ + char _buf[80]; \ + dbg_status_buf(_buf, sizeof(_buf), label, status); \ + fotg210_dbg(fotg210, "%s\n", _buf); \ } -#define dbg_cmd(fotg210, label, command) { \ - char _buf[80]; \ - dbg_command_buf(_buf, sizeof(_buf), label, command); \ - fotg210_dbg(fotg210, "%s\n", _buf); \ +#define dbg_cmd(fotg210, label, command) { \ + char _buf[80]; \ + dbg_command_buf(_buf, sizeof(_buf), label, command); \ + fotg210_dbg(fotg210, "%s\n", _buf); \ } -#define dbg_port(fotg210, label, port, status) { \ - char _buf[80]; \ - fotg210_dbg(fotg210, "%s\n", dbg_port_buf(_buf, sizeof(_buf), label, port, status) ); \ +#define dbg_port(fotg210, label, port, status) { \ + char _buf[80]; \ + fotg210_dbg(fotg210, "%s\n", \ + dbg_port_buf(_buf, sizeof(_buf), label, port, status));\ } -/*-------------------------------------------------------------------------*/ - /* troubleshooting help: expose state in debugfs */ - static int debug_async_open(struct inode *, struct file *); static int debug_periodic_open(struct inode *, struct file *); static int debug_registers_open(struct inode *, struct file *); @@ -347,17 +330,22 @@ struct debug_buffer { size_t alloc_size; }; -#define speed_char(info1)({ char tmp; \ - switch (info1 & (3 << 12)) { \ - case QH_FULL_SPEED: \ - tmp = 'f'; break; \ - case QH_LOW_SPEED: \ - tmp = 'l'; break; \ - case QH_HIGH_SPEED: \ - tmp = 'h'; break; \ - default: \ - tmp = '?'; break; \ - } tmp; }) +static inline char speed_char(u32 scratch) +{ + switch (scratch & (3 << 12)) { + case QH_FULL_SPEED: + return 'f'; + + case QH_LOW_SPEED: + return 'l'; + + case QH_HIGH_SPEED: + return 'h'; + + default: + return '?'; + } +} static inline char token_mark(struct fotg210_hcd *fotg210, __hc32 token) { @@ -373,33 +361,29 @@ static inline char token_mark(struct fotg210_hcd *fotg210, __hc32 token) return '/'; } -static void qh_lines( - struct fotg210_hcd *fotg210, - struct fotg210_qh *qh, - char **nextp, - unsigned *sizep -) -{ - u32 scratch; - u32 hw_curr; - struct fotg210_qtd *td; - unsigned temp; - unsigned size = *sizep; - char *next = *nextp; - char mark; - __le32 list_end = FOTG210_LIST_END(fotg210); - struct fotg210_qh_hw *hw = qh->hw; - - if (hw->hw_qtd_next == list_end) /* NEC does this */ +static void qh_lines(struct fotg210_hcd *fotg210, struct fotg210_qh *qh, + char **nextp, unsigned *sizep) +{ + u32 scratch; + u32 hw_curr; + struct fotg210_qtd *td; + unsigned temp; + unsigned size = *sizep; + char *next = *nextp; + char mark; + __le32 list_end = FOTG210_LIST_END(fotg210); + struct fotg210_qh_hw *hw = qh->hw; + + if (hw->hw_qtd_next == list_end) /* NEC does this */ mark = '@'; else mark = token_mark(fotg210, hw->hw_token); - if (mark == '/') { /* qh_alt_next controls qh advance? */ - if ((hw->hw_alt_next & QTD_MASK(fotg210)) - == fotg210->async->hw->hw_alt_next) - mark = '#'; /* blocked */ + if (mark == '/') { /* qh_alt_next controls qh advance? */ + if ((hw->hw_alt_next & QTD_MASK(fotg210)) == + fotg210->async->hw->hw_alt_next) + mark = '#'; /* blocked */ else if (hw->hw_alt_next == list_end) - mark = '.'; /* use hw_qtd_next */ + mark = '.'; /* use hw_qtd_next */ /* else alt_next points to some other qtd */ } scratch = hc32_to_cpup(fotg210, &hw->hw_info1); @@ -462,6 +446,7 @@ static void qh_lines( temp = snprintf(next, size, "\n"); if (size < temp) temp = size; + size -= temp; next += temp; @@ -472,12 +457,12 @@ done: static ssize_t fill_async_buffer(struct debug_buffer *buf) { - struct usb_hcd *hcd; - struct fotg210_hcd *fotg210; - unsigned long flags; - unsigned temp, size; - char *next; - struct fotg210_qh *qh; + struct usb_hcd *hcd; + struct fotg210_hcd *fotg210; + unsigned long flags; + unsigned temp, size; + char *next; + struct fotg210_qh *qh; hcd = bus_to_hcd(buf->bus); fotg210 = hcd_to_fotg210(hcd); @@ -492,7 +477,7 @@ static ssize_t fill_async_buffer(struct debug_buffer *buf) */ spin_lock_irqsave(&fotg210->lock, flags); for (qh = fotg210->async->qh_next.qh; size > 0 && qh; - qh = qh->qh_next.qh) + qh = qh->qh_next.qh) qh_lines(fotg210, qh, &next, &size); if (fotg210->async_unlink && size > 0) { temp = scnprintf(next, size, "\nunlink =\n"); @@ -508,21 +493,50 @@ static ssize_t fill_async_buffer(struct debug_buffer *buf) return strlen(buf->output_buf); } +/* count tds, get ep direction */ +static unsigned output_buf_tds_dir(char *buf, struct fotg210_hcd *fotg210, + struct fotg210_qh_hw *hw, struct fotg210_qh *qh, unsigned size) +{ + u32 scratch = hc32_to_cpup(fotg210, &hw->hw_info1); + struct fotg210_qtd *qtd; + char *type = ""; + unsigned temp = 0; + + /* count tds, get ep direction */ + list_for_each_entry(qtd, &qh->qtd_list, qtd_list) { + temp++; + switch ((hc32_to_cpu(fotg210, qtd->hw_token) >> 8) & 0x03) { + case 0: + type = "out"; + continue; + case 1: + type = "in"; + continue; + } + } + + return scnprintf(buf, size, "(%c%d ep%d%s [%d/%d] q%d p%d)", + speed_char(scratch), scratch & 0x007f, + (scratch >> 8) & 0x000f, type, qh->usecs, + qh->c_usecs, temp, (scratch >> 16) & 0x7ff); +} + #define DBG_SCHED_LIMIT 64 static ssize_t fill_periodic_buffer(struct debug_buffer *buf) { - struct usb_hcd *hcd; - struct fotg210_hcd *fotg210; - unsigned long flags; - union fotg210_shadow p, *seen; - unsigned temp, size, seen_count; - char *next; - unsigned i; - __hc32 tag; - - seen = kmalloc(DBG_SCHED_LIMIT * sizeof(*seen), GFP_ATOMIC); + struct usb_hcd *hcd; + struct fotg210_hcd *fotg210; + unsigned long flags; + union fotg210_shadow p, *seen; + unsigned temp, size, seen_count; + char *next; + unsigned i; + __hc32 tag; + + seen = kmalloc_array(DBG_SCHED_LIMIT, sizeof(*seen), GFP_ATOMIC); if (!seen) return 0; + seen_count = 0; hcd = bus_to_hcd(buf->bus); @@ -542,6 +556,7 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf) p = fotg210->pshadow[i]; if (likely(!p.ptr)) continue; + tag = Q_NEXT_TYPE(fotg210, fotg210->periodic[i]); temp = scnprintf(next, size, "%4d: ", i); @@ -569,7 +584,7 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf) continue; if (p.qh->qh_next.ptr) { temp = scnprintf(next, size, - " ..."); + " ..."); size -= temp; next += temp; } @@ -577,38 +592,9 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf) } /* show more info the first time around */ if (temp == seen_count) { - u32 scratch = hc32_to_cpup(fotg210, - &hw->hw_info1); - struct fotg210_qtd *qtd; - char *type = ""; - - /* count tds, get ep direction */ - temp = 0; - list_for_each_entry(qtd, - &p.qh->qtd_list, - qtd_list) { - temp++; - switch (0x03 & (hc32_to_cpu( - fotg210, - qtd->hw_token) >> 8)) { - case 0: - type = "out"; - continue; - case 1: - type = "in"; - continue; - } - } - - temp = scnprintf(next, size, - "(%c%d ep%d%s " - "[%d/%d] q%d p%d)", - speed_char(scratch), - scratch & 0x007f, - (scratch >> 8) & 0x000f, type, - p.qh->usecs, p.qh->c_usecs, - temp, - 0x7ff & (scratch >> 16)); + temp = output_buf_tds_dir(next, + fotg210, hw, + p.qh, size); if (seen_count < DBG_SCHED_LIMIT) seen[seen_count++].qh = p.qh; @@ -619,14 +605,14 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf) break; case Q_TYPE_FSTN: temp = scnprintf(next, size, - " fstn-%8x/%p", p.fstn->hw_prev, - p.fstn); + " fstn-%8x/%p", + p.fstn->hw_prev, p.fstn); tag = Q_NEXT_TYPE(fotg210, p.fstn->hw_next); p = p.fstn->fstn_next; break; case Q_TYPE_ITD: temp = scnprintf(next, size, - " itd/%p", p.itd); + " itd/%p", p.itd); tag = Q_NEXT_TYPE(fotg210, p.itd->hw_next); p = p.itd->itd_next; break; @@ -663,13 +649,13 @@ static const char *rh_state_string(struct fotg210_hcd *fotg210) static ssize_t fill_registers_buffer(struct debug_buffer *buf) { - struct usb_hcd *hcd; - struct fotg210_hcd *fotg210; - unsigned long flags; - unsigned temp, size, i; - char *next, scratch[80]; - static const char fmt[] = "%*s\n"; - static const char label[] = ""; + struct usb_hcd *hcd; + struct fotg210_hcd *fotg210; + unsigned long flags; + unsigned temp, size, i; + char *next, scratch[80]; + static const char fmt[] = "%*s\n"; + static const char label[] = ""; hcd = bus_to_hcd(buf->bus); fotg210 = hcd_to_fotg210(hcd); @@ -680,26 +666,26 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) if (!HCD_HW_ACCESSIBLE(hcd)) { size = scnprintf(next, size, - "bus %s, device %s\n" - "%s\n" - "SUSPENDED(no register access)\n", - hcd->self.controller->bus->name, - dev_name(hcd->self.controller), - hcd->product_desc); + "bus %s, device %s\n" + "%s\n" + "SUSPENDED(no register access)\n", + hcd->self.controller->bus->name, + dev_name(hcd->self.controller), + hcd->product_desc); goto done; } /* Capability Registers */ i = HC_VERSION(fotg210, fotg210_readl(fotg210, - &fotg210->caps->hc_capbase)); + &fotg210->caps->hc_capbase)); temp = scnprintf(next, size, - "bus %s, device %s\n" - "%s\n" - "EHCI %x.%02x, rh state %s\n", - hcd->self.controller->bus->name, - dev_name(hcd->self.controller), - hcd->product_desc, - i >> 8, i & 0x0ff, rh_state_string(fotg210)); + "bus %s, device %s\n" + "%s\n" + "EHCI %x.%02x, rh state %s\n", + hcd->self.controller->bus->name, + dev_name(hcd->self.controller), + hcd->product_desc, + i >> 8, i & 0x0ff, rh_state_string(fotg210)); size -= temp; next += temp; @@ -747,14 +733,14 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf) #ifdef FOTG210_STATS temp = scnprintf(next, size, - "irq normal %ld err %ld iaa %ld(lost %ld)\n", - fotg210->stats.normal, fotg210->stats.error, fotg210->stats.iaa, - fotg210->stats.lost_iaa); + "irq normal %ld err %ld iaa %ld(lost %ld)\n", + fotg210->stats.normal, fotg210->stats.error, + fotg210->stats.iaa, fotg210->stats.lost_iaa); size -= temp; next += temp; temp = scnprintf(next, size, "complete %ld unlink %ld\n", - fotg210->stats.complete, fotg210->stats.unlink); + fotg210->stats.complete, fotg210->stats.unlink); size -= temp; next += temp; #endif @@ -765,8 +751,8 @@ done: return buf->alloc_size - size; } -static struct debug_buffer *alloc_buffer(struct usb_bus *bus, - ssize_t (*fill_func)(struct debug_buffer *)) +static struct debug_buffer +*alloc_buffer(struct usb_bus *bus, ssize_t (*fill_func)(struct debug_buffer *)) { struct debug_buffer *buf; @@ -806,7 +792,7 @@ out: } static ssize_t debug_output(struct file *file, char __user *user_buf, - size_t len, loff_t *offset) + size_t len, loff_t *offset) { struct debug_buffer *buf = file->private_data; int ret = 0; @@ -822,7 +808,7 @@ static ssize_t debug_output(struct file *file, char __user *user_buf, mutex_unlock(&buf->mutex); ret = simple_read_from_buffer(user_buf, len, offset, - buf->output_buf, buf->count); + buf->output_buf, buf->count); out: return ret; @@ -850,6 +836,7 @@ static int debug_async_open(struct inode *inode, struct file *file) static int debug_periodic_open(struct inode *inode, struct file *file) { struct debug_buffer *buf; + buf = alloc_buffer(inode->i_private, fill_periodic_buffer); if (!buf) return -ENOMEM; @@ -862,7 +849,7 @@ static int debug_periodic_open(struct inode *inode, struct file *file) static int debug_registers_open(struct inode *inode, struct file *file) { file->private_data = alloc_buffer(inode->i_private, - fill_registers_buffer); + fill_registers_buffer); return file->private_data ? 0 : -ENOMEM; } @@ -872,20 +859,20 @@ static inline void create_debug_files(struct fotg210_hcd *fotg210) struct usb_bus *bus = &fotg210_to_hcd(fotg210)->self; fotg210->debug_dir = debugfs_create_dir(bus->bus_name, - fotg210_debug_root); + fotg210_debug_root); if (!fotg210->debug_dir) return; if (!debugfs_create_file("async", S_IRUGO, fotg210->debug_dir, bus, - &debug_async_fops)) + &debug_async_fops)) goto file_error; if (!debugfs_create_file("periodic", S_IRUGO, fotg210->debug_dir, bus, - &debug_periodic_fops)) + &debug_periodic_fops)) goto file_error; if (!debugfs_create_file("registers", S_IRUGO, fotg210->debug_dir, bus, - &debug_registers_fops)) + &debug_registers_fops)) goto file_error; return; @@ -899,10 +886,7 @@ static inline void remove_debug_files(struct fotg210_hcd *fotg210) debugfs_remove_recursive(fotg210->debug_dir); } -/*-------------------------------------------------------------------------*/ - -/* - * handshake - spin reading hc until handshake completes or fails +/* handshake - spin reading hc until handshake completes or fails * @ptr: address of hc register to be read * @mask: bits to look at in result of read * @done: value of those bits when handshake succeeds @@ -919,9 +903,9 @@ static inline void remove_debug_files(struct fotg210_hcd *fotg210) * bridge shutdown: shutting down the bridge before the devices using it. */ static int handshake(struct fotg210_hcd *fotg210, void __iomem *ptr, - u32 mask, u32 done, int usec) + u32 mask, u32 done, int usec) { - u32 result; + u32 result; do { result = fotg210_readl(fotg210, ptr); @@ -936,13 +920,12 @@ static int handshake(struct fotg210_hcd *fotg210, void __iomem *ptr, return -ETIMEDOUT; } -/* - * Force HC to halt state from unknown (EHCI spec section 2.3). +/* Force HC to halt state from unknown (EHCI spec section 2.3). * Must be called with interrupts enabled and the lock not held. */ static int fotg210_halt(struct fotg210_hcd *fotg210) { - u32 temp; + u32 temp; spin_lock_irq(&fotg210->lock); @@ -962,20 +945,20 @@ static int fotg210_halt(struct fotg210_hcd *fotg210) synchronize_irq(fotg210_to_hcd(fotg210)->irq); return handshake(fotg210, &fotg210->regs->status, - STS_HALT, STS_HALT, 16 * 125); + STS_HALT, STS_HALT, 16 * 125); } -/* - * Reset a non-running (STS_HALT == 1) controller. +/* Reset a non-running (STS_HALT == 1) controller. * Must be called with interrupts enabled and the lock not held. */ static int fotg210_reset(struct fotg210_hcd *fotg210) { - int retval; - u32 command = fotg210_readl(fotg210, &fotg210->regs->command); + int retval; + u32 command = fotg210_readl(fotg210, &fotg210->regs->command); /* If the EHCI debug controller is active, special care must be - * taken before and after a host controller reset */ + * taken before and after a host controller reset + */ if (fotg210->debug && !dbgp_reset_prep(fotg210_to_hcd(fotg210))) fotg210->debug = NULL; @@ -985,7 +968,7 @@ static int fotg210_reset(struct fotg210_hcd *fotg210) fotg210->rh_state = FOTG210_RH_HALTED; fotg210->next_statechange = jiffies; retval = handshake(fotg210, &fotg210->regs->command, - CMD_RESET, 0, 250 * 1000); + CMD_RESET, 0, 250 * 1000); if (retval) return retval; @@ -998,13 +981,12 @@ static int fotg210_reset(struct fotg210_hcd *fotg210) return retval; } -/* - * Idle the controller (turn off the schedules). +/* Idle the controller (turn off the schedules). * Must be called with interrupts enabled and the lock not held. */ static void fotg210_quiesce(struct fotg210_hcd *fotg210) { - u32 temp; + u32 temp; if (fotg210->rh_state != FOTG210_RH_RUNNING) return; @@ -1012,7 +994,7 @@ static void fotg210_quiesce(struct fotg210_hcd *fotg210) /* wait for any schedule enables/disables to take effect */ temp = (fotg210->command << 10) & (STS_ASS | STS_PSS); handshake(fotg210, &fotg210->regs->status, STS_ASS | STS_PSS, temp, - 16 * 125); + 16 * 125); /* then disable anything that's still active */ spin_lock_irq(&fotg210->lock); @@ -1022,11 +1004,9 @@ static void fotg210_quiesce(struct fotg210_hcd *fotg210) /* hardware can take 16 microframes to turn off ... */ handshake(fotg210, &fotg210->regs->status, STS_ASS | STS_PSS, 0, - 16 * 125); + 16 * 125); } -/*-------------------------------------------------------------------------*/ - static void end_unlink_async(struct fotg210_hcd *fotg210); static void unlink_empty_async(struct fotg210_hcd *fotg210); static void fotg210_work(struct fotg210_hcd *fotg210); @@ -1034,8 +1014,6 @@ static void start_unlink_intr(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); static void end_unlink_intr(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); -/*-------------------------------------------------------------------------*/ - /* Set a bit in the USBCMD register */ static void fotg210_set_command_bit(struct fotg210_hcd *fotg210, u32 bit) { @@ -1056,10 +1034,7 @@ static void fotg210_clear_command_bit(struct fotg210_hcd *fotg210, u32 bit) fotg210_readl(fotg210, &fotg210->regs->command); } -/*-------------------------------------------------------------------------*/ - -/* - * EHCI timer support... Now using hrtimers. +/* EHCI timer support... Now using hrtimers. * * Lots of different events are triggered from fotg210->hrtimer. Whenever * the timer routine runs, it checks each possible event; events that are @@ -1081,8 +1056,7 @@ static void fotg210_clear_command_bit(struct fotg210_hcd *fotg210, u32 bit) * allow for an expiration range of 1 ms. */ -/* - * Delay lengths for the hrtimer event types. +/* Delay lengths for the hrtimer event types. * Keep this list sorted by delay length, in the same order as * the event types indexed by enum fotg210_hrtimer_event in fotg210.h. */ @@ -1103,7 +1077,7 @@ static unsigned event_delays_ns[] = { static void fotg210_enable_event(struct fotg210_hcd *fotg210, unsigned event, bool resched) { - ktime_t *timeout = &fotg210->hr_timeouts[event]; + ktime_t *timeout = &fotg210->hr_timeouts[event]; if (resched) *timeout = ktime_add(ktime_get(), @@ -1122,7 +1096,7 @@ static void fotg210_enable_event(struct fotg210_hcd *fotg210, unsigned event, /* Poll the STS_ASS status bit; see when it agrees with CMD_ASE */ static void fotg210_poll_ASS(struct fotg210_hcd *fotg210) { - unsigned actual, want; + unsigned actual, want; /* Don't enable anything if the controller isn't running (e.g., died) */ if (fotg210->rh_state != FOTG210_RH_RUNNING) @@ -1136,7 +1110,7 @@ static void fotg210_poll_ASS(struct fotg210_hcd *fotg210) /* Poll again later, but give up after about 20 ms */ if (fotg210->ASS_poll_count++ < 20) { fotg210_enable_event(fotg210, FOTG210_HRTIMER_POLL_ASS, - true); + true); return; } fotg210_dbg(fotg210, "Waited too long for the async schedule status (%x/%x), giving up\n", @@ -1154,8 +1128,8 @@ static void fotg210_poll_ASS(struct fotg210_hcd *fotg210) /* Turn off the schedule after a while */ fotg210_enable_event(fotg210, - FOTG210_HRTIMER_DISABLE_ASYNC, - true); + FOTG210_HRTIMER_DISABLE_ASYNC, + true); } } } @@ -1170,7 +1144,7 @@ static void fotg210_disable_ASE(struct fotg210_hcd *fotg210) /* Poll the STS_PSS status bit; see when it agrees with CMD_PSE */ static void fotg210_poll_PSS(struct fotg210_hcd *fotg210) { - unsigned actual, want; + unsigned actual, want; /* Don't do anything if the controller isn't running (e.g., died) */ if (fotg210->rh_state != FOTG210_RH_RUNNING) @@ -1184,7 +1158,7 @@ static void fotg210_poll_PSS(struct fotg210_hcd *fotg210) /* Poll again later, but give up after about 20 ms */ if (fotg210->PSS_poll_count++ < 20) { fotg210_enable_event(fotg210, FOTG210_HRTIMER_POLL_PSS, - true); + true); return; } fotg210_dbg(fotg210, "Waited too long for the periodic schedule status (%x/%x), giving up\n", @@ -1202,8 +1176,8 @@ static void fotg210_poll_PSS(struct fotg210_hcd *fotg210) /* Turn off the schedule after a while */ fotg210_enable_event(fotg210, - FOTG210_HRTIMER_DISABLE_PERIODIC, - true); + FOTG210_HRTIMER_DISABLE_PERIODIC, + true); } } } @@ -1224,7 +1198,7 @@ static void fotg210_handle_controller_death(struct fotg210_hcd *fotg210) if (fotg210->died_poll_count++ < 5) { /* Try again later */ fotg210_enable_event(fotg210, - FOTG210_HRTIMER_POLL_DEAD, true); + FOTG210_HRTIMER_POLL_DEAD, true); return; } fotg210_warn(fotg210, "Waited too long for the controller to stop, giving up\n"); @@ -1243,7 +1217,7 @@ static void fotg210_handle_controller_death(struct fotg210_hcd *fotg210) /* Handle unlinked interrupt QHs once they are gone from the hardware */ static void fotg210_handle_intr_unlinks(struct fotg210_hcd *fotg210) { - bool stopped = (fotg210->rh_state < FOTG210_RH_RUNNING); + bool stopped = (fotg210->rh_state < FOTG210_RH_RUNNING); /* * Process all the QHs on the intr_unlink list that were added @@ -1254,7 +1228,7 @@ static void fotg210_handle_intr_unlinks(struct fotg210_hcd *fotg210) */ fotg210->intr_unlinking = true; while (fotg210->intr_unlink) { - struct fotg210_qh *qh = fotg210->intr_unlink; + struct fotg210_qh *qh = fotg210->intr_unlink; if (!stopped && qh->unlink_cycle == fotg210->intr_unlink_cycle) break; @@ -1266,7 +1240,7 @@ static void fotg210_handle_intr_unlinks(struct fotg210_hcd *fotg210) /* Handle remaining entries later */ if (fotg210->intr_unlink) { fotg210_enable_event(fotg210, FOTG210_HRTIMER_UNLINK_INTR, - true); + true); ++fotg210->intr_unlink_cycle; } fotg210->intr_unlinking = false; @@ -1288,7 +1262,7 @@ static void start_free_itds(struct fotg210_hcd *fotg210) /* Wait for controller to stop using old iTDs and siTDs */ static void end_free_itds(struct fotg210_hcd *fotg210) { - struct fotg210_itd *itd, *n; + struct fotg210_itd *itd, *n; if (fotg210->rh_state < FOTG210_RH_RUNNING) fotg210->last_itd_to_free = NULL; @@ -1339,7 +1313,7 @@ static void fotg210_iaa_watchdog(struct fotg210_hcd *fotg210) if ((status & STS_IAA) || !(cmd & CMD_IAAD)) { COUNT(fotg210->stats.lost_iaa); fotg210_writel(fotg210, STS_IAA, - &fotg210->regs->status); + &fotg210->regs->status); } fotg210_dbg(fotg210, "IAA watchdog: status %x cmd %x\n", @@ -1355,7 +1329,7 @@ static void turn_on_io_watchdog(struct fotg210_hcd *fotg210) /* Not needed if the controller isn't running or it's already enabled */ if (fotg210->rh_state != FOTG210_RH_RUNNING || (fotg210->enabled_hrtimer_events & - BIT(FOTG210_HRTIMER_IO_WATCHDOG))) + BIT(FOTG210_HRTIMER_IO_WATCHDOG))) return; /* @@ -1365,12 +1339,11 @@ static void turn_on_io_watchdog(struct fotg210_hcd *fotg210) if (fotg210->isoc_count > 0 || (fotg210->need_io_watchdog && fotg210->async_count + fotg210->intr_count > 0)) fotg210_enable_event(fotg210, FOTG210_HRTIMER_IO_WATCHDOG, - true); + true); } -/* - * Handler functions for the hrtimer event types. +/* Handler functions for the hrtimer event types. * Keep this array in the same order as the event types indexed by * enum fotg210_hrtimer_event in fotg210.h. */ @@ -1391,10 +1364,10 @@ static enum hrtimer_restart fotg210_hrtimer_func(struct hrtimer *t) { struct fotg210_hcd *fotg210 = container_of(t, struct fotg210_hcd, hrtimer); - ktime_t now; - unsigned long events; - unsigned long flags; - unsigned e; + ktime_t now; + unsigned long events; + unsigned long flags; + unsigned e; spin_lock_irqsave(&fotg210->lock, flags); @@ -1418,50 +1391,37 @@ static enum hrtimer_restart fotg210_hrtimer_func(struct hrtimer *t) return HRTIMER_NORESTART; } -/*-------------------------------------------------------------------------*/ - -#define fotg210_bus_suspend NULL -#define fotg210_bus_resume NULL +#define fotg210_bus_suspend NULL +#define fotg210_bus_resume NULL -/*-------------------------------------------------------------------------*/ - -static int check_reset_complete( - struct fotg210_hcd *fotg210, - int index, - u32 __iomem *status_reg, - int port_status -) { +static int check_reset_complete(struct fotg210_hcd *fotg210, int index, + u32 __iomem *status_reg, int port_status) +{ if (!(port_status & PORT_CONNECT)) return port_status; /* if reset finished and it's still not enabled -- handoff */ - if (!(port_status & PORT_PE)) { + if (!(port_status & PORT_PE)) /* with integrated TT, there's nobody to hand it to! */ - fotg210_dbg(fotg210, - "Failed to enable port %d on root hub TT\n", - index+1); - return port_status; - } else { + fotg210_dbg(fotg210, "Failed to enable port %d on root hub TT\n", + index + 1); + else fotg210_dbg(fotg210, "port %d reset complete, port enabled\n", - index + 1); - } + index + 1); return port_status; } -/*-------------------------------------------------------------------------*/ - /* build "status change" packet (one or two bytes) from HC registers */ -static int -fotg210_hub_status_data(struct usb_hcd *hcd, char *buf) +static int fotg210_hub_status_data(struct usb_hcd *hcd, char *buf) { - struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); - u32 temp, status; - u32 mask; - int retval = 1; - unsigned long flags; + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + u32 temp, status; + u32 mask; + int retval = 1; + unsigned long flags; /* init status to no-changes */ buf[0] = 0; @@ -1488,9 +1448,9 @@ fotg210_hub_status_data(struct usb_hcd *hcd, char *buf) * controller by the user. */ - if ((temp & mask) != 0 || test_bit(0, &fotg210->port_c_suspend) - || (fotg210->reset_done[0] && time_after_eq( - jiffies, fotg210->reset_done[0]))) { + if ((temp & mask) != 0 || test_bit(0, &fotg210->port_c_suspend) || + (fotg210->reset_done[0] && + time_after_eq(jiffies, fotg210->reset_done[0]))) { buf[0] |= 1 << 1; status = STS_PCD; } @@ -1499,15 +1459,11 @@ fotg210_hub_status_data(struct usb_hcd *hcd, char *buf) return status ? retval : 0; } -/*-------------------------------------------------------------------------*/ - -static void -fotg210_hub_descriptor( - struct fotg210_hcd *fotg210, - struct usb_hub_descriptor *desc -) { - int ports = HCS_N_PORTS(fotg210->hcs_params); - u16 temp; +static void fotg210_hub_descriptor(struct fotg210_hcd *fotg210, + struct usb_hub_descriptor *desc) +{ + int ports = HCS_N_PORTS(fotg210->hcs_params); + u16 temp; desc->bDescriptorType = USB_DT_HUB; desc->bPwrOn2PwrGood = 10; /* fotg210 1.0, 2.3.9 says 20ms max */ @@ -1526,23 +1482,16 @@ fotg210_hub_descriptor( desc->wHubCharacteristics = cpu_to_le16(temp); } -/*-------------------------------------------------------------------------*/ - -static int fotg210_hub_control( - struct usb_hcd *hcd, - u16 typeReq, - u16 wValue, - u16 wIndex, - char *buf, - u16 wLength -) { - struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); - int ports = HCS_N_PORTS(fotg210->hcs_params); - u32 __iomem *status_reg = &fotg210->regs->port_status; - u32 temp, temp1, status; - unsigned long flags; - int retval = 0; - unsigned selector; +static int fotg210_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, + u16 wIndex, char *buf, u16 wLength) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + int ports = HCS_N_PORTS(fotg210->hcs_params); + u32 __iomem *status_reg = &fotg210->regs->port_status; + u32 temp, temp1, status; + unsigned long flags; + int retval = 0; + unsigned selector; /* * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR. @@ -1605,7 +1554,7 @@ static int fotg210_hub_control( break; case USB_PORT_FEAT_C_OVER_CURRENT: fotg210_writel(fotg210, temp | OTGISR_OVC, - &fotg210->regs->otgisr); + &fotg210->regs->otgisr); break; case USB_PORT_FEAT_C_RESET: /* GetPortStatus clears reset */ @@ -1617,7 +1566,7 @@ static int fotg210_hub_control( break; case GetHubDescriptor: fotg210_hub_descriptor(fotg210, (struct usb_hub_descriptor *) - buf); + buf); break; case GetHubStatus: /* no hub-wide feature/status flags */ @@ -1663,16 +1612,16 @@ static int fotg210_hub_control( /* stop resume signaling */ temp = fotg210_readl(fotg210, status_reg); - fotg210_writel(fotg210, - temp & ~(PORT_RWC_BITS | PORT_RESUME), - status_reg); + fotg210_writel(fotg210, temp & + ~(PORT_RWC_BITS | PORT_RESUME), + status_reg); clear_bit(wIndex, &fotg210->resuming_ports); retval = handshake(fotg210, status_reg, - PORT_RESUME, 0, 2000 /* 2msec */); + PORT_RESUME, 0, 2000);/* 2ms */ if (retval != 0) { fotg210_err(fotg210, - "port %d resume error %d\n", - wIndex + 1, retval); + "port %d resume error %d\n", + wIndex + 1, retval); goto error; } temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10)); @@ -1680,17 +1629,16 @@ static int fotg210_hub_control( } /* whoever resets must GetPortStatus to complete it!! */ - if ((temp & PORT_RESET) - && time_after_eq(jiffies, - fotg210->reset_done[wIndex])) { + if ((temp & PORT_RESET) && time_after_eq(jiffies, + fotg210->reset_done[wIndex])) { status |= USB_PORT_STAT_C_RESET << 16; fotg210->reset_done[wIndex] = 0; clear_bit(wIndex, &fotg210->resuming_ports); /* force reset to complete */ fotg210_writel(fotg210, - temp & ~(PORT_RWC_BITS | PORT_RESET), - status_reg); + temp & ~(PORT_RWC_BITS | PORT_RESET), + status_reg); /* REVISIT: some hardware needs 550+ usec to clear * this bit; seems too long to spin routinely... */ @@ -1698,7 +1646,7 @@ static int fotg210_hub_control( PORT_RESET, 0, 1000); if (retval != 0) { fotg210_err(fotg210, "port %d reset error %d\n", - wIndex + 1, retval); + wIndex + 1, retval); goto error; } @@ -1718,7 +1666,7 @@ static int fotg210_hub_control( temp &= ~PORT_RWC_BITS; fotg210_writel(fotg210, temp, status_reg); fotg210_dbg(fotg210, "port %d --> companion\n", - wIndex + 1); + wIndex + 1); temp = fotg210_readl(fotg210, status_reg); } @@ -1788,7 +1736,7 @@ static int fotg210_hub_control( * mode if we have hostpc feature */ fotg210_writel(fotg210, temp | PORT_SUSPEND, - status_reg); + status_reg); set_bit(wIndex, &fotg210->suspended_ports); break; case USB_PORT_FEAT_RESET: @@ -1866,9 +1814,8 @@ static int __maybe_unused fotg210_port_handed_over(struct usb_hcd *hcd, { return 0; } -/*-------------------------------------------------------------------------*/ -/* - * There's basically three types of memory: + +/* There's basically three types of memory: * - data used only by the HCD ... kmalloc is fine * - async and periodic schedules, shared by HC and HCD ... these * need to use dma_pool or dma_alloc_coherent @@ -1878,12 +1825,9 @@ static int __maybe_unused fotg210_port_handed_over(struct usb_hcd *hcd, * No memory seen by this driver is pageable. */ -/*-------------------------------------------------------------------------*/ - /* Allocate the key transfer structures from the previously allocated pool */ - static inline void fotg210_qtd_init(struct fotg210_hcd *fotg210, - struct fotg210_qtd *qtd, dma_addr_t dma) + struct fotg210_qtd *qtd, dma_addr_t dma) { memset(qtd, 0, sizeof(*qtd)); qtd->qtd_dma = dma; @@ -1894,10 +1838,10 @@ static inline void fotg210_qtd_init(struct fotg210_hcd *fotg210, } static struct fotg210_qtd *fotg210_qtd_alloc(struct fotg210_hcd *fotg210, - gfp_t flags) + gfp_t flags) { - struct fotg210_qtd *qtd; - dma_addr_t dma; + struct fotg210_qtd *qtd; + dma_addr_t dma; qtd = dma_pool_alloc(fotg210->qtd_pool, flags, &dma); if (qtd != NULL) @@ -1907,7 +1851,7 @@ static struct fotg210_qtd *fotg210_qtd_alloc(struct fotg210_hcd *fotg210, } static inline void fotg210_qtd_free(struct fotg210_hcd *fotg210, - struct fotg210_qtd *qtd) + struct fotg210_qtd *qtd) { dma_pool_free(fotg210->qtd_pool, qtd, qtd->qtd_dma); } @@ -1927,10 +1871,10 @@ static void qh_destroy(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) } static struct fotg210_qh *fotg210_qh_alloc(struct fotg210_hcd *fotg210, - gfp_t flags) + gfp_t flags) { - struct fotg210_qh *qh; - dma_addr_t dma; + struct fotg210_qh *qh; + dma_addr_t dma; qh = kzalloc(sizeof(*qh), GFP_ATOMIC); if (!qh) @@ -1958,8 +1902,6 @@ fail: return NULL; } -/*-------------------------------------------------------------------------*/ - /* The queue heads and transfer descriptors are managed from pools tied * to each of the "per device" structures. * This is the initialisation and cleanup code. @@ -1976,23 +1918,19 @@ static void fotg210_mem_cleanup(struct fotg210_hcd *fotg210) fotg210->dummy = NULL; /* DMA consistent memory and pools */ - if (fotg210->qtd_pool) - dma_pool_destroy(fotg210->qtd_pool); + dma_pool_destroy(fotg210->qtd_pool); fotg210->qtd_pool = NULL; - if (fotg210->qh_pool) { - dma_pool_destroy(fotg210->qh_pool); - fotg210->qh_pool = NULL; - } + dma_pool_destroy(fotg210->qh_pool); + fotg210->qh_pool = NULL; - if (fotg210->itd_pool) - dma_pool_destroy(fotg210->itd_pool); + dma_pool_destroy(fotg210->itd_pool); fotg210->itd_pool = NULL; if (fotg210->periodic) dma_free_coherent(fotg210_to_hcd(fotg210)->self.controller, - fotg210->periodic_size * sizeof(u32), - fotg210->periodic, fotg210->periodic_dma); + fotg210->periodic_size * sizeof(u32), + fotg210->periodic, fotg210->periodic_dma); fotg210->periodic = NULL; /* shadow periodic table */ @@ -2039,8 +1977,8 @@ static int fotg210_mem_init(struct fotg210_hcd *fotg210, gfp_t flags) /* Hardware periodic table */ fotg210->periodic = (__le32 *) dma_alloc_coherent(fotg210_to_hcd(fotg210)->self.controller, - fotg210->periodic_size * sizeof(__le32), - &fotg210->periodic_dma, 0); + fotg210->periodic_size * sizeof(__le32), + &fotg210->periodic_dma, 0); if (fotg210->periodic == NULL) goto fail; @@ -2049,7 +1987,7 @@ static int fotg210_mem_init(struct fotg210_hcd *fotg210, gfp_t flags) /* software shadow of hardware table */ fotg210->pshadow = kcalloc(fotg210->periodic_size, sizeof(void *), - flags); + flags); if (fotg210->pshadow != NULL) return 0; @@ -2058,9 +1996,7 @@ fail: fotg210_mem_cleanup(fotg210); return -ENOMEM; } -/*-------------------------------------------------------------------------*/ -/* - * EHCI hardware queue manipulation ... the core. QH/QTD manipulation. +/* EHCI hardware queue manipulation ... the core. QH/QTD manipulation. * * Control, bulk, and interrupt traffic all use "qh" lists. They list "qtd" * entries describing USB transactions, max 16-20kB/entry (with 4kB-aligned @@ -2077,16 +2013,12 @@ fail: * buffer low/full speed data so the host collects it at high speed. */ -/*-------------------------------------------------------------------------*/ - /* fill a qtd, returning how much of the buffer we were able to queue up */ - -static int -qtd_fill(struct fotg210_hcd *fotg210, struct fotg210_qtd *qtd, dma_addr_t buf, - size_t len, int token, int maxpacket) +static int qtd_fill(struct fotg210_hcd *fotg210, struct fotg210_qtd *qtd, + dma_addr_t buf, size_t len, int token, int maxpacket) { - int i, count; - u64 addr = buf; + int i, count; + u64 addr = buf; /* one buffer entry per 4K ... first might be short or unaligned */ qtd->hw_buf[0] = cpu_to_hc32(fotg210, (u32)addr); @@ -2121,11 +2053,8 @@ qtd_fill(struct fotg210_hcd *fotg210, struct fotg210_qtd *qtd, dma_addr_t buf, return count; } -/*-------------------------------------------------------------------------*/ - -static inline void -qh_update(struct fotg210_hcd *fotg210, struct fotg210_qh *qh, - struct fotg210_qtd *qtd) +static inline void qh_update(struct fotg210_hcd *fotg210, + struct fotg210_qh *qh, struct fotg210_qtd *qtd) { struct fotg210_qh_hw *hw = qh->hw; @@ -2141,7 +2070,7 @@ qh_update(struct fotg210_hcd *fotg210, struct fotg210_qh *qh, * ever clear it. */ if (!(hw->hw_info1 & cpu_to_hc32(fotg210, QH_TOGGLE_CTL))) { - unsigned is_out, epnum; + unsigned is_out, epnum; is_out = qh->is_out; epnum = (hc32_to_cpup(fotg210, &hw->hw_info1) >> 8) & 0x0f; @@ -2158,8 +2087,7 @@ qh_update(struct fotg210_hcd *fotg210, struct fotg210_qh *qh, * overlay, so qh->hw_token wrongly becomes inactive/halted), only fault * recovery (including urb dequeue) would need software changes to a QH... */ -static void -qh_refresh(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) +static void qh_refresh(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) { struct fotg210_qtd *qtd; @@ -2185,16 +2113,14 @@ qh_refresh(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) qh_update(fotg210, qh, qtd); } -/*-------------------------------------------------------------------------*/ - static void qh_link_async(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); static void fotg210_clear_tt_buffer_complete(struct usb_hcd *hcd, struct usb_host_endpoint *ep) { - struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); - struct fotg210_qh *qh = ep->hcpriv; - unsigned long flags; + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + struct fotg210_qh *qh = ep->hcpriv; + unsigned long flags; spin_lock_irqsave(&fotg210->lock, flags); qh->clearing_tt = 0; @@ -2205,8 +2131,7 @@ static void fotg210_clear_tt_buffer_complete(struct usb_hcd *hcd, } static void fotg210_clear_tt_buffer(struct fotg210_hcd *fotg210, - struct fotg210_qh *qh, - struct urb *urb, u32 token) + struct fotg210_qh *qh, struct urb *urb, u32 token) { /* If an async split transaction gets an error or is unlinked, @@ -2217,27 +2142,24 @@ static void fotg210_clear_tt_buffer(struct fotg210_hcd *fotg210, */ if (urb->dev->tt && !usb_pipeint(urb->pipe) && !qh->clearing_tt) { struct usb_device *tt = urb->dev->tt->hub; + dev_dbg(&tt->dev, - "clear tt buffer port %d, a%d ep%d t%08x\n", - urb->dev->ttport, urb->dev->devnum, - usb_pipeendpoint(urb->pipe), token); + "clear tt buffer port %d, a%d ep%d t%08x\n", + urb->dev->ttport, urb->dev->devnum, + usb_pipeendpoint(urb->pipe), token); if (urb->dev->tt->hub != - fotg210_to_hcd(fotg210)->self.root_hub) { + fotg210_to_hcd(fotg210)->self.root_hub) { if (usb_hub_clear_tt_buffer(urb) == 0) qh->clearing_tt = 1; } } } -static int qtd_copy_status( - struct fotg210_hcd *fotg210, - struct urb *urb, - size_t length, - u32 token -) +static int qtd_copy_status(struct fotg210_hcd *fotg210, struct urb *urb, + size_t length, u32 token) { - int status = -EINPROGRESS; + int status = -EINPROGRESS; /* count IN/OUT bytes, not SETUP (even short packets) */ if (likely(QTD_PID(token) != 2)) @@ -2274,32 +2196,32 @@ static int qtd_copy_status( } else if (token & QTD_STS_XACT) { /* timeout, bad CRC, wrong PID, etc */ fotg210_dbg(fotg210, "devpath %s ep%d%s 3strikes\n", - urb->dev->devpath, - usb_pipeendpoint(urb->pipe), - usb_pipein(urb->pipe) ? "in" : "out"); + urb->dev->devpath, + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out"); status = -EPROTO; } else { /* unknown */ status = -EPROTO; } fotg210_dbg(fotg210, - "dev%d ep%d%s qtd token %08x --> status %d\n", - usb_pipedevice(urb->pipe), - usb_pipeendpoint(urb->pipe), - usb_pipein(urb->pipe) ? "in" : "out", - token, status); + "dev%d ep%d%s qtd token %08x --> status %d\n", + usb_pipedevice(urb->pipe), + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out", + token, status); } return status; } -static void -fotg210_urb_done(struct fotg210_hcd *fotg210, struct urb *urb, int status) +static void fotg210_urb_done(struct fotg210_hcd *fotg210, struct urb *urb, + int status) __releases(fotg210->lock) __acquires(fotg210->lock) { if (likely(urb->hcpriv != NULL)) { - struct fotg210_qh *qh = (struct fotg210_qh *) urb->hcpriv; + struct fotg210_qh *qh = (struct fotg210_qh *) urb->hcpriv; /* S-mask in a QH means it's an interrupt urb */ if ((qh->hw->hw_info2 & cpu_to_hc32(fotg210, QH_SMASK)) != 0) { @@ -2320,12 +2242,12 @@ __acquires(fotg210->lock) #ifdef FOTG210_URB_TRACE fotg210_dbg(fotg210, - "%s %s urb %p ep%d%s status %d len %d/%d\n", - __func__, urb->dev->devpath, urb, - usb_pipeendpoint(urb->pipe), - usb_pipein(urb->pipe) ? "in" : "out", - status, - urb->actual_length, urb->transfer_buffer_length); + "%s %s urb %p ep%d%s status %d len %d/%d\n", + __func__, urb->dev->devpath, urb, + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out", + status, + urb->actual_length, urb->transfer_buffer_length); #endif /* complete() can reenter this HCD */ @@ -2337,21 +2259,20 @@ __acquires(fotg210->lock) static int qh_schedule(struct fotg210_hcd *fotg210, struct fotg210_qh *qh); -/* - * Process and free completed qtds for a qh, returning URBs to drivers. +/* Process and free completed qtds for a qh, returning URBs to drivers. * Chases up to qh->hw_current. Returns number of completions called, * indicating how much "real" work we did. */ -static unsigned -qh_completions(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) +static unsigned qh_completions(struct fotg210_hcd *fotg210, + struct fotg210_qh *qh) { - struct fotg210_qtd *last, *end = qh->dummy; - struct list_head *entry, *tmp; - int last_status; - int stopped; - unsigned count = 0; - u8 state; - struct fotg210_qh_hw *hw = qh->hw; + struct fotg210_qtd *last, *end = qh->dummy; + struct list_head *entry, *tmp; + int last_status; + int stopped; + unsigned count = 0; + u8 state; + struct fotg210_qh_hw *hw = qh->hw; if (unlikely(list_empty(&qh->qtd_list))) return count; @@ -2370,7 +2291,7 @@ qh_completions(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) qh->qh_state = QH_STATE_COMPLETING; stopped = (state == QH_STATE_IDLE); - rescan: +rescan: last = NULL; last_status = -EINPROGRESS; qh->needs_rescan = 0; @@ -2381,9 +2302,9 @@ qh_completions(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) * if queue is stopped, handles unlinks. */ list_for_each_safe(entry, tmp, &qh->qtd_list) { - struct fotg210_qtd *qtd; - struct urb *urb; - u32 token = 0; + struct fotg210_qtd *qtd; + struct urb *urb; + u32 token = 0; qtd = list_entry(entry, struct fotg210_qtd, qtd_list); urb = qtd->urb; @@ -2392,7 +2313,7 @@ qh_completions(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) if (last) { if (likely(last->urb != urb)) { fotg210_urb_done(fotg210, last->urb, - last_status); + last_status); count++; last_status = -EINPROGRESS; } @@ -2409,20 +2330,17 @@ qh_completions(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) token = hc32_to_cpu(fotg210, qtd->hw_token); /* always clean up qtds the hc de-activated */ - retry_xacterr: +retry_xacterr: if ((token & QTD_STS_ACTIVE) == 0) { /* Report Data Buffer Error: non-fatal but useful */ if (token & QTD_STS_DBE) fotg210_dbg(fotg210, "detected DataBufferErr for urb %p ep%d%s len %d, qtd %p [qh %p]\n", - urb, - usb_endpoint_num(&urb->ep->desc), + urb, usb_endpoint_num(&urb->ep->desc), usb_endpoint_dir_in(&urb->ep->desc) ? "in" : "out", - urb->transfer_buffer_length, - qtd, - qh); + urb->transfer_buffer_length, qtd, qh); /* on STALL, error, and short reads this urb must * complete and all its qtds must be recycled. @@ -2433,12 +2351,14 @@ qh_completions(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) * reach the software xacterr limit */ if ((token & QTD_STS_XACT) && - QTD_CERR(token) == 0 && - ++qh->xacterrs < QH_XACTERR_MAX && - !urb->unlinked) { + QTD_CERR(token) == 0 && + ++qh->xacterrs < QH_XACTERR_MAX && + !urb->unlinked) { fotg210_dbg(fotg210, - "detected XactErr len %zu/%zu retry %d\n", - qtd->length - QTD_LENGTH(token), qtd->length, qh->xacterrs); + "detected XactErr len %zu/%zu retry %d\n", + qtd->length - QTD_LENGTH(token), + qtd->length, + qh->xacterrs); /* reset the token in the qtd and the * qh overlay (which still contains @@ -2466,9 +2386,9 @@ qh_completions(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) * URB_SHORT_NOT_OK was set so the driver submitting * the urbs could clean it up. */ - } else if (IS_SHORT_READ(token) - && !(qtd->hw_alt_next - & FOTG210_LIST_END(fotg210))) { + } else if (IS_SHORT_READ(token) && + !(qtd->hw_alt_next & + FOTG210_LIST_END(fotg210))) { stopped = 1; } @@ -2492,9 +2412,9 @@ qh_completions(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) continue; /* qh unlinked; token in overlay may be most current */ - if (state == QH_STATE_IDLE - && cpu_to_hc32(fotg210, qtd->qtd_dma) - == hw->hw_current) { + if (state == QH_STATE_IDLE && + cpu_to_hc32(fotg210, qtd->qtd_dma) + == hw->hw_current) { token = hc32_to_cpu(fotg210, hw->hw_token); /* An unlink may leave an incomplete @@ -2502,7 +2422,7 @@ qh_completions(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) * We have to clear it. */ fotg210_clear_tt_buffer(fotg210, qh, urb, - token); + token); } } @@ -2516,9 +2436,9 @@ qh_completions(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) if (last_status == -EINPROGRESS) { last_status = qtd_copy_status(fotg210, urb, qtd->length, token); - if (last_status == -EREMOTEIO - && (qtd->hw_alt_next - & FOTG210_LIST_END(fotg210))) + if (last_status == -EREMOTEIO && + (qtd->hw_alt_next & + FOTG210_LIST_END(fotg210))) last_status = -EINPROGRESS; /* As part of low/full-speed endpoint-halt processing @@ -2537,7 +2457,7 @@ qh_completions(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) */ if (last_status != -EPIPE) fotg210_clear_tt_buffer(fotg210, qh, - urb, token); + urb, token); } } @@ -2615,26 +2535,21 @@ qh_completions(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) return count; } -/*-------------------------------------------------------------------------*/ - /* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */ #define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) /* ... and packet size, for any kind of endpoint descriptor */ #define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff) -/* - * reverse of qh_urb_transaction: free a list of TDs. +/* reverse of qh_urb_transaction: free a list of TDs. * used for cleanup after errors, before HC sees an URB's TDs. */ -static void qtd_list_free( - struct fotg210_hcd *fotg210, - struct urb *urb, - struct list_head *qtd_list -) { - struct list_head *entry, *temp; +static void qtd_list_free(struct fotg210_hcd *fotg210, struct urb *urb, + struct list_head *qtd_list) +{ + struct list_head *entry, *temp; list_for_each_safe(entry, temp, qtd_list) { - struct fotg210_qtd *qtd; + struct fotg210_qtd *qtd; qtd = list_entry(entry, struct fotg210_qtd, qtd_list); list_del(&qtd->qtd_list); @@ -2642,23 +2557,18 @@ static void qtd_list_free( } } -/* - * create a list of filled qtds for this URB; won't link into qh. +/* create a list of filled qtds for this URB; won't link into qh. */ -static struct list_head * -qh_urb_transaction( - struct fotg210_hcd *fotg210, - struct urb *urb, - struct list_head *head, - gfp_t flags -) { - struct fotg210_qtd *qtd, *qtd_prev; - dma_addr_t buf; - int len, this_sg_len, maxpacket; - int is_input; - u32 token; - int i; - struct scatterlist *sg; +static struct list_head *qh_urb_transaction(struct fotg210_hcd *fotg210, + struct urb *urb, struct list_head *head, gfp_t flags) +{ + struct fotg210_qtd *qtd, *qtd_prev; + dma_addr_t buf; + int len, this_sg_len, maxpacket; + int is_input; + u32 token; + int i; + struct scatterlist *sg; /* * URBs map to sequences of QTDs: one logical transaction @@ -2768,8 +2678,8 @@ qh_urb_transaction( * have the alt_next mechanism keep the queue running after the * last data qtd (the only one, for control and most other cases). */ - if (likely((urb->transfer_flags & URB_SHORT_NOT_OK) == 0 - || usb_pipecontrol(urb->pipe))) + if (likely((urb->transfer_flags & URB_SHORT_NOT_OK) == 0 || + usb_pipecontrol(urb->pipe))) qtd->hw_alt_next = FOTG210_LIST_END(fotg210); /* @@ -2778,7 +2688,7 @@ qh_urb_transaction( * (zero length). */ if (likely(urb->transfer_buffer_length != 0)) { - int one_more = 0; + int one_more = 0; if (usb_pipecontrol(urb->pipe)) { one_more = 1; @@ -2813,9 +2723,7 @@ cleanup: return NULL; } -/*-------------------------------------------------------------------------*/ -/* - * Would be best to create all qh's from config descriptors, +/* Would be best to create all qh's from config descriptors, * when each interface/altsetting is established. Unlink * any previous qh and cancel its urbs first; endpoints are * implicitly reset then (data toggle too). @@ -2823,26 +2731,22 @@ cleanup: */ -/* - * Each QH holds a qtd list; a QH is used for everything except iso. +/* Each QH holds a qtd list; a QH is used for everything except iso. * * For interrupt urbs, the scheduler must set the microframe scheduling * mask(s) each time the QH gets scheduled. For highspeed, that's * just one microframe in the s-mask. For split interrupt transactions * there are additional complications: c-mask, maybe FSTNs. */ -static struct fotg210_qh * -qh_make( - struct fotg210_hcd *fotg210, - struct urb *urb, - gfp_t flags -) { - struct fotg210_qh *qh = fotg210_qh_alloc(fotg210, flags); - u32 info1 = 0, info2 = 0; - int is_input, type; - int maxp = 0; - struct usb_tt *tt = urb->dev->tt; - struct fotg210_qh_hw *hw; +static struct fotg210_qh *qh_make(struct fotg210_hcd *fotg210, struct urb *urb, + gfp_t flags) +{ + struct fotg210_qh *qh = fotg210_qh_alloc(fotg210, flags); + u32 info1 = 0, info2 = 0; + int is_input, type; + int maxp = 0; + struct usb_tt *tt = urb->dev->tt; + struct fotg210_qh_hw *hw; if (!qh) return qh; @@ -2862,7 +2766,7 @@ qh_make( */ if (max_packet(maxp) > 1024) { fotg210_dbg(fotg210, "bogus qh maxpacket %d\n", - max_packet(maxp)); + max_packet(maxp)); goto done; } @@ -2896,7 +2800,7 @@ qh_make( urb->interval = qh->period << 3; } } else { - int think_time; + int think_time; /* gap is f(FS/LS transfer times) */ qh->gap_uf = 1 + usb_calc_bus_time(urb->dev->speed, @@ -2986,7 +2890,7 @@ qh_make( break; default: fotg210_dbg(fotg210, "bogus dev %p speed %d\n", urb->dev, - urb->dev->speed); + urb->dev->speed); done: qh_destroy(fotg210, qh); return NULL; @@ -3005,8 +2909,6 @@ done: return qh; } -/*-------------------------------------------------------------------------*/ - static void enable_async(struct fotg210_hcd *fotg210) { if (fotg210->async_count++) @@ -3036,8 +2938,8 @@ static void disable_async(struct fotg210_hcd *fotg210) static void qh_link_async(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) { - __hc32 dma = QH_NEXT(fotg210, qh->qh_dma); - struct fotg210_qh *head; + __hc32 dma = QH_NEXT(fotg210, qh->qh_dma); + struct fotg210_qh *head; /* Don't link a QH if there's a Clear-TT-Buffer pending */ if (unlikely(qh->clearing_tt)) @@ -3064,24 +2966,17 @@ static void qh_link_async(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) enable_async(fotg210); } -/*-------------------------------------------------------------------------*/ - -/* - * For control/bulk/interrupt, return QH with these TDs appended. +/* For control/bulk/interrupt, return QH with these TDs appended. * Allocates and initializes the QH if necessary. * Returns null if it can't allocate a QH it needs to. * If the QH has TDs (urbs) already, that's great. */ -static struct fotg210_qh *qh_append_tds( - struct fotg210_hcd *fotg210, - struct urb *urb, - struct list_head *qtd_list, - int epnum, - void **ptr -) +static struct fotg210_qh *qh_append_tds(struct fotg210_hcd *fotg210, + struct urb *urb, struct list_head *qtd_list, + int epnum, void **ptr) { - struct fotg210_qh *qh = NULL; - __hc32 qh_addr_mask = cpu_to_hc32(fotg210, 0x7f); + struct fotg210_qh *qh = NULL; + __hc32 qh_addr_mask = cpu_to_hc32(fotg210, 0x7f); qh = (struct fotg210_qh *) *ptr; if (unlikely(qh == NULL)) { @@ -3090,7 +2985,7 @@ static struct fotg210_qh *qh_append_tds( *ptr = qh; } if (likely(qh != NULL)) { - struct fotg210_qtd *qtd; + struct fotg210_qtd *qtd; if (unlikely(list_empty(qtd_list))) qtd = NULL; @@ -3109,9 +3004,9 @@ static struct fotg210_qh *qh_append_tds( * only hc or qh_refresh() ever modify the overlay. */ if (likely(qtd != NULL)) { - struct fotg210_qtd *dummy; - dma_addr_t dma; - __hc32 token; + struct fotg210_qtd *dummy; + dma_addr_t dma; + __hc32 token; /* to avoid racing the HC, use the dummy td instead of * the first td of our list (becomes new dummy). both @@ -3150,32 +3045,28 @@ static struct fotg210_qh *qh_append_tds( return qh; } -/*-------------------------------------------------------------------------*/ - -static int -submit_async( - struct fotg210_hcd *fotg210, - struct urb *urb, - struct list_head *qtd_list, - gfp_t mem_flags -) { - int epnum; - unsigned long flags; - struct fotg210_qh *qh = NULL; - int rc; +static int submit_async(struct fotg210_hcd *fotg210, struct urb *urb, + struct list_head *qtd_list, gfp_t mem_flags) +{ + int epnum; + unsigned long flags; + struct fotg210_qh *qh = NULL; + int rc; epnum = urb->ep->desc.bEndpointAddress; #ifdef FOTG210_URB_TRACE { struct fotg210_qtd *qtd; + qtd = list_entry(qtd_list->next, struct fotg210_qtd, qtd_list); fotg210_dbg(fotg210, - "%s %s urb %p ep%d%s len %d, qtd %p [qh %p]\n", - __func__, urb->dev->devpath, urb, - epnum & 0x0f, (epnum & USB_DIR_IN) ? "in" : "out", - urb->transfer_buffer_length, - qtd, urb->ep->hcpriv); + "%s %s urb %p ep%d%s len %d, qtd %p [qh %p]\n", + __func__, urb->dev->devpath, urb, + epnum & 0x0f, (epnum & USB_DIR_IN) + ? "in" : "out", + urb->transfer_buffer_length, + qtd, urb->ep->hcpriv); } #endif @@ -3200,19 +3091,17 @@ submit_async( */ if (likely(qh->qh_state == QH_STATE_IDLE)) qh_link_async(fotg210, qh); - done: +done: spin_unlock_irqrestore(&fotg210->lock, flags); if (unlikely(qh == NULL)) qtd_list_free(fotg210, urb, qtd_list); return rc; } -/*-------------------------------------------------------------------------*/ - static void single_unlink_async(struct fotg210_hcd *fotg210, - struct fotg210_qh *qh) + struct fotg210_qh *qh) { - struct fotg210_qh *prev; + struct fotg210_qh *prev; /* Add to the end of the list of QHs waiting for the next IAAD */ qh->qh_state = QH_STATE_UNLINK; @@ -3260,7 +3149,7 @@ static void start_iaa_cycle(struct fotg210_hcd *fotg210, bool nested) &fotg210->regs->command); fotg210_readl(fotg210, &fotg210->regs->command); fotg210_enable_event(fotg210, FOTG210_HRTIMER_IAA_WATCHDOG, - true); + true); } } @@ -3268,10 +3157,10 @@ static void start_iaa_cycle(struct fotg210_hcd *fotg210, bool nested) static void end_unlink_async(struct fotg210_hcd *fotg210) { - struct fotg210_qh *qh; + struct fotg210_qh *qh; /* Process the idle QHs */ - restart: +restart: fotg210->async_unlinking = true; while (fotg210->async_iaa) { qh = fotg210->async_iaa; @@ -3326,7 +3215,7 @@ static void unlink_empty_async(struct fotg210_hcd *fotg210) /* QHs that haven't been empty for long enough will be handled later */ if (check_unlinks_later) { fotg210_enable_event(fotg210, FOTG210_HRTIMER_ASYNC_UNLINKS, - true); + true); ++fotg210->async_unlink_cycle; } } @@ -3335,7 +3224,7 @@ static void unlink_empty_async(struct fotg210_hcd *fotg210) /* caller must own fotg210->lock */ static void start_unlink_async(struct fotg210_hcd *fotg210, - struct fotg210_qh *qh) + struct fotg210_qh *qh) { /* * If the QH isn't linked then there's nothing we can do @@ -3352,18 +3241,16 @@ static void start_unlink_async(struct fotg210_hcd *fotg210, start_iaa_cycle(fotg210, false); } -/*-------------------------------------------------------------------------*/ - static void scan_async(struct fotg210_hcd *fotg210) { - struct fotg210_qh *qh; - bool check_unlinks_later = false; + struct fotg210_qh *qh; + bool check_unlinks_later = false; fotg210->qh_scan_next = fotg210->async->qh_next.qh; while (fotg210->qh_scan_next) { qh = fotg210->qh_scan_next; fotg210->qh_scan_next = qh->qh_next.qh; - rescan: +rescan: /* clean any finished work for this qh */ if (!list_empty(&qh->qtd_list)) { int temp; @@ -3395,15 +3282,13 @@ static void scan_async(struct fotg210_hcd *fotg210) */ if (check_unlinks_later && fotg210->rh_state == FOTG210_RH_RUNNING && !(fotg210->enabled_hrtimer_events & - BIT(FOTG210_HRTIMER_ASYNC_UNLINKS))) { + BIT(FOTG210_HRTIMER_ASYNC_UNLINKS))) { fotg210_enable_event(fotg210, - FOTG210_HRTIMER_ASYNC_UNLINKS, true); + FOTG210_HRTIMER_ASYNC_UNLINKS, true); ++fotg210->async_unlink_cycle; } } -/*-------------------------------------------------------------------------*/ -/* - * EHCI scheduled transaction support: interrupt, iso, split iso +/* EHCI scheduled transaction support: interrupt, iso, split iso * These are called "periodic" transactions in the EHCI spec. * * Note that for interrupt transfers, the QH/QTD manipulation is shared @@ -3414,19 +3299,14 @@ static void scan_async(struct fotg210_hcd *fotg210) * It keeps track of every ITD (or SITD) that's linked, and holds enough * pre-calculated schedule data to make appending to the queue be quick. */ - static int fotg210_get_frame(struct usb_hcd *hcd); -/*-------------------------------------------------------------------------*/ - -/* - * periodic_next_shadow - return "next" pointer on shadow list +/* periodic_next_shadow - return "next" pointer on shadow list * @periodic: host pointer to qh/itd * @tag: hardware tag for type of this record */ -static union fotg210_shadow * -periodic_next_shadow(struct fotg210_hcd *fotg210, - union fotg210_shadow *periodic, __hc32 tag) +static union fotg210_shadow *periodic_next_shadow(struct fotg210_hcd *fotg210, + union fotg210_shadow *periodic, __hc32 tag) { switch (hc32_to_cpu(fotg210, tag)) { case Q_TYPE_QH: @@ -3438,9 +3318,8 @@ periodic_next_shadow(struct fotg210_hcd *fotg210, } } -static __hc32 * -shadow_next_periodic(struct fotg210_hcd *fotg210, - union fotg210_shadow *periodic, __hc32 tag) +static __hc32 *shadow_next_periodic(struct fotg210_hcd *fotg210, + union fotg210_shadow *periodic, __hc32 tag) { switch (hc32_to_cpu(fotg210, tag)) { /* our fotg210_shadow.qh is actually software part */ @@ -3454,11 +3333,11 @@ shadow_next_periodic(struct fotg210_hcd *fotg210, /* caller must hold fotg210->lock */ static void periodic_unlink(struct fotg210_hcd *fotg210, unsigned frame, - void *ptr) + void *ptr) { - union fotg210_shadow *prev_p = &fotg210->pshadow[frame]; - __hc32 *hw_p = &fotg210->periodic[frame]; - union fotg210_shadow here = *prev_p; + union fotg210_shadow *prev_p = &fotg210->pshadow[frame]; + __hc32 *hw_p = &fotg210->periodic[frame]; + union fotg210_shadow here = *prev_p; /* find predecessor of "ptr"; hw and shadow lists are in sync */ while (here.ptr && here.ptr != ptr) { @@ -3479,17 +3358,17 @@ static void periodic_unlink(struct fotg210_hcd *fotg210, unsigned frame, Q_NEXT_TYPE(fotg210, *hw_p)); *hw_p = *shadow_next_periodic(fotg210, &here, - Q_NEXT_TYPE(fotg210, *hw_p)); + Q_NEXT_TYPE(fotg210, *hw_p)); } /* how many of the uframe's 125 usecs are allocated? */ -static unsigned short -periodic_usecs(struct fotg210_hcd *fotg210, unsigned frame, unsigned uframe) +static unsigned short periodic_usecs(struct fotg210_hcd *fotg210, + unsigned frame, unsigned uframe) { - __hc32 *hw_p = &fotg210->periodic[frame]; - union fotg210_shadow *q = &fotg210->pshadow[frame]; - unsigned usecs = 0; - struct fotg210_qh_hw *hw; + __hc32 *hw_p = &fotg210->periodic[frame]; + union fotg210_shadow *q = &fotg210->pshadow[frame]; + unsigned usecs = 0; + struct fotg210_qh_hw *hw; while (q->ptr) { switch (hc32_to_cpu(fotg210, Q_NEXT_TYPE(fotg210, *hw_p))) { @@ -3526,12 +3405,10 @@ periodic_usecs(struct fotg210_hcd *fotg210, unsigned frame, unsigned uframe) } if (usecs > fotg210->uframe_periodic_max) fotg210_err(fotg210, "uframe %d sched overrun: %d usecs\n", - frame * 8 + uframe, usecs); + frame * 8 + uframe, usecs); return usecs; } -/*-------------------------------------------------------------------------*/ - static int same_tt(struct usb_device *dev1, struct usb_device *dev2) { if (!dev1->tt || !dev2->tt) @@ -3548,13 +3425,8 @@ static int same_tt(struct usb_device *dev1, struct usb_device *dev2) * for a periodic transfer starting at the specified frame, using * all the uframes in the mask. */ -static int tt_no_collision( - struct fotg210_hcd *fotg210, - unsigned period, - struct usb_device *dev, - unsigned frame, - u32 uf_mask -) +static int tt_no_collision(struct fotg210_hcd *fotg210, unsigned period, + struct usb_device *dev, unsigned frame, u32 uf_mask) { if (period == 0) /* error */ return 0; @@ -3564,9 +3436,9 @@ static int tt_no_collision( * calling convention doesn't make that distinction. */ for (; frame < fotg210->periodic_size; frame += period) { - union fotg210_shadow here; - __hc32 type; - struct fotg210_qh_hw *hw; + union fotg210_shadow here; + __hc32 type; + struct fotg210_qh_hw *hw; here = fotg210->pshadow[frame]; type = Q_NEXT_TYPE(fotg210, fotg210->periodic[frame]); @@ -3579,7 +3451,7 @@ static int tt_no_collision( case Q_TYPE_QH: hw = here.qh->hw; if (same_tt(dev, here.qh->dev)) { - u32 mask; + u32 mask; mask = hc32_to_cpu(fotg210, hw->hw_info2); @@ -3594,8 +3466,8 @@ static int tt_no_collision( /* case Q_TYPE_FSTN: */ default: fotg210_dbg(fotg210, - "periodic frame %d bogus type %d\n", - frame, type); + "periodic frame %d bogus type %d\n", + frame, type); } /* collision or error */ @@ -3607,8 +3479,6 @@ static int tt_no_collision( return 1; } -/*-------------------------------------------------------------------------*/ - static void enable_periodic(struct fotg210_hcd *fotg210) { if (fotg210->periodic_count++) @@ -3632,8 +3502,6 @@ static void disable_periodic(struct fotg210_hcd *fotg210) fotg210_poll_PSS(fotg210); } -/*-------------------------------------------------------------------------*/ - /* periodic schedule slots have iso tds (normal or split) first, then a * sparse tree for active interrupt transfers. * @@ -3642,24 +3510,24 @@ static void disable_periodic(struct fotg210_hcd *fotg210) */ static void qh_link_periodic(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) { - unsigned i; - unsigned period = qh->period; + unsigned i; + unsigned period = qh->period; dev_dbg(&qh->dev->dev, - "link qh%d-%04x/%p start %d [%d/%d us]\n", - period, hc32_to_cpup(fotg210, &qh->hw->hw_info2) - & (QH_CMASK | QH_SMASK), - qh, qh->start, qh->usecs, qh->c_usecs); + "link qh%d-%04x/%p start %d [%d/%d us]\n", period, + hc32_to_cpup(fotg210, &qh->hw->hw_info2) & + (QH_CMASK | QH_SMASK), qh, qh->start, qh->usecs, + qh->c_usecs); /* high bandwidth, or otherwise every microframe */ if (period == 0) period = 1; for (i = qh->start; i < fotg210->periodic_size; i += period) { - union fotg210_shadow *prev = &fotg210->pshadow[i]; - __hc32 *hw_p = &fotg210->periodic[i]; - union fotg210_shadow here = *prev; - __hc32 type = 0; + union fotg210_shadow *prev = &fotg210->pshadow[i]; + __hc32 *hw_p = &fotg210->periodic[i]; + union fotg210_shadow here = *prev; + __hc32 type = 0; /* skip the iso nodes at list head */ while (here.ptr) { @@ -3707,10 +3575,10 @@ static void qh_link_periodic(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) } static void qh_unlink_periodic(struct fotg210_hcd *fotg210, - struct fotg210_qh *qh) + struct fotg210_qh *qh) { - unsigned i; - unsigned period; + unsigned i; + unsigned period; /* * If qh is for a low/full-speed device, simply unlinking it @@ -3741,10 +3609,10 @@ static void qh_unlink_periodic(struct fotg210_hcd *fotg210, : (qh->usecs * 8); dev_dbg(&qh->dev->dev, - "unlink qh%d-%04x/%p start %d [%d/%d us]\n", - qh->period, - hc32_to_cpup(fotg210, &qh->hw->hw_info2) & - (QH_CMASK | QH_SMASK), qh, qh->start, qh->usecs, qh->c_usecs); + "unlink qh%d-%04x/%p start %d [%d/%d us]\n", + qh->period, hc32_to_cpup(fotg210, &qh->hw->hw_info2) & + (QH_CMASK | QH_SMASK), qh, qh->start, qh->usecs, + qh->c_usecs); /* qh->qh_next still "live" to HC */ qh->qh_state = QH_STATE_UNLINK; @@ -3757,7 +3625,7 @@ static void qh_unlink_periodic(struct fotg210_hcd *fotg210, } static void start_unlink_intr(struct fotg210_hcd *fotg210, - struct fotg210_qh *qh) + struct fotg210_qh *qh) { /* If the QH isn't linked then there's nothing we can do * unless we were called during a giveback, in which case @@ -3794,15 +3662,15 @@ static void start_unlink_intr(struct fotg210_hcd *fotg210, fotg210_handle_intr_unlinks(fotg210); else if (fotg210->intr_unlink == qh) { fotg210_enable_event(fotg210, FOTG210_HRTIMER_UNLINK_INTR, - true); + true); ++fotg210->intr_unlink_cycle; } } static void end_unlink_intr(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) { - struct fotg210_qh_hw *hw = qh->hw; - int rc; + struct fotg210_qh_hw *hw = qh->hw; + int rc; qh->qh_state = QH_STATE_IDLE; hw->hw_next = FOTG210_LIST_END(fotg210); @@ -3811,7 +3679,7 @@ static void end_unlink_intr(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) /* reschedule QH iff another request is queued */ if (!list_empty(&qh->qtd_list) && - fotg210->rh_state == FOTG210_RH_RUNNING) { + fotg210->rh_state == FOTG210_RH_RUNNING) { rc = qh_schedule(fotg210, qh); /* An error here likely indicates handshake failure @@ -3830,16 +3698,10 @@ static void end_unlink_intr(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) disable_periodic(fotg210); } -/*-------------------------------------------------------------------------*/ - -static int check_period( - struct fotg210_hcd *fotg210, - unsigned frame, - unsigned uframe, - unsigned period, - unsigned usecs -) { - int claimed; +static int check_period(struct fotg210_hcd *fotg210, unsigned frame, + unsigned uframe, unsigned period, unsigned usecs) +{ + int claimed; /* complete split running into next frame? * given FSTN support, we could sometimes check... @@ -3857,7 +3719,7 @@ static int check_period( do { for (uframe = 0; uframe < 7; uframe++) { claimed = periodic_usecs(fotg210, frame, - uframe); + uframe); if (claimed > usecs) return 0; } @@ -3876,16 +3738,11 @@ static int check_period( return 1; } -static int check_intr_schedule( - struct fotg210_hcd *fotg210, - unsigned frame, - unsigned uframe, - const struct fotg210_qh *qh, - __hc32 *c_maskp -) +static int check_intr_schedule(struct fotg210_hcd *fotg210, unsigned frame, + unsigned uframe, const struct fotg210_qh *qh, __hc32 *c_maskp) { - int retval = -ENOSPC; - u8 mask = 0; + int retval = -ENOSPC; + u8 mask = 0; if (qh->c_usecs && uframe >= 6) /* FSTN territory? */ goto done; @@ -3911,10 +3768,10 @@ static int check_intr_schedule( mask |= 1 << uframe; if (tt_no_collision(fotg210, qh->period, qh->dev, frame, mask)) { if (!check_period(fotg210, frame, uframe + qh->gap_uf + 1, - qh->period, qh->c_usecs)) + qh->period, qh->c_usecs)) goto done; if (!check_period(fotg210, frame, uframe + qh->gap_uf, - qh->period, qh->c_usecs)) + qh->period, qh->c_usecs)) goto done; retval = 0; } @@ -3927,11 +3784,11 @@ done: */ static int qh_schedule(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) { - int status; - unsigned uframe; - __hc32 c_mask; - unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */ - struct fotg210_qh_hw *hw = qh->hw; + int status; + unsigned uframe; + __hc32 c_mask; + unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */ + struct fotg210_qh_hw *hw = qh->hw; qh_refresh(fotg210, qh); hw->hw_next = FOTG210_LIST_END(fotg210); @@ -3954,7 +3811,7 @@ static int qh_schedule(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) if (status) { /* "normal" case, uframing flexible except with splits */ if (qh->period) { - int i; + int i; for (i = qh->period; status && i > 0; --i) { frame = ++fotg210->random_frame % qh->period; @@ -3971,7 +3828,7 @@ static int qh_schedule(struct fotg210_hcd *fotg210, struct fotg210_qh *qh) } else { frame = 0; status = check_intr_schedule(fotg210, 0, 0, qh, - &c_mask); + &c_mask); } if (status) goto done; @@ -3992,17 +3849,14 @@ done: return status; } -static int intr_submit( - struct fotg210_hcd *fotg210, - struct urb *urb, - struct list_head *qtd_list, - gfp_t mem_flags -) { - unsigned epnum; - unsigned long flags; - struct fotg210_qh *qh; - int status; - struct list_head empty; +static int intr_submit(struct fotg210_hcd *fotg210, struct urb *urb, + struct list_head *qtd_list, gfp_t mem_flags) +{ + unsigned epnum; + unsigned long flags; + struct fotg210_qh *qh; + int status; + struct list_head empty; /* get endpoint and transfer/schedule data */ epnum = urb->ep->desc.bEndpointAddress; @@ -4050,11 +3904,11 @@ done_not_linked: static void scan_intr(struct fotg210_hcd *fotg210) { - struct fotg210_qh *qh; + struct fotg210_qh *qh; list_for_each_entry_safe(qh, fotg210->qh_scan_next, - &fotg210->intr_qh_list, intr_node) { - rescan: + &fotg210->intr_qh_list, intr_node) { +rescan: /* clean any finished work for this qh */ if (!list_empty(&qh->qtd_list)) { int temp; @@ -4069,7 +3923,7 @@ static void scan_intr(struct fotg210_hcd *fotg210) temp = qh_completions(fotg210, qh); if (unlikely(qh->needs_rescan || (list_empty(&qh->qtd_list) && - qh->qh_state == QH_STATE_LINKED))) + qh->qh_state == QH_STATE_LINKED))) start_unlink_intr(fotg210, qh); else if (temp != 0) goto rescan; @@ -4077,12 +3931,9 @@ static void scan_intr(struct fotg210_hcd *fotg210) } } -/*-------------------------------------------------------------------------*/ - /* fotg210_iso_stream ops work with both ITD and SITD */ -static struct fotg210_iso_stream * -iso_stream_alloc(gfp_t mem_flags) +static struct fotg210_iso_stream *iso_stream_alloc(gfp_t mem_flags) { struct fotg210_iso_stream *stream; @@ -4095,20 +3946,15 @@ iso_stream_alloc(gfp_t mem_flags) return stream; } -static void -iso_stream_init( - struct fotg210_hcd *fotg210, - struct fotg210_iso_stream *stream, - struct usb_device *dev, - int pipe, - unsigned interval -) +static void iso_stream_init(struct fotg210_hcd *fotg210, + struct fotg210_iso_stream *stream, struct usb_device *dev, + int pipe, unsigned interval) { - u32 buf1; - unsigned epnum, maxp; - int is_input; - long bandwidth; - unsigned multi; + u32 buf1; + unsigned epnum, maxp; + int is_input; + long bandwidth; + unsigned multi; /* * this might be a "high bandwidth" highspeed endpoint, @@ -4153,13 +3999,13 @@ iso_stream_init( stream->maxp = maxp; } -static struct fotg210_iso_stream * -iso_stream_find(struct fotg210_hcd *fotg210, struct urb *urb) +static struct fotg210_iso_stream *iso_stream_find(struct fotg210_hcd *fotg210, + struct urb *urb) { - unsigned epnum; - struct fotg210_iso_stream *stream; + unsigned epnum; + struct fotg210_iso_stream *stream; struct usb_host_endpoint *ep; - unsigned long flags; + unsigned long flags; epnum = usb_pipeendpoint(urb->pipe); if (usb_pipein(urb->pipe)) @@ -4182,8 +4028,8 @@ iso_stream_find(struct fotg210_hcd *fotg210, struct urb *urb) /* if dev->ep[epnum] is a QH, hw is set */ } else if (unlikely(stream->hw != NULL)) { fotg210_dbg(fotg210, "dev %s ep%d%s, not iso??\n", - urb->dev->devpath, epnum, - usb_pipein(urb->pipe) ? "in" : "out"); + urb->dev->devpath, epnum, + usb_pipein(urb->pipe) ? "in" : "out"); stream = NULL; } @@ -4191,15 +4037,13 @@ iso_stream_find(struct fotg210_hcd *fotg210, struct urb *urb) return stream; } -/*-------------------------------------------------------------------------*/ - /* fotg210_iso_sched ops can be ITD-only or SITD-only */ -static struct fotg210_iso_sched * -iso_sched_alloc(unsigned packets, gfp_t mem_flags) +static struct fotg210_iso_sched *iso_sched_alloc(unsigned packets, + gfp_t mem_flags) { - struct fotg210_iso_sched *iso_sched; - int size = sizeof(*iso_sched); + struct fotg210_iso_sched *iso_sched; + int size = sizeof(*iso_sched); size += packets * sizeof(struct fotg210_iso_packet); iso_sched = kzalloc(size, mem_flags); @@ -4209,16 +4053,12 @@ iso_sched_alloc(unsigned packets, gfp_t mem_flags) return iso_sched; } -static inline void -itd_sched_init( - struct fotg210_hcd *fotg210, - struct fotg210_iso_sched *iso_sched, - struct fotg210_iso_stream *stream, - struct urb *urb -) +static inline void itd_sched_init(struct fotg210_hcd *fotg210, + struct fotg210_iso_sched *iso_sched, + struct fotg210_iso_stream *stream, struct urb *urb) { - unsigned i; - dma_addr_t dma = urb->transfer_dma; + unsigned i; + dma_addr_t dma = urb->transfer_dma; /* how many uframes are needed for these transfers */ iso_sched->span = urb->number_of_packets * stream->interval; @@ -4227,10 +4067,10 @@ itd_sched_init( * when we fit new itds into the schedule. */ for (i = 0; i < urb->number_of_packets; i++) { - struct fotg210_iso_packet *uframe = &iso_sched->packet[i]; - unsigned length; - dma_addr_t buf; - u32 trans; + struct fotg210_iso_packet *uframe = &iso_sched->packet[i]; + unsigned length; + dma_addr_t buf; + u32 trans; length = urb->iso_frame_desc[i].length; buf = dma + urb->iso_frame_desc[i].offset; @@ -4251,11 +4091,8 @@ itd_sched_init( } } -static void -iso_sched_free( - struct fotg210_iso_stream *stream, - struct fotg210_iso_sched *iso_sched -) +static void iso_sched_free(struct fotg210_iso_stream *stream, + struct fotg210_iso_sched *iso_sched) { if (!iso_sched) return; @@ -4264,20 +4101,15 @@ iso_sched_free( kfree(iso_sched); } -static int -itd_urb_transaction( - struct fotg210_iso_stream *stream, - struct fotg210_hcd *fotg210, - struct urb *urb, - gfp_t mem_flags -) +static int itd_urb_transaction(struct fotg210_iso_stream *stream, + struct fotg210_hcd *fotg210, struct urb *urb, gfp_t mem_flags) { - struct fotg210_itd *itd; - dma_addr_t itd_dma; - int i; - unsigned num_itds; - struct fotg210_iso_sched *sched; - unsigned long flags; + struct fotg210_itd *itd; + dma_addr_t itd_dma; + int i; + unsigned num_itds; + struct fotg210_iso_sched *sched; + unsigned long flags; sched = iso_sched_alloc(urb->number_of_packets, mem_flags); if (unlikely(sched == NULL)) @@ -4306,7 +4138,7 @@ itd_urb_transaction( list_del(&itd->itd_list); itd_dma = itd->itd_dma; } else { - alloc_itd: +alloc_itd: spin_unlock_irqrestore(&fotg210->lock, flags); itd = dma_pool_alloc(fotg210->itd_pool, mem_flags, &itd_dma); @@ -4330,16 +4162,8 @@ itd_urb_transaction( return 0; } -/*-------------------------------------------------------------------------*/ - -static inline int -itd_slot_ok( - struct fotg210_hcd *fotg210, - u32 mod, - u32 uframe, - u8 usecs, - u32 period -) +static inline int itd_slot_ok(struct fotg210_hcd *fotg210, u32 mod, u32 uframe, + u8 usecs, u32 period) { uframe %= period; do { @@ -4354,8 +4178,7 @@ itd_slot_ok( return 1; } -/* - * This scheduler plans almost as far into the future as it has actual +/* This scheduler plans almost as far into the future as it has actual * periodic schedule slots. (Affected by TUNE_FLS, which defaults to * "as small as possible" to be cache-friendlier.) That limits the size * transfers you can stream reliably; avoid more than 64 msec per urb. @@ -4365,19 +4188,15 @@ itd_slot_ok( * given FOTG210_TUNE_FLS and the slop). Or, write a smarter scheduler! */ -#define SCHEDULE_SLOP 80 /* microframes */ +#define SCHEDULE_SLOP 80 /* microframes */ -static int -iso_stream_schedule( - struct fotg210_hcd *fotg210, - struct urb *urb, - struct fotg210_iso_stream *stream -) +static int iso_stream_schedule(struct fotg210_hcd *fotg210, struct urb *urb, + struct fotg210_iso_stream *stream) { - u32 now, next, start, period, span; - int status; - unsigned mod = fotg210->periodic_size << 3; - struct fotg210_iso_sched *sched = urb->hcpriv; + u32 now, next, start, period, span; + int status; + unsigned mod = fotg210->periodic_size << 3; + struct fotg210_iso_sched *sched = urb->hcpriv; period = urb->interval; span = sched->span; @@ -4396,7 +4215,7 @@ iso_stream_schedule( * slot in the schedule, implicitly assuming URB_ISO_ASAP. */ if (likely(!list_empty(&stream->td_list))) { - u32 excess; + u32 excess; /* For high speed devices, allow scheduling within the * isochronous scheduling threshold. For full speed devices @@ -4435,6 +4254,7 @@ iso_stream_schedule( */ else { int done = 0; + start = SCHEDULE_SLOP + (now & ~0x07); /* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */ @@ -4457,15 +4277,15 @@ iso_stream_schedule( /* no room in the schedule */ if (!done) { fotg210_dbg(fotg210, "iso resched full %p (now %d max %d)\n", - urb, now, now + mod); + urb, now, now + mod); status = -ENOSPC; goto fail; } } /* Tried to schedule too far into the future? */ - if (unlikely(start - now + span - period - >= mod - 2 * SCHEDULE_SLOP)) { + if (unlikely(start - now + span - period >= + mod - 2 * SCHEDULE_SLOP)) { fotg210_dbg(fotg210, "request %p would overflow (%d+%d >= %d)\n", urb, start - now, span - period, mod - 2 * SCHEDULE_SLOP); @@ -4485,17 +4305,14 @@ iso_stream_schedule( fotg210->next_frame = now >> 3; return 0; - fail: +fail: iso_sched_free(stream, sched); urb->hcpriv = NULL; return status; } -/*-------------------------------------------------------------------------*/ - -static inline void -itd_init(struct fotg210_hcd *fotg210, struct fotg210_iso_stream *stream, - struct fotg210_itd *itd) +static inline void itd_init(struct fotg210_hcd *fotg210, + struct fotg210_iso_stream *stream, struct fotg210_itd *itd) { int i; @@ -4511,17 +4328,12 @@ itd_init(struct fotg210_hcd *fotg210, struct fotg210_iso_stream *stream, /* All other fields are filled when scheduling */ } -static inline void -itd_patch( - struct fotg210_hcd *fotg210, - struct fotg210_itd *itd, - struct fotg210_iso_sched *iso_sched, - unsigned index, - u16 uframe -) +static inline void itd_patch(struct fotg210_hcd *fotg210, + struct fotg210_itd *itd, struct fotg210_iso_sched *iso_sched, + unsigned index, u16 uframe) { - struct fotg210_iso_packet *uf = &iso_sched->packet[index]; - unsigned pg = itd->pg; + struct fotg210_iso_packet *uf = &iso_sched->packet[index]; + unsigned pg = itd->pg; uframe &= 0x07; itd->index[uframe] = index; @@ -4533,7 +4345,7 @@ itd_patch( /* iso_frame_desc[].offset must be strictly increasing */ if (unlikely(uf->cross)) { - u64 bufp = uf->bufp + 4096; + u64 bufp = uf->bufp + 4096; itd->pg = ++pg; itd->hw_bufp[pg] |= cpu_to_hc32(fotg210, bufp & ~(u32)0); @@ -4541,13 +4353,13 @@ itd_patch( } } -static inline void -itd_link(struct fotg210_hcd *fotg210, unsigned frame, struct fotg210_itd *itd) +static inline void itd_link(struct fotg210_hcd *fotg210, unsigned frame, + struct fotg210_itd *itd) { - union fotg210_shadow *prev = &fotg210->pshadow[frame]; - __hc32 *hw_p = &fotg210->periodic[frame]; - union fotg210_shadow here = *prev; - __hc32 type = 0; + union fotg210_shadow *prev = &fotg210->pshadow[frame]; + __hc32 *hw_p = &fotg210->periodic[frame]; + union fotg210_shadow here = *prev; + __hc32 type = 0; /* skip any iso nodes which might belong to previous microframes */ while (here.ptr) { @@ -4568,17 +4380,13 @@ itd_link(struct fotg210_hcd *fotg210, unsigned frame, struct fotg210_itd *itd) } /* fit urb's itds into the selected schedule slot; activate as needed */ -static void itd_link_urb( - struct fotg210_hcd *fotg210, - struct urb *urb, - unsigned mod, - struct fotg210_iso_stream *stream -) -{ - int packet; - unsigned next_uframe, uframe, frame; - struct fotg210_iso_sched *iso_sched = urb->hcpriv; - struct fotg210_itd *itd; +static void itd_link_urb(struct fotg210_hcd *fotg210, struct urb *urb, + unsigned mod, struct fotg210_iso_stream *stream) +{ + int packet; + unsigned next_uframe, uframe, frame; + struct fotg210_iso_sched *iso_sched = urb->hcpriv; + struct fotg210_itd *itd; next_uframe = stream->next_uframe & (mod - 1); @@ -4621,7 +4429,7 @@ static void itd_link_urb( if (((next_uframe >> 3) != frame) || packet == urb->number_of_packets) { itd_link(fotg210, frame & (fotg210->periodic_size - 1), - itd); + itd); itd = NULL; } } @@ -4635,8 +4443,8 @@ static void itd_link_urb( enable_periodic(fotg210); } -#define ISO_ERRS (FOTG210_ISOC_BUF_ERR | FOTG210_ISOC_BABBLE |\ - FOTG210_ISOC_XACTERR) +#define ISO_ERRS (FOTG210_ISOC_BUF_ERR | FOTG210_ISOC_BABBLE |\ + FOTG210_ISOC_XACTERR) /* Process and recycle a completed ITD. Return true iff its urb completed, * and hence its completion callback probably added things to the hardware @@ -4650,14 +4458,14 @@ static void itd_link_urb( */ static bool itd_complete(struct fotg210_hcd *fotg210, struct fotg210_itd *itd) { - struct urb *urb = itd->urb; - struct usb_iso_packet_descriptor *desc; - u32 t; - unsigned uframe; - int urb_index = -1; - struct fotg210_iso_stream *stream = itd->stream; - struct usb_device *dev; - bool retval = false; + struct urb *urb = itd->urb; + struct usb_iso_packet_descriptor *desc; + u32 t; + unsigned uframe; + int urb_index = -1; + struct fotg210_iso_stream *stream = itd->stream; + struct usb_device *dev; + bool retval = false; /* for each uframe with a packet */ for (uframe = 0; uframe < 8; uframe++) { @@ -4702,8 +4510,8 @@ static bool itd_complete(struct fotg210_hcd *fotg210, struct fotg210_itd *itd) goto done; /* ASSERT: it's really the last itd for this urb - list_for_each_entry (itd, &stream->td_list, itd_list) - BUG_ON (itd->urb == urb); + * list_for_each_entry (itd, &stream->td_list, itd_list) + * BUG_ON (itd->urb == urb); */ /* give urb back to the driver; completion often (re)submits */ @@ -4740,14 +4548,12 @@ done: return retval; } -/*-------------------------------------------------------------------------*/ - static int itd_submit(struct fotg210_hcd *fotg210, struct urb *urb, - gfp_t mem_flags) + gfp_t mem_flags) { - int status = -EINVAL; - unsigned long flags; - struct fotg210_iso_stream *stream; + int status = -EINVAL; + unsigned long flags; + struct fotg210_iso_stream *stream; /* Get iso_stream head */ stream = iso_stream_find(fotg210, urb); @@ -4756,22 +4562,22 @@ static int itd_submit(struct fotg210_hcd *fotg210, struct urb *urb, return -ENOMEM; } if (unlikely(urb->interval != stream->interval && - fotg210_port_speed(fotg210, 0) == - USB_PORT_STAT_HIGH_SPEED)) { - fotg210_dbg(fotg210, "can't change iso interval %d --> %d\n", + fotg210_port_speed(fotg210, 0) == + USB_PORT_STAT_HIGH_SPEED)) { + fotg210_dbg(fotg210, "can't change iso interval %d --> %d\n", stream->interval, urb->interval); - goto done; + goto done; } #ifdef FOTG210_URB_TRACE fotg210_dbg(fotg210, - "%s %s urb %p ep%d%s len %d, %d pkts %d uframes[%p]\n", - __func__, urb->dev->devpath, urb, - usb_pipeendpoint(urb->pipe), - usb_pipein(urb->pipe) ? "in" : "out", - urb->transfer_buffer_length, - urb->number_of_packets, urb->interval, - stream); + "%s %s urb %p ep%d%s len %d, %d pkts %d uframes[%p]\n", + __func__, urb->dev->devpath, urb, + usb_pipeendpoint(urb->pipe), + usb_pipein(urb->pipe) ? "in" : "out", + urb->transfer_buffer_length, + urb->number_of_packets, urb->interval, + stream); #endif /* allocate ITDs w/o locking anything */ @@ -4795,19 +4601,87 @@ static int itd_submit(struct fotg210_hcd *fotg210, struct urb *urb, itd_link_urb(fotg210, urb, fotg210->periodic_size << 3, stream); else usb_hcd_unlink_urb_from_ep(fotg210_to_hcd(fotg210), urb); - done_not_linked: +done_not_linked: spin_unlock_irqrestore(&fotg210->lock, flags); - done: +done: return status; } -/*-------------------------------------------------------------------------*/ +static inline int scan_frame_queue(struct fotg210_hcd *fotg210, unsigned frame, + unsigned now_frame, bool live) +{ + unsigned uf; + bool modified; + union fotg210_shadow q, *q_p; + __hc32 type, *hw_p; + + /* scan each element in frame's queue for completions */ + q_p = &fotg210->pshadow[frame]; + hw_p = &fotg210->periodic[frame]; + q.ptr = q_p->ptr; + type = Q_NEXT_TYPE(fotg210, *hw_p); + modified = false; + + while (q.ptr) { + switch (hc32_to_cpu(fotg210, type)) { + case Q_TYPE_ITD: + /* If this ITD is still active, leave it for + * later processing ... check the next entry. + * No need to check for activity unless the + * frame is current. + */ + if (frame == now_frame && live) { + rmb(); + for (uf = 0; uf < 8; uf++) { + if (q.itd->hw_transaction[uf] & + ITD_ACTIVE(fotg210)) + break; + } + if (uf < 8) { + q_p = &q.itd->itd_next; + hw_p = &q.itd->hw_next; + type = Q_NEXT_TYPE(fotg210, + q.itd->hw_next); + q = *q_p; + break; + } + } + + /* Take finished ITDs out of the schedule + * and process them: recycle, maybe report + * URB completion. HC won't cache the + * pointer for much longer, if at all. + */ + *q_p = q.itd->itd_next; + *hw_p = q.itd->hw_next; + type = Q_NEXT_TYPE(fotg210, q.itd->hw_next); + wmb(); + modified = itd_complete(fotg210, q.itd); + q = *q_p; + break; + default: + fotg210_dbg(fotg210, "corrupt type %d frame %d shadow %p\n", + type, frame, q.ptr); + /* FALL THROUGH */ + case Q_TYPE_QH: + case Q_TYPE_FSTN: + /* End of the iTDs and siTDs */ + q.ptr = NULL; + break; + } + + /* assume completion callbacks modify the queue */ + if (unlikely(modified && fotg210->isoc_count > 0)) + return -EINVAL; + } + return 0; +} static void scan_isoc(struct fotg210_hcd *fotg210) { - unsigned uf, now_frame, frame; - unsigned fmask = fotg210->periodic_size - 1; - bool modified, live; + unsigned uf, now_frame, frame, ret; + unsigned fmask = fotg210->periodic_size - 1; + bool live; /* * When running, scan from last scan point up to "now" @@ -4826,69 +4700,10 @@ static void scan_isoc(struct fotg210_hcd *fotg210) frame = fotg210->next_frame; for (;;) { - union fotg210_shadow q, *q_p; - __hc32 type, *hw_p; - -restart: - /* scan each element in frame's queue for completions */ - q_p = &fotg210->pshadow[frame]; - hw_p = &fotg210->periodic[frame]; - q.ptr = q_p->ptr; - type = Q_NEXT_TYPE(fotg210, *hw_p); - modified = false; - - while (q.ptr != NULL) { - switch (hc32_to_cpu(fotg210, type)) { - case Q_TYPE_ITD: - /* If this ITD is still active, leave it for - * later processing ... check the next entry. - * No need to check for activity unless the - * frame is current. - */ - if (frame == now_frame && live) { - rmb(); - for (uf = 0; uf < 8; uf++) { - if (q.itd->hw_transaction[uf] & - ITD_ACTIVE(fotg210)) - break; - } - if (uf < 8) { - q_p = &q.itd->itd_next; - hw_p = &q.itd->hw_next; - type = Q_NEXT_TYPE(fotg210, - q.itd->hw_next); - q = *q_p; - break; - } - } - - /* Take finished ITDs out of the schedule - * and process them: recycle, maybe report - * URB completion. HC won't cache the - * pointer for much longer, if at all. - */ - *q_p = q.itd->itd_next; - *hw_p = q.itd->hw_next; - type = Q_NEXT_TYPE(fotg210, q.itd->hw_next); - wmb(); - modified = itd_complete(fotg210, q.itd); - q = *q_p; - break; - default: - fotg210_dbg(fotg210, "corrupt type %d frame %d shadow %p\n", - type, frame, q.ptr); - /* FALL THROUGH */ - case Q_TYPE_QH: - case Q_TYPE_FSTN: - /* End of the iTDs and siTDs */ - q.ptr = NULL; - break; - } - - /* assume completion callbacks modify the queue */ - if (unlikely(modified && fotg210->isoc_count > 0)) - goto restart; - } + ret = 1; + while (ret != 0) + ret = scan_frame_queue(fotg210, frame, + now_frame, live); /* Stop when we have reached the current frame */ if (frame == now_frame) @@ -4897,16 +4712,14 @@ restart: } fotg210->next_frame = now_frame; } -/*-------------------------------------------------------------------------*/ -/* - * Display / Set uframe_periodic_max + +/* Display / Set uframe_periodic_max */ static ssize_t show_uframe_periodic_max(struct device *dev, - struct device_attribute *attr, - char *buf) + struct device_attribute *attr, char *buf) { - struct fotg210_hcd *fotg210; - int n; + struct fotg210_hcd *fotg210; + int n; fotg210 = hcd_to_fotg210(bus_to_hcd(dev_get_drvdata(dev))); n = scnprintf(buf, PAGE_SIZE, "%d\n", fotg210->uframe_periodic_max); @@ -4915,15 +4728,14 @@ static ssize_t show_uframe_periodic_max(struct device *dev, static ssize_t store_uframe_periodic_max(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) + struct device_attribute *attr, const char *buf, size_t count) { - struct fotg210_hcd *fotg210; - unsigned uframe_periodic_max; - unsigned frame, uframe; - unsigned short allocated_max; - unsigned long flags; - ssize_t ret; + struct fotg210_hcd *fotg210; + unsigned uframe_periodic_max; + unsigned frame, uframe; + unsigned short allocated_max; + unsigned long flags; + ssize_t ret; fotg210 = hcd_to_fotg210(bus_to_hcd(dev_get_drvdata(dev))); if (kstrtouint(buf, 0, &uframe_periodic_max) < 0) @@ -4931,7 +4743,7 @@ static ssize_t store_uframe_periodic_max(struct device *dev, if (uframe_periodic_max < 100 || uframe_periodic_max >= 125) { fotg210_info(fotg210, "rejecting invalid request for uframe_periodic_max=%u\n", - uframe_periodic_max); + uframe_periodic_max); return -EINVAL; } @@ -4954,22 +4766,22 @@ static ssize_t store_uframe_periodic_max(struct device *dev, for (frame = 0; frame < fotg210->periodic_size; ++frame) for (uframe = 0; uframe < 7; ++uframe) allocated_max = max(allocated_max, - periodic_usecs(fotg210, frame, uframe)); + periodic_usecs(fotg210, frame, + uframe)); if (allocated_max > uframe_periodic_max) { fotg210_info(fotg210, - "cannot decrease uframe_periodic_max because " - "periodic bandwidth is already allocated " - "(%u > %u)\n", - allocated_max, uframe_periodic_max); + "cannot decrease uframe_periodic_max because periodic bandwidth is already allocated (%u > %u)\n", + allocated_max, uframe_periodic_max); goto out_unlock; } } /* increasing is always ok */ - fotg210_info(fotg210, "setting max periodic bandwidth to %u%% (== %u usec/uframe)\n", - 100 * uframe_periodic_max/125, uframe_periodic_max); + fotg210_info(fotg210, + "setting max periodic bandwidth to %u%% (== %u usec/uframe)\n", + 100 * uframe_periodic_max/125, uframe_periodic_max); if (uframe_periodic_max != 100) fotg210_warn(fotg210, "max periodic bandwidth set is non-standard\n"); @@ -4987,8 +4799,8 @@ static DEVICE_ATTR(uframe_periodic_max, 0644, show_uframe_periodic_max, static inline int create_sysfs_files(struct fotg210_hcd *fotg210) { - struct device *controller = fotg210_to_hcd(fotg210)->self.controller; - int i = 0; + struct device *controller = fotg210_to_hcd(fotg210)->self.controller; + int i = 0; if (i) goto out; @@ -5000,12 +4812,10 @@ out: static inline void remove_sysfs_files(struct fotg210_hcd *fotg210) { - struct device *controller = fotg210_to_hcd(fotg210)->self.controller; + struct device *controller = fotg210_to_hcd(fotg210)->self.controller; device_remove_file(controller, &dev_attr_uframe_periodic_max); } -/*-------------------------------------------------------------------------*/ - /* On some systems, leaving remote wakeup enabled prevents system shutdown. * The firmware seems to think that powering off is a wakeup event! * This routine turns off remote wakeup and everything else, on all ports. @@ -5017,8 +4827,7 @@ static void fotg210_turn_off_all_ports(struct fotg210_hcd *fotg210) fotg210_writel(fotg210, PORT_RWC_BITS, status_reg); } -/* - * Halt HC, turn off all ports, and let the BIOS use the companion controllers. +/* Halt HC, turn off all ports, and let the BIOS use the companion controllers. * Must be called with interrupts enabled and the lock not held. */ static void fotg210_silence_controller(struct fotg210_hcd *fotg210) @@ -5037,7 +4846,7 @@ static void fotg210_silence_controller(struct fotg210_hcd *fotg210) */ static void fotg210_shutdown(struct usb_hcd *hcd) { - struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); spin_lock_irq(&fotg210->lock); fotg210->shutdown = true; @@ -5050,10 +4859,7 @@ static void fotg210_shutdown(struct usb_hcd *hcd) hrtimer_cancel(&fotg210->hrtimer); } -/*-------------------------------------------------------------------------*/ - -/* - * fotg210_work is called from some interrupts, timers, and so on. +/* fotg210_work is called from some interrupts, timers, and so on. * it calls driver completion functions, after dropping fotg210->lock. */ static void fotg210_work(struct fotg210_hcd *fotg210) @@ -5068,7 +4874,7 @@ static void fotg210_work(struct fotg210_hcd *fotg210) } fotg210->scanning = true; - rescan: +rescan: fotg210->need_rescan = false; if (fotg210->async_count) scan_async(fotg210); @@ -5087,12 +4893,11 @@ static void fotg210_work(struct fotg210_hcd *fotg210) turn_on_io_watchdog(fotg210); } -/* - * Called when the fotg210_hcd module is removed. +/* Called when the fotg210_hcd module is removed. */ static void fotg210_stop(struct usb_hcd *hcd) { - struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); fotg210_dbg(fotg210, "stop\n"); @@ -5116,26 +4921,26 @@ static void fotg210_stop(struct usb_hcd *hcd) spin_unlock_irq(&fotg210->lock); fotg210_mem_cleanup(fotg210); -#ifdef FOTG210_STATS +#ifdef FOTG210_STATS fotg210_dbg(fotg210, "irq normal %ld err %ld iaa %ld (lost %ld)\n", - fotg210->stats.normal, fotg210->stats.error, fotg210->stats.iaa, - fotg210->stats.lost_iaa); + fotg210->stats.normal, fotg210->stats.error, + fotg210->stats.iaa, fotg210->stats.lost_iaa); fotg210_dbg(fotg210, "complete %ld unlink %ld\n", - fotg210->stats.complete, fotg210->stats.unlink); + fotg210->stats.complete, fotg210->stats.unlink); #endif dbg_status(fotg210, "fotg210_stop completed", - fotg210_readl(fotg210, &fotg210->regs->status)); + fotg210_readl(fotg210, &fotg210->regs->status)); } /* one-time init, only for memory state */ static int hcd_fotg210_init(struct usb_hcd *hcd) { - struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); - u32 temp; - int retval; - u32 hcc_params; - struct fotg210_qh_hw *hw; + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + u32 temp; + int retval; + u32 hcc_params; + struct fotg210_qh_hw *hw; spin_lock_init(&fotg210->lock); @@ -5238,18 +5043,18 @@ static int hcd_fotg210_init(struct usb_hcd *hcd) /* start HC running; it's halted, hcd_fotg210_init() has been run (once) */ static int fotg210_run(struct usb_hcd *hcd) { - struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); - u32 temp; - u32 hcc_params; + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + u32 temp; + u32 hcc_params; hcd->uses_new_polling = 1; /* EHCI spec section 4.1 */ fotg210_writel(fotg210, fotg210->periodic_dma, - &fotg210->regs->frame_list); + &fotg210->regs->frame_list); fotg210_writel(fotg210, (u32)fotg210->async->qh_dma, - &fotg210->regs->async_next); + &fotg210->regs->async_next); /* * hcc_params controls whether fotg210->regs->segment must (!!!) @@ -5292,19 +5097,19 @@ static int fotg210_run(struct usb_hcd *hcd) fotg210->rh_state = FOTG210_RH_RUNNING; /* unblock posted writes */ fotg210_readl(fotg210, &fotg210->regs->command); - msleep(5); + usleep_range(5000, 10000); up_write(&ehci_cf_port_reset_rwsem); fotg210->last_periodic_enable = ktime_get_real(); temp = HC_VERSION(fotg210, - fotg210_readl(fotg210, &fotg210->caps->hc_capbase)); + fotg210_readl(fotg210, &fotg210->caps->hc_capbase)); fotg210_info(fotg210, - "USB %x.%x started, EHCI %x.%02x\n", - ((fotg210->sbrn & 0xf0)>>4), (fotg210->sbrn & 0x0f), - temp >> 8, temp & 0xff); + "USB %x.%x started, EHCI %x.%02x\n", + ((fotg210->sbrn & 0xf0) >> 4), (fotg210->sbrn & 0x0f), + temp >> 8, temp & 0xff); fotg210_writel(fotg210, INTR_MASK, - &fotg210->regs->intr_enable); /* Turn On Interrupts */ + &fotg210->regs->intr_enable); /* Turn On Interrupts */ /* GRR this is run-once init(), being done every time the HC starts. * So long as they're part of class devices, we can't do it init() @@ -5322,14 +5127,14 @@ static int fotg210_setup(struct usb_hcd *hcd) int retval; fotg210->regs = (void __iomem *)fotg210->caps + - HC_LENGTH(fotg210, - fotg210_readl(fotg210, &fotg210->caps->hc_capbase)); + HC_LENGTH(fotg210, + fotg210_readl(fotg210, &fotg210->caps->hc_capbase)); dbg_hcs_params(fotg210, "reset"); dbg_hcc_params(fotg210, "reset"); /* cache this readonly data; minimize chip reads */ fotg210->hcs_params = fotg210_readl(fotg210, - &fotg210->caps->hcs_params); + &fotg210->caps->hcs_params); fotg210->sbrn = HCD_USB2; @@ -5347,13 +5152,11 @@ static int fotg210_setup(struct usb_hcd *hcd) return 0; } -/*-------------------------------------------------------------------------*/ - static irqreturn_t fotg210_irq(struct usb_hcd *hcd) { - struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); - u32 status, masked_status, pcd_status = 0, cmd; - int bh; + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + u32 status, masked_status, pcd_status = 0, cmd; + int bh; spin_lock(&fotg210->lock); @@ -5373,7 +5176,7 @@ static irqreturn_t fotg210_irq(struct usb_hcd *hcd) /* Shared IRQ? */ if (!masked_status || - unlikely(fotg210->rh_state == FOTG210_RH_HALTED)) { + unlikely(fotg210->rh_state == FOTG210_RH_HALTED)) { spin_unlock(&fotg210->lock); return IRQ_NONE; } @@ -5440,7 +5243,7 @@ static irqreturn_t fotg210_irq(struct usb_hcd *hcd) if (test_bit(0, &fotg210->suspended_ports) && ((pstatus & PORT_RESUME) || - !(pstatus & PORT_SUSPEND)) && + !(pstatus & PORT_SUSPEND)) && (pstatus & PORT_PE) && fotg210->reset_done[0] == 0) { @@ -5469,7 +5272,7 @@ dead: fotg210->rh_state = FOTG210_RH_STOPPING; fotg210->command &= ~(CMD_RUN | CMD_ASE | CMD_PSE); fotg210_writel(fotg210, fotg210->command, - &fotg210->regs->command); + &fotg210->regs->command); fotg210_writel(fotg210, 0, &fotg210->regs->intr_enable); fotg210_handle_controller_death(fotg210); @@ -5485,10 +5288,7 @@ dead: return IRQ_HANDLED; } -/*-------------------------------------------------------------------------*/ - -/* - * non-error returns are a promise to giveback() the urb later +/* non-error returns are a promise to giveback() the urb later * we drop ownership so next owner (or urb unlink) can get it * * urb + dev is in hcd.self.controller.urb_list @@ -5499,13 +5299,11 @@ dead: * NOTE: control, bulk, and interrupt share the same code to append TDs * to a (possibly active) QH, and the same QH scanning code. */ -static int fotg210_urb_enqueue( - struct usb_hcd *hcd, - struct urb *urb, - gfp_t mem_flags -) { - struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); - struct list_head qtd_list; +static int fotg210_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, + gfp_t mem_flags) +{ + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + struct list_head qtd_list; INIT_LIST_HEAD(&qtd_list); @@ -5539,10 +5337,10 @@ static int fotg210_urb_enqueue( static int fotg210_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) { - struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); - struct fotg210_qh *qh; - unsigned long flags; - int rc; + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + struct fotg210_qh *qh; + unsigned long flags; + int rc; spin_lock_irqsave(&fotg210->lock, flags); rc = usb_hcd_check_unlink_urb(hcd, urb, status); @@ -5603,16 +5401,14 @@ done: return rc; } -/*-------------------------------------------------------------------------*/ - /* bulk qh holds the data toggle */ -static void -fotg210_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep) +static void fotg210_endpoint_disable(struct usb_hcd *hcd, + struct usb_host_endpoint *ep) { - struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); - unsigned long flags; - struct fotg210_qh *qh, *tmp; + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + unsigned long flags; + struct fotg210_qh *qh, *tmp; /* ASSERT: any requests/urbs are being unlinked */ /* ASSERT: nobody can be submitting urbs for this any more */ @@ -5627,7 +5423,7 @@ rescan: * accelerate iso completions ... so spin a while. */ if (qh->hw == NULL) { - struct fotg210_iso_stream *stream = ep->hcpriv; + struct fotg210_iso_stream *stream = ep->hcpriv; if (!list_empty(&stream->td_list)) goto idle_timeout; @@ -5671,24 +5467,24 @@ idle_timeout: * that's not our job. just leak this memory. */ fotg210_err(fotg210, "qh %p (#%02x) state %d%s\n", - qh, ep->desc.bEndpointAddress, qh->qh_state, - list_empty(&qh->qtd_list) ? "" : "(has tds)"); + qh, ep->desc.bEndpointAddress, qh->qh_state, + list_empty(&qh->qtd_list) ? "" : "(has tds)"); break; } - done: +done: ep->hcpriv = NULL; spin_unlock_irqrestore(&fotg210->lock, flags); } -static void -fotg210_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) +static void fotg210_endpoint_reset(struct usb_hcd *hcd, + struct usb_host_endpoint *ep) { - struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); - struct fotg210_qh *qh; - int eptype = usb_endpoint_type(&ep->desc); - int epnum = usb_endpoint_num(&ep->desc); - int is_out = usb_endpoint_dir_out(&ep->desc); - unsigned long flags; + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + struct fotg210_qh *qh; + int eptype = usb_endpoint_type(&ep->desc); + int epnum = usb_endpoint_num(&ep->desc); + int is_out = usb_endpoint_dir_out(&ep->desc); + unsigned long flags; if (eptype != USB_ENDPOINT_XFER_BULK && eptype != USB_ENDPOINT_XFER_INT) return; @@ -5723,15 +5519,13 @@ fotg210_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) static int fotg210_get_frame(struct usb_hcd *hcd) { - struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + struct fotg210_hcd *fotg210 = hcd_to_fotg210(hcd); + return (fotg210_read_frame_index(fotg210) >> 3) % fotg210->periodic_size; } -/*-------------------------------------------------------------------------*/ - -/* - * The EHCI in ChipIdea HDRC cannot be a separate module or device, +/* The EHCI in ChipIdea HDRC cannot be a separate module or device, * because its registers (and irq) are shared between host/gadget/otg * functions and in order to facilitate role switching we cannot * give the fotg210 driver exclusive access to those. @@ -5791,7 +5585,7 @@ static void fotg210_init(struct fotg210_hcd *fotg210) u32 value; iowrite32(GMIR_MDEV_INT | GMIR_MOTG_INT | GMIR_INT_POLARITY, - &fotg210->regs->gmir); + &fotg210->regs->gmir); value = ioread32(&fotg210->regs->otgcsr); value &= ~OTGCSR_A_BUS_DROP; @@ -5808,12 +5602,12 @@ static void fotg210_init(struct fotg210_hcd *fotg210) */ static int fotg210_hcd_probe(struct platform_device *pdev) { - struct device *dev = &pdev->dev; - struct usb_hcd *hcd; - struct resource *res; - int irq; - int retval = -ENODEV; - struct fotg210_hcd *fotg210; + struct device *dev = &pdev->dev; + struct usb_hcd *hcd; + struct resource *res; + int irq; + int retval = -ENODEV; + struct fotg210_hcd *fotg210; if (usb_disabled()) return -ENODEV; @@ -5822,9 +5616,8 @@ static int fotg210_hcd_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (!res) { - dev_err(dev, - "Found HC with no IRQ. Check %s setup!\n", - dev_name(dev)); + dev_err(dev, "Found HC with no IRQ. Check %s setup!\n", + dev_name(dev)); return -ENODEV; } @@ -5883,8 +5676,8 @@ fail_create_hcd: */ static int fotg210_hcd_remove(struct platform_device *pdev) { - struct device *dev = &pdev->dev; - struct usb_hcd *hcd = dev_get_drvdata(dev); + struct device *dev = &pdev->dev; + struct usb_hcd *hcd = dev_get_drvdata(dev); if (!hcd) return 0; @@ -5914,12 +5707,12 @@ static int __init fotg210_hcd_init(void) set_bit(USB_EHCI_LOADED, &usb_hcds_loaded); if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) || test_bit(USB_OHCI_LOADED, &usb_hcds_loaded)) - pr_warn(KERN_WARNING "Warning! fotg210_hcd should always be loaded before uhci_hcd and ohci_hcd, not after\n"); + pr_warn("Warning! fotg210_hcd should always be loaded before uhci_hcd and ohci_hcd, not after\n"); pr_debug("%s: block sizes: qh %Zd qtd %Zd itd %Zd\n", - hcd_name, - sizeof(struct fotg210_qh), sizeof(struct fotg210_qtd), - sizeof(struct fotg210_itd)); + hcd_name, sizeof(struct fotg210_qh), + sizeof(struct fotg210_qtd), + sizeof(struct fotg210_itd)); fotg210_debug_root = debugfs_create_dir("fotg210", usb_debug_root); if (!fotg210_debug_root) { @@ -5932,7 +5725,6 @@ static int __init fotg210_hcd_init(void) goto clean; return retval; - platform_driver_unregister(&fotg210_hcd_driver); clean: debugfs_remove(fotg210_debug_root); fotg210_debug_root = NULL; diff --git a/drivers/usb/host/fotg210.h b/drivers/usb/host/fotg210.h index 3bad178..b5cfa7a 100644 --- a/drivers/usb/host/fotg210.h +++ b/drivers/usb/host/fotg210.h @@ -137,19 +137,25 @@ struct fotg210_hcd { /* one per controller */ /* per root hub port */ unsigned long reset_done[FOTG210_MAX_ROOT_PORTS]; - /* bit vectors (one bit per port) */ - unsigned long bus_suspended; /* which ports were - already suspended at the start of a bus suspend */ - unsigned long companion_ports; /* which ports are - dedicated to the companion controller */ - unsigned long owned_ports; /* which ports are - owned by the companion during a bus suspend */ - unsigned long port_c_suspend; /* which ports have - the change-suspend feature turned on */ - unsigned long suspended_ports; /* which ports are - suspended */ - unsigned long resuming_ports; /* which ports have - started to resume */ + /* bit vectors (one bit per port) + * which ports were already suspended at the start of a bus suspend + */ + unsigned long bus_suspended; + + /* which ports are edicated to the companion controller */ + unsigned long companion_ports; + + /* which ports are owned by the companion during a bus suspend */ + unsigned long owned_ports; + + /* which ports have the change-suspend feature turned on */ + unsigned long port_c_suspend; + + /* which ports are suspended */ + unsigned long suspended_ports; + + /* which ports have started to resume */ + unsigned long resuming_ports; /* per-HC memory pools (could be per-bus, but ...) */ struct dma_pool *qh_pool; /* qh per active urb */ @@ -585,10 +591,10 @@ struct fotg210_fstn { /* Prepare the PORTSC wakeup flags during controller suspend/resume */ #define fotg210_prepare_ports_for_controller_suspend(fotg210, do_wakeup) \ - fotg210_adjust_port_wakeup_flags(fotg210, true, do_wakeup); + fotg210_adjust_port_wakeup_flags(fotg210, true, do_wakeup) #define fotg210_prepare_ports_for_controller_resume(fotg210) \ - fotg210_adjust_port_wakeup_flags(fotg210, false, false); + fotg210_adjust_port_wakeup_flags(fotg210, false, false) /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c index 534c4c5..0c38265 100644 --- a/drivers/usb/host/fsl-mph-dr-of.c +++ b/drivers/usb/host/fsl-mph-dr-of.c @@ -351,6 +351,7 @@ static const struct of_device_id fsl_usb2_mph_dr_of_match[] = { #endif {}, }; +MODULE_DEVICE_TABLE(of, fsl_usb2_mph_dr_of_match); static struct platform_driver fsl_usb2_mph_dr_driver = { .driver = { diff --git a/drivers/usb/host/fusbh200-hcd.c b/drivers/usb/host/fusbh200-hcd.c deleted file mode 100644 index 1fd8718a..0000000 --- a/drivers/usb/host/fusbh200-hcd.c +++ /dev/null @@ -1,5894 +0,0 @@ -/* - * Faraday FUSBH200 EHCI-like driver - * - * Copyright (c) 2013 Faraday Technology Corporation - * - * Author: Yuan-Hsin Chen <yhchen@faraday-tech.com> - * Feng-Hsin Chiang <john453@faraday-tech.com> - * Po-Yu Chuang <ratbert.chuang@gmail.com> - * - * Most of code borrowed from the Linux-3.7 EHCI driver - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include <linux/module.h> -#include <linux/device.h> -#include <linux/dmapool.h> -#include <linux/kernel.h> -#include <linux/delay.h> -#include <linux/ioport.h> -#include <linux/sched.h> -#include <linux/vmalloc.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/hrtimer.h> -#include <linux/list.h> -#include <linux/interrupt.h> -#include <linux/usb.h> -#include <linux/usb/hcd.h> -#include <linux/moduleparam.h> -#include <linux/dma-mapping.h> -#include <linux/debugfs.h> -#include <linux/slab.h> -#include <linux/uaccess.h> -#include <linux/platform_device.h> - -#include <asm/byteorder.h> -#include <asm/io.h> -#include <asm/irq.h> -#include <asm/unaligned.h> - -/*-------------------------------------------------------------------------*/ -#define DRIVER_AUTHOR "Yuan-Hsin Chen" -#define DRIVER_DESC "FUSBH200 Host Controller (EHCI) Driver" - -static const char hcd_name [] = "fusbh200_hcd"; - -#undef FUSBH200_URB_TRACE - -/* magic numbers that can affect system performance */ -#define FUSBH200_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ -#define FUSBH200_TUNE_RL_HS 4 /* nak throttle; see 4.9 */ -#define FUSBH200_TUNE_RL_TT 0 -#define FUSBH200_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */ -#define FUSBH200_TUNE_MULT_TT 1 -/* - * Some drivers think it's safe to schedule isochronous transfers more than - * 256 ms into the future (partly as a result of an old bug in the scheduling - * code). In an attempt to avoid trouble, we will use a minimum scheduling - * length of 512 frames instead of 256. - */ -#define FUSBH200_TUNE_FLS 1 /* (medium) 512-frame schedule */ - -/* Initial IRQ latency: faster than hw default */ -static int log2_irq_thresh = 0; // 0 to 6 -module_param (log2_irq_thresh, int, S_IRUGO); -MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes"); - -/* initial park setting: slower than hw default */ -static unsigned park = 0; -module_param (park, uint, S_IRUGO); -MODULE_PARM_DESC (park, "park setting; 1-3 back-to-back async packets"); - -/* for link power management(LPM) feature */ -static unsigned int hird; -module_param(hird, int, S_IRUGO); -MODULE_PARM_DESC(hird, "host initiated resume duration, +1 for each 75us"); - -#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT) - -#include "fusbh200.h" - -/*-------------------------------------------------------------------------*/ - -#define fusbh200_dbg(fusbh200, fmt, args...) \ - dev_dbg (fusbh200_to_hcd(fusbh200)->self.controller , fmt , ## args ) -#define fusbh200_err(fusbh200, fmt, args...) \ - dev_err (fusbh200_to_hcd(fusbh200)->self.controller , fmt , ## args ) -#define fusbh200_info(fusbh200, fmt, args...) \ - dev_info (fusbh200_to_hcd(fusbh200)->self.controller , fmt , ## args ) -#define fusbh200_warn(fusbh200, fmt, args...) \ - dev_warn (fusbh200_to_hcd(fusbh200)->self.controller , fmt , ## args ) - -/* check the values in the HCSPARAMS register - * (host controller _Structural_ parameters) - * see EHCI spec, Table 2-4 for each value - */ -static void dbg_hcs_params (struct fusbh200_hcd *fusbh200, char *label) -{ - u32 params = fusbh200_readl(fusbh200, &fusbh200->caps->hcs_params); - - fusbh200_dbg (fusbh200, - "%s hcs_params 0x%x ports=%d\n", - label, params, - HCS_N_PORTS (params) - ); -} - -/* check the values in the HCCPARAMS register - * (host controller _Capability_ parameters) - * see EHCI Spec, Table 2-5 for each value - * */ -static void dbg_hcc_params (struct fusbh200_hcd *fusbh200, char *label) -{ - u32 params = fusbh200_readl(fusbh200, &fusbh200->caps->hcc_params); - - fusbh200_dbg (fusbh200, - "%s hcc_params %04x uframes %s%s\n", - label, - params, - HCC_PGM_FRAMELISTLEN(params) ? "256/512/1024" : "1024", - HCC_CANPARK(params) ? " park" : ""); -} - -static void __maybe_unused -dbg_qtd (const char *label, struct fusbh200_hcd *fusbh200, struct fusbh200_qtd *qtd) -{ - fusbh200_dbg(fusbh200, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd, - hc32_to_cpup(fusbh200, &qtd->hw_next), - hc32_to_cpup(fusbh200, &qtd->hw_alt_next), - hc32_to_cpup(fusbh200, &qtd->hw_token), - hc32_to_cpup(fusbh200, &qtd->hw_buf [0])); - if (qtd->hw_buf [1]) - fusbh200_dbg(fusbh200, " p1=%08x p2=%08x p3=%08x p4=%08x\n", - hc32_to_cpup(fusbh200, &qtd->hw_buf[1]), - hc32_to_cpup(fusbh200, &qtd->hw_buf[2]), - hc32_to_cpup(fusbh200, &qtd->hw_buf[3]), - hc32_to_cpup(fusbh200, &qtd->hw_buf[4])); -} - -static void __maybe_unused -dbg_qh (const char *label, struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh) -{ - struct fusbh200_qh_hw *hw = qh->hw; - - fusbh200_dbg (fusbh200, "%s qh %p n%08x info %x %x qtd %x\n", label, - qh, hw->hw_next, hw->hw_info1, hw->hw_info2, hw->hw_current); - dbg_qtd("overlay", fusbh200, (struct fusbh200_qtd *) &hw->hw_qtd_next); -} - -static void __maybe_unused -dbg_itd (const char *label, struct fusbh200_hcd *fusbh200, struct fusbh200_itd *itd) -{ - fusbh200_dbg (fusbh200, "%s [%d] itd %p, next %08x, urb %p\n", - label, itd->frame, itd, hc32_to_cpu(fusbh200, itd->hw_next), - itd->urb); - fusbh200_dbg (fusbh200, - " trans: %08x %08x %08x %08x %08x %08x %08x %08x\n", - hc32_to_cpu(fusbh200, itd->hw_transaction[0]), - hc32_to_cpu(fusbh200, itd->hw_transaction[1]), - hc32_to_cpu(fusbh200, itd->hw_transaction[2]), - hc32_to_cpu(fusbh200, itd->hw_transaction[3]), - hc32_to_cpu(fusbh200, itd->hw_transaction[4]), - hc32_to_cpu(fusbh200, itd->hw_transaction[5]), - hc32_to_cpu(fusbh200, itd->hw_transaction[6]), - hc32_to_cpu(fusbh200, itd->hw_transaction[7])); - fusbh200_dbg (fusbh200, - " buf: %08x %08x %08x %08x %08x %08x %08x\n", - hc32_to_cpu(fusbh200, itd->hw_bufp[0]), - hc32_to_cpu(fusbh200, itd->hw_bufp[1]), - hc32_to_cpu(fusbh200, itd->hw_bufp[2]), - hc32_to_cpu(fusbh200, itd->hw_bufp[3]), - hc32_to_cpu(fusbh200, itd->hw_bufp[4]), - hc32_to_cpu(fusbh200, itd->hw_bufp[5]), - hc32_to_cpu(fusbh200, itd->hw_bufp[6])); - fusbh200_dbg (fusbh200, " index: %d %d %d %d %d %d %d %d\n", - itd->index[0], itd->index[1], itd->index[2], - itd->index[3], itd->index[4], itd->index[5], - itd->index[6], itd->index[7]); -} - -static int __maybe_unused -dbg_status_buf (char *buf, unsigned len, const char *label, u32 status) -{ - return scnprintf (buf, len, - "%s%sstatus %04x%s%s%s%s%s%s%s%s%s%s", - label, label [0] ? " " : "", status, - (status & STS_ASS) ? " Async" : "", - (status & STS_PSS) ? " Periodic" : "", - (status & STS_RECL) ? " Recl" : "", - (status & STS_HALT) ? " Halt" : "", - (status & STS_IAA) ? " IAA" : "", - (status & STS_FATAL) ? " FATAL" : "", - (status & STS_FLR) ? " FLR" : "", - (status & STS_PCD) ? " PCD" : "", - (status & STS_ERR) ? " ERR" : "", - (status & STS_INT) ? " INT" : "" - ); -} - -static int __maybe_unused -dbg_intr_buf (char *buf, unsigned len, const char *label, u32 enable) -{ - return scnprintf (buf, len, - "%s%sintrenable %02x%s%s%s%s%s%s", - label, label [0] ? " " : "", enable, - (enable & STS_IAA) ? " IAA" : "", - (enable & STS_FATAL) ? " FATAL" : "", - (enable & STS_FLR) ? " FLR" : "", - (enable & STS_PCD) ? " PCD" : "", - (enable & STS_ERR) ? " ERR" : "", - (enable & STS_INT) ? " INT" : "" - ); -} - -static const char *const fls_strings [] = - { "1024", "512", "256", "??" }; - -static int -dbg_command_buf (char *buf, unsigned len, const char *label, u32 command) -{ - return scnprintf (buf, len, - "%s%scommand %07x %s=%d ithresh=%d%s%s%s " - "period=%s%s %s", - label, label [0] ? " " : "", command, - (command & CMD_PARK) ? " park" : "(park)", - CMD_PARK_CNT (command), - (command >> 16) & 0x3f, - (command & CMD_IAAD) ? " IAAD" : "", - (command & CMD_ASE) ? " Async" : "", - (command & CMD_PSE) ? " Periodic" : "", - fls_strings [(command >> 2) & 0x3], - (command & CMD_RESET) ? " Reset" : "", - (command & CMD_RUN) ? "RUN" : "HALT" - ); -} - -static int -dbg_port_buf (char *buf, unsigned len, const char *label, int port, u32 status) -{ - char *sig; - - /* signaling state */ - switch (status & (3 << 10)) { - case 0 << 10: sig = "se0"; break; - case 1 << 10: sig = "k"; break; /* low speed */ - case 2 << 10: sig = "j"; break; - default: sig = "?"; break; - } - - return scnprintf (buf, len, - "%s%sport:%d status %06x %d " - "sig=%s%s%s%s%s%s%s%s", - label, label [0] ? " " : "", port, status, - status>>25,/*device address */ - sig, - (status & PORT_RESET) ? " RESET" : "", - (status & PORT_SUSPEND) ? " SUSPEND" : "", - (status & PORT_RESUME) ? " RESUME" : "", - (status & PORT_PEC) ? " PEC" : "", - (status & PORT_PE) ? " PE" : "", - (status & PORT_CSC) ? " CSC" : "", - (status & PORT_CONNECT) ? " CONNECT" : ""); -} - -/* functions have the "wrong" filename when they're output... */ -#define dbg_status(fusbh200, label, status) { \ - char _buf [80]; \ - dbg_status_buf (_buf, sizeof _buf, label, status); \ - fusbh200_dbg (fusbh200, "%s\n", _buf); \ -} - -#define dbg_cmd(fusbh200, label, command) { \ - char _buf [80]; \ - dbg_command_buf (_buf, sizeof _buf, label, command); \ - fusbh200_dbg (fusbh200, "%s\n", _buf); \ -} - -#define dbg_port(fusbh200, label, port, status) { \ - char _buf [80]; \ - dbg_port_buf (_buf, sizeof _buf, label, port, status); \ - fusbh200_dbg (fusbh200, "%s\n", _buf); \ -} - -/*-------------------------------------------------------------------------*/ - -/* troubleshooting help: expose state in debugfs */ - -static int debug_async_open(struct inode *, struct file *); -static int debug_periodic_open(struct inode *, struct file *); -static int debug_registers_open(struct inode *, struct file *); -static int debug_async_open(struct inode *, struct file *); - -static ssize_t debug_output(struct file*, char __user*, size_t, loff_t*); -static int debug_close(struct inode *, struct file *); - -static const struct file_operations debug_async_fops = { - .owner = THIS_MODULE, - .open = debug_async_open, - .read = debug_output, - .release = debug_close, - .llseek = default_llseek, -}; -static const struct file_operations debug_periodic_fops = { - .owner = THIS_MODULE, - .open = debug_periodic_open, - .read = debug_output, - .release = debug_close, - .llseek = default_llseek, -}; -static const struct file_operations debug_registers_fops = { - .owner = THIS_MODULE, - .open = debug_registers_open, - .read = debug_output, - .release = debug_close, - .llseek = default_llseek, -}; - -static struct dentry *fusbh200_debug_root; - -struct debug_buffer { - ssize_t (*fill_func)(struct debug_buffer *); /* fill method */ - struct usb_bus *bus; - struct mutex mutex; /* protect filling of buffer */ - size_t count; /* number of characters filled into buffer */ - char *output_buf; - size_t alloc_size; -}; - -#define speed_char(info1) ({ char tmp; \ - switch (info1 & (3 << 12)) { \ - case QH_FULL_SPEED: tmp = 'f'; break; \ - case QH_LOW_SPEED: tmp = 'l'; break; \ - case QH_HIGH_SPEED: tmp = 'h'; break; \ - default: tmp = '?'; break; \ - } tmp; }) - -static inline char token_mark(struct fusbh200_hcd *fusbh200, __hc32 token) -{ - __u32 v = hc32_to_cpu(fusbh200, token); - - if (v & QTD_STS_ACTIVE) - return '*'; - if (v & QTD_STS_HALT) - return '-'; - if (!IS_SHORT_READ (v)) - return ' '; - /* tries to advance through hw_alt_next */ - return '/'; -} - -static void qh_lines ( - struct fusbh200_hcd *fusbh200, - struct fusbh200_qh *qh, - char **nextp, - unsigned *sizep -) -{ - u32 scratch; - u32 hw_curr; - struct fusbh200_qtd *td; - unsigned temp; - unsigned size = *sizep; - char *next = *nextp; - char mark; - __le32 list_end = FUSBH200_LIST_END(fusbh200); - struct fusbh200_qh_hw *hw = qh->hw; - - if (hw->hw_qtd_next == list_end) /* NEC does this */ - mark = '@'; - else - mark = token_mark(fusbh200, hw->hw_token); - if (mark == '/') { /* qh_alt_next controls qh advance? */ - if ((hw->hw_alt_next & QTD_MASK(fusbh200)) - == fusbh200->async->hw->hw_alt_next) - mark = '#'; /* blocked */ - else if (hw->hw_alt_next == list_end) - mark = '.'; /* use hw_qtd_next */ - /* else alt_next points to some other qtd */ - } - scratch = hc32_to_cpup(fusbh200, &hw->hw_info1); - hw_curr = (mark == '*') ? hc32_to_cpup(fusbh200, &hw->hw_current) : 0; - temp = scnprintf (next, size, - "qh/%p dev%d %cs ep%d %08x %08x (%08x%c %s nak%d)", - qh, scratch & 0x007f, - speed_char (scratch), - (scratch >> 8) & 0x000f, - scratch, hc32_to_cpup(fusbh200, &hw->hw_info2), - hc32_to_cpup(fusbh200, &hw->hw_token), mark, - (cpu_to_hc32(fusbh200, QTD_TOGGLE) & hw->hw_token) - ? "data1" : "data0", - (hc32_to_cpup(fusbh200, &hw->hw_alt_next) >> 1) & 0x0f); - size -= temp; - next += temp; - - /* hc may be modifying the list as we read it ... */ - list_for_each_entry(td, &qh->qtd_list, qtd_list) { - scratch = hc32_to_cpup(fusbh200, &td->hw_token); - mark = ' '; - if (hw_curr == td->qtd_dma) - mark = '*'; - else if (hw->hw_qtd_next == cpu_to_hc32(fusbh200, td->qtd_dma)) - mark = '+'; - else if (QTD_LENGTH (scratch)) { - if (td->hw_alt_next == fusbh200->async->hw->hw_alt_next) - mark = '#'; - else if (td->hw_alt_next != list_end) - mark = '/'; - } - temp = snprintf (next, size, - "\n\t%p%c%s len=%d %08x urb %p", - td, mark, ({ char *tmp; - switch ((scratch>>8)&0x03) { - case 0: tmp = "out"; break; - case 1: tmp = "in"; break; - case 2: tmp = "setup"; break; - default: tmp = "?"; break; - } tmp;}), - (scratch >> 16) & 0x7fff, - scratch, - td->urb); - if (size < temp) - temp = size; - size -= temp; - next += temp; - if (temp == size) - goto done; - } - - temp = snprintf (next, size, "\n"); - if (size < temp) - temp = size; - size -= temp; - next += temp; - -done: - *sizep = size; - *nextp = next; -} - -static ssize_t fill_async_buffer(struct debug_buffer *buf) -{ - struct usb_hcd *hcd; - struct fusbh200_hcd *fusbh200; - unsigned long flags; - unsigned temp, size; - char *next; - struct fusbh200_qh *qh; - - hcd = bus_to_hcd(buf->bus); - fusbh200 = hcd_to_fusbh200 (hcd); - next = buf->output_buf; - size = buf->alloc_size; - - *next = 0; - - /* dumps a snapshot of the async schedule. - * usually empty except for long-term bulk reads, or head. - * one QH per line, and TDs we know about - */ - spin_lock_irqsave (&fusbh200->lock, flags); - for (qh = fusbh200->async->qh_next.qh; size > 0 && qh; qh = qh->qh_next.qh) - qh_lines (fusbh200, qh, &next, &size); - if (fusbh200->async_unlink && size > 0) { - temp = scnprintf(next, size, "\nunlink =\n"); - size -= temp; - next += temp; - - for (qh = fusbh200->async_unlink; size > 0 && qh; - qh = qh->unlink_next) - qh_lines (fusbh200, qh, &next, &size); - } - spin_unlock_irqrestore (&fusbh200->lock, flags); - - return strlen(buf->output_buf); -} - -#define DBG_SCHED_LIMIT 64 -static ssize_t fill_periodic_buffer(struct debug_buffer *buf) -{ - struct usb_hcd *hcd; - struct fusbh200_hcd *fusbh200; - unsigned long flags; - union fusbh200_shadow p, *seen; - unsigned temp, size, seen_count; - char *next; - unsigned i; - __hc32 tag; - - seen = kmalloc(DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC); - if (!seen) - return 0; - seen_count = 0; - - hcd = bus_to_hcd(buf->bus); - fusbh200 = hcd_to_fusbh200 (hcd); - next = buf->output_buf; - size = buf->alloc_size; - - temp = scnprintf (next, size, "size = %d\n", fusbh200->periodic_size); - size -= temp; - next += temp; - - /* dump a snapshot of the periodic schedule. - * iso changes, interrupt usually doesn't. - */ - spin_lock_irqsave (&fusbh200->lock, flags); - for (i = 0; i < fusbh200->periodic_size; i++) { - p = fusbh200->pshadow [i]; - if (likely (!p.ptr)) - continue; - tag = Q_NEXT_TYPE(fusbh200, fusbh200->periodic [i]); - - temp = scnprintf (next, size, "%4d: ", i); - size -= temp; - next += temp; - - do { - struct fusbh200_qh_hw *hw; - - switch (hc32_to_cpu(fusbh200, tag)) { - case Q_TYPE_QH: - hw = p.qh->hw; - temp = scnprintf (next, size, " qh%d-%04x/%p", - p.qh->period, - hc32_to_cpup(fusbh200, - &hw->hw_info2) - /* uframe masks */ - & (QH_CMASK | QH_SMASK), - p.qh); - size -= temp; - next += temp; - /* don't repeat what follows this qh */ - for (temp = 0; temp < seen_count; temp++) { - if (seen [temp].ptr != p.ptr) - continue; - if (p.qh->qh_next.ptr) { - temp = scnprintf (next, size, - " ..."); - size -= temp; - next += temp; - } - break; - } - /* show more info the first time around */ - if (temp == seen_count) { - u32 scratch = hc32_to_cpup(fusbh200, - &hw->hw_info1); - struct fusbh200_qtd *qtd; - char *type = ""; - - /* count tds, get ep direction */ - temp = 0; - list_for_each_entry (qtd, - &p.qh->qtd_list, - qtd_list) { - temp++; - switch (0x03 & (hc32_to_cpu( - fusbh200, - qtd->hw_token) >> 8)) { - case 0: type = "out"; continue; - case 1: type = "in"; continue; - } - } - - temp = scnprintf (next, size, - " (%c%d ep%d%s " - "[%d/%d] q%d p%d)", - speed_char (scratch), - scratch & 0x007f, - (scratch >> 8) & 0x000f, type, - p.qh->usecs, p.qh->c_usecs, - temp, - 0x7ff & (scratch >> 16)); - - if (seen_count < DBG_SCHED_LIMIT) - seen [seen_count++].qh = p.qh; - } else - temp = 0; - tag = Q_NEXT_TYPE(fusbh200, hw->hw_next); - p = p.qh->qh_next; - break; - case Q_TYPE_FSTN: - temp = scnprintf (next, size, - " fstn-%8x/%p", p.fstn->hw_prev, - p.fstn); - tag = Q_NEXT_TYPE(fusbh200, p.fstn->hw_next); - p = p.fstn->fstn_next; - break; - case Q_TYPE_ITD: - temp = scnprintf (next, size, - " itd/%p", p.itd); - tag = Q_NEXT_TYPE(fusbh200, p.itd->hw_next); - p = p.itd->itd_next; - break; - } - size -= temp; - next += temp; - } while (p.ptr); - - temp = scnprintf (next, size, "\n"); - size -= temp; - next += temp; - } - spin_unlock_irqrestore (&fusbh200->lock, flags); - kfree (seen); - - return buf->alloc_size - size; -} -#undef DBG_SCHED_LIMIT - -static const char *rh_state_string(struct fusbh200_hcd *fusbh200) -{ - switch (fusbh200->rh_state) { - case FUSBH200_RH_HALTED: - return "halted"; - case FUSBH200_RH_SUSPENDED: - return "suspended"; - case FUSBH200_RH_RUNNING: - return "running"; - case FUSBH200_RH_STOPPING: - return "stopping"; - } - return "?"; -} - -static ssize_t fill_registers_buffer(struct debug_buffer *buf) -{ - struct usb_hcd *hcd; - struct fusbh200_hcd *fusbh200; - unsigned long flags; - unsigned temp, size, i; - char *next, scratch [80]; - static char fmt [] = "%*s\n"; - static char label [] = ""; - - hcd = bus_to_hcd(buf->bus); - fusbh200 = hcd_to_fusbh200 (hcd); - next = buf->output_buf; - size = buf->alloc_size; - - spin_lock_irqsave (&fusbh200->lock, flags); - - if (!HCD_HW_ACCESSIBLE(hcd)) { - size = scnprintf (next, size, - "bus %s, device %s\n" - "%s\n" - "SUSPENDED (no register access)\n", - hcd->self.controller->bus->name, - dev_name(hcd->self.controller), - hcd->product_desc); - goto done; - } - - /* Capability Registers */ - i = HC_VERSION(fusbh200, fusbh200_readl(fusbh200, &fusbh200->caps->hc_capbase)); - temp = scnprintf (next, size, - "bus %s, device %s\n" - "%s\n" - "EHCI %x.%02x, rh state %s\n", - hcd->self.controller->bus->name, - dev_name(hcd->self.controller), - hcd->product_desc, - i >> 8, i & 0x0ff, rh_state_string(fusbh200)); - size -= temp; - next += temp; - - // FIXME interpret both types of params - i = fusbh200_readl(fusbh200, &fusbh200->caps->hcs_params); - temp = scnprintf (next, size, "structural params 0x%08x\n", i); - size -= temp; - next += temp; - - i = fusbh200_readl(fusbh200, &fusbh200->caps->hcc_params); - temp = scnprintf (next, size, "capability params 0x%08x\n", i); - size -= temp; - next += temp; - - /* Operational Registers */ - temp = dbg_status_buf (scratch, sizeof scratch, label, - fusbh200_readl(fusbh200, &fusbh200->regs->status)); - temp = scnprintf (next, size, fmt, temp, scratch); - size -= temp; - next += temp; - - temp = dbg_command_buf (scratch, sizeof scratch, label, - fusbh200_readl(fusbh200, &fusbh200->regs->command)); - temp = scnprintf (next, size, fmt, temp, scratch); - size -= temp; - next += temp; - - temp = dbg_intr_buf (scratch, sizeof scratch, label, - fusbh200_readl(fusbh200, &fusbh200->regs->intr_enable)); - temp = scnprintf (next, size, fmt, temp, scratch); - size -= temp; - next += temp; - - temp = scnprintf (next, size, "uframe %04x\n", - fusbh200_read_frame_index(fusbh200)); - size -= temp; - next += temp; - - if (fusbh200->async_unlink) { - temp = scnprintf(next, size, "async unlink qh %p\n", - fusbh200->async_unlink); - size -= temp; - next += temp; - } - - temp = scnprintf (next, size, - "irq normal %ld err %ld iaa %ld (lost %ld)\n", - fusbh200->stats.normal, fusbh200->stats.error, fusbh200->stats.iaa, - fusbh200->stats.lost_iaa); - size -= temp; - next += temp; - - temp = scnprintf (next, size, "complete %ld unlink %ld\n", - fusbh200->stats.complete, fusbh200->stats.unlink); - size -= temp; - next += temp; - -done: - spin_unlock_irqrestore (&fusbh200->lock, flags); - - return buf->alloc_size - size; -} - -static struct debug_buffer *alloc_buffer(struct usb_bus *bus, - ssize_t (*fill_func)(struct debug_buffer *)) -{ - struct debug_buffer *buf; - - buf = kzalloc(sizeof(struct debug_buffer), GFP_KERNEL); - - if (buf) { - buf->bus = bus; - buf->fill_func = fill_func; - mutex_init(&buf->mutex); - buf->alloc_size = PAGE_SIZE; - } - - return buf; -} - -static int fill_buffer(struct debug_buffer *buf) -{ - int ret = 0; - - if (!buf->output_buf) - buf->output_buf = vmalloc(buf->alloc_size); - - if (!buf->output_buf) { - ret = -ENOMEM; - goto out; - } - - ret = buf->fill_func(buf); - - if (ret >= 0) { - buf->count = ret; - ret = 0; - } - -out: - return ret; -} - -static ssize_t debug_output(struct file *file, char __user *user_buf, - size_t len, loff_t *offset) -{ - struct debug_buffer *buf = file->private_data; - int ret = 0; - - mutex_lock(&buf->mutex); - if (buf->count == 0) { - ret = fill_buffer(buf); - if (ret != 0) { - mutex_unlock(&buf->mutex); - goto out; - } - } - mutex_unlock(&buf->mutex); - - ret = simple_read_from_buffer(user_buf, len, offset, - buf->output_buf, buf->count); - -out: - return ret; - -} - -static int debug_close(struct inode *inode, struct file *file) -{ - struct debug_buffer *buf = file->private_data; - - if (buf) { - vfree(buf->output_buf); - kfree(buf); - } - - return 0; -} -static int debug_async_open(struct inode *inode, struct file *file) -{ - file->private_data = alloc_buffer(inode->i_private, fill_async_buffer); - - return file->private_data ? 0 : -ENOMEM; -} - -static int debug_periodic_open(struct inode *inode, struct file *file) -{ - struct debug_buffer *buf; - buf = alloc_buffer(inode->i_private, fill_periodic_buffer); - if (!buf) - return -ENOMEM; - - buf->alloc_size = (sizeof(void *) == 4 ? 6 : 8)*PAGE_SIZE; - file->private_data = buf; - return 0; -} - -static int debug_registers_open(struct inode *inode, struct file *file) -{ - file->private_data = alloc_buffer(inode->i_private, - fill_registers_buffer); - - return file->private_data ? 0 : -ENOMEM; -} - -static inline void create_debug_files (struct fusbh200_hcd *fusbh200) -{ - struct usb_bus *bus = &fusbh200_to_hcd(fusbh200)->self; - - fusbh200->debug_dir = debugfs_create_dir(bus->bus_name, fusbh200_debug_root); - if (!fusbh200->debug_dir) - return; - - if (!debugfs_create_file("async", S_IRUGO, fusbh200->debug_dir, bus, - &debug_async_fops)) - goto file_error; - - if (!debugfs_create_file("periodic", S_IRUGO, fusbh200->debug_dir, bus, - &debug_periodic_fops)) - goto file_error; - - if (!debugfs_create_file("registers", S_IRUGO, fusbh200->debug_dir, bus, - &debug_registers_fops)) - goto file_error; - - return; - -file_error: - debugfs_remove_recursive(fusbh200->debug_dir); -} - -static inline void remove_debug_files (struct fusbh200_hcd *fusbh200) -{ - debugfs_remove_recursive(fusbh200->debug_dir); -} - -/*-------------------------------------------------------------------------*/ - -/* - * handshake - spin reading hc until handshake completes or fails - * @ptr: address of hc register to be read - * @mask: bits to look at in result of read - * @done: value of those bits when handshake succeeds - * @usec: timeout in microseconds - * - * Returns negative errno, or zero on success - * - * Success happens when the "mask" bits have the specified value (hardware - * handshake done). There are two failure modes: "usec" have passed (major - * hardware flakeout), or the register reads as all-ones (hardware removed). - * - * That last failure should_only happen in cases like physical cardbus eject - * before driver shutdown. But it also seems to be caused by bugs in cardbus - * bridge shutdown: shutting down the bridge before the devices using it. - */ -static int handshake (struct fusbh200_hcd *fusbh200, void __iomem *ptr, - u32 mask, u32 done, int usec) -{ - u32 result; - - do { - result = fusbh200_readl(fusbh200, ptr); - if (result == ~(u32)0) /* card removed */ - return -ENODEV; - result &= mask; - if (result == done) - return 0; - udelay (1); - usec--; - } while (usec > 0); - return -ETIMEDOUT; -} - -/* - * Force HC to halt state from unknown (EHCI spec section 2.3). - * Must be called with interrupts enabled and the lock not held. - */ -static int fusbh200_halt (struct fusbh200_hcd *fusbh200) -{ - u32 temp; - - spin_lock_irq(&fusbh200->lock); - - /* disable any irqs left enabled by previous code */ - fusbh200_writel(fusbh200, 0, &fusbh200->regs->intr_enable); - - /* - * This routine gets called during probe before fusbh200->command - * has been initialized, so we can't rely on its value. - */ - fusbh200->command &= ~CMD_RUN; - temp = fusbh200_readl(fusbh200, &fusbh200->regs->command); - temp &= ~(CMD_RUN | CMD_IAAD); - fusbh200_writel(fusbh200, temp, &fusbh200->regs->command); - - spin_unlock_irq(&fusbh200->lock); - synchronize_irq(fusbh200_to_hcd(fusbh200)->irq); - - return handshake(fusbh200, &fusbh200->regs->status, - STS_HALT, STS_HALT, 16 * 125); -} - -/* - * Reset a non-running (STS_HALT == 1) controller. - * Must be called with interrupts enabled and the lock not held. - */ -static int fusbh200_reset (struct fusbh200_hcd *fusbh200) -{ - int retval; - u32 command = fusbh200_readl(fusbh200, &fusbh200->regs->command); - - /* If the EHCI debug controller is active, special care must be - * taken before and after a host controller reset */ - if (fusbh200->debug && !dbgp_reset_prep(fusbh200_to_hcd(fusbh200))) - fusbh200->debug = NULL; - - command |= CMD_RESET; - dbg_cmd (fusbh200, "reset", command); - fusbh200_writel(fusbh200, command, &fusbh200->regs->command); - fusbh200->rh_state = FUSBH200_RH_HALTED; - fusbh200->next_statechange = jiffies; - retval = handshake (fusbh200, &fusbh200->regs->command, - CMD_RESET, 0, 250 * 1000); - - if (retval) - return retval; - - if (fusbh200->debug) - dbgp_external_startup(fusbh200_to_hcd(fusbh200)); - - fusbh200->port_c_suspend = fusbh200->suspended_ports = - fusbh200->resuming_ports = 0; - return retval; -} - -/* - * Idle the controller (turn off the schedules). - * Must be called with interrupts enabled and the lock not held. - */ -static void fusbh200_quiesce (struct fusbh200_hcd *fusbh200) -{ - u32 temp; - - if (fusbh200->rh_state != FUSBH200_RH_RUNNING) - return; - - /* wait for any schedule enables/disables to take effect */ - temp = (fusbh200->command << 10) & (STS_ASS | STS_PSS); - handshake(fusbh200, &fusbh200->regs->status, STS_ASS | STS_PSS, temp, 16 * 125); - - /* then disable anything that's still active */ - spin_lock_irq(&fusbh200->lock); - fusbh200->command &= ~(CMD_ASE | CMD_PSE); - fusbh200_writel(fusbh200, fusbh200->command, &fusbh200->regs->command); - spin_unlock_irq(&fusbh200->lock); - - /* hardware can take 16 microframes to turn off ... */ - handshake(fusbh200, &fusbh200->regs->status, STS_ASS | STS_PSS, 0, 16 * 125); -} - -/*-------------------------------------------------------------------------*/ - -static void end_unlink_async(struct fusbh200_hcd *fusbh200); -static void unlink_empty_async(struct fusbh200_hcd *fusbh200); -static void fusbh200_work(struct fusbh200_hcd *fusbh200); -static void start_unlink_intr(struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh); -static void end_unlink_intr(struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh); - -/*-------------------------------------------------------------------------*/ - -/* Set a bit in the USBCMD register */ -static void fusbh200_set_command_bit(struct fusbh200_hcd *fusbh200, u32 bit) -{ - fusbh200->command |= bit; - fusbh200_writel(fusbh200, fusbh200->command, &fusbh200->regs->command); - - /* unblock posted write */ - fusbh200_readl(fusbh200, &fusbh200->regs->command); -} - -/* Clear a bit in the USBCMD register */ -static void fusbh200_clear_command_bit(struct fusbh200_hcd *fusbh200, u32 bit) -{ - fusbh200->command &= ~bit; - fusbh200_writel(fusbh200, fusbh200->command, &fusbh200->regs->command); - - /* unblock posted write */ - fusbh200_readl(fusbh200, &fusbh200->regs->command); -} - -/*-------------------------------------------------------------------------*/ - -/* - * EHCI timer support... Now using hrtimers. - * - * Lots of different events are triggered from fusbh200->hrtimer. Whenever - * the timer routine runs, it checks each possible event; events that are - * currently enabled and whose expiration time has passed get handled. - * The set of enabled events is stored as a collection of bitflags in - * fusbh200->enabled_hrtimer_events, and they are numbered in order of - * increasing delay values (ranging between 1 ms and 100 ms). - * - * Rather than implementing a sorted list or tree of all pending events, - * we keep track only of the lowest-numbered pending event, in - * fusbh200->next_hrtimer_event. Whenever fusbh200->hrtimer gets restarted, its - * expiration time is set to the timeout value for this event. - * - * As a result, events might not get handled right away; the actual delay - * could be anywhere up to twice the requested delay. This doesn't - * matter, because none of the events are especially time-critical. The - * ones that matter most all have a delay of 1 ms, so they will be - * handled after 2 ms at most, which is okay. In addition to this, we - * allow for an expiration range of 1 ms. - */ - -/* - * Delay lengths for the hrtimer event types. - * Keep this list sorted by delay length, in the same order as - * the event types indexed by enum fusbh200_hrtimer_event in fusbh200.h. - */ -static unsigned event_delays_ns[] = { - 1 * NSEC_PER_MSEC, /* FUSBH200_HRTIMER_POLL_ASS */ - 1 * NSEC_PER_MSEC, /* FUSBH200_HRTIMER_POLL_PSS */ - 1 * NSEC_PER_MSEC, /* FUSBH200_HRTIMER_POLL_DEAD */ - 1125 * NSEC_PER_USEC, /* FUSBH200_HRTIMER_UNLINK_INTR */ - 2 * NSEC_PER_MSEC, /* FUSBH200_HRTIMER_FREE_ITDS */ - 6 * NSEC_PER_MSEC, /* FUSBH200_HRTIMER_ASYNC_UNLINKS */ - 10 * NSEC_PER_MSEC, /* FUSBH200_HRTIMER_IAA_WATCHDOG */ - 10 * NSEC_PER_MSEC, /* FUSBH200_HRTIMER_DISABLE_PERIODIC */ - 15 * NSEC_PER_MSEC, /* FUSBH200_HRTIMER_DISABLE_ASYNC */ - 100 * NSEC_PER_MSEC, /* FUSBH200_HRTIMER_IO_WATCHDOG */ -}; - -/* Enable a pending hrtimer event */ -static void fusbh200_enable_event(struct fusbh200_hcd *fusbh200, unsigned event, - bool resched) -{ - ktime_t *timeout = &fusbh200->hr_timeouts[event]; - - if (resched) - *timeout = ktime_add(ktime_get(), - ktime_set(0, event_delays_ns[event])); - fusbh200->enabled_hrtimer_events |= (1 << event); - - /* Track only the lowest-numbered pending event */ - if (event < fusbh200->next_hrtimer_event) { - fusbh200->next_hrtimer_event = event; - hrtimer_start_range_ns(&fusbh200->hrtimer, *timeout, - NSEC_PER_MSEC, HRTIMER_MODE_ABS); - } -} - - -/* Poll the STS_ASS status bit; see when it agrees with CMD_ASE */ -static void fusbh200_poll_ASS(struct fusbh200_hcd *fusbh200) -{ - unsigned actual, want; - - /* Don't enable anything if the controller isn't running (e.g., died) */ - if (fusbh200->rh_state != FUSBH200_RH_RUNNING) - return; - - want = (fusbh200->command & CMD_ASE) ? STS_ASS : 0; - actual = fusbh200_readl(fusbh200, &fusbh200->regs->status) & STS_ASS; - - if (want != actual) { - - /* Poll again later, but give up after about 20 ms */ - if (fusbh200->ASS_poll_count++ < 20) { - fusbh200_enable_event(fusbh200, FUSBH200_HRTIMER_POLL_ASS, true); - return; - } - fusbh200_dbg(fusbh200, "Waited too long for the async schedule status (%x/%x), giving up\n", - want, actual); - } - fusbh200->ASS_poll_count = 0; - - /* The status is up-to-date; restart or stop the schedule as needed */ - if (want == 0) { /* Stopped */ - if (fusbh200->async_count > 0) - fusbh200_set_command_bit(fusbh200, CMD_ASE); - - } else { /* Running */ - if (fusbh200->async_count == 0) { - - /* Turn off the schedule after a while */ - fusbh200_enable_event(fusbh200, FUSBH200_HRTIMER_DISABLE_ASYNC, - true); - } - } -} - -/* Turn off the async schedule after a brief delay */ -static void fusbh200_disable_ASE(struct fusbh200_hcd *fusbh200) -{ - fusbh200_clear_command_bit(fusbh200, CMD_ASE); -} - - -/* Poll the STS_PSS status bit; see when it agrees with CMD_PSE */ -static void fusbh200_poll_PSS(struct fusbh200_hcd *fusbh200) -{ - unsigned actual, want; - - /* Don't do anything if the controller isn't running (e.g., died) */ - if (fusbh200->rh_state != FUSBH200_RH_RUNNING) - return; - - want = (fusbh200->command & CMD_PSE) ? STS_PSS : 0; - actual = fusbh200_readl(fusbh200, &fusbh200->regs->status) & STS_PSS; - - if (want != actual) { - - /* Poll again later, but give up after about 20 ms */ - if (fusbh200->PSS_poll_count++ < 20) { - fusbh200_enable_event(fusbh200, FUSBH200_HRTIMER_POLL_PSS, true); - return; - } - fusbh200_dbg(fusbh200, "Waited too long for the periodic schedule status (%x/%x), giving up\n", - want, actual); - } - fusbh200->PSS_poll_count = 0; - - /* The status is up-to-date; restart or stop the schedule as needed */ - if (want == 0) { /* Stopped */ - if (fusbh200->periodic_count > 0) - fusbh200_set_command_bit(fusbh200, CMD_PSE); - - } else { /* Running */ - if (fusbh200->periodic_count == 0) { - - /* Turn off the schedule after a while */ - fusbh200_enable_event(fusbh200, FUSBH200_HRTIMER_DISABLE_PERIODIC, - true); - } - } -} - -/* Turn off the periodic schedule after a brief delay */ -static void fusbh200_disable_PSE(struct fusbh200_hcd *fusbh200) -{ - fusbh200_clear_command_bit(fusbh200, CMD_PSE); -} - - -/* Poll the STS_HALT status bit; see when a dead controller stops */ -static void fusbh200_handle_controller_death(struct fusbh200_hcd *fusbh200) -{ - if (!(fusbh200_readl(fusbh200, &fusbh200->regs->status) & STS_HALT)) { - - /* Give up after a few milliseconds */ - if (fusbh200->died_poll_count++ < 5) { - /* Try again later */ - fusbh200_enable_event(fusbh200, FUSBH200_HRTIMER_POLL_DEAD, true); - return; - } - fusbh200_warn(fusbh200, "Waited too long for the controller to stop, giving up\n"); - } - - /* Clean up the mess */ - fusbh200->rh_state = FUSBH200_RH_HALTED; - fusbh200_writel(fusbh200, 0, &fusbh200->regs->intr_enable); - fusbh200_work(fusbh200); - end_unlink_async(fusbh200); - - /* Not in process context, so don't try to reset the controller */ -} - - -/* Handle unlinked interrupt QHs once they are gone from the hardware */ -static void fusbh200_handle_intr_unlinks(struct fusbh200_hcd *fusbh200) -{ - bool stopped = (fusbh200->rh_state < FUSBH200_RH_RUNNING); - - /* - * Process all the QHs on the intr_unlink list that were added - * before the current unlink cycle began. The list is in - * temporal order, so stop when we reach the first entry in the - * current cycle. But if the root hub isn't running then - * process all the QHs on the list. - */ - fusbh200->intr_unlinking = true; - while (fusbh200->intr_unlink) { - struct fusbh200_qh *qh = fusbh200->intr_unlink; - - if (!stopped && qh->unlink_cycle == fusbh200->intr_unlink_cycle) - break; - fusbh200->intr_unlink = qh->unlink_next; - qh->unlink_next = NULL; - end_unlink_intr(fusbh200, qh); - } - - /* Handle remaining entries later */ - if (fusbh200->intr_unlink) { - fusbh200_enable_event(fusbh200, FUSBH200_HRTIMER_UNLINK_INTR, true); - ++fusbh200->intr_unlink_cycle; - } - fusbh200->intr_unlinking = false; -} - - -/* Start another free-iTDs/siTDs cycle */ -static void start_free_itds(struct fusbh200_hcd *fusbh200) -{ - if (!(fusbh200->enabled_hrtimer_events & BIT(FUSBH200_HRTIMER_FREE_ITDS))) { - fusbh200->last_itd_to_free = list_entry( - fusbh200->cached_itd_list.prev, - struct fusbh200_itd, itd_list); - fusbh200_enable_event(fusbh200, FUSBH200_HRTIMER_FREE_ITDS, true); - } -} - -/* Wait for controller to stop using old iTDs and siTDs */ -static void end_free_itds(struct fusbh200_hcd *fusbh200) -{ - struct fusbh200_itd *itd, *n; - - if (fusbh200->rh_state < FUSBH200_RH_RUNNING) { - fusbh200->last_itd_to_free = NULL; - } - - list_for_each_entry_safe(itd, n, &fusbh200->cached_itd_list, itd_list) { - list_del(&itd->itd_list); - dma_pool_free(fusbh200->itd_pool, itd, itd->itd_dma); - if (itd == fusbh200->last_itd_to_free) - break; - } - - if (!list_empty(&fusbh200->cached_itd_list)) - start_free_itds(fusbh200); -} - - -/* Handle lost (or very late) IAA interrupts */ -static void fusbh200_iaa_watchdog(struct fusbh200_hcd *fusbh200) -{ - if (fusbh200->rh_state != FUSBH200_RH_RUNNING) - return; - - /* - * Lost IAA irqs wedge things badly; seen first with a vt8235. - * So we need this watchdog, but must protect it against both - * (a) SMP races against real IAA firing and retriggering, and - * (b) clean HC shutdown, when IAA watchdog was pending. - */ - if (fusbh200->async_iaa) { - u32 cmd, status; - - /* If we get here, IAA is *REALLY* late. It's barely - * conceivable that the system is so busy that CMD_IAAD - * is still legitimately set, so let's be sure it's - * clear before we read STS_IAA. (The HC should clear - * CMD_IAAD when it sets STS_IAA.) - */ - cmd = fusbh200_readl(fusbh200, &fusbh200->regs->command); - - /* - * If IAA is set here it either legitimately triggered - * after the watchdog timer expired (_way_ late, so we'll - * still count it as lost) ... or a silicon erratum: - * - VIA seems to set IAA without triggering the IRQ; - * - IAAD potentially cleared without setting IAA. - */ - status = fusbh200_readl(fusbh200, &fusbh200->regs->status); - if ((status & STS_IAA) || !(cmd & CMD_IAAD)) { - COUNT(fusbh200->stats.lost_iaa); - fusbh200_writel(fusbh200, STS_IAA, &fusbh200->regs->status); - } - - fusbh200_dbg(fusbh200, "IAA watchdog: status %x cmd %x\n", - status, cmd); - end_unlink_async(fusbh200); - } -} - - -/* Enable the I/O watchdog, if appropriate */ -static void turn_on_io_watchdog(struct fusbh200_hcd *fusbh200) -{ - /* Not needed if the controller isn't running or it's already enabled */ - if (fusbh200->rh_state != FUSBH200_RH_RUNNING || - (fusbh200->enabled_hrtimer_events & - BIT(FUSBH200_HRTIMER_IO_WATCHDOG))) - return; - - /* - * Isochronous transfers always need the watchdog. - * For other sorts we use it only if the flag is set. - */ - if (fusbh200->isoc_count > 0 || (fusbh200->need_io_watchdog && - fusbh200->async_count + fusbh200->intr_count > 0)) - fusbh200_enable_event(fusbh200, FUSBH200_HRTIMER_IO_WATCHDOG, true); -} - - -/* - * Handler functions for the hrtimer event types. - * Keep this array in the same order as the event types indexed by - * enum fusbh200_hrtimer_event in fusbh200.h. - */ -static void (*event_handlers[])(struct fusbh200_hcd *) = { - fusbh200_poll_ASS, /* FUSBH200_HRTIMER_POLL_ASS */ - fusbh200_poll_PSS, /* FUSBH200_HRTIMER_POLL_PSS */ - fusbh200_handle_controller_death, /* FUSBH200_HRTIMER_POLL_DEAD */ - fusbh200_handle_intr_unlinks, /* FUSBH200_HRTIMER_UNLINK_INTR */ - end_free_itds, /* FUSBH200_HRTIMER_FREE_ITDS */ - unlink_empty_async, /* FUSBH200_HRTIMER_ASYNC_UNLINKS */ - fusbh200_iaa_watchdog, /* FUSBH200_HRTIMER_IAA_WATCHDOG */ - fusbh200_disable_PSE, /* FUSBH200_HRTIMER_DISABLE_PERIODIC */ - fusbh200_disable_ASE, /* FUSBH200_HRTIMER_DISABLE_ASYNC */ - fusbh200_work, /* FUSBH200_HRTIMER_IO_WATCHDOG */ -}; - -static enum hrtimer_restart fusbh200_hrtimer_func(struct hrtimer *t) -{ - struct fusbh200_hcd *fusbh200 = container_of(t, struct fusbh200_hcd, hrtimer); - ktime_t now; - unsigned long events; - unsigned long flags; - unsigned e; - - spin_lock_irqsave(&fusbh200->lock, flags); - - events = fusbh200->enabled_hrtimer_events; - fusbh200->enabled_hrtimer_events = 0; - fusbh200->next_hrtimer_event = FUSBH200_HRTIMER_NO_EVENT; - - /* - * Check each pending event. If its time has expired, handle - * the event; otherwise re-enable it. - */ - now = ktime_get(); - for_each_set_bit(e, &events, FUSBH200_HRTIMER_NUM_EVENTS) { - if (now.tv64 >= fusbh200->hr_timeouts[e].tv64) - event_handlers[e](fusbh200); - else - fusbh200_enable_event(fusbh200, e, false); - } - - spin_unlock_irqrestore(&fusbh200->lock, flags); - return HRTIMER_NORESTART; -} - -/*-------------------------------------------------------------------------*/ - -#define fusbh200_bus_suspend NULL -#define fusbh200_bus_resume NULL - -/*-------------------------------------------------------------------------*/ - -static int check_reset_complete ( - struct fusbh200_hcd *fusbh200, - int index, - u32 __iomem *status_reg, - int port_status -) { - if (!(port_status & PORT_CONNECT)) - return port_status; - - /* if reset finished and it's still not enabled -- handoff */ - if (!(port_status & PORT_PE)) { - /* with integrated TT, there's nobody to hand it to! */ - fusbh200_dbg (fusbh200, - "Failed to enable port %d on root hub TT\n", - index+1); - return port_status; - } else { - fusbh200_dbg(fusbh200, "port %d reset complete, port enabled\n", - index + 1); - } - - return port_status; -} - -/*-------------------------------------------------------------------------*/ - - -/* build "status change" packet (one or two bytes) from HC registers */ - -static int -fusbh200_hub_status_data (struct usb_hcd *hcd, char *buf) -{ - struct fusbh200_hcd *fusbh200 = hcd_to_fusbh200 (hcd); - u32 temp, status; - u32 mask; - int retval = 1; - unsigned long flags; - - /* init status to no-changes */ - buf [0] = 0; - - /* Inform the core about resumes-in-progress by returning - * a non-zero value even if there are no status changes. - */ - status = fusbh200->resuming_ports; - - mask = PORT_CSC | PORT_PEC; - // PORT_RESUME from hardware ~= PORT_STAT_C_SUSPEND - - /* no hub change reports (bit 0) for now (power, ...) */ - - /* port N changes (bit N)? */ - spin_lock_irqsave (&fusbh200->lock, flags); - - temp = fusbh200_readl(fusbh200, &fusbh200->regs->port_status); - - /* - * Return status information even for ports with OWNER set. - * Otherwise hub_wq wouldn't see the disconnect event when a - * high-speed device is switched over to the companion - * controller by the user. - */ - - if ((temp & mask) != 0 || test_bit(0, &fusbh200->port_c_suspend) - || (fusbh200->reset_done[0] && time_after_eq( - jiffies, fusbh200->reset_done[0]))) { - buf [0] |= 1 << 1; - status = STS_PCD; - } - /* FIXME autosuspend idle root hubs */ - spin_unlock_irqrestore (&fusbh200->lock, flags); - return status ? retval : 0; -} - -/*-------------------------------------------------------------------------*/ - -static void -fusbh200_hub_descriptor ( - struct fusbh200_hcd *fusbh200, - struct usb_hub_descriptor *desc -) { - int ports = HCS_N_PORTS (fusbh200->hcs_params); - u16 temp; - - desc->bDescriptorType = USB_DT_HUB; - desc->bPwrOn2PwrGood = 10; /* fusbh200 1.0, 2.3.9 says 20ms max */ - desc->bHubContrCurrent = 0; - - desc->bNbrPorts = ports; - temp = 1 + (ports / 8); - desc->bDescLength = 7 + 2 * temp; - - /* two bitmaps: ports removable, and usb 1.0 legacy PortPwrCtrlMask */ - memset(&desc->u.hs.DeviceRemovable[0], 0, temp); - memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp); - - temp = HUB_CHAR_INDV_PORT_OCPM; /* per-port overcurrent reporting */ - temp |= HUB_CHAR_NO_LPSM; /* no power switching */ - desc->wHubCharacteristics = cpu_to_le16(temp); -} - -/*-------------------------------------------------------------------------*/ - -static int fusbh200_hub_control ( - struct usb_hcd *hcd, - u16 typeReq, - u16 wValue, - u16 wIndex, - char *buf, - u16 wLength -) { - struct fusbh200_hcd *fusbh200 = hcd_to_fusbh200 (hcd); - int ports = HCS_N_PORTS (fusbh200->hcs_params); - u32 __iomem *status_reg = &fusbh200->regs->port_status; - u32 temp, temp1, status; - unsigned long flags; - int retval = 0; - unsigned selector; - - /* - * FIXME: support SetPortFeatures USB_PORT_FEAT_INDICATOR. - * HCS_INDICATOR may say we can change LEDs to off/amber/green. - * (track current state ourselves) ... blink for diagnostics, - * power, "this is the one", etc. EHCI spec supports this. - */ - - spin_lock_irqsave (&fusbh200->lock, flags); - switch (typeReq) { - case ClearHubFeature: - switch (wValue) { - case C_HUB_LOCAL_POWER: - case C_HUB_OVER_CURRENT: - /* no hub-wide feature/status flags */ - break; - default: - goto error; - } - break; - case ClearPortFeature: - if (!wIndex || wIndex > ports) - goto error; - wIndex--; - temp = fusbh200_readl(fusbh200, status_reg); - temp &= ~PORT_RWC_BITS; - - /* - * Even if OWNER is set, so the port is owned by the - * companion controller, hub_wq needs to be able to clear - * the port-change status bits (especially - * USB_PORT_STAT_C_CONNECTION). - */ - - switch (wValue) { - case USB_PORT_FEAT_ENABLE: - fusbh200_writel(fusbh200, temp & ~PORT_PE, status_reg); - break; - case USB_PORT_FEAT_C_ENABLE: - fusbh200_writel(fusbh200, temp | PORT_PEC, status_reg); - break; - case USB_PORT_FEAT_SUSPEND: - if (temp & PORT_RESET) - goto error; - if (!(temp & PORT_SUSPEND)) - break; - if ((temp & PORT_PE) == 0) - goto error; - - fusbh200_writel(fusbh200, temp | PORT_RESUME, status_reg); - fusbh200->reset_done[wIndex] = jiffies - + msecs_to_jiffies(USB_RESUME_TIMEOUT); - break; - case USB_PORT_FEAT_C_SUSPEND: - clear_bit(wIndex, &fusbh200->port_c_suspend); - break; - case USB_PORT_FEAT_C_CONNECTION: - fusbh200_writel(fusbh200, temp | PORT_CSC, status_reg); - break; - case USB_PORT_FEAT_C_OVER_CURRENT: - fusbh200_writel(fusbh200, temp | BMISR_OVC, &fusbh200->regs->bmisr); - break; - case USB_PORT_FEAT_C_RESET: - /* GetPortStatus clears reset */ - break; - default: - goto error; - } - fusbh200_readl(fusbh200, &fusbh200->regs->command); /* unblock posted write */ - break; - case GetHubDescriptor: - fusbh200_hub_descriptor (fusbh200, (struct usb_hub_descriptor *) - buf); - break; - case GetHubStatus: - /* no hub-wide feature/status flags */ - memset (buf, 0, 4); - //cpu_to_le32s ((u32 *) buf); - break; - case GetPortStatus: - if (!wIndex || wIndex > ports) - goto error; - wIndex--; - status = 0; - temp = fusbh200_readl(fusbh200, status_reg); - - // wPortChange bits - if (temp & PORT_CSC) - status |= USB_PORT_STAT_C_CONNECTION << 16; - if (temp & PORT_PEC) - status |= USB_PORT_STAT_C_ENABLE << 16; - - temp1 = fusbh200_readl(fusbh200, &fusbh200->regs->bmisr); - if (temp1 & BMISR_OVC) - status |= USB_PORT_STAT_C_OVERCURRENT << 16; - - /* whoever resumes must GetPortStatus to complete it!! */ - if (temp & PORT_RESUME) { - - /* Remote Wakeup received? */ - if (!fusbh200->reset_done[wIndex]) { - /* resume signaling for 20 msec */ - fusbh200->reset_done[wIndex] = jiffies - + msecs_to_jiffies(20); - /* check the port again */ - mod_timer(&fusbh200_to_hcd(fusbh200)->rh_timer, - fusbh200->reset_done[wIndex]); - } - - /* resume completed? */ - else if (time_after_eq(jiffies, - fusbh200->reset_done[wIndex])) { - clear_bit(wIndex, &fusbh200->suspended_ports); - set_bit(wIndex, &fusbh200->port_c_suspend); - fusbh200->reset_done[wIndex] = 0; - - /* stop resume signaling */ - temp = fusbh200_readl(fusbh200, status_reg); - fusbh200_writel(fusbh200, - temp & ~(PORT_RWC_BITS | PORT_RESUME), - status_reg); - clear_bit(wIndex, &fusbh200->resuming_ports); - retval = handshake(fusbh200, status_reg, - PORT_RESUME, 0, 2000 /* 2msec */); - if (retval != 0) { - fusbh200_err(fusbh200, - "port %d resume error %d\n", - wIndex + 1, retval); - goto error; - } - temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10)); - } - } - - /* whoever resets must GetPortStatus to complete it!! */ - if ((temp & PORT_RESET) - && time_after_eq(jiffies, - fusbh200->reset_done[wIndex])) { - status |= USB_PORT_STAT_C_RESET << 16; - fusbh200->reset_done [wIndex] = 0; - clear_bit(wIndex, &fusbh200->resuming_ports); - - /* force reset to complete */ - fusbh200_writel(fusbh200, temp & ~(PORT_RWC_BITS | PORT_RESET), - status_reg); - /* REVISIT: some hardware needs 550+ usec to clear - * this bit; seems too long to spin routinely... - */ - retval = handshake(fusbh200, status_reg, - PORT_RESET, 0, 1000); - if (retval != 0) { - fusbh200_err (fusbh200, "port %d reset error %d\n", - wIndex + 1, retval); - goto error; - } - - /* see what we found out */ - temp = check_reset_complete (fusbh200, wIndex, status_reg, - fusbh200_readl(fusbh200, status_reg)); - } - - if (!(temp & (PORT_RESUME|PORT_RESET))) { - fusbh200->reset_done[wIndex] = 0; - clear_bit(wIndex, &fusbh200->resuming_ports); - } - - /* transfer dedicated ports to the companion hc */ - if ((temp & PORT_CONNECT) && - test_bit(wIndex, &fusbh200->companion_ports)) { - temp &= ~PORT_RWC_BITS; - fusbh200_writel(fusbh200, temp, status_reg); - fusbh200_dbg(fusbh200, "port %d --> companion\n", wIndex + 1); - temp = fusbh200_readl(fusbh200, status_reg); - } - - /* - * Even if OWNER is set, there's no harm letting hub_wq - * see the wPortStatus values (they should all be 0 except - * for PORT_POWER anyway). - */ - - if (temp & PORT_CONNECT) { - status |= USB_PORT_STAT_CONNECTION; - status |= fusbh200_port_speed(fusbh200, temp); - } - if (temp & PORT_PE) - status |= USB_PORT_STAT_ENABLE; - - /* maybe the port was unsuspended without our knowledge */ - if (temp & (PORT_SUSPEND|PORT_RESUME)) { - status |= USB_PORT_STAT_SUSPEND; - } else if (test_bit(wIndex, &fusbh200->suspended_ports)) { - clear_bit(wIndex, &fusbh200->suspended_ports); - clear_bit(wIndex, &fusbh200->resuming_ports); - fusbh200->reset_done[wIndex] = 0; - if (temp & PORT_PE) - set_bit(wIndex, &fusbh200->port_c_suspend); - } - - temp1 = fusbh200_readl(fusbh200, &fusbh200->regs->bmisr); - if (temp1 & BMISR_OVC) - status |= USB_PORT_STAT_OVERCURRENT; - if (temp & PORT_RESET) - status |= USB_PORT_STAT_RESET; - if (test_bit(wIndex, &fusbh200->port_c_suspend)) - status |= USB_PORT_STAT_C_SUSPEND << 16; - - if (status & ~0xffff) /* only if wPortChange is interesting */ - dbg_port(fusbh200, "GetStatus", wIndex + 1, temp); - put_unaligned_le32(status, buf); - break; - case SetHubFeature: - switch (wValue) { - case C_HUB_LOCAL_POWER: - case C_HUB_OVER_CURRENT: - /* no hub-wide feature/status flags */ - break; - default: - goto error; - } - break; - case SetPortFeature: - selector = wIndex >> 8; - wIndex &= 0xff; - - if (!wIndex || wIndex > ports) - goto error; - wIndex--; - temp = fusbh200_readl(fusbh200, status_reg); - temp &= ~PORT_RWC_BITS; - switch (wValue) { - case USB_PORT_FEAT_SUSPEND: - if ((temp & PORT_PE) == 0 - || (temp & PORT_RESET) != 0) - goto error; - - /* After above check the port must be connected. - * Set appropriate bit thus could put phy into low power - * mode if we have hostpc feature - */ - fusbh200_writel(fusbh200, temp | PORT_SUSPEND, status_reg); - set_bit(wIndex, &fusbh200->suspended_ports); - break; - case USB_PORT_FEAT_RESET: - if (temp & PORT_RESUME) - goto error; - /* line status bits may report this as low speed, - * which can be fine if this root hub has a - * transaction translator built in. - */ - fusbh200_dbg(fusbh200, "port %d reset\n", wIndex + 1); - temp |= PORT_RESET; - temp &= ~PORT_PE; - - /* - * caller must wait, then call GetPortStatus - * usb 2.0 spec says 50 ms resets on root - */ - fusbh200->reset_done [wIndex] = jiffies - + msecs_to_jiffies (50); - fusbh200_writel(fusbh200, temp, status_reg); - break; - - /* For downstream facing ports (these): one hub port is put - * into test mode according to USB2 11.24.2.13, then the hub - * must be reset (which for root hub now means rmmod+modprobe, - * or else system reboot). See EHCI 2.3.9 and 4.14 for info - * about the EHCI-specific stuff. - */ - case USB_PORT_FEAT_TEST: - if (!selector || selector > 5) - goto error; - spin_unlock_irqrestore(&fusbh200->lock, flags); - fusbh200_quiesce(fusbh200); - spin_lock_irqsave(&fusbh200->lock, flags); - - /* Put all enabled ports into suspend */ - temp = fusbh200_readl(fusbh200, status_reg) & ~PORT_RWC_BITS; - if (temp & PORT_PE) - fusbh200_writel(fusbh200, temp | PORT_SUSPEND, - status_reg); - - spin_unlock_irqrestore(&fusbh200->lock, flags); - fusbh200_halt(fusbh200); - spin_lock_irqsave(&fusbh200->lock, flags); - - temp = fusbh200_readl(fusbh200, status_reg); - temp |= selector << 16; - fusbh200_writel(fusbh200, temp, status_reg); - break; - - default: - goto error; - } - fusbh200_readl(fusbh200, &fusbh200->regs->command); /* unblock posted writes */ - break; - - default: -error: - /* "stall" on error */ - retval = -EPIPE; - } - spin_unlock_irqrestore (&fusbh200->lock, flags); - return retval; -} - -static void __maybe_unused fusbh200_relinquish_port(struct usb_hcd *hcd, - int portnum) -{ - return; -} - -static int __maybe_unused fusbh200_port_handed_over(struct usb_hcd *hcd, - int portnum) -{ - return 0; -} -/*-------------------------------------------------------------------------*/ -/* - * There's basically three types of memory: - * - data used only by the HCD ... kmalloc is fine - * - async and periodic schedules, shared by HC and HCD ... these - * need to use dma_pool or dma_alloc_coherent - * - driver buffers, read/written by HC ... single shot DMA mapped - * - * There's also "register" data (e.g. PCI or SOC), which is memory mapped. - * No memory seen by this driver is pageable. - */ - -/*-------------------------------------------------------------------------*/ - -/* Allocate the key transfer structures from the previously allocated pool */ - -static inline void fusbh200_qtd_init(struct fusbh200_hcd *fusbh200, struct fusbh200_qtd *qtd, - dma_addr_t dma) -{ - memset (qtd, 0, sizeof *qtd); - qtd->qtd_dma = dma; - qtd->hw_token = cpu_to_hc32(fusbh200, QTD_STS_HALT); - qtd->hw_next = FUSBH200_LIST_END(fusbh200); - qtd->hw_alt_next = FUSBH200_LIST_END(fusbh200); - INIT_LIST_HEAD (&qtd->qtd_list); -} - -static struct fusbh200_qtd *fusbh200_qtd_alloc (struct fusbh200_hcd *fusbh200, gfp_t flags) -{ - struct fusbh200_qtd *qtd; - dma_addr_t dma; - - qtd = dma_pool_alloc (fusbh200->qtd_pool, flags, &dma); - if (qtd != NULL) { - fusbh200_qtd_init(fusbh200, qtd, dma); - } - return qtd; -} - -static inline void fusbh200_qtd_free (struct fusbh200_hcd *fusbh200, struct fusbh200_qtd *qtd) -{ - dma_pool_free (fusbh200->qtd_pool, qtd, qtd->qtd_dma); -} - - -static void qh_destroy(struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh) -{ - /* clean qtds first, and know this is not linked */ - if (!list_empty (&qh->qtd_list) || qh->qh_next.ptr) { - fusbh200_dbg (fusbh200, "unused qh not empty!\n"); - BUG (); - } - if (qh->dummy) - fusbh200_qtd_free (fusbh200, qh->dummy); - dma_pool_free(fusbh200->qh_pool, qh->hw, qh->qh_dma); - kfree(qh); -} - -static struct fusbh200_qh *fusbh200_qh_alloc (struct fusbh200_hcd *fusbh200, gfp_t flags) -{ - struct fusbh200_qh *qh; - dma_addr_t dma; - - qh = kzalloc(sizeof *qh, GFP_ATOMIC); - if (!qh) - goto done; - qh->hw = (struct fusbh200_qh_hw *) - dma_pool_alloc(fusbh200->qh_pool, flags, &dma); - if (!qh->hw) - goto fail; - memset(qh->hw, 0, sizeof *qh->hw); - qh->qh_dma = dma; - // INIT_LIST_HEAD (&qh->qh_list); - INIT_LIST_HEAD (&qh->qtd_list); - - /* dummy td enables safe urb queuing */ - qh->dummy = fusbh200_qtd_alloc (fusbh200, flags); - if (qh->dummy == NULL) { - fusbh200_dbg (fusbh200, "no dummy td\n"); - goto fail1; - } -done: - return qh; -fail1: - dma_pool_free(fusbh200->qh_pool, qh->hw, qh->qh_dma); -fail: - kfree(qh); - return NULL; -} - -/*-------------------------------------------------------------------------*/ - -/* The queue heads and transfer descriptors are managed from pools tied - * to each of the "per device" structures. - * This is the initialisation and cleanup code. - */ - -static void fusbh200_mem_cleanup (struct fusbh200_hcd *fusbh200) -{ - if (fusbh200->async) - qh_destroy(fusbh200, fusbh200->async); - fusbh200->async = NULL; - - if (fusbh200->dummy) - qh_destroy(fusbh200, fusbh200->dummy); - fusbh200->dummy = NULL; - - /* DMA consistent memory and pools */ - if (fusbh200->qtd_pool) - dma_pool_destroy (fusbh200->qtd_pool); - fusbh200->qtd_pool = NULL; - - if (fusbh200->qh_pool) { - dma_pool_destroy (fusbh200->qh_pool); - fusbh200->qh_pool = NULL; - } - - if (fusbh200->itd_pool) - dma_pool_destroy (fusbh200->itd_pool); - fusbh200->itd_pool = NULL; - - if (fusbh200->periodic) - dma_free_coherent (fusbh200_to_hcd(fusbh200)->self.controller, - fusbh200->periodic_size * sizeof (u32), - fusbh200->periodic, fusbh200->periodic_dma); - fusbh200->periodic = NULL; - - /* shadow periodic table */ - kfree(fusbh200->pshadow); - fusbh200->pshadow = NULL; -} - -/* remember to add cleanup code (above) if you add anything here */ -static int fusbh200_mem_init (struct fusbh200_hcd *fusbh200, gfp_t flags) -{ - int i; - - /* QTDs for control/bulk/intr transfers */ - fusbh200->qtd_pool = dma_pool_create ("fusbh200_qtd", - fusbh200_to_hcd(fusbh200)->self.controller, - sizeof (struct fusbh200_qtd), - 32 /* byte alignment (for hw parts) */, - 4096 /* can't cross 4K */); - if (!fusbh200->qtd_pool) { - goto fail; - } - - /* QHs for control/bulk/intr transfers */ - fusbh200->qh_pool = dma_pool_create ("fusbh200_qh", - fusbh200_to_hcd(fusbh200)->self.controller, - sizeof(struct fusbh200_qh_hw), - 32 /* byte alignment (for hw parts) */, - 4096 /* can't cross 4K */); - if (!fusbh200->qh_pool) { - goto fail; - } - fusbh200->async = fusbh200_qh_alloc (fusbh200, flags); - if (!fusbh200->async) { - goto fail; - } - - /* ITD for high speed ISO transfers */ - fusbh200->itd_pool = dma_pool_create ("fusbh200_itd", - fusbh200_to_hcd(fusbh200)->self.controller, - sizeof (struct fusbh200_itd), - 64 /* byte alignment (for hw parts) */, - 4096 /* can't cross 4K */); - if (!fusbh200->itd_pool) { - goto fail; - } - - /* Hardware periodic table */ - fusbh200->periodic = (__le32 *) - dma_alloc_coherent (fusbh200_to_hcd(fusbh200)->self.controller, - fusbh200->periodic_size * sizeof(__le32), - &fusbh200->periodic_dma, 0); - if (fusbh200->periodic == NULL) { - goto fail; - } - - for (i = 0; i < fusbh200->periodic_size; i++) - fusbh200->periodic[i] = FUSBH200_LIST_END(fusbh200); - - /* software shadow of hardware table */ - fusbh200->pshadow = kcalloc(fusbh200->periodic_size, sizeof(void *), flags); - if (fusbh200->pshadow != NULL) - return 0; - -fail: - fusbh200_dbg (fusbh200, "couldn't init memory\n"); - fusbh200_mem_cleanup (fusbh200); - return -ENOMEM; -} -/*-------------------------------------------------------------------------*/ -/* - * EHCI hardware queue manipulation ... the core. QH/QTD manipulation. - * - * Control, bulk, and interrupt traffic all use "qh" lists. They list "qtd" - * entries describing USB transactions, max 16-20kB/entry (with 4kB-aligned - * buffers needed for the larger number). We use one QH per endpoint, queue - * multiple urbs (all three types) per endpoint. URBs may need several qtds. - * - * ISO traffic uses "ISO TD" (itd) records, and (along with - * interrupts) needs careful scheduling. Performance improvements can be - * an ongoing challenge. That's in "ehci-sched.c". - * - * USB 1.1 devices are handled (a) by "companion" OHCI or UHCI root hubs, - * or otherwise through transaction translators (TTs) in USB 2.0 hubs using - * (b) special fields in qh entries or (c) split iso entries. TTs will - * buffer low/full speed data so the host collects it at high speed. - */ - -/*-------------------------------------------------------------------------*/ - -/* fill a qtd, returning how much of the buffer we were able to queue up */ - -static int -qtd_fill(struct fusbh200_hcd *fusbh200, struct fusbh200_qtd *qtd, dma_addr_t buf, - size_t len, int token, int maxpacket) -{ - int i, count; - u64 addr = buf; - - /* one buffer entry per 4K ... first might be short or unaligned */ - qtd->hw_buf[0] = cpu_to_hc32(fusbh200, (u32)addr); - qtd->hw_buf_hi[0] = cpu_to_hc32(fusbh200, (u32)(addr >> 32)); - count = 0x1000 - (buf & 0x0fff); /* rest of that page */ - if (likely (len < count)) /* ... iff needed */ - count = len; - else { - buf += 0x1000; - buf &= ~0x0fff; - - /* per-qtd limit: from 16K to 20K (best alignment) */ - for (i = 1; count < len && i < 5; i++) { - addr = buf; - qtd->hw_buf[i] = cpu_to_hc32(fusbh200, (u32)addr); - qtd->hw_buf_hi[i] = cpu_to_hc32(fusbh200, - (u32)(addr >> 32)); - buf += 0x1000; - if ((count + 0x1000) < len) - count += 0x1000; - else - count = len; - } - - /* short packets may only terminate transfers */ - if (count != len) - count -= (count % maxpacket); - } - qtd->hw_token = cpu_to_hc32(fusbh200, (count << 16) | token); - qtd->length = count; - - return count; -} - -/*-------------------------------------------------------------------------*/ - -static inline void -qh_update (struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh, struct fusbh200_qtd *qtd) -{ - struct fusbh200_qh_hw *hw = qh->hw; - - /* writes to an active overlay are unsafe */ - BUG_ON(qh->qh_state != QH_STATE_IDLE); - - hw->hw_qtd_next = QTD_NEXT(fusbh200, qtd->qtd_dma); - hw->hw_alt_next = FUSBH200_LIST_END(fusbh200); - - /* Except for control endpoints, we make hardware maintain data - * toggle (like OHCI) ... here (re)initialize the toggle in the QH, - * and set the pseudo-toggle in udev. Only usb_clear_halt() will - * ever clear it. - */ - if (!(hw->hw_info1 & cpu_to_hc32(fusbh200, QH_TOGGLE_CTL))) { - unsigned is_out, epnum; - - is_out = qh->is_out; - epnum = (hc32_to_cpup(fusbh200, &hw->hw_info1) >> 8) & 0x0f; - if (unlikely (!usb_gettoggle (qh->dev, epnum, is_out))) { - hw->hw_token &= ~cpu_to_hc32(fusbh200, QTD_TOGGLE); - usb_settoggle (qh->dev, epnum, is_out, 1); - } - } - - hw->hw_token &= cpu_to_hc32(fusbh200, QTD_TOGGLE | QTD_STS_PING); -} - -/* if it weren't for a common silicon quirk (writing the dummy into the qh - * overlay, so qh->hw_token wrongly becomes inactive/halted), only fault - * recovery (including urb dequeue) would need software changes to a QH... - */ -static void -qh_refresh (struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh) -{ - struct fusbh200_qtd *qtd; - - if (list_empty (&qh->qtd_list)) - qtd = qh->dummy; - else { - qtd = list_entry (qh->qtd_list.next, - struct fusbh200_qtd, qtd_list); - /* - * first qtd may already be partially processed. - * If we come here during unlink, the QH overlay region - * might have reference to the just unlinked qtd. The - * qtd is updated in qh_completions(). Update the QH - * overlay here. - */ - if (cpu_to_hc32(fusbh200, qtd->qtd_dma) == qh->hw->hw_current) { - qh->hw->hw_qtd_next = qtd->hw_next; - qtd = NULL; - } - } - - if (qtd) - qh_update (fusbh200, qh, qtd); -} - -/*-------------------------------------------------------------------------*/ - -static void qh_link_async(struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh); - -static void fusbh200_clear_tt_buffer_complete(struct usb_hcd *hcd, - struct usb_host_endpoint *ep) -{ - struct fusbh200_hcd *fusbh200 = hcd_to_fusbh200(hcd); - struct fusbh200_qh *qh = ep->hcpriv; - unsigned long flags; - - spin_lock_irqsave(&fusbh200->lock, flags); - qh->clearing_tt = 0; - if (qh->qh_state == QH_STATE_IDLE && !list_empty(&qh->qtd_list) - && fusbh200->rh_state == FUSBH200_RH_RUNNING) - qh_link_async(fusbh200, qh); - spin_unlock_irqrestore(&fusbh200->lock, flags); -} - -static void fusbh200_clear_tt_buffer(struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh, - struct urb *urb, u32 token) -{ - - /* If an async split transaction gets an error or is unlinked, - * the TT buffer may be left in an indeterminate state. We - * have to clear the TT buffer. - * - * Note: this routine is never called for Isochronous transfers. - */ - if (urb->dev->tt && !usb_pipeint(urb->pipe) && !qh->clearing_tt) { - struct usb_device *tt = urb->dev->tt->hub; - - dev_dbg(&tt->dev, - "clear tt buffer port %d, a%d ep%d t%08x\n", - urb->dev->ttport, urb->dev->devnum, - usb_pipeendpoint(urb->pipe), token); - - if (urb->dev->tt->hub != - fusbh200_to_hcd(fusbh200)->self.root_hub) { - if (usb_hub_clear_tt_buffer(urb) == 0) - qh->clearing_tt = 1; - } - } -} - -static int qtd_copy_status ( - struct fusbh200_hcd *fusbh200, - struct urb *urb, - size_t length, - u32 token -) -{ - int status = -EINPROGRESS; - - /* count IN/OUT bytes, not SETUP (even short packets) */ - if (likely (QTD_PID (token) != 2)) - urb->actual_length += length - QTD_LENGTH (token); - - /* don't modify error codes */ - if (unlikely(urb->unlinked)) - return status; - - /* force cleanup after short read; not always an error */ - if (unlikely (IS_SHORT_READ (token))) - status = -EREMOTEIO; - - /* serious "can't proceed" faults reported by the hardware */ - if (token & QTD_STS_HALT) { - if (token & QTD_STS_BABBLE) { - /* FIXME "must" disable babbling device's port too */ - status = -EOVERFLOW; - /* CERR nonzero + halt --> stall */ - } else if (QTD_CERR(token)) { - status = -EPIPE; - - /* In theory, more than one of the following bits can be set - * since they are sticky and the transaction is retried. - * Which to test first is rather arbitrary. - */ - } else if (token & QTD_STS_MMF) { - /* fs/ls interrupt xfer missed the complete-split */ - status = -EPROTO; - } else if (token & QTD_STS_DBE) { - status = (QTD_PID (token) == 1) /* IN ? */ - ? -ENOSR /* hc couldn't read data */ - : -ECOMM; /* hc couldn't write data */ - } else if (token & QTD_STS_XACT) { - /* timeout, bad CRC, wrong PID, etc */ - fusbh200_dbg(fusbh200, "devpath %s ep%d%s 3strikes\n", - urb->dev->devpath, - usb_pipeendpoint(urb->pipe), - usb_pipein(urb->pipe) ? "in" : "out"); - status = -EPROTO; - } else { /* unknown */ - status = -EPROTO; - } - - fusbh200_dbg(fusbh200, - "dev%d ep%d%s qtd token %08x --> status %d\n", - usb_pipedevice (urb->pipe), - usb_pipeendpoint (urb->pipe), - usb_pipein (urb->pipe) ? "in" : "out", - token, status); - } - - return status; -} - -static void -fusbh200_urb_done(struct fusbh200_hcd *fusbh200, struct urb *urb, int status) -__releases(fusbh200->lock) -__acquires(fusbh200->lock) -{ - if (likely (urb->hcpriv != NULL)) { - struct fusbh200_qh *qh = (struct fusbh200_qh *) urb->hcpriv; - - /* S-mask in a QH means it's an interrupt urb */ - if ((qh->hw->hw_info2 & cpu_to_hc32(fusbh200, QH_SMASK)) != 0) { - - /* ... update hc-wide periodic stats (for usbfs) */ - fusbh200_to_hcd(fusbh200)->self.bandwidth_int_reqs--; - } - } - - if (unlikely(urb->unlinked)) { - COUNT(fusbh200->stats.unlink); - } else { - /* report non-error and short read status as zero */ - if (status == -EINPROGRESS || status == -EREMOTEIO) - status = 0; - COUNT(fusbh200->stats.complete); - } - -#ifdef FUSBH200_URB_TRACE - fusbh200_dbg (fusbh200, - "%s %s urb %p ep%d%s status %d len %d/%d\n", - __func__, urb->dev->devpath, urb, - usb_pipeendpoint (urb->pipe), - usb_pipein (urb->pipe) ? "in" : "out", - status, - urb->actual_length, urb->transfer_buffer_length); -#endif - - /* complete() can reenter this HCD */ - usb_hcd_unlink_urb_from_ep(fusbh200_to_hcd(fusbh200), urb); - spin_unlock (&fusbh200->lock); - usb_hcd_giveback_urb(fusbh200_to_hcd(fusbh200), urb, status); - spin_lock (&fusbh200->lock); -} - -static int qh_schedule (struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh); - -/* - * Process and free completed qtds for a qh, returning URBs to drivers. - * Chases up to qh->hw_current. Returns number of completions called, - * indicating how much "real" work we did. - */ -static unsigned -qh_completions (struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh) -{ - struct fusbh200_qtd *last, *end = qh->dummy; - struct list_head *entry, *tmp; - int last_status; - int stopped; - unsigned count = 0; - u8 state; - struct fusbh200_qh_hw *hw = qh->hw; - - if (unlikely (list_empty (&qh->qtd_list))) - return count; - - /* completions (or tasks on other cpus) must never clobber HALT - * till we've gone through and cleaned everything up, even when - * they add urbs to this qh's queue or mark them for unlinking. - * - * NOTE: unlinking expects to be done in queue order. - * - * It's a bug for qh->qh_state to be anything other than - * QH_STATE_IDLE, unless our caller is scan_async() or - * scan_intr(). - */ - state = qh->qh_state; - qh->qh_state = QH_STATE_COMPLETING; - stopped = (state == QH_STATE_IDLE); - - rescan: - last = NULL; - last_status = -EINPROGRESS; - qh->needs_rescan = 0; - - /* remove de-activated QTDs from front of queue. - * after faults (including short reads), cleanup this urb - * then let the queue advance. - * if queue is stopped, handles unlinks. - */ - list_for_each_safe (entry, tmp, &qh->qtd_list) { - struct fusbh200_qtd *qtd; - struct urb *urb; - u32 token = 0; - - qtd = list_entry (entry, struct fusbh200_qtd, qtd_list); - urb = qtd->urb; - - /* clean up any state from previous QTD ...*/ - if (last) { - if (likely (last->urb != urb)) { - fusbh200_urb_done(fusbh200, last->urb, last_status); - count++; - last_status = -EINPROGRESS; - } - fusbh200_qtd_free (fusbh200, last); - last = NULL; - } - - /* ignore urbs submitted during completions we reported */ - if (qtd == end) - break; - - /* hardware copies qtd out of qh overlay */ - rmb (); - token = hc32_to_cpu(fusbh200, qtd->hw_token); - - /* always clean up qtds the hc de-activated */ - retry_xacterr: - if ((token & QTD_STS_ACTIVE) == 0) { - - /* Report Data Buffer Error: non-fatal but useful */ - if (token & QTD_STS_DBE) - fusbh200_dbg(fusbh200, - "detected DataBufferErr for urb %p ep%d%s len %d, qtd %p [qh %p]\n", - urb, - usb_endpoint_num(&urb->ep->desc), - usb_endpoint_dir_in(&urb->ep->desc) ? "in" : "out", - urb->transfer_buffer_length, - qtd, - qh); - - /* on STALL, error, and short reads this urb must - * complete and all its qtds must be recycled. - */ - if ((token & QTD_STS_HALT) != 0) { - - /* retry transaction errors until we - * reach the software xacterr limit - */ - if ((token & QTD_STS_XACT) && - QTD_CERR(token) == 0 && - ++qh->xacterrs < QH_XACTERR_MAX && - !urb->unlinked) { - fusbh200_dbg(fusbh200, - "detected XactErr len %zu/%zu retry %d\n", - qtd->length - QTD_LENGTH(token), qtd->length, qh->xacterrs); - - /* reset the token in the qtd and the - * qh overlay (which still contains - * the qtd) so that we pick up from - * where we left off - */ - token &= ~QTD_STS_HALT; - token |= QTD_STS_ACTIVE | - (FUSBH200_TUNE_CERR << 10); - qtd->hw_token = cpu_to_hc32(fusbh200, - token); - wmb(); - hw->hw_token = cpu_to_hc32(fusbh200, - token); - goto retry_xacterr; - } - stopped = 1; - - /* magic dummy for some short reads; qh won't advance. - * that silicon quirk can kick in with this dummy too. - * - * other short reads won't stop the queue, including - * control transfers (status stage handles that) or - * most other single-qtd reads ... the queue stops if - * URB_SHORT_NOT_OK was set so the driver submitting - * the urbs could clean it up. - */ - } else if (IS_SHORT_READ (token) - && !(qtd->hw_alt_next - & FUSBH200_LIST_END(fusbh200))) { - stopped = 1; - } - - /* stop scanning when we reach qtds the hc is using */ - } else if (likely (!stopped - && fusbh200->rh_state >= FUSBH200_RH_RUNNING)) { - break; - - /* scan the whole queue for unlinks whenever it stops */ - } else { - stopped = 1; - - /* cancel everything if we halt, suspend, etc */ - if (fusbh200->rh_state < FUSBH200_RH_RUNNING) - last_status = -ESHUTDOWN; - - /* this qtd is active; skip it unless a previous qtd - * for its urb faulted, or its urb was canceled. - */ - else if (last_status == -EINPROGRESS && !urb->unlinked) - continue; - - /* qh unlinked; token in overlay may be most current */ - if (state == QH_STATE_IDLE - && cpu_to_hc32(fusbh200, qtd->qtd_dma) - == hw->hw_current) { - token = hc32_to_cpu(fusbh200, hw->hw_token); - - /* An unlink may leave an incomplete - * async transaction in the TT buffer. - * We have to clear it. - */ - fusbh200_clear_tt_buffer(fusbh200, qh, urb, token); - } - } - - /* unless we already know the urb's status, collect qtd status - * and update count of bytes transferred. in common short read - * cases with only one data qtd (including control transfers), - * queue processing won't halt. but with two or more qtds (for - * example, with a 32 KB transfer), when the first qtd gets a - * short read the second must be removed by hand. - */ - if (last_status == -EINPROGRESS) { - last_status = qtd_copy_status(fusbh200, urb, - qtd->length, token); - if (last_status == -EREMOTEIO - && (qtd->hw_alt_next - & FUSBH200_LIST_END(fusbh200))) - last_status = -EINPROGRESS; - - /* As part of low/full-speed endpoint-halt processing - * we must clear the TT buffer (11.17.5). - */ - if (unlikely(last_status != -EINPROGRESS && - last_status != -EREMOTEIO)) { - /* The TT's in some hubs malfunction when they - * receive this request following a STALL (they - * stop sending isochronous packets). Since a - * STALL can't leave the TT buffer in a busy - * state (if you believe Figures 11-48 - 11-51 - * in the USB 2.0 spec), we won't clear the TT - * buffer in this case. Strictly speaking this - * is a violation of the spec. - */ - if (last_status != -EPIPE) - fusbh200_clear_tt_buffer(fusbh200, qh, urb, - token); - } - } - - /* if we're removing something not at the queue head, - * patch the hardware queue pointer. - */ - if (stopped && qtd->qtd_list.prev != &qh->qtd_list) { - last = list_entry (qtd->qtd_list.prev, - struct fusbh200_qtd, qtd_list); - last->hw_next = qtd->hw_next; - } - - /* remove qtd; it's recycled after possible urb completion */ - list_del (&qtd->qtd_list); - last = qtd; - - /* reinit the xacterr counter for the next qtd */ - qh->xacterrs = 0; - } - - /* last urb's completion might still need calling */ - if (likely (last != NULL)) { - fusbh200_urb_done(fusbh200, last->urb, last_status); - count++; - fusbh200_qtd_free (fusbh200, last); - } - - /* Do we need to rescan for URBs dequeued during a giveback? */ - if (unlikely(qh->needs_rescan)) { - /* If the QH is already unlinked, do the rescan now. */ - if (state == QH_STATE_IDLE) - goto rescan; - - /* Otherwise we have to wait until the QH is fully unlinked. - * Our caller will start an unlink if qh->needs_rescan is - * set. But if an unlink has already started, nothing needs - * to be done. - */ - if (state != QH_STATE_LINKED) - qh->needs_rescan = 0; - } - - /* restore original state; caller must unlink or relink */ - qh->qh_state = state; - - /* be sure the hardware's done with the qh before refreshing - * it after fault cleanup, or recovering from silicon wrongly - * overlaying the dummy qtd (which reduces DMA chatter). - */ - if (stopped != 0 || hw->hw_qtd_next == FUSBH200_LIST_END(fusbh200)) { - switch (state) { - case QH_STATE_IDLE: - qh_refresh(fusbh200, qh); - break; - case QH_STATE_LINKED: - /* We won't refresh a QH that's linked (after the HC - * stopped the queue). That avoids a race: - * - HC reads first part of QH; - * - CPU updates that first part and the token; - * - HC reads rest of that QH, including token - * Result: HC gets an inconsistent image, and then - * DMAs to/from the wrong memory (corrupting it). - * - * That should be rare for interrupt transfers, - * except maybe high bandwidth ... - */ - - /* Tell the caller to start an unlink */ - qh->needs_rescan = 1; - break; - /* otherwise, unlink already started */ - } - } - - return count; -} - -/*-------------------------------------------------------------------------*/ - -// high bandwidth multiplier, as encoded in highspeed endpoint descriptors -#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) -// ... and packet size, for any kind of endpoint descriptor -#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff) - -/* - * reverse of qh_urb_transaction: free a list of TDs. - * used for cleanup after errors, before HC sees an URB's TDs. - */ -static void qtd_list_free ( - struct fusbh200_hcd *fusbh200, - struct urb *urb, - struct list_head *qtd_list -) { - struct list_head *entry, *temp; - - list_for_each_safe (entry, temp, qtd_list) { - struct fusbh200_qtd *qtd; - - qtd = list_entry (entry, struct fusbh200_qtd, qtd_list); - list_del (&qtd->qtd_list); - fusbh200_qtd_free (fusbh200, qtd); - } -} - -/* - * create a list of filled qtds for this URB; won't link into qh. - */ -static struct list_head * -qh_urb_transaction ( - struct fusbh200_hcd *fusbh200, - struct urb *urb, - struct list_head *head, - gfp_t flags -) { - struct fusbh200_qtd *qtd, *qtd_prev; - dma_addr_t buf; - int len, this_sg_len, maxpacket; - int is_input; - u32 token; - int i; - struct scatterlist *sg; - - /* - * URBs map to sequences of QTDs: one logical transaction - */ - qtd = fusbh200_qtd_alloc (fusbh200, flags); - if (unlikely (!qtd)) - return NULL; - list_add_tail (&qtd->qtd_list, head); - qtd->urb = urb; - - token = QTD_STS_ACTIVE; - token |= (FUSBH200_TUNE_CERR << 10); - /* for split transactions, SplitXState initialized to zero */ - - len = urb->transfer_buffer_length; - is_input = usb_pipein (urb->pipe); - if (usb_pipecontrol (urb->pipe)) { - /* SETUP pid */ - qtd_fill(fusbh200, qtd, urb->setup_dma, - sizeof (struct usb_ctrlrequest), - token | (2 /* "setup" */ << 8), 8); - - /* ... and always at least one more pid */ - token ^= QTD_TOGGLE; - qtd_prev = qtd; - qtd = fusbh200_qtd_alloc (fusbh200, flags); - if (unlikely (!qtd)) - goto cleanup; - qtd->urb = urb; - qtd_prev->hw_next = QTD_NEXT(fusbh200, qtd->qtd_dma); - list_add_tail (&qtd->qtd_list, head); - - /* for zero length DATA stages, STATUS is always IN */ - if (len == 0) - token |= (1 /* "in" */ << 8); - } - - /* - * data transfer stage: buffer setup - */ - i = urb->num_mapped_sgs; - if (len > 0 && i > 0) { - sg = urb->sg; - buf = sg_dma_address(sg); - - /* urb->transfer_buffer_length may be smaller than the - * size of the scatterlist (or vice versa) - */ - this_sg_len = min_t(int, sg_dma_len(sg), len); - } else { - sg = NULL; - buf = urb->transfer_dma; - this_sg_len = len; - } - - if (is_input) - token |= (1 /* "in" */ << 8); - /* else it's already initted to "out" pid (0 << 8) */ - - maxpacket = max_packet(usb_maxpacket(urb->dev, urb->pipe, !is_input)); - - /* - * buffer gets wrapped in one or more qtds; - * last one may be "short" (including zero len) - * and may serve as a control status ack - */ - for (;;) { - int this_qtd_len; - - this_qtd_len = qtd_fill(fusbh200, qtd, buf, this_sg_len, token, - maxpacket); - this_sg_len -= this_qtd_len; - len -= this_qtd_len; - buf += this_qtd_len; - - /* - * short reads advance to a "magic" dummy instead of the next - * qtd ... that forces the queue to stop, for manual cleanup. - * (this will usually be overridden later.) - */ - if (is_input) - qtd->hw_alt_next = fusbh200->async->hw->hw_alt_next; - - /* qh makes control packets use qtd toggle; maybe switch it */ - if ((maxpacket & (this_qtd_len + (maxpacket - 1))) == 0) - token ^= QTD_TOGGLE; - - if (likely(this_sg_len <= 0)) { - if (--i <= 0 || len <= 0) - break; - sg = sg_next(sg); - buf = sg_dma_address(sg); - this_sg_len = min_t(int, sg_dma_len(sg), len); - } - - qtd_prev = qtd; - qtd = fusbh200_qtd_alloc (fusbh200, flags); - if (unlikely (!qtd)) - goto cleanup; - qtd->urb = urb; - qtd_prev->hw_next = QTD_NEXT(fusbh200, qtd->qtd_dma); - list_add_tail (&qtd->qtd_list, head); - } - - /* - * unless the caller requires manual cleanup after short reads, - * have the alt_next mechanism keep the queue running after the - * last data qtd (the only one, for control and most other cases). - */ - if (likely ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0 - || usb_pipecontrol (urb->pipe))) - qtd->hw_alt_next = FUSBH200_LIST_END(fusbh200); - - /* - * control requests may need a terminating data "status" ack; - * other OUT ones may need a terminating short packet - * (zero length). - */ - if (likely (urb->transfer_buffer_length != 0)) { - int one_more = 0; - - if (usb_pipecontrol (urb->pipe)) { - one_more = 1; - token ^= 0x0100; /* "in" <--> "out" */ - token |= QTD_TOGGLE; /* force DATA1 */ - } else if (usb_pipeout(urb->pipe) - && (urb->transfer_flags & URB_ZERO_PACKET) - && !(urb->transfer_buffer_length % maxpacket)) { - one_more = 1; - } - if (one_more) { - qtd_prev = qtd; - qtd = fusbh200_qtd_alloc (fusbh200, flags); - if (unlikely (!qtd)) - goto cleanup; - qtd->urb = urb; - qtd_prev->hw_next = QTD_NEXT(fusbh200, qtd->qtd_dma); - list_add_tail (&qtd->qtd_list, head); - - /* never any data in such packets */ - qtd_fill(fusbh200, qtd, 0, 0, token, 0); - } - } - - /* by default, enable interrupt on urb completion */ - if (likely (!(urb->transfer_flags & URB_NO_INTERRUPT))) - qtd->hw_token |= cpu_to_hc32(fusbh200, QTD_IOC); - return head; - -cleanup: - qtd_list_free (fusbh200, urb, head); - return NULL; -} - -/*-------------------------------------------------------------------------*/ - -// Would be best to create all qh's from config descriptors, -// when each interface/altsetting is established. Unlink -// any previous qh and cancel its urbs first; endpoints are -// implicitly reset then (data toggle too). -// That'd mean updating how usbcore talks to HCDs. (2.7?) - - -/* - * Each QH holds a qtd list; a QH is used for everything except iso. - * - * For interrupt urbs, the scheduler must set the microframe scheduling - * mask(s) each time the QH gets scheduled. For highspeed, that's - * just one microframe in the s-mask. For split interrupt transactions - * there are additional complications: c-mask, maybe FSTNs. - */ -static struct fusbh200_qh * -qh_make ( - struct fusbh200_hcd *fusbh200, - struct urb *urb, - gfp_t flags -) { - struct fusbh200_qh *qh = fusbh200_qh_alloc (fusbh200, flags); - u32 info1 = 0, info2 = 0; - int is_input, type; - int maxp = 0; - struct usb_tt *tt = urb->dev->tt; - struct fusbh200_qh_hw *hw; - - if (!qh) - return qh; - - /* - * init endpoint/device data for this QH - */ - info1 |= usb_pipeendpoint (urb->pipe) << 8; - info1 |= usb_pipedevice (urb->pipe) << 0; - - is_input = usb_pipein (urb->pipe); - type = usb_pipetype (urb->pipe); - maxp = usb_maxpacket (urb->dev, urb->pipe, !is_input); - - /* 1024 byte maxpacket is a hardware ceiling. High bandwidth - * acts like up to 3KB, but is built from smaller packets. - */ - if (max_packet(maxp) > 1024) { - fusbh200_dbg(fusbh200, "bogus qh maxpacket %d\n", max_packet(maxp)); - goto done; - } - - /* Compute interrupt scheduling parameters just once, and save. - * - allowing for high bandwidth, how many nsec/uframe are used? - * - split transactions need a second CSPLIT uframe; same question - * - splits also need a schedule gap (for full/low speed I/O) - * - qh has a polling interval - * - * For control/bulk requests, the HC or TT handles these. - */ - if (type == PIPE_INTERRUPT) { - qh->usecs = NS_TO_US(usb_calc_bus_time(USB_SPEED_HIGH, - is_input, 0, - hb_mult(maxp) * max_packet(maxp))); - qh->start = NO_FRAME; - - if (urb->dev->speed == USB_SPEED_HIGH) { - qh->c_usecs = 0; - qh->gap_uf = 0; - - qh->period = urb->interval >> 3; - if (qh->period == 0 && urb->interval != 1) { - /* NOTE interval 2 or 4 uframes could work. - * But interval 1 scheduling is simpler, and - * includes high bandwidth. - */ - urb->interval = 1; - } else if (qh->period > fusbh200->periodic_size) { - qh->period = fusbh200->periodic_size; - urb->interval = qh->period << 3; - } - } else { - int think_time; - - /* gap is f(FS/LS transfer times) */ - qh->gap_uf = 1 + usb_calc_bus_time (urb->dev->speed, - is_input, 0, maxp) / (125 * 1000); - - /* FIXME this just approximates SPLIT/CSPLIT times */ - if (is_input) { // SPLIT, gap, CSPLIT+DATA - qh->c_usecs = qh->usecs + HS_USECS (0); - qh->usecs = HS_USECS (1); - } else { // SPLIT+DATA, gap, CSPLIT - qh->usecs += HS_USECS (1); - qh->c_usecs = HS_USECS (0); - } - - think_time = tt ? tt->think_time : 0; - qh->tt_usecs = NS_TO_US (think_time + - usb_calc_bus_time (urb->dev->speed, - is_input, 0, max_packet (maxp))); - qh->period = urb->interval; - if (qh->period > fusbh200->periodic_size) { - qh->period = fusbh200->periodic_size; - urb->interval = qh->period; - } - } - } - - /* support for tt scheduling, and access to toggles */ - qh->dev = urb->dev; - - /* using TT? */ - switch (urb->dev->speed) { - case USB_SPEED_LOW: - info1 |= QH_LOW_SPEED; - /* FALL THROUGH */ - - case USB_SPEED_FULL: - /* EPS 0 means "full" */ - if (type != PIPE_INTERRUPT) - info1 |= (FUSBH200_TUNE_RL_TT << 28); - if (type == PIPE_CONTROL) { - info1 |= QH_CONTROL_EP; /* for TT */ - info1 |= QH_TOGGLE_CTL; /* toggle from qtd */ - } - info1 |= maxp << 16; - - info2 |= (FUSBH200_TUNE_MULT_TT << 30); - - /* Some Freescale processors have an erratum in which the - * port number in the queue head was 0..N-1 instead of 1..N. - */ - if (fusbh200_has_fsl_portno_bug(fusbh200)) - info2 |= (urb->dev->ttport-1) << 23; - else - info2 |= urb->dev->ttport << 23; - - /* set the address of the TT; for TDI's integrated - * root hub tt, leave it zeroed. - */ - if (tt && tt->hub != fusbh200_to_hcd(fusbh200)->self.root_hub) - info2 |= tt->hub->devnum << 16; - - /* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */ - - break; - - case USB_SPEED_HIGH: /* no TT involved */ - info1 |= QH_HIGH_SPEED; - if (type == PIPE_CONTROL) { - info1 |= (FUSBH200_TUNE_RL_HS << 28); - info1 |= 64 << 16; /* usb2 fixed maxpacket */ - info1 |= QH_TOGGLE_CTL; /* toggle from qtd */ - info2 |= (FUSBH200_TUNE_MULT_HS << 30); - } else if (type == PIPE_BULK) { - info1 |= (FUSBH200_TUNE_RL_HS << 28); - /* The USB spec says that high speed bulk endpoints - * always use 512 byte maxpacket. But some device - * vendors decided to ignore that, and MSFT is happy - * to help them do so. So now people expect to use - * such nonconformant devices with Linux too; sigh. - */ - info1 |= max_packet(maxp) << 16; - info2 |= (FUSBH200_TUNE_MULT_HS << 30); - } else { /* PIPE_INTERRUPT */ - info1 |= max_packet (maxp) << 16; - info2 |= hb_mult (maxp) << 30; - } - break; - default: - fusbh200_dbg(fusbh200, "bogus dev %p speed %d\n", urb->dev, - urb->dev->speed); -done: - qh_destroy(fusbh200, qh); - return NULL; - } - - /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */ - - /* init as live, toggle clear, advance to dummy */ - qh->qh_state = QH_STATE_IDLE; - hw = qh->hw; - hw->hw_info1 = cpu_to_hc32(fusbh200, info1); - hw->hw_info2 = cpu_to_hc32(fusbh200, info2); - qh->is_out = !is_input; - usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1); - qh_refresh (fusbh200, qh); - return qh; -} - -/*-------------------------------------------------------------------------*/ - -static void enable_async(struct fusbh200_hcd *fusbh200) -{ - if (fusbh200->async_count++) - return; - - /* Stop waiting to turn off the async schedule */ - fusbh200->enabled_hrtimer_events &= ~BIT(FUSBH200_HRTIMER_DISABLE_ASYNC); - - /* Don't start the schedule until ASS is 0 */ - fusbh200_poll_ASS(fusbh200); - turn_on_io_watchdog(fusbh200); -} - -static void disable_async(struct fusbh200_hcd *fusbh200) -{ - if (--fusbh200->async_count) - return; - - /* The async schedule and async_unlink list are supposed to be empty */ - WARN_ON(fusbh200->async->qh_next.qh || fusbh200->async_unlink); - - /* Don't turn off the schedule until ASS is 1 */ - fusbh200_poll_ASS(fusbh200); -} - -/* move qh (and its qtds) onto async queue; maybe enable queue. */ - -static void qh_link_async (struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh) -{ - __hc32 dma = QH_NEXT(fusbh200, qh->qh_dma); - struct fusbh200_qh *head; - - /* Don't link a QH if there's a Clear-TT-Buffer pending */ - if (unlikely(qh->clearing_tt)) - return; - - WARN_ON(qh->qh_state != QH_STATE_IDLE); - - /* clear halt and/or toggle; and maybe recover from silicon quirk */ - qh_refresh(fusbh200, qh); - - /* splice right after start */ - head = fusbh200->async; - qh->qh_next = head->qh_next; - qh->hw->hw_next = head->hw->hw_next; - wmb (); - - head->qh_next.qh = qh; - head->hw->hw_next = dma; - - qh->xacterrs = 0; - qh->qh_state = QH_STATE_LINKED; - /* qtd completions reported later by interrupt */ - - enable_async(fusbh200); -} - -/*-------------------------------------------------------------------------*/ - -/* - * For control/bulk/interrupt, return QH with these TDs appended. - * Allocates and initializes the QH if necessary. - * Returns null if it can't allocate a QH it needs to. - * If the QH has TDs (urbs) already, that's great. - */ -static struct fusbh200_qh *qh_append_tds ( - struct fusbh200_hcd *fusbh200, - struct urb *urb, - struct list_head *qtd_list, - int epnum, - void **ptr -) -{ - struct fusbh200_qh *qh = NULL; - __hc32 qh_addr_mask = cpu_to_hc32(fusbh200, 0x7f); - - qh = (struct fusbh200_qh *) *ptr; - if (unlikely (qh == NULL)) { - /* can't sleep here, we have fusbh200->lock... */ - qh = qh_make (fusbh200, urb, GFP_ATOMIC); - *ptr = qh; - } - if (likely (qh != NULL)) { - struct fusbh200_qtd *qtd; - - if (unlikely (list_empty (qtd_list))) - qtd = NULL; - else - qtd = list_entry (qtd_list->next, struct fusbh200_qtd, - qtd_list); - - /* control qh may need patching ... */ - if (unlikely (epnum == 0)) { - - /* usb_reset_device() briefly reverts to address 0 */ - if (usb_pipedevice (urb->pipe) == 0) - qh->hw->hw_info1 &= ~qh_addr_mask; - } - - /* just one way to queue requests: swap with the dummy qtd. - * only hc or qh_refresh() ever modify the overlay. - */ - if (likely (qtd != NULL)) { - struct fusbh200_qtd *dummy; - dma_addr_t dma; - __hc32 token; - - /* to avoid racing the HC, use the dummy td instead of - * the first td of our list (becomes new dummy). both - * tds stay deactivated until we're done, when the - * HC is allowed to fetch the old dummy (4.10.2). - */ - token = qtd->hw_token; - qtd->hw_token = HALT_BIT(fusbh200); - - dummy = qh->dummy; - - dma = dummy->qtd_dma; - *dummy = *qtd; - dummy->qtd_dma = dma; - - list_del (&qtd->qtd_list); - list_add (&dummy->qtd_list, qtd_list); - list_splice_tail(qtd_list, &qh->qtd_list); - - fusbh200_qtd_init(fusbh200, qtd, qtd->qtd_dma); - qh->dummy = qtd; - - /* hc must see the new dummy at list end */ - dma = qtd->qtd_dma; - qtd = list_entry (qh->qtd_list.prev, - struct fusbh200_qtd, qtd_list); - qtd->hw_next = QTD_NEXT(fusbh200, dma); - - /* let the hc process these next qtds */ - wmb (); - dummy->hw_token = token; - - urb->hcpriv = qh; - } - } - return qh; -} - -/*-------------------------------------------------------------------------*/ - -static int -submit_async ( - struct fusbh200_hcd *fusbh200, - struct urb *urb, - struct list_head *qtd_list, - gfp_t mem_flags -) { - int epnum; - unsigned long flags; - struct fusbh200_qh *qh = NULL; - int rc; - - epnum = urb->ep->desc.bEndpointAddress; - -#ifdef FUSBH200_URB_TRACE - { - struct fusbh200_qtd *qtd; - qtd = list_entry(qtd_list->next, struct fusbh200_qtd, qtd_list); - fusbh200_dbg(fusbh200, - "%s %s urb %p ep%d%s len %d, qtd %p [qh %p]\n", - __func__, urb->dev->devpath, urb, - epnum & 0x0f, (epnum & USB_DIR_IN) ? "in" : "out", - urb->transfer_buffer_length, - qtd, urb->ep->hcpriv); - } -#endif - - spin_lock_irqsave (&fusbh200->lock, flags); - if (unlikely(!HCD_HW_ACCESSIBLE(fusbh200_to_hcd(fusbh200)))) { - rc = -ESHUTDOWN; - goto done; - } - rc = usb_hcd_link_urb_to_ep(fusbh200_to_hcd(fusbh200), urb); - if (unlikely(rc)) - goto done; - - qh = qh_append_tds(fusbh200, urb, qtd_list, epnum, &urb->ep->hcpriv); - if (unlikely(qh == NULL)) { - usb_hcd_unlink_urb_from_ep(fusbh200_to_hcd(fusbh200), urb); - rc = -ENOMEM; - goto done; - } - - /* Control/bulk operations through TTs don't need scheduling, - * the HC and TT handle it when the TT has a buffer ready. - */ - if (likely (qh->qh_state == QH_STATE_IDLE)) - qh_link_async(fusbh200, qh); - done: - spin_unlock_irqrestore (&fusbh200->lock, flags); - if (unlikely (qh == NULL)) - qtd_list_free (fusbh200, urb, qtd_list); - return rc; -} - -/*-------------------------------------------------------------------------*/ - -static void single_unlink_async(struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh) -{ - struct fusbh200_qh *prev; - - /* Add to the end of the list of QHs waiting for the next IAAD */ - qh->qh_state = QH_STATE_UNLINK; - if (fusbh200->async_unlink) - fusbh200->async_unlink_last->unlink_next = qh; - else - fusbh200->async_unlink = qh; - fusbh200->async_unlink_last = qh; - - /* Unlink it from the schedule */ - prev = fusbh200->async; - while (prev->qh_next.qh != qh) - prev = prev->qh_next.qh; - - prev->hw->hw_next = qh->hw->hw_next; - prev->qh_next = qh->qh_next; - if (fusbh200->qh_scan_next == qh) - fusbh200->qh_scan_next = qh->qh_next.qh; -} - -static void start_iaa_cycle(struct fusbh200_hcd *fusbh200, bool nested) -{ - /* - * Do nothing if an IAA cycle is already running or - * if one will be started shortly. - */ - if (fusbh200->async_iaa || fusbh200->async_unlinking) - return; - - /* Do all the waiting QHs at once */ - fusbh200->async_iaa = fusbh200->async_unlink; - fusbh200->async_unlink = NULL; - - /* If the controller isn't running, we don't have to wait for it */ - if (unlikely(fusbh200->rh_state < FUSBH200_RH_RUNNING)) { - if (!nested) /* Avoid recursion */ - end_unlink_async(fusbh200); - - /* Otherwise start a new IAA cycle */ - } else if (likely(fusbh200->rh_state == FUSBH200_RH_RUNNING)) { - /* Make sure the unlinks are all visible to the hardware */ - wmb(); - - fusbh200_writel(fusbh200, fusbh200->command | CMD_IAAD, - &fusbh200->regs->command); - fusbh200_readl(fusbh200, &fusbh200->regs->command); - fusbh200_enable_event(fusbh200, FUSBH200_HRTIMER_IAA_WATCHDOG, true); - } -} - -/* the async qh for the qtds being unlinked are now gone from the HC */ - -static void end_unlink_async(struct fusbh200_hcd *fusbh200) -{ - struct fusbh200_qh *qh; - - /* Process the idle QHs */ - restart: - fusbh200->async_unlinking = true; - while (fusbh200->async_iaa) { - qh = fusbh200->async_iaa; - fusbh200->async_iaa = qh->unlink_next; - qh->unlink_next = NULL; - - qh->qh_state = QH_STATE_IDLE; - qh->qh_next.qh = NULL; - - qh_completions(fusbh200, qh); - if (!list_empty(&qh->qtd_list) && - fusbh200->rh_state == FUSBH200_RH_RUNNING) - qh_link_async(fusbh200, qh); - disable_async(fusbh200); - } - fusbh200->async_unlinking = false; - - /* Start a new IAA cycle if any QHs are waiting for it */ - if (fusbh200->async_unlink) { - start_iaa_cycle(fusbh200, true); - if (unlikely(fusbh200->rh_state < FUSBH200_RH_RUNNING)) - goto restart; - } -} - -static void unlink_empty_async(struct fusbh200_hcd *fusbh200) -{ - struct fusbh200_qh *qh, *next; - bool stopped = (fusbh200->rh_state < FUSBH200_RH_RUNNING); - bool check_unlinks_later = false; - - /* Unlink all the async QHs that have been empty for a timer cycle */ - next = fusbh200->async->qh_next.qh; - while (next) { - qh = next; - next = qh->qh_next.qh; - - if (list_empty(&qh->qtd_list) && - qh->qh_state == QH_STATE_LINKED) { - if (!stopped && qh->unlink_cycle == - fusbh200->async_unlink_cycle) - check_unlinks_later = true; - else - single_unlink_async(fusbh200, qh); - } - } - - /* Start a new IAA cycle if any QHs are waiting for it */ - if (fusbh200->async_unlink) - start_iaa_cycle(fusbh200, false); - - /* QHs that haven't been empty for long enough will be handled later */ - if (check_unlinks_later) { - fusbh200_enable_event(fusbh200, FUSBH200_HRTIMER_ASYNC_UNLINKS, true); - ++fusbh200->async_unlink_cycle; - } -} - -/* makes sure the async qh will become idle */ -/* caller must own fusbh200->lock */ - -static void start_unlink_async(struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh) -{ - /* - * If the QH isn't linked then there's nothing we can do - * unless we were called during a giveback, in which case - * qh_completions() has to deal with it. - */ - if (qh->qh_state != QH_STATE_LINKED) { - if (qh->qh_state == QH_STATE_COMPLETING) - qh->needs_rescan = 1; - return; - } - - single_unlink_async(fusbh200, qh); - start_iaa_cycle(fusbh200, false); -} - -/*-------------------------------------------------------------------------*/ - -static void scan_async (struct fusbh200_hcd *fusbh200) -{ - struct fusbh200_qh *qh; - bool check_unlinks_later = false; - - fusbh200->qh_scan_next = fusbh200->async->qh_next.qh; - while (fusbh200->qh_scan_next) { - qh = fusbh200->qh_scan_next; - fusbh200->qh_scan_next = qh->qh_next.qh; - rescan: - /* clean any finished work for this qh */ - if (!list_empty(&qh->qtd_list)) { - int temp; - - /* - * Unlinks could happen here; completion reporting - * drops the lock. That's why fusbh200->qh_scan_next - * always holds the next qh to scan; if the next qh - * gets unlinked then fusbh200->qh_scan_next is adjusted - * in single_unlink_async(). - */ - temp = qh_completions(fusbh200, qh); - if (qh->needs_rescan) { - start_unlink_async(fusbh200, qh); - } else if (list_empty(&qh->qtd_list) - && qh->qh_state == QH_STATE_LINKED) { - qh->unlink_cycle = fusbh200->async_unlink_cycle; - check_unlinks_later = true; - } else if (temp != 0) - goto rescan; - } - } - - /* - * Unlink empty entries, reducing DMA usage as well - * as HCD schedule-scanning costs. Delay for any qh - * we just scanned, there's a not-unusual case that it - * doesn't stay idle for long. - */ - if (check_unlinks_later && fusbh200->rh_state == FUSBH200_RH_RUNNING && - !(fusbh200->enabled_hrtimer_events & - BIT(FUSBH200_HRTIMER_ASYNC_UNLINKS))) { - fusbh200_enable_event(fusbh200, FUSBH200_HRTIMER_ASYNC_UNLINKS, true); - ++fusbh200->async_unlink_cycle; - } -} -/*-------------------------------------------------------------------------*/ -/* - * EHCI scheduled transaction support: interrupt, iso, split iso - * These are called "periodic" transactions in the EHCI spec. - * - * Note that for interrupt transfers, the QH/QTD manipulation is shared - * with the "asynchronous" transaction support (control/bulk transfers). - * The only real difference is in how interrupt transfers are scheduled. - * - * For ISO, we make an "iso_stream" head to serve the same role as a QH. - * It keeps track of every ITD (or SITD) that's linked, and holds enough - * pre-calculated schedule data to make appending to the queue be quick. - */ - -static int fusbh200_get_frame (struct usb_hcd *hcd); - -/*-------------------------------------------------------------------------*/ - -/* - * periodic_next_shadow - return "next" pointer on shadow list - * @periodic: host pointer to qh/itd - * @tag: hardware tag for type of this record - */ -static union fusbh200_shadow * -periodic_next_shadow(struct fusbh200_hcd *fusbh200, union fusbh200_shadow *periodic, - __hc32 tag) -{ - switch (hc32_to_cpu(fusbh200, tag)) { - case Q_TYPE_QH: - return &periodic->qh->qh_next; - case Q_TYPE_FSTN: - return &periodic->fstn->fstn_next; - default: - return &periodic->itd->itd_next; - } -} - -static __hc32 * -shadow_next_periodic(struct fusbh200_hcd *fusbh200, union fusbh200_shadow *periodic, - __hc32 tag) -{ - switch (hc32_to_cpu(fusbh200, tag)) { - /* our fusbh200_shadow.qh is actually software part */ - case Q_TYPE_QH: - return &periodic->qh->hw->hw_next; - /* others are hw parts */ - default: - return periodic->hw_next; - } -} - -/* caller must hold fusbh200->lock */ -static void periodic_unlink (struct fusbh200_hcd *fusbh200, unsigned frame, void *ptr) -{ - union fusbh200_shadow *prev_p = &fusbh200->pshadow[frame]; - __hc32 *hw_p = &fusbh200->periodic[frame]; - union fusbh200_shadow here = *prev_p; - - /* find predecessor of "ptr"; hw and shadow lists are in sync */ - while (here.ptr && here.ptr != ptr) { - prev_p = periodic_next_shadow(fusbh200, prev_p, - Q_NEXT_TYPE(fusbh200, *hw_p)); - hw_p = shadow_next_periodic(fusbh200, &here, - Q_NEXT_TYPE(fusbh200, *hw_p)); - here = *prev_p; - } - /* an interrupt entry (at list end) could have been shared */ - if (!here.ptr) - return; - - /* update shadow and hardware lists ... the old "next" pointers - * from ptr may still be in use, the caller updates them. - */ - *prev_p = *periodic_next_shadow(fusbh200, &here, - Q_NEXT_TYPE(fusbh200, *hw_p)); - - *hw_p = *shadow_next_periodic(fusbh200, &here, - Q_NEXT_TYPE(fusbh200, *hw_p)); -} - -/* how many of the uframe's 125 usecs are allocated? */ -static unsigned short -periodic_usecs (struct fusbh200_hcd *fusbh200, unsigned frame, unsigned uframe) -{ - __hc32 *hw_p = &fusbh200->periodic [frame]; - union fusbh200_shadow *q = &fusbh200->pshadow [frame]; - unsigned usecs = 0; - struct fusbh200_qh_hw *hw; - - while (q->ptr) { - switch (hc32_to_cpu(fusbh200, Q_NEXT_TYPE(fusbh200, *hw_p))) { - case Q_TYPE_QH: - hw = q->qh->hw; - /* is it in the S-mask? */ - if (hw->hw_info2 & cpu_to_hc32(fusbh200, 1 << uframe)) - usecs += q->qh->usecs; - /* ... or C-mask? */ - if (hw->hw_info2 & cpu_to_hc32(fusbh200, - 1 << (8 + uframe))) - usecs += q->qh->c_usecs; - hw_p = &hw->hw_next; - q = &q->qh->qh_next; - break; - // case Q_TYPE_FSTN: - default: - /* for "save place" FSTNs, count the relevant INTR - * bandwidth from the previous frame - */ - if (q->fstn->hw_prev != FUSBH200_LIST_END(fusbh200)) { - fusbh200_dbg (fusbh200, "ignoring FSTN cost ...\n"); - } - hw_p = &q->fstn->hw_next; - q = &q->fstn->fstn_next; - break; - case Q_TYPE_ITD: - if (q->itd->hw_transaction[uframe]) - usecs += q->itd->stream->usecs; - hw_p = &q->itd->hw_next; - q = &q->itd->itd_next; - break; - } - } - if (usecs > fusbh200->uframe_periodic_max) - fusbh200_err (fusbh200, "uframe %d sched overrun: %d usecs\n", - frame * 8 + uframe, usecs); - return usecs; -} - -/*-------------------------------------------------------------------------*/ - -static int same_tt (struct usb_device *dev1, struct usb_device *dev2) -{ - if (!dev1->tt || !dev2->tt) - return 0; - if (dev1->tt != dev2->tt) - return 0; - if (dev1->tt->multi) - return dev1->ttport == dev2->ttport; - else - return 1; -} - -/* return true iff the device's transaction translator is available - * for a periodic transfer starting at the specified frame, using - * all the uframes in the mask. - */ -static int tt_no_collision ( - struct fusbh200_hcd *fusbh200, - unsigned period, - struct usb_device *dev, - unsigned frame, - u32 uf_mask -) -{ - if (period == 0) /* error */ - return 0; - - /* note bandwidth wastage: split never follows csplit - * (different dev or endpoint) until the next uframe. - * calling convention doesn't make that distinction. - */ - for (; frame < fusbh200->periodic_size; frame += period) { - union fusbh200_shadow here; - __hc32 type; - struct fusbh200_qh_hw *hw; - - here = fusbh200->pshadow [frame]; - type = Q_NEXT_TYPE(fusbh200, fusbh200->periodic [frame]); - while (here.ptr) { - switch (hc32_to_cpu(fusbh200, type)) { - case Q_TYPE_ITD: - type = Q_NEXT_TYPE(fusbh200, here.itd->hw_next); - here = here.itd->itd_next; - continue; - case Q_TYPE_QH: - hw = here.qh->hw; - if (same_tt (dev, here.qh->dev)) { - u32 mask; - - mask = hc32_to_cpu(fusbh200, - hw->hw_info2); - /* "knows" no gap is needed */ - mask |= mask >> 8; - if (mask & uf_mask) - break; - } - type = Q_NEXT_TYPE(fusbh200, hw->hw_next); - here = here.qh->qh_next; - continue; - // case Q_TYPE_FSTN: - default: - fusbh200_dbg (fusbh200, - "periodic frame %d bogus type %d\n", - frame, type); - } - - /* collision or error */ - return 0; - } - } - - /* no collision */ - return 1; -} - -/*-------------------------------------------------------------------------*/ - -static void enable_periodic(struct fusbh200_hcd *fusbh200) -{ - if (fusbh200->periodic_count++) - return; - - /* Stop waiting to turn off the periodic schedule */ - fusbh200->enabled_hrtimer_events &= ~BIT(FUSBH200_HRTIMER_DISABLE_PERIODIC); - - /* Don't start the schedule until PSS is 0 */ - fusbh200_poll_PSS(fusbh200); - turn_on_io_watchdog(fusbh200); -} - -static void disable_periodic(struct fusbh200_hcd *fusbh200) -{ - if (--fusbh200->periodic_count) - return; - - /* Don't turn off the schedule until PSS is 1 */ - fusbh200_poll_PSS(fusbh200); -} - -/*-------------------------------------------------------------------------*/ - -/* periodic schedule slots have iso tds (normal or split) first, then a - * sparse tree for active interrupt transfers. - * - * this just links in a qh; caller guarantees uframe masks are set right. - * no FSTN support (yet; fusbh200 0.96+) - */ -static void qh_link_periodic(struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh) -{ - unsigned i; - unsigned period = qh->period; - - dev_dbg (&qh->dev->dev, - "link qh%d-%04x/%p start %d [%d/%d us]\n", - period, hc32_to_cpup(fusbh200, &qh->hw->hw_info2) - & (QH_CMASK | QH_SMASK), - qh, qh->start, qh->usecs, qh->c_usecs); - - /* high bandwidth, or otherwise every microframe */ - if (period == 0) - period = 1; - - for (i = qh->start; i < fusbh200->periodic_size; i += period) { - union fusbh200_shadow *prev = &fusbh200->pshadow[i]; - __hc32 *hw_p = &fusbh200->periodic[i]; - union fusbh200_shadow here = *prev; - __hc32 type = 0; - - /* skip the iso nodes at list head */ - while (here.ptr) { - type = Q_NEXT_TYPE(fusbh200, *hw_p); - if (type == cpu_to_hc32(fusbh200, Q_TYPE_QH)) - break; - prev = periodic_next_shadow(fusbh200, prev, type); - hw_p = shadow_next_periodic(fusbh200, &here, type); - here = *prev; - } - - /* sorting each branch by period (slow-->fast) - * enables sharing interior tree nodes - */ - while (here.ptr && qh != here.qh) { - if (qh->period > here.qh->period) - break; - prev = &here.qh->qh_next; - hw_p = &here.qh->hw->hw_next; - here = *prev; - } - /* link in this qh, unless some earlier pass did that */ - if (qh != here.qh) { - qh->qh_next = here; - if (here.qh) - qh->hw->hw_next = *hw_p; - wmb (); - prev->qh = qh; - *hw_p = QH_NEXT (fusbh200, qh->qh_dma); - } - } - qh->qh_state = QH_STATE_LINKED; - qh->xacterrs = 0; - - /* update per-qh bandwidth for usbfs */ - fusbh200_to_hcd(fusbh200)->self.bandwidth_allocated += qh->period - ? ((qh->usecs + qh->c_usecs) / qh->period) - : (qh->usecs * 8); - - list_add(&qh->intr_node, &fusbh200->intr_qh_list); - - /* maybe enable periodic schedule processing */ - ++fusbh200->intr_count; - enable_periodic(fusbh200); -} - -static void qh_unlink_periodic(struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh) -{ - unsigned i; - unsigned period; - - /* - * If qh is for a low/full-speed device, simply unlinking it - * could interfere with an ongoing split transaction. To unlink - * it safely would require setting the QH_INACTIVATE bit and - * waiting at least one frame, as described in EHCI 4.12.2.5. - * - * We won't bother with any of this. Instead, we assume that the - * only reason for unlinking an interrupt QH while the current URB - * is still active is to dequeue all the URBs (flush the whole - * endpoint queue). - * - * If rebalancing the periodic schedule is ever implemented, this - * approach will no longer be valid. - */ - - /* high bandwidth, or otherwise part of every microframe */ - if ((period = qh->period) == 0) - period = 1; - - for (i = qh->start; i < fusbh200->periodic_size; i += period) - periodic_unlink (fusbh200, i, qh); - - /* update per-qh bandwidth for usbfs */ - fusbh200_to_hcd(fusbh200)->self.bandwidth_allocated -= qh->period - ? ((qh->usecs + qh->c_usecs) / qh->period) - : (qh->usecs * 8); - - dev_dbg (&qh->dev->dev, - "unlink qh%d-%04x/%p start %d [%d/%d us]\n", - qh->period, - hc32_to_cpup(fusbh200, &qh->hw->hw_info2) & (QH_CMASK | QH_SMASK), - qh, qh->start, qh->usecs, qh->c_usecs); - - /* qh->qh_next still "live" to HC */ - qh->qh_state = QH_STATE_UNLINK; - qh->qh_next.ptr = NULL; - - if (fusbh200->qh_scan_next == qh) - fusbh200->qh_scan_next = list_entry(qh->intr_node.next, - struct fusbh200_qh, intr_node); - list_del(&qh->intr_node); -} - -static void start_unlink_intr(struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh) -{ - /* If the QH isn't linked then there's nothing we can do - * unless we were called during a giveback, in which case - * qh_completions() has to deal with it. - */ - if (qh->qh_state != QH_STATE_LINKED) { - if (qh->qh_state == QH_STATE_COMPLETING) - qh->needs_rescan = 1; - return; - } - - qh_unlink_periodic (fusbh200, qh); - - /* Make sure the unlinks are visible before starting the timer */ - wmb(); - - /* - * The EHCI spec doesn't say how long it takes the controller to - * stop accessing an unlinked interrupt QH. The timer delay is - * 9 uframes; presumably that will be long enough. - */ - qh->unlink_cycle = fusbh200->intr_unlink_cycle; - - /* New entries go at the end of the intr_unlink list */ - if (fusbh200->intr_unlink) - fusbh200->intr_unlink_last->unlink_next = qh; - else - fusbh200->intr_unlink = qh; - fusbh200->intr_unlink_last = qh; - - if (fusbh200->intr_unlinking) - ; /* Avoid recursive calls */ - else if (fusbh200->rh_state < FUSBH200_RH_RUNNING) - fusbh200_handle_intr_unlinks(fusbh200); - else if (fusbh200->intr_unlink == qh) { - fusbh200_enable_event(fusbh200, FUSBH200_HRTIMER_UNLINK_INTR, true); - ++fusbh200->intr_unlink_cycle; - } -} - -static void end_unlink_intr(struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh) -{ - struct fusbh200_qh_hw *hw = qh->hw; - int rc; - - qh->qh_state = QH_STATE_IDLE; - hw->hw_next = FUSBH200_LIST_END(fusbh200); - - qh_completions(fusbh200, qh); - - /* reschedule QH iff another request is queued */ - if (!list_empty(&qh->qtd_list) && fusbh200->rh_state == FUSBH200_RH_RUNNING) { - rc = qh_schedule(fusbh200, qh); - - /* An error here likely indicates handshake failure - * or no space left in the schedule. Neither fault - * should happen often ... - * - * FIXME kill the now-dysfunctional queued urbs - */ - if (rc != 0) - fusbh200_err(fusbh200, "can't reschedule qh %p, err %d\n", - qh, rc); - } - - /* maybe turn off periodic schedule */ - --fusbh200->intr_count; - disable_periodic(fusbh200); -} - -/*-------------------------------------------------------------------------*/ - -static int check_period ( - struct fusbh200_hcd *fusbh200, - unsigned frame, - unsigned uframe, - unsigned period, - unsigned usecs -) { - int claimed; - - /* complete split running into next frame? - * given FSTN support, we could sometimes check... - */ - if (uframe >= 8) - return 0; - - /* convert "usecs we need" to "max already claimed" */ - usecs = fusbh200->uframe_periodic_max - usecs; - - /* we "know" 2 and 4 uframe intervals were rejected; so - * for period 0, check _every_ microframe in the schedule. - */ - if (unlikely (period == 0)) { - do { - for (uframe = 0; uframe < 7; uframe++) { - claimed = periodic_usecs (fusbh200, frame, uframe); - if (claimed > usecs) - return 0; - } - } while ((frame += 1) < fusbh200->periodic_size); - - /* just check the specified uframe, at that period */ - } else { - do { - claimed = periodic_usecs (fusbh200, frame, uframe); - if (claimed > usecs) - return 0; - } while ((frame += period) < fusbh200->periodic_size); - } - - // success! - return 1; -} - -static int check_intr_schedule ( - struct fusbh200_hcd *fusbh200, - unsigned frame, - unsigned uframe, - const struct fusbh200_qh *qh, - __hc32 *c_maskp -) -{ - int retval = -ENOSPC; - u8 mask = 0; - - if (qh->c_usecs && uframe >= 6) /* FSTN territory? */ - goto done; - - if (!check_period (fusbh200, frame, uframe, qh->period, qh->usecs)) - goto done; - if (!qh->c_usecs) { - retval = 0; - *c_maskp = 0; - goto done; - } - - /* Make sure this tt's buffer is also available for CSPLITs. - * We pessimize a bit; probably the typical full speed case - * doesn't need the second CSPLIT. - * - * NOTE: both SPLIT and CSPLIT could be checked in just - * one smart pass... - */ - mask = 0x03 << (uframe + qh->gap_uf); - *c_maskp = cpu_to_hc32(fusbh200, mask << 8); - - mask |= 1 << uframe; - if (tt_no_collision (fusbh200, qh->period, qh->dev, frame, mask)) { - if (!check_period (fusbh200, frame, uframe + qh->gap_uf + 1, - qh->period, qh->c_usecs)) - goto done; - if (!check_period (fusbh200, frame, uframe + qh->gap_uf, - qh->period, qh->c_usecs)) - goto done; - retval = 0; - } -done: - return retval; -} - -/* "first fit" scheduling policy used the first time through, - * or when the previous schedule slot can't be re-used. - */ -static int qh_schedule(struct fusbh200_hcd *fusbh200, struct fusbh200_qh *qh) -{ - int status; - unsigned uframe; - __hc32 c_mask; - unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */ - struct fusbh200_qh_hw *hw = qh->hw; - - qh_refresh(fusbh200, qh); - hw->hw_next = FUSBH200_LIST_END(fusbh200); - frame = qh->start; - - /* reuse the previous schedule slots, if we can */ - if (frame < qh->period) { - uframe = ffs(hc32_to_cpup(fusbh200, &hw->hw_info2) & QH_SMASK); - status = check_intr_schedule (fusbh200, frame, --uframe, - qh, &c_mask); - } else { - uframe = 0; - c_mask = 0; - status = -ENOSPC; - } - - /* else scan the schedule to find a group of slots such that all - * uframes have enough periodic bandwidth available. - */ - if (status) { - /* "normal" case, uframing flexible except with splits */ - if (qh->period) { - int i; - - for (i = qh->period; status && i > 0; --i) { - frame = ++fusbh200->random_frame % qh->period; - for (uframe = 0; uframe < 8; uframe++) { - status = check_intr_schedule (fusbh200, - frame, uframe, qh, - &c_mask); - if (status == 0) - break; - } - } - - /* qh->period == 0 means every uframe */ - } else { - frame = 0; - status = check_intr_schedule (fusbh200, 0, 0, qh, &c_mask); - } - if (status) - goto done; - qh->start = frame; - - /* reset S-frame and (maybe) C-frame masks */ - hw->hw_info2 &= cpu_to_hc32(fusbh200, ~(QH_CMASK | QH_SMASK)); - hw->hw_info2 |= qh->period - ? cpu_to_hc32(fusbh200, 1 << uframe) - : cpu_to_hc32(fusbh200, QH_SMASK); - hw->hw_info2 |= c_mask; - } else - fusbh200_dbg (fusbh200, "reused qh %p schedule\n", qh); - - /* stuff into the periodic schedule */ - qh_link_periodic(fusbh200, qh); -done: - return status; -} - -static int intr_submit ( - struct fusbh200_hcd *fusbh200, - struct urb *urb, - struct list_head *qtd_list, - gfp_t mem_flags -) { - unsigned epnum; - unsigned long flags; - struct fusbh200_qh *qh; - int status; - struct list_head empty; - - /* get endpoint and transfer/schedule data */ - epnum = urb->ep->desc.bEndpointAddress; - - spin_lock_irqsave (&fusbh200->lock, flags); - - if (unlikely(!HCD_HW_ACCESSIBLE(fusbh200_to_hcd(fusbh200)))) { - status = -ESHUTDOWN; - goto done_not_linked; - } - status = usb_hcd_link_urb_to_ep(fusbh200_to_hcd(fusbh200), urb); - if (unlikely(status)) - goto done_not_linked; - - /* get qh and force any scheduling errors */ - INIT_LIST_HEAD (&empty); - qh = qh_append_tds(fusbh200, urb, &empty, epnum, &urb->ep->hcpriv); - if (qh == NULL) { - status = -ENOMEM; - goto done; - } - if (qh->qh_state == QH_STATE_IDLE) { - if ((status = qh_schedule (fusbh200, qh)) != 0) - goto done; - } - - /* then queue the urb's tds to the qh */ - qh = qh_append_tds(fusbh200, urb, qtd_list, epnum, &urb->ep->hcpriv); - BUG_ON (qh == NULL); - - /* ... update usbfs periodic stats */ - fusbh200_to_hcd(fusbh200)->self.bandwidth_int_reqs++; - -done: - if (unlikely(status)) - usb_hcd_unlink_urb_from_ep(fusbh200_to_hcd(fusbh200), urb); -done_not_linked: - spin_unlock_irqrestore (&fusbh200->lock, flags); - if (status) - qtd_list_free (fusbh200, urb, qtd_list); - - return status; -} - -static void scan_intr(struct fusbh200_hcd *fusbh200) -{ - struct fusbh200_qh *qh; - - list_for_each_entry_safe(qh, fusbh200->qh_scan_next, &fusbh200->intr_qh_list, - intr_node) { - rescan: - /* clean any finished work for this qh */ - if (!list_empty(&qh->qtd_list)) { - int temp; - - /* - * Unlinks could happen here; completion reporting - * drops the lock. That's why fusbh200->qh_scan_next - * always holds the next qh to scan; if the next qh - * gets unlinked then fusbh200->qh_scan_next is adjusted - * in qh_unlink_periodic(). - */ - temp = qh_completions(fusbh200, qh); - if (unlikely(qh->needs_rescan || - (list_empty(&qh->qtd_list) && - qh->qh_state == QH_STATE_LINKED))) - start_unlink_intr(fusbh200, qh); - else if (temp != 0) - goto rescan; - } - } -} - -/*-------------------------------------------------------------------------*/ - -/* fusbh200_iso_stream ops work with both ITD and SITD */ - -static struct fusbh200_iso_stream * -iso_stream_alloc (gfp_t mem_flags) -{ - struct fusbh200_iso_stream *stream; - - stream = kzalloc(sizeof *stream, mem_flags); - if (likely (stream != NULL)) { - INIT_LIST_HEAD(&stream->td_list); - INIT_LIST_HEAD(&stream->free_list); - stream->next_uframe = -1; - } - return stream; -} - -static void -iso_stream_init ( - struct fusbh200_hcd *fusbh200, - struct fusbh200_iso_stream *stream, - struct usb_device *dev, - int pipe, - unsigned interval -) -{ - u32 buf1; - unsigned epnum, maxp; - int is_input; - long bandwidth; - unsigned multi; - - /* - * this might be a "high bandwidth" highspeed endpoint, - * as encoded in the ep descriptor's wMaxPacket field - */ - epnum = usb_pipeendpoint (pipe); - is_input = usb_pipein (pipe) ? USB_DIR_IN : 0; - maxp = usb_maxpacket(dev, pipe, !is_input); - if (is_input) { - buf1 = (1 << 11); - } else { - buf1 = 0; - } - - maxp = max_packet(maxp); - multi = hb_mult(maxp); - buf1 |= maxp; - maxp *= multi; - - stream->buf0 = cpu_to_hc32(fusbh200, (epnum << 8) | dev->devnum); - stream->buf1 = cpu_to_hc32(fusbh200, buf1); - stream->buf2 = cpu_to_hc32(fusbh200, multi); - - /* usbfs wants to report the average usecs per frame tied up - * when transfers on this endpoint are scheduled ... - */ - if (dev->speed == USB_SPEED_FULL) { - interval <<= 3; - stream->usecs = NS_TO_US(usb_calc_bus_time(dev->speed, - is_input, 1, maxp)); - stream->usecs /= 8; - } else { - stream->highspeed = 1; - stream->usecs = HS_USECS_ISO (maxp); - } - bandwidth = stream->usecs * 8; - bandwidth /= interval; - - stream->bandwidth = bandwidth; - stream->udev = dev; - stream->bEndpointAddress = is_input | epnum; - stream->interval = interval; - stream->maxp = maxp; -} - -static struct fusbh200_iso_stream * -iso_stream_find (struct fusbh200_hcd *fusbh200, struct urb *urb) -{ - unsigned epnum; - struct fusbh200_iso_stream *stream; - struct usb_host_endpoint *ep; - unsigned long flags; - - epnum = usb_pipeendpoint (urb->pipe); - if (usb_pipein(urb->pipe)) - ep = urb->dev->ep_in[epnum]; - else - ep = urb->dev->ep_out[epnum]; - - spin_lock_irqsave (&fusbh200->lock, flags); - stream = ep->hcpriv; - - if (unlikely (stream == NULL)) { - stream = iso_stream_alloc(GFP_ATOMIC); - if (likely (stream != NULL)) { - ep->hcpriv = stream; - stream->ep = ep; - iso_stream_init(fusbh200, stream, urb->dev, urb->pipe, - urb->interval); - } - - /* if dev->ep [epnum] is a QH, hw is set */ - } else if (unlikely (stream->hw != NULL)) { - fusbh200_dbg (fusbh200, "dev %s ep%d%s, not iso??\n", - urb->dev->devpath, epnum, - usb_pipein(urb->pipe) ? "in" : "out"); - stream = NULL; - } - - spin_unlock_irqrestore (&fusbh200->lock, flags); - return stream; -} - -/*-------------------------------------------------------------------------*/ - -/* fusbh200_iso_sched ops can be ITD-only or SITD-only */ - -static struct fusbh200_iso_sched * -iso_sched_alloc (unsigned packets, gfp_t mem_flags) -{ - struct fusbh200_iso_sched *iso_sched; - int size = sizeof *iso_sched; - - size += packets * sizeof (struct fusbh200_iso_packet); - iso_sched = kzalloc(size, mem_flags); - if (likely (iso_sched != NULL)) { - INIT_LIST_HEAD (&iso_sched->td_list); - } - return iso_sched; -} - -static inline void -itd_sched_init( - struct fusbh200_hcd *fusbh200, - struct fusbh200_iso_sched *iso_sched, - struct fusbh200_iso_stream *stream, - struct urb *urb -) -{ - unsigned i; - dma_addr_t dma = urb->transfer_dma; - - /* how many uframes are needed for these transfers */ - iso_sched->span = urb->number_of_packets * stream->interval; - - /* figure out per-uframe itd fields that we'll need later - * when we fit new itds into the schedule. - */ - for (i = 0; i < urb->number_of_packets; i++) { - struct fusbh200_iso_packet *uframe = &iso_sched->packet [i]; - unsigned length; - dma_addr_t buf; - u32 trans; - - length = urb->iso_frame_desc [i].length; - buf = dma + urb->iso_frame_desc [i].offset; - - trans = FUSBH200_ISOC_ACTIVE; - trans |= buf & 0x0fff; - if (unlikely (((i + 1) == urb->number_of_packets)) - && !(urb->transfer_flags & URB_NO_INTERRUPT)) - trans |= FUSBH200_ITD_IOC; - trans |= length << 16; - uframe->transaction = cpu_to_hc32(fusbh200, trans); - - /* might need to cross a buffer page within a uframe */ - uframe->bufp = (buf & ~(u64)0x0fff); - buf += length; - if (unlikely ((uframe->bufp != (buf & ~(u64)0x0fff)))) - uframe->cross = 1; - } -} - -static void -iso_sched_free ( - struct fusbh200_iso_stream *stream, - struct fusbh200_iso_sched *iso_sched -) -{ - if (!iso_sched) - return; - // caller must hold fusbh200->lock! - list_splice (&iso_sched->td_list, &stream->free_list); - kfree (iso_sched); -} - -static int -itd_urb_transaction ( - struct fusbh200_iso_stream *stream, - struct fusbh200_hcd *fusbh200, - struct urb *urb, - gfp_t mem_flags -) -{ - struct fusbh200_itd *itd; - dma_addr_t itd_dma; - int i; - unsigned num_itds; - struct fusbh200_iso_sched *sched; - unsigned long flags; - - sched = iso_sched_alloc (urb->number_of_packets, mem_flags); - if (unlikely (sched == NULL)) - return -ENOMEM; - - itd_sched_init(fusbh200, sched, stream, urb); - - if (urb->interval < 8) - num_itds = 1 + (sched->span + 7) / 8; - else - num_itds = urb->number_of_packets; - - /* allocate/init ITDs */ - spin_lock_irqsave (&fusbh200->lock, flags); - for (i = 0; i < num_itds; i++) { - - /* - * Use iTDs from the free list, but not iTDs that may - * still be in use by the hardware. - */ - if (likely(!list_empty(&stream->free_list))) { - itd = list_first_entry(&stream->free_list, - struct fusbh200_itd, itd_list); - if (itd->frame == fusbh200->now_frame) - goto alloc_itd; - list_del (&itd->itd_list); - itd_dma = itd->itd_dma; - } else { - alloc_itd: - spin_unlock_irqrestore (&fusbh200->lock, flags); - itd = dma_pool_alloc (fusbh200->itd_pool, mem_flags, - &itd_dma); - spin_lock_irqsave (&fusbh200->lock, flags); - if (!itd) { - iso_sched_free(stream, sched); - spin_unlock_irqrestore(&fusbh200->lock, flags); - return -ENOMEM; - } - } - - memset (itd, 0, sizeof *itd); - itd->itd_dma = itd_dma; - list_add (&itd->itd_list, &sched->td_list); - } - spin_unlock_irqrestore (&fusbh200->lock, flags); - - /* temporarily store schedule info in hcpriv */ - urb->hcpriv = sched; - urb->error_count = 0; - return 0; -} - -/*-------------------------------------------------------------------------*/ - -static inline int -itd_slot_ok ( - struct fusbh200_hcd *fusbh200, - u32 mod, - u32 uframe, - u8 usecs, - u32 period -) -{ - uframe %= period; - do { - /* can't commit more than uframe_periodic_max usec */ - if (periodic_usecs (fusbh200, uframe >> 3, uframe & 0x7) - > (fusbh200->uframe_periodic_max - usecs)) - return 0; - - /* we know urb->interval is 2^N uframes */ - uframe += period; - } while (uframe < mod); - return 1; -} - -/* - * This scheduler plans almost as far into the future as it has actual - * periodic schedule slots. (Affected by TUNE_FLS, which defaults to - * "as small as possible" to be cache-friendlier.) That limits the size - * transfers you can stream reliably; avoid more than 64 msec per urb. - * Also avoid queue depths of less than fusbh200's worst irq latency (affected - * by the per-urb URB_NO_INTERRUPT hint, the log2_irq_thresh module parameter, - * and other factors); or more than about 230 msec total (for portability, - * given FUSBH200_TUNE_FLS and the slop). Or, write a smarter scheduler! - */ - -#define SCHEDULE_SLOP 80 /* microframes */ - -static int -iso_stream_schedule ( - struct fusbh200_hcd *fusbh200, - struct urb *urb, - struct fusbh200_iso_stream *stream -) -{ - u32 now, next, start, period, span; - int status; - unsigned mod = fusbh200->periodic_size << 3; - struct fusbh200_iso_sched *sched = urb->hcpriv; - - period = urb->interval; - span = sched->span; - - if (span > mod - SCHEDULE_SLOP) { - fusbh200_dbg (fusbh200, "iso request %p too long\n", urb); - status = -EFBIG; - goto fail; - } - - now = fusbh200_read_frame_index(fusbh200) & (mod - 1); - - /* Typical case: reuse current schedule, stream is still active. - * Hopefully there are no gaps from the host falling behind - * (irq delays etc), but if there are we'll take the next - * slot in the schedule, implicitly assuming URB_ISO_ASAP. - */ - if (likely (!list_empty (&stream->td_list))) { - u32 excess; - - /* For high speed devices, allow scheduling within the - * isochronous scheduling threshold. For full speed devices - * and Intel PCI-based controllers, don't (work around for - * Intel ICH9 bug). - */ - if (!stream->highspeed && fusbh200->fs_i_thresh) - next = now + fusbh200->i_thresh; - else - next = now; - - /* Fell behind (by up to twice the slop amount)? - * We decide based on the time of the last currently-scheduled - * slot, not the time of the next available slot. - */ - excess = (stream->next_uframe - period - next) & (mod - 1); - if (excess >= mod - 2 * SCHEDULE_SLOP) - start = next + excess - mod + period * - DIV_ROUND_UP(mod - excess, period); - else - start = next + excess + period; - if (start - now >= mod) { - fusbh200_dbg(fusbh200, "request %p would overflow (%d+%d >= %d)\n", - urb, start - now - period, period, - mod); - status = -EFBIG; - goto fail; - } - } - - /* need to schedule; when's the next (u)frame we could start? - * this is bigger than fusbh200->i_thresh allows; scheduling itself - * isn't free, the slop should handle reasonably slow cpus. it - * can also help high bandwidth if the dma and irq loads don't - * jump until after the queue is primed. - */ - else { - int done = 0; - start = SCHEDULE_SLOP + (now & ~0x07); - - /* NOTE: assumes URB_ISO_ASAP, to limit complexity/bugs */ - - /* find a uframe slot with enough bandwidth. - * Early uframes are more precious because full-speed - * iso IN transfers can't use late uframes, - * and therefore they should be allocated last. - */ - next = start; - start += period; - do { - start--; - /* check schedule: enough space? */ - if (itd_slot_ok(fusbh200, mod, start, - stream->usecs, period)) - done = 1; - } while (start > next && !done); - - /* no room in the schedule */ - if (!done) { - fusbh200_dbg(fusbh200, "iso resched full %p (now %d max %d)\n", - urb, now, now + mod); - status = -ENOSPC; - goto fail; - } - } - - /* Tried to schedule too far into the future? */ - if (unlikely(start - now + span - period - >= mod - 2 * SCHEDULE_SLOP)) { - fusbh200_dbg(fusbh200, "request %p would overflow (%d+%d >= %d)\n", - urb, start - now, span - period, - mod - 2 * SCHEDULE_SLOP); - status = -EFBIG; - goto fail; - } - - stream->next_uframe = start & (mod - 1); - - /* report high speed start in uframes; full speed, in frames */ - urb->start_frame = stream->next_uframe; - if (!stream->highspeed) - urb->start_frame >>= 3; - - /* Make sure scan_isoc() sees these */ - if (fusbh200->isoc_count == 0) - fusbh200->next_frame = now >> 3; - return 0; - - fail: - iso_sched_free(stream, sched); - urb->hcpriv = NULL; - return status; -} - -/*-------------------------------------------------------------------------*/ - -static inline void -itd_init(struct fusbh200_hcd *fusbh200, struct fusbh200_iso_stream *stream, - struct fusbh200_itd *itd) -{ - int i; - - /* it's been recently zeroed */ - itd->hw_next = FUSBH200_LIST_END(fusbh200); - itd->hw_bufp [0] = stream->buf0; - itd->hw_bufp [1] = stream->buf1; - itd->hw_bufp [2] = stream->buf2; - - for (i = 0; i < 8; i++) - itd->index[i] = -1; - - /* All other fields are filled when scheduling */ -} - -static inline void -itd_patch( - struct fusbh200_hcd *fusbh200, - struct fusbh200_itd *itd, - struct fusbh200_iso_sched *iso_sched, - unsigned index, - u16 uframe -) -{ - struct fusbh200_iso_packet *uf = &iso_sched->packet [index]; - unsigned pg = itd->pg; - - // BUG_ON (pg == 6 && uf->cross); - - uframe &= 0x07; - itd->index [uframe] = index; - - itd->hw_transaction[uframe] = uf->transaction; - itd->hw_transaction[uframe] |= cpu_to_hc32(fusbh200, pg << 12); - itd->hw_bufp[pg] |= cpu_to_hc32(fusbh200, uf->bufp & ~(u32)0); - itd->hw_bufp_hi[pg] |= cpu_to_hc32(fusbh200, (u32)(uf->bufp >> 32)); - - /* iso_frame_desc[].offset must be strictly increasing */ - if (unlikely (uf->cross)) { - u64 bufp = uf->bufp + 4096; - - itd->pg = ++pg; - itd->hw_bufp[pg] |= cpu_to_hc32(fusbh200, bufp & ~(u32)0); - itd->hw_bufp_hi[pg] |= cpu_to_hc32(fusbh200, (u32)(bufp >> 32)); - } -} - -static inline void -itd_link (struct fusbh200_hcd *fusbh200, unsigned frame, struct fusbh200_itd *itd) -{ - union fusbh200_shadow *prev = &fusbh200->pshadow[frame]; - __hc32 *hw_p = &fusbh200->periodic[frame]; - union fusbh200_shadow here = *prev; - __hc32 type = 0; - - /* skip any iso nodes which might belong to previous microframes */ - while (here.ptr) { - type = Q_NEXT_TYPE(fusbh200, *hw_p); - if (type == cpu_to_hc32(fusbh200, Q_TYPE_QH)) - break; - prev = periodic_next_shadow(fusbh200, prev, type); - hw_p = shadow_next_periodic(fusbh200, &here, type); - here = *prev; - } - - itd->itd_next = here; - itd->hw_next = *hw_p; - prev->itd = itd; - itd->frame = frame; - wmb (); - *hw_p = cpu_to_hc32(fusbh200, itd->itd_dma | Q_TYPE_ITD); -} - -/* fit urb's itds into the selected schedule slot; activate as needed */ -static void itd_link_urb( - struct fusbh200_hcd *fusbh200, - struct urb *urb, - unsigned mod, - struct fusbh200_iso_stream *stream -) -{ - int packet; - unsigned next_uframe, uframe, frame; - struct fusbh200_iso_sched *iso_sched = urb->hcpriv; - struct fusbh200_itd *itd; - - next_uframe = stream->next_uframe & (mod - 1); - - if (unlikely (list_empty(&stream->td_list))) { - fusbh200_to_hcd(fusbh200)->self.bandwidth_allocated - += stream->bandwidth; - fusbh200_dbg(fusbh200, - "schedule devp %s ep%d%s-iso period %d start %d.%d\n", - urb->dev->devpath, stream->bEndpointAddress & 0x0f, - (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out", - urb->interval, - next_uframe >> 3, next_uframe & 0x7); - } - - /* fill iTDs uframe by uframe */ - for (packet = 0, itd = NULL; packet < urb->number_of_packets; ) { - if (itd == NULL) { - /* ASSERT: we have all necessary itds */ - // BUG_ON (list_empty (&iso_sched->td_list)); - - /* ASSERT: no itds for this endpoint in this uframe */ - - itd = list_entry (iso_sched->td_list.next, - struct fusbh200_itd, itd_list); - list_move_tail (&itd->itd_list, &stream->td_list); - itd->stream = stream; - itd->urb = urb; - itd_init (fusbh200, stream, itd); - } - - uframe = next_uframe & 0x07; - frame = next_uframe >> 3; - - itd_patch(fusbh200, itd, iso_sched, packet, uframe); - - next_uframe += stream->interval; - next_uframe &= mod - 1; - packet++; - - /* link completed itds into the schedule */ - if (((next_uframe >> 3) != frame) - || packet == urb->number_of_packets) { - itd_link(fusbh200, frame & (fusbh200->periodic_size - 1), itd); - itd = NULL; - } - } - stream->next_uframe = next_uframe; - - /* don't need that schedule data any more */ - iso_sched_free (stream, iso_sched); - urb->hcpriv = NULL; - - ++fusbh200->isoc_count; - enable_periodic(fusbh200); -} - -#define ISO_ERRS (FUSBH200_ISOC_BUF_ERR | FUSBH200_ISOC_BABBLE | FUSBH200_ISOC_XACTERR) - -/* Process and recycle a completed ITD. Return true iff its urb completed, - * and hence its completion callback probably added things to the hardware - * schedule. - * - * Note that we carefully avoid recycling this descriptor until after any - * completion callback runs, so that it won't be reused quickly. That is, - * assuming (a) no more than two urbs per frame on this endpoint, and also - * (b) only this endpoint's completions submit URBs. It seems some silicon - * corrupts things if you reuse completed descriptors very quickly... - */ -static bool itd_complete(struct fusbh200_hcd *fusbh200, struct fusbh200_itd *itd) -{ - struct urb *urb = itd->urb; - struct usb_iso_packet_descriptor *desc; - u32 t; - unsigned uframe; - int urb_index = -1; - struct fusbh200_iso_stream *stream = itd->stream; - struct usb_device *dev; - bool retval = false; - - /* for each uframe with a packet */ - for (uframe = 0; uframe < 8; uframe++) { - if (likely (itd->index[uframe] == -1)) - continue; - urb_index = itd->index[uframe]; - desc = &urb->iso_frame_desc [urb_index]; - - t = hc32_to_cpup(fusbh200, &itd->hw_transaction [uframe]); - itd->hw_transaction [uframe] = 0; - - /* report transfer status */ - if (unlikely (t & ISO_ERRS)) { - urb->error_count++; - if (t & FUSBH200_ISOC_BUF_ERR) - desc->status = usb_pipein (urb->pipe) - ? -ENOSR /* hc couldn't read */ - : -ECOMM; /* hc couldn't write */ - else if (t & FUSBH200_ISOC_BABBLE) - desc->status = -EOVERFLOW; - else /* (t & FUSBH200_ISOC_XACTERR) */ - desc->status = -EPROTO; - - /* HC need not update length with this error */ - if (!(t & FUSBH200_ISOC_BABBLE)) { - desc->actual_length = fusbh200_itdlen(urb, desc, t); - urb->actual_length += desc->actual_length; - } - } else if (likely ((t & FUSBH200_ISOC_ACTIVE) == 0)) { - desc->status = 0; - desc->actual_length = fusbh200_itdlen(urb, desc, t); - urb->actual_length += desc->actual_length; - } else { - /* URB was too late */ - desc->status = -EXDEV; - } - } - - /* handle completion now? */ - if (likely ((urb_index + 1) != urb->number_of_packets)) - goto done; - - /* ASSERT: it's really the last itd for this urb - list_for_each_entry (itd, &stream->td_list, itd_list) - BUG_ON (itd->urb == urb); - */ - - /* give urb back to the driver; completion often (re)submits */ - dev = urb->dev; - fusbh200_urb_done(fusbh200, urb, 0); - retval = true; - urb = NULL; - - --fusbh200->isoc_count; - disable_periodic(fusbh200); - - if (unlikely(list_is_singular(&stream->td_list))) { - fusbh200_to_hcd(fusbh200)->self.bandwidth_allocated - -= stream->bandwidth; - fusbh200_dbg(fusbh200, - "deschedule devp %s ep%d%s-iso\n", - dev->devpath, stream->bEndpointAddress & 0x0f, - (stream->bEndpointAddress & USB_DIR_IN) ? "in" : "out"); - } - -done: - itd->urb = NULL; - - /* Add to the end of the free list for later reuse */ - list_move_tail(&itd->itd_list, &stream->free_list); - - /* Recycle the iTDs when the pipeline is empty (ep no longer in use) */ - if (list_empty(&stream->td_list)) { - list_splice_tail_init(&stream->free_list, - &fusbh200->cached_itd_list); - start_free_itds(fusbh200); - } - - return retval; -} - -/*-------------------------------------------------------------------------*/ - -static int itd_submit (struct fusbh200_hcd *fusbh200, struct urb *urb, - gfp_t mem_flags) -{ - int status = -EINVAL; - unsigned long flags; - struct fusbh200_iso_stream *stream; - - /* Get iso_stream head */ - stream = iso_stream_find (fusbh200, urb); - if (unlikely (stream == NULL)) { - fusbh200_dbg (fusbh200, "can't get iso stream\n"); - return -ENOMEM; - } - if (unlikely (urb->interval != stream->interval && - fusbh200_port_speed(fusbh200, 0) == USB_PORT_STAT_HIGH_SPEED)) { - fusbh200_dbg (fusbh200, "can't change iso interval %d --> %d\n", - stream->interval, urb->interval); - goto done; - } - -#ifdef FUSBH200_URB_TRACE - fusbh200_dbg (fusbh200, - "%s %s urb %p ep%d%s len %d, %d pkts %d uframes [%p]\n", - __func__, urb->dev->devpath, urb, - usb_pipeendpoint (urb->pipe), - usb_pipein (urb->pipe) ? "in" : "out", - urb->transfer_buffer_length, - urb->number_of_packets, urb->interval, - stream); -#endif - - /* allocate ITDs w/o locking anything */ - status = itd_urb_transaction (stream, fusbh200, urb, mem_flags); - if (unlikely (status < 0)) { - fusbh200_dbg (fusbh200, "can't init itds\n"); - goto done; - } - - /* schedule ... need to lock */ - spin_lock_irqsave (&fusbh200->lock, flags); - if (unlikely(!HCD_HW_ACCESSIBLE(fusbh200_to_hcd(fusbh200)))) { - status = -ESHUTDOWN; - goto done_not_linked; - } - status = usb_hcd_link_urb_to_ep(fusbh200_to_hcd(fusbh200), urb); - if (unlikely(status)) - goto done_not_linked; - status = iso_stream_schedule(fusbh200, urb, stream); - if (likely (status == 0)) - itd_link_urb (fusbh200, urb, fusbh200->periodic_size << 3, stream); - else - usb_hcd_unlink_urb_from_ep(fusbh200_to_hcd(fusbh200), urb); - done_not_linked: - spin_unlock_irqrestore (&fusbh200->lock, flags); - done: - return status; -} - -/*-------------------------------------------------------------------------*/ - -static void scan_isoc(struct fusbh200_hcd *fusbh200) -{ - unsigned uf, now_frame, frame; - unsigned fmask = fusbh200->periodic_size - 1; - bool modified, live; - - /* - * When running, scan from last scan point up to "now" - * else clean up by scanning everything that's left. - * Touches as few pages as possible: cache-friendly. - */ - if (fusbh200->rh_state >= FUSBH200_RH_RUNNING) { - uf = fusbh200_read_frame_index(fusbh200); - now_frame = (uf >> 3) & fmask; - live = true; - } else { - now_frame = (fusbh200->next_frame - 1) & fmask; - live = false; - } - fusbh200->now_frame = now_frame; - - frame = fusbh200->next_frame; - for (;;) { - union fusbh200_shadow q, *q_p; - __hc32 type, *hw_p; - -restart: - /* scan each element in frame's queue for completions */ - q_p = &fusbh200->pshadow [frame]; - hw_p = &fusbh200->periodic [frame]; - q.ptr = q_p->ptr; - type = Q_NEXT_TYPE(fusbh200, *hw_p); - modified = false; - - while (q.ptr != NULL) { - switch (hc32_to_cpu(fusbh200, type)) { - case Q_TYPE_ITD: - /* If this ITD is still active, leave it for - * later processing ... check the next entry. - * No need to check for activity unless the - * frame is current. - */ - if (frame == now_frame && live) { - rmb(); - for (uf = 0; uf < 8; uf++) { - if (q.itd->hw_transaction[uf] & - ITD_ACTIVE(fusbh200)) - break; - } - if (uf < 8) { - q_p = &q.itd->itd_next; - hw_p = &q.itd->hw_next; - type = Q_NEXT_TYPE(fusbh200, - q.itd->hw_next); - q = *q_p; - break; - } - } - - /* Take finished ITDs out of the schedule - * and process them: recycle, maybe report - * URB completion. HC won't cache the - * pointer for much longer, if at all. - */ - *q_p = q.itd->itd_next; - *hw_p = q.itd->hw_next; - type = Q_NEXT_TYPE(fusbh200, q.itd->hw_next); - wmb(); - modified = itd_complete (fusbh200, q.itd); - q = *q_p; - break; - default: - fusbh200_dbg(fusbh200, "corrupt type %d frame %d shadow %p\n", - type, frame, q.ptr); - // BUG (); - /* FALL THROUGH */ - case Q_TYPE_QH: - case Q_TYPE_FSTN: - /* End of the iTDs and siTDs */ - q.ptr = NULL; - break; - } - - /* assume completion callbacks modify the queue */ - if (unlikely(modified && fusbh200->isoc_count > 0)) - goto restart; - } - - /* Stop when we have reached the current frame */ - if (frame == now_frame) - break; - frame = (frame + 1) & fmask; - } - fusbh200->next_frame = now_frame; -} -/*-------------------------------------------------------------------------*/ -/* - * Display / Set uframe_periodic_max - */ -static ssize_t show_uframe_periodic_max(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct fusbh200_hcd *fusbh200; - int n; - - fusbh200 = hcd_to_fusbh200(bus_to_hcd(dev_get_drvdata(dev))); - n = scnprintf(buf, PAGE_SIZE, "%d\n", fusbh200->uframe_periodic_max); - return n; -} - - -static ssize_t store_uframe_periodic_max(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - struct fusbh200_hcd *fusbh200; - unsigned uframe_periodic_max; - unsigned frame, uframe; - unsigned short allocated_max; - unsigned long flags; - ssize_t ret; - - fusbh200 = hcd_to_fusbh200(bus_to_hcd(dev_get_drvdata(dev))); - if (kstrtouint(buf, 0, &uframe_periodic_max) < 0) - return -EINVAL; - - if (uframe_periodic_max < 100 || uframe_periodic_max >= 125) { - fusbh200_info(fusbh200, "rejecting invalid request for " - "uframe_periodic_max=%u\n", uframe_periodic_max); - return -EINVAL; - } - - ret = -EINVAL; - - /* - * lock, so that our checking does not race with possible periodic - * bandwidth allocation through submitting new urbs. - */ - spin_lock_irqsave (&fusbh200->lock, flags); - - /* - * for request to decrease max periodic bandwidth, we have to check - * every microframe in the schedule to see whether the decrease is - * possible. - */ - if (uframe_periodic_max < fusbh200->uframe_periodic_max) { - allocated_max = 0; - - for (frame = 0; frame < fusbh200->periodic_size; ++frame) - for (uframe = 0; uframe < 7; ++uframe) - allocated_max = max(allocated_max, - periodic_usecs (fusbh200, frame, uframe)); - - if (allocated_max > uframe_periodic_max) { - fusbh200_info(fusbh200, - "cannot decrease uframe_periodic_max because " - "periodic bandwidth is already allocated " - "(%u > %u)\n", - allocated_max, uframe_periodic_max); - goto out_unlock; - } - } - - /* increasing is always ok */ - - fusbh200_info(fusbh200, "setting max periodic bandwidth to %u%% " - "(== %u usec/uframe)\n", - 100*uframe_periodic_max/125, uframe_periodic_max); - - if (uframe_periodic_max != 100) - fusbh200_warn(fusbh200, "max periodic bandwidth set is non-standard\n"); - - fusbh200->uframe_periodic_max = uframe_periodic_max; - ret = count; - -out_unlock: - spin_unlock_irqrestore (&fusbh200->lock, flags); - return ret; -} -static DEVICE_ATTR(uframe_periodic_max, 0644, show_uframe_periodic_max, store_uframe_periodic_max); - - -static inline int create_sysfs_files(struct fusbh200_hcd *fusbh200) -{ - struct device *controller = fusbh200_to_hcd(fusbh200)->self.controller; - int i = 0; - - if (i) - goto out; - - i = device_create_file(controller, &dev_attr_uframe_periodic_max); -out: - return i; -} - -static inline void remove_sysfs_files(struct fusbh200_hcd *fusbh200) -{ - struct device *controller = fusbh200_to_hcd(fusbh200)->self.controller; - - device_remove_file(controller, &dev_attr_uframe_periodic_max); -} -/*-------------------------------------------------------------------------*/ - -/* On some systems, leaving remote wakeup enabled prevents system shutdown. - * The firmware seems to think that powering off is a wakeup event! - * This routine turns off remote wakeup and everything else, on all ports. - */ -static void fusbh200_turn_off_all_ports(struct fusbh200_hcd *fusbh200) -{ - u32 __iomem *status_reg = &fusbh200->regs->port_status; - - fusbh200_writel(fusbh200, PORT_RWC_BITS, status_reg); -} - -/* - * Halt HC, turn off all ports, and let the BIOS use the companion controllers. - * Must be called with interrupts enabled and the lock not held. - */ -static void fusbh200_silence_controller(struct fusbh200_hcd *fusbh200) -{ - fusbh200_halt(fusbh200); - - spin_lock_irq(&fusbh200->lock); - fusbh200->rh_state = FUSBH200_RH_HALTED; - fusbh200_turn_off_all_ports(fusbh200); - spin_unlock_irq(&fusbh200->lock); -} - -/* fusbh200_shutdown kick in for silicon on any bus (not just pci, etc). - * This forcibly disables dma and IRQs, helping kexec and other cases - * where the next system software may expect clean state. - */ -static void fusbh200_shutdown(struct usb_hcd *hcd) -{ - struct fusbh200_hcd *fusbh200 = hcd_to_fusbh200(hcd); - - spin_lock_irq(&fusbh200->lock); - fusbh200->shutdown = true; - fusbh200->rh_state = FUSBH200_RH_STOPPING; - fusbh200->enabled_hrtimer_events = 0; - spin_unlock_irq(&fusbh200->lock); - - fusbh200_silence_controller(fusbh200); - - hrtimer_cancel(&fusbh200->hrtimer); -} - -/*-------------------------------------------------------------------------*/ - -/* - * fusbh200_work is called from some interrupts, timers, and so on. - * it calls driver completion functions, after dropping fusbh200->lock. - */ -static void fusbh200_work (struct fusbh200_hcd *fusbh200) -{ - /* another CPU may drop fusbh200->lock during a schedule scan while - * it reports urb completions. this flag guards against bogus - * attempts at re-entrant schedule scanning. - */ - if (fusbh200->scanning) { - fusbh200->need_rescan = true; - return; - } - fusbh200->scanning = true; - - rescan: - fusbh200->need_rescan = false; - if (fusbh200->async_count) - scan_async(fusbh200); - if (fusbh200->intr_count > 0) - scan_intr(fusbh200); - if (fusbh200->isoc_count > 0) - scan_isoc(fusbh200); - if (fusbh200->need_rescan) - goto rescan; - fusbh200->scanning = false; - - /* the IO watchdog guards against hardware or driver bugs that - * misplace IRQs, and should let us run completely without IRQs. - * such lossage has been observed on both VT6202 and VT8235. - */ - turn_on_io_watchdog(fusbh200); -} - -/* - * Called when the fusbh200_hcd module is removed. - */ -static void fusbh200_stop (struct usb_hcd *hcd) -{ - struct fusbh200_hcd *fusbh200 = hcd_to_fusbh200 (hcd); - - fusbh200_dbg (fusbh200, "stop\n"); - - /* no more interrupts ... */ - - spin_lock_irq(&fusbh200->lock); - fusbh200->enabled_hrtimer_events = 0; - spin_unlock_irq(&fusbh200->lock); - - fusbh200_quiesce(fusbh200); - fusbh200_silence_controller(fusbh200); - fusbh200_reset (fusbh200); - - hrtimer_cancel(&fusbh200->hrtimer); - remove_sysfs_files(fusbh200); - remove_debug_files (fusbh200); - - /* root hub is shut down separately (first, when possible) */ - spin_lock_irq (&fusbh200->lock); - end_free_itds(fusbh200); - spin_unlock_irq (&fusbh200->lock); - fusbh200_mem_cleanup (fusbh200); - - fusbh200_dbg(fusbh200, "irq normal %ld err %ld iaa %ld (lost %ld)\n", - fusbh200->stats.normal, fusbh200->stats.error, fusbh200->stats.iaa, - fusbh200->stats.lost_iaa); - fusbh200_dbg (fusbh200, "complete %ld unlink %ld\n", - fusbh200->stats.complete, fusbh200->stats.unlink); - - dbg_status (fusbh200, "fusbh200_stop completed", - fusbh200_readl(fusbh200, &fusbh200->regs->status)); -} - -/* one-time init, only for memory state */ -static int hcd_fusbh200_init(struct usb_hcd *hcd) -{ - struct fusbh200_hcd *fusbh200 = hcd_to_fusbh200(hcd); - u32 temp; - int retval; - u32 hcc_params; - struct fusbh200_qh_hw *hw; - - spin_lock_init(&fusbh200->lock); - - /* - * keep io watchdog by default, those good HCDs could turn off it later - */ - fusbh200->need_io_watchdog = 1; - - hrtimer_init(&fusbh200->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS); - fusbh200->hrtimer.function = fusbh200_hrtimer_func; - fusbh200->next_hrtimer_event = FUSBH200_HRTIMER_NO_EVENT; - - hcc_params = fusbh200_readl(fusbh200, &fusbh200->caps->hcc_params); - - /* - * by default set standard 80% (== 100 usec/uframe) max periodic - * bandwidth as required by USB 2.0 - */ - fusbh200->uframe_periodic_max = 100; - - /* - * hw default: 1K periodic list heads, one per frame. - * periodic_size can shrink by USBCMD update if hcc_params allows. - */ - fusbh200->periodic_size = DEFAULT_I_TDPS; - INIT_LIST_HEAD(&fusbh200->intr_qh_list); - INIT_LIST_HEAD(&fusbh200->cached_itd_list); - - if (HCC_PGM_FRAMELISTLEN(hcc_params)) { - /* periodic schedule size can be smaller than default */ - switch (FUSBH200_TUNE_FLS) { - case 0: fusbh200->periodic_size = 1024; break; - case 1: fusbh200->periodic_size = 512; break; - case 2: fusbh200->periodic_size = 256; break; - default: BUG(); - } - } - if ((retval = fusbh200_mem_init(fusbh200, GFP_KERNEL)) < 0) - return retval; - - /* controllers may cache some of the periodic schedule ... */ - fusbh200->i_thresh = 2; - - /* - * dedicate a qh for the async ring head, since we couldn't unlink - * a 'real' qh without stopping the async schedule [4.8]. use it - * as the 'reclamation list head' too. - * its dummy is used in hw_alt_next of many tds, to prevent the qh - * from automatically advancing to the next td after short reads. - */ - fusbh200->async->qh_next.qh = NULL; - hw = fusbh200->async->hw; - hw->hw_next = QH_NEXT(fusbh200, fusbh200->async->qh_dma); - hw->hw_info1 = cpu_to_hc32(fusbh200, QH_HEAD); - hw->hw_token = cpu_to_hc32(fusbh200, QTD_STS_HALT); - hw->hw_qtd_next = FUSBH200_LIST_END(fusbh200); - fusbh200->async->qh_state = QH_STATE_LINKED; - hw->hw_alt_next = QTD_NEXT(fusbh200, fusbh200->async->dummy->qtd_dma); - - /* clear interrupt enables, set irq latency */ - if (log2_irq_thresh < 0 || log2_irq_thresh > 6) - log2_irq_thresh = 0; - temp = 1 << (16 + log2_irq_thresh); - if (HCC_CANPARK(hcc_params)) { - /* HW default park == 3, on hardware that supports it (like - * NVidia and ALI silicon), maximizes throughput on the async - * schedule by avoiding QH fetches between transfers. - * - * With fast usb storage devices and NForce2, "park" seems to - * make problems: throughput reduction (!), data errors... - */ - if (park) { - park = min(park, (unsigned) 3); - temp |= CMD_PARK; - temp |= park << 8; - } - fusbh200_dbg(fusbh200, "park %d\n", park); - } - if (HCC_PGM_FRAMELISTLEN(hcc_params)) { - /* periodic schedule size can be smaller than default */ - temp &= ~(3 << 2); - temp |= (FUSBH200_TUNE_FLS << 2); - } - fusbh200->command = temp; - - /* Accept arbitrarily long scatter-gather lists */ - if (!(hcd->driver->flags & HCD_LOCAL_MEM)) - hcd->self.sg_tablesize = ~0; - return 0; -} - -/* start HC running; it's halted, hcd_fusbh200_init() has been run (once) */ -static int fusbh200_run (struct usb_hcd *hcd) -{ - struct fusbh200_hcd *fusbh200 = hcd_to_fusbh200 (hcd); - u32 temp; - u32 hcc_params; - - hcd->uses_new_polling = 1; - - /* EHCI spec section 4.1 */ - - fusbh200_writel(fusbh200, fusbh200->periodic_dma, &fusbh200->regs->frame_list); - fusbh200_writel(fusbh200, (u32)fusbh200->async->qh_dma, &fusbh200->regs->async_next); - - /* - * hcc_params controls whether fusbh200->regs->segment must (!!!) - * be used; it constrains QH/ITD/SITD and QTD locations. - * pci_pool consistent memory always uses segment zero. - * streaming mappings for I/O buffers, like pci_map_single(), - * can return segments above 4GB, if the device allows. - * - * NOTE: the dma mask is visible through dma_supported(), so - * drivers can pass this info along ... like NETIF_F_HIGHDMA, - * Scsi_Host.highmem_io, and so forth. It's readonly to all - * host side drivers though. - */ - hcc_params = fusbh200_readl(fusbh200, &fusbh200->caps->hcc_params); - - // Philips, Intel, and maybe others need CMD_RUN before the - // root hub will detect new devices (why?); NEC doesn't - fusbh200->command &= ~(CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET); - fusbh200->command |= CMD_RUN; - fusbh200_writel(fusbh200, fusbh200->command, &fusbh200->regs->command); - dbg_cmd (fusbh200, "init", fusbh200->command); - - /* - * Start, enabling full USB 2.0 functionality ... usb 1.1 devices - * are explicitly handed to companion controller(s), so no TT is - * involved with the root hub. (Except where one is integrated, - * and there's no companion controller unless maybe for USB OTG.) - * - * Turning on the CF flag will transfer ownership of all ports - * from the companions to the EHCI controller. If any of the - * companions are in the middle of a port reset at the time, it - * could cause trouble. Write-locking ehci_cf_port_reset_rwsem - * guarantees that no resets are in progress. After we set CF, - * a short delay lets the hardware catch up; new resets shouldn't - * be started before the port switching actions could complete. - */ - down_write(&ehci_cf_port_reset_rwsem); - fusbh200->rh_state = FUSBH200_RH_RUNNING; - fusbh200_readl(fusbh200, &fusbh200->regs->command); /* unblock posted writes */ - msleep(5); - up_write(&ehci_cf_port_reset_rwsem); - fusbh200->last_periodic_enable = ktime_get_real(); - - temp = HC_VERSION(fusbh200, fusbh200_readl(fusbh200, &fusbh200->caps->hc_capbase)); - fusbh200_info (fusbh200, - "USB %x.%x started, EHCI %x.%02x\n", - ((fusbh200->sbrn & 0xf0)>>4), (fusbh200->sbrn & 0x0f), - temp >> 8, temp & 0xff); - - fusbh200_writel(fusbh200, INTR_MASK, - &fusbh200->regs->intr_enable); /* Turn On Interrupts */ - - /* GRR this is run-once init(), being done every time the HC starts. - * So long as they're part of class devices, we can't do it init() - * since the class device isn't created that early. - */ - create_debug_files(fusbh200); - create_sysfs_files(fusbh200); - - return 0; -} - -static int fusbh200_setup(struct usb_hcd *hcd) -{ - struct fusbh200_hcd *fusbh200 = hcd_to_fusbh200(hcd); - int retval; - - fusbh200->regs = (void __iomem *)fusbh200->caps + - HC_LENGTH(fusbh200, fusbh200_readl(fusbh200, &fusbh200->caps->hc_capbase)); - dbg_hcs_params(fusbh200, "reset"); - dbg_hcc_params(fusbh200, "reset"); - - /* cache this readonly data; minimize chip reads */ - fusbh200->hcs_params = fusbh200_readl(fusbh200, &fusbh200->caps->hcs_params); - - fusbh200->sbrn = HCD_USB2; - - /* data structure init */ - retval = hcd_fusbh200_init(hcd); - if (retval) - return retval; - - retval = fusbh200_halt(fusbh200); - if (retval) - return retval; - - fusbh200_reset(fusbh200); - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -static irqreturn_t fusbh200_irq (struct usb_hcd *hcd) -{ - struct fusbh200_hcd *fusbh200 = hcd_to_fusbh200 (hcd); - u32 status, masked_status, pcd_status = 0, cmd; - int bh; - - spin_lock (&fusbh200->lock); - - status = fusbh200_readl(fusbh200, &fusbh200->regs->status); - - /* e.g. cardbus physical eject */ - if (status == ~(u32) 0) { - fusbh200_dbg (fusbh200, "device removed\n"); - goto dead; - } - - /* - * We don't use STS_FLR, but some controllers don't like it to - * remain on, so mask it out along with the other status bits. - */ - masked_status = status & (INTR_MASK | STS_FLR); - - /* Shared IRQ? */ - if (!masked_status || unlikely(fusbh200->rh_state == FUSBH200_RH_HALTED)) { - spin_unlock(&fusbh200->lock); - return IRQ_NONE; - } - - /* clear (just) interrupts */ - fusbh200_writel(fusbh200, masked_status, &fusbh200->regs->status); - cmd = fusbh200_readl(fusbh200, &fusbh200->regs->command); - bh = 0; - - /* normal [4.15.1.2] or error [4.15.1.1] completion */ - if (likely ((status & (STS_INT|STS_ERR)) != 0)) { - if (likely ((status & STS_ERR) == 0)) - COUNT (fusbh200->stats.normal); - else - COUNT (fusbh200->stats.error); - bh = 1; - } - - /* complete the unlinking of some qh [4.15.2.3] */ - if (status & STS_IAA) { - - /* Turn off the IAA watchdog */ - fusbh200->enabled_hrtimer_events &= ~BIT(FUSBH200_HRTIMER_IAA_WATCHDOG); - - /* - * Mild optimization: Allow another IAAD to reset the - * hrtimer, if one occurs before the next expiration. - * In theory we could always cancel the hrtimer, but - * tests show that about half the time it will be reset - * for some other event anyway. - */ - if (fusbh200->next_hrtimer_event == FUSBH200_HRTIMER_IAA_WATCHDOG) - ++fusbh200->next_hrtimer_event; - - /* guard against (alleged) silicon errata */ - if (cmd & CMD_IAAD) - fusbh200_dbg(fusbh200, "IAA with IAAD still set?\n"); - if (fusbh200->async_iaa) { - COUNT(fusbh200->stats.iaa); - end_unlink_async(fusbh200); - } else - fusbh200_dbg(fusbh200, "IAA with nothing unlinked?\n"); - } - - /* remote wakeup [4.3.1] */ - if (status & STS_PCD) { - int pstatus; - u32 __iomem *status_reg = &fusbh200->regs->port_status; - - /* kick root hub later */ - pcd_status = status; - - /* resume root hub? */ - if (fusbh200->rh_state == FUSBH200_RH_SUSPENDED) - usb_hcd_resume_root_hub(hcd); - - pstatus = fusbh200_readl(fusbh200, status_reg); - - if (test_bit(0, &fusbh200->suspended_ports) && - ((pstatus & PORT_RESUME) || - !(pstatus & PORT_SUSPEND)) && - (pstatus & PORT_PE) && - fusbh200->reset_done[0] == 0) { - - /* start 20 msec resume signaling from this port, - * and make hub_wq collect PORT_STAT_C_SUSPEND to - * stop that signaling. Use 5 ms extra for safety, - * like usb_port_resume() does. - */ - fusbh200->reset_done[0] = jiffies + msecs_to_jiffies(25); - set_bit(0, &fusbh200->resuming_ports); - fusbh200_dbg (fusbh200, "port 1 remote wakeup\n"); - mod_timer(&hcd->rh_timer, fusbh200->reset_done[0]); - } - } - - /* PCI errors [4.15.2.4] */ - if (unlikely ((status & STS_FATAL) != 0)) { - fusbh200_err(fusbh200, "fatal error\n"); - dbg_cmd(fusbh200, "fatal", cmd); - dbg_status(fusbh200, "fatal", status); -dead: - usb_hc_died(hcd); - - /* Don't let the controller do anything more */ - fusbh200->shutdown = true; - fusbh200->rh_state = FUSBH200_RH_STOPPING; - fusbh200->command &= ~(CMD_RUN | CMD_ASE | CMD_PSE); - fusbh200_writel(fusbh200, fusbh200->command, &fusbh200->regs->command); - fusbh200_writel(fusbh200, 0, &fusbh200->regs->intr_enable); - fusbh200_handle_controller_death(fusbh200); - - /* Handle completions when the controller stops */ - bh = 0; - } - - if (bh) - fusbh200_work (fusbh200); - spin_unlock (&fusbh200->lock); - if (pcd_status) - usb_hcd_poll_rh_status(hcd); - return IRQ_HANDLED; -} - -/*-------------------------------------------------------------------------*/ - -/* - * non-error returns are a promise to giveback() the urb later - * we drop ownership so next owner (or urb unlink) can get it - * - * urb + dev is in hcd.self.controller.urb_list - * we're queueing TDs onto software and hardware lists - * - * hcd-specific init for hcpriv hasn't been done yet - * - * NOTE: control, bulk, and interrupt share the same code to append TDs - * to a (possibly active) QH, and the same QH scanning code. - */ -static int fusbh200_urb_enqueue ( - struct usb_hcd *hcd, - struct urb *urb, - gfp_t mem_flags -) { - struct fusbh200_hcd *fusbh200 = hcd_to_fusbh200 (hcd); - struct list_head qtd_list; - - INIT_LIST_HEAD (&qtd_list); - - switch (usb_pipetype (urb->pipe)) { - case PIPE_CONTROL: - /* qh_completions() code doesn't handle all the fault cases - * in multi-TD control transfers. Even 1KB is rare anyway. - */ - if (urb->transfer_buffer_length > (16 * 1024)) - return -EMSGSIZE; - /* FALLTHROUGH */ - /* case PIPE_BULK: */ - default: - if (!qh_urb_transaction (fusbh200, urb, &qtd_list, mem_flags)) - return -ENOMEM; - return submit_async(fusbh200, urb, &qtd_list, mem_flags); - - case PIPE_INTERRUPT: - if (!qh_urb_transaction (fusbh200, urb, &qtd_list, mem_flags)) - return -ENOMEM; - return intr_submit(fusbh200, urb, &qtd_list, mem_flags); - - case PIPE_ISOCHRONOUS: - return itd_submit (fusbh200, urb, mem_flags); - } -} - -/* remove from hardware lists - * completions normally happen asynchronously - */ - -static int fusbh200_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) -{ - struct fusbh200_hcd *fusbh200 = hcd_to_fusbh200 (hcd); - struct fusbh200_qh *qh; - unsigned long flags; - int rc; - - spin_lock_irqsave (&fusbh200->lock, flags); - rc = usb_hcd_check_unlink_urb(hcd, urb, status); - if (rc) - goto done; - - switch (usb_pipetype (urb->pipe)) { - // case PIPE_CONTROL: - // case PIPE_BULK: - default: - qh = (struct fusbh200_qh *) urb->hcpriv; - if (!qh) - break; - switch (qh->qh_state) { - case QH_STATE_LINKED: - case QH_STATE_COMPLETING: - start_unlink_async(fusbh200, qh); - break; - case QH_STATE_UNLINK: - case QH_STATE_UNLINK_WAIT: - /* already started */ - break; - case QH_STATE_IDLE: - /* QH might be waiting for a Clear-TT-Buffer */ - qh_completions(fusbh200, qh); - break; - } - break; - - case PIPE_INTERRUPT: - qh = (struct fusbh200_qh *) urb->hcpriv; - if (!qh) - break; - switch (qh->qh_state) { - case QH_STATE_LINKED: - case QH_STATE_COMPLETING: - start_unlink_intr(fusbh200, qh); - break; - case QH_STATE_IDLE: - qh_completions (fusbh200, qh); - break; - default: - fusbh200_dbg (fusbh200, "bogus qh %p state %d\n", - qh, qh->qh_state); - goto done; - } - break; - - case PIPE_ISOCHRONOUS: - // itd... - - // wait till next completion, do it then. - // completion irqs can wait up to 1024 msec, - break; - } -done: - spin_unlock_irqrestore (&fusbh200->lock, flags); - return rc; -} - -/*-------------------------------------------------------------------------*/ - -// bulk qh holds the data toggle - -static void -fusbh200_endpoint_disable (struct usb_hcd *hcd, struct usb_host_endpoint *ep) -{ - struct fusbh200_hcd *fusbh200 = hcd_to_fusbh200 (hcd); - unsigned long flags; - struct fusbh200_qh *qh, *tmp; - - /* ASSERT: any requests/urbs are being unlinked */ - /* ASSERT: nobody can be submitting urbs for this any more */ - -rescan: - spin_lock_irqsave (&fusbh200->lock, flags); - qh = ep->hcpriv; - if (!qh) - goto done; - - /* endpoints can be iso streams. for now, we don't - * accelerate iso completions ... so spin a while. - */ - if (qh->hw == NULL) { - struct fusbh200_iso_stream *stream = ep->hcpriv; - - if (!list_empty(&stream->td_list)) - goto idle_timeout; - - /* BUG_ON(!list_empty(&stream->free_list)); */ - kfree(stream); - goto done; - } - - if (fusbh200->rh_state < FUSBH200_RH_RUNNING) - qh->qh_state = QH_STATE_IDLE; - switch (qh->qh_state) { - case QH_STATE_LINKED: - case QH_STATE_COMPLETING: - for (tmp = fusbh200->async->qh_next.qh; - tmp && tmp != qh; - tmp = tmp->qh_next.qh) - continue; - /* periodic qh self-unlinks on empty, and a COMPLETING qh - * may already be unlinked. - */ - if (tmp) - start_unlink_async(fusbh200, qh); - /* FALL THROUGH */ - case QH_STATE_UNLINK: /* wait for hw to finish? */ - case QH_STATE_UNLINK_WAIT: -idle_timeout: - spin_unlock_irqrestore (&fusbh200->lock, flags); - schedule_timeout_uninterruptible(1); - goto rescan; - case QH_STATE_IDLE: /* fully unlinked */ - if (qh->clearing_tt) - goto idle_timeout; - if (list_empty (&qh->qtd_list)) { - qh_destroy(fusbh200, qh); - break; - } - /* else FALL THROUGH */ - default: - /* caller was supposed to have unlinked any requests; - * that's not our job. just leak this memory. - */ - fusbh200_err (fusbh200, "qh %p (#%02x) state %d%s\n", - qh, ep->desc.bEndpointAddress, qh->qh_state, - list_empty (&qh->qtd_list) ? "" : "(has tds)"); - break; - } - done: - ep->hcpriv = NULL; - spin_unlock_irqrestore (&fusbh200->lock, flags); -} - -static void -fusbh200_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep) -{ - struct fusbh200_hcd *fusbh200 = hcd_to_fusbh200(hcd); - struct fusbh200_qh *qh; - int eptype = usb_endpoint_type(&ep->desc); - int epnum = usb_endpoint_num(&ep->desc); - int is_out = usb_endpoint_dir_out(&ep->desc); - unsigned long flags; - - if (eptype != USB_ENDPOINT_XFER_BULK && eptype != USB_ENDPOINT_XFER_INT) - return; - - spin_lock_irqsave(&fusbh200->lock, flags); - qh = ep->hcpriv; - - /* For Bulk and Interrupt endpoints we maintain the toggle state - * in the hardware; the toggle bits in udev aren't used at all. - * When an endpoint is reset by usb_clear_halt() we must reset - * the toggle bit in the QH. - */ - if (qh) { - usb_settoggle(qh->dev, epnum, is_out, 0); - if (!list_empty(&qh->qtd_list)) { - WARN_ONCE(1, "clear_halt for a busy endpoint\n"); - } else if (qh->qh_state == QH_STATE_LINKED || - qh->qh_state == QH_STATE_COMPLETING) { - - /* The toggle value in the QH can't be updated - * while the QH is active. Unlink it now; - * re-linking will call qh_refresh(). - */ - if (eptype == USB_ENDPOINT_XFER_BULK) - start_unlink_async(fusbh200, qh); - else - start_unlink_intr(fusbh200, qh); - } - } - spin_unlock_irqrestore(&fusbh200->lock, flags); -} - -static int fusbh200_get_frame (struct usb_hcd *hcd) -{ - struct fusbh200_hcd *fusbh200 = hcd_to_fusbh200 (hcd); - return (fusbh200_read_frame_index(fusbh200) >> 3) % fusbh200->periodic_size; -} - -/*-------------------------------------------------------------------------*/ - -/* - * The EHCI in ChipIdea HDRC cannot be a separate module or device, - * because its registers (and irq) are shared between host/gadget/otg - * functions and in order to facilitate role switching we cannot - * give the fusbh200 driver exclusive access to those. - */ -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_AUTHOR (DRIVER_AUTHOR); -MODULE_LICENSE ("GPL"); - -static const struct hc_driver fusbh200_fusbh200_hc_driver = { - .description = hcd_name, - .product_desc = "Faraday USB2.0 Host Controller", - .hcd_priv_size = sizeof(struct fusbh200_hcd), - - /* - * generic hardware linkage - */ - .irq = fusbh200_irq, - .flags = HCD_MEMORY | HCD_USB2, - - /* - * basic lifecycle operations - */ - .reset = hcd_fusbh200_init, - .start = fusbh200_run, - .stop = fusbh200_stop, - .shutdown = fusbh200_shutdown, - - /* - * managing i/o requests and associated device resources - */ - .urb_enqueue = fusbh200_urb_enqueue, - .urb_dequeue = fusbh200_urb_dequeue, - .endpoint_disable = fusbh200_endpoint_disable, - .endpoint_reset = fusbh200_endpoint_reset, - - /* - * scheduling support - */ - .get_frame_number = fusbh200_get_frame, - - /* - * root hub support - */ - .hub_status_data = fusbh200_hub_status_data, - .hub_control = fusbh200_hub_control, - .bus_suspend = fusbh200_bus_suspend, - .bus_resume = fusbh200_bus_resume, - - .relinquish_port = fusbh200_relinquish_port, - .port_handed_over = fusbh200_port_handed_over, - - .clear_tt_buffer_complete = fusbh200_clear_tt_buffer_complete, -}; - -static void fusbh200_init(struct fusbh200_hcd *fusbh200) -{ - u32 reg; - - reg = fusbh200_readl(fusbh200, &fusbh200->regs->bmcsr); - reg |= BMCSR_INT_POLARITY; - reg &= ~BMCSR_VBUS_OFF; - fusbh200_writel(fusbh200, reg, &fusbh200->regs->bmcsr); - - reg = fusbh200_readl(fusbh200, &fusbh200->regs->bmier); - fusbh200_writel(fusbh200, reg | BMIER_OVC_EN | BMIER_VBUS_ERR_EN, - &fusbh200->regs->bmier); -} - -/** - * fusbh200_hcd_probe - initialize faraday FUSBH200 HCDs - * - * Allocates basic resources for this USB host controller, and - * then invokes the start() method for the HCD associated with it - * through the hotplug entry's driver_data. - */ -static int fusbh200_hcd_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct usb_hcd *hcd; - struct resource *res; - int irq; - int retval = -ENODEV; - struct fusbh200_hcd *fusbh200; - - if (usb_disabled()) - return -ENODEV; - - pdev->dev.power.power_state = PMSG_ON; - - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { - dev_err(dev, - "Found HC with no IRQ. Check %s setup!\n", - dev_name(dev)); - return -ENODEV; - } - - irq = res->start; - - hcd = usb_create_hcd(&fusbh200_fusbh200_hc_driver, dev, - dev_name(dev)); - if (!hcd) { - dev_err(dev, "failed to create hcd with err %d\n", retval); - retval = -ENOMEM; - goto fail_create_hcd; - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(dev, - "Found HC with no register addr. Check %s setup!\n", - dev_name(dev)); - retval = -ENODEV; - goto fail_request_resource; - } - - hcd->rsrc_start = res->start; - hcd->rsrc_len = resource_size(res); - hcd->has_tt = 1; - - if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, - fusbh200_fusbh200_hc_driver.description)) { - dev_dbg(dev, "controller already in use\n"); - retval = -EBUSY; - goto fail_request_resource; - } - - res = platform_get_resource(pdev, IORESOURCE_IO, 0); - if (!res) { - dev_err(dev, - "Found HC with no register addr. Check %s setup!\n", - dev_name(dev)); - retval = -ENODEV; - goto fail_request_resource; - } - - hcd->regs = ioremap_nocache(res->start, resource_size(res)); - if (hcd->regs == NULL) { - dev_dbg(dev, "error mapping memory\n"); - retval = -EFAULT; - goto fail_ioremap; - } - - fusbh200 = hcd_to_fusbh200(hcd); - - fusbh200->caps = hcd->regs; - - retval = fusbh200_setup(hcd); - if (retval) - goto fail_add_hcd; - - fusbh200_init(fusbh200); - - retval = usb_add_hcd(hcd, irq, IRQF_SHARED); - if (retval) { - dev_err(dev, "failed to add hcd with err %d\n", retval); - goto fail_add_hcd; - } - device_wakeup_enable(hcd->self.controller); - - return retval; - -fail_add_hcd: - iounmap(hcd->regs); -fail_ioremap: - release_mem_region(hcd->rsrc_start, hcd->rsrc_len); -fail_request_resource: - usb_put_hcd(hcd); -fail_create_hcd: - dev_err(dev, "init %s fail, %d\n", dev_name(dev), retval); - return retval; -} - -/** - * fusbh200_hcd_remove - shutdown processing for EHCI HCDs - * @dev: USB Host Controller being removed - * - * Reverses the effect of fotg2xx_usb_hcd_probe(), first invoking - * the HCD's stop() method. It is always called from a thread - * context, normally "rmmod", "apmd", or something similar. - */ -static int fusbh200_hcd_remove(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct usb_hcd *hcd = dev_get_drvdata(dev); - - if (!hcd) - return 0; - - 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 fusbh200_hcd_fusbh200_driver = { - .driver = { - .name = "fusbh200", - }, - .probe = fusbh200_hcd_probe, - .remove = fusbh200_hcd_remove, -}; - -static int __init fusbh200_hcd_init(void) -{ - int retval = 0; - - if (usb_disabled()) - return -ENODEV; - - printk(KERN_INFO "%s: " DRIVER_DESC "\n", hcd_name); - set_bit(USB_EHCI_LOADED, &usb_hcds_loaded); - if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) || - test_bit(USB_OHCI_LOADED, &usb_hcds_loaded)) - printk(KERN_WARNING "Warning! fusbh200_hcd should always be loaded" - " before uhci_hcd and ohci_hcd, not after\n"); - - pr_debug("%s: block sizes: qh %Zd qtd %Zd itd %Zd\n", - hcd_name, - sizeof(struct fusbh200_qh), sizeof(struct fusbh200_qtd), - sizeof(struct fusbh200_itd)); - - fusbh200_debug_root = debugfs_create_dir("fusbh200", usb_debug_root); - if (!fusbh200_debug_root) { - retval = -ENOENT; - goto err_debug; - } - - retval = platform_driver_register(&fusbh200_hcd_fusbh200_driver); - if (retval < 0) - goto clean; - return retval; - - platform_driver_unregister(&fusbh200_hcd_fusbh200_driver); -clean: - debugfs_remove(fusbh200_debug_root); - fusbh200_debug_root = NULL; -err_debug: - clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); - return retval; -} -module_init(fusbh200_hcd_init); - -static void __exit fusbh200_hcd_cleanup(void) -{ - platform_driver_unregister(&fusbh200_hcd_fusbh200_driver); - debugfs_remove(fusbh200_debug_root); - clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); -} -module_exit(fusbh200_hcd_cleanup); diff --git a/drivers/usb/host/fusbh200.h b/drivers/usb/host/fusbh200.h deleted file mode 100644 index d6e5b3d..0000000 --- a/drivers/usb/host/fusbh200.h +++ /dev/null @@ -1,675 +0,0 @@ -#ifndef __LINUX_FUSBH200_H -#define __LINUX_FUSBH200_H - -#include <linux/usb/ehci-dbgp.h> - -/* definitions used for the EHCI driver */ - -/* - * __hc32 and __hc16 are "Host Controller" types, they may be equivalent to - * __leXX (normally) or __beXX (given FUSBH200_BIG_ENDIAN_DESC), depending on - * the host controller implementation. - * - * To facilitate the strongest possible byte-order checking from "sparse" - * and so on, we use __leXX unless that's not practical. - */ -#define __hc32 __le32 -#define __hc16 __le16 - -/* statistics can be kept for tuning/monitoring */ -struct fusbh200_stats { - /* irq usage */ - unsigned long normal; - unsigned long error; - unsigned long iaa; - unsigned long lost_iaa; - - /* termination of urbs from core */ - unsigned long complete; - unsigned long unlink; -}; - -/* fusbh200_hcd->lock guards shared data against other CPUs: - * fusbh200_hcd: async, unlink, periodic (and shadow), ... - * usb_host_endpoint: hcpriv - * fusbh200_qh: qh_next, qtd_list - * fusbh200_qtd: qtd_list - * - * Also, hold this lock when talking to HC registers or - * when updating hw_* fields in shared qh/qtd/... structures. - */ - -#define FUSBH200_MAX_ROOT_PORTS 1 /* see HCS_N_PORTS */ - -/* - * fusbh200_rh_state values of FUSBH200_RH_RUNNING or above mean that the - * controller may be doing DMA. Lower values mean there's no DMA. - */ -enum fusbh200_rh_state { - FUSBH200_RH_HALTED, - FUSBH200_RH_SUSPENDED, - FUSBH200_RH_RUNNING, - FUSBH200_RH_STOPPING -}; - -/* - * Timer events, ordered by increasing delay length. - * Always update event_delays_ns[] and event_handlers[] (defined in - * ehci-timer.c) in parallel with this list. - */ -enum fusbh200_hrtimer_event { - FUSBH200_HRTIMER_POLL_ASS, /* Poll for async schedule off */ - FUSBH200_HRTIMER_POLL_PSS, /* Poll for periodic schedule off */ - FUSBH200_HRTIMER_POLL_DEAD, /* Wait for dead controller to stop */ - FUSBH200_HRTIMER_UNLINK_INTR, /* Wait for interrupt QH unlink */ - FUSBH200_HRTIMER_FREE_ITDS, /* Wait for unused iTDs and siTDs */ - FUSBH200_HRTIMER_ASYNC_UNLINKS, /* Unlink empty async QHs */ - FUSBH200_HRTIMER_IAA_WATCHDOG, /* Handle lost IAA interrupts */ - FUSBH200_HRTIMER_DISABLE_PERIODIC, /* Wait to disable periodic sched */ - FUSBH200_HRTIMER_DISABLE_ASYNC, /* Wait to disable async sched */ - FUSBH200_HRTIMER_IO_WATCHDOG, /* Check for missing IRQs */ - FUSBH200_HRTIMER_NUM_EVENTS /* Must come last */ -}; -#define FUSBH200_HRTIMER_NO_EVENT 99 - -struct fusbh200_hcd { /* one per controller */ - /* timing support */ - enum fusbh200_hrtimer_event next_hrtimer_event; - unsigned enabled_hrtimer_events; - ktime_t hr_timeouts[FUSBH200_HRTIMER_NUM_EVENTS]; - struct hrtimer hrtimer; - - int PSS_poll_count; - int ASS_poll_count; - int died_poll_count; - - /* glue to PCI and HCD framework */ - struct fusbh200_caps __iomem *caps; - struct fusbh200_regs __iomem *regs; - struct ehci_dbg_port __iomem *debug; - - __u32 hcs_params; /* cached register copy */ - spinlock_t lock; - enum fusbh200_rh_state rh_state; - - /* general schedule support */ - bool scanning:1; - bool need_rescan:1; - bool intr_unlinking:1; - bool async_unlinking:1; - bool shutdown:1; - struct fusbh200_qh *qh_scan_next; - - /* async schedule support */ - struct fusbh200_qh *async; - struct fusbh200_qh *dummy; /* For AMD quirk use */ - struct fusbh200_qh *async_unlink; - struct fusbh200_qh *async_unlink_last; - struct fusbh200_qh *async_iaa; - unsigned async_unlink_cycle; - unsigned async_count; /* async activity count */ - - /* periodic schedule support */ -#define DEFAULT_I_TDPS 1024 /* some HCs can do less */ - unsigned periodic_size; - __hc32 *periodic; /* hw periodic table */ - dma_addr_t periodic_dma; - struct list_head intr_qh_list; - unsigned i_thresh; /* uframes HC might cache */ - - union fusbh200_shadow *pshadow; /* mirror hw periodic table */ - struct fusbh200_qh *intr_unlink; - struct fusbh200_qh *intr_unlink_last; - unsigned intr_unlink_cycle; - unsigned now_frame; /* frame from HC hardware */ - unsigned next_frame; /* scan periodic, start here */ - unsigned intr_count; /* intr activity count */ - unsigned isoc_count; /* isoc activity count */ - unsigned periodic_count; /* periodic activity count */ - unsigned uframe_periodic_max; /* max periodic time per uframe */ - - - /* list of itds completed while now_frame was still active */ - struct list_head cached_itd_list; - struct fusbh200_itd *last_itd_to_free; - - /* per root hub port */ - unsigned long reset_done [FUSBH200_MAX_ROOT_PORTS]; - - /* bit vectors (one bit per port) */ - unsigned long bus_suspended; /* which ports were - already suspended at the start of a bus suspend */ - unsigned long companion_ports; /* which ports are - dedicated to the companion controller */ - unsigned long owned_ports; /* which ports are - owned by the companion during a bus suspend */ - unsigned long port_c_suspend; /* which ports have - the change-suspend feature turned on */ - unsigned long suspended_ports; /* which ports are - suspended */ - unsigned long resuming_ports; /* which ports have - started to resume */ - - /* per-HC memory pools (could be per-bus, but ...) */ - struct dma_pool *qh_pool; /* qh per active urb */ - struct dma_pool *qtd_pool; /* one or more per qh */ - struct dma_pool *itd_pool; /* itd per iso urb */ - - unsigned random_frame; - unsigned long next_statechange; - ktime_t last_periodic_enable; - u32 command; - - /* SILICON QUIRKS */ - unsigned need_io_watchdog:1; - unsigned fs_i_thresh:1; /* Intel iso scheduling */ - - u8 sbrn; /* packed release number */ - - /* irq statistics */ - struct fusbh200_stats stats; -# define COUNT(x) do { (x)++; } while (0) - - /* debug files */ - struct dentry *debug_dir; -}; - -/* convert between an HCD pointer and the corresponding FUSBH200_HCD */ -static inline struct fusbh200_hcd *hcd_to_fusbh200 (struct usb_hcd *hcd) -{ - return (struct fusbh200_hcd *) (hcd->hcd_priv); -} -static inline struct usb_hcd *fusbh200_to_hcd (struct fusbh200_hcd *fusbh200) -{ - return container_of ((void *) fusbh200, struct usb_hcd, hcd_priv); -} - -/*-------------------------------------------------------------------------*/ - -/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */ - -/* Section 2.2 Host Controller Capability Registers */ -struct fusbh200_caps { - /* these fields are specified as 8 and 16 bit registers, - * but some hosts can't perform 8 or 16 bit PCI accesses. - * some hosts treat caplength and hciversion as parts of a 32-bit - * register, others treat them as two separate registers, this - * affects the memory map for big endian controllers. - */ - u32 hc_capbase; -#define HC_LENGTH(fusbh200, p) (0x00ff&((p) >> /* bits 7:0 / offset 00h */ \ - (fusbh200_big_endian_capbase(fusbh200) ? 24 : 0))) -#define HC_VERSION(fusbh200, p) (0xffff&((p) >> /* bits 31:16 / offset 02h */ \ - (fusbh200_big_endian_capbase(fusbh200) ? 0 : 16))) - u32 hcs_params; /* HCSPARAMS - offset 0x4 */ -#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */ - - u32 hcc_params; /* HCCPARAMS - offset 0x8 */ -#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */ -#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/ - u8 portroute[8]; /* nibbles for routing - offset 0xC */ -}; - - -/* Section 2.3 Host Controller Operational Registers */ -struct fusbh200_regs { - - /* USBCMD: offset 0x00 */ - u32 command; - -/* EHCI 1.1 addendum */ -/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */ -#define CMD_PARK (1<<11) /* enable "park" on async qh */ -#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */ -#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */ -#define CMD_ASE (1<<5) /* async schedule enable */ -#define CMD_PSE (1<<4) /* periodic schedule enable */ -/* 3:2 is periodic frame list size */ -#define CMD_RESET (1<<1) /* reset HC not bus */ -#define CMD_RUN (1<<0) /* start/stop HC */ - - /* USBSTS: offset 0x04 */ - u32 status; -#define STS_ASS (1<<15) /* Async Schedule Status */ -#define STS_PSS (1<<14) /* Periodic Schedule Status */ -#define STS_RECL (1<<13) /* Reclamation */ -#define STS_HALT (1<<12) /* Not running (any reason) */ -/* some bits reserved */ - /* these STS_* flags are also intr_enable bits (USBINTR) */ -#define STS_IAA (1<<5) /* Interrupted on async advance */ -#define STS_FATAL (1<<4) /* such as some PCI access errors */ -#define STS_FLR (1<<3) /* frame list rolled over */ -#define STS_PCD (1<<2) /* port change detect */ -#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */ -#define STS_INT (1<<0) /* "normal" completion (short, ...) */ - - /* USBINTR: offset 0x08 */ - u32 intr_enable; - - /* FRINDEX: offset 0x0C */ - u32 frame_index; /* current microframe number */ - /* CTRLDSSEGMENT: offset 0x10 */ - u32 segment; /* address bits 63:32 if needed */ - /* PERIODICLISTBASE: offset 0x14 */ - u32 frame_list; /* points to periodic list */ - /* ASYNCLISTADDR: offset 0x18 */ - u32 async_next; /* address of next async queue head */ - - u32 reserved1; - /* PORTSC: offset 0x20 */ - u32 port_status; -/* 31:23 reserved */ -#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */ -#define PORT_RESET (1<<8) /* reset port */ -#define PORT_SUSPEND (1<<7) /* suspend port */ -#define PORT_RESUME (1<<6) /* resume it */ -#define PORT_PEC (1<<3) /* port enable change */ -#define PORT_PE (1<<2) /* port enable */ -#define PORT_CSC (1<<1) /* connect status change */ -#define PORT_CONNECT (1<<0) /* device connected */ -#define PORT_RWC_BITS (PORT_CSC | PORT_PEC) - - u32 reserved2[3]; - - /* BMCSR: offset 0x30 */ - u32 bmcsr; /* Bus Moniter Control/Status Register */ -#define BMCSR_HOST_SPD_TYP (3<<9) -#define BMCSR_VBUS_OFF (1<<4) -#define BMCSR_INT_POLARITY (1<<3) - - /* BMISR: offset 0x34 */ - u32 bmisr; /* Bus Moniter Interrupt Status Register*/ -#define BMISR_OVC (1<<1) - - /* BMIER: offset 0x38 */ - u32 bmier; /* Bus Moniter Interrupt Enable Register */ -#define BMIER_OVC_EN (1<<1) -#define BMIER_VBUS_ERR_EN (1<<0) -}; - -/*-------------------------------------------------------------------------*/ - -#define QTD_NEXT(fusbh200, dma) cpu_to_hc32(fusbh200, (u32)dma) - -/* - * EHCI Specification 0.95 Section 3.5 - * QTD: describe data transfer components (buffer, direction, ...) - * See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram". - * - * These are associated only with "QH" (Queue Head) structures, - * used with control, bulk, and interrupt transfers. - */ -struct fusbh200_qtd { - /* first part defined by EHCI spec */ - __hc32 hw_next; /* see EHCI 3.5.1 */ - __hc32 hw_alt_next; /* see EHCI 3.5.2 */ - __hc32 hw_token; /* see EHCI 3.5.3 */ -#define QTD_TOGGLE (1 << 31) /* data toggle */ -#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff) -#define QTD_IOC (1 << 15) /* interrupt on complete */ -#define QTD_CERR(tok) (((tok)>>10) & 0x3) -#define QTD_PID(tok) (((tok)>>8) & 0x3) -#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */ -#define QTD_STS_HALT (1 << 6) /* halted on error */ -#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */ -#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */ -#define QTD_STS_XACT (1 << 3) /* device gave illegal response */ -#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */ -#define QTD_STS_STS (1 << 1) /* split transaction state */ -#define QTD_STS_PING (1 << 0) /* issue PING? */ - -#define ACTIVE_BIT(fusbh200) cpu_to_hc32(fusbh200, QTD_STS_ACTIVE) -#define HALT_BIT(fusbh200) cpu_to_hc32(fusbh200, QTD_STS_HALT) -#define STATUS_BIT(fusbh200) cpu_to_hc32(fusbh200, QTD_STS_STS) - - __hc32 hw_buf [5]; /* see EHCI 3.5.4 */ - __hc32 hw_buf_hi [5]; /* Appendix B */ - - /* the rest is HCD-private */ - dma_addr_t qtd_dma; /* qtd address */ - struct list_head qtd_list; /* sw qtd list */ - struct urb *urb; /* qtd's urb */ - size_t length; /* length of buffer */ -} __attribute__ ((aligned (32))); - -/* mask NakCnt+T in qh->hw_alt_next */ -#define QTD_MASK(fusbh200) cpu_to_hc32 (fusbh200, ~0x1f) - -#define IS_SHORT_READ(token) (QTD_LENGTH (token) != 0 && QTD_PID (token) == 1) - -/*-------------------------------------------------------------------------*/ - -/* type tag from {qh,itd,fstn}->hw_next */ -#define Q_NEXT_TYPE(fusbh200,dma) ((dma) & cpu_to_hc32(fusbh200, 3 << 1)) - -/* - * Now the following defines are not converted using the - * cpu_to_le32() macro anymore, since we have to support - * "dynamic" switching between be and le support, so that the driver - * can be used on one system with SoC EHCI controller using big-endian - * descriptors as well as a normal little-endian PCI EHCI controller. - */ -/* values for that type tag */ -#define Q_TYPE_ITD (0 << 1) -#define Q_TYPE_QH (1 << 1) -#define Q_TYPE_SITD (2 << 1) -#define Q_TYPE_FSTN (3 << 1) - -/* next async queue entry, or pointer to interrupt/periodic QH */ -#define QH_NEXT(fusbh200,dma) (cpu_to_hc32(fusbh200, (((u32)dma)&~0x01f)|Q_TYPE_QH)) - -/* for periodic/async schedules and qtd lists, mark end of list */ -#define FUSBH200_LIST_END(fusbh200) cpu_to_hc32(fusbh200, 1) /* "null pointer" to hw */ - -/* - * Entries in periodic shadow table are pointers to one of four kinds - * of data structure. That's dictated by the hardware; a type tag is - * encoded in the low bits of the hardware's periodic schedule. Use - * Q_NEXT_TYPE to get the tag. - * - * For entries in the async schedule, the type tag always says "qh". - */ -union fusbh200_shadow { - struct fusbh200_qh *qh; /* Q_TYPE_QH */ - struct fusbh200_itd *itd; /* Q_TYPE_ITD */ - struct fusbh200_fstn *fstn; /* Q_TYPE_FSTN */ - __hc32 *hw_next; /* (all types) */ - void *ptr; -}; - -/*-------------------------------------------------------------------------*/ - -/* - * EHCI Specification 0.95 Section 3.6 - * QH: describes control/bulk/interrupt endpoints - * See Fig 3-7 "Queue Head Structure Layout". - * - * These appear in both the async and (for interrupt) periodic schedules. - */ - -/* first part defined by EHCI spec */ -struct fusbh200_qh_hw { - __hc32 hw_next; /* see EHCI 3.6.1 */ - __hc32 hw_info1; /* see EHCI 3.6.2 */ -#define QH_CONTROL_EP (1 << 27) /* FS/LS control endpoint */ -#define QH_HEAD (1 << 15) /* Head of async reclamation list */ -#define QH_TOGGLE_CTL (1 << 14) /* Data toggle control */ -#define QH_HIGH_SPEED (2 << 12) /* Endpoint speed */ -#define QH_LOW_SPEED (1 << 12) -#define QH_FULL_SPEED (0 << 12) -#define QH_INACTIVATE (1 << 7) /* Inactivate on next transaction */ - __hc32 hw_info2; /* see EHCI 3.6.2 */ -#define QH_SMASK 0x000000ff -#define QH_CMASK 0x0000ff00 -#define QH_HUBADDR 0x007f0000 -#define QH_HUBPORT 0x3f800000 -#define QH_MULT 0xc0000000 - __hc32 hw_current; /* qtd list - see EHCI 3.6.4 */ - - /* qtd overlay (hardware parts of a struct fusbh200_qtd) */ - __hc32 hw_qtd_next; - __hc32 hw_alt_next; - __hc32 hw_token; - __hc32 hw_buf [5]; - __hc32 hw_buf_hi [5]; -} __attribute__ ((aligned(32))); - -struct fusbh200_qh { - struct fusbh200_qh_hw *hw; /* Must come first */ - /* the rest is HCD-private */ - dma_addr_t qh_dma; /* address of qh */ - union fusbh200_shadow qh_next; /* ptr to qh; or periodic */ - struct list_head qtd_list; /* sw qtd list */ - struct list_head intr_node; /* list of intr QHs */ - struct fusbh200_qtd *dummy; - struct fusbh200_qh *unlink_next; /* next on unlink list */ - - unsigned unlink_cycle; - - u8 needs_rescan; /* Dequeue during giveback */ - u8 qh_state; -#define QH_STATE_LINKED 1 /* HC sees this */ -#define QH_STATE_UNLINK 2 /* HC may still see this */ -#define QH_STATE_IDLE 3 /* HC doesn't see this */ -#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on unlink q */ -#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */ - - u8 xacterrs; /* XactErr retry counter */ -#define QH_XACTERR_MAX 32 /* XactErr retry limit */ - - /* periodic schedule info */ - u8 usecs; /* intr bandwidth */ - u8 gap_uf; /* uframes split/csplit gap */ - u8 c_usecs; /* ... split completion bw */ - u16 tt_usecs; /* tt downstream bandwidth */ - unsigned short period; /* polling interval */ - unsigned short start; /* where polling starts */ -#define NO_FRAME ((unsigned short)~0) /* pick new start */ - - struct usb_device *dev; /* access to TT */ - unsigned is_out:1; /* bulk or intr OUT */ - unsigned clearing_tt:1; /* Clear-TT-Buf in progress */ -}; - -/*-------------------------------------------------------------------------*/ - -/* description of one iso transaction (up to 3 KB data if highspeed) */ -struct fusbh200_iso_packet { - /* These will be copied to iTD when scheduling */ - u64 bufp; /* itd->hw_bufp{,_hi}[pg] |= */ - __hc32 transaction; /* itd->hw_transaction[i] |= */ - u8 cross; /* buf crosses pages */ - /* for full speed OUT splits */ - u32 buf1; -}; - -/* temporary schedule data for packets from iso urbs (both speeds) - * each packet is one logical usb transaction to the device (not TT), - * beginning at stream->next_uframe - */ -struct fusbh200_iso_sched { - struct list_head td_list; - unsigned span; - struct fusbh200_iso_packet packet [0]; -}; - -/* - * fusbh200_iso_stream - groups all (s)itds for this endpoint. - * acts like a qh would, if EHCI had them for ISO. - */ -struct fusbh200_iso_stream { - /* first field matches fusbh200_hq, but is NULL */ - struct fusbh200_qh_hw *hw; - - u8 bEndpointAddress; - u8 highspeed; - struct list_head td_list; /* queued itds */ - struct list_head free_list; /* list of unused itds */ - struct usb_device *udev; - struct usb_host_endpoint *ep; - - /* output of (re)scheduling */ - int next_uframe; - __hc32 splits; - - /* the rest is derived from the endpoint descriptor, - * trusting urb->interval == f(epdesc->bInterval) and - * including the extra info for hw_bufp[0..2] - */ - u8 usecs, c_usecs; - u16 interval; - u16 tt_usecs; - u16 maxp; - u16 raw_mask; - unsigned bandwidth; - - /* This is used to initialize iTD's hw_bufp fields */ - __hc32 buf0; - __hc32 buf1; - __hc32 buf2; - - /* this is used to initialize sITD's tt info */ - __hc32 address; -}; - -/*-------------------------------------------------------------------------*/ - -/* - * EHCI Specification 0.95 Section 3.3 - * Fig 3-4 "Isochronous Transaction Descriptor (iTD)" - * - * Schedule records for high speed iso xfers - */ -struct fusbh200_itd { - /* first part defined by EHCI spec */ - __hc32 hw_next; /* see EHCI 3.3.1 */ - __hc32 hw_transaction [8]; /* see EHCI 3.3.2 */ -#define FUSBH200_ISOC_ACTIVE (1<<31) /* activate transfer this slot */ -#define FUSBH200_ISOC_BUF_ERR (1<<30) /* Data buffer error */ -#define FUSBH200_ISOC_BABBLE (1<<29) /* babble detected */ -#define FUSBH200_ISOC_XACTERR (1<<28) /* XactErr - transaction error */ -#define FUSBH200_ITD_LENGTH(tok) (((tok)>>16) & 0x0fff) -#define FUSBH200_ITD_IOC (1 << 15) /* interrupt on complete */ - -#define ITD_ACTIVE(fusbh200) cpu_to_hc32(fusbh200, FUSBH200_ISOC_ACTIVE) - - __hc32 hw_bufp [7]; /* see EHCI 3.3.3 */ - __hc32 hw_bufp_hi [7]; /* Appendix B */ - - /* the rest is HCD-private */ - dma_addr_t itd_dma; /* for this itd */ - union fusbh200_shadow itd_next; /* ptr to periodic q entry */ - - struct urb *urb; - struct fusbh200_iso_stream *stream; /* endpoint's queue */ - struct list_head itd_list; /* list of stream's itds */ - - /* any/all hw_transactions here may be used by that urb */ - unsigned frame; /* where scheduled */ - unsigned pg; - unsigned index[8]; /* in urb->iso_frame_desc */ -} __attribute__ ((aligned (32))); - -/*-------------------------------------------------------------------------*/ - -/* - * EHCI Specification 0.96 Section 3.7 - * Periodic Frame Span Traversal Node (FSTN) - * - * Manages split interrupt transactions (using TT) that span frame boundaries - * into uframes 0/1; see 4.12.2.2. In those uframes, a "save place" FSTN - * makes the HC jump (back) to a QH to scan for fs/ls QH completions until - * it hits a "restore" FSTN; then it returns to finish other uframe 0/1 work. - */ -struct fusbh200_fstn { - __hc32 hw_next; /* any periodic q entry */ - __hc32 hw_prev; /* qh or FUSBH200_LIST_END */ - - /* the rest is HCD-private */ - dma_addr_t fstn_dma; - union fusbh200_shadow fstn_next; /* ptr to periodic q entry */ -} __attribute__ ((aligned (32))); - -/*-------------------------------------------------------------------------*/ - -/* Prepare the PORTSC wakeup flags during controller suspend/resume */ - -#define fusbh200_prepare_ports_for_controller_suspend(fusbh200, do_wakeup) \ - fusbh200_adjust_port_wakeup_flags(fusbh200, true, do_wakeup); - -#define fusbh200_prepare_ports_for_controller_resume(fusbh200) \ - fusbh200_adjust_port_wakeup_flags(fusbh200, false, false); - -/*-------------------------------------------------------------------------*/ - -/* - * Some EHCI controllers have a Transaction Translator built into the - * root hub. This is a non-standard feature. Each controller will need - * to add code to the following inline functions, and call them as - * needed (mostly in root hub code). - */ - -static inline unsigned int -fusbh200_get_speed(struct fusbh200_hcd *fusbh200, unsigned int portsc) -{ - return (readl(&fusbh200->regs->bmcsr) - & BMCSR_HOST_SPD_TYP) >> 9; -} - -/* Returns the speed of a device attached to a port on the root hub. */ -static inline unsigned int -fusbh200_port_speed(struct fusbh200_hcd *fusbh200, unsigned int portsc) -{ - switch (fusbh200_get_speed(fusbh200, portsc)) { - case 0: - return 0; - case 1: - return USB_PORT_STAT_LOW_SPEED; - case 2: - default: - return USB_PORT_STAT_HIGH_SPEED; - } -} - -/*-------------------------------------------------------------------------*/ - -#define fusbh200_has_fsl_portno_bug(e) (0) - -/* - * While most USB host controllers implement their registers in - * little-endian format, a minority (celleb companion chip) implement - * them in big endian format. - * - * This attempts to support either format at compile time without a - * runtime penalty, or both formats with the additional overhead - * of checking a flag bit. - * - */ - -#define fusbh200_big_endian_mmio(e) 0 -#define fusbh200_big_endian_capbase(e) 0 - -static inline unsigned int fusbh200_readl(const struct fusbh200_hcd *fusbh200, - __u32 __iomem * regs) -{ - return readl(regs); -} - -static inline void fusbh200_writel(const struct fusbh200_hcd *fusbh200, - const unsigned int val, __u32 __iomem *regs) -{ - writel(val, regs); -} - -/* cpu to fusbh200 */ -static inline __hc32 cpu_to_hc32 (const struct fusbh200_hcd *fusbh200, const u32 x) -{ - return cpu_to_le32(x); -} - -/* fusbh200 to cpu */ -static inline u32 hc32_to_cpu (const struct fusbh200_hcd *fusbh200, const __hc32 x) -{ - return le32_to_cpu(x); -} - -static inline u32 hc32_to_cpup (const struct fusbh200_hcd *fusbh200, const __hc32 *x) -{ - return le32_to_cpup(x); -} - -/*-------------------------------------------------------------------------*/ - -static inline unsigned fusbh200_read_frame_index(struct fusbh200_hcd *fusbh200) -{ - return fusbh200_readl(fusbh200, &fusbh200->regs->frame_index); -} - -#define fusbh200_itdlen(urb, desc, t) ({ \ - usb_pipein((urb)->pipe) ? \ - (desc)->length - FUSBH200_ITD_LENGTH(t) : \ - FUSBH200_ITD_LENGTH(t); \ -}) -/*-------------------------------------------------------------------------*/ - -#endif /* __LINUX_FUSBH200_H */ diff --git a/drivers/usb/host/ohci-nxp.c b/drivers/usb/host/ohci-nxp.c index d9f0481..cfa9427 100644 --- a/drivers/usb/host/ohci-nxp.c +++ b/drivers/usb/host/ohci-nxp.c @@ -203,7 +203,7 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev) goto fail_disable; } - ret = clk_enable(usb_pll_clk); + ret = clk_prepare_enable(usb_pll_clk); if (ret < 0) { dev_err(&pdev->dev, "failed to start USB PLL\n"); goto fail_disable; @@ -223,7 +223,7 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev) goto fail_rate; } - ret = clk_enable(usb_dev_clk); + ret = clk_prepare_enable(usb_dev_clk); if (ret < 0) { dev_err(&pdev->dev, "failed to start USB DEV Clock\n"); goto fail_rate; @@ -239,7 +239,7 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev) __raw_writel(__raw_readl(USB_CTRL) | USB_HOST_NEED_CLK_EN, USB_CTRL); - ret = clk_enable(usb_otg_clk); + ret = clk_prepare_enable(usb_otg_clk); if (ret < 0) { dev_err(&pdev->dev, "failed to start USB DEV Clock\n"); goto fail_otg; @@ -283,11 +283,11 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev) fail_resource: usb_put_hcd(hcd); fail_hcd: - clk_disable(usb_otg_clk); + clk_disable_unprepare(usb_otg_clk); fail_otg: - clk_disable(usb_dev_clk); + clk_disable_unprepare(usb_dev_clk); fail_rate: - clk_disable(usb_pll_clk); + clk_disable_unprepare(usb_pll_clk); fail_disable: isp1301_i2c_client = NULL; return ret; @@ -300,9 +300,9 @@ static int ohci_hcd_nxp_remove(struct platform_device *pdev) usb_remove_hcd(hcd); ohci_nxp_stop_hc(); usb_put_hcd(hcd); - clk_disable(usb_pll_clk); - clk_disable(usb_dev_clk); - i2c_unregister_device(isp1301_i2c_client); + clk_disable_unprepare(usb_otg_clk); + clk_disable_unprepare(usb_dev_clk); + clk_disable_unprepare(usb_pll_clk); isp1301_i2c_client = NULL; return 0; diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c index 707437c..56478ed 100644 --- a/drivers/usb/host/ohci-spear.c +++ b/drivers/usb/host/ohci-spear.c @@ -161,6 +161,7 @@ static const struct of_device_id spear_ohci_id_table[] = { { .compatible = "st,spear600-ohci", }, { }, }; +MODULE_DEVICE_TABLE(of, spear_ohci_id_table); /* Driver definition to register with the platform bus */ static struct platform_driver spear_ohci_hcd_driver = { diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c index a67bd50..0a94895 100644 --- a/drivers/usb/host/u132-hcd.c +++ b/drivers/usb/host/u132-hcd.c @@ -2245,8 +2245,7 @@ static int u132_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, struct u132 *u132 = hcd_to_u132(hcd); if (irqs_disabled()) { if (__GFP_WAIT & mem_flags) { - printk(KERN_ERR "invalid context for function that migh" - "t sleep\n"); + printk(KERN_ERR "invalid context for function that might sleep\n"); return -EINVAL; } } diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c index 3a3e3ee..32a6f3d 100644 --- a/drivers/usb/host/uhci-platform.c +++ b/drivers/usb/host/uhci-platform.c @@ -140,6 +140,7 @@ static const struct of_device_id platform_uhci_ids[] = { { .compatible = "platform-uhci", }, {} }; +MODULE_DEVICE_TABLE(of, platform_uhci_ids); static struct platform_driver uhci_platform_driver = { .probe = uhci_hcd_platform_probe, diff --git a/drivers/usb/host/whci/init.c b/drivers/usb/host/whci/init.c index d3e13b6..e363723 100644 --- a/drivers/usb/host/whci/init.c +++ b/drivers/usb/host/whci/init.c @@ -175,8 +175,7 @@ void whc_clean_up(struct whc *whc) pzl_clean_up(whc); asl_clean_up(whc); - if (whc->qset_pool) - dma_pool_destroy(whc->qset_pool); + dma_pool_destroy(whc->qset_pool); len = resource_size(&whc->umc->resource); if (whc->base) diff --git a/drivers/usb/host/xhci-dbg.c b/drivers/usb/host/xhci-dbg.c index 2d16fae..74c42f7 100644 --- a/drivers/usb/host/xhci-dbg.c +++ b/drivers/usb/host/xhci-dbg.c @@ -58,16 +58,17 @@ void xhci_dbg_regs(struct xhci_hcd *xhci) static void xhci_print_cap_regs(struct xhci_hcd *xhci) { u32 temp; + u32 hci_version; xhci_dbg(xhci, "xHCI capability registers at %p:\n", xhci->cap_regs); temp = readl(&xhci->cap_regs->hc_capbase); + hci_version = HC_VERSION(temp); xhci_dbg(xhci, "CAPLENGTH AND HCIVERSION 0x%x:\n", (unsigned int) temp); xhci_dbg(xhci, "CAPLENGTH: 0x%x\n", (unsigned int) HC_LENGTH(temp)); - xhci_dbg(xhci, "HCIVERSION: 0x%x\n", - (unsigned int) HC_VERSION(temp)); + xhci_dbg(xhci, "HCIVERSION: 0x%x\n", hci_version); temp = readl(&xhci->cap_regs->hcs_params1); xhci_dbg(xhci, "HCSPARAMS 1: 0x%x\n", @@ -108,6 +109,18 @@ static void xhci_print_cap_regs(struct xhci_hcd *xhci) temp = readl(&xhci->cap_regs->run_regs_off); xhci_dbg(xhci, "RTSOFF 0x%x:\n", temp & RTSOFF_MASK); + + /* xhci 1.1 controllers have the HCCPARAMS2 register */ + if (hci_version > 100) { + temp = readl(&xhci->cap_regs->hcc_params2); + xhci_dbg(xhci, "HCC PARAMS2 0x%x:\n", (unsigned int) temp); + xhci_dbg(xhci, " HC %s Force save context capability", + HCC2_FSC(temp) ? "supports" : "doesn't support"); + xhci_dbg(xhci, " HC %s Large ESIT Payload Capability", + HCC2_LEC(temp) ? "supports" : "doesn't support"); + xhci_dbg(xhci, " HC %s Extended TBC capability", + HCC2_ETC(temp) ? "supports" : "doesn't support"); + } } static void xhci_print_command_reg(struct xhci_hcd *xhci) diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 78241b5..5d2d7e9 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -31,13 +31,15 @@ #define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | \ PORT_RC | PORT_PLC | PORT_PE) -/* USB 3.0 BOS descriptor and a capability descriptor, combined */ +/* USB 3 BOS descriptor and a capability descriptors, combined. + * Fields will be adjusted and added later in xhci_create_usb3_bos_desc() + */ static u8 usb_bos_descriptor [] = { USB_DT_BOS_SIZE, /* __u8 bLength, 5 bytes */ USB_DT_BOS, /* __u8 bDescriptorType */ 0x0F, 0x00, /* __le16 wTotalLength, 15 bytes */ 0x1, /* __u8 bNumDeviceCaps */ - /* First device capability */ + /* First device capability, SuperSpeed */ USB_DT_USB_SS_CAP_SIZE, /* __u8 bLength, 10 bytes */ USB_DT_DEVICE_CAPABILITY, /* Device Capability */ USB_SS_CAP_TYPE, /* bDevCapabilityType, SUPERSPEED_USB */ @@ -46,9 +48,108 @@ static u8 usb_bos_descriptor [] = { 0x03, /* bFunctionalitySupport, USB 3.0 speed only */ 0x00, /* bU1DevExitLat, set later. */ - 0x00, 0x00 /* __le16 bU2DevExitLat, set later. */ + 0x00, 0x00, /* __le16 bU2DevExitLat, set later. */ + /* Second device capability, SuperSpeedPlus */ + 0x0c, /* bLength 12, will be adjusted later */ + USB_DT_DEVICE_CAPABILITY, /* Device Capability */ + USB_SSP_CAP_TYPE, /* bDevCapabilityType SUPERSPEED_PLUS */ + 0x00, /* bReserved 0 */ + 0x00, 0x00, 0x00, 0x00, /* bmAttributes, get from xhci psic */ + 0x00, 0x00, /* wFunctionalitySupport */ + 0x00, 0x00, /* wReserved 0 */ + /* Sublink Speed Attributes are added in xhci_create_usb3_bos_desc() */ }; +static int xhci_create_usb3_bos_desc(struct xhci_hcd *xhci, char *buf, + u16 wLength) +{ + int i, ssa_count; + u32 temp; + u16 desc_size, ssp_cap_size, ssa_size = 0; + bool usb3_1 = false; + + desc_size = USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE; + ssp_cap_size = sizeof(usb_bos_descriptor) - desc_size; + + /* does xhci support USB 3.1 Enhanced SuperSpeed */ + if (xhci->usb3_rhub.min_rev >= 0x01 && xhci->usb3_rhub.psi_uid_count) { + /* two SSA entries for each unique PSI ID, one RX and one TX */ + ssa_count = xhci->usb3_rhub.psi_uid_count * 2; + ssa_size = ssa_count * sizeof(u32); + desc_size += ssp_cap_size; + usb3_1 = true; + } + memcpy(buf, &usb_bos_descriptor, min(desc_size, wLength)); + + if (usb3_1) { + /* modify bos descriptor bNumDeviceCaps and wTotalLength */ + buf[4] += 1; + put_unaligned_le16(desc_size + ssa_size, &buf[2]); + } + + if (wLength < USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE) + return wLength; + + /* Indicate whether the host has LTM support. */ + temp = readl(&xhci->cap_regs->hcc_params); + if (HCC_LTC(temp)) + buf[8] |= USB_LTM_SUPPORT; + + /* Set the U1 and U2 exit latencies. */ + if ((xhci->quirks & XHCI_LPM_SUPPORT)) { + temp = readl(&xhci->cap_regs->hcs_params3); + buf[12] = HCS_U1_LATENCY(temp); + put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]); + } + + if (usb3_1) { + u32 ssp_cap_base, bm_attrib, psi; + int offset; + + ssp_cap_base = USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE; + + if (wLength < desc_size) + return wLength; + buf[ssp_cap_base] = ssp_cap_size + ssa_size; + + /* attribute count SSAC bits 4:0 and ID count SSIC bits 8:5 */ + bm_attrib = (ssa_count - 1) & 0x1f; + bm_attrib |= (xhci->usb3_rhub.psi_uid_count - 1) << 5; + put_unaligned_le32(bm_attrib, &buf[ssp_cap_base + 4]); + + if (wLength < desc_size + ssa_size) + return wLength; + /* + * Create the Sublink Speed Attributes (SSA) array. + * The xhci PSI field and USB 3.1 SSA fields are very similar, + * but link type bits 7:6 differ for values 01b and 10b. + * xhci has also only one PSI entry for a symmetric link when + * USB 3.1 requires two SSA entries (RX and TX) for every link + */ + offset = desc_size; + for (i = 0; i < xhci->usb3_rhub.psi_count; i++) { + psi = xhci->usb3_rhub.psi[i]; + psi &= ~USB_SSP_SUBLINK_SPEED_RSVD; + if ((psi & PLT_MASK) == PLT_SYM) { + /* Symmetric, create SSA RX and TX from one PSI entry */ + put_unaligned_le32(psi, &buf[offset]); + psi |= 1 << 7; /* turn entry to TX */ + offset += 4; + if (offset >= desc_size + ssa_size) + return desc_size + ssa_size; + } else if ((psi & PLT_MASK) == PLT_ASYM_RX) { + /* Asymetric RX, flip bits 7:6 for SSA */ + psi ^= PLT_MASK; + } + put_unaligned_le32(psi, &buf[offset]); + offset += 4; + if (offset >= desc_size + ssa_size) + return desc_size + ssa_size; + } + } + /* ssa_size is 0 for other than usb 3.1 hosts */ + return desc_size + ssa_size; +} static void xhci_common_hub_descriptor(struct xhci_hcd *xhci, struct usb_hub_descriptor *desc, int ports) @@ -161,7 +262,7 @@ static void xhci_hub_descriptor(struct usb_hcd *hcd, struct xhci_hcd *xhci, struct usb_hub_descriptor *desc) { - if (hcd->speed == HCD_USB3) + if (hcd->speed >= HCD_USB3) xhci_usb3_hub_descriptor(hcd, xhci, desc); else xhci_usb2_hub_descriptor(hcd, xhci, desc); @@ -250,7 +351,7 @@ int xhci_find_slot_id_by_port(struct usb_hcd *hcd, struct xhci_hcd *xhci, if (!xhci->devs[i]) continue; speed = xhci->devs[i]->udev->speed; - if (((speed == USB_SPEED_SUPER) == (hcd->speed == HCD_USB3)) + if (((speed >= USB_SPEED_SUPER) == (hcd->speed >= HCD_USB3)) && xhci->devs[i]->fake_port == port) { slot_id = i; break; @@ -339,7 +440,7 @@ static void xhci_disable_port(struct usb_hcd *hcd, struct xhci_hcd *xhci, u16 wIndex, __le32 __iomem *addr, u32 port_status) { /* Don't allow the USB core to disable SuperSpeed ports. */ - if (hcd->speed == HCD_USB3) { + if (hcd->speed >= HCD_USB3) { xhci_dbg(xhci, "Ignoring request to disable " "SuperSpeed port.\n"); return; @@ -407,7 +508,7 @@ static int xhci_get_ports(struct usb_hcd *hcd, __le32 __iomem ***port_array) int max_ports; struct xhci_hcd *xhci = hcd_to_xhci(hcd); - if (hcd->speed == HCD_USB3) { + if (hcd->speed >= HCD_USB3) { max_ports = xhci->num_usb3_ports; *port_array = xhci->usb3_ports; } else { @@ -558,6 +659,22 @@ static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, } } +static u32 xhci_get_ext_port_status(u32 raw_port_status, u32 port_li) +{ + u32 ext_stat = 0; + int speed_id; + + /* only support rx and tx lane counts of 1 in usb3.1 spec */ + speed_id = DEV_PORT_SPEED(raw_port_status); + ext_stat |= speed_id; /* bits 3:0, RX speed id */ + ext_stat |= speed_id << 4; /* bits 7:4, TX speed id */ + + ext_stat |= PORT_RX_LANES(port_li) << 8; /* bits 11:8 Rx lane count */ + ext_stat |= PORT_TX_LANES(port_li) << 12; /* bits 15:12 Tx lane count */ + + return ext_stat; +} + /* * Converts a raw xHCI port status into the format that external USB 2.0 or USB * 3.0 hubs use. @@ -590,7 +707,7 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, if ((raw_port_status & PORT_RC)) status |= USB_PORT_STAT_C_RESET << 16; /* USB3.0 only */ - if (hcd->speed == HCD_USB3) { + if (hcd->speed >= HCD_USB3) { /* Port link change with port in resume state should not be * reported to usbcore, as this is an internal state to be * handled by xhci driver. Reporting PLC to usbcore may @@ -606,13 +723,13 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, status |= USB_PORT_STAT_C_CONFIG_ERROR << 16; } - if (hcd->speed != HCD_USB3) { + if (hcd->speed < HCD_USB3) { if ((raw_port_status & PORT_PLS_MASK) == XDEV_U3 && (raw_port_status & PORT_POWER)) status |= USB_PORT_STAT_SUSPEND; } if ((raw_port_status & PORT_PLS_MASK) == XDEV_RESUME && - !DEV_SUPERSPEED(raw_port_status)) { + !DEV_SUPERSPEED_ANY(raw_port_status)) { if ((raw_port_status & PORT_RESET) || !(raw_port_status & PORT_PE)) return 0xffffffff; @@ -669,7 +786,7 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, && (raw_port_status & PORT_POWER) && (bus_state->suspended_ports & (1 << wIndex))) { bus_state->suspended_ports &= ~(1 << wIndex); - if (hcd->speed != HCD_USB3) + if (hcd->speed < HCD_USB3) bus_state->port_c_suspend |= 1 << wIndex; } if (raw_port_status & PORT_CONNECT) { @@ -683,13 +800,13 @@ static u32 xhci_get_port_status(struct usb_hcd *hcd, if (raw_port_status & PORT_RESET) status |= USB_PORT_STAT_RESET; if (raw_port_status & PORT_POWER) { - if (hcd->speed == HCD_USB3) + if (hcd->speed >= HCD_USB3) status |= USB_SS_PORT_STAT_POWER; else status |= USB_PORT_STAT_POWER; } /* Update Port Link State */ - if (hcd->speed == HCD_USB3) { + if (hcd->speed >= HCD_USB3) { xhci_hub_report_usb3_link_state(xhci, &status, raw_port_status); /* * Verify if all USB3 Ports Have entered U0 already. @@ -734,7 +851,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, * descriptor for the USB 3.0 roothub. If not, we stall the * endpoint, like external hubs do. */ - if (hcd->speed == HCD_USB3 && + if (hcd->speed >= HCD_USB3 && (wLength < USB_DT_SS_HUB_SIZE || wValue != (USB_DT_SS_HUB << 8))) { xhci_dbg(xhci, "Wrong hub descriptor type for " @@ -748,25 +865,12 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, if ((wValue & 0xff00) != (USB_DT_BOS << 8)) goto error; - if (hcd->speed != HCD_USB3) + if (hcd->speed < HCD_USB3) goto error; - /* Set the U1 and U2 exit latencies. */ - memcpy(buf, &usb_bos_descriptor, - USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE); - if ((xhci->quirks & XHCI_LPM_SUPPORT)) { - temp = readl(&xhci->cap_regs->hcs_params3); - buf[12] = HCS_U1_LATENCY(temp); - put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]); - } - - /* Indicate whether the host has LTM support. */ - temp = readl(&xhci->cap_regs->hcc_params); - if (HCC_LTC(temp)) - buf[8] |= USB_LTM_SUPPORT; - + retval = xhci_create_usb3_bos_desc(xhci, buf, wLength); spin_unlock_irqrestore(&xhci->lock, flags); - return USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE; + return retval; case GetPortStatus: if (!wIndex || wIndex > max_ports) goto error; @@ -786,6 +890,19 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, xhci_dbg(xhci, "Get port status returned 0x%x\n", status); put_unaligned(cpu_to_le32(status), (__le32 *) buf); + /* if USB 3.1 extended port status return additional 4 bytes */ + if (wValue == 0x02) { + u32 port_li; + + if (hcd->speed < HCD_USB31 || wLength != 8) { + xhci_err(xhci, "get ext port status invalid parameter\n"); + retval = -EINVAL; + break; + } + port_li = readl(port_array[wIndex] + PORTLI); + status = xhci_get_ext_port_status(temp, port_li); + put_unaligned_le32(cpu_to_le32(status), &buf[4]); + } break; case SetPortFeature: if (wValue == USB_PORT_FEAT_LINK_STATE) @@ -952,7 +1069,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, temp = readl(port_array[wIndex]); break; case USB_PORT_FEAT_U1_TIMEOUT: - if (hcd->speed != HCD_USB3) + if (hcd->speed < HCD_USB3) goto error; temp = readl(port_array[wIndex] + PORTPMSC); temp &= ~PORT_U1_TIMEOUT_MASK; @@ -960,7 +1077,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, writel(temp, port_array[wIndex] + PORTPMSC); break; case USB_PORT_FEAT_U2_TIMEOUT: - if (hcd->speed != HCD_USB3) + if (hcd->speed < HCD_USB3) goto error; temp = readl(port_array[wIndex] + PORTPMSC); temp &= ~PORT_U2_TIMEOUT_MASK; @@ -1223,14 +1340,14 @@ int xhci_bus_resume(struct usb_hcd *hcd) u32 temp; temp = readl(port_array[port_index]); - if (DEV_SUPERSPEED(temp)) + if (DEV_SUPERSPEED_ANY(temp)) temp &= ~(PORT_RWC_BITS | PORT_CEC | PORT_WAKE_BITS); else temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); if (test_bit(port_index, &bus_state->bus_suspended) && (temp & PORT_PLS_MASK)) { set_bit(port_index, &port_was_suspended); - if (!DEV_SUPERSPEED(temp)) { + if (!DEV_SUPERSPEED_ANY(temp)) { xhci_set_link_state(xhci, port_array, port_index, XDEV_RESUME); need_usb2_u3_exit = true; diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c index 41f841f..c48cbe7 100644 --- a/drivers/usb/host/xhci-mem.c +++ b/drivers/usb/host/xhci-mem.c @@ -1828,24 +1828,20 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci) for (i = 1; i < MAX_HC_SLOTS; ++i) xhci_free_virt_device(xhci, i); - if (xhci->segment_pool) - dma_pool_destroy(xhci->segment_pool); + dma_pool_destroy(xhci->segment_pool); xhci->segment_pool = NULL; xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed segment pool"); - if (xhci->device_pool) - dma_pool_destroy(xhci->device_pool); + dma_pool_destroy(xhci->device_pool); xhci->device_pool = NULL; xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed device context pool"); - if (xhci->small_streams_pool) - dma_pool_destroy(xhci->small_streams_pool); + dma_pool_destroy(xhci->small_streams_pool); xhci->small_streams_pool = NULL; xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed small stream array pool"); - if (xhci->medium_streams_pool) - dma_pool_destroy(xhci->medium_streams_pool); + dma_pool_destroy(xhci->medium_streams_pool); xhci->medium_streams_pool = NULL; xhci_dbg_trace(xhci, trace_xhci_dbg_init, "Freed medium stream array pool"); @@ -2072,14 +2068,23 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, { u32 temp, port_offset, port_count; int i; + struct xhci_hub *rhub; - if (major_revision > 0x03) { + temp = readl(addr); + + if (XHCI_EXT_PORT_MAJOR(temp) == 0x03) { + rhub = &xhci->usb3_rhub; + } else if (XHCI_EXT_PORT_MAJOR(temp) <= 0x02) { + rhub = &xhci->usb2_rhub; + } else { xhci_warn(xhci, "Ignoring unknown port speed, " "Ext Cap %p, revision = 0x%x\n", addr, major_revision); /* Ignoring port protocol we can't understand. FIXME */ return; } + rhub->maj_rev = XHCI_EXT_PORT_MAJOR(temp); + rhub->min_rev = XHCI_EXT_PORT_MINOR(temp); /* Port offset and count in the third dword, see section 7.2 */ temp = readl(addr + 2); @@ -2094,6 +2099,33 @@ static void xhci_add_in_port(struct xhci_hcd *xhci, unsigned int num_ports, /* WTF? "Valid values are ‘1’ to MaxPorts" */ return; + rhub->psi_count = XHCI_EXT_PORT_PSIC(temp); + if (rhub->psi_count) { + rhub->psi = kcalloc(rhub->psi_count, sizeof(*rhub->psi), + GFP_KERNEL); + if (!rhub->psi) + rhub->psi_count = 0; + + rhub->psi_uid_count++; + for (i = 0; i < rhub->psi_count; i++) { + rhub->psi[i] = readl(addr + 4 + i); + + /* count unique ID values, two consecutive entries can + * have the same ID if link is assymetric + */ + if (i && (XHCI_EXT_PORT_PSIV(rhub->psi[i]) != + XHCI_EXT_PORT_PSIV(rhub->psi[i - 1]))) + rhub->psi_uid_count++; + + xhci_dbg(xhci, "PSIV:%d PSIE:%d PLT:%d PFD:%d LP:%d PSIM:%d\n", + XHCI_EXT_PORT_PSIV(rhub->psi[i]), + XHCI_EXT_PORT_PSIE(rhub->psi[i]), + XHCI_EXT_PORT_PLT(rhub->psi[i]), + XHCI_EXT_PORT_PFD(rhub->psi[i]), + XHCI_EXT_PORT_LP(rhub->psi[i]), + XHCI_EXT_PORT_PSIM(rhub->psi[i])); + } + } /* cache usb2 port capabilities */ if (major_revision < 0x03 && xhci->num_ext_caps < max_caps) xhci->ext_caps[xhci->num_ext_caps++] = temp; diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index c47d3e4..17f6897 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -201,15 +201,17 @@ static int xhci_pci_setup(struct usb_hcd *hcd) struct pci_dev *pdev = to_pci_dev(hcd->self.controller); int retval; + xhci = hcd_to_xhci(hcd); + if (!xhci->sbrn) + pci_read_config_byte(pdev, XHCI_SBRN_OFFSET, &xhci->sbrn); + retval = xhci_gen_setup(hcd, xhci_pci_quirks); if (retval) return retval; - xhci = hcd_to_xhci(hcd); if (!usb_hcd_is_primary_hcd(hcd)) return 0; - pci_read_config_byte(pdev, XHCI_SBRN_OFFSET, &xhci->sbrn); xhci_dbg(xhci, "Got SBRN %u\n", (unsigned int) xhci->sbrn); /* Find any debug ports */ diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 890ad9d..05647e6 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -19,6 +19,7 @@ #include <linux/usb/phy.h> #include <linux/slab.h> #include <linux/usb/xhci_pdriver.h> +#include <linux/acpi.h> #include "xhci.h" #include "xhci-mvebu.h" @@ -93,14 +94,20 @@ static int xhci_plat_probe(struct platform_device *pdev) if (irq < 0) return -ENODEV; - /* Initialize dma_mask and coherent_dma_mask to 32-bits */ - ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); - if (ret) - return ret; - if (!pdev->dev.dma_mask) - pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; + /* Try to set 64-bit DMA first */ + if (WARN_ON(!pdev->dev.dma_mask)) + /* Platform did not initialize dma_mask */ + ret = dma_coerce_mask_and_coherent(&pdev->dev, + DMA_BIT_MASK(64)); else - dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); + + /* If seting 64-bit DMA mask fails, fall back to 32-bit DMA mask */ + if (ret) { + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) + return ret; + } hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) @@ -262,6 +269,13 @@ static const struct of_device_id usb_xhci_of_match[] = { MODULE_DEVICE_TABLE(of, usb_xhci_of_match); #endif +static const struct acpi_device_id usb_xhci_acpi_match[] = { + /* XHCI-compliant USB Controller */ + { "PNP0D10", }, + { } +}; +MODULE_DEVICE_TABLE(acpi, usb_xhci_acpi_match); + static struct platform_driver usb_xhci_driver = { .probe = xhci_plat_probe, .remove = xhci_plat_remove, @@ -269,6 +283,7 @@ static struct platform_driver usb_xhci_driver = { .name = "xhci-hcd", .pm = DEV_PM_OPS, .of_match_table = of_match_ptr(usb_xhci_of_match), + .acpi_match_table = ACPI_PTR(usb_xhci_acpi_match), }, }; MODULE_ALIAS("platform:xhci-hcd"); diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 97ffe39..fa83625 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1453,7 +1453,7 @@ static unsigned int find_faked_portnum_from_hw_portnum(struct usb_hcd *hcd, * 1.1 ports are under the USB 2.0 hub. If the port speed * matches the device speed, it's a similar speed port. */ - if ((port_speed == 0x03) == (hcd->speed == HCD_USB3)) + if ((port_speed == 0x03) == (hcd->speed >= HCD_USB3)) num_similar_speed_ports++; } return num_similar_speed_ports; @@ -1515,7 +1515,7 @@ static void handle_port_status(struct xhci_hcd *xhci, /* Find the right roothub. */ hcd = xhci_to_hcd(xhci); - if ((major_revision == 0x03) != (hcd->speed == HCD_USB3)) + if ((major_revision == 0x03) != (hcd->speed >= HCD_USB3)) hcd = xhci->shared_hcd; if (major_revision == 0) { @@ -1541,7 +1541,7 @@ static void handle_port_status(struct xhci_hcd *xhci, * correct bus_state structure. */ bus_state = &xhci->bus_state[hcd_index(hcd)]; - if (hcd->speed == HCD_USB3) + if (hcd->speed >= HCD_USB3) port_array = xhci->usb3_ports; else port_array = xhci->usb2_ports; @@ -1555,7 +1555,7 @@ static void handle_port_status(struct xhci_hcd *xhci, usb_hcd_resume_root_hub(hcd); } - if (hcd->speed == HCD_USB3 && (temp & PORT_PLS_MASK) == XDEV_INACTIVE) + if (hcd->speed >= HCD_USB3 && (temp & PORT_PLS_MASK) == XDEV_INACTIVE) bus_state->port_remote_wakeup &= ~(1 << faked_port_index); if ((temp & PORT_PLC) && (temp & PORT_PLS_MASK) == XDEV_RESUME) { @@ -1567,7 +1567,7 @@ static void handle_port_status(struct xhci_hcd *xhci, goto cleanup; } - if (DEV_SUPERSPEED(temp)) { + if (DEV_SUPERSPEED_ANY(temp)) { xhci_dbg(xhci, "remote wake SS port %d\n", port_id); /* Set a flag to say the port signaled remote wakeup, * so we can tell the difference between the end of @@ -1595,7 +1595,7 @@ static void handle_port_status(struct xhci_hcd *xhci, } if ((temp & PORT_PLC) && (temp & PORT_PLS_MASK) == XDEV_U0 && - DEV_SUPERSPEED(temp)) { + DEV_SUPERSPEED_ANY(temp)) { xhci_dbg(xhci, "resume SS port %d finished\n", port_id); /* We've just brought the device into U0 through either the * Resume state after a device remote wakeup, or through the @@ -1625,7 +1625,7 @@ static void handle_port_status(struct xhci_hcd *xhci, * RExit to a disconnect state). If so, let the the driver know it's * out of the RExit state. */ - if (!DEV_SUPERSPEED(temp) && + if (!DEV_SUPERSPEED_ANY(temp) && test_and_clear_bit(faked_port_index, &bus_state->rexit_ports)) { complete(&bus_state->rexit_done[faked_port_index]); @@ -1633,7 +1633,7 @@ static void handle_port_status(struct xhci_hcd *xhci, goto cleanup; } - if (hcd->speed != HCD_USB3) + if (hcd->speed < HCD_USB3) xhci_test_and_clear_bit(xhci, port_array, faked_port_index, PORT_PLC); @@ -3049,21 +3049,6 @@ int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags, } /* - * The TD size is the number of bytes remaining in the TD (including this TRB), - * right shifted by 10. - * It must fit in bits 21:17, so it can't be bigger than 31. - */ -static u32 xhci_td_remainder(unsigned int remainder) -{ - u32 max = (1 << (21 - 17 + 1)) - 1; - - if ((remainder >> 10) >= max) - return max << 17; - else - return (remainder >> 10) << 17; -} - -/* * For xHCI 1.0 host controllers, TD size is the number of max packet sized * packets remaining in the TD (*not* including this TRB). * @@ -3075,30 +3060,36 @@ static u32 xhci_td_remainder(unsigned int remainder) * * TD size = total_packet_count - packets_transferred * - * It must fit in bits 21:17, so it can't be bigger than 31. + * For xHCI 0.96 and older, TD size field should be the remaining bytes + * including this TRB, right shifted by 10 + * + * For all hosts it must fit in bits 21:17, so it can't be bigger than 31. + * This is taken care of in the TRB_TD_SIZE() macro + * * The last TRB in a TD must have the TD size set to zero. */ -static u32 xhci_v1_0_td_remainder(int running_total, int trb_buff_len, - unsigned int total_packet_count, struct urb *urb, - unsigned int num_trbs_left) +static u32 xhci_td_remainder(struct xhci_hcd *xhci, int transferred, + int trb_buff_len, unsigned int td_total_len, + struct urb *urb, unsigned int num_trbs_left) { - int packets_transferred; + u32 maxp, total_packet_count; + + if (xhci->hci_version < 0x100) + return ((td_total_len - transferred) >> 10); + + maxp = GET_MAX_PACKET(usb_endpoint_maxp(&urb->ep->desc)); + total_packet_count = DIV_ROUND_UP(td_total_len, maxp); /* One TRB with a zero-length data packet. */ - if (num_trbs_left == 0 || (running_total == 0 && trb_buff_len == 0)) + if (num_trbs_left == 0 || (transferred == 0 && trb_buff_len == 0) || + trb_buff_len == td_total_len) return 0; - /* All the TRB queueing functions don't count the current TRB in - * running_total. - */ - packets_transferred = (running_total + trb_buff_len) / - GET_MAX_PACKET(usb_endpoint_maxp(&urb->ep->desc)); - - if ((total_packet_count - packets_transferred) > 31) - return 31 << 17; - return (total_packet_count - packets_transferred) << 17; + /* Queueing functions don't count the current TRB into transferred */ + return (total_packet_count - ((transferred + trb_buff_len) / maxp)); } + static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct urb *urb, int slot_id, unsigned int ep_index) { @@ -3220,17 +3211,12 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, } /* Set the TRB length, TD size, and interrupter fields. */ - if (xhci->hci_version < 0x100) { - remainder = xhci_td_remainder( - urb->transfer_buffer_length - - running_total); - } else { - remainder = xhci_v1_0_td_remainder(running_total, - trb_buff_len, total_packet_count, urb, - num_trbs - 1); - } + remainder = xhci_td_remainder(xhci, running_total, trb_buff_len, + urb->transfer_buffer_length, + urb, num_trbs - 1); + length_field = TRB_LEN(trb_buff_len) | - remainder | + TRB_TD_SIZE(remainder) | TRB_INTR_TARGET(0); if (num_trbs > 1) @@ -3393,17 +3379,12 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, field |= TRB_ISP; /* Set the TRB length, TD size, and interrupter fields. */ - if (xhci->hci_version < 0x100) { - remainder = xhci_td_remainder( - urb->transfer_buffer_length - - running_total); - } else { - remainder = xhci_v1_0_td_remainder(running_total, - trb_buff_len, total_packet_count, urb, - num_trbs - 1); - } + remainder = xhci_td_remainder(xhci, running_total, trb_buff_len, + urb->transfer_buffer_length, + urb, num_trbs - 1); + length_field = TRB_LEN(trb_buff_len) | - remainder | + TRB_TD_SIZE(remainder) | TRB_INTR_TARGET(0); if (num_trbs > 1) @@ -3441,7 +3422,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, struct usb_ctrlrequest *setup; struct xhci_generic_trb *start_trb; int start_cycle; - u32 field, length_field; + u32 field, length_field, remainder; struct urb_priv *urb_priv; struct xhci_td *td; @@ -3514,9 +3495,15 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, else field = TRB_TYPE(TRB_DATA); + remainder = xhci_td_remainder(xhci, 0, + urb->transfer_buffer_length, + urb->transfer_buffer_length, + urb, 1); + length_field = TRB_LEN(urb->transfer_buffer_length) | - xhci_td_remainder(urb->transfer_buffer_length) | + TRB_TD_SIZE(remainder) | TRB_INTR_TARGET(0); + if (urb->transfer_buffer_length > 0) { if (setup->bRequestType & USB_DIR_IN) field |= TRB_DIR_IN; @@ -3845,17 +3832,12 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, trb_buff_len = td_remain_len; /* Set the TRB length, TD size, & interrupter fields. */ - if (xhci->hci_version < 0x100) { - remainder = xhci_td_remainder( - td_len - running_total); - } else { - remainder = xhci_v1_0_td_remainder( - running_total, trb_buff_len, - total_packet_count, urb, - (trbs_per_td - j - 1)); - } + remainder = xhci_td_remainder(xhci, running_total, + trb_buff_len, td_len, + urb, trbs_per_td - j - 1); + length_field = TRB_LEN(trb_buff_len) | - remainder | + TRB_TD_SIZE(remainder) | TRB_INTR_TARGET(0); queue_trb(xhci, ep_ring, more_trbs_coming, diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 9957bd9..6e7dc6f 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3973,7 +3973,7 @@ int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1) __le32 __iomem *addr; int raw_port; - if (hcd->speed != HCD_USB3) + if (hcd->speed < HCD_USB3) addr = xhci->usb2_ports[port1 - 1]; else addr = xhci->usb3_ports[port1 - 1]; @@ -4124,7 +4124,7 @@ int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd, int hird, exit_latency; int ret; - if (hcd->speed == HCD_USB3 || !xhci->hw_lpm_support || + if (hcd->speed >= HCD_USB3 || !xhci->hw_lpm_support || !udev->lpm_capable) return -EPERM; @@ -4241,7 +4241,7 @@ int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev) struct xhci_hcd *xhci = hcd_to_xhci(hcd); int portnum = udev->portnum - 1; - if (hcd->speed == HCD_USB3 || !xhci->sw_lpm_support || + if (hcd->speed >= HCD_USB3 || !xhci->sw_lpm_support || !udev->lpm_capable) return 0; @@ -4841,8 +4841,9 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) /* XHCI controllers don't stop the ep queue on short packets :| */ hcd->self.no_stop_on_short = 1; + xhci = hcd_to_xhci(hcd); + if (usb_hcd_is_primary_hcd(hcd)) { - xhci = hcd_to_xhci(hcd); xhci->main_hcd = hcd; /* Mark the first roothub as being USB 2.0. * The xHCI driver will register the USB 3.0 roothub. @@ -4856,6 +4857,10 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) */ hcd->has_tt = 1; } else { + if (xhci->sbrn == 0x31) { + xhci_info(xhci, "Host supports USB 3.1 Enhanced SuperSpeed\n"); + hcd->speed = HCD_USB31; + } /* xHCI private pointer was set in xhci_pci_probe for the second * registered roothub. */ @@ -4875,6 +4880,8 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) xhci->hcc_params = readl(&xhci->cap_regs->hc_capbase); xhci->hci_version = HC_VERSION(xhci->hcc_params); xhci->hcc_params = readl(&xhci->cap_regs->hcc_params); + if (xhci->hci_version > 0x100) + xhci->hcc_params2 = readl(&xhci->cap_regs->hcc_params2); xhci_print_registers(xhci); xhci->quirks = quirks; @@ -4906,6 +4913,16 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) !dma_set_mask(dev, DMA_BIT_MASK(64))) { xhci_dbg(xhci, "Enabling 64-bit DMA addresses.\n"); dma_set_coherent_mask(dev, DMA_BIT_MASK(64)); + } else { + /* + * This is to avoid error in cases where a 32-bit USB + * controller is used on a 64-bit capable system. + */ + retval = dma_set_mask(dev, DMA_BIT_MASK(32)); + if (retval) + return retval; + xhci_dbg(xhci, "Enabling 32-bit DMA addresses.\n"); + dma_set_coherent_mask(dev, DMA_BIT_MASK(32)); } xhci_dbg(xhci, "Calling HCD init\n"); @@ -5020,7 +5037,7 @@ static int __init xhci_hcd_init(void) BUILD_BUG_ON(sizeof(struct xhci_stream_ctx) != 4*32/8); BUILD_BUG_ON(sizeof(union xhci_trb) != 4*32/8); BUILD_BUG_ON(sizeof(struct xhci_erst_entry) != 4*32/8); - BUILD_BUG_ON(sizeof(struct xhci_cap_regs) != 7*32/8); + BUILD_BUG_ON(sizeof(struct xhci_cap_regs) != 8*32/8); BUILD_BUG_ON(sizeof(struct xhci_intr_reg) != 8*32/8); /* xhci_run_regs has eight fields and embeds 128 xhci_intr_regs */ BUILD_BUG_ON(sizeof(struct xhci_run_regs) != (8+8*128)*32/8); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index dbda41e..be9048e 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -29,6 +29,8 @@ #include <linux/kernel.h> #include <linux/usb/hcd.h> +#include <asm-generic/io-64-nonatomic-lo-hi.h> + /* Code sharing between pci-quirks and xhci hcd */ #include "xhci-ext-caps.h" #include "pci-quirks.h" @@ -56,6 +58,7 @@ * @hcc_params: HCCPARAMS - Capability Parameters * @db_off: DBOFF - Doorbell array offset * @run_regs_off: RTSOFF - Runtime register space offset + * @hcc_params2: HCCPARAMS2 Capability Parameters 2, xhci 1.1 only */ struct xhci_cap_regs { __le32 hc_capbase; @@ -65,6 +68,7 @@ struct xhci_cap_regs { __le32 hcc_params; __le32 db_off; __le32 run_regs_off; + __le32 hcc_params2; /* xhci 1.1 */ /* Reserved up to (CAPLENGTH - 0x1C) */ }; @@ -134,6 +138,21 @@ struct xhci_cap_regs { /* run_regs_off bitmask - bits 0:4 reserved */ #define RTSOFF_MASK (~0x1f) +/* HCCPARAMS2 - hcc_params2 - bitmasks */ +/* true: HC supports U3 entry Capability */ +#define HCC2_U3C(p) ((p) & (1 << 0)) +/* true: HC supports Configure endpoint command Max exit latency too large */ +#define HCC2_CMC(p) ((p) & (1 << 1)) +/* true: HC supports Force Save context Capability */ +#define HCC2_FSC(p) ((p) & (1 << 2)) +/* true: HC supports Compliance Transition Capability */ +#define HCC2_CTC(p) ((p) & (1 << 3)) +/* true: HC support Large ESIT payload Capability > 48k */ +#define HCC2_LEC(p) ((p) & (1 << 4)) +/* true: HC support Configuration Information Capability */ +#define HCC2_CIC(p) ((p) & (1 << 5)) +/* true: HC support Extended TBC Capability, Isoc burst count > 65535 */ +#define HCC2_ETC(p) ((p) & (1 << 6)) /* Number of registers per port */ #define NUM_PORT_REGS 4 @@ -269,7 +288,11 @@ struct xhci_op_regs { /* CONFIG - Configure Register - config_reg bitmasks */ /* bits 0:7 - maximum number of device slots enabled (NumSlotsEn) */ #define MAX_DEVS(p) ((p) & 0xff) -/* bits 8:31 - reserved and should be preserved */ +/* bit 8: U3 Entry Enabled, assert PLC when root port enters U3, xhci 1.1 */ +#define CONFIG_U3E (1 << 8) +/* bit 9: Configuration Information Enable, xhci 1.1 */ +#define CONFIG_CIE (1 << 9) +/* bits 10:31 - reserved and should be preserved */ /* PORTSC - Port Status and Control Register - port_status_base bitmasks */ /* true: device connected */ @@ -306,11 +329,16 @@ struct xhci_op_regs { #define XDEV_LS (0x2 << 10) #define XDEV_HS (0x3 << 10) #define XDEV_SS (0x4 << 10) +#define XDEV_SSP (0x5 << 10) #define DEV_UNDEFSPEED(p) (((p) & DEV_SPEED_MASK) == (0x0<<10)) #define DEV_FULLSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_FS) #define DEV_LOWSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_LS) #define DEV_HIGHSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_HS) #define DEV_SUPERSPEED(p) (((p) & DEV_SPEED_MASK) == XDEV_SS) +#define DEV_SUPERSPEEDPLUS(p) (((p) & DEV_SPEED_MASK) == XDEV_SSP) +#define DEV_SUPERSPEED_ANY(p) (((p) & DEV_SPEED_MASK) >= XDEV_SS) +#define DEV_PORT_SPEED(p) (((p) >> 10) & 0x0f) + /* Bits 20:23 in the Slot Context are the speed for the device */ #define SLOT_SPEED_FS (XDEV_FS << 10) #define SLOT_SPEED_LS (XDEV_LS << 10) @@ -394,6 +422,9 @@ struct xhci_op_regs { #define PORT_L1DS(p) (((p) & 0xff) << 8) #define PORT_HLE (1 << 16) +/* USB3 Protocol PORTLI Port Link Information */ +#define PORT_RX_LANES(p) (((p) >> 16) & 0xf) +#define PORT_TX_LANES(p) (((p) >> 20) & 0xf) /* USB2 Protocol PORTHLPMC */ #define PORT_HIRDM(p)((p) & 3) @@ -519,9 +550,23 @@ struct xhci_protocol_caps { }; #define XHCI_EXT_PORT_MAJOR(x) (((x) >> 24) & 0xff) +#define XHCI_EXT_PORT_MINOR(x) (((x) >> 16) & 0xff) +#define XHCI_EXT_PORT_PSIC(x) (((x) >> 28) & 0x0f) #define XHCI_EXT_PORT_OFF(x) ((x) & 0xff) #define XHCI_EXT_PORT_COUNT(x) (((x) >> 8) & 0xff) +#define XHCI_EXT_PORT_PSIV(x) (((x) >> 0) & 0x0f) +#define XHCI_EXT_PORT_PSIE(x) (((x) >> 4) & 0x03) +#define XHCI_EXT_PORT_PLT(x) (((x) >> 6) & 0x03) +#define XHCI_EXT_PORT_PFD(x) (((x) >> 8) & 0x01) +#define XHCI_EXT_PORT_LP(x) (((x) >> 14) & 0x03) +#define XHCI_EXT_PORT_PSIM(x) (((x) >> 16) & 0xffff) + +#define PLT_MASK (0x03 << 6) +#define PLT_SYM (0x00 << 6) +#define PLT_ASYM_RX (0x02 << 6) +#define PLT_ASYM_TX (0x03 << 6) + /** * struct xhci_container_ctx * @type: Type of context. Used to calculated offsets to contained contexts. @@ -1136,6 +1181,8 @@ enum xhci_setup_dev { /* Normal TRB fields */ /* transfer_len bitmasks - bits 0:16 */ #define TRB_LEN(p) ((p) & 0x1ffff) +/* TD Size, packets remaining in this TD, bits 21:17 (5 bits, so max 31) */ +#define TRB_TD_SIZE(p) (min((p), (u32)31) << 17) /* Interrupter Target - which MSI-X vector to target the completion event at */ #define TRB_INTR_TARGET(p) (((p) & 0x3ff) << 22) #define GET_INTR_TARGET(p) (((p) >> 22) & 0x3ff) @@ -1448,6 +1495,14 @@ static inline unsigned int hcd_index(struct usb_hcd *hcd) return 1; } +struct xhci_hub { + u8 maj_rev; + u8 min_rev; + u32 *psi; /* array of protocol speed ID entries */ + u8 psi_count; + u8 psi_uid_count; +}; + /* There is one xhci_hcd structure per controller */ struct xhci_hcd { struct usb_hcd *main_hcd; @@ -1465,6 +1520,7 @@ struct xhci_hcd { __u32 hcs_params2; __u32 hcs_params3; __u32 hcc_params; + __u32 hcc_params2; spinlock_t lock; @@ -1586,6 +1642,8 @@ struct xhci_hcd { unsigned int num_usb3_ports; /* Array of pointers to USB 2.0 PORTSC registers */ __le32 __iomem **usb2_ports; + struct xhci_hub usb2_rhub; + struct xhci_hub usb3_rhub; unsigned int num_usb2_ports; /* support xHCI 0.96 spec USB2 software LPM */ unsigned sw_lpm_support:1; @@ -1651,20 +1709,12 @@ static inline struct usb_hcd *xhci_to_hcd(struct xhci_hcd *xhci) static inline u64 xhci_read_64(const struct xhci_hcd *xhci, __le64 __iomem *regs) { - __u32 __iomem *ptr = (__u32 __iomem *) regs; - u64 val_lo = readl(ptr); - u64 val_hi = readl(ptr + 1); - return val_lo + (val_hi << 32); + return lo_hi_readq(regs); } static inline void xhci_write_64(struct xhci_hcd *xhci, const u64 val, __le64 __iomem *regs) { - __u32 __iomem *ptr = (__u32 __iomem *) regs; - u32 val_lo = lower_32_bits(val); - u32 val_hi = upper_32_bits(val); - - writel(val_lo, ptr); - writel(val_hi, ptr + 1); + lo_hi_writeq(val, regs); } static inline int xhci_link_trb_quirk(struct xhci_hcd *xhci) diff --git a/drivers/usb/misc/usb3503.c b/drivers/usb/misc/usb3503.c index 64ff5b9..b45cb77 100644 --- a/drivers/usb/misc/usb3503.c +++ b/drivers/usb/misc/usb3503.c @@ -410,7 +410,7 @@ static int __init usb3503_init(void) { int err; - err = i2c_register_driver(THIS_MODULE, &usb3503_i2c_driver); + err = i2c_add_driver(&usb3503_i2c_driver); if (err != 0) pr_err("usb3503: Failed to register I2C driver: %d\n", err); diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 9517812..637f3f7 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -17,6 +17,7 @@ static int override_alt = -1; module_param_named(alt, override_alt, int, 0644); MODULE_PARM_DESC(alt, ">= 0 to override altsetting selection"); +static void complicated_callback(struct urb *urb); /*-------------------------------------------------------------------------*/ @@ -95,6 +96,7 @@ static struct usb_device *testdev_to_usbdev(struct usbtest_dev *test) dev_warn(&(tdev)->intf->dev , fmt , ## args) #define GUARD_BYTE 0xA5 +#define MAX_SGLEN 128 /*-------------------------------------------------------------------------*/ @@ -238,7 +240,8 @@ static struct urb *usbtest_alloc_urb( unsigned long bytes, unsigned transfer_flags, unsigned offset, - u8 bInterval) + u8 bInterval, + usb_complete_t complete_fn) { struct urb *urb; @@ -247,10 +250,10 @@ static struct urb *usbtest_alloc_urb( return urb; if (bInterval) - usb_fill_int_urb(urb, udev, pipe, NULL, bytes, simple_callback, + usb_fill_int_urb(urb, udev, pipe, NULL, bytes, complete_fn, NULL, bInterval); else - usb_fill_bulk_urb(urb, udev, pipe, NULL, bytes, simple_callback, + usb_fill_bulk_urb(urb, udev, pipe, NULL, bytes, complete_fn, NULL); urb->interval = (udev->speed == USB_SPEED_HIGH) @@ -295,7 +298,17 @@ static struct urb *simple_alloc_urb( u8 bInterval) { return usbtest_alloc_urb(udev, pipe, bytes, URB_NO_TRANSFER_DMA_MAP, 0, - bInterval); + bInterval, simple_callback); +} + +static struct urb *complicated_alloc_urb( + struct usb_device *udev, + int pipe, + unsigned long bytes, + u8 bInterval) +{ + return usbtest_alloc_urb(udev, pipe, bytes, URB_NO_TRANSFER_DMA_MAP, 0, + bInterval, complicated_callback); } static unsigned pattern; @@ -303,11 +316,20 @@ static unsigned mod_pattern; module_param_named(pattern, mod_pattern, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(mod_pattern, "i/o pattern (0 == zeroes)"); -static inline void simple_fill_buf(struct urb *urb) +static unsigned get_maxpacket(struct usb_device *udev, int pipe) +{ + struct usb_host_endpoint *ep; + + ep = usb_pipe_endpoint(udev, pipe); + return le16_to_cpup(&ep->desc.wMaxPacketSize); +} + +static void simple_fill_buf(struct urb *urb) { unsigned i; u8 *buf = urb->transfer_buffer; unsigned len = urb->transfer_buffer_length; + unsigned maxpacket; switch (pattern) { default: @@ -316,8 +338,9 @@ static inline void simple_fill_buf(struct urb *urb) memset(buf, 0, len); break; case 1: /* mod63 */ + maxpacket = get_maxpacket(urb->dev, urb->pipe); for (i = 0; i < len; i++) - *buf++ = (u8) (i % 63); + *buf++ = (u8) ((i % maxpacket) % 63); break; } } @@ -349,6 +372,7 @@ static int simple_check_buf(struct usbtest_dev *tdev, struct urb *urb) u8 expected; u8 *buf = urb->transfer_buffer; unsigned len = urb->actual_length; + unsigned maxpacket = get_maxpacket(urb->dev, urb->pipe); int ret = check_guard_bytes(tdev, urb); if (ret) @@ -366,7 +390,7 @@ static int simple_check_buf(struct usbtest_dev *tdev, struct urb *urb) * with set_interface or set_config. */ case 1: /* mod63 */ - expected = i % 63; + expected = (i % maxpacket) % 63; break; /* always fail unsupported patterns */ default: @@ -478,11 +502,13 @@ static void free_sglist(struct scatterlist *sg, int nents) } static struct scatterlist * -alloc_sglist(int nents, int max, int vary) +alloc_sglist(int nents, int max, int vary, struct usbtest_dev *dev, int pipe) { struct scatterlist *sg; unsigned i; unsigned size = max; + unsigned maxpacket = + get_maxpacket(interface_to_usbdev(dev->intf), pipe); if (max == 0) return NULL; @@ -511,7 +537,7 @@ alloc_sglist(int nents, int max, int vary) break; case 1: for (j = 0; j < size; j++) - *buf++ = (u8) (j % 63); + *buf++ = (u8) ((j % maxpacket) % 63); break; } @@ -1719,7 +1745,7 @@ static int ctrl_out(struct usbtest_dev *dev, for (i = 0; i < count; i++) { /* write patterned data */ for (j = 0; j < len; j++) - buf[j] = i + j; + buf[j] = (u8)(i + j); retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x5b, USB_DIR_OUT|USB_TYPE_VENDOR, 0, 0, buf, len, USB_CTRL_SET_TIMEOUT); @@ -1749,9 +1775,9 @@ static int ctrl_out(struct usbtest_dev *dev, /* fail if we can't verify */ for (j = 0; j < len; j++) { - if (buf[j] != (u8) (i + j)) { + if (buf[j] != (u8)(i + j)) { ERROR(dev, "ctrl_out, byte %d is %d not %d\n", - j, buf[j], (u8) i + j); + j, buf[j], (u8)(i + j)); retval = -EBADMSG; break; } @@ -1781,12 +1807,12 @@ static int ctrl_out(struct usbtest_dev *dev, /*-------------------------------------------------------------------------*/ -/* ISO tests ... mimics common usage +/* ISO/BULK tests ... mimics common usage * - buffer length is split into N packets (mostly maxpacket sized) * - multi-buffers according to sglen */ -struct iso_context { +struct transfer_context { unsigned count; unsigned pending; spinlock_t lock; @@ -1795,11 +1821,12 @@ struct iso_context { unsigned long errors; unsigned long packet_count; struct usbtest_dev *dev; + bool is_iso; }; -static void iso_callback(struct urb *urb) +static void complicated_callback(struct urb *urb) { - struct iso_context *ctx = urb->context; + struct transfer_context *ctx = urb->context; spin_lock(&ctx->lock); ctx->count--; @@ -1808,7 +1835,7 @@ static void iso_callback(struct urb *urb) if (urb->error_count > 0) ctx->errors += urb->error_count; else if (urb->status != 0) - ctx->errors += urb->number_of_packets; + ctx->errors += (ctx->is_iso ? urb->number_of_packets : 1); else if (urb->actual_length != urb->transfer_buffer_length) ctx->errors++; else if (check_guard_bytes(ctx->dev, urb) != 0) @@ -1895,7 +1922,7 @@ static struct urb *iso_alloc_urb( urb->iso_frame_desc[i].offset = maxp * i; } - urb->complete = iso_callback; + urb->complete = complicated_callback; /* urb->context = SET BY CALLER */ urb->interval = 1 << (desc->bInterval - 1); urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; @@ -1903,37 +1930,33 @@ static struct urb *iso_alloc_urb( } static int -test_iso_queue(struct usbtest_dev *dev, struct usbtest_param *param, +test_queue(struct usbtest_dev *dev, struct usbtest_param *param, int pipe, struct usb_endpoint_descriptor *desc, unsigned offset) { - struct iso_context context; + struct transfer_context context; struct usb_device *udev; unsigned i; unsigned long packets = 0; int status = 0; - struct urb *urbs[10]; /* FIXME no limit */ - - if (param->sglen > 10) - return -EDOM; + struct urb *urbs[param->sglen]; memset(&context, 0, sizeof(context)); context.count = param->iterations * param->sglen; context.dev = dev; + context.is_iso = !!desc; init_completion(&context.done); spin_lock_init(&context.lock); - memset(urbs, 0, sizeof(urbs)); udev = testdev_to_usbdev(dev); - dev_info(&dev->intf->dev, - "iso period %d %sframes, wMaxPacket %d, transactions: %d\n", - 1 << (desc->bInterval - 1), - (udev->speed == USB_SPEED_HIGH) ? "micro" : "", - usb_endpoint_maxp(desc) & 0x7ff, - 1 + (0x3 & (usb_endpoint_maxp(desc) >> 11))); for (i = 0; i < param->sglen; i++) { - urbs[i] = iso_alloc_urb(udev, pipe, desc, + if (context.is_iso) + urbs[i] = iso_alloc_urb(udev, pipe, desc, param->length, offset); + else + urbs[i] = complicated_alloc_urb(udev, pipe, + param->length, 0); + if (!urbs[i]) { status = -ENOMEM; goto fail; @@ -1942,11 +1965,21 @@ test_iso_queue(struct usbtest_dev *dev, struct usbtest_param *param, urbs[i]->context = &context; } packets *= param->iterations; - dev_info(&dev->intf->dev, - "total %lu msec (%lu packets)\n", - (packets * (1 << (desc->bInterval - 1))) - / ((udev->speed == USB_SPEED_HIGH) ? 8 : 1), - packets); + + if (context.is_iso) { + dev_info(&dev->intf->dev, + "iso period %d %sframes, wMaxPacket %d, transactions: %d\n", + 1 << (desc->bInterval - 1), + (udev->speed == USB_SPEED_HIGH) ? "micro" : "", + usb_endpoint_maxp(desc) & 0x7ff, + 1 + (0x3 & (usb_endpoint_maxp(desc) >> 11))); + + dev_info(&dev->intf->dev, + "total %lu msec (%lu packets)\n", + (packets * (1 << (desc->bInterval - 1))) + / ((udev->speed == USB_SPEED_HIGH) ? 8 : 1), + packets); + } spin_lock_irq(&context.lock); for (i = 0; i < param->sglen; i++) { @@ -1983,7 +2016,8 @@ test_iso_queue(struct usbtest_dev *dev, struct usbtest_param *param, ; else if (context.submit_error) status = -EACCES; - else if (context.errors > context.packet_count / 10) + else if (context.errors > + (context.is_iso ? context.packet_count / 10 : 0)) status = -EIO; return status; @@ -2004,8 +2038,8 @@ static int test_unaligned_bulk( const char *label) { int retval; - struct urb *urb = usbtest_alloc_urb( - testdev_to_usbdev(tdev), pipe, length, transfer_flags, 1, 0); + struct urb *urb = usbtest_alloc_urb(testdev_to_usbdev(tdev), + pipe, length, transfer_flags, 1, 0, simple_callback); if (!urb) return -ENOMEM; @@ -2061,6 +2095,9 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) if (param->iterations <= 0) return -EINVAL; + if (param->sglen > MAX_SGLEN) + return -EINVAL; + if (mutex_lock_interruptible(&dev->lock)) return -ERESTARTSYS; @@ -2176,7 +2213,8 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) "TEST 5: write %d sglists %d entries of %d bytes\n", param->iterations, param->sglen, param->length); - sg = alloc_sglist(param->sglen, param->length, 0); + sg = alloc_sglist(param->sglen, param->length, + 0, dev, dev->out_pipe); if (!sg) { retval = -ENOMEM; break; @@ -2194,7 +2232,8 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) "TEST 6: read %d sglists %d entries of %d bytes\n", param->iterations, param->sglen, param->length); - sg = alloc_sglist(param->sglen, param->length, 0); + sg = alloc_sglist(param->sglen, param->length, + 0, dev, dev->in_pipe); if (!sg) { retval = -ENOMEM; break; @@ -2211,7 +2250,8 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) "TEST 7: write/%d %d sglists %d entries 0..%d bytes\n", param->vary, param->iterations, param->sglen, param->length); - sg = alloc_sglist(param->sglen, param->length, param->vary); + sg = alloc_sglist(param->sglen, param->length, + param->vary, dev, dev->out_pipe); if (!sg) { retval = -ENOMEM; break; @@ -2228,7 +2268,8 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) "TEST 8: read/%d %d sglists %d entries 0..%d bytes\n", param->vary, param->iterations, param->sglen, param->length); - sg = alloc_sglist(param->sglen, param->length, param->vary); + sg = alloc_sglist(param->sglen, param->length, + param->vary, dev, dev->in_pipe); if (!sg) { retval = -ENOMEM; break; @@ -2325,7 +2366,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) param->iterations, param->sglen, param->length); /* FIRMWARE: iso sink */ - retval = test_iso_queue(dev, param, + retval = test_queue(dev, param, dev->out_iso_pipe, dev->iso_out, 0); break; @@ -2338,7 +2379,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) param->iterations, param->sglen, param->length); /* FIRMWARE: iso source */ - retval = test_iso_queue(dev, param, + retval = test_queue(dev, param, dev->in_iso_pipe, dev->iso_in, 0); break; @@ -2419,7 +2460,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) "TEST 22: write %d iso odd, %d entries of %d bytes\n", param->iterations, param->sglen, param->length); - retval = test_iso_queue(dev, param, + retval = test_queue(dev, param, dev->out_iso_pipe, dev->iso_out, 1); break; @@ -2430,7 +2471,7 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) "TEST 23: read %d iso odd, %d entries of %d bytes\n", param->iterations, param->sglen, param->length); - retval = test_iso_queue(dev, param, + retval = test_queue(dev, param, dev->in_iso_pipe, dev->iso_in, 1); break; @@ -2487,6 +2528,25 @@ usbtest_ioctl(struct usb_interface *intf, unsigned int code, void *buf) retval = simple_io(dev, urb, param->iterations, 0, 0, "test26"); simple_free_urb(urb); break; + case 27: + /* We do performance test, so ignore data compare */ + if (dev->out_pipe == 0 || param->sglen == 0 || pattern != 0) + break; + dev_info(&intf->dev, + "TEST 27: bulk write %dMbytes\n", (param->iterations * + param->sglen * param->length) / (1024 * 1024)); + retval = test_queue(dev, param, + dev->out_pipe, NULL, 0); + break; + case 28: + if (dev->in_pipe == 0 || param->sglen == 0 || pattern != 0) + break; + dev_info(&intf->dev, + "TEST 28: bulk read %dMbytes\n", (param->iterations * + param->sglen * param->length) / (1024 * 1024)); + retval = test_queue(dev, param, + dev->in_pipe, NULL, 0); + break; } do_gettimeofday(¶m->duration); param->duration.tv_sec -= start.tv_sec; diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 4a518ff..ba13529 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1028,18 +1028,22 @@ void musb_start(struct musb *musb) { void __iomem *regs = musb->mregs; u8 devctl = musb_readb(regs, MUSB_DEVCTL); + u8 power; dev_dbg(musb->controller, "<== devctl %02x\n", devctl); musb_enable_interrupts(musb); musb_writeb(regs, MUSB_TESTMODE, 0); - /* put into basic highspeed mode and start session */ - musb_writeb(regs, MUSB_POWER, MUSB_POWER_ISOUPDATE - | MUSB_POWER_HSENAB - /* ENSUSPEND wedges tusb */ - /* | MUSB_POWER_ENSUSPEND */ - ); + power = MUSB_POWER_ISOUPDATE; + /* + * treating UNKNOWN as unspecified maximum speed, in which case + * we will default to high-speed. + */ + if (musb->config->maximum_speed == USB_SPEED_HIGH || + musb->config->maximum_speed == USB_SPEED_UNKNOWN) + power |= MUSB_POWER_HSENAB; + musb_writeb(regs, MUSB_POWER, power); musb->is_active = 0; devctl = musb_readb(regs, MUSB_DEVCTL); @@ -1771,13 +1775,20 @@ musb_vbus_show(struct device *dev, struct device_attribute *attr, char *buf) unsigned long flags; unsigned long val; int vbus; + u8 devctl; spin_lock_irqsave(&musb->lock, flags); val = musb->a_wait_bcon; - /* FIXME get_vbus_status() is normally #defined as false... - * and is effectively TUSB-specific. - */ vbus = musb_platform_get_vbus_status(musb); + if (vbus < 0) { + /* Use default MUSB method by means of DEVCTL register */ + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + if ((devctl & MUSB_DEVCTL_VBUS) + == (3 << MUSB_DEVCTL_VBUS_SHIFT)) + vbus = 1; + else + vbus = 0; + } spin_unlock_irqrestore(&musb->lock, flags); return sprintf(buf, "Vbus %s, timeout %lu msec\n", diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 4b886d0..2337d7a 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -579,7 +579,7 @@ static inline int musb_platform_recover(struct musb *musb) static inline int musb_platform_get_vbus_status(struct musb *musb) { if (!musb->ops->vbus_status) - return 0; + return -EINVAL; return musb->ops->vbus_status(musb); } diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 84512d1..eeb7d9e 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -666,7 +666,7 @@ static int get_musb_port_mode(struct device *dev) { enum usb_dr_mode mode; - mode = of_usb_get_dr_mode(dev->of_node); + mode = usb_get_dr_mode(dev); switch (mode) { case USB_DR_MODE_HOST: return MUSB_PORT_MODE_HOST; @@ -747,6 +747,19 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue, if (!ret && val) config->multipoint = true; + config->maximum_speed = usb_get_maximum_speed(&parent->dev); + switch (config->maximum_speed) { + case USB_SPEED_LOW: + case USB_SPEED_FULL: + break; + case USB_SPEED_SUPER: + dev_warn(dev, "ignore incorrect maximum_speed " + "(super-speed) setting in dts"); + /* fall through */ + default: + config->maximum_speed = USB_SPEED_HIGH; + } + ret = platform_device_add_data(musb, &pdata, sizeof(pdata)); if (ret) { dev_err(dev, "failed to add platform_data\n"); diff --git a/drivers/usb/musb/sunxi.c b/drivers/usb/musb/sunxi.c index f9f6304..d9b0dc4 100644 --- a/drivers/usb/musb/sunxi.c +++ b/drivers/usb/musb/sunxi.c @@ -341,6 +341,16 @@ static void sunxi_musb_disable(struct musb *musb) clear_bit(SUNXI_MUSB_FL_ENABLED, &glue->flags); } +struct dma_controller *sunxi_musb_dma_controller_create(struct musb *musb, + void __iomem *base) +{ + return NULL; +} + +void sunxi_musb_dma_controller_destroy(struct dma_controller *c) +{ +} + /* * sunxi musb register layout * 0x00 - 0x17 fifo regs, 1 long per fifo @@ -566,6 +576,8 @@ static const struct musb_platform_ops sunxi_musb_ops = { .writeb = sunxi_musb_writeb, .readw = sunxi_musb_readw, .writew = sunxi_musb_writew, + .dma_init = sunxi_musb_dma_controller_create, + .dma_exit = sunxi_musb_dma_controller_destroy, .set_vbus = sunxi_musb_set_vbus, .pre_root_reset_end = sunxi_musb_pre_root_reset_end, .post_root_reset_end = sunxi_musb_post_root_reset_end, @@ -617,7 +629,7 @@ static int sunxi_musb_probe(struct platform_device *pdev) return -ENOMEM; memset(&pdata, 0, sizeof(pdata)); - switch (of_usb_get_dr_mode(np)) { + switch (usb_get_dr_mode(&pdev->dev)) { #if defined CONFIG_USB_MUSB_DUAL_ROLE || defined CONFIG_USB_MUSB_HOST case USB_DR_MODE_HOST: pdata.mode = MUSB_PORT_MODE_HOST; diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index c58c3c0..80eb991 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -1529,7 +1529,7 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg) if (IS_ERR(motg->phy_rst)) motg->phy_rst = NULL; - pdata->mode = of_usb_get_dr_mode(node); + pdata->mode = usb_get_dr_mode(&pdev->dev); if (pdata->mode == USB_DR_MODE_UNKNOWN) pdata->mode = USB_DR_MODE_OTG; diff --git a/drivers/usb/phy/phy-qcom-8x16-usb.c b/drivers/usb/phy/phy-qcom-8x16-usb.c index 5d357a9..579587d 100644 --- a/drivers/usb/phy/phy-qcom-8x16-usb.c +++ b/drivers/usb/phy/phy-qcom-8x16-usb.c @@ -71,7 +71,7 @@ struct phy_8x16 { struct reset_control *phy_reset; - struct extcon_specific_cable_nb vbus_cable; + struct extcon_dev *vbus_edev; struct notifier_block vbus_notify; struct gpio_desc *switch_gpio; @@ -234,7 +234,7 @@ static int phy_8x16_init(struct usb_phy *phy) val = ULPI_PWR_OTG_COMP_DISABLE; usb_phy_io_write(phy, val, ULPI_SET(ULPI_PWR_CLK_MNG_REG)); - state = extcon_get_cable_state(qphy->vbus_cable.edev, "USB"); + state = extcon_get_cable_state_(qphy->vbus_edev, EXTCON_USB); if (state) phy_8x16_vbus_on(qphy); else @@ -314,7 +314,6 @@ static int phy_8x16_reboot_notify(struct notifier_block *this, static int phy_8x16_probe(struct platform_device *pdev) { - struct extcon_dev *edev; struct phy_8x16 *qphy; struct resource *res; struct usb_phy *phy; @@ -349,9 +348,9 @@ static int phy_8x16_probe(struct platform_device *pdev) if (ret < 0) return ret; - edev = extcon_get_edev_by_phandle(phy->dev, 0); - if (IS_ERR(edev)) - return PTR_ERR(edev); + qphy->vbus_edev = extcon_get_edev_by_phandle(phy->dev, 0); + if (IS_ERR(qphy->vbus_edev)) + return PTR_ERR(qphy->vbus_edev); ret = clk_set_rate(qphy->core_clk, INT_MAX); if (ret < 0) @@ -370,8 +369,8 @@ static int phy_8x16_probe(struct platform_device *pdev) goto off_clks; qphy->vbus_notify.notifier_call = phy_8x16_vbus_notify; - ret = extcon_register_interest(&qphy->vbus_cable, edev->name, - "USB", &qphy->vbus_notify); + ret = extcon_register_notifier(qphy->vbus_edev, EXTCON_USB, + &qphy->vbus_notify); if (ret < 0) goto off_power; @@ -385,7 +384,8 @@ static int phy_8x16_probe(struct platform_device *pdev) return 0; off_extcon: - extcon_unregister_interest(&qphy->vbus_cable); + extcon_unregister_notifier(qphy->vbus_edev, EXTCON_USB, + &qphy->vbus_notify); off_power: phy_8x16_regulators_disable(qphy); off_clks: @@ -400,7 +400,8 @@ static int phy_8x16_remove(struct platform_device *pdev) struct phy_8x16 *qphy = platform_get_drvdata(pdev); unregister_reboot_notifier(&qphy->reboot_notify); - extcon_unregister_interest(&qphy->vbus_cable); + extcon_unregister_notifier(qphy->vbus_edev, EXTCON_USB, + &qphy->vbus_notify); /* * Ensure that D+/D- lines are routed to uB connector, so diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index ab025b0..5fe4a57 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -1029,7 +1029,7 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) } if (of_find_property(np, "dr_mode", NULL)) - tegra_phy->mode = of_usb_get_dr_mode(np); + tegra_phy->mode = usb_get_dr_mode(&pdev->dev); else tegra_phy->mode = USB_DR_MODE_HOST; diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c index 0ac1b10..fce82fd 100644 --- a/drivers/usb/serial/io_ti.c +++ b/drivers/usb/serial/io_ti.c @@ -54,8 +54,10 @@ #define TI_MODE_CONFIGURING 0 /* Device has not entered start device */ #define TI_MODE_BOOT 1 /* Staying in boot mode */ #define TI_MODE_DOWNLOAD 2 /* Made it to download mode */ -#define TI_MODE_TRANSITIONING 3 /* Currently in boot mode but - transitioning to download mode */ +#define TI_MODE_TRANSITIONING 3 /* + * Currently in boot mode but + * transitioning to download mode + */ /* read urb state */ #define EDGE_READ_URB_RUNNING 0 @@ -97,9 +99,11 @@ struct edgeport_port { __u8 shadow_mcr; __u8 shadow_lsr; __u8 lsr_mask; - __u32 ump_read_timeout; /* Number of milliseconds the UMP will - wait without data before completing - a read short */ + __u32 ump_read_timeout; /* + * Number of milliseconds the UMP will + * wait without data before completing + * a read short + */ int baud_rate; int close_pending; int lsr_event; @@ -115,8 +119,10 @@ struct edgeport_port { struct edgeport_serial { struct product_info product_info; u8 TI_I2C_Type; /* Type of I2C in UMP */ - u8 TiReadI2C; /* Set to TRUE if we have read the - I2c in Boot Mode */ + u8 TiReadI2C; /* + * Set to TRUE if we have read the + * I2c in Boot Mode + */ struct mutex es_lock; int num_ports_open; struct usb_serial *serial; @@ -223,6 +229,11 @@ static void edge_set_termios(struct tty_struct *tty, struct usb_serial_port *port, struct ktermios *old_termios); static void edge_send(struct usb_serial_port *port, struct tty_struct *tty); +static int do_download_mode(struct edgeport_serial *serial, + const struct firmware *fw); +static int do_boot_mode(struct edgeport_serial *serial, + const struct firmware *fw); + /* sysfs attributes */ static int edge_create_sysfs_attrs(struct usb_serial_port *port); static int edge_remove_sysfs_attrs(struct usb_serial_port *port); @@ -324,7 +335,8 @@ static int read_download_mem(struct usb_device *dev, int start_address, dev_dbg(&dev->dev, "%s - @ %x for %d\n", __func__, start_address, length); - /* Read in blocks of 64 bytes + /* + * Read in blocks of 64 bytes * (TI firmware can't handle more than 64 byte reads) */ while (length) { @@ -430,7 +442,6 @@ static int write_boot_mem(struct edgeport_serial *serial, return status; } - /* Write edgeport I2C memory to TI chip */ static int write_i2c_mem(struct edgeport_serial *serial, int start_address, int length, __u8 address_type, __u8 *buffer) @@ -472,8 +483,10 @@ static int write_i2c_mem(struct edgeport_serial *serial, start_address += write_length; buffer += write_length; - /* We should be aligned now -- can write - max page size bytes at a time */ + /* + * We should be aligned now -- can write max page size bytes at a + * time. + */ while (length) { if (length > EPROM_PAGE_SIZE) write_length = EPROM_PAGE_SIZE; @@ -506,7 +519,8 @@ static int write_i2c_mem(struct edgeport_serial *serial, return status; } -/* Examine the UMP DMA registers and LSR +/* + * Examine the UMP DMA registers and LSR * * Check the MSBit of the X and Y DMA byte count registers. * A zero in this bit indicates that the TX DMA buffers are empty @@ -523,9 +537,11 @@ static int tx_active(struct edgeport_port *port) if (!oedb) return -ENOMEM; - lsr = kmalloc(1, GFP_KERNEL); /* Sigh, that's right, just one byte, - as not all platforms can do DMA - from stack */ + /* + * Sigh, that's right, just one byte, as not all platforms can + * do DMA from stack + */ + lsr = kmalloc(1, GFP_KERNEL); if (!lsr) { kfree(oedb); return -ENOMEM; @@ -615,8 +631,6 @@ static int write_rom(struct edgeport_serial *serial, int start_address, return -EINVAL; } - - /* Read a descriptor header from I2C based on type */ static int get_descriptor_addr(struct edgeport_serial *serial, int desc_type, struct ti_i2c_desc *rom_desc) @@ -785,8 +799,7 @@ exit: } /* Build firmware header used for firmware update */ -static int build_i2c_fw_hdr(u8 *header, struct device *dev, - const struct firmware *fw) +static int build_i2c_fw_hdr(u8 *header, const struct firmware *fw) { __u8 *buffer; int buffer_size; @@ -797,7 +810,8 @@ static int build_i2c_fw_hdr(u8 *header, struct device *dev, struct ti_i2c_firmware_rec *firmware_rec; struct edgeport_fw_hdr *fw_hdr = (struct edgeport_fw_hdr *)fw->data; - /* In order to update the I2C firmware we must change the type 2 record + /* + * In order to update the I2C firmware we must change the type 2 record * to type 0xF2. This will force the UMP to come up in Boot Mode. * Then while in boot mode, the driver will download the latest * firmware (padded to 15.5k) into the UMP ram. And finally when the @@ -806,8 +820,10 @@ static int build_i2c_fw_hdr(u8 *header, struct device *dev, * update the record type from 0xf2 to 0x02. */ - /* Allocate a 15.5k buffer + 2 bytes for version number - * (Firmware Record) */ + /* + * Allocate a 15.5k buffer + 2 bytes for version number (Firmware + * Record) + */ buffer_size = (((1024 * 16) - 512 ) + sizeof(struct ti_i2c_firmware_rec)); @@ -815,7 +831,7 @@ static int build_i2c_fw_hdr(u8 *header, struct device *dev, if (!buffer) return -ENOMEM; - // Set entire image of 0xffs + /* Set entire image of 0xffs */ memset(buffer, 0xff, buffer_size); /* Copy version number into firmware record */ @@ -981,32 +997,41 @@ static int check_fw_sanity(struct edgeport_serial *serial, return 0; } -/** +/* * DownloadTIFirmware - Download run-time operating firmware to the TI5052 * * This routine downloads the main operating code into the TI5052, using the * boot code already burned into E2PROM or ROM. */ -static int download_fw(struct edgeport_serial *serial, - const struct firmware *fw) +static int download_fw(struct edgeport_serial *serial) { - struct device *dev = &serial->serial->dev->dev; + struct device *dev = &serial->serial->interface->dev; int status = 0; - int start_address; - struct edge_ti_manuf_descriptor *ti_manuf_desc; struct usb_interface_descriptor *interface; - int download_cur_ver; - int download_new_ver; - struct edgeport_fw_hdr *fw_hdr = (struct edgeport_fw_hdr *)fw->data; + const struct firmware *fw; + const char *fw_name = "edgeport/down3.bin"; + struct edgeport_fw_hdr *fw_hdr; - if (check_fw_sanity(serial, fw)) - return -EINVAL; + status = request_firmware(&fw, fw_name, dev); + if (status) { + dev_err(dev, "Failed to load image \"%s\" err %d\n", + fw_name, status); + return status; + } + + if (check_fw_sanity(serial, fw)) { + status = -EINVAL; + goto out; + } - /* If on-board version is newer, "fw_version" will be updated below. */ + fw_hdr = (struct edgeport_fw_hdr *)fw->data; + + /* If on-board version is newer, "fw_version" will be updated later. */ serial->fw_version = (fw_hdr->major_version << 8) + fw_hdr->minor_version; - /* This routine is entered by both the BOOT mode and the Download mode + /* + * This routine is entered by both the BOOT mode and the Download mode * We can determine which code is running by the reading the config * descriptor and if we have only one bulk pipe it is in boot mode */ @@ -1017,12 +1042,13 @@ static int download_fw(struct edgeport_serial *serial, status = choose_config(serial->serial->dev); if (status) - return status; + goto out; interface = &serial->serial->interface->cur_altsetting->desc; if (!interface) { dev_err(dev, "%s - no interface set, error!\n", __func__); - return -ENODEV; + status = -ENODEV; + goto out; } /* @@ -1030,190 +1056,219 @@ static int download_fw(struct edgeport_serial *serial, * if we have more than one endpoint we are definitely in download * mode */ - if (interface->bNumEndpoints > 1) + if (interface->bNumEndpoints > 1) { serial->product_info.TiMode = TI_MODE_DOWNLOAD; - else + status = do_download_mode(serial, fw); + } else { /* Otherwise we will remain in configuring mode */ serial->product_info.TiMode = TI_MODE_CONFIGURING; + status = do_boot_mode(serial, fw); + } - /********************************************************************/ - /* Download Mode */ - /********************************************************************/ - if (serial->product_info.TiMode == TI_MODE_DOWNLOAD) { - struct ti_i2c_desc *rom_desc; +out: + release_firmware(fw); + return status; +} - dev_dbg(dev, "%s - RUNNING IN DOWNLOAD MODE\n", __func__); +static int do_download_mode(struct edgeport_serial *serial, + const struct firmware *fw) +{ + struct device *dev = &serial->serial->interface->dev; + int status = 0; + int start_address; + struct edge_ti_manuf_descriptor *ti_manuf_desc; + int download_cur_ver; + int download_new_ver; + struct edgeport_fw_hdr *fw_hdr = (struct edgeport_fw_hdr *)fw->data; + struct ti_i2c_desc *rom_desc; - status = check_i2c_image(serial); - if (status) { - dev_dbg(dev, "%s - DOWNLOAD MODE -- BAD I2C\n", __func__); - return status; - } + dev_dbg(dev, "%s - RUNNING IN DOWNLOAD MODE\n", __func__); - /* Validate Hardware version number - * Read Manufacturing Descriptor from TI Based Edgeport - */ - ti_manuf_desc = kmalloc(sizeof(*ti_manuf_desc), GFP_KERNEL); - if (!ti_manuf_desc) + status = check_i2c_image(serial); + if (status) { + dev_dbg(dev, "%s - DOWNLOAD MODE -- BAD I2C\n", __func__); + return status; + } + + /* + * Validate Hardware version number + * Read Manufacturing Descriptor from TI Based Edgeport + */ + ti_manuf_desc = kmalloc(sizeof(*ti_manuf_desc), GFP_KERNEL); + if (!ti_manuf_desc) + return -ENOMEM; + + status = get_manuf_info(serial, (__u8 *)ti_manuf_desc); + if (status) { + kfree(ti_manuf_desc); + return status; + } + + /* Check version number of ION descriptor */ + if (!ignore_cpu_rev && ti_cpu_rev(ti_manuf_desc) < 2) { + dev_dbg(dev, "%s - Wrong CPU Rev %d (Must be 2)\n", + __func__, ti_cpu_rev(ti_manuf_desc)); + kfree(ti_manuf_desc); + return -EINVAL; + } + + rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL); + if (!rom_desc) { + kfree(ti_manuf_desc); + return -ENOMEM; + } + + /* Search for type 2 record (firmware record) */ + start_address = get_descriptor_addr(serial, + I2C_DESC_TYPE_FIRMWARE_BASIC, rom_desc); + if (start_address != 0) { + struct ti_i2c_firmware_rec *firmware_version; + u8 *record; + + dev_dbg(dev, "%s - Found Type FIRMWARE (Type 2) record\n", + __func__); + + firmware_version = kmalloc(sizeof(*firmware_version), + GFP_KERNEL); + if (!firmware_version) { + kfree(rom_desc); + kfree(ti_manuf_desc); return -ENOMEM; + } - status = get_manuf_info(serial, (__u8 *)ti_manuf_desc); + /* + * Validate version number + * Read the descriptor data + */ + status = read_rom(serial, start_address + + sizeof(struct ti_i2c_desc), + sizeof(struct ti_i2c_firmware_rec), + (__u8 *)firmware_version); if (status) { + kfree(firmware_version); + kfree(rom_desc); kfree(ti_manuf_desc); return status; } - /* Check version number of ION descriptor */ - if (!ignore_cpu_rev && ti_cpu_rev(ti_manuf_desc) < 2) { - dev_dbg(dev, "%s - Wrong CPU Rev %d (Must be 2)\n", - __func__, ti_cpu_rev(ti_manuf_desc)); - kfree(ti_manuf_desc); - return -EINVAL; - } + /* + * Check version number of download with current + * version in I2c + */ + download_cur_ver = (firmware_version->Ver_Major << 8) + + (firmware_version->Ver_Minor); + download_new_ver = (fw_hdr->major_version << 8) + + (fw_hdr->minor_version); - rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL); - if (!rom_desc) { - kfree(ti_manuf_desc); - return -ENOMEM; - } + dev_dbg(dev, "%s - >> FW Versions Device %d.%d Driver %d.%d\n", + __func__, firmware_version->Ver_Major, + firmware_version->Ver_Minor, + fw_hdr->major_version, fw_hdr->minor_version); - /* Search for type 2 record (firmware record) */ - start_address = get_descriptor_addr(serial, - I2C_DESC_TYPE_FIRMWARE_BASIC, rom_desc); - if (start_address != 0) { - struct ti_i2c_firmware_rec *firmware_version; - u8 *record; - - dev_dbg(dev, "%s - Found Type FIRMWARE (Type 2) record\n", __func__); + /* + * Check if we have an old version in the I2C and + * update if necessary + */ + if (download_cur_ver < download_new_ver) { + dev_dbg(dev, "%s - Update I2C dld from %d.%d to %d.%d\n", + __func__, + firmware_version->Ver_Major, + firmware_version->Ver_Minor, + fw_hdr->major_version, + fw_hdr->minor_version); - firmware_version = kmalloc(sizeof(*firmware_version), - GFP_KERNEL); - if (!firmware_version) { + record = kmalloc(1, GFP_KERNEL); + if (!record) { + kfree(firmware_version); kfree(rom_desc); kfree(ti_manuf_desc); return -ENOMEM; } + /* + * In order to update the I2C firmware we must + * change the type 2 record to type 0xF2. This + * will force the UMP to come up in Boot Mode. + * Then while in boot mode, the driver will + * download the latest firmware (padded to + * 15.5k) into the UMP ram. Finally when the + * device comes back up in download mode the + * driver will cause the new firmware to be + * copied from the UMP Ram to I2C and the + * firmware will update the record type from + * 0xf2 to 0x02. + */ + *record = I2C_DESC_TYPE_FIRMWARE_BLANK; - /* Validate version number - * Read the descriptor data + /* + * Change the I2C Firmware record type to + * 0xf2 to trigger an update */ - status = read_rom(serial, start_address + - sizeof(struct ti_i2c_desc), - sizeof(struct ti_i2c_firmware_rec), - (__u8 *)firmware_version); + status = write_rom(serial, start_address, + sizeof(*record), record); if (status) { + kfree(record); kfree(firmware_version); kfree(rom_desc); kfree(ti_manuf_desc); return status; } - /* Check version number of download with current - version in I2c */ - download_cur_ver = (firmware_version->Ver_Major << 8) + - (firmware_version->Ver_Minor); - download_new_ver = (fw_hdr->major_version << 8) + - (fw_hdr->minor_version); - - dev_dbg(dev, "%s - >> FW Versions Device %d.%d Driver %d.%d\n", - __func__, firmware_version->Ver_Major, - firmware_version->Ver_Minor, - fw_hdr->major_version, fw_hdr->minor_version); + /* + * verify the write -- must do this in order + * for write to complete before we do the + * hardware reset + */ + status = read_rom(serial, + start_address, + sizeof(*record), + record); + if (status) { + kfree(record); + kfree(firmware_version); + kfree(rom_desc); + kfree(ti_manuf_desc); + return status; + } - /* Check if we have an old version in the I2C and - update if necessary */ - if (download_cur_ver < download_new_ver) { - dev_dbg(dev, "%s - Update I2C dld from %d.%d to %d.%d\n", - __func__, - firmware_version->Ver_Major, - firmware_version->Ver_Minor, - fw_hdr->major_version, - fw_hdr->minor_version); - - record = kmalloc(1, GFP_KERNEL); - if (!record) { - kfree(firmware_version); - kfree(rom_desc); - kfree(ti_manuf_desc); - return -ENOMEM; - } - /* In order to update the I2C firmware we must - * change the type 2 record to type 0xF2. This - * will force the UMP to come up in Boot Mode. - * Then while in boot mode, the driver will - * download the latest firmware (padded to - * 15.5k) into the UMP ram. Finally when the - * device comes back up in download mode the - * driver will cause the new firmware to be - * copied from the UMP Ram to I2C and the - * firmware will update the record type from - * 0xf2 to 0x02. - */ - *record = I2C_DESC_TYPE_FIRMWARE_BLANK; - - /* Change the I2C Firmware record type to - 0xf2 to trigger an update */ - status = write_rom(serial, start_address, - sizeof(*record), record); - if (status) { - kfree(record); - kfree(firmware_version); - kfree(rom_desc); - kfree(ti_manuf_desc); - return status; - } - - /* verify the write -- must do this in order - * for write to complete before we do the - * hardware reset - */ - status = read_rom(serial, - start_address, - sizeof(*record), - record); - if (status) { - kfree(record); - kfree(firmware_version); - kfree(rom_desc); - kfree(ti_manuf_desc); - return status; - } - - if (*record != I2C_DESC_TYPE_FIRMWARE_BLANK) { - dev_err(dev, "%s - error resetting device\n", __func__); - kfree(record); - kfree(firmware_version); - kfree(rom_desc); - kfree(ti_manuf_desc); - return -ENODEV; - } - - dev_dbg(dev, "%s - HARDWARE RESET\n", __func__); - - /* Reset UMP -- Back to BOOT MODE */ - status = ti_vsend_sync(serial->serial->dev, - UMPC_HARDWARE_RESET, - 0, 0, NULL, 0, - TI_VSEND_TIMEOUT_DEFAULT); - - dev_dbg(dev, "%s - HARDWARE RESET return %d\n", __func__, status); - - /* return an error on purpose. */ + if (*record != I2C_DESC_TYPE_FIRMWARE_BLANK) { + dev_err(dev, "%s - error resetting device\n", + __func__); kfree(record); kfree(firmware_version); kfree(rom_desc); kfree(ti_manuf_desc); return -ENODEV; - } else { - /* Same or newer fw version is already loaded */ - serial->fw_version = download_cur_ver; } + + dev_dbg(dev, "%s - HARDWARE RESET\n", __func__); + + /* Reset UMP -- Back to BOOT MODE */ + status = ti_vsend_sync(serial->serial->dev, + UMPC_HARDWARE_RESET, + 0, 0, NULL, 0, + TI_VSEND_TIMEOUT_DEFAULT); + + dev_dbg(dev, "%s - HARDWARE RESET return %d\n", + __func__, status); + + /* return an error on purpose. */ + kfree(record); kfree(firmware_version); + kfree(rom_desc); + kfree(ti_manuf_desc); + return -ENODEV; } - /* Search for type 0xF2 record (firmware blank record) */ - else if ((start_address = get_descriptor_addr(serial, I2C_DESC_TYPE_FIRMWARE_BLANK, rom_desc)) != 0) { + /* Same or newer fw version is already loaded */ + serial->fw_version = download_cur_ver; + kfree(firmware_version); + } + /* Search for type 0xF2 record (firmware blank record) */ + else { + start_address = get_descriptor_addr(serial, + I2C_DESC_TYPE_FIRMWARE_BLANK, rom_desc); + if (start_address != 0) { #define HEADER_SIZE (sizeof(struct ti_i2c_desc) + \ - sizeof(struct ti_i2c_firmware_rec)) + sizeof(struct ti_i2c_firmware_rec)) __u8 *header; __u8 *vheader; @@ -1232,7 +1287,8 @@ static int download_fw(struct edgeport_serial *serial, return -ENOMEM; } - dev_dbg(dev, "%s - Found Type BLANK FIRMWARE (Type F2) record\n", __func__); + dev_dbg(dev, "%s - Found Type BLANK FIRMWARE (Type F2) record\n", + __func__); /* * In order to update the I2C firmware we must change @@ -1245,7 +1301,7 @@ static int download_fw(struct edgeport_serial *serial, * UMP Ram to I2C and the firmware will update the * record type from 0xf2 to 0x02. */ - status = build_i2c_fw_hdr(header, dev, fw); + status = build_i2c_fw_hdr(header, fw); if (status) { kfree(vheader); kfree(header); @@ -1254,8 +1310,10 @@ static int download_fw(struct edgeport_serial *serial, return -EINVAL; } - /* Update I2C with type 0xf2 record with correct - size and checksum */ + /* + * Update I2C with type 0xf2 record with correct + * size and checksum + */ status = write_rom(serial, start_address, HEADER_SIZE, @@ -1268,13 +1326,16 @@ static int download_fw(struct edgeport_serial *serial, return -EINVAL; } - /* verify the write -- must do this in order for - write to complete before we do the hardware reset */ + /* + * verify the write -- must do this in order for + * write to complete before we do the hardware reset + */ status = read_rom(serial, start_address, HEADER_SIZE, vheader); if (status) { - dev_dbg(dev, "%s - can't read header back\n", __func__); + dev_dbg(dev, "%s - can't read header back\n", + __func__); kfree(vheader); kfree(header); kfree(rom_desc); @@ -1282,7 +1343,8 @@ static int download_fw(struct edgeport_serial *serial, return status; } if (memcmp(vheader, header, HEADER_SIZE)) { - dev_dbg(dev, "%s - write download record failed\n", __func__); + dev_dbg(dev, "%s - write download record failed\n", + __func__); kfree(vheader); kfree(header); kfree(rom_desc); @@ -1301,26 +1363,33 @@ static int download_fw(struct edgeport_serial *serial, 0, 0, NULL, 0, TI_VSEND_TIMEOUT_FW_DOWNLOAD); - dev_dbg(dev, "%s - Update complete 0x%x\n", __func__, status); + dev_dbg(dev, "%s - Update complete 0x%x\n", __func__, + status); if (status) { dev_err(dev, "%s - UMPC_COPY_DNLD_TO_I2C failed\n", - __func__); + __func__); kfree(rom_desc); kfree(ti_manuf_desc); return status; } } - - // The device is running the download code - kfree(rom_desc); - kfree(ti_manuf_desc); - return 0; } - /********************************************************************/ - /* Boot Mode */ - /********************************************************************/ + /* The device is running the download code */ + kfree(rom_desc); + kfree(ti_manuf_desc); + return 0; +} + +static int do_boot_mode(struct edgeport_serial *serial, + const struct firmware *fw) +{ + struct device *dev = &serial->serial->interface->dev; + int status = 0; + struct edge_ti_manuf_descriptor *ti_manuf_desc; + struct edgeport_fw_hdr *fw_hdr = (struct edgeport_fw_hdr *)fw->data; + dev_dbg(dev, "%s - RUNNING IN BOOT MODE\n", __func__); /* Configure the TI device so we can use the BULK pipes for download */ @@ -1336,8 +1405,10 @@ static int download_fw(struct edgeport_serial *serial, goto stayinbootmode; } - /* We have an ION device (I2c Must be programmed) - Determine I2C image type */ + /* + * We have an ION device (I2c Must be programmed) + * Determine I2C image type + */ if (i2c_type_bootmode(serial)) goto stayinbootmode; @@ -1349,7 +1420,8 @@ static int download_fw(struct edgeport_serial *serial, __u8 *buffer; int buffer_size; - /* Validate Hardware version number + /* + * Validate Hardware version number * Read Manufacturing Descriptor from TI Based Edgeport */ ti_manuf_desc = kmalloc(sizeof(*ti_manuf_desc), GFP_KERNEL); @@ -1439,7 +1511,6 @@ stayinbootmode: return 0; } - static int ti_do_config(struct edgeport_port *port, int feature, int on) { int port_number = port->port->port_number; @@ -1450,7 +1521,6 @@ static int ti_do_config(struct edgeport_port *port, int feature, int on) on, NULL, 0); } - static int restore_mcr(struct edgeport_port *port, __u8 mcr) { int status = 0; @@ -1556,7 +1626,6 @@ static void handle_new_lsr(struct edgeport_port *edge_port, int lsr_data, icount->frame++; } - static void edge_interrupt_callback(struct urb *urb) { struct edgeport_serial *edge_serial = urb->context; @@ -1616,8 +1685,9 @@ static void edge_interrupt_callback(struct urb *urb) case TIUMP_INTERRUPT_CODE_LSR: lsr = map_line_status(data[1]); if (lsr & UMP_UART_LSR_DATA_MASK) { - /* Save the LSR event for bulk read - completion routine */ + /* + * Save the LSR event for bulk read completion routine + */ dev_dbg(dev, "%s - LSR Event Port %u LSR Status = %02x\n", __func__, port_number, lsr); edge_port->lsr_event = 1; @@ -1925,8 +1995,10 @@ static void edge_close(struct usb_serial_port *port) if (edge_serial == NULL || edge_port == NULL) return; - /* The bulkreadcompletion routine will check - * this flag and dump add read data */ + /* + * The bulkreadcompletion routine will check + * this flag and dump add read data + */ edge_port->close_pending = 1; usb_kill_urb(port->read_urb); @@ -2016,8 +2088,10 @@ static void edge_send(struct usb_serial_port *port, struct tty_struct *tty) } else edge_port->port->icount.tx += count; - /* wakeup any process waiting for writes to complete */ - /* there is now more room in the buffer for new writes */ + /* + * wakeup any process waiting for writes to complete + * there is now more room in the buffer for new writes + */ if (tty) tty_wakeup(tty); } @@ -2089,8 +2163,10 @@ static void edge_throttle(struct tty_struct *tty) } } - /* if we are implementing RTS/CTS, stop reads */ - /* and the Edgeport will clear the RTS line */ + /* + * if we are implementing RTS/CTS, stop reads + * and the Edgeport will clear the RTS line + */ if (C_CRTSCTS(tty)) stop_read(edge_port); @@ -2113,8 +2189,10 @@ static void edge_unthrottle(struct tty_struct *tty) dev_err(&port->dev, "%s - failed to write start character, %d\n", __func__, status); } } - /* if we are implementing RTS/CTS, restart reads */ - /* are the Edgeport will assert the RTS line */ + /* + * if we are implementing RTS/CTS, restart reads + * are the Edgeport will assert the RTS line + */ if (C_CRTSCTS(tty)) { status = restart_read(edge_port); if (status) @@ -2236,8 +2314,10 @@ static void change_port_settings(struct tty_struct *tty, restart_read(edge_port); } - /* if we are implementing XON/XOFF, set the start and stop - character in the device */ + /* + * if we are implementing XON/XOFF, set the start and stop + * character in the device + */ config->cXon = START_CHAR(tty); config->cXoff = STOP_CHAR(tty); @@ -2467,9 +2547,6 @@ static int edge_startup(struct usb_serial *serial) { struct edgeport_serial *edge_serial; int status; - const struct firmware *fw; - const char *fw_name = "edgeport/down3.bin"; - struct device *dev = &serial->interface->dev; u16 product_id; /* create our private serial structure */ @@ -2481,16 +2558,7 @@ static int edge_startup(struct usb_serial *serial) edge_serial->serial = serial; usb_set_serial_data(serial, edge_serial); - status = request_firmware(&fw, fw_name, dev); - if (status) { - dev_err(dev, "Failed to load image \"%s\" err %d\n", - fw_name, status); - kfree(edge_serial); - return status; - } - - status = download_fw(edge_serial, fw); - release_firmware(fw); + status = download_fw(edge_serial); if (status) { kfree(edge_serial); return status; diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 6956c4f..685fef7 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -48,7 +48,6 @@ static int option_probe(struct usb_serial *serial, const struct usb_device_id *id); static int option_attach(struct usb_serial *serial); static void option_release(struct usb_serial *serial); -static int option_send_setup(struct usb_serial_port *port); static void option_instat_callback(struct urb *urb); /* Vendor and product IDs */ @@ -234,8 +233,6 @@ static void option_instat_callback(struct urb *urb); #define QUALCOMM_VENDOR_ID 0x05C6 -#define SIERRA_VENDOR_ID 0x1199 - #define CMOTECH_VENDOR_ID 0x16d8 #define CMOTECH_PRODUCT_6001 0x6001 #define CMOTECH_PRODUCT_CMU_300 0x6002 @@ -611,11 +608,6 @@ static const struct option_blacklist_info telit_le920_blacklist = { .reserved = BIT(1) | BIT(5), }; -static const struct option_blacklist_info sierra_mc73xx_blacklist = { - .sendsetup = BIT(0) | BIT(2), - .reserved = BIT(8) | BIT(10) | BIT(11), -}; - static const struct usb_device_id option_ids[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) }, @@ -1113,10 +1105,6 @@ static const struct usb_device_id option_ids[] = { { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x6613)}, /* Onda H600/ZTE MF330 */ { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x0023)}, /* ONYX 3G device */ { USB_DEVICE(QUALCOMM_VENDOR_ID, 0x9000)}, /* SIMCom SIM5218 */ - { USB_DEVICE_INTERFACE_CLASS(SIERRA_VENDOR_ID, 0x68c0, 0xff), - .driver_info = (kernel_ulong_t)&sierra_mc73xx_blacklist }, /* MC73xx */ - { USB_DEVICE_INTERFACE_CLASS(SIERRA_VENDOR_ID, 0x9041, 0xff), - .driver_info = (kernel_ulong_t)&sierra_mc73xx_blacklist }, /* MC7305/MC7355 */ { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6001) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_CMU_300) }, { USB_DEVICE(CMOTECH_VENDOR_ID, CMOTECH_PRODUCT_6003), @@ -1835,10 +1823,6 @@ static struct usb_serial_driver * const serial_drivers[] = { &option_1port_device, NULL }; -struct option_private { - u8 bInterfaceNumber; -}; - module_usb_serial_driver(serial_drivers, option_ids); static int option_probe(struct usb_serial *serial, @@ -1882,29 +1866,19 @@ static int option_attach(struct usb_serial *serial) struct usb_interface_descriptor *iface_desc; const struct option_blacklist_info *blacklist; struct usb_wwan_intf_private *data; - struct option_private *priv; data = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL); if (!data) return -ENOMEM; - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) { - kfree(data); - return -ENOMEM; - } - /* Retrieve blacklist info stored at probe. */ blacklist = usb_get_serial_data(serial); iface_desc = &serial->interface->cur_altsetting->desc; - priv->bInterfaceNumber = iface_desc->bInterfaceNumber; - data->private = priv; - if (!blacklist || !test_bit(iface_desc->bInterfaceNumber, &blacklist->sendsetup)) { - data->send_setup = option_send_setup; + data->use_send_setup = 1; } spin_lock_init(&data->susp_lock); @@ -1916,9 +1890,7 @@ static int option_attach(struct usb_serial *serial) static void option_release(struct usb_serial *serial) { struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial); - struct option_private *priv = intfdata->private; - kfree(priv); kfree(intfdata); } @@ -1977,40 +1949,6 @@ static void option_instat_callback(struct urb *urb) } } -/** send RTS/DTR state to the port. - * - * This is exactly the same as SET_CONTROL_LINE_STATE from the PSTN - * CDC. -*/ -static int option_send_setup(struct usb_serial_port *port) -{ - struct usb_serial *serial = port->serial; - struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial); - struct option_private *priv = intfdata->private; - struct usb_wwan_port_private *portdata; - int val = 0; - int res; - - portdata = usb_get_serial_port_data(port); - - if (portdata->dtr_state) - val |= 0x01; - if (portdata->rts_state) - val |= 0x02; - - res = usb_autopm_get_interface(serial->interface); - if (res) - return res; - - res = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - 0x22, 0x21, val, priv->bInterfaceNumber, NULL, - 0, USB_CTRL_SET_TIMEOUT); - - usb_autopm_put_interface(serial->interface); - - return res; -} - MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c index ebcec8c..5022fcf 100644 --- a/drivers/usb/serial/qcserial.c +++ b/drivers/usb/serial/qcserial.c @@ -143,9 +143,11 @@ static const struct usb_device_id id_table[] = { {DEVICE_SWI(0x0f3d, 0x68a2)}, /* Sierra Wireless MC7700 */ {DEVICE_SWI(0x114f, 0x68a2)}, /* Sierra Wireless MC7750 */ {DEVICE_SWI(0x1199, 0x68a2)}, /* Sierra Wireless MC7710 */ + {DEVICE_SWI(0x1199, 0x68c0)}, /* Sierra Wireless MC7304/MC7354 */ {DEVICE_SWI(0x1199, 0x901c)}, /* Sierra Wireless EM7700 */ {DEVICE_SWI(0x1199, 0x901f)}, /* Sierra Wireless EM7355 */ {DEVICE_SWI(0x1199, 0x9040)}, /* Sierra Wireless Modem */ + {DEVICE_SWI(0x1199, 0x9041)}, /* Sierra Wireless MC7305/MC7355 */ {DEVICE_SWI(0x1199, 0x9051)}, /* Netgear AirCard 340U */ {DEVICE_SWI(0x1199, 0x9053)}, /* Sierra Wireless Modem */ {DEVICE_SWI(0x1199, 0x9054)}, /* Sierra Wireless Modem */ @@ -153,6 +155,8 @@ static const struct usb_device_id id_table[] = { {DEVICE_SWI(0x1199, 0x9056)}, /* Sierra Wireless Modem */ {DEVICE_SWI(0x1199, 0x9060)}, /* Sierra Wireless Modem */ {DEVICE_SWI(0x1199, 0x9061)}, /* Sierra Wireless Modem */ + {DEVICE_SWI(0x1199, 0x9070)}, /* Sierra Wireless MC74xx/EM74xx */ + {DEVICE_SWI(0x1199, 0x9071)}, /* Sierra Wireless MC74xx/EM74xx */ {DEVICE_SWI(0x413c, 0x81a2)}, /* Dell Wireless 5806 Gobi(TM) 4G LTE Mobile Broadband Card */ {DEVICE_SWI(0x413c, 0x81a3)}, /* Dell Wireless 5570 HSPA+ (42Mbps) Mobile Broadband Card */ {DEVICE_SWI(0x413c, 0x81a4)}, /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */ @@ -175,6 +179,7 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) __u8 nintf; __u8 ifnum; int altsetting = -1; + bool sendsetup = false; nintf = serial->dev->actconfig->desc.bNumInterfaces; dev_dbg(dev, "Num Interfaces = %d\n", nintf); @@ -286,6 +291,7 @@ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id) break; case 3: dev_dbg(dev, "Modem port found\n"); + sendsetup = true; break; default: /* don't claim any unsupported interface */ @@ -337,17 +343,25 @@ done: } } + if (!retval) + usb_set_serial_data(serial, (void *)(unsigned long)sendsetup); + return retval; } static int qc_attach(struct usb_serial *serial) { struct usb_wwan_intf_private *data; + bool sendsetup; data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; + sendsetup = !!(unsigned long)(usb_get_serial_data(serial)); + if (sendsetup) + data->use_send_setup = 1; + spin_lock_init(&data->susp_lock); usb_set_serial_data(serial, data); @@ -374,6 +388,7 @@ static struct usb_serial_driver qcdevice = { .probe = qcprobe, .open = usb_wwan_open, .close = usb_wwan_close, + .dtr_rts = usb_wwan_dtr_rts, .write = usb_wwan_write, .write_room = usb_wwan_write_room, .chars_in_buffer = usb_wwan_chars_in_buffer, diff --git a/drivers/usb/serial/usb-wwan.h b/drivers/usb/serial/usb-wwan.h index f22dff5..44b25c0 100644 --- a/drivers/usb/serial/usb-wwan.h +++ b/drivers/usb/serial/usb-wwan.h @@ -34,9 +34,9 @@ extern int usb_wwan_resume(struct usb_serial *serial); struct usb_wwan_intf_private { spinlock_t susp_lock; unsigned int suspended:1; + unsigned int use_send_setup:1; int in_flight; unsigned int open_ports; - int (*send_setup) (struct usb_serial_port *port); void *private; }; diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c index 825305c..be9cb61 100644 --- a/drivers/usb/serial/usb_wwan.c +++ b/drivers/usb/serial/usb_wwan.c @@ -36,6 +36,40 @@ #include <linux/serial.h> #include "usb-wwan.h" +/* + * Generate DTR/RTS signals on the port using the SET_CONTROL_LINE_STATE request + * in CDC ACM. + */ +static int usb_wwan_send_setup(struct usb_serial_port *port) +{ + struct usb_serial *serial = port->serial; + struct usb_wwan_port_private *portdata; + int val = 0; + int ifnum; + int res; + + portdata = usb_get_serial_port_data(port); + + if (portdata->dtr_state) + val |= 0x01; + if (portdata->rts_state) + val |= 0x02; + + ifnum = serial->interface->cur_altsetting->desc.bInterfaceNumber; + + res = usb_autopm_get_interface(serial->interface); + if (res) + return res; + + res = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + 0x22, 0x21, val, ifnum, NULL, 0, + USB_CTRL_SET_TIMEOUT); + + usb_autopm_put_interface(port->serial->interface); + + return res; +} + void usb_wwan_dtr_rts(struct usb_serial_port *port, int on) { struct usb_wwan_port_private *portdata; @@ -43,7 +77,7 @@ void usb_wwan_dtr_rts(struct usb_serial_port *port, int on) intfdata = usb_get_serial_data(port->serial); - if (!intfdata->send_setup) + if (!intfdata->use_send_setup) return; portdata = usb_get_serial_port_data(port); @@ -51,7 +85,7 @@ void usb_wwan_dtr_rts(struct usb_serial_port *port, int on) portdata->rts_state = on; portdata->dtr_state = on; - intfdata->send_setup(port); + usb_wwan_send_setup(port); } EXPORT_SYMBOL(usb_wwan_dtr_rts); @@ -84,7 +118,7 @@ int usb_wwan_tiocmset(struct tty_struct *tty, portdata = usb_get_serial_port_data(port); intfdata = usb_get_serial_data(port->serial); - if (!intfdata->send_setup) + if (!intfdata->use_send_setup) return -EINVAL; /* FIXME: what locks portdata fields ? */ @@ -97,7 +131,7 @@ int usb_wwan_tiocmset(struct tty_struct *tty, portdata->rts_state = 0; if (clear & TIOCM_DTR) portdata->dtr_state = 0; - return intfdata->send_setup(port); + return usb_wwan_send_setup(port); } EXPORT_SYMBOL(usb_wwan_tiocmset); diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c index 1bac215..39afd70 100644 --- a/drivers/usb/storage/isd200.c +++ b/drivers/usb/storage/isd200.c @@ -1456,30 +1456,26 @@ static void isd200_free_info_ptrs(void *info_) */ static int isd200_init_info(struct us_data *us) { - int retStatus = ISD200_GOOD; struct isd200_info *info; info = kzalloc(sizeof(struct isd200_info), GFP_KERNEL); if (!info) - retStatus = ISD200_ERROR; - else { - info->id = kzalloc(ATA_ID_WORDS * 2, GFP_KERNEL); - info->RegsBuf = kmalloc(sizeof(info->ATARegs), GFP_KERNEL); - info->srb.sense_buffer = - kmalloc(SCSI_SENSE_BUFFERSIZE, GFP_KERNEL); - if (!info->id || !info->RegsBuf || !info->srb.sense_buffer) { - isd200_free_info_ptrs(info); - kfree(info); - retStatus = ISD200_ERROR; - } - } + return ISD200_ERROR; - if (retStatus == ISD200_GOOD) { - us->extra = info; - us->extra_destructor = isd200_free_info_ptrs; + info->id = kzalloc(ATA_ID_WORDS * 2, GFP_KERNEL); + info->RegsBuf = kmalloc(sizeof(info->ATARegs), GFP_KERNEL); + info->srb.sense_buffer = kmalloc(SCSI_SENSE_BUFFERSIZE, GFP_KERNEL); + + if (!info->id || !info->RegsBuf || !info->srb.sense_buffer) { + isd200_free_info_ptrs(info); + kfree(info); + return ISD200_ERROR; } - return retStatus; + us->extra = info; + us->extra_destructor = isd200_free_info_ptrs; + + return ISD200_GOOD; } /************************************************************************** diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index f689219..48ca9c2 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -257,17 +257,16 @@ static void uas_stat_cmplt(struct urb *urb) struct uas_cmd_info *cmdinfo; unsigned long flags; unsigned int idx; + int status = urb->status; spin_lock_irqsave(&devinfo->lock, flags); if (devinfo->resetting) goto out; - if (urb->status) { - if (urb->status != -ENOENT && urb->status != -ECONNRESET) { - dev_err(&urb->dev->dev, "stat urb: status %d\n", - urb->status); - } + if (status) { + if (status != -ENOENT && status != -ECONNRESET && status != -ESHUTDOWN) + dev_err(&urb->dev->dev, "stat urb: status %d\n", status); goto out; } @@ -348,6 +347,7 @@ static void uas_data_cmplt(struct urb *urb) struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata; struct scsi_data_buffer *sdb = NULL; unsigned long flags; + int status = urb->status; spin_lock_irqsave(&devinfo->lock, flags); @@ -374,9 +374,9 @@ static void uas_data_cmplt(struct urb *urb) goto out; } - if (urb->status) { - if (urb->status != -ENOENT && urb->status != -ECONNRESET) - uas_log_cmd_state(cmnd, "data cmplt err", urb->status); + if (status) { + if (status != -ENOENT && status != -ECONNRESET && status != -ESHUTDOWN) + uas_log_cmd_state(cmnd, "data cmplt err", status); /* error: no data transfered */ sdb->resid = sdb->length; } else { diff --git a/drivers/usb/usbip/vhci_hcd.c b/drivers/usb/usbip/vhci_hcd.c index e9ef1ec..7fbe19d 100644 --- a/drivers/usb/usbip/vhci_hcd.c +++ b/drivers/usb/usbip/vhci_hcd.c @@ -218,7 +218,7 @@ static inline void hub_descriptor(struct usb_hub_descriptor *desc) memset(desc, 0, sizeof(*desc)); desc->bDescriptorType = USB_DT_HUB; desc->bDescLength = 9; - desc->wHubCharacteristics = __constant_cpu_to_le16( + desc->wHubCharacteristics = cpu_to_le16( HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_COMMON_OCPM); desc->bNbrPorts = VHCI_NPORTS; desc->u.hs.DeviceRemovable[0] = 0xff; @@ -565,7 +565,9 @@ no_need_xmit: usb_hcd_unlink_urb_from_ep(hcd, urb); no_need_unlink: spin_unlock(&the_controller->lock); - usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb, urb->status); + if (!ret) + usb_hcd_giveback_urb(vhci_to_hcd(the_controller), + urb, urb->status); return ret; } @@ -629,7 +631,7 @@ static int vhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) /* URB was never linked! or will be soon given back by * vhci_rx. */ spin_unlock(&the_controller->lock); - return 0; + return -EIDRM; } { diff --git a/drivers/uwb/drp.c b/drivers/uwb/drp.c index 8fc1b78..38d0504 100644 --- a/drivers/uwb/drp.c +++ b/drivers/uwb/drp.c @@ -642,9 +642,7 @@ static void uwb_drp_handle_alien_drp(struct uwb_rc *rc, struct uwb_ie_drp *drp_i } INIT_LIST_HEAD(&cnflt->rc_node); - init_timer(&cnflt->timer); - cnflt->timer.function = uwb_cnflt_timer; - cnflt->timer.data = (unsigned long)cnflt; + setup_timer(&cnflt->timer, uwb_cnflt_timer, (unsigned long)cnflt); cnflt->rc = rc; INIT_WORK(&cnflt->cnflt_update_work, uwb_cnflt_update_work); diff --git a/drivers/uwb/neh.c b/drivers/uwb/neh.c index 8cb71bb..36b5cb6 100644 --- a/drivers/uwb/neh.c +++ b/drivers/uwb/neh.c @@ -223,9 +223,7 @@ struct uwb_rc_neh *uwb_rc_neh_add(struct uwb_rc *rc, struct uwb_rccb *cmd, kref_init(&neh->kref); INIT_LIST_HEAD(&neh->list_node); - init_timer(&neh->timer); - neh->timer.function = uwb_rc_neh_timer; - neh->timer.data = (unsigned long)neh; + setup_timer(&neh->timer, uwb_rc_neh_timer, (unsigned long)neh); neh->rc = rc; neh->evt_type = expected_type; diff --git a/drivers/uwb/rsv.c b/drivers/uwb/rsv.c index 536ad42..f5e2724 100644 --- a/drivers/uwb/rsv.c +++ b/drivers/uwb/rsv.c @@ -470,9 +470,7 @@ static struct uwb_rsv *uwb_rsv_alloc(struct uwb_rc *rc) INIT_LIST_HEAD(&rsv->rc_node); INIT_LIST_HEAD(&rsv->pal_node); kref_init(&rsv->kref); - init_timer(&rsv->timer); - rsv->timer.function = uwb_rsv_timer; - rsv->timer.data = (unsigned long)rsv; + setup_timer(&rsv->timer, uwb_rsv_timer, (unsigned long)rsv); rsv->rc = rc; INIT_WORK(&rsv->handle_timeout_work, uwb_rsv_handle_timeout_work); @@ -989,9 +987,8 @@ void uwb_rsv_init(struct uwb_rc *rc) rc->bow.can_reserve_extra_mases = true; rc->bow.total_expired = 0; rc->bow.window = UWB_DRP_BACKOFF_WIN_MIN >> 1; - init_timer(&rc->bow.timer); - rc->bow.timer.function = uwb_rsv_backoff_win_timer; - rc->bow.timer.data = (unsigned long)&rc->bow; + setup_timer(&rc->bow.timer, uwb_rsv_backoff_win_timer, + (unsigned long)&rc->bow); bitmap_complement(rc->uwb_dev.streams, rc->uwb_dev.streams, UWB_NUM_STREAMS); } diff --git a/include/linux/platform_data/atmel.h b/include/linux/platform_data/atmel.h index c121ddf..bdc0ee8 100644 --- a/include/linux/platform_data/atmel.h +++ b/include/linux/platform_data/atmel.h @@ -25,15 +25,6 @@ */ #define ATMEL_MAX_UART 7 - /* USB Device */ -struct at91_udc_data { - int vbus_pin; /* high == host powering us */ - u8 vbus_active_low; /* vbus polarity */ - u8 vbus_polled; /* Use polling, not interrupt */ - int pullup_pin; /* active == D+ pulled up */ - u8 pullup_active_low; /* true == pullup_pin is active low */ -}; - /* Compact Flash */ struct at91_cf_data { int irq_pin; /* I/O IRQ */ diff --git a/include/linux/platform_data/s3c-hsotg.h b/include/linux/platform_data/s3c-hsotg.h index 3f1cbf9..3982586 100644 --- a/include/linux/platform_data/s3c-hsotg.h +++ b/include/linux/platform_data/s3c-hsotg.h @@ -17,19 +17,19 @@ struct platform_device; -enum s3c_hsotg_dmamode { +enum dwc2_hsotg_dmamode { S3C_HSOTG_DMA_NONE, /* do not use DMA at-all */ S3C_HSOTG_DMA_ONLY, /* always use DMA */ S3C_HSOTG_DMA_DRV, /* DMA is chosen by driver */ }; /** - * struct s3c_hsotg_plat - platform data for high-speed otg/udc + * struct dwc2_hsotg_plat - platform data for high-speed otg/udc * @dma: Whether to use DMA or not. * @is_osc: The clock source is an oscillator, not a crystal */ -struct s3c_hsotg_plat { - enum s3c_hsotg_dmamode dma; +struct dwc2_hsotg_plat { + enum dwc2_hsotg_dmamode dma; unsigned int is_osc:1; int phy_type; @@ -37,6 +37,6 @@ struct s3c_hsotg_plat { int (*phy_exit)(struct platform_device *pdev, int type); }; -extern void s3c_hsotg_set_platdata(struct s3c_hsotg_plat *pd); +extern void dwc2_hsotg_set_platdata(struct dwc2_hsotg_plat *pd); #endif /* __LINUX_USB_S3C_HSOTG_H */ diff --git a/include/linux/usb.h b/include/linux/usb.h index 447fe29..b9a2807 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -122,6 +122,8 @@ enum usb_interface_condition { * has been deferred. * @needs_binding: flag set when the driver should be re-probed or unbound * following a reset or suspend operation it doesn't support. + * @authorized: This allows to (de)authorize individual interfaces instead + * a whole device in contrast to the device authorization. * @dev: driver model's view of this device * @usb_dev: if an interface is bound to the USB major, this will point * to the sysfs representation for that device. @@ -178,6 +180,7 @@ struct usb_interface { unsigned needs_altsetting0:1; /* switch to altsetting 0 is pending */ unsigned needs_binding:1; /* needs delayed unbind/rebind */ unsigned resetting_device:1; /* true: bandwidth alloc after reset */ + unsigned authorized:1; /* used for interface authorization */ struct device dev; /* interface specific device info */ struct device *usb_dev; @@ -325,6 +328,7 @@ struct usb_host_bos { /* wireless cap descriptor is handled by wusb */ struct usb_ext_cap_descriptor *ext_cap; struct usb_ss_cap_descriptor *ss_cap; + struct usb_ssp_cap_descriptor *ssp_cap; struct usb_ss_container_id_descriptor *ss_id; }; diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h index 27603bc..6cc96bb 100644 --- a/include/linux/usb/ch9.h +++ b/include/linux/usb/ch9.h @@ -32,9 +32,9 @@ #ifndef __LINUX_USB_CH9_H #define __LINUX_USB_CH9_H +#include <linux/device.h> #include <uapi/linux/usb/ch9.h> - /** * usb_speed_string() - Returns human readable-name of the speed. * @speed: The speed to return human-readable name for. If it's not @@ -43,6 +43,15 @@ */ extern const char *usb_speed_string(enum usb_device_speed speed); +/** + * usb_get_maximum_speed - Get maximum requested speed for a given USB + * controller. + * @dev: Pointer to the given USB controller device + * + * The function gets the maximum speed string from property "maximum-speed", + * and returns the corresponding enum usb_device_speed. + */ +extern enum usb_device_speed usb_get_maximum_speed(struct device *dev); /** * usb_state_string - Returns human readable name for the state. diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h index a41833c..5dd75fa 100644 --- a/include/linux/usb/chipidea.h +++ b/include/linux/usb/chipidea.h @@ -5,9 +5,28 @@ #ifndef __LINUX_USB_CHIPIDEA_H #define __LINUX_USB_CHIPIDEA_H +#include <linux/extcon.h> #include <linux/usb/otg.h> struct ci_hdrc; + +/** + * struct ci_hdrc_cable - structure for external connector cable state tracking + * @state: current state of the line + * @changed: set to true when extcon event happen + * @edev: device which generate events + * @ci: driver state of the chipidea device + * @nb: hold event notification callback + * @conn: used for notification registration + */ +struct ci_hdrc_cable { + bool state; + bool changed; + struct extcon_dev *edev; + struct ci_hdrc *ci; + struct notifier_block nb; +}; + struct ci_hdrc_platform_data { const char *name; /* offset of the capability registers */ @@ -48,6 +67,11 @@ struct ci_hdrc_platform_data { u32 ahb_burst_config; u32 tx_burst_size; u32 rx_burst_size; + + /* VBUS and ID signal state tracking, using extcon framework */ + struct ci_hdrc_cable vbus_extcon; + struct ci_hdrc_cable id_extcon; + u32 phy_clkgate_delay_us; }; /* Default offset of capability registers */ diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index c14a69b..3d583a1 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -215,6 +215,7 @@ struct usb_ep { struct list_head ep_list; struct usb_ep_caps caps; bool claimed; + bool enabled; unsigned maxpacket:16; unsigned maxpacket_limit:16; unsigned max_streams:16; @@ -264,7 +265,18 @@ static inline void usb_ep_set_maxpacket_limit(struct usb_ep *ep, */ static inline int usb_ep_enable(struct usb_ep *ep) { - return ep->ops->enable(ep, ep->desc); + int ret; + + if (ep->enabled) + return 0; + + ret = ep->ops->enable(ep, ep->desc); + if (ret) + return ret; + + ep->enabled = true; + + return 0; } /** @@ -281,7 +293,18 @@ static inline int usb_ep_enable(struct usb_ep *ep) */ static inline int usb_ep_disable(struct usb_ep *ep) { - return ep->ops->disable(ep); + int ret; + + if (!ep->enabled) + return 0; + + ret = ep->ops->disable(ep); + if (ret) + return ret; + + ep->enabled = false; + + return 0; } /** @@ -1233,6 +1256,8 @@ extern struct usb_ep *usb_ep_autoconfig_ss(struct usb_gadget *, struct usb_endpoint_descriptor *, struct usb_ss_ep_comp_descriptor *); +extern void usb_ep_autoconfig_release(struct usb_ep *); + extern void usb_ep_autoconfig_reset(struct usb_gadget *); #endif /* __LINUX_USB_GADGET_H */ diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index d2784c1..f89c24b 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -58,12 +58,6 @@ * * Since "struct usb_bus" is so thin, you can't share much code in it. * This framework is a layer over that, and should be more sharable. - * - * @authorized_default: Specifies if new devices are authorized to - * connect by default or they require explicit - * user space authorization; this bit is settable - * through /sys/class/usb_host/X/authorized_default. - * For the rest is RO, so we don't lock to r/w it. */ /*-------------------------------------------------------------------------*/ @@ -120,6 +114,8 @@ struct usb_hcd { #define HCD_FLAG_WAKEUP_PENDING 4 /* root hub is resuming? */ #define HCD_FLAG_RH_RUNNING 5 /* root hub is running? */ #define HCD_FLAG_DEAD 6 /* controller has died? */ +#define HCD_FLAG_INTF_AUTHORIZED 7 /* authorize interfaces? */ +#define HCD_FLAG_DEV_AUTHORIZED 8 /* authorize devices? */ /* The flags can be tested using these macros; they are likely to * be slightly faster than test_bit(). @@ -131,6 +127,22 @@ struct usb_hcd { #define HCD_RH_RUNNING(hcd) ((hcd)->flags & (1U << HCD_FLAG_RH_RUNNING)) #define HCD_DEAD(hcd) ((hcd)->flags & (1U << HCD_FLAG_DEAD)) + /* + * Specifies if interfaces are authorized by default + * or they require explicit user space authorization; this bit is + * settable through /sys/class/usb_host/X/interface_authorized_default + */ +#define HCD_INTF_AUTHORIZED(hcd) \ + ((hcd)->flags & (1U << HCD_FLAG_INTF_AUTHORIZED)) + + /* + * Specifies if devices are authorized by default + * or they require explicit user space authorization; this bit is + * settable through /sys/class/usb_host/X/authorized_default + */ +#define HCD_DEV_AUTHORIZED(hcd) \ + ((hcd)->flags & (1U << HCD_FLAG_DEV_AUTHORIZED)) + /* Flags that get set only during HCD registration or removal. */ unsigned rh_registered:1;/* is root hub registered? */ unsigned rh_pollable:1; /* may we poll the root hub? */ @@ -141,7 +153,6 @@ struct usb_hcd { * support the new root-hub polling mechanism. */ unsigned uses_new_polling:1; unsigned wireless:1; /* Wireless USB HCD */ - unsigned authorized_default:1; unsigned has_tt:1; /* Integrated TT in root hub */ unsigned amd_resume_bug:1; /* AMD remote wakeup quirk */ unsigned can_do_streams:1; /* HC supports streams */ @@ -239,6 +250,7 @@ struct hc_driver { #define HCD_USB2 0x0020 /* USB 2.0 */ #define HCD_USB25 0x0030 /* Wireless USB 1.0 (USB 2.5)*/ #define HCD_USB3 0x0040 /* USB 3.0 */ +#define HCD_USB31 0x0050 /* USB 3.1 */ #define HCD_MASK 0x0070 #define HCD_BH 0x0100 /* URB complete in BH context */ diff --git a/include/linux/usb/musb.h b/include/linux/usb/musb.h index a4ee1b5..fa6dc13 100644 --- a/include/linux/usb/musb.h +++ b/include/linux/usb/musb.h @@ -95,7 +95,7 @@ struct musb_hdrc_config { /* musb CLKIN in Blackfin in MHZ */ unsigned char clkin; #endif - + u32 maximum_speed; }; struct musb_hdrc_platform_data { diff --git a/include/linux/usb/of.h b/include/linux/usb/of.h index 8c5a818..c3fe9e4 100644 --- a/include/linux/usb/of.h +++ b/include/linux/usb/of.h @@ -12,22 +12,10 @@ #include <linux/usb/phy.h> #if IS_ENABLED(CONFIG_OF) -enum usb_dr_mode of_usb_get_dr_mode(struct device_node *np); -enum usb_device_speed of_usb_get_maximum_speed(struct device_node *np); bool of_usb_host_tpl_support(struct device_node *np); int of_usb_update_otg_caps(struct device_node *np, struct usb_otg_caps *otg_caps); #else -static inline enum usb_dr_mode of_usb_get_dr_mode(struct device_node *np) -{ - return USB_DR_MODE_UNKNOWN; -} - -static inline enum usb_device_speed -of_usb_get_maximum_speed(struct device_node *np) -{ - return USB_SPEED_UNKNOWN; -} static inline bool of_usb_host_tpl_support(struct device_node *np) { return false; diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h index bd1dcf8..67929df 100644 --- a/include/linux/usb/otg.h +++ b/include/linux/usb/otg.h @@ -119,4 +119,13 @@ enum usb_dr_mode { USB_DR_MODE_OTG, }; +/** + * usb_get_dr_mode - Get dual role mode for given device + * @dev: Pointer to the given device + * + * The function gets phy interface string from property 'dr_mode', + * and returns the correspondig enum usb_dr_mode + */ +extern enum usb_dr_mode usb_get_dr_mode(struct device *dev); + #endif /* __LINUX_USB_OTG_H */ diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h index e39f251..31a8068 100644 --- a/include/linux/usb/phy.h +++ b/include/linux/usb/phy.h @@ -63,7 +63,7 @@ enum usb_otg_state { struct usb_phy; struct usb_otg; -/* for transceivers connected thru an ULPI interface, the user must +/* for phys connected thru an ULPI interface, the user must * provide access ops */ struct usb_phy_io_ops { @@ -92,10 +92,10 @@ struct usb_phy { u16 port_status; u16 port_change; - /* to support controllers that have multiple transceivers */ + /* to support controllers that have multiple phys */ struct list_head head; - /* initialize/shutdown the OTG controller */ + /* initialize/shutdown the phy */ int (*init)(struct usb_phy *x); void (*shutdown)(struct usb_phy *x); @@ -106,7 +106,7 @@ struct usb_phy { int (*set_power)(struct usb_phy *x, unsigned mA); - /* Set transceiver into suspend mode */ + /* Set phy into suspend mode */ int (*set_suspend)(struct usb_phy *x, int suspend); diff --git a/include/uapi/linux/usb/ch9.h b/include/uapi/linux/usb/ch9.h index f7adc6e..4338eb7 100644 --- a/include/uapi/linux/usb/ch9.h +++ b/include/uapi/linux/usb/ch9.h @@ -866,6 +866,35 @@ struct usb_ss_container_id_descriptor { } __attribute__((packed)); #define USB_DT_USB_SS_CONTN_ID_SIZE 20 + +/* + * SuperSpeed Plus USB Capability descriptor: Defines the set of + * SuperSpeed Plus USB specific device level capabilities + */ +#define USB_SSP_CAP_TYPE 0xa +struct usb_ssp_cap_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bDevCapabilityType; + __u8 bReserved; + __le32 bmAttributes; +#define USB_SSP_SUBLINK_SPEED_ATTRIBS (0x1f << 0) /* sublink speed entries */ +#define USB_SSP_SUBLINK_SPEED_IDS (0xf << 5) /* speed ID entries */ + __u16 wFunctionalitySupport; +#define USB_SSP_MIN_SUBLINK_SPEED_ATTRIBUTE_ID (0xf) +#define USB_SSP_MIN_RX_LANE_COUNT (0xf << 8) +#define USB_SSP_MIN_TX_LANE_COUNT (0xf << 12) + __le16 wReserved; + __le32 bmSublinkSpeedAttr[1]; /* list of sublink speed attrib entries */ +#define USB_SSP_SUBLINK_SPEED_SSID (0xf) /* sublink speed ID */ +#define USB_SSP_SUBLINK_SPEED_LSE (0x3 << 4) /* Lanespeed exponent */ +#define USB_SSP_SUBLINK_SPEED_ST (0x3 << 6) /* Sublink type */ +#define USB_SSP_SUBLINK_SPEED_RSVD (0x3f << 8) /* Reserved */ +#define USB_SSP_SUBLINK_SPEED_LP (0x3 << 14) /* Link protocol */ +#define USB_SSP_SUBLINK_SPEED_LSM (0xff << 16) /* Lanespeed mantissa */ +} __attribute__((packed)); + + /*-------------------------------------------------------------------------*/ /* USB_DT_WIRELESS_ENDPOINT_COMP: companion descriptor associated with diff --git a/tools/usb/testusb.c b/tools/usb/testusb.c index 879f987..0692d99 100644 --- a/tools/usb/testusb.c +++ b/tools/usb/testusb.c @@ -394,7 +394,7 @@ int main (int argc, char **argv) * low speed, interrupt 8 * 1 = 8 */ param.iterations = 1000; - param.length = 512; + param.length = 1024; param.vary = 512; param.sglen = 32; @@ -454,10 +454,10 @@ usage: "\t-t testnum only run specified case\n" "\t-n no test running, show devices to be tested\n" "Case arguments:\n" - "\t-c iterations default 1000\n" - "\t-s packetsize default 512\n" - "\t-g sglen default 32\n" - "\t-v vary default 512\n", + "\t-c iterations default 1000\n" + "\t-s transfer length default 1024\n" + "\t-g sglen default 32\n" + "\t-v vary default 512\n", argv[0]); return 1; } diff --git a/tools/usb/usbip/src/usbip_detach.c b/tools/usb/usbip/src/usbip_detach.c index 05c6d15..9db9d21 100644 --- a/tools/usb/usbip/src/usbip_detach.c +++ b/tools/usb/usbip/src/usbip_detach.c @@ -47,7 +47,9 @@ static int detach_port(char *port) uint8_t portnum; char path[PATH_MAX+1]; - for (unsigned int i = 0; i < strlen(port); i++) + unsigned int port_len = strlen(port); + + for (unsigned int i = 0; i < port_len; i++) if (!isdigit(port[i])) { err("invalid port %s", port); return -1; |