X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=drivers%2Fpinctrl%2Fpinctrl-rockchip.c;h=c6c04aca892f2c1529a848625a7f765fbd608ab9;hb=17f3b4e9bb831d79922a986d62c058f175b42e32;hp=a0651128e23acc68fbe139dc10da3e859da2b9a1;hpb=6545f3189707c9105fcf160c2da2ddd2dacca331;p=firefly-linux-kernel-4.4.55.git diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c index a0651128e23a..c6c04aca892f 100644 --- a/drivers/pinctrl/pinctrl-rockchip.c +++ b/drivers/pinctrl/pinctrl-rockchip.c @@ -63,7 +63,9 @@ enum rockchip_pinctrl_type { RK3066B, RK3188, RK3288, + RK3366, RK3368, + RK3399, }; /** @@ -85,6 +87,51 @@ struct rockchip_iomux { int offset; }; +/** + * enum type index corresponding to rockchip_perpin_drv_list arrays index. + */ +enum rockchip_pin_drv_type { + DRV_TYPE_IO_DEFAULT = 0, + DRV_TYPE_IO_1V8_OR_3V0, + DRV_TYPE_IO_1V8_ONLY, + DRV_TYPE_IO_1V8_3V0_AUTO, + DRV_TYPE_IO_3V3_ONLY, + DRV_TYPE_IO_WIDE_LEVEL, + DRV_TYPE_IO_NARROW_LEVEL, + DRV_TYPE_MAX +}; + +/** + * enum type index corresponding to rockchip_pull_list arrays index. + */ +enum rockchip_pin_pull_type { + PULL_TYPE_IO_DEFAULT = 0, + PULL_TYPE_IO_1V8_ONLY, + PULL_TYPE_MAX +}; + +/** + * enum type of pin extra drive alignment. + */ +enum rockchip_pin_extra_drv_type { + DRV_TYPE_EXTRA_DEFAULT = 0, + DRV_TYPE_EXTRA_SAME_OFFSET, + DRV_TYPE_EXTRA_SAME_BITS +}; + +/** + * @drv_type: drive strength variant using rockchip_pin_drv_type + * @offset: if initialized to -1 it will be autocalculated, by specifying + * an initial offset value the relevant source offset can be reset + * to a new value for autocalculating the following drive strength + * registers. if used chips own cal_drv func instead to calculate + * registers offset, the variant could be ignored. + */ +struct rockchip_drv { + enum rockchip_pin_drv_type drv_type; + int offset; +}; + /** * @reg_base: register base of the gpio bank * @reg_pull: optional separate register for additional pull settings @@ -96,6 +143,8 @@ struct rockchip_iomux { * @name: name of the bank * @bank_num: number of the bank, to account for holes * @iomux: array describing the 4 iomux sources of the bank + * @drv: array describing the 4 drive strength sources of the bank + * @pull_type: array describing the 4 pull type sources of the bank * @valid: are all necessary informations present * @of_node: dt node of this bank * @drvdata: common pinctrl basedata @@ -115,6 +164,8 @@ struct rockchip_pin_bank { char *name; u8 bank_num; struct rockchip_iomux iomux[4]; + struct rockchip_drv drv[4]; + enum rockchip_pin_pull_type pull_type[4]; bool valid; struct device_node *of_node; struct rockchip_pinctrl *drvdata; @@ -151,6 +202,141 @@ struct rockchip_pin_bank { }, \ } +#define PIN_BANK_DRV_FLAGS(id, pins, label, type0, type1, type2, type3) \ + { \ + .bank_num = id, \ + .nr_pins = pins, \ + .name = label, \ + .iomux = { \ + { .offset = -1 }, \ + { .offset = -1 }, \ + { .offset = -1 }, \ + { .offset = -1 }, \ + }, \ + .drv = { \ + { .drv_type = type0, .offset = -1 }, \ + { .drv_type = type1, .offset = -1 }, \ + { .drv_type = type2, .offset = -1 }, \ + { .drv_type = type3, .offset = -1 }, \ + }, \ + } + +#define PIN_BANK_DRV_FLAGS_PULL_FLAGS(id, pins, label, drv0, drv1, drv2,\ + drv3, pull0, pull1, pull2, pull3) \ + { \ + .bank_num = id, \ + .nr_pins = pins, \ + .name = label, \ + .iomux = { \ + { .offset = -1 }, \ + { .offset = -1 }, \ + { .offset = -1 }, \ + { .offset = -1 }, \ + }, \ + .drv = { \ + { .drv_type = drv0, .offset = -1 }, \ + { .drv_type = drv1, .offset = -1 }, \ + { .drv_type = drv2, .offset = -1 }, \ + { .drv_type = drv3, .offset = -1 }, \ + }, \ + .pull_type[0] = pull0, \ + .pull_type[1] = pull1, \ + .pull_type[2] = pull2, \ + .pull_type[3] = pull3, \ + } + +#define PIN_BANK_IOMUX_DRV_FLAGS(id, pins, label, iom0, iom1, iom2, \ + iom3, drv0, drv1, drv2, drv3) \ + { \ + .bank_num = id, \ + .nr_pins = pins, \ + .name = label, \ + .iomux = { \ + { .type = iom0, .offset = -1 }, \ + { .type = iom1, .offset = -1 }, \ + { .type = iom2, .offset = -1 }, \ + { .type = iom3, .offset = -1 }, \ + }, \ + .drv = { \ + { .drv_type = drv0, .offset = -1 }, \ + { .drv_type = drv1, .offset = -1 }, \ + { .drv_type = drv2, .offset = -1 }, \ + { .drv_type = drv3, .offset = -1 }, \ + }, \ + } + +#define PIN_BANK_IOMUX_FLAGS_OFFSET_DRV_FLAGS(id, pins, label, iom0, \ + iom1, iom2, iom3, offset0, \ + offset1, offset2, offset3, \ + drv0, drv1, drv2, drv3) \ + { \ + .bank_num = id, \ + .nr_pins = pins, \ + .name = label, \ + .iomux = { \ + { .type = iom0, .offset = offset0 }, \ + { .type = iom1, .offset = offset1 }, \ + { .type = iom2, .offset = offset2 }, \ + { .type = iom3, .offset = offset3 }, \ + }, \ + .drv = { \ + { .drv_type = drv0, .offset = -1 }, \ + { .drv_type = drv1, .offset = -1 }, \ + { .drv_type = drv2, .offset = -1 }, \ + { .drv_type = drv3, .offset = -1 }, \ + }, \ + } + +#define PIN_BANK_IOMUX_FLAGS_DRV_FLAGS_OFFSET(id, pins, label, iom0, \ + iom1, iom2, iom3, drv0, \ + drv1, drv2, drv3, offset0,\ + offset1, offset2, offset3)\ + { \ + .bank_num = id, \ + .nr_pins = pins, \ + .name = label, \ + .iomux = { \ + { .type = iom0, .offset = -1 }, \ + { .type = iom1, .offset = -1 }, \ + { .type = iom2, .offset = -1 }, \ + { .type = iom3, .offset = -1 }, \ + }, \ + .drv = { \ + { .drv_type = drv0, .offset = offset0 }, \ + { .drv_type = drv1, .offset = offset1 }, \ + { .drv_type = drv2, .offset = offset2 }, \ + { .drv_type = drv3, .offset = offset3 }, \ + }, \ + } + +#define PIN_BANK_IOMUX_FLAGS_DRV_FLAGS_OFFSET_PULL_FLAGS(id, pins, \ + label, iom0, iom1, iom2, \ + iom3, drv0, drv1, drv2, \ + drv3, offset0, offset1, \ + offset2, offset3, pull0, \ + pull1, pull2, pull3) \ + { \ + .bank_num = id, \ + .nr_pins = pins, \ + .name = label, \ + .iomux = { \ + { .type = iom0, .offset = -1 }, \ + { .type = iom1, .offset = -1 }, \ + { .type = iom2, .offset = -1 }, \ + { .type = iom3, .offset = -1 }, \ + }, \ + .drv = { \ + { .drv_type = drv0, .offset = offset0 }, \ + { .drv_type = drv1, .offset = offset1 }, \ + { .drv_type = drv2, .offset = offset2 }, \ + { .drv_type = drv3, .offset = offset3 }, \ + }, \ + .pull_type[0] = pull0, \ + .pull_type[1] = pull1, \ + .pull_type[2] = pull2, \ + .pull_type[3] = pull3, \ + } + /** */ struct rockchip_pin_ctrl { @@ -161,12 +347,20 @@ struct rockchip_pin_ctrl { enum rockchip_pinctrl_type type; int grf_mux_offset; int pmu_mux_offset; + int grf_drv_offset; + int pmu_drv_offset; + void (*pull_calc_reg)(struct rockchip_pin_bank *bank, - int pin_num, struct regmap **regmap, - int *reg, u8 *bit); - void (*drv_calc_reg)(struct rockchip_pin_bank *bank, - int pin_num, struct regmap **regmap, - int *reg, u8 *bit); + int pin_num, struct regmap **regmap, + int *reg, u8 *bit); + enum rockchip_pin_drv_type (*drv_calc_reg)( + struct rockchip_pin_bank *bank, + int pin_num, struct regmap **regmap, + int *reg, u8 *bit); + enum rockchip_pin_extra_drv_type (*drv_calc_extra_reg)( + struct rockchip_pin_bank *bank, + int pin_num, struct regmap **regmap, + int *reg, u8 *bit); }; struct rockchip_pin_config { @@ -586,9 +780,10 @@ static void rk3288_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, #define RK3288_DRV_PINS_PER_REG 8 #define RK3288_DRV_BANK_STRIDE 16 -static void rk3288_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank, - int pin_num, struct regmap **regmap, - int *reg, u8 *bit) +static enum rockchip_pin_drv_type 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; @@ -612,14 +807,222 @@ static void rk3288_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank, *bit = (pin_num % RK3288_DRV_PINS_PER_REG); *bit *= RK3288_DRV_BITS_PER_PIN; } + + return DRV_TYPE_IO_DEFAULT; +} + +#define RK3228_PULL_OFFSET 0x100 + +static void rk3228_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, + int pin_num, struct regmap **regmap, + int *reg, u8 *bit) +{ + struct rockchip_pinctrl *info = bank->drvdata; + + *regmap = info->regmap_base; + *reg = RK3228_PULL_OFFSET; + *reg += bank->bank_num * RK3188_PULL_BANK_STRIDE; + *reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4); + + *bit = (pin_num % RK3188_PULL_PINS_PER_REG); + *bit *= RK3188_PULL_BITS_PER_PIN; +} + +#define RK3228_DRV_GRF_OFFSET 0x200 + +static enum rockchip_pin_drv_type rk3228_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; + + *regmap = info->regmap_base; + *reg = RK3228_DRV_GRF_OFFSET; + *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; + + return DRV_TYPE_IO_DEFAULT; +} + +#define RK3366_PULL_GRF_OFFSET 0x110 +#define RK3366_PULL_PMU_OFFSET 0x10 + +static void rk3366_calc_pull_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 bank0:32 and bank1:16 pins are located in PMU */ + if ((bank->bank_num == 0) || (bank->bank_num == 1)) { + *regmap = info->regmap_pmu; + *reg = RK3366_PULL_PMU_OFFSET + bank->bank_num * 0x30; + + *reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4); + *bit = pin_num % RK3188_PULL_PINS_PER_REG; + *bit *= RK3188_PULL_BITS_PER_PIN; + } else { + *regmap = info->regmap_base; + *reg = RK3366_PULL_GRF_OFFSET; + + /* correct the offset, as we're starting with the 2nd bank */ + *reg -= 0x20; + *reg += bank->bank_num * RK3188_PULL_BANK_STRIDE; + *reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4); + + *bit = (pin_num % RK3188_PULL_PINS_PER_REG); + *bit *= RK3188_PULL_BITS_PER_PIN; + } +} + +#define RK3366_DRV_PMU_OFFSET 0x20 +#define RK3366_DRV_GRF_OFFSET 0x210 + +#define RK3366_DRV_GPIO2B3_OFFSET 0x378 +#define RK3366_DRV_GPIO2B3_BITS 4 + +#define RK3366_DRV_GPIO3A4_OFFSET 0x37c +#define RK3366_DRV_GPIO3A4_BITS 4 + +static enum rockchip_pin_drv_type rk3366_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 bank0:32 and bank1:16 pins are located in PMU */ + if ((bank->bank_num == 0) || (bank->bank_num == 1)) { + *regmap = info->regmap_pmu; + *reg = RK3366_DRV_PMU_OFFSET + bank->bank_num * 0x30; + + *reg += ((pin_num / RK3288_DRV_PINS_PER_REG) * 4); + *bit = pin_num % RK3288_DRV_PINS_PER_REG; + *bit *= RK3288_DRV_BITS_PER_PIN; + + return DRV_TYPE_IO_DEFAULT; + } else if ((bank->bank_num == 2) && (pin_num == 11)) { + /* GPIO2B3 is a special case in bank2 */ + *regmap = info->regmap_base; + *reg = RK3366_DRV_GPIO2B3_OFFSET; + *bit = RK3366_DRV_GPIO2B3_BITS; + + return DRV_TYPE_IO_WIDE_LEVEL; + } else if ((bank->bank_num == 3) && (pin_num == 4)) { + /* GPIO3A4 is a special case in bank3 */ + *regmap = info->regmap_base; + *reg = RK3366_DRV_GPIO3A4_OFFSET; + *bit = RK3366_DRV_GPIO3A4_BITS; + + return DRV_TYPE_IO_WIDE_LEVEL; + } + + *regmap = info->regmap_base; + *reg = RK3366_DRV_GRF_OFFSET; + + /* correct the offset, as we're starting with the 2nd bank */ + *reg -= 0x20; + *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; + + /* special cases need special handle */ + if ((bank->bank_num == 2) && (pin_num == 14)) + return DRV_TYPE_IO_WIDE_LEVEL; + else if ((bank->bank_num == 2) && (pin_num == 16)) + return DRV_TYPE_IO_NARROW_LEVEL; + else if ((bank->bank_num == 2) && (pin_num >= 24) && (pin_num <= 26)) + return DRV_TYPE_IO_WIDE_LEVEL; + + return DRV_TYPE_IO_DEFAULT; +} + +#define RK3366_DRV_GPIO2A_EN_OFFSET 0x360 +#define RK3366_DRV_GPIO2A_EP_OFFSET 0x364 + +#define RK3366_DRV_GPIO2C_EN_OFFSET 0x368 +#define RK3366_DRV_GPIO2C_EP_OFFSET 0x36C + +#define RK3366_DRV_GPIO2D_EN_OFFSET 0x370 +#define RK3366_DRV_GPIO2D_EP_OFFSET 0x374 + +#define RK3366_DRV_GPIO2B3_E_OFFSET 0x378 +#define RK3366_DRV_GPIO2B3_EN_BIT 0 +#define RK3366_DRV_GPIO2B3_EP_BIT 2 + +#define RK3366_DRV_GPIO3A4_E_OFFSET 0x37c +#define RK3366_DRV_GPIO3A4_EN_BIT 0 +#define RK3366_DRV_GPIO3A4_EP_BIT 2 + +#define RK3366_DRV_GPIO2B6_E_OFFSET 0x404 +#define RK3366_DRV_GPIO2B6_EN_BIT 12 +#define RK3366_DRV_GPIO2B6_EP_BIT 14 + +static enum rockchip_pin_extra_drv_type rk3366_calc_drv_extra_reg_and_bit( + struct rockchip_pin_bank *bank, + int pin_num, + struct regmap **regmap, + int *reg, u8 *bit) +{ + struct rockchip_pinctrl *info = bank->drvdata; + + *regmap = info->regmap_base; + if (bank->bank_num == 2) { + switch (pin_num / 8) { + case 0: + *reg = RK3366_DRV_GPIO2A_EN_OFFSET; + break; + case 1: + /* special cases need special handle */ + if (pin_num == 11) { + *reg = RK3366_DRV_GPIO2B3_E_OFFSET; + *bit = RK3366_DRV_GPIO2B3_EN_BIT; + } else if (pin_num == 14) { + *reg = RK3366_DRV_GPIO2B6_E_OFFSET; + *bit = RK3366_DRV_GPIO2B6_EN_BIT; + } else { + return -1; + } + + return DRV_TYPE_EXTRA_SAME_OFFSET; + case 2: + *reg = RK3366_DRV_GPIO2C_EN_OFFSET; + break; + case 3: + *reg = RK3366_DRV_GPIO2D_EN_OFFSET; + break; + default: + return -1; + } + + *bit = pin_num % RK3288_DRV_PINS_PER_REG; + *bit *= RK3288_DRV_BITS_PER_PIN; + + return DRV_TYPE_EXTRA_SAME_BITS; + } + + /* GPIO3A4 is a special case */ + if ((pin_num != 4) && (bank->bank_num != 3)) + return -1; + + *reg = RK3366_DRV_GPIO3A4_E_OFFSET; + *bit = RK3366_DRV_GPIO3A4_EN_BIT; + + return DRV_TYPE_EXTRA_SAME_OFFSET; } #define RK3368_PULL_GRF_OFFSET 0x100 #define RK3368_PULL_PMU_OFFSET 0x10 static void rk3368_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, - int pin_num, struct regmap **regmap, - int *reg, u8 *bit) + int pin_num, struct regmap **regmap, + int *reg, u8 *bit) { struct rockchip_pinctrl *info = bank->drvdata; @@ -648,9 +1051,10 @@ static void rk3368_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, #define RK3368_DRV_PMU_OFFSET 0x20 #define RK3368_DRV_GRF_OFFSET 0x200 -static void rk3368_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank, - int pin_num, struct regmap **regmap, - int *reg, u8 *bit) +static enum rockchip_pin_drv_type rk3368_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; @@ -674,30 +1078,187 @@ static void rk3368_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank, *bit = (pin_num % RK3288_DRV_PINS_PER_REG); *bit *= RK3288_DRV_BITS_PER_PIN; } + + return DRV_TYPE_IO_DEFAULT; } -static int rockchip_perpin_drv_list[] = { 2, 4, 8, 12 }; +#define RK3399_PULL_GRF_OFFSET 0xe040 +#define RK3399_PULL_PMU_OFFSET 0x40 +#define RK3399_DRV_3BITS_PER_PIN 3 + +static void rk3399_calc_pull_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 bank0:16 and bank1:32 pins are located in PMU */ + if ((bank->bank_num == 0) || (bank->bank_num == 1)) { + *regmap = info->regmap_pmu; + *reg = RK3399_PULL_PMU_OFFSET; + + *reg += bank->bank_num * RK3188_PULL_BANK_STRIDE; + + *reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4); + *bit = pin_num % RK3188_PULL_PINS_PER_REG; + *bit *= RK3188_PULL_BITS_PER_PIN; + } else { + *regmap = info->regmap_base; + *reg = RK3399_PULL_GRF_OFFSET; + + /* correct the offset, as we're starting with the 3rd bank */ + *reg -= 0x20; + *reg += bank->bank_num * RK3188_PULL_BANK_STRIDE; + *reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4); + + *bit = (pin_num % RK3188_PULL_PINS_PER_REG); + *bit *= RK3188_PULL_BITS_PER_PIN; + } +} + +static enum rockchip_pin_drv_type rk3399_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; + int drv_num = (pin_num / 8); + + /* The bank0:16 and bank1:32 pins are located in PMU */ + if ((bank->bank_num == 0) || (bank->bank_num == 1)) + *regmap = info->regmap_pmu; + else + *regmap = info->regmap_base; + + *reg = bank->drv[drv_num].offset; + if ((bank->drv[drv_num].drv_type == DRV_TYPE_IO_1V8_3V0_AUTO) || + (bank->drv[drv_num].drv_type == DRV_TYPE_IO_3V3_ONLY)) + *bit = (pin_num % 8) * 3; + else + *bit = (pin_num % 8) * 2; + + return DRV_TYPE_IO_DEFAULT; +} + +static int rockchip_perpin_drv_list[DRV_TYPE_MAX][8] = { + { 2, 4, 8, 12, -1, -1, -1, -1 }, + { 3, 6, 9, 12, -1, -1, -1, -1 }, + { 5, 10, 15, 20, -1, -1, -1, -1 }, + { 4, 6, 8, 10, 12, 14, 16, 18 }, + { 4, 7, 10, 13, 16, 19, 22, 26 }, + { 0, 6, 12, 18, -1, -1, -1, -1 }, + { 4, 8, 12, 16, -1, -1, -1, -1 } +}; static int rockchip_get_drive_perpin(struct rockchip_pin_bank *bank, int pin_num) { struct rockchip_pinctrl *info = bank->drvdata; struct rockchip_pin_ctrl *ctrl = info->ctrl; - struct regmap *regmap; - int reg, ret; - u32 data; - u8 bit; + struct regmap *regmap, *extra_regmap; + int reg, ret, extra_reg; + u32 data, temp, rmask_bits; + u8 bit, extra_bit; + int drv_type; + + drv_type = ctrl->drv_calc_reg(bank, pin_num, ®map, ®, &bit); + if (!drv_type) + drv_type = bank->drv[pin_num / 8].drv_type; + + switch (drv_type) { + case DRV_TYPE_IO_1V8_3V0_AUTO: + case DRV_TYPE_IO_3V3_ONLY: + rmask_bits = RK3399_DRV_3BITS_PER_PIN; + switch (bit) { + case 0 ... 12: + /* regular case, nothing to do */ + break; + case 15: + /* + * drive-strength offset is special, as it is + * spread over 2 registers + */ + ret = regmap_read(regmap, reg, &data); + if (ret) + return ret; - ctrl->drv_calc_reg(bank, pin_num, ®map, ®, &bit); + ret = regmap_read(regmap, reg + 0x4, &temp); + if (ret) + return ret; + + /* + * the bit data[15] contains bit 0 of the value + * while temp[1:0] contains bits 2 and 1 + */ + data >>= 15; + temp &= 0x3; + temp <<= 1; + data |= temp; + + return rockchip_perpin_drv_list[drv_type][data]; + case 18 ... 21: + /* setting fully enclosed in the second register */ + reg += 4; + bit -= 16; + break; + default: + dev_err(info->dev, "unsupported bit: %d for pinctrl drive type: %d\n", + bit, drv_type); + return -EINVAL; + } + + break; + case DRV_TYPE_IO_WIDE_LEVEL: + rmask_bits = RK3288_DRV_BITS_PER_PIN; + /* enable the write to the equivalent lower bits */ + ret = regmap_read(regmap, reg, &data); + if (ret) + return ret; + data >>= bit; + data &= (1 << rmask_bits) - 1; + + /* + * assume the drive strength of N channel and + * P channel are the same. + */ + if (ctrl->drv_calc_extra_reg) + ctrl->drv_calc_extra_reg(bank, pin_num, + &extra_regmap, + &extra_reg, + &extra_bit); + + /* + * It is enough to read one channel drive strength, + * this is N channel. + */ + ret = regmap_read(extra_regmap, extra_reg, &temp); + if (ret) + return ret; + + temp >>= extra_bit; + temp &= (1 << rmask_bits) - 1; + + return (rockchip_perpin_drv_list[drv_type][data]) + (temp * 2); + case DRV_TYPE_IO_DEFAULT: + case DRV_TYPE_IO_1V8_OR_3V0: + case DRV_TYPE_IO_1V8_ONLY: + case DRV_TYPE_IO_NARROW_LEVEL: + rmask_bits = RK3288_DRV_BITS_PER_PIN; + break; + default: + dev_err(info->dev, "unsupported pinctrl drive type: %d\n", + drv_type); + return -EINVAL; + } ret = regmap_read(regmap, reg, &data); if (ret) return ret; data >>= bit; - data &= (1 << RK3288_DRV_BITS_PER_PIN) - 1; + data &= (1 << rmask_bits) - 1; - return rockchip_perpin_drv_list[data]; + return rockchip_perpin_drv_list[drv_type][data]; } static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank, @@ -705,19 +1266,37 @@ static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank, { struct rockchip_pinctrl *info = bank->drvdata; struct rockchip_pin_ctrl *ctrl = info->ctrl; - struct regmap *regmap; + struct regmap *regmap, *extra_regmap; unsigned long flags; int reg, ret, i; - u32 data, rmask; - u8 bit; + u32 data, temp, rmask, rmask_bits; + u8 bit, extra_bit; + int drv_type, extra_drv_type = 0; + int extra_value, extra_reg; + + dev_dbg(info->dev, "setting drive of GPIO%d-%d to %d\n", + bank->bank_num, pin_num, strength); - ctrl->drv_calc_reg(bank, pin_num, ®map, ®, &bit); + drv_type = ctrl->drv_calc_reg(bank, pin_num, ®map, ®, &bit); + if (!drv_type) + drv_type = bank->drv[pin_num / 8].drv_type; ret = -EINVAL; - for (i = 0; i < ARRAY_SIZE(rockchip_perpin_drv_list); i++) { - if (rockchip_perpin_drv_list[i] == strength) { - ret = i; - break; + + if (drv_type == DRV_TYPE_IO_WIDE_LEVEL) { + if ((strength % 2 == 0) && (strength <= 24)) + ret = ((strength > 18) ? 18 : strength) / 6; + } else { + for (i = 0; i < ARRAY_SIZE(rockchip_perpin_drv_list[drv_type]); + i++) { + if (rockchip_perpin_drv_list[drv_type][i] < 0) { + ret = rockchip_perpin_drv_list[drv_type][i]; + break; + } else if (rockchip_perpin_drv_list[drv_type][i] == + strength) { + ret = i; + break; + } } } @@ -729,8 +1308,106 @@ static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank, spin_lock_irqsave(&bank->slock, flags); + switch (drv_type) { + case DRV_TYPE_IO_1V8_3V0_AUTO: + case DRV_TYPE_IO_3V3_ONLY: + rmask_bits = RK3399_DRV_3BITS_PER_PIN; + switch (bit) { + case 0 ... 12: + /* regular case, nothing to do */ + break; + case 15: + /* + * drive-strength offset is special, as it is spread + * over 2 registers, the bit data[15] contains bit 0 + * of the value while temp[1:0] contains bits 2 and 1 + */ + data = (ret & 0x1) << 15; + temp = (ret >> 0x1) & 0x3; + + rmask = BIT(15) | BIT(31); + data |= BIT(31); + ret = regmap_update_bits(regmap, reg, rmask, data); + if (ret) { + spin_unlock_irqrestore(&bank->slock, flags); + return ret; + } + + rmask = 0x3 | (0x3 << 16); + temp |= (0x3 << 16); + reg += 0x4; + ret = regmap_update_bits(regmap, reg, rmask, temp); + + spin_unlock_irqrestore(&bank->slock, flags); + return ret; + case 18 ... 21: + /* setting fully enclosed in the second register */ + reg += 4; + bit -= 16; + break; + default: + spin_unlock_irqrestore(&bank->slock, flags); + dev_err(info->dev, "unsupported bit: %d for pinctrl drive type: %d\n", + bit, drv_type); + return -EINVAL; + } + break; + case DRV_TYPE_IO_WIDE_LEVEL: + extra_value = ((strength - + rockchip_perpin_drv_list[drv_type][ret])) >> 1; + rmask_bits = RK3288_DRV_BITS_PER_PIN; + + /* + * assume the drive strength of N channel and + * P channel are the same. + */ + if (ctrl->drv_calc_extra_reg) + extra_drv_type = ctrl->drv_calc_extra_reg(bank, pin_num, + &extra_regmap, + &extra_reg, + &extra_bit); + + /* enable the write to the equivalent lower bits */ + data = ((1 << rmask_bits) - 1) << (extra_bit + 16); + rmask = data | (data >> 16); + data |= (extra_value << extra_bit); + + /* write drive strength of N channel */ + if (regmap_update_bits(extra_regmap, extra_reg, rmask, data)) + return -EINVAL; + + if (extra_drv_type == DRV_TYPE_EXTRA_SAME_OFFSET) + extra_bit += 2; + else if (extra_drv_type == DRV_TYPE_EXTRA_SAME_BITS) + extra_reg += 0x4; + else + return -EINVAL; + + /* enable the write to the equivalent lower bits */ + data = ((1 << rmask_bits) - 1) << (extra_bit + 16); + rmask = data | (data >> 16); + data |= (extra_value << extra_bit); + + /* write drive strength of P channel */ + if (regmap_update_bits(extra_regmap, extra_reg, rmask, data)) + return -EINVAL; + + break; + case DRV_TYPE_IO_DEFAULT: + case DRV_TYPE_IO_1V8_OR_3V0: + case DRV_TYPE_IO_1V8_ONLY: + case DRV_TYPE_IO_NARROW_LEVEL: + rmask_bits = RK3288_DRV_BITS_PER_PIN; + break; + default: + spin_unlock_irqrestore(&bank->slock, flags); + dev_err(info->dev, "unsupported pinctrl drive type: %d\n", + drv_type); + return -EINVAL; + } + /* enable the write to the equivalent lower bits */ - data = ((1 << RK3288_DRV_BITS_PER_PIN) - 1) << (bit + 16); + data = ((1 << rmask_bits) - 1) << (bit + 16); rmask = data | (data >> 16); data |= (ret << bit); @@ -740,12 +1417,27 @@ static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank, return ret; } +static int rockchip_pull_list[PULL_TYPE_MAX][4] = { + { + PIN_CONFIG_BIAS_DISABLE, + PIN_CONFIG_BIAS_PULL_UP, + PIN_CONFIG_BIAS_PULL_DOWN, + PIN_CONFIG_BIAS_BUS_HOLD + }, + { + PIN_CONFIG_BIAS_DISABLE, + PIN_CONFIG_BIAS_PULL_DOWN, + PIN_CONFIG_BIAS_DISABLE, + PIN_CONFIG_BIAS_PULL_UP + }, +}; + static int rockchip_get_pull(struct rockchip_pin_bank *bank, int pin_num) { struct rockchip_pinctrl *info = bank->drvdata; struct rockchip_pin_ctrl *ctrl = info->ctrl; struct regmap *regmap; - int reg, ret; + int reg, ret, pull_type; u8 bit; u32 data; @@ -766,23 +1458,14 @@ static int rockchip_get_pull(struct rockchip_pin_bank *bank, int pin_num) : PIN_CONFIG_BIAS_DISABLE; case RK3188: case RK3288: + case RK3366: case RK3368: + case RK3399: + pull_type = bank->pull_type[pin_num / 8]; data >>= bit; data &= (1 << RK3188_PULL_BITS_PER_PIN) - 1; - switch (data) { - case 0: - return PIN_CONFIG_BIAS_DISABLE; - case 1: - return PIN_CONFIG_BIAS_PULL_UP; - case 2: - return PIN_CONFIG_BIAS_PULL_DOWN; - case 3: - return PIN_CONFIG_BIAS_BUS_HOLD; - } - - dev_err(info->dev, "unknown pull setting\n"); - return -EIO; + return rockchip_pull_list[pull_type][data]; default: dev_err(info->dev, "unsupported pinctrl type\n"); return -EINVAL; @@ -795,7 +1478,7 @@ static int rockchip_set_pull(struct rockchip_pin_bank *bank, struct rockchip_pinctrl *info = bank->drvdata; struct rockchip_pin_ctrl *ctrl = info->ctrl; struct regmap *regmap; - int reg, ret; + int reg, ret, i, pull_type; unsigned long flags; u8 bit; u32 data, rmask; @@ -822,31 +1505,30 @@ static int rockchip_set_pull(struct rockchip_pin_bank *bank, break; case RK3188: case RK3288: + case RK3366: case RK3368: + case RK3399: + pull_type = bank->pull_type[pin_num / 8]; + ret = -EINVAL; + for (i = 0; i < ARRAY_SIZE(rockchip_pull_list[pull_type]); + i++) { + if (rockchip_pull_list[pull_type][i] == pull) { + ret = i; + break; + } + } + + if (ret < 0) { + dev_err(info->dev, "unknown pull setting %d\n", pull); + return ret; + } + spin_lock_irqsave(&bank->slock, flags); /* enable the write to the equivalent lower bits */ data = ((1 << RK3188_PULL_BITS_PER_PIN) - 1) << (bit + 16); rmask = data | (data >> 16); - - switch (pull) { - case PIN_CONFIG_BIAS_DISABLE: - break; - case PIN_CONFIG_BIAS_PULL_UP: - data |= (1 << bit); - break; - case PIN_CONFIG_BIAS_PULL_DOWN: - data |= (2 << bit); - break; - case PIN_CONFIG_BIAS_BUS_HOLD: - data |= (3 << bit); - break; - default: - spin_unlock_irqrestore(&bank->slock, flags); - dev_err(info->dev, "unsupported pull setting %d\n", - pull); - return -EINVAL; - } + data |= (ret << bit); ret = regmap_update_bits(regmap, reg, rmask, data); @@ -1002,7 +1684,9 @@ static bool rockchip_pinconf_pull_valid(struct rockchip_pin_ctrl *ctrl, return pull ? false : true; case RK3188: case RK3288: + case RK3366: case RK3368: + case RK3399: return (pull != PIN_CONFIG_BIAS_PULL_PIN_DEFAULT); } @@ -1258,8 +1942,10 @@ static int rockchip_pinctrl_parse_functions(struct device_node *np, func->groups[i] = child->name; grp = &info->groups[grp_index++]; ret = rockchip_pinctrl_parse_groups(child, grp, info, i++); - if (ret) + if (ret) { + of_node_put(child); return ret; + } } return 0; @@ -1304,6 +1990,7 @@ static int rockchip_pinctrl_parse_dt(struct platform_device *pdev, ret = rockchip_pinctrl_parse_functions(child, info, i++); if (ret) { dev_err(&pdev->dev, "failed to parse function\n"); + of_node_put(child); return ret; } } @@ -1860,7 +2547,7 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data( struct device_node *np; struct rockchip_pin_ctrl *ctrl; struct rockchip_pin_bank *bank; - int grf_offs, pmu_offs, i, j; + int grf_offs, pmu_offs, drv_grf_offs, drv_pmu_offs, i, j; match = of_match_node(rockchip_pinctrl_dt_match, node); ctrl = (struct rockchip_pin_ctrl *)match->data; @@ -1884,6 +2571,8 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data( grf_offs = ctrl->grf_mux_offset; pmu_offs = ctrl->pmu_mux_offset; + drv_pmu_offs = ctrl->pmu_drv_offset; + drv_grf_offs = ctrl->grf_drv_offset; bank = ctrl->pin_banks; for (i = 0; i < ctrl->nr_banks; ++i, ++bank) { int bank_pins = 0; @@ -1893,27 +2582,39 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data( bank->pin_base = ctrl->nr_pins; ctrl->nr_pins += bank->nr_pins; - /* calculate iomux offsets */ + /* calculate iomux and drv offsets */ for (j = 0; j < 4; j++) { struct rockchip_iomux *iom = &bank->iomux[j]; + struct rockchip_drv *drv = &bank->drv[j]; int inc; if (bank_pins >= bank->nr_pins) break; - /* preset offset value, set new start value */ + /* preset iomux offset value, set new start value */ if (iom->offset >= 0) { if (iom->type & IOMUX_SOURCE_PMU) pmu_offs = iom->offset; else grf_offs = iom->offset; - } else { /* set current offset */ + } else { /* set current iomux offset */ iom->offset = (iom->type & IOMUX_SOURCE_PMU) ? pmu_offs : grf_offs; } - dev_dbg(d->dev, "bank %d, iomux %d has offset 0x%x\n", - i, j, iom->offset); + /* preset drv offset value, set new start value */ + if (drv->offset >= 0) { + if (iom->type & IOMUX_SOURCE_PMU) + drv_pmu_offs = drv->offset; + else + drv_grf_offs = drv->offset; + } else { /* set current drv offset */ + drv->offset = (iom->type & IOMUX_SOURCE_PMU) ? + drv_pmu_offs : drv_grf_offs; + } + + dev_dbg(d->dev, "bank %d, iomux %d has iom_offset 0x%x drv_offset 0x%x\n", + i, j, iom->offset, drv->offset); /* * Increase offset according to iomux width. @@ -1925,6 +2626,21 @@ static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data( else grf_offs += inc; + /* + * Increase offset according to drv width. + * 3bit drive-strenth'es are spread over two registers. + */ + if ((drv->drv_type == DRV_TYPE_IO_1V8_3V0_AUTO) || + (drv->drv_type == DRV_TYPE_IO_3V3_ONLY)) + inc = 8; + else + inc = 4; + + if (iom->type & IOMUX_SOURCE_PMU) + drv_pmu_offs += inc; + else + drv_grf_offs += inc; + bank_pins += 8; } } @@ -2143,6 +2859,23 @@ static struct rockchip_pin_ctrl rk3188_pin_ctrl = { .pull_calc_reg = rk3188_calc_pull_reg_and_bit, }; +static struct rockchip_pin_bank rk3228_pin_banks[] = { + PIN_BANK(0, 32, "gpio0"), + PIN_BANK(1, 32, "gpio1"), + PIN_BANK(2, 32, "gpio2"), + PIN_BANK(3, 32, "gpio3"), +}; + +static struct rockchip_pin_ctrl rk3228_pin_ctrl = { + .pin_banks = rk3228_pin_banks, + .nr_banks = ARRAY_SIZE(rk3228_pin_banks), + .label = "RK3228-GPIO", + .type = RK3288, + .grf_mux_offset = 0x0, + .pull_calc_reg = rk3228_calc_pull_reg_and_bit, + .drv_calc_reg = rk3228_calc_drv_reg_and_bit, +}; + static struct rockchip_pin_bank rk3288_pin_banks[] = { PIN_BANK_IOMUX_FLAGS(0, 24, "gpio0", IOMUX_SOURCE_PMU, IOMUX_SOURCE_PMU, @@ -2186,6 +2919,69 @@ static struct rockchip_pin_ctrl rk3288_pin_ctrl = { .drv_calc_reg = rk3288_calc_drv_reg_and_bit, }; +static struct rockchip_pin_bank rk3366_pin_banks[] = { + PIN_BANK_IOMUX_DRV_FLAGS(0, 32, "gpio0", + IOMUX_SOURCE_PMU, + IOMUX_SOURCE_PMU, + IOMUX_SOURCE_PMU, + IOMUX_SOURCE_PMU, + DRV_TYPE_IO_NARROW_LEVEL, + DRV_TYPE_IO_NARROW_LEVEL, + DRV_TYPE_IO_NARROW_LEVEL, + DRV_TYPE_IO_NARROW_LEVEL + ), + PIN_BANK_IOMUX_FLAGS_OFFSET_DRV_FLAGS(1, 32, "gpio1", + IOMUX_SOURCE_PMU, + IOMUX_SOURCE_PMU, + IOMUX_SOURCE_PMU, + IOMUX_SOURCE_PMU, + 0x30, + 0x34, + 0x38, + 0x3c, + DRV_TYPE_IO_NARROW_LEVEL, + DRV_TYPE_IO_NARROW_LEVEL, + DRV_TYPE_IO_NARROW_LEVEL, + DRV_TYPE_IO_NARROW_LEVEL + ), + PIN_BANK_DRV_FLAGS(2, 32, "gpio2", + DRV_TYPE_IO_WIDE_LEVEL, + DRV_TYPE_IO_NARROW_LEVEL, + DRV_TYPE_IO_WIDE_LEVEL, + DRV_TYPE_IO_NARROW_LEVEL + ), + PIN_BANK_DRV_FLAGS(3, 32, "gpio3", + DRV_TYPE_IO_NARROW_LEVEL, + DRV_TYPE_IO_NARROW_LEVEL, + DRV_TYPE_IO_NARROW_LEVEL, + DRV_TYPE_IO_NARROW_LEVEL + ), + PIN_BANK_DRV_FLAGS(4, 32, "gpio4", + DRV_TYPE_IO_NARROW_LEVEL, + DRV_TYPE_IO_NARROW_LEVEL, + DRV_TYPE_IO_NARROW_LEVEL, + DRV_TYPE_IO_NARROW_LEVEL + ), + PIN_BANK_DRV_FLAGS(5, 32, "gpio5", + DRV_TYPE_IO_NARROW_LEVEL, + DRV_TYPE_IO_NARROW_LEVEL, + DRV_TYPE_IO_NARROW_LEVEL, + DRV_TYPE_IO_NARROW_LEVEL + ), +}; + +static struct rockchip_pin_ctrl rk3366_pin_ctrl = { + .pin_banks = rk3366_pin_banks, + .nr_banks = ARRAY_SIZE(rk3366_pin_banks), + .label = "RK3366-GPIO", + .type = RK3366, + .grf_mux_offset = 0x10, + .pmu_mux_offset = 0x0, + .pull_calc_reg = rk3366_calc_pull_reg_and_bit, + .drv_calc_reg = rk3366_calc_drv_reg_and_bit, + .drv_calc_extra_reg = rk3366_calc_drv_extra_reg_and_bit, +}; + static struct rockchip_pin_bank rk3368_pin_banks[] = { PIN_BANK_IOMUX_FLAGS(0, 32, "gpio0", IOMUX_SOURCE_PMU, IOMUX_SOURCE_PMU, @@ -2208,6 +3004,70 @@ static struct rockchip_pin_ctrl rk3368_pin_ctrl = { .drv_calc_reg = rk3368_calc_drv_reg_and_bit, }; +static struct rockchip_pin_bank rk3399_pin_banks[] = { + PIN_BANK_IOMUX_FLAGS_DRV_FLAGS_OFFSET_PULL_FLAGS(0, 32, "gpio0", + IOMUX_SOURCE_PMU, + IOMUX_SOURCE_PMU, + IOMUX_SOURCE_PMU, + IOMUX_SOURCE_PMU, + DRV_TYPE_IO_1V8_ONLY, + DRV_TYPE_IO_1V8_ONLY, + DRV_TYPE_IO_DEFAULT, + DRV_TYPE_IO_DEFAULT, + 0x0, + 0x8, + -1, + -1, + PULL_TYPE_IO_1V8_ONLY, + PULL_TYPE_IO_1V8_ONLY, + PULL_TYPE_IO_DEFAULT, + PULL_TYPE_IO_DEFAULT), + PIN_BANK_IOMUX_FLAGS_DRV_FLAGS_OFFSET(1, 32, "gpio1", IOMUX_SOURCE_PMU, + IOMUX_SOURCE_PMU, + IOMUX_SOURCE_PMU, + IOMUX_SOURCE_PMU, + DRV_TYPE_IO_1V8_OR_3V0, + DRV_TYPE_IO_1V8_OR_3V0, + DRV_TYPE_IO_1V8_OR_3V0, + DRV_TYPE_IO_1V8_OR_3V0, + 0x20, + 0x28, + 0x30, + 0x38 + ), + PIN_BANK_DRV_FLAGS_PULL_FLAGS(2, 32, "gpio2", DRV_TYPE_IO_1V8_OR_3V0, + DRV_TYPE_IO_1V8_OR_3V0, + DRV_TYPE_IO_1V8_ONLY, + DRV_TYPE_IO_1V8_ONLY, + PULL_TYPE_IO_DEFAULT, + PULL_TYPE_IO_DEFAULT, + PULL_TYPE_IO_1V8_ONLY, + PULL_TYPE_IO_1V8_ONLY + ), + PIN_BANK_DRV_FLAGS(3, 32, "gpio3", DRV_TYPE_IO_3V3_ONLY, + DRV_TYPE_IO_3V3_ONLY, + DRV_TYPE_IO_3V3_ONLY, + DRV_TYPE_IO_1V8_OR_3V0 + ), + PIN_BANK_DRV_FLAGS(4, 32, "gpio4", DRV_TYPE_IO_1V8_OR_3V0, + DRV_TYPE_IO_1V8_3V0_AUTO, + DRV_TYPE_IO_1V8_OR_3V0, + DRV_TYPE_IO_1V8_OR_3V0 + ), +}; + +static struct rockchip_pin_ctrl rk3399_pin_ctrl = { + .pin_banks = rk3399_pin_banks, + .nr_banks = ARRAY_SIZE(rk3399_pin_banks), + .label = "RK3399-GPIO", + .type = RK3399, + .grf_mux_offset = 0xe000, + .pmu_mux_offset = 0x0, + .grf_drv_offset = 0xe100, + .pmu_drv_offset = 0x80, + .pull_calc_reg = rk3399_calc_pull_reg_and_bit, + .drv_calc_reg = rk3399_calc_drv_reg_and_bit, +}; static const struct of_device_id rockchip_pinctrl_dt_match[] = { { .compatible = "rockchip,rk2928-pinctrl", @@ -2220,10 +3080,16 @@ static const struct of_device_id rockchip_pinctrl_dt_match[] = { .data = (void *)&rk3066b_pin_ctrl }, { .compatible = "rockchip,rk3188-pinctrl", .data = (void *)&rk3188_pin_ctrl }, + { .compatible = "rockchip,rk3228-pinctrl", + .data = (void *)&rk3228_pin_ctrl }, { .compatible = "rockchip,rk3288-pinctrl", .data = (void *)&rk3288_pin_ctrl }, + { .compatible = "rockchip,rk3366-pinctrl", + .data = (void *)&rk3366_pin_ctrl }, { .compatible = "rockchip,rk3368-pinctrl", .data = (void *)&rk3368_pin_ctrl }, + { .compatible = "rockchip,rk3399-pinctrl", + .data = (void *)&rk3399_pin_ctrl }, {}, }; MODULE_DEVICE_TABLE(of, rockchip_pinctrl_dt_match);