diff mbox

[8/8] pinctrl: tegra: Add complete device tree support

Message ID 1331931269-12262-8-git-send-email-swarren@wwwdotorg.org
State Superseded, archived
Headers show

Commit Message

Stephen Warren March 16, 2012, 8:54 p.m. UTC
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@wwwdotorg.org>
---
 drivers/pinctrl/pinctrl-tegra.c |  180 +++++++++++++++++++++++++++++++++++++++
 1 files changed, 180 insertions(+), 0 deletions(-)
diff mbox

Patch

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,