|
@@ -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 = ®->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]);
|