Commit 10fef6e4 authored by Thierry Reding's avatar Thierry Reding

Merge branch 'for-4.7/phy' into for-4.7/xusb

parents f55532a0 87d66f28
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"
- 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:
- "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.
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:
==========
Each pad node has a child named "lanes" that contains one or more children of
its own, 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"
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:
===========
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
- 0-3: for Tegra210
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
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:
=========
Tegra124 and Tegra132:
----------------------
SoC include:
padctl@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";
lanes {
usb2-0 {
status = "disabled";
#phy-cells = <0>;
};
usb2-1 {
status = "disabled";
#phy-cells = <0>;
};
usb2-2 {
status = "disabled";
#phy-cells = <0>;
};
};
};
ulpi {
status = "disabled";
lanes {
ulpi-0 {
status = "disabled";
#phy-cells = <0>;
};
};
};
hsic {
status = "disabled";
lanes {
hsic-0 {
status = "disabled";
#phy-cells = <0>;
};
hsic-1 {
status = "disabled";
#phy-cells = <0>;
};
};
};
pcie {
status = "disabled";
lanes {
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";
lanes {
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@7009f000 {
status = "okay";
pads {
usb2 {
status = "okay";
lanes {
usb2-0 {
nvidia,function = "xusb";
status = "okay";
};
usb2-1 {
nvidia,function = "xusb";
status = "okay";
};
usb2-2 {
nvidia,function = "xusb";
status = "okay";
};
};
};
pcie {
status = "okay";
lanes {
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";
lanes {
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";
};
};
};
Tegra210:
---------
SoC include:
padctl@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";
lanes {
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";
lanes {
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";
lanes {
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";
lanes {
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@7009f000 {
status = "okay";
pads {
usb2 {
status = "okay";
lanes {
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";
lanes {
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";
lanes {
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>;
};
};
};
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 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
associated PHY that must be powered up before the pad can be used.
......
......@@ -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);
......
......@@ -175,6 +175,19 @@
#define UTMIP_PLL_CFG1_FORCE_PLL_ENABLE_POWERDOWN BIT(14)
#define UTMIP_PLL_CFG1_FORCE_PLL_ACTIVE_POWERDOWN BIT(12)
#define SATA_PLL_CFG0 0x490
#define SATA_PLL_CFG0_PADPLL_RESET_SWCTL BIT(0)
#define SATA_PLL_CFG0_PADPLL_USE_LOCKDET BIT(2)
#define SATA_PLL_CFG0_PADPLL_SLEEP_IDDQ BIT(13)
#define SATA_PLL_CFG0_SEQ_ENABLE BIT(24)
#define XUSBIO_PLL_CFG0 0x51c
#define XUSBIO_PLL_CFG0_PADPLL_RESET_SWCTL BIT(0)
#define XUSBIO_PLL_CFG0_CLK_ENABLE_SWCTL BIT(2)
#define XUSBIO_PLL_CFG0_PADPLL_USE_LOCKDET BIT(6)
#define XUSBIO_PLL_CFG0_PADPLL_SLEEP_IDDQ BIT(13)
#define XUSBIO_PLL_CFG0_SEQ_ENABLE BIT(24)
#define UTMIPLL_HW_PWRDN_CFG0 0x52c
#define UTMIPLL_HW_PWRDN_CFG0_UTMIPLL_LOCK BIT(31)
#define UTMIPLL_HW_PWRDN_CFG0_SEQ_START_STATE BIT(25)
......@@ -416,6 +429,51 @@ static const char *mux_pllmcp_clkm[] = {
#define PLLU_MISC0_WRITE_MASK 0xbfffffff
#define PLLU_MISC1_WRITE_MASK 0x00000007
void tegra210_xusb_pll_hw_control_enable(void)
{
u32 val;
val = readl_relaxed(clk_base + XUSBIO_PLL_CFG0);
val &= ~(XUSBIO_PLL_CFG0_CLK_ENABLE_SWCTL |
XUSBIO_PLL_CFG0_PADPLL_RESET_SWCTL);
val |= XUSBIO_PLL_CFG0_PADPLL_USE_LOCKDET |
XUSBIO_PLL_CFG0_PADPLL_SLEEP_IDDQ;
writel_relaxed(val, clk_base + XUSBIO_PLL_CFG0);
}
EXPORT_SYMBOL_GPL(tegra210_xusb_pll_hw_control_enable);
void tegra210_xusb_pll_hw_sequence_start(void)
{
u32 val;
val = readl_relaxed(clk_base + XUSBIO_PLL_CFG0);
val |= XUSBIO_PLL_CFG0_SEQ_ENABLE;
writel_relaxed(val, clk_base + XUSBIO_PLL_CFG0);
}
EXPORT_SYMBOL_GPL(tegra210_xusb_pll_hw_sequence_start);
void tegra210_sata_pll_hw_control_enable(void)
{
u32 val;
val = readl_relaxed(clk_base + SATA_PLL_CFG0);
val &= ~SATA_PLL_CFG0_PADPLL_RESET_SWCTL;
val |= SATA_PLL_CFG0_PADPLL_USE_LOCKDET |
SATA_PLL_CFG0_PADPLL_SLEEP_IDDQ;
writel_relaxed(val, clk_base + SATA_PLL_CFG0);
}
EXPORT_SYMBOL_GPL(tegra210_sata_pll_hw_control_enable);
void tegra210_sata_pll_hw_sequence_start(void)
{
u32 val;
val = readl_relaxed(clk_base + SATA_PLL_CFG0);
val |= SATA_PLL_CFG0_SEQ_ENABLE;
writel_relaxed(val, clk_base + SATA_PLL_CFG0);
}
EXPORT_SYMBOL_GPL(tegra210_sata_pll_hw_sequence_start);
static inline void _pll_misc_chk_default(void __iomem *base,
struct tegra_clk_pll_params *params,
u8 misc_num, u32 default_val, u32 mask)
......
......@@ -421,4 +421,6 @@ config PHY_CYGNUS_PCIE
Enable this to support the Broadcom Cygnus PCIe PHY.
If unsure, say N.
source "drivers/phy/tegra/Kconfig"
endmenu
......@@ -52,3 +52,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/
......@@ -141,7 +141,7 @@ static struct phy_provider *of_phy_provider_lookup(struct device_node *node)
if (phy_provider->dev->of_node == node)
return phy_provider;
for_each_child_of_node(phy_provider->dev->of_node, child)
for_each_child_of_node(phy_provider->children, child)
if (child == node)
return phy_provider;
}
......@@ -811,24 +811,59 @@ 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. If NULL, the default (dev->of_node)
* will be used. If non-NULL, the device node must be a child (or further
* descendant) of dev->of_node. Otherwise an ERR_PTR()-encoded -EINVAL
* error code is returned.
*/
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;
/*
* If specified, the device node containing the children must itself
* be the provider's device node or a child (or further descendant)
* thereof.
*/
if (children) {
struct device_node *parent = of_node_get(children), *next;
while (parent) {
if (parent == dev->of_node)
break;
next = of_get_parent(parent);
of_node_put(parent);
parent = next;
}
if (!parent)
return ERR_PTR(-EINVAL);
of_node_put(parent);
} else {
children = dev->of_node;
}
phy_provider = kzalloc(sizeof(*phy_provider), GFP_KERNEL);
if (!phy_provider)
return ERR_PTR(-ENOMEM);
phy_provider->dev = dev;
phy_provider->children = of_node_get(children);
phy_provider->owner = owner;
phy_provider->of_xlate = of_xlate;
......@@ -854,8 +889,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 +899,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);
......@@ -888,6 +925,7 @@ void of_phy_provider_unregister(struct phy_provider *phy_provider)
mutex_lock(&phy_provider_mutex);
list_del(&phy_provider->list);
of_node_put(phy_provider->children);
kfree(phy_provider);
mutex_unlock(&phy_provider_mutex);
}
......
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.
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
/*
* 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) {
kfree(usb2);
goto out;
}
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);
out:
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) {
kfree(ulpi);
goto out;
}
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);
out:
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) {
kfree(hsic);
goto out;
}
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);
out:
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) {
kfree(pcie);
goto out;
}
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);
out:
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) {
kfree(sata);
goto out;
}
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);
out:
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 <treding@nvidia.com>");
MODULE_DESCRIPTION("NVIDIA Tegra 124 XUSB Pad Controller driver");
MODULE_LICENSE("GPL v2");
/*
* 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) {
kfree(usb2);
goto out;
}
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);
out:
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) {
kfree(hsic);
goto out;
}
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);
out:
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) {
kfree(pcie);
goto out;
}
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);
out:
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) {
kfree(sata);
goto out;
}
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);
out:
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 <abrestic@chromium.org>");
MODULE_DESCRIPTION("NVIDIA Tegra 210 XUSB Pad Controller driver");
MODULE_LICENSE("GPL v2");
/*
* 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);
np = of_find_node_by_name(np, "lanes");
if (!np)
return NULL;
return of_find_node_by_name(np, pad->soc->lanes[index].name);
}
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 device_node *children;
struct phy *lane;
unsigned int i;
int err;
children = of_find_node_by_name(pad->dev.of_node, "lanes");
if (!children)
return -ENODEV;
pad->lanes = devm_kcalloc(&pad->dev, pad->soc->num_lanes, sizeof(lane),
GFP_KERNEL);
if (!pad->lanes) {
of_node_put(children);
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)) {
of_node_put(np);
continue;
}
pad->lanes[i] = phy_create(&pad->dev, np, ops);
if (IS_ERR(pad->lanes[i])) {
err = PTR_ERR(pad->lanes[i]);
of_node_put(np);
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_full(&pad->dev, children,
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]);
of_node_put(children);
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)
{
const char *func = lane->soc->funcs[lane->function];
return strcmp(function, func) == 0;
}
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 = ERR_PTR(-ENODEV);
char *name;
name = kasprintf(GFP_KERNEL, "%s-%u", type, index);
if (!name)
return ERR_PTR(-ENOMEM);
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 = ERR_PTR(-ENODEV);
for (map = map; map->type; map++) {
if (port->index != map->port)
continue;
lane = tegra_xusb_find_lane(port->padctl, map->type,
map->index);
if (IS_ERR(lane))
continue;
if (!tegra_xusb_lane_check(lane, function))
continue;
if (!IS_ERR(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 <treding@nvidia.com>");
MODULE_DESCRIPTION("Tegra XUSB Pad Controller driver");
MODULE_LICENSE("GPL v2");
/*
* 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 */
......@@ -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 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev)
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 <treding@nvidia.com>");
MODULE_DESCRIPTION("Tegra 124 XUSB Pad Control driver");
MODULE_LICENSE("GPL v2");
EXPORT_SYMBOL_GPL(tegra_xusb_padctl_legacy_remove);
......@@ -121,4 +121,9 @@ static inline void tegra_cpu_clock_resume(void)
}
#endif
extern void tegra210_xusb_pll_hw_control_enable(void);
extern void tegra210_xusb_pll_hw_sequence_start(void);
extern void tegra210_sata_pll_hw_control_enable(void);
extern void tegra210_sata_pll_hw_sequence_start(void);
#endif /* __LINUX_CLK_TEGRA_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);
}
......
/*
* 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 */
......@@ -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__
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment