From f147cf49ef39f5e87d5df9ef1fab52683bc75c63 Mon Sep 17 00:00:00 2001
From: Linus Walleij <linus.walleij@linaro.org>
Date: Sat, 2 Dec 2017 12:23:09 +0100
Subject: [PATCH 11/31] pinctrl: gemini: Support drive strength setting

The Gemini pin controller can set drive strength for a few
select groups of pins (not individually). Implement this
for GMAC0 and 1 (ethernet ports), IDE and PCI.

Cc: devicetree@vger.kernel.org
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
---
 .../bindings/pinctrl/cortina,gemini-pinctrl.txt    |  3 +
 drivers/pinctrl/pinctrl-gemini.c                   | 81 ++++++++++++++++++++++
 2 files changed, 84 insertions(+)

--- a/Documentation/devicetree/bindings/pinctrl/cortina,gemini-pinctrl.txt
+++ b/Documentation/devicetree/bindings/pinctrl/cortina,gemini-pinctrl.txt
@@ -17,6 +17,9 @@ and generic pin config nodes.
 
 Supported configurations:
 - skew-delay is supported on the Ethernet pins
+- drive-strength with 4, 8, 12 or 16 mA as argument is supported for
+  entire groups on the groups "idegrp", "gmii_gmac0_grp", "gmii_gmac1_grp"
+  and "pcigrp".
 
 Example:
 
--- a/drivers/pinctrl/pinctrl-gemini.c
+++ b/drivers/pinctrl/pinctrl-gemini.c
@@ -67,6 +67,9 @@ struct gemini_pmx {
  *	elements in .pins so we can iterate over that array
  * @mask: bits to clear to enable this when doing pin muxing
  * @value: bits to set to enable this when doing pin muxing
+ * @driving_mask: bitmask for the IO Pad driving register for this
+ *	group, if it supports altering the driving strength of
+ *	its lines.
  */
 struct gemini_pin_group {
 	const char *name;
@@ -74,12 +77,14 @@ struct gemini_pin_group {
 	const unsigned int num_pins;
 	u32 mask;
 	u32 value;
+	u32 driving_mask;
 };
 
 /* Some straight-forward control registers */
 #define GLOBAL_WORD_ID		0x00
 #define GLOBAL_STATUS		0x04
 #define GLOBAL_STATUS_FLPIN	BIT(20)
+#define GLOBAL_IODRIVE		0x10
 #define GLOBAL_GMAC_CTRL_SKEW	0x1c
 #define GLOBAL_GMAC0_DATA_SKEW	0x20
 #define GLOBAL_GMAC1_DATA_SKEW	0x24
@@ -741,6 +746,7 @@ static const struct gemini_pin_group gem
 		/* Conflict with all flash usage */
 		.value = IDE_PADS_ENABLE | NAND_PADS_DISABLE |
 			PFLASH_PADS_DISABLE | SFLASH_PADS_DISABLE,
+		.driving_mask = GENMASK(21, 20),
 	},
 	{
 		.name = "satagrp",
@@ -756,6 +762,7 @@ static const struct gemini_pin_group gem
 		.name = "gmii_gmac0_grp",
 		.pins = gmii_gmac0_3512_pins,
 		.num_pins = ARRAY_SIZE(gmii_gmac0_3512_pins),
+		.driving_mask = GENMASK(17, 16),
 	},
 	{
 		.name = "gmii_gmac1_grp",
@@ -763,6 +770,7 @@ static const struct gemini_pin_group gem
 		.num_pins = ARRAY_SIZE(gmii_gmac1_3512_pins),
 		/* Bring out RGMII on the GMAC1 pins */
 		.value = GEMINI_GMAC_IOSEL_GMAC0_GMAC1_RGMII,
+		.driving_mask = GENMASK(19, 18),
 	},
 	{
 		.name = "pcigrp",
@@ -770,6 +778,7 @@ static const struct gemini_pin_group gem
 		.num_pins = ARRAY_SIZE(pci_3512_pins),
 		/* Conflict only with GPIO2 */
 		.value = PCI_PADS_ENABLE | PCI_CLK_PAD_ENABLE,
+		.driving_mask = GENMASK(23, 22),
 	},
 	{
 		.name = "lpcgrp",
@@ -1686,6 +1695,7 @@ static const struct gemini_pin_group gem
 		/* Conflict with all flash usage */
 		.value = IDE_PADS_ENABLE | NAND_PADS_DISABLE |
 			PFLASH_PADS_DISABLE | SFLASH_PADS_DISABLE,
+		.driving_mask = GENMASK(21, 20),
 	},
 	{
 		.name = "satagrp",
@@ -1701,6 +1711,7 @@ static const struct gemini_pin_group gem
 		.name = "gmii_gmac0_grp",
 		.pins = gmii_gmac0_3516_pins,
 		.num_pins = ARRAY_SIZE(gmii_gmac0_3516_pins),
+		.driving_mask = GENMASK(17, 16),
 	},
 	{
 		.name = "gmii_gmac1_grp",
@@ -1708,6 +1719,7 @@ static const struct gemini_pin_group gem
 		.num_pins = ARRAY_SIZE(gmii_gmac1_3516_pins),
 		/* Bring out RGMII on the GMAC1 pins */
 		.value = GEMINI_GMAC_IOSEL_GMAC0_GMAC1_RGMII,
+		.driving_mask = GENMASK(19, 18),
 	},
 	{
 		.name = "pcigrp",
@@ -1715,6 +1727,7 @@ static const struct gemini_pin_group gem
 		.num_pins = ARRAY_SIZE(pci_3516_pins),
 		/* Conflict only with GPIO2 */
 		.value = PCI_PADS_ENABLE | PCI_CLK_PAD_ENABLE,
+		.driving_mask = GENMASK(23, 22),
 	},
 	{
 		.name = "lpcgrp",
@@ -2423,9 +2436,77 @@ static int gemini_pinconf_set(struct pin
 	return ret;
 }
 
+static int gemini_pinconf_group_set(struct pinctrl_dev *pctldev,
+				    unsigned selector,
+				    unsigned long *configs,
+				    unsigned num_configs)
+{
+	struct gemini_pmx *pmx = pinctrl_dev_get_drvdata(pctldev);
+	const struct gemini_pin_group *grp = NULL;
+	enum pin_config_param param;
+	u32 arg;
+	u32 val;
+	int i;
+
+	if (pmx->is_3512)
+		grp = &gemini_3512_pin_groups[selector];
+	if (pmx->is_3516)
+		grp = &gemini_3516_pin_groups[selector];
+
+	/* First figure out if this group supports configs */
+	if (!grp->driving_mask) {
+		dev_err(pmx->dev, "pin config group \"%s\" does "
+			"not support drive strength setting\n",
+			grp->name);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < num_configs; i++) {
+		param = pinconf_to_config_param(configs[i]);
+		arg = pinconf_to_config_argument(configs[i]);
+
+		switch (param) {
+		case PIN_CONFIG_DRIVE_STRENGTH:
+			switch (arg) {
+			case 4:
+				val = 0;
+				break;
+			case 8:
+				val = 1;
+				break;
+			case 12:
+				val = 2;
+				break;
+			case 16:
+				val = 3;
+				break;
+			default:
+				dev_err(pmx->dev,
+					"invalid drive strength %d mA\n",
+					arg);
+				return -ENOTSUPP;
+			}
+			val <<= (ffs(grp->driving_mask) - 1);
+			regmap_update_bits(pmx->map, GLOBAL_IODRIVE,
+					   grp->driving_mask,
+					   val);
+			dev_info(pmx->dev,
+				 "set group %s to %d mA drive strength mask %08x val %08x\n",
+				 grp->name, arg, grp->driving_mask, val);
+			break;
+		default:
+			dev_err(pmx->dev, "invalid config param %04x\n", param);
+			return -ENOTSUPP;
+		}
+	}
+
+	return 0;
+}
+
 static const struct pinconf_ops gemini_pinconf_ops = {
 	.pin_config_get = gemini_pinconf_get,
 	.pin_config_set = gemini_pinconf_set,
+	.pin_config_group_set = gemini_pinconf_group_set,
 	.is_generic = true,
 };
 
