mirror of
https://github.com/torvalds/linux.git
synced 2024-12-07 11:31:41 +00:00
Merge branch 'for-3.17/xusb-padctl' into for-3.17/dt
This commit is contained in:
commit
69c018268b
@ -0,0 +1,127 @@
|
||||
Device tree binding for NVIDIA Tegra XUSB pad controller
|
||||
========================================================
|
||||
|
||||
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.
|
||||
|
||||
This document defines the device-specific binding for the XUSB pad controller.
|
||||
|
||||
Refer to pinctrl-bindings.txt in this directory for generic information about
|
||||
pin controller device tree bindings and ../phy/phy-bindings.txt for details on
|
||||
how to describe and reference PHYs in device trees.
|
||||
|
||||
Required properties:
|
||||
--------------------
|
||||
- compatible: should be "nvidia,tegra124-xusb-padctl"
|
||||
- reg: Physical base address and length of the controller's registers.
|
||||
- resets: Must contain an entry for each entry in reset-names.
|
||||
See ../reset/reset.txt for details.
|
||||
- reset-names: Must include the following entries:
|
||||
- padctl
|
||||
- #phy-cells: Should be 1. The specifier is the index of the PHY to reference.
|
||||
See <dt-bindings/pinctrl/pinctrl-tegra-xusb.h> for the list of valid values.
|
||||
|
||||
Lane muxing:
|
||||
------------
|
||||
|
||||
Child nodes contain the pinmux configurations following the conventions from
|
||||
the pinctrl-bindings.txt document. Typically a single, static configuration is
|
||||
given and applied at boot time.
|
||||
|
||||
Each subnode describes groups of lanes along with parameters and pads that
|
||||
they should be assigned to. The name of these subnodes is not important. All
|
||||
subnodes should be parsed solely based on their content.
|
||||
|
||||
Each subnode only applies the parameters that are explicitly listed. In other
|
||||
words, if a subnode that lists a function but no pin configuration parameters
|
||||
implies no information about any pin configuration parameters. Similarly, a
|
||||
subnode that describes only an IDDQ parameter implies no information about
|
||||
what function the pins are assigned to. For this reason even seemingly boolean
|
||||
values are actually tristates in this binding: unspecified, off or on.
|
||||
Unspecified is represented as an absent property, and off/on are represented
|
||||
as integer values 0 and 1.
|
||||
|
||||
Required properties:
|
||||
- nvidia,lanes: An array of strings. Each string is the name of a lane.
|
||||
|
||||
Optional properties:
|
||||
- nvidia,function: A string that is the name of the function (pad) that the
|
||||
pin or group should be assigned to. Valid values for function names are
|
||||
listed below.
|
||||
- nvidia,iddq: Enables IDDQ mode of the lane. (0: no, 1: yes)
|
||||
|
||||
Note that not all of these properties are valid for all lanes. Lanes can be
|
||||
divided into three groups:
|
||||
|
||||
- otg-0, otg-1, otg-2:
|
||||
|
||||
Valid functions for this group are: "snps", "xusb", "uart", "rsvd".
|
||||
|
||||
The nvidia,iddq property does not apply to this group.
|
||||
|
||||
- ulpi-0, hsic-0, hsic-1:
|
||||
|
||||
Valid functions for this group are: "snps", "xusb".
|
||||
|
||||
The nvidia,iddq property does not apply to this group.
|
||||
|
||||
- pcie-0, pcie-1, pcie-2, pcie-3, pcie-4, sata-0:
|
||||
|
||||
Valid functions for this group are: "pcie", "usb3", "sata", "rsvd".
|
||||
|
||||
|
||||
Example:
|
||||
========
|
||||
|
||||
SoC file extract:
|
||||
-----------------
|
||||
|
||||
padctl@0,7009f000 {
|
||||
compatible = "nvidia,tegra124-xusb-padctl";
|
||||
reg = <0x0 0x7009f000 0x0 0x1000>;
|
||||
resets = <&tegra_car 142>;
|
||||
reset-names = "padctl";
|
||||
|
||||
#phy-cells = <1>;
|
||||
};
|
||||
|
||||
Board file extract:
|
||||
-------------------
|
||||
|
||||
pcie-controller@0,01003000 {
|
||||
...
|
||||
|
||||
phys = <&padctl 0>;
|
||||
phy-names = "pcie";
|
||||
|
||||
...
|
||||
};
|
||||
|
||||
...
|
||||
|
||||
padctl: padctl@0,7009f000 {
|
||||
pinctrl-0 = <&padctl_default>;
|
||||
pinctrl-names = "default";
|
||||
|
||||
padctl_default: pinmux {
|
||||
usb3 {
|
||||
nvidia,lanes = "pcie-0", "pcie-1";
|
||||
nvidia,function = "usb3";
|
||||
nvidia,iddq = <0>;
|
||||
};
|
||||
|
||||
pcie {
|
||||
nvidia,lanes = "pcie-2", "pcie-3",
|
||||
"pcie-4";
|
||||
nvidia,function = "pcie";
|
||||
nvidia,iddq = <0>;
|
||||
};
|
||||
|
||||
sata {
|
||||
nvidia,lanes = "sata-0";
|
||||
nvidia,function = "sata";
|
||||
nvidia,iddq = <0>;
|
||||
};
|
||||
};
|
||||
};
|
@ -328,6 +328,12 @@ config PINCTRL_TEGRA124
|
||||
bool
|
||||
select PINCTRL_TEGRA
|
||||
|
||||
config PINCTRL_TEGRA_XUSB
|
||||
def_bool y if ARCH_TEGRA
|
||||
select GENERIC_PHY
|
||||
select PINCONF
|
||||
select PINMUX
|
||||
|
||||
config PINCTRL_TZ1090
|
||||
bool "Toumaz Xenif TZ1090 pin control driver"
|
||||
depends on SOC_TZ1090
|
||||
|
@ -55,6 +55,7 @@ obj-$(CONFIG_PINCTRL_TEGRA20) += pinctrl-tegra20.o
|
||||
obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o
|
||||
obj-$(CONFIG_PINCTRL_TEGRA114) += pinctrl-tegra114.o
|
||||
obj-$(CONFIG_PINCTRL_TEGRA124) += pinctrl-tegra124.o
|
||||
obj-$(CONFIG_PINCTRL_TEGRA_XUSB) += pinctrl-tegra-xusb.o
|
||||
obj-$(CONFIG_PINCTRL_TZ1090) += pinctrl-tz1090.o
|
||||
obj-$(CONFIG_PINCTRL_TZ1090_PDC) += pinctrl-tz1090-pdc.o
|
||||
obj-$(CONFIG_PINCTRL_U300) += pinctrl-u300.o
|
||||
|
973
drivers/pinctrl/pinctrl-tegra-xusb.c
Normal file
973
drivers/pinctrl/pinctrl-tegra-xusb.c
Normal file
@ -0,0 +1,973 @@
|
||||
/*
|
||||
* 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/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/phy/phy.h>
|
||||
#include <linux/pinctrl/pinctrl.h>
|
||||
#include <linux/pinctrl/pinmux.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/reset.h>
|
||||
|
||||
#include <dt-bindings/pinctrl/pinctrl-tegra-xusb.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "pinctrl-utils.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_function {
|
||||
const char *name;
|
||||
const char * const *groups;
|
||||
unsigned int num_groups;
|
||||
};
|
||||
|
||||
struct tegra_xusb_padctl_group {
|
||||
const unsigned int *funcs;
|
||||
unsigned int num_funcs;
|
||||
};
|
||||
|
||||
struct tegra_xusb_padctl_soc {
|
||||
const struct pinctrl_pin_desc *pins;
|
||||
unsigned int num_pins;
|
||||
|
||||
const struct tegra_xusb_padctl_function *functions;
|
||||
unsigned int num_functions;
|
||||
|
||||
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 unsigned int *funcs;
|
||||
unsigned int num_funcs;
|
||||
};
|
||||
|
||||
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 pinctrl_dev *pinctrl;
|
||||
struct pinctrl_desc desc;
|
||||
|
||||
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_get_groups_count(struct pinctrl_dev *pinctrl)
|
||||
{
|
||||
struct tegra_xusb_padctl *padctl = pinctrl_dev_get_drvdata(pinctrl);
|
||||
|
||||
return padctl->soc->num_pins;
|
||||
}
|
||||
|
||||
static const char *tegra_xusb_padctl_get_group_name(struct pinctrl_dev *pinctrl,
|
||||
unsigned int group)
|
||||
{
|
||||
struct tegra_xusb_padctl *padctl = pinctrl_dev_get_drvdata(pinctrl);
|
||||
|
||||
return padctl->soc->pins[group].name;
|
||||
}
|
||||
|
||||
enum tegra_xusb_padctl_param {
|
||||
TEGRA_XUSB_PADCTL_IDDQ,
|
||||
};
|
||||
|
||||
static const struct tegra_xusb_padctl_property {
|
||||
const char *name;
|
||||
enum tegra_xusb_padctl_param param;
|
||||
} properties[] = {
|
||||
{ "nvidia,iddq", TEGRA_XUSB_PADCTL_IDDQ },
|
||||
};
|
||||
|
||||
#define TEGRA_XUSB_PADCTL_PACK(param, value) ((param) << 16 | (value))
|
||||
#define TEGRA_XUSB_PADCTL_UNPACK_PARAM(config) ((config) >> 16)
|
||||
#define TEGRA_XUSB_PADCTL_UNPACK_VALUE(config) ((config) & 0xffff)
|
||||
|
||||
static int tegra_xusb_padctl_parse_subnode(struct tegra_xusb_padctl *padctl,
|
||||
struct device_node *np,
|
||||
struct pinctrl_map **maps,
|
||||
unsigned int *reserved_maps,
|
||||
unsigned int *num_maps)
|
||||
{
|
||||
unsigned int i, reserve = 0, num_configs = 0;
|
||||
unsigned long config, *configs = NULL;
|
||||
const char *function, *group;
|
||||
struct property *prop;
|
||||
int err = 0;
|
||||
u32 value;
|
||||
|
||||
err = of_property_read_string(np, "nvidia,function", &function);
|
||||
if (err < 0) {
|
||||
if (err != -EINVAL)
|
||||
return err;
|
||||
|
||||
function = NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(properties); i++) {
|
||||
err = of_property_read_u32(np, properties[i].name, &value);
|
||||
if (err < 0) {
|
||||
if (err == -EINVAL)
|
||||
continue;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
config = TEGRA_XUSB_PADCTL_PACK(properties[i].param, value);
|
||||
|
||||
err = pinctrl_utils_add_config(padctl->pinctrl, &configs,
|
||||
&num_configs, config);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (function)
|
||||
reserve++;
|
||||
|
||||
if (num_configs)
|
||||
reserve++;
|
||||
|
||||
err = of_property_count_strings(np, "nvidia,lanes");
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
reserve *= err;
|
||||
|
||||
err = pinctrl_utils_reserve_map(padctl->pinctrl, maps, reserved_maps,
|
||||
num_maps, reserve);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
of_property_for_each_string(np, "nvidia,lanes", prop, group) {
|
||||
if (function) {
|
||||
err = pinctrl_utils_add_map_mux(padctl->pinctrl, maps,
|
||||
reserved_maps, num_maps, group,
|
||||
function);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (num_configs) {
|
||||
err = pinctrl_utils_add_map_configs(padctl->pinctrl,
|
||||
maps, reserved_maps, num_maps, group,
|
||||
configs, num_configs,
|
||||
PIN_MAP_TYPE_CONFIGS_GROUP);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_xusb_padctl_dt_node_to_map(struct pinctrl_dev *pinctrl,
|
||||
struct device_node *parent,
|
||||
struct pinctrl_map **maps,
|
||||
unsigned int *num_maps)
|
||||
{
|
||||
struct tegra_xusb_padctl *padctl = pinctrl_dev_get_drvdata(pinctrl);
|
||||
unsigned int reserved_maps = 0;
|
||||
struct device_node *np;
|
||||
int err;
|
||||
|
||||
*num_maps = 0;
|
||||
*maps = NULL;
|
||||
|
||||
for_each_child_of_node(parent, np) {
|
||||
err = tegra_xusb_padctl_parse_subnode(padctl, np, maps,
|
||||
&reserved_maps,
|
||||
num_maps);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct pinctrl_ops tegra_xusb_padctl_pinctrl_ops = {
|
||||
.get_groups_count = tegra_xusb_padctl_get_groups_count,
|
||||
.get_group_name = tegra_xusb_padctl_get_group_name,
|
||||
.dt_node_to_map = tegra_xusb_padctl_dt_node_to_map,
|
||||
.dt_free_map = pinctrl_utils_dt_free_map,
|
||||
};
|
||||
|
||||
static int tegra_xusb_padctl_get_functions_count(struct pinctrl_dev *pinctrl)
|
||||
{
|
||||
struct tegra_xusb_padctl *padctl = pinctrl_dev_get_drvdata(pinctrl);
|
||||
|
||||
return padctl->soc->num_functions;
|
||||
}
|
||||
|
||||
static const char *
|
||||
tegra_xusb_padctl_get_function_name(struct pinctrl_dev *pinctrl,
|
||||
unsigned int function)
|
||||
{
|
||||
struct tegra_xusb_padctl *padctl = pinctrl_dev_get_drvdata(pinctrl);
|
||||
|
||||
return padctl->soc->functions[function].name;
|
||||
}
|
||||
|
||||
static int tegra_xusb_padctl_get_function_groups(struct pinctrl_dev *pinctrl,
|
||||
unsigned int function,
|
||||
const char * const **groups,
|
||||
unsigned * const num_groups)
|
||||
{
|
||||
struct tegra_xusb_padctl *padctl = pinctrl_dev_get_drvdata(pinctrl);
|
||||
|
||||
*num_groups = padctl->soc->functions[function].num_groups;
|
||||
*groups = padctl->soc->functions[function].groups;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_xusb_padctl_pinmux_enable(struct pinctrl_dev *pinctrl,
|
||||
unsigned int function,
|
||||
unsigned int group)
|
||||
{
|
||||
struct tegra_xusb_padctl *padctl = pinctrl_dev_get_drvdata(pinctrl);
|
||||
const struct tegra_xusb_padctl_lane *lane;
|
||||
unsigned int i;
|
||||
u32 value;
|
||||
|
||||
lane = &padctl->soc->lanes[group];
|
||||
|
||||
for (i = 0; i < lane->num_funcs; i++)
|
||||
if (lane->funcs[i] == function)
|
||||
break;
|
||||
|
||||
if (i >= lane->num_funcs)
|
||||
return -EINVAL;
|
||||
|
||||
value = padctl_readl(padctl, lane->offset);
|
||||
value &= ~(lane->mask << lane->shift);
|
||||
value |= i << lane->shift;
|
||||
padctl_writel(padctl, value, lane->offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct pinmux_ops tegra_xusb_padctl_pinmux_ops = {
|
||||
.get_functions_count = tegra_xusb_padctl_get_functions_count,
|
||||
.get_function_name = tegra_xusb_padctl_get_function_name,
|
||||
.get_function_groups = tegra_xusb_padctl_get_function_groups,
|
||||
.enable = tegra_xusb_padctl_pinmux_enable,
|
||||
};
|
||||
|
||||
static int tegra_xusb_padctl_pinconf_group_get(struct pinctrl_dev *pinctrl,
|
||||
unsigned int group,
|
||||
unsigned long *config)
|
||||
{
|
||||
struct tegra_xusb_padctl *padctl = pinctrl_dev_get_drvdata(pinctrl);
|
||||
const struct tegra_xusb_padctl_lane *lane;
|
||||
enum tegra_xusb_padctl_param param;
|
||||
u32 value;
|
||||
|
||||
param = TEGRA_XUSB_PADCTL_UNPACK_PARAM(*config);
|
||||
lane = &padctl->soc->lanes[group];
|
||||
|
||||
switch (param) {
|
||||
case TEGRA_XUSB_PADCTL_IDDQ:
|
||||
/* lanes with iddq == 0 don't support this parameter */
|
||||
if (lane->iddq == 0)
|
||||
return -EINVAL;
|
||||
|
||||
value = padctl_readl(padctl, lane->offset);
|
||||
|
||||
if (value & BIT(lane->iddq))
|
||||
value = 0;
|
||||
else
|
||||
value = 1;
|
||||
|
||||
*config = TEGRA_XUSB_PADCTL_PACK(param, value);
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(padctl->dev, "invalid configuration parameter: %04x\n",
|
||||
param);
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra_xusb_padctl_pinconf_group_set(struct pinctrl_dev *pinctrl,
|
||||
unsigned int group,
|
||||
unsigned long *configs,
|
||||
unsigned int num_configs)
|
||||
{
|
||||
struct tegra_xusb_padctl *padctl = pinctrl_dev_get_drvdata(pinctrl);
|
||||
const struct tegra_xusb_padctl_lane *lane;
|
||||
enum tegra_xusb_padctl_param param;
|
||||
unsigned long value;
|
||||
unsigned int i;
|
||||
u32 regval;
|
||||
|
||||
lane = &padctl->soc->lanes[group];
|
||||
|
||||
for (i = 0; i < num_configs; i++) {
|
||||
param = TEGRA_XUSB_PADCTL_UNPACK_PARAM(configs[i]);
|
||||
value = TEGRA_XUSB_PADCTL_UNPACK_VALUE(configs[i]);
|
||||
|
||||
switch (param) {
|
||||
case TEGRA_XUSB_PADCTL_IDDQ:
|
||||
/* lanes with iddq == 0 don't support this parameter */
|
||||
if (lane->iddq == 0)
|
||||
return -EINVAL;
|
||||
|
||||
regval = padctl_readl(padctl, lane->offset);
|
||||
|
||||
if (value)
|
||||
regval &= ~BIT(lane->iddq);
|
||||
else
|
||||
regval |= BIT(lane->iddq);
|
||||
|
||||
padctl_writel(padctl, regval, lane->offset);
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(padctl->dev,
|
||||
"invalid configuration parameter: %04x\n",
|
||||
param);
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static const char *strip_prefix(const char *s)
|
||||
{
|
||||
const char *comma = strchr(s, ',');
|
||||
if (!comma)
|
||||
return s;
|
||||
|
||||
return comma + 1;
|
||||
}
|
||||
|
||||
static void
|
||||
tegra_xusb_padctl_pinconf_group_dbg_show(struct pinctrl_dev *pinctrl,
|
||||
struct seq_file *s,
|
||||
unsigned int group)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(properties); i++) {
|
||||
unsigned long config, value;
|
||||
int err;
|
||||
|
||||
config = TEGRA_XUSB_PADCTL_PACK(properties[i].param, 0);
|
||||
|
||||
err = tegra_xusb_padctl_pinconf_group_get(pinctrl, group,
|
||||
&config);
|
||||
if (err < 0)
|
||||
continue;
|
||||
|
||||
value = TEGRA_XUSB_PADCTL_UNPACK_VALUE(config);
|
||||
|
||||
seq_printf(s, "\n\t%s=%lu\n", strip_prefix(properties[i].name),
|
||||
value);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
tegra_xusb_padctl_pinconf_config_dbg_show(struct pinctrl_dev *pinctrl,
|
||||
struct seq_file *s,
|
||||
unsigned long config)
|
||||
{
|
||||
enum tegra_xusb_padctl_param param;
|
||||
const char *name = "unknown";
|
||||
unsigned long value;
|
||||
unsigned int i;
|
||||
|
||||
param = TEGRA_XUSB_PADCTL_UNPACK_PARAM(config);
|
||||
value = TEGRA_XUSB_PADCTL_UNPACK_VALUE(config);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(properties); i++) {
|
||||
if (properties[i].param == param) {
|
||||
name = properties[i].name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
seq_printf(s, "%s=%lu", strip_prefix(name), value);
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct pinconf_ops tegra_xusb_padctl_pinconf_ops = {
|
||||
.pin_config_group_get = tegra_xusb_padctl_pinconf_group_get,
|
||||
.pin_config_group_set = tegra_xusb_padctl_pinconf_group_set,
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
.pin_config_group_dbg_show = tegra_xusb_padctl_pinconf_group_dbg_show,
|
||||
.pin_config_config_dbg_show = tegra_xusb_padctl_pinconf_config_dbg_show,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int tegra_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 tegra_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 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);
|
||||
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);
|
||||
}
|
||||
|
||||
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,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int sata_phy_power_on(struct phy *phy)
|
||||
{
|
||||
struct tegra_xusb_padctl *padctl = phy_get_drvdata(phy);
|
||||
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);
|
||||
}
|
||||
|
||||
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,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static struct phy *tegra_xusb_padctl_xlate(struct device *dev,
|
||||
struct of_phandle_args *args)
|
||||
{
|
||||
struct tegra_xusb_padctl *padctl = dev_get_drvdata(dev);
|
||||
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];
|
||||
}
|
||||
|
||||
#define PIN_OTG_0 0
|
||||
#define PIN_OTG_1 1
|
||||
#define PIN_OTG_2 2
|
||||
#define PIN_ULPI_0 3
|
||||
#define PIN_HSIC_0 4
|
||||
#define PIN_HSIC_1 5
|
||||
#define PIN_PCIE_0 6
|
||||
#define PIN_PCIE_1 7
|
||||
#define PIN_PCIE_2 8
|
||||
#define PIN_PCIE_3 9
|
||||
#define PIN_PCIE_4 10
|
||||
#define PIN_SATA_0 11
|
||||
|
||||
static const struct pinctrl_pin_desc tegra124_pins[] = {
|
||||
PINCTRL_PIN(PIN_OTG_0, "otg-0"),
|
||||
PINCTRL_PIN(PIN_OTG_1, "otg-1"),
|
||||
PINCTRL_PIN(PIN_OTG_2, "otg-2"),
|
||||
PINCTRL_PIN(PIN_ULPI_0, "ulpi-0"),
|
||||
PINCTRL_PIN(PIN_HSIC_0, "hsic-0"),
|
||||
PINCTRL_PIN(PIN_HSIC_1, "hsic-1"),
|
||||
PINCTRL_PIN(PIN_PCIE_0, "pcie-0"),
|
||||
PINCTRL_PIN(PIN_PCIE_1, "pcie-1"),
|
||||
PINCTRL_PIN(PIN_PCIE_2, "pcie-2"),
|
||||
PINCTRL_PIN(PIN_PCIE_3, "pcie-3"),
|
||||
PINCTRL_PIN(PIN_PCIE_4, "pcie-4"),
|
||||
PINCTRL_PIN(PIN_SATA_0, "sata-0"),
|
||||
};
|
||||
|
||||
static const char * const tegra124_snps_groups[] = {
|
||||
"otg-0",
|
||||
"otg-1",
|
||||
"otg-2",
|
||||
"ulpi-0",
|
||||
"hsic-0",
|
||||
"hsic-1",
|
||||
};
|
||||
|
||||
static const char * const tegra124_xusb_groups[] = {
|
||||
"otg-0",
|
||||
"otg-1",
|
||||
"otg-2",
|
||||
"ulpi-0",
|
||||
"hsic-0",
|
||||
"hsic-1",
|
||||
};
|
||||
|
||||
static const char * const tegra124_uart_groups[] = {
|
||||
"otg-0",
|
||||
"otg-1",
|
||||
"otg-2",
|
||||
};
|
||||
|
||||
static const char * const tegra124_pcie_groups[] = {
|
||||
"pcie-0",
|
||||
"pcie-1",
|
||||
"pcie-2",
|
||||
"pcie-3",
|
||||
"pcie-4",
|
||||
"sata-0",
|
||||
};
|
||||
|
||||
static const char * const tegra124_usb3_groups[] = {
|
||||
"pcie-0",
|
||||
"pcie-1",
|
||||
"pcie-2",
|
||||
"pcie-3",
|
||||
"pcie-4",
|
||||
"sata-0",
|
||||
};
|
||||
|
||||
static const char * const tegra124_sata_groups[] = {
|
||||
"pcie-0",
|
||||
"pcie-1",
|
||||
"pcie-2",
|
||||
"pcie-3",
|
||||
"pcie-4",
|
||||
"sata-0",
|
||||
};
|
||||
|
||||
static const char * const tegra124_rsvd_groups[] = {
|
||||
"otg-0",
|
||||
"otg-1",
|
||||
"otg-2",
|
||||
"pcie-0",
|
||||
"pcie-1",
|
||||
"pcie-2",
|
||||
"pcie-3",
|
||||
"pcie-4",
|
||||
"sata-0",
|
||||
};
|
||||
|
||||
#define TEGRA124_FUNCTION(_name) \
|
||||
{ \
|
||||
.name = #_name, \
|
||||
.num_groups = ARRAY_SIZE(tegra124_##_name##_groups), \
|
||||
.groups = tegra124_##_name##_groups, \
|
||||
}
|
||||
|
||||
static struct tegra_xusb_padctl_function tegra124_functions[] = {
|
||||
TEGRA124_FUNCTION(snps),
|
||||
TEGRA124_FUNCTION(xusb),
|
||||
TEGRA124_FUNCTION(uart),
|
||||
TEGRA124_FUNCTION(pcie),
|
||||
TEGRA124_FUNCTION(usb3),
|
||||
TEGRA124_FUNCTION(sata),
|
||||
TEGRA124_FUNCTION(rsvd),
|
||||
};
|
||||
|
||||
enum tegra124_function {
|
||||
TEGRA124_FUNC_SNPS,
|
||||
TEGRA124_FUNC_XUSB,
|
||||
TEGRA124_FUNC_UART,
|
||||
TEGRA124_FUNC_PCIE,
|
||||
TEGRA124_FUNC_USB3,
|
||||
TEGRA124_FUNC_SATA,
|
||||
TEGRA124_FUNC_RSVD,
|
||||
};
|
||||
|
||||
static const unsigned int tegra124_otg_functions[] = {
|
||||
TEGRA124_FUNC_SNPS,
|
||||
TEGRA124_FUNC_XUSB,
|
||||
TEGRA124_FUNC_UART,
|
||||
TEGRA124_FUNC_RSVD,
|
||||
};
|
||||
|
||||
static const unsigned int tegra124_usb_functions[] = {
|
||||
TEGRA124_FUNC_SNPS,
|
||||
TEGRA124_FUNC_XUSB,
|
||||
};
|
||||
|
||||
static const unsigned int tegra124_pci_functions[] = {
|
||||
TEGRA124_FUNC_PCIE,
|
||||
TEGRA124_FUNC_USB3,
|
||||
TEGRA124_FUNC_SATA,
|
||||
TEGRA124_FUNC_RSVD,
|
||||
};
|
||||
|
||||
#define TEGRA124_LANE(_name, _offset, _shift, _mask, _iddq, _funcs) \
|
||||
{ \
|
||||
.name = _name, \
|
||||
.offset = _offset, \
|
||||
.shift = _shift, \
|
||||
.mask = _mask, \
|
||||
.iddq = _iddq, \
|
||||
.num_funcs = ARRAY_SIZE(tegra124_##_funcs##_functions), \
|
||||
.funcs = tegra124_##_funcs##_functions, \
|
||||
}
|
||||
|
||||
static const struct tegra_xusb_padctl_lane tegra124_lanes[] = {
|
||||
TEGRA124_LANE("otg-0", 0x004, 0, 0x3, 0, otg),
|
||||
TEGRA124_LANE("otg-1", 0x004, 2, 0x3, 0, otg),
|
||||
TEGRA124_LANE("otg-2", 0x004, 4, 0x3, 0, otg),
|
||||
TEGRA124_LANE("ulpi-0", 0x004, 12, 0x1, 0, usb),
|
||||
TEGRA124_LANE("hsic-0", 0x004, 14, 0x1, 0, usb),
|
||||
TEGRA124_LANE("hsic-1", 0x004, 15, 0x1, 0, usb),
|
||||
TEGRA124_LANE("pcie-0", 0x134, 16, 0x3, 1, pci),
|
||||
TEGRA124_LANE("pcie-1", 0x134, 18, 0x3, 2, pci),
|
||||
TEGRA124_LANE("pcie-2", 0x134, 20, 0x3, 3, pci),
|
||||
TEGRA124_LANE("pcie-3", 0x134, 22, 0x3, 4, pci),
|
||||
TEGRA124_LANE("pcie-4", 0x134, 24, 0x3, 5, pci),
|
||||
TEGRA124_LANE("sata-0", 0x134, 26, 0x3, 6, pci),
|
||||
};
|
||||
|
||||
static const struct tegra_xusb_padctl_soc tegra124_soc = {
|
||||
.num_pins = ARRAY_SIZE(tegra124_pins),
|
||||
.pins = tegra124_pins,
|
||||
.num_functions = ARRAY_SIZE(tegra124_functions),
|
||||
.functions = tegra124_functions,
|
||||
.num_lanes = ARRAY_SIZE(tegra124_lanes),
|
||||
.lanes = tegra124_lanes,
|
||||
};
|
||||
|
||||
static const struct of_device_id tegra_xusb_padctl_of_match[] = {
|
||||
{ .compatible = "nvidia,tegra124-xusb-padctl", .data = &tegra124_soc },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tegra_xusb_padctl_of_match);
|
||||
|
||||
static int tegra_xusb_padctl_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra_xusb_padctl *padctl;
|
||||
const struct of_device_id *match;
|
||||
struct resource *res;
|
||||
struct phy *phy;
|
||||
int err;
|
||||
|
||||
padctl = devm_kzalloc(&pdev->dev, sizeof(*padctl), GFP_KERNEL);
|
||||
if (!padctl)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, padctl);
|
||||
mutex_init(&padctl->lock);
|
||||
padctl->dev = &pdev->dev;
|
||||
|
||||
match = of_match_node(tegra_xusb_padctl_of_match, pdev->dev.of_node);
|
||||
padctl->soc = match->data;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
padctl->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(padctl->regs))
|
||||
return PTR_ERR(padctl->regs);
|
||||
|
||||
padctl->rst = devm_reset_control_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(padctl->rst))
|
||||
return PTR_ERR(padctl->rst);
|
||||
|
||||
err = reset_control_deassert(padctl->rst);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
memset(&padctl->desc, 0, sizeof(padctl->desc));
|
||||
padctl->desc.name = dev_name(padctl->dev);
|
||||
padctl->desc.pctlops = &tegra_xusb_padctl_pinctrl_ops;
|
||||
padctl->desc.pmxops = &tegra_xusb_padctl_pinmux_ops;
|
||||
padctl->desc.confops = &tegra_xusb_padctl_pinconf_ops;
|
||||
padctl->desc.owner = THIS_MODULE;
|
||||
|
||||
padctl->pinctrl = pinctrl_register(&padctl->desc, &pdev->dev, padctl);
|
||||
if (!padctl->pinctrl) {
|
||||
dev_err(&pdev->dev, "failed to register pincontrol\n");
|
||||
err = -ENODEV;
|
||||
goto reset;
|
||||
}
|
||||
|
||||
phy = devm_phy_create(&pdev->dev, &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 = devm_phy_create(&pdev->dev, &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 = devm_of_phy_provider_register(&pdev->dev,
|
||||
tegra_xusb_padctl_xlate);
|
||||
if (err < 0) {
|
||||
dev_err(&pdev->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 int tegra_xusb_padctl_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tegra_xusb_padctl *padctl = platform_get_drvdata(pdev);
|
||||
int err;
|
||||
|
||||
pinctrl_unregister(padctl->pinctrl);
|
||||
|
||||
err = reset_control_assert(padctl->rst);
|
||||
if (err < 0)
|
||||
dev_err(&pdev->dev, "failed to assert reset: %d\n", err);
|
||||
|
||||
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");
|
7
include/dt-bindings/pinctrl/pinctrl-tegra-xusb.h
Normal file
7
include/dt-bindings/pinctrl/pinctrl-tegra-xusb.h
Normal file
@ -0,0 +1,7 @@
|
||||
#ifndef _DT_BINDINGS_PINCTRL_TEGRA_XUSB_H
|
||||
#define _DT_BINDINGS_PINCTRL_TEGRA_XUSB_H 1
|
||||
|
||||
#define TEGRA_XUSB_PADCTL_PCIE 0
|
||||
#define TEGRA_XUSB_PADCTL_SATA 1
|
||||
|
||||
#endif /* _DT_BINDINGS_PINCTRL_TEGRA_XUSB_H */
|
Loading…
Reference in New Issue
Block a user