pinctrl: uniphier: support pin configuration

Support the following DT properties:
  "bias-disable"
  "bias-pull-up"
  "bias-pull-down"
  "bias-pull-pin-default"
  "input-enable"
  "input-disable"

My main motivation is to support pull up/down biasing.  For Pro5 and
later SoCs, the pupdctrl register number is the same as the pinmux
number, so this feature can be supported without having big pin
tables.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c
index 51144b8..d8e9948 100644
--- a/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c
+++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-core.c
@@ -5,6 +5,7 @@
  * SPDX-License-Identifier:	GPL-2.0+
  */
 
+#include <common.h>
 #include <linux/io.h>
 #include <linux/err.h>
 #include <linux/sizes.h>
@@ -15,6 +16,7 @@
 
 #define UNIPHIER_PINCTRL_PINMUX_BASE	0x1000
 #define UNIPHIER_PINCTRL_LOAD_PINMUX	0x1700
+#define UNIPHIER_PINCTRL_PUPDCTRL_BASE	0x1a00
 #define UNIPHIER_PINCTRL_IECTRL		0x1d00
 
 static const char *uniphier_pinctrl_dummy_name = "_dummy";
@@ -55,8 +57,8 @@
 	return priv->socdata->functions[selector];
 }
 
-static void uniphier_pinconf_input_enable_perpin(struct udevice *dev,
-						 unsigned pin)
+static int uniphier_pinconf_input_enable_perpin(struct udevice *dev,
+						unsigned int pin, int enable)
 {
 	struct uniphier_pinctrl_priv *priv = dev_get_priv(dev);
 	unsigned reg;
@@ -66,18 +68,30 @@
 	mask = BIT(pin % 32);
 
 	tmp = readl(priv->base + reg);
-	tmp |= mask;
+	if (enable)
+		tmp |= mask;
+	else
+		tmp &= ~mask;
 	writel(tmp, priv->base + reg);
+
+	return 0;
 }
 
-static void uniphier_pinconf_input_enable_legacy(struct udevice *dev,
-						 unsigned pin)
+static int uniphier_pinconf_input_enable_legacy(struct udevice *dev,
+						unsigned int pin, int enable)
 {
 	struct uniphier_pinctrl_priv *priv = dev_get_priv(dev);
 	int pins_count = priv->socdata->pins_count;
 	const struct uniphier_pinctrl_pin *pins = priv->socdata->pins;
 	int i;
 
+	/*
+	 * Multiple pins share one input enable, per-pin disabling is
+	 * impossible.
+	 */
+	if (!enable)
+		return -EINVAL;
+
 	for (i = 0; i < pins_count; i++) {
 		if (pins[i].number == pin) {
 			unsigned int iectrl;
@@ -89,18 +103,115 @@
 			writel(tmp, priv->base + UNIPHIER_PINCTRL_IECTRL);
 		}
 	}
+
+	return 0;
 }
 
-static void uniphier_pinconf_input_enable(struct udevice *dev, unsigned pin)
+static int uniphier_pinconf_input_enable(struct udevice *dev,
+					 unsigned int pin, int enable)
 {
 	struct uniphier_pinctrl_priv *priv = dev_get_priv(dev);
 
 	if (priv->socdata->caps & UNIPHIER_PINCTRL_CAPS_PERPIN_IECTRL)
-		uniphier_pinconf_input_enable_perpin(dev, pin);
+		return uniphier_pinconf_input_enable_perpin(dev, pin, enable);
 	else
-		uniphier_pinconf_input_enable_legacy(dev, pin);
+		return uniphier_pinconf_input_enable_legacy(dev, pin, enable);
 }
 
+#if CONFIG_IS_ENABLED(PINCONF)
+
+static const struct pinconf_param uniphier_pinconf_params[] = {
+	{ "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 },
+	{ "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 },
+	{ "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 },
+	{ "bias-pull-pin-default", PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, 1 },
+	{ "input-enable", PIN_CONFIG_INPUT_ENABLE, 1 },
+	{ "input-disable", PIN_CONFIG_INPUT_ENABLE, 0 },
+};
+
+static int uniphier_pinconf_bias_set(struct udevice *dev, unsigned int pin,
+				     unsigned int param, unsigned int arg)
+{
+	struct uniphier_pinctrl_priv *priv = dev_get_priv(dev);
+	unsigned int enable = 1;
+	unsigned int reg;
+	u32 mask, tmp;
+
+	if (!(priv->socdata->caps & UNIPHIER_PINCTRL_CAPS_PUPD_SIMPLE))
+		return -ENOTSUPP;
+
+	switch (param) {
+	case PIN_CONFIG_BIAS_DISABLE:
+		enable = 0;
+		break;
+	case PIN_CONFIG_BIAS_PULL_UP:
+	case PIN_CONFIG_BIAS_PULL_DOWN:
+		if (arg == 0)	/* total bias is not supported */
+			return -EINVAL;
+		break;
+	case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT:
+		if (arg == 0)	/* configuration ignored */
+			return 0;
+	default:
+		BUG();
+	}
+
+	reg = UNIPHIER_PINCTRL_PUPDCTRL_BASE + pin / 32 * 4;
+	mask = BIT(pin % 32);
+
+	tmp = readl(priv->base + reg);
+	if (enable)
+		tmp |= mask;
+	else
+		tmp &= ~mask;
+	writel(tmp, priv->base + reg);
+
+	return 0;
+}
+
+static int uniphier_pinconf_set_one(struct udevice *dev, unsigned int pin,
+				    unsigned int param, unsigned int arg)
+{
+	int ret;
+
+	switch (param) {
+	case PIN_CONFIG_BIAS_DISABLE:
+	case PIN_CONFIG_BIAS_PULL_UP:
+	case PIN_CONFIG_BIAS_PULL_DOWN:
+	case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT:
+		ret = uniphier_pinconf_bias_set(dev, pin, param, arg);
+		break;
+	case PIN_CONFIG_INPUT_ENABLE:
+		ret = uniphier_pinconf_input_enable(dev, pin, arg);
+		break;
+	default:
+		printf("unsupported configuration parameter %u\n", param);
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+static int uniphier_pinconf_group_set(struct udevice *dev,
+				      unsigned int group_selector,
+				      unsigned int param, unsigned int arg)
+{
+	struct uniphier_pinctrl_priv *priv = dev_get_priv(dev);
+	const struct uniphier_pinctrl_group *grp =
+					&priv->socdata->groups[group_selector];
+	int i, ret;
+
+	for (i = 0; i < grp->num_pins; i++) {
+		ret = uniphier_pinconf_set_one(dev, grp->pins[i], param, arg);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+#endif /* CONFIG_IS_ENABLED(PINCONF) */
+
 static void uniphier_pinmux_set_one(struct udevice *dev, unsigned pin,
 				    int muxval)
 {
@@ -112,7 +223,7 @@
 	u32 tmp;
 
 	/* some pins need input-enabling */
-	uniphier_pinconf_input_enable(dev, pin);
+	uniphier_pinconf_input_enable(dev, pin, 1);
 
 	if (muxval < 0)
 		return;		/* dedicated pin; nothing to do for pin-mux */
@@ -174,6 +285,11 @@
 	.get_functions_count = uniphier_pinmux_get_functions_count,
 	.get_function_name = uniphier_pinmux_get_function_name,
 	.pinmux_group_set = uniphier_pinmux_group_set,
+#if CONFIG_IS_ENABLED(PINCONF)
+	.pinconf_num_params = ARRAY_SIZE(uniphier_pinconf_params),
+	.pinconf_params = uniphier_pinconf_params,
+	.pinconf_group_set = uniphier_pinconf_group_set,
+#endif
 	.set_state = pinctrl_generic_set_state,
 };
 
diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-ld11.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld11.c
index 1d318d8..53c37cd 100644
--- a/drivers/pinctrl/uniphier/pinctrl-uniphier-ld11.c
+++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld11.c
@@ -92,7 +92,8 @@
 	.groups_count = ARRAY_SIZE(uniphier_ld11_groups),
 	.functions = uniphier_ld11_functions,
 	.functions_count = ARRAY_SIZE(uniphier_ld11_functions),
-	.caps = UNIPHIER_PINCTRL_CAPS_PERPIN_IECTRL,
+	.caps = UNIPHIER_PINCTRL_CAPS_PUPD_SIMPLE |
+		UNIPHIER_PINCTRL_CAPS_PERPIN_IECTRL,
 };
 
 static int uniphier_ld11_pinctrl_probe(struct udevice *dev)
diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-ld20.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld20.c
index 0c46450..5a7d142 100644
--- a/drivers/pinctrl/uniphier/pinctrl-uniphier-ld20.c
+++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld20.c
@@ -106,7 +106,8 @@
 	.groups_count = ARRAY_SIZE(uniphier_ld20_groups),
 	.functions = uniphier_ld20_functions,
 	.functions_count = ARRAY_SIZE(uniphier_ld20_functions),
-	.caps = UNIPHIER_PINCTRL_CAPS_PERPIN_IECTRL,
+	.caps = UNIPHIER_PINCTRL_CAPS_PUPD_SIMPLE |
+		UNIPHIER_PINCTRL_CAPS_PERPIN_IECTRL,
 };
 
 static int uniphier_ld20_pinctrl_probe(struct udevice *dev)
diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-ld6b.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld6b.c
index 80d782c8..b25c7ea 100644
--- a/drivers/pinctrl/uniphier/pinctrl-uniphier-ld6b.c
+++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-ld6b.c
@@ -140,6 +140,7 @@
 	.groups_count = ARRAY_SIZE(uniphier_ld6b_groups),
 	.functions = uniphier_ld6b_functions,
 	.functions_count = ARRAY_SIZE(uniphier_ld6b_functions),
+	.caps = UNIPHIER_PINCTRL_CAPS_PUPD_SIMPLE,
 };
 
 static int uniphier_ld6b_pinctrl_probe(struct udevice *dev)
diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-pro5.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-pro5.c
index 9670f25..70c90ba 100644
--- a/drivers/pinctrl/uniphier/pinctrl-uniphier-pro5.c
+++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-pro5.c
@@ -147,7 +147,8 @@
 	.groups_count = ARRAY_SIZE(uniphier_pro5_groups),
 	.functions = uniphier_pro5_functions,
 	.functions_count = ARRAY_SIZE(uniphier_pro5_functions),
-	.caps = UNIPHIER_PINCTRL_CAPS_DBGMUX_SEPARATE,
+	.caps = UNIPHIER_PINCTRL_CAPS_PUPD_SIMPLE |
+		UNIPHIER_PINCTRL_CAPS_DBGMUX_SEPARATE,
 };
 
 static int uniphier_pro5_pinctrl_probe(struct udevice *dev)
diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier-pxs2.c b/drivers/pinctrl/uniphier/pinctrl-uniphier-pxs2.c
index 1d29170..60777c3 100644
--- a/drivers/pinctrl/uniphier/pinctrl-uniphier-pxs2.c
+++ b/drivers/pinctrl/uniphier/pinctrl-uniphier-pxs2.c
@@ -140,6 +140,7 @@
 	.groups_count = ARRAY_SIZE(uniphier_pxs2_groups),
 	.functions = uniphier_pxs2_functions,
 	.functions_count = ARRAY_SIZE(uniphier_pxs2_functions),
+	.caps = UNIPHIER_PINCTRL_CAPS_PUPD_SIMPLE,
 };
 
 static int uniphier_pxs2_pinctrl_probe(struct udevice *dev)
diff --git a/drivers/pinctrl/uniphier/pinctrl-uniphier.h b/drivers/pinctrl/uniphier/pinctrl-uniphier.h
index 21e2d37..a0eccf8 100644
--- a/drivers/pinctrl/uniphier/pinctrl-uniphier.h
+++ b/drivers/pinctrl/uniphier/pinctrl-uniphier.h
@@ -67,6 +67,7 @@
 	const char * const *functions;
 	int functions_count;
 	unsigned caps;
+#define UNIPHIER_PINCTRL_CAPS_PUPD_SIMPLE	BIT(3)
 #define UNIPHIER_PINCTRL_CAPS_PERPIN_IECTRL	BIT(2)
 #define UNIPHIER_PINCTRL_CAPS_DBGMUX_SEPARATE	BIT(1)
 #define UNIPHIER_PINCTRL_CAPS_MUX_4BIT		BIT(0)