1f2e4e921SDavid Wu // SPDX-License-Identifier: GPL-2.0+ 2f2e4e921SDavid Wu /* 3f2e4e921SDavid Wu * (C) Copyright 2019 Rockchip Electronics Co., Ltd 4f2e4e921SDavid Wu */ 5f2e4e921SDavid Wu 6f2e4e921SDavid Wu #include <common.h> 7f2e4e921SDavid Wu #include <dm.h> 8f2e4e921SDavid Wu #include <dm/pinctrl.h> 9f2e4e921SDavid Wu #include <regmap.h> 10f2e4e921SDavid Wu #include <syscon.h> 11f2e4e921SDavid Wu #include <fdtdec.h> 121e9d2e36SCody Xie #include <dm/uclass-internal.h> 131e9d2e36SCody Xie #include <asm/gpio.h> 14f2e4e921SDavid Wu 15f2e4e921SDavid Wu #include "pinctrl-rockchip.h" 16f2e4e921SDavid Wu 17f2e4e921SDavid Wu #define MAX_ROCKCHIP_PINS_ENTRIES 30 18f2e4e921SDavid Wu #define MAX_ROCKCHIP_GPIO_PER_BANK 32 19f2e4e921SDavid Wu #define RK_FUNC_GPIO 0 20f2e4e921SDavid Wu 21f2e4e921SDavid Wu static int rockchip_verify_config(struct udevice *dev, u32 bank, u32 pin) 22f2e4e921SDavid Wu { 23f2e4e921SDavid Wu struct rockchip_pinctrl_priv *priv = dev_get_priv(dev); 24f2e4e921SDavid Wu struct rockchip_pin_ctrl *ctrl = priv->ctrl; 25f2e4e921SDavid Wu 26f2e4e921SDavid Wu if (bank >= ctrl->nr_banks) { 27f2e4e921SDavid Wu debug("pin conf bank %d >= nbanks %d\n", bank, ctrl->nr_banks); 28f2e4e921SDavid Wu return -EINVAL; 29f2e4e921SDavid Wu } 30f2e4e921SDavid Wu 31f2e4e921SDavid Wu if (pin >= MAX_ROCKCHIP_GPIO_PER_BANK) { 32f2e4e921SDavid Wu debug("pin conf pin %d >= %d\n", pin, 33f2e4e921SDavid Wu MAX_ROCKCHIP_GPIO_PER_BANK); 34f2e4e921SDavid Wu return -EINVAL; 35f2e4e921SDavid Wu } 36f2e4e921SDavid Wu 37f2e4e921SDavid Wu return 0; 38f2e4e921SDavid Wu } 39f2e4e921SDavid Wu 405f55bbd7SDavid Wu void rockchip_get_recalced_mux(struct rockchip_pin_bank *bank, int pin, 41f2e4e921SDavid Wu int *reg, u8 *bit, int *mask) 42f2e4e921SDavid Wu { 43f2e4e921SDavid Wu struct rockchip_pinctrl_priv *priv = bank->priv; 44f2e4e921SDavid Wu struct rockchip_pin_ctrl *ctrl = priv->ctrl; 45f2e4e921SDavid Wu struct rockchip_mux_recalced_data *data; 46f2e4e921SDavid Wu int i; 47f2e4e921SDavid Wu 48f2e4e921SDavid Wu for (i = 0; i < ctrl->niomux_recalced; i++) { 49f2e4e921SDavid Wu data = &ctrl->iomux_recalced[i]; 50f2e4e921SDavid Wu if (data->num == bank->bank_num && 51f2e4e921SDavid Wu data->pin == pin) 52f2e4e921SDavid Wu break; 53f2e4e921SDavid Wu } 54f2e4e921SDavid Wu 55f2e4e921SDavid Wu if (i >= ctrl->niomux_recalced) 56f2e4e921SDavid Wu return; 57f2e4e921SDavid Wu 58f2e4e921SDavid Wu *reg = data->reg; 59f2e4e921SDavid Wu *mask = data->mask; 60f2e4e921SDavid Wu *bit = data->bit; 61f2e4e921SDavid Wu } 62f2e4e921SDavid Wu 63b8d3e6ffSJianqun Xu static enum rockchip_pin_route_type 64b8d3e6ffSJianqun Xu rockchip_get_mux_route(struct rockchip_pin_bank *bank, int pin, 65f2e4e921SDavid Wu int mux, u32 *reg, u32 *value) 66f2e4e921SDavid Wu { 67f2e4e921SDavid Wu struct rockchip_pinctrl_priv *priv = bank->priv; 68f2e4e921SDavid Wu struct rockchip_pin_ctrl *ctrl = priv->ctrl; 69f2e4e921SDavid Wu struct rockchip_mux_route_data *data; 70f2e4e921SDavid Wu int i; 71f2e4e921SDavid Wu 72f2e4e921SDavid Wu for (i = 0; i < ctrl->niomux_routes; i++) { 73f2e4e921SDavid Wu data = &ctrl->iomux_routes[i]; 74f2e4e921SDavid Wu if (data->bank_num == bank->bank_num && 75f2e4e921SDavid Wu data->pin == pin && data->func == mux) 76f2e4e921SDavid Wu break; 77f2e4e921SDavid Wu } 78f2e4e921SDavid Wu 79f2e4e921SDavid Wu if (i >= ctrl->niomux_routes) 80b8d3e6ffSJianqun Xu return ROUTE_TYPE_INVALID; 81f2e4e921SDavid Wu 82f2e4e921SDavid Wu *reg = data->route_offset; 83f2e4e921SDavid Wu *value = data->route_val; 84f2e4e921SDavid Wu 85b8d3e6ffSJianqun Xu return data->route_type; 86f2e4e921SDavid Wu } 87f2e4e921SDavid Wu 885f55bbd7SDavid Wu int rockchip_get_mux_data(int mux_type, int pin, u8 *bit, int *mask) 89f2e4e921SDavid Wu { 90f2e4e921SDavid Wu int offset = 0; 91f2e4e921SDavid Wu 92f2e4e921SDavid Wu if (mux_type & IOMUX_WIDTH_4BIT) { 93f2e4e921SDavid Wu if ((pin % 8) >= 4) 94f2e4e921SDavid Wu offset = 0x4; 95f2e4e921SDavid Wu *bit = (pin % 4) * 4; 96f2e4e921SDavid Wu *mask = 0xf; 97f2e4e921SDavid Wu } else if (mux_type & IOMUX_WIDTH_3BIT) { 98f2e4e921SDavid Wu /* 99f2e4e921SDavid Wu * pin0 ~ pin4 are at first register, and 100f2e4e921SDavid Wu * pin5 ~ pin7 are at second register. 101f2e4e921SDavid Wu */ 102f2e4e921SDavid Wu if ((pin % 8) >= 5) 103f2e4e921SDavid Wu offset = 0x4; 104f2e4e921SDavid Wu *bit = (pin % 8 % 5) * 3; 105f2e4e921SDavid Wu *mask = 0x7; 106f2e4e921SDavid Wu } else { 107f2e4e921SDavid Wu *bit = (pin % 8) * 2; 108f2e4e921SDavid Wu *mask = 0x3; 109f2e4e921SDavid Wu } 110f2e4e921SDavid Wu 111f2e4e921SDavid Wu return offset; 112f2e4e921SDavid Wu } 113f2e4e921SDavid Wu 114f2e4e921SDavid Wu static int rockchip_get_mux(struct rockchip_pin_bank *bank, int pin) 115f2e4e921SDavid Wu { 116f2e4e921SDavid Wu struct rockchip_pinctrl_priv *priv = bank->priv; 117f2e4e921SDavid Wu int iomux_num = (pin / 8); 118f2e4e921SDavid Wu struct regmap *regmap; 119f2e4e921SDavid Wu unsigned int val; 120f2e4e921SDavid Wu int reg, ret, mask, mux_type; 121f2e4e921SDavid Wu u8 bit; 122f2e4e921SDavid Wu 123f2e4e921SDavid Wu if (iomux_num > 3) 124f2e4e921SDavid Wu return -EINVAL; 125f2e4e921SDavid Wu 126f2e4e921SDavid Wu if (bank->iomux[iomux_num].type & IOMUX_UNROUTED) { 127f2e4e921SDavid Wu debug("pin %d is unrouted\n", pin); 1288273b391SJianqun Xu return -ENOTSUPP; 129f2e4e921SDavid Wu } 130f2e4e921SDavid Wu 131f2e4e921SDavid Wu if (bank->iomux[iomux_num].type & IOMUX_GPIO_ONLY) 132f2e4e921SDavid Wu return RK_FUNC_GPIO; 133f2e4e921SDavid Wu 134f2e4e921SDavid Wu regmap = (bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU) 135f2e4e921SDavid Wu ? priv->regmap_pmu : priv->regmap_base; 136f2e4e921SDavid Wu 137f2e4e921SDavid Wu /* get basic quadrupel of mux registers and the correct reg inside */ 138f2e4e921SDavid Wu mux_type = bank->iomux[iomux_num].type; 139f2e4e921SDavid Wu reg = bank->iomux[iomux_num].offset; 140f2e4e921SDavid Wu reg += rockchip_get_mux_data(mux_type, pin, &bit, &mask); 141f2e4e921SDavid Wu 142f2e4e921SDavid Wu if (bank->recalced_mask & BIT(pin)) 143f2e4e921SDavid Wu rockchip_get_recalced_mux(bank, pin, ®, &bit, &mask); 144f2e4e921SDavid Wu 145f2e4e921SDavid Wu ret = regmap_read(regmap, reg, &val); 146f2e4e921SDavid Wu if (ret) 147f2e4e921SDavid Wu return ret; 148f2e4e921SDavid Wu 149f2e4e921SDavid Wu return ((val >> bit) & mask); 150f2e4e921SDavid Wu } 151f2e4e921SDavid Wu 152f2e4e921SDavid Wu static int rockchip_pinctrl_get_gpio_mux(struct udevice *dev, int banknum, 153f2e4e921SDavid Wu int index) 154f2e4e921SDavid Wu { struct rockchip_pinctrl_priv *priv = dev_get_priv(dev); 155f2e4e921SDavid Wu struct rockchip_pin_ctrl *ctrl = priv->ctrl; 156f2e4e921SDavid Wu 157f2e4e921SDavid Wu return rockchip_get_mux(&ctrl->pin_banks[banknum], index); 158f2e4e921SDavid Wu } 159f2e4e921SDavid Wu 160f2e4e921SDavid Wu static int rockchip_verify_mux(struct rockchip_pin_bank *bank, 161f2e4e921SDavid Wu int pin, int mux) 162f2e4e921SDavid Wu { 163f2e4e921SDavid Wu int iomux_num = (pin / 8); 164f2e4e921SDavid Wu 165f2e4e921SDavid Wu if (iomux_num > 3) 166f2e4e921SDavid Wu return -EINVAL; 167f2e4e921SDavid Wu 168f2e4e921SDavid Wu if (bank->iomux[iomux_num].type & IOMUX_UNROUTED) { 169f2e4e921SDavid Wu debug("pin %d is unrouted\n", pin); 170f2e4e921SDavid Wu return -ENOTSUPP; 171f2e4e921SDavid Wu } 172f2e4e921SDavid Wu 173f2e4e921SDavid Wu if (bank->iomux[iomux_num].type & IOMUX_GPIO_ONLY) { 174f2e4e921SDavid Wu if (mux != IOMUX_GPIO_ONLY) { 175f2e4e921SDavid Wu debug("pin %d only supports a gpio mux\n", pin); 176f2e4e921SDavid Wu return -ENOTSUPP; 177f2e4e921SDavid Wu } 178f2e4e921SDavid Wu } 179f2e4e921SDavid Wu 180f2e4e921SDavid Wu return 0; 181f2e4e921SDavid Wu } 182f2e4e921SDavid Wu 183f2e4e921SDavid Wu /* 184f2e4e921SDavid Wu * Set a new mux function for a pin. 185f2e4e921SDavid Wu * 186f2e4e921SDavid Wu * The register is divided into the upper and lower 16 bit. When changing 187f2e4e921SDavid Wu * a value, the previous register value is not read and changed. Instead 188f2e4e921SDavid Wu * it seems the changed bits are marked in the upper 16 bit, while the 189f2e4e921SDavid Wu * changed value gets set in the same offset in the lower 16 bit. 190f2e4e921SDavid Wu * All pin settings seem to be 2 bit wide in both the upper and lower 191f2e4e921SDavid Wu * parts. 192f2e4e921SDavid Wu * @bank: pin bank to change 193f2e4e921SDavid Wu * @pin: pin to change 194f2e4e921SDavid Wu * @mux: new mux function to set 195f2e4e921SDavid Wu */ 196f2e4e921SDavid Wu static int rockchip_set_mux(struct rockchip_pin_bank *bank, int pin, int mux) 197f2e4e921SDavid Wu { 198f2e4e921SDavid Wu struct rockchip_pinctrl_priv *priv = bank->priv; 1995f55bbd7SDavid Wu struct rockchip_pin_ctrl *ctrl = priv->ctrl; 200f2e4e921SDavid Wu int iomux_num = (pin / 8); 2015f55bbd7SDavid Wu int ret; 202f2e4e921SDavid Wu 203f2e4e921SDavid Wu ret = rockchip_verify_mux(bank, pin, mux); 204f2e4e921SDavid Wu if (ret < 0) 205f2e4e921SDavid Wu return ret == -ENOTSUPP ? 0 : ret; 206f2e4e921SDavid Wu 207f2e4e921SDavid Wu if (bank->iomux[iomux_num].type & IOMUX_GPIO_ONLY) 208f2e4e921SDavid Wu return 0; 209f2e4e921SDavid Wu 210f2e4e921SDavid Wu debug("setting mux of GPIO%d-%d to %d\n", bank->bank_num, pin, mux); 211f2e4e921SDavid Wu 2125f55bbd7SDavid Wu if (!ctrl->set_mux) 2135f55bbd7SDavid Wu return -ENOTSUPP; 214f2e4e921SDavid Wu 2155f55bbd7SDavid Wu ret = ctrl->set_mux(bank, pin, mux); 216b8d3e6ffSJianqun Xu if (ret) 217f2e4e921SDavid Wu return ret; 218b8d3e6ffSJianqun Xu 219b8d3e6ffSJianqun Xu if (bank->route_mask & BIT(pin)) { 220b8d3e6ffSJianqun Xu struct regmap *regmap; 221b8d3e6ffSJianqun Xu u32 route_reg = 0, route_val = 0; 222b8d3e6ffSJianqun Xu 223b8d3e6ffSJianqun Xu ret = rockchip_get_mux_route(bank, pin, mux, 224b8d3e6ffSJianqun Xu &route_reg, &route_val); 225b8d3e6ffSJianqun Xu switch (ret) { 226b8d3e6ffSJianqun Xu case ROUTE_TYPE_DEFAULT: 227b8d3e6ffSJianqun Xu if (bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU) 228b8d3e6ffSJianqun Xu regmap = priv->regmap_pmu; 229b8d3e6ffSJianqun Xu else if (bank->iomux[iomux_num].type & IOMUX_L_SOURCE_PMU) 230b8d3e6ffSJianqun Xu regmap = (pin % 8 < 4) ? priv->regmap_pmu : priv->regmap_base; 231b8d3e6ffSJianqun Xu else 232b8d3e6ffSJianqun Xu regmap = priv->regmap_base; 233b8d3e6ffSJianqun Xu 234b8d3e6ffSJianqun Xu regmap_write(regmap, route_reg, route_val); 235b8d3e6ffSJianqun Xu break; 236b8d3e6ffSJianqun Xu case ROUTE_TYPE_TOPGRF: 237b8d3e6ffSJianqun Xu regmap_write(priv->regmap_base, route_reg, route_val); 238b8d3e6ffSJianqun Xu break; 239b8d3e6ffSJianqun Xu case ROUTE_TYPE_PMUGRF: 240b8d3e6ffSJianqun Xu regmap_write(priv->regmap_pmu, route_reg, route_val); 241b8d3e6ffSJianqun Xu break; 242b8d3e6ffSJianqun Xu case ROUTE_TYPE_INVALID: /* Fall through */ 243b8d3e6ffSJianqun Xu default: 244b8d3e6ffSJianqun Xu break; 245b8d3e6ffSJianqun Xu } 246b8d3e6ffSJianqun Xu } 247b8d3e6ffSJianqun Xu 248b8d3e6ffSJianqun Xu return 0; 249f2e4e921SDavid Wu } 250f2e4e921SDavid Wu 251f2e4e921SDavid Wu static int rockchip_perpin_drv_list[DRV_TYPE_MAX][8] = { 252f2e4e921SDavid Wu { 2, 4, 8, 12, -1, -1, -1, -1 }, 253f2e4e921SDavid Wu { 3, 6, 9, 12, -1, -1, -1, -1 }, 254f2e4e921SDavid Wu { 5, 10, 15, 20, -1, -1, -1, -1 }, 255f2e4e921SDavid Wu { 4, 6, 8, 10, 12, 14, 16, 18 }, 256f2e4e921SDavid Wu { 4, 7, 10, 13, 16, 19, 22, 26 } 257f2e4e921SDavid Wu }; 258f2e4e921SDavid Wu 259681441e6SDavid Wu int rockchip_translate_drive_value(int type, int strength) 260681441e6SDavid Wu { 261681441e6SDavid Wu int i, ret; 262681441e6SDavid Wu 263681441e6SDavid Wu ret = -EINVAL; 264681441e6SDavid Wu for (i = 0; i < ARRAY_SIZE(rockchip_perpin_drv_list[type]); i++) { 265681441e6SDavid Wu if (rockchip_perpin_drv_list[type][i] == strength) { 266681441e6SDavid Wu ret = i; 267681441e6SDavid Wu break; 268681441e6SDavid Wu } else if (rockchip_perpin_drv_list[type][i] < 0) { 269681441e6SDavid Wu ret = rockchip_perpin_drv_list[type][i]; 270681441e6SDavid Wu break; 271681441e6SDavid Wu } 272681441e6SDavid Wu } 273681441e6SDavid Wu 274681441e6SDavid Wu return ret; 275681441e6SDavid Wu } 276681441e6SDavid Wu 277f2e4e921SDavid Wu static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank, 278f2e4e921SDavid Wu int pin_num, int strength) 279f2e4e921SDavid Wu { 280f2e4e921SDavid Wu struct rockchip_pinctrl_priv *priv = bank->priv; 281f2e4e921SDavid Wu struct rockchip_pin_ctrl *ctrl = priv->ctrl; 282f2e4e921SDavid Wu 283f2e4e921SDavid Wu debug("setting drive of GPIO%d-%d to %d\n", bank->bank_num, 284f2e4e921SDavid Wu pin_num, strength); 285f2e4e921SDavid Wu 286681441e6SDavid Wu if (!ctrl->set_drive) 287681441e6SDavid Wu return -ENOTSUPP; 288f2e4e921SDavid Wu 289681441e6SDavid Wu return ctrl->set_drive(bank, pin_num, strength); 290f2e4e921SDavid Wu } 291f2e4e921SDavid Wu 292f2e4e921SDavid Wu static int rockchip_pull_list[PULL_TYPE_MAX][4] = { 293f2e4e921SDavid Wu { 294f2e4e921SDavid Wu PIN_CONFIG_BIAS_DISABLE, 295f2e4e921SDavid Wu PIN_CONFIG_BIAS_PULL_UP, 296f2e4e921SDavid Wu PIN_CONFIG_BIAS_PULL_DOWN, 297f2e4e921SDavid Wu PIN_CONFIG_BIAS_BUS_HOLD 298f2e4e921SDavid Wu }, 299f2e4e921SDavid Wu { 300f2e4e921SDavid Wu PIN_CONFIG_BIAS_DISABLE, 301f2e4e921SDavid Wu PIN_CONFIG_BIAS_PULL_DOWN, 302f2e4e921SDavid Wu PIN_CONFIG_BIAS_DISABLE, 303f2e4e921SDavid Wu PIN_CONFIG_BIAS_PULL_UP 304f2e4e921SDavid Wu }, 305f2e4e921SDavid Wu }; 306f2e4e921SDavid Wu 30705a5688eSDavid Wu int rockchip_translate_pull_value(int type, int pull) 308f2e4e921SDavid Wu { 30905a5688eSDavid Wu int i, ret; 310f2e4e921SDavid Wu 311f2e4e921SDavid Wu ret = -EINVAL; 31205a5688eSDavid Wu for (i = 0; i < ARRAY_SIZE(rockchip_pull_list[type]); 313f2e4e921SDavid Wu i++) { 31405a5688eSDavid Wu if (rockchip_pull_list[type][i] == pull) { 315f2e4e921SDavid Wu ret = i; 316f2e4e921SDavid Wu break; 317f2e4e921SDavid Wu } 318f2e4e921SDavid Wu } 319f2e4e921SDavid Wu 320f2e4e921SDavid Wu return ret; 321f2e4e921SDavid Wu } 322f2e4e921SDavid Wu 32305a5688eSDavid Wu static int rockchip_set_pull(struct rockchip_pin_bank *bank, 32405a5688eSDavid Wu int pin_num, int pull) 32505a5688eSDavid Wu { 32605a5688eSDavid Wu struct rockchip_pinctrl_priv *priv = bank->priv; 32705a5688eSDavid Wu struct rockchip_pin_ctrl *ctrl = priv->ctrl; 328f2e4e921SDavid Wu 32905a5688eSDavid Wu debug("setting pull of GPIO%d-%d to %d\n", bank->bank_num, 33005a5688eSDavid Wu pin_num, pull); 331f2e4e921SDavid Wu 33205a5688eSDavid Wu if (!ctrl->set_pull) 33305a5688eSDavid Wu return -ENOTSUPP; 33405a5688eSDavid Wu 33505a5688eSDavid Wu return ctrl->set_pull(bank, pin_num, pull); 336f2e4e921SDavid Wu } 337f2e4e921SDavid Wu 338f2e4e921SDavid Wu static int rockchip_set_schmitt(struct rockchip_pin_bank *bank, 339f2e4e921SDavid Wu int pin_num, int enable) 340f2e4e921SDavid Wu { 341f2e4e921SDavid Wu struct rockchip_pinctrl_priv *priv = bank->priv; 342f2e4e921SDavid Wu struct rockchip_pin_ctrl *ctrl = priv->ctrl; 343f2e4e921SDavid Wu 344f2e4e921SDavid Wu debug("setting input schmitt of GPIO%d-%d to %d\n", bank->bank_num, 345f2e4e921SDavid Wu pin_num, enable); 346f2e4e921SDavid Wu 3475635c457SDavid Wu if (!ctrl->set_schmitt) 3485635c457SDavid Wu return -ENOTSUPP; 349f2e4e921SDavid Wu 3505635c457SDavid Wu return ctrl->set_schmitt(bank, pin_num, enable); 351f2e4e921SDavid Wu } 352f2e4e921SDavid Wu 353f2e4e921SDavid Wu /* set the pin config settings for a specified pin */ 354f2e4e921SDavid Wu static int rockchip_pinconf_set(struct rockchip_pin_bank *bank, 355f2e4e921SDavid Wu u32 pin, u32 param, u32 arg) 356f2e4e921SDavid Wu { 357f2e4e921SDavid Wu int rc; 3581e9d2e36SCody Xie struct gpio_desc desc; 3591e9d2e36SCody Xie char gpio_name[16]; 360f2e4e921SDavid Wu 361f2e4e921SDavid Wu switch (param) { 362f2e4e921SDavid Wu case PIN_CONFIG_BIAS_DISABLE: 363f2e4e921SDavid Wu case PIN_CONFIG_BIAS_PULL_UP: 364f2e4e921SDavid Wu case PIN_CONFIG_BIAS_PULL_DOWN: 365f2e4e921SDavid Wu case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: 366f2e4e921SDavid Wu case PIN_CONFIG_BIAS_BUS_HOLD: 367f2e4e921SDavid Wu rc = rockchip_set_pull(bank, pin, param); 368f2e4e921SDavid Wu if (rc) 369f2e4e921SDavid Wu return rc; 370f2e4e921SDavid Wu break; 371f2e4e921SDavid Wu 372f2e4e921SDavid Wu case PIN_CONFIG_DRIVE_STRENGTH: 373f2e4e921SDavid Wu rc = rockchip_set_drive_perpin(bank, pin, arg); 374f2e4e921SDavid Wu if (rc < 0) 375f2e4e921SDavid Wu return rc; 376f2e4e921SDavid Wu break; 377f2e4e921SDavid Wu 378f2e4e921SDavid Wu case PIN_CONFIG_INPUT_SCHMITT_ENABLE: 379f2e4e921SDavid Wu rc = rockchip_set_schmitt(bank, pin, arg); 380f2e4e921SDavid Wu if (rc < 0) 381f2e4e921SDavid Wu return rc; 382f2e4e921SDavid Wu break; 383f2e4e921SDavid Wu 3841e9d2e36SCody Xie case PIN_CONFIG_OUTPUT: 3851e9d2e36SCody Xie desc.flags = GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE; 3861e9d2e36SCody Xie if (!arg) desc.flags |= GPIOD_ACTIVE_LOW; 3871e9d2e36SCody Xie snprintf(gpio_name, 16, "%s%d", bank->name, pin); 3881e9d2e36SCody Xie rc = dm_gpio_lookup_name(gpio_name, &desc); 3891e9d2e36SCody Xie if (rc < 0) { 3901e9d2e36SCody Xie debug("%s: GPIO %s-%d lookup failed (ret=%d)\n", __func__, 3911e9d2e36SCody Xie bank->name, pin, rc); 392*04b7ba5aSCody Xie return rc; 3931e9d2e36SCody Xie } 3941e9d2e36SCody Xie 3951e9d2e36SCody Xie rc = dm_gpio_request(&desc, gpio_name); 3961e9d2e36SCody Xie if (rc < 0) { 3971e9d2e36SCody Xie debug("%s: GPIO %s-%d request failed (ret=%d)\n", __func__, 3981e9d2e36SCody Xie bank->name, pin, rc); 3991e9d2e36SCody Xie return rc; 4001e9d2e36SCody Xie } 4011e9d2e36SCody Xie dm_gpio_set_dir_flags(&desc, GPIOD_IS_OUT); 4021e9d2e36SCody Xie dm_gpio_set_value(&desc, arg ? 1 : 0); 4031e9d2e36SCody Xie debug("%s: GPIO %s-%d set to %d\n", __func__, bank->name, pin, arg); 4041e9d2e36SCody Xie break; 405f2e4e921SDavid Wu default: 406f2e4e921SDavid Wu break; 407f2e4e921SDavid Wu } 408f2e4e921SDavid Wu 409f2e4e921SDavid Wu return 0; 410f2e4e921SDavid Wu } 411f2e4e921SDavid Wu 412f2e4e921SDavid Wu static const struct pinconf_param rockchip_conf_params[] = { 413f2e4e921SDavid Wu { "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 }, 414f2e4e921SDavid Wu { "bias-bus-hold", PIN_CONFIG_BIAS_BUS_HOLD, 0 }, 415f2e4e921SDavid Wu { "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 }, 416f2e4e921SDavid Wu { "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 }, 4178fa6c062SDavid Wu { "bias-pull-pin-default", PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, 1 }, 418f2e4e921SDavid Wu { "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 0 }, 419f2e4e921SDavid Wu { "input-schmitt-disable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 0 }, 420f2e4e921SDavid Wu { "input-schmitt-enable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 1 }, 4211e9d2e36SCody Xie { "output-high", PIN_CONFIG_OUTPUT, 1, }, 4221e9d2e36SCody Xie { "output-low", PIN_CONFIG_OUTPUT, 0, }, 423f2e4e921SDavid Wu }; 424f2e4e921SDavid Wu 425f2e4e921SDavid Wu static int rockchip_pinconf_prop_name_to_param(const char *property, 426f2e4e921SDavid Wu u32 *default_value) 427f2e4e921SDavid Wu { 428f2e4e921SDavid Wu const struct pinconf_param *p, *end; 429f2e4e921SDavid Wu 430f2e4e921SDavid Wu p = rockchip_conf_params; 431f2e4e921SDavid Wu end = p + sizeof(rockchip_conf_params) / sizeof(struct pinconf_param); 432f2e4e921SDavid Wu 433f2e4e921SDavid Wu /* See if this pctldev supports this parameter */ 434f2e4e921SDavid Wu for (; p < end; p++) { 435f2e4e921SDavid Wu if (!strcmp(property, p->property)) { 436f2e4e921SDavid Wu *default_value = p->default_value; 437f2e4e921SDavid Wu return p->param; 438f2e4e921SDavid Wu } 439f2e4e921SDavid Wu } 440f2e4e921SDavid Wu 441f2e4e921SDavid Wu *default_value = 0; 442f2e4e921SDavid Wu return -EPERM; 443f2e4e921SDavid Wu } 444f2e4e921SDavid Wu 445f2e4e921SDavid Wu static int rockchip_pinctrl_set_state(struct udevice *dev, 446f2e4e921SDavid Wu struct udevice *config) 447f2e4e921SDavid Wu { 448f2e4e921SDavid Wu struct rockchip_pinctrl_priv *priv = dev_get_priv(dev); 449f2e4e921SDavid Wu struct rockchip_pin_ctrl *ctrl = priv->ctrl; 450f2e4e921SDavid Wu u32 cells[MAX_ROCKCHIP_PINS_ENTRIES * 4]; 451f2e4e921SDavid Wu u32 bank, pin, mux, conf, arg, default_val; 452f2e4e921SDavid Wu int ret, count, i; 453f2e4e921SDavid Wu const char *prop_name; 454f2e4e921SDavid Wu const void *value; 455f2e4e921SDavid Wu int prop_len, param; 456f2e4e921SDavid Wu const u32 *data; 457f2e4e921SDavid Wu ofnode node; 458d7965d03SJoseph Chen #if CONFIG_IS_ENABLED(OF_LIVE) 459f2e4e921SDavid Wu const struct device_node *np; 460f2e4e921SDavid Wu struct property *pp; 461f2e4e921SDavid Wu #else 462f2e4e921SDavid Wu int property_offset, pcfg_node; 463f2e4e921SDavid Wu const void *blob = gd->fdt_blob; 464f2e4e921SDavid Wu #endif 465f2e4e921SDavid Wu data = dev_read_prop(config, "rockchip,pins", &count); 466f2e4e921SDavid Wu if (count < 0) { 467f2e4e921SDavid Wu debug("%s: bad array size %d\n", __func__, count); 468f2e4e921SDavid Wu return -EINVAL; 469f2e4e921SDavid Wu } 470f2e4e921SDavid Wu 471f2e4e921SDavid Wu count /= sizeof(u32); 472f2e4e921SDavid Wu if (count > MAX_ROCKCHIP_PINS_ENTRIES * 4) { 473f2e4e921SDavid Wu debug("%s: unsupported pins array count %d\n", 474f2e4e921SDavid Wu __func__, count); 475f2e4e921SDavid Wu return -EINVAL; 476f2e4e921SDavid Wu } 477f2e4e921SDavid Wu 478f2e4e921SDavid Wu for (i = 0; i < count; i++) 479f2e4e921SDavid Wu cells[i] = fdt32_to_cpu(data[i]); 480f2e4e921SDavid Wu 481f2e4e921SDavid Wu for (i = 0; i < (count >> 2); i++) { 482f2e4e921SDavid Wu bank = cells[4 * i + 0]; 483f2e4e921SDavid Wu pin = cells[4 * i + 1]; 484f2e4e921SDavid Wu mux = cells[4 * i + 2]; 485f2e4e921SDavid Wu conf = cells[4 * i + 3]; 486f2e4e921SDavid Wu 487f2e4e921SDavid Wu ret = rockchip_verify_config(dev, bank, pin); 488f2e4e921SDavid Wu if (ret) 489f2e4e921SDavid Wu return ret; 490f2e4e921SDavid Wu 491f2e4e921SDavid Wu ret = rockchip_set_mux(&ctrl->pin_banks[bank], pin, mux); 492f2e4e921SDavid Wu if (ret) 493f2e4e921SDavid Wu return ret; 494f2e4e921SDavid Wu 495f2e4e921SDavid Wu node = ofnode_get_by_phandle(conf); 496f2e4e921SDavid Wu if (!ofnode_valid(node)) 497f2e4e921SDavid Wu return -ENODEV; 498d7965d03SJoseph Chen #if CONFIG_IS_ENABLED(OF_LIVE) 499f2e4e921SDavid Wu np = ofnode_to_np(node); 500f2e4e921SDavid Wu for (pp = np->properties; pp; pp = pp->next) { 501f2e4e921SDavid Wu prop_name = pp->name; 502f2e4e921SDavid Wu prop_len = pp->length; 503f2e4e921SDavid Wu value = pp->value; 504f2e4e921SDavid Wu #else 505f2e4e921SDavid Wu pcfg_node = ofnode_to_offset(node); 506f2e4e921SDavid Wu fdt_for_each_property_offset(property_offset, blob, pcfg_node) { 507f2e4e921SDavid Wu value = fdt_getprop_by_offset(blob, property_offset, 508f2e4e921SDavid Wu &prop_name, &prop_len); 509f2e4e921SDavid Wu if (!value) 510f2e4e921SDavid Wu return -ENOENT; 511f2e4e921SDavid Wu #endif 512f2e4e921SDavid Wu param = rockchip_pinconf_prop_name_to_param(prop_name, 513f2e4e921SDavid Wu &default_val); 514f2e4e921SDavid Wu if (param < 0) 515f2e4e921SDavid Wu break; 516f2e4e921SDavid Wu 517f2e4e921SDavid Wu if (prop_len >= sizeof(fdt32_t)) 518f2e4e921SDavid Wu arg = fdt32_to_cpu(*(fdt32_t *)value); 519f2e4e921SDavid Wu else 520f2e4e921SDavid Wu arg = default_val; 521f2e4e921SDavid Wu 522f2e4e921SDavid Wu ret = rockchip_pinconf_set(&ctrl->pin_banks[bank], pin, 523f2e4e921SDavid Wu param, arg); 524f2e4e921SDavid Wu if (ret) { 525f2e4e921SDavid Wu debug("%s: rockchip_pinconf_set fail: %d\n", 526f2e4e921SDavid Wu __func__, ret); 527f2e4e921SDavid Wu return ret; 528f2e4e921SDavid Wu } 529f2e4e921SDavid Wu } 530f2e4e921SDavid Wu } 531f2e4e921SDavid Wu 532f2e4e921SDavid Wu return 0; 533f2e4e921SDavid Wu } 534f2e4e921SDavid Wu 535f2e4e921SDavid Wu static int rockchip_pinctrl_get_pins_count(struct udevice *dev) 536f2e4e921SDavid Wu { 537f2e4e921SDavid Wu struct rockchip_pinctrl_priv *priv = dev_get_priv(dev); 538f2e4e921SDavid Wu struct rockchip_pin_ctrl *ctrl = priv->ctrl; 539f2e4e921SDavid Wu 540f2e4e921SDavid Wu return ctrl->nr_pins; 541f2e4e921SDavid Wu } 542f2e4e921SDavid Wu 543f2e4e921SDavid Wu const struct pinctrl_ops rockchip_pinctrl_ops = { 544f2e4e921SDavid Wu .get_pins_count = rockchip_pinctrl_get_pins_count, 545f2e4e921SDavid Wu .set_state = rockchip_pinctrl_set_state, 546f2e4e921SDavid Wu .get_gpio_mux = rockchip_pinctrl_get_gpio_mux, 547f2e4e921SDavid Wu }; 548f2e4e921SDavid Wu 549f2e4e921SDavid Wu /* retrieve the soc specific data */ 550f2e4e921SDavid Wu static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(struct udevice *dev) 551f2e4e921SDavid Wu { 552f2e4e921SDavid Wu struct rockchip_pinctrl_priv *priv = dev_get_priv(dev); 553f2e4e921SDavid Wu struct rockchip_pin_ctrl *ctrl = 554f2e4e921SDavid Wu (struct rockchip_pin_ctrl *)dev_get_driver_data(dev); 555f2e4e921SDavid Wu struct rockchip_pin_bank *bank; 556f2e4e921SDavid Wu int grf_offs, pmu_offs, drv_grf_offs, drv_pmu_offs, i, j; 5573624458aSJianqun Xu u32 nr_pins; 558f2e4e921SDavid Wu 559f2e4e921SDavid Wu grf_offs = ctrl->grf_mux_offset; 560f2e4e921SDavid Wu pmu_offs = ctrl->pmu_mux_offset; 561f2e4e921SDavid Wu drv_pmu_offs = ctrl->pmu_drv_offset; 562f2e4e921SDavid Wu drv_grf_offs = ctrl->grf_drv_offset; 563f2e4e921SDavid Wu bank = ctrl->pin_banks; 564f2e4e921SDavid Wu 5653624458aSJianqun Xu nr_pins = 0; 566f2e4e921SDavid Wu for (i = 0; i < ctrl->nr_banks; ++i, ++bank) { 567f2e4e921SDavid Wu int bank_pins = 0; 568f2e4e921SDavid Wu 569f2e4e921SDavid Wu bank->priv = priv; 5703624458aSJianqun Xu bank->pin_base = nr_pins; 5713624458aSJianqun Xu nr_pins += bank->nr_pins; 572f2e4e921SDavid Wu 573f2e4e921SDavid Wu /* calculate iomux and drv offsets */ 574f2e4e921SDavid Wu for (j = 0; j < 4; j++) { 575f2e4e921SDavid Wu struct rockchip_iomux *iom = &bank->iomux[j]; 576f2e4e921SDavid Wu struct rockchip_drv *drv = &bank->drv[j]; 577f2e4e921SDavid Wu int inc; 578f2e4e921SDavid Wu 5793624458aSJianqun Xu if (bank_pins >= nr_pins) 580f2e4e921SDavid Wu break; 581f2e4e921SDavid Wu 582f2e4e921SDavid Wu /* preset iomux offset value, set new start value */ 583f2e4e921SDavid Wu if (iom->offset >= 0) { 5844730583aSDavid Wu if ((iom->type & IOMUX_SOURCE_PMU) || (iom->type & IOMUX_L_SOURCE_PMU)) 585f2e4e921SDavid Wu pmu_offs = iom->offset; 586f2e4e921SDavid Wu else 587f2e4e921SDavid Wu grf_offs = iom->offset; 588f2e4e921SDavid Wu } else { /* set current iomux offset */ 5894730583aSDavid Wu iom->offset = ((iom->type & IOMUX_SOURCE_PMU) || 5904730583aSDavid Wu (iom->type & IOMUX_L_SOURCE_PMU)) ? 591f2e4e921SDavid Wu pmu_offs : grf_offs; 592f2e4e921SDavid Wu } 593f2e4e921SDavid Wu 594f2e4e921SDavid Wu /* preset drv offset value, set new start value */ 595f2e4e921SDavid Wu if (drv->offset >= 0) { 596f2e4e921SDavid Wu if (iom->type & IOMUX_SOURCE_PMU) 597f2e4e921SDavid Wu drv_pmu_offs = drv->offset; 598f2e4e921SDavid Wu else 599f2e4e921SDavid Wu drv_grf_offs = drv->offset; 600f2e4e921SDavid Wu } else { /* set current drv offset */ 601f2e4e921SDavid Wu drv->offset = (iom->type & IOMUX_SOURCE_PMU) ? 602f2e4e921SDavid Wu drv_pmu_offs : drv_grf_offs; 603f2e4e921SDavid Wu } 604f2e4e921SDavid Wu 605f2e4e921SDavid Wu debug("bank %d, iomux %d has iom_offset 0x%x drv_offset 0x%x\n", 606f2e4e921SDavid Wu i, j, iom->offset, drv->offset); 607f2e4e921SDavid Wu 608f2e4e921SDavid Wu /* 609f2e4e921SDavid Wu * Increase offset according to iomux width. 610f2e4e921SDavid Wu * 4bit iomux'es are spread over two registers. 611f2e4e921SDavid Wu */ 612f2e4e921SDavid Wu inc = (iom->type & (IOMUX_WIDTH_4BIT | 613b8d3e6ffSJianqun Xu IOMUX_WIDTH_3BIT | 614b8d3e6ffSJianqun Xu IOMUX_8WIDTH_2BIT)) ? 8 : 4; 615b8d3e6ffSJianqun Xu if ((iom->type & IOMUX_SOURCE_PMU) || (iom->type & IOMUX_L_SOURCE_PMU)) 616f2e4e921SDavid Wu pmu_offs += inc; 617f2e4e921SDavid Wu else 618f2e4e921SDavid Wu grf_offs += inc; 619f2e4e921SDavid Wu 620f2e4e921SDavid Wu /* 621f2e4e921SDavid Wu * Increase offset according to drv width. 622f2e4e921SDavid Wu * 3bit drive-strenth'es are spread over two registers. 623f2e4e921SDavid Wu */ 624f2e4e921SDavid Wu if ((drv->drv_type == DRV_TYPE_IO_1V8_3V0_AUTO) || 625f2e4e921SDavid Wu (drv->drv_type == DRV_TYPE_IO_3V3_ONLY)) 626f2e4e921SDavid Wu inc = 8; 627f2e4e921SDavid Wu else 628f2e4e921SDavid Wu inc = 4; 629f2e4e921SDavid Wu 630f2e4e921SDavid Wu if (iom->type & IOMUX_SOURCE_PMU) 631f2e4e921SDavid Wu drv_pmu_offs += inc; 632f2e4e921SDavid Wu else 633f2e4e921SDavid Wu drv_grf_offs += inc; 634f2e4e921SDavid Wu 635f2e4e921SDavid Wu bank_pins += 8; 636f2e4e921SDavid Wu } 637f2e4e921SDavid Wu 638f2e4e921SDavid Wu /* calculate the per-bank recalced_mask */ 639f2e4e921SDavid Wu for (j = 0; j < ctrl->niomux_recalced; j++) { 640f2e4e921SDavid Wu int pin = 0; 641f2e4e921SDavid Wu 642f2e4e921SDavid Wu if (ctrl->iomux_recalced[j].num == bank->bank_num) { 643f2e4e921SDavid Wu pin = ctrl->iomux_recalced[j].pin; 644f2e4e921SDavid Wu bank->recalced_mask |= BIT(pin); 645f2e4e921SDavid Wu } 646f2e4e921SDavid Wu } 647f2e4e921SDavid Wu 648f2e4e921SDavid Wu /* calculate the per-bank route_mask */ 649f2e4e921SDavid Wu for (j = 0; j < ctrl->niomux_routes; j++) { 650f2e4e921SDavid Wu int pin = 0; 651f2e4e921SDavid Wu 652f2e4e921SDavid Wu if (ctrl->iomux_routes[j].bank_num == bank->bank_num) { 653f2e4e921SDavid Wu pin = ctrl->iomux_routes[j].pin; 654f2e4e921SDavid Wu bank->route_mask |= BIT(pin); 655f2e4e921SDavid Wu } 656f2e4e921SDavid Wu } 657f2e4e921SDavid Wu } 658f2e4e921SDavid Wu 6593624458aSJianqun Xu WARN_ON(nr_pins != ctrl->nr_pins); 6603624458aSJianqun Xu 661f2e4e921SDavid Wu return ctrl; 662f2e4e921SDavid Wu } 663f2e4e921SDavid Wu 664f2e4e921SDavid Wu int rockchip_pinctrl_probe(struct udevice *dev) 665f2e4e921SDavid Wu { 666f2e4e921SDavid Wu struct rockchip_pinctrl_priv *priv = dev_get_priv(dev); 667f2e4e921SDavid Wu struct rockchip_pin_ctrl *ctrl; 668f2e4e921SDavid Wu struct udevice *syscon; 669f2e4e921SDavid Wu struct regmap *regmap; 670f2e4e921SDavid Wu int ret = 0; 671f2e4e921SDavid Wu 672f2e4e921SDavid Wu /* get rockchip grf syscon phandle */ 673f2e4e921SDavid Wu ret = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, "rockchip,grf", 674f2e4e921SDavid Wu &syscon); 675f2e4e921SDavid Wu if (ret) { 676f2e4e921SDavid Wu debug("unable to find rockchip,grf syscon device (%d)\n", ret); 677f2e4e921SDavid Wu return ret; 678f2e4e921SDavid Wu } 679f2e4e921SDavid Wu 680f2e4e921SDavid Wu /* get grf-reg base address */ 681f2e4e921SDavid Wu regmap = syscon_get_regmap(syscon); 682f2e4e921SDavid Wu if (!regmap) { 683f2e4e921SDavid Wu debug("unable to find rockchip grf regmap\n"); 684f2e4e921SDavid Wu return -ENODEV; 685f2e4e921SDavid Wu } 686f2e4e921SDavid Wu priv->regmap_base = regmap; 687f2e4e921SDavid Wu 688f2e4e921SDavid Wu /* option: get pmu-reg base address */ 689f2e4e921SDavid Wu ret = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, "rockchip,pmu", 690f2e4e921SDavid Wu &syscon); 691f2e4e921SDavid Wu if (!ret) { 692f2e4e921SDavid Wu /* get pmugrf-reg base address */ 693f2e4e921SDavid Wu regmap = syscon_get_regmap(syscon); 694f2e4e921SDavid Wu if (!regmap) { 695f2e4e921SDavid Wu debug("unable to find rockchip pmu regmap\n"); 696f2e4e921SDavid Wu return -ENODEV; 697f2e4e921SDavid Wu } 698f2e4e921SDavid Wu priv->regmap_pmu = regmap; 699f2e4e921SDavid Wu } 700f2e4e921SDavid Wu 701f2e4e921SDavid Wu ctrl = rockchip_pinctrl_get_soc_data(dev); 702f2e4e921SDavid Wu if (!ctrl) { 703f2e4e921SDavid Wu debug("driver data not available\n"); 704f2e4e921SDavid Wu return -EINVAL; 705f2e4e921SDavid Wu } 706f2e4e921SDavid Wu 707f2e4e921SDavid Wu priv->ctrl = ctrl; 708f2e4e921SDavid Wu return 0; 709f2e4e921SDavid Wu } 710