All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/5] Meson GXL and GXM USB support
@ 2016-11-26 14:56 ` Martin Blumenstingl
  0 siblings, 0 replies; 39+ messages in thread
From: Martin Blumenstingl @ 2016-11-26 14:56 UTC (permalink / raw)
  To: linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, kishon-l0cyMroinI0,
	khilman-rdvid1DuHRBWk0Htik3J/w, carlo-KA+7E9HrN00dnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A
  Cc: catalin.marinas-5wv7dgnIgG8, will.deacon-5wv7dgnIgG8,
	narmstrong-rdvid1DuHRBWk0Htik3J/w, Martin Blumenstingl

USB support on GXL and GXM differs a lot from Meson8b and GXBB:
The most obvious change is that GXL and GXM now have one dwc3
controller and one dwc2 controller (instead of two dwc2 controllers).
With that there are also new USB PHYs.

Due to lack of hardware I was only able to test this on a board with
GXM, but as far as I understand the hardware my preparations should be
correct (so it should also work on GXL).

dwc2 will probably stay unused on most GXM devices since it's limited
to device mode via some dwc2 hardware configuration register.

dwc3 is probably used on all devices, even if there is more than just
one USB port. dwc3 has a built-in USB2 hub - on GXL this hub has two
ports enabled, while on GXM there are three ports enabled (see below
for lsusb output). There are no USB3 ports enabled in the dwc3 hardware
configuration, meaning that the SoC is limited to high-speed mode.
On my GXM device the dwc3 hardware configuration forces it into "host
only" mode.

The SoCs contain two PHY blocks: one USB3 PHY and up to four USB2 PHYs
(on GXM there are only three enabled, but the registers should support
up to four).
The USB3 PHY also handles the OTG interrupts, but since dwc3's hardware
configuration enforces "host only" mode I was not able to test this. It
simply takes care of an interrupt and then notifies all related PHYs
about the new mode.
The USB2 PHY block is a bit different: I created one PHY driver which
spans all "PHY ports" because the handling is a bit tricky. It turns
out that for each available USB port in dwc3's hub the corresponding
PHY must be enabled (even if there is no physical port - in my case
port 3 is not connected to anything, but disabling the PHY breaks
ports 1 and 2 as well).
I decided not not pass the USB2 PHYs directly to dwc3 due to three
reasons: 1. the USB3 PHY (which holds a reference to all relevant
USB2 PHY ports) controls the mode of the USB2 PHY ports (since both
are used with the same controller and thus it makes sense to keep the
mode consistent across all ports) 2. the dwc3 driver does not support
passing multiple USB2 PHYs (only one USB2 and one USB3 PHY can be
passed to it) 3. it is similar to how the vendor reference driver
manages the PHYs. Please note that this coupling is not a fixed, this
is all configurable via devicetree (so if the third USB2 PHY has to
be passed two the dwc2 controller then this is still possible by
just moving on PHY reference in the .dts).

The coupling of the USB2 and USB3 PHYs is the reason why I sent the
two drivers in one patch, even though they are handling different IP
blocks (different registers, etc.).

Unfortunately there are no datasheets available for any of these PHYs.
Both drivers were written by reading the reference drivers provided by
Amlogic and analyzing the registers on the kernel that was shipped with
my board.

As a last note: the dwc3 driver currently only explicitly enables the
first USB port "DWC3_GUSB2PHYCFG(0)" in the internal hub. The hardware
seems to enable the other two (DWC3_GUSB2PHYCFG(1) and
DWC3_GUSB2PHYCFG(2)) automatically. I will ask the dwc3 maintainers if
changes to dwc3 are desired any how these should look like, but for now
it's working fine even without changes there.

lsusb output on GXM for the dwc3 hub:
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
...
 Hub Port Status:
   Port 1: 0000.0100 power
   Port 2: 0000.0100 power
   Port 3: 0000.0100 power

NOTE: The devicetree changes depend on my previous series:
"[PATCH 0/2] minor GXL and GXM improvements" - see [0]

NOTE2: This series depends on an upstream dwc3/xhci-plat DMA fix
(special thanks to Arnd Bergmann and Sriram Dash for fixing that):
"[PATCH v5 0/6] inherit dma configuration from parent dev" - see [1]

I have a tree with all dependencies applied available at [2] if
someone wants a quick way to test this (I don't take any responsibility
if anything explodes though).

[0] http://lists.infradead.org/pipermail/linux-amlogic/2016-November/001665.html
[1] http://marc.info/?l=linux-usb&m=147938307209685&w=2
[2] https://github.com/xdarklight/linux/commits/meson-gx-integration-4.10-20161126

Martin Blumenstingl (5):
  Documentation: dt-bindings: Add documentation for Meson GXL USB2/3
    PHYs
  phy: meson: add USB2 and USB3 PHY support for Meson GXL
  arm64: dts: meson-gxl: add USB support
  ARM64: dts: meson-gxm: add GXM specific USB configuration
  ARM64: dts: meson-gx-p23x-q20x: enable USB on P23x and Q20x boards

 .../devicetree/bindings/phy/meson-gxl-usb2-phy.txt |  25 ++
 .../devicetree/bindings/phy/meson-gxl-usb3-phy.txt |  27 ++
 .../arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi |  12 +
 arch/arm64/boot/dts/amlogic/meson-gxl.dtsi         |  49 +++
 .../arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts |  17 +
 arch/arm64/boot/dts/amlogic/meson-gxm.dtsi         |  10 +
 drivers/phy/Kconfig                                |  13 +
 drivers/phy/Makefile                               |   2 +
 drivers/phy/phy-meson-gxl-usb2.c                   | 374 ++++++++++++++++++++
 drivers/phy/phy-meson-gxl-usb3.c                   | 377 +++++++++++++++++++++
 10 files changed, 906 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
 create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
 create mode 100644 drivers/phy/phy-meson-gxl-usb2.c
 create mode 100644 drivers/phy/phy-meson-gxl-usb3.c

-- 
2.10.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 0/5] Meson GXL and GXM USB support
@ 2016-11-26 14:56 ` Martin Blumenstingl
  0 siblings, 0 replies; 39+ messages in thread
From: Martin Blumenstingl @ 2016-11-26 14:56 UTC (permalink / raw)
  To: linux-arm-kernel

USB support on GXL and GXM differs a lot from Meson8b and GXBB:
The most obvious change is that GXL and GXM now have one dwc3
controller and one dwc2 controller (instead of two dwc2 controllers).
With that there are also new USB PHYs.

Due to lack of hardware I was only able to test this on a board with
GXM, but as far as I understand the hardware my preparations should be
correct (so it should also work on GXL).

dwc2 will probably stay unused on most GXM devices since it's limited
to device mode via some dwc2 hardware configuration register.

dwc3 is probably used on all devices, even if there is more than just
one USB port. dwc3 has a built-in USB2 hub - on GXL this hub has two
ports enabled, while on GXM there are three ports enabled (see below
for lsusb output). There are no USB3 ports enabled in the dwc3 hardware
configuration, meaning that the SoC is limited to high-speed mode.
On my GXM device the dwc3 hardware configuration forces it into "host
only" mode.

The SoCs contain two PHY blocks: one USB3 PHY and up to four USB2 PHYs
(on GXM there are only three enabled, but the registers should support
up to four).
The USB3 PHY also handles the OTG interrupts, but since dwc3's hardware
configuration enforces "host only" mode I was not able to test this. It
simply takes care of an interrupt and then notifies all related PHYs
about the new mode.
The USB2 PHY block is a bit different: I created one PHY driver which
spans all "PHY ports" because the handling is a bit tricky. It turns
out that for each available USB port in dwc3's hub the corresponding
PHY must be enabled (even if there is no physical port - in my case
port 3 is not connected to anything, but disabling the PHY breaks
ports 1 and 2 as well).
I decided not not pass the USB2 PHYs directly to dwc3 due to three
reasons: 1. the USB3 PHY (which holds a reference to all relevant
USB2 PHY ports) controls the mode of the USB2 PHY ports (since both
are used with the same controller and thus it makes sense to keep the
mode consistent across all ports) 2. the dwc3 driver does not support
passing multiple USB2 PHYs (only one USB2 and one USB3 PHY can be
passed to it) 3. it is similar to how the vendor reference driver
manages the PHYs. Please note that this coupling is not a fixed, this
is all configurable via devicetree (so if the third USB2 PHY has to
be passed two the dwc2 controller then this is still possible by
just moving on PHY reference in the .dts).

The coupling of the USB2 and USB3 PHYs is the reason why I sent the
two drivers in one patch, even though they are handling different IP
blocks (different registers, etc.).

Unfortunately there are no datasheets available for any of these PHYs.
Both drivers were written by reading the reference drivers provided by
Amlogic and analyzing the registers on the kernel that was shipped with
my board.

As a last note: the dwc3 driver currently only explicitly enables the
first USB port "DWC3_GUSB2PHYCFG(0)" in the internal hub. The hardware
seems to enable the other two (DWC3_GUSB2PHYCFG(1) and
DWC3_GUSB2PHYCFG(2)) automatically. I will ask the dwc3 maintainers if
changes to dwc3 are desired any how these should look like, but for now
it's working fine even without changes there.

lsusb output on GXM for the dwc3 hub:
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
...
 Hub Port Status:
   Port 1: 0000.0100 power
   Port 2: 0000.0100 power
   Port 3: 0000.0100 power

NOTE: The devicetree changes depend on my previous series:
"[PATCH 0/2] minor GXL and GXM improvements" - see [0]

NOTE2: This series depends on an upstream dwc3/xhci-plat DMA fix
(special thanks to Arnd Bergmann and Sriram Dash for fixing that):
"[PATCH v5 0/6] inherit dma configuration from parent dev" - see [1]

I have a tree with all dependencies applied available at [2] if
someone wants a quick way to test this (I don't take any responsibility
if anything explodes though).

[0] http://lists.infradead.org/pipermail/linux-amlogic/2016-November/001665.html
[1] http://marc.info/?l=linux-usb&m=147938307209685&w=2
[2] https://github.com/xdarklight/linux/commits/meson-gx-integration-4.10-20161126

Martin Blumenstingl (5):
  Documentation: dt-bindings: Add documentation for Meson GXL USB2/3
    PHYs
  phy: meson: add USB2 and USB3 PHY support for Meson GXL
  arm64: dts: meson-gxl: add USB support
  ARM64: dts: meson-gxm: add GXM specific USB configuration
  ARM64: dts: meson-gx-p23x-q20x: enable USB on P23x and Q20x boards

 .../devicetree/bindings/phy/meson-gxl-usb2-phy.txt |  25 ++
 .../devicetree/bindings/phy/meson-gxl-usb3-phy.txt |  27 ++
 .../arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi |  12 +
 arch/arm64/boot/dts/amlogic/meson-gxl.dtsi         |  49 +++
 .../arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts |  17 +
 arch/arm64/boot/dts/amlogic/meson-gxm.dtsi         |  10 +
 drivers/phy/Kconfig                                |  13 +
 drivers/phy/Makefile                               |   2 +
 drivers/phy/phy-meson-gxl-usb2.c                   | 374 ++++++++++++++++++++
 drivers/phy/phy-meson-gxl-usb3.c                   | 377 +++++++++++++++++++++
 10 files changed, 906 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
 create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
 create mode 100644 drivers/phy/phy-meson-gxl-usb2.c
 create mode 100644 drivers/phy/phy-meson-gxl-usb3.c

-- 
2.10.2

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

* [PATCH 0/5] Meson GXL and GXM USB support
@ 2016-11-26 14:56 ` Martin Blumenstingl
  0 siblings, 0 replies; 39+ messages in thread
From: Martin Blumenstingl @ 2016-11-26 14:56 UTC (permalink / raw)
  To: linus-amlogic

USB support on GXL and GXM differs a lot from Meson8b and GXBB:
The most obvious change is that GXL and GXM now have one dwc3
controller and one dwc2 controller (instead of two dwc2 controllers).
With that there are also new USB PHYs.

Due to lack of hardware I was only able to test this on a board with
GXM, but as far as I understand the hardware my preparations should be
correct (so it should also work on GXL).

dwc2 will probably stay unused on most GXM devices since it's limited
to device mode via some dwc2 hardware configuration register.

dwc3 is probably used on all devices, even if there is more than just
one USB port. dwc3 has a built-in USB2 hub - on GXL this hub has two
ports enabled, while on GXM there are three ports enabled (see below
for lsusb output). There are no USB3 ports enabled in the dwc3 hardware
configuration, meaning that the SoC is limited to high-speed mode.
On my GXM device the dwc3 hardware configuration forces it into "host
only" mode.

The SoCs contain two PHY blocks: one USB3 PHY and up to four USB2 PHYs
(on GXM there are only three enabled, but the registers should support
up to four).
The USB3 PHY also handles the OTG interrupts, but since dwc3's hardware
configuration enforces "host only" mode I was not able to test this. It
simply takes care of an interrupt and then notifies all related PHYs
about the new mode.
The USB2 PHY block is a bit different: I created one PHY driver which
spans all "PHY ports" because the handling is a bit tricky. It turns
out that for each available USB port in dwc3's hub the corresponding
PHY must be enabled (even if there is no physical port - in my case
port 3 is not connected to anything, but disabling the PHY breaks
ports 1 and 2 as well).
I decided not not pass the USB2 PHYs directly to dwc3 due to three
reasons: 1. the USB3 PHY (which holds a reference to all relevant
USB2 PHY ports) controls the mode of the USB2 PHY ports (since both
are used with the same controller and thus it makes sense to keep the
mode consistent across all ports) 2. the dwc3 driver does not support
passing multiple USB2 PHYs (only one USB2 and one USB3 PHY can be
passed to it) 3. it is similar to how the vendor reference driver
manages the PHYs. Please note that this coupling is not a fixed, this
is all configurable via devicetree (so if the third USB2 PHY has to
be passed two the dwc2 controller then this is still possible by
just moving on PHY reference in the .dts).

The coupling of the USB2 and USB3 PHYs is the reason why I sent the
two drivers in one patch, even though they are handling different IP
blocks (different registers, etc.).

Unfortunately there are no datasheets available for any of these PHYs.
Both drivers were written by reading the reference drivers provided by
Amlogic and analyzing the registers on the kernel that was shipped with
my board.

As a last note: the dwc3 driver currently only explicitly enables the
first USB port "DWC3_GUSB2PHYCFG(0)" in the internal hub. The hardware
seems to enable the other two (DWC3_GUSB2PHYCFG(1) and
DWC3_GUSB2PHYCFG(2)) automatically. I will ask the dwc3 maintainers if
changes to dwc3 are desired any how these should look like, but for now
it's working fine even without changes there.

lsusb output on GXM for the dwc3 hub:
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
...
 Hub Port Status:
   Port 1: 0000.0100 power
   Port 2: 0000.0100 power
   Port 3: 0000.0100 power

NOTE: The devicetree changes depend on my previous series:
"[PATCH 0/2] minor GXL and GXM improvements" - see [0]

NOTE2: This series depends on an upstream dwc3/xhci-plat DMA fix
(special thanks to Arnd Bergmann and Sriram Dash for fixing that):
"[PATCH v5 0/6] inherit dma configuration from parent dev" - see [1]

I have a tree with all dependencies applied available at [2] if
someone wants a quick way to test this (I don't take any responsibility
if anything explodes though).

[0] http://lists.infradead.org/pipermail/linux-amlogic/2016-November/001665.html
[1] http://marc.info/?l=linux-usb&m=147938307209685&w=2
[2] https://github.com/xdarklight/linux/commits/meson-gx-integration-4.10-20161126

Martin Blumenstingl (5):
  Documentation: dt-bindings: Add documentation for Meson GXL USB2/3
    PHYs
  phy: meson: add USB2 and USB3 PHY support for Meson GXL
  arm64: dts: meson-gxl: add USB support
  ARM64: dts: meson-gxm: add GXM specific USB configuration
  ARM64: dts: meson-gx-p23x-q20x: enable USB on P23x and Q20x boards

 .../devicetree/bindings/phy/meson-gxl-usb2-phy.txt |  25 ++
 .../devicetree/bindings/phy/meson-gxl-usb3-phy.txt |  27 ++
 .../arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi |  12 +
 arch/arm64/boot/dts/amlogic/meson-gxl.dtsi         |  49 +++
 .../arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts |  17 +
 arch/arm64/boot/dts/amlogic/meson-gxm.dtsi         |  10 +
 drivers/phy/Kconfig                                |  13 +
 drivers/phy/Makefile                               |   2 +
 drivers/phy/phy-meson-gxl-usb2.c                   | 374 ++++++++++++++++++++
 drivers/phy/phy-meson-gxl-usb3.c                   | 377 +++++++++++++++++++++
 10 files changed, 906 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
 create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
 create mode 100644 drivers/phy/phy-meson-gxl-usb2.c
 create mode 100644 drivers/phy/phy-meson-gxl-usb3.c

-- 
2.10.2

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

* [PATCH 1/5] Documentation: dt-bindings: Add documentation for Meson GXL USB2/3 PHYs
  2016-11-26 14:56 ` Martin Blumenstingl
  (?)
@ 2016-11-26 14:56     ` Martin Blumenstingl
  -1 siblings, 0 replies; 39+ messages in thread
From: Martin Blumenstingl @ 2016-11-26 14:56 UTC (permalink / raw)
  To: linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, kishon-l0cyMroinI0,
	khilman-rdvid1DuHRBWk0Htik3J/w, carlo-KA+7E9HrN00dnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A
  Cc: catalin.marinas-5wv7dgnIgG8, will.deacon-5wv7dgnIgG8,
	narmstrong-rdvid1DuHRBWk0Htik3J/w, Martin Blumenstingl

This adds the DT binding documentation for the USB2 and USB3 PHYs found
in the Meson GXL and GXM SoCs.

Signed-off-by: Martin Blumenstingl <martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>
---
 .../devicetree/bindings/phy/meson-gxl-usb2-phy.txt | 25 ++++++++++++++++++++
 .../devicetree/bindings/phy/meson-gxl-usb3-phy.txt | 27 ++++++++++++++++++++++
 2 files changed, 52 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
 create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt

diff --git a/Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt b/Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
new file mode 100644
index 0000000..e7828ca
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
@@ -0,0 +1,25 @@
+* Amlogic Meson GXL and GXM USB2 PHY binding
+
+This describes the USB2 PHY block which provides multiple USB2 PHY ports.
+
+Required properties:
+- compatible:	Should be "amlogic,meson-gxl-usb2-phy"
+- reg:		The base address and length of the registers
+- #phys-cells:	should be 1 (see phy-bindings.txt in this directory)
+- clocks:	phandle and clock identifier for the phy clocks
+- clock-names:	"usb" and "usb_ddr"
+- resets:	reference to the reset controller
+
+Optional properties:
+- phy-supply:	see phy-bindings.txt in this directory
+
+
+Example:
+	usb2_phys: phy@78000 {
+		compatible = "amlogic,meson-gxl-usb2-phy";
+		#phy-cells = <1>;
+		reg = <0x0 0x78000 0x0 0x80>;
+		clocks = <&clkc CLKID_USB1>, <&clkc CLKID_USB1_DDR_BRIDGE>;
+		clock-names = "usb", "usb_ddr";
+		resets = <&reset RESET_USB_OTG>;
+	};
diff --git a/Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt b/Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
new file mode 100644
index 0000000..be779e8
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
@@ -0,0 +1,27 @@
+* Amlogic Meson GXL and GXM USB3 PHY binding
+
+Required properties:
+- compatible:	Should be "amlogic,meson-gxl-usb3-phy"
+- reg:		The base address and length of the registers
+- #phys-cells:	should be 0 (see phy-bindings.txt in this directory)
+- interrupts:	describes the OTG device/host mode detection interrupt
+- phys:		a list of related PHYs (typically the USB2 PHYs, see
+		meson-gxl-usb2-phy.txt in this directory). The mode of
+		the listed PHYs will be managed by the USB3 PHY (which
+		is required for OTG device/host detection to work).
+		The number of PHYs listed typically matches the number
+		of ports which are enabled in the USB controller which
+		uses this PHY.
+
+Optional properties:
+- phy-supply:	see phy-bindings.txt in this directory
+
+
+Example:
+	usb3_phy0: phy@78080 {
+		compatible = "amlogic,meson-gxl-usb3-phy";
+		#phy-cells = <0>;
+		reg = <0x0 0x78080 0x0 0x20>;
+		interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
+		phys = <&usb2_phys 0>, <&usb2_phys 1>;
+	};
-- 
2.10.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 1/5] Documentation: dt-bindings: Add documentation for Meson GXL USB2/3 PHYs
@ 2016-11-26 14:56     ` Martin Blumenstingl
  0 siblings, 0 replies; 39+ messages in thread
From: Martin Blumenstingl @ 2016-11-26 14:56 UTC (permalink / raw)
  To: linux-arm-kernel

This adds the DT binding documentation for the USB2 and USB3 PHYs found
in the Meson GXL and GXM SoCs.

Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
---
 .../devicetree/bindings/phy/meson-gxl-usb2-phy.txt | 25 ++++++++++++++++++++
 .../devicetree/bindings/phy/meson-gxl-usb3-phy.txt | 27 ++++++++++++++++++++++
 2 files changed, 52 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
 create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt

diff --git a/Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt b/Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
new file mode 100644
index 0000000..e7828ca
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
@@ -0,0 +1,25 @@
+* Amlogic Meson GXL and GXM USB2 PHY binding
+
+This describes the USB2 PHY block which provides multiple USB2 PHY ports.
+
+Required properties:
+- compatible:	Should be "amlogic,meson-gxl-usb2-phy"
+- reg:		The base address and length of the registers
+- #phys-cells:	should be 1 (see phy-bindings.txt in this directory)
+- clocks:	phandle and clock identifier for the phy clocks
+- clock-names:	"usb" and "usb_ddr"
+- resets:	reference to the reset controller
+
+Optional properties:
+- phy-supply:	see phy-bindings.txt in this directory
+
+
+Example:
+	usb2_phys: phy at 78000 {
+		compatible = "amlogic,meson-gxl-usb2-phy";
+		#phy-cells = <1>;
+		reg = <0x0 0x78000 0x0 0x80>;
+		clocks = <&clkc CLKID_USB1>, <&clkc CLKID_USB1_DDR_BRIDGE>;
+		clock-names = "usb", "usb_ddr";
+		resets = <&reset RESET_USB_OTG>;
+	};
diff --git a/Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt b/Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
new file mode 100644
index 0000000..be779e8
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
@@ -0,0 +1,27 @@
+* Amlogic Meson GXL and GXM USB3 PHY binding
+
+Required properties:
+- compatible:	Should be "amlogic,meson-gxl-usb3-phy"
+- reg:		The base address and length of the registers
+- #phys-cells:	should be 0 (see phy-bindings.txt in this directory)
+- interrupts:	describes the OTG device/host mode detection interrupt
+- phys:		a list of related PHYs (typically the USB2 PHYs, see
+		meson-gxl-usb2-phy.txt in this directory). The mode of
+		the listed PHYs will be managed by the USB3 PHY (which
+		is required for OTG device/host detection to work).
+		The number of PHYs listed typically matches the number
+		of ports which are enabled in the USB controller which
+		uses this PHY.
+
+Optional properties:
+- phy-supply:	see phy-bindings.txt in this directory
+
+
+Example:
+	usb3_phy0: phy at 78080 {
+		compatible = "amlogic,meson-gxl-usb3-phy";
+		#phy-cells = <0>;
+		reg = <0x0 0x78080 0x0 0x20>;
+		interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
+		phys = <&usb2_phys 0>, <&usb2_phys 1>;
+	};
-- 
2.10.2

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

* [PATCH 1/5] Documentation: dt-bindings: Add documentation for Meson GXL USB2/3 PHYs
@ 2016-11-26 14:56     ` Martin Blumenstingl
  0 siblings, 0 replies; 39+ messages in thread
From: Martin Blumenstingl @ 2016-11-26 14:56 UTC (permalink / raw)
  To: linus-amlogic

This adds the DT binding documentation for the USB2 and USB3 PHYs found
in the Meson GXL and GXM SoCs.

Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
---
 .../devicetree/bindings/phy/meson-gxl-usb2-phy.txt | 25 ++++++++++++++++++++
 .../devicetree/bindings/phy/meson-gxl-usb3-phy.txt | 27 ++++++++++++++++++++++
 2 files changed, 52 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
 create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt

diff --git a/Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt b/Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
new file mode 100644
index 0000000..e7828ca
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
@@ -0,0 +1,25 @@
+* Amlogic Meson GXL and GXM USB2 PHY binding
+
+This describes the USB2 PHY block which provides multiple USB2 PHY ports.
+
+Required properties:
+- compatible:	Should be "amlogic,meson-gxl-usb2-phy"
+- reg:		The base address and length of the registers
+- #phys-cells:	should be 1 (see phy-bindings.txt in this directory)
+- clocks:	phandle and clock identifier for the phy clocks
+- clock-names:	"usb" and "usb_ddr"
+- resets:	reference to the reset controller
+
+Optional properties:
+- phy-supply:	see phy-bindings.txt in this directory
+
+
+Example:
+	usb2_phys: phy at 78000 {
+		compatible = "amlogic,meson-gxl-usb2-phy";
+		#phy-cells = <1>;
+		reg = <0x0 0x78000 0x0 0x80>;
+		clocks = <&clkc CLKID_USB1>, <&clkc CLKID_USB1_DDR_BRIDGE>;
+		clock-names = "usb", "usb_ddr";
+		resets = <&reset RESET_USB_OTG>;
+	};
diff --git a/Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt b/Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
new file mode 100644
index 0000000..be779e8
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
@@ -0,0 +1,27 @@
+* Amlogic Meson GXL and GXM USB3 PHY binding
+
+Required properties:
+- compatible:	Should be "amlogic,meson-gxl-usb3-phy"
+- reg:		The base address and length of the registers
+- #phys-cells:	should be 0 (see phy-bindings.txt in this directory)
+- interrupts:	describes the OTG device/host mode detection interrupt
+- phys:		a list of related PHYs (typically the USB2 PHYs, see
+		meson-gxl-usb2-phy.txt in this directory). The mode of
+		the listed PHYs will be managed by the USB3 PHY (which
+		is required for OTG device/host detection to work).
+		The number of PHYs listed typically matches the number
+		of ports which are enabled in the USB controller which
+		uses this PHY.
+
+Optional properties:
+- phy-supply:	see phy-bindings.txt in this directory
+
+
+Example:
+	usb3_phy0: phy at 78080 {
+		compatible = "amlogic,meson-gxl-usb3-phy";
+		#phy-cells = <0>;
+		reg = <0x0 0x78080 0x0 0x20>;
+		interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
+		phys = <&usb2_phys 0>, <&usb2_phys 1>;
+	};
-- 
2.10.2

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

* [PATCH 2/5] phy: meson: add USB2 and USB3 PHY support for Meson GXL
  2016-11-26 14:56 ` Martin Blumenstingl
  (?)
@ 2016-11-26 14:56     ` Martin Blumenstingl
  -1 siblings, 0 replies; 39+ messages in thread
From: Martin Blumenstingl @ 2016-11-26 14:56 UTC (permalink / raw)
  To: linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, kishon-l0cyMroinI0,
	khilman-rdvid1DuHRBWk0Htik3J/w, carlo-KA+7E9HrN00dnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A
  Cc: catalin.marinas-5wv7dgnIgG8, will.deacon-5wv7dgnIgG8,
	narmstrong-rdvid1DuHRBWk0Htik3J/w, Martin Blumenstingl

This adds two new USB PHY drivers found on Meson GXL and GXM SoCs.

The registers for the USB2 PHY block handle a maximum of 4 ports (newer
SoCs may allow more ports, the driver handles this as long as the
register length is adjusted in the .dts). The PHY block theoretically
allows powering down each PHY port separately (by putting it into
"reset" state). Unfortunately this does not work (my board has 2 USB
ports, connected to port 1 and 2 of the dwc3's internal hub. When
leaving the third USB PHY disabled then the hub sees that a device is
plugged in, but it does not work: "usb usb1-port2: connect-debounce
failed").
The USB3 PHY will take care of enabling/disabling all available ports,
because the USB3 PHY also manages the mode of the USB2 PHYs.

The USB3 PHY actually has three purposes:
- it provides the USB3 PHY
- it handles the OTG device/host mode detection interrupt
- it notifies the corresponding USB2 PHYs of the OTG mode changes
On GXL and GXM SoCs one references all available USB2 PHY ports in the
USB3 PHY because all are connected to the same USB controller (thus the
mode will always match). This behavior is configurable via devicetree,
by passing (or not passing) a list of other ("child") PHYs which should
be configured by the USB3 PHY.

Unfortunately there are no datasheets available for any of these PHYs.
Both drivers were written by reading the reference drivers provided by
Amlogic and analyzing the registers on the kernel that was shipped with
my board.

Signed-off-by: Martin Blumenstingl <martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>
---
 drivers/phy/Kconfig              |  13 ++
 drivers/phy/Makefile             |   2 +
 drivers/phy/phy-meson-gxl-usb2.c | 374 ++++++++++++++++++++++++++++++++++++++
 drivers/phy/phy-meson-gxl-usb3.c | 377 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 766 insertions(+)
 create mode 100644 drivers/phy/phy-meson-gxl-usb2.c
 create mode 100644 drivers/phy/phy-meson-gxl-usb3.c

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 728e03f..ea74843 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -502,4 +502,17 @@ config PHY_MESON8B_USB2
 	  and GXBB SoCs.
 	  If unsure, say N.
 
+config PHY_MESON_GXL_USB
+	tristate "Meson GXL USB2 and USB3 PHY drivers"
+	default ARCH_MESON
+	depends on OF && (ARCH_MESON || COMPILE_TEST)
+	depends on USB_SUPPORT
+	select USB_COMMON
+	select GENERIC_PHY
+	select REGMAP_MMIO
+	help
+	  Enable this to support the Meson USB2 and USB3 PHYs found in
+	  Meson GXL SoCs.
+	  If unsure, say N.
+
 endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 0c7fdae..960a96e 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -61,3 +61,5 @@ obj-$(CONFIG_PHY_CYGNUS_PCIE)		+= phy-bcm-cygnus-pcie.o
 obj-$(CONFIG_ARCH_TEGRA) += tegra/
 obj-$(CONFIG_PHY_NS2_PCIE)		+= phy-bcm-ns2-pcie.o
 obj-$(CONFIG_PHY_MESON8B_USB2)		+= phy-meson8b-usb2.o
+obj-$(CONFIG_PHY_MESON_GXL_USB)		+= phy-meson-gxl-usb2.o
+obj-$(CONFIG_PHY_MESON_GXL_USB)		+= phy-meson-gxl-usb3.o
diff --git a/drivers/phy/phy-meson-gxl-usb2.c b/drivers/phy/phy-meson-gxl-usb2.c
new file mode 100644
index 0000000..c081ce3
--- /dev/null
+++ b/drivers/phy/phy-meson-gxl-usb2.c
@@ -0,0 +1,374 @@
+/*
+ * Meson GXL USB2 PHY driver
+ *
+ * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/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>
+#include <linux/usb/of.h>
+
+/* bits [31:27] are read-only */
+#define U2P_R0							0x0
+	#define U2P_R0_BYPASS_SEL				BIT(0)
+	#define U2P_R0_BYPASS_DM_EN				BIT(1)
+	#define U2P_R0_BYPASS_DP_EN				BIT(2)
+	#define U2P_R0_TXBITSTUFF_ENH				BIT(3)
+	#define U2P_R0_TXBITSTUFF_EN				BIT(4)
+	#define U2P_R0_DM_PULLDOWN				BIT(5)
+	#define U2P_R0_DP_PULLDOWN				BIT(6)
+	#define U2P_R0_DP_VBUS_VLD_EXT_SEL			BIT(7)
+	#define U2P_R0_DP_VBUS_VLD_EXT				BIT(8)
+	#define U2P_R0_ADP_PRB_EN				BIT(9)
+	#define U2P_R0_ADP_DISCHARGE				BIT(10)
+	#define U2P_R0_ADP_CHARGE				BIT(11)
+	#define U2P_R0_DRV_VBUS					BIT(12)
+	#define U2P_R0_ID_PULLUP				BIT(13)
+	#define U2P_R0_LOOPBACK_EN_B				BIT(14)
+	#define U2P_R0_OTG_DISABLE				BIT(15)
+	#define U2P_R0_COMMON_ONN				BIT(16)
+	#define U2P_R0_FSEL_SHIFT				17
+	#define U2P_R0_FSEL_MASK				GENMASK(19, 17)
+	#define U2P_R0_REF_CLK_SEL_SHIFT			20
+	#define U2P_R0_REF_CLK_SEL_MASK				GENMASK(21, 20)
+	#define U2P_R0_POWER_ON_RESET				BIT(22)
+	#define U2P_R0_V_ATE_TEST_EN_B_SHIFT			23
+	#define U2P_R0_V_ATE_TEST_EN_B_MASK			GENMASK(24, 23)
+	#define U2P_R0_ID_SET_ID_DQ				BIT(25)
+	#define U2P_R0_ATE_RESET				BIT(26)
+	#define U2P_R0_FSV_MINUS				BIT(27)
+	#define U2P_R0_FSV_PLUS					BIT(28)
+	#define U2P_R0_BYPASS_DM_DATA				BIT(29)
+	#define U2P_R0_BYPASS_DP_DATA				BIT(30)
+
+#define U2P_R1							0x4
+	#define U2P_R1_BURN_IN_TEST				BIT(0)
+	#define U2P_R1_ACA_ENABLE				BIT(1)
+	#define U2P_R1_DCD_ENABLE				BIT(2)
+	#define U2P_R1_VDAT_SRC_EN_B				BIT(3)
+	#define U2P_R1_VDAT_DET_EN_B				BIT(4)
+	#define U2P_R1_CHARGES_SEL				BIT(5)
+	#define U2P_R1_TX_PREEMP_PULSE_TUNE			BIT(6)
+	#define U2P_R1_TX_PREEMP_AMP_TUNE_SHIFT			7
+	#define U2P_R1_TX_PREEMP_AMP_TUNE_MASK			GENMASK(8, 7)
+	#define U2P_R1_TX_RES_TUNE_SHIFT			9
+	#define U2P_R1_TX_RES_TUNE_MASK				GENMASK(10, 9)
+	#define U2P_R1_TX_RISE_TUNE_SHIFT			11
+	#define U2P_R1_TX_RISE_TUNE_MASK			GENMASK(12, 11)
+	#define U2P_R1_TX_VREF_TUNE_SHIFT			13
+	#define U2P_R1_TX_VREF_TUNE_MASK			GENMASK(16, 13)
+	#define U2P_R1_TX_FSLS_TUNE_SHIFT			17
+	#define U2P_R1_TX_FSLS_TUNE_MASK			GENMASK(20, 17)
+	#define U2P_R1_TX_HSXV_TUNE_SHIFT			21
+	#define U2P_R1_TX_HSXV_TUNE_MASK			GENMASK(22, 21)
+	#define U2P_R1_OTG_TUNE_SHIFT				23
+	#define U2P_R1_OTG_TUNE_MASK				GENMASK(25, 23)
+	#define U2P_R1_SQRX_TUNE_SHIFT				26
+	#define U2P_R1_SQRX_TUNE_MASK				GENMASK(28, 26)
+	#define U2P_R1_COMP_DIS_TUNE_SHIFT			29
+	#define U2P_R1_COMP_DIS_TUNE_MASK			GENMASK(31, 29)
+
+/* bits [31:14] are read-only */
+#define U2P_R2							0x8
+	#define U2P_R2_DATA_IN_SHIFT				0
+	#define U2P_R2_DATA_IN_MASK				GENMASK(3, 0)
+	#define U2P_R2_DATA_IN_EN_SHIFT				4
+	#define U2P_R2_DATA_IN_EN_MASK				GENMASK(7, 4)
+	#define U2P_R2_ADDR_SHIFT				8
+	#define U2P_R2_ADDR_MASK				GENMASK(11, 8)
+	#define U2P_R2_DATA_OUT_SEL				BIT(12)
+	#define U2P_R2_CLK					BIT(13)
+	#define U2P_R2_DATA_OUT_SHIFT				14
+	#define U2P_R2_DATA_OUT_MASK				GENMASK(17, 14)
+	#define U2P_R2_ACA_PIN_RANGE_C				BIT(18)
+	#define U2P_R2_ACA_PIN_RANGE_B				BIT(19)
+	#define U2P_R2_ACA_PIN_RANGE_A				BIT(20)
+	#define U2P_R2_ACA_PIN_GND				BIT(21)
+	#define U2P_R2_ACA_PIN_FLOAT				BIT(22)
+	#define U2P_R2_CHARGE_DETECT				BIT(23)
+	#define U2P_R2_DEVICE_SESSION_VALID			BIT(24)
+	#define U2P_R2_ADP_PROBE				BIT(25)
+	#define U2P_R2_ADP_SENSE				BIT(26)
+	#define U2P_R2_SESSION_END				BIT(27)
+	#define U2P_R2_VBUS_VALID				BIT(28)
+	#define U2P_R2_B_VALID					BIT(29)
+	#define U2P_R2_A_VALID					BIT(30)
+	#define U2P_R2_ID_DIG					BIT(31)
+
+#define U2P_R3							0xc
+
+#define PHY_PORT_RESOURCE_SIZE					0x20
+
+#define RESET_COMPLETE_TIME				500
+
+struct phy_meson_gxl_usb2_priv {
+	struct regmap		*regmap;
+	enum phy_mode		mode;
+};
+
+struct phy_meson_gxl_usb2_drv {
+	void __iomem		*base;
+	int			num_ports;
+	struct phy		**ports;
+	struct clk		*clk_usb;
+	struct clk		*clk_usb_ddr;
+};
+
+static const struct regmap_config phy_meson_gxl_usb2_regmap_conf = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.max_register = U2P_R3,
+};
+
+static int phy_meson_gxl_usb2_set_mode(struct phy *phy, enum phy_mode mode)
+{
+	struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
+
+	switch (mode) {
+	case PHY_MODE_USB_HOST:
+	case PHY_MODE_USB_OTG:
+		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DM_PULLDOWN,
+				   U2P_R0_DM_PULLDOWN);
+		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DP_PULLDOWN,
+				   U2P_R0_DP_PULLDOWN);
+		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_ID_PULLUP, 0);
+		break;
+
+	case PHY_MODE_USB_DEVICE:
+		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DM_PULLDOWN,
+				   0);
+		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DP_PULLDOWN,
+				   0);
+		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_ID_PULLUP,
+				   U2P_R0_ID_PULLUP);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	/* reset the PHY and wait until settings are stabilized */
+	regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET,
+			   U2P_R0_POWER_ON_RESET);
+	udelay(RESET_COMPLETE_TIME);
+	regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET, 0);
+	udelay(RESET_COMPLETE_TIME);
+
+	priv->mode = mode;
+
+	return 0;
+}
+
+static int phy_meson_gxl_usb2_power_off(struct phy *phy)
+{
+	struct phy_meson_gxl_usb2_drv *drv_priv =
+		dev_get_drvdata(phy->dev.parent);
+	struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
+
+	/* power off the PHY by putting it into reset mode */
+	regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET,
+			   U2P_R0_POWER_ON_RESET);
+
+	clk_disable_unprepare(drv_priv->clk_usb_ddr);
+	clk_disable_unprepare(drv_priv->clk_usb);
+
+	return 0;
+}
+
+static int phy_meson_gxl_usb2_power_on(struct phy *phy)
+{
+	struct phy_meson_gxl_usb2_drv *drv_priv =
+		dev_get_drvdata(phy->dev.parent);
+	struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
+	int ret;
+
+	ret = clk_prepare_enable(drv_priv->clk_usb);
+	if (ret) {
+		dev_err(&phy->dev, "Failed to enable USB clock\n");
+		return ret;
+	}
+
+	ret = clk_prepare_enable(drv_priv->clk_usb_ddr);
+	if (ret) {
+		clk_disable_unprepare(drv_priv->clk_usb);
+
+		dev_err(&phy->dev, "Failed to enable USB DDR clock\n");
+		return ret;
+	}
+
+	/* power on the PHY by taking it out of reset mode */
+	regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET, 0);
+
+	ret = phy_meson_gxl_usb2_set_mode(phy, priv->mode);
+	if (ret) {
+		phy_meson_gxl_usb2_power_off(phy);
+
+		dev_err(&phy->dev, "Failed to initialize PHY with mode %d\n",
+			priv->mode);
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct phy_ops phy_meson_gxl_usb2_ops = {
+	.power_on	= phy_meson_gxl_usb2_power_on,
+	.power_off	= phy_meson_gxl_usb2_power_off,
+	.set_mode	= phy_meson_gxl_usb2_set_mode,
+	.owner		= THIS_MODULE,
+};
+
+static struct phy *phy_meson_gxl_usb2_of_xlate(struct device *dev,
+					       struct of_phandle_args *args)
+{
+	struct phy_meson_gxl_usb2_drv *priv = dev_get_drvdata(dev);
+	int port;
+
+	if (args->args_count != 1) {
+		dev_err(dev, "Invalid number of cells in 'phy' property\n");
+		return ERR_PTR(-ENODEV);
+	}
+
+	port = args->args[0];
+	if (WARN_ON(port >= priv->num_ports))
+		return ERR_PTR(-ENODEV);
+
+	return priv->ports[port];
+}
+
+static int phy_meson_gxl_usb2_probe_port(struct device *dev, int port)
+{
+	struct phy_meson_gxl_usb2_drv *drv_priv = dev_get_drvdata(dev);
+	struct phy_meson_gxl_usb2_priv *phy_priv;
+	struct phy *phy;
+	void __iomem *port_base;
+
+	phy_priv = devm_kzalloc(dev, sizeof(*phy_priv), GFP_KERNEL);
+	if (!phy_priv)
+		return -ENOMEM;
+
+	switch (of_usb_get_dr_mode_by_phy(dev->of_node, port)) {
+	case USB_DR_MODE_PERIPHERAL:
+		phy_priv->mode = PHY_MODE_USB_DEVICE;
+		break;
+	case USB_DR_MODE_OTG:
+		phy_priv->mode = PHY_MODE_USB_OTG;
+		break;
+	case USB_DR_MODE_HOST:
+	default:
+		phy_priv->mode = PHY_MODE_USB_HOST;
+		break;
+	}
+
+	phy = devm_phy_create(dev, NULL, &phy_meson_gxl_usb2_ops);
+	if (IS_ERR(phy)) {
+		dev_err(dev, "failed to create PHY port %d\n", port);
+		return PTR_ERR(phy);
+	}
+
+	port_base = drv_priv->base + (port * PHY_PORT_RESOURCE_SIZE);
+	phy_priv->regmap = devm_regmap_init_mmio(&phy->dev, port_base,
+						 &phy_meson_gxl_usb2_regmap_conf);
+	if (IS_ERR(phy_priv->regmap))
+		return PTR_ERR(phy_priv->regmap);
+
+	phy_set_drvdata(phy, phy_priv);
+
+	drv_priv->ports[port] = phy;
+
+	return 0;
+}
+
+static int phy_meson_gxl_usb2_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct phy_meson_gxl_usb2_drv *priv;
+	struct phy_provider *phy_provider;
+	struct resource *res;
+	int i, ret;
+
+	ret = device_reset(dev);
+	if (ret) {
+		dev_err(dev, "failed to reset device\n");
+		return ret;
+	}
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, priv);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(priv->base))
+		return PTR_ERR(priv->base);
+
+	priv->num_ports = resource_size(res) / PHY_PORT_RESOURCE_SIZE;
+	if (priv->num_ports < 1) {
+		dev_err(dev, "specified memory range is too small\n");
+		return -EINVAL;
+	}
+
+	priv->ports = devm_kcalloc(dev, priv->num_ports, sizeof(*priv->ports),
+				   GFP_KERNEL);
+	if (!priv->ports)
+		return -ENOMEM;
+
+	priv->clk_usb = devm_clk_get(dev, "usb");
+	if (IS_ERR(priv->clk_usb)) {
+		dev_err(dev, "failed to get USB clock\n");
+		return PTR_ERR(priv->clk_usb);
+	}
+
+	priv->clk_usb_ddr = devm_clk_get(dev, "usb_ddr");
+	if (IS_ERR(priv->clk_usb_ddr)) {
+		dev_err(dev, "failed to get USB DDR clock\n");
+		return PTR_ERR(priv->clk_usb_ddr);
+	}
+
+	for (i = 0; i < priv->num_ports; i++) {
+		ret = phy_meson_gxl_usb2_probe_port(dev, i);
+		if (ret)
+			return ret;
+	}
+
+	phy_provider = devm_of_phy_provider_register(dev,
+						     phy_meson_gxl_usb2_of_xlate);
+
+	return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id phy_meson_gxl_usb2_of_match[] = {
+	{ .compatible = "amlogic,meson-gxl-usb2-phy", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, phy_meson_gxl_usb2_of_match);
+
+static struct platform_driver phy_meson_gxl_usb2_driver = {
+	.probe	= phy_meson_gxl_usb2_probe,
+	.driver	= {
+		.name		= "phy-meson-gxl-usb2",
+		.of_match_table	= phy_meson_gxl_usb2_of_match,
+	},
+};
+module_platform_driver(phy_meson_gxl_usb2_driver);
+
+MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>");
+MODULE_DESCRIPTION("Meson GXL USB2 PHY driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/phy-meson-gxl-usb3.c b/drivers/phy/phy-meson-gxl-usb3.c
new file mode 100644
index 0000000..90a4028
--- /dev/null
+++ b/drivers/phy/phy-meson-gxl-usb3.c
@@ -0,0 +1,377 @@
+/*
+ * Meson GXL USB3 PHY driver
+ *
+ * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/reset.h>
+#include <linux/regmap.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/usb/of.h>
+#include <linux/workqueue.h>
+
+#define USB_R0							0x00
+	#define USB_R0_P30_FSEL_SHIFT				0
+	#define USB_R0_P30_FSEL_MASK				GENMASK(5, 0)
+	#define USB_R0_P30_PHY_RESET				BIT(6)
+	#define USB_R0_P30_TEST_POWERDOWN_HSP			BIT(7)
+	#define USB_R0_P30_TEST_POWERDOWN_SSP			BIT(8)
+	#define USB_R0_P30_ACJT_LEVEL_SHIFT			9
+	#define USB_R0_P30_ACJT_LEVEL_MASK			GENMASK(13, 9)
+	#define USB_R0_P30_TX_BOOST_LEVEL_SHIFT			14
+	#define USB_R0_P30_TX_BOOST_LEVEL_MASK			GENMASK(16, 14)
+	#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_SHIFT		19
+	#define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK		GENMASK(28, 19)
+	#define USB_R0_U2D_SS_SCALEDOWN_MODE_SHIFT		29
+	#define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK		GENMASK(30, 29)
+	#define USB_R0_U2D_ACT					BIT(31)
+
+#define USB_R1							0x04
+	#define USB_R1_U3H_BIGENDIAN_GS				BIT(0)
+	#define USB_R1_U3H_PME_ENABLE				BIT(1)
+	#define USB_R1_U3H_HUB_PORT_OVERCURRENT_SHIFT		2
+	#define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK		GENMASK(6, 2)
+	#define USB_R1_U3H_HUB_PORT_PERM_ATTACH_SHIFT		7
+	#define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK		GENMASK(11, 7)
+	#define USB_R1_U3H_HOST_U2_PORT_DISABLE_SHIFT		12
+	#define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK		GENMASK(15, 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_SHIFT		19
+	#define USB_R1_U3H_FLADJ_30MHZ_REG_MASK			GENMASK(24, 19)
+	#define USB_R1_P30_PCS_TX_SWING_FULL_SHIFT		25
+	#define USB_R1_P30_PCS_TX_SWING_FULL_MASK		GENMASK(31, 25)
+
+#define USB_R2							0x08
+	#define USB_R2_P30_CR_DATA_IN_SHIFT			0
+	#define USB_R2_P30_CR_DATA_IN_MASK			GENMASK(15, 0)
+	#define USB_R2_P30_CR_READ				BIT(16)
+	#define USB_R2_P30_CR_WRITE				BIT(17)
+	#define USB_R2_P30_CR_CAP_ADDR				BIT(18)
+	#define USB_R2_P30_CR_CAP_DATA				BIT(19)
+	#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_SHIFT		20
+	#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK		GENMASK(25, 20)
+	#define USB_R2_P30_PCS_TX_DEEMPH_6DB_SHIFT		26
+	#define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK		GENMASK(31, 26)
+
+#define USB_R3							0x0c
+	#define USB_R3_P30_SSC_ENABLE				BIT(0)
+	#define USB_R3_P30_SSC_RANGE_SHIFT			1
+	#define USB_R3_P30_SSC_RANGE_MASK			GENMASK(3, 1)
+	#define USB_R3_P30_SSC_REF_CLK_SEL_SHIFT		4
+	#define USB_R3_P30_SSC_REF_CLK_SEL_MASK			GENMASK(12, 4)
+	#define USB_R3_P30_REF_SSP_EN				BIT(13)
+	#define USB_R3_P30_LOS_BIAS_SHIFT			16
+	#define USB_R3_P30_LOS_BIAS_MASK			GENMASK(18, 16)
+	#define USB_R3_P30_LOS_LEVEL_SHIFT			19
+	#define USB_R3_P30_LOS_LEVEL_MASK			GENMASK(23, 19)
+	#define USB_R3_P30_MPLL_MULTIPLIER_SHIFT		24
+	#define USB_R3_P30_MPLL_MULTIPLIER_MASK			GENMASK(30, 24)
+
+#define USB_R4							0x10
+	#define USB_R4_P21_PORT_RESET_0				BIT(0)
+	#define USB_R4_P21_SLEEP_M0				BIT(1)
+	#define USB_R4_MEM_PD_SHIFT				2
+	#define USB_R4_MEM_PD_MASK				GENMASK(3, 2)
+	#define USB_R4_P21_ONLY					BIT(4)
+
+#define USB_R5							0x14
+	#define USB_R5_ID_DIG_SYNC				BIT(0)
+	#define USB_R5_ID_DIG_REG				BIT(1)
+	#define USB_R5_ID_DIG_CFG_SHIFT				2
+	#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_SHIFT				8
+	#define USB_R5_ID_DIG_TH_MASK				GENMASK(15, 8)
+	#define USB_R5_ID_DIG_CNT_SHIFT				16
+	#define USB_R5_ID_DIG_CNT_MASK				GENMASK(23, 16)
+
+/* read-only register */
+#define USB_R6							0x18
+	#define USB_R6_P30_CR_DATA_OUT_SHIFT			0
+	#define USB_R6_P30_CR_DATA_OUT_MASK			GENMASK(15, 0)
+	#define USB_R6_P30_CR_ACK				BIT(16)
+
+#define RESET_COMPLETE_TIME				500
+
+struct phy_meson_gxl_usb3_priv {
+	struct regmap		*regmap;
+	struct delayed_work	otg_work;
+	struct phy		*this_phy;
+	int			num_usb2_phys;
+	struct phy		**usb2_phys;
+};
+
+static const struct regmap_config phy_meson_gxl_usb3_regmap_conf = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.max_register = USB_R6,
+};
+
+static int phy_meson_gxl_usb3_update_mode(struct phy *phy)
+{
+	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+	u32 val;
+	enum phy_mode mode;
+	int i, ret;
+
+	ret = regmap_read(priv->regmap, USB_R5, &val);
+	if (ret)
+		return ret;
+
+	if (val & USB_R5_ID_DIG_CURR) {
+		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_R4, USB_R4_P21_SLEEP_M0,
+				   USB_R4_P21_SLEEP_M0);
+	} else {
+		mode = PHY_MODE_USB_HOST;
+
+		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);
+	}
+
+	/* inform the USB2 PHY that we have changed the mode */
+	for (i = 0; i < priv->num_usb2_phys; i++) {
+		ret = phy_set_mode(priv->usb2_phys[i], mode);
+		if (ret) {
+			dev_err(&phy->dev,
+				"Failed to update usb2-phy #%d mode to %d\n",
+				i, mode);
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
+static void phy_meson_gxl_usb3_work(struct work_struct *data)
+{
+	struct phy_meson_gxl_usb3_priv *priv =
+		container_of(data, struct phy_meson_gxl_usb3_priv,
+			     otg_work.work);
+
+	phy_meson_gxl_usb3_update_mode(priv->this_phy);
+
+	/* unmask IRQs which may have arrived in the meantime */
+	regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_IRQ, 0);
+}
+
+static int phy_meson_gxl_usb3_init(struct phy *phy)
+{
+	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+	int i, ret;
+
+	for (i = 0; i < priv->num_usb2_phys; i++) {
+		ret = phy_init(priv->usb2_phys[i]);
+		if (ret) {
+			dev_err(&phy->dev,
+				"Failed to initialize related usb2-phy #%d\n",
+				i);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int phy_meson_gxl_usb3_exit(struct phy *phy)
+{
+	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+	int i, ret;
+
+	for (i = 0; i < priv->num_usb2_phys; i++) {
+		ret = phy_exit(priv->usb2_phys[i]);
+		if (ret) {
+			dev_err(&phy->dev,
+				"Failed to exit related usb2-phy #%d\n", i);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int phy_meson_gxl_usb3_power_on(struct phy *phy)
+{
+	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+	int i, ret;
+
+	for (i = 0; i < priv->num_usb2_phys; i++) {
+		ret = phy_power_on(priv->usb2_phys[i]);
+		if (ret) {
+			dev_err(&phy->dev,
+				"Failed to power on related usb2-phy #%d\n",
+				i);
+			return ret;
+		}
+	}
+
+	regmap_update_bits(priv->regmap, USB_R1,
+			   USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
+			   0x20 << USB_R1_U3H_FLADJ_30MHZ_REG_SHIFT);
+
+	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,
+			   0xff << USB_R5_ID_DIG_TH_SHIFT);
+
+	return phy_meson_gxl_usb3_update_mode(phy);
+}
+
+static int phy_meson_gxl_usb3_power_off(struct phy *phy)
+{
+	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+	int i, ret;
+
+	for (i = 0; i < priv->num_usb2_phys; i++) {
+		ret = phy_power_off(priv->usb2_phys[i]);
+		if (ret) {
+			dev_err(&phy->dev,
+				"Failed to power off related usb2-phy #%d\n",
+				i);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static irqreturn_t phy_meson_gxl_usb3_irq(int irq, void *data)
+{
+	u32 val;
+	struct phy_meson_gxl_usb3_priv *priv = data;
+
+	regmap_read(priv->regmap, USB_R5, &val);
+	if (!(val & USB_R5_ID_DIG_IRQ)) {
+		dev_err(&priv->this_phy->dev, "spurious interrupt\n");
+		return IRQ_NONE;
+	}
+
+	schedule_delayed_work(&priv->otg_work, msecs_to_jiffies(10));
+
+	/* acknowledge the IRQ */
+	regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_IRQ, 0);
+
+	return IRQ_HANDLED;
+}
+
+static const struct phy_ops phy_meson_gxl_usb3_ops = {
+	.init		= phy_meson_gxl_usb3_init,
+	.exit		= phy_meson_gxl_usb3_exit,
+	.power_on	= phy_meson_gxl_usb3_power_on,
+	.power_off	= phy_meson_gxl_usb3_power_off,
+	.owner		= THIS_MODULE,
+};
+
+static int phy_meson_gxl_usb3_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct phy_meson_gxl_usb3_priv *priv;
+	struct resource *res;
+	struct phy *phy;
+	struct phy_provider *phy_provider;
+	void __iomem *base;
+	int i, irq;
+
+	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_gxl_usb3_regmap_conf);
+	if (IS_ERR(priv->regmap))
+		return PTR_ERR(priv->regmap);
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq >= 0) {
+		INIT_DELAYED_WORK(&priv->otg_work, phy_meson_gxl_usb3_work);
+
+		irq = devm_request_irq(dev, irq, phy_meson_gxl_usb3_irq,
+				       IRQF_SHARED, dev_name(dev),
+				       priv);
+		if (irq < 0) {
+			dev_err(dev, "could not register IRQ handler (%d)\n",
+				irq);
+			return -EINVAL;
+		}
+	}
+
+	priv->num_usb2_phys = of_count_phandle_with_args(np, "phys",
+							 "#phy-cells");
+
+	priv->usb2_phys = devm_kcalloc(dev, priv->num_usb2_phys,
+				       sizeof(*priv->usb2_phys), GFP_KERNEL);
+	if (!priv->usb2_phys)
+		return -ENOMEM;
+
+	for (i = 0; i < priv->num_usb2_phys; i++) {
+		priv->usb2_phys[i] = devm_of_phy_get_by_index(dev, np, i);
+		if (IS_ERR(priv->usb2_phys[i])) {
+			dev_err(dev, "failed to get related usb2-phy #%d", i);
+			return PTR_ERR(priv->usb2_phys[i]);
+		}
+	}
+
+	phy = devm_phy_create(dev, np, &phy_meson_gxl_usb3_ops);
+	if (IS_ERR(phy)) {
+		dev_err(dev, "failed to create PHY\n");
+		return PTR_ERR(phy);
+	}
+
+	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_gxl_usb3_of_match[] = {
+	{ .compatible = "amlogic,meson-gxl-usb3-phy", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, phy_meson_gxl_usb3_of_match);
+
+static struct platform_driver phy_meson_gxl_usb3_driver = {
+	.probe	= phy_meson_gxl_usb3_probe,
+	.driver	= {
+		.name		= "phy-meson-gxl-usb3",
+		.of_match_table	= phy_meson_gxl_usb3_of_match,
+	},
+};
+module_platform_driver(phy_meson_gxl_usb3_driver);
+
+MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>");
+MODULE_DESCRIPTION("Meson GXL USB3 PHY driver");
+MODULE_LICENSE("GPL");
-- 
2.10.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 2/5] phy: meson: add USB2 and USB3 PHY support for Meson GXL
@ 2016-11-26 14:56     ` Martin Blumenstingl
  0 siblings, 0 replies; 39+ messages in thread
From: Martin Blumenstingl @ 2016-11-26 14:56 UTC (permalink / raw)
  To: linux-arm-kernel

This adds two new USB PHY drivers found on Meson GXL and GXM SoCs.

The registers for the USB2 PHY block handle a maximum of 4 ports (newer
SoCs may allow more ports, the driver handles this as long as the
register length is adjusted in the .dts). The PHY block theoretically
allows powering down each PHY port separately (by putting it into
"reset" state). Unfortunately this does not work (my board has 2 USB
ports, connected to port 1 and 2 of the dwc3's internal hub. When
leaving the third USB PHY disabled then the hub sees that a device is
plugged in, but it does not work: "usb usb1-port2: connect-debounce
failed").
The USB3 PHY will take care of enabling/disabling all available ports,
because the USB3 PHY also manages the mode of the USB2 PHYs.

The USB3 PHY actually has three purposes:
- it provides the USB3 PHY
- it handles the OTG device/host mode detection interrupt
- it notifies the corresponding USB2 PHYs of the OTG mode changes
On GXL and GXM SoCs one references all available USB2 PHY ports in the
USB3 PHY because all are connected to the same USB controller (thus the
mode will always match). This behavior is configurable via devicetree,
by passing (or not passing) a list of other ("child") PHYs which should
be configured by the USB3 PHY.

Unfortunately there are no datasheets available for any of these PHYs.
Both drivers were written by reading the reference drivers provided by
Amlogic and analyzing the registers on the kernel that was shipped with
my board.

Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
---
 drivers/phy/Kconfig              |  13 ++
 drivers/phy/Makefile             |   2 +
 drivers/phy/phy-meson-gxl-usb2.c | 374 ++++++++++++++++++++++++++++++++++++++
 drivers/phy/phy-meson-gxl-usb3.c | 377 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 766 insertions(+)
 create mode 100644 drivers/phy/phy-meson-gxl-usb2.c
 create mode 100644 drivers/phy/phy-meson-gxl-usb3.c

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 728e03f..ea74843 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -502,4 +502,17 @@ config PHY_MESON8B_USB2
 	  and GXBB SoCs.
 	  If unsure, say N.
 
+config PHY_MESON_GXL_USB
+	tristate "Meson GXL USB2 and USB3 PHY drivers"
+	default ARCH_MESON
+	depends on OF && (ARCH_MESON || COMPILE_TEST)
+	depends on USB_SUPPORT
+	select USB_COMMON
+	select GENERIC_PHY
+	select REGMAP_MMIO
+	help
+	  Enable this to support the Meson USB2 and USB3 PHYs found in
+	  Meson GXL SoCs.
+	  If unsure, say N.
+
 endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 0c7fdae..960a96e 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -61,3 +61,5 @@ obj-$(CONFIG_PHY_CYGNUS_PCIE)		+= phy-bcm-cygnus-pcie.o
 obj-$(CONFIG_ARCH_TEGRA) += tegra/
 obj-$(CONFIG_PHY_NS2_PCIE)		+= phy-bcm-ns2-pcie.o
 obj-$(CONFIG_PHY_MESON8B_USB2)		+= phy-meson8b-usb2.o
+obj-$(CONFIG_PHY_MESON_GXL_USB)		+= phy-meson-gxl-usb2.o
+obj-$(CONFIG_PHY_MESON_GXL_USB)		+= phy-meson-gxl-usb3.o
diff --git a/drivers/phy/phy-meson-gxl-usb2.c b/drivers/phy/phy-meson-gxl-usb2.c
new file mode 100644
index 0000000..c081ce3
--- /dev/null
+++ b/drivers/phy/phy-meson-gxl-usb2.c
@@ -0,0 +1,374 @@
+/*
+ * Meson GXL USB2 PHY driver
+ *
+ * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/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>
+#include <linux/usb/of.h>
+
+/* bits [31:27] are read-only */
+#define U2P_R0							0x0
+	#define U2P_R0_BYPASS_SEL				BIT(0)
+	#define U2P_R0_BYPASS_DM_EN				BIT(1)
+	#define U2P_R0_BYPASS_DP_EN				BIT(2)
+	#define U2P_R0_TXBITSTUFF_ENH				BIT(3)
+	#define U2P_R0_TXBITSTUFF_EN				BIT(4)
+	#define U2P_R0_DM_PULLDOWN				BIT(5)
+	#define U2P_R0_DP_PULLDOWN				BIT(6)
+	#define U2P_R0_DP_VBUS_VLD_EXT_SEL			BIT(7)
+	#define U2P_R0_DP_VBUS_VLD_EXT				BIT(8)
+	#define U2P_R0_ADP_PRB_EN				BIT(9)
+	#define U2P_R0_ADP_DISCHARGE				BIT(10)
+	#define U2P_R0_ADP_CHARGE				BIT(11)
+	#define U2P_R0_DRV_VBUS					BIT(12)
+	#define U2P_R0_ID_PULLUP				BIT(13)
+	#define U2P_R0_LOOPBACK_EN_B				BIT(14)
+	#define U2P_R0_OTG_DISABLE				BIT(15)
+	#define U2P_R0_COMMON_ONN				BIT(16)
+	#define U2P_R0_FSEL_SHIFT				17
+	#define U2P_R0_FSEL_MASK				GENMASK(19, 17)
+	#define U2P_R0_REF_CLK_SEL_SHIFT			20
+	#define U2P_R0_REF_CLK_SEL_MASK				GENMASK(21, 20)
+	#define U2P_R0_POWER_ON_RESET				BIT(22)
+	#define U2P_R0_V_ATE_TEST_EN_B_SHIFT			23
+	#define U2P_R0_V_ATE_TEST_EN_B_MASK			GENMASK(24, 23)
+	#define U2P_R0_ID_SET_ID_DQ				BIT(25)
+	#define U2P_R0_ATE_RESET				BIT(26)
+	#define U2P_R0_FSV_MINUS				BIT(27)
+	#define U2P_R0_FSV_PLUS					BIT(28)
+	#define U2P_R0_BYPASS_DM_DATA				BIT(29)
+	#define U2P_R0_BYPASS_DP_DATA				BIT(30)
+
+#define U2P_R1							0x4
+	#define U2P_R1_BURN_IN_TEST				BIT(0)
+	#define U2P_R1_ACA_ENABLE				BIT(1)
+	#define U2P_R1_DCD_ENABLE				BIT(2)
+	#define U2P_R1_VDAT_SRC_EN_B				BIT(3)
+	#define U2P_R1_VDAT_DET_EN_B				BIT(4)
+	#define U2P_R1_CHARGES_SEL				BIT(5)
+	#define U2P_R1_TX_PREEMP_PULSE_TUNE			BIT(6)
+	#define U2P_R1_TX_PREEMP_AMP_TUNE_SHIFT			7
+	#define U2P_R1_TX_PREEMP_AMP_TUNE_MASK			GENMASK(8, 7)
+	#define U2P_R1_TX_RES_TUNE_SHIFT			9
+	#define U2P_R1_TX_RES_TUNE_MASK				GENMASK(10, 9)
+	#define U2P_R1_TX_RISE_TUNE_SHIFT			11
+	#define U2P_R1_TX_RISE_TUNE_MASK			GENMASK(12, 11)
+	#define U2P_R1_TX_VREF_TUNE_SHIFT			13
+	#define U2P_R1_TX_VREF_TUNE_MASK			GENMASK(16, 13)
+	#define U2P_R1_TX_FSLS_TUNE_SHIFT			17
+	#define U2P_R1_TX_FSLS_TUNE_MASK			GENMASK(20, 17)
+	#define U2P_R1_TX_HSXV_TUNE_SHIFT			21
+	#define U2P_R1_TX_HSXV_TUNE_MASK			GENMASK(22, 21)
+	#define U2P_R1_OTG_TUNE_SHIFT				23
+	#define U2P_R1_OTG_TUNE_MASK				GENMASK(25, 23)
+	#define U2P_R1_SQRX_TUNE_SHIFT				26
+	#define U2P_R1_SQRX_TUNE_MASK				GENMASK(28, 26)
+	#define U2P_R1_COMP_DIS_TUNE_SHIFT			29
+	#define U2P_R1_COMP_DIS_TUNE_MASK			GENMASK(31, 29)
+
+/* bits [31:14] are read-only */
+#define U2P_R2							0x8
+	#define U2P_R2_DATA_IN_SHIFT				0
+	#define U2P_R2_DATA_IN_MASK				GENMASK(3, 0)
+	#define U2P_R2_DATA_IN_EN_SHIFT				4
+	#define U2P_R2_DATA_IN_EN_MASK				GENMASK(7, 4)
+	#define U2P_R2_ADDR_SHIFT				8
+	#define U2P_R2_ADDR_MASK				GENMASK(11, 8)
+	#define U2P_R2_DATA_OUT_SEL				BIT(12)
+	#define U2P_R2_CLK					BIT(13)
+	#define U2P_R2_DATA_OUT_SHIFT				14
+	#define U2P_R2_DATA_OUT_MASK				GENMASK(17, 14)
+	#define U2P_R2_ACA_PIN_RANGE_C				BIT(18)
+	#define U2P_R2_ACA_PIN_RANGE_B				BIT(19)
+	#define U2P_R2_ACA_PIN_RANGE_A				BIT(20)
+	#define U2P_R2_ACA_PIN_GND				BIT(21)
+	#define U2P_R2_ACA_PIN_FLOAT				BIT(22)
+	#define U2P_R2_CHARGE_DETECT				BIT(23)
+	#define U2P_R2_DEVICE_SESSION_VALID			BIT(24)
+	#define U2P_R2_ADP_PROBE				BIT(25)
+	#define U2P_R2_ADP_SENSE				BIT(26)
+	#define U2P_R2_SESSION_END				BIT(27)
+	#define U2P_R2_VBUS_VALID				BIT(28)
+	#define U2P_R2_B_VALID					BIT(29)
+	#define U2P_R2_A_VALID					BIT(30)
+	#define U2P_R2_ID_DIG					BIT(31)
+
+#define U2P_R3							0xc
+
+#define PHY_PORT_RESOURCE_SIZE					0x20
+
+#define RESET_COMPLETE_TIME				500
+
+struct phy_meson_gxl_usb2_priv {
+	struct regmap		*regmap;
+	enum phy_mode		mode;
+};
+
+struct phy_meson_gxl_usb2_drv {
+	void __iomem		*base;
+	int			num_ports;
+	struct phy		**ports;
+	struct clk		*clk_usb;
+	struct clk		*clk_usb_ddr;
+};
+
+static const struct regmap_config phy_meson_gxl_usb2_regmap_conf = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.max_register = U2P_R3,
+};
+
+static int phy_meson_gxl_usb2_set_mode(struct phy *phy, enum phy_mode mode)
+{
+	struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
+
+	switch (mode) {
+	case PHY_MODE_USB_HOST:
+	case PHY_MODE_USB_OTG:
+		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DM_PULLDOWN,
+				   U2P_R0_DM_PULLDOWN);
+		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DP_PULLDOWN,
+				   U2P_R0_DP_PULLDOWN);
+		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_ID_PULLUP, 0);
+		break;
+
+	case PHY_MODE_USB_DEVICE:
+		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DM_PULLDOWN,
+				   0);
+		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DP_PULLDOWN,
+				   0);
+		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_ID_PULLUP,
+				   U2P_R0_ID_PULLUP);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	/* reset the PHY and wait until settings are stabilized */
+	regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET,
+			   U2P_R0_POWER_ON_RESET);
+	udelay(RESET_COMPLETE_TIME);
+	regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET, 0);
+	udelay(RESET_COMPLETE_TIME);
+
+	priv->mode = mode;
+
+	return 0;
+}
+
+static int phy_meson_gxl_usb2_power_off(struct phy *phy)
+{
+	struct phy_meson_gxl_usb2_drv *drv_priv =
+		dev_get_drvdata(phy->dev.parent);
+	struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
+
+	/* power off the PHY by putting it into reset mode */
+	regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET,
+			   U2P_R0_POWER_ON_RESET);
+
+	clk_disable_unprepare(drv_priv->clk_usb_ddr);
+	clk_disable_unprepare(drv_priv->clk_usb);
+
+	return 0;
+}
+
+static int phy_meson_gxl_usb2_power_on(struct phy *phy)
+{
+	struct phy_meson_gxl_usb2_drv *drv_priv =
+		dev_get_drvdata(phy->dev.parent);
+	struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
+	int ret;
+
+	ret = clk_prepare_enable(drv_priv->clk_usb);
+	if (ret) {
+		dev_err(&phy->dev, "Failed to enable USB clock\n");
+		return ret;
+	}
+
+	ret = clk_prepare_enable(drv_priv->clk_usb_ddr);
+	if (ret) {
+		clk_disable_unprepare(drv_priv->clk_usb);
+
+		dev_err(&phy->dev, "Failed to enable USB DDR clock\n");
+		return ret;
+	}
+
+	/* power on the PHY by taking it out of reset mode */
+	regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET, 0);
+
+	ret = phy_meson_gxl_usb2_set_mode(phy, priv->mode);
+	if (ret) {
+		phy_meson_gxl_usb2_power_off(phy);
+
+		dev_err(&phy->dev, "Failed to initialize PHY with mode %d\n",
+			priv->mode);
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct phy_ops phy_meson_gxl_usb2_ops = {
+	.power_on	= phy_meson_gxl_usb2_power_on,
+	.power_off	= phy_meson_gxl_usb2_power_off,
+	.set_mode	= phy_meson_gxl_usb2_set_mode,
+	.owner		= THIS_MODULE,
+};
+
+static struct phy *phy_meson_gxl_usb2_of_xlate(struct device *dev,
+					       struct of_phandle_args *args)
+{
+	struct phy_meson_gxl_usb2_drv *priv = dev_get_drvdata(dev);
+	int port;
+
+	if (args->args_count != 1) {
+		dev_err(dev, "Invalid number of cells in 'phy' property\n");
+		return ERR_PTR(-ENODEV);
+	}
+
+	port = args->args[0];
+	if (WARN_ON(port >= priv->num_ports))
+		return ERR_PTR(-ENODEV);
+
+	return priv->ports[port];
+}
+
+static int phy_meson_gxl_usb2_probe_port(struct device *dev, int port)
+{
+	struct phy_meson_gxl_usb2_drv *drv_priv = dev_get_drvdata(dev);
+	struct phy_meson_gxl_usb2_priv *phy_priv;
+	struct phy *phy;
+	void __iomem *port_base;
+
+	phy_priv = devm_kzalloc(dev, sizeof(*phy_priv), GFP_KERNEL);
+	if (!phy_priv)
+		return -ENOMEM;
+
+	switch (of_usb_get_dr_mode_by_phy(dev->of_node, port)) {
+	case USB_DR_MODE_PERIPHERAL:
+		phy_priv->mode = PHY_MODE_USB_DEVICE;
+		break;
+	case USB_DR_MODE_OTG:
+		phy_priv->mode = PHY_MODE_USB_OTG;
+		break;
+	case USB_DR_MODE_HOST:
+	default:
+		phy_priv->mode = PHY_MODE_USB_HOST;
+		break;
+	}
+
+	phy = devm_phy_create(dev, NULL, &phy_meson_gxl_usb2_ops);
+	if (IS_ERR(phy)) {
+		dev_err(dev, "failed to create PHY port %d\n", port);
+		return PTR_ERR(phy);
+	}
+
+	port_base = drv_priv->base + (port * PHY_PORT_RESOURCE_SIZE);
+	phy_priv->regmap = devm_regmap_init_mmio(&phy->dev, port_base,
+						 &phy_meson_gxl_usb2_regmap_conf);
+	if (IS_ERR(phy_priv->regmap))
+		return PTR_ERR(phy_priv->regmap);
+
+	phy_set_drvdata(phy, phy_priv);
+
+	drv_priv->ports[port] = phy;
+
+	return 0;
+}
+
+static int phy_meson_gxl_usb2_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct phy_meson_gxl_usb2_drv *priv;
+	struct phy_provider *phy_provider;
+	struct resource *res;
+	int i, ret;
+
+	ret = device_reset(dev);
+	if (ret) {
+		dev_err(dev, "failed to reset device\n");
+		return ret;
+	}
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, priv);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(priv->base))
+		return PTR_ERR(priv->base);
+
+	priv->num_ports = resource_size(res) / PHY_PORT_RESOURCE_SIZE;
+	if (priv->num_ports < 1) {
+		dev_err(dev, "specified memory range is too small\n");
+		return -EINVAL;
+	}
+
+	priv->ports = devm_kcalloc(dev, priv->num_ports, sizeof(*priv->ports),
+				   GFP_KERNEL);
+	if (!priv->ports)
+		return -ENOMEM;
+
+	priv->clk_usb = devm_clk_get(dev, "usb");
+	if (IS_ERR(priv->clk_usb)) {
+		dev_err(dev, "failed to get USB clock\n");
+		return PTR_ERR(priv->clk_usb);
+	}
+
+	priv->clk_usb_ddr = devm_clk_get(dev, "usb_ddr");
+	if (IS_ERR(priv->clk_usb_ddr)) {
+		dev_err(dev, "failed to get USB DDR clock\n");
+		return PTR_ERR(priv->clk_usb_ddr);
+	}
+
+	for (i = 0; i < priv->num_ports; i++) {
+		ret = phy_meson_gxl_usb2_probe_port(dev, i);
+		if (ret)
+			return ret;
+	}
+
+	phy_provider = devm_of_phy_provider_register(dev,
+						     phy_meson_gxl_usb2_of_xlate);
+
+	return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id phy_meson_gxl_usb2_of_match[] = {
+	{ .compatible = "amlogic,meson-gxl-usb2-phy", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, phy_meson_gxl_usb2_of_match);
+
+static struct platform_driver phy_meson_gxl_usb2_driver = {
+	.probe	= phy_meson_gxl_usb2_probe,
+	.driver	= {
+		.name		= "phy-meson-gxl-usb2",
+		.of_match_table	= phy_meson_gxl_usb2_of_match,
+	},
+};
+module_platform_driver(phy_meson_gxl_usb2_driver);
+
+MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
+MODULE_DESCRIPTION("Meson GXL USB2 PHY driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/phy-meson-gxl-usb3.c b/drivers/phy/phy-meson-gxl-usb3.c
new file mode 100644
index 0000000..90a4028
--- /dev/null
+++ b/drivers/phy/phy-meson-gxl-usb3.c
@@ -0,0 +1,377 @@
+/*
+ * Meson GXL USB3 PHY driver
+ *
+ * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/reset.h>
+#include <linux/regmap.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/usb/of.h>
+#include <linux/workqueue.h>
+
+#define USB_R0							0x00
+	#define USB_R0_P30_FSEL_SHIFT				0
+	#define USB_R0_P30_FSEL_MASK				GENMASK(5, 0)
+	#define USB_R0_P30_PHY_RESET				BIT(6)
+	#define USB_R0_P30_TEST_POWERDOWN_HSP			BIT(7)
+	#define USB_R0_P30_TEST_POWERDOWN_SSP			BIT(8)
+	#define USB_R0_P30_ACJT_LEVEL_SHIFT			9
+	#define USB_R0_P30_ACJT_LEVEL_MASK			GENMASK(13, 9)
+	#define USB_R0_P30_TX_BOOST_LEVEL_SHIFT			14
+	#define USB_R0_P30_TX_BOOST_LEVEL_MASK			GENMASK(16, 14)
+	#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_SHIFT		19
+	#define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK		GENMASK(28, 19)
+	#define USB_R0_U2D_SS_SCALEDOWN_MODE_SHIFT		29
+	#define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK		GENMASK(30, 29)
+	#define USB_R0_U2D_ACT					BIT(31)
+
+#define USB_R1							0x04
+	#define USB_R1_U3H_BIGENDIAN_GS				BIT(0)
+	#define USB_R1_U3H_PME_ENABLE				BIT(1)
+	#define USB_R1_U3H_HUB_PORT_OVERCURRENT_SHIFT		2
+	#define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK		GENMASK(6, 2)
+	#define USB_R1_U3H_HUB_PORT_PERM_ATTACH_SHIFT		7
+	#define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK		GENMASK(11, 7)
+	#define USB_R1_U3H_HOST_U2_PORT_DISABLE_SHIFT		12
+	#define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK		GENMASK(15, 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_SHIFT		19
+	#define USB_R1_U3H_FLADJ_30MHZ_REG_MASK			GENMASK(24, 19)
+	#define USB_R1_P30_PCS_TX_SWING_FULL_SHIFT		25
+	#define USB_R1_P30_PCS_TX_SWING_FULL_MASK		GENMASK(31, 25)
+
+#define USB_R2							0x08
+	#define USB_R2_P30_CR_DATA_IN_SHIFT			0
+	#define USB_R2_P30_CR_DATA_IN_MASK			GENMASK(15, 0)
+	#define USB_R2_P30_CR_READ				BIT(16)
+	#define USB_R2_P30_CR_WRITE				BIT(17)
+	#define USB_R2_P30_CR_CAP_ADDR				BIT(18)
+	#define USB_R2_P30_CR_CAP_DATA				BIT(19)
+	#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_SHIFT		20
+	#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK		GENMASK(25, 20)
+	#define USB_R2_P30_PCS_TX_DEEMPH_6DB_SHIFT		26
+	#define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK		GENMASK(31, 26)
+
+#define USB_R3							0x0c
+	#define USB_R3_P30_SSC_ENABLE				BIT(0)
+	#define USB_R3_P30_SSC_RANGE_SHIFT			1
+	#define USB_R3_P30_SSC_RANGE_MASK			GENMASK(3, 1)
+	#define USB_R3_P30_SSC_REF_CLK_SEL_SHIFT		4
+	#define USB_R3_P30_SSC_REF_CLK_SEL_MASK			GENMASK(12, 4)
+	#define USB_R3_P30_REF_SSP_EN				BIT(13)
+	#define USB_R3_P30_LOS_BIAS_SHIFT			16
+	#define USB_R3_P30_LOS_BIAS_MASK			GENMASK(18, 16)
+	#define USB_R3_P30_LOS_LEVEL_SHIFT			19
+	#define USB_R3_P30_LOS_LEVEL_MASK			GENMASK(23, 19)
+	#define USB_R3_P30_MPLL_MULTIPLIER_SHIFT		24
+	#define USB_R3_P30_MPLL_MULTIPLIER_MASK			GENMASK(30, 24)
+
+#define USB_R4							0x10
+	#define USB_R4_P21_PORT_RESET_0				BIT(0)
+	#define USB_R4_P21_SLEEP_M0				BIT(1)
+	#define USB_R4_MEM_PD_SHIFT				2
+	#define USB_R4_MEM_PD_MASK				GENMASK(3, 2)
+	#define USB_R4_P21_ONLY					BIT(4)
+
+#define USB_R5							0x14
+	#define USB_R5_ID_DIG_SYNC				BIT(0)
+	#define USB_R5_ID_DIG_REG				BIT(1)
+	#define USB_R5_ID_DIG_CFG_SHIFT				2
+	#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_SHIFT				8
+	#define USB_R5_ID_DIG_TH_MASK				GENMASK(15, 8)
+	#define USB_R5_ID_DIG_CNT_SHIFT				16
+	#define USB_R5_ID_DIG_CNT_MASK				GENMASK(23, 16)
+
+/* read-only register */
+#define USB_R6							0x18
+	#define USB_R6_P30_CR_DATA_OUT_SHIFT			0
+	#define USB_R6_P30_CR_DATA_OUT_MASK			GENMASK(15, 0)
+	#define USB_R6_P30_CR_ACK				BIT(16)
+
+#define RESET_COMPLETE_TIME				500
+
+struct phy_meson_gxl_usb3_priv {
+	struct regmap		*regmap;
+	struct delayed_work	otg_work;
+	struct phy		*this_phy;
+	int			num_usb2_phys;
+	struct phy		**usb2_phys;
+};
+
+static const struct regmap_config phy_meson_gxl_usb3_regmap_conf = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.max_register = USB_R6,
+};
+
+static int phy_meson_gxl_usb3_update_mode(struct phy *phy)
+{
+	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+	u32 val;
+	enum phy_mode mode;
+	int i, ret;
+
+	ret = regmap_read(priv->regmap, USB_R5, &val);
+	if (ret)
+		return ret;
+
+	if (val & USB_R5_ID_DIG_CURR) {
+		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_R4, USB_R4_P21_SLEEP_M0,
+				   USB_R4_P21_SLEEP_M0);
+	} else {
+		mode = PHY_MODE_USB_HOST;
+
+		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);
+	}
+
+	/* inform the USB2 PHY that we have changed the mode */
+	for (i = 0; i < priv->num_usb2_phys; i++) {
+		ret = phy_set_mode(priv->usb2_phys[i], mode);
+		if (ret) {
+			dev_err(&phy->dev,
+				"Failed to update usb2-phy #%d mode to %d\n",
+				i, mode);
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
+static void phy_meson_gxl_usb3_work(struct work_struct *data)
+{
+	struct phy_meson_gxl_usb3_priv *priv =
+		container_of(data, struct phy_meson_gxl_usb3_priv,
+			     otg_work.work);
+
+	phy_meson_gxl_usb3_update_mode(priv->this_phy);
+
+	/* unmask IRQs which may have arrived in the meantime */
+	regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_IRQ, 0);
+}
+
+static int phy_meson_gxl_usb3_init(struct phy *phy)
+{
+	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+	int i, ret;
+
+	for (i = 0; i < priv->num_usb2_phys; i++) {
+		ret = phy_init(priv->usb2_phys[i]);
+		if (ret) {
+			dev_err(&phy->dev,
+				"Failed to initialize related usb2-phy #%d\n",
+				i);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int phy_meson_gxl_usb3_exit(struct phy *phy)
+{
+	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+	int i, ret;
+
+	for (i = 0; i < priv->num_usb2_phys; i++) {
+		ret = phy_exit(priv->usb2_phys[i]);
+		if (ret) {
+			dev_err(&phy->dev,
+				"Failed to exit related usb2-phy #%d\n", i);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int phy_meson_gxl_usb3_power_on(struct phy *phy)
+{
+	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+	int i, ret;
+
+	for (i = 0; i < priv->num_usb2_phys; i++) {
+		ret = phy_power_on(priv->usb2_phys[i]);
+		if (ret) {
+			dev_err(&phy->dev,
+				"Failed to power on related usb2-phy #%d\n",
+				i);
+			return ret;
+		}
+	}
+
+	regmap_update_bits(priv->regmap, USB_R1,
+			   USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
+			   0x20 << USB_R1_U3H_FLADJ_30MHZ_REG_SHIFT);
+
+	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,
+			   0xff << USB_R5_ID_DIG_TH_SHIFT);
+
+	return phy_meson_gxl_usb3_update_mode(phy);
+}
+
+static int phy_meson_gxl_usb3_power_off(struct phy *phy)
+{
+	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+	int i, ret;
+
+	for (i = 0; i < priv->num_usb2_phys; i++) {
+		ret = phy_power_off(priv->usb2_phys[i]);
+		if (ret) {
+			dev_err(&phy->dev,
+				"Failed to power off related usb2-phy #%d\n",
+				i);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static irqreturn_t phy_meson_gxl_usb3_irq(int irq, void *data)
+{
+	u32 val;
+	struct phy_meson_gxl_usb3_priv *priv = data;
+
+	regmap_read(priv->regmap, USB_R5, &val);
+	if (!(val & USB_R5_ID_DIG_IRQ)) {
+		dev_err(&priv->this_phy->dev, "spurious interrupt\n");
+		return IRQ_NONE;
+	}
+
+	schedule_delayed_work(&priv->otg_work, msecs_to_jiffies(10));
+
+	/* acknowledge the IRQ */
+	regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_IRQ, 0);
+
+	return IRQ_HANDLED;
+}
+
+static const struct phy_ops phy_meson_gxl_usb3_ops = {
+	.init		= phy_meson_gxl_usb3_init,
+	.exit		= phy_meson_gxl_usb3_exit,
+	.power_on	= phy_meson_gxl_usb3_power_on,
+	.power_off	= phy_meson_gxl_usb3_power_off,
+	.owner		= THIS_MODULE,
+};
+
+static int phy_meson_gxl_usb3_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct phy_meson_gxl_usb3_priv *priv;
+	struct resource *res;
+	struct phy *phy;
+	struct phy_provider *phy_provider;
+	void __iomem *base;
+	int i, irq;
+
+	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_gxl_usb3_regmap_conf);
+	if (IS_ERR(priv->regmap))
+		return PTR_ERR(priv->regmap);
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq >= 0) {
+		INIT_DELAYED_WORK(&priv->otg_work, phy_meson_gxl_usb3_work);
+
+		irq = devm_request_irq(dev, irq, phy_meson_gxl_usb3_irq,
+				       IRQF_SHARED, dev_name(dev),
+				       priv);
+		if (irq < 0) {
+			dev_err(dev, "could not register IRQ handler (%d)\n",
+				irq);
+			return -EINVAL;
+		}
+	}
+
+	priv->num_usb2_phys = of_count_phandle_with_args(np, "phys",
+							 "#phy-cells");
+
+	priv->usb2_phys = devm_kcalloc(dev, priv->num_usb2_phys,
+				       sizeof(*priv->usb2_phys), GFP_KERNEL);
+	if (!priv->usb2_phys)
+		return -ENOMEM;
+
+	for (i = 0; i < priv->num_usb2_phys; i++) {
+		priv->usb2_phys[i] = devm_of_phy_get_by_index(dev, np, i);
+		if (IS_ERR(priv->usb2_phys[i])) {
+			dev_err(dev, "failed to get related usb2-phy #%d", i);
+			return PTR_ERR(priv->usb2_phys[i]);
+		}
+	}
+
+	phy = devm_phy_create(dev, np, &phy_meson_gxl_usb3_ops);
+	if (IS_ERR(phy)) {
+		dev_err(dev, "failed to create PHY\n");
+		return PTR_ERR(phy);
+	}
+
+	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_gxl_usb3_of_match[] = {
+	{ .compatible = "amlogic,meson-gxl-usb3-phy", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, phy_meson_gxl_usb3_of_match);
+
+static struct platform_driver phy_meson_gxl_usb3_driver = {
+	.probe	= phy_meson_gxl_usb3_probe,
+	.driver	= {
+		.name		= "phy-meson-gxl-usb3",
+		.of_match_table	= phy_meson_gxl_usb3_of_match,
+	},
+};
+module_platform_driver(phy_meson_gxl_usb3_driver);
+
+MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
+MODULE_DESCRIPTION("Meson GXL USB3 PHY driver");
+MODULE_LICENSE("GPL");
-- 
2.10.2

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

* [PATCH 2/5] phy: meson: add USB2 and USB3 PHY support for Meson GXL
@ 2016-11-26 14:56     ` Martin Blumenstingl
  0 siblings, 0 replies; 39+ messages in thread
From: Martin Blumenstingl @ 2016-11-26 14:56 UTC (permalink / raw)
  To: linus-amlogic

This adds two new USB PHY drivers found on Meson GXL and GXM SoCs.

The registers for the USB2 PHY block handle a maximum of 4 ports (newer
SoCs may allow more ports, the driver handles this as long as the
register length is adjusted in the .dts). The PHY block theoretically
allows powering down each PHY port separately (by putting it into
"reset" state). Unfortunately this does not work (my board has 2 USB
ports, connected to port 1 and 2 of the dwc3's internal hub. When
leaving the third USB PHY disabled then the hub sees that a device is
plugged in, but it does not work: "usb usb1-port2: connect-debounce
failed").
The USB3 PHY will take care of enabling/disabling all available ports,
because the USB3 PHY also manages the mode of the USB2 PHYs.

The USB3 PHY actually has three purposes:
- it provides the USB3 PHY
- it handles the OTG device/host mode detection interrupt
- it notifies the corresponding USB2 PHYs of the OTG mode changes
On GXL and GXM SoCs one references all available USB2 PHY ports in the
USB3 PHY because all are connected to the same USB controller (thus the
mode will always match). This behavior is configurable via devicetree,
by passing (or not passing) a list of other ("child") PHYs which should
be configured by the USB3 PHY.

Unfortunately there are no datasheets available for any of these PHYs.
Both drivers were written by reading the reference drivers provided by
Amlogic and analyzing the registers on the kernel that was shipped with
my board.

Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
---
 drivers/phy/Kconfig              |  13 ++
 drivers/phy/Makefile             |   2 +
 drivers/phy/phy-meson-gxl-usb2.c | 374 ++++++++++++++++++++++++++++++++++++++
 drivers/phy/phy-meson-gxl-usb3.c | 377 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 766 insertions(+)
 create mode 100644 drivers/phy/phy-meson-gxl-usb2.c
 create mode 100644 drivers/phy/phy-meson-gxl-usb3.c

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 728e03f..ea74843 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -502,4 +502,17 @@ config PHY_MESON8B_USB2
 	  and GXBB SoCs.
 	  If unsure, say N.
 
+config PHY_MESON_GXL_USB
+	tristate "Meson GXL USB2 and USB3 PHY drivers"
+	default ARCH_MESON
+	depends on OF && (ARCH_MESON || COMPILE_TEST)
+	depends on USB_SUPPORT
+	select USB_COMMON
+	select GENERIC_PHY
+	select REGMAP_MMIO
+	help
+	  Enable this to support the Meson USB2 and USB3 PHYs found in
+	  Meson GXL SoCs.
+	  If unsure, say N.
+
 endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 0c7fdae..960a96e 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -61,3 +61,5 @@ obj-$(CONFIG_PHY_CYGNUS_PCIE)		+= phy-bcm-cygnus-pcie.o
 obj-$(CONFIG_ARCH_TEGRA) += tegra/
 obj-$(CONFIG_PHY_NS2_PCIE)		+= phy-bcm-ns2-pcie.o
 obj-$(CONFIG_PHY_MESON8B_USB2)		+= phy-meson8b-usb2.o
+obj-$(CONFIG_PHY_MESON_GXL_USB)		+= phy-meson-gxl-usb2.o
+obj-$(CONFIG_PHY_MESON_GXL_USB)		+= phy-meson-gxl-usb3.o
diff --git a/drivers/phy/phy-meson-gxl-usb2.c b/drivers/phy/phy-meson-gxl-usb2.c
new file mode 100644
index 0000000..c081ce3
--- /dev/null
+++ b/drivers/phy/phy-meson-gxl-usb2.c
@@ -0,0 +1,374 @@
+/*
+ * Meson GXL USB2 PHY driver
+ *
+ * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/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>
+#include <linux/usb/of.h>
+
+/* bits [31:27] are read-only */
+#define U2P_R0							0x0
+	#define U2P_R0_BYPASS_SEL				BIT(0)
+	#define U2P_R0_BYPASS_DM_EN				BIT(1)
+	#define U2P_R0_BYPASS_DP_EN				BIT(2)
+	#define U2P_R0_TXBITSTUFF_ENH				BIT(3)
+	#define U2P_R0_TXBITSTUFF_EN				BIT(4)
+	#define U2P_R0_DM_PULLDOWN				BIT(5)
+	#define U2P_R0_DP_PULLDOWN				BIT(6)
+	#define U2P_R0_DP_VBUS_VLD_EXT_SEL			BIT(7)
+	#define U2P_R0_DP_VBUS_VLD_EXT				BIT(8)
+	#define U2P_R0_ADP_PRB_EN				BIT(9)
+	#define U2P_R0_ADP_DISCHARGE				BIT(10)
+	#define U2P_R0_ADP_CHARGE				BIT(11)
+	#define U2P_R0_DRV_VBUS					BIT(12)
+	#define U2P_R0_ID_PULLUP				BIT(13)
+	#define U2P_R0_LOOPBACK_EN_B				BIT(14)
+	#define U2P_R0_OTG_DISABLE				BIT(15)
+	#define U2P_R0_COMMON_ONN				BIT(16)
+	#define U2P_R0_FSEL_SHIFT				17
+	#define U2P_R0_FSEL_MASK				GENMASK(19, 17)
+	#define U2P_R0_REF_CLK_SEL_SHIFT			20
+	#define U2P_R0_REF_CLK_SEL_MASK				GENMASK(21, 20)
+	#define U2P_R0_POWER_ON_RESET				BIT(22)
+	#define U2P_R0_V_ATE_TEST_EN_B_SHIFT			23
+	#define U2P_R0_V_ATE_TEST_EN_B_MASK			GENMASK(24, 23)
+	#define U2P_R0_ID_SET_ID_DQ				BIT(25)
+	#define U2P_R0_ATE_RESET				BIT(26)
+	#define U2P_R0_FSV_MINUS				BIT(27)
+	#define U2P_R0_FSV_PLUS					BIT(28)
+	#define U2P_R0_BYPASS_DM_DATA				BIT(29)
+	#define U2P_R0_BYPASS_DP_DATA				BIT(30)
+
+#define U2P_R1							0x4
+	#define U2P_R1_BURN_IN_TEST				BIT(0)
+	#define U2P_R1_ACA_ENABLE				BIT(1)
+	#define U2P_R1_DCD_ENABLE				BIT(2)
+	#define U2P_R1_VDAT_SRC_EN_B				BIT(3)
+	#define U2P_R1_VDAT_DET_EN_B				BIT(4)
+	#define U2P_R1_CHARGES_SEL				BIT(5)
+	#define U2P_R1_TX_PREEMP_PULSE_TUNE			BIT(6)
+	#define U2P_R1_TX_PREEMP_AMP_TUNE_SHIFT			7
+	#define U2P_R1_TX_PREEMP_AMP_TUNE_MASK			GENMASK(8, 7)
+	#define U2P_R1_TX_RES_TUNE_SHIFT			9
+	#define U2P_R1_TX_RES_TUNE_MASK				GENMASK(10, 9)
+	#define U2P_R1_TX_RISE_TUNE_SHIFT			11
+	#define U2P_R1_TX_RISE_TUNE_MASK			GENMASK(12, 11)
+	#define U2P_R1_TX_VREF_TUNE_SHIFT			13
+	#define U2P_R1_TX_VREF_TUNE_MASK			GENMASK(16, 13)
+	#define U2P_R1_TX_FSLS_TUNE_SHIFT			17
+	#define U2P_R1_TX_FSLS_TUNE_MASK			GENMASK(20, 17)
+	#define U2P_R1_TX_HSXV_TUNE_SHIFT			21
+	#define U2P_R1_TX_HSXV_TUNE_MASK			GENMASK(22, 21)
+	#define U2P_R1_OTG_TUNE_SHIFT				23
+	#define U2P_R1_OTG_TUNE_MASK				GENMASK(25, 23)
+	#define U2P_R1_SQRX_TUNE_SHIFT				26
+	#define U2P_R1_SQRX_TUNE_MASK				GENMASK(28, 26)
+	#define U2P_R1_COMP_DIS_TUNE_SHIFT			29
+	#define U2P_R1_COMP_DIS_TUNE_MASK			GENMASK(31, 29)
+
+/* bits [31:14] are read-only */
+#define U2P_R2							0x8
+	#define U2P_R2_DATA_IN_SHIFT				0
+	#define U2P_R2_DATA_IN_MASK				GENMASK(3, 0)
+	#define U2P_R2_DATA_IN_EN_SHIFT				4
+	#define U2P_R2_DATA_IN_EN_MASK				GENMASK(7, 4)
+	#define U2P_R2_ADDR_SHIFT				8
+	#define U2P_R2_ADDR_MASK				GENMASK(11, 8)
+	#define U2P_R2_DATA_OUT_SEL				BIT(12)
+	#define U2P_R2_CLK					BIT(13)
+	#define U2P_R2_DATA_OUT_SHIFT				14
+	#define U2P_R2_DATA_OUT_MASK				GENMASK(17, 14)
+	#define U2P_R2_ACA_PIN_RANGE_C				BIT(18)
+	#define U2P_R2_ACA_PIN_RANGE_B				BIT(19)
+	#define U2P_R2_ACA_PIN_RANGE_A				BIT(20)
+	#define U2P_R2_ACA_PIN_GND				BIT(21)
+	#define U2P_R2_ACA_PIN_FLOAT				BIT(22)
+	#define U2P_R2_CHARGE_DETECT				BIT(23)
+	#define U2P_R2_DEVICE_SESSION_VALID			BIT(24)
+	#define U2P_R2_ADP_PROBE				BIT(25)
+	#define U2P_R2_ADP_SENSE				BIT(26)
+	#define U2P_R2_SESSION_END				BIT(27)
+	#define U2P_R2_VBUS_VALID				BIT(28)
+	#define U2P_R2_B_VALID					BIT(29)
+	#define U2P_R2_A_VALID					BIT(30)
+	#define U2P_R2_ID_DIG					BIT(31)
+
+#define U2P_R3							0xc
+
+#define PHY_PORT_RESOURCE_SIZE					0x20
+
+#define RESET_COMPLETE_TIME				500
+
+struct phy_meson_gxl_usb2_priv {
+	struct regmap		*regmap;
+	enum phy_mode		mode;
+};
+
+struct phy_meson_gxl_usb2_drv {
+	void __iomem		*base;
+	int			num_ports;
+	struct phy		**ports;
+	struct clk		*clk_usb;
+	struct clk		*clk_usb_ddr;
+};
+
+static const struct regmap_config phy_meson_gxl_usb2_regmap_conf = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.max_register = U2P_R3,
+};
+
+static int phy_meson_gxl_usb2_set_mode(struct phy *phy, enum phy_mode mode)
+{
+	struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
+
+	switch (mode) {
+	case PHY_MODE_USB_HOST:
+	case PHY_MODE_USB_OTG:
+		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DM_PULLDOWN,
+				   U2P_R0_DM_PULLDOWN);
+		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DP_PULLDOWN,
+				   U2P_R0_DP_PULLDOWN);
+		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_ID_PULLUP, 0);
+		break;
+
+	case PHY_MODE_USB_DEVICE:
+		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DM_PULLDOWN,
+				   0);
+		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DP_PULLDOWN,
+				   0);
+		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_ID_PULLUP,
+				   U2P_R0_ID_PULLUP);
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	/* reset the PHY and wait until settings are stabilized */
+	regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET,
+			   U2P_R0_POWER_ON_RESET);
+	udelay(RESET_COMPLETE_TIME);
+	regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET, 0);
+	udelay(RESET_COMPLETE_TIME);
+
+	priv->mode = mode;
+
+	return 0;
+}
+
+static int phy_meson_gxl_usb2_power_off(struct phy *phy)
+{
+	struct phy_meson_gxl_usb2_drv *drv_priv =
+		dev_get_drvdata(phy->dev.parent);
+	struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
+
+	/* power off the PHY by putting it into reset mode */
+	regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET,
+			   U2P_R0_POWER_ON_RESET);
+
+	clk_disable_unprepare(drv_priv->clk_usb_ddr);
+	clk_disable_unprepare(drv_priv->clk_usb);
+
+	return 0;
+}
+
+static int phy_meson_gxl_usb2_power_on(struct phy *phy)
+{
+	struct phy_meson_gxl_usb2_drv *drv_priv =
+		dev_get_drvdata(phy->dev.parent);
+	struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
+	int ret;
+
+	ret = clk_prepare_enable(drv_priv->clk_usb);
+	if (ret) {
+		dev_err(&phy->dev, "Failed to enable USB clock\n");
+		return ret;
+	}
+
+	ret = clk_prepare_enable(drv_priv->clk_usb_ddr);
+	if (ret) {
+		clk_disable_unprepare(drv_priv->clk_usb);
+
+		dev_err(&phy->dev, "Failed to enable USB DDR clock\n");
+		return ret;
+	}
+
+	/* power on the PHY by taking it out of reset mode */
+	regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET, 0);
+
+	ret = phy_meson_gxl_usb2_set_mode(phy, priv->mode);
+	if (ret) {
+		phy_meson_gxl_usb2_power_off(phy);
+
+		dev_err(&phy->dev, "Failed to initialize PHY with mode %d\n",
+			priv->mode);
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct phy_ops phy_meson_gxl_usb2_ops = {
+	.power_on	= phy_meson_gxl_usb2_power_on,
+	.power_off	= phy_meson_gxl_usb2_power_off,
+	.set_mode	= phy_meson_gxl_usb2_set_mode,
+	.owner		= THIS_MODULE,
+};
+
+static struct phy *phy_meson_gxl_usb2_of_xlate(struct device *dev,
+					       struct of_phandle_args *args)
+{
+	struct phy_meson_gxl_usb2_drv *priv = dev_get_drvdata(dev);
+	int port;
+
+	if (args->args_count != 1) {
+		dev_err(dev, "Invalid number of cells in 'phy' property\n");
+		return ERR_PTR(-ENODEV);
+	}
+
+	port = args->args[0];
+	if (WARN_ON(port >= priv->num_ports))
+		return ERR_PTR(-ENODEV);
+
+	return priv->ports[port];
+}
+
+static int phy_meson_gxl_usb2_probe_port(struct device *dev, int port)
+{
+	struct phy_meson_gxl_usb2_drv *drv_priv = dev_get_drvdata(dev);
+	struct phy_meson_gxl_usb2_priv *phy_priv;
+	struct phy *phy;
+	void __iomem *port_base;
+
+	phy_priv = devm_kzalloc(dev, sizeof(*phy_priv), GFP_KERNEL);
+	if (!phy_priv)
+		return -ENOMEM;
+
+	switch (of_usb_get_dr_mode_by_phy(dev->of_node, port)) {
+	case USB_DR_MODE_PERIPHERAL:
+		phy_priv->mode = PHY_MODE_USB_DEVICE;
+		break;
+	case USB_DR_MODE_OTG:
+		phy_priv->mode = PHY_MODE_USB_OTG;
+		break;
+	case USB_DR_MODE_HOST:
+	default:
+		phy_priv->mode = PHY_MODE_USB_HOST;
+		break;
+	}
+
+	phy = devm_phy_create(dev, NULL, &phy_meson_gxl_usb2_ops);
+	if (IS_ERR(phy)) {
+		dev_err(dev, "failed to create PHY port %d\n", port);
+		return PTR_ERR(phy);
+	}
+
+	port_base = drv_priv->base + (port * PHY_PORT_RESOURCE_SIZE);
+	phy_priv->regmap = devm_regmap_init_mmio(&phy->dev, port_base,
+						 &phy_meson_gxl_usb2_regmap_conf);
+	if (IS_ERR(phy_priv->regmap))
+		return PTR_ERR(phy_priv->regmap);
+
+	phy_set_drvdata(phy, phy_priv);
+
+	drv_priv->ports[port] = phy;
+
+	return 0;
+}
+
+static int phy_meson_gxl_usb2_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct phy_meson_gxl_usb2_drv *priv;
+	struct phy_provider *phy_provider;
+	struct resource *res;
+	int i, ret;
+
+	ret = device_reset(dev);
+	if (ret) {
+		dev_err(dev, "failed to reset device\n");
+		return ret;
+	}
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, priv);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(priv->base))
+		return PTR_ERR(priv->base);
+
+	priv->num_ports = resource_size(res) / PHY_PORT_RESOURCE_SIZE;
+	if (priv->num_ports < 1) {
+		dev_err(dev, "specified memory range is too small\n");
+		return -EINVAL;
+	}
+
+	priv->ports = devm_kcalloc(dev, priv->num_ports, sizeof(*priv->ports),
+				   GFP_KERNEL);
+	if (!priv->ports)
+		return -ENOMEM;
+
+	priv->clk_usb = devm_clk_get(dev, "usb");
+	if (IS_ERR(priv->clk_usb)) {
+		dev_err(dev, "failed to get USB clock\n");
+		return PTR_ERR(priv->clk_usb);
+	}
+
+	priv->clk_usb_ddr = devm_clk_get(dev, "usb_ddr");
+	if (IS_ERR(priv->clk_usb_ddr)) {
+		dev_err(dev, "failed to get USB DDR clock\n");
+		return PTR_ERR(priv->clk_usb_ddr);
+	}
+
+	for (i = 0; i < priv->num_ports; i++) {
+		ret = phy_meson_gxl_usb2_probe_port(dev, i);
+		if (ret)
+			return ret;
+	}
+
+	phy_provider = devm_of_phy_provider_register(dev,
+						     phy_meson_gxl_usb2_of_xlate);
+
+	return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id phy_meson_gxl_usb2_of_match[] = {
+	{ .compatible = "amlogic,meson-gxl-usb2-phy", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, phy_meson_gxl_usb2_of_match);
+
+static struct platform_driver phy_meson_gxl_usb2_driver = {
+	.probe	= phy_meson_gxl_usb2_probe,
+	.driver	= {
+		.name		= "phy-meson-gxl-usb2",
+		.of_match_table	= phy_meson_gxl_usb2_of_match,
+	},
+};
+module_platform_driver(phy_meson_gxl_usb2_driver);
+
+MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
+MODULE_DESCRIPTION("Meson GXL USB2 PHY driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/phy-meson-gxl-usb3.c b/drivers/phy/phy-meson-gxl-usb3.c
new file mode 100644
index 0000000..90a4028
--- /dev/null
+++ b/drivers/phy/phy-meson-gxl-usb3.c
@@ -0,0 +1,377 @@
+/*
+ * Meson GXL USB3 PHY driver
+ *
+ * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/reset.h>
+#include <linux/regmap.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/usb/of.h>
+#include <linux/workqueue.h>
+
+#define USB_R0							0x00
+	#define USB_R0_P30_FSEL_SHIFT				0
+	#define USB_R0_P30_FSEL_MASK				GENMASK(5, 0)
+	#define USB_R0_P30_PHY_RESET				BIT(6)
+	#define USB_R0_P30_TEST_POWERDOWN_HSP			BIT(7)
+	#define USB_R0_P30_TEST_POWERDOWN_SSP			BIT(8)
+	#define USB_R0_P30_ACJT_LEVEL_SHIFT			9
+	#define USB_R0_P30_ACJT_LEVEL_MASK			GENMASK(13, 9)
+	#define USB_R0_P30_TX_BOOST_LEVEL_SHIFT			14
+	#define USB_R0_P30_TX_BOOST_LEVEL_MASK			GENMASK(16, 14)
+	#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_SHIFT		19
+	#define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK		GENMASK(28, 19)
+	#define USB_R0_U2D_SS_SCALEDOWN_MODE_SHIFT		29
+	#define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK		GENMASK(30, 29)
+	#define USB_R0_U2D_ACT					BIT(31)
+
+#define USB_R1							0x04
+	#define USB_R1_U3H_BIGENDIAN_GS				BIT(0)
+	#define USB_R1_U3H_PME_ENABLE				BIT(1)
+	#define USB_R1_U3H_HUB_PORT_OVERCURRENT_SHIFT		2
+	#define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK		GENMASK(6, 2)
+	#define USB_R1_U3H_HUB_PORT_PERM_ATTACH_SHIFT		7
+	#define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK		GENMASK(11, 7)
+	#define USB_R1_U3H_HOST_U2_PORT_DISABLE_SHIFT		12
+	#define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK		GENMASK(15, 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_SHIFT		19
+	#define USB_R1_U3H_FLADJ_30MHZ_REG_MASK			GENMASK(24, 19)
+	#define USB_R1_P30_PCS_TX_SWING_FULL_SHIFT		25
+	#define USB_R1_P30_PCS_TX_SWING_FULL_MASK		GENMASK(31, 25)
+
+#define USB_R2							0x08
+	#define USB_R2_P30_CR_DATA_IN_SHIFT			0
+	#define USB_R2_P30_CR_DATA_IN_MASK			GENMASK(15, 0)
+	#define USB_R2_P30_CR_READ				BIT(16)
+	#define USB_R2_P30_CR_WRITE				BIT(17)
+	#define USB_R2_P30_CR_CAP_ADDR				BIT(18)
+	#define USB_R2_P30_CR_CAP_DATA				BIT(19)
+	#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_SHIFT		20
+	#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK		GENMASK(25, 20)
+	#define USB_R2_P30_PCS_TX_DEEMPH_6DB_SHIFT		26
+	#define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK		GENMASK(31, 26)
+
+#define USB_R3							0x0c
+	#define USB_R3_P30_SSC_ENABLE				BIT(0)
+	#define USB_R3_P30_SSC_RANGE_SHIFT			1
+	#define USB_R3_P30_SSC_RANGE_MASK			GENMASK(3, 1)
+	#define USB_R3_P30_SSC_REF_CLK_SEL_SHIFT		4
+	#define USB_R3_P30_SSC_REF_CLK_SEL_MASK			GENMASK(12, 4)
+	#define USB_R3_P30_REF_SSP_EN				BIT(13)
+	#define USB_R3_P30_LOS_BIAS_SHIFT			16
+	#define USB_R3_P30_LOS_BIAS_MASK			GENMASK(18, 16)
+	#define USB_R3_P30_LOS_LEVEL_SHIFT			19
+	#define USB_R3_P30_LOS_LEVEL_MASK			GENMASK(23, 19)
+	#define USB_R3_P30_MPLL_MULTIPLIER_SHIFT		24
+	#define USB_R3_P30_MPLL_MULTIPLIER_MASK			GENMASK(30, 24)
+
+#define USB_R4							0x10
+	#define USB_R4_P21_PORT_RESET_0				BIT(0)
+	#define USB_R4_P21_SLEEP_M0				BIT(1)
+	#define USB_R4_MEM_PD_SHIFT				2
+	#define USB_R4_MEM_PD_MASK				GENMASK(3, 2)
+	#define USB_R4_P21_ONLY					BIT(4)
+
+#define USB_R5							0x14
+	#define USB_R5_ID_DIG_SYNC				BIT(0)
+	#define USB_R5_ID_DIG_REG				BIT(1)
+	#define USB_R5_ID_DIG_CFG_SHIFT				2
+	#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_SHIFT				8
+	#define USB_R5_ID_DIG_TH_MASK				GENMASK(15, 8)
+	#define USB_R5_ID_DIG_CNT_SHIFT				16
+	#define USB_R5_ID_DIG_CNT_MASK				GENMASK(23, 16)
+
+/* read-only register */
+#define USB_R6							0x18
+	#define USB_R6_P30_CR_DATA_OUT_SHIFT			0
+	#define USB_R6_P30_CR_DATA_OUT_MASK			GENMASK(15, 0)
+	#define USB_R6_P30_CR_ACK				BIT(16)
+
+#define RESET_COMPLETE_TIME				500
+
+struct phy_meson_gxl_usb3_priv {
+	struct regmap		*regmap;
+	struct delayed_work	otg_work;
+	struct phy		*this_phy;
+	int			num_usb2_phys;
+	struct phy		**usb2_phys;
+};
+
+static const struct regmap_config phy_meson_gxl_usb3_regmap_conf = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+	.max_register = USB_R6,
+};
+
+static int phy_meson_gxl_usb3_update_mode(struct phy *phy)
+{
+	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+	u32 val;
+	enum phy_mode mode;
+	int i, ret;
+
+	ret = regmap_read(priv->regmap, USB_R5, &val);
+	if (ret)
+		return ret;
+
+	if (val & USB_R5_ID_DIG_CURR) {
+		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_R4, USB_R4_P21_SLEEP_M0,
+				   USB_R4_P21_SLEEP_M0);
+	} else {
+		mode = PHY_MODE_USB_HOST;
+
+		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);
+	}
+
+	/* inform the USB2 PHY that we have changed the mode */
+	for (i = 0; i < priv->num_usb2_phys; i++) {
+		ret = phy_set_mode(priv->usb2_phys[i], mode);
+		if (ret) {
+			dev_err(&phy->dev,
+				"Failed to update usb2-phy #%d mode to %d\n",
+				i, mode);
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
+static void phy_meson_gxl_usb3_work(struct work_struct *data)
+{
+	struct phy_meson_gxl_usb3_priv *priv =
+		container_of(data, struct phy_meson_gxl_usb3_priv,
+			     otg_work.work);
+
+	phy_meson_gxl_usb3_update_mode(priv->this_phy);
+
+	/* unmask IRQs which may have arrived in the meantime */
+	regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_IRQ, 0);
+}
+
+static int phy_meson_gxl_usb3_init(struct phy *phy)
+{
+	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+	int i, ret;
+
+	for (i = 0; i < priv->num_usb2_phys; i++) {
+		ret = phy_init(priv->usb2_phys[i]);
+		if (ret) {
+			dev_err(&phy->dev,
+				"Failed to initialize related usb2-phy #%d\n",
+				i);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int phy_meson_gxl_usb3_exit(struct phy *phy)
+{
+	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+	int i, ret;
+
+	for (i = 0; i < priv->num_usb2_phys; i++) {
+		ret = phy_exit(priv->usb2_phys[i]);
+		if (ret) {
+			dev_err(&phy->dev,
+				"Failed to exit related usb2-phy #%d\n", i);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int phy_meson_gxl_usb3_power_on(struct phy *phy)
+{
+	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+	int i, ret;
+
+	for (i = 0; i < priv->num_usb2_phys; i++) {
+		ret = phy_power_on(priv->usb2_phys[i]);
+		if (ret) {
+			dev_err(&phy->dev,
+				"Failed to power on related usb2-phy #%d\n",
+				i);
+			return ret;
+		}
+	}
+
+	regmap_update_bits(priv->regmap, USB_R1,
+			   USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
+			   0x20 << USB_R1_U3H_FLADJ_30MHZ_REG_SHIFT);
+
+	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,
+			   0xff << USB_R5_ID_DIG_TH_SHIFT);
+
+	return phy_meson_gxl_usb3_update_mode(phy);
+}
+
+static int phy_meson_gxl_usb3_power_off(struct phy *phy)
+{
+	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
+	int i, ret;
+
+	for (i = 0; i < priv->num_usb2_phys; i++) {
+		ret = phy_power_off(priv->usb2_phys[i]);
+		if (ret) {
+			dev_err(&phy->dev,
+				"Failed to power off related usb2-phy #%d\n",
+				i);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static irqreturn_t phy_meson_gxl_usb3_irq(int irq, void *data)
+{
+	u32 val;
+	struct phy_meson_gxl_usb3_priv *priv = data;
+
+	regmap_read(priv->regmap, USB_R5, &val);
+	if (!(val & USB_R5_ID_DIG_IRQ)) {
+		dev_err(&priv->this_phy->dev, "spurious interrupt\n");
+		return IRQ_NONE;
+	}
+
+	schedule_delayed_work(&priv->otg_work, msecs_to_jiffies(10));
+
+	/* acknowledge the IRQ */
+	regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_IRQ, 0);
+
+	return IRQ_HANDLED;
+}
+
+static const struct phy_ops phy_meson_gxl_usb3_ops = {
+	.init		= phy_meson_gxl_usb3_init,
+	.exit		= phy_meson_gxl_usb3_exit,
+	.power_on	= phy_meson_gxl_usb3_power_on,
+	.power_off	= phy_meson_gxl_usb3_power_off,
+	.owner		= THIS_MODULE,
+};
+
+static int phy_meson_gxl_usb3_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *np = dev->of_node;
+	struct phy_meson_gxl_usb3_priv *priv;
+	struct resource *res;
+	struct phy *phy;
+	struct phy_provider *phy_provider;
+	void __iomem *base;
+	int i, irq;
+
+	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_gxl_usb3_regmap_conf);
+	if (IS_ERR(priv->regmap))
+		return PTR_ERR(priv->regmap);
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq >= 0) {
+		INIT_DELAYED_WORK(&priv->otg_work, phy_meson_gxl_usb3_work);
+
+		irq = devm_request_irq(dev, irq, phy_meson_gxl_usb3_irq,
+				       IRQF_SHARED, dev_name(dev),
+				       priv);
+		if (irq < 0) {
+			dev_err(dev, "could not register IRQ handler (%d)\n",
+				irq);
+			return -EINVAL;
+		}
+	}
+
+	priv->num_usb2_phys = of_count_phandle_with_args(np, "phys",
+							 "#phy-cells");
+
+	priv->usb2_phys = devm_kcalloc(dev, priv->num_usb2_phys,
+				       sizeof(*priv->usb2_phys), GFP_KERNEL);
+	if (!priv->usb2_phys)
+		return -ENOMEM;
+
+	for (i = 0; i < priv->num_usb2_phys; i++) {
+		priv->usb2_phys[i] = devm_of_phy_get_by_index(dev, np, i);
+		if (IS_ERR(priv->usb2_phys[i])) {
+			dev_err(dev, "failed to get related usb2-phy #%d", i);
+			return PTR_ERR(priv->usb2_phys[i]);
+		}
+	}
+
+	phy = devm_phy_create(dev, np, &phy_meson_gxl_usb3_ops);
+	if (IS_ERR(phy)) {
+		dev_err(dev, "failed to create PHY\n");
+		return PTR_ERR(phy);
+	}
+
+	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_gxl_usb3_of_match[] = {
+	{ .compatible = "amlogic,meson-gxl-usb3-phy", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, phy_meson_gxl_usb3_of_match);
+
+static struct platform_driver phy_meson_gxl_usb3_driver = {
+	.probe	= phy_meson_gxl_usb3_probe,
+	.driver	= {
+		.name		= "phy-meson-gxl-usb3",
+		.of_match_table	= phy_meson_gxl_usb3_of_match,
+	},
+};
+module_platform_driver(phy_meson_gxl_usb3_driver);
+
+MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
+MODULE_DESCRIPTION("Meson GXL USB3 PHY driver");
+MODULE_LICENSE("GPL");
-- 
2.10.2

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

* [PATCH 3/5] arm64: dts: meson-gxl: add USB support
  2016-11-26 14:56 ` Martin Blumenstingl
  (?)
@ 2016-11-26 14:56     ` Martin Blumenstingl
  -1 siblings, 0 replies; 39+ messages in thread
From: Martin Blumenstingl @ 2016-11-26 14:56 UTC (permalink / raw)
  To: linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, kishon-l0cyMroinI0,
	khilman-rdvid1DuHRBWk0Htik3J/w, carlo-KA+7E9HrN00dnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A
  Cc: catalin.marinas-5wv7dgnIgG8, will.deacon-5wv7dgnIgG8,
	narmstrong-rdvid1DuHRBWk0Htik3J/w, Martin Blumenstingl

This adds USB support the the Meson GXL SoC. On most devices only the
dwc3 controller is used. It has an internal USB hub which provides two
ports. The PHYs for these ports are defined through the USB3 PHY, which
is ensures that all PHYs are using the same mode (because all of them
are used by the same controller).

Signed-off-by: Martin Blumenstingl <martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>
---
 arch/arm64/boot/dts/amlogic/meson-gxl.dtsi | 49 ++++++++++++++++++++++++++++++
 1 file changed, 49 insertions(+)

diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
index 3af54dc..99ac900 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
@@ -44,9 +44,58 @@
 #include "meson-gx.dtsi"
 #include <dt-bindings/clock/gxbb-clkc.h>
 #include <dt-bindings/gpio/meson-gxl-gpio.h>
+#include <dt-bindings/reset/amlogic,meson-gxbb-reset.h>
 
 / {
 	compatible = "amlogic,meson-gxl";
+
+	soc {
+
+		usb0: usb@c9000000 {
+			compatible = "snps,dwc3";
+			reg = <0x0 0xc9000000 0x0 0x100000>;
+			interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
+			phys = <&usb3_phy0>;
+			phy-names = "usb3-phy";
+			dr_mode = "host";
+			maximum-speed = "high-speed";
+			snps,dis_u2_susphy_quirk;
+		};
+
+		usb1: usb@c9100000 {
+			compatible = "snps,dwc2";
+			reg = <0x0 0xc9100000 0x0 0x40000>;
+			interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&clkc CLKID_USB>;
+			clock-names = "otg";
+			dr_mode = "host";
+			status = "disabled";
+		};
+	};
+};
+
+&apb {
+	usb2_phys: phy@78000 {
+		compatible = "amlogic,meson-gxl-usb2-phy";
+		#phy-cells = <1>;
+		reg = <0x0 0x78000 0x0 0x80>;
+		clocks = <&clkc CLKID_USB1>, <&clkc CLKID_USB1_DDR_BRIDGE>;
+		clock-names = "usb", "usb_ddr";
+		resets = <&reset RESET_USB_OTG>;
+		status = "disabled";
+	};
+
+	usb3_phy0: phy@78080 {
+		compatible = "amlogic,meson-gxl-usb3-phy";
+		#phy-cells = <0>;
+		reg = <0x0 0x78080 0x0 0x20>;
+		interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
+
+		/* dwc3 on GXL enables 2 USB ports on the internal hub */
+		phys = <&usb2_phys 0>, <&usb2_phys 1>;
+
+		status = "disabled";
+	};
 };
 
 &ethmac {
-- 
2.10.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 3/5] arm64: dts: meson-gxl: add USB support
@ 2016-11-26 14:56     ` Martin Blumenstingl
  0 siblings, 0 replies; 39+ messages in thread
From: Martin Blumenstingl @ 2016-11-26 14:56 UTC (permalink / raw)
  To: linux-arm-kernel

This adds USB support the the Meson GXL SoC. On most devices only the
dwc3 controller is used. It has an internal USB hub which provides two
ports. The PHYs for these ports are defined through the USB3 PHY, which
is ensures that all PHYs are using the same mode (because all of them
are used by the same controller).

Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
---
 arch/arm64/boot/dts/amlogic/meson-gxl.dtsi | 49 ++++++++++++++++++++++++++++++
 1 file changed, 49 insertions(+)

diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
index 3af54dc..99ac900 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
@@ -44,9 +44,58 @@
 #include "meson-gx.dtsi"
 #include <dt-bindings/clock/gxbb-clkc.h>
 #include <dt-bindings/gpio/meson-gxl-gpio.h>
+#include <dt-bindings/reset/amlogic,meson-gxbb-reset.h>
 
 / {
 	compatible = "amlogic,meson-gxl";
+
+	soc {
+
+		usb0: usb at c9000000 {
+			compatible = "snps,dwc3";
+			reg = <0x0 0xc9000000 0x0 0x100000>;
+			interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
+			phys = <&usb3_phy0>;
+			phy-names = "usb3-phy";
+			dr_mode = "host";
+			maximum-speed = "high-speed";
+			snps,dis_u2_susphy_quirk;
+		};
+
+		usb1: usb at c9100000 {
+			compatible = "snps,dwc2";
+			reg = <0x0 0xc9100000 0x0 0x40000>;
+			interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&clkc CLKID_USB>;
+			clock-names = "otg";
+			dr_mode = "host";
+			status = "disabled";
+		};
+	};
+};
+
+&apb {
+	usb2_phys: phy at 78000 {
+		compatible = "amlogic,meson-gxl-usb2-phy";
+		#phy-cells = <1>;
+		reg = <0x0 0x78000 0x0 0x80>;
+		clocks = <&clkc CLKID_USB1>, <&clkc CLKID_USB1_DDR_BRIDGE>;
+		clock-names = "usb", "usb_ddr";
+		resets = <&reset RESET_USB_OTG>;
+		status = "disabled";
+	};
+
+	usb3_phy0: phy at 78080 {
+		compatible = "amlogic,meson-gxl-usb3-phy";
+		#phy-cells = <0>;
+		reg = <0x0 0x78080 0x0 0x20>;
+		interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
+
+		/* dwc3 on GXL enables 2 USB ports on the internal hub */
+		phys = <&usb2_phys 0>, <&usb2_phys 1>;
+
+		status = "disabled";
+	};
 };
 
 &ethmac {
-- 
2.10.2

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

* [PATCH 3/5] arm64: dts: meson-gxl: add USB support
@ 2016-11-26 14:56     ` Martin Blumenstingl
  0 siblings, 0 replies; 39+ messages in thread
From: Martin Blumenstingl @ 2016-11-26 14:56 UTC (permalink / raw)
  To: linus-amlogic

This adds USB support the the Meson GXL SoC. On most devices only the
dwc3 controller is used. It has an internal USB hub which provides two
ports. The PHYs for these ports are defined through the USB3 PHY, which
is ensures that all PHYs are using the same mode (because all of them
are used by the same controller).

Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
---
 arch/arm64/boot/dts/amlogic/meson-gxl.dtsi | 49 ++++++++++++++++++++++++++++++
 1 file changed, 49 insertions(+)

diff --git a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
index 3af54dc..99ac900 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxl.dtsi
@@ -44,9 +44,58 @@
 #include "meson-gx.dtsi"
 #include <dt-bindings/clock/gxbb-clkc.h>
 #include <dt-bindings/gpio/meson-gxl-gpio.h>
+#include <dt-bindings/reset/amlogic,meson-gxbb-reset.h>
 
 / {
 	compatible = "amlogic,meson-gxl";
+
+	soc {
+
+		usb0: usb at c9000000 {
+			compatible = "snps,dwc3";
+			reg = <0x0 0xc9000000 0x0 0x100000>;
+			interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
+			phys = <&usb3_phy0>;
+			phy-names = "usb3-phy";
+			dr_mode = "host";
+			maximum-speed = "high-speed";
+			snps,dis_u2_susphy_quirk;
+		};
+
+		usb1: usb at c9100000 {
+			compatible = "snps,dwc2";
+			reg = <0x0 0xc9100000 0x0 0x40000>;
+			interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
+			clocks = <&clkc CLKID_USB>;
+			clock-names = "otg";
+			dr_mode = "host";
+			status = "disabled";
+		};
+	};
+};
+
+&apb {
+	usb2_phys: phy at 78000 {
+		compatible = "amlogic,meson-gxl-usb2-phy";
+		#phy-cells = <1>;
+		reg = <0x0 0x78000 0x0 0x80>;
+		clocks = <&clkc CLKID_USB1>, <&clkc CLKID_USB1_DDR_BRIDGE>;
+		clock-names = "usb", "usb_ddr";
+		resets = <&reset RESET_USB_OTG>;
+		status = "disabled";
+	};
+
+	usb3_phy0: phy at 78080 {
+		compatible = "amlogic,meson-gxl-usb3-phy";
+		#phy-cells = <0>;
+		reg = <0x0 0x78080 0x0 0x20>;
+		interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
+
+		/* dwc3 on GXL enables 2 USB ports on the internal hub */
+		phys = <&usb2_phys 0>, <&usb2_phys 1>;
+
+		status = "disabled";
+	};
 };
 
 &ethmac {
-- 
2.10.2

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

* [PATCH 4/5] ARM64: dts: meson-gxm: add GXM specific USB configuration
  2016-11-26 14:56 ` Martin Blumenstingl
  (?)
@ 2016-11-26 14:56     ` Martin Blumenstingl
  -1 siblings, 0 replies; 39+ messages in thread
From: Martin Blumenstingl @ 2016-11-26 14:56 UTC (permalink / raw)
  To: linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, kishon-l0cyMroinI0,
	khilman-rdvid1DuHRBWk0Htik3J/w, carlo-KA+7E9HrN00dnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A
  Cc: catalin.marinas-5wv7dgnIgG8, will.deacon-5wv7dgnIgG8,
	narmstrong-rdvid1DuHRBWk0Htik3J/w, Martin Blumenstingl

The USB configuration on GXM is slightly different than on GXL. On GXM
the dwc2 controller is limited (via GHWCFG2_OP_MODE_MASK) to "device
mode".
The dwc3 controller's internal hub has 3 ports (instead of 2 on GXL)
enabled. It's hardware configuration limits it (via DWC3_GHWPARAMS0)
to host mode only.

Signed-off-by: Martin Blumenstingl <martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>
---
 arch/arm64/boot/dts/amlogic/meson-gxm.dtsi | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi
index 2b1d276e..0a6b224 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi
@@ -121,3 +121,13 @@
 	clock-indices = <0 1>;
 	clock-output-names = "vbig", "vlittle";
 };
+
+&usb3_phy0 {
+	/* dwc3 on GXM enables 3 USB ports on the internal hub */
+	phys = <&usb2_phys 0>, <&usb2_phys 1>, <&usb2_phys 2>;
+};
+
+&usb1 {
+	/* the dwc2 hardware configuration on GXM only allows device mode */
+	dr_mode = "device";
+};
-- 
2.10.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 4/5] ARM64: dts: meson-gxm: add GXM specific USB configuration
@ 2016-11-26 14:56     ` Martin Blumenstingl
  0 siblings, 0 replies; 39+ messages in thread
From: Martin Blumenstingl @ 2016-11-26 14:56 UTC (permalink / raw)
  To: linux-arm-kernel

The USB configuration on GXM is slightly different than on GXL. On GXM
the dwc2 controller is limited (via GHWCFG2_OP_MODE_MASK) to "device
mode".
The dwc3 controller's internal hub has 3 ports (instead of 2 on GXL)
enabled. It's hardware configuration limits it (via DWC3_GHWPARAMS0)
to host mode only.

Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
---
 arch/arm64/boot/dts/amlogic/meson-gxm.dtsi | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi
index 2b1d276e..0a6b224 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi
@@ -121,3 +121,13 @@
 	clock-indices = <0 1>;
 	clock-output-names = "vbig", "vlittle";
 };
+
+&usb3_phy0 {
+	/* dwc3 on GXM enables 3 USB ports on the internal hub */
+	phys = <&usb2_phys 0>, <&usb2_phys 1>, <&usb2_phys 2>;
+};
+
+&usb1 {
+	/* the dwc2 hardware configuration on GXM only allows device mode */
+	dr_mode = "device";
+};
-- 
2.10.2

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

* [PATCH 4/5] ARM64: dts: meson-gxm: add GXM specific USB configuration
@ 2016-11-26 14:56     ` Martin Blumenstingl
  0 siblings, 0 replies; 39+ messages in thread
From: Martin Blumenstingl @ 2016-11-26 14:56 UTC (permalink / raw)
  To: linus-amlogic

The USB configuration on GXM is slightly different than on GXL. On GXM
the dwc2 controller is limited (via GHWCFG2_OP_MODE_MASK) to "device
mode".
The dwc3 controller's internal hub has 3 ports (instead of 2 on GXL)
enabled. It's hardware configuration limits it (via DWC3_GHWPARAMS0)
to host mode only.

Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
---
 arch/arm64/boot/dts/amlogic/meson-gxm.dtsi | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi b/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi
index 2b1d276e..0a6b224 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gxm.dtsi
@@ -121,3 +121,13 @@
 	clock-indices = <0 1>;
 	clock-output-names = "vbig", "vlittle";
 };
+
+&usb3_phy0 {
+	/* dwc3 on GXM enables 3 USB ports on the internal hub */
+	phys = <&usb2_phys 0>, <&usb2_phys 1>, <&usb2_phys 2>;
+};
+
+&usb1 {
+	/* the dwc2 hardware configuration on GXM only allows device mode */
+	dr_mode = "device";
+};
-- 
2.10.2

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

* [PATCH 5/5] ARM64: dts: meson-gx-p23x-q20x: enable USB on P23x and Q20x boards
  2016-11-26 14:56 ` Martin Blumenstingl
  (?)
@ 2016-11-26 14:56     ` Martin Blumenstingl
  -1 siblings, 0 replies; 39+ messages in thread
From: Martin Blumenstingl @ 2016-11-26 14:56 UTC (permalink / raw)
  To: linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, kishon-l0cyMroinI0,
	khilman-rdvid1DuHRBWk0Htik3J/w, carlo-KA+7E9HrN00dnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A
  Cc: catalin.marinas-5wv7dgnIgG8, will.deacon-5wv7dgnIgG8,
	narmstrong-rdvid1DuHRBWk0Htik3J/w, Martin Blumenstingl

All four devices are only using the dwc3 controller. The actual ports
are provided by dwc3's internal USB hub.
The implementation on P230, P231 and Q201 seems identical: the USB VBUS
supply seems to be hard-wired, while on Q200 the USB VBUS is provided
by GPIOAO_5.

Signed-off-by: Martin Blumenstingl <martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>
---
 arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi | 12 ++++++++++++
 arch/arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts | 17 +++++++++++++++++
 2 files changed, 29 insertions(+)

diff --git a/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
index 7a078be..360c91d 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
@@ -188,3 +188,15 @@
 &ethmac {
 	status = "okay";
 };
+
+&usb2_phys {
+	status = "okay";
+};
+
+&usb3_phy0 {
+	status = "okay";
+};
+
+&usb0 {
+	status = "okay";
+};
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts b/arch/arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts
index 5dbc660..6c28e87 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts
@@ -49,6 +49,19 @@
 / {
 	compatible = "amlogic,q200", "amlogic,s912", "amlogic,meson-gxm";
 	model = "Amlogic Meson GXM (S912) Q200 Development Board";
+
+	usb_pwr: regulator-usb-pwrs {
+		compatible = "regulator-fixed";
+
+		regulator-name = "USB_PWR";
+
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+
+		/* signal name in schematic: USB_PWR_EN */
+		gpio = <&gpio GPIODV_24 GPIO_ACTIVE_HIGH>;
+		enable-active-high;
+	};
 };
 
 /* Q200 has exclusive choice between internal or external PHY */
@@ -75,3 +88,7 @@
 		max-speed = <1000>;
 	};
 };
+
+&usb3_phy0 {
+	phy-supply = <&usb_pwr>;
+};
-- 
2.10.2

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 5/5] ARM64: dts: meson-gx-p23x-q20x: enable USB on P23x and Q20x boards
@ 2016-11-26 14:56     ` Martin Blumenstingl
  0 siblings, 0 replies; 39+ messages in thread
From: Martin Blumenstingl @ 2016-11-26 14:56 UTC (permalink / raw)
  To: linux-arm-kernel

All four devices are only using the dwc3 controller. The actual ports
are provided by dwc3's internal USB hub.
The implementation on P230, P231 and Q201 seems identical: the USB VBUS
supply seems to be hard-wired, while on Q200 the USB VBUS is provided
by GPIOAO_5.

Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
---
 arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi | 12 ++++++++++++
 arch/arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts | 17 +++++++++++++++++
 2 files changed, 29 insertions(+)

diff --git a/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
index 7a078be..360c91d 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
@@ -188,3 +188,15 @@
 &ethmac {
 	status = "okay";
 };
+
+&usb2_phys {
+	status = "okay";
+};
+
+&usb3_phy0 {
+	status = "okay";
+};
+
+&usb0 {
+	status = "okay";
+};
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts b/arch/arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts
index 5dbc660..6c28e87 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts
@@ -49,6 +49,19 @@
 / {
 	compatible = "amlogic,q200", "amlogic,s912", "amlogic,meson-gxm";
 	model = "Amlogic Meson GXM (S912) Q200 Development Board";
+
+	usb_pwr: regulator-usb-pwrs {
+		compatible = "regulator-fixed";
+
+		regulator-name = "USB_PWR";
+
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+
+		/* signal name in schematic: USB_PWR_EN */
+		gpio = <&gpio GPIODV_24 GPIO_ACTIVE_HIGH>;
+		enable-active-high;
+	};
 };
 
 /* Q200 has exclusive choice between internal or external PHY */
@@ -75,3 +88,7 @@
 		max-speed = <1000>;
 	};
 };
+
+&usb3_phy0 {
+	phy-supply = <&usb_pwr>;
+};
-- 
2.10.2

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

* [PATCH 5/5] ARM64: dts: meson-gx-p23x-q20x: enable USB on P23x and Q20x boards
@ 2016-11-26 14:56     ` Martin Blumenstingl
  0 siblings, 0 replies; 39+ messages in thread
From: Martin Blumenstingl @ 2016-11-26 14:56 UTC (permalink / raw)
  To: linus-amlogic

All four devices are only using the dwc3 controller. The actual ports
are provided by dwc3's internal USB hub.
The implementation on P230, P231 and Q201 seems identical: the USB VBUS
supply seems to be hard-wired, while on Q200 the USB VBUS is provided
by GPIOAO_5.

Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
---
 arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi | 12 ++++++++++++
 arch/arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts | 17 +++++++++++++++++
 2 files changed, 29 insertions(+)

diff --git a/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
index 7a078be..360c91d 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
+++ b/arch/arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi
@@ -188,3 +188,15 @@
 &ethmac {
 	status = "okay";
 };
+
+&usb2_phys {
+	status = "okay";
+};
+
+&usb3_phy0 {
+	status = "okay";
+};
+
+&usb0 {
+	status = "okay";
+};
diff --git a/arch/arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts b/arch/arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts
index 5dbc660..6c28e87 100644
--- a/arch/arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts
+++ b/arch/arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts
@@ -49,6 +49,19 @@
 / {
 	compatible = "amlogic,q200", "amlogic,s912", "amlogic,meson-gxm";
 	model = "Amlogic Meson GXM (S912) Q200 Development Board";
+
+	usb_pwr: regulator-usb-pwrs {
+		compatible = "regulator-fixed";
+
+		regulator-name = "USB_PWR";
+
+		regulator-min-microvolt = <5000000>;
+		regulator-max-microvolt = <5000000>;
+
+		/* signal name in schematic: USB_PWR_EN */
+		gpio = <&gpio GPIODV_24 GPIO_ACTIVE_HIGH>;
+		enable-active-high;
+	};
 };
 
 /* Q200 has exclusive choice between internal or external PHY */
@@ -75,3 +88,7 @@
 		max-speed = <1000>;
 	};
 };
+
+&usb3_phy0 {
+	phy-supply = <&usb_pwr>;
+};
-- 
2.10.2

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

* Re: [PATCH 0/5] Meson GXL and GXM USB support
  2016-11-26 14:56 ` Martin Blumenstingl
  (?)
@ 2016-11-27 22:42     ` Martin Blumenstingl
  -1 siblings, 0 replies; 39+ messages in thread
From: Martin Blumenstingl @ 2016-11-27 22:42 UTC (permalink / raw)
  To: linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, kishon-l0cyMroinI0,
	khilman-rdvid1DuHRBWk0Htik3J/w, carlo-KA+7E9HrN00dnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A
  Cc: catalin.marinas-5wv7dgnIgG8, will.deacon-5wv7dgnIgG8,
	narmstrong-rdvid1DuHRBWk0Htik3J/w, Martin Blumenstingl

Hello Kishon,

On Sat, Nov 26, 2016 at 3:56 PM, Martin Blumenstingl
<martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org> wrote:
> USB support on GXL and GXM differs a lot from Meson8b and GXBB:
> The most obvious change is that GXL and GXM now have one dwc3
> controller and one dwc2 controller (instead of two dwc2 controllers).
> With that there are also new USB PHYs.
>
> Due to lack of hardware I was only able to test this on a board with
> GXM, but as far as I understand the hardware my preparations should be
> correct (so it should also work on GXL).
>
> dwc2 will probably stay unused on most GXM devices since it's limited
> to device mode via some dwc2 hardware configuration register.
>
> dwc3 is probably used on all devices, even if there is more than just
> one USB port. dwc3 has a built-in USB2 hub - on GXL this hub has two
> ports enabled, while on GXM there are three ports enabled (see below
> for lsusb output). There are no USB3 ports enabled in the dwc3 hardware
> configuration, meaning that the SoC is limited to high-speed mode.
> On my GXM device the dwc3 hardware configuration forces it into "host
> only" mode.
>
> The SoCs contain two PHY blocks: one USB3 PHY and up to four USB2 PHYs
> (on GXM there are only three enabled, but the registers should support
> up to four).
> The USB3 PHY also handles the OTG interrupts, but since dwc3's hardware
> configuration enforces "host only" mode I was not able to test this. It
> simply takes care of an interrupt and then notifies all related PHYs
> about the new mode.
> The USB2 PHY block is a bit different: I created one PHY driver which
> spans all "PHY ports" because the handling is a bit tricky. It turns
> out that for each available USB port in dwc3's hub the corresponding
> PHY must be enabled (even if there is no physical port - in my case
> port 3 is not connected to anything, but disabling the PHY breaks
> ports 1 and 2 as well).
> I decided not not pass the USB2 PHYs directly to dwc3 due to three
> reasons: 1. the USB3 PHY (which holds a reference to all relevant
> USB2 PHY ports) controls the mode of the USB2 PHY ports (since both
> are used with the same controller and thus it makes sense to keep the
> mode consistent across all ports) 2. the dwc3 driver does not support
> passing multiple USB2 PHYs (only one USB2 and one USB3 PHY can be
> passed to it) 3. it is similar to how the vendor reference driver
> manages the PHYs. Please note that this coupling is not a fixed, this
> is all configurable via devicetree (so if the third USB2 PHY has to
> be passed two the dwc2 controller then this is still possible by
> just moving on PHY reference in the .dts).
after not staring at my own code for 24 hours I realized this:
(I went through quite a few iterations before getting these drivers to work)
I'm basically re-modelling an "USB PHY hub" with my USB3 PHY driver
(there's one "upstream" PHY interface which is passed to dwc3 and
multiple downstream PHYs, each for one port on dwc3's internal hub).
With this approach I could split each of the the USB2s into separate
nodes again (instead of one devicetree node with #phy-cells = <1>) as
the USB3 PHY is taking care of that special "we have to enable all
ports or no port will be usable".

We could go even one step further: why implement this in the Meson GXL
specific PHY driver - why not implement a generic "phy-hub" driver
(which would be valid whenever the PHY controller has to manage
multiple PHYs at once, but wants to keep them all in a consistent
state).
The devicetree could look like this:
    usb2_phy_hub: phy@0 {
        compatible = "phy-hub";
        phys = <&other_phy1>, <&other_phy 2>;
    };

&dwc3 {
     phys = <&usb2_phy_hub>, <&usb3_phy0>;
     phy-names = "usb2-phy", "usb3-phy";
};

The generic phy-hub driver would then implement all phy_ops callbacks
and pass then to each of it's downstream PHYs.

That's just what came into my head - please let me know what you think
of this or share your ideas on how to approach this!

> The coupling of the USB2 and USB3 PHYs is the reason why I sent the
> two drivers in one patch, even though they are handling different IP
> blocks (different registers, etc.).
>
> Unfortunately there are no datasheets available for any of these PHYs.
> Both drivers were written by reading the reference drivers provided by
> Amlogic and analyzing the registers on the kernel that was shipped with
> my board.
>
> As a last note: the dwc3 driver currently only explicitly enables the
> first USB port "DWC3_GUSB2PHYCFG(0)" in the internal hub. The hardware
> seems to enable the other two (DWC3_GUSB2PHYCFG(1) and
> DWC3_GUSB2PHYCFG(2)) automatically. I will ask the dwc3 maintainers if
> changes to dwc3 are desired any how these should look like, but for now
> it's working fine even without changes there.
>
> lsusb output on GXM for the dwc3 hub:
> Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
> ...
>  Hub Port Status:
>    Port 1: 0000.0100 power
>    Port 2: 0000.0100 power
>    Port 3: 0000.0100 power
>
> NOTE: The devicetree changes depend on my previous series:
> "[PATCH 0/2] minor GXL and GXM improvements" - see [0]
>
> NOTE2: This series depends on an upstream dwc3/xhci-plat DMA fix
> (special thanks to Arnd Bergmann and Sriram Dash for fixing that):
> "[PATCH v5 0/6] inherit dma configuration from parent dev" - see [1]
>
> I have a tree with all dependencies applied available at [2] if
> someone wants a quick way to test this (I don't take any responsibility
> if anything explodes though).
>
> [0] http://lists.infradead.org/pipermail/linux-amlogic/2016-November/001665.html
> [1] http://marc.info/?l=linux-usb&m=147938307209685&w=2
> [2] https://github.com/xdarklight/linux/commits/meson-gx-integration-4.10-20161126
>
> Martin Blumenstingl (5):
>   Documentation: dt-bindings: Add documentation for Meson GXL USB2/3
>     PHYs
>   phy: meson: add USB2 and USB3 PHY support for Meson GXL
>   arm64: dts: meson-gxl: add USB support
>   ARM64: dts: meson-gxm: add GXM specific USB configuration
>   ARM64: dts: meson-gx-p23x-q20x: enable USB on P23x and Q20x boards
>
>  .../devicetree/bindings/phy/meson-gxl-usb2-phy.txt |  25 ++
>  .../devicetree/bindings/phy/meson-gxl-usb3-phy.txt |  27 ++
>  .../arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi |  12 +
>  arch/arm64/boot/dts/amlogic/meson-gxl.dtsi         |  49 +++
>  .../arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts |  17 +
>  arch/arm64/boot/dts/amlogic/meson-gxm.dtsi         |  10 +
>  drivers/phy/Kconfig                                |  13 +
>  drivers/phy/Makefile                               |   2 +
>  drivers/phy/phy-meson-gxl-usb2.c                   | 374 ++++++++++++++++++++
>  drivers/phy/phy-meson-gxl-usb3.c                   | 377 +++++++++++++++++++++
>  10 files changed, 906 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
>  create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
>  create mode 100644 drivers/phy/phy-meson-gxl-usb2.c
>  create mode 100644 drivers/phy/phy-meson-gxl-usb3.c
>
> --
> 2.10.2
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 0/5] Meson GXL and GXM USB support
@ 2016-11-27 22:42     ` Martin Blumenstingl
  0 siblings, 0 replies; 39+ messages in thread
From: Martin Blumenstingl @ 2016-11-27 22:42 UTC (permalink / raw)
  To: linux-arm-kernel

Hello Kishon,

On Sat, Nov 26, 2016 at 3:56 PM, Martin Blumenstingl
<martin.blumenstingl@googlemail.com> wrote:
> USB support on GXL and GXM differs a lot from Meson8b and GXBB:
> The most obvious change is that GXL and GXM now have one dwc3
> controller and one dwc2 controller (instead of two dwc2 controllers).
> With that there are also new USB PHYs.
>
> Due to lack of hardware I was only able to test this on a board with
> GXM, but as far as I understand the hardware my preparations should be
> correct (so it should also work on GXL).
>
> dwc2 will probably stay unused on most GXM devices since it's limited
> to device mode via some dwc2 hardware configuration register.
>
> dwc3 is probably used on all devices, even if there is more than just
> one USB port. dwc3 has a built-in USB2 hub - on GXL this hub has two
> ports enabled, while on GXM there are three ports enabled (see below
> for lsusb output). There are no USB3 ports enabled in the dwc3 hardware
> configuration, meaning that the SoC is limited to high-speed mode.
> On my GXM device the dwc3 hardware configuration forces it into "host
> only" mode.
>
> The SoCs contain two PHY blocks: one USB3 PHY and up to four USB2 PHYs
> (on GXM there are only three enabled, but the registers should support
> up to four).
> The USB3 PHY also handles the OTG interrupts, but since dwc3's hardware
> configuration enforces "host only" mode I was not able to test this. It
> simply takes care of an interrupt and then notifies all related PHYs
> about the new mode.
> The USB2 PHY block is a bit different: I created one PHY driver which
> spans all "PHY ports" because the handling is a bit tricky. It turns
> out that for each available USB port in dwc3's hub the corresponding
> PHY must be enabled (even if there is no physical port - in my case
> port 3 is not connected to anything, but disabling the PHY breaks
> ports 1 and 2 as well).
> I decided not not pass the USB2 PHYs directly to dwc3 due to three
> reasons: 1. the USB3 PHY (which holds a reference to all relevant
> USB2 PHY ports) controls the mode of the USB2 PHY ports (since both
> are used with the same controller and thus it makes sense to keep the
> mode consistent across all ports) 2. the dwc3 driver does not support
> passing multiple USB2 PHYs (only one USB2 and one USB3 PHY can be
> passed to it) 3. it is similar to how the vendor reference driver
> manages the PHYs. Please note that this coupling is not a fixed, this
> is all configurable via devicetree (so if the third USB2 PHY has to
> be passed two the dwc2 controller then this is still possible by
> just moving on PHY reference in the .dts).
after not staring at my own code for 24 hours I realized this:
(I went through quite a few iterations before getting these drivers to work)
I'm basically re-modelling an "USB PHY hub" with my USB3 PHY driver
(there's one "upstream" PHY interface which is passed to dwc3 and
multiple downstream PHYs, each for one port on dwc3's internal hub).
With this approach I could split each of the the USB2s into separate
nodes again (instead of one devicetree node with #phy-cells = <1>) as
the USB3 PHY is taking care of that special "we have to enable all
ports or no port will be usable".

We could go even one step further: why implement this in the Meson GXL
specific PHY driver - why not implement a generic "phy-hub" driver
(which would be valid whenever the PHY controller has to manage
multiple PHYs at once, but wants to keep them all in a consistent
state).
The devicetree could look like this:
    usb2_phy_hub: phy at 0 {
        compatible = "phy-hub";
        phys = <&other_phy1>, <&other_phy 2>;
    };

&dwc3 {
     phys = <&usb2_phy_hub>, <&usb3_phy0>;
     phy-names = "usb2-phy", "usb3-phy";
};

The generic phy-hub driver would then implement all phy_ops callbacks
and pass then to each of it's downstream PHYs.

That's just what came into my head - please let me know what you think
of this or share your ideas on how to approach this!

> The coupling of the USB2 and USB3 PHYs is the reason why I sent the
> two drivers in one patch, even though they are handling different IP
> blocks (different registers, etc.).
>
> Unfortunately there are no datasheets available for any of these PHYs.
> Both drivers were written by reading the reference drivers provided by
> Amlogic and analyzing the registers on the kernel that was shipped with
> my board.
>
> As a last note: the dwc3 driver currently only explicitly enables the
> first USB port "DWC3_GUSB2PHYCFG(0)" in the internal hub. The hardware
> seems to enable the other two (DWC3_GUSB2PHYCFG(1) and
> DWC3_GUSB2PHYCFG(2)) automatically. I will ask the dwc3 maintainers if
> changes to dwc3 are desired any how these should look like, but for now
> it's working fine even without changes there.
>
> lsusb output on GXM for the dwc3 hub:
> Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
> ...
>  Hub Port Status:
>    Port 1: 0000.0100 power
>    Port 2: 0000.0100 power
>    Port 3: 0000.0100 power
>
> NOTE: The devicetree changes depend on my previous series:
> "[PATCH 0/2] minor GXL and GXM improvements" - see [0]
>
> NOTE2: This series depends on an upstream dwc3/xhci-plat DMA fix
> (special thanks to Arnd Bergmann and Sriram Dash for fixing that):
> "[PATCH v5 0/6] inherit dma configuration from parent dev" - see [1]
>
> I have a tree with all dependencies applied available at [2] if
> someone wants a quick way to test this (I don't take any responsibility
> if anything explodes though).
>
> [0] http://lists.infradead.org/pipermail/linux-amlogic/2016-November/001665.html
> [1] http://marc.info/?l=linux-usb&m=147938307209685&w=2
> [2] https://github.com/xdarklight/linux/commits/meson-gx-integration-4.10-20161126
>
> Martin Blumenstingl (5):
>   Documentation: dt-bindings: Add documentation for Meson GXL USB2/3
>     PHYs
>   phy: meson: add USB2 and USB3 PHY support for Meson GXL
>   arm64: dts: meson-gxl: add USB support
>   ARM64: dts: meson-gxm: add GXM specific USB configuration
>   ARM64: dts: meson-gx-p23x-q20x: enable USB on P23x and Q20x boards
>
>  .../devicetree/bindings/phy/meson-gxl-usb2-phy.txt |  25 ++
>  .../devicetree/bindings/phy/meson-gxl-usb3-phy.txt |  27 ++
>  .../arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi |  12 +
>  arch/arm64/boot/dts/amlogic/meson-gxl.dtsi         |  49 +++
>  .../arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts |  17 +
>  arch/arm64/boot/dts/amlogic/meson-gxm.dtsi         |  10 +
>  drivers/phy/Kconfig                                |  13 +
>  drivers/phy/Makefile                               |   2 +
>  drivers/phy/phy-meson-gxl-usb2.c                   | 374 ++++++++++++++++++++
>  drivers/phy/phy-meson-gxl-usb3.c                   | 377 +++++++++++++++++++++
>  10 files changed, 906 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
>  create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
>  create mode 100644 drivers/phy/phy-meson-gxl-usb2.c
>  create mode 100644 drivers/phy/phy-meson-gxl-usb3.c
>
> --
> 2.10.2
>

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

* [PATCH 0/5] Meson GXL and GXM USB support
@ 2016-11-27 22:42     ` Martin Blumenstingl
  0 siblings, 0 replies; 39+ messages in thread
From: Martin Blumenstingl @ 2016-11-27 22:42 UTC (permalink / raw)
  To: linus-amlogic

Hello Kishon,

On Sat, Nov 26, 2016 at 3:56 PM, Martin Blumenstingl
<martin.blumenstingl@googlemail.com> wrote:
> USB support on GXL and GXM differs a lot from Meson8b and GXBB:
> The most obvious change is that GXL and GXM now have one dwc3
> controller and one dwc2 controller (instead of two dwc2 controllers).
> With that there are also new USB PHYs.
>
> Due to lack of hardware I was only able to test this on a board with
> GXM, but as far as I understand the hardware my preparations should be
> correct (so it should also work on GXL).
>
> dwc2 will probably stay unused on most GXM devices since it's limited
> to device mode via some dwc2 hardware configuration register.
>
> dwc3 is probably used on all devices, even if there is more than just
> one USB port. dwc3 has a built-in USB2 hub - on GXL this hub has two
> ports enabled, while on GXM there are three ports enabled (see below
> for lsusb output). There are no USB3 ports enabled in the dwc3 hardware
> configuration, meaning that the SoC is limited to high-speed mode.
> On my GXM device the dwc3 hardware configuration forces it into "host
> only" mode.
>
> The SoCs contain two PHY blocks: one USB3 PHY and up to four USB2 PHYs
> (on GXM there are only three enabled, but the registers should support
> up to four).
> The USB3 PHY also handles the OTG interrupts, but since dwc3's hardware
> configuration enforces "host only" mode I was not able to test this. It
> simply takes care of an interrupt and then notifies all related PHYs
> about the new mode.
> The USB2 PHY block is a bit different: I created one PHY driver which
> spans all "PHY ports" because the handling is a bit tricky. It turns
> out that for each available USB port in dwc3's hub the corresponding
> PHY must be enabled (even if there is no physical port - in my case
> port 3 is not connected to anything, but disabling the PHY breaks
> ports 1 and 2 as well).
> I decided not not pass the USB2 PHYs directly to dwc3 due to three
> reasons: 1. the USB3 PHY (which holds a reference to all relevant
> USB2 PHY ports) controls the mode of the USB2 PHY ports (since both
> are used with the same controller and thus it makes sense to keep the
> mode consistent across all ports) 2. the dwc3 driver does not support
> passing multiple USB2 PHYs (only one USB2 and one USB3 PHY can be
> passed to it) 3. it is similar to how the vendor reference driver
> manages the PHYs. Please note that this coupling is not a fixed, this
> is all configurable via devicetree (so if the third USB2 PHY has to
> be passed two the dwc2 controller then this is still possible by
> just moving on PHY reference in the .dts).
after not staring at my own code for 24 hours I realized this:
(I went through quite a few iterations before getting these drivers to work)
I'm basically re-modelling an "USB PHY hub" with my USB3 PHY driver
(there's one "upstream" PHY interface which is passed to dwc3 and
multiple downstream PHYs, each for one port on dwc3's internal hub).
With this approach I could split each of the the USB2s into separate
nodes again (instead of one devicetree node with #phy-cells = <1>) as
the USB3 PHY is taking care of that special "we have to enable all
ports or no port will be usable".

We could go even one step further: why implement this in the Meson GXL
specific PHY driver - why not implement a generic "phy-hub" driver
(which would be valid whenever the PHY controller has to manage
multiple PHYs at once, but wants to keep them all in a consistent
state).
The devicetree could look like this:
    usb2_phy_hub: phy at 0 {
        compatible = "phy-hub";
        phys = <&other_phy1>, <&other_phy 2>;
    };

&dwc3 {
     phys = <&usb2_phy_hub>, <&usb3_phy0>;
     phy-names = "usb2-phy", "usb3-phy";
};

The generic phy-hub driver would then implement all phy_ops callbacks
and pass then to each of it's downstream PHYs.

That's just what came into my head - please let me know what you think
of this or share your ideas on how to approach this!

> The coupling of the USB2 and USB3 PHYs is the reason why I sent the
> two drivers in one patch, even though they are handling different IP
> blocks (different registers, etc.).
>
> Unfortunately there are no datasheets available for any of these PHYs.
> Both drivers were written by reading the reference drivers provided by
> Amlogic and analyzing the registers on the kernel that was shipped with
> my board.
>
> As a last note: the dwc3 driver currently only explicitly enables the
> first USB port "DWC3_GUSB2PHYCFG(0)" in the internal hub. The hardware
> seems to enable the other two (DWC3_GUSB2PHYCFG(1) and
> DWC3_GUSB2PHYCFG(2)) automatically. I will ask the dwc3 maintainers if
> changes to dwc3 are desired any how these should look like, but for now
> it's working fine even without changes there.
>
> lsusb output on GXM for the dwc3 hub:
> Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
> ...
>  Hub Port Status:
>    Port 1: 0000.0100 power
>    Port 2: 0000.0100 power
>    Port 3: 0000.0100 power
>
> NOTE: The devicetree changes depend on my previous series:
> "[PATCH 0/2] minor GXL and GXM improvements" - see [0]
>
> NOTE2: This series depends on an upstream dwc3/xhci-plat DMA fix
> (special thanks to Arnd Bergmann and Sriram Dash for fixing that):
> "[PATCH v5 0/6] inherit dma configuration from parent dev" - see [1]
>
> I have a tree with all dependencies applied available at [2] if
> someone wants a quick way to test this (I don't take any responsibility
> if anything explodes though).
>
> [0] http://lists.infradead.org/pipermail/linux-amlogic/2016-November/001665.html
> [1] http://marc.info/?l=linux-usb&m=147938307209685&w=2
> [2] https://github.com/xdarklight/linux/commits/meson-gx-integration-4.10-20161126
>
> Martin Blumenstingl (5):
>   Documentation: dt-bindings: Add documentation for Meson GXL USB2/3
>     PHYs
>   phy: meson: add USB2 and USB3 PHY support for Meson GXL
>   arm64: dts: meson-gxl: add USB support
>   ARM64: dts: meson-gxm: add GXM specific USB configuration
>   ARM64: dts: meson-gx-p23x-q20x: enable USB on P23x and Q20x boards
>
>  .../devicetree/bindings/phy/meson-gxl-usb2-phy.txt |  25 ++
>  .../devicetree/bindings/phy/meson-gxl-usb3-phy.txt |  27 ++
>  .../arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi |  12 +
>  arch/arm64/boot/dts/amlogic/meson-gxl.dtsi         |  49 +++
>  .../arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts |  17 +
>  arch/arm64/boot/dts/amlogic/meson-gxm.dtsi         |  10 +
>  drivers/phy/Kconfig                                |  13 +
>  drivers/phy/Makefile                               |   2 +
>  drivers/phy/phy-meson-gxl-usb2.c                   | 374 ++++++++++++++++++++
>  drivers/phy/phy-meson-gxl-usb3.c                   | 377 +++++++++++++++++++++
>  10 files changed, 906 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
>  create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
>  create mode 100644 drivers/phy/phy-meson-gxl-usb2.c
>  create mode 100644 drivers/phy/phy-meson-gxl-usb3.c
>
> --
> 2.10.2
>

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

* Re: [PATCH 0/5] Meson GXL and GXM USB support
  2016-11-26 14:56 ` Martin Blumenstingl
  (?)
@ 2016-11-28 14:30     ` Neil Armstrong
  -1 siblings, 0 replies; 39+ messages in thread
From: Neil Armstrong @ 2016-11-28 14:30 UTC (permalink / raw)
  To: Martin Blumenstingl,
	linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, kishon-l0cyMroinI0,
	khilman-rdvid1DuHRBWk0Htik3J/w, carlo-KA+7E9HrN00dnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A
  Cc: catalin.marinas-5wv7dgnIgG8, will.deacon-5wv7dgnIgG8

On 11/26/2016 03:56 PM, Martin Blumenstingl wrote:
> USB support on GXL and GXM differs a lot from Meson8b and GXBB:
> The most obvious change is that GXL and GXM now have one dwc3
> controller and one dwc2 controller (instead of two dwc2 controllers).
> With that there are also new USB PHYs.
> 
> Due to lack of hardware I was only able to test this on a board with
> GXM, but as far as I understand the hardware my preparations should be
> correct (so it should also work on GXL).
> 
> dwc2 will probably stay unused on most GXM devices since it's limited
> to device mode via some dwc2 hardware configuration register.
> 
> dwc3 is probably used on all devices, even if there is more than just
> one USB port. dwc3 has a built-in USB2 hub - on GXL this hub has two
> ports enabled, while on GXM there are three ports enabled (see below
> for lsusb output). There are no USB3 ports enabled in the dwc3 hardware
> configuration, meaning that the SoC is limited to high-speed mode.
> On my GXM device the dwc3 hardware configuration forces it into "host
> only" mode.
> 
> The SoCs contain two PHY blocks: one USB3 PHY and up to four USB2 PHYs
> (on GXM there are only three enabled, but the registers should support
> up to four).
> The USB3 PHY also handles the OTG interrupts, but since dwc3's hardware
> configuration enforces "host only" mode I was not able to test this. It
> simply takes care of an interrupt and then notifies all related PHYs
> about the new mode.
> The USB2 PHY block is a bit different: I created one PHY driver which
> spans all "PHY ports" because the handling is a bit tricky. It turns
> out that for each available USB port in dwc3's hub the corresponding
> PHY must be enabled (even if there is no physical port - in my case
> port 3 is not connected to anything, but disabling the PHY breaks
> ports 1 and 2 as well).
> I decided not not pass the USB2 PHYs directly to dwc3 due to three
> reasons: 1. the USB3 PHY (which holds a reference to all relevant
> USB2 PHY ports) controls the mode of the USB2 PHY ports (since both
> are used with the same controller and thus it makes sense to keep the
> mode consistent across all ports) 2. the dwc3 driver does not support
> passing multiple USB2 PHYs (only one USB2 and one USB3 PHY can be
> passed to it) 3. it is similar to how the vendor reference driver
> manages the PHYs. Please note that this coupling is not a fixed, this
> is all configurable via devicetree (so if the third USB2 PHY has to
> be passed two the dwc2 controller then this is still possible by
> just moving on PHY reference in the .dts).
> 
> The coupling of the USB2 and USB3 PHYs is the reason why I sent the
> two drivers in one patch, even though they are handling different IP
> blocks (different registers, etc.).
> 
> Unfortunately there are no datasheets available for any of these PHYs.
> Both drivers were written by reading the reference drivers provided by
> Amlogic and analyzing the registers on the kernel that was shipped with
> my board.
> 
> As a last note: the dwc3 driver currently only explicitly enables the
> first USB port "DWC3_GUSB2PHYCFG(0)" in the internal hub. The hardware
> seems to enable the other two (DWC3_GUSB2PHYCFG(1) and
> DWC3_GUSB2PHYCFG(2)) automatically. I will ask the dwc3 maintainers if
> changes to dwc3 are desired any how these should look like, but for now
> it's working fine even without changes there.
> 
> lsusb output on GXM for the dwc3 hub:
> Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
> ...
>  Hub Port Status:
>    Port 1: 0000.0100 power
>    Port 2: 0000.0100 power
>    Port 3: 0000.0100 power
> 
> NOTE: The devicetree changes depend on my previous series:
> "[PATCH 0/2] minor GXL and GXM improvements" - see [0]
> 
> NOTE2: This series depends on an upstream dwc3/xhci-plat DMA fix
> (special thanks to Arnd Bergmann and Sriram Dash for fixing that):
> "[PATCH v5 0/6] inherit dma configuration from parent dev" - see [1]
> 
> I have a tree with all dependencies applied available at [2] if
> someone wants a quick way to test this (I don't take any responsibility
> if anything explodes though).
> 
> [0] http://lists.infradead.org/pipermail/linux-amlogic/2016-November/001665.html
> [1] http://marc.info/?l=linux-usb&m=147938307209685&w=2
> [2] https://github.com/xdarklight/linux/commits/meson-gx-integration-4.10-20161126
> 
> Martin Blumenstingl (5):
>   Documentation: dt-bindings: Add documentation for Meson GXL USB2/3
>     PHYs
>   phy: meson: add USB2 and USB3 PHY support for Meson GXL
>   arm64: dts: meson-gxl: add USB support
>   ARM64: dts: meson-gxm: add GXM specific USB configuration
>   ARM64: dts: meson-gx-p23x-q20x: enable USB on P23x and Q20x boards
> 
>  .../devicetree/bindings/phy/meson-gxl-usb2-phy.txt |  25 ++
>  .../devicetree/bindings/phy/meson-gxl-usb3-phy.txt |  27 ++
>  .../arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi |  12 +
>  arch/arm64/boot/dts/amlogic/meson-gxl.dtsi         |  49 +++
>  .../arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts |  17 +
>  arch/arm64/boot/dts/amlogic/meson-gxm.dtsi         |  10 +
>  drivers/phy/Kconfig                                |  13 +
>  drivers/phy/Makefile                               |   2 +
>  drivers/phy/phy-meson-gxl-usb2.c                   | 374 ++++++++++++++++++++
>  drivers/phy/phy-meson-gxl-usb3.c                   | 377 +++++++++++++++++++++
>  10 files changed, 906 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
>  create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
>  create mode 100644 drivers/phy/phy-meson-gxl-usb2.c
>  create mode 100644 drivers/phy/phy-meson-gxl-usb3.c
> 

With the patchset in [1], tested successfully on a Nexbox A1.

On my Amlogic P230, the two USB-A ports works, the USB-OTG port work in Host mode, but does nothing connected as peripheral.
When trying to enable the DWC2 in peripheral mode, it fails by :
[   11.609586] dwc2 c9100000.usb: dwc2_core_reset() HANG! Soft Reset GRSTCTL=80000001
[   11.616962] dwc2 c9100000.usb: Specified GNPTXFDEP=1024 > 768
[   11.622643] dwc2 c9100000.usb: EPs: 7, dedicated fifos, 712 entries in SPRAM

Tested-by: Neil Armstrong <narmstrong-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 0/5] Meson GXL and GXM USB support
@ 2016-11-28 14:30     ` Neil Armstrong
  0 siblings, 0 replies; 39+ messages in thread
From: Neil Armstrong @ 2016-11-28 14:30 UTC (permalink / raw)
  To: linux-arm-kernel

On 11/26/2016 03:56 PM, Martin Blumenstingl wrote:
> USB support on GXL and GXM differs a lot from Meson8b and GXBB:
> The most obvious change is that GXL and GXM now have one dwc3
> controller and one dwc2 controller (instead of two dwc2 controllers).
> With that there are also new USB PHYs.
> 
> Due to lack of hardware I was only able to test this on a board with
> GXM, but as far as I understand the hardware my preparations should be
> correct (so it should also work on GXL).
> 
> dwc2 will probably stay unused on most GXM devices since it's limited
> to device mode via some dwc2 hardware configuration register.
> 
> dwc3 is probably used on all devices, even if there is more than just
> one USB port. dwc3 has a built-in USB2 hub - on GXL this hub has two
> ports enabled, while on GXM there are three ports enabled (see below
> for lsusb output). There are no USB3 ports enabled in the dwc3 hardware
> configuration, meaning that the SoC is limited to high-speed mode.
> On my GXM device the dwc3 hardware configuration forces it into "host
> only" mode.
> 
> The SoCs contain two PHY blocks: one USB3 PHY and up to four USB2 PHYs
> (on GXM there are only three enabled, but the registers should support
> up to four).
> The USB3 PHY also handles the OTG interrupts, but since dwc3's hardware
> configuration enforces "host only" mode I was not able to test this. It
> simply takes care of an interrupt and then notifies all related PHYs
> about the new mode.
> The USB2 PHY block is a bit different: I created one PHY driver which
> spans all "PHY ports" because the handling is a bit tricky. It turns
> out that for each available USB port in dwc3's hub the corresponding
> PHY must be enabled (even if there is no physical port - in my case
> port 3 is not connected to anything, but disabling the PHY breaks
> ports 1 and 2 as well).
> I decided not not pass the USB2 PHYs directly to dwc3 due to three
> reasons: 1. the USB3 PHY (which holds a reference to all relevant
> USB2 PHY ports) controls the mode of the USB2 PHY ports (since both
> are used with the same controller and thus it makes sense to keep the
> mode consistent across all ports) 2. the dwc3 driver does not support
> passing multiple USB2 PHYs (only one USB2 and one USB3 PHY can be
> passed to it) 3. it is similar to how the vendor reference driver
> manages the PHYs. Please note that this coupling is not a fixed, this
> is all configurable via devicetree (so if the third USB2 PHY has to
> be passed two the dwc2 controller then this is still possible by
> just moving on PHY reference in the .dts).
> 
> The coupling of the USB2 and USB3 PHYs is the reason why I sent the
> two drivers in one patch, even though they are handling different IP
> blocks (different registers, etc.).
> 
> Unfortunately there are no datasheets available for any of these PHYs.
> Both drivers were written by reading the reference drivers provided by
> Amlogic and analyzing the registers on the kernel that was shipped with
> my board.
> 
> As a last note: the dwc3 driver currently only explicitly enables the
> first USB port "DWC3_GUSB2PHYCFG(0)" in the internal hub. The hardware
> seems to enable the other two (DWC3_GUSB2PHYCFG(1) and
> DWC3_GUSB2PHYCFG(2)) automatically. I will ask the dwc3 maintainers if
> changes to dwc3 are desired any how these should look like, but for now
> it's working fine even without changes there.
> 
> lsusb output on GXM for the dwc3 hub:
> Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
> ...
>  Hub Port Status:
>    Port 1: 0000.0100 power
>    Port 2: 0000.0100 power
>    Port 3: 0000.0100 power
> 
> NOTE: The devicetree changes depend on my previous series:
> "[PATCH 0/2] minor GXL and GXM improvements" - see [0]
> 
> NOTE2: This series depends on an upstream dwc3/xhci-plat DMA fix
> (special thanks to Arnd Bergmann and Sriram Dash for fixing that):
> "[PATCH v5 0/6] inherit dma configuration from parent dev" - see [1]
> 
> I have a tree with all dependencies applied available at [2] if
> someone wants a quick way to test this (I don't take any responsibility
> if anything explodes though).
> 
> [0] http://lists.infradead.org/pipermail/linux-amlogic/2016-November/001665.html
> [1] http://marc.info/?l=linux-usb&m=147938307209685&w=2
> [2] https://github.com/xdarklight/linux/commits/meson-gx-integration-4.10-20161126
> 
> Martin Blumenstingl (5):
>   Documentation: dt-bindings: Add documentation for Meson GXL USB2/3
>     PHYs
>   phy: meson: add USB2 and USB3 PHY support for Meson GXL
>   arm64: dts: meson-gxl: add USB support
>   ARM64: dts: meson-gxm: add GXM specific USB configuration
>   ARM64: dts: meson-gx-p23x-q20x: enable USB on P23x and Q20x boards
> 
>  .../devicetree/bindings/phy/meson-gxl-usb2-phy.txt |  25 ++
>  .../devicetree/bindings/phy/meson-gxl-usb3-phy.txt |  27 ++
>  .../arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi |  12 +
>  arch/arm64/boot/dts/amlogic/meson-gxl.dtsi         |  49 +++
>  .../arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts |  17 +
>  arch/arm64/boot/dts/amlogic/meson-gxm.dtsi         |  10 +
>  drivers/phy/Kconfig                                |  13 +
>  drivers/phy/Makefile                               |   2 +
>  drivers/phy/phy-meson-gxl-usb2.c                   | 374 ++++++++++++++++++++
>  drivers/phy/phy-meson-gxl-usb3.c                   | 377 +++++++++++++++++++++
>  10 files changed, 906 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
>  create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
>  create mode 100644 drivers/phy/phy-meson-gxl-usb2.c
>  create mode 100644 drivers/phy/phy-meson-gxl-usb3.c
> 

With the patchset in [1], tested successfully on a Nexbox A1.

On my Amlogic P230, the two USB-A ports works, the USB-OTG port work in Host mode, but does nothing connected as peripheral.
When trying to enable the DWC2 in peripheral mode, it fails by :
[   11.609586] dwc2 c9100000.usb: dwc2_core_reset() HANG! Soft Reset GRSTCTL=80000001
[   11.616962] dwc2 c9100000.usb: Specified GNPTXFDEP=1024 > 768
[   11.622643] dwc2 c9100000.usb: EPs: 7, dedicated fifos, 712 entries in SPRAM

Tested-by: Neil Armstrong <narmstrong@baylibre.com>

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

* [PATCH 0/5] Meson GXL and GXM USB support
@ 2016-11-28 14:30     ` Neil Armstrong
  0 siblings, 0 replies; 39+ messages in thread
From: Neil Armstrong @ 2016-11-28 14:30 UTC (permalink / raw)
  To: linus-amlogic

On 11/26/2016 03:56 PM, Martin Blumenstingl wrote:
> USB support on GXL and GXM differs a lot from Meson8b and GXBB:
> The most obvious change is that GXL and GXM now have one dwc3
> controller and one dwc2 controller (instead of two dwc2 controllers).
> With that there are also new USB PHYs.
> 
> Due to lack of hardware I was only able to test this on a board with
> GXM, but as far as I understand the hardware my preparations should be
> correct (so it should also work on GXL).
> 
> dwc2 will probably stay unused on most GXM devices since it's limited
> to device mode via some dwc2 hardware configuration register.
> 
> dwc3 is probably used on all devices, even if there is more than just
> one USB port. dwc3 has a built-in USB2 hub - on GXL this hub has two
> ports enabled, while on GXM there are three ports enabled (see below
> for lsusb output). There are no USB3 ports enabled in the dwc3 hardware
> configuration, meaning that the SoC is limited to high-speed mode.
> On my GXM device the dwc3 hardware configuration forces it into "host
> only" mode.
> 
> The SoCs contain two PHY blocks: one USB3 PHY and up to four USB2 PHYs
> (on GXM there are only three enabled, but the registers should support
> up to four).
> The USB3 PHY also handles the OTG interrupts, but since dwc3's hardware
> configuration enforces "host only" mode I was not able to test this. It
> simply takes care of an interrupt and then notifies all related PHYs
> about the new mode.
> The USB2 PHY block is a bit different: I created one PHY driver which
> spans all "PHY ports" because the handling is a bit tricky. It turns
> out that for each available USB port in dwc3's hub the corresponding
> PHY must be enabled (even if there is no physical port - in my case
> port 3 is not connected to anything, but disabling the PHY breaks
> ports 1 and 2 as well).
> I decided not not pass the USB2 PHYs directly to dwc3 due to three
> reasons: 1. the USB3 PHY (which holds a reference to all relevant
> USB2 PHY ports) controls the mode of the USB2 PHY ports (since both
> are used with the same controller and thus it makes sense to keep the
> mode consistent across all ports) 2. the dwc3 driver does not support
> passing multiple USB2 PHYs (only one USB2 and one USB3 PHY can be
> passed to it) 3. it is similar to how the vendor reference driver
> manages the PHYs. Please note that this coupling is not a fixed, this
> is all configurable via devicetree (so if the third USB2 PHY has to
> be passed two the dwc2 controller then this is still possible by
> just moving on PHY reference in the .dts).
> 
> The coupling of the USB2 and USB3 PHYs is the reason why I sent the
> two drivers in one patch, even though they are handling different IP
> blocks (different registers, etc.).
> 
> Unfortunately there are no datasheets available for any of these PHYs.
> Both drivers were written by reading the reference drivers provided by
> Amlogic and analyzing the registers on the kernel that was shipped with
> my board.
> 
> As a last note: the dwc3 driver currently only explicitly enables the
> first USB port "DWC3_GUSB2PHYCFG(0)" in the internal hub. The hardware
> seems to enable the other two (DWC3_GUSB2PHYCFG(1) and
> DWC3_GUSB2PHYCFG(2)) automatically. I will ask the dwc3 maintainers if
> changes to dwc3 are desired any how these should look like, but for now
> it's working fine even without changes there.
> 
> lsusb output on GXM for the dwc3 hub:
> Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
> ...
>  Hub Port Status:
>    Port 1: 0000.0100 power
>    Port 2: 0000.0100 power
>    Port 3: 0000.0100 power
> 
> NOTE: The devicetree changes depend on my previous series:
> "[PATCH 0/2] minor GXL and GXM improvements" - see [0]
> 
> NOTE2: This series depends on an upstream dwc3/xhci-plat DMA fix
> (special thanks to Arnd Bergmann and Sriram Dash for fixing that):
> "[PATCH v5 0/6] inherit dma configuration from parent dev" - see [1]
> 
> I have a tree with all dependencies applied available at [2] if
> someone wants a quick way to test this (I don't take any responsibility
> if anything explodes though).
> 
> [0] http://lists.infradead.org/pipermail/linux-amlogic/2016-November/001665.html
> [1] http://marc.info/?l=linux-usb&m=147938307209685&w=2
> [2] https://github.com/xdarklight/linux/commits/meson-gx-integration-4.10-20161126
> 
> Martin Blumenstingl (5):
>   Documentation: dt-bindings: Add documentation for Meson GXL USB2/3
>     PHYs
>   phy: meson: add USB2 and USB3 PHY support for Meson GXL
>   arm64: dts: meson-gxl: add USB support
>   ARM64: dts: meson-gxm: add GXM specific USB configuration
>   ARM64: dts: meson-gx-p23x-q20x: enable USB on P23x and Q20x boards
> 
>  .../devicetree/bindings/phy/meson-gxl-usb2-phy.txt |  25 ++
>  .../devicetree/bindings/phy/meson-gxl-usb3-phy.txt |  27 ++
>  .../arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi |  12 +
>  arch/arm64/boot/dts/amlogic/meson-gxl.dtsi         |  49 +++
>  .../arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts |  17 +
>  arch/arm64/boot/dts/amlogic/meson-gxm.dtsi         |  10 +
>  drivers/phy/Kconfig                                |  13 +
>  drivers/phy/Makefile                               |   2 +
>  drivers/phy/phy-meson-gxl-usb2.c                   | 374 ++++++++++++++++++++
>  drivers/phy/phy-meson-gxl-usb3.c                   | 377 +++++++++++++++++++++
>  10 files changed, 906 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
>  create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
>  create mode 100644 drivers/phy/phy-meson-gxl-usb2.c
>  create mode 100644 drivers/phy/phy-meson-gxl-usb3.c
> 

With the patchset in [1], tested successfully on a Nexbox A1.

On my Amlogic P230, the two USB-A ports works, the USB-OTG port work in Host mode, but does nothing connected as peripheral.
When trying to enable the DWC2 in peripheral mode, it fails by :
[   11.609586] dwc2 c9100000.usb: dwc2_core_reset() HANG! Soft Reset GRSTCTL=80000001
[   11.616962] dwc2 c9100000.usb: Specified GNPTXFDEP=1024 > 768
[   11.622643] dwc2 c9100000.usb: EPs: 7, dedicated fifos, 712 entries in SPRAM

Tested-by: Neil Armstrong <narmstrong@baylibre.com>

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

* Re: [PATCH 0/5] Meson GXL and GXM USB support
  2016-11-27 22:42     ` Martin Blumenstingl
  (?)
@ 2016-11-30 22:22         ` Rob Herring
  -1 siblings, 0 replies; 39+ messages in thread
From: Rob Herring @ 2016-11-30 22:22 UTC (permalink / raw)
  To: Martin Blumenstingl
  Cc: linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, kishon-l0cyMroinI0,
	khilman-rdvid1DuHRBWk0Htik3J/w, carlo-KA+7E9HrN00dnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, catalin.marinas-5wv7dgnIgG8,
	will.deacon-5wv7dgnIgG8, narmstrong-rdvid1DuHRBWk0Htik3J/w

On Sun, Nov 27, 2016 at 11:42:02PM +0100, Martin Blumenstingl wrote:
> Hello Kishon,
> 
> On Sat, Nov 26, 2016 at 3:56 PM, Martin Blumenstingl
> <martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org> wrote:
> > USB support on GXL and GXM differs a lot from Meson8b and GXBB:
> > The most obvious change is that GXL and GXM now have one dwc3
> > controller and one dwc2 controller (instead of two dwc2 controllers).
> > With that there are also new USB PHYs.
> >
> > Due to lack of hardware I was only able to test this on a board with
> > GXM, but as far as I understand the hardware my preparations should be
> > correct (so it should also work on GXL).
> >
> > dwc2 will probably stay unused on most GXM devices since it's limited
> > to device mode via some dwc2 hardware configuration register.
> >
> > dwc3 is probably used on all devices, even if there is more than just
> > one USB port. dwc3 has a built-in USB2 hub - on GXL this hub has two
> > ports enabled, while on GXM there are three ports enabled (see below

This hub is an actual USB hub? If so, then you should probably model the 
USB bus topology (which we have a binding definition for).

> > for lsusb output). There are no USB3 ports enabled in the dwc3 hardware
> > configuration, meaning that the SoC is limited to high-speed mode.
> > On my GXM device the dwc3 hardware configuration forces it into "host
> > only" mode.
> >
> > The SoCs contain two PHY blocks: one USB3 PHY and up to four USB2 PHYs
> > (on GXM there are only three enabled, but the registers should support
> > up to four).
> > The USB3 PHY also handles the OTG interrupts, but since dwc3's hardware
> > configuration enforces "host only" mode I was not able to test this. It
> > simply takes care of an interrupt and then notifies all related PHYs
> > about the new mode.
> > The USB2 PHY block is a bit different: I created one PHY driver which
> > spans all "PHY ports" because the handling is a bit tricky. It turns
> > out that for each available USB port in dwc3's hub the corresponding
> > PHY must be enabled (even if there is no physical port - in my case
> > port 3 is not connected to anything, but disabling the PHY breaks
> > ports 1 and 2 as well).
> > I decided not not pass the USB2 PHYs directly to dwc3 due to three
> > reasons: 1. the USB3 PHY (which holds a reference to all relevant
> > USB2 PHY ports) controls the mode of the USB2 PHY ports (since both
> > are used with the same controller and thus it makes sense to keep the
> > mode consistent across all ports) 2. the dwc3 driver does not support
> > passing multiple USB2 PHYs (only one USB2 and one USB3 PHY can be
> > passed to it) 3. it is similar to how the vendor reference driver
> > manages the PHYs. Please note that this coupling is not a fixed, this
> > is all configurable via devicetree (so if the third USB2 PHY has to
> > be passed two the dwc2 controller then this is still possible by
> > just moving on PHY reference in the .dts).
> after not staring at my own code for 24 hours I realized this:
> (I went through quite a few iterations before getting these drivers to work)
> I'm basically re-modelling an "USB PHY hub" with my USB3 PHY driver
> (there's one "upstream" PHY interface which is passed to dwc3 and
> multiple downstream PHYs, each for one port on dwc3's internal hub).
> With this approach I could split each of the the USB2s into separate
> nodes again (instead of one devicetree node with #phy-cells = <1>) as
> the USB3 PHY is taking care of that special "we have to enable all
> ports or no port will be usable".
> 
> We could go even one step further: why implement this in the Meson GXL
> specific PHY driver - why not implement a generic "phy-hub" driver
> (which would be valid whenever the PHY controller has to manage
> multiple PHYs at once, but wants to keep them all in a consistent
> state).
> The devicetree could look like this:
>     usb2_phy_hub: phy@0 {
>         compatible = "phy-hub";
>         phys = <&other_phy1>, <&other_phy 2>;
>     };
> 
> &dwc3 {
>      phys = <&usb2_phy_hub>, <&usb3_phy0>;
>      phy-names = "usb2-phy", "usb3-phy";
> };

I'm okay with a hub if it is modeled as a USB hub. Here though, it 
looks like you are just trying to group things which doesn't need to be 
in DT.

> 
> The generic phy-hub driver would then implement all phy_ops callbacks
> and pass then to each of it's downstream PHYs.

You can have generic drivers without a generic binding.

> That's just what came into my head - please let me know what you think
> of this or share your ideas on how to approach this!
> 
> > The coupling of the USB2 and USB3 PHYs is the reason why I sent the
> > two drivers in one patch, even though they are handling different IP
> > blocks (different registers, etc.).
> >
> > Unfortunately there are no datasheets available for any of these PHYs.
> > Both drivers were written by reading the reference drivers provided by
> > Amlogic and analyzing the registers on the kernel that was shipped with
> > my board.
> >
> > As a last note: the dwc3 driver currently only explicitly enables the
> > first USB port "DWC3_GUSB2PHYCFG(0)" in the internal hub. The hardware
> > seems to enable the other two (DWC3_GUSB2PHYCFG(1) and
> > DWC3_GUSB2PHYCFG(2)) automatically. I will ask the dwc3 maintainers if
> > changes to dwc3 are desired any how these should look like, but for now
> > it's working fine even without changes there.
> >
> > lsusb output on GXM for the dwc3 hub:
> > Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
> > ...
> >  Hub Port Status:
> >    Port 1: 0000.0100 power
> >    Port 2: 0000.0100 power
> >    Port 3: 0000.0100 power
> >
> > NOTE: The devicetree changes depend on my previous series:
> > "[PATCH 0/2] minor GXL and GXM improvements" - see [0]
> >
> > NOTE2: This series depends on an upstream dwc3/xhci-plat DMA fix
> > (special thanks to Arnd Bergmann and Sriram Dash for fixing that):
> > "[PATCH v5 0/6] inherit dma configuration from parent dev" - see [1]
> >
> > I have a tree with all dependencies applied available at [2] if
> > someone wants a quick way to test this (I don't take any responsibility
> > if anything explodes though).
> >
> > [0] http://lists.infradead.org/pipermail/linux-amlogic/2016-November/001665.html
> > [1] http://marc.info/?l=linux-usb&m=147938307209685&w=2
> > [2] https://github.com/xdarklight/linux/commits/meson-gx-integration-4.10-20161126
> >
> > Martin Blumenstingl (5):
> >   Documentation: dt-bindings: Add documentation for Meson GXL USB2/3
> >     PHYs
> >   phy: meson: add USB2 and USB3 PHY support for Meson GXL
> >   arm64: dts: meson-gxl: add USB support
> >   ARM64: dts: meson-gxm: add GXM specific USB configuration
> >   ARM64: dts: meson-gx-p23x-q20x: enable USB on P23x and Q20x boards
> >
> >  .../devicetree/bindings/phy/meson-gxl-usb2-phy.txt |  25 ++
> >  .../devicetree/bindings/phy/meson-gxl-usb3-phy.txt |  27 ++
> >  .../arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi |  12 +
> >  arch/arm64/boot/dts/amlogic/meson-gxl.dtsi         |  49 +++
> >  .../arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts |  17 +
> >  arch/arm64/boot/dts/amlogic/meson-gxm.dtsi         |  10 +
> >  drivers/phy/Kconfig                                |  13 +
> >  drivers/phy/Makefile                               |   2 +
> >  drivers/phy/phy-meson-gxl-usb2.c                   | 374 ++++++++++++++++++++
> >  drivers/phy/phy-meson-gxl-usb3.c                   | 377 +++++++++++++++++++++
> >  10 files changed, 906 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
> >  create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
> >  create mode 100644 drivers/phy/phy-meson-gxl-usb2.c
> >  create mode 100644 drivers/phy/phy-meson-gxl-usb3.c
> >
> > --
> > 2.10.2
> >
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 0/5] Meson GXL and GXM USB support
@ 2016-11-30 22:22         ` Rob Herring
  0 siblings, 0 replies; 39+ messages in thread
From: Rob Herring @ 2016-11-30 22:22 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, Nov 27, 2016 at 11:42:02PM +0100, Martin Blumenstingl wrote:
> Hello Kishon,
> 
> On Sat, Nov 26, 2016 at 3:56 PM, Martin Blumenstingl
> <martin.blumenstingl@googlemail.com> wrote:
> > USB support on GXL and GXM differs a lot from Meson8b and GXBB:
> > The most obvious change is that GXL and GXM now have one dwc3
> > controller and one dwc2 controller (instead of two dwc2 controllers).
> > With that there are also new USB PHYs.
> >
> > Due to lack of hardware I was only able to test this on a board with
> > GXM, but as far as I understand the hardware my preparations should be
> > correct (so it should also work on GXL).
> >
> > dwc2 will probably stay unused on most GXM devices since it's limited
> > to device mode via some dwc2 hardware configuration register.
> >
> > dwc3 is probably used on all devices, even if there is more than just
> > one USB port. dwc3 has a built-in USB2 hub - on GXL this hub has two
> > ports enabled, while on GXM there are three ports enabled (see below

This hub is an actual USB hub? If so, then you should probably model the 
USB bus topology (which we have a binding definition for).

> > for lsusb output). There are no USB3 ports enabled in the dwc3 hardware
> > configuration, meaning that the SoC is limited to high-speed mode.
> > On my GXM device the dwc3 hardware configuration forces it into "host
> > only" mode.
> >
> > The SoCs contain two PHY blocks: one USB3 PHY and up to four USB2 PHYs
> > (on GXM there are only three enabled, but the registers should support
> > up to four).
> > The USB3 PHY also handles the OTG interrupts, but since dwc3's hardware
> > configuration enforces "host only" mode I was not able to test this. It
> > simply takes care of an interrupt and then notifies all related PHYs
> > about the new mode.
> > The USB2 PHY block is a bit different: I created one PHY driver which
> > spans all "PHY ports" because the handling is a bit tricky. It turns
> > out that for each available USB port in dwc3's hub the corresponding
> > PHY must be enabled (even if there is no physical port - in my case
> > port 3 is not connected to anything, but disabling the PHY breaks
> > ports 1 and 2 as well).
> > I decided not not pass the USB2 PHYs directly to dwc3 due to three
> > reasons: 1. the USB3 PHY (which holds a reference to all relevant
> > USB2 PHY ports) controls the mode of the USB2 PHY ports (since both
> > are used with the same controller and thus it makes sense to keep the
> > mode consistent across all ports) 2. the dwc3 driver does not support
> > passing multiple USB2 PHYs (only one USB2 and one USB3 PHY can be
> > passed to it) 3. it is similar to how the vendor reference driver
> > manages the PHYs. Please note that this coupling is not a fixed, this
> > is all configurable via devicetree (so if the third USB2 PHY has to
> > be passed two the dwc2 controller then this is still possible by
> > just moving on PHY reference in the .dts).
> after not staring at my own code for 24 hours I realized this:
> (I went through quite a few iterations before getting these drivers to work)
> I'm basically re-modelling an "USB PHY hub" with my USB3 PHY driver
> (there's one "upstream" PHY interface which is passed to dwc3 and
> multiple downstream PHYs, each for one port on dwc3's internal hub).
> With this approach I could split each of the the USB2s into separate
> nodes again (instead of one devicetree node with #phy-cells = <1>) as
> the USB3 PHY is taking care of that special "we have to enable all
> ports or no port will be usable".
> 
> We could go even one step further: why implement this in the Meson GXL
> specific PHY driver - why not implement a generic "phy-hub" driver
> (which would be valid whenever the PHY controller has to manage
> multiple PHYs at once, but wants to keep them all in a consistent
> state).
> The devicetree could look like this:
>     usb2_phy_hub: phy at 0 {
>         compatible = "phy-hub";
>         phys = <&other_phy1>, <&other_phy 2>;
>     };
> 
> &dwc3 {
>      phys = <&usb2_phy_hub>, <&usb3_phy0>;
>      phy-names = "usb2-phy", "usb3-phy";
> };

I'm okay with a hub if it is modeled as a USB hub. Here though, it 
looks like you are just trying to group things which doesn't need to be 
in DT.

> 
> The generic phy-hub driver would then implement all phy_ops callbacks
> and pass then to each of it's downstream PHYs.

You can have generic drivers without a generic binding.

> That's just what came into my head - please let me know what you think
> of this or share your ideas on how to approach this!
> 
> > The coupling of the USB2 and USB3 PHYs is the reason why I sent the
> > two drivers in one patch, even though they are handling different IP
> > blocks (different registers, etc.).
> >
> > Unfortunately there are no datasheets available for any of these PHYs.
> > Both drivers were written by reading the reference drivers provided by
> > Amlogic and analyzing the registers on the kernel that was shipped with
> > my board.
> >
> > As a last note: the dwc3 driver currently only explicitly enables the
> > first USB port "DWC3_GUSB2PHYCFG(0)" in the internal hub. The hardware
> > seems to enable the other two (DWC3_GUSB2PHYCFG(1) and
> > DWC3_GUSB2PHYCFG(2)) automatically. I will ask the dwc3 maintainers if
> > changes to dwc3 are desired any how these should look like, but for now
> > it's working fine even without changes there.
> >
> > lsusb output on GXM for the dwc3 hub:
> > Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
> > ...
> >  Hub Port Status:
> >    Port 1: 0000.0100 power
> >    Port 2: 0000.0100 power
> >    Port 3: 0000.0100 power
> >
> > NOTE: The devicetree changes depend on my previous series:
> > "[PATCH 0/2] minor GXL and GXM improvements" - see [0]
> >
> > NOTE2: This series depends on an upstream dwc3/xhci-plat DMA fix
> > (special thanks to Arnd Bergmann and Sriram Dash for fixing that):
> > "[PATCH v5 0/6] inherit dma configuration from parent dev" - see [1]
> >
> > I have a tree with all dependencies applied available at [2] if
> > someone wants a quick way to test this (I don't take any responsibility
> > if anything explodes though).
> >
> > [0] http://lists.infradead.org/pipermail/linux-amlogic/2016-November/001665.html
> > [1] http://marc.info/?l=linux-usb&m=147938307209685&w=2
> > [2] https://github.com/xdarklight/linux/commits/meson-gx-integration-4.10-20161126
> >
> > Martin Blumenstingl (5):
> >   Documentation: dt-bindings: Add documentation for Meson GXL USB2/3
> >     PHYs
> >   phy: meson: add USB2 and USB3 PHY support for Meson GXL
> >   arm64: dts: meson-gxl: add USB support
> >   ARM64: dts: meson-gxm: add GXM specific USB configuration
> >   ARM64: dts: meson-gx-p23x-q20x: enable USB on P23x and Q20x boards
> >
> >  .../devicetree/bindings/phy/meson-gxl-usb2-phy.txt |  25 ++
> >  .../devicetree/bindings/phy/meson-gxl-usb3-phy.txt |  27 ++
> >  .../arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi |  12 +
> >  arch/arm64/boot/dts/amlogic/meson-gxl.dtsi         |  49 +++
> >  .../arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts |  17 +
> >  arch/arm64/boot/dts/amlogic/meson-gxm.dtsi         |  10 +
> >  drivers/phy/Kconfig                                |  13 +
> >  drivers/phy/Makefile                               |   2 +
> >  drivers/phy/phy-meson-gxl-usb2.c                   | 374 ++++++++++++++++++++
> >  drivers/phy/phy-meson-gxl-usb3.c                   | 377 +++++++++++++++++++++
> >  10 files changed, 906 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
> >  create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
> >  create mode 100644 drivers/phy/phy-meson-gxl-usb2.c
> >  create mode 100644 drivers/phy/phy-meson-gxl-usb3.c
> >
> > --
> > 2.10.2
> >

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

* [PATCH 0/5] Meson GXL and GXM USB support
@ 2016-11-30 22:22         ` Rob Herring
  0 siblings, 0 replies; 39+ messages in thread
From: Rob Herring @ 2016-11-30 22:22 UTC (permalink / raw)
  To: linus-amlogic

On Sun, Nov 27, 2016 at 11:42:02PM +0100, Martin Blumenstingl wrote:
> Hello Kishon,
> 
> On Sat, Nov 26, 2016 at 3:56 PM, Martin Blumenstingl
> <martin.blumenstingl@googlemail.com> wrote:
> > USB support on GXL and GXM differs a lot from Meson8b and GXBB:
> > The most obvious change is that GXL and GXM now have one dwc3
> > controller and one dwc2 controller (instead of two dwc2 controllers).
> > With that there are also new USB PHYs.
> >
> > Due to lack of hardware I was only able to test this on a board with
> > GXM, but as far as I understand the hardware my preparations should be
> > correct (so it should also work on GXL).
> >
> > dwc2 will probably stay unused on most GXM devices since it's limited
> > to device mode via some dwc2 hardware configuration register.
> >
> > dwc3 is probably used on all devices, even if there is more than just
> > one USB port. dwc3 has a built-in USB2 hub - on GXL this hub has two
> > ports enabled, while on GXM there are three ports enabled (see below

This hub is an actual USB hub? If so, then you should probably model the 
USB bus topology (which we have a binding definition for).

> > for lsusb output). There are no USB3 ports enabled in the dwc3 hardware
> > configuration, meaning that the SoC is limited to high-speed mode.
> > On my GXM device the dwc3 hardware configuration forces it into "host
> > only" mode.
> >
> > The SoCs contain two PHY blocks: one USB3 PHY and up to four USB2 PHYs
> > (on GXM there are only three enabled, but the registers should support
> > up to four).
> > The USB3 PHY also handles the OTG interrupts, but since dwc3's hardware
> > configuration enforces "host only" mode I was not able to test this. It
> > simply takes care of an interrupt and then notifies all related PHYs
> > about the new mode.
> > The USB2 PHY block is a bit different: I created one PHY driver which
> > spans all "PHY ports" because the handling is a bit tricky. It turns
> > out that for each available USB port in dwc3's hub the corresponding
> > PHY must be enabled (even if there is no physical port - in my case
> > port 3 is not connected to anything, but disabling the PHY breaks
> > ports 1 and 2 as well).
> > I decided not not pass the USB2 PHYs directly to dwc3 due to three
> > reasons: 1. the USB3 PHY (which holds a reference to all relevant
> > USB2 PHY ports) controls the mode of the USB2 PHY ports (since both
> > are used with the same controller and thus it makes sense to keep the
> > mode consistent across all ports) 2. the dwc3 driver does not support
> > passing multiple USB2 PHYs (only one USB2 and one USB3 PHY can be
> > passed to it) 3. it is similar to how the vendor reference driver
> > manages the PHYs. Please note that this coupling is not a fixed, this
> > is all configurable via devicetree (so if the third USB2 PHY has to
> > be passed two the dwc2 controller then this is still possible by
> > just moving on PHY reference in the .dts).
> after not staring at my own code for 24 hours I realized this:
> (I went through quite a few iterations before getting these drivers to work)
> I'm basically re-modelling an "USB PHY hub" with my USB3 PHY driver
> (there's one "upstream" PHY interface which is passed to dwc3 and
> multiple downstream PHYs, each for one port on dwc3's internal hub).
> With this approach I could split each of the the USB2s into separate
> nodes again (instead of one devicetree node with #phy-cells = <1>) as
> the USB3 PHY is taking care of that special "we have to enable all
> ports or no port will be usable".
> 
> We could go even one step further: why implement this in the Meson GXL
> specific PHY driver - why not implement a generic "phy-hub" driver
> (which would be valid whenever the PHY controller has to manage
> multiple PHYs at once, but wants to keep them all in a consistent
> state).
> The devicetree could look like this:
>     usb2_phy_hub: phy at 0 {
>         compatible = "phy-hub";
>         phys = <&other_phy1>, <&other_phy 2>;
>     };
> 
> &dwc3 {
>      phys = <&usb2_phy_hub>, <&usb3_phy0>;
>      phy-names = "usb2-phy", "usb3-phy";
> };

I'm okay with a hub if it is modeled as a USB hub. Here though, it 
looks like you are just trying to group things which doesn't need to be 
in DT.

> 
> The generic phy-hub driver would then implement all phy_ops callbacks
> and pass then to each of it's downstream PHYs.

You can have generic drivers without a generic binding.

> That's just what came into my head - please let me know what you think
> of this or share your ideas on how to approach this!
> 
> > The coupling of the USB2 and USB3 PHYs is the reason why I sent the
> > two drivers in one patch, even though they are handling different IP
> > blocks (different registers, etc.).
> >
> > Unfortunately there are no datasheets available for any of these PHYs.
> > Both drivers were written by reading the reference drivers provided by
> > Amlogic and analyzing the registers on the kernel that was shipped with
> > my board.
> >
> > As a last note: the dwc3 driver currently only explicitly enables the
> > first USB port "DWC3_GUSB2PHYCFG(0)" in the internal hub. The hardware
> > seems to enable the other two (DWC3_GUSB2PHYCFG(1) and
> > DWC3_GUSB2PHYCFG(2)) automatically. I will ask the dwc3 maintainers if
> > changes to dwc3 are desired any how these should look like, but for now
> > it's working fine even without changes there.
> >
> > lsusb output on GXM for the dwc3 hub:
> > Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
> > ...
> >  Hub Port Status:
> >    Port 1: 0000.0100 power
> >    Port 2: 0000.0100 power
> >    Port 3: 0000.0100 power
> >
> > NOTE: The devicetree changes depend on my previous series:
> > "[PATCH 0/2] minor GXL and GXM improvements" - see [0]
> >
> > NOTE2: This series depends on an upstream dwc3/xhci-plat DMA fix
> > (special thanks to Arnd Bergmann and Sriram Dash for fixing that):
> > "[PATCH v5 0/6] inherit dma configuration from parent dev" - see [1]
> >
> > I have a tree with all dependencies applied available at [2] if
> > someone wants a quick way to test this (I don't take any responsibility
> > if anything explodes though).
> >
> > [0] http://lists.infradead.org/pipermail/linux-amlogic/2016-November/001665.html
> > [1] http://marc.info/?l=linux-usb&m=147938307209685&w=2
> > [2] https://github.com/xdarklight/linux/commits/meson-gx-integration-4.10-20161126
> >
> > Martin Blumenstingl (5):
> >   Documentation: dt-bindings: Add documentation for Meson GXL USB2/3
> >     PHYs
> >   phy: meson: add USB2 and USB3 PHY support for Meson GXL
> >   arm64: dts: meson-gxl: add USB support
> >   ARM64: dts: meson-gxm: add GXM specific USB configuration
> >   ARM64: dts: meson-gx-p23x-q20x: enable USB on P23x and Q20x boards
> >
> >  .../devicetree/bindings/phy/meson-gxl-usb2-phy.txt |  25 ++
> >  .../devicetree/bindings/phy/meson-gxl-usb3-phy.txt |  27 ++
> >  .../arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi |  12 +
> >  arch/arm64/boot/dts/amlogic/meson-gxl.dtsi         |  49 +++
> >  .../arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts |  17 +
> >  arch/arm64/boot/dts/amlogic/meson-gxm.dtsi         |  10 +
> >  drivers/phy/Kconfig                                |  13 +
> >  drivers/phy/Makefile                               |   2 +
> >  drivers/phy/phy-meson-gxl-usb2.c                   | 374 ++++++++++++++++++++
> >  drivers/phy/phy-meson-gxl-usb3.c                   | 377 +++++++++++++++++++++
> >  10 files changed, 906 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
> >  create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
> >  create mode 100644 drivers/phy/phy-meson-gxl-usb2.c
> >  create mode 100644 drivers/phy/phy-meson-gxl-usb3.c
> >
> > --
> > 2.10.2
> >

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

* Re: [PATCH 0/5] Meson GXL and GXM USB support
  2016-11-30 22:22         ` Rob Herring
  (?)
@ 2016-11-30 22:49           ` Martin Blumenstingl
  -1 siblings, 0 replies; 39+ messages in thread
From: Martin Blumenstingl @ 2016-11-30 22:49 UTC (permalink / raw)
  To: Rob Herring
  Cc: linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, kishon-l0cyMroinI0,
	khilman-rdvid1DuHRBWk0Htik3J/w, carlo-KA+7E9HrN00dnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, catalin.marinas-5wv7dgnIgG8,
	will.deacon-5wv7dgnIgG8, narmstrong-rdvid1DuHRBWk0Htik3J/w

On Wed, Nov 30, 2016 at 11:22 PM, Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
> On Sun, Nov 27, 2016 at 11:42:02PM +0100, Martin Blumenstingl wrote:
>> Hello Kishon,
>>
>> On Sat, Nov 26, 2016 at 3:56 PM, Martin Blumenstingl
>> <martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org> wrote:
>> > USB support on GXL and GXM differs a lot from Meson8b and GXBB:
>> > The most obvious change is that GXL and GXM now have one dwc3
>> > controller and one dwc2 controller (instead of two dwc2 controllers).
>> > With that there are also new USB PHYs.
>> >
>> > Due to lack of hardware I was only able to test this on a board with
>> > GXM, but as far as I understand the hardware my preparations should be
>> > correct (so it should also work on GXL).
>> >
>> > dwc2 will probably stay unused on most GXM devices since it's limited
>> > to device mode via some dwc2 hardware configuration register.
>> >
>> > dwc3 is probably used on all devices, even if there is more than just
>> > one USB port. dwc3 has a built-in USB2 hub - on GXL this hub has two
>> > ports enabled, while on GXM there are three ports enabled (see below
>
> This hub is an actual USB hub? If so, then you should probably model the
> USB bus topology (which we have a binding definition for).
(the following explanation is based on a) what I found is going on in
the hardware registers b) reading the vendor drivers - unfortunately
there are no datasheets available which could give more details).
lsusb on my GXM gives:
...
 Hub Port Status:
   Port 1: 0000.0100 power
   Port 2: 0000.0100 power
   Port 3: 0000.0100 power

The layout looks like this:
dwc3 provides a USB hub with 2 (on GXL) or 3 (on GXM) USB ports.
Each of the port is driven by a PHY (port 1 = abp@0x78000, port2 =
abp@0x78020, etc...).

On GXM USB2 PHY port 3 = abp@0x78040 is connected to the third dwc3 hub port.
On GXL PHY port 3 = abp@0x78040 is connected to the dwc2 (I could not
prove this yet as I don't have access to any GXL hardware).

So the answer is: we don't have an actual USB hub here (as this hub is
provided by dwc3), but rather a set of PHYs which is assigned to
dwc3's hub (if we don't configure *all* PHYs then none of the USB
ports provided by the dwc3 hub works).

Could you please point me to the USB bus topology binding (is it the
one described in usb-device.txt)?

>> > for lsusb output). There are no USB3 ports enabled in the dwc3 hardware
>> > configuration, meaning that the SoC is limited to high-speed mode.
>> > On my GXM device the dwc3 hardware configuration forces it into "host
>> > only" mode.
>> >
>> > The SoCs contain two PHY blocks: one USB3 PHY and up to four USB2 PHYs
>> > (on GXM there are only three enabled, but the registers should support
>> > up to four).
>> > The USB3 PHY also handles the OTG interrupts, but since dwc3's hardware
>> > configuration enforces "host only" mode I was not able to test this. It
>> > simply takes care of an interrupt and then notifies all related PHYs
>> > about the new mode.
>> > The USB2 PHY block is a bit different: I created one PHY driver which
>> > spans all "PHY ports" because the handling is a bit tricky. It turns
>> > out that for each available USB port in dwc3's hub the corresponding
>> > PHY must be enabled (even if there is no physical port - in my case
>> > port 3 is not connected to anything, but disabling the PHY breaks
>> > ports 1 and 2 as well).
>> > I decided not not pass the USB2 PHYs directly to dwc3 due to three
>> > reasons: 1. the USB3 PHY (which holds a reference to all relevant
>> > USB2 PHY ports) controls the mode of the USB2 PHY ports (since both
>> > are used with the same controller and thus it makes sense to keep the
>> > mode consistent across all ports) 2. the dwc3 driver does not support
>> > passing multiple USB2 PHYs (only one USB2 and one USB3 PHY can be
>> > passed to it) 3. it is similar to how the vendor reference driver
>> > manages the PHYs. Please note that this coupling is not a fixed, this
>> > is all configurable via devicetree (so if the third USB2 PHY has to
>> > be passed two the dwc2 controller then this is still possible by
>> > just moving on PHY reference in the .dts).
>> after not staring at my own code for 24 hours I realized this:
>> (I went through quite a few iterations before getting these drivers to work)
>> I'm basically re-modelling an "USB PHY hub" with my USB3 PHY driver
>> (there's one "upstream" PHY interface which is passed to dwc3 and
>> multiple downstream PHYs, each for one port on dwc3's internal hub).
>> With this approach I could split each of the the USB2s into separate
>> nodes again (instead of one devicetree node with #phy-cells = <1>) as
>> the USB3 PHY is taking care of that special "we have to enable all
>> ports or no port will be usable".
>>
>> We could go even one step further: why implement this in the Meson GXL
>> specific PHY driver - why not implement a generic "phy-hub" driver
>> (which would be valid whenever the PHY controller has to manage
>> multiple PHYs at once, but wants to keep them all in a consistent
>> state).
>> The devicetree could look like this:
>>     usb2_phy_hub: phy@0 {
>>         compatible = "phy-hub";
>>         phys = <&other_phy1>, <&other_phy 2>;
>>     };
>>
>> &dwc3 {
>>      phys = <&usb2_phy_hub>, <&usb3_phy0>;
>>      phy-names = "usb2-phy", "usb3-phy";
>> };
>
> I'm okay with a hub if it is modeled as a USB hub. Here though, it
> looks like you are just trying to group things which doesn't need to be
> in DT.
I hope my answer above makes things more clear

>> The generic phy-hub driver would then implement all phy_ops callbacks
>> and pass then to each of it's downstream PHYs.
>
> You can have generic drivers without a generic binding.
So you'd rather implement a generic driver which would provide
functions like of_create_grouped_phy(struct device_node *np) which
would implement the grouping logic as described (but has no binding
itself, but rather relies on the actual platform driver taking care of
creating that binding and re-using generic code)?

>> That's just what came into my head - please let me know what you think
>> of this or share your ideas on how to approach this!
>>
>> > The coupling of the USB2 and USB3 PHYs is the reason why I sent the
>> > two drivers in one patch, even though they are handling different IP
>> > blocks (different registers, etc.).
>> >
>> > Unfortunately there are no datasheets available for any of these PHYs.
>> > Both drivers were written by reading the reference drivers provided by
>> > Amlogic and analyzing the registers on the kernel that was shipped with
>> > my board.
>> >
>> > As a last note: the dwc3 driver currently only explicitly enables the
>> > first USB port "DWC3_GUSB2PHYCFG(0)" in the internal hub. The hardware
>> > seems to enable the other two (DWC3_GUSB2PHYCFG(1) and
>> > DWC3_GUSB2PHYCFG(2)) automatically. I will ask the dwc3 maintainers if
>> > changes to dwc3 are desired any how these should look like, but for now
>> > it's working fine even without changes there.
>> >
>> > lsusb output on GXM for the dwc3 hub:
>> > Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
>> > ...
>> >  Hub Port Status:
>> >    Port 1: 0000.0100 power
>> >    Port 2: 0000.0100 power
>> >    Port 3: 0000.0100 power
>> >
>> > NOTE: The devicetree changes depend on my previous series:
>> > "[PATCH 0/2] minor GXL and GXM improvements" - see [0]
>> >
>> > NOTE2: This series depends on an upstream dwc3/xhci-plat DMA fix
>> > (special thanks to Arnd Bergmann and Sriram Dash for fixing that):
>> > "[PATCH v5 0/6] inherit dma configuration from parent dev" - see [1]
>> >
>> > I have a tree with all dependencies applied available at [2] if
>> > someone wants a quick way to test this (I don't take any responsibility
>> > if anything explodes though).
>> >
>> > [0] http://lists.infradead.org/pipermail/linux-amlogic/2016-November/001665.html
>> > [1] http://marc.info/?l=linux-usb&m=147938307209685&w=2
>> > [2] https://github.com/xdarklight/linux/commits/meson-gx-integration-4.10-20161126
>> >
>> > Martin Blumenstingl (5):
>> >   Documentation: dt-bindings: Add documentation for Meson GXL USB2/3
>> >     PHYs
>> >   phy: meson: add USB2 and USB3 PHY support for Meson GXL
>> >   arm64: dts: meson-gxl: add USB support
>> >   ARM64: dts: meson-gxm: add GXM specific USB configuration
>> >   ARM64: dts: meson-gx-p23x-q20x: enable USB on P23x and Q20x boards
>> >
>> >  .../devicetree/bindings/phy/meson-gxl-usb2-phy.txt |  25 ++
>> >  .../devicetree/bindings/phy/meson-gxl-usb3-phy.txt |  27 ++
>> >  .../arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi |  12 +
>> >  arch/arm64/boot/dts/amlogic/meson-gxl.dtsi         |  49 +++
>> >  .../arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts |  17 +
>> >  arch/arm64/boot/dts/amlogic/meson-gxm.dtsi         |  10 +
>> >  drivers/phy/Kconfig                                |  13 +
>> >  drivers/phy/Makefile                               |   2 +
>> >  drivers/phy/phy-meson-gxl-usb2.c                   | 374 ++++++++++++++++++++
>> >  drivers/phy/phy-meson-gxl-usb3.c                   | 377 +++++++++++++++++++++
>> >  10 files changed, 906 insertions(+)
>> >  create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
>> >  create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
>> >  create mode 100644 drivers/phy/phy-meson-gxl-usb2.c
>> >  create mode 100644 drivers/phy/phy-meson-gxl-usb3.c
>> >
>> > --
>> > 2.10.2
>> >
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 0/5] Meson GXL and GXM USB support
@ 2016-11-30 22:49           ` Martin Blumenstingl
  0 siblings, 0 replies; 39+ messages in thread
From: Martin Blumenstingl @ 2016-11-30 22:49 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Nov 30, 2016 at 11:22 PM, Rob Herring <robh@kernel.org> wrote:
> On Sun, Nov 27, 2016 at 11:42:02PM +0100, Martin Blumenstingl wrote:
>> Hello Kishon,
>>
>> On Sat, Nov 26, 2016 at 3:56 PM, Martin Blumenstingl
>> <martin.blumenstingl@googlemail.com> wrote:
>> > USB support on GXL and GXM differs a lot from Meson8b and GXBB:
>> > The most obvious change is that GXL and GXM now have one dwc3
>> > controller and one dwc2 controller (instead of two dwc2 controllers).
>> > With that there are also new USB PHYs.
>> >
>> > Due to lack of hardware I was only able to test this on a board with
>> > GXM, but as far as I understand the hardware my preparations should be
>> > correct (so it should also work on GXL).
>> >
>> > dwc2 will probably stay unused on most GXM devices since it's limited
>> > to device mode via some dwc2 hardware configuration register.
>> >
>> > dwc3 is probably used on all devices, even if there is more than just
>> > one USB port. dwc3 has a built-in USB2 hub - on GXL this hub has two
>> > ports enabled, while on GXM there are three ports enabled (see below
>
> This hub is an actual USB hub? If so, then you should probably model the
> USB bus topology (which we have a binding definition for).
(the following explanation is based on a) what I found is going on in
the hardware registers b) reading the vendor drivers - unfortunately
there are no datasheets available which could give more details).
lsusb on my GXM gives:
...
 Hub Port Status:
   Port 1: 0000.0100 power
   Port 2: 0000.0100 power
   Port 3: 0000.0100 power

The layout looks like this:
dwc3 provides a USB hub with 2 (on GXL) or 3 (on GXM) USB ports.
Each of the port is driven by a PHY (port 1 = abp at 0x78000, port2 =
abp at 0x78020, etc...).

On GXM USB2 PHY port 3 = abp at 0x78040 is connected to the third dwc3 hub port.
On GXL PHY port 3 = abp at 0x78040 is connected to the dwc2 (I could not
prove this yet as I don't have access to any GXL hardware).

So the answer is: we don't have an actual USB hub here (as this hub is
provided by dwc3), but rather a set of PHYs which is assigned to
dwc3's hub (if we don't configure *all* PHYs then none of the USB
ports provided by the dwc3 hub works).

Could you please point me to the USB bus topology binding (is it the
one described in usb-device.txt)?

>> > for lsusb output). There are no USB3 ports enabled in the dwc3 hardware
>> > configuration, meaning that the SoC is limited to high-speed mode.
>> > On my GXM device the dwc3 hardware configuration forces it into "host
>> > only" mode.
>> >
>> > The SoCs contain two PHY blocks: one USB3 PHY and up to four USB2 PHYs
>> > (on GXM there are only three enabled, but the registers should support
>> > up to four).
>> > The USB3 PHY also handles the OTG interrupts, but since dwc3's hardware
>> > configuration enforces "host only" mode I was not able to test this. It
>> > simply takes care of an interrupt and then notifies all related PHYs
>> > about the new mode.
>> > The USB2 PHY block is a bit different: I created one PHY driver which
>> > spans all "PHY ports" because the handling is a bit tricky. It turns
>> > out that for each available USB port in dwc3's hub the corresponding
>> > PHY must be enabled (even if there is no physical port - in my case
>> > port 3 is not connected to anything, but disabling the PHY breaks
>> > ports 1 and 2 as well).
>> > I decided not not pass the USB2 PHYs directly to dwc3 due to three
>> > reasons: 1. the USB3 PHY (which holds a reference to all relevant
>> > USB2 PHY ports) controls the mode of the USB2 PHY ports (since both
>> > are used with the same controller and thus it makes sense to keep the
>> > mode consistent across all ports) 2. the dwc3 driver does not support
>> > passing multiple USB2 PHYs (only one USB2 and one USB3 PHY can be
>> > passed to it) 3. it is similar to how the vendor reference driver
>> > manages the PHYs. Please note that this coupling is not a fixed, this
>> > is all configurable via devicetree (so if the third USB2 PHY has to
>> > be passed two the dwc2 controller then this is still possible by
>> > just moving on PHY reference in the .dts).
>> after not staring at my own code for 24 hours I realized this:
>> (I went through quite a few iterations before getting these drivers to work)
>> I'm basically re-modelling an "USB PHY hub" with my USB3 PHY driver
>> (there's one "upstream" PHY interface which is passed to dwc3 and
>> multiple downstream PHYs, each for one port on dwc3's internal hub).
>> With this approach I could split each of the the USB2s into separate
>> nodes again (instead of one devicetree node with #phy-cells = <1>) as
>> the USB3 PHY is taking care of that special "we have to enable all
>> ports or no port will be usable".
>>
>> We could go even one step further: why implement this in the Meson GXL
>> specific PHY driver - why not implement a generic "phy-hub" driver
>> (which would be valid whenever the PHY controller has to manage
>> multiple PHYs at once, but wants to keep them all in a consistent
>> state).
>> The devicetree could look like this:
>>     usb2_phy_hub: phy at 0 {
>>         compatible = "phy-hub";
>>         phys = <&other_phy1>, <&other_phy 2>;
>>     };
>>
>> &dwc3 {
>>      phys = <&usb2_phy_hub>, <&usb3_phy0>;
>>      phy-names = "usb2-phy", "usb3-phy";
>> };
>
> I'm okay with a hub if it is modeled as a USB hub. Here though, it
> looks like you are just trying to group things which doesn't need to be
> in DT.
I hope my answer above makes things more clear

>> The generic phy-hub driver would then implement all phy_ops callbacks
>> and pass then to each of it's downstream PHYs.
>
> You can have generic drivers without a generic binding.
So you'd rather implement a generic driver which would provide
functions like of_create_grouped_phy(struct device_node *np) which
would implement the grouping logic as described (but has no binding
itself, but rather relies on the actual platform driver taking care of
creating that binding and re-using generic code)?

>> That's just what came into my head - please let me know what you think
>> of this or share your ideas on how to approach this!
>>
>> > The coupling of the USB2 and USB3 PHYs is the reason why I sent the
>> > two drivers in one patch, even though they are handling different IP
>> > blocks (different registers, etc.).
>> >
>> > Unfortunately there are no datasheets available for any of these PHYs.
>> > Both drivers were written by reading the reference drivers provided by
>> > Amlogic and analyzing the registers on the kernel that was shipped with
>> > my board.
>> >
>> > As a last note: the dwc3 driver currently only explicitly enables the
>> > first USB port "DWC3_GUSB2PHYCFG(0)" in the internal hub. The hardware
>> > seems to enable the other two (DWC3_GUSB2PHYCFG(1) and
>> > DWC3_GUSB2PHYCFG(2)) automatically. I will ask the dwc3 maintainers if
>> > changes to dwc3 are desired any how these should look like, but for now
>> > it's working fine even without changes there.
>> >
>> > lsusb output on GXM for the dwc3 hub:
>> > Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
>> > ...
>> >  Hub Port Status:
>> >    Port 1: 0000.0100 power
>> >    Port 2: 0000.0100 power
>> >    Port 3: 0000.0100 power
>> >
>> > NOTE: The devicetree changes depend on my previous series:
>> > "[PATCH 0/2] minor GXL and GXM improvements" - see [0]
>> >
>> > NOTE2: This series depends on an upstream dwc3/xhci-plat DMA fix
>> > (special thanks to Arnd Bergmann and Sriram Dash for fixing that):
>> > "[PATCH v5 0/6] inherit dma configuration from parent dev" - see [1]
>> >
>> > I have a tree with all dependencies applied available at [2] if
>> > someone wants a quick way to test this (I don't take any responsibility
>> > if anything explodes though).
>> >
>> > [0] http://lists.infradead.org/pipermail/linux-amlogic/2016-November/001665.html
>> > [1] http://marc.info/?l=linux-usb&m=147938307209685&w=2
>> > [2] https://github.com/xdarklight/linux/commits/meson-gx-integration-4.10-20161126
>> >
>> > Martin Blumenstingl (5):
>> >   Documentation: dt-bindings: Add documentation for Meson GXL USB2/3
>> >     PHYs
>> >   phy: meson: add USB2 and USB3 PHY support for Meson GXL
>> >   arm64: dts: meson-gxl: add USB support
>> >   ARM64: dts: meson-gxm: add GXM specific USB configuration
>> >   ARM64: dts: meson-gx-p23x-q20x: enable USB on P23x and Q20x boards
>> >
>> >  .../devicetree/bindings/phy/meson-gxl-usb2-phy.txt |  25 ++
>> >  .../devicetree/bindings/phy/meson-gxl-usb3-phy.txt |  27 ++
>> >  .../arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi |  12 +
>> >  arch/arm64/boot/dts/amlogic/meson-gxl.dtsi         |  49 +++
>> >  .../arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts |  17 +
>> >  arch/arm64/boot/dts/amlogic/meson-gxm.dtsi         |  10 +
>> >  drivers/phy/Kconfig                                |  13 +
>> >  drivers/phy/Makefile                               |   2 +
>> >  drivers/phy/phy-meson-gxl-usb2.c                   | 374 ++++++++++++++++++++
>> >  drivers/phy/phy-meson-gxl-usb3.c                   | 377 +++++++++++++++++++++
>> >  10 files changed, 906 insertions(+)
>> >  create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
>> >  create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
>> >  create mode 100644 drivers/phy/phy-meson-gxl-usb2.c
>> >  create mode 100644 drivers/phy/phy-meson-gxl-usb3.c
>> >
>> > --
>> > 2.10.2
>> >

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

* [PATCH 0/5] Meson GXL and GXM USB support
@ 2016-11-30 22:49           ` Martin Blumenstingl
  0 siblings, 0 replies; 39+ messages in thread
From: Martin Blumenstingl @ 2016-11-30 22:49 UTC (permalink / raw)
  To: linus-amlogic

On Wed, Nov 30, 2016 at 11:22 PM, Rob Herring <robh@kernel.org> wrote:
> On Sun, Nov 27, 2016 at 11:42:02PM +0100, Martin Blumenstingl wrote:
>> Hello Kishon,
>>
>> On Sat, Nov 26, 2016 at 3:56 PM, Martin Blumenstingl
>> <martin.blumenstingl@googlemail.com> wrote:
>> > USB support on GXL and GXM differs a lot from Meson8b and GXBB:
>> > The most obvious change is that GXL and GXM now have one dwc3
>> > controller and one dwc2 controller (instead of two dwc2 controllers).
>> > With that there are also new USB PHYs.
>> >
>> > Due to lack of hardware I was only able to test this on a board with
>> > GXM, but as far as I understand the hardware my preparations should be
>> > correct (so it should also work on GXL).
>> >
>> > dwc2 will probably stay unused on most GXM devices since it's limited
>> > to device mode via some dwc2 hardware configuration register.
>> >
>> > dwc3 is probably used on all devices, even if there is more than just
>> > one USB port. dwc3 has a built-in USB2 hub - on GXL this hub has two
>> > ports enabled, while on GXM there are three ports enabled (see below
>
> This hub is an actual USB hub? If so, then you should probably model the
> USB bus topology (which we have a binding definition for).
(the following explanation is based on a) what I found is going on in
the hardware registers b) reading the vendor drivers - unfortunately
there are no datasheets available which could give more details).
lsusb on my GXM gives:
...
 Hub Port Status:
   Port 1: 0000.0100 power
   Port 2: 0000.0100 power
   Port 3: 0000.0100 power

The layout looks like this:
dwc3 provides a USB hub with 2 (on GXL) or 3 (on GXM) USB ports.
Each of the port is driven by a PHY (port 1 = abp at 0x78000, port2 =
abp at 0x78020, etc...).

On GXM USB2 PHY port 3 = abp at 0x78040 is connected to the third dwc3 hub port.
On GXL PHY port 3 = abp at 0x78040 is connected to the dwc2 (I could not
prove this yet as I don't have access to any GXL hardware).

So the answer is: we don't have an actual USB hub here (as this hub is
provided by dwc3), but rather a set of PHYs which is assigned to
dwc3's hub (if we don't configure *all* PHYs then none of the USB
ports provided by the dwc3 hub works).

Could you please point me to the USB bus topology binding (is it the
one described in usb-device.txt)?

>> > for lsusb output). There are no USB3 ports enabled in the dwc3 hardware
>> > configuration, meaning that the SoC is limited to high-speed mode.
>> > On my GXM device the dwc3 hardware configuration forces it into "host
>> > only" mode.
>> >
>> > The SoCs contain two PHY blocks: one USB3 PHY and up to four USB2 PHYs
>> > (on GXM there are only three enabled, but the registers should support
>> > up to four).
>> > The USB3 PHY also handles the OTG interrupts, but since dwc3's hardware
>> > configuration enforces "host only" mode I was not able to test this. It
>> > simply takes care of an interrupt and then notifies all related PHYs
>> > about the new mode.
>> > The USB2 PHY block is a bit different: I created one PHY driver which
>> > spans all "PHY ports" because the handling is a bit tricky. It turns
>> > out that for each available USB port in dwc3's hub the corresponding
>> > PHY must be enabled (even if there is no physical port - in my case
>> > port 3 is not connected to anything, but disabling the PHY breaks
>> > ports 1 and 2 as well).
>> > I decided not not pass the USB2 PHYs directly to dwc3 due to three
>> > reasons: 1. the USB3 PHY (which holds a reference to all relevant
>> > USB2 PHY ports) controls the mode of the USB2 PHY ports (since both
>> > are used with the same controller and thus it makes sense to keep the
>> > mode consistent across all ports) 2. the dwc3 driver does not support
>> > passing multiple USB2 PHYs (only one USB2 and one USB3 PHY can be
>> > passed to it) 3. it is similar to how the vendor reference driver
>> > manages the PHYs. Please note that this coupling is not a fixed, this
>> > is all configurable via devicetree (so if the third USB2 PHY has to
>> > be passed two the dwc2 controller then this is still possible by
>> > just moving on PHY reference in the .dts).
>> after not staring at my own code for 24 hours I realized this:
>> (I went through quite a few iterations before getting these drivers to work)
>> I'm basically re-modelling an "USB PHY hub" with my USB3 PHY driver
>> (there's one "upstream" PHY interface which is passed to dwc3 and
>> multiple downstream PHYs, each for one port on dwc3's internal hub).
>> With this approach I could split each of the the USB2s into separate
>> nodes again (instead of one devicetree node with #phy-cells = <1>) as
>> the USB3 PHY is taking care of that special "we have to enable all
>> ports or no port will be usable".
>>
>> We could go even one step further: why implement this in the Meson GXL
>> specific PHY driver - why not implement a generic "phy-hub" driver
>> (which would be valid whenever the PHY controller has to manage
>> multiple PHYs at once, but wants to keep them all in a consistent
>> state).
>> The devicetree could look like this:
>>     usb2_phy_hub: phy at 0 {
>>         compatible = "phy-hub";
>>         phys = <&other_phy1>, <&other_phy 2>;
>>     };
>>
>> &dwc3 {
>>      phys = <&usb2_phy_hub>, <&usb3_phy0>;
>>      phy-names = "usb2-phy", "usb3-phy";
>> };
>
> I'm okay with a hub if it is modeled as a USB hub. Here though, it
> looks like you are just trying to group things which doesn't need to be
> in DT.
I hope my answer above makes things more clear

>> The generic phy-hub driver would then implement all phy_ops callbacks
>> and pass then to each of it's downstream PHYs.
>
> You can have generic drivers without a generic binding.
So you'd rather implement a generic driver which would provide
functions like of_create_grouped_phy(struct device_node *np) which
would implement the grouping logic as described (but has no binding
itself, but rather relies on the actual platform driver taking care of
creating that binding and re-using generic code)?

>> That's just what came into my head - please let me know what you think
>> of this or share your ideas on how to approach this!
>>
>> > The coupling of the USB2 and USB3 PHYs is the reason why I sent the
>> > two drivers in one patch, even though they are handling different IP
>> > blocks (different registers, etc.).
>> >
>> > Unfortunately there are no datasheets available for any of these PHYs.
>> > Both drivers were written by reading the reference drivers provided by
>> > Amlogic and analyzing the registers on the kernel that was shipped with
>> > my board.
>> >
>> > As a last note: the dwc3 driver currently only explicitly enables the
>> > first USB port "DWC3_GUSB2PHYCFG(0)" in the internal hub. The hardware
>> > seems to enable the other two (DWC3_GUSB2PHYCFG(1) and
>> > DWC3_GUSB2PHYCFG(2)) automatically. I will ask the dwc3 maintainers if
>> > changes to dwc3 are desired any how these should look like, but for now
>> > it's working fine even without changes there.
>> >
>> > lsusb output on GXM for the dwc3 hub:
>> > Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
>> > ...
>> >  Hub Port Status:
>> >    Port 1: 0000.0100 power
>> >    Port 2: 0000.0100 power
>> >    Port 3: 0000.0100 power
>> >
>> > NOTE: The devicetree changes depend on my previous series:
>> > "[PATCH 0/2] minor GXL and GXM improvements" - see [0]
>> >
>> > NOTE2: This series depends on an upstream dwc3/xhci-plat DMA fix
>> > (special thanks to Arnd Bergmann and Sriram Dash for fixing that):
>> > "[PATCH v5 0/6] inherit dma configuration from parent dev" - see [1]
>> >
>> > I have a tree with all dependencies applied available at [2] if
>> > someone wants a quick way to test this (I don't take any responsibility
>> > if anything explodes though).
>> >
>> > [0] http://lists.infradead.org/pipermail/linux-amlogic/2016-November/001665.html
>> > [1] http://marc.info/?l=linux-usb&m=147938307209685&w=2
>> > [2] https://github.com/xdarklight/linux/commits/meson-gx-integration-4.10-20161126
>> >
>> > Martin Blumenstingl (5):
>> >   Documentation: dt-bindings: Add documentation for Meson GXL USB2/3
>> >     PHYs
>> >   phy: meson: add USB2 and USB3 PHY support for Meson GXL
>> >   arm64: dts: meson-gxl: add USB support
>> >   ARM64: dts: meson-gxm: add GXM specific USB configuration
>> >   ARM64: dts: meson-gx-p23x-q20x: enable USB on P23x and Q20x boards
>> >
>> >  .../devicetree/bindings/phy/meson-gxl-usb2-phy.txt |  25 ++
>> >  .../devicetree/bindings/phy/meson-gxl-usb3-phy.txt |  27 ++
>> >  .../arm64/boot/dts/amlogic/meson-gx-p23x-q20x.dtsi |  12 +
>> >  arch/arm64/boot/dts/amlogic/meson-gxl.dtsi         |  49 +++
>> >  .../arm64/boot/dts/amlogic/meson-gxm-s912-q200.dts |  17 +
>> >  arch/arm64/boot/dts/amlogic/meson-gxm.dtsi         |  10 +
>> >  drivers/phy/Kconfig                                |  13 +
>> >  drivers/phy/Makefile                               |   2 +
>> >  drivers/phy/phy-meson-gxl-usb2.c                   | 374 ++++++++++++++++++++
>> >  drivers/phy/phy-meson-gxl-usb3.c                   | 377 +++++++++++++++++++++
>> >  10 files changed, 906 insertions(+)
>> >  create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
>> >  create mode 100644 Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
>> >  create mode 100644 drivers/phy/phy-meson-gxl-usb2.c
>> >  create mode 100644 drivers/phy/phy-meson-gxl-usb3.c
>> >
>> > --
>> > 2.10.2
>> >

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

* Re: [PATCH 0/5] Meson GXL and GXM USB support
  2016-11-30 22:49           ` Martin Blumenstingl
  (?)
@ 2016-12-01 15:54               ` Rob Herring
  -1 siblings, 0 replies; 39+ messages in thread
From: Rob Herring @ 2016-12-01 15:54 UTC (permalink / raw)
  To: Martin Blumenstingl
  Cc: linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, kishon-l0cyMroinI0,
	khilman-rdvid1DuHRBWk0Htik3J/w, carlo-KA+7E9HrN00dnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, catalin.marinas-5wv7dgnIgG8,
	will.deacon-5wv7dgnIgG8, narmstrong-rdvid1DuHRBWk0Htik3J/w

On Wed, Nov 30, 2016 at 11:49:03PM +0100, Martin Blumenstingl wrote:
> On Wed, Nov 30, 2016 at 11:22 PM, Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org> wrote:
> > On Sun, Nov 27, 2016 at 11:42:02PM +0100, Martin Blumenstingl wrote:
> >> Hello Kishon,
> >>
> >> On Sat, Nov 26, 2016 at 3:56 PM, Martin Blumenstingl
> >> <martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org> wrote:
> >> > USB support on GXL and GXM differs a lot from Meson8b and GXBB:
> >> > The most obvious change is that GXL and GXM now have one dwc3
> >> > controller and one dwc2 controller (instead of two dwc2 controllers).
> >> > With that there are also new USB PHYs.
> >> >
> >> > Due to lack of hardware I was only able to test this on a board with
> >> > GXM, but as far as I understand the hardware my preparations should be
> >> > correct (so it should also work on GXL).
> >> >
> >> > dwc2 will probably stay unused on most GXM devices since it's limited
> >> > to device mode via some dwc2 hardware configuration register.
> >> >
> >> > dwc3 is probably used on all devices, even if there is more than just
> >> > one USB port. dwc3 has a built-in USB2 hub - on GXL this hub has two
> >> > ports enabled, while on GXM there are three ports enabled (see below
> >
> > This hub is an actual USB hub? If so, then you should probably model the
> > USB bus topology (which we have a binding definition for).
> (the following explanation is based on a) what I found is going on in
> the hardware registers b) reading the vendor drivers - unfortunately
> there are no datasheets available which could give more details).
> lsusb on my GXM gives:
> ...
>  Hub Port Status:
>    Port 1: 0000.0100 power
>    Port 2: 0000.0100 power
>    Port 3: 0000.0100 power
> 
> The layout looks like this:
> dwc3 provides a USB hub with 2 (on GXL) or 3 (on GXM) USB ports.
> Each of the port is driven by a PHY (port 1 = abp@0x78000, port2 =
> abp@0x78020, etc...).
> 
> On GXM USB2 PHY port 3 = abp@0x78040 is connected to the third dwc3 hub port.
> On GXL PHY port 3 = abp@0x78040 is connected to the dwc2 (I could not
> prove this yet as I don't have access to any GXL hardware).
> 
> So the answer is: we don't have an actual USB hub here (as this hub is
> provided by dwc3), but rather a set of PHYs which is assigned to
> dwc3's hub (if we don't configure *all* PHYs then none of the USB
> ports provided by the dwc3 hub works).
> 
> Could you please point me to the USB bus topology binding (is it the
> one described in usb-device.txt)?

Yes.

> 
> >> > for lsusb output). There are no USB3 ports enabled in the dwc3 hardware
> >> > configuration, meaning that the SoC is limited to high-speed mode.
> >> > On my GXM device the dwc3 hardware configuration forces it into "host
> >> > only" mode.
> >> >
> >> > The SoCs contain two PHY blocks: one USB3 PHY and up to four USB2 PHYs
> >> > (on GXM there are only three enabled, but the registers should support
> >> > up to four).
> >> > The USB3 PHY also handles the OTG interrupts, but since dwc3's hardware
> >> > configuration enforces "host only" mode I was not able to test this. It
> >> > simply takes care of an interrupt and then notifies all related PHYs
> >> > about the new mode.
> >> > The USB2 PHY block is a bit different: I created one PHY driver which
> >> > spans all "PHY ports" because the handling is a bit tricky. It turns
> >> > out that for each available USB port in dwc3's hub the corresponding
> >> > PHY must be enabled (even if there is no physical port - in my case
> >> > port 3 is not connected to anything, but disabling the PHY breaks
> >> > ports 1 and 2 as well).
> >> > I decided not not pass the USB2 PHYs directly to dwc3 due to three
> >> > reasons: 1. the USB3 PHY (which holds a reference to all relevant
> >> > USB2 PHY ports) controls the mode of the USB2 PHY ports (since both
> >> > are used with the same controller and thus it makes sense to keep the
> >> > mode consistent across all ports) 2. the dwc3 driver does not support
> >> > passing multiple USB2 PHYs (only one USB2 and one USB3 PHY can be
> >> > passed to it) 3. it is similar to how the vendor reference driver
> >> > manages the PHYs. Please note that this coupling is not a fixed, this
> >> > is all configurable via devicetree (so if the third USB2 PHY has to
> >> > be passed two the dwc2 controller then this is still possible by
> >> > just moving on PHY reference in the .dts).
> >> after not staring at my own code for 24 hours I realized this:
> >> (I went through quite a few iterations before getting these drivers to work)
> >> I'm basically re-modelling an "USB PHY hub" with my USB3 PHY driver
> >> (there's one "upstream" PHY interface which is passed to dwc3 and
> >> multiple downstream PHYs, each for one port on dwc3's internal hub).
> >> With this approach I could split each of the the USB2s into separate
> >> nodes again (instead of one devicetree node with #phy-cells = <1>) as
> >> the USB3 PHY is taking care of that special "we have to enable all
> >> ports or no port will be usable".
> >>
> >> We could go even one step further: why implement this in the Meson GXL
> >> specific PHY driver - why not implement a generic "phy-hub" driver
> >> (which would be valid whenever the PHY controller has to manage
> >> multiple PHYs at once, but wants to keep them all in a consistent
> >> state).
> >> The devicetree could look like this:
> >>     usb2_phy_hub: phy@0 {
> >>         compatible = "phy-hub";
> >>         phys = <&other_phy1>, <&other_phy 2>;
> >>     };
> >>
> >> &dwc3 {
> >>      phys = <&usb2_phy_hub>, <&usb3_phy0>;
> >>      phy-names = "usb2-phy", "usb3-phy";
> >> };
> >
> > I'm okay with a hub if it is modeled as a USB hub. Here though, it
> > looks like you are just trying to group things which doesn't need to be
> > in DT.
> I hope my answer above makes things more clear

Yes, I thought there was some heirarchy here, but it seems not.

So you should just list the 3 phys at the controller. The controller 
has 3 ports and you have a phy for each port. The fact that they all 
need to be on/initialized is a quirk and shouldn't mean that you create 
some heirarchy in DT.

> >> The generic phy-hub driver would then implement all phy_ops callbacks
> >> and pass then to each of it's downstream PHYs.
> >
> > You can have generic drivers without a generic binding.
> So you'd rather implement a generic driver which would provide
> functions like of_create_grouped_phy(struct device_node *np) which
> would implement the grouping logic as described (but has no binding
> itself, but rather relies on the actual platform driver taking care of
> creating that binding and re-using generic code)?

Right.

Rob
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 0/5] Meson GXL and GXM USB support
@ 2016-12-01 15:54               ` Rob Herring
  0 siblings, 0 replies; 39+ messages in thread
From: Rob Herring @ 2016-12-01 15:54 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Nov 30, 2016 at 11:49:03PM +0100, Martin Blumenstingl wrote:
> On Wed, Nov 30, 2016 at 11:22 PM, Rob Herring <robh@kernel.org> wrote:
> > On Sun, Nov 27, 2016 at 11:42:02PM +0100, Martin Blumenstingl wrote:
> >> Hello Kishon,
> >>
> >> On Sat, Nov 26, 2016 at 3:56 PM, Martin Blumenstingl
> >> <martin.blumenstingl@googlemail.com> wrote:
> >> > USB support on GXL and GXM differs a lot from Meson8b and GXBB:
> >> > The most obvious change is that GXL and GXM now have one dwc3
> >> > controller and one dwc2 controller (instead of two dwc2 controllers).
> >> > With that there are also new USB PHYs.
> >> >
> >> > Due to lack of hardware I was only able to test this on a board with
> >> > GXM, but as far as I understand the hardware my preparations should be
> >> > correct (so it should also work on GXL).
> >> >
> >> > dwc2 will probably stay unused on most GXM devices since it's limited
> >> > to device mode via some dwc2 hardware configuration register.
> >> >
> >> > dwc3 is probably used on all devices, even if there is more than just
> >> > one USB port. dwc3 has a built-in USB2 hub - on GXL this hub has two
> >> > ports enabled, while on GXM there are three ports enabled (see below
> >
> > This hub is an actual USB hub? If so, then you should probably model the
> > USB bus topology (which we have a binding definition for).
> (the following explanation is based on a) what I found is going on in
> the hardware registers b) reading the vendor drivers - unfortunately
> there are no datasheets available which could give more details).
> lsusb on my GXM gives:
> ...
>  Hub Port Status:
>    Port 1: 0000.0100 power
>    Port 2: 0000.0100 power
>    Port 3: 0000.0100 power
> 
> The layout looks like this:
> dwc3 provides a USB hub with 2 (on GXL) or 3 (on GXM) USB ports.
> Each of the port is driven by a PHY (port 1 = abp at 0x78000, port2 =
> abp at 0x78020, etc...).
> 
> On GXM USB2 PHY port 3 = abp at 0x78040 is connected to the third dwc3 hub port.
> On GXL PHY port 3 = abp at 0x78040 is connected to the dwc2 (I could not
> prove this yet as I don't have access to any GXL hardware).
> 
> So the answer is: we don't have an actual USB hub here (as this hub is
> provided by dwc3), but rather a set of PHYs which is assigned to
> dwc3's hub (if we don't configure *all* PHYs then none of the USB
> ports provided by the dwc3 hub works).
> 
> Could you please point me to the USB bus topology binding (is it the
> one described in usb-device.txt)?

Yes.

> 
> >> > for lsusb output). There are no USB3 ports enabled in the dwc3 hardware
> >> > configuration, meaning that the SoC is limited to high-speed mode.
> >> > On my GXM device the dwc3 hardware configuration forces it into "host
> >> > only" mode.
> >> >
> >> > The SoCs contain two PHY blocks: one USB3 PHY and up to four USB2 PHYs
> >> > (on GXM there are only three enabled, but the registers should support
> >> > up to four).
> >> > The USB3 PHY also handles the OTG interrupts, but since dwc3's hardware
> >> > configuration enforces "host only" mode I was not able to test this. It
> >> > simply takes care of an interrupt and then notifies all related PHYs
> >> > about the new mode.
> >> > The USB2 PHY block is a bit different: I created one PHY driver which
> >> > spans all "PHY ports" because the handling is a bit tricky. It turns
> >> > out that for each available USB port in dwc3's hub the corresponding
> >> > PHY must be enabled (even if there is no physical port - in my case
> >> > port 3 is not connected to anything, but disabling the PHY breaks
> >> > ports 1 and 2 as well).
> >> > I decided not not pass the USB2 PHYs directly to dwc3 due to three
> >> > reasons: 1. the USB3 PHY (which holds a reference to all relevant
> >> > USB2 PHY ports) controls the mode of the USB2 PHY ports (since both
> >> > are used with the same controller and thus it makes sense to keep the
> >> > mode consistent across all ports) 2. the dwc3 driver does not support
> >> > passing multiple USB2 PHYs (only one USB2 and one USB3 PHY can be
> >> > passed to it) 3. it is similar to how the vendor reference driver
> >> > manages the PHYs. Please note that this coupling is not a fixed, this
> >> > is all configurable via devicetree (so if the third USB2 PHY has to
> >> > be passed two the dwc2 controller then this is still possible by
> >> > just moving on PHY reference in the .dts).
> >> after not staring at my own code for 24 hours I realized this:
> >> (I went through quite a few iterations before getting these drivers to work)
> >> I'm basically re-modelling an "USB PHY hub" with my USB3 PHY driver
> >> (there's one "upstream" PHY interface which is passed to dwc3 and
> >> multiple downstream PHYs, each for one port on dwc3's internal hub).
> >> With this approach I could split each of the the USB2s into separate
> >> nodes again (instead of one devicetree node with #phy-cells = <1>) as
> >> the USB3 PHY is taking care of that special "we have to enable all
> >> ports or no port will be usable".
> >>
> >> We could go even one step further: why implement this in the Meson GXL
> >> specific PHY driver - why not implement a generic "phy-hub" driver
> >> (which would be valid whenever the PHY controller has to manage
> >> multiple PHYs at once, but wants to keep them all in a consistent
> >> state).
> >> The devicetree could look like this:
> >>     usb2_phy_hub: phy at 0 {
> >>         compatible = "phy-hub";
> >>         phys = <&other_phy1>, <&other_phy 2>;
> >>     };
> >>
> >> &dwc3 {
> >>      phys = <&usb2_phy_hub>, <&usb3_phy0>;
> >>      phy-names = "usb2-phy", "usb3-phy";
> >> };
> >
> > I'm okay with a hub if it is modeled as a USB hub. Here though, it
> > looks like you are just trying to group things which doesn't need to be
> > in DT.
> I hope my answer above makes things more clear

Yes, I thought there was some heirarchy here, but it seems not.

So you should just list the 3 phys at the controller. The controller 
has 3 ports and you have a phy for each port. The fact that they all 
need to be on/initialized is a quirk and shouldn't mean that you create 
some heirarchy in DT.

> >> The generic phy-hub driver would then implement all phy_ops callbacks
> >> and pass then to each of it's downstream PHYs.
> >
> > You can have generic drivers without a generic binding.
> So you'd rather implement a generic driver which would provide
> functions like of_create_grouped_phy(struct device_node *np) which
> would implement the grouping logic as described (but has no binding
> itself, but rather relies on the actual platform driver taking care of
> creating that binding and re-using generic code)?

Right.

Rob

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

* [PATCH 0/5] Meson GXL and GXM USB support
@ 2016-12-01 15:54               ` Rob Herring
  0 siblings, 0 replies; 39+ messages in thread
From: Rob Herring @ 2016-12-01 15:54 UTC (permalink / raw)
  To: linus-amlogic

On Wed, Nov 30, 2016 at 11:49:03PM +0100, Martin Blumenstingl wrote:
> On Wed, Nov 30, 2016 at 11:22 PM, Rob Herring <robh@kernel.org> wrote:
> > On Sun, Nov 27, 2016 at 11:42:02PM +0100, Martin Blumenstingl wrote:
> >> Hello Kishon,
> >>
> >> On Sat, Nov 26, 2016 at 3:56 PM, Martin Blumenstingl
> >> <martin.blumenstingl@googlemail.com> wrote:
> >> > USB support on GXL and GXM differs a lot from Meson8b and GXBB:
> >> > The most obvious change is that GXL and GXM now have one dwc3
> >> > controller and one dwc2 controller (instead of two dwc2 controllers).
> >> > With that there are also new USB PHYs.
> >> >
> >> > Due to lack of hardware I was only able to test this on a board with
> >> > GXM, but as far as I understand the hardware my preparations should be
> >> > correct (so it should also work on GXL).
> >> >
> >> > dwc2 will probably stay unused on most GXM devices since it's limited
> >> > to device mode via some dwc2 hardware configuration register.
> >> >
> >> > dwc3 is probably used on all devices, even if there is more than just
> >> > one USB port. dwc3 has a built-in USB2 hub - on GXL this hub has two
> >> > ports enabled, while on GXM there are three ports enabled (see below
> >
> > This hub is an actual USB hub? If so, then you should probably model the
> > USB bus topology (which we have a binding definition for).
> (the following explanation is based on a) what I found is going on in
> the hardware registers b) reading the vendor drivers - unfortunately
> there are no datasheets available which could give more details).
> lsusb on my GXM gives:
> ...
>  Hub Port Status:
>    Port 1: 0000.0100 power
>    Port 2: 0000.0100 power
>    Port 3: 0000.0100 power
> 
> The layout looks like this:
> dwc3 provides a USB hub with 2 (on GXL) or 3 (on GXM) USB ports.
> Each of the port is driven by a PHY (port 1 = abp at 0x78000, port2 =
> abp at 0x78020, etc...).
> 
> On GXM USB2 PHY port 3 = abp at 0x78040 is connected to the third dwc3 hub port.
> On GXL PHY port 3 = abp at 0x78040 is connected to the dwc2 (I could not
> prove this yet as I don't have access to any GXL hardware).
> 
> So the answer is: we don't have an actual USB hub here (as this hub is
> provided by dwc3), but rather a set of PHYs which is assigned to
> dwc3's hub (if we don't configure *all* PHYs then none of the USB
> ports provided by the dwc3 hub works).
> 
> Could you please point me to the USB bus topology binding (is it the
> one described in usb-device.txt)?

Yes.

> 
> >> > for lsusb output). There are no USB3 ports enabled in the dwc3 hardware
> >> > configuration, meaning that the SoC is limited to high-speed mode.
> >> > On my GXM device the dwc3 hardware configuration forces it into "host
> >> > only" mode.
> >> >
> >> > The SoCs contain two PHY blocks: one USB3 PHY and up to four USB2 PHYs
> >> > (on GXM there are only three enabled, but the registers should support
> >> > up to four).
> >> > The USB3 PHY also handles the OTG interrupts, but since dwc3's hardware
> >> > configuration enforces "host only" mode I was not able to test this. It
> >> > simply takes care of an interrupt and then notifies all related PHYs
> >> > about the new mode.
> >> > The USB2 PHY block is a bit different: I created one PHY driver which
> >> > spans all "PHY ports" because the handling is a bit tricky. It turns
> >> > out that for each available USB port in dwc3's hub the corresponding
> >> > PHY must be enabled (even if there is no physical port - in my case
> >> > port 3 is not connected to anything, but disabling the PHY breaks
> >> > ports 1 and 2 as well).
> >> > I decided not not pass the USB2 PHYs directly to dwc3 due to three
> >> > reasons: 1. the USB3 PHY (which holds a reference to all relevant
> >> > USB2 PHY ports) controls the mode of the USB2 PHY ports (since both
> >> > are used with the same controller and thus it makes sense to keep the
> >> > mode consistent across all ports) 2. the dwc3 driver does not support
> >> > passing multiple USB2 PHYs (only one USB2 and one USB3 PHY can be
> >> > passed to it) 3. it is similar to how the vendor reference driver
> >> > manages the PHYs. Please note that this coupling is not a fixed, this
> >> > is all configurable via devicetree (so if the third USB2 PHY has to
> >> > be passed two the dwc2 controller then this is still possible by
> >> > just moving on PHY reference in the .dts).
> >> after not staring at my own code for 24 hours I realized this:
> >> (I went through quite a few iterations before getting these drivers to work)
> >> I'm basically re-modelling an "USB PHY hub" with my USB3 PHY driver
> >> (there's one "upstream" PHY interface which is passed to dwc3 and
> >> multiple downstream PHYs, each for one port on dwc3's internal hub).
> >> With this approach I could split each of the the USB2s into separate
> >> nodes again (instead of one devicetree node with #phy-cells = <1>) as
> >> the USB3 PHY is taking care of that special "we have to enable all
> >> ports or no port will be usable".
> >>
> >> We could go even one step further: why implement this in the Meson GXL
> >> specific PHY driver - why not implement a generic "phy-hub" driver
> >> (which would be valid whenever the PHY controller has to manage
> >> multiple PHYs at once, but wants to keep them all in a consistent
> >> state).
> >> The devicetree could look like this:
> >>     usb2_phy_hub: phy at 0 {
> >>         compatible = "phy-hub";
> >>         phys = <&other_phy1>, <&other_phy 2>;
> >>     };
> >>
> >> &dwc3 {
> >>      phys = <&usb2_phy_hub>, <&usb3_phy0>;
> >>      phy-names = "usb2-phy", "usb3-phy";
> >> };
> >
> > I'm okay with a hub if it is modeled as a USB hub. Here though, it
> > looks like you are just trying to group things which doesn't need to be
> > in DT.
> I hope my answer above makes things more clear

Yes, I thought there was some heirarchy here, but it seems not.

So you should just list the 3 phys at the controller. The controller 
has 3 ports and you have a phy for each port. The fact that they all 
need to be on/initialized is a quirk and shouldn't mean that you create 
some heirarchy in DT.

> >> The generic phy-hub driver would then implement all phy_ops callbacks
> >> and pass then to each of it's downstream PHYs.
> >
> > You can have generic drivers without a generic binding.
> So you'd rather implement a generic driver which would provide
> functions like of_create_grouped_phy(struct device_node *np) which
> would implement the grouping logic as described (but has no binding
> itself, but rather relies on the actual platform driver taking care of
> creating that binding and re-using generic code)?

Right.

Rob

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

* Re: [PATCH 2/5] phy: meson: add USB2 and USB3 PHY support for Meson GXL
  2016-11-26 14:56     ` Martin Blumenstingl
  (?)
@ 2017-01-16  9:41       ` Kishon Vijay Abraham I
  -1 siblings, 0 replies; 39+ messages in thread
From: Kishon Vijay Abraham I @ 2017-01-16  9:41 UTC (permalink / raw)
  To: Martin Blumenstingl, linux-amlogic, linux-arm-kernel, devicetree,
	khilman, carlo, mark.rutland, robh+dt
  Cc: catalin.marinas, will.deacon, narmstrong

Hi,

On Saturday 26 November 2016 08:26 PM, Martin Blumenstingl wrote:
> This adds two new USB PHY drivers found on Meson GXL and GXM SoCs.

Please send them as separate drivers.
> 
> The registers for the USB2 PHY block handle a maximum of 4 ports (newer
> SoCs may allow more ports, the driver handles this as long as the
> register length is adjusted in the .dts). The PHY block theoretically
> allows powering down each PHY port separately (by putting it into
> "reset" state). Unfortunately this does not work (my board has 2 USB
> ports, connected to port 1 and 2 of the dwc3's internal hub. When
> leaving the third USB PHY disabled then the hub sees that a device is
> plugged in, but it does not work: "usb usb1-port2: connect-debounce
> failed").
> The USB3 PHY will take care of enabling/disabling all available ports,
> because the USB3 PHY also manages the mode of the USB2 PHYs.
> 
> The USB3 PHY actually has three purposes:
> - it provides the USB3 PHY
> - it handles the OTG device/host mode detection interrupt
> - it notifies the corresponding USB2 PHYs of the OTG mode changes
> On GXL and GXM SoCs one references all available USB2 PHY ports in the
> USB3 PHY because all are connected to the same USB controller (thus the
> mode will always match). This behavior is configurable via devicetree,
> by passing (or not passing) a list of other ("child") PHYs which should
> be configured by the USB3 PHY.
> 
> Unfortunately there are no datasheets available for any of these PHYs.
> Both drivers were written by reading the reference drivers provided by
> Amlogic and analyzing the registers on the kernel that was shipped with
> my board.
> 
> Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
> ---
>  drivers/phy/Kconfig              |  13 ++
>  drivers/phy/Makefile             |   2 +
>  drivers/phy/phy-meson-gxl-usb2.c | 374 ++++++++++++++++++++++++++++++++++++++
>  drivers/phy/phy-meson-gxl-usb3.c | 377 +++++++++++++++++++++++++++++++++++++++
>  4 files changed, 766 insertions(+)
>  create mode 100644 drivers/phy/phy-meson-gxl-usb2.c
>  create mode 100644 drivers/phy/phy-meson-gxl-usb3.c
> 
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index 728e03f..ea74843 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -502,4 +502,17 @@ config PHY_MESON8B_USB2
>  	  and GXBB SoCs.
>  	  If unsure, say N.
>  
> +config PHY_MESON_GXL_USB
> +	tristate "Meson GXL USB2 and USB3 PHY drivers"
> +	default ARCH_MESON
> +	depends on OF && (ARCH_MESON || COMPILE_TEST)
> +	depends on USB_SUPPORT
> +	select USB_COMMON
> +	select GENERIC_PHY
> +	select REGMAP_MMIO
> +	help
> +	  Enable this to support the Meson USB2 and USB3 PHYs found in
> +	  Meson GXL SoCs.
> +	  If unsure, say N.
> +
>  endmenu
> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> index 0c7fdae..960a96e 100644
> --- a/drivers/phy/Makefile
> +++ b/drivers/phy/Makefile
> @@ -61,3 +61,5 @@ obj-$(CONFIG_PHY_CYGNUS_PCIE)		+= phy-bcm-cygnus-pcie.o
>  obj-$(CONFIG_ARCH_TEGRA) += tegra/
>  obj-$(CONFIG_PHY_NS2_PCIE)		+= phy-bcm-ns2-pcie.o
>  obj-$(CONFIG_PHY_MESON8B_USB2)		+= phy-meson8b-usb2.o
> +obj-$(CONFIG_PHY_MESON_GXL_USB)		+= phy-meson-gxl-usb2.o
> +obj-$(CONFIG_PHY_MESON_GXL_USB)		+= phy-meson-gxl-usb3.o
> diff --git a/drivers/phy/phy-meson-gxl-usb2.c b/drivers/phy/phy-meson-gxl-usb2.c
> new file mode 100644
> index 0000000..c081ce3
> --- /dev/null
> +++ b/drivers/phy/phy-meson-gxl-usb2.c
> @@ -0,0 +1,374 @@
> +/*
> + * Meson GXL USB2 PHY driver
> + *
> + * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/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>
> +#include <linux/usb/of.h>
> +
> +/* bits [31:27] are read-only */
> +#define U2P_R0							0x0
> +	#define U2P_R0_BYPASS_SEL				BIT(0)
> +	#define U2P_R0_BYPASS_DM_EN				BIT(1)
> +	#define U2P_R0_BYPASS_DP_EN				BIT(2)
> +	#define U2P_R0_TXBITSTUFF_ENH				BIT(3)
> +	#define U2P_R0_TXBITSTUFF_EN				BIT(4)
> +	#define U2P_R0_DM_PULLDOWN				BIT(5)
> +	#define U2P_R0_DP_PULLDOWN				BIT(6)
> +	#define U2P_R0_DP_VBUS_VLD_EXT_SEL			BIT(7)
> +	#define U2P_R0_DP_VBUS_VLD_EXT				BIT(8)
> +	#define U2P_R0_ADP_PRB_EN				BIT(9)
> +	#define U2P_R0_ADP_DISCHARGE				BIT(10)
> +	#define U2P_R0_ADP_CHARGE				BIT(11)
> +	#define U2P_R0_DRV_VBUS					BIT(12)
> +	#define U2P_R0_ID_PULLUP				BIT(13)
> +	#define U2P_R0_LOOPBACK_EN_B				BIT(14)
> +	#define U2P_R0_OTG_DISABLE				BIT(15)
> +	#define U2P_R0_COMMON_ONN				BIT(16)
> +	#define U2P_R0_FSEL_SHIFT				17
> +	#define U2P_R0_FSEL_MASK				GENMASK(19, 17)
> +	#define U2P_R0_REF_CLK_SEL_SHIFT			20
> +	#define U2P_R0_REF_CLK_SEL_MASK				GENMASK(21, 20)
> +	#define U2P_R0_POWER_ON_RESET				BIT(22)
> +	#define U2P_R0_V_ATE_TEST_EN_B_SHIFT			23
> +	#define U2P_R0_V_ATE_TEST_EN_B_MASK			GENMASK(24, 23)
> +	#define U2P_R0_ID_SET_ID_DQ				BIT(25)
> +	#define U2P_R0_ATE_RESET				BIT(26)
> +	#define U2P_R0_FSV_MINUS				BIT(27)
> +	#define U2P_R0_FSV_PLUS					BIT(28)
> +	#define U2P_R0_BYPASS_DM_DATA				BIT(29)
> +	#define U2P_R0_BYPASS_DP_DATA				BIT(30)
> +
> +#define U2P_R1							0x4
> +	#define U2P_R1_BURN_IN_TEST				BIT(0)
> +	#define U2P_R1_ACA_ENABLE				BIT(1)
> +	#define U2P_R1_DCD_ENABLE				BIT(2)
> +	#define U2P_R1_VDAT_SRC_EN_B				BIT(3)
> +	#define U2P_R1_VDAT_DET_EN_B				BIT(4)
> +	#define U2P_R1_CHARGES_SEL				BIT(5)
> +	#define U2P_R1_TX_PREEMP_PULSE_TUNE			BIT(6)
> +	#define U2P_R1_TX_PREEMP_AMP_TUNE_SHIFT			7
> +	#define U2P_R1_TX_PREEMP_AMP_TUNE_MASK			GENMASK(8, 7)
> +	#define U2P_R1_TX_RES_TUNE_SHIFT			9
> +	#define U2P_R1_TX_RES_TUNE_MASK				GENMASK(10, 9)
> +	#define U2P_R1_TX_RISE_TUNE_SHIFT			11
> +	#define U2P_R1_TX_RISE_TUNE_MASK			GENMASK(12, 11)
> +	#define U2P_R1_TX_VREF_TUNE_SHIFT			13
> +	#define U2P_R1_TX_VREF_TUNE_MASK			GENMASK(16, 13)
> +	#define U2P_R1_TX_FSLS_TUNE_SHIFT			17
> +	#define U2P_R1_TX_FSLS_TUNE_MASK			GENMASK(20, 17)
> +	#define U2P_R1_TX_HSXV_TUNE_SHIFT			21
> +	#define U2P_R1_TX_HSXV_TUNE_MASK			GENMASK(22, 21)
> +	#define U2P_R1_OTG_TUNE_SHIFT				23
> +	#define U2P_R1_OTG_TUNE_MASK				GENMASK(25, 23)
> +	#define U2P_R1_SQRX_TUNE_SHIFT				26
> +	#define U2P_R1_SQRX_TUNE_MASK				GENMASK(28, 26)
> +	#define U2P_R1_COMP_DIS_TUNE_SHIFT			29
> +	#define U2P_R1_COMP_DIS_TUNE_MASK			GENMASK(31, 29)
> +
> +/* bits [31:14] are read-only */
> +#define U2P_R2							0x8
> +	#define U2P_R2_DATA_IN_SHIFT				0
> +	#define U2P_R2_DATA_IN_MASK				GENMASK(3, 0)
> +	#define U2P_R2_DATA_IN_EN_SHIFT				4
> +	#define U2P_R2_DATA_IN_EN_MASK				GENMASK(7, 4)
> +	#define U2P_R2_ADDR_SHIFT				8
> +	#define U2P_R2_ADDR_MASK				GENMASK(11, 8)
> +	#define U2P_R2_DATA_OUT_SEL				BIT(12)
> +	#define U2P_R2_CLK					BIT(13)
> +	#define U2P_R2_DATA_OUT_SHIFT				14
> +	#define U2P_R2_DATA_OUT_MASK				GENMASK(17, 14)
> +	#define U2P_R2_ACA_PIN_RANGE_C				BIT(18)
> +	#define U2P_R2_ACA_PIN_RANGE_B				BIT(19)
> +	#define U2P_R2_ACA_PIN_RANGE_A				BIT(20)
> +	#define U2P_R2_ACA_PIN_GND				BIT(21)
> +	#define U2P_R2_ACA_PIN_FLOAT				BIT(22)
> +	#define U2P_R2_CHARGE_DETECT				BIT(23)
> +	#define U2P_R2_DEVICE_SESSION_VALID			BIT(24)
> +	#define U2P_R2_ADP_PROBE				BIT(25)
> +	#define U2P_R2_ADP_SENSE				BIT(26)
> +	#define U2P_R2_SESSION_END				BIT(27)
> +	#define U2P_R2_VBUS_VALID				BIT(28)
> +	#define U2P_R2_B_VALID					BIT(29)
> +	#define U2P_R2_A_VALID					BIT(30)
> +	#define U2P_R2_ID_DIG					BIT(31)
> +
> +#define U2P_R3							0xc
> +
> +#define PHY_PORT_RESOURCE_SIZE					0x20
> +
> +#define RESET_COMPLETE_TIME				500
> +
> +struct phy_meson_gxl_usb2_priv {
> +	struct regmap		*regmap;
> +	enum phy_mode		mode;
> +};
> +
> +struct phy_meson_gxl_usb2_drv {
> +	void __iomem		*base;
> +	int			num_ports;
> +	struct phy		**ports;
> +	struct clk		*clk_usb;
> +	struct clk		*clk_usb_ddr;
> +};
> +
> +static const struct regmap_config phy_meson_gxl_usb2_regmap_conf = {
> +	.reg_bits = 32,
> +	.val_bits = 32,
> +	.reg_stride = 4,
> +	.max_register = U2P_R3,
> +};
> +
> +static int phy_meson_gxl_usb2_set_mode(struct phy *phy, enum phy_mode mode)
> +{
> +	struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
> +
> +	switch (mode) {
> +	case PHY_MODE_USB_HOST:
> +	case PHY_MODE_USB_OTG:
> +		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DM_PULLDOWN,
> +				   U2P_R0_DM_PULLDOWN);
> +		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DP_PULLDOWN,
> +				   U2P_R0_DP_PULLDOWN);
> +		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_ID_PULLUP, 0);
> +		break;
> +
> +	case PHY_MODE_USB_DEVICE:
> +		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DM_PULLDOWN,
> +				   0);
> +		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DP_PULLDOWN,
> +				   0);
> +		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_ID_PULLUP,
> +				   U2P_R0_ID_PULLUP);
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	/* reset the PHY and wait until settings are stabilized */
> +	regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET,
> +			   U2P_R0_POWER_ON_RESET);
> +	udelay(RESET_COMPLETE_TIME);
> +	regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET, 0);
> +	udelay(RESET_COMPLETE_TIME);
> +
> +	priv->mode = mode;
> +
> +	return 0;
> +}
> +
> +static int phy_meson_gxl_usb2_power_off(struct phy *phy)
> +{
> +	struct phy_meson_gxl_usb2_drv *drv_priv =
> +		dev_get_drvdata(phy->dev.parent);
> +	struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
> +
> +	/* power off the PHY by putting it into reset mode */
> +	regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET,
> +			   U2P_R0_POWER_ON_RESET);
> +
> +	clk_disable_unprepare(drv_priv->clk_usb_ddr);
> +	clk_disable_unprepare(drv_priv->clk_usb);
> +
> +	return 0;
> +}
> +
> +static int phy_meson_gxl_usb2_power_on(struct phy *phy)
> +{
> +	struct phy_meson_gxl_usb2_drv *drv_priv =
> +		dev_get_drvdata(phy->dev.parent);
> +	struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
> +	int ret;
> +
> +	ret = clk_prepare_enable(drv_priv->clk_usb);
> +	if (ret) {
> +		dev_err(&phy->dev, "Failed to enable USB clock\n");
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(drv_priv->clk_usb_ddr);
> +	if (ret) {
> +		clk_disable_unprepare(drv_priv->clk_usb);
> +
> +		dev_err(&phy->dev, "Failed to enable USB DDR clock\n");
> +		return ret;
> +	}
> +
> +	/* power on the PHY by taking it out of reset mode */
> +	regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET, 0);
> +
> +	ret = phy_meson_gxl_usb2_set_mode(phy, priv->mode);
> +	if (ret) {
> +		phy_meson_gxl_usb2_power_off(phy);
> +
> +		dev_err(&phy->dev, "Failed to initialize PHY with mode %d\n",
> +			priv->mode);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct phy_ops phy_meson_gxl_usb2_ops = {
> +	.power_on	= phy_meson_gxl_usb2_power_on,
> +	.power_off	= phy_meson_gxl_usb2_power_off,
> +	.set_mode	= phy_meson_gxl_usb2_set_mode,
> +	.owner		= THIS_MODULE,
> +};
> +
> +static struct phy *phy_meson_gxl_usb2_of_xlate(struct device *dev,
> +					       struct of_phandle_args *args)
> +{
> +	struct phy_meson_gxl_usb2_drv *priv = dev_get_drvdata(dev);
> +	int port;
> +
> +	if (args->args_count != 1) {
> +		dev_err(dev, "Invalid number of cells in 'phy' property\n");
> +		return ERR_PTR(-ENODEV);
> +	}
> +
> +	port = args->args[0];
> +	if (WARN_ON(port >= priv->num_ports))
> +		return ERR_PTR(-ENODEV);
> +
> +	return priv->ports[port];
> +}

Please model every port as a sub-node and get rid of custom xlate implementation.

> +
> +static int phy_meson_gxl_usb2_probe_port(struct device *dev, int port)
> +{
> +	struct phy_meson_gxl_usb2_drv *drv_priv = dev_get_drvdata(dev);
> +	struct phy_meson_gxl_usb2_priv *phy_priv;
> +	struct phy *phy;
> +	void __iomem *port_base;
> +
> +	phy_priv = devm_kzalloc(dev, sizeof(*phy_priv), GFP_KERNEL);
> +	if (!phy_priv)
> +		return -ENOMEM;
> +
> +	switch (of_usb_get_dr_mode_by_phy(dev->of_node, port)) {
> +	case USB_DR_MODE_PERIPHERAL:
> +		phy_priv->mode = PHY_MODE_USB_DEVICE;
> +		break;
> +	case USB_DR_MODE_OTG:
> +		phy_priv->mode = PHY_MODE_USB_OTG;
> +		break;
> +	case USB_DR_MODE_HOST:
> +	default:
> +		phy_priv->mode = PHY_MODE_USB_HOST;
> +		break;
> +	}
> +
> +	phy = devm_phy_create(dev, NULL, &phy_meson_gxl_usb2_ops);
> +	if (IS_ERR(phy)) {
> +		dev_err(dev, "failed to create PHY port %d\n", port);
> +		return PTR_ERR(phy);
> +	}
> +
> +	port_base = drv_priv->base + (port * PHY_PORT_RESOURCE_SIZE);
> +	phy_priv->regmap = devm_regmap_init_mmio(&phy->dev, port_base,
> +						 &phy_meson_gxl_usb2_regmap_conf);
> +	if (IS_ERR(phy_priv->regmap))
> +		return PTR_ERR(phy_priv->regmap);
> +
> +	phy_set_drvdata(phy, phy_priv);
> +
> +	drv_priv->ports[port] = phy;
> +
> +	return 0;
> +}
> +
> +static int phy_meson_gxl_usb2_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct phy_meson_gxl_usb2_drv *priv;
> +	struct phy_provider *phy_provider;
> +	struct resource *res;
> +	int i, ret;
> +
> +	ret = device_reset(dev);
> +	if (ret) {
> +		dev_err(dev, "failed to reset device\n");
> +		return ret;
> +	}
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	platform_set_drvdata(pdev, priv);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	priv->base = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(priv->base))
> +		return PTR_ERR(priv->base);
> +
> +	priv->num_ports = resource_size(res) / PHY_PORT_RESOURCE_SIZE;
> +	if (priv->num_ports < 1) {
> +		dev_err(dev, "specified memory range is too small\n");
> +		return -EINVAL;
> +	}
> +
> +	priv->ports = devm_kcalloc(dev, priv->num_ports, sizeof(*priv->ports),
> +				   GFP_KERNEL);
> +	if (!priv->ports)
> +		return -ENOMEM;
> +
> +	priv->clk_usb = devm_clk_get(dev, "usb");
> +	if (IS_ERR(priv->clk_usb)) {
> +		dev_err(dev, "failed to get USB clock\n");
> +		return PTR_ERR(priv->clk_usb);
> +	}
> +
> +	priv->clk_usb_ddr = devm_clk_get(dev, "usb_ddr");
> +	if (IS_ERR(priv->clk_usb_ddr)) {
> +		dev_err(dev, "failed to get USB DDR clock\n");
> +		return PTR_ERR(priv->clk_usb_ddr);
> +	}
> +
> +	for (i = 0; i < priv->num_ports; i++) {
> +		ret = phy_meson_gxl_usb2_probe_port(dev, i);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	phy_provider = devm_of_phy_provider_register(dev,
> +						     phy_meson_gxl_usb2_of_xlate);
> +
> +	return PTR_ERR_OR_ZERO(phy_provider);
> +}
> +
> +static const struct of_device_id phy_meson_gxl_usb2_of_match[] = {
> +	{ .compatible = "amlogic,meson-gxl-usb2-phy", },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, phy_meson_gxl_usb2_of_match);
> +
> +static struct platform_driver phy_meson_gxl_usb2_driver = {
> +	.probe	= phy_meson_gxl_usb2_probe,
> +	.driver	= {
> +		.name		= "phy-meson-gxl-usb2",
> +		.of_match_table	= phy_meson_gxl_usb2_of_match,
> +	},
> +};
> +module_platform_driver(phy_meson_gxl_usb2_driver);
> +
> +MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
> +MODULE_DESCRIPTION("Meson GXL USB2 PHY driver");
> +MODULE_LICENSE("GPL");

GPL v2 to match with the file header.
> diff --git a/drivers/phy/phy-meson-gxl-usb3.c b/drivers/phy/phy-meson-gxl-usb3.c
> new file mode 100644
> index 0000000..90a4028
> --- /dev/null
> +++ b/drivers/phy/phy-meson-gxl-usb3.c
> @@ -0,0 +1,377 @@
> +/*
> + * Meson GXL USB3 PHY driver
> + *
> + * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/reset.h>
> +#include <linux/regmap.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/usb/of.h>
> +#include <linux/workqueue.h>
> +
> +#define USB_R0							0x00
> +	#define USB_R0_P30_FSEL_SHIFT				0
> +	#define USB_R0_P30_FSEL_MASK				GENMASK(5, 0)
> +	#define USB_R0_P30_PHY_RESET				BIT(6)
> +	#define USB_R0_P30_TEST_POWERDOWN_HSP			BIT(7)
> +	#define USB_R0_P30_TEST_POWERDOWN_SSP			BIT(8)
> +	#define USB_R0_P30_ACJT_LEVEL_SHIFT			9
> +	#define USB_R0_P30_ACJT_LEVEL_MASK			GENMASK(13, 9)
> +	#define USB_R0_P30_TX_BOOST_LEVEL_SHIFT			14
> +	#define USB_R0_P30_TX_BOOST_LEVEL_MASK			GENMASK(16, 14)
> +	#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_SHIFT		19
> +	#define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK		GENMASK(28, 19)
> +	#define USB_R0_U2D_SS_SCALEDOWN_MODE_SHIFT		29
> +	#define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK		GENMASK(30, 29)
> +	#define USB_R0_U2D_ACT					BIT(31)
> +
> +#define USB_R1							0x04
> +	#define USB_R1_U3H_BIGENDIAN_GS				BIT(0)
> +	#define USB_R1_U3H_PME_ENABLE				BIT(1)
> +	#define USB_R1_U3H_HUB_PORT_OVERCURRENT_SHIFT		2
> +	#define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK		GENMASK(6, 2)
> +	#define USB_R1_U3H_HUB_PORT_PERM_ATTACH_SHIFT		7
> +	#define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK		GENMASK(11, 7)
> +	#define USB_R1_U3H_HOST_U2_PORT_DISABLE_SHIFT		12
> +	#define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK		GENMASK(15, 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_SHIFT		19
> +	#define USB_R1_U3H_FLADJ_30MHZ_REG_MASK			GENMASK(24, 19)
> +	#define USB_R1_P30_PCS_TX_SWING_FULL_SHIFT		25
> +	#define USB_R1_P30_PCS_TX_SWING_FULL_MASK		GENMASK(31, 25)
> +
> +#define USB_R2							0x08
> +	#define USB_R2_P30_CR_DATA_IN_SHIFT			0
> +	#define USB_R2_P30_CR_DATA_IN_MASK			GENMASK(15, 0)
> +	#define USB_R2_P30_CR_READ				BIT(16)
> +	#define USB_R2_P30_CR_WRITE				BIT(17)
> +	#define USB_R2_P30_CR_CAP_ADDR				BIT(18)
> +	#define USB_R2_P30_CR_CAP_DATA				BIT(19)
> +	#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_SHIFT		20
> +	#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK		GENMASK(25, 20)
> +	#define USB_R2_P30_PCS_TX_DEEMPH_6DB_SHIFT		26
> +	#define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK		GENMASK(31, 26)
> +
> +#define USB_R3							0x0c
> +	#define USB_R3_P30_SSC_ENABLE				BIT(0)
> +	#define USB_R3_P30_SSC_RANGE_SHIFT			1
> +	#define USB_R3_P30_SSC_RANGE_MASK			GENMASK(3, 1)
> +	#define USB_R3_P30_SSC_REF_CLK_SEL_SHIFT		4
> +	#define USB_R3_P30_SSC_REF_CLK_SEL_MASK			GENMASK(12, 4)
> +	#define USB_R3_P30_REF_SSP_EN				BIT(13)
> +	#define USB_R3_P30_LOS_BIAS_SHIFT			16
> +	#define USB_R3_P30_LOS_BIAS_MASK			GENMASK(18, 16)
> +	#define USB_R3_P30_LOS_LEVEL_SHIFT			19
> +	#define USB_R3_P30_LOS_LEVEL_MASK			GENMASK(23, 19)
> +	#define USB_R3_P30_MPLL_MULTIPLIER_SHIFT		24
> +	#define USB_R3_P30_MPLL_MULTIPLIER_MASK			GENMASK(30, 24)
> +
> +#define USB_R4							0x10
> +	#define USB_R4_P21_PORT_RESET_0				BIT(0)
> +	#define USB_R4_P21_SLEEP_M0				BIT(1)
> +	#define USB_R4_MEM_PD_SHIFT				2
> +	#define USB_R4_MEM_PD_MASK				GENMASK(3, 2)
> +	#define USB_R4_P21_ONLY					BIT(4)
> +
> +#define USB_R5							0x14
> +	#define USB_R5_ID_DIG_SYNC				BIT(0)
> +	#define USB_R5_ID_DIG_REG				BIT(1)
> +	#define USB_R5_ID_DIG_CFG_SHIFT				2
> +	#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_SHIFT				8
> +	#define USB_R5_ID_DIG_TH_MASK				GENMASK(15, 8)
> +	#define USB_R5_ID_DIG_CNT_SHIFT				16
> +	#define USB_R5_ID_DIG_CNT_MASK				GENMASK(23, 16)
> +
> +/* read-only register */
> +#define USB_R6							0x18
> +	#define USB_R6_P30_CR_DATA_OUT_SHIFT			0
> +	#define USB_R6_P30_CR_DATA_OUT_MASK			GENMASK(15, 0)
> +	#define USB_R6_P30_CR_ACK				BIT(16)
> +
> +#define RESET_COMPLETE_TIME				500
> +
> +struct phy_meson_gxl_usb3_priv {
> +	struct regmap		*regmap;
> +	struct delayed_work	otg_work;
> +	struct phy		*this_phy;
> +	int			num_usb2_phys;
> +	struct phy		**usb2_phys;
> +};
> +
> +static const struct regmap_config phy_meson_gxl_usb3_regmap_conf = {
> +	.reg_bits = 32,
> +	.val_bits = 32,
> +	.reg_stride = 4,
> +	.max_register = USB_R6,
> +};
> +
> +static int phy_meson_gxl_usb3_update_mode(struct phy *phy)
> +{
> +	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
> +	u32 val;
> +	enum phy_mode mode;
> +	int i, ret;
> +
> +	ret = regmap_read(priv->regmap, USB_R5, &val);
> +	if (ret)
> +		return ret;
> +
> +	if (val & USB_R5_ID_DIG_CURR) {
> +		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_R4, USB_R4_P21_SLEEP_M0,
> +				   USB_R4_P21_SLEEP_M0);
> +	} else {
> +		mode = PHY_MODE_USB_HOST;
> +
> +		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);
> +	}
> +
> +	/* inform the USB2 PHY that we have changed the mode */
> +	for (i = 0; i < priv->num_usb2_phys; i++) {
> +		ret = phy_set_mode(priv->usb2_phys[i], mode);

I'm finding it difficult to understand this. Why should the mode of one phy be
set from another phy? Maybe this part should be implemented using extcon?

> +		if (ret) {
> +			dev_err(&phy->dev,
> +				"Failed to update usb2-phy #%d mode to %d\n",
> +				i, mode);
> +			return ret;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static void phy_meson_gxl_usb3_work(struct work_struct *data)
> +{
> +	struct phy_meson_gxl_usb3_priv *priv =
> +		container_of(data, struct phy_meson_gxl_usb3_priv,
> +			     otg_work.work);
> +
> +	phy_meson_gxl_usb3_update_mode(priv->this_phy);
> +
> +	/* unmask IRQs which may have arrived in the meantime */
> +	regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_IRQ, 0);
> +}
> +
> +static int phy_meson_gxl_usb3_init(struct phy *phy)
> +{
> +	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
> +	int i, ret;
> +
> +	for (i = 0; i < priv->num_usb2_phys; i++) {
> +		ret = phy_init(priv->usb2_phys[i]);
> +		if (ret) {
> +			dev_err(&phy->dev,
> +				"Failed to initialize related usb2-phy #%d\n",
> +				i);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int phy_meson_gxl_usb3_exit(struct phy *phy)
> +{
> +	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
> +	int i, ret;
> +
> +	for (i = 0; i < priv->num_usb2_phys; i++) {
> +		ret = phy_exit(priv->usb2_phys[i]);
> +		if (ret) {
> +			dev_err(&phy->dev,
> +				"Failed to exit related usb2-phy #%d\n", i);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int phy_meson_gxl_usb3_power_on(struct phy *phy)
> +{
> +	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
> +	int i, ret;
> +
> +	for (i = 0; i < priv->num_usb2_phys; i++) {
> +		ret = phy_power_on(priv->usb2_phys[i]);
> +		if (ret) {
> +			dev_err(&phy->dev,
> +				"Failed to power on related usb2-phy #%d\n",
> +				i);
> +			return ret;
> +		}
> +	}
> +
> +	regmap_update_bits(priv->regmap, USB_R1,
> +			   USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
> +			   0x20 << USB_R1_U3H_FLADJ_30MHZ_REG_SHIFT);
> +
> +	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,
> +			   0xff << USB_R5_ID_DIG_TH_SHIFT);
> +
> +	return phy_meson_gxl_usb3_update_mode(phy);
> +}
> +
> +static int phy_meson_gxl_usb3_power_off(struct phy *phy)
> +{
> +	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
> +	int i, ret;
> +
> +	for (i = 0; i < priv->num_usb2_phys; i++) {
> +		ret = phy_power_off(priv->usb2_phys[i]);
> +		if (ret) {
> +			dev_err(&phy->dev,
> +				"Failed to power off related usb2-phy #%d\n",
> +				i);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static irqreturn_t phy_meson_gxl_usb3_irq(int irq, void *data)
> +{
> +	u32 val;
> +	struct phy_meson_gxl_usb3_priv *priv = data;
> +
> +	regmap_read(priv->regmap, USB_R5, &val);
> +	if (!(val & USB_R5_ID_DIG_IRQ)) {
> +		dev_err(&priv->this_phy->dev, "spurious interrupt\n");
> +		return IRQ_NONE;
> +	}
> +
> +	schedule_delayed_work(&priv->otg_work, msecs_to_jiffies(10));
> +
> +	/* acknowledge the IRQ */
> +	regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_IRQ, 0);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static const struct phy_ops phy_meson_gxl_usb3_ops = {
> +	.init		= phy_meson_gxl_usb3_init,
> +	.exit		= phy_meson_gxl_usb3_exit,
> +	.power_on	= phy_meson_gxl_usb3_power_on,
> +	.power_off	= phy_meson_gxl_usb3_power_off,
> +	.owner		= THIS_MODULE,
> +};
> +
> +static int phy_meson_gxl_usb3_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *np = dev->of_node;
> +	struct phy_meson_gxl_usb3_priv *priv;
> +	struct resource *res;
> +	struct phy *phy;
> +	struct phy_provider *phy_provider;
> +	void __iomem *base;
> +	int i, irq;
> +
> +	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_gxl_usb3_regmap_conf);
> +	if (IS_ERR(priv->regmap))
> +		return PTR_ERR(priv->regmap);
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq >= 0) {
> +		INIT_DELAYED_WORK(&priv->otg_work, phy_meson_gxl_usb3_work);
> +
> +		irq = devm_request_irq(dev, irq, phy_meson_gxl_usb3_irq,
> +				       IRQF_SHARED, dev_name(dev),
> +				       priv);
> +		if (irq < 0) {
> +			dev_err(dev, "could not register IRQ handler (%d)\n",
> +				irq);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	priv->num_usb2_phys = of_count_phandle_with_args(np, "phys",
> +							 "#phy-cells");
> +
> +	priv->usb2_phys = devm_kcalloc(dev, priv->num_usb2_phys,
> +				       sizeof(*priv->usb2_phys), GFP_KERNEL);
> +	if (!priv->usb2_phys)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < priv->num_usb2_phys; i++) {
> +		priv->usb2_phys[i] = devm_of_phy_get_by_index(dev, np, i);

I'm not sure if referencing usb2_phy from here is the right approach.

Thanks
Kishon

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

* [PATCH 2/5] phy: meson: add USB2 and USB3 PHY support for Meson GXL
@ 2017-01-16  9:41       ` Kishon Vijay Abraham I
  0 siblings, 0 replies; 39+ messages in thread
From: Kishon Vijay Abraham I @ 2017-01-16  9:41 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On Saturday 26 November 2016 08:26 PM, Martin Blumenstingl wrote:
> This adds two new USB PHY drivers found on Meson GXL and GXM SoCs.

Please send them as separate drivers.
> 
> The registers for the USB2 PHY block handle a maximum of 4 ports (newer
> SoCs may allow more ports, the driver handles this as long as the
> register length is adjusted in the .dts). The PHY block theoretically
> allows powering down each PHY port separately (by putting it into
> "reset" state). Unfortunately this does not work (my board has 2 USB
> ports, connected to port 1 and 2 of the dwc3's internal hub. When
> leaving the third USB PHY disabled then the hub sees that a device is
> plugged in, but it does not work: "usb usb1-port2: connect-debounce
> failed").
> The USB3 PHY will take care of enabling/disabling all available ports,
> because the USB3 PHY also manages the mode of the USB2 PHYs.
> 
> The USB3 PHY actually has three purposes:
> - it provides the USB3 PHY
> - it handles the OTG device/host mode detection interrupt
> - it notifies the corresponding USB2 PHYs of the OTG mode changes
> On GXL and GXM SoCs one references all available USB2 PHY ports in the
> USB3 PHY because all are connected to the same USB controller (thus the
> mode will always match). This behavior is configurable via devicetree,
> by passing (or not passing) a list of other ("child") PHYs which should
> be configured by the USB3 PHY.
> 
> Unfortunately there are no datasheets available for any of these PHYs.
> Both drivers were written by reading the reference drivers provided by
> Amlogic and analyzing the registers on the kernel that was shipped with
> my board.
> 
> Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
> ---
>  drivers/phy/Kconfig              |  13 ++
>  drivers/phy/Makefile             |   2 +
>  drivers/phy/phy-meson-gxl-usb2.c | 374 ++++++++++++++++++++++++++++++++++++++
>  drivers/phy/phy-meson-gxl-usb3.c | 377 +++++++++++++++++++++++++++++++++++++++
>  4 files changed, 766 insertions(+)
>  create mode 100644 drivers/phy/phy-meson-gxl-usb2.c
>  create mode 100644 drivers/phy/phy-meson-gxl-usb3.c
> 
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index 728e03f..ea74843 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -502,4 +502,17 @@ config PHY_MESON8B_USB2
>  	  and GXBB SoCs.
>  	  If unsure, say N.
>  
> +config PHY_MESON_GXL_USB
> +	tristate "Meson GXL USB2 and USB3 PHY drivers"
> +	default ARCH_MESON
> +	depends on OF && (ARCH_MESON || COMPILE_TEST)
> +	depends on USB_SUPPORT
> +	select USB_COMMON
> +	select GENERIC_PHY
> +	select REGMAP_MMIO
> +	help
> +	  Enable this to support the Meson USB2 and USB3 PHYs found in
> +	  Meson GXL SoCs.
> +	  If unsure, say N.
> +
>  endmenu
> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> index 0c7fdae..960a96e 100644
> --- a/drivers/phy/Makefile
> +++ b/drivers/phy/Makefile
> @@ -61,3 +61,5 @@ obj-$(CONFIG_PHY_CYGNUS_PCIE)		+= phy-bcm-cygnus-pcie.o
>  obj-$(CONFIG_ARCH_TEGRA) += tegra/
>  obj-$(CONFIG_PHY_NS2_PCIE)		+= phy-bcm-ns2-pcie.o
>  obj-$(CONFIG_PHY_MESON8B_USB2)		+= phy-meson8b-usb2.o
> +obj-$(CONFIG_PHY_MESON_GXL_USB)		+= phy-meson-gxl-usb2.o
> +obj-$(CONFIG_PHY_MESON_GXL_USB)		+= phy-meson-gxl-usb3.o
> diff --git a/drivers/phy/phy-meson-gxl-usb2.c b/drivers/phy/phy-meson-gxl-usb2.c
> new file mode 100644
> index 0000000..c081ce3
> --- /dev/null
> +++ b/drivers/phy/phy-meson-gxl-usb2.c
> @@ -0,0 +1,374 @@
> +/*
> + * Meson GXL USB2 PHY driver
> + *
> + * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/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>
> +#include <linux/usb/of.h>
> +
> +/* bits [31:27] are read-only */
> +#define U2P_R0							0x0
> +	#define U2P_R0_BYPASS_SEL				BIT(0)
> +	#define U2P_R0_BYPASS_DM_EN				BIT(1)
> +	#define U2P_R0_BYPASS_DP_EN				BIT(2)
> +	#define U2P_R0_TXBITSTUFF_ENH				BIT(3)
> +	#define U2P_R0_TXBITSTUFF_EN				BIT(4)
> +	#define U2P_R0_DM_PULLDOWN				BIT(5)
> +	#define U2P_R0_DP_PULLDOWN				BIT(6)
> +	#define U2P_R0_DP_VBUS_VLD_EXT_SEL			BIT(7)
> +	#define U2P_R0_DP_VBUS_VLD_EXT				BIT(8)
> +	#define U2P_R0_ADP_PRB_EN				BIT(9)
> +	#define U2P_R0_ADP_DISCHARGE				BIT(10)
> +	#define U2P_R0_ADP_CHARGE				BIT(11)
> +	#define U2P_R0_DRV_VBUS					BIT(12)
> +	#define U2P_R0_ID_PULLUP				BIT(13)
> +	#define U2P_R0_LOOPBACK_EN_B				BIT(14)
> +	#define U2P_R0_OTG_DISABLE				BIT(15)
> +	#define U2P_R0_COMMON_ONN				BIT(16)
> +	#define U2P_R0_FSEL_SHIFT				17
> +	#define U2P_R0_FSEL_MASK				GENMASK(19, 17)
> +	#define U2P_R0_REF_CLK_SEL_SHIFT			20
> +	#define U2P_R0_REF_CLK_SEL_MASK				GENMASK(21, 20)
> +	#define U2P_R0_POWER_ON_RESET				BIT(22)
> +	#define U2P_R0_V_ATE_TEST_EN_B_SHIFT			23
> +	#define U2P_R0_V_ATE_TEST_EN_B_MASK			GENMASK(24, 23)
> +	#define U2P_R0_ID_SET_ID_DQ				BIT(25)
> +	#define U2P_R0_ATE_RESET				BIT(26)
> +	#define U2P_R0_FSV_MINUS				BIT(27)
> +	#define U2P_R0_FSV_PLUS					BIT(28)
> +	#define U2P_R0_BYPASS_DM_DATA				BIT(29)
> +	#define U2P_R0_BYPASS_DP_DATA				BIT(30)
> +
> +#define U2P_R1							0x4
> +	#define U2P_R1_BURN_IN_TEST				BIT(0)
> +	#define U2P_R1_ACA_ENABLE				BIT(1)
> +	#define U2P_R1_DCD_ENABLE				BIT(2)
> +	#define U2P_R1_VDAT_SRC_EN_B				BIT(3)
> +	#define U2P_R1_VDAT_DET_EN_B				BIT(4)
> +	#define U2P_R1_CHARGES_SEL				BIT(5)
> +	#define U2P_R1_TX_PREEMP_PULSE_TUNE			BIT(6)
> +	#define U2P_R1_TX_PREEMP_AMP_TUNE_SHIFT			7
> +	#define U2P_R1_TX_PREEMP_AMP_TUNE_MASK			GENMASK(8, 7)
> +	#define U2P_R1_TX_RES_TUNE_SHIFT			9
> +	#define U2P_R1_TX_RES_TUNE_MASK				GENMASK(10, 9)
> +	#define U2P_R1_TX_RISE_TUNE_SHIFT			11
> +	#define U2P_R1_TX_RISE_TUNE_MASK			GENMASK(12, 11)
> +	#define U2P_R1_TX_VREF_TUNE_SHIFT			13
> +	#define U2P_R1_TX_VREF_TUNE_MASK			GENMASK(16, 13)
> +	#define U2P_R1_TX_FSLS_TUNE_SHIFT			17
> +	#define U2P_R1_TX_FSLS_TUNE_MASK			GENMASK(20, 17)
> +	#define U2P_R1_TX_HSXV_TUNE_SHIFT			21
> +	#define U2P_R1_TX_HSXV_TUNE_MASK			GENMASK(22, 21)
> +	#define U2P_R1_OTG_TUNE_SHIFT				23
> +	#define U2P_R1_OTG_TUNE_MASK				GENMASK(25, 23)
> +	#define U2P_R1_SQRX_TUNE_SHIFT				26
> +	#define U2P_R1_SQRX_TUNE_MASK				GENMASK(28, 26)
> +	#define U2P_R1_COMP_DIS_TUNE_SHIFT			29
> +	#define U2P_R1_COMP_DIS_TUNE_MASK			GENMASK(31, 29)
> +
> +/* bits [31:14] are read-only */
> +#define U2P_R2							0x8
> +	#define U2P_R2_DATA_IN_SHIFT				0
> +	#define U2P_R2_DATA_IN_MASK				GENMASK(3, 0)
> +	#define U2P_R2_DATA_IN_EN_SHIFT				4
> +	#define U2P_R2_DATA_IN_EN_MASK				GENMASK(7, 4)
> +	#define U2P_R2_ADDR_SHIFT				8
> +	#define U2P_R2_ADDR_MASK				GENMASK(11, 8)
> +	#define U2P_R2_DATA_OUT_SEL				BIT(12)
> +	#define U2P_R2_CLK					BIT(13)
> +	#define U2P_R2_DATA_OUT_SHIFT				14
> +	#define U2P_R2_DATA_OUT_MASK				GENMASK(17, 14)
> +	#define U2P_R2_ACA_PIN_RANGE_C				BIT(18)
> +	#define U2P_R2_ACA_PIN_RANGE_B				BIT(19)
> +	#define U2P_R2_ACA_PIN_RANGE_A				BIT(20)
> +	#define U2P_R2_ACA_PIN_GND				BIT(21)
> +	#define U2P_R2_ACA_PIN_FLOAT				BIT(22)
> +	#define U2P_R2_CHARGE_DETECT				BIT(23)
> +	#define U2P_R2_DEVICE_SESSION_VALID			BIT(24)
> +	#define U2P_R2_ADP_PROBE				BIT(25)
> +	#define U2P_R2_ADP_SENSE				BIT(26)
> +	#define U2P_R2_SESSION_END				BIT(27)
> +	#define U2P_R2_VBUS_VALID				BIT(28)
> +	#define U2P_R2_B_VALID					BIT(29)
> +	#define U2P_R2_A_VALID					BIT(30)
> +	#define U2P_R2_ID_DIG					BIT(31)
> +
> +#define U2P_R3							0xc
> +
> +#define PHY_PORT_RESOURCE_SIZE					0x20
> +
> +#define RESET_COMPLETE_TIME				500
> +
> +struct phy_meson_gxl_usb2_priv {
> +	struct regmap		*regmap;
> +	enum phy_mode		mode;
> +};
> +
> +struct phy_meson_gxl_usb2_drv {
> +	void __iomem		*base;
> +	int			num_ports;
> +	struct phy		**ports;
> +	struct clk		*clk_usb;
> +	struct clk		*clk_usb_ddr;
> +};
> +
> +static const struct regmap_config phy_meson_gxl_usb2_regmap_conf = {
> +	.reg_bits = 32,
> +	.val_bits = 32,
> +	.reg_stride = 4,
> +	.max_register = U2P_R3,
> +};
> +
> +static int phy_meson_gxl_usb2_set_mode(struct phy *phy, enum phy_mode mode)
> +{
> +	struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
> +
> +	switch (mode) {
> +	case PHY_MODE_USB_HOST:
> +	case PHY_MODE_USB_OTG:
> +		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DM_PULLDOWN,
> +				   U2P_R0_DM_PULLDOWN);
> +		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DP_PULLDOWN,
> +				   U2P_R0_DP_PULLDOWN);
> +		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_ID_PULLUP, 0);
> +		break;
> +
> +	case PHY_MODE_USB_DEVICE:
> +		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DM_PULLDOWN,
> +				   0);
> +		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DP_PULLDOWN,
> +				   0);
> +		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_ID_PULLUP,
> +				   U2P_R0_ID_PULLUP);
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	/* reset the PHY and wait until settings are stabilized */
> +	regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET,
> +			   U2P_R0_POWER_ON_RESET);
> +	udelay(RESET_COMPLETE_TIME);
> +	regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET, 0);
> +	udelay(RESET_COMPLETE_TIME);
> +
> +	priv->mode = mode;
> +
> +	return 0;
> +}
> +
> +static int phy_meson_gxl_usb2_power_off(struct phy *phy)
> +{
> +	struct phy_meson_gxl_usb2_drv *drv_priv =
> +		dev_get_drvdata(phy->dev.parent);
> +	struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
> +
> +	/* power off the PHY by putting it into reset mode */
> +	regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET,
> +			   U2P_R0_POWER_ON_RESET);
> +
> +	clk_disable_unprepare(drv_priv->clk_usb_ddr);
> +	clk_disable_unprepare(drv_priv->clk_usb);
> +
> +	return 0;
> +}
> +
> +static int phy_meson_gxl_usb2_power_on(struct phy *phy)
> +{
> +	struct phy_meson_gxl_usb2_drv *drv_priv =
> +		dev_get_drvdata(phy->dev.parent);
> +	struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
> +	int ret;
> +
> +	ret = clk_prepare_enable(drv_priv->clk_usb);
> +	if (ret) {
> +		dev_err(&phy->dev, "Failed to enable USB clock\n");
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(drv_priv->clk_usb_ddr);
> +	if (ret) {
> +		clk_disable_unprepare(drv_priv->clk_usb);
> +
> +		dev_err(&phy->dev, "Failed to enable USB DDR clock\n");
> +		return ret;
> +	}
> +
> +	/* power on the PHY by taking it out of reset mode */
> +	regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET, 0);
> +
> +	ret = phy_meson_gxl_usb2_set_mode(phy, priv->mode);
> +	if (ret) {
> +		phy_meson_gxl_usb2_power_off(phy);
> +
> +		dev_err(&phy->dev, "Failed to initialize PHY with mode %d\n",
> +			priv->mode);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct phy_ops phy_meson_gxl_usb2_ops = {
> +	.power_on	= phy_meson_gxl_usb2_power_on,
> +	.power_off	= phy_meson_gxl_usb2_power_off,
> +	.set_mode	= phy_meson_gxl_usb2_set_mode,
> +	.owner		= THIS_MODULE,
> +};
> +
> +static struct phy *phy_meson_gxl_usb2_of_xlate(struct device *dev,
> +					       struct of_phandle_args *args)
> +{
> +	struct phy_meson_gxl_usb2_drv *priv = dev_get_drvdata(dev);
> +	int port;
> +
> +	if (args->args_count != 1) {
> +		dev_err(dev, "Invalid number of cells in 'phy' property\n");
> +		return ERR_PTR(-ENODEV);
> +	}
> +
> +	port = args->args[0];
> +	if (WARN_ON(port >= priv->num_ports))
> +		return ERR_PTR(-ENODEV);
> +
> +	return priv->ports[port];
> +}

Please model every port as a sub-node and get rid of custom xlate implementation.

> +
> +static int phy_meson_gxl_usb2_probe_port(struct device *dev, int port)
> +{
> +	struct phy_meson_gxl_usb2_drv *drv_priv = dev_get_drvdata(dev);
> +	struct phy_meson_gxl_usb2_priv *phy_priv;
> +	struct phy *phy;
> +	void __iomem *port_base;
> +
> +	phy_priv = devm_kzalloc(dev, sizeof(*phy_priv), GFP_KERNEL);
> +	if (!phy_priv)
> +		return -ENOMEM;
> +
> +	switch (of_usb_get_dr_mode_by_phy(dev->of_node, port)) {
> +	case USB_DR_MODE_PERIPHERAL:
> +		phy_priv->mode = PHY_MODE_USB_DEVICE;
> +		break;
> +	case USB_DR_MODE_OTG:
> +		phy_priv->mode = PHY_MODE_USB_OTG;
> +		break;
> +	case USB_DR_MODE_HOST:
> +	default:
> +		phy_priv->mode = PHY_MODE_USB_HOST;
> +		break;
> +	}
> +
> +	phy = devm_phy_create(dev, NULL, &phy_meson_gxl_usb2_ops);
> +	if (IS_ERR(phy)) {
> +		dev_err(dev, "failed to create PHY port %d\n", port);
> +		return PTR_ERR(phy);
> +	}
> +
> +	port_base = drv_priv->base + (port * PHY_PORT_RESOURCE_SIZE);
> +	phy_priv->regmap = devm_regmap_init_mmio(&phy->dev, port_base,
> +						 &phy_meson_gxl_usb2_regmap_conf);
> +	if (IS_ERR(phy_priv->regmap))
> +		return PTR_ERR(phy_priv->regmap);
> +
> +	phy_set_drvdata(phy, phy_priv);
> +
> +	drv_priv->ports[port] = phy;
> +
> +	return 0;
> +}
> +
> +static int phy_meson_gxl_usb2_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct phy_meson_gxl_usb2_drv *priv;
> +	struct phy_provider *phy_provider;
> +	struct resource *res;
> +	int i, ret;
> +
> +	ret = device_reset(dev);
> +	if (ret) {
> +		dev_err(dev, "failed to reset device\n");
> +		return ret;
> +	}
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	platform_set_drvdata(pdev, priv);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	priv->base = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(priv->base))
> +		return PTR_ERR(priv->base);
> +
> +	priv->num_ports = resource_size(res) / PHY_PORT_RESOURCE_SIZE;
> +	if (priv->num_ports < 1) {
> +		dev_err(dev, "specified memory range is too small\n");
> +		return -EINVAL;
> +	}
> +
> +	priv->ports = devm_kcalloc(dev, priv->num_ports, sizeof(*priv->ports),
> +				   GFP_KERNEL);
> +	if (!priv->ports)
> +		return -ENOMEM;
> +
> +	priv->clk_usb = devm_clk_get(dev, "usb");
> +	if (IS_ERR(priv->clk_usb)) {
> +		dev_err(dev, "failed to get USB clock\n");
> +		return PTR_ERR(priv->clk_usb);
> +	}
> +
> +	priv->clk_usb_ddr = devm_clk_get(dev, "usb_ddr");
> +	if (IS_ERR(priv->clk_usb_ddr)) {
> +		dev_err(dev, "failed to get USB DDR clock\n");
> +		return PTR_ERR(priv->clk_usb_ddr);
> +	}
> +
> +	for (i = 0; i < priv->num_ports; i++) {
> +		ret = phy_meson_gxl_usb2_probe_port(dev, i);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	phy_provider = devm_of_phy_provider_register(dev,
> +						     phy_meson_gxl_usb2_of_xlate);
> +
> +	return PTR_ERR_OR_ZERO(phy_provider);
> +}
> +
> +static const struct of_device_id phy_meson_gxl_usb2_of_match[] = {
> +	{ .compatible = "amlogic,meson-gxl-usb2-phy", },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, phy_meson_gxl_usb2_of_match);
> +
> +static struct platform_driver phy_meson_gxl_usb2_driver = {
> +	.probe	= phy_meson_gxl_usb2_probe,
> +	.driver	= {
> +		.name		= "phy-meson-gxl-usb2",
> +		.of_match_table	= phy_meson_gxl_usb2_of_match,
> +	},
> +};
> +module_platform_driver(phy_meson_gxl_usb2_driver);
> +
> +MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
> +MODULE_DESCRIPTION("Meson GXL USB2 PHY driver");
> +MODULE_LICENSE("GPL");

GPL v2 to match with the file header.
> diff --git a/drivers/phy/phy-meson-gxl-usb3.c b/drivers/phy/phy-meson-gxl-usb3.c
> new file mode 100644
> index 0000000..90a4028
> --- /dev/null
> +++ b/drivers/phy/phy-meson-gxl-usb3.c
> @@ -0,0 +1,377 @@
> +/*
> + * Meson GXL USB3 PHY driver
> + *
> + * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/reset.h>
> +#include <linux/regmap.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/usb/of.h>
> +#include <linux/workqueue.h>
> +
> +#define USB_R0							0x00
> +	#define USB_R0_P30_FSEL_SHIFT				0
> +	#define USB_R0_P30_FSEL_MASK				GENMASK(5, 0)
> +	#define USB_R0_P30_PHY_RESET				BIT(6)
> +	#define USB_R0_P30_TEST_POWERDOWN_HSP			BIT(7)
> +	#define USB_R0_P30_TEST_POWERDOWN_SSP			BIT(8)
> +	#define USB_R0_P30_ACJT_LEVEL_SHIFT			9
> +	#define USB_R0_P30_ACJT_LEVEL_MASK			GENMASK(13, 9)
> +	#define USB_R0_P30_TX_BOOST_LEVEL_SHIFT			14
> +	#define USB_R0_P30_TX_BOOST_LEVEL_MASK			GENMASK(16, 14)
> +	#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_SHIFT		19
> +	#define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK		GENMASK(28, 19)
> +	#define USB_R0_U2D_SS_SCALEDOWN_MODE_SHIFT		29
> +	#define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK		GENMASK(30, 29)
> +	#define USB_R0_U2D_ACT					BIT(31)
> +
> +#define USB_R1							0x04
> +	#define USB_R1_U3H_BIGENDIAN_GS				BIT(0)
> +	#define USB_R1_U3H_PME_ENABLE				BIT(1)
> +	#define USB_R1_U3H_HUB_PORT_OVERCURRENT_SHIFT		2
> +	#define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK		GENMASK(6, 2)
> +	#define USB_R1_U3H_HUB_PORT_PERM_ATTACH_SHIFT		7
> +	#define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK		GENMASK(11, 7)
> +	#define USB_R1_U3H_HOST_U2_PORT_DISABLE_SHIFT		12
> +	#define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK		GENMASK(15, 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_SHIFT		19
> +	#define USB_R1_U3H_FLADJ_30MHZ_REG_MASK			GENMASK(24, 19)
> +	#define USB_R1_P30_PCS_TX_SWING_FULL_SHIFT		25
> +	#define USB_R1_P30_PCS_TX_SWING_FULL_MASK		GENMASK(31, 25)
> +
> +#define USB_R2							0x08
> +	#define USB_R2_P30_CR_DATA_IN_SHIFT			0
> +	#define USB_R2_P30_CR_DATA_IN_MASK			GENMASK(15, 0)
> +	#define USB_R2_P30_CR_READ				BIT(16)
> +	#define USB_R2_P30_CR_WRITE				BIT(17)
> +	#define USB_R2_P30_CR_CAP_ADDR				BIT(18)
> +	#define USB_R2_P30_CR_CAP_DATA				BIT(19)
> +	#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_SHIFT		20
> +	#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK		GENMASK(25, 20)
> +	#define USB_R2_P30_PCS_TX_DEEMPH_6DB_SHIFT		26
> +	#define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK		GENMASK(31, 26)
> +
> +#define USB_R3							0x0c
> +	#define USB_R3_P30_SSC_ENABLE				BIT(0)
> +	#define USB_R3_P30_SSC_RANGE_SHIFT			1
> +	#define USB_R3_P30_SSC_RANGE_MASK			GENMASK(3, 1)
> +	#define USB_R3_P30_SSC_REF_CLK_SEL_SHIFT		4
> +	#define USB_R3_P30_SSC_REF_CLK_SEL_MASK			GENMASK(12, 4)
> +	#define USB_R3_P30_REF_SSP_EN				BIT(13)
> +	#define USB_R3_P30_LOS_BIAS_SHIFT			16
> +	#define USB_R3_P30_LOS_BIAS_MASK			GENMASK(18, 16)
> +	#define USB_R3_P30_LOS_LEVEL_SHIFT			19
> +	#define USB_R3_P30_LOS_LEVEL_MASK			GENMASK(23, 19)
> +	#define USB_R3_P30_MPLL_MULTIPLIER_SHIFT		24
> +	#define USB_R3_P30_MPLL_MULTIPLIER_MASK			GENMASK(30, 24)
> +
> +#define USB_R4							0x10
> +	#define USB_R4_P21_PORT_RESET_0				BIT(0)
> +	#define USB_R4_P21_SLEEP_M0				BIT(1)
> +	#define USB_R4_MEM_PD_SHIFT				2
> +	#define USB_R4_MEM_PD_MASK				GENMASK(3, 2)
> +	#define USB_R4_P21_ONLY					BIT(4)
> +
> +#define USB_R5							0x14
> +	#define USB_R5_ID_DIG_SYNC				BIT(0)
> +	#define USB_R5_ID_DIG_REG				BIT(1)
> +	#define USB_R5_ID_DIG_CFG_SHIFT				2
> +	#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_SHIFT				8
> +	#define USB_R5_ID_DIG_TH_MASK				GENMASK(15, 8)
> +	#define USB_R5_ID_DIG_CNT_SHIFT				16
> +	#define USB_R5_ID_DIG_CNT_MASK				GENMASK(23, 16)
> +
> +/* read-only register */
> +#define USB_R6							0x18
> +	#define USB_R6_P30_CR_DATA_OUT_SHIFT			0
> +	#define USB_R6_P30_CR_DATA_OUT_MASK			GENMASK(15, 0)
> +	#define USB_R6_P30_CR_ACK				BIT(16)
> +
> +#define RESET_COMPLETE_TIME				500
> +
> +struct phy_meson_gxl_usb3_priv {
> +	struct regmap		*regmap;
> +	struct delayed_work	otg_work;
> +	struct phy		*this_phy;
> +	int			num_usb2_phys;
> +	struct phy		**usb2_phys;
> +};
> +
> +static const struct regmap_config phy_meson_gxl_usb3_regmap_conf = {
> +	.reg_bits = 32,
> +	.val_bits = 32,
> +	.reg_stride = 4,
> +	.max_register = USB_R6,
> +};
> +
> +static int phy_meson_gxl_usb3_update_mode(struct phy *phy)
> +{
> +	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
> +	u32 val;
> +	enum phy_mode mode;
> +	int i, ret;
> +
> +	ret = regmap_read(priv->regmap, USB_R5, &val);
> +	if (ret)
> +		return ret;
> +
> +	if (val & USB_R5_ID_DIG_CURR) {
> +		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_R4, USB_R4_P21_SLEEP_M0,
> +				   USB_R4_P21_SLEEP_M0);
> +	} else {
> +		mode = PHY_MODE_USB_HOST;
> +
> +		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);
> +	}
> +
> +	/* inform the USB2 PHY that we have changed the mode */
> +	for (i = 0; i < priv->num_usb2_phys; i++) {
> +		ret = phy_set_mode(priv->usb2_phys[i], mode);

I'm finding it difficult to understand this. Why should the mode of one phy be
set from another phy? Maybe this part should be implemented using extcon?

> +		if (ret) {
> +			dev_err(&phy->dev,
> +				"Failed to update usb2-phy #%d mode to %d\n",
> +				i, mode);
> +			return ret;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static void phy_meson_gxl_usb3_work(struct work_struct *data)
> +{
> +	struct phy_meson_gxl_usb3_priv *priv =
> +		container_of(data, struct phy_meson_gxl_usb3_priv,
> +			     otg_work.work);
> +
> +	phy_meson_gxl_usb3_update_mode(priv->this_phy);
> +
> +	/* unmask IRQs which may have arrived in the meantime */
> +	regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_IRQ, 0);
> +}
> +
> +static int phy_meson_gxl_usb3_init(struct phy *phy)
> +{
> +	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
> +	int i, ret;
> +
> +	for (i = 0; i < priv->num_usb2_phys; i++) {
> +		ret = phy_init(priv->usb2_phys[i]);
> +		if (ret) {
> +			dev_err(&phy->dev,
> +				"Failed to initialize related usb2-phy #%d\n",
> +				i);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int phy_meson_gxl_usb3_exit(struct phy *phy)
> +{
> +	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
> +	int i, ret;
> +
> +	for (i = 0; i < priv->num_usb2_phys; i++) {
> +		ret = phy_exit(priv->usb2_phys[i]);
> +		if (ret) {
> +			dev_err(&phy->dev,
> +				"Failed to exit related usb2-phy #%d\n", i);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int phy_meson_gxl_usb3_power_on(struct phy *phy)
> +{
> +	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
> +	int i, ret;
> +
> +	for (i = 0; i < priv->num_usb2_phys; i++) {
> +		ret = phy_power_on(priv->usb2_phys[i]);
> +		if (ret) {
> +			dev_err(&phy->dev,
> +				"Failed to power on related usb2-phy #%d\n",
> +				i);
> +			return ret;
> +		}
> +	}
> +
> +	regmap_update_bits(priv->regmap, USB_R1,
> +			   USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
> +			   0x20 << USB_R1_U3H_FLADJ_30MHZ_REG_SHIFT);
> +
> +	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,
> +			   0xff << USB_R5_ID_DIG_TH_SHIFT);
> +
> +	return phy_meson_gxl_usb3_update_mode(phy);
> +}
> +
> +static int phy_meson_gxl_usb3_power_off(struct phy *phy)
> +{
> +	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
> +	int i, ret;
> +
> +	for (i = 0; i < priv->num_usb2_phys; i++) {
> +		ret = phy_power_off(priv->usb2_phys[i]);
> +		if (ret) {
> +			dev_err(&phy->dev,
> +				"Failed to power off related usb2-phy #%d\n",
> +				i);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static irqreturn_t phy_meson_gxl_usb3_irq(int irq, void *data)
> +{
> +	u32 val;
> +	struct phy_meson_gxl_usb3_priv *priv = data;
> +
> +	regmap_read(priv->regmap, USB_R5, &val);
> +	if (!(val & USB_R5_ID_DIG_IRQ)) {
> +		dev_err(&priv->this_phy->dev, "spurious interrupt\n");
> +		return IRQ_NONE;
> +	}
> +
> +	schedule_delayed_work(&priv->otg_work, msecs_to_jiffies(10));
> +
> +	/* acknowledge the IRQ */
> +	regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_IRQ, 0);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static const struct phy_ops phy_meson_gxl_usb3_ops = {
> +	.init		= phy_meson_gxl_usb3_init,
> +	.exit		= phy_meson_gxl_usb3_exit,
> +	.power_on	= phy_meson_gxl_usb3_power_on,
> +	.power_off	= phy_meson_gxl_usb3_power_off,
> +	.owner		= THIS_MODULE,
> +};
> +
> +static int phy_meson_gxl_usb3_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *np = dev->of_node;
> +	struct phy_meson_gxl_usb3_priv *priv;
> +	struct resource *res;
> +	struct phy *phy;
> +	struct phy_provider *phy_provider;
> +	void __iomem *base;
> +	int i, irq;
> +
> +	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_gxl_usb3_regmap_conf);
> +	if (IS_ERR(priv->regmap))
> +		return PTR_ERR(priv->regmap);
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq >= 0) {
> +		INIT_DELAYED_WORK(&priv->otg_work, phy_meson_gxl_usb3_work);
> +
> +		irq = devm_request_irq(dev, irq, phy_meson_gxl_usb3_irq,
> +				       IRQF_SHARED, dev_name(dev),
> +				       priv);
> +		if (irq < 0) {
> +			dev_err(dev, "could not register IRQ handler (%d)\n",
> +				irq);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	priv->num_usb2_phys = of_count_phandle_with_args(np, "phys",
> +							 "#phy-cells");
> +
> +	priv->usb2_phys = devm_kcalloc(dev, priv->num_usb2_phys,
> +				       sizeof(*priv->usb2_phys), GFP_KERNEL);
> +	if (!priv->usb2_phys)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < priv->num_usb2_phys; i++) {
> +		priv->usb2_phys[i] = devm_of_phy_get_by_index(dev, np, i);

I'm not sure if referencing usb2_phy from here is the right approach.

Thanks
Kishon

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

* [PATCH 2/5] phy: meson: add USB2 and USB3 PHY support for Meson GXL
@ 2017-01-16  9:41       ` Kishon Vijay Abraham I
  0 siblings, 0 replies; 39+ messages in thread
From: Kishon Vijay Abraham I @ 2017-01-16  9:41 UTC (permalink / raw)
  To: linus-amlogic

Hi,

On Saturday 26 November 2016 08:26 PM, Martin Blumenstingl wrote:
> This adds two new USB PHY drivers found on Meson GXL and GXM SoCs.

Please send them as separate drivers.
> 
> The registers for the USB2 PHY block handle a maximum of 4 ports (newer
> SoCs may allow more ports, the driver handles this as long as the
> register length is adjusted in the .dts). The PHY block theoretically
> allows powering down each PHY port separately (by putting it into
> "reset" state). Unfortunately this does not work (my board has 2 USB
> ports, connected to port 1 and 2 of the dwc3's internal hub. When
> leaving the third USB PHY disabled then the hub sees that a device is
> plugged in, but it does not work: "usb usb1-port2: connect-debounce
> failed").
> The USB3 PHY will take care of enabling/disabling all available ports,
> because the USB3 PHY also manages the mode of the USB2 PHYs.
> 
> The USB3 PHY actually has three purposes:
> - it provides the USB3 PHY
> - it handles the OTG device/host mode detection interrupt
> - it notifies the corresponding USB2 PHYs of the OTG mode changes
> On GXL and GXM SoCs one references all available USB2 PHY ports in the
> USB3 PHY because all are connected to the same USB controller (thus the
> mode will always match). This behavior is configurable via devicetree,
> by passing (or not passing) a list of other ("child") PHYs which should
> be configured by the USB3 PHY.
> 
> Unfortunately there are no datasheets available for any of these PHYs.
> Both drivers were written by reading the reference drivers provided by
> Amlogic and analyzing the registers on the kernel that was shipped with
> my board.
> 
> Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
> ---
>  drivers/phy/Kconfig              |  13 ++
>  drivers/phy/Makefile             |   2 +
>  drivers/phy/phy-meson-gxl-usb2.c | 374 ++++++++++++++++++++++++++++++++++++++
>  drivers/phy/phy-meson-gxl-usb3.c | 377 +++++++++++++++++++++++++++++++++++++++
>  4 files changed, 766 insertions(+)
>  create mode 100644 drivers/phy/phy-meson-gxl-usb2.c
>  create mode 100644 drivers/phy/phy-meson-gxl-usb3.c
> 
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index 728e03f..ea74843 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -502,4 +502,17 @@ config PHY_MESON8B_USB2
>  	  and GXBB SoCs.
>  	  If unsure, say N.
>  
> +config PHY_MESON_GXL_USB
> +	tristate "Meson GXL USB2 and USB3 PHY drivers"
> +	default ARCH_MESON
> +	depends on OF && (ARCH_MESON || COMPILE_TEST)
> +	depends on USB_SUPPORT
> +	select USB_COMMON
> +	select GENERIC_PHY
> +	select REGMAP_MMIO
> +	help
> +	  Enable this to support the Meson USB2 and USB3 PHYs found in
> +	  Meson GXL SoCs.
> +	  If unsure, say N.
> +
>  endmenu
> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> index 0c7fdae..960a96e 100644
> --- a/drivers/phy/Makefile
> +++ b/drivers/phy/Makefile
> @@ -61,3 +61,5 @@ obj-$(CONFIG_PHY_CYGNUS_PCIE)		+= phy-bcm-cygnus-pcie.o
>  obj-$(CONFIG_ARCH_TEGRA) += tegra/
>  obj-$(CONFIG_PHY_NS2_PCIE)		+= phy-bcm-ns2-pcie.o
>  obj-$(CONFIG_PHY_MESON8B_USB2)		+= phy-meson8b-usb2.o
> +obj-$(CONFIG_PHY_MESON_GXL_USB)		+= phy-meson-gxl-usb2.o
> +obj-$(CONFIG_PHY_MESON_GXL_USB)		+= phy-meson-gxl-usb3.o
> diff --git a/drivers/phy/phy-meson-gxl-usb2.c b/drivers/phy/phy-meson-gxl-usb2.c
> new file mode 100644
> index 0000000..c081ce3
> --- /dev/null
> +++ b/drivers/phy/phy-meson-gxl-usb2.c
> @@ -0,0 +1,374 @@
> +/*
> + * Meson GXL USB2 PHY driver
> + *
> + * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/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>
> +#include <linux/usb/of.h>
> +
> +/* bits [31:27] are read-only */
> +#define U2P_R0							0x0
> +	#define U2P_R0_BYPASS_SEL				BIT(0)
> +	#define U2P_R0_BYPASS_DM_EN				BIT(1)
> +	#define U2P_R0_BYPASS_DP_EN				BIT(2)
> +	#define U2P_R0_TXBITSTUFF_ENH				BIT(3)
> +	#define U2P_R0_TXBITSTUFF_EN				BIT(4)
> +	#define U2P_R0_DM_PULLDOWN				BIT(5)
> +	#define U2P_R0_DP_PULLDOWN				BIT(6)
> +	#define U2P_R0_DP_VBUS_VLD_EXT_SEL			BIT(7)
> +	#define U2P_R0_DP_VBUS_VLD_EXT				BIT(8)
> +	#define U2P_R0_ADP_PRB_EN				BIT(9)
> +	#define U2P_R0_ADP_DISCHARGE				BIT(10)
> +	#define U2P_R0_ADP_CHARGE				BIT(11)
> +	#define U2P_R0_DRV_VBUS					BIT(12)
> +	#define U2P_R0_ID_PULLUP				BIT(13)
> +	#define U2P_R0_LOOPBACK_EN_B				BIT(14)
> +	#define U2P_R0_OTG_DISABLE				BIT(15)
> +	#define U2P_R0_COMMON_ONN				BIT(16)
> +	#define U2P_R0_FSEL_SHIFT				17
> +	#define U2P_R0_FSEL_MASK				GENMASK(19, 17)
> +	#define U2P_R0_REF_CLK_SEL_SHIFT			20
> +	#define U2P_R0_REF_CLK_SEL_MASK				GENMASK(21, 20)
> +	#define U2P_R0_POWER_ON_RESET				BIT(22)
> +	#define U2P_R0_V_ATE_TEST_EN_B_SHIFT			23
> +	#define U2P_R0_V_ATE_TEST_EN_B_MASK			GENMASK(24, 23)
> +	#define U2P_R0_ID_SET_ID_DQ				BIT(25)
> +	#define U2P_R0_ATE_RESET				BIT(26)
> +	#define U2P_R0_FSV_MINUS				BIT(27)
> +	#define U2P_R0_FSV_PLUS					BIT(28)
> +	#define U2P_R0_BYPASS_DM_DATA				BIT(29)
> +	#define U2P_R0_BYPASS_DP_DATA				BIT(30)
> +
> +#define U2P_R1							0x4
> +	#define U2P_R1_BURN_IN_TEST				BIT(0)
> +	#define U2P_R1_ACA_ENABLE				BIT(1)
> +	#define U2P_R1_DCD_ENABLE				BIT(2)
> +	#define U2P_R1_VDAT_SRC_EN_B				BIT(3)
> +	#define U2P_R1_VDAT_DET_EN_B				BIT(4)
> +	#define U2P_R1_CHARGES_SEL				BIT(5)
> +	#define U2P_R1_TX_PREEMP_PULSE_TUNE			BIT(6)
> +	#define U2P_R1_TX_PREEMP_AMP_TUNE_SHIFT			7
> +	#define U2P_R1_TX_PREEMP_AMP_TUNE_MASK			GENMASK(8, 7)
> +	#define U2P_R1_TX_RES_TUNE_SHIFT			9
> +	#define U2P_R1_TX_RES_TUNE_MASK				GENMASK(10, 9)
> +	#define U2P_R1_TX_RISE_TUNE_SHIFT			11
> +	#define U2P_R1_TX_RISE_TUNE_MASK			GENMASK(12, 11)
> +	#define U2P_R1_TX_VREF_TUNE_SHIFT			13
> +	#define U2P_R1_TX_VREF_TUNE_MASK			GENMASK(16, 13)
> +	#define U2P_R1_TX_FSLS_TUNE_SHIFT			17
> +	#define U2P_R1_TX_FSLS_TUNE_MASK			GENMASK(20, 17)
> +	#define U2P_R1_TX_HSXV_TUNE_SHIFT			21
> +	#define U2P_R1_TX_HSXV_TUNE_MASK			GENMASK(22, 21)
> +	#define U2P_R1_OTG_TUNE_SHIFT				23
> +	#define U2P_R1_OTG_TUNE_MASK				GENMASK(25, 23)
> +	#define U2P_R1_SQRX_TUNE_SHIFT				26
> +	#define U2P_R1_SQRX_TUNE_MASK				GENMASK(28, 26)
> +	#define U2P_R1_COMP_DIS_TUNE_SHIFT			29
> +	#define U2P_R1_COMP_DIS_TUNE_MASK			GENMASK(31, 29)
> +
> +/* bits [31:14] are read-only */
> +#define U2P_R2							0x8
> +	#define U2P_R2_DATA_IN_SHIFT				0
> +	#define U2P_R2_DATA_IN_MASK				GENMASK(3, 0)
> +	#define U2P_R2_DATA_IN_EN_SHIFT				4
> +	#define U2P_R2_DATA_IN_EN_MASK				GENMASK(7, 4)
> +	#define U2P_R2_ADDR_SHIFT				8
> +	#define U2P_R2_ADDR_MASK				GENMASK(11, 8)
> +	#define U2P_R2_DATA_OUT_SEL				BIT(12)
> +	#define U2P_R2_CLK					BIT(13)
> +	#define U2P_R2_DATA_OUT_SHIFT				14
> +	#define U2P_R2_DATA_OUT_MASK				GENMASK(17, 14)
> +	#define U2P_R2_ACA_PIN_RANGE_C				BIT(18)
> +	#define U2P_R2_ACA_PIN_RANGE_B				BIT(19)
> +	#define U2P_R2_ACA_PIN_RANGE_A				BIT(20)
> +	#define U2P_R2_ACA_PIN_GND				BIT(21)
> +	#define U2P_R2_ACA_PIN_FLOAT				BIT(22)
> +	#define U2P_R2_CHARGE_DETECT				BIT(23)
> +	#define U2P_R2_DEVICE_SESSION_VALID			BIT(24)
> +	#define U2P_R2_ADP_PROBE				BIT(25)
> +	#define U2P_R2_ADP_SENSE				BIT(26)
> +	#define U2P_R2_SESSION_END				BIT(27)
> +	#define U2P_R2_VBUS_VALID				BIT(28)
> +	#define U2P_R2_B_VALID					BIT(29)
> +	#define U2P_R2_A_VALID					BIT(30)
> +	#define U2P_R2_ID_DIG					BIT(31)
> +
> +#define U2P_R3							0xc
> +
> +#define PHY_PORT_RESOURCE_SIZE					0x20
> +
> +#define RESET_COMPLETE_TIME				500
> +
> +struct phy_meson_gxl_usb2_priv {
> +	struct regmap		*regmap;
> +	enum phy_mode		mode;
> +};
> +
> +struct phy_meson_gxl_usb2_drv {
> +	void __iomem		*base;
> +	int			num_ports;
> +	struct phy		**ports;
> +	struct clk		*clk_usb;
> +	struct clk		*clk_usb_ddr;
> +};
> +
> +static const struct regmap_config phy_meson_gxl_usb2_regmap_conf = {
> +	.reg_bits = 32,
> +	.val_bits = 32,
> +	.reg_stride = 4,
> +	.max_register = U2P_R3,
> +};
> +
> +static int phy_meson_gxl_usb2_set_mode(struct phy *phy, enum phy_mode mode)
> +{
> +	struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
> +
> +	switch (mode) {
> +	case PHY_MODE_USB_HOST:
> +	case PHY_MODE_USB_OTG:
> +		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DM_PULLDOWN,
> +				   U2P_R0_DM_PULLDOWN);
> +		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DP_PULLDOWN,
> +				   U2P_R0_DP_PULLDOWN);
> +		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_ID_PULLUP, 0);
> +		break;
> +
> +	case PHY_MODE_USB_DEVICE:
> +		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DM_PULLDOWN,
> +				   0);
> +		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_DP_PULLDOWN,
> +				   0);
> +		regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_ID_PULLUP,
> +				   U2P_R0_ID_PULLUP);
> +		break;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	/* reset the PHY and wait until settings are stabilized */
> +	regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET,
> +			   U2P_R0_POWER_ON_RESET);
> +	udelay(RESET_COMPLETE_TIME);
> +	regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET, 0);
> +	udelay(RESET_COMPLETE_TIME);
> +
> +	priv->mode = mode;
> +
> +	return 0;
> +}
> +
> +static int phy_meson_gxl_usb2_power_off(struct phy *phy)
> +{
> +	struct phy_meson_gxl_usb2_drv *drv_priv =
> +		dev_get_drvdata(phy->dev.parent);
> +	struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
> +
> +	/* power off the PHY by putting it into reset mode */
> +	regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET,
> +			   U2P_R0_POWER_ON_RESET);
> +
> +	clk_disable_unprepare(drv_priv->clk_usb_ddr);
> +	clk_disable_unprepare(drv_priv->clk_usb);
> +
> +	return 0;
> +}
> +
> +static int phy_meson_gxl_usb2_power_on(struct phy *phy)
> +{
> +	struct phy_meson_gxl_usb2_drv *drv_priv =
> +		dev_get_drvdata(phy->dev.parent);
> +	struct phy_meson_gxl_usb2_priv *priv = phy_get_drvdata(phy);
> +	int ret;
> +
> +	ret = clk_prepare_enable(drv_priv->clk_usb);
> +	if (ret) {
> +		dev_err(&phy->dev, "Failed to enable USB clock\n");
> +		return ret;
> +	}
> +
> +	ret = clk_prepare_enable(drv_priv->clk_usb_ddr);
> +	if (ret) {
> +		clk_disable_unprepare(drv_priv->clk_usb);
> +
> +		dev_err(&phy->dev, "Failed to enable USB DDR clock\n");
> +		return ret;
> +	}
> +
> +	/* power on the PHY by taking it out of reset mode */
> +	regmap_update_bits(priv->regmap, U2P_R0, U2P_R0_POWER_ON_RESET, 0);
> +
> +	ret = phy_meson_gxl_usb2_set_mode(phy, priv->mode);
> +	if (ret) {
> +		phy_meson_gxl_usb2_power_off(phy);
> +
> +		dev_err(&phy->dev, "Failed to initialize PHY with mode %d\n",
> +			priv->mode);
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct phy_ops phy_meson_gxl_usb2_ops = {
> +	.power_on	= phy_meson_gxl_usb2_power_on,
> +	.power_off	= phy_meson_gxl_usb2_power_off,
> +	.set_mode	= phy_meson_gxl_usb2_set_mode,
> +	.owner		= THIS_MODULE,
> +};
> +
> +static struct phy *phy_meson_gxl_usb2_of_xlate(struct device *dev,
> +					       struct of_phandle_args *args)
> +{
> +	struct phy_meson_gxl_usb2_drv *priv = dev_get_drvdata(dev);
> +	int port;
> +
> +	if (args->args_count != 1) {
> +		dev_err(dev, "Invalid number of cells in 'phy' property\n");
> +		return ERR_PTR(-ENODEV);
> +	}
> +
> +	port = args->args[0];
> +	if (WARN_ON(port >= priv->num_ports))
> +		return ERR_PTR(-ENODEV);
> +
> +	return priv->ports[port];
> +}

Please model every port as a sub-node and get rid of custom xlate implementation.

> +
> +static int phy_meson_gxl_usb2_probe_port(struct device *dev, int port)
> +{
> +	struct phy_meson_gxl_usb2_drv *drv_priv = dev_get_drvdata(dev);
> +	struct phy_meson_gxl_usb2_priv *phy_priv;
> +	struct phy *phy;
> +	void __iomem *port_base;
> +
> +	phy_priv = devm_kzalloc(dev, sizeof(*phy_priv), GFP_KERNEL);
> +	if (!phy_priv)
> +		return -ENOMEM;
> +
> +	switch (of_usb_get_dr_mode_by_phy(dev->of_node, port)) {
> +	case USB_DR_MODE_PERIPHERAL:
> +		phy_priv->mode = PHY_MODE_USB_DEVICE;
> +		break;
> +	case USB_DR_MODE_OTG:
> +		phy_priv->mode = PHY_MODE_USB_OTG;
> +		break;
> +	case USB_DR_MODE_HOST:
> +	default:
> +		phy_priv->mode = PHY_MODE_USB_HOST;
> +		break;
> +	}
> +
> +	phy = devm_phy_create(dev, NULL, &phy_meson_gxl_usb2_ops);
> +	if (IS_ERR(phy)) {
> +		dev_err(dev, "failed to create PHY port %d\n", port);
> +		return PTR_ERR(phy);
> +	}
> +
> +	port_base = drv_priv->base + (port * PHY_PORT_RESOURCE_SIZE);
> +	phy_priv->regmap = devm_regmap_init_mmio(&phy->dev, port_base,
> +						 &phy_meson_gxl_usb2_regmap_conf);
> +	if (IS_ERR(phy_priv->regmap))
> +		return PTR_ERR(phy_priv->regmap);
> +
> +	phy_set_drvdata(phy, phy_priv);
> +
> +	drv_priv->ports[port] = phy;
> +
> +	return 0;
> +}
> +
> +static int phy_meson_gxl_usb2_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct phy_meson_gxl_usb2_drv *priv;
> +	struct phy_provider *phy_provider;
> +	struct resource *res;
> +	int i, ret;
> +
> +	ret = device_reset(dev);
> +	if (ret) {
> +		dev_err(dev, "failed to reset device\n");
> +		return ret;
> +	}
> +
> +	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return -ENOMEM;
> +
> +	platform_set_drvdata(pdev, priv);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	priv->base = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(priv->base))
> +		return PTR_ERR(priv->base);
> +
> +	priv->num_ports = resource_size(res) / PHY_PORT_RESOURCE_SIZE;
> +	if (priv->num_ports < 1) {
> +		dev_err(dev, "specified memory range is too small\n");
> +		return -EINVAL;
> +	}
> +
> +	priv->ports = devm_kcalloc(dev, priv->num_ports, sizeof(*priv->ports),
> +				   GFP_KERNEL);
> +	if (!priv->ports)
> +		return -ENOMEM;
> +
> +	priv->clk_usb = devm_clk_get(dev, "usb");
> +	if (IS_ERR(priv->clk_usb)) {
> +		dev_err(dev, "failed to get USB clock\n");
> +		return PTR_ERR(priv->clk_usb);
> +	}
> +
> +	priv->clk_usb_ddr = devm_clk_get(dev, "usb_ddr");
> +	if (IS_ERR(priv->clk_usb_ddr)) {
> +		dev_err(dev, "failed to get USB DDR clock\n");
> +		return PTR_ERR(priv->clk_usb_ddr);
> +	}
> +
> +	for (i = 0; i < priv->num_ports; i++) {
> +		ret = phy_meson_gxl_usb2_probe_port(dev, i);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	phy_provider = devm_of_phy_provider_register(dev,
> +						     phy_meson_gxl_usb2_of_xlate);
> +
> +	return PTR_ERR_OR_ZERO(phy_provider);
> +}
> +
> +static const struct of_device_id phy_meson_gxl_usb2_of_match[] = {
> +	{ .compatible = "amlogic,meson-gxl-usb2-phy", },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, phy_meson_gxl_usb2_of_match);
> +
> +static struct platform_driver phy_meson_gxl_usb2_driver = {
> +	.probe	= phy_meson_gxl_usb2_probe,
> +	.driver	= {
> +		.name		= "phy-meson-gxl-usb2",
> +		.of_match_table	= phy_meson_gxl_usb2_of_match,
> +	},
> +};
> +module_platform_driver(phy_meson_gxl_usb2_driver);
> +
> +MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
> +MODULE_DESCRIPTION("Meson GXL USB2 PHY driver");
> +MODULE_LICENSE("GPL");

GPL v2 to match with the file header.
> diff --git a/drivers/phy/phy-meson-gxl-usb3.c b/drivers/phy/phy-meson-gxl-usb3.c
> new file mode 100644
> index 0000000..90a4028
> --- /dev/null
> +++ b/drivers/phy/phy-meson-gxl-usb3.c
> @@ -0,0 +1,377 @@
> +/*
> + * Meson GXL USB3 PHY driver
> + *
> + * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/reset.h>
> +#include <linux/regmap.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/usb/of.h>
> +#include <linux/workqueue.h>
> +
> +#define USB_R0							0x00
> +	#define USB_R0_P30_FSEL_SHIFT				0
> +	#define USB_R0_P30_FSEL_MASK				GENMASK(5, 0)
> +	#define USB_R0_P30_PHY_RESET				BIT(6)
> +	#define USB_R0_P30_TEST_POWERDOWN_HSP			BIT(7)
> +	#define USB_R0_P30_TEST_POWERDOWN_SSP			BIT(8)
> +	#define USB_R0_P30_ACJT_LEVEL_SHIFT			9
> +	#define USB_R0_P30_ACJT_LEVEL_MASK			GENMASK(13, 9)
> +	#define USB_R0_P30_TX_BOOST_LEVEL_SHIFT			14
> +	#define USB_R0_P30_TX_BOOST_LEVEL_MASK			GENMASK(16, 14)
> +	#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_SHIFT		19
> +	#define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK		GENMASK(28, 19)
> +	#define USB_R0_U2D_SS_SCALEDOWN_MODE_SHIFT		29
> +	#define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK		GENMASK(30, 29)
> +	#define USB_R0_U2D_ACT					BIT(31)
> +
> +#define USB_R1							0x04
> +	#define USB_R1_U3H_BIGENDIAN_GS				BIT(0)
> +	#define USB_R1_U3H_PME_ENABLE				BIT(1)
> +	#define USB_R1_U3H_HUB_PORT_OVERCURRENT_SHIFT		2
> +	#define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK		GENMASK(6, 2)
> +	#define USB_R1_U3H_HUB_PORT_PERM_ATTACH_SHIFT		7
> +	#define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK		GENMASK(11, 7)
> +	#define USB_R1_U3H_HOST_U2_PORT_DISABLE_SHIFT		12
> +	#define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK		GENMASK(15, 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_SHIFT		19
> +	#define USB_R1_U3H_FLADJ_30MHZ_REG_MASK			GENMASK(24, 19)
> +	#define USB_R1_P30_PCS_TX_SWING_FULL_SHIFT		25
> +	#define USB_R1_P30_PCS_TX_SWING_FULL_MASK		GENMASK(31, 25)
> +
> +#define USB_R2							0x08
> +	#define USB_R2_P30_CR_DATA_IN_SHIFT			0
> +	#define USB_R2_P30_CR_DATA_IN_MASK			GENMASK(15, 0)
> +	#define USB_R2_P30_CR_READ				BIT(16)
> +	#define USB_R2_P30_CR_WRITE				BIT(17)
> +	#define USB_R2_P30_CR_CAP_ADDR				BIT(18)
> +	#define USB_R2_P30_CR_CAP_DATA				BIT(19)
> +	#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_SHIFT		20
> +	#define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK		GENMASK(25, 20)
> +	#define USB_R2_P30_PCS_TX_DEEMPH_6DB_SHIFT		26
> +	#define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK		GENMASK(31, 26)
> +
> +#define USB_R3							0x0c
> +	#define USB_R3_P30_SSC_ENABLE				BIT(0)
> +	#define USB_R3_P30_SSC_RANGE_SHIFT			1
> +	#define USB_R3_P30_SSC_RANGE_MASK			GENMASK(3, 1)
> +	#define USB_R3_P30_SSC_REF_CLK_SEL_SHIFT		4
> +	#define USB_R3_P30_SSC_REF_CLK_SEL_MASK			GENMASK(12, 4)
> +	#define USB_R3_P30_REF_SSP_EN				BIT(13)
> +	#define USB_R3_P30_LOS_BIAS_SHIFT			16
> +	#define USB_R3_P30_LOS_BIAS_MASK			GENMASK(18, 16)
> +	#define USB_R3_P30_LOS_LEVEL_SHIFT			19
> +	#define USB_R3_P30_LOS_LEVEL_MASK			GENMASK(23, 19)
> +	#define USB_R3_P30_MPLL_MULTIPLIER_SHIFT		24
> +	#define USB_R3_P30_MPLL_MULTIPLIER_MASK			GENMASK(30, 24)
> +
> +#define USB_R4							0x10
> +	#define USB_R4_P21_PORT_RESET_0				BIT(0)
> +	#define USB_R4_P21_SLEEP_M0				BIT(1)
> +	#define USB_R4_MEM_PD_SHIFT				2
> +	#define USB_R4_MEM_PD_MASK				GENMASK(3, 2)
> +	#define USB_R4_P21_ONLY					BIT(4)
> +
> +#define USB_R5							0x14
> +	#define USB_R5_ID_DIG_SYNC				BIT(0)
> +	#define USB_R5_ID_DIG_REG				BIT(1)
> +	#define USB_R5_ID_DIG_CFG_SHIFT				2
> +	#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_SHIFT				8
> +	#define USB_R5_ID_DIG_TH_MASK				GENMASK(15, 8)
> +	#define USB_R5_ID_DIG_CNT_SHIFT				16
> +	#define USB_R5_ID_DIG_CNT_MASK				GENMASK(23, 16)
> +
> +/* read-only register */
> +#define USB_R6							0x18
> +	#define USB_R6_P30_CR_DATA_OUT_SHIFT			0
> +	#define USB_R6_P30_CR_DATA_OUT_MASK			GENMASK(15, 0)
> +	#define USB_R6_P30_CR_ACK				BIT(16)
> +
> +#define RESET_COMPLETE_TIME				500
> +
> +struct phy_meson_gxl_usb3_priv {
> +	struct regmap		*regmap;
> +	struct delayed_work	otg_work;
> +	struct phy		*this_phy;
> +	int			num_usb2_phys;
> +	struct phy		**usb2_phys;
> +};
> +
> +static const struct regmap_config phy_meson_gxl_usb3_regmap_conf = {
> +	.reg_bits = 32,
> +	.val_bits = 32,
> +	.reg_stride = 4,
> +	.max_register = USB_R6,
> +};
> +
> +static int phy_meson_gxl_usb3_update_mode(struct phy *phy)
> +{
> +	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
> +	u32 val;
> +	enum phy_mode mode;
> +	int i, ret;
> +
> +	ret = regmap_read(priv->regmap, USB_R5, &val);
> +	if (ret)
> +		return ret;
> +
> +	if (val & USB_R5_ID_DIG_CURR) {
> +		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_R4, USB_R4_P21_SLEEP_M0,
> +				   USB_R4_P21_SLEEP_M0);
> +	} else {
> +		mode = PHY_MODE_USB_HOST;
> +
> +		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);
> +	}
> +
> +	/* inform the USB2 PHY that we have changed the mode */
> +	for (i = 0; i < priv->num_usb2_phys; i++) {
> +		ret = phy_set_mode(priv->usb2_phys[i], mode);

I'm finding it difficult to understand this. Why should the mode of one phy be
set from another phy? Maybe this part should be implemented using extcon?

> +		if (ret) {
> +			dev_err(&phy->dev,
> +				"Failed to update usb2-phy #%d mode to %d\n",
> +				i, mode);
> +			return ret;
> +		}
> +	}
> +
> +	return ret;
> +}
> +
> +static void phy_meson_gxl_usb3_work(struct work_struct *data)
> +{
> +	struct phy_meson_gxl_usb3_priv *priv =
> +		container_of(data, struct phy_meson_gxl_usb3_priv,
> +			     otg_work.work);
> +
> +	phy_meson_gxl_usb3_update_mode(priv->this_phy);
> +
> +	/* unmask IRQs which may have arrived in the meantime */
> +	regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_IRQ, 0);
> +}
> +
> +static int phy_meson_gxl_usb3_init(struct phy *phy)
> +{
> +	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
> +	int i, ret;
> +
> +	for (i = 0; i < priv->num_usb2_phys; i++) {
> +		ret = phy_init(priv->usb2_phys[i]);
> +		if (ret) {
> +			dev_err(&phy->dev,
> +				"Failed to initialize related usb2-phy #%d\n",
> +				i);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int phy_meson_gxl_usb3_exit(struct phy *phy)
> +{
> +	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
> +	int i, ret;
> +
> +	for (i = 0; i < priv->num_usb2_phys; i++) {
> +		ret = phy_exit(priv->usb2_phys[i]);
> +		if (ret) {
> +			dev_err(&phy->dev,
> +				"Failed to exit related usb2-phy #%d\n", i);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static int phy_meson_gxl_usb3_power_on(struct phy *phy)
> +{
> +	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
> +	int i, ret;
> +
> +	for (i = 0; i < priv->num_usb2_phys; i++) {
> +		ret = phy_power_on(priv->usb2_phys[i]);
> +		if (ret) {
> +			dev_err(&phy->dev,
> +				"Failed to power on related usb2-phy #%d\n",
> +				i);
> +			return ret;
> +		}
> +	}
> +
> +	regmap_update_bits(priv->regmap, USB_R1,
> +			   USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
> +			   0x20 << USB_R1_U3H_FLADJ_30MHZ_REG_SHIFT);
> +
> +	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,
> +			   0xff << USB_R5_ID_DIG_TH_SHIFT);
> +
> +	return phy_meson_gxl_usb3_update_mode(phy);
> +}
> +
> +static int phy_meson_gxl_usb3_power_off(struct phy *phy)
> +{
> +	struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
> +	int i, ret;
> +
> +	for (i = 0; i < priv->num_usb2_phys; i++) {
> +		ret = phy_power_off(priv->usb2_phys[i]);
> +		if (ret) {
> +			dev_err(&phy->dev,
> +				"Failed to power off related usb2-phy #%d\n",
> +				i);
> +			return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +static irqreturn_t phy_meson_gxl_usb3_irq(int irq, void *data)
> +{
> +	u32 val;
> +	struct phy_meson_gxl_usb3_priv *priv = data;
> +
> +	regmap_read(priv->regmap, USB_R5, &val);
> +	if (!(val & USB_R5_ID_DIG_IRQ)) {
> +		dev_err(&priv->this_phy->dev, "spurious interrupt\n");
> +		return IRQ_NONE;
> +	}
> +
> +	schedule_delayed_work(&priv->otg_work, msecs_to_jiffies(10));
> +
> +	/* acknowledge the IRQ */
> +	regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_IRQ, 0);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static const struct phy_ops phy_meson_gxl_usb3_ops = {
> +	.init		= phy_meson_gxl_usb3_init,
> +	.exit		= phy_meson_gxl_usb3_exit,
> +	.power_on	= phy_meson_gxl_usb3_power_on,
> +	.power_off	= phy_meson_gxl_usb3_power_off,
> +	.owner		= THIS_MODULE,
> +};
> +
> +static int phy_meson_gxl_usb3_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct device_node *np = dev->of_node;
> +	struct phy_meson_gxl_usb3_priv *priv;
> +	struct resource *res;
> +	struct phy *phy;
> +	struct phy_provider *phy_provider;
> +	void __iomem *base;
> +	int i, irq;
> +
> +	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_gxl_usb3_regmap_conf);
> +	if (IS_ERR(priv->regmap))
> +		return PTR_ERR(priv->regmap);
> +
> +	irq = platform_get_irq(pdev, 0);
> +	if (irq >= 0) {
> +		INIT_DELAYED_WORK(&priv->otg_work, phy_meson_gxl_usb3_work);
> +
> +		irq = devm_request_irq(dev, irq, phy_meson_gxl_usb3_irq,
> +				       IRQF_SHARED, dev_name(dev),
> +				       priv);
> +		if (irq < 0) {
> +			dev_err(dev, "could not register IRQ handler (%d)\n",
> +				irq);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	priv->num_usb2_phys = of_count_phandle_with_args(np, "phys",
> +							 "#phy-cells");
> +
> +	priv->usb2_phys = devm_kcalloc(dev, priv->num_usb2_phys,
> +				       sizeof(*priv->usb2_phys), GFP_KERNEL);
> +	if (!priv->usb2_phys)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < priv->num_usb2_phys; i++) {
> +		priv->usb2_phys[i] = devm_of_phy_get_by_index(dev, np, i);

I'm not sure if referencing usb2_phy from here is the right approach.

Thanks
Kishon

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

* Re: [PATCH 2/5] phy: meson: add USB2 and USB3 PHY support for Meson GXL
  2017-01-16  9:41       ` Kishon Vijay Abraham I
  (?)
@ 2017-01-16 12:07           ` Martin Blumenstingl
  -1 siblings, 0 replies; 39+ messages in thread
From: Martin Blumenstingl @ 2017-01-16 12:07 UTC (permalink / raw)
  To: Kishon Vijay Abraham I
  Cc: linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	khilman-rdvid1DuHRBWk0Htik3J/w, carlo-KA+7E9HrN00dnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	catalin.marinas-5wv7dgnIgG8, will.deacon-5wv7dgnIgG8,
	narmstrong-rdvid1DuHRBWk0Htik3J/w

Hi Kishon,

thank you for taking the time to review this!

On Mon, Jan 16, 2017 at 10:41 AM, Kishon Vijay Abraham I <kishon-l0cyMroinI0@public.gmane.org> wrote:
> Hi,
>
> On Saturday 26 November 2016 08:26 PM, Martin Blumenstingl wrote:
>> This adds two new USB PHY drivers found on Meson GXL and GXM SoCs.
>
> Please send them as separate drivers.
OK, I also just discovered that USB2 works even when not configuring
the USB3 PHY. That's another good reason for splitting these.

>> The registers for the USB2 PHY block handle a maximum of 4 ports (newer
>> SoCs may allow more ports, the driver handles this as long as the
>> register length is adjusted in the .dts). The PHY block theoretically
>> allows powering down each PHY port separately (by putting it into
>> "reset" state). Unfortunately this does not work (my board has 2 USB
>> ports, connected to port 1 and 2 of the dwc3's internal hub. When
>> leaving the third USB PHY disabled then the hub sees that a device is
>> plugged in, but it does not work: "usb usb1-port2: connect-debounce
>> failed").
>> The USB3 PHY will take care of enabling/disabling all available ports,
>> because the USB3 PHY also manages the mode of the USB2 PHYs.
>>
>> The USB3 PHY actually has three purposes:
>> - it provides the USB3 PHY
>> - it handles the OTG device/host mode detection interrupt
>> - it notifies the corresponding USB2 PHYs of the OTG mode changes
>> On GXL and GXM SoCs one references all available USB2 PHY ports in the
>> USB3 PHY because all are connected to the same USB controller (thus the
>> mode will always match). This behavior is configurable via devicetree,
>> by passing (or not passing) a list of other ("child") PHYs which should
>> be configured by the USB3 PHY.
>>
>> Unfortunately there are no datasheets available for any of these PHYs.
>> Both drivers were written by reading the reference drivers provided by
>> Amlogic and analyzing the registers on the kernel that was shipped with
>> my board.
>>
>> Signed-off-by: Martin Blumenstingl <martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>
>> ---
>>  drivers/phy/Kconfig              |  13 ++
>>  drivers/phy/Makefile             |   2 +
>>  drivers/phy/phy-meson-gxl-usb2.c | 374 ++++++++++++++++++++++++++++++++++++++
>>  drivers/phy/phy-meson-gxl-usb3.c | 377 +++++++++++++++++++++++++++++++++++++++
>>  4 files changed, 766 insertions(+)
>>  create mode 100644 drivers/phy/phy-meson-gxl-usb2.c
>>  create mode 100644 drivers/phy/phy-meson-gxl-usb3.c
>>
>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>> index 728e03f..ea74843 100644
>> --- a/drivers/phy/Kconfig
>> +++ b/drivers/phy/Kconfig
>> @@ -502,4 +502,17 @@ config PHY_MESON8B_USB2
>>         and GXBB SoCs.
>>         If unsure, say N.
>>
>> +config PHY_MESON_GXL_USB
>> +     tristate "Meson GXL USB2 and USB3 PHY drivers"
>> +     default ARCH_MESON
>> +     depends on OF && (ARCH_MESON || COMPILE_TEST)
>> +     depends on USB_SUPPORT
>> +     select USB_COMMON
>> +     select GENERIC_PHY
>> +     select REGMAP_MMIO
>> +     help
>> +       Enable this to support the Meson USB2 and USB3 PHYs found in
>> +       Meson GXL SoCs.
>> +       If unsure, say N.
>> +
>>  endmenu
>> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
>> index 0c7fdae..960a96e 100644
>> --- a/drivers/phy/Makefile
>> +++ b/drivers/phy/Makefile
>> @@ -61,3 +61,5 @@ obj-$(CONFIG_PHY_CYGNUS_PCIE)               += phy-bcm-cygnus-pcie.o
>>  obj-$(CONFIG_ARCH_TEGRA) += tegra/
>>  obj-$(CONFIG_PHY_NS2_PCIE)           += phy-bcm-ns2-pcie.o
>>  obj-$(CONFIG_PHY_MESON8B_USB2)               += phy-meson8b-usb2.o
>> +obj-$(CONFIG_PHY_MESON_GXL_USB)              += phy-meson-gxl-usb2.o
>> +obj-$(CONFIG_PHY_MESON_GXL_USB)              += phy-meson-gxl-usb3.o
>> diff --git a/drivers/phy/phy-meson-gxl-usb2.c b/drivers/phy/phy-meson-gxl-usb2.c
>> new file mode 100644
>> index 0000000..c081ce3
[snip]
>> +static struct phy *phy_meson_gxl_usb2_of_xlate(struct device *dev,
>> +                                            struct of_phandle_args *args)
>> +{
>> +     struct phy_meson_gxl_usb2_drv *priv = dev_get_drvdata(dev);
>> +     int port;
>> +
>> +     if (args->args_count != 1) {
>> +             dev_err(dev, "Invalid number of cells in 'phy' property\n");
>> +             return ERR_PTR(-ENODEV);
>> +     }
>> +
>> +     port = args->args[0];
>> +     if (WARN_ON(port >= priv->num_ports))
>> +             return ERR_PTR(-ENODEV);
>> +
>> +     return priv->ports[port];
>> +}
>
> Please model every port as a sub-node and get rid of custom xlate implementation.
already done in my local tree.
I am in contact with the USB developers to get the USB2 PHYs working
(as all of them have to be turned on when powering up dwc3). The
result of how the dwc3 node with the PHY references may look like can
be seen here: [0]

>> +MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>");
>> +MODULE_DESCRIPTION("Meson GXL USB2 PHY driver");
>> +MODULE_LICENSE("GPL");
>
> GPL v2 to match with the file header.
good catch, thanks

>> diff --git a/drivers/phy/phy-meson-gxl-usb3.c b/drivers/phy/phy-meson-gxl-usb3.c
>> new file mode 100644
>> index 0000000..90a4028
>> --- /dev/null
>> +++ b/drivers/phy/phy-meson-gxl-usb3.c
>> @@ -0,0 +1,377 @@
>> +/*
>> + * Meson GXL USB3 PHY driver
>> + *
>> + * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/of_device.h>
>> +#include <linux/reset.h>
>> +#include <linux/regmap.h>
>> +#include <linux/phy/phy.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/usb/of.h>
>> +#include <linux/workqueue.h>
>> +
>> +#define USB_R0                                                       0x00
>> +     #define USB_R0_P30_FSEL_SHIFT                           0
>> +     #define USB_R0_P30_FSEL_MASK                            GENMASK(5, 0)
>> +     #define USB_R0_P30_PHY_RESET                            BIT(6)
>> +     #define USB_R0_P30_TEST_POWERDOWN_HSP                   BIT(7)
>> +     #define USB_R0_P30_TEST_POWERDOWN_SSP                   BIT(8)
>> +     #define USB_R0_P30_ACJT_LEVEL_SHIFT                     9
>> +     #define USB_R0_P30_ACJT_LEVEL_MASK                      GENMASK(13, 9)
>> +     #define USB_R0_P30_TX_BOOST_LEVEL_SHIFT                 14
>> +     #define USB_R0_P30_TX_BOOST_LEVEL_MASK                  GENMASK(16, 14)
>> +     #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_SHIFT            19
>> +     #define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK             GENMASK(28, 19)
>> +     #define USB_R0_U2D_SS_SCALEDOWN_MODE_SHIFT              29
>> +     #define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK               GENMASK(30, 29)
>> +     #define USB_R0_U2D_ACT                                  BIT(31)
>> +
>> +#define USB_R1                                                       0x04
>> +     #define USB_R1_U3H_BIGENDIAN_GS                         BIT(0)
>> +     #define USB_R1_U3H_PME_ENABLE                           BIT(1)
>> +     #define USB_R1_U3H_HUB_PORT_OVERCURRENT_SHIFT           2
>> +     #define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK            GENMASK(6, 2)
>> +     #define USB_R1_U3H_HUB_PORT_PERM_ATTACH_SHIFT           7
>> +     #define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK            GENMASK(11, 7)
>> +     #define USB_R1_U3H_HOST_U2_PORT_DISABLE_SHIFT           12
>> +     #define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK            GENMASK(15, 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_SHIFT                19
>> +     #define USB_R1_U3H_FLADJ_30MHZ_REG_MASK                 GENMASK(24, 19)
>> +     #define USB_R1_P30_PCS_TX_SWING_FULL_SHIFT              25
>> +     #define USB_R1_P30_PCS_TX_SWING_FULL_MASK               GENMASK(31, 25)
>> +
>> +#define USB_R2                                                       0x08
>> +     #define USB_R2_P30_CR_DATA_IN_SHIFT                     0
>> +     #define USB_R2_P30_CR_DATA_IN_MASK                      GENMASK(15, 0)
>> +     #define USB_R2_P30_CR_READ                              BIT(16)
>> +     #define USB_R2_P30_CR_WRITE                             BIT(17)
>> +     #define USB_R2_P30_CR_CAP_ADDR                          BIT(18)
>> +     #define USB_R2_P30_CR_CAP_DATA                          BIT(19)
>> +     #define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_SHIFT            20
>> +     #define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK             GENMASK(25, 20)
>> +     #define USB_R2_P30_PCS_TX_DEEMPH_6DB_SHIFT              26
>> +     #define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK               GENMASK(31, 26)
>> +
>> +#define USB_R3                                                       0x0c
>> +     #define USB_R3_P30_SSC_ENABLE                           BIT(0)
>> +     #define USB_R3_P30_SSC_RANGE_SHIFT                      1
>> +     #define USB_R3_P30_SSC_RANGE_MASK                       GENMASK(3, 1)
>> +     #define USB_R3_P30_SSC_REF_CLK_SEL_SHIFT                4
>> +     #define USB_R3_P30_SSC_REF_CLK_SEL_MASK                 GENMASK(12, 4)
>> +     #define USB_R3_P30_REF_SSP_EN                           BIT(13)
>> +     #define USB_R3_P30_LOS_BIAS_SHIFT                       16
>> +     #define USB_R3_P30_LOS_BIAS_MASK                        GENMASK(18, 16)
>> +     #define USB_R3_P30_LOS_LEVEL_SHIFT                      19
>> +     #define USB_R3_P30_LOS_LEVEL_MASK                       GENMASK(23, 19)
>> +     #define USB_R3_P30_MPLL_MULTIPLIER_SHIFT                24
>> +     #define USB_R3_P30_MPLL_MULTIPLIER_MASK                 GENMASK(30, 24)
>> +
>> +#define USB_R4                                                       0x10
>> +     #define USB_R4_P21_PORT_RESET_0                         BIT(0)
>> +     #define USB_R4_P21_SLEEP_M0                             BIT(1)
>> +     #define USB_R4_MEM_PD_SHIFT                             2
>> +     #define USB_R4_MEM_PD_MASK                              GENMASK(3, 2)
>> +     #define USB_R4_P21_ONLY                                 BIT(4)
>> +
>> +#define USB_R5                                                       0x14
>> +     #define USB_R5_ID_DIG_SYNC                              BIT(0)
>> +     #define USB_R5_ID_DIG_REG                               BIT(1)
>> +     #define USB_R5_ID_DIG_CFG_SHIFT                         2
>> +     #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_SHIFT                          8
>> +     #define USB_R5_ID_DIG_TH_MASK                           GENMASK(15, 8)
>> +     #define USB_R5_ID_DIG_CNT_SHIFT                         16
>> +     #define USB_R5_ID_DIG_CNT_MASK                          GENMASK(23, 16)
>> +
>> +/* read-only register */
>> +#define USB_R6                                                       0x18
>> +     #define USB_R6_P30_CR_DATA_OUT_SHIFT                    0
>> +     #define USB_R6_P30_CR_DATA_OUT_MASK                     GENMASK(15, 0)
>> +     #define USB_R6_P30_CR_ACK                               BIT(16)
>> +
>> +#define RESET_COMPLETE_TIME                          500
>> +
>> +struct phy_meson_gxl_usb3_priv {
>> +     struct regmap           *regmap;
>> +     struct delayed_work     otg_work;
>> +     struct phy              *this_phy;
>> +     int                     num_usb2_phys;
>> +     struct phy              **usb2_phys;
>> +};
>> +
>> +static const struct regmap_config phy_meson_gxl_usb3_regmap_conf = {
>> +     .reg_bits = 32,
>> +     .val_bits = 32,
>> +     .reg_stride = 4,
>> +     .max_register = USB_R6,
>> +};
>> +
>> +static int phy_meson_gxl_usb3_update_mode(struct phy *phy)
>> +{
>> +     struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
>> +     u32 val;
>> +     enum phy_mode mode;
>> +     int i, ret;
>> +
>> +     ret = regmap_read(priv->regmap, USB_R5, &val);
>> +     if (ret)
>> +             return ret;
>> +
>> +     if (val & USB_R5_ID_DIG_CURR) {
>> +             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_R4, USB_R4_P21_SLEEP_M0,
>> +                                USB_R4_P21_SLEEP_M0);
>> +     } else {
>> +             mode = PHY_MODE_USB_HOST;
>> +
>> +             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);
>> +     }
>> +
>> +     /* inform the USB2 PHY that we have changed the mode */
>> +     for (i = 0; i < priv->num_usb2_phys; i++) {
>> +             ret = phy_set_mode(priv->usb2_phys[i], mode);
>
> I'm finding it difficult to understand this. Why should the mode of one phy be
> set from another phy? Maybe this part should be implemented using extcon?
sounds like a good idea, I will postpone the USB3 PHY driver though
since it's currently only used for "mode switching" (between USB
host/device) and the whole thing requires MUCH more work (as a dwc2
controller is used for device mode, while a dwc3 controller is used
for host mode).

>> +             if (ret) {
>> +                     dev_err(&phy->dev,
>> +                             "Failed to update usb2-phy #%d mode to %d\n",
>> +                             i, mode);
>> +                     return ret;
>> +             }
>> +     }
>> +
>> +     return ret;
>> +}
>> +
>> +static void phy_meson_gxl_usb3_work(struct work_struct *data)
>> +{
>> +     struct phy_meson_gxl_usb3_priv *priv =
>> +             container_of(data, struct phy_meson_gxl_usb3_priv,
>> +                          otg_work.work);
>> +
>> +     phy_meson_gxl_usb3_update_mode(priv->this_phy);
>> +
>> +     /* unmask IRQs which may have arrived in the meantime */
>> +     regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_IRQ, 0);
>> +}
>> +
>> +static int phy_meson_gxl_usb3_init(struct phy *phy)
>> +{
>> +     struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
>> +     int i, ret;
>> +
>> +     for (i = 0; i < priv->num_usb2_phys; i++) {
>> +             ret = phy_init(priv->usb2_phys[i]);
>> +             if (ret) {
>> +                     dev_err(&phy->dev,
>> +                             "Failed to initialize related usb2-phy #%d\n",
>> +                             i);
>> +                     return ret;
>> +             }
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static int phy_meson_gxl_usb3_exit(struct phy *phy)
>> +{
>> +     struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
>> +     int i, ret;
>> +
>> +     for (i = 0; i < priv->num_usb2_phys; i++) {
>> +             ret = phy_exit(priv->usb2_phys[i]);
>> +             if (ret) {
>> +                     dev_err(&phy->dev,
>> +                             "Failed to exit related usb2-phy #%d\n", i);
>> +                     return ret;
>> +             }
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static int phy_meson_gxl_usb3_power_on(struct phy *phy)
>> +{
>> +     struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
>> +     int i, ret;
>> +
>> +     for (i = 0; i < priv->num_usb2_phys; i++) {
>> +             ret = phy_power_on(priv->usb2_phys[i]);
>> +             if (ret) {
>> +                     dev_err(&phy->dev,
>> +                             "Failed to power on related usb2-phy #%d\n",
>> +                             i);
>> +                     return ret;
>> +             }
>> +     }
>> +
>> +     regmap_update_bits(priv->regmap, USB_R1,
>> +                        USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
>> +                        0x20 << USB_R1_U3H_FLADJ_30MHZ_REG_SHIFT);
>> +
>> +     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,
>> +                        0xff << USB_R5_ID_DIG_TH_SHIFT);
>> +
>> +     return phy_meson_gxl_usb3_update_mode(phy);
>> +}
>> +
>> +static int phy_meson_gxl_usb3_power_off(struct phy *phy)
>> +{
>> +     struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
>> +     int i, ret;
>> +
>> +     for (i = 0; i < priv->num_usb2_phys; i++) {
>> +             ret = phy_power_off(priv->usb2_phys[i]);
>> +             if (ret) {
>> +                     dev_err(&phy->dev,
>> +                             "Failed to power off related usb2-phy #%d\n",
>> +                             i);
>> +                     return ret;
>> +             }
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static irqreturn_t phy_meson_gxl_usb3_irq(int irq, void *data)
>> +{
>> +     u32 val;
>> +     struct phy_meson_gxl_usb3_priv *priv = data;
>> +
>> +     regmap_read(priv->regmap, USB_R5, &val);
>> +     if (!(val & USB_R5_ID_DIG_IRQ)) {
>> +             dev_err(&priv->this_phy->dev, "spurious interrupt\n");
>> +             return IRQ_NONE;
>> +     }
>> +
>> +     schedule_delayed_work(&priv->otg_work, msecs_to_jiffies(10));
>> +
>> +     /* acknowledge the IRQ */
>> +     regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_IRQ, 0);
>> +
>> +     return IRQ_HANDLED;
>> +}
>> +
>> +static const struct phy_ops phy_meson_gxl_usb3_ops = {
>> +     .init           = phy_meson_gxl_usb3_init,
>> +     .exit           = phy_meson_gxl_usb3_exit,
>> +     .power_on       = phy_meson_gxl_usb3_power_on,
>> +     .power_off      = phy_meson_gxl_usb3_power_off,
>> +     .owner          = THIS_MODULE,
>> +};
>> +
>> +static int phy_meson_gxl_usb3_probe(struct platform_device *pdev)
>> +{
>> +     struct device *dev = &pdev->dev;
>> +     struct device_node *np = dev->of_node;
>> +     struct phy_meson_gxl_usb3_priv *priv;
>> +     struct resource *res;
>> +     struct phy *phy;
>> +     struct phy_provider *phy_provider;
>> +     void __iomem *base;
>> +     int i, irq;
>> +
>> +     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_gxl_usb3_regmap_conf);
>> +     if (IS_ERR(priv->regmap))
>> +             return PTR_ERR(priv->regmap);
>> +
>> +     irq = platform_get_irq(pdev, 0);
>> +     if (irq >= 0) {
>> +             INIT_DELAYED_WORK(&priv->otg_work, phy_meson_gxl_usb3_work);
>> +
>> +             irq = devm_request_irq(dev, irq, phy_meson_gxl_usb3_irq,
>> +                                    IRQF_SHARED, dev_name(dev),
>> +                                    priv);
>> +             if (irq < 0) {
>> +                     dev_err(dev, "could not register IRQ handler (%d)\n",
>> +                             irq);
>> +                     return -EINVAL;
>> +             }
>> +     }
>> +
>> +     priv->num_usb2_phys = of_count_phandle_with_args(np, "phys",
>> +                                                      "#phy-cells");
>> +
>> +     priv->usb2_phys = devm_kcalloc(dev, priv->num_usb2_phys,
>> +                                    sizeof(*priv->usb2_phys), GFP_KERNEL);
>> +     if (!priv->usb2_phys)
>> +             return -ENOMEM;
>> +
>> +     for (i = 0; i < priv->num_usb2_phys; i++) {
>> +             priv->usb2_phys[i] = devm_of_phy_get_by_index(dev, np, i);
>
> I'm not sure if referencing usb2_phy from here is the right approach.
that would probably be gone with the USB patches from [0] and with the
switch to extcon

I will send an updated version once we know how to handle powering up
the PHY in xhci-plat.c (which is the series from [0]).


Regards,
Martin


[0] http://marc.info/?l=linux-usb&m=148414866203601&w=2
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 2/5] phy: meson: add USB2 and USB3 PHY support for Meson GXL
@ 2017-01-16 12:07           ` Martin Blumenstingl
  0 siblings, 0 replies; 39+ messages in thread
From: Martin Blumenstingl @ 2017-01-16 12:07 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Kishon,

thank you for taking the time to review this!

On Mon, Jan 16, 2017 at 10:41 AM, Kishon Vijay Abraham I <kishon@ti.com> wrote:
> Hi,
>
> On Saturday 26 November 2016 08:26 PM, Martin Blumenstingl wrote:
>> This adds two new USB PHY drivers found on Meson GXL and GXM SoCs.
>
> Please send them as separate drivers.
OK, I also just discovered that USB2 works even when not configuring
the USB3 PHY. That's another good reason for splitting these.

>> The registers for the USB2 PHY block handle a maximum of 4 ports (newer
>> SoCs may allow more ports, the driver handles this as long as the
>> register length is adjusted in the .dts). The PHY block theoretically
>> allows powering down each PHY port separately (by putting it into
>> "reset" state). Unfortunately this does not work (my board has 2 USB
>> ports, connected to port 1 and 2 of the dwc3's internal hub. When
>> leaving the third USB PHY disabled then the hub sees that a device is
>> plugged in, but it does not work: "usb usb1-port2: connect-debounce
>> failed").
>> The USB3 PHY will take care of enabling/disabling all available ports,
>> because the USB3 PHY also manages the mode of the USB2 PHYs.
>>
>> The USB3 PHY actually has three purposes:
>> - it provides the USB3 PHY
>> - it handles the OTG device/host mode detection interrupt
>> - it notifies the corresponding USB2 PHYs of the OTG mode changes
>> On GXL and GXM SoCs one references all available USB2 PHY ports in the
>> USB3 PHY because all are connected to the same USB controller (thus the
>> mode will always match). This behavior is configurable via devicetree,
>> by passing (or not passing) a list of other ("child") PHYs which should
>> be configured by the USB3 PHY.
>>
>> Unfortunately there are no datasheets available for any of these PHYs.
>> Both drivers were written by reading the reference drivers provided by
>> Amlogic and analyzing the registers on the kernel that was shipped with
>> my board.
>>
>> Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
>> ---
>>  drivers/phy/Kconfig              |  13 ++
>>  drivers/phy/Makefile             |   2 +
>>  drivers/phy/phy-meson-gxl-usb2.c | 374 ++++++++++++++++++++++++++++++++++++++
>>  drivers/phy/phy-meson-gxl-usb3.c | 377 +++++++++++++++++++++++++++++++++++++++
>>  4 files changed, 766 insertions(+)
>>  create mode 100644 drivers/phy/phy-meson-gxl-usb2.c
>>  create mode 100644 drivers/phy/phy-meson-gxl-usb3.c
>>
>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>> index 728e03f..ea74843 100644
>> --- a/drivers/phy/Kconfig
>> +++ b/drivers/phy/Kconfig
>> @@ -502,4 +502,17 @@ config PHY_MESON8B_USB2
>>         and GXBB SoCs.
>>         If unsure, say N.
>>
>> +config PHY_MESON_GXL_USB
>> +     tristate "Meson GXL USB2 and USB3 PHY drivers"
>> +     default ARCH_MESON
>> +     depends on OF && (ARCH_MESON || COMPILE_TEST)
>> +     depends on USB_SUPPORT
>> +     select USB_COMMON
>> +     select GENERIC_PHY
>> +     select REGMAP_MMIO
>> +     help
>> +       Enable this to support the Meson USB2 and USB3 PHYs found in
>> +       Meson GXL SoCs.
>> +       If unsure, say N.
>> +
>>  endmenu
>> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
>> index 0c7fdae..960a96e 100644
>> --- a/drivers/phy/Makefile
>> +++ b/drivers/phy/Makefile
>> @@ -61,3 +61,5 @@ obj-$(CONFIG_PHY_CYGNUS_PCIE)               += phy-bcm-cygnus-pcie.o
>>  obj-$(CONFIG_ARCH_TEGRA) += tegra/
>>  obj-$(CONFIG_PHY_NS2_PCIE)           += phy-bcm-ns2-pcie.o
>>  obj-$(CONFIG_PHY_MESON8B_USB2)               += phy-meson8b-usb2.o
>> +obj-$(CONFIG_PHY_MESON_GXL_USB)              += phy-meson-gxl-usb2.o
>> +obj-$(CONFIG_PHY_MESON_GXL_USB)              += phy-meson-gxl-usb3.o
>> diff --git a/drivers/phy/phy-meson-gxl-usb2.c b/drivers/phy/phy-meson-gxl-usb2.c
>> new file mode 100644
>> index 0000000..c081ce3
[snip]
>> +static struct phy *phy_meson_gxl_usb2_of_xlate(struct device *dev,
>> +                                            struct of_phandle_args *args)
>> +{
>> +     struct phy_meson_gxl_usb2_drv *priv = dev_get_drvdata(dev);
>> +     int port;
>> +
>> +     if (args->args_count != 1) {
>> +             dev_err(dev, "Invalid number of cells in 'phy' property\n");
>> +             return ERR_PTR(-ENODEV);
>> +     }
>> +
>> +     port = args->args[0];
>> +     if (WARN_ON(port >= priv->num_ports))
>> +             return ERR_PTR(-ENODEV);
>> +
>> +     return priv->ports[port];
>> +}
>
> Please model every port as a sub-node and get rid of custom xlate implementation.
already done in my local tree.
I am in contact with the USB developers to get the USB2 PHYs working
(as all of them have to be turned on when powering up dwc3). The
result of how the dwc3 node with the PHY references may look like can
be seen here: [0]

>> +MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
>> +MODULE_DESCRIPTION("Meson GXL USB2 PHY driver");
>> +MODULE_LICENSE("GPL");
>
> GPL v2 to match with the file header.
good catch, thanks

>> diff --git a/drivers/phy/phy-meson-gxl-usb3.c b/drivers/phy/phy-meson-gxl-usb3.c
>> new file mode 100644
>> index 0000000..90a4028
>> --- /dev/null
>> +++ b/drivers/phy/phy-meson-gxl-usb3.c
>> @@ -0,0 +1,377 @@
>> +/*
>> + * Meson GXL USB3 PHY driver
>> + *
>> + * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/of_device.h>
>> +#include <linux/reset.h>
>> +#include <linux/regmap.h>
>> +#include <linux/phy/phy.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/usb/of.h>
>> +#include <linux/workqueue.h>
>> +
>> +#define USB_R0                                                       0x00
>> +     #define USB_R0_P30_FSEL_SHIFT                           0
>> +     #define USB_R0_P30_FSEL_MASK                            GENMASK(5, 0)
>> +     #define USB_R0_P30_PHY_RESET                            BIT(6)
>> +     #define USB_R0_P30_TEST_POWERDOWN_HSP                   BIT(7)
>> +     #define USB_R0_P30_TEST_POWERDOWN_SSP                   BIT(8)
>> +     #define USB_R0_P30_ACJT_LEVEL_SHIFT                     9
>> +     #define USB_R0_P30_ACJT_LEVEL_MASK                      GENMASK(13, 9)
>> +     #define USB_R0_P30_TX_BOOST_LEVEL_SHIFT                 14
>> +     #define USB_R0_P30_TX_BOOST_LEVEL_MASK                  GENMASK(16, 14)
>> +     #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_SHIFT            19
>> +     #define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK             GENMASK(28, 19)
>> +     #define USB_R0_U2D_SS_SCALEDOWN_MODE_SHIFT              29
>> +     #define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK               GENMASK(30, 29)
>> +     #define USB_R0_U2D_ACT                                  BIT(31)
>> +
>> +#define USB_R1                                                       0x04
>> +     #define USB_R1_U3H_BIGENDIAN_GS                         BIT(0)
>> +     #define USB_R1_U3H_PME_ENABLE                           BIT(1)
>> +     #define USB_R1_U3H_HUB_PORT_OVERCURRENT_SHIFT           2
>> +     #define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK            GENMASK(6, 2)
>> +     #define USB_R1_U3H_HUB_PORT_PERM_ATTACH_SHIFT           7
>> +     #define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK            GENMASK(11, 7)
>> +     #define USB_R1_U3H_HOST_U2_PORT_DISABLE_SHIFT           12
>> +     #define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK            GENMASK(15, 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_SHIFT                19
>> +     #define USB_R1_U3H_FLADJ_30MHZ_REG_MASK                 GENMASK(24, 19)
>> +     #define USB_R1_P30_PCS_TX_SWING_FULL_SHIFT              25
>> +     #define USB_R1_P30_PCS_TX_SWING_FULL_MASK               GENMASK(31, 25)
>> +
>> +#define USB_R2                                                       0x08
>> +     #define USB_R2_P30_CR_DATA_IN_SHIFT                     0
>> +     #define USB_R2_P30_CR_DATA_IN_MASK                      GENMASK(15, 0)
>> +     #define USB_R2_P30_CR_READ                              BIT(16)
>> +     #define USB_R2_P30_CR_WRITE                             BIT(17)
>> +     #define USB_R2_P30_CR_CAP_ADDR                          BIT(18)
>> +     #define USB_R2_P30_CR_CAP_DATA                          BIT(19)
>> +     #define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_SHIFT            20
>> +     #define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK             GENMASK(25, 20)
>> +     #define USB_R2_P30_PCS_TX_DEEMPH_6DB_SHIFT              26
>> +     #define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK               GENMASK(31, 26)
>> +
>> +#define USB_R3                                                       0x0c
>> +     #define USB_R3_P30_SSC_ENABLE                           BIT(0)
>> +     #define USB_R3_P30_SSC_RANGE_SHIFT                      1
>> +     #define USB_R3_P30_SSC_RANGE_MASK                       GENMASK(3, 1)
>> +     #define USB_R3_P30_SSC_REF_CLK_SEL_SHIFT                4
>> +     #define USB_R3_P30_SSC_REF_CLK_SEL_MASK                 GENMASK(12, 4)
>> +     #define USB_R3_P30_REF_SSP_EN                           BIT(13)
>> +     #define USB_R3_P30_LOS_BIAS_SHIFT                       16
>> +     #define USB_R3_P30_LOS_BIAS_MASK                        GENMASK(18, 16)
>> +     #define USB_R3_P30_LOS_LEVEL_SHIFT                      19
>> +     #define USB_R3_P30_LOS_LEVEL_MASK                       GENMASK(23, 19)
>> +     #define USB_R3_P30_MPLL_MULTIPLIER_SHIFT                24
>> +     #define USB_R3_P30_MPLL_MULTIPLIER_MASK                 GENMASK(30, 24)
>> +
>> +#define USB_R4                                                       0x10
>> +     #define USB_R4_P21_PORT_RESET_0                         BIT(0)
>> +     #define USB_R4_P21_SLEEP_M0                             BIT(1)
>> +     #define USB_R4_MEM_PD_SHIFT                             2
>> +     #define USB_R4_MEM_PD_MASK                              GENMASK(3, 2)
>> +     #define USB_R4_P21_ONLY                                 BIT(4)
>> +
>> +#define USB_R5                                                       0x14
>> +     #define USB_R5_ID_DIG_SYNC                              BIT(0)
>> +     #define USB_R5_ID_DIG_REG                               BIT(1)
>> +     #define USB_R5_ID_DIG_CFG_SHIFT                         2
>> +     #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_SHIFT                          8
>> +     #define USB_R5_ID_DIG_TH_MASK                           GENMASK(15, 8)
>> +     #define USB_R5_ID_DIG_CNT_SHIFT                         16
>> +     #define USB_R5_ID_DIG_CNT_MASK                          GENMASK(23, 16)
>> +
>> +/* read-only register */
>> +#define USB_R6                                                       0x18
>> +     #define USB_R6_P30_CR_DATA_OUT_SHIFT                    0
>> +     #define USB_R6_P30_CR_DATA_OUT_MASK                     GENMASK(15, 0)
>> +     #define USB_R6_P30_CR_ACK                               BIT(16)
>> +
>> +#define RESET_COMPLETE_TIME                          500
>> +
>> +struct phy_meson_gxl_usb3_priv {
>> +     struct regmap           *regmap;
>> +     struct delayed_work     otg_work;
>> +     struct phy              *this_phy;
>> +     int                     num_usb2_phys;
>> +     struct phy              **usb2_phys;
>> +};
>> +
>> +static const struct regmap_config phy_meson_gxl_usb3_regmap_conf = {
>> +     .reg_bits = 32,
>> +     .val_bits = 32,
>> +     .reg_stride = 4,
>> +     .max_register = USB_R6,
>> +};
>> +
>> +static int phy_meson_gxl_usb3_update_mode(struct phy *phy)
>> +{
>> +     struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
>> +     u32 val;
>> +     enum phy_mode mode;
>> +     int i, ret;
>> +
>> +     ret = regmap_read(priv->regmap, USB_R5, &val);
>> +     if (ret)
>> +             return ret;
>> +
>> +     if (val & USB_R5_ID_DIG_CURR) {
>> +             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_R4, USB_R4_P21_SLEEP_M0,
>> +                                USB_R4_P21_SLEEP_M0);
>> +     } else {
>> +             mode = PHY_MODE_USB_HOST;
>> +
>> +             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);
>> +     }
>> +
>> +     /* inform the USB2 PHY that we have changed the mode */
>> +     for (i = 0; i < priv->num_usb2_phys; i++) {
>> +             ret = phy_set_mode(priv->usb2_phys[i], mode);
>
> I'm finding it difficult to understand this. Why should the mode of one phy be
> set from another phy? Maybe this part should be implemented using extcon?
sounds like a good idea, I will postpone the USB3 PHY driver though
since it's currently only used for "mode switching" (between USB
host/device) and the whole thing requires MUCH more work (as a dwc2
controller is used for device mode, while a dwc3 controller is used
for host mode).

>> +             if (ret) {
>> +                     dev_err(&phy->dev,
>> +                             "Failed to update usb2-phy #%d mode to %d\n",
>> +                             i, mode);
>> +                     return ret;
>> +             }
>> +     }
>> +
>> +     return ret;
>> +}
>> +
>> +static void phy_meson_gxl_usb3_work(struct work_struct *data)
>> +{
>> +     struct phy_meson_gxl_usb3_priv *priv =
>> +             container_of(data, struct phy_meson_gxl_usb3_priv,
>> +                          otg_work.work);
>> +
>> +     phy_meson_gxl_usb3_update_mode(priv->this_phy);
>> +
>> +     /* unmask IRQs which may have arrived in the meantime */
>> +     regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_IRQ, 0);
>> +}
>> +
>> +static int phy_meson_gxl_usb3_init(struct phy *phy)
>> +{
>> +     struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
>> +     int i, ret;
>> +
>> +     for (i = 0; i < priv->num_usb2_phys; i++) {
>> +             ret = phy_init(priv->usb2_phys[i]);
>> +             if (ret) {
>> +                     dev_err(&phy->dev,
>> +                             "Failed to initialize related usb2-phy #%d\n",
>> +                             i);
>> +                     return ret;
>> +             }
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static int phy_meson_gxl_usb3_exit(struct phy *phy)
>> +{
>> +     struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
>> +     int i, ret;
>> +
>> +     for (i = 0; i < priv->num_usb2_phys; i++) {
>> +             ret = phy_exit(priv->usb2_phys[i]);
>> +             if (ret) {
>> +                     dev_err(&phy->dev,
>> +                             "Failed to exit related usb2-phy #%d\n", i);
>> +                     return ret;
>> +             }
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static int phy_meson_gxl_usb3_power_on(struct phy *phy)
>> +{
>> +     struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
>> +     int i, ret;
>> +
>> +     for (i = 0; i < priv->num_usb2_phys; i++) {
>> +             ret = phy_power_on(priv->usb2_phys[i]);
>> +             if (ret) {
>> +                     dev_err(&phy->dev,
>> +                             "Failed to power on related usb2-phy #%d\n",
>> +                             i);
>> +                     return ret;
>> +             }
>> +     }
>> +
>> +     regmap_update_bits(priv->regmap, USB_R1,
>> +                        USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
>> +                        0x20 << USB_R1_U3H_FLADJ_30MHZ_REG_SHIFT);
>> +
>> +     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,
>> +                        0xff << USB_R5_ID_DIG_TH_SHIFT);
>> +
>> +     return phy_meson_gxl_usb3_update_mode(phy);
>> +}
>> +
>> +static int phy_meson_gxl_usb3_power_off(struct phy *phy)
>> +{
>> +     struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
>> +     int i, ret;
>> +
>> +     for (i = 0; i < priv->num_usb2_phys; i++) {
>> +             ret = phy_power_off(priv->usb2_phys[i]);
>> +             if (ret) {
>> +                     dev_err(&phy->dev,
>> +                             "Failed to power off related usb2-phy #%d\n",
>> +                             i);
>> +                     return ret;
>> +             }
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static irqreturn_t phy_meson_gxl_usb3_irq(int irq, void *data)
>> +{
>> +     u32 val;
>> +     struct phy_meson_gxl_usb3_priv *priv = data;
>> +
>> +     regmap_read(priv->regmap, USB_R5, &val);
>> +     if (!(val & USB_R5_ID_DIG_IRQ)) {
>> +             dev_err(&priv->this_phy->dev, "spurious interrupt\n");
>> +             return IRQ_NONE;
>> +     }
>> +
>> +     schedule_delayed_work(&priv->otg_work, msecs_to_jiffies(10));
>> +
>> +     /* acknowledge the IRQ */
>> +     regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_IRQ, 0);
>> +
>> +     return IRQ_HANDLED;
>> +}
>> +
>> +static const struct phy_ops phy_meson_gxl_usb3_ops = {
>> +     .init           = phy_meson_gxl_usb3_init,
>> +     .exit           = phy_meson_gxl_usb3_exit,
>> +     .power_on       = phy_meson_gxl_usb3_power_on,
>> +     .power_off      = phy_meson_gxl_usb3_power_off,
>> +     .owner          = THIS_MODULE,
>> +};
>> +
>> +static int phy_meson_gxl_usb3_probe(struct platform_device *pdev)
>> +{
>> +     struct device *dev = &pdev->dev;
>> +     struct device_node *np = dev->of_node;
>> +     struct phy_meson_gxl_usb3_priv *priv;
>> +     struct resource *res;
>> +     struct phy *phy;
>> +     struct phy_provider *phy_provider;
>> +     void __iomem *base;
>> +     int i, irq;
>> +
>> +     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_gxl_usb3_regmap_conf);
>> +     if (IS_ERR(priv->regmap))
>> +             return PTR_ERR(priv->regmap);
>> +
>> +     irq = platform_get_irq(pdev, 0);
>> +     if (irq >= 0) {
>> +             INIT_DELAYED_WORK(&priv->otg_work, phy_meson_gxl_usb3_work);
>> +
>> +             irq = devm_request_irq(dev, irq, phy_meson_gxl_usb3_irq,
>> +                                    IRQF_SHARED, dev_name(dev),
>> +                                    priv);
>> +             if (irq < 0) {
>> +                     dev_err(dev, "could not register IRQ handler (%d)\n",
>> +                             irq);
>> +                     return -EINVAL;
>> +             }
>> +     }
>> +
>> +     priv->num_usb2_phys = of_count_phandle_with_args(np, "phys",
>> +                                                      "#phy-cells");
>> +
>> +     priv->usb2_phys = devm_kcalloc(dev, priv->num_usb2_phys,
>> +                                    sizeof(*priv->usb2_phys), GFP_KERNEL);
>> +     if (!priv->usb2_phys)
>> +             return -ENOMEM;
>> +
>> +     for (i = 0; i < priv->num_usb2_phys; i++) {
>> +             priv->usb2_phys[i] = devm_of_phy_get_by_index(dev, np, i);
>
> I'm not sure if referencing usb2_phy from here is the right approach.
that would probably be gone with the USB patches from [0] and with the
switch to extcon

I will send an updated version once we know how to handle powering up
the PHY in xhci-plat.c (which is the series from [0]).


Regards,
Martin


[0] http://marc.info/?l=linux-usb&m=148414866203601&w=2

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

* [PATCH 2/5] phy: meson: add USB2 and USB3 PHY support for Meson GXL
@ 2017-01-16 12:07           ` Martin Blumenstingl
  0 siblings, 0 replies; 39+ messages in thread
From: Martin Blumenstingl @ 2017-01-16 12:07 UTC (permalink / raw)
  To: linus-amlogic

Hi Kishon,

thank you for taking the time to review this!

On Mon, Jan 16, 2017 at 10:41 AM, Kishon Vijay Abraham I <kishon@ti.com> wrote:
> Hi,
>
> On Saturday 26 November 2016 08:26 PM, Martin Blumenstingl wrote:
>> This adds two new USB PHY drivers found on Meson GXL and GXM SoCs.
>
> Please send them as separate drivers.
OK, I also just discovered that USB2 works even when not configuring
the USB3 PHY. That's another good reason for splitting these.

>> The registers for the USB2 PHY block handle a maximum of 4 ports (newer
>> SoCs may allow more ports, the driver handles this as long as the
>> register length is adjusted in the .dts). The PHY block theoretically
>> allows powering down each PHY port separately (by putting it into
>> "reset" state). Unfortunately this does not work (my board has 2 USB
>> ports, connected to port 1 and 2 of the dwc3's internal hub. When
>> leaving the third USB PHY disabled then the hub sees that a device is
>> plugged in, but it does not work: "usb usb1-port2: connect-debounce
>> failed").
>> The USB3 PHY will take care of enabling/disabling all available ports,
>> because the USB3 PHY also manages the mode of the USB2 PHYs.
>>
>> The USB3 PHY actually has three purposes:
>> - it provides the USB3 PHY
>> - it handles the OTG device/host mode detection interrupt
>> - it notifies the corresponding USB2 PHYs of the OTG mode changes
>> On GXL and GXM SoCs one references all available USB2 PHY ports in the
>> USB3 PHY because all are connected to the same USB controller (thus the
>> mode will always match). This behavior is configurable via devicetree,
>> by passing (or not passing) a list of other ("child") PHYs which should
>> be configured by the USB3 PHY.
>>
>> Unfortunately there are no datasheets available for any of these PHYs.
>> Both drivers were written by reading the reference drivers provided by
>> Amlogic and analyzing the registers on the kernel that was shipped with
>> my board.
>>
>> Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
>> ---
>>  drivers/phy/Kconfig              |  13 ++
>>  drivers/phy/Makefile             |   2 +
>>  drivers/phy/phy-meson-gxl-usb2.c | 374 ++++++++++++++++++++++++++++++++++++++
>>  drivers/phy/phy-meson-gxl-usb3.c | 377 +++++++++++++++++++++++++++++++++++++++
>>  4 files changed, 766 insertions(+)
>>  create mode 100644 drivers/phy/phy-meson-gxl-usb2.c
>>  create mode 100644 drivers/phy/phy-meson-gxl-usb3.c
>>
>> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
>> index 728e03f..ea74843 100644
>> --- a/drivers/phy/Kconfig
>> +++ b/drivers/phy/Kconfig
>> @@ -502,4 +502,17 @@ config PHY_MESON8B_USB2
>>         and GXBB SoCs.
>>         If unsure, say N.
>>
>> +config PHY_MESON_GXL_USB
>> +     tristate "Meson GXL USB2 and USB3 PHY drivers"
>> +     default ARCH_MESON
>> +     depends on OF && (ARCH_MESON || COMPILE_TEST)
>> +     depends on USB_SUPPORT
>> +     select USB_COMMON
>> +     select GENERIC_PHY
>> +     select REGMAP_MMIO
>> +     help
>> +       Enable this to support the Meson USB2 and USB3 PHYs found in
>> +       Meson GXL SoCs.
>> +       If unsure, say N.
>> +
>>  endmenu
>> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
>> index 0c7fdae..960a96e 100644
>> --- a/drivers/phy/Makefile
>> +++ b/drivers/phy/Makefile
>> @@ -61,3 +61,5 @@ obj-$(CONFIG_PHY_CYGNUS_PCIE)               += phy-bcm-cygnus-pcie.o
>>  obj-$(CONFIG_ARCH_TEGRA) += tegra/
>>  obj-$(CONFIG_PHY_NS2_PCIE)           += phy-bcm-ns2-pcie.o
>>  obj-$(CONFIG_PHY_MESON8B_USB2)               += phy-meson8b-usb2.o
>> +obj-$(CONFIG_PHY_MESON_GXL_USB)              += phy-meson-gxl-usb2.o
>> +obj-$(CONFIG_PHY_MESON_GXL_USB)              += phy-meson-gxl-usb3.o
>> diff --git a/drivers/phy/phy-meson-gxl-usb2.c b/drivers/phy/phy-meson-gxl-usb2.c
>> new file mode 100644
>> index 0000000..c081ce3
[snip]
>> +static struct phy *phy_meson_gxl_usb2_of_xlate(struct device *dev,
>> +                                            struct of_phandle_args *args)
>> +{
>> +     struct phy_meson_gxl_usb2_drv *priv = dev_get_drvdata(dev);
>> +     int port;
>> +
>> +     if (args->args_count != 1) {
>> +             dev_err(dev, "Invalid number of cells in 'phy' property\n");
>> +             return ERR_PTR(-ENODEV);
>> +     }
>> +
>> +     port = args->args[0];
>> +     if (WARN_ON(port >= priv->num_ports))
>> +             return ERR_PTR(-ENODEV);
>> +
>> +     return priv->ports[port];
>> +}
>
> Please model every port as a sub-node and get rid of custom xlate implementation.
already done in my local tree.
I am in contact with the USB developers to get the USB2 PHYs working
(as all of them have to be turned on when powering up dwc3). The
result of how the dwc3 node with the PHY references may look like can
be seen here: [0]

>> +MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
>> +MODULE_DESCRIPTION("Meson GXL USB2 PHY driver");
>> +MODULE_LICENSE("GPL");
>
> GPL v2 to match with the file header.
good catch, thanks

>> diff --git a/drivers/phy/phy-meson-gxl-usb3.c b/drivers/phy/phy-meson-gxl-usb3.c
>> new file mode 100644
>> index 0000000..90a4028
>> --- /dev/null
>> +++ b/drivers/phy/phy-meson-gxl-usb3.c
>> @@ -0,0 +1,377 @@
>> +/*
>> + * Meson GXL USB3 PHY driver
>> + *
>> + * Copyright (C) 2016 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + *
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/io.h>
>> +#include <linux/module.h>
>> +#include <linux/of_device.h>
>> +#include <linux/reset.h>
>> +#include <linux/regmap.h>
>> +#include <linux/phy/phy.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/usb/of.h>
>> +#include <linux/workqueue.h>
>> +
>> +#define USB_R0                                                       0x00
>> +     #define USB_R0_P30_FSEL_SHIFT                           0
>> +     #define USB_R0_P30_FSEL_MASK                            GENMASK(5, 0)
>> +     #define USB_R0_P30_PHY_RESET                            BIT(6)
>> +     #define USB_R0_P30_TEST_POWERDOWN_HSP                   BIT(7)
>> +     #define USB_R0_P30_TEST_POWERDOWN_SSP                   BIT(8)
>> +     #define USB_R0_P30_ACJT_LEVEL_SHIFT                     9
>> +     #define USB_R0_P30_ACJT_LEVEL_MASK                      GENMASK(13, 9)
>> +     #define USB_R0_P30_TX_BOOST_LEVEL_SHIFT                 14
>> +     #define USB_R0_P30_TX_BOOST_LEVEL_MASK                  GENMASK(16, 14)
>> +     #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_SHIFT            19
>> +     #define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK             GENMASK(28, 19)
>> +     #define USB_R0_U2D_SS_SCALEDOWN_MODE_SHIFT              29
>> +     #define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK               GENMASK(30, 29)
>> +     #define USB_R0_U2D_ACT                                  BIT(31)
>> +
>> +#define USB_R1                                                       0x04
>> +     #define USB_R1_U3H_BIGENDIAN_GS                         BIT(0)
>> +     #define USB_R1_U3H_PME_ENABLE                           BIT(1)
>> +     #define USB_R1_U3H_HUB_PORT_OVERCURRENT_SHIFT           2
>> +     #define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK            GENMASK(6, 2)
>> +     #define USB_R1_U3H_HUB_PORT_PERM_ATTACH_SHIFT           7
>> +     #define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK            GENMASK(11, 7)
>> +     #define USB_R1_U3H_HOST_U2_PORT_DISABLE_SHIFT           12
>> +     #define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK            GENMASK(15, 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_SHIFT                19
>> +     #define USB_R1_U3H_FLADJ_30MHZ_REG_MASK                 GENMASK(24, 19)
>> +     #define USB_R1_P30_PCS_TX_SWING_FULL_SHIFT              25
>> +     #define USB_R1_P30_PCS_TX_SWING_FULL_MASK               GENMASK(31, 25)
>> +
>> +#define USB_R2                                                       0x08
>> +     #define USB_R2_P30_CR_DATA_IN_SHIFT                     0
>> +     #define USB_R2_P30_CR_DATA_IN_MASK                      GENMASK(15, 0)
>> +     #define USB_R2_P30_CR_READ                              BIT(16)
>> +     #define USB_R2_P30_CR_WRITE                             BIT(17)
>> +     #define USB_R2_P30_CR_CAP_ADDR                          BIT(18)
>> +     #define USB_R2_P30_CR_CAP_DATA                          BIT(19)
>> +     #define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_SHIFT            20
>> +     #define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK             GENMASK(25, 20)
>> +     #define USB_R2_P30_PCS_TX_DEEMPH_6DB_SHIFT              26
>> +     #define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK               GENMASK(31, 26)
>> +
>> +#define USB_R3                                                       0x0c
>> +     #define USB_R3_P30_SSC_ENABLE                           BIT(0)
>> +     #define USB_R3_P30_SSC_RANGE_SHIFT                      1
>> +     #define USB_R3_P30_SSC_RANGE_MASK                       GENMASK(3, 1)
>> +     #define USB_R3_P30_SSC_REF_CLK_SEL_SHIFT                4
>> +     #define USB_R3_P30_SSC_REF_CLK_SEL_MASK                 GENMASK(12, 4)
>> +     #define USB_R3_P30_REF_SSP_EN                           BIT(13)
>> +     #define USB_R3_P30_LOS_BIAS_SHIFT                       16
>> +     #define USB_R3_P30_LOS_BIAS_MASK                        GENMASK(18, 16)
>> +     #define USB_R3_P30_LOS_LEVEL_SHIFT                      19
>> +     #define USB_R3_P30_LOS_LEVEL_MASK                       GENMASK(23, 19)
>> +     #define USB_R3_P30_MPLL_MULTIPLIER_SHIFT                24
>> +     #define USB_R3_P30_MPLL_MULTIPLIER_MASK                 GENMASK(30, 24)
>> +
>> +#define USB_R4                                                       0x10
>> +     #define USB_R4_P21_PORT_RESET_0                         BIT(0)
>> +     #define USB_R4_P21_SLEEP_M0                             BIT(1)
>> +     #define USB_R4_MEM_PD_SHIFT                             2
>> +     #define USB_R4_MEM_PD_MASK                              GENMASK(3, 2)
>> +     #define USB_R4_P21_ONLY                                 BIT(4)
>> +
>> +#define USB_R5                                                       0x14
>> +     #define USB_R5_ID_DIG_SYNC                              BIT(0)
>> +     #define USB_R5_ID_DIG_REG                               BIT(1)
>> +     #define USB_R5_ID_DIG_CFG_SHIFT                         2
>> +     #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_SHIFT                          8
>> +     #define USB_R5_ID_DIG_TH_MASK                           GENMASK(15, 8)
>> +     #define USB_R5_ID_DIG_CNT_SHIFT                         16
>> +     #define USB_R5_ID_DIG_CNT_MASK                          GENMASK(23, 16)
>> +
>> +/* read-only register */
>> +#define USB_R6                                                       0x18
>> +     #define USB_R6_P30_CR_DATA_OUT_SHIFT                    0
>> +     #define USB_R6_P30_CR_DATA_OUT_MASK                     GENMASK(15, 0)
>> +     #define USB_R6_P30_CR_ACK                               BIT(16)
>> +
>> +#define RESET_COMPLETE_TIME                          500
>> +
>> +struct phy_meson_gxl_usb3_priv {
>> +     struct regmap           *regmap;
>> +     struct delayed_work     otg_work;
>> +     struct phy              *this_phy;
>> +     int                     num_usb2_phys;
>> +     struct phy              **usb2_phys;
>> +};
>> +
>> +static const struct regmap_config phy_meson_gxl_usb3_regmap_conf = {
>> +     .reg_bits = 32,
>> +     .val_bits = 32,
>> +     .reg_stride = 4,
>> +     .max_register = USB_R6,
>> +};
>> +
>> +static int phy_meson_gxl_usb3_update_mode(struct phy *phy)
>> +{
>> +     struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
>> +     u32 val;
>> +     enum phy_mode mode;
>> +     int i, ret;
>> +
>> +     ret = regmap_read(priv->regmap, USB_R5, &val);
>> +     if (ret)
>> +             return ret;
>> +
>> +     if (val & USB_R5_ID_DIG_CURR) {
>> +             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_R4, USB_R4_P21_SLEEP_M0,
>> +                                USB_R4_P21_SLEEP_M0);
>> +     } else {
>> +             mode = PHY_MODE_USB_HOST;
>> +
>> +             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);
>> +     }
>> +
>> +     /* inform the USB2 PHY that we have changed the mode */
>> +     for (i = 0; i < priv->num_usb2_phys; i++) {
>> +             ret = phy_set_mode(priv->usb2_phys[i], mode);
>
> I'm finding it difficult to understand this. Why should the mode of one phy be
> set from another phy? Maybe this part should be implemented using extcon?
sounds like a good idea, I will postpone the USB3 PHY driver though
since it's currently only used for "mode switching" (between USB
host/device) and the whole thing requires MUCH more work (as a dwc2
controller is used for device mode, while a dwc3 controller is used
for host mode).

>> +             if (ret) {
>> +                     dev_err(&phy->dev,
>> +                             "Failed to update usb2-phy #%d mode to %d\n",
>> +                             i, mode);
>> +                     return ret;
>> +             }
>> +     }
>> +
>> +     return ret;
>> +}
>> +
>> +static void phy_meson_gxl_usb3_work(struct work_struct *data)
>> +{
>> +     struct phy_meson_gxl_usb3_priv *priv =
>> +             container_of(data, struct phy_meson_gxl_usb3_priv,
>> +                          otg_work.work);
>> +
>> +     phy_meson_gxl_usb3_update_mode(priv->this_phy);
>> +
>> +     /* unmask IRQs which may have arrived in the meantime */
>> +     regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_IRQ, 0);
>> +}
>> +
>> +static int phy_meson_gxl_usb3_init(struct phy *phy)
>> +{
>> +     struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
>> +     int i, ret;
>> +
>> +     for (i = 0; i < priv->num_usb2_phys; i++) {
>> +             ret = phy_init(priv->usb2_phys[i]);
>> +             if (ret) {
>> +                     dev_err(&phy->dev,
>> +                             "Failed to initialize related usb2-phy #%d\n",
>> +                             i);
>> +                     return ret;
>> +             }
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static int phy_meson_gxl_usb3_exit(struct phy *phy)
>> +{
>> +     struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
>> +     int i, ret;
>> +
>> +     for (i = 0; i < priv->num_usb2_phys; i++) {
>> +             ret = phy_exit(priv->usb2_phys[i]);
>> +             if (ret) {
>> +                     dev_err(&phy->dev,
>> +                             "Failed to exit related usb2-phy #%d\n", i);
>> +                     return ret;
>> +             }
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static int phy_meson_gxl_usb3_power_on(struct phy *phy)
>> +{
>> +     struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
>> +     int i, ret;
>> +
>> +     for (i = 0; i < priv->num_usb2_phys; i++) {
>> +             ret = phy_power_on(priv->usb2_phys[i]);
>> +             if (ret) {
>> +                     dev_err(&phy->dev,
>> +                             "Failed to power on related usb2-phy #%d\n",
>> +                             i);
>> +                     return ret;
>> +             }
>> +     }
>> +
>> +     regmap_update_bits(priv->regmap, USB_R1,
>> +                        USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
>> +                        0x20 << USB_R1_U3H_FLADJ_30MHZ_REG_SHIFT);
>> +
>> +     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,
>> +                        0xff << USB_R5_ID_DIG_TH_SHIFT);
>> +
>> +     return phy_meson_gxl_usb3_update_mode(phy);
>> +}
>> +
>> +static int phy_meson_gxl_usb3_power_off(struct phy *phy)
>> +{
>> +     struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
>> +     int i, ret;
>> +
>> +     for (i = 0; i < priv->num_usb2_phys; i++) {
>> +             ret = phy_power_off(priv->usb2_phys[i]);
>> +             if (ret) {
>> +                     dev_err(&phy->dev,
>> +                             "Failed to power off related usb2-phy #%d\n",
>> +                             i);
>> +                     return ret;
>> +             }
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static irqreturn_t phy_meson_gxl_usb3_irq(int irq, void *data)
>> +{
>> +     u32 val;
>> +     struct phy_meson_gxl_usb3_priv *priv = data;
>> +
>> +     regmap_read(priv->regmap, USB_R5, &val);
>> +     if (!(val & USB_R5_ID_DIG_IRQ)) {
>> +             dev_err(&priv->this_phy->dev, "spurious interrupt\n");
>> +             return IRQ_NONE;
>> +     }
>> +
>> +     schedule_delayed_work(&priv->otg_work, msecs_to_jiffies(10));
>> +
>> +     /* acknowledge the IRQ */
>> +     regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_IRQ, 0);
>> +
>> +     return IRQ_HANDLED;
>> +}
>> +
>> +static const struct phy_ops phy_meson_gxl_usb3_ops = {
>> +     .init           = phy_meson_gxl_usb3_init,
>> +     .exit           = phy_meson_gxl_usb3_exit,
>> +     .power_on       = phy_meson_gxl_usb3_power_on,
>> +     .power_off      = phy_meson_gxl_usb3_power_off,
>> +     .owner          = THIS_MODULE,
>> +};
>> +
>> +static int phy_meson_gxl_usb3_probe(struct platform_device *pdev)
>> +{
>> +     struct device *dev = &pdev->dev;
>> +     struct device_node *np = dev->of_node;
>> +     struct phy_meson_gxl_usb3_priv *priv;
>> +     struct resource *res;
>> +     struct phy *phy;
>> +     struct phy_provider *phy_provider;
>> +     void __iomem *base;
>> +     int i, irq;
>> +
>> +     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_gxl_usb3_regmap_conf);
>> +     if (IS_ERR(priv->regmap))
>> +             return PTR_ERR(priv->regmap);
>> +
>> +     irq = platform_get_irq(pdev, 0);
>> +     if (irq >= 0) {
>> +             INIT_DELAYED_WORK(&priv->otg_work, phy_meson_gxl_usb3_work);
>> +
>> +             irq = devm_request_irq(dev, irq, phy_meson_gxl_usb3_irq,
>> +                                    IRQF_SHARED, dev_name(dev),
>> +                                    priv);
>> +             if (irq < 0) {
>> +                     dev_err(dev, "could not register IRQ handler (%d)\n",
>> +                             irq);
>> +                     return -EINVAL;
>> +             }
>> +     }
>> +
>> +     priv->num_usb2_phys = of_count_phandle_with_args(np, "phys",
>> +                                                      "#phy-cells");
>> +
>> +     priv->usb2_phys = devm_kcalloc(dev, priv->num_usb2_phys,
>> +                                    sizeof(*priv->usb2_phys), GFP_KERNEL);
>> +     if (!priv->usb2_phys)
>> +             return -ENOMEM;
>> +
>> +     for (i = 0; i < priv->num_usb2_phys; i++) {
>> +             priv->usb2_phys[i] = devm_of_phy_get_by_index(dev, np, i);
>
> I'm not sure if referencing usb2_phy from here is the right approach.
that would probably be gone with the USB patches from [0] and with the
switch to extcon

I will send an updated version once we know how to handle powering up
the PHY in xhci-plat.c (which is the series from [0]).


Regards,
Martin


[0] http://marc.info/?l=linux-usb&m=148414866203601&w=2

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

end of thread, other threads:[~2017-01-16 12:07 UTC | newest]

Thread overview: 39+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-11-26 14:56 [PATCH 0/5] Meson GXL and GXM USB support Martin Blumenstingl
2016-11-26 14:56 ` Martin Blumenstingl
2016-11-26 14:56 ` Martin Blumenstingl
     [not found] ` <20161126145635.24488-1-martin.blumenstingl-gM/Ye1E23mwN+BqQ9rBEUg@public.gmane.org>
2016-11-26 14:56   ` [PATCH 1/5] Documentation: dt-bindings: Add documentation for Meson GXL USB2/3 PHYs Martin Blumenstingl
2016-11-26 14:56     ` Martin Blumenstingl
2016-11-26 14:56     ` Martin Blumenstingl
2016-11-26 14:56   ` [PATCH 2/5] phy: meson: add USB2 and USB3 PHY support for Meson GXL Martin Blumenstingl
2016-11-26 14:56     ` Martin Blumenstingl
2016-11-26 14:56     ` Martin Blumenstingl
2017-01-16  9:41     ` Kishon Vijay Abraham I
2017-01-16  9:41       ` Kishon Vijay Abraham I
2017-01-16  9:41       ` Kishon Vijay Abraham I
     [not found]       ` <587C9566.7080306-l0cyMroinI0@public.gmane.org>
2017-01-16 12:07         ` Martin Blumenstingl
2017-01-16 12:07           ` Martin Blumenstingl
2017-01-16 12:07           ` Martin Blumenstingl
2016-11-26 14:56   ` [PATCH 3/5] arm64: dts: meson-gxl: add USB support Martin Blumenstingl
2016-11-26 14:56     ` Martin Blumenstingl
2016-11-26 14:56     ` Martin Blumenstingl
2016-11-26 14:56   ` [PATCH 4/5] ARM64: dts: meson-gxm: add GXM specific USB configuration Martin Blumenstingl
2016-11-26 14:56     ` Martin Blumenstingl
2016-11-26 14:56     ` Martin Blumenstingl
2016-11-26 14:56   ` [PATCH 5/5] ARM64: dts: meson-gx-p23x-q20x: enable USB on P23x and Q20x boards Martin Blumenstingl
2016-11-26 14:56     ` Martin Blumenstingl
2016-11-26 14:56     ` Martin Blumenstingl
2016-11-27 22:42   ` [PATCH 0/5] Meson GXL and GXM USB support Martin Blumenstingl
2016-11-27 22:42     ` Martin Blumenstingl
2016-11-27 22:42     ` Martin Blumenstingl
     [not found]     ` <CAFBinCAA_JEtr_0Ze0thoRaEKMnWQMKcPxJ8y88zkWAAhuxsMw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2016-11-30 22:22       ` Rob Herring
2016-11-30 22:22         ` Rob Herring
2016-11-30 22:22         ` Rob Herring
2016-11-30 22:49         ` Martin Blumenstingl
2016-11-30 22:49           ` Martin Blumenstingl
2016-11-30 22:49           ` Martin Blumenstingl
     [not found]           ` <CAFBinCC8c2sXgo80LMMg=jqZ2hr8eLM_2g03H1J99m4xxFGYFA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2016-12-01 15:54             ` Rob Herring
2016-12-01 15:54               ` Rob Herring
2016-12-01 15:54               ` Rob Herring
2016-11-28 14:30   ` Neil Armstrong
2016-11-28 14:30     ` Neil Armstrong
2016-11-28 14:30     ` Neil Armstrong

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.