Implement pinctrl_ops dt_node_to_map() and dt_free_map(). These allow
complete specification of the desired pinmux configuration using device
tree.

Signed-off-by: Stephen Warren <[email protected]>
---
 drivers/pinctrl/pinctrl-tegra.c |  180 +++++++++++++++++++++++++++++++++++++++
 1 files changed, 180 insertions(+), 0 deletions(-)

diff --git a/drivers/pinctrl/pinctrl-tegra.c b/drivers/pinctrl/pinctrl-tegra.c
index 9b32968..19ac661 100644
--- a/drivers/pinctrl/pinctrl-tegra.c
+++ b/drivers/pinctrl/pinctrl-tegra.c
@@ -23,9 +23,12 @@
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
+#include <linux/of_iter_prop.h>
+#include <linux/pinctrl/machine.h>
 #include <linux/pinctrl/pinctrl.h>
 #include <linux/pinctrl/pinmux.h>
 #include <linux/pinctrl/pinconf.h>
+#include <linux/slab.h>
 
 #include <mach/pinconf-tegra.h>
 
@@ -98,11 +101,188 @@ static void tegra_pinctrl_pin_dbg_show(struct pinctrl_dev 
*pctldev,
        seq_printf(s, " " DRIVER_NAME);
 }
 
+static int add_map(struct pinctrl_map **map, unsigned *num_maps)
+{
+       unsigned old_num = *num_maps;
+       unsigned new_num = old_num + 1;
+       struct pinctrl_map *new_map;
+
+       new_map = krealloc(*map, sizeof(*new_map) * new_num, GFP_KERNEL);
+       if (!new_map)
+               return -ENOMEM;
+
+       memset(new_map + old_num, 0, sizeof(*new_map));
+
+       *map = new_map;
+       *num_maps = new_num;
+
+       return 0;
+}
+
+static int add_map_mux(struct pinctrl_map **map, unsigned *num_maps,
+                      const char *group, const char *function)
+{
+       unsigned i = *num_maps;
+       int ret;
+
+       ret = add_map(map, num_maps);
+       if (ret < 0)
+               return ret;
+
+       (*map)[i].type = PIN_MAP_TYPE_MUX_GROUP;
+       (*map)[i].data.mux.group = group;
+       (*map)[i].data.mux.function = function;
+
+       return 0;
+}
+
+static int add_map_configs(struct pinctrl_map **map, unsigned *num_maps,
+                          const char *group, unsigned long *configs,
+                          unsigned num_configs)
+{
+       unsigned i = *num_maps;
+       unsigned long *dup_configs;
+       int ret;
+
+       dup_configs = kmemdup(configs, num_configs * sizeof(*dup_configs),
+                             GFP_KERNEL);
+       if (!dup_configs)
+               return -ENOMEM;
+
+       ret = add_map(map, num_maps);
+       if (ret < 0)
+               return ret;
+
+       (*map)[i].type = PIN_MAP_TYPE_CONFIGS_GROUP;
+       (*map)[i].data.configs.group_or_pin = group;
+       (*map)[i].data.configs.configs = dup_configs;
+       (*map)[i].data.configs.num_configs = num_configs;
+
+       return 0;
+}
+
+static int add_config(unsigned long **configs, unsigned *num_configs,
+                     unsigned long config)
+{
+       unsigned old_num = *num_configs;
+       unsigned new_num = old_num + 1;
+       unsigned long *new_configs;
+
+       new_configs = krealloc(*configs, sizeof(*new_configs) * new_num,
+                              GFP_KERNEL);
+       if (!new_configs)
+               return -ENOMEM;
+
+       new_configs[old_num] = config;
+
+       *configs = new_configs;
+       *num_configs = new_num;
+
+       return 0;
+}
+
+void tegra_pinctrl_dt_free_map(struct pinctrl_dev *pctldev,
+                              struct pinctrl_map *map, unsigned num_maps)
+{
+       int i;
+
+       for (i = 0; i < num_maps; i++)
+               if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP)
+                       kfree(map[i].data.configs.configs);
+
+       kfree(map);
+}
+
+static const struct cfg_param {
+       const char *property;
+       enum tegra_pinconf_param param;
+} cfg_params[] = {
+       {"nvidia,pull",                 TEGRA_PINCONF_PARAM_PULL},
+       {"nvidia,tristate",             TEGRA_PINCONF_PARAM_TRISTATE},
+       {"nvidia,enable-input",         TEGRA_PINCONF_PARAM_ENABLE_INPUT},
+       {"nvidia,open-drain",           TEGRA_PINCONF_PARAM_OPEN_DRAIN},
+       {"nvidia,lock",                 TEGRA_PINCONF_PARAM_LOCK},
+       {"nvidia,io-reset",             TEGRA_PINCONF_PARAM_IORESET},
+       {"nvidia,high-speed-mode",      TEGRA_PINCONF_PARAM_HIGH_SPEED_MODE},
+       {"nvidia,schmitt",              TEGRA_PINCONF_PARAM_SCHMITT},
+       {"nvidia,low-power-mode",       TEGRA_PINCONF_PARAM_LOW_POWER_MODE},
+       {"nvidia,pull-down-strength",   
TEGRA_PINCONF_PARAM_DRIVE_DOWN_STRENGTH},
+       {"nvidia,pull-up-strength",     TEGRA_PINCONF_PARAM_DRIVE_UP_STRENGTH},
+       {"nvidia,slew-rate-falling",    TEGRA_PINCONF_PARAM_SLEW_RATE_FALLING},
+       {"nvidia,slew-rate-rising",     TEGRA_PINCONF_PARAM_SLEW_RATE_RISING},
+};
+
+int tegra_pinctrl_dt_node_to_map(struct pinctrl_dev *pctldev,
+                                struct device_node *np_config,
+                                struct pinctrl_map **map, unsigned *num_maps)
+{
+       struct device_node *np;
+       struct of_iter_string_prop group;
+       unsigned long config;
+       unsigned long *configs = NULL;
+       unsigned num_configs = 0;
+       const char *function;
+       u32 val;
+       int ret, i;
+
+       *map = NULL;
+       *num_maps = 0;
+
+       for_each_child_of_node(np_config, np) {
+               ret = of_property_read_string(np, "nvidia,function", &function);
+               if (ret < 0)
+                       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);
+                               if (ret < 0)
+                                       goto error;
+                       }
+               }
+
+               for_each_string_property_value(group, np, "nvidia,pins") {
+                       if (function) {
+                               ret = add_map_mux(map, num_maps, group.value,
+                                                 function);
+                               if (ret < 0)
+                                       goto error;
+                       }
+
+                       if (num_configs) {
+                               ret = add_map_configs(map, num_maps,
+                                       group.value, configs, num_configs);
+                               if (ret < 0)
+                                       goto error;
+                       }
+               }
+
+               kfree(configs);
+               configs = NULL;
+               num_configs = 0;
+       }
+
+       return 0;
+
+error:
+       kfree(configs);
+       tegra_pinctrl_dt_free_map(pctldev, *map, *num_maps);
+
+       return ret;
+}
+
 static struct pinctrl_ops tegra_pinctrl_ops = {
        .list_groups = tegra_pinctrl_list_groups,
        .get_group_name = tegra_pinctrl_get_group_name,
        .get_group_pins = tegra_pinctrl_get_group_pins,
        .pin_dbg_show = tegra_pinctrl_pin_dbg_show,
+       .dt_node_to_map = tegra_pinctrl_dt_node_to_map,
+       .dt_free_map = tegra_pinctrl_dt_free_map,
 };
 
 static int tegra_pinctrl_list_funcs(struct pinctrl_dev *pctldev,
-- 
1.7.0.4

--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to