diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-06-26 15:59:26 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-06-26 15:59:26 -0700 |
commit | 2a298679b41199ae742a77ce69766385dffe816f (patch) | |
tree | 93a23c0d828ccca7053f604dbfcdd4d3278972b3 | |
parent | 8c7febe83915332276cab49e89f6580bb963fb9a (diff) | |
parent | 50641056d833813b71b0ad51823f7ded8dd62e7f (diff) | |
download | linux-stable-2a298679b41199ae742a77ce69766385dffe816f.tar.gz linux-stable-2a298679b41199ae742a77ce69766385dffe816f.tar.bz2 linux-stable-2a298679b41199ae742a77ce69766385dffe816f.zip |
Merge tag 'usb-4.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB updates from Greg KH:
"Here's the big USB patchset for 4.2-rc1. As is normal these days, the
majority of changes are in the gadget drivers, with a bunch of other
small driver changes.
All of these have been in linux-next with no reported issues"
* tag 'usb-4.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (175 commits)
usb: dwc3: Use ASCII space in Kconfig
usb: chipidea: add work-around for Marvell HSIC PHY startup
usb: chipidea: allow multiple instances to use default ci_default_pdata
dt-bindings: Consolidate ChipIdea USB ci13xxx bindings
phy: add Marvell HSIC 28nm PHY
phy: Add Marvell USB 2.0 OTG 28nm PHY
dt-bindings: Add Marvell PXA1928 USB and HSIC PHY bindings
USB: ssb: use devm_kzalloc
USB: ssb: fix error handling in ssb_hcd_create_pdev()
usb: isp1760: check for null return from kzalloc
cdc-acm: Add support of ATOL FPrint fiscal printers
usb: chipidea: usbmisc_imx: Remove unneeded semicolon
USB: usbtmc: add device quirk for Rigol DS6104
USB: serial: mos7840: Use setup_timer
phy: twl4030-usb: add ABI documentation
phy: twl4030-usb: remove incorrect pm_runtime_get_sync() in probe function.
phy: twl4030-usb: remove pointless 'suspended' test in 'suspend' callback.
phy: twl4030-usb: make runtime pm more reliable.
drivers:usb:fsl: Fix compilation error for fsl ehci drv
usb: renesas_usbhs: Don't disable the pipe if Control write status stage
...
180 files changed, 5630 insertions, 2330 deletions
diff --git a/Documentation/ABI/testing/sysfs-platform-twl4030-usb b/Documentation/ABI/testing/sysfs-platform-twl4030-usb new file mode 100644 index 000000000000..512c51be64ae --- /dev/null +++ b/Documentation/ABI/testing/sysfs-platform-twl4030-usb @@ -0,0 +1,8 @@ +What: /sys/bus/platform/devices/*twl4030-usb/vbus +Description: + Read-only status reporting if VBUS (approx 5V) + is being supplied by the USB bus. + + Possible values: "on", "off". + + Changes are notified via select/poll. diff --git a/Documentation/devicetree/bindings/phy/brcm,brcmstb-sata-phy.txt b/Documentation/devicetree/bindings/phy/brcm,brcmstb-sata-phy.txt new file mode 100644 index 000000000000..7f81ef90146a --- /dev/null +++ b/Documentation/devicetree/bindings/phy/brcm,brcmstb-sata-phy.txt @@ -0,0 +1,40 @@ +* Broadcom SATA3 PHY for STB + +Required properties: +- compatible: should be one or more of + "brcm,bcm7445-sata-phy" + "brcm,phy-sata3" +- address-cells: should be 1 +- size-cells: should be 0 +- reg: register range for the PHY PCB interface +- reg-names: should be "phy" + +Sub-nodes: + Each port's PHY should be represented as a sub-node. + +Sub-nodes required properties: +- reg: the PHY number +- phy-cells: generic PHY binding; must be 0 +Optional: +- brcm,enable-ssc: use spread spectrum clocking (SSC) on this port + + +Example: + + sata-phy@f0458100 { + compatible = "brcm,bcm7445-sata-phy", "brcm,phy-sata3"; + reg = <0xf0458100 0x1e00>, <0xf045804c 0x10>; + reg-names = "phy"; + #address-cells = <1>; + #size-cells = <0>; + + sata-phy@0 { + reg = <0>; + #phy-cells = <0>; + }; + + sata-phy@1 { + reg = <1>; + #phy-cells = <0>; + }; + }; diff --git a/Documentation/devicetree/bindings/phy/pxa1928-usb-phy.txt b/Documentation/devicetree/bindings/phy/pxa1928-usb-phy.txt new file mode 100644 index 000000000000..660a13ca90b3 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/pxa1928-usb-phy.txt @@ -0,0 +1,18 @@ +* Marvell PXA1928 USB and HSIC PHYs + +Required properties: +- compatible: "marvell,pxa1928-usb-phy" or "marvell,pxa1928-hsic-phy" +- reg: base address and length of the registers +- clocks - A single clock. From common clock binding. +- #phys-cells: should be 0. From commmon phy binding. +- resets: reference to the reset controller + +Example: + + usbphy: phy@7000 { + compatible = "marvell,pxa1928-usb-phy"; + reg = <0x7000 0xe0>; + clocks = <&apmu_clocks PXA1928_CLK_USB>; + #phy-cells = <0>; + }; + diff --git a/Documentation/devicetree/bindings/phy/rcar-gen2-phy.txt b/Documentation/devicetree/bindings/phy/rcar-gen2-phy.txt index 00fc52a034b7..d564ba4f1cf6 100644 --- a/Documentation/devicetree/bindings/phy/rcar-gen2-phy.txt +++ b/Documentation/devicetree/bindings/phy/rcar-gen2-phy.txt @@ -6,6 +6,7 @@ This file provides information on what the device node for the R-Car generation Required properties: - compatible: "renesas,usb-phy-r8a7790" if the device is a part of R8A7790 SoC. "renesas,usb-phy-r8a7791" if the device is a part of R8A7791 SoC. + "renesas,usb-phy-r8a7794" if the device is a part of R8A7794 SoC. - reg: offset and length of the register block. - #address-cells: number of address cells for the USB channel subnodes, must be <1>. diff --git a/Documentation/devicetree/bindings/power/twl-charger.txt b/Documentation/devicetree/bindings/power/twl-charger.txt index d5c706216df5..3b4ea1b73b38 100644 --- a/Documentation/devicetree/bindings/power/twl-charger.txt +++ b/Documentation/devicetree/bindings/power/twl-charger.txt @@ -1,5 +1,15 @@ TWL BCI (Battery Charger Interface) +The battery charger needs to interact with the USB phy in order +to know when charging is permissible, and when there is a connection +or disconnection. + +The choice of phy cannot be configured at a hardware level, so there +is no value in explicit configuration in device-tree. Rather +if there is a sibling of the BCI node which is compatible with +"ti,twl4030-usb", then that is used to determine when and how +use USB power for charging. + Required properties: - compatible: - "ti,twl4030-bci" diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-imx.txt b/Documentation/devicetree/bindings/usb/ci-hdrc-imx.txt deleted file mode 100644 index 38a548001e3a..000000000000 --- a/Documentation/devicetree/bindings/usb/ci-hdrc-imx.txt +++ /dev/null @@ -1,35 +0,0 @@ -* Freescale i.MX ci13xxx usb controllers - -Required properties: -- compatible: Should be "fsl,imx27-usb" -- reg: Should contain registers location and length -- interrupts: Should contain controller interrupt -- fsl,usbphy: phandle of usb phy that connects to the port - -Recommended properies: -- phy_type: the type of the phy connected to the core. Should be one - of "utmi", "utmi_wide", "ulpi", "serial" or "hsic". Without this - property the PORTSC register won't be touched -- dr_mode: One of "host", "peripheral" or "otg". Defaults to "otg" - -Optional properties: -- fsl,usbmisc: phandler of non-core register device, with one argument - that indicate usb controller index -- vbus-supply: regulator for vbus -- disable-over-current: disable over current detect -- external-vbus-divider: enables off-chip resistor divider for Vbus -- maximum-speed: limit the maximum connection speed to "full-speed". -- tpl-support: TPL (Targeted Peripheral List) feature for targeted hosts - -Examples: -usb@02184000 { /* USB OTG */ - compatible = "fsl,imx6q-usb", "fsl,imx27-usb"; - reg = <0x02184000 0x200>; - interrupts = <0 43 0x04>; - fsl,usbphy = <&usbphy1>; - fsl,usbmisc = <&usbmisc 0>; - disable-over-current; - external-vbus-divider; - maximum-speed = "full-speed"; - tpl-support; -}; diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-qcom.txt b/Documentation/devicetree/bindings/usb/ci-hdrc-qcom.txt deleted file mode 100644 index f2899b550939..000000000000 --- a/Documentation/devicetree/bindings/usb/ci-hdrc-qcom.txt +++ /dev/null @@ -1,17 +0,0 @@ -Qualcomm CI13xxx (Chipidea) USB controllers - -Required properties: -- compatible: should contain "qcom,ci-hdrc" -- reg: offset and length of the register set in the memory map -- interrupts: interrupt-specifier for the controller interrupt. -- usb-phy: phandle for the PHY device -- dr_mode: Should be "peripheral" - -Examples: - gadget@f9a55000 { - compatible = "qcom,ci-hdrc"; - reg = <0xf9a55000 0x400>; - dr_mode = "peripheral"; - interrupts = <0 134 0>; - usb-phy = <&usbphy0>; - }; diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt index 27f8b1e5ee46..553e2fae3a76 100644 --- a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt +++ b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt @@ -1,15 +1,35 @@ * USB2 ChipIdea USB controller for ci13xxx Required properties: -- compatible: should be "chipidea,usb2" +- compatible: should be one of: + "fsl,imx27-usb" + "lsi,zevio-usb" + "qcom,ci-hdrc" + "chipidea,usb2" - reg: base address and length of the registers - interrupts: interrupt for the USB controller +Recommended properies: +- phy_type: the type of the phy connected to the core. Should be one + of "utmi", "utmi_wide", "ulpi", "serial" or "hsic". Without this + property the PORTSC register won't be touched. +- dr_mode: One of "host", "peripheral" or "otg". Defaults to "otg" + +Deprecated properties: +- usb-phy: phandle for the PHY device. Use "phys" instead. +- fsl,usbphy: phandle of usb phy that connects to the port. Use "phys" instead. + Optional properties: - clocks: reference to the USB clock - phys: reference to the USB PHY - phy-names: should be "usb-phy" - 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 Example: diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-zevio.txt b/Documentation/devicetree/bindings/usb/ci-hdrc-zevio.txt deleted file mode 100644 index abbcb2aea38c..000000000000 --- a/Documentation/devicetree/bindings/usb/ci-hdrc-zevio.txt +++ /dev/null @@ -1,17 +0,0 @@ -* LSI Zevio USB OTG Controller - -Required properties: -- compatible: Should be "lsi,zevio-usb" -- reg: Should contain registers location and length -- interrupts: Should contain controller interrupt - -Optional properties: -- vbus-supply: regulator for vbus - -Examples: - usb0: usb@b0000000 { - reg = <0xb0000000 0x1000>; - compatible = "lsi,zevio-usb"; - interrupts = <8>; - vbus-supply = <&vbus_reg>; - }; diff --git a/Documentation/devicetree/bindings/usb/dwc3-st.txt b/Documentation/devicetree/bindings/usb/dwc3-st.txt index f9d70252bbb2..01c71b1258f4 100644 --- a/Documentation/devicetree/bindings/usb/dwc3-st.txt +++ b/Documentation/devicetree/bindings/usb/dwc3-st.txt @@ -49,8 +49,7 @@ st_dwc3: dwc3@8f94000 { st,syscfg = <&syscfg_core>; resets = <&powerdown STIH407_USB3_POWERDOWN>, <&softreset STIH407_MIPHY2_SOFTRESET>; - reset-names = "powerdown", - "softreset"; + reset-names = "powerdown", "softreset"; #address-cells = <1>; #size-cells = <1>; pinctrl-names = "default"; @@ -62,7 +61,7 @@ st_dwc3: dwc3@8f94000 { reg = <0x09900000 0x100000>; interrupts = <GIC_SPI 155 IRQ_TYPE_NONE>; dr_mode = "host"; - phys-names = "usb2-phy", "usb3-phy"; - phys = <&usb2_picophy2>, <&phy_port2 MIPHY_TYPE_USB>; + phy-names = "usb2-phy", "usb3-phy"; + phys = <&usb2_picophy2>, <&phy_port2 PHY_TYPE_USB3>; }; }; diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt index 5cc364309edb..0815eac5b185 100644 --- a/Documentation/devicetree/bindings/usb/dwc3.txt +++ b/Documentation/devicetree/bindings/usb/dwc3.txt @@ -38,6 +38,8 @@ Optional properties: - 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. This is usually a subnode to DWC3 glue to which it is connected. diff --git a/Documentation/devicetree/bindings/usb/msm-hsusb.txt b/Documentation/devicetree/bindings/usb/msm-hsusb.txt index 2826f2af503a..bd8d9e753029 100644 --- a/Documentation/devicetree/bindings/usb/msm-hsusb.txt +++ b/Documentation/devicetree/bindings/usb/msm-hsusb.txt @@ -69,6 +69,17 @@ Optional properties: (no, min, max) where each value represents either a voltage in microvolts or a value corresponding to voltage corner. +- qcom,manual-pullup: If present, vbus is not routed to USB controller/phy + and controller driver therefore enables pull-up explicitly + before starting controller using usbcmd run/stop bit. + +- 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. + Example HSUSB OTG controller device node: usb@f9a55000 { diff --git a/Documentation/devicetree/bindings/usb/renesas_usbhs.txt b/Documentation/devicetree/bindings/usb/renesas_usbhs.txt index ddbe304beb21..64a4ca6cf96f 100644 --- a/Documentation/devicetree/bindings/usb/renesas_usbhs.txt +++ b/Documentation/devicetree/bindings/usb/renesas_usbhs.txt @@ -4,6 +4,7 @@ Required properties: - compatible: Must contain one of the following: - "renesas,usbhs-r8a7790" - "renesas,usbhs-r8a7791" + - "renesas,usbhs-r8a7794" - reg: Base address and length of the register for the USBHS - interrupts: Interrupt specifier for the USBHS - clocks: A list of phandle + clock specifier pairs diff --git a/Documentation/devicetree/bindings/usb/twlxxxx-usb.txt b/Documentation/devicetree/bindings/usb/twlxxxx-usb.txt index 0aee0ad3f035..17327a296110 100644 --- a/Documentation/devicetree/bindings/usb/twlxxxx-usb.txt +++ b/Documentation/devicetree/bindings/usb/twlxxxx-usb.txt @@ -30,6 +30,9 @@ TWL4030 USB PHY AND COMPARATOR - usb_mode : The mode used by the phy to connect to the controller. "1" specifies "ULPI" mode and "2" specifies "CEA2011_3PIN" mode. +If a sibling node is compatible "ti,twl4030-bci", then it will find +this device and query it for USB power status. + twl4030-usb { compatible = "ti,twl4030-usb"; interrupts = < 10 4 >; diff --git a/Documentation/devicetree/bindings/usb/usb-ehci.txt b/Documentation/devicetree/bindings/usb/usb-ehci.txt index 0b04fdff9d5a..a12d6012a40f 100644 --- a/Documentation/devicetree/bindings/usb/usb-ehci.txt +++ b/Documentation/devicetree/bindings/usb/usb-ehci.txt @@ -13,6 +13,8 @@ Optional properties: - big-endian-desc : boolean, set this for hcds with big-endian descriptors - big-endian : boolean, for hcds with big-endian-regs + big-endian-desc - needs-reset-on-resume : boolean, set this to force EHCI reset after resume + - has-transaction-translator : boolean, set this if EHCI have a Transaction + Translator built into the root hub. - clocks : a list of phandle + clock specifier pairs - phys : phandle + phy specifier pair - phy-names : "usb" diff --git a/Documentation/phy.txt b/Documentation/phy.txt index 371361c69a4b..b388c5af9e72 100644 --- a/Documentation/phy.txt +++ b/Documentation/phy.txt @@ -76,6 +76,8 @@ struct phy *phy_get(struct device *dev, const char *string); struct phy *phy_optional_get(struct device *dev, const char *string); struct phy *devm_phy_get(struct device *dev, const char *string); struct phy *devm_phy_optional_get(struct device *dev, const char *string); +struct phy *devm_of_phy_get_by_index(struct device *dev, struct device_node *np, + int index); phy_get, phy_optional_get, devm_phy_get and devm_phy_optional_get can be used to get the PHY. In the case of dt boot, the string arguments @@ -86,7 +88,10 @@ successful PHY get. On driver detach, release function is invoked on the the devres data and devres data is freed. phy_optional_get and devm_phy_optional_get should be used when the phy is optional. These two functions will never return -ENODEV, but instead returns NULL when -the phy cannot be found. +the phy cannot be found.Some generic drivers, such as ehci, may use multiple +phys and for such drivers referencing phy(s) by name(s) does not make sense. In +this case, devm_of_phy_get_by_index can be used to get a phy reference based on +the index. It should be noted that NULL is a valid phy reference. All phy consumer calls on the NULL phy become NOPs. That is the release calls, diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt index f45b2bf4b41d..592678009c15 100644 --- a/Documentation/usb/gadget-testing.txt +++ b/Documentation/usb/gadget-testing.txt @@ -526,8 +526,6 @@ Except for ifname they can be written to until the function is linked to a configuration. The ifname is read-only and contains the name of the interface which was assigned by the net core, e. g. usb0. -By default there can be only 1 RNDIS interface in the system. - Testing the RNDIS function -------------------------- @@ -629,7 +627,7 @@ Function-specific configfs interface The function name to use when creating the function directory is "uac2". The uac2 function provides these attributes in its function directory: - chmask - capture channel mask + c_chmask - capture channel mask c_srate - capture sampling rate c_ssize - capture sample size (bytes) p_chmask - playback channel mask diff --git a/MAINTAINERS b/MAINTAINERS index df6007824715..68457d869b61 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10712,6 +10712,13 @@ S: Maintained F: Documentation/video4linux/zr364xx.txt F: drivers/media/usb/zr364xx/ +ULPI BUS +M: Heikki Krogerus <heikki.krogerus@linux.intel.com> +L: linux-usb@vger.kernel.org +S: Maintained +F: drivers/usb/common/ulpi.c +F: include/linux/ulpi/ + USER-MODE LINUX (UML) M: Jeff Dike <jdike@addtoit.com> M: Richard Weinberger <richard@nod.at> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index fc9b9f0ea91e..487d057431cc 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -54,6 +54,26 @@ config PHY_EXYNOS_MIPI_VIDEO Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P and EXYNOS SoCs. +config PHY_PXA_28NM_HSIC + tristate "Marvell USB HSIC 28nm PHY Driver" + select GENERIC_PHY + help + Enable this to support Marvell USB HSIC PHY driver for Marvell + SoC. This driver will do the PHY initialization and shutdown. + The PHY driver will be used by Marvell ehci driver. + + To compile this driver as a module, choose M here. + +config PHY_PXA_28NM_USB2 + tristate "Marvell USB 2.0 28nm PHY Driver" + select GENERIC_PHY + help + Enable this to support Marvell USB 2.0 PHY driver for Marvell + SoC. This driver will do the PHY initialization and shutdown. + The PHY driver will be used by Marvell udc/ehci/otg driver. + + To compile this driver as a module, choose M here. + config PHY_MVEBU_SATA def_bool y depends on ARCH_DOVE || MACH_DOVE || MACH_KIRKWOOD @@ -313,4 +333,20 @@ config PHY_QCOM_UFS help Support for UFS PHY on QCOM chipsets. +config PHY_TUSB1210 + tristate "TI TUSB1210 ULPI PHY module" + depends on USB_ULPI_BUS + select GENERIC_PHY + help + Support for TI TUSB1210 USB ULPI PHY. + +config PHY_BRCMSTB_SATA + tristate "Broadcom STB SATA PHY driver" + depends on ARCH_BRCMSTB + depends on OF + select GENERIC_PHY + help + Enable this to support the SATA3 PHY on 28nm Broadcom STB SoCs. + Likely useful only with CONFIG_SATA_BRCMSTB enabled. + endmenu diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index f12625178780..42f58e95aff0 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -10,6 +10,8 @@ obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o obj-$(CONFIG_BCM_KONA_USB2_PHY) += phy-bcm-kona-usb2.o obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o +obj-$(CONFIG_PHY_PXA_28NM_USB2) += phy-pxa-28nm-usb2.o +obj-$(CONFIG_PHY_PXA_28NM_HSIC) += phy-pxa-28nm-hsic.o obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o obj-$(CONFIG_PHY_MIPHY28LP) += phy-miphy28lp.o obj-$(CONFIG_PHY_MIPHY365X) += phy-miphy365x.o @@ -40,3 +42,5 @@ obj-$(CONFIG_PHY_STIH41X_USB) += phy-stih41x-usb.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs-qmp-20nm.o 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 diff --git a/drivers/phy/phy-brcmstb-sata.c b/drivers/phy/phy-brcmstb-sata.c new file mode 100644 index 000000000000..b7e303d28caf --- /dev/null +++ b/drivers/phy/phy-brcmstb-sata.c @@ -0,0 +1,216 @@ +/* + * Broadcom SATA3 AHCI Controller PHY Driver + * + * Copyright © 2009-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; either version 2, 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. + */ + +#include <linux/device.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> + +#define SATA_MDIO_BANK_OFFSET 0x23c +#define SATA_MDIO_REG_OFFSET(ofs) ((ofs) * 4) +#define SATA_MDIO_REG_SPACE_SIZE 0x1000 +#define SATA_MDIO_REG_LENGTH 0x1f00 + +#define MAX_PORTS 2 + +/* Register offset between PHYs in PCB space */ +#define SATA_MDIO_REG_SPACE_SIZE 0x1000 + +struct brcm_sata_port { + int portnum; + struct phy *phy; + struct brcm_sata_phy *phy_priv; + bool ssc_en; +}; + +struct brcm_sata_phy { + struct device *dev; + void __iomem *phy_base; + + struct brcm_sata_port phys[MAX_PORTS]; +}; + +enum sata_mdio_phy_regs_28nm { + PLL_REG_BANK_0 = 0x50, + PLL_REG_BANK_0_PLLCONTROL_0 = 0x81, + + TXPMD_REG_BANK = 0x1a0, + TXPMD_CONTROL1 = 0x81, + TXPMD_CONTROL1_TX_SSC_EN_FRC = BIT(0), + TXPMD_CONTROL1_TX_SSC_EN_FRC_VAL = BIT(1), + TXPMD_TX_FREQ_CTRL_CONTROL1 = 0x82, + TXPMD_TX_FREQ_CTRL_CONTROL2 = 0x83, + TXPMD_TX_FREQ_CTRL_CONTROL2_FMIN_MASK = 0x3ff, + TXPMD_TX_FREQ_CTRL_CONTROL3 = 0x84, + TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK = 0x3ff, +}; + +static inline void __iomem *brcm_sata_phy_base(struct brcm_sata_port *port) +{ + struct brcm_sata_phy *priv = port->phy_priv; + + return priv->phy_base + (port->portnum * SATA_MDIO_REG_SPACE_SIZE); +} + +static void brcm_sata_mdio_wr(void __iomem *addr, u32 bank, u32 ofs, + u32 msk, u32 value) +{ + u32 tmp; + + writel(bank, addr + SATA_MDIO_BANK_OFFSET); + tmp = readl(addr + SATA_MDIO_REG_OFFSET(ofs)); + tmp = (tmp & msk) | value; + writel(tmp, addr + SATA_MDIO_REG_OFFSET(ofs)); +} + +/* These defaults were characterized by H/W group */ +#define FMIN_VAL_DEFAULT 0x3df +#define FMAX_VAL_DEFAULT 0x3df +#define FMAX_VAL_SSC 0x83 + +static void brcm_sata_cfg_ssc_28nm(struct brcm_sata_port *port) +{ + void __iomem *base = brcm_sata_phy_base(port); + struct brcm_sata_phy *priv = port->phy_priv; + u32 tmp; + + /* override the TX spread spectrum setting */ + tmp = TXPMD_CONTROL1_TX_SSC_EN_FRC_VAL | TXPMD_CONTROL1_TX_SSC_EN_FRC; + brcm_sata_mdio_wr(base, TXPMD_REG_BANK, TXPMD_CONTROL1, ~tmp, tmp); + + /* set fixed min freq */ + brcm_sata_mdio_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL2, + ~TXPMD_TX_FREQ_CTRL_CONTROL2_FMIN_MASK, + FMIN_VAL_DEFAULT); + + /* set fixed max freq depending on SSC config */ + if (port->ssc_en) { + dev_info(priv->dev, "enabling SSC on port %d\n", port->portnum); + tmp = FMAX_VAL_SSC; + } else { + tmp = FMAX_VAL_DEFAULT; + } + + brcm_sata_mdio_wr(base, TXPMD_REG_BANK, TXPMD_TX_FREQ_CTRL_CONTROL3, + ~TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK, tmp); +} + +static int brcm_sata_phy_init(struct phy *phy) +{ + struct brcm_sata_port *port = phy_get_drvdata(phy); + + brcm_sata_cfg_ssc_28nm(port); + + return 0; +} + +static struct phy_ops phy_ops_28nm = { + .init = brcm_sata_phy_init, + .owner = THIS_MODULE, +}; + +static const struct of_device_id brcm_sata_phy_of_match[] = { + { .compatible = "brcm,bcm7445-sata-phy" }, + {}, +}; +MODULE_DEVICE_TABLE(of, brcm_sata_phy_of_match); + +static int brcm_sata_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *dn = dev->of_node, *child; + struct brcm_sata_phy *priv; + struct resource *res; + struct phy_provider *provider; + int count = 0; + + if (of_get_child_count(dn) == 0) + return -ENODEV; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + dev_set_drvdata(dev, priv); + priv->dev = dev; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); + priv->phy_base = devm_ioremap_resource(dev, res); + if (IS_ERR(priv->phy_base)) + return PTR_ERR(priv->phy_base); + + for_each_available_child_of_node(dn, child) { + unsigned int id; + struct brcm_sata_port *port; + + if (of_property_read_u32(child, "reg", &id)) { + dev_err(dev, "missing reg property in node %s\n", + child->name); + return -EINVAL; + } + + if (id >= MAX_PORTS) { + dev_err(dev, "invalid reg: %u\n", id); + return -EINVAL; + } + if (priv->phys[id].phy) { + dev_err(dev, "already registered port %u\n", id); + return -EINVAL; + } + + port = &priv->phys[id]; + port->portnum = id; + port->phy_priv = priv; + port->phy = devm_phy_create(dev, child, &phy_ops_28nm); + port->ssc_en = of_property_read_bool(child, "brcm,enable-ssc"); + if (IS_ERR(port->phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(port->phy); + } + + phy_set_drvdata(port->phy, port); + count++; + } + + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(provider)) { + dev_err(dev, "could not register PHY provider\n"); + return PTR_ERR(provider); + } + + dev_info(dev, "registered %d port(s)\n", count); + + return 0; +} + +static struct platform_driver brcm_sata_phy_driver = { + .probe = brcm_sata_phy_probe, + .driver = { + .of_match_table = brcm_sata_phy_of_match, + .name = "brcmstb-sata-phy", + } +}; +module_platform_driver(brcm_sata_phy_driver); + +MODULE_DESCRIPTION("Broadcom STB SATA PHY driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Marc Carino"); +MODULE_AUTHOR("Brian Norris"); +MODULE_ALIAS("platform:phy-brcmstb-sata"); diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index 63bc12d7a73e..fc48fac003a6 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -367,13 +367,21 @@ static struct phy *_of_phy_get(struct device_node *np, int index) phy_provider = of_phy_provider_lookup(args.np); if (IS_ERR(phy_provider) || !try_module_get(phy_provider->owner)) { phy = ERR_PTR(-EPROBE_DEFER); - goto err0; + goto out_unlock; + } + + if (!of_device_is_available(args.np)) { + dev_warn(phy_provider->dev, "Requested PHY is disabled\n"); + phy = ERR_PTR(-ENODEV); + goto out_put_module; } phy = phy_provider->of_xlate(phy_provider->dev, &args); + +out_put_module: module_put(phy_provider->owner); -err0: +out_unlock: mutex_unlock(&phy_provider_mutex); of_node_put(args.np); @@ -623,6 +631,38 @@ struct phy *devm_of_phy_get(struct device *dev, struct device_node *np, EXPORT_SYMBOL_GPL(devm_of_phy_get); /** + * devm_of_phy_get_by_index() - lookup and obtain a reference to a phy by index. + * @dev: device that requests this phy + * @np: node containing the phy + * @index: index of the phy + * + * Gets the phy using _of_phy_get(), and associates a device with it using + * devres. On driver detach, release function is invoked on the devres data, + * then, devres data is freed. + * + */ +struct phy *devm_of_phy_get_by_index(struct device *dev, struct device_node *np, + int index) +{ + struct phy **ptr, *phy; + + ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + phy = _of_phy_get(np, index); + if (!IS_ERR(phy)) { + *ptr = phy; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return phy; +} +EXPORT_SYMBOL_GPL(devm_of_phy_get_by_index); + +/** * phy_create() - create a new phy * @dev: device that is creating the new phy * @node: device node of the phy @@ -651,16 +691,6 @@ struct phy *phy_create(struct device *dev, struct device_node *node, goto free_phy; } - /* phy-supply */ - phy->pwr = regulator_get_optional(dev, "phy"); - if (IS_ERR(phy->pwr)) { - if (PTR_ERR(phy->pwr) == -EPROBE_DEFER) { - ret = -EPROBE_DEFER; - goto free_ida; - } - phy->pwr = NULL; - } - device_initialize(&phy->dev); mutex_init(&phy->mutex); @@ -674,6 +704,16 @@ struct phy *phy_create(struct device *dev, struct device_node *node, if (ret) goto put_dev; + /* phy-supply */ + phy->pwr = regulator_get_optional(&phy->dev, "phy"); + if (IS_ERR(phy->pwr)) { + ret = PTR_ERR(phy->pwr); + if (ret == -EPROBE_DEFER) + goto put_dev; + + phy->pwr = NULL; + } + ret = device_add(&phy->dev); if (ret) goto put_dev; @@ -689,9 +729,6 @@ put_dev: put_device(&phy->dev); /* calls phy_release() which frees resources */ return ERR_PTR(ret); -free_ida: - ida_simple_remove(&phy_ida, phy->id); - free_phy: kfree(phy); return ERR_PTR(ret); diff --git a/drivers/phy/phy-miphy28lp.c b/drivers/phy/phy-miphy28lp.c index c4cc11dcb2a2..5e257ef7ac05 100644 --- a/drivers/phy/phy-miphy28lp.c +++ b/drivers/phy/phy-miphy28lp.c @@ -367,7 +367,7 @@ static struct miphy28lp_pll_gen pcie_pll_gen[] = { static inline void miphy28lp_set_reset(struct miphy28lp_phy *miphy_phy) { - void *base = miphy_phy->base; + void __iomem *base = miphy_phy->base; u8 val; /* Putting Macro in reset */ @@ -391,7 +391,7 @@ static inline void miphy28lp_set_reset(struct miphy28lp_phy *miphy_phy) static inline void miphy28lp_pll_calibration(struct miphy28lp_phy *miphy_phy, struct pll_ratio *pll_ratio) { - void *base = miphy_phy->base; + void __iomem *base = miphy_phy->base; u8 val; /* Applying PLL Settings */ @@ -1107,11 +1107,6 @@ static struct phy *miphy28lp_xlate(struct device *dev, struct device_node *phynode = args->np; int ret, index = 0; - if (!of_device_is_available(phynode)) { - dev_warn(dev, "Requested PHY is disabled\n"); - return ERR_PTR(-ENODEV); - } - if (args->args_count != 1) { dev_err(dev, "Invalid number of cells in 'phy' property\n"); return ERR_PTR(-EINVAL); diff --git a/drivers/phy/phy-miphy365x.c b/drivers/phy/phy-miphy365x.c index 019c2d75344e..0ff354d6e183 100644 --- a/drivers/phy/phy-miphy365x.c +++ b/drivers/phy/phy-miphy365x.c @@ -441,8 +441,8 @@ static int miphy365x_init(struct phy *phy) return ret; } -int miphy365x_get_addr(struct device *dev, struct miphy365x_phy *miphy_phy, - int index) +static int miphy365x_get_addr(struct device *dev, + struct miphy365x_phy *miphy_phy, int index) { struct device_node *phynode = miphy_phy->phy->dev.of_node; const char *name; @@ -476,11 +476,6 @@ static struct phy *miphy365x_xlate(struct device *dev, struct device_node *phynode = args->np; int ret, index; - if (!of_device_is_available(phynode)) { - dev_warn(dev, "Requested PHY is disabled\n"); - return ERR_PTR(-ENODEV); - } - if (args->args_count != 1) { dev_err(dev, "Invalid number of cells in 'phy' property\n"); return ERR_PTR(-EINVAL); diff --git a/drivers/phy/phy-pxa-28nm-hsic.c b/drivers/phy/phy-pxa-28nm-hsic.c new file mode 100644 index 000000000000..234aacf4db20 --- /dev/null +++ b/drivers/phy/phy-pxa-28nm-hsic.c @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2015 Linaro, Ltd. + * Rob Herring <robh@kernel.org> + * + * Based on vendor driver: + * Copyright (C) 2013 Marvell Inc. + * Author: Chao Xie <xiechao.mail@gmail.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 <linux/delay.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/phy/phy.h> + +#define PHY_28NM_HSIC_CTRL 0x08 +#define PHY_28NM_HSIC_IMPCAL_CAL 0x18 +#define PHY_28NM_HSIC_PLL_CTRL01 0x1c +#define PHY_28NM_HSIC_PLL_CTRL2 0x20 +#define PHY_28NM_HSIC_INT 0x28 + +#define PHY_28NM_HSIC_PLL_SELLPFR_SHIFT 26 +#define PHY_28NM_HSIC_PLL_FBDIV_SHIFT 0 +#define PHY_28NM_HSIC_PLL_REFDIV_SHIFT 9 + +#define PHY_28NM_HSIC_S2H_PU_PLL BIT(10) +#define PHY_28NM_HSIC_H2S_PLL_LOCK BIT(15) +#define PHY_28NM_HSIC_S2H_HSIC_EN BIT(7) +#define S2H_DRV_SE0_4RESUME BIT(14) +#define PHY_28NM_HSIC_H2S_IMPCAL_DONE BIT(27) + +#define PHY_28NM_HSIC_CONNECT_INT BIT(1) +#define PHY_28NM_HSIC_HS_READY_INT BIT(2) + +struct mv_hsic_phy { + struct phy *phy; + struct platform_device *pdev; + void __iomem *base; + struct clk *clk; +}; + +static bool wait_for_reg(void __iomem *reg, u32 mask, unsigned long timeout) +{ + timeout += jiffies; + while (time_is_after_eq_jiffies(timeout)) { + if ((readl(reg) & mask) == mask) + return true; + msleep(1); + } + return false; +} + +static int mv_hsic_phy_init(struct phy *phy) +{ + struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); + struct platform_device *pdev = mv_phy->pdev; + void __iomem *base = mv_phy->base; + + clk_prepare_enable(mv_phy->clk); + + /* Set reference clock */ + writel(0x1 << PHY_28NM_HSIC_PLL_SELLPFR_SHIFT | + 0xf0 << PHY_28NM_HSIC_PLL_FBDIV_SHIFT | + 0xd << PHY_28NM_HSIC_PLL_REFDIV_SHIFT, + base + PHY_28NM_HSIC_PLL_CTRL01); + + /* Turn on PLL */ + writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) | + PHY_28NM_HSIC_S2H_PU_PLL, + base + PHY_28NM_HSIC_PLL_CTRL2); + + /* Make sure PHY PLL is locked */ + if (!wait_for_reg(base + PHY_28NM_HSIC_PLL_CTRL2, + PHY_28NM_HSIC_H2S_PLL_LOCK, HZ / 10)) { + dev_err(&pdev->dev, "HSIC PHY PLL not locked after 100mS."); + clk_disable_unprepare(mv_phy->clk); + return -ETIMEDOUT; + } + + return 0; +} + +static int mv_hsic_phy_power_on(struct phy *phy) +{ + struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); + struct platform_device *pdev = mv_phy->pdev; + void __iomem *base = mv_phy->base; + u32 reg; + + reg = readl(base + PHY_28NM_HSIC_CTRL); + /* Avoid SE0 state when resume for some device will take it as reset */ + reg &= ~S2H_DRV_SE0_4RESUME; + reg |= PHY_28NM_HSIC_S2H_HSIC_EN; /* Enable HSIC PHY */ + writel(reg, base + PHY_28NM_HSIC_CTRL); + + /* + * Calibration Timing + * ____________________________ + * CAL START ___| + * ____________________ + * CAL_DONE ___________| + * | 400us | + */ + + /* Make sure PHY Calibration is ready */ + if (!wait_for_reg(base + PHY_28NM_HSIC_IMPCAL_CAL, + PHY_28NM_HSIC_H2S_IMPCAL_DONE, HZ / 10)) { + dev_warn(&pdev->dev, "HSIC PHY READY not set after 100mS."); + return -ETIMEDOUT; + } + + /* Waiting for HSIC connect int*/ + if (!wait_for_reg(base + PHY_28NM_HSIC_INT, + PHY_28NM_HSIC_CONNECT_INT, HZ / 5)) { + dev_warn(&pdev->dev, "HSIC wait for connect interrupt timeout."); + return -ETIMEDOUT; + } + + return 0; +} + +static int mv_hsic_phy_power_off(struct phy *phy) +{ + struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); + void __iomem *base = mv_phy->base; + + writel(readl(base + PHY_28NM_HSIC_CTRL) & ~PHY_28NM_HSIC_S2H_HSIC_EN, + base + PHY_28NM_HSIC_CTRL); + + return 0; +} + +static int mv_hsic_phy_exit(struct phy *phy) +{ + struct mv_hsic_phy *mv_phy = phy_get_drvdata(phy); + void __iomem *base = mv_phy->base; + + /* Turn off PLL */ + writel(readl(base + PHY_28NM_HSIC_PLL_CTRL2) & + ~PHY_28NM_HSIC_S2H_PU_PLL, + base + PHY_28NM_HSIC_PLL_CTRL2); + + clk_disable_unprepare(mv_phy->clk); + return 0; +} + + +static const struct phy_ops hsic_ops = { + .init = mv_hsic_phy_init, + .power_on = mv_hsic_phy_power_on, + .power_off = mv_hsic_phy_power_off, + .exit = mv_hsic_phy_exit, + .owner = THIS_MODULE, +}; + +static int mv_hsic_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct mv_hsic_phy *mv_phy; + struct resource *r; + + mv_phy = devm_kzalloc(&pdev->dev, sizeof(*mv_phy), GFP_KERNEL); + if (!mv_phy) + return -ENOMEM; + + mv_phy->pdev = pdev; + + mv_phy->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(mv_phy->clk)) { + dev_err(&pdev->dev, "failed to get clock.\n"); + return PTR_ERR(mv_phy->clk); + } + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mv_phy->base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(mv_phy->base)) + return PTR_ERR(mv_phy->base); + + mv_phy->phy = devm_phy_create(&pdev->dev, pdev->dev.of_node, &hsic_ops); + if (IS_ERR(mv_phy->phy)) + return PTR_ERR(mv_phy->phy); + + phy_set_drvdata(mv_phy->phy, mv_phy); + + phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id mv_hsic_phy_dt_match[] = { + { .compatible = "marvell,pxa1928-hsic-phy", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mv_hsic_phy_dt_match); + +static struct platform_driver mv_hsic_phy_driver = { + .probe = mv_hsic_phy_probe, + .driver = { + .name = "mv-hsic-phy", + .of_match_table = of_match_ptr(mv_hsic_phy_dt_match), + }, +}; +module_platform_driver(mv_hsic_phy_driver); + +MODULE_AUTHOR("Rob Herring <robh@kernel.org>"); +MODULE_DESCRIPTION("Marvell HSIC phy driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-pxa-28nm-usb2.c b/drivers/phy/phy-pxa-28nm-usb2.c new file mode 100644 index 000000000000..37e9c8ca4983 --- /dev/null +++ b/drivers/phy/phy-pxa-28nm-usb2.c @@ -0,0 +1,355 @@ +/* + * Copyright (C) 2015 Linaro, Ltd. + * Rob Herring <robh@kernel.org> + * + * Based on vendor driver: + * Copyright (C) 2013 Marvell Inc. + * Author: Chao Xie <xiechao.mail@gmail.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 <linux/delay.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/phy/phy.h> + +/* USB PXA1928 PHY mapping */ +#define PHY_28NM_PLL_REG0 0x0 +#define PHY_28NM_PLL_REG1 0x4 +#define PHY_28NM_CAL_REG 0x8 +#define PHY_28NM_TX_REG0 0x0c +#define PHY_28NM_TX_REG1 0x10 +#define PHY_28NM_RX_REG0 0x14 +#define PHY_28NM_RX_REG1 0x18 +#define PHY_28NM_DIG_REG0 0x1c +#define PHY_28NM_DIG_REG1 0x20 +#define PHY_28NM_TEST_REG0 0x24 +#define PHY_28NM_TEST_REG1 0x28 +#define PHY_28NM_MOC_REG 0x2c +#define PHY_28NM_PHY_RESERVE 0x30 +#define PHY_28NM_OTG_REG 0x34 +#define PHY_28NM_CHRG_DET 0x38 +#define PHY_28NM_CTRL_REG0 0xc4 +#define PHY_28NM_CTRL_REG1 0xc8 +#define PHY_28NM_CTRL_REG2 0xd4 +#define PHY_28NM_CTRL_REG3 0xdc + +/* PHY_28NM_PLL_REG0 */ +#define PHY_28NM_PLL_READY BIT(31) + +#define PHY_28NM_PLL_SELLPFR_SHIFT 28 +#define PHY_28NM_PLL_SELLPFR_MASK (0x3 << 28) + +#define PHY_28NM_PLL_FBDIV_SHIFT 16 +#define PHY_28NM_PLL_FBDIV_MASK (0x1ff << 16) + +#define PHY_28NM_PLL_ICP_SHIFT 8 +#define PHY_28NM_PLL_ICP_MASK (0x7 << 8) + +#define PHY_28NM_PLL_REFDIV_SHIFT 0 +#define PHY_28NM_PLL_REFDIV_MASK 0x7f + +/* PHY_28NM_PLL_REG1 */ +#define PHY_28NM_PLL_PU_BY_REG BIT(1) + +#define PHY_28NM_PLL_PU_PLL BIT(0) + +/* PHY_28NM_CAL_REG */ +#define PHY_28NM_PLL_PLLCAL_DONE BIT(31) + +#define PHY_28NM_PLL_IMPCAL_DONE BIT(23) + +#define PHY_28NM_PLL_KVCO_SHIFT 16 +#define PHY_28NM_PLL_KVCO_MASK (0x7 << 16) + +#define PHY_28NM_PLL_CAL12_SHIFT 20 +#define PHY_28NM_PLL_CAL12_MASK (0x3 << 20) + +#define PHY_28NM_IMPCAL_VTH_SHIFT 8 +#define PHY_28NM_IMPCAL_VTH_MASK (0x7 << 8) + +#define PHY_28NM_PLLCAL_START_SHIFT 22 +#define PHY_28NM_IMPCAL_START_SHIFT 13 + +/* PHY_28NM_TX_REG0 */ +#define PHY_28NM_TX_PU_BY_REG BIT(25) + +#define PHY_28NM_TX_PU_ANA BIT(24) + +#define PHY_28NM_TX_AMP_SHIFT 20 +#define PHY_28NM_TX_AMP_MASK (0x7 << 20) + +/* PHY_28NM_RX_REG0 */ +#define PHY_28NM_RX_SQ_THRESH_SHIFT 0 +#define PHY_28NM_RX_SQ_THRESH_MASK (0xf << 0) + +/* PHY_28NM_RX_REG1 */ +#define PHY_28NM_RX_SQCAL_DONE BIT(31) + +/* PHY_28NM_DIG_REG0 */ +#define PHY_28NM_DIG_BITSTAFFING_ERR BIT(31) +#define PHY_28NM_DIG_SYNC_ERR BIT(30) + +#define PHY_28NM_DIG_SQ_FILT_SHIFT 16 +#define PHY_28NM_DIG_SQ_FILT_MASK (0x7 << 16) + +#define PHY_28NM_DIG_SQ_BLK_SHIFT 12 +#define PHY_28NM_DIG_SQ_BLK_MASK (0x7 << 12) + +#define PHY_28NM_DIG_SYNC_NUM_SHIFT 0 +#define PHY_28NM_DIG_SYNC_NUM_MASK (0x3 << 0) + +#define PHY_28NM_PLL_LOCK_BYPASS BIT(7) + +/* PHY_28NM_OTG_REG */ +#define PHY_28NM_OTG_CONTROL_BY_PIN BIT(5) +#define PHY_28NM_OTG_PU_OTG BIT(4) + +#define PHY_28NM_CHGDTC_ENABLE_SWITCH_DM_SHIFT_28 13 +#define PHY_28NM_CHGDTC_ENABLE_SWITCH_DP_SHIFT_28 12 +#define PHY_28NM_CHGDTC_VSRC_CHARGE_SHIFT_28 10 +#define PHY_28NM_CHGDTC_VDAT_CHARGE_SHIFT_28 8 +#define PHY_28NM_CHGDTC_CDP_DM_AUTO_SWITCH_SHIFT_28 7 +#define PHY_28NM_CHGDTC_DP_DM_SWAP_SHIFT_28 6 +#define PHY_28NM_CHGDTC_PU_CHRG_DTC_SHIFT_28 5 +#define PHY_28NM_CHGDTC_PD_EN_SHIFT_28 4 +#define PHY_28NM_CHGDTC_DCP_EN_SHIFT_28 3 +#define PHY_28NM_CHGDTC_CDP_EN_SHIFT_28 2 +#define PHY_28NM_CHGDTC_TESTMON_CHRGDTC_SHIFT_28 0 + +#define PHY_28NM_CTRL1_CHRG_DTC_OUT_SHIFT_28 4 +#define PHY_28NM_CTRL1_VBUSDTC_OUT_SHIFT_28 2 + +#define PHY_28NM_CTRL3_OVERWRITE BIT(0) +#define PHY_28NM_CTRL3_VBUS_VALID BIT(4) +#define PHY_28NM_CTRL3_AVALID BIT(5) +#define PHY_28NM_CTRL3_BVALID BIT(6) + +struct mv_usb2_phy { + struct phy *phy; + struct platform_device *pdev; + void __iomem *base; + struct clk *clk; +}; + +static bool wait_for_reg(void __iomem *reg, u32 mask, unsigned long timeout) +{ + timeout += jiffies; + while (time_is_after_eq_jiffies(timeout)) { + if ((readl(reg) & mask) == mask) + return true; + msleep(1); + } + return false; +} + +static int mv_usb2_phy_28nm_init(struct phy *phy) +{ + struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy); + struct platform_device *pdev = mv_phy->pdev; + void __iomem *base = mv_phy->base; + u32 reg; + int ret; + + clk_prepare_enable(mv_phy->clk); + + /* PHY_28NM_PLL_REG0 */ + reg = readl(base + PHY_28NM_PLL_REG0) & + ~(PHY_28NM_PLL_SELLPFR_MASK | PHY_28NM_PLL_FBDIV_MASK + | PHY_28NM_PLL_ICP_MASK | PHY_28NM_PLL_REFDIV_MASK); + writel(reg | (0x1 << PHY_28NM_PLL_SELLPFR_SHIFT + | 0xf0 << PHY_28NM_PLL_FBDIV_SHIFT + | 0x3 << PHY_28NM_PLL_ICP_SHIFT + | 0xd << PHY_28NM_PLL_REFDIV_SHIFT), + base + PHY_28NM_PLL_REG0); + + /* PHY_28NM_PLL_REG1 */ + reg = readl(base + PHY_28NM_PLL_REG1); + writel(reg | PHY_28NM_PLL_PU_PLL | PHY_28NM_PLL_PU_BY_REG, + base + PHY_28NM_PLL_REG1); + + /* PHY_28NM_TX_REG0 */ + reg = readl(base + PHY_28NM_TX_REG0) & ~PHY_28NM_TX_AMP_MASK; + writel(reg | PHY_28NM_TX_PU_BY_REG | 0x3 << PHY_28NM_TX_AMP_SHIFT | + PHY_28NM_TX_PU_ANA, + base + PHY_28NM_TX_REG0); + + /* PHY_28NM_RX_REG0 */ + reg = readl(base + PHY_28NM_RX_REG0) & ~PHY_28NM_RX_SQ_THRESH_MASK; + writel(reg | 0xa << PHY_28NM_RX_SQ_THRESH_SHIFT, + base + PHY_28NM_RX_REG0); + + /* PHY_28NM_DIG_REG0 */ + reg = readl(base + PHY_28NM_DIG_REG0) & + ~(PHY_28NM_DIG_BITSTAFFING_ERR | PHY_28NM_DIG_SYNC_ERR | + PHY_28NM_DIG_SQ_FILT_MASK | PHY_28NM_DIG_SQ_BLK_MASK | + PHY_28NM_DIG_SYNC_NUM_MASK); + writel(reg | (0x1 << PHY_28NM_DIG_SYNC_NUM_SHIFT | + PHY_28NM_PLL_LOCK_BYPASS), + base + PHY_28NM_DIG_REG0); + + /* PHY_28NM_OTG_REG */ + reg = readl(base + PHY_28NM_OTG_REG) | PHY_28NM_OTG_PU_OTG; + writel(reg & ~PHY_28NM_OTG_CONTROL_BY_PIN, base + PHY_28NM_OTG_REG); + + /* + * Calibration Timing + * ____________________________ + * CAL START ___| + * ____________________ + * CAL_DONE ___________| + * | 400us | + */ + + /* Make sure PHY Calibration is ready */ + if (!wait_for_reg(base + PHY_28NM_CAL_REG, + PHY_28NM_PLL_PLLCAL_DONE | PHY_28NM_PLL_IMPCAL_DONE, + HZ / 10)) { + dev_warn(&pdev->dev, "USB PHY PLL calibrate not done after 100mS."); + ret = -ETIMEDOUT; + goto err_clk; + } + if (!wait_for_reg(base + PHY_28NM_RX_REG1, + PHY_28NM_RX_SQCAL_DONE, HZ / 10)) { + dev_warn(&pdev->dev, "USB PHY RX SQ calibrate not done after 100mS."); + ret = -ETIMEDOUT; + goto err_clk; + } + /* Make sure PHY PLL is ready */ + if (!wait_for_reg(base + PHY_28NM_PLL_REG0, + PHY_28NM_PLL_READY, HZ / 10)) { + dev_warn(&pdev->dev, "PLL_READY not set after 100mS."); + ret = -ETIMEDOUT; + goto err_clk; + } + + return 0; +err_clk: + clk_disable_unprepare(mv_phy->clk); + return ret; +} + +static int mv_usb2_phy_28nm_power_on(struct phy *phy) +{ + struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy); + void __iomem *base = mv_phy->base; + + writel(readl(base + PHY_28NM_CTRL_REG3) | + (PHY_28NM_CTRL3_OVERWRITE | PHY_28NM_CTRL3_VBUS_VALID | + PHY_28NM_CTRL3_AVALID | PHY_28NM_CTRL3_BVALID), + base + PHY_28NM_CTRL_REG3); + + return 0; +} + +static int mv_usb2_phy_28nm_power_off(struct phy *phy) +{ + struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy); + void __iomem *base = mv_phy->base; + + writel(readl(base + PHY_28NM_CTRL_REG3) | + ~(PHY_28NM_CTRL3_OVERWRITE | PHY_28NM_CTRL3_VBUS_VALID + | PHY_28NM_CTRL3_AVALID | PHY_28NM_CTRL3_BVALID), + base + PHY_28NM_CTRL_REG3); + + return 0; +} + +static int mv_usb2_phy_28nm_exit(struct phy *phy) +{ + struct mv_usb2_phy *mv_phy = phy_get_drvdata(phy); + void __iomem *base = mv_phy->base; + unsigned int val; + + val = readw(base + PHY_28NM_PLL_REG1); + val &= ~PHY_28NM_PLL_PU_PLL; + writew(val, base + PHY_28NM_PLL_REG1); + + /* power down PHY Analog part */ + val = readw(base + PHY_28NM_TX_REG0); + val &= ~PHY_28NM_TX_PU_ANA; + writew(val, base + PHY_28NM_TX_REG0); + + /* power down PHY OTG part */ + val = readw(base + PHY_28NM_OTG_REG); + val &= ~PHY_28NM_OTG_PU_OTG; + writew(val, base + PHY_28NM_OTG_REG); + + clk_disable_unprepare(mv_phy->clk); + return 0; +} + +static const struct phy_ops usb_ops = { + .init = mv_usb2_phy_28nm_init, + .power_on = mv_usb2_phy_28nm_power_on, + .power_off = mv_usb2_phy_28nm_power_off, + .exit = mv_usb2_phy_28nm_exit, + .owner = THIS_MODULE, +}; + +static int mv_usb2_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct mv_usb2_phy *mv_phy; + struct resource *r; + + mv_phy = devm_kzalloc(&pdev->dev, sizeof(*mv_phy), GFP_KERNEL); + if (!mv_phy) + return -ENOMEM; + + mv_phy->pdev = pdev; + + mv_phy->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(mv_phy->clk)) { + dev_err(&pdev->dev, "failed to get clock.\n"); + return PTR_ERR(mv_phy->clk); + } + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mv_phy->base = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(mv_phy->base)) + return PTR_ERR(mv_phy->base); + + mv_phy->phy = devm_phy_create(&pdev->dev, pdev->dev.of_node, &usb_ops); + if (IS_ERR(mv_phy->phy)) + return PTR_ERR(mv_phy->phy); + + phy_set_drvdata(mv_phy->phy, mv_phy); + + phy_provider = devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id mv_usbphy_dt_match[] = { + { .compatible = "marvell,pxa1928-usb-phy", }, + {}, +}; +MODULE_DEVICE_TABLE(of, mv_usbphy_dt_match); + +static struct platform_driver mv_usb2_phy_driver = { + .probe = mv_usb2_phy_probe, + .driver = { + .name = "mv-usb2-phy", + .of_match_table = of_match_ptr(mv_usbphy_dt_match), + }, +}; +module_platform_driver(mv_usb2_phy_driver); + +MODULE_AUTHOR("Rob Herring <robh@kernel.org>"); +MODULE_DESCRIPTION("Marvell USB2 phy driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/phy-rcar-gen2.c b/drivers/phy/phy-rcar-gen2.c index 97d45f47d1ad..39d9b2995435 100644 --- a/drivers/phy/phy-rcar-gen2.c +++ b/drivers/phy/phy-rcar-gen2.c @@ -195,6 +195,7 @@ static struct phy_ops rcar_gen2_phy_ops = { static const struct of_device_id rcar_gen2_phy_match_table[] = { { .compatible = "renesas,usb-phy-r8a7790" }, { .compatible = "renesas,usb-phy-r8a7791" }, + { .compatible = "renesas,usb-phy-r8a7794" }, { } }; MODULE_DEVICE_TABLE(of, rcar_gen2_phy_match_table); @@ -206,11 +207,6 @@ static struct phy *rcar_gen2_phy_xlate(struct device *dev, struct device_node *np = args->np; int i; - if (!of_device_is_available(np)) { - dev_warn(dev, "Requested PHY is disabled\n"); - return ERR_PTR(-ENODEV); - } - drv = dev_get_drvdata(dev); if (!drv) return ERR_PTR(-EINVAL); diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c index a2b08f3ccb03..e17c539e4f6f 100644 --- a/drivers/phy/phy-sun4i-usb.c +++ b/drivers/phy/phy-sun4i-usb.c @@ -30,6 +30,7 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/phy/phy.h> +#include <linux/phy/phy-sun4i-usb.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> #include <linux/reset.h> @@ -58,6 +59,7 @@ #define PHY_OTG_FUNC_EN 0x28 #define PHY_VBUS_DET_EN 0x29 #define PHY_DISCON_TH_SEL 0x2a +#define PHY_SQUELCH_DETECT 0x3c #define MAX_PHYS 3 @@ -204,6 +206,13 @@ static int sun4i_usb_phy_power_off(struct phy *_phy) return 0; } +void sun4i_usb_phy_set_squelch_detect(struct phy *_phy, bool enabled) +{ + struct sun4i_usb_phy *phy = phy_get_drvdata(_phy); + + sun4i_usb_phy_write(phy, PHY_SQUELCH_DETECT, enabled ? 0 : 2, 2); +} + static struct phy_ops sun4i_usb_phy_ops = { .init = sun4i_usb_phy_init, .exit = sun4i_usb_phy_exit, diff --git a/drivers/phy/phy-tusb1210.c b/drivers/phy/phy-tusb1210.c new file mode 100644 index 000000000000..07efdd318bdc --- /dev/null +++ b/drivers/phy/phy-tusb1210.c @@ -0,0 +1,153 @@ +/** + * tusb1210.c - TUSB1210 USB ULPI PHY driver + * + * Copyright (C) 2015 Intel Corporation + * + * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/module.h> +#include <linux/ulpi/driver.h> +#include <linux/gpio/consumer.h> + +#include "ulpi_phy.h" + +#define TUSB1210_VENDOR_SPECIFIC2 0x80 +#define TUSB1210_VENDOR_SPECIFIC2_IHSTX_SHIFT 0 +#define TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_SHIFT 4 +#define TUSB1210_VENDOR_SPECIFIC2_DP_SHIFT 6 + +struct tusb1210 { + struct ulpi *ulpi; + struct phy *phy; + struct gpio_desc *gpio_reset; + struct gpio_desc *gpio_cs; + u8 vendor_specific2; +}; + +static int tusb1210_power_on(struct phy *phy) +{ + struct tusb1210 *tusb = phy_get_drvdata(phy); + + gpiod_set_value_cansleep(tusb->gpio_reset, 1); + gpiod_set_value_cansleep(tusb->gpio_cs, 1); + + /* Restore the optional eye diagram optimization value */ + if (tusb->vendor_specific2) + ulpi_write(tusb->ulpi, TUSB1210_VENDOR_SPECIFIC2, + tusb->vendor_specific2); + + return 0; +} + +static int tusb1210_power_off(struct phy *phy) +{ + struct tusb1210 *tusb = phy_get_drvdata(phy); + + gpiod_set_value_cansleep(tusb->gpio_reset, 0); + gpiod_set_value_cansleep(tusb->gpio_cs, 0); + + return 0; +} + +static struct phy_ops phy_ops = { + .power_on = tusb1210_power_on, + .power_off = tusb1210_power_off, + .owner = THIS_MODULE, +}; + +static int tusb1210_probe(struct ulpi *ulpi) +{ + struct gpio_desc *gpio; + struct tusb1210 *tusb; + u8 val, reg; + int ret; + + tusb = devm_kzalloc(&ulpi->dev, sizeof(*tusb), GFP_KERNEL); + if (!tusb) + return -ENOMEM; + + gpio = devm_gpiod_get(&ulpi->dev, "reset"); + if (!IS_ERR(gpio)) { + ret = gpiod_direction_output(gpio, 0); + if (ret) + return ret; + gpiod_set_value_cansleep(gpio, 1); + tusb->gpio_reset = gpio; + } + + gpio = devm_gpiod_get(&ulpi->dev, "cs"); + if (!IS_ERR(gpio)) { + ret = gpiod_direction_output(gpio, 0); + if (ret) + return ret; + gpiod_set_value_cansleep(gpio, 1); + tusb->gpio_cs = gpio; + } + + /* + * VENDOR_SPECIFIC2 register in TUSB1210 can be used for configuring eye + * diagram optimization and DP/DM swap. + */ + + /* High speed output drive strength configuration */ + device_property_read_u8(&ulpi->dev, "ihstx", &val); + reg = val << TUSB1210_VENDOR_SPECIFIC2_IHSTX_SHIFT; + + /* High speed output impedance configuration */ + device_property_read_u8(&ulpi->dev, "zhsdrv", &val); + reg |= val << TUSB1210_VENDOR_SPECIFIC2_ZHSDRV_SHIFT; + + /* DP/DM swap control */ + device_property_read_u8(&ulpi->dev, "datapolarity", &val); + reg |= val << TUSB1210_VENDOR_SPECIFIC2_DP_SHIFT; + + if (reg) { + ulpi_write(ulpi, TUSB1210_VENDOR_SPECIFIC2, reg); + tusb->vendor_specific2 = reg; + } + + tusb->phy = ulpi_phy_create(ulpi, &phy_ops); + if (IS_ERR(tusb->phy)) + return PTR_ERR(tusb->phy); + + tusb->ulpi = ulpi; + + phy_set_drvdata(tusb->phy, tusb); + ulpi_set_drvdata(ulpi, tusb); + return 0; +} + +static void tusb1210_remove(struct ulpi *ulpi) +{ + struct tusb1210 *tusb = ulpi_get_drvdata(ulpi); + + ulpi_phy_destroy(ulpi, tusb->phy); +} + +#define TI_VENDOR_ID 0x0451 + +static const struct ulpi_device_id tusb1210_ulpi_id[] = { + { TI_VENDOR_ID, 0x1507, }, + { }, +}; +MODULE_DEVICE_TABLE(ulpi, tusb1210_ulpi_id); + +static struct ulpi_driver tusb1210_driver = { + .id_table = tusb1210_ulpi_id, + .probe = tusb1210_probe, + .remove = tusb1210_remove, + .driver = { + .name = "tusb1210", + .owner = THIS_MODULE, + }, +}; + +module_ulpi_driver(tusb1210_driver); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TUSB1210 ULPI PHY driver"); diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c index bc42d6a8939f..3a707dd14238 100644 --- a/drivers/phy/phy-twl4030-usb.c +++ b/drivers/phy/phy-twl4030-usb.c @@ -144,6 +144,16 @@ #define PMBR1 0x0D #define GPIO_USB_4PIN_ULPI_2430C (3 << 0) +/* + * If VBUS is valid or ID is ground, then we know a + * cable is present and we need to be runtime-enabled + */ +static inline bool cable_present(enum omap_musb_vbus_id_status stat) +{ + return stat == OMAP_MUSB_VBUS_VALID || + stat == OMAP_MUSB_ID_GROUND; +} + struct twl4030_usb { struct usb_phy phy; struct device *dev; @@ -386,8 +396,6 @@ static int twl4030_usb_runtime_suspend(struct device *dev) struct twl4030_usb *twl = dev_get_drvdata(dev); dev_dbg(twl->dev, "%s\n", __func__); - if (pm_runtime_suspended(dev)) - return 0; __twl4030_phy_power(twl, 0); regulator_disable(twl->usb1v5); @@ -403,8 +411,6 @@ static int twl4030_usb_runtime_resume(struct device *dev) int res; dev_dbg(twl->dev, "%s\n", __func__); - if (pm_runtime_active(dev)) - return 0; res = regulator_enable(twl->usb3v1); if (res) @@ -536,8 +542,10 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) mutex_lock(&twl->lock); if (status >= 0 && status != twl->linkstat) { + status_changed = + cable_present(twl->linkstat) != + cable_present(status); twl->linkstat = status; - status_changed = true; } mutex_unlock(&twl->lock); @@ -553,15 +561,11 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl) * USB_LINK_VBUS state. musb_hdrc won't care until it * starts to handle softconnect right. */ - if ((status == OMAP_MUSB_VBUS_VALID) || - (status == OMAP_MUSB_ID_GROUND)) { - if (pm_runtime_suspended(twl->dev)) - pm_runtime_get_sync(twl->dev); + if (cable_present(status)) { + pm_runtime_get_sync(twl->dev); } else { - if (pm_runtime_active(twl->dev)) { - pm_runtime_mark_last_busy(twl->dev); - pm_runtime_put_autosuspend(twl->dev); - } + pm_runtime_mark_last_busy(twl->dev); + pm_runtime_put_autosuspend(twl->dev); } omap_musb_mailbox(status); } @@ -711,7 +715,6 @@ static int twl4030_usb_probe(struct platform_device *pdev) pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, 2000); pm_runtime_enable(&pdev->dev); - pm_runtime_get_sync(&pdev->dev); /* Our job is to use irqs and status from the power module * to keep the transceiver disabled when nothing's connected. @@ -767,6 +770,9 @@ static int twl4030_usb_remove(struct platform_device *pdev) /* disable complete OTG block */ twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB); + + if (cable_present(twl->linkstat)) + pm_runtime_put_noidle(twl->dev); pm_runtime_mark_last_busy(twl->dev); pm_runtime_put(twl->dev); diff --git a/drivers/phy/ulpi_phy.h b/drivers/phy/ulpi_phy.h new file mode 100644 index 000000000000..ac49fb6285ee --- /dev/null +++ b/drivers/phy/ulpi_phy.h @@ -0,0 +1,31 @@ +#include <linux/phy/phy.h> + +/** + * Helper that registers PHY for a ULPI device and adds a lookup for binding it + * and it's controller, which is always the parent. + */ +static inline struct phy +*ulpi_phy_create(struct ulpi *ulpi, struct phy_ops *ops) +{ + struct phy *phy; + int ret; + + phy = phy_create(&ulpi->dev, NULL, ops); + if (IS_ERR(phy)) + return phy; + + ret = phy_create_lookup(phy, "usb2-phy", dev_name(ulpi->dev.parent)); + if (ret) { + phy_destroy(phy); + return ERR_PTR(ret); + } + + return phy; +} + +/* Remove a PHY that was created with ulpi_phy_create() and it's lookup. */ +static inline void ulpi_phy_destroy(struct ulpi *ulpi, struct phy *phy) +{ + phy_remove_lookup(phy, "usb2-phy", dev_name(ulpi->dev.parent)); + phy_destroy(phy); +} diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c index 02a522cb7753..022b8910e443 100644 --- a/drivers/power/twl4030_charger.c +++ b/drivers/power/twl4030_charger.c @@ -638,10 +638,15 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) INIT_WORK(&bci->work, twl4030_bci_usb_work); - bci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2); - if (!IS_ERR_OR_NULL(bci->transceiver)) { - bci->usb_nb.notifier_call = twl4030_bci_usb_ncb; - usb_register_notifier(bci->transceiver, &bci->usb_nb); + bci->usb_nb.notifier_call = twl4030_bci_usb_ncb; + if (bci->dev->of_node) { + struct device_node *phynode; + + phynode = of_find_compatible_node(bci->dev->of_node->parent, + NULL, "ti,twl4030-usb"); + if (phynode) + bci->transceiver = devm_usb_get_phy_by_node( + bci->dev, phynode, &bci->usb_nb); } /* Enable interrupts now. */ @@ -671,10 +676,6 @@ static int __init twl4030_bci_probe(struct platform_device *pdev) return 0; fail_unmask_interrupts: - if (!IS_ERR_OR_NULL(bci->transceiver)) { - usb_unregister_notifier(bci->transceiver, &bci->usb_nb); - usb_put_phy(bci->transceiver); - } free_irq(bci->irq_bci, bci); fail_bci_irq: free_irq(bci->irq_chg, bci); @@ -703,10 +704,6 @@ static int __exit twl4030_bci_remove(struct platform_device *pdev) twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, 0xff, TWL4030_INTERRUPTS_BCIIMR2A); - if (!IS_ERR_OR_NULL(bci->transceiver)) { - usb_unregister_notifier(bci->transceiver, &bci->usb_nb); - usb_put_phy(bci->transceiver); - } free_irq(bci->irq_bci, bci); free_irq(bci->irq_chg, bci); power_supply_unregister(bci->usb); diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c index 0dc8c06a7b5f..0270d1312f83 100644 --- a/drivers/usb/atm/speedtch.c +++ b/drivers/usb/atm/speedtch.c @@ -255,7 +255,8 @@ static int speedtch_upload_firmware(struct speedtch_instance_data *instance, usb_dbg(usbatm, "%s entered\n", __func__); - if (!(buffer = (unsigned char *)__get_free_page(GFP_KERNEL))) { + buffer = (unsigned char *)__get_free_page(GFP_KERNEL); + if (!buffer) { ret = -ENOMEM; usb_dbg(usbatm, "%s: no memory for buffer!\n", __func__); goto out; @@ -638,7 +639,8 @@ static void speedtch_handle_int(struct urb *int_urb) goto fail; } - if ((int_urb = instance->int_urb)) { + int_urb = instance->int_urb; + if (int_urb) { ret = usb_submit_urb(int_urb, GFP_ATOMIC); schedule_work(&instance->status_check_work); if (ret < 0) { @@ -650,7 +652,8 @@ static void speedtch_handle_int(struct urb *int_urb) return; fail: - if ((int_urb = instance->int_urb)) + int_urb = instance->int_urb; + if (int_urb) mod_timer(&instance->resubmit_timer, jiffies + msecs_to_jiffies(RESUBMIT_DELAY)); } @@ -759,11 +762,13 @@ static void speedtch_release_interfaces(struct usb_device *usb_dev, struct usb_interface *cur_intf; int i; - for (i = 0; i < num_interfaces; i++) - if ((cur_intf = usb_ifnum_to_if(usb_dev, i))) { + for (i = 0; i < num_interfaces; i++) { + cur_intf = usb_ifnum_to_if(usb_dev, i); + if (cur_intf) { usb_set_intfdata(cur_intf, NULL); usb_driver_release_interface(&speedtch_usb_driver, cur_intf); } + } } static int speedtch_bind(struct usbatm_data *usbatm, @@ -787,7 +792,8 @@ static int speedtch_bind(struct usbatm_data *usbatm, return -ENODEV; } - if (!(data_intf = usb_ifnum_to_if(usb_dev, INTERFACE_DATA))) { + data_intf = usb_ifnum_to_if(usb_dev, INTERFACE_DATA); + if (!data_intf) { usb_err(usbatm, "%s: data interface not found!\n", __func__); return -ENODEV; } diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c index dada0146cd7f..db322d9ccb6e 100644 --- a/drivers/usb/atm/usbatm.c +++ b/drivers/usb/atm/usbatm.c @@ -382,7 +382,8 @@ static void usbatm_extract_one_cell(struct usbatm_data *instance, unsigned char "%s: got packet (length: %u, pdu_length: %u, vcc: 0x%p)", __func__, length, pdu_length, vcc); - if (!(skb = dev_alloc_skb(length))) { + skb = dev_alloc_skb(length); + if (!skb) { if (printk_ratelimit()) atm_err(instance, "%s: no memory for skb (length: %u)!\n", __func__, length); @@ -816,7 +817,8 @@ static int usbatm_atm_open(struct atm_vcc *vcc) goto fail; } - if (!(new = kzalloc(sizeof(struct usbatm_vcc_data), GFP_KERNEL))) { + new = kzalloc(sizeof(struct usbatm_vcc_data), GFP_KERNEL); + if (!new) { atm_err(instance, "%s: no memory for vcc_data!\n", __func__); ret = -ENOMEM; goto fail; diff --git a/drivers/usb/atm/xusbatm.c b/drivers/usb/atm/xusbatm.c index b3b1bb78b2ef..a87597f88a84 100644 --- a/drivers/usb/atm/xusbatm.c +++ b/drivers/usb/atm/xusbatm.c @@ -73,7 +73,8 @@ static int xusbatm_capture_intf(struct usbatm_data *usbatm, struct usb_device *u usb_err(usbatm, "%s: failed to claim interface %2d (%d)!\n", __func__, ifnum, ret); return ret; } - if ((ret = usb_set_interface(usb_dev, ifnum, altsetting))) { + ret = usb_set_interface(usb_dev, ifnum, altsetting); + if (ret) { usb_err(usbatm, "%s: altsetting %2d for interface %2d failed (%d)!\n", __func__, altsetting, ifnum, ret); return ret; } @@ -128,7 +129,8 @@ static int xusbatm_bind(struct usbatm_data *usbatm, rx_intf->altsetting->desc.bInterfaceNumber, tx_intf->altsetting->desc.bInterfaceNumber); - if ((ret = xusbatm_capture_intf(usbatm, usb_dev, rx_intf, rx_alt, rx_intf != intf))) + ret = xusbatm_capture_intf(usbatm, usb_dev, rx_intf, rx_alt, rx_intf != intf); + if (ret) return ret; if ((tx_intf != rx_intf) && (ret = xusbatm_capture_intf(usbatm, usb_dev, tx_intf, tx_alt, tx_intf != intf))) { diff --git a/drivers/usb/chipidea/ci_hdrc_usb2.c b/drivers/usb/chipidea/ci_hdrc_usb2.c index 45844c951788..9eae1a16cef9 100644 --- a/drivers/usb/chipidea/ci_hdrc_usb2.c +++ b/drivers/usb/chipidea/ci_hdrc_usb2.c @@ -25,7 +25,7 @@ struct ci_hdrc_usb2_priv { struct clk *clk; }; -static struct ci_hdrc_platform_data ci_default_pdata = { +static const struct ci_hdrc_platform_data ci_default_pdata = { .capoffset = DEF_CAPOFFSET, .flags = CI_HDRC_DISABLE_STREAMING, }; @@ -37,8 +37,10 @@ static int ci_hdrc_usb2_probe(struct platform_device *pdev) struct ci_hdrc_platform_data *ci_pdata = dev_get_platdata(dev); int ret; - if (!ci_pdata) - ci_pdata = &ci_default_pdata; + if (!ci_pdata) { + ci_pdata = devm_kmalloc(dev, sizeof(*ci_pdata), GFP_KERNEL); + *ci_pdata = ci_default_pdata; /* struct copy */ + } priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c index 21fe1a314313..6cf87b8b13a8 100644 --- a/drivers/usb/chipidea/host.c +++ b/drivers/usb/chipidea/host.c @@ -37,12 +37,14 @@ static int (*orig_bus_suspend)(struct usb_hcd *hcd); struct ehci_ci_priv { struct regulator *reg_vbus; + struct ci_hdrc *ci; }; static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); struct ehci_ci_priv *priv = (struct ehci_ci_priv *)ehci->priv; + struct ci_hdrc *ci = priv->ci; struct device *dev = hcd->self.controller; int ret = 0; int port = HCS_N_PORTS(ehci->hcs_params); @@ -64,6 +66,15 @@ static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable) return ret; } } + + if (enable && (ci->platdata->phy_mode == USBPHY_INTERFACE_MODE_HSIC)) { + /* + * Marvell 28nm HSIC PHY requires forcing the port to HS mode. + * As HSIC is always HS, this should be safe for others. + */ + hw_port_test_set(ci, 5); + hw_port_test_set(ci, 0); + } return 0; }; @@ -112,6 +123,7 @@ static int host_start(struct ci_hdrc *ci) priv = (struct ehci_ci_priv *)ehci->priv; priv->reg_vbus = NULL; + priv->ci = ci; if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci)) { if (ci->platdata->flags & CI_HDRC_TURN_VBUS_EARLY_ON) { diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c index 140945cb124f..3cefd49ddb00 100644 --- a/drivers/usb/chipidea/usbmisc_imx.c +++ b/drivers/usb/chipidea/usbmisc_imx.c @@ -160,7 +160,7 @@ static int usbmisc_imx27_init(struct imx_usbmisc_data *data) break; default: return -EINVAL; - }; + } spin_lock_irqsave(&usbmisc->lock, flags); if (data->disable_oc) diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 5c8f58114677..519a77ba214c 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -46,6 +46,7 @@ #include <linux/usb/cdc.h> #include <asm/byteorder.h> #include <asm/unaligned.h> +#include <linux/idr.h> #include <linux/list.h> #include "cdc-acm.h" @@ -56,27 +57,27 @@ static struct usb_driver acm_driver; static struct tty_driver *acm_tty_driver; -static struct acm *acm_table[ACM_TTY_MINORS]; -static DEFINE_MUTEX(acm_table_lock); +static DEFINE_IDR(acm_minors); +static DEFINE_MUTEX(acm_minors_lock); static void acm_tty_set_termios(struct tty_struct *tty, struct ktermios *termios_old); /* - * acm_table accessors + * acm_minors accessors */ /* - * Look up an ACM structure by index. If found and not disconnected, increment + * Look up an ACM structure by minor. If found and not disconnected, increment * its refcount and return it with its mutex held. */ -static struct acm *acm_get_by_index(unsigned index) +static struct acm *acm_get_by_minor(unsigned int minor) { struct acm *acm; - mutex_lock(&acm_table_lock); - acm = acm_table[index]; + mutex_lock(&acm_minors_lock); + acm = idr_find(&acm_minors, minor); if (acm) { mutex_lock(&acm->mutex); if (acm->disconnected) { @@ -87,7 +88,7 @@ static struct acm *acm_get_by_index(unsigned index) mutex_unlock(&acm->mutex); } } - mutex_unlock(&acm_table_lock); + mutex_unlock(&acm_minors_lock); return acm; } @@ -98,14 +99,9 @@ static int acm_alloc_minor(struct acm *acm) { int minor; - mutex_lock(&acm_table_lock); - for (minor = 0; minor < ACM_TTY_MINORS; minor++) { - if (!acm_table[minor]) { - acm_table[minor] = acm; - break; - } - } - mutex_unlock(&acm_table_lock); + mutex_lock(&acm_minors_lock); + minor = idr_alloc(&acm_minors, acm, 0, ACM_TTY_MINORS, GFP_KERNEL); + mutex_unlock(&acm_minors_lock); return minor; } @@ -113,9 +109,9 @@ static int acm_alloc_minor(struct acm *acm) /* Release the minor number associated with 'acm'. */ static void acm_release_minor(struct acm *acm) { - mutex_lock(&acm_table_lock); - acm_table[acm->minor] = NULL; - mutex_unlock(&acm_table_lock); + mutex_lock(&acm_minors_lock); + idr_remove(&acm_minors, acm->minor); + mutex_unlock(&acm_minors_lock); } /* @@ -497,7 +493,7 @@ static int acm_tty_install(struct tty_driver *driver, struct tty_struct *tty) dev_dbg(tty->dev, "%s\n", __func__); - acm = acm_get_by_index(tty->index); + acm = acm_get_by_minor(tty->index); if (!acm) return -ENODEV; @@ -1267,12 +1263,9 @@ skip_normal_probe: != CDC_DATA_INTERFACE_TYPE) { if (control_interface->cur_altsetting->desc.bInterfaceClass == CDC_DATA_INTERFACE_TYPE) { - struct usb_interface *t; dev_dbg(&intf->dev, "Your device has switched interfaces.\n"); - t = control_interface; - control_interface = data_interface; - data_interface = t; + swap(control_interface, data_interface); } else { return -EINVAL; } @@ -1301,12 +1294,9 @@ skip_normal_probe: /* workaround for switched endpoints */ if (!usb_endpoint_dir_in(epread)) { /* descriptors are swapped */ - struct usb_endpoint_descriptor *t; dev_dbg(&intf->dev, "The data interface has switched endpoints\n"); - t = epread; - epread = epwrite; - epwrite = t; + swap(epread, epwrite); } made_compressed_probe: dev_dbg(&intf->dev, "interfaces are valid\n"); @@ -1316,7 +1306,7 @@ made_compressed_probe: goto alloc_fail; minor = acm_alloc_minor(acm); - if (minor == ACM_TTY_MINORS) { + if (minor < 0) { dev_err(&intf->dev, "no more free acm devices\n"); kfree(acm); return -ENODEV; @@ -1477,6 +1467,11 @@ skip_countries: goto alloc_fail8; } + if (quirks & CLEAR_HALT_CONDITIONS) { + usb_clear_halt(usb_dev, usb_rcvbulkpipe(usb_dev, epread->bEndpointAddress)); + usb_clear_halt(usb_dev, usb_sndbulkpipe(usb_dev, epwrite->bEndpointAddress)); + } + return 0; alloc_fail8: if (acm->country_codes) { @@ -1756,6 +1751,10 @@ static const struct usb_device_id acm_ids[] = { .driver_info = NO_UNION_NORMAL, /* reports zero length descriptor */ }, + { USB_DEVICE(0x2912, 0x0001), /* ATOL FPrint */ + .driver_info = CLEAR_HALT_CONDITIONS, + }, + /* Nokia S60 phones expose two ACM channels. The first is * a modem and is picked up by the standard AT-command * information below. The second is 'vendor-specific' but diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index ffeb3c83941f..dd9af38e7cda 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -19,7 +19,7 @@ */ #define ACM_TTY_MAJOR 166 -#define ACM_TTY_MINORS 32 +#define ACM_TTY_MINORS 256 /* * Requests. @@ -133,3 +133,4 @@ struct acm { #define NO_DATA_INTERFACE BIT(4) #define IGNORE_DEVICE BIT(5) #define QUIRK_CONTROL_LINE_STATE BIT(6) +#define CLEAR_HALT_CONDITIONS BIT(7) diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c index 0924ee40a966..f38e875a3fb1 100644 --- a/drivers/usb/class/usblp.c +++ b/drivers/usb/class/usblp.c @@ -660,7 +660,8 @@ static long usblp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) switch (cmd) { case LPGETSTATUS: - if ((retval = usblp_read_status(usblp, usblp->statusbuf))) { + retval = usblp_read_status(usblp, usblp->statusbuf); + if (retval) { printk_ratelimited(KERN_ERR "usblp%d:" "failed reading printer status (%d)\n", usblp->minor, retval); @@ -693,9 +694,11 @@ static struct urb *usblp_new_writeurb(struct usblp *usblp, int transfer_length) struct urb *urb; char *writebuf; - if ((writebuf = kmalloc(transfer_length, GFP_KERNEL)) == NULL) + writebuf = kmalloc(transfer_length, GFP_KERNEL); + if (writebuf == NULL) return NULL; - if ((urb = usb_alloc_urb(0, GFP_KERNEL)) == NULL) { + urb = usb_alloc_urb(0, GFP_KERNEL); + if (urb == NULL) { kfree(writebuf); return NULL; } @@ -732,7 +735,8 @@ static ssize_t usblp_write(struct file *file, const char __user *buffer, size_t transfer_length = USBLP_BUF_SIZE; rv = -ENOMEM; - if ((writeurb = usblp_new_writeurb(usblp, transfer_length)) == NULL) + writeurb = usblp_new_writeurb(usblp, transfer_length); + if (writeurb == NULL) goto raise_urb; usb_anchor_urb(writeurb, &usblp->urbs); @@ -980,7 +984,8 @@ static int usblp_submit_read(struct usblp *usblp) int rc; rc = -ENOMEM; - if ((urb = usb_alloc_urb(0, GFP_KERNEL)) == NULL) + urb = usb_alloc_urb(0, GFP_KERNEL); + if (urb == NULL) goto raise_urb; usb_fill_bulk_urb(urb, usblp->dev, diff --git a/drivers/usb/class/usbtmc.c b/drivers/usb/class/usbtmc.c index 960bc089111b..7a11a8263171 100644 --- a/drivers/usb/class/usbtmc.c +++ b/drivers/usb/class/usbtmc.c @@ -109,6 +109,7 @@ struct usbtmc_ID_rigol_quirk { static const struct usbtmc_ID_rigol_quirk usbtmc_id_quirk[] = { { 0x1ab1, 0x0588 }, + { 0x1ab1, 0x04b0 }, { 0, 0 } }; diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile index ca2f8bd0e431..6bbb3ec17018 100644 --- a/drivers/usb/common/Makefile +++ b/drivers/usb/common/Makefile @@ -7,3 +7,4 @@ usb-common-y += common.o usb-common-$(CONFIG_USB_LED_TRIG) += led.o obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o +obj-$(CONFIG_USB_ULPI_BUS) += ulpi.o diff --git a/drivers/usb/common/ulpi.c b/drivers/usb/common/ulpi.c new file mode 100644 index 000000000000..0e6f968e93fe --- /dev/null +++ b/drivers/usb/common/ulpi.c @@ -0,0 +1,255 @@ +/** + * ulpi.c - USB ULPI PHY bus + * + * Copyright (C) 2015 Intel Corporation + * + * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/ulpi/interface.h> +#include <linux/ulpi/driver.h> +#include <linux/ulpi/regs.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/acpi.h> + +/* -------------------------------------------------------------------------- */ + +int ulpi_read(struct ulpi *ulpi, u8 addr) +{ + return ulpi->ops->read(ulpi->ops, addr); +} +EXPORT_SYMBOL_GPL(ulpi_read); + +int ulpi_write(struct ulpi *ulpi, u8 addr, u8 val) +{ + return ulpi->ops->write(ulpi->ops, addr, val); +} +EXPORT_SYMBOL_GPL(ulpi_write); + +/* -------------------------------------------------------------------------- */ + +static int ulpi_match(struct device *dev, struct device_driver *driver) +{ + struct ulpi_driver *drv = to_ulpi_driver(driver); + struct ulpi *ulpi = to_ulpi_dev(dev); + const struct ulpi_device_id *id; + + for (id = drv->id_table; id->vendor; id++) + if (id->vendor == ulpi->id.vendor && + id->product == ulpi->id.product) + return 1; + + return 0; +} + +static int ulpi_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct ulpi *ulpi = to_ulpi_dev(dev); + + if (add_uevent_var(env, "MODALIAS=ulpi:v%04xp%04x", + ulpi->id.vendor, ulpi->id.product)) + return -ENOMEM; + return 0; +} + +static int ulpi_probe(struct device *dev) +{ + struct ulpi_driver *drv = to_ulpi_driver(dev->driver); + + return drv->probe(to_ulpi_dev(dev)); +} + +static int ulpi_remove(struct device *dev) +{ + struct ulpi_driver *drv = to_ulpi_driver(dev->driver); + + if (drv->remove) + drv->remove(to_ulpi_dev(dev)); + + return 0; +} + +static struct bus_type ulpi_bus = { + .name = "ulpi", + .match = ulpi_match, + .uevent = ulpi_uevent, + .probe = ulpi_probe, + .remove = ulpi_remove, +}; + +/* -------------------------------------------------------------------------- */ + +static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct ulpi *ulpi = to_ulpi_dev(dev); + + return sprintf(buf, "ulpi:v%04xp%04x\n", + ulpi->id.vendor, ulpi->id.product); +} +static DEVICE_ATTR_RO(modalias); + +static struct attribute *ulpi_dev_attrs[] = { + &dev_attr_modalias.attr, + NULL +}; + +static struct attribute_group ulpi_dev_attr_group = { + .attrs = ulpi_dev_attrs, +}; + +static const struct attribute_group *ulpi_dev_attr_groups[] = { + &ulpi_dev_attr_group, + NULL +}; + +static void ulpi_dev_release(struct device *dev) +{ + kfree(to_ulpi_dev(dev)); +} + +static struct device_type ulpi_dev_type = { + .name = "ulpi_device", + .groups = ulpi_dev_attr_groups, + .release = ulpi_dev_release, +}; + +/* -------------------------------------------------------------------------- */ + +/** + * ulpi_register_driver - register a driver with the ULPI bus + * @drv: driver being registered + * + * Registers a driver with the ULPI bus. + */ +int ulpi_register_driver(struct ulpi_driver *drv) +{ + if (!drv->probe) + return -EINVAL; + + drv->driver.bus = &ulpi_bus; + + return driver_register(&drv->driver); +} +EXPORT_SYMBOL_GPL(ulpi_register_driver); + +/** + * ulpi_unregister_driver - unregister a driver with the ULPI bus + * @drv: driver to unregister + * + * Unregisters a driver with the ULPI bus. + */ +void ulpi_unregister_driver(struct ulpi_driver *drv) +{ + driver_unregister(&drv->driver); +} +EXPORT_SYMBOL_GPL(ulpi_unregister_driver); + +/* -------------------------------------------------------------------------- */ + +static int ulpi_register(struct device *dev, struct ulpi *ulpi) +{ + int ret; + + /* Test the interface */ + ret = ulpi_write(ulpi, ULPI_SCRATCH, 0xaa); + if (ret < 0) + return ret; + + ret = ulpi_read(ulpi, ULPI_SCRATCH); + if (ret < 0) + return ret; + + if (ret != 0xaa) + return -ENODEV; + + ulpi->id.vendor = ulpi_read(ulpi, ULPI_VENDOR_ID_LOW); + ulpi->id.vendor |= ulpi_read(ulpi, ULPI_VENDOR_ID_HIGH) << 8; + + ulpi->id.product = ulpi_read(ulpi, ULPI_PRODUCT_ID_LOW); + ulpi->id.product |= ulpi_read(ulpi, ULPI_PRODUCT_ID_HIGH) << 8; + + ulpi->dev.parent = dev; + ulpi->dev.bus = &ulpi_bus; + ulpi->dev.type = &ulpi_dev_type; + dev_set_name(&ulpi->dev, "%s.ulpi", dev_name(dev)); + + ACPI_COMPANION_SET(&ulpi->dev, ACPI_COMPANION(dev)); + + request_module("ulpi:v%04xp%04x", ulpi->id.vendor, ulpi->id.product); + + ret = device_register(&ulpi->dev); + if (ret) + return ret; + + dev_dbg(&ulpi->dev, "registered ULPI PHY: vendor %04x, product %04x\n", + ulpi->id.vendor, ulpi->id.product); + + return 0; +} + +/** + * ulpi_register_interface - instantiate new ULPI device + * @dev: USB controller's device interface + * @ops: ULPI register access + * + * Allocates and registers a ULPI device and an interface for it. Called from + * the USB controller that provides the ULPI interface. + */ +struct ulpi *ulpi_register_interface(struct device *dev, struct ulpi_ops *ops) +{ + struct ulpi *ulpi; + int ret; + + ulpi = kzalloc(sizeof(*ulpi), GFP_KERNEL); + if (!ulpi) + return ERR_PTR(-ENOMEM); + + ulpi->ops = ops; + ops->dev = dev; + + ret = ulpi_register(dev, ulpi); + if (ret) { + kfree(ulpi); + return ERR_PTR(ret); + } + + return ulpi; +} +EXPORT_SYMBOL_GPL(ulpi_register_interface); + +/** + * ulpi_unregister_interface - unregister ULPI interface + * @intrf: struct ulpi_interface + * + * Unregisters a ULPI device and it's interface that was created with + * ulpi_create_interface(). + */ +void ulpi_unregister_interface(struct ulpi *ulpi) +{ + device_unregister(&ulpi->dev); +} +EXPORT_SYMBOL_GPL(ulpi_unregister_interface); + +/* -------------------------------------------------------------------------- */ + +static int __init ulpi_init(void) +{ + return bus_register(&ulpi_bus); +} +module_init(ulpi_init); + +static void __exit ulpi_exit(void) +{ + bus_unregister(&ulpi_bus); +} +module_exit(ulpi_exit); + +MODULE_AUTHOR("Intel Corporation"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("USB ULPI PHY bus"); diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index cc0ced08bae2..a99c89e78126 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -84,3 +84,23 @@ config USB_OTG_FSM Implements OTG Finite State Machine as specified in On-The-Go and Embedded Host Supplement to the USB Revision 2.0 Specification. +config USB_ULPI_BUS + tristate "USB ULPI PHY interface support" + depends on USB_SUPPORT + help + UTMI+ Low Pin Interface (ULPI) is specification for a commonly used + USB 2.0 PHY interface. The ULPI specification defines a standard set + of registers that can be used to detect the vendor and product which + allows ULPI to be handled as a bus. This module is the driver for that + bus. + + The ULPI interfaces (the buses) are registered by the drivers for USB + controllers which support ULPI register access and have ULPI PHY + attached to them. The ULPI PHY drivers themselves are normal PHY + drivers. + + ULPI PHYs provide often functions such as ADP sensing/probing (OTG + protocol) and USB charger detection. + + To compile this driver as a module, choose M here: the module will + be called ulpi. diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c index 506b969ea7fd..89f2e7765093 100644 --- a/drivers/usb/core/buffer.c +++ b/drivers/usb/core/buffer.c @@ -70,7 +70,7 @@ int hcd_buffer_create(struct usb_hcd *hcd) size = pool_max[i]; if (!size) continue; - snprintf(name, sizeof name, "buffer-%d", size); + snprintf(name, sizeof(name), "buffer-%d", size); hcd->pool[i] = dma_pool_create(name, hcd->self.controller, size, size, 0); if (!hcd->pool[i]) { @@ -95,6 +95,7 @@ void hcd_buffer_destroy(struct usb_hcd *hcd) for (i = 0; i < HCD_BUFFER_POOLS; i++) { struct dma_pool *pool = hcd->pool[i]; + if (pool) { dma_pool_destroy(pool); hcd->pool[i] = NULL; diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 4b0448c26810..986abde07683 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -513,7 +513,7 @@ static void async_completed(struct urb *urb) snoop(&urb->dev->dev, "urb complete\n"); snoop_urb(urb->dev, as->userurb, urb->pipe, urb->actual_length, as->status, COMPLETE, NULL, 0); - if ((urb->transfer_flags & URB_DIR_MASK) == USB_DIR_IN) + if ((urb->transfer_flags & URB_DIR_MASK) == URB_DIR_IN) snoop_urb_data(urb, urb->actual_length); if (as->status < 0 && as->bulk_addr && as->status != -ECONNRESET && diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 45a915ccd71c..be5b2074f906 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -2691,7 +2691,8 @@ int usb_add_hcd(struct usb_hcd *hcd, if ((retval = usb_register_bus(&hcd->self)) < 0) goto err_register_bus; - if ((rhdev = usb_alloc_dev(NULL, &hcd->self, 0)) == NULL) { + rhdev = usb_alloc_dev(NULL, &hcd->self, 0); + if (rhdev == NULL) { dev_err(hcd->self.controller, "unable to allocate root hub\n"); retval = -ENOMEM; goto err_allocate_root_hub; diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 3b7151687776..43cb2f2e3b43 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -127,7 +127,7 @@ static int usb_device_supports_lpm(struct usb_device *udev) /* USB 2.1 (and greater) devices indicate LPM support through * their USB 2.0 Extended Capabilities BOS descriptor. */ - if (udev->speed == USB_SPEED_HIGH) { + if (udev->speed == USB_SPEED_HIGH || udev->speed == USB_SPEED_FULL) { if (udev->bos->ext_cap && (USB_LPM_SUPPORT & le32_to_cpu(udev->bos->ext_cap->bmAttributes))) @@ -795,7 +795,8 @@ int usb_hub_clear_tt_buffer(struct urb *urb) * since each TT has "at least two" buffers that can need it (and * there can be many TTs per hub). even if they're uncommon. */ - if ((clear = kmalloc (sizeof *clear, GFP_ATOMIC)) == NULL) { + clear = kmalloc(sizeof *clear, GFP_ATOMIC); + if (clear == NULL) { dev_err (&udev->dev, "can't save CLEAR_TT_BUFFER state\n"); /* FIXME recover somehow ... RESET_TT? */ return -ENOMEM; @@ -2350,6 +2351,26 @@ static void set_usb_port_removable(struct usb_device *udev) hub = usb_hub_to_struct_hub(udev->parent); + /* + * If the platform firmware has provided information about a port, + * use that to determine whether it's removable. + */ + switch (hub->ports[udev->portnum - 1]->connect_type) { + case USB_PORT_CONNECT_TYPE_HOT_PLUG: + udev->removable = USB_DEVICE_REMOVABLE; + return; + case USB_PORT_CONNECT_TYPE_HARD_WIRED: + case USB_PORT_NOT_USED: + udev->removable = USB_DEVICE_FIXED; + return; + default: + break; + } + + /* + * Otherwise, check whether the hub knows whether a port is removable + * or not + */ wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics); if (!(wHubCharacteristics & HUB_CHAR_COMPOUND)) @@ -2369,21 +2390,6 @@ static void set_usb_port_removable(struct usb_device *udev) else udev->removable = USB_DEVICE_FIXED; - /* - * Platform firmware may have populated an alternative value for - * removable. If the parent port has a known connect_type use - * that instead. - */ - switch (hub->ports[udev->portnum - 1]->connect_type) { - case USB_PORT_CONNECT_TYPE_HOT_PLUG: - udev->removable = USB_DEVICE_REMOVABLE; - break; - case USB_PORT_CONNECT_TYPE_HARD_WIRED: - udev->removable = USB_DEVICE_FIXED; - break; - default: /* use what was set above */ - break; - } } /** @@ -2616,9 +2622,6 @@ static bool use_new_scheme(struct usb_device *udev, int retry) return USE_NEW_SCHEME(retry); } -static int hub_port_reset(struct usb_hub *hub, int port1, - struct usb_device *udev, unsigned int delay, bool warm); - /* Is a USB 3.0 port in the Inactive or Compliance Mode state? * Port worm reset is required to recover */ @@ -2706,44 +2709,6 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, return 0; } -static void hub_port_finish_reset(struct usb_hub *hub, int port1, - struct usb_device *udev, int *status) -{ - switch (*status) { - case 0: - /* TRSTRCY = 10 ms; plus some extra */ - msleep(10 + 40); - if (udev) { - struct usb_hcd *hcd = bus_to_hcd(udev->bus); - - update_devnum(udev, 0); - /* The xHC may think the device is already reset, - * so ignore the status. - */ - if (hcd->driver->reset_device) - hcd->driver->reset_device(hcd, udev); - } - /* FALL THROUGH */ - case -ENOTCONN: - case -ENODEV: - usb_clear_port_feature(hub->hdev, - port1, USB_PORT_FEAT_C_RESET); - if (hub_is_superspeed(hub->hdev)) { - usb_clear_port_feature(hub->hdev, port1, - USB_PORT_FEAT_C_BH_PORT_RESET); - usb_clear_port_feature(hub->hdev, port1, - USB_PORT_FEAT_C_PORT_LINK_STATE); - usb_clear_port_feature(hub->hdev, port1, - USB_PORT_FEAT_C_CONNECTION); - } - if (udev) - usb_set_device_state(udev, *status - ? USB_STATE_NOTATTACHED - : USB_STATE_DEFAULT); - break; - } -} - /* Handle port reset and port warm(BH) reset (for USB3 protocol ports) */ static int hub_port_reset(struct usb_hub *hub, int port1, struct usb_device *udev, unsigned int delay, bool warm) @@ -2767,13 +2732,10 @@ static int hub_port_reset(struct usb_hub *hub, int port1, * If the caller hasn't explicitly requested a warm reset, * double check and see if one is needed. */ - status = hub_port_status(hub, port1, - &portstatus, &portchange); - if (status < 0) - goto done; - - if (hub_port_warm_reset_required(hub, port1, portstatus)) - warm = true; + if (hub_port_status(hub, port1, &portstatus, &portchange) == 0) + if (hub_port_warm_reset_required(hub, port1, + portstatus)) + warm = true; } clear_bit(port1, hub->warm_reset_bits); @@ -2799,11 +2761,19 @@ static int hub_port_reset(struct usb_hub *hub, int port1, /* Check for disconnect or reset */ if (status == 0 || status == -ENOTCONN || status == -ENODEV) { - hub_port_finish_reset(hub, port1, udev, &status); + usb_clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_RESET); if (!hub_is_superspeed(hub->hdev)) goto done; + usb_clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_BH_PORT_RESET); + usb_clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_PORT_LINK_STATE); + usb_clear_port_feature(hub->hdev, port1, + USB_PORT_FEAT_C_CONNECTION); + /* * If a USB 3.0 device migrates from reset to an error * state, re-issue the warm reset. @@ -2836,6 +2806,26 @@ static int hub_port_reset(struct usb_hub *hub, int port1, dev_err(&port_dev->dev, "Cannot enable. Maybe the USB cable is bad?\n"); done: + if (status == 0) { + /* TRSTRCY = 10 ms; plus some extra */ + msleep(10 + 40); + if (udev) { + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + update_devnum(udev, 0); + /* The xHC may think the device is already reset, + * so ignore the status. + */ + if (hcd->driver->reset_device) + hcd->driver->reset_device(hcd, udev); + + usb_set_device_state(udev, USB_STATE_DEFAULT); + } + } else { + if (udev) + usb_set_device_state(udev, USB_STATE_NOTATTACHED); + } + if (!hub_is_superspeed(hub->hdev)) up_read(&ehci_cf_port_reset_rwsem); diff --git a/drivers/usb/dwc2/Kconfig b/drivers/usb/dwc2/Kconfig index 1bcb36ae6505..fd95ba6ec317 100644 --- a/drivers/usb/dwc2/Kconfig +++ b/drivers/usb/dwc2/Kconfig @@ -50,18 +50,10 @@ config USB_DWC2_DUAL_ROLE option requires USB_GADGET to be enabled. endchoice -config USB_DWC2_PLATFORM - tristate "DWC2 Platform" - default USB_DWC2_HOST || USB_DWC2_PERIPHERAL - help - The Designware USB2.0 platform interface module for - controllers directly connected to the CPU. - config USB_DWC2_PCI tristate "DWC2 PCI" depends on PCI default n - select USB_DWC2_PLATFORM select NOP_USB_XCEIV help The Designware USB2.0 PCI interface module for controllers diff --git a/drivers/usb/dwc2/Makefile b/drivers/usb/dwc2/Makefile index f07b425eaff3..50fdaace1e73 100644 --- a/drivers/usb/dwc2/Makefile +++ b/drivers/usb/dwc2/Makefile @@ -2,7 +2,7 @@ ccflags-$(CONFIG_USB_DWC2_DEBUG) += -DDEBUG ccflags-$(CONFIG_USB_DWC2_VERBOSE) += -DVERBOSE_DEBUG obj-$(CONFIG_USB_DWC2) += dwc2.o -dwc2-y := core.o core_intr.o +dwc2-y := core.o core_intr.o platform.o ifneq ($(filter y,$(CONFIG_USB_DWC2_HOST) $(CONFIG_USB_DWC2_DUAL_ROLE)),) dwc2-y += hcd.o hcd_intr.o @@ -13,6 +13,10 @@ ifneq ($(filter y,$(CONFIG_USB_DWC2_PERIPHERAL) $(CONFIG_USB_DWC2_DUAL_ROLE)),) dwc2-y += gadget.o endif +ifneq ($(CONFIG_DEBUG_FS),) + dwc2-y += debugfs.o +endif + # NOTE: The previous s3c-hsotg peripheral mode only driver has been moved to # this location and renamed gadget.c. When building for dynamically linked # modules, dwc2.ko will get built for host mode, peripheral mode, and dual-role @@ -21,6 +25,3 @@ endif obj-$(CONFIG_USB_DWC2_PCI) += dwc2_pci.o dwc2_pci-y := pci.o - -obj-$(CONFIG_USB_DWC2_PLATFORM) += dwc2_platform.o -dwc2_platform-y := platform.o diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index d5197d492e21..e5b546f1152e 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -56,6 +56,389 @@ #include "core.h" #include "hcd.h" +#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) +/** + * dwc2_backup_host_registers() - Backup controller host registers. + * When suspending usb bus, registers needs to be backuped + * if controller power is disabled once suspended. + * + * @hsotg: Programming view of the DWC_otg controller + */ +static int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg) +{ + struct dwc2_hregs_backup *hr; + int i; + + dev_dbg(hsotg->dev, "%s\n", __func__); + + /* Backup Host regs */ + hr = hsotg->hr_backup; + if (!hr) { + hr = devm_kzalloc(hsotg->dev, sizeof(*hr), GFP_KERNEL); + if (!hr) { + dev_err(hsotg->dev, "%s: can't allocate host regs\n", + __func__); + return -ENOMEM; + } + + hsotg->hr_backup = hr; + } + hr->hcfg = readl(hsotg->regs + HCFG); + hr->haintmsk = readl(hsotg->regs + HAINTMSK); + for (i = 0; i < hsotg->core_params->host_channels; ++i) + hr->hcintmsk[i] = readl(hsotg->regs + HCINTMSK(i)); + + hr->hprt0 = readl(hsotg->regs + HPRT0); + hr->hfir = readl(hsotg->regs + HFIR); + + return 0; +} + +/** + * dwc2_restore_host_registers() - Restore controller host registers. + * When resuming usb bus, device registers needs to be restored + * if controller power were disabled. + * + * @hsotg: Programming view of the DWC_otg controller + */ +static int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg) +{ + struct dwc2_hregs_backup *hr; + int i; + + dev_dbg(hsotg->dev, "%s\n", __func__); + + /* Restore host regs */ + hr = hsotg->hr_backup; + if (!hr) { + dev_err(hsotg->dev, "%s: no host registers to restore\n", + __func__); + return -EINVAL; + } + + writel(hr->hcfg, hsotg->regs + HCFG); + writel(hr->haintmsk, hsotg->regs + HAINTMSK); + + for (i = 0; i < hsotg->core_params->host_channels; ++i) + writel(hr->hcintmsk[i], hsotg->regs + HCINTMSK(i)); + + writel(hr->hprt0, hsotg->regs + HPRT0); + writel(hr->hfir, hsotg->regs + HFIR); + + return 0; +} +#else +static inline int dwc2_backup_host_registers(struct dwc2_hsotg *hsotg) +{ return 0; } + +static inline int dwc2_restore_host_registers(struct dwc2_hsotg *hsotg) +{ return 0; } +#endif + +#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \ + IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) +/** + * dwc2_backup_device_registers() - Backup controller device registers. + * When suspending usb bus, registers needs to be backuped + * if controller power is disabled once suspended. + * + * @hsotg: Programming view of the DWC_otg controller + */ +static int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg) +{ + struct dwc2_dregs_backup *dr; + int i; + + dev_dbg(hsotg->dev, "%s\n", __func__); + + /* Backup dev regs */ + dr = hsotg->dr_backup; + if (!dr) { + dr = devm_kzalloc(hsotg->dev, sizeof(*dr), GFP_KERNEL); + if (!dr) { + dev_err(hsotg->dev, "%s: can't allocate device regs\n", + __func__); + return -ENOMEM; + } + + hsotg->dr_backup = dr; + } + + 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); + + for (i = 0; i < hsotg->num_of_eps; i++) { + /* Backup IN EPs */ + dr->diepctl[i] = readl(hsotg->regs + DIEPCTL(i)); + + /* Ensure DATA PID is correctly configured */ + if (dr->diepctl[i] & DXEPCTL_DPID) + dr->diepctl[i] |= DXEPCTL_SETD1PID; + else + dr->diepctl[i] |= DXEPCTL_SETD0PID; + + dr->dieptsiz[i] = readl(hsotg->regs + DIEPTSIZ(i)); + dr->diepdma[i] = readl(hsotg->regs + DIEPDMA(i)); + + /* Backup OUT EPs */ + dr->doepctl[i] = readl(hsotg->regs + DOEPCTL(i)); + + /* Ensure DATA PID is correctly configured */ + if (dr->doepctl[i] & DXEPCTL_DPID) + dr->doepctl[i] |= DXEPCTL_SETD1PID; + else + dr->doepctl[i] |= DXEPCTL_SETD0PID; + + dr->doeptsiz[i] = readl(hsotg->regs + DOEPTSIZ(i)); + dr->doepdma[i] = readl(hsotg->regs + DOEPDMA(i)); + } + + return 0; +} + +/** + * dwc2_restore_device_registers() - Restore controller device registers. + * When resuming usb bus, device registers needs to be restored + * if controller power were disabled. + * + * @hsotg: Programming view of the DWC_otg controller + */ +static int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg) +{ + struct dwc2_dregs_backup *dr; + u32 dctl; + int i; + + dev_dbg(hsotg->dev, "%s\n", __func__); + + /* Restore dev regs */ + dr = hsotg->dr_backup; + if (!dr) { + dev_err(hsotg->dev, "%s: no device registers to restore\n", + __func__); + return -EINVAL; + } + + 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); + + 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)); + + /* 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)); + } + + /* Set the Power-On Programming done bit */ + dctl = readl(hsotg->regs + DCTL); + dctl |= DCTL_PWRONPRGDONE; + writel(dctl, hsotg->regs + DCTL); + + return 0; +} +#else +static inline int dwc2_backup_device_registers(struct dwc2_hsotg *hsotg) +{ return 0; } + +static inline int dwc2_restore_device_registers(struct dwc2_hsotg *hsotg) +{ return 0; } +#endif + +/** + * dwc2_backup_global_registers() - Backup global controller registers. + * When suspending usb bus, registers needs to be backuped + * if controller power is disabled once suspended. + * + * @hsotg: Programming view of the DWC_otg controller + */ +static int dwc2_backup_global_registers(struct dwc2_hsotg *hsotg) +{ + struct dwc2_gregs_backup *gr; + int i; + + /* Backup global regs */ + gr = hsotg->gr_backup; + if (!gr) { + gr = devm_kzalloc(hsotg->dev, sizeof(*gr), GFP_KERNEL); + if (!gr) { + dev_err(hsotg->dev, "%s: can't allocate global regs\n", + __func__); + return -ENOMEM; + } + + hsotg->gr_backup = gr; + } + + 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); + for (i = 0; i < MAX_EPS_CHANNELS; i++) + gr->dtxfsiz[i] = readl(hsotg->regs + DPTXFSIZN(i)); + + return 0; +} + +/** + * dwc2_restore_global_registers() - Restore controller global registers. + * When resuming usb bus, device registers needs to be restored + * if controller power were disabled. + * + * @hsotg: Programming view of the DWC_otg controller + */ +static int dwc2_restore_global_registers(struct dwc2_hsotg *hsotg) +{ + struct dwc2_gregs_backup *gr; + int i; + + dev_dbg(hsotg->dev, "%s\n", __func__); + + /* Restore global regs */ + gr = hsotg->gr_backup; + if (!gr) { + dev_err(hsotg->dev, "%s: no global registers to restore\n", + __func__); + return -EINVAL; + } + + 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); + for (i = 0; i < MAX_EPS_CHANNELS; i++) + writel(gr->dtxfsiz[i], hsotg->regs + DPTXFSIZN(i)); + + return 0; +} + +/** + * dwc2_exit_hibernation() - Exit controller from Partial Power Down. + * + * @hsotg: Programming view of the DWC_otg controller + * @restore: Controller registers need to be restored + */ +int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore) +{ + u32 pcgcctl; + int ret = 0; + + if (!hsotg->core_params->hibernation) + return -ENOTSUPP; + + pcgcctl = readl(hsotg->regs + PCGCTL); + pcgcctl &= ~PCGCTL_STOPPCLK; + writel(pcgcctl, hsotg->regs + PCGCTL); + + pcgcctl = readl(hsotg->regs + PCGCTL); + pcgcctl &= ~PCGCTL_PWRCLMP; + writel(pcgcctl, hsotg->regs + PCGCTL); + + pcgcctl = readl(hsotg->regs + PCGCTL); + pcgcctl &= ~PCGCTL_RSTPDWNMODULE; + writel(pcgcctl, hsotg->regs + PCGCTL); + + udelay(100); + if (restore) { + ret = dwc2_restore_global_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore registers\n", + __func__); + return ret; + } + if (dwc2_is_host_mode(hsotg)) { + ret = dwc2_restore_host_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore host registers\n", + __func__); + return ret; + } + } else { + ret = dwc2_restore_device_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to restore device registers\n", + __func__); + return ret; + } + } + } + + return ret; +} + +/** + * dwc2_enter_hibernation() - Put controller in Partial Power Down. + * + * @hsotg: Programming view of the DWC_otg controller + */ +int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg) +{ + u32 pcgcctl; + int ret = 0; + + if (!hsotg->core_params->hibernation) + return -ENOTSUPP; + + /* Backup all registers */ + ret = dwc2_backup_global_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup global registers\n", + __func__); + return ret; + } + + if (dwc2_is_host_mode(hsotg)) { + ret = dwc2_backup_host_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup host registers\n", + __func__); + return ret; + } + } else { + ret = dwc2_backup_device_registers(hsotg); + if (ret) { + dev_err(hsotg->dev, "%s: failed to backup device registers\n", + __func__); + return ret; + } + } + + /* Put the controller in low power state */ + pcgcctl = readl(hsotg->regs + PCGCTL); + + pcgcctl |= PCGCTL_PWRCLMP; + writel(pcgcctl, hsotg->regs + PCGCTL); + ndelay(20); + + pcgcctl |= PCGCTL_RSTPDWNMODULE; + writel(pcgcctl, hsotg->regs + PCGCTL); + ndelay(20); + + pcgcctl |= PCGCTL_STOPPCLK; + writel(pcgcctl, hsotg->regs + PCGCTL); + + return ret; +} + /** * dwc2_enable_common_interrupts() - Initializes the commmon interrupts, * used in both device and host modes @@ -77,8 +460,10 @@ static void dwc2_enable_common_interrupts(struct dwc2_hsotg *hsotg) if (hsotg->core_params->dma_enable <= 0) intmsk |= GINTSTS_RXFLVL; + if (hsotg->core_params->external_id_pin_ctl <= 0) + intmsk |= GINTSTS_CONIDSTSCHNG; - intmsk |= GINTSTS_CONIDSTSCHNG | GINTSTS_WKUPINT | GINTSTS_USBSUSP | + intmsk |= GINTSTS_WKUPINT | GINTSTS_USBSUSP | GINTSTS_SESSREQINT; writel(intmsk, hsotg->regs + GINTMSK); @@ -2602,6 +2987,40 @@ static void dwc2_set_param_uframe_sched(struct dwc2_hsotg *hsotg, int val) hsotg->core_params->uframe_sched = val; } +static void dwc2_set_param_external_id_pin_ctl(struct dwc2_hsotg *hsotg, + int val) +{ + if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) { + if (val >= 0) { + dev_err(hsotg->dev, + "'%d' invalid for parameter external_id_pin_ctl\n", + val); + dev_err(hsotg->dev, "external_id_pin_ctl must be 0 or 1\n"); + } + val = 0; + dev_dbg(hsotg->dev, "Setting external_id_pin_ctl to %d\n", val); + } + + hsotg->core_params->external_id_pin_ctl = val; +} + +static void dwc2_set_param_hibernation(struct dwc2_hsotg *hsotg, + int val) +{ + if (DWC2_OUT_OF_BOUNDS(val, 0, 1)) { + if (val >= 0) { + dev_err(hsotg->dev, + "'%d' invalid for parameter hibernation\n", + val); + dev_err(hsotg->dev, "hibernation must be 0 or 1\n"); + } + val = 0; + dev_dbg(hsotg->dev, "Setting hibernation to %d\n", val); + } + + hsotg->core_params->hibernation = val; +} + /* * This function is called during module intialization to pass module parameters * for the DWC_otg core. @@ -2646,6 +3065,8 @@ void dwc2_set_parameters(struct dwc2_hsotg *hsotg, dwc2_set_param_ahbcfg(hsotg, params->ahbcfg); dwc2_set_param_otg_ver(hsotg, params->otg_ver); dwc2_set_param_uframe_sched(hsotg, params->uframe_sched); + dwc2_set_param_external_id_pin_ctl(hsotg, params->external_id_pin_ctl); + dwc2_set_param_hibernation(hsotg, params->hibernation); } /** @@ -2814,6 +3235,22 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) return 0; } +/* + * Sets all parameters to the given value. + * + * Assumes that the dwc2_core_params struct contains only integers. + */ +void dwc2_set_all_params(struct dwc2_core_params *params, int value) +{ + int *p = (int *)params; + size_t size = sizeof(*params) / sizeof(*p); + int i; + + for (i = 0; i < size; i++) + p[i] = value; +} + + u16 dwc2_get_otg_version(struct dwc2_hsotg *hsotg) { return hsotg->core_params->otg_ver == 1 ? 0x0200 : 0x0103; diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 836c012c7707..53b8de03f102 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -331,6 +331,17 @@ enum dwc2_ep0_state { * by the driver and are ignored in this * configuration value. * @uframe_sched: True to enable the microframe scheduler + * @external_id_pin_ctl: Specifies whether ID pin is handled externally. + * Disable CONIDSTSCHNG controller interrupt in such + * case. + * 0 - No (default) + * 1 - Yes + * @hibernation: Specifies whether the controller support hibernation. + * If hibernation is enabled, the controller will enter + * hibernation in both peripheral and host mode when + * needed. + * 0 - No (default) + * 1 - Yes * * The following parameters may be specified when starting the module. These * parameters define how the DWC_otg controller should be configured. A @@ -368,6 +379,8 @@ struct dwc2_core_params { int reload_ctl; int ahbcfg; int uframe_sched; + int external_id_pin_ctl; + int hibernation; }; /** @@ -452,6 +465,82 @@ struct dwc2_hw_params { #define DWC2_CTRL_BUFF_SIZE 8 /** + * struct dwc2_gregs_backup - Holds global registers state before entering partial + * power down + * @gotgctl: Backup of GOTGCTL register + * @gintmsk: Backup of GINTMSK register + * @gahbcfg: Backup of GAHBCFG register + * @gusbcfg: Backup of GUSBCFG register + * @grxfsiz: Backup of GRXFSIZ register + * @gnptxfsiz: Backup of GNPTXFSIZ register + * @gi2cctl: Backup of GI2CCTL register + * @hptxfsiz: Backup of HPTXFSIZ register + * @gdfifocfg: Backup of GDFIFOCFG register + * @dtxfsiz: Backup of DTXFSIZ registers for each endpoint + * @gpwrdn: Backup of GPWRDN register + */ +struct dwc2_gregs_backup { + u32 gotgctl; + u32 gintmsk; + u32 gahbcfg; + u32 gusbcfg; + u32 grxfsiz; + u32 gnptxfsiz; + u32 gi2cctl; + u32 hptxfsiz; + u32 pcgcctl; + u32 gdfifocfg; + u32 dtxfsiz[MAX_EPS_CHANNELS]; + u32 gpwrdn; +}; + +/** + * struct dwc2_dregs_backup - Holds device registers state before entering partial + * power down + * @dcfg: Backup of DCFG register + * @dctl: Backup of DCTL register + * @daintmsk: Backup of DAINTMSK register + * @diepmsk: Backup of DIEPMSK register + * @doepmsk: Backup of DOEPMSK register + * @diepctl: Backup of DIEPCTL register + * @dieptsiz: Backup of DIEPTSIZ register + * @diepdma: Backup of DIEPDMA register + * @doepctl: Backup of DOEPCTL register + * @doeptsiz: Backup of DOEPTSIZ register + * @doepdma: Backup of DOEPDMA register + */ +struct dwc2_dregs_backup { + u32 dcfg; + u32 dctl; + u32 daintmsk; + u32 diepmsk; + u32 doepmsk; + u32 diepctl[MAX_EPS_CHANNELS]; + u32 dieptsiz[MAX_EPS_CHANNELS]; + u32 diepdma[MAX_EPS_CHANNELS]; + u32 doepctl[MAX_EPS_CHANNELS]; + u32 doeptsiz[MAX_EPS_CHANNELS]; + u32 doepdma[MAX_EPS_CHANNELS]; +}; + +/** + * struct dwc2_hregs_backup - Holds host registers state before entering partial + * power down + * @hcfg: Backup of HCFG register + * @haintmsk: Backup of HAINTMSK register + * @hcintmsk: Backup of HCINTMSK register + * @hptr0: Backup of HPTR0 register + * @hfir: Backup of HFIR register + */ +struct dwc2_hregs_backup { + u32 hcfg; + u32 haintmsk; + u32 hcintmsk[MAX_EPS_CHANNELS]; + u32 hprt0; + u32 hfir; +}; + +/** * struct dwc2_hsotg - Holds the state of the driver, including the non-periodic * and periodic schedules * @@ -481,6 +570,9 @@ struct dwc2_hw_params { * interrupt * @wkp_timer: Timer object for handling Wakeup Detected interrupt * @lx_state: Lx state of connected device + * @gregs_backup: Backup of global registers during suspend + * @dregs_backup: Backup of device registers during suspend + * @hregs_backup: Backup of host registers during suspend * * These are for host mode: * @@ -613,11 +705,12 @@ struct dwc2_hsotg { struct work_struct wf_otg; struct timer_list wkp_timer; enum dwc2_lx_state lx_state; + struct dwc2_gregs_backup *gr_backup; + struct dwc2_dregs_backup *dr_backup; + struct dwc2_hregs_backup *hr_backup; struct dentry *debug_root; - struct dentry *debug_file; - struct dentry *debug_testmode; - struct dentry *debug_fifo; + struct debugfs_regset32 *regset; /* DWC OTG HW Release versions */ #define DWC2_CORE_REV_2_71a 0x4f54271a @@ -751,6 +844,8 @@ enum dwc2_halt_status { * and the DWC_otg controller */ extern void dwc2_core_host_init(struct dwc2_hsotg *hsotg); +extern int dwc2_enter_hibernation(struct dwc2_hsotg *hsotg); +extern int dwc2_exit_hibernation(struct dwc2_hsotg *hsotg, bool restore); /* * Host core Functions. @@ -983,6 +1078,15 @@ extern void dwc2_set_param_ahbcfg(struct dwc2_hsotg *hsotg, int val); extern void dwc2_set_param_otg_ver(struct dwc2_hsotg *hsotg, int val); +extern void dwc2_set_parameters(struct dwc2_hsotg *hsotg, + const struct dwc2_core_params *params); + +extern void dwc2_set_all_params(struct dwc2_core_params *params, int value); + +extern int dwc2_get_hwparams(struct dwc2_hsotg *hsotg); + + + /* * Dump core registers and SPRAM */ @@ -1005,6 +1109,8 @@ extern void s3c_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); +#define dwc2_is_device_connected(hsotg) (hsotg->connected) #else static inline int s3c_hsotg_remove(struct dwc2_hsotg *dwc2) { return 0; } @@ -1018,6 +1124,10 @@ static inline void s3c_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, + int testmode) +{ return 0; } +#define dwc2_is_device_connected(hsotg) (0) #endif #if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) @@ -1025,14 +1135,12 @@ extern int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg); extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg); extern void dwc2_hcd_start(struct dwc2_hsotg *hsotg); #else -static inline void dwc2_set_all_params(struct dwc2_core_params *params, int value) {} static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg) { return 0; } static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg) {} static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {} static inline void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) {} -static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, - const struct dwc2_core_params *params) +static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq) { return 0; } #endif diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c index 6cf047878dba..927be1e8b3dc 100644 --- a/drivers/usb/dwc2/core_intr.c +++ b/drivers/usb/dwc2/core_intr.c @@ -334,6 +334,7 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg) */ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) { + int ret; dev_dbg(hsotg->dev, "++Resume or Remote Wakeup Detected Interrupt++\n"); dev_dbg(hsotg->dev, "%s lxstate = %d\n", __func__, hsotg->lx_state); @@ -345,6 +346,11 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg) /* Clear Remote Wakeup Signaling */ dctl &= ~DCTL_RMTWKUPSIG; writel(dctl, hsotg->regs + DCTL); + ret = dwc2_exit_hibernation(hsotg, true); + if (ret && (ret != -ENOTSUPP)) + dev_err(hsotg->dev, "exit hibernation failed\n"); + + call_gadget(hsotg, resume); } /* Change to L0 state */ hsotg->lx_state = DWC2_L0; @@ -397,6 +403,7 @@ static void dwc2_handle_disconnect_intr(struct dwc2_hsotg *hsotg) static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) { u32 dsts; + int ret; dev_dbg(hsotg->dev, "USB SUSPEND\n"); @@ -411,10 +418,43 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) "DSTS.Suspend Status=%d HWCFG4.Power Optimize=%d\n", !!(dsts & DSTS_SUSPSTS), hsotg->hw_params.power_optimized); + if ((dsts & DSTS_SUSPSTS) && hsotg->hw_params.power_optimized) { + /* Ignore suspend request before enumeration */ + if (!dwc2_is_device_connected(hsotg)) { + dev_dbg(hsotg->dev, + "ignore suspend request before enumeration\n"); + goto clear_int; + } + + ret = dwc2_enter_hibernation(hsotg); + if (ret) { + if (ret != -ENOTSUPP) + dev_err(hsotg->dev, + "enter hibernation failed\n"); + goto skip_power_saving; + } + + udelay(100); + + /* Ask phy to be suspended */ + if (!IS_ERR_OR_NULL(hsotg->uphy)) + usb_phy_set_suspend(hsotg->uphy, true); +skip_power_saving: + /* + * Change to L2 (suspend) state before releasing + * spinlock + */ + hsotg->lx_state = DWC2_L2; + + /* Call gadget suspend callback */ + call_gadget(hsotg, suspend); + } } else { if (hsotg->op_state == OTG_STATE_A_PERIPHERAL) { dev_dbg(hsotg->dev, "a_peripheral->a_host\n"); + /* Change to L2 (suspend) state */ + hsotg->lx_state = DWC2_L2; /* Clear the a_peripheral flag, back to a_host */ spin_unlock(&hsotg->lock); dwc2_hcd_start(hsotg); @@ -423,9 +463,7 @@ static void dwc2_handle_usb_suspend_intr(struct dwc2_hsotg *hsotg) } } - /* Change to L2 (suspend) state */ - hsotg->lx_state = DWC2_L2; - +clear_int: /* Clear interrupt */ writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS); } @@ -522,4 +560,3 @@ out: spin_unlock(&hsotg->lock); return retval; } -EXPORT_SYMBOL_GPL(dwc2_handle_common_intr); diff --git a/drivers/usb/dwc2/debug.h b/drivers/usb/dwc2/debug.h new file mode 100644 index 000000000000..12dbd1daec87 --- /dev/null +++ b/drivers/usb/dwc2/debug.h @@ -0,0 +1,27 @@ +/** + * debug.h - Designware USB2 DRD controller debug header + * + * Copyright (C) 2015 Intel Corporation + * Mian Yousaf Kaukab <yousaf.kaukab@intel.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * 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 "core.h" + +#ifdef CONFIG_DEBUG_FS +extern int dwc2_debugfs_init(struct dwc2_hsotg *); +extern void dwc2_debugfs_exit(struct dwc2_hsotg *); +#else +static inline int dwc2_debugfs_init(struct dwc2_hsotg *hsotg) +{ return 0; } +static inline void dwc2_debugfs_exit(struct dwc2_hsotg *hsotg) +{ } +#endif diff --git a/drivers/usb/dwc2/debugfs.c b/drivers/usb/dwc2/debugfs.c new file mode 100644 index 000000000000..ef2ee3d9a25d --- /dev/null +++ b/drivers/usb/dwc2/debugfs.c @@ -0,0 +1,771 @@ +/** + * debugfs.c - Designware USB2 DRD controller debugfs + * + * Copyright (C) 2015 Intel Corporation + * Mian Yousaf Kaukab <yousaf.kaukab@intel.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * 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 <linux/spinlock.h> +#include <linux/debugfs.h> +#include <linux/seq_file.h> +#include <linux/uaccess.h> + +#include "core.h" +#include "debug.h" + +#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || \ + IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE) +/** + * testmode_write - debugfs: change usb test mode + * @seq: The seq file to write to. + * @v: Unused parameter. + * + * This debugfs entry modify the current usb test mode. + */ +static ssize_t testmode_write(struct file *file, const char __user *ubuf, size_t + count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct dwc2_hsotg *hsotg = s->private; + unsigned long flags; + u32 testmode = 0; + char buf[32]; + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + return -EFAULT; + + if (!strncmp(buf, "test_j", 6)) + testmode = TEST_J; + else if (!strncmp(buf, "test_k", 6)) + testmode = TEST_K; + else if (!strncmp(buf, "test_se0_nak", 12)) + testmode = TEST_SE0_NAK; + else if (!strncmp(buf, "test_packet", 11)) + testmode = TEST_PACKET; + else if (!strncmp(buf, "test_force_enable", 17)) + testmode = TEST_FORCE_EN; + else + testmode = 0; + + spin_lock_irqsave(&hsotg->lock, flags); + s3c_hsotg_set_test_mode(hsotg, testmode); + spin_unlock_irqrestore(&hsotg->lock, flags); + return count; +} + +/** + * testmode_show - debugfs: show usb test mode state + * @seq: The seq file to write to. + * @v: Unused parameter. + * + * This debugfs entry shows which usb test mode is currently enabled. + */ +static int testmode_show(struct seq_file *s, void *unused) +{ + struct dwc2_hsotg *hsotg = s->private; + unsigned long flags; + int dctl; + + spin_lock_irqsave(&hsotg->lock, flags); + dctl = readl(hsotg->regs + DCTL); + dctl &= DCTL_TSTCTL_MASK; + dctl >>= DCTL_TSTCTL_SHIFT; + spin_unlock_irqrestore(&hsotg->lock, flags); + + switch (dctl) { + case 0: + seq_puts(s, "no test\n"); + break; + case TEST_J: + seq_puts(s, "test_j\n"); + break; + case TEST_K: + seq_puts(s, "test_k\n"); + break; + case TEST_SE0_NAK: + seq_puts(s, "test_se0_nak\n"); + break; + case TEST_PACKET: + seq_puts(s, "test_packet\n"); + break; + case TEST_FORCE_EN: + seq_puts(s, "test_force_enable\n"); + break; + default: + seq_printf(s, "UNKNOWN %d\n", dctl); + } + + return 0; +} + +static int testmode_open(struct inode *inode, struct file *file) +{ + return single_open(file, testmode_show, inode->i_private); +} + +static const struct file_operations testmode_fops = { + .owner = THIS_MODULE, + .open = testmode_open, + .write = testmode_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/** + * state_show - debugfs: show overall driver and device state. + * @seq: The seq file to write to. + * @v: Unused parameter. + * + * This debugfs entry shows the overall state of the hardware and + * some general information about each of the endpoints available + * to the system. + */ +static int state_show(struct seq_file *seq, void *v) +{ + struct dwc2_hsotg *hsotg = seq->private; + void __iomem *regs = hsotg->regs; + int idx; + + seq_printf(seq, "DCFG=0x%08x, DCTL=0x%08x, DSTS=0x%08x\n", + readl(regs + DCFG), + readl(regs + DCTL), + readl(regs + DSTS)); + + seq_printf(seq, "DIEPMSK=0x%08x, DOEPMASK=0x%08x\n", + readl(regs + DIEPMSK), readl(regs + DOEPMSK)); + + seq_printf(seq, "GINTMSK=0x%08x, GINTSTS=0x%08x\n", + readl(regs + GINTMSK), + readl(regs + GINTSTS)); + + seq_printf(seq, "DAINTMSK=0x%08x, DAINT=0x%08x\n", + readl(regs + DAINTMSK), + readl(regs + DAINT)); + + seq_printf(seq, "GNPTXSTS=0x%08x, GRXSTSR=%08x\n", + readl(regs + GNPTXSTS), + 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)); + + seq_printf(seq, "ep%d: DIEPCTL=0x%08x, DOEPCTL=0x%08x", + idx, in, out); + + in = readl(regs + DIEPTSIZ(idx)); + out = readl(regs + DOEPTSIZ(idx)); + + seq_printf(seq, ", DIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x", + in, out); + + seq_puts(seq, "\n"); + } + + return 0; +} + +static int state_open(struct inode *inode, struct file *file) +{ + return single_open(file, state_show, inode->i_private); +} + +static const struct file_operations state_fops = { + .owner = THIS_MODULE, + .open = state_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/** + * fifo_show - debugfs: show the fifo information + * @seq: The seq_file to write data to. + * @v: Unused parameter. + * + * Show the FIFO information for the overall fifo and all the + * periodic transmission FIFOs. + */ +static int fifo_show(struct seq_file *seq, void *v) +{ + struct dwc2_hsotg *hsotg = seq->private; + void __iomem *regs = hsotg->regs; + u32 val; + int idx; + + seq_puts(seq, "Non-periodic FIFOs:\n"); + seq_printf(seq, "RXFIFO: Size %d\n", readl(regs + GRXFSIZ)); + + val = readl(regs + GNPTXFSIZ); + seq_printf(seq, "NPTXFIFO: Size %d, Start 0x%08x\n", + val >> FIFOSIZE_DEPTH_SHIFT, + val & FIFOSIZE_DEPTH_MASK); + + seq_puts(seq, "\nPeriodic TXFIFOs:\n"); + + for (idx = 1; idx < hsotg->num_of_eps; idx++) { + val = readl(regs + DPTXFSIZN(idx)); + + seq_printf(seq, "\tDPTXFIFO%2d: Size %d, Start 0x%08x\n", idx, + val >> FIFOSIZE_DEPTH_SHIFT, + val & FIFOSIZE_STARTADDR_MASK); + } + + return 0; +} + +static int fifo_open(struct inode *inode, struct file *file) +{ + return single_open(file, fifo_show, inode->i_private); +} + +static const struct file_operations fifo_fops = { + .owner = THIS_MODULE, + .open = fifo_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static const char *decode_direction(int is_in) +{ + return is_in ? "in" : "out"; +} + +/** + * ep_show - debugfs: show the state of an endpoint. + * @seq: The seq_file to write data to. + * @v: Unused parameter. + * + * This debugfs entry shows the state of the given endpoint (one is + * registered for each available). + */ +static int ep_show(struct seq_file *seq, void *v) +{ + struct s3c_hsotg_ep *ep = seq->private; + struct dwc2_hsotg *hsotg = ep->parent; + struct s3c_hsotg_req *req; + void __iomem *regs = hsotg->regs; + int index = ep->index; + int show_limit = 15; + unsigned long flags; + + seq_printf(seq, "Endpoint index %d, named %s, dir %s:\n", + ep->index, ep->ep.name, decode_direction(ep->dir_in)); + + /* first show the register state */ + + seq_printf(seq, "\tDIEPCTL=0x%08x, DOEPCTL=0x%08x\n", + readl(regs + DIEPCTL(index)), + readl(regs + DOEPCTL(index))); + + seq_printf(seq, "\tDIEPDMA=0x%08x, DOEPDMA=0x%08x\n", + readl(regs + DIEPDMA(index)), + readl(regs + DOEPDMA(index))); + + seq_printf(seq, "\tDIEPINT=0x%08x, DOEPINT=0x%08x\n", + readl(regs + DIEPINT(index)), + readl(regs + DOEPINT(index))); + + seq_printf(seq, "\tDIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x\n", + readl(regs + DIEPTSIZ(index)), + readl(regs + DOEPTSIZ(index))); + + seq_puts(seq, "\n"); + seq_printf(seq, "mps %d\n", ep->ep.maxpacket); + seq_printf(seq, "total_data=%ld\n", ep->total_data); + + seq_printf(seq, "request list (%p,%p):\n", + ep->queue.next, ep->queue.prev); + + spin_lock_irqsave(&hsotg->lock, flags); + + list_for_each_entry(req, &ep->queue, queue) { + if (--show_limit < 0) { + seq_puts(seq, "not showing more requests...\n"); + break; + } + + seq_printf(seq, "%c req %p: %d bytes @%p, ", + req == ep->req ? '*' : ' ', + req, req->req.length, req->req.buf); + seq_printf(seq, "%d done, res %d\n", + req->req.actual, req->req.status); + } + + spin_unlock_irqrestore(&hsotg->lock, flags); + + return 0; +} + +static int ep_open(struct inode *inode, struct file *file) +{ + return single_open(file, ep_show, inode->i_private); +} + +static const struct file_operations ep_fops = { + .owner = THIS_MODULE, + .open = ep_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/** + * s3c_hsotg_create_debug - create debugfs directory and files + * @hsotg: The driver state + * + * Create the debugfs files to allow the user to get information + * about the state of the system. The directory name is created + * 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) +{ + struct dentry *root; + struct dentry *file; + unsigned epidx; + + root = hsotg->debug_root; + + /* create general state file */ + + file = debugfs_create_file("state", S_IRUGO, root, hsotg, &state_fops); + if (IS_ERR(file)) + dev_err(hsotg->dev, "%s: failed to create state\n", __func__); + + file = debugfs_create_file("testmode", S_IRUGO | S_IWUSR, root, hsotg, + &testmode_fops); + if (IS_ERR(file)) + dev_err(hsotg->dev, "%s: failed to create testmode\n", + __func__); + + file = debugfs_create_file("fifo", S_IRUGO, root, hsotg, &fifo_fops); + if (IS_ERR(file)) + dev_err(hsotg->dev, "%s: failed to create fifo\n", __func__); + + /* Create one file for each out endpoint */ + for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) { + struct s3c_hsotg_ep *ep; + + ep = hsotg->eps_out[epidx]; + if (ep) { + file = debugfs_create_file(ep->name, S_IRUGO, + root, ep, &ep_fops); + if (IS_ERR(file)) + dev_err(hsotg->dev, "failed to create %s debug file\n", + ep->name); + } + } + /* 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; + + ep = hsotg->eps_in[epidx]; + if (ep) { + file = debugfs_create_file(ep->name, S_IRUGO, + root, ep, &ep_fops); + if (IS_ERR(file)) + dev_err(hsotg->dev, "failed to create %s debug file\n", + ep->name); + } + } +} +#else +static inline void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg) {} +#endif + +/* s3c_hsotg_delete_debug is removed as cleanup in done in dwc2_debugfs_exit */ + +#define dump_register(nm) \ +{ \ + .name = #nm, \ + .offset = nm, \ +} + +static const struct debugfs_reg32 dwc2_regs[] = { + /* + * Accessing registers like this can trigger mode mismatch interrupt. + * However, according to dwc2 databook, the register access, in this + * case, is completed on the processor bus but is ignored by the core + * and does not affect its operation. + */ + dump_register(GOTGCTL), + dump_register(GOTGINT), + dump_register(GAHBCFG), + dump_register(GUSBCFG), + dump_register(GRSTCTL), + dump_register(GINTSTS), + dump_register(GINTMSK), + dump_register(GRXSTSR), + dump_register(GRXSTSP), + dump_register(GRXFSIZ), + dump_register(GNPTXFSIZ), + dump_register(GNPTXSTS), + dump_register(GI2CCTL), + dump_register(GPVNDCTL), + dump_register(GGPIO), + dump_register(GUID), + dump_register(GSNPSID), + dump_register(GHWCFG1), + dump_register(GHWCFG2), + dump_register(GHWCFG3), + dump_register(GHWCFG4), + dump_register(GLPMCFG), + dump_register(GPWRDN), + dump_register(GDFIFOCFG), + dump_register(ADPCTL), + dump_register(HPTXFSIZ), + dump_register(DPTXFSIZN(1)), + dump_register(DPTXFSIZN(2)), + dump_register(DPTXFSIZN(3)), + dump_register(DPTXFSIZN(4)), + dump_register(DPTXFSIZN(5)), + dump_register(DPTXFSIZN(6)), + dump_register(DPTXFSIZN(7)), + dump_register(DPTXFSIZN(8)), + dump_register(DPTXFSIZN(9)), + dump_register(DPTXFSIZN(10)), + dump_register(DPTXFSIZN(11)), + dump_register(DPTXFSIZN(12)), + dump_register(DPTXFSIZN(13)), + dump_register(DPTXFSIZN(14)), + dump_register(DPTXFSIZN(15)), + dump_register(DCFG), + dump_register(DCTL), + dump_register(DSTS), + dump_register(DIEPMSK), + dump_register(DOEPMSK), + dump_register(DAINT), + dump_register(DAINTMSK), + dump_register(DTKNQR1), + dump_register(DTKNQR2), + dump_register(DTKNQR3), + dump_register(DTKNQR4), + dump_register(DVBUSDIS), + dump_register(DVBUSPULSE), + dump_register(DIEPCTL(0)), + dump_register(DIEPCTL(1)), + dump_register(DIEPCTL(2)), + dump_register(DIEPCTL(3)), + dump_register(DIEPCTL(4)), + dump_register(DIEPCTL(5)), + dump_register(DIEPCTL(6)), + dump_register(DIEPCTL(7)), + dump_register(DIEPCTL(8)), + dump_register(DIEPCTL(9)), + dump_register(DIEPCTL(10)), + dump_register(DIEPCTL(11)), + dump_register(DIEPCTL(12)), + dump_register(DIEPCTL(13)), + dump_register(DIEPCTL(14)), + dump_register(DIEPCTL(15)), + dump_register(DOEPCTL(0)), + dump_register(DOEPCTL(1)), + dump_register(DOEPCTL(2)), + dump_register(DOEPCTL(3)), + dump_register(DOEPCTL(4)), + dump_register(DOEPCTL(5)), + dump_register(DOEPCTL(6)), + dump_register(DOEPCTL(7)), + dump_register(DOEPCTL(8)), + dump_register(DOEPCTL(9)), + dump_register(DOEPCTL(10)), + dump_register(DOEPCTL(11)), + dump_register(DOEPCTL(12)), + dump_register(DOEPCTL(13)), + dump_register(DOEPCTL(14)), + dump_register(DOEPCTL(15)), + dump_register(DIEPINT(0)), + dump_register(DIEPINT(1)), + dump_register(DIEPINT(2)), + dump_register(DIEPINT(3)), + dump_register(DIEPINT(4)), + dump_register(DIEPINT(5)), + dump_register(DIEPINT(6)), + dump_register(DIEPINT(7)), + dump_register(DIEPINT(8)), + dump_register(DIEPINT(9)), + dump_register(DIEPINT(10)), + dump_register(DIEPINT(11)), + dump_register(DIEPINT(12)), + dump_register(DIEPINT(13)), + dump_register(DIEPINT(14)), + dump_register(DIEPINT(15)), + dump_register(DOEPINT(0)), + dump_register(DOEPINT(1)), + dump_register(DOEPINT(2)), + dump_register(DOEPINT(3)), + dump_register(DOEPINT(4)), + dump_register(DOEPINT(5)), + dump_register(DOEPINT(6)), + dump_register(DOEPINT(7)), + dump_register(DOEPINT(8)), + dump_register(DOEPINT(9)), + dump_register(DOEPINT(10)), + dump_register(DOEPINT(11)), + dump_register(DOEPINT(12)), + dump_register(DOEPINT(13)), + dump_register(DOEPINT(14)), + dump_register(DOEPINT(15)), + dump_register(DIEPTSIZ(0)), + dump_register(DIEPTSIZ(1)), + dump_register(DIEPTSIZ(2)), + dump_register(DIEPTSIZ(3)), + dump_register(DIEPTSIZ(4)), + dump_register(DIEPTSIZ(5)), + dump_register(DIEPTSIZ(6)), + dump_register(DIEPTSIZ(7)), + dump_register(DIEPTSIZ(8)), + dump_register(DIEPTSIZ(9)), + dump_register(DIEPTSIZ(10)), + dump_register(DIEPTSIZ(11)), + dump_register(DIEPTSIZ(12)), + dump_register(DIEPTSIZ(13)), + dump_register(DIEPTSIZ(14)), + dump_register(DIEPTSIZ(15)), + dump_register(DOEPTSIZ(0)), + dump_register(DOEPTSIZ(1)), + dump_register(DOEPTSIZ(2)), + dump_register(DOEPTSIZ(3)), + dump_register(DOEPTSIZ(4)), + dump_register(DOEPTSIZ(5)), + dump_register(DOEPTSIZ(6)), + dump_register(DOEPTSIZ(7)), + dump_register(DOEPTSIZ(8)), + dump_register(DOEPTSIZ(9)), + dump_register(DOEPTSIZ(10)), + dump_register(DOEPTSIZ(11)), + dump_register(DOEPTSIZ(12)), + dump_register(DOEPTSIZ(13)), + dump_register(DOEPTSIZ(14)), + dump_register(DOEPTSIZ(15)), + dump_register(DIEPDMA(0)), + dump_register(DIEPDMA(1)), + dump_register(DIEPDMA(2)), + dump_register(DIEPDMA(3)), + dump_register(DIEPDMA(4)), + dump_register(DIEPDMA(5)), + dump_register(DIEPDMA(6)), + dump_register(DIEPDMA(7)), + dump_register(DIEPDMA(8)), + dump_register(DIEPDMA(9)), + dump_register(DIEPDMA(10)), + dump_register(DIEPDMA(11)), + dump_register(DIEPDMA(12)), + dump_register(DIEPDMA(13)), + dump_register(DIEPDMA(14)), + dump_register(DIEPDMA(15)), + dump_register(DOEPDMA(0)), + dump_register(DOEPDMA(1)), + dump_register(DOEPDMA(2)), + dump_register(DOEPDMA(3)), + dump_register(DOEPDMA(4)), + dump_register(DOEPDMA(5)), + dump_register(DOEPDMA(6)), + dump_register(DOEPDMA(7)), + dump_register(DOEPDMA(8)), + dump_register(DOEPDMA(9)), + dump_register(DOEPDMA(10)), + dump_register(DOEPDMA(11)), + dump_register(DOEPDMA(12)), + dump_register(DOEPDMA(13)), + dump_register(DOEPDMA(14)), + dump_register(DOEPDMA(15)), + dump_register(DTXFSTS(0)), + dump_register(DTXFSTS(1)), + dump_register(DTXFSTS(2)), + dump_register(DTXFSTS(3)), + dump_register(DTXFSTS(4)), + dump_register(DTXFSTS(5)), + dump_register(DTXFSTS(6)), + dump_register(DTXFSTS(7)), + dump_register(DTXFSTS(8)), + dump_register(DTXFSTS(9)), + dump_register(DTXFSTS(10)), + dump_register(DTXFSTS(11)), + dump_register(DTXFSTS(12)), + dump_register(DTXFSTS(13)), + dump_register(DTXFSTS(14)), + dump_register(DTXFSTS(15)), + dump_register(PCGCTL), + dump_register(HCFG), + dump_register(HFIR), + dump_register(HFNUM), + dump_register(HPTXSTS), + dump_register(HAINT), + dump_register(HAINTMSK), + dump_register(HFLBADDR), + dump_register(HPRT0), + dump_register(HCCHAR(0)), + dump_register(HCCHAR(1)), + dump_register(HCCHAR(2)), + dump_register(HCCHAR(3)), + dump_register(HCCHAR(4)), + dump_register(HCCHAR(5)), + dump_register(HCCHAR(6)), + dump_register(HCCHAR(7)), + dump_register(HCCHAR(8)), + dump_register(HCCHAR(9)), + dump_register(HCCHAR(10)), + dump_register(HCCHAR(11)), + dump_register(HCCHAR(12)), + dump_register(HCCHAR(13)), + dump_register(HCCHAR(14)), + dump_register(HCCHAR(15)), + dump_register(HCSPLT(0)), + dump_register(HCSPLT(1)), + dump_register(HCSPLT(2)), + dump_register(HCSPLT(3)), + dump_register(HCSPLT(4)), + dump_register(HCSPLT(5)), + dump_register(HCSPLT(6)), + dump_register(HCSPLT(7)), + dump_register(HCSPLT(8)), + dump_register(HCSPLT(9)), + dump_register(HCSPLT(10)), + dump_register(HCSPLT(11)), + dump_register(HCSPLT(12)), + dump_register(HCSPLT(13)), + dump_register(HCSPLT(14)), + dump_register(HCSPLT(15)), + dump_register(HCINT(0)), + dump_register(HCINT(1)), + dump_register(HCINT(2)), + dump_register(HCINT(3)), + dump_register(HCINT(4)), + dump_register(HCINT(5)), + dump_register(HCINT(6)), + dump_register(HCINT(7)), + dump_register(HCINT(8)), + dump_register(HCINT(9)), + dump_register(HCINT(10)), + dump_register(HCINT(11)), + dump_register(HCINT(12)), + dump_register(HCINT(13)), + dump_register(HCINT(14)), + dump_register(HCINT(15)), + dump_register(HCINTMSK(0)), + dump_register(HCINTMSK(1)), + dump_register(HCINTMSK(2)), + dump_register(HCINTMSK(3)), + dump_register(HCINTMSK(4)), + dump_register(HCINTMSK(5)), + dump_register(HCINTMSK(6)), + dump_register(HCINTMSK(7)), + dump_register(HCINTMSK(8)), + dump_register(HCINTMSK(9)), + dump_register(HCINTMSK(10)), + dump_register(HCINTMSK(11)), + dump_register(HCINTMSK(12)), + dump_register(HCINTMSK(13)), + dump_register(HCINTMSK(14)), + dump_register(HCINTMSK(15)), + dump_register(HCTSIZ(0)), + dump_register(HCTSIZ(1)), + dump_register(HCTSIZ(2)), + dump_register(HCTSIZ(3)), + dump_register(HCTSIZ(4)), + dump_register(HCTSIZ(5)), + dump_register(HCTSIZ(6)), + dump_register(HCTSIZ(7)), + dump_register(HCTSIZ(8)), + dump_register(HCTSIZ(9)), + dump_register(HCTSIZ(10)), + dump_register(HCTSIZ(11)), + dump_register(HCTSIZ(12)), + dump_register(HCTSIZ(13)), + dump_register(HCTSIZ(14)), + dump_register(HCTSIZ(15)), + dump_register(HCDMA(0)), + dump_register(HCDMA(1)), + dump_register(HCDMA(2)), + dump_register(HCDMA(3)), + dump_register(HCDMA(4)), + dump_register(HCDMA(5)), + dump_register(HCDMA(6)), + dump_register(HCDMA(7)), + dump_register(HCDMA(8)), + dump_register(HCDMA(9)), + dump_register(HCDMA(10)), + dump_register(HCDMA(11)), + dump_register(HCDMA(12)), + dump_register(HCDMA(13)), + dump_register(HCDMA(14)), + dump_register(HCDMA(15)), + dump_register(HCDMAB(0)), + dump_register(HCDMAB(1)), + dump_register(HCDMAB(2)), + dump_register(HCDMAB(3)), + dump_register(HCDMAB(4)), + dump_register(HCDMAB(5)), + dump_register(HCDMAB(6)), + dump_register(HCDMAB(7)), + dump_register(HCDMAB(8)), + dump_register(HCDMAB(9)), + dump_register(HCDMAB(10)), + dump_register(HCDMAB(11)), + dump_register(HCDMAB(12)), + dump_register(HCDMAB(13)), + dump_register(HCDMAB(14)), + dump_register(HCDMAB(15)), +}; + +int dwc2_debugfs_init(struct dwc2_hsotg *hsotg) +{ + int ret; + struct dentry *file; + + hsotg->debug_root = debugfs_create_dir(dev_name(hsotg->dev), NULL); + if (!hsotg->debug_root) { + ret = -ENOMEM; + goto err0; + } + + /* Add gadget debugfs nodes */ + s3c_hsotg_create_debug(hsotg); + + hsotg->regset = devm_kzalloc(hsotg->dev, sizeof(*hsotg->regset), + GFP_KERNEL); + if (!hsotg->regset) { + ret = -ENOMEM; + goto err1; + } + + hsotg->regset->regs = dwc2_regs; + hsotg->regset->nregs = ARRAY_SIZE(dwc2_regs); + hsotg->regset->base = hsotg->regs; + + file = debugfs_create_regset32("regdump", S_IRUGO, hsotg->debug_root, + hsotg->regset); + if (!file) { + ret = -ENOMEM; + goto err1; + } + + return 0; +err1: + debugfs_remove_recursive(hsotg->debug_root); +err0: + return ret; +} + +void dwc2_debugfs_exit(struct dwc2_hsotg *hsotg) +{ + debugfs_remove_recursive(hsotg->debug_root); + hsotg->debug_root = NULL; +} diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 6a30887082cd..4d47b7c09238 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -20,7 +20,6 @@ #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> -#include <linux/debugfs.h> #include <linux/mutex.h> #include <linux/seq_file.h> #include <linux/delay.h> @@ -35,7 +34,6 @@ #include <linux/usb/gadget.h> #include <linux/usb/phy.h> #include <linux/platform_data/s3c-hsotg.h> -#include <linux/uaccess.h> #include "core.h" #include "hw.h" @@ -792,6 +790,13 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, ep->name, req, req->length, req->buf, req->no_interrupt, req->zero, req->short_not_ok); + /* Prevent new request submission when controller is suspended */ + if (hs->lx_state == DWC2_L2) { + dev_dbg(hs->dev, "%s: don't submit request while suspended\n", + __func__); + return -EAGAIN; + } + /* initialise status of the request */ INIT_LIST_HEAD(&hs_req->queue); req->actual = 0; @@ -894,7 +899,7 @@ static struct s3c_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg, * @testmode: requested usb test mode * Enable usb Test Mode requested by the Host. */ -static int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode) +int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode) { int dctl = readl(hsotg->regs + DCTL); @@ -2185,7 +2190,6 @@ void s3c_hsotg_disconnect(struct dwc2_hsotg *hsotg) call_gadget(hsotg, disconnect); } -EXPORT_SYMBOL_GPL(s3c_hsotg_disconnect); /** * s3c_hsotg_irq_fifoempty - TX FIFO empty interrupt handler @@ -2310,8 +2314,9 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, writel(GINTSTS_ERLYSUSP | GINTSTS_SESSREQINT | GINTSTS_GOUTNAKEFF | GINTSTS_GINNAKEFF | GINTSTS_CONIDSTSCHNG | GINTSTS_USBRST | - GINTSTS_ENUMDONE | GINTSTS_OTGINT | - GINTSTS_USBSUSP | GINTSTS_WKUPINT, + GINTSTS_RESETDET | GINTSTS_ENUMDONE | + GINTSTS_OTGINT | GINTSTS_USBSUSP | + GINTSTS_WKUPINT, hsotg->regs + GINTMSK); if (using_dma(hsotg)) @@ -2477,7 +2482,19 @@ irq_retry: } } - if (gintsts & GINTSTS_USBRST) { + if (gintsts & GINTSTS_RESETDET) { + dev_dbg(hsotg->dev, "%s: USBRstDet\n", __func__); + + writel(GINTSTS_RESETDET, hsotg->regs + GINTSTS); + + /* This event must be used only if controller is suspended */ + if (hsotg->lx_state == DWC2_L2) { + dwc2_exit_hibernation(hsotg, true); + hsotg->lx_state = DWC2_L0; + } + } + + if (gintsts & (GINTSTS_USBRST | GINTSTS_RESETDET)) { u32 usb_status = readl(hsotg->regs + GOTGCTL); @@ -2497,6 +2514,7 @@ irq_retry: kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET); + hsotg->lx_state = DWC2_L0; s3c_hsotg_core_init_disconnected(hsotg, true); } } @@ -2745,7 +2763,7 @@ error: * s3c_hsotg_ep_disable - disable given endpoint * @ep: The endpoint to disable. */ -static int s3c_hsotg_ep_disable_force(struct usb_ep *ep, bool force) +static int s3c_hsotg_ep_disable(struct usb_ep *ep) { struct s3c_hsotg_ep *hs_ep = our_ep(ep); struct dwc2_hsotg *hsotg = hs_ep->parent; @@ -2788,10 +2806,6 @@ static int s3c_hsotg_ep_disable_force(struct usb_ep *ep, bool force) return 0; } -static int s3c_hsotg_ep_disable(struct usb_ep *ep) -{ - return s3c_hsotg_ep_disable_force(ep, false); -} /** * on_list - check request is on the given endpoint * @ep: The endpoint to check. @@ -3187,6 +3201,14 @@ static int s3c_hsotg_vbus_session(struct usb_gadget *gadget, int is_active) spin_lock_irqsave(&hsotg->lock, flags); 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); @@ -3391,404 +3413,6 @@ static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg) #endif } -/** - * testmode_write - debugfs: change usb test mode - * @seq: The seq file to write to. - * @v: Unused parameter. - * - * This debugfs entry modify the current usb test mode. - */ -static ssize_t testmode_write(struct file *file, const char __user *ubuf, size_t - count, loff_t *ppos) -{ - struct seq_file *s = file->private_data; - struct dwc2_hsotg *hsotg = s->private; - unsigned long flags; - u32 testmode = 0; - char buf[32]; - - if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) - return -EFAULT; - - if (!strncmp(buf, "test_j", 6)) - testmode = TEST_J; - else if (!strncmp(buf, "test_k", 6)) - testmode = TEST_K; - else if (!strncmp(buf, "test_se0_nak", 12)) - testmode = TEST_SE0_NAK; - else if (!strncmp(buf, "test_packet", 11)) - testmode = TEST_PACKET; - else if (!strncmp(buf, "test_force_enable", 17)) - testmode = TEST_FORCE_EN; - else - testmode = 0; - - spin_lock_irqsave(&hsotg->lock, flags); - s3c_hsotg_set_test_mode(hsotg, testmode); - spin_unlock_irqrestore(&hsotg->lock, flags); - return count; -} - -/** - * testmode_show - debugfs: show usb test mode state - * @seq: The seq file to write to. - * @v: Unused parameter. - * - * This debugfs entry shows which usb test mode is currently enabled. - */ -static int testmode_show(struct seq_file *s, void *unused) -{ - struct dwc2_hsotg *hsotg = s->private; - unsigned long flags; - int dctl; - - spin_lock_irqsave(&hsotg->lock, flags); - dctl = readl(hsotg->regs + DCTL); - dctl &= DCTL_TSTCTL_MASK; - dctl >>= DCTL_TSTCTL_SHIFT; - spin_unlock_irqrestore(&hsotg->lock, flags); - - switch (dctl) { - case 0: - seq_puts(s, "no test\n"); - break; - case TEST_J: - seq_puts(s, "test_j\n"); - break; - case TEST_K: - seq_puts(s, "test_k\n"); - break; - case TEST_SE0_NAK: - seq_puts(s, "test_se0_nak\n"); - break; - case TEST_PACKET: - seq_puts(s, "test_packet\n"); - break; - case TEST_FORCE_EN: - seq_puts(s, "test_force_enable\n"); - break; - default: - seq_printf(s, "UNKNOWN %d\n", dctl); - } - - return 0; -} - -static int testmode_open(struct inode *inode, struct file *file) -{ - return single_open(file, testmode_show, inode->i_private); -} - -static const struct file_operations testmode_fops = { - .owner = THIS_MODULE, - .open = testmode_open, - .write = testmode_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/** - * state_show - debugfs: show overall driver and device state. - * @seq: The seq file to write to. - * @v: Unused parameter. - * - * This debugfs entry shows the overall state of the hardware and - * some general information about each of the endpoints available - * to the system. - */ -static int state_show(struct seq_file *seq, void *v) -{ - struct dwc2_hsotg *hsotg = seq->private; - void __iomem *regs = hsotg->regs; - int idx; - - seq_printf(seq, "DCFG=0x%08x, DCTL=0x%08x, DSTS=0x%08x\n", - readl(regs + DCFG), - readl(regs + DCTL), - readl(regs + DSTS)); - - seq_printf(seq, "DIEPMSK=0x%08x, DOEPMASK=0x%08x\n", - readl(regs + DIEPMSK), readl(regs + DOEPMSK)); - - seq_printf(seq, "GINTMSK=0x%08x, GINTSTS=0x%08x\n", - readl(regs + GINTMSK), - readl(regs + GINTSTS)); - - seq_printf(seq, "DAINTMSK=0x%08x, DAINT=0x%08x\n", - readl(regs + DAINTMSK), - readl(regs + DAINT)); - - seq_printf(seq, "GNPTXSTS=0x%08x, GRXSTSR=%08x\n", - readl(regs + GNPTXSTS), - 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)); - - seq_printf(seq, "ep%d: DIEPCTL=0x%08x, DOEPCTL=0x%08x", - idx, in, out); - - in = readl(regs + DIEPTSIZ(idx)); - out = readl(regs + DOEPTSIZ(idx)); - - seq_printf(seq, ", DIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x", - in, out); - - seq_puts(seq, "\n"); - } - - return 0; -} - -static int state_open(struct inode *inode, struct file *file) -{ - return single_open(file, state_show, inode->i_private); -} - -static const struct file_operations state_fops = { - .owner = THIS_MODULE, - .open = state_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/** - * fifo_show - debugfs: show the fifo information - * @seq: The seq_file to write data to. - * @v: Unused parameter. - * - * Show the FIFO information for the overall fifo and all the - * periodic transmission FIFOs. - */ -static int fifo_show(struct seq_file *seq, void *v) -{ - struct dwc2_hsotg *hsotg = seq->private; - void __iomem *regs = hsotg->regs; - u32 val; - int idx; - - seq_puts(seq, "Non-periodic FIFOs:\n"); - seq_printf(seq, "RXFIFO: Size %d\n", readl(regs + GRXFSIZ)); - - val = readl(regs + GNPTXFSIZ); - seq_printf(seq, "NPTXFIFO: Size %d, Start 0x%08x\n", - val >> FIFOSIZE_DEPTH_SHIFT, - val & FIFOSIZE_DEPTH_MASK); - - seq_puts(seq, "\nPeriodic TXFIFOs:\n"); - - for (idx = 1; idx < hsotg->num_of_eps; idx++) { - val = readl(regs + DPTXFSIZN(idx)); - - seq_printf(seq, "\tDPTXFIFO%2d: Size %d, Start 0x%08x\n", idx, - val >> FIFOSIZE_DEPTH_SHIFT, - val & FIFOSIZE_STARTADDR_MASK); - } - - return 0; -} - -static int fifo_open(struct inode *inode, struct file *file) -{ - return single_open(file, fifo_show, inode->i_private); -} - -static const struct file_operations fifo_fops = { - .owner = THIS_MODULE, - .open = fifo_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - - -static const char *decode_direction(int is_in) -{ - return is_in ? "in" : "out"; -} - -/** - * ep_show - debugfs: show the state of an endpoint. - * @seq: The seq_file to write data to. - * @v: Unused parameter. - * - * This debugfs entry shows the state of the given endpoint (one is - * registered for each available). - */ -static int ep_show(struct seq_file *seq, void *v) -{ - struct s3c_hsotg_ep *ep = seq->private; - struct dwc2_hsotg *hsotg = ep->parent; - struct s3c_hsotg_req *req; - void __iomem *regs = hsotg->regs; - int index = ep->index; - int show_limit = 15; - unsigned long flags; - - seq_printf(seq, "Endpoint index %d, named %s, dir %s:\n", - ep->index, ep->ep.name, decode_direction(ep->dir_in)); - - /* first show the register state */ - - seq_printf(seq, "\tDIEPCTL=0x%08x, DOEPCTL=0x%08x\n", - readl(regs + DIEPCTL(index)), - readl(regs + DOEPCTL(index))); - - seq_printf(seq, "\tDIEPDMA=0x%08x, DOEPDMA=0x%08x\n", - readl(regs + DIEPDMA(index)), - readl(regs + DOEPDMA(index))); - - seq_printf(seq, "\tDIEPINT=0x%08x, DOEPINT=0x%08x\n", - readl(regs + DIEPINT(index)), - readl(regs + DOEPINT(index))); - - seq_printf(seq, "\tDIEPTSIZ=0x%08x, DOEPTSIZ=0x%08x\n", - readl(regs + DIEPTSIZ(index)), - readl(regs + DOEPTSIZ(index))); - - seq_puts(seq, "\n"); - seq_printf(seq, "mps %d\n", ep->ep.maxpacket); - seq_printf(seq, "total_data=%ld\n", ep->total_data); - - seq_printf(seq, "request list (%p,%p):\n", - ep->queue.next, ep->queue.prev); - - spin_lock_irqsave(&hsotg->lock, flags); - - list_for_each_entry(req, &ep->queue, queue) { - if (--show_limit < 0) { - seq_puts(seq, "not showing more requests...\n"); - break; - } - - seq_printf(seq, "%c req %p: %d bytes @%p, ", - req == ep->req ? '*' : ' ', - req, req->req.length, req->req.buf); - seq_printf(seq, "%d done, res %d\n", - req->req.actual, req->req.status); - } - - spin_unlock_irqrestore(&hsotg->lock, flags); - - return 0; -} - -static int ep_open(struct inode *inode, struct file *file) -{ - return single_open(file, ep_show, inode->i_private); -} - -static const struct file_operations ep_fops = { - .owner = THIS_MODULE, - .open = ep_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -/** - * s3c_hsotg_create_debug - create debugfs directory and files - * @hsotg: The driver state - * - * Create the debugfs files to allow the user to get information - * about the state of the system. The directory name is created - * 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) -{ - struct dentry *root; - unsigned epidx; - - root = debugfs_create_dir(dev_name(hsotg->dev), NULL); - hsotg->debug_root = root; - if (IS_ERR(root)) { - dev_err(hsotg->dev, "cannot create debug root\n"); - return; - } - - /* create general state file */ - - hsotg->debug_file = debugfs_create_file("state", S_IRUGO, root, - hsotg, &state_fops); - - if (IS_ERR(hsotg->debug_file)) - dev_err(hsotg->dev, "%s: failed to create state\n", __func__); - - hsotg->debug_testmode = debugfs_create_file("testmode", - S_IRUGO | S_IWUSR, root, - hsotg, &testmode_fops); - - if (IS_ERR(hsotg->debug_testmode)) - dev_err(hsotg->dev, "%s: failed to create testmode\n", - __func__); - - hsotg->debug_fifo = debugfs_create_file("fifo", S_IRUGO, root, - hsotg, &fifo_fops); - - if (IS_ERR(hsotg->debug_fifo)) - dev_err(hsotg->dev, "%s: failed to create fifo\n", __func__); - - /* Create one file for each out endpoint */ - for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) { - struct s3c_hsotg_ep *ep; - - ep = hsotg->eps_out[epidx]; - if (ep) { - ep->debugfs = debugfs_create_file(ep->name, S_IRUGO, - root, ep, &ep_fops); - - if (IS_ERR(ep->debugfs)) - dev_err(hsotg->dev, "failed to create %s debug file\n", - ep->name); - } - } - /* 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; - - ep = hsotg->eps_in[epidx]; - if (ep) { - ep->debugfs = debugfs_create_file(ep->name, S_IRUGO, - root, ep, &ep_fops); - - if (IS_ERR(ep->debugfs)) - dev_err(hsotg->dev, "failed to create %s debug file\n", - ep->name); - } - } -} - -/** - * s3c_hsotg_delete_debug - cleanup debugfs entries - * @hsotg: The driver state - * - * Cleanup (remove) the debugfs files for use on module exit. - */ -static void s3c_hsotg_delete_debug(struct dwc2_hsotg *hsotg) -{ - unsigned epidx; - - for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) { - if (hsotg->eps_in[epidx]) - debugfs_remove(hsotg->eps_in[epidx]->debugfs); - if (hsotg->eps_out[epidx]) - debugfs_remove(hsotg->eps_out[epidx]->debugfs); - } - - debugfs_remove(hsotg->debug_file); - debugfs_remove(hsotg->debug_testmode); - debugfs_remove(hsotg->debug_fifo); - debugfs_remove(hsotg->debug_root); -} - #ifdef CONFIG_OF static void s3c_hsotg_of_probe(struct dwc2_hsotg *hsotg) { @@ -3896,6 +3520,8 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) hsotg->gadget.max_speed = USB_SPEED_HIGH; hsotg->gadget.ops = &s3c_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 */ @@ -4028,8 +3654,6 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq) if (ret) goto err_supplies; - s3c_hsotg_create_debug(hsotg); - s3c_hsotg_dump(hsotg); return 0; @@ -4041,7 +3665,6 @@ err_clk: return ret; } -EXPORT_SYMBOL_GPL(dwc2_gadget_init); /** * s3c_hsotg_remove - remove function for hsotg driver @@ -4050,18 +3673,19 @@ EXPORT_SYMBOL_GPL(dwc2_gadget_init); int s3c_hsotg_remove(struct dwc2_hsotg *hsotg) { usb_del_gadget_udc(&hsotg->gadget); - s3c_hsotg_delete_debug(hsotg); clk_disable_unprepare(hsotg->clk); return 0; } -EXPORT_SYMBOL_GPL(s3c_hsotg_remove); int s3c_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); if (hsotg->driver) { @@ -4095,13 +3719,15 @@ int s3c_hsotg_suspend(struct dwc2_hsotg *hsotg) return ret; } -EXPORT_SYMBOL_GPL(s3c_hsotg_suspend); int s3c_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); if (hsotg->driver) { @@ -4124,4 +3750,3 @@ int s3c_hsotg_resume(struct dwc2_hsotg *hsotg) return ret; } -EXPORT_SYMBOL_GPL(s3c_hsotg_resume); diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index fbbbac2150a5..b10377c65064 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -357,12 +357,12 @@ void dwc2_hcd_stop(struct dwc2_hsotg *hsotg) writel(0, hsotg->regs + HPRT0); } +/* Caller must hold driver lock */ static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg, struct dwc2_hcd_urb *urb, void **ep_handle, gfp_t mem_flags) { struct dwc2_qtd *qtd; - unsigned long flags; u32 intr_mask; int retval; int dev_speed; @@ -413,11 +413,9 @@ static int dwc2_hcd_urb_enqueue(struct dwc2_hsotg *hsotg, */ return 0; - spin_lock_irqsave(&hsotg->lock, flags); tr_type = dwc2_hcd_select_transactions(hsotg); if (tr_type != DWC2_TRANSACTION_NONE) dwc2_hcd_queue_transactions(hsotg, tr_type); - spin_unlock_irqrestore(&hsotg->lock, flags); } return 0; @@ -721,9 +719,7 @@ static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, /* 3072 = 3 max-size Isoc packets */ buf_size = 3072; - qh->dw_align_buf = dma_alloc_coherent(hsotg->dev, buf_size, - &qh->dw_align_buf_dma, - GFP_ATOMIC); + qh->dw_align_buf = kmalloc(buf_size, GFP_ATOMIC | GFP_DMA); if (!qh->dw_align_buf) return -ENOMEM; qh->dw_align_buf_size = buf_size; @@ -748,6 +744,15 @@ static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, } } + qh->dw_align_buf_dma = dma_map_single(hsotg->dev, + qh->dw_align_buf, qh->dw_align_buf_size, + chan->ep_is_in ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (dma_mapping_error(hsotg->dev, qh->dw_align_buf_dma)) { + dev_err(hsotg->dev, "can't map align_buf\n"); + chan->align_buf = 0; + return -EINVAL; + } + chan->align_buf = qh->dw_align_buf_dma; return 0; } @@ -1774,6 +1779,15 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq, /* Not supported */ break; + case USB_PORT_FEAT_TEST: + hprt0 = dwc2_read_hprt0(hsotg); + dev_dbg(hsotg->dev, + "SetPortFeature - USB_PORT_FEAT_TEST\n"); + hprt0 &= ~HPRT0_TSTCTL_MASK; + hprt0 |= (windex >> 8) << HPRT0_TSTCTL_SHIFT; + writel(hprt0, hsotg->regs + HPRT0); + break; + default: retval = -EINVAL; dev_err(hsotg->dev, @@ -2313,6 +2327,22 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd) usleep_range(1000, 3000); } +static int _dwc2_hcd_suspend(struct usb_hcd *hcd) +{ + struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); + + hsotg->lx_state = DWC2_L2; + return 0; +} + +static int _dwc2_hcd_resume(struct usb_hcd *hcd) +{ + struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd); + + hsotg->lx_state = DWC2_L0; + return 0; +} + /* Returns the current frame number */ static int _dwc2_hcd_get_frame_number(struct usb_hcd *hcd) { @@ -2468,7 +2498,7 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, "%s: unaligned transfer with no transfer_buffer", __func__); retval = -EINVAL; - goto fail1; + goto fail0; } } @@ -2496,7 +2526,6 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, spin_lock_irqsave(&hsotg->lock, flags); retval = usb_hcd_link_urb_to_ep(hcd, urb); - spin_unlock_irqrestore(&hsotg->lock, flags); if (retval) goto fail1; @@ -2505,22 +2534,22 @@ static int _dwc2_hcd_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, goto fail2; if (alloc_bandwidth) { - spin_lock_irqsave(&hsotg->lock, flags); dwc2_allocate_bus_bandwidth(hcd, dwc2_hcd_get_ep_bandwidth(hsotg, ep), urb); - spin_unlock_irqrestore(&hsotg->lock, flags); } + spin_unlock_irqrestore(&hsotg->lock, flags); + return 0; fail2: - spin_lock_irqsave(&hsotg->lock, flags); dwc2_urb->priv = NULL; usb_hcd_unlink_urb_from_ep(hcd, urb); - spin_unlock_irqrestore(&hsotg->lock, flags); fail1: + spin_unlock_irqrestore(&hsotg->lock, flags); urb->hcpriv = NULL; +fail0: kfree(dwc2_urb); return retval; @@ -2683,6 +2712,9 @@ static struct hc_driver dwc2_hc_driver = { .hub_status_data = _dwc2_hcd_hub_status_data, .hub_control = _dwc2_hcd_hub_control, .clear_tt_buffer_complete = _dwc2_hcd_clear_tt_buffer_complete, + + .bus_suspend = _dwc2_hcd_suspend, + .bus_resume = _dwc2_hcd_resume, }; /* @@ -2748,8 +2780,6 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg) destroy_workqueue(hsotg->wq_otg); } - kfree(hsotg->core_params); - hsotg->core_params = NULL; del_timer(&hsotg->wkp_timer); } @@ -2762,29 +2792,12 @@ static void dwc2_hcd_release(struct dwc2_hsotg *hsotg) } /* - * Sets all parameters to the given value. - * - * Assumes that the dwc2_core_params struct contains only integers. - */ -void dwc2_set_all_params(struct dwc2_core_params *params, int value) -{ - int *p = (int *)params; - size_t size = sizeof(*params) / sizeof(*p); - int i; - - for (i = 0; i < size; i++) - p[i] = value; -} -EXPORT_SYMBOL_GPL(dwc2_set_all_params); - -/* * Initializes the HCD. This function allocates memory for and initializes the * static parts of the usb_hcd and dwc2_hsotg structures. It also registers the * USB bus with the core and calls the hc_driver->start() function. It returns * a negative error on failure. */ -int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, - const struct dwc2_core_params *params) +int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq) { struct usb_hcd *hcd; struct dwc2_host_chan *channel; @@ -2797,12 +2810,6 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, dev_dbg(hsotg->dev, "DWC OTG HCD INIT\n"); - /* Detect config values from hardware */ - retval = dwc2_get_hwparams(hsotg); - - if (retval) - return retval; - retval = -ENOMEM; hcfg = readl(hsotg->regs + HCFG); @@ -2821,15 +2828,6 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, hsotg->last_frame_num = HFNUM_MAX_FRNUM; #endif - hsotg->core_params = kzalloc(sizeof(*hsotg->core_params), GFP_KERNEL); - if (!hsotg->core_params) - goto error1; - - dwc2_set_all_params(hsotg->core_params, -1); - - /* Validate parameter values */ - dwc2_set_parameters(hsotg, params); - /* Check if the bus driver or platform code has setup a dma_mask */ if (hsotg->core_params->dma_enable > 0 && hsotg->dev->dma_mask == NULL) { @@ -2947,6 +2945,9 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, /* Don't support SG list at this point */ hcd->self.sg_tablesize = 0; + if (!IS_ERR_OR_NULL(hsotg->uphy)) + otg_set_host(hsotg->uphy->otg, &hcd->self); + /* * Finish generic HCD initialization and start the HCD. This function * allocates the DMA buffer pool, registers the USB bus, requests the @@ -2979,7 +2980,6 @@ error1: dev_err(hsotg->dev, "%s() FAILED, returning %d\n", __func__, retval); return retval; } -EXPORT_SYMBOL_GPL(dwc2_hcd_init); /* * Removes the HCD. @@ -3000,6 +3000,9 @@ void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) return; } + if (!IS_ERR_OR_NULL(hsotg->uphy)) + otg_set_host(hsotg->uphy->otg, NULL); + usb_remove_hcd(hcd); hsotg->priv = NULL; dwc2_hcd_release(hsotg); @@ -3010,4 +3013,3 @@ void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) kfree(hsotg->frame_num_array); #endif } -EXPORT_SYMBOL_GPL(dwc2_hcd_remove); diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h index e69a843d8928..7b5841c40033 100644 --- a/drivers/usb/dwc2/hcd.h +++ b/drivers/usb/dwc2/hcd.h @@ -451,13 +451,8 @@ static inline u8 dwc2_hcd_is_pipe_out(struct dwc2_hcd_pipe_info *pipe) return !dwc2_hcd_is_pipe_in(pipe); } -extern int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq, - const struct dwc2_core_params *params); +extern int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq); extern void dwc2_hcd_remove(struct dwc2_hsotg *hsotg); -extern void dwc2_set_parameters(struct dwc2_hsotg *hsotg, - const struct dwc2_core_params *params); -extern void dwc2_set_all_params(struct dwc2_core_params *params, int value); -extern int dwc2_get_hwparams(struct dwc2_hsotg *hsotg); /* Transaction Execution Functions */ extern enum dwc2_transaction_type dwc2_hcd_select_transactions( diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c index 551ba878b003..4cc95df4262d 100644 --- a/drivers/usb/dwc2/hcd_intr.c +++ b/drivers/usb/dwc2/hcd_intr.c @@ -350,6 +350,9 @@ static void dwc2_port_intr(struct dwc2_hsotg *hsotg) dev_vdbg(hsotg->dev, "--Port Interrupt HPRT0=0x%08x Port Connect Detected--\n", hprt0); + if (hsotg->lx_state != DWC2_L0) + usb_hcd_resume_root_hub(hsotg->priv); + hsotg->flags.b.port_connect_status_change = 1; hsotg->flags.b.port_connect_status = 1; hprt0_modify |= HPRT0_CONNDET; @@ -463,10 +466,15 @@ static int dwc2_update_urb_state(struct dwc2_hsotg *hsotg, } /* Non DWORD-aligned buffer case handling */ - if (chan->align_buf && xfer_length && chan->ep_is_in) { + if (chan->align_buf && xfer_length) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf, - xfer_length); + dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, + chan->qh->dw_align_buf_size, + chan->ep_is_in ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (chan->ep_is_in) + memcpy(urb->buf + urb->actual_length, + chan->qh->dw_align_buf, xfer_length); } dev_vdbg(hsotg->dev, "urb->actual_length=%d xfer_length=%d\n", @@ -552,13 +560,18 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state( chan, chnum, qtd, halt_status, NULL); /* Non DWORD-aligned buffer case handling */ - if (chan->align_buf && frame_desc->actual_length && - chan->ep_is_in) { + if (chan->align_buf && frame_desc->actual_length) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - memcpy(urb->buf + frame_desc->offset + - qtd->isoc_split_offset, chan->qh->dw_align_buf, - frame_desc->actual_length); + dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, + chan->qh->dw_align_buf_size, + chan->ep_is_in ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (chan->ep_is_in) + memcpy(urb->buf + frame_desc->offset + + qtd->isoc_split_offset, + chan->qh->dw_align_buf, + frame_desc->actual_length); } break; case DWC2_HC_XFER_FRAME_OVERRUN: @@ -581,13 +594,18 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state( chan, chnum, qtd, halt_status, NULL); /* Non DWORD-aligned buffer case handling */ - if (chan->align_buf && frame_desc->actual_length && - chan->ep_is_in) { + if (chan->align_buf && frame_desc->actual_length) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - memcpy(urb->buf + frame_desc->offset + - qtd->isoc_split_offset, chan->qh->dw_align_buf, - frame_desc->actual_length); + dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, + chan->qh->dw_align_buf_size, + chan->ep_is_in ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (chan->ep_is_in) + memcpy(urb->buf + frame_desc->offset + + qtd->isoc_split_offset, + chan->qh->dw_align_buf, + frame_desc->actual_length); } /* Skip whole frame */ @@ -923,6 +941,8 @@ static int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg, if (chan->align_buf) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); + dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, + chan->qh->dw_align_buf_size, DMA_FROM_DEVICE); memcpy(qtd->urb->buf + frame_desc->offset + qtd->isoc_split_offset, chan->qh->dw_align_buf, len); } @@ -1152,8 +1172,14 @@ static void dwc2_update_urb_state_abn(struct dwc2_hsotg *hsotg, /* Non DWORD-aligned buffer case handling */ if (chan->align_buf && xfer_length && chan->ep_is_in) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf, - xfer_length); + dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma, + chan->qh->dw_align_buf_size, + chan->ep_is_in ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + if (chan->ep_is_in) + memcpy(urb->buf + urb->actual_length, + chan->qh->dw_align_buf, + xfer_length); } urb->actual_length += xfer_length; @@ -1182,6 +1208,16 @@ static void dwc2_hc_nak_intr(struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan, int chnum, struct dwc2_qtd *qtd) { + if (!qtd) { + dev_dbg(hsotg->dev, "%s: qtd is NULL\n", __func__); + return; + } + + if (!qtd->urb) { + dev_dbg(hsotg->dev, "%s: qtd->urb is NULL\n", __func__); + return; + } + if (dbg_hc(chan)) dev_vdbg(hsotg->dev, "--Host Channel %d Interrupt: NAK Received--\n", chnum); diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c index bb97838bc6c0..9b5c36256627 100644 --- a/drivers/usb/dwc2/hcd_queue.c +++ b/drivers/usb/dwc2/hcd_queue.c @@ -229,11 +229,13 @@ static struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg, */ void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) { - if (hsotg->core_params->dma_desc_enable > 0) + if (hsotg->core_params->dma_desc_enable > 0) { dwc2_hcd_qh_free_ddma(hsotg, qh); - else if (qh->dw_align_buf) - dma_free_coherent(hsotg->dev, qh->dw_align_buf_size, - qh->dw_align_buf, qh->dw_align_buf_dma); + } else { + /* kfree(NULL) is safe */ + kfree(qh->dw_align_buf); + qh->dw_align_buf_dma = (dma_addr_t)0; + } kfree(qh); } @@ -761,6 +763,7 @@ void dwc2_hcd_qtd_init(struct dwc2_qtd *qtd, struct dwc2_hcd_urb *urb) /** * dwc2_hcd_qtd_add() - Adds a QTD to the QTD-list of a QH + * Caller must hold driver lock. * * @hsotg: The DWC HCD structure * @qtd: The QTD to add @@ -777,7 +780,6 @@ int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd, struct dwc2_qh **qh, gfp_t mem_flags) { struct dwc2_hcd_urb *urb = qtd->urb; - unsigned long flags; int allocated = 0; int retval; @@ -792,15 +794,12 @@ int dwc2_hcd_qtd_add(struct dwc2_hsotg *hsotg, struct dwc2_qtd *qtd, allocated = 1; } - spin_lock_irqsave(&hsotg->lock, flags); - retval = dwc2_hcd_qh_add(hsotg, *qh); if (retval) goto fail; qtd->qh = *qh; list_add_tail(&qtd->qtd_list_entry, &(*qh)->qtd_list); - spin_unlock_irqrestore(&hsotg->lock, flags); return 0; @@ -817,10 +816,7 @@ fail: qtd_list_entry) dwc2_hcd_qtd_unlink_and_free(hsotg, qtd2, qh_tmp); - spin_unlock_irqrestore(&hsotg->lock, flags); dwc2_hcd_qh_free(hsotg, qh_tmp); - } else { - spin_unlock_irqrestore(&hsotg->lock, flags); } return retval; diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index 185663e0b5f4..90935304185a 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -47,6 +47,7 @@ #include "core.h" #include "hcd.h" +#include "debug.h" static const char dwc2_driver_name[] = "dwc2"; @@ -76,6 +77,8 @@ static const struct dwc2_core_params params_bcm2835 = { .reload_ctl = 0, .ahbcfg = 0x10, .uframe_sched = 0, + .external_id_pin_ctl = -1, + .hibernation = -1, }; static const struct dwc2_core_params params_rk3066 = { @@ -104,6 +107,8 @@ static const struct dwc2_core_params params_rk3066 = { .reload_ctl = -1, .ahbcfg = 0x7, /* INCR16 */ .uframe_sched = -1, + .external_id_pin_ctl = -1, + .hibernation = -1, }; /** @@ -121,6 +126,7 @@ static int dwc2_driver_remove(struct platform_device *dev) { struct dwc2_hsotg *hsotg = platform_get_drvdata(dev); + dwc2_debugfs_exit(hsotg); if (hsotg->hcd_enabled) dwc2_hcd_remove(hsotg); if (hsotg->gadget_enabled) @@ -237,6 +243,21 @@ static int dwc2_driver_probe(struct platform_device *dev) spin_lock_init(&hsotg->lock); mutex_init(&hsotg->init_mutex); + /* Detect config values from hardware */ + retval = dwc2_get_hwparams(hsotg); + if (retval) + return retval; + + hsotg->core_params = devm_kzalloc(&dev->dev, + sizeof(*hsotg->core_params), GFP_KERNEL); + if (!hsotg->core_params) + return -ENOMEM; + + dwc2_set_all_params(hsotg->core_params, -1); + + /* Validate parameter values */ + dwc2_set_parameters(hsotg, params); + if (hsotg->dr_mode != USB_DR_MODE_HOST) { retval = dwc2_gadget_init(hsotg, irq); if (retval) @@ -245,7 +266,7 @@ static int dwc2_driver_probe(struct platform_device *dev) } if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) { - retval = dwc2_hcd_init(hsotg, irq, params); + retval = dwc2_hcd_init(hsotg, irq); if (retval) { if (hsotg->gadget_enabled) s3c_hsotg_remove(hsotg); @@ -256,6 +277,8 @@ static int dwc2_driver_probe(struct platform_device *dev) platform_set_drvdata(dev, hsotg); + dwc2_debugfs_init(hsotg); + return retval; } diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index 827c4f80379f..dede32e809b6 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -11,6 +11,13 @@ config USB_DWC3 if USB_DWC3 +config USB_DWC3_ULPI + bool "Register ULPI PHY Interface" + depends on USB_ULPI_BUS=y || USB_ULPI_BUS=USB_DWC3 + help + Select this if you have ULPI type PHY attached to your DWC3 + controller. + choice bool "DWC3 Mode Selection" default USB_DWC3_DUAL_ROLE if (USB && USB_GADGET) diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile index 46172f47f02d..c7076e37c4ed 100644 --- a/drivers/usb/dwc3/Makefile +++ b/drivers/usb/dwc3/Makefile @@ -15,6 +15,10 @@ ifneq ($(filter y,$(CONFIG_USB_DWC3_GADGET) $(CONFIG_USB_DWC3_DUAL_ROLE)),) dwc3-y += gadget.o ep0.o endif +ifneq ($(CONFIG_USB_DWC3_ULPI),) + dwc3-y += ulpi.o +endif + ifneq ($(CONFIG_DEBUG_FS),) dwc3-y += debugfs.o endif diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 2bbab3d86fff..5c110d8e293b 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -117,6 +117,33 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc) } /** + * dwc3_soft_reset - Issue soft reset + * @dwc: Pointer to our controller context structure + */ +static int dwc3_soft_reset(struct dwc3 *dwc) +{ + unsigned long timeout; + u32 reg; + + timeout = jiffies + msecs_to_jiffies(500); + dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST); + do { + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + if (!(reg & DWC3_DCTL_CSFTRST)) + break; + + if (time_after(jiffies, timeout)) { + dev_err(dwc->dev, "Reset Timed Out\n"); + return -ETIMEDOUT; + } + + cpu_relax(); + } while (true); + + return 0; +} + +/** * dwc3_free_one_event_buffer - Frees one event buffer * @dwc: Pointer to our controller context structure * @evt: Pointer to event buffer to be freed @@ -367,10 +394,15 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc) /** * dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core * @dwc: Pointer to our controller context structure + * + * Returns 0 on success. The USB PHY interfaces are configured but not + * initialized. The PHY interfaces and the PHYs get initialized together with + * the core in dwc3_core_init. */ -static void dwc3_phy_setup(struct dwc3 *dwc) +static int dwc3_phy_setup(struct dwc3 *dwc) { u32 reg; + int ret; reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); @@ -409,10 +441,41 @@ static void dwc3_phy_setup(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); - mdelay(100); - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + /* Select the HS PHY interface */ + switch (DWC3_GHWPARAMS3_HSPHY_IFC(dwc->hwparams.hwparams3)) { + case DWC3_GHWPARAMS3_HSPHY_IFC_UTMI_ULPI: + if (!strncmp(dwc->hsphy_interface, "utmi", 4)) { + reg &= ~DWC3_GUSB2PHYCFG_ULPI_UTMI; + break; + } else if (!strncmp(dwc->hsphy_interface, "ulpi", 4)) { + reg |= DWC3_GUSB2PHYCFG_ULPI_UTMI; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + } else { + dev_warn(dwc->dev, "HSPHY Interface not defined\n"); + + /* Relying on default value. */ + if (!(reg & DWC3_GUSB2PHYCFG_ULPI_UTMI)) + break; + } + /* FALLTHROUGH */ + case DWC3_GHWPARAMS3_HSPHY_IFC_ULPI: + /* Making sure the interface and PHY are operational */ + ret = dwc3_soft_reset(dwc); + if (ret) + return ret; + + udelay(1); + + ret = dwc3_ulpi_init(dwc); + if (ret) + return ret; + /* FALLTHROUGH */ + default: + break; + } + /* * Above 1.94a, it is recommended to set DWC3_GUSB2PHYCFG_SUSPHY to * '0' during coreConsultant configuration. So default value will @@ -427,7 +490,7 @@ static void dwc3_phy_setup(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); - mdelay(100); + return 0; } /** @@ -438,7 +501,6 @@ static void dwc3_phy_setup(struct dwc3 *dwc) */ static int dwc3_core_init(struct dwc3 *dwc) { - unsigned long timeout; u32 hwparams4 = dwc->hwparams.hwparams4; u32 reg; int ret; @@ -466,21 +528,9 @@ static int dwc3_core_init(struct dwc3 *dwc) } /* issue device SoftReset too */ - timeout = jiffies + msecs_to_jiffies(500); - dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST); - do { - reg = dwc3_readl(dwc->regs, DWC3_DCTL); - if (!(reg & DWC3_DCTL_CSFTRST)) - break; - - if (time_after(jiffies, timeout)) { - dev_err(dwc->dev, "Reset Timed Out\n"); - ret = -ETIMEDOUT; - goto err0; - } - - cpu_relax(); - } while (true); + ret = dwc3_soft_reset(dwc); + if (ret) + goto err0; ret = dwc3_core_soft_reset(dwc); if (ret) @@ -555,8 +605,6 @@ static int dwc3_core_init(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_GCTL, reg); - dwc3_phy_setup(dwc); - ret = dwc3_alloc_scratch_buffers(dwc); if (ret) goto err1; @@ -836,6 +884,8 @@ static int dwc3_probe(struct platform_device *pdev) "snps,tx_de_emphasis_quirk"); of_property_read_u8(node, "snps,tx_de_emphasis", &tx_de_emphasis); + of_property_read_string(node, "snps,hsphy_interface", + &dwc->hsphy_interface); } else if (pdata) { dwc->maximum_speed = pdata->maximum_speed; dwc->has_lpm_erratum = pdata->has_lpm_erratum; @@ -863,6 +913,8 @@ static int dwc3_probe(struct platform_device *pdev) 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; } /* default to superspeed if no maximum_speed passed */ @@ -875,12 +927,18 @@ static int dwc3_probe(struct platform_device *pdev) dwc->hird_threshold = hird_threshold | (dwc->is_utmi_l1_suspend << 4); + platform_set_drvdata(pdev, dwc); + dwc3_cache_hwparams(dwc); + + ret = dwc3_phy_setup(dwc); + if (ret) + goto err0; + ret = dwc3_core_get_phy(dwc); if (ret) goto err0; spin_lock_init(&dwc->lock); - platform_set_drvdata(pdev, dwc); if (!dev->dma_mask) { dev->dma_mask = dev->parent->dma_mask; @@ -892,8 +950,6 @@ static int dwc3_probe(struct platform_device *pdev) pm_runtime_get_sync(dev); pm_runtime_forbid(dev); - dwc3_cache_hwparams(dwc); - ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE); if (ret) { dev_err(dwc->dev, "failed to allocate event buffers\n"); @@ -964,6 +1020,7 @@ err2: err1: dwc3_free_event_buffers(dwc); + dwc3_ulpi_exit(dwc); err0: /* @@ -999,6 +1056,7 @@ static int dwc3_remove(struct platform_device *pdev) phy_power_off(dwc->usb3_generic_phy); dwc3_core_exit(dwc); + dwc3_ulpi_exit(dwc); pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index c0eafa6fd403..044778884585 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -30,6 +30,7 @@ #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> #include <linux/usb/otg.h> +#include <linux/ulpi/interface.h> #include <linux/phy/phy.h> @@ -173,6 +174,15 @@ /* Global USB2 PHY Configuration Register */ #define DWC3_GUSB2PHYCFG_PHYSOFTRST (1 << 31) #define DWC3_GUSB2PHYCFG_SUSPHY (1 << 6) +#define DWC3_GUSB2PHYCFG_ULPI_UTMI (1 << 4) + +/* Global USB2 PHY Vendor Control Register */ +#define DWC3_GUSB2PHYACC_NEWREGREQ (1 << 25) +#define DWC3_GUSB2PHYACC_BUSY (1 << 23) +#define DWC3_GUSB2PHYACC_WRITE (1 << 22) +#define DWC3_GUSB2PHYACC_ADDR(n) (n << 16) +#define DWC3_GUSB2PHYACC_EXTEND_ADDR(n) (n << 8) +#define DWC3_GUSB2PHYACC_DATA(n) (n & 0xff) /* Global USB3 PIPE Control Register */ #define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31) @@ -652,6 +662,7 @@ struct dwc3_scratchpad_array { * @usb3_phy: pointer to USB3 PHY * @usb2_generic_phy: pointer to USB2 PHY * @usb3_generic_phy: pointer to USB3 PHY + * @ulpi: pointer to ulpi interface * @dcfg: saved contents of DCFG register * @gctl: saved contents of GCTL register * @isoch_delay: wValue from Set Isochronous Delay request; @@ -673,6 +684,7 @@ struct dwc3_scratchpad_array { * @test_mode_nr: test feature selector * @lpm_nyet_threshold: LPM NYET response threshold * @hird_threshold: HIRD threshold + * @hsphy_interface: "utmi" or "ulpi" * @delayed_status: true when gadget driver asks for delayed status * @ep0_bounced: true when we used bounce buffer * @ep0_expect_in: true when we expect a DATA IN transfer @@ -739,6 +751,8 @@ struct dwc3 { struct phy *usb2_generic_phy; struct phy *usb3_generic_phy; + struct ulpi *ulpi; + void __iomem *regs; size_t regs_size; @@ -800,6 +814,8 @@ struct dwc3 { u8 lpm_nyet_threshold; u8 hird_threshold; + const char *hsphy_interface; + unsigned delayed_status:1; unsigned ep0_bounced:1; unsigned ep0_expect_in:1; @@ -1035,4 +1051,14 @@ static inline int dwc3_gadget_resume(struct dwc3 *dwc) } #endif /* !IS_ENABLED(CONFIG_USB_DWC3_HOST) */ +#if IS_ENABLED(CONFIG_USB_DWC3_ULPI) +int dwc3_ulpi_init(struct dwc3 *dwc); +void dwc3_ulpi_exit(struct dwc3 *dwc); +#else +static inline int dwc3_ulpi_init(struct dwc3 *dwc) +{ return 0; } +static inline void dwc3_ulpi_exit(struct dwc3 *dwc) +{ } +#endif + #endif /* __DRIVERS_USB_DWC3_CORE_H */ diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index b773fb53d6a7..27e4fc896e9d 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -21,6 +21,8 @@ #include <linux/slab.h> #include <linux/pci.h> #include <linux/platform_device.h> +#include <linux/gpio/consumer.h> +#include <linux/acpi.h> #include "platform_data.h" @@ -31,6 +33,15 @@ #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 }; + +static const struct acpi_gpio_mapping acpi_dwc3_byt_gpios[] = { + { "reset-gpios", &reset_gpios, 1 }, + { "cs-gpios", &cs_gpios, 1 }, + { }, +}; + static int dwc3_pci_quirks(struct pci_dev *pdev) { if (pdev->vendor == PCI_VENDOR_ID_AMD && @@ -65,6 +76,30 @@ static int dwc3_pci_quirks(struct pci_dev *pdev) sizeof(pdata)); } + if (pdev->vendor == PCI_VENDOR_ID_INTEL && + pdev->device == PCI_DEVICE_ID_INTEL_BYT) { + struct gpio_desc *gpio; + + acpi_dev_add_driver_gpios(ACPI_COMPANION(&pdev->dev), + acpi_dwc3_byt_gpios); + + /* These GPIOs will turn on the USB2 PHY */ + gpio = gpiod_get(&pdev->dev, "cs"); + if (!IS_ERR(gpio)) { + gpiod_direction_output(gpio, 0); + gpiod_set_value_cansleep(gpio, 1); + gpiod_put(gpio); + } + + gpio = gpiod_get(&pdev->dev, "reset"); + if (!IS_ERR(gpio)) { + gpiod_direction_output(gpio, 0); + gpiod_set_value_cansleep(gpio, 1); + gpiod_put(gpio); + usleep_range(10000, 11000); + } + } + return 0; } @@ -128,6 +163,7 @@ err: static void dwc3_pci_remove(struct pci_dev *pci) { + acpi_dev_remove_driver_gpios(ACPI_COMPANION(&pci->dev)); platform_device_unregister(pci_get_drvdata(pci)); } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 8946c32047e9..333a7c0078fc 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -291,6 +291,8 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param) dwc3_trace(trace_dwc3_gadget, "Command Complete --> %d", DWC3_DGCMD_STATUS(reg)); + if (DWC3_DGCMD_STATUS(reg)) + return -EINVAL; return 0; } @@ -328,6 +330,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, dwc3_trace(trace_dwc3_gadget, "Command Complete --> %d", DWC3_DEPCMD_STATUS(reg)); + if (DWC3_DEPCMD_STATUS(reg)) + return -EINVAL; return 0; } @@ -1902,12 +1906,16 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc, { unsigned status = 0; int clean_busy; + u32 is_xfer_complete; + + is_xfer_complete = (event->endpoint_event == DWC3_DEPEVT_XFERCOMPLETE); if (event->status & DEPEVT_STATUS_BUSERR) status = -ECONNRESET; clean_busy = dwc3_cleanup_done_reqs(dwc, dep, event, status); - if (clean_busy) + if (clean_busy && (is_xfer_complete || + usb_endpoint_xfer_isoc(dep->endpoint.desc))) dep->flags &= ~DWC3_EP_BUSY; /* diff --git a/drivers/usb/dwc3/platform_data.h b/drivers/usb/dwc3/platform_data.h index a2bd464be828..d3614ecbb9ca 100644 --- a/drivers/usb/dwc3/platform_data.h +++ b/drivers/usb/dwc3/platform_data.h @@ -45,4 +45,6 @@ struct dwc3_platform_data { unsigned tx_de_emphasis_quirk:1; unsigned tx_de_emphasis:2; + + const char *hsphy_interface; }; diff --git a/drivers/usb/dwc3/ulpi.c b/drivers/usb/dwc3/ulpi.c new file mode 100644 index 000000000000..ec004c6d76f2 --- /dev/null +++ b/drivers/usb/dwc3/ulpi.c @@ -0,0 +1,91 @@ +/** + * ulpi.c - DesignWare USB3 Controller's ULPI PHY interface + * + * Copyright (C) 2015 Intel Corporation + * + * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/ulpi/regs.h> + +#include "core.h" +#include "io.h" + +#define DWC3_ULPI_ADDR(a) \ + ((a >= ULPI_EXT_VENDOR_SPECIFIC) ? \ + DWC3_GUSB2PHYACC_ADDR(ULPI_ACCESS_EXTENDED) | \ + DWC3_GUSB2PHYACC_EXTEND_ADDR(a) : DWC3_GUSB2PHYACC_ADDR(a)) + +static int dwc3_ulpi_busyloop(struct dwc3 *dwc) +{ + unsigned count = 1000; + u32 reg; + + while (count--) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0)); + if (!(reg & DWC3_GUSB2PHYACC_BUSY)) + return 0; + cpu_relax(); + } + + return -ETIMEDOUT; +} + +static int dwc3_ulpi_read(struct ulpi_ops *ops, u8 addr) +{ + struct dwc3 *dwc = dev_get_drvdata(ops->dev); + u32 reg; + int ret; + + reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr); + dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg); + + ret = dwc3_ulpi_busyloop(dwc); + if (ret) + return ret; + + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYACC(0)); + + return DWC3_GUSB2PHYACC_DATA(reg); +} + +static int dwc3_ulpi_write(struct ulpi_ops *ops, u8 addr, u8 val) +{ + struct dwc3 *dwc = dev_get_drvdata(ops->dev); + u32 reg; + + reg = DWC3_GUSB2PHYACC_NEWREGREQ | DWC3_ULPI_ADDR(addr); + reg |= DWC3_GUSB2PHYACC_WRITE | val; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYACC(0), reg); + + return dwc3_ulpi_busyloop(dwc); +} + +static struct ulpi_ops dwc3_ulpi_ops = { + .read = dwc3_ulpi_read, + .write = dwc3_ulpi_write, +}; + +int dwc3_ulpi_init(struct dwc3 *dwc) +{ + /* Register the interface */ + dwc->ulpi = ulpi_register_interface(dwc->dev, &dwc3_ulpi_ops); + if (IS_ERR(dwc->ulpi)) { + dev_err(dwc->dev, "failed to register ULPI interface"); + return PTR_ERR(dwc->ulpi); + } + + return 0; +} + +void dwc3_ulpi_exit(struct dwc3 *dwc) +{ + if (dwc->ulpi) { + ulpi_unregister_interface(dwc->ulpi); + dwc->ulpi = NULL; + } +} diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c index 0567cca1465e..919cdfdda78b 100644 --- a/drivers/usb/gadget/epautoconf.c +++ b/drivers/usb/gadget/epautoconf.c @@ -258,15 +258,25 @@ struct usb_ep *usb_ep_autoconfig_ss( /* First, apply chip-specific "best usage" knowledge. * This might make a good usb_gadget_ops hook ... */ - if (gadget_is_net2280 (gadget) && type == USB_ENDPOINT_XFER_INT) { - /* ep-e, ep-f are PIO with only 64 byte fifos */ - ep = find_ep (gadget, "ep-e"); - if (ep && ep_matches(gadget, ep, desc, ep_comp)) - goto found_ep; - ep = find_ep (gadget, "ep-f"); + if (gadget_is_net2280(gadget)) { + char name[8]; + + if (type == USB_ENDPOINT_XFER_INT) { + /* ep-e, ep-f are PIO with only 64 byte fifos */ + ep = find_ep(gadget, "ep-e"); + if (ep && ep_matches(gadget, ep, desc, ep_comp)) + goto found_ep; + ep = find_ep(gadget, "ep-f"); + if (ep && ep_matches(gadget, ep, desc, ep_comp)) + goto found_ep; + } + + /* USB3380: use same address for usb and hardware endpoints */ + snprintf(name, sizeof(name), "ep%d%s", usb_endpoint_num(desc), + usb_endpoint_dir_in(desc) ? "in" : "out"); + ep = find_ep(gadget, name); if (ep && ep_matches(gadget, ep, desc, ep_comp)) goto found_ep; - } else if (gadget_is_goku (gadget)) { if (USB_ENDPOINT_XFER_INT == type) { /* single buffering is enough */ diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 3507f880eb74..45b8c8b338df 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -3435,6 +3435,7 @@ done: static void ffs_closed(struct ffs_data *ffs) { struct ffs_dev *ffs_obj; + struct f_fs_opts *opts; ENTER(); ffs_dev_lock(); @@ -3449,8 +3450,13 @@ static void ffs_closed(struct ffs_data *ffs) ffs_obj->ffs_closed_callback) ffs_obj->ffs_closed_callback(ffs); - if (!ffs_obj->opts || ffs_obj->opts->no_configfs - || !ffs_obj->opts->func_inst.group.cg_item.ci_parent) + if (ffs_obj->opts) + opts = ffs_obj->opts; + else + goto done; + + if (opts->no_configfs || !opts->func_inst.group.cg_item.ci_parent + || !atomic_read(&opts->func_inst.group.cg_item.ci_kref.refcount)) goto done; unregister_gadget_item(ffs_obj->opts-> diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c index 829edf878dac..32985dade838 100644 --- a/drivers/usb/gadget/function/f_rndis.c +++ b/drivers/usb/gadget/function/f_rndis.c @@ -76,7 +76,7 @@ struct f_rndis { u8 ethaddr[ETH_ALEN]; u32 vendorID; const char *manufacturer; - int config; + struct rndis_params *params; struct usb_ep *notify; struct usb_request *notify_req; @@ -453,7 +453,7 @@ static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req) /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */ // spin_lock(&dev->lock); - status = rndis_msg_parser(rndis->config, (u8 *) req->buf); + status = rndis_msg_parser(rndis->params, (u8 *) req->buf); if (status < 0) pr_err("RNDIS command error %d, %d/%d\n", status, req->actual, req->length); @@ -499,12 +499,12 @@ rndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) u32 n; /* return the result */ - buf = rndis_get_next_response(rndis->config, &n); + buf = rndis_get_next_response(rndis->params, &n); if (buf) { memcpy(req->buf, buf, n); req->complete = rndis_response_complete; req->context = rndis; - rndis_free_response(rndis->config, buf); + rndis_free_response(rndis->params, buf); value = n; } /* else stalls ... spec says to avoid that */ @@ -597,7 +597,7 @@ static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt) if (IS_ERR(net)) return PTR_ERR(net); - rndis_set_param_dev(rndis->config, net, + rndis_set_param_dev(rndis->params, net, &rndis->port.cdc_filter); } else goto fail; @@ -617,7 +617,7 @@ static void rndis_disable(struct usb_function *f) DBG(cdev, "rndis deactivated\n"); - rndis_uninit(rndis->config); + rndis_uninit(rndis->params); gether_disconnect(&rndis->port); usb_ep_disable(rndis->notify); @@ -640,9 +640,9 @@ static void rndis_open(struct gether *geth) DBG(cdev, "%s\n", __func__); - rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, + rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, bitrate(cdev->gadget) / 100); - rndis_signal_connect(rndis->config); + rndis_signal_connect(rndis->params); } static void rndis_close(struct gether *geth) @@ -651,8 +651,8 @@ static void rndis_close(struct gether *geth) DBG(geth->func.config->cdev, "%s\n", __func__); - rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0); - rndis_signal_disconnect(rndis->config); + rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, 0); + rndis_signal_disconnect(rndis->params); } /*-------------------------------------------------------------------------*/ @@ -796,11 +796,11 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f) rndis->port.open = rndis_open; rndis->port.close = rndis_close; - rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0); - rndis_set_host_mac(rndis->config, rndis->ethaddr); + rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3, 0); + rndis_set_host_mac(rndis->params, rndis->ethaddr); if (rndis->manufacturer && rndis->vendorID && - rndis_set_param_vendor(rndis->config, rndis->vendorID, + rndis_set_param_vendor(rndis->params, rndis->vendorID, rndis->manufacturer)) { status = -EINVAL; goto fail_free_descs; @@ -944,7 +944,7 @@ static void rndis_free(struct usb_function *f) struct f_rndis_opts *opts; rndis = func_to_rndis(f); - rndis_deregister(rndis->config); + rndis_deregister(rndis->params); opts = container_of(f->fi, struct f_rndis_opts, func_inst); kfree(rndis); mutex_lock(&opts->lock); @@ -968,7 +968,7 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi) { struct f_rndis *rndis; struct f_rndis_opts *opts; - int status; + struct rndis_params *params; /* allocate and initialize one new instance */ rndis = kzalloc(sizeof(*rndis), GFP_KERNEL); @@ -1002,36 +1002,16 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi) rndis->port.func.disable = rndis_disable; rndis->port.func.free_func = rndis_free; - status = rndis_register(rndis_response_available, rndis); - if (status < 0) { + params = rndis_register(rndis_response_available, rndis); + if (IS_ERR(params)) { kfree(rndis); - return ERR_PTR(status); + return ERR_CAST(params); } - rndis->config = status; + rndis->params = params; return &rndis->port.func; } -DECLARE_USB_FUNCTION(rndis, rndis_alloc_inst, rndis_alloc); - -static int __init rndis_mod_init(void) -{ - int ret; - - ret = rndis_init(); - if (ret) - return ret; - - return usb_function_register(&rndisusb_func); -} -module_init(rndis_mod_init); - -static void __exit rndis_mod_exit(void) -{ - usb_function_unregister(&rndisusb_func); - rndis_exit(); -} -module_exit(rndis_mod_exit); - +DECLARE_USB_FUNCTION_INIT(rndis, rndis_alloc_inst, rndis_alloc); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Brownell"); diff --git a/drivers/usb/gadget/function/rndis.c b/drivers/usb/gadget/function/rndis.c index 95d2324f6977..70d3917cc003 100644 --- a/drivers/usb/gadget/function/rndis.c +++ b/drivers/usb/gadget/function/rndis.c @@ -25,6 +25,7 @@ #include <linux/moduleparam.h> #include <linux/kernel.h> #include <linux/errno.h> +#include <linux/idr.h> #include <linux/list.h> #include <linux/proc_fs.h> #include <linux/slab.h> @@ -57,17 +58,26 @@ MODULE_PARM_DESC (rndis_debug, "enable debugging"); #define rndis_debug 0 #endif -#define RNDIS_MAX_CONFIGS 1 +#ifdef CONFIG_USB_GADGET_DEBUG_FILES +#define NAME_TEMPLATE "driver/rndis-%03d" -static rndis_params rndis_per_dev_params[RNDIS_MAX_CONFIGS]; +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ + +static DEFINE_IDA(rndis_ida); /* Driver Version */ static const __le32 rndis_driver_version = cpu_to_le32(1); /* Function Prototypes */ -static rndis_resp_t *rndis_add_response(int configNr, u32 length); +static rndis_resp_t *rndis_add_response(struct rndis_params *params, + u32 length); + +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + +static const struct file_operations rndis_proc_fops; +#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ /* supported OIDs */ static const u32 oid_supported_list[] = @@ -160,7 +170,7 @@ static const u32 oid_supported_list[] = /* NDIS Functions */ -static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, +static int gen_ndis_query_resp(struct rndis_params *params, u32 OID, u8 *buf, unsigned buf_len, rndis_resp_t *r) { int retval = -ENOTSUPP; @@ -192,7 +202,7 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, outbuf = (__le32 *)&resp[1]; resp->InformationBufferOffset = cpu_to_le32(16); - net = rndis_per_dev_params[configNr].dev; + net = params->dev; stats = dev_get_stats(net, &temp); switch (OID) { @@ -225,7 +235,7 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, /* mandatory */ case RNDIS_OID_GEN_MEDIA_SUPPORTED: pr_debug("%s: RNDIS_OID_GEN_MEDIA_SUPPORTED\n", __func__); - *outbuf = cpu_to_le32(rndis_per_dev_params[configNr].medium); + *outbuf = cpu_to_le32(params->medium); retval = 0; break; @@ -233,16 +243,15 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, case RNDIS_OID_GEN_MEDIA_IN_USE: pr_debug("%s: RNDIS_OID_GEN_MEDIA_IN_USE\n", __func__); /* one medium, one transport... (maybe you do it better) */ - *outbuf = cpu_to_le32(rndis_per_dev_params[configNr].medium); + *outbuf = cpu_to_le32(params->medium); retval = 0; break; /* mandatory */ case RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE: pr_debug("%s: RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE\n", __func__); - if (rndis_per_dev_params[configNr].dev) { - *outbuf = cpu_to_le32( - rndis_per_dev_params[configNr].dev->mtu); + if (params->dev) { + *outbuf = cpu_to_le32(params->dev->mtu); retval = 0; } break; @@ -251,21 +260,18 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, case RNDIS_OID_GEN_LINK_SPEED: if (rndis_debug > 1) pr_debug("%s: RNDIS_OID_GEN_LINK_SPEED\n", __func__); - if (rndis_per_dev_params[configNr].media_state - == RNDIS_MEDIA_STATE_DISCONNECTED) + if (params->media_state == RNDIS_MEDIA_STATE_DISCONNECTED) *outbuf = cpu_to_le32(0); else - *outbuf = cpu_to_le32( - rndis_per_dev_params[configNr].speed); + *outbuf = cpu_to_le32(params->speed); retval = 0; break; /* mandatory */ case RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE: pr_debug("%s: RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE\n", __func__); - if (rndis_per_dev_params[configNr].dev) { - *outbuf = cpu_to_le32( - rndis_per_dev_params[configNr].dev->mtu); + if (params->dev) { + *outbuf = cpu_to_le32(params->dev->mtu); retval = 0; } break; @@ -273,9 +279,8 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, /* mandatory */ case RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE: pr_debug("%s: RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE\n", __func__); - if (rndis_per_dev_params[configNr].dev) { - *outbuf = cpu_to_le32( - rndis_per_dev_params[configNr].dev->mtu); + if (params->dev) { + *outbuf = cpu_to_le32(params->dev->mtu); retval = 0; } break; @@ -283,20 +288,16 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, /* mandatory */ case RNDIS_OID_GEN_VENDOR_ID: pr_debug("%s: RNDIS_OID_GEN_VENDOR_ID\n", __func__); - *outbuf = cpu_to_le32( - rndis_per_dev_params[configNr].vendorID); + *outbuf = cpu_to_le32(params->vendorID); retval = 0; break; /* mandatory */ case RNDIS_OID_GEN_VENDOR_DESCRIPTION: pr_debug("%s: RNDIS_OID_GEN_VENDOR_DESCRIPTION\n", __func__); - if (rndis_per_dev_params[configNr].vendorDescr) { - length = strlen(rndis_per_dev_params[configNr]. - vendorDescr); - memcpy(outbuf, - rndis_per_dev_params[configNr].vendorDescr, - length); + if (params->vendorDescr) { + length = strlen(params->vendorDescr); + memcpy(outbuf, params->vendorDescr, length); } else { outbuf[0] = 0; } @@ -313,7 +314,7 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, /* mandatory */ case RNDIS_OID_GEN_CURRENT_PACKET_FILTER: pr_debug("%s: RNDIS_OID_GEN_CURRENT_PACKET_FILTER\n", __func__); - *outbuf = cpu_to_le32(*rndis_per_dev_params[configNr].filter); + *outbuf = cpu_to_le32(*params->filter); retval = 0; break; @@ -328,8 +329,7 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, case RNDIS_OID_GEN_MEDIA_CONNECT_STATUS: if (rndis_debug > 1) pr_debug("%s: RNDIS_OID_GEN_MEDIA_CONNECT_STATUS\n", __func__); - *outbuf = cpu_to_le32(rndis_per_dev_params[configNr] - .media_state); + *outbuf = cpu_to_le32(params->media_state); retval = 0; break; @@ -409,11 +409,9 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, /* mandatory */ case RNDIS_OID_802_3_PERMANENT_ADDRESS: pr_debug("%s: RNDIS_OID_802_3_PERMANENT_ADDRESS\n", __func__); - if (rndis_per_dev_params[configNr].dev) { + if (params->dev) { length = ETH_ALEN; - memcpy(outbuf, - rndis_per_dev_params[configNr].host_mac, - length); + memcpy(outbuf, params->host_mac, length); retval = 0; } break; @@ -421,11 +419,9 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, /* mandatory */ case RNDIS_OID_802_3_CURRENT_ADDRESS: pr_debug("%s: RNDIS_OID_802_3_CURRENT_ADDRESS\n", __func__); - if (rndis_per_dev_params[configNr].dev) { + if (params->dev) { length = ETH_ALEN; - memcpy(outbuf, - rndis_per_dev_params [configNr].host_mac, - length); + memcpy(outbuf, params->host_mac, length); retval = 0; } break; @@ -490,12 +486,11 @@ static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf, return retval; } -static int gen_ndis_set_resp(u8 configNr, u32 OID, u8 *buf, u32 buf_len, - rndis_resp_t *r) +static int gen_ndis_set_resp(struct rndis_params *params, u32 OID, + u8 *buf, u32 buf_len, rndis_resp_t *r) { rndis_set_cmplt_type *resp; int i, retval = -ENOTSUPP; - struct rndis_params *params; if (!r) return -ENOMEM; @@ -514,7 +509,6 @@ static int gen_ndis_set_resp(u8 configNr, u32 OID, u8 *buf, u32 buf_len, } } - params = &rndis_per_dev_params[configNr]; switch (OID) { case RNDIS_OID_GEN_CURRENT_PACKET_FILTER: @@ -563,16 +557,16 @@ static int gen_ndis_set_resp(u8 configNr, u32 OID, u8 *buf, u32 buf_len, * Response Functions */ -static int rndis_init_response(int configNr, rndis_init_msg_type *buf) +static int rndis_init_response(struct rndis_params *params, + rndis_init_msg_type *buf) { rndis_init_cmplt_type *resp; rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; if (!params->dev) return -ENOTSUPP; - r = rndis_add_response(configNr, sizeof(rndis_init_cmplt_type)); + r = rndis_add_response(params, sizeof(rndis_init_cmplt_type)); if (!r) return -ENOMEM; resp = (rndis_init_cmplt_type *)r->buf; @@ -599,11 +593,11 @@ static int rndis_init_response(int configNr, rndis_init_msg_type *buf) return 0; } -static int rndis_query_response(int configNr, rndis_query_msg_type *buf) +static int rndis_query_response(struct rndis_params *params, + rndis_query_msg_type *buf) { rndis_query_cmplt_type *resp; rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; /* pr_debug("%s: OID = %08X\n", __func__, cpu_to_le32(buf->OID)); */ if (!params->dev) @@ -615,7 +609,7 @@ static int rndis_query_response(int configNr, rndis_query_msg_type *buf) * rndis_query_cmplt_type followed by data. * oid_supported_list is the largest data reply */ - r = rndis_add_response(configNr, + r = rndis_add_response(params, sizeof(oid_supported_list) + sizeof(rndis_query_cmplt_type)); if (!r) return -ENOMEM; @@ -624,7 +618,7 @@ static int rndis_query_response(int configNr, rndis_query_msg_type *buf) resp->MessageType = cpu_to_le32(RNDIS_MSG_QUERY_C); resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ - if (gen_ndis_query_resp(configNr, le32_to_cpu(buf->OID), + if (gen_ndis_query_resp(params, le32_to_cpu(buf->OID), le32_to_cpu(buf->InformationBufferOffset) + 8 + (u8 *)buf, le32_to_cpu(buf->InformationBufferLength), @@ -641,14 +635,14 @@ static int rndis_query_response(int configNr, rndis_query_msg_type *buf) return 0; } -static int rndis_set_response(int configNr, rndis_set_msg_type *buf) +static int rndis_set_response(struct rndis_params *params, + rndis_set_msg_type *buf) { u32 BufLength, BufOffset; rndis_set_cmplt_type *resp; rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; - r = rndis_add_response(configNr, sizeof(rndis_set_cmplt_type)); + r = rndis_add_response(params, sizeof(rndis_set_cmplt_type)); if (!r) return -ENOMEM; resp = (rndis_set_cmplt_type *)r->buf; @@ -671,7 +665,7 @@ static int rndis_set_response(int configNr, rndis_set_msg_type *buf) resp->MessageType = cpu_to_le32(RNDIS_MSG_SET_C); resp->MessageLength = cpu_to_le32(16); resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ - if (gen_ndis_set_resp(configNr, le32_to_cpu(buf->OID), + if (gen_ndis_set_resp(params, le32_to_cpu(buf->OID), ((u8 *)buf) + 8 + BufOffset, BufLength, r)) resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED); else @@ -681,13 +675,13 @@ static int rndis_set_response(int configNr, rndis_set_msg_type *buf) return 0; } -static int rndis_reset_response(int configNr, rndis_reset_msg_type *buf) +static int rndis_reset_response(struct rndis_params *params, + rndis_reset_msg_type *buf) { rndis_reset_cmplt_type *resp; rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; - r = rndis_add_response(configNr, sizeof(rndis_reset_cmplt_type)); + r = rndis_add_response(params, sizeof(rndis_reset_cmplt_type)); if (!r) return -ENOMEM; resp = (rndis_reset_cmplt_type *)r->buf; @@ -702,16 +696,15 @@ static int rndis_reset_response(int configNr, rndis_reset_msg_type *buf) return 0; } -static int rndis_keepalive_response(int configNr, +static int rndis_keepalive_response(struct rndis_params *params, rndis_keepalive_msg_type *buf) { rndis_keepalive_cmplt_type *resp; rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; /* host "should" check only in RNDIS_DATA_INITIALIZED state */ - r = rndis_add_response(configNr, sizeof(rndis_keepalive_cmplt_type)); + r = rndis_add_response(params, sizeof(rndis_keepalive_cmplt_type)); if (!r) return -ENOMEM; resp = (rndis_keepalive_cmplt_type *)r->buf; @@ -729,17 +722,15 @@ static int rndis_keepalive_response(int configNr, /* * Device to Host Comunication */ -static int rndis_indicate_status_msg(int configNr, u32 status) +static int rndis_indicate_status_msg(struct rndis_params *params, u32 status) { rndis_indicate_status_msg_type *resp; rndis_resp_t *r; - struct rndis_params *params = rndis_per_dev_params + configNr; if (params->state == RNDIS_UNINITIALIZED) return -ENOTSUPP; - r = rndis_add_response(configNr, - sizeof(rndis_indicate_status_msg_type)); + r = rndis_add_response(params, sizeof(rndis_indicate_status_msg_type)); if (!r) return -ENOMEM; resp = (rndis_indicate_status_msg_type *)r->buf; @@ -754,53 +745,48 @@ static int rndis_indicate_status_msg(int configNr, u32 status) return 0; } -int rndis_signal_connect(int configNr) +int rndis_signal_connect(struct rndis_params *params) { - rndis_per_dev_params[configNr].media_state - = RNDIS_MEDIA_STATE_CONNECTED; - return rndis_indicate_status_msg(configNr, - RNDIS_STATUS_MEDIA_CONNECT); + params->media_state = RNDIS_MEDIA_STATE_CONNECTED; + return rndis_indicate_status_msg(params, RNDIS_STATUS_MEDIA_CONNECT); } EXPORT_SYMBOL_GPL(rndis_signal_connect); -int rndis_signal_disconnect(int configNr) +int rndis_signal_disconnect(struct rndis_params *params) { - rndis_per_dev_params[configNr].media_state - = RNDIS_MEDIA_STATE_DISCONNECTED; - return rndis_indicate_status_msg(configNr, - RNDIS_STATUS_MEDIA_DISCONNECT); + params->media_state = RNDIS_MEDIA_STATE_DISCONNECTED; + return rndis_indicate_status_msg(params, RNDIS_STATUS_MEDIA_DISCONNECT); } EXPORT_SYMBOL_GPL(rndis_signal_disconnect); -void rndis_uninit(int configNr) +void rndis_uninit(struct rndis_params *params) { u8 *buf; u32 length; - if (configNr >= RNDIS_MAX_CONFIGS) + if (!params) return; - rndis_per_dev_params[configNr].state = RNDIS_UNINITIALIZED; + params->state = RNDIS_UNINITIALIZED; /* drain the response queue */ - while ((buf = rndis_get_next_response(configNr, &length))) - rndis_free_response(configNr, buf); + while ((buf = rndis_get_next_response(params, &length))) + rndis_free_response(params, buf); } EXPORT_SYMBOL_GPL(rndis_uninit); -void rndis_set_host_mac(int configNr, const u8 *addr) +void rndis_set_host_mac(struct rndis_params *params, const u8 *addr) { - rndis_per_dev_params[configNr].host_mac = addr; + params->host_mac = addr; } EXPORT_SYMBOL_GPL(rndis_set_host_mac); /* * Message Parser */ -int rndis_msg_parser(u8 configNr, u8 *buf) +int rndis_msg_parser(struct rndis_params *params, u8 *buf) { u32 MsgType, MsgLength; __le32 *tmp; - struct rndis_params *params; if (!buf) return -ENOMEM; @@ -809,9 +795,8 @@ int rndis_msg_parser(u8 configNr, u8 *buf) MsgType = get_unaligned_le32(tmp++); MsgLength = get_unaligned_le32(tmp++); - if (configNr >= RNDIS_MAX_CONFIGS) + if (!params) return -ENOTSUPP; - params = &rndis_per_dev_params[configNr]; /* NOTE: RNDIS is *EXTREMELY* chatty ... Windows constantly polls for * rx/tx statistics and link status, in addition to KEEPALIVE traffic @@ -824,8 +809,7 @@ int rndis_msg_parser(u8 configNr, u8 *buf) pr_debug("%s: RNDIS_MSG_INIT\n", __func__); params->state = RNDIS_INITIALIZED; - return rndis_init_response(configNr, - (rndis_init_msg_type *)buf); + return rndis_init_response(params, (rndis_init_msg_type *)buf); case RNDIS_MSG_HALT: pr_debug("%s: RNDIS_MSG_HALT\n", @@ -838,17 +822,16 @@ int rndis_msg_parser(u8 configNr, u8 *buf) return 0; case RNDIS_MSG_QUERY: - return rndis_query_response(configNr, + return rndis_query_response(params, (rndis_query_msg_type *)buf); case RNDIS_MSG_SET: - return rndis_set_response(configNr, - (rndis_set_msg_type *)buf); + return rndis_set_response(params, (rndis_set_msg_type *)buf); case RNDIS_MSG_RESET: pr_debug("%s: RNDIS_MSG_RESET\n", __func__); - return rndis_reset_response(configNr, + return rndis_reset_response(params, (rndis_reset_msg_type *)buf); case RNDIS_MSG_KEEPALIVE: @@ -856,7 +839,7 @@ int rndis_msg_parser(u8 configNr, u8 *buf) if (rndis_debug > 1) pr_debug("%s: RNDIS_MSG_KEEPALIVE\n", __func__); - return rndis_keepalive_response(configNr, + return rndis_keepalive_response(params, (rndis_keepalive_msg_type *) buf); @@ -876,71 +859,131 @@ int rndis_msg_parser(u8 configNr, u8 *buf) } EXPORT_SYMBOL_GPL(rndis_msg_parser); -int rndis_register(void (*resp_avail)(void *v), void *v) +static inline int rndis_get_nr(void) { - u8 i; + return ida_simple_get(&rndis_ida, 0, 0, GFP_KERNEL); +} + +static inline void rndis_put_nr(int nr) +{ + ida_simple_remove(&rndis_ida, nr); +} + +struct rndis_params *rndis_register(void (*resp_avail)(void *v), void *v) +{ + struct rndis_params *params; + int i; if (!resp_avail) - return -EINVAL; + return ERR_PTR(-EINVAL); + + i = rndis_get_nr(); + if (i < 0) { + pr_debug("failed\n"); + + return ERR_PTR(-ENODEV); + } + + params = kzalloc(sizeof(*params), GFP_KERNEL); + if (!params) { + rndis_put_nr(i); - for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { - if (!rndis_per_dev_params[i].used) { - rndis_per_dev_params[i].used = 1; - rndis_per_dev_params[i].resp_avail = resp_avail; - rndis_per_dev_params[i].v = v; - pr_debug("%s: configNr = %d\n", __func__, i); - return i; + return ERR_PTR(-ENOMEM); + } + +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + { + struct proc_dir_entry *proc_entry; + char name[20]; + + sprintf(name, NAME_TEMPLATE, i); + proc_entry = proc_create_data(name, 0660, NULL, + &rndis_proc_fops, params); + if (!proc_entry) { + kfree(params); + rndis_put_nr(i); + + return ERR_PTR(-EIO); } } - pr_debug("failed\n"); +#endif + + params->confignr = i; + params->used = 1; + params->state = RNDIS_UNINITIALIZED; + params->media_state = RNDIS_MEDIA_STATE_DISCONNECTED; + params->resp_avail = resp_avail; + params->v = v; + INIT_LIST_HEAD(&(params->resp_queue)); + pr_debug("%s: configNr = %d\n", __func__, i); - return -ENODEV; + return params; } EXPORT_SYMBOL_GPL(rndis_register); -void rndis_deregister(int configNr) +void rndis_deregister(struct rndis_params *params) { + int i; + pr_debug("%s:\n", __func__); - if (configNr >= RNDIS_MAX_CONFIGS) return; - rndis_per_dev_params[configNr].used = 0; + if (!params) + return; + + i = params->confignr; + +#ifdef CONFIG_USB_GADGET_DEBUG_FILES + { + char name[20]; + + sprintf(name, NAME_TEMPLATE, i); + remove_proc_entry(name, NULL); + } +#endif + + kfree(params); + rndis_put_nr(i); } EXPORT_SYMBOL_GPL(rndis_deregister); - -int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter) +int rndis_set_param_dev(struct rndis_params *params, struct net_device *dev, + u16 *cdc_filter) { pr_debug("%s:\n", __func__); if (!dev) return -EINVAL; - if (configNr >= RNDIS_MAX_CONFIGS) return -1; + if (!params) + return -1; - rndis_per_dev_params[configNr].dev = dev; - rndis_per_dev_params[configNr].filter = cdc_filter; + params->dev = dev; + params->filter = cdc_filter; return 0; } EXPORT_SYMBOL_GPL(rndis_set_param_dev); -int rndis_set_param_vendor(u8 configNr, u32 vendorID, const char *vendorDescr) +int rndis_set_param_vendor(struct rndis_params *params, u32 vendorID, + const char *vendorDescr) { pr_debug("%s:\n", __func__); if (!vendorDescr) return -1; - if (configNr >= RNDIS_MAX_CONFIGS) return -1; + if (!params) + return -1; - rndis_per_dev_params[configNr].vendorID = vendorID; - rndis_per_dev_params[configNr].vendorDescr = vendorDescr; + params->vendorID = vendorID; + params->vendorDescr = vendorDescr; return 0; } EXPORT_SYMBOL_GPL(rndis_set_param_vendor); -int rndis_set_param_medium(u8 configNr, u32 medium, u32 speed) +int rndis_set_param_medium(struct rndis_params *params, u32 medium, u32 speed) { pr_debug("%s: %u %u\n", __func__, medium, speed); - if (configNr >= RNDIS_MAX_CONFIGS) return -1; + if (!params) + return -1; - rndis_per_dev_params[configNr].medium = medium; - rndis_per_dev_params[configNr].speed = speed; + params->medium = medium; + params->speed = speed; return 0; } @@ -961,13 +1004,12 @@ void rndis_add_hdr(struct sk_buff *skb) } EXPORT_SYMBOL_GPL(rndis_add_hdr); -void rndis_free_response(int configNr, u8 *buf) +void rndis_free_response(struct rndis_params *params, u8 *buf) { rndis_resp_t *r; struct list_head *act, *tmp; - list_for_each_safe(act, tmp, - &(rndis_per_dev_params[configNr].resp_queue)) + list_for_each_safe(act, tmp, &(params->resp_queue)) { r = list_entry(act, rndis_resp_t, list); if (r && r->buf == buf) { @@ -978,15 +1020,14 @@ void rndis_free_response(int configNr, u8 *buf) } EXPORT_SYMBOL_GPL(rndis_free_response); -u8 *rndis_get_next_response(int configNr, u32 *length) +u8 *rndis_get_next_response(struct rndis_params *params, u32 *length) { rndis_resp_t *r; struct list_head *act, *tmp; if (!length) return NULL; - list_for_each_safe(act, tmp, - &(rndis_per_dev_params[configNr].resp_queue)) + list_for_each_safe(act, tmp, &(params->resp_queue)) { r = list_entry(act, rndis_resp_t, list); if (!r->send) { @@ -1000,7 +1041,7 @@ u8 *rndis_get_next_response(int configNr, u32 *length) } EXPORT_SYMBOL_GPL(rndis_get_next_response); -static rndis_resp_t *rndis_add_response(int configNr, u32 length) +static rndis_resp_t *rndis_add_response(struct rndis_params *params, u32 length) { rndis_resp_t *r; @@ -1012,8 +1053,7 @@ static rndis_resp_t *rndis_add_response(int configNr, u32 length) r->length = length; r->send = 0; - list_add_tail(&r->list, - &(rndis_per_dev_params[configNr].resp_queue)); + list_add_tail(&r->list, &(params->resp_queue)); return r; } @@ -1103,11 +1143,11 @@ static ssize_t rndis_proc_write(struct file *file, const char __user *buffer, break; case 'C': case 'c': - rndis_signal_connect(p->confignr); + rndis_signal_connect(p); break; case 'D': case 'd': - rndis_signal_disconnect(p->confignr); + rndis_signal_disconnect(p); break; default: if (fl_speed) p->speed = speed; @@ -1137,54 +1177,4 @@ static const struct file_operations rndis_proc_fops = { #define NAME_TEMPLATE "driver/rndis-%03d" -static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS]; - #endif /* CONFIG_USB_GADGET_DEBUG_FILES */ - - -int rndis_init(void) -{ - u8 i; - - for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { -#ifdef CONFIG_USB_GADGET_DEBUG_FILES - char name [20]; - - sprintf(name, NAME_TEMPLATE, i); - rndis_connect_state[i] = proc_create_data(name, 0660, NULL, - &rndis_proc_fops, - (void *)(rndis_per_dev_params + i)); - if (!rndis_connect_state[i]) { - pr_debug("%s: remove entries", __func__); - while (i) { - sprintf(name, NAME_TEMPLATE, --i); - remove_proc_entry(name, NULL); - } - pr_debug("\n"); - return -EIO; - } -#endif - rndis_per_dev_params[i].confignr = i; - rndis_per_dev_params[i].used = 0; - rndis_per_dev_params[i].state = RNDIS_UNINITIALIZED; - rndis_per_dev_params[i].media_state - = RNDIS_MEDIA_STATE_DISCONNECTED; - INIT_LIST_HEAD(&(rndis_per_dev_params[i].resp_queue)); - } - - return 0; -} - -void rndis_exit(void) -{ -#ifdef CONFIG_USB_GADGET_DEBUG_FILES - u8 i; - char name[20]; - - for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { - sprintf(name, NAME_TEMPLATE, i); - remove_proc_entry(name, NULL); - } -#endif -} - diff --git a/drivers/usb/gadget/function/rndis.h b/drivers/usb/gadget/function/rndis.h index 0f4abb4c3775..ef92eb66d8ad 100644 --- a/drivers/usb/gadget/function/rndis.h +++ b/drivers/usb/gadget/function/rndis.h @@ -177,7 +177,7 @@ typedef struct rndis_resp_t typedef struct rndis_params { - u8 confignr; + int confignr; u8 used; u16 saved_filter; enum rndis_state state; @@ -197,24 +197,25 @@ typedef struct rndis_params } rndis_params; /* RNDIS Message parser and other useless functions */ -int rndis_msg_parser (u8 configNr, u8 *buf); -int rndis_register(void (*resp_avail)(void *v), void *v); -void rndis_deregister (int configNr); -int rndis_set_param_dev (u8 configNr, struct net_device *dev, +int rndis_msg_parser(struct rndis_params *params, u8 *buf); +struct rndis_params *rndis_register(void (*resp_avail)(void *v), void *v); +void rndis_deregister(struct rndis_params *params); +int rndis_set_param_dev(struct rndis_params *params, struct net_device *dev, u16 *cdc_filter); -int rndis_set_param_vendor (u8 configNr, u32 vendorID, +int rndis_set_param_vendor(struct rndis_params *params, u32 vendorID, const char *vendorDescr); -int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed); -void rndis_add_hdr (struct sk_buff *skb); +int rndis_set_param_medium(struct rndis_params *params, u32 medium, + u32 speed); +void rndis_add_hdr(struct sk_buff *skb); int rndis_rm_hdr(struct gether *port, struct sk_buff *skb, struct sk_buff_head *list); -u8 *rndis_get_next_response (int configNr, u32 *length); -void rndis_free_response (int configNr, u8 *buf); - -void rndis_uninit (int configNr); -int rndis_signal_connect (int configNr); -int rndis_signal_disconnect (int configNr); -int rndis_state (int configNr); -extern void rndis_set_host_mac (int configNr, const u8 *addr); +u8 *rndis_get_next_response(struct rndis_params *params, u32 *length); +void rndis_free_response(struct rndis_params *params, u8 *buf); + +void rndis_uninit(struct rndis_params *params); +int rndis_signal_connect(struct rndis_params *params); +int rndis_signal_disconnect(struct rndis_params *params); +int rndis_state(struct rndis_params *params); +extern void rndis_set_host_mac(struct rndis_params *params, const u8 *addr); #endif /* _LINUX_RNDIS_H */ diff --git a/drivers/usb/gadget/function/u_rndis.h b/drivers/usb/gadget/function/u_rndis.h index e902aa42a297..4eafd5050545 100644 --- a/drivers/usb/gadget/function/u_rndis.h +++ b/drivers/usb/gadget/function/u_rndis.h @@ -39,8 +39,6 @@ struct f_rndis_opts { int refcnt; }; -int rndis_init(void); -void rndis_exit(void); void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net); #endif /* U_RNDIS_H */ diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index ebe409b9e419..7d3bb6272e06 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -56,7 +56,6 @@ struct uvc_event #include <linux/usb/composite.h> #include <linux/usb/gadget.h> #include <linux/videodev2.h> -#include <linux/version.h> #include <media/v4l2-fh.h> #include <media/v4l2-device.h> diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 2030565c6789..f454c7af489c 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -769,9 +769,12 @@ ep_config (struct ep_data *data, const char *buf, size_t len) if (data->dev->state == STATE_DEV_UNBOUND) { value = -ENOENT; goto gone; - } else if ((ep = data->ep) == NULL) { - value = -ENODEV; - goto gone; + } else { + ep = data->ep; + if (ep == NULL) { + value = -ENODEV; + goto gone; + } } switch (data->dev->gadget->speed) { case USB_SPEED_LOW: diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 351d48550c33..4095cce05e6a 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -704,8 +704,8 @@ static int queue_dma(struct usba_udc *udc, struct usba_ep *ep, unsigned long flags; int ret; - DBG(DBG_DMA, "%s: req l/%u d/%08x %c%c%c\n", - ep->ep.name, req->req.length, req->req.dma, + DBG(DBG_DMA, "%s: req l/%u d/%pad %c%c%c\n", + ep->ep.name, req->req.length, &req->req.dma, req->req.zero ? 'Z' : 'z', req->req.short_not_ok ? 'S' : 's', req->req.no_interrupt ? 'I' : 'i'); @@ -2203,7 +2203,7 @@ static int usba_udc_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int usba_udc_suspend(struct device *dev) { struct usba_udc *udc = dev_get_drvdata(dev); diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index 9871b90195ad..2bee912ca65b 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -123,6 +123,11 @@ static char *type_string(u8 bmAttributes) #define valid_bit cpu_to_le32(BIT(VALID_BIT)) #define dma_done_ie cpu_to_le32(BIT(DMA_DONE_INTERRUPT_ENABLE)) +static void ep_clear_seqnum(struct net2280_ep *ep); +static void stop_activity(struct net2280 *dev, + struct usb_gadget_driver *driver); +static void ep0_start(struct net2280 *dev); + /*-------------------------------------------------------------------------*/ static inline void enable_pciirqenb(struct net2280_ep *ep) { @@ -142,7 +147,9 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) { struct net2280 *dev; struct net2280_ep *ep; - u32 max, tmp; + u32 max; + u32 tmp = 0; + u32 type; unsigned long flags; static const u32 ep_key[9] = { 1, 0, 1, 0, 1, 1, 0, 1, 0 }; int ret = 0; @@ -198,15 +205,29 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) /* set type, direction, address; reset fifo counters */ writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat); - tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); - if (tmp == USB_ENDPOINT_XFER_INT) { + + if ((dev->quirks & PLX_SUPERSPEED) && dev->enhanced_mode) { + tmp = readl(&ep->cfg->ep_cfg); + /* If USB ep number doesn't match hardware ep number */ + if ((tmp & 0xf) != usb_endpoint_num(desc)) { + ret = -EINVAL; + spin_unlock_irqrestore(&dev->lock, flags); + goto print_err; + } + if (ep->is_in) + tmp &= ~USB3380_EP_CFG_MASK_IN; + else + tmp &= ~USB3380_EP_CFG_MASK_OUT; + } + type = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); + if (type == USB_ENDPOINT_XFER_INT) { /* erratum 0105 workaround prevents hs NYET */ if (dev->chiprev == 0100 && dev->gadget.speed == USB_SPEED_HIGH && !(desc->bEndpointAddress & USB_DIR_IN)) writel(BIT(CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp); - } else if (tmp == USB_ENDPOINT_XFER_BULK) { + } else if (type == USB_ENDPOINT_XFER_BULK) { /* catch some particularly blatant driver bugs */ if ((dev->gadget.speed == USB_SPEED_SUPER && max != 1024) || (dev->gadget.speed == USB_SPEED_HIGH && max != 512) || @@ -216,10 +237,10 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) goto print_err; } } - ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC); + ep->is_iso = (type == USB_ENDPOINT_XFER_ISOC); /* Enable this endpoint */ if (dev->quirks & PLX_LEGACY) { - tmp <<= ENDPOINT_TYPE; + tmp |= type << ENDPOINT_TYPE; tmp |= desc->bEndpointAddress; /* default full fifo lines */ tmp |= (4 << ENDPOINT_BYTE_COUNT); @@ -228,17 +249,17 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) } else { /* In Legacy mode, only OUT endpoints are used */ if (dev->enhanced_mode && ep->is_in) { - tmp <<= IN_ENDPOINT_TYPE; + tmp |= type << IN_ENDPOINT_TYPE; tmp |= BIT(IN_ENDPOINT_ENABLE); - /* Not applicable to Legacy */ - tmp |= BIT(ENDPOINT_DIRECTION); } else { - tmp <<= OUT_ENDPOINT_TYPE; + tmp |= type << OUT_ENDPOINT_TYPE; tmp |= BIT(OUT_ENDPOINT_ENABLE); tmp |= (ep->is_in << ENDPOINT_DIRECTION); } - tmp |= usb_endpoint_num(desc); + tmp |= (4 << ENDPOINT_BYTE_COUNT); + if (!dev->enhanced_mode) + tmp |= usb_endpoint_num(desc); tmp |= (ep->ep.maxburst << MAX_BURST_SIZE); } @@ -256,6 +277,8 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) BIT(CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp); } + if (dev->quirks & PLX_SUPERSPEED) + ep_clear_seqnum(ep); writel(tmp, &ep->cfg->ep_cfg); /* enable irqs */ @@ -441,6 +464,13 @@ static void ep_reset_338x(struct net2280_regs __iomem *regs, BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) | BIT(DATA_OUT_PING_TOKEN_INTERRUPT) | BIT(DATA_IN_TOKEN_INTERRUPT), &ep->regs->ep_stat); + + tmp = readl(&ep->cfg->ep_cfg); + if (ep->is_in) + tmp &= ~USB3380_EP_CFG_MASK_IN; + else + tmp &= ~USB3380_EP_CFG_MASK_OUT; + writel(tmp, &ep->cfg->ep_cfg); } static void nuke(struct net2280_ep *); @@ -1468,11 +1498,14 @@ static int net2280_pullup(struct usb_gadget *_gadget, int is_on) spin_lock_irqsave(&dev->lock, flags); tmp = readl(&dev->usb->usbctl); dev->softconnect = (is_on != 0); - if (is_on) - tmp |= BIT(USB_DETECT_ENABLE); - else - tmp &= ~BIT(USB_DETECT_ENABLE); - writel(tmp, &dev->usb->usbctl); + if (is_on) { + ep0_start(dev); + writel(tmp | BIT(USB_DETECT_ENABLE), &dev->usb->usbctl); + } else { + writel(tmp & ~BIT(USB_DETECT_ENABLE), &dev->usb->usbctl); + stop_activity(dev, dev->driver); + } + spin_unlock_irqrestore(&dev->lock, flags); return 0; @@ -1860,8 +1893,8 @@ static void defect7374_enable_data_eps_zero(struct net2280 *dev) tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_DIRECTION) | (2 << OUT_ENDPOINT_TYPE) | (2 << IN_ENDPOINT_TYPE) | ((dev->enhanced_mode) ? - BIT(OUT_ENDPOINT_ENABLE) : BIT(ENDPOINT_ENABLE)) | - BIT(IN_ENDPOINT_ENABLE)); + BIT(OUT_ENDPOINT_ENABLE) | BIT(IN_ENDPOINT_ENABLE) : + BIT(ENDPOINT_ENABLE))); for (i = 1; i < 5; i++) writel(tmp, &dev->ep[i].cfg->ep_cfg); @@ -1975,9 +2008,15 @@ static void usb_reset_338x(struct net2280 *dev) /* clear old dma and irq state */ for (tmp = 0; tmp < 4; tmp++) { struct net2280_ep *ep = &dev->ep[tmp + 1]; + struct net2280_dma_regs __iomem *dma; - if (ep->dma) + if (ep->dma) { abort_dma(ep); + } else { + dma = &dev->dma[tmp]; + writel(BIT(DMA_ABORT), &dma->dmastat); + writel(0, &dma->dmactl); + } } writel(~0, &dev->regs->irqstat0), writel(~0, &dev->regs->irqstat1); @@ -2065,6 +2104,12 @@ static void usb_reinit_338x(struct net2280 *dev) if (dev->enhanced_mode) { ep->cfg = &dev->epregs[ne[i]]; + /* + * Set USB endpoint number, hardware allows same number + * in both directions. + */ + if (i > 0 && i < 5) + writel(ne[i], &ep->cfg->ep_cfg); ep->regs = (struct net2280_ep_regs __iomem *) (((void __iomem *)&dev->epregs[ne[i]]) + ep_reg_addr[i]); @@ -2874,6 +2919,26 @@ next_endpoints3: return; } +static void usb338x_handle_ep_intr(struct net2280 *dev, u32 stat0) +{ + u32 index; + u32 bit; + + for (index = 0; index < ARRAY_SIZE(ep_bit); index++) { + bit = BIT(ep_bit[index]); + + if (!stat0) + break; + + if (!(stat0 & bit)) + continue; + + stat0 &= ~bit; + + handle_ep_small(&dev->ep[index]); + } +} + static void handle_stat0_irqs(struct net2280 *dev, u32 stat) { struct net2280_ep *ep; @@ -3098,20 +3163,31 @@ do_stall: #undef w_length next_endpoints: - /* endpoint data irq ? */ - scratch = stat & 0x7f; - stat &= ~0x7f; - for (num = 0; scratch; num++) { - u32 t; - - /* do this endpoint's FIFO and queue need tending? */ - t = BIT(num); - if ((scratch & t) == 0) - continue; - scratch ^= t; + if ((dev->quirks & PLX_SUPERSPEED) && dev->enhanced_mode) { + u32 mask = (BIT(ENDPOINT_0_INTERRUPT) | + USB3380_IRQSTAT0_EP_INTR_MASK_IN | + USB3380_IRQSTAT0_EP_INTR_MASK_OUT); + + if (stat & mask) { + usb338x_handle_ep_intr(dev, stat & mask); + stat &= ~mask; + } + } else { + /* endpoint data irq ? */ + scratch = stat & 0x7f; + stat &= ~0x7f; + for (num = 0; scratch; num++) { + u32 t; + + /* do this endpoint's FIFO and queue need tending? */ + t = BIT(num); + if ((scratch & t) == 0) + continue; + scratch ^= t; - ep = &dev->ep[num]; - handle_ep_small(ep); + ep = &dev->ep[num]; + handle_ep_small(ep); + } } if (stat) diff --git a/drivers/usb/gadget/udc/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c index 99fd9a5667df..5d9aa81969b4 100644 --- a/drivers/usb/gadget/udc/s3c2410_udc.c +++ b/drivers/usb/gadget/udc/s3c2410_udc.c @@ -92,40 +92,38 @@ static struct s3c2410_udc_mach_info *udc_info; static uint32_t s3c2410_ticks = 0; -static int dprintk(int level, const char *fmt, ...) +__printf(2, 3) +static void dprintk(int level, const char *fmt, ...) { - static char printk_buf[1024]; static long prevticks; static int invocation; + struct va_format vaf; va_list args; - int len; if (level > USB_S3C2410_DEBUG_LEVEL) - return 0; + return; + + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; if (s3c2410_ticks != prevticks) { prevticks = s3c2410_ticks; invocation = 0; } - len = scnprintf(printk_buf, - sizeof(printk_buf), "%1lu.%02d USB: ", - prevticks, invocation++); + pr_debug("%1lu.%02d USB: %pV", prevticks, invocation++, &vaf); - va_start(args, fmt); - len = vscnprintf(printk_buf+len, - sizeof(printk_buf)-len, fmt, args); va_end(args); - - pr_debug("%s", printk_buf); - return len; } #else -static int dprintk(int level, const char *fmt, ...) +__printf(2, 3) +static void dprintk(int level, const char *fmt, ...) { - return 0; } #endif + static int s3c2410_udc_debugfs_seq_show(struct seq_file *m, void *p) { u32 addr_reg, pwr_reg, ep_int_reg, usb_int_reg; diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 197a6a3e613b..547cee83400b 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -137,7 +137,7 @@ config XPS_USB_HCD_XILINX devices only. config USB_EHCI_FSL - bool "Support for Freescale PPC on-chip EHCI USB controller" + tristate "Support for Freescale PPC on-chip EHCI USB controller" depends on FSL_SOC select USB_EHCI_ROOT_HUB_TT select USB_FSL_MPH_DR_OF if OF diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 65b0b6a58599..754efaa8ccf8 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -24,7 +24,9 @@ endif obj-$(CONFIG_USB_WHCI_HCD) += whci/ -obj-$(CONFIG_PCI) += pci-quirks.o +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 @@ -70,6 +72,7 @@ obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o obj-$(CONFIG_USB_HWA_HCD) += hwa-hc.o obj-$(CONFIG_USB_IMX21_HCD) += imx21-hcd.o 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 diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c index 524cbf26d992..b26b96e25a13 100644 --- a/drivers/usb/host/ehci-dbg.c +++ b/drivers/usb/host/ehci-dbg.c @@ -628,7 +628,8 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf) unsigned i; __hc32 tag; - if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC))) + seen = kmalloc(DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC); + if (!seen) return 0; seen_count = 0; diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c index ab4eee3df97a..5352e74b92e2 100644 --- a/drivers/usb/host/ehci-fsl.c +++ b/drivers/usb/host/ehci-fsl.c @@ -1,6 +1,6 @@ /* * Copyright 2005-2009 MontaVista Software, Inc. - * Copyright 2008,2012 Freescale Semiconductor, Inc. + * Copyright 2008,2012,2015 Freescale Semiconductor, Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -24,29 +24,38 @@ */ #include <linux/kernel.h> +#include <linux/module.h> #include <linux/types.h> #include <linux/delay.h> #include <linux/pm.h> #include <linux/err.h> +#include <linux/usb.h> +#include <linux/usb/ehci_def.h> +#include <linux/usb/hcd.h> +#include <linux/usb/otg.h> #include <linux/platform_device.h> #include <linux/fsl_devices.h> +#include "ehci.h" #include "ehci-fsl.h" +#define DRIVER_DESC "Freescale EHCI Host controller driver" +#define DRV_NAME "ehci-fsl" + +static struct hc_driver __read_mostly fsl_ehci_hc_driver; + /* configure so an HC device and id are always provided */ /* always called with process context; sleeping is OK */ -/** - * usb_hcd_fsl_probe - initialize FSL-based HCDs - * @drvier: Driver to be used for this HCD +/* + * fsl_ehci_drv_probe - initialize FSL-based HCDs * @pdev: USB Host Controller being probed * Context: !in_interrupt() * * Allocates basic resources for this USB host controller. * */ -static int usb_hcd_fsl_probe(const struct hc_driver *driver, - struct platform_device *pdev) +static int fsl_ehci_drv_probe(struct platform_device *pdev) { struct fsl_usb2_platform_data *pdata; struct usb_hcd *hcd; @@ -86,7 +95,8 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, } irq = res->start; - hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); + hcd = usb_create_hcd(&fsl_ehci_hc_driver, &pdev->dev, + dev_name(&pdev->dev)); if (!hcd) { retval = -ENOMEM; goto err1; @@ -159,38 +169,6 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver, return retval; } -/* may be called without controller electrically present */ -/* may be called with controller, bus, and devices active */ - -/** - * usb_hcd_fsl_remove - shutdown processing for FSL-based HCDs - * @dev: USB Host Controller being removed - * Context: !in_interrupt() - * - * Reverses the effect of usb_hcd_fsl_probe(). - * - */ -static void usb_hcd_fsl_remove(struct usb_hcd *hcd, - struct platform_device *pdev) -{ - struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev); - - if (!IS_ERR_OR_NULL(hcd->usb_phy)) { - otg_set_host(hcd->usb_phy->otg, NULL); - usb_put_phy(hcd->usb_phy); - } - - usb_remove_hcd(hcd); - - /* - * do platform specific un-initialization: - * release iomux pins, disable clock, etc. - */ - if (pdata->exit) - pdata->exit(pdev); - usb_put_hcd(hcd); -} - static int ehci_fsl_setup_phy(struct usb_hcd *hcd, enum fsl_usb2_phy_modes phy_mode, unsigned int port_offset) @@ -636,79 +614,77 @@ static int ehci_start_port_reset(struct usb_hcd *hcd, unsigned port) #define ehci_start_port_reset NULL #endif /* CONFIG_USB_OTG */ +static struct ehci_driver_overrides ehci_fsl_overrides __initdata = { + .extra_priv_size = sizeof(struct ehci_fsl), + .reset = ehci_fsl_setup, +}; -static const struct hc_driver ehci_fsl_hc_driver = { - .description = hcd_name, - .product_desc = "Freescale On-Chip EHCI Host Controller", - .hcd_priv_size = sizeof(struct ehci_fsl), +/** + * fsl_ehci_drv_remove - shutdown processing for FSL-based HCDs + * @dev: USB Host Controller being removed + * Context: !in_interrupt() + * + * Reverses the effect of usb_hcd_fsl_probe(). + * + */ - /* - * generic hardware linkage - */ - .irq = ehci_irq, - .flags = HCD_USB2 | HCD_MEMORY | HCD_BH, +static int fsl_ehci_drv_remove(struct platform_device *pdev) +{ + struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct usb_hcd *hcd = platform_get_drvdata(pdev); - /* - * basic lifecycle operations - */ - .reset = ehci_fsl_setup, - .start = ehci_run, - .stop = ehci_stop, - .shutdown = ehci_shutdown, + if (!IS_ERR_OR_NULL(hcd->usb_phy)) { + otg_set_host(hcd->usb_phy->otg, NULL); + usb_put_phy(hcd->usb_phy); + } - /* - * managing i/o requests and associated device resources - */ - .urb_enqueue = ehci_urb_enqueue, - .urb_dequeue = ehci_urb_dequeue, - .endpoint_disable = ehci_endpoint_disable, - .endpoint_reset = ehci_endpoint_reset, + usb_remove_hcd(hcd); /* - * scheduling support + * do platform specific un-initialization: + * release iomux pins, disable clock, etc. */ - .get_frame_number = ehci_get_frame, + if (pdata->exit) + pdata->exit(pdev); + usb_put_hcd(hcd); - /* - * root hub support - */ - .hub_status_data = ehci_hub_status_data, - .hub_control = ehci_hub_control, - .bus_suspend = ehci_bus_suspend, - .bus_resume = ehci_bus_resume, - .start_port_reset = ehci_start_port_reset, - .relinquish_port = ehci_relinquish_port, - .port_handed_over = ehci_port_handed_over, - - .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, + return 0; +} + +static struct platform_driver ehci_fsl_driver = { + .probe = fsl_ehci_drv_probe, + .remove = fsl_ehci_drv_remove, + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .name = "fsl-ehci", + .pm = EHCI_FSL_PM_OPS, + }, }; -static int ehci_fsl_drv_probe(struct platform_device *pdev) +static int __init ehci_fsl_init(void) { if (usb_disabled()) return -ENODEV; - /* FIXME we only want one one probe() not two */ - return usb_hcd_fsl_probe(&ehci_fsl_hc_driver, pdev); -} + pr_info(DRV_NAME ": " DRIVER_DESC "\n"); -static int ehci_fsl_drv_remove(struct platform_device *pdev) -{ - struct usb_hcd *hcd = platform_get_drvdata(pdev); + ehci_init_driver(&fsl_ehci_hc_driver, &ehci_fsl_overrides); - /* FIXME we only want one one remove() not two */ - usb_hcd_fsl_remove(hcd, pdev); - return 0; + fsl_ehci_hc_driver.product_desc = + "Freescale On-Chip EHCI Host Controller"; + fsl_ehci_hc_driver.start_port_reset = ehci_start_port_reset; + + + return platform_driver_register(&ehci_fsl_driver); } +module_init(ehci_fsl_init); -MODULE_ALIAS("platform:fsl-ehci"); +static void __exit ehci_fsl_cleanup(void) +{ + platform_driver_unregister(&ehci_fsl_driver); +} +module_exit(ehci_fsl_cleanup); -static struct platform_driver ehci_fsl_driver = { - .probe = ehci_fsl_drv_probe, - .remove = ehci_fsl_drv_remove, - .shutdown = usb_hcd_platform_shutdown, - .driver = { - .name = "fsl-ehci", - .pm = EHCI_FSL_PM_OPS, - }, -}; +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index f4d88dfb26a7..c63d82c91f10 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -239,7 +239,7 @@ static void tdi_reset (struct ehci_hcd *ehci) * Reset a non-running (STS_HALT == 1) controller. * Must be called with interrupts enabled and the lock not held. */ -static int ehci_reset (struct ehci_hcd *ehci) +int ehci_reset(struct ehci_hcd *ehci) { int retval; u32 command = ehci_readl(ehci, &ehci->regs->command); @@ -275,6 +275,7 @@ static int ehci_reset (struct ehci_hcd *ehci) ehci->resuming_ports = 0; return retval; } +EXPORT_SYMBOL_GPL(ehci_reset); /* * Idle the controller (turn off the schedules). @@ -1250,11 +1251,6 @@ MODULE_DESCRIPTION(DRIVER_DESC); MODULE_AUTHOR (DRIVER_AUTHOR); MODULE_LICENSE ("GPL"); -#ifdef CONFIG_USB_EHCI_FSL -#include "ehci-fsl.c" -#define PLATFORM_DRIVER ehci_fsl_driver -#endif - #ifdef CONFIG_USB_EHCI_SH #include "ehci-sh.c" #define PLATFORM_DRIVER ehci_hcd_sh_driver diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c index 69208447d213..22abb6830dfa 100644 --- a/drivers/usb/host/ehci-hub.c +++ b/drivers/usb/host/ehci-hub.c @@ -155,7 +155,7 @@ static int ehci_port_change(struct ehci_hcd *ehci) return 0; } -static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, +void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, bool suspending, bool do_wakeup) { int port; @@ -220,6 +220,7 @@ static void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, spin_unlock_irq(&ehci->lock); } +EXPORT_SYMBOL_GPL(ehci_adjust_port_wakeup_flags); static int ehci_bus_suspend (struct usb_hcd *hcd) { diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index d8a75a51d6d4..2593def13cea 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -88,15 +88,13 @@ static int ehci_platform_power_on(struct platform_device *dev) } for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { - if (priv->phys[phy_num]) { - ret = phy_init(priv->phys[phy_num]); - if (ret) - goto err_exit_phy; - ret = phy_power_on(priv->phys[phy_num]); - if (ret) { - phy_exit(priv->phys[phy_num]); - goto err_exit_phy; - } + ret = phy_init(priv->phys[phy_num]); + if (ret) + goto err_exit_phy; + ret = phy_power_on(priv->phys[phy_num]); + if (ret) { + phy_exit(priv->phys[phy_num]); + goto err_exit_phy; } } @@ -104,10 +102,8 @@ static int ehci_platform_power_on(struct platform_device *dev) err_exit_phy: while (--phy_num >= 0) { - if (priv->phys[phy_num]) { - phy_power_off(priv->phys[phy_num]); - phy_exit(priv->phys[phy_num]); - } + phy_power_off(priv->phys[phy_num]); + phy_exit(priv->phys[phy_num]); } err_disable_clks: while (--clk >= 0) @@ -123,10 +119,8 @@ static void ehci_platform_power_off(struct platform_device *dev) int clk, phy_num; for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { - if (priv->phys[phy_num]) { - phy_power_off(priv->phys[phy_num]); - phy_exit(priv->phys[phy_num]); - } + phy_power_off(priv->phys[phy_num]); + phy_exit(priv->phys[phy_num]); } for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--) @@ -154,7 +148,6 @@ static int ehci_platform_probe(struct platform_device *dev) struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev); struct ehci_platform_priv *priv; struct ehci_hcd *ehci; - const char *phy_name; int err, irq, phy_num, clk = 0; if (usb_disabled()) @@ -202,38 +195,28 @@ static int ehci_platform_probe(struct platform_device *dev) "needs-reset-on-resume")) pdata->reset_on_resume = 1; + if (of_property_read_bool(dev->dev.of_node, + "has-transaction-translator")) + pdata->has_tt = 1; + priv->num_phys = of_count_phandle_with_args(dev->dev.of_node, "phys", "#phy-cells"); - priv->num_phys = priv->num_phys > 0 ? priv->num_phys : 1; - priv->phys = devm_kcalloc(&dev->dev, priv->num_phys, - sizeof(struct phy *), GFP_KERNEL); - if (!priv->phys) - return -ENOMEM; + if (priv->num_phys > 0) { + priv->phys = devm_kcalloc(&dev->dev, priv->num_phys, + sizeof(struct phy *), GFP_KERNEL); + if (!priv->phys) + return -ENOMEM; + } else + priv->num_phys = 0; for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { - err = of_property_read_string_index( - dev->dev.of_node, - "phy-names", phy_num, - &phy_name); - - if (err < 0) { - if (priv->num_phys > 1) { - dev_err(&dev->dev, "phy-names not provided"); - goto err_put_hcd; - } else - phy_name = "usb"; - } - - priv->phys[phy_num] = devm_phy_get(&dev->dev, - phy_name); - if (IS_ERR(priv->phys[phy_num])) { - err = PTR_ERR(priv->phys[phy_num]); - if ((priv->num_phys > 1) || - (err == -EPROBE_DEFER)) - goto err_put_hcd; - priv->phys[phy_num] = NULL; - } + priv->phys[phy_num] = devm_of_phy_get_by_index( + &dev->dev, dev->dev.of_node, phy_num); + if (IS_ERR(priv->phys[phy_num])) { + err = PTR_ERR(priv->phys[phy_num]); + goto err_put_hcd; + } } for (clk = 0; clk < EHCI_MAX_CLKS; clk++) { diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c index ff9af29b4e9f..4031b372008e 100644 --- a/drivers/usb/host/ehci-tegra.c +++ b/drivers/usb/host/ehci-tegra.c @@ -304,6 +304,7 @@ struct dma_aligned_buffer { static void free_dma_aligned_buffer(struct urb *urb) { struct dma_aligned_buffer *temp; + size_t length; if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER)) return; @@ -311,9 +312,14 @@ static void free_dma_aligned_buffer(struct urb *urb) temp = container_of(urb->transfer_buffer, struct dma_aligned_buffer, data); - if (usb_urb_dir_in(urb)) - memcpy(temp->old_xfer_buffer, temp->data, - urb->transfer_buffer_length); + if (usb_urb_dir_in(urb)) { + if (usb_pipeisoc(urb->pipe)) + length = urb->transfer_buffer_length; + else + length = urb->actual_length; + + memcpy(temp->old_xfer_buffer, temp->data, length); + } urb->transfer_buffer = temp->old_xfer_buffer; kfree(temp->kmalloc_ptr); diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 52ef0844a180..f700157cd084 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -868,10 +868,13 @@ extern void ehci_init_driver(struct hc_driver *drv, extern int ehci_setup(struct usb_hcd *hcd); extern int ehci_handshake(struct ehci_hcd *ehci, void __iomem *ptr, u32 mask, u32 done, int usec); +extern int ehci_reset(struct ehci_hcd *ehci); #ifdef CONFIG_PM extern int ehci_suspend(struct usb_hcd *hcd, bool do_wakeup); extern int ehci_resume(struct usb_hcd *hcd, bool force_reset); +extern void ehci_adjust_port_wakeup_flags(struct ehci_hcd *ehci, + bool suspending, bool do_wakeup); #endif /* CONFIG_PM */ extern int ehci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c index 7e325e90d7d9..5e0d60035216 100644 --- a/drivers/usb/host/fsl-mph-dr-of.c +++ b/drivers/usb/host/fsl-mph-dr-of.c @@ -126,6 +126,8 @@ static int usb_get_ver_info(struct device_node *np) /* * returns 1 for usb controller version 1.6 * returns 2 for usb controller version 2.2 + * returns 3 for usb controller version 2.4 + * returns 4 for usb controller version 2.5 * returns 0 otherwise */ if (of_device_is_compatible(np, "fsl-usb2-dr")) { @@ -135,6 +137,8 @@ static int usb_get_ver_info(struct device_node *np) ver = FSL_USB_VER_2_2; else if (of_device_is_compatible(np, "fsl-usb2-dr-v2.4")) ver = FSL_USB_VER_2_4; + else if (of_device_is_compatible(np, "fsl-usb2-dr-v2.5")) + ver = FSL_USB_VER_2_5; else /* for previous controller versions */ ver = FSL_USB_VER_OLD; @@ -150,6 +154,10 @@ static int usb_get_ver_info(struct device_node *np) ver = FSL_USB_VER_1_6; else if (of_device_is_compatible(np, "fsl-usb2-mph-v2.2")) ver = FSL_USB_VER_2_2; + else if (of_device_is_compatible(np, "fsl-usb2-mph-v2.4")) + ver = FSL_USB_VER_2_4; + else if (of_device_is_compatible(np, "fsl-usb2-mph-v2.5")) + ver = FSL_USB_VER_2_5; else /* for previous controller versions */ ver = FSL_USB_VER_OLD; } diff --git a/drivers/usb/host/fusbh200-hcd.c b/drivers/usb/host/fusbh200-hcd.c index 00e492eaba6a..1fd8718a9f11 100644 --- a/drivers/usb/host/fusbh200-hcd.c +++ b/drivers/usb/host/fusbh200-hcd.c @@ -499,7 +499,8 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf) unsigned i; __hc32 tag; - if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC))) + seen = kmalloc(DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC); + if (!seen) return 0; seen_count = 0; diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c index 13181dcd9820..d089b3fb7a13 100644 --- a/drivers/usb/host/isp116x-hcd.c +++ b/drivers/usb/host/isp116x-hcd.c @@ -500,7 +500,8 @@ static void start_atl_transfers(struct isp116x *isp116x) if (isp116x->periodic_count) { isp116x->fmindex = index = (isp116x->fmindex + 1) & (PERIODIC_SIZE - 1); - if ((load = isp116x->load[index])) { + load = isp116x->load[index]; + if (load) { /* Bring all int transfers for this frame into the active queue */ isp116x->atl_active = last_ep = diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c index 04f2186939d2..c3eded317495 100644 --- a/drivers/usb/host/ohci-dbg.c +++ b/drivers/usb/host/ohci-dbg.c @@ -491,7 +491,8 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf) char *next; unsigned i; - if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC))) + seen = kmalloc(DBG_SCHED_LIMIT * sizeof *seen, GFP_ATOMIC); + if (!seen) return 0; seen_count = 0; @@ -506,7 +507,8 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf) /* dump a snapshot of the periodic schedule (and load) */ spin_lock_irqsave (&ohci->lock, flags); for (i = 0; i < NUM_INTS; i++) { - if (!(ed = ohci->periodic [i])) + ed = ohci->periodic[i]; + if (!ed) continue; temp = scnprintf (next, size, "%2d [%3d]:", i, ohci->load [i]); diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 1dab9dfbca6a..760cb57e954e 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -155,7 +155,8 @@ static int ohci_urb_enqueue ( int retval = 0; /* every endpoint has a ed, locate and maybe (re)initialize it */ - if (! (ed = ed_get (ohci, urb->ep, urb->dev, pipe, urb->interval))) + ed = ed_get(ohci, urb->ep, urb->dev, pipe, urb->interval); + if (! ed) return -ENOMEM; /* for the private part of the URB we need the number of TDs (size) */ diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index 185ceee52d47..c2669f185f65 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -57,15 +57,13 @@ static int ohci_platform_power_on(struct platform_device *dev) } for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { - if (priv->phys[phy_num]) { - ret = phy_init(priv->phys[phy_num]); - if (ret) - goto err_exit_phy; - ret = phy_power_on(priv->phys[phy_num]); - if (ret) { - phy_exit(priv->phys[phy_num]); - goto err_exit_phy; - } + ret = phy_init(priv->phys[phy_num]); + if (ret) + goto err_exit_phy; + ret = phy_power_on(priv->phys[phy_num]); + if (ret) { + phy_exit(priv->phys[phy_num]); + goto err_exit_phy; } } @@ -73,10 +71,8 @@ static int ohci_platform_power_on(struct platform_device *dev) err_exit_phy: while (--phy_num >= 0) { - if (priv->phys[phy_num]) { - phy_power_off(priv->phys[phy_num]); - phy_exit(priv->phys[phy_num]); - } + phy_power_off(priv->phys[phy_num]); + phy_exit(priv->phys[phy_num]); } err_disable_clks: while (--clk >= 0) @@ -92,10 +88,8 @@ static void ohci_platform_power_off(struct platform_device *dev) int clk, phy_num; for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { - if (priv->phys[phy_num]) { - phy_power_off(priv->phys[phy_num]); - phy_exit(priv->phys[phy_num]); - } + phy_power_off(priv->phys[phy_num]); + phy_exit(priv->phys[phy_num]); } for (clk = OHCI_MAX_CLKS - 1; clk >= 0; clk--) @@ -123,7 +117,6 @@ static int ohci_platform_probe(struct platform_device *dev) struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev); struct ohci_platform_priv *priv; struct ohci_hcd *ohci; - const char *phy_name; int err, irq, phy_num, clk = 0; if (usb_disabled()) @@ -174,36 +167,22 @@ static int ohci_platform_probe(struct platform_device *dev) priv->num_phys = of_count_phandle_with_args(dev->dev.of_node, "phys", "#phy-cells"); - priv->num_phys = priv->num_phys > 0 ? priv->num_phys : 1; - priv->phys = devm_kcalloc(&dev->dev, priv->num_phys, - sizeof(struct phy *), GFP_KERNEL); - if (!priv->phys) - return -ENOMEM; + if (priv->num_phys > 0) { + priv->phys = devm_kcalloc(&dev->dev, priv->num_phys, + sizeof(struct phy *), GFP_KERNEL); + if (!priv->phys) + return -ENOMEM; + } else + priv->num_phys = 0; for (phy_num = 0; phy_num < priv->num_phys; phy_num++) { - err = of_property_read_string_index( - dev->dev.of_node, - "phy-names", phy_num, - &phy_name); - - if (err < 0) { - if (priv->num_phys > 1) { - dev_err(&dev->dev, "phy-names not provided"); - goto err_put_hcd; - } else - phy_name = "usb"; - } - - priv->phys[phy_num] = devm_phy_get(&dev->dev, - phy_name); - if (IS_ERR(priv->phys[phy_num])) { - err = PTR_ERR(priv->phys[phy_num]); - if ((priv->num_phys > 1) || - (err == -EPROBE_DEFER)) - goto err_put_hcd; - priv->phys[phy_num] = NULL; - } + priv->phys[phy_num] = devm_of_phy_get_by_index( + &dev->dev, dev->dev.of_node, phy_num); + if (IS_ERR(priv->phys[phy_num])) { + err = PTR_ERR(priv->phys[phy_num]); + goto err_put_hcd; + } } for (clk = 0; clk < OHCI_MAX_CLKS; clk++) { diff --git a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c index 1463c398d322..f7d561ed3c23 100644 --- a/drivers/usb/host/ohci-q.c +++ b/drivers/usb/host/ohci-q.c @@ -407,7 +407,8 @@ static struct ed *ed_get ( spin_lock_irqsave (&ohci->lock, flags); - if (!(ed = ep->hcpriv)) { + ed = ep->hcpriv; + if (!ed) { struct td *td; int is_out; u32 info; diff --git a/drivers/usb/host/ssb-hcd.c b/drivers/usb/host/ssb-hcd.c index ffc32f4b1b1b..62b6b7804c66 100644 --- a/drivers/usb/host/ssb-hcd.c +++ b/drivers/usb/host/ssb-hcd.c @@ -105,7 +105,7 @@ static struct platform_device *ssb_hcd_create_pdev(struct ssb_device *dev, bool { struct platform_device *hci_dev; struct resource hci_res[2]; - int ret = -ENOMEM; + int ret; memset(hci_res, 0, sizeof(hci_res)); @@ -119,7 +119,7 @@ static struct platform_device *ssb_hcd_create_pdev(struct ssb_device *dev, bool hci_dev = platform_device_alloc(ohci ? "ohci-platform" : "ehci-platform" , 0); if (!hci_dev) - return NULL; + return ERR_PTR(-ENOMEM); hci_dev->dev.parent = dev->dev; hci_dev->dev.dma_mask = &hci_dev->dev.coherent_dma_mask; @@ -166,7 +166,8 @@ static int ssb_hcd_probe(struct ssb_device *dev, if (dma_set_mask_and_coherent(dev->dma_dev, DMA_BIT_MASK(32))) return -EOPNOTSUPP; - usb_dev = kzalloc(sizeof(struct ssb_hcd_device), GFP_KERNEL); + usb_dev = devm_kzalloc(dev->dev, sizeof(struct ssb_hcd_device), + GFP_KERNEL); if (!usb_dev) return -ENOMEM; @@ -181,10 +182,8 @@ static int ssb_hcd_probe(struct ssb_device *dev, start = ssb_admatch_base(tmp); len = (coreid == SSB_DEV_USB20_HOST) ? 0x800 : ssb_admatch_size(tmp); usb_dev->ohci_dev = ssb_hcd_create_pdev(dev, true, start, len); - if (IS_ERR(usb_dev->ohci_dev)) { - err = PTR_ERR(usb_dev->ohci_dev); - goto err_free_usb_dev; - } + if (IS_ERR(usb_dev->ohci_dev)) + return PTR_ERR(usb_dev->ohci_dev); if (coreid == SSB_DEV_USB20_HOST) { start = ssb_admatch_base(tmp) + 0x800; /* ehci core offset */ @@ -200,8 +199,6 @@ static int ssb_hcd_probe(struct ssb_device *dev, err_unregister_ohci_dev: platform_device_unregister(usb_dev->ohci_dev); -err_free_usb_dev: - kfree(usb_dev); return err; } diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c index 0827d7c96527..e75c565feb53 100644 --- a/drivers/usb/host/xhci-hub.c +++ b/drivers/usb/host/xhci-hub.c @@ -1184,6 +1184,10 @@ int xhci_bus_resume(struct usb_hcd *hcd) struct xhci_bus_state *bus_state; u32 temp; unsigned long flags; + unsigned long port_was_suspended = 0; + bool need_usb2_u3_exit = false; + int slot_id; + int sret; max_ports = xhci_get_ports(hcd, &port_array); bus_state = &xhci->bus_state[hcd_index(hcd)]; @@ -1207,7 +1211,6 @@ int xhci_bus_resume(struct usb_hcd *hcd) /* Check whether need resume ports. If needed resume port and disable remote wakeup */ u32 temp; - int slot_id; temp = readl(port_array[port_index]); if (DEV_SUPERSPEED(temp)) @@ -1216,39 +1219,47 @@ int xhci_bus_resume(struct usb_hcd *hcd) temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS); if (test_bit(port_index, &bus_state->bus_suspended) && (temp & PORT_PLS_MASK)) { - if (DEV_SUPERSPEED(temp)) { - xhci_set_link_state(xhci, port_array, - port_index, XDEV_U0); - } else { + set_bit(port_index, &port_was_suspended); + if (!DEV_SUPERSPEED(temp)) { xhci_set_link_state(xhci, port_array, port_index, XDEV_RESUME); - - spin_unlock_irqrestore(&xhci->lock, flags); - msleep(20); - spin_lock_irqsave(&xhci->lock, flags); - - xhci_set_link_state(xhci, port_array, - port_index, XDEV_U0); + need_usb2_u3_exit = true; } - /* wait for the port to enter U0 and report port link - * state change. - */ - spin_unlock_irqrestore(&xhci->lock, flags); - msleep(20); - spin_lock_irqsave(&xhci->lock, flags); - - /* Clear PLC */ - xhci_test_and_clear_bit(xhci, port_array, port_index, - PORT_PLC); - - slot_id = xhci_find_slot_id_by_port(hcd, - xhci, port_index + 1); - if (slot_id) - xhci_ring_device(xhci, slot_id); } else writel(temp, port_array[port_index]); } + if (need_usb2_u3_exit) { + spin_unlock_irqrestore(&xhci->lock, flags); + msleep(20); + spin_lock_irqsave(&xhci->lock, flags); + } + + port_index = max_ports; + while (port_index--) { + if (!(port_was_suspended & BIT(port_index))) + continue; + /* Clear PLC to poll it later after XDEV_U0 */ + xhci_test_and_clear_bit(xhci, port_array, port_index, PORT_PLC); + xhci_set_link_state(xhci, port_array, port_index, XDEV_U0); + } + + port_index = max_ports; + while (port_index--) { + if (!(port_was_suspended & BIT(port_index))) + continue; + /* Poll and Clear PLC */ + sret = xhci_handshake(port_array[port_index], PORT_PLC, + PORT_PLC, 10 * 1000); + if (sret) + xhci_warn(xhci, "port %d resume PLC timeout\n", + port_index); + xhci_test_and_clear_bit(xhci, port_array, port_index, PORT_PLC); + slot_id = xhci_find_slot_id_by_port(hcd, xhci, port_index + 1); + if (slot_id) + xhci_ring_device(xhci, slot_id); + } + (void) readl(&xhci->op_regs->command); bus_state->next_statechange = jiffies + msecs_to_jiffies(5); diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index 2af32e26fafc..4a4cb1d91ac8 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -45,6 +45,13 @@ static const char hcd_name[] = "xhci_hcd"; static struct hc_driver __read_mostly xhci_pci_hc_driver; +static int xhci_pci_setup(struct usb_hcd *hcd); + +static const struct xhci_driver_overrides xhci_pci_overrides __initconst = { + .extra_priv_size = sizeof(struct xhci_hcd), + .reset = xhci_pci_setup, +}; + /* called after powerup, by probe or system-pm "wakeup" */ static int xhci_pci_reinit(struct xhci_hcd *xhci, struct pci_dev *pdev) { @@ -206,7 +213,6 @@ static int xhci_pci_setup(struct usb_hcd *hcd) if (!retval) return retval; - kfree(xhci); return retval; } @@ -247,11 +253,6 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) goto dealloc_usb2_hcd; } - /* Set the xHCI pointer before xhci_pci_setup() (aka hcd_driver.reset) - * is called by usb_add_hcd(). - */ - *((struct xhci_hcd **) xhci->shared_hcd->hcd_priv) = xhci; - retval = usb_add_hcd(xhci->shared_hcd, dev->irq, IRQF_SHARED); if (retval) @@ -290,8 +291,6 @@ static void xhci_pci_remove(struct pci_dev *dev) /* Workaround for spurious wakeups at shutdown with HSW */ if (xhci->quirks & XHCI_SPURIOUS_WAKEUP) pci_set_power_state(dev, PCI_D3hot); - - kfree(xhci); } #ifdef CONFIG_PM @@ -379,7 +378,7 @@ static struct pci_driver xhci_pci_driver = { static int __init xhci_pci_init(void) { - xhci_init_driver(&xhci_pci_hc_driver, xhci_pci_setup); + xhci_init_driver(&xhci_pci_hc_driver, &xhci_pci_overrides); #ifdef CONFIG_PM xhci_pci_hc_driver.pci_suspend = xhci_pci_suspend; xhci_pci_hc_driver.pci_resume = xhci_pci_resume; diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c index 783e819139a7..890ad9d9d329 100644 --- a/drivers/usb/host/xhci-plat.c +++ b/drivers/usb/host/xhci-plat.c @@ -26,6 +26,15 @@ static struct hc_driver __read_mostly xhci_plat_hc_driver; +static int xhci_plat_setup(struct usb_hcd *hcd); +static int xhci_plat_start(struct usb_hcd *hcd); + +static const struct xhci_driver_overrides xhci_plat_overrides __initconst = { + .extra_priv_size = sizeof(struct xhci_hcd), + .reset = xhci_plat_setup, + .start = xhci_plat_start, +}; + static void xhci_plat_quirks(struct device *dev, struct xhci_hcd *xhci) { /* @@ -127,31 +136,21 @@ static int xhci_plat_probe(struct platform_device *pdev) goto disable_clk; } - ret = usb_add_hcd(hcd, irq, IRQF_SHARED); - if (ret) - goto disable_clk; - device_wakeup_enable(hcd->self.controller); - /* USB 2.0 roothub is stored in the platform_device now. */ - hcd = platform_get_drvdata(pdev); xhci = hcd_to_xhci(hcd); xhci->clk = clk; + xhci->main_hcd = hcd; xhci->shared_hcd = usb_create_shared_hcd(driver, &pdev->dev, dev_name(&pdev->dev), hcd); if (!xhci->shared_hcd) { ret = -ENOMEM; - goto dealloc_usb2_hcd; + goto disable_clk; } if ((node && of_property_read_bool(node, "usb3-lpm-capable")) || (pdata && pdata->usb3_lpm_capable)) xhci->quirks |= XHCI_LPM_SUPPORT; - /* - * Set the xHCI pointer before xhci_plat_setup() (aka hcd_driver.reset) - * is called by usb_add_hcd(). - */ - *((struct xhci_hcd **) xhci->shared_hcd->hcd_priv) = xhci; if (HCC_MAX_PSA(xhci->hcc_params) >= 4) xhci->shared_hcd->can_do_streams = 1; @@ -168,21 +167,26 @@ static int xhci_plat_probe(struct platform_device *pdev) goto put_usb3_hcd; } - ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED); + ret = usb_add_hcd(hcd, irq, IRQF_SHARED); if (ret) goto disable_usb_phy; + ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED); + if (ret) + goto dealloc_usb2_hcd; + return 0; + +dealloc_usb2_hcd: + usb_remove_hcd(hcd); + disable_usb_phy: usb_phy_shutdown(hcd->usb_phy); put_usb3_hcd: usb_put_hcd(xhci->shared_hcd); -dealloc_usb2_hcd: - usb_remove_hcd(hcd); - disable_clk: if (!IS_ERR(clk)) clk_disable_unprepare(clk); @@ -201,13 +205,13 @@ static int xhci_plat_remove(struct platform_device *dev) usb_remove_hcd(xhci->shared_hcd); usb_phy_shutdown(hcd->usb_phy); - usb_put_hcd(xhci->shared_hcd); usb_remove_hcd(hcd); + usb_put_hcd(xhci->shared_hcd); + if (!IS_ERR(clk)) clk_disable_unprepare(clk); usb_put_hcd(hcd); - kfree(xhci); return 0; } @@ -271,8 +275,7 @@ MODULE_ALIAS("platform:xhci-hcd"); static int __init xhci_plat_init(void) { - xhci_init_driver(&xhci_plat_hc_driver, xhci_plat_setup); - xhci_plat_hc_driver.start = xhci_plat_start; + xhci_init_driver(&xhci_plat_hc_driver, &xhci_plat_overrides); return platform_driver_register(&usb_xhci_driver); } module_init(xhci_plat_init); diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 7d34cbfaf373..94416ff70810 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1934,7 +1934,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td, td->urb->actual_length = td->urb->transfer_buffer_length - EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)); - else + else if (!td->urb_length_set) td->urb->actual_length = 0; return finish_td(xhci, td, event_trb, event, ep, status, false); @@ -3781,8 +3781,11 @@ static int queue_command(struct xhci_hcd *xhci, struct xhci_command *cmd, { int reserved_trbs = xhci->cmd_ring_reserved_trbs; int ret; - if (xhci->xhc_state & XHCI_STATE_DYING) + + if (xhci->xhc_state) { + xhci_dbg(xhci, "xHCI dying or halted, can't queue_command\n"); return -ESHUTDOWN; + } if (!command_must_succeed) reserved_trbs++; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 36bf089b708f..7da0d6043d33 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -660,12 +660,6 @@ static void xhci_only_stop_hcd(struct usb_hcd *hcd) spin_lock_irq(&xhci->lock); xhci_halt(xhci); - - /* The shared_hcd is going to be deallocated shortly (the USB core only - * calls this function when allocation fails in usb_add_hcd(), or - * usb_remove_hcd() is called). So we need to unset xHCI's pointer. - */ - xhci->shared_hcd = NULL; spin_unlock_irq(&xhci->lock); } @@ -897,6 +891,9 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup) struct usb_hcd *hcd = xhci_to_hcd(xhci); u32 command; + if (!hcd->state) + return 0; + if (hcd->state != HC_STATE_SUSPENDED || xhci->shared_hcd->state != HC_STATE_SUSPENDED) return -EINVAL; @@ -983,6 +980,9 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated) int retval = 0; bool comp_timer_running = false; + if (!hcd->state) + return 0; + /* Wait a bit if either of the roothubs need to settle from the * transition into bus suspend. */ @@ -3769,8 +3769,6 @@ disable_slot: /* * Issue an Address Device command and optionally send a corresponding * SetAddress request to the device. - * We should be protected by the usb_address0_mutex in hub_wq's hub_port_init, - * so we should only issue and wait on one address command at the same time. */ static int xhci_setup_device(struct usb_hcd *hcd, struct usb_device *udev, enum xhci_setup_dev setup) @@ -4842,10 +4840,7 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) hcd->self.no_stop_on_short = 1; if (usb_hcd_is_primary_hcd(hcd)) { - xhci = kzalloc(sizeof(struct xhci_hcd), GFP_KERNEL); - if (!xhci) - return -ENOMEM; - *((struct xhci_hcd **) hcd->hcd_priv) = xhci; + 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. @@ -4894,13 +4889,13 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) /* Make sure the HC is halted. */ retval = xhci_halt(xhci); if (retval) - goto error; + return retval; xhci_dbg(xhci, "Resetting HCD\n"); /* Reset the internal HC memory state and registers. */ retval = xhci_reset(xhci); if (retval) - goto error; + return retval; xhci_dbg(xhci, "Reset complete\n"); /* Set dma_mask and coherent_dma_mask to 64-bits, @@ -4915,16 +4910,13 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) /* Initialize HCD and host controller data structures. */ retval = xhci_init(hcd); if (retval) - goto error; + return retval; xhci_dbg(xhci, "Called HCD init\n"); xhci_info(xhci, "hcc params 0x%08x hci version 0x%x quirks 0x%08x\n", xhci->hcc_params, xhci->hci_version, xhci->quirks); return 0; -error: - kfree(xhci); - return retval; } EXPORT_SYMBOL_GPL(xhci_gen_setup); @@ -4989,11 +4981,21 @@ static const struct hc_driver xhci_hc_driver = { .find_raw_port_number = xhci_find_raw_port_number, }; -void xhci_init_driver(struct hc_driver *drv, int (*setup_fn)(struct usb_hcd *)) +void xhci_init_driver(struct hc_driver *drv, + const struct xhci_driver_overrides *over) { - BUG_ON(!setup_fn); + BUG_ON(!over); + + /* Copy the generic table to drv then apply the overrides */ *drv = xhci_hc_driver; - drv->reset = setup_fn; + + if (over) { + drv->hcd_priv_size += over->extra_priv_size; + if (over->reset) + drv->reset = over->reset; + if (over->start) + drv->start = over->start; + } } EXPORT_SYMBOL_GPL(xhci_init_driver); diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 6977f8491fa7..31e46cc55807 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1593,10 +1593,24 @@ struct xhci_hcd { #define COMP_MODE_RCVRY_MSECS 2000 }; +/* Platform specific overrides to generic XHCI hc_driver ops */ +struct xhci_driver_overrides { + size_t extra_priv_size; + int (*reset)(struct usb_hcd *hcd); + int (*start)(struct usb_hcd *hcd); +}; + /* convert between an HCD pointer and the corresponding EHCI_HCD */ static inline struct xhci_hcd *hcd_to_xhci(struct usb_hcd *hcd) { - return *((struct xhci_hcd **) (hcd->hcd_priv)); + struct usb_hcd *primary_hcd; + + if (usb_hcd_is_primary_hcd(hcd)) + primary_hcd = hcd; + else + primary_hcd = hcd->primary_hcd; + + return (struct xhci_hcd *) (primary_hcd->hcd_priv); } static inline struct usb_hcd *xhci_to_hcd(struct xhci_hcd *xhci) @@ -1750,7 +1764,8 @@ int xhci_run(struct usb_hcd *hcd); void xhci_stop(struct usb_hcd *hcd); void xhci_shutdown(struct usb_hcd *hcd); int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks); -void xhci_init_driver(struct hc_driver *drv, int (*setup_fn)(struct usb_hcd *)); +void xhci_init_driver(struct hc_driver *drv, + const struct xhci_driver_overrides *over); #ifdef CONFIG_PM int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup); diff --git a/drivers/usb/isp1760/isp1760-udc.c b/drivers/usb/isp1760/isp1760-udc.c index 3fc4fe770253..18ebf5b1f256 100644 --- a/drivers/usb/isp1760/isp1760-udc.c +++ b/drivers/usb/isp1760/isp1760-udc.c @@ -812,6 +812,8 @@ static struct usb_request *isp1760_ep_alloc_request(struct usb_ep *ep, struct isp1760_request *req; req = kzalloc(sizeof(*req), gfp_flags); + if (!req) + return NULL; return &req->req; } diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c index 022dc0008f2a..306d6852ebc7 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.c +++ b/drivers/usb/misc/sisusbvga/sisusb.c @@ -2316,10 +2316,12 @@ sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init) /* Set mode 0x03 */ SiSUSBSetMode(sisusb->SiS_Pr, 0x03); - if (!(myfont = find_font("VGA8x16"))) + myfont = find_font("VGA8x16"); + if (!myfont) return 1; - if (!(tempbuf = vmalloc(8192))) + tempbuf = vmalloc(8192); + if (!tempbuf) return 1; for (i = 0; i < 256; i++) @@ -2342,7 +2344,8 @@ sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init) if (init && !sisusb->scrbuf) { - if ((tempbuf = vmalloc(8192))) { + tempbuf = vmalloc(8192); + if (tempbuf) { i = 4096; tempbufb = (u16 *)tempbuf; @@ -2417,11 +2420,13 @@ sisusb_open(struct inode *inode, struct file *file) struct usb_interface *interface; int subminor = iminor(inode); - if (!(interface = usb_find_interface(&sisusb_driver, subminor))) { + interface = usb_find_interface(&sisusb_driver, subminor); + if (!interface) { return -ENODEV; } - if (!(sisusb = usb_get_intfdata(interface))) { + sisusb = usb_get_intfdata(interface); + if (!sisusb) { return -ENODEV; } @@ -2488,7 +2493,8 @@ sisusb_release(struct inode *inode, struct file *file) { struct sisusb_usb_data *sisusb; - if (!(sisusb = file->private_data)) + sisusb = file->private_data; + if (!sisusb) return -ENODEV; mutex_lock(&sisusb->lock); @@ -2520,7 +2526,8 @@ sisusb_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) u16 buf16; u32 buf32, address; - if (!(sisusb = file->private_data)) + sisusb = file->private_data; + if (!sisusb) return -ENODEV; mutex_lock(&sisusb->lock); @@ -2662,7 +2669,8 @@ sisusb_write(struct file *file, const char __user *buffer, size_t count, u16 buf16; u32 buf32, address; - if (!(sisusb = file->private_data)) + sisusb = file->private_data; + if (!sisusb) return -ENODEV; mutex_lock(&sisusb->lock); @@ -2805,7 +2813,8 @@ sisusb_lseek(struct file *file, loff_t offset, int orig) struct sisusb_usb_data *sisusb; loff_t ret; - if (!(sisusb = file->private_data)) + sisusb = file->private_data; + if (!sisusb) return -ENODEV; mutex_lock(&sisusb->lock); @@ -2970,7 +2979,8 @@ sisusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) long retval = 0; u32 __user *argp = (u32 __user *)arg; - if (!(sisusb = file->private_data)) + sisusb = file->private_data; + if (!sisusb) return -ENODEV; mutex_lock(&sisusb->lock); @@ -3084,7 +3094,8 @@ static int sisusb_probe(struct usb_interface *intf, dev->devnum); /* Allocate memory for our private */ - if (!(sisusb = kzalloc(sizeof(*sisusb), GFP_KERNEL))) { + sisusb = kzalloc(sizeof(*sisusb), GFP_KERNEL); + if (!sisusb) { dev_err(&dev->dev, "Failed to allocate memory for private data\n"); return -ENOMEM; } @@ -3093,7 +3104,8 @@ static int sisusb_probe(struct usb_interface *intf, mutex_init(&(sisusb->lock)); /* Register device */ - if ((retval = usb_register_dev(intf, &usb_sisusb_class))) { + retval = usb_register_dev(intf, &usb_sisusb_class); + if (retval) { dev_err(&sisusb->sisusb_dev->dev, "Failed to get a minor for device %d\n", dev->devnum); retval = -ENODEV; @@ -3214,7 +3226,8 @@ static void sisusb_disconnect(struct usb_interface *intf) struct sisusb_usb_data *sisusb; /* This should *not* happen */ - if (!(sisusb = usb_get_intfdata(intf))) + sisusb = usb_get_intfdata(intf); + if (!sisusb) return; #ifdef INCL_SISUSB_CON diff --git a/drivers/usb/misc/sisusbvga/sisusb_con.c b/drivers/usb/misc/sisusbvga/sisusb_con.c index a638c4e9a947..ace343088915 100644 --- a/drivers/usb/misc/sisusbvga/sisusb_con.c +++ b/drivers/usb/misc/sisusbvga/sisusb_con.c @@ -169,7 +169,8 @@ sisusb_get_sisusb_lock_and_check(unsigned short console) if (in_atomic()) return NULL; - if (!(sisusb = sisusb_get_sisusb(console))) + sisusb = sisusb_get_sisusb(console); + if (!sisusb) return NULL; mutex_lock(&sisusb->lock); @@ -214,7 +215,8 @@ sisusbcon_init(struct vc_data *c, int init) * are set up/restored. */ - if (!(sisusb = sisusb_get_sisusb(c->vc_num))) + sisusb = sisusb_get_sisusb(c->vc_num); + if (!sisusb) return; mutex_lock(&sisusb->lock); @@ -277,7 +279,8 @@ sisusbcon_deinit(struct vc_data *c) * and others, ie not under our control. */ - if (!(sisusb = sisusb_get_sisusb(c->vc_num))) + sisusb = sisusb_get_sisusb(c->vc_num); + if (!sisusb) return; mutex_lock(&sisusb->lock); @@ -369,7 +372,8 @@ sisusbcon_putc(struct vc_data *c, int ch, int y, int x) struct sisusb_usb_data *sisusb; ssize_t written; - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return; /* sisusb->lock is down */ @@ -395,7 +399,8 @@ sisusbcon_putcs(struct vc_data *c, const unsigned short *s, u16 *dest; int i; - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return; /* sisusb->lock is down */ @@ -433,7 +438,8 @@ sisusbcon_clear(struct vc_data *c, int y, int x, int height, int width) if (width <= 0 || height <= 0) return; - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return; /* sisusb->lock is down */ @@ -486,7 +492,8 @@ sisusbcon_bmove(struct vc_data *c, int sy, int sx, if (width <= 0 || height <= 0) return; - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return; /* sisusb->lock is down */ @@ -520,7 +527,8 @@ sisusbcon_switch(struct vc_data *c) * Returnvalue != 0 naturally means the opposite. */ - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return 0; /* sisusb->lock is down */ @@ -569,7 +577,8 @@ sisusbcon_save_screen(struct vc_data *c) * buffer. */ - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return; /* sisusb->lock is down */ @@ -602,7 +611,8 @@ sisusbcon_set_palette(struct vc_data *c, unsigned char *table) if (!CON_IS_VISIBLE(c)) return -EINVAL; - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return -EINVAL; /* sisusb->lock is down */ @@ -637,7 +647,8 @@ sisusbcon_blank(struct vc_data *c, int blank, int mode_switch) ssize_t written; int ret = 0; - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return 0; /* sisusb->lock is down */ @@ -721,7 +732,8 @@ sisusbcon_scrolldelta(struct vc_data *c, int lines) /* The return value does not seem to be used */ - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return 0; /* sisusb->lock is down */ @@ -779,7 +791,8 @@ sisusbcon_cursor(struct vc_data *c, int mode) struct sisusb_usb_data *sisusb; int from, to, baseline; - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return; /* sisusb->lock is down */ @@ -906,7 +919,8 @@ sisusbcon_scroll(struct vc_data *c, int t, int b, int dir, int lines) if (!lines) return 1; - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return 0; /* sisusb->lock is down */ @@ -1018,7 +1032,8 @@ sisusbcon_set_origin(struct vc_data *c) * screenbuffer as the origin. */ - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return 0; /* sisusb->lock is down */ @@ -1047,7 +1062,8 @@ sisusbcon_resize(struct vc_data *c, unsigned int newcols, unsigned int newrows, struct sisusb_usb_data *sisusb; int fh; - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return -ENODEV; fh = sisusb->current_font_height; @@ -1286,7 +1302,8 @@ sisusbcon_font_set(struct vc_data *c, struct console_font *font, if (font->width != 8 || (charcount != 256 && charcount != 512)) return -EINVAL; - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return -ENODEV; /* sisusb->lock is down */ @@ -1326,7 +1343,8 @@ sisusbcon_font_get(struct vc_data *c, struct console_font *font) { struct sisusb_usb_data *sisusb; - if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num))) + sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); + if (!sisusb) return -ENODEV; /* sisusb->lock is down */ diff --git a/drivers/usb/misc/uss720.c b/drivers/usb/misc/uss720.c index 588d62a73e1a..bbd029c9c725 100644 --- a/drivers/usb/misc/uss720.c +++ b/drivers/usb/misc/uss720.c @@ -714,7 +714,8 @@ static int uss720_probe(struct usb_interface *intf, /* * Allocate parport interface */ - if (!(priv = kzalloc(sizeof(struct parport_uss720_private), GFP_KERNEL))) { + priv = kzalloc(sizeof(struct parport_uss720_private), GFP_KERNEL); + if (!priv) { usb_put_dev(usbdev); return -ENOMEM; } @@ -723,7 +724,8 @@ static int uss720_probe(struct usb_interface *intf, kref_init(&priv->ref_count); spin_lock_init(&priv->asynclock); INIT_LIST_HEAD(&priv->asynclist); - if (!(pp = parport_register_port(0, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &parport_uss720_ops))) { + pp = parport_register_port(0, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &parport_uss720_ops); + if (!pp) { printk(KERN_WARNING "uss720: could not register parport\n"); goto probe_abort; } diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index 9a62e89d6dc0..3598f1a62673 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -675,7 +675,8 @@ static int mon_bin_open(struct inode *inode, struct file *file) int rc; mutex_lock(&mon_lock); - if ((mbus = mon_bus_lookup(iminor(inode))) == NULL) { + mbus = mon_bus_lookup(iminor(inode)); + if (mbus == NULL) { mutex_unlock(&mon_lock); return -ENODEV; } @@ -1018,8 +1019,8 @@ static long mon_bin_ioctl(struct file *file, unsigned int cmd, unsigned long arg return -EINVAL; size = CHUNK_ALIGN(arg); - if ((vec = kzalloc(sizeof(struct mon_pgmap) * (size/CHUNK_SIZE), - GFP_KERNEL)) == NULL) { + vec = kzalloc(sizeof(struct mon_pgmap) * (size / CHUNK_SIZE), GFP_KERNEL); + if (vec == NULL) { ret = -ENOMEM; break; } diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c index 10405119985c..f7c292f4891e 100644 --- a/drivers/usb/mon/mon_main.c +++ b/drivers/usb/mon/mon_main.c @@ -96,7 +96,8 @@ static void mon_submit(struct usb_bus *ubus, struct urb *urb) { struct mon_bus *mbus; - if ((mbus = ubus->mon_bus) != NULL) + mbus = ubus->mon_bus; + if (mbus != NULL) mon_bus_submit(mbus, urb); mon_bus_submit(&mon_bus0, urb); } @@ -122,7 +123,8 @@ static void mon_submit_error(struct usb_bus *ubus, struct urb *urb, int error) { struct mon_bus *mbus; - if ((mbus = ubus->mon_bus) != NULL) + mbus = ubus->mon_bus; + if (mbus != NULL) mon_bus_submit_error(mbus, urb, error); mon_bus_submit_error(&mon_bus0, urb, error); } @@ -148,7 +150,8 @@ static void mon_complete(struct usb_bus *ubus, struct urb *urb, int status) { struct mon_bus *mbus; - if ((mbus = ubus->mon_bus) != NULL) + mbus = ubus->mon_bus; + if (mbus != NULL) mon_bus_complete(mbus, urb, status); mon_bus_complete(&mon_bus0, urb, status); } @@ -280,7 +283,8 @@ static void mon_bus_init(struct usb_bus *ubus) { struct mon_bus *mbus; - if ((mbus = kzalloc(sizeof(struct mon_bus), GFP_KERNEL)) == NULL) + mbus = kzalloc(sizeof(struct mon_bus), GFP_KERNEL); + if (mbus == NULL) goto err_alloc; kref_init(&mbus->ref); spin_lock_init(&mbus->lock); diff --git a/drivers/usb/mon/mon_stat.c b/drivers/usb/mon/mon_stat.c index ebd6189a5014..5388a339cfb8 100644 --- a/drivers/usb/mon/mon_stat.c +++ b/drivers/usb/mon/mon_stat.c @@ -28,7 +28,8 @@ static int mon_stat_open(struct inode *inode, struct file *file) struct mon_bus *mbus; struct snap *sp; - if ((sp = kmalloc(sizeof(struct snap), GFP_KERNEL)) == NULL) + sp = kmalloc(sizeof(struct snap), GFP_KERNEL); + if (sp == NULL) return -ENOMEM; mbus = inode->i_private; diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c index 220fd4d3b41c..c41fe588d14d 100644 --- a/drivers/usb/musb/am35x.c +++ b/drivers/usb/musb/am35x.c @@ -438,11 +438,15 @@ static void am35x_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) } static const struct musb_platform_ops am35x_ops = { - .quirks = MUSB_INDEXED_EP, + .quirks = MUSB_DMA_INVENTRA | MUSB_INDEXED_EP, .init = am35x_musb_init, .exit = am35x_musb_exit, .read_fifo = am35x_read_fifo, +#ifdef CONFIG_USB_INVENTRA_DMA + .dma_init = musbhs_dma_controller_create, + .dma_exit = musbhs_dma_controller_destroy, +#endif .enable = am35x_musb_enable, .disable = am35x_musb_disable, @@ -565,7 +569,7 @@ static int am35x_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int am35x_suspend(struct device *dev) { struct am35x_glue *glue = dev_get_drvdata(dev); diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c index 6123b748d262..310238c6b5cd 100644 --- a/drivers/usb/musb/blackfin.c +++ b/drivers/usb/musb/blackfin.c @@ -465,6 +465,7 @@ static int bfin_musb_exit(struct musb *musb) } static const struct musb_platform_ops bfin_ops = { + .quirks = MUSB_DMA_INVENTRA, .init = bfin_musb_init, .exit = bfin_musb_exit, @@ -477,6 +478,10 @@ static const struct musb_platform_ops bfin_ops = { .fifo_mode = 2, .read_fifo = bfin_read_fifo, .write_fifo = bfin_write_fifo, +#ifdef CONFIG_USB_INVENTRA_DMA + .dma_init = musbhs_dma_controller_create, + .dma_exit = musbhs_dma_controller_destroy, +#endif .enable = bfin_musb_enable, .disable = bfin_musb_disable, diff --git a/drivers/usb/musb/cppi_dma.c b/drivers/usb/musb/cppi_dma.c index 904fb85d85a6..cc134109b056 100644 --- a/drivers/usb/musb/cppi_dma.c +++ b/drivers/usb/musb/cppi_dma.c @@ -1297,7 +1297,8 @@ irqreturn_t cppi_interrupt(int irq, void *dev_id) EXPORT_SYMBOL_GPL(cppi_interrupt); /* Instantiate a software object representing a DMA controller. */ -struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *mregs) +struct dma_controller * +cppi_dma_controller_create(struct musb *musb, void __iomem *mregs) { struct cppi *controller; struct device *dev = musb->controller; @@ -1334,7 +1335,7 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *mr if (irq > 0) { if (request_irq(irq, cppi_interrupt, 0, "cppi-dma", musb)) { dev_err(dev, "request_irq %d failed!\n", irq); - dma_controller_destroy(&controller->controller); + musb_dma_controller_destroy(&controller->controller); return NULL; } controller->irq = irq; @@ -1343,11 +1344,12 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *mr cppi_controller_start(controller); return &controller->controller; } +EXPORT_SYMBOL_GPL(cppi_dma_controller_create); /* * Destroy a previously-instantiated DMA controller. */ -void dma_controller_destroy(struct dma_controller *c) +void cppi_dma_controller_destroy(struct dma_controller *c) { struct cppi *cppi; @@ -1363,6 +1365,7 @@ void dma_controller_destroy(struct dma_controller *c) kfree(cppi); } +EXPORT_SYMBOL_GPL(cppi_dma_controller_destroy); /* * Context: controller irqlocked, endpoint selected diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c index 9a9c82a4d35d..b03d3b867fca 100644 --- a/drivers/usb/musb/da8xx.c +++ b/drivers/usb/musb/da8xx.c @@ -458,11 +458,15 @@ static int da8xx_musb_exit(struct musb *musb) } static const struct musb_platform_ops da8xx_ops = { - .quirks = MUSB_INDEXED_EP, + .quirks = MUSB_DMA_CPPI | MUSB_INDEXED_EP, .init = da8xx_musb_init, .exit = da8xx_musb_exit, .fifo_mode = 2, +#ifdef CONFIG_USB_TI_CPPI_DMA + .dma_init = cppi_dma_controller_create, + .dma_exit = cppi_dma_controller_destroy, +#endif .enable = da8xx_musb_enable, .disable = da8xx_musb_disable, diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 3c1d9b211b51..cee61a51645e 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -284,7 +284,7 @@ static irqreturn_t davinci_musb_interrupt(int irq, void *__hci) * mask, state, "vector", and EOI registers. */ cppi = container_of(musb->dma_controller, struct cppi, controller); - if (is_cppi_enabled() && musb->dma_controller && !cppi->irq) + if (is_cppi_enabled(musb) && musb->dma_controller && !cppi->irq) retval = cppi_interrupt(irq, __hci); /* ack and handle non-CPPI interrupts */ @@ -491,9 +491,14 @@ static int davinci_musb_exit(struct musb *musb) } static const struct musb_platform_ops davinci_ops = { + .quirks = MUSB_DMA_CPPI, .init = davinci_musb_init, .exit = davinci_musb_exit, +#ifdef CONFIG_USB_TI_CPPI_DMA + .dma_init = cppi_dma_controller_create, + .dma_exit = cppi_dma_controller_destroy, +#endif .enable = davinci_musb_enable, .disable = davinci_musb_disable, diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c index bb7b26325a74..5e5a8fa005f8 100644 --- a/drivers/usb/musb/jz4740.c +++ b/drivers/usb/musb/jz4740.c @@ -105,8 +105,12 @@ static int jz4740_musb_exit(struct musb *musb) return 0; } +/* + * DMA has not been confirmed to work with CONFIG_USB_INVENTRA_DMA, + * so let's not set up the dma function pointers yet. + */ static const struct musb_platform_ops jz4740_musb_ops = { - .quirks = MUSB_INDEXED_EP, + .quirks = MUSB_DMA_INVENTRA | MUSB_INDEXED_EP, .fifo_mode = 2, .init = jz4740_musb_init, .exit = jz4740_musb_exit, diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 6dca3d794ced..514a6cdaeff6 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -251,6 +251,11 @@ static u32 musb_indexed_ep_offset(u8 epnum, u16 offset) return 0x10 + offset; } +static u32 musb_default_busctl_offset(u8 epnum, u16 offset) +{ + return 0x80 + (0x08 * epnum) + offset; +} + static u8 musb_default_readb(const void __iomem *addr, unsigned offset) { return __raw_readb(addr + offset); @@ -309,7 +314,7 @@ static void musb_default_write_fifo(struct musb_hw_ep *hw_ep, u16 len, index += len & ~0x03; } if (len & 0x02) { - musb_writew(fifo, 0, *(u16 *)&src[index]); + __raw_writew(*(u16 *)&src[index], fifo); index += 2; } } else { @@ -319,7 +324,7 @@ static void musb_default_write_fifo(struct musb_hw_ep *hw_ep, u16 len, } } if (len & 0x01) - musb_writeb(fifo, 0, src[index]); + __raw_writeb(src[index], fifo); } else { /* byte aligned */ iowrite8_rep(fifo, src, len); @@ -351,7 +356,7 @@ static void musb_default_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) index = len & ~0x03; } if (len & 0x02) { - *(u16 *)&dst[index] = musb_readw(fifo, 0); + *(u16 *)&dst[index] = __raw_readw(fifo); index += 2; } } else { @@ -361,7 +366,7 @@ static void musb_default_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) } } if (len & 0x01) - dst[index] = musb_readb(fifo, 0); + dst[index] = __raw_readb(fifo); } else { /* byte aligned */ ioread8_rep(fifo, dst, len); @@ -389,6 +394,15 @@ EXPORT_SYMBOL_GPL(musb_readl); void (*musb_writel)(void __iomem *addr, unsigned offset, u32 data); EXPORT_SYMBOL_GPL(musb_writel); +#ifndef CONFIG_MUSB_PIO_ONLY +struct dma_controller * +(*musb_dma_controller_create)(struct musb *musb, void __iomem *base); +EXPORT_SYMBOL(musb_dma_controller_create); + +void (*musb_dma_controller_destroy)(struct dma_controller *c); +EXPORT_SYMBOL(musb_dma_controller_destroy); +#endif + /* * New style IO functions */ @@ -1535,7 +1549,6 @@ static int musb_core_init(u16 musb_type, struct musb *musb) #endif hw_ep->regs = musb->io.ep_offset(i, 0) + mbase; - hw_ep->target_regs = musb_read_target_reg_base(i, mbase); hw_ep->rx_reinit = 1; hw_ep->tx_reinit = 1; @@ -1658,15 +1671,13 @@ void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit) /* called with controller lock already held */ if (!epnum) { -#ifndef CONFIG_USB_TUSB_OMAP_DMA - if (!is_cppi_enabled()) { + if (!is_cppi_enabled(musb)) { /* endpoint 0 */ if (is_host_active(musb)) musb_h_ep0_irq(musb); else musb_g_ep0_irq(musb); } -#endif } else { /* endpoints 1..15 */ if (transmit) { @@ -2029,6 +2040,11 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) musb->io.ep_offset = musb_flat_ep_offset; musb->io.ep_select = musb_flat_ep_select; } + /* And override them with platform specific ops if specified. */ + if (musb->ops->ep_offset) + musb->io.ep_offset = musb->ops->ep_offset; + if (musb->ops->ep_select) + musb->io.ep_select = musb->ops->ep_select; /* At least tusb6010 has its own offsets */ if (musb->ops->ep_offset) @@ -2046,6 +2062,11 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) else musb->io.fifo_offset = musb_default_fifo_offset; + if (musb->ops->busctl_offset) + musb->io.busctl_offset = musb->ops->busctl_offset; + else + musb->io.busctl_offset = musb_default_busctl_offset; + if (musb->ops->readb) musb_readb = musb->ops->readb; if (musb->ops->writeb) @@ -2059,6 +2080,15 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) if (musb->ops->writel) musb_writel = musb->ops->writel; +#ifndef CONFIG_MUSB_PIO_ONLY + if (!musb->ops->dma_init || !musb->ops->dma_exit) { + dev_err(dev, "DMA controller not set\n"); + goto fail2; + } + musb_dma_controller_create = musb->ops->dma_init; + musb_dma_controller_destroy = musb->ops->dma_exit; +#endif + if (musb->ops->read_fifo) musb->io.read_fifo = musb->ops->read_fifo; else @@ -2078,7 +2108,8 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl) pm_runtime_get_sync(musb->controller); if (use_dma && dev->dma_mask) { - musb->dma_controller = dma_controller_create(musb, musb->mregs); + musb->dma_controller = + musb_dma_controller_create(musb, musb->mregs); if (IS_ERR(musb->dma_controller)) { status = PTR_ERR(musb->dma_controller); goto fail2_5; @@ -2189,7 +2220,7 @@ fail3: cancel_delayed_work_sync(&musb->finish_resume_work); cancel_delayed_work_sync(&musb->deassert_reset_work); if (musb->dma_controller) - dma_controller_destroy(musb->dma_controller); + musb_dma_controller_destroy(musb->dma_controller); fail2_5: pm_runtime_put_sync(musb->controller); @@ -2248,7 +2279,7 @@ static int musb_remove(struct platform_device *pdev) musb_shutdown(pdev); if (musb->dma_controller) - dma_controller_destroy(musb->dma_controller); + musb_dma_controller_destroy(musb->dma_controller); cancel_work_sync(&musb->irq_work); cancel_delayed_work_sync(&musb->finish_resume_work); @@ -2316,18 +2347,18 @@ static void musb_save_context(struct musb *musb) musb_readb(epio, MUSB_RXINTERVAL); musb->context.index_regs[i].txfunaddr = - musb_read_txfunaddr(musb_base, i); + musb_read_txfunaddr(musb, i); musb->context.index_regs[i].txhubaddr = - musb_read_txhubaddr(musb_base, i); + musb_read_txhubaddr(musb, i); musb->context.index_regs[i].txhubport = - musb_read_txhubport(musb_base, i); + musb_read_txhubport(musb, i); musb->context.index_regs[i].rxfunaddr = - musb_read_rxfunaddr(musb_base, i); + musb_read_rxfunaddr(musb, i); musb->context.index_regs[i].rxhubaddr = - musb_read_rxhubaddr(musb_base, i); + musb_read_rxhubaddr(musb, i); musb->context.index_regs[i].rxhubport = - musb_read_rxhubport(musb_base, i); + musb_read_rxhubport(musb, i); } } @@ -2335,7 +2366,6 @@ static void musb_restore_context(struct musb *musb) { int i; void __iomem *musb_base = musb->mregs; - void __iomem *ep_target_regs; void __iomem *epio; u8 power; @@ -2396,21 +2426,18 @@ static void musb_restore_context(struct musb *musb) musb_writeb(epio, MUSB_RXINTERVAL, musb->context.index_regs[i].rxinterval); - musb_write_txfunaddr(musb_base, i, + musb_write_txfunaddr(musb, i, musb->context.index_regs[i].txfunaddr); - musb_write_txhubaddr(musb_base, i, + musb_write_txhubaddr(musb, i, musb->context.index_regs[i].txhubaddr); - musb_write_txhubport(musb_base, i, + musb_write_txhubport(musb, i, musb->context.index_regs[i].txhubport); - ep_target_regs = - musb_read_target_reg_base(i, musb_base); - - musb_write_rxfunaddr(ep_target_regs, + musb_write_rxfunaddr(musb, i, musb->context.index_regs[i].rxfunaddr); - musb_write_rxhubaddr(ep_target_regs, + musb_write_rxhubaddr(musb, i, musb->context.index_regs[i].rxhubaddr); - musb_write_rxhubport(ep_target_regs, + musb_write_rxhubport(musb, i, musb->context.index_regs[i].rxhubport); } musb_writeb(musb_base, MUSB_INDEX, musb->context.index); diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h index 3877249a8b2d..4b886d0f6bdf 100644 --- a/drivers/usb/musb/musb_core.h +++ b/drivers/usb/musb/musb_core.h @@ -67,7 +67,6 @@ struct musb_ep; #include "musb_dma.h" #include "musb_io.h" -#include "musb_regs.h" #include "musb_gadget.h" #include <linux/usb/hcd.h> @@ -157,6 +156,8 @@ struct musb_io; * @writel: write 32 bits * @read_fifo: reads the fifo * @write_fifo: writes to fifo + * @dma_init: platform specific dma init function + * @dma_exit: platform specific dma exit function * @init: turns on clocks, sets up platform-specific registers, etc * @exit: undoes @init * @set_mode: forcefully changes operating mode @@ -165,6 +166,8 @@ struct musb_io; * @vbus_status: returns vbus status if possible * @set_vbus: forces vbus status * @adjust_channel_params: pre check for standard dma channel_program func + * @pre_root_reset_end: called before the root usb port reset flag gets cleared + * @post_root_reset_end: called after the root usb port reset flag gets cleared */ struct musb_platform_ops { @@ -187,6 +190,7 @@ struct musb_platform_ops { void (*ep_select)(void __iomem *mbase, u8 epnum); u16 fifo_mode; u32 (*fifo_offset)(u8 epnum); + u32 (*busctl_offset)(u8 epnum, u16 offset); u8 (*readb)(const void __iomem *addr, unsigned offset); void (*writeb)(void __iomem *addr, unsigned offset, u8 data); u16 (*readw)(const void __iomem *addr, unsigned offset); @@ -195,6 +199,9 @@ struct musb_platform_ops { void (*writel)(void __iomem *addr, unsigned offset, u32 data); void (*read_fifo)(struct musb_hw_ep *hw_ep, u16 len, u8 *buf); void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf); + struct dma_controller * + (*dma_init) (struct musb *musb, void __iomem *base); + void (*dma_exit)(struct dma_controller *c); int (*set_mode)(struct musb *musb, u8 mode); void (*try_idle)(struct musb *musb, unsigned long timeout); int (*recover)(struct musb *musb); @@ -205,6 +212,8 @@ struct musb_platform_ops { int (*adjust_channel_params)(struct dma_channel *channel, u16 packet_sz, u8 *mode, dma_addr_t *dma_addr, u32 *len); + void (*pre_root_reset_end)(struct musb *musb); + void (*post_root_reset_end)(struct musb *musb); }; /* @@ -241,8 +250,6 @@ struct musb_hw_ep { void __iomem *fifo_sync_va; #endif - void __iomem *target_regs; - /* currently scheduled peripheral endpoint */ struct musb_qh *in_qh; struct musb_qh *out_qh; @@ -437,6 +444,9 @@ struct musb { #endif }; +/* This must be included after struct musb is defined */ +#include "musb_regs.h" + static inline struct musb *gadget_to_musb(struct usb_gadget *g) { return container_of(g, struct musb, g); @@ -590,4 +600,16 @@ static inline int musb_platform_exit(struct musb *musb) return musb->ops->exit(musb); } +static inline void musb_platform_pre_root_reset_end(struct musb *musb) +{ + if (musb->ops->pre_root_reset_end) + musb->ops->pre_root_reset_end(musb); +} + +static inline void musb_platform_post_root_reset_end(struct musb *musb) +{ + if (musb->ops->post_root_reset_end) + musb->ops->post_root_reset_end(musb); +} + #endif /* __MUSB_CORE_H__ */ diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index 8bd8c5e26921..4d1b44c232ee 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -678,7 +678,7 @@ err: return ret; } -void dma_controller_destroy(struct dma_controller *c) +void cppi41_dma_controller_destroy(struct dma_controller *c) { struct cppi41_dma_controller *controller = container_of(c, struct cppi41_dma_controller, controller); @@ -687,9 +687,10 @@ void dma_controller_destroy(struct dma_controller *c) cppi41_dma_controller_stop(controller); kfree(controller); } +EXPORT_SYMBOL_GPL(cppi41_dma_controller_destroy); -struct dma_controller *dma_controller_create(struct musb *musb, - void __iomem *base) +struct dma_controller * +cppi41_dma_controller_create(struct musb *musb, void __iomem *base) { struct cppi41_dma_controller *controller; int ret = 0; @@ -726,3 +727,4 @@ kzalloc_fail: return ERR_PTR(ret); return NULL; } +EXPORT_SYMBOL_GPL(cppi41_dma_controller_create); diff --git a/drivers/usb/musb/musb_debugfs.c b/drivers/usb/musb/musb_debugfs.c index 78a283e9ce40..9b22d946c089 100644 --- a/drivers/usb/musb/musb_debugfs.c +++ b/drivers/usb/musb/musb_debugfs.c @@ -191,9 +191,16 @@ static ssize_t musb_test_mode_write(struct file *file, { struct seq_file *s = file->private_data; struct musb *musb = s->private; - u8 test = 0; + u8 test; char buf[18]; + test = musb_readb(musb->mregs, MUSB_TESTMODE); + if (test) { + dev_err(musb->controller, "Error: test mode is already set. " + "Please do USB Bus Reset to start a new test.\n"); + return count; + } + memset(buf, 0x00, sizeof(buf)); if (copy_from_user(buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) @@ -238,6 +245,90 @@ static const struct file_operations musb_test_mode_fops = { .release = single_release, }; +static int musb_softconnect_show(struct seq_file *s, void *unused) +{ + struct musb *musb = s->private; + u8 reg; + int connect; + + switch (musb->xceiv->otg->state) { + case OTG_STATE_A_HOST: + case OTG_STATE_A_WAIT_BCON: + reg = musb_readb(musb->mregs, MUSB_DEVCTL); + connect = reg & MUSB_DEVCTL_SESSION ? 1 : 0; + break; + default: + connect = -1; + } + + seq_printf(s, "%d\n", connect); + + return 0; +} + +static int musb_softconnect_open(struct inode *inode, struct file *file) +{ + return single_open(file, musb_softconnect_show, inode->i_private); +} + +static ssize_t musb_softconnect_write(struct file *file, + const char __user *ubuf, size_t count, loff_t *ppos) +{ + struct seq_file *s = file->private_data; + struct musb *musb = s->private; + char buf[2]; + u8 reg; + + memset(buf, 0x00, sizeof(buf)); + + if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count))) + return -EFAULT; + + if (!strncmp(buf, "0", 1)) { + switch (musb->xceiv->otg->state) { + case OTG_STATE_A_HOST: + musb_root_disconnect(musb); + reg = musb_readb(musb->mregs, MUSB_DEVCTL); + reg &= ~MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, reg); + break; + default: + break; + } + } else if (!strncmp(buf, "1", 1)) { + switch (musb->xceiv->otg->state) { + case OTG_STATE_A_WAIT_BCON: + /* + * musb_save_context() called in musb_runtime_suspend() + * might cache devctl with SESSION bit cleared during + * soft-disconnect, so specifically set SESSION bit + * here to preserve it for musb_runtime_resume(). + */ + musb->context.devctl |= MUSB_DEVCTL_SESSION; + reg = musb_readb(musb->mregs, MUSB_DEVCTL); + reg |= MUSB_DEVCTL_SESSION; + musb_writeb(musb->mregs, MUSB_DEVCTL, reg); + break; + default: + break; + } + } + + return count; +} + +/* + * In host mode, connect/disconnect the bus without physically + * remove the devices. + */ +static const struct file_operations musb_softconnect_fops = { + .open = musb_softconnect_open, + .write = musb_softconnect_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + int musb_init_debugfs(struct musb *musb) { struct dentry *root; @@ -264,6 +355,13 @@ int musb_init_debugfs(struct musb *musb) goto err1; } + file = debugfs_create_file("softconnect", S_IRUGO | S_IWUSR, + root, musb, &musb_softconnect_fops); + if (!file) { + ret = -ENOMEM; + goto err1; + } + musb->debugfs_root = root; return 0; diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h index 1d44faa86252..46357e183b4c 100644 --- a/drivers/usb/musb/musb_dma.h +++ b/drivers/usb/musb/musb_dma.h @@ -68,16 +68,41 @@ struct musb_hw_ep; #define is_dma_capable() (1) #endif -#if defined(CONFIG_USB_TI_CPPI_DMA) || defined(CONFIG_USB_TI_CPPI41_DMA) -#define is_cppi_enabled() 1 +#ifdef CONFIG_USB_UX500_DMA +#define musb_dma_ux500(musb) (musb->io.quirks & MUSB_DMA_UX500) +#else +#define musb_dma_ux500(musb) 0 +#endif + +#ifdef CONFIG_USB_TI_CPPI41_DMA +#define musb_dma_cppi41(musb) (musb->io.quirks & MUSB_DMA_CPPI41) +#else +#define musb_dma_cppi41(musb) 0 +#endif + +#ifdef CONFIG_USB_TI_CPPI_DMA +#define musb_dma_cppi(musb) (musb->io.quirks & MUSB_DMA_CPPI) #else -#define is_cppi_enabled() 0 +#define musb_dma_cppi(musb) 0 #endif #ifdef CONFIG_USB_TUSB_OMAP_DMA -#define tusb_dma_omap() 1 +#define tusb_dma_omap(musb) (musb->io.quirks & MUSB_DMA_TUSB_OMAP) +#else +#define tusb_dma_omap(musb) 0 +#endif + +#ifdef CONFIG_USB_INVENTRA_DMA +#define musb_dma_inventra(musb) (musb->io.quirks & MUSB_DMA_INVENTRA) #else -#define tusb_dma_omap() 0 +#define musb_dma_inventra(musb) 0 +#endif + +#if defined(CONFIG_USB_TI_CPPI_DMA) || defined(CONFIG_USB_TI_CPPI41_DMA) +#define is_cppi_enabled(musb) \ + (musb_dma_cppi(musb) || musb_dma_cppi41(musb)) +#else +#define is_cppi_enabled(musb) 0 #endif /* Anomaly 05000456 - USB Receive Interrupt Is Not Generated in DMA Mode 1 @@ -177,19 +202,41 @@ struct dma_controller { extern void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit); #ifdef CONFIG_MUSB_PIO_ONLY -static inline struct dma_controller *dma_controller_create(struct musb *m, - void __iomem *io) +static inline struct dma_controller * +musb_dma_controller_create(struct musb *m, void __iomem *io) { return NULL; } -static inline void dma_controller_destroy(struct dma_controller *d) { } +static inline void musb_dma_controller_destroy(struct dma_controller *d) { } #else -extern struct dma_controller *dma_controller_create(struct musb *, void __iomem *); +extern struct dma_controller * +(*musb_dma_controller_create)(struct musb *, void __iomem *); -extern void dma_controller_destroy(struct dma_controller *); +extern void (*musb_dma_controller_destroy)(struct dma_controller *); #endif +/* Platform specific DMA functions */ +extern struct dma_controller * +musbhs_dma_controller_create(struct musb *musb, void __iomem *base); +extern void musbhs_dma_controller_destroy(struct dma_controller *c); + +extern struct dma_controller * +tusb_dma_controller_create(struct musb *musb, void __iomem *base); +extern void tusb_dma_controller_destroy(struct dma_controller *c); + +extern struct dma_controller * +cppi_dma_controller_create(struct musb *musb, void __iomem *base); +extern void cppi_dma_controller_destroy(struct dma_controller *c); + +extern struct dma_controller * +cppi41_dma_controller_create(struct musb *musb, void __iomem *base); +extern void cppi41_dma_controller_destroy(struct dma_controller *c); + +extern struct dma_controller * +ux500_dma_controller_create(struct musb *musb, void __iomem *base); +extern void ux500_dma_controller_destroy(struct dma_controller *c); + #endif /* __MUSB_DMA_H__ */ diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c index 65d931a28a14..1334a3de31b8 100644 --- a/drivers/usb/musb/musb_dsps.c +++ b/drivers/usb/musb/musb_dsps.c @@ -634,10 +634,14 @@ static void dsps_read_fifo32(struct musb_hw_ep *hw_ep, u16 len, u8 *dst) } static struct musb_platform_ops dsps_ops = { - .quirks = MUSB_INDEXED_EP, + .quirks = MUSB_DMA_CPPI41 | MUSB_INDEXED_EP, .init = dsps_musb_init, .exit = dsps_musb_exit, +#ifdef CONFIG_USB_TI_CPPI41_DMA + .dma_init = cppi41_dma_controller_create, + .dma_exit = cppi41_dma_controller_destroy, +#endif .enable = dsps_musb_enable, .disable = dsps_musb_disable, diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 4c481cd66c77..625d482f1a97 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -366,7 +366,7 @@ static void txstate(struct musb *musb, struct musb_request *req) } #endif - if (is_cppi_enabled()) { + if (is_cppi_enabled(musb)) { /* program endpoint CSR first, then setup DMA */ csr &= ~(MUSB_TXCSR_P_UNDERRUN | MUSB_TXCSR_TXPKTRDY); csr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_DMAMODE | @@ -402,7 +402,7 @@ static void txstate(struct musb *musb, struct musb_request *req) musb_writew(epio, MUSB_TXCSR, csr); /* invariant: prequest->buf is non-null */ } - } else if (tusb_dma_omap()) + } else if (tusb_dma_omap(musb)) use_dma = use_dma && c->channel_program( musb_ep->dma, musb_ep->packet_sz, request->zero, @@ -489,6 +489,7 @@ void musb_g_tx(struct musb *musb, u8 epnum) if (request) { u8 is_dma = 0; + bool short_packet = false; if (dma && (csr & MUSB_TXCSR_DMAENAB)) { is_dma = 1; @@ -507,15 +508,18 @@ void musb_g_tx(struct musb *musb, u8 epnum) * First, maybe a terminating short packet. Some DMA * engines might handle this by themselves. */ - if ((request->zero && request->length + if ((request->zero && request->length) && (request->length % musb_ep->packet_sz == 0) && (request->actual == request->length)) -#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) - || (is_dma && (!dma->desired_mode || + short_packet = true; + + if ((musb_dma_inventra(musb) || musb_dma_ux500(musb)) && + (is_dma && (!dma->desired_mode || (request->actual & - (musb_ep->packet_sz - 1)))) -#endif - ) { + (musb_ep->packet_sz - 1))))) + short_packet = true; + + if (short_packet) { /* * On DMA completion, FIFO may not be * available yet... @@ -595,7 +599,7 @@ static void rxstate(struct musb *musb, struct musb_request *req) return; } - if (is_cppi_enabled() && is_buffer_mapped(req)) { + if (is_cppi_enabled(musb) && is_buffer_mapped(req)) { struct dma_controller *c = musb->dma_controller; struct dma_channel *channel = musb_ep->dma; @@ -772,7 +776,7 @@ static void rxstate(struct musb *musb, struct musb_request *req) fifo_count = min_t(unsigned, len, fifo_count); #ifdef CONFIG_USB_TUSB_OMAP_DMA - if (tusb_dma_omap() && is_buffer_mapped(req)) { + if (tusb_dma_omap(musb) && is_buffer_mapped(req)) { struct dma_controller *c = musb->dma_controller; struct dma_channel *channel = musb_ep->dma; u32 dma_addr = request->dma + request->actual; diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index c3d5fc9dfb5b..26c65e66cc0f 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -181,7 +181,7 @@ static inline void musb_h_tx_dma_start(struct musb_hw_ep *ep) /* NOTE: no locks here; caller should lock and select EP */ txcsr = musb_readw(ep->regs, MUSB_TXCSR); txcsr |= MUSB_TXCSR_DMAENAB | MUSB_TXCSR_H_WZC_BITS; - if (is_cppi_enabled()) + if (is_cppi_enabled(ep->musb)) txcsr |= MUSB_TXCSR_DMAMODE; musb_writew(ep->regs, MUSB_TXCSR, txcsr); } @@ -294,7 +294,7 @@ start: if (!hw_ep->tx_channel) musb_h_tx_start(hw_ep); - else if (is_cppi_enabled() || tusb_dma_omap()) + else if (is_cppi_enabled(musb) || tusb_dma_omap(musb)) musb_h_tx_dma_start(hw_ep); } } @@ -555,8 +555,9 @@ musb_host_packet_rx(struct musb *musb, struct urb *urb, u8 epnum, u8 iso_err) * the busy/not-empty tests are basically paranoia. */ static void -musb_rx_reinit(struct musb *musb, struct musb_qh *qh, struct musb_hw_ep *ep) +musb_rx_reinit(struct musb *musb, struct musb_qh *qh, u8 epnum) { + struct musb_hw_ep *ep = musb->endpoints + epnum; u16 csr; /* NOTE: we know the "rx" fifo reinit never triggers for ep0. @@ -594,10 +595,9 @@ musb_rx_reinit(struct musb *musb, struct musb_qh *qh, struct musb_hw_ep *ep) /* target addr and (for multipoint) hub addr/port */ if (musb->is_multipoint) { - musb_write_rxfunaddr(ep->target_regs, qh->addr_reg); - musb_write_rxhubaddr(ep->target_regs, qh->h_addr_reg); - musb_write_rxhubport(ep->target_regs, qh->h_port_reg); - + musb_write_rxfunaddr(musb, epnum, qh->addr_reg); + musb_write_rxhubaddr(musb, epnum, qh->h_addr_reg); + musb_write_rxhubport(musb, epnum, qh->h_port_reg); } else musb_writeb(musb->mregs, MUSB_FADDR, qh->addr_reg); @@ -617,23 +617,22 @@ musb_rx_reinit(struct musb *musb, struct musb_qh *qh, struct musb_hw_ep *ep) ep->rx_reinit = 0; } -static bool musb_tx_dma_program(struct dma_controller *dma, +static int musb_tx_dma_set_mode_mentor(struct dma_controller *dma, struct musb_hw_ep *hw_ep, struct musb_qh *qh, - struct urb *urb, u32 offset, u32 length) + struct urb *urb, u32 offset, + u32 *length, u8 *mode) { struct dma_channel *channel = hw_ep->tx_channel; void __iomem *epio = hw_ep->regs; u16 pkt_size = qh->maxpacket; u16 csr; - u8 mode; -#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) - if (length > channel->max_len) - length = channel->max_len; + if (*length > channel->max_len) + *length = channel->max_len; csr = musb_readw(epio, MUSB_TXCSR); - if (length > pkt_size) { - mode = 1; + if (*length > pkt_size) { + *mode = 1; csr |= MUSB_TXCSR_DMAMODE | MUSB_TXCSR_DMAENAB; /* autoset shouldn't be set in high bandwidth */ /* @@ -649,15 +648,28 @@ static bool musb_tx_dma_program(struct dma_controller *dma, can_bulk_split(hw_ep->musb, qh->type))) csr |= MUSB_TXCSR_AUTOSET; } else { - mode = 0; + *mode = 0; csr &= ~(MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAMODE); csr |= MUSB_TXCSR_DMAENAB; /* against programmer's guide */ } channel->desired_mode = mode; musb_writew(epio, MUSB_TXCSR, csr); -#else - if (!is_cppi_enabled() && !tusb_dma_omap()) - return false; + + return 0; +} + +static int musb_tx_dma_set_mode_cppi_tusb(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, + struct musb_qh *qh, + struct urb *urb, + u32 offset, + u32 *length, + u8 *mode) +{ + struct dma_channel *channel = hw_ep->tx_channel; + + if (!is_cppi_enabled(hw_ep->musb) && !tusb_dma_omap(hw_ep->musb)) + return -ENODEV; channel->actual_len = 0; @@ -665,8 +677,28 @@ static bool musb_tx_dma_program(struct dma_controller *dma, * TX uses "RNDIS" mode automatically but needs help * to identify the zero-length-final-packet case. */ - mode = (urb->transfer_flags & URB_ZERO_PACKET) ? 1 : 0; -#endif + *mode = (urb->transfer_flags & URB_ZERO_PACKET) ? 1 : 0; + + return 0; +} + +static bool musb_tx_dma_program(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, struct musb_qh *qh, + struct urb *urb, u32 offset, u32 length) +{ + struct dma_channel *channel = hw_ep->tx_channel; + u16 pkt_size = qh->maxpacket; + u8 mode; + int res; + + if (musb_dma_inventra(hw_ep->musb) || musb_dma_ux500(hw_ep->musb)) + res = musb_tx_dma_set_mode_mentor(dma, hw_ep, qh, urb, + offset, &length, &mode); + else + res = musb_tx_dma_set_mode_cppi_tusb(dma, hw_ep, qh, urb, + offset, &length, &mode); + if (res) + return false; qh->segsize = length; @@ -678,6 +710,9 @@ static bool musb_tx_dma_program(struct dma_controller *dma, if (!dma->channel_program(channel, pkt_size, mode, urb->transfer_dma + offset, length)) { + void __iomem *epio = hw_ep->regs; + u16 csr; + dma->channel_release(channel); hw_ep->tx_channel = NULL; @@ -801,9 +836,9 @@ static void musb_ep_program(struct musb *musb, u8 epnum, /* target addr and (for multipoint) hub addr/port */ if (musb->is_multipoint) { - musb_write_txfunaddr(mbase, epnum, qh->addr_reg); - musb_write_txhubaddr(mbase, epnum, qh->h_addr_reg); - musb_write_txhubport(mbase, epnum, qh->h_port_reg); + musb_write_txfunaddr(musb, epnum, qh->addr_reg); + musb_write_txhubaddr(musb, epnum, qh->h_addr_reg); + musb_write_txhubport(musb, epnum, qh->h_port_reg); /* FIXME if !epnum, do the same for RX ... */ } else musb_writeb(mbase, MUSB_FADDR, qh->addr_reg); @@ -875,7 +910,7 @@ finish: u16 csr; if (hw_ep->rx_reinit) { - musb_rx_reinit(musb, qh, hw_ep); + musb_rx_reinit(musb, qh, epnum); /* init new state: toggle and NYET, maybe DMA later */ if (usb_gettoggle(urb->dev, qh->epnum, 0)) @@ -901,7 +936,7 @@ finish: /* kick things off */ - if ((is_cppi_enabled() || tusb_dma_omap()) && dma_channel) { + if ((is_cppi_enabled(musb) || tusb_dma_omap(musb)) && dma_channel) { /* Candidate for DMA */ dma_channel->actual_len = 0L; qh->segsize = len; @@ -1441,7 +1476,7 @@ done: } else if ((usb_pipeisoc(pipe) || transfer_pending) && dma) { if (musb_tx_dma_program(musb->dma_controller, hw_ep, qh, urb, offset, length)) { - if (is_cppi_enabled() || tusb_dma_omap()) + if (is_cppi_enabled(musb) || tusb_dma_omap(musb)) musb_h_tx_dma_start(hw_ep); return; } @@ -1498,9 +1533,47 @@ done: MUSB_TXCSR_H_WZC_BITS | MUSB_TXCSR_TXPKTRDY); } +#ifdef CONFIG_USB_TI_CPPI41_DMA +/* Seems to set up ISO for cppi41 and not advance len. See commit c57c41d */ +static int musb_rx_dma_iso_cppi41(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, + struct musb_qh *qh, + struct urb *urb, + size_t len) +{ + struct dma_channel *channel = hw_ep->tx_channel; + void __iomem *epio = hw_ep->regs; + dma_addr_t *buf; + u32 length, res; + u16 val; -#ifdef CONFIG_USB_INVENTRA_DMA + buf = (void *)urb->iso_frame_desc[qh->iso_idx].offset + + (u32)urb->transfer_dma; + + length = urb->iso_frame_desc[qh->iso_idx].length; + val = musb_readw(epio, MUSB_RXCSR); + val |= MUSB_RXCSR_DMAENAB; + musb_writew(hw_ep->regs, MUSB_RXCSR, val); + + res = dma->channel_program(channel, qh->maxpacket, 0, + (u32)buf, length); + + return res; +} +#else +static inline int musb_rx_dma_iso_cppi41(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, + struct musb_qh *qh, + struct urb *urb, + size_t len) +{ + return false; +} +#endif + +#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) || \ + defined(CONFIG_USB_TI_CPPI41_DMA) /* Host side RX (IN) using Mentor DMA works as follows: submit_urb -> - if queue was empty, ProgramEndpoint @@ -1535,7 +1608,194 @@ done: * thus be a great candidate for using mode 1 ... for all but the * last packet of one URB's transfer. */ +static int musb_rx_dma_inventra_cppi41(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, + struct musb_qh *qh, + struct urb *urb, + size_t len) +{ + struct dma_channel *channel = hw_ep->rx_channel; + void __iomem *epio = hw_ep->regs; + u16 val; + int pipe; + bool done; + + pipe = urb->pipe; + + if (usb_pipeisoc(pipe)) { + struct usb_iso_packet_descriptor *d; + + d = urb->iso_frame_desc + qh->iso_idx; + d->actual_length = len; + + /* even if there was an error, we did the dma + * for iso_frame_desc->length + */ + if (d->status != -EILSEQ && d->status != -EOVERFLOW) + d->status = 0; + + if (++qh->iso_idx >= urb->number_of_packets) { + done = true; + } else { + /* REVISIT: Why ignore return value here? */ + if (musb_dma_cppi41(hw_ep->musb)) + done = musb_rx_dma_iso_cppi41(dma, hw_ep, qh, + urb, len); + done = false; + } + + } else { + /* done if urb buffer is full or short packet is recd */ + done = (urb->actual_length + len >= + urb->transfer_buffer_length + || channel->actual_len < qh->maxpacket + || channel->rx_packet_done); + } + + /* send IN token for next packet, without AUTOREQ */ + if (!done) { + val = musb_readw(epio, MUSB_RXCSR); + val |= MUSB_RXCSR_H_REQPKT; + musb_writew(epio, MUSB_RXCSR, MUSB_RXCSR_H_WZC_BITS | val); + } + + return done; +} + +/* Disadvantage of using mode 1: + * It's basically usable only for mass storage class; essentially all + * other protocols also terminate transfers on short packets. + * + * Details: + * An extra IN token is sent at the end of the transfer (due to AUTOREQ) + * If you try to use mode 1 for (transfer_buffer_length - 512), and try + * to use the extra IN token to grab the last packet using mode 0, then + * the problem is that you cannot be sure when the device will send the + * last packet and RxPktRdy set. Sometimes the packet is recd too soon + * such that it gets lost when RxCSR is re-set at the end of the mode 1 + * transfer, while sometimes it is recd just a little late so that if you + * try to configure for mode 0 soon after the mode 1 transfer is + * completed, you will find rxcount 0. Okay, so you might think why not + * wait for an interrupt when the pkt is recd. Well, you won't get any! + */ +static int musb_rx_dma_in_inventra_cppi41(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, + struct musb_qh *qh, + struct urb *urb, + size_t len, + u8 iso_err) +{ + struct musb *musb = hw_ep->musb; + void __iomem *epio = hw_ep->regs; + struct dma_channel *channel = hw_ep->rx_channel; + u16 rx_count, val; + int length, pipe, done; + dma_addr_t buf; + + rx_count = musb_readw(epio, MUSB_RXCOUNT); + pipe = urb->pipe; + + if (usb_pipeisoc(pipe)) { + int d_status = 0; + struct usb_iso_packet_descriptor *d; + + d = urb->iso_frame_desc + qh->iso_idx; + if (iso_err) { + d_status = -EILSEQ; + urb->error_count++; + } + if (rx_count > d->length) { + if (d_status == 0) { + d_status = -EOVERFLOW; + urb->error_count++; + } + dev_dbg(musb->controller, "** OVERFLOW %d into %d\n", + rx_count, d->length); + + length = d->length; + } else + length = rx_count; + d->status = d_status; + buf = urb->transfer_dma + d->offset; + } else { + length = rx_count; + buf = urb->transfer_dma + urb->actual_length; + } + + channel->desired_mode = 0; +#ifdef USE_MODE1 + /* because of the issue below, mode 1 will + * only rarely behave with correct semantics. + */ + if ((urb->transfer_flags & URB_SHORT_NOT_OK) + && (urb->transfer_buffer_length - urb->actual_length) + > qh->maxpacket) + channel->desired_mode = 1; + if (rx_count < hw_ep->max_packet_sz_rx) { + length = rx_count; + channel->desired_mode = 0; + } else { + length = urb->transfer_buffer_length; + } +#endif + + /* See comments above on disadvantages of using mode 1 */ + val = musb_readw(epio, MUSB_RXCSR); + val &= ~MUSB_RXCSR_H_REQPKT; + + if (channel->desired_mode == 0) + val &= ~MUSB_RXCSR_H_AUTOREQ; + else + val |= MUSB_RXCSR_H_AUTOREQ; + val |= MUSB_RXCSR_DMAENAB; + + /* autoclear shouldn't be set in high bandwidth */ + if (qh->hb_mult == 1) + val |= MUSB_RXCSR_AUTOCLEAR; + + musb_writew(epio, MUSB_RXCSR, MUSB_RXCSR_H_WZC_BITS | val); + + /* REVISIT if when actual_length != 0, + * transfer_buffer_length needs to be + * adjusted first... + */ + done = dma->channel_program(channel, qh->maxpacket, + channel->desired_mode, + buf, length); + + if (!done) { + dma->channel_release(channel); + hw_ep->rx_channel = NULL; + channel = NULL; + val = musb_readw(epio, MUSB_RXCSR); + val &= ~(MUSB_RXCSR_DMAENAB + | MUSB_RXCSR_H_AUTOREQ + | MUSB_RXCSR_AUTOCLEAR); + musb_writew(epio, MUSB_RXCSR, val); + } + + return done; +} +#else +static inline int musb_rx_dma_inventra_cppi41(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, + struct musb_qh *qh, + struct urb *urb, + size_t len) +{ + return false; +} + +static inline int musb_rx_dma_in_inventra_cppi41(struct dma_controller *dma, + struct musb_hw_ep *hw_ep, + struct musb_qh *qh, + struct urb *urb, + size_t len, + u8 iso_err) +{ + return false; +} #endif /* @@ -1546,6 +1806,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) { struct urb *urb; struct musb_hw_ep *hw_ep = musb->endpoints + epnum; + struct dma_controller *c = musb->dma_controller; void __iomem *epio = hw_ep->regs; struct musb_qh *qh = hw_ep->in_qh; size_t xfer_len; @@ -1661,9 +1922,8 @@ void musb_host_rx(struct musb *musb, u8 epnum) */ /* FIXME this is _way_ too much in-line logic for Mentor DMA */ - -#if !defined(CONFIG_USB_INVENTRA_DMA) && !defined(CONFIG_USB_UX500_DMA) - if (rx_csr & MUSB_RXCSR_H_REQPKT) { + if (!musb_dma_inventra(musb) && !musb_dma_ux500(musb) && + (rx_csr & MUSB_RXCSR_H_REQPKT)) { /* REVISIT this happened for a while on some short reads... * the cleanup still needs investigation... looks bad... * and also duplicates dma cleanup code above ... plus, @@ -1684,7 +1944,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) musb_writew(epio, MUSB_RXCSR, MUSB_RXCSR_H_WZC_BITS | rx_csr); } -#endif + if (dma && (rx_csr & MUSB_RXCSR_DMAENAB)) { xfer_len = dma->actual_len; @@ -1694,67 +1954,18 @@ void musb_host_rx(struct musb *musb, u8 epnum) | MUSB_RXCSR_RXPKTRDY); musb_writew(hw_ep->regs, MUSB_RXCSR, val); -#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) || \ - defined(CONFIG_USB_TI_CPPI41_DMA) - if (usb_pipeisoc(pipe)) { - struct usb_iso_packet_descriptor *d; - - d = urb->iso_frame_desc + qh->iso_idx; - d->actual_length = xfer_len; - - /* even if there was an error, we did the dma - * for iso_frame_desc->length - */ - if (d->status != -EILSEQ && d->status != -EOVERFLOW) - d->status = 0; - - if (++qh->iso_idx >= urb->number_of_packets) { - done = true; - } else { -#if defined(CONFIG_USB_TI_CPPI41_DMA) - struct dma_controller *c; - dma_addr_t *buf; - u32 length, ret; - - c = musb->dma_controller; - buf = (void *) - urb->iso_frame_desc[qh->iso_idx].offset - + (u32)urb->transfer_dma; - - length = - urb->iso_frame_desc[qh->iso_idx].length; - - val |= MUSB_RXCSR_DMAENAB; - musb_writew(hw_ep->regs, MUSB_RXCSR, val); - - ret = c->channel_program(dma, qh->maxpacket, - 0, (u32) buf, length); -#endif - done = false; - } - - } else { - /* done if urb buffer is full or short packet is recd */ - done = (urb->actual_length + xfer_len >= - urb->transfer_buffer_length - || dma->actual_len < qh->maxpacket - || dma->rx_packet_done); - } - - /* send IN token for next packet, without AUTOREQ */ - if (!done) { - val |= MUSB_RXCSR_H_REQPKT; - musb_writew(epio, MUSB_RXCSR, - MUSB_RXCSR_H_WZC_BITS | val); + if (musb_dma_inventra(musb) || musb_dma_ux500(musb) || + musb_dma_cppi41(musb)) { + done = musb_rx_dma_inventra_cppi41(c, hw_ep, qh, urb, xfer_len); + dev_dbg(hw_ep->musb->controller, + "ep %d dma %s, rxcsr %04x, rxcount %d\n", + epnum, done ? "off" : "reset", + musb_readw(epio, MUSB_RXCSR), + musb_readw(epio, MUSB_RXCOUNT)); + } else { + done = true; } - dev_dbg(musb->controller, "ep %d dma %s, rxcsr %04x, rxcount %d\n", epnum, - done ? "off" : "reset", - musb_readw(epio, MUSB_RXCSR), - musb_readw(epio, MUSB_RXCOUNT)); -#else - done = true; -#endif } else if (urb->status == -EINPROGRESS) { /* if no errors, be sure a packet is ready for unloading */ if (unlikely(!(rx_csr & MUSB_RXCSR_RXPKTRDY))) { @@ -1772,126 +1983,24 @@ void musb_host_rx(struct musb *musb, u8 epnum) } /* we are expecting IN packets */ -#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) || \ - defined(CONFIG_USB_TI_CPPI41_DMA) - if (dma) { - struct dma_controller *c; - u16 rx_count; - int ret, length; - dma_addr_t buf; - - rx_count = musb_readw(epio, MUSB_RXCOUNT); - - dev_dbg(musb->controller, "RX%d count %d, buffer 0x%llx len %d/%d\n", - epnum, rx_count, - (unsigned long long) urb->transfer_dma - + urb->actual_length, - qh->offset, - urb->transfer_buffer_length); - - c = musb->dma_controller; - - if (usb_pipeisoc(pipe)) { - int d_status = 0; - struct usb_iso_packet_descriptor *d; - - d = urb->iso_frame_desc + qh->iso_idx; - - if (iso_err) { - d_status = -EILSEQ; - urb->error_count++; - } - if (rx_count > d->length) { - if (d_status == 0) { - d_status = -EOVERFLOW; - urb->error_count++; - } - dev_dbg(musb->controller, "** OVERFLOW %d into %d\n",\ - rx_count, d->length); - - length = d->length; - } else - length = rx_count; - d->status = d_status; - buf = urb->transfer_dma + d->offset; - } else { - length = rx_count; - buf = urb->transfer_dma + - urb->actual_length; - } - - dma->desired_mode = 0; -#ifdef USE_MODE1 - /* because of the issue below, mode 1 will - * only rarely behave with correct semantics. - */ - if ((urb->transfer_flags & - URB_SHORT_NOT_OK) - && (urb->transfer_buffer_length - - urb->actual_length) - > qh->maxpacket) - dma->desired_mode = 1; - if (rx_count < hw_ep->max_packet_sz_rx) { - length = rx_count; - dma->desired_mode = 0; - } else { - length = urb->transfer_buffer_length; - } -#endif - -/* Disadvantage of using mode 1: - * It's basically usable only for mass storage class; essentially all - * other protocols also terminate transfers on short packets. - * - * Details: - * An extra IN token is sent at the end of the transfer (due to AUTOREQ) - * If you try to use mode 1 for (transfer_buffer_length - 512), and try - * to use the extra IN token to grab the last packet using mode 0, then - * the problem is that you cannot be sure when the device will send the - * last packet and RxPktRdy set. Sometimes the packet is recd too soon - * such that it gets lost when RxCSR is re-set at the end of the mode 1 - * transfer, while sometimes it is recd just a little late so that if you - * try to configure for mode 0 soon after the mode 1 transfer is - * completed, you will find rxcount 0. Okay, so you might think why not - * wait for an interrupt when the pkt is recd. Well, you won't get any! - */ - - val = musb_readw(epio, MUSB_RXCSR); - val &= ~MUSB_RXCSR_H_REQPKT; - - if (dma->desired_mode == 0) - val &= ~MUSB_RXCSR_H_AUTOREQ; + if ((musb_dma_inventra(musb) || musb_dma_ux500(musb) || + musb_dma_cppi41(musb)) && dma) { + dev_dbg(hw_ep->musb->controller, + "RX%d count %d, buffer 0x%llx len %d/%d\n", + epnum, musb_readw(epio, MUSB_RXCOUNT), + (unsigned long long) urb->transfer_dma + + urb->actual_length, + qh->offset, + urb->transfer_buffer_length); + + done = musb_rx_dma_in_inventra_cppi41(c, hw_ep, qh, + urb, xfer_len, + iso_err); + if (done) + goto finish; else - val |= MUSB_RXCSR_H_AUTOREQ; - val |= MUSB_RXCSR_DMAENAB; - - /* autoclear shouldn't be set in high bandwidth */ - if (qh->hb_mult == 1) - val |= MUSB_RXCSR_AUTOCLEAR; - - musb_writew(epio, MUSB_RXCSR, - MUSB_RXCSR_H_WZC_BITS | val); - - /* REVISIT if when actual_length != 0, - * transfer_buffer_length needs to be - * adjusted first... - */ - ret = c->channel_program( - dma, qh->maxpacket, - dma->desired_mode, buf, length); - - if (!ret) { - c->channel_release(dma); - hw_ep->rx_channel = NULL; - dma = NULL; - val = musb_readw(epio, MUSB_RXCSR); - val &= ~(MUSB_RXCSR_DMAENAB - | MUSB_RXCSR_H_AUTOREQ - | MUSB_RXCSR_AUTOCLEAR); - musb_writew(epio, MUSB_RXCSR, val); - } + dev_err(musb->controller, "error: rx_dma failed\n"); } -#endif /* Mentor DMA */ if (!dma) { unsigned int received_len; @@ -2512,6 +2621,7 @@ static void musb_free_temp_buffer(struct urb *urb) { enum dma_data_direction dir; struct musb_temp_buffer *temp; + size_t length; if (!(urb->transfer_flags & URB_ALIGNED_TEMP_BUFFER)) return; @@ -2522,8 +2632,12 @@ static void musb_free_temp_buffer(struct urb *urb) data); if (dir == DMA_FROM_DEVICE) { - memcpy(temp->old_xfer_buffer, temp->data, - urb->transfer_buffer_length); + if (usb_pipeisoc(urb->pipe)) + length = urb->transfer_buffer_length; + else + length = urb->actual_length; + + memcpy(temp->old_xfer_buffer, temp->data, length); } urb->transfer_buffer = temp->old_xfer_buffer; kfree(temp->kmalloc_ptr); diff --git a/drivers/usb/musb/musb_io.h b/drivers/usb/musb/musb_io.h index 8a57a6f4b3a6..17a80ae20674 100644 --- a/drivers/usb/musb/musb_io.h +++ b/drivers/usb/musb/musb_io.h @@ -47,6 +47,7 @@ * @fifo_offset: platform specific function to get fifo offset * @read_fifo: platform specific function to read fifo * @write_fifo: platform specific function to write fifo + * @busctl_offset: platform specific function to get busctl offset */ struct musb_io { u32 quirks; @@ -55,6 +56,7 @@ struct musb_io { u32 (*fifo_offset)(u8 epnum); void (*read_fifo)(struct musb_hw_ep *hw_ep, u16 len, u8 *buf); void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf); + u32 (*busctl_offset)(u8 epnum, u16 offset); }; /* Do not add new entries here, add them the struct musb_io instead */ diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h index 11f0be07491e..cff5bcf0d00f 100644 --- a/drivers/usb/musb/musb_regs.h +++ b/drivers/usb/musb/musb_regs.h @@ -300,9 +300,6 @@ #define MUSB_RXHUBADDR 0x06 #define MUSB_RXHUBPORT 0x07 -#define MUSB_BUSCTL_OFFSET(_epnum, _offset) \ - (0x80 + (8*(_epnum)) + (_offset)) - static inline void musb_write_txfifosz(void __iomem *mbase, u8 c_size) { musb_writeb(mbase, MUSB_TXFIFOSZ, c_size); @@ -364,78 +361,84 @@ static inline u16 musb_read_hwvers(void __iomem *mbase) return musb_readw(mbase, MUSB_HWVERS); } -static inline void __iomem *musb_read_target_reg_base(u8 i, void __iomem *mbase) -{ - return (MUSB_BUSCTL_OFFSET(i, 0) + mbase); -} - -static inline void musb_write_rxfunaddr(void __iomem *ep_target_regs, +static inline void musb_write_rxfunaddr(struct musb *musb, u8 epnum, u8 qh_addr_reg) { - musb_writeb(ep_target_regs, MUSB_RXFUNCADDR, qh_addr_reg); + musb_writeb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_RXFUNCADDR), + qh_addr_reg); } -static inline void musb_write_rxhubaddr(void __iomem *ep_target_regs, +static inline void musb_write_rxhubaddr(struct musb *musb, u8 epnum, u8 qh_h_addr_reg) { - musb_writeb(ep_target_regs, MUSB_RXHUBADDR, qh_h_addr_reg); + musb_writeb(musb->mregs, musb->io.busctl_offset(epnum, MUSB_RXHUBADDR), + qh_h_addr_reg); } -static inline void musb_write_rxhubport(void __iomem *ep_target_regs, +static inline void musb_write_rxhubport(struct musb *musb, u8 epnum, u8 qh_h_port_reg) { - musb_writeb(ep_target_regs, MUSB_RXHUBPORT, qh_h_port_reg); + musb_writeb(musb->mregs, musb->io.busctl_offset(epnum, MUSB_RXHUBPORT), + qh_h_port_reg); } -static inline void musb_write_txfunaddr(void __iomem *mbase, u8 epnum, +static inline void musb_write_txfunaddr(struct musb *musb, u8 epnum, u8 qh_addr_reg) { - musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXFUNCADDR), - qh_addr_reg); + musb_writeb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_TXFUNCADDR), + qh_addr_reg); } -static inline void musb_write_txhubaddr(void __iomem *mbase, u8 epnum, +static inline void musb_write_txhubaddr(struct musb *musb, u8 epnum, u8 qh_addr_reg) { - musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBADDR), + musb_writeb(musb->mregs, musb->io.busctl_offset(epnum, MUSB_TXHUBADDR), qh_addr_reg); } -static inline void musb_write_txhubport(void __iomem *mbase, u8 epnum, +static inline void musb_write_txhubport(struct musb *musb, u8 epnum, u8 qh_h_port_reg) { - musb_writeb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBPORT), + musb_writeb(musb->mregs, musb->io.busctl_offset(epnum, MUSB_TXHUBPORT), qh_h_port_reg); } -static inline u8 musb_read_rxfunaddr(void __iomem *mbase, u8 epnum) +static inline u8 musb_read_rxfunaddr(struct musb *musb, u8 epnum) { - return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXFUNCADDR)); + return musb_readb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_RXFUNCADDR)); } -static inline u8 musb_read_rxhubaddr(void __iomem *mbase, u8 epnum) +static inline u8 musb_read_rxhubaddr(struct musb *musb, u8 epnum) { - return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXHUBADDR)); + return musb_readb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_RXHUBADDR)); } -static inline u8 musb_read_rxhubport(void __iomem *mbase, u8 epnum) +static inline u8 musb_read_rxhubport(struct musb *musb, u8 epnum) { - return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_RXHUBPORT)); + return musb_readb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_RXHUBPORT)); } -static inline u8 musb_read_txfunaddr(void __iomem *mbase, u8 epnum) +static inline u8 musb_read_txfunaddr(struct musb *musb, u8 epnum) { - return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXFUNCADDR)); + return musb_readb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_TXFUNCADDR)); } -static inline u8 musb_read_txhubaddr(void __iomem *mbase, u8 epnum) +static inline u8 musb_read_txhubaddr(struct musb *musb, u8 epnum) { - return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBADDR)); + return musb_readb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_TXHUBADDR)); } -static inline u8 musb_read_txhubport(void __iomem *mbase, u8 epnum) +static inline u8 musb_read_txhubport(struct musb *musb, u8 epnum) { - return musb_readb(mbase, MUSB_BUSCTL_OFFSET(epnum, MUSB_TXHUBPORT)); + return musb_readb(musb->mregs, + musb->io.busctl_offset(epnum, MUSB_TXHUBPORT)); } #else /* CONFIG_BLACKFIN */ @@ -556,22 +559,17 @@ static inline u16 musb_read_hwvers(void __iomem *mbase) return MUSB_HWVERS_1900; } -static inline void __iomem *musb_read_target_reg_base(u8 i, void __iomem *mbase) -{ - return NULL; -} - -static inline void musb_write_rxfunaddr(void __iomem *ep_target_regs, +static inline void musb_write_rxfunaddr(void __iomem *mbase, u8 epnum, u8 qh_addr_req) { } -static inline void musb_write_rxhubaddr(void __iomem *ep_target_regs, +static inline void musb_write_rxhubaddr(void __iomem *mbase, u8 epnum, u8 qh_h_addr_reg) { } -static inline void musb_write_rxhubport(void __iomem *ep_target_regs, +static inline void musb_write_rxhubport(void __iomem *mbase, u8 epnum, u8 qh_h_port_reg) { } diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c index 86c4b533e90b..30842bc195f5 100644 --- a/drivers/usb/musb/musb_virthub.c +++ b/drivers/usb/musb/musb_virthub.c @@ -195,8 +195,10 @@ void musb_port_reset(struct musb *musb, bool do_reset) msecs_to_jiffies(50)); } else { dev_dbg(musb->controller, "root port reset stopped\n"); + musb_platform_pre_root_reset_end(musb); musb_writeb(mbase, MUSB_POWER, power & ~MUSB_POWER_RESET); + musb_platform_post_root_reset_end(musb); power = musb_readb(mbase, MUSB_POWER); if (power & MUSB_POWER_HSMODE) { diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c index ab7ec09a8afe..7539c3188ffc 100644 --- a/drivers/usb/musb/musbhsdma.c +++ b/drivers/usb/musb/musbhsdma.c @@ -357,7 +357,7 @@ done: return retval; } -void dma_controller_destroy(struct dma_controller *c) +void musbhs_dma_controller_destroy(struct dma_controller *c) { struct musb_dma_controller *controller = container_of(c, struct musb_dma_controller, controller); @@ -369,8 +369,10 @@ void dma_controller_destroy(struct dma_controller *c) kfree(controller); } +EXPORT_SYMBOL_GPL(musbhs_dma_controller_destroy); -struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *base) +struct dma_controller *musbhs_dma_controller_create(struct musb *musb, + void __iomem *base) { struct musb_dma_controller *controller; struct device *dev = musb->controller; @@ -398,7 +400,7 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *ba if (request_irq(irq, dma_controller_irq, 0, dev_name(musb->controller), &controller->controller)) { dev_err(dev, "request_irq %d failed!\n", irq); - dma_controller_destroy(&controller->controller); + musb_dma_controller_destroy(&controller->controller); return NULL; } @@ -407,3 +409,4 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *ba return &controller->controller; } +EXPORT_SYMBOL_GPL(musbhs_dma_controller_create); diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index cc752d8c7773..70f2b8a2e6cf 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -493,6 +493,11 @@ static int omap2430_musb_exit(struct musb *musb) } static const struct musb_platform_ops omap2430_ops = { + .quirks = MUSB_DMA_INVENTRA, +#ifdef CONFIG_USB_INVENTRA_DMA + .dma_init = musbhs_dma_controller_create, + .dma_exit = musbhs_dma_controller_destroy, +#endif .init = omap2430_musb_init, .exit = omap2430_musb_exit, diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 3a5ffd575438..df7c9f46be54 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -890,7 +890,7 @@ static irqreturn_t tusb_musb_interrupt(int irq, void *__hci) dev_dbg(musb->controller, "DMA IRQ %08x\n", dma_src); real_dma_src = ~real_dma_src & dma_src; - if (tusb_dma_omap() && real_dma_src) { + if (tusb_dma_omap(musb) && real_dma_src) { int tx_source = (real_dma_src & 0xffff); int i; @@ -1181,7 +1181,7 @@ static int tusb_musb_exit(struct musb *musb) } static const struct musb_platform_ops tusb_ops = { - .quirks = MUSB_IN_TUSB, + .quirks = MUSB_DMA_TUSB_OMAP | MUSB_IN_TUSB, .init = tusb_musb_init, .exit = tusb_musb_exit, @@ -1192,6 +1192,10 @@ static const struct musb_platform_ops tusb_ops = { .writeb = tusb_writeb, .read_fifo = tusb_read_fifo, .write_fifo = tusb_write_fifo, +#ifdef CONFIG_USB_TUSB_OMAP_DMA + .dma_init = tusb_dma_controller_create, + .dma_exit = tusb_dma_controller_destroy, +#endif .enable = tusb_musb_enable, .disable = tusb_musb_disable, diff --git a/drivers/usb/musb/tusb6010.h b/drivers/usb/musb/tusb6010.h index aec86c86ce32..72cdad23ced9 100644 --- a/drivers/usb/musb/tusb6010.h +++ b/drivers/usb/musb/tusb6010.h @@ -12,12 +12,6 @@ #ifndef __TUSB6010_H__ #define __TUSB6010_H__ -#ifdef CONFIG_USB_TUSB_OMAP_DMA -#define tusb_dma_omap() 1 -#else -#define tusb_dma_omap() 0 -#endif - /* VLYNQ control register. 32-bit at offset 0x000 */ #define TUSB_VLYNQ_CTRL 0x004 diff --git a/drivers/usb/musb/tusb6010_omap.c b/drivers/usb/musb/tusb6010_omap.c index 3ce152c0408e..4c82077da475 100644 --- a/drivers/usb/musb/tusb6010_omap.c +++ b/drivers/usb/musb/tusb6010_omap.c @@ -625,7 +625,7 @@ static void tusb_omap_dma_release(struct dma_channel *channel) channel = NULL; } -void dma_controller_destroy(struct dma_controller *c) +void tusb_dma_controller_destroy(struct dma_controller *c) { struct tusb_omap_dma *tusb_dma; int i; @@ -644,8 +644,10 @@ void dma_controller_destroy(struct dma_controller *c) kfree(tusb_dma); } +EXPORT_SYMBOL_GPL(tusb_dma_controller_destroy); -struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *base) +struct dma_controller * +tusb_dma_controller_create(struct musb *musb, void __iomem *base) { void __iomem *tbase = musb->ctrl_base; struct tusb_omap_dma *tusb_dma; @@ -701,7 +703,8 @@ struct dma_controller *dma_controller_create(struct musb *musb, void __iomem *ba return &tusb_dma->controller; cleanup: - dma_controller_destroy(&tusb_dma->controller); + musb_dma_controller_destroy(&tusb_dma->controller); out: return NULL; } +EXPORT_SYMBOL_GPL(tusb_dma_controller_create); diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c index abf72728825f..39168fe9b406 100644 --- a/drivers/usb/musb/ux500.c +++ b/drivers/usb/musb/ux500.c @@ -188,7 +188,11 @@ static int ux500_musb_exit(struct musb *musb) } static const struct musb_platform_ops ux500_ops = { - .quirks = MUSB_INDEXED_EP, + .quirks = MUSB_DMA_UX500 | MUSB_INDEXED_EP, +#ifdef CONFIG_USB_UX500_DMA + .dma_init = ux500_dma_controller_create, + .dma_exit = ux500_dma_controller_destroy, +#endif .init = ux500_musb_init, .exit = ux500_musb_exit, .fifo_mode = 5, @@ -338,7 +342,7 @@ static int ux500_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int ux500_suspend(struct device *dev) { struct ux500_glue *glue = dev_get_drvdata(dev); diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c index e93845c26bdb..d0b6a1cd7f62 100644 --- a/drivers/usb/musb/ux500_dma.c +++ b/drivers/usb/musb/ux500_dma.c @@ -359,7 +359,7 @@ static int ux500_dma_controller_start(struct ux500_dma_controller *controller) return 0; } -void dma_controller_destroy(struct dma_controller *c) +void ux500_dma_controller_destroy(struct dma_controller *c) { struct ux500_dma_controller *controller = container_of(c, struct ux500_dma_controller, controller); @@ -367,9 +367,10 @@ void dma_controller_destroy(struct dma_controller *c) ux500_dma_controller_stop(controller); kfree(controller); } +EXPORT_SYMBOL_GPL(ux500_dma_controller_destroy); -struct dma_controller *dma_controller_create(struct musb *musb, - void __iomem *base) +struct dma_controller * +ux500_dma_controller_create(struct musb *musb, void __iomem *base) { struct ux500_dma_controller *controller; struct platform_device *pdev = to_platform_device(musb->controller); @@ -407,3 +408,4 @@ plat_get_fail: kzalloc_fail: return NULL; } +EXPORT_SYMBOL_GPL(ux500_dma_controller_create); diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 2175678e674e..869c0cfcad98 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -91,7 +91,7 @@ config TWL6030_USB config USB_GPIO_VBUS tristate "GPIO based peripheral-only VBUS sensing 'transceiver'" - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST select USB_PHY help Provides simple GPIO VBUS sensing for controllers with an @@ -141,6 +141,7 @@ config USB_MSM_OTG tristate "Qualcomm on-chip USB OTG controller support" depends on (USB || USB_GADGET) && (ARCH_QCOM || COMPILE_TEST) depends on RESET_CONTROLLER + depends on EXTCON select USB_PHY help Enable this to support the USB OTG transceiver on Qualcomm chips. It @@ -186,19 +187,6 @@ config USB_RCAR_PHY To compile this driver as a module, choose M here: the module will be called phy-rcar-usb. -config USB_RCAR_GEN2_PHY - tristate "Renesas R-Car Gen2 USB PHY support" - depends on ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST - select USB_PHY - help - Say Y here to add support for the Renesas R-Car Gen2 USB PHY driver. - It is typically used to control internal USB PHY for USBHS, - and to configure shared USB channels 0 and 2. - This driver supports R8A7790 and R8A7791. - - To compile this driver as a module, choose M here: the - module will be called phy-rcar-gen2-usb. - config USB_ULPI bool "Generic ULPI Transceiver Driver" depends on ARM || ARM64 diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 75f2bba58c84..e36ab1d46d8b 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -23,7 +23,6 @@ obj-$(CONFIG_USB_MSM_OTG) += phy-msm-usb.o obj-$(CONFIG_USB_MV_OTG) += phy-mv-usb.o obj-$(CONFIG_USB_MXS_PHY) += phy-mxs-usb.o obj-$(CONFIG_USB_RCAR_PHY) += phy-rcar-usb.o -obj-$(CONFIG_USB_RCAR_GEN2_PHY) += phy-rcar-gen2-usb.o obj-$(CONFIG_USB_ULPI) += phy-ulpi.o obj-$(CONFIG_USB_ULPI_VIEWPORT) += phy-ulpi-viewport.o obj-$(CONFIG_KEYSTONE_USB_PHY) += phy-keystone.o diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c index 03ab0c699f74..0c912d3950a5 100644 --- a/drivers/usb/phy/phy-ab8500-usb.c +++ b/drivers/usb/phy/phy-ab8500-usb.c @@ -1504,7 +1504,7 @@ static int ab8500_usb_remove(struct platform_device *pdev) return 0; } -static struct platform_device_id ab8500_usb_devtype[] = { +static const struct platform_device_id ab8500_usb_devtype[] = { { .name = "ab8500-usb", }, { .name = "ab8540-usb", }, { .name = "ab9540-usb", }, diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index c9156beeadef..00c49bb1bd29 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -240,8 +240,14 @@ static void ulpi_init(struct msm_otg *motg) static int msm_phy_notify_disconnect(struct usb_phy *phy, enum usb_device_speed speed) { + struct msm_otg *motg = container_of(phy, struct msm_otg, phy); int val; + if (motg->manual_pullup) { + val = ULPI_MISC_A_VBUSVLDEXT | ULPI_MISC_A_VBUSVLDEXTSEL; + usb_phy_io_write(phy, val, ULPI_CLR(ULPI_MISC_A)); + } + /* * Put the transceiver in non-driving mode. Otherwise host * may not detect soft-disconnection. @@ -422,6 +428,24 @@ static int msm_phy_init(struct usb_phy *phy) ulpi_write(phy, ulpi_val, ULPI_USB_INT_EN_FALL); } + if (motg->manual_pullup) { + val = ULPI_MISC_A_VBUSVLDEXTSEL | ULPI_MISC_A_VBUSVLDEXT; + ulpi_write(phy, val, ULPI_SET(ULPI_MISC_A)); + + val = readl(USB_GENCONFIG_2); + val |= GENCONFIG_2_SESS_VLD_CTRL_EN; + writel(val, USB_GENCONFIG_2); + + val = readl(USB_USBCMD); + val |= USBCMD_SESS_VLD_CTRL; + writel(val, USB_USBCMD); + + val = ulpi_read(phy, ULPI_FUNC_CTRL); + val &= ~ULPI_FUNC_CTRL_OPMODE_MASK; + val |= ULPI_FUNC_CTRL_OPMODE_NORMAL; + ulpi_write(phy, val, ULPI_FUNC_CTRL); + } + if (motg->phy_number) writel(readl(USB_PHY_CTRL2) | BIT(16), USB_PHY_CTRL2); @@ -1436,9 +1460,42 @@ static const struct of_device_id msm_otg_dt_match[] = { }; MODULE_DEVICE_TABLE(of, msm_otg_dt_match); +static int msm_otg_vbus_notifier(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct msm_usb_cable *vbus = container_of(nb, struct msm_usb_cable, nb); + struct msm_otg *motg = container_of(vbus, struct msm_otg, vbus); + + if (event) + set_bit(B_SESS_VLD, &motg->inputs); + else + clear_bit(B_SESS_VLD, &motg->inputs); + + schedule_work(&motg->sm_work); + + return NOTIFY_DONE; +} + +static int msm_otg_id_notifier(struct notifier_block *nb, unsigned long event, + void *ptr) +{ + struct msm_usb_cable *id = container_of(nb, struct msm_usb_cable, nb); + struct msm_otg *motg = container_of(id, struct msm_otg, id); + + if (event) + clear_bit(ID, &motg->inputs); + else + set_bit(ID, &motg->inputs); + + schedule_work(&motg->sm_work); + + return NOTIFY_DONE; +} + static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg) { struct msm_otg_platform_data *pdata; + struct extcon_dev *ext_id, *ext_vbus; const struct of_device_id *id; struct device_node *node = pdev->dev.of_node; struct property *prop; @@ -1487,6 +1544,54 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg) motg->vdd_levels[VDD_LEVEL_MAX] = tmp[VDD_LEVEL_MAX]; } + motg->manual_pullup = of_property_read_bool(node, "qcom,manual-pullup"); + + ext_id = ERR_PTR(-ENODEV); + ext_vbus = ERR_PTR(-ENODEV); + if (of_property_read_bool(node, "extcon")) { + + /* Each one of them is not mandatory */ + ext_vbus = extcon_get_edev_by_phandle(&pdev->dev, 0); + if (IS_ERR(ext_vbus) && PTR_ERR(ext_vbus) != -ENODEV) + return PTR_ERR(ext_vbus); + + ext_id = extcon_get_edev_by_phandle(&pdev->dev, 1); + if (IS_ERR(ext_id) && PTR_ERR(ext_id) != -ENODEV) + return PTR_ERR(ext_id); + } + + if (!IS_ERR(ext_vbus)) { + motg->vbus.nb.notifier_call = msm_otg_vbus_notifier; + ret = extcon_register_interest(&motg->vbus.conn, ext_vbus->name, + "USB", &motg->vbus.nb); + if (ret < 0) { + dev_err(&pdev->dev, "register VBUS notifier failed\n"); + return ret; + } + + ret = extcon_get_cable_state(ext_vbus, "USB"); + if (ret) + set_bit(B_SESS_VLD, &motg->inputs); + else + clear_bit(B_SESS_VLD, &motg->inputs); + } + + if (!IS_ERR(ext_id)) { + motg->id.nb.notifier_call = msm_otg_id_notifier; + ret = extcon_register_interest(&motg->id.conn, ext_id->name, + "USB-HOST", &motg->id.nb); + if (ret < 0) { + dev_err(&pdev->dev, "register ID notifier failed\n"); + return ret; + } + + ret = extcon_get_cable_state(ext_id, "USB-HOST"); + if (ret) + clear_bit(ID, &motg->inputs); + else + set_bit(ID, &motg->inputs); + } + prop = of_find_property(node, "qcom,phy-init-sequence", &len); if (!prop || !len) return 0; @@ -1700,6 +1805,11 @@ static int msm_otg_remove(struct platform_device *pdev) if (phy->otg->host || phy->otg->gadget) return -EBUSY; + if (motg->id.conn.edev) + extcon_unregister_interest(&motg->id.conn); + if (motg->vbus.conn.edev) + extcon_unregister_interest(&motg->vbus.conn); + msm_otg_debugfs_cleanup(); cancel_delayed_work_sync(&motg->chg_work); cancel_work_sync(&motg->sm_work); diff --git a/drivers/usb/phy/phy-rcar-gen2-usb.c b/drivers/usb/phy/phy-rcar-gen2-usb.c deleted file mode 100644 index f81800b6562a..000000000000 --- a/drivers/usb/phy/phy-rcar-gen2-usb.c +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Renesas R-Car Gen2 USB phy driver - * - * Copyright (C) 2013 Renesas Solutions Corp. - * Copyright (C) 2013 Cogent Embedded, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include <linux/clk.h> -#include <linux/delay.h> -#include <linux/io.h> -#include <linux/module.h> -#include <linux/platform_data/usb-rcar-gen2-phy.h> -#include <linux/platform_device.h> -#include <linux/spinlock.h> -#include <linux/usb/otg.h> - -struct rcar_gen2_usb_phy_priv { - struct usb_phy phy; - void __iomem *base; - struct clk *clk; - spinlock_t lock; - int usecount; - u32 ugctrl2; -}; - -#define usb_phy_to_priv(p) container_of(p, struct rcar_gen2_usb_phy_priv, phy) - -/* Low Power Status register */ -#define USBHS_LPSTS_REG 0x02 -#define USBHS_LPSTS_SUSPM (1 << 14) - -/* USB General control register */ -#define USBHS_UGCTRL_REG 0x80 -#define USBHS_UGCTRL_CONNECT (1 << 2) -#define USBHS_UGCTRL_PLLRESET (1 << 0) - -/* USB General control register 2 */ -#define USBHS_UGCTRL2_REG 0x84 -#define USBHS_UGCTRL2_USB0_PCI (1 << 4) -#define USBHS_UGCTRL2_USB0_HS (3 << 4) -#define USBHS_UGCTRL2_USB2_PCI (0 << 31) -#define USBHS_UGCTRL2_USB2_SS (1 << 31) - -/* USB General status register */ -#define USBHS_UGSTS_REG 0x88 -#define USBHS_UGSTS_LOCK (1 << 8) - -/* Enable USBHS internal phy */ -static int __rcar_gen2_usbhs_phy_enable(void __iomem *base) -{ - u32 val; - int i; - - /* USBHS PHY power on */ - val = ioread32(base + USBHS_UGCTRL_REG); - val &= ~USBHS_UGCTRL_PLLRESET; - iowrite32(val, base + USBHS_UGCTRL_REG); - - val = ioread16(base + USBHS_LPSTS_REG); - val |= USBHS_LPSTS_SUSPM; - iowrite16(val, base + USBHS_LPSTS_REG); - - for (i = 0; i < 20; i++) { - val = ioread32(base + USBHS_UGSTS_REG); - if ((val & USBHS_UGSTS_LOCK) == USBHS_UGSTS_LOCK) { - val = ioread32(base + USBHS_UGCTRL_REG); - val |= USBHS_UGCTRL_CONNECT; - iowrite32(val, base + USBHS_UGCTRL_REG); - return 0; - } - udelay(1); - } - - /* Timed out waiting for the PLL lock */ - return -ETIMEDOUT; -} - -/* Disable USBHS internal phy */ -static int __rcar_gen2_usbhs_phy_disable(void __iomem *base) -{ - u32 val; - - /* USBHS PHY power off */ - val = ioread32(base + USBHS_UGCTRL_REG); - val &= ~USBHS_UGCTRL_CONNECT; - iowrite32(val, base + USBHS_UGCTRL_REG); - - val = ioread16(base + USBHS_LPSTS_REG); - val &= ~USBHS_LPSTS_SUSPM; - iowrite16(val, base + USBHS_LPSTS_REG); - - val = ioread32(base + USBHS_UGCTRL_REG); - val |= USBHS_UGCTRL_PLLRESET; - iowrite32(val, base + USBHS_UGCTRL_REG); - return 0; -} - -/* Setup USB channels */ -static void __rcar_gen2_usb_phy_init(struct rcar_gen2_usb_phy_priv *priv) -{ - u32 val; - - clk_prepare_enable(priv->clk); - - /* Set USB channels in the USBHS UGCTRL2 register */ - val = ioread32(priv->base + USBHS_UGCTRL2_REG); - val &= ~(USBHS_UGCTRL2_USB0_HS | USBHS_UGCTRL2_USB2_SS); - val |= priv->ugctrl2; - iowrite32(val, priv->base + USBHS_UGCTRL2_REG); -} - -/* Shutdown USB channels */ -static void __rcar_gen2_usb_phy_shutdown(struct rcar_gen2_usb_phy_priv *priv) -{ - __rcar_gen2_usbhs_phy_disable(priv->base); - clk_disable_unprepare(priv->clk); -} - -static int rcar_gen2_usb_phy_set_suspend(struct usb_phy *phy, int suspend) -{ - struct rcar_gen2_usb_phy_priv *priv = usb_phy_to_priv(phy); - unsigned long flags; - int retval; - - spin_lock_irqsave(&priv->lock, flags); - retval = suspend ? __rcar_gen2_usbhs_phy_disable(priv->base) : - __rcar_gen2_usbhs_phy_enable(priv->base); - spin_unlock_irqrestore(&priv->lock, flags); - return retval; -} - -static int rcar_gen2_usb_phy_init(struct usb_phy *phy) -{ - struct rcar_gen2_usb_phy_priv *priv = usb_phy_to_priv(phy); - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - /* - * Enable the clock and setup USB channels - * if it's the first user - */ - if (!priv->usecount++) - __rcar_gen2_usb_phy_init(priv); - spin_unlock_irqrestore(&priv->lock, flags); - return 0; -} - -static void rcar_gen2_usb_phy_shutdown(struct usb_phy *phy) -{ - struct rcar_gen2_usb_phy_priv *priv = usb_phy_to_priv(phy); - unsigned long flags; - - spin_lock_irqsave(&priv->lock, flags); - if (!priv->usecount) { - dev_warn(phy->dev, "Trying to disable phy with 0 usecount\n"); - goto out; - } - - /* Disable everything if it's the last user */ - if (!--priv->usecount) - __rcar_gen2_usb_phy_shutdown(priv); -out: - spin_unlock_irqrestore(&priv->lock, flags); -} - -static int rcar_gen2_usb_phy_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct rcar_gen2_phy_platform_data *pdata; - struct rcar_gen2_usb_phy_priv *priv; - struct resource *res; - void __iomem *base; - struct clk *clk; - int retval; - - pdata = dev_get_platdata(dev); - if (!pdata) { - dev_err(dev, "No platform data\n"); - return -EINVAL; - } - - clk = devm_clk_get(dev, "usbhs"); - if (IS_ERR(clk)) { - dev_err(dev, "Can't get the clock\n"); - return PTR_ERR(clk); - } - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - spin_lock_init(&priv->lock); - priv->clk = clk; - priv->base = base; - priv->ugctrl2 = pdata->chan0_pci ? - USBHS_UGCTRL2_USB0_PCI : USBHS_UGCTRL2_USB0_HS; - priv->ugctrl2 |= pdata->chan2_pci ? - USBHS_UGCTRL2_USB2_PCI : USBHS_UGCTRL2_USB2_SS; - priv->phy.dev = dev; - priv->phy.label = dev_name(dev); - priv->phy.init = rcar_gen2_usb_phy_init; - priv->phy.shutdown = rcar_gen2_usb_phy_shutdown; - priv->phy.set_suspend = rcar_gen2_usb_phy_set_suspend; - - retval = usb_add_phy_dev(&priv->phy); - if (retval < 0) { - dev_err(dev, "Failed to add USB phy\n"); - return retval; - } - - platform_set_drvdata(pdev, priv); - - return retval; -} - -static int rcar_gen2_usb_phy_remove(struct platform_device *pdev) -{ - struct rcar_gen2_usb_phy_priv *priv = platform_get_drvdata(pdev); - - usb_remove_phy(&priv->phy); - - return 0; -} - -static struct platform_driver rcar_gen2_usb_phy_driver = { - .driver = { - .name = "usb_phy_rcar_gen2", - }, - .probe = rcar_gen2_usb_phy_probe, - .remove = rcar_gen2_usb_phy_remove, -}; - -module_platform_driver(rcar_gen2_usb_phy_driver); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Renesas R-Car Gen2 USB phy"); -MODULE_AUTHOR("Valentine Barshak <valentine.barshak@cogentembedded.com>"); diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index d1cd6b50f520..98f75d2842b7 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -22,6 +22,11 @@ static LIST_HEAD(phy_list); static LIST_HEAD(phy_bind_list); static DEFINE_SPINLOCK(phy_lock); +struct phy_devm { + struct usb_phy *phy; + struct notifier_block *nb; +}; + static struct usb_phy *__usb_find_phy(struct list_head *list, enum usb_phy_type type) { @@ -79,6 +84,15 @@ static void devm_usb_phy_release(struct device *dev, void *res) usb_put_phy(phy); } +static void devm_usb_phy_release2(struct device *dev, void *_res) +{ + struct phy_devm *res = _res; + + if (res->nb) + usb_unregister_notifier(res->phy, res->nb); + usb_put_phy(res->phy); +} + static int devm_usb_phy_match(struct device *dev, void *res, void *match_data) { struct usb_phy **phy = res; @@ -153,40 +167,30 @@ err0: EXPORT_SYMBOL_GPL(usb_get_phy); /** - * devm_usb_get_phy_by_phandle - find the USB PHY by phandle + * devm_usb_get_phy_by_node - find the USB PHY by device_node * @dev - device that requests this phy - * @phandle - name of the property holding the phy phandle value - * @index - the index of the phy + * @node - the device_node for the phy device. + * @nb - a notifier_block to register with the phy. * - * Returns the phy driver associated with the given phandle value, + * Returns the phy driver associated with the given device_node, * after getting a refcount to it, -ENODEV if there is no such phy or - * -EPROBE_DEFER if there is a phandle to the phy, but the device is - * not yet loaded. While at that, it also associates the device with + * -EPROBE_DEFER if the device is not yet loaded. While at that, it + * also associates the device with * the phy using devres. On driver detach, release function is invoked * on the devres data, then, devres data is freed. * - * For use by USB host and peripheral drivers. + * For use by peripheral drivers for devices related to a phy, + * such as a charger. */ -struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, - const char *phandle, u8 index) +struct usb_phy *devm_usb_get_phy_by_node(struct device *dev, + struct device_node *node, + struct notifier_block *nb) { - struct usb_phy *phy = ERR_PTR(-ENOMEM), **ptr; + struct usb_phy *phy = ERR_PTR(-ENOMEM); + struct phy_devm *ptr; unsigned long flags; - struct device_node *node; - if (!dev->of_node) { - dev_dbg(dev, "device does not have a device node entry\n"); - return ERR_PTR(-EINVAL); - } - - node = of_parse_phandle(dev->of_node, phandle, index); - if (!node) { - dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle, - dev->of_node->full_name); - return ERR_PTR(-ENODEV); - } - - ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL); + ptr = devres_alloc(devm_usb_phy_release2, sizeof(*ptr), GFP_KERNEL); if (!ptr) { dev_dbg(dev, "failed to allocate memory for devres\n"); goto err0; @@ -205,8 +209,10 @@ struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, devres_free(ptr); goto err1; } - - *ptr = phy; + if (nb) + usb_register_notifier(phy, nb); + ptr->phy = phy; + ptr->nb = nb; devres_add(dev, ptr); get_device(phy->dev); @@ -215,10 +221,47 @@ err1: spin_unlock_irqrestore(&phy_lock, flags); err0: - of_node_put(node); return phy; } +EXPORT_SYMBOL_GPL(devm_usb_get_phy_by_node); + +/** + * devm_usb_get_phy_by_phandle - find the USB PHY by phandle + * @dev - device that requests this phy + * @phandle - name of the property holding the phy phandle value + * @index - the index of the phy + * + * Returns the phy driver associated with the given phandle value, + * after getting a refcount to it, -ENODEV if there is no such phy or + * -EPROBE_DEFER if there is a phandle to the phy, but the device is + * not yet loaded. While at that, it also associates the device with + * the phy using devres. On driver detach, release function is invoked + * on the devres data, then, devres data is freed. + * + * For use by USB host and peripheral drivers. + */ +struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, + const char *phandle, u8 index) +{ + struct device_node *node; + struct usb_phy *phy; + + if (!dev->of_node) { + dev_dbg(dev, "device does not have a device node entry\n"); + return ERR_PTR(-EINVAL); + } + + node = of_parse_phandle(dev->of_node, phandle, index); + if (!node) { + dev_dbg(dev, "failed to get %s phandle in %s node\n", phandle, + dev->of_node->full_name); + return ERR_PTR(-ENODEV); + } + phy = devm_usb_get_phy_by_node(dev, node, NULL); + of_node_put(node); + return phy; +} EXPORT_SYMBOL_GPL(devm_usb_get_phy_by_phandle); /** diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 0f7e850fd4aa..e8bf40808b39 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -466,11 +466,15 @@ static int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev) static const struct of_device_id usbhs_of_match[] = { { .compatible = "renesas,usbhs-r8a7790", - .data = (void *)USBHS_TYPE_R8A7790, + .data = (void *)USBHS_TYPE_RCAR_GEN2, }, { .compatible = "renesas,usbhs-r8a7791", - .data = (void *)USBHS_TYPE_R8A7791, + .data = (void *)USBHS_TYPE_RCAR_GEN2, + }, + { + .compatible = "renesas,usbhs-r8a7794", + .data = (void *)USBHS_TYPE_RCAR_GEN2, }, { }, }; @@ -497,14 +501,8 @@ static struct renesas_usbhs_platform_info *usbhs_parse_dt(struct device *dev) if (gpio > 0) dparam->enable_gpio = gpio; - switch (dparam->type) { - case USBHS_TYPE_R8A7790: - case USBHS_TYPE_R8A7791: + if (dparam->type == USBHS_TYPE_RCAR_GEN2) dparam->has_usb_dmac = 1; - break; - default: - break; - } return info; } @@ -559,8 +557,7 @@ static int usbhs_probe(struct platform_device *pdev) sizeof(struct renesas_usbhs_driver_param)); switch (priv->dparam.type) { - case USBHS_TYPE_R8A7790: - case USBHS_TYPE_R8A7791: + case USBHS_TYPE_RCAR_GEN2: priv->pfunc = usbhs_rcar2_ops; if (!priv->dparam.pipe_type) { priv->dparam.pipe_type = usbhsc_new_pipe_type; diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h index 04d3f8abad9e..c7d9b86d51bf 100644 --- a/drivers/usb/renesas_usbhs/fifo.h +++ b/drivers/usb/renesas_usbhs/fifo.h @@ -44,10 +44,11 @@ struct usbhs_fifo_info { struct usbhs_fifo dfifo[USBHS_MAX_NUM_DFIFO]; }; #define usbhsf_get_dnfifo(p, n) (&((p)->fifo_info.dfifo[n])) -#define usbhs_for_each_dfifo(priv, dfifo, i) \ - for ((i) = 0, dfifo = usbhsf_get_dnfifo(priv, (i)); \ - ((i) < USBHS_MAX_NUM_DFIFO); \ - (i)++, dfifo = usbhsf_get_dnfifo(priv, (i))) +#define usbhs_for_each_dfifo(priv, dfifo, i) \ + for ((i) = 0; \ + ((i) < USBHS_MAX_NUM_DFIFO) && \ + ((dfifo) = usbhsf_get_dnfifo(priv, (i))); \ + (i)++) struct usbhs_pkt_handle; struct usbhs_pkt { diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c index 9a705b15b3a1..d4be5d594896 100644 --- a/drivers/usb/renesas_usbhs/mod.c +++ b/drivers/usb/renesas_usbhs/mod.c @@ -218,10 +218,14 @@ static int usbhs_status_get_each_irq(struct usbhs_priv *priv, /******************** spin lock ********************/ usbhs_lock(priv, flags); state->intsts0 = usbhs_read(priv, INTSTS0); - state->intsts1 = usbhs_read(priv, INTSTS1); - intenb0 = usbhs_read(priv, INTENB0); - intenb1 = usbhs_read(priv, INTENB1); + + if (usbhs_mod_is_host(priv)) { + state->intsts1 = usbhs_read(priv, INTSTS1); + intenb1 = usbhs_read(priv, INTENB1); + } else { + state->intsts1 = intenb1 = 0; + } /* mask */ if (mod) { @@ -275,7 +279,8 @@ static irqreturn_t usbhs_interrupt(int irq, void *data) * - Function :: VALID bit should 0 */ usbhs_write(priv, INTSTS0, ~irq_state.intsts0 & INTSTS0_MAGIC); - usbhs_write(priv, INTSTS1, ~irq_state.intsts1 & INTSTS1_MAGIC); + if (usbhs_mod_is_host(priv)) + usbhs_write(priv, INTSTS1, ~irq_state.intsts1 & INTSTS1_MAGIC); usbhs_write(priv, BRDYSTS, ~irq_state.brdysts); usbhs_write(priv, NRDYSTS, ~irq_state.nrdysts); @@ -303,19 +308,20 @@ static irqreturn_t usbhs_interrupt(int irq, void *data) if (irq_state.intsts0 & BRDY) usbhs_mod_call(priv, irq_ready, priv, &irq_state); - /* INTSTS1 */ - if (irq_state.intsts1 & ATTCH) - usbhs_mod_call(priv, irq_attch, priv, &irq_state); - - if (irq_state.intsts1 & DTCH) - usbhs_mod_call(priv, irq_dtch, priv, &irq_state); + if (usbhs_mod_is_host(priv)) { + /* INTSTS1 */ + if (irq_state.intsts1 & ATTCH) + usbhs_mod_call(priv, irq_attch, priv, &irq_state); - if (irq_state.intsts1 & SIGN) - usbhs_mod_call(priv, irq_sign, priv, &irq_state); + if (irq_state.intsts1 & DTCH) + usbhs_mod_call(priv, irq_dtch, priv, &irq_state); - if (irq_state.intsts1 & SACK) - usbhs_mod_call(priv, irq_sack, priv, &irq_state); + if (irq_state.intsts1 & SIGN) + usbhs_mod_call(priv, irq_sign, priv, &irq_state); + if (irq_state.intsts1 & SACK) + usbhs_mod_call(priv, irq_sack, priv, &irq_state); + } return IRQ_HANDLED; } @@ -334,7 +340,8 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) * - update INTSTS0 */ usbhs_write(priv, INTENB0, 0); - usbhs_write(priv, INTENB1, 0); + if (usbhs_mod_is_host(priv)) + usbhs_write(priv, INTENB1, 0); usbhs_write(priv, BEMPENB, 0); usbhs_write(priv, BRDYENB, 0); @@ -368,25 +375,27 @@ void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod) intenb0 |= BRDYE; } - /* - * INTSTS1 - */ - if (mod->irq_attch) - intenb1 |= ATTCHE; + if (usbhs_mod_is_host(priv)) { + /* + * INTSTS1 + */ + if (mod->irq_attch) + intenb1 |= ATTCHE; - if (mod->irq_dtch) - intenb1 |= DTCHE; + if (mod->irq_dtch) + intenb1 |= DTCHE; - if (mod->irq_sign) - intenb1 |= SIGNE; + if (mod->irq_sign) + intenb1 |= SIGNE; - if (mod->irq_sack) - intenb1 |= SACKE; + if (mod->irq_sack) + intenb1 |= SACKE; + } } if (intenb0) usbhs_write(priv, INTENB0, intenb0); - if (intenb1) + if (usbhs_mod_is_host(priv) && intenb1) usbhs_write(priv, INTENB1, intenb1); } diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c index e4473a9109cf..8ac9b55f05af 100644 --- a/drivers/usb/serial/mos7840.c +++ b/drivers/usb/serial/mos7840.c @@ -2301,17 +2301,14 @@ static int mos7840_port_probe(struct usb_serial_port *port) goto error; } - init_timer(&mos7840_port->led_timer1); - mos7840_port->led_timer1.function = mos7840_led_off; + setup_timer(&mos7840_port->led_timer1, mos7840_led_off, + (unsigned long)mos7840_port); mos7840_port->led_timer1.expires = jiffies + msecs_to_jiffies(LED_ON_MS); - mos7840_port->led_timer1.data = (unsigned long)mos7840_port; - - init_timer(&mos7840_port->led_timer2); - mos7840_port->led_timer2.function = mos7840_led_flag_off; + setup_timer(&mos7840_port->led_timer2, mos7840_led_flag_off, + (unsigned long)mos7840_port); mos7840_port->led_timer2.expires = jiffies + msecs_to_jiffies(LED_OFF_MS); - mos7840_port->led_timer2.data = (unsigned long)mos7840_port; /* Turn off LED */ mos7840_set_led_sync(port, MODEM_CONTROL_REGISTER, 0x0300); diff --git a/drivers/usb/storage/alauda.c b/drivers/usb/storage/alauda.c index 4b55ab66a534..171fa7d793bc 100644 --- a/drivers/usb/storage/alauda.c +++ b/drivers/usb/storage/alauda.c @@ -42,6 +42,9 @@ #include "transport.h" #include "protocol.h" #include "debug.h" +#include "scsiglue.h" + +#define DRV_NAME "ums-alauda" MODULE_DESCRIPTION("Driver for Alauda-based card readers"); MODULE_AUTHOR("Daniel Drake <dsd@gentoo.org>"); @@ -1232,6 +1235,8 @@ static int alauda_transport(struct scsi_cmnd *srb, struct us_data *us) return USB_STOR_TRANSPORT_FAILED; } +static struct scsi_host_template alauda_host_template; + static int alauda_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1239,7 +1244,8 @@ static int alauda_probe(struct usb_interface *intf, int result; result = usb_stor_probe1(&us, intf, id, - (id - alauda_usb_ids) + alauda_unusual_dev_list); + (id - alauda_usb_ids) + alauda_unusual_dev_list, + &alauda_host_template); if (result) return result; @@ -1253,7 +1259,7 @@ static int alauda_probe(struct usb_interface *intf, } static struct usb_driver alauda_driver = { - .name = "ums-alauda", + .name = DRV_NAME, .probe = alauda_probe, .disconnect = usb_stor_disconnect, .suspend = usb_stor_suspend, @@ -1266,4 +1272,4 @@ static struct usb_driver alauda_driver = { .no_dynamic_id = 1, }; -module_usb_driver(alauda_driver); +module_usb_stor_driver(alauda_driver, alauda_host_template, DRV_NAME); diff --git a/drivers/usb/storage/cypress_atacb.c b/drivers/usb/storage/cypress_atacb.c index b3466d1395f2..c80d3dec9a34 100644 --- a/drivers/usb/storage/cypress_atacb.c +++ b/drivers/usb/storage/cypress_atacb.c @@ -30,6 +30,8 @@ #include "scsiglue.h" #include "debug.h" +#define DRV_NAME "ums-cypress" + MODULE_DESCRIPTION("SAT support for Cypress USB/ATA bridges with ATACB"); MODULE_AUTHOR("Matthieu Castet <castet.matthieu@free.fr>"); MODULE_LICENSE("GPL"); @@ -241,6 +243,7 @@ end: srb->cmd_len = 12; } +static struct scsi_host_template cypress_host_template; static int cypress_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -250,7 +253,8 @@ static int cypress_probe(struct usb_interface *intf, struct usb_device *device; result = usb_stor_probe1(&us, intf, id, - (id - cypress_usb_ids) + cypress_unusual_dev_list); + (id - cypress_usb_ids) + cypress_unusual_dev_list, + &cypress_host_template); if (result) return result; @@ -273,7 +277,7 @@ static int cypress_probe(struct usb_interface *intf, } static struct usb_driver cypress_driver = { - .name = "ums-cypress", + .name = DRV_NAME, .probe = cypress_probe, .disconnect = usb_stor_disconnect, .suspend = usb_stor_suspend, @@ -286,4 +290,4 @@ static struct usb_driver cypress_driver = { .no_dynamic_id = 1, }; -module_usb_driver(cypress_driver); +module_usb_stor_driver(cypress_driver, cypress_host_template, DRV_NAME); diff --git a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c index 7b17c2169812..aa4f51944a4a 100644 --- a/drivers/usb/storage/datafab.c +++ b/drivers/usb/storage/datafab.c @@ -59,6 +59,9 @@ #include "transport.h" #include "protocol.h" #include "debug.h" +#include "scsiglue.h" + +#define DRV_NAME "ums-datafab" MODULE_DESCRIPTION("Driver for Datafab USB Compact Flash reader"); MODULE_AUTHOR("Jimmie Mayfield <mayfield+datafab@sackheads.org>"); @@ -721,6 +724,8 @@ static int datafab_transport(struct scsi_cmnd *srb, struct us_data *us) return USB_STOR_TRANSPORT_FAILED; } +static struct scsi_host_template datafab_host_template; + static int datafab_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -728,7 +733,8 @@ static int datafab_probe(struct usb_interface *intf, int result; result = usb_stor_probe1(&us, intf, id, - (id - datafab_usb_ids) + datafab_unusual_dev_list); + (id - datafab_usb_ids) + datafab_unusual_dev_list, + &datafab_host_template); if (result) return result; @@ -742,7 +748,7 @@ static int datafab_probe(struct usb_interface *intf, } static struct usb_driver datafab_driver = { - .name = "ums-datafab", + .name = DRV_NAME, .probe = datafab_probe, .disconnect = usb_stor_disconnect, .suspend = usb_stor_suspend, @@ -755,4 +761,4 @@ static struct usb_driver datafab_driver = { .no_dynamic_id = 1, }; -module_usb_driver(datafab_driver); +module_usb_stor_driver(datafab_driver, datafab_host_template, DRV_NAME); diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c index 56f782bef36b..f3cf4cecd2b7 100644 --- a/drivers/usb/storage/ene_ub6250.c +++ b/drivers/usb/storage/ene_ub6250.c @@ -28,6 +28,7 @@ #include "transport.h" #include "protocol.h" #include "debug.h" +#include "scsiglue.h" #define SD_INIT1_FIRMWARE "ene-ub6250/sd_init1.bin" #define SD_INIT2_FIRMWARE "ene-ub6250/sd_init2.bin" @@ -36,6 +37,8 @@ #define MSP_RW_FIRMWARE "ene-ub6250/msp_rdwr.bin" #define MS_RW_FIRMWARE "ene-ub6250/ms_rdwr.bin" +#define DRV_NAME "ums_eneub6250" + MODULE_DESCRIPTION("Driver for ENE UB6250 reader"); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(SD_INIT1_FIRMWARE); @@ -2307,6 +2310,7 @@ static int ene_transport(struct scsi_cmnd *srb, struct us_data *us) return 0; } +static struct scsi_host_template ene_ub6250_host_template; static int ene_ub6250_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -2316,7 +2320,8 @@ static int ene_ub6250_probe(struct usb_interface *intf, struct us_data *us; result = usb_stor_probe1(&us, intf, id, - (id - ene_ub6250_usb_ids) + ene_ub6250_unusual_dev_list); + (id - ene_ub6250_usb_ids) + ene_ub6250_unusual_dev_list, + &ene_ub6250_host_template); if (result) return result; @@ -2404,7 +2409,7 @@ static int ene_ub6250_reset_resume(struct usb_interface *iface) #endif static struct usb_driver ene_ub6250_driver = { - .name = "ums_eneub6250", + .name = DRV_NAME, .probe = ene_ub6250_probe, .disconnect = usb_stor_disconnect, .suspend = usb_stor_suspend, @@ -2417,4 +2422,4 @@ static struct usb_driver ene_ub6250_driver = { .no_dynamic_id = 1, }; -module_usb_driver(ene_ub6250_driver); +module_usb_stor_driver(ene_ub6250_driver, ene_ub6250_host_template, DRV_NAME); diff --git a/drivers/usb/storage/freecom.c b/drivers/usb/storage/freecom.c index ef16068b7087..3f2b08966b9d 100644 --- a/drivers/usb/storage/freecom.c +++ b/drivers/usb/storage/freecom.c @@ -34,6 +34,9 @@ #include "transport.h" #include "protocol.h" #include "debug.h" +#include "scsiglue.h" + +#define DRV_NAME "ums-freecom" MODULE_DESCRIPTION("Driver for Freecom USB/IDE adaptor"); MODULE_AUTHOR("David Brown <usb-storage@davidb.org>"); @@ -523,6 +526,8 @@ static void pdump(struct us_data *us, void *ibuffer, int length) } #endif +static struct scsi_host_template freecom_host_template; + static int freecom_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -530,7 +535,8 @@ static int freecom_probe(struct usb_interface *intf, int result; result = usb_stor_probe1(&us, intf, id, - (id - freecom_usb_ids) + freecom_unusual_dev_list); + (id - freecom_usb_ids) + freecom_unusual_dev_list, + &freecom_host_template); if (result) return result; @@ -544,7 +550,7 @@ static int freecom_probe(struct usb_interface *intf, } static struct usb_driver freecom_driver = { - .name = "ums-freecom", + .name = DRV_NAME, .probe = freecom_probe, .disconnect = usb_stor_disconnect, .suspend = usb_stor_suspend, @@ -557,4 +563,4 @@ static struct usb_driver freecom_driver = { .no_dynamic_id = 1, }; -module_usb_driver(freecom_driver); +module_usb_stor_driver(freecom_driver, freecom_host_template, DRV_NAME); diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c index 076178645ba4..1bac215202d2 100644 --- a/drivers/usb/storage/isd200.c +++ b/drivers/usb/storage/isd200.c @@ -60,6 +60,8 @@ #include "debug.h" #include "scsiglue.h" +#define DRV_NAME "ums-isd200" + MODULE_DESCRIPTION("Driver for In-System Design, Inc. ISD200 ASIC"); MODULE_AUTHOR("Björn Stenberg <bjorn@haxx.se>"); MODULE_LICENSE("GPL"); @@ -1537,6 +1539,8 @@ static void isd200_ata_command(struct scsi_cmnd *srb, struct us_data *us) isd200_srb_set_bufflen(srb, orig_bufflen); } +static struct scsi_host_template isd200_host_template; + static int isd200_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1544,7 +1548,8 @@ static int isd200_probe(struct usb_interface *intf, int result; result = usb_stor_probe1(&us, intf, id, - (id - isd200_usb_ids) + isd200_unusual_dev_list); + (id - isd200_usb_ids) + isd200_unusual_dev_list, + &isd200_host_template); if (result) return result; @@ -1556,7 +1561,7 @@ static int isd200_probe(struct usb_interface *intf, } static struct usb_driver isd200_driver = { - .name = "ums-isd200", + .name = DRV_NAME, .probe = isd200_probe, .disconnect = usb_stor_disconnect, .suspend = usb_stor_suspend, @@ -1569,4 +1574,4 @@ static struct usb_driver isd200_driver = { .no_dynamic_id = 1, }; -module_usb_driver(isd200_driver); +module_usb_stor_driver(isd200_driver, isd200_host_template, DRV_NAME); diff --git a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c index 563078be6547..ee613e258db0 100644 --- a/drivers/usb/storage/jumpshot.c +++ b/drivers/usb/storage/jumpshot.c @@ -56,7 +56,9 @@ #include "transport.h" #include "protocol.h" #include "debug.h" +#include "scsiglue.h" +#define DRV_NAME "ums-jumpshot" MODULE_DESCRIPTION("Driver for Lexar \"Jumpshot\" Compact Flash reader"); MODULE_AUTHOR("Jimmie Mayfield <mayfield+usb@sackheads.org>"); @@ -647,6 +649,8 @@ static int jumpshot_transport(struct scsi_cmnd *srb, struct us_data *us) return USB_STOR_TRANSPORT_FAILED; } +static struct scsi_host_template jumpshot_host_template; + static int jumpshot_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -654,7 +658,8 @@ static int jumpshot_probe(struct usb_interface *intf, int result; result = usb_stor_probe1(&us, intf, id, - (id - jumpshot_usb_ids) + jumpshot_unusual_dev_list); + (id - jumpshot_usb_ids) + jumpshot_unusual_dev_list, + &jumpshot_host_template); if (result) return result; @@ -668,7 +673,7 @@ static int jumpshot_probe(struct usb_interface *intf, } static struct usb_driver jumpshot_driver = { - .name = "ums-jumpshot", + .name = DRV_NAME, .probe = jumpshot_probe, .disconnect = usb_stor_disconnect, .suspend = usb_stor_suspend, @@ -681,4 +686,4 @@ static struct usb_driver jumpshot_driver = { .no_dynamic_id = 1, }; -module_usb_driver(jumpshot_driver); +module_usb_stor_driver(jumpshot_driver, jumpshot_host_template, DRV_NAME); diff --git a/drivers/usb/storage/karma.c b/drivers/usb/storage/karma.c index 94d16ee5e84b..ae201e69475c 100644 --- a/drivers/usb/storage/karma.c +++ b/drivers/usb/storage/karma.c @@ -28,6 +28,9 @@ #include "usb.h" #include "transport.h" #include "debug.h" +#include "scsiglue.h" + +#define DRV_NAME "ums-karma" MODULE_DESCRIPTION("Driver for Rio Karma"); MODULE_AUTHOR("Bob Copeland <me@bobcopeland.com>, Keith Bennett <keith@mcs.st-and.ac.uk>"); @@ -200,6 +203,8 @@ out: return ret; } +static struct scsi_host_template karma_host_template; + static int karma_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -207,7 +212,8 @@ static int karma_probe(struct usb_interface *intf, int result; result = usb_stor_probe1(&us, intf, id, - (id - karma_usb_ids) + karma_unusual_dev_list); + (id - karma_usb_ids) + karma_unusual_dev_list, + &karma_host_template); if (result) return result; @@ -220,7 +226,7 @@ static int karma_probe(struct usb_interface *intf, } static struct usb_driver karma_driver = { - .name = "ums-karma", + .name = DRV_NAME, .probe = karma_probe, .disconnect = usb_stor_disconnect, .suspend = usb_stor_suspend, @@ -233,4 +239,4 @@ static struct usb_driver karma_driver = { .no_dynamic_id = 1, }; -module_usb_driver(karma_driver); +module_usb_stor_driver(karma_driver, karma_host_template, DRV_NAME); diff --git a/drivers/usb/storage/onetouch.c b/drivers/usb/storage/onetouch.c index 74e2aa23b045..acc3d03d8c1e 100644 --- a/drivers/usb/storage/onetouch.c +++ b/drivers/usb/storage/onetouch.c @@ -35,6 +35,9 @@ #include <linux/usb/input.h> #include "usb.h" #include "debug.h" +#include "scsiglue.h" + +#define DRV_NAME "ums-onetouch" MODULE_DESCRIPTION("Maxtor USB OneTouch hard drive button driver"); MODULE_AUTHOR("Nick Sillik <n.sillik@temple.edu>"); @@ -283,6 +286,8 @@ static void onetouch_release_input(void *onetouch_) } } +static struct scsi_host_template onetouch_host_template; + static int onetouch_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -290,7 +295,8 @@ static int onetouch_probe(struct usb_interface *intf, int result; result = usb_stor_probe1(&us, intf, id, - (id - onetouch_usb_ids) + onetouch_unusual_dev_list); + (id - onetouch_usb_ids) + onetouch_unusual_dev_list, + &onetouch_host_template); if (result) return result; @@ -301,7 +307,7 @@ static int onetouch_probe(struct usb_interface *intf, } static struct usb_driver onetouch_driver = { - .name = "ums-onetouch", + .name = DRV_NAME, .probe = onetouch_probe, .disconnect = usb_stor_disconnect, .suspend = usb_stor_suspend, @@ -314,4 +320,4 @@ static struct usb_driver onetouch_driver = { .no_dynamic_id = 1, }; -module_usb_driver(onetouch_driver); +module_usb_stor_driver(onetouch_driver, onetouch_host_template, DRV_NAME); diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c index 27e4a580d2ed..20433563a601 100644 --- a/drivers/usb/storage/realtek_cr.c +++ b/drivers/usb/storage/realtek_cr.c @@ -39,6 +39,9 @@ #include "transport.h" #include "protocol.h" #include "debug.h" +#include "scsiglue.h" + +#define DRV_NAME "ums-realtek" MODULE_DESCRIPTION("Driver for Realtek USB Card Reader"); MODULE_AUTHOR("wwang <wei_wang@realsil.com.cn>"); @@ -1034,6 +1037,8 @@ INIT_FAIL: return -EIO; } +static struct scsi_host_template realtek_cr_host_template; + static int realtek_cr_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1044,7 +1049,8 @@ static int realtek_cr_probe(struct usb_interface *intf, result = usb_stor_probe1(&us, intf, id, (id - realtek_cr_ids) + - realtek_cr_unusual_dev_list); + realtek_cr_unusual_dev_list, + &realtek_cr_host_template); if (result) return result; @@ -1054,7 +1060,7 @@ static int realtek_cr_probe(struct usb_interface *intf, } static struct usb_driver realtek_cr_driver = { - .name = "ums-realtek", + .name = DRV_NAME, .probe = realtek_cr_probe, .disconnect = usb_stor_disconnect, /* .suspend = usb_stor_suspend, */ @@ -1070,4 +1076,4 @@ static struct usb_driver realtek_cr_driver = { .no_dynamic_id = 1, }; -module_usb_driver(realtek_cr_driver); +module_usb_stor_driver(realtek_cr_driver, realtek_cr_host_template, DRV_NAME); diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c index 996ef1e882d3..dba51362d2e2 100644 --- a/drivers/usb/storage/scsiglue.c +++ b/drivers/usb/storage/scsiglue.c @@ -456,17 +456,13 @@ static int write_info(struct Scsi_Host *host, char *buffer, int length) return length; } -/* we use this macro to help us write into the buffer */ -#undef SPRINTF -#define SPRINTF(args...) seq_printf(m, ## args) - static int show_info (struct seq_file *m, struct Scsi_Host *host) { struct us_data *us = host_to_us(host); const char *string; /* print the controller name */ - SPRINTF(" Host scsi%d: usb-storage\n", host->host_no); + seq_printf(m, " Host scsi%d: usb-storage\n", host->host_no); /* print product, vendor, and serial number strings */ if (us->pusb_dev->manufacturer) @@ -475,26 +471,26 @@ static int show_info (struct seq_file *m, struct Scsi_Host *host) string = us->unusual_dev->vendorName; else string = "Unknown"; - SPRINTF(" Vendor: %s\n", string); + seq_printf(m, " Vendor: %s\n", string); if (us->pusb_dev->product) string = us->pusb_dev->product; else if (us->unusual_dev->productName) string = us->unusual_dev->productName; else string = "Unknown"; - SPRINTF(" Product: %s\n", string); + seq_printf(m, " Product: %s\n", string); if (us->pusb_dev->serial) string = us->pusb_dev->serial; else string = "None"; - SPRINTF("Serial Number: %s\n", string); + seq_printf(m, "Serial Number: %s\n", string); /* show the protocol and transport */ - SPRINTF(" Protocol: %s\n", us->protocol_name); - SPRINTF(" Transport: %s\n", us->transport_name); + seq_printf(m, " Protocol: %s\n", us->protocol_name); + seq_printf(m, " Transport: %s\n", us->transport_name); /* show the device flags */ - SPRINTF(" Quirks:"); + seq_printf(m, " Quirks:"); #define US_FLAG(name, value) \ if (us->fflags & value) seq_printf(m, " " #name); @@ -540,7 +536,7 @@ static struct device_attribute *sysfs_device_attr_list[] = { * this defines our host template, with which we'll allocate hosts */ -struct scsi_host_template usb_stor_host_template = { +static const struct scsi_host_template usb_stor_host_template = { /* basic userland interface stuff */ .name = "usb-storage", .proc_name = "usb-storage", @@ -591,6 +587,16 @@ struct scsi_host_template usb_stor_host_template = { .module = THIS_MODULE }; +void usb_stor_host_template_init(struct scsi_host_template *sht, + const char *name, struct module *owner) +{ + *sht = usb_stor_host_template; + sht->name = name; + sht->proc_name = name; + sht->module = owner; +} +EXPORT_SYMBOL_GPL(usb_stor_host_template_init); + /* To Report "Illegal Request: Invalid Field in CDB */ unsigned char usb_stor_sense_invalidCDB[18] = { [0] = 0x70, /* current error */ diff --git a/drivers/usb/storage/scsiglue.h b/drivers/usb/storage/scsiglue.h index ffa1cca93d2c..5494d87607fb 100644 --- a/drivers/usb/storage/scsiglue.h +++ b/drivers/usb/storage/scsiglue.h @@ -41,8 +41,9 @@ extern void usb_stor_report_device_reset(struct us_data *us); extern void usb_stor_report_bus_reset(struct us_data *us); +extern void usb_stor_host_template_init(struct scsi_host_template *sht, + const char *name, struct module *owner); extern unsigned char usb_stor_sense_invalidCDB[18]; -extern struct scsi_host_template usb_stor_host_template; #endif diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c index 3847053d732c..b74603689b9e 100644 --- a/drivers/usb/storage/sddr09.c +++ b/drivers/usb/storage/sddr09.c @@ -52,6 +52,9 @@ #include "transport.h" #include "protocol.h" #include "debug.h" +#include "scsiglue.h" + +#define DRV_NAME "ums-sddr09" MODULE_DESCRIPTION("Driver for SanDisk SDDR-09 SmartMedia reader"); MODULE_AUTHOR("Andries Brouwer <aeb@cwi.nl>, Robert Baruch <autophile@starband.net>"); @@ -1738,6 +1741,8 @@ usb_stor_sddr09_init(struct us_data *us) { return sddr09_common_init(us); } +static struct scsi_host_template sddr09_host_template; + static int sddr09_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1745,7 +1750,8 @@ static int sddr09_probe(struct usb_interface *intf, int result; result = usb_stor_probe1(&us, intf, id, - (id - sddr09_usb_ids) + sddr09_unusual_dev_list); + (id - sddr09_usb_ids) + sddr09_unusual_dev_list, + &sddr09_host_template); if (result) return result; @@ -1766,7 +1772,7 @@ static int sddr09_probe(struct usb_interface *intf, } static struct usb_driver sddr09_driver = { - .name = "ums-sddr09", + .name = DRV_NAME, .probe = sddr09_probe, .disconnect = usb_stor_disconnect, .suspend = usb_stor_suspend, @@ -1779,4 +1785,4 @@ static struct usb_driver sddr09_driver = { .no_dynamic_id = 1, }; -module_usb_driver(sddr09_driver); +module_usb_stor_driver(sddr09_driver, sddr09_host_template, DRV_NAME); diff --git a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c index aacedef9667c..e5e0a25ecd96 100644 --- a/drivers/usb/storage/sddr55.c +++ b/drivers/usb/storage/sddr55.c @@ -34,6 +34,9 @@ #include "transport.h" #include "protocol.h" #include "debug.h" +#include "scsiglue.h" + +#define DRV_NAME "ums-sddr55" MODULE_DESCRIPTION("Driver for SanDisk SDDR-55 SmartMedia reader"); MODULE_AUTHOR("Simon Munton"); @@ -968,6 +971,7 @@ static int sddr55_transport(struct scsi_cmnd *srb, struct us_data *us) return USB_STOR_TRANSPORT_FAILED; // FIXME: sense buffer? } +static struct scsi_host_template sddr55_host_template; static int sddr55_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -976,7 +980,8 @@ static int sddr55_probe(struct usb_interface *intf, int result; result = usb_stor_probe1(&us, intf, id, - (id - sddr55_usb_ids) + sddr55_unusual_dev_list); + (id - sddr55_usb_ids) + sddr55_unusual_dev_list, + &sddr55_host_template); if (result) return result; @@ -990,7 +995,7 @@ static int sddr55_probe(struct usb_interface *intf, } static struct usb_driver sddr55_driver = { - .name = "ums-sddr55", + .name = DRV_NAME, .probe = sddr55_probe, .disconnect = usb_stor_disconnect, .suspend = usb_stor_suspend, @@ -1003,4 +1008,4 @@ static struct usb_driver sddr55_driver = { .no_dynamic_id = 1, }; -module_usb_driver(sddr55_driver); +module_usb_stor_driver(sddr55_driver, sddr55_host_template, DRV_NAME); diff --git a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c index 008d805c3d21..a3ec86b913a1 100644 --- a/drivers/usb/storage/shuttle_usbat.c +++ b/drivers/usb/storage/shuttle_usbat.c @@ -53,6 +53,9 @@ #include "transport.h" #include "protocol.h" #include "debug.h" +#include "scsiglue.h" + +#define DRV_NAME "ums-usbat" MODULE_DESCRIPTION("Driver for SCM Microsystems (a.k.a. Shuttle) USB-ATAPI cable"); MODULE_AUTHOR("Daniel Drake <dsd@gentoo.org>, Robert Baruch <autophile@starband.net>"); @@ -1834,6 +1837,8 @@ static int init_usbat_flash(struct us_data *us) return init_usbat(us, USBAT_DEV_FLASH); } +static struct scsi_host_template usbat_host_template; + static int usbat_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1841,7 +1846,8 @@ static int usbat_probe(struct usb_interface *intf, int result; result = usb_stor_probe1(&us, intf, id, - (id - usbat_usb_ids) + usbat_unusual_dev_list); + (id - usbat_usb_ids) + usbat_unusual_dev_list, + &usbat_host_template); if (result) return result; @@ -1858,7 +1864,7 @@ static int usbat_probe(struct usb_interface *intf, } static struct usb_driver usbat_driver = { - .name = "ums-usbat", + .name = DRV_NAME, .probe = usbat_probe, .disconnect = usb_stor_disconnect, .suspend = usb_stor_suspend, @@ -1871,4 +1877,4 @@ static struct usb_driver usbat_driver = { .no_dynamic_id = 1, }; -module_usb_driver(usbat_driver); +module_usb_stor_driver(usbat_driver, usbat_host_template, DRV_NAME); diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c index 6c10c888f35f..43576ed31ccd 100644 --- a/drivers/usb/storage/usb.c +++ b/drivers/usb/storage/usb.c @@ -76,6 +76,8 @@ #include "uas-detect.h" #endif +#define DRV_NAME "usb-storage" + /* Some informational data */ MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>"); MODULE_DESCRIPTION("USB Mass Storage driver for Linux"); @@ -924,7 +926,8 @@ static unsigned int usb_stor_sg_tablesize(struct usb_interface *intf) int usb_stor_probe1(struct us_data **pus, struct usb_interface *intf, const struct usb_device_id *id, - struct us_unusual_dev *unusual_dev) + struct us_unusual_dev *unusual_dev, + struct scsi_host_template *sht) { struct Scsi_Host *host; struct us_data *us; @@ -936,7 +939,7 @@ int usb_stor_probe1(struct us_data **pus, * Ask the SCSI layer to allocate a host structure, with extra * space at the end for our private us_data structure. */ - host = scsi_host_alloc(&usb_stor_host_template, sizeof(*us)); + host = scsi_host_alloc(sht, sizeof(*us)); if (!host) { dev_warn(&intf->dev, "Unable to allocate the scsi host\n"); return -ENOMEM; @@ -1073,6 +1076,8 @@ void usb_stor_disconnect(struct usb_interface *intf) } EXPORT_SYMBOL_GPL(usb_stor_disconnect); +static struct scsi_host_template usb_stor_host_template; + /* The main probe routine for standard devices */ static int storage_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -1113,7 +1118,8 @@ static int storage_probe(struct usb_interface *intf, id->idVendor, id->idProduct); } - result = usb_stor_probe1(&us, intf, id, unusual_dev); + result = usb_stor_probe1(&us, intf, id, unusual_dev, + &usb_stor_host_template); if (result) return result; @@ -1124,7 +1130,7 @@ static int storage_probe(struct usb_interface *intf, } static struct usb_driver usb_storage_driver = { - .name = "usb-storage", + .name = DRV_NAME, .probe = storage_probe, .disconnect = usb_stor_disconnect, .suspend = usb_stor_suspend, @@ -1137,4 +1143,4 @@ static struct usb_driver usb_storage_driver = { .soft_unbind = 1, }; -module_usb_driver(usb_storage_driver); +module_usb_stor_driver(usb_storage_driver, usb_stor_host_template, DRV_NAME); diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h index 307e339a9478..da0ad3241728 100644 --- a/drivers/usb/storage/usb.h +++ b/drivers/usb/storage/usb.h @@ -197,11 +197,25 @@ extern int usb_stor_post_reset(struct usb_interface *iface); extern int usb_stor_probe1(struct us_data **pus, struct usb_interface *intf, const struct usb_device_id *id, - struct us_unusual_dev *unusual_dev); + struct us_unusual_dev *unusual_dev, + struct scsi_host_template *sht); extern int usb_stor_probe2(struct us_data *us); extern void usb_stor_disconnect(struct usb_interface *intf); extern void usb_stor_adjust_quirks(struct usb_device *dev, unsigned long *fflags); +#define module_usb_stor_driver(__driver, __sht, __name) \ +static int __init __driver##_init(void) \ +{ \ + usb_stor_host_template_init(&(__sht), __name, THIS_MODULE); \ + return usb_register(&(__driver)); \ +} \ +module_init(__driver##_init); \ +static void __exit __driver##_exit(void) \ +{ \ + usb_deregister(&(__driver)); \ +} \ +module_exit(__driver##_exit) + #endif diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h index a82296af413f..2a2f56b292c1 100644 --- a/include/linux/fsl_devices.h +++ b/include/linux/fsl_devices.h @@ -24,6 +24,7 @@ #define FSL_USB_VER_1_6 1 #define FSL_USB_VER_2_2 2 #define FSL_USB_VER_2_4 3 +#define FSL_USB_VER_2_5 4 #include <linux/types.h> diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h index 048c270822f9..8183d6640ca7 100644 --- a/include/linux/mod_devicetable.h +++ b/include/linux/mod_devicetable.h @@ -642,4 +642,10 @@ struct mcb_device_id { kernel_ulong_t driver_data; }; +struct ulpi_device_id { + __u16 vendor; + __u16 product; + kernel_ulong_t driver_data; +}; + #endif /* LINUX_MOD_DEVICETABLE_H */ diff --git a/include/linux/phy/phy-sun4i-usb.h b/include/linux/phy/phy-sun4i-usb.h new file mode 100644 index 000000000000..50aed92ea89c --- /dev/null +++ b/include/linux/phy/phy-sun4i-usb.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2015 Hans de Goede <hdegoede@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + */ + +#ifndef PHY_SUN4I_USB_H_ +#define PHY_SUN4I_USB_H_ + +#include "phy.h" + +/** + * sun4i_usb_phy_set_squelch_detect() - Enable/disable squelch detect + * @phy: reference to a sun4i usb phy + * @enabled: wether to enable or disable squelch detect + */ +void sun4i_usb_phy_set_squelch_detect(struct phy *phy, bool enabled); + +#endif diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index a0197fa1b116..8cf05e341cff 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -133,6 +133,8 @@ struct phy *devm_phy_get(struct device *dev, const char *string); struct phy *devm_phy_optional_get(struct device *dev, const char *string); struct phy *devm_of_phy_get(struct device *dev, struct device_node *np, const char *con_id); +struct phy *devm_of_phy_get_by_index(struct device *dev, struct device_node *np, + int index); void phy_put(struct phy *phy); void devm_phy_put(struct device *dev, struct phy *phy); struct phy *of_phy_get(struct device_node *np, const char *con_id); @@ -261,6 +263,13 @@ static inline struct phy *devm_of_phy_get(struct device *dev, return ERR_PTR(-ENOSYS); } +static inline struct phy *devm_of_phy_get_by_index(struct device *dev, + struct device_node *np, + int index) +{ + return ERR_PTR(-ENOSYS); +} + static inline void phy_put(struct phy *phy) { } diff --git a/include/linux/platform_data/usb-rcar-gen2-phy.h b/include/linux/platform_data/usb-rcar-gen2-phy.h deleted file mode 100644 index dd3ba46c0d90..000000000000 --- a/include/linux/platform_data/usb-rcar-gen2-phy.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2013 Renesas Solutions Corp. - * Copyright (C) 2013 Cogent Embedded, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef __USB_RCAR_GEN2_PHY_H -#define __USB_RCAR_GEN2_PHY_H - -#include <linux/types.h> - -struct rcar_gen2_phy_platform_data { - /* USB channel 0 configuration */ - bool chan0_pci:1; /* true: PCI USB host 0, false: USBHS */ - /* USB channel 2 configuration */ - bool chan2_pci:1; /* true: PCI USB host 2, false: USBSS */ -}; - -#endif diff --git a/include/linux/ulpi/driver.h b/include/linux/ulpi/driver.h new file mode 100644 index 000000000000..388f6e08b9d4 --- /dev/null +++ b/include/linux/ulpi/driver.h @@ -0,0 +1,60 @@ +#ifndef __LINUX_ULPI_DRIVER_H +#define __LINUX_ULPI_DRIVER_H + +#include <linux/mod_devicetable.h> + +#include <linux/device.h> + +struct ulpi_ops; + +/** + * struct ulpi - describes ULPI PHY device + * @id: vendor and product ids for ULPI device + * @ops: I/O access + * @dev: device interface + */ +struct ulpi { + struct ulpi_device_id id; + struct ulpi_ops *ops; + struct device dev; +}; + +#define to_ulpi_dev(d) container_of(d, struct ulpi, dev) + +static inline void ulpi_set_drvdata(struct ulpi *ulpi, void *data) +{ + dev_set_drvdata(&ulpi->dev, data); +} + +static inline void *ulpi_get_drvdata(struct ulpi *ulpi) +{ + return dev_get_drvdata(&ulpi->dev); +} + +/** + * struct ulpi_driver - describes a ULPI PHY driver + * @id_table: array of device identifiers supported by this driver + * @probe: binds this driver to ULPI device + * @remove: unbinds this driver from ULPI device + * @driver: the name and owner members must be initialized by the drivers + */ +struct ulpi_driver { + const struct ulpi_device_id *id_table; + int (*probe)(struct ulpi *ulpi); + void (*remove)(struct ulpi *ulpi); + struct device_driver driver; +}; + +#define to_ulpi_driver(d) container_of(d, struct ulpi_driver, driver) + +int ulpi_register_driver(struct ulpi_driver *drv); +void ulpi_unregister_driver(struct ulpi_driver *drv); + +#define module_ulpi_driver(__ulpi_driver) \ + module_driver(__ulpi_driver, ulpi_register_driver, \ + ulpi_unregister_driver) + +int ulpi_read(struct ulpi *ulpi, u8 addr); +int ulpi_write(struct ulpi *ulpi, u8 addr, u8 val); + +#endif /* __LINUX_ULPI_DRIVER_H */ diff --git a/include/linux/ulpi/interface.h b/include/linux/ulpi/interface.h new file mode 100644 index 000000000000..4de8ab491038 --- /dev/null +++ b/include/linux/ulpi/interface.h @@ -0,0 +1,23 @@ +#ifndef __LINUX_ULPI_INTERFACE_H +#define __LINUX_ULPI_INTERFACE_H + +#include <linux/types.h> + +struct ulpi; + +/** + * struct ulpi_ops - ULPI register access + * @dev: the interface provider + * @read: read operation for ULPI register access + * @write: write operation for ULPI register access + */ +struct ulpi_ops { + struct device *dev; + int (*read)(struct ulpi_ops *ops, u8 addr); + int (*write)(struct ulpi_ops *ops, u8 addr, u8 val); +}; + +struct ulpi *ulpi_register_interface(struct device *, struct ulpi_ops *); +void ulpi_unregister_interface(struct ulpi *); + +#endif /* __LINUX_ULPI_INTERFACE_H */ diff --git a/include/linux/ulpi/regs.h b/include/linux/ulpi/regs.h new file mode 100644 index 000000000000..b5b8b8804560 --- /dev/null +++ b/include/linux/ulpi/regs.h @@ -0,0 +1,130 @@ +#ifndef __LINUX_ULPI_REGS_H +#define __LINUX_ULPI_REGS_H + +/* + * Macros for Set and Clear + * See ULPI 1.1 specification to find the registers with Set and Clear offsets + */ +#define ULPI_SET(a) (a + 1) +#define ULPI_CLR(a) (a + 2) + +/* + * Register Map + */ +#define ULPI_VENDOR_ID_LOW 0x00 +#define ULPI_VENDOR_ID_HIGH 0x01 +#define ULPI_PRODUCT_ID_LOW 0x02 +#define ULPI_PRODUCT_ID_HIGH 0x03 +#define ULPI_FUNC_CTRL 0x04 +#define ULPI_IFC_CTRL 0x07 +#define ULPI_OTG_CTRL 0x0a +#define ULPI_USB_INT_EN_RISE 0x0d +#define ULPI_USB_INT_EN_FALL 0x10 +#define ULPI_USB_INT_STS 0x13 +#define ULPI_USB_INT_LATCH 0x14 +#define ULPI_DEBUG 0x15 +#define ULPI_SCRATCH 0x16 +/* Optional Carkit Registers */ +#define ULPI_CARKIT_CTRL 0x19 +#define ULPI_CARKIT_INT_DELAY 0x1c +#define ULPI_CARKIT_INT_EN 0x1d +#define ULPI_CARKIT_INT_STS 0x20 +#define ULPI_CARKIT_INT_LATCH 0x21 +#define ULPI_CARKIT_PLS_CTRL 0x22 +/* Other Optional Registers */ +#define ULPI_TX_POS_WIDTH 0x25 +#define ULPI_TX_NEG_WIDTH 0x26 +#define ULPI_POLARITY_RECOVERY 0x27 +/* Access Extended Register Set */ +#define ULPI_ACCESS_EXTENDED 0x2f +/* Vendor Specific */ +#define ULPI_VENDOR_SPECIFIC 0x30 +/* Extended Registers */ +#define ULPI_EXT_VENDOR_SPECIFIC 0x80 + +/* + * Register Bits + */ + +/* Function Control */ +#define ULPI_FUNC_CTRL_XCVRSEL BIT(0) +#define ULPI_FUNC_CTRL_XCVRSEL_MASK 0x3 +#define ULPI_FUNC_CTRL_HIGH_SPEED 0x0 +#define ULPI_FUNC_CTRL_FULL_SPEED 0x1 +#define ULPI_FUNC_CTRL_LOW_SPEED 0x2 +#define ULPI_FUNC_CTRL_FS4LS 0x3 +#define ULPI_FUNC_CTRL_TERMSELECT BIT(2) +#define ULPI_FUNC_CTRL_OPMODE BIT(3) +#define ULPI_FUNC_CTRL_OPMODE_MASK (0x3 << 3) +#define ULPI_FUNC_CTRL_OPMODE_NORMAL (0x0 << 3) +#define ULPI_FUNC_CTRL_OPMODE_NONDRIVING (0x1 << 3) +#define ULPI_FUNC_CTRL_OPMODE_DISABLE_NRZI (0x2 << 3) +#define ULPI_FUNC_CTRL_OPMODE_NOSYNC_NOEOP (0x3 << 3) +#define ULPI_FUNC_CTRL_RESET BIT(5) +#define ULPI_FUNC_CTRL_SUSPENDM BIT(6) + +/* Interface Control */ +#define ULPI_IFC_CTRL_6_PIN_SERIAL_MODE BIT(0) +#define ULPI_IFC_CTRL_3_PIN_SERIAL_MODE BIT(1) +#define ULPI_IFC_CTRL_CARKITMODE BIT(2) +#define ULPI_IFC_CTRL_CLOCKSUSPENDM BIT(3) +#define ULPI_IFC_CTRL_AUTORESUME BIT(4) +#define ULPI_IFC_CTRL_EXTERNAL_VBUS BIT(5) +#define ULPI_IFC_CTRL_PASSTHRU BIT(6) +#define ULPI_IFC_CTRL_PROTECT_IFC_DISABLE BIT(7) + +/* OTG Control */ +#define ULPI_OTG_CTRL_ID_PULLUP BIT(0) +#define ULPI_OTG_CTRL_DP_PULLDOWN BIT(1) +#define ULPI_OTG_CTRL_DM_PULLDOWN BIT(2) +#define ULPI_OTG_CTRL_DISCHRGVBUS BIT(3) +#define ULPI_OTG_CTRL_CHRGVBUS BIT(4) +#define ULPI_OTG_CTRL_DRVVBUS BIT(5) +#define ULPI_OTG_CTRL_DRVVBUS_EXT BIT(6) +#define ULPI_OTG_CTRL_EXTVBUSIND BIT(7) + +/* USB Interrupt Enable Rising, + * USB Interrupt Enable Falling, + * USB Interrupt Status and + * USB Interrupt Latch + */ +#define ULPI_INT_HOST_DISCONNECT BIT(0) +#define ULPI_INT_VBUS_VALID BIT(1) +#define ULPI_INT_SESS_VALID BIT(2) +#define ULPI_INT_SESS_END BIT(3) +#define ULPI_INT_IDGRD BIT(4) + +/* Debug */ +#define ULPI_DEBUG_LINESTATE0 BIT(0) +#define ULPI_DEBUG_LINESTATE1 BIT(1) + +/* Carkit Control */ +#define ULPI_CARKIT_CTRL_CARKITPWR BIT(0) +#define ULPI_CARKIT_CTRL_IDGNDDRV BIT(1) +#define ULPI_CARKIT_CTRL_TXDEN BIT(2) +#define ULPI_CARKIT_CTRL_RXDEN BIT(3) +#define ULPI_CARKIT_CTRL_SPKLEFTEN BIT(4) +#define ULPI_CARKIT_CTRL_SPKRIGHTEN BIT(5) +#define ULPI_CARKIT_CTRL_MICEN BIT(6) + +/* Carkit Interrupt Enable */ +#define ULPI_CARKIT_INT_EN_IDFLOAT_RISE BIT(0) +#define ULPI_CARKIT_INT_EN_IDFLOAT_FALL BIT(1) +#define ULPI_CARKIT_INT_EN_CARINTDET BIT(2) +#define ULPI_CARKIT_INT_EN_DP_RISE BIT(3) +#define ULPI_CARKIT_INT_EN_DP_FALL BIT(4) + +/* Carkit Interrupt Status and + * Carkit Interrupt Latch + */ +#define ULPI_CARKIT_INT_IDFLOAT BIT(0) +#define ULPI_CARKIT_INT_CARINTDET BIT(1) +#define ULPI_CARKIT_INT_DP BIT(2) + +/* Carkit Pulse Control*/ +#define ULPI_CARKIT_PLS_CTRL_TXPLSEN BIT(0) +#define ULPI_CARKIT_PLS_CTRL_RXPLSEN BIT(1) +#define ULPI_CARKIT_PLS_CTRL_SPKRLEFT_BIASEN BIT(2) +#define ULPI_CARKIT_PLS_CTRL_SPKRRIGHT_BIASEN BIT(3) + +#endif /* __LINUX_ULPI_REGS_H */ diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 68b1e836dff1..c9aa7792de10 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -622,8 +622,6 @@ extern struct list_head usb_bus_list; extern struct mutex usb_bus_list_lock; extern wait_queue_head_t usb_kill_urb_queue; -extern int usb_find_interface_driver(struct usb_device *dev, - struct usb_interface *interface); #define usb_endpoint_out(ep_dir) (!((ep_dir) & USB_DIR_IN)) diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h index 7dbecf9a4656..e55a1504266e 100644 --- a/include/linux/usb/msm_hsusb.h +++ b/include/linux/usb/msm_hsusb.h @@ -18,6 +18,7 @@ #ifndef __ASM_ARCH_MSM_HSUSB_H #define __ASM_ARCH_MSM_HSUSB_H +#include <linux/extcon.h> #include <linux/types.h> #include <linux/usb/otg.h> #include <linux/clk.h> @@ -120,6 +121,17 @@ struct msm_otg_platform_data { }; /** + * struct msm_usb_cable - structure for exteternal connector cable + * state tracking + * @nb: hold event notification callback + * @conn: used for notification registration + */ +struct msm_usb_cable { + struct notifier_block nb; + struct extcon_specific_cable_nb conn; +}; + +/** * struct msm_otg: OTG driver data. Shared by HCD and DCD. * @otg: USB OTG Transceiver structure. * @pdata: otg device platform data. @@ -138,6 +150,11 @@ struct msm_otg_platform_data { * @chg_type: The type of charger attached. * @dcd_retires: The retry count used to track Data contact * detection process. + * @manual_pullup: true if VBUS is not routed to USB controller/phy + * and controller driver therefore enables pull-up explicitly before + * starting controller using usbcmd run/stop bit. + * @vbus: VBUS signal state trakining, using extcon framework + * @id: ID signal state trakining, using extcon framework */ struct msm_otg { struct usb_phy phy; @@ -166,6 +183,11 @@ struct msm_otg { struct reset_control *phy_rst; struct reset_control *link_rst; int vdd_levels[3]; + + bool manual_pullup; + + struct msm_usb_cable vbus; + struct msm_usb_cable id; }; #endif diff --git a/include/linux/usb/msm_hsusb_hw.h b/include/linux/usb/msm_hsusb_hw.h index a29f6030afb1..e159b39f67a2 100644 --- a/include/linux/usb/msm_hsusb_hw.h +++ b/include/linux/usb/msm_hsusb_hw.h @@ -21,6 +21,8 @@ #define USB_AHBBURST (MSM_USB_BASE + 0x0090) #define USB_AHBMODE (MSM_USB_BASE + 0x0098) +#define USB_GENCONFIG_2 (MSM_USB_BASE + 0x00a0) + #define USB_CAPLENGTH (MSM_USB_BASE + 0x0100) /* 8 bit */ #define USB_USBCMD (MSM_USB_BASE + 0x0140) @@ -30,6 +32,9 @@ #define USB_PHY_CTRL (MSM_USB_BASE + 0x0240) #define USB_PHY_CTRL2 (MSM_USB_BASE + 0x0278) +#define GENCONFIG_2_SESS_VLD_CTRL_EN BIT(7) +#define USBCMD_SESS_VLD_CTRL BIT(25) + #define USBCMD_RESET 2 #define USB_USBINTR (MSM_USB_BASE + 0x0148) @@ -50,6 +55,10 @@ #define ULPI_PWR_CLK_MNG_REG 0x88 #define OTG_COMP_DISABLE BIT(0) +#define ULPI_MISC_A 0x96 +#define ULPI_MISC_A_VBUSVLDEXTSEL BIT(1) +#define ULPI_MISC_A_VBUSVLDEXT BIT(0) + #define ASYNC_INTR_CTRL (1 << 29) /* Enable async interrupt */ #define ULPI_STP_CTRL (1 << 30) /* Block communication with PHY */ #define PHY_RETEN (1 << 1) /* PHY retention enable/disable */ diff --git a/include/linux/usb/net2280.h b/include/linux/usb/net2280.h index 148b8fa5b1a2..725120224472 100644 --- a/include/linux/usb/net2280.h +++ b/include/linux/usb/net2280.h @@ -168,6 +168,9 @@ struct net2280_regs { #define ENDPOINT_B_INTERRUPT 2 #define ENDPOINT_A_INTERRUPT 1 #define ENDPOINT_0_INTERRUPT 0 +#define USB3380_IRQSTAT0_EP_INTR_MASK_IN (0xF << 17) +#define USB3380_IRQSTAT0_EP_INTR_MASK_OUT (0xF << 1) + u32 irqstat1; #define POWER_STATE_CHANGE_INTERRUPT 27 #define PCI_ARBITER_TIMEOUT_INTERRUPT 26 diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h index bc91b5d380fd..e39f251cf861 100644 --- a/include/linux/usb/phy.h +++ b/include/linux/usb/phy.h @@ -205,6 +205,8 @@ extern struct usb_phy *usb_get_phy_dev(struct device *dev, u8 index); extern struct usb_phy *devm_usb_get_phy_dev(struct device *dev, u8 index); extern struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, const char *phandle, u8 index); +extern struct usb_phy *devm_usb_get_phy_by_node(struct device *dev, + struct device_node *node, struct notifier_block *nb); extern void usb_put_phy(struct usb_phy *); extern void devm_usb_put_phy(struct device *dev, struct usb_phy *x); extern int usb_bind_phy(const char *dev_name, u8 index, @@ -238,6 +240,12 @@ static inline struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev, return ERR_PTR(-ENXIO); } +static inline struct usb_phy *devm_usb_get_phy_by_node(struct device *dev, + struct device_node *node, struct notifier_block *nb) +{ + return ERR_PTR(-ENXIO); +} + static inline void usb_put_phy(struct usb_phy *x) { } diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h index f06529c14141..3dd5a781da99 100644 --- a/include/linux/usb/renesas_usbhs.h +++ b/include/linux/usb/renesas_usbhs.h @@ -169,8 +169,7 @@ struct renesas_usbhs_driver_param { #define USBHS_USB_DMAC_XFER_SIZE 32 /* hardcode the xfer size */ }; -#define USBHS_TYPE_R8A7790 1 -#define USBHS_TYPE_R8A7791 2 +#define USBHS_TYPE_RCAR_GEN2 1 /* * option: diff --git a/include/linux/usb/ulpi.h b/include/linux/usb/ulpi.h index 5c295c26ad37..5f07407a367a 100644 --- a/include/linux/usb/ulpi.h +++ b/include/linux/usb/ulpi.h @@ -12,6 +12,8 @@ #define __LINUX_USB_ULPI_H #include <linux/usb/otg.h> +#include <linux/ulpi/regs.h> + /*-------------------------------------------------------------------------*/ /* @@ -49,138 +51,6 @@ /*-------------------------------------------------------------------------*/ -/* - * Macros for Set and Clear - * See ULPI 1.1 specification to find the registers with Set and Clear offsets - */ -#define ULPI_SET(a) (a + 1) -#define ULPI_CLR(a) (a + 2) - -/*-------------------------------------------------------------------------*/ - -/* - * Register Map - */ -#define ULPI_VENDOR_ID_LOW 0x00 -#define ULPI_VENDOR_ID_HIGH 0x01 -#define ULPI_PRODUCT_ID_LOW 0x02 -#define ULPI_PRODUCT_ID_HIGH 0x03 -#define ULPI_FUNC_CTRL 0x04 -#define ULPI_IFC_CTRL 0x07 -#define ULPI_OTG_CTRL 0x0a -#define ULPI_USB_INT_EN_RISE 0x0d -#define ULPI_USB_INT_EN_FALL 0x10 -#define ULPI_USB_INT_STS 0x13 -#define ULPI_USB_INT_LATCH 0x14 -#define ULPI_DEBUG 0x15 -#define ULPI_SCRATCH 0x16 -/* Optional Carkit Registers */ -#define ULPI_CARCIT_CTRL 0x19 -#define ULPI_CARCIT_INT_DELAY 0x1c -#define ULPI_CARCIT_INT_EN 0x1d -#define ULPI_CARCIT_INT_STS 0x20 -#define ULPI_CARCIT_INT_LATCH 0x21 -#define ULPI_CARCIT_PLS_CTRL 0x22 -/* Other Optional Registers */ -#define ULPI_TX_POS_WIDTH 0x25 -#define ULPI_TX_NEG_WIDTH 0x26 -#define ULPI_POLARITY_RECOVERY 0x27 -/* Access Extended Register Set */ -#define ULPI_ACCESS_EXTENDED 0x2f -/* Vendor Specific */ -#define ULPI_VENDOR_SPECIFIC 0x30 -/* Extended Registers */ -#define ULPI_EXT_VENDOR_SPECIFIC 0x80 - -/*-------------------------------------------------------------------------*/ - -/* - * Register Bits - */ - -/* Function Control */ -#define ULPI_FUNC_CTRL_XCVRSEL (1 << 0) -#define ULPI_FUNC_CTRL_XCVRSEL_MASK (3 << 0) -#define ULPI_FUNC_CTRL_HIGH_SPEED (0 << 0) -#define ULPI_FUNC_CTRL_FULL_SPEED (1 << 0) -#define ULPI_FUNC_CTRL_LOW_SPEED (2 << 0) -#define ULPI_FUNC_CTRL_FS4LS (3 << 0) -#define ULPI_FUNC_CTRL_TERMSELECT (1 << 2) -#define ULPI_FUNC_CTRL_OPMODE (1 << 3) -#define ULPI_FUNC_CTRL_OPMODE_MASK (3 << 3) -#define ULPI_FUNC_CTRL_OPMODE_NORMAL (0 << 3) -#define ULPI_FUNC_CTRL_OPMODE_NONDRIVING (1 << 3) -#define ULPI_FUNC_CTRL_OPMODE_DISABLE_NRZI (2 << 3) -#define ULPI_FUNC_CTRL_OPMODE_NOSYNC_NOEOP (3 << 3) -#define ULPI_FUNC_CTRL_RESET (1 << 5) -#define ULPI_FUNC_CTRL_SUSPENDM (1 << 6) - -/* Interface Control */ -#define ULPI_IFC_CTRL_6_PIN_SERIAL_MODE (1 << 0) -#define ULPI_IFC_CTRL_3_PIN_SERIAL_MODE (1 << 1) -#define ULPI_IFC_CTRL_CARKITMODE (1 << 2) -#define ULPI_IFC_CTRL_CLOCKSUSPENDM (1 << 3) -#define ULPI_IFC_CTRL_AUTORESUME (1 << 4) -#define ULPI_IFC_CTRL_EXTERNAL_VBUS (1 << 5) -#define ULPI_IFC_CTRL_PASSTHRU (1 << 6) -#define ULPI_IFC_CTRL_PROTECT_IFC_DISABLE (1 << 7) - -/* OTG Control */ -#define ULPI_OTG_CTRL_ID_PULLUP (1 << 0) -#define ULPI_OTG_CTRL_DP_PULLDOWN (1 << 1) -#define ULPI_OTG_CTRL_DM_PULLDOWN (1 << 2) -#define ULPI_OTG_CTRL_DISCHRGVBUS (1 << 3) -#define ULPI_OTG_CTRL_CHRGVBUS (1 << 4) -#define ULPI_OTG_CTRL_DRVVBUS (1 << 5) -#define ULPI_OTG_CTRL_DRVVBUS_EXT (1 << 6) -#define ULPI_OTG_CTRL_EXTVBUSIND (1 << 7) - -/* USB Interrupt Enable Rising, - * USB Interrupt Enable Falling, - * USB Interrupt Status and - * USB Interrupt Latch - */ -#define ULPI_INT_HOST_DISCONNECT (1 << 0) -#define ULPI_INT_VBUS_VALID (1 << 1) -#define ULPI_INT_SESS_VALID (1 << 2) -#define ULPI_INT_SESS_END (1 << 3) -#define ULPI_INT_IDGRD (1 << 4) - -/* Debug */ -#define ULPI_DEBUG_LINESTATE0 (1 << 0) -#define ULPI_DEBUG_LINESTATE1 (1 << 1) - -/* Carkit Control */ -#define ULPI_CARKIT_CTRL_CARKITPWR (1 << 0) -#define ULPI_CARKIT_CTRL_IDGNDDRV (1 << 1) -#define ULPI_CARKIT_CTRL_TXDEN (1 << 2) -#define ULPI_CARKIT_CTRL_RXDEN (1 << 3) -#define ULPI_CARKIT_CTRL_SPKLEFTEN (1 << 4) -#define ULPI_CARKIT_CTRL_SPKRIGHTEN (1 << 5) -#define ULPI_CARKIT_CTRL_MICEN (1 << 6) - -/* Carkit Interrupt Enable */ -#define ULPI_CARKIT_INT_EN_IDFLOAT_RISE (1 << 0) -#define ULPI_CARKIT_INT_EN_IDFLOAT_FALL (1 << 1) -#define ULPI_CARKIT_INT_EN_CARINTDET (1 << 2) -#define ULPI_CARKIT_INT_EN_DP_RISE (1 << 3) -#define ULPI_CARKIT_INT_EN_DP_FALL (1 << 4) - -/* Carkit Interrupt Status and - * Carkit Interrupt Latch - */ -#define ULPI_CARKIT_INT_IDFLOAT (1 << 0) -#define ULPI_CARKIT_INT_CARINTDET (1 << 1) -#define ULPI_CARKIT_INT_DP (1 << 2) - -/* Carkit Pulse Control*/ -#define ULPI_CARKIT_PLS_CTRL_TXPLSEN (1 << 0) -#define ULPI_CARKIT_PLS_CTRL_RXPLSEN (1 << 1) -#define ULPI_CARKIT_PLS_CTRL_SPKRLEFT_BIASEN (1 << 2) -#define ULPI_CARKIT_PLS_CTRL_SPKRRIGHT_BIASEN (1 << 3) - -/*-------------------------------------------------------------------------*/ - #if IS_ENABLED(CONFIG_USB_ULPI) struct usb_phy *otg_ulpi_create(struct usb_phy_io_ops *ops, unsigned int flags); diff --git a/include/linux/usb/usb338x.h b/include/linux/usb/usb338x.h index f92eb635b9d3..11525d8d89a7 100644 --- a/include/linux/usb/usb338x.h +++ b/include/linux/usb/usb338x.h @@ -43,6 +43,10 @@ #define IN_ENDPOINT_TYPE 12 #define OUT_ENDPOINT_ENABLE 10 #define OUT_ENDPOINT_TYPE 8 +#define USB3380_EP_CFG_MASK_IN ((0x3 << IN_ENDPOINT_TYPE) | \ + BIT(IN_ENDPOINT_ENABLE)) +#define USB3380_EP_CFG_MASK_OUT ((0x3 << OUT_ENDPOINT_TYPE) | \ + BIT(OUT_ENDPOINT_ENABLE)) struct usb338x_usb_ext_regs { u32 usbclass; diff --git a/scripts/mod/devicetable-offsets.c b/scripts/mod/devicetable-offsets.c index 091f6290a651..eff7de1fc82e 100644 --- a/scripts/mod/devicetable-offsets.c +++ b/scripts/mod/devicetable-offsets.c @@ -190,5 +190,9 @@ int main(void) DEVID_FIELD(rio_device_id, asm_did); DEVID_FIELD(rio_device_id, asm_vid); + DEVID(ulpi_device_id); + DEVID_FIELD(ulpi_device_id, vendor); + DEVID_FIELD(ulpi_device_id, product); + return 0; } diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c index 718b2a29bd43..84c86f3cd6cd 100644 --- a/scripts/mod/file2alias.c +++ b/scripts/mod/file2alias.c @@ -1209,6 +1209,19 @@ static int do_rio_entry(const char *filename, } ADD_TO_DEVTABLE("rapidio", rio_device_id, do_rio_entry); +/* Looks like: ulpi:vNpN */ +static int do_ulpi_entry(const char *filename, void *symval, + char *alias) +{ + DEF_FIELD(symval, ulpi_device_id, vendor); + DEF_FIELD(symval, ulpi_device_id, product); + + sprintf(alias, "ulpi:v%04xp%04x", vendor, product); + + return 1; +} +ADD_TO_DEVTABLE("ulpi", ulpi_device_id, do_ulpi_entry); + /* Does namelen bytes of name exactly match the symbol? */ static bool sym_is(const char *name, unsigned namelen, const char *symbol) { |