diff options
author | Simon Glass <sjg@chromium.org> | 2016-01-21 19:43:46 -0700 |
---|---|---|
committer | Simon Glass <sjg@chromium.org> | 2016-01-21 20:42:35 -0700 |
commit | bea705c993a13a4737705fc739318a1fe9a6a178 (patch) | |
tree | 25e65e9210148ff5fdbe04d63c8c4c0e56181c7b | |
parent | 342999f9b3ce6c5c30b100549799e646e7fa70c5 (diff) |
rockchip: pinctrl: Add a full pinctrl driver
We can make use of the device tree to configure pinctrl settings. Add this
support for the driver so we can use it in U-Boot proper.
Signed-off-by: Simon Glass <sjg@chromium.org>
-rw-r--r-- | drivers/pinctrl/rockchip/pinctrl_rk3288.c | 230 |
1 files changed, 229 insertions, 1 deletions
diff --git a/drivers/pinctrl/rockchip/pinctrl_rk3288.c b/drivers/pinctrl/rockchip/pinctrl_rk3288.c index ec3c4fe5990..0e7721e0b5e 100644 --- a/drivers/pinctrl/rockchip/pinctrl_rk3288.c +++ b/drivers/pinctrl/rockchip/pinctrl_rk3288.c @@ -24,8 +24,103 @@ DECLARE_GLOBAL_DATA_PTR; struct rk3288_pinctrl_priv { struct rk3288_grf *grf; struct rk3288_pmu *pmu; + int num_banks; }; +/** + * Encode variants of iomux registers into a type variable + */ +#define IOMUX_GPIO_ONLY BIT(0) +#define IOMUX_WIDTH_4BIT BIT(1) +#define IOMUX_SOURCE_PMU BIT(2) +#define IOMUX_UNROUTED BIT(3) + +/** + * @type: iomux variant using IOMUX_* constants + * @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 iomux registers. + */ +struct rockchip_iomux { + u8 type; + s16 offset; +}; + +/** + * @reg: register offset of the gpio bank + * @nr_pins: number of pins in this bank + * @bank_num: number of the bank, to account for holes + * @name: name of the bank + * @iomux: array describing the 4 iomux sources of the bank + */ +struct rockchip_pin_bank { + u16 reg; + u8 nr_pins; + u8 bank_num; + char *name; + struct rockchip_iomux iomux[4]; +}; + +#define PIN_BANK(id, pins, label) \ + { \ + .bank_num = id, \ + .nr_pins = pins, \ + .name = label, \ + .iomux = { \ + { .offset = -1 }, \ + { .offset = -1 }, \ + { .offset = -1 }, \ + { .offset = -1 }, \ + }, \ + } + +#define PIN_BANK_IOMUX_FLAGS(id, pins, label, iom0, iom1, iom2, iom3) \ + { \ + .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 }, \ + }, \ + } + +#ifndef CONFIG_SPL_BUILD +static struct rockchip_pin_bank rk3288_pin_banks[] = { + PIN_BANK_IOMUX_FLAGS(0, 24, "gpio0", IOMUX_SOURCE_PMU, + IOMUX_SOURCE_PMU, + IOMUX_SOURCE_PMU, + IOMUX_UNROUTED + ), + PIN_BANK_IOMUX_FLAGS(1, 32, "gpio1", IOMUX_UNROUTED, + IOMUX_UNROUTED, + IOMUX_UNROUTED, + 0 + ), + PIN_BANK_IOMUX_FLAGS(2, 32, "gpio2", 0, 0, 0, IOMUX_UNROUTED), + PIN_BANK_IOMUX_FLAGS(3, 32, "gpio3", 0, 0, 0, IOMUX_WIDTH_4BIT), + PIN_BANK_IOMUX_FLAGS(4, 32, "gpio4", IOMUX_WIDTH_4BIT, + IOMUX_WIDTH_4BIT, + 0, + 0 + ), + PIN_BANK_IOMUX_FLAGS(5, 32, "gpio5", IOMUX_UNROUTED, + 0, + 0, + IOMUX_UNROUTED + ), + PIN_BANK_IOMUX_FLAGS(6, 32, "gpio6", 0, 0, 0, IOMUX_UNROUTED), + PIN_BANK_IOMUX_FLAGS(7, 32, "gpio7", 0, + 0, + IOMUX_WIDTH_4BIT, + IOMUX_UNROUTED + ), + PIN_BANK(8, 16, "gpio8"), +}; +#endif + static void pinctrl_rk3288_pwm_config(struct rk3288_grf *grf, int pwm_id) { switch (pwm_id) { @@ -410,7 +505,106 @@ static int rk3288_pinctrl_set_state_simple(struct udevice *dev, return rk3288_pinctrl_request(dev, func, 0); } +#ifndef CONFIG_SPL_BUILD +static int rk3288_pinctrl_set_pins(struct udevice *dev, int banknum, int index, + int muxval, int flags) +{ + struct rk3288_pinctrl_priv *priv = dev_get_priv(dev); + struct rockchip_pin_bank *bank = &rk3288_pin_banks[banknum]; + uint shift, muxnum, ind = index; + u32 *addr; + + debug("%s: %x %x %x %x\n", __func__, banknum, index, muxval, flags); + for (muxnum = 0; muxnum < 4; muxnum++) { + struct rockchip_iomux *mux = &bank->iomux[muxnum]; + uint mask; + + if (ind >= 8) { + ind -= 8; + continue; + } + + if (mux->type & IOMUX_SOURCE_PMU) + addr = priv->pmu->gpio0_iomux; + else + addr = (u32 *)priv->grf - 4; + addr += mux->offset; + shift = ind & 7; + if (mux->type & IOMUX_WIDTH_4BIT) { + mask = 0xf; + shift *= 4; + if (shift >= 16) { + shift -= 16; + addr++; + } + } else { + mask = 3; + shift *= 2; + } + + debug("%s: addr=%p, mask=%x, shift=%x\n", __func__, addr, + mask, shift); + rk_clrsetreg(addr, mask << shift, muxval << shift); + break; + } + if (flags) { + uint val = 0; + + if (flags & (1 << PIN_CONFIG_BIAS_PULL_UP)) + val = 1; + else if (flags & (1 << PIN_CONFIG_BIAS_PULL_DOWN)) + val = 2; + shift = (index & 7) * 2; + ind = index >> 3; + if (banknum == 0) + addr = &priv->pmu->gpio0pull[ind]; + else + addr = &priv->grf->gpio1_p[banknum - 1][ind]; + debug("%s: addr=%p, val=%x, shift=%x\n", __func__, addr, val, + shift); + rk_clrsetreg(addr, 3 << shift, val << shift); + } + + return 0; +} + +static int rk3288_pinctrl_set_state(struct udevice *dev, struct udevice *config) +{ + const void *blob = gd->fdt_blob; + int pcfg_node, ret, flags, count, i; + u32 cell[40], *ptr; + + debug("%s: %s %s\n", __func__, dev->name, config->name); + ret = fdtdec_get_int_array_count(blob, config->of_offset, + "rockchip,pins", cell, + ARRAY_SIZE(cell)); + if (ret < 0) { + debug("%s: bad array %d\n", __func__, ret); + return -EINVAL; + } + count = ret; + for (i = 0, ptr = cell; i < count; i += 4, ptr += 4) { + pcfg_node = fdt_node_offset_by_phandle(blob, ptr[3]); + if (pcfg_node < 0) + return -EINVAL; + flags = pinctrl_decode_pin_config(blob, pcfg_node); + if (flags < 0) + return flags; + + ret = rk3288_pinctrl_set_pins(dev, ptr[0], ptr[1], ptr[2], + flags); + if (ret) + return ret; + } + + return 0; +} +#endif + static struct pinctrl_ops rk3288_pinctrl_ops = { +#ifndef CONFIG_SPL_BUILD + .set_state = rk3288_pinctrl_set_state, +#endif .set_state_simple = rk3288_pinctrl_set_state_simple, .request = rk3288_pinctrl_request, .get_periph_id = rk3288_pinctrl_get_periph_id, @@ -422,15 +616,49 @@ static int rk3288_pinctrl_bind(struct udevice *dev) return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false); } +#ifndef CONFIG_SPL_BUILD +static int rk3288_pinctrl_parse_tables(struct rk3288_pinctrl_priv *priv, + struct rockchip_pin_bank *banks, + int count) +{ + struct rockchip_pin_bank *bank; + uint reg, muxnum, banknum; + + reg = 0; + for (banknum = 0; banknum < count; banknum++) { + bank = &banks[banknum]; + bank->reg = reg; + debug("%s: bank %d, reg %x\n", __func__, banknum, reg * 4); + for (muxnum = 0; muxnum < 4; muxnum++) { + struct rockchip_iomux *mux = &bank->iomux[muxnum]; + + if (!(mux->type & IOMUX_UNROUTED)) + mux->offset = reg; + if (mux->type & IOMUX_WIDTH_4BIT) + reg += 2; + else + reg += 1; + } + } + + return 0; +} +#endif + static int rk3288_pinctrl_probe(struct udevice *dev) { struct rk3288_pinctrl_priv *priv = dev_get_priv(dev); + int ret = 0; priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF); priv->pmu = syscon_get_first_range(ROCKCHIP_SYSCON_PMU); debug("%s: grf=%p, pmu=%p\n", __func__, priv->grf, priv->pmu); +#ifndef CONFIG_SPL_BUILD + ret = rk3288_pinctrl_parse_tables(priv, rk3288_pin_banks, + ARRAY_SIZE(rk3288_pin_banks)); +#endif - return 0; + return ret; } static const struct udevice_id rk3288_pinctrl_ids[] = { |