18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for the NVIDIA Tegra pinmux 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Derived from code: 88c2ecf20Sopenharmony_ci * Copyright (C) 2010 Google, Inc. 98c2ecf20Sopenharmony_ci * Copyright (C) 2010 NVIDIA Corporation 108c2ecf20Sopenharmony_ci * Copyright (C) 2009-2011 ST-Ericsson AB 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/err.h> 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci#include <linux/of.h> 178c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 188c2ecf20Sopenharmony_ci#include <linux/pinctrl/machine.h> 198c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinctrl.h> 208c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinmux.h> 218c2ecf20Sopenharmony_ci#include <linux/pinctrl/pinconf.h> 228c2ecf20Sopenharmony_ci#include <linux/slab.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "../core.h" 258c2ecf20Sopenharmony_ci#include "../pinctrl-utils.h" 268c2ecf20Sopenharmony_ci#include "pinctrl-tegra.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic inline u32 pmx_readl(struct tegra_pmx *pmx, u32 bank, u32 reg) 298c2ecf20Sopenharmony_ci{ 308c2ecf20Sopenharmony_ci return readl(pmx->regs[bank] + reg); 318c2ecf20Sopenharmony_ci} 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic inline void pmx_writel(struct tegra_pmx *pmx, u32 val, u32 bank, u32 reg) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci writel_relaxed(val, pmx->regs[bank] + reg); 368c2ecf20Sopenharmony_ci /* make sure pinmux register write completed */ 378c2ecf20Sopenharmony_ci pmx_readl(pmx, bank, reg); 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int tegra_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci return pmx->soc->ngroups; 458c2ecf20Sopenharmony_ci} 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic const char *tegra_pinctrl_get_group_name(struct pinctrl_dev *pctldev, 488c2ecf20Sopenharmony_ci unsigned group) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci return pmx->soc->groups[group].name; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic int tegra_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, 568c2ecf20Sopenharmony_ci unsigned group, 578c2ecf20Sopenharmony_ci const unsigned **pins, 588c2ecf20Sopenharmony_ci unsigned *num_pins) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci *pins = pmx->soc->groups[group].pins; 638c2ecf20Sopenharmony_ci *num_pins = pmx->soc->groups[group].npins; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci return 0; 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 698c2ecf20Sopenharmony_cistatic void tegra_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev, 708c2ecf20Sopenharmony_ci struct seq_file *s, 718c2ecf20Sopenharmony_ci unsigned offset) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci seq_printf(s, " %s", dev_name(pctldev->dev)); 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci#endif 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic const struct cfg_param { 788c2ecf20Sopenharmony_ci const char *property; 798c2ecf20Sopenharmony_ci enum tegra_pinconf_param param; 808c2ecf20Sopenharmony_ci} cfg_params[] = { 818c2ecf20Sopenharmony_ci {"nvidia,pull", TEGRA_PINCONF_PARAM_PULL}, 828c2ecf20Sopenharmony_ci {"nvidia,tristate", TEGRA_PINCONF_PARAM_TRISTATE}, 838c2ecf20Sopenharmony_ci {"nvidia,enable-input", TEGRA_PINCONF_PARAM_ENABLE_INPUT}, 848c2ecf20Sopenharmony_ci {"nvidia,open-drain", TEGRA_PINCONF_PARAM_OPEN_DRAIN}, 858c2ecf20Sopenharmony_ci {"nvidia,lock", TEGRA_PINCONF_PARAM_LOCK}, 868c2ecf20Sopenharmony_ci {"nvidia,io-reset", TEGRA_PINCONF_PARAM_IORESET}, 878c2ecf20Sopenharmony_ci {"nvidia,rcv-sel", TEGRA_PINCONF_PARAM_RCV_SEL}, 888c2ecf20Sopenharmony_ci {"nvidia,io-hv", TEGRA_PINCONF_PARAM_RCV_SEL}, 898c2ecf20Sopenharmony_ci {"nvidia,high-speed-mode", TEGRA_PINCONF_PARAM_HIGH_SPEED_MODE}, 908c2ecf20Sopenharmony_ci {"nvidia,schmitt", TEGRA_PINCONF_PARAM_SCHMITT}, 918c2ecf20Sopenharmony_ci {"nvidia,low-power-mode", TEGRA_PINCONF_PARAM_LOW_POWER_MODE}, 928c2ecf20Sopenharmony_ci {"nvidia,pull-down-strength", TEGRA_PINCONF_PARAM_DRIVE_DOWN_STRENGTH}, 938c2ecf20Sopenharmony_ci {"nvidia,pull-up-strength", TEGRA_PINCONF_PARAM_DRIVE_UP_STRENGTH}, 948c2ecf20Sopenharmony_ci {"nvidia,slew-rate-falling", TEGRA_PINCONF_PARAM_SLEW_RATE_FALLING}, 958c2ecf20Sopenharmony_ci {"nvidia,slew-rate-rising", TEGRA_PINCONF_PARAM_SLEW_RATE_RISING}, 968c2ecf20Sopenharmony_ci {"nvidia,drive-type", TEGRA_PINCONF_PARAM_DRIVE_TYPE}, 978c2ecf20Sopenharmony_ci}; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic int tegra_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev, 1008c2ecf20Sopenharmony_ci struct device_node *np, 1018c2ecf20Sopenharmony_ci struct pinctrl_map **map, 1028c2ecf20Sopenharmony_ci unsigned *reserved_maps, 1038c2ecf20Sopenharmony_ci unsigned *num_maps) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct device *dev = pctldev->dev; 1068c2ecf20Sopenharmony_ci int ret, i; 1078c2ecf20Sopenharmony_ci const char *function; 1088c2ecf20Sopenharmony_ci u32 val; 1098c2ecf20Sopenharmony_ci unsigned long config; 1108c2ecf20Sopenharmony_ci unsigned long *configs = NULL; 1118c2ecf20Sopenharmony_ci unsigned num_configs = 0; 1128c2ecf20Sopenharmony_ci unsigned reserve; 1138c2ecf20Sopenharmony_ci struct property *prop; 1148c2ecf20Sopenharmony_ci const char *group; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci ret = of_property_read_string(np, "nvidia,function", &function); 1178c2ecf20Sopenharmony_ci if (ret < 0) { 1188c2ecf20Sopenharmony_ci /* EINVAL=missing, which is fine since it's optional */ 1198c2ecf20Sopenharmony_ci if (ret != -EINVAL) 1208c2ecf20Sopenharmony_ci dev_err(dev, 1218c2ecf20Sopenharmony_ci "could not parse property nvidia,function\n"); 1228c2ecf20Sopenharmony_ci function = NULL; 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cfg_params); i++) { 1268c2ecf20Sopenharmony_ci ret = of_property_read_u32(np, cfg_params[i].property, &val); 1278c2ecf20Sopenharmony_ci if (!ret) { 1288c2ecf20Sopenharmony_ci config = TEGRA_PINCONF_PACK(cfg_params[i].param, val); 1298c2ecf20Sopenharmony_ci ret = pinctrl_utils_add_config(pctldev, &configs, 1308c2ecf20Sopenharmony_ci &num_configs, config); 1318c2ecf20Sopenharmony_ci if (ret < 0) 1328c2ecf20Sopenharmony_ci goto exit; 1338c2ecf20Sopenharmony_ci /* EINVAL=missing, which is fine since it's optional */ 1348c2ecf20Sopenharmony_ci } else if (ret != -EINVAL) { 1358c2ecf20Sopenharmony_ci dev_err(dev, "could not parse property %s\n", 1368c2ecf20Sopenharmony_ci cfg_params[i].property); 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci reserve = 0; 1418c2ecf20Sopenharmony_ci if (function != NULL) 1428c2ecf20Sopenharmony_ci reserve++; 1438c2ecf20Sopenharmony_ci if (num_configs) 1448c2ecf20Sopenharmony_ci reserve++; 1458c2ecf20Sopenharmony_ci ret = of_property_count_strings(np, "nvidia,pins"); 1468c2ecf20Sopenharmony_ci if (ret < 0) { 1478c2ecf20Sopenharmony_ci dev_err(dev, "could not parse property nvidia,pins\n"); 1488c2ecf20Sopenharmony_ci goto exit; 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci reserve *= ret; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci ret = pinctrl_utils_reserve_map(pctldev, map, reserved_maps, 1538c2ecf20Sopenharmony_ci num_maps, reserve); 1548c2ecf20Sopenharmony_ci if (ret < 0) 1558c2ecf20Sopenharmony_ci goto exit; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci of_property_for_each_string(np, "nvidia,pins", prop, group) { 1588c2ecf20Sopenharmony_ci if (function) { 1598c2ecf20Sopenharmony_ci ret = pinctrl_utils_add_map_mux(pctldev, map, 1608c2ecf20Sopenharmony_ci reserved_maps, num_maps, group, 1618c2ecf20Sopenharmony_ci function); 1628c2ecf20Sopenharmony_ci if (ret < 0) 1638c2ecf20Sopenharmony_ci goto exit; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci if (num_configs) { 1678c2ecf20Sopenharmony_ci ret = pinctrl_utils_add_map_configs(pctldev, map, 1688c2ecf20Sopenharmony_ci reserved_maps, num_maps, group, 1698c2ecf20Sopenharmony_ci configs, num_configs, 1708c2ecf20Sopenharmony_ci PIN_MAP_TYPE_CONFIGS_GROUP); 1718c2ecf20Sopenharmony_ci if (ret < 0) 1728c2ecf20Sopenharmony_ci goto exit; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci ret = 0; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ciexit: 1798c2ecf20Sopenharmony_ci kfree(configs); 1808c2ecf20Sopenharmony_ci return ret; 1818c2ecf20Sopenharmony_ci} 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic int tegra_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, 1848c2ecf20Sopenharmony_ci struct device_node *np_config, 1858c2ecf20Sopenharmony_ci struct pinctrl_map **map, 1868c2ecf20Sopenharmony_ci unsigned *num_maps) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci unsigned reserved_maps; 1898c2ecf20Sopenharmony_ci struct device_node *np; 1908c2ecf20Sopenharmony_ci int ret; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci reserved_maps = 0; 1938c2ecf20Sopenharmony_ci *map = NULL; 1948c2ecf20Sopenharmony_ci *num_maps = 0; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci for_each_child_of_node(np_config, np) { 1978c2ecf20Sopenharmony_ci ret = tegra_pinctrl_dt_subnode_to_map(pctldev, np, map, 1988c2ecf20Sopenharmony_ci &reserved_maps, num_maps); 1998c2ecf20Sopenharmony_ci if (ret < 0) { 2008c2ecf20Sopenharmony_ci pinctrl_utils_free_map(pctldev, *map, 2018c2ecf20Sopenharmony_ci *num_maps); 2028c2ecf20Sopenharmony_ci of_node_put(np); 2038c2ecf20Sopenharmony_ci return ret; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci return 0; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_cistatic const struct pinctrl_ops tegra_pinctrl_ops = { 2118c2ecf20Sopenharmony_ci .get_groups_count = tegra_pinctrl_get_groups_count, 2128c2ecf20Sopenharmony_ci .get_group_name = tegra_pinctrl_get_group_name, 2138c2ecf20Sopenharmony_ci .get_group_pins = tegra_pinctrl_get_group_pins, 2148c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 2158c2ecf20Sopenharmony_ci .pin_dbg_show = tegra_pinctrl_pin_dbg_show, 2168c2ecf20Sopenharmony_ci#endif 2178c2ecf20Sopenharmony_ci .dt_node_to_map = tegra_pinctrl_dt_node_to_map, 2188c2ecf20Sopenharmony_ci .dt_free_map = pinctrl_utils_free_map, 2198c2ecf20Sopenharmony_ci}; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic int tegra_pinctrl_get_funcs_count(struct pinctrl_dev *pctldev) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci return pmx->soc->nfunctions; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic const char *tegra_pinctrl_get_func_name(struct pinctrl_dev *pctldev, 2298c2ecf20Sopenharmony_ci unsigned function) 2308c2ecf20Sopenharmony_ci{ 2318c2ecf20Sopenharmony_ci struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci return pmx->soc->functions[function].name; 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic int tegra_pinctrl_get_func_groups(struct pinctrl_dev *pctldev, 2378c2ecf20Sopenharmony_ci unsigned function, 2388c2ecf20Sopenharmony_ci const char * const **groups, 2398c2ecf20Sopenharmony_ci unsigned * const num_groups) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci *groups = pmx->soc->functions[function].groups; 2448c2ecf20Sopenharmony_ci *num_groups = pmx->soc->functions[function].ngroups; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci return 0; 2478c2ecf20Sopenharmony_ci} 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic int tegra_pinctrl_set_mux(struct pinctrl_dev *pctldev, 2508c2ecf20Sopenharmony_ci unsigned function, 2518c2ecf20Sopenharmony_ci unsigned group) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 2548c2ecf20Sopenharmony_ci const struct tegra_pingroup *g; 2558c2ecf20Sopenharmony_ci int i; 2568c2ecf20Sopenharmony_ci u32 val; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci g = &pmx->soc->groups[group]; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (WARN_ON(g->mux_reg < 0)) 2618c2ecf20Sopenharmony_ci return -EINVAL; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(g->funcs); i++) { 2648c2ecf20Sopenharmony_ci if (g->funcs[i] == function) 2658c2ecf20Sopenharmony_ci break; 2668c2ecf20Sopenharmony_ci } 2678c2ecf20Sopenharmony_ci if (WARN_ON(i == ARRAY_SIZE(g->funcs))) 2688c2ecf20Sopenharmony_ci return -EINVAL; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci val = pmx_readl(pmx, g->mux_bank, g->mux_reg); 2718c2ecf20Sopenharmony_ci val &= ~(0x3 << g->mux_bit); 2728c2ecf20Sopenharmony_ci val |= i << g->mux_bit; 2738c2ecf20Sopenharmony_ci pmx_writel(pmx, val, g->mux_bank, g->mux_reg); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci return 0; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_cistatic int tegra_pinctrl_gpio_request_enable(struct pinctrl_dev *pctldev, 2798c2ecf20Sopenharmony_ci struct pinctrl_gpio_range *range, 2808c2ecf20Sopenharmony_ci unsigned int offset) 2818c2ecf20Sopenharmony_ci{ 2828c2ecf20Sopenharmony_ci struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 2838c2ecf20Sopenharmony_ci const struct tegra_pingroup *group; 2848c2ecf20Sopenharmony_ci u32 value; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (!pmx->soc->sfsel_in_mux) 2878c2ecf20Sopenharmony_ci return 0; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci group = &pmx->soc->groups[offset]; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (group->mux_reg < 0 || group->sfsel_bit < 0) 2928c2ecf20Sopenharmony_ci return -EINVAL; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci value = pmx_readl(pmx, group->mux_bank, group->mux_reg); 2958c2ecf20Sopenharmony_ci value &= ~BIT(group->sfsel_bit); 2968c2ecf20Sopenharmony_ci pmx_writel(pmx, value, group->mux_bank, group->mux_reg); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci return 0; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cistatic void tegra_pinctrl_gpio_disable_free(struct pinctrl_dev *pctldev, 3028c2ecf20Sopenharmony_ci struct pinctrl_gpio_range *range, 3038c2ecf20Sopenharmony_ci unsigned int offset) 3048c2ecf20Sopenharmony_ci{ 3058c2ecf20Sopenharmony_ci struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 3068c2ecf20Sopenharmony_ci const struct tegra_pingroup *group; 3078c2ecf20Sopenharmony_ci u32 value; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci if (!pmx->soc->sfsel_in_mux) 3108c2ecf20Sopenharmony_ci return; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci group = &pmx->soc->groups[offset]; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (group->mux_reg < 0 || group->sfsel_bit < 0) 3158c2ecf20Sopenharmony_ci return; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci value = pmx_readl(pmx, group->mux_bank, group->mux_reg); 3188c2ecf20Sopenharmony_ci value |= BIT(group->sfsel_bit); 3198c2ecf20Sopenharmony_ci pmx_writel(pmx, value, group->mux_bank, group->mux_reg); 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic const struct pinmux_ops tegra_pinmux_ops = { 3238c2ecf20Sopenharmony_ci .get_functions_count = tegra_pinctrl_get_funcs_count, 3248c2ecf20Sopenharmony_ci .get_function_name = tegra_pinctrl_get_func_name, 3258c2ecf20Sopenharmony_ci .get_function_groups = tegra_pinctrl_get_func_groups, 3268c2ecf20Sopenharmony_ci .set_mux = tegra_pinctrl_set_mux, 3278c2ecf20Sopenharmony_ci .gpio_request_enable = tegra_pinctrl_gpio_request_enable, 3288c2ecf20Sopenharmony_ci .gpio_disable_free = tegra_pinctrl_gpio_disable_free, 3298c2ecf20Sopenharmony_ci}; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic int tegra_pinconf_reg(struct tegra_pmx *pmx, 3328c2ecf20Sopenharmony_ci const struct tegra_pingroup *g, 3338c2ecf20Sopenharmony_ci enum tegra_pinconf_param param, 3348c2ecf20Sopenharmony_ci bool report_err, 3358c2ecf20Sopenharmony_ci s8 *bank, s32 *reg, s8 *bit, s8 *width) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci switch (param) { 3388c2ecf20Sopenharmony_ci case TEGRA_PINCONF_PARAM_PULL: 3398c2ecf20Sopenharmony_ci *bank = g->pupd_bank; 3408c2ecf20Sopenharmony_ci *reg = g->pupd_reg; 3418c2ecf20Sopenharmony_ci *bit = g->pupd_bit; 3428c2ecf20Sopenharmony_ci *width = 2; 3438c2ecf20Sopenharmony_ci break; 3448c2ecf20Sopenharmony_ci case TEGRA_PINCONF_PARAM_TRISTATE: 3458c2ecf20Sopenharmony_ci *bank = g->tri_bank; 3468c2ecf20Sopenharmony_ci *reg = g->tri_reg; 3478c2ecf20Sopenharmony_ci *bit = g->tri_bit; 3488c2ecf20Sopenharmony_ci *width = 1; 3498c2ecf20Sopenharmony_ci break; 3508c2ecf20Sopenharmony_ci case TEGRA_PINCONF_PARAM_ENABLE_INPUT: 3518c2ecf20Sopenharmony_ci *bank = g->mux_bank; 3528c2ecf20Sopenharmony_ci *reg = g->mux_reg; 3538c2ecf20Sopenharmony_ci *bit = g->einput_bit; 3548c2ecf20Sopenharmony_ci *width = 1; 3558c2ecf20Sopenharmony_ci break; 3568c2ecf20Sopenharmony_ci case TEGRA_PINCONF_PARAM_OPEN_DRAIN: 3578c2ecf20Sopenharmony_ci *bank = g->mux_bank; 3588c2ecf20Sopenharmony_ci *reg = g->mux_reg; 3598c2ecf20Sopenharmony_ci *bit = g->odrain_bit; 3608c2ecf20Sopenharmony_ci *width = 1; 3618c2ecf20Sopenharmony_ci break; 3628c2ecf20Sopenharmony_ci case TEGRA_PINCONF_PARAM_LOCK: 3638c2ecf20Sopenharmony_ci *bank = g->mux_bank; 3648c2ecf20Sopenharmony_ci *reg = g->mux_reg; 3658c2ecf20Sopenharmony_ci *bit = g->lock_bit; 3668c2ecf20Sopenharmony_ci *width = 1; 3678c2ecf20Sopenharmony_ci break; 3688c2ecf20Sopenharmony_ci case TEGRA_PINCONF_PARAM_IORESET: 3698c2ecf20Sopenharmony_ci *bank = g->mux_bank; 3708c2ecf20Sopenharmony_ci *reg = g->mux_reg; 3718c2ecf20Sopenharmony_ci *bit = g->ioreset_bit; 3728c2ecf20Sopenharmony_ci *width = 1; 3738c2ecf20Sopenharmony_ci break; 3748c2ecf20Sopenharmony_ci case TEGRA_PINCONF_PARAM_RCV_SEL: 3758c2ecf20Sopenharmony_ci *bank = g->mux_bank; 3768c2ecf20Sopenharmony_ci *reg = g->mux_reg; 3778c2ecf20Sopenharmony_ci *bit = g->rcv_sel_bit; 3788c2ecf20Sopenharmony_ci *width = 1; 3798c2ecf20Sopenharmony_ci break; 3808c2ecf20Sopenharmony_ci case TEGRA_PINCONF_PARAM_HIGH_SPEED_MODE: 3818c2ecf20Sopenharmony_ci if (pmx->soc->hsm_in_mux) { 3828c2ecf20Sopenharmony_ci *bank = g->mux_bank; 3838c2ecf20Sopenharmony_ci *reg = g->mux_reg; 3848c2ecf20Sopenharmony_ci } else { 3858c2ecf20Sopenharmony_ci *bank = g->drv_bank; 3868c2ecf20Sopenharmony_ci *reg = g->drv_reg; 3878c2ecf20Sopenharmony_ci } 3888c2ecf20Sopenharmony_ci *bit = g->hsm_bit; 3898c2ecf20Sopenharmony_ci *width = 1; 3908c2ecf20Sopenharmony_ci break; 3918c2ecf20Sopenharmony_ci case TEGRA_PINCONF_PARAM_SCHMITT: 3928c2ecf20Sopenharmony_ci if (pmx->soc->schmitt_in_mux) { 3938c2ecf20Sopenharmony_ci *bank = g->mux_bank; 3948c2ecf20Sopenharmony_ci *reg = g->mux_reg; 3958c2ecf20Sopenharmony_ci } else { 3968c2ecf20Sopenharmony_ci *bank = g->drv_bank; 3978c2ecf20Sopenharmony_ci *reg = g->drv_reg; 3988c2ecf20Sopenharmony_ci } 3998c2ecf20Sopenharmony_ci *bit = g->schmitt_bit; 4008c2ecf20Sopenharmony_ci *width = 1; 4018c2ecf20Sopenharmony_ci break; 4028c2ecf20Sopenharmony_ci case TEGRA_PINCONF_PARAM_LOW_POWER_MODE: 4038c2ecf20Sopenharmony_ci *bank = g->drv_bank; 4048c2ecf20Sopenharmony_ci *reg = g->drv_reg; 4058c2ecf20Sopenharmony_ci *bit = g->lpmd_bit; 4068c2ecf20Sopenharmony_ci *width = 2; 4078c2ecf20Sopenharmony_ci break; 4088c2ecf20Sopenharmony_ci case TEGRA_PINCONF_PARAM_DRIVE_DOWN_STRENGTH: 4098c2ecf20Sopenharmony_ci *bank = g->drv_bank; 4108c2ecf20Sopenharmony_ci *reg = g->drv_reg; 4118c2ecf20Sopenharmony_ci *bit = g->drvdn_bit; 4128c2ecf20Sopenharmony_ci *width = g->drvdn_width; 4138c2ecf20Sopenharmony_ci break; 4148c2ecf20Sopenharmony_ci case TEGRA_PINCONF_PARAM_DRIVE_UP_STRENGTH: 4158c2ecf20Sopenharmony_ci *bank = g->drv_bank; 4168c2ecf20Sopenharmony_ci *reg = g->drv_reg; 4178c2ecf20Sopenharmony_ci *bit = g->drvup_bit; 4188c2ecf20Sopenharmony_ci *width = g->drvup_width; 4198c2ecf20Sopenharmony_ci break; 4208c2ecf20Sopenharmony_ci case TEGRA_PINCONF_PARAM_SLEW_RATE_FALLING: 4218c2ecf20Sopenharmony_ci *bank = g->drv_bank; 4228c2ecf20Sopenharmony_ci *reg = g->drv_reg; 4238c2ecf20Sopenharmony_ci *bit = g->slwf_bit; 4248c2ecf20Sopenharmony_ci *width = g->slwf_width; 4258c2ecf20Sopenharmony_ci break; 4268c2ecf20Sopenharmony_ci case TEGRA_PINCONF_PARAM_SLEW_RATE_RISING: 4278c2ecf20Sopenharmony_ci *bank = g->drv_bank; 4288c2ecf20Sopenharmony_ci *reg = g->drv_reg; 4298c2ecf20Sopenharmony_ci *bit = g->slwr_bit; 4308c2ecf20Sopenharmony_ci *width = g->slwr_width; 4318c2ecf20Sopenharmony_ci break; 4328c2ecf20Sopenharmony_ci case TEGRA_PINCONF_PARAM_DRIVE_TYPE: 4338c2ecf20Sopenharmony_ci if (pmx->soc->drvtype_in_mux) { 4348c2ecf20Sopenharmony_ci *bank = g->mux_bank; 4358c2ecf20Sopenharmony_ci *reg = g->mux_reg; 4368c2ecf20Sopenharmony_ci } else { 4378c2ecf20Sopenharmony_ci *bank = g->drv_bank; 4388c2ecf20Sopenharmony_ci *reg = g->drv_reg; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci *bit = g->drvtype_bit; 4418c2ecf20Sopenharmony_ci *width = 2; 4428c2ecf20Sopenharmony_ci break; 4438c2ecf20Sopenharmony_ci default: 4448c2ecf20Sopenharmony_ci dev_err(pmx->dev, "Invalid config param %04x\n", param); 4458c2ecf20Sopenharmony_ci return -ENOTSUPP; 4468c2ecf20Sopenharmony_ci } 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci if (*reg < 0 || *bit < 0) { 4498c2ecf20Sopenharmony_ci if (report_err) { 4508c2ecf20Sopenharmony_ci const char *prop = "unknown"; 4518c2ecf20Sopenharmony_ci int i; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cfg_params); i++) { 4548c2ecf20Sopenharmony_ci if (cfg_params[i].param == param) { 4558c2ecf20Sopenharmony_ci prop = cfg_params[i].property; 4568c2ecf20Sopenharmony_ci break; 4578c2ecf20Sopenharmony_ci } 4588c2ecf20Sopenharmony_ci } 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci dev_err(pmx->dev, 4618c2ecf20Sopenharmony_ci "Config param %04x (%s) not supported on group %s\n", 4628c2ecf20Sopenharmony_ci param, prop, g->name); 4638c2ecf20Sopenharmony_ci } 4648c2ecf20Sopenharmony_ci return -ENOTSUPP; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci return 0; 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_cistatic int tegra_pinconf_get(struct pinctrl_dev *pctldev, 4718c2ecf20Sopenharmony_ci unsigned pin, unsigned long *config) 4728c2ecf20Sopenharmony_ci{ 4738c2ecf20Sopenharmony_ci dev_err(pctldev->dev, "pin_config_get op not supported\n"); 4748c2ecf20Sopenharmony_ci return -ENOTSUPP; 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic int tegra_pinconf_set(struct pinctrl_dev *pctldev, 4788c2ecf20Sopenharmony_ci unsigned pin, unsigned long *configs, 4798c2ecf20Sopenharmony_ci unsigned num_configs) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci dev_err(pctldev->dev, "pin_config_set op not supported\n"); 4828c2ecf20Sopenharmony_ci return -ENOTSUPP; 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cistatic int tegra_pinconf_group_get(struct pinctrl_dev *pctldev, 4868c2ecf20Sopenharmony_ci unsigned group, unsigned long *config) 4878c2ecf20Sopenharmony_ci{ 4888c2ecf20Sopenharmony_ci struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 4898c2ecf20Sopenharmony_ci enum tegra_pinconf_param param = TEGRA_PINCONF_UNPACK_PARAM(*config); 4908c2ecf20Sopenharmony_ci u16 arg; 4918c2ecf20Sopenharmony_ci const struct tegra_pingroup *g; 4928c2ecf20Sopenharmony_ci int ret; 4938c2ecf20Sopenharmony_ci s8 bank, bit, width; 4948c2ecf20Sopenharmony_ci s32 reg; 4958c2ecf20Sopenharmony_ci u32 val, mask; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci g = &pmx->soc->groups[group]; 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci ret = tegra_pinconf_reg(pmx, g, param, true, &bank, ®, &bit, 5008c2ecf20Sopenharmony_ci &width); 5018c2ecf20Sopenharmony_ci if (ret < 0) 5028c2ecf20Sopenharmony_ci return ret; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci val = pmx_readl(pmx, bank, reg); 5058c2ecf20Sopenharmony_ci mask = (1 << width) - 1; 5068c2ecf20Sopenharmony_ci arg = (val >> bit) & mask; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci *config = TEGRA_PINCONF_PACK(param, arg); 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci return 0; 5118c2ecf20Sopenharmony_ci} 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_cistatic int tegra_pinconf_group_set(struct pinctrl_dev *pctldev, 5148c2ecf20Sopenharmony_ci unsigned group, unsigned long *configs, 5158c2ecf20Sopenharmony_ci unsigned num_configs) 5168c2ecf20Sopenharmony_ci{ 5178c2ecf20Sopenharmony_ci struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 5188c2ecf20Sopenharmony_ci enum tegra_pinconf_param param; 5198c2ecf20Sopenharmony_ci u16 arg; 5208c2ecf20Sopenharmony_ci const struct tegra_pingroup *g; 5218c2ecf20Sopenharmony_ci int ret, i; 5228c2ecf20Sopenharmony_ci s8 bank, bit, width; 5238c2ecf20Sopenharmony_ci s32 reg; 5248c2ecf20Sopenharmony_ci u32 val, mask; 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci g = &pmx->soc->groups[group]; 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci for (i = 0; i < num_configs; i++) { 5298c2ecf20Sopenharmony_ci param = TEGRA_PINCONF_UNPACK_PARAM(configs[i]); 5308c2ecf20Sopenharmony_ci arg = TEGRA_PINCONF_UNPACK_ARG(configs[i]); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci ret = tegra_pinconf_reg(pmx, g, param, true, &bank, ®, &bit, 5338c2ecf20Sopenharmony_ci &width); 5348c2ecf20Sopenharmony_ci if (ret < 0) 5358c2ecf20Sopenharmony_ci return ret; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci val = pmx_readl(pmx, bank, reg); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci /* LOCK can't be cleared */ 5408c2ecf20Sopenharmony_ci if (param == TEGRA_PINCONF_PARAM_LOCK) { 5418c2ecf20Sopenharmony_ci if ((val & BIT(bit)) && !arg) { 5428c2ecf20Sopenharmony_ci dev_err(pctldev->dev, "LOCK bit cannot be cleared\n"); 5438c2ecf20Sopenharmony_ci return -EINVAL; 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci /* Special-case Boolean values; allow any non-zero as true */ 5488c2ecf20Sopenharmony_ci if (width == 1) 5498c2ecf20Sopenharmony_ci arg = !!arg; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci /* Range-check user-supplied value */ 5528c2ecf20Sopenharmony_ci mask = (1 << width) - 1; 5538c2ecf20Sopenharmony_ci if (arg & ~mask) { 5548c2ecf20Sopenharmony_ci dev_err(pctldev->dev, 5558c2ecf20Sopenharmony_ci "config %lx: %x too big for %d bit register\n", 5568c2ecf20Sopenharmony_ci configs[i], arg, width); 5578c2ecf20Sopenharmony_ci return -EINVAL; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci /* Update register */ 5618c2ecf20Sopenharmony_ci val &= ~(mask << bit); 5628c2ecf20Sopenharmony_ci val |= arg << bit; 5638c2ecf20Sopenharmony_ci pmx_writel(pmx, val, bank, reg); 5648c2ecf20Sopenharmony_ci } /* for each config */ 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci return 0; 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 5708c2ecf20Sopenharmony_cistatic void tegra_pinconf_dbg_show(struct pinctrl_dev *pctldev, 5718c2ecf20Sopenharmony_ci struct seq_file *s, unsigned offset) 5728c2ecf20Sopenharmony_ci{ 5738c2ecf20Sopenharmony_ci} 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_cistatic const char *strip_prefix(const char *s) 5768c2ecf20Sopenharmony_ci{ 5778c2ecf20Sopenharmony_ci const char *comma = strchr(s, ','); 5788c2ecf20Sopenharmony_ci if (!comma) 5798c2ecf20Sopenharmony_ci return s; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci return comma + 1; 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_cistatic void tegra_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, 5858c2ecf20Sopenharmony_ci struct seq_file *s, unsigned group) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); 5888c2ecf20Sopenharmony_ci const struct tegra_pingroup *g; 5898c2ecf20Sopenharmony_ci int i, ret; 5908c2ecf20Sopenharmony_ci s8 bank, bit, width; 5918c2ecf20Sopenharmony_ci s32 reg; 5928c2ecf20Sopenharmony_ci u32 val; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci g = &pmx->soc->groups[group]; 5958c2ecf20Sopenharmony_ci 5968c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cfg_params); i++) { 5978c2ecf20Sopenharmony_ci ret = tegra_pinconf_reg(pmx, g, cfg_params[i].param, false, 5988c2ecf20Sopenharmony_ci &bank, ®, &bit, &width); 5998c2ecf20Sopenharmony_ci if (ret < 0) 6008c2ecf20Sopenharmony_ci continue; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci val = pmx_readl(pmx, bank, reg); 6038c2ecf20Sopenharmony_ci val >>= bit; 6048c2ecf20Sopenharmony_ci val &= (1 << width) - 1; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci seq_printf(s, "\n\t%s=%u", 6078c2ecf20Sopenharmony_ci strip_prefix(cfg_params[i].property), val); 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci} 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_cistatic void tegra_pinconf_config_dbg_show(struct pinctrl_dev *pctldev, 6128c2ecf20Sopenharmony_ci struct seq_file *s, 6138c2ecf20Sopenharmony_ci unsigned long config) 6148c2ecf20Sopenharmony_ci{ 6158c2ecf20Sopenharmony_ci enum tegra_pinconf_param param = TEGRA_PINCONF_UNPACK_PARAM(config); 6168c2ecf20Sopenharmony_ci u16 arg = TEGRA_PINCONF_UNPACK_ARG(config); 6178c2ecf20Sopenharmony_ci const char *pname = "unknown"; 6188c2ecf20Sopenharmony_ci int i; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(cfg_params); i++) { 6218c2ecf20Sopenharmony_ci if (cfg_params[i].param == param) { 6228c2ecf20Sopenharmony_ci pname = cfg_params[i].property; 6238c2ecf20Sopenharmony_ci break; 6248c2ecf20Sopenharmony_ci } 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci seq_printf(s, "%s=%d", strip_prefix(pname), arg); 6288c2ecf20Sopenharmony_ci} 6298c2ecf20Sopenharmony_ci#endif 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_cistatic const struct pinconf_ops tegra_pinconf_ops = { 6328c2ecf20Sopenharmony_ci .pin_config_get = tegra_pinconf_get, 6338c2ecf20Sopenharmony_ci .pin_config_set = tegra_pinconf_set, 6348c2ecf20Sopenharmony_ci .pin_config_group_get = tegra_pinconf_group_get, 6358c2ecf20Sopenharmony_ci .pin_config_group_set = tegra_pinconf_group_set, 6368c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 6378c2ecf20Sopenharmony_ci .pin_config_dbg_show = tegra_pinconf_dbg_show, 6388c2ecf20Sopenharmony_ci .pin_config_group_dbg_show = tegra_pinconf_group_dbg_show, 6398c2ecf20Sopenharmony_ci .pin_config_config_dbg_show = tegra_pinconf_config_dbg_show, 6408c2ecf20Sopenharmony_ci#endif 6418c2ecf20Sopenharmony_ci}; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_cistatic struct pinctrl_gpio_range tegra_pinctrl_gpio_range = { 6448c2ecf20Sopenharmony_ci .name = "Tegra GPIOs", 6458c2ecf20Sopenharmony_ci .id = 0, 6468c2ecf20Sopenharmony_ci .base = 0, 6478c2ecf20Sopenharmony_ci}; 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_cistatic struct pinctrl_desc tegra_pinctrl_desc = { 6508c2ecf20Sopenharmony_ci .pctlops = &tegra_pinctrl_ops, 6518c2ecf20Sopenharmony_ci .pmxops = &tegra_pinmux_ops, 6528c2ecf20Sopenharmony_ci .confops = &tegra_pinconf_ops, 6538c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 6548c2ecf20Sopenharmony_ci}; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_cistatic void tegra_pinctrl_clear_parked_bits(struct tegra_pmx *pmx) 6578c2ecf20Sopenharmony_ci{ 6588c2ecf20Sopenharmony_ci int i = 0; 6598c2ecf20Sopenharmony_ci const struct tegra_pingroup *g; 6608c2ecf20Sopenharmony_ci u32 val; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci for (i = 0; i < pmx->soc->ngroups; ++i) { 6638c2ecf20Sopenharmony_ci g = &pmx->soc->groups[i]; 6648c2ecf20Sopenharmony_ci if (g->parked_bitmask > 0) { 6658c2ecf20Sopenharmony_ci unsigned int bank, reg; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci if (g->mux_reg != -1) { 6688c2ecf20Sopenharmony_ci bank = g->mux_bank; 6698c2ecf20Sopenharmony_ci reg = g->mux_reg; 6708c2ecf20Sopenharmony_ci } else { 6718c2ecf20Sopenharmony_ci bank = g->drv_bank; 6728c2ecf20Sopenharmony_ci reg = g->drv_reg; 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci val = pmx_readl(pmx, bank, reg); 6768c2ecf20Sopenharmony_ci val &= ~g->parked_bitmask; 6778c2ecf20Sopenharmony_ci pmx_writel(pmx, val, bank, reg); 6788c2ecf20Sopenharmony_ci } 6798c2ecf20Sopenharmony_ci } 6808c2ecf20Sopenharmony_ci} 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_cistatic size_t tegra_pinctrl_get_bank_size(struct device *dev, 6838c2ecf20Sopenharmony_ci unsigned int bank_id) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 6868c2ecf20Sopenharmony_ci struct resource *res; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, bank_id); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci return resource_size(res) / 4; 6918c2ecf20Sopenharmony_ci} 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_cistatic int tegra_pinctrl_suspend(struct device *dev) 6948c2ecf20Sopenharmony_ci{ 6958c2ecf20Sopenharmony_ci struct tegra_pmx *pmx = dev_get_drvdata(dev); 6968c2ecf20Sopenharmony_ci u32 *backup_regs = pmx->backup_regs; 6978c2ecf20Sopenharmony_ci u32 __iomem *regs; 6988c2ecf20Sopenharmony_ci size_t bank_size; 6998c2ecf20Sopenharmony_ci unsigned int i, k; 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci for (i = 0; i < pmx->nbanks; i++) { 7028c2ecf20Sopenharmony_ci bank_size = tegra_pinctrl_get_bank_size(dev, i); 7038c2ecf20Sopenharmony_ci regs = pmx->regs[i]; 7048c2ecf20Sopenharmony_ci for (k = 0; k < bank_size; k++) 7058c2ecf20Sopenharmony_ci *backup_regs++ = readl_relaxed(regs++); 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci return pinctrl_force_sleep(pmx->pctl); 7098c2ecf20Sopenharmony_ci} 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_cistatic int tegra_pinctrl_resume(struct device *dev) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci struct tegra_pmx *pmx = dev_get_drvdata(dev); 7148c2ecf20Sopenharmony_ci u32 *backup_regs = pmx->backup_regs; 7158c2ecf20Sopenharmony_ci u32 __iomem *regs; 7168c2ecf20Sopenharmony_ci size_t bank_size; 7178c2ecf20Sopenharmony_ci unsigned int i, k; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci for (i = 0; i < pmx->nbanks; i++) { 7208c2ecf20Sopenharmony_ci bank_size = tegra_pinctrl_get_bank_size(dev, i); 7218c2ecf20Sopenharmony_ci regs = pmx->regs[i]; 7228c2ecf20Sopenharmony_ci for (k = 0; k < bank_size; k++) 7238c2ecf20Sopenharmony_ci writel_relaxed(*backup_regs++, regs++); 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci /* flush all the prior writes */ 7278c2ecf20Sopenharmony_ci readl_relaxed(pmx->regs[0]); 7288c2ecf20Sopenharmony_ci /* wait for pinctrl register read to complete */ 7298c2ecf20Sopenharmony_ci rmb(); 7308c2ecf20Sopenharmony_ci return 0; 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_ciconst struct dev_pm_ops tegra_pinctrl_pm = { 7348c2ecf20Sopenharmony_ci .suspend_noirq = &tegra_pinctrl_suspend, 7358c2ecf20Sopenharmony_ci .resume_noirq = &tegra_pinctrl_resume 7368c2ecf20Sopenharmony_ci}; 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_cistatic bool tegra_pinctrl_gpio_node_has_range(struct tegra_pmx *pmx) 7398c2ecf20Sopenharmony_ci{ 7408c2ecf20Sopenharmony_ci struct device_node *np; 7418c2ecf20Sopenharmony_ci bool has_prop = false; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci np = of_find_compatible_node(NULL, NULL, pmx->soc->gpio_compatible); 7448c2ecf20Sopenharmony_ci if (!np) 7458c2ecf20Sopenharmony_ci return has_prop; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci has_prop = of_find_property(np, "gpio-ranges", NULL); 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci of_node_put(np); 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci return has_prop; 7528c2ecf20Sopenharmony_ci} 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ciint tegra_pinctrl_probe(struct platform_device *pdev, 7558c2ecf20Sopenharmony_ci const struct tegra_pinctrl_soc_data *soc_data) 7568c2ecf20Sopenharmony_ci{ 7578c2ecf20Sopenharmony_ci struct tegra_pmx *pmx; 7588c2ecf20Sopenharmony_ci struct resource *res; 7598c2ecf20Sopenharmony_ci int i; 7608c2ecf20Sopenharmony_ci const char **group_pins; 7618c2ecf20Sopenharmony_ci int fn, gn, gfn; 7628c2ecf20Sopenharmony_ci unsigned long backup_regs_size = 0; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci pmx = devm_kzalloc(&pdev->dev, sizeof(*pmx), GFP_KERNEL); 7658c2ecf20Sopenharmony_ci if (!pmx) 7668c2ecf20Sopenharmony_ci return -ENOMEM; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci pmx->dev = &pdev->dev; 7698c2ecf20Sopenharmony_ci pmx->soc = soc_data; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci /* 7728c2ecf20Sopenharmony_ci * Each mux group will appear in 4 functions' list of groups. 7738c2ecf20Sopenharmony_ci * This over-allocates slightly, since not all groups are mux groups. 7748c2ecf20Sopenharmony_ci */ 7758c2ecf20Sopenharmony_ci pmx->group_pins = devm_kcalloc(&pdev->dev, 7768c2ecf20Sopenharmony_ci soc_data->ngroups * 4, sizeof(*pmx->group_pins), 7778c2ecf20Sopenharmony_ci GFP_KERNEL); 7788c2ecf20Sopenharmony_ci if (!pmx->group_pins) 7798c2ecf20Sopenharmony_ci return -ENOMEM; 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci group_pins = pmx->group_pins; 7828c2ecf20Sopenharmony_ci for (fn = 0; fn < soc_data->nfunctions; fn++) { 7838c2ecf20Sopenharmony_ci struct tegra_function *func = &soc_data->functions[fn]; 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci func->groups = group_pins; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci for (gn = 0; gn < soc_data->ngroups; gn++) { 7888c2ecf20Sopenharmony_ci const struct tegra_pingroup *g = &soc_data->groups[gn]; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci if (g->mux_reg == -1) 7918c2ecf20Sopenharmony_ci continue; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci for (gfn = 0; gfn < 4; gfn++) 7948c2ecf20Sopenharmony_ci if (g->funcs[gfn] == fn) 7958c2ecf20Sopenharmony_ci break; 7968c2ecf20Sopenharmony_ci if (gfn == 4) 7978c2ecf20Sopenharmony_ci continue; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_ci BUG_ON(group_pins - pmx->group_pins >= 8008c2ecf20Sopenharmony_ci soc_data->ngroups * 4); 8018c2ecf20Sopenharmony_ci *group_pins++ = g->name; 8028c2ecf20Sopenharmony_ci func->ngroups++; 8038c2ecf20Sopenharmony_ci } 8048c2ecf20Sopenharmony_ci } 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_ci tegra_pinctrl_gpio_range.npins = pmx->soc->ngpios; 8078c2ecf20Sopenharmony_ci tegra_pinctrl_desc.name = dev_name(&pdev->dev); 8088c2ecf20Sopenharmony_ci tegra_pinctrl_desc.pins = pmx->soc->pins; 8098c2ecf20Sopenharmony_ci tegra_pinctrl_desc.npins = pmx->soc->npins; 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci for (i = 0; ; i++) { 8128c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, i); 8138c2ecf20Sopenharmony_ci if (!res) 8148c2ecf20Sopenharmony_ci break; 8158c2ecf20Sopenharmony_ci backup_regs_size += resource_size(res); 8168c2ecf20Sopenharmony_ci } 8178c2ecf20Sopenharmony_ci pmx->nbanks = i; 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci pmx->regs = devm_kcalloc(&pdev->dev, pmx->nbanks, sizeof(*pmx->regs), 8208c2ecf20Sopenharmony_ci GFP_KERNEL); 8218c2ecf20Sopenharmony_ci if (!pmx->regs) 8228c2ecf20Sopenharmony_ci return -ENOMEM; 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci pmx->backup_regs = devm_kzalloc(&pdev->dev, backup_regs_size, 8258c2ecf20Sopenharmony_ci GFP_KERNEL); 8268c2ecf20Sopenharmony_ci if (!pmx->backup_regs) 8278c2ecf20Sopenharmony_ci return -ENOMEM; 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci for (i = 0; i < pmx->nbanks; i++) { 8308c2ecf20Sopenharmony_ci pmx->regs[i] = devm_platform_ioremap_resource(pdev, i); 8318c2ecf20Sopenharmony_ci if (IS_ERR(pmx->regs[i])) 8328c2ecf20Sopenharmony_ci return PTR_ERR(pmx->regs[i]); 8338c2ecf20Sopenharmony_ci } 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci pmx->pctl = devm_pinctrl_register(&pdev->dev, &tegra_pinctrl_desc, pmx); 8368c2ecf20Sopenharmony_ci if (IS_ERR(pmx->pctl)) { 8378c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Couldn't register pinctrl driver\n"); 8388c2ecf20Sopenharmony_ci return PTR_ERR(pmx->pctl); 8398c2ecf20Sopenharmony_ci } 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci tegra_pinctrl_clear_parked_bits(pmx); 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci if (pmx->soc->ngpios > 0 && !tegra_pinctrl_gpio_node_has_range(pmx)) 8448c2ecf20Sopenharmony_ci pinctrl_add_gpio_range(pmx->pctl, &tegra_pinctrl_gpio_range); 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, pmx); 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "Probed Tegra pinctrl driver\n"); 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci return 0; 8518c2ecf20Sopenharmony_ci} 852