162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for the NVIDIA Tegra pinmux 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Derived from code: 862306a36Sopenharmony_ci * Copyright (C) 2010 Google, Inc. 962306a36Sopenharmony_ci * Copyright (C) 2010 NVIDIA Corporation 1062306a36Sopenharmony_ci * Copyright (C) 2009-2011 ST-Ericsson AB 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/err.h> 1462306a36Sopenharmony_ci#include <linux/init.h> 1562306a36Sopenharmony_ci#include <linux/io.h> 1662306a36Sopenharmony_ci#include <linux/of.h> 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci#include <linux/seq_file.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/pinctrl/machine.h> 2262306a36Sopenharmony_ci#include <linux/pinctrl/pinconf.h> 2362306a36Sopenharmony_ci#include <linux/pinctrl/pinctrl.h> 2462306a36Sopenharmony_ci#include <linux/pinctrl/pinmux.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#include "../core.h" 2762306a36Sopenharmony_ci#include "../pinctrl-utils.h" 2862306a36Sopenharmony_ci#include "pinctrl-tegra.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic inline u32 pmx_readl(struct tegra_pmx *pmx, u32 bank, u32 reg) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci return readl(pmx->regs[bank] + reg); 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic inline void pmx_writel(struct tegra_pmx *pmx, u32 val, u32 bank, u32 reg) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci writel_relaxed(val, pmx->regs[bank] + reg); 3862306a36Sopenharmony_ci /* make sure pinmux register write completed */ 3962306a36Sopenharmony_ci pmx_readl(pmx, bank, reg); 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic int tegra_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci return pmx->soc->ngroups; 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic const char *tegra_pinctrl_get_group_name(struct pinctrl_dev *pctldev, 5062306a36Sopenharmony_ci unsigned group) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci return pmx->soc->groups[group].name; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic int tegra_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, 5862306a36Sopenharmony_ci unsigned group, 5962306a36Sopenharmony_ci const unsigned **pins, 6062306a36Sopenharmony_ci unsigned *num_pins) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci *pins = pmx->soc->groups[group].pins; 6562306a36Sopenharmony_ci *num_pins = pmx->soc->groups[group].npins; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci return 0; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 7162306a36Sopenharmony_cistatic void tegra_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev, 7262306a36Sopenharmony_ci struct seq_file *s, 7362306a36Sopenharmony_ci unsigned offset) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci seq_printf(s, " %s", dev_name(pctldev->dev)); 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ci#endif 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_cistatic const struct cfg_param { 8062306a36Sopenharmony_ci const char *property; 8162306a36Sopenharmony_ci enum tegra_pinconf_param param; 8262306a36Sopenharmony_ci} cfg_params[] = { 8362306a36Sopenharmony_ci {"nvidia,pull", TEGRA_PINCONF_PARAM_PULL}, 8462306a36Sopenharmony_ci {"nvidia,tristate", TEGRA_PINCONF_PARAM_TRISTATE}, 8562306a36Sopenharmony_ci {"nvidia,enable-input", TEGRA_PINCONF_PARAM_ENABLE_INPUT}, 8662306a36Sopenharmony_ci {"nvidia,open-drain", TEGRA_PINCONF_PARAM_OPEN_DRAIN}, 8762306a36Sopenharmony_ci {"nvidia,lock", TEGRA_PINCONF_PARAM_LOCK}, 8862306a36Sopenharmony_ci {"nvidia,io-reset", TEGRA_PINCONF_PARAM_IORESET}, 8962306a36Sopenharmony_ci {"nvidia,rcv-sel", TEGRA_PINCONF_PARAM_RCV_SEL}, 9062306a36Sopenharmony_ci {"nvidia,io-hv", TEGRA_PINCONF_PARAM_RCV_SEL}, 9162306a36Sopenharmony_ci {"nvidia,high-speed-mode", TEGRA_PINCONF_PARAM_HIGH_SPEED_MODE}, 9262306a36Sopenharmony_ci {"nvidia,schmitt", TEGRA_PINCONF_PARAM_SCHMITT}, 9362306a36Sopenharmony_ci {"nvidia,low-power-mode", TEGRA_PINCONF_PARAM_LOW_POWER_MODE}, 9462306a36Sopenharmony_ci {"nvidia,pull-down-strength", TEGRA_PINCONF_PARAM_DRIVE_DOWN_STRENGTH}, 9562306a36Sopenharmony_ci {"nvidia,pull-up-strength", TEGRA_PINCONF_PARAM_DRIVE_UP_STRENGTH}, 9662306a36Sopenharmony_ci {"nvidia,slew-rate-falling", TEGRA_PINCONF_PARAM_SLEW_RATE_FALLING}, 9762306a36Sopenharmony_ci {"nvidia,slew-rate-rising", TEGRA_PINCONF_PARAM_SLEW_RATE_RISING}, 9862306a36Sopenharmony_ci {"nvidia,drive-type", TEGRA_PINCONF_PARAM_DRIVE_TYPE}, 9962306a36Sopenharmony_ci}; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_cistatic int tegra_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev, 10262306a36Sopenharmony_ci struct device_node *np, 10362306a36Sopenharmony_ci struct pinctrl_map **map, 10462306a36Sopenharmony_ci unsigned *reserved_maps, 10562306a36Sopenharmony_ci unsigned *num_maps) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct device *dev = pctldev->dev; 10862306a36Sopenharmony_ci int ret, i; 10962306a36Sopenharmony_ci const char *function; 11062306a36Sopenharmony_ci u32 val; 11162306a36Sopenharmony_ci unsigned long config; 11262306a36Sopenharmony_ci unsigned long *configs = NULL; 11362306a36Sopenharmony_ci unsigned num_configs = 0; 11462306a36Sopenharmony_ci unsigned reserve; 11562306a36Sopenharmony_ci struct property *prop; 11662306a36Sopenharmony_ci const char *group; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci ret = of_property_read_string(np, "nvidia,function", &function); 11962306a36Sopenharmony_ci if (ret < 0) { 12062306a36Sopenharmony_ci /* EINVAL=missing, which is fine since it's optional */ 12162306a36Sopenharmony_ci if (ret != -EINVAL) 12262306a36Sopenharmony_ci dev_err(dev, 12362306a36Sopenharmony_ci "could not parse property nvidia,function\n"); 12462306a36Sopenharmony_ci function = NULL; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cfg_params); i++) { 12862306a36Sopenharmony_ci ret = of_property_read_u32(np, cfg_params[i].property, &val); 12962306a36Sopenharmony_ci if (!ret) { 13062306a36Sopenharmony_ci config = TEGRA_PINCONF_PACK(cfg_params[i].param, val); 13162306a36Sopenharmony_ci ret = pinctrl_utils_add_config(pctldev, &configs, 13262306a36Sopenharmony_ci &num_configs, config); 13362306a36Sopenharmony_ci if (ret < 0) 13462306a36Sopenharmony_ci goto exit; 13562306a36Sopenharmony_ci /* EINVAL=missing, which is fine since it's optional */ 13662306a36Sopenharmony_ci } else if (ret != -EINVAL) { 13762306a36Sopenharmony_ci dev_err(dev, "could not parse property %s\n", 13862306a36Sopenharmony_ci cfg_params[i].property); 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci reserve = 0; 14362306a36Sopenharmony_ci if (function != NULL) 14462306a36Sopenharmony_ci reserve++; 14562306a36Sopenharmony_ci if (num_configs) 14662306a36Sopenharmony_ci reserve++; 14762306a36Sopenharmony_ci ret = of_property_count_strings(np, "nvidia,pins"); 14862306a36Sopenharmony_ci if (ret < 0) { 14962306a36Sopenharmony_ci dev_err(dev, "could not parse property nvidia,pins\n"); 15062306a36Sopenharmony_ci goto exit; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci reserve *= ret; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci ret = pinctrl_utils_reserve_map(pctldev, map, reserved_maps, 15562306a36Sopenharmony_ci num_maps, reserve); 15662306a36Sopenharmony_ci if (ret < 0) 15762306a36Sopenharmony_ci goto exit; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci of_property_for_each_string(np, "nvidia,pins", prop, group) { 16062306a36Sopenharmony_ci if (function) { 16162306a36Sopenharmony_ci ret = pinctrl_utils_add_map_mux(pctldev, map, 16262306a36Sopenharmony_ci reserved_maps, num_maps, group, 16362306a36Sopenharmony_ci function); 16462306a36Sopenharmony_ci if (ret < 0) 16562306a36Sopenharmony_ci goto exit; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (num_configs) { 16962306a36Sopenharmony_ci ret = pinctrl_utils_add_map_configs(pctldev, map, 17062306a36Sopenharmony_ci reserved_maps, num_maps, group, 17162306a36Sopenharmony_ci configs, num_configs, 17262306a36Sopenharmony_ci PIN_MAP_TYPE_CONFIGS_GROUP); 17362306a36Sopenharmony_ci if (ret < 0) 17462306a36Sopenharmony_ci goto exit; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci ret = 0; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ciexit: 18162306a36Sopenharmony_ci kfree(configs); 18262306a36Sopenharmony_ci return ret; 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic int tegra_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, 18662306a36Sopenharmony_ci struct device_node *np_config, 18762306a36Sopenharmony_ci struct pinctrl_map **map, 18862306a36Sopenharmony_ci unsigned *num_maps) 18962306a36Sopenharmony_ci{ 19062306a36Sopenharmony_ci unsigned reserved_maps; 19162306a36Sopenharmony_ci struct device_node *np; 19262306a36Sopenharmony_ci int ret; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci reserved_maps = 0; 19562306a36Sopenharmony_ci *map = NULL; 19662306a36Sopenharmony_ci *num_maps = 0; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci for_each_child_of_node(np_config, np) { 19962306a36Sopenharmony_ci ret = tegra_pinctrl_dt_subnode_to_map(pctldev, np, map, 20062306a36Sopenharmony_ci &reserved_maps, num_maps); 20162306a36Sopenharmony_ci if (ret < 0) { 20262306a36Sopenharmony_ci pinctrl_utils_free_map(pctldev, *map, 20362306a36Sopenharmony_ci *num_maps); 20462306a36Sopenharmony_ci of_node_put(np); 20562306a36Sopenharmony_ci return ret; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci return 0; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cistatic const struct pinctrl_ops tegra_pinctrl_ops = { 21362306a36Sopenharmony_ci .get_groups_count = tegra_pinctrl_get_groups_count, 21462306a36Sopenharmony_ci .get_group_name = tegra_pinctrl_get_group_name, 21562306a36Sopenharmony_ci .get_group_pins = tegra_pinctrl_get_group_pins, 21662306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 21762306a36Sopenharmony_ci .pin_dbg_show = tegra_pinctrl_pin_dbg_show, 21862306a36Sopenharmony_ci#endif 21962306a36Sopenharmony_ci .dt_node_to_map = tegra_pinctrl_dt_node_to_map, 22062306a36Sopenharmony_ci .dt_free_map = pinctrl_utils_free_map, 22162306a36Sopenharmony_ci}; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic int tegra_pinctrl_get_funcs_count(struct pinctrl_dev *pctldev) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci return pmx->soc->nfunctions; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_cistatic const char *tegra_pinctrl_get_func_name(struct pinctrl_dev *pctldev, 23162306a36Sopenharmony_ci unsigned function) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci return pmx->functions[function].name; 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic int tegra_pinctrl_get_func_groups(struct pinctrl_dev *pctldev, 23962306a36Sopenharmony_ci unsigned function, 24062306a36Sopenharmony_ci const char * const **groups, 24162306a36Sopenharmony_ci unsigned * const num_groups) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci *groups = pmx->functions[function].groups; 24662306a36Sopenharmony_ci *num_groups = pmx->functions[function].ngroups; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci return 0; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_cistatic int tegra_pinctrl_set_mux(struct pinctrl_dev *pctldev, 25262306a36Sopenharmony_ci unsigned function, 25362306a36Sopenharmony_ci unsigned group) 25462306a36Sopenharmony_ci{ 25562306a36Sopenharmony_ci struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 25662306a36Sopenharmony_ci const struct tegra_pingroup *g; 25762306a36Sopenharmony_ci int i; 25862306a36Sopenharmony_ci u32 val; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci g = &pmx->soc->groups[group]; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (WARN_ON(g->mux_reg < 0)) 26362306a36Sopenharmony_ci return -EINVAL; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(g->funcs); i++) { 26662306a36Sopenharmony_ci if (g->funcs[i] == function) 26762306a36Sopenharmony_ci break; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci if (WARN_ON(i == ARRAY_SIZE(g->funcs))) 27062306a36Sopenharmony_ci return -EINVAL; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci val = pmx_readl(pmx, g->mux_bank, g->mux_reg); 27362306a36Sopenharmony_ci val &= ~(0x3 << g->mux_bit); 27462306a36Sopenharmony_ci val |= i << g->mux_bit; 27562306a36Sopenharmony_ci pmx_writel(pmx, val, g->mux_bank, g->mux_reg); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci return 0; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic const struct tegra_pingroup *tegra_pinctrl_get_group(struct pinctrl_dev *pctldev, 28162306a36Sopenharmony_ci unsigned int offset) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 28462306a36Sopenharmony_ci unsigned int group, num_pins, j; 28562306a36Sopenharmony_ci const unsigned int *pins; 28662306a36Sopenharmony_ci int ret; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci for (group = 0; group < pmx->soc->ngroups; ++group) { 28962306a36Sopenharmony_ci ret = tegra_pinctrl_get_group_pins(pctldev, group, &pins, &num_pins); 29062306a36Sopenharmony_ci if (ret < 0) 29162306a36Sopenharmony_ci continue; 29262306a36Sopenharmony_ci for (j = 0; j < num_pins; j++) { 29362306a36Sopenharmony_ci if (offset == pins[j]) 29462306a36Sopenharmony_ci return &pmx->soc->groups[group]; 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci dev_err(pctldev->dev, "Pingroup not found for pin %u\n", offset); 29962306a36Sopenharmony_ci return NULL; 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic int tegra_pinctrl_gpio_request_enable(struct pinctrl_dev *pctldev, 30362306a36Sopenharmony_ci struct pinctrl_gpio_range *range, 30462306a36Sopenharmony_ci unsigned int offset) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 30762306a36Sopenharmony_ci const struct tegra_pingroup *group; 30862306a36Sopenharmony_ci u32 value; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (!pmx->soc->sfsel_in_mux) 31162306a36Sopenharmony_ci return 0; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci group = tegra_pinctrl_get_group(pctldev, offset); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (!group) 31662306a36Sopenharmony_ci return -EINVAL; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (group->mux_reg < 0 || group->sfsel_bit < 0) 31962306a36Sopenharmony_ci return -EINVAL; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci value = pmx_readl(pmx, group->mux_bank, group->mux_reg); 32262306a36Sopenharmony_ci value &= ~BIT(group->sfsel_bit); 32362306a36Sopenharmony_ci pmx_writel(pmx, value, group->mux_bank, group->mux_reg); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci return 0; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic void tegra_pinctrl_gpio_disable_free(struct pinctrl_dev *pctldev, 32962306a36Sopenharmony_ci struct pinctrl_gpio_range *range, 33062306a36Sopenharmony_ci unsigned int offset) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 33362306a36Sopenharmony_ci const struct tegra_pingroup *group; 33462306a36Sopenharmony_ci u32 value; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (!pmx->soc->sfsel_in_mux) 33762306a36Sopenharmony_ci return; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci group = tegra_pinctrl_get_group(pctldev, offset); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci if (!group) 34262306a36Sopenharmony_ci return; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (group->mux_reg < 0 || group->sfsel_bit < 0) 34562306a36Sopenharmony_ci return; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci value = pmx_readl(pmx, group->mux_bank, group->mux_reg); 34862306a36Sopenharmony_ci value |= BIT(group->sfsel_bit); 34962306a36Sopenharmony_ci pmx_writel(pmx, value, group->mux_bank, group->mux_reg); 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic const struct pinmux_ops tegra_pinmux_ops = { 35362306a36Sopenharmony_ci .get_functions_count = tegra_pinctrl_get_funcs_count, 35462306a36Sopenharmony_ci .get_function_name = tegra_pinctrl_get_func_name, 35562306a36Sopenharmony_ci .get_function_groups = tegra_pinctrl_get_func_groups, 35662306a36Sopenharmony_ci .set_mux = tegra_pinctrl_set_mux, 35762306a36Sopenharmony_ci .gpio_request_enable = tegra_pinctrl_gpio_request_enable, 35862306a36Sopenharmony_ci .gpio_disable_free = tegra_pinctrl_gpio_disable_free, 35962306a36Sopenharmony_ci}; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic int tegra_pinconf_reg(struct tegra_pmx *pmx, 36262306a36Sopenharmony_ci const struct tegra_pingroup *g, 36362306a36Sopenharmony_ci enum tegra_pinconf_param param, 36462306a36Sopenharmony_ci bool report_err, 36562306a36Sopenharmony_ci s8 *bank, s32 *reg, s8 *bit, s8 *width) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci switch (param) { 36862306a36Sopenharmony_ci case TEGRA_PINCONF_PARAM_PULL: 36962306a36Sopenharmony_ci *bank = g->pupd_bank; 37062306a36Sopenharmony_ci *reg = g->pupd_reg; 37162306a36Sopenharmony_ci *bit = g->pupd_bit; 37262306a36Sopenharmony_ci *width = 2; 37362306a36Sopenharmony_ci break; 37462306a36Sopenharmony_ci case TEGRA_PINCONF_PARAM_TRISTATE: 37562306a36Sopenharmony_ci *bank = g->tri_bank; 37662306a36Sopenharmony_ci *reg = g->tri_reg; 37762306a36Sopenharmony_ci *bit = g->tri_bit; 37862306a36Sopenharmony_ci *width = 1; 37962306a36Sopenharmony_ci break; 38062306a36Sopenharmony_ci case TEGRA_PINCONF_PARAM_ENABLE_INPUT: 38162306a36Sopenharmony_ci *bank = g->mux_bank; 38262306a36Sopenharmony_ci *reg = g->mux_reg; 38362306a36Sopenharmony_ci *bit = g->einput_bit; 38462306a36Sopenharmony_ci *width = 1; 38562306a36Sopenharmony_ci break; 38662306a36Sopenharmony_ci case TEGRA_PINCONF_PARAM_OPEN_DRAIN: 38762306a36Sopenharmony_ci *bank = g->mux_bank; 38862306a36Sopenharmony_ci *reg = g->mux_reg; 38962306a36Sopenharmony_ci *bit = g->odrain_bit; 39062306a36Sopenharmony_ci *width = 1; 39162306a36Sopenharmony_ci break; 39262306a36Sopenharmony_ci case TEGRA_PINCONF_PARAM_LOCK: 39362306a36Sopenharmony_ci *bank = g->mux_bank; 39462306a36Sopenharmony_ci *reg = g->mux_reg; 39562306a36Sopenharmony_ci *bit = g->lock_bit; 39662306a36Sopenharmony_ci *width = 1; 39762306a36Sopenharmony_ci break; 39862306a36Sopenharmony_ci case TEGRA_PINCONF_PARAM_IORESET: 39962306a36Sopenharmony_ci *bank = g->mux_bank; 40062306a36Sopenharmony_ci *reg = g->mux_reg; 40162306a36Sopenharmony_ci *bit = g->ioreset_bit; 40262306a36Sopenharmony_ci *width = 1; 40362306a36Sopenharmony_ci break; 40462306a36Sopenharmony_ci case TEGRA_PINCONF_PARAM_RCV_SEL: 40562306a36Sopenharmony_ci *bank = g->mux_bank; 40662306a36Sopenharmony_ci *reg = g->mux_reg; 40762306a36Sopenharmony_ci *bit = g->rcv_sel_bit; 40862306a36Sopenharmony_ci *width = 1; 40962306a36Sopenharmony_ci break; 41062306a36Sopenharmony_ci case TEGRA_PINCONF_PARAM_HIGH_SPEED_MODE: 41162306a36Sopenharmony_ci if (pmx->soc->hsm_in_mux) { 41262306a36Sopenharmony_ci *bank = g->mux_bank; 41362306a36Sopenharmony_ci *reg = g->mux_reg; 41462306a36Sopenharmony_ci } else { 41562306a36Sopenharmony_ci *bank = g->drv_bank; 41662306a36Sopenharmony_ci *reg = g->drv_reg; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci *bit = g->hsm_bit; 41962306a36Sopenharmony_ci *width = 1; 42062306a36Sopenharmony_ci break; 42162306a36Sopenharmony_ci case TEGRA_PINCONF_PARAM_SCHMITT: 42262306a36Sopenharmony_ci if (pmx->soc->schmitt_in_mux) { 42362306a36Sopenharmony_ci *bank = g->mux_bank; 42462306a36Sopenharmony_ci *reg = g->mux_reg; 42562306a36Sopenharmony_ci } else { 42662306a36Sopenharmony_ci *bank = g->drv_bank; 42762306a36Sopenharmony_ci *reg = g->drv_reg; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci *bit = g->schmitt_bit; 43062306a36Sopenharmony_ci *width = 1; 43162306a36Sopenharmony_ci break; 43262306a36Sopenharmony_ci case TEGRA_PINCONF_PARAM_LOW_POWER_MODE: 43362306a36Sopenharmony_ci *bank = g->drv_bank; 43462306a36Sopenharmony_ci *reg = g->drv_reg; 43562306a36Sopenharmony_ci *bit = g->lpmd_bit; 43662306a36Sopenharmony_ci *width = 2; 43762306a36Sopenharmony_ci break; 43862306a36Sopenharmony_ci case TEGRA_PINCONF_PARAM_DRIVE_DOWN_STRENGTH: 43962306a36Sopenharmony_ci *bank = g->drv_bank; 44062306a36Sopenharmony_ci *reg = g->drv_reg; 44162306a36Sopenharmony_ci *bit = g->drvdn_bit; 44262306a36Sopenharmony_ci *width = g->drvdn_width; 44362306a36Sopenharmony_ci break; 44462306a36Sopenharmony_ci case TEGRA_PINCONF_PARAM_DRIVE_UP_STRENGTH: 44562306a36Sopenharmony_ci *bank = g->drv_bank; 44662306a36Sopenharmony_ci *reg = g->drv_reg; 44762306a36Sopenharmony_ci *bit = g->drvup_bit; 44862306a36Sopenharmony_ci *width = g->drvup_width; 44962306a36Sopenharmony_ci break; 45062306a36Sopenharmony_ci case TEGRA_PINCONF_PARAM_SLEW_RATE_FALLING: 45162306a36Sopenharmony_ci *bank = g->drv_bank; 45262306a36Sopenharmony_ci *reg = g->drv_reg; 45362306a36Sopenharmony_ci *bit = g->slwf_bit; 45462306a36Sopenharmony_ci *width = g->slwf_width; 45562306a36Sopenharmony_ci break; 45662306a36Sopenharmony_ci case TEGRA_PINCONF_PARAM_SLEW_RATE_RISING: 45762306a36Sopenharmony_ci *bank = g->drv_bank; 45862306a36Sopenharmony_ci *reg = g->drv_reg; 45962306a36Sopenharmony_ci *bit = g->slwr_bit; 46062306a36Sopenharmony_ci *width = g->slwr_width; 46162306a36Sopenharmony_ci break; 46262306a36Sopenharmony_ci case TEGRA_PINCONF_PARAM_DRIVE_TYPE: 46362306a36Sopenharmony_ci if (pmx->soc->drvtype_in_mux) { 46462306a36Sopenharmony_ci *bank = g->mux_bank; 46562306a36Sopenharmony_ci *reg = g->mux_reg; 46662306a36Sopenharmony_ci } else { 46762306a36Sopenharmony_ci *bank = g->drv_bank; 46862306a36Sopenharmony_ci *reg = g->drv_reg; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci *bit = g->drvtype_bit; 47162306a36Sopenharmony_ci *width = 2; 47262306a36Sopenharmony_ci break; 47362306a36Sopenharmony_ci default: 47462306a36Sopenharmony_ci dev_err(pmx->dev, "Invalid config param %04x\n", param); 47562306a36Sopenharmony_ci return -ENOTSUPP; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci if (*reg < 0 || *bit < 0) { 47962306a36Sopenharmony_ci if (report_err) { 48062306a36Sopenharmony_ci const char *prop = "unknown"; 48162306a36Sopenharmony_ci int i; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cfg_params); i++) { 48462306a36Sopenharmony_ci if (cfg_params[i].param == param) { 48562306a36Sopenharmony_ci prop = cfg_params[i].property; 48662306a36Sopenharmony_ci break; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci dev_err(pmx->dev, 49162306a36Sopenharmony_ci "Config param %04x (%s) not supported on group %s\n", 49262306a36Sopenharmony_ci param, prop, g->name); 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci return -ENOTSUPP; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci return 0; 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic int tegra_pinconf_get(struct pinctrl_dev *pctldev, 50162306a36Sopenharmony_ci unsigned pin, unsigned long *config) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci dev_err(pctldev->dev, "pin_config_get op not supported\n"); 50462306a36Sopenharmony_ci return -ENOTSUPP; 50562306a36Sopenharmony_ci} 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_cistatic int tegra_pinconf_set(struct pinctrl_dev *pctldev, 50862306a36Sopenharmony_ci unsigned pin, unsigned long *configs, 50962306a36Sopenharmony_ci unsigned num_configs) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci dev_err(pctldev->dev, "pin_config_set op not supported\n"); 51262306a36Sopenharmony_ci return -ENOTSUPP; 51362306a36Sopenharmony_ci} 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_cistatic int tegra_pinconf_group_get(struct pinctrl_dev *pctldev, 51662306a36Sopenharmony_ci unsigned group, unsigned long *config) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 51962306a36Sopenharmony_ci enum tegra_pinconf_param param = TEGRA_PINCONF_UNPACK_PARAM(*config); 52062306a36Sopenharmony_ci u16 arg; 52162306a36Sopenharmony_ci const struct tegra_pingroup *g; 52262306a36Sopenharmony_ci int ret; 52362306a36Sopenharmony_ci s8 bank, bit, width; 52462306a36Sopenharmony_ci s32 reg; 52562306a36Sopenharmony_ci u32 val, mask; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci g = &pmx->soc->groups[group]; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci ret = tegra_pinconf_reg(pmx, g, param, true, &bank, ®, &bit, 53062306a36Sopenharmony_ci &width); 53162306a36Sopenharmony_ci if (ret < 0) 53262306a36Sopenharmony_ci return ret; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci val = pmx_readl(pmx, bank, reg); 53562306a36Sopenharmony_ci mask = (1 << width) - 1; 53662306a36Sopenharmony_ci arg = (val >> bit) & mask; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci *config = TEGRA_PINCONF_PACK(param, arg); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci return 0; 54162306a36Sopenharmony_ci} 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_cistatic int tegra_pinconf_group_set(struct pinctrl_dev *pctldev, 54462306a36Sopenharmony_ci unsigned group, unsigned long *configs, 54562306a36Sopenharmony_ci unsigned num_configs) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 54862306a36Sopenharmony_ci enum tegra_pinconf_param param; 54962306a36Sopenharmony_ci u16 arg; 55062306a36Sopenharmony_ci const struct tegra_pingroup *g; 55162306a36Sopenharmony_ci int ret, i; 55262306a36Sopenharmony_ci s8 bank, bit, width; 55362306a36Sopenharmony_ci s32 reg; 55462306a36Sopenharmony_ci u32 val, mask; 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci g = &pmx->soc->groups[group]; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci for (i = 0; i < num_configs; i++) { 55962306a36Sopenharmony_ci param = TEGRA_PINCONF_UNPACK_PARAM(configs[i]); 56062306a36Sopenharmony_ci arg = TEGRA_PINCONF_UNPACK_ARG(configs[i]); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci ret = tegra_pinconf_reg(pmx, g, param, true, &bank, ®, &bit, 56362306a36Sopenharmony_ci &width); 56462306a36Sopenharmony_ci if (ret < 0) 56562306a36Sopenharmony_ci return ret; 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci val = pmx_readl(pmx, bank, reg); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* LOCK can't be cleared */ 57062306a36Sopenharmony_ci if (param == TEGRA_PINCONF_PARAM_LOCK) { 57162306a36Sopenharmony_ci if ((val & BIT(bit)) && !arg) { 57262306a36Sopenharmony_ci dev_err(pctldev->dev, "LOCK bit cannot be cleared\n"); 57362306a36Sopenharmony_ci return -EINVAL; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci } 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci /* Special-case Boolean values; allow any non-zero as true */ 57862306a36Sopenharmony_ci if (width == 1) 57962306a36Sopenharmony_ci arg = !!arg; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci /* Range-check user-supplied value */ 58262306a36Sopenharmony_ci mask = (1 << width) - 1; 58362306a36Sopenharmony_ci if (arg & ~mask) { 58462306a36Sopenharmony_ci dev_err(pctldev->dev, 58562306a36Sopenharmony_ci "config %lx: %x too big for %d bit register\n", 58662306a36Sopenharmony_ci configs[i], arg, width); 58762306a36Sopenharmony_ci return -EINVAL; 58862306a36Sopenharmony_ci } 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci /* Update register */ 59162306a36Sopenharmony_ci val &= ~(mask << bit); 59262306a36Sopenharmony_ci val |= arg << bit; 59362306a36Sopenharmony_ci pmx_writel(pmx, val, bank, reg); 59462306a36Sopenharmony_ci } /* for each config */ 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci return 0; 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 60062306a36Sopenharmony_cistatic void tegra_pinconf_dbg_show(struct pinctrl_dev *pctldev, 60162306a36Sopenharmony_ci struct seq_file *s, unsigned offset) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci} 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_cistatic const char *strip_prefix(const char *s) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci const char *comma = strchr(s, ','); 60862306a36Sopenharmony_ci if (!comma) 60962306a36Sopenharmony_ci return s; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci return comma + 1; 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_cistatic void tegra_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, 61562306a36Sopenharmony_ci struct seq_file *s, unsigned group) 61662306a36Sopenharmony_ci{ 61762306a36Sopenharmony_ci struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 61862306a36Sopenharmony_ci const struct tegra_pingroup *g; 61962306a36Sopenharmony_ci int i, ret; 62062306a36Sopenharmony_ci s8 bank, bit, width; 62162306a36Sopenharmony_ci s32 reg; 62262306a36Sopenharmony_ci u32 val; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci g = &pmx->soc->groups[group]; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cfg_params); i++) { 62762306a36Sopenharmony_ci ret = tegra_pinconf_reg(pmx, g, cfg_params[i].param, false, 62862306a36Sopenharmony_ci &bank, ®, &bit, &width); 62962306a36Sopenharmony_ci if (ret < 0) 63062306a36Sopenharmony_ci continue; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci val = pmx_readl(pmx, bank, reg); 63362306a36Sopenharmony_ci val >>= bit; 63462306a36Sopenharmony_ci val &= (1 << width) - 1; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci seq_printf(s, "\n\t%s=%u", 63762306a36Sopenharmony_ci strip_prefix(cfg_params[i].property), val); 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci} 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_cistatic void tegra_pinconf_config_dbg_show(struct pinctrl_dev *pctldev, 64262306a36Sopenharmony_ci struct seq_file *s, 64362306a36Sopenharmony_ci unsigned long config) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci enum tegra_pinconf_param param = TEGRA_PINCONF_UNPACK_PARAM(config); 64662306a36Sopenharmony_ci u16 arg = TEGRA_PINCONF_UNPACK_ARG(config); 64762306a36Sopenharmony_ci const char *pname = "unknown"; 64862306a36Sopenharmony_ci int i; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cfg_params); i++) { 65162306a36Sopenharmony_ci if (cfg_params[i].param == param) { 65262306a36Sopenharmony_ci pname = cfg_params[i].property; 65362306a36Sopenharmony_ci break; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci seq_printf(s, "%s=%d", strip_prefix(pname), arg); 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci#endif 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_cistatic const struct pinconf_ops tegra_pinconf_ops = { 66262306a36Sopenharmony_ci .pin_config_get = tegra_pinconf_get, 66362306a36Sopenharmony_ci .pin_config_set = tegra_pinconf_set, 66462306a36Sopenharmony_ci .pin_config_group_get = tegra_pinconf_group_get, 66562306a36Sopenharmony_ci .pin_config_group_set = tegra_pinconf_group_set, 66662306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 66762306a36Sopenharmony_ci .pin_config_dbg_show = tegra_pinconf_dbg_show, 66862306a36Sopenharmony_ci .pin_config_group_dbg_show = tegra_pinconf_group_dbg_show, 66962306a36Sopenharmony_ci .pin_config_config_dbg_show = tegra_pinconf_config_dbg_show, 67062306a36Sopenharmony_ci#endif 67162306a36Sopenharmony_ci}; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_cistatic void tegra_pinctrl_clear_parked_bits(struct tegra_pmx *pmx) 67462306a36Sopenharmony_ci{ 67562306a36Sopenharmony_ci int i = 0; 67662306a36Sopenharmony_ci const struct tegra_pingroup *g; 67762306a36Sopenharmony_ci u32 val; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci for (i = 0; i < pmx->soc->ngroups; ++i) { 68062306a36Sopenharmony_ci g = &pmx->soc->groups[i]; 68162306a36Sopenharmony_ci if (g->parked_bitmask > 0) { 68262306a36Sopenharmony_ci unsigned int bank, reg; 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci if (g->mux_reg != -1) { 68562306a36Sopenharmony_ci bank = g->mux_bank; 68662306a36Sopenharmony_ci reg = g->mux_reg; 68762306a36Sopenharmony_ci } else { 68862306a36Sopenharmony_ci bank = g->drv_bank; 68962306a36Sopenharmony_ci reg = g->drv_reg; 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci val = pmx_readl(pmx, bank, reg); 69362306a36Sopenharmony_ci val &= ~g->parked_bitmask; 69462306a36Sopenharmony_ci pmx_writel(pmx, val, bank, reg); 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci} 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_cistatic size_t tegra_pinctrl_get_bank_size(struct device *dev, 70062306a36Sopenharmony_ci unsigned int bank_id) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 70362306a36Sopenharmony_ci struct resource *res; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, bank_id); 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci return resource_size(res) / 4; 70862306a36Sopenharmony_ci} 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_cistatic int tegra_pinctrl_suspend(struct device *dev) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci struct tegra_pmx *pmx = dev_get_drvdata(dev); 71362306a36Sopenharmony_ci u32 *backup_regs = pmx->backup_regs; 71462306a36Sopenharmony_ci u32 __iomem *regs; 71562306a36Sopenharmony_ci size_t bank_size; 71662306a36Sopenharmony_ci unsigned int i, k; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci for (i = 0; i < pmx->nbanks; i++) { 71962306a36Sopenharmony_ci bank_size = tegra_pinctrl_get_bank_size(dev, i); 72062306a36Sopenharmony_ci regs = pmx->regs[i]; 72162306a36Sopenharmony_ci for (k = 0; k < bank_size; k++) 72262306a36Sopenharmony_ci *backup_regs++ = readl_relaxed(regs++); 72362306a36Sopenharmony_ci } 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci return pinctrl_force_sleep(pmx->pctl); 72662306a36Sopenharmony_ci} 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_cistatic int tegra_pinctrl_resume(struct device *dev) 72962306a36Sopenharmony_ci{ 73062306a36Sopenharmony_ci struct tegra_pmx *pmx = dev_get_drvdata(dev); 73162306a36Sopenharmony_ci u32 *backup_regs = pmx->backup_regs; 73262306a36Sopenharmony_ci u32 __iomem *regs; 73362306a36Sopenharmony_ci size_t bank_size; 73462306a36Sopenharmony_ci unsigned int i, k; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci for (i = 0; i < pmx->nbanks; i++) { 73762306a36Sopenharmony_ci bank_size = tegra_pinctrl_get_bank_size(dev, i); 73862306a36Sopenharmony_ci regs = pmx->regs[i]; 73962306a36Sopenharmony_ci for (k = 0; k < bank_size; k++) 74062306a36Sopenharmony_ci writel_relaxed(*backup_regs++, regs++); 74162306a36Sopenharmony_ci } 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci /* flush all the prior writes */ 74462306a36Sopenharmony_ci readl_relaxed(pmx->regs[0]); 74562306a36Sopenharmony_ci /* wait for pinctrl register read to complete */ 74662306a36Sopenharmony_ci rmb(); 74762306a36Sopenharmony_ci return 0; 74862306a36Sopenharmony_ci} 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ciDEFINE_NOIRQ_DEV_PM_OPS(tegra_pinctrl_pm, tegra_pinctrl_suspend, tegra_pinctrl_resume); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_cistatic bool tegra_pinctrl_gpio_node_has_range(struct tegra_pmx *pmx) 75362306a36Sopenharmony_ci{ 75462306a36Sopenharmony_ci struct device_node *np; 75562306a36Sopenharmony_ci bool has_prop = false; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, pmx->soc->gpio_compatible); 75862306a36Sopenharmony_ci if (!np) 75962306a36Sopenharmony_ci return has_prop; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci has_prop = of_find_property(np, "gpio-ranges", NULL); 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci of_node_put(np); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci return has_prop; 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ciint tegra_pinctrl_probe(struct platform_device *pdev, 76962306a36Sopenharmony_ci const struct tegra_pinctrl_soc_data *soc_data) 77062306a36Sopenharmony_ci{ 77162306a36Sopenharmony_ci struct tegra_pmx *pmx; 77262306a36Sopenharmony_ci struct resource *res; 77362306a36Sopenharmony_ci int i; 77462306a36Sopenharmony_ci const char **group_pins; 77562306a36Sopenharmony_ci int fn, gn, gfn; 77662306a36Sopenharmony_ci unsigned long backup_regs_size = 0; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci pmx = devm_kzalloc(&pdev->dev, sizeof(*pmx), GFP_KERNEL); 77962306a36Sopenharmony_ci if (!pmx) 78062306a36Sopenharmony_ci return -ENOMEM; 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci pmx->dev = &pdev->dev; 78362306a36Sopenharmony_ci pmx->soc = soc_data; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci /* 78662306a36Sopenharmony_ci * Each mux group will appear in 4 functions' list of groups. 78762306a36Sopenharmony_ci * This over-allocates slightly, since not all groups are mux groups. 78862306a36Sopenharmony_ci */ 78962306a36Sopenharmony_ci pmx->group_pins = devm_kcalloc(&pdev->dev, pmx->soc->ngroups * 4, 79062306a36Sopenharmony_ci sizeof(*pmx->group_pins), GFP_KERNEL); 79162306a36Sopenharmony_ci if (!pmx->group_pins) 79262306a36Sopenharmony_ci return -ENOMEM; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci pmx->functions = devm_kcalloc(&pdev->dev, pmx->soc->nfunctions, 79562306a36Sopenharmony_ci sizeof(*pmx->functions), GFP_KERNEL); 79662306a36Sopenharmony_ci if (!pmx->functions) 79762306a36Sopenharmony_ci return -ENOMEM; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci group_pins = pmx->group_pins; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci for (fn = 0; fn < pmx->soc->nfunctions; fn++) { 80262306a36Sopenharmony_ci struct tegra_function *func = &pmx->functions[fn]; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci func->name = pmx->soc->functions[fn]; 80562306a36Sopenharmony_ci func->groups = group_pins; 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci for (gn = 0; gn < pmx->soc->ngroups; gn++) { 80862306a36Sopenharmony_ci const struct tegra_pingroup *g = &pmx->soc->groups[gn]; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci if (g->mux_reg == -1) 81162306a36Sopenharmony_ci continue; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci for (gfn = 0; gfn < 4; gfn++) 81462306a36Sopenharmony_ci if (g->funcs[gfn] == fn) 81562306a36Sopenharmony_ci break; 81662306a36Sopenharmony_ci if (gfn == 4) 81762306a36Sopenharmony_ci continue; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci BUG_ON(group_pins - pmx->group_pins >= 82062306a36Sopenharmony_ci pmx->soc->ngroups * 4); 82162306a36Sopenharmony_ci *group_pins++ = g->name; 82262306a36Sopenharmony_ci func->ngroups++; 82362306a36Sopenharmony_ci } 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci pmx->gpio_range.name = "Tegra GPIOs"; 82762306a36Sopenharmony_ci pmx->gpio_range.id = 0; 82862306a36Sopenharmony_ci pmx->gpio_range.base = 0; 82962306a36Sopenharmony_ci pmx->gpio_range.npins = pmx->soc->ngpios; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci pmx->desc.pctlops = &tegra_pinctrl_ops; 83262306a36Sopenharmony_ci pmx->desc.pmxops = &tegra_pinmux_ops; 83362306a36Sopenharmony_ci pmx->desc.confops = &tegra_pinconf_ops; 83462306a36Sopenharmony_ci pmx->desc.owner = THIS_MODULE; 83562306a36Sopenharmony_ci pmx->desc.name = dev_name(&pdev->dev); 83662306a36Sopenharmony_ci pmx->desc.pins = pmx->soc->pins; 83762306a36Sopenharmony_ci pmx->desc.npins = pmx->soc->npins; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci for (i = 0; ; i++) { 84062306a36Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, i); 84162306a36Sopenharmony_ci if (!res) 84262306a36Sopenharmony_ci break; 84362306a36Sopenharmony_ci backup_regs_size += resource_size(res); 84462306a36Sopenharmony_ci } 84562306a36Sopenharmony_ci pmx->nbanks = i; 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci pmx->regs = devm_kcalloc(&pdev->dev, pmx->nbanks, sizeof(*pmx->regs), 84862306a36Sopenharmony_ci GFP_KERNEL); 84962306a36Sopenharmony_ci if (!pmx->regs) 85062306a36Sopenharmony_ci return -ENOMEM; 85162306a36Sopenharmony_ci 85262306a36Sopenharmony_ci pmx->backup_regs = devm_kzalloc(&pdev->dev, backup_regs_size, 85362306a36Sopenharmony_ci GFP_KERNEL); 85462306a36Sopenharmony_ci if (!pmx->backup_regs) 85562306a36Sopenharmony_ci return -ENOMEM; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci for (i = 0; i < pmx->nbanks; i++) { 85862306a36Sopenharmony_ci pmx->regs[i] = devm_platform_ioremap_resource(pdev, i); 85962306a36Sopenharmony_ci if (IS_ERR(pmx->regs[i])) 86062306a36Sopenharmony_ci return PTR_ERR(pmx->regs[i]); 86162306a36Sopenharmony_ci } 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci pmx->pctl = devm_pinctrl_register(&pdev->dev, &pmx->desc, pmx); 86462306a36Sopenharmony_ci if (IS_ERR(pmx->pctl)) { 86562306a36Sopenharmony_ci dev_err(&pdev->dev, "Couldn't register pinctrl driver\n"); 86662306a36Sopenharmony_ci return PTR_ERR(pmx->pctl); 86762306a36Sopenharmony_ci } 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci tegra_pinctrl_clear_parked_bits(pmx); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci if (pmx->soc->ngpios > 0 && !tegra_pinctrl_gpio_node_has_range(pmx)) 87262306a36Sopenharmony_ci pinctrl_add_gpio_range(pmx->pctl, &pmx->gpio_range); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci platform_set_drvdata(pdev, pmx); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Probed Tegra pinctrl driver\n"); 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci return 0; 87962306a36Sopenharmony_ci} 880