diff options
Diffstat (limited to 'drivers/pinctrl/tegra/pinctrl-tegra.c')
| -rw-r--r-- | drivers/pinctrl/tegra/pinctrl-tegra.c | 350 |
1 files changed, 269 insertions, 81 deletions
diff --git a/drivers/pinctrl/tegra/pinctrl-tegra.c b/drivers/pinctrl/tegra/pinctrl-tegra.c index 51716819129d..11ecbd6a9b2a 100644 --- a/drivers/pinctrl/tegra/pinctrl-tegra.c +++ b/drivers/pinctrl/tegra/pinctrl-tegra.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Driver for the NVIDIA Tegra pinmux * @@ -7,15 +8,6 @@ * Copyright (C) 2010 Google, Inc. * Copyright (C) 2010 NVIDIA Corporation * Copyright (C) 2009-2011 ST-Ericsson AB - * - * 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/err.h> @@ -23,27 +15,18 @@ #include <linux/io.h> #include <linux/of.h> #include <linux/platform_device.h> +#include <linux/seq_file.h> +#include <linux/slab.h> + #include <linux/pinctrl/machine.h> +#include <linux/pinctrl/pinconf.h> #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinmux.h> -#include <linux/pinctrl/pinconf.h> -#include <linux/slab.h> #include "../core.h" #include "../pinctrl-utils.h" #include "pinctrl-tegra.h" -struct tegra_pmx { - struct device *dev; - struct pinctrl_dev *pctl; - - const struct tegra_pinctrl_soc_data *soc; - const char **group_pins; - - int nbanks; - void __iomem **regs; -}; - static inline u32 pmx_readl(struct tegra_pmx *pmx, u32 bank, u32 reg) { return readl(pmx->regs[bank] + reg); @@ -51,7 +34,9 @@ static inline u32 pmx_readl(struct tegra_pmx *pmx, u32 bank, u32 reg) static inline void pmx_writel(struct tegra_pmx *pmx, u32 val, u32 bank, u32 reg) { - writel(val, pmx->regs[bank] + reg); + writel_relaxed(val, pmx->regs[bank] + reg); + /* make sure pinmux register write completed */ + pmx_readl(pmx, bank, reg); } static int tegra_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) @@ -111,6 +96,7 @@ static const struct cfg_param { {"nvidia,slew-rate-falling", TEGRA_PINCONF_PARAM_SLEW_RATE_FALLING}, {"nvidia,slew-rate-rising", TEGRA_PINCONF_PARAM_SLEW_RATE_RISING}, {"nvidia,drive-type", TEGRA_PINCONF_PARAM_DRIVE_TYPE}, + {"nvidia,gpio-mode", TEGRA_PINCONF_PARAM_GPIO_MODE}, }; static int tegra_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev, @@ -135,7 +121,7 @@ static int tegra_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev, /* EINVAL=missing, which is fine since it's optional */ if (ret != -EINVAL) dev_err(dev, - "could not parse property nvidia,function\n"); + "%pOF: could not parse property nvidia,function\n", np); function = NULL; } @@ -149,8 +135,8 @@ static int tegra_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev, goto exit; /* EINVAL=missing, which is fine since it's optional */ } else if (ret != -EINVAL) { - dev_err(dev, "could not parse property %s\n", - cfg_params[i].property); + dev_err(dev, "%pOF: could not parse property %s\n", + np, cfg_params[i].property); } } @@ -161,7 +147,7 @@ static int tegra_pinctrl_dt_subnode_to_map(struct pinctrl_dev *pctldev, reserve++; ret = of_property_count_strings(np, "nvidia,pins"); if (ret < 0) { - dev_err(dev, "could not parse property nvidia,pins\n"); + dev_err(dev, "%pOF: could not parse property nvidia,pins\n", np); goto exit; } reserve *= ret; @@ -203,20 +189,18 @@ static int tegra_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, unsigned *num_maps) { unsigned reserved_maps; - struct device_node *np; int ret; reserved_maps = 0; *map = NULL; *num_maps = 0; - for_each_child_of_node(np_config, np) { + for_each_child_of_node_scoped(np_config, np) { ret = tegra_pinctrl_dt_subnode_to_map(pctldev, np, map, &reserved_maps, num_maps); if (ret < 0) { pinctrl_utils_free_map(pctldev, *map, *num_maps); - of_node_put(np); return ret; } } @@ -247,7 +231,7 @@ static const char *tegra_pinctrl_get_func_name(struct pinctrl_dev *pctldev, { struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); - return pmx->soc->functions[function].name; + return pmx->functions[function].name; } static int tegra_pinctrl_get_func_groups(struct pinctrl_dev *pctldev, @@ -257,8 +241,8 @@ static int tegra_pinctrl_get_func_groups(struct pinctrl_dev *pctldev, { struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); - *groups = pmx->soc->functions[function].groups; - *num_groups = pmx->soc->functions[function].ngroups; + *groups = pmx->functions[function].groups; + *num_groups = pmx->functions[function].ngroups; return 0; } @@ -287,23 +271,137 @@ static int tegra_pinctrl_set_mux(struct pinctrl_dev *pctldev, val = pmx_readl(pmx, g->mux_bank, g->mux_reg); val &= ~(0x3 << g->mux_bit); val |= i << g->mux_bit; + /* Set the SFIO/GPIO selection to SFIO when under pinmux control*/ + if (pmx->soc->sfsel_in_mux) + val |= (1 << g->sfsel_bit); pmx_writel(pmx, val, g->mux_bank, g->mux_reg); return 0; } +static int tegra_pinctrl_get_group_index(struct pinctrl_dev *pctldev, + unsigned int offset) +{ + struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + unsigned int group, num_pins, j; + const unsigned int *pins; + int ret; + + for (group = 0; group < pmx->soc->ngroups; ++group) { + ret = tegra_pinctrl_get_group_pins(pctldev, group, &pins, &num_pins); + if (ret < 0) + continue; + for (j = 0; j < num_pins; j++) { + if (offset == pins[j]) + return group; + } + } + + return -EINVAL; +} + +static const struct tegra_pingroup *tegra_pinctrl_get_group(struct pinctrl_dev *pctldev, + unsigned int offset, + int group_index) +{ + struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + + if (group_index < 0 || group_index >= pmx->soc->ngroups) + return NULL; + + return &pmx->soc->groups[group_index]; +} + +static struct tegra_pingroup_config *tegra_pinctrl_get_group_config(struct pinctrl_dev *pctldev, + unsigned int offset, + int group_index) +{ + struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + + if (group_index < 0) + return NULL; + + return &pmx->pingroup_configs[group_index]; +} + +static int tegra_pinctrl_gpio_request_enable(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int offset) +{ + struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + const struct tegra_pingroup *group; + struct tegra_pingroup_config *config; + int group_index; + u32 value; + + if (!pmx->soc->sfsel_in_mux) + return 0; + + group_index = tegra_pinctrl_get_group_index(pctldev, offset); + group = tegra_pinctrl_get_group(pctldev, offset, group_index); + + if (!group) + return -EINVAL; + + if (group->mux_reg < 0 || group->sfsel_bit < 0) + return -EINVAL; + + config = tegra_pinctrl_get_group_config(pctldev, offset, group_index); + if (!config) + return -EINVAL; + value = pmx_readl(pmx, group->mux_bank, group->mux_reg); + config->is_sfsel = (value & BIT(group->sfsel_bit)) != 0; + value &= ~BIT(group->sfsel_bit); + pmx_writel(pmx, value, group->mux_bank, group->mux_reg); + + return 0; +} + +static void tegra_pinctrl_gpio_disable_free(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int offset) +{ + struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + const struct tegra_pingroup *group; + struct tegra_pingroup_config *config; + int group_index; + u32 value; + + if (!pmx->soc->sfsel_in_mux) + return; + + group_index = tegra_pinctrl_get_group_index(pctldev, offset); + group = tegra_pinctrl_get_group(pctldev, offset, group_index); + + if (!group) + return; + + if (group->mux_reg < 0 || group->sfsel_bit < 0) + return; + + config = tegra_pinctrl_get_group_config(pctldev, offset, group_index); + if (!config) + return; + value = pmx_readl(pmx, group->mux_bank, group->mux_reg); + if (config->is_sfsel) + value |= BIT(group->sfsel_bit); + pmx_writel(pmx, value, group->mux_bank, group->mux_reg); +} + static const struct pinmux_ops tegra_pinmux_ops = { .get_functions_count = tegra_pinctrl_get_funcs_count, .get_function_name = tegra_pinctrl_get_func_name, .get_function_groups = tegra_pinctrl_get_func_groups, .set_mux = tegra_pinctrl_set_mux, + .gpio_request_enable = tegra_pinctrl_gpio_request_enable, + .gpio_disable_free = tegra_pinctrl_gpio_disable_free, }; static int tegra_pinconf_reg(struct tegra_pmx *pmx, const struct tegra_pingroup *g, enum tegra_pinconf_param param, bool report_err, - s8 *bank, s16 *reg, s8 *bit, s8 *width) + s8 *bank, s32 *reg, s8 *bit, s8 *width) { switch (param) { case TEGRA_PINCONF_PARAM_PULL: @@ -411,6 +509,16 @@ static int tegra_pinconf_reg(struct tegra_pmx *pmx, *bit = g->drvtype_bit; *width = 2; break; + case TEGRA_PINCONF_PARAM_GPIO_MODE: + if (pmx->soc->sfsel_in_mux) { + *bank = g->mux_bank; + *reg = g->mux_reg; + *bit = g->sfsel_bit; + *width = 1; + } else { + *reg = -EINVAL; + } + break; default: dev_err(pmx->dev, "Invalid config param %04x\n", param); return -ENOTSUPP; @@ -462,7 +570,7 @@ static int tegra_pinconf_group_get(struct pinctrl_dev *pctldev, const struct tegra_pingroup *g; int ret; s8 bank, bit, width; - s16 reg; + s32 reg; u32 val, mask; g = &pmx->soc->groups[group]; @@ -491,7 +599,7 @@ static int tegra_pinconf_group_set(struct pinctrl_dev *pctldev, const struct tegra_pingroup *g; int ret, i; s8 bank, bit, width; - s16 reg; + s32 reg; u32 val, mask; g = &pmx->soc->groups[group]; @@ -559,7 +667,7 @@ static void tegra_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, const struct tegra_pingroup *g; int i, ret; s8 bank, bit, width; - s16 reg; + s32 reg; u32 val; g = &pmx->soc->groups[group]; @@ -577,6 +685,14 @@ static void tegra_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, seq_printf(s, "\n\t%s=%u", strip_prefix(cfg_params[i].property), val); } + + if (g->mux_reg >= 0) { + /* read pinmux function and dump to seq_file */ + val = pmx_readl(pmx, g->mux_bank, g->mux_reg); + val = g->funcs[(val >> g->mux_bit) & 0x3]; + + seq_printf(s, "\n\tfunction=%s", pmx->functions[val].name); + } } static void tegra_pinconf_config_dbg_show(struct pinctrl_dev *pctldev, @@ -611,19 +727,6 @@ static const struct pinconf_ops tegra_pinconf_ops = { #endif }; -static struct pinctrl_gpio_range tegra_pinctrl_gpio_range = { - .name = "Tegra GPIOs", - .id = 0, - .base = 0, -}; - -static struct pinctrl_desc tegra_pinctrl_desc = { - .pctlops = &tegra_pinctrl_ops, - .pmxops = &tegra_pinmux_ops, - .confops = &tegra_pinconf_ops, - .owner = THIS_MODULE, -}; - static void tegra_pinctrl_clear_parked_bits(struct tegra_pmx *pmx) { int i = 0; @@ -632,20 +735,83 @@ static void tegra_pinctrl_clear_parked_bits(struct tegra_pmx *pmx) for (i = 0; i < pmx->soc->ngroups; ++i) { g = &pmx->soc->groups[i]; - if (g->parked_bit >= 0) { - val = pmx_readl(pmx, g->mux_bank, g->mux_reg); - val &= ~(1 << g->parked_bit); - pmx_writel(pmx, val, g->mux_bank, g->mux_reg); + if (g->parked_bitmask > 0) { + unsigned int bank, reg; + + if (g->mux_reg != -1) { + bank = g->mux_bank; + reg = g->mux_reg; + } else { + bank = g->drv_bank; + reg = g->drv_reg; + } + + val = pmx_readl(pmx, bank, reg); + val &= ~g->parked_bitmask; + pmx_writel(pmx, val, bank, reg); } } } -static bool gpio_node_has_range(void) +static size_t tegra_pinctrl_get_bank_size(struct device *dev, + unsigned int bank_id) +{ + struct platform_device *pdev = to_platform_device(dev); + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, bank_id); + + return resource_size(res) / 4; +} + +static int tegra_pinctrl_suspend(struct device *dev) +{ + struct tegra_pmx *pmx = dev_get_drvdata(dev); + u32 *backup_regs = pmx->backup_regs; + u32 __iomem *regs; + size_t bank_size; + unsigned int i, k; + + for (i = 0; i < pmx->nbanks; i++) { + bank_size = tegra_pinctrl_get_bank_size(dev, i); + regs = pmx->regs[i]; + for (k = 0; k < bank_size; k++) + *backup_regs++ = readl_relaxed(regs++); + } + + return pinctrl_force_sleep(pmx->pctl); +} + +static int tegra_pinctrl_resume(struct device *dev) +{ + struct tegra_pmx *pmx = dev_get_drvdata(dev); + u32 *backup_regs = pmx->backup_regs; + u32 __iomem *regs; + size_t bank_size; + unsigned int i, k; + + for (i = 0; i < pmx->nbanks; i++) { + bank_size = tegra_pinctrl_get_bank_size(dev, i); + regs = pmx->regs[i]; + for (k = 0; k < bank_size; k++) + writel_relaxed(*backup_regs++, regs++); + } + + /* flush all the prior writes */ + readl_relaxed(pmx->regs[0]); + /* wait for pinctrl register read to complete */ + rmb(); + return 0; +} + +DEFINE_NOIRQ_DEV_PM_OPS(tegra_pinctrl_pm, tegra_pinctrl_suspend, tegra_pinctrl_resume); + +static bool tegra_pinctrl_gpio_node_has_range(struct tegra_pmx *pmx) { struct device_node *np; bool has_prop = false; - np = of_find_compatible_node(NULL, NULL, "nvidia,tegra30-gpio"); + np = of_find_compatible_node(NULL, NULL, pmx->soc->gpio_compatible); if (!np) return has_prop; @@ -664,33 +830,45 @@ int tegra_pinctrl_probe(struct platform_device *pdev, int i; const char **group_pins; int fn, gn, gfn; + unsigned long backup_regs_size = 0; pmx = devm_kzalloc(&pdev->dev, sizeof(*pmx), GFP_KERNEL); - if (!pmx) { - dev_err(&pdev->dev, "Can't alloc tegra_pmx\n"); + if (!pmx) return -ENOMEM; - } + pmx->dev = &pdev->dev; pmx->soc = soc_data; + pmx->pingroup_configs = devm_kcalloc(&pdev->dev, + pmx->soc->ngroups, sizeof(*pmx->pingroup_configs), + GFP_KERNEL); + if (!pmx->pingroup_configs) + return -ENOMEM; + /* * Each mux group will appear in 4 functions' list of groups. * This over-allocates slightly, since not all groups are mux groups. */ - pmx->group_pins = devm_kzalloc(&pdev->dev, - soc_data->ngroups * 4 * sizeof(*pmx->group_pins), - GFP_KERNEL); + pmx->group_pins = devm_kcalloc(&pdev->dev, pmx->soc->ngroups * 4, + sizeof(*pmx->group_pins), GFP_KERNEL); if (!pmx->group_pins) return -ENOMEM; + pmx->functions = devm_kcalloc(&pdev->dev, pmx->soc->nfunctions, + sizeof(*pmx->functions), GFP_KERNEL); + if (!pmx->functions) + return -ENOMEM; + group_pins = pmx->group_pins; - for (fn = 0; fn < soc_data->nfunctions; fn++) { - struct tegra_function *func = &soc_data->functions[fn]; + for (fn = 0; fn < pmx->soc->nfunctions; fn++) { + struct tegra_function *func = &pmx->functions[fn]; + + func->name = pmx->soc->functions[fn]; func->groups = group_pins; - for (gn = 0; gn < soc_data->ngroups; gn++) { - const struct tegra_pingroup *g = &soc_data->groups[gn]; + for (gn = 0; gn < pmx->soc->ngroups; gn++) { + const struct tegra_pingroup *g = &pmx->soc->groups[gn]; if (g->mux_reg == -1) continue; @@ -702,39 +880,50 @@ int tegra_pinctrl_probe(struct platform_device *pdev, continue; BUG_ON(group_pins - pmx->group_pins >= - soc_data->ngroups * 4); + pmx->soc->ngroups * 4); *group_pins++ = g->name; func->ngroups++; } } - tegra_pinctrl_gpio_range.npins = pmx->soc->ngpios; - tegra_pinctrl_desc.name = dev_name(&pdev->dev); - tegra_pinctrl_desc.pins = pmx->soc->pins; - tegra_pinctrl_desc.npins = pmx->soc->npins; + pmx->gpio_range.name = "Tegra GPIOs"; + pmx->gpio_range.id = 0; + pmx->gpio_range.base = 0; + pmx->gpio_range.npins = pmx->soc->ngpios; + + pmx->desc.pctlops = &tegra_pinctrl_ops; + pmx->desc.pmxops = &tegra_pinmux_ops; + pmx->desc.confops = &tegra_pinconf_ops; + pmx->desc.owner = THIS_MODULE; + pmx->desc.name = dev_name(&pdev->dev); + pmx->desc.pins = pmx->soc->pins; + pmx->desc.npins = pmx->soc->npins; for (i = 0; ; i++) { res = platform_get_resource(pdev, IORESOURCE_MEM, i); if (!res) break; + backup_regs_size += resource_size(res); } pmx->nbanks = i; - pmx->regs = devm_kzalloc(&pdev->dev, pmx->nbanks * sizeof(*pmx->regs), + pmx->regs = devm_kcalloc(&pdev->dev, pmx->nbanks, sizeof(*pmx->regs), GFP_KERNEL); - if (!pmx->regs) { - dev_err(&pdev->dev, "Can't alloc regs pointer\n"); + if (!pmx->regs) + return -ENOMEM; + + pmx->backup_regs = devm_kzalloc(&pdev->dev, backup_regs_size, + GFP_KERNEL); + if (!pmx->backup_regs) return -ENOMEM; - } for (i = 0; i < pmx->nbanks; i++) { - res = platform_get_resource(pdev, IORESOURCE_MEM, i); - pmx->regs[i] = devm_ioremap_resource(&pdev->dev, res); + pmx->regs[i] = devm_platform_ioremap_resource(pdev, i); if (IS_ERR(pmx->regs[i])) return PTR_ERR(pmx->regs[i]); } - pmx->pctl = devm_pinctrl_register(&pdev->dev, &tegra_pinctrl_desc, pmx); + pmx->pctl = devm_pinctrl_register(&pdev->dev, &pmx->desc, pmx); if (IS_ERR(pmx->pctl)) { dev_err(&pdev->dev, "Couldn't register pinctrl driver\n"); return PTR_ERR(pmx->pctl); @@ -742,8 +931,8 @@ int tegra_pinctrl_probe(struct platform_device *pdev, tegra_pinctrl_clear_parked_bits(pmx); - if (!gpio_node_has_range()) - pinctrl_add_gpio_range(pmx->pctl, &tegra_pinctrl_gpio_range); + if (pmx->soc->ngpios > 0 && !tegra_pinctrl_gpio_node_has_range(pmx)) + pinctrl_add_gpio_range(pmx->pctl, &pmx->gpio_range); platform_set_drvdata(pdev, pmx); @@ -751,4 +940,3 @@ int tegra_pinctrl_probe(struct platform_device *pdev, return 0; } -EXPORT_SYMBOL_GPL(tegra_pinctrl_probe); |
