summaryrefslogtreecommitdiff
path: root/drivers/pinctrl/tegra/pinctrl-tegra.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pinctrl/tegra/pinctrl-tegra.c')
-rw-r--r--drivers/pinctrl/tegra/pinctrl-tegra.c350
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);