summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDong Aisheng <aisheng.dong@nxp.com>2019-12-02 18:05:31 +0800
committerDong Aisheng <aisheng.dong@nxp.com>2019-12-02 18:05:31 +0800
commit210428cc3751c57bedc69e55567b326d5e5ce08a (patch)
tree287c4dadcf7ef69df98538cd2ced0c8f6a56235c
parent3e2e5e55509651952ff06ef12db50d6b6d4cb1da (diff)
parent8baeb551e70c8c9d5c1f5f3fd1c1ac541838ba82 (diff)
Merge branch 'usb/next' into next
* usb/next: (188 commits) LF-252 usb: cdns3: gadget: fix the issue for DMA scatter buffer list usb: dwc3: Add cache type configuration support usb: dwc3: Add chip-specific compatible string MLK-22675 usb: dwc3: host: disable park mode MLK-22878 usb: cdns3: gadget: add imx8qxp C0 support ...
-rw-r--r--Documentation/ABI/testing/sysfs-platform-cadence-usb39
-rw-r--r--Documentation/ABI/testing/usb-charger-uevent45
-rw-r--r--Documentation/devicetree/bindings/usb/cdns-usb3.txt72
-rw-r--r--Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt1
-rw-r--r--Documentation/devicetree/bindings/usb/dwc3.txt14
-rw-r--r--Documentation/devicetree/bindings/usb/typec-switch-gpio.txt30
-rw-r--r--Documentation/devicetree/bindings/usb/usb-xhci.txt2
-rw-r--r--drivers/usb/Kconfig2
-rw-r--r--drivers/usb/Makefile3
-rw-r--r--drivers/usb/cdns3/Kconfig38
-rw-r--r--drivers/usb/cdns3/Makefile18
-rw-r--r--drivers/usb/cdns3/cdns3-nxp-reg-def.h174
-rw-r--r--drivers/usb/cdns3/cdns3-pci-wrap.c204
-rw-r--r--drivers/usb/cdns3/core.c1304
-rw-r--r--drivers/usb/cdns3/core.h154
-rw-r--r--drivers/usb/cdns3/debug.h10
-rw-r--r--drivers/usb/cdns3/dev-regs-macro.h894
-rw-r--r--drivers/usb/cdns3/dev-regs-map.h126
-rw-r--r--drivers/usb/cdns3/drd.c381
-rw-r--r--drivers/usb/cdns3/drd.h167
-rw-r--r--drivers/usb/cdns3/ep0.c161
-rw-r--r--drivers/usb/cdns3/gadget-export.h3
-rw-r--r--drivers/usb/cdns3/gadget.c1370
-rw-r--r--drivers/usb/cdns3/gadget.h247
-rw-r--r--drivers/usb/cdns3/host-export.h32
-rw-r--r--drivers/usb/cdns3/host.c298
-rw-r--r--drivers/usb/cdns3/io.h35
-rw-r--r--drivers/usb/cdns3/trace.c14
-rw-r--r--drivers/usb/cdns3/trace.h81
-rw-r--r--drivers/usb/chipidea/bits.h4
-rw-r--r--drivers/usb/chipidea/ci.h44
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.c171
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.h11
-rw-r--r--drivers/usb/chipidea/core.c148
-rw-r--r--drivers/usb/chipidea/host.c189
-rw-r--r--drivers/usb/chipidea/host.h8
-rw-r--r--drivers/usb/chipidea/otg.c44
-rw-r--r--drivers/usb/chipidea/otg.h4
-rw-r--r--drivers/usb/chipidea/otg_fsm.c18
-rw-r--r--drivers/usb/chipidea/otg_fsm.h2
-rw-r--r--drivers/usb/chipidea/udc.c147
-rw-r--r--drivers/usb/chipidea/udc.h13
-rw-r--r--drivers/usb/chipidea/usbmisc_imx.c574
-rw-r--r--drivers/usb/core/hcd.c134
-rw-r--r--drivers/usb/core/hub.c3
-rw-r--r--drivers/usb/core/otg_whitelist.h73
-rw-r--r--drivers/usb/dwc3/core.c111
-rw-r--r--drivers/usb/dwc3/core.h24
-rw-r--r--drivers/usb/dwc3/drd.c48
-rw-r--r--drivers/usb/dwc3/gadget.c6
-rw-r--r--drivers/usb/dwc3/host.c48
-rw-r--r--drivers/usb/gadget/udc/fsl_udc_core.c10
-rw-r--r--drivers/usb/host/ehci-hcd.c6
-rw-r--r--drivers/usb/host/ehci-hub.c139
-rw-r--r--drivers/usb/host/ehci-q.c2
-rw-r--r--drivers/usb/host/xhci-hub.c13
-rw-r--r--drivers/usb/host/xhci-plat.c4
-rw-r--r--drivers/usb/host/xhci-ring.c130
-rw-r--r--drivers/usb/host/xhci.c5
-rw-r--r--drivers/usb/host/xhci.h12
-rw-r--r--drivers/usb/phy/Kconfig2
-rw-r--r--drivers/usb/phy/phy-mxs-usb.c402
-rw-r--r--drivers/usb/phy/phy.c13
-rw-r--r--drivers/usb/typec/mux/Kconfig6
-rw-r--r--drivers/usb/typec/mux/Makefile1
-rw-r--r--drivers/usb/typec/mux/gpio-switch.c117
-rw-r--r--drivers/usb/typec/tcpm/tcpci.c38
-rw-r--r--drivers/usb/typec/tcpm/tcpm.c51
-rw-r--r--include/linux/usb/chipidea.h6
-rw-r--r--include/linux/usb/hcd.h13
-rw-r--r--include/linux/usb/otg-fsm.h1
-rw-r--r--include/linux/usb/phy.h43
72 files changed, 5804 insertions, 2923 deletions
diff --git a/Documentation/ABI/testing/sysfs-platform-cadence-usb3 b/Documentation/ABI/testing/sysfs-platform-cadence-usb3
new file mode 100644
index 000000000000..c969518dcc30
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-platform-cadence-usb3
@@ -0,0 +1,9 @@
+What: /sys/bus/platform/devices/5b110000.usb3/role
+Date: Jan 2019
+Contact: Peter Chen <peter.chen@nxp.com>
+Description:
+ It returns string "gadget", "host" and "none" when read it,
+ it indicates current controller role.
+
+ It will do role switch when write "gadget" or "host" to it.
+ Only controller at dual-role configuration supports writing.
diff --git a/Documentation/ABI/testing/usb-charger-uevent b/Documentation/ABI/testing/usb-charger-uevent
new file mode 100644
index 000000000000..93ffd3a54a7f
--- /dev/null
+++ b/Documentation/ABI/testing/usb-charger-uevent
@@ -0,0 +1,45 @@
+What: Raise a uevent when a USB charger is inserted or removed
+Date: 2019-11-11
+KernelVersion: 5.5
+Contact: linux-usb@vger.kernel.org
+Description: There are two USB charger states:
+ USB_CHARGER_ABSENT
+ USB_CHARGER_PRESENT
+ There are five USB charger types:
+ USB_CHARGER_UNKNOWN_TYPE
+ USB_CHARGER_SDP_TYPE
+ USB_CHARGER_CDP_TYPE
+ USB_CHARGER_DCP_TYPE
+ USB_CHARGER_ACA_TYPE
+
+ Here are two examples taken using udevadm monitor -p when
+ USB charger is online:
+ UDEV [227.425096] change /devices/soc0/usbphynop1 (platform)
+ ACTION=change
+ DEVPATH=/devices/soc0/usbphynop1
+ DRIVER=usb_phy_generic
+ MODALIAS=of:Nusbphynop1T(null)Cusb-nop-xceiv
+ OF_COMPATIBLE_0=usb-nop-xceiv
+ OF_COMPATIBLE_N=1
+ OF_FULLNAME=/usbphynop1
+ OF_NAME=usbphynop1
+ SEQNUM=2493
+ SUBSYSTEM=platform
+ USB_CHARGER_STATE=USB_CHARGER_PRESENT
+ USB_CHARGER_TYPE=USB_CHARGER_SDP_TYPE
+ USEC_INITIALIZED=227422826
+
+ USB charger is offline:
+ KERNEL[229.533933] change /devices/soc0/usbphynop1 (platform)
+ ACTION=change
+ DEVPATH=/devices/soc0/usbphynop1
+ DRIVER=usb_phy_generic
+ MODALIAS=of:Nusbphynop1T(null)Cusb-nop-xceiv
+ OF_COMPATIBLE_0=usb-nop-xceiv
+ OF_COMPATIBLE_N=1
+ OF_FULLNAME=/usbphynop1
+ OF_NAME=usbphynop1
+ SEQNUM=2494
+ SUBSYSTEM=platform
+ USB_CHARGER_STATE=USB_CHARGER_ABSENT
+ USB_CHARGER_TYPE=USB_CHARGER_UNKNOWN_TYPE
diff --git a/Documentation/devicetree/bindings/usb/cdns-usb3.txt b/Documentation/devicetree/bindings/usb/cdns-usb3.txt
index b7dc606d37b5..13daee1b053b 100644
--- a/Documentation/devicetree/bindings/usb/cdns-usb3.txt
+++ b/Documentation/devicetree/bindings/usb/cdns-usb3.txt
@@ -1,45 +1,39 @@
-Binding for the Cadence USBSS-DRD controller
+* Cadence USB3 Controller
Required properties:
- - reg: Physical base address and size of the controller's register areas.
- Controller has 3 different regions:
- - HOST registers area
- - DEVICE registers area
- - OTG/DRD registers area
- - reg-names - register memory area names:
- "xhci" - for HOST registers space
- "dev" - for DEVICE registers space
- "otg" - for OTG/DRD registers space
- - compatible: Should contain: "cdns,usb3"
- - interrupts: Interrupts used by cdns3 controller:
- "host" - interrupt used by XHCI driver.
- "peripheral" - interrupt used by device driver
- "otg" - interrupt used by DRD/OTG part of driver
+- compatible: "Cadence,usb3";
+- reg: base address and length of the registers
+- interrupts: interrupt for the USB controller
+- interrupt-parent: the interrupt parent for this module
+- clocks: reference to the USB clock
+- clock-names: the name of clocks
+- cdns3,usbphy: reference to the USB PHY
Optional properties:
- - maximum-speed : valid arguments are "super-speed", "high-speed" and
- "full-speed"; refer to usb/generic.txt
- - dr_mode: Should be one of "host", "peripheral" or "otg".
- - phys: reference to the USB PHY
- - phy-names: from the *Generic PHY* bindings;
- Supported names are:
- - cdns3,usb2-phy
- - cdns3,usb3-phy
+- dr_mode: One of "host", "peripheral" or "otg". Defaults to "otg"
+- extcon: extcon phandler for cdns3 device
+- power-domains: the power domain for cdns3 controller and phy
- - cdns,on-chip-buff-size : size of memory intended as internal memory for endpoints
- buffers expressed in KB
+Examples:
-Example:
- usb@f3000000 {
- compatible = "cdns,usb3";
- interrupts = <GIC_USB_IRQ 7 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_USB_IRQ 7 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_USB_IRQ 8 IRQ_TYPE_LEVEL_HIGH>;
- interrupt-names = "host", "peripheral", "otg";
- reg = <0xf3000000 0x10000>, /* memory area for HOST registers */
- <0xf3010000 0x10000>, /* memory area for DEVICE registers */
- <0xf3020000 0x10000>; /* memory area for OTG/DRD registers */
- reg-names = "xhci", "dev", "otg";
- phys = <&usb2_phy>, <&usb3_phy>;
- phy-names = "cdns3,usb2-phy", "cnds3,usb3-phy";
- };
+usbotg3: cdns3@5b110000 {
+ compatible = "Cadence,usb3";
+ reg = <0x0 0x5B110000 0x0 0x10000>,
+ <0x0 0x5B130000 0x0 0x10000>,
+ <0x0 0x5B140000 0x0 0x10000>,
+ <0x0 0x5B160000 0x0 0x40000>;
+ interrupt-parent = <&gic>;
+ interrupts = <GIC_SPI 271 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk IMX8QM_USB3_LPM_CLK>,
+ <&clk IMX8QM_USB3_BUS_CLK>,
+ <&clk IMX8QM_USB3_ACLK>,
+ <&clk IMX8QM_USB3_IPG_CLK>,
+ <&clk IMX8QM_USB3_CORE_PCLK>;
+ clock-names = "usb3_lpm_clk", "usb3_bus_clk", "usb3_aclk",
+ "usb3_ipg_clk", "usb3_core_pclk";
+ power-domains = <&pd_conn_usb2>;
+ cdns3,usbphy = <&usbphynop1>;
+ dr_mode = "otg";
+ extcon = <&typec_ptn5150>;
+ status = "disabled";
+};
diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt
index cfc9f40ab641..c2d72c8fe6a0 100644
--- a/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt
+++ b/Documentation/devicetree/bindings/usb/ci-hdrc-usb2.txt
@@ -86,6 +86,7 @@ Optional properties:
case, the "idle" state needs to pull down the data and strobe pin
and the "active" state needs to pull up the strobe pin.
- pinctrl-n: alternate pin modes
+- ci-disable-lpm: Some chipidea hardware need to disable low power mode
i.mx specific properties
- fsl,usbmisc: phandler of non-core register device, with one
diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt
index 66780a47ad85..45043c4c54ae 100644
--- a/Documentation/devicetree/bindings/usb/dwc3.txt
+++ b/Documentation/devicetree/bindings/usb/dwc3.txt
@@ -4,7 +4,15 @@ DWC3- USB3 CONTROLLER. Complies to the generic USB binding properties
as described in 'usb/generic.txt'
Required properties:
- - compatible: must be "snps,dwc3"
+ - compatible: must be "snps,dwc3" and (if applicable) may contain a
+ chip-specific compatible string in front of it to allow dwc3 driver be
+ able to update cache type configuration accordingly, otherwise
+ Layerscape SoC will encounter USB init failure when adding property
+ dma-coherent on device tree.
+ Example:
+ * "fsl,layerscape-dwc3", "snps,dwc3"
+ * "snps,dwc3"
+
- reg : Address and length of the register set for the device
- interrupts: Interrupts used by the dwc3 controller.
- clock-names: should contain "ref", "bus_early", "suspend"
@@ -109,6 +117,10 @@ Optional properties:
more than one value, which means undefined length INCR burst type
enabled. The values can be 1, 4, 8, 16, 32, 64, 128 and 256.
+ - snps,host-vbus-glitches: Power off all Root Hub ports immediately after
+ setting host mode to avoid vbus (negative) glitch happen in later
+ xhci reset. And the vbus will back to 5V automatically when reset done.
+
- in addition all properties from usb-xhci.txt from the current directory are
supported as well
diff --git a/Documentation/devicetree/bindings/usb/typec-switch-gpio.txt b/Documentation/devicetree/bindings/usb/typec-switch-gpio.txt
new file mode 100644
index 000000000000..4ef76cfdf3d5
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/typec-switch-gpio.txt
@@ -0,0 +1,30 @@
+Typec orientation switch via a GPIO
+-----------------------------------
+
+Required properties:
+- compatible: should be set one of following:
+ - "nxp,ptn36043" for NXP Type-C SuperSpeed active switch.
+
+- gpios: the GPIO used to switch the super speed active channel,
+ GPIO_ACTIVE_HIGH: GPIO state high for cc1;
+ GPIO_ACTIVE_LOW: GPIO state low for cc1.
+- orientation-switch: must be present.
+
+Required sub-node:
+- port: specify the remote endpoint of typec switch consumer.
+
+Example:
+
+ptn36043 {
+ compatible = "nxp,ptn36043";
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_ss_sel>;
+ gpios = <&gpio3 15 GPIO_ACTIVE_HIGH>;
+ orientation-switch;
+
+ port {
+ usb3_data_ss: endpoint {
+ remote-endpoint = <&typec_con_ss>;
+ };
+ };
+};
diff --git a/Documentation/devicetree/bindings/usb/usb-xhci.txt b/Documentation/devicetree/bindings/usb/usb-xhci.txt
index b49b819571f9..170c56477c97 100644
--- a/Documentation/devicetree/bindings/usb/usb-xhci.txt
+++ b/Documentation/devicetree/bindings/usb/usb-xhci.txt
@@ -42,6 +42,8 @@ Optional properties:
- quirk-broken-port-ped: set if the controller has broken port disable mechanism
- imod-interval-ns: default interrupt moderation interval is 5000ns
- phys : see usb-hcd.yaml in the current directory
+ - usb3-resume-missing-cas: set if the CAS(Cold Attach Status) may lose in case
+ device plugged in while system sleep.
additionally the properties from usb-hcd.yaml (in the current directory) are
supported.
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 275568abc670..18747e6b86cd 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -125,6 +125,8 @@ source "drivers/usb/chipidea/Kconfig"
source "drivers/usb/isp1760/Kconfig"
+source "drivers/usb/cdns3/Kconfig"
+
comment "USB port drivers"
if USB
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 1c1c1d659394..b3f08b8303f6 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -12,8 +12,7 @@ obj-$(CONFIG_USB_SUPPORT) += phy/
obj-$(CONFIG_USB_DWC3) += dwc3/
obj-$(CONFIG_USB_DWC2) += dwc2/
obj-$(CONFIG_USB_ISP1760) += isp1760/
-
-obj-$(CONFIG_USB_CDNS3) += cdns3/
+obj-$(CONFIG_USB_CDNS3) += cdns3/
obj-$(CONFIG_USB_MON) += mon/
obj-$(CONFIG_USB_MTU3) += mtu3/
diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig
index d0331613a355..165afdb99d15 100644
--- a/drivers/usb/cdns3/Kconfig
+++ b/drivers/usb/cdns3/Kconfig
@@ -1,46 +1,26 @@
config USB_CDNS3
tristate "Cadence USB3 Dual-Role Controller"
- depends on USB_SUPPORT && (USB || USB_GADGET) && HAS_DMA
- select USB_XHCI_PLATFORM if USB_XHCI_HCD
- select USB_ROLE_SWITCH
+ depends on ((USB_XHCI_HCD && USB_GADGET) || (USB_XHCI_HCD && !USB_GADGET) || (!USB_XHCI_HCD && USB_GADGET)) && HAS_DMA
+ select EXTCON
help
- Say Y here if your system has a Cadence USB3 dual-role controller.
- It supports: dual-role switch, Host-only, and Peripheral-only.
+ Say Y here if your system has a cadence USB3 dual-role controller.
+ It supports: dual-role switch Host-only, and Peripheral-only.
- If you choose to build this driver is a dynamically linked
- as module, the module will be called cdns3.ko.
+ When compiled dynamically, the module will be called cdns3.ko.
if USB_CDNS3
config USB_CDNS3_GADGET
bool "Cadence USB3 device controller"
- depends on USB_GADGET=y || USB_GADGET=USB_CDNS3
+ depends on USB_GADGET
help
Say Y here to enable device controller functionality of the
- Cadence USBSS-DEV driver.
-
- This controller supports FF, HS and SS mode. It doesn't support
- LS and SSP mode.
+ cadence usb3 driver.
config USB_CDNS3_HOST
bool "Cadence USB3 host controller"
- depends on USB=y || USB=USB_CDNS3
+ depends on USB_XHCI_HCD
help
Say Y here to enable host controller functionality of the
- Cadence driver.
-
- Host controller is compliant with XHCI so it will use
- standard XHCI driver.
-
-config USB_CDNS3_PCI_WRAP
- tristate "Cadence USB3 support on PCIe-based platforms"
- depends on USB_PCI && ACPI
- default USB_CDNS3
- help
- If you're using the USBSS Core IP with a PCIe, please say
- 'Y' or 'M' here.
-
- If you choose to build this driver as module it will
- be dynamically linked and module will be called cdns3-pci.ko
-
+ cadence usb3 driver.
endif
diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile
index a703547350bb..0c3813da2d1a 100644
--- a/drivers/usb/cdns3/Makefile
+++ b/drivers/usb/cdns3/Makefile
@@ -1,16 +1,8 @@
-# SPDX-License-Identifier: GPL-2.0
# define_trace.h needs to know how to find our header
-CFLAGS_trace.o := -I$(src)
+CFLAGS_trace.o := -I$(src)
-cdns3-y := core.o drd.o
+obj-$(CONFIG_USB_CDNS3) += cdns3.o
-obj-$(CONFIG_USB_CDNS3) += cdns3.o
-cdns3-$(CONFIG_USB_CDNS3_GADGET) += gadget.o ep0.o
-
-ifneq ($(CONFIG_USB_CDNS3_GADGET),)
-cdns3-$(CONFIG_TRACING) += trace.o
-endif
-
-cdns3-$(CONFIG_USB_CDNS3_HOST) += host.o
-
-obj-$(CONFIG_USB_CDNS3_PCI_WRAP) += cdns3-pci-wrap.o
+cdns3-y := core.o
+cdns3-$(CONFIG_USB_CDNS3_GADGET) += gadget.o ep0.o trace.o
+cdns3-$(CONFIG_USB_CDNS3_HOST) += host.o
diff --git a/drivers/usb/cdns3/cdns3-nxp-reg-def.h b/drivers/usb/cdns3/cdns3-nxp-reg-def.h
new file mode 100644
index 000000000000..4ac73005c023
--- /dev/null
+++ b/drivers/usb/cdns3/cdns3-nxp-reg-def.h
@@ -0,0 +1,174 @@
+/**
+ * cdns3-nxp-reg-def.h - nxp wrap layer register definition
+ *
+ * Copyright 2017 NXP
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __DRIVERS_USB_CDNS3_NXP_H
+#define __DRIVERS_USB_CDNS3_NXP_H
+
+#define USB3_CORE_CTRL1 0x00
+#define USB3_CORE_CTRL2 0x04
+#define USB3_INT_REG 0x08
+#define USB3_CORE_STATUS 0x0c
+#define XHCI_DEBUG_LINK_ST 0x10
+#define XHCI_DEBUG_BUS 0x14
+#define USB3_SSPHY_CTRL1 0x40
+#define USB3_SSPHY_CTRL2 0x44
+#define USB3_SSPHY_STATUS 0x4c
+#define USB2_PHY_CTRL1 0x50
+#define USB2_PHY_CTRL2 0x54
+#define USB2_PHY_STATUS 0x5c
+
+/* Register bits definition */
+
+/* USB3_CORE_CTRL1 */
+#define SW_RESET_MASK (0x3f << 26)
+#define PWR_SW_RESET (1 << 31)
+#define APB_SW_RESET (1 << 30)
+#define AXI_SW_RESET (1 << 29)
+#define RW_SW_RESET (1 << 28)
+#define PHY_SW_RESET (1 << 27)
+#define PHYAHB_SW_RESET (1 << 26)
+#define ALL_SW_RESET (PWR_SW_RESET | APB_SW_RESET | AXI_SW_RESET | \
+ RW_SW_RESET | PHY_SW_RESET | PHYAHB_SW_RESET)
+#define OC_DISABLE (1 << 9)
+#define MDCTRL_CLK_SEL (1 << 7)
+#define MODE_STRAP_MASK (0x7)
+#define DEV_MODE (1 << 2)
+#define HOST_MODE (1 << 1)
+#define OTG_MODE (1 << 0)
+
+/* USB3_INT_REG */
+#define CLK_125_REQ (1 << 29)
+#define LPM_CLK_REQ (1 << 28)
+#define DEVU3_WAEKUP_EN (1 << 14)
+#define OTG_WAKEUP_EN (1 << 12)
+#define DEV_INT_EN (3 << 8) /* DEV INT b9:8 */
+#define HOST_INT1_EN (1 << 0) /* HOST INT b7:0 */
+
+/* USB3_CORE_STATUS */
+#define MDCTRL_CLK_STATUS (1 << 15)
+#define DEV_POWER_ON_READY (1 << 13)
+#define HOST_POWER_ON_READY (1 << 12)
+
+/* USB3_SSPHY_STATUS */
+#define PHY_REFCLK_REQ (1 << 0)
+
+
+/* PHY register definition */
+#define PHY_PMA_CMN_CTRL1 (0xC800 * 4)
+#define TB_ADDR_CMN_DIAG_HSCLK_SEL (0x01e0 * 4)
+#define TB_ADDR_CMN_PLL0_VCOCAL_INIT_TMR (0x0084 * 4)
+#define TB_ADDR_CMN_PLL0_VCOCAL_ITER_TMR (0x0085 * 4)
+#define TB_ADDR_CMN_PLL0_INTDIV (0x0094 * 4)
+#define TB_ADDR_CMN_PLL0_FRACDIV (0x0095 * 4)
+#define TB_ADDR_CMN_PLL0_HIGH_THR (0x0096 * 4)
+#define TB_ADDR_CMN_PLL0_SS_CTRL1 (0x0098 * 4)
+#define TB_ADDR_CMN_PLL0_SS_CTRL2 (0x0099 * 4)
+#define TB_ADDR_CMN_PLL0_DSM_DIAG (0x0097 * 4)
+#define TB_ADDR_CMN_DIAG_PLL0_OVRD (0x01c2 * 4)
+#define TB_ADDR_CMN_DIAG_PLL0_FBH_OVRD (0x01c0 * 4)
+#define TB_ADDR_CMN_DIAG_PLL0_FBL_OVRD (0x01c1 * 4)
+#define TB_ADDR_CMN_DIAG_PLL0_V2I_TUNE (0x01C5 * 4)
+#define TB_ADDR_CMN_DIAG_PLL0_CP_TUNE (0x01C6 * 4)
+#define TB_ADDR_CMN_DIAG_PLL0_LF_PROG (0x01C7 * 4)
+#define TB_ADDR_CMN_DIAG_PLL0_TEST_MODE (0x01c4 * 4)
+#define TB_ADDR_CMN_PSM_CLK_CTRL (0x0061 * 4)
+#define TB_ADDR_XCVR_DIAG_RX_LANE_CAL_RST_TMR (0x40ea * 4)
+#define TB_ADDR_XCVR_PSM_RCTRL (0x4001 * 4)
+#define TB_ADDR_TX_PSC_A0 (0x4100 * 4)
+#define TB_ADDR_TX_PSC_A1 (0x4101 * 4)
+#define TB_ADDR_TX_PSC_A2 (0x4102 * 4)
+#define TB_ADDR_TX_PSC_A3 (0x4103 * 4)
+#define TB_ADDR_TX_DIAG_ECTRL_OVRD (0x41f5 * 4)
+#define TB_ADDR_TX_PSC_CAL (0x4106 * 4)
+#define TB_ADDR_TX_PSC_RDY (0x4107 * 4)
+#define TB_ADDR_RX_PSC_A0 (0x8000 * 4)
+#define TB_ADDR_RX_PSC_A1 (0x8001 * 4)
+#define TB_ADDR_RX_PSC_A2 (0x8002 * 4)
+#define TB_ADDR_RX_PSC_A3 (0x8003 * 4)
+#define TB_ADDR_RX_PSC_CAL (0x8006 * 4)
+#define TB_ADDR_RX_PSC_RDY (0x8007 * 4)
+#define TB_ADDR_TX_TXCC_MGNLS_MULT_000 (0x4058 * 4)
+#define TB_ADDR_TX_DIAG_BGREF_PREDRV_DELAY (0x41e7 * 4)
+#define TB_ADDR_RX_SLC_CU_ITER_TMR (0x80e3 * 4)
+#define TB_ADDR_RX_SIGDET_HL_FILT_TMR (0x8090 * 4)
+#define TB_ADDR_RX_SAMP_DAC_CTRL (0x8058 * 4)
+#define TB_ADDR_RX_DIAG_SIGDET_TUNE (0x81dc * 4)
+#define TB_ADDR_RX_DIAG_LFPSDET_TUNE2 (0x81df * 4)
+#define TB_ADDR_RX_DIAG_BS_TM (0x81f5 * 4)
+#define TB_ADDR_RX_DIAG_DFE_CTRL1 (0x81d3 * 4)
+#define TB_ADDR_RX_DIAG_ILL_IQE_TRIM4 (0x81c7 * 4)
+#define TB_ADDR_RX_DIAG_ILL_E_TRIM0 (0x81c2 * 4)
+#define TB_ADDR_RX_DIAG_ILL_IQ_TRIM0 (0x81c1 * 4)
+#define TB_ADDR_RX_DIAG_ILL_IQE_TRIM6 (0x81c9 * 4)
+#define TB_ADDR_RX_DIAG_RXFE_TM3 (0x81f8 * 4)
+#define TB_ADDR_RX_DIAG_RXFE_TM4 (0x81f9 * 4)
+#define TB_ADDR_RX_DIAG_LFPSDET_TUNE (0x81dd * 4)
+#define TB_ADDR_RX_DIAG_DFE_CTRL3 (0x81d5 * 4)
+#define TB_ADDR_RX_DIAG_SC2C_DELAY (0x81e1 * 4)
+#define TB_ADDR_RX_REE_VGA_GAIN_NODFE (0x81bf * 4)
+#define TB_ADDR_XCVR_PSM_CAL_TMR (0x4002 * 4)
+#define TB_ADDR_XCVR_PSM_A0BYP_TMR (0x4004 * 4)
+#define TB_ADDR_XCVR_PSM_A0IN_TMR (0x4003 * 4)
+#define TB_ADDR_XCVR_PSM_A1IN_TMR (0x4005 * 4)
+#define TB_ADDR_XCVR_PSM_A2IN_TMR (0x4006 * 4)
+#define TB_ADDR_XCVR_PSM_A3IN_TMR (0x4007 * 4)
+#define TB_ADDR_XCVR_PSM_A4IN_TMR (0x4008 * 4)
+#define TB_ADDR_XCVR_PSM_A5IN_TMR (0x4009 * 4)
+#define TB_ADDR_XCVR_PSM_A0OUT_TMR (0x400a * 4)
+#define TB_ADDR_XCVR_PSM_A1OUT_TMR (0x400b * 4)
+#define TB_ADDR_XCVR_PSM_A2OUT_TMR (0x400c * 4)
+#define TB_ADDR_XCVR_PSM_A3OUT_TMR (0x400d * 4)
+#define TB_ADDR_XCVR_PSM_A4OUT_TMR (0x400e * 4)
+#define TB_ADDR_XCVR_PSM_A5OUT_TMR (0x400f * 4)
+#define TB_ADDR_TX_RCVDET_EN_TMR (0x4122 * 4)
+#define TB_ADDR_TX_RCVDET_ST_TMR (0x4123 * 4)
+#define TB_ADDR_XCVR_DIAG_LANE_FCM_EN_MGN_TMR (0x40f2 * 4)
+#define TB_ADDR_TX_RCVDETSC_CTRL (0x4124 * 4)
+
+/* Register bits definition */
+
+/* TB_ADDR_TX_RCVDETSC_CTRL */
+#define RXDET_IN_P3_32KHZ (1 << 0)
+
+/* OTG registers definition */
+#define OTGSTS 0x4
+#define OTGREFCLK 0xc
+
+/* Register bits definition */
+/* OTGSTS */
+#define OTG_NRDY (1 << 11)
+/* OTGREFCLK */
+#define OTG_STB_CLK_SWITCH_EN (1 << 31)
+
+/* xHCI registers definition */
+#define XECP_PORT_CAP_REG 0x8000
+#define XECP_PM_PMCSR 0x8018
+#define XECP_AUX_CTRL_REG1 0x8120
+
+/* Register bits definition */
+/* XECP_PORT_CAP_REG */
+#define LPM_2_STB_SWITCH_EN (1 << 25)
+
+/* XECP_AUX_CTRL_REG1 */
+#define CFG_RXDET_P3_EN (1 << 15)
+
+/* XECP_PM_PMCSR */
+#define PS_MASK (3 << 0)
+#define PS_D0 0
+#define PS_D1 (1 << 0)
+#endif /* __DRIVERS_USB_CDNS3_NXP_H */
diff --git a/drivers/usb/cdns3/cdns3-pci-wrap.c b/drivers/usb/cdns3/cdns3-pci-wrap.c
deleted file mode 100644
index b0a29efe7d31..000000000000
--- a/drivers/usb/cdns3/cdns3-pci-wrap.c
+++ /dev/null
@@ -1,204 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Cadence USBSS PCI Glue driver
- *
- * Copyright (C) 2018-2019 Cadence.
- *
- * Author: Pawel Laszczak <pawell@cadence.com>
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/platform_device.h>
-#include <linux/dma-mapping.h>
-#include <linux/slab.h>
-
-struct cdns3_wrap {
- struct platform_device *plat_dev;
- struct resource dev_res[6];
- int devfn;
-};
-
-#define RES_IRQ_HOST_ID 0
-#define RES_IRQ_PERIPHERAL_ID 1
-#define RES_IRQ_OTG_ID 2
-#define RES_HOST_ID 3
-#define RES_DEV_ID 4
-#define RES_DRD_ID 5
-
-#define PCI_BAR_HOST 0
-#define PCI_BAR_DEV 2
-#define PCI_BAR_OTG 0
-
-#define PCI_DEV_FN_HOST_DEVICE 0
-#define PCI_DEV_FN_OTG 1
-
-#define PCI_DRIVER_NAME "cdns3-pci-usbss"
-#define PLAT_DRIVER_NAME "cdns-usb3"
-
-#define CDNS_VENDOR_ID 0x17cd
-#define CDNS_DEVICE_ID 0x0100
-
-static struct pci_dev *cdns3_get_second_fun(struct pci_dev *pdev)
-{
- struct pci_dev *func;
-
- /*
- * Gets the second function.
- * It's little tricky, but this platform has two function.
- * The fist keeps resources for Host/Device while the second
- * keeps resources for DRD/OTG.
- */
- func = pci_get_device(pdev->vendor, pdev->device, NULL);
- if (unlikely(!func))
- return NULL;
-
- if (func->devfn == pdev->devfn) {
- func = pci_get_device(pdev->vendor, pdev->device, func);
- if (unlikely(!func))
- return NULL;
- }
-
- return func;
-}
-
-static int cdns3_pci_probe(struct pci_dev *pdev,
- const struct pci_device_id *id)
-{
- struct platform_device_info plat_info;
- struct cdns3_wrap *wrap;
- struct resource *res;
- struct pci_dev *func;
- int err;
-
- /*
- * for GADGET/HOST PCI (devfn) function number is 0,
- * for OTG PCI (devfn) function number is 1
- */
- if (!id || (pdev->devfn != PCI_DEV_FN_HOST_DEVICE &&
- pdev->devfn != PCI_DEV_FN_OTG))
- return -EINVAL;
-
- func = cdns3_get_second_fun(pdev);
- if (unlikely(!func))
- return -EINVAL;
-
- err = pcim_enable_device(pdev);
- if (err) {
- dev_err(&pdev->dev, "Enabling PCI device has failed %d\n", err);
- return err;
- }
-
- pci_set_master(pdev);
-
- if (pci_is_enabled(func)) {
- wrap = pci_get_drvdata(func);
- } else {
- wrap = kzalloc(sizeof(*wrap), GFP_KERNEL);
- if (!wrap) {
- pci_disable_device(pdev);
- return -ENOMEM;
- }
- }
-
- res = wrap->dev_res;
-
- if (pdev->devfn == PCI_DEV_FN_HOST_DEVICE) {
- /* function 0: host(BAR_0) + device(BAR_1).*/
- dev_dbg(&pdev->dev, "Initialize Device resources\n");
- res[RES_DEV_ID].start = pci_resource_start(pdev, PCI_BAR_DEV);
- res[RES_DEV_ID].end = pci_resource_end(pdev, PCI_BAR_DEV);
- res[RES_DEV_ID].name = "dev";
- res[RES_DEV_ID].flags = IORESOURCE_MEM;
- dev_dbg(&pdev->dev, "USBSS-DEV physical base addr: %pa\n",
- &res[RES_DEV_ID].start);
-
- res[RES_HOST_ID].start = pci_resource_start(pdev, PCI_BAR_HOST);
- res[RES_HOST_ID].end = pci_resource_end(pdev, PCI_BAR_HOST);
- res[RES_HOST_ID].name = "xhci";
- res[RES_HOST_ID].flags = IORESOURCE_MEM;
- dev_dbg(&pdev->dev, "USBSS-XHCI physical base addr: %pa\n",
- &res[RES_HOST_ID].start);
-
- /* Interrupt for XHCI */
- wrap->dev_res[RES_IRQ_HOST_ID].start = pdev->irq;
- wrap->dev_res[RES_IRQ_HOST_ID].name = "host";
- wrap->dev_res[RES_IRQ_HOST_ID].flags = IORESOURCE_IRQ;
-
- /* Interrupt device. It's the same as for HOST. */
- wrap->dev_res[RES_IRQ_PERIPHERAL_ID].start = pdev->irq;
- wrap->dev_res[RES_IRQ_PERIPHERAL_ID].name = "peripheral";
- wrap->dev_res[RES_IRQ_PERIPHERAL_ID].flags = IORESOURCE_IRQ;
- } else {
- res[RES_DRD_ID].start = pci_resource_start(pdev, PCI_BAR_OTG);
- res[RES_DRD_ID].end = pci_resource_end(pdev, PCI_BAR_OTG);
- res[RES_DRD_ID].name = "otg";
- res[RES_DRD_ID].flags = IORESOURCE_MEM;
- dev_dbg(&pdev->dev, "USBSS-DRD physical base addr: %pa\n",
- &res[RES_DRD_ID].start);
-
- /* Interrupt for OTG/DRD. */
- wrap->dev_res[RES_IRQ_OTG_ID].start = pdev->irq;
- wrap->dev_res[RES_IRQ_OTG_ID].name = "otg";
- wrap->dev_res[RES_IRQ_OTG_ID].flags = IORESOURCE_IRQ;
- }
-
- if (pci_is_enabled(func)) {
- /* set up platform device info */
- memset(&plat_info, 0, sizeof(plat_info));
- plat_info.parent = &pdev->dev;
- plat_info.fwnode = pdev->dev.fwnode;
- plat_info.name = PLAT_DRIVER_NAME;
- plat_info.id = pdev->devfn;
- wrap->devfn = pdev->devfn;
- plat_info.res = wrap->dev_res;
- plat_info.num_res = ARRAY_SIZE(wrap->dev_res);
- plat_info.dma_mask = pdev->dma_mask;
- /* register platform device */
- wrap->plat_dev = platform_device_register_full(&plat_info);
- if (IS_ERR(wrap->plat_dev)) {
- pci_disable_device(pdev);
- err = PTR_ERR(wrap->plat_dev);
- kfree(wrap);
- return err;
- }
- }
-
- pci_set_drvdata(pdev, wrap);
- return err;
-}
-
-static void cdns3_pci_remove(struct pci_dev *pdev)
-{
- struct cdns3_wrap *wrap;
- struct pci_dev *func;
-
- func = cdns3_get_second_fun(pdev);
-
- wrap = (struct cdns3_wrap *)pci_get_drvdata(pdev);
- if (wrap->devfn == pdev->devfn)
- platform_device_unregister(wrap->plat_dev);
-
- if (!pci_is_enabled(func))
- kfree(wrap);
-}
-
-static const struct pci_device_id cdns3_pci_ids[] = {
- { PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), },
- { 0, }
-};
-
-static struct pci_driver cdns3_pci_driver = {
- .name = PCI_DRIVER_NAME,
- .id_table = cdns3_pci_ids,
- .probe = cdns3_pci_probe,
- .remove = cdns3_pci_remove,
-};
-
-module_pci_driver(cdns3_pci_driver);
-MODULE_DEVICE_TABLE(pci, cdns3_pci_ids);
-
-MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("Cadence USBSS PCI wrapperr");
diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c
index c2123ef8d8a3..c201d00ac194 100644
--- a/drivers/usb/cdns3/core.c
+++ b/drivers/usb/cdns3/core.c
@@ -1,89 +1,289 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Cadence USBSS DRD Driver.
+/**
+ * core.c - Cadence USB3 DRD Controller Core file
+ *
+ * Copyright 2017-2019 NXP
+ *
+ * Authors: Peter Chen <peter.chen@nxp.com>
*
- * Copyright (C) 2018-2019 Cadence.
- * Copyright (C) 2017-2018 NXP
- * Copyright (C) 2019 Texas Instruments
+ * 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.
*
- * Author: Peter Chen <peter.chen@nxp.com>
- * Pawel Laszczak <pawell@cadence.com>
- * Roger Quadros <rogerq@ti.com>
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/usb/of.h>
+#include <linux/usb/phy.h>
+#include <linux/extcon.h>
#include <linux/pm_runtime.h>
-#include "gadget.h"
+#include "cdns3-nxp-reg-def.h"
#include "core.h"
#include "host-export.h"
#include "gadget-export.h"
-#include "drd.h"
-
-static int cdns3_idle_init(struct cdns3 *cdns);
-static inline
-struct cdns3_role_driver *cdns3_get_current_role_driver(struct cdns3 *cdns)
+/**
+ * cdns3_handshake - spin reading until handshake completes or fails
+ * @ptr: address of device controller register to be read
+ * @mask: bits to look at in result of read
+ * @done: value of those bits when handshake succeeds
+ * @usec: timeout in microseconds
+ *
+ * Returns negative errno, or zero on success
+ *
+ * Success happens when the "mask" bits have the specified value (hardware
+ * handshake done). There are two failure modes: "usec" have passed (major
+ * hardware flakeout), or the register reads as all-ones (hardware removed).
+ */
+int cdns3_handshake(void __iomem *ptr, u32 mask, u32 done, int usec)
{
- WARN_ON(!cdns->roles[cdns->role]);
- return cdns->roles[cdns->role];
-}
+ u32 result;
-static int cdns3_role_start(struct cdns3 *cdns, enum usb_role role)
-{
- int ret;
+ do {
+ result = readl(ptr);
+ if (result == ~(u32)0) /* card removed */
+ return -ENODEV;
- if (WARN_ON(role > USB_ROLE_DEVICE))
- return 0;
-
- mutex_lock(&cdns->mutex);
- cdns->role = role;
- mutex_unlock(&cdns->mutex);
-
- if (!cdns->roles[role])
- return -ENXIO;
+ result &= mask;
+ if (result == done)
+ return 0;
- if (cdns->roles[role]->state == CDNS3_ROLE_STATE_ACTIVE)
- return 0;
+ udelay(1);
+ usec--;
+ } while (usec > 0);
- mutex_lock(&cdns->mutex);
- ret = cdns->roles[role]->start(cdns);
- if (!ret)
- cdns->roles[role]->state = CDNS3_ROLE_STATE_ACTIVE;
- mutex_unlock(&cdns->mutex);
+ return -ETIMEDOUT;
+}
- return ret;
+static void cdns3_usb_phy_init(void __iomem *regs)
+{
+ u32 value;
+
+ pr_debug("begin of %s\n", __func__);
+
+ writel(0x0830, regs + PHY_PMA_CMN_CTRL1);
+ writel(0x10, regs + TB_ADDR_CMN_DIAG_HSCLK_SEL);
+ writel(0x00F0, regs + TB_ADDR_CMN_PLL0_VCOCAL_INIT_TMR);
+ writel(0x0018, regs + TB_ADDR_CMN_PLL0_VCOCAL_ITER_TMR);
+ writel(0x00D0, regs + TB_ADDR_CMN_PLL0_INTDIV);
+ writel(0x4aaa, regs + TB_ADDR_CMN_PLL0_FRACDIV);
+ writel(0x0034, regs + TB_ADDR_CMN_PLL0_HIGH_THR);
+ writel(0x1ee, regs + TB_ADDR_CMN_PLL0_SS_CTRL1);
+ writel(0x7F03, regs + TB_ADDR_CMN_PLL0_SS_CTRL2);
+ writel(0x0020, regs + TB_ADDR_CMN_PLL0_DSM_DIAG);
+ writel(0x0000, regs + TB_ADDR_CMN_DIAG_PLL0_OVRD);
+ writel(0x0000, regs + TB_ADDR_CMN_DIAG_PLL0_FBH_OVRD);
+ writel(0x0000, regs + TB_ADDR_CMN_DIAG_PLL0_FBL_OVRD);
+ writel(0x0007, regs + TB_ADDR_CMN_DIAG_PLL0_V2I_TUNE);
+ writel(0x0027, regs + TB_ADDR_CMN_DIAG_PLL0_CP_TUNE);
+ writel(0x0008, regs + TB_ADDR_CMN_DIAG_PLL0_LF_PROG);
+ writel(0x0022, regs + TB_ADDR_CMN_DIAG_PLL0_TEST_MODE);
+ writel(0x000a, regs + TB_ADDR_CMN_PSM_CLK_CTRL);
+ writel(0x139, regs + TB_ADDR_XCVR_DIAG_RX_LANE_CAL_RST_TMR);
+ writel(0xbefc, regs + TB_ADDR_XCVR_PSM_RCTRL);
+
+ writel(0x7799, regs + TB_ADDR_TX_PSC_A0);
+ writel(0x7798, regs + TB_ADDR_TX_PSC_A1);
+ writel(0x509b, regs + TB_ADDR_TX_PSC_A2);
+ writel(0x3, regs + TB_ADDR_TX_DIAG_ECTRL_OVRD);
+ writel(0x509b, regs + TB_ADDR_TX_PSC_A3);
+ writel(0x2090, regs + TB_ADDR_TX_PSC_CAL);
+ writel(0x2090, regs + TB_ADDR_TX_PSC_RDY);
+
+ writel(0xA6FD, regs + TB_ADDR_RX_PSC_A0);
+ writel(0xA6FD, regs + TB_ADDR_RX_PSC_A1);
+ writel(0xA410, regs + TB_ADDR_RX_PSC_A2);
+ writel(0x2410, regs + TB_ADDR_RX_PSC_A3);
+
+ writel(0x23FF, regs + TB_ADDR_RX_PSC_CAL);
+ writel(0x2010, regs + TB_ADDR_RX_PSC_RDY);
+
+ writel(0x0020, regs + TB_ADDR_TX_TXCC_MGNLS_MULT_000);
+ writel(0x00ff, regs + TB_ADDR_TX_DIAG_BGREF_PREDRV_DELAY);
+ writel(0x0002, regs + TB_ADDR_RX_SLC_CU_ITER_TMR);
+ writel(0x0013, regs + TB_ADDR_RX_SIGDET_HL_FILT_TMR);
+ writel(0x0000, regs + TB_ADDR_RX_SAMP_DAC_CTRL);
+ writel(0x1004, regs + TB_ADDR_RX_DIAG_SIGDET_TUNE);
+ writel(0x4041, regs + TB_ADDR_RX_DIAG_LFPSDET_TUNE2);
+ writel(0x0480, regs + TB_ADDR_RX_DIAG_BS_TM);
+ writel(0x8006, regs + TB_ADDR_RX_DIAG_DFE_CTRL1);
+ writel(0x003f, regs + TB_ADDR_RX_DIAG_ILL_IQE_TRIM4);
+ writel(0x543f, regs + TB_ADDR_RX_DIAG_ILL_E_TRIM0);
+ writel(0x543f, regs + TB_ADDR_RX_DIAG_ILL_IQ_TRIM0);
+ writel(0x0000, regs + TB_ADDR_RX_DIAG_ILL_IQE_TRIM6);
+ writel(0x8000, regs + TB_ADDR_RX_DIAG_RXFE_TM3);
+ writel(0x0003, regs + TB_ADDR_RX_DIAG_RXFE_TM4);
+ writel(0x2408, regs + TB_ADDR_RX_DIAG_LFPSDET_TUNE);
+ writel(0x05ca, regs + TB_ADDR_RX_DIAG_DFE_CTRL3);
+ writel(0x0258, regs + TB_ADDR_RX_DIAG_SC2C_DELAY);
+ writel(0x1fff, regs + TB_ADDR_RX_REE_VGA_GAIN_NODFE);
+
+ writel(0x02c6, regs + TB_ADDR_XCVR_PSM_CAL_TMR);
+ writel(0x0002, regs + TB_ADDR_XCVR_PSM_A0BYP_TMR);
+ writel(0x02c6, regs + TB_ADDR_XCVR_PSM_A0IN_TMR);
+ writel(0x0010, regs + TB_ADDR_XCVR_PSM_A1IN_TMR);
+ writel(0x0010, regs + TB_ADDR_XCVR_PSM_A2IN_TMR);
+ writel(0x0010, regs + TB_ADDR_XCVR_PSM_A3IN_TMR);
+ writel(0x0010, regs + TB_ADDR_XCVR_PSM_A4IN_TMR);
+ writel(0x0010, regs + TB_ADDR_XCVR_PSM_A5IN_TMR);
+
+ writel(0x0002, regs + TB_ADDR_XCVR_PSM_A0OUT_TMR);
+ writel(0x0002, regs + TB_ADDR_XCVR_PSM_A1OUT_TMR);
+ writel(0x0002, regs + TB_ADDR_XCVR_PSM_A2OUT_TMR);
+ writel(0x0002, regs + TB_ADDR_XCVR_PSM_A3OUT_TMR);
+ writel(0x0002, regs + TB_ADDR_XCVR_PSM_A4OUT_TMR);
+ writel(0x0002, regs + TB_ADDR_XCVR_PSM_A5OUT_TMR);
+
+ /* Change rx detect parameter */
+ writel(0x960, regs + TB_ADDR_TX_RCVDET_EN_TMR);
+ writel(0x01e0, regs + TB_ADDR_TX_RCVDET_ST_TMR);
+ writel(0x0090, regs + TB_ADDR_XCVR_DIAG_LANE_FCM_EN_MGN_TMR);
+
+ /* RXDET_IN_P3_32KHZ, Receiver detect slow clock enable */
+ value = readl(regs + TB_ADDR_TX_RCVDETSC_CTRL);
+ value |= RXDET_IN_P3_32KHZ;
+ writel(value, regs + TB_ADDR_TX_RCVDETSC_CTRL);
+
+ udelay(10);
+
+ pr_debug("end of %s\n", __func__);
}
-static void cdns3_role_stop(struct cdns3 *cdns)
+static void cdns_set_role(struct cdns3 *cdns, enum cdns3_roles role)
{
- enum usb_role role = cdns->role;
+ u32 value;
+ int timeout_us = 100000;
+ void __iomem *xhci_regs = cdns->xhci_regs;
- if (WARN_ON(role > USB_ROLE_DEVICE))
+ if (role == CDNS3_ROLE_END)
return;
- if (cdns->roles[role]->state == CDNS3_ROLE_STATE_INACTIVE)
- return;
+ /* Wait clk value */
+ value = readl(cdns->none_core_regs + USB3_SSPHY_STATUS);
+ writel(value, cdns->none_core_regs + USB3_SSPHY_STATUS);
+ udelay(1);
+ value = readl(cdns->none_core_regs + USB3_SSPHY_STATUS);
+ while ((value & 0xf0000000) != 0xf0000000 && timeout_us-- > 0) {
+ value = readl(cdns->none_core_regs + USB3_SSPHY_STATUS);
+ dev_dbg(cdns->dev, "clkvld:0x%x\n", value);
+ udelay(1);
+ }
+
+ if (timeout_us <= 0)
+ dev_err(cdns->dev, "wait clkvld timeout\n");
+
+ /* Set all Reset bits */
+ value = readl(cdns->none_core_regs + USB3_CORE_CTRL1);
+ value |= ALL_SW_RESET;
+ writel(value, cdns->none_core_regs + USB3_CORE_CTRL1);
+ udelay(1);
+
+ if (role == CDNS3_ROLE_HOST) {
+ value = readl(cdns->none_core_regs + USB3_CORE_CTRL1);
+ value = (value & ~MODE_STRAP_MASK) | HOST_MODE | OC_DISABLE;
+ writel(value, cdns->none_core_regs + USB3_CORE_CTRL1);
+ value &= ~PHYAHB_SW_RESET;
+ writel(value, cdns->none_core_regs + USB3_CORE_CTRL1);
+ mdelay(1);
+ cdns3_usb_phy_init(cdns->phy_regs);
+ /* Force B Session Valid as 1 */
+ writel(0x0060, cdns->phy_regs + 0x380a4);
+ mdelay(1);
+
+ value = readl(cdns->none_core_regs + USB3_INT_REG);
+ value |= HOST_INT1_EN;
+ writel(value, cdns->none_core_regs + USB3_INT_REG);
+
+ value = readl(cdns->none_core_regs + USB3_CORE_CTRL1);
+ value &= ~ALL_SW_RESET;
+ writel(value, cdns->none_core_regs + USB3_CORE_CTRL1);
+
+ dev_dbg(cdns->dev, "wait xhci_power_on_ready\n");
+
+ value = readl(cdns->none_core_regs + USB3_CORE_STATUS);
+ timeout_us = 100000;
+ while (!(value & HOST_POWER_ON_READY) && timeout_us-- > 0) {
+ value = readl(cdns->none_core_regs + USB3_CORE_STATUS);
+ udelay(1);
+ }
- mutex_lock(&cdns->mutex);
- cdns->roles[role]->stop(cdns);
- cdns->roles[role]->state = CDNS3_ROLE_STATE_INACTIVE;
- mutex_unlock(&cdns->mutex);
+ if (timeout_us <= 0)
+ dev_err(cdns->dev, "wait xhci_power_on_ready timeout\n");
+
+ value = readl(xhci_regs + XECP_PORT_CAP_REG);
+ value |= LPM_2_STB_SWITCH_EN;
+ writel(value, xhci_regs + XECP_PORT_CAP_REG);
+
+ mdelay(1);
+
+ dev_dbg(cdns->dev, "switch to host role successfully\n");
+ } else { /* gadget mode */
+ value = readl(cdns->none_core_regs + USB3_CORE_CTRL1);
+ value = (value & ~MODE_STRAP_MASK) | DEV_MODE;
+ writel(value, cdns->none_core_regs + USB3_CORE_CTRL1);
+ value &= ~PHYAHB_SW_RESET;
+ writel(value, cdns->none_core_regs + USB3_CORE_CTRL1);
+
+ cdns3_usb_phy_init(cdns->phy_regs);
+ /* Force B Session Valid as 1 */
+ writel(0x0060, cdns->phy_regs + 0x380a4);
+ value = readl(cdns->none_core_regs + USB3_INT_REG);
+ value |= DEV_INT_EN;
+ writel(value, cdns->none_core_regs + USB3_INT_REG);
+
+ value = readl(cdns->none_core_regs + USB3_CORE_CTRL1);
+ value &= ~ALL_SW_RESET;
+ writel(value, cdns->none_core_regs + USB3_CORE_CTRL1);
+
+ dev_dbg(cdns->dev, "wait gadget_power_on_ready\n");
+
+ value = readl(cdns->none_core_regs + USB3_CORE_STATUS);
+ timeout_us = 100000;
+ while (!(value & DEV_POWER_ON_READY) && timeout_us-- > 0) {
+ value = readl(cdns->none_core_regs + USB3_CORE_STATUS);
+ udelay(1);
+ }
+
+ if (timeout_us <= 0)
+ dev_err(cdns->dev,
+ "wait gadget_power_on_ready timeout\n");
+
+ mdelay(1);
+
+ dev_dbg(cdns->dev, "switch to gadget role successfully\n");
+ }
}
-static void cdns3_exit_roles(struct cdns3 *cdns)
+static enum cdns3_roles cdns3_get_role(struct cdns3 *cdns)
{
- cdns3_role_stop(cdns);
- cdns3_drd_exit(cdns);
+ if (cdns->roles[CDNS3_ROLE_HOST] && cdns->roles[CDNS3_ROLE_GADGET]) {
+ if (extcon_get_state(cdns->extcon, EXTCON_USB_HOST))
+ return CDNS3_ROLE_HOST;
+ else if (extcon_get_state(cdns->extcon, EXTCON_USB))
+ return CDNS3_ROLE_GADGET;
+ else
+ return CDNS3_ROLE_END;
+ } else {
+ return cdns->roles[CDNS3_ROLE_HOST]
+ ? CDNS3_ROLE_HOST
+ : CDNS3_ROLE_GADGET;
+ }
}
-static enum usb_role cdsn3_hw_role_state_machine(struct cdns3 *cdns);
-
/**
* cdns3_core_init_role - initialize role of operation
* @cdns: Pointer to cdns3 structure
@@ -93,340 +293,303 @@ static enum usb_role cdsn3_hw_role_state_machine(struct cdns3 *cdns);
static int cdns3_core_init_role(struct cdns3 *cdns)
{
struct device *dev = cdns->dev;
- enum usb_dr_mode best_dr_mode;
- enum usb_dr_mode dr_mode;
- int ret = 0;
-
- dr_mode = usb_get_dr_mode(dev);
- cdns->role = USB_ROLE_NONE;
-
- /*
- * If driver can't read mode by means of usb_get_dr_mode function then
- * chooses mode according with Kernel configuration. This setting
- * can be restricted later depending on strap pin configuration.
- */
- if (dr_mode == USB_DR_MODE_UNKNOWN) {
- if (IS_ENABLED(CONFIG_USB_CDNS3_HOST) &&
- IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
- dr_mode = USB_DR_MODE_OTG;
- else if (IS_ENABLED(CONFIG_USB_CDNS3_HOST))
- dr_mode = USB_DR_MODE_HOST;
- else if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
- dr_mode = USB_DR_MODE_PERIPHERAL;
- }
-
- /*
- * At this point cdns->dr_mode contains strap configuration.
- * Driver try update this setting considering kernel configuration
- */
- best_dr_mode = cdns->dr_mode;
-
- ret = cdns3_idle_init(cdns);
- if (ret)
- return ret;
-
- if (dr_mode == USB_DR_MODE_OTG) {
- best_dr_mode = cdns->dr_mode;
- } else if (cdns->dr_mode == USB_DR_MODE_OTG) {
- best_dr_mode = dr_mode;
- } else if (cdns->dr_mode != dr_mode) {
- dev_err(dev, "Incorrect DRD configuration\n");
- return -EINVAL;
- }
+ enum usb_dr_mode dr_mode = usb_get_dr_mode(dev);
- dr_mode = best_dr_mode;
+ cdns->role = CDNS3_ROLE_END;
+ if (dr_mode == USB_DR_MODE_UNKNOWN)
+ dr_mode = USB_DR_MODE_OTG;
if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
- ret = cdns3_host_init(cdns);
- if (ret) {
- dev_err(dev, "Host initialization failed with %d\n",
- ret);
- goto err;
- }
+ if (cdns3_host_init(cdns))
+ dev_info(dev, "doesn't support host\n");
}
if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) {
- ret = cdns3_gadget_init(cdns);
- if (ret) {
- dev_err(dev, "Device initialization failed with %d\n",
- ret);
- goto err;
- }
+ if (cdns3_gadget_init(cdns))
+ dev_info(dev, "doesn't support gadget\n");
}
- cdns->dr_mode = dr_mode;
-
- ret = cdns3_drd_update_mode(cdns);
- if (ret)
- goto err;
-
- /* Initialize idle role to start with */
- ret = cdns3_role_start(cdns, USB_ROLE_NONE);
- if (ret)
- goto err;
-
- switch (cdns->dr_mode) {
- case USB_DR_MODE_OTG:
- ret = cdns3_hw_role_switch(cdns);
- if (ret)
- goto err;
- break;
- case USB_DR_MODE_PERIPHERAL:
- ret = cdns3_role_start(cdns, USB_ROLE_DEVICE);
- if (ret)
- goto err;
- break;
- case USB_DR_MODE_HOST:
- ret = cdns3_role_start(cdns, USB_ROLE_HOST);
- if (ret)
- goto err;
- break;
- default:
- ret = -EINVAL;
- goto err;
+ if (!cdns->roles[CDNS3_ROLE_HOST] && !cdns->roles[CDNS3_ROLE_GADGET]) {
+ dev_err(dev, "no supported roles\n");
+ return -ENODEV;
}
- return ret;
-err:
- cdns3_exit_roles(cdns);
- return ret;
+ return 0;
}
/**
- * cdsn3_hw_role_state_machine - role switch state machine based on hw events.
- * @cdns: Pointer to controller structure.
+ * cdns3_irq - interrupt handler for cdns3 core device
+ *
+ * @irq: irq number for cdns3 core device
+ * @data: structure of cdns3
*
- * Returns next role to be entered based on hw events.
+ * Returns IRQ_HANDLED or IRQ_NONE
*/
-static enum usb_role cdsn3_hw_role_state_machine(struct cdns3 *cdns)
+static irqreturn_t cdns3_irq(int irq, void *data)
{
- enum usb_role role;
- int id, vbus;
+ struct cdns3 *cdns = data;
+ irqreturn_t ret = IRQ_NONE;
+
+ if (cdns->in_lpm) {
+ disable_irq_nosync(cdns->irq);
+ cdns->wakeup_int = true;
+ pm_runtime_get(cdns->dev);
+ return IRQ_HANDLED;
+ }
- if (cdns->dr_mode != USB_DR_MODE_OTG)
- goto not_otg;
+ /* Handle device/host interrupt */
+ if (cdns->role != CDNS3_ROLE_END)
+ ret = cdns3_role(cdns)->irq(cdns);
- id = cdns3_get_id(cdns);
- vbus = cdns3_get_vbus(cdns);
+ return ret;
+}
- /*
- * Role change state machine
- * Inputs: ID, VBUS
- * Previous state: cdns->role
- * Next state: role
- */
- role = cdns->role;
+static irqreturn_t cdns3_thread_irq(int irq, void *data)
+{
+ struct cdns3 *cdns = data;
+ irqreturn_t ret = IRQ_NONE;
- switch (role) {
- case USB_ROLE_NONE:
- /*
- * Driver treats USB_ROLE_NONE synonymous to IDLE state from
- * controller specification.
- */
- if (!id)
- role = USB_ROLE_HOST;
- else if (vbus)
- role = USB_ROLE_DEVICE;
- break;
- case USB_ROLE_HOST: /* from HOST, we can only change to NONE */
- if (id)
- role = USB_ROLE_NONE;
- break;
- case USB_ROLE_DEVICE: /* from GADGET, we can only change to NONE*/
- if (!vbus)
- role = USB_ROLE_NONE;
- break;
- }
-
- dev_dbg(cdns->dev, "role %d -> %d\n", cdns->role, role);
-
- return role;
-
-not_otg:
- if (cdns3_is_host(cdns))
- role = USB_ROLE_HOST;
- if (cdns3_is_device(cdns))
- role = USB_ROLE_DEVICE;
-
- return role;
+ /* Handle device/host interrupt */
+ if (cdns->role != CDNS3_ROLE_END && cdns3_role(cdns)->thread_irq)
+ ret = cdns3_role(cdns)->thread_irq(cdns);
+
+ return ret;
}
-static int cdns3_idle_role_start(struct cdns3 *cdns)
+static int cdns3_get_clks(struct device *dev)
{
+ struct cdns3 *cdns = dev_get_drvdata(dev);
+ int ret = 0;
+
+ cdns->cdns3_clks[0] = devm_clk_get(dev, "usb3_lpm_clk");
+ if (IS_ERR(cdns->cdns3_clks[0])) {
+ ret = PTR_ERR(cdns->cdns3_clks[0]);
+ dev_err(dev, "Failed to get usb3_lpm_clk, err=%d\n", ret);
+ return ret;
+ }
+
+ cdns->cdns3_clks[1] = devm_clk_get(dev, "usb3_bus_clk");
+ if (IS_ERR(cdns->cdns3_clks[1])) {
+ ret = PTR_ERR(cdns->cdns3_clks[1]);
+ dev_err(dev, "Failed to get usb3_bus_clk, err=%d\n", ret);
+ return ret;
+ }
+
+ cdns->cdns3_clks[2] = devm_clk_get(dev, "usb3_aclk");
+ if (IS_ERR(cdns->cdns3_clks[2])) {
+ ret = PTR_ERR(cdns->cdns3_clks[2]);
+ dev_err(dev, "Failed to get usb3_aclk, err=%d\n", ret);
+ return ret;
+ }
+
+ cdns->cdns3_clks[3] = devm_clk_get(dev, "usb3_ipg_clk");
+ if (IS_ERR(cdns->cdns3_clks[3])) {
+ ret = PTR_ERR(cdns->cdns3_clks[3]);
+ dev_err(dev, "Failed to get usb3_ipg_clk, err=%d\n", ret);
+ return ret;
+ }
+
+ cdns->cdns3_clks[4] = devm_clk_get(dev, "usb3_core_pclk");
+ if (IS_ERR(cdns->cdns3_clks[4])) {
+ ret = PTR_ERR(cdns->cdns3_clks[4]);
+ dev_err(dev, "Failed to get usb3_core_pclk, err=%d\n", ret);
+ return ret;
+ }
+
return 0;
}
-static void cdns3_idle_role_stop(struct cdns3 *cdns)
+static int cdns3_prepare_enable_clks(struct device *dev)
{
- /* Program Lane swap and bring PHY out of RESET */
- phy_reset(cdns->usb3_phy);
-}
+ struct cdns3 *cdns = dev_get_drvdata(dev);
+ int i, j, ret = 0;
-static int cdns3_idle_init(struct cdns3 *cdns)
-{
- struct cdns3_role_driver *rdrv;
+ for (i = 0; i < CDNS3_NUM_OF_CLKS; i++) {
+ ret = clk_prepare_enable(cdns->cdns3_clks[i]);
+ if (ret) {
+ dev_err(dev,
+ "Failed to prepare/enable cdns3 clk, err=%d\n",
+ ret);
+ goto err;
+ }
+ }
- rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
- if (!rdrv)
- return -ENOMEM;
+ return ret;
+err:
+ for (j = i; j > 0; j--)
+ clk_disable_unprepare(cdns->cdns3_clks[j - 1]);
- rdrv->start = cdns3_idle_role_start;
- rdrv->stop = cdns3_idle_role_stop;
- rdrv->state = CDNS3_ROLE_STATE_INACTIVE;
- rdrv->suspend = NULL;
- rdrv->resume = NULL;
- rdrv->name = "idle";
+ return ret;
+}
- cdns->roles[USB_ROLE_NONE] = rdrv;
+static void cdns3_disable_unprepare_clks(struct device *dev)
+{
+ struct cdns3 *cdns = dev_get_drvdata(dev);
+ int i;
- return 0;
+ for (i = CDNS3_NUM_OF_CLKS - 1; i >= 0; i--)
+ clk_disable_unprepare(cdns->cdns3_clks[i]);
}
-/**
- * cdns3_hw_role_switch - switch roles based on HW state
- * @cdns3: controller
- */
-int cdns3_hw_role_switch(struct cdns3 *cdns)
+static void cdns3_remove_roles(struct cdns3 *cdns)
+{
+ cdns3_gadget_exit(cdns);
+ cdns3_host_remove(cdns);
+}
+
+static int cdns3_do_role_switch(struct cdns3 *cdns, enum cdns3_roles role)
{
- enum usb_role real_role, current_role;
int ret = 0;
+ enum cdns3_roles current_role;
+
+ dev_dbg(cdns->dev, "current role is %d, switch to %d\n",
+ cdns->role, role);
- /* Do nothing if role based on syfs. */
- if (cdns->role_override)
+ if (cdns->role == role)
return 0;
pm_runtime_get_sync(cdns->dev);
-
current_role = cdns->role;
- real_role = cdsn3_hw_role_state_machine(cdns);
-
- /* Do nothing if nothing changed */
- if (current_role == real_role)
- goto exit;
-
cdns3_role_stop(cdns);
+ if (role == CDNS3_ROLE_END) {
+ /* Force B Session Valid as 0 */
+ writel(0x0040, cdns->phy_regs + 0x380a4);
+ pm_runtime_put_sync(cdns->dev);
+ return 0;
+ }
- dev_dbg(cdns->dev, "Switching role %d -> %d", current_role, real_role);
-
- ret = cdns3_role_start(cdns, real_role);
+ cdns_set_role(cdns, role);
+ ret = cdns3_role_start(cdns, role);
if (ret) {
/* Back to current role */
dev_err(cdns->dev, "set %d has failed, back to %d\n",
- real_role, current_role);
+ role, current_role);
+ cdns_set_role(cdns, current_role);
ret = cdns3_role_start(cdns, current_role);
- if (ret)
- dev_err(cdns->dev, "back to %d failed too\n",
- current_role);
}
-exit:
+
pm_runtime_put_sync(cdns->dev);
return ret;
}
/**
- * cdsn3_role_get - get current role of controller.
+ * cdns3_role_switch - work queue handler for role switch
*
- * @dev: Pointer to device structure
+ * @work: work queue item structure
*
- * Returns role
+ * Handles below events:
+ * - Role switch for dual-role devices
+ * - CDNS3_ROLE_GADGET <--> CDNS3_ROLE_END for peripheral-only devices
*/
-static enum usb_role cdns3_role_get(struct device *dev)
+static void cdns3_role_switch(struct work_struct *work)
{
- struct cdns3 *cdns = dev_get_drvdata(dev);
+ struct cdns3 *cdns = container_of(work, struct cdns3,
+ role_switch_wq);
+ bool device, host;
+
+ host = extcon_get_state(cdns->extcon, EXTCON_USB_HOST);
+ device = extcon_get_state(cdns->extcon, EXTCON_USB);
+
+ if (host) {
+ if (cdns->roles[CDNS3_ROLE_HOST])
+ cdns3_do_role_switch(cdns, CDNS3_ROLE_HOST);
+ return;
+ }
- return cdns->role;
+ if (device)
+ cdns3_do_role_switch(cdns, CDNS3_ROLE_GADGET);
+ else
+ cdns3_do_role_switch(cdns, CDNS3_ROLE_END);
}
-/**
- * cdns3_role_set - set current role of controller.
- *
- * @dev: pointer to device object
- * @role - the previous role
- * Handles below events:
- * - Role switch for dual-role devices
- * - USB_ROLE_GADGET <--> USB_ROLE_NONE for peripheral-only devices
- */
-static int cdns3_role_set(struct device *dev, enum usb_role role)
+static int cdns3_extcon_notifier(struct notifier_block *nb, unsigned long event,
+ void *ptr)
{
- struct cdns3 *cdns = dev_get_drvdata(dev);
- int ret = 0;
+ struct cdns3 *cdns = container_of(nb, struct cdns3, extcon_nb);
- pm_runtime_get_sync(cdns->dev);
+ queue_work(system_freezable_wq, &cdns->role_switch_wq);
- /*
- * FIXME: switch role framework should be extended to meet
- * requirements. Driver assumes that role can be controlled
- * by SW or HW. Temporary workaround is to use USB_ROLE_NONE to
- * switch from SW to HW control.
- *
- * For dr_mode == USB_DR_MODE_OTG:
- * if user sets USB_ROLE_HOST or USB_ROLE_DEVICE then driver
- * sets role_override flag and forces that role.
- * if user sets USB_ROLE_NONE, driver clears role_override and lets
- * HW state machine take over.
- *
- * For dr_mode != USB_DR_MODE_OTG:
- * Assumptions:
- * 1. Restricted user control between NONE and dr_mode.
- * 2. Driver doesn't need to rely on role_override flag.
- * 3. Driver needs to ensure that HW state machine is never called
- * if dr_mode != USB_DR_MODE_OTG.
- */
- if (role == USB_ROLE_NONE)
- cdns->role_override = 0;
- else
- cdns->role_override = 1;
+ return NOTIFY_DONE;
+}
- /*
- * HW state might have changed so driver need to trigger
- * HW state machine if dr_mode == USB_DR_MODE_OTG.
- */
- if (!cdns->role_override && cdns->dr_mode == USB_DR_MODE_OTG) {
- cdns3_hw_role_switch(cdns);
- goto pm_put;
- }
+static int cdns3_register_extcon(struct cdns3 *cdns)
+{
+ struct extcon_dev *extcon;
+ struct device *dev = cdns->dev;
+ int ret;
- if (cdns->role == role)
- goto pm_put;
+ if (of_property_read_bool(dev->of_node, "extcon")) {
+ extcon = extcon_get_edev_by_phandle(dev, 0);
+ if (IS_ERR(extcon))
+ return PTR_ERR(extcon);
- if (cdns->dr_mode == USB_DR_MODE_HOST) {
- switch (role) {
- case USB_ROLE_NONE:
- case USB_ROLE_HOST:
- break;
- default:
- ret = -EPERM;
- goto pm_put;
+ ret = devm_extcon_register_notifier(dev, extcon,
+ EXTCON_USB_HOST, &cdns->extcon_nb);
+ if (ret < 0) {
+ dev_err(dev, "register Host Connector failed\n");
+ return ret;
}
- }
- if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL) {
- switch (role) {
- case USB_ROLE_NONE:
- case USB_ROLE_DEVICE:
- break;
- default:
- ret = -EPERM;
- goto pm_put;
+ ret = devm_extcon_register_notifier(dev, extcon,
+ EXTCON_USB, &cdns->extcon_nb);
+ if (ret < 0) {
+ dev_err(dev, "register Device Connector failed\n");
+ return ret;
}
+
+ cdns->extcon = extcon;
+ cdns->extcon_nb.notifier_call = cdns3_extcon_notifier;
}
- cdns3_role_stop(cdns);
- ret = cdns3_role_start(cdns, role);
- if (ret) {
- dev_err(cdns->dev, "set role %d has failed\n", role);
- ret = -EPERM;
+ return 0;
+}
+
+static ssize_t cdns3_role_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cdns3 *cdns = dev_get_drvdata(dev);
+
+ if (cdns->role != CDNS3_ROLE_END)
+ return sprintf(buf, "%s\n", cdns3_role(cdns)->name);
+ else
+ return sprintf(buf, "%s\n", "none");
+}
+
+static ssize_t cdns3_role_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t n)
+{
+ struct cdns3 *cdns = dev_get_drvdata(dev);
+ enum cdns3_roles role;
+ int ret;
+
+ if (!(cdns->roles[CDNS3_ROLE_HOST] && cdns->roles[CDNS3_ROLE_GADGET])) {
+ dev_warn(dev, "Current configuration is not dual-role, quit\n");
+ return -EPERM;
}
-pm_put:
- pm_runtime_put_sync(cdns->dev);
- return ret;
+ for (role = CDNS3_ROLE_HOST; role <= CDNS3_ROLE_GADGET; role++)
+ if (!strncmp(buf, cdns->roles[role]->name,
+ strlen(cdns->roles[role]->name)))
+ break;
+
+ if (role == cdns->role)
+ return -EINVAL;
+
+ disable_irq(cdns->irq);
+ ret = cdns3_do_role_switch(cdns, role);
+ enable_irq(cdns->irq);
+
+ return (ret == 0) ? n : ret;
}
+static DEVICE_ATTR(role, 0644, cdns3_role_show, cdns3_role_store);
+
+static struct attribute *cdns3_attrs[] = {
+ &dev_attr_role.attr,
+ NULL,
+};
-static const struct usb_role_switch_desc cdns3_switch_desc = {
- .set = cdns3_role_set,
- .get = cdns3_role_get,
- .allow_userspace_control = true,
+static const struct attribute_group cdns3_attr_group = {
+ .attrs = cdns3_attrs,
};
/**
@@ -443,111 +606,113 @@ static int cdns3_probe(struct platform_device *pdev)
void __iomem *regs;
int ret;
- ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
- if (ret) {
- dev_err(dev, "error setting dma mask: %d\n", ret);
- return -ENODEV;
- }
-
cdns = devm_kzalloc(dev, sizeof(*cdns), GFP_KERNEL);
if (!cdns)
return -ENOMEM;
cdns->dev = dev;
-
platform_set_drvdata(pdev, cdns);
- res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "host");
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
- dev_err(dev, "missing host IRQ\n");
+ dev_err(dev, "missing IRQ\n");
return -ENODEV;
}
+ cdns->irq = res->start;
- cdns->xhci_res[0] = *res;
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "xhci");
- if (!res) {
- dev_err(dev, "couldn't get xhci resource\n");
- return -ENXIO;
- }
-
- cdns->xhci_res[1] = *res;
-
- cdns->dev_irq = platform_get_irq_byname(pdev, "peripheral");
- if (cdns->dev_irq == -EPROBE_DEFER)
- return cdns->dev_irq;
+ /*
+ * Request memory region
+ * region-0: nxp wrap registers
+ * region-1: xHCI
+ * region-2: Peripheral
+ * region-3: PHY registers
+ * region-4: OTG registers
+ */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+ cdns->none_core_regs = regs;
- if (cdns->dev_irq < 0)
- dev_err(dev, "couldn't get peripheral irq\n");
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+ cdns->xhci_regs = regs;
+ cdns->xhci_res = res;
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dev");
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
regs = devm_ioremap_resource(dev, res);
if (IS_ERR(regs))
return PTR_ERR(regs);
cdns->dev_regs = regs;
- cdns->otg_irq = platform_get_irq_byname(pdev, "otg");
- if (cdns->otg_irq == -EPROBE_DEFER)
- return cdns->otg_irq;
-
- if (cdns->otg_irq < 0) {
- dev_err(dev, "couldn't get otg irq\n");
- return cdns->otg_irq;
- }
-
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otg");
- if (!res) {
- dev_err(dev, "couldn't get otg resource\n");
- return -ENXIO;
- }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 3);
+ regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+ cdns->phy_regs = regs;
- cdns->otg_res = *res;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 4);
+ regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+ cdns->otg_regs = regs;
mutex_init(&cdns->mutex);
+ ret = cdns3_get_clks(dev);
+ if (ret)
+ return ret;
- cdns->usb2_phy = devm_phy_optional_get(dev, "cdns3,usb2-phy");
- if (IS_ERR(cdns->usb2_phy))
- return PTR_ERR(cdns->usb2_phy);
-
- ret = phy_init(cdns->usb2_phy);
+ ret = cdns3_prepare_enable_clks(dev);
if (ret)
return ret;
- cdns->usb3_phy = devm_phy_optional_get(dev, "cdns3,usb3-phy");
- if (IS_ERR(cdns->usb3_phy))
- return PTR_ERR(cdns->usb3_phy);
+ cdns->usbphy = devm_usb_get_phy_by_phandle(dev, "cdns3,usbphy", 0);
+ if (IS_ERR(cdns->usbphy)) {
+ ret = PTR_ERR(cdns->usbphy);
+ if (ret == -ENODEV)
+ ret = -EINVAL;
+ goto err1;
+ }
- ret = phy_init(cdns->usb3_phy);
+ ret = usb_phy_init(cdns->usbphy);
if (ret)
goto err1;
- ret = phy_power_on(cdns->usb2_phy);
+ ret = cdns3_core_init_role(cdns);
if (ret)
goto err2;
- ret = phy_power_on(cdns->usb3_phy);
- if (ret)
- goto err3;
+ if (cdns->roles[CDNS3_ROLE_GADGET]) {
+ INIT_WORK(&cdns->role_switch_wq, cdns3_role_switch);
+ ret = cdns3_register_extcon(cdns);
+ if (ret)
+ goto err3;
+ }
- cdns->role_sw = usb_role_switch_register(dev, &cdns3_switch_desc);
- if (IS_ERR(cdns->role_sw)) {
- ret = PTR_ERR(cdns->role_sw);
- dev_warn(dev, "Unable to register Role Switch\n");
- goto err4;
+ cdns->role = cdns3_get_role(cdns);
+ dev_dbg(dev, "the init role is %d\n", cdns->role);
+ cdns_set_role(cdns, cdns->role);
+ ret = cdns3_role_start(cdns, cdns->role);
+ if (ret) {
+ dev_err(dev, "can't start %s role\n",
+ cdns3_role(cdns)->name);
+ goto err3;
}
- ret = cdns3_drd_init(cdns);
+ ret = devm_request_threaded_irq(dev, cdns->irq, cdns3_irq,
+ cdns3_thread_irq, IRQF_SHARED, dev_name(dev), cdns);
if (ret)
- goto err5;
+ goto err4;
- ret = cdns3_core_init_role(cdns);
+ ret = sysfs_create_group(&dev->kobj, &cdns3_attr_group);
if (ret)
- goto err5;
+ goto err4;
device_set_wakeup_capable(dev, true);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
-
/*
* The controller needs less time between bus and controller suspend,
* and we also needs a small delay to avoid frequently entering low
@@ -559,24 +724,20 @@ static int cdns3_probe(struct platform_device *pdev)
dev_dbg(dev, "Cadence USB3 core: probe succeed\n");
return 0;
-err5:
- cdns3_drd_exit(cdns);
- usb_role_switch_unregister(cdns->role_sw);
-err4:
- phy_power_off(cdns->usb3_phy);
+err4:
+ cdns3_role_stop(cdns);
err3:
- phy_power_off(cdns->usb2_phy);
+ cdns3_remove_roles(cdns);
err2:
- phy_exit(cdns->usb3_phy);
+ usb_phy_shutdown(cdns->usbphy);
err1:
- phy_exit(cdns->usb2_phy);
-
+ cdns3_disable_unprepare_clks(dev);
return ret;
}
/**
- * cdns3_remove - unbind drd driver and clean up
+ * cdns3_remove - unbind our drd driver and clean up
* @pdev: Pointer to Linux platform device
*
* Returns 0 on success otherwise negative errno
@@ -584,75 +745,363 @@ err1:
static int cdns3_remove(struct platform_device *pdev)
{
struct cdns3 *cdns = platform_get_drvdata(pdev);
+ struct device *dev = &pdev->dev;
+
+ pm_runtime_get_sync(dev);
+ pm_runtime_disable(dev);
+ pm_runtime_put_noidle(dev);
+ sysfs_remove_group(&dev->kobj, &cdns3_attr_group);
+ cdns3_remove_roles(cdns);
+ usb_phy_shutdown(cdns->usbphy);
+ cdns3_disable_unprepare_clks(dev);
- pm_runtime_get_sync(&pdev->dev);
- pm_runtime_disable(&pdev->dev);
- pm_runtime_put_noidle(&pdev->dev);
- cdns3_exit_roles(cdns);
- usb_role_switch_unregister(cdns->role_sw);
- phy_power_off(cdns->usb2_phy);
- phy_power_off(cdns->usb3_phy);
- phy_exit(cdns->usb2_phy);
- phy_exit(cdns->usb3_phy);
return 0;
}
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_OF
+static const struct of_device_id of_cdns3_match[] = {
+ { .compatible = "Cadence,usb3" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, of_cdns3_match);
+#endif
+
+#ifdef CONFIG_PM
+static inline bool controller_power_is_lost(struct cdns3 *cdns)
+{
+ u32 value;
+
+ value = readl(cdns->none_core_regs + USB3_CORE_CTRL1);
+ if ((value & SW_RESET_MASK) == ALL_SW_RESET)
+ return true;
+ else
+ return false;
+}
+
+static void cdns3_set_wakeup(void *none_core_regs, bool enable)
+{
+ u32 value;
+
+ if (enable) {
+ /* Enable wakeup and phy_refclk_req */
+ value = readl(none_core_regs + USB3_INT_REG);
+ value |= OTG_WAKEUP_EN | DEVU3_WAEKUP_EN;
+ writel(value, none_core_regs + USB3_INT_REG);
+ } else {
+ /* disable wakeup and phy_refclk_req */
+ value = readl(none_core_regs + USB3_INT_REG);
+ value &= ~(OTG_WAKEUP_EN | DEVU3_WAEKUP_EN);
+ writel(value, none_core_regs + USB3_INT_REG);
+ }
+}
+
+static int cdns3_enter_suspend(struct cdns3 *cdns, bool suspend, bool wakeup)
+{
+ void __iomem *otg_regs = cdns->otg_regs;
+ void __iomem *xhci_regs = cdns->xhci_regs;
+ void __iomem *none_core_regs = cdns->none_core_regs;
+ u32 value;
+ int timeout_us = 100000;
+ int ret = 0;
+
+ if (cdns->role == CDNS3_ROLE_GADGET) {
+ if (suspend) {
+ /* When at device mode, set controller at reset mode */
+ value = readl(cdns->none_core_regs + USB3_CORE_CTRL1);
+ value |= ALL_SW_RESET;
+ writel(value, cdns->none_core_regs + USB3_CORE_CTRL1);
+ }
+ return 0;
+ } else if (cdns->role == CDNS3_ROLE_END) {
+ return 0;
+ }
+
+ if (suspend) {
+ if (cdns3_role(cdns)->suspend)
+ ret = cdns3_role(cdns)->suspend(cdns, wakeup);
+
+ if (ret)
+ return ret;
+
+ /* SW request low power when all usb ports allow to it ??? */
+ value = readl(xhci_regs + XECP_PM_PMCSR);
+ value &= ~PS_MASK;
+ value |= PS_D1;
+ writel(value, xhci_regs + XECP_PM_PMCSR);
+
+ /* mdctrl_clk_sel */
+ value = readl(none_core_regs + USB3_CORE_CTRL1);
+ value |= MDCTRL_CLK_SEL;
+ writel(value, none_core_regs + USB3_CORE_CTRL1);
+
+ /* wait for mdctrl_clk_status */
+ value = readl(none_core_regs + USB3_CORE_STATUS);
+ while (!(value & MDCTRL_CLK_STATUS) && timeout_us-- > 0) {
+ value = readl(none_core_regs + USB3_CORE_STATUS);
+ udelay(1);
+ }
+
+ if (timeout_us <= 0)
+ dev_err(cdns->dev, "wait mdctrl_clk_status timeout\n");
+
+ dev_dbg(cdns->dev, "mdctrl_clk_status is set\n");
+
+ /* wait lpm_clk_req to be 0 */
+ value = readl(none_core_regs + USB3_INT_REG);
+ timeout_us = 100000;
+ while ((value & LPM_CLK_REQ) && timeout_us-- > 0) {
+ value = readl(none_core_regs + USB3_INT_REG);
+ udelay(1);
+ }
+
+ if (timeout_us <= 0)
+ dev_err(cdns->dev, "wait lpm_clk_req timeout\n");
+
+ dev_dbg(cdns->dev, "lpm_clk_req cleared\n");
+
+ /* wait phy_refclk_req to be 0 */
+ value = readl(none_core_regs + USB3_SSPHY_STATUS);
+ timeout_us = 100000;
+ while ((value & PHY_REFCLK_REQ) && timeout_us-- > 0) {
+ value = readl(none_core_regs + USB3_SSPHY_STATUS);
+ udelay(1);
+ }
+ if (timeout_us <= 0)
+ dev_err(cdns->dev, "wait phy_refclk_req timeout\n");
+
+ dev_dbg(cdns->dev, "phy_refclk_req cleared\n");
+ cdns3_set_wakeup(none_core_regs, true);
+ } else {
+ value = readl(none_core_regs + USB3_INT_REG);
+ /* wait CLK_125_REQ to be 1 */
+ value = readl(none_core_regs + USB3_INT_REG);
+ while (!(value & CLK_125_REQ) && timeout_us-- > 0) {
+ value = readl(none_core_regs + USB3_INT_REG);
+ udelay(1);
+ }
+
+ cdns3_set_wakeup(none_core_regs, false);
+
+ /* SW request D0 */
+ value = readl(xhci_regs + XECP_PM_PMCSR);
+ value &= ~PS_MASK;
+ value |= PS_D0;
+ writel(value, xhci_regs + XECP_PM_PMCSR);
+
+ /* clr CFG_RXDET_P3_EN */
+ value = readl(xhci_regs + XECP_AUX_CTRL_REG1);
+ value &= ~CFG_RXDET_P3_EN;
+ writel(value, xhci_regs + XECP_AUX_CTRL_REG1);
+
+ /* clear mdctrl_clk_sel */
+ value = readl(none_core_regs + USB3_CORE_CTRL1);
+ value &= ~MDCTRL_CLK_SEL;
+ writel(value, none_core_regs + USB3_CORE_CTRL1);
+
+ /* wait for mdctrl_clk_status is cleared */
+ value = readl(none_core_regs + USB3_CORE_STATUS);
+ timeout_us = 100000;
+ while ((value & MDCTRL_CLK_STATUS) && timeout_us-- > 0) {
+ value = readl(none_core_regs + USB3_CORE_STATUS);
+ udelay(1);
+ }
+
+ if (timeout_us <= 0)
+ dev_err(cdns->dev, "wait mdctrl_clk_status timeout\n");
+
+ dev_dbg(cdns->dev, "mdctrl_clk_status cleared\n");
+
+ /* Wait until OTG_NRDY is 0 */
+ value = readl(otg_regs + OTGSTS);
+ timeout_us = 100000;
+ while ((value & OTG_NRDY) && timeout_us-- > 0) {
+ value = readl(otg_regs + OTGSTS);
+ udelay(1);
+ }
+
+ if (timeout_us <= 0)
+ dev_err(cdns->dev, "wait OTG ready timeout\n");
+
+ value = readl(none_core_regs + USB3_CORE_STATUS);
+ timeout_us = 100000;
+ while (!(value & HOST_POWER_ON_READY) && timeout_us-- > 0) {
+ value = readl(none_core_regs + USB3_CORE_STATUS);
+ udelay(1);
+ }
+
+ if (timeout_us <= 0)
+ dev_err(cdns->dev, "wait xhci_power_on_ready timeout\n");
+ }
+
+ return ret;
+}
+
+static int cdns3_controller_suspend(struct cdns3 *cdns, bool wakeup)
+{
+ int ret = 0;
+
+ disable_irq(cdns->irq);
+ ret = cdns3_enter_suspend(cdns, true, wakeup);
+ if (ret) {
+ enable_irq(cdns->irq);
+ return ret;
+ }
+
+ usb_phy_set_suspend(cdns->usbphy, 1);
+ cdns->in_lpm = true;
+ enable_irq(cdns->irq);
+ return ret;
+}
+
+#ifdef CONFIG_PM_SLEEP
static int cdns3_suspend(struct device *dev)
{
struct cdns3 *cdns = dev_get_drvdata(dev);
- unsigned long flags;
+ bool wakeup = device_may_wakeup(dev);
+ int ret;
- if (cdns->role == USB_ROLE_HOST)
- return 0;
+ dev_dbg(dev, "at %s\n", __func__);
if (pm_runtime_status_suspended(dev))
pm_runtime_resume(dev);
- if (cdns->roles[cdns->role]->suspend) {
- spin_lock_irqsave(&cdns->gadget_dev->lock, flags);
- cdns->roles[cdns->role]->suspend(cdns, false);
- spin_unlock_irqrestore(&cdns->gadget_dev->lock, flags);
- }
+ ret = cdns3_controller_suspend(cdns, wakeup);
+ if (ret)
+ return ret;
- return 0;
+ cdns3_disable_unprepare_clks(dev);
+ if (wakeup)
+ enable_irq_wake(cdns->irq);
+
+ return ret;
}
static int cdns3_resume(struct device *dev)
{
struct cdns3 *cdns = dev_get_drvdata(dev);
- unsigned long flags;
+ int ret;
+ bool power_lost;
- if (cdns->role == USB_ROLE_HOST)
+ dev_dbg(dev, "at %s\n", __func__);
+ if (!cdns->in_lpm) {
+ WARN_ON(1);
return 0;
+ }
+
+ ret = cdns3_prepare_enable_clks(dev);
+ if (ret)
+ return ret;
- if (cdns->roles[cdns->role]->resume) {
- spin_lock_irqsave(&cdns->gadget_dev->lock, flags);
- cdns->roles[cdns->role]->resume(cdns, false);
- spin_unlock_irqrestore(&cdns->gadget_dev->lock, flags);
+ usb_phy_set_suspend(cdns->usbphy, 0);
+ cdns->in_lpm = false;
+ if (device_may_wakeup(dev))
+ disable_irq_wake(cdns->irq);
+ power_lost = controller_power_is_lost(cdns);
+ if (power_lost) {
+ dev_dbg(dev, "power is lost, the role is %d\n", cdns->role);
+ cdns_set_role(cdns, cdns->role);
+ if ((cdns->role != CDNS3_ROLE_END)
+ && cdns3_role(cdns)->resume) {
+ /* Force B Session Valid as 1 */
+ writel(0x0060, cdns->phy_regs + 0x380a4);
+ cdns3_role(cdns)->resume(cdns, true);
+ }
+ } else {
+ /* At resume path, never return error */
+ cdns3_enter_suspend(cdns, false, false);
+ if (cdns->wakeup_int) {
+ cdns->wakeup_int = false;
+ pm_runtime_mark_last_busy(cdns->dev);
+ pm_runtime_put_autosuspend(cdns->dev);
+ enable_irq(cdns->irq);
+ }
+
+ if ((cdns->role != CDNS3_ROLE_END) && cdns3_role(cdns)->resume)
+ cdns3_role(cdns)->resume(cdns, false);
}
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
+ if (cdns->role == CDNS3_ROLE_HOST) {
+ /*
+ * There is no PM APIs for cdns->host_dev, we can only do
+ * it at its parent PM APIs
+ */
+ pm_runtime_disable(cdns->host_dev);
+ pm_runtime_set_active(cdns->host_dev);
+ pm_runtime_enable(cdns->host_dev);
+ }
+
+ dev_dbg(dev, "at end of %s\n", __func__);
+
return 0;
}
-#endif
+#endif /* CONFIG_PM_SLEEP */
+static int cdns3_runtime_suspend(struct device *dev)
+{
+ struct cdns3 *cdns = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(dev, "at the begin of %s\n", __func__);
+ if (cdns->in_lpm) {
+ WARN_ON(1);
+ return 0;
+ }
+
+ ret = cdns3_controller_suspend(cdns, true);
+ if (ret)
+ return ret;
+ cdns3_disable_unprepare_clks(dev);
+
+ dev_dbg(dev, "at the end of %s\n", __func__);
+
+ return ret;
+}
+
+static int cdns3_runtime_resume(struct device *dev)
+{
+ struct cdns3 *cdns = dev_get_drvdata(dev);
+ int ret;
+
+ if (!cdns->in_lpm) {
+ WARN_ON(1);
+ return 0;
+ }
+
+ ret = cdns3_prepare_enable_clks(dev);
+ if (ret)
+ return ret;
+
+ usb_phy_set_suspend(cdns->usbphy, 0);
+ /* At resume path, never return error */
+ cdns3_enter_suspend(cdns, false, false);
+ cdns->in_lpm = 0;
+
+ if (cdns->role == CDNS3_ROLE_HOST) {
+ if (cdns->wakeup_int) {
+ cdns->wakeup_int = false;
+ pm_runtime_mark_last_busy(cdns->dev);
+ pm_runtime_put_autosuspend(cdns->dev);
+ enable_irq(cdns->irq);
+ }
+
+ if ((cdns->role != CDNS3_ROLE_END) && cdns3_role(cdns)->resume)
+ cdns3_role(cdns)->resume(cdns, false);
+ }
+
+ dev_dbg(dev, "at %s\n", __func__);
+ return 0;
+}
+#endif /* CONFIG_PM */
static const struct dev_pm_ops cdns3_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(cdns3_suspend, cdns3_resume)
+ SET_RUNTIME_PM_OPS(cdns3_runtime_suspend, cdns3_runtime_resume, NULL)
};
-#ifdef CONFIG_OF
-static const struct of_device_id of_cdns3_match[] = {
- { .compatible = "cdns,usb3" },
- { },
-};
-MODULE_DEVICE_TABLE(of, of_cdns3_match);
-#endif
-
static struct platform_driver cdns3_driver = {
.probe = cdns3_probe,
.remove = cdns3_remove,
@@ -663,9 +1112,20 @@ static struct platform_driver cdns3_driver = {
},
};
-module_platform_driver(cdns3_driver);
+static int __init cdns3_driver_platform_register(void)
+{
+ cdns3_host_driver_init();
+ return platform_driver_register(&cdns3_driver);
+}
+module_init(cdns3_driver_platform_register);
+
+static void __exit cdns3_driver_platform_unregister(void)
+{
+ platform_driver_unregister(&cdns3_driver);
+}
+module_exit(cdns3_driver_platform_unregister);
-MODULE_ALIAS("platform:cdns3");
-MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
+MODULE_ALIAS("platform:cdns-usb3");
+MODULE_AUTHOR("Peter Chen <peter.chen@nxp.com>");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Cadence USB3 DRD Controller Driver");
diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h
index 969eb94de204..a6bd1cfb4c7d 100644
--- a/drivers/usb/cdns3/core.h
+++ b/drivers/usb/cdns3/core.h
@@ -1,20 +1,32 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Cadence USBSS DRD Header File.
+/**
+ * core.h - Cadence USB3 DRD Controller Core header file
*
- * Copyright (C) 2017-2018 NXP
- * Copyright (C) 2018-2019 Cadence.
+ * Copyright 2017-2019 NXP
*
* Authors: Peter Chen <peter.chen@nxp.com>
- * Pawel Laszczak <pawell@cadence.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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-#include <linux/usb/otg.h>
-#include <linux/usb/role.h>
-#ifndef __LINUX_CDNS3_CORE_H
-#define __LINUX_CDNS3_CORE_H
+#ifndef __DRIVERS_USB_CDNS3_CORE_H
+#define __DRIVERS_USB_CDNS3_CORE_H
struct cdns3;
+enum cdns3_roles {
+ CDNS3_ROLE_HOST = 0,
+ CDNS3_ROLE_GADGET,
+ CDNS3_ROLE_END,
+};
/**
* struct cdns3_role_driver - host/gadget role driver
@@ -23,76 +35,100 @@ struct cdns3;
* @suspend: suspend callback for this role
* @resume: resume callback for this role
* @irq: irq handler for this role
+ * @thread_irq: thread irq handler for this role
* @name: role name string (host/gadget)
- * @state: current state
*/
struct cdns3_role_driver {
- int (*start)(struct cdns3 *cdns);
- void (*stop)(struct cdns3 *cdns);
- int (*suspend)(struct cdns3 *cdns, bool do_wakeup);
- int (*resume)(struct cdns3 *cdns, bool hibernated);
+ int (*start)(struct cdns3 *);
+ void (*stop)(struct cdns3 *);
+ int (*suspend)(struct cdns3 *, bool do_wakeup);
+ int (*resume)(struct cdns3 *, bool hibernated);
+ irqreturn_t (*irq)(struct cdns3 *);
+ irqreturn_t (*thread_irq)(struct cdns3 *);
const char *name;
-#define CDNS3_ROLE_STATE_INACTIVE 0
-#define CDNS3_ROLE_STATE_ACTIVE 1
- int state;
};
-#define CDNS3_XHCI_RESOURCES_NUM 2
+#define CDNS3_NUM_OF_CLKS 5
/**
* struct cdns3 - Representation of Cadence USB3 DRD controller.
* @dev: pointer to Cadence device struct
* @xhci_regs: pointer to base of xhci registers
* @xhci_res: the resource for xhci
* @dev_regs: pointer to base of dev registers
- * @otg_res: the resource for otg
- * @otg_v0_regs: pointer to base of v0 otg registers
- * @otg_v1_regs: pointer to base of v1 otg registers
+ * @none_core_regs: pointer to base of nxp wrapper registers
+ * @phy_regs: pointer to base of phy registers
* @otg_regs: pointer to base of otg registers
- * @otg_irq: irq number for otg controller
- * @dev_irq: irq number for device controller
+ * @irq: irq number for controller
* @roles: array of supported roles for this controller
* @role: current role
* @host_dev: the child host device pointer for cdns3 core
* @gadget_dev: the child gadget device pointer for cdns3 core
- * @usb2_phy: pointer to USB2 PHY
- * @usb3_phy: pointer to USB3 PHY
+ * @usbphy: usbphy for this controller
+ * @cdns3_clks: Clock pointer array for cdns3 core
+ * @extcon: Type-C extern connector
+ * @extcon_nb: notifier block for Type-C extern connector
+ * @role_switch_wq: work queue item for role switch
+ * @in_lpm: the controller in low power mode
+ * @wakeup_int: the wakeup interrupt
* @mutex: the mutex for concurrent code at driver
- * @dr_mode: supported mode of operation it can be only Host, only Device
- * or OTG mode that allow to switch between Device and Host mode.
- * This field based on firmware setting, kernel configuration
- * and hardware configuration.
- * @role_sw: pointer to role switch object.
- * @role_override: set 1 if role rely on SW.
*/
struct cdns3 {
- struct device *dev;
- void __iomem *xhci_regs;
- struct resource xhci_res[CDNS3_XHCI_RESOURCES_NUM];
- struct cdns3_usb_regs __iomem *dev_regs;
+ struct device *dev;
+ void __iomem *xhci_regs;
+ struct resource *xhci_res;
+ struct cdns3_usb_regs __iomem *dev_regs;
+ void __iomem *none_core_regs;
+ void __iomem *phy_regs;
+ void __iomem *otg_regs;
+ int irq;
+ struct cdns3_role_driver *roles[CDNS3_ROLE_END];
+ enum cdns3_roles role;
+ struct device *host_dev;
+ struct cdns3_device *gadget_dev;
+ struct usb_phy *usbphy;
+ struct clk *cdns3_clks[CDNS3_NUM_OF_CLKS];
+ struct extcon_dev *extcon;
+ struct notifier_block extcon_nb;
+ struct work_struct role_switch_wq;
+ bool in_lpm;
+ bool wakeup_int;
+ struct mutex mutex;
+};
- struct resource otg_res;
- struct cdns3_otg_legacy_regs *otg_v0_regs;
- struct cdns3_otg_regs *otg_v1_regs;
- struct cdns3_otg_common_regs *otg_regs;
-#define CDNS3_CONTROLLER_V0 0
-#define CDNS3_CONTROLLER_V1 1
- u32 version;
+static inline struct cdns3_role_driver *cdns3_role(struct cdns3 *cdns)
+{
+ WARN_ON(cdns->role >= CDNS3_ROLE_END || !cdns->roles[cdns->role]);
+ return cdns->roles[cdns->role];
+}
- int otg_irq;
- int dev_irq;
- struct cdns3_role_driver *roles[USB_ROLE_DEVICE + 1];
- enum usb_role role;
- struct platform_device *host_dev;
- struct cdns3_device *gadget_dev;
- struct phy *usb2_phy;
- struct phy *usb3_phy;
- /* mutext used in workqueue*/
- struct mutex mutex;
- enum usb_dr_mode dr_mode;
- struct usb_role_switch *role_sw;
- int role_override;
-};
+static inline int cdns3_role_start(struct cdns3 *cdns, enum cdns3_roles role)
+{
+ int ret;
+ if (role >= CDNS3_ROLE_END)
+ return 0;
+
+ if (!cdns->roles[role])
+ return -ENXIO;
+
+ mutex_lock(&cdns->mutex);
+ cdns->role = role;
+ ret = cdns->roles[role]->start(cdns);
+ mutex_unlock(&cdns->mutex);
+ return ret;
+}
+
+static inline void cdns3_role_stop(struct cdns3 *cdns)
+{
+ enum cdns3_roles role = cdns->role;
+
+ if (role == CDNS3_ROLE_END)
+ return;
-int cdns3_hw_role_switch(struct cdns3 *cdns);
+ mutex_lock(&cdns->mutex);
+ cdns->roles[role]->stop(cdns);
+ cdns->role = CDNS3_ROLE_END;
+ mutex_unlock(&cdns->mutex);
+}
+int cdns3_handshake(void __iomem *ptr, u32 mask, u32 done, int usec);
-#endif /* __LINUX_CDNS3_CORE_H */
+#endif /* __DRIVERS_USB_CDNS3_CORE_H */
diff --git a/drivers/usb/cdns3/debug.h b/drivers/usb/cdns3/debug.h
index 2c9afbfe988b..8193676dc9f3 100644
--- a/drivers/usb/cdns3/debug.h
+++ b/drivers/usb/cdns3/debug.h
@@ -10,8 +10,6 @@
#ifndef __LINUX_CDNS3_DEBUG
#define __LINUX_CDNS3_DEBUG
-#include "core.h"
-
static inline char *cdns3_decode_usb_irq(char *str,
enum usb_device_speed speed,
u32 usb_ists)
@@ -28,12 +26,7 @@ static inline char *cdns3_decode_usb_irq(char *str,
ret += sprintf(str + ret, "Disconnection ");
if (usb_ists & USB_ISTS_L2ENTI)
ret += sprintf(str + ret, "suspended ");
- if (usb_ists & USB_ISTS_L1ENTI)
- ret += sprintf(str + ret, "L1 enter ");
- if (usb_ists & USB_ISTS_L1EXTI)
- ret += sprintf(str + ret, "L1 exit ");
- if (usb_ists & USB_ISTS_L2ENTI)
- ret += sprintf(str + ret, "L2 enter ");
+
if (usb_ists & USB_ISTS_L2EXTI)
ret += sprintf(str + ret, "L2 exit ");
if (usb_ists & USB_ISTS_U3EXTI)
@@ -158,4 +151,5 @@ static inline char *cdns3_dbg_ring(struct cdns3_endpoint *priv_ep,
return str;
}
+void cdns3_dbg(struct cdns3_device *priv_dev, const char *fmt, ...);
#endif /*__LINUX_CDNS3_DEBUG*/
diff --git a/drivers/usb/cdns3/dev-regs-macro.h b/drivers/usb/cdns3/dev-regs-macro.h
new file mode 100644
index 000000000000..5b307623afec
--- /dev/null
+++ b/drivers/usb/cdns3/dev-regs-macro.h
@@ -0,0 +1,894 @@
+/**
+ * dev-regs-macro.h - Cadence USB3 Device register definition
+ *
+ * Copyright (C) 2016 Cadence Design Systems - http://www.cadence.com
+ * Copyright 2017 NXP
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef __REG_USBSS_DEV_ADDR_MAP_MACRO_H__
+#define __REG_USBSS_DEV_ADDR_MAP_MACRO_H__
+
+
+/* macros for BlueprintGlobalNameSpace::USB_CONF */
+#ifndef __USB_CONF_MACRO__
+#define __USB_CONF_MACRO__
+
+/* macros for field CFGRST */
+#define USB_CONF__CFGRST__MASK 0x00000001U
+#define USB_CONF__CFGSET__MASK 0x00000002U
+#define USB_CONF__USB3DIS__MASK 0x00000008U
+#define USB_CONF__DEVEN__MASK 0x00004000U
+#define USB_CONF__DEVDS__MASK 0x00008000U
+#define USB_CONF__L1EN__MASK 0x00010000U
+#define USB_CONF__L1DS__MASK 0x00020000U
+#define USB_CONF__CLK2OFFDS__MASK 0x00080000U
+#define USB_CONF__U1EN__MASK 0x01000000U
+#define USB_CONF__U1DS__MASK 0x02000000U
+#define USB_CONF__U2EN__MASK 0x04000000U
+#define USB_CONF__U2DS__MASK 0x08000000U
+#endif /* __USB_CONF_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.usb_conf */
+#ifndef __USB_STS_MACRO__
+#define __USB_STS_MACRO__
+
+/* macros for field CFGSTS */
+#define USB_STS__CFGSTS__MASK 0x00000001U
+#define USB_STS__USBSPEED__READ(src) (((uint32_t)(src) & 0x00000070U) >> 4)
+
+/* macros for field ENDIAN_MIRROR */
+#define USB_STS__LPMST__READ(src) (((uint32_t)(src) & 0x000c0000U) >> 18)
+
+/* macros for field USB2CONS */
+#define USB_STS__U1ENS__MASK 0x01000000U
+#define USB_STS__U2ENS__MASK 0x02000000U
+#define USB_STS__LST__READ(src) (((uint32_t)(src) & 0x3c000000U) >> 26)
+
+/* macros for field DMAOFF */
+#endif /* __USB_STS_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.usb_sts */
+#ifndef __USB_CMD_MACRO__
+#define __USB_CMD_MACRO__
+
+/* macros for field SET_ADDR */
+#define USB_CMD__SET_ADDR__MASK 0x00000001U
+#define USB_CMD__STMODE 0x00000200U
+#define USB_CMD__TMODE_SEL(x) (x << 10)
+#define USB_CMD__FADDR__WRITE(src) (((uint32_t)(src) << 1) & 0x000000feU)
+#endif /* __USB_CMD_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.usb_cmd */
+#ifndef __USB_ITPN_MACRO__
+#define __USB_ITPN_MACRO__
+
+/* macros for field ITPN */
+#endif /* __USB_ITPN_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.usb_iptn */
+#ifndef __USB_LPM_MACRO__
+#define __USB_LPM_MACRO__
+
+/* macros for field HIRD */
+#endif /* __USB_LPM_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.usb_lpm */
+#ifndef __USB_IEN_MACRO__
+#define __USB_IEN_MACRO__
+
+/* macros for field CONIEN */
+#define USB_IEN__CONIEN__MASK 0x00000001U
+#define USB_IEN__DISIEN__MASK 0x00000002U
+#define USB_IEN__UWRESIEN__MASK 0x00000004U
+#define USB_IEN__UHRESIEN__MASK 0x00000008U
+#define USB_IEN__U3EXTIEN__MASK 0x00000020U
+#define USB_IEN__CON2IEN__MASK 0x00010000U
+#define USB_IEN__U2RESIEN__MASK 0x00040000U
+#define USB_IEN__L2ENTIEN__MASK 0x00100000U
+#define USB_IEN__L2EXTIEN__MASK 0x00200000U
+#endif /* __USB_IEN_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.usb_ien */
+#ifndef __USB_ISTS_MACRO__
+#define __USB_ISTS_MACRO__
+
+/* macros for field CONI */
+#define USB_ISTS__CONI__SHIFT 0
+#define USB_ISTS__DISI__SHIFT 1
+#define USB_ISTS__UWRESI__SHIFT 2
+#define USB_ISTS__UHRESI__SHIFT 3
+#define USB_ISTS__U3EXTI__SHIFT 5
+#define USB_ISTS__CON2I__SHIFT 16
+#define USB_ISTS__DIS2I__SHIFT 17
+#define USB_ISTS__DIS2I__MASK 0x00020000U
+#define USB_ISTS__U2RESI__SHIFT 18
+#define USB_ISTS__L2ENTI__SHIFT 20
+#define USB_ISTS__L2EXTI__SHIFT 21
+#endif /* __USB_ISTS_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.usb_ists */
+#ifndef __EP_SEL_MACRO__
+#define __EP_SEL_MACRO__
+
+/* macros for field EPNO */
+#endif /* __EP_SEL_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.ep_sel */
+#ifndef __EP_TRADDR_MACRO__
+#define __EP_TRADDR_MACRO__
+
+/* macros for field TRADDR */
+#define EP_TRADDR__TRADDR__WRITE(src) ((uint32_t)(src) & 0xffffffffU)
+#endif /* __EP_TRADDR_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.ep_traddr */
+#ifndef __EP_CFG_MACRO__
+#define __EP_CFG_MACRO__
+
+/* macros for field ENABLE */
+#define EP_CFG__ENABLE__MASK 0x00000001U
+#define EP_CFG__EPTYPE__WRITE(src) (((uint32_t)(src) << 1) & 0x00000006U)
+#define EP_CFG__MAXBURST__WRITE(src) (((uint32_t)(src) << 8) & 0x00000f00U)
+#define EP_CFG__MAXPKTSIZE__WRITE(src) (((uint32_t)(src) << 16) & 0x07ff0000U)
+#define EP_CFG__BUFFERING__WRITE(src) (((uint32_t)(src) << 27) & 0xf8000000U)
+#endif /* __EP_CFG_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.ep_cfg */
+#ifndef __EP_CMD_MACRO__
+#define __EP_CMD_MACRO__
+
+/* macros for field EPRST */
+#define EP_CMD__EPRST__MASK 0x00000001U
+#define EP_CMD__SSTALL__MASK 0x00000002U
+#define EP_CMD__CSTALL__MASK 0x00000004U
+#define EP_CMD__ERDY__MASK 0x00000008U
+#define EP_CMD__REQ_CMPL__MASK 0x00000020U
+#define EP_CMD__DRDY__MASK 0x00000040U
+#define EP_CMD__DFLUSH__MASK 0x00000080U
+#endif /* __EP_CMD_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.ep_cmd */
+#ifndef __EP_STS_MACRO__
+#define __EP_STS_MACRO__
+
+/* macros for field SETUP */
+#define EP_STS__SETUP__MASK 0x00000001U
+#define EP_STS__STALL__MASK 0x00000002U
+#define EP_STS__IOC__MASK 0x00000004U
+#define EP_STS__ISP__MASK 0x00000008U
+#define EP_STS__DESCMIS__MASK 0x00000010U
+#define EP_STS__TRBERR__MASK 0x00000080U
+#define EP_STS__NRDY__MASK 0x00000100U
+#define EP_STS__DBUSY__MASK 0x00000200U
+#define EP_STS__OUTSMM__MASK 0x00004000U
+#define EP_STS__ISOERR__MASK 0x00008000U
+#endif /* __EP_STS_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.ep_sts */
+#ifndef __EP_STS_SID_MACRO__
+#define __EP_STS_SID_MACRO__
+
+/* macros for field SID */
+#endif /* __EP_STS_SID_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.ep_sts_sid */
+#ifndef __EP_STS_EN_MACRO__
+#define __EP_STS_EN_MACRO__
+
+/* macros for field SETUPEN */
+#define EP_STS_EN__SETUPEN__MASK 0x00000001U
+#define EP_STS_EN__DESCMISEN__MASK 0x00000010U
+#define EP_STS_EN__TRBERREN__MASK 0x00000080U
+#endif /* __EP_STS_EN_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.ep_sts_en */
+#ifndef __DRBL_MACRO__
+#define __DRBL_MACRO__
+
+/* macros for field DRBL0O */
+#endif /* __DRBL_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.drbl */
+#ifndef __EP_IEN_MACRO__
+#define __EP_IEN_MACRO__
+
+/* macros for field EOUTEN0 */
+#define EP_IEN__EOUTEN0__MASK 0x00000001U
+#define EP_IEN__EINEN0__MASK 0x00010000U
+#endif /* __EP_IEN_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.ep_ien */
+#ifndef __EP_ISTS_MACRO__
+#define __EP_ISTS_MACRO__
+
+/* macros for field EOUT0 */
+#define EP_ISTS__EOUT0__MASK 0x00000001U
+#define EP_ISTS__EIN0__MASK 0x00010000U
+#endif /* __EP_ISTS_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.ep_ists */
+#ifndef __USB_PWR_MACRO__
+#define __USB_PWR_MACRO__
+
+/* macros for field PSO_EN */
+#endif /* __USB_PWR_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.usb_pwr */
+#ifndef __USB_CONF2_MACRO__
+#define __USB_CONF2_MACRO__
+
+/* macros for field AHB_RETRY_EN */
+#endif /* __USB_CONF2_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.usb_conf2 */
+#ifndef __USB_CAP1_MACRO__
+#define __USB_CAP1_MACRO__
+
+/* macros for field SFR_TYPE */
+#endif /* __USB_CAP1_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.usb_cap1 */
+#ifndef __USB_CAP2_MACRO__
+#define __USB_CAP2_MACRO__
+
+/* macros for field ACTUAL_MEM_SIZE */
+#endif /* __USB_CAP2_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.usb_cap2 */
+#ifndef __USB_CAP3_MACRO__
+#define __USB_CAP3_MACRO__
+
+/* macros for field EPOUT_N */
+#endif /* __USB_CAP3_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.usb_cap3 */
+#ifndef __USB_CAP4_MACRO__
+#define __USB_CAP4_MACRO__
+
+/* macros for field EPOUTI_N */
+#endif /* __USB_CAP4_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.usb_cap4 */
+#ifndef __USB_CAP5_MACRO__
+#define __USB_CAP5_MACRO__
+
+/* macros for field EPOUTI_N */
+#endif /* __USB_CAP5_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.usb_cap5 */
+#ifndef __USB_CAP6_MACRO__
+#define __USB_CAP6_MACRO__
+
+/* macros for field VERSION */
+#endif /* __USB_CAP6_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.usb_cap6 */
+#ifndef __USB_CPKT1_MACRO__
+#define __USB_CPKT1_MACRO__
+
+/* macros for field CPKT1 */
+#endif /* __USB_CPKT1_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.usb_cpkt1 */
+#ifndef __USB_CPKT2_MACRO__
+#define __USB_CPKT2_MACRO__
+
+/* macros for field CPKT2 */
+#endif /* __USB_CPKT2_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.usb_cpkt2 */
+#ifndef __USB_CPKT3_MACRO__
+#define __USB_CPKT3_MACRO__
+
+/* macros for field CPKT3 */
+#endif /* __USB_CPKT3_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.usb_cpkt3 */
+#ifndef __CFG_REG1_MACRO__
+#define __CFG_REG1_MACRO__
+
+/* macros for field DEBOUNCER_CNT */
+#endif /* __CFG_REG1_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg1 */
+#ifndef __DBG_LINK1_MACRO__
+#define __DBG_LINK1_MACRO__
+
+/* macros for field LFPS_MIN_DET_U1_EXIT */
+#define DBG_LINK1__LFPS_MIN_GEN_U1_EXIT__WRITE(src) \
+ (((uint32_t)(src)\
+ << 8) & 0x0000ff00U)
+#define DBG_LINK1__LFPS_MIN_GEN_U1_EXIT_SET__MASK 0x02000000U
+#endif /* __DBG_LINK1_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.dbg_link1 */
+#ifndef __DBG_LINK2_MACRO__
+#define __DBG_LINK2_MACRO__
+
+/* macros for field RXEQTR_AVAL */
+#endif /* __DBG_LINK2_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.dbg_link2 */
+#ifndef __CFG_REG4_MACRO__
+#define __CFG_REG4_MACRO__
+
+/* macros for field RXDETECT_QUIET_TIMEOUT */
+#endif /* __CFG_REG4_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg4 */
+#ifndef __CFG_REG5_MACRO__
+#define __CFG_REG5_MACRO__
+
+/* macros for field U3_HDSK_FAIL_TIMEOUT */
+#endif /* __CFG_REG5_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg5 */
+#ifndef __CFG_REG6_MACRO__
+#define __CFG_REG6_MACRO__
+
+/* macros for field SSINACTIVE_QUIET_TIMEOUT */
+#endif /* __CFG_REG6_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg6 */
+#ifndef __CFG_REG7_MACRO__
+#define __CFG_REG7_MACRO__
+
+/* macros for field POLLING_LFPS_TIMEOUT */
+#endif /* __CFG_REG7_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg7 */
+#ifndef __CFG_REG8_MACRO__
+#define __CFG_REG8_MACRO__
+
+/* macros for field POLLING_ACTIVE_TIMEOUT */
+#endif /* __CFG_REG8_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg8 */
+#ifndef __CFG_REG9_MACRO__
+#define __CFG_REG9_MACRO__
+
+/* macros for field POLLING_IDLE_TIMEOUT */
+#endif /* __CFG_REG9_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg9 */
+#ifndef __CFG_REG10_MACRO__
+#define __CFG_REG10_MACRO__
+
+/* macros for field POLLING_CONF_TIMEOUT */
+#endif /* __CFG_REG10_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg10 */
+#ifndef __CFG_REG11_MACRO__
+#define __CFG_REG11_MACRO__
+
+/* macros for field RECOVERY_ACTIVE_TIMEOUT */
+#endif /* __CFG_REG11_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg11 */
+#ifndef __CFG_REG12_MACRO__
+#define __CFG_REG12_MACRO__
+
+/* macros for field RECOVERY_CONF_TIMEOUT */
+#endif /* __CFG_REG12_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg12 */
+#ifndef __CFG_REG13_MACRO__
+#define __CFG_REG13_MACRO__
+
+/* macros for field RECOVERY_IDLE_TIMEOUT */
+#endif /* __CFG_REG13_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg13 */
+#ifndef __CFG_REG14_MACRO__
+#define __CFG_REG14_MACRO__
+
+/* macros for field HOTRESET_ACTIVE_TIMEOUT */
+#endif /* __CFG_REG14_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg14 */
+#ifndef __CFG_REG15_MACRO__
+#define __CFG_REG15_MACRO__
+
+/* macros for field HOTRESET_EXIT_TIMEOUT */
+#endif /* __CFG_REG15_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg15 */
+#ifndef __CFG_REG16_MACRO__
+#define __CFG_REG16_MACRO__
+
+/* macros for field LFPS_PING_REPEAT */
+#endif /* __CFG_REG16_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg16 */
+#ifndef __CFG_REG17_MACRO__
+#define __CFG_REG17_MACRO__
+
+/* macros for field PENDING_HP_TIMEOUT */
+#endif /* __CFG_REG17_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg17 */
+#ifndef __CFG_REG18_MACRO__
+#define __CFG_REG18_MACRO__
+
+/* macros for field CREDIT_HP_TIMEOUT */
+#endif /* __CFG_REG18_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg18 */
+#ifndef __CFG_REG19_MACRO__
+#define __CFG_REG19_MACRO__
+
+/* macros for field LUP_TIMEOUT */
+#endif /* __CFG_REG19_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg19 */
+#ifndef __CFG_REG20_MACRO__
+#define __CFG_REG20_MACRO__
+
+/* macros for field LDN_TIMEOUT */
+#endif /* __CFG_REG20_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg20 */
+#ifndef __CFG_REG21_MACRO__
+#define __CFG_REG21_MACRO__
+
+/* macros for field PM_LC_TIMEOUT */
+#endif /* __CFG_REG21_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg21 */
+#ifndef __CFG_REG22_MACRO__
+#define __CFG_REG22_MACRO__
+
+/* macros for field PM_ENTRY_TIMEOUT */
+#endif /* __CFG_REG22_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg22 */
+#ifndef __CFG_REG23_MACRO__
+#define __CFG_REG23_MACRO__
+
+/* macros for field UX_EXIT_TIMEOUT */
+#endif /* __CFG_REG23_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg23 */
+#ifndef __CFG_REG24_MACRO__
+#define __CFG_REG24_MACRO__
+
+/* macros for field LFPS_DET_RESET_MIN */
+#endif /* __CFG_REG24_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg24 */
+#ifndef __CFG_REG25_MACRO__
+#define __CFG_REG25_MACRO__
+
+/* macros for field LFPS_DET_RESET_MAX */
+#endif /* __CFG_REG25_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg25 */
+#ifndef __CFG_REG26_MACRO__
+#define __CFG_REG26_MACRO__
+
+/* macros for field LFPS_DET_POLLING_MIN */
+#endif /* __CFG_REG26_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg26 */
+#ifndef __CFG_REG27_MACRO__
+#define __CFG_REG27_MACRO__
+
+/* macros for field LFPS_DET_POLLING_MAX */
+#endif /* __CFG_REG27_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg27 */
+#ifndef __CFG_REG28_MACRO__
+#define __CFG_REG28_MACRO__
+
+/* macros for field LFPS_DET_PING_MIN */
+#endif /* __CFG_REG28_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg28 */
+#ifndef __CFG_REG29_MACRO__
+#define __CFG_REG29_MACRO__
+
+/* macros for field LFPS_DET_PING_MAX */
+#endif /* __CFG_REG29_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg29 */
+#ifndef __CFG_REG30_MACRO__
+#define __CFG_REG30_MACRO__
+
+/* macros for field LFPS_DET_U1EXIT_MIN */
+#endif /* __CFG_REG30_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg30 */
+#ifndef __CFG_REG31_MACRO__
+#define __CFG_REG31_MACRO__
+
+/* macros for field LFPS_DET_U1EXIT_MAX */
+#endif /* __CFG_REG31_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg31 */
+#ifndef __CFG_REG32_MACRO__
+#define __CFG_REG32_MACRO__
+
+/* macros for field LFPS_DET_U2EXIT_MIN */
+#endif /* __CFG_REG32_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg32 */
+#ifndef __CFG_REG33_MACRO__
+#define __CFG_REG33_MACRO__
+
+/* macros for field LFPS_DET_U2EXIT_MAX */
+#endif /* __CFG_REG33_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg33 */
+#ifndef __CFG_REG34_MACRO__
+#define __CFG_REG34_MACRO__
+
+/* macros for field LFPS_DET_U3EXIT_MIN */
+#endif /* __CFG_REG34_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg34 */
+#ifndef __CFG_REG35_MACRO__
+#define __CFG_REG35_MACRO__
+
+/* macros for field LFPS_DET_U3EXIT_MAX */
+#endif /* __CFG_REG35_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg35 */
+#ifndef __CFG_REG36_MACRO__
+#define __CFG_REG36_MACRO__
+
+/* macros for field LFPS_GEN_PING */
+#endif /* __CFG_REG36_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg36 */
+#ifndef __CFG_REG37_MACRO__
+#define __CFG_REG37_MACRO__
+
+/* macros for field LFPS_GEN_POLLING */
+#endif /* __CFG_REG37_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg37 */
+#ifndef __CFG_REG38_MACRO__
+#define __CFG_REG38_MACRO__
+
+/* macros for field LFPS_GEN_U1EXIT */
+#endif /* __CFG_REG38_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg38 */
+#ifndef __CFG_REG39_MACRO__
+#define __CFG_REG39_MACRO__
+
+/* macros for field LFPS_GEN_U3EXIT */
+#endif /* __CFG_REG39_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg39 */
+#ifndef __CFG_REG40_MACRO__
+#define __CFG_REG40_MACRO__
+
+/* macros for field LFPS_MIN_GEN_U1EXIT */
+#endif /* __CFG_REG40_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg40 */
+#ifndef __CFG_REG41_MACRO__
+#define __CFG_REG41_MACRO__
+
+/* macros for field LFPS_MIN_GEN_U2EXIT */
+#endif /* __CFG_REG41_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg41 */
+#ifndef __CFG_REG42_MACRO__
+#define __CFG_REG42_MACRO__
+
+/* macros for field LFPS_POLLING_REPEAT */
+#endif /* __CFG_REG42_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg42 */
+#ifndef __CFG_REG43_MACRO__
+#define __CFG_REG43_MACRO__
+
+/* macros for field LFPS_POLLING_MAX_TREPEAT */
+#endif /* __CFG_REG43_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg43 */
+#ifndef __CFG_REG44_MACRO__
+#define __CFG_REG44_MACRO__
+
+/* macros for field LFPS_POLLING_MIN_TREPEAT */
+#endif /* __CFG_REG44_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg44 */
+#ifndef __CFG_REG45_MACRO__
+#define __CFG_REG45_MACRO__
+
+/* macros for field ITP_WAKEUP_TIMEOUT */
+#endif /* __CFG_REG45_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg45 */
+#ifndef __CFG_REG46_MACRO__
+#define __CFG_REG46_MACRO__
+
+/* macros for field TSEQ_QUANTITY */
+#endif /* __CFG_REG46_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg46 */
+#ifndef __CFG_REG47_MACRO__
+#define __CFG_REG47_MACRO__
+
+/* macros for field ERDY_TIMEOUT_CNT */
+#endif /* __CFG_REG47_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg47 */
+#ifndef __CFG_REG48_MACRO__
+#define __CFG_REG48_MACRO__
+
+/* macros for field TWTRSTFS_J_CNT */
+#endif /* __CFG_REG48_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg48 */
+#ifndef __CFG_REG49_MACRO__
+#define __CFG_REG49_MACRO__
+
+/* macros for field TUCH_CNT */
+#endif /* __CFG_REG49_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg49 */
+#ifndef __CFG_REG50_MACRO__
+#define __CFG_REG50_MACRO__
+
+/* macros for field TWAITCHK_CNT */
+#endif /* __CFG_REG50_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg50 */
+#ifndef __CFG_REG51_MACRO__
+#define __CFG_REG51_MACRO__
+
+/* macros for field TWTFS_CNT */
+#endif /* __CFG_REG51_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg51 */
+#ifndef __CFG_REG52_MACRO__
+#define __CFG_REG52_MACRO__
+
+/* macros for field TWTREV_CNT */
+#endif /* __CFG_REG52_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg52 */
+#ifndef __CFG_REG53_MACRO__
+#define __CFG_REG53_MACRO__
+
+/* macros for field TWTRSTHS_CNT */
+#endif /* __CFG_REG53_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg53 */
+#ifndef __CFG_REG54_MACRO__
+#define __CFG_REG54_MACRO__
+
+/* macros for field TWTRSM_CNT */
+#endif /* __CFG_REG54_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg54 */
+#ifndef __CFG_REG55_MACRO__
+#define __CFG_REG55_MACRO__
+
+/* macros for field TDRSMUP_CNT */
+#endif /* __CFG_REG55_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg55 */
+#ifndef __CFG_REG56_MACRO__
+#define __CFG_REG56_MACRO__
+
+/* macros for field TOUTHS_CNT */
+#endif /* __CFG_REG56_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg56 */
+#ifndef __CFG_REG57_MACRO__
+#define __CFG_REG57_MACRO__
+
+/* macros for field LFPS_DEB_WIDTH */
+#endif /* __CFG_REG57_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg57 */
+#ifndef __CFG_REG58_MACRO__
+#define __CFG_REG58_MACRO__
+
+/* macros for field LFPS_GEN_U2EXIT */
+#endif /* __CFG_REG58_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg58 */
+#ifndef __CFG_REG59_MACRO__
+#define __CFG_REG59_MACRO__
+
+/* macros for field LFPS_MIN_GEN_U3EXIT */
+#endif /* __CFG_REG59_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg59 */
+#ifndef __CFG_REG60_MACRO__
+#define __CFG_REG60_MACRO__
+
+/* macros for field PORT_CONFIG_TIMEOUT */
+#endif /* __CFG_REG60_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg60 */
+#ifndef __CFG_REG61_MACRO__
+#define __CFG_REG61_MACRO__
+
+/* macros for field LFPS_POL_LFPS_TO_RXEQ */
+#endif /* __CFG_REG61_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg61 */
+#ifndef __CFG_REG62_MACRO__
+#define __CFG_REG62_MACRO__
+
+/* macros for field PHY_TX_LATENCY */
+#endif /* __CFG_REG62_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg62 */
+#ifndef __CFG_REG63_MACRO__
+#define __CFG_REG63_MACRO__
+
+/* macros for field U2_INACTIVITY_TMOUT */
+#endif /* __CFG_REG63_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg63 */
+#ifndef __CFG_REG64_MACRO__
+#define __CFG_REG64_MACRO__
+
+/* macros for field TFILTSE0 */
+#endif /* __CFG_REG64_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg64 */
+#ifndef __CFG_REG65_MACRO__
+#define __CFG_REG65_MACRO__
+
+/* macros for field TFILT */
+#endif /* __CFG_REG65_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg65 */
+#ifndef __CFG_REG66_MACRO__
+#define __CFG_REG66_MACRO__
+
+/* macros for field TWTRSTFS_SE0 */
+#endif /* __CFG_REG66_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.cfg_reg66 */
+#ifndef __DMA_AXI_CTRL_MACRO__
+#define __DMA_AXI_CTRL_MACRO__
+
+/* macros for field MAWPROT */
+#endif /* __DMA_AXI_CTRL_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.dma_axi_ctrl */
+#ifndef __DMA_AXI_ID_MACRO__
+#define __DMA_AXI_ID_MACRO__
+
+/* macros for field MAW_ID */
+#endif /* __DMA_AXI_ID_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.dma_axi_id */
+#ifndef __DMA_AXI_CAP_MACRO__
+#define __DMA_AXI_CAP_MACRO__
+
+/* macros for field RESERVED0 */
+#endif /* __DMA_AXI_CAP_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.dma_axi_cap */
+#ifndef __DMA_AXI_CTRL0_MACRO__
+#define __DMA_AXI_CTRL0_MACRO__
+
+/* macros for field B_MAX */
+#endif /* __DMA_AXI_CTRL0_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.dma_axi_ctrl0 */
+#ifndef __DMA_AXI_CTRL1_MACRO__
+#define __DMA_AXI_CTRL1_MACRO__
+
+/* macros for field ROT */
+#endif /* __DMA_AXI_CTRL1_MACRO__ */
+
+
+/* macros for usbss_dev_register_block.dma_axi_ctrl1 */
+#endif /* __REG_USBSS_DEV_ADDR_MAP_MACRO_H__ */
diff --git a/drivers/usb/cdns3/dev-regs-map.h b/drivers/usb/cdns3/dev-regs-map.h
new file mode 100644
index 000000000000..ef9cfe2ff342
--- /dev/null
+++ b/drivers/usb/cdns3/dev-regs-map.h
@@ -0,0 +1,126 @@
+/**
+ * dev-regs-map.h - Cadence USB3 Device register map definition
+ *
+ * Copyright (C) 2016 Cadence Design Systems - http://www.cadence.com
+ * Copyright 2017 NXP
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+
+#ifndef __REG_USBSS_DEV_ADDR_MAP_H__
+#define __REG_USBSS_DEV_ADDR_MAP_H__
+
+#include "dev-regs-macro.h"
+
+struct usbss_dev_register_block_type {
+ uint32_t usb_conf; /* 0x0 - 0x4 */
+ uint32_t usb_sts; /* 0x4 - 0x8 */
+ uint32_t usb_cmd; /* 0x8 - 0xc */
+ uint32_t usb_iptn; /* 0xc - 0x10 */
+ uint32_t usb_lpm; /* 0x10 - 0x14 */
+ uint32_t usb_ien; /* 0x14 - 0x18 */
+ uint32_t usb_ists; /* 0x18 - 0x1c */
+ uint32_t ep_sel; /* 0x1c - 0x20 */
+ uint32_t ep_traddr; /* 0x20 - 0x24 */
+ uint32_t ep_cfg; /* 0x24 - 0x28 */
+ uint32_t ep_cmd; /* 0x28 - 0x2c */
+ uint32_t ep_sts; /* 0x2c - 0x30 */
+ uint32_t ep_sts_sid; /* 0x30 - 0x34 */
+ uint32_t ep_sts_en; /* 0x34 - 0x38 */
+ uint32_t drbl; /* 0x38 - 0x3c */
+ uint32_t ep_ien; /* 0x3c - 0x40 */
+ uint32_t ep_ists; /* 0x40 - 0x44 */
+ uint32_t usb_pwr; /* 0x44 - 0x48 */
+ uint32_t usb_conf2; /* 0x48 - 0x4c */
+ uint32_t usb_cap1; /* 0x4c - 0x50 */
+ uint32_t usb_cap2; /* 0x50 - 0x54 */
+ uint32_t usb_cap3; /* 0x54 - 0x58 */
+ uint32_t usb_cap4; /* 0x58 - 0x5c */
+ uint32_t usb_cap5; /* 0x5c - 0x60 */
+ uint32_t PAD2_73; /* 0x60 - 0x64 */
+ uint32_t usb_cpkt1; /* 0x64 - 0x68 */
+ uint32_t usb_cpkt2; /* 0x68 - 0x6c */
+ uint32_t usb_cpkt3; /* 0x6c - 0x70 */
+ char pad__0[0x90]; /* 0x70 - 0x100 */
+ uint32_t PAD2_78; /* 0x100 - 0x104 */
+ uint32_t dbg_link1; /* 0x104 - 0x108 */
+ uint32_t PAD2_80; /* 0x108 - 0x10c */
+ uint32_t PAD2_81; /* 0x10c - 0x110 */
+ uint32_t PAD2_82; /* 0x110 - 0x114 */
+ uint32_t PAD2_83; /* 0x114 - 0x118 */
+ uint32_t PAD2_84; /* 0x118 - 0x11c */
+ uint32_t PAD2_85; /* 0x11c - 0x120 */
+ uint32_t PAD2_86; /* 0x120 - 0x124 */
+ uint32_t PAD2_87; /* 0x124 - 0x128 */
+ uint32_t PAD2_88; /* 0x128 - 0x12c */
+ uint32_t PAD2_89; /* 0x12c - 0x130 */
+ uint32_t PAD2_90; /* 0x130 - 0x134 */
+ uint32_t PAD2_91; /* 0x134 - 0x138 */
+ uint32_t PAD2_92; /* 0x138 - 0x13c */
+ uint32_t PAD2_93; /* 0x13c - 0x140 */
+ uint32_t PAD2_94; /* 0x140 - 0x144 */
+ uint32_t PAD2_95; /* 0x144 - 0x148 */
+ uint32_t PAD2_96; /* 0x148 - 0x14c */
+ uint32_t PAD2_97; /* 0x14c - 0x150 */
+ uint32_t PAD2_98; /* 0x150 - 0x154 */
+ uint32_t PAD2_99; /* 0x154 - 0x158 */
+ uint32_t PAD2_100; /* 0x158 - 0x15c */
+ uint32_t PAD2_101; /* 0x15c - 0x160 */
+ uint32_t PAD2_102; /* 0x160 - 0x164 */
+ uint32_t PAD2_103; /* 0x164 - 0x168 */
+ uint32_t PAD2_104; /* 0x168 - 0x16c */
+ uint32_t PAD2_105; /* 0x16c - 0x170 */
+ uint32_t PAD2_106; /* 0x170 - 0x174 */
+ uint32_t PAD2_107; /* 0x174 - 0x178 */
+ uint32_t PAD2_108; /* 0x178 - 0x17c */
+ uint32_t PAD2_109; /* 0x17c - 0x180 */
+ uint32_t PAD2_110; /* 0x180 - 0x184 */
+ uint32_t PAD2_111; /* 0x184 - 0x188 */
+ uint32_t PAD2_112; /* 0x188 - 0x18c */
+ char pad__1[0x20]; /* 0x18c - 0x1ac */
+ uint32_t PAD2_114; /* 0x1ac - 0x1b0 */
+ uint32_t PAD2_115; /* 0x1b0 - 0x1b4 */
+ uint32_t PAD2_116; /* 0x1b4 - 0x1b8 */
+ uint32_t PAD2_117; /* 0x1b8 - 0x1bc */
+ uint32_t PAD2_118; /* 0x1bc - 0x1c0 */
+ uint32_t PAD2_119; /* 0x1c0 - 0x1c4 */
+ uint32_t PAD2_120; /* 0x1c4 - 0x1c8 */
+ uint32_t PAD2_121; /* 0x1c8 - 0x1cc */
+ uint32_t PAD2_122; /* 0x1cc - 0x1d0 */
+ uint32_t PAD2_123; /* 0x1d0 - 0x1d4 */
+ uint32_t PAD2_124; /* 0x1d4 - 0x1d8 */
+ uint32_t PAD2_125; /* 0x1d8 - 0x1dc */
+ uint32_t PAD2_126; /* 0x1dc - 0x1e0 */
+ uint32_t PAD2_127; /* 0x1e0 - 0x1e4 */
+ uint32_t PAD2_128; /* 0x1e4 - 0x1e8 */
+ uint32_t PAD2_129; /* 0x1e8 - 0x1ec */
+ uint32_t PAD2_130; /* 0x1ec - 0x1f0 */
+ uint32_t PAD2_131; /* 0x1f0 - 0x1f4 */
+ uint32_t PAD2_132; /* 0x1f4 - 0x1f8 */
+ uint32_t PAD2_133; /* 0x1f8 - 0x1fc */
+ uint32_t PAD2_134; /* 0x1fc - 0x200 */
+ uint32_t PAD2_135; /* 0x200 - 0x204 */
+ uint32_t PAD2_136; /* 0x204 - 0x208 */
+ uint32_t PAD2_137; /* 0x208 - 0x20c */
+ uint32_t PAD2_138; /* 0x20c - 0x210 */
+ uint32_t PAD2_139; /* 0x210 - 0x214 */
+ uint32_t PAD2_140; /* 0x214 - 0x218 */
+ uint32_t PAD2_141; /* 0x218 - 0x21c */
+ uint32_t PAD2_142; /* 0x21c - 0x220 */
+ uint32_t PAD2_143; /* 0x220 - 0x224 */
+ uint32_t PAD2_144; /* 0x224 - 0x228 */
+ char pad__2[0xd8]; /* 0x228 - 0x300 */
+ uint32_t dma_axi_ctrl; /* 0x300 - 0x304 */
+ uint32_t PAD2_147; /* 0x304 - 0x308 */
+ uint32_t PAD2_148; /* 0x308 - 0x30c */
+ uint32_t PAD2_149; /* 0x30c - 0x310 */
+ uint32_t PAD2_150; /* 0x310 - 0x314 */
+};
+
+#endif /* __REG_USBSS_DEV_ADDR_MAP_H__ */
diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c
deleted file mode 100644
index 16ad485f0b69..000000000000
--- a/drivers/usb/cdns3/drd.c
+++ /dev/null
@@ -1,381 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Cadence USBSS DRD Driver.
- *
- * Copyright (C) 2018-2019 Cadence.
- * Copyright (C) 2019 Texas Instruments
- *
- * Author: Pawel Laszczak <pawell@cadence.com>
- * Roger Quadros <rogerq@ti.com>
- *
- *
- */
-#include <linux/kernel.h>
-#include <linux/interrupt.h>
-#include <linux/delay.h>
-#include <linux/iopoll.h>
-#include <linux/usb/otg.h>
-
-#include "gadget.h"
-#include "drd.h"
-#include "core.h"
-
-/**
- * cdns3_set_mode - change mode of OTG Core
- * @cdns: pointer to context structure
- * @mode: selected mode from cdns_role
- *
- * Returns 0 on success otherwise negative errno
- */
-int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
-{
- int ret = 0;
- u32 reg;
-
- switch (mode) {
- case USB_DR_MODE_PERIPHERAL:
- break;
- case USB_DR_MODE_HOST:
- break;
- case USB_DR_MODE_OTG:
- dev_dbg(cdns->dev, "Set controller to OTG mode\n");
- if (cdns->version == CDNS3_CONTROLLER_V1) {
- reg = readl(&cdns->otg_v1_regs->override);
- reg |= OVERRIDE_IDPULLUP;
- writel(reg, &cdns->otg_v1_regs->override);
- } else {
- reg = readl(&cdns->otg_v0_regs->ctrl1);
- reg |= OVERRIDE_IDPULLUP_V0;
- writel(reg, &cdns->otg_v0_regs->ctrl1);
- }
-
- /*
- * Hardware specification says: "ID_VALUE must be valid within
- * 50ms after idpullup is set to '1" so driver must wait
- * 50ms before reading this pin.
- */
- usleep_range(50000, 60000);
- break;
- default:
- dev_err(cdns->dev, "Unsupported mode of operation %d\n", mode);
- return -EINVAL;
- }
-
- return ret;
-}
-
-int cdns3_get_id(struct cdns3 *cdns)
-{
- int id;
-
- id = readl(&cdns->otg_regs->sts) & OTGSTS_ID_VALUE;
- dev_dbg(cdns->dev, "OTG ID: %d", id);
-
- return id;
-}
-
-int cdns3_get_vbus(struct cdns3 *cdns)
-{
- int vbus;
-
- vbus = !!(readl(&cdns->otg_regs->sts) & OTGSTS_VBUS_VALID);
- dev_dbg(cdns->dev, "OTG VBUS: %d", vbus);
-
- return vbus;
-}
-
-int cdns3_is_host(struct cdns3 *cdns)
-{
- if (cdns->dr_mode == USB_DR_MODE_HOST)
- return 1;
- else if (!cdns3_get_id(cdns))
- return 1;
-
- return 0;
-}
-
-int cdns3_is_device(struct cdns3 *cdns)
-{
- if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL)
- return 1;
- else if (cdns->dr_mode == USB_DR_MODE_OTG)
- if (cdns3_get_id(cdns))
- return 1;
-
- return 0;
-}
-
-/**
- * cdns3_otg_disable_irq - Disable all OTG interrupts
- * @cdns: Pointer to controller context structure
- */
-static void cdns3_otg_disable_irq(struct cdns3 *cdns)
-{
- writel(0, &cdns->otg_regs->ien);
-}
-
-/**
- * cdns3_otg_enable_irq - enable id and sess_valid interrupts
- * @cdns: Pointer to controller context structure
- */
-static void cdns3_otg_enable_irq(struct cdns3 *cdns)
-{
- writel(OTGIEN_ID_CHANGE_INT | OTGIEN_VBUSVALID_RISE_INT |
- OTGIEN_VBUSVALID_FALL_INT, &cdns->otg_regs->ien);
-}
-
-/**
- * cdns3_drd_switch_host - start/stop host
- * @cdns: Pointer to controller context structure
- * @on: 1 for start, 0 for stop
- *
- * Returns 0 on success otherwise negative errno
- */
-int cdns3_drd_switch_host(struct cdns3 *cdns, int on)
-{
- int ret, val;
- u32 reg = OTGCMD_OTG_DIS;
-
- /* switch OTG core */
- if (on) {
- writel(OTGCMD_HOST_BUS_REQ | reg, &cdns->otg_regs->cmd);
-
- dev_dbg(cdns->dev, "Waiting till Host mode is turned on\n");
- ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val,
- val & OTGSTS_XHCI_READY,
- 1, 100000);
- if (ret) {
- dev_err(cdns->dev, "timeout waiting for xhci_ready\n");
- return ret;
- }
- } else {
- writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
- OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
- &cdns->otg_regs->cmd);
- /* Waiting till H_IDLE state.*/
- readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
- !(val & OTGSTATE_HOST_STATE_MASK),
- 1, 2000000);
- }
-
- return 0;
-}
-
-/**
- * cdns3_drd_switch_gadget - start/stop gadget
- * @cdns: Pointer to controller context structure
- * @on: 1 for start, 0 for stop
- *
- * Returns 0 on success otherwise negative errno
- */
-int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on)
-{
- int ret, val;
- u32 reg = OTGCMD_OTG_DIS;
-
- /* switch OTG core */
- if (on) {
- writel(OTGCMD_DEV_BUS_REQ | reg, &cdns->otg_regs->cmd);
-
- dev_dbg(cdns->dev, "Waiting till Device mode is turned on\n");
-
- ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val,
- val & OTGSTS_DEV_READY,
- 1, 100000);
- if (ret) {
- dev_err(cdns->dev, "timeout waiting for dev_ready\n");
- return ret;
- }
- } else {
- /*
- * driver should wait at least 10us after disabling Device
- * before turning-off Device (DEV_BUS_DROP)
- */
- usleep_range(20, 30);
- writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
- OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
- &cdns->otg_regs->cmd);
- /* Waiting till DEV_IDLE state.*/
- readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
- !(val & OTGSTATE_DEV_STATE_MASK),
- 1, 2000000);
- }
-
- return 0;
-}
-
-/**
- * cdns3_init_otg_mode - initialize drd controller
- * @cdns: Pointer to controller context structure
- *
- * Returns 0 on success otherwise negative errno
- */
-static int cdns3_init_otg_mode(struct cdns3 *cdns)
-{
- int ret = 0;
-
- cdns3_otg_disable_irq(cdns);
- /* clear all interrupts */
- writel(~0, &cdns->otg_regs->ivect);
-
- ret = cdns3_set_mode(cdns, USB_DR_MODE_OTG);
- if (ret)
- return ret;
-
- cdns3_otg_enable_irq(cdns);
- return ret;
-}
-
-/**
- * cdns3_drd_update_mode - initialize mode of operation
- * @cdns: Pointer to controller context structure
- *
- * Returns 0 on success otherwise negative errno
- */
-int cdns3_drd_update_mode(struct cdns3 *cdns)
-{
- int ret = 0;
-
- switch (cdns->dr_mode) {
- case USB_DR_MODE_PERIPHERAL:
- ret = cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL);
- break;
- case USB_DR_MODE_HOST:
- ret = cdns3_set_mode(cdns, USB_DR_MODE_HOST);
- break;
- case USB_DR_MODE_OTG:
- ret = cdns3_init_otg_mode(cdns);
- break;
- default:
- dev_err(cdns->dev, "Unsupported mode of operation %d\n",
- cdns->dr_mode);
- return -EINVAL;
- }
-
- return ret;
-}
-
-static irqreturn_t cdns3_drd_thread_irq(int irq, void *data)
-{
- struct cdns3 *cdns = data;
-
- cdns3_hw_role_switch(cdns);
-
- return IRQ_HANDLED;
-}
-
-/**
- * cdns3_drd_irq - interrupt handler for OTG events
- *
- * @irq: irq number for cdns3 core device
- * @data: structure of cdns3
- *
- * Returns IRQ_HANDLED or IRQ_NONE
- */
-static irqreturn_t cdns3_drd_irq(int irq, void *data)
-{
- irqreturn_t ret = IRQ_NONE;
- struct cdns3 *cdns = data;
- u32 reg;
-
- if (cdns->dr_mode != USB_DR_MODE_OTG)
- return ret;
-
- reg = readl(&cdns->otg_regs->ivect);
-
- if (!reg)
- return ret;
-
- if (reg & OTGIEN_ID_CHANGE_INT) {
- dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n",
- cdns3_get_id(cdns));
-
- ret = IRQ_WAKE_THREAD;
- }
-
- if (reg & (OTGIEN_VBUSVALID_RISE_INT | OTGIEN_VBUSVALID_FALL_INT)) {
- dev_dbg(cdns->dev, "OTG IRQ: new VBUS: %d\n",
- cdns3_get_vbus(cdns));
-
- ret = IRQ_WAKE_THREAD;
- }
-
- writel(~0, &cdns->otg_regs->ivect);
- return ret;
-}
-
-int cdns3_drd_init(struct cdns3 *cdns)
-{
- void __iomem *regs;
- int ret = 0;
- u32 state;
-
- regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res);
- if (IS_ERR(regs))
- return PTR_ERR(regs);
-
- /* Detection of DRD version. Controller has been released
- * in two versions. Both are similar, but they have same changes
- * in register maps.
- * The first register in old version is command register and it's read
- * only, so driver should read 0 from it. On the other hand, in v1
- * the first register contains device ID number which is not set to 0.
- * Driver uses this fact to detect the proper version of
- * controller.
- */
- cdns->otg_v0_regs = regs;
- if (!readl(&cdns->otg_v0_regs->cmd)) {
- cdns->version = CDNS3_CONTROLLER_V0;
- cdns->otg_v1_regs = NULL;
- cdns->otg_regs = regs;
- writel(1, &cdns->otg_v0_regs->simulate);
- dev_info(cdns->dev, "DRD version v0 (%08x)\n",
- readl(&cdns->otg_v0_regs->version));
- } else {
- cdns->otg_v0_regs = NULL;
- cdns->otg_v1_regs = regs;
- cdns->otg_regs = (void *)&cdns->otg_v1_regs->cmd;
- cdns->version = CDNS3_CONTROLLER_V1;
- writel(1, &cdns->otg_v1_regs->simulate);
- dev_info(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
- readl(&cdns->otg_v1_regs->did),
- readl(&cdns->otg_v1_regs->rid));
- }
-
- state = OTGSTS_STRAP(readl(&cdns->otg_regs->sts));
-
- /* Update dr_mode according to STRAP configuration. */
- cdns->dr_mode = USB_DR_MODE_OTG;
- if (state == OTGSTS_STRAP_HOST) {
- dev_dbg(cdns->dev, "Controller strapped to HOST\n");
- cdns->dr_mode = USB_DR_MODE_HOST;
- } else if (state == OTGSTS_STRAP_GADGET) {
- dev_dbg(cdns->dev, "Controller strapped to PERIPHERAL\n");
- cdns->dr_mode = USB_DR_MODE_PERIPHERAL;
- }
-
- ret = devm_request_threaded_irq(cdns->dev, cdns->otg_irq,
- cdns3_drd_irq,
- cdns3_drd_thread_irq,
- IRQF_SHARED,
- dev_name(cdns->dev), cdns);
-
- if (ret) {
- dev_err(cdns->dev, "couldn't get otg_irq\n");
- return ret;
- }
-
- state = readl(&cdns->otg_regs->sts);
- if (OTGSTS_OTG_NRDY(state) != 0) {
- dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n");
- return -ENODEV;
- }
-
- return ret;
-}
-
-int cdns3_drd_exit(struct cdns3 *cdns)
-{
- cdns3_otg_disable_irq(cdns);
- return 0;
-}
diff --git a/drivers/usb/cdns3/drd.h b/drivers/usb/cdns3/drd.h
deleted file mode 100644
index 04e01c4d2377..000000000000
--- a/drivers/usb/cdns3/drd.h
+++ /dev/null
@@ -1,167 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * Cadence USB3 DRD header file.
- *
- * Copyright (C) 2018-2019 Cadence.
- *
- * Author: Pawel Laszczak <pawell@cadence.com>
- */
-#ifndef __LINUX_CDNS3_DRD
-#define __LINUX_CDNS3_DRD
-
-#include <linux/usb/otg.h>
-#include <linux/phy/phy.h>
-#include "core.h"
-
-/* DRD register interface for version v1. */
-struct cdns3_otg_regs {
- __le32 did;
- __le32 rid;
- __le32 capabilities;
- __le32 reserved1;
- __le32 cmd;
- __le32 sts;
- __le32 state;
- __le32 reserved2;
- __le32 ien;
- __le32 ivect;
- __le32 refclk;
- __le32 tmr;
- __le32 reserved3[4];
- __le32 simulate;
- __le32 override;
- __le32 susp_ctrl;
- __le32 reserved4;
- __le32 anasts;
- __le32 adp_ramp_time;
- __le32 ctrl1;
- __le32 ctrl2;
-};
-
-/* DRD register interface for version v0. */
-struct cdns3_otg_legacy_regs {
- __le32 cmd;
- __le32 sts;
- __le32 state;
- __le32 refclk;
- __le32 ien;
- __le32 ivect;
- __le32 reserved1[3];
- __le32 tmr;
- __le32 reserved2[2];
- __le32 version;
- __le32 capabilities;
- __le32 reserved3[2];
- __le32 simulate;
- __le32 reserved4[5];
- __le32 ctrl1;
-};
-
-/*
- * Common registers interface for both version of DRD.
- */
-struct cdns3_otg_common_regs {
- __le32 cmd;
- __le32 sts;
- __le32 state;
- __le32 different1;
- __le32 ien;
- __le32 ivect;
-};
-
-/* CDNS_RID - bitmasks */
-#define CDNS_RID(p) ((p) & GENMASK(15, 0))
-
-/* CDNS_VID - bitmasks */
-#define CDNS_DID(p) ((p) & GENMASK(31, 0))
-
-/* OTGCMD - bitmasks */
-/* "Request the bus for Device mode. */
-#define OTGCMD_DEV_BUS_REQ BIT(0)
-/* Request the bus for Host mode */
-#define OTGCMD_HOST_BUS_REQ BIT(1)
-/* Enable OTG mode. */
-#define OTGCMD_OTG_EN BIT(2)
-/* Disable OTG mode */
-#define OTGCMD_OTG_DIS BIT(3)
-/*"Configure OTG as A-Device. */
-#define OTGCMD_A_DEV_EN BIT(4)
-/*"Configure OTG as A-Device. */
-#define OTGCMD_A_DEV_DIS BIT(5)
-/* Drop the bus for Device mod e. */
-#define OTGCMD_DEV_BUS_DROP BIT(8)
-/* Drop the bus for Host mode*/
-#define OTGCMD_HOST_BUS_DROP BIT(9)
-/* Power Down USBSS-DEV. */
-#define OTGCMD_DEV_POWER_OFF BIT(11)
-/* Power Down CDNSXHCI. */
-#define OTGCMD_HOST_POWER_OFF BIT(12)
-
-/* OTGIEN - bitmasks */
-/* ID change interrupt enable */
-#define OTGIEN_ID_CHANGE_INT BIT(0)
-/* Vbusvalid fall detected interrupt enable.*/
-#define OTGIEN_VBUSVALID_RISE_INT BIT(4)
-/* Vbusvalid fall detected interrupt enable */
-#define OTGIEN_VBUSVALID_FALL_INT BIT(5)
-
-/* OTGSTS - bitmasks */
-/*
- * Current value of the ID pin. It is only valid when idpullup in
- * OTGCTRL1_TYPE register is set to '1'.
- */
-#define OTGSTS_ID_VALUE BIT(0)
-/* Current value of the vbus_valid */
-#define OTGSTS_VBUS_VALID BIT(1)
-/* Current value of the b_sess_vld */
-#define OTGSTS_SESSION_VALID BIT(2)
-/*Device mode is active*/
-#define OTGSTS_DEV_ACTIVE BIT(3)
-/* Host mode is active. */
-#define OTGSTS_HOST_ACTIVE BIT(4)
-/* OTG Controller not ready. */
-#define OTGSTS_OTG_NRDY_MASK BIT(11)
-#define OTGSTS_OTG_NRDY(p) ((p) & OTGSTS_OTG_NRDY_MASK)
-/*
- * Value of the strap pins.
- * 000 - no default configuration
- * 010 - Controller initiall configured as Host
- * 100 - Controller initially configured as Device
- */
-#define OTGSTS_STRAP(p) (((p) & GENMASK(14, 12)) >> 12)
-#define OTGSTS_STRAP_NO_DEFAULT_CFG 0x00
-#define OTGSTS_STRAP_HOST_OTG 0x01
-#define OTGSTS_STRAP_HOST 0x02
-#define OTGSTS_STRAP_GADGET 0x04
-/* Host mode is turned on. */
-#define OTGSTS_XHCI_READY BIT(26)
-/* "Device mode is turned on .*/
-#define OTGSTS_DEV_READY BIT(27)
-
-/* OTGSTATE- bitmasks */
-#define OTGSTATE_DEV_STATE_MASK GENMASK(2, 0)
-#define OTGSTATE_HOST_STATE_MASK GENMASK(5, 3)
-#define OTGSTATE_HOST_STATE_IDLE 0x0
-#define OTGSTATE_HOST_STATE_VBUS_FALL 0x7
-#define OTGSTATE_HOST_STATE(p) (((p) & OTGSTATE_HOST_STATE_MASK) >> 3)
-
-/* OTGREFCLK - bitmasks */
-#define OTGREFCLK_STB_CLK_SWITCH_EN BIT(31)
-
-/* OVERRIDE - bitmasks */
-#define OVERRIDE_IDPULLUP BIT(0)
-/* Only for CDNS3_CONTROLLER_V0 version */
-#define OVERRIDE_IDPULLUP_V0 BIT(24)
-
-int cdns3_is_host(struct cdns3 *cdns);
-int cdns3_is_device(struct cdns3 *cdns);
-int cdns3_get_id(struct cdns3 *cdns);
-int cdns3_get_vbus(struct cdns3 *cdns);
-int cdns3_drd_init(struct cdns3 *cdns);
-int cdns3_drd_exit(struct cdns3 *cdns);
-int cdns3_drd_update_mode(struct cdns3 *cdns);
-int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on);
-int cdns3_drd_switch_host(struct cdns3 *cdns, int on);
-int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode);
-
-#endif /* __LINUX_CDNS3_DRD */
diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c
index e71240b386b4..09ab1a87a267 100644
--- a/drivers/usb/cdns3/ep0.c
+++ b/drivers/usb/cdns3/ep0.c
@@ -7,14 +7,16 @@
*
* Authors: Pawel Jez <pjez@cadence.com>,
* Pawel Laszczak <pawell@cadence.com>
- * Peter Chen <peter.chen@nxp.com>
+ * Peter Chen <peter.chen@nxp.com>
*/
+#include <linux/delay.h>
+#include <linux/interrupt.h>
#include <linux/usb/composite.h>
-#include <linux/iopoll.h>
#include "gadget.h"
#include "trace.h"
+#include "core.h"
static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
@@ -32,25 +34,14 @@ static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
*/
static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev,
dma_addr_t dma_addr,
- unsigned int length, int erdy, int zlp)
+ unsigned int length, int erdy)
{
struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
- priv_ep->trb_pool[0].buffer = TRB_BUFFER(dma_addr);
- priv_ep->trb_pool[0].length = TRB_LEN(length);
-
- if (zlp) {
- priv_ep->trb_pool[0].control = TRB_CYCLE | TRB_TYPE(TRB_NORMAL);
- priv_ep->trb_pool[1].buffer = TRB_BUFFER(dma_addr);
- priv_ep->trb_pool[1].length = TRB_LEN(0);
- priv_ep->trb_pool[1].control = TRB_CYCLE | TRB_IOC |
- TRB_TYPE(TRB_NORMAL);
- } else {
- priv_ep->trb_pool[0].control = TRB_CYCLE | TRB_IOC |
- TRB_TYPE(TRB_NORMAL);
- priv_ep->trb_pool[1].control = 0;
- }
+ priv_ep->trb_pool->buffer = TRB_BUFFER(dma_addr);
+ priv_ep->trb_pool->length = TRB_LEN(length);
+ priv_ep->trb_pool->control = TRB_CYCLE | TRB_IOC | TRB_TYPE(TRB_NORMAL);
trace_cdns3_prepare_trb(priv_ep, priv_ep->trb_pool);
@@ -61,12 +52,9 @@ static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev,
trace_cdns3_doorbell_ep0(priv_dev->ep0_data_dir ? "ep0in" : "ep0out",
readl(&regs->ep_traddr));
- /* TRB should be prepared before starting transfer. */
+ /* TRB should be prepared before starting transfer */
writel(EP_CMD_DRDY, &regs->ep_cmd);
- /* Resume controller before arming transfer. */
- __cdns3_gadget_wakeup(priv_dev);
-
if (erdy)
writel(EP_CMD_ERDY, &priv_dev->regs->ep_cmd);
}
@@ -82,11 +70,14 @@ static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev,
static int cdns3_ep0_delegate_req(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl_req)
{
- int ret;
+ int ret = 0;
spin_unlock(&priv_dev->lock);
priv_dev->setup_pending = 1;
- ret = priv_dev->gadget_driver->setup(&priv_dev->gadget, ctrl_req);
+ if (priv_dev->gadget_driver && priv_dev->gadget_driver->setup
+ && get_gadget_data(&priv_dev->gadget))
+ ret = priv_dev->gadget_driver->setup(&priv_dev->gadget,
+ ctrl_req);
priv_dev->setup_pending = 0;
spin_lock(&priv_dev->lock);
return ret;
@@ -97,7 +88,7 @@ static void cdns3_prepare_setup_packet(struct cdns3_device *priv_dev)
priv_dev->ep0_data_dir = 0;
priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
- sizeof(struct usb_ctrlrequest), 0, 0);
+ sizeof(struct usb_ctrlrequest), 0);
}
static void cdns3_ep0_complete_setup(struct cdns3_device *priv_dev,
@@ -111,7 +102,7 @@ static void cdns3_ep0_complete_setup(struct cdns3_device *priv_dev,
list_del_init(&request->list);
if (send_stall) {
- trace_cdns3_halt(priv_ep, send_stall, 0);
+ cdns3_dbg(priv_ep->cdns3_dev, "STALL for ep0\n");
/* set_stall on ep0 */
cdns3_select_ep(priv_dev, 0x00);
writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
@@ -122,8 +113,6 @@ static void cdns3_ep0_complete_setup(struct cdns3_device *priv_dev,
priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
writel((send_erdy ? EP_CMD_ERDY : 0) | EP_CMD_REQ_CMPL,
&priv_dev->regs->ep_cmd);
-
- cdns3_allow_enable_l1(priv_dev, 1);
}
/**
@@ -234,11 +223,10 @@ static int cdns3_req_ep0_set_address(struct cdns3_device *priv_dev,
static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev,
struct usb_ctrlrequest *ctrl)
{
- struct cdns3_endpoint *priv_ep;
__le16 *response_pkt;
u16 usb_status = 0;
u32 recip;
- u8 index;
+ u32 reg;
recip = ctrl->bRequestType & USB_RECIP_MASK;
@@ -254,6 +242,8 @@ static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev,
if (priv_dev->gadget.speed != USB_SPEED_SUPER)
break;
+ reg = readl(&priv_dev->regs->usb_sts);
+
if (priv_dev->u1_allowed)
usb_status |= BIT(USB_DEV_STAT_U1_ENABLED);
@@ -264,13 +254,9 @@ static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev,
case USB_RECIP_INTERFACE:
return cdns3_ep0_delegate_req(priv_dev, ctrl);
case USB_RECIP_ENDPOINT:
- index = cdns3_ep_addr_to_index(ctrl->wIndex);
- priv_ep = priv_dev->eps[index];
-
- /* check if endpoint is stalled or stall is pending */
+ /* check if endpoint is stalled */
cdns3_select_ep(priv_dev, ctrl->wIndex);
- if (EP_STS_STALL(readl(&priv_dev->regs->ep_sts)) ||
- (priv_ep->flags & EP_STALL_PENDING))
+ if (EP_STS_STALL(readl(&priv_dev->regs->ep_sts)))
usb_status = BIT(USB_ENDPOINT_HALT);
break;
default:
@@ -281,7 +267,7 @@ static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev,
*response_pkt = cpu_to_le16(usb_status);
cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
- sizeof(*response_pkt), 1, 0);
+ sizeof(*response_pkt), 1);
return 0;
}
@@ -293,13 +279,15 @@ static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev,
enum usb_device_speed speed;
int ret = 0;
u32 wValue;
+ u32 wIndex;
u16 tmode;
wValue = le16_to_cpu(ctrl->wValue);
+ wIndex = le16_to_cpu(ctrl->wIndex);
state = priv_dev->gadget.state;
speed = priv_dev->gadget.speed;
- switch (wValue) {
+ switch (ctrl->wValue) {
case USB_DEVICE_REMOTE_WAKEUP:
priv_dev->wake_up_flag = !!set;
break;
@@ -338,7 +326,7 @@ static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev,
* for sending status stage.
* This time should be less then 3ms.
*/
- mdelay(1);
+ usleep_range(1000, 2000);
cdns3_set_register_bit(&priv_dev->regs->usb_cmd,
USB_CMD_STMODE |
USB_STS_TMODE_SEL(tmode - 1));
@@ -392,12 +380,40 @@ static int cdns3_ep0_feature_handle_endpoint(struct cdns3_device *priv_dev,
cdns3_select_ep(priv_dev, ctrl->wIndex);
- if (set)
- __cdns3_gadget_ep_set_halt(priv_ep);
- else if (!(priv_ep->flags & EP_WEDGE))
- ret = __cdns3_gadget_ep_clear_halt(priv_ep);
+ if (set) {
+ cdns3_dbg(priv_ep->cdns3_dev, "Stall endpoint %s\n",
+ priv_ep->name);
+ writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
+ priv_ep->flags |= EP_STALL;
+ } else {
+ struct usb_request *request;
+
+ if (priv_dev->eps[index]->flags & EP_WEDGE) {
+ cdns3_select_ep(priv_dev, 0x00);
+ return 0;
+ }
+
+ cdns3_dbg(priv_ep->cdns3_dev, "Clear Stalled endpoint %s\n",
+ priv_ep->name);
+
+ writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
+
+ /* wait for EPRST cleared */
+ ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
+ EP_CMD_EPRST, 0, 100);
+ if (ret)
+ return -EINVAL;
+
+ priv_ep->flags &= ~EP_STALL;
+
+ request = cdns3_next_request(&priv_ep->pending_req_list);
+ if (request) {
+ cdns3_dbg(priv_ep->cdns3_dev, "Resume transfer for %s\n",
+ priv_ep->name);
- cdns3_select_ep(priv_dev, 0x00);
+ cdns3_rearm_transfer(priv_ep, 1);
+ }
+ }
return ret;
}
@@ -457,7 +473,7 @@ static int cdns3_req_ep0_set_sel(struct cdns3_device *priv_dev,
return -EINVAL;
}
- cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 6, 1, 0);
+ cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 6, 1);
return 0;
}
@@ -545,6 +561,30 @@ void cdns3_pending_setup_status_handler(struct work_struct *work)
}
/**
+ * cdns3_gadget_ep_giveback - call struct usb_request's ->complete callback
+ * @priv_ep: The endpoint to whom the request belongs to
+ * @priv_req: The request we're giving back
+ * @status: completion code for the request
+ *
+ * Must be called with controller's lock held and interrupts disabled. This
+ * function will unmap @req and call its ->complete() callback to notify upper
+ * layers that it has completed.
+ */
+
+void cdns3_gadget_ep0_giveback(struct cdns3_device *priv_dev,
+ int status)
+{
+ struct cdns3_endpoint *priv_ep;
+ struct usb_request *request;
+
+ priv_ep = priv_dev->eps[0];
+ request = cdns3_next_request(&priv_ep->pending_req_list);
+
+ priv_ep->dir = priv_dev->ep0_data_dir;
+ cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), status);
+}
+
+/**
* cdns3_ep0_setup_phase - Handling setup USB requests
* @priv_dev: extended gadget object
*/
@@ -600,6 +640,10 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev)
TRB_LEN(le32_to_cpu(priv_ep->trb_pool->length));
priv_ep->dir = priv_dev->ep0_data_dir;
+ if (request->zero && request->length && priv_ep->dir
+ && (request->length % priv_dev->gadget.ep0->maxpacket == 0))
+ cdns3_ep0_run_transfer(priv_dev, request->dma, 0, 1);
+
cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), 0);
}
@@ -640,12 +684,7 @@ void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir)
__pending_setup_status_handler(priv_dev);
- if (ep_sts_reg & EP_STS_SETUP)
- priv_dev->wait_for_setup = 1;
-
- if (priv_dev->wait_for_setup && ep_sts_reg & EP_STS_IOC) {
- priv_dev->wait_for_setup = 0;
- cdns3_allow_enable_l1(priv_dev, 0);
+ if ((ep_sts_reg & EP_STS_SETUP)) {
cdns3_ep0_setup_phase(priv_dev);
} else if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
priv_dev->ep0_data_dir = dir;
@@ -709,9 +748,10 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
unsigned long flags;
int erdy_sent = 0;
int ret = 0;
- u8 zlp = 0;
- trace_cdns3_ep0_queue(priv_dev, request);
+ cdns3_dbg(priv_ep->cdns3_dev, "Queue to Ep0%s L: %d\n",
+ priv_dev->ep0_data_dir ? "IN" : "OUT",
+ request->length);
/* cancel the request if controller receive new SETUP packet. */
if (cdns3_check_new_setup(priv_dev))
@@ -728,8 +768,6 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
if (!erdy_sent)
cdns3_ep0_complete_setup(priv_dev, 0, 1);
- cdns3_allow_enable_l1(priv_dev, 1);
-
request->actual = 0;
priv_dev->status_completion_no_call = true;
priv_dev->pending_status_request = request;
@@ -762,13 +800,7 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
request->status = -EINPROGRESS;
list_add_tail(&request->list, &priv_ep->pending_req_list);
-
- if (request->zero && request->length &&
- (request->length % ep->maxpacket == 0))
- zlp = 1;
-
- cdns3_ep0_run_transfer(priv_dev, request->dma, request->length, 1, zlp);
-
+ cdns3_ep0_run_transfer(priv_dev, request->dma, request->length, 1);
spin_unlock_irqrestore(&priv_dev->lock, flags);
return ret;
@@ -838,13 +870,6 @@ void cdns3_ep0_config(struct cdns3_device *priv_dev)
/* init ep out */
cdns3_select_ep(priv_dev, USB_DIR_OUT);
- if (priv_dev->dev_ver >= DEV_VER_V3) {
- cdns3_set_register_bit(&priv_dev->regs->dtrans,
- BIT(0) | BIT(16));
- cdns3_set_register_bit(&priv_dev->regs->tdl_from_trb,
- BIT(0) | BIT(16));
- }
-
writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
&regs->ep_cfg);
diff --git a/drivers/usb/cdns3/gadget-export.h b/drivers/usb/cdns3/gadget-export.h
index 577469eee961..7d8692409483 100644
--- a/drivers/usb/cdns3/gadget-export.h
+++ b/drivers/usb/cdns3/gadget-export.h
@@ -2,8 +2,7 @@
/*
* Cadence USBSS DRD Driver - Gadget Export APIs.
*
- * Copyright (C) 2017 NXP
- * Copyright (C) 2017-2018 NXP
+ * Copyright (C) 2017-2019 NXP
*
* Authors: Peter Chen <peter.chen@nxp.com>
*/
diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
index 4c1e75509303..f34bd7de4fc5 100644
--- a/drivers/usb/cdns3/gadget.c
+++ b/drivers/usb/cdns3/gadget.c
@@ -3,11 +3,11 @@
* Cadence USBSS DRD Driver - gadget side.
*
* Copyright (C) 2018-2019 Cadence Design Systems.
- * Copyright (C) 2017-2018 NXP
+ * Copyright (C) 2017-2019 NXP
*
* Authors: Pawel Jez <pjez@cadence.com>,
* Pawel Laszczak <pawell@cadence.com>
- * Peter Chen <peter.chen@nxp.com>
+ * Peter Chen <peter.chen@nxp.com>
*/
/*
@@ -26,8 +26,6 @@
* as valid during adding next TRB only if DMA is stopped or at TRBERR
* interrupt.
*
- * Issue has been fixed in DEV_VER_V3 version of controller.
- *
* Work around 2:
* Controller for OUT endpoints has shared on-chip buffers for all incoming
* packets, including ep0out. It's FIFO buffer, so packets must be handle by DMA
@@ -45,32 +43,44 @@
* use this workaround only for these endpoint.
*
* Driver use limited number of buffer. This number can be set by macro
- * CDNS3_WA2_NUM_BUFFERS.
+ * CDNS_WA2_NUM_BUFFERS.
*
* Such blocking situation was observed on ACM gadget. For this function
* host send OUT data packet but ACM function is not prepared for this packet.
* It's cause that buffer placed in on chip memory block transfer to other
* endpoints.
*
- * Issue has been fixed in DEV_VER_V2 version of controller.
+ * It's limitation of controller but maybe this issues should be fixed in
+ * function driver.
*
+ * This work around can be disabled/enabled by means of quirk_internal_buffer
+ * module parameter. By default feature is enabled. It can has impact to
+ * transfer performance and in most case this feature can be disabled.
*/
#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
#include <linux/usb/gadget.h>
+#include <linux/pm_runtime.h>
#include <linux/module.h>
-#include <linux/iopoll.h>
#include "core.h"
#include "gadget-export.h"
#include "gadget.h"
#include "trace.h"
-#include "drd.h"
static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
struct usb_request *request,
gfp_t gfp_flags);
+/*
+ * Parameter allows to disable/enable handling of work around 2 feature.
+ * By default this value is enabled.
+ */
+static bool quirk_internal_buffer = 1;
+module_param(quirk_internal_buffer, bool, 0644);
+MODULE_PARM_DESC(quirk_internal_buffer, "Disable/enable WA2 algorithm");
+
/**
* cdns3_set_register_bit - set bit in given register.
* @ptr: address of device controller register to be read and changed
@@ -93,16 +103,6 @@ u8 cdns3_ep_addr_to_index(u8 ep_addr)
return (((ep_addr & 0x7F)) + ((ep_addr & USB_DIR_IN) ? 16 : 0));
}
-static int cdns3_get_dma_pos(struct cdns3_device *priv_dev,
- struct cdns3_endpoint *priv_ep)
-{
- int dma_index;
-
- dma_index = readl(&priv_dev->regs->ep_traddr) - priv_ep->trb_pool_dma;
-
- return dma_index / TRB_SIZE;
-}
-
/**
* cdns3_next_request - returns next request from list
* @list: list containing requests
@@ -115,7 +115,7 @@ struct usb_request *cdns3_next_request(struct list_head *list)
}
/**
- * cdns3_next_align_buf - returns next buffer from list
+ * cdns3_aligned_buf - returns next buffer from list
* @list: list containing buffers
*
* Returns buffer or NULL if no buffers in list
@@ -184,9 +184,9 @@ int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep)
if (!priv_ep->trb_pool) {
priv_ep->trb_pool = dma_alloc_coherent(priv_dev->sysdev,
- ring_size,
- &priv_ep->trb_pool_dma,
- GFP_DMA32 | GFP_ATOMIC);
+ ring_size,
+ &priv_ep->trb_pool_dma,
+ GFP_DMA);
if (!priv_ep->trb_pool)
return -ENOMEM;
} else {
@@ -197,10 +197,11 @@ int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep)
return 0;
priv_ep->num_trbs = ring_size / TRB_SIZE;
- /* Initialize the last TRB as Link TRB. */
+ /* Initialize the last TRB as Link TRB */
link_trb = (priv_ep->trb_pool + (priv_ep->num_trbs - 1));
link_trb->buffer = TRB_BUFFER(priv_ep->trb_pool_dma);
- link_trb->control = TRB_CYCLE | TRB_TYPE(TRB_LINK) | TRB_TOGGLE;
+ link_trb->control = TRB_CYCLE | TRB_TYPE(TRB_LINK) |
+ TRB_CHAIN | TRB_TOGGLE;
return 0;
}
@@ -226,18 +227,16 @@ static void cdns3_free_trb_pool(struct cdns3_endpoint *priv_ep)
static void cdns3_ep_stall_flush(struct cdns3_endpoint *priv_ep)
{
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
- int val;
- trace_cdns3_halt(priv_ep, 1, 1);
+ cdns3_dbg(priv_ep->cdns3_dev, "Stall & flush endpoint %s\n",
+ priv_ep->name);
writel(EP_CMD_DFLUSH | EP_CMD_ERDY | EP_CMD_SSTALL,
&priv_dev->regs->ep_cmd);
/* wait for DFLUSH cleared */
- readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val,
- !(val & EP_CMD_DFLUSH), 1, 1000);
- priv_ep->flags |= EP_STALLED;
- priv_ep->flags &= ~EP_STALL_PENDING;
+ cdns3_handshake(&priv_dev->regs->ep_cmd, EP_CMD_DFLUSH, 0, 1000);
+ priv_ep->flags |= EP_STALL;
}
/**
@@ -250,9 +249,8 @@ void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev)
cdns3_allow_enable_l1(priv_dev, 0);
priv_dev->hw_configured_flag = 0;
- priv_dev->onchip_used_size = 0;
+ priv_dev->onchip_mem_allocated_size = 0;
priv_dev->out_mem_is_allocated = 0;
- priv_dev->wait_for_setup = 0;
}
/**
@@ -354,11 +352,13 @@ enum usb_device_speed cdns3_get_speed(struct cdns3_device *priv_dev)
static int cdns3_start_all_request(struct cdns3_device *priv_dev,
struct cdns3_endpoint *priv_ep)
{
+ struct cdns3_request *priv_req;
struct usb_request *request;
int ret = 0;
while (!list_empty(&priv_ep->deferred_req_list)) {
request = cdns3_next_request(&priv_ep->deferred_req_list);
+ priv_req = to_cdns3_request(request);
ret = cdns3_ep_run_transfer(priv_ep, request);
if (ret)
@@ -373,36 +373,24 @@ static int cdns3_start_all_request(struct cdns3_device *priv_dev,
return ret;
}
-/*
- * WA2: Set flag for all not ISOC OUT endpoints. If this flag is set
- * driver try to detect whether endpoint need additional internal
- * buffer for unblocking on-chip FIFO buffer. This flag will be cleared
- * if before first DESCMISS interrupt the DMA will be armed.
- */
-#define cdns3_wa2_enable_detection(priv_dev, ep_priv, reg) do { \
- if (!priv_ep->dir && priv_ep->type != USB_ENDPOINT_XFER_ISOC) { \
- priv_ep->flags |= EP_QUIRK_EXTRA_BUF_DET; \
- (reg) |= EP_STS_EN_DESCMISEN; \
- } } while (0)
-
/**
- * cdns3_wa2_descmiss_copy_data copy data from internal requests to
- * request queued by class driver.
+ * cdns3_descmiss_copy_data copy data from internal requests to request queued
+ * by class driver.
* @priv_ep: extended endpoint object
* @request: request object
*/
-static void cdns3_wa2_descmiss_copy_data(struct cdns3_endpoint *priv_ep,
- struct usb_request *request)
+static void cdns3_descmiss_copy_data(struct cdns3_endpoint *priv_ep,
+ struct usb_request *request)
{
struct usb_request *descmiss_req;
struct cdns3_request *descmiss_priv_req;
- while (!list_empty(&priv_ep->wa2_descmiss_req_list)) {
+ while (!list_empty(&priv_ep->descmiss_req_list)) {
int chunk_end;
int length;
descmiss_priv_req =
- cdns3_next_priv_request(&priv_ep->wa2_descmiss_req_list);
+ cdns3_next_priv_request(&priv_ep->descmiss_req_list);
descmiss_req = &descmiss_priv_req->request;
/* driver can't touch pending request */
@@ -412,8 +400,6 @@ static void cdns3_wa2_descmiss_copy_data(struct cdns3_endpoint *priv_ep,
chunk_end = descmiss_priv_req->flags & REQUEST_INTERNAL_CH;
length = request->actual + descmiss_req->actual;
- request->status = descmiss_req->status;
-
if (length <= request->length) {
memcpy(&((u8 *)request->buf)[request->actual],
descmiss_req->buf,
@@ -428,199 +414,12 @@ static void cdns3_wa2_descmiss_copy_data(struct cdns3_endpoint *priv_ep,
kfree(descmiss_req->buf);
cdns3_gadget_ep_free_request(&priv_ep->endpoint, descmiss_req);
- --priv_ep->wa2_counter;
if (!chunk_end)
break;
}
}
-struct usb_request *cdns3_wa2_gadget_giveback(struct cdns3_device *priv_dev,
- struct cdns3_endpoint *priv_ep,
- struct cdns3_request *priv_req)
-{
- if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN &&
- priv_req->flags & REQUEST_INTERNAL) {
- struct usb_request *req;
-
- req = cdns3_next_request(&priv_ep->deferred_req_list);
-
- priv_ep->descmis_req = NULL;
-
- if (!req)
- return NULL;
-
- cdns3_wa2_descmiss_copy_data(priv_ep, req);
- if (!(priv_ep->flags & EP_QUIRK_END_TRANSFER) &&
- req->length != req->actual) {
- /* wait for next part of transfer */
- return NULL;
- }
-
- if (req->status == -EINPROGRESS)
- req->status = 0;
-
- list_del_init(&req->list);
- cdns3_start_all_request(priv_dev, priv_ep);
- return req;
- }
-
- return &priv_req->request;
-}
-
-int cdns3_wa2_gadget_ep_queue(struct cdns3_device *priv_dev,
- struct cdns3_endpoint *priv_ep,
- struct cdns3_request *priv_req)
-{
- int deferred = 0;
-
- /*
- * If transfer was queued before DESCMISS appear than we
- * can disable handling of DESCMISS interrupt. Driver assumes that it
- * can disable special treatment for this endpoint.
- */
- if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) {
- u32 reg;
-
- cdns3_select_ep(priv_dev, priv_ep->num | priv_ep->dir);
- priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET;
- reg = readl(&priv_dev->regs->ep_sts_en);
- reg &= ~EP_STS_EN_DESCMISEN;
- trace_cdns3_wa2(priv_ep, "workaround disabled\n");
- writel(reg, &priv_dev->regs->ep_sts_en);
- }
-
- if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) {
- u8 pending_empty = list_empty(&priv_ep->pending_req_list);
- u8 descmiss_empty = list_empty(&priv_ep->wa2_descmiss_req_list);
-
- /*
- * DESCMISS transfer has been finished, so data will be
- * directly copied from internal allocated usb_request
- * objects.
- */
- if (pending_empty && !descmiss_empty &&
- !(priv_req->flags & REQUEST_INTERNAL)) {
- cdns3_wa2_descmiss_copy_data(priv_ep,
- &priv_req->request);
-
- trace_cdns3_wa2(priv_ep, "get internal stored data");
-
- list_add_tail(&priv_req->request.list,
- &priv_ep->pending_req_list);
- cdns3_gadget_giveback(priv_ep, priv_req,
- priv_req->request.status);
-
- /*
- * Intentionally driver returns positive value as
- * correct value. It informs that transfer has
- * been finished.
- */
- return EINPROGRESS;
- }
-
- /*
- * Driver will wait for completion DESCMISS transfer,
- * before starts new, not DESCMISS transfer.
- */
- if (!pending_empty && !descmiss_empty) {
- trace_cdns3_wa2(priv_ep, "wait for pending transfer\n");
- deferred = 1;
- }
-
- if (priv_req->flags & REQUEST_INTERNAL)
- list_add_tail(&priv_req->list,
- &priv_ep->wa2_descmiss_req_list);
- }
-
- return deferred;
-}
-
-static void cdns3_wa2_remove_old_request(struct cdns3_endpoint *priv_ep)
-{
- struct cdns3_request *priv_req;
-
- while (!list_empty(&priv_ep->wa2_descmiss_req_list)) {
- u8 chain;
-
- priv_req = cdns3_next_priv_request(&priv_ep->wa2_descmiss_req_list);
- chain = !!(priv_req->flags & REQUEST_INTERNAL_CH);
-
- trace_cdns3_wa2(priv_ep, "removes eldest request");
-
- kfree(priv_req->request.buf);
- cdns3_gadget_ep_free_request(&priv_ep->endpoint,
- &priv_req->request);
- list_del_init(&priv_req->list);
- --priv_ep->wa2_counter;
-
- if (!chain)
- break;
- }
-}
-
-/**
- * cdns3_wa2_descmissing_packet - handles descriptor missing event.
- * @priv_dev: extended gadget object
- *
- * This function is used only for WA2. For more information see Work around 2
- * description.
- */
-static void cdns3_wa2_descmissing_packet(struct cdns3_endpoint *priv_ep)
-{
- struct cdns3_request *priv_req;
- struct usb_request *request;
-
- if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) {
- priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET;
- priv_ep->flags |= EP_QUIRK_EXTRA_BUF_EN;
- }
-
- trace_cdns3_wa2(priv_ep, "Description Missing detected\n");
-
- if (priv_ep->wa2_counter >= CDNS3_WA2_NUM_BUFFERS)
- cdns3_wa2_remove_old_request(priv_ep);
-
- request = cdns3_gadget_ep_alloc_request(&priv_ep->endpoint,
- GFP_ATOMIC);
- if (!request)
- goto err;
-
- priv_req = to_cdns3_request(request);
- priv_req->flags |= REQUEST_INTERNAL;
-
- /* if this field is still assigned it indicate that transfer related
- * with this request has not been finished yet. Driver in this
- * case simply allocate next request and assign flag REQUEST_INTERNAL_CH
- * flag to previous one. It will indicate that current request is
- * part of the previous one.
- */
- if (priv_ep->descmis_req)
- priv_ep->descmis_req->flags |= REQUEST_INTERNAL_CH;
-
- priv_req->request.buf = kzalloc(CDNS3_DESCMIS_BUF_SIZE,
- GFP_ATOMIC);
- priv_ep->wa2_counter++;
-
- if (!priv_req->request.buf) {
- cdns3_gadget_ep_free_request(&priv_ep->endpoint, request);
- goto err;
- }
-
- priv_req->request.length = CDNS3_DESCMIS_BUF_SIZE;
- priv_ep->descmis_req = priv_req;
-
- __cdns3_gadget_ep_queue(&priv_ep->endpoint,
- &priv_ep->descmis_req->request,
- GFP_ATOMIC);
-
- return;
-
-err:
- dev_err(priv_ep->cdns3_dev->dev,
- "Failed: No sufficient memory for DESCMIS\n");
-}
-
/**
* cdns3_gadget_giveback - call struct usb_request's ->complete callback
* @priv_ep: The endpoint to whom the request belongs to
@@ -654,13 +453,36 @@ void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
priv_req->flags &= ~(REQUEST_PENDING | REQUEST_UNALIGNED);
trace_cdns3_gadget_giveback(priv_req);
- if (priv_dev->dev_ver < DEV_VER_V2) {
- request = cdns3_wa2_gadget_giveback(priv_dev, priv_ep,
- priv_req);
- if (!request)
+ /* WA2: */
+ if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN &&
+ priv_req->flags & REQUEST_INTERNAL) {
+ struct usb_request *req;
+
+ req = cdns3_next_request(&priv_ep->deferred_req_list);
+ request = req;
+ priv_ep->descmis_req = NULL;
+
+ if (!req)
return;
+
+ cdns3_descmiss_copy_data(priv_ep, req);
+ if (!(priv_ep->flags & EP_QUIRK_END_TRANSFER) &&
+ req->length != req->actual) {
+ /* wait for next part of transfer */
+ return;
+ }
+
+ if (req->status == -EINPROGRESS)
+ req->status = 0;
+
+ list_del_init(&req->list);
+ cdns3_start_all_request(priv_dev, priv_ep);
}
+ /* Start all not pending request */
+ if (priv_ep->flags & EP_RING_FULL)
+ cdns3_start_all_request(priv_dev, priv_ep);
+
if (request->complete) {
spin_unlock(&priv_dev->lock);
usb_gadget_giveback_request(&priv_ep->endpoint,
@@ -674,10 +496,11 @@ void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
void cdns3_wa1_restore_cycle_bit(struct cdns3_endpoint *priv_ep)
{
+ struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+
/* Work around for stale data address in TRB*/
if (priv_ep->wa1_set) {
- trace_cdns3_wa1(priv_ep, "restore cycle bit");
-
+ cdns3_dbg(priv_dev, "WA1: update cycle bit\n");
priv_ep->wa1_set = 0;
priv_ep->wa1_trb_index = 0xFFFF;
if (priv_ep->wa1_cycle_bit) {
@@ -690,35 +513,6 @@ void cdns3_wa1_restore_cycle_bit(struct cdns3_endpoint *priv_ep)
}
}
-static void cdns3_free_aligned_request_buf(struct work_struct *work)
-{
- struct cdns3_device *priv_dev = container_of(work, struct cdns3_device,
- aligned_buf_wq);
- struct cdns3_aligned_buf *buf, *tmp;
- unsigned long flags;
-
- spin_lock_irqsave(&priv_dev->lock, flags);
-
- list_for_each_entry_safe(buf, tmp, &priv_dev->aligned_buf_list, list) {
- if (!buf->in_use) {
- list_del(&buf->list);
-
- /*
- * Re-enable interrupts to free DMA capable memory.
- * Driver can't free this memory with disabled
- * interrupts.
- */
- spin_unlock_irqrestore(&priv_dev->lock, flags);
- dma_free_coherent(priv_dev->sysdev, buf->size,
- buf->buf, buf->dma);
- kfree(buf);
- spin_lock_irqsave(&priv_dev->lock, flags);
- }
- }
-
- spin_unlock_irqrestore(&priv_dev->lock, flags);
-}
-
static int cdns3_prepare_aligned_request_buf(struct cdns3_request *priv_req)
{
struct cdns3_endpoint *priv_ep = priv_req->priv_ep;
@@ -750,8 +544,7 @@ static int cdns3_prepare_aligned_request_buf(struct cdns3_request *priv_req)
if (priv_req->aligned_buf) {
trace_cdns3_free_aligned_request(priv_req);
priv_req->aligned_buf->in_use = 0;
- queue_work(system_freezable_wq,
- &priv_dev->aligned_buf_wq);
+ priv_dev->run_garbage_colector = 1;
}
buf->in_use = 1;
@@ -772,41 +565,6 @@ static int cdns3_prepare_aligned_request_buf(struct cdns3_request *priv_req)
return 0;
}
-static int cdns3_wa1_update_guard(struct cdns3_endpoint *priv_ep,
- struct cdns3_trb *trb)
-{
- struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
-
- if (!priv_ep->wa1_set) {
- u32 doorbell;
-
- doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY);
-
- if (doorbell) {
- priv_ep->wa1_cycle_bit = priv_ep->pcs ? TRB_CYCLE : 0;
- priv_ep->wa1_set = 1;
- priv_ep->wa1_trb = trb;
- priv_ep->wa1_trb_index = priv_ep->enqueue;
- trace_cdns3_wa1(priv_ep, "set guard");
- return 0;
- }
- }
- return 1;
-}
-
-static void cdns3_wa1_tray_restore_cycle_bit(struct cdns3_device *priv_dev,
- struct cdns3_endpoint *priv_ep)
-{
- int dma_index;
- u32 doorbell;
-
- doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY);
- dma_index = cdns3_get_dma_pos(priv_dev, priv_ep);
-
- if (!doorbell || dma_index != priv_ep->wa1_trb_index)
- cdns3_wa1_restore_cycle_bit(priv_ep);
-}
-
/**
* cdns3_ep_run_transfer - start transfer on no-default endpoint hardware
* @priv_ep: endpoint object
@@ -820,8 +578,11 @@ int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
struct cdns3_request *priv_req;
struct cdns3_trb *trb;
dma_addr_t trb_dma;
+ int prev_enqueue;
u32 togle_pcs = 1;
int sg_iter = 0;
+ int dma_index;
+ u32 doorbell;
int num_trb;
int address;
u32 control;
@@ -851,68 +612,54 @@ int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
trb = priv_ep->trb_pool + priv_ep->enqueue;
priv_req->start_trb = priv_ep->enqueue;
priv_req->trb = trb;
-
- cdns3_select_ep(priv_ep->cdns3_dev, address);
+ prev_enqueue = priv_ep->enqueue;
/* prepare ring */
if ((priv_ep->enqueue + num_trb) >= (priv_ep->num_trbs - 1)) {
- struct cdns3_trb *link_trb;
- int doorbell, dma_index;
- u32 ch_bit = 0;
-
- doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY);
- dma_index = cdns3_get_dma_pos(priv_dev, priv_ep);
-
- /* Driver can't update LINK TRB if it is current processed. */
- if (doorbell && dma_index == priv_ep->num_trbs - 1) {
- priv_ep->flags |= EP_DEFERRED_DRDY;
- return -ENOBUFS;
- }
-
/*updating C bt in Link TRB before starting DMA*/
- link_trb = priv_ep->trb_pool + (priv_ep->num_trbs - 1);
- /*
- * For TRs size equal 2 enabling TRB_CHAIN for epXin causes
- * that DMA stuck at the LINK TRB.
- * On the other hand, removing TRB_CHAIN for longer TRs for
- * epXout cause that DMA stuck after handling LINK TRB.
- * To eliminate this strange behavioral driver set TRB_CHAIN
- * bit only for TR size > 2.
- */
- if (priv_ep->type == USB_ENDPOINT_XFER_ISOC ||
- TRBS_PER_SEGMENT > 2)
- ch_bit = TRB_CHAIN;
-
+ struct cdns3_trb *link_trb = priv_ep->trb_pool +
+ (priv_ep->num_trbs - 1);
link_trb->control = ((priv_ep->pcs) ? TRB_CYCLE : 0) |
- TRB_TYPE(TRB_LINK) | TRB_TOGGLE | ch_bit;
+ TRB_TYPE(TRB_LINK) | TRB_CHAIN |
+ TRB_TOGGLE;
}
- if (priv_dev->dev_ver <= DEV_VER_V2)
- togle_pcs = cdns3_wa1_update_guard(priv_ep, trb);
+ /* arm transfer on selected endpoint */
+ cdns3_select_ep(priv_ep->cdns3_dev, address);
+
+ doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY);
+
+ if (!priv_ep->wa1_set) {
+ if (doorbell) {
+ priv_ep->wa1_cycle_bit = priv_ep->pcs ? TRB_CYCLE : 0;
+ priv_ep->wa1_set = 1;
+ priv_ep->wa1_trb = trb;
+ priv_ep->wa1_trb_index = priv_ep->enqueue;
+ togle_pcs = 0;
+ cdns3_dbg(priv_dev, "WA1 set guard\n");
+ }
+ }
/* set incorrect Cycle Bit for first trb*/
control = priv_ep->pcs ? 0 : TRB_CYCLE;
-
do {
u32 length;
- u16 td_size = 0;
+ u8 td_size = 0;
/* fill TRB */
control |= TRB_TYPE(TRB_NORMAL);
trb->buffer = TRB_BUFFER(request->num_sgs == 0
? trb_dma : request->sg[sg_iter].dma_address);
-
if (likely(!request->num_sgs))
length = request->length;
else
length = request->sg[sg_iter].length;
- if (likely(priv_dev->dev_ver >= DEV_VER_V2))
+ if (priv_dev->dev_ver == DEV_VER_V2)
td_size = DIV_ROUND_UP(length,
priv_ep->endpoint.maxpacket);
- trb->length = TRB_BURST_LEN(priv_ep->trb_burst_size) |
- TRB_LEN(length);
+ trb->length = TRB_BURST_LEN(16) | TRB_LEN(length);
if (priv_dev->gadget.speed == USB_SPEED_SUPER)
trb->length |= TRB_TDL_SS_SIZE(td_size);
else
@@ -935,12 +682,9 @@ int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
control |= pcs | TRB_IOC | TRB_ISP;
}
- if (sg_iter)
- trb->control = control;
- else
- priv_req->trb->control = control;
-
+ trb->control = control;
control = 0;
+
++sg_iter;
priv_req->end_trb = priv_ep->enqueue;
cdns3_ep_inc_enq(priv_ep);
@@ -951,6 +695,7 @@ int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
priv_req->flags |= REQUEST_PENDING;
+ /* give the TD to the consumer*/
if (sg_iter == 1)
trb->control |= TRB_IOC | TRB_ISP;
@@ -959,12 +704,18 @@ int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
*/
wmb();
- /* give the TD to the consumer*/
if (togle_pcs)
- trb->control = trb->control ^ 1;
+ trb->control = trb->control ^ 1;
- if (priv_dev->dev_ver <= DEV_VER_V2)
- cdns3_wa1_tray_restore_cycle_bit(priv_dev, priv_ep);
+ doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY);
+ dma_index = (readl(&priv_dev->regs->ep_traddr) -
+ priv_ep->trb_pool_dma) / TRB_SIZE;
+
+ cdns3_dbg(priv_dev, "dorbel %d, dma_index %d, prev_enqueu %d",
+ doorbell, dma_index, prev_enqueue);
+
+ if (!doorbell || dma_index != priv_ep->wa1_trb_index)
+ cdns3_wa1_restore_cycle_bit(priv_ep);
trace_cdns3_prepare_trb(priv_ep, priv_req->trb);
@@ -979,26 +730,17 @@ int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
* enabling endpoint.
*/
if (priv_ep->flags & EP_UPDATE_EP_TRBADDR) {
- /*
- * Until SW is not ready to handle the OUT transfer the ISO OUT
- * Endpoint should be disabled (EP_CFG.ENABLE = 0).
- * EP_CFG_ENABLE must be set before updating ep_traddr.
- */
- if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && !priv_ep->dir &&
- !(priv_ep->flags & EP_QUIRK_ISO_OUT_EN)) {
- priv_ep->flags |= EP_QUIRK_ISO_OUT_EN;
- cdns3_set_register_bit(&priv_dev->regs->ep_cfg,
- EP_CFG_ENABLE);
- }
-
writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma +
priv_req->start_trb * TRB_SIZE),
- &priv_dev->regs->ep_traddr);
+ &priv_dev->regs->ep_traddr);
+
+ cdns3_dbg(priv_ep->cdns3_dev, "Update ep_trbaddr for %s to %08x\n",
+ priv_ep->name, readl(&priv_dev->regs->ep_traddr));
priv_ep->flags &= ~EP_UPDATE_EP_TRBADDR;
}
- if (!priv_ep->wa1_set && !(priv_ep->flags & EP_STALLED)) {
+ if (!priv_ep->wa1_set && !(priv_ep->flags & EP_STALL)) {
trace_cdns3_ring(priv_ep);
/*clearing TRBERR and EP_STS_DESCMIS before seting DRDY*/
writel(EP_STS_TRBERR | EP_STS_DESCMIS, &priv_dev->regs->ep_sts);
@@ -1007,9 +749,6 @@ int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
readl(&priv_dev->regs->ep_traddr));
}
- /* WORKAROUND for transition to L0 */
- __cdns3_gadget_wakeup(priv_dev);
-
return 0;
}
@@ -1017,7 +756,7 @@ void cdns3_set_hw_configuration(struct cdns3_device *priv_dev)
{
struct cdns3_endpoint *priv_ep;
struct usb_ep *ep;
- int val;
+ int result = 0;
if (priv_dev->hw_configured_flag)
return;
@@ -1029,10 +768,11 @@ void cdns3_set_hw_configuration(struct cdns3_device *priv_dev)
USB_CONF_U1EN | USB_CONF_U2EN);
/* wait until configuration set */
- readl_poll_timeout_atomic(&priv_dev->regs->usb_sts, val,
- val & USB_STS_CFGSTS_MASK, 1, 100);
+ result = cdns3_handshake(&priv_dev->regs->usb_sts,
+ USB_STS_CFGSTS_MASK, 1, 100);
priv_dev->hw_configured_flag = 1;
+// cdns3_allow_enable_l1(priv_dev, 1);
list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
if (ep->enabled) {
@@ -1066,9 +806,6 @@ void cdns3_set_hw_configuration(struct cdns3_device *priv_dev)
* some rules:
* 1. priv_ep->dequeue never exceed current_index.
* 2 priv_ep->enqueue never exceed priv_ep->dequeue
- * 3. exception: priv_ep->enqueue == priv_ep->dequeue
- * and priv_ep->free_trbs is zero.
- * This case indicate that TR is full.
*
* Then We can split recognition into two parts:
* Case 1 - priv_ep->dequeue < current_index
@@ -1092,29 +829,16 @@ static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep,
struct cdns3_trb *trb = priv_req->trb;
int current_index = 0;
int handled = 0;
- int doorbell;
- current_index = cdns3_get_dma_pos(priv_dev, priv_ep);
- doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY);
+ current_index = (readl(&priv_dev->regs->ep_traddr) -
+ priv_ep->trb_pool_dma) / TRB_SIZE;
trb = &priv_ep->trb_pool[priv_req->start_trb];
if ((trb->control & TRB_CYCLE) != priv_ep->ccs)
goto finish;
- if (doorbell == 1 && current_index == priv_ep->dequeue)
- goto finish;
-
- /* The corner case for TRBS_PER_SEGMENT equal 2). */
- if (TRBS_PER_SEGMENT == 2 && priv_ep->type != USB_ENDPOINT_XFER_ISOC) {
- handled = 1;
- goto finish;
- }
-
- if (priv_ep->enqueue == priv_ep->dequeue &&
- priv_ep->free_trbs == 0) {
- handled = 1;
- } else if (priv_ep->dequeue < current_index) {
+ if (priv_ep->dequeue < current_index) {
if ((current_index == (priv_ep->num_trbs - 1)) &&
!priv_ep->dequeue)
goto finish;
@@ -1145,21 +869,13 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev,
request = cdns3_next_request(&priv_ep->pending_req_list);
priv_req = to_cdns3_request(request);
- trb = priv_ep->trb_pool + priv_ep->dequeue;
-
- /* Request was dequeued and TRB was changed to TRB_LINK. */
- if (TRB_FIELD_TO_TYPE(trb->control) == TRB_LINK) {
- trace_cdns3_complete_trb(priv_ep, trb);
- cdns3_move_deq_to_next_trb(priv_req);
- }
-
/* Re-select endpoint. It could be changed by other CPU during
* handling usb_gadget_giveback_request.
*/
cdns3_select_ep(priv_dev, priv_ep->endpoint.address);
if (!cdns3_request_handled(priv_ep, priv_req))
- goto prepare_next_td;
+ return;
trb = priv_ep->trb_pool + priv_ep->dequeue;
trace_cdns3_complete_trb(priv_ep, trb);
@@ -1172,17 +888,8 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev,
request->actual = TRB_LEN(le32_to_cpu(trb->length));
cdns3_move_deq_to_next_trb(priv_req);
cdns3_gadget_giveback(priv_ep, priv_req, 0);
-
- if (priv_ep->type != USB_ENDPOINT_XFER_ISOC &&
- TRBS_PER_SEGMENT == 2)
- break;
}
priv_ep->flags &= ~EP_PENDING_REQUEST;
-
-prepare_next_td:
- if (!(priv_ep->flags & EP_STALLED) &&
- !(priv_ep->flags & EP_STALL_PENDING))
- cdns3_start_all_request(priv_dev, priv_ep);
}
void cdns3_rearm_transfer(struct cdns3_endpoint *priv_ep, u8 rearm)
@@ -1198,14 +905,65 @@ void cdns3_rearm_transfer(struct cdns3_endpoint *priv_ep, u8 rearm)
wmb();
writel(EP_CMD_DRDY, &priv_dev->regs->ep_cmd);
- __cdns3_gadget_wakeup(priv_dev);
-
trace_cdns3_doorbell_epx(priv_ep->name,
readl(&priv_dev->regs->ep_traddr));
}
}
/**
+ * cdns3_descmissing_packet - handles descriptor missing event.
+ * @priv_dev: extended gadget object
+ *
+ * This function is used only for WA2. For more information see Work around 2
+ * description.
+ */
+static int cdns3_descmissing_packet(struct cdns3_endpoint *priv_ep)
+{
+ struct cdns3_request *priv_req;
+ struct usb_request *request;
+
+ if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) {
+ priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET;
+ priv_ep->flags |= EP_QUIRK_EXTRA_BUF_EN;
+ }
+
+ cdns3_dbg(priv_ep->cdns3_dev, "WA2: Description Missing detected\n");
+
+ request = cdns3_gadget_ep_alloc_request(&priv_ep->endpoint,
+ GFP_ATOMIC);
+ if (!request)
+ return -ENOMEM;
+
+ priv_req = to_cdns3_request(request);
+ priv_req->flags |= REQUEST_INTERNAL;
+
+ /* if this field is still assigned it indicate that transfer related
+ * with this request has not been finished yet. Driver in this
+ * case simply allocate next request and assign flag REQUEST_INTERNAL_CH
+ * flag to previous one. It will indicate that current request is
+ * part of the previous one.
+ */
+ if (priv_ep->descmis_req)
+ priv_ep->descmis_req->flags |= REQUEST_INTERNAL_CH;
+
+ priv_req->request.buf = kzalloc(CDNS3_DESCMIS_BUF_SIZE,
+ GFP_ATOMIC);
+ if (!priv_req->request.buf) {
+ cdns3_gadget_ep_free_request(&priv_ep->endpoint, request);
+ return -ENOMEM;
+ }
+
+ priv_req->request.length = CDNS3_DESCMIS_BUF_SIZE;
+ priv_ep->descmis_req = priv_req;
+
+ __cdns3_gadget_ep_queue(&priv_ep->endpoint,
+ &priv_ep->descmis_req->request,
+ GFP_ATOMIC);
+
+ return 0;
+}
+
+/**
* cdns3_check_ep_interrupt_proceed - Processes interrupt related to endpoint
* @priv_ep: endpoint object
*
@@ -1224,12 +982,6 @@ static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep)
writel(ep_sts_reg, &priv_dev->regs->ep_sts);
if (ep_sts_reg & EP_STS_TRBERR) {
- if (priv_ep->flags & EP_STALL_PENDING &&
- !(ep_sts_reg & EP_STS_DESCMIS &&
- priv_dev->dev_ver < DEV_VER_V2)) {
- cdns3_ep_stall_flush(priv_ep);
- }
-
/*
* For isochronous transfer driver completes request on
* IOC or on TRBERR. IOC appears only when device receive
@@ -1238,25 +990,10 @@ static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep)
* on TRBERR event.
*/
if (priv_ep->type == USB_ENDPOINT_XFER_ISOC &&
- !priv_ep->wa1_set) {
- if (!priv_ep->dir) {
- u32 ep_cfg = readl(&priv_dev->regs->ep_cfg);
-
- ep_cfg &= ~EP_CFG_ENABLE;
- writel(ep_cfg, &priv_dev->regs->ep_cfg);
- priv_ep->flags &= ~EP_QUIRK_ISO_OUT_EN;
- }
+ !priv_ep->wa1_set)
cdns3_transfer_completed(priv_dev, priv_ep);
- } else if (!(priv_ep->flags & EP_STALLED) &&
- !(priv_ep->flags & EP_STALL_PENDING)) {
- if (priv_ep->flags & EP_DEFERRED_DRDY) {
- priv_ep->flags &= ~EP_DEFERRED_DRDY;
- cdns3_start_all_request(priv_dev, priv_ep);
- } else {
- cdns3_rearm_transfer(priv_ep,
- priv_ep->wa1_set);
- }
- }
+ else
+ cdns3_rearm_transfer(priv_ep, priv_ep->wa1_set);
}
if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
@@ -1276,20 +1013,16 @@ static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep)
* priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN.
* In other cases this interrupt will be disabled/
*/
- if (ep_sts_reg & EP_STS_DESCMIS && priv_dev->dev_ver < DEV_VER_V2 &&
- !(priv_ep->flags & EP_STALLED))
- cdns3_wa2_descmissing_packet(priv_ep);
+ if (ep_sts_reg & EP_STS_DESCMIS) {
+ int err;
- return 0;
-}
-
-static void cdns3_disconnect_gadget(struct cdns3_device *priv_dev)
-{
- if (priv_dev->gadget_driver && priv_dev->gadget_driver->disconnect) {
- spin_unlock(&priv_dev->lock);
- priv_dev->gadget_driver->disconnect(&priv_dev->gadget);
- spin_lock(&priv_dev->lock);
+ err = cdns3_descmissing_packet(priv_ep);
+ if (err)
+ dev_err(priv_dev->dev,
+ "Failed: No sufficient memory for DESCMIS\n");
}
+
+ return 0;
}
/**
@@ -1304,16 +1037,6 @@ static void cdns3_check_usb_interrupt_proceed(struct cdns3_device *priv_dev,
int speed = 0;
trace_cdns3_usb_irq(priv_dev, usb_ists);
- if (usb_ists & USB_ISTS_L1ENTI) {
- /*
- * WORKAROUND: CDNS3 controller has issue with hardware resuming
- * from L1. To fix it, if any DMA transfer is pending driver
- * must starts driving resume signal immediately.
- */
- if (readl(&priv_dev->regs->drbl))
- __cdns3_gadget_wakeup(priv_dev);
- }
-
/* Connection detected */
if (usb_ists & (USB_ISTS_CON2I | USB_ISTS_CONI)) {
speed = cdns3_get_speed(priv_dev);
@@ -1324,63 +1047,56 @@ static void cdns3_check_usb_interrupt_proceed(struct cdns3_device *priv_dev,
/* Disconnection detected */
if (usb_ists & (USB_ISTS_DIS2I | USB_ISTS_DISI)) {
- cdns3_disconnect_gadget(priv_dev);
- priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
- usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED);
- cdns3_hw_reset_eps_config(priv_dev);
- }
-
- if (usb_ists & (USB_ISTS_L2ENTI | USB_ISTS_U3ENTI)) {
if (priv_dev->gadget_driver &&
- priv_dev->gadget_driver->suspend) {
+ priv_dev->gadget_driver->disconnect &&
+ priv_dev->gadget.state ==
+ USB_STATE_CONFIGURED) {
spin_unlock(&priv_dev->lock);
- priv_dev->gadget_driver->suspend(&priv_dev->gadget);
+ priv_dev->gadget_driver->disconnect(&priv_dev->gadget);
spin_lock(&priv_dev->lock);
}
- }
- if (usb_ists & (USB_ISTS_L2EXTI | USB_ISTS_U3EXTI)) {
- if (priv_dev->gadget_driver &&
- priv_dev->gadget_driver->resume) {
- spin_unlock(&priv_dev->lock);
- priv_dev->gadget_driver->resume(&priv_dev->gadget);
- spin_lock(&priv_dev->lock);
- }
+ priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
+ usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED);
+ cdns3_hw_reset_eps_config(priv_dev);
}
/* reset*/
if (usb_ists & (USB_ISTS_UWRESI | USB_ISTS_UHRESI | USB_ISTS_U2RESI)) {
- if (priv_dev->gadget_driver) {
+ if (priv_dev->gadget_driver &&
+ priv_dev->gadget_driver->reset &&
+ priv_dev->gadget.state ==
+ USB_STATE_CONFIGURED) {
spin_unlock(&priv_dev->lock);
- usb_gadget_udc_reset(&priv_dev->gadget,
- priv_dev->gadget_driver);
+ priv_dev->gadget_driver->reset(&priv_dev->gadget);
spin_lock(&priv_dev->lock);
-
- /*read again to check the actual speed*/
- speed = cdns3_get_speed(priv_dev);
- priv_dev->gadget.speed = speed;
- cdns3_hw_reset_eps_config(priv_dev);
- cdns3_ep0_config(priv_dev);
}
+
+ /*read again to check the actual speed*/
+ speed = cdns3_get_speed(priv_dev);
+ usb_gadget_set_state(&priv_dev->gadget, USB_STATE_DEFAULT);
+ priv_dev->gadget.speed = speed;
+ cdns3_hw_reset_eps_config(priv_dev);
+ cdns3_ep0_config(priv_dev);
}
}
/**
* cdns3_device_irq_handler- interrupt handler for device part of controller
*
- * @irq: irq number for cdns3 core device
- * @data: structure of cdns3
+ * @cdns: structure of cdns3
*
* Returns IRQ_HANDLED or IRQ_NONE
*/
-static irqreturn_t cdns3_device_irq_handler(int irq, void *data)
+static irqreturn_t cdns3_device_irq_handler(struct cdns3 *cdns)
{
struct cdns3_device *priv_dev;
- struct cdns3 *cdns = data;
irqreturn_t ret = IRQ_NONE;
+ unsigned long flags;
u32 reg;
priv_dev = cdns->gadget_dev;
+ spin_lock_irqsave(&priv_dev->lock, flags);
/* check USB device interrupt */
reg = readl(&priv_dev->regs->usb_ists);
@@ -1400,11 +1116,16 @@ static irqreturn_t cdns3_device_irq_handler(int irq, void *data)
/* check endpoint interrupt */
reg = readl(&priv_dev->regs->ep_ists);
+
if (reg) {
- writel(0, &priv_dev->regs->ep_ien);
+ priv_dev->shadow_ep_en |= reg;
+ reg = ~reg & readl(&priv_dev->regs->ep_ien);
+ /* mask deferred interrupt. */
+ writel(reg, &priv_dev->regs->ep_ien);
ret = IRQ_WAKE_THREAD;
}
+ spin_unlock_irqrestore(&priv_dev->lock, flags);
return ret;
}
@@ -1417,12 +1138,12 @@ static irqreturn_t cdns3_device_irq_handler(int irq, void *data)
*
* Returns IRQ_HANDLED or IRQ_NONE
*/
-static irqreturn_t cdns3_device_thread_irq_handler(int irq, void *data)
+static irqreturn_t cdns3_device_thread_irq_handler(struct cdns3 *cdns)
{
struct cdns3_device *priv_dev;
- struct cdns3 *cdns = data;
irqreturn_t ret = IRQ_NONE;
unsigned long flags;
+ u32 ep_ien;
int bit;
u32 reg;
@@ -1458,14 +1179,38 @@ static irqreturn_t cdns3_device_thread_irq_handler(int irq, void *data)
for_each_set_bit(bit, (unsigned long *)&reg,
sizeof(u32) * BITS_PER_BYTE) {
+ priv_dev->shadow_ep_en |= BIT(bit);
cdns3_check_ep_interrupt_proceed(priv_dev->eps[bit]);
ret = IRQ_HANDLED;
}
+ if (priv_dev->run_garbage_colector) {
+ struct cdns3_aligned_buf *buf, *tmp;
+
+ list_for_each_entry_safe(buf, tmp, &priv_dev->aligned_buf_list,
+ list) {
+ if (!buf->in_use) {
+ list_del(&buf->list);
+
+ spin_unlock_irqrestore(&priv_dev->lock, flags);
+ dma_free_coherent(priv_dev->sysdev, buf->size,
+ buf->buf,
+ buf->dma);
+ spin_lock_irqsave(&priv_dev->lock, flags);
+
+ kfree(buf);
+ }
+ }
+
+ priv_dev->run_garbage_colector = 0;
+ }
+
irqend:
- writel(~0, &priv_dev->regs->ep_ien);
+ ep_ien = readl(&priv_dev->regs->ep_ien) | priv_dev->shadow_ep_en;
+ priv_dev->shadow_ep_en = 0;
+ /* Unmask all handled EP interrupts */
+ writel(ep_ien, &priv_dev->regs->ep_ien);
spin_unlock_irqrestore(&priv_dev->lock, flags);
-
return ret;
}
@@ -1477,76 +1222,26 @@ irqend:
*
* @priv_dev: extended gadget object
* @size: the size (KB) for EP would like to allocate
- * @is_in: endpoint direction
*
* Return 0 if the required size can met or negative value on failure
*/
+#define CDNS3_ONCHIP_BUF_SIZE 16
static int cdns3_ep_onchip_buffer_reserve(struct cdns3_device *priv_dev,
int size, int is_in)
{
- int remained;
-
- /* 2KB are reserved for EP0*/
- remained = priv_dev->onchip_buffers - priv_dev->onchip_used_size - 2;
-
if (is_in) {
- if (remained < size)
- return -EPERM;
-
- priv_dev->onchip_used_size += size;
- } else {
- int required;
-
- /**
- * ALL OUT EPs are shared the same chunk onchip memory, so
- * driver checks if it already has assigned enough buffers
- */
- if (priv_dev->out_mem_is_allocated >= size)
- return 0;
-
- required = size - priv_dev->out_mem_is_allocated;
-
- if (required > remained)
- return -EPERM;
-
- priv_dev->out_mem_is_allocated += required;
- priv_dev->onchip_used_size += required;
+ priv_dev->onchip_mem_allocated_size += size;
+ } else if (!priv_dev->out_mem_is_allocated) {
+ /* ALL OUT EPs are shared the same chunk onchip memory */
+ priv_dev->onchip_mem_allocated_size += size;
+ priv_dev->out_mem_is_allocated = 1;
}
- return 0;
-}
-
-void cdns3_configure_dmult(struct cdns3_device *priv_dev,
- struct cdns3_endpoint *priv_ep)
-{
- struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
-
- /* For dev_ver > DEV_VER_V2 DMULT is configured per endpoint */
- if (priv_dev->dev_ver <= DEV_VER_V2)
- writel(USB_CONF_DMULT, &regs->usb_conf);
-
- if (priv_dev->dev_ver == DEV_VER_V2)
- writel(USB_CONF2_EN_TDL_TRB, &regs->usb_conf2);
-
- if (priv_dev->dev_ver >= DEV_VER_V3 && priv_ep) {
- u32 mask;
-
- if (priv_ep->dir)
- mask = BIT(priv_ep->num + 16);
- else
- mask = BIT(priv_ep->num);
-
- if (priv_ep->type != USB_ENDPOINT_XFER_ISOC) {
- cdns3_set_register_bit(&regs->tdl_from_trb, mask);
- cdns3_set_register_bit(&regs->tdl_beh, mask);
- cdns3_set_register_bit(&regs->tdl_beh2, mask);
- cdns3_set_register_bit(&regs->dma_adv_td, mask);
- }
-
- if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && !priv_ep->dir)
- cdns3_set_register_bit(&regs->tdl_from_trb, mask);
-
- cdns3_set_register_bit(&regs->dtrans, mask);
+ if (priv_dev->onchip_mem_allocated_size > CDNS3_ONCHIP_BUF_SIZE) {
+ priv_dev->onchip_mem_allocated_size -= size;
+ return -EPERM;
+ } else {
+ return 0;
}
}
@@ -1560,30 +1255,21 @@ void cdns3_ep_config(struct cdns3_endpoint *priv_ep)
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
u32 bEndpointAddress = priv_ep->num | priv_ep->dir;
u32 max_packet_size = 0;
+ u8 buffering;
u8 maxburst = 0;
u32 ep_cfg = 0;
- u8 buffering;
u8 mult = 0;
int ret;
buffering = CDNS3_EP_BUF_SIZE - 1;
- cdns3_configure_dmult(priv_dev, priv_ep);
-
switch (priv_ep->type) {
case USB_ENDPOINT_XFER_INT:
- ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_INT);
-
- if ((priv_dev->dev_ver == DEV_VER_V2 && !priv_ep->dir) ||
- priv_dev->dev_ver > DEV_VER_V2)
- ep_cfg |= EP_CFG_TDL_CHK;
- break;
case USB_ENDPOINT_XFER_BULK:
- ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_BULK);
-
- if ((priv_dev->dev_ver == DEV_VER_V2 && !priv_ep->dir) ||
- priv_dev->dev_ver > DEV_VER_V2)
+ ep_cfg = EP_CFG_EPTYPE(priv_ep->type);
+ if (priv_dev->dev_ver == DEV_VER_V2 && !priv_ep->dir)
ep_cfg |= EP_CFG_TDL_CHK;
+
break;
default:
ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_ISOC);
@@ -1625,8 +1311,8 @@ void cdns3_ep_config(struct cdns3_endpoint *priv_ep)
else
priv_ep->trb_burst_size = 16;
- ret = cdns3_ep_onchip_buffer_reserve(priv_dev, buffering + 1,
- !!priv_ep->dir);
+ ret = cdns3_ep_onchip_buffer_reserve(priv_dev, buffering,
+ !!priv_ep->dir);
if (ret) {
dev_err(priv_dev->dev, "onchip mem is full, ep is invalid\n");
return;
@@ -1762,6 +1448,7 @@ void cdns3_gadget_ep_free_request(struct usb_ep *ep,
priv_req->aligned_buf->in_use = 0;
trace_cdns3_free_request(priv_req);
+ request = NULL;
kfree(priv_req);
}
@@ -1780,9 +1467,7 @@ static int cdns3_gadget_ep_enable(struct usb_ep *ep,
u32 reg = EP_STS_EN_TRBERREN;
u32 bEndpointAddress;
unsigned long flags;
- int enable = 1;
int ret;
- int val;
priv_ep = ep_to_cdns3_ep(ep);
priv_dev = priv_ep->cdns3_dev;
@@ -1828,42 +1513,30 @@ static int cdns3_gadget_ep_enable(struct usb_ep *ep,
writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
- ret = readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val,
- !(val & (EP_CMD_CSTALL | EP_CMD_EPRST)),
- 1, 1000);
-
- if (unlikely(ret)) {
- cdns3_free_trb_pool(priv_ep);
- ret = -EINVAL;
- goto exit;
- }
+ ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
+ EP_CMD_CSTALL | EP_CMD_EPRST, 0, 1000);
/* enable interrupt for selected endpoint */
cdns3_set_register_bit(&priv_dev->regs->ep_ien,
BIT(cdns3_ep_addr_to_index(bEndpointAddress)));
-
- if (priv_dev->dev_ver < DEV_VER_V2)
- cdns3_wa2_enable_detection(priv_dev, priv_ep, reg);
-
- writel(reg, &priv_dev->regs->ep_sts_en);
-
/*
- * For some versions of controller at some point during ISO OUT traffic
- * DMA reads Transfer Ring for the EP which has never got doorbell.
- * This issue was detected only on simulation, but to avoid this issue
- * driver add protection against it. To fix it driver enable ISO OUT
- * endpoint before setting DRBL. This special treatment of ISO OUT
- * endpoints are recommended by controller specification.
+ * WA2: Set flag for all not ISOC OUT endpoints. If this flag is set
+ * driver try to detect whether endpoint need additional internal
+ * buffer for unblocking on-chip FIFO buffer. This flag will be cleared
+ * if before first DESCMISS interrupt the DMA will be armed.
*/
- if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && !priv_ep->dir)
- enable = 0;
+ if (quirk_internal_buffer && (priv_dev->dev_ver < DEV_VER_V2)) {
+ if (!priv_ep->dir && priv_ep->type != USB_ENDPOINT_XFER_ISOC) {
+ priv_ep->flags |= EP_QUIRK_EXTRA_BUF_DET;
+ reg |= EP_STS_EN_DESCMISEN;
+ }
+ }
- if (enable)
- cdns3_set_register_bit(&priv_dev->regs->ep_cfg, EP_CFG_ENABLE);
+ writel(reg, &priv_dev->regs->ep_sts_en);
+
+ cdns3_set_register_bit(&priv_dev->regs->ep_cfg, EP_CFG_ENABLE);
ep->desc = desc;
- priv_ep->flags &= ~(EP_PENDING_REQUEST | EP_STALLED | EP_STALL_PENDING |
- EP_QUIRK_ISO_OUT_EN | EP_QUIRK_EXTRA_BUF_EN);
priv_ep->flags |= EP_ENABLED | EP_UPDATE_EP_TRBADDR;
priv_ep->wa1_set = 0;
priv_ep->enqueue = 0;
@@ -1894,7 +1567,6 @@ static int cdns3_gadget_ep_disable(struct usb_ep *ep)
unsigned long flags;
int ret = 0;
u32 ep_cfg;
- int val;
if (!ep) {
pr_err("usbss: invalid parameters\n");
@@ -1904,10 +1576,10 @@ static int cdns3_gadget_ep_disable(struct usb_ep *ep)
priv_ep = ep_to_cdns3_ep(ep);
priv_dev = priv_ep->cdns3_dev;
- if (dev_WARN_ONCE(priv_dev->dev, !(priv_ep->flags & EP_ENABLED),
- "%s is already disabled\n", priv_ep->name))
+ if (!(priv_ep->flags & EP_ENABLED) || (priv_ep->endpoint.desc == NULL))
return 0;
+ pm_runtime_get_sync(priv_dev->dev);
spin_lock_irqsave(&priv_dev->lock, flags);
trace_cdns3_gadget_ep_disable(priv_ep);
@@ -1918,22 +1590,10 @@ static int cdns3_gadget_ep_disable(struct usb_ep *ep)
ep_cfg &= ~EP_CFG_ENABLE;
writel(ep_cfg, &priv_dev->regs->ep_cfg);
- /**
- * Driver needs some time before resetting endpoint.
- * It need waits for clearing DBUSY bit or for timeout expired.
- * 10us is enough time for controller to stop transfer.
- */
- readl_poll_timeout_atomic(&priv_dev->regs->ep_sts, val,
- !(val & EP_STS_DBUSY), 1, 10);
writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
- readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val,
- !(val & (EP_CMD_CSTALL | EP_CMD_EPRST)),
- 1, 1000);
- if (unlikely(ret))
- dev_err(priv_dev->dev, "Timeout: %s resetting failed.\n",
- priv_ep->name);
-
+ ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
+ EP_CMD_CSTALL | EP_CMD_EPRST, 0, 1000);
while (!list_empty(&priv_ep->pending_req_list)) {
request = cdns3_next_request(&priv_ep->pending_req_list);
@@ -1941,14 +1601,12 @@ static int cdns3_gadget_ep_disable(struct usb_ep *ep)
-ESHUTDOWN);
}
- while (!list_empty(&priv_ep->wa2_descmiss_req_list)) {
- priv_req = cdns3_next_priv_request(&priv_ep->wa2_descmiss_req_list);
-
+ while (!list_empty(&priv_ep->descmiss_req_list)) {
+ priv_req = cdns3_next_priv_request(&priv_ep->descmiss_req_list);
kfree(priv_req->request.buf);
+ list_del_init(&priv_req->list);
cdns3_gadget_ep_free_request(&priv_ep->endpoint,
&priv_req->request);
- list_del_init(&priv_req->list);
- --priv_ep->wa2_counter;
}
while (!list_empty(&priv_ep->deferred_req_list)) {
@@ -1960,10 +1618,12 @@ static int cdns3_gadget_ep_disable(struct usb_ep *ep)
priv_ep->descmis_req = NULL;
- ep->desc = NULL;
- priv_ep->flags &= ~EP_ENABLED;
+ priv_ep->endpoint.desc = NULL;
+ priv_ep->flags = 0;
+ priv_ep->flags |= EP_CLAIMED;
spin_unlock_irqrestore(&priv_dev->lock, flags);
+ pm_runtime_put_sync(priv_dev->dev);
return ret;
}
@@ -1983,6 +1643,7 @@ static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
struct cdns3_request *priv_req;
+ int deferred = 0;
int ret = 0;
request->actual = 0;
@@ -1990,12 +1651,51 @@ static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
priv_req = to_cdns3_request(request);
trace_cdns3_ep_queue(priv_req);
- if (priv_dev->dev_ver < DEV_VER_V2) {
- ret = cdns3_wa2_gadget_ep_queue(priv_dev, priv_ep,
- priv_req);
+ /*
+ * WA2: if transfer was queued before DESCMISS appear than we
+ * can disable handling of DESCMISS interrupt. Driver assumes that it
+ * can disable special treatment for this endpoint.
+ */
+ if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) {
+ u32 reg;
- if (ret == EINPROGRESS)
- return 0;
+ cdns3_select_ep(priv_dev, priv_ep->num | priv_ep->dir);
+ priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET;
+ reg = readl(&priv_dev->regs->ep_sts_en);
+ reg &= ~EP_STS_EN_DESCMISEN;
+ writel(reg, &priv_dev->regs->ep_sts_en);
+ }
+
+ /* WA2 */
+ if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) {
+ u8 pending_empty = list_empty(&priv_ep->pending_req_list);
+ u8 descmiss_empty = list_empty(&priv_ep->descmiss_req_list);
+
+ /*
+ * DESCMISS transfer has been finished, so data will be
+ * directly copied from internal allocated usb_request
+ * objects.
+ */
+ if (pending_empty && !descmiss_empty &&
+ !(priv_req->flags & REQUEST_INTERNAL)) {
+ cdns3_descmiss_copy_data(priv_ep, request);
+ list_add_tail(&request->list,
+ &priv_ep->pending_req_list);
+ cdns3_gadget_giveback(priv_ep, priv_req,
+ request->status);
+ return ret;
+ }
+
+ /*
+ * WA2 driver will wait for completion DESCMISS transfer,
+ * before starts new, not DESCMISS transfer.
+ */
+ if (!pending_empty && !descmiss_empty)
+ deferred = 1;
+
+ if (priv_req->flags & REQUEST_INTERNAL)
+ list_add_tail(&priv_req->list,
+ &priv_ep->descmiss_req_list);
}
ret = cdns3_prepare_aligned_request_buf(priv_req);
@@ -2007,18 +1707,22 @@ static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
if (ret)
return ret;
- list_add_tail(&request->list, &priv_ep->deferred_req_list);
-
/*
* If hardware endpoint configuration has not been set yet then
* just queue request in deferred list. Transfer will be started in
* cdns3_set_hw_configuration.
*/
- if (priv_dev->hw_configured_flag && !(priv_ep->flags & EP_STALLED) &&
- !(priv_ep->flags & EP_STALL_PENDING))
- cdns3_start_all_request(priv_dev, priv_ep);
+ if (!priv_dev->hw_configured_flag)
+ deferred = 1;
+ else
+ ret = cdns3_ep_run_transfer(priv_ep, request);
- return 0;
+ if (ret || deferred)
+ list_add_tail(&request->list, &priv_ep->deferred_req_list);
+ else
+ list_add_tail(&request->list, &priv_ep->pending_req_list);
+
+ return ret;
}
static int cdns3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
@@ -2036,6 +1740,9 @@ static int cdns3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
priv_ep = ep_to_cdns3_ep(ep);
priv_dev = priv_ep->cdns3_dev;
+ if (!priv_ep->endpoint.desc)
+ return -EINVAL;
+
spin_lock_irqsave(&priv_dev->lock, flags);
ret = __cdns3_gadget_ep_queue(ep, request, gfp_flags);
@@ -2075,7 +1782,6 @@ int cdns3_gadget_ep_dequeue(struct usb_ep *ep,
struct usb_request *req, *req_temp;
struct cdns3_request *priv_req;
struct cdns3_trb *link_trb;
- u8 req_on_hw_ring = 0;
unsigned long flags;
int ret = 0;
@@ -2092,10 +1798,8 @@ int cdns3_gadget_ep_dequeue(struct usb_ep *ep,
list_for_each_entry_safe(req, req_temp, &priv_ep->pending_req_list,
list) {
- if (request == req) {
- req_on_hw_ring = 1;
+ if (request == req)
goto found;
- }
}
list_for_each_entry_safe(req, req_temp, &priv_ep->deferred_req_list,
@@ -2107,77 +1811,32 @@ int cdns3_gadget_ep_dequeue(struct usb_ep *ep,
goto not_found;
found:
+
+ if (priv_ep->wa1_trb == priv_req->trb)
+ cdns3_wa1_restore_cycle_bit(priv_ep);
+
link_trb = priv_req->trb;
+ cdns3_move_deq_to_next_trb(priv_req);
+ cdns3_gadget_giveback(priv_ep, priv_req, -ECONNRESET);
+
+ /* Update ring */
+ request = cdns3_next_request(&priv_ep->deferred_req_list);
+ if (request) {
+ priv_req = to_cdns3_request(request);
- /* Update ring only if removed request is on pending_req_list list */
- if (req_on_hw_ring) {
link_trb->buffer = TRB_BUFFER(priv_ep->trb_pool_dma +
(priv_req->start_trb * TRB_SIZE));
link_trb->control = (link_trb->control & TRB_CYCLE) |
- TRB_TYPE(TRB_LINK) | TRB_CHAIN;
-
- if (priv_ep->wa1_trb == priv_req->trb)
- cdns3_wa1_restore_cycle_bit(priv_ep);
+ TRB_TYPE(TRB_LINK) | TRB_CHAIN | TRB_TOGGLE;
+ } else {
+ priv_ep->flags |= EP_UPDATE_EP_TRBADDR;
}
- cdns3_gadget_giveback(priv_ep, priv_req, -ECONNRESET);
-
not_found:
spin_unlock_irqrestore(&priv_dev->lock, flags);
- return ret;
-}
-
-/**
- * __cdns3_gadget_ep_set_halt Sets stall on selected endpoint
- * Should be called after acquiring spin_lock and selecting ep
- * @ep: endpoint object to set stall on.
- */
-void __cdns3_gadget_ep_set_halt(struct cdns3_endpoint *priv_ep)
-{
- struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
-
- trace_cdns3_halt(priv_ep, 1, 0);
-
- if (!(priv_ep->flags & EP_STALLED)) {
- u32 ep_sts_reg = readl(&priv_dev->regs->ep_sts);
-
- if (!(ep_sts_reg & EP_STS_DBUSY))
- cdns3_ep_stall_flush(priv_ep);
- else
- priv_ep->flags |= EP_STALL_PENDING;
- }
-}
-
-/**
- * __cdns3_gadget_ep_clear_halt Clears stall on selected endpoint
- * Should be called after acquiring spin_lock and selecting ep
- * @ep: endpoint object to clear stall on
- */
-int __cdns3_gadget_ep_clear_halt(struct cdns3_endpoint *priv_ep)
-{
- struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
- struct usb_request *request;
- int ret;
- int val;
-
- trace_cdns3_halt(priv_ep, 0, 0);
-
- writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
+ if (ep == priv_dev->gadget.ep0)
+ flush_work(&priv_dev->pending_status_wq);
- /* wait for EPRST cleared */
- ret = readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val,
- !(val & EP_CMD_EPRST), 1, 100);
- if (ret)
- return -EINVAL;
-
- priv_ep->flags &= ~(EP_STALLED | EP_STALL_PENDING);
-
- request = cdns3_next_request(&priv_ep->pending_req_list);
-
- if (request)
- cdns3_rearm_transfer(priv_ep, 1);
-
- cdns3_start_all_request(priv_dev, priv_ep);
return ret;
}
@@ -2201,14 +1860,37 @@ int cdns3_gadget_ep_set_halt(struct usb_ep *ep, int value)
spin_lock_irqsave(&priv_dev->lock, flags);
cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
+ if (value) {
+ if (!list_empty(&priv_ep->pending_req_list)) {
+ ret = -EAGAIN;
+ goto finish;
+ }
- if (!value) {
- priv_ep->flags &= ~EP_WEDGE;
- ret = __cdns3_gadget_ep_clear_halt(priv_ep);
+ cdns3_ep_stall_flush(priv_ep);
} else {
- __cdns3_gadget_ep_set_halt(priv_ep);
+ priv_ep->flags &= ~EP_WEDGE;
+
+ cdns3_dbg(priv_ep->cdns3_dev, "Clear stalled endpoint %s\n",
+ priv_ep->name);
+
+ writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
+
+ /* wait for EPRST cleared */
+ ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
+ EP_CMD_EPRST, 0, 100);
+ if (unlikely(ret)) {
+ dev_err(priv_dev->dev,
+ "Clearing halt condition failed for %s\n",
+ priv_ep->name);
+ goto finish;
+
+ } else {
+ priv_ep->flags &= ~EP_STALL;
+ }
}
+ priv_ep->flags &= ~EP_PENDING_REQUEST;
+finish:
spin_unlock_irqrestore(&priv_dev->lock, flags);
return ret;
@@ -2240,31 +1922,9 @@ static int cdns3_gadget_get_frame(struct usb_gadget *gadget)
return readl(&priv_dev->regs->usb_itpn);
}
-int __cdns3_gadget_wakeup(struct cdns3_device *priv_dev)
-{
- enum usb_device_speed speed;
-
- speed = cdns3_get_speed(priv_dev);
-
- if (speed >= USB_SPEED_SUPER)
- return 0;
-
- /* Start driving resume signaling to indicate remote wakeup. */
- writel(USB_CONF_LGO_L0, &priv_dev->regs->usb_conf);
-
- return 0;
-}
-
static int cdns3_gadget_wakeup(struct usb_gadget *gadget)
{
- struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
- unsigned long flags;
- int ret = 0;
-
- spin_lock_irqsave(&priv_dev->lock, flags);
- ret = __cdns3_gadget_wakeup(priv_dev);
- spin_unlock_irqrestore(&priv_dev->lock, flags);
- return ret;
+ return 0;
}
static int cdns3_gadget_set_selfpowered(struct usb_gadget *gadget,
@@ -2283,6 +1943,9 @@ static int cdns3_gadget_pullup(struct usb_gadget *gadget, int is_on)
{
struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
+ if (!priv_dev->start_gadget)
+ return 0;
+
if (is_on)
writel(USB_CONF_DEVEN, &priv_dev->regs->usb_conf);
else
@@ -2294,19 +1957,20 @@ static int cdns3_gadget_pullup(struct usb_gadget *gadget, int is_on)
static void cdns3_gadget_config(struct cdns3_device *priv_dev)
{
struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
- u32 reg;
cdns3_ep0_config(priv_dev);
/* enable interrupts for endpoint 0 (in and out) */
writel(EP_IEN_EP_OUT0 | EP_IEN_EP_IN0, &regs->ep_ien);
+ priv_dev->dev_ver = readl(&priv_dev->regs->usb_cap6);
+ priv_dev->dev_ver = GET_DEV_BASE_VERSION(priv_dev->dev_ver);
/*
* Driver needs to modify LFPS minimal U1 Exit time for DEV_VER_TI_V1
* revision of controller.
*/
if (priv_dev->dev_ver == DEV_VER_TI_V1) {
- reg = readl(&regs->dbg_link1);
+ u32 reg = readl(&regs->dbg_link1);
reg &= ~DBG_LINK1_LFPS_MIN_GEN_U1_EXIT_MASK;
reg |= DBG_LINK1_LFPS_MIN_GEN_U1_EXIT(0x55) |
@@ -2314,21 +1978,14 @@ static void cdns3_gadget_config(struct cdns3_device *priv_dev)
writel(reg, &regs->dbg_link1);
}
- /*
- * By default some platforms has set protected access to memory.
- * This cause problem with cache, so driver restore non-secure
- * access to memory.
- */
- reg = readl(&regs->dma_axi_ctrl);
- reg |= DMA_AXI_CTRL_MARPROT(DMA_AXI_CTRL_NON_SECURE) |
- DMA_AXI_CTRL_MAWPROT(DMA_AXI_CTRL_NON_SECURE);
- writel(reg, &regs->dma_axi_ctrl);
-
/* enable generic interrupt*/
writel(USB_IEN_INIT, &regs->usb_ien);
writel(USB_CONF_CLK2OFFDS | USB_CONF_L1DS, &regs->usb_conf);
+ writel(USB_CONF_DMULT, &regs->usb_conf);
+ if (priv_dev->dev_ver == DEV_VER_V2)
+ writel(USB_CONF2_EN_TDL_TRB, &regs->usb_conf2);
- cdns3_configure_dmult(priv_dev, NULL);
+ cdns3_gadget_pullup(&priv_dev->gadget, 1);
}
/**
@@ -2343,33 +2000,13 @@ static int cdns3_gadget_udc_start(struct usb_gadget *gadget,
{
struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
unsigned long flags;
- enum usb_device_speed max_speed = driver->max_speed;
+ dev_dbg(priv_dev->dev, "%s begins\n", __func__);
spin_lock_irqsave(&priv_dev->lock, flags);
priv_dev->gadget_driver = driver;
-
- /* limit speed if necessary */
- max_speed = min(driver->max_speed, gadget->max_speed);
-
- switch (max_speed) {
- case USB_SPEED_FULL:
- writel(USB_CONF_SFORCE_FS, &priv_dev->regs->usb_conf);
- writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf);
- break;
- case USB_SPEED_HIGH:
- writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf);
- break;
- case USB_SPEED_SUPER:
- break;
- default:
- dev_err(priv_dev->dev,
- "invalid maximum_speed parameter %d\n",
- max_speed);
- /* fall through */
- case USB_SPEED_UNKNOWN:
- /* default to superspeed */
- max_speed = USB_SPEED_SUPER;
- break;
+ if (!priv_dev->start_gadget) {
+ spin_unlock_irqrestore(&priv_dev->lock, flags);
+ return 0;
}
cdns3_gadget_config(priv_dev);
@@ -2387,28 +2024,32 @@ static int cdns3_gadget_udc_stop(struct usb_gadget *gadget)
{
struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
struct cdns3_endpoint *priv_ep;
- u32 bEndpointAddress;
struct usb_ep *ep;
+ unsigned long flags;
int ret = 0;
- int val;
+ spin_lock_irqsave(&priv_dev->lock, flags);
priv_dev->gadget_driver = NULL;
- priv_dev->onchip_used_size = 0;
+ priv_dev->status_completion_no_call = 0;
+ priv_dev->onchip_mem_allocated_size = 0;
priv_dev->out_mem_is_allocated = 0;
priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
-
list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
priv_ep = ep_to_cdns3_ep(ep);
- bEndpointAddress = priv_ep->num | priv_ep->dir;
- cdns3_select_ep(priv_dev, bEndpointAddress);
- writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
- readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val,
- !(val & EP_CMD_EPRST), 1, 100);
-
priv_ep->flags &= ~EP_CLAIMED;
}
+ spin_unlock_irqrestore(&priv_dev->lock, flags);
+ if (!priv_dev->start_gadget)
+ return ret;
+
+ list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
+ priv_ep = ep_to_cdns3_ep(ep);
+ usb_ep_disable(ep);
+ cdns3_free_trb_pool(priv_ep);
+ }
+
/* disable interrupt for device */
writel(0, &priv_dev->regs->usb_ien);
writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
@@ -2430,14 +2071,14 @@ static void cdns3_free_all_eps(struct cdns3_device *priv_dev)
{
int i;
- /* ep0 OUT point to ep0 IN. */
+ /*ep0 OUT point to ep0 IN*/
priv_dev->eps[16] = NULL;
+ cdns3_free_trb_pool(priv_dev->eps[0]);
+
for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++)
- if (priv_dev->eps[i]) {
- cdns3_free_trb_pool(priv_dev->eps[i]);
+ if (priv_dev->eps[i])
devm_kfree(priv_dev->dev, priv_dev->eps[i]);
- }
}
/**
@@ -2455,9 +2096,9 @@ static int cdns3_init_eps(struct cdns3_device *priv_dev)
int ret = 0;
int i;
- /* Read it from USB_CAP3 to USB_CAP5 */
- ep_enabled_reg = readl(&priv_dev->regs->usb_cap3);
- iso_ep_reg = readl(&priv_dev->regs->usb_cap4);
+ /* Read it from USB_CAP3 and USB_CAP4 */
+ ep_enabled_reg = 0x00ff00ff;
+ iso_ep_reg = 0x00fe00fe;
dev_dbg(priv_dev->dev, "Initializing non-zero endpoints\n");
@@ -2476,8 +2117,10 @@ static int cdns3_init_eps(struct cdns3_device *priv_dev)
priv_ep = devm_kzalloc(priv_dev->dev, sizeof(*priv_ep),
GFP_KERNEL);
- if (!priv_ep)
+ if (!priv_ep) {
+ ret = -ENOMEM;
goto err;
+ }
/* set parent of endpoint object */
priv_ep->cdns3_dev = priv_dev;
@@ -2517,14 +2160,14 @@ static int cdns3_init_eps(struct cdns3_device *priv_dev)
priv_ep->flags = 0;
- dev_info(priv_dev->dev, "Initialized %s support: %s %s\n",
+ dev_dbg(priv_dev->dev, "Initialized %s support: %s %s\n",
priv_ep->name,
priv_ep->endpoint.caps.type_bulk ? "BULK, INT" : "",
priv_ep->endpoint.caps.type_iso ? "ISO" : "");
INIT_LIST_HEAD(&priv_ep->pending_req_list);
INIT_LIST_HEAD(&priv_ep->deferred_req_list);
- INIT_LIST_HEAD(&priv_ep->wa2_descmiss_req_list);
+ INIT_LIST_HEAD(&priv_ep->descmiss_req_list);
}
return 0;
@@ -2533,13 +2176,27 @@ err:
return -ENOMEM;
}
+static void cdns3_gadget_disable(struct cdns3 *cdns)
+{
+ struct cdns3_device *priv_dev;
+
+ priv_dev = cdns->gadget_dev;
+
+ if (priv_dev->gadget_driver) {
+ priv_dev->gadget_driver->disconnect(&priv_dev->gadget);
+ usb_gadget_disconnect(&priv_dev->gadget);
+ }
+
+ priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
+}
+
void cdns3_gadget_exit(struct cdns3 *cdns)
{
struct cdns3_device *priv_dev;
priv_dev = cdns->gadget_dev;
- devm_free_irq(cdns->dev, cdns->dev_irq, cdns);
+ cdns3_gadget_disable(cdns);
pm_runtime_mark_last_busy(cdns->dev);
pm_runtime_put_autosuspend(cdns->dev);
@@ -2552,6 +2209,7 @@ void cdns3_gadget_exit(struct cdns3 *cdns)
struct cdns3_aligned_buf *buf;
buf = cdns3_next_align_buf(&priv_dev->aligned_buf_list);
+
dma_free_coherent(priv_dev->sysdev, buf->size,
buf->buf,
buf->dma);
@@ -2566,14 +2224,32 @@ void cdns3_gadget_exit(struct cdns3 *cdns)
kfree(priv_dev->zlp_buf);
kfree(priv_dev);
cdns->gadget_dev = NULL;
- cdns3_drd_switch_gadget(cdns, 0);
}
static int cdns3_gadget_start(struct cdns3 *cdns)
{
+ struct cdns3_device *priv_dev = cdns->gadget_dev;
+ unsigned long flags;
+
+ pm_runtime_get_sync(cdns->dev);
+ spin_lock_irqsave(&priv_dev->lock, flags);
+ priv_dev->start_gadget = 1;
+ if (!priv_dev->gadget_driver) {
+ spin_unlock_irqrestore(&priv_dev->lock, flags);
+ return 0;
+ }
+
+ cdns3_gadget_config(priv_dev);
+ spin_unlock_irqrestore(&priv_dev->lock, flags);
+
+ return 0;
+}
+
+static int __cdns3_gadget_init(struct cdns3 *cdns)
+{
struct cdns3_device *priv_dev;
u32 max_speed;
- int ret;
+ int ret = 0;
priv_dev = kzalloc(sizeof(*priv_dev), GFP_KERNEL);
if (!priv_dev)
@@ -2584,18 +2260,6 @@ static int cdns3_gadget_start(struct cdns3 *cdns)
priv_dev->dev = cdns->dev;
priv_dev->regs = cdns->dev_regs;
- device_property_read_u16(priv_dev->dev, "cdns,on-chip-buff-size",
- &priv_dev->onchip_buffers);
-
- if (priv_dev->onchip_buffers <= 0) {
- u32 reg = readl(&priv_dev->regs->usb_cap2);
-
- priv_dev->onchip_buffers = USB_CAP2_ACTUAL_MEM_SIZE(reg);
- }
-
- if (!priv_dev->onchip_buffers)
- priv_dev->onchip_buffers = 256;
-
max_speed = usb_get_maximum_speed(cdns->dev);
/* Check the maximum_speed parameter */
@@ -2626,13 +2290,11 @@ static int cdns3_gadget_start(struct cdns3 *cdns)
INIT_WORK(&priv_dev->pending_status_wq,
cdns3_pending_setup_status_handler);
- INIT_WORK(&priv_dev->aligned_buf_wq,
- cdns3_free_aligned_request_buf);
-
/* initialize endpoint container */
INIT_LIST_HEAD(&priv_dev->gadget.ep_list);
INIT_LIST_HEAD(&priv_dev->aligned_buf_list);
+ pm_runtime_get_sync(cdns->dev);
ret = cdns3_init_eps(priv_dev);
if (ret) {
dev_err(priv_dev->dev, "Failed to create endpoints\n");
@@ -2643,21 +2305,11 @@ static int cdns3_gadget_start(struct cdns3 *cdns)
priv_dev->setup_buf = dma_alloc_coherent(priv_dev->sysdev, 8,
&priv_dev->setup_dma, GFP_DMA);
if (!priv_dev->setup_buf) {
+ dev_err(priv_dev->dev, "Failed to allocate memory for SETUP buffer\n");
ret = -ENOMEM;
goto err2;
}
- priv_dev->dev_ver = readl(&priv_dev->regs->usb_cap6);
-
- dev_dbg(priv_dev->dev, "Device Controller version: %08x\n",
- readl(&priv_dev->regs->usb_cap6));
- dev_dbg(priv_dev->dev, "USB Capabilities:: %08x\n",
- readl(&priv_dev->regs->usb_cap1));
- dev_dbg(priv_dev->dev, "On-Chip memory configuration: %08x\n",
- readl(&priv_dev->regs->usb_cap2));
-
- priv_dev->dev_ver = GET_DEV_BASE_VERSION(priv_dev->dev_ver);
-
priv_dev->zlp_buf = kzalloc(CDNS3_EP_ZLP_BUF_SIZE, GFP_KERNEL);
if (!priv_dev->zlp_buf) {
ret = -ENOMEM;
@@ -2672,6 +2324,11 @@ static int cdns3_gadget_start(struct cdns3 *cdns)
goto err4;
}
+ if (ret)
+ goto err4;
+
+
+ pm_runtime_put_sync(cdns->dev);
return 0;
err4:
kfree(priv_dev->zlp_buf);
@@ -2682,70 +2339,54 @@ err2:
cdns3_free_all_eps(priv_dev);
err1:
cdns->gadget_dev = NULL;
+ pm_runtime_put_sync(cdns->dev);
return ret;
}
-static int __cdns3_gadget_init(struct cdns3 *cdns)
-{
- int ret = 0;
-
- /* Ensure 32-bit DMA Mask in case we switched back from Host mode */
- ret = dma_set_mask_and_coherent(cdns->dev, DMA_BIT_MASK(32));
- if (ret) {
- dev_err(cdns->dev, "Failed to set dma mask: %d\n", ret);
- return ret;
- }
-
- cdns3_drd_switch_gadget(cdns, 1);
- pm_runtime_get_sync(cdns->dev);
-
- ret = cdns3_gadget_start(cdns);
- if (ret)
- return ret;
-
- /*
- * Because interrupt line can be shared with other components in
- * driver it can't use IRQF_ONESHOT flag here.
- */
- ret = devm_request_threaded_irq(cdns->dev, cdns->dev_irq,
- cdns3_device_irq_handler,
- cdns3_device_thread_irq_handler,
- IRQF_SHARED, dev_name(cdns->dev), cdns);
-
- if (ret)
- goto err0;
-
- return 0;
-err0:
- cdns3_gadget_exit(cdns);
- return ret;
-}
-
-static int cdns3_gadget_suspend(struct cdns3 *cdns, bool do_wakeup)
+static void __cdns3_gadget_stop(struct cdns3 *cdns)
{
struct cdns3_device *priv_dev = cdns->gadget_dev;
+ unsigned long flags;
- cdns3_disconnect_gadget(priv_dev);
-
- priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
+ cdns3_gadget_disable(cdns);
+ spin_lock_irqsave(&priv_dev->lock, flags);
usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED);
- cdns3_hw_reset_eps_config(priv_dev);
-
/* disable interrupt for device */
writel(0, &priv_dev->regs->usb_ien);
+ writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
+ priv_dev->start_gadget = 0;
+ spin_unlock_irqrestore(&priv_dev->lock, flags);
+}
+static void cdns3_gadget_stop(struct cdns3 *cdns)
+{
+ if (cdns->role == CDNS3_ROLE_GADGET)
+ __cdns3_gadget_stop(cdns);
+ pm_runtime_mark_last_busy(cdns->dev);
+ pm_runtime_put_autosuspend(cdns->dev);
+}
+
+static int cdns3_gadget_suspend(struct cdns3 *cdns, bool do_wakeup)
+{
+ __cdns3_gadget_stop(cdns);
return 0;
}
static int cdns3_gadget_resume(struct cdns3 *cdns, bool hibernated)
{
struct cdns3_device *priv_dev = cdns->gadget_dev;
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv_dev->lock, flags);
+ priv_dev->start_gadget = 1;
- if (!priv_dev->gadget_driver)
+ if (!priv_dev->gadget_driver) {
+ spin_unlock_irqrestore(&priv_dev->lock, flags);
return 0;
+ }
cdns3_gadget_config(priv_dev);
-
+ spin_unlock_irqrestore(&priv_dev->lock, flags);
return 0;
}
@@ -2764,13 +2405,14 @@ int cdns3_gadget_init(struct cdns3 *cdns)
if (!rdrv)
return -ENOMEM;
- rdrv->start = __cdns3_gadget_init;
- rdrv->stop = cdns3_gadget_exit;
+ rdrv->start = cdns3_gadget_start;
+ rdrv->stop = cdns3_gadget_stop;
rdrv->suspend = cdns3_gadget_suspend;
rdrv->resume = cdns3_gadget_resume;
- rdrv->state = CDNS3_ROLE_STATE_INACTIVE;
+ rdrv->irq = cdns3_device_irq_handler;
+ rdrv->thread_irq = cdns3_device_thread_irq_handler;
rdrv->name = "gadget";
- cdns->roles[USB_ROLE_DEVICE] = rdrv;
+ cdns->roles[CDNS3_ROLE_GADGET] = rdrv;
- return 0;
+ return __cdns3_gadget_init(cdns);
}
diff --git a/drivers/usb/cdns3/gadget.h b/drivers/usb/cdns3/gadget.h
index bc4024041ef2..bd470afa25ce 100644
--- a/drivers/usb/cdns3/gadget.h
+++ b/drivers/usb/cdns3/gadget.h
@@ -3,7 +3,7 @@
* USBSS device controller driver header file
*
* Copyright (C) 2018-2019 Cadence.
- * Copyright (C) 2017-2018 NXP
+ * Copyright (C) 2017-2019 NXP
*
* Author: Pawel Laszczak <pawell@cadence.com>
* Pawel Jez <pjez@cadence.com>
@@ -20,52 +20,42 @@
/**
* struct cdns3_usb_regs - device controller registers.
- * @usb_conf: Global Configuration.
- * @usb_sts: Global Status.
- * @usb_cmd: Global Command.
- * @usb_itpn: ITP/SOF number.
- * @usb_lpm: Global Command.
- * @usb_ien: USB Interrupt Enable.
- * @usb_ists: USB Interrupt Status.
- * @ep_sel: Endpoint Select.
- * @ep_traddr: Endpoint Transfer Ring Address.
- * @ep_cfg: Endpoint Configuration.
- * @ep_cmd: Endpoint Command.
- * @ep_sts: Endpoint Status.
- * @ep_sts_sid: Endpoint Status.
- * @ep_sts_en: Endpoint Status Enable.
- * @drbl: Doorbell.
- * @ep_ien: EP Interrupt Enable.
- * @ep_ists: EP Interrupt Status.
- * @usb_pwr: Global Power Configuration.
- * @usb_conf2: Global Configuration 2.
- * @usb_cap1: Capability 1.
- * @usb_cap2: Capability 2.
- * @usb_cap3: Capability 3.
- * @usb_cap4: Capability 4.
- * @usb_cap5: Capability 5.
- * @usb_cap6: Capability 6.
- * @usb_cpkt1: Custom Packet 1.
- * @usb_cpkt2: Custom Packet 2.
- * @usb_cpkt3: Custom Packet 3.
- * @ep_dma_ext_addr: Upper address for DMA operations.
- * @buf_addr: Address for On-chip Buffer operations.
- * @buf_data: Data for On-chip Buffer operations.
- * @buf_ctrl: On-chip Buffer Access Control.
- * @dtrans: DMA Transfer Mode.
- * @tdl_from_trb: Source of TD Configuration.
- * @tdl_beh: TDL Behavior Configuration.
- * @ep_tdl: Endpoint TDL.
- * @tdl_beh2: TDL Behavior 2 Configuration.
- * @dma_adv_td: DMA Advance TD Configuration.
+ * @usb_conf: Global Configuration Register.
+ * @usb_sts: Global Status Register.
+ * @usb_cmd: Global Command Register.
+ * @usb_itpn: ITP/SOF number Register.
+ * @usb_lpm: Global Command Register.
+ * @usb_ien: USB Interrupt Enable Register.
+ * @usb_ists: USB Interrupt Status Register.
+ * @ep_sel: Endpoint Select Register.
+ * @ep_traddr: Endpoint Transfer Ring Address Register.
+ * @ep_cfg: Endpoint Configuration Register.
+ * @ep_cmd: Endpoint Command Register.
+ * @ep_sts: Endpoint Status Register.
+ * @ep_sts_sid: Endpoint Status Register.
+ * @ep_sts_en: Endpoint Status Register Enable.
+ * @drbl: Doorbell Register.
+ * @ep_ien: EP Interrupt Enable Register.
+ * @ep_ists: EP Interrupt Status Register.
+ * @usb_pwr: Global Power Configuration Register.
+ * @usb_conf2: Global Configuration Register 2.
+ * @usb_cap1: Capability Register 1.
+ * @usb_cap2: Capability Register 2.
+ * @usb_cap3: Capability Register 3.
+ * @usb_cap4: Capability Register 4.
+ * @usb_cap5: Capability Register 5.
+ * @usb_cap6: Capability Register 6.
+ * @usb_cpkt1: Custom Packet Register 1.
+ * @usb_cpkt2: Custom Packet Register 2.
+ * @usb_cpkt3: Custom Packet Register 3.
* @reserved1: Reserved.
- * @cfg_regs: Configuration.
+ * @cfg_regs: Configuration registers.
* @reserved2: Reserved.
- * @dma_axi_ctrl: AXI Control.
+ * @dma_axi_ctrl: AXI Control register.
* @dma_axi_id: AXI ID register.
- * @dma_axi_cap: AXI Capability.
- * @dma_axi_ctrl0: AXI Control 0.
- * @dma_axi_ctrl1: AXI Control 1.
+ * @dma_axi_cap: AXI Capability register.
+ * @dma_axi_ctrl0: AXI Control 0 register.
+ * @dma_axi_ctrl1: AXI Control 1 register.
*/
struct cdns3_usb_regs {
__le32 usb_conf;
@@ -96,22 +86,12 @@ struct cdns3_usb_regs {
__le32 usb_cpkt1;
__le32 usb_cpkt2;
__le32 usb_cpkt3;
- __le32 ep_dma_ext_addr;
- __le32 buf_addr;
- __le32 buf_data;
- __le32 buf_ctrl;
- __le32 dtrans;
- __le32 tdl_from_trb;
- __le32 tdl_beh;
- __le32 ep_tdl;
- __le32 tdl_beh2;
- __le32 dma_adv_td;
- __le32 reserved1[26];
+ __le32 reserved1[36];
__le32 cfg_reg1;
__le32 dbg_link1;
__le32 dbg_link2;
__le32 cfg_regs[74];
- __le32 reserved2[51];
+ __le32 reserved2[34];
__le32 dma_axi_ctrl;
__le32 dma_axi_id;
__le32 dma_axi_cap;
@@ -138,9 +118,9 @@ struct cdns3_usb_regs {
#define USB_CONF_BENDIAN BIT(6)
/* Device software reset. */
#define USB_CONF_SWRST BIT(7)
-/* Singular DMA transfer mode. Only for VER < DEV_VER_V3*/
+/* Singular DMA transfer mode. */
#define USB_CONF_DSING BIT(8)
-/* Multiple DMA transfers mode. Only for VER < DEV_VER_V3 */
+/* Multiple DMA transfers mode. */
#define USB_CONF_DMULT BIT(9)
/* DMA clock turn-off enable. */
#define USB_CONF_DMAOFFEN BIT(10)
@@ -185,6 +165,19 @@ struct cdns3_usb_regs {
#define USB_CONF_LGO_U2 BIT(30)
/* SS.Inactive state entry request (used in SS mode) */
#define USB_CONF_LGO_SSINACT BIT(31)
+/* USB_CONF2- bitmasks */
+/*
+ * Writing 1 disables TDL calculation basing on TRB feature in controller
+ * for DMULT mode.
+ * Bit supported only for DEV_VER_V2 version.
+ */
+#define USB_CONF2_DIS_TDL_TRB BIT(1)
+/*
+ * Writing 1 enables TDL calculation basing on TRB feature in controller
+ * for DMULT mode.
+ * Bit supported only for DEV_VER_V2 version.
+ */
+#define USB_CONF2_EN_TDL_TRB BIT(2)
/* USB_STS - bitmasks */
/*
@@ -212,7 +205,6 @@ struct cdns3_usb_regs {
* DMA transfer configuration status.
* 0 - single request.
* 1 - multiple TRB chain
- * Supported only for controller version < DEV_VER_V3
*/
#define USB_STS_DTRANS_MASK BIT(3)
#define USB_STS_DTRANS(p) ((p) & USB_STS_DTRANS_MASK)
@@ -266,13 +258,6 @@ struct cdns3_usb_regs {
#define USB_STS_IN_RST_MASK BIT(10)
#define USB_STS_IN_RST(p) ((p) & USB_STS_IN_RST_MASK)
/*
- * Status of the "TDL calculation basing on TRB" feature.
- * 0 - disabled
- * 1 - enabled
- * Supported only for DEV_VER_V2 controller version.
- */
-#define USB_STS_TDL_TRB_ENABLED BIT(11)
-/*
* Device enable Status.
* 0 - USB device is disabled (VBUS input is disconnected from internal logic).
* 1 - USB device is enabled (VBUS input is connected to the internal logic).
@@ -280,7 +265,7 @@ struct cdns3_usb_regs {
#define USB_STS_DEVS_MASK BIT(14)
#define USB_STS_DEVS(p) ((p) & USB_STS_DEVS_MASK)
/*
- * Address status.
+ * DAddress statuss.
* 0 - USB device is default state.
* 1 - USB device is at least in address state.
*/
@@ -365,7 +350,7 @@ struct cdns3_usb_regs {
#define USB_STS_DMAOFF_MASK BIT(30)
#define USB_STS_DMAOFF(p) ((p) & USB_STS_DMAOFF_MASK)
/*
- * SFR Endian status.
+ * SFR Endian statuss.
* 0 - Little Endian order (default after hardware reset).
* 1 - Big Endian order.
*/
@@ -475,7 +460,7 @@ struct cdns3_usb_regs {
#define USB_IEN_INIT (USB_IEN_U2RESIEN | USB_ISTS_DIS2I | USB_IEN_CON2IEN \
| USB_IEN_UHRESIEN | USB_IEN_UWRESIEN | USB_IEN_DISIEN \
| USB_IEN_CONIEN | USB_IEN_U3EXTIEN | USB_IEN_L2ENTIEN \
- | USB_IEN_L2EXTIEN | USB_IEN_L1ENTIEN | USB_IEN_U3ENTIEN)
+ | USB_IEN_L2EXTIEN)
/* USB_ISTS - bitmasks */
/* SS Connection detected. */
@@ -589,20 +574,14 @@ struct cdns3_usb_regs {
/*
* Transfer Descriptor Length write (used only for Bulk Stream capable
* endpoints in SS mode).
- * Bit Removed from DEV_VER_V3 controller version.
*/
#define EP_CMD_STDL BIT(8)
-/*
- * Transfer Descriptor Length (used only in SS mode for bulk endpoints).
- * Bits Removed from DEV_VER_V3 controller version.
- */
+/* Transfer Descriptor Length (used only in SS mode for bulk endpoints). */
#define EP_CMD_TDL_MASK GENMASK(15, 9)
-#define EP_CMD_TDL_SET(p) (((p) << 9) & EP_CMD_TDL_MASK)
-#define EP_CMD_TDL_GET(p) (((p) & EP_CMD_TDL_MASK) >> 9)
-
+#define EP_CMD_TDL(p) (((p) << 9) & EP_CMD_TDL_MASK)
/* ERDY Stream ID value (used in SS mode). */
#define EP_CMD_ERDY_SID_MASK GENMASK(31, 16)
-#define EP_CMD_ERDY_SID(p) (((p) << 16) & EP_CMD_ERDY_SID_MASK)
+#define EP_CMD_ERDY_SID(p) (((p) << 16) & EP_CMD_SID_MASK)
/* EP_STS - bitmasks */
/* Setup transfer complete. */
@@ -623,8 +602,8 @@ struct cdns3_usb_regs {
#define EP_STS_TRBERR BIT(7)
/* Not ready (used only in SS mode). */
#define EP_STS_NRDY BIT(8)
-/* DMA busy bit. */
-#define EP_STS_DBUSY BIT(9)
+/* DMA busy. */
+#define EP_STS_DBUSY(p) ((p) & BIT(9))
/* Endpoint Buffer Empty */
#define EP_STS_BUFFEMPTY(p) ((p) & BIT(10))
/* Current Cycle Status */
@@ -702,7 +681,7 @@ struct cdns3_usb_regs {
#define EP_ISTS_EP_OUT0 BIT(0)
#define EP_ISTS_EP_IN0 BIT(16)
-/* USB_PWR- bitmasks */
+/* EP_PWR- bitmasks */
/*Power Shut Off capability enable*/
#define PUSB_PWR_PSO_EN BIT(0)
/*Power Shut Off capability disable*/
@@ -721,21 +700,7 @@ struct cdns3_usb_regs {
/* This bit informs if Fast Registers Access is enabled. */
#define PUSB_PWR_FST_REG_ACCESS_STAT BIT(30)
/* Fast Registers Access Enable. */
-#define PUSB_PWR_FST_REG_ACCESS BIT(31)
-
-/* USB_CONF2- bitmasks */
-/*
- * Writing 1 disables TDL calculation basing on TRB feature in controller
- * for DMULT mode.
- * Bit supported only for DEV_VER_V2 version.
- */
-#define USB_CONF2_DIS_TDL_TRB BIT(1)
-/*
- * Writing 1 enables TDL calculation basing on TRB feature in controller
- * for DMULT mode.
- * Bit supported only for DEV_VER_V2 version.
- */
-#define USB_CONF2_EN_TDL_TRB BIT(2)
+#define PUSB_PWR_FST_REG_ACCESS BIT(31)
/* USB_CAP1- bitmasks */
/*
@@ -852,13 +817,6 @@ struct cdns3_usb_regs {
*/
#define USB_CAP1_OTG_READY(p) ((p) & BIT(27))
-/*
- * When set, indicates that controller supports automatic internal TDL
- * calculation basing on the size provided in TRB (TRB[22:17]) for DMULT mode
- * Supported only for DEV_VER_V2 controller version.
- */
-#define USB_CAP1_TDL_FROM_TRB(p) ((p) & BIT(28))
-
/* USB_CAP2- bitmasks */
/*
* The actual size of the connected On-chip RAM memory in kB:
@@ -950,13 +908,6 @@ struct cdns3_usb_regs {
*/
#define DBG_LINK1_LFPS_GEN_PING_SET BIT(27)
-/* DMA_AXI_CTRL- bitmasks */
-/* The mawprot pin configuration. */
-#define DMA_AXI_CTRL_MARPROT(p) ((p) & GENMASK(2, 0))
-/* The marprot pin configuration. */
-#define DMA_AXI_CTRL_MAWPROT(p) (((p) & GENMASK(2, 0)) << 16)
-#define DMA_AXI_CTRL_NON_SECURE 0x02
-
#define gadget_to_cdns3_device(g) (container_of(g, struct cdns3_device, gadget))
#define ep_to_cdns3_ep(ep) (container_of(ep, struct cdns3_endpoint, endpoint))
@@ -965,14 +916,10 @@ struct cdns3_usb_regs {
/*
* USBSS-DEV DMA interface.
*/
-#define TRBS_PER_SEGMENT 40
+#define TRBS_PER_SEGMENT 150
#define ISO_MAX_INTERVAL 10
-#if TRBS_PER_SEGMENT < 2
-#error "Incorrect TRBS_PER_SEGMENT. Minimal Transfer Ring size is 2."
-#endif
-
/*
*Only for ISOC endpoints - maximum number of TRBs is calculated as
* pow(2, bInterval-1) * number of usb requests. It is limitation made by
@@ -1021,16 +968,6 @@ struct cdns3_trb {
*/
#define TRB_TOGGLE BIT(1)
-/*
- * Short Packet (SP). OUT EPs at DMULT=1 only. Indicates if the TRB was
- * processed while USB short packet was received. No more buffers defined by
- * the TD will be used. DMA will automatically advance to next TD.
- * - Shall be set to 0 by Software when putting TRB on the Transfer Ring
- * - Shall be set to 1 by Controller when Short Packet condition for this TRB
- * is detected independent if ISP is set or not.
- */
-#define TRB_SP BIT(1)
-
/* Interrupt on short packet*/
#define TRB_ISP BIT(2)
/*Setting this bit enables FIFO DMA operation mode*/
@@ -1041,9 +978,7 @@ struct cdns3_trb {
#define TRB_IOC BIT(5)
/* stream ID bitmasks. */
-#define TRB_STREAM_ID_BITMASK GENMASK(31, 16)
-#define TRB_STREAM_ID(p) ((p) << 16)
-#define TRB_FIELD_TO_STREAMID(p) (((p) & TRB_STREAM_ID_BITMASK) >> 16)
+#define TRB_STREAM_ID(p) ((p) & GENMASK(31, 16))
/* Size of TD expressed in USB packets for HS/FS mode. */
#define TRB_TDL_HS_SIZE(p) (((p) << 16) & GENMASK(31, 16))
@@ -1083,7 +1018,6 @@ struct cdns3_trb {
#define CDNS3_EP_ISO_SS_BURST 3
#define CDNS3_MAX_NUM_DESCMISS_BUF 32
#define CDNS3_DESCMIS_BUF_SIZE 2048 /* Bytes */
-#define CDNS3_WA2_NUM_BUFFERS 128
/*-------------------------------------------------------------------------*/
/* Used structs */
@@ -1094,7 +1028,7 @@ struct cdns3_device;
* @endpoint: usb endpoint
* @pending_req_list: list of requests queuing on transfer ring.
* @deferred_req_list: list of requests waiting for queuing on transfer ring.
- * @wa2_descmiss_req_list: list of requests internally allocated by driver.
+ * @descmiss_req_list: list of requests internally allocated by driver (WA2).
* @trb_pool: transfer ring - array of transaction buffers
* @trb_pool_dma: dma address of transfer ring
* @cdns3_dev: device associated with this endpoint
@@ -1119,8 +1053,7 @@ struct cdns3_endpoint {
struct usb_ep endpoint;
struct list_head pending_req_list;
struct list_head deferred_req_list;
- struct list_head wa2_descmiss_req_list;
- int wa2_counter;
+ struct list_head descmiss_req_list;
struct cdns3_trb *trb_pool;
dma_addr_t trb_pool_dma;
@@ -1129,19 +1062,17 @@ struct cdns3_endpoint {
char name[20];
#define EP_ENABLED BIT(0)
-#define EP_STALLED BIT(1)
-#define EP_STALL_PENDING BIT(2)
-#define EP_WEDGE BIT(3)
-#define EP_TRANSFER_STARTED BIT(4)
-#define EP_UPDATE_EP_TRBADDR BIT(5)
-#define EP_PENDING_REQUEST BIT(6)
-#define EP_RING_FULL BIT(7)
-#define EP_CLAIMED BIT(8)
-#define EP_DEFERRED_DRDY BIT(9)
-#define EP_QUIRK_ISO_OUT_EN BIT(10)
-#define EP_QUIRK_END_TRANSFER BIT(11)
-#define EP_QUIRK_EXTRA_BUF_DET BIT(12)
-#define EP_QUIRK_EXTRA_BUF_EN BIT(13)
+#define EP_STALL BIT(1)
+#define EP_WEDGE BIT(2)
+#define EP_TRANSFER_STARTED BIT(3)
+#define EP_UPDATE_EP_TRBADDR BIT(4)
+#define EP_PENDING_REQUEST BIT(5)
+#define EP_RING_FULL BIT(6)
+#define EP_CLAIMED BIT(7)
+#define EP_QUIRK_EXTRA_BUF_DET BIT(8)
+#define EP_QUIRK_EXTRA_BUF_EN BIT(9)
+#define EP_QUIRK_END_TRANSFER BIT(10)
+
u32 flags;
struct cdns3_request *descmis_req;
@@ -1193,7 +1124,7 @@ struct cdns3_aligned_buf {
* @aligned_buf: object holds information about aligned buffer associated whit
* this endpoint
* @flags: flag specifying special usage of request
- * @list: used by internally allocated request to add to wa2_descmiss_req_list.
+ * @list: used by internally allocated request to add to descmiss_req_list.
*/
struct cdns3_request {
struct usb_request request;
@@ -1234,7 +1165,8 @@ struct cdns3_request {
* @ep0_data_dir: direction for control transfer
* @eps: array of pointers to all endpoints with exclusion ep0
* @aligned_buf_list: list of aligned buffers internally allocated by driver
- * @aligned_buf_wq: workqueue freeing no longer used aligned buf.
+ * @run_garbage_colector: infroms that at least one element of aligned_buf_list
+ * can be freed
* @selected_ep: actually selected endpoint. It's used only to improve
* performance.
* @isoch_delay: value from Set Isoch Delay request. Only valid on SS/SSP.
@@ -1246,10 +1178,13 @@ struct cdns3_request {
* @wake_up_flag: allow device to remote up the host
* @status_completion_no_call: indicate that driver is waiting for status s
* stage completion. It's used in deferred SET_CONFIGURATION request.
- * @onchip_buffers: number of available on-chip buffers.
- * @onchip_used_size: actual size of on-chip memory assigned to endpoints.
+ * @onchip_mem_allocated_size: actual size of on-chip memory assigned
+ * to endpoints
* @pending_status_wq: workqueue handling status stage for deferred requests.
+ * @shadow_ep_en: hold information about endpoints that will be enabled
+ * in soft irq.
* @pending_status_request: request for which status stage was deferred
+ * @start_gadget: the current role is at CDNS3_ROLE_GADGET
*/
struct cdns3_device {
struct device *dev;
@@ -1277,12 +1212,11 @@ struct cdns3_device {
struct cdns3_endpoint *eps[CDNS3_ENDPOINTS_MAX_COUNT];
struct list_head aligned_buf_list;
- struct work_struct aligned_buf_wq;
+ unsigned run_garbage_colector:1;
u32 selected_ep;
u16 isoch_delay;
- unsigned wait_for_setup:1;
unsigned u1_allowed:1;
unsigned u2_allowed:1;
unsigned is_selfpowered:1;
@@ -1290,14 +1224,14 @@ struct cdns3_device {
int hw_configured_flag:1;
int wake_up_flag:1;
unsigned status_completion_no_call:1;
- int out_mem_is_allocated;
+ int out_mem_is_allocated:1;
struct work_struct pending_status_wq;
struct usb_request *pending_status_request;
-
+ u32 shadow_ep_en;
/*in KB */
- u16 onchip_buffers;
- u16 onchip_used_size;
+ int onchip_mem_allocated_size;
+ unsigned start_gadget:1;
};
void cdns3_set_register_bit(void __iomem *ptr, u32 mask);
@@ -1317,8 +1251,6 @@ int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep);
u8 cdns3_ep_addr_to_index(u8 ep_addr);
int cdns3_gadget_ep_set_wedge(struct usb_ep *ep);
int cdns3_gadget_ep_set_halt(struct usb_ep *ep, int value);
-void __cdns3_gadget_ep_set_halt(struct cdns3_endpoint *priv_ep);
-int __cdns3_gadget_ep_clear_halt(struct cdns3_endpoint *priv_ep);
struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep,
gfp_t gfp_flags);
void cdns3_gadget_ep_free_request(struct usb_ep *ep,
@@ -1333,6 +1265,5 @@ int cdns3_init_ep0(struct cdns3_device *priv_dev,
void cdns3_ep0_config(struct cdns3_device *priv_dev);
void cdns3_ep_config(struct cdns3_endpoint *priv_ep);
void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir);
-int __cdns3_gadget_wakeup(struct cdns3_device *priv_dev);
#endif /* __LINUX_CDNS3_GADGET */
diff --git a/drivers/usb/cdns3/host-export.h b/drivers/usb/cdns3/host-export.h
index ae11810f8826..a981d5cf3658 100644
--- a/drivers/usb/cdns3/host-export.h
+++ b/drivers/usb/cdns3/host-export.h
@@ -1,17 +1,25 @@
-/* SPDX-License-Identifier: GPL-2.0 */
/*
- * Cadence USBSS DRD Driver - Host Export APIs
- *
- * Copyright (C) 2017-2018 NXP
+ * host-export.h - Host Export APIs
*
+ * Copyright 2017 NXP
* Authors: Peter Chen <peter.chen@nxp.com>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
*/
-#ifndef __LINUX_CDNS3_HOST_EXPORT
-#define __LINUX_CDNS3_HOST_EXPORT
+
+#ifndef __DRIVERS_USB_CDNS3_HOST_H
+#define __DRIVERS_USB_CDNS3_HOST_H
#ifdef CONFIG_USB_CDNS3_HOST
int cdns3_host_init(struct cdns3 *cdns);
+void cdns3_host_remove(struct cdns3 *cdns);
+void cdns3_host_driver_init(void);
#else
@@ -20,8 +28,16 @@ static inline int cdns3_host_init(struct cdns3 *cdns)
return -ENXIO;
}
-static inline void cdns3_host_exit(struct cdns3 *cdns) { }
+static inline void cdns3_host_remove(struct cdns3 *cdns)
+{
+
+}
+
+static inline void cdns3_host_driver_init(void)
+{
+
+}
#endif /* CONFIG_USB_CDNS3_HOST */
-#endif /* __LINUX_CDNS3_HOST_EXPORT */
+#endif /* __DRIVERS_USB_CDNS3_HOST_H */
diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c
index ad788bf3fe4f..bddaa233913d 100644
--- a/drivers/usb/cdns3/host.c
+++ b/drivers/usb/cdns3/host.c
@@ -1,59 +1,276 @@
-// SPDX-License-Identifier: GPL-2.0
/*
- * Cadence USBSS DRD Driver - host side
- *
- * Copyright (C) 2018-2019 Cadence Design Systems.
- * Copyright (C) 2017-2018 NXP
+ * host.c - Cadence USB3 host controller driver
*
+ * Copyright 2017 NXP
* Authors: Peter Chen <peter.chen@nxp.com>
- * Pawel Laszczak <pawell@cadence.com>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
*/
-#include <linux/platform_device.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/pm_runtime.h>
+#include <linux/usb/of.h>
+
+#include "../host/xhci.h"
+
#include "core.h"
-#include "drd.h"
#include "host-export.h"
+#include "cdns3-nxp-reg-def.h"
+
+#define XHCI_WAKEUP_STATUS (PORT_RC | PORT_PLC)
+
+static struct hc_driver __read_mostly xhci_cdns3_hc_driver;
+
+static void xhci_cdns3_quirks(struct device *dev, struct xhci_hcd *xhci)
+{
+ /*
+ * As of now platform drivers don't provide MSI support so we ensure
+ * here that the generic code does not try to make a pci_dev from our
+ * dev struct in order to setup MSI
+ */
+ xhci->quirks |= (XHCI_PLAT | XHCI_AVOID_BEI | XHCI_CDNS_HOST);
+}
-static int __cdns3_host_init(struct cdns3 *cdns)
+static int xhci_cdns3_setup(struct usb_hcd *hcd)
{
- struct platform_device *xhci;
int ret;
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ u32 command;
+
+ ret = xhci_gen_setup(hcd, xhci_cdns3_quirks);
+ if (ret)
+ return ret;
+ /* set usbcmd.EU3S */
+ command = readl(&xhci->op_regs->command);
+ command |= CMD_PM_INDEX;
+ writel(command, &xhci->op_regs->command);
+
+ return 0;
+}
+
+struct cdns3_host {
+ struct device dev;
+ struct usb_hcd *hcd;
+ struct cdns3 *cdns;
+};
+
+static int xhci_cdns3_bus_suspend(struct usb_hcd *hcd)
+{
+ struct device *dev = hcd->self.controller;
+ struct cdns3_host *host = container_of(dev, struct cdns3_host, dev);
+ struct cdns3 *cdns = host->cdns;
+ void __iomem *xhci_regs = cdns->xhci_regs;
+ u32 value;
+ int ret;
+
+ ret = xhci_bus_suspend(hcd);
+ if (ret)
+ return ret;
- cdns3_drd_switch_host(cdns, 1);
+ value = readl(xhci_regs + XECP_AUX_CTRL_REG1);
+ value |= CFG_RXDET_P3_EN;
+ writel(value, xhci_regs + XECP_AUX_CTRL_REG1);
- xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
- if (!xhci) {
- dev_err(cdns->dev, "couldn't allocate xHCI device\n");
+ return 0;
+}
+
+static const struct xhci_driver_overrides xhci_cdns3_overrides __initconst = {
+ .extra_priv_size = sizeof(struct xhci_hcd),
+ .reset = xhci_cdns3_setup,
+ .bus_suspend = xhci_cdns3_bus_suspend,
+};
+
+static irqreturn_t cdns3_host_irq(struct cdns3 *cdns)
+{
+ struct device *dev = cdns->host_dev;
+ struct usb_hcd *hcd;
+
+ if (dev)
+ hcd = dev_get_drvdata(dev);
+ else
+ return IRQ_NONE;
+
+ if (hcd)
+ return usb_hcd_irq(cdns->irq, hcd);
+ else
+ return IRQ_NONE;
+}
+
+static void cdns3_host_release(struct device *dev)
+{
+ struct cdns3_host *host = container_of(dev, struct cdns3_host, dev);
+
+ dev_dbg(dev, "releasing '%s'\n", dev_name(dev));
+ kfree(host);
+}
+
+static int cdns3_host_start(struct cdns3 *cdns)
+{
+ struct cdns3_host *host;
+ struct device *dev;
+ struct device *sysdev;
+ struct xhci_hcd *xhci;
+ int ret;
+
+ host = kzalloc(sizeof(*host), GFP_KERNEL);
+ if (!host)
return -ENOMEM;
- }
- xhci->dev.parent = cdns->dev;
- cdns->host_dev = xhci;
+ dev = &host->dev;
+ dev->release = cdns3_host_release;
+ dev->parent = cdns->dev;
+ dev_set_name(dev, "xhci-cdns3");
+ cdns->host_dev = dev;
+ host->cdns = cdns;
+ ret = device_register(dev);
+ if (ret)
+ goto err1;
+
+ sysdev = cdns->dev;
+ /* Try to set 64-bit DMA first */
+ if (WARN_ON(!sysdev->dma_mask))
+ /* Platform did not initialize dma_mask */
+ ret = dma_coerce_mask_and_coherent(sysdev,
+ DMA_BIT_MASK(64));
+ else
+ ret = dma_set_mask_and_coherent(sysdev, DMA_BIT_MASK(64));
- ret = platform_device_add_resources(xhci, cdns->xhci_res,
- CDNS3_XHCI_RESOURCES_NUM);
+ /* If setting 64-bit DMA mask fails, fall back to 32-bit DMA mask */
if (ret) {
- dev_err(cdns->dev, "couldn't add resources to xHCI device\n");
- goto err1;
+ ret = dma_set_mask_and_coherent(sysdev, DMA_BIT_MASK(32));
+ if (ret)
+ return ret;
}
+ pm_runtime_set_active(dev);
+ pm_runtime_no_callbacks(dev);
+ pm_runtime_enable(dev);
- ret = platform_device_add(xhci);
- if (ret) {
- dev_err(cdns->dev, "failed to register xHCI device\n");
- goto err1;
+ host->hcd = __usb_create_hcd(&xhci_cdns3_hc_driver, sysdev, dev,
+ dev_name(dev), NULL);
+ if (!host->hcd) {
+ ret = -ENOMEM;
+ goto err2;
+ }
+
+ host->hcd->regs = cdns->xhci_regs;
+ host->hcd->rsrc_start = cdns->xhci_res->start;
+ host->hcd->rsrc_len = resource_size(cdns->xhci_res);
+
+ device_wakeup_enable(host->hcd->self.controller);
+
+ xhci = hcd_to_xhci(host->hcd);
+
+ xhci->main_hcd = host->hcd;
+ xhci->shared_hcd = __usb_create_hcd(&xhci_cdns3_hc_driver, sysdev, dev,
+ dev_name(dev), host->hcd);
+ if (!xhci->shared_hcd) {
+ ret = -ENOMEM;
+ goto err3;
}
+ host->hcd->tpl_support = of_usb_host_tpl_support(sysdev->of_node);
+ xhci->shared_hcd->tpl_support = host->hcd->tpl_support;
+
+ ret = usb_add_hcd(host->hcd, 0, IRQF_SHARED);
+ if (ret)
+ goto err4;
+
+ ret = usb_add_hcd(xhci->shared_hcd, 0, IRQF_SHARED);
+ if (ret)
+ goto err5;
+
+ device_set_wakeup_capable(dev, true);
+ dev_dbg(dev, "%s ends\n", __func__);
return 0;
+
+err5:
+ usb_remove_hcd(host->hcd);
+err4:
+ usb_put_hcd(xhci->shared_hcd);
+err3:
+ usb_put_hcd(host->hcd);
+err2:
+ device_del(dev);
err1:
- platform_device_put(xhci);
+ put_device(dev);
+ cdns->host_dev = NULL;
return ret;
}
-static void cdns3_host_exit(struct cdns3 *cdns)
+static void cdns3_host_stop(struct cdns3 *cdns)
{
- platform_device_unregister(cdns->host_dev);
- cdns->host_dev = NULL;
- cdns3_drd_switch_host(cdns, 0);
+ struct device *dev = cdns->host_dev;
+ struct usb_hcd *hcd, *shared_hcd;
+ struct xhci_hcd *xhci;
+
+ if (dev) {
+ hcd = dev_get_drvdata(dev);
+ xhci = hcd_to_xhci(hcd);
+ shared_hcd = xhci->shared_hcd;
+ xhci->xhc_state |= XHCI_STATE_REMOVING;
+ usb_remove_hcd(shared_hcd);
+ xhci->shared_hcd = NULL;
+ usb_remove_hcd(hcd);
+ synchronize_irq(cdns->irq);
+ usb_put_hcd(shared_hcd);
+ usb_put_hcd(hcd);
+ cdns->host_dev = NULL;
+ pm_runtime_set_suspended(dev);
+ pm_runtime_disable(dev);
+ device_del(dev);
+ put_device(dev);
+ }
+}
+
+static int cdns3_host_suspend(struct cdns3 *cdns, bool do_wakeup)
+{
+ struct device *dev = cdns->host_dev;
+ struct xhci_hcd *xhci;
+ void __iomem *xhci_regs = cdns->xhci_regs;
+ u32 portsc_usb2, portsc_usb3;
+ int ret;
+
+ if (!dev)
+ return 0;
+
+ xhci = hcd_to_xhci(dev_get_drvdata(dev));
+ ret = xhci_suspend(xhci, do_wakeup);
+ if (ret)
+ return ret;
+
+ portsc_usb2 = readl(xhci_regs + 0x480);
+ portsc_usb3 = readl(xhci_regs + 0x490);
+ if ((portsc_usb2 & XHCI_WAKEUP_STATUS) ||
+ (portsc_usb3 & XHCI_WAKEUP_STATUS)) {
+ dev_dbg(cdns->dev, "wakeup occurs\n");
+ cdns3_role(cdns)->resume(cdns, false);
+ return -EBUSY;
+ }
+
+ return ret;
+}
+
+static int cdns3_host_resume(struct cdns3 *cdns, bool hibernated)
+{
+ struct device *dev = cdns->host_dev;
+ struct xhci_hcd *xhci;
+
+ if (!dev)
+ return 0;
+
+ xhci = hcd_to_xhci(dev_get_drvdata(dev));
+ return xhci_resume(xhci, hibernated);
}
int cdns3_host_init(struct cdns3 *cdns)
@@ -64,12 +281,23 @@ int cdns3_host_init(struct cdns3 *cdns)
if (!rdrv)
return -ENOMEM;
- rdrv->start = __cdns3_host_init;
- rdrv->stop = cdns3_host_exit;
- rdrv->state = CDNS3_ROLE_STATE_INACTIVE;
+ rdrv->start = cdns3_host_start;
+ rdrv->stop = cdns3_host_stop;
+ rdrv->irq = cdns3_host_irq;
+ rdrv->suspend = cdns3_host_suspend;
+ rdrv->resume = cdns3_host_resume;
rdrv->name = "host";
-
- cdns->roles[USB_ROLE_HOST] = rdrv;
+ cdns->roles[CDNS3_ROLE_HOST] = rdrv;
return 0;
}
+
+void cdns3_host_remove(struct cdns3 *cdns)
+{
+ cdns3_host_stop(cdns);
+}
+
+void __init cdns3_host_driver_init(void)
+{
+ xhci_init_driver(&xhci_cdns3_hc_driver, &xhci_cdns3_overrides);
+}
diff --git a/drivers/usb/cdns3/io.h b/drivers/usb/cdns3/io.h
new file mode 100644
index 000000000000..c16edbf54b8b
--- /dev/null
+++ b/drivers/usb/cdns3/io.h
@@ -0,0 +1,35 @@
+/**
+ * io.h - Cadence USB3 IO Header
+ *
+ * Copyright (C) 2016 Cadence Design Systems - https://www.cadence.com/
+ *
+ * Authors: Rafal Ozieblo <rafalo@cadence.com>,
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef __DRIVERS_USB_CDNS_IO_H
+#define __DRIVERS_USB_CDNS_IO_H
+
+#include <linux/io.h>
+
+static inline u32 cdns_readl(uint32_t __iomem *reg)
+{
+ u32 value = 0;
+
+ value = readl(reg);
+ return value;
+}
+
+static inline void cdns_writel(uint32_t __iomem *reg, u32 value)
+{
+ writel(value, reg);
+}
+
+
+#endif /* __DRIVERS_USB_CDNS_IO_H */
diff --git a/drivers/usb/cdns3/trace.c b/drivers/usb/cdns3/trace.c
index 459fa72d9c74..9431eb86d4ff 100644
--- a/drivers/usb/cdns3/trace.c
+++ b/drivers/usb/cdns3/trace.c
@@ -2,10 +2,22 @@
/*
* USBSS device controller driver Trace Support
*
- * Copyright (C) 2018-2019 Cadence.
+ * Copyright (C) 2018 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*/
#define CREATE_TRACE_POINTS
#include "trace.h"
+
+void cdns3_dbg(struct cdns3_device *priv_dev, const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ va_start(args, fmt);
+ vaf.fmt = fmt;
+ vaf.va = &args;
+ trace_cdns3_log(priv_dev, &vaf);
+ va_end(args);
+}
diff --git a/drivers/usb/cdns3/trace.h b/drivers/usb/cdns3/trace.h
index e92348c9b4d7..7f852a7e9ab5 100644
--- a/drivers/usb/cdns3/trace.h
+++ b/drivers/usb/cdns3/trace.h
@@ -3,7 +3,7 @@
* USBSS device controller driver.
* Trace support header file.
*
- * Copyright (C) 2018-2019 Cadence.
+ * Copyright (C) 2018 Cadence.
*
* Author: Pawel Laszczak <pawell@cadence.com>
*/
@@ -18,55 +18,23 @@
#include <linux/tracepoint.h>
#include <asm/byteorder.h>
#include <linux/usb/ch9.h>
-#include "core.h"
#include "gadget.h"
#include "debug.h"
#define CDNS3_MSG_MAX 500
-TRACE_EVENT(cdns3_halt,
- TP_PROTO(struct cdns3_endpoint *ep_priv, u8 halt, u8 flush),
- TP_ARGS(ep_priv, halt, flush),
+TRACE_EVENT(cdns3_log,
+ TP_PROTO(struct cdns3_device *priv_dev, struct va_format *vaf),
+ TP_ARGS(priv_dev, vaf),
TP_STRUCT__entry(
- __string(name, ep_priv->name)
- __field(u8, halt)
- __field(u8, flush)
+ __string(name, dev_name(priv_dev->dev))
+ __dynamic_array(char, msg, CDNS3_MSG_MAX)
),
TP_fast_assign(
- __assign_str(name, ep_priv->name);
- __entry->halt = halt;
- __entry->flush = flush;
+ __assign_str(name, dev_name(priv_dev->dev));
+ vsnprintf(__get_str(msg), CDNS3_MSG_MAX, vaf->fmt, *vaf->va);
),
- TP_printk("Halt %s for %s: %s", __entry->flush ? " and flush" : "",
- __get_str(name), __entry->halt ? "set" : "cleared")
-);
-
-TRACE_EVENT(cdns3_wa1,
- TP_PROTO(struct cdns3_endpoint *ep_priv, char *msg),
- TP_ARGS(ep_priv, msg),
- TP_STRUCT__entry(
- __string(ep_name, ep_priv->name)
- __string(msg, msg)
- ),
- TP_fast_assign(
- __assign_str(ep_name, ep_priv->name);
- __assign_str(msg, msg);
- ),
- TP_printk("WA1: %s %s", __get_str(ep_name), __get_str(msg))
-);
-
-TRACE_EVENT(cdns3_wa2,
- TP_PROTO(struct cdns3_endpoint *ep_priv, char *msg),
- TP_ARGS(ep_priv, msg),
- TP_STRUCT__entry(
- __string(ep_name, ep_priv->name)
- __string(msg, msg)
- ),
- TP_fast_assign(
- __assign_str(ep_name, ep_priv->name);
- __assign_str(msg, msg);
- ),
- TP_printk("WA2: %s %s", __get_str(ep_name), __get_str(msg))
+ TP_printk("%s: %s", __get_str(name), __get_str(msg))
);
DECLARE_EVENT_CLASS(cdns3_log_doorbell,
@@ -80,7 +48,7 @@ DECLARE_EVENT_CLASS(cdns3_log_doorbell,
__assign_str(name, ep_name);
__entry->ep_trbaddr = ep_trbaddr;
),
- TP_printk("%s, ep_trbaddr %08x", __get_str(name),
+ TP_printk("//Ding Dong %s, ep_trbaddr %08x", __get_str(name),
__entry->ep_trbaddr)
);
@@ -230,9 +198,9 @@ DECLARE_EVENT_CLASS(cdns3_log_request,
" trb: [start:%d, end:%d: virt addr %pa], flags:%x ",
__get_str(name), __entry->req, __entry->buf, __entry->actual,
__entry->length,
- __entry->zero ? "Z" : "z",
- __entry->short_not_ok ? "S" : "s",
- __entry->no_interrupt ? "I" : "i",
+ __entry->zero ? "zero | " : "",
+ __entry->short_not_ok ? "short | " : "",
+ __entry->no_interrupt ? "no int" : "",
__entry->status,
__entry->start_trb,
__entry->end_trb,
@@ -266,21 +234,6 @@ DEFINE_EVENT(cdns3_log_request, cdns3_gadget_giveback,
TP_ARGS(req)
);
-TRACE_EVENT(cdns3_ep0_queue,
- TP_PROTO(struct cdns3_device *dev_priv, struct usb_request *request),
- TP_ARGS(dev_priv, request),
- TP_STRUCT__entry(
- __field(int, dir)
- __field(int, length)
- ),
- TP_fast_assign(
- __entry->dir = dev_priv->ep0_data_dir;
- __entry->length = request->length;
- ),
- TP_printk("Queue to ep0%s length: %u", __entry->dir ? "in" : "out",
- __entry->length)
-);
-
DECLARE_EVENT_CLASS(cdns3_log_aligned_request,
TP_PROTO(struct cdns3_request *priv_req),
TP_ARGS(priv_req),
@@ -302,9 +255,9 @@ DECLARE_EVENT_CLASS(cdns3_log_aligned_request,
__entry->aligned_dma = priv_req->aligned_buf->dma;
__entry->aligned_buf_size = priv_req->aligned_buf->size;
),
- TP_printk("%s: req: %p, req buf %p, dma %pad a_buf %p a_dma %pad, size %d",
- __get_str(name), __entry->req, __entry->buf, &__entry->dma,
- __entry->aligned_buf, &__entry->aligned_dma,
+ TP_printk("%s: req: %p, req buf %p, dma %08llx a_buf %p a_dma %08llx, size %d",
+ __get_str(name), __entry->req, __entry->buf, __entry->dma,
+ __entry->aligned_buf, __entry->aligned_dma,
__entry->aligned_buf_size
)
);
@@ -422,7 +375,7 @@ DECLARE_EVENT_CLASS(cdns3_log_ep,
__entry->maxburst, __entry->enqueue,
__entry->dequeue,
__entry->flags & EP_ENABLED ? "EN | " : "",
- __entry->flags & EP_STALLED ? "STALLED | " : "",
+ __entry->flags & EP_STALL ? "STALL | " : "",
__entry->flags & EP_WEDGE ? "WEDGE | " : "",
__entry->flags & EP_TRANSFER_STARTED ? "STARTED | " : "",
__entry->flags & EP_UPDATE_EP_TRBADDR ? "UPD TRB | " : "",
diff --git a/drivers/usb/chipidea/bits.h b/drivers/usb/chipidea/bits.h
index 98da99510be7..d83ecfab9550 100644
--- a/drivers/usb/chipidea/bits.h
+++ b/drivers/usb/chipidea/bits.h
@@ -70,6 +70,9 @@
#define PORTSC_FPR BIT(6)
#define PORTSC_SUSP BIT(7)
#define PORTSC_HSP BIT(9)
+#define PORTSC_LS (BIT(11) | BIT(10))
+#define PORTSC_LS_J BIT(11)
+#define PORTSC_LS_K BIT(10)
#define PORTSC_PP BIT(12)
#define PORTSC_PTC (0x0FUL << 16)
#define PORTSC_WKCN BIT(20)
@@ -78,6 +81,7 @@
#define PORTSC_PFSC BIT(24)
#define PORTSC_PTS(d) \
(u32)((((d) & 0x3) << 30) | (((d) & 0x4) ? BIT(25) : 0))
+#define PORT_SPEED_LOW(d) ((((d) >> 26) & 0x3) == 1)
#define PORTSC_PTW BIT(28)
#define PORTSC_STS BIT(29)
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
index 6911aef500e9..487991ded6d2 100644
--- a/drivers/usb/chipidea/ci.h
+++ b/drivers/usb/chipidea/ci.h
@@ -126,12 +126,16 @@ enum ci_revision {
* @start: start this role
* @stop: stop this role
* @irq: irq handler for this role
+ * @suspend: system suspend handler for this role
+ * @resume: system resume handler for this role
* @name: role name string (host/gadget)
*/
struct ci_role_driver {
int (*start)(struct ci_hdrc *);
void (*stop)(struct ci_hdrc *);
irqreturn_t (*irq)(struct ci_hdrc *);
+ void (*suspend)(struct ci_hdrc *);
+ void (*resume)(struct ci_hdrc *, bool power_lost);
const char *name;
};
@@ -203,6 +207,9 @@ struct hw_bank {
* @in_lpm: if the core in low power mode
* @wakeup_int: if wakeup interrupt occur
* @rev: The revision number for controller
+ * @mutex: protect code from concorrent running
+ * @power_lost_work: work item when controller power is lost
+ * @power_lost_wq: work queue for controller power is lost
*/
struct ci_hdrc {
struct device *dev;
@@ -256,6 +263,20 @@ struct ci_hdrc {
bool in_lpm;
bool wakeup_int;
enum ci_revision rev;
+ /* register save area for suspend&resume */
+ u32 pm_command;
+ u32 pm_status;
+ u32 pm_intr_enable;
+ u32 pm_frame_index;
+ u32 pm_segment;
+ u32 pm_frame_list;
+ u32 pm_async_next;
+ u32 pm_configured_flag;
+ u32 pm_portsc;
+ u32 pm_usbmode;
+ struct work_struct power_lost_work;
+ struct workqueue_struct *power_lost_wq;
+ struct mutex mutex;
};
static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci)
@@ -275,9 +296,21 @@ static inline int ci_role_start(struct ci_hdrc *ci, enum ci_role role)
return -ENXIO;
ret = ci->roles[role]->start(ci);
- if (!ret)
- ci->role = role;
- return ret;
+ if (ret)
+ return ret;
+
+ ci->role = role;
+
+ if (ci->usb_phy) {
+ if (role == CI_ROLE_HOST)
+ usb_phy_set_mode(ci->usb_phy,
+ CUR_USB_MODE_HOST);
+ else
+ usb_phy_set_mode(ci->usb_phy,
+ CUR_USB_MODE_DEVICE);
+ }
+
+ return 0;
}
static inline void ci_role_stop(struct ci_hdrc *ci)
@@ -290,6 +323,9 @@ static inline void ci_role_stop(struct ci_hdrc *ci)
ci->role = CI_ROLE_END;
ci->roles[role]->stop(ci);
+
+ if (ci->usb_phy)
+ usb_phy_set_mode(ci->usb_phy, CUR_USB_MODE_NONE);
}
static inline enum usb_role ci_role_to_usb_role(struct ci_hdrc *ci)
@@ -453,8 +489,10 @@ u8 hw_port_test_get(struct ci_hdrc *ci);
void hw_phymode_configure(struct ci_hdrc *ci);
void ci_platform_configure(struct ci_hdrc *ci);
+int hw_controller_reset(struct ci_hdrc *ci);
void dbg_create_files(struct ci_hdrc *ci);
void dbg_remove_files(struct ci_hdrc *ci);
+void ci_hdrc_enter_lpm(struct ci_hdrc *ci, bool enable);
#endif /* __DRIVERS_USB_CHIPIDEA_CI_H */
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
index df8812c30640..48ff355c2255 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.c
+++ b/drivers/usb/chipidea/ci_hdrc_imx.c
@@ -14,6 +14,7 @@
#include <linux/clk.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pm_qos.h>
+#include <linux/busfreq-imx.h>
#include "ci.h"
#include "ci_hdrc_imx.h"
@@ -165,6 +166,11 @@ static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
if (of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI)
data->ulpi = 1;
+ of_property_read_u32(np, "picophy,pre-emp-curr-control",
+ &data->emp_curr_control);
+ of_property_read_u32(np, "picophy,dc-vol-level-adjust",
+ &data->dc_vol_level_adjust);
+
return data;
}
@@ -271,14 +277,18 @@ static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event)
struct device *dev = ci->dev->parent;
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
int ret = 0;
+ struct imx_usbmisc_data *mdata = data->usbmisc_data;
switch (event) {
case CI_HDRC_IMX_HSIC_ACTIVE_EVENT:
- ret = pinctrl_select_state(data->pinctrl,
- data->pinctrl_hsic_active);
- if (ret)
- dev_err(dev, "hsic_active select failed, err=%d\n",
- ret);
+ if (data->pinctrl) {
+ ret = pinctrl_select_state(data->pinctrl,
+ data->pinctrl_hsic_active);
+ if (ret)
+ dev_err(dev,
+ "hsic_active select failed, err=%d\n",
+ ret);
+ }
break;
case CI_HDRC_IMX_HSIC_SUSPEND_EVENT:
ret = imx_usbmisc_hsic_set_connect(data->usbmisc_data);
@@ -286,6 +296,12 @@ static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event)
dev_err(dev,
"hsic_set_connect failed, err=%d\n", ret);
break;
+ case CI_HDRC_CONTROLLER_VBUS_EVENT:
+ if (ci->vbus_active)
+ ret = imx_usbmisc_charger_detection(mdata, true);
+ else
+ ret = imx_usbmisc_charger_detection(mdata, false);
+ break;
default:
break;
}
@@ -306,7 +322,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
const struct ci_hdrc_imx_platform_flag *imx_platform_flag;
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
- struct pinctrl_state *pinctrl_hsic_idle;
of_id = of_match_device(ci_hdrc_imx_dt_ids, dev);
if (!of_id)
@@ -330,12 +345,42 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
pdata.flags |= CI_HDRC_IMX_IS_HSIC;
data->usbmisc_data->hsic = 1;
data->pinctrl = devm_pinctrl_get(dev);
- if (IS_ERR(data->pinctrl)) {
- dev_err(dev, "pinctrl get failed, err=%ld\n",
+ if (PTR_ERR(data->pinctrl) == -ENODEV)
+ data->pinctrl = NULL;
+ else if (IS_ERR(data->pinctrl)) {
+ if (PTR_ERR(data->pinctrl) != -EPROBE_DEFER)
+ dev_err(dev, "pinctrl get failed, err=%ld\n",
PTR_ERR(data->pinctrl));
return PTR_ERR(data->pinctrl);
}
+ data->hsic_pad_regulator =
+ devm_regulator_get_optional(dev, "hsic");
+ if (PTR_ERR(data->hsic_pad_regulator) == -ENODEV) {
+ /* no pad regualator is needed */
+ data->hsic_pad_regulator = NULL;
+ } else if (IS_ERR(data->hsic_pad_regulator)) {
+ if (PTR_ERR(data->hsic_pad_regulator) != -EPROBE_DEFER)
+ dev_err(dev,
+ "Get HSIC pad regulator error: %ld\n",
+ PTR_ERR(data->hsic_pad_regulator));
+ return PTR_ERR(data->hsic_pad_regulator);
+ }
+
+ if (data->hsic_pad_regulator) {
+ ret = regulator_enable(data->hsic_pad_regulator);
+ if (ret) {
+ dev_err(dev,
+ "Failed to enable HSIC pad regulator\n");
+ return ret;
+ }
+ }
+ }
+
+ /* HSIC pinctrl handling */
+ if (data->pinctrl) {
+ struct pinctrl_state *pinctrl_hsic_idle;
+
pinctrl_hsic_idle = pinctrl_lookup_state(data->pinctrl, "idle");
if (IS_ERR(pinctrl_hsic_idle)) {
dev_err(dev,
@@ -358,33 +403,13 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
PTR_ERR(data->pinctrl_hsic_active));
return PTR_ERR(data->pinctrl_hsic_active);
}
-
- data->hsic_pad_regulator = devm_regulator_get(dev, "hsic");
- if (PTR_ERR(data->hsic_pad_regulator) == -EPROBE_DEFER) {
- return -EPROBE_DEFER;
- } else if (PTR_ERR(data->hsic_pad_regulator) == -ENODEV) {
- /* no pad regualator is needed */
- data->hsic_pad_regulator = NULL;
- } else if (IS_ERR(data->hsic_pad_regulator)) {
- dev_err(dev, "Get HSIC pad regulator error: %ld\n",
- PTR_ERR(data->hsic_pad_regulator));
- return PTR_ERR(data->hsic_pad_regulator);
- }
-
- if (data->hsic_pad_regulator) {
- ret = regulator_enable(data->hsic_pad_regulator);
- if (ret) {
- dev_err(dev,
- "Failed to enable HSIC pad regulator\n");
- return ret;
- }
- }
}
if (pdata.flags & CI_HDRC_PMQOS)
pm_qos_add_request(&data->pm_qos_req,
PM_QOS_CPU_DMA_LATENCY, 0);
+ request_bus_freq(BUS_FREQ_HIGH);
ret = imx_get_clks(dev);
if (ret)
goto disable_hsic_regulator;
@@ -404,6 +429,8 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
}
pdata.usb_phy = data->phy;
+ if (data->usbmisc_data)
+ data->usbmisc_data->usb_phy = data->phy;
if ((of_device_is_compatible(np, "fsl,imx53-usb") ||
of_device_is_compatible(np, "fsl,imx51-usb")) && pdata.usb_phy &&
@@ -416,6 +443,11 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM)
data->supports_runtime_pm = true;
+ if (of_find_property(np, "ci-disable-lpm", NULL)) {
+ data->supports_runtime_pm = false;
+ pdata.flags &= ~CI_HDRC_SUPPORTS_RUNTIME_PM;
+ }
+
ret = imx_usbmisc_init(data->usbmisc_data);
if (ret) {
dev_err(dev, "usbmisc init failed, ret=%d\n", ret);
@@ -433,12 +465,24 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
goto err_clk;
}
+ if (!IS_ERR(pdata.id_extcon.edev) ||
+ of_property_read_bool(np, "usb-role-switch"))
+ data->usbmisc_data->ext_id = 1;
+
+ if (!IS_ERR(pdata.vbus_extcon.edev) ||
+ of_property_read_bool(np, "usb-role-switch"))
+ data->usbmisc_data->ext_vbus = 1;
+
ret = imx_usbmisc_init_post(data->usbmisc_data);
if (ret) {
dev_err(dev, "usbmisc post failed, ret=%d\n", ret);
goto disable_device;
}
+ /* usbmisc needs to know dr mode to choose wakeup setting */
+ data->usbmisc_data->available_role =
+ ci_hdrc_query_available_role(data->ci_pdev);
+
if (data->supports_runtime_pm) {
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
@@ -453,6 +497,7 @@ disable_device:
err_clk:
imx_disable_unprepare_clks(dev);
disable_hsic_regulator:
+ release_bus_freq(BUS_FREQ_HIGH);
if (data->hsic_pad_regulator)
/* don't overwrite original ret (cf. EPROBE_DEFER) */
regulator_disable(data->hsic_pad_regulator);
@@ -466,6 +511,10 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev)
{
struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev);
+ /* usbmisc needs to know dr mode to choose wakeup setting */
+ data->usbmisc_data->available_role =
+ ci_hdrc_query_available_role(data->ci_pdev);
+
if (data->supports_runtime_pm) {
pm_runtime_get_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
@@ -477,6 +526,7 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev)
usb_phy_shutdown(data->phy);
if (data->ci_pdev) {
imx_disable_unprepare_clks(&pdev->dev);
+ release_bus_freq(BUS_FREQ_HIGH);
if (data->plat_data->flags & CI_HDRC_PMQOS)
pm_qos_remove_request(&data->pm_qos_req);
if (data->hsic_pad_regulator)
@@ -491,20 +541,24 @@ static void ci_hdrc_imx_shutdown(struct platform_device *pdev)
ci_hdrc_imx_remove(pdev);
}
-static int __maybe_unused imx_controller_suspend(struct device *dev)
+static int __maybe_unused imx_controller_suspend(struct device *dev,
+ pm_message_t msg)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
int ret = 0;
dev_dbg(dev, "at %s\n", __func__);
- ret = imx_usbmisc_hsic_set_clk(data->usbmisc_data, false);
+ ret = imx_usbmisc_suspend(data->usbmisc_data,
+ PMSG_IS_AUTO(msg) || device_may_wakeup(dev));
if (ret) {
- dev_err(dev, "usbmisc hsic_set_clk failed, ret=%d\n", ret);
+ dev_err(dev,
+ "usbmisc suspend failed, ret=%d\n", ret);
return ret;
}
imx_disable_unprepare_clks(dev);
+ release_bus_freq(BUS_FREQ_HIGH);
if (data->plat_data->flags & CI_HDRC_PMQOS)
pm_qos_remove_request(&data->pm_qos_req);
@@ -513,44 +567,37 @@ static int __maybe_unused imx_controller_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused imx_controller_resume(struct device *dev)
+static int __maybe_unused imx_controller_resume(struct device *dev,
+ pm_message_t msg)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
int ret = 0;
dev_dbg(dev, "at %s\n", __func__);
- if (!data->in_lpm) {
- WARN_ON(1);
+ if (!data->in_lpm)
return 0;
- }
if (data->plat_data->flags & CI_HDRC_PMQOS)
pm_qos_add_request(&data->pm_qos_req,
PM_QOS_CPU_DMA_LATENCY, 0);
+ request_bus_freq(BUS_FREQ_HIGH);
ret = imx_prepare_enable_clks(dev);
if (ret)
return ret;
data->in_lpm = false;
- ret = imx_usbmisc_set_wakeup(data->usbmisc_data, false);
+ ret = imx_usbmisc_resume(data->usbmisc_data,
+ PMSG_IS_AUTO(msg) || device_may_wakeup(dev));
if (ret) {
- dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret);
+ dev_err(dev, "usbmisc resume failed, ret=%d\n", ret);
goto clk_disable;
}
- ret = imx_usbmisc_hsic_set_clk(data->usbmisc_data, true);
- if (ret) {
- dev_err(dev, "usbmisc hsic_set_clk failed, ret=%d\n", ret);
- goto hsic_set_clk_fail;
- }
-
return 0;
-hsic_set_clk_fail:
- imx_usbmisc_set_wakeup(data->usbmisc_data, true);
clk_disable:
imx_disable_unprepare_clks(dev);
return ret;
@@ -566,16 +613,12 @@ static int __maybe_unused ci_hdrc_imx_suspend(struct device *dev)
/* The core's suspend doesn't run */
return 0;
- if (device_may_wakeup(dev)) {
- ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true);
- if (ret) {
- dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n",
- ret);
- return ret;
- }
- }
+ ret = imx_controller_suspend(dev, PMSG_SUSPEND);
+ if (ret)
+ return ret;
- return imx_controller_suspend(dev);
+ pinctrl_pm_select_sleep_state(dev);
+ return ret;
}
static int __maybe_unused ci_hdrc_imx_resume(struct device *dev)
@@ -583,7 +626,8 @@ static int __maybe_unused ci_hdrc_imx_resume(struct device *dev)
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
int ret;
- ret = imx_controller_resume(dev);
+ pinctrl_pm_select_default_state(dev);
+ ret = imx_controller_resume(dev, PMSG_RESUME);
if (!ret && data->supports_runtime_pm) {
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
@@ -596,25 +640,16 @@ static int __maybe_unused ci_hdrc_imx_resume(struct device *dev)
static int __maybe_unused ci_hdrc_imx_runtime_suspend(struct device *dev)
{
struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
- int ret;
- if (data->in_lpm) {
- WARN_ON(1);
+ if (data->in_lpm)
return 0;
- }
-
- ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true);
- if (ret) {
- dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret);
- return ret;
- }
- return imx_controller_suspend(dev);
+ return imx_controller_suspend(dev, PMSG_AUTO_SUSPEND);
}
static int __maybe_unused ci_hdrc_imx_runtime_resume(struct device *dev)
{
- return imx_controller_resume(dev);
+ return imx_controller_resume(dev, PMSG_AUTO_RESUME);
}
static const struct dev_pm_ops ci_hdrc_imx_pm_ops = {
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.h b/drivers/usb/chipidea/ci_hdrc_imx.h
index c842e03f8767..c910f74474e0 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.h
+++ b/drivers/usb/chipidea/ci_hdrc_imx.h
@@ -22,12 +22,19 @@ struct imx_usbmisc_data {
unsigned int evdo:1; /* set external vbus divider option */
unsigned int ulpi:1; /* connected to an ULPI phy */
unsigned int hsic:1; /* HSIC controlller */
+ enum usb_dr_mode available_role;
+ unsigned int ext_id:1; /* ID from exteranl event */
+ unsigned int ext_vbus:1; /* Vbus from exteranl event */
+ int emp_curr_control;
+ int dc_vol_level_adjust;
+ struct usb_phy *usb_phy;
};
int imx_usbmisc_init(struct imx_usbmisc_data *data);
int imx_usbmisc_init_post(struct imx_usbmisc_data *data);
-int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *data, bool enabled);
int imx_usbmisc_hsic_set_connect(struct imx_usbmisc_data *data);
-int imx_usbmisc_hsic_set_clk(struct imx_usbmisc_data *data, bool on);
+int imx_usbmisc_charger_detection(struct imx_usbmisc_data *data, bool connect);
+int imx_usbmisc_suspend(struct imx_usbmisc_data *data, bool wakeup);
+int imx_usbmisc_resume(struct imx_usbmisc_data *data, bool wakeup);
#endif /* __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H */
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 98ee575ee500..7f17da3f9f79 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -220,7 +220,7 @@ static void hw_wait_phy_stable(void)
}
/* The PHY enters/leaves low power mode */
-static void ci_hdrc_enter_lpm(struct ci_hdrc *ci, bool enable)
+void ci_hdrc_enter_lpm(struct ci_hdrc *ci, bool enable)
{
enum ci_hw_regs reg = ci->hw_bank.lpm ? OP_DEVLC : OP_PORTSC;
bool lpm = !!(hw_read(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm)));
@@ -475,7 +475,7 @@ void ci_platform_configure(struct ci_hdrc *ci)
*
* This function returns an error code
*/
-static int hw_controller_reset(struct ci_hdrc *ci)
+int hw_controller_reset(struct ci_hdrc *ci)
{
int count = 0;
@@ -899,6 +899,33 @@ void ci_hdrc_remove_device(struct platform_device *pdev)
}
EXPORT_SYMBOL_GPL(ci_hdrc_remove_device);
+/**
+ * ci_hdrc_query_available_role: get runtime available operation mode
+ *
+ * The glue layer can get current operation mode (host/peripheral/otg)
+ * This function should be called after ci core device has created.
+ *
+ * @pdev: the platform device of ci core.
+ *
+ * Return USB_DR_MODE_XXX.
+ */
+enum usb_dr_mode ci_hdrc_query_available_role(struct platform_device *pdev)
+{
+ struct ci_hdrc *ci = platform_get_drvdata(pdev);
+
+ if (!ci)
+ return USB_DR_MODE_UNKNOWN;
+ if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET])
+ return USB_DR_MODE_OTG;
+ else if (ci->roles[CI_ROLE_HOST])
+ return USB_DR_MODE_HOST;
+ else if (ci->roles[CI_ROLE_GADGET])
+ return USB_DR_MODE_PERIPHERAL;
+ else
+ return USB_DR_MODE_UNKNOWN;
+}
+EXPORT_SYMBOL_GPL(ci_hdrc_query_available_role);
+
static inline void ci_role_destroy(struct ci_hdrc *ci)
{
ci_hdrc_gadget_destroy(ci);
@@ -973,6 +1000,53 @@ static struct attribute *ci_attrs[] = {
};
ATTRIBUTE_GROUPS(ci);
+static enum ci_role ci_get_role(struct ci_hdrc *ci)
+{
+ if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) {
+ if (ci->is_otg) {
+ hw_write_otgsc(ci, OTGSC_IDIE, OTGSC_IDIE);
+ return ci_otg_role(ci);
+ } else {
+ /*
+ * If the controller is not OTG capable, but support
+ * role switch, the defalt role is gadget, and the
+ * user can switch it through debugfs.
+ */
+ return CI_ROLE_GADGET;
+ }
+ } else {
+ return ci->roles[CI_ROLE_HOST]
+ ? CI_ROLE_HOST
+ : CI_ROLE_GADGET;
+ }
+}
+
+static void ci_start_new_role(struct ci_hdrc *ci)
+{
+ enum ci_role role = ci_get_role(ci);
+
+ if (ci->role != role) {
+ ci_handle_id_switch(ci);
+ } else if (role == CI_ROLE_GADGET) {
+ if (ci->vbus_active)
+ usb_gadget_vbus_disconnect(&ci->gadget);
+ ci_handle_vbus_connected(ci);
+ }
+}
+
+static void ci_power_lost_work(struct work_struct *work)
+{
+ struct ci_hdrc *ci = container_of(work, struct ci_hdrc,
+ power_lost_work);
+
+ disable_irq_nosync(ci->irq);
+ pm_runtime_get_sync(ci->dev);
+ if (!ci_otg_is_fsm_mode(ci))
+ ci_start_new_role(ci);
+ pm_runtime_put_sync(ci->dev);
+ enable_irq(ci->irq);
+}
+
static int ci_hdrc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -1143,11 +1217,15 @@ static int ci_hdrc_probe(struct platform_device *pdev)
: CI_ROLE_GADGET;
}
- if (!ci_otg_is_fsm_mode(ci)) {
- /* only update vbus status for peripheral */
- if (ci->role == CI_ROLE_GADGET)
- ci_handle_vbus_change(ci);
+ ci->role = ci_get_role(ci);
+ /* only update vbus status for peripheral */
+ if (ci->role == CI_ROLE_GADGET) {
+ /* Let DP pull down if it isn't currently */
+ hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
+ ci_handle_vbus_connected(ci);
+ }
+ if (!ci_otg_is_fsm_mode(ci)) {
ret = ci_role_start(ci, ci->role);
if (ret) {
dev_err(dev, "can't start %s role\n",
@@ -1178,9 +1256,20 @@ static int ci_hdrc_probe(struct platform_device *pdev)
device_set_wakeup_capable(&pdev->dev, true);
dbg_create_files(ci);
+ /* Init workqueue for controller power lost handling */
+ ci->power_lost_wq = create_freezable_workqueue("ci_power_lost");
+ if (!ci->power_lost_wq) {
+ dev_err(ci->dev, "can't create power_lost workqueue\n");
+ goto remove_debug;
+ }
+
+ INIT_WORK(&ci->power_lost_work, ci_power_lost_work);
+ mutex_init(&ci->mutex);
return 0;
+remove_debug:
+ dbg_remove_files(ci);
stop:
if (ci->role_switch)
usb_role_switch_unregister(ci->role_switch);
@@ -1212,6 +1301,8 @@ static int ci_hdrc_remove(struct platform_device *pdev)
pm_runtime_put_noidle(&pdev->dev);
}
+ flush_workqueue(ci->power_lost_wq);
+ destroy_workqueue(ci->power_lost_wq);
dbg_remove_files(ci);
ci_role_destroy(ci);
ci_hdrc_enter_lpm(ci, true);
@@ -1239,13 +1330,10 @@ static void ci_otg_fsm_wakeup_by_srp(struct ci_hdrc *ci)
{
if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) &&
(ci->fsm.a_bus_drop == 1) && (ci->fsm.a_bus_req == 0)) {
- if (!hw_read_otgsc(ci, OTGSC_ID)) {
- ci->fsm.a_srp_det = 1;
- ci->fsm.a_bus_drop = 0;
- } else {
+ if (!hw_read_otgsc(ci, OTGSC_ID))
+ otg_add_timer(&ci->fsm, A_DP_END);
+ else
ci->fsm.id = 1;
- }
- ci_otg_queue_work(ci);
}
}
@@ -1268,10 +1356,8 @@ static int ci_controller_resume(struct device *dev)
dev_dbg(dev, "at %s\n", __func__);
- if (!ci->in_lpm) {
- WARN_ON(1);
+ if (!ci->in_lpm)
return 0;
- }
ci_hdrc_enter_lpm(ci, false);
@@ -1303,6 +1389,7 @@ static int ci_suspend(struct device *dev)
{
struct ci_hdrc *ci = dev_get_drvdata(dev);
+ flush_workqueue(ci->power_lost_wq);
if (ci->wq)
flush_workqueue(ci->wq);
/*
@@ -1319,6 +1406,10 @@ static int ci_suspend(struct device *dev)
return 0;
}
+ /* Extra routine per role before system suspend */
+ if (ci->role != CI_ROLE_END && ci_role(ci)->suspend)
+ ci_role(ci)->suspend(ci);
+
if (device_may_wakeup(dev)) {
if (ci_otg_is_fsm_mode(ci))
ci_otg_fsm_suspend_for_srp(ci);
@@ -1335,8 +1426,18 @@ static int ci_suspend(struct device *dev)
static int ci_resume(struct device *dev)
{
struct ci_hdrc *ci = dev_get_drvdata(dev);
+ bool power_lost = false;
+ u32 sample_reg_val;
int ret;
+ /* Check if controller resume from power lost */
+ sample_reg_val = hw_read(ci, OP_ENDPTLISTADDR, ~0);
+ if (sample_reg_val == 0)
+ power_lost = true;
+ else if (sample_reg_val == 0xFFFFFFFF)
+ /* Restore value 0 if it was set for power lost check */
+ hw_write(ci, OP_ENDPTLISTADDR, ~0, 0);
+
if (device_may_wakeup(dev))
disable_irq_wake(ci->irq);
@@ -1344,6 +1445,19 @@ static int ci_resume(struct device *dev)
if (ret)
return ret;
+ if (power_lost) {
+ /* shutdown and re-init for phy */
+ ci_usb_phy_exit(ci);
+ ci_usb_phy_init(ci);
+ }
+
+ /* Extra routine per role after system resume */
+ if (ci->role != CI_ROLE_END && ci_role(ci)->resume)
+ ci_role(ci)->resume(ci, power_lost);
+
+ if (power_lost)
+ queue_work(ci->power_lost_wq, &ci->power_lost_work);
+
if (ci->supports_runtime_pm) {
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
@@ -1360,10 +1474,8 @@ static int ci_runtime_suspend(struct device *dev)
dev_dbg(dev, "at %s\n", __func__);
- if (ci->in_lpm) {
- WARN_ON(1);
+ if (ci->in_lpm)
return 0;
- }
if (ci_otg_is_fsm_mode(ci))
ci_otg_fsm_suspend_for_srp(ci);
diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c
index b45ceb91c735..3026b6b4f1e1 100644
--- a/drivers/usb/chipidea/host.c
+++ b/drivers/usb/chipidea/host.c
@@ -23,6 +23,7 @@
static struct hc_driver __read_mostly ci_ehci_hc_driver;
static int (*orig_bus_suspend)(struct usb_hcd *hcd);
+static int (*orig_bus_resume)(struct usb_hcd *hcd);
struct ehci_ci_priv {
struct regulator *reg_vbus;
@@ -99,7 +100,10 @@ static const struct ehci_driver_overrides ehci_ci_overrides = {
static irqreturn_t host_irq(struct ci_hdrc *ci)
{
- return usb_hcd_irq(ci->irq, ci->hcd);
+ if (ci->hcd)
+ return usb_hcd_irq(ci->irq, ci->hcd);
+ else
+ return IRQ_NONE;
}
static int host_start(struct ci_hdrc *ci)
@@ -213,11 +217,111 @@ static void host_stop(struct ci_hdrc *ci)
ci->platdata->pins_default);
}
+bool ci_hdrc_host_has_device(struct ci_hdrc *ci)
+{
+ struct usb_device *roothub;
+ int i;
+
+ if ((ci->role == CI_ROLE_HOST) && ci->hcd) {
+ roothub = ci->hcd->self.root_hub;
+ for (i = 0; i < roothub->maxchild; ++i) {
+ if (usb_hub_find_child(roothub, (i + 1)))
+ return true;
+ }
+ }
+ return false;
+}
+
+static void ci_hdrc_host_save_for_power_lost(struct ci_hdrc *ci)
+{
+ struct ehci_hcd *ehci;
+
+ if (!ci->hcd)
+ return;
+
+ ehci = hcd_to_ehci(ci->hcd);
+ /* save EHCI registers */
+ ci->pm_usbmode = ehci_readl(ehci, &ehci->regs->usbmode);
+ ci->pm_command = ehci_readl(ehci, &ehci->regs->command);
+ ci->pm_command &= ~CMD_RUN;
+ ci->pm_status = ehci_readl(ehci, &ehci->regs->status);
+ ci->pm_intr_enable = ehci_readl(ehci, &ehci->regs->intr_enable);
+ ci->pm_frame_index = ehci_readl(ehci, &ehci->regs->frame_index);
+ ci->pm_segment = ehci_readl(ehci, &ehci->regs->segment);
+ ci->pm_frame_list = ehci_readl(ehci, &ehci->regs->frame_list);
+ ci->pm_async_next = ehci_readl(ehci, &ehci->regs->async_next);
+ ci->pm_configured_flag =
+ ehci_readl(ehci, &ehci->regs->configured_flag);
+ ci->pm_portsc = ehci_readl(ehci, &ehci->regs->port_status[0]);
+}
+
+static void ci_hdrc_host_restore_from_power_lost(struct ci_hdrc *ci)
+{
+ struct ehci_hcd *ehci;
+ unsigned long flags;
+ u32 tmp;
+ int step_ms;
+ /*
+ * If the vbus is off during system suspend, most of devices will pull
+ * DP up within 200ms when they see vbus, set 1000ms for safety.
+ */
+ int timeout_ms = 1000;
+
+ if (!ci->hcd)
+ return;
+
+ hw_controller_reset(ci);
+
+ ehci = hcd_to_ehci(ci->hcd);
+ spin_lock_irqsave(&ehci->lock, flags);
+ /* Restore EHCI registers */
+ ehci_writel(ehci, ci->pm_usbmode, &ehci->regs->usbmode);
+ ehci_writel(ehci, ci->pm_portsc, &ehci->regs->port_status[0]);
+ ehci_writel(ehci, ci->pm_command, &ehci->regs->command);
+ ehci_writel(ehci, ci->pm_intr_enable, &ehci->regs->intr_enable);
+ ehci_writel(ehci, ci->pm_frame_index, &ehci->regs->frame_index);
+ ehci_writel(ehci, ci->pm_segment, &ehci->regs->segment);
+ ehci_writel(ehci, ci->pm_frame_list, &ehci->regs->frame_list);
+ ehci_writel(ehci, ci->pm_async_next, &ehci->regs->async_next);
+ ehci_writel(ehci, ci->pm_configured_flag,
+ &ehci->regs->configured_flag);
+ /* Restore the PHY's connect notifier setting */
+ if (ci->pm_portsc & PORTSC_HSP)
+ usb_phy_notify_connect(ci->usb_phy, USB_SPEED_HIGH);
+
+ tmp = ehci_readl(ehci, &ehci->regs->command);
+ tmp |= CMD_RUN;
+ ehci_writel(ehci, tmp, &ehci->regs->command);
+ spin_unlock_irqrestore(&ehci->lock, flags);
+
+ if (!(ci->pm_portsc & PORTSC_CCS))
+ return;
+
+ for (step_ms = 0; step_ms < timeout_ms; step_ms += 25) {
+ if (ehci_readl(ehci, &ehci->regs->port_status[0]) & PORTSC_CCS)
+ break;
+ msleep(25);
+ }
+}
+
+static void ci_hdrc_host_suspend(struct ci_hdrc *ci)
+{
+ ci_hdrc_host_save_for_power_lost(ci);
+}
+
+static void ci_hdrc_host_resume(struct ci_hdrc *ci, bool power_lost)
+{
+ if (power_lost)
+ ci_hdrc_host_restore_from_power_lost(ci);
+}
void ci_hdrc_host_destroy(struct ci_hdrc *ci)
{
- if (ci->role == CI_ROLE_HOST && ci->hcd)
+ if (ci->role == CI_ROLE_HOST && ci->hcd) {
+ disable_irq_nosync(ci->irq);
host_stop(ci);
+ enable_irq(ci->irq);
+ }
}
/* The below code is based on tegra ehci driver */
@@ -232,7 +336,7 @@ static int ci_ehci_hub_control(
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
u32 __iomem *status_reg;
- u32 temp;
+ u32 temp, suspend_line_state;
unsigned long flags;
int retval = 0;
struct device *dev = hcd->self.controller;
@@ -261,6 +365,17 @@ static int ci_ehci_hub_control(
PORT_SUSPEND, 5000))
ehci_err(ehci, "timeout waiting for SUSPEND\n");
+ if (ci->platdata->flags & CI_HDRC_HOST_SUSP_PHY_LPM) {
+ if (PORT_SPEED_LOW(temp))
+ suspend_line_state = PORTSC_LS_K;
+ else
+ suspend_line_state = PORTSC_LS_J;
+ if (!ehci_handshake(ehci, status_reg, PORTSC_LS,
+ suspend_line_state, 5000))
+ ci_hdrc_enter_lpm(ci, true);
+ }
+
+
if (ci->platdata->flags & CI_HDRC_IMX_IS_HSIC) {
if (ci->platdata->notify_event)
ci->platdata->notify_event(ci,
@@ -271,6 +386,14 @@ static int ci_ehci_hub_control(
ehci_writel(ehci, temp, status_reg);
}
+ spin_unlock_irqrestore(&ehci->lock, flags);
+ if (ehci_port_speed(ehci, temp) ==
+ USB_PORT_STAT_HIGH_SPEED && hcd->usb_phy) {
+ /* notify the USB PHY */
+ usb_phy_notify_suspend(hcd->usb_phy, USB_SPEED_HIGH);
+ }
+ spin_lock_irqsave(&ehci->lock, flags);
+
set_bit((wIndex & 0xff) - 1, &ehci->suspended_ports);
goto done;
}
@@ -284,6 +407,14 @@ static int ci_ehci_hub_control(
/* Make sure the resume has finished, it should be finished */
if (ehci_handshake(ehci, status_reg, PORT_RESUME, 0, 25000))
ehci_err(ehci, "timeout waiting for resume\n");
+
+ temp = ehci_readl(ehci, status_reg);
+
+ if (ehci_port_speed(ehci, temp) ==
+ USB_PORT_STAT_HIGH_SPEED && hcd->usb_phy) {
+ /* notify the USB PHY */
+ usb_phy_notify_resume(hcd->usb_phy, USB_SPEED_HIGH);
+ }
}
spin_unlock_irqrestore(&ehci->lock, flags);
@@ -331,6 +462,15 @@ static int ci_ehci_bus_suspend(struct usb_hcd *hcd)
*/
usleep_range(150, 200);
/*
+ * If a transaction is in progress, there may be
+ * a delay in suspending the port. Poll until the
+ * port is suspended.
+ */
+ if (test_bit(port, &ehci->bus_suspended) &&
+ ehci_handshake(ehci, reg, PORT_SUSPEND,
+ PORT_SUSPEND, 5000))
+ ehci_err(ehci, "timeout waiting for SUSPEND\n");
+ /*
* Need to clear WKCN and WKOC for imx HSIC,
* otherwise, there will be wakeup event.
*/
@@ -340,6 +480,15 @@ static int ci_ehci_bus_suspend(struct usb_hcd *hcd)
ehci_writel(ehci, tmp, reg);
}
+ if (hcd->usb_phy && test_bit(port, &ehci->bus_suspended)
+ && (ehci_port_speed(ehci, portsc) ==
+ USB_PORT_STAT_HIGH_SPEED))
+ /*
+ * notify the USB PHY, it is for global
+ * suspend case.
+ */
+ usb_phy_notify_suspend(hcd->usb_phy,
+ USB_SPEED_HIGH);
break;
}
}
@@ -347,6 +496,36 @@ static int ci_ehci_bus_suspend(struct usb_hcd *hcd)
return 0;
}
+static int ci_ehci_bus_resume(struct usb_hcd *hcd)
+{
+ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+ int port;
+
+ int ret = orig_bus_resume(hcd);
+
+ if (ret)
+ return ret;
+
+ port = HCS_N_PORTS(ehci->hcs_params);
+ while (port--) {
+ u32 __iomem *reg = &ehci->regs->port_status[port];
+ u32 portsc = ehci_readl(ehci, reg);
+ /*
+ * Notify PHY after resume signal has finished, it is
+ * for global suspend case.
+ */
+ if (hcd->usb_phy
+ && test_bit(port, &ehci->bus_suspended)
+ && (portsc & PORT_CONNECT)
+ && (ehci_port_speed(ehci, portsc) ==
+ USB_PORT_STAT_HIGH_SPEED))
+ /* notify the USB PHY */
+ usb_phy_notify_resume(hcd->usb_phy, USB_SPEED_HIGH);
+ }
+
+ return 0;
+}
+
int ci_hdrc_host_init(struct ci_hdrc *ci)
{
struct ci_role_driver *rdrv;
@@ -361,6 +540,8 @@ int ci_hdrc_host_init(struct ci_hdrc *ci)
rdrv->start = host_start;
rdrv->stop = host_stop;
rdrv->irq = host_irq;
+ rdrv->suspend = ci_hdrc_host_suspend;
+ rdrv->resume = ci_hdrc_host_resume;
rdrv->name = "host";
ci->roles[CI_ROLE_HOST] = rdrv;
@@ -371,6 +552,8 @@ void ci_hdrc_host_driver_init(void)
{
ehci_init_driver(&ci_ehci_hc_driver, &ehci_ci_overrides);
orig_bus_suspend = ci_ehci_hc_driver.bus_suspend;
+ orig_bus_resume = ci_ehci_hc_driver.bus_resume;
+ ci_ehci_hc_driver.bus_resume = ci_ehci_bus_resume;
ci_ehci_hc_driver.bus_suspend = ci_ehci_bus_suspend;
ci_ehci_hc_driver.hub_control = ci_ehci_hub_control;
}
diff --git a/drivers/usb/chipidea/host.h b/drivers/usb/chipidea/host.h
index 70112cf0f195..448df8894e26 100644
--- a/drivers/usb/chipidea/host.h
+++ b/drivers/usb/chipidea/host.h
@@ -7,6 +7,7 @@
int ci_hdrc_host_init(struct ci_hdrc *ci);
void ci_hdrc_host_destroy(struct ci_hdrc *ci);
void ci_hdrc_host_driver_init(void);
+bool ci_hdrc_host_has_device(struct ci_hdrc *ci);
#else
@@ -20,11 +21,16 @@ static inline void ci_hdrc_host_destroy(struct ci_hdrc *ci)
}
-static void ci_hdrc_host_driver_init(void)
+static inline void ci_hdrc_host_driver_init(void)
{
}
+static inline bool ci_hdrc_host_has_device(struct ci_hdrc *ci)
+{
+ return false;
+}
+
#endif
#endif /* __DRIVERS_USB_CHIPIDEA_HOST_H */
diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
index fbfb02e05c97..c944f2f905a2 100644
--- a/drivers/usb/chipidea/otg.c
+++ b/drivers/usb/chipidea/otg.c
@@ -2,7 +2,8 @@
/*
* otg.c - ChipIdea USB IP core OTG driver
*
- * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ * Copyright (C) 2013-2016 Freescale Semiconductor, Inc.
+ * Copyright 2017 NXP
*
* Author: Peter Chen
*/
@@ -20,6 +21,7 @@
#include "bits.h"
#include "otg.h"
#include "otg_fsm.h"
+#include "host.h"
/**
* hw_read_otgsc returns otgsc register bits value.
@@ -126,6 +128,20 @@ enum ci_role ci_otg_role(struct ci_hdrc *ci)
return role;
}
+void ci_handle_vbus_connected(struct ci_hdrc *ci)
+{
+ /*
+ * TODO: if the platform does not supply 5v to udc, or use other way
+ * to supply 5v, it needs to use other conditions to call
+ * usb_gadget_vbus_connect.
+ */
+ if (!ci->is_otg)
+ return;
+
+ if (hw_read_otgsc(ci, OTGSC_BSV))
+ usb_gadget_vbus_connect(&ci->gadget);
+}
+
void ci_handle_vbus_change(struct ci_hdrc *ci)
{
if (!ci->is_otg)
@@ -162,10 +178,13 @@ static int hw_wait_vbus_lower_bsv(struct ci_hdrc *ci)
return 0;
}
-static void ci_handle_id_switch(struct ci_hdrc *ci)
+void ci_handle_id_switch(struct ci_hdrc *ci)
{
- enum ci_role role = ci_otg_role(ci);
+ enum ci_role role;
+ int ret = 0;
+ mutex_lock(&ci->mutex);
+ role = ci_otg_role(ci);
if (role != ci->role) {
dev_dbg(ci->dev, "switching from %s to %s\n",
ci_role(ci)->name, ci->roles[role]->name);
@@ -181,13 +200,30 @@ static void ci_handle_id_switch(struct ci_hdrc *ci)
* care vbus on the board, since it will not affect
* external connector status.
*/
- hw_wait_vbus_lower_bsv(ci);
+ ret = hw_wait_vbus_lower_bsv(ci);
+ else if (ci->vbus_active)
+ /*
+ * If the role switch happens(e.g. during
+ * system sleep), and we lose vbus drop
+ * event, disconnect gadget for it before
+ * start host.
+ */
+ usb_gadget_vbus_disconnect(&ci->gadget);
ci_role_start(ci, role);
/* vbus change may have already occurred */
if (role == CI_ROLE_GADGET)
ci_handle_vbus_change(ci);
+
+ /*
+ * If the role switch happens(e.g. during system
+ * sleep) and vbus keeps on afterwards, we connect
+ * gadget as vbus connect event lost.
+ */
+ if (ret == -ETIMEDOUT)
+ usb_gadget_vbus_connect(&ci->gadget);
}
+ mutex_unlock(&ci->mutex);
}
/**
* ci_otg_work - perform otg (vbus/id) event handle
diff --git a/drivers/usb/chipidea/otg.h b/drivers/usb/chipidea/otg.h
index 4f8b8179ec96..86a46d2a1821 100644
--- a/drivers/usb/chipidea/otg.h
+++ b/drivers/usb/chipidea/otg.h
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * Copyright (C) 2013-2014 Freescale Semiconductor, Inc.
+ * Copyright (C) 2013-2015 Freescale Semiconductor, Inc.
*
* Author: Peter Chen
*/
@@ -14,6 +14,8 @@ int ci_hdrc_otg_init(struct ci_hdrc *ci);
void ci_hdrc_otg_destroy(struct ci_hdrc *ci);
enum ci_role ci_otg_role(struct ci_hdrc *ci);
void ci_handle_vbus_change(struct ci_hdrc *ci);
+void ci_handle_id_switch(struct ci_hdrc *ci);
+void ci_handle_vbus_connected(struct ci_hdrc *ci);
static inline void ci_otg_queue_work(struct ci_hdrc *ci)
{
disable_irq_nosync(ci->irq);
diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c
index 6ed4b00dba96..17cdf8b08ec3 100644
--- a/drivers/usb/chipidea/otg_fsm.c
+++ b/drivers/usb/chipidea/otg_fsm.c
@@ -25,6 +25,7 @@
#include "ci.h"
#include "bits.h"
#include "otg.h"
+#include "udc.h"
#include "otg_fsm.h"
/* Add for otg: interact with user space app */
@@ -211,6 +212,7 @@ static unsigned otg_timer_ms[] = {
0,
TB_DATA_PLS,
TB_SSEND_SRP,
+ TA_DP_END,
};
/*
@@ -356,6 +358,13 @@ static int b_ssend_srp_tmout(struct ci_hdrc *ci)
return 1;
}
+static int a_dp_end_tmout(struct ci_hdrc *ci)
+{
+ ci->fsm.a_bus_drop = 0;
+ ci->fsm.a_srp_det = 1;
+ return 0;
+}
+
/*
* Keep this list in the same order as timers indexed
* by enum otg_fsm_timer in include/linux/usb/otg-fsm.h
@@ -373,6 +382,7 @@ static int (*otg_timer_handlers[])(struct ci_hdrc *) = {
NULL, /* A_WAIT_ENUM */
b_data_pls_tmout, /* B_DATA_PLS */
b_ssend_srp_tmout, /* B_SSEND_SRP */
+ a_dp_end_tmout, /* A_DP_END */
};
/*
@@ -559,10 +569,7 @@ static int ci_otg_start_gadget(struct otg_fsm *fsm, int on)
{
struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm);
- if (on)
- usb_gadget_vbus_connect(&ci->gadget);
- else
- usb_gadget_vbus_disconnect(&ci->gadget);
+ ci_hdrc_gadget_connect(&ci->gadget, on);
return 0;
}
@@ -742,8 +749,7 @@ irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci)
if (otg_int_src) {
if (otg_int_src & OTGSC_DPIS) {
hw_write_otgsc(ci, OTGSC_DPIS, OTGSC_DPIS);
- fsm->a_srp_det = 1;
- fsm->a_bus_drop = 0;
+ ci_otg_add_timer(ci, A_DP_END);
} else if (otg_int_src & OTGSC_IDIS) {
hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS);
if (fsm->id == 0) {
diff --git a/drivers/usb/chipidea/otg_fsm.h b/drivers/usb/chipidea/otg_fsm.h
index 2b49d29bf2fb..638eec94d783 100644
--- a/drivers/usb/chipidea/otg_fsm.h
+++ b/drivers/usb/chipidea/otg_fsm.h
@@ -40,6 +40,8 @@
* for safe
*/
+#define TA_DP_END (200)
+
/*
* B-device timing constants
*/
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 8f18e7b6cadf..2646737f3969 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -1540,27 +1540,19 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
usb_phy_set_charger_state(ci->usb_phy, is_active ?
USB_CHARGER_PRESENT : USB_CHARGER_ABSENT);
- if (gadget_ready) {
- if (is_active) {
- pm_runtime_get_sync(&_gadget->dev);
- hw_device_reset(ci);
- hw_device_state(ci, ci->ep0out->qh.dma);
- usb_gadget_set_state(_gadget, USB_STATE_POWERED);
- usb_udc_vbus_handler(_gadget, true);
- } else {
- usb_udc_vbus_handler(_gadget, false);
- if (ci->driver)
- ci->driver->disconnect(&ci->gadget);
- hw_device_state(ci, 0);
- if (ci->platdata->notify_event)
- ci->platdata->notify_event(ci,
- CI_HDRC_CONTROLLER_STOPPED_EVENT);
- _gadget_stop_activity(&ci->gadget);
- pm_runtime_put_sync(&_gadget->dev);
- usb_gadget_set_state(_gadget, USB_STATE_NOTATTACHED);
- }
+ /* Charger Detection */
+ ci_usb_charger_connect(ci, is_active);
+
+ if (ci->usb_phy) {
+ if (is_active)
+ usb_phy_set_event(ci->usb_phy, USB_EVENT_VBUS);
+ else
+ usb_phy_set_event(ci->usb_phy, USB_EVENT_NONE);
}
+ if (gadget_ready)
+ ci_hdrc_gadget_connect(_gadget, is_active);
+
return 0;
}
@@ -1625,12 +1617,12 @@ static int ci_udc_pullup(struct usb_gadget *_gadget, int is_on)
if (ci_otg_is_fsm_mode(ci) || ci->role == CI_ROLE_HOST)
return 0;
- pm_runtime_get_sync(&ci->gadget.dev);
+ pm_runtime_get_sync(ci->dev);
if (is_on)
hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
else
hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
- pm_runtime_put_sync(&ci->gadget.dev);
+ pm_runtime_put_sync(ci->dev);
return 0;
}
@@ -1780,23 +1772,14 @@ static int ci_udc_start(struct usb_gadget *gadget,
ci->driver = driver;
/* Start otg fsm for B-device */
- if (ci_otg_is_fsm_mode(ci) && ci->fsm.id) {
- ci_hdrc_otg_fsm_start(ci);
+ if (ci_otg_is_fsm_mode(ci)) {
+ if (ci->fsm.id)
+ ci_hdrc_otg_fsm_start(ci);
return retval;
}
- pm_runtime_get_sync(&ci->gadget.dev);
- if (ci->vbus_active) {
- hw_device_reset(ci);
- } else {
- usb_udc_vbus_handler(&ci->gadget, false);
- pm_runtime_put_sync(&ci->gadget.dev);
- return retval;
- }
-
- retval = hw_device_state(ci, ci->ep0out->qh.dma);
- if (retval)
- pm_runtime_put_sync(&ci->gadget.dev);
+ if (ci->vbus_active)
+ ci_hdrc_gadget_connect(&ci->gadget, 1);
return retval;
}
@@ -1835,7 +1818,7 @@ static int ci_udc_stop(struct usb_gadget *gadget)
CI_HDRC_CONTROLLER_STOPPED_EVENT);
_gadget_stop_activity(&ci->gadget);
spin_lock_irqsave(&ci->lock, flags);
- pm_runtime_put(&ci->gadget.dev);
+ pm_runtime_put(ci->dev);
}
ci->driver = NULL;
@@ -1881,6 +1864,9 @@ static irqreturn_t udc_irq(struct ci_hdrc *ci)
if (USBi_PCI & intr) {
ci->gadget.speed = hw_port_is_high_speed(ci) ?
USB_SPEED_HIGH : USB_SPEED_FULL;
+ if (ci->usb_phy)
+ usb_phy_set_event(ci->usb_phy,
+ USB_EVENT_ENUMERATED);
if (ci->suspended) {
if (ci->driver->resume) {
spin_unlock(&ci->lock);
@@ -1967,9 +1953,6 @@ static int udc_start(struct ci_hdrc *ci)
if (retval)
goto destroy_eps;
- pm_runtime_no_callbacks(&ci->gadget.dev);
- pm_runtime_enable(&ci->gadget.dev);
-
return retval;
destroy_eps:
@@ -1999,6 +1982,52 @@ void ci_hdrc_gadget_destroy(struct ci_hdrc *ci)
dma_pool_destroy(ci->qh_pool);
}
+int ci_usb_charger_connect(struct ci_hdrc *ci, int is_active)
+{
+ int ret = 0;
+
+ pm_runtime_get_sync(ci->dev);
+
+ if (ci->usb_phy->charger_detect) {
+ usb_phy_set_charger_state(ci->usb_phy, is_active ?
+ USB_CHARGER_PRESENT : USB_CHARGER_ABSENT);
+ } else if (ci->platdata->notify_event) {
+ ret = ci->platdata->notify_event(ci,
+ CI_HDRC_CONTROLLER_VBUS_EVENT);
+ schedule_work(&ci->usb_phy->chg_work);
+ }
+
+ pm_runtime_put_sync(ci->dev);
+ return ret;
+}
+
+/**
+ * ci_hdrc_gadget_connect: caller make sure gadget driver is binded
+ */
+void ci_hdrc_gadget_connect(struct usb_gadget *gadget, int is_active)
+{
+ struct ci_hdrc *ci = container_of(gadget, struct ci_hdrc, gadget);
+
+ if (is_active) {
+ pm_runtime_get_sync(ci->dev);
+ hw_device_reset(ci);
+ hw_device_state(ci, ci->ep0out->qh.dma);
+ usb_gadget_set_state(gadget, USB_STATE_POWERED);
+ usb_udc_vbus_handler(gadget, true);
+ } else {
+ usb_udc_vbus_handler(gadget, false);
+ if (ci->driver)
+ ci->driver->disconnect(gadget);
+ hw_device_state(ci, 0);
+ if (ci->platdata->notify_event)
+ ci->platdata->notify_event(ci,
+ CI_HDRC_CONTROLLER_STOPPED_EVENT);
+ _gadget_stop_activity(gadget);
+ pm_runtime_put_sync(ci->dev);
+ usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
+ }
+}
+
static int udc_id_switch_for_device(struct ci_hdrc *ci)
{
if (ci->platdata->pins_device)
@@ -2029,6 +2058,44 @@ static void udc_id_switch_for_host(struct ci_hdrc *ci)
ci->platdata->pins_default);
}
+static void udc_suspend_for_power_lost(struct ci_hdrc *ci)
+{
+ /*
+ * Set OP_ENDPTLISTADDR to be non-zero for
+ * checking if controller resume from power lost
+ * in non-host mode.
+ */
+ if (hw_read(ci, OP_ENDPTLISTADDR, ~0) == 0)
+ hw_write(ci, OP_ENDPTLISTADDR, ~0, ~0);
+}
+
+/* Power lost with device mode */
+static void udc_resume_from_power_lost(struct ci_hdrc *ci)
+{
+ if (ci->is_otg)
+ hw_write_otgsc(ci, OTGSC_BSVIS | OTGSC_BSVIE,
+ OTGSC_BSVIS | OTGSC_BSVIE);
+}
+
+static void udc_suspend(struct ci_hdrc *ci)
+{
+ udc_suspend_for_power_lost(ci);
+
+ if (ci->driver && ci->vbus_active &&
+ (ci->gadget.state != USB_STATE_SUSPENDED))
+ usb_gadget_disconnect(&ci->gadget);
+}
+
+static void udc_resume(struct ci_hdrc *ci, bool power_lost)
+{
+ if (power_lost) {
+ udc_resume_from_power_lost(ci);
+ } else {
+ if (ci->driver && ci->vbus_active)
+ usb_gadget_connect(&ci->gadget);
+ }
+}
+
/**
* ci_hdrc_gadget_init - initialize device related bits
* ci: the controller
@@ -2050,6 +2117,8 @@ int ci_hdrc_gadget_init(struct ci_hdrc *ci)
rdrv->start = udc_id_switch_for_device;
rdrv->stop = udc_id_switch_for_host;
rdrv->irq = udc_irq;
+ rdrv->suspend = udc_suspend;
+ rdrv->resume = udc_resume;
rdrv->name = "gadget";
ret = udc_start(ci);
diff --git a/drivers/usb/chipidea/udc.h b/drivers/usb/chipidea/udc.h
index e023735d94b7..da8529c2684f 100644
--- a/drivers/usb/chipidea/udc.h
+++ b/drivers/usb/chipidea/udc.h
@@ -82,6 +82,8 @@ struct ci_hw_req {
int ci_hdrc_gadget_init(struct ci_hdrc *ci);
void ci_hdrc_gadget_destroy(struct ci_hdrc *ci);
+int ci_usb_charger_connect(struct ci_hdrc *ci, int is_active);
+void ci_hdrc_gadget_connect(struct usb_gadget *gadget, int is_active);
#else
@@ -95,6 +97,17 @@ static inline void ci_hdrc_gadget_destroy(struct ci_hdrc *ci)
}
+static inline int ci_usb_charger_connect(struct ci_hdrc *ci, int is_active)
+{
+ return 0;
+}
+
+static inline void ci_hdrc_gadget_connect(struct usb_gadget *gadget,
+ int is_active)
+{
+
+}
+
#endif
#endif /* __DRIVERS_USB_CHIPIDEA_UDC_H */
diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c
index 078c1fdce493..5666061588f5 100644
--- a/drivers/usb/chipidea/usbmisc_imx.c
+++ b/drivers/usb/chipidea/usbmisc_imx.c
@@ -8,6 +8,8 @@
#include <linux/err.h>
#include <linux/io.h>
#include <linux/delay.h>
+#include <linux/usb/otg.h>
+#include <linux/regulator/consumer.h>
#include "ci_hdrc_imx.h"
@@ -93,6 +95,18 @@
#define VF610_OVER_CUR_DIS BIT(7)
#define MX7D_USBNC_USB_CTRL2 0x4
+/* The default DM/DP value is pull-down */
+#define MX7D_USBNC_USB_CTRL2_DM_OVERRIDE_EN BIT(15)
+#define MX7D_USBNC_USB_CTRL2_DM_OVERRIDE_VAL BIT(14)
+#define MX7D_USBNC_USB_CTRL2_DP_OVERRIDE_EN BIT(13)
+#define MX7D_USBNC_USB_CTRL2_DP_OVERRIDE_VAL BIT(12)
+#define MX7D_USBNC_USB_CTRL2_DP_DM_MASK (BIT(12) | BIT(13) | \
+ BIT(14) | BIT(15))
+#define MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN BIT(8)
+#define MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK (BIT(7) | BIT(6))
+#define MX7D_USBNC_USB_CTRL2_OPMODE(v) (v << 6)
+#define MX7D_USBNC_USB_CTRL2_OPMODE_NON_DRIVING MX7D_USBNC_USB_CTRL2_OPMODE(1)
+#define MX7D_USBNC_AUTO_RESUME BIT(2)
#define MX7D_USB_VBUS_WAKEUP_SOURCE_MASK 0x3
#define MX7D_USB_VBUS_WAKEUP_SOURCE(v) (v << 0)
#define MX7D_USB_VBUS_WAKEUP_SOURCE_VBUS MX7D_USB_VBUS_WAKEUP_SOURCE(0)
@@ -100,6 +114,27 @@
#define MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID MX7D_USB_VBUS_WAKEUP_SOURCE(2)
#define MX7D_USB_VBUS_WAKEUP_SOURCE_SESS_END MX7D_USB_VBUS_WAKEUP_SOURCE(3)
+#define MX7D_USB_OTG_PHY_CFG1 0x30
+#define TXPREEMPAMPTUNE0_BIT 28
+#define TXPREEMPAMPTUNE0_MASK (3 << 28)
+#define TXVREFTUNE0_BIT 20
+#define TXVREFTUNE0_MASK (0xf << 20)
+#define MX7D_USB_OTG_PHY_CFG2_DRVVBUS0 BIT(16)
+#define MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB BIT(3)
+#define MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 BIT(2)
+#define MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 BIT(1)
+#define MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL BIT(0)
+#define MX7D_USB_OTG_PHY_CFG2 0x34
+
+#define MX7D_USB_OTG_PHY_STATUS 0x3c
+#define MX7D_USB_OTG_PHY_STATUS_CHRGDET BIT(29)
+#define MX7D_USB_OTG_PHY_STATUS_VBUS_VLD BIT(3)
+#define MX7D_USB_OTG_PHY_STATUS_LINE_STATE1 BIT(1)
+#define MX7D_USB_OTG_PHY_STATUS_LINE_STATE0 BIT(0)
+
+#define MX6_USB_OTG_WAKEUP_BITS (MX6_BM_WAKEUP_ENABLE | MX6_BM_VBUS_WAKEUP | \
+ MX6_BM_ID_WAKEUP)
+
struct usbmisc_ops {
/* It's called once when probe a usb device */
int (*init)(struct imx_usbmisc_data *data);
@@ -111,6 +146,11 @@ struct usbmisc_ops {
int (*hsic_set_connect)(struct imx_usbmisc_data *data);
/* It's called during suspend/resume */
int (*hsic_set_clk)(struct imx_usbmisc_data *data, bool enabled);
+ /* It's called when system resume from usb power lost */
+ int (*power_lost_check)(struct imx_usbmisc_data *data);
+ /* usb charger detection */
+ int (*charger_detection)(struct imx_usbmisc_data *data);
+ void (*vbus_comparator_on)(struct imx_usbmisc_data *data, bool on);
};
struct imx_usbmisc {
@@ -119,6 +159,8 @@ struct imx_usbmisc {
const struct usbmisc_ops *ops;
};
+static struct regulator *vbus_wakeup_reg;
+
static inline bool is_imx53_usbmisc(struct imx_usbmisc_data *data);
static int usbmisc_imx25_init(struct imx_usbmisc_data *data)
@@ -330,15 +372,26 @@ static int usbmisc_imx53_init(struct imx_usbmisc_data *data)
return 0;
}
+static u32 usbmisc_wakeup_setting(struct imx_usbmisc_data *data)
+{
+ u32 wakeup_setting = MX6_USB_OTG_WAKEUP_BITS;
+
+ if (data->ext_id || data->available_role != USB_DR_MODE_OTG)
+ wakeup_setting &= ~MX6_BM_ID_WAKEUP;
+
+ if (data->ext_vbus || data->available_role == USB_DR_MODE_HOST)
+ wakeup_setting &= ~MX6_BM_VBUS_WAKEUP;
+
+ return wakeup_setting;
+}
+
static int usbmisc_imx6q_set_wakeup
(struct imx_usbmisc_data *data, bool enabled)
{
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
unsigned long flags;
- u32 val;
- u32 wakeup_setting = (MX6_BM_WAKEUP_ENABLE |
- MX6_BM_VBUS_WAKEUP | MX6_BM_ID_WAKEUP);
int ret = 0;
+ u32 val;
if (data->index > 3)
return -EINVAL;
@@ -346,14 +399,22 @@ static int usbmisc_imx6q_set_wakeup
spin_lock_irqsave(&usbmisc->lock, flags);
val = readl(usbmisc->base + data->index * 4);
if (enabled) {
- val |= wakeup_setting;
+ val &= ~MX6_USB_OTG_WAKEUP_BITS;
+ val |= usbmisc_wakeup_setting(data);
+ if (vbus_wakeup_reg) {
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+ ret = regulator_enable(vbus_wakeup_reg);
+ spin_lock_irqsave(&usbmisc->lock, flags);
+ }
} else {
if (val & MX6_BM_WAKEUP_INTR)
pr_debug("wakeup int at ci_hdrc.%d\n", data->index);
- val &= ~wakeup_setting;
+ val &= ~MX6_USB_OTG_WAKEUP_BITS;
}
writel(val, usbmisc->base + data->index * 4);
spin_unlock_irqrestore(&usbmisc->lock, flags);
+ if (vbus_wakeup_reg && regulator_is_enabled(vbus_wakeup_reg))
+ regulator_disable(vbus_wakeup_reg);
return ret;
}
@@ -547,17 +608,17 @@ static int usbmisc_imx7d_set_wakeup
struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
unsigned long flags;
u32 val;
- u32 wakeup_setting = (MX6_BM_WAKEUP_ENABLE |
- MX6_BM_VBUS_WAKEUP | MX6_BM_ID_WAKEUP);
spin_lock_irqsave(&usbmisc->lock, flags);
val = readl(usbmisc->base);
if (enabled) {
- writel(val | wakeup_setting, usbmisc->base);
+ val &= ~MX6_USB_OTG_WAKEUP_BITS;
+ val |= usbmisc_wakeup_setting(data);
+ writel(val, usbmisc->base);
} else {
if (val & MX6_BM_WAKEUP_INTR)
dev_dbg(data->dev, "wakeup int\n");
- writel(val & ~wakeup_setting, usbmisc->base);
+ writel(val & ~MX6_USB_OTG_WAKEUP_BITS, usbmisc->base);
}
spin_unlock_irqrestore(&usbmisc->lock, flags);
@@ -594,10 +655,32 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data)
reg |= MX6_BM_PWR_POLARITY;
writel(reg, usbmisc->base);
- reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
- reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK;
- writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID,
- usbmisc->base + MX7D_USBNC_USB_CTRL2);
+ /* SoC non-burst setting */
+ reg = readl(usbmisc->base);
+ writel(reg | MX6_BM_NON_BURST_SETTING, usbmisc->base);
+
+ if (!data->hsic) {
+ reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
+ reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK;
+ writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID
+ | MX7D_USBNC_AUTO_RESUME,
+ usbmisc->base + MX7D_USBNC_USB_CTRL2);
+ /* PHY tuning for signal quality */
+ reg = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG1);
+ if (data->emp_curr_control && data->emp_curr_control <=
+ (TXPREEMPAMPTUNE0_MASK >> TXPREEMPAMPTUNE0_BIT)) {
+ reg &= ~TXPREEMPAMPTUNE0_MASK;
+ reg |= (data->emp_curr_control << TXPREEMPAMPTUNE0_BIT);
+ }
+
+ if (data->dc_vol_level_adjust && data->dc_vol_level_adjust <=
+ (TXVREFTUNE0_MASK >> TXVREFTUNE0_BIT)) {
+ reg &= ~TXVREFTUNE0_MASK;
+ reg |= (data->dc_vol_level_adjust << TXVREFTUNE0_BIT);
+ }
+
+ writel(reg, usbmisc->base + MX7D_USB_OTG_PHY_CFG1);
+ }
spin_unlock_irqrestore(&usbmisc->lock, flags);
@@ -606,6 +689,315 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data)
return 0;
}
+static int usbmisc_imx7d_power_lost_check(struct imx_usbmisc_data *data)
+{
+ struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&usbmisc->lock, flags);
+ val = readl(usbmisc->base);
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+ /*
+ * Here use a power on reset value to judge
+ * if the controller experienced a power lost
+ */
+ if (val == 0x30001000)
+ return 1;
+ else
+ return 0;
+}
+
+static int imx7d_charger_secondary_detection(struct imx_usbmisc_data *data)
+{
+ struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+ struct usb_phy *usb_phy = data->usb_phy;
+ int val;
+ unsigned long flags;
+
+ /* VDM_SRC is connected to D- and IDP_SINK is connected to D+ */
+ spin_lock_irqsave(&usbmisc->lock, flags);
+ val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+ writel(val | MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 |
+ MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 |
+ MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL,
+ usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+ usleep_range(1000, 2000);
+
+ /*
+ * Per BC 1.2, check voltage of D+:
+ * DCP: if greater than VDAT_REF;
+ * CDP: if less than VDAT_REF.
+ */
+ val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS);
+ if (val & MX7D_USB_OTG_PHY_STATUS_CHRGDET) {
+ dev_dbg(data->dev, "It is a dedicate charging port\n");
+ usb_phy->chg_type = DCP_TYPE;
+ } else {
+ dev_dbg(data->dev, "It is a charging downstream port\n");
+ usb_phy->chg_type = CDP_TYPE;
+ }
+
+ return 0;
+}
+
+static void imx7_disable_charger_detector(struct imx_usbmisc_data *data)
+{
+ struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&usbmisc->lock, flags);
+ val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+ val &= ~(MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB |
+ MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 |
+ MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 |
+ MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL);
+ writel(val, usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+
+ /* Set OPMODE to be 2'b00 and disable its override */
+ val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
+ val &= ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK;
+ writel(val, usbmisc->base + MX7D_USBNC_USB_CTRL2);
+
+ val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
+ writel(val & ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN,
+ usbmisc->base + MX7D_USBNC_USB_CTRL2);
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+}
+
+static int imx7d_charger_data_contact_detect(struct imx_usbmisc_data *data)
+{
+ struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+ unsigned long flags;
+ u32 val;
+ int i, data_pin_contact_count = 0;
+
+ /* Enable Data Contact Detect (DCD) per the USB BC 1.2 */
+ spin_lock_irqsave(&usbmisc->lock, flags);
+ val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+ writel(val | MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB,
+ usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+ for (i = 0; i < 100; i = i + 1) {
+ val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS);
+ if (!(val & MX7D_USB_OTG_PHY_STATUS_LINE_STATE0)) {
+ if (data_pin_contact_count++ > 5)
+ /* Data pin makes contact */
+ break;
+ usleep_range(5000, 10000);
+ } else {
+ data_pin_contact_count = 0;
+ usleep_range(5000, 6000);
+ }
+ }
+
+ /* Disable DCD after finished data contact check */
+ spin_lock_irqsave(&usbmisc->lock, flags);
+ val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+ writel(val & ~MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB,
+ usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+ if (i == 100) {
+ dev_err(data->dev,
+ "VBUS is coming from a dedicated power supply.\n");
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static int imx7d_charger_primary_detection(struct imx_usbmisc_data *data)
+{
+ struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+ struct usb_phy *usb_phy = data->usb_phy;
+ unsigned long flags;
+ u32 val;
+
+ /* VDP_SRC is connected to D+ and IDM_SINK is connected to D- */
+ spin_lock_irqsave(&usbmisc->lock, flags);
+ val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+ val &= ~MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL;
+ writel(val | MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 |
+ MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0,
+ usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+ usleep_range(1000, 2000);
+
+ /* Check if D- is less than VDAT_REF to determine an SDP per BC 1.2 */
+ val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS);
+ if (!(val & MX7D_USB_OTG_PHY_STATUS_CHRGDET)) {
+ dev_dbg(data->dev, "It is a standard downstream port\n");
+ usb_phy->chg_type = SDP_TYPE;
+ }
+
+ return 0;
+}
+
+/**
+ * Whole charger detection process:
+ * 1. OPMODE override to be non-driving
+ * 2. Data contact check
+ * 3. Primary detection
+ * 4. Secondary detection
+ * 5. Disable charger detection
+ */
+static int imx7d_charger_detection(struct imx_usbmisc_data *data)
+{
+ struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+ struct usb_phy *usb_phy = data->usb_phy;
+ unsigned long flags;
+ u32 val;
+ int ret;
+
+ /* Check if vbus is valid */
+ val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS);
+ if (!(val & MX7D_USB_OTG_PHY_STATUS_VBUS_VLD)) {
+ dev_err(data->dev, "vbus is error\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Keep OPMODE to be non-driving mode during the whole
+ * charger detection process.
+ */
+ spin_lock_irqsave(&usbmisc->lock, flags);
+ val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
+ val &= ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK;
+ val |= MX7D_USBNC_USB_CTRL2_OPMODE_NON_DRIVING;
+ writel(val, usbmisc->base + MX7D_USBNC_USB_CTRL2);
+
+ val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
+ writel(val | MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN,
+ usbmisc->base + MX7D_USBNC_USB_CTRL2);
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+ ret = imx7d_charger_data_contact_detect(data);
+ if (ret)
+ return ret;
+
+ ret = imx7d_charger_primary_detection(data);
+ if (!ret && usb_phy->chg_type != SDP_TYPE)
+ ret = imx7d_charger_secondary_detection(data);
+
+ imx7_disable_charger_detector(data);
+
+ return ret;
+}
+
+static void usbmisc_imx7d_vbus_comparator_on(struct imx_usbmisc_data *data,
+ bool on)
+{
+ unsigned long flags;
+ struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+ u32 val;
+
+ if (data->hsic)
+ return;
+
+ spin_lock_irqsave(&usbmisc->lock, flags);
+ /*
+ * Disable VBUS valid comparator when in suspend mode,
+ * when OTG is disabled and DRVVBUS0 is asserted case
+ * the Bandgap circuitry and VBUS Valid comparator are
+ * still powered, even in Suspend or Sleep mode.
+ */
+ val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+ if (on)
+ val |= MX7D_USB_OTG_PHY_CFG2_DRVVBUS0;
+ else
+ val &= ~MX7D_USB_OTG_PHY_CFG2_DRVVBUS0;
+
+ writel(val, usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+}
+
+static int usbmisc_imx6sx_power_lost_check(struct imx_usbmisc_data *data)
+{
+ struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&usbmisc->lock, flags);
+ val = readl(usbmisc->base + data->index * 4);
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+ /*
+ * Here use a power on reset value to judge
+ * if the controller experienced a power lost
+ */
+ if (val == 0x30001000)
+ return 1;
+ else
+ return 0;
+}
+
+static int usbmisc_imx7ulp_init(struct imx_usbmisc_data *data)
+{
+ struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+ unsigned long flags;
+ u32 reg;
+
+ if (data->index >= 1)
+ return -EINVAL;
+
+ spin_lock_irqsave(&usbmisc->lock, flags);
+ reg = readl(usbmisc->base);
+ if (data->disable_oc) {
+ reg |= MX6_BM_OVER_CUR_DIS;
+ } else {
+ reg &= ~MX6_BM_OVER_CUR_DIS;
+
+ /*
+ * If the polarity is not configured keep it as setup by the
+ * bootloader.
+ */
+ if (data->oc_pol_configured && data->oc_pol_active_low)
+ reg |= MX6_BM_OVER_CUR_POLARITY;
+ else if (data->oc_pol_configured)
+ reg &= ~MX6_BM_OVER_CUR_POLARITY;
+ }
+ /* If the polarity is not set keep it as setup by the bootlader */
+ if (data->pwr_pol == 1)
+ reg |= MX6_BM_PWR_POLARITY;
+ writel(reg, usbmisc->base);
+
+ /* SoC non-burst setting */
+ reg = readl(usbmisc->base);
+ writel(reg | MX6_BM_NON_BURST_SETTING, usbmisc->base);
+
+ if (data->hsic) {
+ reg = readl(usbmisc->base);
+ writel(reg | MX6_BM_UTMI_ON_CLOCK, usbmisc->base);
+
+ reg = readl(usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET);
+ reg |= MX6_BM_HSIC_EN | MX6_BM_HSIC_CLK_ON;
+ writel(reg, usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET);
+
+ /*
+ * For non-HSIC controller, the autoresume is enabled
+ * at MXS PHY driver (usbphy_ctrl bit18).
+ */
+ reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
+ writel(reg | MX7D_USBNC_AUTO_RESUME,
+ usbmisc->base + MX7D_USBNC_USB_CTRL2);
+ } else {
+ reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
+ reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK;
+ writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID,
+ usbmisc->base + MX7D_USBNC_USB_CTRL2);
+ }
+
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+ usbmisc_imx7d_set_wakeup(data, false);
+
+ return 0;
+}
static const struct usbmisc_ops imx25_usbmisc_ops = {
.init = usbmisc_imx25_init,
.post = usbmisc_imx25_post,
@@ -639,11 +1031,23 @@ static const struct usbmisc_ops imx6sx_usbmisc_ops = {
.init = usbmisc_imx6sx_init,
.hsic_set_connect = usbmisc_imx6_hsic_set_connect,
.hsic_set_clk = usbmisc_imx6_hsic_set_clk,
+ .power_lost_check = usbmisc_imx6sx_power_lost_check,
};
static const struct usbmisc_ops imx7d_usbmisc_ops = {
+ .init = usbmisc_imx7ulp_init,
+ .set_wakeup = usbmisc_imx7d_set_wakeup,
+ .power_lost_check = usbmisc_imx7d_power_lost_check,
+ .charger_detection = imx7d_charger_detection,
+ .vbus_comparator_on = usbmisc_imx7d_vbus_comparator_on,
+};
+
+static const struct usbmisc_ops imx7ulp_usbmisc_ops = {
.init = usbmisc_imx7d_init,
.set_wakeup = usbmisc_imx7d_set_wakeup,
+ .power_lost_check = usbmisc_imx7d_power_lost_check,
+ .hsic_set_connect = usbmisc_imx6_hsic_set_connect,
+ .hsic_set_clk = usbmisc_imx6_hsic_set_clk,
};
static inline bool is_imx53_usbmisc(struct imx_usbmisc_data *data)
@@ -670,18 +1074,31 @@ EXPORT_SYMBOL_GPL(imx_usbmisc_init);
int imx_usbmisc_init_post(struct imx_usbmisc_data *data)
{
struct imx_usbmisc *usbmisc;
+ int ret = 0;
if (!data)
return 0;
usbmisc = dev_get_drvdata(data->dev);
- if (!usbmisc->ops->post)
- return 0;
- return usbmisc->ops->post(data);
+ if (usbmisc->ops->post)
+ ret = usbmisc->ops->post(data);
+ if (ret) {
+ dev_err(data->dev, "post init failed, ret=%d\n", ret);
+ return ret;
+ }
+
+ if (usbmisc->ops->set_wakeup)
+ ret = usbmisc->ops->set_wakeup(data, false);
+ if (ret) {
+ dev_err(data->dev, "set_wakeup failed, ret=%d\n", ret);
+ return ret;
+ }
+
+ return 0;
}
EXPORT_SYMBOL_GPL(imx_usbmisc_init_post);
-int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *data, bool enabled)
+int imx_usbmisc_hsic_set_connect(struct imx_usbmisc_data *data)
{
struct imx_usbmisc *usbmisc;
@@ -689,39 +1106,122 @@ int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *data, bool enabled)
return 0;
usbmisc = dev_get_drvdata(data->dev);
- if (!usbmisc->ops->set_wakeup)
+ if (!usbmisc->ops->hsic_set_connect || !data->hsic)
return 0;
- return usbmisc->ops->set_wakeup(data, enabled);
+ return usbmisc->ops->hsic_set_connect(data);
}
-EXPORT_SYMBOL_GPL(imx_usbmisc_set_wakeup);
+EXPORT_SYMBOL_GPL(imx_usbmisc_hsic_set_connect);
-int imx_usbmisc_hsic_set_connect(struct imx_usbmisc_data *data)
+int imx_usbmisc_charger_detection(struct imx_usbmisc_data *data, bool connect)
{
struct imx_usbmisc *usbmisc;
+ struct usb_phy *usb_phy;
+ int ret = 0;
if (!data)
- return 0;
+ return -EINVAL;
usbmisc = dev_get_drvdata(data->dev);
- if (!usbmisc->ops->hsic_set_connect || !data->hsic)
- return 0;
- return usbmisc->ops->hsic_set_connect(data);
+ usb_phy = data->usb_phy;
+ if (!usbmisc->ops->charger_detection)
+ return -ENOTSUPP;
+
+ if (connect) {
+ ret = usbmisc->ops->charger_detection(data);
+ if (ret) {
+ dev_err(data->dev,
+ "Error occurs during detection: %d\n",
+ ret);
+ usb_phy->chg_state = USB_CHARGER_ABSENT;
+ } else {
+ usb_phy->chg_state = USB_CHARGER_PRESENT;
+ }
+ } else {
+ usb_phy->chg_state = USB_CHARGER_ABSENT;
+ usb_phy->chg_type = UNKNOWN_TYPE;
+ }
+ return ret;
}
-EXPORT_SYMBOL_GPL(imx_usbmisc_hsic_set_connect);
+EXPORT_SYMBOL_GPL(imx_usbmisc_charger_detection);
-int imx_usbmisc_hsic_set_clk(struct imx_usbmisc_data *data, bool on)
+int imx_usbmisc_suspend(struct imx_usbmisc_data *data, bool wakeup)
{
struct imx_usbmisc *usbmisc;
+ int ret = 0;
if (!data)
return 0;
usbmisc = dev_get_drvdata(data->dev);
- if (!usbmisc->ops->hsic_set_clk || !data->hsic)
+
+ if (usbmisc->ops->vbus_comparator_on)
+ usbmisc->ops->vbus_comparator_on(data, false);
+
+ if (wakeup && usbmisc->ops->set_wakeup)
+ ret = usbmisc->ops->set_wakeup(data, true);
+ if (ret) {
+ dev_err(data->dev, "set_wakeup failed, ret=%d\n", ret);
+ return ret;
+ }
+
+ if (usbmisc->ops->hsic_set_clk && data->hsic)
+ ret = usbmisc->ops->hsic_set_clk(data, false);
+ if (ret) {
+ dev_err(data->dev, "set_wakeup failed, ret=%d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(imx_usbmisc_suspend);
+
+int imx_usbmisc_resume(struct imx_usbmisc_data *data, bool wakeup)
+{
+ struct imx_usbmisc *usbmisc;
+ int ret = 0;
+
+ if (!data)
return 0;
- return usbmisc->ops->hsic_set_clk(data, on);
+
+ usbmisc = dev_get_drvdata(data->dev);
+
+ if (usbmisc->ops->power_lost_check)
+ ret = usbmisc->ops->power_lost_check(data);
+ if (ret > 0) {
+ /* re-init if resume from power lost */
+ ret = imx_usbmisc_init(data);
+ if (ret) {
+ dev_err(data->dev, "re-init failed, ret=%d\n", ret);
+ return ret;
+ }
+ }
+
+ if (wakeup && usbmisc->ops->set_wakeup)
+ ret = usbmisc->ops->set_wakeup(data, false);
+ if (ret) {
+ dev_err(data->dev, "set_wakeup failed, ret=%d\n", ret);
+ return ret;
+ }
+
+ if (usbmisc->ops->hsic_set_clk && data->hsic)
+ ret = usbmisc->ops->hsic_set_clk(data, true);
+ if (ret) {
+ dev_err(data->dev, "set_wakeup failed, ret=%d\n", ret);
+ goto hsic_set_clk_fail;
+ }
+
+ if (usbmisc->ops->vbus_comparator_on)
+ usbmisc->ops->vbus_comparator_on(data, true);
+
+ return 0;
+
+hsic_set_clk_fail:
+ if (wakeup && usbmisc->ops->set_wakeup)
+ usbmisc->ops->set_wakeup(data, true);
+ return ret;
}
-EXPORT_SYMBOL_GPL(imx_usbmisc_hsic_set_clk);
+EXPORT_SYMBOL_GPL(imx_usbmisc_resume);
+
static const struct of_device_id usbmisc_imx_dt_ids[] = {
{
.compatible = "fsl,imx25-usbmisc",
@@ -765,7 +1265,7 @@ static const struct of_device_id usbmisc_imx_dt_ids[] = {
},
{
.compatible = "fsl,imx7ulp-usbmisc",
- .data = &imx7d_usbmisc_ops,
+ .data = &imx7ulp_usbmisc_ops,
},
{ /* sentinel */ }
};
@@ -793,6 +1293,18 @@ static int usbmisc_imx_probe(struct platform_device *pdev)
data->ops = (const struct usbmisc_ops *)of_id->data;
platform_set_drvdata(pdev, data);
+ vbus_wakeup_reg = devm_regulator_get_optional(&pdev->dev, "vbus-wakeup");
+ if (PTR_ERR(vbus_wakeup_reg) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ else if (PTR_ERR(vbus_wakeup_reg) == -ENODEV)
+ /* no vbus regualator is needed */
+ vbus_wakeup_reg = NULL;
+ else if (IS_ERR(vbus_wakeup_reg)) {
+ dev_err(&pdev->dev, "Getting regulator error: %ld\n",
+ PTR_ERR(vbus_wakeup_reg));
+ return PTR_ERR(vbus_wakeup_reg);
+ }
+
return 0;
}
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index f225eaa98ff8..ec01bf5837c2 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -2104,6 +2104,140 @@ int usb_hcd_get_frame_number (struct usb_device *udev)
}
/*-------------------------------------------------------------------------*/
+#ifdef CONFIG_USB_HCD_TEST_MODE
+
+static void usb_ehset_completion(struct urb *urb)
+{
+ struct completion *done = urb->context;
+
+ complete(done);
+}
+/*
+ * Allocate and initialize a control URB. This request will be used by the
+ * EHSET SINGLE_STEP_SET_FEATURE test in which the DATA and STATUS stages
+ * of the GetDescriptor request are sent 15 seconds after the SETUP stage.
+ * Return NULL if failed.
+ */
+static struct urb *request_single_step_set_feature_urb(
+ struct usb_device *udev,
+ void *dr,
+ void *buf,
+ struct completion *done
+) {
+ struct urb *urb;
+ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+ struct usb_host_endpoint *ep;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ return NULL;
+
+ urb->pipe = usb_rcvctrlpipe(udev, 0);
+ ep = (usb_pipein(urb->pipe) ? udev->ep_in : udev->ep_out)
+ [usb_pipeendpoint(urb->pipe)];
+ if (!ep) {
+ usb_free_urb(urb);
+ return NULL;
+ }
+
+ urb->ep = ep;
+ urb->dev = udev;
+ urb->setup_packet = (void *)dr;
+ urb->transfer_buffer = buf;
+ urb->transfer_buffer_length = USB_DT_DEVICE_SIZE;
+ urb->complete = usb_ehset_completion;
+ urb->status = -EINPROGRESS;
+ urb->actual_length = 0;
+ urb->transfer_flags = URB_DIR_IN;
+ usb_get_urb(urb);
+ atomic_inc(&urb->use_count);
+ atomic_inc(&urb->dev->urbnum);
+ urb->setup_dma = dma_map_single(
+ hcd->self.sysdev,
+ urb->setup_packet,
+ sizeof(struct usb_ctrlrequest),
+ DMA_TO_DEVICE);
+ urb->transfer_dma = dma_map_single(
+ hcd->self.sysdev,
+ urb->transfer_buffer,
+ urb->transfer_buffer_length,
+ DMA_FROM_DEVICE);
+ urb->context = done;
+ return urb;
+}
+
+int ehset_single_step_set_feature(struct usb_hcd *hcd, int port)
+{
+ int retval = -ENOMEM;
+ struct usb_ctrlrequest *dr;
+ struct urb *urb;
+ struct usb_device *udev;
+ struct usb_device_descriptor *buf;
+ DECLARE_COMPLETION_ONSTACK(done);
+
+ /* Obtain udev of the rhub's child port */
+ udev = usb_hub_find_child(hcd->self.root_hub, port);
+ if (!udev) {
+ dev_err(hcd->self.controller, "No device attached to the RootHub\n");
+ return -ENODEV;
+ }
+ buf = kmalloc(USB_DT_DEVICE_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
+ if (!dr) {
+ kfree(buf);
+ return -ENOMEM;
+ }
+
+ /* Fill Setup packet for GetDescriptor */
+ dr->bRequestType = USB_DIR_IN;
+ dr->bRequest = USB_REQ_GET_DESCRIPTOR;
+ dr->wValue = cpu_to_le16(USB_DT_DEVICE << 8);
+ dr->wIndex = 0;
+ dr->wLength = cpu_to_le16(USB_DT_DEVICE_SIZE);
+ urb = request_single_step_set_feature_urb(udev, dr, buf, &done);
+ if (!urb)
+ goto cleanup;
+
+ /* Submit just the SETUP stage */
+ retval = hcd->driver->submit_single_step_set_feature(hcd, urb, 1);
+ if (retval)
+ goto out1;
+ if (!wait_for_completion_timeout(&done, msecs_to_jiffies(2000))) {
+ usb_kill_urb(urb);
+ retval = -ETIMEDOUT;
+ dev_err(hcd->self.controller,
+ "%s SETUP stage timed out on ep0\n", __func__);
+ goto out1;
+ }
+ msleep(15 * 1000);
+
+ /* Complete remaining DATA and STATUS stages using the same URB */
+ urb->status = -EINPROGRESS;
+ usb_get_urb(urb);
+ atomic_inc(&urb->use_count);
+ atomic_inc(&urb->dev->urbnum);
+ retval = hcd->driver->submit_single_step_set_feature(hcd, urb, 0);
+ if (!retval && !wait_for_completion_timeout(&done,
+ msecs_to_jiffies(2000))) {
+ usb_kill_urb(urb);
+ retval = -ETIMEDOUT;
+ dev_err(hcd->self.controller,
+ "%s IN stage timed out on ep0\n", __func__);
+ }
+out1:
+ usb_free_urb(urb);
+cleanup:
+ kfree(dr);
+ kfree(buf);
+ return retval;
+}
+EXPORT_SYMBOL_GPL(ehset_single_step_set_feature);
+#endif /* CONFIG_USB_HCD_TEST_MODE */
+
+/*-------------------------------------------------------------------------*/
#ifdef CONFIG_PM
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 236313f41f4a..c39985ea2eca 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -4705,7 +4705,8 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1,
}
if (r) {
if (r != -ENODEV)
- dev_err(&udev->dev, "device descriptor read/64, error %d\n",
+ dev_err(&udev->dev,
+ "device no response, device descriptor read/64, error %d\n",
r);
retval = -EMSGSIZE;
continue;
diff --git a/drivers/usb/core/otg_whitelist.h b/drivers/usb/core/otg_whitelist.h
index 2ae90158ded7..9d0896174628 100644
--- a/drivers/usb/core/otg_whitelist.h
+++ b/drivers/usb/core/otg_whitelist.h
@@ -13,35 +13,62 @@
*/
static struct usb_device_id whitelist_table[] = {
-
-/* hubs are optional in OTG, but very handy ... */
-{ USB_DEVICE_INFO(USB_CLASS_HUB, 0, 0), },
-{ USB_DEVICE_INFO(USB_CLASS_HUB, 0, 1), },
-
-#ifdef CONFIG_USB_PRINTER /* ignoring nonstatic linkage! */
-/* FIXME actually, printers are NOT supposed to use device classes;
- * they're supposed to use interface classes...
- */
-{ USB_DEVICE_INFO(7, 1, 1) },
-{ USB_DEVICE_INFO(7, 1, 2) },
-{ USB_DEVICE_INFO(7, 1, 3) },
+/* Add FSL i.mx whitelist, the default list is for USB Compliance Test */
+#if defined(CONFIG_USB_EHSET_TEST_FIXTURE) \
+ || defined(CONFIG_USB_EHSET_TEST_FIXTURE_MODULE)
+#define TEST_SE0_NAK_PID 0x0101
+#define TEST_J_PID 0x0102
+#define TEST_K_PID 0x0103
+#define TEST_PACKET_PID 0x0104
+#define TEST_HS_HOST_PORT_SUSPEND_RESUME 0x0106
+#define TEST_SINGLE_STEP_GET_DEV_DESC 0x0107
+#define TEST_SINGLE_STEP_SET_FEATURE 0x0108
+{ USB_DEVICE(0x1a0a, TEST_SE0_NAK_PID) },
+{ USB_DEVICE(0x1a0a, TEST_J_PID) },
+{ USB_DEVICE(0x1a0a, TEST_K_PID) },
+{ USB_DEVICE(0x1a0a, TEST_PACKET_PID) },
+{ USB_DEVICE(0x1a0a, TEST_HS_HOST_PORT_SUSPEND_RESUME) },
+{ USB_DEVICE(0x1a0a, TEST_SINGLE_STEP_GET_DEV_DESC) },
+{ USB_DEVICE(0x1a0a, TEST_SINGLE_STEP_SET_FEATURE) },
#endif
-#ifdef CONFIG_USB_NET_CDCETHER
-/* Linux-USB CDC Ethernet gadget */
-{ USB_DEVICE(0x0525, 0xa4a1), },
-/* Linux-USB CDC Ethernet + RNDIS gadget */
-{ USB_DEVICE(0x0525, 0xa4a2), },
-#endif
+#define USB_INTERFACE_CLASS_INFO(cl) \
+ .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS, \
+ .bInterfaceClass = (cl)
-#if IS_ENABLED(CONFIG_USB_TEST)
-/* gadget zero, for testing */
-{ USB_DEVICE(0x0525, 0xa4a0), },
+{USB_INTERFACE_CLASS_INFO(USB_CLASS_HUB) },
+#if defined(CONFIG_USB_STORAGE) || defined(CONFIG_USB_STORAGE_MODULE)
+{USB_INTERFACE_CLASS_INFO(USB_CLASS_MASS_STORAGE) },
+#endif
+#if defined(CONFIG_USB_HID) || defined(CONFIG_USB_HID_MODULE)
+{USB_INTERFACE_CLASS_INFO(USB_CLASS_HID) },
#endif
{ } /* Terminating entry */
};
+static bool match_int_class(struct usb_device_id *id, struct usb_device *udev)
+{
+ struct usb_host_config *c;
+ int num_configs, i;
+
+ /* Copy the code from generic.c */
+ c = udev->config;
+ num_configs = udev->descriptor.bNumConfigurations;
+ for (i = 0; i < num_configs; (i++, c++)) {
+ struct usb_interface_descriptor *desc = NULL;
+
+ /* It's possible that a config has no interfaces! */
+ if (c->desc.bNumInterfaces > 0)
+ desc = &c->intf_cache[0]->altsetting->desc;
+
+ if (desc && (desc->bInterfaceClass == id->bInterfaceClass))
+ return true;
+ }
+
+ return false;
+}
+
static int is_targeted(struct usb_device *dev)
{
struct usb_device_id *id = whitelist_table;
@@ -90,6 +117,10 @@ static int is_targeted(struct usb_device *dev)
(id->bDeviceProtocol != dev->descriptor.bDeviceProtocol))
continue;
+ if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) &&
+ (!match_int_class(id, dev)))
+ continue;
+
return 1;
}
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 97d6ae3c4df2..d1dc2e940af1 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -894,6 +894,73 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, cfg);
}
+static void dwc3_set_power_down_clk_scale(struct dwc3 *dwc)
+{
+ u32 reg, scale;
+
+ if (dwc->num_clks == 0)
+ return;
+
+ /*
+ * The power down scale field specifies how many suspend_clk
+ * periods fit into a 16KHz clock period. When performing
+ * the division, round up the remainder.
+ */
+ scale = DIV_ROUND_UP(clk_get_rate(dwc->clks[2].clk), 16384);
+ reg = dwc3_readl(dwc->regs, DWC3_GCTL);
+ reg &= ~(DWC3_GCTL_PWRDNSCALE_MASK);
+ reg |= DWC3_GCTL_PWRDNSCALE(scale);
+ dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+}
+
+#ifdef CONFIG_OF
+struct dwc3_cache_type {
+ u8 transfer_type_datard;
+ u8 transfer_type_descrd;
+ u8 transfer_type_datawr;
+ u8 transfer_type_descwr;
+};
+
+static const struct dwc3_cache_type layerscape_dwc3_cache_type = {
+ .transfer_type_datard = 2,
+ .transfer_type_descrd = 2,
+ .transfer_type_datawr = 2,
+ .transfer_type_descwr = 2,
+};
+
+/**
+ * dwc3_set_cache_type - Configure cache type registers
+ * @dwc: Pointer to our controller context structure
+ */
+static void dwc3_set_cache_type(struct dwc3 *dwc)
+{
+ u32 tmp, reg;
+ const struct dwc3_cache_type *cache_type =
+ device_get_match_data(dwc->dev);
+
+ if (cache_type) {
+ reg = dwc3_readl(dwc->regs, DWC3_GSBUSCFG0);
+ tmp = reg;
+
+ reg &= ~DWC3_GSBUSCFG0_DATARD(~0);
+ reg |= DWC3_GSBUSCFG0_DATARD(cache_type->transfer_type_datard);
+
+ reg &= ~DWC3_GSBUSCFG0_DESCRD(~0);
+ reg |= DWC3_GSBUSCFG0_DESCRD(cache_type->transfer_type_descrd);
+
+ reg &= ~DWC3_GSBUSCFG0_DATAWR(~0);
+ reg |= DWC3_GSBUSCFG0_DATAWR(cache_type->transfer_type_datawr);
+
+ reg &= ~DWC3_GSBUSCFG0_DESCWR(~0);
+ reg |= DWC3_GSBUSCFG0_DESCWR(cache_type->transfer_type_descwr);
+
+ if (tmp != reg)
+ dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, reg);
+ }
+}
+#endif
+
+
/**
* dwc3_core_init - Low-level initialization of DWC3 Core
* @dwc: Pointer to our controller context structure
@@ -918,6 +985,8 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc->maximum_speed = USB_SPEED_HIGH;
}
+ dwc3_set_power_down_clk_scale(dwc);
+
ret = dwc3_phy_setup(dwc);
if (ret)
goto err0;
@@ -952,6 +1021,10 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_set_incr_burst_type(dwc);
+#ifdef CONFIG_OF
+ dwc3_set_cache_type(dwc);
+#endif
+
usb_phy_set_suspend(dwc->usb2_phy, 0);
usb_phy_set_suspend(dwc->usb3_phy, 0);
ret = phy_power_on(dwc->usb2_generic_phy);
@@ -1009,6 +1082,21 @@ static int dwc3_core_init(struct dwc3 *dwc)
reg |= DWC3_GUCTL_HSTINAUTORETRY;
dwc3_writel(dwc->regs, DWC3_GUCTL, reg);
+
+ /*
+ * Disable Park Mode for super speed:
+ * Park mode is used in host mode when only a single async
+ * endpoint is active, but which has a known issue cause
+ * USB3.0 HC may die when read and write at the same time,
+ * considering the advantages of this mode are minimal,
+ * this issue only impacts super speed and exist on all IP
+ * versions, disable it for SS, Synopsys will release a formal
+ * STAR 9001415732, and disable it by default in next IP
+ * release.
+ */
+ reg = dwc3_readl(dwc->regs, DWC3_GUCTL1);
+ reg |= DWC3_GUCTL1_PARKMODE_DISABLE_SS;
+ dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
}
/*
@@ -1236,6 +1324,17 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->maximum_speed = usb_get_maximum_speed(dev);
dwc->dr_mode = usb_get_dr_mode(dev);
+ if (dwc->dr_mode == USB_DR_MODE_OTG) {
+ dwc->otg_caps.otg_rev = 0x0300;
+ dwc->otg_caps.hnp_support = true;
+ dwc->otg_caps.srp_support = true;
+ dwc->otg_caps.adp_support = true;
+
+ /* Update otg capabilities by DT properties */
+ of_usb_update_otg_caps(dev->of_node,
+ &dwc->otg_caps);
+ }
+
dwc->hsphy_mode = of_usb_get_phy_mode(dev->of_node);
dwc->sysdev_is_parent = device_property_read_bool(dev,
@@ -1315,6 +1414,9 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->dis_metastability_quirk = device_property_read_bool(dev,
"snps,dis_metastability_quirk");
+ dwc->host_vbus_glitches = device_property_read_bool(dev,
+ "snps,host-vbus-glitches");
+
dwc->lpm_nyet_threshold = lpm_nyet_threshold;
dwc->tx_de_emphasis = tx_de_emphasis;
@@ -1837,12 +1939,9 @@ static const struct dev_pm_ops dwc3_dev_pm_ops = {
#ifdef CONFIG_OF
static const struct of_device_id of_dwc3_match[] = {
- {
- .compatible = "snps,dwc3"
- },
- {
- .compatible = "synopsys,dwc3"
- },
+ { .compatible = "fsl,layerscape-dwc3", .data = &layerscape_dwc3_cache_type, },
+ { .compatible = "snps,dwc3" },
+ { .compatible = "synopsys,dwc3" },
{ },
};
MODULE_DEVICE_TABLE(of, of_dwc3_match);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 1c8b349379af..1ea3c50d2586 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -25,6 +25,7 @@
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
+#include <linux/usb/role.h>
#include <linux/ulpi/interface.h>
#include <linux/phy/phy.h>
@@ -165,6 +166,21 @@
/* Bit fields */
/* Global SoC Bus Configuration INCRx Register 0 */
+#ifdef CONFIG_OF
+#define DWC3_GSBUSCFG0_DATARD_SHIFT 28
+#define DWC3_GSBUSCFG0_DATARD(n) (((n) & 0xf) \
+ << DWC3_GSBUSCFG0_DATARD_SHIFT)
+#define DWC3_GSBUSCFG0_DESCRD_SHIFT 24
+#define DWC3_GSBUSCFG0_DESCRD(n) (((n) & 0xf) \
+ << DWC3_GSBUSCFG0_DESCRD_SHIFT)
+#define DWC3_GSBUSCFG0_DATAWR_SHIFT 20
+#define DWC3_GSBUSCFG0_DATAWR(n) (((n) & 0xf) \
+ << DWC3_GSBUSCFG0_DATAWR_SHIFT)
+#define DWC3_GSBUSCFG0_DESCWR_SHIFT 16
+#define DWC3_GSBUSCFG0_DESCWR(n) (((n) & 0xf) \
+ << DWC3_GSBUSCFG0_DESCWR_SHIFT)
+#endif
+
#define DWC3_GSBUSCFG0_INCR256BRSTENA (1 << 7) /* INCR256 burst */
#define DWC3_GSBUSCFG0_INCR128BRSTENA (1 << 6) /* INCR128 burst */
#define DWC3_GSBUSCFG0_INCR64BRSTENA (1 << 5) /* INCR64 burst */
@@ -223,6 +239,7 @@
/* Global Configuration Register */
#define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19)
+#define DWC3_GCTL_PWRDNSCALE_MASK DWC3_GCTL_PWRDNSCALE(0x1fff)
#define DWC3_GCTL_U2RSTECN BIT(16)
#define DWC3_GCTL_RAMCLKSEL(x) (((x) & DWC3_GCTL_CLK_MASK) << 6)
#define DWC3_GCTL_CLK_BUS (0)
@@ -251,6 +268,7 @@
/* Global User Control 1 Register */
#define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS BIT(28)
#define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW BIT(24)
+#define DWC3_GUCTL1_PARKMODE_DISABLE_SS BIT(17)
/* Global Status Register */
#define DWC3_GSTS_OTG_IP BIT(10)
@@ -941,6 +959,7 @@ struct dwc3_scratchpad_array {
* @nr_scratch: number of scratch buffers
* @u1u2: only used on revisions <1.83a for workaround
* @maximum_speed: maximum speed requested (mainly for testing purposes)
+ * @otg_caps: the OTG capabilities from hardware point
* @revision: revision register contents
* @version_type: VERSIONTYPE register contents, a sub release of a revision
* @dr_mode: requested mode of operation
@@ -1029,6 +1048,8 @@ struct dwc3_scratchpad_array {
* 2 - No de-emphasis
* 3 - Reserved
* @dis_metastability_quirk: set to disable metastability quirk.
+ * @host_vbus_glitches: set to avoid vbus glitch during
+ * xhci reset.
* @imod_interval: set the interrupt moderation interval in 250ns
* increments or 0 to disable.
*/
@@ -1078,6 +1099,7 @@ struct dwc3 {
void __iomem *regs;
size_t regs_size;
+ struct usb_role_switch *role_switch;
enum usb_dr_mode dr_mode;
u32 current_dr_role;
u32 desired_dr_role;
@@ -1094,6 +1116,7 @@ struct dwc3 {
u32 nr_scratch;
u32 u1u2;
u32 maximum_speed;
+ struct usb_otg_caps otg_caps;
/*
* All 3.1 IP version constants are greater than the 3.0 IP
@@ -1218,6 +1241,7 @@ struct dwc3 {
unsigned tx_de_emphasis:2;
unsigned dis_metastability_quirk:1;
+ unsigned host_vbus_glitches:1;
u16 imod_interval;
};
diff --git a/drivers/usb/dwc3/drd.c b/drivers/usb/dwc3/drd.c
index c946d64142ad..72cbbc2c1bc1 100644
--- a/drivers/usb/dwc3/drd.c
+++ b/drivers/usb/dwc3/drd.c
@@ -476,6 +476,43 @@ static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc)
return edev;
}
+static int dwc3_usb_role_switch_set(struct device *dev, enum usb_role role)
+{
+ u32 mode;
+
+ switch (role) {
+ case USB_ROLE_HOST:
+ mode = DWC3_GCTL_PRTCAP_HOST;
+ break;
+ case USB_ROLE_DEVICE:
+ mode = DWC3_GCTL_PRTCAP_DEVICE;
+ break;
+ default:
+ return 0;
+ };
+
+ dwc3_set_mode(dev_get_drvdata(dev), mode);
+ return 0;
+}
+
+static enum usb_role dwc3_usb_role_switch_get(struct device *dev)
+{
+ struct dwc3 *dwc = dev_get_drvdata(dev);
+ unsigned long flags;
+ enum usb_role role;
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ role = dwc->current_dr_role;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ return role;
+}
+
+static struct usb_role_switch_desc dwc3_role_switch = {
+ .set = dwc3_usb_role_switch_set,
+ .get = dwc3_usb_role_switch_get,
+};
+
int dwc3_drd_init(struct dwc3 *dwc)
{
int ret, irq;
@@ -484,7 +521,13 @@ int dwc3_drd_init(struct dwc3 *dwc)
if (IS_ERR(dwc->edev))
return PTR_ERR(dwc->edev);
- if (dwc->edev) {
+ if (device_property_read_bool(dwc->dev, "usb-role-switch")) {
+ dwc3_role_switch.fwnode = dev_fwnode(dwc->dev);
+ dwc->role_switch = usb_role_switch_register(dwc->dev,
+ &dwc3_role_switch);
+ if (IS_ERR(dwc->role_switch))
+ return PTR_ERR(dwc->role_switch);
+ } else if (dwc->edev) {
dwc->edev_nb.notifier_call = dwc3_drd_notifier;
ret = extcon_register_notifier(dwc->edev, EXTCON_USB_HOST,
&dwc->edev_nb);
@@ -531,6 +574,9 @@ void dwc3_drd_exit(struct dwc3 *dwc)
{
unsigned long flags;
+ if (dwc->role_switch)
+ usb_role_switch_unregister(dwc->role_switch);
+
if (dwc->edev)
extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST,
&dwc->edev_nb);
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index a9aba716bf80..8e2bdcf6ed81 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -270,7 +270,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
{
const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
struct dwc3 *dwc = dep->dwc;
- u32 timeout = 1000;
+ u32 timeout = 2000;
u32 saved_config = 0;
u32 reg;
@@ -3343,6 +3343,10 @@ int dwc3_gadget_init(struct dwc3 *dwc)
dwc->gadget.sg_supported = true;
dwc->gadget.name = "dwc3-gadget";
dwc->gadget.lpm_capable = true;
+ dwc->gadget.is_otg = (dwc->dr_mode == USB_DR_MODE_OTG) &&
+ (dwc->otg_caps.hnp_support ||
+ dwc->otg_caps.srp_support ||
+ dwc->otg_caps.adp_support);
/*
* FIXME We might be setting max_speed to <SUPER, however versions
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c
index 5567ed2cddbe..cbadceae92e7 100644
--- a/drivers/usb/dwc3/host.c
+++ b/drivers/usb/dwc3/host.c
@@ -9,8 +9,49 @@
#include <linux/platform_device.h>
+#include "../host/xhci.h"
+
#include "core.h"
+
+#define XHCI_HCSPARAMS1 0x4
+#define XHCI_PORTSC_BASE 0x400
+
+/*
+ * dwc3_power_off_all_roothub_ports - Power off all Root hub ports
+ * @dwc3: Pointer to our controller context structure
+ */
+static void dwc3_power_off_all_roothub_ports(struct dwc3 *dwc)
+{
+ int i, port_num;
+ u32 reg, op_regs_base, offset;
+ void __iomem *xhci_regs;
+
+ /* xhci regs is not mapped yet, do it temperary here */
+ if (dwc->xhci_resources[0].start) {
+ xhci_regs = ioremap(dwc->xhci_resources[0].start,
+ DWC3_XHCI_REGS_END);
+ if (IS_ERR(xhci_regs)) {
+ dev_err(dwc->dev, "Failed to ioremap xhci_regs\n");
+ return;
+ }
+
+ op_regs_base = HC_LENGTH(readl(xhci_regs));
+ reg = readl(xhci_regs + XHCI_HCSPARAMS1);
+ port_num = HCS_MAX_PORTS(reg);
+
+ for (i = 1; i <= port_num; i++) {
+ offset = op_regs_base + XHCI_PORTSC_BASE + 0x10*(i-1);
+ reg = readl(xhci_regs + offset);
+ reg &= ~PORT_POWER;
+ writel(reg, xhci_regs + offset);
+ }
+
+ iounmap(xhci_regs);
+ } else
+ dev_err(dwc->dev, "xhci base reg invalid\n");
+}
+
static int dwc3_host_get_irq(struct dwc3 *dwc)
{
struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
@@ -50,6 +91,13 @@ int dwc3_host_init(struct dwc3 *dwc)
struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
int prop_idx = 0;
+ /*
+ * We have to power off all Root hub ports immediately after DWC3 set
+ * to host mode to avoid VBUS glitch happen when xhci get reset later.
+ */
+ if (dwc->host_vbus_glitches)
+ dwc3_power_off_all_roothub_ports(dwc);
+
irq = dwc3_host_get_irq(dwc);
if (irq < 0)
return irq;
diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c
index 9a05863b2876..980cb1382851 100644
--- a/drivers/usb/gadget/udc/fsl_udc_core.c
+++ b/drivers/usb/gadget/udc/fsl_udc_core.c
@@ -1052,10 +1052,11 @@ static int fsl_ep_fifo_status(struct usb_ep *_ep)
u32 bitmask;
struct ep_queue_head *qh;
- ep = container_of(_ep, struct fsl_ep, ep);
- if (!_ep || (!ep->ep.desc && ep_index(ep) != 0))
+ if (!_ep || _ep->desc || !(_ep->desc->bEndpointAddress&0xF))
return -ENODEV;
+ ep = container_of(_ep, struct fsl_ep, ep);
+
udc = (struct fsl_udc *)ep->udc;
if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
@@ -1595,14 +1596,13 @@ static int process_ep_req(struct fsl_udc *udc, int pipe,
struct fsl_req *curr_req)
{
struct ep_td_struct *curr_td;
- int td_complete, actual, remaining_length, j, tmp;
+ int actual, remaining_length, j, tmp;
int status = 0;
int errors = 0;
struct ep_queue_head *curr_qh = &udc->ep_qh[pipe];
int direction = pipe % 2;
curr_td = curr_req->head;
- td_complete = 0;
actual = curr_req->req.length;
for (j = 0; j < curr_req->dtd_count; j++) {
@@ -1647,11 +1647,9 @@ static int process_ep_req(struct fsl_udc *udc, int pipe,
status = -EPROTO;
break;
} else {
- td_complete++;
break;
}
} else {
- td_complete++;
VDBG("dTD transmitted successful");
}
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index cf2b7ae93b7e..ba8c799b5521 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -80,7 +80,7 @@ module_param (log2_irq_thresh, int, S_IRUGO);
MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes");
/* initial park setting: slower than hw default */
-static unsigned park = 0;
+static unsigned park = 3;
module_param (park, uint, S_IRUGO);
MODULE_PARM_DESC (park, "park setting; 1-3 back-to-back async packets");
@@ -1232,6 +1232,10 @@ static const struct hc_driver ehci_hc_driver = {
* device support
*/
.free_dev = ehci_remove_device,
+#ifdef CONFIG_USB_HCD_TEST_MODE
+ /* EH SINGLE_STEP_SET_FEATURE test support */
+ .submit_single_step_set_feature = ehci_submit_single_step_set_feature,
+#endif
};
void ehci_init_driver(struct hc_driver *drv,
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index ce0eaf7d7c12..fb4463f03b45 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -725,145 +725,6 @@ ehci_hub_descriptor (
}
/*-------------------------------------------------------------------------*/
-#ifdef CONFIG_USB_HCD_TEST_MODE
-
-#define EHSET_TEST_SINGLE_STEP_SET_FEATURE 0x06
-
-static void usb_ehset_completion(struct urb *urb)
-{
- struct completion *done = urb->context;
-
- complete(done);
-}
-static int submit_single_step_set_feature(
- struct usb_hcd *hcd,
- struct urb *urb,
- int is_setup
-);
-
-/*
- * Allocate and initialize a control URB. This request will be used by the
- * EHSET SINGLE_STEP_SET_FEATURE test in which the DATA and STATUS stages
- * of the GetDescriptor request are sent 15 seconds after the SETUP stage.
- * Return NULL if failed.
- */
-static struct urb *request_single_step_set_feature_urb(
- struct usb_device *udev,
- void *dr,
- void *buf,
- struct completion *done
-) {
- struct urb *urb;
- struct usb_hcd *hcd = bus_to_hcd(udev->bus);
- struct usb_host_endpoint *ep;
-
- urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!urb)
- return NULL;
-
- urb->pipe = usb_rcvctrlpipe(udev, 0);
- ep = (usb_pipein(urb->pipe) ? udev->ep_in : udev->ep_out)
- [usb_pipeendpoint(urb->pipe)];
- if (!ep) {
- usb_free_urb(urb);
- return NULL;
- }
-
- urb->ep = ep;
- urb->dev = udev;
- urb->setup_packet = (void *)dr;
- urb->transfer_buffer = buf;
- urb->transfer_buffer_length = USB_DT_DEVICE_SIZE;
- urb->complete = usb_ehset_completion;
- urb->status = -EINPROGRESS;
- urb->actual_length = 0;
- urb->transfer_flags = URB_DIR_IN;
- usb_get_urb(urb);
- atomic_inc(&urb->use_count);
- atomic_inc(&urb->dev->urbnum);
- urb->setup_dma = dma_map_single(
- hcd->self.sysdev,
- urb->setup_packet,
- sizeof(struct usb_ctrlrequest),
- DMA_TO_DEVICE);
- urb->transfer_dma = dma_map_single(
- hcd->self.sysdev,
- urb->transfer_buffer,
- urb->transfer_buffer_length,
- DMA_FROM_DEVICE);
- urb->context = done;
- return urb;
-}
-
-static int ehset_single_step_set_feature(struct usb_hcd *hcd, int port)
-{
- int retval = -ENOMEM;
- struct usb_ctrlrequest *dr;
- struct urb *urb;
- struct usb_device *udev;
- struct ehci_hcd *ehci = hcd_to_ehci(hcd);
- struct usb_device_descriptor *buf;
- DECLARE_COMPLETION_ONSTACK(done);
-
- /* Obtain udev of the rhub's child port */
- udev = usb_hub_find_child(hcd->self.root_hub, port);
- if (!udev) {
- ehci_err(ehci, "No device attached to the RootHub\n");
- return -ENODEV;
- }
- buf = kmalloc(USB_DT_DEVICE_SIZE, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
-
- dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL);
- if (!dr) {
- kfree(buf);
- return -ENOMEM;
- }
-
- /* Fill Setup packet for GetDescriptor */
- dr->bRequestType = USB_DIR_IN;
- dr->bRequest = USB_REQ_GET_DESCRIPTOR;
- dr->wValue = cpu_to_le16(USB_DT_DEVICE << 8);
- dr->wIndex = 0;
- dr->wLength = cpu_to_le16(USB_DT_DEVICE_SIZE);
- urb = request_single_step_set_feature_urb(udev, dr, buf, &done);
- if (!urb)
- goto cleanup;
-
- /* Submit just the SETUP stage */
- retval = submit_single_step_set_feature(hcd, urb, 1);
- if (retval)
- goto out1;
- if (!wait_for_completion_timeout(&done, msecs_to_jiffies(2000))) {
- usb_kill_urb(urb);
- retval = -ETIMEDOUT;
- ehci_err(ehci, "%s SETUP stage timed out on ep0\n", __func__);
- goto out1;
- }
- msleep(15 * 1000);
-
- /* Complete remaining DATA and STATUS stages using the same URB */
- urb->status = -EINPROGRESS;
- usb_get_urb(urb);
- atomic_inc(&urb->use_count);
- atomic_inc(&urb->dev->urbnum);
- retval = submit_single_step_set_feature(hcd, urb, 0);
- if (!retval && !wait_for_completion_timeout(&done,
- msecs_to_jiffies(2000))) {
- usb_kill_urb(urb);
- retval = -ETIMEDOUT;
- ehci_err(ehci, "%s IN stage timed out on ep0\n", __func__);
- }
-out1:
- usb_free_urb(urb);
-cleanup:
- kfree(dr);
- kfree(buf);
- return retval;
-}
-#endif /* CONFIG_USB_HCD_TEST_MODE */
-/*-------------------------------------------------------------------------*/
int ehci_hub_control(
struct usb_hcd *hcd,
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index aa2f77f1506d..6709e5708453 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -1154,7 +1154,7 @@ submit_async (
* performed; TRUE - SETUP and FALSE - IN+STATUS
* Returns 0 if success
*/
-static int submit_single_step_set_feature(
+static int ehci_submit_single_step_set_feature(
struct usb_hcd *hcd,
struct urb *urb,
int is_setup
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index b7d23c438756..e8db6429a4e3 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -1359,6 +1359,15 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
/* 4.19.6 Port Test Modes (USB2 Test Mode) */
if (hcd->speed != HCD_USB2)
goto error;
+#ifdef CONFIG_USB_HCD_TEST_MODE
+ if (test_mode == EHSET_TEST_SINGLE_STEP_SET_FEATURE) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ retval = ehset_single_step_set_feature(hcd,
+ wIndex + 1);
+ spin_lock_irqsave(&xhci->lock, flags);
+ break;
+ }
+#endif
if (test_mode > TEST_FORCE_EN || test_mode < TEST_J)
goto error;
retval = xhci_enter_test_mode(xhci, test_mode, wIndex,
@@ -1637,6 +1646,7 @@ retry:
spin_unlock_irqrestore(&xhci->lock, flags);
return 0;
}
+EXPORT_SYMBOL(xhci_bus_suspend);
/*
* Workaround for missing Cold Attach Status (CAS) if device re-plugged in S3.
@@ -1654,7 +1664,8 @@ static bool xhci_port_missing_cas_quirk(struct xhci_port *port)
return false;
if (((portsc & PORT_PLS_MASK) != XDEV_POLLING) &&
- ((portsc & PORT_PLS_MASK) != XDEV_COMP_MODE))
+ ((portsc & PORT_PLS_MASK) != XDEV_COMP_MODE) &&
+ ((portsc & PORT_PLS_MASK) != XDEV_RXDETECT))
return false;
/* clear wakeup/change bits, and do a warm port reset */
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index d90cd5ec09cf..4655016eaf45 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -291,6 +291,10 @@ static int xhci_plat_probe(struct platform_device *pdev)
device_property_read_u32(tmpdev, "imod-interval-ns",
&xhci->imod_interval);
+
+ if (device_property_read_bool(tmpdev,
+ "usb3-resume-missing-cas"))
+ xhci->quirks |= XHCI_MISSING_CAS;
}
hcd->usb_phy = devm_usb_get_phy_by_phandle(sysdev, "usb-phy", 0);
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index e7aab31fd9a5..8000ad9db792 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -2028,12 +2028,9 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
switch (trb_comp_code) {
case COMP_SUCCESS:
- if (trb_type != TRB_STATUS) {
- xhci_warn(xhci, "WARN: Success on ctrl %s TRB without IOC set?\n",
+ if (trb_type != TRB_STATUS)
+ xhci_dbg(xhci, "Success on ctrl %s TRB without IOC set?\n",
(trb_type == TRB_DATA) ? "data" : "setup");
- *status = -ESHUTDOWN;
- break;
- }
*status = 0;
break;
case COMP_SHORT_PACKET:
@@ -3519,6 +3516,129 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
return 0;
}
+#ifdef CONFIG_USB_HCD_TEST_MODE
+/*
+ * This function prepare TRBs and submits them for the
+ * SINGLE_STEP_SET_FEATURE Test.
+ * This is done in two parts: first SETUP req for GetDesc is sent then
+ * 15 seconds later, the IN stage for GetDesc starts to req data from dev
+ *
+ * is_setup : argument decides which of the two stage needs to be
+ * performed; TRUE - SETUP and FALSE - IN+STATUS
+ * Returns 0 if success
+ */
+int xhci_submit_single_step_set_feature(struct usb_hcd *hcd,
+ struct urb *urb, int is_setup)
+{
+ int slot_id;
+ unsigned int ep_index;
+ struct xhci_ring *ep_ring;
+ int ret;
+ struct usb_ctrlrequest *setup;
+ struct xhci_generic_trb *start_trb;
+ int start_cycle;
+ u32 field, length_field, remainder;
+ struct urb_priv *urb_priv;
+ struct xhci_td *td;
+ struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+
+ /* urb_priv will be free after transcation has completed */
+ urb_priv = kzalloc(sizeof(struct urb_priv) +
+ sizeof(struct xhci_td), GFP_KERNEL);
+ if (!urb_priv)
+ return -ENOMEM;
+
+ td = &urb_priv->td[0];
+ urb_priv->num_tds = 1;
+ urb_priv->num_tds_done = 0;
+ urb->hcpriv = urb_priv;
+
+ ep_ring = xhci_urb_to_transfer_ring(xhci, urb);
+ if (!ep_ring) {
+ ret = -EINVAL;
+ goto free_priv;
+ }
+
+ slot_id = urb->dev->slot_id;
+ ep_index = xhci_get_endpoint_index(&urb->ep->desc);
+
+ setup = (struct usb_ctrlrequest *) urb->setup_packet;
+ if (is_setup) {
+ ret = prepare_transfer(xhci, xhci->devs[slot_id],
+ ep_index, urb->stream_id,
+ 1, urb, 0, GFP_KERNEL);
+ if (ret < 0)
+ goto free_priv;
+
+ start_trb = &ep_ring->enqueue->generic;
+ start_cycle = ep_ring->cycle_state;
+ /* Save the DMA address of the last TRB in the TD */
+ td->last_trb = ep_ring->enqueue;
+ field = TRB_IOC | TRB_IDT | TRB_TYPE(TRB_SETUP) | start_cycle;
+ /* xHCI 1.0/1.1 6.4.1.2.1: Transfer Type field */
+ if ((xhci->hci_version >= 0x100) ||
+ (xhci->quirks & XHCI_MTK_HOST))
+ field |= TRB_TX_TYPE(TRB_DATA_IN);
+
+ queue_trb(xhci, ep_ring, false,
+ setup->bRequestType | setup->bRequest << 8 |
+ le16_to_cpu(setup->wValue) << 16,
+ le16_to_cpu(setup->wIndex) |
+ le16_to_cpu(setup->wLength) << 16,
+ TRB_LEN(8) | TRB_INTR_TARGET(0),
+ /* Immediate data in pointer */
+ field);
+ giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id,
+ start_cycle, start_trb);
+ return 0;
+ }
+
+ ret = prepare_transfer(xhci, xhci->devs[slot_id],
+ ep_index, urb->stream_id,
+ 2, urb, 0, GFP_KERNEL);
+ if (ret < 0)
+ goto free_priv;
+
+ start_trb = &ep_ring->enqueue->generic;
+ start_cycle = ep_ring->cycle_state;
+ field = TRB_ISP | TRB_TYPE(TRB_DATA);
+
+ remainder = xhci_td_remainder(xhci, 0,
+ urb->transfer_buffer_length,
+ urb->transfer_buffer_length,
+ urb, 1);
+
+ length_field = TRB_LEN(urb->transfer_buffer_length) |
+ TRB_TD_SIZE(remainder) |
+ TRB_INTR_TARGET(0);
+
+ if (urb->transfer_buffer_length > 0) {
+ field |= TRB_DIR_IN;
+ queue_trb(xhci, ep_ring, true,
+ lower_32_bits(urb->transfer_dma),
+ upper_32_bits(urb->transfer_dma),
+ length_field,
+ field | ep_ring->cycle_state);
+ }
+
+ td->last_trb = ep_ring->enqueue;
+ field = TRB_IOC | TRB_TYPE(TRB_STATUS) | ep_ring->cycle_state;
+ queue_trb(xhci, ep_ring, false,
+ 0,
+ 0,
+ TRB_INTR_TARGET(0),
+ field);
+
+ giveback_first_trb(xhci, slot_id, ep_index, 0,
+ start_cycle, start_trb);
+
+ return 0;
+free_priv:
+ xhci_urb_free_priv(urb_priv);
+ return ret;
+}
+#endif /* CONFIG_USB_HCD_TEST_MODE */
+
/*
* The transfer burst count field of the isochronous TRB defines the number of
* bursts that are required to move all packets in this TD. Only SuperSpeed
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 6c17e3fe181a..2a5d71374855 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -193,7 +193,7 @@ int xhci_reset(struct xhci_hcd *xhci)
* Without this delay, the subsequent HC register access,
* may result in a system hang very rarely.
*/
- if (xhci->quirks & XHCI_INTEL_HOST)
+ if (xhci->quirks & (XHCI_INTEL_HOST | XHCI_CDNS_HOST))
udelay(1000);
ret = xhci_handshake(&xhci->op_regs->command,
@@ -5355,6 +5355,7 @@ static const struct hc_driver xhci_hc_driver = {
.disable_usb3_lpm_timeout = xhci_disable_usb3_lpm_timeout,
.find_raw_port_number = xhci_find_raw_port_number,
.clear_tt_buffer_complete = xhci_clear_tt_buffer_complete,
+ .submit_single_step_set_feature = xhci_submit_single_step_set_feature,
};
void xhci_init_driver(struct hc_driver *drv,
@@ -5371,6 +5372,8 @@ void xhci_init_driver(struct hc_driver *drv,
drv->reset = over->reset;
if (over->start)
drv->start = over->start;
+ if (over->bus_suspend)
+ drv->bus_suspend = over->bus_suspend;
}
}
EXPORT_SYMBOL_GPL(xhci_init_driver);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index f9f88626a57a..321cf42f50d9 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1867,6 +1867,7 @@ struct xhci_hcd {
#define XHCI_DEFAULT_PM_RUNTIME_ALLOW BIT_ULL(33)
#define XHCI_RESET_PLL_ON_DISCONNECT BIT_ULL(34)
#define XHCI_SNPS_BROKEN_SUSPEND BIT_ULL(35)
+#define XHCI_CDNS_HOST BIT_ULL(36)
unsigned int num_active_eps;
unsigned int limit_active_eps;
@@ -1901,6 +1902,7 @@ struct xhci_driver_overrides {
size_t extra_priv_size;
int (*reset)(struct usb_hcd *hcd);
int (*start)(struct usb_hcd *hcd);
+ int (*bus_suspend)(struct usb_hcd *hcd);
};
#define XHCI_CFC_DELAY 10
@@ -2132,6 +2134,16 @@ int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1);
struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd);
void xhci_hc_died(struct xhci_hcd *xhci);
+#ifdef CONFIG_USB_HCD_TEST_MODE
+int xhci_submit_single_step_set_feature(struct usb_hcd *hcd,
+ struct urb *urb, int is_setup);
+#else
+static inline int xhci_submit_single_step_set_feature(struct usb_hcd *hcd,
+ struct urb *urb, int is_setup)
+{
+ return 0;
+}
+#endif
#ifdef CONFIG_PM
int xhci_bus_suspend(struct usb_hcd *hcd);
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index 24b4f091acb8..4b2002dabcdf 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -152,7 +152,7 @@ config USB_MV_OTG
config USB_MXS_PHY
tristate "Freescale MXS USB PHY support"
- depends on ARCH_MXC || ARCH_MXS
+ depends on ARCH_MXC || ARCH_MXS || ARCH_MXC_ARM64
select STMP_DEVICE
select USB_PHY
help
diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c
index 70b8c8248caf..299eb6be79b8 100644
--- a/drivers/usb/phy/phy-mxs-usb.c
+++ b/drivers/usb/phy/phy-mxs-usb.c
@@ -1,6 +1,7 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * Copyright 2012-2014 Freescale Semiconductor, Inc.
+ * Copyright 2012-2016 Freescale Semiconductor, Inc.
+ * Copyright 2017 NXP
* Copyright (C) 2012 Marek Vasut <marex@denx.de>
* on behalf of DENX Software Engineering GmbH
*/
@@ -18,6 +19,7 @@
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
#include <linux/iopoll.h>
+#include <linux/regulator/consumer.h>
#define DRIVER_NAME "mxs_phy"
@@ -70,6 +72,12 @@
#define BM_USBPHY_PLL_EN_USB_CLKS BIT(6)
/* Anatop Registers */
+#define ANADIG_PLL_USB2 0x20
+#define ANADIG_PLL_USB2_SET 0x24
+#define ANADIG_PLL_USB2_CLR 0x28
+#define ANADIG_REG_1P1_SET 0x114
+#define ANADIG_REG_1P1_CLR 0x118
+
#define ANADIG_ANA_MISC0 0x150
#define ANADIG_ANA_MISC0_SET 0x154
#define ANADIG_ANA_MISC0_CLR 0x158
@@ -117,6 +125,42 @@
#define BM_ANADIG_USB2_MISC_RX_VPIN_FS BIT(29)
#define BM_ANADIG_USB2_MISC_RX_VMIN_FS BIT(28)
+/* System Integration Module (SIM) Registers */
+#define SIM_GPR1 0x30
+
+#define USB_PHY_VLLS_WAKEUP_EN BIT(0)
+
+#define BM_ANADIG_REG_1P1_ENABLE_WEAK_LINREG BIT(18)
+#define BM_ANADIG_REG_1P1_TRACK_VDD_SOC_CAP BIT(19)
+
+#define BM_ANADIG_PLL_USB2_HOLD_RING_OFF BIT(11)
+
+/* DCD module, the offset is 0x800 */
+#define DCD_CONTROL 0x800
+#define DCD_CLOCK (DCD_CONTROL + 0x4)
+#define DCD_STATUS (DCD_CONTROL + 0x8)
+
+#define DCD_CONTROL_SR BIT(25)
+#define DCD_CONTROL_START BIT(24)
+#define DCD_CONTROL_BC12 BIT(17)
+#define DCD_CONTROL_IE BIT(16)
+#define DCD_CONTROL_IF BIT(8)
+#define DCD_CONTROL_IACK BIT(0)
+
+#define DCD_CLOCK_MHZ BIT(0)
+
+#define DCD_STATUS_ACTIVE BIT(22)
+#define DCD_STATUS_TO BIT(21)
+#define DCD_STATUS_ERR BIT(20)
+#define DCD_STATUS_SEQ_STAT (BIT(18) | BIT(19))
+#define DCD_CHG_PORT BIT(19)
+#define DCD_CHG_DET (BIT(18) | BIT(19))
+#define DCD_CHG_DPIN BIT(18)
+#define DCD_STATUS_SEQ_RES (BIT(16) | BIT(17))
+#define DCD_SDP_PORT BIT(16)
+#define DCD_CDP_PORT BIT(17)
+#define DCD_DCP_PORT (BIT(16) | BIT(17))
+
#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy)
/* Do disconnection between PHY and controller without vbus */
@@ -149,6 +193,19 @@
#define MXS_PHY_TX_D_CAL_MIN 79
#define MXS_PHY_TX_D_CAL_MAX 119
+/*
+ * At some versions, the PHY2's clock is controlled by hardware directly,
+ * eg, according to PHY's suspend status. In these PHYs, we only need to
+ * open the clock at the initialization and close it at its shutdown routine.
+ * It will be benefit for remote wakeup case which needs to send resume
+ * signal as soon as possible, and in this case, the resume signal can be sent
+ * out without software interfere.
+ */
+#define MXS_PHY_HARDWARE_CONTROL_PHY2_CLK BIT(4)
+
+/* The MXS PHYs which have DCD module for charger detection */
+#define MXS_PHY_HAS_DCD BIT(5)
+
struct mxs_phy_data {
unsigned int flags;
};
@@ -160,12 +217,14 @@ static const struct mxs_phy_data imx23_phy_data = {
static const struct mxs_phy_data imx6q_phy_data = {
.flags = MXS_PHY_SENDING_SOF_TOO_FAST |
MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
- MXS_PHY_NEED_IP_FIX,
+ MXS_PHY_NEED_IP_FIX |
+ MXS_PHY_HARDWARE_CONTROL_PHY2_CLK,
};
static const struct mxs_phy_data imx6sl_phy_data = {
.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
- MXS_PHY_NEED_IP_FIX,
+ MXS_PHY_NEED_IP_FIX |
+ MXS_PHY_HARDWARE_CONTROL_PHY2_CLK,
};
static const struct mxs_phy_data vf610_phy_data = {
@@ -174,14 +233,17 @@ static const struct mxs_phy_data vf610_phy_data = {
};
static const struct mxs_phy_data imx6sx_phy_data = {
- .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS,
+ .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
+ MXS_PHY_HARDWARE_CONTROL_PHY2_CLK,
};
static const struct mxs_phy_data imx6ul_phy_data = {
- .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS,
+ .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
+ MXS_PHY_HARDWARE_CONTROL_PHY2_CLK,
};
static const struct mxs_phy_data imx7ulp_phy_data = {
+ .flags = MXS_PHY_HAS_DCD,
};
static const struct of_device_id mxs_phy_dt_ids[] = {
@@ -201,9 +263,14 @@ struct mxs_phy {
struct clk *clk;
const struct mxs_phy_data *data;
struct regmap *regmap_anatop;
+ struct regmap *regmap_sim;
int port_id;
u32 tx_reg_set;
u32 tx_reg_mask;
+ struct regulator *phy_3p0;
+ bool hardware_control_phy2_clk;
+ enum usb_current_mode mode;
+ unsigned long clk_rate;
};
static inline bool is_imx6q_phy(struct mxs_phy *mxs_phy)
@@ -221,6 +288,11 @@ static inline bool is_imx7ulp_phy(struct mxs_phy *mxs_phy)
return mxs_phy->data == &imx7ulp_phy_data;
}
+static inline bool is_imx6ul_phy(struct mxs_phy *mxs_phy)
+{
+ return mxs_phy->data == &imx6ul_phy_data;
+}
+
/*
* PHY needs some 32K cycles to switch from 32K clock to
* bus (such as AHB/AXI, etc) clock.
@@ -288,6 +360,16 @@ static int mxs_phy_hw_init(struct mxs_phy *mxs_phy)
if (ret)
goto disable_pll;
+ if (mxs_phy->phy_3p0) {
+ ret = regulator_enable(mxs_phy->phy_3p0);
+ if (ret) {
+ dev_err(mxs_phy->phy.dev,
+ "Failed to enable 3p0 regulator, ret=%d\n",
+ ret);
+ return ret;
+ }
+ }
+
/* Power up the PHY */
writel(0, base + HW_USBPHY_PWD);
@@ -386,21 +468,10 @@ static void __mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool disconnect)
usleep_range(500, 1000);
}
-static bool mxs_phy_is_otg_host(struct mxs_phy *mxs_phy)
-{
- void __iomem *base = mxs_phy->phy.io_priv;
- u32 phyctrl = readl(base + HW_USBPHY_CTRL);
-
- if (IS_ENABLED(CONFIG_USB_OTG) &&
- !(phyctrl & BM_USBPHY_CTRL_OTG_ID_VALUE))
- return true;
-
- return false;
-}
-
static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on)
{
bool vbus_is_on = false;
+ enum usb_phy_events last_event = mxs_phy->phy.last_event;
/* If the SoCs don't need to disconnect line without vbus, quit */
if (!(mxs_phy->data->flags & MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS))
@@ -412,7 +483,8 @@ static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on)
vbus_is_on = mxs_phy_get_vbus_status(mxs_phy);
- if (on && !vbus_is_on && !mxs_phy_is_otg_host(mxs_phy))
+ if (on && ((!vbus_is_on && mxs_phy->mode != CUR_USB_MODE_HOST) ||
+ (last_event == USB_EVENT_VBUS)))
__mxs_phy_disconnect_line(mxs_phy, true);
else
__mxs_phy_disconnect_line(mxs_phy, false);
@@ -453,6 +525,9 @@ static void mxs_phy_shutdown(struct usb_phy *phy)
if (is_imx7ulp_phy(mxs_phy))
mxs_phy_pll_enable(phy->io_priv, false);
+ if (mxs_phy->phy_3p0)
+ regulator_disable(mxs_phy->phy_3p0);
+
clk_disable_unprepare(mxs_phy->clk);
}
@@ -506,14 +581,49 @@ static int mxs_phy_suspend(struct usb_phy *x, int suspend)
} else {
writel(0xffffffff, x->io_priv + HW_USBPHY_PWD);
}
+
+ /*
+ * USB2 PLL use ring VCO, when the PLL power up, the ring
+ * VCO’s supply also ramp up. There is a possibility that
+ * the ring VCO start oscillation at multi nodes in this
+ * phase, especially for VCO which has many stages, then
+ * the multiwave will be kept until PLL power down. the bit
+ * hold_ring_off can force the VCO in one determined state
+ * to avoid the multiwave issue when VCO supply start ramp
+ * up.
+ */
+ if (mxs_phy->port_id == 1 && mxs_phy->regmap_anatop)
+ regmap_write(mxs_phy->regmap_anatop,
+ ANADIG_PLL_USB2_SET,
+ BM_ANADIG_PLL_USB2_HOLD_RING_OFF);
+
writel(BM_USBPHY_CTRL_CLKGATE,
x->io_priv + HW_USBPHY_CTRL_SET);
- clk_disable_unprepare(mxs_phy->clk);
+ if (!(mxs_phy->port_id == 1 &&
+ mxs_phy->hardware_control_phy2_clk))
+ clk_disable_unprepare(mxs_phy->clk);
} else {
mxs_phy_clock_switch_delay();
- ret = clk_prepare_enable(mxs_phy->clk);
- if (ret)
- return ret;
+ if (!(mxs_phy->port_id == 1 &&
+ mxs_phy->hardware_control_phy2_clk)) {
+ ret = clk_prepare_enable(mxs_phy->clk);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * Per IC design's requirement, hold_ring_off bit can be
+ * cleared 25us after PLL power up and 25us before any USB
+ * TX/RX.
+ */
+ if (mxs_phy->port_id == 1 && mxs_phy->regmap_anatop) {
+ udelay(25);
+ regmap_write(mxs_phy->regmap_anatop,
+ ANADIG_PLL_USB2_CLR,
+ BM_ANADIG_PLL_USB2_HOLD_RING_OFF);
+ udelay(25);
+ }
+
writel(BM_USBPHY_CTRL_CLKGATE,
x->io_priv + HW_USBPHY_CTRL_CLR);
writel(0, x->io_priv + HW_USBPHY_PWD);
@@ -708,6 +818,169 @@ static enum usb_charger_type mxs_phy_charger_detect(struct usb_phy *phy)
return chgr_type;
}
+static int mxs_phy_on_suspend(struct usb_phy *phy,
+ enum usb_device_speed speed)
+{
+ struct mxs_phy *mxs_phy = to_mxs_phy(phy);
+
+ dev_dbg(phy->dev, "%s device has suspended\n",
+ (speed == USB_SPEED_HIGH) ? "HS" : "FS/LS");
+
+ /* delay 4ms to wait bus entering idle */
+ usleep_range(4000, 5000);
+
+ if (mxs_phy->data->flags & MXS_PHY_ABNORMAL_IN_SUSPEND) {
+ writel_relaxed(0xffffffff, phy->io_priv + HW_USBPHY_PWD);
+ writel_relaxed(0, phy->io_priv + HW_USBPHY_PWD);
+ }
+
+ if (speed == USB_SPEED_HIGH)
+ writel_relaxed(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
+ phy->io_priv + HW_USBPHY_CTRL_CLR);
+
+ return 0;
+}
+
+/*
+ * The resume signal must be finished here.
+ */
+static int mxs_phy_on_resume(struct usb_phy *phy,
+ enum usb_device_speed speed)
+{
+ dev_dbg(phy->dev, "%s device has resumed\n",
+ (speed == USB_SPEED_HIGH) ? "HS" : "FS/LS");
+
+ if (speed == USB_SPEED_HIGH) {
+ /* Make sure the device has switched to High-Speed mode */
+ udelay(500);
+ writel_relaxed(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
+ phy->io_priv + HW_USBPHY_CTRL_SET);
+ }
+
+ return 0;
+}
+
+/*
+ * Set the usb current role for phy.
+ */
+static int mxs_phy_set_mode(struct usb_phy *phy,
+ enum usb_current_mode mode)
+{
+ struct mxs_phy *mxs_phy = to_mxs_phy(phy);
+
+ mxs_phy->mode = mode;
+
+ return 0;
+}
+
+static int mxs_phy_dcd_start(struct mxs_phy *mxs_phy)
+{
+ void __iomem *base = mxs_phy->phy.io_priv;
+ u32 value;
+
+ value = readl(base + DCD_CONTROL);
+ writel(value | DCD_CONTROL_SR, base + DCD_CONTROL);
+
+ if (!mxs_phy->clk_rate)
+ return -EINVAL;
+
+ value = readl(base + DCD_CONTROL);
+ writel(((mxs_phy->clk_rate / 1000000) << 2) | DCD_CLOCK_MHZ,
+ base + DCD_CLOCK);
+
+ value = readl(base + DCD_CONTROL);
+ value &= ~DCD_CONTROL_IE;
+ writel(value | DCD_CONTROL_BC12, base + DCD_CONTROL);
+
+ value = readl(base + DCD_CONTROL);
+ writel(value | DCD_CONTROL_START, base + DCD_CONTROL);
+
+ return 0;
+}
+
+#define DCD_CHARGING_DURTION 1000 /* One second according to BC 1.2 */
+static enum usb_charger_type mxs_phy_dcd_flow(struct usb_phy *phy)
+{
+ struct mxs_phy *mxs_phy = to_mxs_phy(phy);
+ void __iomem *base = mxs_phy->phy.io_priv;
+ u32 value;
+ int i = 0;
+ enum usb_charger_type chgr_type;
+
+ if (mxs_phy_dcd_start(mxs_phy))
+ return UNKNOWN_TYPE;
+
+ while (i++ <= (DCD_CHARGING_DURTION / 50)) {
+ value = readl(base + DCD_CONTROL);
+ if (value & DCD_CONTROL_IF) {
+ value = readl(base + DCD_STATUS);
+ if (value & DCD_STATUS_ACTIVE) {
+ dev_err(phy->dev, "still detecting\n");
+ chgr_type = UNKNOWN_TYPE;
+ break;
+ }
+
+ if (value & DCD_STATUS_TO) {
+ dev_err(phy->dev, "detect timeout\n");
+ chgr_type = UNKNOWN_TYPE;
+ break;
+ }
+
+ if (value & DCD_STATUS_ERR) {
+ dev_err(phy->dev, "detect error\n");
+ chgr_type = UNKNOWN_TYPE;
+ break;
+ }
+
+ if ((value & DCD_STATUS_SEQ_STAT) <= DCD_CHG_DPIN) {
+ dev_err(phy->dev, "error occurs\n");
+ chgr_type = UNKNOWN_TYPE;
+ break;
+ }
+
+ /* SDP */
+ if (((value & DCD_STATUS_SEQ_STAT) == DCD_CHG_PORT) &&
+ ((value & DCD_STATUS_SEQ_RES)
+ == DCD_SDP_PORT)) {
+ dev_dbg(phy->dev, "SDP\n");
+ chgr_type = SDP_TYPE;
+ break;
+ }
+
+ if ((value & DCD_STATUS_SEQ_STAT) == DCD_CHG_DET) {
+ if ((value & DCD_STATUS_SEQ_RES) ==
+ DCD_CDP_PORT) {
+ dev_dbg(phy->dev, "CDP\n");
+ chgr_type = CDP_TYPE;
+ break;
+ }
+
+ if ((value & DCD_STATUS_SEQ_RES) ==
+ DCD_DCP_PORT) {
+ dev_dbg(phy->dev, "DCP\n");
+ chgr_type = DCP_TYPE;
+ break;
+ }
+ }
+ dev_err(phy->dev, "unknown error occurs\n");
+ chgr_type = UNKNOWN_TYPE;
+ break;
+ }
+ msleep(50);
+ }
+
+ if (i > 20) {
+ dev_err(phy->dev, "charger detecting timeout\n");
+ chgr_type = UNKNOWN_TYPE;
+ }
+
+ /* disable dcd module */
+ readl(base + DCD_STATUS);
+ writel(DCD_CONTROL_IACK, base + DCD_CONTROL);
+ writel(DCD_CONTROL_SR, base + DCD_CONTROL);
+ return chgr_type;
+}
+
static int mxs_phy_probe(struct platform_device *pdev)
{
struct resource *res;
@@ -739,6 +1012,7 @@ static int mxs_phy_probe(struct platform_device *pdev)
if (!mxs_phy)
return -ENOMEM;
+ mxs_phy->clk_rate = clk_get_rate(clk);
/* Some SoCs don't have anatop registers */
if (of_get_property(np, "fsl,anatop", NULL)) {
mxs_phy->regmap_anatop = syscon_regmap_lookup_by_phandle
@@ -750,6 +1024,17 @@ static int mxs_phy_probe(struct platform_device *pdev)
}
}
+ /* Currently, only imx7ulp has SIM module */
+ if (of_get_property(np, "nxp,sim", NULL)) {
+ mxs_phy->regmap_sim = syscon_regmap_lookup_by_phandle
+ (np, "nxp,sim");
+ if (IS_ERR(mxs_phy->regmap_sim)) {
+ dev_dbg(&pdev->dev,
+ "failed to find regmap for sim\n");
+ return PTR_ERR(mxs_phy->regmap_sim);
+ }
+ }
+
/* Precompute which bits of the TX register are to be updated, if any */
if (!of_property_read_u32(np, "fsl,tx-cal-45-dn-ohms", &val) &&
val >= MXS_PHY_TX_CAL45_MIN && val <= MXS_PHY_TX_CAL45_MAX) {
@@ -784,6 +1069,8 @@ static int mxs_phy_probe(struct platform_device *pdev)
ret = of_alias_get_id(np, "usbphy");
if (ret < 0)
dev_dbg(&pdev->dev, "failed to get alias id, errno %d\n", ret);
+ mxs_phy->clk = clk;
+ mxs_phy->data = of_id->data;
mxs_phy->port_id = ret;
mxs_phy->phy.io_priv = base;
@@ -796,10 +1083,33 @@ static int mxs_phy_probe(struct platform_device *pdev)
mxs_phy->phy.notify_disconnect = mxs_phy_on_disconnect;
mxs_phy->phy.type = USB_PHY_TYPE_USB2;
mxs_phy->phy.set_wakeup = mxs_phy_set_wakeup;
- mxs_phy->phy.charger_detect = mxs_phy_charger_detect;
+ if (mxs_phy->data->flags & MXS_PHY_HAS_DCD)
+ mxs_phy->phy.charger_detect = mxs_phy_dcd_flow;
+ else
+ mxs_phy->phy.charger_detect = mxs_phy_charger_detect;
- mxs_phy->clk = clk;
- mxs_phy->data = of_id->data;
+ mxs_phy->phy.set_mode = mxs_phy_set_mode;
+ if (mxs_phy->data->flags & MXS_PHY_SENDING_SOF_TOO_FAST) {
+ mxs_phy->phy.notify_suspend = mxs_phy_on_suspend;
+ mxs_phy->phy.notify_resume = mxs_phy_on_resume;
+ }
+
+ mxs_phy->phy_3p0 = devm_regulator_get(&pdev->dev, "phy-3p0");
+ if (PTR_ERR(mxs_phy->phy_3p0) == -EPROBE_DEFER) {
+ return -EPROBE_DEFER;
+ } else if (PTR_ERR(mxs_phy->phy_3p0) == -ENODEV) {
+ /* not exist */
+ mxs_phy->phy_3p0 = NULL;
+ } else if (IS_ERR(mxs_phy->phy_3p0)) {
+ dev_err(&pdev->dev, "Getting regulator error: %ld\n",
+ PTR_ERR(mxs_phy->phy_3p0));
+ return PTR_ERR(mxs_phy->phy_3p0);
+ }
+ if (mxs_phy->phy_3p0)
+ regulator_set_voltage(mxs_phy->phy_3p0, 3200000, 3200000);
+
+ if (mxs_phy->data->flags & MXS_PHY_HARDWARE_CONTROL_PHY2_CLK)
+ mxs_phy->hardware_control_phy2_clk = true;
platform_set_drvdata(pdev, mxs_phy);
@@ -818,28 +1128,58 @@ static int mxs_phy_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM_SLEEP
+static void mxs_phy_wakeup_enable(struct mxs_phy *mxs_phy, bool on)
+{
+ u32 mask = USB_PHY_VLLS_WAKEUP_EN;
+
+ /* If the SoCs don't have SIM, quit */
+ if (!mxs_phy->regmap_sim)
+ return;
+
+ if (on) {
+ regmap_update_bits(mxs_phy->regmap_sim, SIM_GPR1, mask, mask);
+ udelay(500);
+ } else {
+ regmap_update_bits(mxs_phy->regmap_sim, SIM_GPR1, mask, 0);
+ }
+}
+
static void mxs_phy_enable_ldo_in_suspend(struct mxs_phy *mxs_phy, bool on)
{
- unsigned int reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR;
+ unsigned int reg;
+ u32 value;
/* If the SoCs don't have anatop, quit */
if (!mxs_phy->regmap_anatop)
return;
- if (is_imx6q_phy(mxs_phy))
+ if (is_imx6q_phy(mxs_phy)) {
+ reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR;
regmap_write(mxs_phy->regmap_anatop, reg,
BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG);
- else if (is_imx6sl_phy(mxs_phy))
+ } else if (is_imx6sl_phy(mxs_phy)) {
+ reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR;
regmap_write(mxs_phy->regmap_anatop,
reg, BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL);
+ } else if (is_imx6ul_phy(mxs_phy)) {
+ reg = on ? ANADIG_REG_1P1_SET : ANADIG_REG_1P1_CLR;
+ value = BM_ANADIG_REG_1P1_ENABLE_WEAK_LINREG |
+ BM_ANADIG_REG_1P1_TRACK_VDD_SOC_CAP;
+ if (mxs_phy_get_vbus_status(mxs_phy) && on)
+ regmap_write(mxs_phy->regmap_anatop, reg, value);
+ else if (!on)
+ regmap_write(mxs_phy->regmap_anatop, reg, value);
+ }
}
static int mxs_phy_system_suspend(struct device *dev)
{
struct mxs_phy *mxs_phy = dev_get_drvdata(dev);
- if (device_may_wakeup(dev))
+ if (device_may_wakeup(dev)) {
mxs_phy_enable_ldo_in_suspend(mxs_phy, true);
+ mxs_phy_wakeup_enable(mxs_phy, true);
+ }
return 0;
}
@@ -848,8 +1188,10 @@ static int mxs_phy_system_resume(struct device *dev)
{
struct mxs_phy *mxs_phy = dev_get_drvdata(dev);
- if (device_may_wakeup(dev))
+ if (device_may_wakeup(dev)) {
mxs_phy_enable_ldo_in_suspend(mxs_phy, false);
+ mxs_phy_wakeup_enable(mxs_phy, false);
+ }
return 0;
}
diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c
index 0277f62739a2..ad2554630889 100644
--- a/drivers/usb/phy/phy.c
+++ b/drivers/usb/phy/phy.c
@@ -34,6 +34,14 @@ struct phy_devm {
struct notifier_block *nb;
};
+static const char *const usb_chger_type[] = {
+ [UNKNOWN_TYPE] = "USB_CHARGER_UNKNOWN_TYPE",
+ [SDP_TYPE] = "USB_CHARGER_SDP_TYPE",
+ [CDP_TYPE] = "USB_CHARGER_CDP_TYPE",
+ [DCP_TYPE] = "USB_CHARGER_DCP_TYPE",
+ [ACA_TYPE] = "USB_CHARGER_ACA_TYPE",
+};
+
static struct usb_phy *__usb_find_phy(struct list_head *list,
enum usb_phy_type type)
{
@@ -98,7 +106,8 @@ static void usb_phy_notify_charger_work(struct work_struct *work)
{
struct usb_phy *usb_phy = container_of(work, struct usb_phy, chg_work);
char uchger_state[50] = { 0 };
- char *envp[] = { uchger_state, NULL };
+ char uchger_type[50] = { 0 };
+ char *envp[] = { uchger_state, uchger_type, NULL };
unsigned int min, max;
switch (usb_phy->chg_state) {
@@ -122,6 +131,8 @@ static void usb_phy_notify_charger_work(struct work_struct *work)
return;
}
+ snprintf(uchger_type, ARRAY_SIZE(uchger_type),
+ "USB_CHARGER_TYPE=%s", usb_chger_type[usb_phy->chg_type]);
kobject_uevent_env(&usb_phy->dev->kobj, KOBJ_CHANGE, envp);
}
diff --git a/drivers/usb/typec/mux/Kconfig b/drivers/usb/typec/mux/Kconfig
index 01ed0d5e10e8..bc7d3c78e556 100644
--- a/drivers/usb/typec/mux/Kconfig
+++ b/drivers/usb/typec/mux/Kconfig
@@ -9,4 +9,10 @@ config TYPEC_MUX_PI3USB30532
Say Y or M if your system has a Pericom PI3USB30532 Type-C cross
switch / mux chip found on some devices with a Type-C port.
+config TYPEC_SWITCH_GPIO
+ tristate "Simple Super Speed Active Switch via GPIO"
+ help
+ Say Y or M if your system has a typec super speed channel
+ switch via a simple GPIO control.
+
endmenu
diff --git a/drivers/usb/typec/mux/Makefile b/drivers/usb/typec/mux/Makefile
index 1332e469b8a0..e29377c5ff9a 100644
--- a/drivers/usb/typec/mux/Makefile
+++ b/drivers/usb/typec/mux/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_TYPEC_MUX_PI3USB30532) += pi3usb30532.o
+obj-$(CONFIG_TYPEC_SWITCH_GPIO) += gpio-switch.o
diff --git a/drivers/usb/typec/mux/gpio-switch.c b/drivers/usb/typec/mux/gpio-switch.c
new file mode 100644
index 000000000000..ef4805cdc575
--- /dev/null
+++ b/drivers/usb/typec/mux/gpio-switch.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * gpio-switch.c - typec switch via a simple GPIO control.
+ *
+ * Copyright 2019 NXP
+ * Author: Jun Li <jun.li@nxp.com>
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/usb/typec_mux.h>
+
+struct gpio_typec_switch {
+ struct typec_switch *sw;
+ struct mutex lock;
+ struct gpio_desc *ss_sel;
+ struct gpio_desc *ss_reset;
+};
+
+static int switch_gpio_set(struct typec_switch *sw,
+ enum typec_orientation orientation)
+{
+ struct gpio_typec_switch *gpio_sw = typec_switch_get_drvdata(sw);
+
+ mutex_lock(&gpio_sw->lock);
+
+ switch (orientation) {
+ case TYPEC_ORIENTATION_NORMAL:
+ gpiod_set_value_cansleep(gpio_sw->ss_sel, 1);
+ break;
+ case TYPEC_ORIENTATION_REVERSE:
+ gpiod_set_value_cansleep(gpio_sw->ss_sel, 0);
+ break;
+ case TYPEC_ORIENTATION_NONE:
+ break;
+ }
+
+ mutex_unlock(&gpio_sw->lock);
+
+ return 0;
+}
+
+static int typec_switch_gpio_probe(struct platform_device *pdev)
+{
+ struct gpio_typec_switch *gpio_sw;
+ struct device *dev = &pdev->dev;
+ struct typec_switch_desc sw_desc;
+
+ gpio_sw = devm_kzalloc(dev, sizeof(*gpio_sw), GFP_KERNEL);
+ if (!gpio_sw)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, gpio_sw);
+
+ sw_desc.drvdata = gpio_sw;
+ sw_desc.fwnode = dev->fwnode;
+ sw_desc.set = switch_gpio_set;
+ mutex_init(&gpio_sw->lock);
+
+ /* Get the super speed mux reset GPIO, it's optional */
+ gpio_sw->ss_reset = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(gpio_sw->ss_reset))
+ return PTR_ERR(gpio_sw->ss_reset);
+
+ if (gpio_sw->ss_reset)
+ usleep_range(700, 1000);
+
+ /* Get the super speed active channel selection GPIO */
+ gpio_sw->ss_sel = devm_gpiod_get(dev, "switch", GPIOD_OUT_LOW);
+ if (IS_ERR(gpio_sw->ss_sel))
+ return PTR_ERR(gpio_sw->ss_sel);
+
+ gpio_sw->sw = typec_switch_register(dev, &sw_desc);
+ if (IS_ERR(gpio_sw->sw)) {
+ dev_err(dev, "Error registering typec switch: %ld\n", PTR_ERR(gpio_sw->sw));
+ return PTR_ERR(gpio_sw->sw);
+ }
+
+ return 0;
+}
+
+static int typec_switch_gpio_remove(struct platform_device *pdev)
+{
+ struct gpio_typec_switch *gpio_sw = platform_get_drvdata(pdev);
+
+ typec_switch_unregister(gpio_sw->sw);
+
+ return 0;
+}
+
+static const struct of_device_id of_typec_switch_gpio_match[] = {
+ { .compatible = "nxp,ptn36043" },
+ { .compatible = "nxp,cbtl04gp" },
+ { /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, of_typec_switch_gpio_match);
+
+static struct platform_driver typec_switch_gpio_driver = {
+ .probe = typec_switch_gpio_probe,
+ .remove = typec_switch_gpio_remove,
+ .driver = {
+ .name = "typec-switch-gpio",
+ .of_match_table = of_typec_switch_gpio_match,
+ },
+};
+
+module_platform_driver(typec_switch_gpio_driver);
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("TypeC Super Speed Switch GPIO driver");
+MODULE_AUTHOR("Jun Li <jun.li@nxp.com>");
diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c
index c1f7073a56de..4b2f380ca640 100644
--- a/drivers/usb/typec/tcpm/tcpci.c
+++ b/drivers/usb/typec/tcpm/tcpci.c
@@ -10,6 +10,7 @@
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
+#include <linux/irq.h>
#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/usb/pd.h>
@@ -108,9 +109,6 @@ static int tcpci_start_toggling(struct tcpc_dev *tcpc,
struct tcpci *tcpci = tcpc_to_tcpci(tcpc);
unsigned int reg = TCPC_ROLE_CTRL_DRP;
- if (port_type != TYPEC_PORT_DRP)
- return -EOPNOTSUPP;
-
/* Handle vendor drp toggling */
if (tcpci->data->start_drp_toggling) {
ret = tcpci->data->start_drp_toggling(tcpci, tcpci->data, cc);
@@ -566,6 +564,7 @@ static int tcpci_probe(struct i2c_client *client,
if (IS_ERR(chip->tcpci))
return PTR_ERR(chip->tcpci);
+ irq_set_status_flags(client->irq, IRQ_DISABLE_UNLAZY);
err = devm_request_threaded_irq(&client->dev, client->irq, NULL,
_tcpci_irq,
IRQF_ONESHOT | IRQF_TRIGGER_LOW,
@@ -575,6 +574,8 @@ static int tcpci_probe(struct i2c_client *client,
return err;
}
+ device_set_wakeup_capable(chip->tcpci->dev, true);
+
return 0;
}
@@ -583,10 +584,40 @@ static int tcpci_remove(struct i2c_client *client)
struct tcpci_chip *chip = i2c_get_clientdata(client);
tcpci_unregister_port(chip->tcpci);
+ irq_clear_status_flags(client->irq, IRQ_DISABLE_UNLAZY);
+
+ return 0;
+}
+
+static int __maybe_unused tcpci_suspend(struct device *dev)
+{
+ struct i2c_client *i2c = to_i2c_client(dev);
+
+ if (device_may_wakeup(dev))
+ enable_irq_wake(i2c->irq);
+ else
+ disable_irq(i2c->irq);
return 0;
}
+
+static int __maybe_unused tcpci_resume(struct device *dev)
+{
+ struct i2c_client *i2c = to_i2c_client(dev);
+
+ if (device_may_wakeup(dev))
+ disable_irq_wake(i2c->irq);
+ else
+ enable_irq(i2c->irq);
+
+ return 0;
+}
+
+static const struct dev_pm_ops tcpci_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(tcpci_suspend, tcpci_resume)
+};
+
static const struct i2c_device_id tcpci_id[] = {
{ "tcpci", 0 },
{ }
@@ -604,6 +635,7 @@ MODULE_DEVICE_TABLE(of, tcpci_of_match);
static struct i2c_driver tcpci_i2c_driver = {
.driver = {
.name = "tcpci",
+ .pm = &tcpci_pm_ops,
.of_match_table = of_match_ptr(tcpci_of_match),
},
.probe = tcpci_probe,
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
index 5f61d9977a15..ea68559ce60e 100644
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -29,6 +29,8 @@
#include <linux/usb/tcpm.h>
#include <linux/usb/typec_altmode.h>
#include <linux/workqueue.h>
+#include <linux/extcon.h>
+#include <linux/extcon-provider.h>
#define FOREACH_STATE(S) \
S(INVALID_STATE), \
@@ -139,6 +141,12 @@ static const char * const tcpm_states[] = {
FOREACH_STATE(GENERATE_STRING)
};
+static const unsigned int tcpm_extcon_cable[] = {
+ EXTCON_USB_HOST,
+ EXTCON_USB,
+ EXTCON_NONE,
+};
+
enum vdm_states {
VDM_STATE_ERR_BUSY = -3,
VDM_STATE_ERR_SEND = -2,
@@ -193,6 +201,7 @@ struct pd_pps_data {
struct tcpm_port {
struct device *dev;
+ struct extcon_dev *edev;
struct mutex lock; /* tcpm state machine lock */
struct workqueue_struct *wq;
@@ -670,6 +679,20 @@ static int tcpm_mux_set(struct tcpm_port *port, int state,
ret = usb_role_switch_set_role(port->role_sw, usb_role);
if (ret)
return ret;
+ } else if (port->edev) {
+ if (usb_role == USB_ROLE_NONE) {
+ extcon_set_state_sync(port->edev, EXTCON_USB_HOST,
+ false);
+ extcon_set_state_sync(port->edev, EXTCON_USB, false);
+ } else if (usb_role == USB_ROLE_DEVICE) {
+ extcon_set_state_sync(port->edev, EXTCON_USB_HOST,
+ false);
+ extcon_set_state_sync(port->edev, EXTCON_USB, true);
+ } else {
+ extcon_set_state_sync(port->edev, EXTCON_USB, false);
+ extcon_set_state_sync(port->edev, EXTCON_USB_HOST,
+ true);
+ }
}
return typec_set_mode(port->typec_port, state);
@@ -2623,6 +2646,8 @@ static int tcpm_src_attach(struct tcpm_port *port)
if (port->attached)
return 0;
+ tcpm_set_cc(port, tcpm_rp_cc(port));
+
ret = tcpm_set_polarity(port, polarity);
if (ret < 0)
return ret;
@@ -2744,6 +2769,8 @@ static int tcpm_snk_attach(struct tcpm_port *port)
if (port->attached)
return 0;
+ tcpm_set_cc(port, TYPEC_CC_RD);
+
ret = tcpm_set_polarity(port, port->cc2 != TYPEC_CC_OPEN ?
TYPEC_POLARITY_CC2 : TYPEC_POLARITY_CC1);
if (ret < 0)
@@ -3146,7 +3173,11 @@ static void run_state_machine(struct tcpm_port *port)
ret = tcpm_snk_attach(port);
if (ret < 0)
tcpm_set_state(port, SNK_UNATTACHED, 0);
- else
+ else if (port->port_type == TYPEC_PORT_SRC &&
+ port->typec_caps.data == TYPEC_PORT_DRD) {
+ tcpm_typec_connect(port);
+ tcpm_log(port, "Keep at SNK_ATTACHED for USB data.");
+ } else
tcpm_set_state(port, SNK_STARTUP, 0);
break;
case SNK_STARTUP:
@@ -4744,7 +4775,7 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
mutex_init(&port->lock);
mutex_init(&port->swap_lock);
- port->wq = create_singlethread_workqueue(dev_name(dev));
+ port->wq = create_freezable_workqueue(dev_name(dev));
if (!port->wq)
return ERR_PTR(-ENOMEM);
INIT_DELAYED_WORK(&port->state_machine, tcpm_state_machine_work);
@@ -4820,6 +4851,19 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
}
}
+ port->edev = devm_extcon_dev_allocate(port->dev, tcpm_extcon_cable);
+ if (IS_ERR(port->edev)) {
+ dev_err(port->dev, "failed to allocate extcon dev.\n");
+ err = -ENOMEM;
+ goto out_role_sw_put;
+ }
+
+ err = devm_extcon_dev_register(port->dev, port->edev);
+ if (err) {
+ dev_err(port->dev, "failed to register extcon dev.\n");
+ goto out_role_sw_put;
+ }
+
mutex_lock(&port->lock);
tcpm_init(port);
mutex_unlock(&port->lock);
@@ -4840,6 +4884,9 @@ void tcpm_unregister_port(struct tcpm_port *port)
{
int i;
+ cancel_delayed_work_sync(&port->state_machine);
+ cancel_delayed_work_sync(&port->vdm_state_machine);
+
tcpm_reset_port(port);
for (i = 0; i < ARRAY_SIZE(port->port_altmode); i++)
typec_unregister_altmode(port->port_altmode[i]);
diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h
index edd89b7c8f18..17e9b62f4660 100644
--- a/include/linux/usb/chipidea.h
+++ b/include/linux/usb/chipidea.h
@@ -62,11 +62,15 @@ struct ci_hdrc_platform_data {
#define CI_HDRC_REQUIRES_ALIGNED_DMA BIT(13)
#define CI_HDRC_IMX_IS_HSIC BIT(14)
#define CI_HDRC_PMQOS BIT(15)
+/* PHY enter low power mode when bus suspend */
+#define CI_HDRC_HOST_SUSP_PHY_LPM BIT(16)
enum usb_dr_mode dr_mode;
#define CI_HDRC_CONTROLLER_RESET_EVENT 0
#define CI_HDRC_CONTROLLER_STOPPED_EVENT 1
#define CI_HDRC_IMX_HSIC_ACTIVE_EVENT 2
#define CI_HDRC_IMX_HSIC_SUSPEND_EVENT 3
+#define CI_HDRC_CONTROLLER_VBUS_EVENT 4
+#define CI_HDRC_NOTIFY_RET_DEFER_EVENT 5
int (*notify_event) (struct ci_hdrc *ci, unsigned event);
struct regulator *reg_vbus;
struct usb_otg_caps ci_otg_caps;
@@ -99,4 +103,6 @@ struct platform_device *ci_hdrc_add_device(struct device *dev,
/* Remove ci hdrc device */
void ci_hdrc_remove_device(struct platform_device *pdev);
+/* Get current available role */
+enum usb_dr_mode ci_hdrc_query_available_role(struct platform_device *pdev);
#endif
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index 712b2a603645..5e2695d3852c 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -409,7 +409,10 @@ struct hc_driver {
int (*find_raw_port_number)(struct usb_hcd *, int);
/* Call for power on/off the port if necessary */
int (*port_power)(struct usb_hcd *hcd, int portnum, bool enable);
-
+ /* Call for SINGLE_STEP_SET_FEATURE Test for USB2 EH certification */
+#define EHSET_TEST_SINGLE_STEP_SET_FEATURE 0x06
+ int (*submit_single_step_set_feature)(struct usb_hcd *,
+ struct urb *, int);
};
static inline int hcd_giveback_urb_in_bh(struct usb_hcd *hcd)
@@ -474,6 +477,14 @@ int usb_hcd_setup_local_mem(struct usb_hcd *hcd, phys_addr_t phys_addr,
struct platform_device;
extern void usb_hcd_platform_shutdown(struct platform_device *dev);
+#ifdef CONFIG_USB_HCD_TEST_MODE
+extern int ehset_single_step_set_feature(struct usb_hcd *hcd, int port);
+#else
+static inline int ehset_single_step_set_feature(struct usb_hcd *hcd, int port)
+{
+ return 0;
+}
+#endif /* CONFIG_USB_HCD_TEST_MODE */
#ifdef CONFIG_USB_PCI
struct pci_dev;
diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h
index e78eb577d0fa..1a0155ff051e 100644
--- a/include/linux/usb/otg-fsm.h
+++ b/include/linux/usb/otg-fsm.h
@@ -54,6 +54,7 @@ enum otg_fsm_timer {
A_WAIT_ENUM,
B_DATA_PLS,
B_SSEND_SRP,
+ A_DP_END,
NUM_OTG_FSM_TIMERS,
};
diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h
index e4de6bc1f69b..086f95f7b424 100644
--- a/include/linux/usb/phy.h
+++ b/include/linux/usb/phy.h
@@ -63,6 +63,13 @@ enum usb_otg_state {
OTG_STATE_A_VBUS_ERR,
};
+/* The usb role of phy to be working with */
+enum usb_current_mode {
+ CUR_USB_MODE_NONE,
+ CUR_USB_MODE_HOST,
+ CUR_USB_MODE_DEVICE,
+};
+
struct usb_phy;
struct usb_otg;
@@ -155,6 +162,15 @@ struct usb_phy {
* manually detect the charger type.
*/
enum usb_charger_type (*charger_detect)(struct usb_phy *x);
+
+ int (*notify_suspend)(struct usb_phy *x,
+ enum usb_device_speed speed);
+ int (*notify_resume)(struct usb_phy *x,
+ enum usb_device_speed speed);
+
+ int (*set_mode)(struct usb_phy *x,
+ enum usb_current_mode mode);
+
};
/* for board-specific init logic */
@@ -213,6 +229,15 @@ usb_phy_vbus_off(struct usb_phy *x)
return x->set_vbus(x, false);
}
+static inline int
+usb_phy_set_mode(struct usb_phy *x, enum usb_current_mode mode)
+{
+ if (!x || !x->set_mode)
+ return 0;
+
+ return x->set_mode(x, mode);
+}
+
/* for usb host and peripheral controller drivers */
#if IS_ENABLED(CONFIG_USB_PHY)
extern struct usb_phy *usb_get_phy(enum usb_phy_type type);
@@ -334,6 +359,24 @@ usb_phy_notify_disconnect(struct usb_phy *x, enum usb_device_speed speed)
return 0;
}
+static inline int usb_phy_notify_suspend
+ (struct usb_phy *x, enum usb_device_speed speed)
+{
+ if (x && x->notify_suspend)
+ return x->notify_suspend(x, speed);
+ else
+ return 0;
+}
+
+static inline int usb_phy_notify_resume
+ (struct usb_phy *x, enum usb_device_speed speed)
+{
+ if (x && x->notify_resume)
+ return x->notify_resume(x, speed);
+ else
+ return 0;
+}
+
/* notifiers */
static inline int
usb_register_notifier(struct usb_phy *x, struct notifier_block *nb)