2016-03-04 16:19:48

by Thierry Reding

[permalink] [raw]
Subject: [PATCH v10 1/9] dt-bindings: phy: Add NVIDIA Tegra XUSB pad controller binding

From: Thierry Reding <[email protected]>

The NVIDIA Tegra XUSB pad controller provides a set of pads, each with a
set of lanes that are used for PCIe, SATA and USB.

Signed-off-by: Thierry Reding <[email protected]>
---
Changes in v10:
- clarify that the hardware documentation means something different when
referring to a "port" (intra-SoC connectivity)

Changes in v9:
- rename UTMI -> USB2 to match hardware documentation
- reword according to suggestions by Stephen Warren
- make Tegra132 compatible string list consistent
- remove mailbox support

.../bindings/phy/nvidia,tegra124-xusb-padctl.txt | 376 +++++++++++++++++++++
.../pinctrl/nvidia,tegra124-xusb-padctl.txt | 5 +
2 files changed, 381 insertions(+)
create mode 100644 Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt

diff --git a/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
new file mode 100644
index 000000000000..8b642d9e3433
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
@@ -0,0 +1,376 @@
+Device tree binding for NVIDIA Tegra XUSB pad controller
+========================================================
+
+The Tegra XUSB pad controller manages a set of I/O lanes (with differential
+signals) which connect directly to pins/pads on the SoC package. Each lane
+is controlled by a HW block referred to as a "pad" in the Tegra hardware
+documentation. Each such "pad" may control either one or multiple lanes,
+and thus contains any logic common to all its lanes. Each lane can be
+separately configured and powered up.
+
+Some of the lanes are high-speed lanes, which can be used for PCIe, SATA or
+super-speed USB. Other lanes are for various types of low-speed, full-speed
+or high-speed USB (such as UTMI, ULPI and HSIC). The XUSB pad controller
+contains a software-configurable mux that sits between the I/O controller
+ports (e.g. PCIe) and the lanes.
+
+In addition to per-lane configuration, USB 3.0 ports may require additional
+settings on a per-board basis.
+
+Pads will be represented as children of the top-level XUSB pad controller
+device tree node. Each lane exposed by the pad will be represented by its
+own subnode and can be referenced by users of the lane using the standard
+PHY bindings, as described by the phy-bindings.txt file in this directory.
+
+The Tegra hardware documentation refers to the connection between the XUSB
+pad controller and the XUSB controller as "ports". This is confusing since
+"port" is typically used to denote the physical USB receptacle. The device
+tree binding in this document uses the term "port" to refer to the logical
+abstraction of the signals that are routed to a USB receptacle (i.e. a PHY
+for the USB signal, the VBUS power supply, the USB 2.0 companion port for
+USB 3.0 receptacles, ...).
+
+Required properties:
+--------------------
+- compatible: Must be:
+ - Tegra124: "nvidia,tegra124-xusb-padctl"
+ - Tegra132: "nvidia,tegra132-xusb-padctl", "nvidia,tegra124-xusb-padctl"
+- reg: Physical base address and length of the controller's registers.
+- resets: Must contain an entry for each entry in reset-names.
+- reset-names: Must include the following entries:
+ - "padctl"
+
+
+Pad nodes:
+==========
+
+A required child node named "pads" contains a list of subnodes, one for each
+of the pads exposed by the XUSB pad controller. Each pad may need additional
+resources that can be referenced in its pad node.
+
+The "status" property is used to enable or disable the use of a pad. If set
+to "disabled", the pad will not be used on the given board. In order to use
+the pad and any of its lanes, this property must be set to "okay".
+
+For Tegra124 and Tegra132, the following pads exist: usb2, ulpi, hsic, pcie
+and sata. No extra resources are required for operation of these pads.
+
+
+PHY nodes:
+==========
+
+Each pad node has one or more children, each representing one of the lanes
+controlled by the pad.
+
+Required properties:
+--------------------
+- status: Defines the operation status of the PHY. Valid values are:
+ - "disabled": the PHY is disabled
+ - "okay": the PHY is enabled
+- #phy-cells: Should be 0. Since each lane represents a single PHY, there is
+ no need for an additional specifier.
+- nvidia,function: The output function of the PHY. See below for a list of
+ valid functions per SoC generation.
+
+For Tegra124 and Tegra132, the list of valid PHY nodes is given below:
+- usb2: usb2-0, usb2-1, usb2-2
+ - functions: "snps", "xusb", "uart"
+- ulpi: ulpi-0
+ - functions: "snps", "xusb"
+- hsic: hsic-0, hsic-1
+ - functions: "snps", "xusb"
+- pcie: pcie-0, pcie-1, pcie-2, pcie-3, pcie-4
+ - functions: "pcie", "usb3-ss"
+- sata: sata-0
+ - functions: "usb3-ss", "sata"
+
+
+Port nodes:
+===========
+
+A required child node named "ports" contains a list of all the ports exposed
+by the XUSB pad controller. Per-port configuration is only required for USB.
+
+USB2 ports:
+-----------
+
+Required properties:
+- status: Defines the operation status of the port. Valid values are:
+ - "disabled": the port is disabled
+ - "okay": the port is enabled
+- mode: A string that determines the mode in which to run the port. Valid
+ values are:
+ - "host": for USB host mode
+ - "device": for USB device mode
+ - "otg": for USB OTG mode
+
+Optional properties:
+- nvidia,internal: A boolean property whose presence determines that a port
+ is internal. In the absence of this property the port is considered to be
+ external.
+- vbus-supply: phandle to a regulator supplying the VBUS voltage.
+
+ULPI ports:
+-----------
+
+Optional properties:
+- status: Defines the operation status of the port. Valid values are:
+ - "disabled": the port is disabled
+ - "okay": the port is enabled
+- nvidia,internal: A boolean property whose presence determines that a port
+ is internal. In the absence of this property the port is considered to be
+ external.
+- vbus-supply: phandle to a regulator supplying the VBUS voltage.
+
+HSIC ports:
+-----------
+
+Required properties:
+- status: Defines the operation status of the port. Valid values are:
+ - "disabled": the port is disabled
+ - "okay": the port is enabled
+
+Optional properties:
+- vbus-supply: phandle to a regulator supplying the VBUS voltage.
+
+Super-speed USB ports:
+----------------------
+
+Required properties:
+- status: Defines the operation status of the port. Valid values are:
+ - "disabled": the port is disabled
+ - "okay": the port is enabled
+- nvidia,usb2-companion: A single cell that specifies the physical port number
+ to map this super-speed USB port to. The range of valid port numbers varies
+ with the SoC generation:
+ - 0-2: for Tegra124 and Tegra132
+
+Optional properties:
+- nvidia,internal: A boolean property whose presence determines that a port
+ is internal. In the absence of this property the port is considered to be
+ external.
+
+For Tegra124 and Tegra132, the XUSB pad controller exposes the following
+ports:
+- 3x USB2: usb2-0, usb2-1, usb2-2
+- 1x ULPI: ulpi-0
+- 2x HSIC: hsic-0, hsic-1
+- 2x super-speed USB: usb3-0, usb3-1
+
+
+Examples:
+=========
+
+Tegra124 and Tegra132:
+----------------------
+
+SoC include:
+
+ padctl@0,7009f000 {
+ /* for Tegra124 */
+ compatible = "nvidia,tegra124-xusb-padctl";
+ /* for Tegra132 */
+ compatible = "nvidia,tegra132-xusb-padctl",
+ "nvidia,tegra124-xusb-padctl";
+ reg = <0x0 0x7009f000 0x0 0x1000>;
+ resets = <&tegra_car 142>;
+ reset-names = "padctl";
+
+ pads {
+ usb2 {
+ status = "disabled";
+
+ usb2-0 {
+ status = "disabled";
+ #phy-cells = <0>;
+ };
+
+ usb2-1 {
+ status = "disabled";
+ #phy-cells = <0>;
+ };
+
+ usb2-2 {
+ status = "disabled";
+ #phy-cells = <0>;
+ };
+ };
+
+ ulpi {
+ status = "disabled";
+
+ ulpi-0 {
+ status = "disabled";
+ #phy-cells = <0>;
+ };
+ };
+
+ hsic {
+ status = "disabled";
+
+ hsic-0 {
+ status = "disabled";
+ #phy-cells = <0>;
+ };
+
+ hsic-1 {
+ status = "disabled";
+ #phy-cells = <0>;
+ };
+ };
+
+ pcie {
+ status = "disabled";
+
+ pcie-0 {
+ status = "disabled";
+ #phy-cells = <0>;
+ };
+
+ pcie-1 {
+ status = "disabled";
+ #phy-cells = <0>;
+ };
+
+ pcie-2 {
+ status = "disabled";
+ #phy-cells = <0>;
+ };
+
+ pcie-3 {
+ status = "disabled";
+ #phy-cells = <0>;
+ };
+
+ pcie-4 {
+ status = "disabled";
+ #phy-cells = <0>;
+ };
+ };
+
+ sata {
+ status = "disabled";
+
+ sata-0 {
+ status = "disabled";
+ #phy-cells = <0>;
+ };
+ };
+ };
+
+ ports {
+ usb2-0 {
+ status = "disabled";
+ };
+
+ usb2-1 {
+ status = "disabled";
+ };
+
+ usb2-2 {
+ status = "disabled";
+ };
+
+ ulpi-0 {
+ status = "disabled";
+ };
+
+ hsic-0 {
+ status = "disabled";
+ };
+
+ hsic-1 {
+ status = "disabled";
+ };
+
+ usb3-0 {
+ status = "disabled";
+ };
+
+ usb3-1 {
+ status = "disabled";
+ };
+ };
+ };
+
+Board file:
+
+ padctl@0,7009f000 {
+ status = "okay";
+
+ pads {
+ usb2 {
+ status = "okay";
+
+ usb2-0 {
+ nvidia,function = "xusb";
+ status = "okay";
+ };
+
+ usb2-1 {
+ nvidia,function = "xusb";
+ status = "okay";
+ };
+
+ usb2-2 {
+ nvidia,function = "xusb";
+ status = "okay";
+ };
+ };
+
+ pcie {
+ status = "okay";
+
+ pcie-0 {
+ nvidia,function = "usb3-ss";
+ status = "okay";
+ };
+
+ pcie-2 {
+ nvidia,function = "pcie";
+ status = "okay";
+ };
+
+ pcie-4 {
+ nvidia,function = "pcie";
+ status = "okay";
+ };
+ };
+
+ sata {
+ status = "okay";
+
+ sata-0 {
+ nvidia,function = "sata";
+ status = "okay";
+ };
+ };
+ };
+
+ ports {
+ /* Micro A/B */
+ usb2-0 {
+ status = "okay";
+ mode = "otg";
+ };
+
+ /* Mini PCIe */
+ usb2-1 {
+ status = "okay";
+ mode = "host";
+ };
+
+ /* USB3 */
+ usb2-2 {
+ status = "okay";
+ mode = "host";
+
+ vbus-supply = <&vdd_usb3_vbus>;
+ };
+
+ usb3-0 {
+ nvidia,port = <2>;
+ status = "okay";
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra124-xusb-padctl.txt b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra124-xusb-padctl.txt
index 30676ded85bb..77dfba05ccfd 100644
--- a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra124-xusb-padctl.txt
+++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra124-xusb-padctl.txt
@@ -1,6 +1,11 @@
Device tree binding for NVIDIA Tegra XUSB pad controller
========================================================

+NOTE: It turns out that this binding isn't an accurate description of the XUSB
+pad controller. While the description is good enough for the functional subset
+required for PCIe and SATA, it lacks the flexibility to represent the features
+needed for USB. For the new binding, see ../phy/nvidia,tegra-xusb-padctl.txt.
+
The Tegra XUSB pad controller manages a set of lanes, each of which can be
assigned to one out of a set of different pads. Some of these pads have an
associated PHY that must be powered up before the pad can be used.
--
2.7.1


2016-03-04 16:19:53

by Thierry Reding

[permalink] [raw]
Subject: [PATCH v10 2/9] dt-bindings: pinctrl: Deprecate Tegra XUSB pad controller binding

From: Thierry Reding <[email protected]>

This is an old version of the binding that isn't flexible enough to
describe all aspects of the XUSB pad controller. Specifically with the
addition of XUSB support (for SuperSpeed USB) the existing binding is
no longer suitable.

Signed-off-by: Thierry Reding <[email protected]>
---
.../devicetree/bindings/pinctrl/nvidia,tegra124-xusb-padctl.txt | 1 +
1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra124-xusb-padctl.txt b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra124-xusb-padctl.txt
index 77dfba05ccfd..8a6223dbc143 100644
--- a/Documentation/devicetree/bindings/pinctrl/nvidia,tegra124-xusb-padctl.txt
+++ b/Documentation/devicetree/bindings/pinctrl/nvidia,tegra124-xusb-padctl.txt
@@ -5,6 +5,7 @@ NOTE: It turns out that this binding isn't an accurate description of the XUSB
pad controller. While the description is good enough for the functional subset
required for PCIe and SATA, it lacks the flexibility to represent the features
needed for USB. For the new binding, see ../phy/nvidia,tegra-xusb-padctl.txt.
+The binding described in this file is deprecated and should not be used.

The Tegra XUSB pad controller manages a set of lanes, each of which can be
assigned to one out of a set of different pads. Some of these pads have an
--
2.7.1

2016-03-04 16:19:58

by Thierry Reding

[permalink] [raw]
Subject: [PATCH v10 3/9] dt-bindings: phy: tegra-xusb-padctl: Add Tegra210 support

From: Thierry Reding <[email protected]>

Extend the binding to cover the set of feature found in Tegra210.

Signed-off-by: Thierry Reding <[email protected]>
---
.../bindings/phy/nvidia,tegra124-xusb-padctl.txt | 327 +++++++++++++++++++++
1 file changed, 327 insertions(+)

diff --git a/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
index 8b642d9e3433..8cbfeb60f864 100644
--- a/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
+++ b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
@@ -35,6 +35,7 @@ Required properties:
- compatible: Must be:
- Tegra124: "nvidia,tegra124-xusb-padctl"
- Tegra132: "nvidia,tegra132-xusb-padctl", "nvidia,tegra124-xusb-padctl"
+ - Tegra210: "nvidia,tegra210-xusb-padctl"
- reg: Physical base address and length of the controller's registers.
- resets: Must contain an entry for each entry in reset-names.
- reset-names: Must include the following entries:
@@ -55,6 +56,44 @@ the pad and any of its lanes, this property must be set to "okay".
For Tegra124 and Tegra132, the following pads exist: usb2, ulpi, hsic, pcie
and sata. No extra resources are required for operation of these pads.

+For Tegra210, the following pads exist: usb2, hsic, pcie and sata. Below is
+a description of the properties of each pad.
+
+UTMI pad:
+---------
+
+Required properties:
+- clocks: Must contain an entry for each entry in clock-names.
+- clock-names: Must contain the following entries:
+ - "trk": phandle and specifier referring to the USB2 tracking clock
+
+HSIC pad:
+---------
+
+Required properties:
+- clocks: Must contain an entry for each entry in clock-names.
+- clock-names: Must contain the following entries:
+ - "trk": phandle and specifier referring to the HSIC tracking clock
+
+PCIe pad:
+---------
+
+Required properties:
+- clocks: Must contain an entry for each entry in clock-names.
+- clock-names: Must contain the following entries:
+ - "pll": phandle and specifier referring to the PLLE
+- resets: Must contain an entry for each entry in reset-names.
+- reset-names: Must contain the following entries:
+ - "phy": reset for the PCIe UPHY block
+
+SATA pad:
+---------
+
+Required properties:
+- resets: Must contain an entry for each entry in reset-names.
+- reset-names: Must contain the following entries:
+ - "phy": reset for the SATA UPHY block
+

PHY nodes:
==========
@@ -84,6 +123,16 @@ For Tegra124 and Tegra132, the list of valid PHY nodes is given below:
- sata: sata-0
- functions: "usb3-ss", "sata"

+For Tegra210, the list of valid PHY nodes is given below:
+- utmi: utmi-0, utmi-1, utmi-2, utmi-3
+ - functions: "snps", "xusb", "uart"
+- hsic: hsic-0, hsic-1
+ - functions: "snps", "xusb"
+- pcie: pcie-0, pcie-1, pcie-2, pcie-3, pcie-4, pcie-5, pcie-6
+ - functions: "pcie-x1", "usb3-ss", "pcie-x4"
+- sata: sata-0
+ - functions: "usb3-ss", "sata"
+

Port nodes:
===========
@@ -144,6 +193,7 @@ Required properties:
to map this super-speed USB port to. The range of valid port numbers varies
with the SoC generation:
- 0-2: for Tegra124 and Tegra132
+ - 0-3: for Tegra210

Optional properties:
- nvidia,internal: A boolean property whose presence determines that a port
@@ -157,6 +207,11 @@ ports:
- 2x HSIC: hsic-0, hsic-1
- 2x super-speed USB: usb3-0, usb3-1

+For Tegra210, the XUSB pad controller exposes the following ports:
+- 4x USB2: usb2-0, usb2-1, usb2-2, usb2-3
+- 2x HSIC: hsic-0, hsic-1
+- 4x super-speed USB: usb3-0, usb3-1, usb3-2, usb3-3
+

Examples:
=========
@@ -374,3 +429,275 @@ Board file:
};
};
};
+
+Tegra210:
+---------
+
+SoC include:
+
+ padctl@0,7009f000 {
+ compatible = "nvidia,tegra210-xusb-padctl";
+ reg = <0x0 0x7009f000 0x0 0x1000>;
+ resets = <&tegra_car 142>;
+ reset-names = "padctl";
+
+ status = "disabled";
+
+ pads {
+ usb2 {
+ clocks = <&tegra_car TEGRA210_CLK_USB2_TRK>;
+ clock-names = "trk";
+ status = "disabled";
+
+ usb2-0 {
+ status = "disabled";
+ #phy-cells = <0>;
+ };
+
+ usb2-1 {
+ status = "disabled";
+ #phy-cells = <0>;
+ };
+
+ usb2-2 {
+ status = "disabled";
+ #phy-cells = <0>;
+ };
+
+ usb2-3 {
+ status = "disabled";
+ #phy-cells = <0>;
+ };
+ };
+
+ hsic {
+ clocks = <&tegra_car TEGRA210_CLK_HSIC_TRK>;
+ clock-names = "trk";
+ status = "disabled";
+
+ hsic-0 {
+ status = "disabled";
+ #phy-cells = <0>;
+ };
+
+ hsic-1 {
+ status = "disabled";
+ #phy-cells = <0>;
+ };
+ };
+
+ pcie {
+ clocks = <&tegra_car TEGRA210_CLK_PLL_E>;
+ clock-names = "pll";
+ resets = <&tegra_car 205>;
+ reset-names = "phy";
+ status = "disabled";
+
+ pcie-0 {
+ status = "disabled";
+ #phy-cells = <0>;
+ };
+
+ pcie-1 {
+ status = "disabled";
+ #phy-cells = <0>;
+ };
+
+ pcie-2 {
+ status = "disabled";
+ #phy-cells = <0>;
+ };
+
+ pcie-3 {
+ status = "disabled";
+ #phy-cells = <0>;
+ };
+
+ pcie-4 {
+ status = "disabled";
+ #phy-cells = <0>;
+ };
+
+ pcie-5 {
+ status = "disabled";
+ #phy-cells = <0>;
+ };
+
+ pcie-6 {
+ status = "disabled";
+ #phy-cells = <0>;
+ };
+ };
+
+ sata {
+ clocks = <&tegra_car TEGRA210_CLK_PLL_E>;
+ clock-names = "pll";
+ resets = <&tegra_car 204>;
+ reset-names = "phy";
+ status = "disabled";
+
+ sata-0 {
+ status = "disabled";
+ #phy-cells = <0>;
+ };
+ };
+ };
+
+ ports {
+ usb2-0 {
+ status = "disabled";
+ };
+
+ usb2-1 {
+ status = "disabled";
+ };
+
+ usb2-2 {
+ status = "disabled";
+ };
+
+ usb2-3 {
+ status = "disabled";
+ };
+
+ hsic-0 {
+ status = "disabled";
+ };
+
+ hsic-1 {
+ status = "disabled";
+ };
+
+ usb3-0 {
+ status = "disabled";
+ };
+
+ usb3-1 {
+ status = "disabled";
+ };
+
+ usb3-2 {
+ status = "disabled";
+ };
+
+ usb3-3 {
+ status = "disabled";
+ };
+ };
+ };
+
+Board file:
+
+ padctl@0,7009f000 {
+ status = "okay";
+
+ pads {
+ usb2 {
+ status = "okay";
+
+ usb2-0 {
+ nvidia,function = "xusb";
+ status = "okay";
+ };
+
+ usb2-1 {
+ nvidia,function = "xusb";
+ status = "okay";
+ };
+
+ usb2-2 {
+ nvidia,function = "xusb";
+ status = "okay";
+ };
+
+ usb2-3 {
+ nvidia,function = "xusb";
+ status = "okay";
+ };
+ };
+
+ pcie {
+ status = "okay";
+
+ pcie-0 {
+ nvidia,function = "pcie-x1";
+ status = "okay";
+ };
+
+ pcie-1 {
+ nvidia,function = "pcie-x4";
+ status = "okay";
+ };
+
+ pcie-2 {
+ nvidia,function = "pcie-x4";
+ status = "okay";
+ };
+
+ pcie-3 {
+ nvidia,function = "pcie-x4";
+ status = "okay";
+ };
+
+ pcie-4 {
+ nvidia,function = "pcie-x4";
+ status = "okay";
+ };
+
+ pcie-5 {
+ nvidia,function = "usb3-ss";
+ status = "okay";
+ };
+
+ pcie-6 {
+ nvidia,function = "usb3-ss";
+ status = "okay";
+ };
+ };
+
+ sata {
+ status = "okay";
+
+ sata-0 {
+ nvidia,function = "sata";
+ status = "okay";
+ };
+ };
+ };
+
+ ports {
+ usb2-0 {
+ status = "okay";
+ mode = "otg";
+ };
+
+ usb2-1 {
+ status = "okay";
+ vbus-supply = <&vdd_5v0_rtl>;
+ mode = "host";
+ };
+
+ usb2-2 {
+ status = "okay";
+ vbus-supply = <&vdd_usb_vbus>;
+ mode = "host";
+ };
+
+ usb2-3 {
+ status = "okay";
+ mode = "host";
+ };
+
+ usb3-0 {
+ status = "okay";
+ nvidia,lanes = "pcie-6";
+ nvidia,port = <1>;
+ };
+
+ usb3-1 {
+ status = "okay";
+ nvidia,lanes = "pcie-5";
+ nvidia,port = <2>;
+ };
+ };
+ };
--
2.7.1

2016-03-04 16:20:14

by Thierry Reding

[permalink] [raw]
Subject: [PATCH v10 6/9] dt-bindings: usb: Add NVIDIA Tegra XUSB controller binding

From: Thierry Reding <[email protected]>

Add device-tree binding documentation for the XUSB controller present
on Tegra124 and later SoCs. This controller supports USB 3.0 via an xHCI
compliant interface.

Based on work by Andrew Bresticker <[email protected]>.

Cc: Rob Herring <[email protected]>
Cc: Pawel Moll <[email protected]>
Cc: Mark Rutland <[email protected]>
Cc: Ian Campbell <[email protected]>
Cc: Kumar Gala <[email protected]>
Cc: Mathias Nyman <[email protected]>
Cc: Greg Kroah-Hartman <[email protected]>
Signed-off-by: Thierry Reding <[email protected]>
---
Changes in v9:
- rename UTMI to USB2 to match hardware documentation
- remove MFD and mailbox components from bindings
- reference XUSB pad controller via phandle

.../bindings/usb/nvidia,tegra124-xusb.txt | 108 +++++++++++++++++++++
1 file changed, 108 insertions(+)
create mode 100644 Documentation/devicetree/bindings/usb/nvidia,tegra124-xusb.txt

diff --git a/Documentation/devicetree/bindings/usb/nvidia,tegra124-xusb.txt b/Documentation/devicetree/bindings/usb/nvidia,tegra124-xusb.txt
new file mode 100644
index 000000000000..79616f9268d8
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/nvidia,tegra124-xusb.txt
@@ -0,0 +1,108 @@
+NVIDIA Tegra xHCI controller
+============================
+
+The Tegra xHCI controller supports both USB2 and USB3 interfaces exposed by
+the Tegra XUSB pad controller.
+
+Required properties:
+--------------------
+- compatible: Must be:
+ - Tegra124: "nvidia,tegra124-xusb"
+ - Tegra132: "nvidia,tegra132-xusb", "nvidia,tegra124-xusb"
+- reg: Must contain the base and length of the xHCI host registers, XUSB FPCI
+ registers and XUSB IPFS registers.
+- reg-names: Must contain the following entries:
+ - "hcd"
+ - "fpci"
+ - "ipfs"
+- interrupts: Must contain the xHCI host interrupt and the mailbox interrupt.
+- clocks: Must contain an entry for each entry in clock-names.
+ See ../clock/clock-bindings.txt for details.
+- clock-names: Must include the following entries:
+ - xusb_host
+ - xusb_host_src
+ - xusb_falcon_src
+ - xusb_ss
+ - xusb_ss_src
+ - xusb_ss_div2
+ - xusb_hs_src
+ - xusb_fs_src
+ - pll_u_480m
+ - clk_m
+ - pll_e
+- resets: Must contain an entry for each entry in reset-names.
+ See ../reset/reset.txt for details.
+- reset-names: Must include the following entries:
+ - xusb_host
+ - xusb_ss
+ - xusb_src
+ Note that xusb_src is the shared reset for xusb_{ss,hs,fs,falcon,host}_src.
+- nvidia,xusb-padctl: phandle to the XUSB pad controller that is used to
+ configure the USB pads used by the XHCI controller
+
+For Tegra124 and Tegra132:
+- avddio-pex-supply: PCIe/USB3 analog logic power supply. Must supply 1.05 V.
+- dvddio-pex-supply: PCIe/USB3 digital logic power supply. Must supply 1.05 V.
+- avdd-usb-supply: USB controller power supply. Must supply 3.3 V.
+- avdd-pll-utmip-supply: UTMI PLL power supply. Must supply 1.8 V.
+- avdd-pll-erefe-supply: PLLE reference PLL power supply. Must supply 1.05 V.
+- avdd-usb-ss-pll-supply: PCIe/USB3 PLL power supply. Must supply 1.05 V.
+- hvdd-usb-ss-supply: High-voltage PCIe/USB3 power supply. Must supply 3.3 V.
+- hvdd-usb-ss-pll-e-supply: High-voltage PLLE power supply. Must supply 3.3 V.
+
+Optional properties:
+--------------------
+- phys: Must contain an entry for each entry in phy-names.
+ See ../phy/phy-bindings.txt for details.
+- phy-names: Should include an entry for each PHY used by the controller. The
+ following PHYs are available:
+ - Tegra124: usb2-0, usb2-1, usb2-2, hsic-0, hsic-1, usb3-0, usb3-1
+ - Tegra132: usb2-0, usb2-1, usb2-2, hsic-0, hsic-1, usb3-0, usb3-1
+
+Example:
+--------
+
+ usb@0,70090000 {
+ compatible = "nvidia,tegra124-xusb";
+ reg = <0x0 0x70090000 0x0 0x8000>,
+ <0x0 0x70098000 0x0 0x1000>,
+ <0x0 0x70099000 0x0 0x1000>;
+ reg-names = "hcd", "fpci", "ipfs";
+
+ interrupts = <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>;
+
+ clocks = <&tegra_car TEGRA124_CLK_XUSB_HOST>,
+ <&tegra_car TEGRA124_CLK_XUSB_HOST_SRC>,
+ <&tegra_car TEGRA124_CLK_XUSB_FALCON_SRC>,
+ <&tegra_car TEGRA124_CLK_XUSB_SS>,
+ <&tegra_car TEGRA124_CLK_XUSB_SS_DIV2>,
+ <&tegra_car TEGRA124_CLK_XUSB_SS_SRC>,
+ <&tegra_car TEGRA124_CLK_XUSB_HS_SRC>,
+ <&tegra_car TEGRA124_CLK_XUSB_FS_SRC>,
+ <&tegra_car TEGRA124_CLK_PLL_U_480M>,
+ <&tegra_car TEGRA124_CLK_CLK_M>,
+ <&tegra_car TEGRA124_CLK_PLL_E>;
+ clock-names = "xusb_host", "xusb_host_src", "xusb_falcon_src",
+ "xusb_ss", "xusb_ss_div2", "xusb_ss_src",
+ "xusb_hs_src", "xusb_fs_src", "pll_u_480m",
+ "clk_m", "pll_e";
+ resets = <&tegra_car 89>, <&tegra_car 156>, <&tegra_car 143>;
+ reset-names = "xusb_host", "xusb_ss", "xusb_src";
+
+ nvidia,xusb-padctl = <&padctl>;
+
+ phys = <&{/padctl@0,7009f000/pads/usb2/usb2-1}>, /* mini-PCIe USB */
+ <&{/padctl@0,7009f000/pads/usb2/usb2-2}>, /* USB A */
+ <&{/padctl@0,7009f000/pads/pcie/pcie-0}>; /* USB A */
+ phy-names = "utmi-1", "utmi-2", "usb3-0";
+
+ avddio-pex-supply = <&vdd_1v05_run>;
+ dvddio-pex-supply = <&vdd_1v05_run>;
+ avdd-usb-supply = <&vdd_3v3_lp0>;
+ avdd-pll-utmip-supply = <&vddio_1v8>;
+ avdd-pll-erefe-supply = <&avdd_1v05_run>;
+ avdd-usb-ss-pll-supply = <&vdd_1v05_run>;
+ hvdd-usb-ss-supply = <&vdd_3v3_lp0>;
+ hvdd-usb-ss-pll-e-supply = <&vdd_3v3_lp0>;
+ };
--
2.7.1

2016-03-04 16:20:18

by Thierry Reding

[permalink] [raw]
Subject: [PATCH v10 4/9] phy: Add Tegra XUSB pad controller support

From: Thierry Reding <[email protected]>

Add a new driver for the XUSB pad controller found on NVIDIA Tegra SoCs.
This hardware block used to be exposed as a pin controller, but it turns
out that this isn't a good fit. The new driver and DT binding much more
accurately describe the hardware and are more flexible in supporting new
SoC generations.

Signed-off-by: Thierry Reding <[email protected]>
---
Changes in v9:
- export public API for direct use by the xHCI driver (replaces mailbox
API which had introduced a nasty circular dependency)

drivers/phy/Kconfig | 2 +
drivers/phy/Makefile | 2 +
drivers/phy/tegra/Kconfig | 8 +
drivers/phy/tegra/Makefile | 5 +
drivers/phy/tegra/xusb-tegra124.c | 1747 ++++++++++++++++++++++++++++
drivers/phy/tegra/xusb.c | 1017 ++++++++++++++++
drivers/phy/tegra/xusb.h | 421 +++++++
drivers/pinctrl/tegra/pinctrl-tegra-xusb.c | 20 +-
include/linux/phy/tegra/xusb.h | 30 +
9 files changed, 3236 insertions(+), 16 deletions(-)
create mode 100644 drivers/phy/tegra/Kconfig
create mode 100644 drivers/phy/tegra/Makefile
create mode 100644 drivers/phy/tegra/xusb-tegra124.c
create mode 100644 drivers/phy/tegra/xusb.c
create mode 100644 drivers/phy/tegra/xusb.h
create mode 100644 include/linux/phy/tegra/xusb.h

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 0124d17bd9fe..4bf65ceb3250 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -407,4 +407,6 @@ config PHY_CYGNUS_PCIE
Enable this to support the Broadcom Cygnus PCIe PHY.
If unsure, say N.

+source "drivers/phy/tegra/Kconfig"
+
endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index c80f09df3bb8..82709141d072 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -50,3 +50,5 @@ obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o
obj-$(CONFIG_PHY_BRCMSTB_SATA) += phy-brcmstb-sata.o
obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o
obj-$(CONFIG_PHY_CYGNUS_PCIE) += phy-bcm-cygnus-pcie.o
+
+obj-$(CONFIG_ARCH_TEGRA) += tegra/
diff --git a/drivers/phy/tegra/Kconfig b/drivers/phy/tegra/Kconfig
new file mode 100644
index 000000000000..a3b1de953fb7
--- /dev/null
+++ b/drivers/phy/tegra/Kconfig
@@ -0,0 +1,8 @@
+config PHY_TEGRA_XUSB
+ tristate "NVIDIA Tegra XUSB pad controller driver"
+ depends on ARCH_TEGRA
+ help
+ Choose this option if you have an NVIDIA Tegra SoC.
+
+ To compile this driver as a module, choose M here: the module will
+ be called phy-tegra-xusb.
diff --git a/drivers/phy/tegra/Makefile b/drivers/phy/tegra/Makefile
new file mode 100644
index 000000000000..31150b4337cd
--- /dev/null
+++ b/drivers/phy/tegra/Makefile
@@ -0,0 +1,5 @@
+obj-$(CONFIG_PHY_TEGRA_XUSB) += phy-tegra-xusb.o
+
+phy-tegra-xusb-y += xusb.o
+phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_124_SOC) += xusb-tegra124.o
+phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_132_SOC) += xusb-tegra124.o
diff --git a/drivers/phy/tegra/xusb-tegra124.c b/drivers/phy/tegra/xusb-tegra124.c
new file mode 100644
index 000000000000..6340d43688d3
--- /dev/null
+++ b/drivers/phy/tegra/xusb-tegra124.c
@@ -0,0 +1,1747 @@
+/*
+ * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mailbox_client.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+
+#include <soc/tegra/fuse.h>
+
+#include "xusb.h"
+
+#define FUSE_SKU_CALIB_HS_CURR_LEVEL_PADX_SHIFT(x) ((x) ? 15 : 0)
+#define FUSE_SKU_CALIB_HS_CURR_LEVEL_PAD_MASK 0x3f
+#define FUSE_SKU_CALIB_HS_IREF_CAP_SHIFT 13
+#define FUSE_SKU_CALIB_HS_IREF_CAP_MASK 0x3
+#define FUSE_SKU_CALIB_HS_SQUELCH_LEVEL_SHIFT 11
+#define FUSE_SKU_CALIB_HS_SQUELCH_LEVEL_MASK 0x3
+#define FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_SHIFT 7
+#define FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_MASK 0xf
+
+#define XUSB_PADCTL_USB2_PORT_CAP 0x008
+#define XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_SHIFT(x) ((x) * 4)
+#define XUSB_PADCTL_USB2_PORT_CAP_PORT_CAP_MASK 0x3
+#define XUSB_PADCTL_USB2_PORT_CAP_DISABLED 0x0
+#define XUSB_PADCTL_USB2_PORT_CAP_HOST 0x1
+#define XUSB_PADCTL_USB2_PORT_CAP_DEVICE 0x2
+#define XUSB_PADCTL_USB2_PORT_CAP_OTG 0x3
+
+#define XUSB_PADCTL_SS_PORT_MAP 0x014
+#define XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(x) (1 << (((x) * 4) + 3))
+#define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_SHIFT(x) ((x) * 4)
+#define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(x) (0x7 << ((x) * 4))
+#define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(x, v) (((v) & 0x7) << ((x) * 4))
+#define XUSB_PADCTL_SS_PORT_MAP_PORT_MAP_MASK 0x7
+
+#define XUSB_PADCTL_ELPG_PROGRAM 0x01c
+#define XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN (1 << 26)
+#define XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 25)
+#define XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN (1 << 24)
+#define XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_VCORE_DOWN(x) (1 << (18 + (x) * 4))
+#define XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN_EARLY(x) \
+ (1 << (17 + (x) * 4))
+#define XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN(x) (1 << (16 + (x) * 4))
+
+#define XUSB_PADCTL_IOPHY_PLL_P0_CTL1 0x040
+#define XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL0_LOCKDET (1 << 19)
+#define XUSB_PADCTL_IOPHY_PLL_P0_CTL1_REFCLK_SEL_MASK (0xf << 12)
+#define XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL_RST (1 << 1)
+
+#define XUSB_PADCTL_IOPHY_PLL_P0_CTL2 0x044
+#define XUSB_PADCTL_IOPHY_PLL_P0_CTL2_REFCLKBUF_EN (1 << 6)
+#define XUSB_PADCTL_IOPHY_PLL_P0_CTL2_TXCLKREF_EN (1 << 5)
+#define XUSB_PADCTL_IOPHY_PLL_P0_CTL2_TXCLKREF_SEL (1 << 4)
+
+#define XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(x) (0x058 + (x) * 4)
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_SHIFT 24
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_MASK 0xff
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_VAL 0x24
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT 16
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_MASK 0x3f
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT 8
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_MASK 0x3f
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_SHIFT 8
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_MASK 0xffff
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_VAL 0xf070
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_SHIFT 4
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_MASK 0xf
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_VAL 0xf
+
+#define XUSB_PADCTL_IOPHY_USB3_PADX_CTL4(x) (0x068 + (x) * 4)
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT 24
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_MASK 0x1f
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT 16
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_MASK 0x7f
+#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_VAL 0x002008ee
+
+#define XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL2(x) ((x) < 2 ? 0x078 + (x) * 4 : \
+ 0x0f8 + (x) * 4)
+#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_SHIFT 28
+#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_MASK 0x3
+#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_VAL 0x1
+
+#define XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL5(x) ((x) < 2 ? 0x090 + (x) * 4 : \
+ 0x11c + (x) * 4)
+#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL5_RX_QEYE_EN (1 << 8)
+
+#define XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL6(x) ((x) < 2 ? 0x098 + (x) * 4 : \
+ 0x128 + (x) * 4)
+#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT 24
+#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_G_Z_MASK 0x3f
+#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_TAP_MASK 0x1f
+#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_AMP_MASK 0x7f
+#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT 16
+#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK 0xff
+#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_G_Z 0x21
+#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_TAP 0x32
+#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_AMP 0x33
+#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_CTLE_Z 0x48
+#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_LATCH_G_Z 0xa1
+
+#define XUSB_PADCTL_USB2_OTG_PADX_CTL0(x) (0x0a0 + (x) * 4)
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD_ZI (1 << 21)
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD2 (1 << 20)
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD (1 << 19)
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_SHIFT 14
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_MASK 0x3
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_VAL(x) ((x) ? 0x0 : 0x3)
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_SHIFT 6
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_MASK 0x3f
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_VAL 0x0e
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_SHIFT 0
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_MASK 0x3f
+
+#define XUSB_PADCTL_USB2_OTG_PADX_CTL1(x) (0x0ac + (x) * 4)
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_HS_IREF_CAP_SHIFT 9
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_HS_IREF_CAP_MASK 0x3
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_SHIFT 3
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_MASK 0x7
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR (1 << 2)
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DISC_FORCE_POWERUP (1 << 1)
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_CHRP_FORCE_POWERUP (1 << 0)
+
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 0x0b8
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD (1 << 12)
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_SHIFT 2
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_MASK 0x7
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_VAL 0x5
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_SHIFT 0
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_MASK 0x3
+
+#define XUSB_PADCTL_HSIC_PADX_CTL0(x) (0x0c0 + (x) * 4)
+#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_SHIFT 12
+#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_MASK 0x7
+#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_SHIFT 8
+#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_MASK 0x7
+#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_SHIFT 4
+#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_MASK 0x7
+#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_SHIFT 0
+#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_MASK 0x7
+
+#define XUSB_PADCTL_HSIC_PADX_CTL1(x) (0x0c8 + (x) * 4)
+#define XUSB_PADCTL_HSIC_PAD_CTL1_RPU_STROBE (1 << 10)
+#define XUSB_PADCTL_HSIC_PAD_CTL1_RPU_DATA (1 << 9)
+#define XUSB_PADCTL_HSIC_PAD_CTL1_RPD_STROBE (1 << 8)
+#define XUSB_PADCTL_HSIC_PAD_CTL1_RPD_DATA (1 << 7)
+#define XUSB_PADCTL_HSIC_PAD_CTL1_PD_ZI (1 << 5)
+#define XUSB_PADCTL_HSIC_PAD_CTL1_PD_RX (1 << 4)
+#define XUSB_PADCTL_HSIC_PAD_CTL1_PD_TRX (1 << 3)
+#define XUSB_PADCTL_HSIC_PAD_CTL1_PD_TX (1 << 2)
+#define XUSB_PADCTL_HSIC_PAD_CTL1_AUTO_TERM_EN (1 << 0)
+
+#define XUSB_PADCTL_HSIC_PADX_CTL2(x) (0x0d0 + (x) * 4)
+#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_SHIFT 4
+#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_MASK 0x7
+#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_SHIFT 0
+#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_MASK 0x7
+
+#define XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL 0x0e0
+#define XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL_STRB_TRIM_MASK 0x1f
+
+#define XUSB_PADCTL_USB3_PAD_MUX 0x134
+#define XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(x) (1 << (1 + (x)))
+#define XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(x) (1 << (6 + (x)))
+
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1 0x138
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_LOCKDET (1 << 27)
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_MODE (1 << 24)
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL0_REFCLK_NDIV_SHIFT 20
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL0_REFCLK_NDIV_MASK 0x3
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD (1 << 3)
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_RST (1 << 1)
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_IDDQ (1 << 0)
+
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2 0x13c
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL1_CP_CNTL_SHIFT 20
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL1_CP_CNTL_MASK 0xf
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL0_CP_CNTL_SHIFT 16
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL0_CP_CNTL_MASK 0xf
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_TCLKOUT_EN (1 << 12)
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_TXCLKREF_SEL (1 << 4)
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_XDIGCLK_SEL_SHIFT 0
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_XDIGCLK_SEL_MASK 0x7
+
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL3 0x140
+#define XUSB_PADCTL_IOPHY_PLL_S0_CTL3_RCAL_BYPASS (1 << 7)
+
+#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1 0x148
+#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD (1 << 1)
+#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ (1 << 0)
+
+#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL2 0x14c
+
+#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL5 0x158
+
+#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL6 0x15c
+
+struct tegra124_xusb_fuse_calibration {
+ u32 hs_curr_level[3];
+ u32 hs_iref_cap;
+ u32 hs_term_range_adj;
+ u32 hs_squelch_level;
+};
+
+struct tegra124_xusb_padctl {
+ struct tegra_xusb_padctl base;
+
+ struct tegra124_xusb_fuse_calibration fuse;
+};
+
+static inline struct tegra124_xusb_padctl *
+to_tegra124_xusb_padctl(struct tegra_xusb_padctl *padctl)
+{
+ return container_of(padctl, struct tegra124_xusb_padctl, base);
+}
+
+static int tegra124_xusb_padctl_enable(struct tegra_xusb_padctl *padctl)
+{
+ u32 value;
+
+ mutex_lock(&padctl->lock);
+
+ if (padctl->enable++ > 0)
+ goto out;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value &= ~XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN;
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value &= ~XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY;
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value &= ~XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN;
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+out:
+ mutex_unlock(&padctl->lock);
+ return 0;
+}
+
+static int tegra124_xusb_padctl_disable(struct tegra_xusb_padctl *padctl)
+{
+ u32 value;
+
+ mutex_lock(&padctl->lock);
+
+ if (WARN_ON(padctl->enable == 0))
+ goto out;
+
+ if (--padctl->enable > 0)
+ goto out;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value |= XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN;
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value |= XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY;
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value |= XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN;
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+out:
+ mutex_unlock(&padctl->lock);
+ return 0;
+}
+
+static int tegra124_usb3_save_context(struct tegra_xusb_padctl *padctl,
+ unsigned int index)
+{
+ struct tegra_xusb_usb3_port *port;
+ struct tegra_xusb_lane *lane;
+ u32 value, offset;
+
+ port = tegra_xusb_find_usb3_port(padctl, index);
+ if (!port)
+ return -ENODEV;
+
+ port->context_saved = true;
+ lane = port->base.lane;
+
+ if (lane->pad == padctl->pcie)
+ offset = XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL6(lane->index);
+ else
+ offset = XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL6;
+
+ value = padctl_readl(padctl, offset);
+ value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK <<
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT);
+ value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_TAP <<
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT;
+ padctl_writel(padctl, value, offset);
+
+ value = padctl_readl(padctl, offset) >>
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT;
+ port->tap1 = value & XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_TAP_MASK;
+
+ value = padctl_readl(padctl, offset);
+ value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK <<
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT);
+ value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_AMP <<
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT;
+ padctl_writel(padctl, value, offset);
+
+ value = padctl_readl(padctl, offset) >>
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT;
+ port->amp = value & XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_AMP_MASK;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_USB3_PADX_CTL4(index));
+ value &= ~((XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_MASK <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT) |
+ (XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_MASK <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT));
+ value |= (port->tap1 <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT) |
+ (port->amp <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT);
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_USB3_PADX_CTL4(index));
+
+ value = padctl_readl(padctl, offset);
+ value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK <<
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT);
+ value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_LATCH_G_Z <<
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT;
+ padctl_writel(padctl, value, offset);
+
+ value = padctl_readl(padctl, offset);
+ value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK <<
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT);
+ value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_G_Z <<
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT;
+ padctl_writel(padctl, value, offset);
+
+ value = padctl_readl(padctl, offset) >>
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT;
+ port->ctle_g = value &
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_G_Z_MASK;
+
+ value = padctl_readl(padctl, offset);
+ value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK <<
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT);
+ value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_CTLE_Z <<
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT;
+ padctl_writel(padctl, value, offset);
+
+ value = padctl_readl(padctl, offset) >>
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT;
+ port->ctle_z = value &
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_G_Z_MASK;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(index));
+ value &= ~((XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_MASK <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT) |
+ (XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_MASK <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT));
+ value |= (port->ctle_g <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT) |
+ (port->ctle_z <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT);
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(index));
+
+ return 0;
+}
+
+static int tegra124_hsic_set_idle(struct tegra_xusb_padctl *padctl,
+ unsigned int index, bool idle)
+{
+ u32 value;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL1(index));
+
+ if (idle)
+ value |= XUSB_PADCTL_HSIC_PAD_CTL1_RPD_DATA |
+ XUSB_PADCTL_HSIC_PAD_CTL1_RPU_STROBE;
+ else
+ value &= ~(XUSB_PADCTL_HSIC_PAD_CTL1_RPD_DATA |
+ XUSB_PADCTL_HSIC_PAD_CTL1_RPU_STROBE);
+
+ padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL1(index));
+
+ return 0;
+}
+
+#define TEGRA124_LANE(_name, _offset, _shift, _mask, _type) \
+ { \
+ .name = _name, \
+ .offset = _offset, \
+ .shift = _shift, \
+ .mask = _mask, \
+ .num_funcs = ARRAY_SIZE(tegra124_##_type##_functions), \
+ .funcs = tegra124_##_type##_functions, \
+ }
+
+static const char * const tegra124_usb2_functions[] = {
+ "snps",
+ "xusb",
+ "uart",
+};
+
+static const struct tegra_xusb_lane_soc tegra124_usb2_lanes[] = {
+ TEGRA124_LANE("usb2-0", 0x004, 0, 0x3, usb2),
+ TEGRA124_LANE("usb2-1", 0x004, 2, 0x3, usb2),
+ TEGRA124_LANE("usb2-2", 0x004, 4, 0x3, usb2),
+};
+
+static struct tegra_xusb_lane *
+tegra124_usb2_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np,
+ unsigned int index)
+{
+ struct tegra_xusb_usb2_lane *usb2;
+ int err;
+
+ usb2 = kzalloc(sizeof(*usb2), GFP_KERNEL);
+ if (!usb2)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&usb2->base.list);
+ usb2->base.soc = &pad->soc->lanes[index];
+ usb2->base.index = index;
+ usb2->base.pad = pad;
+ usb2->base.np = np;
+
+ err = tegra_xusb_lane_parse_dt(&usb2->base, np);
+ if (err < 0) {
+ kfree(usb2);
+ return ERR_PTR(err);
+ }
+
+ return &usb2->base;
+}
+
+static void tegra124_usb2_lane_remove(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_usb2_lane *usb2 = to_usb2_lane(lane);
+
+ kfree(usb2);
+}
+
+static const struct tegra_xusb_lane_ops tegra124_usb2_lane_ops = {
+ .probe = tegra124_usb2_lane_probe,
+ .remove = tegra124_usb2_lane_remove,
+};
+
+static int tegra124_usb2_phy_init(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+ return tegra124_xusb_padctl_enable(lane->pad->padctl);
+}
+
+static int tegra124_usb2_phy_exit(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+ return tegra124_xusb_padctl_disable(lane->pad->padctl);
+}
+
+static int tegra124_usb2_phy_power_on(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_usb2_lane *usb2 = to_usb2_lane(lane);
+ struct tegra_xusb_usb2_pad *pad = to_usb2_pad(lane->pad);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ struct tegra124_xusb_padctl *priv;
+ struct tegra_xusb_usb2_port *port;
+ unsigned int index = lane->index;
+ u32 value;
+ int err;
+
+ port = tegra_xusb_find_usb2_port(padctl, index);
+ if (!port) {
+ dev_err(&phy->dev, "no port found for USB2 lane %u\n", index);
+ return -ENODEV;
+ }
+
+ priv = to_tegra124_xusb_padctl(padctl);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
+ value &= ~((XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_MASK <<
+ XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_SHIFT) |
+ (XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_MASK <<
+ XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_SHIFT));
+ value |= (priv->fuse.hs_squelch_level <<
+ XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_SHIFT) |
+ (XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_VAL <<
+ XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_SHIFT);
+ padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_PORT_CAP);
+ value &= ~(XUSB_PADCTL_USB2_PORT_CAP_PORT_CAP_MASK <<
+ XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_SHIFT(index));
+ value |= XUSB_PADCTL_USB2_PORT_CAP_HOST <<
+ XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_SHIFT(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_USB2_PORT_CAP);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
+ value &= ~((XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_MASK <<
+ XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_SHIFT) |
+ (XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_MASK <<
+ XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_SHIFT) |
+ (XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_MASK <<
+ XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_SHIFT) |
+ XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD |
+ XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD2 |
+ XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD_ZI);
+ value |= (priv->fuse.hs_curr_level[index] +
+ usb2->hs_curr_level_offset) <<
+ XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_SHIFT;
+ value |= XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_VAL <<
+ XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_SHIFT;
+ value |= XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_VAL(index) <<
+ XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_SHIFT;
+ padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
+ value &= ~((XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_MASK <<
+ XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_SHIFT) |
+ (XUSB_PADCTL_USB2_OTG_PAD_CTL1_HS_IREF_CAP_MASK <<
+ XUSB_PADCTL_USB2_OTG_PAD_CTL1_HS_IREF_CAP_SHIFT) |
+ XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR |
+ XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_CHRP_FORCE_POWERUP |
+ XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DISC_FORCE_POWERUP);
+ value |= (priv->fuse.hs_term_range_adj <<
+ XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_SHIFT) |
+ (priv->fuse.hs_iref_cap <<
+ XUSB_PADCTL_USB2_OTG_PAD_CTL1_HS_IREF_CAP_SHIFT);
+ padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
+
+ err = regulator_enable(port->supply);
+ if (err)
+ return err;
+
+ mutex_lock(&pad->lock);
+
+ if (pad->enable++ > 0)
+ goto out;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
+ value &= ~XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD;
+ padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
+
+out:
+ mutex_unlock(&pad->lock);
+ return 0;
+}
+
+static int tegra124_usb2_phy_power_off(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_usb2_pad *pad = to_usb2_pad(lane->pad);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ struct tegra_xusb_usb2_port *port;
+ u32 value;
+
+ port = tegra_xusb_find_usb2_port(padctl, lane->index);
+ if (!port) {
+ dev_err(&phy->dev, "no port found for USB2 lane %u\n",
+ lane->index);
+ return -ENODEV;
+ }
+
+ mutex_lock(&pad->lock);
+
+ if (WARN_ON(pad->enable == 0))
+ goto out;
+
+ if (--pad->enable > 0)
+ goto out;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
+ value |= XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD;
+ padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
+
+out:
+ regulator_disable(port->supply);
+ mutex_unlock(&pad->lock);
+ return 0;
+}
+
+static const struct phy_ops tegra124_usb2_phy_ops = {
+ .init = tegra124_usb2_phy_init,
+ .exit = tegra124_usb2_phy_exit,
+ .power_on = tegra124_usb2_phy_power_on,
+ .power_off = tegra124_usb2_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static struct tegra_xusb_pad *
+tegra124_usb2_pad_probe(struct tegra_xusb_padctl *padctl,
+ const struct tegra_xusb_pad_soc *soc,
+ struct device_node *np)
+{
+ struct tegra_xusb_usb2_pad *usb2;
+ struct tegra_xusb_pad *pad;
+ int err;
+
+ usb2 = kzalloc(sizeof(*usb2), GFP_KERNEL);
+ if (!usb2)
+ return ERR_PTR(-ENOMEM);
+
+ mutex_init(&usb2->lock);
+
+ pad = &usb2->base;
+ pad->ops = &tegra124_usb2_lane_ops;
+ pad->soc = soc;
+
+ err = tegra_xusb_pad_init(pad, padctl, np);
+ if (err < 0)
+ goto free;
+
+ err = tegra_xusb_pad_register(pad, &tegra124_usb2_phy_ops);
+ if (err < 0)
+ goto unregister;
+
+ dev_set_drvdata(&pad->dev, pad);
+
+ return pad;
+
+unregister:
+ device_unregister(&pad->dev);
+free:
+ kfree(usb2);
+ return ERR_PTR(err);
+}
+
+static void tegra124_usb2_pad_remove(struct tegra_xusb_pad *pad)
+{
+ struct tegra_xusb_usb2_pad *usb2 = to_usb2_pad(pad);
+
+ kfree(usb2);
+}
+
+static const struct tegra_xusb_pad_ops tegra124_usb2_ops = {
+ .probe = tegra124_usb2_pad_probe,
+ .remove = tegra124_usb2_pad_remove,
+};
+
+static const struct tegra_xusb_pad_soc tegra124_usb2_pad = {
+ .name = "usb2",
+ .num_lanes = ARRAY_SIZE(tegra124_usb2_lanes),
+ .lanes = tegra124_usb2_lanes,
+ .ops = &tegra124_usb2_ops,
+};
+
+static const char * const tegra124_ulpi_functions[] = {
+ "snps",
+ "xusb",
+};
+
+static const struct tegra_xusb_lane_soc tegra124_ulpi_lanes[] = {
+ TEGRA124_LANE("ulpi-0", 0x004, 12, 0x1, ulpi),
+};
+
+static struct tegra_xusb_lane *
+tegra124_ulpi_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np,
+ unsigned int index)
+{
+ struct tegra_xusb_ulpi_lane *ulpi;
+ int err;
+
+ ulpi = kzalloc(sizeof(*ulpi), GFP_KERNEL);
+ if (!ulpi)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&ulpi->base.list);
+ ulpi->base.soc = &pad->soc->lanes[index];
+ ulpi->base.index = index;
+ ulpi->base.pad = pad;
+ ulpi->base.np = np;
+
+ err = tegra_xusb_lane_parse_dt(&ulpi->base, np);
+ if (err < 0) {
+ kfree(ulpi);
+ return ERR_PTR(err);
+ }
+
+ return &ulpi->base;
+}
+
+static void tegra124_ulpi_lane_remove(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_ulpi_lane *ulpi = to_ulpi_lane(lane);
+
+ kfree(ulpi);
+}
+
+static const struct tegra_xusb_lane_ops tegra124_ulpi_lane_ops = {
+ .probe = tegra124_ulpi_lane_probe,
+ .remove = tegra124_ulpi_lane_remove,
+};
+
+static int tegra124_ulpi_phy_init(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+ return tegra124_xusb_padctl_enable(lane->pad->padctl);
+}
+
+static int tegra124_ulpi_phy_exit(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+ return tegra124_xusb_padctl_disable(lane->pad->padctl);
+}
+
+static int tegra124_ulpi_phy_power_on(struct phy *phy)
+{
+ return 0;
+}
+
+static int tegra124_ulpi_phy_power_off(struct phy *phy)
+{
+ return 0;
+}
+
+static const struct phy_ops tegra124_ulpi_phy_ops = {
+ .init = tegra124_ulpi_phy_init,
+ .exit = tegra124_ulpi_phy_exit,
+ .power_on = tegra124_ulpi_phy_power_on,
+ .power_off = tegra124_ulpi_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static struct tegra_xusb_pad *
+tegra124_ulpi_pad_probe(struct tegra_xusb_padctl *padctl,
+ const struct tegra_xusb_pad_soc *soc,
+ struct device_node *np)
+{
+ struct tegra_xusb_ulpi_pad *ulpi;
+ struct tegra_xusb_pad *pad;
+ int err;
+
+ ulpi = kzalloc(sizeof(*ulpi), GFP_KERNEL);
+ if (!ulpi)
+ return ERR_PTR(-ENOMEM);
+
+ pad = &ulpi->base;
+ pad->ops = &tegra124_ulpi_lane_ops;
+ pad->soc = soc;
+
+ err = tegra_xusb_pad_init(pad, padctl, np);
+ if (err < 0)
+ goto free;
+
+ err = tegra_xusb_pad_register(pad, &tegra124_ulpi_phy_ops);
+ if (err < 0)
+ goto unregister;
+
+ dev_set_drvdata(&pad->dev, pad);
+
+ return pad;
+
+unregister:
+ device_unregister(&pad->dev);
+free:
+ kfree(ulpi);
+ return ERR_PTR(err);
+}
+
+static void tegra124_ulpi_pad_remove(struct tegra_xusb_pad *pad)
+{
+ struct tegra_xusb_ulpi_pad *ulpi = to_ulpi_pad(pad);
+
+ kfree(ulpi);
+}
+
+static const struct tegra_xusb_pad_ops tegra124_ulpi_ops = {
+ .probe = tegra124_ulpi_pad_probe,
+ .remove = tegra124_ulpi_pad_remove,
+};
+
+static const struct tegra_xusb_pad_soc tegra124_ulpi_pad = {
+ .name = "ulpi",
+ .num_lanes = ARRAY_SIZE(tegra124_ulpi_lanes),
+ .lanes = tegra124_ulpi_lanes,
+ .ops = &tegra124_ulpi_ops,
+};
+
+static const char * const tegra124_hsic_functions[] = {
+ "snps",
+ "xusb",
+};
+
+static const struct tegra_xusb_lane_soc tegra124_hsic_lanes[] = {
+ TEGRA124_LANE("hsic-0", 0x004, 14, 0x1, hsic),
+ TEGRA124_LANE("hsic-1", 0x004, 15, 0x1, hsic),
+};
+
+static struct tegra_xusb_lane *
+tegra124_hsic_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np,
+ unsigned int index)
+{
+ struct tegra_xusb_hsic_lane *hsic;
+ int err;
+
+ hsic = kzalloc(sizeof(*hsic), GFP_KERNEL);
+ if (!hsic)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&hsic->base.list);
+ hsic->base.soc = &pad->soc->lanes[index];
+ hsic->base.index = index;
+ hsic->base.pad = pad;
+ hsic->base.np = np;
+
+ err = tegra_xusb_lane_parse_dt(&hsic->base, np);
+ if (err < 0) {
+ kfree(hsic);
+ return ERR_PTR(err);
+ }
+
+ return &hsic->base;
+}
+
+static void tegra124_hsic_lane_remove(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_hsic_lane *hsic = to_hsic_lane(lane);
+
+ kfree(hsic);
+}
+
+static const struct tegra_xusb_lane_ops tegra124_hsic_lane_ops = {
+ .probe = tegra124_hsic_lane_probe,
+ .remove = tegra124_hsic_lane_remove,
+};
+
+static int tegra124_hsic_phy_init(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+ return tegra124_xusb_padctl_enable(lane->pad->padctl);
+}
+
+static int tegra124_hsic_phy_exit(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+ return tegra124_xusb_padctl_disable(lane->pad->padctl);
+}
+
+static int tegra124_hsic_phy_power_on(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_hsic_lane *hsic = to_hsic_lane(lane);
+ struct tegra_xusb_hsic_pad *pad = to_hsic_pad(lane->pad);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+ int err;
+
+ err = regulator_enable(pad->supply);
+ if (err)
+ return err;
+
+ padctl_writel(padctl, hsic->strobe_trim,
+ XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL1(index));
+
+ if (hsic->auto_term)
+ value |= XUSB_PADCTL_HSIC_PAD_CTL1_AUTO_TERM_EN;
+ else
+ value &= ~XUSB_PADCTL_HSIC_PAD_CTL1_AUTO_TERM_EN;
+
+ padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL1(index));
+
+ value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL0(index));
+ value &= ~((XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_MASK <<
+ XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_SHIFT) |
+ (XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_MASK <<
+ XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_SHIFT) |
+ (XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_MASK <<
+ XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_SHIFT) |
+ (XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_MASK <<
+ XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_SHIFT));
+ value |= (hsic->tx_rtune_n <<
+ XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_SHIFT) |
+ (hsic->tx_rtune_p <<
+ XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_SHIFT) |
+ (hsic->tx_rslew_n <<
+ XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_SHIFT) |
+ (hsic->tx_rslew_p <<
+ XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_SHIFT);
+ padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL0(index));
+
+ value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL2(index));
+ value &= ~((XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_MASK <<
+ XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_SHIFT) |
+ (XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_MASK <<
+ XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_SHIFT));
+ value |= (hsic->rx_strobe_trim <<
+ XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_SHIFT) |
+ (hsic->rx_data_trim <<
+ XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_SHIFT);
+ padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL2(index));
+
+ value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL1(index));
+ value &= ~(XUSB_PADCTL_HSIC_PAD_CTL1_RPD_STROBE |
+ XUSB_PADCTL_HSIC_PAD_CTL1_RPU_DATA |
+ XUSB_PADCTL_HSIC_PAD_CTL1_PD_RX |
+ XUSB_PADCTL_HSIC_PAD_CTL1_PD_ZI |
+ XUSB_PADCTL_HSIC_PAD_CTL1_PD_TRX |
+ XUSB_PADCTL_HSIC_PAD_CTL1_PD_TX);
+ value |= XUSB_PADCTL_HSIC_PAD_CTL1_RPD_DATA |
+ XUSB_PADCTL_HSIC_PAD_CTL1_RPU_STROBE;
+ padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL1(index));
+
+ return 0;
+}
+
+static int tegra124_hsic_phy_power_off(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_hsic_pad *pad = to_hsic_pad(lane->pad);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL1(index));
+ value |= XUSB_PADCTL_HSIC_PAD_CTL1_PD_RX |
+ XUSB_PADCTL_HSIC_PAD_CTL1_PD_ZI |
+ XUSB_PADCTL_HSIC_PAD_CTL1_PD_TRX |
+ XUSB_PADCTL_HSIC_PAD_CTL1_PD_TX;
+ padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL1(index));
+
+ regulator_disable(pad->supply);
+
+ return 0;
+}
+
+static const struct phy_ops tegra124_hsic_phy_ops = {
+ .init = tegra124_hsic_phy_init,
+ .exit = tegra124_hsic_phy_exit,
+ .power_on = tegra124_hsic_phy_power_on,
+ .power_off = tegra124_hsic_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static struct tegra_xusb_pad *
+tegra124_hsic_pad_probe(struct tegra_xusb_padctl *padctl,
+ const struct tegra_xusb_pad_soc *soc,
+ struct device_node *np)
+{
+ struct tegra_xusb_hsic_pad *hsic;
+ struct tegra_xusb_pad *pad;
+ int err;
+
+ hsic = kzalloc(sizeof(*hsic), GFP_KERNEL);
+ if (!hsic)
+ return ERR_PTR(-ENOMEM);
+
+ pad = &hsic->base;
+ pad->ops = &tegra124_hsic_lane_ops;
+ pad->soc = soc;
+
+ err = tegra_xusb_pad_init(pad, padctl, np);
+ if (err < 0)
+ goto free;
+
+ err = tegra_xusb_pad_register(pad, &tegra124_hsic_phy_ops);
+ if (err < 0)
+ goto unregister;
+
+ dev_set_drvdata(&pad->dev, pad);
+
+ return pad;
+
+unregister:
+ device_unregister(&pad->dev);
+free:
+ kfree(hsic);
+ return ERR_PTR(err);
+}
+
+static void tegra124_hsic_pad_remove(struct tegra_xusb_pad *pad)
+{
+ struct tegra_xusb_hsic_pad *hsic = to_hsic_pad(pad);
+
+ kfree(hsic);
+}
+
+static const struct tegra_xusb_pad_ops tegra124_hsic_ops = {
+ .probe = tegra124_hsic_pad_probe,
+ .remove = tegra124_hsic_pad_remove,
+};
+
+static const struct tegra_xusb_pad_soc tegra124_hsic_pad = {
+ .name = "hsic",
+ .num_lanes = ARRAY_SIZE(tegra124_hsic_lanes),
+ .lanes = tegra124_hsic_lanes,
+ .ops = &tegra124_hsic_ops,
+};
+
+static const char * const tegra124_pcie_functions[] = {
+ "pcie",
+ "usb3-ss",
+ "sata",
+};
+
+static const struct tegra_xusb_lane_soc tegra124_pcie_lanes[] = {
+ TEGRA124_LANE("pcie-0", 0x134, 16, 0x3, pcie),
+ TEGRA124_LANE("pcie-1", 0x134, 18, 0x3, pcie),
+ TEGRA124_LANE("pcie-2", 0x134, 20, 0x3, pcie),
+ TEGRA124_LANE("pcie-3", 0x134, 22, 0x3, pcie),
+ TEGRA124_LANE("pcie-4", 0x134, 24, 0x3, pcie),
+};
+
+static struct tegra_xusb_lane *
+tegra124_pcie_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np,
+ unsigned int index)
+{
+ struct tegra_xusb_pcie_lane *pcie;
+ int err;
+
+ pcie = kzalloc(sizeof(*pcie), GFP_KERNEL);
+ if (!pcie)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&pcie->base.list);
+ pcie->base.soc = &pad->soc->lanes[index];
+ pcie->base.index = index;
+ pcie->base.pad = pad;
+ pcie->base.np = np;
+
+ err = tegra_xusb_lane_parse_dt(&pcie->base, np);
+ if (err < 0) {
+ kfree(pcie);
+ return ERR_PTR(err);
+ }
+
+ return &pcie->base;
+}
+
+static void tegra124_pcie_lane_remove(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_pcie_lane *pcie = to_pcie_lane(lane);
+
+ kfree(pcie);
+}
+
+static const struct tegra_xusb_lane_ops tegra124_pcie_lane_ops = {
+ .probe = tegra124_pcie_lane_probe,
+ .remove = tegra124_pcie_lane_remove,
+};
+
+static int tegra124_pcie_phy_init(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+ return tegra124_xusb_padctl_enable(lane->pad->padctl);
+}
+
+static int tegra124_pcie_phy_exit(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+ return tegra124_xusb_padctl_disable(lane->pad->padctl);
+}
+
+static int tegra124_pcie_phy_power_on(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned long timeout;
+ int err = -ETIMEDOUT;
+ u32 value;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
+ value &= ~XUSB_PADCTL_IOPHY_PLL_P0_CTL1_REFCLK_SEL_MASK;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_P0_CTL2);
+ value |= XUSB_PADCTL_IOPHY_PLL_P0_CTL2_REFCLKBUF_EN |
+ XUSB_PADCTL_IOPHY_PLL_P0_CTL2_TXCLKREF_EN |
+ XUSB_PADCTL_IOPHY_PLL_P0_CTL2_TXCLKREF_SEL;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_P0_CTL2);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
+ value |= XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL_RST;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
+
+ timeout = jiffies + msecs_to_jiffies(50);
+
+ while (time_before(jiffies, timeout)) {
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
+ if (value & XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL0_LOCKDET) {
+ err = 0;
+ break;
+ }
+
+ usleep_range(100, 200);
+ }
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+ value |= XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->index);
+ padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+
+ return err;
+}
+
+static int tegra124_pcie_phy_power_off(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ u32 value;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+ value &= ~XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->index);
+ padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
+ value &= ~XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL_RST;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
+
+ return 0;
+}
+
+static const struct phy_ops tegra124_pcie_phy_ops = {
+ .init = tegra124_pcie_phy_init,
+ .exit = tegra124_pcie_phy_exit,
+ .power_on = tegra124_pcie_phy_power_on,
+ .power_off = tegra124_pcie_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static struct tegra_xusb_pad *
+tegra124_pcie_pad_probe(struct tegra_xusb_padctl *padctl,
+ const struct tegra_xusb_pad_soc *soc,
+ struct device_node *np)
+{
+ struct tegra_xusb_pcie_pad *pcie;
+ struct tegra_xusb_pad *pad;
+ int err;
+
+ pcie = kzalloc(sizeof(*pcie), GFP_KERNEL);
+ if (!pcie)
+ return ERR_PTR(-ENOMEM);
+
+ pad = &pcie->base;
+ pad->ops = &tegra124_pcie_lane_ops;
+ pad->soc = soc;
+
+ err = tegra_xusb_pad_init(pad, padctl, np);
+ if (err < 0)
+ goto free;
+
+ err = tegra_xusb_pad_register(pad, &tegra124_pcie_phy_ops);
+ if (err < 0)
+ goto unregister;
+
+ dev_set_drvdata(&pad->dev, pad);
+
+ return pad;
+
+unregister:
+ device_unregister(&pad->dev);
+free:
+ kfree(pcie);
+ return ERR_PTR(err);
+}
+
+static void tegra124_pcie_pad_remove(struct tegra_xusb_pad *pad)
+{
+ struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(pad);
+
+ kfree(pcie);
+}
+
+static const struct tegra_xusb_pad_ops tegra124_pcie_ops = {
+ .probe = tegra124_pcie_pad_probe,
+ .remove = tegra124_pcie_pad_remove,
+};
+
+static const struct tegra_xusb_pad_soc tegra124_pcie_pad = {
+ .name = "pcie",
+ .num_lanes = ARRAY_SIZE(tegra124_pcie_lanes),
+ .lanes = tegra124_pcie_lanes,
+ .ops = &tegra124_pcie_ops,
+};
+
+static const struct tegra_xusb_lane_soc tegra124_sata_lanes[] = {
+ TEGRA124_LANE("sata-0", 0x134, 26, 0x3, pcie),
+};
+
+static struct tegra_xusb_lane *
+tegra124_sata_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np,
+ unsigned int index)
+{
+ struct tegra_xusb_sata_lane *sata;
+ int err;
+
+ sata = kzalloc(sizeof(*sata), GFP_KERNEL);
+ if (!sata)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&sata->base.list);
+ sata->base.soc = &pad->soc->lanes[index];
+ sata->base.index = index;
+ sata->base.pad = pad;
+ sata->base.np = np;
+
+ err = tegra_xusb_lane_parse_dt(&sata->base, np);
+ if (err < 0) {
+ kfree(sata);
+ return ERR_PTR(err);
+ }
+
+ return &sata->base;
+}
+
+static void tegra124_sata_lane_remove(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_sata_lane *sata = to_sata_lane(lane);
+
+ kfree(sata);
+}
+
+static const struct tegra_xusb_lane_ops tegra124_sata_lane_ops = {
+ .probe = tegra124_sata_lane_probe,
+ .remove = tegra124_sata_lane_remove,
+};
+
+static int tegra124_sata_phy_init(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+ return tegra124_xusb_padctl_enable(lane->pad->padctl);
+}
+
+static int tegra124_sata_phy_exit(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+ return tegra124_xusb_padctl_disable(lane->pad->padctl);
+}
+
+static int tegra124_sata_phy_power_on(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned long timeout;
+ int err = -ETIMEDOUT;
+ u32 value;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1);
+ value &= ~XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD;
+ value &= ~XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+ value &= ~XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD;
+ value &= ~XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_IDDQ;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+ value |= XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_MODE;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+ value |= XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_RST;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+
+ timeout = jiffies + msecs_to_jiffies(50);
+
+ while (time_before(jiffies, timeout)) {
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+ if (value & XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_LOCKDET) {
+ err = 0;
+ break;
+ }
+
+ usleep_range(100, 200);
+ }
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+ value |= XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(lane->index);
+ padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+
+ return err;
+}
+
+static int tegra124_sata_phy_power_off(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ u32 value;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+ value &= ~XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(lane->index);
+ padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+ value &= ~XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_RST;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+ value &= ~XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_MODE;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+ value |= XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD;
+ value |= XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_IDDQ;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1);
+ value |= ~XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD;
+ value |= ~XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1);
+
+ return 0;
+}
+
+static const struct phy_ops tegra124_sata_phy_ops = {
+ .init = tegra124_sata_phy_init,
+ .exit = tegra124_sata_phy_exit,
+ .power_on = tegra124_sata_phy_power_on,
+ .power_off = tegra124_sata_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static struct tegra_xusb_pad *
+tegra124_sata_pad_probe(struct tegra_xusb_padctl *padctl,
+ const struct tegra_xusb_pad_soc *soc,
+ struct device_node *np)
+{
+ struct tegra_xusb_sata_pad *sata;
+ struct tegra_xusb_pad *pad;
+ int err;
+
+ sata = kzalloc(sizeof(*sata), GFP_KERNEL);
+ if (!sata)
+ return ERR_PTR(-ENOMEM);
+
+ pad = &sata->base;
+ pad->ops = &tegra124_sata_lane_ops;
+ pad->soc = soc;
+
+ err = tegra_xusb_pad_init(pad, padctl, np);
+ if (err < 0)
+ goto free;
+
+ err = tegra_xusb_pad_register(pad, &tegra124_sata_phy_ops);
+ if (err < 0)
+ goto unregister;
+
+ dev_set_drvdata(&pad->dev, pad);
+
+ return pad;
+
+unregister:
+ device_unregister(&pad->dev);
+free:
+ kfree(sata);
+ return ERR_PTR(err);
+}
+
+static void tegra124_sata_pad_remove(struct tegra_xusb_pad *pad)
+{
+ struct tegra_xusb_sata_pad *sata = to_sata_pad(pad);
+
+ kfree(sata);
+}
+
+static const struct tegra_xusb_pad_ops tegra124_sata_ops = {
+ .probe = tegra124_sata_pad_probe,
+ .remove = tegra124_sata_pad_remove,
+};
+
+static const struct tegra_xusb_pad_soc tegra124_sata_pad = {
+ .name = "sata",
+ .num_lanes = ARRAY_SIZE(tegra124_sata_lanes),
+ .lanes = tegra124_sata_lanes,
+ .ops = &tegra124_sata_ops,
+};
+
+static const struct tegra_xusb_pad_soc *tegra124_pads[] = {
+ &tegra124_usb2_pad,
+ &tegra124_ulpi_pad,
+ &tegra124_hsic_pad,
+ &tegra124_pcie_pad,
+ &tegra124_sata_pad,
+};
+
+static int tegra124_usb2_port_enable(struct tegra_xusb_port *port)
+{
+ return 0;
+}
+
+static void tegra124_usb2_port_disable(struct tegra_xusb_port *port)
+{
+}
+
+static struct tegra_xusb_lane *
+tegra124_usb2_port_map(struct tegra_xusb_port *port)
+{
+ return tegra_xusb_find_lane(port->padctl, "usb2", port->index);
+}
+
+static const struct tegra_xusb_port_ops tegra124_usb2_port_ops = {
+ .enable = tegra124_usb2_port_enable,
+ .disable = tegra124_usb2_port_disable,
+ .map = tegra124_usb2_port_map,
+};
+
+static int tegra124_ulpi_port_enable(struct tegra_xusb_port *port)
+{
+ return 0;
+}
+
+static void tegra124_ulpi_port_disable(struct tegra_xusb_port *port)
+{
+}
+
+static struct tegra_xusb_lane *
+tegra124_ulpi_port_map(struct tegra_xusb_port *port)
+{
+ return tegra_xusb_find_lane(port->padctl, "ulpi", port->index);
+}
+
+static const struct tegra_xusb_port_ops tegra124_ulpi_port_ops = {
+ .enable = tegra124_ulpi_port_enable,
+ .disable = tegra124_ulpi_port_disable,
+ .map = tegra124_ulpi_port_map,
+};
+
+static int tegra124_hsic_port_enable(struct tegra_xusb_port *port)
+{
+ return 0;
+}
+
+static void tegra124_hsic_port_disable(struct tegra_xusb_port *port)
+{
+}
+
+static struct tegra_xusb_lane *
+tegra124_hsic_port_map(struct tegra_xusb_port *port)
+{
+ return tegra_xusb_find_lane(port->padctl, "hsic", port->index);
+}
+
+static const struct tegra_xusb_port_ops tegra124_hsic_port_ops = {
+ .enable = tegra124_hsic_port_enable,
+ .disable = tegra124_hsic_port_disable,
+ .map = tegra124_hsic_port_map,
+};
+
+static int tegra124_usb3_port_enable(struct tegra_xusb_port *port)
+{
+ struct tegra_xusb_usb3_port *usb3 = to_usb3_port(port);
+ struct tegra_xusb_padctl *padctl = port->padctl;
+ struct tegra_xusb_lane *lane = usb3->base.lane;
+ unsigned int index = port->index, offset;
+ int ret = 0;
+ u32 value;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
+
+ if (!usb3->internal)
+ value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index);
+ else
+ value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index);
+
+ value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(index);
+ value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(index, usb3->port);
+ padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP);
+
+ /*
+ * TODO: move this code into the PCIe/SATA PHY ->power_on() callbacks
+ * and conditionalize based on mux function? This seems to work, but
+ * might not be the exact proper sequence.
+ */
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(index));
+ value &= ~((XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_MASK <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_SHIFT) |
+ (XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_MASK <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_SHIFT) |
+ (XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_MASK <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_SHIFT));
+ value |= (XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_VAL <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_SHIFT) |
+ (XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_VAL <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_SHIFT) |
+ (XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_VAL <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_SHIFT);
+
+ if (usb3->context_saved) {
+ value &= ~((XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_MASK <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT) |
+ (XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_MASK <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT));
+ value |= (usb3->ctle_g <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT) |
+ (usb3->ctle_z <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT);
+ }
+
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(index));
+
+ value = XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_VAL;
+
+ if (usb3->context_saved) {
+ value &= ~((XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_MASK <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT) |
+ (XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_MASK <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT));
+ value |= (usb3->tap1 <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT) |
+ (usb3->amp <<
+ XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT);
+ }
+
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_USB3_PADX_CTL4(index));
+
+ if (lane->pad == padctl->pcie)
+ offset = XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL2(lane->index);
+ else
+ offset = XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL2;
+
+ value = padctl_readl(padctl, offset);
+ value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_MASK <<
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_SHIFT);
+ value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_VAL <<
+ XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_SHIFT;
+ padctl_writel(padctl, value, offset);
+
+ if (lane->pad == padctl->pcie)
+ offset = XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL5(lane->index);
+ else
+ offset = XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL5;
+
+ value = padctl_readl(padctl, offset);
+ value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL5_RX_QEYE_EN;
+ padctl_writel(padctl, value, offset);
+
+ /* Enable SATA PHY when SATA lane is used */
+ if (lane->pad == padctl->sata) {
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+ value &= ~(XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL0_REFCLK_NDIV_MASK <<
+ XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL0_REFCLK_NDIV_SHIFT);
+ value |= 0x2 <<
+ XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL0_REFCLK_NDIV_SHIFT;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL2);
+ value &= ~((XUSB_PADCTL_IOPHY_PLL_S0_CTL2_XDIGCLK_SEL_MASK <<
+ XUSB_PADCTL_IOPHY_PLL_S0_CTL2_XDIGCLK_SEL_SHIFT) |
+ (XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL1_CP_CNTL_MASK <<
+ XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL1_CP_CNTL_SHIFT) |
+ (XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL0_CP_CNTL_MASK <<
+ XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL0_CP_CNTL_SHIFT) |
+ XUSB_PADCTL_IOPHY_PLL_S0_CTL2_TCLKOUT_EN);
+ value |= (0x7 <<
+ XUSB_PADCTL_IOPHY_PLL_S0_CTL2_XDIGCLK_SEL_SHIFT) |
+ (0x8 <<
+ XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL1_CP_CNTL_SHIFT) |
+ (0x8 <<
+ XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL0_CP_CNTL_SHIFT) |
+ XUSB_PADCTL_IOPHY_PLL_S0_CTL2_TXCLKREF_SEL;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL2);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL3);
+ value &= ~XUSB_PADCTL_IOPHY_PLL_S0_CTL3_RCAL_BYPASS;
+ padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL3);
+ }
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value &= ~XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_VCORE_DOWN(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value &= ~XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN_EARLY(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value &= ~XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ return ret;
+}
+
+static void tegra124_usb3_port_disable(struct tegra_xusb_port *port)
+{
+ struct tegra_xusb_padctl *padctl = port->padctl;
+ u32 value;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value |= XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN_EARLY(port->index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value |= XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN(port->index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ usleep_range(250, 350);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
+ value |= XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_VCORE_DOWN(port->index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
+ value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(port->index);
+ value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(port->index, 0x7);
+ padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP);
+}
+
+static const struct tegra_xusb_lane_map tegra124_usb3_map[] = {
+ { 0, "pcie", 0 },
+ { 1, "pcie", 1 },
+ { 1, "sata", 0 },
+ { 0, NULL, 0 },
+};
+
+static struct tegra_xusb_lane *
+tegra124_usb3_port_map(struct tegra_xusb_port *port)
+{
+ return tegra_xusb_port_find_lane(port, tegra124_usb3_map, "usb3-ss");
+}
+
+static const struct tegra_xusb_port_ops tegra124_usb3_port_ops = {
+ .enable = tegra124_usb3_port_enable,
+ .disable = tegra124_usb3_port_disable,
+ .map = tegra124_usb3_port_map,
+};
+
+static int
+tegra124_xusb_read_fuse_calibration(struct tegra124_xusb_fuse_calibration *fuse)
+{
+ unsigned int i;
+ int err;
+ u32 value;
+
+ err = tegra_fuse_readl(TEGRA_FUSE_SKU_CALIB_0, &value);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < ARRAY_SIZE(fuse->hs_curr_level); i++) {
+ fuse->hs_curr_level[i] =
+ (value >> FUSE_SKU_CALIB_HS_CURR_LEVEL_PADX_SHIFT(i)) &
+ FUSE_SKU_CALIB_HS_CURR_LEVEL_PAD_MASK;
+ }
+ fuse->hs_iref_cap =
+ (value >> FUSE_SKU_CALIB_HS_IREF_CAP_SHIFT) &
+ FUSE_SKU_CALIB_HS_IREF_CAP_MASK;
+ fuse->hs_term_range_adj =
+ (value >> FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_SHIFT) &
+ FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_MASK;
+ fuse->hs_squelch_level =
+ (value >> FUSE_SKU_CALIB_HS_SQUELCH_LEVEL_SHIFT) &
+ FUSE_SKU_CALIB_HS_SQUELCH_LEVEL_MASK;
+
+ return 0;
+}
+
+static struct tegra_xusb_padctl *
+tegra124_xusb_padctl_probe(struct device *dev,
+ const struct tegra_xusb_padctl_soc *soc)
+{
+ struct tegra124_xusb_padctl *padctl;
+ int err;
+
+ padctl = devm_kzalloc(dev, sizeof(*padctl), GFP_KERNEL);
+ if (!padctl)
+ return ERR_PTR(-ENOMEM);
+
+ padctl->base.dev = dev;
+ padctl->base.soc = soc;
+
+ err = tegra124_xusb_read_fuse_calibration(&padctl->fuse);
+ if (err < 0)
+ return ERR_PTR(err);
+
+ return &padctl->base;
+}
+
+static void tegra124_xusb_padctl_remove(struct tegra_xusb_padctl *padctl)
+{
+}
+
+static const struct tegra_xusb_padctl_ops tegra124_xusb_padctl_ops = {
+ .probe = tegra124_xusb_padctl_probe,
+ .remove = tegra124_xusb_padctl_remove,
+ .usb3_save_context = tegra124_usb3_save_context,
+ .hsic_set_idle = tegra124_hsic_set_idle,
+};
+
+const struct tegra_xusb_padctl_soc tegra124_xusb_padctl_soc = {
+ .num_pads = ARRAY_SIZE(tegra124_pads),
+ .pads = tegra124_pads,
+ .ports = {
+ .usb2 = {
+ .ops = &tegra124_usb2_port_ops,
+ .count = 3,
+ },
+ .ulpi = {
+ .ops = &tegra124_ulpi_port_ops,
+ .count = 1,
+ },
+ .hsic = {
+ .ops = &tegra124_hsic_port_ops,
+ .count = 2,
+ },
+ .usb3 = {
+ .ops = &tegra124_usb3_port_ops,
+ .count = 2,
+ },
+ },
+ .ops = &tegra124_xusb_padctl_ops,
+};
+EXPORT_SYMBOL_GPL(tegra124_xusb_padctl_soc);
+
+MODULE_AUTHOR("Thierry Reding <[email protected]>");
+MODULE_DESCRIPTION("NVIDIA Tegra 124 XUSB Pad Controller driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c
new file mode 100644
index 000000000000..809998f6ce85
--- /dev/null
+++ b/drivers/phy/tegra/xusb.c
@@ -0,0 +1,1017 @@
+/*
+ * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mailbox_client.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#include <soc/tegra/fuse.h>
+
+#include "xusb.h"
+
+static struct phy *tegra_xusb_pad_of_xlate(struct device *dev,
+ struct of_phandle_args *args)
+{
+ struct tegra_xusb_pad *pad = dev_get_drvdata(dev);
+ struct phy *phy = NULL;
+ unsigned int i;
+
+ if (args->args_count != 0)
+ return ERR_PTR(-EINVAL);
+
+ for (i = 0; i < pad->soc->num_lanes; i++) {
+ if (!pad->lanes[i])
+ continue;
+
+ if (pad->lanes[i]->dev.of_node == args->np) {
+ phy = pad->lanes[i];
+ break;
+ }
+ }
+
+ if (phy == NULL)
+ phy = ERR_PTR(-ENODEV);
+
+ return phy;
+}
+
+static const struct of_device_id tegra_xusb_padctl_of_match[] = {
+#if defined(CONFIG_ARCH_TEGRA_124_SOC) || defined(CONFIG_ARCH_TEGRA_132_SOC)
+ {
+ .compatible = "nvidia,tegra124-xusb-padctl",
+ .data = &tegra124_xusb_padctl_soc,
+ },
+#endif
+#if defined(CONFIG_ARCH_TEGRA_210_SOC)
+ {
+ .compatible = "nvidia,tegra210-xusb-padctl",
+ .data = &tegra210_xusb_padctl_soc,
+ },
+#endif
+ { }
+};
+MODULE_DEVICE_TABLE(of, tegra_xusb_padctl_of_match);
+
+static struct device_node *
+tegra_xusb_find_pad_node(struct tegra_xusb_padctl *padctl, const char *name)
+{
+ /*
+ * of_find_node_by_name() drops a reference, so make sure to grab one.
+ */
+ struct device_node *np = of_node_get(padctl->dev->of_node);
+
+ np = of_find_node_by_name(np, "pads");
+ if (np)
+ np = of_find_node_by_name(np, name);
+
+ return np;
+}
+
+static struct device_node *
+tegra_xusb_pad_find_phy_node(struct tegra_xusb_pad *pad, unsigned int index)
+{
+ /*
+ * of_find_node_by_name() drops a reference, so make sure to grab one.
+ */
+ struct device_node *np = of_node_get(pad->dev.of_node);
+ char *name;
+
+ name = kasprintf(GFP_KERNEL, "%s-%u", np->name, index);
+ if (!name) {
+ of_node_put(np);
+ return NULL;
+ }
+
+ np = of_find_node_by_name(np, name);
+
+ kfree(name);
+
+ return np;
+}
+
+int tegra_xusb_lane_lookup_function(struct tegra_xusb_lane *lane,
+ const char *function)
+{
+ unsigned int i;
+
+ for (i = 0; i < lane->soc->num_funcs; i++)
+ if (strcmp(function, lane->soc->funcs[i]) == 0)
+ return i;
+
+ return -EINVAL;
+}
+
+int tegra_xusb_lane_parse_dt(struct tegra_xusb_lane *lane,
+ struct device_node *np)
+{
+ struct device *dev = &lane->pad->dev;
+ const char *function;
+ int err;
+
+ err = of_property_read_string(np, "nvidia,function", &function);
+ if (err < 0)
+ return err;
+
+ err = tegra_xusb_lane_lookup_function(lane, function);
+ if (err < 0) {
+ dev_err(dev, "invalid function \"%s\" for lane \"%s\"\n",
+ function, np->name);
+ return err;
+ }
+
+ lane->function = err;
+
+ return 0;
+}
+
+static void tegra_xusb_lane_destroy(struct phy *phy)
+{
+ if (phy) {
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+ lane->pad->ops->remove(lane);
+ phy_destroy(phy);
+ }
+}
+
+static void tegra_xusb_pad_release(struct device *dev)
+{
+ struct tegra_xusb_pad *pad = to_tegra_xusb_pad(dev);
+
+ pad->soc->ops->remove(pad);
+}
+
+static struct device_type tegra_xusb_pad_type = {
+ .release = tegra_xusb_pad_release,
+};
+
+int tegra_xusb_pad_init(struct tegra_xusb_pad *pad,
+ struct tegra_xusb_padctl *padctl,
+ struct device_node *np)
+{
+ int err;
+
+ device_initialize(&pad->dev);
+ INIT_LIST_HEAD(&pad->list);
+ pad->dev.parent = padctl->dev;
+ pad->dev.type = &tegra_xusb_pad_type;
+ pad->dev.of_node = np;
+ pad->padctl = padctl;
+
+ err = dev_set_name(&pad->dev, "%s", pad->soc->name);
+ if (err < 0)
+ goto unregister;
+
+ err = device_add(&pad->dev);
+ if (err < 0)
+ goto unregister;
+
+ return 0;
+
+unregister:
+ device_unregister(&pad->dev);
+ return err;
+}
+
+int tegra_xusb_pad_register(struct tegra_xusb_pad *pad,
+ const struct phy_ops *ops)
+{
+ struct phy *lane;
+ unsigned int i;
+ int err;
+
+ pad->lanes = devm_kcalloc(&pad->dev, pad->soc->num_lanes, sizeof(lane),
+ GFP_KERNEL);
+ if (!pad->lanes)
+ return -ENOMEM;
+
+ for (i = 0; i < pad->soc->num_lanes; i++) {
+ struct device_node *np = tegra_xusb_pad_find_phy_node(pad, i);
+ struct tegra_xusb_lane *lane;
+
+ /* skip disabled lanes */
+ if (!np || !of_device_is_available(np))
+ continue;
+
+ pad->lanes[i] = phy_create(&pad->dev, np, ops);
+ if (IS_ERR(pad->lanes[i])) {
+ err = PTR_ERR(pad->lanes[i]);
+ goto remove;
+ }
+
+ lane = pad->ops->probe(pad, np, i);
+ if (IS_ERR(lane)) {
+ phy_destroy(pad->lanes[i]);
+ err = PTR_ERR(lane);
+ goto remove;
+ }
+
+ list_add_tail(&lane->list, &pad->padctl->lanes);
+ phy_set_drvdata(pad->lanes[i], lane);
+ }
+
+ pad->provider = of_phy_provider_register(&pad->dev,
+ tegra_xusb_pad_of_xlate);
+ if (IS_ERR(pad->provider)) {
+ err = PTR_ERR(pad->provider);
+ goto remove;
+ }
+
+ return 0;
+
+remove:
+ while (i--)
+ tegra_xusb_lane_destroy(pad->lanes[i]);
+
+ return err;
+}
+
+void tegra_xusb_pad_unregister(struct tegra_xusb_pad *pad)
+{
+ unsigned int i = pad->soc->num_lanes;
+
+ of_phy_provider_unregister(pad->provider);
+
+ while (i--)
+ tegra_xusb_lane_destroy(pad->lanes[i]);
+
+ device_unregister(&pad->dev);
+}
+
+static struct tegra_xusb_pad *
+tegra_xusb_pad_create(struct tegra_xusb_padctl *padctl,
+ const struct tegra_xusb_pad_soc *soc)
+{
+ struct tegra_xusb_pad *pad;
+ struct device_node *np;
+ int err;
+
+ np = tegra_xusb_find_pad_node(padctl, soc->name);
+ if (!np || !of_device_is_available(np))
+ return NULL;
+
+ pad = soc->ops->probe(padctl, soc, np);
+ if (IS_ERR(pad)) {
+ err = PTR_ERR(pad);
+ dev_err(padctl->dev, "failed to create pad %s: %d\n",
+ soc->name, err);
+ return ERR_PTR(err);
+ }
+
+ /* XXX move this into ->probe() to avoid string comparison */
+ if (strcmp(soc->name, "pcie") == 0)
+ padctl->pcie = pad;
+
+ if (strcmp(soc->name, "sata") == 0)
+ padctl->sata = pad;
+
+ if (strcmp(soc->name, "usb2") == 0)
+ padctl->usb2 = pad;
+
+ if (strcmp(soc->name, "ulpi") == 0)
+ padctl->ulpi = pad;
+
+ if (strcmp(soc->name, "hsic") == 0)
+ padctl->hsic = pad;
+
+ return pad;
+}
+
+static void __tegra_xusb_remove_pads(struct tegra_xusb_padctl *padctl)
+{
+ struct tegra_xusb_pad *pad, *tmp;
+
+ list_for_each_entry_safe_reverse(pad, tmp, &padctl->pads, list) {
+ list_del(&pad->list);
+ tegra_xusb_pad_unregister(pad);
+ }
+}
+
+static void tegra_xusb_remove_pads(struct tegra_xusb_padctl *padctl)
+{
+ mutex_lock(&padctl->lock);
+ __tegra_xusb_remove_pads(padctl);
+ mutex_unlock(&padctl->lock);
+}
+
+static void tegra_xusb_lane_program(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ const struct tegra_xusb_lane_soc *soc = lane->soc;
+ u32 value;
+
+ /* choose function */
+ value = padctl_readl(padctl, soc->offset);
+ value &= ~(soc->mask << soc->shift);
+ value |= lane->function << soc->shift;
+ padctl_writel(padctl, value, soc->offset);
+}
+
+static void tegra_xusb_pad_program(struct tegra_xusb_pad *pad)
+{
+ unsigned int i;
+
+ for (i = 0; i < pad->soc->num_lanes; i++) {
+ struct tegra_xusb_lane *lane;
+
+ if (pad->lanes[i]) {
+ lane = phy_get_drvdata(pad->lanes[i]);
+ tegra_xusb_lane_program(lane);
+ }
+ }
+}
+
+static int tegra_xusb_setup_pads(struct tegra_xusb_padctl *padctl)
+{
+ struct tegra_xusb_pad *pad;
+ unsigned int i;
+
+ mutex_lock(&padctl->lock);
+
+ for (i = 0; i < padctl->soc->num_pads; i++) {
+ const struct tegra_xusb_pad_soc *soc = padctl->soc->pads[i];
+ int err;
+
+ pad = tegra_xusb_pad_create(padctl, soc);
+ if (IS_ERR(pad)) {
+ err = PTR_ERR(pad);
+ dev_err(padctl->dev, "failed to create pad %s: %d\n",
+ soc->name, err);
+ __tegra_xusb_remove_pads(padctl);
+ mutex_unlock(&padctl->lock);
+ return err;
+ }
+
+ if (!pad)
+ continue;
+
+ list_add_tail(&pad->list, &padctl->pads);
+ }
+
+ list_for_each_entry(pad, &padctl->pads, list)
+ tegra_xusb_pad_program(pad);
+
+ mutex_unlock(&padctl->lock);
+ return 0;
+}
+
+static bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane,
+ const char *function)
+{
+ if (lane) {
+ const char *func = lane->soc->funcs[lane->function];
+
+ return strcmp(function, func) == 0;
+ }
+
+ return false;
+}
+
+struct tegra_xusb_lane *tegra_xusb_find_lane(struct tegra_xusb_padctl *padctl,
+ const char *type,
+ unsigned int index)
+{
+ struct tegra_xusb_lane *lane, *hit = NULL;
+ char *name;
+
+ name = kasprintf(GFP_KERNEL, "%s-%u", type, index);
+ if (!name)
+ return NULL;
+
+ list_for_each_entry(lane, &padctl->lanes, list) {
+ if (strcmp(lane->soc->name, name) == 0) {
+ hit = lane;
+ break;
+ }
+ }
+
+ kfree(name);
+ return hit;
+}
+
+struct tegra_xusb_lane *
+tegra_xusb_port_find_lane(struct tegra_xusb_port *port,
+ const struct tegra_xusb_lane_map *map,
+ const char *function)
+{
+ struct tegra_xusb_lane *lane, *match = NULL;
+
+ for (map = map; map->type; map++) {
+ if (port->index != map->port)
+ continue;
+
+ lane = tegra_xusb_find_lane(port->padctl, map->type,
+ map->index);
+ if (!tegra_xusb_lane_check(lane, function))
+ continue;
+
+ if (match)
+ dev_err(&port->dev, "conflicting match: %s-%u / %s\n",
+ map->type, map->index, match->soc->name);
+ else
+ match = lane;
+ }
+
+ return match;
+}
+
+static struct device_node *
+tegra_xusb_find_port_node(struct tegra_xusb_padctl *padctl, const char *type,
+ unsigned int index)
+{
+ /*
+ * of_find_node_by_name() drops a reference, so make sure to grab one.
+ */
+ struct device_node *np = of_node_get(padctl->dev->of_node);
+
+ np = of_find_node_by_name(np, "ports");
+ if (np) {
+ char *name;
+
+ name = kasprintf(GFP_KERNEL, "%s-%u", type, index);
+ np = of_find_node_by_name(np, name);
+ kfree(name);
+ }
+
+ return np;
+}
+
+struct tegra_xusb_port *
+tegra_xusb_find_port(struct tegra_xusb_padctl *padctl, const char *type,
+ unsigned int index)
+{
+ struct tegra_xusb_port *port;
+ struct device_node *np;
+
+ np = tegra_xusb_find_port_node(padctl, type, index);
+ if (!np)
+ return NULL;
+
+ list_for_each_entry(port, &padctl->ports, list) {
+ if (np == port->dev.of_node) {
+ of_node_put(np);
+ return port;
+ }
+ }
+
+ of_node_put(np);
+
+ return NULL;
+}
+
+struct tegra_xusb_usb2_port *
+tegra_xusb_find_usb2_port(struct tegra_xusb_padctl *padctl, unsigned int index)
+{
+ struct tegra_xusb_port *port;
+
+ port = tegra_xusb_find_port(padctl, "usb2", index);
+ if (port)
+ return to_usb2_port(port);
+
+ return NULL;
+}
+
+struct tegra_xusb_usb3_port *
+tegra_xusb_find_usb3_port(struct tegra_xusb_padctl *padctl, unsigned int index)
+{
+ struct tegra_xusb_port *port;
+
+ port = tegra_xusb_find_port(padctl, "usb3", index);
+ if (port)
+ return to_usb3_port(port);
+
+ return NULL;
+}
+
+static void tegra_xusb_port_release(struct device *dev)
+{
+}
+
+static struct device_type tegra_xusb_port_type = {
+ .release = tegra_xusb_port_release,
+};
+
+static int tegra_xusb_port_init(struct tegra_xusb_port *port,
+ struct tegra_xusb_padctl *padctl,
+ struct device_node *np,
+ const char *name,
+ unsigned int index)
+{
+ int err;
+
+ INIT_LIST_HEAD(&port->list);
+ port->padctl = padctl;
+ port->index = index;
+
+ device_initialize(&port->dev);
+ port->dev.type = &tegra_xusb_port_type;
+ port->dev.of_node = of_node_get(np);
+ port->dev.parent = padctl->dev;
+
+ err = dev_set_name(&port->dev, "%s-%u", name, index);
+ if (err < 0)
+ goto unregister;
+
+ err = device_add(&port->dev);
+ if (err < 0)
+ goto unregister;
+
+ return 0;
+
+unregister:
+ device_unregister(&port->dev);
+ return err;
+}
+
+static void tegra_xusb_port_unregister(struct tegra_xusb_port *port)
+{
+ device_unregister(&port->dev);
+}
+
+static int tegra_xusb_usb2_port_parse_dt(struct tegra_xusb_usb2_port *usb2)
+{
+ struct tegra_xusb_port *port = &usb2->base;
+ struct device_node *np = port->dev.of_node;
+
+ usb2->internal = of_property_read_bool(np, "nvidia,internal");
+
+ usb2->supply = devm_regulator_get(&port->dev, "vbus");
+ if (IS_ERR(usb2->supply))
+ return PTR_ERR(usb2->supply);
+
+ return 0;
+}
+
+static int tegra_xusb_add_usb2_port(struct tegra_xusb_padctl *padctl,
+ unsigned int index)
+{
+ struct tegra_xusb_usb2_port *usb2;
+ struct device_node *np;
+ int err = 0;
+
+ /*
+ * USB2 ports don't require additional properties, but if the port is
+ * marked as disabled there is no reason to register it.
+ */
+ np = tegra_xusb_find_port_node(padctl, "usb2", index);
+ if (!np || !of_device_is_available(np))
+ goto out;
+
+ usb2 = devm_kzalloc(padctl->dev, sizeof(*usb2), GFP_KERNEL);
+ if (!usb2) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = tegra_xusb_port_init(&usb2->base, padctl, np, "usb2", index);
+ if (err < 0)
+ goto out;
+
+ usb2->base.ops = padctl->soc->ports.usb2.ops;
+
+ usb2->base.lane = usb2->base.ops->map(&usb2->base);
+ if (IS_ERR(usb2->base.lane)) {
+ err = PTR_ERR(usb2->base.lane);
+ goto out;
+ }
+
+ err = tegra_xusb_usb2_port_parse_dt(usb2);
+ if (err < 0) {
+ tegra_xusb_port_unregister(&usb2->base);
+ goto out;
+ }
+
+ list_add_tail(&usb2->base.list, &padctl->ports);
+
+out:
+ of_node_put(np);
+ return err;
+}
+
+static int tegra_xusb_ulpi_port_parse_dt(struct tegra_xusb_ulpi_port *ulpi)
+{
+ struct tegra_xusb_port *port = &ulpi->base;
+ struct device_node *np = port->dev.of_node;
+
+ ulpi->internal = of_property_read_bool(np, "nvidia,internal");
+
+ return 0;
+}
+
+static int tegra_xusb_add_ulpi_port(struct tegra_xusb_padctl *padctl,
+ unsigned int index)
+{
+ struct tegra_xusb_ulpi_port *ulpi;
+ struct device_node *np;
+ int err = 0;
+
+ np = tegra_xusb_find_port_node(padctl, "ulpi", index);
+ if (!np || !of_device_is_available(np))
+ goto out;
+
+ ulpi = devm_kzalloc(padctl->dev, sizeof(*ulpi), GFP_KERNEL);
+ if (!ulpi) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = tegra_xusb_port_init(&ulpi->base, padctl, np, "ulpi", index);
+ if (err < 0)
+ goto out;
+
+ ulpi->base.ops = padctl->soc->ports.ulpi.ops;
+
+ ulpi->base.lane = ulpi->base.ops->map(&ulpi->base);
+ if (IS_ERR(ulpi->base.lane)) {
+ err = PTR_ERR(ulpi->base.lane);
+ goto out;
+ }
+
+ err = tegra_xusb_ulpi_port_parse_dt(ulpi);
+ if (err < 0) {
+ tegra_xusb_port_unregister(&ulpi->base);
+ goto out;
+ }
+
+ list_add_tail(&ulpi->base.list, &padctl->ports);
+
+out:
+ of_node_put(np);
+ return err;
+}
+
+static int tegra_xusb_hsic_port_parse_dt(struct tegra_xusb_hsic_port *hsic)
+{
+ /* XXX */
+ return 0;
+}
+
+static int tegra_xusb_add_hsic_port(struct tegra_xusb_padctl *padctl,
+ unsigned int index)
+{
+ struct tegra_xusb_hsic_port *hsic;
+ struct device_node *np;
+ int err = 0;
+
+ np = tegra_xusb_find_port_node(padctl, "hsic", index);
+ if (!np || !of_device_is_available(np))
+ goto out;
+
+ hsic = devm_kzalloc(padctl->dev, sizeof(*hsic), GFP_KERNEL);
+ if (!hsic) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = tegra_xusb_port_init(&hsic->base, padctl, np, "hsic", index);
+ if (err < 0)
+ goto out;
+
+ hsic->base.ops = padctl->soc->ports.hsic.ops;
+
+ hsic->base.lane = hsic->base.ops->map(&hsic->base);
+ if (IS_ERR(hsic->base.lane)) {
+ err = PTR_ERR(hsic->base.lane);
+ goto out;
+ }
+
+ err = tegra_xusb_hsic_port_parse_dt(hsic);
+ if (err < 0) {
+ tegra_xusb_port_unregister(&hsic->base);
+ goto out;
+ }
+
+ list_add_tail(&hsic->base.list, &padctl->ports);
+
+out:
+ of_node_put(np);
+ return err;
+}
+
+static int tegra_xusb_usb3_port_parse_dt(struct tegra_xusb_usb3_port *usb3)
+{
+ struct tegra_xusb_port *port = &usb3->base;
+ struct device_node *np = port->dev.of_node;
+ u32 value;
+ int err;
+
+ err = of_property_read_u32(np, "nvidia,usb2-companion", &value);
+ if (err < 0) {
+ dev_err(&port->dev, "failed to read port: %d\n", err);
+ return err;
+ }
+
+ usb3->port = value;
+
+ usb3->internal = of_property_read_bool(np, "nvidia,internal");
+
+ usb3->supply = devm_regulator_get(&port->dev, "vbus");
+ if (IS_ERR(usb3->supply))
+ return PTR_ERR(usb3->supply);
+
+ return 0;
+}
+
+static int tegra_xusb_add_usb3_port(struct tegra_xusb_padctl *padctl,
+ unsigned int index)
+{
+ struct tegra_xusb_usb3_port *usb3;
+ struct device_node *np;
+ int err = 0;
+
+ /*
+ * If there is no supplemental configuration in the device tree the
+ * port is unusable. But it is valid to configure only a single port,
+ * hence return 0 instead of an error to allow ports to be optional.
+ */
+ np = tegra_xusb_find_port_node(padctl, "usb3", index);
+ if (!np || !of_device_is_available(np))
+ goto out;
+
+ usb3 = devm_kzalloc(padctl->dev, sizeof(*usb3), GFP_KERNEL);
+ if (!usb3) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = tegra_xusb_port_init(&usb3->base, padctl, np, "usb3", index);
+ if (err < 0)
+ goto out;
+
+ usb3->base.ops = padctl->soc->ports.usb3.ops;
+
+ usb3->base.lane = usb3->base.ops->map(&usb3->base);
+ if (IS_ERR(usb3->base.lane)) {
+ err = PTR_ERR(usb3->base.lane);
+ goto out;
+ }
+
+ err = tegra_xusb_usb3_port_parse_dt(usb3);
+ if (err < 0) {
+ tegra_xusb_port_unregister(&usb3->base);
+ goto out;
+ }
+
+ list_add_tail(&usb3->base.list, &padctl->ports);
+
+out:
+ of_node_put(np);
+ return err;
+}
+
+static void __tegra_xusb_remove_ports(struct tegra_xusb_padctl *padctl)
+{
+ struct tegra_xusb_port *port, *tmp;
+
+ list_for_each_entry_safe_reverse(port, tmp, &padctl->ports, list) {
+ list_del(&port->list);
+ tegra_xusb_port_unregister(port);
+ }
+}
+
+static int tegra_xusb_setup_ports(struct tegra_xusb_padctl *padctl)
+{
+ struct tegra_xusb_port *port;
+ unsigned int i;
+ int err = 0;
+
+ mutex_lock(&padctl->lock);
+
+ for (i = 0; i < padctl->soc->ports.usb2.count; i++) {
+ err = tegra_xusb_add_usb2_port(padctl, i);
+ if (err < 0)
+ goto remove_ports;
+ }
+
+ for (i = 0; i < padctl->soc->ports.ulpi.count; i++) {
+ err = tegra_xusb_add_ulpi_port(padctl, i);
+ if (err < 0)
+ goto remove_ports;
+ }
+
+ for (i = 0; i < padctl->soc->ports.hsic.count; i++) {
+ err = tegra_xusb_add_hsic_port(padctl, i);
+ if (err < 0)
+ goto remove_ports;
+ }
+
+ for (i = 0; i < padctl->soc->ports.usb3.count; i++) {
+ err = tegra_xusb_add_usb3_port(padctl, i);
+ if (err < 0)
+ goto remove_ports;
+ }
+
+ list_for_each_entry(port, &padctl->ports, list) {
+ err = port->ops->enable(port);
+ if (err < 0)
+ dev_err(padctl->dev, "failed to enable port %s: %d\n",
+ dev_name(&port->dev), err);
+ }
+
+ goto unlock;
+
+remove_ports:
+ __tegra_xusb_remove_ports(padctl);
+unlock:
+ mutex_unlock(&padctl->lock);
+ return err;
+}
+
+static void tegra_xusb_remove_ports(struct tegra_xusb_padctl *padctl)
+{
+ mutex_lock(&padctl->lock);
+ __tegra_xusb_remove_ports(padctl);
+ mutex_unlock(&padctl->lock);
+}
+
+static int tegra_xusb_padctl_probe(struct platform_device *pdev)
+{
+ struct device_node *np = of_node_get(pdev->dev.of_node);
+ const struct tegra_xusb_padctl_soc *soc;
+ struct tegra_xusb_padctl *padctl;
+ const struct of_device_id *match;
+ struct resource *res;
+ int err;
+
+ /* for backwards compatibility with old device trees */
+ np = of_find_node_by_name(np, "pads");
+ if (!np) {
+ dev_warn(&pdev->dev, "deprecated DT, using legacy driver\n");
+ return tegra_xusb_padctl_legacy_probe(pdev);
+ }
+
+ of_node_put(np);
+
+ match = of_match_node(tegra_xusb_padctl_of_match, pdev->dev.of_node);
+ soc = match->data;
+
+ padctl = soc->ops->probe(&pdev->dev, soc);
+ if (IS_ERR(padctl))
+ return PTR_ERR(padctl);
+
+ platform_set_drvdata(pdev, padctl);
+ INIT_LIST_HEAD(&padctl->ports);
+ INIT_LIST_HEAD(&padctl->lanes);
+ INIT_LIST_HEAD(&padctl->pads);
+ mutex_init(&padctl->lock);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ padctl->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(padctl->regs)) {
+ err = PTR_ERR(padctl->regs);
+ goto remove;
+ }
+
+ padctl->rst = devm_reset_control_get(&pdev->dev, NULL);
+ if (IS_ERR(padctl->rst)) {
+ err = PTR_ERR(padctl->rst);
+ goto remove;
+ }
+
+ err = reset_control_deassert(padctl->rst);
+ if (err < 0)
+ goto remove;
+
+ err = tegra_xusb_setup_pads(padctl);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to setup pads: %d\n", err);
+ goto reset;
+ }
+
+ err = tegra_xusb_setup_ports(padctl);
+ if (err) {
+ dev_err(&pdev->dev, "failed to setup XUSB ports: %d\n", err);
+ goto remove_pads;
+ }
+
+ return 0;
+
+remove_pads:
+ tegra_xusb_remove_pads(padctl);
+reset:
+ reset_control_assert(padctl->rst);
+remove:
+ soc->ops->remove(padctl);
+ return err;
+}
+
+static int tegra_xusb_padctl_remove(struct platform_device *pdev)
+{
+ struct tegra_xusb_padctl *padctl = platform_get_drvdata(pdev);
+ int err;
+
+ tegra_xusb_remove_ports(padctl);
+ tegra_xusb_remove_pads(padctl);
+
+ err = reset_control_assert(padctl->rst);
+ if (err < 0)
+ dev_err(&pdev->dev, "failed to assert reset: %d\n", err);
+
+ padctl->soc->ops->remove(padctl);
+
+ return err;
+}
+
+static struct platform_driver tegra_xusb_padctl_driver = {
+ .driver = {
+ .name = "tegra-xusb-padctl",
+ .of_match_table = tegra_xusb_padctl_of_match,
+ },
+ .probe = tegra_xusb_padctl_probe,
+ .remove = tegra_xusb_padctl_remove,
+};
+module_platform_driver(tegra_xusb_padctl_driver);
+
+struct tegra_xusb_padctl *tegra_xusb_padctl_get(struct device *dev)
+{
+ struct tegra_xusb_padctl *padctl;
+ struct platform_device *pdev;
+ struct device_node *np;
+
+ np = of_parse_phandle(dev->of_node, "nvidia,xusb-padctl", 0);
+ if (!np)
+ return ERR_PTR(-EINVAL);
+
+ /*
+ * This is slightly ugly. A better implementation would be to keep a
+ * registry of pad controllers, but since there will almost certainly
+ * only ever be one per SoC that would be a little overkill.
+ */
+ pdev = of_find_device_by_node(np);
+ if (!pdev) {
+ of_node_put(np);
+ return ERR_PTR(-ENODEV);
+ }
+
+ of_node_put(np);
+
+ padctl = platform_get_drvdata(pdev);
+ if (!padctl) {
+ put_device(&pdev->dev);
+ return ERR_PTR(-EPROBE_DEFER);
+ }
+
+ return padctl;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_get);
+
+void tegra_xusb_padctl_put(struct tegra_xusb_padctl *padctl)
+{
+ if (padctl)
+ put_device(padctl->dev);
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_put);
+
+int tegra_xusb_padctl_usb3_save_context(struct tegra_xusb_padctl *padctl,
+ unsigned int port)
+{
+ if (padctl->soc->ops->usb3_save_context)
+ return padctl->soc->ops->usb3_save_context(padctl, port);
+
+ return -ENOSYS;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_usb3_save_context);
+
+int tegra_xusb_padctl_hsic_set_idle(struct tegra_xusb_padctl *padctl,
+ unsigned int port, bool idle)
+{
+ if (padctl->soc->ops->hsic_set_idle)
+ return padctl->soc->ops->hsic_set_idle(padctl, port, idle);
+
+ return -ENOSYS;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_hsic_set_idle);
+
+int tegra_xusb_padctl_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl,
+ unsigned int port, bool enable)
+{
+ if (padctl->soc->ops->usb3_set_lfps_detect)
+ return padctl->soc->ops->usb3_set_lfps_detect(padctl, port,
+ enable);
+
+ return -ENOSYS;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_usb3_set_lfps_detect);
+
+MODULE_AUTHOR("Thierry Reding <[email protected]>");
+MODULE_DESCRIPTION("Tegra XUSB Pad Controller driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h
new file mode 100644
index 000000000000..b49dbc36efa3
--- /dev/null
+++ b/drivers/phy/tegra/xusb.h
@@ -0,0 +1,421 @@
+/*
+ * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2015, Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#ifndef __PHY_TEGRA_XUSB_H
+#define __PHY_TEGRA_XUSB_H
+
+#include <linux/io.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+
+/* legacy entry points for backwards-compatibility */
+int tegra_xusb_padctl_legacy_probe(struct platform_device *pdev);
+int tegra_xusb_padctl_legacy_remove(struct platform_device *pdev);
+
+struct phy;
+struct phy_provider;
+struct platform_device;
+struct regulator;
+
+/*
+ * lanes
+ */
+struct tegra_xusb_lane_soc {
+ const char *name;
+
+ unsigned int offset;
+ unsigned int shift;
+ unsigned int mask;
+
+ const char * const *funcs;
+ unsigned int num_funcs;
+};
+
+struct tegra_xusb_lane {
+ const struct tegra_xusb_lane_soc *soc;
+ struct tegra_xusb_pad *pad;
+ struct device_node *np;
+ struct list_head list;
+ unsigned int function;
+ unsigned int index;
+};
+
+int tegra_xusb_lane_parse_dt(struct tegra_xusb_lane *lane,
+ struct device_node *np);
+
+struct tegra_xusb_usb2_lane {
+ struct tegra_xusb_lane base;
+
+ u32 hs_curr_level_offset;
+};
+
+static inline struct tegra_xusb_usb2_lane *
+to_usb2_lane(struct tegra_xusb_lane *lane)
+{
+ return container_of(lane, struct tegra_xusb_usb2_lane, base);
+}
+
+struct tegra_xusb_ulpi_lane {
+ struct tegra_xusb_lane base;
+};
+
+static inline struct tegra_xusb_ulpi_lane *
+to_ulpi_lane(struct tegra_xusb_lane *lane)
+{
+ return container_of(lane, struct tegra_xusb_ulpi_lane, base);
+}
+
+struct tegra_xusb_hsic_lane {
+ struct tegra_xusb_lane base;
+
+ u32 strobe_trim;
+ u32 rx_strobe_trim;
+ u32 rx_data_trim;
+ u32 tx_rtune_n;
+ u32 tx_rtune_p;
+ u32 tx_rslew_n;
+ u32 tx_rslew_p;
+ bool auto_term;
+};
+
+static inline struct tegra_xusb_hsic_lane *
+to_hsic_lane(struct tegra_xusb_lane *lane)
+{
+ return container_of(lane, struct tegra_xusb_hsic_lane, base);
+}
+
+struct tegra_xusb_pcie_lane {
+ struct tegra_xusb_lane base;
+};
+
+static inline struct tegra_xusb_pcie_lane *
+to_pcie_lane(struct tegra_xusb_lane *lane)
+{
+ return container_of(lane, struct tegra_xusb_pcie_lane, base);
+}
+
+struct tegra_xusb_sata_lane {
+ struct tegra_xusb_lane base;
+};
+
+static inline struct tegra_xusb_sata_lane *
+to_sata_lane(struct tegra_xusb_lane *lane)
+{
+ return container_of(lane, struct tegra_xusb_sata_lane, base);
+}
+
+struct tegra_xusb_lane_ops {
+ struct tegra_xusb_lane *(*probe)(struct tegra_xusb_pad *pad,
+ struct device_node *np,
+ unsigned int index);
+ void (*remove)(struct tegra_xusb_lane *lane);
+};
+
+/*
+ * pads
+ */
+struct tegra_xusb_pad_soc;
+struct tegra_xusb_padctl;
+
+struct tegra_xusb_pad_ops {
+ struct tegra_xusb_pad *(*probe)(struct tegra_xusb_padctl *padctl,
+ const struct tegra_xusb_pad_soc *soc,
+ struct device_node *np);
+ void (*remove)(struct tegra_xusb_pad *pad);
+};
+
+struct tegra_xusb_pad_soc {
+ const char *name;
+
+ const struct tegra_xusb_lane_soc *lanes;
+ unsigned int num_lanes;
+
+ const struct tegra_xusb_pad_ops *ops;
+};
+
+struct tegra_xusb_pad {
+ const struct tegra_xusb_pad_soc *soc;
+ struct tegra_xusb_padctl *padctl;
+ struct phy_provider *provider;
+ struct phy **lanes;
+ struct device dev;
+
+ const struct tegra_xusb_lane_ops *ops;
+
+ struct list_head list;
+};
+
+static inline struct tegra_xusb_pad *to_tegra_xusb_pad(struct device *dev)
+{
+ return container_of(dev, struct tegra_xusb_pad, dev);
+}
+
+int tegra_xusb_pad_init(struct tegra_xusb_pad *pad,
+ struct tegra_xusb_padctl *padctl,
+ struct device_node *np);
+int tegra_xusb_pad_register(struct tegra_xusb_pad *pad,
+ const struct phy_ops *ops);
+void tegra_xusb_pad_unregister(struct tegra_xusb_pad *pad);
+
+struct tegra_xusb_usb2_pad {
+ struct tegra_xusb_pad base;
+
+ struct clk *clk;
+ unsigned int enable;
+ struct mutex lock;
+};
+
+static inline struct tegra_xusb_usb2_pad *
+to_usb2_pad(struct tegra_xusb_pad *pad)
+{
+ return container_of(pad, struct tegra_xusb_usb2_pad, base);
+}
+
+struct tegra_xusb_ulpi_pad {
+ struct tegra_xusb_pad base;
+};
+
+static inline struct tegra_xusb_ulpi_pad *
+to_ulpi_pad(struct tegra_xusb_pad *pad)
+{
+ return container_of(pad, struct tegra_xusb_ulpi_pad, base);
+}
+
+struct tegra_xusb_hsic_pad {
+ struct tegra_xusb_pad base;
+
+ struct regulator *supply;
+ struct clk *clk;
+};
+
+static inline struct tegra_xusb_hsic_pad *
+to_hsic_pad(struct tegra_xusb_pad *pad)
+{
+ return container_of(pad, struct tegra_xusb_hsic_pad, base);
+}
+
+struct tegra_xusb_pcie_pad {
+ struct tegra_xusb_pad base;
+
+ struct reset_control *rst;
+ struct clk *pll;
+
+ unsigned int enable;
+};
+
+static inline struct tegra_xusb_pcie_pad *
+to_pcie_pad(struct tegra_xusb_pad *pad)
+{
+ return container_of(pad, struct tegra_xusb_pcie_pad, base);
+}
+
+struct tegra_xusb_sata_pad {
+ struct tegra_xusb_pad base;
+
+ struct reset_control *rst;
+ struct clk *pll;
+
+ unsigned int enable;
+};
+
+static inline struct tegra_xusb_sata_pad *
+to_sata_pad(struct tegra_xusb_pad *pad)
+{
+ return container_of(pad, struct tegra_xusb_sata_pad, base);
+}
+
+/*
+ * ports
+ */
+struct tegra_xusb_port_ops;
+
+struct tegra_xusb_port {
+ struct tegra_xusb_padctl *padctl;
+ struct tegra_xusb_lane *lane;
+ unsigned int index;
+
+ struct list_head list;
+ struct device dev;
+
+ const struct tegra_xusb_port_ops *ops;
+};
+
+struct tegra_xusb_lane_map {
+ unsigned int port;
+ const char *type;
+ unsigned int index;
+ const char *func;
+};
+
+struct tegra_xusb_lane *
+tegra_xusb_port_find_lane(struct tegra_xusb_port *port,
+ const struct tegra_xusb_lane_map *map,
+ const char *function);
+
+struct tegra_xusb_port *
+tegra_xusb_find_port(struct tegra_xusb_padctl *padctl, const char *type,
+ unsigned int index);
+
+struct tegra_xusb_usb2_port {
+ struct tegra_xusb_port base;
+
+ struct regulator *supply;
+ bool internal;
+};
+
+static inline struct tegra_xusb_usb2_port *
+to_usb2_port(struct tegra_xusb_port *port)
+{
+ return container_of(port, struct tegra_xusb_usb2_port, base);
+}
+
+struct tegra_xusb_usb2_port *
+tegra_xusb_find_usb2_port(struct tegra_xusb_padctl *padctl,
+ unsigned int index);
+
+struct tegra_xusb_ulpi_port {
+ struct tegra_xusb_port base;
+
+ struct regulator *supply;
+ bool internal;
+};
+
+static inline struct tegra_xusb_ulpi_port *
+to_ulpi_port(struct tegra_xusb_port *port)
+{
+ return container_of(port, struct tegra_xusb_ulpi_port, base);
+}
+
+struct tegra_xusb_hsic_port {
+ struct tegra_xusb_port base;
+};
+
+static inline struct tegra_xusb_hsic_port *
+to_hsic_port(struct tegra_xusb_port *port)
+{
+ return container_of(port, struct tegra_xusb_hsic_port, base);
+}
+
+struct tegra_xusb_usb3_port {
+ struct tegra_xusb_port base;
+ struct regulator *supply;
+ bool context_saved;
+ unsigned int port;
+ bool internal;
+
+ u32 tap1;
+ u32 amp;
+ u32 ctle_z;
+ u32 ctle_g;
+};
+
+static inline struct tegra_xusb_usb3_port *
+to_usb3_port(struct tegra_xusb_port *port)
+{
+ return container_of(port, struct tegra_xusb_usb3_port, base);
+}
+
+struct tegra_xusb_usb3_port *
+tegra_xusb_find_usb3_port(struct tegra_xusb_padctl *padctl,
+ unsigned int index);
+
+struct tegra_xusb_port_ops {
+ int (*enable)(struct tegra_xusb_port *port);
+ void (*disable)(struct tegra_xusb_port *port);
+ struct tegra_xusb_lane *(*map)(struct tegra_xusb_port *port);
+};
+
+/*
+ * pad controller
+ */
+struct tegra_xusb_padctl_soc;
+
+struct tegra_xusb_padctl_ops {
+ struct tegra_xusb_padctl *
+ (*probe)(struct device *dev,
+ const struct tegra_xusb_padctl_soc *soc);
+ void (*remove)(struct tegra_xusb_padctl *padctl);
+
+ int (*usb3_save_context)(struct tegra_xusb_padctl *padctl,
+ unsigned int index);
+ int (*hsic_set_idle)(struct tegra_xusb_padctl *padctl,
+ unsigned int index, bool idle);
+ int (*usb3_set_lfps_detect)(struct tegra_xusb_padctl *padctl,
+ unsigned int index, bool enable);
+};
+
+struct tegra_xusb_padctl_soc {
+ const struct tegra_xusb_pad_soc * const *pads;
+ unsigned int num_pads;
+
+ struct {
+ struct {
+ const struct tegra_xusb_port_ops *ops;
+ unsigned int count;
+ } usb2, ulpi, hsic, usb3;
+ } ports;
+
+ const struct tegra_xusb_padctl_ops *ops;
+};
+
+struct tegra_xusb_padctl {
+ struct device *dev;
+ void __iomem *regs;
+ struct mutex lock;
+ struct reset_control *rst;
+
+ const struct tegra_xusb_padctl_soc *soc;
+
+ struct tegra_xusb_pad *pcie;
+ struct tegra_xusb_pad *sata;
+ struct tegra_xusb_pad *ulpi;
+ struct tegra_xusb_pad *usb2;
+ struct tegra_xusb_pad *hsic;
+
+ struct list_head ports;
+ struct list_head lanes;
+ struct list_head pads;
+
+ unsigned int enable;
+
+ struct clk *clk;
+};
+
+static inline void padctl_writel(struct tegra_xusb_padctl *padctl, u32 value,
+ unsigned long offset)
+{
+ dev_dbg(padctl->dev, "%08lx < %08x\n", offset, value);
+ writel(value, padctl->regs + offset);
+}
+
+static inline u32 padctl_readl(struct tegra_xusb_padctl *padctl,
+ unsigned long offset)
+{
+ u32 value = readl(padctl->regs + offset);
+ dev_dbg(padctl->dev, "%08lx > %08x\n", offset, value);
+ return value;
+}
+
+struct tegra_xusb_lane *tegra_xusb_find_lane(struct tegra_xusb_padctl *padctl,
+ const char *name,
+ unsigned int index);
+
+#if defined(CONFIG_ARCH_TEGRA_124_SOC) || defined(CONFIG_ARCH_TEGRA_132_SOC)
+extern const struct tegra_xusb_padctl_soc tegra124_xusb_padctl_soc;
+#endif
+#if defined(CONFIG_ARCH_TEGRA_210_SOC)
+extern const struct tegra_xusb_padctl_soc tegra210_xusb_padctl_soc;
+#endif
+
+#endif /* __PHY_TEGRA_XUSB_H */
diff --git a/drivers/pinctrl/tegra/pinctrl-tegra-xusb.c b/drivers/pinctrl/tegra/pinctrl-tegra-xusb.c
index 2f06029c9405..946cda3fee35 100644
--- a/drivers/pinctrl/tegra/pinctrl-tegra-xusb.c
+++ b/drivers/pinctrl/tegra/pinctrl-tegra-xusb.c
@@ -873,7 +873,7 @@ static const struct of_device_id tegra_xusb_padctl_of_match[] = {
};
MODULE_DEVICE_TABLE(of, tegra_xusb_padctl_of_match);

-static int tegra_xusb_padctl_probe(struct platform_device *pdev)
+int tegra_xusb_padctl_legacy_probe(struct platform_device *pdev)
{
struct tegra_xusb_padctl *padctl;
const struct of_device_id *match;
@@ -955,8 +955,9 @@ reset:
reset_control_assert(padctl->rst);
return err;
}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_legacy_probe);

-static int tegra_xusb_padctl_remove(struct platform_device *pdev)
+int tegra_xusb_padctl_legacy_remove(struct platform_device *pdev)
{
struct tegra_xusb_padctl *padctl = platform_get_drvdata(pdev);
int err;
@@ -969,17 +970,4 @@ static int tegra_xusb_padctl_remove(struct platform_device *pdev)

return err;
}
-
-static struct platform_driver tegra_xusb_padctl_driver = {
- .driver = {
- .name = "tegra-xusb-padctl",
- .of_match_table = tegra_xusb_padctl_of_match,
- },
- .probe = tegra_xusb_padctl_probe,
- .remove = tegra_xusb_padctl_remove,
-};
-module_platform_driver(tegra_xusb_padctl_driver);
-
-MODULE_AUTHOR("Thierry Reding <[email protected]>");
-MODULE_DESCRIPTION("Tegra 124 XUSB Pad Control driver");
-MODULE_LICENSE("GPL v2");
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_legacy_remove);
diff --git a/include/linux/phy/tegra/xusb.h b/include/linux/phy/tegra/xusb.h
new file mode 100644
index 000000000000..8e1a57a78d9f
--- /dev/null
+++ b/include/linux/phy/tegra/xusb.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#ifndef PHY_TEGRA_XUSB_H
+#define PHY_TEGRA_XUSB_H
+
+struct tegra_xusb_padctl;
+struct device;
+
+struct tegra_xusb_padctl *tegra_xusb_padctl_get(struct device *dev);
+void tegra_xusb_padctl_put(struct tegra_xusb_padctl *padctl);
+
+int tegra_xusb_padctl_usb3_save_context(struct tegra_xusb_padctl *padctl,
+ unsigned int port);
+int tegra_xusb_padctl_hsic_set_idle(struct tegra_xusb_padctl *padctl,
+ unsigned int port, bool idle);
+int tegra_xusb_padctl_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl,
+ unsigned int port, bool enable);
+
+#endif /* PHY_TEGRA_XUSB_H */
--
2.7.1

2016-03-04 16:20:28

by Thierry Reding

[permalink] [raw]
Subject: [PATCH v10 9/9] usb: xhci: tegra: Add Tegra210 support

From: Thierry Reding <[email protected]>

Parameterize more parts of the driver and add support for Tegra210.

Cc: Greg Kroah-Hartman <[email protected]>
Cc: Mathias Nyman <[email protected]>
Signed-off-by: Thierry Reding <[email protected]>
---
drivers/usb/host/xhci-tegra.c | 59 +++++++++++++++++++++++++++++++++++++------
1 file changed, 51 insertions(+), 8 deletions(-)

diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
index ab6f9856c5c4..426e8d922cf4 100644
--- a/drivers/usb/host/xhci-tegra.c
+++ b/drivers/usb/host/xhci-tegra.c
@@ -159,6 +159,8 @@ struct tegra_xusb_soc {
unsigned int count;
} usb2, ulpi, hsic, usb3;
} ports;
+
+ bool scale_ss_clock;
};

struct tegra_xusb {
@@ -495,13 +497,19 @@ static void tegra_xusb_mbox_handle(struct tegra_xusb *tegra,

case MBOX_CMD_INC_SSPI_CLOCK:
case MBOX_CMD_DEC_SSPI_CLOCK:
- err = tegra_xusb_set_ss_clk(tegra, msg->data * 1000);
- if (err < 0)
- rsp.cmd = MBOX_CMD_NAK;
- else
+ if (tegra->soc->scale_ss_clock) {
+ err = tegra_xusb_set_ss_clk(tegra, msg->data * 1000);
+ if (err < 0)
+ rsp.cmd = MBOX_CMD_NAK;
+ else
+ rsp.cmd = MBOX_CMD_ACK;
+
+ rsp.data = clk_get_rate(tegra->ss_src_clk) / 1000;
+ } else {
rsp.cmd = MBOX_CMD_ACK;
+ rsp.data = msg->data;
+ }

- rsp.data = clk_get_rate(tegra->ss_src_clk) / 1000;
break;

case MBOX_CMD_SET_BW:
@@ -783,9 +791,11 @@ static int tegra_xusb_clk_enable(struct tegra_xusb *tegra)
if (err < 0)
goto disable_fs_src;

- err = tegra_xusb_set_ss_clk(tegra, TEGRA_XHCI_SS_HIGH_SPEED);
- if (err < 0)
- goto disable_hs_src;
+ if (tegra->soc->scale_ss_clock) {
+ err = tegra_xusb_set_ss_clk(tegra, TEGRA_XHCI_SS_HIGH_SPEED);
+ if (err < 0)
+ goto disable_hs_src;
+ }

return 0;

@@ -890,11 +900,44 @@ static const struct tegra_xusb_soc tegra124_soc = {
.hsic = { .offset = 6, .count = 2, },
.usb3 = { .offset = 0, .count = 2, },
},
+ .scale_ss_clock = true,
};
MODULE_FIRMWARE("nvidia/tegra124/xusb.bin");

+static const char * const tegra210_supply_names[] = {
+ "dvddio-pex",
+ "hvddio-pex",
+ "avdd-usb",
+ "avdd-pll-utmip",
+ "avdd-pll-uerefe",
+ "dvdd-pex-pll",
+ "hvdd-pex-pll-e",
+};
+
+static const struct tegra_xusb_phy_type tegra210_phy_types[] = {
+ { .name = "usb3", .num = 4, },
+ { .name = "usb2", .num = 4, },
+ { .name = "hsic", .num = 1, },
+};
+
+static const struct tegra_xusb_soc tegra210_soc = {
+ .firmware_file = "nvidia/tegra210/xusb.bin",
+ .supply_names = tegra210_supply_names,
+ .num_supplies = ARRAY_SIZE(tegra210_supply_names),
+ .phy_types = tegra210_phy_types,
+ .num_types = ARRAY_SIZE(tegra210_phy_types),
+ .ports = {
+ .usb2 = { .offset = 4, .count = 4, },
+ .hsic = { .offset = 8, .count = 1, },
+ .usb3 = { .offset = 0, .count = 4, },
+ },
+ .scale_ss_clock = false,
+};
+MODULE_FIRMWARE("nvidia/tegra210/xusb.bin");
+
static const struct of_device_id tegra_xusb_of_match[] = {
{ .compatible = "nvidia,tegra124-xusb", .data = &tegra124_soc },
+ { .compatible = "nvidia,tegra210-xusb", .data = &tegra210_soc },
{ },
};
MODULE_DEVICE_TABLE(of, tegra_xusb_of_match);
--
2.7.1

2016-03-04 16:21:04

by Thierry Reding

[permalink] [raw]
Subject: [PATCH v10 8/9] usb: xhci: Add NVIDIA Tegra XUSB controller driver

From: Thierry Reding <[email protected]>

Add support for the on-chip XUSB controller present on Tegra SoCs. This
controller, when loaded with external firmware, exposes an interface
compliant with xHCI. This driver loads the firmware, starts the
controller, and is able to service host-specific messages sent by the
controller's firmware.

The controller also supports USB device mode as well as powergating
of the SuperSpeed and host-controller logic when not in use, but
support for these is not yet implemented.

Based on work by:
Ajay Gupta <[email protected]>
Bharath Yadav <[email protected]>
Andrew Bresticker <[email protected]>

Cc: Greg Kroah-Hartman <[email protected]>
Cc: Mathias Nyman <[email protected]>
Signed-off-by: Thierry Reding <[email protected]>
---
drivers/usb/host/Kconfig | 9 +
drivers/usb/host/Makefile | 1 +
drivers/usb/host/xhci-tegra.c | 1332 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 1342 insertions(+)
create mode 100644 drivers/usb/host/xhci-tegra.c

diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 8c20ebbc049c..191fdeb8b841 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -69,6 +69,15 @@ config USB_XHCI_RCAR
Say 'Y' to enable the support for the xHCI host controller
found in Renesas R-Car ARM SoCs.

+config USB_XHCI_TEGRA
+ tristate "xHCI support for NVIDIA Tegra SoCs"
+ depends on PHY_TEGRA_XUSB
+ depends on RESET_CONTROLLER
+ select FW_LOADER
+ ---help---
+ Say 'Y' to enable the support for the xHCI host controller
+ found in NVIDIA Tegra124 and later SoCs.
+
endif # USB_XHCI_HCD

config USB_EHCI_HCD
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index a9ddd3c9ec94..6ef785b0ea8f 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -68,6 +68,7 @@ obj-$(CONFIG_USB_XHCI_HCD) += xhci-hcd.o
obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o
obj-$(CONFIG_USB_XHCI_PLATFORM) += xhci-plat-hcd.o
obj-$(CONFIG_USB_XHCI_MTK) += xhci-mtk.o
+obj-$(CONFIG_USB_XHCI_TEGRA) += xhci-tegra.o
obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o
obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o
obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o
diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
new file mode 100644
index 000000000000..ab6f9856c5c4
--- /dev/null
+++ b/drivers/usb/host/xhci-tegra.c
@@ -0,0 +1,1332 @@
+/*
+ * NVIDIA Tegra xHCI host controller driver
+ *
+ * Copyright (C) 2014 NVIDIA Corporation
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/firmware.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/phy/tegra/xusb.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+
+#include "xhci.h"
+
+#define TEGRA_XHCI_SS_HIGH_SPEED 120000000
+#define TEGRA_XHCI_SS_LOW_SPEED 12000000
+
+/* FPCI CFG registers */
+#define XUSB_CFG_1 0x004
+#define XUSB_IO_SPACE_EN BIT(0)
+#define XUSB_MEM_SPACE_EN BIT(1)
+#define XUSB_BUS_MASTER_EN BIT(2)
+#define XUSB_CFG_4 0x010
+#define XUSB_BASE_ADDR_SHIFT 15
+#define XUSB_BASE_ADDR_MASK 0x1ffff
+#define XUSB_CFG_ARU_C11_CSBRANGE 0x41c
+#define XUSB_CFG_CSB_BASE_ADDR 0x800
+
+/* FPCI mailbox registers */
+#define XUSB_CFG_ARU_MBOX_CMD 0x0e4
+#define MBOX_DEST_FALC BIT(27)
+#define MBOX_DEST_PME BIT(28)
+#define MBOX_DEST_SMI BIT(29)
+#define MBOX_DEST_XHCI BIT(30)
+#define MBOX_INT_EN BIT(31)
+#define XUSB_CFG_ARU_MBOX_DATA_IN 0x0e8
+#define CMD_DATA_SHIFT 0
+#define CMD_DATA_MASK 0xffffff
+#define CMD_TYPE_SHIFT 24
+#define CMD_TYPE_MASK 0xff
+#define XUSB_CFG_ARU_MBOX_DATA_OUT 0x0ec
+#define XUSB_CFG_ARU_MBOX_OWNER 0x0f0
+#define MBOX_OWNER_NONE 0
+#define MBOX_OWNER_FW 1
+#define MBOX_OWNER_SW 2
+#define XUSB_CFG_ARU_SMI_INTR 0x428
+#define MBOX_SMI_INTR_FW_HANG BIT(1)
+#define MBOX_SMI_INTR_EN BIT(3)
+
+/* IPFS registers */
+#define IPFS_XUSB_HOST_CONFIGURATION_0 0x180
+#define IPFS_EN_FPCI BIT(0)
+#define IPFS_XUSB_HOST_INTR_MASK_0 0x188
+#define IPFS_IP_INT_MASK BIT(16)
+#define IPFS_XUSB_HOST_CLKGATE_HYSTERESIS_0 0x1bc
+
+#define CSB_PAGE_SELECT_MASK 0x7fffff
+#define CSB_PAGE_SELECT_SHIFT 9
+#define CSB_PAGE_OFFSET_MASK 0x1ff
+#define CSB_PAGE_SELECT(addr) ((addr) >> (CSB_PAGE_SELECT_SHIFT) & \
+ CSB_PAGE_SELECT_MASK)
+#define CSB_PAGE_OFFSET(addr) ((addr) & CSB_PAGE_OFFSET_MASK)
+
+/* Falcon CSB registers */
+#define XUSB_FALC_CPUCTL 0x100
+#define CPUCTL_STARTCPU BIT(1)
+#define CPUCTL_STATE_HALTED BIT(4)
+#define CPUCTL_STATE_STOPPED BIT(5)
+#define XUSB_FALC_BOOTVEC 0x104
+#define XUSB_FALC_DMACTL 0x10c
+#define XUSB_FALC_IMFILLRNG1 0x154
+#define IMFILLRNG1_TAG_MASK 0xffff
+#define IMFILLRNG1_TAG_LO_SHIFT 0
+#define IMFILLRNG1_TAG_HI_SHIFT 16
+#define XUSB_FALC_IMFILLCTL 0x158
+
+/* MP CSB registers */
+#define XUSB_CSB_MP_ILOAD_ATTR 0x101a00
+#define XUSB_CSB_MP_ILOAD_BASE_LO 0x101a04
+#define XUSB_CSB_MP_ILOAD_BASE_HI 0x101a08
+#define XUSB_CSB_MP_L2IMEMOP_SIZE 0x101a10
+#define L2IMEMOP_SIZE_SRC_OFFSET_SHIFT 8
+#define L2IMEMOP_SIZE_SRC_OFFSET_MASK 0x3ff
+#define L2IMEMOP_SIZE_SRC_COUNT_SHIFT 24
+#define L2IMEMOP_SIZE_SRC_COUNT_MASK 0xff
+#define XUSB_CSB_MP_L2IMEMOP_TRIG 0x101a14
+#define L2IMEMOP_ACTION_SHIFT 24
+#define L2IMEMOP_INVALIDATE_ALL (0x40 << L2IMEMOP_ACTION_SHIFT)
+#define L2IMEMOP_LOAD_LOCKED_RESULT (0x11 << L2IMEMOP_ACTION_SHIFT)
+#define XUSB_CSB_MP_APMAP 0x10181c
+#define APMAP_BOOTPATH BIT(31)
+
+#define IMEM_BLOCK_SIZE 256
+
+struct tegra_xusb_fw_header {
+ u32 boot_loadaddr_in_imem;
+ u32 boot_codedfi_offset;
+ u32 boot_codetag;
+ u32 boot_codesize;
+ u32 phys_memaddr;
+ u16 reqphys_memsize;
+ u16 alloc_phys_memsize;
+ u32 rodata_img_offset;
+ u32 rodata_section_start;
+ u32 rodata_section_end;
+ u32 main_fnaddr;
+ u32 fwimg_cksum;
+ u32 fwimg_created_time;
+ u32 imem_resident_start;
+ u32 imem_resident_end;
+ u32 idirect_start;
+ u32 idirect_end;
+ u32 l2_imem_start;
+ u32 l2_imem_end;
+ u32 version_id;
+ u8 init_ddirect;
+ u8 reserved[3];
+ u32 phys_addr_log_buffer;
+ u32 total_log_entries;
+ u32 dequeue_ptr;
+ u32 dummy_var[2];
+ u32 fwimg_len;
+ u8 magic[8];
+ u32 ss_low_power_entry_timeout;
+ u8 num_hsic_port;
+ u8 padding[139]; /* Pad to 256 bytes */
+};
+
+struct tegra_xusb_phy_type {
+ const char *name;
+ unsigned int num;
+};
+
+struct tegra_xusb_soc {
+ const char *firmware_file;
+ const char * const *supply_names;
+ unsigned int num_supplies;
+ const struct tegra_xusb_phy_type *phy_types;
+ unsigned int num_types;
+
+ struct {
+ struct {
+ unsigned int offset;
+ unsigned int count;
+ } usb2, ulpi, hsic, usb3;
+ } ports;
+};
+
+struct tegra_xusb {
+ struct device *dev;
+ struct usb_hcd *hcd;
+
+ struct mutex lock;
+
+ int xhci_irq;
+ int mbox_irq;
+
+ void __iomem *ipfs_base;
+ void __iomem *fpci_base;
+
+ const struct tegra_xusb_soc *soc;
+
+ struct regulator_bulk_data *supplies;
+
+ struct tegra_xusb_padctl *padctl;
+
+ struct clk *host_clk;
+ struct clk *falcon_clk;
+ struct clk *ss_clk;
+ struct clk *ss_src_clk;
+ struct clk *hs_src_clk;
+ struct clk *fs_src_clk;
+ struct clk *pll_u_480m;
+ struct clk *clk_m;
+ struct clk *pll_e;
+
+ struct reset_control *host_rst;
+ struct reset_control *ss_rst;
+
+ struct phy **phys;
+ unsigned int num_phys;
+
+ /* Firmware loading related */
+ void *fw_data;
+ size_t fw_size;
+ dma_addr_t fw_dma_addr;
+ bool fw_loaded;
+};
+
+static struct hc_driver __read_mostly tegra_xhci_hc_driver;
+
+static inline u32 fpci_readl(struct tegra_xusb *tegra, unsigned int offset)
+{
+ return readl(tegra->fpci_base + offset);
+}
+
+static inline void fpci_writel(struct tegra_xusb *tegra, u32 value,
+ unsigned int offset)
+{
+ writel(value, tegra->fpci_base + offset);
+}
+
+static inline u32 ipfs_readl(struct tegra_xusb *tegra, unsigned int offset)
+{
+ return readl(tegra->ipfs_base + offset);
+}
+
+static inline void ipfs_writel(struct tegra_xusb *tegra, u32 value,
+ unsigned int offset)
+{
+ writel(value, tegra->ipfs_base + offset);
+}
+
+static u32 csb_readl(struct tegra_xusb *tegra, unsigned int offset)
+{
+ u32 page = CSB_PAGE_SELECT(offset);
+ u32 ofs = CSB_PAGE_OFFSET(offset);
+
+ fpci_writel(tegra, page, XUSB_CFG_ARU_C11_CSBRANGE);
+
+ return fpci_readl(tegra, XUSB_CFG_CSB_BASE_ADDR + ofs);
+}
+
+static void csb_writel(struct tegra_xusb *tegra, u32 value,
+ unsigned int offset)
+{
+ u32 page = CSB_PAGE_SELECT(offset);
+ u32 ofs = CSB_PAGE_OFFSET(offset);
+
+ fpci_writel(tegra, page, XUSB_CFG_ARU_C11_CSBRANGE);
+ fpci_writel(tegra, value, XUSB_CFG_CSB_BASE_ADDR + ofs);
+}
+
+static int tegra_xusb_set_ss_clk(struct tegra_xusb *tegra,
+ unsigned long rate)
+{
+ unsigned long new_parent_rate, old_parent_rate;
+ struct clk *clk = tegra->ss_src_clk;
+ unsigned int div;
+ int err;
+
+ if (clk_get_rate(clk) == rate)
+ return 0;
+
+ switch (rate) {
+ case TEGRA_XHCI_SS_HIGH_SPEED:
+ /*
+ * Reparent to PLLU_480M. Set divider first to avoid
+ * overclocking.
+ */
+ old_parent_rate = clk_get_rate(clk_get_parent(clk));
+ new_parent_rate = clk_get_rate(tegra->pll_u_480m);
+ div = new_parent_rate / rate;
+
+ err = clk_set_rate(clk, old_parent_rate / div);
+ if (err)
+ return err;
+
+ err = clk_set_parent(clk, tegra->pll_u_480m);
+ if (err)
+ return err;
+
+ /*
+ * The rate should already be correct, but set it again just
+ * to be sure.
+ */
+ err = clk_set_rate(clk, rate);
+ if (err)
+ return err;
+
+ break;
+
+ case TEGRA_XHCI_SS_LOW_SPEED:
+ /* Reparent to CLK_M */
+ err = clk_set_parent(clk, tegra->clk_m);
+ if (err)
+ return err;
+
+ err = clk_set_rate(clk, rate);
+ if (err)
+ return err;
+
+ break;
+
+ default:
+ dev_err(tegra->dev, "Invalid SS rate: %lu Hz\n", rate);
+ return -EINVAL;
+ }
+
+ if (clk_get_rate(clk) != rate) {
+ dev_err(tegra->dev, "SS clock doesn't match requested rate\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static unsigned long extract_field(u32 value, unsigned int start,
+ unsigned int count)
+{
+ return (value >> start) & ((1 << count) - 1);
+}
+
+/* Command requests from the firmware */
+enum tegra_xusb_mbox_cmd {
+ MBOX_CMD_MSG_ENABLED = 1,
+ MBOX_CMD_INC_FALC_CLOCK,
+ MBOX_CMD_DEC_FALC_CLOCK,
+ MBOX_CMD_INC_SSPI_CLOCK,
+ MBOX_CMD_DEC_SSPI_CLOCK,
+ MBOX_CMD_SET_BW, /* no ACK/NAK required */
+ MBOX_CMD_SET_SS_PWR_GATING,
+ MBOX_CMD_SET_SS_PWR_UNGATING,
+ MBOX_CMD_SAVE_DFE_CTLE_CTX,
+ MBOX_CMD_AIRPLANE_MODE_ENABLED, /* unused */
+ MBOX_CMD_AIRPLANE_MODE_DISABLED, /* unused */
+ MBOX_CMD_START_HSIC_IDLE,
+ MBOX_CMD_STOP_HSIC_IDLE,
+ MBOX_CMD_DBC_WAKE_STACK, /* unused */
+ MBOX_CMD_HSIC_PRETEND_CONNECT,
+ MBOX_CMD_RESET_SSPI,
+ MBOX_CMD_DISABLE_SS_LFPS_DETECTION,
+ MBOX_CMD_ENABLE_SS_LFPS_DETECTION,
+
+ MBOX_CMD_MAX,
+
+ /* Response message to above commands */
+ MBOX_CMD_ACK = 128,
+ MBOX_CMD_NAK
+};
+
+static const char * const mbox_cmd_name[] = {
+ [ 1] = "MSG_ENABLE",
+ [ 2] = "INC_FALCON_CLOCK",
+ [ 3] = "DEC_FALCON_CLOCK",
+ [ 4] = "INC_SSPI_CLOCK",
+ [ 5] = "DEC_SSPI_CLOCK",
+ [ 6] = "SET_BW",
+ [ 7] = "SET_SS_PWR_GATING",
+ [ 8] = "SET_SS_PWR_UNGATING",
+ [ 9] = "SAVE_DFE_CTLE_CTX",
+ [ 10] = "AIRPLANE_MODE_ENABLED",
+ [ 11] = "AIRPLANE_MODE_DISABLED",
+ [ 12] = "START_HSIC_IDLE",
+ [ 13] = "STOP_HSIC_IDLE",
+ [ 14] = "DBC_WAKE_STACK",
+ [ 15] = "HSIC_PRETEND_CONNECT",
+ [ 16] = "RESET_SSPI",
+ [ 17] = "DISABLE_SS_LFPS_DETECTION",
+ [ 18] = "ENABLE_SS_LFPS_DETECTION",
+ [128] = "ACK",
+ [129] = "NAK",
+};
+
+struct tegra_xusb_mbox_msg {
+ u32 cmd;
+ u32 data;
+};
+
+static inline u32 tegra_xusb_mbox_pack(const struct tegra_xusb_mbox_msg *msg)
+{
+ return (msg->cmd & CMD_TYPE_MASK) << CMD_TYPE_SHIFT |
+ (msg->data & CMD_DATA_MASK) << CMD_DATA_SHIFT;
+}
+static inline void tegra_xusb_mbox_unpack(struct tegra_xusb_mbox_msg *msg,
+ u32 value)
+{
+ msg->cmd = (value >> CMD_TYPE_SHIFT) & CMD_TYPE_MASK;
+ msg->data = (value >> CMD_DATA_SHIFT) & CMD_DATA_MASK;
+}
+
+static bool tegra_xusb_mbox_cmd_requires_ack(enum tegra_xusb_mbox_cmd cmd)
+{
+ switch (cmd) {
+ case MBOX_CMD_SET_BW:
+ case MBOX_CMD_ACK:
+ case MBOX_CMD_NAK:
+ return false;
+
+ default:
+ return true;
+ }
+}
+
+static int tegra_xusb_mbox_send(struct tegra_xusb *tegra,
+ const struct tegra_xusb_mbox_msg *msg)
+{
+ bool wait_for_idle = false;
+ u32 value;
+
+ /*
+ * Acquire the mailbox. The firmware still owns the mailbox for
+ * ACK/NAK messages.
+ */
+ if (!(msg->cmd == MBOX_CMD_ACK || msg->cmd == MBOX_CMD_NAK)) {
+ value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_OWNER);
+ if (value != MBOX_OWNER_NONE) {
+ dev_err(tegra->dev, "mailbox is busy\n");
+ return -EBUSY;
+ }
+
+ fpci_writel(tegra, MBOX_OWNER_SW, XUSB_CFG_ARU_MBOX_OWNER);
+
+ value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_OWNER);
+ if (value != MBOX_OWNER_SW) {
+ dev_err(tegra->dev, "failed to acquire mailbox\n");
+ return -EBUSY;
+ }
+
+ wait_for_idle = true;
+ }
+
+ value = tegra_xusb_mbox_pack(msg);
+ fpci_writel(tegra, value, XUSB_CFG_ARU_MBOX_DATA_IN);
+
+ value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_CMD);
+ value |= MBOX_INT_EN | MBOX_DEST_FALC;
+ fpci_writel(tegra, value, XUSB_CFG_ARU_MBOX_CMD);
+
+ if (wait_for_idle) {
+ unsigned long timeout = jiffies + msecs_to_jiffies(250);
+
+ while (time_before(jiffies, timeout)) {
+ value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_OWNER);
+ if (value == MBOX_OWNER_NONE)
+ break;
+
+ usleep_range(10, 20);
+ }
+
+ if (time_after(jiffies, timeout))
+ value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_OWNER);
+
+ if (value != MBOX_OWNER_NONE)
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static irqreturn_t tegra_xusb_mbox_irq(int irq, void *data)
+{
+ struct tegra_xusb *tegra = data;
+ u32 value;
+
+ /* clear mailbox interrupts */
+ value = fpci_readl(tegra, XUSB_CFG_ARU_SMI_INTR);
+ fpci_writel(tegra, value, XUSB_CFG_ARU_SMI_INTR);
+
+ if (value & MBOX_SMI_INTR_FW_HANG)
+ dev_err(tegra->dev, "controller firmware hang\n");
+
+ return IRQ_WAKE_THREAD;
+}
+
+static void tegra_xusb_mbox_handle(struct tegra_xusb *tegra,
+ const struct tegra_xusb_mbox_msg *msg)
+{
+ struct tegra_xusb_padctl *padctl = tegra->padctl;
+ const struct tegra_xusb_soc *soc = tegra->soc;
+ struct device *dev = tegra->dev;
+ struct tegra_xusb_mbox_msg rsp;
+ unsigned long mask;
+ unsigned int port;
+ bool idle, enable;
+ int err;
+
+ memset(&rsp, 0, sizeof(rsp));
+
+ switch (msg->cmd) {
+ case MBOX_CMD_INC_FALC_CLOCK:
+ case MBOX_CMD_DEC_FALC_CLOCK:
+ rsp.data = clk_get_rate(tegra->falcon_clk) / 1000;
+ if (rsp.data != msg->data)
+ rsp.cmd = MBOX_CMD_NAK;
+ else
+ rsp.cmd = MBOX_CMD_ACK;
+
+ break;
+
+ case MBOX_CMD_INC_SSPI_CLOCK:
+ case MBOX_CMD_DEC_SSPI_CLOCK:
+ err = tegra_xusb_set_ss_clk(tegra, msg->data * 1000);
+ if (err < 0)
+ rsp.cmd = MBOX_CMD_NAK;
+ else
+ rsp.cmd = MBOX_CMD_ACK;
+
+ rsp.data = clk_get_rate(tegra->ss_src_clk) / 1000;
+ break;
+
+ case MBOX_CMD_SET_BW:
+ /*
+ * TODO: Request bandwidth once EMC scaling is supported.
+ * Ignore for now since ACK/NAK is not required for SET_BW
+ * messages.
+ */
+ break;
+
+ case MBOX_CMD_SAVE_DFE_CTLE_CTX:
+ err = tegra_xusb_padctl_usb3_save_context(padctl, msg->data);
+ if (err < 0) {
+ dev_err(dev, "failed to save context for USB3#%u: %d\n",
+ msg->data, err);
+ rsp.cmd = MBOX_CMD_NAK;
+ } else {
+ rsp.cmd = MBOX_CMD_ACK;
+ }
+
+ rsp.data = msg->data;
+ break;
+
+ case MBOX_CMD_START_HSIC_IDLE:
+ case MBOX_CMD_STOP_HSIC_IDLE:
+ if (msg->cmd == MBOX_CMD_STOP_HSIC_IDLE)
+ idle = false;
+ else
+ idle = true;
+
+ mask = extract_field(msg->data, 1 + soc->ports.hsic.offset,
+ soc->ports.hsic.count);
+
+ for_each_set_bit(port, &mask, 32) {
+ err = tegra_xusb_padctl_hsic_set_idle(padctl, port,
+ idle);
+ if (err < 0)
+ break;
+ }
+
+ if (err < 0) {
+ dev_err(dev, "failed to set HSIC#%u %s: %d\n", port,
+ idle ? "idle" : "busy", err);
+ rsp.cmd = MBOX_CMD_NAK;
+ } else {
+ rsp.cmd = MBOX_CMD_ACK;
+ }
+
+ rsp.data = msg->data;
+ break;
+
+ case MBOX_CMD_DISABLE_SS_LFPS_DETECTION:
+ case MBOX_CMD_ENABLE_SS_LFPS_DETECTION:
+ if (msg->cmd == MBOX_CMD_DISABLE_SS_LFPS_DETECTION)
+ enable = false;
+ else
+ enable = true;
+
+ mask = extract_field(msg->data, 1 + soc->ports.usb3.offset,
+ soc->ports.usb3.count);
+
+ for_each_set_bit(port, &mask, soc->ports.usb3.count) {
+ err = tegra_xusb_padctl_usb3_set_lfps_detect(padctl,
+ port,
+ enable);
+ if (err < 0)
+ break;
+ }
+
+ if (err < 0) {
+ dev_err(dev,
+ "failed to %s LFPS detection on USB3#%u: %d\n",
+ enable ? "enable" : "disable", port, err);
+ rsp.cmd = MBOX_CMD_NAK;
+ } else {
+ rsp.cmd = MBOX_CMD_ACK;
+ }
+
+ rsp.data = msg->data;
+ break;
+
+ default:
+ dev_warn(dev, "unknown message: %#x\n", msg->cmd);
+ break;
+ }
+
+ if (rsp.cmd) {
+ const char *cmd = (rsp.cmd == MBOX_CMD_ACK) ? "ACK" : "NAK";
+
+ err = tegra_xusb_mbox_send(tegra, &rsp);
+ if (err < 0)
+ dev_err(dev, "failed to send %s: %d\n", cmd, err);
+ }
+}
+
+static irqreturn_t tegra_xusb_mbox_thread(int irq, void *data)
+{
+ struct tegra_xusb *tegra = data;
+ struct tegra_xusb_mbox_msg msg;
+ u32 value;
+
+ mutex_lock(&tegra->lock);
+
+ value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_DATA_OUT);
+ tegra_xusb_mbox_unpack(&msg, value);
+
+ value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_CMD);
+ value &= ~MBOX_DEST_SMI;
+ fpci_writel(tegra, value, XUSB_CFG_ARU_MBOX_CMD);
+
+ /* clear mailbox owner if no ACK/NAK is required */
+ if (!tegra_xusb_mbox_cmd_requires_ack(msg.cmd))
+ fpci_writel(tegra, MBOX_OWNER_NONE, XUSB_CFG_ARU_MBOX_OWNER);
+
+ tegra_xusb_mbox_handle(tegra, &msg);
+
+ mutex_unlock(&tegra->lock);
+ return IRQ_HANDLED;
+}
+
+static void tegra_xusb_ipfs_config(struct tegra_xusb *tegra)
+{
+ u32 value;
+
+ value = ipfs_readl(tegra, IPFS_XUSB_HOST_CONFIGURATION_0);
+ value |= IPFS_EN_FPCI;
+ ipfs_writel(tegra, value, IPFS_XUSB_HOST_CONFIGURATION_0);
+
+ usleep_range(10, 20);
+
+ /* Program BAR0 space */
+ value = fpci_readl(tegra, XUSB_CFG_4);
+ value &= ~(XUSB_BASE_ADDR_MASK << XUSB_BASE_ADDR_SHIFT);
+ value |= tegra->hcd->rsrc_start & (XUSB_BASE_ADDR_MASK <<
+ XUSB_BASE_ADDR_SHIFT);
+ fpci_writel(tegra, value, XUSB_CFG_4);
+
+ usleep_range(100, 200);
+
+ /* Enable bus master */
+ value = fpci_readl(tegra, XUSB_CFG_1);
+ value |= XUSB_IO_SPACE_EN | XUSB_MEM_SPACE_EN | XUSB_BUS_MASTER_EN;
+ fpci_writel(tegra, value, XUSB_CFG_1);
+
+ /* Enable interrupt assertion */
+ value = ipfs_readl(tegra, IPFS_XUSB_HOST_INTR_MASK_0);
+ value |= IPFS_IP_INT_MASK;
+ ipfs_writel(tegra, value, IPFS_XUSB_HOST_INTR_MASK_0);
+
+ /* Set hysteresis */
+ ipfs_writel(tegra, 0x80, IPFS_XUSB_HOST_CLKGATE_HYSTERESIS_0);
+}
+
+static int tegra_xusb_load_firmware(struct tegra_xusb *tegra)
+{
+ unsigned int code_tag_blocks, code_size_blocks, code_blocks;
+ struct tegra_xusb_fw_header *header;
+ struct device *dev = tegra->dev;
+ unsigned long timeout;
+ struct tm fw_tm;
+ time_t fw_time;
+ u64 fw_base;
+ u32 value;
+
+ if (csb_readl(tegra, XUSB_CSB_MP_ILOAD_BASE_LO) != 0) {
+ dev_info(dev, "Firmware already loaded, Falcon state %#x\n",
+ csb_readl(tegra, XUSB_FALC_CPUCTL));
+ return 0;
+ }
+
+ header = (struct tegra_xusb_fw_header *)tegra->fw_data;
+
+ /* Program the size of DFI into ILOAD_ATTR. */
+ csb_writel(tegra, tegra->fw_size, XUSB_CSB_MP_ILOAD_ATTR);
+
+ /*
+ * Boot code of the firmware reads the ILOAD_BASE registers
+ * to get to the start of the DFI in system memory.
+ */
+ fw_base = tegra->fw_dma_addr + sizeof(*header);
+ csb_writel(tegra, fw_base, XUSB_CSB_MP_ILOAD_BASE_LO);
+ csb_writel(tegra, fw_base >> 32, XUSB_CSB_MP_ILOAD_BASE_HI);
+
+ /* Set BOOTPATH to 1 in APMAP. */
+ csb_writel(tegra, APMAP_BOOTPATH, XUSB_CSB_MP_APMAP);
+
+ /* Invalidate L2IMEM. */
+ csb_writel(tegra, L2IMEMOP_INVALIDATE_ALL, XUSB_CSB_MP_L2IMEMOP_TRIG);
+
+ /*
+ * Initiate fetch of bootcode from system memory into L2IMEM.
+ * Program bootcode location and size in system memory.
+ */
+ code_tag_blocks = DIV_ROUND_UP(le32_to_cpu(header->boot_codetag),
+ IMEM_BLOCK_SIZE);
+ code_size_blocks = DIV_ROUND_UP(le32_to_cpu(header->boot_codesize),
+ IMEM_BLOCK_SIZE);
+ code_blocks = code_tag_blocks + code_size_blocks;
+
+ value = ((code_tag_blocks & L2IMEMOP_SIZE_SRC_OFFSET_MASK) <<
+ L2IMEMOP_SIZE_SRC_OFFSET_SHIFT) |
+ ((code_size_blocks & L2IMEMOP_SIZE_SRC_COUNT_MASK) <<
+ L2IMEMOP_SIZE_SRC_COUNT_SHIFT);
+ csb_writel(tegra, value, XUSB_CSB_MP_L2IMEMOP_SIZE);
+
+ /* Trigger L2IMEM load operation. */
+ csb_writel(tegra, L2IMEMOP_LOAD_LOCKED_RESULT,
+ XUSB_CSB_MP_L2IMEMOP_TRIG);
+
+ /* Setup Falcon auto-fill. */
+ csb_writel(tegra, code_size_blocks, XUSB_FALC_IMFILLCTL);
+
+ value = ((code_tag_blocks & IMFILLRNG1_TAG_MASK) <<
+ IMFILLRNG1_TAG_LO_SHIFT) |
+ ((code_blocks & IMFILLRNG1_TAG_MASK) <<
+ IMFILLRNG1_TAG_HI_SHIFT);
+ csb_writel(tegra, value, XUSB_FALC_IMFILLRNG1);
+
+ csb_writel(tegra, 0, XUSB_FALC_DMACTL);
+
+ msleep(50);
+
+ csb_writel(tegra, le32_to_cpu(header->boot_codetag),
+ XUSB_FALC_BOOTVEC);
+
+ /* Boot Falcon CPU and wait for it to enter the STOPPED (idle) state. */
+ timeout = jiffies + msecs_to_jiffies(5);
+
+ csb_writel(tegra, CPUCTL_STARTCPU, XUSB_FALC_CPUCTL);
+
+ while (time_before(jiffies, timeout)) {
+ if (csb_readl(tegra, XUSB_FALC_CPUCTL) == CPUCTL_STATE_STOPPED)
+ break;
+
+ usleep_range(100, 200);
+ }
+
+ if (csb_readl(tegra, XUSB_FALC_CPUCTL) != CPUCTL_STATE_STOPPED) {
+ dev_err(dev, "Falcon failed to start, state: %#x\n",
+ csb_readl(tegra, XUSB_FALC_CPUCTL));
+ return -EIO;
+ }
+
+ fw_time = le32_to_cpu(header->fwimg_created_time);
+ time_to_tm(fw_time, 0, &fw_tm);
+
+ dev_info(dev, "Firmware timestamp: %ld-%02d-%02d %02d:%02d:%02d UTC\n",
+ fw_tm.tm_year + 1900, fw_tm.tm_mon + 1, fw_tm.tm_mday,
+ fw_tm.tm_hour, fw_tm.tm_min, fw_tm.tm_sec);
+
+ return 0;
+}
+
+static int tegra_xusb_clk_enable(struct tegra_xusb *tegra)
+{
+ int err;
+
+ err = clk_prepare_enable(tegra->pll_e);
+ if (err < 0)
+ return err;
+
+ err = clk_prepare_enable(tegra->host_clk);
+ if (err < 0)
+ goto disable_plle;
+
+ err = clk_prepare_enable(tegra->ss_clk);
+ if (err < 0)
+ goto disable_host;
+
+ err = clk_prepare_enable(tegra->falcon_clk);
+ if (err < 0)
+ goto disable_ss;
+
+ err = clk_prepare_enable(tegra->fs_src_clk);
+ if (err < 0)
+ goto disable_falc;
+
+ err = clk_prepare_enable(tegra->hs_src_clk);
+ if (err < 0)
+ goto disable_fs_src;
+
+ err = tegra_xusb_set_ss_clk(tegra, TEGRA_XHCI_SS_HIGH_SPEED);
+ if (err < 0)
+ goto disable_hs_src;
+
+ return 0;
+
+disable_hs_src:
+ clk_disable_unprepare(tegra->hs_src_clk);
+disable_fs_src:
+ clk_disable_unprepare(tegra->fs_src_clk);
+disable_falc:
+ clk_disable_unprepare(tegra->falcon_clk);
+disable_ss:
+ clk_disable_unprepare(tegra->ss_clk);
+disable_host:
+ clk_disable_unprepare(tegra->host_clk);
+disable_plle:
+ clk_disable_unprepare(tegra->pll_e);
+ return err;
+}
+
+static void tegra_xusb_clk_disable(struct tegra_xusb *tegra)
+{
+ clk_disable_unprepare(tegra->pll_e);
+ clk_disable_unprepare(tegra->host_clk);
+ clk_disable_unprepare(tegra->ss_clk);
+ clk_disable_unprepare(tegra->falcon_clk);
+ clk_disable_unprepare(tegra->fs_src_clk);
+ clk_disable_unprepare(tegra->hs_src_clk);
+}
+
+static int tegra_xusb_phy_enable(struct tegra_xusb *tegra)
+{
+ unsigned int i;
+ int err;
+
+ for (i = 0; i < tegra->num_phys; i++) {
+ err = phy_init(tegra->phys[i]);
+ if (err)
+ goto disable_phy;
+
+ err = phy_power_on(tegra->phys[i]);
+ if (err) {
+ phy_exit(tegra->phys[i]);
+ goto disable_phy;
+ }
+ }
+
+ return 0;
+
+disable_phy:
+ while (i--) {
+ phy_power_off(tegra->phys[i]);
+ phy_exit(tegra->phys[i]);
+ }
+
+ return err;
+}
+
+static void tegra_xusb_phy_disable(struct tegra_xusb *tegra)
+{
+ unsigned int i;
+
+ for (i = 0; i < tegra->num_phys; i++) {
+ phy_power_off(tegra->phys[i]);
+ phy_exit(tegra->phys[i]);
+ }
+}
+
+static void tegra_xhci_quirks(struct device *dev, struct xhci_hcd *xhci)
+{
+ xhci->quirks |= XHCI_PLAT;
+}
+
+static int tegra_xhci_setup(struct usb_hcd *hcd)
+{
+ return xhci_gen_setup(hcd, tegra_xhci_quirks);
+}
+
+static const char * const tegra124_supply_names[] = {
+ "avddio-pex",
+ "dvddio-pex",
+ "avdd-usb",
+ "avdd-pll-utmip",
+ "avdd-pll-erefe",
+ "avdd-usb-ss-pll",
+ "hvdd-usb-ss",
+ "hvdd-usb-ss-pll-e",
+};
+
+static const struct tegra_xusb_phy_type tegra124_phy_types[] = {
+ { .name = "usb3", .num = 2, },
+ { .name = "usb2", .num = 3, },
+ { .name = "hsic", .num = 2, },
+};
+
+static const struct tegra_xusb_soc tegra124_soc = {
+ .firmware_file = "nvidia/tegra124/xusb.bin",
+ .supply_names = tegra124_supply_names,
+ .num_supplies = ARRAY_SIZE(tegra124_supply_names),
+ .phy_types = tegra124_phy_types,
+ .num_types = ARRAY_SIZE(tegra124_phy_types),
+ .ports = {
+ .usb2 = { .offset = 4, .count = 4, },
+ .hsic = { .offset = 6, .count = 2, },
+ .usb3 = { .offset = 0, .count = 2, },
+ },
+};
+MODULE_FIRMWARE("nvidia/tegra124/xusb.bin");
+
+static const struct of_device_id tegra_xusb_of_match[] = {
+ { .compatible = "nvidia,tegra124-xusb", .data = &tegra124_soc },
+ { },
+};
+MODULE_DEVICE_TABLE(of, tegra_xusb_of_match);
+
+static void tegra_xusb_probe_finish(const struct firmware *fw, void *context)
+{
+ struct tegra_xusb_fw_header *header;
+ struct tegra_xusb *tegra = context;
+ struct device *dev = tegra->dev;
+ struct tegra_xusb_mbox_msg msg;
+ struct xhci_hcd *xhci = NULL;
+ int err;
+
+ if (!fw) {
+ dev_err(tegra->dev, "no firmware loaded\n");
+ goto put_usb2_hcd;
+ }
+
+ /* Load Falcon controller with its firmware. */
+ header = (struct tegra_xusb_fw_header *)fw->data;
+ tegra->fw_size = le32_to_cpu(header->fwimg_len);
+
+ tegra->fw_data = dma_alloc_coherent(dev, tegra->fw_size,
+ &tegra->fw_dma_addr,
+ GFP_KERNEL);
+ if (!tegra->fw_data) {
+ dev_err(tegra->dev, "failed to allocate memory for firmware\n");
+ goto put_usb2_hcd;
+ }
+
+ memcpy(tegra->fw_data, fw->data, tegra->fw_size);
+
+ err = tegra_xusb_load_firmware(tegra);
+ if (err < 0) {
+ dev_err(tegra->dev, "failed to parse firmware: %d\n", err);
+ goto put_usb2_hcd;
+ }
+
+ err = usb_add_hcd(tegra->hcd, tegra->xhci_irq, IRQF_SHARED);
+ if (err < 0) {
+ dev_err(tegra->dev, "failed to add USB HCD: %d\n", err);
+ goto put_usb2_hcd;
+ }
+
+ device_wakeup_enable(tegra->hcd->self.controller);
+
+ /*
+ * USB 2.0 roothub is stored in drvdata now. Swap it with the Tegra HCD.
+ */
+ tegra->hcd = dev_get_drvdata(dev);
+ dev_set_drvdata(dev, tegra);
+ xhci = hcd_to_xhci(tegra->hcd);
+
+ xhci->shared_hcd = usb_create_shared_hcd(&tegra_xhci_hc_driver,
+ dev, dev_name(dev),
+ tegra->hcd);
+ if (!xhci->shared_hcd) {
+ dev_err(tegra->dev, "failed to create shared HCD\n");
+ goto dealloc_usb2_hcd;
+ }
+
+ err = usb_add_hcd(xhci->shared_hcd, tegra->xhci_irq, IRQF_SHARED);
+ if (err < 0) {
+ dev_err(tegra->dev, "failed to add shared HCD: %d\n", err);
+ goto put_usb3_hcd;
+ }
+
+ mutex_lock(&tegra->lock);
+
+ /* Enable firmware messages from controller. */
+ msg.cmd = MBOX_CMD_MSG_ENABLED;
+ msg.data = 0;
+
+ err = tegra_xusb_mbox_send(tegra, &msg);
+ if (err < 0) {
+ dev_err(tegra->dev, "failed to enable messages: %d\n", err);
+ mutex_unlock(&tegra->lock);
+ goto dealloc_usb3_hcd;
+ }
+
+ mutex_unlock(&tegra->lock);
+
+ err = devm_request_threaded_irq(dev, tegra->mbox_irq,
+ tegra_xusb_mbox_irq,
+ tegra_xusb_mbox_thread, 0,
+ dev_name(dev), tegra);
+ if (err < 0) {
+ dev_err(tegra->dev, "failed to request IRQ: %d\n", err);
+ goto dealloc_usb3_hcd;
+ }
+
+ tegra->fw_loaded = true;
+ release_firmware(fw);
+
+ return;
+
+ /* Free up as much as we can and wait to be unbound. */
+dealloc_usb3_hcd:
+ usb_remove_hcd(xhci->shared_hcd);
+put_usb3_hcd:
+ usb_put_hcd(xhci->shared_hcd);
+dealloc_usb2_hcd:
+ usb_remove_hcd(tegra->hcd);
+put_usb2_hcd:
+ usb_put_hcd(tegra->hcd);
+ tegra->hcd = NULL;
+ release_firmware(fw);
+}
+
+static int tegra_xusb_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct tegra_xusb *tegra;
+ unsigned int i, j, k;
+ struct resource *res;
+ struct usb_hcd *hcd;
+ struct phy *phy;
+ int err;
+
+ BUILD_BUG_ON(sizeof(struct tegra_xusb_fw_header) != 256);
+
+ tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL);
+ if (!tegra)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, tegra);
+ mutex_init(&tegra->lock);
+ tegra->dev = &pdev->dev;
+
+ match = of_match_device(tegra_xusb_of_match, &pdev->dev);
+ tegra->soc = match->data;
+
+ hcd = usb_create_hcd(&tegra_xhci_hc_driver, &pdev->dev,
+ dev_name(&pdev->dev));
+ if (!hcd)
+ return -ENOMEM;
+
+ tegra->hcd = hcd;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(hcd->regs)) {
+ err = PTR_ERR(hcd->regs);
+ goto put_hcd;
+ }
+
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ tegra->fpci_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(tegra->fpci_base)) {
+ err = PTR_ERR(tegra->fpci_base);
+ goto put_hcd;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+ tegra->ipfs_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(tegra->ipfs_base)) {
+ err = PTR_ERR(tegra->ipfs_base);
+ goto put_hcd;
+ }
+
+ tegra->xhci_irq = platform_get_irq(pdev, 0);
+ if (tegra->xhci_irq < 0) {
+ err = tegra->xhci_irq;
+ goto put_hcd;
+ }
+
+ tegra->mbox_irq = platform_get_irq(pdev, 1);
+ if (tegra->mbox_irq < 0) {
+ err = tegra->mbox_irq;
+ goto put_hcd;
+ }
+
+ tegra->padctl = tegra_xusb_padctl_get(&pdev->dev);
+ if (IS_ERR(tegra->padctl)) {
+ err = PTR_ERR(tegra->padctl);
+ goto put_hcd;
+ }
+
+ tegra->host_rst = devm_reset_control_get(&pdev->dev, "xusb_host");
+ if (IS_ERR(tegra->host_rst)) {
+ err = PTR_ERR(tegra->host_rst);
+ dev_err(&pdev->dev, "failed to get xusb_host reset: %d\n", err);
+ goto put_padctl;
+ }
+
+ tegra->ss_rst = devm_reset_control_get(&pdev->dev, "xusb_ss");
+ if (IS_ERR(tegra->ss_rst)) {
+ err = PTR_ERR(tegra->ss_rst);
+ dev_err(&pdev->dev, "failed to get xusb_ss reset: %d\n", err);
+ goto put_padctl;
+ }
+
+ tegra->host_clk = devm_clk_get(&pdev->dev, "xusb_host");
+ if (IS_ERR(tegra->host_clk)) {
+ err = PTR_ERR(tegra->host_clk);
+ dev_err(&pdev->dev, "failed to get xusb_host: %d\n", err);
+ goto put_padctl;
+ }
+
+ tegra->falcon_clk = devm_clk_get(&pdev->dev, "xusb_falcon_src");
+ if (IS_ERR(tegra->falcon_clk)) {
+ err = PTR_ERR(tegra->falcon_clk);
+ dev_err(&pdev->dev, "failed to get xusb_falcon_src: %d\n", err);
+ goto put_padctl;
+ }
+
+ tegra->ss_clk = devm_clk_get(&pdev->dev, "xusb_ss");
+ if (IS_ERR(tegra->ss_clk)) {
+ err = PTR_ERR(tegra->ss_clk);
+ dev_err(&pdev->dev, "failed to get xusb_ss: %d\n", err);
+ goto put_padctl;
+ }
+
+ tegra->ss_src_clk = devm_clk_get(&pdev->dev, "xusb_ss_src");
+ if (IS_ERR(tegra->ss_src_clk)) {
+ err = PTR_ERR(tegra->ss_src_clk);
+ dev_err(&pdev->dev, "failed to get xusb_ss_src: %d\n", err);
+ goto put_padctl;
+ }
+
+ tegra->hs_src_clk = devm_clk_get(&pdev->dev, "xusb_hs_src");
+ if (IS_ERR(tegra->hs_src_clk)) {
+ err = PTR_ERR(tegra->hs_src_clk);
+ dev_err(&pdev->dev, "failed to get xusb_hs_src: %d\n", err);
+ goto put_padctl;
+ }
+
+ tegra->fs_src_clk = devm_clk_get(&pdev->dev, "xusb_fs_src");
+ if (IS_ERR(tegra->fs_src_clk)) {
+ err = PTR_ERR(tegra->fs_src_clk);
+ dev_err(&pdev->dev, "failed to get xusb_fs_src: %d\n", err);
+ goto put_padctl;
+ }
+
+ tegra->pll_u_480m = devm_clk_get(&pdev->dev, "pll_u_480m");
+ if (IS_ERR(tegra->pll_u_480m)) {
+ err = PTR_ERR(tegra->pll_u_480m);
+ dev_err(&pdev->dev, "failed to get pll_u_480m: %d\n", err);
+ goto put_padctl;
+ }
+
+ tegra->clk_m = devm_clk_get(&pdev->dev, "clk_m");
+ if (IS_ERR(tegra->clk_m)) {
+ err = PTR_ERR(tegra->clk_m);
+ dev_err(&pdev->dev, "failed to get clk_m: %d\n", err);
+ goto put_padctl;
+ }
+
+ tegra->pll_e = devm_clk_get(&pdev->dev, "pll_e");
+ if (IS_ERR(tegra->pll_e)) {
+ err = PTR_ERR(tegra->pll_e);
+ dev_err(&pdev->dev, "failed to get pll_e: %d\n", err);
+ goto put_padctl;
+ }
+
+ err = tegra_xusb_clk_enable(tegra);
+ if (err) {
+ dev_err(&pdev->dev, "failed to enable clocks: %d\n", err);
+ goto put_padctl;
+ }
+
+ tegra->supplies = devm_kcalloc(&pdev->dev, tegra->soc->num_supplies,
+ sizeof(*tegra->supplies), GFP_KERNEL);
+ if (!tegra->supplies) {
+ err = -ENOMEM;
+ goto disable_clk;
+ }
+
+ for (i = 0; i < tegra->soc->num_supplies; i++)
+ tegra->supplies[i].supply = tegra->soc->supply_names[i];
+
+ err = devm_regulator_bulk_get(&pdev->dev, tegra->soc->num_supplies,
+ tegra->supplies);
+ if (err) {
+ dev_err(&pdev->dev, "failed to get regulators: %d\n", err);
+ goto disable_clk;
+ }
+
+ err = regulator_bulk_enable(tegra->soc->num_supplies, tegra->supplies);
+ if (err) {
+ dev_err(&pdev->dev, "failed to enable regulators: %d\n", err);
+ goto disable_clk;
+ }
+
+ for (i = 0; i < tegra->soc->num_types; i++)
+ tegra->num_phys += tegra->soc->phy_types[i].num;
+
+ tegra->phys = devm_kcalloc(&pdev->dev, tegra->num_phys,
+ sizeof(*tegra->phys), GFP_KERNEL);
+ if (!tegra->phys) {
+ dev_err(&pdev->dev, "failed to allocate PHY array\n");
+ err = -ENOMEM;
+ goto disable_regulator;
+ }
+
+ for (i = 0, k = 0; i < tegra->soc->num_types; i++) {
+ char prop[8];
+
+ for (j = 0; j < tegra->soc->phy_types[i].num; j++) {
+ snprintf(prop, sizeof(prop), "%s-%d",
+ tegra->soc->phy_types[i].name, j);
+
+ phy = devm_phy_optional_get(&pdev->dev, prop);
+ if (IS_ERR(phy)) {
+ dev_err(&pdev->dev, "failed to get PHY %s: %ld\n", prop, PTR_ERR(phy));
+ err = PTR_ERR(phy);
+ goto disable_regulator;
+ }
+
+ tegra->phys[k++] = phy;
+ }
+ }
+
+ tegra_xusb_ipfs_config(tegra);
+
+ err = tegra_xusb_phy_enable(tegra);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to enable PHYs: %d\n", err);
+ goto disable_regulator;
+ }
+
+ err = request_firmware_nowait(THIS_MODULE, true,
+ tegra->soc->firmware_file,
+ tegra->dev, GFP_KERNEL, tegra,
+ tegra_xusb_probe_finish);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to request firmware: %d\n", err);
+ goto disable_phy;
+ }
+
+ return 0;
+
+disable_phy:
+ tegra_xusb_phy_disable(tegra);
+disable_regulator:
+ regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
+disable_clk:
+ tegra_xusb_clk_disable(tegra);
+put_padctl:
+ tegra_xusb_padctl_put(tegra->padctl);
+put_hcd:
+ usb_put_hcd(hcd);
+ return err;
+}
+
+static int tegra_xusb_remove(struct platform_device *pdev)
+{
+ struct tegra_xusb *tegra = platform_get_drvdata(pdev);
+ struct usb_hcd *hcd = tegra->hcd;
+ struct xhci_hcd *xhci;
+
+ if (tegra->fw_loaded) {
+ xhci = hcd_to_xhci(hcd);
+ usb_remove_hcd(xhci->shared_hcd);
+ usb_put_hcd(xhci->shared_hcd);
+ usb_remove_hcd(hcd);
+ tegra_xusb_padctl_put(tegra->padctl);
+ usb_put_hcd(hcd);
+ kfree(xhci);
+ } else if (hcd) {
+ /* Unbound after probe(), but before firmware loading. */
+ tegra_xusb_padctl_put(tegra->padctl);
+ usb_put_hcd(hcd);
+ }
+
+ if (tegra->fw_data)
+ dma_free_coherent(tegra->dev, tegra->fw_size, tegra->fw_data,
+ tegra->fw_dma_addr);
+
+ tegra_xusb_phy_disable(tegra);
+ regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
+ tegra_xusb_clk_disable(tegra);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int tegra_xusb_suspend(struct device *dev)
+{
+ struct tegra_xusb *tegra = dev_get_drvdata(dev);
+ struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
+ bool wakeup = device_may_wakeup(dev);
+
+ /* TODO: Powergate controller across suspend/resume. */
+ return xhci_suspend(xhci, wakeup);
+}
+
+static int tegra_xusb_resume(struct device *dev)
+{
+ struct tegra_xusb *tegra = dev_get_drvdata(dev);
+ struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
+
+ return xhci_resume(xhci, 0);
+}
+#endif
+
+static const struct dev_pm_ops tegra_xusb_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(tegra_xusb_suspend, tegra_xusb_resume)
+};
+
+static struct platform_driver tegra_xusb_driver = {
+ .probe = tegra_xusb_probe,
+ .remove = tegra_xusb_remove,
+ .driver = {
+ .name = "tegra-xusb",
+ .pm = &tegra_xusb_pm_ops,
+ .of_match_table = tegra_xusb_of_match,
+ },
+};
+
+static const struct xhci_driver_overrides tegra_xhci_overrides __initconst = {
+ .extra_priv_size = sizeof(struct xhci_hcd),
+ .reset = tegra_xhci_setup,
+};
+
+static int __init tegra_xusb_init(void)
+{
+ xhci_init_driver(&tegra_xhci_hc_driver, &tegra_xhci_overrides);
+
+ return platform_driver_register(&tegra_xusb_driver);
+}
+module_init(tegra_xusb_init);
+
+static void __exit tegra_xusb_exit(void)
+{
+ platform_driver_unregister(&tegra_xusb_driver);
+}
+module_exit(tegra_xusb_exit);
+
+MODULE_AUTHOR("Andrew Bresticker <[email protected]>");
+MODULE_DESCRIPTION("NVIDIA Tegra XUSB xHCI host-controller driver");
+MODULE_LICENSE("GPL v2");
--
2.7.1

2016-03-04 16:21:35

by Thierry Reding

[permalink] [raw]
Subject: [PATCH v10 7/9] dt-bindings: usb: xhci-tegra: Add Tegra210 XUSB controller support

From: Thierry Reding <[email protected]>

Extend the Tegra XUSB controller device tree binding with Tegra210
support.

Signed-off-by: Thierry Reding <[email protected]>
---
.../devicetree/bindings/usb/nvidia,tegra124-xusb.txt | 12 ++++++++++++
1 file changed, 12 insertions(+)

diff --git a/Documentation/devicetree/bindings/usb/nvidia,tegra124-xusb.txt b/Documentation/devicetree/bindings/usb/nvidia,tegra124-xusb.txt
index 79616f9268d8..d28295a3e55f 100644
--- a/Documentation/devicetree/bindings/usb/nvidia,tegra124-xusb.txt
+++ b/Documentation/devicetree/bindings/usb/nvidia,tegra124-xusb.txt
@@ -9,6 +9,7 @@ Required properties:
- compatible: Must be:
- Tegra124: "nvidia,tegra124-xusb"
- Tegra132: "nvidia,tegra132-xusb", "nvidia,tegra124-xusb"
+ - Tegra210: "nvidia,tegra210-xusb"
- reg: Must contain the base and length of the xHCI host registers, XUSB FPCI
registers and XUSB IPFS registers.
- reg-names: Must contain the following entries:
@@ -50,6 +51,15 @@ For Tegra124 and Tegra132:
- hvdd-usb-ss-supply: High-voltage PCIe/USB3 power supply. Must supply 3.3 V.
- hvdd-usb-ss-pll-e-supply: High-voltage PLLE power supply. Must supply 3.3 V.

+For Tegra210:
+- dvddio-pex-supply: PCIe/USB3 analog logic power supply. Must supply 1.05 V.
+- hvddio-pex-supply: High-voltage PCIe/USB3 power supply. Must supply 1.8 V.
+- avdd-usb-supply: USB controller power supply. Must supply 3.3 V.
+- avdd-pll-utmip-supply: UTMI PLL power supply. Must supply 1.8 V.
+- avdd-pll-uerefe-supply: PLLE reference PLL power supply. Must supply 1.05 V.
+- dvdd-pex-pll-supply: PCIe/USB3 PLL power supply. Must supply 1.05 V.
+- hvdd-pex-pll-e-supply: High-voltage PLLE power supply. Must supply 1.8 V.
+
Optional properties:
--------------------
- phys: Must contain an entry for each entry in phy-names.
@@ -58,6 +68,8 @@ Optional properties:
following PHYs are available:
- Tegra124: usb2-0, usb2-1, usb2-2, hsic-0, hsic-1, usb3-0, usb3-1
- Tegra132: usb2-0, usb2-1, usb2-2, hsic-0, hsic-1, usb3-0, usb3-1
+ - Tegra210: usb2-0, usb2-1, usb2-2, usb2-3, hsic-0, usb3-0, usb3-1, usb3-2,
+ usb3-3

Example:
--------
--
2.7.1

2016-03-04 16:20:09

by Thierry Reding

[permalink] [raw]
Subject: [PATCH v10 5/9] phy: tegra: Add Tegra210 support

From: Thierry Reding <[email protected]>

Add support for the XUSB pad controller found on Tegra210 SoCs. The
hardware is roughly the same, but some of the registers have been moved
around and the number and type of supported pads has changed.

Signed-off-by: Thierry Reding <[email protected]>
---
Changes in v9:
- expose more public API for direct use by the xHCI driver

drivers/phy/tegra/Makefile | 1 +
drivers/phy/tegra/xusb-tegra210.c | 2041 +++++++++++++++++++++++++++++++++++++
include/soc/tegra/fuse.h | 1 +
3 files changed, 2043 insertions(+)
create mode 100644 drivers/phy/tegra/xusb-tegra210.c

diff --git a/drivers/phy/tegra/Makefile b/drivers/phy/tegra/Makefile
index 31150b4337cd..898589238fd9 100644
--- a/drivers/phy/tegra/Makefile
+++ b/drivers/phy/tegra/Makefile
@@ -3,3 +3,4 @@ obj-$(CONFIG_PHY_TEGRA_XUSB) += phy-tegra-xusb.o
phy-tegra-xusb-y += xusb.o
phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_124_SOC) += xusb-tegra124.o
phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_132_SOC) += xusb-tegra124.o
+phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_210_SOC) += xusb-tegra210.o
diff --git a/drivers/phy/tegra/xusb-tegra210.c b/drivers/phy/tegra/xusb-tegra210.c
new file mode 100644
index 000000000000..194ed98a5577
--- /dev/null
+++ b/drivers/phy/tegra/xusb-tegra210.c
@@ -0,0 +1,2041 @@
+/*
+ * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (C) 2015 Google, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk/tegra.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mailbox_client.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+
+#include <soc/tegra/fuse.h>
+
+#include "xusb.h"
+
+#define FUSE_SKU_CALIB_HS_CURR_LEVEL_PADX_SHIFT(x) \
+ ((x) ? (11 + ((x) - 1) * 6) : 0)
+#define FUSE_SKU_CALIB_HS_CURR_LEVEL_PAD_MASK 0x3f
+#define FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_SHIFT 7
+#define FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_MASK 0xf
+
+#define FUSE_USB_CALIB_EXT_RPD_CTRL_SHIFT 0
+#define FUSE_USB_CALIB_EXT_RPD_CTRL_MASK 0x1f
+
+#define XUSB_PADCTL_USB2_PAD_MUX 0x004
+#define XUSB_PADCTL_USB2_PAD_MUX_HSIC_PAD_TRK_SHIFT 16
+#define XUSB_PADCTL_USB2_PAD_MUX_HSIC_PAD_TRK_MASK 0x3
+#define XUSB_PADCTL_USB2_PAD_MUX_HSIC_PAD_TRK_XUSB 0x1
+#define XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_SHIFT 18
+#define XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_MASK 0x3
+#define XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_XUSB 0x1
+
+#define XUSB_PADCTL_USB2_PORT_CAP 0x008
+#define XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_HOST(x) (0x1 << ((x) * 4))
+#define XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_MASK(x) (0x3 << ((x) * 4))
+
+#define XUSB_PADCTL_SS_PORT_MAP 0x014
+#define XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(x) (1 << (((x) * 5) + 4))
+#define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_SHIFT(x) ((x) * 5)
+#define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(x) (0x7 << ((x) * 5))
+#define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(x, v) (((v) & 0x7) << ((x) * 5))
+
+#define XUSB_PADCTL_ELPG_PROGRAM1 0x024
+#define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN (1 << 31)
+#define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 30)
+#define XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN (1 << 29)
+#define XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN(x) (1 << (2 + (x) * 3))
+#define XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(x) \
+ (1 << (1 + (x) * 3))
+#define XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(x) (1 << ((x) * 3))
+
+#define XUSB_PADCTL_USB3_PAD_MUX 0x028
+#define XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(x) (1 << (1 + (x)))
+#define XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(x) (1 << (8 + (x)))
+
+#define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(x) (0x084 + (x) * 0x40)
+#define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_SHIFT 7
+#define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_MASK 0x3
+#define XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_FIX18 (1 << 6)
+
+#define XUSB_PADCTL_USB2_OTG_PADX_CTL0(x) (0x088 + (x) * 0x40)
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD_ZI (1 << 29)
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD2 (1 << 27)
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD (1 << 26)
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_SHIFT 0
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_MASK 0x3f
+
+#define XUSB_PADCTL_USB2_OTG_PADX_CTL1(x) (0x08c + (x) * 0x40)
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_RPD_CTRL_SHIFT 26
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_RPD_CTRL_MASK 0x1f
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_SHIFT 3
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_MASK 0xf
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR (1 << 2)
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DISC_OVRD (1 << 1)
+#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_CHRP_OVRD (1 << 0)
+
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 0x284
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD (1 << 11)
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_SHIFT 3
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_MASK 0x7
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_VAL 0x7
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_SHIFT 0
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_MASK 0x7
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_VAL 0x2
+
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL1 0x288
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_PD_TRK (1 << 26)
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_SHIFT 19
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_MASK 0x7f
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_VAL 0x0a
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_SHIFT 12
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK 0x7f
+#define XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_VAL 0x1e
+
+#define XUSB_PADCTL_HSIC_PADX_CTL0(x) (0x300 + (x) * 0x20)
+#define XUSB_PADCTL_HSIC_PAD_CTL0_RPU_STROBE (1 << 18)
+#define XUSB_PADCTL_HSIC_PAD_CTL0_RPU_DATA1 (1 << 17)
+#define XUSB_PADCTL_HSIC_PAD_CTL0_RPU_DATA0 (1 << 16)
+#define XUSB_PADCTL_HSIC_PAD_CTL0_RPD_STROBE (1 << 15)
+#define XUSB_PADCTL_HSIC_PAD_CTL0_RPD_DATA1 (1 << 14)
+#define XUSB_PADCTL_HSIC_PAD_CTL0_RPD_DATA0 (1 << 13)
+#define XUSB_PADCTL_HSIC_PAD_CTL0_PD_ZI_STROBE (1 << 9)
+#define XUSB_PADCTL_HSIC_PAD_CTL0_PD_ZI_DATA1 (1 << 8)
+#define XUSB_PADCTL_HSIC_PAD_CTL0_PD_ZI_DATA0 (1 << 7)
+#define XUSB_PADCTL_HSIC_PAD_CTL0_PD_RX_STROBE (1 << 6)
+#define XUSB_PADCTL_HSIC_PAD_CTL0_PD_RX_DATA1 (1 << 5)
+#define XUSB_PADCTL_HSIC_PAD_CTL0_PD_RX_DATA0 (1 << 4)
+#define XUSB_PADCTL_HSIC_PAD_CTL0_PD_TX_STROBE (1 << 3)
+#define XUSB_PADCTL_HSIC_PAD_CTL0_PD_TX_DATA1 (1 << 2)
+#define XUSB_PADCTL_HSIC_PAD_CTL0_PD_TX_DATA0 (1 << 1)
+
+#define XUSB_PADCTL_HSIC_PADX_CTL1(x) (0x304 + (x) * 0x20)
+#define XUSB_PADCTL_HSIC_PAD_CTL1_TX_RTUNEP_SHIFT 0
+#define XUSB_PADCTL_HSIC_PAD_CTL1_TX_RTUNEP_MASK 0xf
+
+#define XUSB_PADCTL_HSIC_PADX_CTL2(x) (0x308 + (x) * 0x20)
+#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_SHIFT 8
+#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_MASK 0xf
+#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_SHIFT 0
+#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_MASK 0xff
+
+#define XUSB_PADCTL_HSIC_PAD_TRK_CTL 0x340
+#define XUSB_PADCTL_HSIC_PAD_TRK_CTL_PD_TRK (1 << 19)
+#define XUSB_PADCTL_HSIC_PAD_TRK_CTL_TRK_DONE_RESET_TIMER_SHIFT 12
+#define XUSB_PADCTL_HSIC_PAD_TRK_CTL_TRK_DONE_RESET_TIMER_MASK 0x7f
+#define XUSB_PADCTL_HSIC_PAD_TRK_CTL_TRK_DONE_RESET_TIMER_VAL 0x0a
+#define XUSB_PADCTL_HSIC_PAD_TRK_CTL_TRK_START_TIMER_SHIFT 5
+#define XUSB_PADCTL_HSIC_PAD_TRK_CTL_TRK_START_TIMER_MASK 0x7f
+#define XUSB_PADCTL_HSIC_PAD_TRK_CTL_TRK_START_TIMER_VAL 0x1e
+
+#define XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL 0x344
+
+#define XUSB_PADCTL_UPHY_PLL_P0_CTL1 0x360
+#define XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_NDIV_SHIFT 20
+#define XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_NDIV_MASK 0xff
+#define XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_NDIV_USB_VAL 0x19
+#define XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_NDIV_SATA_VAL 0x1e
+#define XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_MDIV_SHIFT 16
+#define XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_MDIV_MASK 0x3
+#define XUSB_PADCTL_UPHY_PLL_CTL1_LOCKDET_STATUS (1 << 15)
+#define XUSB_PADCTL_UPHY_PLL_CTL1_PWR_OVRD (1 << 4)
+#define XUSB_PADCTL_UPHY_PLL_CTL1_ENABLE (1 << 3)
+#define XUSB_PADCTL_UPHY_PLL_CTL1_SLEEP_SHIFT 1
+#define XUSB_PADCTL_UPHY_PLL_CTL1_SLEEP_MASK 0x3
+#define XUSB_PADCTL_UPHY_PLL_CTL1_IDDQ (1 << 0)
+
+#define XUSB_PADCTL_UPHY_PLL_P0_CTL2 0x364
+#define XUSB_PADCTL_UPHY_PLL_CTL2_CAL_CTRL_SHIFT 4
+#define XUSB_PADCTL_UPHY_PLL_CTL2_CAL_CTRL_MASK 0xffffff
+#define XUSB_PADCTL_UPHY_PLL_CTL2_CAL_CTRL_VAL 0x136
+#define XUSB_PADCTL_UPHY_PLL_CTL2_CAL_OVRD (1 << 2)
+#define XUSB_PADCTL_UPHY_PLL_CTL2_CAL_DONE (1 << 1)
+#define XUSB_PADCTL_UPHY_PLL_CTL2_CAL_EN (1 << 0)
+
+#define XUSB_PADCTL_UPHY_PLL_P0_CTL4 0x36c
+#define XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_EN (1 << 15)
+#define XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_SHIFT 12
+#define XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_MASK 0x3
+#define XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_USB_VAL 0x2
+#define XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_SATA_VAL 0x0
+#define XUSB_PADCTL_UPHY_PLL_CTL4_REFCLKBUF_EN (1 << 8)
+#define XUSB_PADCTL_UPHY_PLL_CTL4_REFCLK_SEL_SHIFT 4
+#define XUSB_PADCTL_UPHY_PLL_CTL4_REFCLK_SEL_MASK 0xf
+
+#define XUSB_PADCTL_UPHY_PLL_P0_CTL5 0x370
+#define XUSB_PADCTL_UPHY_PLL_CTL5_DCO_CTRL_SHIFT 16
+#define XUSB_PADCTL_UPHY_PLL_CTL5_DCO_CTRL_MASK 0xff
+#define XUSB_PADCTL_UPHY_PLL_CTL5_DCO_CTRL_VAL 0x2a
+
+#define XUSB_PADCTL_UPHY_PLL_P0_CTL8 0x37c
+#define XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_DONE (1 << 31)
+#define XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_OVRD (1 << 15)
+#define XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_CLK_EN (1 << 13)
+#define XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_EN (1 << 12)
+
+#define XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL1(x) (0x460 + (x) * 0x40)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_IDLE_MODE_SHIFT 20
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_IDLE_MODE_MASK 0x3
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_IDLE_MODE_VAL 0x1
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_TERM_EN BIT(18)
+#define XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_MODE_OVRD BIT(13)
+
+#define XUSB_PADCTL_UPHY_PLL_S0_CTL1 0x860
+
+#define XUSB_PADCTL_UPHY_PLL_S0_CTL2 0x864
+
+#define XUSB_PADCTL_UPHY_PLL_S0_CTL4 0x86c
+
+#define XUSB_PADCTL_UPHY_PLL_S0_CTL5 0x870
+
+#define XUSB_PADCTL_UPHY_PLL_S0_CTL8 0x87c
+
+#define XUSB_PADCTL_UPHY_MISC_PAD_S0_CTL1 0x960
+
+#define XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(x) (0xa60 + (x) * 0x40)
+#define XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT 16
+#define XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_MASK 0x3
+#define XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_VAL 0x2
+
+#define XUSB_PADCTL_UPHY_USB3_PADX_ECTL2(x) (0xa64 + (x) * 0x40)
+#define XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_SHIFT 0
+#define XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_MASK 0xffff
+#define XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_VAL 0x00fc
+
+#define XUSB_PADCTL_UPHY_USB3_PADX_ECTL3(x) (0xa68 + (x) * 0x40)
+#define XUSB_PADCTL_UPHY_USB3_PAD_ECTL3_RX_DFE_VAL 0xc0077f1f
+
+#define XUSB_PADCTL_UPHY_USB3_PADX_ECTL4(x) (0xa6c + (x) * 0x40)
+#define XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_SHIFT 16
+#define XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_MASK 0xffff
+#define XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_VAL 0x01c7
+
+#define XUSB_PADCTL_UPHY_USB3_PADX_ECTL6(x) (0xa74 + (x) * 0x40)
+#define XUSB_PADCTL_UPHY_USB3_PAD_ECTL6_RX_EQ_CTRL_H_VAL 0xfcf01368
+
+struct tegra210_xusb_fuse_calibration {
+ u32 hs_curr_level[4];
+ u32 hs_term_range_adj;
+ u32 rpd_ctrl;
+};
+
+struct tegra210_xusb_padctl {
+ struct tegra_xusb_padctl base;
+
+ struct tegra210_xusb_fuse_calibration fuse;
+};
+
+static inline struct tegra210_xusb_padctl *
+to_tegra210_xusb_padctl(struct tegra_xusb_padctl *padctl)
+{
+ return container_of(padctl, struct tegra210_xusb_padctl, base);
+}
+
+/* must be called under padctl->lock */
+static int tegra210_pex_uphy_enable(struct tegra_xusb_padctl *padctl)
+{
+ struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie);
+ unsigned long timeout;
+ u32 value;
+ int err;
+
+ if (pcie->enable > 0) {
+ pcie->enable++;
+ return 0;
+ }
+
+ err = clk_prepare_enable(pcie->pll);
+ if (err < 0)
+ return err;
+
+ err = reset_control_deassert(pcie->rst);
+ if (err < 0)
+ goto disable;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL2);
+ value &= ~(XUSB_PADCTL_UPHY_PLL_CTL2_CAL_CTRL_MASK <<
+ XUSB_PADCTL_UPHY_PLL_CTL2_CAL_CTRL_SHIFT);
+ value |= XUSB_PADCTL_UPHY_PLL_CTL2_CAL_CTRL_VAL <<
+ XUSB_PADCTL_UPHY_PLL_CTL2_CAL_CTRL_SHIFT;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL2);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL5);
+ value &= ~(XUSB_PADCTL_UPHY_PLL_CTL5_DCO_CTRL_MASK <<
+ XUSB_PADCTL_UPHY_PLL_CTL5_DCO_CTRL_SHIFT);
+ value |= XUSB_PADCTL_UPHY_PLL_CTL5_DCO_CTRL_VAL <<
+ XUSB_PADCTL_UPHY_PLL_CTL5_DCO_CTRL_SHIFT;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL5);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL1);
+ value |= XUSB_PADCTL_UPHY_PLL_CTL1_PWR_OVRD;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL1);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL2);
+ value |= XUSB_PADCTL_UPHY_PLL_CTL2_CAL_OVRD;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL2);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL8);
+ value |= XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_OVRD;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL8);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL4);
+ value &= ~((XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_MASK <<
+ XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_SHIFT) |
+ (XUSB_PADCTL_UPHY_PLL_CTL4_REFCLK_SEL_MASK <<
+ XUSB_PADCTL_UPHY_PLL_CTL4_REFCLK_SEL_SHIFT));
+ value |= (XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_USB_VAL <<
+ XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_SHIFT) |
+ XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_EN;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL4);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL1);
+ value &= ~((XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_MDIV_MASK <<
+ XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_MDIV_SHIFT) |
+ (XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_NDIV_MASK <<
+ XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_NDIV_SHIFT));
+ value |= XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_NDIV_USB_VAL <<
+ XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_NDIV_SHIFT;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL1);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL1);
+ value &= ~XUSB_PADCTL_UPHY_PLL_CTL1_IDDQ;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL1);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL1);
+ value &= ~(XUSB_PADCTL_UPHY_PLL_CTL1_SLEEP_MASK <<
+ XUSB_PADCTL_UPHY_PLL_CTL1_SLEEP_SHIFT);
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL1);
+
+ usleep_range(10, 20);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL4);
+ value |= XUSB_PADCTL_UPHY_PLL_CTL4_REFCLKBUF_EN;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL4);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL2);
+ value |= XUSB_PADCTL_UPHY_PLL_CTL2_CAL_EN;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL2);
+
+ timeout = jiffies + msecs_to_jiffies(100);
+
+ while (time_before(jiffies, timeout)) {
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL2);
+ if (value & XUSB_PADCTL_UPHY_PLL_CTL2_CAL_DONE)
+ break;
+
+ usleep_range(10, 20);
+ }
+
+ if (time_after_eq(jiffies, timeout)) {
+ err = -ETIMEDOUT;
+ goto reset;
+ }
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL2);
+ value &= ~XUSB_PADCTL_UPHY_PLL_CTL2_CAL_EN;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL2);
+
+ timeout = jiffies + msecs_to_jiffies(100);
+
+ while (time_before(jiffies, timeout)) {
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL2);
+ if (!(value & XUSB_PADCTL_UPHY_PLL_CTL2_CAL_DONE))
+ break;
+
+ usleep_range(10, 20);
+ }
+
+ if (time_after_eq(jiffies, timeout)) {
+ err = -ETIMEDOUT;
+ goto reset;
+ }
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL1);
+ value |= XUSB_PADCTL_UPHY_PLL_CTL1_ENABLE;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL1);
+
+ timeout = jiffies + msecs_to_jiffies(100);
+
+ while (time_before(jiffies, timeout)) {
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL1);
+ if (value & XUSB_PADCTL_UPHY_PLL_CTL1_LOCKDET_STATUS)
+ break;
+
+ usleep_range(10, 20);
+ }
+
+ if (time_after_eq(jiffies, timeout)) {
+ err = -ETIMEDOUT;
+ goto reset;
+ }
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL8);
+ value |= XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_EN |
+ XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_CLK_EN;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL8);
+
+ timeout = jiffies + msecs_to_jiffies(100);
+
+ while (time_before(jiffies, timeout)) {
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL8);
+ if (value & XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_DONE)
+ break;
+
+ usleep_range(10, 20);
+ }
+
+ if (time_after_eq(jiffies, timeout)) {
+ err = -ETIMEDOUT;
+ goto reset;
+ }
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL8);
+ value &= ~XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_EN;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL8);
+
+ timeout = jiffies + msecs_to_jiffies(100);
+
+ while (time_before(jiffies, timeout)) {
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL8);
+ if (!(value & XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_DONE))
+ break;
+
+ usleep_range(10, 20);
+ }
+
+ if (time_after_eq(jiffies, timeout)) {
+ err = -ETIMEDOUT;
+ goto reset;
+ }
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL8);
+ value &= ~XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_CLK_EN;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL8);
+
+ tegra210_xusb_pll_hw_control_enable();
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL1);
+ value &= ~XUSB_PADCTL_UPHY_PLL_CTL1_PWR_OVRD;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL1);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL2);
+ value &= ~XUSB_PADCTL_UPHY_PLL_CTL2_CAL_OVRD;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL2);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_P0_CTL8);
+ value &= ~XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_OVRD;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_P0_CTL8);
+
+ usleep_range(10, 20);
+
+ tegra210_xusb_pll_hw_sequence_start();
+
+ pcie->enable++;
+
+ return 0;
+
+reset:
+ reset_control_assert(pcie->rst);
+disable:
+ clk_disable_unprepare(pcie->pll);
+ return err;
+}
+
+static void tegra210_pex_uphy_disable(struct tegra_xusb_padctl *padctl)
+{
+ struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(padctl->pcie);
+
+ mutex_lock(&padctl->lock);
+
+ if (WARN_ON(pcie->enable == 0))
+ goto unlock;
+
+ if (--pcie->enable > 0)
+ goto unlock;
+
+ reset_control_assert(pcie->rst);
+ clk_disable_unprepare(pcie->pll);
+
+unlock:
+ mutex_unlock(&padctl->lock);
+}
+
+/* must be called under padctl->lock */
+static int tegra210_sata_uphy_enable(struct tegra_xusb_padctl *padctl, bool usb)
+{
+ struct tegra_xusb_sata_pad *sata = to_sata_pad(padctl->sata);
+ unsigned long timeout;
+ u32 value;
+ int err;
+
+ if (sata->enable > 0) {
+ sata->enable++;
+ return 0;
+ }
+
+ err = clk_prepare_enable(sata->pll);
+ if (err < 0)
+ return err;
+
+ err = reset_control_deassert(sata->rst);
+ if (err < 0)
+ goto disable;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL2);
+ value &= ~(XUSB_PADCTL_UPHY_PLL_CTL2_CAL_CTRL_MASK <<
+ XUSB_PADCTL_UPHY_PLL_CTL2_CAL_CTRL_SHIFT);
+ value |= XUSB_PADCTL_UPHY_PLL_CTL2_CAL_CTRL_VAL <<
+ XUSB_PADCTL_UPHY_PLL_CTL2_CAL_CTRL_SHIFT;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL2);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL5);
+ value &= ~(XUSB_PADCTL_UPHY_PLL_CTL5_DCO_CTRL_MASK <<
+ XUSB_PADCTL_UPHY_PLL_CTL5_DCO_CTRL_SHIFT);
+ value |= XUSB_PADCTL_UPHY_PLL_CTL5_DCO_CTRL_VAL <<
+ XUSB_PADCTL_UPHY_PLL_CTL5_DCO_CTRL_SHIFT;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL5);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL1);
+ value |= XUSB_PADCTL_UPHY_PLL_CTL1_PWR_OVRD;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL1);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL2);
+ value |= XUSB_PADCTL_UPHY_PLL_CTL2_CAL_OVRD;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL2);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL8);
+ value |= XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_OVRD;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL8);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL4);
+ value &= ~((XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_MASK <<
+ XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_SHIFT) |
+ (XUSB_PADCTL_UPHY_PLL_CTL4_REFCLK_SEL_MASK <<
+ XUSB_PADCTL_UPHY_PLL_CTL4_REFCLK_SEL_SHIFT));
+ value |= XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_EN;
+
+ if (usb)
+ value |= (XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_USB_VAL <<
+ XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_SHIFT);
+ else
+ value |= (XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_SATA_VAL <<
+ XUSB_PADCTL_UPHY_PLL_CTL4_TXCLKREF_SEL_SHIFT);
+
+ /* XXX PLL0_XDIGCLK_EN */
+ /*
+ value &= ~(1 << 19);
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL4);
+ */
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL1);
+ value &= ~((XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_MDIV_MASK <<
+ XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_MDIV_SHIFT) |
+ (XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_NDIV_MASK <<
+ XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_NDIV_SHIFT));
+
+ if (usb)
+ value |= XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_NDIV_USB_VAL <<
+ XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_NDIV_SHIFT;
+ else
+ value |= XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_NDIV_SATA_VAL <<
+ XUSB_PADCTL_UPHY_PLL_CTL1_FREQ_NDIV_SHIFT;
+
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL1);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL1);
+ value &= ~XUSB_PADCTL_UPHY_PLL_CTL1_IDDQ;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL1);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL1);
+ value &= ~(XUSB_PADCTL_UPHY_PLL_CTL1_SLEEP_MASK <<
+ XUSB_PADCTL_UPHY_PLL_CTL1_SLEEP_SHIFT);
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL1);
+
+ usleep_range(10, 20);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL4);
+ value |= XUSB_PADCTL_UPHY_PLL_CTL4_REFCLKBUF_EN;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL4);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL2);
+ value |= XUSB_PADCTL_UPHY_PLL_CTL2_CAL_EN;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL2);
+
+ timeout = jiffies + msecs_to_jiffies(100);
+
+ while (time_before(jiffies, timeout)) {
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL2);
+ if (value & XUSB_PADCTL_UPHY_PLL_CTL2_CAL_DONE)
+ break;
+
+ usleep_range(10, 20);
+ }
+
+ if (time_after_eq(jiffies, timeout)) {
+ err = -ETIMEDOUT;
+ goto reset;
+ }
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL2);
+ value &= ~XUSB_PADCTL_UPHY_PLL_CTL2_CAL_EN;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL2);
+
+ timeout = jiffies + msecs_to_jiffies(100);
+
+ while (time_before(jiffies, timeout)) {
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL2);
+ if (!(value & XUSB_PADCTL_UPHY_PLL_CTL2_CAL_DONE))
+ break;
+
+ usleep_range(10, 20);
+ }
+
+ if (time_after_eq(jiffies, timeout)) {
+ err = -ETIMEDOUT;
+ goto reset;
+ }
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL1);
+ value |= XUSB_PADCTL_UPHY_PLL_CTL1_ENABLE;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL1);
+
+ timeout = jiffies + msecs_to_jiffies(100);
+
+ while (time_before(jiffies, timeout)) {
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL1);
+ if (value & XUSB_PADCTL_UPHY_PLL_CTL1_LOCKDET_STATUS)
+ break;
+
+ usleep_range(10, 20);
+ }
+
+ if (time_after_eq(jiffies, timeout)) {
+ err = -ETIMEDOUT;
+ goto reset;
+ }
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL8);
+ value |= XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_EN |
+ XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_CLK_EN;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL8);
+
+ timeout = jiffies + msecs_to_jiffies(100);
+
+ while (time_before(jiffies, timeout)) {
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL8);
+ if (value & XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_DONE)
+ break;
+
+ usleep_range(10, 20);
+ }
+
+ if (time_after_eq(jiffies, timeout)) {
+ err = -ETIMEDOUT;
+ goto reset;
+ }
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL8);
+ value &= ~XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_EN;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL8);
+
+ timeout = jiffies + msecs_to_jiffies(100);
+
+ while (time_before(jiffies, timeout)) {
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL8);
+ if (!(value & XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_DONE))
+ break;
+
+ usleep_range(10, 20);
+ }
+
+ if (time_after_eq(jiffies, timeout)) {
+ err = -ETIMEDOUT;
+ goto reset;
+ }
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL8);
+ value &= ~XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_CLK_EN;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL8);
+
+ tegra210_sata_pll_hw_control_enable();
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL1);
+ value &= ~XUSB_PADCTL_UPHY_PLL_CTL1_PWR_OVRD;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL1);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL2);
+ value &= ~XUSB_PADCTL_UPHY_PLL_CTL2_CAL_OVRD;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL2);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_PLL_S0_CTL8);
+ value &= ~XUSB_PADCTL_UPHY_PLL_CTL8_RCAL_OVRD;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_PLL_S0_CTL8);
+
+ usleep_range(10, 20);
+
+ tegra210_sata_pll_hw_sequence_start();
+
+ sata->enable++;
+
+ return 0;
+
+reset:
+ reset_control_assert(sata->rst);
+disable:
+ clk_disable_unprepare(sata->pll);
+ return err;
+}
+
+static void tegra210_sata_uphy_disable(struct tegra_xusb_padctl *padctl)
+{
+ struct tegra_xusb_sata_pad *sata = to_sata_pad(padctl->sata);
+
+ mutex_lock(&padctl->lock);
+
+ if (WARN_ON(sata->enable == 0))
+ goto unlock;
+
+ if (--sata->enable > 0)
+ goto unlock;
+
+ reset_control_assert(sata->rst);
+ clk_disable_unprepare(sata->pll);
+
+unlock:
+ mutex_unlock(&padctl->lock);
+}
+
+static int tegra210_xusb_padctl_enable(struct tegra_xusb_padctl *padctl)
+{
+ u32 value;
+
+ mutex_lock(&padctl->lock);
+
+ if (padctl->enable++ > 0)
+ goto out;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value &= ~XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN;
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value &= ~XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY;
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value &= ~XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN;
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+out:
+ mutex_unlock(&padctl->lock);
+ return 0;
+}
+
+static int tegra210_xusb_padctl_disable(struct tegra_xusb_padctl *padctl)
+{
+ u32 value;
+
+ mutex_lock(&padctl->lock);
+
+ if (WARN_ON(padctl->enable == 0))
+ goto out;
+
+ if (--padctl->enable > 0)
+ goto out;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value |= XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_VCORE_DOWN;
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value |= XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN_EARLY;
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value |= XUSB_PADCTL_ELPG_PROGRAM1_AUX_MUX_LP0_CLAMP_EN;
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+out:
+ mutex_unlock(&padctl->lock);
+ return 0;
+}
+
+static int tegra210_hsic_set_idle(struct tegra_xusb_padctl *padctl,
+ unsigned int index, bool idle)
+{
+ u32 value;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL0(index));
+
+ value &= ~(XUSB_PADCTL_HSIC_PAD_CTL0_RPU_DATA0 |
+ XUSB_PADCTL_HSIC_PAD_CTL0_RPU_DATA1 |
+ XUSB_PADCTL_HSIC_PAD_CTL0_RPD_STROBE);
+
+ if (idle)
+ value |= XUSB_PADCTL_HSIC_PAD_CTL0_RPD_DATA0 |
+ XUSB_PADCTL_HSIC_PAD_CTL0_RPD_DATA1 |
+ XUSB_PADCTL_HSIC_PAD_CTL0_RPU_STROBE;
+ else
+ value &= ~(XUSB_PADCTL_HSIC_PAD_CTL0_RPD_DATA0 |
+ XUSB_PADCTL_HSIC_PAD_CTL0_RPD_DATA1 |
+ XUSB_PADCTL_HSIC_PAD_CTL0_RPU_STROBE);
+
+ padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL0(index));
+
+ return 0;
+}
+
+static int tegra210_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl,
+ unsigned int index, bool enable)
+{
+ struct tegra_xusb_port *port;
+ struct tegra_xusb_lane *lane;
+ u32 value, offset;
+
+ port = tegra_xusb_find_port(padctl, "usb3", index);
+ if (!port)
+ return -ENODEV;
+
+ lane = port->lane;
+
+ if (lane->pad == padctl->pcie)
+ offset = XUSB_PADCTL_UPHY_MISC_PAD_PX_CTL1(lane->index);
+ else
+ offset = XUSB_PADCTL_UPHY_MISC_PAD_S0_CTL1;
+
+ value = padctl_readl(padctl, offset);
+
+ value &= ~((XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_IDLE_MODE_MASK <<
+ XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_IDLE_MODE_SHIFT) |
+ XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_TERM_EN |
+ XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_MODE_OVRD);
+
+ if (!enable) {
+ value |= (XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_IDLE_MODE_VAL <<
+ XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_IDLE_MODE_SHIFT) |
+ XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_TERM_EN |
+ XUSB_PADCTL_UPHY_MISC_PAD_CTL1_AUX_RX_MODE_OVRD;
+ }
+
+ padctl_writel(padctl, value, offset);
+
+ return 0;
+}
+
+#define TEGRA210_LANE(_name, _offset, _shift, _mask, _type) \
+ { \
+ .name = _name, \
+ .offset = _offset, \
+ .shift = _shift, \
+ .mask = _mask, \
+ .num_funcs = ARRAY_SIZE(tegra210_##_type##_functions), \
+ .funcs = tegra210_##_type##_functions, \
+ }
+
+static const char *tegra210_usb2_functions[] = {
+ "snps",
+ "xusb",
+ "uart"
+};
+
+static const struct tegra_xusb_lane_soc tegra210_usb2_lanes[] = {
+ TEGRA210_LANE("usb2-0", 0x004, 0, 0x3, usb2),
+ TEGRA210_LANE("usb2-1", 0x004, 2, 0x3, usb2),
+ TEGRA210_LANE("usb2-2", 0x004, 4, 0x3, usb2),
+ TEGRA210_LANE("usb2-3", 0x004, 6, 0x3, usb2),
+};
+
+static struct tegra_xusb_lane *
+tegra210_usb2_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np,
+ unsigned int index)
+{
+ struct tegra_xusb_usb2_lane *usb2;
+ int err;
+
+ usb2 = kzalloc(sizeof(*usb2), GFP_KERNEL);
+ if (!usb2)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&usb2->base.list);
+ usb2->base.soc = &pad->soc->lanes[index];
+ usb2->base.index = index;
+ usb2->base.pad = pad;
+ usb2->base.np = np;
+
+ err = tegra_xusb_lane_parse_dt(&usb2->base, np);
+ if (err < 0) {
+ kfree(usb2);
+ return ERR_PTR(err);
+ }
+
+ return &usb2->base;
+}
+
+static void tegra210_usb2_lane_remove(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_usb2_lane *usb2 = to_usb2_lane(lane);
+
+ kfree(usb2);
+}
+
+static const struct tegra_xusb_lane_ops tegra210_usb2_lane_ops = {
+ .probe = tegra210_usb2_lane_probe,
+ .remove = tegra210_usb2_lane_remove,
+};
+
+static int tegra210_usb2_phy_init(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ u32 value;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_PAD_MUX);
+ value &= ~(XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_MASK <<
+ XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_SHIFT);
+ value |= XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_XUSB <<
+ XUSB_PADCTL_USB2_PAD_MUX_USB2_BIAS_PAD_SHIFT;
+ padctl_writel(padctl, value, XUSB_PADCTL_USB2_PAD_MUX);
+
+ return tegra210_xusb_padctl_enable(padctl);
+}
+
+static int tegra210_usb2_phy_exit(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+ return tegra210_xusb_padctl_disable(lane->pad->padctl);
+}
+
+static int tegra210_usb2_phy_power_on(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_usb2_lane *usb2 = to_usb2_lane(lane);
+ struct tegra_xusb_usb2_pad *pad = to_usb2_pad(lane->pad);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ struct tegra210_xusb_padctl *priv;
+ struct tegra_xusb_usb2_port *port;
+ unsigned int index = lane->index;
+ u32 value;
+ int err;
+
+ port = tegra_xusb_find_usb2_port(padctl, index);
+ if (!port) {
+ dev_err(&phy->dev, "no port found for USB2 lane %u\n", index);
+ return -ENODEV;
+ }
+
+ priv = to_tegra210_xusb_padctl(padctl);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
+ value &= ~((XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_MASK <<
+ XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_SHIFT) |
+ (XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_MASK <<
+ XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_SHIFT));
+ value |= (XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_VAL <<
+ XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_SHIFT);
+
+ if (tegra_sku_info.revision < TEGRA_REVISION_A02)
+ value |=
+ (XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_VAL <<
+ XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_SHIFT);
+
+ padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_PORT_CAP);
+ value &= ~XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_MASK(index);
+ value |= XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_HOST(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_USB2_PORT_CAP);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
+ value &= ~((XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_MASK <<
+ XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_SHIFT) |
+ XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD |
+ XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD2 |
+ XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD_ZI);
+ value |= (priv->fuse.hs_curr_level[index] +
+ usb2->hs_curr_level_offset) <<
+ XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_SHIFT;
+ padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
+ value &= ~((XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_MASK <<
+ XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_SHIFT) |
+ (XUSB_PADCTL_USB2_OTG_PAD_CTL1_RPD_CTRL_MASK <<
+ XUSB_PADCTL_USB2_OTG_PAD_CTL1_RPD_CTRL_SHIFT) |
+ XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR |
+ XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_CHRP_OVRD |
+ XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DISC_OVRD);
+ value |= (priv->fuse.hs_term_range_adj <<
+ XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_SHIFT) |
+ (priv->fuse.rpd_ctrl <<
+ XUSB_PADCTL_USB2_OTG_PAD_CTL1_RPD_CTRL_SHIFT);
+ padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
+
+ value = padctl_readl(padctl,
+ XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(index));
+ value &= ~(XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_MASK <<
+ XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_LEV_SHIFT);
+ value |= XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPAD_CTL1_VREG_FIX18;
+ padctl_writel(padctl, value,
+ XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(index));
+
+ err = regulator_enable(port->supply);
+ if (err)
+ return err;
+
+ mutex_lock(&padctl->lock);
+
+ if (pad->enable > 0) {
+ pad->enable++;
+ mutex_unlock(&padctl->lock);
+ return 0;
+ }
+
+ err = clk_prepare_enable(pad->clk);
+ if (err)
+ goto disable_regulator;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
+ value &= ~((XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_MASK <<
+ XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_SHIFT) |
+ (XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_MASK <<
+ XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_SHIFT));
+ value |= (XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_VAL <<
+ XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_START_TIMER_SHIFT) |
+ (XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_VAL <<
+ XUSB_PADCTL_USB2_BIAS_PAD_CTL1_TRK_DONE_RESET_TIMER_SHIFT);
+ padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
+ value &= ~XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD;
+ padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
+
+ udelay(1);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
+ value &= ~XUSB_PADCTL_USB2_BIAS_PAD_CTL1_PD_TRK;
+ padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL1);
+
+ udelay(50);
+
+ clk_disable_unprepare(pad->clk);
+
+ pad->enable++;
+ mutex_unlock(&padctl->lock);
+
+ return 0;
+
+disable_regulator:
+ regulator_disable(port->supply);
+ mutex_unlock(&padctl->lock);
+ return err;
+}
+
+static int tegra210_usb2_phy_power_off(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_usb2_pad *pad = to_usb2_pad(lane->pad);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ struct tegra_xusb_usb2_port *port;
+ u32 value;
+
+ port = tegra_xusb_find_usb2_port(padctl, lane->index);
+ if (!port) {
+ dev_err(&phy->dev, "no port found for USB2 lane %u\n",
+ lane->index);
+ return -ENODEV;
+ }
+
+ mutex_lock(&padctl->lock);
+
+ if (WARN_ON(pad->enable == 0))
+ goto out;
+
+ if (--pad->enable > 0)
+ goto out;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
+ value |= XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD;
+ padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
+
+out:
+ regulator_disable(port->supply);
+ mutex_unlock(&padctl->lock);
+ return 0;
+}
+
+static const struct phy_ops tegra210_usb2_phy_ops = {
+ .init = tegra210_usb2_phy_init,
+ .exit = tegra210_usb2_phy_exit,
+ .power_on = tegra210_usb2_phy_power_on,
+ .power_off = tegra210_usb2_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static struct tegra_xusb_pad *
+tegra210_usb2_pad_probe(struct tegra_xusb_padctl *padctl,
+ const struct tegra_xusb_pad_soc *soc,
+ struct device_node *np)
+{
+ struct tegra_xusb_usb2_pad *usb2;
+ struct tegra_xusb_pad *pad;
+ int err;
+
+ usb2 = kzalloc(sizeof(*usb2), GFP_KERNEL);
+ if (!usb2)
+ return ERR_PTR(-ENOMEM);
+
+ pad = &usb2->base;
+ pad->ops = &tegra210_usb2_lane_ops;
+ pad->soc = soc;
+
+ err = tegra_xusb_pad_init(pad, padctl, np);
+ if (err < 0)
+ goto free;
+
+ usb2->clk = devm_clk_get(&pad->dev, "trk");
+ if (IS_ERR(usb2->clk)) {
+ err = PTR_ERR(usb2->clk);
+ dev_err(&pad->dev, "failed to get trk clock: %d\n", err);
+ goto unregister;
+ }
+
+ err = tegra_xusb_pad_register(pad, &tegra210_usb2_phy_ops);
+ if (err < 0)
+ goto unregister;
+
+ dev_set_drvdata(&pad->dev, pad);
+
+ return pad;
+
+unregister:
+ device_unregister(&pad->dev);
+free:
+ kfree(usb2);
+ return ERR_PTR(err);
+}
+
+static void tegra210_usb2_pad_remove(struct tegra_xusb_pad *pad)
+{
+ struct tegra_xusb_usb2_pad *usb2 = to_usb2_pad(pad);
+
+ kfree(usb2);
+}
+
+static const struct tegra_xusb_pad_ops tegra210_usb2_ops = {
+ .probe = tegra210_usb2_pad_probe,
+ .remove = tegra210_usb2_pad_remove,
+};
+
+static const struct tegra_xusb_pad_soc tegra210_usb2_pad = {
+ .name = "usb2",
+ .num_lanes = ARRAY_SIZE(tegra210_usb2_lanes),
+ .lanes = tegra210_usb2_lanes,
+ .ops = &tegra210_usb2_ops,
+};
+
+static const char *tegra210_hsic_functions[] = {
+ "snps",
+ "xusb",
+};
+
+static const struct tegra_xusb_lane_soc tegra210_hsic_lanes[] = {
+ TEGRA210_LANE("hsic-0", 0x004, 14, 0x1, hsic),
+};
+
+static struct tegra_xusb_lane *
+tegra210_hsic_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np,
+ unsigned int index)
+{
+ struct tegra_xusb_hsic_lane *hsic;
+ int err;
+
+ hsic = kzalloc(sizeof(*hsic), GFP_KERNEL);
+ if (!hsic)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&hsic->base.list);
+ hsic->base.soc = &pad->soc->lanes[index];
+ hsic->base.index = index;
+ hsic->base.pad = pad;
+ hsic->base.np = np;
+
+ err = tegra_xusb_lane_parse_dt(&hsic->base, np);
+ if (err < 0) {
+ kfree(hsic);
+ return ERR_PTR(err);
+ }
+
+ return &hsic->base;
+}
+
+static void tegra210_hsic_lane_remove(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_hsic_lane *hsic = to_hsic_lane(lane);
+
+ kfree(hsic);
+}
+
+static const struct tegra_xusb_lane_ops tegra210_hsic_lane_ops = {
+ .probe = tegra210_hsic_lane_probe,
+ .remove = tegra210_hsic_lane_remove,
+};
+
+static int tegra210_hsic_phy_init(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ u32 value;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB2_PAD_MUX);
+ value &= ~(XUSB_PADCTL_USB2_PAD_MUX_HSIC_PAD_TRK_MASK <<
+ XUSB_PADCTL_USB2_PAD_MUX_HSIC_PAD_TRK_SHIFT);
+ value |= XUSB_PADCTL_USB2_PAD_MUX_HSIC_PAD_TRK_XUSB <<
+ XUSB_PADCTL_USB2_PAD_MUX_HSIC_PAD_TRK_SHIFT;
+ padctl_writel(padctl, value, XUSB_PADCTL_USB2_PAD_MUX);
+
+ return tegra210_xusb_padctl_enable(padctl);
+}
+
+static int tegra210_hsic_phy_exit(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+ return tegra210_xusb_padctl_disable(lane->pad->padctl);
+}
+
+static int tegra210_hsic_phy_power_on(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_hsic_lane *hsic = to_hsic_lane(lane);
+ struct tegra_xusb_hsic_pad *pad = to_hsic_pad(lane->pad);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ struct tegra210_xusb_padctl *priv;
+ unsigned int index = lane->index;
+ u32 value;
+ int err;
+
+ priv = to_tegra210_xusb_padctl(padctl);
+
+ err = regulator_enable(pad->supply);
+ if (err)
+ return err;
+
+ padctl_writel(padctl, hsic->strobe_trim,
+ XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL1(index));
+ value &= ~(XUSB_PADCTL_HSIC_PAD_CTL1_TX_RTUNEP_MASK <<
+ XUSB_PADCTL_HSIC_PAD_CTL1_TX_RTUNEP_SHIFT);
+ value |= (hsic->tx_rtune_p <<
+ XUSB_PADCTL_HSIC_PAD_CTL1_TX_RTUNEP_SHIFT);
+ padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL1(index));
+
+ value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL2(index));
+ value &= ~((XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_MASK <<
+ XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_SHIFT) |
+ (XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_MASK <<
+ XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_SHIFT));
+ value |= (hsic->rx_strobe_trim <<
+ XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_SHIFT) |
+ (hsic->rx_data_trim <<
+ XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_SHIFT);
+ padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL2(index));
+
+ value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL0(index));
+ value &= ~(XUSB_PADCTL_HSIC_PAD_CTL0_RPU_DATA0 |
+ XUSB_PADCTL_HSIC_PAD_CTL0_RPU_DATA1 |
+ XUSB_PADCTL_HSIC_PAD_CTL0_RPU_STROBE |
+ XUSB_PADCTL_HSIC_PAD_CTL0_PD_RX_DATA0 |
+ XUSB_PADCTL_HSIC_PAD_CTL0_PD_RX_DATA1 |
+ XUSB_PADCTL_HSIC_PAD_CTL0_PD_RX_STROBE |
+ XUSB_PADCTL_HSIC_PAD_CTL0_PD_ZI_DATA0 |
+ XUSB_PADCTL_HSIC_PAD_CTL0_PD_ZI_DATA1 |
+ XUSB_PADCTL_HSIC_PAD_CTL0_PD_ZI_STROBE |
+ XUSB_PADCTL_HSIC_PAD_CTL0_PD_TX_DATA0 |
+ XUSB_PADCTL_HSIC_PAD_CTL0_PD_TX_DATA1 |
+ XUSB_PADCTL_HSIC_PAD_CTL0_PD_TX_STROBE);
+ value |= XUSB_PADCTL_HSIC_PAD_CTL0_RPD_DATA0 |
+ XUSB_PADCTL_HSIC_PAD_CTL0_RPD_DATA1 |
+ XUSB_PADCTL_HSIC_PAD_CTL0_RPD_STROBE;
+ padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL0(index));
+
+ err = clk_prepare_enable(pad->clk);
+ if (err)
+ goto disable;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PAD_TRK_CTL);
+ value &= ~((XUSB_PADCTL_HSIC_PAD_TRK_CTL_TRK_START_TIMER_MASK <<
+ XUSB_PADCTL_HSIC_PAD_TRK_CTL_TRK_START_TIMER_SHIFT) |
+ (XUSB_PADCTL_HSIC_PAD_TRK_CTL_TRK_DONE_RESET_TIMER_MASK <<
+ XUSB_PADCTL_HSIC_PAD_TRK_CTL_TRK_DONE_RESET_TIMER_SHIFT));
+ value |= (XUSB_PADCTL_HSIC_PAD_TRK_CTL_TRK_START_TIMER_VAL <<
+ XUSB_PADCTL_HSIC_PAD_TRK_CTL_TRK_START_TIMER_SHIFT) |
+ (XUSB_PADCTL_HSIC_PAD_TRK_CTL_TRK_DONE_RESET_TIMER_VAL <<
+ XUSB_PADCTL_HSIC_PAD_TRK_CTL_TRK_DONE_RESET_TIMER_SHIFT);
+ padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PAD_TRK_CTL);
+
+ udelay(1);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PAD_TRK_CTL);
+ value &= ~XUSB_PADCTL_HSIC_PAD_TRK_CTL_PD_TRK;
+ padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PAD_TRK_CTL);
+
+ udelay(50);
+
+ clk_disable_unprepare(pad->clk);
+
+ return 0;
+
+disable:
+ regulator_disable(pad->supply);
+ return err;
+}
+
+static int tegra210_hsic_phy_power_off(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_hsic_pad *pad = to_hsic_pad(lane->pad);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ unsigned int index = lane->index;
+ u32 value;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL0(index));
+ value |= XUSB_PADCTL_HSIC_PAD_CTL0_PD_RX_DATA0 |
+ XUSB_PADCTL_HSIC_PAD_CTL0_PD_RX_DATA1 |
+ XUSB_PADCTL_HSIC_PAD_CTL0_PD_RX_STROBE |
+ XUSB_PADCTL_HSIC_PAD_CTL0_PD_ZI_DATA0 |
+ XUSB_PADCTL_HSIC_PAD_CTL0_PD_ZI_DATA1 |
+ XUSB_PADCTL_HSIC_PAD_CTL0_PD_ZI_STROBE |
+ XUSB_PADCTL_HSIC_PAD_CTL0_PD_TX_DATA0 |
+ XUSB_PADCTL_HSIC_PAD_CTL0_PD_TX_DATA1 |
+ XUSB_PADCTL_HSIC_PAD_CTL0_PD_TX_STROBE;
+ padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL1(index));
+
+ regulator_disable(pad->supply);
+
+ return 0;
+}
+
+static const struct phy_ops tegra210_hsic_phy_ops = {
+ .init = tegra210_hsic_phy_init,
+ .exit = tegra210_hsic_phy_exit,
+ .power_on = tegra210_hsic_phy_power_on,
+ .power_off = tegra210_hsic_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static struct tegra_xusb_pad *
+tegra210_hsic_pad_probe(struct tegra_xusb_padctl *padctl,
+ const struct tegra_xusb_pad_soc *soc,
+ struct device_node *np)
+{
+ struct tegra_xusb_hsic_pad *hsic;
+ struct tegra_xusb_pad *pad;
+ int err;
+
+ hsic = kzalloc(sizeof(*hsic), GFP_KERNEL);
+ if (!hsic)
+ return ERR_PTR(-ENOMEM);
+
+ pad = &hsic->base;
+ pad->ops = &tegra210_hsic_lane_ops;
+ pad->soc = soc;
+
+ err = tegra_xusb_pad_init(pad, padctl, np);
+ if (err < 0)
+ goto free;
+
+ hsic->clk = devm_clk_get(&pad->dev, "trk");
+ if (IS_ERR(hsic->clk)) {
+ err = PTR_ERR(hsic->clk);
+ dev_err(&pad->dev, "failed to get trk clock: %d\n", err);
+ goto unregister;
+ }
+
+ err = tegra_xusb_pad_register(pad, &tegra210_hsic_phy_ops);
+ if (err < 0)
+ goto unregister;
+
+ dev_set_drvdata(&pad->dev, pad);
+
+ return pad;
+
+unregister:
+ device_unregister(&pad->dev);
+free:
+ kfree(hsic);
+ return ERR_PTR(err);
+}
+
+static void tegra210_hsic_pad_remove(struct tegra_xusb_pad *pad)
+{
+ struct tegra_xusb_hsic_pad *hsic = to_hsic_pad(pad);
+
+ kfree(hsic);
+}
+
+static const struct tegra_xusb_pad_ops tegra210_hsic_ops = {
+ .probe = tegra210_hsic_pad_probe,
+ .remove = tegra210_hsic_pad_remove,
+};
+
+static const struct tegra_xusb_pad_soc tegra210_hsic_pad = {
+ .name = "hsic",
+ .num_lanes = ARRAY_SIZE(tegra210_hsic_lanes),
+ .lanes = tegra210_hsic_lanes,
+ .ops = &tegra210_hsic_ops,
+};
+
+static const char *tegra210_pcie_functions[] = {
+ "pcie-x1",
+ "usb3-ss",
+ "sata",
+ "pcie-x4",
+};
+
+static const struct tegra_xusb_lane_soc tegra210_pcie_lanes[] = {
+ TEGRA210_LANE("pcie-0", 0x028, 12, 0x3, pcie),
+ TEGRA210_LANE("pcie-1", 0x028, 14, 0x3, pcie),
+ TEGRA210_LANE("pcie-2", 0x028, 16, 0x3, pcie),
+ TEGRA210_LANE("pcie-3", 0x028, 18, 0x3, pcie),
+ TEGRA210_LANE("pcie-4", 0x028, 20, 0x3, pcie),
+ TEGRA210_LANE("pcie-5", 0x028, 22, 0x3, pcie),
+ TEGRA210_LANE("pcie-6", 0x028, 24, 0x3, pcie),
+};
+
+static struct tegra_xusb_lane *
+tegra210_pcie_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np,
+ unsigned int index)
+{
+ struct tegra_xusb_pcie_lane *pcie;
+ int err;
+
+ pcie = kzalloc(sizeof(*pcie), GFP_KERNEL);
+ if (!pcie)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&pcie->base.list);
+ pcie->base.soc = &pad->soc->lanes[index];
+ pcie->base.index = index;
+ pcie->base.pad = pad;
+ pcie->base.np = np;
+
+ err = tegra_xusb_lane_parse_dt(&pcie->base, np);
+ if (err < 0) {
+ kfree(pcie);
+ return ERR_PTR(err);
+ }
+
+ return &pcie->base;
+}
+
+static void tegra210_pcie_lane_remove(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_pcie_lane *pcie = to_pcie_lane(lane);
+
+ kfree(pcie);
+}
+
+static const struct tegra_xusb_lane_ops tegra210_pcie_lane_ops = {
+ .probe = tegra210_pcie_lane_probe,
+ .remove = tegra210_pcie_lane_remove,
+};
+
+static int tegra210_pcie_phy_init(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+ return tegra210_xusb_padctl_enable(lane->pad->padctl);
+}
+
+static int tegra210_pcie_phy_exit(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+ return tegra210_xusb_padctl_disable(lane->pad->padctl);
+}
+
+static int tegra210_pcie_phy_power_on(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ u32 value;
+ int err;
+
+ mutex_lock(&padctl->lock);
+
+ err = tegra210_pex_uphy_enable(padctl);
+ if (err < 0)
+ goto unlock;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+ value |= XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->index);
+ padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+
+unlock:
+ mutex_unlock(&padctl->lock);
+ return err;
+}
+
+static int tegra210_pcie_phy_power_off(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ u32 value;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+ value &= ~XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->index);
+ padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+
+ tegra210_pex_uphy_disable(padctl);
+
+ return 0;
+}
+
+static const struct phy_ops tegra210_pcie_phy_ops = {
+ .init = tegra210_pcie_phy_init,
+ .exit = tegra210_pcie_phy_exit,
+ .power_on = tegra210_pcie_phy_power_on,
+ .power_off = tegra210_pcie_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static struct tegra_xusb_pad *
+tegra210_pcie_pad_probe(struct tegra_xusb_padctl *padctl,
+ const struct tegra_xusb_pad_soc *soc,
+ struct device_node *np)
+{
+ struct tegra_xusb_pcie_pad *pcie;
+ struct tegra_xusb_pad *pad;
+ int err;
+
+ pcie = kzalloc(sizeof(*pcie), GFP_KERNEL);
+ if (!pcie)
+ return ERR_PTR(-ENOMEM);
+
+ pad = &pcie->base;
+ pad->ops = &tegra210_pcie_lane_ops;
+ pad->soc = soc;
+
+ err = tegra_xusb_pad_init(pad, padctl, np);
+ if (err < 0)
+ goto free;
+
+ pcie->pll = devm_clk_get(&pad->dev, "pll");
+ if (IS_ERR(pcie->pll)) {
+ err = PTR_ERR(pcie->pll);
+ dev_err(&pad->dev, "failed to get PLL: %d\n", err);
+ goto unregister;
+ }
+
+ pcie->rst = devm_reset_control_get(&pad->dev, "phy");
+ if (IS_ERR(pcie->rst)) {
+ err = PTR_ERR(pcie->rst);
+ dev_err(&pad->dev, "failed to get PCIe pad reset: %d\n", err);
+ goto unregister;
+ }
+
+ err = tegra_xusb_pad_register(pad, &tegra210_pcie_phy_ops);
+ if (err < 0)
+ goto unregister;
+
+ dev_set_drvdata(&pad->dev, pad);
+
+ return pad;
+
+unregister:
+ device_unregister(&pad->dev);
+free:
+ kfree(pcie);
+ return ERR_PTR(err);
+}
+
+static void tegra210_pcie_pad_remove(struct tegra_xusb_pad *pad)
+{
+ struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(pad);
+
+ kfree(pcie);
+}
+
+static const struct tegra_xusb_pad_ops tegra210_pcie_ops = {
+ .probe = tegra210_pcie_pad_probe,
+ .remove = tegra210_pcie_pad_remove,
+};
+
+static const struct tegra_xusb_pad_soc tegra210_pcie_pad = {
+ .name = "pcie",
+ .num_lanes = ARRAY_SIZE(tegra210_pcie_lanes),
+ .lanes = tegra210_pcie_lanes,
+ .ops = &tegra210_pcie_ops,
+};
+
+static const struct tegra_xusb_lane_soc tegra210_sata_lanes[] = {
+ TEGRA210_LANE("sata-0", 0x028, 30, 0x3, pcie),
+};
+
+static struct tegra_xusb_lane *
+tegra210_sata_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np,
+ unsigned int index)
+{
+ struct tegra_xusb_sata_lane *sata;
+ int err;
+
+ sata = kzalloc(sizeof(*sata), GFP_KERNEL);
+ if (!sata)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&sata->base.list);
+ sata->base.soc = &pad->soc->lanes[index];
+ sata->base.index = index;
+ sata->base.pad = pad;
+ sata->base.np = np;
+
+ err = tegra_xusb_lane_parse_dt(&sata->base, np);
+ if (err < 0) {
+ kfree(sata);
+ return ERR_PTR(err);
+ }
+
+ return &sata->base;
+}
+
+static void tegra210_sata_lane_remove(struct tegra_xusb_lane *lane)
+{
+ struct tegra_xusb_sata_lane *sata = to_sata_lane(lane);
+
+ kfree(sata);
+}
+
+static const struct tegra_xusb_lane_ops tegra210_sata_lane_ops = {
+ .probe = tegra210_sata_lane_probe,
+ .remove = tegra210_sata_lane_remove,
+};
+
+static int tegra210_sata_phy_init(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+ return tegra210_xusb_padctl_enable(lane->pad->padctl);
+}
+
+static int tegra210_sata_phy_exit(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+
+ return tegra210_xusb_padctl_disable(lane->pad->padctl);
+}
+
+static int tegra210_sata_phy_power_on(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ u32 value;
+ int err;
+
+ mutex_lock(&padctl->lock);
+
+ err = tegra210_sata_uphy_enable(padctl, false);
+ if (err < 0)
+ goto unlock;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+ value |= XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(lane->index);
+ padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+
+unlock:
+ mutex_unlock(&padctl->lock);
+ return err;
+}
+
+static int tegra210_sata_phy_power_off(struct phy *phy)
+{
+ struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+ struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+ u32 value;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
+ value &= ~XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(lane->index);
+ padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
+
+ tegra210_sata_uphy_disable(lane->pad->padctl);
+
+ return 0;
+}
+
+static const struct phy_ops tegra210_sata_phy_ops = {
+ .init = tegra210_sata_phy_init,
+ .exit = tegra210_sata_phy_exit,
+ .power_on = tegra210_sata_phy_power_on,
+ .power_off = tegra210_sata_phy_power_off,
+ .owner = THIS_MODULE,
+};
+
+static struct tegra_xusb_pad *
+tegra210_sata_pad_probe(struct tegra_xusb_padctl *padctl,
+ const struct tegra_xusb_pad_soc *soc,
+ struct device_node *np)
+{
+ struct tegra_xusb_sata_pad *sata;
+ struct tegra_xusb_pad *pad;
+ int err;
+
+ sata = kzalloc(sizeof(*sata), GFP_KERNEL);
+ if (!sata)
+ return ERR_PTR(-ENOMEM);
+
+ pad = &sata->base;
+ pad->ops = &tegra210_sata_lane_ops;
+ pad->soc = soc;
+
+ err = tegra_xusb_pad_init(pad, padctl, np);
+ if (err < 0)
+ goto free;
+
+ sata->rst = devm_reset_control_get(&pad->dev, "phy");
+ if (IS_ERR(sata->rst)) {
+ err = PTR_ERR(sata->rst);
+ dev_err(&pad->dev, "failed to get SATA pad reset: %d\n", err);
+ goto unregister;
+ }
+
+ err = tegra_xusb_pad_register(pad, &tegra210_sata_phy_ops);
+ if (err < 0)
+ goto unregister;
+
+ dev_set_drvdata(&pad->dev, pad);
+
+ return pad;
+
+unregister:
+ device_unregister(&pad->dev);
+free:
+ kfree(sata);
+ return ERR_PTR(err);
+}
+
+static void tegra210_sata_pad_remove(struct tegra_xusb_pad *pad)
+{
+ struct tegra_xusb_sata_pad *sata = to_sata_pad(pad);
+
+ kfree(sata);
+}
+
+static const struct tegra_xusb_pad_ops tegra210_sata_ops = {
+ .probe = tegra210_sata_pad_probe,
+ .remove = tegra210_sata_pad_remove,
+};
+
+static const struct tegra_xusb_pad_soc tegra210_sata_pad = {
+ .name = "sata",
+ .num_lanes = ARRAY_SIZE(tegra210_sata_lanes),
+ .lanes = tegra210_sata_lanes,
+ .ops = &tegra210_sata_ops,
+};
+
+static const struct tegra_xusb_pad_soc * const tegra210_pads[] = {
+ &tegra210_usb2_pad,
+ &tegra210_hsic_pad,
+ &tegra210_pcie_pad,
+ &tegra210_sata_pad,
+};
+
+static int tegra210_usb2_port_enable(struct tegra_xusb_port *port)
+{
+ return 0;
+}
+
+static void tegra210_usb2_port_disable(struct tegra_xusb_port *port)
+{
+}
+
+static struct tegra_xusb_lane *
+tegra210_usb2_port_map(struct tegra_xusb_port *port)
+{
+ return tegra_xusb_find_lane(port->padctl, "usb2", port->index);
+}
+
+static const struct tegra_xusb_port_ops tegra210_usb2_port_ops = {
+ .enable = tegra210_usb2_port_enable,
+ .disable = tegra210_usb2_port_disable,
+ .map = tegra210_usb2_port_map,
+};
+
+static int tegra210_hsic_port_enable(struct tegra_xusb_port *port)
+{
+ return 0;
+}
+
+static void tegra210_hsic_port_disable(struct tegra_xusb_port *port)
+{
+}
+
+static struct tegra_xusb_lane *
+tegra210_hsic_port_map(struct tegra_xusb_port *port)
+{
+ return tegra_xusb_find_lane(port->padctl, "hsic", port->index);
+}
+
+static const struct tegra_xusb_port_ops tegra210_hsic_port_ops = {
+ .enable = tegra210_hsic_port_enable,
+ .disable = tegra210_hsic_port_disable,
+ .map = tegra210_hsic_port_map,
+};
+
+static int tegra210_usb3_port_enable(struct tegra_xusb_port *port)
+{
+ struct tegra_xusb_usb3_port *usb3 = to_usb3_port(port);
+ struct tegra_xusb_padctl *padctl = port->padctl;
+ struct tegra_xusb_lane *lane = usb3->base.lane;
+ unsigned int index = port->index;
+ u32 value;
+ int err;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
+
+ if (!usb3->internal)
+ value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index);
+ else
+ value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index);
+
+ value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(index);
+ value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(index, usb3->port);
+ padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP);
+
+ /*
+ * TODO: move this code into the PCIe/SATA PHY ->power_on() callbacks
+ * and conditionalize based on mux function? This seems to work, but
+ * might not be the exact proper sequence.
+ */
+ err = regulator_enable(usb3->supply);
+ if (err < 0)
+ return err;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(index));
+ value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_MASK <<
+ XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT);
+ value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_VAL <<
+ XUSB_PADCTL_UPHY_USB3_PAD_ECTL1_TX_TERM_CTRL_SHIFT;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL1(index));
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL2(index));
+ value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_MASK <<
+ XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_SHIFT);
+ value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_VAL <<
+ XUSB_PADCTL_UPHY_USB3_PAD_ECTL2_RX_CTLE_SHIFT;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL2(index));
+
+ padctl_writel(padctl, XUSB_PADCTL_UPHY_USB3_PAD_ECTL3_RX_DFE_VAL,
+ XUSB_PADCTL_UPHY_USB3_PADX_ECTL3(index));
+
+ value = padctl_readl(padctl, XUSB_PADCTL_UPHY_USB3_PADX_ECTL4(index));
+ value &= ~(XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_MASK <<
+ XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_SHIFT);
+ value |= XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_VAL <<
+ XUSB_PADCTL_UPHY_USB3_PAD_ECTL4_RX_CDR_CTRL_SHIFT;
+ padctl_writel(padctl, value, XUSB_PADCTL_UPHY_USB3_PADX_ECTL4(index));
+
+ padctl_writel(padctl, XUSB_PADCTL_UPHY_USB3_PAD_ECTL6_RX_EQ_CTRL_H_VAL,
+ XUSB_PADCTL_UPHY_USB3_PADX_ECTL6(index));
+
+ if (lane->pad == padctl->sata)
+ err = tegra210_sata_uphy_enable(padctl, true);
+ else
+ err = tegra210_pex_uphy_enable(padctl);
+
+ if (err) {
+ dev_err(&port->dev, "%s: failed to enable UPHY: %d\n",
+ __func__, err);
+ return err;
+ }
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value &= ~XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ return 0;
+}
+
+static void tegra210_usb3_port_disable(struct tegra_xusb_port *port)
+{
+ struct tegra_xusb_usb3_port *usb3 = to_usb3_port(port);
+ struct tegra_xusb_padctl *padctl = port->padctl;
+ struct tegra_xusb_lane *lane = port->lane;
+ unsigned int index = port->index;
+ u32 value;
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN_EARLY(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ usleep_range(100, 200);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_CLAMP_EN(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ usleep_range(250, 350);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM1);
+ value |= XUSB_PADCTL_ELPG_PROGRAM1_SSPX_ELPG_VCORE_DOWN(index);
+ padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM1);
+
+ if (lane->pad == padctl->sata)
+ tegra210_sata_uphy_disable(padctl);
+ else
+ tegra210_pex_uphy_disable(padctl);
+
+ regulator_disable(usb3->supply);
+
+ value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
+ value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(index);
+ value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(index, 0x7);
+ padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP);
+}
+
+static const struct tegra_xusb_lane_map tegra210_usb3_map[] = {
+ { 0, "pcie", 6 },
+ { 1, "pcie", 5 },
+ { 2, "pcie", 0 },
+ { 2, "pcie", 3 },
+ { 3, "pcie", 4 },
+ { 3, "pcie", 4 },
+ { 0, NULL, 0 }
+};
+
+static struct tegra_xusb_lane *
+tegra210_usb3_port_map(struct tegra_xusb_port *port)
+{
+ return tegra_xusb_port_find_lane(port, tegra210_usb3_map, "usb3-ss");
+}
+
+static const struct tegra_xusb_port_ops tegra210_usb3_port_ops = {
+ .enable = tegra210_usb3_port_enable,
+ .disable = tegra210_usb3_port_disable,
+ .map = tegra210_usb3_port_map,
+};
+
+static int
+tegra210_xusb_read_fuse_calibration(struct tegra210_xusb_fuse_calibration *fuse)
+{
+ unsigned int i;
+ u32 value;
+ int err;
+
+ err = tegra_fuse_readl(TEGRA_FUSE_SKU_CALIB_0, &value);
+ if (err < 0)
+ return err;
+
+ for (i = 0; i < ARRAY_SIZE(fuse->hs_curr_level); i++) {
+ fuse->hs_curr_level[i] =
+ (value >> FUSE_SKU_CALIB_HS_CURR_LEVEL_PADX_SHIFT(i)) &
+ FUSE_SKU_CALIB_HS_CURR_LEVEL_PAD_MASK;
+ }
+
+ fuse->hs_term_range_adj =
+ (value >> FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_SHIFT) &
+ FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_MASK;
+
+ err = tegra_fuse_readl(TEGRA_FUSE_USB_CALIB_EXT_0, &value);
+ if (err < 0)
+ return err;
+
+ fuse->rpd_ctrl =
+ (value >> FUSE_USB_CALIB_EXT_RPD_CTRL_SHIFT) &
+ FUSE_USB_CALIB_EXT_RPD_CTRL_MASK;
+
+ return 0;
+}
+
+static struct tegra_xusb_padctl *
+tegra210_xusb_padctl_probe(struct device *dev,
+ const struct tegra_xusb_padctl_soc *soc)
+{
+ struct tegra210_xusb_padctl *padctl;
+ int err;
+
+ padctl = devm_kzalloc(dev, sizeof(*padctl), GFP_KERNEL);
+ if (!padctl)
+ return ERR_PTR(-ENOMEM);
+
+ padctl->base.dev = dev;
+ padctl->base.soc = soc;
+
+ err = tegra210_xusb_read_fuse_calibration(&padctl->fuse);
+ if (err < 0)
+ return ERR_PTR(err);
+
+ return &padctl->base;
+}
+
+static void tegra210_xusb_padctl_remove(struct tegra_xusb_padctl *padctl)
+{
+}
+
+static const struct tegra_xusb_padctl_ops tegra210_xusb_padctl_ops = {
+ .probe = tegra210_xusb_padctl_probe,
+ .remove = tegra210_xusb_padctl_remove,
+ .usb3_set_lfps_detect = tegra210_usb3_set_lfps_detect,
+ .hsic_set_idle = tegra210_hsic_set_idle,
+};
+
+const struct tegra_xusb_padctl_soc tegra210_xusb_padctl_soc = {
+ .num_pads = ARRAY_SIZE(tegra210_pads),
+ .pads = tegra210_pads,
+ .ports = {
+ .usb2 = {
+ .ops = &tegra210_usb2_port_ops,
+ .count = 4,
+ },
+ .hsic = {
+ .ops = &tegra210_hsic_port_ops,
+ .count = 1,
+ },
+ .usb3 = {
+ .ops = &tegra210_usb3_port_ops,
+ .count = 4,
+ },
+ },
+ .ops = &tegra210_xusb_padctl_ops,
+};
+EXPORT_SYMBOL_GPL(tegra210_xusb_padctl_soc);
+
+MODULE_AUTHOR("Andrew Bresticker <[email protected]>");
+MODULE_DESCRIPTION("NVIDIA Tegra 210 XUSB Pad Controller driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/soc/tegra/fuse.h b/include/soc/tegra/fuse.h
index 961b821b6a46..b4c9219e7f95 100644
--- a/include/soc/tegra/fuse.h
+++ b/include/soc/tegra/fuse.h
@@ -26,6 +26,7 @@

#define TEGRA_FUSE_SKU_CALIB_0 0xf0
#define TEGRA30_FUSE_SATA_CALIB 0x124
+#define TEGRA_FUSE_USB_CALIB_EXT_0 0x250

#ifndef __ASSEMBLY__

--
2.7.1

2016-03-04 21:36:17

by Andrew Bresticker

[permalink] [raw]
Subject: Re: [PATCH v10 1/9] dt-bindings: phy: Add NVIDIA Tegra XUSB pad controller binding

Hi Thierry,

On Fri, Mar 4, 2016 at 8:19 AM, Thierry Reding <[email protected]> wrote:
> From: Thierry Reding <[email protected]>
>
> The NVIDIA Tegra XUSB pad controller provides a set of pads, each with a
> set of lanes that are used for PCIe, SATA and USB.
>
> Signed-off-by: Thierry Reding <[email protected]>

Thanks, this binding looks much better, IMO. A couple small comments below...

> +Port nodes:
> +===========
> +
> +A required child node named "ports" contains a list of all the ports exposed
> +by the XUSB pad controller. Per-port configuration is only required for USB.
> +
> +USB2 ports:
> +-----------
> +
> +Required properties:
> +- status: Defines the operation status of the port. Valid values are:
> + - "disabled": the port is disabled
> + - "okay": the port is enabled
> +- mode: A string that determines the mode in which to run the port. Valid
> + values are:
> + - "host": for USB host mode
> + - "device": for USB device mode
> + - "otg": for USB OTG mode
> +
> +Optional properties:
> +- nvidia,internal: A boolean property whose presence determines that a port
> + is internal. In the absence of this property the port is considered to be
> + external.
> +- vbus-supply: phandle to a regulator supplying the VBUS voltage.

Both Blaze and Smaug require an offset to be applied to the fused
HS_CURR_LEVEL value, so I think we need another property here for
that.

> +ULPI ports:
> +-----------
> +
> +Optional properties:
> +- status: Defines the operation status of the port. Valid values are:
> + - "disabled": the port is disabled
> + - "okay": the port is enabled
> +- nvidia,internal: A boolean property whose presence determines that a port
> + is internal. In the absence of this property the port is considered to be
> + external.
> +- vbus-supply: phandle to a regulator supplying the VBUS voltage.
> +
> +HSIC ports:
> +-----------
> +
> +Required properties:
> +- status: Defines the operation status of the port. Valid values are:
> + - "disabled": the port is disabled
> + - "okay": the port is enabled
> +
> +Optional properties:
> +- vbus-supply: phandle to a regulator supplying the VBUS voltage.

I believe this pin is named VDDIO_HSIC?

Also there are several other HSIC pad parameters (STROBE_TRIM,
DATA_TRIM, etc.) which probably should be supplied via DT.

2016-03-04 21:41:15

by Andrew Bresticker

[permalink] [raw]
Subject: Re: [PATCH v10 3/9] dt-bindings: phy: tegra-xusb-padctl: Add Tegra210 support

On Fri, Mar 4, 2016 at 8:19 AM, Thierry Reding <[email protected]> wrote:
> From: Thierry Reding <[email protected]>
>
> Extend the binding to cover the set of feature found in Tegra210.
>
> Signed-off-by: Thierry Reding <[email protected]>

> +PCIe pad:
> +---------
> +
> +Required properties:
> +- clocks: Must contain an entry for each entry in clock-names.
> +- clock-names: Must contain the following entries:
> + - "pll": phandle and specifier referring to the PLLE
> +- resets: Must contain an entry for each entry in reset-names.
> +- reset-names: Must contain the following entries:
> + - "phy": reset for the PCIe UPHY block
> +
> +SATA pad:
> +---------
> +
> +Required properties:
> +- resets: Must contain an entry for each entry in reset-names.
> +- reset-names: Must contain the following entries:
> + - "phy": reset for the SATA UPHY block

Doesn't the SATA pad require PLLE as well? You've included it in the
example DT fragment, but it's absent here.

2016-03-04 21:47:45

by Andrew Bresticker

[permalink] [raw]
Subject: Re: [PATCH v10 1/9] dt-bindings: phy: Add NVIDIA Tegra XUSB pad controller binding

> +Required properties:
> +--------------------
> +- compatible: Must be:
> + - Tegra124: "nvidia,tegra124-xusb-padctl"
> + - Tegra132: "nvidia,tegra132-xusb-padctl", "nvidia,tegra124-xusb-padctl"
> +- reg: Physical base address and length of the controller's registers.
> +- resets: Must contain an entry for each entry in reset-names.
> +- reset-names: Must include the following entries:
> + - "padctl"

Also... there's a padctl interrupt that'll be necessary for LP0
suspend/resume and runtime power-gating of the xHC. We should
probably include that here too.

2016-03-05 04:31:56

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v10 1/9] dt-bindings: phy: Add NVIDIA Tegra XUSB pad controller binding

On Fri, Mar 04, 2016 at 05:19:31PM +0100, Thierry Reding wrote:
> From: Thierry Reding <[email protected]>
>
> The NVIDIA Tegra XUSB pad controller provides a set of pads, each with a
> set of lanes that are used for PCIe, SATA and USB.
>
> Signed-off-by: Thierry Reding <[email protected]>
> ---
> Changes in v10:
> - clarify that the hardware documentation means something different when
> referring to a "port" (intra-SoC connectivity)
>
> Changes in v9:
> - rename UTMI -> USB2 to match hardware documentation
> - reword according to suggestions by Stephen Warren
> - make Tegra132 compatible string list consistent
> - remove mailbox support
>
> .../bindings/phy/nvidia,tegra124-xusb-padctl.txt | 376 +++++++++++++++++++++
> .../pinctrl/nvidia,tegra124-xusb-padctl.txt | 5 +
> 2 files changed, 381 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt

Without really understanding the h/w here, looks okay to me.

Acked-by: Rob Herring <[email protected]>

> +SoC include:
> +
> + padctl@0,7009f000 {

Drop the comma. Commas should only be used if there are distinct fields.

If I get my dtc patch done, these are going to start warning, so you
might want to go fix dts files (assuming that's where this is coming
from).

Rob

2016-03-05 04:32:23

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v10 2/9] dt-bindings: pinctrl: Deprecate Tegra XUSB pad controller binding

On Fri, Mar 04, 2016 at 05:19:32PM +0100, Thierry Reding wrote:
> From: Thierry Reding <[email protected]>
>
> This is an old version of the binding that isn't flexible enough to
> describe all aspects of the XUSB pad controller. Specifically with the
> addition of XUSB support (for SuperSpeed USB) the existing binding is
> no longer suitable.
>
> Signed-off-by: Thierry Reding <[email protected]>
> ---
> .../devicetree/bindings/pinctrl/nvidia,tegra124-xusb-padctl.txt | 1 +
> 1 file changed, 1 insertion(+)

Acked-by: Rob Herring <[email protected]>

2016-03-05 04:32:27

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v10 3/9] dt-bindings: phy: tegra-xusb-padctl: Add Tegra210 support

On Fri, Mar 04, 2016 at 05:19:33PM +0100, Thierry Reding wrote:
> From: Thierry Reding <[email protected]>
>
> Extend the binding to cover the set of feature found in Tegra210.
>
> Signed-off-by: Thierry Reding <[email protected]>
> ---
> .../bindings/phy/nvidia,tegra124-xusb-padctl.txt | 327 +++++++++++++++++++++
> 1 file changed, 327 insertions(+)

Acked-by: Rob Herring <[email protected]>

> +Tegra210:
> +---------
> +
> +SoC include:
> +
> + padctl@0,7009f000 {

Drop the comma.

2016-03-07 11:24:32

by Thierry Reding

[permalink] [raw]
Subject: Re: [PATCH v10 1/9] dt-bindings: phy: Add NVIDIA Tegra XUSB pad controller binding

On Fri, Mar 04, 2016 at 10:31:45PM -0600, Rob Herring wrote:
> On Fri, Mar 04, 2016 at 05:19:31PM +0100, Thierry Reding wrote:
> > From: Thierry Reding <[email protected]>
> >
> > The NVIDIA Tegra XUSB pad controller provides a set of pads, each with a
> > set of lanes that are used for PCIe, SATA and USB.
> >
> > Signed-off-by: Thierry Reding <[email protected]>
> > ---
> > Changes in v10:
> > - clarify that the hardware documentation means something different when
> > referring to a "port" (intra-SoC connectivity)
> >
> > Changes in v9:
> > - rename UTMI -> USB2 to match hardware documentation
> > - reword according to suggestions by Stephen Warren
> > - make Tegra132 compatible string list consistent
> > - remove mailbox support
> >
> > .../bindings/phy/nvidia,tegra124-xusb-padctl.txt | 376 +++++++++++++++++++++
> > .../pinctrl/nvidia,tegra124-xusb-padctl.txt | 5 +
> > 2 files changed, 381 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
>
> Without really understanding the h/w here, looks okay to me.
>
> Acked-by: Rob Herring <[email protected]>
>
> > +SoC include:
> > +
> > + padctl@0,7009f000 {
>
> Drop the comma. Commas should only be used if there are distinct fields.
>
> If I get my dtc patch done, these are going to start warning, so you
> might want to go fix dts files (assuming that's where this is coming
> from).

I noticed that in today's next the updated DTC already complains about a
lot of things in existing DTS files. For Tegra that's primarily the
memory node, because it has a reg property but no unit name. Any hints
as to how to solve that? I think I remember from way back that memory
was supposed to be an exception, perhaps DTC needs to be taught that?

Thierry


Attachments:
(No filename) (1.75 kB)
signature.asc (819.00 B)
Download all attachments

2016-03-15 09:01:32

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH v10 2/9] dt-bindings: pinctrl: Deprecate Tegra XUSB pad controller binding

On Fri, Mar 4, 2016 at 5:19 PM, Thierry Reding <[email protected]> wrote:

> From: Thierry Reding <[email protected]>
>
> This is an old version of the binding that isn't flexible enough to
> describe all aspects of the XUSB pad controller. Specifically with the
> addition of XUSB support (for SuperSpeed USB) the existing binding is
> no longer suitable.
>
> Signed-off-by: Thierry Reding <[email protected]>

That's unfortunate, not to say unelegant. I want to know Stephen's
opinion on these patches (probably they are in another mail)
before merging.

Will the new binding also work with SuperDuperSpeed USB and
SuperSuperMegaUltraOrtonSpeed USB I wonder... or will we
change the bindings again?

Yours,
Linus Walleij

2016-03-15 09:03:53

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH v10 3/9] dt-bindings: phy: tegra-xusb-padctl: Add Tegra210 support

On Fri, Mar 4, 2016 at 5:19 PM, Thierry Reding <[email protected]> wrote:

> From: Thierry Reding <[email protected]>
>
> Extend the binding to cover the set of feature found in Tegra210.
>
> Signed-off-by: Thierry Reding <[email protected]>

Again I'd like Stephen's ACK on this to keep things together,
thanks.

Please resend the pinctrl patches to me after v4.6-rc1 with the apropriate
ACKs (hoping they will appear).

Yours,
Linus Walleij

2016-03-16 06:42:23

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v10 7/9] dt-bindings: usb: xhci-tegra: Add Tegra210 XUSB controller support

On Fri, Mar 04, 2016 at 05:19:37PM +0100, Thierry Reding wrote:
> From: Thierry Reding <[email protected]>
>
> Extend the Tegra XUSB controller device tree binding with Tegra210
> support.
>
> Signed-off-by: Thierry Reding <[email protected]>
> ---
> .../devicetree/bindings/usb/nvidia,tegra124-xusb.txt | 12 ++++++++++++
> 1 file changed, 12 insertions(+)
>

Acked-by: Rob Herring <[email protected]>

2016-03-16 06:43:06

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v10 1/9] dt-bindings: phy: Add NVIDIA Tegra XUSB pad controller binding

On Mon, Mar 07, 2016 at 12:24:18PM +0100, Thierry Reding wrote:
> On Fri, Mar 04, 2016 at 10:31:45PM -0600, Rob Herring wrote:
> > On Fri, Mar 04, 2016 at 05:19:31PM +0100, Thierry Reding wrote:
> > > From: Thierry Reding <[email protected]>
> > >
> > > The NVIDIA Tegra XUSB pad controller provides a set of pads, each with a
> > > set of lanes that are used for PCIe, SATA and USB.
> > >
> > > Signed-off-by: Thierry Reding <[email protected]>
> > > ---
> > > Changes in v10:
> > > - clarify that the hardware documentation means something different when
> > > referring to a "port" (intra-SoC connectivity)
> > >
> > > Changes in v9:
> > > - rename UTMI -> USB2 to match hardware documentation
> > > - reword according to suggestions by Stephen Warren
> > > - make Tegra132 compatible string list consistent
> > > - remove mailbox support
> > >
> > > .../bindings/phy/nvidia,tegra124-xusb-padctl.txt | 376 +++++++++++++++++++++
> > > .../pinctrl/nvidia,tegra124-xusb-padctl.txt | 5 +
> > > 2 files changed, 381 insertions(+)
> > > create mode 100644 Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
> >
> > Without really understanding the h/w here, looks okay to me.
> >
> > Acked-by: Rob Herring <[email protected]>
> >
> > > +SoC include:
> > > +
> > > + padctl@0,7009f000 {
> >
> > Drop the comma. Commas should only be used if there are distinct fields.
> >
> > If I get my dtc patch done, these are going to start warning, so you
> > might want to go fix dts files (assuming that's where this is coming
> > from).
>
> I noticed that in today's next the updated DTC already complains about a
> lot of things in existing DTS files. For Tegra that's primarily the
> memory node, because it has a reg property but no unit name. Any hints
> as to how to solve that? I think I remember from way back that memory
> was supposed to be an exception, perhaps DTC needs to be taught that?

I remember something about the skeleton.dtsi files and needing to avoid
having both memory and memory@x nodes, but we really should fix them and
have a unit address.

Rob

2016-03-16 17:39:34

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH v10 1/9] dt-bindings: phy: Add NVIDIA Tegra XUSB pad controller binding

On 03/04/2016 09:19 AM, Thierry Reding wrote:
> From: Thierry Reding <[email protected]>
>
> The NVIDIA Tegra XUSB pad controller provides a set of pads, each with a
> set of lanes that are used for PCIe, SATA and USB.

> .../bindings/phy/nvidia,tegra124-xusb-padctl.txt | 376 +++++++++++++++++++++
> .../pinctrl/nvidia,tegra124-xusb-padctl.txt | 5 +

It seems odd to add part of the deprecation notice in this patch and one
more line in the second/next patch. Did an update get squashed into the
wrong commit? I'd suggest moving the edit to existing file
nvidia,tegra124-xusb-padctl.txt entirely into patch 2. Perhaps this
could happen while/when the patch is applied to avoid having to post
another version.

> diff --git a/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt

...
> +Pads will be represented as children of the top-level XUSB pad controller
> +device tree node.

Nit: grand-children, since they're house inside pads{} first. I might
suggest:

A "pads" node exists to represent all pads contained within the XUSB
controller. Each pad is represented as a subnode of the pads node.

> Each lane exposed by the pad will be represented by its
> +own subnode and can be referenced by users of the lane using the standard
> +PHY bindings, as described by the phy-bindings.txt file in this directory.

"Each lane exposed by the pad will be represented as a subnode of the
pad node ..."?

I'd suggest adding a similar paragraph describing the ports node, and
that ports are child nodes of that. Otherwise, since the documentation
of the nodes isn't nested in any way, it's not clear from the text
exactly what nodes are children of what other nodes.

> +The Tegra hardware documentation refers to the connection between the XUSB
> +pad controller and the XUSB controller as "ports".

I think, being pedantic, that "port" in the TRM refers to the set of
signals at the edge/interface-to the IO controller, not the connection
between the IO controller and the XUSB controller. Still, the existing
wording in this patch is fine; no need to change it.

Still, the examples do clear this up, so perhaps it's not worth another
version of the series to fix this. Or if you do think it's worth fixing,
I'd be perfectly happy to see that done in follow-on patches. If you
want I can write that follow-on patch once this series is applied.

...
> +PHY nodes:
> +==========
> +
> +Each pad node has one or more children, each representing one of the lanes
> +controlled by the pad.
> +
> +Required properties:
> +--------------------
> +- status: Defines the operation status of the PHY. Valid values are:
> + - "disabled": the PHY is disabled
> + - "okay": the PHY is enabled

Presumably the standard semantics of a missing status property
implicitly meaning "okay" are also intended? A similar comment applies
to other places where status is documented. "status" is typically not a
required property.

> +Port nodes:
> +===========

> +USB2 ports:
> +-----------

Should that say "UTMI ports"? ULPI and HSIC below are (or can be) USB2
ports too.

> +Required properties:
> +- status: Defines the operation status of the port. Valid values are:
> + - "disabled": the port is disabled
> + - "okay": the port is enabled
> +- mode: A string that determines the mode in which to run the port. Valid
> + values are:
> + - "host": for USB host mode
> + - "device": for USB device mode
> + - "otg": for USB OTG mode

How do these properties tie the DT-based port definition to a particular
PHY/lane/... in HW? I don't see a property that contains any kind of HW
ID here.

...
> +Optional properties:
> +- nvidia,internal: A boolean property whose presence determines that a port
> + is internal. In the absence of this property the port is considered to be
> + external.

It's not clear what "internal" and "external" mean. Presumably it's
on-PCB vs. physical-connector-exposed-to-the-user. It may be worth
explicitly mentioning that.

Is there no vbus-supply for USB2/UTMI ports? I'm also not sure why
vbus-supply is optional for ULPI and HSIC. Even if there is no SW
/control/ over VBUS, there still must be some source of power; IIRC Mark
Brown typically desires that to be explicitly modelled with an always-on
regulator if there's no SW control.

> +Super-speed USB ports:
> +----------------------
> +
> +Required properties:
> +- status: Defines the operation status of the port. Valid values are:
> + - "disabled": the port is disabled
> + - "okay": the port is enabled
> +- nvidia,usb2-companion: A single cell that specifies the physical port number
> + to map this super-speed USB port to. The range of valid port numbers varies
> + with the SoC generation:
> + - 0-2: for Tegra124 and Tegra132

How can this be used to look up the corresponding USB2 node in DT? I
would have expected a phandle here, with the physical (HW) port ID being
represented in the referenced node. Otherwise, I don't see how to tie
together the USB2 and USB3 DT nodes.

> +For Tegra124 and Tegra132, the XUSB pad controller exposes the following
> +ports:
> +- 3x USB2: usb2-0, usb2-1, usb2-2
> +- 1x ULPI: ulpi-0
> +- 2x HSIC: hsic-0, hsic-1
> +- 2x super-speed USB: usb3-0, usb3-1

Oh, is the physical port ID implicit in the DT node name? It may be
worth mentioning that when describing the properties for each type of node.

I'll assume that's how the USB2<->USB3 mapping works. All of my comments
are mainly re: the description/documentation of the binding itself. That
can all be enhanced later. The underlying binding itself, and the
example, look reasonable. As such, this patch,

Acked-by: Stephen Warren <[email protected]>

2016-03-16 17:40:25

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH v10 2/9] dt-bindings: pinctrl: Deprecate Tegra XUSB pad controller binding

On 03/04/2016 09:19 AM, Thierry Reding wrote:
> From: Thierry Reding <[email protected]>
>
> This is an old version of the binding that isn't flexible enough to
> describe all aspects of the XUSB pad controller. Specifically with the
> addition of XUSB support (for SuperSpeed USB) the existing binding is
> no longer suitable.

Assuming the rest of the edits to this file in patch 1/9 get pulled into
this patch, or perhaps this patch simply gets squashed into path 1/9,
then this patch,

Acked-by: Stephen Warren <[email protected]>

2016-03-16 17:59:51

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH v10 3/9] dt-bindings: phy: tegra-xusb-padctl: Add Tegra210 support

On 03/04/2016 09:19 AM, Thierry Reding wrote:
> From: Thierry Reding <[email protected]>
>
> Extend the binding to cover the set of feature found in Tegra210.

Acked-by: Stephen Warren <[email protected]>

> diff --git a/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt

> + padctl@0,7009f000 {
...
> + pads {
...
> + };
> +
> + ports {
...
> + };

As a comment not affecting my ack in any way: At the top-level, we place
all the child nodes into "array container" nodes named "pads" and
"ports". This is nice since it separates different types of child nodes
and allows easily adding more types of child nodes in the future without
interference, and in a way that allows us to explicitly know what each
node is without having to interpret its name or compatible value to do
so. However, we haven't done this with the per-lane child nodes inside
each pad. If we were to rev the design, I'd be tempted to suggest:

padctl@0,7009f000 {
pads {
usb2 {
lanes { // This level is new
usb2-0 {

2016-03-16 18:08:11

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH v10 6/9] dt-bindings: usb: Add NVIDIA Tegra XUSB controller binding

On 03/04/2016 09:19 AM, Thierry Reding wrote:
> From: Thierry Reding <[email protected]>
>
> Add device-tree binding documentation for the XUSB controller present
> on Tegra124 and later SoCs. This controller supports USB 3.0 via an xHCI
> compliant interface.
>
> Based on work by Andrew Bresticker <[email protected]>.

> diff --git a/Documentation/devicetree/bindings/usb/nvidia,tegra124-xusb.txt b/Documentation/devicetree/bindings/usb/nvidia,tegra124-xusb.txt

> +Required properties:

> +- nvidia,xusb-padctl: phandle to the XUSB pad controller that is used to
> + configure the USB pads used by the XHCI controller

Is "global" access to the PADCTL DT node/driver required? I rather would
have expected this binding to reference the port objects in the PADCTL
node. Perhaps the intent is that drivers can use this property to go and
read the port information directly from the PADCTL node and interpret it
themselves without requiring the PADCTL driver to provide an interface
for ports? I guess that would also explain why this binding references
only PHYs and not ports:

> +Optional properties:
> +--------------------
> +- phys: Must contain an entry for each entry in phy-names.
> + See ../phy/phy-bindings.txt for details.
> +- phy-names: Should include an entry for each PHY used by the controller. The
> + following PHYs are available:
> + - Tegra124: usb2-0, usb2-1, usb2-2, hsic-0, hsic-1, usb3-0, usb3-1
> + - Tegra132: usb2-0, usb2-1, usb2-2, hsic-0, hsic-1, usb3-0, usb3-1

If that's how this works (which might be worth mentioning in the binding
doc), then:
Acked-by: Stephen Warren <[email protected]>

2016-03-16 18:08:50

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH v10 7/9] dt-bindings: usb: xhci-tegra: Add Tegra210 XUSB controller support

On 03/04/2016 09:19 AM, Thierry Reding wrote:
> From: Thierry Reding <[email protected]>
>
> Extend the Tegra XUSB controller device tree binding with Tegra210
> support.

Acked-by: Stephen Warren <[email protected]>

2016-03-22 11:02:10

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH v10 1/9] dt-bindings: phy: Add NVIDIA Tegra XUSB pad controller binding

On Fri, Mar 4, 2016 at 5:19 PM, Thierry Reding <[email protected]> wrote:

> From: Thierry Reding <[email protected]>
>
> The NVIDIA Tegra XUSB pad controller provides a set of pads, each with a
> set of lanes that are used for PCIe, SATA and USB.
>
> Signed-off-by: Thierry Reding <[email protected]>
> ---
> Changes in v10:
> - clarify that the hardware documentation means something different when
> referring to a "port" (intra-SoC connectivity)

Thierry I'm a bit out of sync, so can you resend these patches with
collected ACKs after -rc1?

Please send me the patches I can just merge into the pinctrl tree
separately if possible, I encourage any DTS changes to go in
orthogonally through ARM SoC. The DTS business I regard as
kind of its own tree.

Yours,
Linus Walleij

2016-04-05 13:16:26

by Thierry Reding

[permalink] [raw]
Subject: Re: [PATCH v10 4/9] phy: Add Tegra XUSB pad controller support

Hi Kishon,

The dependencies within this series somewhat complicated, so I'd prefer
to take it all via one tree. Would you be willing to give an Acked-by on
this patch?

Thierry

On Fri, Mar 04, 2016 at 05:19:34PM +0100, Thierry Reding wrote:
> From: Thierry Reding <[email protected]>
>
> Add a new driver for the XUSB pad controller found on NVIDIA Tegra SoCs.
> This hardware block used to be exposed as a pin controller, but it turns
> out that this isn't a good fit. The new driver and DT binding much more
> accurately describe the hardware and are more flexible in supporting new
> SoC generations.
>
> Signed-off-by: Thierry Reding <[email protected]>
> ---
> Changes in v9:
> - export public API for direct use by the xHCI driver (replaces mailbox
> API which had introduced a nasty circular dependency)
>
> drivers/phy/Kconfig | 2 +
> drivers/phy/Makefile | 2 +
> drivers/phy/tegra/Kconfig | 8 +
> drivers/phy/tegra/Makefile | 5 +
> drivers/phy/tegra/xusb-tegra124.c | 1747 ++++++++++++++++++++++++++++
> drivers/phy/tegra/xusb.c | 1017 ++++++++++++++++
> drivers/phy/tegra/xusb.h | 421 +++++++
> drivers/pinctrl/tegra/pinctrl-tegra-xusb.c | 20 +-
> include/linux/phy/tegra/xusb.h | 30 +
> 9 files changed, 3236 insertions(+), 16 deletions(-)
> create mode 100644 drivers/phy/tegra/Kconfig
> create mode 100644 drivers/phy/tegra/Makefile
> create mode 100644 drivers/phy/tegra/xusb-tegra124.c
> create mode 100644 drivers/phy/tegra/xusb.c
> create mode 100644 drivers/phy/tegra/xusb.h
> create mode 100644 include/linux/phy/tegra/xusb.h
>
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index 0124d17bd9fe..4bf65ceb3250 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -407,4 +407,6 @@ config PHY_CYGNUS_PCIE
> Enable this to support the Broadcom Cygnus PCIe PHY.
> If unsure, say N.
>
> +source "drivers/phy/tegra/Kconfig"
> +
> endmenu
> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> index c80f09df3bb8..82709141d072 100644
> --- a/drivers/phy/Makefile
> +++ b/drivers/phy/Makefile
> @@ -50,3 +50,5 @@ obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o
> obj-$(CONFIG_PHY_BRCMSTB_SATA) += phy-brcmstb-sata.o
> obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o
> obj-$(CONFIG_PHY_CYGNUS_PCIE) += phy-bcm-cygnus-pcie.o
> +
> +obj-$(CONFIG_ARCH_TEGRA) += tegra/
> diff --git a/drivers/phy/tegra/Kconfig b/drivers/phy/tegra/Kconfig
> new file mode 100644
> index 000000000000..a3b1de953fb7
> --- /dev/null
> +++ b/drivers/phy/tegra/Kconfig
> @@ -0,0 +1,8 @@
> +config PHY_TEGRA_XUSB
> + tristate "NVIDIA Tegra XUSB pad controller driver"
> + depends on ARCH_TEGRA
> + help
> + Choose this option if you have an NVIDIA Tegra SoC.
> +
> + To compile this driver as a module, choose M here: the module will
> + be called phy-tegra-xusb.
> diff --git a/drivers/phy/tegra/Makefile b/drivers/phy/tegra/Makefile
> new file mode 100644
> index 000000000000..31150b4337cd
> --- /dev/null
> +++ b/drivers/phy/tegra/Makefile
> @@ -0,0 +1,5 @@
> +obj-$(CONFIG_PHY_TEGRA_XUSB) += phy-tegra-xusb.o
> +
> +phy-tegra-xusb-y += xusb.o
> +phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_124_SOC) += xusb-tegra124.o
> +phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_132_SOC) += xusb-tegra124.o
> diff --git a/drivers/phy/tegra/xusb-tegra124.c b/drivers/phy/tegra/xusb-tegra124.c
> new file mode 100644
> index 000000000000..6340d43688d3
> --- /dev/null
> +++ b/drivers/phy/tegra/xusb-tegra124.c
> @@ -0,0 +1,1747 @@
> +/*
> + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + * more details.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/mailbox_client.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/reset.h>
> +#include <linux/slab.h>
> +
> +#include <soc/tegra/fuse.h>
> +
> +#include "xusb.h"
> +
> +#define FUSE_SKU_CALIB_HS_CURR_LEVEL_PADX_SHIFT(x) ((x) ? 15 : 0)
> +#define FUSE_SKU_CALIB_HS_CURR_LEVEL_PAD_MASK 0x3f
> +#define FUSE_SKU_CALIB_HS_IREF_CAP_SHIFT 13
> +#define FUSE_SKU_CALIB_HS_IREF_CAP_MASK 0x3
> +#define FUSE_SKU_CALIB_HS_SQUELCH_LEVEL_SHIFT 11
> +#define FUSE_SKU_CALIB_HS_SQUELCH_LEVEL_MASK 0x3
> +#define FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_SHIFT 7
> +#define FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_MASK 0xf
> +
> +#define XUSB_PADCTL_USB2_PORT_CAP 0x008
> +#define XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_SHIFT(x) ((x) * 4)
> +#define XUSB_PADCTL_USB2_PORT_CAP_PORT_CAP_MASK 0x3
> +#define XUSB_PADCTL_USB2_PORT_CAP_DISABLED 0x0
> +#define XUSB_PADCTL_USB2_PORT_CAP_HOST 0x1
> +#define XUSB_PADCTL_USB2_PORT_CAP_DEVICE 0x2
> +#define XUSB_PADCTL_USB2_PORT_CAP_OTG 0x3
> +
> +#define XUSB_PADCTL_SS_PORT_MAP 0x014
> +#define XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(x) (1 << (((x) * 4) + 3))
> +#define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_SHIFT(x) ((x) * 4)
> +#define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(x) (0x7 << ((x) * 4))
> +#define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(x, v) (((v) & 0x7) << ((x) * 4))
> +#define XUSB_PADCTL_SS_PORT_MAP_PORT_MAP_MASK 0x7
> +
> +#define XUSB_PADCTL_ELPG_PROGRAM 0x01c
> +#define XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN (1 << 26)
> +#define XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 25)
> +#define XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN (1 << 24)
> +#define XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_VCORE_DOWN(x) (1 << (18 + (x) * 4))
> +#define XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN_EARLY(x) \
> + (1 << (17 + (x) * 4))
> +#define XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN(x) (1 << (16 + (x) * 4))
> +
> +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL1 0x040
> +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL0_LOCKDET (1 << 19)
> +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL1_REFCLK_SEL_MASK (0xf << 12)
> +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL_RST (1 << 1)
> +
> +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL2 0x044
> +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL2_REFCLKBUF_EN (1 << 6)
> +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL2_TXCLKREF_EN (1 << 5)
> +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL2_TXCLKREF_SEL (1 << 4)
> +
> +#define XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(x) (0x058 + (x) * 4)
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_SHIFT 24
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_MASK 0xff
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_VAL 0x24
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT 16
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_MASK 0x3f
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT 8
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_MASK 0x3f
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_SHIFT 8
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_MASK 0xffff
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_VAL 0xf070
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_SHIFT 4
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_MASK 0xf
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_VAL 0xf
> +
> +#define XUSB_PADCTL_IOPHY_USB3_PADX_CTL4(x) (0x068 + (x) * 4)
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT 24
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_MASK 0x1f
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT 16
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_MASK 0x7f
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_VAL 0x002008ee
> +
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL2(x) ((x) < 2 ? 0x078 + (x) * 4 : \
> + 0x0f8 + (x) * 4)
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_SHIFT 28
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_MASK 0x3
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_VAL 0x1
> +
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL5(x) ((x) < 2 ? 0x090 + (x) * 4 : \
> + 0x11c + (x) * 4)
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL5_RX_QEYE_EN (1 << 8)
> +
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL6(x) ((x) < 2 ? 0x098 + (x) * 4 : \
> + 0x128 + (x) * 4)
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT 24
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_G_Z_MASK 0x3f
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_TAP_MASK 0x1f
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_AMP_MASK 0x7f
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT 16
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK 0xff
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_G_Z 0x21
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_TAP 0x32
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_AMP 0x33
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_CTLE_Z 0x48
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_LATCH_G_Z 0xa1
> +
> +#define XUSB_PADCTL_USB2_OTG_PADX_CTL0(x) (0x0a0 + (x) * 4)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD_ZI (1 << 21)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD2 (1 << 20)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD (1 << 19)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_SHIFT 14
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_MASK 0x3
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_VAL(x) ((x) ? 0x0 : 0x3)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_SHIFT 6
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_MASK 0x3f
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_VAL 0x0e
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_SHIFT 0
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_MASK 0x3f
> +
> +#define XUSB_PADCTL_USB2_OTG_PADX_CTL1(x) (0x0ac + (x) * 4)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_HS_IREF_CAP_SHIFT 9
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_HS_IREF_CAP_MASK 0x3
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_SHIFT 3
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_MASK 0x7
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR (1 << 2)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DISC_FORCE_POWERUP (1 << 1)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_CHRP_FORCE_POWERUP (1 << 0)
> +
> +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 0x0b8
> +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD (1 << 12)
> +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_SHIFT 2
> +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_MASK 0x7
> +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_VAL 0x5
> +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_SHIFT 0
> +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_MASK 0x3
> +
> +#define XUSB_PADCTL_HSIC_PADX_CTL0(x) (0x0c0 + (x) * 4)
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_SHIFT 12
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_MASK 0x7
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_SHIFT 8
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_MASK 0x7
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_SHIFT 4
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_MASK 0x7
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_SHIFT 0
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_MASK 0x7
> +
> +#define XUSB_PADCTL_HSIC_PADX_CTL1(x) (0x0c8 + (x) * 4)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_RPU_STROBE (1 << 10)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_RPU_DATA (1 << 9)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_RPD_STROBE (1 << 8)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_RPD_DATA (1 << 7)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_PD_ZI (1 << 5)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_PD_RX (1 << 4)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_PD_TRX (1 << 3)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_PD_TX (1 << 2)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_AUTO_TERM_EN (1 << 0)
> +
> +#define XUSB_PADCTL_HSIC_PADX_CTL2(x) (0x0d0 + (x) * 4)
> +#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_SHIFT 4
> +#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_MASK 0x7
> +#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_SHIFT 0
> +#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_MASK 0x7
> +
> +#define XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL 0x0e0
> +#define XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL_STRB_TRIM_MASK 0x1f
> +
> +#define XUSB_PADCTL_USB3_PAD_MUX 0x134
> +#define XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(x) (1 << (1 + (x)))
> +#define XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(x) (1 << (6 + (x)))
> +
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1 0x138
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_LOCKDET (1 << 27)
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_MODE (1 << 24)
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL0_REFCLK_NDIV_SHIFT 20
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL0_REFCLK_NDIV_MASK 0x3
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD (1 << 3)
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_RST (1 << 1)
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_IDDQ (1 << 0)
> +
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2 0x13c
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL1_CP_CNTL_SHIFT 20
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL1_CP_CNTL_MASK 0xf
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL0_CP_CNTL_SHIFT 16
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL0_CP_CNTL_MASK 0xf
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_TCLKOUT_EN (1 << 12)
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_TXCLKREF_SEL (1 << 4)
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_XDIGCLK_SEL_SHIFT 0
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_XDIGCLK_SEL_MASK 0x7
> +
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL3 0x140
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL3_RCAL_BYPASS (1 << 7)
> +
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1 0x148
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD (1 << 1)
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ (1 << 0)
> +
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL2 0x14c
> +
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL5 0x158
> +
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL6 0x15c
> +
> +struct tegra124_xusb_fuse_calibration {
> + u32 hs_curr_level[3];
> + u32 hs_iref_cap;
> + u32 hs_term_range_adj;
> + u32 hs_squelch_level;
> +};
> +
> +struct tegra124_xusb_padctl {
> + struct tegra_xusb_padctl base;
> +
> + struct tegra124_xusb_fuse_calibration fuse;
> +};
> +
> +static inline struct tegra124_xusb_padctl *
> +to_tegra124_xusb_padctl(struct tegra_xusb_padctl *padctl)
> +{
> + return container_of(padctl, struct tegra124_xusb_padctl, base);
> +}
> +
> +static int tegra124_xusb_padctl_enable(struct tegra_xusb_padctl *padctl)
> +{
> + u32 value;
> +
> + mutex_lock(&padctl->lock);
> +
> + if (padctl->enable++ > 0)
> + goto out;
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
> + value &= ~XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN;
> + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
> +
> + usleep_range(100, 200);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
> + value &= ~XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY;
> + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
> +
> + usleep_range(100, 200);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
> + value &= ~XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN;
> + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
> +
> +out:
> + mutex_unlock(&padctl->lock);
> + return 0;
> +}
> +
> +static int tegra124_xusb_padctl_disable(struct tegra_xusb_padctl *padctl)
> +{
> + u32 value;
> +
> + mutex_lock(&padctl->lock);
> +
> + if (WARN_ON(padctl->enable == 0))
> + goto out;
> +
> + if (--padctl->enable > 0)
> + goto out;
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
> + value |= XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN;
> + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
> +
> + usleep_range(100, 200);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
> + value |= XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY;
> + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
> +
> + usleep_range(100, 200);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
> + value |= XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN;
> + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
> +
> +out:
> + mutex_unlock(&padctl->lock);
> + return 0;
> +}
> +
> +static int tegra124_usb3_save_context(struct tegra_xusb_padctl *padctl,
> + unsigned int index)
> +{
> + struct tegra_xusb_usb3_port *port;
> + struct tegra_xusb_lane *lane;
> + u32 value, offset;
> +
> + port = tegra_xusb_find_usb3_port(padctl, index);
> + if (!port)
> + return -ENODEV;
> +
> + port->context_saved = true;
> + lane = port->base.lane;
> +
> + if (lane->pad == padctl->pcie)
> + offset = XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL6(lane->index);
> + else
> + offset = XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL6;
> +
> + value = padctl_readl(padctl, offset);
> + value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK <<
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT);
> + value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_TAP <<
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT;
> + padctl_writel(padctl, value, offset);
> +
> + value = padctl_readl(padctl, offset) >>
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT;
> + port->tap1 = value & XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_TAP_MASK;
> +
> + value = padctl_readl(padctl, offset);
> + value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK <<
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT);
> + value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_AMP <<
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT;
> + padctl_writel(padctl, value, offset);
> +
> + value = padctl_readl(padctl, offset) >>
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT;
> + port->amp = value & XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_AMP_MASK;
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_USB3_PADX_CTL4(index));
> + value &= ~((XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_MASK <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT) |
> + (XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_MASK <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT));
> + value |= (port->tap1 <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT) |
> + (port->amp <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT);
> + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_USB3_PADX_CTL4(index));
> +
> + value = padctl_readl(padctl, offset);
> + value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK <<
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT);
> + value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_LATCH_G_Z <<
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT;
> + padctl_writel(padctl, value, offset);
> +
> + value = padctl_readl(padctl, offset);
> + value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK <<
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT);
> + value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_G_Z <<
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT;
> + padctl_writel(padctl, value, offset);
> +
> + value = padctl_readl(padctl, offset) >>
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT;
> + port->ctle_g = value &
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_G_Z_MASK;
> +
> + value = padctl_readl(padctl, offset);
> + value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK <<
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT);
> + value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_CTLE_Z <<
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT;
> + padctl_writel(padctl, value, offset);
> +
> + value = padctl_readl(padctl, offset) >>
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT;
> + port->ctle_z = value &
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_G_Z_MASK;
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(index));
> + value &= ~((XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_MASK <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT) |
> + (XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_MASK <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT));
> + value |= (port->ctle_g <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT) |
> + (port->ctle_z <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT);
> + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(index));
> +
> + return 0;
> +}
> +
> +static int tegra124_hsic_set_idle(struct tegra_xusb_padctl *padctl,
> + unsigned int index, bool idle)
> +{
> + u32 value;
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL1(index));
> +
> + if (idle)
> + value |= XUSB_PADCTL_HSIC_PAD_CTL1_RPD_DATA |
> + XUSB_PADCTL_HSIC_PAD_CTL1_RPU_STROBE;
> + else
> + value &= ~(XUSB_PADCTL_HSIC_PAD_CTL1_RPD_DATA |
> + XUSB_PADCTL_HSIC_PAD_CTL1_RPU_STROBE);
> +
> + padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL1(index));
> +
> + return 0;
> +}
> +
> +#define TEGRA124_LANE(_name, _offset, _shift, _mask, _type) \
> + { \
> + .name = _name, \
> + .offset = _offset, \
> + .shift = _shift, \
> + .mask = _mask, \
> + .num_funcs = ARRAY_SIZE(tegra124_##_type##_functions), \
> + .funcs = tegra124_##_type##_functions, \
> + }
> +
> +static const char * const tegra124_usb2_functions[] = {
> + "snps",
> + "xusb",
> + "uart",
> +};
> +
> +static const struct tegra_xusb_lane_soc tegra124_usb2_lanes[] = {
> + TEGRA124_LANE("usb2-0", 0x004, 0, 0x3, usb2),
> + TEGRA124_LANE("usb2-1", 0x004, 2, 0x3, usb2),
> + TEGRA124_LANE("usb2-2", 0x004, 4, 0x3, usb2),
> +};
> +
> +static struct tegra_xusb_lane *
> +tegra124_usb2_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np,
> + unsigned int index)
> +{
> + struct tegra_xusb_usb2_lane *usb2;
> + int err;
> +
> + usb2 = kzalloc(sizeof(*usb2), GFP_KERNEL);
> + if (!usb2)
> + return ERR_PTR(-ENOMEM);
> +
> + INIT_LIST_HEAD(&usb2->base.list);
> + usb2->base.soc = &pad->soc->lanes[index];
> + usb2->base.index = index;
> + usb2->base.pad = pad;
> + usb2->base.np = np;
> +
> + err = tegra_xusb_lane_parse_dt(&usb2->base, np);
> + if (err < 0) {
> + kfree(usb2);
> + return ERR_PTR(err);
> + }
> +
> + return &usb2->base;
> +}
> +
> +static void tegra124_usb2_lane_remove(struct tegra_xusb_lane *lane)
> +{
> + struct tegra_xusb_usb2_lane *usb2 = to_usb2_lane(lane);
> +
> + kfree(usb2);
> +}
> +
> +static const struct tegra_xusb_lane_ops tegra124_usb2_lane_ops = {
> + .probe = tegra124_usb2_lane_probe,
> + .remove = tegra124_usb2_lane_remove,
> +};
> +
> +static int tegra124_usb2_phy_init(struct phy *phy)
> +{
> + struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
> +
> + return tegra124_xusb_padctl_enable(lane->pad->padctl);
> +}
> +
> +static int tegra124_usb2_phy_exit(struct phy *phy)
> +{
> + struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
> +
> + return tegra124_xusb_padctl_disable(lane->pad->padctl);
> +}
> +
> +static int tegra124_usb2_phy_power_on(struct phy *phy)
> +{
> + struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
> + struct tegra_xusb_usb2_lane *usb2 = to_usb2_lane(lane);
> + struct tegra_xusb_usb2_pad *pad = to_usb2_pad(lane->pad);
> + struct tegra_xusb_padctl *padctl = lane->pad->padctl;
> + struct tegra124_xusb_padctl *priv;
> + struct tegra_xusb_usb2_port *port;
> + unsigned int index = lane->index;
> + u32 value;
> + int err;
> +
> + port = tegra_xusb_find_usb2_port(padctl, index);
> + if (!port) {
> + dev_err(&phy->dev, "no port found for USB2 lane %u\n", index);
> + return -ENODEV;
> + }
> +
> + priv = to_tegra124_xusb_padctl(padctl);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
> + value &= ~((XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_MASK <<
> + XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_SHIFT) |
> + (XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_MASK <<
> + XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_SHIFT));
> + value |= (priv->fuse.hs_squelch_level <<
> + XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_SHIFT) |
> + (XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_VAL <<
> + XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_SHIFT);
> + padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_USB2_PORT_CAP);
> + value &= ~(XUSB_PADCTL_USB2_PORT_CAP_PORT_CAP_MASK <<
> + XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_SHIFT(index));
> + value |= XUSB_PADCTL_USB2_PORT_CAP_HOST <<
> + XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_SHIFT(index);
> + padctl_writel(padctl, value, XUSB_PADCTL_USB2_PORT_CAP);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
> + value &= ~((XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_MASK <<
> + XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_SHIFT) |
> + (XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_MASK <<
> + XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_SHIFT) |
> + (XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_MASK <<
> + XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_SHIFT) |
> + XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD |
> + XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD2 |
> + XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD_ZI);
> + value |= (priv->fuse.hs_curr_level[index] +
> + usb2->hs_curr_level_offset) <<
> + XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_SHIFT;
> + value |= XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_VAL <<
> + XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_SHIFT;
> + value |= XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_VAL(index) <<
> + XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_SHIFT;
> + padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL0(index));
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
> + value &= ~((XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_MASK <<
> + XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_SHIFT) |
> + (XUSB_PADCTL_USB2_OTG_PAD_CTL1_HS_IREF_CAP_MASK <<
> + XUSB_PADCTL_USB2_OTG_PAD_CTL1_HS_IREF_CAP_SHIFT) |
> + XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR |
> + XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_CHRP_FORCE_POWERUP |
> + XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DISC_FORCE_POWERUP);
> + value |= (priv->fuse.hs_term_range_adj <<
> + XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_SHIFT) |
> + (priv->fuse.hs_iref_cap <<
> + XUSB_PADCTL_USB2_OTG_PAD_CTL1_HS_IREF_CAP_SHIFT);
> + padctl_writel(padctl, value, XUSB_PADCTL_USB2_OTG_PADX_CTL1(index));
> +
> + err = regulator_enable(port->supply);
> + if (err)
> + return err;
> +
> + mutex_lock(&pad->lock);
> +
> + if (pad->enable++ > 0)
> + goto out;
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
> + value &= ~XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD;
> + padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
> +
> +out:
> + mutex_unlock(&pad->lock);
> + return 0;
> +}
> +
> +static int tegra124_usb2_phy_power_off(struct phy *phy)
> +{
> + struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
> + struct tegra_xusb_usb2_pad *pad = to_usb2_pad(lane->pad);
> + struct tegra_xusb_padctl *padctl = lane->pad->padctl;
> + struct tegra_xusb_usb2_port *port;
> + u32 value;
> +
> + port = tegra_xusb_find_usb2_port(padctl, lane->index);
> + if (!port) {
> + dev_err(&phy->dev, "no port found for USB2 lane %u\n",
> + lane->index);
> + return -ENODEV;
> + }
> +
> + mutex_lock(&pad->lock);
> +
> + if (WARN_ON(pad->enable == 0))
> + goto out;
> +
> + if (--pad->enable > 0)
> + goto out;
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
> + value |= XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD;
> + padctl_writel(padctl, value, XUSB_PADCTL_USB2_BIAS_PAD_CTL0);
> +
> +out:
> + regulator_disable(port->supply);
> + mutex_unlock(&pad->lock);
> + return 0;
> +}
> +
> +static const struct phy_ops tegra124_usb2_phy_ops = {
> + .init = tegra124_usb2_phy_init,
> + .exit = tegra124_usb2_phy_exit,
> + .power_on = tegra124_usb2_phy_power_on,
> + .power_off = tegra124_usb2_phy_power_off,
> + .owner = THIS_MODULE,
> +};
> +
> +static struct tegra_xusb_pad *
> +tegra124_usb2_pad_probe(struct tegra_xusb_padctl *padctl,
> + const struct tegra_xusb_pad_soc *soc,
> + struct device_node *np)
> +{
> + struct tegra_xusb_usb2_pad *usb2;
> + struct tegra_xusb_pad *pad;
> + int err;
> +
> + usb2 = kzalloc(sizeof(*usb2), GFP_KERNEL);
> + if (!usb2)
> + return ERR_PTR(-ENOMEM);
> +
> + mutex_init(&usb2->lock);
> +
> + pad = &usb2->base;
> + pad->ops = &tegra124_usb2_lane_ops;
> + pad->soc = soc;
> +
> + err = tegra_xusb_pad_init(pad, padctl, np);
> + if (err < 0)
> + goto free;
> +
> + err = tegra_xusb_pad_register(pad, &tegra124_usb2_phy_ops);
> + if (err < 0)
> + goto unregister;
> +
> + dev_set_drvdata(&pad->dev, pad);
> +
> + return pad;
> +
> +unregister:
> + device_unregister(&pad->dev);
> +free:
> + kfree(usb2);
> + return ERR_PTR(err);
> +}
> +
> +static void tegra124_usb2_pad_remove(struct tegra_xusb_pad *pad)
> +{
> + struct tegra_xusb_usb2_pad *usb2 = to_usb2_pad(pad);
> +
> + kfree(usb2);
> +}
> +
> +static const struct tegra_xusb_pad_ops tegra124_usb2_ops = {
> + .probe = tegra124_usb2_pad_probe,
> + .remove = tegra124_usb2_pad_remove,
> +};
> +
> +static const struct tegra_xusb_pad_soc tegra124_usb2_pad = {
> + .name = "usb2",
> + .num_lanes = ARRAY_SIZE(tegra124_usb2_lanes),
> + .lanes = tegra124_usb2_lanes,
> + .ops = &tegra124_usb2_ops,
> +};
> +
> +static const char * const tegra124_ulpi_functions[] = {
> + "snps",
> + "xusb",
> +};
> +
> +static const struct tegra_xusb_lane_soc tegra124_ulpi_lanes[] = {
> + TEGRA124_LANE("ulpi-0", 0x004, 12, 0x1, ulpi),
> +};
> +
> +static struct tegra_xusb_lane *
> +tegra124_ulpi_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np,
> + unsigned int index)
> +{
> + struct tegra_xusb_ulpi_lane *ulpi;
> + int err;
> +
> + ulpi = kzalloc(sizeof(*ulpi), GFP_KERNEL);
> + if (!ulpi)
> + return ERR_PTR(-ENOMEM);
> +
> + INIT_LIST_HEAD(&ulpi->base.list);
> + ulpi->base.soc = &pad->soc->lanes[index];
> + ulpi->base.index = index;
> + ulpi->base.pad = pad;
> + ulpi->base.np = np;
> +
> + err = tegra_xusb_lane_parse_dt(&ulpi->base, np);
> + if (err < 0) {
> + kfree(ulpi);
> + return ERR_PTR(err);
> + }
> +
> + return &ulpi->base;
> +}
> +
> +static void tegra124_ulpi_lane_remove(struct tegra_xusb_lane *lane)
> +{
> + struct tegra_xusb_ulpi_lane *ulpi = to_ulpi_lane(lane);
> +
> + kfree(ulpi);
> +}
> +
> +static const struct tegra_xusb_lane_ops tegra124_ulpi_lane_ops = {
> + .probe = tegra124_ulpi_lane_probe,
> + .remove = tegra124_ulpi_lane_remove,
> +};
> +
> +static int tegra124_ulpi_phy_init(struct phy *phy)
> +{
> + struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
> +
> + return tegra124_xusb_padctl_enable(lane->pad->padctl);
> +}
> +
> +static int tegra124_ulpi_phy_exit(struct phy *phy)
> +{
> + struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
> +
> + return tegra124_xusb_padctl_disable(lane->pad->padctl);
> +}
> +
> +static int tegra124_ulpi_phy_power_on(struct phy *phy)
> +{
> + return 0;
> +}
> +
> +static int tegra124_ulpi_phy_power_off(struct phy *phy)
> +{
> + return 0;
> +}
> +
> +static const struct phy_ops tegra124_ulpi_phy_ops = {
> + .init = tegra124_ulpi_phy_init,
> + .exit = tegra124_ulpi_phy_exit,
> + .power_on = tegra124_ulpi_phy_power_on,
> + .power_off = tegra124_ulpi_phy_power_off,
> + .owner = THIS_MODULE,
> +};
> +
> +static struct tegra_xusb_pad *
> +tegra124_ulpi_pad_probe(struct tegra_xusb_padctl *padctl,
> + const struct tegra_xusb_pad_soc *soc,
> + struct device_node *np)
> +{
> + struct tegra_xusb_ulpi_pad *ulpi;
> + struct tegra_xusb_pad *pad;
> + int err;
> +
> + ulpi = kzalloc(sizeof(*ulpi), GFP_KERNEL);
> + if (!ulpi)
> + return ERR_PTR(-ENOMEM);
> +
> + pad = &ulpi->base;
> + pad->ops = &tegra124_ulpi_lane_ops;
> + pad->soc = soc;
> +
> + err = tegra_xusb_pad_init(pad, padctl, np);
> + if (err < 0)
> + goto free;
> +
> + err = tegra_xusb_pad_register(pad, &tegra124_ulpi_phy_ops);
> + if (err < 0)
> + goto unregister;
> +
> + dev_set_drvdata(&pad->dev, pad);
> +
> + return pad;
> +
> +unregister:
> + device_unregister(&pad->dev);
> +free:
> + kfree(ulpi);
> + return ERR_PTR(err);
> +}
> +
> +static void tegra124_ulpi_pad_remove(struct tegra_xusb_pad *pad)
> +{
> + struct tegra_xusb_ulpi_pad *ulpi = to_ulpi_pad(pad);
> +
> + kfree(ulpi);
> +}
> +
> +static const struct tegra_xusb_pad_ops tegra124_ulpi_ops = {
> + .probe = tegra124_ulpi_pad_probe,
> + .remove = tegra124_ulpi_pad_remove,
> +};
> +
> +static const struct tegra_xusb_pad_soc tegra124_ulpi_pad = {
> + .name = "ulpi",
> + .num_lanes = ARRAY_SIZE(tegra124_ulpi_lanes),
> + .lanes = tegra124_ulpi_lanes,
> + .ops = &tegra124_ulpi_ops,
> +};
> +
> +static const char * const tegra124_hsic_functions[] = {
> + "snps",
> + "xusb",
> +};
> +
> +static const struct tegra_xusb_lane_soc tegra124_hsic_lanes[] = {
> + TEGRA124_LANE("hsic-0", 0x004, 14, 0x1, hsic),
> + TEGRA124_LANE("hsic-1", 0x004, 15, 0x1, hsic),
> +};
> +
> +static struct tegra_xusb_lane *
> +tegra124_hsic_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np,
> + unsigned int index)
> +{
> + struct tegra_xusb_hsic_lane *hsic;
> + int err;
> +
> + hsic = kzalloc(sizeof(*hsic), GFP_KERNEL);
> + if (!hsic)
> + return ERR_PTR(-ENOMEM);
> +
> + INIT_LIST_HEAD(&hsic->base.list);
> + hsic->base.soc = &pad->soc->lanes[index];
> + hsic->base.index = index;
> + hsic->base.pad = pad;
> + hsic->base.np = np;
> +
> + err = tegra_xusb_lane_parse_dt(&hsic->base, np);
> + if (err < 0) {
> + kfree(hsic);
> + return ERR_PTR(err);
> + }
> +
> + return &hsic->base;
> +}
> +
> +static void tegra124_hsic_lane_remove(struct tegra_xusb_lane *lane)
> +{
> + struct tegra_xusb_hsic_lane *hsic = to_hsic_lane(lane);
> +
> + kfree(hsic);
> +}
> +
> +static const struct tegra_xusb_lane_ops tegra124_hsic_lane_ops = {
> + .probe = tegra124_hsic_lane_probe,
> + .remove = tegra124_hsic_lane_remove,
> +};
> +
> +static int tegra124_hsic_phy_init(struct phy *phy)
> +{
> + struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
> +
> + return tegra124_xusb_padctl_enable(lane->pad->padctl);
> +}
> +
> +static int tegra124_hsic_phy_exit(struct phy *phy)
> +{
> + struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
> +
> + return tegra124_xusb_padctl_disable(lane->pad->padctl);
> +}
> +
> +static int tegra124_hsic_phy_power_on(struct phy *phy)
> +{
> + struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
> + struct tegra_xusb_hsic_lane *hsic = to_hsic_lane(lane);
> + struct tegra_xusb_hsic_pad *pad = to_hsic_pad(lane->pad);
> + struct tegra_xusb_padctl *padctl = lane->pad->padctl;
> + unsigned int index = lane->index;
> + u32 value;
> + int err;
> +
> + err = regulator_enable(pad->supply);
> + if (err)
> + return err;
> +
> + padctl_writel(padctl, hsic->strobe_trim,
> + XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL1(index));
> +
> + if (hsic->auto_term)
> + value |= XUSB_PADCTL_HSIC_PAD_CTL1_AUTO_TERM_EN;
> + else
> + value &= ~XUSB_PADCTL_HSIC_PAD_CTL1_AUTO_TERM_EN;
> +
> + padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL1(index));
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL0(index));
> + value &= ~((XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_MASK <<
> + XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_SHIFT) |
> + (XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_MASK <<
> + XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_SHIFT) |
> + (XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_MASK <<
> + XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_SHIFT) |
> + (XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_MASK <<
> + XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_SHIFT));
> + value |= (hsic->tx_rtune_n <<
> + XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_SHIFT) |
> + (hsic->tx_rtune_p <<
> + XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_SHIFT) |
> + (hsic->tx_rslew_n <<
> + XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_SHIFT) |
> + (hsic->tx_rslew_p <<
> + XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_SHIFT);
> + padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL0(index));
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL2(index));
> + value &= ~((XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_MASK <<
> + XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_SHIFT) |
> + (XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_MASK <<
> + XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_SHIFT));
> + value |= (hsic->rx_strobe_trim <<
> + XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_SHIFT) |
> + (hsic->rx_data_trim <<
> + XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_SHIFT);
> + padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL2(index));
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL1(index));
> + value &= ~(XUSB_PADCTL_HSIC_PAD_CTL1_RPD_STROBE |
> + XUSB_PADCTL_HSIC_PAD_CTL1_RPU_DATA |
> + XUSB_PADCTL_HSIC_PAD_CTL1_PD_RX |
> + XUSB_PADCTL_HSIC_PAD_CTL1_PD_ZI |
> + XUSB_PADCTL_HSIC_PAD_CTL1_PD_TRX |
> + XUSB_PADCTL_HSIC_PAD_CTL1_PD_TX);
> + value |= XUSB_PADCTL_HSIC_PAD_CTL1_RPD_DATA |
> + XUSB_PADCTL_HSIC_PAD_CTL1_RPU_STROBE;
> + padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL1(index));
> +
> + return 0;
> +}
> +
> +static int tegra124_hsic_phy_power_off(struct phy *phy)
> +{
> + struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
> + struct tegra_xusb_hsic_pad *pad = to_hsic_pad(lane->pad);
> + struct tegra_xusb_padctl *padctl = lane->pad->padctl;
> + unsigned int index = lane->index;
> + u32 value;
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_HSIC_PADX_CTL1(index));
> + value |= XUSB_PADCTL_HSIC_PAD_CTL1_PD_RX |
> + XUSB_PADCTL_HSIC_PAD_CTL1_PD_ZI |
> + XUSB_PADCTL_HSIC_PAD_CTL1_PD_TRX |
> + XUSB_PADCTL_HSIC_PAD_CTL1_PD_TX;
> + padctl_writel(padctl, value, XUSB_PADCTL_HSIC_PADX_CTL1(index));
> +
> + regulator_disable(pad->supply);
> +
> + return 0;
> +}
> +
> +static const struct phy_ops tegra124_hsic_phy_ops = {
> + .init = tegra124_hsic_phy_init,
> + .exit = tegra124_hsic_phy_exit,
> + .power_on = tegra124_hsic_phy_power_on,
> + .power_off = tegra124_hsic_phy_power_off,
> + .owner = THIS_MODULE,
> +};
> +
> +static struct tegra_xusb_pad *
> +tegra124_hsic_pad_probe(struct tegra_xusb_padctl *padctl,
> + const struct tegra_xusb_pad_soc *soc,
> + struct device_node *np)
> +{
> + struct tegra_xusb_hsic_pad *hsic;
> + struct tegra_xusb_pad *pad;
> + int err;
> +
> + hsic = kzalloc(sizeof(*hsic), GFP_KERNEL);
> + if (!hsic)
> + return ERR_PTR(-ENOMEM);
> +
> + pad = &hsic->base;
> + pad->ops = &tegra124_hsic_lane_ops;
> + pad->soc = soc;
> +
> + err = tegra_xusb_pad_init(pad, padctl, np);
> + if (err < 0)
> + goto free;
> +
> + err = tegra_xusb_pad_register(pad, &tegra124_hsic_phy_ops);
> + if (err < 0)
> + goto unregister;
> +
> + dev_set_drvdata(&pad->dev, pad);
> +
> + return pad;
> +
> +unregister:
> + device_unregister(&pad->dev);
> +free:
> + kfree(hsic);
> + return ERR_PTR(err);
> +}
> +
> +static void tegra124_hsic_pad_remove(struct tegra_xusb_pad *pad)
> +{
> + struct tegra_xusb_hsic_pad *hsic = to_hsic_pad(pad);
> +
> + kfree(hsic);
> +}
> +
> +static const struct tegra_xusb_pad_ops tegra124_hsic_ops = {
> + .probe = tegra124_hsic_pad_probe,
> + .remove = tegra124_hsic_pad_remove,
> +};
> +
> +static const struct tegra_xusb_pad_soc tegra124_hsic_pad = {
> + .name = "hsic",
> + .num_lanes = ARRAY_SIZE(tegra124_hsic_lanes),
> + .lanes = tegra124_hsic_lanes,
> + .ops = &tegra124_hsic_ops,
> +};
> +
> +static const char * const tegra124_pcie_functions[] = {
> + "pcie",
> + "usb3-ss",
> + "sata",
> +};
> +
> +static const struct tegra_xusb_lane_soc tegra124_pcie_lanes[] = {
> + TEGRA124_LANE("pcie-0", 0x134, 16, 0x3, pcie),
> + TEGRA124_LANE("pcie-1", 0x134, 18, 0x3, pcie),
> + TEGRA124_LANE("pcie-2", 0x134, 20, 0x3, pcie),
> + TEGRA124_LANE("pcie-3", 0x134, 22, 0x3, pcie),
> + TEGRA124_LANE("pcie-4", 0x134, 24, 0x3, pcie),
> +};
> +
> +static struct tegra_xusb_lane *
> +tegra124_pcie_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np,
> + unsigned int index)
> +{
> + struct tegra_xusb_pcie_lane *pcie;
> + int err;
> +
> + pcie = kzalloc(sizeof(*pcie), GFP_KERNEL);
> + if (!pcie)
> + return ERR_PTR(-ENOMEM);
> +
> + INIT_LIST_HEAD(&pcie->base.list);
> + pcie->base.soc = &pad->soc->lanes[index];
> + pcie->base.index = index;
> + pcie->base.pad = pad;
> + pcie->base.np = np;
> +
> + err = tegra_xusb_lane_parse_dt(&pcie->base, np);
> + if (err < 0) {
> + kfree(pcie);
> + return ERR_PTR(err);
> + }
> +
> + return &pcie->base;
> +}
> +
> +static void tegra124_pcie_lane_remove(struct tegra_xusb_lane *lane)
> +{
> + struct tegra_xusb_pcie_lane *pcie = to_pcie_lane(lane);
> +
> + kfree(pcie);
> +}
> +
> +static const struct tegra_xusb_lane_ops tegra124_pcie_lane_ops = {
> + .probe = tegra124_pcie_lane_probe,
> + .remove = tegra124_pcie_lane_remove,
> +};
> +
> +static int tegra124_pcie_phy_init(struct phy *phy)
> +{
> + struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
> +
> + return tegra124_xusb_padctl_enable(lane->pad->padctl);
> +}
> +
> +static int tegra124_pcie_phy_exit(struct phy *phy)
> +{
> + struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
> +
> + return tegra124_xusb_padctl_disable(lane->pad->padctl);
> +}
> +
> +static int tegra124_pcie_phy_power_on(struct phy *phy)
> +{
> + struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
> + struct tegra_xusb_padctl *padctl = lane->pad->padctl;
> + unsigned long timeout;
> + int err = -ETIMEDOUT;
> + u32 value;
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
> + value &= ~XUSB_PADCTL_IOPHY_PLL_P0_CTL1_REFCLK_SEL_MASK;
> + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_P0_CTL2);
> + value |= XUSB_PADCTL_IOPHY_PLL_P0_CTL2_REFCLKBUF_EN |
> + XUSB_PADCTL_IOPHY_PLL_P0_CTL2_TXCLKREF_EN |
> + XUSB_PADCTL_IOPHY_PLL_P0_CTL2_TXCLKREF_SEL;
> + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_P0_CTL2);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
> + value |= XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL_RST;
> + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
> +
> + timeout = jiffies + msecs_to_jiffies(50);
> +
> + while (time_before(jiffies, timeout)) {
> + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
> + if (value & XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL0_LOCKDET) {
> + err = 0;
> + break;
> + }
> +
> + usleep_range(100, 200);
> + }
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
> + value |= XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->index);
> + padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
> +
> + return err;
> +}
> +
> +static int tegra124_pcie_phy_power_off(struct phy *phy)
> +{
> + struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
> + struct tegra_xusb_padctl *padctl = lane->pad->padctl;
> + u32 value;
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
> + value &= ~XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(lane->index);
> + padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
> + value &= ~XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL_RST;
> + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_P0_CTL1);
> +
> + return 0;
> +}
> +
> +static const struct phy_ops tegra124_pcie_phy_ops = {
> + .init = tegra124_pcie_phy_init,
> + .exit = tegra124_pcie_phy_exit,
> + .power_on = tegra124_pcie_phy_power_on,
> + .power_off = tegra124_pcie_phy_power_off,
> + .owner = THIS_MODULE,
> +};
> +
> +static struct tegra_xusb_pad *
> +tegra124_pcie_pad_probe(struct tegra_xusb_padctl *padctl,
> + const struct tegra_xusb_pad_soc *soc,
> + struct device_node *np)
> +{
> + struct tegra_xusb_pcie_pad *pcie;
> + struct tegra_xusb_pad *pad;
> + int err;
> +
> + pcie = kzalloc(sizeof(*pcie), GFP_KERNEL);
> + if (!pcie)
> + return ERR_PTR(-ENOMEM);
> +
> + pad = &pcie->base;
> + pad->ops = &tegra124_pcie_lane_ops;
> + pad->soc = soc;
> +
> + err = tegra_xusb_pad_init(pad, padctl, np);
> + if (err < 0)
> + goto free;
> +
> + err = tegra_xusb_pad_register(pad, &tegra124_pcie_phy_ops);
> + if (err < 0)
> + goto unregister;
> +
> + dev_set_drvdata(&pad->dev, pad);
> +
> + return pad;
> +
> +unregister:
> + device_unregister(&pad->dev);
> +free:
> + kfree(pcie);
> + return ERR_PTR(err);
> +}
> +
> +static void tegra124_pcie_pad_remove(struct tegra_xusb_pad *pad)
> +{
> + struct tegra_xusb_pcie_pad *pcie = to_pcie_pad(pad);
> +
> + kfree(pcie);
> +}
> +
> +static const struct tegra_xusb_pad_ops tegra124_pcie_ops = {
> + .probe = tegra124_pcie_pad_probe,
> + .remove = tegra124_pcie_pad_remove,
> +};
> +
> +static const struct tegra_xusb_pad_soc tegra124_pcie_pad = {
> + .name = "pcie",
> + .num_lanes = ARRAY_SIZE(tegra124_pcie_lanes),
> + .lanes = tegra124_pcie_lanes,
> + .ops = &tegra124_pcie_ops,
> +};
> +
> +static const struct tegra_xusb_lane_soc tegra124_sata_lanes[] = {
> + TEGRA124_LANE("sata-0", 0x134, 26, 0x3, pcie),
> +};
> +
> +static struct tegra_xusb_lane *
> +tegra124_sata_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np,
> + unsigned int index)
> +{
> + struct tegra_xusb_sata_lane *sata;
> + int err;
> +
> + sata = kzalloc(sizeof(*sata), GFP_KERNEL);
> + if (!sata)
> + return ERR_PTR(-ENOMEM);
> +
> + INIT_LIST_HEAD(&sata->base.list);
> + sata->base.soc = &pad->soc->lanes[index];
> + sata->base.index = index;
> + sata->base.pad = pad;
> + sata->base.np = np;
> +
> + err = tegra_xusb_lane_parse_dt(&sata->base, np);
> + if (err < 0) {
> + kfree(sata);
> + return ERR_PTR(err);
> + }
> +
> + return &sata->base;
> +}
> +
> +static void tegra124_sata_lane_remove(struct tegra_xusb_lane *lane)
> +{
> + struct tegra_xusb_sata_lane *sata = to_sata_lane(lane);
> +
> + kfree(sata);
> +}
> +
> +static const struct tegra_xusb_lane_ops tegra124_sata_lane_ops = {
> + .probe = tegra124_sata_lane_probe,
> + .remove = tegra124_sata_lane_remove,
> +};
> +
> +static int tegra124_sata_phy_init(struct phy *phy)
> +{
> + struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
> +
> + return tegra124_xusb_padctl_enable(lane->pad->padctl);
> +}
> +
> +static int tegra124_sata_phy_exit(struct phy *phy)
> +{
> + struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
> +
> + return tegra124_xusb_padctl_disable(lane->pad->padctl);
> +}
> +
> +static int tegra124_sata_phy_power_on(struct phy *phy)
> +{
> + struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
> + struct tegra_xusb_padctl *padctl = lane->pad->padctl;
> + unsigned long timeout;
> + int err = -ETIMEDOUT;
> + u32 value;
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1);
> + value &= ~XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD;
> + value &= ~XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ;
> + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
> + value &= ~XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD;
> + value &= ~XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_IDDQ;
> + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
> + value |= XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_MODE;
> + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
> + value |= XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_RST;
> + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
> +
> + timeout = jiffies + msecs_to_jiffies(50);
> +
> + while (time_before(jiffies, timeout)) {
> + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
> + if (value & XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_LOCKDET) {
> + err = 0;
> + break;
> + }
> +
> + usleep_range(100, 200);
> + }
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
> + value |= XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(lane->index);
> + padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
> +
> + return err;
> +}
> +
> +static int tegra124_sata_phy_power_off(struct phy *phy)
> +{
> + struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
> + struct tegra_xusb_padctl *padctl = lane->pad->padctl;
> + u32 value;
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_USB3_PAD_MUX);
> + value &= ~XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(lane->index);
> + padctl_writel(padctl, value, XUSB_PADCTL_USB3_PAD_MUX);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
> + value &= ~XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_RST;
> + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
> + value &= ~XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_MODE;
> + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
> + value |= XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD;
> + value |= XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_IDDQ;
> + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1);
> + value |= ~XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD;
> + value |= ~XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ;
> + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1);
> +
> + return 0;
> +}
> +
> +static const struct phy_ops tegra124_sata_phy_ops = {
> + .init = tegra124_sata_phy_init,
> + .exit = tegra124_sata_phy_exit,
> + .power_on = tegra124_sata_phy_power_on,
> + .power_off = tegra124_sata_phy_power_off,
> + .owner = THIS_MODULE,
> +};
> +
> +static struct tegra_xusb_pad *
> +tegra124_sata_pad_probe(struct tegra_xusb_padctl *padctl,
> + const struct tegra_xusb_pad_soc *soc,
> + struct device_node *np)
> +{
> + struct tegra_xusb_sata_pad *sata;
> + struct tegra_xusb_pad *pad;
> + int err;
> +
> + sata = kzalloc(sizeof(*sata), GFP_KERNEL);
> + if (!sata)
> + return ERR_PTR(-ENOMEM);
> +
> + pad = &sata->base;
> + pad->ops = &tegra124_sata_lane_ops;
> + pad->soc = soc;
> +
> + err = tegra_xusb_pad_init(pad, padctl, np);
> + if (err < 0)
> + goto free;
> +
> + err = tegra_xusb_pad_register(pad, &tegra124_sata_phy_ops);
> + if (err < 0)
> + goto unregister;
> +
> + dev_set_drvdata(&pad->dev, pad);
> +
> + return pad;
> +
> +unregister:
> + device_unregister(&pad->dev);
> +free:
> + kfree(sata);
> + return ERR_PTR(err);
> +}
> +
> +static void tegra124_sata_pad_remove(struct tegra_xusb_pad *pad)
> +{
> + struct tegra_xusb_sata_pad *sata = to_sata_pad(pad);
> +
> + kfree(sata);
> +}
> +
> +static const struct tegra_xusb_pad_ops tegra124_sata_ops = {
> + .probe = tegra124_sata_pad_probe,
> + .remove = tegra124_sata_pad_remove,
> +};
> +
> +static const struct tegra_xusb_pad_soc tegra124_sata_pad = {
> + .name = "sata",
> + .num_lanes = ARRAY_SIZE(tegra124_sata_lanes),
> + .lanes = tegra124_sata_lanes,
> + .ops = &tegra124_sata_ops,
> +};
> +
> +static const struct tegra_xusb_pad_soc *tegra124_pads[] = {
> + &tegra124_usb2_pad,
> + &tegra124_ulpi_pad,
> + &tegra124_hsic_pad,
> + &tegra124_pcie_pad,
> + &tegra124_sata_pad,
> +};
> +
> +static int tegra124_usb2_port_enable(struct tegra_xusb_port *port)
> +{
> + return 0;
> +}
> +
> +static void tegra124_usb2_port_disable(struct tegra_xusb_port *port)
> +{
> +}
> +
> +static struct tegra_xusb_lane *
> +tegra124_usb2_port_map(struct tegra_xusb_port *port)
> +{
> + return tegra_xusb_find_lane(port->padctl, "usb2", port->index);
> +}
> +
> +static const struct tegra_xusb_port_ops tegra124_usb2_port_ops = {
> + .enable = tegra124_usb2_port_enable,
> + .disable = tegra124_usb2_port_disable,
> + .map = tegra124_usb2_port_map,
> +};
> +
> +static int tegra124_ulpi_port_enable(struct tegra_xusb_port *port)
> +{
> + return 0;
> +}
> +
> +static void tegra124_ulpi_port_disable(struct tegra_xusb_port *port)
> +{
> +}
> +
> +static struct tegra_xusb_lane *
> +tegra124_ulpi_port_map(struct tegra_xusb_port *port)
> +{
> + return tegra_xusb_find_lane(port->padctl, "ulpi", port->index);
> +}
> +
> +static const struct tegra_xusb_port_ops tegra124_ulpi_port_ops = {
> + .enable = tegra124_ulpi_port_enable,
> + .disable = tegra124_ulpi_port_disable,
> + .map = tegra124_ulpi_port_map,
> +};
> +
> +static int tegra124_hsic_port_enable(struct tegra_xusb_port *port)
> +{
> + return 0;
> +}
> +
> +static void tegra124_hsic_port_disable(struct tegra_xusb_port *port)
> +{
> +}
> +
> +static struct tegra_xusb_lane *
> +tegra124_hsic_port_map(struct tegra_xusb_port *port)
> +{
> + return tegra_xusb_find_lane(port->padctl, "hsic", port->index);
> +}
> +
> +static const struct tegra_xusb_port_ops tegra124_hsic_port_ops = {
> + .enable = tegra124_hsic_port_enable,
> + .disable = tegra124_hsic_port_disable,
> + .map = tegra124_hsic_port_map,
> +};
> +
> +static int tegra124_usb3_port_enable(struct tegra_xusb_port *port)
> +{
> + struct tegra_xusb_usb3_port *usb3 = to_usb3_port(port);
> + struct tegra_xusb_padctl *padctl = port->padctl;
> + struct tegra_xusb_lane *lane = usb3->base.lane;
> + unsigned int index = port->index, offset;
> + int ret = 0;
> + u32 value;
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
> +
> + if (!usb3->internal)
> + value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index);
> + else
> + value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(index);
> +
> + value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(index);
> + value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(index, usb3->port);
> + padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP);
> +
> + /*
> + * TODO: move this code into the PCIe/SATA PHY ->power_on() callbacks
> + * and conditionalize based on mux function? This seems to work, but
> + * might not be the exact proper sequence.
> + */
> + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(index));
> + value &= ~((XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_MASK <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_SHIFT) |
> + (XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_MASK <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_SHIFT) |
> + (XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_MASK <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_SHIFT));
> + value |= (XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_VAL <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_SHIFT) |
> + (XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_VAL <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_SHIFT) |
> + (XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_VAL <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_SHIFT);
> +
> + if (usb3->context_saved) {
> + value &= ~((XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_MASK <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT) |
> + (XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_MASK <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT));
> + value |= (usb3->ctle_g <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT) |
> + (usb3->ctle_z <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT);
> + }
> +
> + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(index));
> +
> + value = XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_VAL;
> +
> + if (usb3->context_saved) {
> + value &= ~((XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_MASK <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT) |
> + (XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_MASK <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT));
> + value |= (usb3->tap1 <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT) |
> + (usb3->amp <<
> + XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT);
> + }
> +
> + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_USB3_PADX_CTL4(index));
> +
> + if (lane->pad == padctl->pcie)
> + offset = XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL2(lane->index);
> + else
> + offset = XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL2;
> +
> + value = padctl_readl(padctl, offset);
> + value &= ~(XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_MASK <<
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_SHIFT);
> + value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_VAL <<
> + XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_SHIFT;
> + padctl_writel(padctl, value, offset);
> +
> + if (lane->pad == padctl->pcie)
> + offset = XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL5(lane->index);
> + else
> + offset = XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL5;
> +
> + value = padctl_readl(padctl, offset);
> + value |= XUSB_PADCTL_IOPHY_MISC_PAD_CTL5_RX_QEYE_EN;
> + padctl_writel(padctl, value, offset);
> +
> + /* Enable SATA PHY when SATA lane is used */
> + if (lane->pad == padctl->sata) {
> + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
> + value &= ~(XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL0_REFCLK_NDIV_MASK <<
> + XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL0_REFCLK_NDIV_SHIFT);
> + value |= 0x2 <<
> + XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL0_REFCLK_NDIV_SHIFT;
> + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL1);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL2);
> + value &= ~((XUSB_PADCTL_IOPHY_PLL_S0_CTL2_XDIGCLK_SEL_MASK <<
> + XUSB_PADCTL_IOPHY_PLL_S0_CTL2_XDIGCLK_SEL_SHIFT) |
> + (XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL1_CP_CNTL_MASK <<
> + XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL1_CP_CNTL_SHIFT) |
> + (XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL0_CP_CNTL_MASK <<
> + XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL0_CP_CNTL_SHIFT) |
> + XUSB_PADCTL_IOPHY_PLL_S0_CTL2_TCLKOUT_EN);
> + value |= (0x7 <<
> + XUSB_PADCTL_IOPHY_PLL_S0_CTL2_XDIGCLK_SEL_SHIFT) |
> + (0x8 <<
> + XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL1_CP_CNTL_SHIFT) |
> + (0x8 <<
> + XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL0_CP_CNTL_SHIFT) |
> + XUSB_PADCTL_IOPHY_PLL_S0_CTL2_TXCLKREF_SEL;
> + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL2);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL3);
> + value &= ~XUSB_PADCTL_IOPHY_PLL_S0_CTL3_RCAL_BYPASS;
> + padctl_writel(padctl, value, XUSB_PADCTL_IOPHY_PLL_S0_CTL3);
> + }
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
> + value &= ~XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_VCORE_DOWN(index);
> + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
> +
> + usleep_range(100, 200);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
> + value &= ~XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN_EARLY(index);
> + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
> +
> + usleep_range(100, 200);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
> + value &= ~XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN(index);
> + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
> +
> + return ret;
> +}
> +
> +static void tegra124_usb3_port_disable(struct tegra_xusb_port *port)
> +{
> + struct tegra_xusb_padctl *padctl = port->padctl;
> + u32 value;
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
> + value |= XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN_EARLY(port->index);
> + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
> +
> + usleep_range(100, 200);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
> + value |= XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN(port->index);
> + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
> +
> + usleep_range(250, 350);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM);
> + value |= XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_VCORE_DOWN(port->index);
> + padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM);
> +
> + value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_MAP);
> + value &= ~XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(port->index);
> + value |= XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(port->index, 0x7);
> + padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_MAP);
> +}
> +
> +static const struct tegra_xusb_lane_map tegra124_usb3_map[] = {
> + { 0, "pcie", 0 },
> + { 1, "pcie", 1 },
> + { 1, "sata", 0 },
> + { 0, NULL, 0 },
> +};
> +
> +static struct tegra_xusb_lane *
> +tegra124_usb3_port_map(struct tegra_xusb_port *port)
> +{
> + return tegra_xusb_port_find_lane(port, tegra124_usb3_map, "usb3-ss");
> +}
> +
> +static const struct tegra_xusb_port_ops tegra124_usb3_port_ops = {
> + .enable = tegra124_usb3_port_enable,
> + .disable = tegra124_usb3_port_disable,
> + .map = tegra124_usb3_port_map,
> +};
> +
> +static int
> +tegra124_xusb_read_fuse_calibration(struct tegra124_xusb_fuse_calibration *fuse)
> +{
> + unsigned int i;
> + int err;
> + u32 value;
> +
> + err = tegra_fuse_readl(TEGRA_FUSE_SKU_CALIB_0, &value);
> + if (err < 0)
> + return err;
> +
> + for (i = 0; i < ARRAY_SIZE(fuse->hs_curr_level); i++) {
> + fuse->hs_curr_level[i] =
> + (value >> FUSE_SKU_CALIB_HS_CURR_LEVEL_PADX_SHIFT(i)) &
> + FUSE_SKU_CALIB_HS_CURR_LEVEL_PAD_MASK;
> + }
> + fuse->hs_iref_cap =
> + (value >> FUSE_SKU_CALIB_HS_IREF_CAP_SHIFT) &
> + FUSE_SKU_CALIB_HS_IREF_CAP_MASK;
> + fuse->hs_term_range_adj =
> + (value >> FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_SHIFT) &
> + FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_MASK;
> + fuse->hs_squelch_level =
> + (value >> FUSE_SKU_CALIB_HS_SQUELCH_LEVEL_SHIFT) &
> + FUSE_SKU_CALIB_HS_SQUELCH_LEVEL_MASK;
> +
> + return 0;
> +}
> +
> +static struct tegra_xusb_padctl *
> +tegra124_xusb_padctl_probe(struct device *dev,
> + const struct tegra_xusb_padctl_soc *soc)
> +{
> + struct tegra124_xusb_padctl *padctl;
> + int err;
> +
> + padctl = devm_kzalloc(dev, sizeof(*padctl), GFP_KERNEL);
> + if (!padctl)
> + return ERR_PTR(-ENOMEM);
> +
> + padctl->base.dev = dev;
> + padctl->base.soc = soc;
> +
> + err = tegra124_xusb_read_fuse_calibration(&padctl->fuse);
> + if (err < 0)
> + return ERR_PTR(err);
> +
> + return &padctl->base;
> +}
> +
> +static void tegra124_xusb_padctl_remove(struct tegra_xusb_padctl *padctl)
> +{
> +}
> +
> +static const struct tegra_xusb_padctl_ops tegra124_xusb_padctl_ops = {
> + .probe = tegra124_xusb_padctl_probe,
> + .remove = tegra124_xusb_padctl_remove,
> + .usb3_save_context = tegra124_usb3_save_context,
> + .hsic_set_idle = tegra124_hsic_set_idle,
> +};
> +
> +const struct tegra_xusb_padctl_soc tegra124_xusb_padctl_soc = {
> + .num_pads = ARRAY_SIZE(tegra124_pads),
> + .pads = tegra124_pads,
> + .ports = {
> + .usb2 = {
> + .ops = &tegra124_usb2_port_ops,
> + .count = 3,
> + },
> + .ulpi = {
> + .ops = &tegra124_ulpi_port_ops,
> + .count = 1,
> + },
> + .hsic = {
> + .ops = &tegra124_hsic_port_ops,
> + .count = 2,
> + },
> + .usb3 = {
> + .ops = &tegra124_usb3_port_ops,
> + .count = 2,
> + },
> + },
> + .ops = &tegra124_xusb_padctl_ops,
> +};
> +EXPORT_SYMBOL_GPL(tegra124_xusb_padctl_soc);
> +
> +MODULE_AUTHOR("Thierry Reding <[email protected]>");
> +MODULE_DESCRIPTION("NVIDIA Tegra 124 XUSB Pad Controller driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c
> new file mode 100644
> index 000000000000..809998f6ce85
> --- /dev/null
> +++ b/drivers/phy/tegra/xusb.c
> @@ -0,0 +1,1017 @@
> +/*
> + * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + * more details.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/mailbox_client.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/reset.h>
> +#include <linux/slab.h>
> +#include <linux/workqueue.h>
> +
> +#include <soc/tegra/fuse.h>
> +
> +#include "xusb.h"
> +
> +static struct phy *tegra_xusb_pad_of_xlate(struct device *dev,
> + struct of_phandle_args *args)
> +{
> + struct tegra_xusb_pad *pad = dev_get_drvdata(dev);
> + struct phy *phy = NULL;
> + unsigned int i;
> +
> + if (args->args_count != 0)
> + return ERR_PTR(-EINVAL);
> +
> + for (i = 0; i < pad->soc->num_lanes; i++) {
> + if (!pad->lanes[i])
> + continue;
> +
> + if (pad->lanes[i]->dev.of_node == args->np) {
> + phy = pad->lanes[i];
> + break;
> + }
> + }
> +
> + if (phy == NULL)
> + phy = ERR_PTR(-ENODEV);
> +
> + return phy;
> +}
> +
> +static const struct of_device_id tegra_xusb_padctl_of_match[] = {
> +#if defined(CONFIG_ARCH_TEGRA_124_SOC) || defined(CONFIG_ARCH_TEGRA_132_SOC)
> + {
> + .compatible = "nvidia,tegra124-xusb-padctl",
> + .data = &tegra124_xusb_padctl_soc,
> + },
> +#endif
> +#if defined(CONFIG_ARCH_TEGRA_210_SOC)
> + {
> + .compatible = "nvidia,tegra210-xusb-padctl",
> + .data = &tegra210_xusb_padctl_soc,
> + },
> +#endif
> + { }
> +};
> +MODULE_DEVICE_TABLE(of, tegra_xusb_padctl_of_match);
> +
> +static struct device_node *
> +tegra_xusb_find_pad_node(struct tegra_xusb_padctl *padctl, const char *name)
> +{
> + /*
> + * of_find_node_by_name() drops a reference, so make sure to grab one.
> + */
> + struct device_node *np = of_node_get(padctl->dev->of_node);
> +
> + np = of_find_node_by_name(np, "pads");
> + if (np)
> + np = of_find_node_by_name(np, name);
> +
> + return np;
> +}
> +
> +static struct device_node *
> +tegra_xusb_pad_find_phy_node(struct tegra_xusb_pad *pad, unsigned int index)
> +{
> + /*
> + * of_find_node_by_name() drops a reference, so make sure to grab one.
> + */
> + struct device_node *np = of_node_get(pad->dev.of_node);
> + char *name;
> +
> + name = kasprintf(GFP_KERNEL, "%s-%u", np->name, index);
> + if (!name) {
> + of_node_put(np);
> + return NULL;
> + }
> +
> + np = of_find_node_by_name(np, name);
> +
> + kfree(name);
> +
> + return np;
> +}
> +
> +int tegra_xusb_lane_lookup_function(struct tegra_xusb_lane *lane,
> + const char *function)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < lane->soc->num_funcs; i++)
> + if (strcmp(function, lane->soc->funcs[i]) == 0)
> + return i;
> +
> + return -EINVAL;
> +}
> +
> +int tegra_xusb_lane_parse_dt(struct tegra_xusb_lane *lane,
> + struct device_node *np)
> +{
> + struct device *dev = &lane->pad->dev;
> + const char *function;
> + int err;
> +
> + err = of_property_read_string(np, "nvidia,function", &function);
> + if (err < 0)
> + return err;
> +
> + err = tegra_xusb_lane_lookup_function(lane, function);
> + if (err < 0) {
> + dev_err(dev, "invalid function \"%s\" for lane \"%s\"\n",
> + function, np->name);
> + return err;
> + }
> +
> + lane->function = err;
> +
> + return 0;
> +}
> +
> +static void tegra_xusb_lane_destroy(struct phy *phy)
> +{
> + if (phy) {
> + struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
> +
> + lane->pad->ops->remove(lane);
> + phy_destroy(phy);
> + }
> +}
> +
> +static void tegra_xusb_pad_release(struct device *dev)
> +{
> + struct tegra_xusb_pad *pad = to_tegra_xusb_pad(dev);
> +
> + pad->soc->ops->remove(pad);
> +}
> +
> +static struct device_type tegra_xusb_pad_type = {
> + .release = tegra_xusb_pad_release,
> +};
> +
> +int tegra_xusb_pad_init(struct tegra_xusb_pad *pad,
> + struct tegra_xusb_padctl *padctl,
> + struct device_node *np)
> +{
> + int err;
> +
> + device_initialize(&pad->dev);
> + INIT_LIST_HEAD(&pad->list);
> + pad->dev.parent = padctl->dev;
> + pad->dev.type = &tegra_xusb_pad_type;
> + pad->dev.of_node = np;
> + pad->padctl = padctl;
> +
> + err = dev_set_name(&pad->dev, "%s", pad->soc->name);
> + if (err < 0)
> + goto unregister;
> +
> + err = device_add(&pad->dev);
> + if (err < 0)
> + goto unregister;
> +
> + return 0;
> +
> +unregister:
> + device_unregister(&pad->dev);
> + return err;
> +}
> +
> +int tegra_xusb_pad_register(struct tegra_xusb_pad *pad,
> + const struct phy_ops *ops)
> +{
> + struct phy *lane;
> + unsigned int i;
> + int err;
> +
> + pad->lanes = devm_kcalloc(&pad->dev, pad->soc->num_lanes, sizeof(lane),
> + GFP_KERNEL);
> + if (!pad->lanes)
> + return -ENOMEM;
> +
> + for (i = 0; i < pad->soc->num_lanes; i++) {
> + struct device_node *np = tegra_xusb_pad_find_phy_node(pad, i);
> + struct tegra_xusb_lane *lane;
> +
> + /* skip disabled lanes */
> + if (!np || !of_device_is_available(np))
> + continue;
> +
> + pad->lanes[i] = phy_create(&pad->dev, np, ops);
> + if (IS_ERR(pad->lanes[i])) {
> + err = PTR_ERR(pad->lanes[i]);
> + goto remove;
> + }
> +
> + lane = pad->ops->probe(pad, np, i);
> + if (IS_ERR(lane)) {
> + phy_destroy(pad->lanes[i]);
> + err = PTR_ERR(lane);
> + goto remove;
> + }
> +
> + list_add_tail(&lane->list, &pad->padctl->lanes);
> + phy_set_drvdata(pad->lanes[i], lane);
> + }
> +
> + pad->provider = of_phy_provider_register(&pad->dev,
> + tegra_xusb_pad_of_xlate);
> + if (IS_ERR(pad->provider)) {
> + err = PTR_ERR(pad->provider);
> + goto remove;
> + }
> +
> + return 0;
> +
> +remove:
> + while (i--)
> + tegra_xusb_lane_destroy(pad->lanes[i]);
> +
> + return err;
> +}
> +
> +void tegra_xusb_pad_unregister(struct tegra_xusb_pad *pad)
> +{
> + unsigned int i = pad->soc->num_lanes;
> +
> + of_phy_provider_unregister(pad->provider);
> +
> + while (i--)
> + tegra_xusb_lane_destroy(pad->lanes[i]);
> +
> + device_unregister(&pad->dev);
> +}
> +
> +static struct tegra_xusb_pad *
> +tegra_xusb_pad_create(struct tegra_xusb_padctl *padctl,
> + const struct tegra_xusb_pad_soc *soc)
> +{
> + struct tegra_xusb_pad *pad;
> + struct device_node *np;
> + int err;
> +
> + np = tegra_xusb_find_pad_node(padctl, soc->name);
> + if (!np || !of_device_is_available(np))
> + return NULL;
> +
> + pad = soc->ops->probe(padctl, soc, np);
> + if (IS_ERR(pad)) {
> + err = PTR_ERR(pad);
> + dev_err(padctl->dev, "failed to create pad %s: %d\n",
> + soc->name, err);
> + return ERR_PTR(err);
> + }
> +
> + /* XXX move this into ->probe() to avoid string comparison */
> + if (strcmp(soc->name, "pcie") == 0)
> + padctl->pcie = pad;
> +
> + if (strcmp(soc->name, "sata") == 0)
> + padctl->sata = pad;
> +
> + if (strcmp(soc->name, "usb2") == 0)
> + padctl->usb2 = pad;
> +
> + if (strcmp(soc->name, "ulpi") == 0)
> + padctl->ulpi = pad;
> +
> + if (strcmp(soc->name, "hsic") == 0)
> + padctl->hsic = pad;
> +
> + return pad;
> +}
> +
> +static void __tegra_xusb_remove_pads(struct tegra_xusb_padctl *padctl)
> +{
> + struct tegra_xusb_pad *pad, *tmp;
> +
> + list_for_each_entry_safe_reverse(pad, tmp, &padctl->pads, list) {
> + list_del(&pad->list);
> + tegra_xusb_pad_unregister(pad);
> + }
> +}
> +
> +static void tegra_xusb_remove_pads(struct tegra_xusb_padctl *padctl)
> +{
> + mutex_lock(&padctl->lock);
> + __tegra_xusb_remove_pads(padctl);
> + mutex_unlock(&padctl->lock);
> +}
> +
> +static void tegra_xusb_lane_program(struct tegra_xusb_lane *lane)
> +{
> + struct tegra_xusb_padctl *padctl = lane->pad->padctl;
> + const struct tegra_xusb_lane_soc *soc = lane->soc;
> + u32 value;
> +
> + /* choose function */
> + value = padctl_readl(padctl, soc->offset);
> + value &= ~(soc->mask << soc->shift);
> + value |= lane->function << soc->shift;
> + padctl_writel(padctl, value, soc->offset);
> +}
> +
> +static void tegra_xusb_pad_program(struct tegra_xusb_pad *pad)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < pad->soc->num_lanes; i++) {
> + struct tegra_xusb_lane *lane;
> +
> + if (pad->lanes[i]) {
> + lane = phy_get_drvdata(pad->lanes[i]);
> + tegra_xusb_lane_program(lane);
> + }
> + }
> +}
> +
> +static int tegra_xusb_setup_pads(struct tegra_xusb_padctl *padctl)
> +{
> + struct tegra_xusb_pad *pad;
> + unsigned int i;
> +
> + mutex_lock(&padctl->lock);
> +
> + for (i = 0; i < padctl->soc->num_pads; i++) {
> + const struct tegra_xusb_pad_soc *soc = padctl->soc->pads[i];
> + int err;
> +
> + pad = tegra_xusb_pad_create(padctl, soc);
> + if (IS_ERR(pad)) {
> + err = PTR_ERR(pad);
> + dev_err(padctl->dev, "failed to create pad %s: %d\n",
> + soc->name, err);
> + __tegra_xusb_remove_pads(padctl);
> + mutex_unlock(&padctl->lock);
> + return err;
> + }
> +
> + if (!pad)
> + continue;
> +
> + list_add_tail(&pad->list, &padctl->pads);
> + }
> +
> + list_for_each_entry(pad, &padctl->pads, list)
> + tegra_xusb_pad_program(pad);
> +
> + mutex_unlock(&padctl->lock);
> + return 0;
> +}
> +
> +static bool tegra_xusb_lane_check(struct tegra_xusb_lane *lane,
> + const char *function)
> +{
> + if (lane) {
> + const char *func = lane->soc->funcs[lane->function];
> +
> + return strcmp(function, func) == 0;
> + }
> +
> + return false;
> +}
> +
> +struct tegra_xusb_lane *tegra_xusb_find_lane(struct tegra_xusb_padctl *padctl,
> + const char *type,
> + unsigned int index)
> +{
> + struct tegra_xusb_lane *lane, *hit = NULL;
> + char *name;
> +
> + name = kasprintf(GFP_KERNEL, "%s-%u", type, index);
> + if (!name)
> + return NULL;
> +
> + list_for_each_entry(lane, &padctl->lanes, list) {
> + if (strcmp(lane->soc->name, name) == 0) {
> + hit = lane;
> + break;
> + }
> + }
> +
> + kfree(name);
> + return hit;
> +}
> +
> +struct tegra_xusb_lane *
> +tegra_xusb_port_find_lane(struct tegra_xusb_port *port,
> + const struct tegra_xusb_lane_map *map,
> + const char *function)
> +{
> + struct tegra_xusb_lane *lane, *match = NULL;
> +
> + for (map = map; map->type; map++) {
> + if (port->index != map->port)
> + continue;
> +
> + lane = tegra_xusb_find_lane(port->padctl, map->type,
> + map->index);
> + if (!tegra_xusb_lane_check(lane, function))
> + continue;
> +
> + if (match)
> + dev_err(&port->dev, "conflicting match: %s-%u / %s\n",
> + map->type, map->index, match->soc->name);
> + else
> + match = lane;
> + }
> +
> + return match;
> +}
> +
> +static struct device_node *
> +tegra_xusb_find_port_node(struct tegra_xusb_padctl *padctl, const char *type,
> + unsigned int index)
> +{
> + /*
> + * of_find_node_by_name() drops a reference, so make sure to grab one.
> + */
> + struct device_node *np = of_node_get(padctl->dev->of_node);
> +
> + np = of_find_node_by_name(np, "ports");
> + if (np) {
> + char *name;
> +
> + name = kasprintf(GFP_KERNEL, "%s-%u", type, index);
> + np = of_find_node_by_name(np, name);
> + kfree(name);
> + }
> +
> + return np;
> +}
> +
> +struct tegra_xusb_port *
> +tegra_xusb_find_port(struct tegra_xusb_padctl *padctl, const char *type,
> + unsigned int index)
> +{
> + struct tegra_xusb_port *port;
> + struct device_node *np;
> +
> + np = tegra_xusb_find_port_node(padctl, type, index);
> + if (!np)
> + return NULL;
> +
> + list_for_each_entry(port, &padctl->ports, list) {
> + if (np == port->dev.of_node) {
> + of_node_put(np);
> + return port;
> + }
> + }
> +
> + of_node_put(np);
> +
> + return NULL;
> +}
> +
> +struct tegra_xusb_usb2_port *
> +tegra_xusb_find_usb2_port(struct tegra_xusb_padctl *padctl, unsigned int index)
> +{
> + struct tegra_xusb_port *port;
> +
> + port = tegra_xusb_find_port(padctl, "usb2", index);
> + if (port)
> + return to_usb2_port(port);
> +
> + return NULL;
> +}
> +
> +struct tegra_xusb_usb3_port *
> +tegra_xusb_find_usb3_port(struct tegra_xusb_padctl *padctl, unsigned int index)
> +{
> + struct tegra_xusb_port *port;
> +
> + port = tegra_xusb_find_port(padctl, "usb3", index);
> + if (port)
> + return to_usb3_port(port);
> +
> + return NULL;
> +}
> +
> +static void tegra_xusb_port_release(struct device *dev)
> +{
> +}
> +
> +static struct device_type tegra_xusb_port_type = {
> + .release = tegra_xusb_port_release,
> +};
> +
> +static int tegra_xusb_port_init(struct tegra_xusb_port *port,
> + struct tegra_xusb_padctl *padctl,
> + struct device_node *np,
> + const char *name,
> + unsigned int index)
> +{
> + int err;
> +
> + INIT_LIST_HEAD(&port->list);
> + port->padctl = padctl;
> + port->index = index;
> +
> + device_initialize(&port->dev);
> + port->dev.type = &tegra_xusb_port_type;
> + port->dev.of_node = of_node_get(np);
> + port->dev.parent = padctl->dev;
> +
> + err = dev_set_name(&port->dev, "%s-%u", name, index);
> + if (err < 0)
> + goto unregister;
> +
> + err = device_add(&port->dev);
> + if (err < 0)
> + goto unregister;
> +
> + return 0;
> +
> +unregister:
> + device_unregister(&port->dev);
> + return err;
> +}
> +
> +static void tegra_xusb_port_unregister(struct tegra_xusb_port *port)
> +{
> + device_unregister(&port->dev);
> +}
> +
> +static int tegra_xusb_usb2_port_parse_dt(struct tegra_xusb_usb2_port *usb2)
> +{
> + struct tegra_xusb_port *port = &usb2->base;
> + struct device_node *np = port->dev.of_node;
> +
> + usb2->internal = of_property_read_bool(np, "nvidia,internal");
> +
> + usb2->supply = devm_regulator_get(&port->dev, "vbus");
> + if (IS_ERR(usb2->supply))
> + return PTR_ERR(usb2->supply);
> +
> + return 0;
> +}
> +
> +static int tegra_xusb_add_usb2_port(struct tegra_xusb_padctl *padctl,
> + unsigned int index)
> +{
> + struct tegra_xusb_usb2_port *usb2;
> + struct device_node *np;
> + int err = 0;
> +
> + /*
> + * USB2 ports don't require additional properties, but if the port is
> + * marked as disabled there is no reason to register it.
> + */
> + np = tegra_xusb_find_port_node(padctl, "usb2", index);
> + if (!np || !of_device_is_available(np))
> + goto out;
> +
> + usb2 = devm_kzalloc(padctl->dev, sizeof(*usb2), GFP_KERNEL);
> + if (!usb2) {
> + err = -ENOMEM;
> + goto out;
> + }
> +
> + err = tegra_xusb_port_init(&usb2->base, padctl, np, "usb2", index);
> + if (err < 0)
> + goto out;
> +
> + usb2->base.ops = padctl->soc->ports.usb2.ops;
> +
> + usb2->base.lane = usb2->base.ops->map(&usb2->base);
> + if (IS_ERR(usb2->base.lane)) {
> + err = PTR_ERR(usb2->base.lane);
> + goto out;
> + }
> +
> + err = tegra_xusb_usb2_port_parse_dt(usb2);
> + if (err < 0) {
> + tegra_xusb_port_unregister(&usb2->base);
> + goto out;
> + }
> +
> + list_add_tail(&usb2->base.list, &padctl->ports);
> +
> +out:
> + of_node_put(np);
> + return err;
> +}
> +
> +static int tegra_xusb_ulpi_port_parse_dt(struct tegra_xusb_ulpi_port *ulpi)
> +{
> + struct tegra_xusb_port *port = &ulpi->base;
> + struct device_node *np = port->dev.of_node;
> +
> + ulpi->internal = of_property_read_bool(np, "nvidia,internal");
> +
> + return 0;
> +}
> +
> +static int tegra_xusb_add_ulpi_port(struct tegra_xusb_padctl *padctl,
> + unsigned int index)
> +{
> + struct tegra_xusb_ulpi_port *ulpi;
> + struct device_node *np;
> + int err = 0;
> +
> + np = tegra_xusb_find_port_node(padctl, "ulpi", index);
> + if (!np || !of_device_is_available(np))
> + goto out;
> +
> + ulpi = devm_kzalloc(padctl->dev, sizeof(*ulpi), GFP_KERNEL);
> + if (!ulpi) {
> + err = -ENOMEM;
> + goto out;
> + }
> +
> + err = tegra_xusb_port_init(&ulpi->base, padctl, np, "ulpi", index);
> + if (err < 0)
> + goto out;
> +
> + ulpi->base.ops = padctl->soc->ports.ulpi.ops;
> +
> + ulpi->base.lane = ulpi->base.ops->map(&ulpi->base);
> + if (IS_ERR(ulpi->base.lane)) {
> + err = PTR_ERR(ulpi->base.lane);
> + goto out;
> + }
> +
> + err = tegra_xusb_ulpi_port_parse_dt(ulpi);
> + if (err < 0) {
> + tegra_xusb_port_unregister(&ulpi->base);
> + goto out;
> + }
> +
> + list_add_tail(&ulpi->base.list, &padctl->ports);
> +
> +out:
> + of_node_put(np);
> + return err;
> +}
> +
> +static int tegra_xusb_hsic_port_parse_dt(struct tegra_xusb_hsic_port *hsic)
> +{
> + /* XXX */
> + return 0;
> +}
> +
> +static int tegra_xusb_add_hsic_port(struct tegra_xusb_padctl *padctl,
> + unsigned int index)
> +{
> + struct tegra_xusb_hsic_port *hsic;
> + struct device_node *np;
> + int err = 0;
> +
> + np = tegra_xusb_find_port_node(padctl, "hsic", index);
> + if (!np || !of_device_is_available(np))
> + goto out;
> +
> + hsic = devm_kzalloc(padctl->dev, sizeof(*hsic), GFP_KERNEL);
> + if (!hsic) {
> + err = -ENOMEM;
> + goto out;
> + }
> +
> + err = tegra_xusb_port_init(&hsic->base, padctl, np, "hsic", index);
> + if (err < 0)
> + goto out;
> +
> + hsic->base.ops = padctl->soc->ports.hsic.ops;
> +
> + hsic->base.lane = hsic->base.ops->map(&hsic->base);
> + if (IS_ERR(hsic->base.lane)) {
> + err = PTR_ERR(hsic->base.lane);
> + goto out;
> + }
> +
> + err = tegra_xusb_hsic_port_parse_dt(hsic);
> + if (err < 0) {
> + tegra_xusb_port_unregister(&hsic->base);
> + goto out;
> + }
> +
> + list_add_tail(&hsic->base.list, &padctl->ports);
> +
> +out:
> + of_node_put(np);
> + return err;
> +}
> +
> +static int tegra_xusb_usb3_port_parse_dt(struct tegra_xusb_usb3_port *usb3)
> +{
> + struct tegra_xusb_port *port = &usb3->base;
> + struct device_node *np = port->dev.of_node;
> + u32 value;
> + int err;
> +
> + err = of_property_read_u32(np, "nvidia,usb2-companion", &value);
> + if (err < 0) {
> + dev_err(&port->dev, "failed to read port: %d\n", err);
> + return err;
> + }
> +
> + usb3->port = value;
> +
> + usb3->internal = of_property_read_bool(np, "nvidia,internal");
> +
> + usb3->supply = devm_regulator_get(&port->dev, "vbus");
> + if (IS_ERR(usb3->supply))
> + return PTR_ERR(usb3->supply);
> +
> + return 0;
> +}
> +
> +static int tegra_xusb_add_usb3_port(struct tegra_xusb_padctl *padctl,
> + unsigned int index)
> +{
> + struct tegra_xusb_usb3_port *usb3;
> + struct device_node *np;
> + int err = 0;
> +
> + /*
> + * If there is no supplemental configuration in the device tree the
> + * port is unusable. But it is valid to configure only a single port,
> + * hence return 0 instead of an error to allow ports to be optional.
> + */
> + np = tegra_xusb_find_port_node(padctl, "usb3", index);
> + if (!np || !of_device_is_available(np))
> + goto out;
> +
> + usb3 = devm_kzalloc(padctl->dev, sizeof(*usb3), GFP_KERNEL);
> + if (!usb3) {
> + err = -ENOMEM;
> + goto out;
> + }
> +
> + err = tegra_xusb_port_init(&usb3->base, padctl, np, "usb3", index);
> + if (err < 0)
> + goto out;
> +
> + usb3->base.ops = padctl->soc->ports.usb3.ops;
> +
> + usb3->base.lane = usb3->base.ops->map(&usb3->base);
> + if (IS_ERR(usb3->base.lane)) {
> + err = PTR_ERR(usb3->base.lane);
> + goto out;
> + }
> +
> + err = tegra_xusb_usb3_port_parse_dt(usb3);
> + if (err < 0) {
> + tegra_xusb_port_unregister(&usb3->base);
> + goto out;
> + }
> +
> + list_add_tail(&usb3->base.list, &padctl->ports);
> +
> +out:
> + of_node_put(np);
> + return err;
> +}
> +
> +static void __tegra_xusb_remove_ports(struct tegra_xusb_padctl *padctl)
> +{
> + struct tegra_xusb_port *port, *tmp;
> +
> + list_for_each_entry_safe_reverse(port, tmp, &padctl->ports, list) {
> + list_del(&port->list);
> + tegra_xusb_port_unregister(port);
> + }
> +}
> +
> +static int tegra_xusb_setup_ports(struct tegra_xusb_padctl *padctl)
> +{
> + struct tegra_xusb_port *port;
> + unsigned int i;
> + int err = 0;
> +
> + mutex_lock(&padctl->lock);
> +
> + for (i = 0; i < padctl->soc->ports.usb2.count; i++) {
> + err = tegra_xusb_add_usb2_port(padctl, i);
> + if (err < 0)
> + goto remove_ports;
> + }
> +
> + for (i = 0; i < padctl->soc->ports.ulpi.count; i++) {
> + err = tegra_xusb_add_ulpi_port(padctl, i);
> + if (err < 0)
> + goto remove_ports;
> + }
> +
> + for (i = 0; i < padctl->soc->ports.hsic.count; i++) {
> + err = tegra_xusb_add_hsic_port(padctl, i);
> + if (err < 0)
> + goto remove_ports;
> + }
> +
> + for (i = 0; i < padctl->soc->ports.usb3.count; i++) {
> + err = tegra_xusb_add_usb3_port(padctl, i);
> + if (err < 0)
> + goto remove_ports;
> + }
> +
> + list_for_each_entry(port, &padctl->ports, list) {
> + err = port->ops->enable(port);
> + if (err < 0)
> + dev_err(padctl->dev, "failed to enable port %s: %d\n",
> + dev_name(&port->dev), err);
> + }
> +
> + goto unlock;
> +
> +remove_ports:
> + __tegra_xusb_remove_ports(padctl);
> +unlock:
> + mutex_unlock(&padctl->lock);
> + return err;
> +}
> +
> +static void tegra_xusb_remove_ports(struct tegra_xusb_padctl *padctl)
> +{
> + mutex_lock(&padctl->lock);
> + __tegra_xusb_remove_ports(padctl);
> + mutex_unlock(&padctl->lock);
> +}
> +
> +static int tegra_xusb_padctl_probe(struct platform_device *pdev)
> +{
> + struct device_node *np = of_node_get(pdev->dev.of_node);
> + const struct tegra_xusb_padctl_soc *soc;
> + struct tegra_xusb_padctl *padctl;
> + const struct of_device_id *match;
> + struct resource *res;
> + int err;
> +
> + /* for backwards compatibility with old device trees */
> + np = of_find_node_by_name(np, "pads");
> + if (!np) {
> + dev_warn(&pdev->dev, "deprecated DT, using legacy driver\n");
> + return tegra_xusb_padctl_legacy_probe(pdev);
> + }
> +
> + of_node_put(np);
> +
> + match = of_match_node(tegra_xusb_padctl_of_match, pdev->dev.of_node);
> + soc = match->data;
> +
> + padctl = soc->ops->probe(&pdev->dev, soc);
> + if (IS_ERR(padctl))
> + return PTR_ERR(padctl);
> +
> + platform_set_drvdata(pdev, padctl);
> + INIT_LIST_HEAD(&padctl->ports);
> + INIT_LIST_HEAD(&padctl->lanes);
> + INIT_LIST_HEAD(&padctl->pads);
> + mutex_init(&padctl->lock);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + padctl->regs = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(padctl->regs)) {
> + err = PTR_ERR(padctl->regs);
> + goto remove;
> + }
> +
> + padctl->rst = devm_reset_control_get(&pdev->dev, NULL);
> + if (IS_ERR(padctl->rst)) {
> + err = PTR_ERR(padctl->rst);
> + goto remove;
> + }
> +
> + err = reset_control_deassert(padctl->rst);
> + if (err < 0)
> + goto remove;
> +
> + err = tegra_xusb_setup_pads(padctl);
> + if (err < 0) {
> + dev_err(&pdev->dev, "failed to setup pads: %d\n", err);
> + goto reset;
> + }
> +
> + err = tegra_xusb_setup_ports(padctl);
> + if (err) {
> + dev_err(&pdev->dev, "failed to setup XUSB ports: %d\n", err);
> + goto remove_pads;
> + }
> +
> + return 0;
> +
> +remove_pads:
> + tegra_xusb_remove_pads(padctl);
> +reset:
> + reset_control_assert(padctl->rst);
> +remove:
> + soc->ops->remove(padctl);
> + return err;
> +}
> +
> +static int tegra_xusb_padctl_remove(struct platform_device *pdev)
> +{
> + struct tegra_xusb_padctl *padctl = platform_get_drvdata(pdev);
> + int err;
> +
> + tegra_xusb_remove_ports(padctl);
> + tegra_xusb_remove_pads(padctl);
> +
> + err = reset_control_assert(padctl->rst);
> + if (err < 0)
> + dev_err(&pdev->dev, "failed to assert reset: %d\n", err);
> +
> + padctl->soc->ops->remove(padctl);
> +
> + return err;
> +}
> +
> +static struct platform_driver tegra_xusb_padctl_driver = {
> + .driver = {
> + .name = "tegra-xusb-padctl",
> + .of_match_table = tegra_xusb_padctl_of_match,
> + },
> + .probe = tegra_xusb_padctl_probe,
> + .remove = tegra_xusb_padctl_remove,
> +};
> +module_platform_driver(tegra_xusb_padctl_driver);
> +
> +struct tegra_xusb_padctl *tegra_xusb_padctl_get(struct device *dev)
> +{
> + struct tegra_xusb_padctl *padctl;
> + struct platform_device *pdev;
> + struct device_node *np;
> +
> + np = of_parse_phandle(dev->of_node, "nvidia,xusb-padctl", 0);
> + if (!np)
> + return ERR_PTR(-EINVAL);
> +
> + /*
> + * This is slightly ugly. A better implementation would be to keep a
> + * registry of pad controllers, but since there will almost certainly
> + * only ever be one per SoC that would be a little overkill.
> + */
> + pdev = of_find_device_by_node(np);
> + if (!pdev) {
> + of_node_put(np);
> + return ERR_PTR(-ENODEV);
> + }
> +
> + of_node_put(np);
> +
> + padctl = platform_get_drvdata(pdev);
> + if (!padctl) {
> + put_device(&pdev->dev);
> + return ERR_PTR(-EPROBE_DEFER);
> + }
> +
> + return padctl;
> +}
> +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_get);
> +
> +void tegra_xusb_padctl_put(struct tegra_xusb_padctl *padctl)
> +{
> + if (padctl)
> + put_device(padctl->dev);
> +}
> +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_put);
> +
> +int tegra_xusb_padctl_usb3_save_context(struct tegra_xusb_padctl *padctl,
> + unsigned int port)
> +{
> + if (padctl->soc->ops->usb3_save_context)
> + return padctl->soc->ops->usb3_save_context(padctl, port);
> +
> + return -ENOSYS;
> +}
> +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_usb3_save_context);
> +
> +int tegra_xusb_padctl_hsic_set_idle(struct tegra_xusb_padctl *padctl,
> + unsigned int port, bool idle)
> +{
> + if (padctl->soc->ops->hsic_set_idle)
> + return padctl->soc->ops->hsic_set_idle(padctl, port, idle);
> +
> + return -ENOSYS;
> +}
> +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_hsic_set_idle);
> +
> +int tegra_xusb_padctl_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl,
> + unsigned int port, bool enable)
> +{
> + if (padctl->soc->ops->usb3_set_lfps_detect)
> + return padctl->soc->ops->usb3_set_lfps_detect(padctl, port,
> + enable);
> +
> + return -ENOSYS;
> +}
> +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_usb3_set_lfps_detect);
> +
> +MODULE_AUTHOR("Thierry Reding <[email protected]>");
> +MODULE_DESCRIPTION("Tegra XUSB Pad Controller driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/phy/tegra/xusb.h b/drivers/phy/tegra/xusb.h
> new file mode 100644
> index 000000000000..b49dbc36efa3
> --- /dev/null
> +++ b/drivers/phy/tegra/xusb.h
> @@ -0,0 +1,421 @@
> +/*
> + * Copyright (c) 2014-2015, NVIDIA CORPORATION. All rights reserved.
> + * Copyright (c) 2015, Google Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + * more details.
> + */
> +
> +#ifndef __PHY_TEGRA_XUSB_H
> +#define __PHY_TEGRA_XUSB_H
> +
> +#include <linux/io.h>
> +#include <linux/mutex.h>
> +#include <linux/workqueue.h>
> +
> +/* legacy entry points for backwards-compatibility */
> +int tegra_xusb_padctl_legacy_probe(struct platform_device *pdev);
> +int tegra_xusb_padctl_legacy_remove(struct platform_device *pdev);
> +
> +struct phy;
> +struct phy_provider;
> +struct platform_device;
> +struct regulator;
> +
> +/*
> + * lanes
> + */
> +struct tegra_xusb_lane_soc {
> + const char *name;
> +
> + unsigned int offset;
> + unsigned int shift;
> + unsigned int mask;
> +
> + const char * const *funcs;
> + unsigned int num_funcs;
> +};
> +
> +struct tegra_xusb_lane {
> + const struct tegra_xusb_lane_soc *soc;
> + struct tegra_xusb_pad *pad;
> + struct device_node *np;
> + struct list_head list;
> + unsigned int function;
> + unsigned int index;
> +};
> +
> +int tegra_xusb_lane_parse_dt(struct tegra_xusb_lane *lane,
> + struct device_node *np);
> +
> +struct tegra_xusb_usb2_lane {
> + struct tegra_xusb_lane base;
> +
> + u32 hs_curr_level_offset;
> +};
> +
> +static inline struct tegra_xusb_usb2_lane *
> +to_usb2_lane(struct tegra_xusb_lane *lane)
> +{
> + return container_of(lane, struct tegra_xusb_usb2_lane, base);
> +}
> +
> +struct tegra_xusb_ulpi_lane {
> + struct tegra_xusb_lane base;
> +};
> +
> +static inline struct tegra_xusb_ulpi_lane *
> +to_ulpi_lane(struct tegra_xusb_lane *lane)
> +{
> + return container_of(lane, struct tegra_xusb_ulpi_lane, base);
> +}
> +
> +struct tegra_xusb_hsic_lane {
> + struct tegra_xusb_lane base;
> +
> + u32 strobe_trim;
> + u32 rx_strobe_trim;
> + u32 rx_data_trim;
> + u32 tx_rtune_n;
> + u32 tx_rtune_p;
> + u32 tx_rslew_n;
> + u32 tx_rslew_p;
> + bool auto_term;
> +};
> +
> +static inline struct tegra_xusb_hsic_lane *
> +to_hsic_lane(struct tegra_xusb_lane *lane)
> +{
> + return container_of(lane, struct tegra_xusb_hsic_lane, base);
> +}
> +
> +struct tegra_xusb_pcie_lane {
> + struct tegra_xusb_lane base;
> +};
> +
> +static inline struct tegra_xusb_pcie_lane *
> +to_pcie_lane(struct tegra_xusb_lane *lane)
> +{
> + return container_of(lane, struct tegra_xusb_pcie_lane, base);
> +}
> +
> +struct tegra_xusb_sata_lane {
> + struct tegra_xusb_lane base;
> +};
> +
> +static inline struct tegra_xusb_sata_lane *
> +to_sata_lane(struct tegra_xusb_lane *lane)
> +{
> + return container_of(lane, struct tegra_xusb_sata_lane, base);
> +}
> +
> +struct tegra_xusb_lane_ops {
> + struct tegra_xusb_lane *(*probe)(struct tegra_xusb_pad *pad,
> + struct device_node *np,
> + unsigned int index);
> + void (*remove)(struct tegra_xusb_lane *lane);
> +};
> +
> +/*
> + * pads
> + */
> +struct tegra_xusb_pad_soc;
> +struct tegra_xusb_padctl;
> +
> +struct tegra_xusb_pad_ops {
> + struct tegra_xusb_pad *(*probe)(struct tegra_xusb_padctl *padctl,
> + const struct tegra_xusb_pad_soc *soc,
> + struct device_node *np);
> + void (*remove)(struct tegra_xusb_pad *pad);
> +};
> +
> +struct tegra_xusb_pad_soc {
> + const char *name;
> +
> + const struct tegra_xusb_lane_soc *lanes;
> + unsigned int num_lanes;
> +
> + const struct tegra_xusb_pad_ops *ops;
> +};
> +
> +struct tegra_xusb_pad {
> + const struct tegra_xusb_pad_soc *soc;
> + struct tegra_xusb_padctl *padctl;
> + struct phy_provider *provider;
> + struct phy **lanes;
> + struct device dev;
> +
> + const struct tegra_xusb_lane_ops *ops;
> +
> + struct list_head list;
> +};
> +
> +static inline struct tegra_xusb_pad *to_tegra_xusb_pad(struct device *dev)
> +{
> + return container_of(dev, struct tegra_xusb_pad, dev);
> +}
> +
> +int tegra_xusb_pad_init(struct tegra_xusb_pad *pad,
> + struct tegra_xusb_padctl *padctl,
> + struct device_node *np);
> +int tegra_xusb_pad_register(struct tegra_xusb_pad *pad,
> + const struct phy_ops *ops);
> +void tegra_xusb_pad_unregister(struct tegra_xusb_pad *pad);
> +
> +struct tegra_xusb_usb2_pad {
> + struct tegra_xusb_pad base;
> +
> + struct clk *clk;
> + unsigned int enable;
> + struct mutex lock;
> +};
> +
> +static inline struct tegra_xusb_usb2_pad *
> +to_usb2_pad(struct tegra_xusb_pad *pad)
> +{
> + return container_of(pad, struct tegra_xusb_usb2_pad, base);
> +}
> +
> +struct tegra_xusb_ulpi_pad {
> + struct tegra_xusb_pad base;
> +};
> +
> +static inline struct tegra_xusb_ulpi_pad *
> +to_ulpi_pad(struct tegra_xusb_pad *pad)
> +{
> + return container_of(pad, struct tegra_xusb_ulpi_pad, base);
> +}
> +
> +struct tegra_xusb_hsic_pad {
> + struct tegra_xusb_pad base;
> +
> + struct regulator *supply;
> + struct clk *clk;
> +};
> +
> +static inline struct tegra_xusb_hsic_pad *
> +to_hsic_pad(struct tegra_xusb_pad *pad)
> +{
> + return container_of(pad, struct tegra_xusb_hsic_pad, base);
> +}
> +
> +struct tegra_xusb_pcie_pad {
> + struct tegra_xusb_pad base;
> +
> + struct reset_control *rst;
> + struct clk *pll;
> +
> + unsigned int enable;
> +};
> +
> +static inline struct tegra_xusb_pcie_pad *
> +to_pcie_pad(struct tegra_xusb_pad *pad)
> +{
> + return container_of(pad, struct tegra_xusb_pcie_pad, base);
> +}
> +
> +struct tegra_xusb_sata_pad {
> + struct tegra_xusb_pad base;
> +
> + struct reset_control *rst;
> + struct clk *pll;
> +
> + unsigned int enable;
> +};
> +
> +static inline struct tegra_xusb_sata_pad *
> +to_sata_pad(struct tegra_xusb_pad *pad)
> +{
> + return container_of(pad, struct tegra_xusb_sata_pad, base);
> +}
> +
> +/*
> + * ports
> + */
> +struct tegra_xusb_port_ops;
> +
> +struct tegra_xusb_port {
> + struct tegra_xusb_padctl *padctl;
> + struct tegra_xusb_lane *lane;
> + unsigned int index;
> +
> + struct list_head list;
> + struct device dev;
> +
> + const struct tegra_xusb_port_ops *ops;
> +};
> +
> +struct tegra_xusb_lane_map {
> + unsigned int port;
> + const char *type;
> + unsigned int index;
> + const char *func;
> +};
> +
> +struct tegra_xusb_lane *
> +tegra_xusb_port_find_lane(struct tegra_xusb_port *port,
> + const struct tegra_xusb_lane_map *map,
> + const char *function);
> +
> +struct tegra_xusb_port *
> +tegra_xusb_find_port(struct tegra_xusb_padctl *padctl, const char *type,
> + unsigned int index);
> +
> +struct tegra_xusb_usb2_port {
> + struct tegra_xusb_port base;
> +
> + struct regulator *supply;
> + bool internal;
> +};
> +
> +static inline struct tegra_xusb_usb2_port *
> +to_usb2_port(struct tegra_xusb_port *port)
> +{
> + return container_of(port, struct tegra_xusb_usb2_port, base);
> +}
> +
> +struct tegra_xusb_usb2_port *
> +tegra_xusb_find_usb2_port(struct tegra_xusb_padctl *padctl,
> + unsigned int index);
> +
> +struct tegra_xusb_ulpi_port {
> + struct tegra_xusb_port base;
> +
> + struct regulator *supply;
> + bool internal;
> +};
> +
> +static inline struct tegra_xusb_ulpi_port *
> +to_ulpi_port(struct tegra_xusb_port *port)
> +{
> + return container_of(port, struct tegra_xusb_ulpi_port, base);
> +}
> +
> +struct tegra_xusb_hsic_port {
> + struct tegra_xusb_port base;
> +};
> +
> +static inline struct tegra_xusb_hsic_port *
> +to_hsic_port(struct tegra_xusb_port *port)
> +{
> + return container_of(port, struct tegra_xusb_hsic_port, base);
> +}
> +
> +struct tegra_xusb_usb3_port {
> + struct tegra_xusb_port base;
> + struct regulator *supply;
> + bool context_saved;
> + unsigned int port;
> + bool internal;
> +
> + u32 tap1;
> + u32 amp;
> + u32 ctle_z;
> + u32 ctle_g;
> +};
> +
> +static inline struct tegra_xusb_usb3_port *
> +to_usb3_port(struct tegra_xusb_port *port)
> +{
> + return container_of(port, struct tegra_xusb_usb3_port, base);
> +}
> +
> +struct tegra_xusb_usb3_port *
> +tegra_xusb_find_usb3_port(struct tegra_xusb_padctl *padctl,
> + unsigned int index);
> +
> +struct tegra_xusb_port_ops {
> + int (*enable)(struct tegra_xusb_port *port);
> + void (*disable)(struct tegra_xusb_port *port);
> + struct tegra_xusb_lane *(*map)(struct tegra_xusb_port *port);
> +};
> +
> +/*
> + * pad controller
> + */
> +struct tegra_xusb_padctl_soc;
> +
> +struct tegra_xusb_padctl_ops {
> + struct tegra_xusb_padctl *
> + (*probe)(struct device *dev,
> + const struct tegra_xusb_padctl_soc *soc);
> + void (*remove)(struct tegra_xusb_padctl *padctl);
> +
> + int (*usb3_save_context)(struct tegra_xusb_padctl *padctl,
> + unsigned int index);
> + int (*hsic_set_idle)(struct tegra_xusb_padctl *padctl,
> + unsigned int index, bool idle);
> + int (*usb3_set_lfps_detect)(struct tegra_xusb_padctl *padctl,
> + unsigned int index, bool enable);
> +};
> +
> +struct tegra_xusb_padctl_soc {
> + const struct tegra_xusb_pad_soc * const *pads;
> + unsigned int num_pads;
> +
> + struct {
> + struct {
> + const struct tegra_xusb_port_ops *ops;
> + unsigned int count;
> + } usb2, ulpi, hsic, usb3;
> + } ports;
> +
> + const struct tegra_xusb_padctl_ops *ops;
> +};
> +
> +struct tegra_xusb_padctl {
> + struct device *dev;
> + void __iomem *regs;
> + struct mutex lock;
> + struct reset_control *rst;
> +
> + const struct tegra_xusb_padctl_soc *soc;
> +
> + struct tegra_xusb_pad *pcie;
> + struct tegra_xusb_pad *sata;
> + struct tegra_xusb_pad *ulpi;
> + struct tegra_xusb_pad *usb2;
> + struct tegra_xusb_pad *hsic;
> +
> + struct list_head ports;
> + struct list_head lanes;
> + struct list_head pads;
> +
> + unsigned int enable;
> +
> + struct clk *clk;
> +};
> +
> +static inline void padctl_writel(struct tegra_xusb_padctl *padctl, u32 value,
> + unsigned long offset)
> +{
> + dev_dbg(padctl->dev, "%08lx < %08x\n", offset, value);
> + writel(value, padctl->regs + offset);
> +}
> +
> +static inline u32 padctl_readl(struct tegra_xusb_padctl *padctl,
> + unsigned long offset)
> +{
> + u32 value = readl(padctl->regs + offset);
> + dev_dbg(padctl->dev, "%08lx > %08x\n", offset, value);
> + return value;
> +}
> +
> +struct tegra_xusb_lane *tegra_xusb_find_lane(struct tegra_xusb_padctl *padctl,
> + const char *name,
> + unsigned int index);
> +
> +#if defined(CONFIG_ARCH_TEGRA_124_SOC) || defined(CONFIG_ARCH_TEGRA_132_SOC)
> +extern const struct tegra_xusb_padctl_soc tegra124_xusb_padctl_soc;
> +#endif
> +#if defined(CONFIG_ARCH_TEGRA_210_SOC)
> +extern const struct tegra_xusb_padctl_soc tegra210_xusb_padctl_soc;
> +#endif
> +
> +#endif /* __PHY_TEGRA_XUSB_H */
> diff --git a/drivers/pinctrl/tegra/pinctrl-tegra-xusb.c b/drivers/pinctrl/tegra/pinctrl-tegra-xusb.c
> index 2f06029c9405..946cda3fee35 100644
> --- a/drivers/pinctrl/tegra/pinctrl-tegra-xusb.c
> +++ b/drivers/pinctrl/tegra/pinctrl-tegra-xusb.c
> @@ -873,7 +873,7 @@ static const struct of_device_id tegra_xusb_padctl_of_match[] = {
> };
> MODULE_DEVICE_TABLE(of, tegra_xusb_padctl_of_match);
>
> -static int tegra_xusb_padctl_probe(struct platform_device *pdev)
> +int tegra_xusb_padctl_legacy_probe(struct platform_device *pdev)
> {
> struct tegra_xusb_padctl *padctl;
> const struct of_device_id *match;
> @@ -955,8 +955,9 @@ reset:
> reset_control_assert(padctl->rst);
> return err;
> }
> +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_legacy_probe);
>
> -static int tegra_xusb_padctl_remove(struct platform_device *pdev)
> +int tegra_xusb_padctl_legacy_remove(struct platform_device *pdev)
> {
> struct tegra_xusb_padctl *padctl = platform_get_drvdata(pdev);
> int err;
> @@ -969,17 +970,4 @@ static int tegra_xusb_padctl_remove(struct platform_device *pdev)
>
> return err;
> }
> -
> -static struct platform_driver tegra_xusb_padctl_driver = {
> - .driver = {
> - .name = "tegra-xusb-padctl",
> - .of_match_table = tegra_xusb_padctl_of_match,
> - },
> - .probe = tegra_xusb_padctl_probe,
> - .remove = tegra_xusb_padctl_remove,
> -};
> -module_platform_driver(tegra_xusb_padctl_driver);
> -
> -MODULE_AUTHOR("Thierry Reding <[email protected]>");
> -MODULE_DESCRIPTION("Tegra 124 XUSB Pad Control driver");
> -MODULE_LICENSE("GPL v2");
> +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_legacy_remove);
> diff --git a/include/linux/phy/tegra/xusb.h b/include/linux/phy/tegra/xusb.h
> new file mode 100644
> index 000000000000..8e1a57a78d9f
> --- /dev/null
> +++ b/include/linux/phy/tegra/xusb.h
> @@ -0,0 +1,30 @@
> +/*
> + * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + * more details.
> + */
> +
> +#ifndef PHY_TEGRA_XUSB_H
> +#define PHY_TEGRA_XUSB_H
> +
> +struct tegra_xusb_padctl;
> +struct device;
> +
> +struct tegra_xusb_padctl *tegra_xusb_padctl_get(struct device *dev);
> +void tegra_xusb_padctl_put(struct tegra_xusb_padctl *padctl);
> +
> +int tegra_xusb_padctl_usb3_save_context(struct tegra_xusb_padctl *padctl,
> + unsigned int port);
> +int tegra_xusb_padctl_hsic_set_idle(struct tegra_xusb_padctl *padctl,
> + unsigned int port, bool idle);
> +int tegra_xusb_padctl_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl,
> + unsigned int port, bool enable);
> +
> +#endif /* PHY_TEGRA_XUSB_H */
> --
> 2.7.1
>


Attachments:
(No filename) (100.13 kB)
signature.asc (819.00 B)
Download all attachments

2016-04-05 13:18:00

by Thierry Reding

[permalink] [raw]
Subject: Re: [PATCH v10 8/9] usb: xhci: Add NVIDIA Tegra XUSB controller driver

Hi Mathias, Greg,

Due to the various dependencies within the series, I'd prefer this to go
via the Tegra tree. Would you be okay with providing your Acked-by?

Thanks,
Thierry

On Fri, Mar 04, 2016 at 05:19:38PM +0100, Thierry Reding wrote:
> From: Thierry Reding <[email protected]>
>
> Add support for the on-chip XUSB controller present on Tegra SoCs. This
> controller, when loaded with external firmware, exposes an interface
> compliant with xHCI. This driver loads the firmware, starts the
> controller, and is able to service host-specific messages sent by the
> controller's firmware.
>
> The controller also supports USB device mode as well as powergating
> of the SuperSpeed and host-controller logic when not in use, but
> support for these is not yet implemented.
>
> Based on work by:
> Ajay Gupta <[email protected]>
> Bharath Yadav <[email protected]>
> Andrew Bresticker <[email protected]>
>
> Cc: Greg Kroah-Hartman <[email protected]>
> Cc: Mathias Nyman <[email protected]>
> Signed-off-by: Thierry Reding <[email protected]>
> ---
> drivers/usb/host/Kconfig | 9 +
> drivers/usb/host/Makefile | 1 +
> drivers/usb/host/xhci-tegra.c | 1332 +++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 1342 insertions(+)
> create mode 100644 drivers/usb/host/xhci-tegra.c
>
> diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
> index 8c20ebbc049c..191fdeb8b841 100644
> --- a/drivers/usb/host/Kconfig
> +++ b/drivers/usb/host/Kconfig
> @@ -69,6 +69,15 @@ config USB_XHCI_RCAR
> Say 'Y' to enable the support for the xHCI host controller
> found in Renesas R-Car ARM SoCs.
>
> +config USB_XHCI_TEGRA
> + tristate "xHCI support for NVIDIA Tegra SoCs"
> + depends on PHY_TEGRA_XUSB
> + depends on RESET_CONTROLLER
> + select FW_LOADER
> + ---help---
> + Say 'Y' to enable the support for the xHCI host controller
> + found in NVIDIA Tegra124 and later SoCs.
> +
> endif # USB_XHCI_HCD
>
> config USB_EHCI_HCD
> diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
> index a9ddd3c9ec94..6ef785b0ea8f 100644
> --- a/drivers/usb/host/Makefile
> +++ b/drivers/usb/host/Makefile
> @@ -68,6 +68,7 @@ obj-$(CONFIG_USB_XHCI_HCD) += xhci-hcd.o
> obj-$(CONFIG_USB_XHCI_PCI) += xhci-pci.o
> obj-$(CONFIG_USB_XHCI_PLATFORM) += xhci-plat-hcd.o
> obj-$(CONFIG_USB_XHCI_MTK) += xhci-mtk.o
> +obj-$(CONFIG_USB_XHCI_TEGRA) += xhci-tegra.o
> obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o
> obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o
> obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o
> diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c
> new file mode 100644
> index 000000000000..ab6f9856c5c4
> --- /dev/null
> +++ b/drivers/usb/host/xhci-tegra.c
> @@ -0,0 +1,1332 @@
> +/*
> + * NVIDIA Tegra xHCI host controller driver
> + *
> + * Copyright (C) 2014 NVIDIA Corporation
> + * Copyright (C) 2014 Google, Inc.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/firmware.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of_device.h>
> +#include <linux/phy/phy.h>
> +#include <linux/phy/tegra/xusb.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/reset.h>
> +#include <linux/slab.h>
> +
> +#include "xhci.h"
> +
> +#define TEGRA_XHCI_SS_HIGH_SPEED 120000000
> +#define TEGRA_XHCI_SS_LOW_SPEED 12000000
> +
> +/* FPCI CFG registers */
> +#define XUSB_CFG_1 0x004
> +#define XUSB_IO_SPACE_EN BIT(0)
> +#define XUSB_MEM_SPACE_EN BIT(1)
> +#define XUSB_BUS_MASTER_EN BIT(2)
> +#define XUSB_CFG_4 0x010
> +#define XUSB_BASE_ADDR_SHIFT 15
> +#define XUSB_BASE_ADDR_MASK 0x1ffff
> +#define XUSB_CFG_ARU_C11_CSBRANGE 0x41c
> +#define XUSB_CFG_CSB_BASE_ADDR 0x800
> +
> +/* FPCI mailbox registers */
> +#define XUSB_CFG_ARU_MBOX_CMD 0x0e4
> +#define MBOX_DEST_FALC BIT(27)
> +#define MBOX_DEST_PME BIT(28)
> +#define MBOX_DEST_SMI BIT(29)
> +#define MBOX_DEST_XHCI BIT(30)
> +#define MBOX_INT_EN BIT(31)
> +#define XUSB_CFG_ARU_MBOX_DATA_IN 0x0e8
> +#define CMD_DATA_SHIFT 0
> +#define CMD_DATA_MASK 0xffffff
> +#define CMD_TYPE_SHIFT 24
> +#define CMD_TYPE_MASK 0xff
> +#define XUSB_CFG_ARU_MBOX_DATA_OUT 0x0ec
> +#define XUSB_CFG_ARU_MBOX_OWNER 0x0f0
> +#define MBOX_OWNER_NONE 0
> +#define MBOX_OWNER_FW 1
> +#define MBOX_OWNER_SW 2
> +#define XUSB_CFG_ARU_SMI_INTR 0x428
> +#define MBOX_SMI_INTR_FW_HANG BIT(1)
> +#define MBOX_SMI_INTR_EN BIT(3)
> +
> +/* IPFS registers */
> +#define IPFS_XUSB_HOST_CONFIGURATION_0 0x180
> +#define IPFS_EN_FPCI BIT(0)
> +#define IPFS_XUSB_HOST_INTR_MASK_0 0x188
> +#define IPFS_IP_INT_MASK BIT(16)
> +#define IPFS_XUSB_HOST_CLKGATE_HYSTERESIS_0 0x1bc
> +
> +#define CSB_PAGE_SELECT_MASK 0x7fffff
> +#define CSB_PAGE_SELECT_SHIFT 9
> +#define CSB_PAGE_OFFSET_MASK 0x1ff
> +#define CSB_PAGE_SELECT(addr) ((addr) >> (CSB_PAGE_SELECT_SHIFT) & \
> + CSB_PAGE_SELECT_MASK)
> +#define CSB_PAGE_OFFSET(addr) ((addr) & CSB_PAGE_OFFSET_MASK)
> +
> +/* Falcon CSB registers */
> +#define XUSB_FALC_CPUCTL 0x100
> +#define CPUCTL_STARTCPU BIT(1)
> +#define CPUCTL_STATE_HALTED BIT(4)
> +#define CPUCTL_STATE_STOPPED BIT(5)
> +#define XUSB_FALC_BOOTVEC 0x104
> +#define XUSB_FALC_DMACTL 0x10c
> +#define XUSB_FALC_IMFILLRNG1 0x154
> +#define IMFILLRNG1_TAG_MASK 0xffff
> +#define IMFILLRNG1_TAG_LO_SHIFT 0
> +#define IMFILLRNG1_TAG_HI_SHIFT 16
> +#define XUSB_FALC_IMFILLCTL 0x158
> +
> +/* MP CSB registers */
> +#define XUSB_CSB_MP_ILOAD_ATTR 0x101a00
> +#define XUSB_CSB_MP_ILOAD_BASE_LO 0x101a04
> +#define XUSB_CSB_MP_ILOAD_BASE_HI 0x101a08
> +#define XUSB_CSB_MP_L2IMEMOP_SIZE 0x101a10
> +#define L2IMEMOP_SIZE_SRC_OFFSET_SHIFT 8
> +#define L2IMEMOP_SIZE_SRC_OFFSET_MASK 0x3ff
> +#define L2IMEMOP_SIZE_SRC_COUNT_SHIFT 24
> +#define L2IMEMOP_SIZE_SRC_COUNT_MASK 0xff
> +#define XUSB_CSB_MP_L2IMEMOP_TRIG 0x101a14
> +#define L2IMEMOP_ACTION_SHIFT 24
> +#define L2IMEMOP_INVALIDATE_ALL (0x40 << L2IMEMOP_ACTION_SHIFT)
> +#define L2IMEMOP_LOAD_LOCKED_RESULT (0x11 << L2IMEMOP_ACTION_SHIFT)
> +#define XUSB_CSB_MP_APMAP 0x10181c
> +#define APMAP_BOOTPATH BIT(31)
> +
> +#define IMEM_BLOCK_SIZE 256
> +
> +struct tegra_xusb_fw_header {
> + u32 boot_loadaddr_in_imem;
> + u32 boot_codedfi_offset;
> + u32 boot_codetag;
> + u32 boot_codesize;
> + u32 phys_memaddr;
> + u16 reqphys_memsize;
> + u16 alloc_phys_memsize;
> + u32 rodata_img_offset;
> + u32 rodata_section_start;
> + u32 rodata_section_end;
> + u32 main_fnaddr;
> + u32 fwimg_cksum;
> + u32 fwimg_created_time;
> + u32 imem_resident_start;
> + u32 imem_resident_end;
> + u32 idirect_start;
> + u32 idirect_end;
> + u32 l2_imem_start;
> + u32 l2_imem_end;
> + u32 version_id;
> + u8 init_ddirect;
> + u8 reserved[3];
> + u32 phys_addr_log_buffer;
> + u32 total_log_entries;
> + u32 dequeue_ptr;
> + u32 dummy_var[2];
> + u32 fwimg_len;
> + u8 magic[8];
> + u32 ss_low_power_entry_timeout;
> + u8 num_hsic_port;
> + u8 padding[139]; /* Pad to 256 bytes */
> +};
> +
> +struct tegra_xusb_phy_type {
> + const char *name;
> + unsigned int num;
> +};
> +
> +struct tegra_xusb_soc {
> + const char *firmware_file;
> + const char * const *supply_names;
> + unsigned int num_supplies;
> + const struct tegra_xusb_phy_type *phy_types;
> + unsigned int num_types;
> +
> + struct {
> + struct {
> + unsigned int offset;
> + unsigned int count;
> + } usb2, ulpi, hsic, usb3;
> + } ports;
> +};
> +
> +struct tegra_xusb {
> + struct device *dev;
> + struct usb_hcd *hcd;
> +
> + struct mutex lock;
> +
> + int xhci_irq;
> + int mbox_irq;
> +
> + void __iomem *ipfs_base;
> + void __iomem *fpci_base;
> +
> + const struct tegra_xusb_soc *soc;
> +
> + struct regulator_bulk_data *supplies;
> +
> + struct tegra_xusb_padctl *padctl;
> +
> + struct clk *host_clk;
> + struct clk *falcon_clk;
> + struct clk *ss_clk;
> + struct clk *ss_src_clk;
> + struct clk *hs_src_clk;
> + struct clk *fs_src_clk;
> + struct clk *pll_u_480m;
> + struct clk *clk_m;
> + struct clk *pll_e;
> +
> + struct reset_control *host_rst;
> + struct reset_control *ss_rst;
> +
> + struct phy **phys;
> + unsigned int num_phys;
> +
> + /* Firmware loading related */
> + void *fw_data;
> + size_t fw_size;
> + dma_addr_t fw_dma_addr;
> + bool fw_loaded;
> +};
> +
> +static struct hc_driver __read_mostly tegra_xhci_hc_driver;
> +
> +static inline u32 fpci_readl(struct tegra_xusb *tegra, unsigned int offset)
> +{
> + return readl(tegra->fpci_base + offset);
> +}
> +
> +static inline void fpci_writel(struct tegra_xusb *tegra, u32 value,
> + unsigned int offset)
> +{
> + writel(value, tegra->fpci_base + offset);
> +}
> +
> +static inline u32 ipfs_readl(struct tegra_xusb *tegra, unsigned int offset)
> +{
> + return readl(tegra->ipfs_base + offset);
> +}
> +
> +static inline void ipfs_writel(struct tegra_xusb *tegra, u32 value,
> + unsigned int offset)
> +{
> + writel(value, tegra->ipfs_base + offset);
> +}
> +
> +static u32 csb_readl(struct tegra_xusb *tegra, unsigned int offset)
> +{
> + u32 page = CSB_PAGE_SELECT(offset);
> + u32 ofs = CSB_PAGE_OFFSET(offset);
> +
> + fpci_writel(tegra, page, XUSB_CFG_ARU_C11_CSBRANGE);
> +
> + return fpci_readl(tegra, XUSB_CFG_CSB_BASE_ADDR + ofs);
> +}
> +
> +static void csb_writel(struct tegra_xusb *tegra, u32 value,
> + unsigned int offset)
> +{
> + u32 page = CSB_PAGE_SELECT(offset);
> + u32 ofs = CSB_PAGE_OFFSET(offset);
> +
> + fpci_writel(tegra, page, XUSB_CFG_ARU_C11_CSBRANGE);
> + fpci_writel(tegra, value, XUSB_CFG_CSB_BASE_ADDR + ofs);
> +}
> +
> +static int tegra_xusb_set_ss_clk(struct tegra_xusb *tegra,
> + unsigned long rate)
> +{
> + unsigned long new_parent_rate, old_parent_rate;
> + struct clk *clk = tegra->ss_src_clk;
> + unsigned int div;
> + int err;
> +
> + if (clk_get_rate(clk) == rate)
> + return 0;
> +
> + switch (rate) {
> + case TEGRA_XHCI_SS_HIGH_SPEED:
> + /*
> + * Reparent to PLLU_480M. Set divider first to avoid
> + * overclocking.
> + */
> + old_parent_rate = clk_get_rate(clk_get_parent(clk));
> + new_parent_rate = clk_get_rate(tegra->pll_u_480m);
> + div = new_parent_rate / rate;
> +
> + err = clk_set_rate(clk, old_parent_rate / div);
> + if (err)
> + return err;
> +
> + err = clk_set_parent(clk, tegra->pll_u_480m);
> + if (err)
> + return err;
> +
> + /*
> + * The rate should already be correct, but set it again just
> + * to be sure.
> + */
> + err = clk_set_rate(clk, rate);
> + if (err)
> + return err;
> +
> + break;
> +
> + case TEGRA_XHCI_SS_LOW_SPEED:
> + /* Reparent to CLK_M */
> + err = clk_set_parent(clk, tegra->clk_m);
> + if (err)
> + return err;
> +
> + err = clk_set_rate(clk, rate);
> + if (err)
> + return err;
> +
> + break;
> +
> + default:
> + dev_err(tegra->dev, "Invalid SS rate: %lu Hz\n", rate);
> + return -EINVAL;
> + }
> +
> + if (clk_get_rate(clk) != rate) {
> + dev_err(tegra->dev, "SS clock doesn't match requested rate\n");
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static unsigned long extract_field(u32 value, unsigned int start,
> + unsigned int count)
> +{
> + return (value >> start) & ((1 << count) - 1);
> +}
> +
> +/* Command requests from the firmware */
> +enum tegra_xusb_mbox_cmd {
> + MBOX_CMD_MSG_ENABLED = 1,
> + MBOX_CMD_INC_FALC_CLOCK,
> + MBOX_CMD_DEC_FALC_CLOCK,
> + MBOX_CMD_INC_SSPI_CLOCK,
> + MBOX_CMD_DEC_SSPI_CLOCK,
> + MBOX_CMD_SET_BW, /* no ACK/NAK required */
> + MBOX_CMD_SET_SS_PWR_GATING,
> + MBOX_CMD_SET_SS_PWR_UNGATING,
> + MBOX_CMD_SAVE_DFE_CTLE_CTX,
> + MBOX_CMD_AIRPLANE_MODE_ENABLED, /* unused */
> + MBOX_CMD_AIRPLANE_MODE_DISABLED, /* unused */
> + MBOX_CMD_START_HSIC_IDLE,
> + MBOX_CMD_STOP_HSIC_IDLE,
> + MBOX_CMD_DBC_WAKE_STACK, /* unused */
> + MBOX_CMD_HSIC_PRETEND_CONNECT,
> + MBOX_CMD_RESET_SSPI,
> + MBOX_CMD_DISABLE_SS_LFPS_DETECTION,
> + MBOX_CMD_ENABLE_SS_LFPS_DETECTION,
> +
> + MBOX_CMD_MAX,
> +
> + /* Response message to above commands */
> + MBOX_CMD_ACK = 128,
> + MBOX_CMD_NAK
> +};
> +
> +static const char * const mbox_cmd_name[] = {
> + [ 1] = "MSG_ENABLE",
> + [ 2] = "INC_FALCON_CLOCK",
> + [ 3] = "DEC_FALCON_CLOCK",
> + [ 4] = "INC_SSPI_CLOCK",
> + [ 5] = "DEC_SSPI_CLOCK",
> + [ 6] = "SET_BW",
> + [ 7] = "SET_SS_PWR_GATING",
> + [ 8] = "SET_SS_PWR_UNGATING",
> + [ 9] = "SAVE_DFE_CTLE_CTX",
> + [ 10] = "AIRPLANE_MODE_ENABLED",
> + [ 11] = "AIRPLANE_MODE_DISABLED",
> + [ 12] = "START_HSIC_IDLE",
> + [ 13] = "STOP_HSIC_IDLE",
> + [ 14] = "DBC_WAKE_STACK",
> + [ 15] = "HSIC_PRETEND_CONNECT",
> + [ 16] = "RESET_SSPI",
> + [ 17] = "DISABLE_SS_LFPS_DETECTION",
> + [ 18] = "ENABLE_SS_LFPS_DETECTION",
> + [128] = "ACK",
> + [129] = "NAK",
> +};
> +
> +struct tegra_xusb_mbox_msg {
> + u32 cmd;
> + u32 data;
> +};
> +
> +static inline u32 tegra_xusb_mbox_pack(const struct tegra_xusb_mbox_msg *msg)
> +{
> + return (msg->cmd & CMD_TYPE_MASK) << CMD_TYPE_SHIFT |
> + (msg->data & CMD_DATA_MASK) << CMD_DATA_SHIFT;
> +}
> +static inline void tegra_xusb_mbox_unpack(struct tegra_xusb_mbox_msg *msg,
> + u32 value)
> +{
> + msg->cmd = (value >> CMD_TYPE_SHIFT) & CMD_TYPE_MASK;
> + msg->data = (value >> CMD_DATA_SHIFT) & CMD_DATA_MASK;
> +}
> +
> +static bool tegra_xusb_mbox_cmd_requires_ack(enum tegra_xusb_mbox_cmd cmd)
> +{
> + switch (cmd) {
> + case MBOX_CMD_SET_BW:
> + case MBOX_CMD_ACK:
> + case MBOX_CMD_NAK:
> + return false;
> +
> + default:
> + return true;
> + }
> +}
> +
> +static int tegra_xusb_mbox_send(struct tegra_xusb *tegra,
> + const struct tegra_xusb_mbox_msg *msg)
> +{
> + bool wait_for_idle = false;
> + u32 value;
> +
> + /*
> + * Acquire the mailbox. The firmware still owns the mailbox for
> + * ACK/NAK messages.
> + */
> + if (!(msg->cmd == MBOX_CMD_ACK || msg->cmd == MBOX_CMD_NAK)) {
> + value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_OWNER);
> + if (value != MBOX_OWNER_NONE) {
> + dev_err(tegra->dev, "mailbox is busy\n");
> + return -EBUSY;
> + }
> +
> + fpci_writel(tegra, MBOX_OWNER_SW, XUSB_CFG_ARU_MBOX_OWNER);
> +
> + value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_OWNER);
> + if (value != MBOX_OWNER_SW) {
> + dev_err(tegra->dev, "failed to acquire mailbox\n");
> + return -EBUSY;
> + }
> +
> + wait_for_idle = true;
> + }
> +
> + value = tegra_xusb_mbox_pack(msg);
> + fpci_writel(tegra, value, XUSB_CFG_ARU_MBOX_DATA_IN);
> +
> + value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_CMD);
> + value |= MBOX_INT_EN | MBOX_DEST_FALC;
> + fpci_writel(tegra, value, XUSB_CFG_ARU_MBOX_CMD);
> +
> + if (wait_for_idle) {
> + unsigned long timeout = jiffies + msecs_to_jiffies(250);
> +
> + while (time_before(jiffies, timeout)) {
> + value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_OWNER);
> + if (value == MBOX_OWNER_NONE)
> + break;
> +
> + usleep_range(10, 20);
> + }
> +
> + if (time_after(jiffies, timeout))
> + value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_OWNER);
> +
> + if (value != MBOX_OWNER_NONE)
> + return -ETIMEDOUT;
> + }
> +
> + return 0;
> +}
> +
> +static irqreturn_t tegra_xusb_mbox_irq(int irq, void *data)
> +{
> + struct tegra_xusb *tegra = data;
> + u32 value;
> +
> + /* clear mailbox interrupts */
> + value = fpci_readl(tegra, XUSB_CFG_ARU_SMI_INTR);
> + fpci_writel(tegra, value, XUSB_CFG_ARU_SMI_INTR);
> +
> + if (value & MBOX_SMI_INTR_FW_HANG)
> + dev_err(tegra->dev, "controller firmware hang\n");
> +
> + return IRQ_WAKE_THREAD;
> +}
> +
> +static void tegra_xusb_mbox_handle(struct tegra_xusb *tegra,
> + const struct tegra_xusb_mbox_msg *msg)
> +{
> + struct tegra_xusb_padctl *padctl = tegra->padctl;
> + const struct tegra_xusb_soc *soc = tegra->soc;
> + struct device *dev = tegra->dev;
> + struct tegra_xusb_mbox_msg rsp;
> + unsigned long mask;
> + unsigned int port;
> + bool idle, enable;
> + int err;
> +
> + memset(&rsp, 0, sizeof(rsp));
> +
> + switch (msg->cmd) {
> + case MBOX_CMD_INC_FALC_CLOCK:
> + case MBOX_CMD_DEC_FALC_CLOCK:
> + rsp.data = clk_get_rate(tegra->falcon_clk) / 1000;
> + if (rsp.data != msg->data)
> + rsp.cmd = MBOX_CMD_NAK;
> + else
> + rsp.cmd = MBOX_CMD_ACK;
> +
> + break;
> +
> + case MBOX_CMD_INC_SSPI_CLOCK:
> + case MBOX_CMD_DEC_SSPI_CLOCK:
> + err = tegra_xusb_set_ss_clk(tegra, msg->data * 1000);
> + if (err < 0)
> + rsp.cmd = MBOX_CMD_NAK;
> + else
> + rsp.cmd = MBOX_CMD_ACK;
> +
> + rsp.data = clk_get_rate(tegra->ss_src_clk) / 1000;
> + break;
> +
> + case MBOX_CMD_SET_BW:
> + /*
> + * TODO: Request bandwidth once EMC scaling is supported.
> + * Ignore for now since ACK/NAK is not required for SET_BW
> + * messages.
> + */
> + break;
> +
> + case MBOX_CMD_SAVE_DFE_CTLE_CTX:
> + err = tegra_xusb_padctl_usb3_save_context(padctl, msg->data);
> + if (err < 0) {
> + dev_err(dev, "failed to save context for USB3#%u: %d\n",
> + msg->data, err);
> + rsp.cmd = MBOX_CMD_NAK;
> + } else {
> + rsp.cmd = MBOX_CMD_ACK;
> + }
> +
> + rsp.data = msg->data;
> + break;
> +
> + case MBOX_CMD_START_HSIC_IDLE:
> + case MBOX_CMD_STOP_HSIC_IDLE:
> + if (msg->cmd == MBOX_CMD_STOP_HSIC_IDLE)
> + idle = false;
> + else
> + idle = true;
> +
> + mask = extract_field(msg->data, 1 + soc->ports.hsic.offset,
> + soc->ports.hsic.count);
> +
> + for_each_set_bit(port, &mask, 32) {
> + err = tegra_xusb_padctl_hsic_set_idle(padctl, port,
> + idle);
> + if (err < 0)
> + break;
> + }
> +
> + if (err < 0) {
> + dev_err(dev, "failed to set HSIC#%u %s: %d\n", port,
> + idle ? "idle" : "busy", err);
> + rsp.cmd = MBOX_CMD_NAK;
> + } else {
> + rsp.cmd = MBOX_CMD_ACK;
> + }
> +
> + rsp.data = msg->data;
> + break;
> +
> + case MBOX_CMD_DISABLE_SS_LFPS_DETECTION:
> + case MBOX_CMD_ENABLE_SS_LFPS_DETECTION:
> + if (msg->cmd == MBOX_CMD_DISABLE_SS_LFPS_DETECTION)
> + enable = false;
> + else
> + enable = true;
> +
> + mask = extract_field(msg->data, 1 + soc->ports.usb3.offset,
> + soc->ports.usb3.count);
> +
> + for_each_set_bit(port, &mask, soc->ports.usb3.count) {
> + err = tegra_xusb_padctl_usb3_set_lfps_detect(padctl,
> + port,
> + enable);
> + if (err < 0)
> + break;
> + }
> +
> + if (err < 0) {
> + dev_err(dev,
> + "failed to %s LFPS detection on USB3#%u: %d\n",
> + enable ? "enable" : "disable", port, err);
> + rsp.cmd = MBOX_CMD_NAK;
> + } else {
> + rsp.cmd = MBOX_CMD_ACK;
> + }
> +
> + rsp.data = msg->data;
> + break;
> +
> + default:
> + dev_warn(dev, "unknown message: %#x\n", msg->cmd);
> + break;
> + }
> +
> + if (rsp.cmd) {
> + const char *cmd = (rsp.cmd == MBOX_CMD_ACK) ? "ACK" : "NAK";
> +
> + err = tegra_xusb_mbox_send(tegra, &rsp);
> + if (err < 0)
> + dev_err(dev, "failed to send %s: %d\n", cmd, err);
> + }
> +}
> +
> +static irqreturn_t tegra_xusb_mbox_thread(int irq, void *data)
> +{
> + struct tegra_xusb *tegra = data;
> + struct tegra_xusb_mbox_msg msg;
> + u32 value;
> +
> + mutex_lock(&tegra->lock);
> +
> + value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_DATA_OUT);
> + tegra_xusb_mbox_unpack(&msg, value);
> +
> + value = fpci_readl(tegra, XUSB_CFG_ARU_MBOX_CMD);
> + value &= ~MBOX_DEST_SMI;
> + fpci_writel(tegra, value, XUSB_CFG_ARU_MBOX_CMD);
> +
> + /* clear mailbox owner if no ACK/NAK is required */
> + if (!tegra_xusb_mbox_cmd_requires_ack(msg.cmd))
> + fpci_writel(tegra, MBOX_OWNER_NONE, XUSB_CFG_ARU_MBOX_OWNER);
> +
> + tegra_xusb_mbox_handle(tegra, &msg);
> +
> + mutex_unlock(&tegra->lock);
> + return IRQ_HANDLED;
> +}
> +
> +static void tegra_xusb_ipfs_config(struct tegra_xusb *tegra)
> +{
> + u32 value;
> +
> + value = ipfs_readl(tegra, IPFS_XUSB_HOST_CONFIGURATION_0);
> + value |= IPFS_EN_FPCI;
> + ipfs_writel(tegra, value, IPFS_XUSB_HOST_CONFIGURATION_0);
> +
> + usleep_range(10, 20);
> +
> + /* Program BAR0 space */
> + value = fpci_readl(tegra, XUSB_CFG_4);
> + value &= ~(XUSB_BASE_ADDR_MASK << XUSB_BASE_ADDR_SHIFT);
> + value |= tegra->hcd->rsrc_start & (XUSB_BASE_ADDR_MASK <<
> + XUSB_BASE_ADDR_SHIFT);
> + fpci_writel(tegra, value, XUSB_CFG_4);
> +
> + usleep_range(100, 200);
> +
> + /* Enable bus master */
> + value = fpci_readl(tegra, XUSB_CFG_1);
> + value |= XUSB_IO_SPACE_EN | XUSB_MEM_SPACE_EN | XUSB_BUS_MASTER_EN;
> + fpci_writel(tegra, value, XUSB_CFG_1);
> +
> + /* Enable interrupt assertion */
> + value = ipfs_readl(tegra, IPFS_XUSB_HOST_INTR_MASK_0);
> + value |= IPFS_IP_INT_MASK;
> + ipfs_writel(tegra, value, IPFS_XUSB_HOST_INTR_MASK_0);
> +
> + /* Set hysteresis */
> + ipfs_writel(tegra, 0x80, IPFS_XUSB_HOST_CLKGATE_HYSTERESIS_0);
> +}
> +
> +static int tegra_xusb_load_firmware(struct tegra_xusb *tegra)
> +{
> + unsigned int code_tag_blocks, code_size_blocks, code_blocks;
> + struct tegra_xusb_fw_header *header;
> + struct device *dev = tegra->dev;
> + unsigned long timeout;
> + struct tm fw_tm;
> + time_t fw_time;
> + u64 fw_base;
> + u32 value;
> +
> + if (csb_readl(tegra, XUSB_CSB_MP_ILOAD_BASE_LO) != 0) {
> + dev_info(dev, "Firmware already loaded, Falcon state %#x\n",
> + csb_readl(tegra, XUSB_FALC_CPUCTL));
> + return 0;
> + }
> +
> + header = (struct tegra_xusb_fw_header *)tegra->fw_data;
> +
> + /* Program the size of DFI into ILOAD_ATTR. */
> + csb_writel(tegra, tegra->fw_size, XUSB_CSB_MP_ILOAD_ATTR);
> +
> + /*
> + * Boot code of the firmware reads the ILOAD_BASE registers
> + * to get to the start of the DFI in system memory.
> + */
> + fw_base = tegra->fw_dma_addr + sizeof(*header);
> + csb_writel(tegra, fw_base, XUSB_CSB_MP_ILOAD_BASE_LO);
> + csb_writel(tegra, fw_base >> 32, XUSB_CSB_MP_ILOAD_BASE_HI);
> +
> + /* Set BOOTPATH to 1 in APMAP. */
> + csb_writel(tegra, APMAP_BOOTPATH, XUSB_CSB_MP_APMAP);
> +
> + /* Invalidate L2IMEM. */
> + csb_writel(tegra, L2IMEMOP_INVALIDATE_ALL, XUSB_CSB_MP_L2IMEMOP_TRIG);
> +
> + /*
> + * Initiate fetch of bootcode from system memory into L2IMEM.
> + * Program bootcode location and size in system memory.
> + */
> + code_tag_blocks = DIV_ROUND_UP(le32_to_cpu(header->boot_codetag),
> + IMEM_BLOCK_SIZE);
> + code_size_blocks = DIV_ROUND_UP(le32_to_cpu(header->boot_codesize),
> + IMEM_BLOCK_SIZE);
> + code_blocks = code_tag_blocks + code_size_blocks;
> +
> + value = ((code_tag_blocks & L2IMEMOP_SIZE_SRC_OFFSET_MASK) <<
> + L2IMEMOP_SIZE_SRC_OFFSET_SHIFT) |
> + ((code_size_blocks & L2IMEMOP_SIZE_SRC_COUNT_MASK) <<
> + L2IMEMOP_SIZE_SRC_COUNT_SHIFT);
> + csb_writel(tegra, value, XUSB_CSB_MP_L2IMEMOP_SIZE);
> +
> + /* Trigger L2IMEM load operation. */
> + csb_writel(tegra, L2IMEMOP_LOAD_LOCKED_RESULT,
> + XUSB_CSB_MP_L2IMEMOP_TRIG);
> +
> + /* Setup Falcon auto-fill. */
> + csb_writel(tegra, code_size_blocks, XUSB_FALC_IMFILLCTL);
> +
> + value = ((code_tag_blocks & IMFILLRNG1_TAG_MASK) <<
> + IMFILLRNG1_TAG_LO_SHIFT) |
> + ((code_blocks & IMFILLRNG1_TAG_MASK) <<
> + IMFILLRNG1_TAG_HI_SHIFT);
> + csb_writel(tegra, value, XUSB_FALC_IMFILLRNG1);
> +
> + csb_writel(tegra, 0, XUSB_FALC_DMACTL);
> +
> + msleep(50);
> +
> + csb_writel(tegra, le32_to_cpu(header->boot_codetag),
> + XUSB_FALC_BOOTVEC);
> +
> + /* Boot Falcon CPU and wait for it to enter the STOPPED (idle) state. */
> + timeout = jiffies + msecs_to_jiffies(5);
> +
> + csb_writel(tegra, CPUCTL_STARTCPU, XUSB_FALC_CPUCTL);
> +
> + while (time_before(jiffies, timeout)) {
> + if (csb_readl(tegra, XUSB_FALC_CPUCTL) == CPUCTL_STATE_STOPPED)
> + break;
> +
> + usleep_range(100, 200);
> + }
> +
> + if (csb_readl(tegra, XUSB_FALC_CPUCTL) != CPUCTL_STATE_STOPPED) {
> + dev_err(dev, "Falcon failed to start, state: %#x\n",
> + csb_readl(tegra, XUSB_FALC_CPUCTL));
> + return -EIO;
> + }
> +
> + fw_time = le32_to_cpu(header->fwimg_created_time);
> + time_to_tm(fw_time, 0, &fw_tm);
> +
> + dev_info(dev, "Firmware timestamp: %ld-%02d-%02d %02d:%02d:%02d UTC\n",
> + fw_tm.tm_year + 1900, fw_tm.tm_mon + 1, fw_tm.tm_mday,
> + fw_tm.tm_hour, fw_tm.tm_min, fw_tm.tm_sec);
> +
> + return 0;
> +}
> +
> +static int tegra_xusb_clk_enable(struct tegra_xusb *tegra)
> +{
> + int err;
> +
> + err = clk_prepare_enable(tegra->pll_e);
> + if (err < 0)
> + return err;
> +
> + err = clk_prepare_enable(tegra->host_clk);
> + if (err < 0)
> + goto disable_plle;
> +
> + err = clk_prepare_enable(tegra->ss_clk);
> + if (err < 0)
> + goto disable_host;
> +
> + err = clk_prepare_enable(tegra->falcon_clk);
> + if (err < 0)
> + goto disable_ss;
> +
> + err = clk_prepare_enable(tegra->fs_src_clk);
> + if (err < 0)
> + goto disable_falc;
> +
> + err = clk_prepare_enable(tegra->hs_src_clk);
> + if (err < 0)
> + goto disable_fs_src;
> +
> + err = tegra_xusb_set_ss_clk(tegra, TEGRA_XHCI_SS_HIGH_SPEED);
> + if (err < 0)
> + goto disable_hs_src;
> +
> + return 0;
> +
> +disable_hs_src:
> + clk_disable_unprepare(tegra->hs_src_clk);
> +disable_fs_src:
> + clk_disable_unprepare(tegra->fs_src_clk);
> +disable_falc:
> + clk_disable_unprepare(tegra->falcon_clk);
> +disable_ss:
> + clk_disable_unprepare(tegra->ss_clk);
> +disable_host:
> + clk_disable_unprepare(tegra->host_clk);
> +disable_plle:
> + clk_disable_unprepare(tegra->pll_e);
> + return err;
> +}
> +
> +static void tegra_xusb_clk_disable(struct tegra_xusb *tegra)
> +{
> + clk_disable_unprepare(tegra->pll_e);
> + clk_disable_unprepare(tegra->host_clk);
> + clk_disable_unprepare(tegra->ss_clk);
> + clk_disable_unprepare(tegra->falcon_clk);
> + clk_disable_unprepare(tegra->fs_src_clk);
> + clk_disable_unprepare(tegra->hs_src_clk);
> +}
> +
> +static int tegra_xusb_phy_enable(struct tegra_xusb *tegra)
> +{
> + unsigned int i;
> + int err;
> +
> + for (i = 0; i < tegra->num_phys; i++) {
> + err = phy_init(tegra->phys[i]);
> + if (err)
> + goto disable_phy;
> +
> + err = phy_power_on(tegra->phys[i]);
> + if (err) {
> + phy_exit(tegra->phys[i]);
> + goto disable_phy;
> + }
> + }
> +
> + return 0;
> +
> +disable_phy:
> + while (i--) {
> + phy_power_off(tegra->phys[i]);
> + phy_exit(tegra->phys[i]);
> + }
> +
> + return err;
> +}
> +
> +static void tegra_xusb_phy_disable(struct tegra_xusb *tegra)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < tegra->num_phys; i++) {
> + phy_power_off(tegra->phys[i]);
> + phy_exit(tegra->phys[i]);
> + }
> +}
> +
> +static void tegra_xhci_quirks(struct device *dev, struct xhci_hcd *xhci)
> +{
> + xhci->quirks |= XHCI_PLAT;
> +}
> +
> +static int tegra_xhci_setup(struct usb_hcd *hcd)
> +{
> + return xhci_gen_setup(hcd, tegra_xhci_quirks);
> +}
> +
> +static const char * const tegra124_supply_names[] = {
> + "avddio-pex",
> + "dvddio-pex",
> + "avdd-usb",
> + "avdd-pll-utmip",
> + "avdd-pll-erefe",
> + "avdd-usb-ss-pll",
> + "hvdd-usb-ss",
> + "hvdd-usb-ss-pll-e",
> +};
> +
> +static const struct tegra_xusb_phy_type tegra124_phy_types[] = {
> + { .name = "usb3", .num = 2, },
> + { .name = "usb2", .num = 3, },
> + { .name = "hsic", .num = 2, },
> +};
> +
> +static const struct tegra_xusb_soc tegra124_soc = {
> + .firmware_file = "nvidia/tegra124/xusb.bin",
> + .supply_names = tegra124_supply_names,
> + .num_supplies = ARRAY_SIZE(tegra124_supply_names),
> + .phy_types = tegra124_phy_types,
> + .num_types = ARRAY_SIZE(tegra124_phy_types),
> + .ports = {
> + .usb2 = { .offset = 4, .count = 4, },
> + .hsic = { .offset = 6, .count = 2, },
> + .usb3 = { .offset = 0, .count = 2, },
> + },
> +};
> +MODULE_FIRMWARE("nvidia/tegra124/xusb.bin");
> +
> +static const struct of_device_id tegra_xusb_of_match[] = {
> + { .compatible = "nvidia,tegra124-xusb", .data = &tegra124_soc },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, tegra_xusb_of_match);
> +
> +static void tegra_xusb_probe_finish(const struct firmware *fw, void *context)
> +{
> + struct tegra_xusb_fw_header *header;
> + struct tegra_xusb *tegra = context;
> + struct device *dev = tegra->dev;
> + struct tegra_xusb_mbox_msg msg;
> + struct xhci_hcd *xhci = NULL;
> + int err;
> +
> + if (!fw) {
> + dev_err(tegra->dev, "no firmware loaded\n");
> + goto put_usb2_hcd;
> + }
> +
> + /* Load Falcon controller with its firmware. */
> + header = (struct tegra_xusb_fw_header *)fw->data;
> + tegra->fw_size = le32_to_cpu(header->fwimg_len);
> +
> + tegra->fw_data = dma_alloc_coherent(dev, tegra->fw_size,
> + &tegra->fw_dma_addr,
> + GFP_KERNEL);
> + if (!tegra->fw_data) {
> + dev_err(tegra->dev, "failed to allocate memory for firmware\n");
> + goto put_usb2_hcd;
> + }
> +
> + memcpy(tegra->fw_data, fw->data, tegra->fw_size);
> +
> + err = tegra_xusb_load_firmware(tegra);
> + if (err < 0) {
> + dev_err(tegra->dev, "failed to parse firmware: %d\n", err);
> + goto put_usb2_hcd;
> + }
> +
> + err = usb_add_hcd(tegra->hcd, tegra->xhci_irq, IRQF_SHARED);
> + if (err < 0) {
> + dev_err(tegra->dev, "failed to add USB HCD: %d\n", err);
> + goto put_usb2_hcd;
> + }
> +
> + device_wakeup_enable(tegra->hcd->self.controller);
> +
> + /*
> + * USB 2.0 roothub is stored in drvdata now. Swap it with the Tegra HCD.
> + */
> + tegra->hcd = dev_get_drvdata(dev);
> + dev_set_drvdata(dev, tegra);
> + xhci = hcd_to_xhci(tegra->hcd);
> +
> + xhci->shared_hcd = usb_create_shared_hcd(&tegra_xhci_hc_driver,
> + dev, dev_name(dev),
> + tegra->hcd);
> + if (!xhci->shared_hcd) {
> + dev_err(tegra->dev, "failed to create shared HCD\n");
> + goto dealloc_usb2_hcd;
> + }
> +
> + err = usb_add_hcd(xhci->shared_hcd, tegra->xhci_irq, IRQF_SHARED);
> + if (err < 0) {
> + dev_err(tegra->dev, "failed to add shared HCD: %d\n", err);
> + goto put_usb3_hcd;
> + }
> +
> + mutex_lock(&tegra->lock);
> +
> + /* Enable firmware messages from controller. */
> + msg.cmd = MBOX_CMD_MSG_ENABLED;
> + msg.data = 0;
> +
> + err = tegra_xusb_mbox_send(tegra, &msg);
> + if (err < 0) {
> + dev_err(tegra->dev, "failed to enable messages: %d\n", err);
> + mutex_unlock(&tegra->lock);
> + goto dealloc_usb3_hcd;
> + }
> +
> + mutex_unlock(&tegra->lock);
> +
> + err = devm_request_threaded_irq(dev, tegra->mbox_irq,
> + tegra_xusb_mbox_irq,
> + tegra_xusb_mbox_thread, 0,
> + dev_name(dev), tegra);
> + if (err < 0) {
> + dev_err(tegra->dev, "failed to request IRQ: %d\n", err);
> + goto dealloc_usb3_hcd;
> + }
> +
> + tegra->fw_loaded = true;
> + release_firmware(fw);
> +
> + return;
> +
> + /* Free up as much as we can and wait to be unbound. */
> +dealloc_usb3_hcd:
> + usb_remove_hcd(xhci->shared_hcd);
> +put_usb3_hcd:
> + usb_put_hcd(xhci->shared_hcd);
> +dealloc_usb2_hcd:
> + usb_remove_hcd(tegra->hcd);
> +put_usb2_hcd:
> + usb_put_hcd(tegra->hcd);
> + tegra->hcd = NULL;
> + release_firmware(fw);
> +}
> +
> +static int tegra_xusb_probe(struct platform_device *pdev)
> +{
> + const struct of_device_id *match;
> + struct tegra_xusb *tegra;
> + unsigned int i, j, k;
> + struct resource *res;
> + struct usb_hcd *hcd;
> + struct phy *phy;
> + int err;
> +
> + BUILD_BUG_ON(sizeof(struct tegra_xusb_fw_header) != 256);
> +
> + tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL);
> + if (!tegra)
> + return -ENOMEM;
> +
> + platform_set_drvdata(pdev, tegra);
> + mutex_init(&tegra->lock);
> + tegra->dev = &pdev->dev;
> +
> + match = of_match_device(tegra_xusb_of_match, &pdev->dev);
> + tegra->soc = match->data;
> +
> + hcd = usb_create_hcd(&tegra_xhci_hc_driver, &pdev->dev,
> + dev_name(&pdev->dev));
> + if (!hcd)
> + return -ENOMEM;
> +
> + tegra->hcd = hcd;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + hcd->regs = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(hcd->regs)) {
> + err = PTR_ERR(hcd->regs);
> + goto put_hcd;
> + }
> +
> + hcd->rsrc_start = res->start;
> + hcd->rsrc_len = resource_size(res);
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> + tegra->fpci_base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(tegra->fpci_base)) {
> + err = PTR_ERR(tegra->fpci_base);
> + goto put_hcd;
> + }
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
> + tegra->ipfs_base = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(tegra->ipfs_base)) {
> + err = PTR_ERR(tegra->ipfs_base);
> + goto put_hcd;
> + }
> +
> + tegra->xhci_irq = platform_get_irq(pdev, 0);
> + if (tegra->xhci_irq < 0) {
> + err = tegra->xhci_irq;
> + goto put_hcd;
> + }
> +
> + tegra->mbox_irq = platform_get_irq(pdev, 1);
> + if (tegra->mbox_irq < 0) {
> + err = tegra->mbox_irq;
> + goto put_hcd;
> + }
> +
> + tegra->padctl = tegra_xusb_padctl_get(&pdev->dev);
> + if (IS_ERR(tegra->padctl)) {
> + err = PTR_ERR(tegra->padctl);
> + goto put_hcd;
> + }
> +
> + tegra->host_rst = devm_reset_control_get(&pdev->dev, "xusb_host");
> + if (IS_ERR(tegra->host_rst)) {
> + err = PTR_ERR(tegra->host_rst);
> + dev_err(&pdev->dev, "failed to get xusb_host reset: %d\n", err);
> + goto put_padctl;
> + }
> +
> + tegra->ss_rst = devm_reset_control_get(&pdev->dev, "xusb_ss");
> + if (IS_ERR(tegra->ss_rst)) {
> + err = PTR_ERR(tegra->ss_rst);
> + dev_err(&pdev->dev, "failed to get xusb_ss reset: %d\n", err);
> + goto put_padctl;
> + }
> +
> + tegra->host_clk = devm_clk_get(&pdev->dev, "xusb_host");
> + if (IS_ERR(tegra->host_clk)) {
> + err = PTR_ERR(tegra->host_clk);
> + dev_err(&pdev->dev, "failed to get xusb_host: %d\n", err);
> + goto put_padctl;
> + }
> +
> + tegra->falcon_clk = devm_clk_get(&pdev->dev, "xusb_falcon_src");
> + if (IS_ERR(tegra->falcon_clk)) {
> + err = PTR_ERR(tegra->falcon_clk);
> + dev_err(&pdev->dev, "failed to get xusb_falcon_src: %d\n", err);
> + goto put_padctl;
> + }
> +
> + tegra->ss_clk = devm_clk_get(&pdev->dev, "xusb_ss");
> + if (IS_ERR(tegra->ss_clk)) {
> + err = PTR_ERR(tegra->ss_clk);
> + dev_err(&pdev->dev, "failed to get xusb_ss: %d\n", err);
> + goto put_padctl;
> + }
> +
> + tegra->ss_src_clk = devm_clk_get(&pdev->dev, "xusb_ss_src");
> + if (IS_ERR(tegra->ss_src_clk)) {
> + err = PTR_ERR(tegra->ss_src_clk);
> + dev_err(&pdev->dev, "failed to get xusb_ss_src: %d\n", err);
> + goto put_padctl;
> + }
> +
> + tegra->hs_src_clk = devm_clk_get(&pdev->dev, "xusb_hs_src");
> + if (IS_ERR(tegra->hs_src_clk)) {
> + err = PTR_ERR(tegra->hs_src_clk);
> + dev_err(&pdev->dev, "failed to get xusb_hs_src: %d\n", err);
> + goto put_padctl;
> + }
> +
> + tegra->fs_src_clk = devm_clk_get(&pdev->dev, "xusb_fs_src");
> + if (IS_ERR(tegra->fs_src_clk)) {
> + err = PTR_ERR(tegra->fs_src_clk);
> + dev_err(&pdev->dev, "failed to get xusb_fs_src: %d\n", err);
> + goto put_padctl;
> + }
> +
> + tegra->pll_u_480m = devm_clk_get(&pdev->dev, "pll_u_480m");
> + if (IS_ERR(tegra->pll_u_480m)) {
> + err = PTR_ERR(tegra->pll_u_480m);
> + dev_err(&pdev->dev, "failed to get pll_u_480m: %d\n", err);
> + goto put_padctl;
> + }
> +
> + tegra->clk_m = devm_clk_get(&pdev->dev, "clk_m");
> + if (IS_ERR(tegra->clk_m)) {
> + err = PTR_ERR(tegra->clk_m);
> + dev_err(&pdev->dev, "failed to get clk_m: %d\n", err);
> + goto put_padctl;
> + }
> +
> + tegra->pll_e = devm_clk_get(&pdev->dev, "pll_e");
> + if (IS_ERR(tegra->pll_e)) {
> + err = PTR_ERR(tegra->pll_e);
> + dev_err(&pdev->dev, "failed to get pll_e: %d\n", err);
> + goto put_padctl;
> + }
> +
> + err = tegra_xusb_clk_enable(tegra);
> + if (err) {
> + dev_err(&pdev->dev, "failed to enable clocks: %d\n", err);
> + goto put_padctl;
> + }
> +
> + tegra->supplies = devm_kcalloc(&pdev->dev, tegra->soc->num_supplies,
> + sizeof(*tegra->supplies), GFP_KERNEL);
> + if (!tegra->supplies) {
> + err = -ENOMEM;
> + goto disable_clk;
> + }
> +
> + for (i = 0; i < tegra->soc->num_supplies; i++)
> + tegra->supplies[i].supply = tegra->soc->supply_names[i];
> +
> + err = devm_regulator_bulk_get(&pdev->dev, tegra->soc->num_supplies,
> + tegra->supplies);
> + if (err) {
> + dev_err(&pdev->dev, "failed to get regulators: %d\n", err);
> + goto disable_clk;
> + }
> +
> + err = regulator_bulk_enable(tegra->soc->num_supplies, tegra->supplies);
> + if (err) {
> + dev_err(&pdev->dev, "failed to enable regulators: %d\n", err);
> + goto disable_clk;
> + }
> +
> + for (i = 0; i < tegra->soc->num_types; i++)
> + tegra->num_phys += tegra->soc->phy_types[i].num;
> +
> + tegra->phys = devm_kcalloc(&pdev->dev, tegra->num_phys,
> + sizeof(*tegra->phys), GFP_KERNEL);
> + if (!tegra->phys) {
> + dev_err(&pdev->dev, "failed to allocate PHY array\n");
> + err = -ENOMEM;
> + goto disable_regulator;
> + }
> +
> + for (i = 0, k = 0; i < tegra->soc->num_types; i++) {
> + char prop[8];
> +
> + for (j = 0; j < tegra->soc->phy_types[i].num; j++) {
> + snprintf(prop, sizeof(prop), "%s-%d",
> + tegra->soc->phy_types[i].name, j);
> +
> + phy = devm_phy_optional_get(&pdev->dev, prop);
> + if (IS_ERR(phy)) {
> + dev_err(&pdev->dev, "failed to get PHY %s: %ld\n", prop, PTR_ERR(phy));
> + err = PTR_ERR(phy);
> + goto disable_regulator;
> + }
> +
> + tegra->phys[k++] = phy;
> + }
> + }
> +
> + tegra_xusb_ipfs_config(tegra);
> +
> + err = tegra_xusb_phy_enable(tegra);
> + if (err < 0) {
> + dev_err(&pdev->dev, "failed to enable PHYs: %d\n", err);
> + goto disable_regulator;
> + }
> +
> + err = request_firmware_nowait(THIS_MODULE, true,
> + tegra->soc->firmware_file,
> + tegra->dev, GFP_KERNEL, tegra,
> + tegra_xusb_probe_finish);
> + if (err < 0) {
> + dev_err(&pdev->dev, "failed to request firmware: %d\n", err);
> + goto disable_phy;
> + }
> +
> + return 0;
> +
> +disable_phy:
> + tegra_xusb_phy_disable(tegra);
> +disable_regulator:
> + regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
> +disable_clk:
> + tegra_xusb_clk_disable(tegra);
> +put_padctl:
> + tegra_xusb_padctl_put(tegra->padctl);
> +put_hcd:
> + usb_put_hcd(hcd);
> + return err;
> +}
> +
> +static int tegra_xusb_remove(struct platform_device *pdev)
> +{
> + struct tegra_xusb *tegra = platform_get_drvdata(pdev);
> + struct usb_hcd *hcd = tegra->hcd;
> + struct xhci_hcd *xhci;
> +
> + if (tegra->fw_loaded) {
> + xhci = hcd_to_xhci(hcd);
> + usb_remove_hcd(xhci->shared_hcd);
> + usb_put_hcd(xhci->shared_hcd);
> + usb_remove_hcd(hcd);
> + tegra_xusb_padctl_put(tegra->padctl);
> + usb_put_hcd(hcd);
> + kfree(xhci);
> + } else if (hcd) {
> + /* Unbound after probe(), but before firmware loading. */
> + tegra_xusb_padctl_put(tegra->padctl);
> + usb_put_hcd(hcd);
> + }
> +
> + if (tegra->fw_data)
> + dma_free_coherent(tegra->dev, tegra->fw_size, tegra->fw_data,
> + tegra->fw_dma_addr);
> +
> + tegra_xusb_phy_disable(tegra);
> + regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
> + tegra_xusb_clk_disable(tegra);
> +
> + return 0;
> +}
> +
> +#ifdef CONFIG_PM_SLEEP
> +static int tegra_xusb_suspend(struct device *dev)
> +{
> + struct tegra_xusb *tegra = dev_get_drvdata(dev);
> + struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
> + bool wakeup = device_may_wakeup(dev);
> +
> + /* TODO: Powergate controller across suspend/resume. */
> + return xhci_suspend(xhci, wakeup);
> +}
> +
> +static int tegra_xusb_resume(struct device *dev)
> +{
> + struct tegra_xusb *tegra = dev_get_drvdata(dev);
> + struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
> +
> + return xhci_resume(xhci, 0);
> +}
> +#endif
> +
> +static const struct dev_pm_ops tegra_xusb_pm_ops = {
> + SET_SYSTEM_SLEEP_PM_OPS(tegra_xusb_suspend, tegra_xusb_resume)
> +};
> +
> +static struct platform_driver tegra_xusb_driver = {
> + .probe = tegra_xusb_probe,
> + .remove = tegra_xusb_remove,
> + .driver = {
> + .name = "tegra-xusb",
> + .pm = &tegra_xusb_pm_ops,
> + .of_match_table = tegra_xusb_of_match,
> + },
> +};
> +
> +static const struct xhci_driver_overrides tegra_xhci_overrides __initconst = {
> + .extra_priv_size = sizeof(struct xhci_hcd),
> + .reset = tegra_xhci_setup,
> +};
> +
> +static int __init tegra_xusb_init(void)
> +{
> + xhci_init_driver(&tegra_xhci_hc_driver, &tegra_xhci_overrides);
> +
> + return platform_driver_register(&tegra_xusb_driver);
> +}
> +module_init(tegra_xusb_init);
> +
> +static void __exit tegra_xusb_exit(void)
> +{
> + platform_driver_unregister(&tegra_xusb_driver);
> +}
> +module_exit(tegra_xusb_exit);
> +
> +MODULE_AUTHOR("Andrew Bresticker <[email protected]>");
> +MODULE_DESCRIPTION("NVIDIA Tegra XUSB xHCI host-controller driver");
> +MODULE_LICENSE("GPL v2");
> --
> 2.7.1
>


Attachments:
(No filename) (39.44 kB)
signature.asc (819.00 B)
Download all attachments

2016-04-05 13:35:57

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH v10 8/9] usb: xhci: Add NVIDIA Tegra XUSB controller driver

On Tue, Apr 05, 2016 at 03:17:51PM +0200, Thierry Reding wrote:
> Hi Mathias, Greg,
>
> Due to the various dependencies within the series, I'd prefer this to go
> via the Tegra tree. Would you be okay with providing your Acked-by?

That's all up to Mathias, I'll defer to him here :)

thanks,

greg k-h

2016-04-05 14:44:25

by Thierry Reding

[permalink] [raw]
Subject: Re: [PATCH v10 3/9] dt-bindings: phy: tegra-xusb-padctl: Add Tegra210 support

On Wed, Mar 16, 2016 at 11:59:44AM -0600, Stephen Warren wrote:
> On 03/04/2016 09:19 AM, Thierry Reding wrote:
> > From: Thierry Reding <[email protected]>
> >
> > Extend the binding to cover the set of feature found in Tegra210.
>
> Acked-by: Stephen Warren <[email protected]>
>
> > diff --git a/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
>
> > + padctl@0,7009f000 {
> ...
> > + pads {
> ...
> > + };
> > +
> > + ports {
> ...
> > + };
>
> As a comment not affecting my ack in any way: At the top-level, we place all
> the child nodes into "array container" nodes named "pads" and "ports". This
> is nice since it separates different types of child nodes and allows easily
> adding more types of child nodes in the future without interference, and in
> a way that allows us to explicitly know what each node is without having to
> interpret its name or compatible value to do so. However, we haven't done
> this with the per-lane child nodes inside each pad. If we were to rev the
> design, I'd be tempted to suggest:
>
> padctl@0,7009f000 {
> pads {
> usb2 {
> lanes { // This level is new
> usb2-0 {

I tried to make this work, but it's unfortunately not possible with the
current code. The reason is that the PHY subsystem assumes that either
the provider DT node corresponds to that of the device (the usb2 pad in
the above example) or one of its children. Hence, putting everything
into one more level further down would require some mechanism to tell
the subsystem about it so that it can be found.

Arguably the current support code isn't a good argument for designing a
binding, so perhaps it'd be useful to add this mechanism in order to get
a better binding. On the other hand, I'm not sure it's really worth it,
since we already have generic bindings that specify the layout of child
devices, and those have been agreed upon broadly (presumably).

In light of the recent discussion on DPAUX vs. I2C, I see how having the
extra level would be useful to provide additional context. If you think
it's worth it I can spend the extra time to get this implemented in the
core.

Thierry


Attachments:
(No filename) (2.17 kB)
signature.asc (819.00 B)
Download all attachments

2016-04-05 16:12:29

by Mathias Nyman

[permalink] [raw]
Subject: Re: [PATCH v10 8/9] usb: xhci: Add NVIDIA Tegra XUSB controller driver

On 05.04.2016 16:35, Greg Kroah-Hartman wrote:
> On Tue, Apr 05, 2016 at 03:17:51PM +0200, Thierry Reding wrote:
>> Hi Mathias, Greg,
>>
>> Due to the various dependencies within the series, I'd prefer this to go
>> via the Tegra tree. Would you be okay with providing your Acked-by?
>
> That's all up to Mathias, I'll defer to him here :)
>
> thanks,
>

It's on my todo list, should get it reviewed during this week

-Mathias

2016-04-05 21:10:25

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH v10 3/9] dt-bindings: phy: tegra-xusb-padctl: Add Tegra210 support

On 04/05/2016 08:44 AM, Thierry Reding wrote:
> On Wed, Mar 16, 2016 at 11:59:44AM -0600, Stephen Warren wrote:
>> On 03/04/2016 09:19 AM, Thierry Reding wrote:
>>> From: Thierry Reding <[email protected]>
>>>
>>> Extend the binding to cover the set of feature found in Tegra210.
>>
>> Acked-by: Stephen Warren <[email protected]>
>>
>>> diff --git a/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
>>
>>> + padctl@0,7009f000 {
>> ...
>>> + pads {
>> ...
>>> + };
>>> +
>>> + ports {
>> ...
>>> + };
>>
>> As a comment not affecting my ack in any way: At the top-level, we place all
>> the child nodes into "array container" nodes named "pads" and "ports". This
>> is nice since it separates different types of child nodes and allows easily
>> adding more types of child nodes in the future without interference, and in
>> a way that allows us to explicitly know what each node is without having to
>> interpret its name or compatible value to do so. However, we haven't done
>> this with the per-lane child nodes inside each pad. If we were to rev the
>> design, I'd be tempted to suggest:
>>
>> padctl@0,7009f000 {
>> pads {
>> usb2 {
>> lanes { // This level is new
>> usb2-0 {
>
> I tried to make this work, but it's unfortunately not possible with the
> current code. The reason is that the PHY subsystem assumes that either
> the provider DT node corresponds to that of the device (the usb2 pad in
> the above example) or one of its children. Hence, putting everything
> into one more level further down would require some mechanism to tell
> the subsystem about it so that it can be found.

When the padctl driver registers the PHY objects with the PHY subsystem,
can it pass the lanes node as the DT node? That woulud mean each lane
/was/ a child of the node registered with the PHY subsystem.

Perhaps the PHY subsystem requires a struct device rather than a DT node
registered with it? If so, does it make sense to create a separate
struct device with the of_node pointing at lanes{}?

> Arguably the current support code isn't a good argument for designing a
> binding, so perhaps it'd be useful to add this mechanism in order to get
> a better binding. On the other hand, I'm not sure it's really worth it,
> since we already have generic bindings that specify the layout of child
> devices, and those have been agreed upon broadly (presumably).
>
> In light of the recent discussion on DPAUX vs. I2C, I see how having the
> extra level would be useful to provide additional context. If you think
> it's worth it I can spend the extra time to get this implemented in the
> core.

Naively, it sounds like it'd be a good idea to fix the PHY core. It
really shouldn't care about the parent of any object registered with it;
it should only interact with the specific object it was given, and any
other data such as "ops" callbacks. Do you have any inkling how much
work that would be?

2016-04-06 12:43:52

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: Re: [PATCH v10 4/9] phy: Add Tegra XUSB pad controller support

Hi,

On Friday 04 March 2016 09:49 PM, Thierry Reding wrote:
> From: Thierry Reding <[email protected]>
>
> Add a new driver for the XUSB pad controller found on NVIDIA Tegra SoCs.
> This hardware block used to be exposed as a pin controller, but it turns
> out that this isn't a good fit. The new driver and DT binding much more
> accurately describe the hardware and are more flexible in supporting new
> SoC generations.
>
> Signed-off-by: Thierry Reding <[email protected]>
> ---
> Changes in v9:
> - export public API for direct use by the xHCI driver (replaces mailbox
> API which had introduced a nasty circular dependency)
>
> drivers/phy/Kconfig | 2 +
> drivers/phy/Makefile | 2 +
> drivers/phy/tegra/Kconfig | 8 +
> drivers/phy/tegra/Makefile | 5 +
> drivers/phy/tegra/xusb-tegra124.c | 1747 ++++++++++++++++++++++++++++
> drivers/phy/tegra/xusb.c | 1017 ++++++++++++++++
> drivers/phy/tegra/xusb.h | 421 +++++++
> drivers/pinctrl/tegra/pinctrl-tegra-xusb.c | 20 +-
> include/linux/phy/tegra/xusb.h | 30 +
> 9 files changed, 3236 insertions(+), 16 deletions(-)
> create mode 100644 drivers/phy/tegra/Kconfig
> create mode 100644 drivers/phy/tegra/Makefile
> create mode 100644 drivers/phy/tegra/xusb-tegra124.c
> create mode 100644 drivers/phy/tegra/xusb.c
> create mode 100644 drivers/phy/tegra/xusb.h
> create mode 100644 include/linux/phy/tegra/xusb.h
>
> diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
> index 0124d17bd9fe..4bf65ceb3250 100644
> --- a/drivers/phy/Kconfig
> +++ b/drivers/phy/Kconfig
> @@ -407,4 +407,6 @@ config PHY_CYGNUS_PCIE
> Enable this to support the Broadcom Cygnus PCIe PHY.
> If unsure, say N.
>
> +source "drivers/phy/tegra/Kconfig"
> +
> endmenu
> diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
> index c80f09df3bb8..82709141d072 100644
> --- a/drivers/phy/Makefile
> +++ b/drivers/phy/Makefile
> @@ -50,3 +50,5 @@ obj-$(CONFIG_PHY_TUSB1210) += phy-tusb1210.o
> obj-$(CONFIG_PHY_BRCMSTB_SATA) += phy-brcmstb-sata.o
> obj-$(CONFIG_PHY_PISTACHIO_USB) += phy-pistachio-usb.o
> obj-$(CONFIG_PHY_CYGNUS_PCIE) += phy-bcm-cygnus-pcie.o
> +
> +obj-$(CONFIG_ARCH_TEGRA) += tegra/
> diff --git a/drivers/phy/tegra/Kconfig b/drivers/phy/tegra/Kconfig
> new file mode 100644
> index 000000000000..a3b1de953fb7
> --- /dev/null
> +++ b/drivers/phy/tegra/Kconfig
> @@ -0,0 +1,8 @@
> +config PHY_TEGRA_XUSB
> + tristate "NVIDIA Tegra XUSB pad controller driver"
> + depends on ARCH_TEGRA
> + help
> + Choose this option if you have an NVIDIA Tegra SoC.
> +
> + To compile this driver as a module, choose M here: the module will
> + be called phy-tegra-xusb.
> diff --git a/drivers/phy/tegra/Makefile b/drivers/phy/tegra/Makefile
> new file mode 100644
> index 000000000000..31150b4337cd
> --- /dev/null
> +++ b/drivers/phy/tegra/Makefile
> @@ -0,0 +1,5 @@
> +obj-$(CONFIG_PHY_TEGRA_XUSB) += phy-tegra-xusb.o
> +
> +phy-tegra-xusb-y += xusb.o
> +phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_124_SOC) += xusb-tegra124.o
> +phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_132_SOC) += xusb-tegra124.o
> diff --git a/drivers/phy/tegra/xusb-tegra124.c b/drivers/phy/tegra/xusb-tegra124.c
> new file mode 100644
> index 000000000000..6340d43688d3
> --- /dev/null
> +++ b/drivers/phy/tegra/xusb-tegra124.c
> @@ -0,0 +1,1747 @@
> +/*
> + * Copyright (c) 2014, NVIDIA CORPORATION. All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + * more details.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include <linux/mailbox_client.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/phy/phy.h>
> +#include <linux/platform_device.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/reset.h>
> +#include <linux/slab.h>
> +
> +#include <soc/tegra/fuse.h>
> +
> +#include "xusb.h"
> +
> +#define FUSE_SKU_CALIB_HS_CURR_LEVEL_PADX_SHIFT(x) ((x) ? 15 : 0)
> +#define FUSE_SKU_CALIB_HS_CURR_LEVEL_PAD_MASK 0x3f
> +#define FUSE_SKU_CALIB_HS_IREF_CAP_SHIFT 13
> +#define FUSE_SKU_CALIB_HS_IREF_CAP_MASK 0x3
> +#define FUSE_SKU_CALIB_HS_SQUELCH_LEVEL_SHIFT 11
> +#define FUSE_SKU_CALIB_HS_SQUELCH_LEVEL_MASK 0x3
> +#define FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_SHIFT 7
> +#define FUSE_SKU_CALIB_HS_TERM_RANGE_ADJ_MASK 0xf
> +
> +#define XUSB_PADCTL_USB2_PORT_CAP 0x008
> +#define XUSB_PADCTL_USB2_PORT_CAP_PORTX_CAP_SHIFT(x) ((x) * 4)
> +#define XUSB_PADCTL_USB2_PORT_CAP_PORT_CAP_MASK 0x3
> +#define XUSB_PADCTL_USB2_PORT_CAP_DISABLED 0x0
> +#define XUSB_PADCTL_USB2_PORT_CAP_HOST 0x1
> +#define XUSB_PADCTL_USB2_PORT_CAP_DEVICE 0x2
> +#define XUSB_PADCTL_USB2_PORT_CAP_OTG 0x3
> +
> +#define XUSB_PADCTL_SS_PORT_MAP 0x014
> +#define XUSB_PADCTL_SS_PORT_MAP_PORTX_INTERNAL(x) (1 << (((x) * 4) + 3))
> +#define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_SHIFT(x) ((x) * 4)
> +#define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP_MASK(x) (0x7 << ((x) * 4))
> +#define XUSB_PADCTL_SS_PORT_MAP_PORTX_MAP(x, v) (((v) & 0x7) << ((x) * 4))
> +#define XUSB_PADCTL_SS_PORT_MAP_PORT_MAP_MASK 0x7
> +
> +#define XUSB_PADCTL_ELPG_PROGRAM 0x01c
> +#define XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_VCORE_DOWN (1 << 26)
> +#define XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN_EARLY (1 << 25)
> +#define XUSB_PADCTL_ELPG_PROGRAM_AUX_MUX_LP0_CLAMP_EN (1 << 24)
> +#define XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_VCORE_DOWN(x) (1 << (18 + (x) * 4))
> +#define XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN_EARLY(x) \
> + (1 << (17 + (x) * 4))
> +#define XUSB_PADCTL_ELPG_PROGRAM_SSPX_ELPG_CLAMP_EN(x) (1 << (16 + (x) * 4))
> +
> +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL1 0x040
> +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL0_LOCKDET (1 << 19)
> +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL1_REFCLK_SEL_MASK (0xf << 12)
> +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL_RST (1 << 1)
> +
> +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL2 0x044
> +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL2_REFCLKBUF_EN (1 << 6)
> +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL2_TXCLKREF_EN (1 << 5)
> +#define XUSB_PADCTL_IOPHY_PLL_P0_CTL2_TXCLKREF_SEL (1 << 4)
> +
> +#define XUSB_PADCTL_IOPHY_USB3_PADX_CTL2(x) (0x058 + (x) * 4)
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_SHIFT 24
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_MASK 0xff
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_CDR_CNTL_VAL 0x24
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_SHIFT 16
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_Z_MASK 0x3f
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_SHIFT 8
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_G_MASK 0x3f
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_SHIFT 8
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_MASK 0xffff
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_EQ_VAL 0xf070
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_SHIFT 4
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_MASK 0xf
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL2_RX_WANDER_VAL 0xf
> +
> +#define XUSB_PADCTL_IOPHY_USB3_PADX_CTL4(x) (0x068 + (x) * 4)
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_SHIFT 24
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_TAP_MASK 0x1f
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_SHIFT 16
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_AMP_MASK 0x7f
> +#define XUSB_PADCTL_IOPHY_USB3_PAD_CTL4_DFE_CNTL_VAL 0x002008ee
> +
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL2(x) ((x) < 2 ? 0x078 + (x) * 4 : \
> + 0x0f8 + (x) * 4)
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_SHIFT 28
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_MASK 0x3
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL2_SPARE_IN_VAL 0x1
> +
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL5(x) ((x) < 2 ? 0x090 + (x) * 4 : \
> + 0x11c + (x) * 4)
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL5_RX_QEYE_EN (1 << 8)
> +
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_PX_CTL6(x) ((x) < 2 ? 0x098 + (x) * 4 : \
> + 0x128 + (x) * 4)
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SHIFT 24
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_G_Z_MASK 0x3f
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_TAP_MASK 0x1f
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_AMP_MASK 0x7f
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_SHIFT 16
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_MASK 0xff
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_G_Z 0x21
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_TAP 0x32
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_AMP 0x33
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_CTLE_Z 0x48
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_CTL6_MISC_OUT_SEL_LATCH_G_Z 0xa1
> +
> +#define XUSB_PADCTL_USB2_OTG_PADX_CTL0(x) (0x0a0 + (x) * 4)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD_ZI (1 << 21)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD2 (1 << 20)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_PD (1 << 19)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_SHIFT 14
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_MASK 0x3
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_LS_RSLEW_VAL(x) ((x) ? 0x0 : 0x3)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_SHIFT 6
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_MASK 0x3f
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_SLEW_VAL 0x0e
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_SHIFT 0
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL0_HS_CURR_LEVEL_MASK 0x3f
> +
> +#define XUSB_PADCTL_USB2_OTG_PADX_CTL1(x) (0x0ac + (x) * 4)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_HS_IREF_CAP_SHIFT 9
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_HS_IREF_CAP_MASK 0x3
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_SHIFT 3
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_TERM_RANGE_ADJ_MASK 0x7
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DR (1 << 2)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_DISC_FORCE_POWERUP (1 << 1)
> +#define XUSB_PADCTL_USB2_OTG_PAD_CTL1_PD_CHRP_FORCE_POWERUP (1 << 0)
> +
> +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0 0x0b8
> +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_PD (1 << 12)
> +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_SHIFT 2
> +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_MASK 0x7
> +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_DISCON_LEVEL_VAL 0x5
> +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_SHIFT 0
> +#define XUSB_PADCTL_USB2_BIAS_PAD_CTL0_HS_SQUELCH_LEVEL_MASK 0x3
> +
> +#define XUSB_PADCTL_HSIC_PADX_CTL0(x) (0x0c0 + (x) * 4)
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_SHIFT 12
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWN_MASK 0x7
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_SHIFT 8
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RSLEWP_MASK 0x7
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_SHIFT 4
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEN_MASK 0x7
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_SHIFT 0
> +#define XUSB_PADCTL_HSIC_PAD_CTL0_TX_RTUNEP_MASK 0x7
> +
> +#define XUSB_PADCTL_HSIC_PADX_CTL1(x) (0x0c8 + (x) * 4)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_RPU_STROBE (1 << 10)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_RPU_DATA (1 << 9)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_RPD_STROBE (1 << 8)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_RPD_DATA (1 << 7)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_PD_ZI (1 << 5)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_PD_RX (1 << 4)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_PD_TRX (1 << 3)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_PD_TX (1 << 2)
> +#define XUSB_PADCTL_HSIC_PAD_CTL1_AUTO_TERM_EN (1 << 0)
> +
> +#define XUSB_PADCTL_HSIC_PADX_CTL2(x) (0x0d0 + (x) * 4)
> +#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_SHIFT 4
> +#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_STROBE_TRIM_MASK 0x7
> +#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_SHIFT 0
> +#define XUSB_PADCTL_HSIC_PAD_CTL2_RX_DATA_TRIM_MASK 0x7
> +
> +#define XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL 0x0e0
> +#define XUSB_PADCTL_HSIC_STRB_TRIM_CONTROL_STRB_TRIM_MASK 0x1f
> +
> +#define XUSB_PADCTL_USB3_PAD_MUX 0x134
> +#define XUSB_PADCTL_USB3_PAD_MUX_PCIE_IDDQ_DISABLE(x) (1 << (1 + (x)))
> +#define XUSB_PADCTL_USB3_PAD_MUX_SATA_IDDQ_DISABLE(x) (1 << (6 + (x)))
> +
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1 0x138
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_LOCKDET (1 << 27)
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_MODE (1 << 24)
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL0_REFCLK_NDIV_SHIFT 20
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL0_REFCLK_NDIV_MASK 0x3
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_PWR_OVRD (1 << 3)
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_RST (1 << 1)
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL_IDDQ (1 << 0)
> +
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2 0x13c
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL1_CP_CNTL_SHIFT 20
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL1_CP_CNTL_MASK 0xf
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL0_CP_CNTL_SHIFT 16
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_PLL0_CP_CNTL_MASK 0xf
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_TCLKOUT_EN (1 << 12)
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_TXCLKREF_SEL (1 << 4)
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_XDIGCLK_SEL_SHIFT 0
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL2_XDIGCLK_SEL_MASK 0x7
> +
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL3 0x140
> +#define XUSB_PADCTL_IOPHY_PLL_S0_CTL3_RCAL_BYPASS (1 << 7)
> +
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1 0x148
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ_OVRD (1 << 1)
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL1_IDDQ (1 << 0)
> +
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL2 0x14c
> +
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL5 0x158
> +
> +#define XUSB_PADCTL_IOPHY_MISC_PAD_S0_CTL6 0x15c
> +
> +struct tegra124_xusb_fuse_calibration {
> + u32 hs_curr_level[3];
> + u32 hs_iref_cap;
> + u32 hs_term_range_adj;
> + u32 hs_squelch_level;
> +};

All these calibration data can come from dt and a generic PHY function to set
these data to registers.

But before that can be done we have to standardize the bindings for every
calibration data and then add a generic PHY function :-/ That might take some
time to add, so for now I'm okay to have all calibration data in driver.
> +
> +struct tegra124_xusb_padctl {
> + struct tegra_xusb_padctl base;
> +
> + struct tegra124_xusb_fuse_calibration fuse;
> +};
> +
> +static inline struct tegra124_xusb_padctl *
> +to_tegra124_xusb_padctl(struct tegra_xusb_padctl *padctl)
> +{
> + return container_of(padctl, struct tegra124_xusb_padctl, base);
> +}
> +
.
.
<snip>
.
.

> +
> +static const char * const tegra124_ulpi_functions[] = {
> + "snps",
> + "xusb",
> +};
> +
> +static const struct tegra_xusb_lane_soc tegra124_ulpi_lanes[] = {
> + TEGRA124_LANE("ulpi-0", 0x004, 12, 0x1, ulpi),
> +};
> +
> +static struct tegra_xusb_lane *
> +tegra124_ulpi_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np,
> + unsigned int index)
> +{
> + struct tegra_xusb_ulpi_lane *ulpi;
> + int err;
> +
> + ulpi = kzalloc(sizeof(*ulpi), GFP_KERNEL);
> + if (!ulpi)
> + return ERR_PTR(-ENOMEM);
> +
> + INIT_LIST_HEAD(&ulpi->base.list);
> + ulpi->base.soc = &pad->soc->lanes[index];
> + ulpi->base.index = index;
> + ulpi->base.pad = pad;
> + ulpi->base.np = np;
> +

ulpi PHY's can be found dynamically right? Should this use the ulpi phy library?

Thanks
Kishon

2016-04-06 17:08:33

by Thierry Reding

[permalink] [raw]
Subject: Re: [PATCH v10 3/9] dt-bindings: phy: tegra-xusb-padctl: Add Tegra210 support

On Tue, Apr 05, 2016 at 03:10:16PM -0600, Stephen Warren wrote:
> On 04/05/2016 08:44 AM, Thierry Reding wrote:
> > On Wed, Mar 16, 2016 at 11:59:44AM -0600, Stephen Warren wrote:
> > > On 03/04/2016 09:19 AM, Thierry Reding wrote:
> > > > From: Thierry Reding <[email protected]>
> > > >
> > > > Extend the binding to cover the set of feature found in Tegra210.
> > >
> > > Acked-by: Stephen Warren <[email protected]>
> > >
> > > > diff --git a/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
> > >
> > > > + padctl@0,7009f000 {
> > > ...
> > > > + pads {
> > > ...
> > > > + };
> > > > +
> > > > + ports {
> > > ...
> > > > + };
> > >
> > > As a comment not affecting my ack in any way: At the top-level, we place all
> > > the child nodes into "array container" nodes named "pads" and "ports". This
> > > is nice since it separates different types of child nodes and allows easily
> > > adding more types of child nodes in the future without interference, and in
> > > a way that allows us to explicitly know what each node is without having to
> > > interpret its name or compatible value to do so. However, we haven't done
> > > this with the per-lane child nodes inside each pad. If we were to rev the
> > > design, I'd be tempted to suggest:
> > >
> > > padctl@0,7009f000 {
> > > pads {
> > > usb2 {
> > > lanes { // This level is new
> > > usb2-0 {
> >
> > I tried to make this work, but it's unfortunately not possible with the
> > current code. The reason is that the PHY subsystem assumes that either
> > the provider DT node corresponds to that of the device (the usb2 pad in
> > the above example) or one of its children. Hence, putting everything
> > into one more level further down would require some mechanism to tell
> > the subsystem about it so that it can be found.
>
> When the padctl driver registers the PHY objects with the PHY subsystem, can
> it pass the lanes node as the DT node? That woulud mean each lane /was/ a
> child of the node registered with the PHY subsystem.
>
> Perhaps the PHY subsystem requires a struct device rather than a DT node
> registered with it?

Yes, that's how the PHY subsystem works. You pass it a struct device *
that it will wrap a struct phy_provider around. It will then use that
struct device's ->of_node as the parent for the child lookup.

> If so, does it make sense to create a separate struct device with the
> of_node pointing at lanes{}?

I suspect that that would work, but I'm already somewhat uncomfortable
about how many devices the driver creates. Adding more dummy devices
seems like a workaround.

> > Arguably the current support code isn't a good argument for designing a
> > binding, so perhaps it'd be useful to add this mechanism in order to get
> > a better binding. On the other hand, I'm not sure it's really worth it,
> > since we already have generic bindings that specify the layout of child
> > devices, and those have been agreed upon broadly (presumably).
> >
> > In light of the recent discussion on DPAUX vs. I2C, I see how having the
> > extra level would be useful to provide additional context. If you think
> > it's worth it I can spend the extra time to get this implemented in the
> > core.
>
> Naively, it sounds like it'd be a good idea to fix the PHY core. It really
> shouldn't care about the parent of any object registered with it; it should
> only interact with the specific object it was given, and any other data such
> as "ops" callbacks. Do you have any inkling how much work that would be?

I attached what I came up with. It extends the OF PHY provider registry
by allowing an additional node to be specified that if specified will
serve as the parent for the child lookup (and hence overrides the
default node that's taken from the struct device).

It is a fairly trivial patch, and you'll notice the bulk of the changes
is adding the additional parameter in a number of different places. The
only thing I'm not quite happy about is that we start needing to pass a
fairly large number of arguments. But perhaps it's still okay.

An alternative would be to make struct phy_provider embeddable into
driver private structures. That way they could be completely initialized
by a driver before being passed to the __of_phy_provider_register()
function (much like struct gpio_chip and others). That would be a fairly
intrusive change, one that I'd be willing to take on, though I'd like to
have Kishon's opinion on this before going ahead.

For reference, here's what I'm imagining:

struct foo_phy_provider {
struct phy_provider base;
...
};

int foo_probe(struct platform_device *pdev)
{
struct foo_phy_provider *priv;
...

priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;

priv->base.dev = &pdev->dev;
priv->base.of_xlate = foo_of_xlate;

err = devm_of_phy_provider_register(&priv->base);
if (err < 0)
return err;

...
}

And of course adapt the signature of the __of_phy_provider_register()
function and remove the allocation from it.

Thierry

--- >8 ---
From 15e5348a1a63837efd00309fdce5cda979498f77 Mon Sep 17 00:00:00 2001
From: Thierry Reding <[email protected]>
Date: Tue, 5 Apr 2016 17:17:34 +0200
Subject: [PATCH] phy: core: Allow children node to be overridden

In order to more flexibly support device tree bindings, allow drivers to
override the container of the child nodes. By default the device node of
the PHY provider is assumed to be the parent for children, but bindings
may decide to add additional levels for better organization.

Signed-off-by: Thierry Reding <[email protected]>
---
Documentation/phy.txt | 16 ++++++++++++++--
drivers/phy/phy-core.c | 27 +++++++++++++++++++++------
include/linux/phy/phy.h | 31 +++++++++++++++++++++----------
3 files changed, 56 insertions(+), 18 deletions(-)

diff --git a/Documentation/phy.txt b/Documentation/phy.txt
index b388c5af9e72..0aa994bd9a91 100644
--- a/Documentation/phy.txt
+++ b/Documentation/phy.txt
@@ -31,16 +31,28 @@ should provide its own implementation of of_xlate. of_xlate is used only for
dt boot case.

#define of_phy_provider_register(dev, xlate) \
- __of_phy_provider_register((dev), THIS_MODULE, (xlate))
+ __of_phy_provider_register((dev), NULL, THIS_MODULE, (xlate))

#define devm_of_phy_provider_register(dev, xlate) \
- __devm_of_phy_provider_register((dev), THIS_MODULE, (xlate))
+ __devm_of_phy_provider_register((dev), NULL, THIS_MODULE, (xlate))

of_phy_provider_register and devm_of_phy_provider_register macros can be used to
register the phy_provider and it takes device and of_xlate as
arguments. For the dt boot case, all PHY providers should use one of the above
2 macros to register the PHY provider.

+Often the device tree nodes associated with a PHY provider will contain a set
+of children that each represent a single PHY. Some bindings may nest the child
+nodes within extra levels for context and extensibility, in which case the low
+level of_phy_provider_register_full() and devm_of_phy_provider_register_full()
+macros can be used to override the node containing the children.
+
+#define of_phy_provider_register_full(dev, children, xlate) \
+ __of_phy_provider_register(dev, children, THIS_MODULE, xlate)
+
+#define devm_of_phy_provider_register_full(dev, children, xlate) \
+ __devm_of_phy_provider_register_full(dev, children, THIS_MODULE, xlate)
+
void devm_of_phy_provider_unregister(struct device *dev,
struct phy_provider *phy_provider);
void of_phy_provider_unregister(struct phy_provider *phy_provider);
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
index e7e574dc667a..7f7da2138c82 100644
--- a/drivers/phy/phy-core.c
+++ b/drivers/phy/phy-core.c
@@ -135,13 +135,19 @@ static struct phy *phy_find(struct device *dev, const char *con_id)
static struct phy_provider *of_phy_provider_lookup(struct device_node *node)
{
struct phy_provider *phy_provider;
+ struct device_node *children;
struct device_node *child;

list_for_each_entry(phy_provider, &phy_provider_list, list) {
if (phy_provider->dev->of_node == node)
return phy_provider;

- for_each_child_of_node(phy_provider->dev->of_node, child)
+ if (!phy_provider->children)
+ children = phy_provider->dev->of_node;
+ else
+ children = phy_provider->children;
+
+ for_each_child_of_node(children, child)
if (child == node)
return phy_provider;
}
@@ -811,16 +817,22 @@ EXPORT_SYMBOL_GPL(devm_phy_destroy);
/**
* __of_phy_provider_register() - create/register phy provider with the framework
* @dev: struct device of the phy provider
+ * @children: device node containing children (if different from dev->of_node)
* @owner: the module owner containing of_xlate
* @of_xlate: function pointer to obtain phy instance from phy provider
*
* Creates struct phy_provider from dev and of_xlate function pointer.
* This is used in the case of dt boot for finding the phy instance from
* phy provider.
+ *
+ * If the PHY provider doesn't nest children directly but uses a separate
+ * child node to contain the individual children, the @children parameter
+ * can be used to override the default (i.e. dev->of_node).
*/
struct phy_provider *__of_phy_provider_register(struct device *dev,
- struct module *owner, struct phy * (*of_xlate)(struct device *dev,
- struct of_phandle_args *args))
+ struct device_node *children, struct module *owner,
+ struct phy * (*of_xlate)(struct device *dev,
+ struct of_phandle_args *args))
{
struct phy_provider *phy_provider;

@@ -829,6 +841,7 @@ struct phy_provider *__of_phy_provider_register(struct device *dev,
return ERR_PTR(-ENOMEM);

phy_provider->dev = dev;
+ phy_provider->children = children;
phy_provider->owner = owner;
phy_provider->of_xlate = of_xlate;

@@ -854,8 +867,9 @@ EXPORT_SYMBOL_GPL(__of_phy_provider_register);
* on the devres data, then, devres data is freed.
*/
struct phy_provider *__devm_of_phy_provider_register(struct device *dev,
- struct module *owner, struct phy * (*of_xlate)(struct device *dev,
- struct of_phandle_args *args))
+ struct device_node *children, struct module *owner,
+ struct phy * (*of_xlate)(struct device *dev,
+ struct of_phandle_args *args))
{
struct phy_provider **ptr, *phy_provider;

@@ -863,7 +877,8 @@ struct phy_provider *__devm_of_phy_provider_register(struct device *dev,
if (!ptr)
return ERR_PTR(-ENOMEM);

- phy_provider = __of_phy_provider_register(dev, owner, of_xlate);
+ phy_provider = __of_phy_provider_register(dev, children, owner,
+ of_xlate);
if (!IS_ERR(phy_provider)) {
*ptr = phy_provider;
devres_add(dev, ptr);
diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
index 8cf05e341cff..a810f2a18842 100644
--- a/include/linux/phy/phy.h
+++ b/include/linux/phy/phy.h
@@ -77,6 +77,7 @@ struct phy {
*/
struct phy_provider {
struct device *dev;
+ struct device_node *children;
struct module *owner;
struct list_head list;
struct phy * (*of_xlate)(struct device *dev,
@@ -93,10 +94,16 @@ struct phy_lookup {
#define to_phy(a) (container_of((a), struct phy, dev))

#define of_phy_provider_register(dev, xlate) \
- __of_phy_provider_register((dev), THIS_MODULE, (xlate))
+ __of_phy_provider_register((dev), NULL, THIS_MODULE, (xlate))

#define devm_of_phy_provider_register(dev, xlate) \
- __devm_of_phy_provider_register((dev), THIS_MODULE, (xlate))
+ __devm_of_phy_provider_register((dev), NULL, THIS_MODULE, (xlate))
+
+#define of_phy_provider_register_full(dev, children, xlate) \
+ __of_phy_provider_register(dev, children, THIS_MODULE, xlate)
+
+#define devm_of_phy_provider_register_full(dev, children, xlate) \
+ __devm_of_phy_provider_register(dev, children, THIS_MODULE, xlate)

static inline void phy_set_drvdata(struct phy *phy, void *data)
{
@@ -147,11 +154,13 @@ struct phy *devm_phy_create(struct device *dev, struct device_node *node,
void phy_destroy(struct phy *phy);
void devm_phy_destroy(struct device *dev, struct phy *phy);
struct phy_provider *__of_phy_provider_register(struct device *dev,
- struct module *owner, struct phy * (*of_xlate)(struct device *dev,
- struct of_phandle_args *args));
+ struct device_node *children, struct module *owner,
+ struct phy * (*of_xlate)(struct device *dev,
+ struct of_phandle_args *args));
struct phy_provider *__devm_of_phy_provider_register(struct device *dev,
- struct module *owner, struct phy * (*of_xlate)(struct device *dev,
- struct of_phandle_args *args));
+ struct device_node *children, struct module *owner,
+ struct phy * (*of_xlate)(struct device *dev,
+ struct of_phandle_args *args));
void of_phy_provider_unregister(struct phy_provider *phy_provider);
void devm_of_phy_provider_unregister(struct device *dev,
struct phy_provider *phy_provider);
@@ -312,15 +321,17 @@ static inline void devm_phy_destroy(struct device *dev, struct phy *phy)
}

static inline struct phy_provider *__of_phy_provider_register(
- struct device *dev, struct module *owner, struct phy * (*of_xlate)(
- struct device *dev, struct of_phandle_args *args))
+ struct device *dev, struct device_node *children, struct module *owner,
+ struct phy * (*of_xlate)(struct device *dev,
+ struct of_phandle_args *args))
{
return ERR_PTR(-ENOSYS);
}

static inline struct phy_provider *__devm_of_phy_provider_register(struct device
- *dev, struct module *owner, struct phy * (*of_xlate)(struct device *dev,
- struct of_phandle_args *args))
+ *dev, struct device_node *children, struct module *owner,
+ struct phy * (*of_xlate)(struct device *dev,
+ struct of_phandle_args *args))
{
return ERR_PTR(-ENOSYS);
}
--
2.8.0


Attachments:
(No filename) (13.46 kB)
signature.asc (819.00 B)
Download all attachments

2016-04-06 17:26:25

by Thierry Reding

[permalink] [raw]
Subject: Re: [PATCH v10 4/9] phy: Add Tegra XUSB pad controller support

On Wed, Apr 06, 2016 at 06:13:42PM +0530, Kishon Vijay Abraham I wrote:
> On Friday 04 March 2016 09:49 PM, Thierry Reding wrote:
[...]
> > +struct tegra124_xusb_fuse_calibration {
> > + u32 hs_curr_level[3];
> > + u32 hs_iref_cap;
> > + u32 hs_term_range_adj;
> > + u32 hs_squelch_level;
> > +};
>
> All these calibration data can come from dt and a generic PHY function to set
> these data to registers.

This calibration data is actually read from fuses within the chip. As I
understand it the process is that these values are characterized during
chip development and written to the fuses at the fab (or perhaps they
are characterized even as late as at the fab). There should be no need
to read these from DT.

> > +static const char * const tegra124_ulpi_functions[] = {
> > + "snps",
> > + "xusb",
> > +};
> > +
> > +static const struct tegra_xusb_lane_soc tegra124_ulpi_lanes[] = {
> > + TEGRA124_LANE("ulpi-0", 0x004, 12, 0x1, ulpi),
> > +};
> > +
> > +static struct tegra_xusb_lane *
> > +tegra124_ulpi_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np,
> > + unsigned int index)
> > +{
> > + struct tegra_xusb_ulpi_lane *ulpi;
> > + int err;
> > +
> > + ulpi = kzalloc(sizeof(*ulpi), GFP_KERNEL);
> > + if (!ulpi)
> > + return ERR_PTR(-ENOMEM);
> > +
> > + INIT_LIST_HEAD(&ulpi->base.list);
> > + ulpi->base.soc = &pad->soc->lanes[index];
> > + ulpi->base.index = index;
> > + ulpi->base.pad = pad;
> > + ulpi->base.np = np;
> > +
>
> ulpi PHY's can be found dynamically right? Should this use the ulpi
> phy library?

I don't think that would work here. The registered accessed by this code
are all very Tegra specific as far as I can tell. I doubt that any kind
of generic library would work here.

Perhaps you can point me at the exact code you're thinking of. I only
found drivers/phy/ulpi_phy.h and drivers/usb/common/ulpi.c in a quick
search, neither of which seem to provide anything that would be useful
in this context. The former contains a couple of small helpers that I
don't think are appropriate here, whereas the latter seems to want the
driver to implement a ULPI interface, something which the Tegra XUSB pad
controller doesn't expose.

Thierry


Attachments:
(No filename) (2.14 kB)
signature.asc (819.00 B)
Download all attachments

2016-04-07 09:33:06

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: Re: [PATCH v10 4/9] phy: Add Tegra XUSB pad controller support

Hi,

On Wednesday 06 April 2016 10:56 PM, Thierry Reding wrote:
> On Wed, Apr 06, 2016 at 06:13:42PM +0530, Kishon Vijay Abraham I wrote:
>> On Friday 04 March 2016 09:49 PM, Thierry Reding wrote:
> [...]
>>> +struct tegra124_xusb_fuse_calibration {
>>> + u32 hs_curr_level[3];
>>> + u32 hs_iref_cap;
>>> + u32 hs_term_range_adj;
>>> + u32 hs_squelch_level;
>>> +};
>>
>> All these calibration data can come from dt and a generic PHY function to set
>> these data to registers.
>
> This calibration data is actually read from fuses within the chip. As I
> understand it the process is that these values are characterized during
> chip development and written to the fuses at the fab (or perhaps they
> are characterized even as late as at the fab). There should be no need
> to read these from DT.
>
>>> +static const char * const tegra124_ulpi_functions[] = {
>>> + "snps",
>>> + "xusb",
>>> +};
>>> +
>>> +static const struct tegra_xusb_lane_soc tegra124_ulpi_lanes[] = {
>>> + TEGRA124_LANE("ulpi-0", 0x004, 12, 0x1, ulpi),
>>> +};
>>> +
>>> +static struct tegra_xusb_lane *
>>> +tegra124_ulpi_lane_probe(struct tegra_xusb_pad *pad, struct device_node *np,
>>> + unsigned int index)
>>> +{
>>> + struct tegra_xusb_ulpi_lane *ulpi;
>>> + int err;
>>> +
>>> + ulpi = kzalloc(sizeof(*ulpi), GFP_KERNEL);
>>> + if (!ulpi)
>>> + return ERR_PTR(-ENOMEM);
>>> +
>>> + INIT_LIST_HEAD(&ulpi->base.list);
>>> + ulpi->base.soc = &pad->soc->lanes[index];
>>> + ulpi->base.index = index;
>>> + ulpi->base.pad = pad;
>>> + ulpi->base.np = np;
>>> +
>>
>> ulpi PHY's can be found dynamically right? Should this use the ulpi
>> phy library?
>
> I don't think that would work here. The registered accessed by this code
> are all very Tegra specific as far as I can tell. I doubt that any kind
> of generic library would work here.
>
> Perhaps you can point me at the exact code you're thinking of. I only
> found drivers/phy/ulpi_phy.h and drivers/usb/common/ulpi.c in a quick
> search, neither of which seem to provide anything that would be useful
> in this context. The former contains a couple of small helpers that I
> don't think are appropriate here, whereas the latter seems to want the
> driver to implement a ULPI interface, something which the Tegra XUSB pad
> controller doesn't expose.

All right then.

FWIW:
Acked-by: Kishon Vijay Abraham I <[email protected]>
>
> Thierry
>

2016-04-07 10:57:22

by Mathias Nyman

[permalink] [raw]
Subject: Re: [PATCH v10 8/9] usb: xhci: Add NVIDIA Tegra XUSB controller driver

On 04.03.2016 18:19, Thierry Reding wrote:
> From: Thierry Reding <[email protected]>
>
> Add support for the on-chip XUSB controller present on Tegra SoCs. This
> controller, when loaded with external firmware, exposes an interface
> compliant with xHCI. This driver loads the firmware, starts the
> controller, and is able to service host-specific messages sent by the
> controller's firmware.
>
> The controller also supports USB device mode as well as powergating
> of the SuperSpeed and host-controller logic when not in use, but
> support for these is not yet implemented.
>
> Based on work by:
> Ajay Gupta <[email protected]>
> Bharath Yadav <[email protected]>
> Andrew Bresticker <[email protected]>
>
> Cc: Greg Kroah-Hartman <[email protected]>
> Cc: Mathias Nyman <[email protected]>
> Signed-off-by: Thierry Reding <[email protected]>
> ---

...

> +static int tegra_xusb_remove(struct platform_device *pdev)
> +{
> + struct tegra_xusb *tegra = platform_get_drvdata(pdev);
> + struct usb_hcd *hcd = tegra->hcd;
> + struct xhci_hcd *xhci;
> +
> + if (tegra->fw_loaded) {
> + xhci = hcd_to_xhci(hcd);
> + usb_remove_hcd(xhci->shared_hcd);
> + usb_put_hcd(xhci->shared_hcd);
> + usb_remove_hcd(hcd);
> + tegra_xusb_padctl_put(tegra->padctl);
> + usb_put_hcd(hcd);
> + kfree(xhci);

Don't free xhci here, xhci is a part of hcd (hcd_priv) and hcd is already freed in usb_put_hcd()

Other than that I can't see any issues

-Mathias


2016-04-07 11:05:10

by Thierry Reding

[permalink] [raw]
Subject: Re: [PATCH v10 8/9] usb: xhci: Add NVIDIA Tegra XUSB controller driver

On Thu, Apr 07, 2016 at 02:03:45PM +0300, Mathias Nyman wrote:
> On 04.03.2016 18:19, Thierry Reding wrote:
> > From: Thierry Reding <[email protected]>
> >
> > Add support for the on-chip XUSB controller present on Tegra SoCs. This
> > controller, when loaded with external firmware, exposes an interface
> > compliant with xHCI. This driver loads the firmware, starts the
> > controller, and is able to service host-specific messages sent by the
> > controller's firmware.
> >
> > The controller also supports USB device mode as well as powergating
> > of the SuperSpeed and host-controller logic when not in use, but
> > support for these is not yet implemented.
> >
> > Based on work by:
> > Ajay Gupta <[email protected]>
> > Bharath Yadav <[email protected]>
> > Andrew Bresticker <[email protected]>
> >
> > Cc: Greg Kroah-Hartman <[email protected]>
> > Cc: Mathias Nyman <[email protected]>
> > Signed-off-by: Thierry Reding <[email protected]>
> > ---
>
> ...
>
> > +static int tegra_xusb_remove(struct platform_device *pdev)
> > +{
> > + struct tegra_xusb *tegra = platform_get_drvdata(pdev);
> > + struct usb_hcd *hcd = tegra->hcd;
> > + struct xhci_hcd *xhci;
> > +
> > + if (tegra->fw_loaded) {
> > + xhci = hcd_to_xhci(hcd);
> > + usb_remove_hcd(xhci->shared_hcd);
> > + usb_put_hcd(xhci->shared_hcd);
> > + usb_remove_hcd(hcd);
> > + tegra_xusb_padctl_put(tegra->padctl);
> > + usb_put_hcd(hcd);
> > + kfree(xhci);
>
> Don't free xhci here, xhci is a part of hcd (hcd_priv) and hcd is already freed in usb_put_hcd()

Ah, this was indeed reported to me about two weeks ago and fixed since
then in my local tree.

> Other than that I can't see any issues

Thanks. Does this count as an Acked-by provided the kfree() is removed?

Thierry


Attachments:
(No filename) (1.75 kB)
signature.asc (819.00 B)
Download all attachments

2016-04-07 11:43:57

by Mathias Nyman

[permalink] [raw]
Subject: Re: [PATCH v10 8/9] usb: xhci: Add NVIDIA Tegra XUSB controller driver

On 07.04.2016 14:05, Thierry Reding wrote:
> On Thu, Apr 07, 2016 at 02:03:45PM +0300, Mathias Nyman wrote:
>> On 04.03.2016 18:19, Thierry Reding wrote:
>>> From: Thierry Reding <[email protected]>
>>>
>>> Add support for the on-chip XUSB controller present on Tegra SoCs. This
>>> controller, when loaded with external firmware, exposes an interface
>>> compliant with xHCI. This driver loads the firmware, starts the
>>> controller, and is able to service host-specific messages sent by the
>>> controller's firmware.
>>>
>>> The controller also supports USB device mode as well as powergating
>>> of the SuperSpeed and host-controller logic when not in use, but
>>> support for these is not yet implemented.
>>>
>>> Based on work by:
>>> Ajay Gupta <[email protected]>
>>> Bharath Yadav <[email protected]>
>>> Andrew Bresticker <[email protected]>
>>>
>>> Cc: Greg Kroah-Hartman <[email protected]>
>>> Cc: Mathias Nyman <[email protected]>
>>> Signed-off-by: Thierry Reding <[email protected]>
>>> ---
>>
>> ...
>>
>>> +static int tegra_xusb_remove(struct platform_device *pdev)
>>> +{
>>> + struct tegra_xusb *tegra = platform_get_drvdata(pdev);
>> > + struct usb_hcd *hcd = tegra->hcd;
>>> + struct xhci_hcd *xhci;
>>> +
>>> + if (tegra->fw_loaded) {
>>> + xhci = hcd_to_xhci(hcd);
>>> + usb_remove_hcd(xhci->shared_hcd);
>>> + usb_put_hcd(xhci->shared_hcd);
>>> + usb_remove_hcd(hcd);
>>> + tegra_xusb_padctl_put(tegra->padctl);
>>> + usb_put_hcd(hcd);
>>> + kfree(xhci);
>>
>> Don't free xhci here, xhci is a part of hcd (hcd_priv) and hcd is already freed in usb_put_hcd()
>
> Ah, this was indeed reported to me about two weeks ago and fixed since
> then in my local tree.
>
>> Other than that I can't see any issues
>
> Thanks. Does this count as an Acked-by provided the kfree() is removed?
>

yes,

Acked-by: Mathias Nyman <[email protected]>

2016-04-07 20:43:07

by Stephen Warren

[permalink] [raw]
Subject: Re: [PATCH v10 3/9] dt-bindings: phy: tegra-xusb-padctl: Add Tegra210 support

On 04/06/2016 11:08 AM, Thierry Reding wrote:
> On Tue, Apr 05, 2016 at 03:10:16PM -0600, Stephen Warren wrote:
>> On 04/05/2016 08:44 AM, Thierry Reding wrote:
>>> On Wed, Mar 16, 2016 at 11:59:44AM -0600, Stephen Warren wrote:
>>>> On 03/04/2016 09:19 AM, Thierry Reding wrote:
>>>>> From: Thierry Reding <[email protected]>
>>>>>
>>>>> Extend the binding to cover the set of feature found in Tegra210.
>>>>
>>>> Acked-by: Stephen Warren <[email protected]>
>>>>
>>>>> diff --git a/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt b/Documentation/devicetree/bindings/phy/nvidia,tegra124-xusb-padctl.txt
>>>>
>>>>> + padctl@0,7009f000 {
>>>> ...
>>>>> + pads {
>>>> ...
>>>>> + };
>>>>> +
>>>>> + ports {
>>>> ...
>>>>> + };
>>>>
>>>> As a comment not affecting my ack in any way: At the top-level, we place all
>>>> the child nodes into "array container" nodes named "pads" and "ports". This
>>>> is nice since it separates different types of child nodes and allows easily
>>>> adding more types of child nodes in the future without interference, and in
>>>> a way that allows us to explicitly know what each node is without having to
>>>> interpret its name or compatible value to do so. However, we haven't done
>>>> this with the per-lane child nodes inside each pad. If we were to rev the
>>>> design, I'd be tempted to suggest:
>>>>
>>>> padctl@0,7009f000 {
>>>> pads {
>>>> usb2 {
>>>> lanes { // This level is new
>>>> usb2-0 {
>>>
>>> I tried to make this work, but it's unfortunately not possible with the
>>> current code. The reason is that the PHY subsystem assumes that either
>>> the provider DT node corresponds to that of the device (the usb2 pad in
>>> the above example) or one of its children. Hence, putting everything
>>> into one more level further down would require some mechanism to tell
>>> the subsystem about it so that it can be found.
>>
>> When the padctl driver registers the PHY objects with the PHY subsystem, can
>> it pass the lanes node as the DT node? That woulud mean each lane /was/ a
>> child of the node registered with the PHY subsystem.
>>
>> Perhaps the PHY subsystem requires a struct device rather than a DT node
>> registered with it?
>
> Yes, that's how the PHY subsystem works. You pass it a struct device *
> that it will wrap a struct phy_provider around. It will then use that
> struct device's ->of_node as the parent for the child lookup.
>
>> If so, does it make sense to create a separate struct device with the
>> of_node pointing at lanes{}?
>
> I suspect that that would work, but I'm already somewhat uncomfortable
> about how many devices the driver creates. Adding more dummy devices
> seems like a workaround.
>
>>> Arguably the current support code isn't a good argument for designing a
>>> binding, so perhaps it'd be useful to add this mechanism in order to get
>>> a better binding. On the other hand, I'm not sure it's really worth it,
>>> since we already have generic bindings that specify the layout of child
>>> devices, and those have been agreed upon broadly (presumably).
>>>
>>> In light of the recent discussion on DPAUX vs. I2C, I see how having the
>>> extra level would be useful to provide additional context. If you think
>>> it's worth it I can spend the extra time to get this implemented in the
>>> core.
>>
>> Naively, it sounds like it'd be a good idea to fix the PHY core. It really
>> shouldn't care about the parent of any object registered with it; it should
>> only interact with the specific object it was given, and any other data such
>> as "ops" callbacks. Do you have any inkling how much work that would be?
>
> I attached what I came up with. It extends the OF PHY provider registry
> by allowing an additional node to be specified that if specified will
> serve as the parent for the child lookup (and hence overrides the
> default node that's taken from the struct device).
>
> It is a fairly trivial patch, and you'll notice the bulk of the changes
> is adding the additional parameter in a number of different places. The
> only thing I'm not quite happy about is that we start needing to pass a
> fairly large number of arguments. But perhaps it's still okay.
>
> An alternative would be to make struct phy_provider embeddable into
> driver private structures. That way they could be completely initialized
> by a driver before being passed to the __of_phy_provider_register()
> function (much like struct gpio_chip and others). That would be a fairly
> intrusive change, one that I'd be willing to take on, though I'd like to
> have Kishon's opinion on this before going ahead.

Either of those options look OK to me, and don't seem too invasive. I
imagine the amount of work is not too great given you've already
implemented the first option:-)

> For reference, here's what I'm imagining:
>
> struct foo_phy_provider {
> struct phy_provider base;
> ...
> };
>
> int foo_probe(struct platform_device *pdev)
> {
> struct foo_phy_provider *priv;
> ...
>
> priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> if (!priv)
> return -ENOMEM;
>
> priv->base.dev = &pdev->dev;
> priv->base.of_xlate = foo_of_xlate;
>
> err = devm_of_phy_provider_register(&priv->base);
> if (err < 0)
> return err;
>
> ...
> }
>
> And of course adapt the signature of the __of_phy_provider_register()
> function and remove the allocation from it.
>
> Thierry
>
> --- >8 ---
> From 15e5348a1a63837efd00309fdce5cda979498f77 Mon Sep 17 00:00:00 2001
> From: Thierry Reding <[email protected]>
> Date: Tue, 5 Apr 2016 17:17:34 +0200
> Subject: [PATCH] phy: core: Allow children node to be overridden
>
> In order to more flexibly support device tree bindings, allow drivers to
> override the container of the child nodes. By default the device node of
> the PHY provider is assumed to be the parent for children, but bindings
> may decide to add additional levels for better organization.
>
> Signed-off-by: Thierry Reding <[email protected]>
> ---
> Documentation/phy.txt | 16 ++++++++++++++--
> drivers/phy/phy-core.c | 27 +++++++++++++++++++++------
> include/linux/phy/phy.h | 31 +++++++++++++++++++++----------
> 3 files changed, 56 insertions(+), 18 deletions(-)
>
> diff --git a/Documentation/phy.txt b/Documentation/phy.txt
> index b388c5af9e72..0aa994bd9a91 100644
> --- a/Documentation/phy.txt
> +++ b/Documentation/phy.txt
> @@ -31,16 +31,28 @@ should provide its own implementation of of_xlate. of_xlate is used only for
> dt boot case.
>
> #define of_phy_provider_register(dev, xlate) \
> - __of_phy_provider_register((dev), THIS_MODULE, (xlate))
> + __of_phy_provider_register((dev), NULL, THIS_MODULE, (xlate))
>
> #define devm_of_phy_provider_register(dev, xlate) \
> - __devm_of_phy_provider_register((dev), THIS_MODULE, (xlate))
> + __devm_of_phy_provider_register((dev), NULL, THIS_MODULE, (xlate))
>
> of_phy_provider_register and devm_of_phy_provider_register macros can be used to
> register the phy_provider and it takes device and of_xlate as
> arguments. For the dt boot case, all PHY providers should use one of the above
> 2 macros to register the PHY provider.
>
> +Often the device tree nodes associated with a PHY provider will contain a set
> +of children that each represent a single PHY. Some bindings may nest the child
> +nodes within extra levels for context and extensibility, in which case the low
> +level of_phy_provider_register_full() and devm_of_phy_provider_register_full()
> +macros can be used to override the node containing the children.
> +
> +#define of_phy_provider_register_full(dev, children, xlate) \
> + __of_phy_provider_register(dev, children, THIS_MODULE, xlate)
> +
> +#define devm_of_phy_provider_register_full(dev, children, xlate) \
> + __devm_of_phy_provider_register_full(dev, children, THIS_MODULE, xlate)
> +
> void devm_of_phy_provider_unregister(struct device *dev,
> struct phy_provider *phy_provider);
> void of_phy_provider_unregister(struct phy_provider *phy_provider);
> diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
> index e7e574dc667a..7f7da2138c82 100644
> --- a/drivers/phy/phy-core.c
> +++ b/drivers/phy/phy-core.c
> @@ -135,13 +135,19 @@ static struct phy *phy_find(struct device *dev, const char *con_id)
> static struct phy_provider *of_phy_provider_lookup(struct device_node *node)
> {
> struct phy_provider *phy_provider;
> + struct device_node *children;
> struct device_node *child;
>
> list_for_each_entry(phy_provider, &phy_provider_list, list) {
> if (phy_provider->dev->of_node == node)
> return phy_provider;
>
> - for_each_child_of_node(phy_provider->dev->of_node, child)
> + if (!phy_provider->children)
> + children = phy_provider->dev->of_node;
> + else
> + children = phy_provider->children;
> +
> + for_each_child_of_node(children, child)
> if (child == node)
> return phy_provider;
> }
> @@ -811,16 +817,22 @@ EXPORT_SYMBOL_GPL(devm_phy_destroy);
> /**
> * __of_phy_provider_register() - create/register phy provider with the framework
> * @dev: struct device of the phy provider
> + * @children: device node containing children (if different from dev->of_node)
> * @owner: the module owner containing of_xlate
> * @of_xlate: function pointer to obtain phy instance from phy provider
> *
> * Creates struct phy_provider from dev and of_xlate function pointer.
> * This is used in the case of dt boot for finding the phy instance from
> * phy provider.
> + *
> + * If the PHY provider doesn't nest children directly but uses a separate
> + * child node to contain the individual children, the @children parameter
> + * can be used to override the default (i.e. dev->of_node).
> */
> struct phy_provider *__of_phy_provider_register(struct device *dev,
> - struct module *owner, struct phy * (*of_xlate)(struct device *dev,
> - struct of_phandle_args *args))
> + struct device_node *children, struct module *owner,
> + struct phy * (*of_xlate)(struct device *dev,
> + struct of_phandle_args *args))
> {
> struct phy_provider *phy_provider;
>
> @@ -829,6 +841,7 @@ struct phy_provider *__of_phy_provider_register(struct device *dev,
> return ERR_PTR(-ENOMEM);
>
> phy_provider->dev = dev;
> + phy_provider->children = children;
> phy_provider->owner = owner;
> phy_provider->of_xlate = of_xlate;
>
> @@ -854,8 +867,9 @@ EXPORT_SYMBOL_GPL(__of_phy_provider_register);
> * on the devres data, then, devres data is freed.
> */
> struct phy_provider *__devm_of_phy_provider_register(struct device *dev,
> - struct module *owner, struct phy * (*of_xlate)(struct device *dev,
> - struct of_phandle_args *args))
> + struct device_node *children, struct module *owner,
> + struct phy * (*of_xlate)(struct device *dev,
> + struct of_phandle_args *args))
> {
> struct phy_provider **ptr, *phy_provider;
>
> @@ -863,7 +877,8 @@ struct phy_provider *__devm_of_phy_provider_register(struct device *dev,
> if (!ptr)
> return ERR_PTR(-ENOMEM);
>
> - phy_provider = __of_phy_provider_register(dev, owner, of_xlate);
> + phy_provider = __of_phy_provider_register(dev, children, owner,
> + of_xlate);
> if (!IS_ERR(phy_provider)) {
> *ptr = phy_provider;
> devres_add(dev, ptr);
> diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
> index 8cf05e341cff..a810f2a18842 100644
> --- a/include/linux/phy/phy.h
> +++ b/include/linux/phy/phy.h
> @@ -77,6 +77,7 @@ struct phy {
> */
> struct phy_provider {
> struct device *dev;
> + struct device_node *children;
> struct module *owner;
> struct list_head list;
> struct phy * (*of_xlate)(struct device *dev,
> @@ -93,10 +94,16 @@ struct phy_lookup {
> #define to_phy(a) (container_of((a), struct phy, dev))
>
> #define of_phy_provider_register(dev, xlate) \
> - __of_phy_provider_register((dev), THIS_MODULE, (xlate))
> + __of_phy_provider_register((dev), NULL, THIS_MODULE, (xlate))
>
> #define devm_of_phy_provider_register(dev, xlate) \
> - __devm_of_phy_provider_register((dev), THIS_MODULE, (xlate))
> + __devm_of_phy_provider_register((dev), NULL, THIS_MODULE, (xlate))
> +
> +#define of_phy_provider_register_full(dev, children, xlate) \
> + __of_phy_provider_register(dev, children, THIS_MODULE, xlate)
> +
> +#define devm_of_phy_provider_register_full(dev, children, xlate) \
> + __devm_of_phy_provider_register(dev, children, THIS_MODULE, xlate)
>
> static inline void phy_set_drvdata(struct phy *phy, void *data)
> {
> @@ -147,11 +154,13 @@ struct phy *devm_phy_create(struct device *dev, struct device_node *node,
> void phy_destroy(struct phy *phy);
> void devm_phy_destroy(struct device *dev, struct phy *phy);
> struct phy_provider *__of_phy_provider_register(struct device *dev,
> - struct module *owner, struct phy * (*of_xlate)(struct device *dev,
> - struct of_phandle_args *args));
> + struct device_node *children, struct module *owner,
> + struct phy * (*of_xlate)(struct device *dev,
> + struct of_phandle_args *args));
> struct phy_provider *__devm_of_phy_provider_register(struct device *dev,
> - struct module *owner, struct phy * (*of_xlate)(struct device *dev,
> - struct of_phandle_args *args));
> + struct device_node *children, struct module *owner,
> + struct phy * (*of_xlate)(struct device *dev,
> + struct of_phandle_args *args));
> void of_phy_provider_unregister(struct phy_provider *phy_provider);
> void devm_of_phy_provider_unregister(struct device *dev,
> struct phy_provider *phy_provider);
> @@ -312,15 +321,17 @@ static inline void devm_phy_destroy(struct device *dev, struct phy *phy)
> }
>
> static inline struct phy_provider *__of_phy_provider_register(
> - struct device *dev, struct module *owner, struct phy * (*of_xlate)(
> - struct device *dev, struct of_phandle_args *args))
> + struct device *dev, struct device_node *children, struct module *owner,
> + struct phy * (*of_xlate)(struct device *dev,
> + struct of_phandle_args *args))
> {
> return ERR_PTR(-ENOSYS);
> }
>
> static inline struct phy_provider *__devm_of_phy_provider_register(struct device
> - *dev, struct module *owner, struct phy * (*of_xlate)(struct device *dev,
> - struct of_phandle_args *args))
> + *dev, struct device_node *children, struct module *owner,
> + struct phy * (*of_xlate)(struct device *dev,
> + struct of_phandle_args *args))
> {
> return ERR_PTR(-ENOSYS);
> }
>

2016-04-18 11:12:28

by Thierry Reding

[permalink] [raw]
Subject: Re: [PATCH v10 2/9] dt-bindings: pinctrl: Deprecate Tegra XUSB pad controller binding

On Tue, Mar 15, 2016 at 10:01:25AM +0100, Linus Walleij wrote:
> On Fri, Mar 4, 2016 at 5:19 PM, Thierry Reding <[email protected]> wrote:
>
> > From: Thierry Reding <[email protected]>
> >
> > This is an old version of the binding that isn't flexible enough to
> > describe all aspects of the XUSB pad controller. Specifically with the
> > addition of XUSB support (for SuperSpeed USB) the existing binding is
> > no longer suitable.
> >
> > Signed-off-by: Thierry Reding <[email protected]>
>
> That's unfortunate, not to say unelegant. I want to know Stephen's
> opinion on these patches (probably they are in another mail)
> before merging.
>
> Will the new binding also work with SuperDuperSpeed USB and
> SuperSuperMegaUltraOrtonSpeed USB I wonder... or will we
> change the bindings again?

Linus, Stephen's gave his Acked-by on this patch (provided it gets
merged into patch 1/9, which I've done locally), would you be willing to
give your Acked-by so that this change can go in via the PHY tree where
the new bindings are introduced?

Thanks,
Thierry


Attachments:
(No filename) (1.05 kB)
signature.asc (819.00 B)
Download all attachments

2016-04-18 11:26:26

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH v10 2/9] dt-bindings: pinctrl: Deprecate Tegra XUSB pad controller binding

On Mon, Apr 18, 2016 at 1:12 PM, Thierry Reding
<[email protected]> wrote:
> On Tue, Mar 15, 2016 at 10:01:25AM +0100, Linus Walleij wrote:
>> On Fri, Mar 4, 2016 at 5:19 PM, Thierry Reding <[email protected]> wrote:
>>
>> > From: Thierry Reding <[email protected]>
>> >
>> > This is an old version of the binding that isn't flexible enough to
>> > describe all aspects of the XUSB pad controller. Specifically with the
>> > addition of XUSB support (for SuperSpeed USB) the existing binding is
>> > no longer suitable.
>> >
>> > Signed-off-by: Thierry Reding <[email protected]>
>>
>> That's unfortunate, not to say unelegant. I want to know Stephen's
>> opinion on these patches (probably they are in another mail)
>> before merging.
>>
>> Will the new binding also work with SuperDuperSpeed USB and
>> SuperSuperMegaUltraOrtonSpeed USB I wonder... or will we
>> change the bindings again?
>
> Linus, Stephen's gave his Acked-by on this patch (provided it gets
> merged into patch 1/9, which I've done locally), would you be willing to
> give your Acked-by so that this change can go in via the PHY tree where
> the new bindings are introduced?

Yes I trust Stephen to know what's right for Tegra.
Acked-by: Linus Walleij <[email protected]>

Yours,
Linus Walleij

2016-04-18 11:36:08

by Thierry Reding

[permalink] [raw]
Subject: Re: [PATCH v10 2/9] dt-bindings: pinctrl: Deprecate Tegra XUSB pad controller binding

On Tue, Mar 15, 2016 at 10:01:25AM +0100, Linus Walleij wrote:
> On Fri, Mar 4, 2016 at 5:19 PM, Thierry Reding <[email protected]> wrote:
>
> > From: Thierry Reding <[email protected]>
> >
> > This is an old version of the binding that isn't flexible enough to
> > describe all aspects of the XUSB pad controller. Specifically with the
> > addition of XUSB support (for SuperSpeed USB) the existing binding is
> > no longer suitable.
> >
> > Signed-off-by: Thierry Reding <[email protected]>
>
> That's unfortunate, not to say unelegant. I want to know Stephen's
> opinion on these patches (probably they are in another mail)
> before merging.
>
> Will the new binding also work with SuperDuperSpeed USB and
> SuperSuperMegaUltraOrtonSpeed USB I wonder... or will we
> change the bindings again?

To answer this question: yes, I think that it will be flexible enough to
support future generations of this IP, provided that the design doesn't
change in a fundamental way.

The change from the pinctrl-based bindings to this new one is that each
lane provided by the XUSB pad controller is exposed as a separate PHY
object and hence allows fine-grained control over when which lane is
powered up. It also allows each lane to be easily configured because it
has its own device tree node.

Thierry


Attachments:
(No filename) (1.28 kB)
signature.asc (819.00 B)
Download all attachments

2016-04-18 11:43:25

by Thierry Reding

[permalink] [raw]
Subject: Re: [PATCH v10 4/9] phy: Add Tegra XUSB pad controller support

On Fri, Mar 04, 2016 at 05:19:34PM +0100, Thierry Reding wrote:
> From: Thierry Reding <[email protected]>
>
> Add a new driver for the XUSB pad controller found on NVIDIA Tegra SoCs.
> This hardware block used to be exposed as a pin controller, but it turns
> out that this isn't a good fit. The new driver and DT binding much more
> accurately describe the hardware and are more flexible in supporting new
> SoC generations.
>
> Signed-off-by: Thierry Reding <[email protected]>
> ---
> Changes in v9:
> - export public API for direct use by the xHCI driver (replaces mailbox
> API which had introduced a nasty circular dependency)
>
> drivers/phy/Kconfig | 2 +
> drivers/phy/Makefile | 2 +
> drivers/phy/tegra/Kconfig | 8 +
> drivers/phy/tegra/Makefile | 5 +
> drivers/phy/tegra/xusb-tegra124.c | 1747 ++++++++++++++++++++++++++++
> drivers/phy/tegra/xusb.c | 1017 ++++++++++++++++
> drivers/phy/tegra/xusb.h | 421 +++++++
> drivers/pinctrl/tegra/pinctrl-tegra-xusb.c | 20 +-

Hi Linus,

the changes to the existing pinctrl driver here would need an Acked-by
from you as well. Effectively this turns the pinctrl driver into library
code that is used by the PHY driver to preserve backwards-compatibility
with older bindings.

Here's the hunk that does this:

[...]
> diff --git a/drivers/pinctrl/tegra/pinctrl-tegra-xusb.c b/drivers/pinctrl/tegra/pinctrl-tegra-xusb.c
> index 2f06029c9405..946cda3fee35 100644
> --- a/drivers/pinctrl/tegra/pinctrl-tegra-xusb.c
> +++ b/drivers/pinctrl/tegra/pinctrl-tegra-xusb.c
> @@ -873,7 +873,7 @@ static const struct of_device_id tegra_xusb_padctl_of_match[] = {
> };
> MODULE_DEVICE_TABLE(of, tegra_xusb_padctl_of_match);
>
> -static int tegra_xusb_padctl_probe(struct platform_device *pdev)
> +int tegra_xusb_padctl_legacy_probe(struct platform_device *pdev)
> {
> struct tegra_xusb_padctl *padctl;
> const struct of_device_id *match;
> @@ -955,8 +955,9 @@ reset:
> reset_control_assert(padctl->rst);
> return err;
> }
> +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_legacy_probe);
>
> -static int tegra_xusb_padctl_remove(struct platform_device *pdev)
> +int tegra_xusb_padctl_legacy_remove(struct platform_device *pdev)
> {
> struct tegra_xusb_padctl *padctl = platform_get_drvdata(pdev);
> int err;
> @@ -969,17 +970,4 @@ static int tegra_xusb_padctl_remove(struct platform_device *pdev)
>
> return err;
> }
> -
> -static struct platform_driver tegra_xusb_padctl_driver = {
> - .driver = {
> - .name = "tegra-xusb-padctl",
> - .of_match_table = tegra_xusb_padctl_of_match,
> - },
> - .probe = tegra_xusb_padctl_probe,
> - .remove = tegra_xusb_padctl_remove,
> -};
> -module_platform_driver(tegra_xusb_padctl_driver);
> -
> -MODULE_AUTHOR("Thierry Reding <[email protected]>");
> -MODULE_DESCRIPTION("Tegra 124 XUSB Pad Control driver");
> -MODULE_LICENSE("GPL v2");
> +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_legacy_remove);

Since this merely implements the binding change, does your Acked-by on
the binding apply to this part as well?

Thanks,
Thierry


Attachments:
(No filename) (3.09 kB)
signature.asc (819.00 B)
Download all attachments

2016-04-18 11:50:47

by Thierry Reding

[permalink] [raw]
Subject: Re: [PATCH v10 3/9] dt-bindings: phy: tegra-xusb-padctl: Add Tegra210 support

On Wed, Apr 06, 2016 at 07:08:24PM +0200, Thierry Reding wrote:
[...]
> I attached what I came up with. It extends the OF PHY provider registry
> by allowing an additional node to be specified that if specified will
> serve as the parent for the child lookup (and hence overrides the
> default node that's taken from the struct device).
>
> It is a fairly trivial patch, and you'll notice the bulk of the changes
> is adding the additional parameter in a number of different places. The
> only thing I'm not quite happy about is that we start needing to pass a
> fairly large number of arguments. But perhaps it's still okay.
>
> An alternative would be to make struct phy_provider embeddable into
> driver private structures. That way they could be completely initialized
> by a driver before being passed to the __of_phy_provider_register()
> function (much like struct gpio_chip and others). That would be a fairly
> intrusive change, one that I'd be willing to take on, though I'd like to
> have Kishon's opinion on this before going ahead.
>
> For reference, here's what I'm imagining:
>
> struct foo_phy_provider {
> struct phy_provider base;
> ...
> };
>
> int foo_probe(struct platform_device *pdev)
> {
> struct foo_phy_provider *priv;
> ...
>
> priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
> if (!priv)
> return -ENOMEM;
>
> priv->base.dev = &pdev->dev;
> priv->base.of_xlate = foo_of_xlate;
>
> err = devm_of_phy_provider_register(&priv->base);
> if (err < 0)
> return err;
>
> ...
> }
>
> And of course adapt the signature of the __of_phy_provider_register()
> function and remove the allocation from it.
>
> Thierry
>
> --- >8 ---
> From 15e5348a1a63837efd00309fdce5cda979498f77 Mon Sep 17 00:00:00 2001
> From: Thierry Reding <[email protected]>
> Date: Tue, 5 Apr 2016 17:17:34 +0200
> Subject: [PATCH] phy: core: Allow children node to be overridden
>
> In order to more flexibly support device tree bindings, allow drivers to
> override the container of the child nodes. By default the device node of
> the PHY provider is assumed to be the parent for children, but bindings
> may decide to add additional levels for better organization.
>
> Signed-off-by: Thierry Reding <[email protected]>
> ---
> Documentation/phy.txt | 16 ++++++++++++++--
> drivers/phy/phy-core.c | 27 +++++++++++++++++++++------
> include/linux/phy/phy.h | 31 +++++++++++++++++++++----------
> 3 files changed, 56 insertions(+), 18 deletions(-)

Hi Kishon,

I'd like to take this through the Tegra tree along with the remainder of
the XUSB pad controller and XHCI patches that depend on it. The nice
thing is that it's fairly unintrusive but gives people an easy way to
support more flexible bindings by allowing the OF node to be overridden
when registering the PHY provider.

Are you okay with the change below? If so, could you provide an
Acked-by? I can provide a stable branch for you to pull into the PHY
tree that I'd like to use as a base for the remainder of the series.
I did a fair amount of compile-testing and upon inspection the API that
the patch modifies isn't called in many places, everyone uses the
convenience macros. The stable branch will be helpful in fixing up any
new users, should any appear during the development cycle.

Thanks,
Thierry

> diff --git a/Documentation/phy.txt b/Documentation/phy.txt
> index b388c5af9e72..0aa994bd9a91 100644
> --- a/Documentation/phy.txt
> +++ b/Documentation/phy.txt
> @@ -31,16 +31,28 @@ should provide its own implementation of of_xlate. of_xlate is used only for
> dt boot case.
>
> #define of_phy_provider_register(dev, xlate) \
> - __of_phy_provider_register((dev), THIS_MODULE, (xlate))
> + __of_phy_provider_register((dev), NULL, THIS_MODULE, (xlate))
>
> #define devm_of_phy_provider_register(dev, xlate) \
> - __devm_of_phy_provider_register((dev), THIS_MODULE, (xlate))
> + __devm_of_phy_provider_register((dev), NULL, THIS_MODULE, (xlate))
>
> of_phy_provider_register and devm_of_phy_provider_register macros can be used to
> register the phy_provider and it takes device and of_xlate as
> arguments. For the dt boot case, all PHY providers should use one of the above
> 2 macros to register the PHY provider.
>
> +Often the device tree nodes associated with a PHY provider will contain a set
> +of children that each represent a single PHY. Some bindings may nest the child
> +nodes within extra levels for context and extensibility, in which case the low
> +level of_phy_provider_register_full() and devm_of_phy_provider_register_full()
> +macros can be used to override the node containing the children.
> +
> +#define of_phy_provider_register_full(dev, children, xlate) \
> + __of_phy_provider_register(dev, children, THIS_MODULE, xlate)
> +
> +#define devm_of_phy_provider_register_full(dev, children, xlate) \
> + __devm_of_phy_provider_register_full(dev, children, THIS_MODULE, xlate)
> +
> void devm_of_phy_provider_unregister(struct device *dev,
> struct phy_provider *phy_provider);
> void of_phy_provider_unregister(struct phy_provider *phy_provider);
> diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
> index e7e574dc667a..7f7da2138c82 100644
> --- a/drivers/phy/phy-core.c
> +++ b/drivers/phy/phy-core.c
> @@ -135,13 +135,19 @@ static struct phy *phy_find(struct device *dev, const char *con_id)
> static struct phy_provider *of_phy_provider_lookup(struct device_node *node)
> {
> struct phy_provider *phy_provider;
> + struct device_node *children;
> struct device_node *child;
>
> list_for_each_entry(phy_provider, &phy_provider_list, list) {
> if (phy_provider->dev->of_node == node)
> return phy_provider;
>
> - for_each_child_of_node(phy_provider->dev->of_node, child)
> + if (!phy_provider->children)
> + children = phy_provider->dev->of_node;
> + else
> + children = phy_provider->children;
> +
> + for_each_child_of_node(children, child)
> if (child == node)
> return phy_provider;
> }
> @@ -811,16 +817,22 @@ EXPORT_SYMBOL_GPL(devm_phy_destroy);
> /**
> * __of_phy_provider_register() - create/register phy provider with the framework
> * @dev: struct device of the phy provider
> + * @children: device node containing children (if different from dev->of_node)
> * @owner: the module owner containing of_xlate
> * @of_xlate: function pointer to obtain phy instance from phy provider
> *
> * Creates struct phy_provider from dev and of_xlate function pointer.
> * This is used in the case of dt boot for finding the phy instance from
> * phy provider.
> + *
> + * If the PHY provider doesn't nest children directly but uses a separate
> + * child node to contain the individual children, the @children parameter
> + * can be used to override the default (i.e. dev->of_node).
> */
> struct phy_provider *__of_phy_provider_register(struct device *dev,
> - struct module *owner, struct phy * (*of_xlate)(struct device *dev,
> - struct of_phandle_args *args))
> + struct device_node *children, struct module *owner,
> + struct phy * (*of_xlate)(struct device *dev,
> + struct of_phandle_args *args))
> {
> struct phy_provider *phy_provider;
>
> @@ -829,6 +841,7 @@ struct phy_provider *__of_phy_provider_register(struct device *dev,
> return ERR_PTR(-ENOMEM);
>
> phy_provider->dev = dev;
> + phy_provider->children = children;
> phy_provider->owner = owner;
> phy_provider->of_xlate = of_xlate;
>
> @@ -854,8 +867,9 @@ EXPORT_SYMBOL_GPL(__of_phy_provider_register);
> * on the devres data, then, devres data is freed.
> */
> struct phy_provider *__devm_of_phy_provider_register(struct device *dev,
> - struct module *owner, struct phy * (*of_xlate)(struct device *dev,
> - struct of_phandle_args *args))
> + struct device_node *children, struct module *owner,
> + struct phy * (*of_xlate)(struct device *dev,
> + struct of_phandle_args *args))
> {
> struct phy_provider **ptr, *phy_provider;
>
> @@ -863,7 +877,8 @@ struct phy_provider *__devm_of_phy_provider_register(struct device *dev,
> if (!ptr)
> return ERR_PTR(-ENOMEM);
>
> - phy_provider = __of_phy_provider_register(dev, owner, of_xlate);
> + phy_provider = __of_phy_provider_register(dev, children, owner,
> + of_xlate);
> if (!IS_ERR(phy_provider)) {
> *ptr = phy_provider;
> devres_add(dev, ptr);
> diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
> index 8cf05e341cff..a810f2a18842 100644
> --- a/include/linux/phy/phy.h
> +++ b/include/linux/phy/phy.h
> @@ -77,6 +77,7 @@ struct phy {
> */
> struct phy_provider {
> struct device *dev;
> + struct device_node *children;
> struct module *owner;
> struct list_head list;
> struct phy * (*of_xlate)(struct device *dev,
> @@ -93,10 +94,16 @@ struct phy_lookup {
> #define to_phy(a) (container_of((a), struct phy, dev))
>
> #define of_phy_provider_register(dev, xlate) \
> - __of_phy_provider_register((dev), THIS_MODULE, (xlate))
> + __of_phy_provider_register((dev), NULL, THIS_MODULE, (xlate))
>
> #define devm_of_phy_provider_register(dev, xlate) \
> - __devm_of_phy_provider_register((dev), THIS_MODULE, (xlate))
> + __devm_of_phy_provider_register((dev), NULL, THIS_MODULE, (xlate))
> +
> +#define of_phy_provider_register_full(dev, children, xlate) \
> + __of_phy_provider_register(dev, children, THIS_MODULE, xlate)
> +
> +#define devm_of_phy_provider_register_full(dev, children, xlate) \
> + __devm_of_phy_provider_register(dev, children, THIS_MODULE, xlate)
>
> static inline void phy_set_drvdata(struct phy *phy, void *data)
> {
> @@ -147,11 +154,13 @@ struct phy *devm_phy_create(struct device *dev, struct device_node *node,
> void phy_destroy(struct phy *phy);
> void devm_phy_destroy(struct device *dev, struct phy *phy);
> struct phy_provider *__of_phy_provider_register(struct device *dev,
> - struct module *owner, struct phy * (*of_xlate)(struct device *dev,
> - struct of_phandle_args *args));
> + struct device_node *children, struct module *owner,
> + struct phy * (*of_xlate)(struct device *dev,
> + struct of_phandle_args *args));
> struct phy_provider *__devm_of_phy_provider_register(struct device *dev,
> - struct module *owner, struct phy * (*of_xlate)(struct device *dev,
> - struct of_phandle_args *args));
> + struct device_node *children, struct module *owner,
> + struct phy * (*of_xlate)(struct device *dev,
> + struct of_phandle_args *args));
> void of_phy_provider_unregister(struct phy_provider *phy_provider);
> void devm_of_phy_provider_unregister(struct device *dev,
> struct phy_provider *phy_provider);
> @@ -312,15 +321,17 @@ static inline void devm_phy_destroy(struct device *dev, struct phy *phy)
> }
>
> static inline struct phy_provider *__of_phy_provider_register(
> - struct device *dev, struct module *owner, struct phy * (*of_xlate)(
> - struct device *dev, struct of_phandle_args *args))
> + struct device *dev, struct device_node *children, struct module *owner,
> + struct phy * (*of_xlate)(struct device *dev,
> + struct of_phandle_args *args))
> {
> return ERR_PTR(-ENOSYS);
> }
>
> static inline struct phy_provider *__devm_of_phy_provider_register(struct device
> - *dev, struct module *owner, struct phy * (*of_xlate)(struct device *dev,
> - struct of_phandle_args *args))
> + *dev, struct device_node *children, struct module *owner,
> + struct phy * (*of_xlate)(struct device *dev,
> + struct of_phandle_args *args))
> {
> return ERR_PTR(-ENOSYS);
> }
> --
> 2.8.0
>



Attachments:
(No filename) (11.29 kB)
signature.asc (819.00 B)
Download all attachments

2016-04-25 13:48:55

by Kishon Vijay Abraham I

[permalink] [raw]
Subject: Re: [PATCH v10 3/9] dt-bindings: phy: tegra-xusb-padctl: Add Tegra210 support

Hi,

On Monday 18 April 2016 05:20 PM, Thierry Reding wrote:
> On Wed, Apr 06, 2016 at 07:08:24PM +0200, Thierry Reding wrote:
> [...]
>> I attached what I came up with. It extends the OF PHY provider registry
>> by allowing an additional node to be specified that if specified will
>> serve as the parent for the child lookup (and hence overrides the
>> default node that's taken from the struct device).
>>
>> It is a fairly trivial patch, and you'll notice the bulk of the changes
>> is adding the additional parameter in a number of different places. The
>> only thing I'm not quite happy about is that we start needing to pass a
>> fairly large number of arguments. But perhaps it's still okay.
>>
>> An alternative would be to make struct phy_provider embeddable into
>> driver private structures. That way they could be completely initialized
>> by a driver before being passed to the __of_phy_provider_register()
>> function (much like struct gpio_chip and others). That would be a fairly
>> intrusive change, one that I'd be willing to take on, though I'd like to
>> have Kishon's opinion on this before going ahead.
>>
>> For reference, here's what I'm imagining:
>>
>> struct foo_phy_provider {
>> struct phy_provider base;
>> ...
>> };
>>
>> int foo_probe(struct platform_device *pdev)
>> {
>> struct foo_phy_provider *priv;
>> ...
>>
>> priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
>> if (!priv)
>> return -ENOMEM;
>>
>> priv->base.dev = &pdev->dev;
>> priv->base.of_xlate = foo_of_xlate;
>>
>> err = devm_of_phy_provider_register(&priv->base);
>> if (err < 0)
>> return err;
>>
>> ...
>> }
>>
>> And of course adapt the signature of the __of_phy_provider_register()
>> function and remove the allocation from it.
>>
>> Thierry
>>
>> --- >8 ---
>> From 15e5348a1a63837efd00309fdce5cda979498f77 Mon Sep 17 00:00:00 2001
>> From: Thierry Reding <[email protected]>
>> Date: Tue, 5 Apr 2016 17:17:34 +0200
>> Subject: [PATCH] phy: core: Allow children node to be overridden
>>
>> In order to more flexibly support device tree bindings, allow drivers to
>> override the container of the child nodes. By default the device node of
>> the PHY provider is assumed to be the parent for children, but bindings
>> may decide to add additional levels for better organization.
>>
>> Signed-off-by: Thierry Reding <[email protected]>
>> ---
>> Documentation/phy.txt | 16 ++++++++++++++--
>> drivers/phy/phy-core.c | 27 +++++++++++++++++++++------
>> include/linux/phy/phy.h | 31 +++++++++++++++++++++----------
>> 3 files changed, 56 insertions(+), 18 deletions(-)
>
> Hi Kishon,
>
> I'd like to take this through the Tegra tree along with the remainder of
> the XUSB pad controller and XHCI patches that depend on it. The nice
> thing is that it's fairly unintrusive but gives people an easy way to
> support more flexible bindings by allowing the OF node to be overridden
> when registering the PHY provider.
>
> Are you okay with the change below? If so, could you provide an
> Acked-by? I can provide a stable branch for you to pull into the PHY
> tree that I'd like to use as a base for the remainder of the series.
> I did a fair amount of compile-testing and upon inspection the API that
> the patch modifies isn't called in many places, everyone uses the
> convenience macros. The stable branch will be helpful in fixing up any
> new users, should any appear during the development cycle.
>
> Thanks,
> Thierry
>
>> diff --git a/Documentation/phy.txt b/Documentation/phy.txt
>> index b388c5af9e72..0aa994bd9a91 100644
>> --- a/Documentation/phy.txt
>> +++ b/Documentation/phy.txt
>> @@ -31,16 +31,28 @@ should provide its own implementation of of_xlate. of_xlate is used only for
>> dt boot case.
>>
>> #define of_phy_provider_register(dev, xlate) \
>> - __of_phy_provider_register((dev), THIS_MODULE, (xlate))
>> + __of_phy_provider_register((dev), NULL, THIS_MODULE, (xlate))
>>
>> #define devm_of_phy_provider_register(dev, xlate) \
>> - __devm_of_phy_provider_register((dev), THIS_MODULE, (xlate))
>> + __devm_of_phy_provider_register((dev), NULL, THIS_MODULE, (xlate))
>>
>> of_phy_provider_register and devm_of_phy_provider_register macros can be used to
>> register the phy_provider and it takes device and of_xlate as
>> arguments. For the dt boot case, all PHY providers should use one of the above
>> 2 macros to register the PHY provider.
>>
>> +Often the device tree nodes associated with a PHY provider will contain a set
>> +of children that each represent a single PHY. Some bindings may nest the child
>> +nodes within extra levels for context and extensibility, in which case the low
>> +level of_phy_provider_register_full() and devm_of_phy_provider_register_full()
>> +macros can be used to override the node containing the children.
>> +
>> +#define of_phy_provider_register_full(dev, children, xlate) \
>> + __of_phy_provider_register(dev, children, THIS_MODULE, xlate)
>> +
>> +#define devm_of_phy_provider_register_full(dev, children, xlate) \
>> + __devm_of_phy_provider_register_full(dev, children, THIS_MODULE, xlate)
>> +
>> void devm_of_phy_provider_unregister(struct device *dev,
>> struct phy_provider *phy_provider);
>> void of_phy_provider_unregister(struct phy_provider *phy_provider);
>> diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
>> index e7e574dc667a..7f7da2138c82 100644
>> --- a/drivers/phy/phy-core.c
>> +++ b/drivers/phy/phy-core.c
>> @@ -135,13 +135,19 @@ static struct phy *phy_find(struct device *dev, const char *con_id)
>> static struct phy_provider *of_phy_provider_lookup(struct device_node *node)
>> {
>> struct phy_provider *phy_provider;
>> + struct device_node *children;
>> struct device_node *child;
>>
>> list_for_each_entry(phy_provider, &phy_provider_list, list) {
>> if (phy_provider->dev->of_node == node)
>> return phy_provider;
>>
>> - for_each_child_of_node(phy_provider->dev->of_node, child)
>> + if (!phy_provider->children)
>> + children = phy_provider->dev->of_node;
>> + else
>> + children = phy_provider->children;
>> +
>> + for_each_child_of_node(children, child)
>> if (child == node)
>> return phy_provider;
>> }
>> @@ -811,16 +817,22 @@ EXPORT_SYMBOL_GPL(devm_phy_destroy);
>> /**
>> * __of_phy_provider_register() - create/register phy provider with the framework
>> * @dev: struct device of the phy provider
>> + * @children: device node containing children (if different from dev->of_node)
>> * @owner: the module owner containing of_xlate
>> * @of_xlate: function pointer to obtain phy instance from phy provider
>> *
>> * Creates struct phy_provider from dev and of_xlate function pointer.
>> * This is used in the case of dt boot for finding the phy instance from
>> * phy provider.
>> + *
>> + * If the PHY provider doesn't nest children directly but uses a separate
>> + * child node to contain the individual children, the @children parameter
>> + * can be used to override the default (i.e. dev->of_node).
>> */
>> struct phy_provider *__of_phy_provider_register(struct device *dev,
>> - struct module *owner, struct phy * (*of_xlate)(struct device *dev,
>> - struct of_phandle_args *args))
>> + struct device_node *children, struct module *owner,
>> + struct phy * (*of_xlate)(struct device *dev,
>> + struct of_phandle_args *args))
>> {
>> struct phy_provider *phy_provider;
>>
>> @@ -829,6 +841,7 @@ struct phy_provider *__of_phy_provider_register(struct device *dev,
>> return ERR_PTR(-ENOMEM);
>>
>> phy_provider->dev = dev;
>> + phy_provider->children = children;

I think we should atleast make sure that *children* is actually a subnode of
*dev* whatever level it might be?

Thanks
Kishon

>> phy_provider->owner = owner;
>> phy_provider->of_xlate = of_xlate;
>>
>> @@ -854,8 +867,9 @@ EXPORT_SYMBOL_GPL(__of_phy_provider_register);
>> * on the devres data, then, devres data is freed.
>> */
>> struct phy_provider *__devm_of_phy_provider_register(struct device *dev,
>> - struct module *owner, struct phy * (*of_xlate)(struct device *dev,
>> - struct of_phandle_args *args))
>> + struct device_node *children, struct module *owner,
>> + struct phy * (*of_xlate)(struct device *dev,
>> + struct of_phandle_args *args))
>> {
>> struct phy_provider **ptr, *phy_provider;
>>
>> @@ -863,7 +877,8 @@ struct phy_provider *__devm_of_phy_provider_register(struct device *dev,
>> if (!ptr)
>> return ERR_PTR(-ENOMEM);
>>
>> - phy_provider = __of_phy_provider_register(dev, owner, of_xlate);
>> + phy_provider = __of_phy_provider_register(dev, children, owner,
>> + of_xlate);
>> if (!IS_ERR(phy_provider)) {
>> *ptr = phy_provider;
>> devres_add(dev, ptr);
>> diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
>> index 8cf05e341cff..a810f2a18842 100644
>> --- a/include/linux/phy/phy.h
>> +++ b/include/linux/phy/phy.h
>> @@ -77,6 +77,7 @@ struct phy {
>> */
>> struct phy_provider {
>> struct device *dev;
>> + struct device_node *children;
>> struct module *owner;
>> struct list_head list;
>> struct phy * (*of_xlate)(struct device *dev,
>> @@ -93,10 +94,16 @@ struct phy_lookup {
>> #define to_phy(a) (container_of((a), struct phy, dev))
>>
>> #define of_phy_provider_register(dev, xlate) \
>> - __of_phy_provider_register((dev), THIS_MODULE, (xlate))
>> + __of_phy_provider_register((dev), NULL, THIS_MODULE, (xlate))
>>
>> #define devm_of_phy_provider_register(dev, xlate) \
>> - __devm_of_phy_provider_register((dev), THIS_MODULE, (xlate))
>> + __devm_of_phy_provider_register((dev), NULL, THIS_MODULE, (xlate))
>> +
>> +#define of_phy_provider_register_full(dev, children, xlate) \
>> + __of_phy_provider_register(dev, children, THIS_MODULE, xlate)
>> +
>> +#define devm_of_phy_provider_register_full(dev, children, xlate) \
>> + __devm_of_phy_provider_register(dev, children, THIS_MODULE, xlate)
>>
>> static inline void phy_set_drvdata(struct phy *phy, void *data)
>> {
>> @@ -147,11 +154,13 @@ struct phy *devm_phy_create(struct device *dev, struct device_node *node,
>> void phy_destroy(struct phy *phy);
>> void devm_phy_destroy(struct device *dev, struct phy *phy);
>> struct phy_provider *__of_phy_provider_register(struct device *dev,
>> - struct module *owner, struct phy * (*of_xlate)(struct device *dev,
>> - struct of_phandle_args *args));
>> + struct device_node *children, struct module *owner,
>> + struct phy * (*of_xlate)(struct device *dev,
>> + struct of_phandle_args *args));
>> struct phy_provider *__devm_of_phy_provider_register(struct device *dev,
>> - struct module *owner, struct phy * (*of_xlate)(struct device *dev,
>> - struct of_phandle_args *args));
>> + struct device_node *children, struct module *owner,
>> + struct phy * (*of_xlate)(struct device *dev,
>> + struct of_phandle_args *args));
>> void of_phy_provider_unregister(struct phy_provider *phy_provider);
>> void devm_of_phy_provider_unregister(struct device *dev,
>> struct phy_provider *phy_provider);
>> @@ -312,15 +321,17 @@ static inline void devm_phy_destroy(struct device *dev, struct phy *phy)
>> }
>>
>> static inline struct phy_provider *__of_phy_provider_register(
>> - struct device *dev, struct module *owner, struct phy * (*of_xlate)(
>> - struct device *dev, struct of_phandle_args *args))
>> + struct device *dev, struct device_node *children, struct module *owner,
>> + struct phy * (*of_xlate)(struct device *dev,
>> + struct of_phandle_args *args))
>> {
>> return ERR_PTR(-ENOSYS);
>> }
>>
>> static inline struct phy_provider *__devm_of_phy_provider_register(struct device
>> - *dev, struct module *owner, struct phy * (*of_xlate)(struct device *dev,
>> - struct of_phandle_args *args))
>> + *dev, struct device_node *children, struct module *owner,
>> + struct phy * (*of_xlate)(struct device *dev,
>> + struct of_phandle_args *args))
>> {
>> return ERR_PTR(-ENOSYS);
>> }
>> --
>> 2.8.0
>>
>
>

2016-04-26 13:44:46

by Linus Walleij

[permalink] [raw]
Subject: Re: [PATCH v10 4/9] phy: Add Tegra XUSB pad controller support

On Mon, Apr 18, 2016 at 1:43 PM, Thierry Reding
<[email protected]> wrote:
> On Fri, Mar 04, 2016 at 05:19:34PM +0100, Thierry Reding wrote:
>> -MODULE_LICENSE("GPL v2");
>> +EXPORT_SYMBOL_GPL(tegra_xusb_padctl_legacy_remove);
>
> Since this merely implements the binding change, does your Acked-by on
> the binding apply to this part as well?

Sure
Acked-by: Linus Walleij <[email protected]>

Sorry for missing to reply to this, have been swamped.

Basically I trust anything that you and Stephen agree on.

Yours,
Linus Walleij