linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 0/8] arm64: meson: Add support for USB on Amlogic G12A
@ 2019-03-18 10:11 Neil Armstrong
  2019-03-18 10:11 ` [PATCH v3 1/8] dt-bindings: phy: Add Amlogic G12A USB2 PHY Bindings Neil Armstrong
                   ` (8 more replies)
  0 siblings, 9 replies; 16+ messages in thread
From: Neil Armstrong @ 2019-03-18 10:11 UTC (permalink / raw)
  To: gregkh, hminas, balbi, kishon
  Cc: Neil Armstrong, linux-amlogic, linux-usb, linux-arm-kernel, linux-kernel

This patchset adds support for USB on Amlogic G12A SoCs.

This patchset is composed with :
- bindings of the PHYs
- bindings of the USB Control Glue
- PHY Drivers
- USB Control Glue driver

Device Tree nodes will be added in a separate patchset.

The Amlogic G12A USB Complex is composed of :
- 2 USB Controllers :
 * DWC3 for USB2 and USB3 Host functionality
 * DWC2 for USB2 Peripheral functionality
- 2 USB2 OTG PHYs, only a single one will be routed to either DWC2 to DWC3
- 1 USB3 PHY shared with PCIE funcionnality
- A Glue to control PHY routing, setup and OTG detection

The Glue configures the UTMI 8bit interfaces for the USB2 PHYs, including
routing of the OTG PHY between the DWC3 and DWC2 controllers, and
setups the on-chip OTG mode selection for this PHY.

This drivers supports the on-probe setup of the OTG mode, and manually
via the USB Role interface. The IRQ mode change detect is yet to be added
in a future patchset, mainly due to lack of hardware to validate on.

Changes since v2 at [1]:
- Collected review tags
- Removed syscon.h in usb2 phy driver
- Added comment on the USB3/PCIe switch
- Renamed dwc3_meson_g12a_usb_init_mode in glue driver
- Changed indentation in dwc3_meson_g12a_role_set
- Switched to of_get_compatible_child in glue code
- Added devm_add_action_or_reset() to disable clock in case of probe failure
- Checks priv->vbus before enabling regulator
- Switched phy_init/phy_power_on
- Removed priv->phys[i] NULL checks in suspend code
- Removed clk_disable_unprepare/clk_put in remove code

Changes since v1 at [0]:
 - USB2 PHY Bindings: collected tags and updated commit log
 - USB3/PCIE PHY Bindings: collected tag and updated reset list
 - DWC2 Compatible: collected tags
 - DWC3 Glue Bindings:
   - removed clock-names and reset-names for single entries
   - removed ports graph for PHYs and replaced by phys list with phy-names
   - added dr_mode and optional vbus-supply
   - fixed example
 - USB2 PHY Driver: removed USB_SUPPORT and otg.h include
 - USB3/PCIE PHY Driver: removed USB_SUPPORT, fixed comment and unused reg variable
 - USB Glue Driver:
   - Moved debugfs I/F to USB_ROLE_SWITCH framework
   - Integrated USB Glue Control Registers offset
   - Removed of graph parsing to use devm_phy_optional_get()
   - Fixed PHY mode handling and OTG switching
   - Simplified PHY calls by removing the NULL test
   - Added optional vbus regulator handling

[1] https://lkml.kernel.org/r/20190212151413.24632-1-narmstrong@baylibre.com
[2] https://lkml.kernel.org/r/20190304103846.2060-1-narmstrong@baylibre.com

Neil Armstrong (8):
  dt-bindings: phy: Add Amlogic G12A USB2 PHY Bindings
  dt-bindings: phy: Add Amlogic G12A USB3+PCIE Combo PHY Bindings
  dt-bindings: usb: dwc2: Add Amlogic G12A DWC2 Compatible
  dt-bindings: usb: dwc3: Add Amlogic G12A DWC3 Glue Bindings
  phy: amlogic: add Amlogic G12A USB2 PHY Driver
  phy: amlogic: Add Amlogic G12A USB3 + PCIE Combo PHY Driver
  usb: dwc2: Add Amlogic G12A DWC2 Params
  usb: dwc3: Add Amlogic G12A DWC3 glue

 .../bindings/phy/meson-g12a-usb2-phy.txt      |  22 +
 .../bindings/phy/meson-g12a-usb3-pcie-phy.txt |  22 +
 .../devicetree/bindings/usb/amlogic,dwc3.txt  |  88 +++
 .../devicetree/bindings/usb/dwc2.txt          |   1 +
 drivers/phy/amlogic/Kconfig                   |  22 +
 drivers/phy/amlogic/Makefile                  |   2 +
 drivers/phy/amlogic/phy-meson-g12a-usb2.c     | 189 ++++++
 .../phy/amlogic/phy-meson-g12a-usb3-pcie.c    | 413 ++++++++++++
 drivers/usb/dwc2/params.c                     |  12 +
 drivers/usb/dwc3/Kconfig                      |  10 +
 drivers/usb/dwc3/Makefile                     |   1 +
 drivers/usb/dwc3/dwc3-meson-g12a.c            | 602 ++++++++++++++++++
 12 files changed, 1384 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/meson-g12a-usb2-phy.txt
 create mode 100644 Documentation/devicetree/bindings/phy/meson-g12a-usb3-pcie-phy.txt
 create mode 100644 drivers/phy/amlogic/phy-meson-g12a-usb2.c
 create mode 100644 drivers/phy/amlogic/phy-meson-g12a-usb3-pcie.c
 create mode 100644 drivers/usb/dwc3/dwc3-meson-g12a.c

-- 
2.20.1


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH v3 1/8] dt-bindings: phy: Add Amlogic G12A USB2 PHY Bindings
  2019-03-18 10:11 [PATCH v3 0/8] arm64: meson: Add support for USB on Amlogic G12A Neil Armstrong
@ 2019-03-18 10:11 ` Neil Armstrong
  2019-03-18 10:11 ` [PATCH v3 2/8] dt-bindings: phy: Add Amlogic G12A USB3+PCIE Combo " Neil Armstrong
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 16+ messages in thread
From: Neil Armstrong @ 2019-03-18 10:11 UTC (permalink / raw)
  To: gregkh, hminas, balbi, kishon, devicetree
  Cc: Neil Armstrong, linux-amlogic, linux-usb, linux-arm-kernel,
	linux-kernel, Martin Blumenstingl, Rob Herring

Add the Amlogic G12A Family USB2 OTG PHY Bindings

The PHY can work in host or peripheral modes depending on it's position.
Configuration of the mode is part of the USBCTRL registers which are
outside of the PHY registers.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
Reviewed-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../bindings/phy/meson-g12a-usb2-phy.txt      | 22 +++++++++++++++++++
 1 file changed, 22 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/meson-g12a-usb2-phy.txt

diff --git a/Documentation/devicetree/bindings/phy/meson-g12a-usb2-phy.txt b/Documentation/devicetree/bindings/phy/meson-g12a-usb2-phy.txt
new file mode 100644
index 000000000000..a6ebc3dea159
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/meson-g12a-usb2-phy.txt
@@ -0,0 +1,22 @@
+* Amlogic G12A USB2 PHY binding
+
+Required properties:
+- compatible:	Should be "amlogic,meson-g12a-usb2-phy"
+- reg:		The base address and length of the registers
+- #phys-cells:	must be 0 (see phy-bindings.txt in this directory)
+- clocks:	a phandle to the clock of this PHY
+- clock-names:	must be "xtal"
+- resets:	a phandle to the reset line of this PHY
+- reset-names:	must be "phy"
+- phy-supply:	see phy-bindings.txt in this directory
+
+Example:
+	usb2_phy0: phy@36000 {
+		compatible = "amlogic,g12a-usb2-phy";
+		reg = <0x0 0x36000 0x0 0x2000>;
+		clocks = <&xtal>;
+		clock-names = "xtal";
+		resets = <&reset RESET_USB_PHY21>;
+		reset-names = "phy";
+		#phy-cells = <0>;
+	};
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v3 2/8] dt-bindings: phy: Add Amlogic G12A USB3+PCIE Combo PHY Bindings
  2019-03-18 10:11 [PATCH v3 0/8] arm64: meson: Add support for USB on Amlogic G12A Neil Armstrong
  2019-03-18 10:11 ` [PATCH v3 1/8] dt-bindings: phy: Add Amlogic G12A USB2 PHY Bindings Neil Armstrong
@ 2019-03-18 10:11 ` Neil Armstrong
  2019-03-18 10:11 ` [PATCH v3 3/8] dt-bindings: usb: dwc2: Add Amlogic G12A DWC2 Compatible Neil Armstrong
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 16+ messages in thread
From: Neil Armstrong @ 2019-03-18 10:11 UTC (permalink / raw)
  To: gregkh, hminas, balbi, kishon, devicetree
  Cc: Neil Armstrong, linux-amlogic, linux-usb, linux-arm-kernel,
	linux-kernel, Martin Blumenstingl, Rob Herring

Add the Amlogic G12A Family USB3 + PCIE Combo PHY Bindings.

This PHY can provide exclusively USB3 or PCIE support on shared I/Os.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
Reviewed-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../bindings/phy/meson-g12a-usb3-pcie-phy.txt | 22 +++++++++++++++++++
 1 file changed, 22 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/meson-g12a-usb3-pcie-phy.txt

diff --git a/Documentation/devicetree/bindings/phy/meson-g12a-usb3-pcie-phy.txt b/Documentation/devicetree/bindings/phy/meson-g12a-usb3-pcie-phy.txt
new file mode 100644
index 000000000000..7cfc17e2df31
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/meson-g12a-usb3-pcie-phy.txt
@@ -0,0 +1,22 @@
+* Amlogic G12A USB3 + PCIE Combo PHY binding
+
+Required properties:
+- compatible:	Should be "amlogic,meson-g12a-usb3-pcie-phy"
+- #phys-cells:	must be 1. The cell number is used to select the phy mode
+  as defined in <dt-bindings/phy/phy.h> between PHY_TYPE_USB3 and PHY_TYPE_PCIE
+- reg:		The base address and length of the registers
+- clocks:	a phandle to the 100MHz reference clock of this PHY
+- clock-names:	must be "ref_clk"
+- resets:	phandle to the reset lines for the PHY control
+- reset-names:	must be "phy"
+
+Example:
+	usb3_pcie_phy: phy@46000 {
+		compatible = "amlogic,g12a-usb3-pcie-phy";
+		reg = <0x0 0x46000 0x0 0x2000>;
+		clocks = <&clkc CLKID_PCIE_PLL>;
+		clock-names = "ref_clk";
+		resets = <&reset RESET_PCIE_PHY>;
+		reset-names = "phy";
+		#phy-cells = <1>;
+	};
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v3 3/8] dt-bindings: usb: dwc2: Add Amlogic G12A DWC2 Compatible
  2019-03-18 10:11 [PATCH v3 0/8] arm64: meson: Add support for USB on Amlogic G12A Neil Armstrong
  2019-03-18 10:11 ` [PATCH v3 1/8] dt-bindings: phy: Add Amlogic G12A USB2 PHY Bindings Neil Armstrong
  2019-03-18 10:11 ` [PATCH v3 2/8] dt-bindings: phy: Add Amlogic G12A USB3+PCIE Combo " Neil Armstrong
@ 2019-03-18 10:11 ` Neil Armstrong
  2019-03-18 10:11 ` [PATCH v3 4/8] dt-bindings: usb: dwc3: Add Amlogic G12A DWC3 Glue Bindings Neil Armstrong
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 16+ messages in thread
From: Neil Armstrong @ 2019-03-18 10:11 UTC (permalink / raw)
  To: gregkh, hminas, balbi, kishon, devicetree
  Cc: Neil Armstrong, linux-amlogic, linux-usb, linux-arm-kernel,
	linux-kernel, Martin Blumenstingl, Rob Herring

Adds the specific compatible string for the DWC2 IP found in the
Amlogic G12A SoC Family.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
Reviewed-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 Documentation/devicetree/bindings/usb/dwc2.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/usb/dwc2.txt b/Documentation/devicetree/bindings/usb/dwc2.txt
index 6dc3c4a34483..e150b7b227c9 100644
--- a/Documentation/devicetree/bindings/usb/dwc2.txt
+++ b/Documentation/devicetree/bindings/usb/dwc2.txt
@@ -14,6 +14,7 @@ Required properties:
   - "amlogic,meson8-usb": The DWC2 USB controller instance in Amlogic Meson8 SoCs;
   - "amlogic,meson8b-usb": The DWC2 USB controller instance in Amlogic Meson8b SoCs;
   - "amlogic,meson-gxbb-usb": The DWC2 USB controller instance in Amlogic S905 SoCs;
+  - "amlogic,meson-g12a-usb": The DWC2 USB controller instance in Amlogic G12A SoCs;
   - "amcc,dwc-otg": The DWC2 USB controller instance in AMCC Canyonlands 460EX SoCs;
   - snps,dwc2: A generic DWC2 USB controller with default parameters.
   - "st,stm32f4x9-fsotg": The DWC2 USB FS/HS controller instance in STM32F4x9 SoCs
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v3 4/8] dt-bindings: usb: dwc3: Add Amlogic G12A DWC3 Glue Bindings
  2019-03-18 10:11 [PATCH v3 0/8] arm64: meson: Add support for USB on Amlogic G12A Neil Armstrong
                   ` (2 preceding siblings ...)
  2019-03-18 10:11 ` [PATCH v3 3/8] dt-bindings: usb: dwc2: Add Amlogic G12A DWC2 Compatible Neil Armstrong
@ 2019-03-18 10:11 ` Neil Armstrong
  2019-03-18 10:11 ` [PATCH v3 5/8] phy: amlogic: add Amlogic G12A USB2 PHY Driver Neil Armstrong
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 16+ messages in thread
From: Neil Armstrong @ 2019-03-18 10:11 UTC (permalink / raw)
  To: gregkh, hminas, balbi, kishon, devicetree
  Cc: Neil Armstrong, linux-amlogic, linux-usb, linux-arm-kernel,
	linux-kernel, Martin Blumenstingl, Rob Herring

Adds the bindings for the Amlogic G12A USB Glue HW.

The Amlogic G12A SoC Family embeds 2 USB Controllers :
- a DWC3 IP configured as Host for USB2 and USB3
- a DWC2 IP configured as Peripheral USB2 Only

A glue connects these both controllers to 2 USB2 PHYs,
and optionnally to an USB3+PCIE Combo PHY shared with the PCIE controller.

The Glue configures the UTMI 8bit interfaces for the USB2 PHYs, including
routing of the OTG PHY between the DWC3 and DWC2 controllers, and
setups the on-chip OTG mode selection for this PHY.

The PHYs phandles are passed  to the Glue node since the Glue controls the
interface with the PHY, not the DWC3 controller.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
Reviewed-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
 .../devicetree/bindings/usb/amlogic,dwc3.txt  | 88 +++++++++++++++++++
 1 file changed, 88 insertions(+)

diff --git a/Documentation/devicetree/bindings/usb/amlogic,dwc3.txt b/Documentation/devicetree/bindings/usb/amlogic,dwc3.txt
index 9a8b631904fd..b9f04e617eb7 100644
--- a/Documentation/devicetree/bindings/usb/amlogic,dwc3.txt
+++ b/Documentation/devicetree/bindings/usb/amlogic,dwc3.txt
@@ -40,3 +40,91 @@ Example device nodes:
 				phy-names = "usb2-phy", "usb3-phy";
 			};
 		};
+
+Amlogic Meson G12A DWC3 USB SoC Controller Glue
+
+The Amlogic G12A embeds a DWC3 USB IP Core configured for USB2 and USB3
+in host-only mode, and a DWC2 IP Core configured for USB2 peripheral mode
+only.
+
+A glue connects the DWC3 core to USB2 PHYs and optionnaly to an USB3 PHY.
+
+One of the USB2 PHY can be re-routed in peripheral mode to a DWC2 USB IP.
+
+The DWC3 Glue controls the PHY routing and power, an interrupt line is
+connected to the Glue to serve as OTG ID change detection.
+
+Required properties:
+- compatible:	Should be "amlogic,meson-g12a-usb-ctrl"
+- clocks:	a handle for the "USB" clock
+- resets:	a handle for the shared "USB" reset line
+- reg:		The base address and length of the registers
+- interrupts:	the interrupt specifier for the OTG detection
+- phys: 	handle to used PHYs on the system
+	- a <0> phandle can be used if a PHY is not used
+- phy-names:	names of the used PHYs on the system :
+	- "usb2-phy0" for USB2 PHY0 if USBHOST_A port is used
+	- "usb2-phy1" for USB2 PHY1 if USBOTG_B port is used
+	- "usb3-phy0" for USB3 PHY if USB3_0 is used
+- dr_mode:	should be "host", "peripheral", or "otg" depending on
+	the usage and configuration of the OTG Capable port.
+	- "host" and "peripheral" means a fixed Host or Device only connection
+	- "otg" means the port can be used as both Host or Device and
+	  be switched automatically using the OTG ID pin.
+
+Optional properties:
+- vbus-supply:	should be a phandle to the regulator controlling the VBUS
+		power supply when used in OTG switchable mode
+
+Required child nodes:
+
+A child node must exist to represent the core DWC3 IP block. The name of
+the node is not important. The content of the node is defined in dwc3.txt.
+
+A child node must exist to represent the core DWC2 IP block. The name of
+the node is not important. The content of the node is defined in dwc2.txt.
+
+PHY documentation is provided in the following places:
+- Documentation/devicetree/bindings/phy/meson-g12a-usb2-phy.txt
+- Documentation/devicetree/bindings/phy/meson-g12a-usb3-pcie-phy.txt
+
+Example device nodes:
+	usb: usb@ffe09000 {
+			compatible = "amlogic,meson-g12a-usb-ctrl";
+			reg = <0x0 0xffe09000 0x0 0xa0>;
+			interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
+			#address-cells = <2>;
+			#size-cells = <2>;
+			ranges;
+
+			clocks = <&clkc CLKID_USB>;
+			resets = <&reset RESET_USB>;
+
+			dr_mode = "otg";
+
+			phys = <&usb2_phy0>, <&usb2_phy1>,
+			       <&usb3_pcie_phy PHY_TYPE_USB3>;
+			phy-names = "usb2-phy0", "usb2-phy1", "usb3-phy0";
+
+			dwc2: usb@ff400000 {
+				compatible = "amlogic,meson-g12a-usb", "snps,dwc2";
+				reg = <0x0 0xff400000 0x0 0x40000>;
+				interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
+				clocks = <&clkc CLKID_USB1_DDR_BRIDGE>;
+				clock-names = "ddr";
+				phys = <&usb2_phy1>;
+				dr_mode = "peripheral";
+				g-rx-fifo-size = <192>;
+				g-np-tx-fifo-size = <128>;
+				g-tx-fifo-size = <128 128 16 16 16>;
+			};
+
+			dwc3: usb@ff500000 {
+				compatible = "snps,dwc3";
+				reg = <0x0 0xff500000 0x0 0x100000>;
+				interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
+				dr_mode = "host";
+				snps,dis_u2_susphy_quirk;
+				snps,quirk-frame-length-adjustment;
+			};
+	};
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v3 5/8] phy: amlogic: add Amlogic G12A USB2 PHY Driver
  2019-03-18 10:11 [PATCH v3 0/8] arm64: meson: Add support for USB on Amlogic G12A Neil Armstrong
                   ` (3 preceding siblings ...)
  2019-03-18 10:11 ` [PATCH v3 4/8] dt-bindings: usb: dwc3: Add Amlogic G12A DWC3 Glue Bindings Neil Armstrong
@ 2019-03-18 10:11 ` Neil Armstrong
  2019-03-18 10:18   ` Kishon Vijay Abraham I
  2019-03-18 10:11 ` [PATCH v3 6/8] phy: amlogic: Add Amlogic G12A USB3 + PCIE Combo " Neil Armstrong
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 16+ messages in thread
From: Neil Armstrong @ 2019-03-18 10:11 UTC (permalink / raw)
  To: gregkh, hminas, balbi, kishon
  Cc: Neil Armstrong, linux-amlogic, linux-usb, linux-arm-kernel,
	linux-kernel, Martin Blumenstingl

This adds support for the USB2 PHY found in the Amlogic G12A SoC Family.

It supports Host and/or Peripheral mode, depending on it's position.
The first PHY is only used as Host, but the second supports Dual modes
defined by the USB Control Glue HW in front of the USB Controllers.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
Reviewed-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
---
 drivers/phy/amlogic/Kconfig               |  11 ++
 drivers/phy/amlogic/Makefile              |   1 +
 drivers/phy/amlogic/phy-meson-g12a-usb2.c | 189 ++++++++++++++++++++++
 3 files changed, 201 insertions(+)
 create mode 100644 drivers/phy/amlogic/phy-meson-g12a-usb2.c

diff --git a/drivers/phy/amlogic/Kconfig b/drivers/phy/amlogic/Kconfig
index 23fe1cda2f70..560ff0f1ed4c 100644
--- a/drivers/phy/amlogic/Kconfig
+++ b/drivers/phy/amlogic/Kconfig
@@ -36,3 +36,14 @@ config PHY_MESON_GXL_USB3
 	  Enable this to support the Meson USB3 PHY and OTG detection
 	  IP block found in Meson GXL and GXM SoCs.
 	  If unsure, say N.
+
+config PHY_MESON_G12A_USB2
+	tristate "Meson G12A USB2 PHY driver"
+	default ARCH_MESON
+	depends on OF && (ARCH_MESON || COMPILE_TEST)
+	select GENERIC_PHY
+	select REGMAP_MMIO
+	help
+	  Enable this to support the Meson USB2 PHYs found in Meson
+	  G12A SoCs.
+	  If unsure, say N.
diff --git a/drivers/phy/amlogic/Makefile b/drivers/phy/amlogic/Makefile
index 4fd8848c194d..7d4d10f5a6b3 100644
--- a/drivers/phy/amlogic/Makefile
+++ b/drivers/phy/amlogic/Makefile
@@ -1,3 +1,4 @@
 obj-$(CONFIG_PHY_MESON8B_USB2)		+= phy-meson8b-usb2.o
 obj-$(CONFIG_PHY_MESON_GXL_USB2)	+= phy-meson-gxl-usb2.o
+obj-$(CONFIG_PHY_MESON_G12A_USB2)	+= phy-meson-g12a-usb2.o
 obj-$(CONFIG_PHY_MESON_GXL_USB3)	+= phy-meson-gxl-usb3.o
diff --git a/drivers/phy/amlogic/phy-meson-g12a-usb2.c b/drivers/phy/amlogic/phy-meson-g12a-usb2.c
new file mode 100644
index 000000000000..9f35259f237f
--- /dev/null
+++ b/drivers/phy/amlogic/phy-meson-g12a-usb2.c
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Meson G12A USB2 PHY driver
+ *
+ * Copyright (C) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved
+ * Copyright (C) 2019 BayLibre, SAS
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+
+#define PHY_CTRL_R0						0x0
+#define PHY_CTRL_R1						0x4
+#define PHY_CTRL_R2						0x8
+#define PHY_CTRL_R3						0xc
+#define PHY_CTRL_R4						0x10
+#define PHY_CTRL_R5						0x14
+#define PHY_CTRL_R6						0x18
+#define PHY_CTRL_R7						0x1c
+#define PHY_CTRL_R8						0x20
+#define PHY_CTRL_R9						0x24
+#define PHY_CTRL_R10						0x28
+#define PHY_CTRL_R11						0x2c
+#define PHY_CTRL_R12						0x30
+#define PHY_CTRL_R13						0x34
+#define PHY_CTRL_R14						0x38
+#define PHY_CTRL_R15						0x3c
+#define PHY_CTRL_R16						0x40
+#define PHY_CTRL_R17						0x44
+#define PHY_CTRL_R18						0x48
+#define PHY_CTRL_R19						0x4c
+#define PHY_CTRL_R20						0x50
+#define PHY_CTRL_R21						0x54
+#define PHY_CTRL_R22						0x58
+#define PHY_CTRL_R23						0x5c
+
+#define RESET_COMPLETE_TIME					1000
+#define PLL_RESET_COMPLETE_TIME					100
+
+struct phy_meson_g12a_usb2_priv {
+	struct device		*dev;
+	struct regmap		*regmap;
+	struct clk		*clk;
+	struct reset_control	*reset;
+};
+
+static const struct regmap_config phy_meson_g12a_usb2_regmap_conf = {
+	.reg_bits = 8,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.max_register = PHY_CTRL_R23,
+};
+
+static int phy_meson_g12a_usb2_init(struct phy *phy)
+{
+	struct phy_meson_g12a_usb2_priv *priv = phy_get_drvdata(phy);
+	int ret;
+
+	ret = reset_control_reset(priv->reset);
+	if (ret)
+		return ret;
+
+	udelay(RESET_COMPLETE_TIME);
+
+	/* usb2_otg_aca_en == 0 */
+	regmap_update_bits(priv->regmap, PHY_CTRL_R21, BIT(2), 0);
+
+	/* PLL Setup : 24MHz * 20 / 1 = 480MHz */
+	regmap_write(priv->regmap, PHY_CTRL_R16, 0x39400414);
+	regmap_write(priv->regmap, PHY_CTRL_R17, 0x927e0000);
+	regmap_write(priv->regmap, PHY_CTRL_R18, 0xac5f49e5);
+
+	udelay(PLL_RESET_COMPLETE_TIME);
+
+	/* UnReset PLL */
+	regmap_write(priv->regmap, PHY_CTRL_R16, 0x19400414);
+
+	/* PHY Tuning */
+	regmap_write(priv->regmap, PHY_CTRL_R20, 0xfe18);
+	regmap_write(priv->regmap, PHY_CTRL_R4, 0x8000fff);
+
+	/* Tuning Disconnect Threshold */
+	regmap_write(priv->regmap, PHY_CTRL_R3, 0x34);
+
+	/* Analog Settings */
+	regmap_write(priv->regmap, PHY_CTRL_R14, 0);
+	regmap_write(priv->regmap, PHY_CTRL_R13, 0x78000);
+
+	return 0;
+}
+
+static int phy_meson_g12a_usb2_exit(struct phy *phy)
+{
+	struct phy_meson_g12a_usb2_priv *priv = phy_get_drvdata(phy);
+
+	return reset_control_reset(priv->reset);
+}
+
+/* set_mode is not needed, mode setting is handled via the UTMI bus */
+static const struct phy_ops phy_meson_g12a_usb2_ops = {
+	.init		= phy_meson_g12a_usb2_init,
+	.exit		= phy_meson_g12a_usb2_exit,
+	.owner		= THIS_MODULE,
+};
+
+static int phy_meson_g12a_usb2_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct phy_provider *phy_provider;
+	struct resource *res;
+	struct phy_meson_g12a_usb2_priv *priv;
+	struct phy *phy;
+	void __iomem *base;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->dev = dev;
+	platform_set_drvdata(pdev, priv);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	priv->regmap = devm_regmap_init_mmio(dev, base,
+					     &phy_meson_g12a_usb2_regmap_conf);
+	if (IS_ERR(priv->regmap))
+		return PTR_ERR(priv->regmap);
+
+	priv->clk = devm_clk_get(dev, "xtal");
+	if (IS_ERR(priv->clk))
+		return PTR_ERR(priv->clk);
+
+	priv->reset = devm_reset_control_get(dev, "phy");
+	if (IS_ERR(priv->reset))
+		return PTR_ERR(priv->reset);
+
+	ret = reset_control_deassert(priv->reset);
+	if (ret)
+		return ret;
+
+	phy = devm_phy_create(dev, NULL, &phy_meson_g12a_usb2_ops);
+	if (IS_ERR(phy)) {
+		ret = PTR_ERR(phy);
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "failed to create PHY\n");
+
+		return ret;
+	}
+
+	phy_set_bus_width(phy, 8);
+	phy_set_drvdata(phy, priv);
+
+	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+	return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id phy_meson_g12a_usb2_of_match[] = {
+	{ .compatible = "amlogic,g12a-usb2-phy", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, phy_meson_g12a_usb2_of_match);
+
+static struct platform_driver phy_meson_g12a_usb2_driver = {
+	.probe	= phy_meson_g12a_usb2_probe,
+	.driver	= {
+		.name		= "phy-meson-g12a-usb2",
+		.of_match_table	= phy_meson_g12a_usb2_of_match,
+	},
+};
+module_platform_driver(phy_meson_g12a_usb2_driver);
+
+MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
+MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
+MODULE_DESCRIPTION("Meson G12A USB2 PHY driver");
+MODULE_LICENSE("GPL v2");
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v3 6/8] phy: amlogic: Add Amlogic G12A USB3 + PCIE Combo PHY Driver
  2019-03-18 10:11 [PATCH v3 0/8] arm64: meson: Add support for USB on Amlogic G12A Neil Armstrong
                   ` (4 preceding siblings ...)
  2019-03-18 10:11 ` [PATCH v3 5/8] phy: amlogic: add Amlogic G12A USB2 PHY Driver Neil Armstrong
@ 2019-03-18 10:11 ` Neil Armstrong
  2019-03-18 10:11 ` [PATCH v3 7/8] usb: dwc2: Add Amlogic G12A DWC2 Params Neil Armstrong
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 16+ messages in thread
From: Neil Armstrong @ 2019-03-18 10:11 UTC (permalink / raw)
  To: gregkh, hminas, balbi, kishon
  Cc: Neil Armstrong, linux-amlogic, linux-usb, linux-arm-kernel,
	linux-kernel, Martin Blumenstingl

This adds support for the shared USB3 + PCIE PHY found in the
Amlogic G12A SoC Family.

It supports USB3 Host mode or PCIE 2.0 mode, depending on the layout of
the board.

Selection is done by the #phy-cells, making the mode static and exclusive.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
Reviewed-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
---
 drivers/phy/amlogic/Kconfig                   |  11 +
 drivers/phy/amlogic/Makefile                  |   1 +
 .../phy/amlogic/phy-meson-g12a-usb3-pcie.c    | 413 ++++++++++++++++++
 3 files changed, 425 insertions(+)
 create mode 100644 drivers/phy/amlogic/phy-meson-g12a-usb3-pcie.c

diff --git a/drivers/phy/amlogic/Kconfig b/drivers/phy/amlogic/Kconfig
index 560ff0f1ed4c..4c08c1ccdd04 100644
--- a/drivers/phy/amlogic/Kconfig
+++ b/drivers/phy/amlogic/Kconfig
@@ -47,3 +47,14 @@ config PHY_MESON_G12A_USB2
 	  Enable this to support the Meson USB2 PHYs found in Meson
 	  G12A SoCs.
 	  If unsure, say N.
+
+config PHY_MESON_G12A_USB3_PCIE
+	tristate "Meson G12A USB3+PCIE Combo PHY driver"
+	default ARCH_MESON
+	depends on OF && (ARCH_MESON || COMPILE_TEST)
+	select GENERIC_PHY
+	select REGMAP_MMIO
+	help
+	  Enable this to support the Meson USB3 + PCIE Combo PHY found
+	  in Meson G12A SoCs.
+	  If unsure, say N.
diff --git a/drivers/phy/amlogic/Makefile b/drivers/phy/amlogic/Makefile
index 7d4d10f5a6b3..fdd008e1b19b 100644
--- a/drivers/phy/amlogic/Makefile
+++ b/drivers/phy/amlogic/Makefile
@@ -2,3 +2,4 @@ obj-$(CONFIG_PHY_MESON8B_USB2)		+= phy-meson8b-usb2.o
 obj-$(CONFIG_PHY_MESON_GXL_USB2)	+= phy-meson-gxl-usb2.o
 obj-$(CONFIG_PHY_MESON_G12A_USB2)	+= phy-meson-g12a-usb2.o
 obj-$(CONFIG_PHY_MESON_GXL_USB3)	+= phy-meson-gxl-usb3.o
+obj-$(CONFIG_PHY_MESON_G12A_USB3_PCIE)	+= phy-meson-g12a-usb3-pcie.o
diff --git a/drivers/phy/amlogic/phy-meson-g12a-usb3-pcie.c b/drivers/phy/amlogic/phy-meson-g12a-usb3-pcie.c
new file mode 100644
index 000000000000..6233a7979a93
--- /dev/null
+++ b/drivers/phy/amlogic/phy-meson-g12a-usb3-pcie.c
@@ -0,0 +1,413 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Amlogic G12A USB3 + PCIE Combo PHY driver
+ *
+ * Copyright (C) 2017 Amlogic, Inc. All rights reserved
+ * Copyright (C) 2019 BayLibre, SAS
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/platform_device.h>
+#include <dt-bindings/phy/phy.h>
+
+#define PHY_R0							0x00
+	#define PHY_R0_PCIE_POWER_STATE				GENMASK(4, 0)
+	#define PHY_R0_PCIE_USB3_SWITCH				GENMASK(6, 5)
+
+#define PHY_R1							0x04
+	#define PHY_R1_PHY_TX1_TERM_OFFSET			GENMASK(4, 0)
+	#define PHY_R1_PHY_TX0_TERM_OFFSET			GENMASK(9, 5)
+	#define PHY_R1_PHY_RX1_EQ				GENMASK(12, 10)
+	#define PHY_R1_PHY_RX0_EQ				GENMASK(15, 13)
+	#define PHY_R1_PHY_LOS_LEVEL				GENMASK(20, 16)
+	#define PHY_R1_PHY_LOS_BIAS				GENMASK(23, 21)
+	#define PHY_R1_PHY_REF_CLKDIV2				BIT(24)
+	#define PHY_R1_PHY_MPLL_MULTIPLIER			GENMASK(31, 25)
+
+#define PHY_R2							0x08
+	#define PHY_R2_PCS_TX_DEEMPH_GEN2_6DB			GENMASK(5, 0)
+	#define PHY_R2_PCS_TX_DEEMPH_GEN2_3P5DB			GENMASK(11, 6)
+	#define PHY_R2_PCS_TX_DEEMPH_GEN1			GENMASK(17, 12)
+	#define PHY_R2_PHY_TX_VBOOST_LVL			GENMASK(20, 18)
+
+#define PHY_R4							0x10
+	#define PHY_R4_PHY_CR_WRITE				BIT(0)
+	#define PHY_R4_PHY_CR_READ				BIT(1)
+	#define PHY_R4_PHY_CR_DATA_IN				GENMASK(17, 2)
+	#define PHY_R4_PHY_CR_CAP_DATA				BIT(18)
+	#define PHY_R4_PHY_CR_CAP_ADDR				BIT(19)
+
+#define PHY_R5							0x14
+	#define PHY_R5_PHY_CR_DATA_OUT				GENMASK(15, 0)
+	#define PHY_R5_PHY_CR_ACK				BIT(16)
+	#define PHY_R5_PHY_BS_OUT				BIT(17)
+
+struct phy_g12a_usb3_pcie_priv {
+	struct regmap		*regmap;
+	struct regmap		*regmap_cr;
+	struct clk		*clk_ref;
+	struct reset_control	*reset;
+	struct phy		*phy;
+	unsigned int		mode;
+};
+
+static const struct regmap_config phy_g12a_usb3_pcie_regmap_conf = {
+	.reg_bits = 8,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.max_register = PHY_R5,
+};
+
+static int phy_g12a_usb3_pcie_cr_bus_addr(struct phy_g12a_usb3_pcie_priv *priv,
+					  unsigned int addr)
+{
+	unsigned int val, reg;
+	int ret;
+
+	reg = FIELD_PREP(PHY_R4_PHY_CR_DATA_IN, addr);
+
+	regmap_write(priv->regmap, PHY_R4, reg);
+	regmap_write(priv->regmap, PHY_R4, reg);
+
+	regmap_write(priv->regmap, PHY_R4, reg | PHY_R4_PHY_CR_CAP_ADDR);
+
+	ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+				       (val & PHY_R5_PHY_CR_ACK),
+				       5, 1000);
+	if (ret)
+		return ret;
+
+	regmap_write(priv->regmap, PHY_R4, reg);
+
+	ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+				       !(val & PHY_R5_PHY_CR_ACK),
+				       5, 1000);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int phy_g12a_usb3_pcie_cr_bus_read(void *context, unsigned int addr,
+					  unsigned int *data)
+{
+	struct phy_g12a_usb3_pcie_priv *priv = context;
+	unsigned int val;
+	int ret;
+
+	ret = phy_g12a_usb3_pcie_cr_bus_addr(priv, addr);
+	if (ret)
+		return ret;
+
+	regmap_write(priv->regmap, PHY_R4, 0);
+	regmap_write(priv->regmap, PHY_R4, PHY_R4_PHY_CR_READ);
+
+	ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+				       (val & PHY_R5_PHY_CR_ACK),
+				       5, 1000);
+	if (ret)
+		return ret;
+
+	*data = FIELD_GET(PHY_R5_PHY_CR_DATA_OUT, val);
+
+	regmap_write(priv->regmap, PHY_R4, 0);
+
+	ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+				       !(val & PHY_R5_PHY_CR_ACK),
+				       5, 1000);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int phy_g12a_usb3_pcie_cr_bus_write(void *context, unsigned int addr,
+					   unsigned int data)
+{
+	struct phy_g12a_usb3_pcie_priv *priv = context;
+	unsigned int val, reg;
+	int ret;
+
+	ret = phy_g12a_usb3_pcie_cr_bus_addr(priv, addr);
+	if (ret)
+		return ret;
+
+	reg = FIELD_PREP(PHY_R4_PHY_CR_DATA_IN, data);
+
+	regmap_write(priv->regmap, PHY_R4, reg);
+	regmap_write(priv->regmap, PHY_R4, reg);
+
+	regmap_write(priv->regmap, PHY_R4, reg | PHY_R4_PHY_CR_CAP_DATA);
+
+	ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+				       (val & PHY_R5_PHY_CR_ACK),
+				       5, 1000);
+	if (ret)
+		return ret;
+
+	regmap_write(priv->regmap, PHY_R4, reg);
+
+	ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+				       (val & PHY_R5_PHY_CR_ACK) == 0,
+				       5, 1000);
+	if (ret)
+		return ret;
+
+	regmap_write(priv->regmap, PHY_R4, reg);
+
+	regmap_write(priv->regmap, PHY_R4, reg | PHY_R4_PHY_CR_WRITE);
+
+	ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+				       (val & PHY_R5_PHY_CR_ACK),
+				       5, 1000);
+	if (ret)
+		return ret;
+
+	regmap_write(priv->regmap, PHY_R4, reg);
+
+	ret = regmap_read_poll_timeout(priv->regmap, PHY_R5, val,
+				       (val & PHY_R5_PHY_CR_ACK) == 0,
+				       5, 1000);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static const struct regmap_config phy_g12a_usb3_pcie_cr_regmap_conf = {
+	.reg_bits = 16,
+	.val_bits = 16,
+	.reg_read = phy_g12a_usb3_pcie_cr_bus_read,
+	.reg_write = phy_g12a_usb3_pcie_cr_bus_write,
+	.max_register = 0xffff,
+	.fast_io = true,
+};
+
+static int phy_g12a_usb3_init(struct phy *phy)
+{
+	struct phy_g12a_usb3_pcie_priv *priv = phy_get_drvdata(phy);
+	int data, ret;
+
+	/* Switch PHY to USB3 */
+	/* TODO figure out how to handle when PCIe was set in the bootloader */
+	regmap_update_bits(priv->regmap, PHY_R0,
+			   PHY_R0_PCIE_USB3_SWITCH,
+			   PHY_R0_PCIE_USB3_SWITCH);
+
+	/*
+	 * WORKAROUND: There is SSPHY suspend bug due to
+	 * which USB enumerates
+	 * in HS mode instead of SS mode. Workaround it by asserting
+	 * LANE0.TX_ALT_BLOCK.EN_ALT_BUS to enable TX to use alt bus
+	 * mode
+	 */
+	ret = regmap_update_bits(priv->regmap_cr, 0x102d, BIT(7), BIT(7));
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(priv->regmap_cr, 0x1010, 0xff0, 20);
+	if (ret)
+		return ret;
+
+	/*
+	 * Fix RX Equalization setting as follows
+	 * LANE0.RX_OVRD_IN_HI. RX_EQ_EN set to 0
+	 * LANE0.RX_OVRD_IN_HI.RX_EQ_EN_OVRD set to 1
+	 * LANE0.RX_OVRD_IN_HI.RX_EQ set to 3
+	 * LANE0.RX_OVRD_IN_HI.RX_EQ_OVRD set to 1
+	 */
+	ret = regmap_read(priv->regmap_cr, 0x1006, &data);
+	if (ret)
+		return ret;
+
+	data &= ~BIT(6);
+	data |= BIT(7);
+	data &= ~(0x7 << 8);
+	data |= (0x3 << 8);
+	data |= (1 << 11);
+	ret = regmap_write(priv->regmap_cr, 0x1006, data);
+	if (ret)
+		return ret;
+
+	/*
+	 * Set EQ and TX launch amplitudes as follows
+	 * LANE0.TX_OVRD_DRV_LO.PREEMPH set to 22
+	 * LANE0.TX_OVRD_DRV_LO.AMPLITUDE set to 127
+	 * LANE0.TX_OVRD_DRV_LO.EN set to 1.
+	 */
+	ret = regmap_read(priv->regmap_cr, 0x1002, &data);
+	if (ret)
+		return ret;
+
+	data &= ~0x3f80;
+	data |= (0x16 << 7);
+	data &= ~0x7f;
+	data |= (0x7f | BIT(14));
+	ret = regmap_write(priv->regmap_cr, 0x1002, data);
+	if (ret)
+		return ret;
+
+	/* MPLL_LOOP_CTL.PROP_CNTRL = 8 */
+	ret = regmap_update_bits(priv->regmap_cr, 0x30, 0xf << 4, 8 << 4);
+	if (ret)
+		return ret;
+
+	regmap_update_bits(priv->regmap, PHY_R2,
+			PHY_R2_PHY_TX_VBOOST_LVL,
+			FIELD_PREP(PHY_R2_PHY_TX_VBOOST_LVL, 0x4));
+
+	regmap_update_bits(priv->regmap, PHY_R1,
+			PHY_R1_PHY_LOS_BIAS | PHY_R1_PHY_LOS_LEVEL,
+			FIELD_PREP(PHY_R1_PHY_LOS_BIAS, 4) |
+			FIELD_PREP(PHY_R1_PHY_LOS_LEVEL, 9));
+
+	return 0;
+}
+
+static int phy_g12a_usb3_pcie_init(struct phy *phy)
+{
+	struct phy_g12a_usb3_pcie_priv *priv = phy_get_drvdata(phy);
+	int ret;
+
+	ret = reset_control_reset(priv->reset);
+	if (ret)
+		return ret;
+
+	if (priv->mode == PHY_TYPE_USB3)
+		return phy_g12a_usb3_init(phy);
+
+	/* Power UP PCIE */
+	/* TODO figure out when the bootloader has set USB3 mode before */
+	regmap_update_bits(priv->regmap, PHY_R0,
+			   PHY_R0_PCIE_POWER_STATE,
+			   FIELD_PREP(PHY_R0_PCIE_POWER_STATE, 0x1c));
+
+	return 0;
+}
+
+static int phy_g12a_usb3_pcie_exit(struct phy *phy)
+{
+	struct phy_g12a_usb3_pcie_priv *priv = phy_get_drvdata(phy);
+
+	return reset_control_reset(priv->reset);
+}
+
+static struct phy *phy_g12a_usb3_pcie_xlate(struct device *dev,
+					    struct of_phandle_args *args)
+{
+	struct phy_g12a_usb3_pcie_priv *priv = dev_get_drvdata(dev);
+	unsigned int mode;
+
+	if (args->args_count < 1) {
+		dev_err(dev, "invalid number of arguments\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	mode = args->args[0];
+
+	if (mode != PHY_TYPE_USB3 && mode != PHY_TYPE_PCIE) {
+		dev_err(dev, "invalid phy mode select argument\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	priv->mode = mode;
+
+	return priv->phy;
+}
+
+static const struct phy_ops phy_g12a_usb3_pcie_ops = {
+	.init		= phy_g12a_usb3_pcie_init,
+	.exit		= phy_g12a_usb3_pcie_exit,
+	.owner		= THIS_MODULE,
+};
+
+static int phy_g12a_usb3_pcie_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct phy_g12a_usb3_pcie_priv *priv;
+	struct resource *res;
+	struct phy_provider *phy_provider;
+	void __iomem *base;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	priv->regmap = devm_regmap_init_mmio(dev, base,
+					     &phy_g12a_usb3_pcie_regmap_conf);
+	if (IS_ERR(priv->regmap))
+		return PTR_ERR(priv->regmap);
+
+	priv->regmap_cr = devm_regmap_init(dev, NULL, priv,
+					   &phy_g12a_usb3_pcie_cr_regmap_conf);
+	if (IS_ERR(priv->regmap_cr))
+		return PTR_ERR(priv->regmap_cr);
+
+	priv->clk_ref = devm_clk_get(dev, "ref_clk");
+	if (IS_ERR(priv->clk_ref))
+		return PTR_ERR(priv->clk_ref);
+
+	ret = clk_prepare_enable(priv->clk_ref);
+	if (ret)
+		goto err_disable_clk_ref;
+
+	priv->reset = devm_reset_control_array_get(dev, false, false);
+	if (IS_ERR(priv->reset))
+		return PTR_ERR(priv->reset);
+
+	priv->phy = devm_phy_create(dev, np, &phy_g12a_usb3_pcie_ops);
+	if (IS_ERR(priv->phy)) {
+		ret = PTR_ERR(priv->phy);
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev, "failed to create PHY\n");
+
+		return ret;
+	}
+
+	phy_set_drvdata(priv->phy, priv);
+	dev_set_drvdata(dev, priv);
+
+	phy_provider = devm_of_phy_provider_register(dev,
+						     phy_g12a_usb3_pcie_xlate);
+
+	return PTR_ERR_OR_ZERO(phy_provider);
+
+err_disable_clk_ref:
+	clk_disable_unprepare(priv->clk_ref);
+
+	return ret;
+}
+
+static const struct of_device_id phy_g12a_usb3_pcie_of_match[] = {
+	{ .compatible = "amlogic,g12a-usb3-pcie-phy", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, phy_g12a_usb3_pcie_of_match);
+
+static struct platform_driver phy_g12a_usb3_pcie_driver = {
+	.probe	= phy_g12a_usb3_pcie_probe,
+	.driver	= {
+		.name		= "phy-g12a-usb3-pcie",
+		.of_match_table	= phy_g12a_usb3_pcie_of_match,
+	},
+};
+module_platform_driver(phy_g12a_usb3_pcie_driver);
+
+MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
+MODULE_DESCRIPTION("Amlogic G12A USB3 + PCIE Combo PHY driver");
+MODULE_LICENSE("GPL v2");
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v3 7/8] usb: dwc2: Add Amlogic G12A DWC2 Params
  2019-03-18 10:11 [PATCH v3 0/8] arm64: meson: Add support for USB on Amlogic G12A Neil Armstrong
                   ` (5 preceding siblings ...)
  2019-03-18 10:11 ` [PATCH v3 6/8] phy: amlogic: Add Amlogic G12A USB3 + PCIE Combo " Neil Armstrong
@ 2019-03-18 10:11 ` Neil Armstrong
  2019-03-18 10:11 ` [PATCH v3 8/8] usb: dwc3: Add Amlogic G12A DWC3 glue Neil Armstrong
  2019-03-18 13:27 ` [PATCH v3 0/8] arm64: meson: Add support for USB on Amlogic G12A Neil Armstrong
  8 siblings, 0 replies; 16+ messages in thread
From: Neil Armstrong @ 2019-03-18 10:11 UTC (permalink / raw)
  To: gregkh, hminas, balbi, kishon
  Cc: Neil Armstrong, linux-amlogic, linux-usb, linux-arm-kernel, linux-kernel

This patchs sets the params for the DWC2 Controller found in the
Amlogic G12A SoC family.

It mainly sets the settings reported incorrect by the driver,
leaving the remaining detected automatically by the driver and
provided by the DT node.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
 drivers/usb/dwc2/params.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/drivers/usb/dwc2/params.c b/drivers/usb/dwc2/params.c
index 24ff5f21cb25..442113246cba 100644
--- a/drivers/usb/dwc2/params.c
+++ b/drivers/usb/dwc2/params.c
@@ -121,6 +121,16 @@ static void dwc2_set_amlogic_params(struct dwc2_hsotg *hsotg)
 	p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
 }
 
+static void dwc2_set_amlogic_g12a_params(struct dwc2_hsotg *hsotg)
+{
+	struct dwc2_core_params *p = &hsotg->params;
+
+	p->lpm = false;
+	p->lpm_clock_gating = false;
+	p->besl = false;
+	p->hird_threshold_en = false;
+}
+
 static void dwc2_set_amcc_params(struct dwc2_hsotg *hsotg)
 {
 	struct dwc2_core_params *p = &hsotg->params;
@@ -167,6 +177,8 @@ const struct of_device_id dwc2_of_match_table[] = {
 	  .data = dwc2_set_amlogic_params },
 	{ .compatible = "amlogic,meson-gxbb-usb",
 	  .data = dwc2_set_amlogic_params },
+	{ .compatible = "amlogic,meson-g12a-usb",
+	  .data = dwc2_set_amlogic_g12a_params },
 	{ .compatible = "amcc,dwc-otg", .data = dwc2_set_amcc_params },
 	{ .compatible = "st,stm32f4x9-fsotg",
 	  .data = dwc2_set_stm32f4x9_fsotg_params },
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH v3 8/8] usb: dwc3: Add Amlogic G12A DWC3 glue
  2019-03-18 10:11 [PATCH v3 0/8] arm64: meson: Add support for USB on Amlogic G12A Neil Armstrong
                   ` (6 preceding siblings ...)
  2019-03-18 10:11 ` [PATCH v3 7/8] usb: dwc2: Add Amlogic G12A DWC2 Params Neil Armstrong
@ 2019-03-18 10:11 ` Neil Armstrong
  2019-03-19  1:57   ` Chunfeng Yun
  2019-03-18 13:27 ` [PATCH v3 0/8] arm64: meson: Add support for USB on Amlogic G12A Neil Armstrong
  8 siblings, 1 reply; 16+ messages in thread
From: Neil Armstrong @ 2019-03-18 10:11 UTC (permalink / raw)
  To: gregkh, hminas, balbi, kishon
  Cc: Neil Armstrong, linux-amlogic, linux-usb, linux-arm-kernel, linux-kernel

Adds support for Amlogic G12A USB Control Glue HW.

The Amlogic G12A SoC Family embeds 2 USB Controllers :
- a DWC3 IP configured as Host for USB2 and USB3
- a DWC2 IP configured as Peripheral USB2 Only

A glue connects these both controllers to 2 USB2 PHYs, and optionnally
to an USB3+PCIE Combo PHY shared with the PCIE controller.

The Glue configures the UTMI 8bit interfaces for the USB2 PHYs, including
routing of the OTG PHY between the DWC3 and DWC2 controllers, and
setups the on-chip OTG mode selection for this PHY.

This drivers supports the on-probe setup of the OTG mode, and manually
via a debugfs interface. The IRQ mode change detect is yet to be added
in a future patchset, mainly due to lack of hardware to validate on.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
 drivers/usb/dwc3/Kconfig           |  10 +
 drivers/usb/dwc3/Makefile          |   1 +
 drivers/usb/dwc3/dwc3-meson-g12a.c | 602 +++++++++++++++++++++++++++++
 3 files changed, 613 insertions(+)
 create mode 100644 drivers/usb/dwc3/dwc3-meson-g12a.c

diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index 2b1494460d0c..d2ea9670563c 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -95,6 +95,16 @@ config USB_DWC3_KEYSTONE
 	  Support of USB2/3 functionality in TI Keystone2 and AM654 platforms.
 	  Say 'Y' or 'M' here if you have one such device
 
+config USB_DWC3_MESON_G12A
+       tristate "Amlogic Meson G12A Platforms"
+       depends on OF && COMMON_CLK
+       depends on ARCH_MESON || COMPILE_TEST
+       default USB_DWC3
+       select USB_ROLE_SWITCH
+       help
+         Support USB2/3 functionality in Amlogic G12A platforms.
+	 Say 'Y' or 'M' if you have one such device.
+
 config USB_DWC3_OF_SIMPLE
        tristate "Generic OF Simple Glue Layer"
        depends on OF && COMMON_CLK
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index 6e3ef6144e5d..ae86da0dc5bd 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -47,6 +47,7 @@ obj-$(CONFIG_USB_DWC3_EXYNOS)		+= dwc3-exynos.o
 obj-$(CONFIG_USB_DWC3_PCI)		+= dwc3-pci.o
 obj-$(CONFIG_USB_DWC3_HAPS)		+= dwc3-haps.o
 obj-$(CONFIG_USB_DWC3_KEYSTONE)		+= dwc3-keystone.o
+obj-$(CONFIG_USB_DWC3_MESON_G12A)	+= dwc3-meson-g12a.o
 obj-$(CONFIG_USB_DWC3_OF_SIMPLE)	+= dwc3-of-simple.o
 obj-$(CONFIG_USB_DWC3_ST)		+= dwc3-st.o
 obj-$(CONFIG_USB_DWC3_QCOM)		+= dwc3-qcom.o
diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c
new file mode 100644
index 000000000000..9f4554e8e6e7
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-meson-g12a.c
@@ -0,0 +1,602 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USB Glue for Amlogic G12A SoCs
+ *
+ * Copyright (c) 2019 BayLibre, SAS
+ * Author: Neil Armstrong <narmstrong@baylibre.com>
+ */
+
+/*
+ * The USB is organized with a glue around the DWC3 Controller IP as :
+ * - Control registers for each USB2 Ports
+ * - Control registers for the USB PHY layer
+ * - SuperSpeed PHY can be enabled only if port is used
+ *
+ * TOFIX:
+ * - Add dynamic OTG switching with ID change interrupt
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/reset.h>
+#include <linux/phy/phy.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/role.h>
+#include <linux/regulator/consumer.h>
+
+/* USB2 Ports Control Registers */
+
+#define U2P_REG_SIZE						0x20
+
+#define U2P_R0							0x0
+	#define U2P_R0_HOST_DEVICE				BIT(0)
+	#define U2P_R0_POWER_OK					BIT(1)
+	#define U2P_R0_HAST_MODE				BIT(2)
+	#define U2P_R0_POWER_ON_RESET				BIT(3)
+	#define U2P_R0_ID_PULLUP				BIT(4)
+	#define U2P_R0_DRV_VBUS					BIT(5)
+
+#define U2P_R1							0x4
+	#define U2P_R1_PHY_READY				BIT(0)
+	#define U2P_R1_ID_DIG					BIT(1)
+	#define U2P_R1_OTG_SESSION_VALID			BIT(2)
+	#define U2P_R1_VBUS_VALID				BIT(3)
+
+/* USB Glue Control Registers */
+
+#define USB_R0							0x80
+	#define USB_R0_P30_LANE0_TX2RX_LOOPBACK			BIT(17)
+	#define USB_R0_P30_LANE0_EXT_PCLK_REQ			BIT(18)
+	#define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK		GENMASK(28, 19)
+	#define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK		GENMASK(30, 29)
+	#define USB_R0_U2D_ACT					BIT(31)
+
+#define USB_R1							0x84
+	#define USB_R1_U3H_BIGENDIAN_GS				BIT(0)
+	#define USB_R1_U3H_PME_ENABLE				BIT(1)
+	#define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK		GENMASK(4, 2)
+	#define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK		GENMASK(9, 7)
+	#define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK		GENMASK(13, 12)
+	#define USB_R1_U3H_HOST_U3_PORT_DISABLE			BIT(16)
+	#define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT	BIT(17)
+	#define USB_R1_U3H_HOST_MSI_ENABLE			BIT(18)
+	#define USB_R1_U3H_FLADJ_30MHZ_REG_MASK			GENMASK(24, 19)
+	#define USB_R1_P30_PCS_TX_SWING_FULL_MASK		GENMASK(31, 25)
+
+#define USB_R2							0x88
+	#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK		GENMASK(25, 20)
+	#define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK		GENMASK(31, 26)
+
+#define USB_R3							0x8c
+	#define USB_R3_P30_SSC_ENABLE				BIT(0)
+	#define USB_R3_P30_SSC_RANGE_MASK			GENMASK(3, 1)
+	#define USB_R3_P30_SSC_REF_CLK_SEL_MASK			GENMASK(12, 4)
+	#define USB_R3_P30_REF_SSP_EN				BIT(13)
+
+#define USB_R4							0x90
+	#define USB_R4_P21_PORT_RESET_0				BIT(0)
+	#define USB_R4_P21_SLEEP_M0				BIT(1)
+	#define USB_R4_MEM_PD_MASK				GENMASK(3, 2)
+	#define USB_R4_P21_ONLY					BIT(4)
+
+#define USB_R5							0x94
+	#define USB_R5_ID_DIG_SYNC				BIT(0)
+	#define USB_R5_ID_DIG_REG				BIT(1)
+	#define USB_R5_ID_DIG_CFG_MASK				GENMASK(3, 2)
+	#define USB_R5_ID_DIG_EN_0				BIT(4)
+	#define USB_R5_ID_DIG_EN_1				BIT(5)
+	#define USB_R5_ID_DIG_CURR				BIT(6)
+	#define USB_R5_ID_DIG_IRQ				BIT(7)
+	#define USB_R5_ID_DIG_TH_MASK				GENMASK(15, 8)
+	#define USB_R5_ID_DIG_CNT_MASK				GENMASK(23, 16)
+
+enum {
+	USB2_HOST_PHY = 0,
+	USB2_OTG_PHY,
+	USB3_HOST_PHY,
+	PHY_COUNT,
+};
+
+static const char *phy_names[PHY_COUNT] = {
+	"usb2-phy0", "usb2-phy1", "usb3-phy0",
+};
+
+struct dwc3_meson_g12a {
+	struct device		*dev;
+	struct regmap		*regmap;
+	struct clk		*clk;
+	struct reset_control	*reset;
+	struct phy		*phys[PHY_COUNT];
+	enum usb_dr_mode	otg_mode;
+	enum phy_mode		otg_phy_mode;
+	unsigned int		usb2_ports;
+	unsigned int		usb3_ports;
+	struct regulator	*vbus;
+	struct usb_role_switch_desc switch_desc;
+	struct usb_role_switch	*role_switch;
+};
+
+static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv,
+					  int i, enum phy_mode mode)
+{
+	if (mode == PHY_MODE_USB_HOST)
+		regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
+				U2P_R0_HOST_DEVICE,
+				U2P_R0_HOST_DEVICE);
+	else
+		regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
+				U2P_R0_HOST_DEVICE, 0);
+}
+
+static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv)
+{
+	int i;
+
+	if (priv->otg_mode == USB_DR_MODE_PERIPHERAL)
+		priv->otg_phy_mode = PHY_MODE_USB_DEVICE;
+	else
+		priv->otg_phy_mode = PHY_MODE_USB_HOST;
+
+	for (i = 0 ; i < USB3_HOST_PHY ; ++i) {
+		if (!priv->phys[i])
+			continue;
+
+		regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
+				   U2P_R0_POWER_ON_RESET,
+				   U2P_R0_POWER_ON_RESET);
+
+		if (i == USB2_OTG_PHY) {
+			regmap_update_bits(priv->regmap,
+				U2P_R0 + (U2P_REG_SIZE * i),
+				U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS,
+				U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS);
+
+			dwc3_meson_g12a_usb2_set_mode(priv, i,
+						      priv->otg_phy_mode);
+		} else
+			dwc3_meson_g12a_usb2_set_mode(priv, i,
+						      PHY_MODE_USB_HOST);
+
+		regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
+				   U2P_R0_POWER_ON_RESET, 0);
+	}
+
+	return 0;
+}
+
+static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv)
+{
+	regmap_update_bits(priv->regmap, USB_R3,
+			USB_R3_P30_SSC_RANGE_MASK |
+			USB_R3_P30_REF_SSP_EN,
+			USB_R3_P30_SSC_ENABLE |
+			FIELD_PREP(USB_R3_P30_SSC_RANGE_MASK, 2) |
+			USB_R3_P30_REF_SSP_EN);
+	udelay(2);
+
+	regmap_update_bits(priv->regmap, USB_R2,
+			USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK,
+			FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, 0x15));
+
+	regmap_update_bits(priv->regmap, USB_R2,
+			USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK,
+			FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, 0x20));
+
+	udelay(2);
+
+	regmap_update_bits(priv->regmap, USB_R1,
+			USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT,
+			USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT);
+
+	regmap_update_bits(priv->regmap, USB_R1,
+			USB_R1_P30_PCS_TX_SWING_FULL_MASK,
+			FIELD_PREP(USB_R1_P30_PCS_TX_SWING_FULL_MASK, 127));
+}
+
+static void dwc3_meson_g12a_usb_otg_apply_mode(struct dwc3_meson_g12a *priv)
+{
+	if (priv->otg_phy_mode == PHY_MODE_USB_DEVICE) {
+		regmap_update_bits(priv->regmap, USB_R0,
+				USB_R0_U2D_ACT, USB_R0_U2D_ACT);
+		regmap_update_bits(priv->regmap, USB_R0,
+				USB_R0_U2D_SS_SCALEDOWN_MODE_MASK, 0);
+		regmap_update_bits(priv->regmap, USB_R4,
+				USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0);
+	} else {
+		regmap_update_bits(priv->regmap, USB_R0,
+				USB_R0_U2D_ACT, 0);
+		regmap_update_bits(priv->regmap, USB_R4,
+				USB_R4_P21_SLEEP_M0, 0);
+	}
+}
+
+static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv)
+{
+	int ret;
+
+	ret = dwc3_meson_g12a_usb2_init(priv);
+	if (ret)
+		return ret;
+
+	regmap_update_bits(priv->regmap, USB_R1,
+			USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
+			FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20));
+
+	regmap_update_bits(priv->regmap, USB_R5,
+			USB_R5_ID_DIG_EN_0,
+			USB_R5_ID_DIG_EN_0);
+	regmap_update_bits(priv->regmap, USB_R5,
+			USB_R5_ID_DIG_EN_1,
+			USB_R5_ID_DIG_EN_1);
+	regmap_update_bits(priv->regmap, USB_R5,
+			USB_R5_ID_DIG_TH_MASK,
+			FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff));
+
+	/* If we have an actual SuperSpeed port, initialize it */
+	if (priv->usb3_ports)
+		dwc3_meson_g12a_usb3_init(priv);
+
+	dwc3_meson_g12a_usb_otg_apply_mode(priv);
+
+	return 0;
+}
+
+static const struct regmap_config phy_meson_g12a_usb3_regmap_conf = {
+	.reg_bits = 8,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.max_register = USB_R5,
+};
+
+static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv)
+{
+	int i;
+
+	for (i = 0 ; i < PHY_COUNT ; ++i) {
+		priv->phys[i] = devm_phy_optional_get(priv->dev, phy_names[i]);
+		if (!priv->phys[i])
+			continue;
+
+		if (IS_ERR(priv->phys[i]))
+			return PTR_ERR(priv->phys[i]);
+
+		if (i == USB3_HOST_PHY)
+			priv->usb3_ports++;
+		else
+			priv->usb2_ports++;
+	}
+
+	dev_info(priv->dev, "USB2 ports: %d\n", priv->usb2_ports);
+	dev_info(priv->dev, "USB3 ports: %d\n", priv->usb3_ports);
+
+	return 0;
+}
+
+static enum phy_mode dwc3_meson_g12a_get_id(struct dwc3_meson_g12a *priv)
+{
+	u32 reg;
+
+	regmap_read(priv->regmap, USB_R5, &reg);
+
+	if (reg & (USB_R5_ID_DIG_SYNC | USB_R5_ID_DIG_REG))
+		return PHY_MODE_USB_DEVICE;
+
+	return PHY_MODE_USB_HOST;
+}
+
+static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv,
+					enum phy_mode mode)
+{
+	int ret;
+
+	if (!priv->phys[USB2_OTG_PHY])
+		return -EINVAL;
+
+	if (mode == PHY_MODE_USB_HOST)
+		dev_info(priv->dev, "switching to Host Mode\n");
+	else
+		dev_info(priv->dev, "switching to Device Mode\n");
+
+	if (priv->vbus) {
+		if (mode == PHY_MODE_USB_DEVICE)
+			ret = regulator_disable(priv->vbus);
+		else
+			ret = regulator_enable(priv->vbus);
+		if (ret)
+			return ret;
+	}
+
+	priv->otg_phy_mode = mode;
+
+	dwc3_meson_g12a_usb2_set_mode(priv, USB2_OTG_PHY, mode);
+
+	dwc3_meson_g12a_usb_otg_apply_mode(priv);
+
+	return 0;
+}
+
+static int dwc3_meson_g12a_role_set(struct device *dev, enum usb_role role)
+{
+	struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
+	enum phy_mode mode;
+
+	if (role == USB_ROLE_NONE)
+		return 0;
+
+	mode = (role == USB_ROLE_HOST) ? PHY_MODE_USB_HOST
+				       : PHY_MODE_USB_DEVICE;
+
+	if (mode == priv->otg_phy_mode)
+		return 0;
+
+	return dwc3_meson_g12a_otg_mode_set(priv, mode);
+}
+
+static enum usb_role dwc3_meson_g12a_role_get(struct device *dev)
+{
+	struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
+
+	return priv->otg_phy_mode == PHY_MODE_USB_HOST ?
+		USB_ROLE_HOST : USB_ROLE_DEVICE;
+}
+
+static struct device *dwc3_meson_g12_find_child(struct device *dev,
+						const char *compatible)
+{
+	struct platform_device *pdev;
+	struct device_node *np;
+
+	np = of_get_compatible_child(dev->of_node, compatible);
+	if (!np)
+		return NULL;
+
+	pdev = of_find_device_by_node(np);
+	of_node_put(np);
+	if (!pdev)
+		return NULL;
+
+	return &pdev->dev;
+}
+
+static int dwc3_meson_g12a_probe(struct platform_device *pdev)
+{
+	struct dwc3_meson_g12a	*priv;
+	struct device		*dev = &pdev->dev;
+	struct device_node	*np = dev->of_node;
+	void __iomem *base;
+	struct resource *res;
+	enum phy_mode otg_id;
+	int ret, i;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	priv->regmap = devm_regmap_init_mmio(dev, base,
+					     &phy_meson_g12a_usb3_regmap_conf);
+	if (IS_ERR(priv->regmap))
+		return PTR_ERR(priv->regmap);
+
+	priv->vbus = devm_regulator_get_optional(dev, "vbus");
+	if (IS_ERR(priv->vbus)) {
+		if (PTR_ERR(priv->vbus) == -EPROBE_DEFER)
+			return PTR_ERR(priv->vbus);
+		priv->vbus = NULL;
+	}
+
+	priv->clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(priv->clk))
+		return PTR_ERR(priv->clk);
+
+	ret = clk_prepare_enable(priv->clk);
+	if (ret)
+		return ret;
+
+	devm_add_action_or_reset(dev,
+				 (void(*)(void *))clk_disable_unprepare,
+				 priv->clk);
+
+	platform_set_drvdata(pdev, priv);
+	priv->dev = dev;
+
+	priv->reset = devm_reset_control_get(dev, NULL);
+	if (IS_ERR(priv->reset)) {
+		ret = PTR_ERR(priv->reset);
+		dev_err(dev, "failed to get device reset, err=%d\n", ret);
+		return ret;
+	}
+
+	ret = reset_control_reset(priv->reset);
+	if (ret)
+		return ret;
+
+	ret = dwc3_meson_g12a_get_phys(priv);
+	if (ret)
+		return ret;
+
+	if (priv->vbus) {
+		ret = regulator_enable(priv->vbus);
+		if (ret)
+			return ret;
+	}
+
+	/* Get dr_mode */
+	priv->otg_mode = usb_get_dr_mode(dev);
+
+	dwc3_meson_g12a_usb_init(priv);
+
+	/* Init PHYs */
+	for (i = 0 ; i < PHY_COUNT ; ++i) {
+		ret = phy_init(priv->phys[i]);
+		if (ret)
+			goto err_phys_power;
+	}
+
+	/* Set PHY Power */
+	for (i = 0 ; i < PHY_COUNT ; ++i) {
+		ret = phy_power_on(priv->phys[i]);
+		if (ret)
+			goto err_phys_put;
+	}
+
+	ret = of_platform_populate(np, NULL, NULL, dev);
+	if (ret) {
+		clk_disable_unprepare(priv->clk);
+		clk_put(priv->clk);
+
+		goto err_phys_exit;
+	}
+
+	/* Setup OTG mode corresponding to the ID pin */
+	if (priv->otg_mode == USB_DR_MODE_OTG) {
+		/* TOFIX Handle ID mode toggling via IRQ */
+		otg_id = dwc3_meson_g12a_get_id(priv);
+		if (otg_id != priv->otg_phy_mode) {
+			if (dwc3_meson_g12a_otg_mode_set(priv, otg_id))
+				dev_warn(dev, "Failed to switch OTG mode\n");
+		}
+	}
+
+	/* Setup role switcher */
+	priv->switch_desc.usb2_port = dwc3_meson_g12_find_child(dev,
+								"snps,dwc3");
+	priv->switch_desc.udc = dwc3_meson_g12_find_child(dev, "snps,dwc2");
+	priv->switch_desc.allow_userspace_control = true;
+	priv->switch_desc.set = dwc3_meson_g12a_role_set;
+	priv->switch_desc.get = dwc3_meson_g12a_role_get;
+
+	priv->role_switch = usb_role_switch_register(dev, &priv->switch_desc);
+	if (IS_ERR(priv->role_switch))
+		dev_warn(dev, "Unable to register Role Switch\n");
+
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+	pm_runtime_get_sync(dev);
+
+	return 0;
+
+err_phys_exit:
+	for (i = 0 ; i < PHY_COUNT ; ++i)
+		phy_exit(priv->phys[i]);
+
+err_phys_power:
+	for (i = 0 ; i < PHY_COUNT ; ++i)
+		phy_power_off(priv->phys[i]);
+
+err_phys_put:
+	for (i = 0 ; i < PHY_COUNT ; ++i)
+		phy_put(priv->phys[i]);
+
+	return ret;
+}
+
+static int dwc3_meson_g12a_remove(struct platform_device *pdev)
+{
+	struct dwc3_meson_g12a *priv = platform_get_drvdata(pdev);
+	struct device *dev = &pdev->dev;
+	int i;
+
+	usb_role_switch_unregister(priv->role_switch);
+
+	of_platform_depopulate(dev);
+
+	for (i = 0 ; i < PHY_COUNT ; ++i) {
+		phy_power_off(priv->phys[i]);
+		phy_exit(priv->phys[i]);
+		phy_put(priv->phys[i]);
+	}
+
+	pm_runtime_disable(dev);
+	pm_runtime_put_noidle(dev);
+	pm_runtime_set_suspended(dev);
+
+	return 0;
+}
+
+static int __maybe_unused dwc3_meson_g12a_runtime_suspend(struct device *dev)
+{
+	struct dwc3_meson_g12a	*priv = dev_get_drvdata(dev);
+
+	clk_disable(priv->clk);
+
+	return 0;
+}
+
+static int __maybe_unused dwc3_meson_g12a_runtime_resume(struct device *dev)
+{
+	struct dwc3_meson_g12a	*priv = dev_get_drvdata(dev);
+
+	return clk_enable(priv->clk);
+}
+
+static int __maybe_unused dwc3_meson_g12a_suspend(struct device *dev)
+{
+	struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
+	int i;
+
+	for (i = 0 ; i < PHY_COUNT ; ++i)
+		phy_exit(priv->phys[i]);
+
+	reset_control_assert(priv->reset);
+
+	return 0;
+}
+
+static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev)
+{
+	struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
+	int i, ret;
+
+	reset_control_deassert(priv->reset);
+
+	dwc3_meson_g12a_usb_init(priv);
+
+	/* Init PHYs */
+	for (i = 0 ; i < PHY_COUNT ; ++i) {
+		ret = phy_init(priv->phys[i]);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(dwc3_meson_g12a_suspend, dwc3_meson_g12a_resume)
+	SET_RUNTIME_PM_OPS(dwc3_meson_g12a_runtime_suspend,
+			dwc3_meson_g12a_runtime_resume, NULL)
+};
+
+static const struct of_device_id dwc3_meson_g12a_match[] = {
+	{ .compatible = "amlogic,meson-g12a-usb-ctrl" },
+	{ /* Sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, dwc3_meson_g12a_match);
+
+static struct platform_driver dwc3_meson_g12a_driver = {
+	.probe		= dwc3_meson_g12a_probe,
+	.remove		= dwc3_meson_g12a_remove,
+	.driver		= {
+		.name	= "dwc3-meson-g12a",
+		.of_match_table = dwc3_meson_g12a_match,
+		.pm	= &dwc3_meson_g12a_dev_pm_ops,
+	},
+};
+
+module_platform_driver(dwc3_meson_g12a_driver);
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Amlogic Meson G12A USB Glue Layer");
+MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
-- 
2.20.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 5/8] phy: amlogic: add Amlogic G12A USB2 PHY Driver
  2019-03-18 10:11 ` [PATCH v3 5/8] phy: amlogic: add Amlogic G12A USB2 PHY Driver Neil Armstrong
@ 2019-03-18 10:18   ` Kishon Vijay Abraham I
  2019-03-18 11:28     ` Neil Armstrong
  0 siblings, 1 reply; 16+ messages in thread
From: Kishon Vijay Abraham I @ 2019-03-18 10:18 UTC (permalink / raw)
  To: Neil Armstrong, gregkh, hminas, balbi
  Cc: linux-amlogic, linux-usb, linux-arm-kernel, linux-kernel,
	Martin Blumenstingl

Hi,

On 18/03/19 3:41 PM, Neil Armstrong wrote:
> This adds support for the USB2 PHY found in the Amlogic G12A SoC Family.
> 
> It supports Host and/or Peripheral mode, depending on it's position.
> The first PHY is only used as Host, but the second supports Dual modes
> defined by the USB Control Glue HW in front of the USB Controllers.
> 
> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
> Reviewed-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
> ---
>  drivers/phy/amlogic/Kconfig               |  11 ++
>  drivers/phy/amlogic/Makefile              |   1 +
>  drivers/phy/amlogic/phy-meson-g12a-usb2.c | 189 ++++++++++++++++++++++
>  3 files changed, 201 insertions(+)
>  create mode 100644 drivers/phy/amlogic/phy-meson-g12a-usb2.c
> 
> diff --git a/drivers/phy/amlogic/Kconfig b/drivers/phy/amlogic/Kconfig
> index 23fe1cda2f70..560ff0f1ed4c 100644
> --- a/drivers/phy/amlogic/Kconfig
> +++ b/drivers/phy/amlogic/Kconfig
> @@ -36,3 +36,14 @@ config PHY_MESON_GXL_USB3
>  	  Enable this to support the Meson USB3 PHY and OTG detection
>  	  IP block found in Meson GXL and GXM SoCs.
>  	  If unsure, say N.
> +
> +config PHY_MESON_G12A_USB2
> +	tristate "Meson G12A USB2 PHY driver"
> +	default ARCH_MESON
> +	depends on OF && (ARCH_MESON || COMPILE_TEST)
> +	select GENERIC_PHY
> +	select REGMAP_MMIO
> +	help
> +	  Enable this to support the Meson USB2 PHYs found in Meson
> +	  G12A SoCs.
> +	  If unsure, say N.
> diff --git a/drivers/phy/amlogic/Makefile b/drivers/phy/amlogic/Makefile
> index 4fd8848c194d..7d4d10f5a6b3 100644
> --- a/drivers/phy/amlogic/Makefile
> +++ b/drivers/phy/amlogic/Makefile
> @@ -1,3 +1,4 @@
>  obj-$(CONFIG_PHY_MESON8B_USB2)		+= phy-meson8b-usb2.o
>  obj-$(CONFIG_PHY_MESON_GXL_USB2)	+= phy-meson-gxl-usb2.o
> +obj-$(CONFIG_PHY_MESON_G12A_USB2)	+= phy-meson-g12a-usb2.o
>  obj-$(CONFIG_PHY_MESON_GXL_USB3)	+= phy-meson-gxl-usb3.o
> diff --git a/drivers/phy/amlogic/phy-meson-g12a-usb2.c b/drivers/phy/amlogic/phy-meson-g12a-usb2.c
> new file mode 100644
> index 000000000000..9f35259f237f
> --- /dev/null
> +++ b/drivers/phy/amlogic/phy-meson-g12a-usb2.c
> @@ -0,0 +1,189 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Meson G12A USB2 PHY driver
> + *
> + * Copyright (C) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
> + * Copyright (C) 2017 Amlogic, Inc. All rights reserved
> + * Copyright (C) 2019 BayLibre, SAS
> + * Author: Neil Armstrong <narmstrong@baylibre.com>
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/regmap.h>
> +#include <linux/reset.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +
> +#define PHY_CTRL_R0						0x0
> +#define PHY_CTRL_R1						0x4
> +#define PHY_CTRL_R2						0x8
> +#define PHY_CTRL_R3						0xc
> +#define PHY_CTRL_R4						0x10
> +#define PHY_CTRL_R5						0x14
> +#define PHY_CTRL_R6						0x18
> +#define PHY_CTRL_R7						0x1c
> +#define PHY_CTRL_R8						0x20
> +#define PHY_CTRL_R9						0x24
> +#define PHY_CTRL_R10						0x28
> +#define PHY_CTRL_R11						0x2c
> +#define PHY_CTRL_R12						0x30
> +#define PHY_CTRL_R13						0x34
> +#define PHY_CTRL_R14						0x38
> +#define PHY_CTRL_R15						0x3c
> +#define PHY_CTRL_R16						0x40
> +#define PHY_CTRL_R17						0x44
> +#define PHY_CTRL_R18						0x48
> +#define PHY_CTRL_R19						0x4c
> +#define PHY_CTRL_R20						0x50
> +#define PHY_CTRL_R21						0x54
> +#define PHY_CTRL_R22						0x58
> +#define PHY_CTRL_R23						0x5c
> +
> +#define RESET_COMPLETE_TIME					1000
> +#define PLL_RESET_COMPLETE_TIME					100
> +
> +struct phy_meson_g12a_usb2_priv {
> +	struct device		*dev;
> +	struct regmap		*regmap;
> +	struct clk		*clk;
> +	struct reset_control	*reset;
> +};
> +
> +static const struct regmap_config phy_meson_g12a_usb2_regmap_conf = {
> +	.reg_bits = 8,
> +	.val_bits = 32,
> +	.reg_stride = 4,
> +	.max_register = PHY_CTRL_R23,
> +};
> +
> +static int phy_meson_g12a_usb2_init(struct phy *phy)
> +{
> +	struct phy_meson_g12a_usb2_priv *priv = phy_get_drvdata(phy);
> +	int ret;
> +
> +	ret = reset_control_reset(priv->reset);
> +	if (ret)
> +		return ret;
> +
> +	udelay(RESET_COMPLETE_TIME);
> +
> +	/* usb2_otg_aca_en == 0 */
> +	regmap_update_bits(priv->regmap, PHY_CTRL_R21, BIT(2), 0);
> +
> +	/* PLL Setup : 24MHz * 20 / 1 = 480MHz */
> +	regmap_write(priv->regmap, PHY_CTRL_R16, 0x39400414);
> +	regmap_write(priv->regmap, PHY_CTRL_R17, 0x927e0000);
> +	regmap_write(priv->regmap, PHY_CTRL_R18, 0xac5f49e5);

Please add macros for each of the individual bit-fields.
> +
> +	udelay(PLL_RESET_COMPLETE_TIME);
> +
> +	/* UnReset PLL */
> +	regmap_write(priv->regmap, PHY_CTRL_R16, 0x19400414);

Here too..

Thanks
Kishon

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 5/8] phy: amlogic: add Amlogic G12A USB2 PHY Driver
  2019-03-18 10:18   ` Kishon Vijay Abraham I
@ 2019-03-18 11:28     ` Neil Armstrong
  0 siblings, 0 replies; 16+ messages in thread
From: Neil Armstrong @ 2019-03-18 11:28 UTC (permalink / raw)
  To: Kishon Vijay Abraham I, gregkh, hminas, balbi
  Cc: linux-amlogic, linux-usb, linux-arm-kernel, linux-kernel,
	Martin Blumenstingl

On 18/03/2019 11:18, Kishon Vijay Abraham I wrote:
> Hi,
> 
> On 18/03/19 3:41 PM, Neil Armstrong wrote:
>> This adds support for the USB2 PHY found in the Amlogic G12A SoC Family.
>>
>> It supports Host and/or Peripheral mode, depending on it's position.
>> The first PHY is only used as Host, but the second supports Dual modes
>> defined by the USB Control Glue HW in front of the USB Controllers.
>>
>> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
>> Reviewed-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
>> ---
>>  drivers/phy/amlogic/Kconfig               |  11 ++
>>  drivers/phy/amlogic/Makefile              |   1 +
>>  drivers/phy/amlogic/phy-meson-g12a-usb2.c | 189 ++++++++++++++++++++++
>>  3 files changed, 201 insertions(+)
>>  create mode 100644 drivers/phy/amlogic/phy-meson-g12a-usb2.c
>>
>> diff --git a/drivers/phy/amlogic/Kconfig b/drivers/phy/amlogic/Kconfig
>> index 23fe1cda2f70..560ff0f1ed4c 100644
>> --- a/drivers/phy/amlogic/Kconfig
>> +++ b/drivers/phy/amlogic/Kconfig
>> @@ -36,3 +36,14 @@ config PHY_MESON_GXL_USB3
>>  	  Enable this to support the Meson USB3 PHY and OTG detection
>>  	  IP block found in Meson GXL and GXM SoCs.
>>  	  If unsure, say N.
>> +
>> +config PHY_MESON_G12A_USB2
>> +	tristate "Meson G12A USB2 PHY driver"
>> +	default ARCH_MESON
>> +	depends on OF && (ARCH_MESON || COMPILE_TEST)
>> +	select GENERIC_PHY
>> +	select REGMAP_MMIO
>> +	help
>> +	  Enable this to support the Meson USB2 PHYs found in Meson
>> +	  G12A SoCs.
>> +	  If unsure, say N.
>> diff --git a/drivers/phy/amlogic/Makefile b/drivers/phy/amlogic/Makefile
>> index 4fd8848c194d..7d4d10f5a6b3 100644
>> --- a/drivers/phy/amlogic/Makefile
>> +++ b/drivers/phy/amlogic/Makefile
>> @@ -1,3 +1,4 @@
>>  obj-$(CONFIG_PHY_MESON8B_USB2)		+= phy-meson8b-usb2.o
>>  obj-$(CONFIG_PHY_MESON_GXL_USB2)	+= phy-meson-gxl-usb2.o
>> +obj-$(CONFIG_PHY_MESON_G12A_USB2)	+= phy-meson-g12a-usb2.o
>>  obj-$(CONFIG_PHY_MESON_GXL_USB3)	+= phy-meson-gxl-usb3.o
>> diff --git a/drivers/phy/amlogic/phy-meson-g12a-usb2.c b/drivers/phy/amlogic/phy-meson-g12a-usb2.c
>> new file mode 100644
>> index 000000000000..9f35259f237f
>> --- /dev/null
>> +++ b/drivers/phy/amlogic/phy-meson-g12a-usb2.c
>> @@ -0,0 +1,189 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Meson G12A USB2 PHY driver
>> + *
>> + * Copyright (C) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
>> + * Copyright (C) 2017 Amlogic, Inc. All rights reserved
>> + * Copyright (C) 2019 BayLibre, SAS
>> + * Author: Neil Armstrong <narmstrong@baylibre.com>
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/of_device.h>
>> +#include <linux/regmap.h>
>> +#include <linux/reset.h>
>> +#include <linux/phy/phy.h>
>> +#include <linux/platform_device.h>
>> +
>> +#define PHY_CTRL_R0						0x0
>> +#define PHY_CTRL_R1						0x4
>> +#define PHY_CTRL_R2						0x8
>> +#define PHY_CTRL_R3						0xc
>> +#define PHY_CTRL_R4						0x10
>> +#define PHY_CTRL_R5						0x14
>> +#define PHY_CTRL_R6						0x18
>> +#define PHY_CTRL_R7						0x1c
>> +#define PHY_CTRL_R8						0x20
>> +#define PHY_CTRL_R9						0x24
>> +#define PHY_CTRL_R10						0x28
>> +#define PHY_CTRL_R11						0x2c
>> +#define PHY_CTRL_R12						0x30
>> +#define PHY_CTRL_R13						0x34
>> +#define PHY_CTRL_R14						0x38
>> +#define PHY_CTRL_R15						0x3c
>> +#define PHY_CTRL_R16						0x40
>> +#define PHY_CTRL_R17						0x44
>> +#define PHY_CTRL_R18						0x48
>> +#define PHY_CTRL_R19						0x4c
>> +#define PHY_CTRL_R20						0x50
>> +#define PHY_CTRL_R21						0x54
>> +#define PHY_CTRL_R22						0x58
>> +#define PHY_CTRL_R23						0x5c
>> +
>> +#define RESET_COMPLETE_TIME					1000
>> +#define PLL_RESET_COMPLETE_TIME					100
>> +
>> +struct phy_meson_g12a_usb2_priv {
>> +	struct device		*dev;
>> +	struct regmap		*regmap;
>> +	struct clk		*clk;
>> +	struct reset_control	*reset;
>> +};
>> +
>> +static const struct regmap_config phy_meson_g12a_usb2_regmap_conf = {
>> +	.reg_bits = 8,
>> +	.val_bits = 32,
>> +	.reg_stride = 4,
>> +	.max_register = PHY_CTRL_R23,
>> +};
>> +
>> +static int phy_meson_g12a_usb2_init(struct phy *phy)
>> +{
>> +	struct phy_meson_g12a_usb2_priv *priv = phy_get_drvdata(phy);
>> +	int ret;
>> +
>> +	ret = reset_control_reset(priv->reset);
>> +	if (ret)
>> +		return ret;
>> +
>> +	udelay(RESET_COMPLETE_TIME);
>> +
>> +	/* usb2_otg_aca_en == 0 */
>> +	regmap_update_bits(priv->regmap, PHY_CTRL_R21, BIT(2), 0);
>> +
>> +	/* PLL Setup : 24MHz * 20 / 1 = 480MHz */
>> +	regmap_write(priv->regmap, PHY_CTRL_R16, 0x39400414);
>> +	regmap_write(priv->regmap, PHY_CTRL_R17, 0x927e0000);
>> +	regmap_write(priv->regmap, PHY_CTRL_R18, 0xac5f49e5);
> 
> Please add macros for each of the individual bit-fields.

Will resent with this added.

Neil

>> +
>> +	udelay(PLL_RESET_COMPLETE_TIME);
>> +
>> +	/* UnReset PLL */
>> +	regmap_write(priv->regmap, PHY_CTRL_R16, 0x19400414);
> 
> Here too..
> 
> Thanks
> Kishon
> 


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 0/8] arm64: meson: Add support for USB on Amlogic G12A
  2019-03-18 10:11 [PATCH v3 0/8] arm64: meson: Add support for USB on Amlogic G12A Neil Armstrong
                   ` (7 preceding siblings ...)
  2019-03-18 10:11 ` [PATCH v3 8/8] usb: dwc3: Add Amlogic G12A DWC3 glue Neil Armstrong
@ 2019-03-18 13:27 ` Neil Armstrong
  2019-03-18 19:41   ` Martin Blumenstingl
  8 siblings, 1 reply; 16+ messages in thread
From: Neil Armstrong @ 2019-03-18 13:27 UTC (permalink / raw)
  To: gregkh, hminas, balbi, kishon
  Cc: linux-amlogic, linux-usb, linux-arm-kernel, linux-kernel

On 18/03/2019 11:11, Neil Armstrong wrote:
> This patchset adds support for USB on Amlogic G12A SoCs.
> 
> This patchset is composed with :
> - bindings of the PHYs
> - bindings of the USB Control Glue
> - PHY Drivers
> - USB Control Glue driver
> 
> Device Tree nodes will be added in a separate patchset.
> 
> The Amlogic G12A USB Complex is composed of :
> - 2 USB Controllers :
>  * DWC3 for USB2 and USB3 Host functionality
>  * DWC2 for USB2 Peripheral functionality
> - 2 USB2 OTG PHYs, only a single one will be routed to either DWC2 to DWC3
> - 1 USB3 PHY shared with PCIE funcionnality
> - A Glue to control PHY routing, setup and OTG detection
> 
> The Glue configures the UTMI 8bit interfaces for the USB2 PHYs, including
> routing of the OTG PHY between the DWC3 and DWC2 controllers, and
> setups the on-chip OTG mode selection for this PHY.
> 
> This drivers supports the on-probe setup of the OTG mode, and manually
> via the USB Role interface. The IRQ mode change detect is yet to be added
> in a future patchset, mainly due to lack of hardware to validate on.
> 

Resent with USB2 PHY registers documented

Neil

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 0/8] arm64: meson: Add support for USB on Amlogic G12A
  2019-03-18 13:27 ` [PATCH v3 0/8] arm64: meson: Add support for USB on Amlogic G12A Neil Armstrong
@ 2019-03-18 19:41   ` Martin Blumenstingl
  0 siblings, 0 replies; 16+ messages in thread
From: Martin Blumenstingl @ 2019-03-18 19:41 UTC (permalink / raw)
  To: Neil Armstrong
  Cc: gregkh, hminas, balbi, kishon, linux-amlogic, linux-usb,
	linux-kernel, linux-arm-kernel

Hi Neil,

On Mon, Mar 18, 2019 at 2:29 PM Neil Armstrong <narmstrong@baylibre.com> wrote:
>
> On 18/03/2019 11:11, Neil Armstrong wrote:
> > This patchset adds support for USB on Amlogic G12A SoCs.
> >
> > This patchset is composed with :
> > - bindings of the PHYs
> > - bindings of the USB Control Glue
> > - PHY Drivers
> > - USB Control Glue driver
> >
> > Device Tree nodes will be added in a separate patchset.
> >
> > The Amlogic G12A USB Complex is composed of :
> > - 2 USB Controllers :
> >  * DWC3 for USB2 and USB3 Host functionality
> >  * DWC2 for USB2 Peripheral functionality
> > - 2 USB2 OTG PHYs, only a single one will be routed to either DWC2 to DWC3
> > - 1 USB3 PHY shared with PCIE funcionnality
> > - A Glue to control PHY routing, setup and OTG detection
> >
> > The Glue configures the UTMI 8bit interfaces for the USB2 PHYs, including
> > routing of the OTG PHY between the DWC3 and DWC2 controllers, and
> > setups the on-chip OTG mode selection for this PHY.
> >
> > This drivers supports the on-probe setup of the OTG mode, and manually
> > via the USB Role interface. The IRQ mode change detect is yet to be added
> > in a future patchset, mainly due to lack of hardware to validate on.
> >
>
> Resent with USB2 PHY registers documented
awesome, thank you very much for this!


Regards
Martin

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 8/8] usb: dwc3: Add Amlogic G12A DWC3 glue
  2019-03-18 10:11 ` [PATCH v3 8/8] usb: dwc3: Add Amlogic G12A DWC3 glue Neil Armstrong
@ 2019-03-19  1:57   ` Chunfeng Yun
  2019-03-19  8:15     ` Neil Armstrong
  0 siblings, 1 reply; 16+ messages in thread
From: Chunfeng Yun @ 2019-03-19  1:57 UTC (permalink / raw)
  To: Neil Armstrong
  Cc: gregkh, hminas, balbi, kishon, linux-amlogic, linux-usb,
	linux-arm-kernel, linux-kernel

Hi Neil,
On Mon, 2019-03-18 at 11:11 +0100, Neil Armstrong wrote:
> Adds support for Amlogic G12A USB Control Glue HW.
> 
> The Amlogic G12A SoC Family embeds 2 USB Controllers :
> - a DWC3 IP configured as Host for USB2 and USB3
> - a DWC2 IP configured as Peripheral USB2 Only
> 
> A glue connects these both controllers to 2 USB2 PHYs, and optionnally
> to an USB3+PCIE Combo PHY shared with the PCIE controller.
> 
> The Glue configures the UTMI 8bit interfaces for the USB2 PHYs, including
> routing of the OTG PHY between the DWC3 and DWC2 controllers, and
> setups the on-chip OTG mode selection for this PHY.
> 
> This drivers supports the on-probe setup of the OTG mode, and manually
> via a debugfs interface. The IRQ mode change detect is yet to be added
> in a future patchset, mainly due to lack of hardware to validate on.
> 
> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
> ---
>  drivers/usb/dwc3/Kconfig           |  10 +
>  drivers/usb/dwc3/Makefile          |   1 +
>  drivers/usb/dwc3/dwc3-meson-g12a.c | 602 +++++++++++++++++++++++++++++
>  3 files changed, 613 insertions(+)
>  create mode 100644 drivers/usb/dwc3/dwc3-meson-g12a.c
> 
> diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
> index 2b1494460d0c..d2ea9670563c 100644
> --- a/drivers/usb/dwc3/Kconfig
> +++ b/drivers/usb/dwc3/Kconfig
> @@ -95,6 +95,16 @@ config USB_DWC3_KEYSTONE
>  	  Support of USB2/3 functionality in TI Keystone2 and AM654 platforms.
>  	  Say 'Y' or 'M' here if you have one such device
>  
> +config USB_DWC3_MESON_G12A
> +       tristate "Amlogic Meson G12A Platforms"
> +       depends on OF && COMMON_CLK
> +       depends on ARCH_MESON || COMPILE_TEST
> +       default USB_DWC3
> +       select USB_ROLE_SWITCH
> +       help
> +         Support USB2/3 functionality in Amlogic G12A platforms.
> +	 Say 'Y' or 'M' if you have one such device.
> +
>  config USB_DWC3_OF_SIMPLE
>         tristate "Generic OF Simple Glue Layer"
>         depends on OF && COMMON_CLK
> diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
> index 6e3ef6144e5d..ae86da0dc5bd 100644
> --- a/drivers/usb/dwc3/Makefile
> +++ b/drivers/usb/dwc3/Makefile
> @@ -47,6 +47,7 @@ obj-$(CONFIG_USB_DWC3_EXYNOS)		+= dwc3-exynos.o
>  obj-$(CONFIG_USB_DWC3_PCI)		+= dwc3-pci.o
>  obj-$(CONFIG_USB_DWC3_HAPS)		+= dwc3-haps.o
>  obj-$(CONFIG_USB_DWC3_KEYSTONE)		+= dwc3-keystone.o
> +obj-$(CONFIG_USB_DWC3_MESON_G12A)	+= dwc3-meson-g12a.o
>  obj-$(CONFIG_USB_DWC3_OF_SIMPLE)	+= dwc3-of-simple.o
>  obj-$(CONFIG_USB_DWC3_ST)		+= dwc3-st.o
>  obj-$(CONFIG_USB_DWC3_QCOM)		+= dwc3-qcom.o
> diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c
> new file mode 100644
> index 000000000000..9f4554e8e6e7
> --- /dev/null
> +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c
> @@ -0,0 +1,602 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * USB Glue for Amlogic G12A SoCs
> + *
> + * Copyright (c) 2019 BayLibre, SAS
> + * Author: Neil Armstrong <narmstrong@baylibre.com>
> + */
> +
> +/*
> + * The USB is organized with a glue around the DWC3 Controller IP as :
> + * - Control registers for each USB2 Ports
> + * - Control registers for the USB PHY layer
> + * - SuperSpeed PHY can be enabled only if port is used
> + *
> + * TOFIX:
> + * - Add dynamic OTG switching with ID change interrupt
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/platform_device.h>
> +#include <linux/clk.h>
> +#include <linux/of.h>
> +#include <linux/of_platform.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
> +#include <linux/bitfield.h>
> +#include <linux/bitops.h>
> +#include <linux/reset.h>
> +#include <linux/phy/phy.h>
> +#include <linux/usb/otg.h>
> +#include <linux/usb/role.h>
> +#include <linux/regulator/consumer.h>
> +
> +/* USB2 Ports Control Registers */
> +
> +#define U2P_REG_SIZE						0x20
> +
> +#define U2P_R0							0x0
> +	#define U2P_R0_HOST_DEVICE				BIT(0)
> +	#define U2P_R0_POWER_OK					BIT(1)
> +	#define U2P_R0_HAST_MODE				BIT(2)
> +	#define U2P_R0_POWER_ON_RESET				BIT(3)
> +	#define U2P_R0_ID_PULLUP				BIT(4)
> +	#define U2P_R0_DRV_VBUS					BIT(5)
> +
> +#define U2P_R1							0x4
> +	#define U2P_R1_PHY_READY				BIT(0)
> +	#define U2P_R1_ID_DIG					BIT(1)
> +	#define U2P_R1_OTG_SESSION_VALID			BIT(2)
> +	#define U2P_R1_VBUS_VALID				BIT(3)
> +
> +/* USB Glue Control Registers */
> +
> +#define USB_R0							0x80
> +	#define USB_R0_P30_LANE0_TX2RX_LOOPBACK			BIT(17)
> +	#define USB_R0_P30_LANE0_EXT_PCLK_REQ			BIT(18)
> +	#define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK		GENMASK(28, 19)
> +	#define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK		GENMASK(30, 29)
> +	#define USB_R0_U2D_ACT					BIT(31)
> +
> +#define USB_R1							0x84
> +	#define USB_R1_U3H_BIGENDIAN_GS				BIT(0)
> +	#define USB_R1_U3H_PME_ENABLE				BIT(1)
> +	#define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK		GENMASK(4, 2)
> +	#define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK		GENMASK(9, 7)
> +	#define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK		GENMASK(13, 12)
> +	#define USB_R1_U3H_HOST_U3_PORT_DISABLE			BIT(16)
> +	#define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT	BIT(17)
> +	#define USB_R1_U3H_HOST_MSI_ENABLE			BIT(18)
> +	#define USB_R1_U3H_FLADJ_30MHZ_REG_MASK			GENMASK(24, 19)
> +	#define USB_R1_P30_PCS_TX_SWING_FULL_MASK		GENMASK(31, 25)
> +
> +#define USB_R2							0x88
> +	#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK		GENMASK(25, 20)
> +	#define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK		GENMASK(31, 26)
> +
> +#define USB_R3							0x8c
> +	#define USB_R3_P30_SSC_ENABLE				BIT(0)
> +	#define USB_R3_P30_SSC_RANGE_MASK			GENMASK(3, 1)
> +	#define USB_R3_P30_SSC_REF_CLK_SEL_MASK			GENMASK(12, 4)
> +	#define USB_R3_P30_REF_SSP_EN				BIT(13)
> +
> +#define USB_R4							0x90
> +	#define USB_R4_P21_PORT_RESET_0				BIT(0)
> +	#define USB_R4_P21_SLEEP_M0				BIT(1)
> +	#define USB_R4_MEM_PD_MASK				GENMASK(3, 2)
> +	#define USB_R4_P21_ONLY					BIT(4)
> +
> +#define USB_R5							0x94
> +	#define USB_R5_ID_DIG_SYNC				BIT(0)
> +	#define USB_R5_ID_DIG_REG				BIT(1)
> +	#define USB_R5_ID_DIG_CFG_MASK				GENMASK(3, 2)
> +	#define USB_R5_ID_DIG_EN_0				BIT(4)
> +	#define USB_R5_ID_DIG_EN_1				BIT(5)
> +	#define USB_R5_ID_DIG_CURR				BIT(6)
> +	#define USB_R5_ID_DIG_IRQ				BIT(7)
> +	#define USB_R5_ID_DIG_TH_MASK				GENMASK(15, 8)
> +	#define USB_R5_ID_DIG_CNT_MASK				GENMASK(23, 16)
> +
> +enum {
> +	USB2_HOST_PHY = 0,
> +	USB2_OTG_PHY,
> +	USB3_HOST_PHY,
> +	PHY_COUNT,
> +};
> +
> +static const char *phy_names[PHY_COUNT] = {
> +	"usb2-phy0", "usb2-phy1", "usb3-phy0",
> +};
> +
> +struct dwc3_meson_g12a {
> +	struct device		*dev;
> +	struct regmap		*regmap;
> +	struct clk		*clk;
> +	struct reset_control	*reset;
> +	struct phy		*phys[PHY_COUNT];
> +	enum usb_dr_mode	otg_mode;
> +	enum phy_mode		otg_phy_mode;
> +	unsigned int		usb2_ports;
> +	unsigned int		usb3_ports;
> +	struct regulator	*vbus;
> +	struct usb_role_switch_desc switch_desc;
> +	struct usb_role_switch	*role_switch;
> +};
> +
> +static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv,
> +					  int i, enum phy_mode mode)
> +{
> +	if (mode == PHY_MODE_USB_HOST)
> +		regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
> +				U2P_R0_HOST_DEVICE,
> +				U2P_R0_HOST_DEVICE);
> +	else
> +		regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
> +				U2P_R0_HOST_DEVICE, 0);
> +}
> +
> +static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv)
> +{
> +	int i;
> +
> +	if (priv->otg_mode == USB_DR_MODE_PERIPHERAL)
> +		priv->otg_phy_mode = PHY_MODE_USB_DEVICE;
> +	else
> +		priv->otg_phy_mode = PHY_MODE_USB_HOST;
> +
> +	for (i = 0 ; i < USB3_HOST_PHY ; ++i) {
> +		if (!priv->phys[i])
> +			continue;
> +
> +		regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
> +				   U2P_R0_POWER_ON_RESET,
> +				   U2P_R0_POWER_ON_RESET);
> +
> +		if (i == USB2_OTG_PHY) {
> +			regmap_update_bits(priv->regmap,
> +				U2P_R0 + (U2P_REG_SIZE * i),
> +				U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS,
> +				U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS);
> +
> +			dwc3_meson_g12a_usb2_set_mode(priv, i,
> +						      priv->otg_phy_mode);
> +		} else
> +			dwc3_meson_g12a_usb2_set_mode(priv, i,
> +						      PHY_MODE_USB_HOST);
> +
> +		regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
> +				   U2P_R0_POWER_ON_RESET, 0);
> +	}
> +
> +	return 0;
> +}
> +
> +static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv)
> +{
> +	regmap_update_bits(priv->regmap, USB_R3,
> +			USB_R3_P30_SSC_RANGE_MASK |
> +			USB_R3_P30_REF_SSP_EN,
> +			USB_R3_P30_SSC_ENABLE |
> +			FIELD_PREP(USB_R3_P30_SSC_RANGE_MASK, 2) |
> +			USB_R3_P30_REF_SSP_EN);
> +	udelay(2);
> +
> +	regmap_update_bits(priv->regmap, USB_R2,
> +			USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK,
> +			FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, 0x15));
> +
> +	regmap_update_bits(priv->regmap, USB_R2,
> +			USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK,
> +			FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, 0x20));
> +
> +	udelay(2);
> +
> +	regmap_update_bits(priv->regmap, USB_R1,
> +			USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT,
> +			USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT);
> +
> +	regmap_update_bits(priv->regmap, USB_R1,
> +			USB_R1_P30_PCS_TX_SWING_FULL_MASK,
> +			FIELD_PREP(USB_R1_P30_PCS_TX_SWING_FULL_MASK, 127));
> +}
> +
> +static void dwc3_meson_g12a_usb_otg_apply_mode(struct dwc3_meson_g12a *priv)
> +{
> +	if (priv->otg_phy_mode == PHY_MODE_USB_DEVICE) {
> +		regmap_update_bits(priv->regmap, USB_R0,
> +				USB_R0_U2D_ACT, USB_R0_U2D_ACT);
> +		regmap_update_bits(priv->regmap, USB_R0,
> +				USB_R0_U2D_SS_SCALEDOWN_MODE_MASK, 0);
> +		regmap_update_bits(priv->regmap, USB_R4,
> +				USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0);
> +	} else {
> +		regmap_update_bits(priv->regmap, USB_R0,
> +				USB_R0_U2D_ACT, 0);
> +		regmap_update_bits(priv->regmap, USB_R4,
> +				USB_R4_P21_SLEEP_M0, 0);
> +	}
> +}
> +
> +static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv)
> +{
> +	int ret;
> +
> +	ret = dwc3_meson_g12a_usb2_init(priv);
> +	if (ret)
> +		return ret;
> +
> +	regmap_update_bits(priv->regmap, USB_R1,
> +			USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
> +			FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20));
> +
> +	regmap_update_bits(priv->regmap, USB_R5,
> +			USB_R5_ID_DIG_EN_0,
> +			USB_R5_ID_DIG_EN_0);
> +	regmap_update_bits(priv->regmap, USB_R5,
> +			USB_R5_ID_DIG_EN_1,
> +			USB_R5_ID_DIG_EN_1);
> +	regmap_update_bits(priv->regmap, USB_R5,
> +			USB_R5_ID_DIG_TH_MASK,
> +			FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff));
> +
> +	/* If we have an actual SuperSpeed port, initialize it */
> +	if (priv->usb3_ports)
> +		dwc3_meson_g12a_usb3_init(priv);
> +
> +	dwc3_meson_g12a_usb_otg_apply_mode(priv);
> +
> +	return 0;
> +}
> +
> +static const struct regmap_config phy_meson_g12a_usb3_regmap_conf = {
> +	.reg_bits = 8,
> +	.val_bits = 32,
> +	.reg_stride = 4,
> +	.max_register = USB_R5,
> +};
> +
> +static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv)
> +{
> +	int i;
> +
> +	for (i = 0 ; i < PHY_COUNT ; ++i) {
> +		priv->phys[i] = devm_phy_optional_get(priv->dev, phy_names[i]);
> +		if (!priv->phys[i])
> +			continue;
> +
> +		if (IS_ERR(priv->phys[i]))
> +			return PTR_ERR(priv->phys[i]);
> +
> +		if (i == USB3_HOST_PHY)
> +			priv->usb3_ports++;
> +		else
> +			priv->usb2_ports++;
> +	}
> +
> +	dev_info(priv->dev, "USB2 ports: %d\n", priv->usb2_ports);
> +	dev_info(priv->dev, "USB3 ports: %d\n", priv->usb3_ports);
> +
> +	return 0;
> +}
> +
> +static enum phy_mode dwc3_meson_g12a_get_id(struct dwc3_meson_g12a *priv)
> +{
> +	u32 reg;
> +
> +	regmap_read(priv->regmap, USB_R5, &reg);
> +
> +	if (reg & (USB_R5_ID_DIG_SYNC | USB_R5_ID_DIG_REG))
> +		return PHY_MODE_USB_DEVICE;
> +
> +	return PHY_MODE_USB_HOST;
> +}
> +
> +static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv,
> +					enum phy_mode mode)
> +{
> +	int ret;
> +
> +	if (!priv->phys[USB2_OTG_PHY])
> +		return -EINVAL;
> +
> +	if (mode == PHY_MODE_USB_HOST)
> +		dev_info(priv->dev, "switching to Host Mode\n");
> +	else
> +		dev_info(priv->dev, "switching to Device Mode\n");
> +
> +	if (priv->vbus) {
> +		if (mode == PHY_MODE_USB_DEVICE)
> +			ret = regulator_disable(priv->vbus);
> +		else
> +			ret = regulator_enable(priv->vbus);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	priv->otg_phy_mode = mode;
> +
> +	dwc3_meson_g12a_usb2_set_mode(priv, USB2_OTG_PHY, mode);
> +
> +	dwc3_meson_g12a_usb_otg_apply_mode(priv);
> +
> +	return 0;
> +}
> +
> +static int dwc3_meson_g12a_role_set(struct device *dev, enum usb_role role)
> +{
> +	struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
> +	enum phy_mode mode;
> +
> +	if (role == USB_ROLE_NONE)
> +		return 0;
> +
> +	mode = (role == USB_ROLE_HOST) ? PHY_MODE_USB_HOST
> +				       : PHY_MODE_USB_DEVICE;
> +
> +	if (mode == priv->otg_phy_mode)
> +		return 0;
> +
> +	return dwc3_meson_g12a_otg_mode_set(priv, mode);
> +}
> +
> +static enum usb_role dwc3_meson_g12a_role_get(struct device *dev)
> +{
> +	struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
> +
> +	return priv->otg_phy_mode == PHY_MODE_USB_HOST ?
> +		USB_ROLE_HOST : USB_ROLE_DEVICE;
> +}
> +
> +static struct device *dwc3_meson_g12_find_child(struct device *dev,
> +						const char *compatible)
> +{
> +	struct platform_device *pdev;
> +	struct device_node *np;
> +
> +	np = of_get_compatible_child(dev->of_node, compatible);
> +	if (!np)
> +		return NULL;
> +
> +	pdev = of_find_device_by_node(np);
> +	of_node_put(np);
> +	if (!pdev)
> +		return NULL;
> +
> +	return &pdev->dev;
> +}
> +
> +static int dwc3_meson_g12a_probe(struct platform_device *pdev)
> +{
> +	struct dwc3_meson_g12a	*priv;
> +	struct device		*dev = &pdev->dev;
> +	struct device_node	*np = dev->of_node;
> +	void __iomem *base;
> +	struct resource *res;
> +	enum phy_mode otg_id;
> +	int ret, i;
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	base = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(base))
> +		return PTR_ERR(base);
> +
> +	priv->regmap = devm_regmap_init_mmio(dev, base,
> +					     &phy_meson_g12a_usb3_regmap_conf);
> +	if (IS_ERR(priv->regmap))
> +		return PTR_ERR(priv->regmap);
> +
> +	priv->vbus = devm_regulator_get_optional(dev, "vbus");
> +	if (IS_ERR(priv->vbus)) {
> +		if (PTR_ERR(priv->vbus) == -EPROBE_DEFER)
> +			return PTR_ERR(priv->vbus);
> +		priv->vbus = NULL;
> +	}
> +
> +	priv->clk = devm_clk_get(dev, NULL);
> +	if (IS_ERR(priv->clk))
> +		return PTR_ERR(priv->clk);
> +
> +	ret = clk_prepare_enable(priv->clk);
> +	if (ret)
> +		return ret;
> +
> +	devm_add_action_or_reset(dev,
> +				 (void(*)(void *))clk_disable_unprepare,
> +				 priv->clk);
> +
> +	platform_set_drvdata(pdev, priv);
> +	priv->dev = dev;
> +
> +	priv->reset = devm_reset_control_get(dev, NULL);
> +	if (IS_ERR(priv->reset)) {
> +		ret = PTR_ERR(priv->reset);
> +		dev_err(dev, "failed to get device reset, err=%d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = reset_control_reset(priv->reset);
> +	if (ret)
> +		return ret;
> +
> +	ret = dwc3_meson_g12a_get_phys(priv);
> +	if (ret)
> +		return ret;
> +
> +	if (priv->vbus) {
> +		ret = regulator_enable(priv->vbus);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	/* Get dr_mode */
> +	priv->otg_mode = usb_get_dr_mode(dev);
> +
> +	dwc3_meson_g12a_usb_init(priv);
> +
> +	/* Init PHYs */
> +	for (i = 0 ; i < PHY_COUNT ; ++i) {
> +		ret = phy_init(priv->phys[i]);
> +		if (ret)
> +			goto err_phys_power;
> +	}
> +
> +	/* Set PHY Power */
> +	for (i = 0 ; i < PHY_COUNT ; ++i) {
> +		ret = phy_power_on(priv->phys[i]);
> +		if (ret)
> +			goto err_phys_put;
> +	}
> +
> +	ret = of_platform_populate(np, NULL, NULL, dev);
> +	if (ret) {
> +		clk_disable_unprepare(priv->clk);
> +		clk_put(priv->clk);
No need clk_put()
> +
> +		goto err_phys_exit;
> +	}
> +
> +	/* Setup OTG mode corresponding to the ID pin */
> +	if (priv->otg_mode == USB_DR_MODE_OTG) {
> +		/* TOFIX Handle ID mode toggling via IRQ */
> +		otg_id = dwc3_meson_g12a_get_id(priv);
> +		if (otg_id != priv->otg_phy_mode) {
> +			if (dwc3_meson_g12a_otg_mode_set(priv, otg_id))
> +				dev_warn(dev, "Failed to switch OTG mode\n");
> +		}
> +	}
> +
> +	/* Setup role switcher */
> +	priv->switch_desc.usb2_port = dwc3_meson_g12_find_child(dev,
> +								"snps,dwc3");
> +	priv->switch_desc.udc = dwc3_meson_g12_find_child(dev, "snps,dwc2");
> +	priv->switch_desc.allow_userspace_control = true;
> +	priv->switch_desc.set = dwc3_meson_g12a_role_set;
> +	priv->switch_desc.get = dwc3_meson_g12a_role_get;
> +
> +	priv->role_switch = usb_role_switch_register(dev, &priv->switch_desc);
> +	if (IS_ERR(priv->role_switch))
> +		dev_warn(dev, "Unable to register Role Switch\n");
> +
> +	pm_runtime_set_active(dev);
> +	pm_runtime_enable(dev);
> +	pm_runtime_get_sync(dev);
> +
> +	return 0;
> +
> +err_phys_exit:
> +	for (i = 0 ; i < PHY_COUNT ; ++i)
> +		phy_exit(priv->phys[i]);
> +
> +err_phys_power:
> +	for (i = 0 ; i < PHY_COUNT ; ++i)
> +		phy_power_off(priv->phys[i]);
> +
> +err_phys_put:
> +	for (i = 0 ; i < PHY_COUNT ; ++i)
> +		phy_put(priv->phys[i]);
you get phy by devm_phy_optional_get(), no need call phy_put(), 
> +
> +	return ret;
> +}
> +
> +static int dwc3_meson_g12a_remove(struct platform_device *pdev)
> +{
> +	struct dwc3_meson_g12a *priv = platform_get_drvdata(pdev);
> +	struct device *dev = &pdev->dev;
> +	int i;
> +
> +	usb_role_switch_unregister(priv->role_switch);
> +
> +	of_platform_depopulate(dev);
> +
> +	for (i = 0 ; i < PHY_COUNT ; ++i) {
> +		phy_power_off(priv->phys[i]);
> +		phy_exit(priv->phys[i]);
> +		phy_put(priv->phys[i]);
here, and remove others if have.
> +	}
> +
> +	pm_runtime_disable(dev);
> +	pm_runtime_put_noidle(dev);
> +	pm_runtime_set_suspended(dev);
> +
> +	return 0;
> +}
> +
> +static int __maybe_unused dwc3_meson_g12a_runtime_suspend(struct device *dev)
> +{
> +	struct dwc3_meson_g12a	*priv = dev_get_drvdata(dev);
> +
> +	clk_disable(priv->clk);
> +
> +	return 0;
> +}
> +
> +static int __maybe_unused dwc3_meson_g12a_runtime_resume(struct device *dev)
> +{
> +	struct dwc3_meson_g12a	*priv = dev_get_drvdata(dev);
> +
> +	return clk_enable(priv->clk);
> +}
> +
> +static int __maybe_unused dwc3_meson_g12a_suspend(struct device *dev)
> +{
> +	struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
> +	int i;
> +
> +	for (i = 0 ; i < PHY_COUNT ; ++i)
> +		phy_exit(priv->phys[i]);
> +
> +	reset_control_assert(priv->reset);
> +
> +	return 0;
> +}
> +
> +static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev)
> +{
> +	struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
> +	int i, ret;
> +
> +	reset_control_deassert(priv->reset);
> +
> +	dwc3_meson_g12a_usb_init(priv);
> +
> +	/* Init PHYs */
> +	for (i = 0 ; i < PHY_COUNT ; ++i) {
> +		ret = phy_init(priv->phys[i]);
> +		if (ret)
> +			return ret;
> +	}
Do you need power on phys?
> +
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = {
> +	SET_SYSTEM_SLEEP_PM_OPS(dwc3_meson_g12a_suspend, dwc3_meson_g12a_resume)
> +	SET_RUNTIME_PM_OPS(dwc3_meson_g12a_runtime_suspend,
> +			dwc3_meson_g12a_runtime_resume, NULL)
> +};
> +
> +static const struct of_device_id dwc3_meson_g12a_match[] = {
> +	{ .compatible = "amlogic,meson-g12a-usb-ctrl" },
> +	{ /* Sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, dwc3_meson_g12a_match);
> +
> +static struct platform_driver dwc3_meson_g12a_driver = {
> +	.probe		= dwc3_meson_g12a_probe,
> +	.remove		= dwc3_meson_g12a_remove,
> +	.driver		= {
> +		.name	= "dwc3-meson-g12a",
> +		.of_match_table = dwc3_meson_g12a_match,
> +		.pm	= &dwc3_meson_g12a_dev_pm_ops,
> +	},
> +};
> +
> +module_platform_driver(dwc3_meson_g12a_driver);
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("Amlogic Meson G12A USB Glue Layer");
> +MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 8/8] usb: dwc3: Add Amlogic G12A DWC3 glue
  2019-03-19  1:57   ` Chunfeng Yun
@ 2019-03-19  8:15     ` Neil Armstrong
  2019-03-20  1:43       ` Chunfeng Yun
  0 siblings, 1 reply; 16+ messages in thread
From: Neil Armstrong @ 2019-03-19  8:15 UTC (permalink / raw)
  To: Chunfeng Yun
  Cc: gregkh, hminas, balbi, kishon, linux-amlogic, linux-usb,
	linux-arm-kernel, linux-kernel

Hi,

On 19/03/2019 02:57, Chunfeng Yun wrote:
> Hi Neil,
> On Mon, 2019-03-18 at 11:11 +0100, Neil Armstrong wrote:
>> Adds support for Amlogic G12A USB Control Glue HW.
>>
>> The Amlogic G12A SoC Family embeds 2 USB Controllers :
>> - a DWC3 IP configured as Host for USB2 and USB3
>> - a DWC2 IP configured as Peripheral USB2 Only
>>
>> A glue connects these both controllers to 2 USB2 PHYs, and optionnally
>> to an USB3+PCIE Combo PHY shared with the PCIE controller.
>>
>> The Glue configures the UTMI 8bit interfaces for the USB2 PHYs, including
>> routing of the OTG PHY between the DWC3 and DWC2 controllers, and
>> setups the on-chip OTG mode selection for this PHY.
>>
>> This drivers supports the on-probe setup of the OTG mode, and manually
>> via a debugfs interface. The IRQ mode change detect is yet to be added
>> in a future patchset, mainly due to lack of hardware to validate on.
>>
>> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
>> ---
>>  drivers/usb/dwc3/Kconfig           |  10 +
>>  drivers/usb/dwc3/Makefile          |   1 +
>>  drivers/usb/dwc3/dwc3-meson-g12a.c | 602 +++++++++++++++++++++++++++++
>>  3 files changed, 613 insertions(+)
>>  create mode 100644 drivers/usb/dwc3/dwc3-meson-g12a.c
>>
>> diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
>> index 2b1494460d0c..d2ea9670563c 100644
>> --- a/drivers/usb/dwc3/Kconfig
>> +++ b/drivers/usb/dwc3/Kconfig
>> @@ -95,6 +95,16 @@ config USB_DWC3_KEYSTONE
>>  	  Support of USB2/3 functionality in TI Keystone2 and AM654 platforms.
>>  	  Say 'Y' or 'M' here if you have one such device
>>  
>> +config USB_DWC3_MESON_G12A
>> +       tristate "Amlogic Meson G12A Platforms"
>> +       depends on OF && COMMON_CLK
>> +       depends on ARCH_MESON || COMPILE_TEST
>> +       default USB_DWC3
>> +       select USB_ROLE_SWITCH
>> +       help
>> +         Support USB2/3 functionality in Amlogic G12A platforms.
>> +	 Say 'Y' or 'M' if you have one such device.
>> +
>>  config USB_DWC3_OF_SIMPLE
>>         tristate "Generic OF Simple Glue Layer"
>>         depends on OF && COMMON_CLK
>> diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
>> index 6e3ef6144e5d..ae86da0dc5bd 100644
>> --- a/drivers/usb/dwc3/Makefile
>> +++ b/drivers/usb/dwc3/Makefile
>> @@ -47,6 +47,7 @@ obj-$(CONFIG_USB_DWC3_EXYNOS)		+= dwc3-exynos.o
>>  obj-$(CONFIG_USB_DWC3_PCI)		+= dwc3-pci.o
>>  obj-$(CONFIG_USB_DWC3_HAPS)		+= dwc3-haps.o
>>  obj-$(CONFIG_USB_DWC3_KEYSTONE)		+= dwc3-keystone.o
>> +obj-$(CONFIG_USB_DWC3_MESON_G12A)	+= dwc3-meson-g12a.o
>>  obj-$(CONFIG_USB_DWC3_OF_SIMPLE)	+= dwc3-of-simple.o
>>  obj-$(CONFIG_USB_DWC3_ST)		+= dwc3-st.o
>>  obj-$(CONFIG_USB_DWC3_QCOM)		+= dwc3-qcom.o
>> diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c
>> new file mode 100644
>> index 000000000000..9f4554e8e6e7
>> --- /dev/null
>> +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c
>> @@ -0,0 +1,602 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * USB Glue for Amlogic G12A SoCs
>> + *
>> + * Copyright (c) 2019 BayLibre, SAS
>> + * Author: Neil Armstrong <narmstrong@baylibre.com>
>> + */
>> +
>> +/*
>> + * The USB is organized with a glue around the DWC3 Controller IP as :
>> + * - Control registers for each USB2 Ports
>> + * - Control registers for the USB PHY layer
>> + * - SuperSpeed PHY can be enabled only if port is used
>> + *
>> + * TOFIX:
>> + * - Add dynamic OTG switching with ID change interrupt
>> + */
>> +

[..]

>> +
>> +	ret = of_platform_populate(np, NULL, NULL, dev);
>> +	if (ret) {
>> +		clk_disable_unprepare(priv->clk);
>> +		clk_put(priv->clk);
> No need clk_put()

Exact

>> +
>> +		goto err_phys_exit;
>> +	}
>> +
>> +	/* Setup OTG mode corresponding to the ID pin */
>> +	if (priv->otg_mode == USB_DR_MODE_OTG) {
>> +		/* TOFIX Handle ID mode toggling via IRQ */
>> +		otg_id = dwc3_meson_g12a_get_id(priv);
>> +		if (otg_id != priv->otg_phy_mode) {
>> +			if (dwc3_meson_g12a_otg_mode_set(priv, otg_id))
>> +				dev_warn(dev, "Failed to switch OTG mode\n");
>> +		}
>> +	}
>> +
>> +	/* Setup role switcher */
>> +	priv->switch_desc.usb2_port = dwc3_meson_g12_find_child(dev,
>> +								"snps,dwc3");
>> +	priv->switch_desc.udc = dwc3_meson_g12_find_child(dev, "snps,dwc2");
>> +	priv->switch_desc.allow_userspace_control = true;
>> +	priv->switch_desc.set = dwc3_meson_g12a_role_set;
>> +	priv->switch_desc.get = dwc3_meson_g12a_role_get;
>> +
>> +	priv->role_switch = usb_role_switch_register(dev, &priv->switch_desc);
>> +	if (IS_ERR(priv->role_switch))
>> +		dev_warn(dev, "Unable to register Role Switch\n");
>> +
>> +	pm_runtime_set_active(dev);
>> +	pm_runtime_enable(dev);
>> +	pm_runtime_get_sync(dev);
>> +
>> +	return 0;
>> +
>> +err_phys_exit:
>> +	for (i = 0 ; i < PHY_COUNT ; ++i)
>> +		phy_exit(priv->phys[i]);
>> +
>> +err_phys_power:
>> +	for (i = 0 ; i < PHY_COUNT ; ++i)
>> +		phy_power_off(priv->phys[i]);
>> +
>> +err_phys_put:
>> +	for (i = 0 ; i < PHY_COUNT ; ++i)
>> +		phy_put(priv->phys[i]);
> you get phy by devm_phy_optional_get(), no need call phy_put(), 

Indeed, also forgot these

>> +
>> +	return ret;
>> +}
>> +
>> +static int dwc3_meson_g12a_remove(struct platform_device *pdev)
>> +{
>> +	struct dwc3_meson_g12a *priv = platform_get_drvdata(pdev);
>> +	struct device *dev = &pdev->dev;
>> +	int i;
>> +
>> +	usb_role_switch_unregister(priv->role_switch);
>> +
>> +	of_platform_depopulate(dev);
>> +
>> +	for (i = 0 ; i < PHY_COUNT ; ++i) {
>> +		phy_power_off(priv->phys[i]);
>> +		phy_exit(priv->phys[i]);
>> +		phy_put(priv->phys[i]);
> here, and remove others if have.

This is the last one I hope

>> +	}
>> +
>> +	pm_runtime_disable(dev);
>> +	pm_runtime_put_noidle(dev);
>> +	pm_runtime_set_suspended(dev);
>> +
>> +	return 0;
>> +}
>> +
>> +static int __maybe_unused dwc3_meson_g12a_runtime_suspend(struct device *dev)
>> +{
>> +	struct dwc3_meson_g12a	*priv = dev_get_drvdata(dev);
>> +
>> +	clk_disable(priv->clk);
>> +
>> +	return 0;
>> +}
>> +
>> +static int __maybe_unused dwc3_meson_g12a_runtime_resume(struct device *dev)
>> +{
>> +	struct dwc3_meson_g12a	*priv = dev_get_drvdata(dev);
>> +
>> +	return clk_enable(priv->clk);
>> +}
>> +
>> +static int __maybe_unused dwc3_meson_g12a_suspend(struct device *dev)
>> +{
>> +	struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
>> +	int i;
>> +
>> +	for (i = 0 ; i < PHY_COUNT ; ++i)
>> +		phy_exit(priv->phys[i]);
>> +
>> +	reset_control_assert(priv->reset);
>> +
>> +	return 0;
>> +}
>> +
>> +static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev)
>> +{
>> +	struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
>> +	int i, ret;
>> +
>> +	reset_control_deassert(priv->reset);
>> +
>> +	dwc3_meson_g12a_usb_init(priv);
>> +
>> +	/* Init PHYs */
>> +	for (i = 0 ; i < PHY_COUNT ; ++i) {
>> +		ret = phy_init(priv->phys[i]);
>> +		if (ret)
>> +			return ret;
>> +	}
> Do you need power on phys?

Regulators can be tied to the PHYs, should I disable power on suspend aswell ?

>> +
>> +	return 0;
>> +}
>> +
>> +static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = {
>> +	SET_SYSTEM_SLEEP_PM_OPS(dwc3_meson_g12a_suspend, dwc3_meson_g12a_resume)
>> +	SET_RUNTIME_PM_OPS(dwc3_meson_g12a_runtime_suspend,
>> +			dwc3_meson_g12a_runtime_resume, NULL)
>> +};
>> +
>> +static const struct of_device_id dwc3_meson_g12a_match[] = {
>> +	{ .compatible = "amlogic,meson-g12a-usb-ctrl" },
>> +	{ /* Sentinel */ }
>> +};
>> +MODULE_DEVICE_TABLE(of, dwc3_meson_g12a_match);
>> +
>> +static struct platform_driver dwc3_meson_g12a_driver = {
>> +	.probe		= dwc3_meson_g12a_probe,
>> +	.remove		= dwc3_meson_g12a_remove,
>> +	.driver		= {
>> +		.name	= "dwc3-meson-g12a",
>> +		.of_match_table = dwc3_meson_g12a_match,
>> +		.pm	= &dwc3_meson_g12a_dev_pm_ops,
>> +	},
>> +};
>> +
>> +module_platform_driver(dwc3_meson_g12a_driver);
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_DESCRIPTION("Amlogic Meson G12A USB Glue Layer");
>> +MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
> 
> 

Thanks,
Neil

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH v3 8/8] usb: dwc3: Add Amlogic G12A DWC3 glue
  2019-03-19  8:15     ` Neil Armstrong
@ 2019-03-20  1:43       ` Chunfeng Yun
  0 siblings, 0 replies; 16+ messages in thread
From: Chunfeng Yun @ 2019-03-20  1:43 UTC (permalink / raw)
  To: Neil Armstrong
  Cc: gregkh, hminas, balbi, kishon, linux-amlogic, linux-usb,
	linux-arm-kernel, linux-kernel

Hi,
On Tue, 2019-03-19 at 09:15 +0100, Neil Armstrong wrote:
> Hi,
> 
> On 19/03/2019 02:57, Chunfeng Yun wrote:
> > Hi Neil,
> > On Mon, 2019-03-18 at 11:11 +0100, Neil Armstrong wrote:
> >> Adds support for Amlogic G12A USB Control Glue HW.
> >>
> >> The Amlogic G12A SoC Family embeds 2 USB Controllers :
> >> - a DWC3 IP configured as Host for USB2 and USB3
> >> - a DWC2 IP configured as Peripheral USB2 Only
> >>
> >> A glue connects these both controllers to 2 USB2 PHYs, and optionnally
> >> to an USB3+PCIE Combo PHY shared with the PCIE controller.
> >>
> >> The Glue configures the UTMI 8bit interfaces for the USB2 PHYs, including
> >> routing of the OTG PHY between the DWC3 and DWC2 controllers, and
> >> setups the on-chip OTG mode selection for this PHY.
> >>
> >> This drivers supports the on-probe setup of the OTG mode, and manually
> >> via a debugfs interface. The IRQ mode change detect is yet to be added
> >> in a future patchset, mainly due to lack of hardware to validate on.
> >>
> >> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
> >> ---
> >>  drivers/usb/dwc3/Kconfig           |  10 +
> >>  drivers/usb/dwc3/Makefile          |   1 +
> >>  drivers/usb/dwc3/dwc3-meson-g12a.c | 602 +++++++++++++++++++++++++++++
> >>  3 files changed, 613 insertions(+)
> >>  create mode 100644 drivers/usb/dwc3/dwc3-meson-g12a.c
> >>
> >> diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
> >> index 2b1494460d0c..d2ea9670563c 100644
> >> --- a/drivers/usb/dwc3/Kconfig
> >> +++ b/drivers/usb/dwc3/Kconfig
> >> @@ -95,6 +95,16 @@ config USB_DWC3_KEYSTONE
> >>  	  Support of USB2/3 functionality in TI Keystone2 and AM654 platforms.
> >>  	  Say 'Y' or 'M' here if you have one such device
> >>  
> >> +config USB_DWC3_MESON_G12A
> >> +       tristate "Amlogic Meson G12A Platforms"
> >> +       depends on OF && COMMON_CLK
> >> +       depends on ARCH_MESON || COMPILE_TEST
> >> +       default USB_DWC3
> >> +       select USB_ROLE_SWITCH
> >> +       help
> >> +         Support USB2/3 functionality in Amlogic G12A platforms.
> >> +	 Say 'Y' or 'M' if you have one such device.
> >> +
> >>  config USB_DWC3_OF_SIMPLE
> >>         tristate "Generic OF Simple Glue Layer"
> >>         depends on OF && COMMON_CLK
> >> diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
> >> index 6e3ef6144e5d..ae86da0dc5bd 100644
> >> --- a/drivers/usb/dwc3/Makefile
> >> +++ b/drivers/usb/dwc3/Makefile
> >> @@ -47,6 +47,7 @@ obj-$(CONFIG_USB_DWC3_EXYNOS)		+= dwc3-exynos.o
> >>  obj-$(CONFIG_USB_DWC3_PCI)		+= dwc3-pci.o
> >>  obj-$(CONFIG_USB_DWC3_HAPS)		+= dwc3-haps.o
> >>  obj-$(CONFIG_USB_DWC3_KEYSTONE)		+= dwc3-keystone.o
> >> +obj-$(CONFIG_USB_DWC3_MESON_G12A)	+= dwc3-meson-g12a.o
> >>  obj-$(CONFIG_USB_DWC3_OF_SIMPLE)	+= dwc3-of-simple.o
> >>  obj-$(CONFIG_USB_DWC3_ST)		+= dwc3-st.o
> >>  obj-$(CONFIG_USB_DWC3_QCOM)		+= dwc3-qcom.o
> >> diff --git a/drivers/usb/dwc3/dwc3-meson-g12a.c b/drivers/usb/dwc3/dwc3-meson-g12a.c
> >> new file mode 100644
> >> index 000000000000..9f4554e8e6e7
> >> --- /dev/null
> >> +++ b/drivers/usb/dwc3/dwc3-meson-g12a.c
> >> @@ -0,0 +1,602 @@
> >> +// SPDX-License-Identifier: GPL-2.0
> >> +/*
> >> + * USB Glue for Amlogic G12A SoCs
> >> + *
> >> + * Copyright (c) 2019 BayLibre, SAS
> >> + * Author: Neil Armstrong <narmstrong@baylibre.com>
> >> + */
> >> +
> >> +/*
> >> + * The USB is organized with a glue around the DWC3 Controller IP as :
> >> + * - Control registers for each USB2 Ports
> >> + * - Control registers for the USB PHY layer
> >> + * - SuperSpeed PHY can be enabled only if port is used
> >> + *
> >> + * TOFIX:
> >> + * - Add dynamic OTG switching with ID change interrupt
> >> + */
> >> +
> 
> [..]
> 
> >> +
> >> +	ret = of_platform_populate(np, NULL, NULL, dev);
> >> +	if (ret) {
> >> +		clk_disable_unprepare(priv->clk);
> >> +		clk_put(priv->clk);
> > No need clk_put()
> 
> Exact
> 
> >> +
> >> +		goto err_phys_exit;
> >> +	}
> >> +
> >> +	/* Setup OTG mode corresponding to the ID pin */
> >> +	if (priv->otg_mode == USB_DR_MODE_OTG) {
> >> +		/* TOFIX Handle ID mode toggling via IRQ */
> >> +		otg_id = dwc3_meson_g12a_get_id(priv);
> >> +		if (otg_id != priv->otg_phy_mode) {
> >> +			if (dwc3_meson_g12a_otg_mode_set(priv, otg_id))
> >> +				dev_warn(dev, "Failed to switch OTG mode\n");
> >> +		}
> >> +	}
> >> +
> >> +	/* Setup role switcher */
> >> +	priv->switch_desc.usb2_port = dwc3_meson_g12_find_child(dev,
> >> +								"snps,dwc3");
> >> +	priv->switch_desc.udc = dwc3_meson_g12_find_child(dev, "snps,dwc2");
> >> +	priv->switch_desc.allow_userspace_control = true;
> >> +	priv->switch_desc.set = dwc3_meson_g12a_role_set;
> >> +	priv->switch_desc.get = dwc3_meson_g12a_role_get;
> >> +
> >> +	priv->role_switch = usb_role_switch_register(dev, &priv->switch_desc);
> >> +	if (IS_ERR(priv->role_switch))
> >> +		dev_warn(dev, "Unable to register Role Switch\n");
> >> +
> >> +	pm_runtime_set_active(dev);
> >> +	pm_runtime_enable(dev);
> >> +	pm_runtime_get_sync(dev);
> >> +
> >> +	return 0;
> >> +
> >> +err_phys_exit:
> >> +	for (i = 0 ; i < PHY_COUNT ; ++i)
> >> +		phy_exit(priv->phys[i]);
> >> +
> >> +err_phys_power:
> >> +	for (i = 0 ; i < PHY_COUNT ; ++i)
> >> +		phy_power_off(priv->phys[i]);
> >> +
> >> +err_phys_put:
> >> +	for (i = 0 ; i < PHY_COUNT ; ++i)
> >> +		phy_put(priv->phys[i]);
> > you get phy by devm_phy_optional_get(), no need call phy_put(), 
> 
> Indeed, also forgot these
> 
> >> +
> >> +	return ret;
> >> +}
> >> +
> >> +static int dwc3_meson_g12a_remove(struct platform_device *pdev)
> >> +{
> >> +	struct dwc3_meson_g12a *priv = platform_get_drvdata(pdev);
> >> +	struct device *dev = &pdev->dev;
> >> +	int i;
> >> +
> >> +	usb_role_switch_unregister(priv->role_switch);
> >> +
> >> +	of_platform_depopulate(dev);
> >> +
> >> +	for (i = 0 ; i < PHY_COUNT ; ++i) {
> >> +		phy_power_off(priv->phys[i]);
> >> +		phy_exit(priv->phys[i]);
> >> +		phy_put(priv->phys[i]);
> > here, and remove others if have.
> 
> This is the last one I hope
> 
> >> +	}
> >> +
> >> +	pm_runtime_disable(dev);
> >> +	pm_runtime_put_noidle(dev);
> >> +	pm_runtime_set_suspended(dev);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int __maybe_unused dwc3_meson_g12a_runtime_suspend(struct device *dev)
> >> +{
> >> +	struct dwc3_meson_g12a	*priv = dev_get_drvdata(dev);
> >> +
> >> +	clk_disable(priv->clk);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int __maybe_unused dwc3_meson_g12a_runtime_resume(struct device *dev)
> >> +{
> >> +	struct dwc3_meson_g12a	*priv = dev_get_drvdata(dev);
> >> +
> >> +	return clk_enable(priv->clk);
> >> +}
> >> +
> >> +static int __maybe_unused dwc3_meson_g12a_suspend(struct device *dev)
> >> +{
> >> +	struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
> >> +	int i;
> >> +
> >> +	for (i = 0 ; i < PHY_COUNT ; ++i)
> >> +		phy_exit(priv->phys[i]);
> >> +
> >> +	reset_control_assert(priv->reset);
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev)
> >> +{
> >> +	struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
> >> +	int i, ret;
> >> +
> >> +	reset_control_deassert(priv->reset);
> >> +
> >> +	dwc3_meson_g12a_usb_init(priv);
> >> +
> >> +	/* Init PHYs */
> >> +	for (i = 0 ; i < PHY_COUNT ; ++i) {
> >> +		ret = phy_init(priv->phys[i]);
> >> +		if (ret)
> >> +			return ret;
> >> +	}
> > Do you need power on phys?
> 
> Regulators can be tied to the PHYs, should I disable power on suspend aswell ?
I find that the phy driver you used doesn't provide .power_on/off, so
the current flow won't cause something wrong, but if the phy driver
provides .power_on/off, it will do.

> 
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = {
> >> +	SET_SYSTEM_SLEEP_PM_OPS(dwc3_meson_g12a_suspend, dwc3_meson_g12a_resume)
> >> +	SET_RUNTIME_PM_OPS(dwc3_meson_g12a_runtime_suspend,
> >> +			dwc3_meson_g12a_runtime_resume, NULL)
> >> +};
> >> +
> >> +static const struct of_device_id dwc3_meson_g12a_match[] = {
> >> +	{ .compatible = "amlogic,meson-g12a-usb-ctrl" },
> >> +	{ /* Sentinel */ }
> >> +};
> >> +MODULE_DEVICE_TABLE(of, dwc3_meson_g12a_match);
> >> +
> >> +static struct platform_driver dwc3_meson_g12a_driver = {
> >> +	.probe		= dwc3_meson_g12a_probe,
> >> +	.remove		= dwc3_meson_g12a_remove,
> >> +	.driver		= {
> >> +		.name	= "dwc3-meson-g12a",
> >> +		.of_match_table = dwc3_meson_g12a_match,
> >> +		.pm	= &dwc3_meson_g12a_dev_pm_ops,
> >> +	},
> >> +};
> >> +
> >> +module_platform_driver(dwc3_meson_g12a_driver);
> >> +MODULE_LICENSE("GPL v2");
> >> +MODULE_DESCRIPTION("Amlogic Meson G12A USB Glue Layer");
> >> +MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
> > 
> > 
> 
> Thanks,
> Neil



^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2019-03-20  1:43 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-03-18 10:11 [PATCH v3 0/8] arm64: meson: Add support for USB on Amlogic G12A Neil Armstrong
2019-03-18 10:11 ` [PATCH v3 1/8] dt-bindings: phy: Add Amlogic G12A USB2 PHY Bindings Neil Armstrong
2019-03-18 10:11 ` [PATCH v3 2/8] dt-bindings: phy: Add Amlogic G12A USB3+PCIE Combo " Neil Armstrong
2019-03-18 10:11 ` [PATCH v3 3/8] dt-bindings: usb: dwc2: Add Amlogic G12A DWC2 Compatible Neil Armstrong
2019-03-18 10:11 ` [PATCH v3 4/8] dt-bindings: usb: dwc3: Add Amlogic G12A DWC3 Glue Bindings Neil Armstrong
2019-03-18 10:11 ` [PATCH v3 5/8] phy: amlogic: add Amlogic G12A USB2 PHY Driver Neil Armstrong
2019-03-18 10:18   ` Kishon Vijay Abraham I
2019-03-18 11:28     ` Neil Armstrong
2019-03-18 10:11 ` [PATCH v3 6/8] phy: amlogic: Add Amlogic G12A USB3 + PCIE Combo " Neil Armstrong
2019-03-18 10:11 ` [PATCH v3 7/8] usb: dwc2: Add Amlogic G12A DWC2 Params Neil Armstrong
2019-03-18 10:11 ` [PATCH v3 8/8] usb: dwc3: Add Amlogic G12A DWC3 glue Neil Armstrong
2019-03-19  1:57   ` Chunfeng Yun
2019-03-19  8:15     ` Neil Armstrong
2019-03-20  1:43       ` Chunfeng Yun
2019-03-18 13:27 ` [PATCH v3 0/8] arm64: meson: Add support for USB on Amlogic G12A Neil Armstrong
2019-03-18 19:41   ` Martin Blumenstingl

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).