sh-pfc: Add pinconf support to DT bindings

Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Acked-by: Heiko Stuebner <heiko@sntech.de>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
diff --git a/drivers/pinctrl/sh-pfc/pinctrl.c b/drivers/pinctrl/sh-pfc/pinctrl.c
index 7e32bb8..2cf2347 100644
--- a/drivers/pinctrl/sh-pfc/pinctrl.c
+++ b/drivers/pinctrl/sh-pfc/pinctrl.c
@@ -74,6 +74,27 @@
 	seq_printf(s, "%s", DRV_NAME);
 }
 
+static int sh_pfc_map_add_config(struct pinctrl_map *map,
+				 const char *group_or_pin,
+				 enum pinctrl_map_type type,
+				 unsigned long *configs,
+				 unsigned int num_configs)
+{
+	unsigned long *cfgs;
+
+	cfgs = kmemdup(configs, num_configs * sizeof(*cfgs),
+		       GFP_KERNEL);
+	if (cfgs == NULL)
+		return -ENOMEM;
+
+	map->type = type;
+	map->data.configs.group_or_pin = group_or_pin;
+	map->data.configs.configs = cfgs;
+	map->data.configs.num_configs = num_configs;
+
+	return 0;
+}
+
 static int sh_pfc_dt_subnode_to_map(struct device *dev, struct device_node *np,
 				    struct pinctrl_map **map,
 				    unsigned int *num_maps, unsigned int *index)
@@ -81,9 +102,14 @@
 	struct pinctrl_map *maps = *map;
 	unsigned int nmaps = *num_maps;
 	unsigned int idx = *index;
+	unsigned int num_configs;
 	const char *function = NULL;
+	unsigned long *configs;
 	struct property *prop;
+	unsigned int num_groups;
+	unsigned int num_pins;
 	const char *group;
+	const char *pin;
 	int ret;
 
 	/* Parse the function and configuration properties. At least a function
@@ -95,25 +121,47 @@
 		return ret;
 	}
 
-	if (!function) {
-		dev_err(dev, "DT node must contain at least one function\n");
+	ret = pinconf_generic_parse_dt_config(np, &configs, &num_configs);
+	if (ret < 0)
+		return ret;
+
+	if (!function && num_configs == 0) {
+		dev_err(dev,
+			"DT node must contain at least a function or config\n");
 		goto done;
 	}
 
-	/* Count the number of groups and reallocate mappings. */
+	/* Count the number of pins and groups and reallocate mappings. */
+	ret = of_property_count_strings(np, "renesas,pins");
+	if (ret == -EINVAL) {
+		num_pins = 0;
+	} else if (ret < 0) {
+		dev_err(dev, "Invalid pins list in DT\n");
+		goto done;
+	} else {
+		num_pins = ret;
+	}
+
 	ret = of_property_count_strings(np, "renesas,groups");
-	if (ret < 0 && ret != -EINVAL) {
+	if (ret == -EINVAL) {
+		num_groups = 0;
+	} else if (ret < 0) {
 		dev_err(dev, "Invalid pin groups list in DT\n");
 		goto done;
+	} else {
+		num_groups = ret;
 	}
 
-	if (!ret) {
-		dev_err(dev, "No group provided in DT node\n");
+	if (!num_pins && !num_groups) {
+		dev_err(dev, "No pin or group provided in DT node\n");
 		ret = -ENODEV;
 		goto done;
 	}
 
-	nmaps += ret;
+	if (function)
+		nmaps += num_groups;
+	if (configs)
+		nmaps += num_pins + num_groups;
 
 	maps = krealloc(maps, sizeof(*maps) * nmaps, GFP_KERNEL);
 	if (maps == NULL) {
@@ -126,22 +174,59 @@
 
 	/* Iterate over pins and groups and create the mappings. */
 	of_property_for_each_string(np, "renesas,groups", prop, group) {
-		maps[idx].type = PIN_MAP_TYPE_MUX_GROUP;
-		maps[idx].data.mux.group = group;
-		maps[idx].data.mux.function = function;
+		if (function) {
+			maps[idx].type = PIN_MAP_TYPE_MUX_GROUP;
+			maps[idx].data.mux.group = group;
+			maps[idx].data.mux.function = function;
+			idx++;
+		}
+
+		if (configs) {
+			ret = sh_pfc_map_add_config(&maps[idx], group,
+						    PIN_MAP_TYPE_CONFIGS_GROUP,
+						    configs, num_configs);
+			if (ret < 0)
+				goto done;
+
+			idx++;
+		}
+	}
+
+	if (!configs) {
+		ret = 0;
+		goto done;
+	}
+
+	of_property_for_each_string(np, "renesas,pins", prop, pin) {
+		ret = sh_pfc_map_add_config(&maps[idx], pin,
+					    PIN_MAP_TYPE_CONFIGS_PIN,
+					    configs, num_configs);
+		if (ret < 0)
+			goto done;
+
 		idx++;
 	}
 
-	ret = 0;
-
 done:
 	*index = idx;
+	kfree(configs);
 	return ret;
 }
 
 static void sh_pfc_dt_free_map(struct pinctrl_dev *pctldev,
 			       struct pinctrl_map *map, unsigned num_maps)
 {
+	unsigned int i;
+
+	if (map == NULL)
+		return;
+
+	for (i = 0; i < num_maps; ++i) {
+		if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP ||
+		    map[i].type == PIN_MAP_TYPE_CONFIGS_PIN)
+			kfree(map[i].data.configs.configs);
+	}
+
 	kfree(map);
 }