pinctrl: rockchip: add drive-strength control for rk3288
authorHeiko Stübner <heiko@sntech.de>
Sat, 19 Jul 2014 23:50:11 +0000 (01:50 +0200)
committerLinus Walleij <linus.walleij@linaro.org>
Wed, 23 Jul 2014 14:41:16 +0000 (16:41 +0200)
The rk3288 is the first Rockchip soc handling the drive strength on a per-pin
basis, while the older ones can set the drive-strength only for specific
pin-groups. Therefore limit setting the drive-strength to this soc for now.

Signed-off-by: Heiko Stübner <heiko@sntech.de>
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
drivers/pinctrl/pinctrl-rockchip.c

index 3e239c75f52c4cb150fc24e644bc86d8e38aa7bc..5e8b2e04cd7a322e6aefa85adbd3996ad04cc19e 100644 (file)
@@ -573,6 +573,98 @@ static void rk3288_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
        }
 }
 
+#define RK3288_DRV_PMU_OFFSET          0x70
+#define RK3288_DRV_GRF_OFFSET          0x1c0
+#define RK3288_DRV_BITS_PER_PIN                2
+#define RK3288_DRV_PINS_PER_REG                8
+#define RK3288_DRV_BANK_STRIDE         16
+static int rk3288_drv_list[] = { 2, 4, 8, 12 };
+
+static void rk3288_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank,
+                                   int pin_num, struct regmap **regmap,
+                                   int *reg, u8 *bit)
+{
+       struct rockchip_pinctrl *info = bank->drvdata;
+
+       /* The first 24 pins of the first bank are located in PMU */
+       if (bank->bank_num == 0) {
+               *regmap = info->regmap_pmu;
+               *reg = RK3288_DRV_PMU_OFFSET;
+
+               *reg += ((pin_num / RK3288_DRV_PINS_PER_REG) * 4);
+               *bit = pin_num % RK3288_DRV_PINS_PER_REG;
+               *bit *= RK3288_DRV_BITS_PER_PIN;
+       } else {
+               *regmap = info->regmap_base;
+               *reg = RK3288_DRV_GRF_OFFSET;
+
+               /* correct the offset, as we're starting with the 2nd bank */
+               *reg -= 0x10;
+               *reg += bank->bank_num * RK3288_DRV_BANK_STRIDE;
+               *reg += ((pin_num / RK3288_DRV_PINS_PER_REG) * 4);
+
+               *bit = (pin_num % RK3288_DRV_PINS_PER_REG);
+               *bit *= RK3288_DRV_BITS_PER_PIN;
+       }
+}
+
+static int rk3288_get_drive(struct rockchip_pin_bank *bank, int pin_num)
+{
+       struct regmap *regmap;
+       int reg, ret;
+       u32 data;
+       u8 bit;
+
+       rk3288_calc_drv_reg_and_bit(bank, pin_num, &regmap, &reg, &bit);
+
+       ret = regmap_read(regmap, reg, &data);
+       if (ret)
+               return ret;
+
+       data >>= bit;
+       data &= (1 << RK3288_DRV_BITS_PER_PIN) - 1;
+
+       return rk3288_drv_list[data];
+}
+
+static int rk3288_set_drive(struct rockchip_pin_bank *bank, int pin_num,
+                           int strength)
+{
+       struct rockchip_pinctrl *info = bank->drvdata;
+       struct regmap *regmap;
+       unsigned long flags;
+       int reg, ret, i;
+       u32 data;
+       u8 bit;
+
+       rk3288_calc_drv_reg_and_bit(bank, pin_num, &regmap, &reg, &bit);
+
+       ret = -EINVAL;
+       for (i = 0; i < ARRAY_SIZE(rk3288_drv_list); i++) {
+               if (rk3288_drv_list[i] == strength) {
+                       ret = i;
+                       break;
+               }
+       }
+
+       if (ret < 0) {
+               dev_err(info->dev, "unsupported driver strength %d\n",
+                       strength);
+               return ret;
+       }
+
+       spin_lock_irqsave(&bank->slock, flags);
+
+       /* enable the write to the equivalent lower bits */
+       data = ((1 << RK3288_DRV_BITS_PER_PIN) - 1) << (bit + 16);
+       data |= (ret << bit);
+
+       ret = regmap_write(regmap, reg, data);
+       spin_unlock_irqrestore(&bank->slock, flags);
+
+       return ret;
+}
+
 static int rockchip_get_pull(struct rockchip_pin_bank *bank, int pin_num)
 {
        struct rockchip_pinctrl *info = bank->drvdata;
@@ -870,6 +962,15 @@ static int rockchip_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
                        if (rc)
                                return rc;
                        break;
+               case PIN_CONFIG_DRIVE_STRENGTH:
+                       /* rk3288 is the first with per-pin drive-strength */
+                       if (info->ctrl->type != RK3288)
+                               return -ENOTSUPP;
+
+                       rc = rk3288_set_drive(bank, pin - bank->pin_base, arg);
+                       if (rc < 0)
+                               return rc;
+                       break;
                default:
                        return -ENOTSUPP;
                        break;
@@ -919,6 +1020,17 @@ static int rockchip_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin,
 
                arg = rc ? 1 : 0;
                break;
+       case PIN_CONFIG_DRIVE_STRENGTH:
+               /* rk3288 is the first with per-pin drive-strength */
+               if (info->ctrl->type != RK3288)
+                       return -ENOTSUPP;
+
+               rc = rk3288_get_drive(bank, pin - bank->pin_base);
+               if (rc < 0)
+                       return rc;
+
+               arg = rc;
+               break;
        default:
                return -ENOTSUPP;
                break;