[PATCH v3 4/4] pinctrl: rockchip: Add RV1106 pinctrl support
Simon Glass <[email protected]> Wed, 29 Jul 2026 07:27:28 -0600
| Newsgroups | org.kernel.vger.linux-gpio,org.infradead.lists.linux-arm-kernel,org.infradead.lists.linux-rockchip,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <20260729072727.v3.4.0af20e702c540f54a3d1694654b158a9b783d68e@changeid> |
Add pinctrl support for the Rockchip RV1106, based on the vendor kernel in the Luckfox Pico SDK [1] at commit 824b817f8 (a Linux 5.10.160 kernel tree). Each GPIO bank has its own IO control (IOC) register block, referenced by the rockchip,grf phandle of the bank node; the register offsets are relative to the bank's own block. The drive strength uses the RK3568-style exponential encoding and only pins 0-6 of GPIO0 have drive-strength registers. The RV1103 is a package variant of the RV1106 with fewer pins and uses the same pin controller. [1] https://github.com/LuckfoxTECH/luckfox-pico Signed-off-by: Simon Glass <[email protected]> Reviewed-by: Heiko Stuebner <[email protected]> --- Changes in v3: - Declare the 8-bit level drive type on the banks so that reads decode correctly - Return an error from the iomux functions when a bank has no IOC regmap, rather than falling back to the shared regmap Changes in v2: - Use a separate IOC regmap per bank, taken from the rockchip,grf phandle of each bank node and identified by the gpio alias, with block-relative register offsets - Reject drive-strength requests for GPIO0 pins above 6, which have no drive-strength registers - Specify only the first iomux offset for each bank, letting the driver calculate the increments drivers/pinctrl/pinctrl-rockchip.c | 192 +++++++++++++++++++++++++++++ drivers/pinctrl/pinctrl-rockchip.h | 4 + 2 files changed, 196 insertions(+) diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c index 08a46a04a815..4e5e4434ba4e 100644 --- a/drivers/pinctrl/pinctrl-rockchip.c +++ b/drivers/pinctrl/pinctrl-rockchip.c @@ -1188,6 +1188,12 @@ static int rockchip_get_mux(struct rockchip_pin_bank *bank, int pin) else regmap = info->regmap_base; + /* Banks with their own IOC block use its regmap for the iomux */ + if (bank->regmap_ioc) + regmap = bank->regmap_ioc; + else if (ctrl->type == RV1106) + return -EINVAL; + if (ctrl->type == RV1103B && bank->bank_num == 2 && pin >= 12) return 0; @@ -1317,6 +1323,12 @@ static int rockchip_set_mux(struct rockchip_pin_bank *bank, int pin, int mux) else regmap = info->regmap_base; + /* Banks with their own IOC block use its regmap for the iomux */ + if (bank->regmap_ioc) + regmap = bank->regmap_ioc; + else if (ctrl->type == RV1106) + return -EINVAL; + if (ctrl->type == RV1103B && bank->bank_num == 2 && pin >= 12) return 0; @@ -1725,6 +1737,78 @@ static int rv1103b_calc_schmitt_reg_and_bit(struct rockchip_pin_bank *bank, return 0; } +#define RV1106_DRV_BITS_PER_PIN 8 +#define RV1106_DRV_PINS_PER_REG 2 +#define RV1106_PULL_BITS_PER_PIN 2 +#define RV1106_PULL_PINS_PER_REG 8 +#define RV1106_SMT_BITS_PER_PIN 1 +#define RV1106_SMT_PINS_PER_REG 8 + +/* + * Each bank has its own IOC block, referenced by the rockchip,grf + * phandle of the bank node. The offsets below are relative to the + * bank's own block. + */ +static const int rv1106_drv_offsets[] = { 0x10, 0x80, 0xc0, 0x100, 0x20 }; +static const int rv1106_pull_offsets[] = { 0x38, 0x1c0, 0x1d0, 0x1e0, 0x70 }; +static const int rv1106_smt_offsets[] = { 0x40, 0x280, 0x290, 0x2a0, 0xa0 }; + +static int rv1106_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank, + int pin_num, struct regmap **regmap, + int *reg, u8 *bit) +{ + if (bank->bank_num >= ARRAY_SIZE(rv1106_drv_offsets) || + !bank->regmap_ioc) + return -EINVAL; + + /* Only pins 0-6 of GPIO0 have drive-strength registers */ + if (bank->bank_num == 0 && pin_num > 6) + return -ENOTSUPP; + + *regmap = bank->regmap_ioc; + *reg = rv1106_drv_offsets[bank->bank_num]; + *reg += ((pin_num / RV1106_DRV_PINS_PER_REG) * 4); + *bit = pin_num % RV1106_DRV_PINS_PER_REG; + *bit *= RV1106_DRV_BITS_PER_PIN; + + return 0; +} + +static int rv1106_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, + int pin_num, struct regmap **regmap, + int *reg, u8 *bit) +{ + if (bank->bank_num >= ARRAY_SIZE(rv1106_pull_offsets) || + !bank->regmap_ioc) + return -EINVAL; + + *regmap = bank->regmap_ioc; + *reg = rv1106_pull_offsets[bank->bank_num]; + *reg += ((pin_num / RV1106_PULL_PINS_PER_REG) * 4); + *bit = pin_num % RV1106_PULL_PINS_PER_REG; + *bit *= RV1106_PULL_BITS_PER_PIN; + + return 0; +} + +static int rv1106_calc_schmitt_reg_and_bit(struct rockchip_pin_bank *bank, + int pin_num, + struct regmap **regmap, + int *reg, u8 *bit) +{ + if (bank->bank_num >= ARRAY_SIZE(rv1106_smt_offsets) || + !bank->regmap_ioc) + return -EINVAL; + + *regmap = bank->regmap_ioc; + *reg = rv1106_smt_offsets[bank->bank_num]; + *reg += ((pin_num / RV1106_SMT_PINS_PER_REG) * 4); + *bit = pin_num % RV1106_SMT_PINS_PER_REG; + *bit *= RV1106_SMT_BITS_PER_PIN; + + return 0; +} + #define RV1108_PULL_PMU_OFFSET 0x10 #define RV1108_PULL_OFFSET 0x110 #define RV1108_PULL_PINS_PER_REG 8 @@ -3329,6 +3413,7 @@ static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank, ret = strength; goto config; } else if (ctrl->type == RV1103B || + ctrl->type == RV1106 || ctrl->type == RK3506 || ctrl->type == RK3528 || ctrl->type == RK3562 || @@ -3482,6 +3567,7 @@ static int rockchip_get_pull(struct rockchip_pin_bank *bank, int pin_num) : PIN_CONFIG_BIAS_DISABLE; case PX30: case RV1103B: + case RV1106: case RV1108: case RK3188: case RK3288: @@ -3547,6 +3633,7 @@ static int rockchip_set_pull(struct rockchip_pin_bank *bank, break; case PX30: case RV1103B: + case RV1106: case RV1108: case RV1126: case RK3188: @@ -3843,6 +3930,7 @@ static bool rockchip_pinconf_pull_valid(struct rockchip_pin_ctrl *ctrl, return pull ? false : true; case PX30: case RV1103B: + case RV1106: case RV1108: case RV1126: case RK3188: @@ -4452,6 +4540,7 @@ static int rockchip_pinctrl_probe(struct platform_device *pdev) struct resource *res; void __iomem *base; int ret; + int i; if (!dev->of_node) return dev_err_probe(dev, -ENODEV, "device tree node not found\n"); @@ -4505,6 +4594,44 @@ static int rockchip_pinctrl_probe(struct platform_device *pdev) /* try to find the optional reference to the ioc1 syscon */ info->regmap_ioc1 = syscon_regmap_lookup_by_phandle_optional(np, "rockchip,ioc1"); + /* + * On SoCs where each GPIO bank has its own IOC block, the bank nodes + * carry a rockchip,grf phandle pointing at it. The bank number comes + * from the gpio alias, as used by the gpio driver, falling back to + * the node position for devicetrees without aliases. The fallback is + * wrong when an SoC variant omits a bank, so aliases are needed + * there. + */ + i = 0; + for_each_child_of_node_scoped(np, child) { + struct rockchip_pin_bank *bank = NULL; + int id, j; + + if (!of_match_node(rockchip_bank_match, child)) + continue; + + id = of_alias_get_id(child, "gpio"); + if (id < 0) + id = i; + i++; + + for (j = 0; j < ctrl->nr_banks; j++) { + if (ctrl->pin_banks[j].bank_num == id) { + bank = &ctrl->pin_banks[j]; + break; + } + } + if (!bank) + continue; + + bank->regmap_ioc = syscon_regmap_lookup_by_phandle_optional( + child, "rockchip,grf"); + if (IS_ERR(bank->regmap_ioc)) + return dev_err_probe(dev, PTR_ERR(bank->regmap_ioc), + "%pOFn: failed to look up bank ioc\n", + child); + } + ret = rockchip_pinctrl_register(pdev, info); if (ret) return ret; @@ -4623,6 +4750,69 @@ static struct rockchip_pin_ctrl rv1103b_pin_ctrl __maybe_unused = { .schmitt_calc_reg = rv1103b_calc_schmitt_reg_and_bit, }; +static struct rockchip_pin_bank rv1106_pin_banks[] = { + PIN_BANK_IOMUX_FLAGS_OFFSET_DRV_FLAGS(0, 32, "gpio0", + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + 0, -1, -1, -1, + DRV_TYPE_IO_LEVEL_8_BIT, + DRV_TYPE_IO_LEVEL_8_BIT, + DRV_TYPE_IO_LEVEL_8_BIT, + DRV_TYPE_IO_LEVEL_8_BIT), + PIN_BANK_IOMUX_FLAGS_OFFSET_DRV_FLAGS(1, 32, "gpio1", + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + 0, -1, -1, -1, + DRV_TYPE_IO_LEVEL_8_BIT, + DRV_TYPE_IO_LEVEL_8_BIT, + DRV_TYPE_IO_LEVEL_8_BIT, + DRV_TYPE_IO_LEVEL_8_BIT), + PIN_BANK_IOMUX_FLAGS_OFFSET_DRV_FLAGS(2, 32, "gpio2", + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + 0x20, -1, -1, -1, + DRV_TYPE_IO_LEVEL_8_BIT, + DRV_TYPE_IO_LEVEL_8_BIT, + DRV_TYPE_IO_LEVEL_8_BIT, + DRV_TYPE_IO_LEVEL_8_BIT), + PIN_BANK_IOMUX_FLAGS_OFFSET_DRV_FLAGS(3, 32, "gpio3", + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + 0x40, -1, -1, -1, + DRV_TYPE_IO_LEVEL_8_BIT, + DRV_TYPE_IO_LEVEL_8_BIT, + DRV_TYPE_IO_LEVEL_8_BIT, + DRV_TYPE_IO_LEVEL_8_BIT), + PIN_BANK_IOMUX_FLAGS_OFFSET_DRV_FLAGS(4, 24, "gpio4", + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + 0, + 0, -1, -1, -1, + DRV_TYPE_IO_LEVEL_8_BIT, + DRV_TYPE_IO_LEVEL_8_BIT, + DRV_TYPE_IO_LEVEL_8_BIT, + DRV_TYPE_IO_LEVEL_8_BIT), +}; + +static struct rockchip_pin_ctrl rv1106_pin_ctrl __maybe_unused = { + .pin_banks = rv1106_pin_banks, + .nr_banks = ARRAY_SIZE(rv1106_pin_banks), + .label = "RV1106-GPIO", + .type = RV1106, + .pull_calc_reg = rv1106_calc_pull_reg_and_bit, + .drv_calc_reg = rv1106_calc_drv_reg_and_bit, + .schmitt_calc_reg = rv1106_calc_schmitt_reg_and_bit, +}; + static struct rockchip_pin_bank rv1108_pin_banks[] = { PIN_BANK_IOMUX_FLAGS(0, 32, "gpio0", IOMUX_SOURCE_PMU, IOMUX_SOURCE_PMU, @@ -5261,6 +5451,8 @@ static const struct of_device_id rockchip_pinctrl_dt_match[] = { .data = &px30_pin_ctrl }, { .compatible = "rockchip,rv1103b-pinctrl", .data = &rv1103b_pin_ctrl }, + { .compatible = "rockchip,rv1106-pinctrl", + .data = &rv1106_pin_ctrl }, { .compatible = "rockchip,rv1108-pinctrl", .data = &rv1108_pin_ctrl }, { .compatible = "rockchip,rv1126-pinctrl", diff --git a/drivers/pinctrl/pinctrl-rockchip.h b/drivers/pinctrl/pinctrl-rockchip.h index bb0e803e3b8a..f62213d99792 100644 --- a/drivers/pinctrl/pinctrl-rockchip.h +++ b/drivers/pinctrl/pinctrl-rockchip.h @@ -186,6 +186,7 @@ enum rockchip_pinctrl_type { PX30, RV1103B, + RV1106, RV1108, RV1126, RK2928, @@ -295,6 +296,8 @@ struct rockchip_drv { * @dev: the pinctrl device bind to the bank * @reg_base: register base of the gpio bank * @regmap_pull: optional separate register for additional pull settings + * @regmap_ioc: optional per-bank IO control regmap, for SoCs where each + * bank has its own IOC block * @clk: clock of the gpio bank * @db_clk: clock of the gpio debounce * @irq: interrupt of the gpio bank @@ -323,6 +326,7 @@ struct rockchip_pin_bank { struct device *dev; void __iomem *reg_base; struct regmap *regmap_pull; + struct regmap *regmap_ioc; struct clk *clk; struct clk *db_clk; int irq; -- 2.43.0