pinctrl: uniphier: support drive-strength configuration
authorMasahiro Yamada <yamada.masahiro@socionext.com>
Sat, 5 May 2018 10:53:55 +0000 (19:53 +0900)
committerMasahiro Yamada <yamada.masahiro@socionext.com>
Tue, 8 May 2018 01:25:15 +0000 (10:25 +0900)
This allows our DT to specify drive-strength property.

Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>
drivers/pinctrl/uniphier/pinctrl-uniphier-core.c
drivers/pinctrl/uniphier/pinctrl-uniphier.h

index 916d0519110a95edf1aad55af3a76b0252926edf..a5935e84de3e15fa22bccd81a568da9d16e90468 100644 (file)
@@ -16,6 +16,9 @@
 
 #define UNIPHIER_PINCTRL_PINMUX_BASE   0x1000
 #define UNIPHIER_PINCTRL_LOAD_PINMUX   0x1700
+#define UNIPHIER_PINCTRL_DRVCTRL_BASE  0x1800
+#define UNIPHIER_PINCTRL_DRV2CTRL_BASE 0x1900
+#define UNIPHIER_PINCTRL_DRV3CTRL_BASE 0x1980
 #define UNIPHIER_PINCTRL_PUPDCTRL_BASE 0x1a00
 #define UNIPHIER_PINCTRL_IECTRL                0x1d00
 
@@ -141,10 +144,25 @@ static const struct pinconf_param uniphier_pinconf_params[] = {
        { "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 },
+       { "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 0 },
        { "input-enable", PIN_CONFIG_INPUT_ENABLE, 1 },
        { "input-disable", PIN_CONFIG_INPUT_ENABLE, 0 },
 };
 
+static const struct uniphier_pinctrl_pin *
+uniphier_pinctrl_pin_get(struct uniphier_pinctrl_priv *priv, unsigned int pin)
+{
+       const struct uniphier_pinctrl_pin *pins = priv->socdata->pins;
+       int pins_count = priv->socdata->pins_count;
+       int i;
+
+       for (i = 0; i < pins_count; i++)
+               if (pins[i].number == pin)
+                       return &pins[i];
+
+       return NULL;
+}
+
 static int uniphier_pinconf_bias_set(struct udevice *dev, unsigned int pin,
                                     unsigned int param, unsigned int arg)
 {
@@ -185,6 +203,86 @@ static int uniphier_pinconf_bias_set(struct udevice *dev, unsigned int pin,
        return 0;
 }
 
+static const unsigned int uniphier_pinconf_drv_strengths_1bit[] = {
+       4, 8,
+};
+
+static const unsigned int uniphier_pinconf_drv_strengths_2bit[] = {
+       8, 12, 16, 20,
+};
+
+static const unsigned int uniphier_pinconf_drv_strengths_3bit[] = {
+       4, 5, 7, 9, 11, 12, 14, 16,
+};
+
+static int uniphier_pinconf_drive_set(struct udevice *dev, unsigned int pin,
+                                     unsigned int strength)
+{
+       struct uniphier_pinctrl_priv *priv = dev_get_priv(dev);
+       const struct uniphier_pinctrl_pin *desc;
+       const unsigned int *strengths;
+       unsigned int base, stride, width, drvctrl, reg, shift;
+       u32 val, mask, tmp;
+
+       desc = uniphier_pinctrl_pin_get(priv, pin);
+       if (WARN_ON(!desc))
+               return -EINVAL;
+
+       switch (uniphier_pin_get_drv_type(desc->data)) {
+       case UNIPHIER_PIN_DRV_1BIT:
+               strengths = uniphier_pinconf_drv_strengths_1bit;
+               base = UNIPHIER_PINCTRL_DRVCTRL_BASE;
+               stride = 1;
+               width = 1;
+               break;
+       case UNIPHIER_PIN_DRV_2BIT:
+               strengths = uniphier_pinconf_drv_strengths_2bit;
+               base = UNIPHIER_PINCTRL_DRV2CTRL_BASE;
+               stride = 2;
+               width = 2;
+               break;
+       case UNIPHIER_PIN_DRV_3BIT:
+               strengths = uniphier_pinconf_drv_strengths_3bit;
+               base = UNIPHIER_PINCTRL_DRV3CTRL_BASE;
+               stride = 4;
+               width = 3;
+               break;
+       default:
+               /* drive strength control is not supported for this pin */
+               return -EINVAL;
+       }
+
+       drvctrl = uniphier_pin_get_drvctrl(desc->data);
+       drvctrl *= stride;
+
+       reg = base + drvctrl / 32 * 4;
+       shift = drvctrl % 32;
+       mask = (1U << width) - 1;
+
+       for (val = 0; val <= mask; val++) {
+               if (strengths[val] > strength)
+                       break;
+       }
+
+       if (val == 0) {
+               dev_err(dev, "unsupported drive strength %u mA for pin %s\n",
+                       strength, desc->name);
+               return -EINVAL;
+       }
+
+       if (!mask)
+               return 0;
+
+       val--;
+
+       tmp = readl(priv->base + reg);
+       tmp &= ~(mask << shift);
+       tmp |= (mask & val) << shift;
+       writel(tmp, priv->base + reg);
+
+       return 0;
+}
+
 static int uniphier_pinconf_set(struct udevice *dev, unsigned int pin,
                                unsigned int param, unsigned int arg)
 {
@@ -197,6 +295,9 @@ static int uniphier_pinconf_set(struct udevice *dev, unsigned int pin,
        case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT:
                ret = uniphier_pinconf_bias_set(dev, pin, param, arg);
                break;
+       case PIN_CONFIG_DRIVE_STRENGTH:
+               ret = uniphier_pinconf_drive_set(dev, pin, arg);
+               break;
        case PIN_CONFIG_INPUT_ENABLE:
                ret = uniphier_pinconf_input_enable(dev, pin, arg);
                break;
index b3257f430d45d9a8c5dcc26575c7329a2c34df90..8f83ecae7dbabcd31a21f6e3f3beedf0544d1707 100644 (file)
 #include <linux/kernel.h>
 #include <linux/types.h>
 
-#define UNIPHIER_PIN_ATTR_PACKED(iectrl)       (iectrl)
+/* drive strength control register number */
+#define UNIPHIER_PIN_DRVCTRL_SHIFT     0
+#define UNIPHIER_PIN_DRVCTRL_BITS      9
+#define UNIPHIER_PIN_DRVCTRL_MASK      ((1U << (UNIPHIER_PIN_DRVCTRL_BITS)) \
+                                        - 1)
+
+/* drive control type */
+#define UNIPHIER_PIN_DRV_TYPE_SHIFT    ((UNIPHIER_PIN_DRVCTRL_SHIFT) + \
+                                        (UNIPHIER_PIN_DRVCTRL_BITS))
+#define UNIPHIER_PIN_DRV_TYPE_BITS     2
+#define UNIPHIER_PIN_DRV_TYPE_MASK     ((1U << (UNIPHIER_PIN_DRV_TYPE_BITS)) \
+                                        - 1)
+
+/* drive control type */
+enum uniphier_pin_drv_type {
+       UNIPHIER_PIN_DRV_1BIT,          /* 2 level control: 4/8 mA */
+       UNIPHIER_PIN_DRV_2BIT,          /* 4 level control: 8/12/16/20 mA */
+       UNIPHIER_PIN_DRV_3BIT,          /* 8 level control: 4/5/7/9/11/12/14/16 mA */
+};
+
+#define UNIPHIER_PIN_DRVCTRL(x) \
+       (((x) & (UNIPHIER_PIN_DRVCTRL_MASK)) << (UNIPHIER_PIN_DRVCTRL_SHIFT))
+#define UNIPHIER_PIN_DRV_TYPE(x) \
+       (((x) & (UNIPHIER_PIN_DRV_TYPE_MASK)) << (UNIPHIER_PIN_DRV_TYPE_SHIFT))
+
+#define UNIPHIER_PIN_ATTR_PACKED(drvctrl, drv_type)    \
+       UNIPHIER_PIN_DRVCTRL(drvctrl) |                 \
+       UNIPHIER_PIN_DRV_TYPE(drv_type)
+
+static inline unsigned int uniphier_pin_get_drvctrl(unsigned int data)
+{
+       return (data >> UNIPHIER_PIN_DRVCTRL_SHIFT) & UNIPHIER_PIN_DRVCTRL_MASK;
+}
 
-static inline unsigned int uniphier_pin_get_iectrl(unsigned long data)
+static inline unsigned int uniphier_pin_get_drv_type(unsigned int data)
 {
-       return data;
+       return (data >> UNIPHIER_PIN_DRV_TYPE_SHIFT) &
+                                               UNIPHIER_PIN_DRV_TYPE_MASK;
 }
 
 /**
@@ -73,10 +106,11 @@ struct uniphier_pinctrl_socdata {
 #define UNIPHIER_PINCTRL_CAPS_MUX_4BIT         BIT(0)
 };
 
-#define UNIPHIER_PINCTRL_PIN(a, b)                                     \
+#define UNIPHIER_PINCTRL_PIN(a, b, c, d)                               \
 {                                                                      \
        .number = a,                                                    \
-       .data = UNIPHIER_PIN_ATTR_PACKED(b),                            \
+       .name = b,                                                      \
+       .data = UNIPHIER_PIN_ATTR_PACKED(c, d),                         \
 }
 
 #define __UNIPHIER_PINCTRL_GROUP(grp)                                  \