]> git.ipfire.org Git - people/ms/u-boot.git/blobdiff - drivers/pinctrl/renesas/pfc.c
Merge git://git.denx.de/u-boot-sunxi
[people/ms/u-boot.git] / drivers / pinctrl / renesas / pfc.c
index 63e2eeb4495575901fa4c85fc6da7412f227c6c2..69e4cec01bd22989e6d2473e45d0a109a79cd58b 100644 (file)
@@ -26,6 +26,8 @@ DECLARE_GLOBAL_DATA_PTR;
 enum sh_pfc_model {
        SH_PFC_R8A7795 = 0,
        SH_PFC_R8A7796,
+       SH_PFC_R8A77970,
+       SH_PFC_R8A77995,
 };
 
 struct sh_pfc_pin_config {
@@ -448,6 +450,51 @@ static const char *sh_pfc_pinctrl_get_function_name(struct udevice *dev,
        return priv->pfc.info->functions[selector].name;
 }
 
+int sh_pfc_config_mux_for_gpio(struct udevice *dev, unsigned pin_selector)
+{
+       struct sh_pfc_pinctrl_priv *priv = dev_get_priv(dev);
+       struct sh_pfc_pinctrl *pmx = &priv->pmx;
+       struct sh_pfc *pfc = &priv->pfc;
+       struct sh_pfc_pin_config *cfg;
+       const struct sh_pfc_pin *pin = NULL;
+       int i, idx;
+
+       for (i = 1; i < pfc->info->nr_pins; i++) {
+               if (priv->pfc.info->pins[i].pin != pin_selector)
+                       continue;
+
+               pin = &priv->pfc.info->pins[i];
+               break;
+       }
+
+       if (!pin)
+               return -EINVAL;
+
+       idx = sh_pfc_get_pin_index(pfc, pin->pin);
+       cfg = &pmx->configs[idx];
+
+       if (cfg->type != PINMUX_TYPE_NONE)
+               return -EBUSY;
+
+       return sh_pfc_config_mux(pfc, pin->enum_id, PINMUX_TYPE_GPIO);
+}
+
+static int sh_pfc_pinctrl_pin_set(struct udevice *dev, unsigned pin_selector,
+                                 unsigned func_selector)
+{
+       struct sh_pfc_pinctrl_priv *priv = dev_get_priv(dev);
+       struct sh_pfc_pinctrl *pmx = &priv->pmx;
+       struct sh_pfc *pfc = &priv->pfc;
+       const struct sh_pfc_pin *pin = &priv->pfc.info->pins[pin_selector];
+       int idx = sh_pfc_get_pin_index(pfc, pin->pin);
+       struct sh_pfc_pin_config *cfg = &pmx->configs[idx];
+
+       if (cfg->type != PINMUX_TYPE_NONE)
+               return -EBUSY;
+
+       return sh_pfc_config_mux(pfc, pin->enum_id, PINMUX_TYPE_FUNCTION);
+}
+
 static int sh_pfc_pinctrl_group_set(struct udevice *dev, unsigned group_selector,
                                     unsigned func_selector)
 {
@@ -477,6 +524,201 @@ static int sh_pfc_pinctrl_group_set(struct udevice *dev, unsigned group_selector
 done:
        return ret;
 }
+#if CONFIG_IS_ENABLED(PINCONF)
+static const struct pinconf_param sh_pfc_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 },
+       { "drive-strength",     PIN_CONFIG_DRIVE_STRENGTH,      0 },
+       { "power-source",       PIN_CONFIG_POWER_SOURCE,        3300 },
+};
+
+static void __iomem *
+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 (void __iomem *)(uintptr_t)reg->reg;
+                       }
+               }
+       }
+
+       return NULL;
+}
+
+static int sh_pfc_pinconf_set_drive_strength(struct sh_pfc *pfc,
+                                            unsigned int pin, u16 strength)
+{
+       unsigned int offset;
+       unsigned int size;
+       unsigned int step;
+       void __iomem *reg;
+       void __iomem *unlock_reg =
+               (void __iomem *)(uintptr_t)pfc->info->unlock_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;
+
+       val = sh_pfc_read_raw_reg(reg, 32);
+       val &= ~GENMASK(offset + size - 1, offset);
+       val |= strength << offset;
+
+       if (unlock_reg)
+               sh_pfc_write_raw_reg(unlock_reg, 32, ~val);
+
+       sh_pfc_write_raw_reg(reg, 32, val);
+
+       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,
+                                   unsigned int param)
+{
+       int idx = sh_pfc_get_pin_index(pfc, _pin);
+       const struct sh_pfc_pin *pin = &pfc->info->pins[idx];
+
+       switch (param) {
+       case PIN_CONFIG_BIAS_DISABLE:
+               return pin->configs &
+                       (SH_PFC_PIN_CFG_PULL_UP | SH_PFC_PIN_CFG_PULL_DOWN);
+
+       case PIN_CONFIG_BIAS_PULL_UP:
+               return pin->configs & SH_PFC_PIN_CFG_PULL_UP;
+
+       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;
+
+       default:
+               return false;
+       }
+}
+
+static int sh_pfc_pinconf_set(struct sh_pfc_pinctrl *pmx, unsigned _pin,
+                             unsigned int param, unsigned int arg)
+{
+       struct sh_pfc *pfc = pmx->pfc;
+       void __iomem *pocctrl;
+       void __iomem *unlock_reg =
+               (void __iomem *)(uintptr_t)pfc->info->unlock_reg;
+       u32 addr, val;
+       int bit, ret;
+
+       if (!sh_pfc_pinconf_validate(pfc, _pin, param))
+               return -ENOTSUPP;
+
+       switch (param) {
+       case PIN_CONFIG_BIAS_PULL_UP:
+       case PIN_CONFIG_BIAS_PULL_DOWN:
+       case PIN_CONFIG_BIAS_DISABLE:
+               if (!pfc->info->ops || !pfc->info->ops->set_bias)
+                       return -ENOTSUPP;
+
+               pfc->info->ops->set_bias(pfc, _pin, param);
+
+               break;
+
+       case PIN_CONFIG_DRIVE_STRENGTH:
+               ret = sh_pfc_pinconf_set_drive_strength(pfc, _pin, arg);
+               if (ret < 0)
+                       return ret;
+
+               break;
+
+       case PIN_CONFIG_POWER_SOURCE:
+               if (!pfc->info->ops || !pfc->info->ops->pin_to_pocctrl)
+                       return -ENOTSUPP;
+
+               bit = pfc->info->ops->pin_to_pocctrl(pfc, _pin, &addr);
+               if (bit < 0) {
+                       printf("invalid pin %#x", _pin);
+                       return bit;
+               }
+
+               if (arg != 1800 && arg != 3300)
+                       return -EINVAL;
+
+               pocctrl = (void __iomem *)(uintptr_t)addr;
+
+               val = sh_pfc_read_raw_reg(pocctrl, 32);
+               if (arg == 3300)
+                       val |= BIT(bit);
+               else
+                       val &= ~BIT(bit);
+
+               if (unlock_reg)
+                       sh_pfc_write_raw_reg(unlock_reg, 32, ~val);
+
+               sh_pfc_write_raw_reg(pocctrl, 32, val);
+
+               break;
+
+       default:
+               return -ENOTSUPP;
+       }
+
+       return 0;
+}
+
+static int sh_pfc_pinconf_pin_set(struct udevice *dev,
+                                 unsigned int pin_selector,
+                                 unsigned int param, unsigned int arg)
+{
+       struct sh_pfc_pinctrl_priv *priv = dev_get_priv(dev);
+       struct sh_pfc_pinctrl *pmx = &priv->pmx;
+       struct sh_pfc *pfc = &priv->pfc;
+       const struct sh_pfc_pin *pin = &pfc->info->pins[pin_selector];
+
+       sh_pfc_pinconf_set(pmx, pin->pin, param, arg);
+
+       return 0;
+}
+
+static int sh_pfc_pinconf_group_set(struct udevice *dev,
+                                     unsigned int group_selector,
+                                     unsigned int param, unsigned int arg)
+{
+       struct sh_pfc_pinctrl_priv *priv = dev_get_priv(dev);
+       struct sh_pfc_pinctrl *pmx = &priv->pmx;
+       struct sh_pfc *pfc = &priv->pfc;
+       const struct sh_pfc_pin_group *grp = &pfc->info->groups[group_selector];
+       unsigned int i;
+
+       for (i = 0; i < grp->nr_pins; i++)
+               sh_pfc_pinconf_set(pmx, grp->pins[i], param, arg);
+
+       return 0;
+}
+#endif
 
 static struct pinctrl_ops sh_pfc_pinctrl_ops = {
        .get_pins_count         = sh_pfc_pinctrl_get_pins_count,
@@ -486,6 +728,13 @@ static struct pinctrl_ops sh_pfc_pinctrl_ops = {
        .get_functions_count    = sh_pfc_pinctrl_get_functions_count,
        .get_function_name      = sh_pfc_pinctrl_get_function_name,
 
+#if CONFIG_IS_ENABLED(PINCONF)
+       .pinconf_num_params     = ARRAY_SIZE(sh_pfc_pinconf_params),
+       .pinconf_params         = sh_pfc_pinconf_params,
+       .pinconf_set            = sh_pfc_pinconf_pin_set,
+       .pinconf_group_set      = sh_pfc_pinconf_group_set,
+#endif
+       .pinmux_set             = sh_pfc_pinctrl_pin_set,
        .pinmux_group_set       = sh_pfc_pinctrl_group_set,
        .set_state              = pinctrl_generic_set_state,
 };
@@ -531,6 +780,14 @@ static int sh_pfc_pinctrl_probe(struct udevice *dev)
        if (model == SH_PFC_R8A7796)
                priv->pfc.info = &r8a7796_pinmux_info;
 #endif
+#ifdef CONFIG_PINCTRL_PFC_R8A77970
+       if (model == SH_PFC_R8A77970)
+               priv->pfc.info = &r8a77970_pinmux_info;
+#endif
+#ifdef CONFIG_PINCTRL_PFC_R8A77995
+       if (model == SH_PFC_R8A77995)
+               priv->pfc.info = &r8a77995_pinmux_info;
+#endif
 
        priv->pmx.pfc = &priv->pfc;
        sh_pfc_init_ranges(&priv->pfc);
@@ -551,6 +808,18 @@ static const struct udevice_id sh_pfc_pinctrl_ids[] = {
                .compatible = "renesas,pfc-r8a7796",
                .data = SH_PFC_R8A7796,
        },
+#endif
+#ifdef CONFIG_PINCTRL_PFC_R8A77970
+       {
+               .compatible = "renesas,pfc-r8a77970",
+               .data = SH_PFC_R8A77970,
+       },
+#endif
+#ifdef CONFIG_PINCTRL_PFC_R8A77995
+       {
+               .compatible = "renesas,pfc-r8a77995",
+               .data = SH_PFC_R8A77995,
+       },
 #endif
        { },
 };