|  | /* | 
|  | * 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 <linux/slab.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_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; | 
|  | } | 
|  |  | 
|  | static int tegra_xusb_padctl_get_group_pins(struct pinctrl_dev *pinctrl, | 
|  | unsigned group, | 
|  | const unsigned **pins, | 
|  | unsigned *num_pins) | 
|  | { | 
|  | /* | 
|  | * For the tegra-xusb pad controller groups are synonomous | 
|  | * with lanes/pins and there is always one lane/pin per group. | 
|  | */ | 
|  | *pins = &pinctrl->desc->pins[group].number; | 
|  | *num_pins = 1; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | 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; | 
|  |  | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | config = TEGRA_XUSB_PADCTL_PACK(properties[i].param, value); | 
|  |  | 
|  | err = pinctrl_utils_add_config(padctl->pinctrl, &configs, | 
|  | &num_configs, config); | 
|  | if (err < 0) | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | if (function) | 
|  | reserve++; | 
|  |  | 
|  | if (num_configs) | 
|  | reserve++; | 
|  |  | 
|  | err = of_property_count_strings(np, "nvidia,lanes"); | 
|  | if (err < 0) | 
|  | goto out; | 
|  |  | 
|  | reserve *= err; | 
|  |  | 
|  | err = pinctrl_utils_reserve_map(padctl->pinctrl, maps, reserved_maps, | 
|  | num_maps, reserve); | 
|  | if (err < 0) | 
|  | goto out; | 
|  |  | 
|  | 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) | 
|  | goto out; | 
|  | } | 
|  |  | 
|  | 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) | 
|  | goto out; | 
|  | } | 
|  | } | 
|  |  | 
|  | err = 0; | 
|  |  | 
|  | out: | 
|  | kfree(configs); | 
|  | return err; | 
|  | } | 
|  |  | 
|  | 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, | 
|  | .get_group_pins = tegra_xusb_padctl_get_group_pins, | 
|  | .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_set(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, | 
|  | .set_mux = tegra_xusb_padctl_pinmux_set, | 
|  | }; | 
|  |  | 
|  | 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.pins = tegra124_pins; | 
|  | padctl->desc.npins = ARRAY_SIZE(tegra124_pins); | 
|  | 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 (IS_ERR(padctl->pinctrl)) { | 
|  | dev_err(&pdev->dev, "failed to register pincontrol\n"); | 
|  | err = PTR_ERR(padctl->pinctrl); | 
|  | goto reset; | 
|  | } | 
|  |  | 
|  | phy = devm_phy_create(&pdev->dev, NULL, &pcie_phy_ops); | 
|  | 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, NULL, &sata_phy_ops); | 
|  | 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 (IS_ERR(padctl->provider)) { | 
|  | err = PTR_ERR(padctl->provider); | 
|  | 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"); |