Merge tag 'pinctrl-v5.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw...
[sfrench/cifs-2.6.git] / drivers / pinctrl / meson / pinctrl-meson.c
index 077de592578350ba8cc7f7ac001b33a3016d1c0c..59678692620986316faad33a6b231d8a30067f4f 100644 (file)
@@ -168,68 +168,223 @@ int meson_pmx_get_groups(struct pinctrl_dev *pcdev, unsigned selector,
        return 0;
 }
 
-static int meson_pinconf_set(struct pinctrl_dev *pcdev, unsigned int pin,
-                            unsigned long *configs, unsigned num_configs)
+static int meson_pinconf_set_gpio_bit(struct meson_pinctrl *pc,
+                                     unsigned int pin,
+                                     unsigned int reg_type,
+                                     bool arg)
 {
-       struct meson_pinctrl *pc = pinctrl_dev_get_drvdata(pcdev);
        struct meson_bank *bank;
-       enum pin_config_param param;
        unsigned int reg, bit;
-       int i, ret;
+       int ret;
+
+       ret = meson_get_bank(pc, pin, &bank);
+       if (ret)
+               return ret;
+
+       meson_calc_reg_and_bit(bank, pin, reg_type, &reg, &bit);
+       return regmap_update_bits(pc->reg_gpio, reg, BIT(bit),
+                                 arg ? BIT(bit) : 0);
+}
+
+static int meson_pinconf_get_gpio_bit(struct meson_pinctrl *pc,
+                                     unsigned int pin,
+                                     unsigned int reg_type)
+{
+       struct meson_bank *bank;
+       unsigned int reg, bit, val;
+       int ret;
 
        ret = meson_get_bank(pc, pin, &bank);
        if (ret)
                return ret;
 
+       meson_calc_reg_and_bit(bank, pin, reg_type, &reg, &bit);
+       ret = regmap_read(pc->reg_gpio, reg, &val);
+       if (ret)
+               return ret;
+
+       return BIT(bit) & val ? 1 : 0;
+}
+
+static int meson_pinconf_set_output(struct meson_pinctrl *pc,
+                                   unsigned int pin,
+                                   bool out)
+{
+       return meson_pinconf_set_gpio_bit(pc, pin, REG_DIR, !out);
+}
+
+static int meson_pinconf_get_output(struct meson_pinctrl *pc,
+                                   unsigned int pin)
+{
+       int ret = meson_pinconf_get_gpio_bit(pc, pin, REG_DIR);
+
+       if (ret < 0)
+               return ret;
+
+       return !ret;
+}
+
+static int meson_pinconf_set_drive(struct meson_pinctrl *pc,
+                                  unsigned int pin,
+                                  bool high)
+{
+       return meson_pinconf_set_gpio_bit(pc, pin, REG_OUT, high);
+}
+
+static int meson_pinconf_get_drive(struct meson_pinctrl *pc,
+                                  unsigned int pin)
+{
+       return meson_pinconf_get_gpio_bit(pc, pin, REG_OUT);
+}
+
+static int meson_pinconf_set_output_drive(struct meson_pinctrl *pc,
+                                         unsigned int pin,
+                                         bool high)
+{
+       int ret;
+
+       ret = meson_pinconf_set_output(pc, pin, true);
+       if (ret)
+               return ret;
+
+       return meson_pinconf_set_drive(pc, pin, high);
+}
+
+static int meson_pinconf_disable_bias(struct meson_pinctrl *pc,
+                                     unsigned int pin)
+{
+       struct meson_bank *bank;
+       unsigned int reg, bit = 0;
+       int ret;
+
+       ret = meson_get_bank(pc, pin, &bank);
+       if (ret)
+               return ret;
+
+       meson_calc_reg_and_bit(bank, pin, REG_PULLEN, &reg, &bit);
+       ret = regmap_update_bits(pc->reg_pullen, reg, BIT(bit), 0);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int meson_pinconf_enable_bias(struct meson_pinctrl *pc, unsigned int pin,
+                                    bool pull_up)
+{
+       struct meson_bank *bank;
+       unsigned int reg, bit, val = 0;
+       int ret;
+
+       ret = meson_get_bank(pc, pin, &bank);
+       if (ret)
+               return ret;
+
+       meson_calc_reg_and_bit(bank, pin, REG_PULL, &reg, &bit);
+       if (pull_up)
+               val = BIT(bit);
+
+       ret = regmap_update_bits(pc->reg_pull, reg, BIT(bit), val);
+       if (ret)
+               return ret;
+
+       meson_calc_reg_and_bit(bank, pin, REG_PULLEN, &reg, &bit);
+       ret = regmap_update_bits(pc->reg_pullen, reg, BIT(bit), BIT(bit));
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int meson_pinconf_set_drive_strength(struct meson_pinctrl *pc,
+                                           unsigned int pin,
+                                           u16 drive_strength_ua)
+{
+       struct meson_bank *bank;
+       unsigned int reg, bit, ds_val;
+       int ret;
+
+       if (!pc->reg_ds) {
+               dev_err(pc->dev, "drive-strength not supported\n");
+               return -ENOTSUPP;
+       }
+
+       ret = meson_get_bank(pc, pin, &bank);
+       if (ret)
+               return ret;
+
+       meson_calc_reg_and_bit(bank, pin, REG_DS, &reg, &bit);
+       bit = bit << 1;
+
+       if (drive_strength_ua <= 500) {
+               ds_val = MESON_PINCONF_DRV_500UA;
+       } else if (drive_strength_ua <= 2500) {
+               ds_val = MESON_PINCONF_DRV_2500UA;
+       } else if (drive_strength_ua <= 3000) {
+               ds_val = MESON_PINCONF_DRV_3000UA;
+       } else if (drive_strength_ua <= 4000) {
+               ds_val = MESON_PINCONF_DRV_4000UA;
+       } else {
+               dev_warn_once(pc->dev,
+                             "pin %u: invalid drive-strength : %d , default to 4mA\n",
+                             pin, drive_strength_ua);
+               ds_val = MESON_PINCONF_DRV_4000UA;
+       }
+
+       ret = regmap_update_bits(pc->reg_ds, reg, 0x3 << bit, ds_val << bit);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int meson_pinconf_set(struct pinctrl_dev *pcdev, unsigned int pin,
+                            unsigned long *configs, unsigned num_configs)
+{
+       struct meson_pinctrl *pc = pinctrl_dev_get_drvdata(pcdev);
+       enum pin_config_param param;
+       unsigned int arg = 0;
+       int i, ret;
+
        for (i = 0; i < num_configs; i++) {
                param = pinconf_to_config_param(configs[i]);
 
+               switch (param) {
+               case PIN_CONFIG_DRIVE_STRENGTH_UA:
+               case PIN_CONFIG_OUTPUT_ENABLE:
+               case PIN_CONFIG_OUTPUT:
+                       arg = pinconf_to_config_argument(configs[i]);
+                       break;
+
+               default:
+                       break;
+               }
+
                switch (param) {
                case PIN_CONFIG_BIAS_DISABLE:
-                       dev_dbg(pc->dev, "pin %u: disable bias\n", pin);
-
-                       meson_calc_reg_and_bit(bank, pin, REG_PULLEN, &reg,
-                                              &bit);
-                       ret = regmap_update_bits(pc->reg_pullen, reg,
-                                                BIT(bit), 0);
-                       if (ret)
-                               return ret;
+                       ret = meson_pinconf_disable_bias(pc, pin);
                        break;
                case PIN_CONFIG_BIAS_PULL_UP:
-                       dev_dbg(pc->dev, "pin %u: enable pull-up\n", pin);
-
-                       meson_calc_reg_and_bit(bank, pin, REG_PULLEN,
-                                              &reg, &bit);
-                       ret = regmap_update_bits(pc->reg_pullen, reg,
-                                                BIT(bit), BIT(bit));
-                       if (ret)
-                               return ret;
-
-                       meson_calc_reg_and_bit(bank, pin, REG_PULL, &reg, &bit);
-                       ret = regmap_update_bits(pc->reg_pull, reg,
-                                                BIT(bit), BIT(bit));
-                       if (ret)
-                               return ret;
+                       ret = meson_pinconf_enable_bias(pc, pin, true);
                        break;
                case PIN_CONFIG_BIAS_PULL_DOWN:
-                       dev_dbg(pc->dev, "pin %u: enable pull-down\n", pin);
-
-                       meson_calc_reg_and_bit(bank, pin, REG_PULLEN,
-                                              &reg, &bit);
-                       ret = regmap_update_bits(pc->reg_pullen, reg,
-                                                BIT(bit), BIT(bit));
-                       if (ret)
-                               return ret;
-
-                       meson_calc_reg_and_bit(bank, pin, REG_PULL, &reg, &bit);
-                       ret = regmap_update_bits(pc->reg_pull, reg,
-                                                BIT(bit), 0);
-                       if (ret)
-                               return ret;
+                       ret = meson_pinconf_enable_bias(pc, pin, false);
+                       break;
+               case PIN_CONFIG_DRIVE_STRENGTH_UA:
+                       ret = meson_pinconf_set_drive_strength(pc, pin, arg);
+                       break;
+               case PIN_CONFIG_OUTPUT_ENABLE:
+                       ret = meson_pinconf_set_output(pc, pin, arg);
+                       break;
+               case PIN_CONFIG_OUTPUT:
+                       ret = meson_pinconf_set_output_drive(pc, pin, arg);
                        break;
                default:
-                       return -ENOTSUPP;
+                       ret = -ENOTSUPP;
                }
+
+               if (ret)
+                       return ret;
        }
 
        return 0;
@@ -269,12 +424,55 @@ static int meson_pinconf_get_pull(struct meson_pinctrl *pc, unsigned int pin)
        return conf;
 }
 
+static int meson_pinconf_get_drive_strength(struct meson_pinctrl *pc,
+                                           unsigned int pin,
+                                           u16 *drive_strength_ua)
+{
+       struct meson_bank *bank;
+       unsigned int reg, bit;
+       unsigned int val;
+       int ret;
+
+       if (!pc->reg_ds)
+               return -ENOTSUPP;
+
+       ret = meson_get_bank(pc, pin, &bank);
+       if (ret)
+               return ret;
+
+       meson_calc_reg_and_bit(bank, pin, REG_DS, &reg, &bit);
+
+       ret = regmap_read(pc->reg_ds, reg, &val);
+       if (ret)
+               return ret;
+
+       switch ((val >> bit) & 0x3) {
+       case MESON_PINCONF_DRV_500UA:
+               *drive_strength_ua = 500;
+               break;
+       case MESON_PINCONF_DRV_2500UA:
+               *drive_strength_ua = 2500;
+               break;
+       case MESON_PINCONF_DRV_3000UA:
+               *drive_strength_ua = 3000;
+               break;
+       case MESON_PINCONF_DRV_4000UA:
+               *drive_strength_ua = 4000;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int meson_pinconf_get(struct pinctrl_dev *pcdev, unsigned int pin,
                             unsigned long *config)
 {
        struct meson_pinctrl *pc = pinctrl_dev_get_drvdata(pcdev);
        enum pin_config_param param = pinconf_to_config_param(*config);
        u16 arg;
+       int ret;
 
        switch (param) {
        case PIN_CONFIG_BIAS_DISABLE:
@@ -285,6 +483,29 @@ static int meson_pinconf_get(struct pinctrl_dev *pcdev, unsigned int pin,
                else
                        return -EINVAL;
                break;
+       case PIN_CONFIG_DRIVE_STRENGTH_UA:
+               ret = meson_pinconf_get_drive_strength(pc, pin, &arg);
+               if (ret)
+                       return ret;
+               break;
+       case PIN_CONFIG_OUTPUT_ENABLE:
+               ret = meson_pinconf_get_output(pc, pin);
+               if (ret <= 0)
+                       return -EINVAL;
+               arg = 1;
+               break;
+       case PIN_CONFIG_OUTPUT:
+               ret = meson_pinconf_get_output(pc, pin);
+               if (ret <= 0)
+                       return -EINVAL;
+
+               ret = meson_pinconf_get_drive(pc, pin);
+               if (ret < 0)
+                       return -EINVAL;
+
+               arg = ret;
+               break;
+
        default:
                return -ENOTSUPP;
        }
@@ -329,56 +550,19 @@ static const struct pinconf_ops meson_pinconf_ops = {
 
 static int meson_gpio_direction_input(struct gpio_chip *chip, unsigned gpio)
 {
-       struct meson_pinctrl *pc = gpiochip_get_data(chip);
-       unsigned int reg, bit;
-       struct meson_bank *bank;
-       int ret;
-
-       ret = meson_get_bank(pc, gpio, &bank);
-       if (ret)
-               return ret;
-
-       meson_calc_reg_and_bit(bank, gpio, REG_DIR, &reg, &bit);
-
-       return regmap_update_bits(pc->reg_gpio, reg, BIT(bit), BIT(bit));
+       return meson_pinconf_set_output(gpiochip_get_data(chip), gpio, false);
 }
 
 static int meson_gpio_direction_output(struct gpio_chip *chip, unsigned gpio,
                                       int value)
 {
-       struct meson_pinctrl *pc = gpiochip_get_data(chip);
-       unsigned int reg, bit;
-       struct meson_bank *bank;
-       int ret;
-
-       ret = meson_get_bank(pc, gpio, &bank);
-       if (ret)
-               return ret;
-
-       meson_calc_reg_and_bit(bank, gpio, REG_DIR, &reg, &bit);
-       ret = regmap_update_bits(pc->reg_gpio, reg, BIT(bit), 0);
-       if (ret)
-               return ret;
-
-       meson_calc_reg_and_bit(bank, gpio, REG_OUT, &reg, &bit);
-       return regmap_update_bits(pc->reg_gpio, reg, BIT(bit),
-                                 value ? BIT(bit) : 0);
+       return meson_pinconf_set_output_drive(gpiochip_get_data(chip),
+                                             gpio, value);
 }
 
 static void meson_gpio_set(struct gpio_chip *chip, unsigned gpio, int value)
 {
-       struct meson_pinctrl *pc = gpiochip_get_data(chip);
-       unsigned int reg, bit;
-       struct meson_bank *bank;
-       int ret;
-
-       ret = meson_get_bank(pc, gpio, &bank);
-       if (ret)
-               return;
-
-       meson_calc_reg_and_bit(bank, gpio, REG_OUT, &reg, &bit);
-       regmap_update_bits(pc->reg_gpio, reg, BIT(bit),
-                          value ? BIT(bit) : 0);
+       meson_pinconf_set_drive(gpiochip_get_data(chip), gpio, value);
 }
 
 static int meson_gpio_get(struct gpio_chip *chip, unsigned gpio)