summaryrefslogtreecommitdiffstats
path: root/drivers/pinctrl
diff options
context:
space:
mode:
authorLucas Stach <dev@lynxeye.de>2014-11-02 21:13:54 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2014-11-04 12:16:51 +0100
commitd712ce182499f06aafff5ee4af7b83cde209da9e (patch)
tree855d5e7d493045df72f96b3b458aec065e466625 /drivers/pinctrl
parent3e41e7561a1b3636140214d92401c10e15962d22 (diff)
downloadbarebox-d712ce182499f06aafff5ee4af7b83cde209da9e.tar.gz
barebox-d712ce182499f06aafff5ee4af7b83cde209da9e.tar.xz
pinctrl: tegra: add XUSB pad controller
This is a combined pincontrol/PHY driver for the SerDes lanes on Tegra K1. Signed-off-by: Lucas Stach <dev@lynxeye.de> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers/pinctrl')
-rw-r--r--drivers/pinctrl/Kconfig8
-rw-r--r--drivers/pinctrl/Makefile1
-rw-r--r--drivers/pinctrl/pinctrl-tegra-xusb.c519
3 files changed, 528 insertions, 0 deletions
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 770fb2d414..4cbc167cd0 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -60,6 +60,14 @@ config PINCTRL_TEGRA30
help
The pinmux controller found on the Tegra 30+ line of SoCs.
+config PINCTRL_TEGRA_XUSB
+ bool
+ default y if ARCH_TEGRA_124_SOC
+ select GENERIC_PHY
+ help
+ The pinmux controller found on the Tegra 124 line of SoCs used for
+ the SerDes lanes.
+
source drivers/pinctrl/mvebu/Kconfig
endif
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 3ea8649efb..724e6d7d06 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -7,5 +7,6 @@ obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o
obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o
obj-$(CONFIG_PINCTRL_TEGRA20) += pinctrl-tegra20.o
obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o
+obj-$(CONFIG_PINCTRL_TEGRA_XUSB) += pinctrl-tegra-xusb.o
obj-$(CONFIG_ARCH_MVEBU) += mvebu/
diff --git a/drivers/pinctrl/pinctrl-tegra-xusb.c b/drivers/pinctrl/pinctrl-tegra-xusb.c
new file mode 100644
index 0000000000..05cdecbabf
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-tegra-xusb.c
@@ -0,0 +1,519 @@
+/*
+ * Copyright (C) 2014 Lucas Stach <l.stach@pengutronix.de>
+ *
+ * Partly based on code
+ * Copyright (C) 2014, NVIDIA CORPORATION.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <common.h>
+#include <clock.h>
+#include <init.h>
+#include <io.h>
+#include <malloc.h>
+#include <pinctrl.h>
+#include <linux/err.h>
+#include <linux/reset.h>
+#include <linux/phy/phy.h>
+
+#include <dt-bindings/pinctrl/pinctrl-tegra-xusb.h>
+
+#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_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_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_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_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)
+
+struct tegra_xusb_padctl_soc {
+ const struct tegra_xusb_padctl_lane *lanes;
+ unsigned int num_lanes;
+};
+
+struct tegra_xusb_padctl_lane {
+ const char *name;
+
+ unsigned int offset;
+ unsigned int shift;
+ unsigned int mask;
+ unsigned int iddq;
+
+ const char **funcs;
+ unsigned int num_funcs;
+};
+
+struct tegra_xusb_padctl {
+ struct device_d *dev;
+ void __iomem *regs;
+ struct reset_control *rst;
+
+ const struct tegra_xusb_padctl_soc *soc;
+ struct pinctrl_device pinctrl;
+
+ struct phy_provider *provider;
+ struct phy *phys[2];
+
+ unsigned int enable;
+};
+
+static inline void padctl_writel(struct tegra_xusb_padctl *padctl, u32 value,
+ unsigned long offset)
+{
+ writel(value, padctl->regs + offset);
+}
+
+static inline u32 padctl_readl(struct tegra_xusb_padctl *padctl,
+ unsigned long offset)
+{
+ return readl(padctl->regs + offset);
+}
+
+static int tegra_xusb_padctl_enable(struct tegra_xusb_padctl *padctl)
+{
+ u32 value;
+
+ if (padctl->enable++ > 0)
+ return 0;
+
+ 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);
+
+ udelay(100);
+
+ 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);
+
+ udelay(100);
+
+ 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);
+
+ return 0;
+}
+
+static int tegra_xusb_padctl_disable(struct tegra_xusb_padctl *padctl)
+{
+ u32 value;
+
+ if (WARN_ON(padctl->enable == 0))
+ return 0;
+
+ if (--padctl->enable > 0)
+ return 0;
+
+ 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);
+
+ udelay(100);
+
+ 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);
+
+ udelay(100);
+
+ 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);
+
+ return 0;
+}
+
+static int tegra_xusb_phy_init(struct phy *phy)
+{
+ struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
+
+ return tegra_xusb_padctl_enable(padctl);
+}
+
+static int tegra_xusb_phy_exit(struct phy *phy)
+{
+ struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
+
+ return tegra_xusb_padctl_disable(padctl);
+}
+
+static int pcie_phy_power_on(struct phy *phy)
+{
+ struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
+ int err;
+ 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);
+
+ err = wait_on_timeout(50 * MSECOND,
+ padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_P0_CTL1) &
+ XUSB_PADCTL_IOPHY_PLL_P0_CTL1_PLL0_LOCKDET);
+
+ return err;
+}
+
+static int pcie_phy_power_off(struct phy *phy)
+{
+ struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
+ u32 value;
+
+ 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 pcie_phy_ops = {
+
+ .init = tegra_xusb_phy_init,
+ .exit = tegra_xusb_phy_exit,
+ .power_on = pcie_phy_power_on,
+ .power_off = pcie_phy_power_off,
+};
+
+static int sata_phy_power_on(struct phy *phy)
+{
+ struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
+ int err;
+ 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);
+
+ err = wait_on_timeout(50 * MSECOND,
+ padctl_readl(padctl, XUSB_PADCTL_IOPHY_PLL_S0_CTL1) &
+ XUSB_PADCTL_IOPHY_PLL_S0_CTL1_PLL1_LOCKDET);
+
+ return err;
+}
+
+static int sata_phy_power_off(struct phy *phy)
+{
+ struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
+ u32 value;
+
+ 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 sata_phy_ops = {
+
+ .init = tegra_xusb_phy_init,
+ .exit = tegra_xusb_phy_exit,
+ .power_on = sata_phy_power_on,
+ .power_off = sata_phy_power_off,
+};
+
+static struct phy *tegra_xusb_padctl_xlate(struct device_d *dev,
+ struct of_phandle_args *args)
+{
+ struct tegra_xusb_padctl *padctl = dev->priv;
+ unsigned int index = args->args[0];
+
+ if (args->args_count <= 0)
+ return ERR_PTR(-EINVAL);
+
+ if (index >= ARRAY_SIZE(padctl->phys))
+ return ERR_PTR(-EINVAL);
+
+ return padctl->phys[index];
+}
+
+static int pinctrl_tegra_xusb_set_state(struct pinctrl_device *pdev,
+ struct device_node *np)
+{
+ struct tegra_xusb_padctl *padctl =
+ container_of(pdev, struct tegra_xusb_padctl, pinctrl);
+ struct device_node *childnode;
+ int iddq = -1, i, j, k;
+ const char *lanes, *func = NULL;
+ const struct tegra_xusb_padctl_lane *lane = NULL;
+ u32 val;
+
+ /*
+ * At first look if the node we are pointed at has children,
+ * which we may want to visit.
+ */
+ list_for_each_entry(childnode, &np->children, parent_list)
+ pinctrl_tegra_xusb_set_state(pdev, childnode);
+
+ /* read relevant state from devicetree */
+ of_property_read_string(np, "nvidia,function", &func);
+ of_property_read_u32_array(np, "nvidia,iddq", &iddq, 1);
+
+ /* iterate over all lanes referenced in the dt node */
+ for (i = 0; ; i++) {
+ if (of_property_read_string_index(np, "nvidia,lanes", i, &lanes))
+ break;
+
+ for (j = 0; j < padctl->soc->num_lanes; j++) {
+ if (!strcmp(lanes, padctl->soc->lanes[j].name)) {
+ lane = &padctl->soc->lanes[j];
+ break;
+ }
+ }
+ /* if no matching lane is found */
+ if (j == padctl->soc->num_lanes) {
+ /* nothing matching found, warn and bail out */
+ dev_warn(padctl->pinctrl.dev,
+ "invalid lane %s referenced in node %s\n",
+ lanes, np->name);
+ continue;
+ }
+
+ if (func) {
+ for (k = 0; k < lane->num_funcs; k++) {
+ if (!strcmp(func, lane->funcs[k]))
+ break;
+ }
+ if (k < lane->num_funcs) {
+ val = padctl_readl(padctl, lane->offset);
+ val &= ~(lane->mask << lane->shift);
+ val |= k << lane->shift;
+ padctl_writel(padctl, val, lane->offset);
+ } else {
+ dev_warn(padctl->pinctrl.dev,
+ "invalid function %s for lane %s in node %s\n",
+ func, lane->name, np->name);
+ }
+ }
+
+ if (iddq >= 0) {
+ if (lane->iddq) {
+ val = padctl_readl(padctl, lane->offset);
+ if (iddq)
+ val &= ~BIT(lane->iddq);
+ else
+ val |= BIT(lane->iddq);
+ padctl_writel(padctl, val, lane->offset);
+ } else {
+ dev_warn(padctl->pinctrl.dev,
+ "invalid iddq setting for lane %s in node %s\n",
+ lane->name, np->name);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static struct pinctrl_ops pinctrl_tegra_xusb_ops = {
+ .set_state = pinctrl_tegra_xusb_set_state,
+};
+
+static int pinctrl_tegra_xusb_probe(struct device_d *dev)
+{
+ struct tegra_xusb_padctl *padctl;
+ struct phy *phy;
+ int err;
+
+ padctl = xzalloc(sizeof(*padctl));
+
+ dev->priv = padctl;
+ padctl->dev = dev;
+
+ dev_get_drvdata(dev, (unsigned long *)&padctl->soc);
+
+ padctl->regs = dev_request_mem_region(dev, 0);
+ if (IS_ERR(padctl->regs)) {
+ dev_err(dev, "Could not get iomem region\n");
+ return PTR_ERR(padctl->regs);
+ }
+
+ padctl->rst = reset_control_get(dev, NULL);
+ if (IS_ERR(padctl->rst))
+ return PTR_ERR(padctl->rst);
+
+ err = reset_control_deassert(padctl->rst);
+ if (err < 0)
+ return err;
+
+ padctl->pinctrl.dev = dev;
+ padctl->pinctrl.ops = &pinctrl_tegra_xusb_ops;
+
+ err = pinctrl_register(&padctl->pinctrl);
+ if (err) {
+ dev_err(dev, "failed to register pincontrol\n");
+ err = -ENODEV;
+ goto reset;
+ }
+
+ phy = phy_create(dev, NULL, &pcie_phy_ops, NULL);
+ if (IS_ERR(phy)) {
+ err = PTR_ERR(phy);
+ goto unregister;
+ }
+
+ padctl->phys[TEGRA_XUSB_PADCTL_PCIE] = phy;
+ phy_set_drvdata(phy, padctl);
+
+ phy = phy_create(dev, NULL, &sata_phy_ops, NULL);
+ if (IS_ERR(phy)) {
+ err = PTR_ERR(phy);
+ goto unregister;
+ }
+
+ padctl->phys[TEGRA_XUSB_PADCTL_SATA] = phy;
+ phy_set_drvdata(phy, padctl);
+
+ padctl->provider = of_phy_provider_register(dev, tegra_xusb_padctl_xlate);
+ if (IS_ERR(padctl->provider)) {
+ err = PTR_ERR(padctl->provider);
+ dev_err(dev, "failed to register PHYs: %d\n", err);
+ goto unregister;
+ }
+
+ return 0;
+
+unregister:
+ pinctrl_unregister(&padctl->pinctrl);
+reset:
+ reset_control_assert(padctl->rst);
+ return err;
+}
+
+static const char *tegra124_otg_functions[] = {
+ "snps",
+ "xusb",
+ "uart",
+ "rsvd",
+};
+
+static const char *tegra124_usb_functions[] = {
+ "snps",
+ "xusb",
+};
+
+static const char *tegra124_pci_functions[] = {
+ "pcie",
+ "usb3",
+ "sata",
+ "rsvd",
+};
+
+#define TEGRA124_LANE(_name, _offs, _shift, _mask, _iddq, _funcs, _num) \
+ { \
+ .name = _name, \
+ .offset = _offs, \
+ .shift = _shift, \
+ .mask = _mask, \
+ .iddq = _iddq, \
+ .num_funcs = _num, \
+ .funcs = tegra124_##_funcs##_functions, \
+ }
+
+static const struct tegra_xusb_padctl_lane tegra124_lanes[] = {
+ TEGRA124_LANE("otg-0", 0x004, 0, 0x3, 0, otg, 4),
+ TEGRA124_LANE("otg-1", 0x004, 2, 0x3, 0, otg, 4),
+ TEGRA124_LANE("otg-2", 0x004, 4, 0x3, 0, otg, 4),
+ TEGRA124_LANE("ulpi-0", 0x004, 12, 0x1, 0, usb, 2),
+ TEGRA124_LANE("hsic-0", 0x004, 14, 0x1, 0, usb, 2),
+ TEGRA124_LANE("hsic-1", 0x004, 15, 0x1, 0, usb, 2),
+ TEGRA124_LANE("pcie-0", 0x134, 16, 0x3, 1, pci, 4),
+ TEGRA124_LANE("pcie-1", 0x134, 18, 0x3, 2, pci, 4),
+ TEGRA124_LANE("pcie-2", 0x134, 20, 0x3, 3, pci, 4),
+ TEGRA124_LANE("pcie-3", 0x134, 22, 0x3, 4, pci, 4),
+ TEGRA124_LANE("pcie-4", 0x134, 24, 0x3, 5, pci, 4),
+ TEGRA124_LANE("sata-0", 0x134, 26, 0x3, 6, pci, 4),
+};
+
+static const struct tegra_xusb_padctl_soc tegra124_soc = {
+ .num_lanes = ARRAY_SIZE(tegra124_lanes),
+ .lanes = tegra124_lanes,
+};
+
+static __maybe_unused struct of_device_id pinctrl_tegra_xusb_dt_ids[] = {
+ {
+ .compatible = "nvidia,tegra124-xusb-padctl",
+ .data = (unsigned long)&tegra124_soc,
+ }, {
+ /* sentinel */
+ }
+};
+
+static struct driver_d pinctrl_tegra_xusb_driver = {
+ .name = "pinctrl-tegra-xusb",
+ .probe = pinctrl_tegra_xusb_probe,
+ .of_compatible = DRV_OF_COMPAT(pinctrl_tegra_xusb_dt_ids),
+};
+
+static int pinctrl_tegra_xusb_init(void)
+{
+ return platform_driver_register(&pinctrl_tegra_xusb_driver);
+}
+postcore_initcall(pinctrl_tegra_xusb_init);