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 <swarren@xxxxxxxxxxxxx> --- v2: Rebase on of_property_for_each_string() API changes. --- drivers/pinctrl/pinctrl-tegra.c | 181 +++++++++++++++++++++++++++++++++++++++ 1 files changed, 181 insertions(+), 0 deletions(-) diff --git a/drivers/pinctrl/pinctrl-tegra.c b/drivers/pinctrl/pinctrl-tegra.c index 9b32968..304788f 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,189 @@ 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; + unsigned long config; + const char *function; + unsigned long *configs = NULL; + unsigned num_configs = 0; + u32 val; + int ret, i; + struct property *prop; + const char *group; + + *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; + } + } + + of_property_for_each_string(np, "nvidia,pins", prop, group) { + if (function) { + ret = add_map_mux(map, num_maps, group, + function); + if (ret < 0) + goto error; + } + + if (num_configs) { + ret = add_map_configs(map, num_maps, group, + 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 majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html