aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pinctrl/sh-pfc/pinctrl.c
diff options
context:
space:
mode:
authorLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>2016-03-23 16:06:00 +0200
committerGeert Uytterhoeven <geert+renesas@glider.be>2016-03-29 09:23:01 +0200
commit3caa7d8c3f03ad6f1b66f10f67dc68cb3af55fe1 (patch)
treebba9820da349807a8581fdd59235406fcad48e40 /drivers/pinctrl/sh-pfc/pinctrl.c
parentpinctrl: sh-pfc: IPSRx and MOD_SELx should be set before GPSRx (diff)
downloadlinux-dev-3caa7d8c3f03ad6f1b66f10f67dc68cb3af55fe1.tar.xz
linux-dev-3caa7d8c3f03ad6f1b66f10f67dc68cb3af55fe1.zip
pinctrl: sh-pfc: Add drive strength support
Add support for the drive-strengh pin configuration using the generic pinconf DT bindings. Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Diffstat (limited to 'drivers/pinctrl/sh-pfc/pinctrl.c')
-rw-r--r--drivers/pinctrl/sh-pfc/pinctrl.c111
1 files changed, 111 insertions, 0 deletions
diff --git a/drivers/pinctrl/sh-pfc/pinctrl.c b/drivers/pinctrl/sh-pfc/pinctrl.c
index 87b0a599afaf..8efaa0631be6 100644
--- a/drivers/pinctrl/sh-pfc/pinctrl.c
+++ b/drivers/pinctrl/sh-pfc/pinctrl.c
@@ -476,6 +476,91 @@ static const struct pinmux_ops sh_pfc_pinmux_ops = {
.gpio_set_direction = sh_pfc_gpio_set_direction,
};
+static u32 sh_pfc_pinconf_find_drive_strength_reg(struct sh_pfc *pfc,
+ unsigned int pin, unsigned int *offset, unsigned int *size)
+{
+ const struct pinmux_drive_reg_field *field;
+ const struct pinmux_drive_reg *reg;
+ unsigned int i;
+
+ for (reg = pfc->info->drive_regs; reg->reg; ++reg) {
+ for (i = 0; i < ARRAY_SIZE(reg->fields); ++i) {
+ field = &reg->fields[i];
+
+ if (field->size && field->pin == pin) {
+ *offset = field->offset;
+ *size = field->size;
+
+ return reg->reg;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int sh_pfc_pinconf_get_drive_strength(struct sh_pfc *pfc,
+ unsigned int pin)
+{
+ unsigned long flags;
+ unsigned int offset;
+ unsigned int size;
+ u32 reg;
+ u32 val;
+
+ reg = sh_pfc_pinconf_find_drive_strength_reg(pfc, pin, &offset, &size);
+ if (!reg)
+ return -EINVAL;
+
+ spin_lock_irqsave(&pfc->lock, flags);
+ val = sh_pfc_read_reg(pfc, reg, 32);
+ spin_unlock_irqrestore(&pfc->lock, flags);
+
+ val = (val >> offset) & GENMASK(size - 1, 0);
+
+ /* Convert the value to mA based on a full drive strength value of 24mA.
+ * We can make the full value configurable later if needed.
+ */
+ return (val + 1) * (size == 2 ? 6 : 3);
+}
+
+static int sh_pfc_pinconf_set_drive_strength(struct sh_pfc *pfc,
+ unsigned int pin, u16 strength)
+{
+ unsigned long flags;
+ unsigned int offset;
+ unsigned int size;
+ unsigned int step;
+ u32 reg;
+ u32 val;
+
+ reg = sh_pfc_pinconf_find_drive_strength_reg(pfc, pin, &offset, &size);
+ if (!reg)
+ return -EINVAL;
+
+ step = size == 2 ? 6 : 3;
+
+ if (strength < step || strength > 24)
+ return -EINVAL;
+
+ /* Convert the value from mA based on a full drive strength value of
+ * 24mA. We can make the full value configurable later if needed.
+ */
+ strength = strength / step - 1;
+
+ spin_lock_irqsave(&pfc->lock, flags);
+
+ val = sh_pfc_read_reg(pfc, reg, 32);
+ val &= ~GENMASK(offset + size - 1, offset);
+ val |= strength << offset;
+
+ sh_pfc_write_reg(pfc, reg, 32, val);
+
+ spin_unlock_irqrestore(&pfc->lock, flags);
+
+ return 0;
+}
+
/* Check whether the requested parameter is supported for a pin. */
static bool sh_pfc_pinconf_validate(struct sh_pfc *pfc, unsigned int _pin,
enum pin_config_param param)
@@ -493,6 +578,9 @@ static bool sh_pfc_pinconf_validate(struct sh_pfc *pfc, unsigned int _pin,
case PIN_CONFIG_BIAS_PULL_DOWN:
return pin->configs & SH_PFC_PIN_CFG_PULL_DOWN;
+ case PIN_CONFIG_DRIVE_STRENGTH:
+ return pin->configs & SH_PFC_PIN_CFG_DRIVE_STRENGTH;
+
case PIN_CONFIG_POWER_SOURCE:
return pin->configs & SH_PFC_PIN_CFG_IO_VOLTAGE;
@@ -532,6 +620,17 @@ static int sh_pfc_pinconf_get(struct pinctrl_dev *pctldev, unsigned _pin,
break;
}
+ case PIN_CONFIG_DRIVE_STRENGTH: {
+ int ret;
+
+ ret = sh_pfc_pinconf_get_drive_strength(pfc, _pin);
+ if (ret < 0)
+ return ret;
+
+ *config = ret;
+ break;
+ }
+
case PIN_CONFIG_POWER_SOURCE: {
int ret;
@@ -584,6 +683,18 @@ static int sh_pfc_pinconf_set(struct pinctrl_dev *pctldev, unsigned _pin,
break;
+ case PIN_CONFIG_DRIVE_STRENGTH: {
+ unsigned int arg =
+ pinconf_to_config_argument(configs[i]);
+ int ret;
+
+ ret = sh_pfc_pinconf_set_drive_strength(pfc, _pin, arg);
+ if (ret < 0)
+ return ret;
+
+ break;
+ }
+
case PIN_CONFIG_POWER_SOURCE: {
unsigned int arg =
pinconf_to_config_argument(configs[i]);