diff options
Diffstat (limited to 'drivers/pinctrl/pinctrl-tegra.c')
-rw-r--r-- | drivers/pinctrl/pinctrl-tegra.c | 236 |
1 files changed, 136 insertions, 100 deletions
diff --git a/drivers/pinctrl/pinctrl-tegra.c b/drivers/pinctrl/pinctrl-tegra.c index 2c98fba01ca..b6934867d8d 100644 --- a/drivers/pinctrl/pinctrl-tegra.c +++ b/drivers/pinctrl/pinctrl-tegra.c @@ -1,7 +1,7 @@ /* * Driver for the NVIDIA Tegra pinmux * - * Copyright (c) 2011, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2011-2012, NVIDIA CORPORATION. All rights reserved. * * Derived from code: * Copyright (C) 2010 Google, Inc. @@ -22,7 +22,8 @@ #include <linux/init.h> #include <linux/io.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/of.h> +#include <linux/platform_device.h> #include <linux/pinctrl/machine.h> #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinmux.h> @@ -31,10 +32,9 @@ #include <mach/pinconf-tegra.h> +#include "core.h" #include "pinctrl-tegra.h" -#define DRIVER_NAME "tegra-pinmux-disabled" - struct tegra_pmx { struct device *dev; struct pinctrl_dev *pctl; @@ -83,15 +83,18 @@ static int tegra_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, return 0; } +#ifdef CONFIG_DEBUG_FS static void tegra_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned offset) { - seq_printf(s, " " DRIVER_NAME); + seq_printf(s, " %s", dev_name(pctldev->dev)); } +#endif -static int reserve_map(struct pinctrl_map **map, unsigned *reserved_maps, - unsigned *num_maps, unsigned reserve) +static int reserve_map(struct device *dev, struct pinctrl_map **map, + unsigned *reserved_maps, unsigned *num_maps, + unsigned reserve) { unsigned old_num = *reserved_maps; unsigned new_num = *num_maps + reserve; @@ -101,8 +104,10 @@ static int reserve_map(struct pinctrl_map **map, unsigned *reserved_maps, return 0; new_map = krealloc(*map, sizeof(*new_map) * new_num, GFP_KERNEL); - if (!new_map) + if (!new_map) { + dev_err(dev, "krealloc(map) failed\n"); return -ENOMEM; + } memset(new_map + old_num, 0, (new_num - old_num) * sizeof(*new_map)); @@ -116,7 +121,7 @@ static int add_map_mux(struct pinctrl_map **map, unsigned *reserved_maps, unsigned *num_maps, const char *group, const char *function) { - if (*num_maps == *reserved_maps) + if (WARN_ON(*num_maps == *reserved_maps)) return -ENOSPC; (*map)[*num_maps].type = PIN_MAP_TYPE_MUX_GROUP; @@ -127,19 +132,22 @@ static int add_map_mux(struct pinctrl_map **map, unsigned *reserved_maps, return 0; } -static int add_map_configs(struct pinctrl_map **map, unsigned *reserved_maps, - unsigned *num_maps, const char *group, - unsigned long *configs, unsigned num_configs) +static int add_map_configs(struct device *dev, struct pinctrl_map **map, + unsigned *reserved_maps, unsigned *num_maps, + const char *group, unsigned long *configs, + unsigned num_configs) { unsigned long *dup_configs; - if (*num_maps == *reserved_maps) + if (WARN_ON(*num_maps == *reserved_maps)) return -ENOSPC; dup_configs = kmemdup(configs, num_configs * sizeof(*dup_configs), GFP_KERNEL); - if (!dup_configs) + if (!dup_configs) { + dev_err(dev, "kmemdup(configs) failed\n"); return -ENOMEM; + } (*map)[*num_maps].type = PIN_MAP_TYPE_CONFIGS_GROUP; (*map)[*num_maps].data.configs.group_or_pin = group; @@ -150,8 +158,8 @@ static int add_map_configs(struct pinctrl_map **map, unsigned *reserved_maps, return 0; } -static int add_config(unsigned long **configs, unsigned *num_configs, - unsigned long config) +static int add_config(struct device *dev, unsigned long **configs, + unsigned *num_configs, unsigned long config) { unsigned old_num = *num_configs; unsigned new_num = old_num + 1; @@ -159,8 +167,10 @@ static int add_config(unsigned long **configs, unsigned *num_configs, new_configs = krealloc(*configs, sizeof(*new_configs) * new_num, GFP_KERNEL); - if (!new_configs) + if (!new_configs) { + dev_err(dev, "krealloc(configs) failed\n"); return -ENOMEM; + } new_configs[old_num] = config; @@ -201,7 +211,8 @@ static const struct cfg_param { {"nvidia,slew-rate-rising", TEGRA_PINCONF_PARAM_SLEW_RATE_RISING}, }; -int tegra_pinctrl_dt_subnode_to_map(struct device_node *np, +int tegra_pinctrl_dt_subnode_to_map(struct device *dev, + struct device_node *np, struct pinctrl_map **map, unsigned *reserved_maps, unsigned *num_maps) @@ -217,16 +228,25 @@ int tegra_pinctrl_dt_subnode_to_map(struct device_node *np, const char *group; ret = of_property_read_string(np, "nvidia,function", &function); - if (ret < 0) + if (ret < 0) { + /* EINVAL=missing, which is fine since it's optional */ + if (ret != -EINVAL) + dev_err(dev, + "could not parse property nvidia,function\n"); function = NULL; + } for (i = 0; i < ARRAY_SIZE(cfg_params); i++) { ret = of_property_read_u32(np, cfg_params[i].property, &val); if (!ret) { config = TEGRA_PINCONF_PACK(cfg_params[i].param, val); - ret = add_config(&configs, &num_configs, config); + ret = add_config(dev, &configs, &num_configs, config); if (ret < 0) 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); } } @@ -236,11 +256,13 @@ int tegra_pinctrl_dt_subnode_to_map(struct device_node *np, if (num_configs) reserve++; ret = of_property_count_strings(np, "nvidia,pins"); - if (ret < 0) + if (ret < 0) { + dev_err(dev, "could not parse property nvidia,pins\n"); goto exit; + } reserve *= ret; - ret = reserve_map(map, reserved_maps, num_maps, reserve); + ret = reserve_map(dev, map, reserved_maps, num_maps, reserve); if (ret < 0) goto exit; @@ -253,8 +275,9 @@ int tegra_pinctrl_dt_subnode_to_map(struct device_node *np, } if (num_configs) { - ret = add_map_configs(map, reserved_maps, num_maps, - group, configs, num_configs); + ret = add_map_configs(dev, map, reserved_maps, + num_maps, group, configs, + num_configs); if (ret < 0) goto exit; } @@ -280,8 +303,8 @@ int tegra_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev, *num_maps = 0; for_each_child_of_node(np_config, np) { - ret = tegra_pinctrl_dt_subnode_to_map(np, map, &reserved_maps, - num_maps); + ret = tegra_pinctrl_dt_subnode_to_map(pctldev->dev, np, map, + &reserved_maps, num_maps); if (ret < 0) { tegra_pinctrl_dt_free_map(pctldev, *map, *num_maps); return ret; @@ -295,7 +318,9 @@ static struct pinctrl_ops tegra_pinctrl_ops = { .get_groups_count = tegra_pinctrl_get_groups_count, .get_group_name = tegra_pinctrl_get_group_name, .get_group_pins = tegra_pinctrl_get_group_pins, +#ifdef CONFIG_DEBUG_FS .pin_dbg_show = tegra_pinctrl_pin_dbg_show, +#endif .dt_node_to_map = tegra_pinctrl_dt_node_to_map, .dt_free_map = tegra_pinctrl_dt_free_map, }; @@ -338,14 +363,14 @@ static int tegra_pinctrl_enable(struct pinctrl_dev *pctldev, unsigned function, g = &pmx->soc->groups[group]; - if (g->mux_reg < 0) + if (WARN_ON(g->mux_reg < 0)) return -EINVAL; for (i = 0; i < ARRAY_SIZE(g->funcs); i++) { if (g->funcs[i] == function) break; } - if (i == ARRAY_SIZE(g->funcs)) + if (WARN_ON(i == ARRAY_SIZE(g->funcs))) return -EINVAL; val = pmx_readl(pmx, g->mux_bank, g->mux_reg); @@ -365,7 +390,7 @@ static void tegra_pinctrl_disable(struct pinctrl_dev *pctldev, g = &pmx->soc->groups[group]; - if (g->mux_reg < 0) + if (WARN_ON(g->mux_reg < 0)) return; val = pmx_readl(pmx, g->mux_bank, g->mux_reg); @@ -385,6 +410,7 @@ static struct pinmux_ops tegra_pinmux_ops = { 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) { switch (param) { @@ -472,9 +498,10 @@ static int tegra_pinconf_reg(struct tegra_pmx *pmx, } if (*reg < 0) { - dev_err(pmx->dev, - "Config param %04x not supported on group %s\n", - param, g->name); + if (report_err) + dev_err(pmx->dev, + "Config param %04x not supported on group %s\n", + param, g->name); return -ENOTSUPP; } @@ -484,12 +511,14 @@ static int tegra_pinconf_reg(struct tegra_pmx *pmx, static int tegra_pinconf_get(struct pinctrl_dev *pctldev, unsigned pin, unsigned long *config) { + dev_err(pctldev->dev, "pin_config_get op not supported\n"); return -ENOTSUPP; } static int tegra_pinconf_set(struct pinctrl_dev *pctldev, unsigned pin, unsigned long config) { + dev_err(pctldev->dev, "pin_config_set op not supported\n"); return -ENOTSUPP; } @@ -507,7 +536,8 @@ static int tegra_pinconf_group_get(struct pinctrl_dev *pctldev, g = &pmx->soc->groups[group]; - ret = tegra_pinconf_reg(pmx, g, param, &bank, ®, &bit, &width); + ret = tegra_pinconf_reg(pmx, g, param, true, &bank, ®, &bit, + &width); if (ret < 0) return ret; @@ -534,7 +564,8 @@ static int tegra_pinconf_group_set(struct pinctrl_dev *pctldev, g = &pmx->soc->groups[group]; - ret = tegra_pinconf_reg(pmx, g, param, &bank, ®, &bit, &width); + ret = tegra_pinconf_reg(pmx, g, param, true, &bank, ®, &bit, + &width); if (ret < 0) return ret; @@ -542,8 +573,10 @@ static int tegra_pinconf_group_set(struct pinctrl_dev *pctldev, /* LOCK can't be cleared */ if (param == TEGRA_PINCONF_PARAM_LOCK) { - if ((val & BIT(bit)) && !arg) + if ((val & BIT(bit)) && !arg) { + dev_err(pctldev->dev, "LOCK bit cannot be cleared\n"); return -EINVAL; + } } /* Special-case Boolean values; allow any non-zero as true */ @@ -552,8 +585,12 @@ static int tegra_pinconf_group_set(struct pinctrl_dev *pctldev, /* Range-check user-supplied value */ mask = (1 << width) - 1; - if (arg & ~mask) + if (arg & ~mask) { + dev_err(pctldev->dev, + "config %lx: %x too big for %d bit register\n", + config, arg, width); return -EINVAL; + } /* Update register */ val &= ~(mask << bit); @@ -563,23 +600,78 @@ static int tegra_pinconf_group_set(struct pinctrl_dev *pctldev, return 0; } +#ifdef CONFIG_DEBUG_FS static void tegra_pinconf_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned offset) { } +static const char *strip_prefix(const char *s) +{ + const char *comma = strchr(s, ','); + if (!comma) + return s; + + return comma + 1; +} + static void tegra_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, - struct seq_file *s, unsigned selector) + struct seq_file *s, unsigned group) +{ + struct tegra_pmx *pmx = pinctrl_dev_get_drvdata(pctldev); + const struct tegra_pingroup *g; + int i, ret; + s8 bank, bit, width; + s16 reg; + u32 val; + + g = &pmx->soc->groups[group]; + + for (i = 0; i < ARRAY_SIZE(cfg_params); i++) { + ret = tegra_pinconf_reg(pmx, g, cfg_params[i].param, false, + &bank, ®, &bit, &width); + if (ret < 0) + continue; + + val = pmx_readl(pmx, bank, reg); + val >>= bit; + val &= (1 << width) - 1; + + seq_printf(s, "\n\t%s=%u", + strip_prefix(cfg_params[i].property), val); + } +} + +static void tegra_pinconf_config_dbg_show(struct pinctrl_dev *pctldev, + struct seq_file *s, + unsigned long config) { + enum tegra_pinconf_param param = TEGRA_PINCONF_UNPACK_PARAM(config); + u16 arg = TEGRA_PINCONF_UNPACK_ARG(config); + const char *pname = "unknown"; + int i; + + for (i = 0; i < ARRAY_SIZE(cfg_params); i++) { + if (cfg_params[i].param == param) { + pname = cfg_params[i].property; + break; + } + } + + seq_printf(s, "%s=%d", strip_prefix(pname), arg); } +#endif struct pinconf_ops tegra_pinconf_ops = { .pin_config_get = tegra_pinconf_get, .pin_config_set = tegra_pinconf_set, .pin_config_group_get = tegra_pinconf_group_get, .pin_config_group_set = tegra_pinconf_group_set, +#ifdef CONFIG_DEBUG_FS .pin_config_dbg_show = tegra_pinconf_dbg_show, .pin_config_group_dbg_show = tegra_pinconf_group_dbg_show, + .pin_config_config_dbg_show = tegra_pinconf_config_dbg_show, +#endif }; static struct pinctrl_gpio_range tegra_pinctrl_gpio_range = { @@ -589,60 +681,29 @@ static struct pinctrl_gpio_range tegra_pinctrl_gpio_range = { }; static struct pinctrl_desc tegra_pinctrl_desc = { - .name = DRIVER_NAME, .pctlops = &tegra_pinctrl_ops, .pmxops = &tegra_pinmux_ops, .confops = &tegra_pinconf_ops, .owner = THIS_MODULE, }; -static struct of_device_id tegra_pinctrl_of_match[] __devinitdata = { -#ifdef CONFIG_PINCTRL_TEGRA20 - { - .compatible = "nvidia,tegra20-pinmux-disabled", - .data = tegra20_pinctrl_init, - }, -#endif -#ifdef CONFIG_PINCTRL_TEGRA30 - { - .compatible = "nvidia,tegra30-pinmux-disabled", - .data = tegra30_pinctrl_init, - }, -#endif - {}, -}; - -static int __devinit tegra_pinctrl_probe(struct platform_device *pdev) +int __devinit tegra_pinctrl_probe(struct platform_device *pdev, + const struct tegra_pinctrl_soc_data *soc_data) { - const struct of_device_id *match; - tegra_pinctrl_soc_initf initf = NULL; struct tegra_pmx *pmx; struct resource *res; int i; - match = of_match_device(tegra_pinctrl_of_match, &pdev->dev); - if (match) - initf = (tegra_pinctrl_soc_initf)match->data; -#ifdef CONFIG_PINCTRL_TEGRA20 - if (!initf) - initf = tegra20_pinctrl_init; -#endif - if (!initf) { - dev_err(&pdev->dev, - "Could not determine SoC-specific init func\n"); - return -EINVAL; - } - pmx = devm_kzalloc(&pdev->dev, sizeof(*pmx), GFP_KERNEL); if (!pmx) { dev_err(&pdev->dev, "Can't alloc tegra_pmx\n"); return -ENOMEM; } pmx->dev = &pdev->dev; - - (*initf)(&pmx->soc); + pmx->soc = soc_data; 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; @@ -697,8 +758,9 @@ static int __devinit tegra_pinctrl_probe(struct platform_device *pdev) return 0; } +EXPORT_SYMBOL_GPL(tegra_pinctrl_probe); -static int __devexit tegra_pinctrl_remove(struct platform_device *pdev) +int __devexit tegra_pinctrl_remove(struct platform_device *pdev) { struct tegra_pmx *pmx = platform_get_drvdata(pdev); @@ -707,30 +769,4 @@ static int __devexit tegra_pinctrl_remove(struct platform_device *pdev) return 0; } - -static struct platform_driver tegra_pinctrl_driver = { - .driver = { - .name = DRIVER_NAME, - .owner = THIS_MODULE, - .of_match_table = tegra_pinctrl_of_match, - }, - .probe = tegra_pinctrl_probe, - .remove = __devexit_p(tegra_pinctrl_remove), -}; - -static int __init tegra_pinctrl_init(void) -{ - return platform_driver_register(&tegra_pinctrl_driver); -} -arch_initcall(tegra_pinctrl_init); - -static void __exit tegra_pinctrl_exit(void) -{ - platform_driver_unregister(&tegra_pinctrl_driver); -} -module_exit(tegra_pinctrl_exit); - -MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); -MODULE_DESCRIPTION("NVIDIA Tegra pinctrl driver"); -MODULE_LICENSE("GPL v2"); -MODULE_DEVICE_TABLE(of, tegra_pinctrl_of_match); +EXPORT_SYMBOL_GPL(tegra_pinctrl_remove); |