123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * (C) Copyright 2019 Rockchip Electronics Co., Ltd
- */
- #include <common.h>
- #include <dm.h>
- #include <log.h>
- #include <dm/pinctrl.h>
- #include <regmap.h>
- #include <syscon.h>
- #include <fdtdec.h>
- #include <linux/bitops.h>
- #include <linux/libfdt.h>
- #include <asm/global_data.h>
- #include "pinctrl-rockchip.h"
- #define MAX_ROCKCHIP_PINS_ENTRIES 30
- #define MAX_ROCKCHIP_GPIO_PER_BANK 32
- #define RK_FUNC_GPIO 0
- static int rockchip_verify_config(struct udevice *dev, u32 bank, u32 pin)
- {
- struct rockchip_pinctrl_priv *priv = dev_get_priv(dev);
- struct rockchip_pin_ctrl *ctrl = priv->ctrl;
- if (bank >= ctrl->nr_banks) {
- debug("pin conf bank %d >= nbanks %d\n", bank, ctrl->nr_banks);
- return -EINVAL;
- }
- if (pin >= MAX_ROCKCHIP_GPIO_PER_BANK) {
- debug("pin conf pin %d >= %d\n", pin,
- MAX_ROCKCHIP_GPIO_PER_BANK);
- return -EINVAL;
- }
- return 0;
- }
- void rockchip_get_recalced_mux(struct rockchip_pin_bank *bank, int pin,
- int *reg, u8 *bit, int *mask)
- {
- struct rockchip_pinctrl_priv *priv = bank->priv;
- struct rockchip_pin_ctrl *ctrl = priv->ctrl;
- struct rockchip_mux_recalced_data *data;
- int i;
- for (i = 0; i < ctrl->niomux_recalced; i++) {
- data = &ctrl->iomux_recalced[i];
- if (data->num == bank->bank_num &&
- data->pin == pin)
- break;
- }
- if (i >= ctrl->niomux_recalced)
- return;
- *reg = data->reg;
- *mask = data->mask;
- *bit = data->bit;
- }
- bool rockchip_get_mux_route(struct rockchip_pin_bank *bank, int pin,
- int mux, u32 *reg, u32 *value)
- {
- struct rockchip_pinctrl_priv *priv = bank->priv;
- struct rockchip_pin_ctrl *ctrl = priv->ctrl;
- struct rockchip_mux_route_data *data;
- int i;
- for (i = 0; i < ctrl->niomux_routes; i++) {
- data = &ctrl->iomux_routes[i];
- if (data->bank_num == bank->bank_num &&
- data->pin == pin && data->func == mux)
- break;
- }
- if (i >= ctrl->niomux_routes)
- return false;
- *reg = data->route_offset;
- *value = data->route_val;
- return true;
- }
- int rockchip_get_mux_data(int mux_type, int pin, u8 *bit, int *mask)
- {
- int offset = 0;
- if (mux_type & IOMUX_WIDTH_4BIT) {
- if ((pin % 8) >= 4)
- offset = 0x4;
- *bit = (pin % 4) * 4;
- *mask = 0xf;
- } else if (mux_type & IOMUX_WIDTH_3BIT) {
- /*
- * pin0 ~ pin4 are at first register, and
- * pin5 ~ pin7 are at second register.
- */
- if ((pin % 8) >= 5)
- offset = 0x4;
- *bit = (pin % 8 % 5) * 3;
- *mask = 0x7;
- } else {
- *bit = (pin % 8) * 2;
- *mask = 0x3;
- }
- return offset;
- }
- static int rockchip_get_mux(struct rockchip_pin_bank *bank, int pin)
- {
- struct rockchip_pinctrl_priv *priv = bank->priv;
- int iomux_num = (pin / 8);
- struct regmap *regmap;
- unsigned int val;
- int reg, ret, mask, mux_type;
- u8 bit;
- if (iomux_num > 3)
- return -EINVAL;
- if (bank->iomux[iomux_num].type & IOMUX_UNROUTED) {
- debug("pin %d is unrouted\n", pin);
- return -EINVAL;
- }
- if (bank->iomux[iomux_num].type & IOMUX_GPIO_ONLY)
- return RK_FUNC_GPIO;
- regmap = (bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU)
- ? priv->regmap_pmu : priv->regmap_base;
- /* get basic quadrupel of mux registers and the correct reg inside */
- mux_type = bank->iomux[iomux_num].type;
- reg = bank->iomux[iomux_num].offset;
- reg += rockchip_get_mux_data(mux_type, pin, &bit, &mask);
- if (bank->recalced_mask & BIT(pin))
- rockchip_get_recalced_mux(bank, pin, ®, &bit, &mask);
- ret = regmap_read(regmap, reg, &val);
- if (ret)
- return ret;
- return ((val >> bit) & mask);
- }
- static int rockchip_pinctrl_get_gpio_mux(struct udevice *dev, int banknum,
- int index)
- { struct rockchip_pinctrl_priv *priv = dev_get_priv(dev);
- struct rockchip_pin_ctrl *ctrl = priv->ctrl;
- return rockchip_get_mux(&ctrl->pin_banks[banknum], index);
- }
- static int rockchip_verify_mux(struct rockchip_pin_bank *bank,
- int pin, int mux)
- {
- int iomux_num = (pin / 8);
- if (iomux_num > 3)
- return -EINVAL;
- if (bank->iomux[iomux_num].type & IOMUX_UNROUTED) {
- debug("pin %d is unrouted\n", pin);
- return -EINVAL;
- }
- if (bank->iomux[iomux_num].type & IOMUX_GPIO_ONLY) {
- if (mux != IOMUX_GPIO_ONLY) {
- debug("pin %d only supports a gpio mux\n", pin);
- return -ENOTSUPP;
- }
- }
- return 0;
- }
- /*
- * Set a new mux function for a pin.
- *
- * The register is divided into the upper and lower 16 bit. When changing
- * a value, the previous register value is not read and changed. Instead
- * it seems the changed bits are marked in the upper 16 bit, while the
- * changed value gets set in the same offset in the lower 16 bit.
- * All pin settings seem to be 2 bit wide in both the upper and lower
- * parts.
- * @bank: pin bank to change
- * @pin: pin to change
- * @mux: new mux function to set
- */
- static int rockchip_set_mux(struct rockchip_pin_bank *bank, int pin, int mux)
- {
- struct rockchip_pinctrl_priv *priv = bank->priv;
- struct rockchip_pin_ctrl *ctrl = priv->ctrl;
- int iomux_num = (pin / 8);
- int ret;
- ret = rockchip_verify_mux(bank, pin, mux);
- if (ret < 0)
- return ret;
- if (bank->iomux[iomux_num].type & IOMUX_GPIO_ONLY)
- return 0;
- debug("setting mux of GPIO%d-%d to %d\n", bank->bank_num, pin, mux);
- if (!ctrl->set_mux)
- return -ENOTSUPP;
- ret = ctrl->set_mux(bank, pin, mux);
- return ret;
- }
- 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 }
- };
- int rockchip_translate_drive_value(int type, int strength)
- {
- int i, ret;
- ret = -EINVAL;
- for (i = 0; i < ARRAY_SIZE(rockchip_perpin_drv_list[type]); i++) {
- if (rockchip_perpin_drv_list[type][i] == strength) {
- ret = i;
- break;
- } else if (rockchip_perpin_drv_list[type][i] < 0) {
- ret = rockchip_perpin_drv_list[type][i];
- break;
- }
- }
- return ret;
- }
- static int rockchip_set_drive_perpin(struct rockchip_pin_bank *bank,
- int pin_num, int strength)
- {
- struct rockchip_pinctrl_priv *priv = bank->priv;
- struct rockchip_pin_ctrl *ctrl = priv->ctrl;
- debug("setting drive of GPIO%d-%d to %d\n", bank->bank_num,
- pin_num, strength);
- if (!ctrl->set_drive)
- return -ENOTSUPP;
- return ctrl->set_drive(bank, pin_num, strength);
- }
- 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
- },
- };
- int rockchip_translate_pull_value(int type, int pull)
- {
- int i, ret;
- ret = -EINVAL;
- for (i = 0; i < ARRAY_SIZE(rockchip_pull_list[type]);
- i++) {
- if (rockchip_pull_list[type][i] == pull) {
- ret = i;
- break;
- }
- }
- return ret;
- }
- static int rockchip_set_pull(struct rockchip_pin_bank *bank,
- int pin_num, int pull)
- {
- struct rockchip_pinctrl_priv *priv = bank->priv;
- struct rockchip_pin_ctrl *ctrl = priv->ctrl;
- debug("setting pull of GPIO%d-%d to %d\n", bank->bank_num,
- pin_num, pull);
- if (!ctrl->set_pull)
- return -ENOTSUPP;
- return ctrl->set_pull(bank, pin_num, pull);
- }
- static int rockchip_set_schmitt(struct rockchip_pin_bank *bank,
- int pin_num, int enable)
- {
- struct rockchip_pinctrl_priv *priv = bank->priv;
- struct rockchip_pin_ctrl *ctrl = priv->ctrl;
- debug("setting input schmitt of GPIO%d-%d to %d\n", bank->bank_num,
- pin_num, enable);
- if (!ctrl->set_schmitt)
- return -ENOTSUPP;
- return ctrl->set_schmitt(bank, pin_num, enable);
- }
- /* set the pin config settings for a specified pin */
- static int rockchip_pinconf_set(struct rockchip_pin_bank *bank,
- u32 pin, u32 param, u32 arg)
- {
- int rc;
- switch (param) {
- case PIN_CONFIG_BIAS_DISABLE:
- case PIN_CONFIG_BIAS_PULL_UP:
- case PIN_CONFIG_BIAS_PULL_DOWN:
- case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT:
- case PIN_CONFIG_BIAS_BUS_HOLD:
- rc = rockchip_set_pull(bank, pin, param);
- if (rc)
- return rc;
- break;
- case PIN_CONFIG_DRIVE_STRENGTH:
- rc = rockchip_set_drive_perpin(bank, pin, arg);
- if (rc < 0)
- return rc;
- break;
- case PIN_CONFIG_INPUT_SCHMITT_ENABLE:
- rc = rockchip_set_schmitt(bank, pin, arg);
- if (rc < 0)
- return rc;
- break;
- default:
- break;
- }
- return 0;
- }
- static const struct pinconf_param rockchip_conf_params[] = {
- { "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 },
- { "bias-bus-hold", PIN_CONFIG_BIAS_BUS_HOLD, 0 },
- { "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 },
- { "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 },
- { "bias-pull-pin-default", PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, 1 },
- { "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 0 },
- { "input-schmitt-disable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 0 },
- { "input-schmitt-enable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 1 },
- };
- static int rockchip_pinconf_prop_name_to_param(const char *property,
- u32 *default_value)
- {
- const struct pinconf_param *p, *end;
- p = rockchip_conf_params;
- end = p + sizeof(rockchip_conf_params) / sizeof(struct pinconf_param);
- /* See if this pctldev supports this parameter */
- for (; p < end; p++) {
- if (!strcmp(property, p->property)) {
- *default_value = p->default_value;
- return p->param;
- }
- }
- *default_value = 0;
- return -EPERM;
- }
- static int rockchip_pinctrl_set_state(struct udevice *dev,
- struct udevice *config)
- {
- struct rockchip_pinctrl_priv *priv = dev_get_priv(dev);
- struct rockchip_pin_ctrl *ctrl = priv->ctrl;
- u32 cells[MAX_ROCKCHIP_PINS_ENTRIES * 4];
- u32 bank, pin, mux, conf, arg, default_val;
- int ret, count, i;
- const char *prop_name;
- const void *value;
- int prop_len, param;
- const u32 *data;
- ofnode node;
- #ifdef CONFIG_OF_LIVE
- const struct device_node *np;
- struct property *pp;
- #else
- int property_offset, pcfg_node;
- const void *blob = gd->fdt_blob;
- #endif
- data = dev_read_prop(config, "rockchip,pins", &count);
- if (count < 0) {
- debug("%s: bad array size %d\n", __func__, count);
- return -EINVAL;
- }
- count /= sizeof(u32);
- if (count > MAX_ROCKCHIP_PINS_ENTRIES * 4) {
- debug("%s: unsupported pins array count %d\n",
- __func__, count);
- return -EINVAL;
- }
- for (i = 0; i < count; i++)
- cells[i] = fdt32_to_cpu(data[i]);
- for (i = 0; i < (count >> 2); i++) {
- bank = cells[4 * i + 0];
- pin = cells[4 * i + 1];
- mux = cells[4 * i + 2];
- conf = cells[4 * i + 3];
- ret = rockchip_verify_config(dev, bank, pin);
- if (ret)
- return ret;
- ret = rockchip_set_mux(&ctrl->pin_banks[bank], pin, mux);
- if (ret)
- return ret;
- node = ofnode_get_by_phandle(conf);
- if (!ofnode_valid(node))
- return -ENODEV;
- #ifdef CONFIG_OF_LIVE
- np = ofnode_to_np(node);
- for (pp = np->properties; pp; pp = pp->next) {
- prop_name = pp->name;
- prop_len = pp->length;
- value = pp->value;
- #else
- pcfg_node = ofnode_to_offset(node);
- fdt_for_each_property_offset(property_offset, blob, pcfg_node) {
- value = fdt_getprop_by_offset(blob, property_offset,
- &prop_name, &prop_len);
- if (!value)
- return -ENOENT;
- #endif
- param = rockchip_pinconf_prop_name_to_param(prop_name,
- &default_val);
- if (param < 0)
- break;
- if (prop_len >= sizeof(fdt32_t))
- arg = fdt32_to_cpu(*(fdt32_t *)value);
- else
- arg = default_val;
- ret = rockchip_pinconf_set(&ctrl->pin_banks[bank], pin,
- param, arg);
- if (ret) {
- debug("%s: rockchip_pinconf_set fail: %d\n",
- __func__, ret);
- return ret;
- }
- }
- }
- return 0;
- }
- const struct pinctrl_ops rockchip_pinctrl_ops = {
- .set_state = rockchip_pinctrl_set_state,
- .get_gpio_mux = rockchip_pinctrl_get_gpio_mux,
- };
- /* retrieve the soc specific data */
- static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(struct udevice *dev)
- {
- struct rockchip_pinctrl_priv *priv = dev_get_priv(dev);
- struct rockchip_pin_ctrl *ctrl =
- (struct rockchip_pin_ctrl *)dev_get_driver_data(dev);
- struct rockchip_pin_bank *bank;
- int grf_offs, pmu_offs, drv_grf_offs, drv_pmu_offs, i, j;
- 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;
- bank->priv = priv;
- bank->pin_base = ctrl->nr_pins;
- ctrl->nr_pins += bank->nr_pins;
- /* 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 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 iomux offset */
- iom->offset = (iom->type & IOMUX_SOURCE_PMU) ?
- pmu_offs : grf_offs;
- }
- /* 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;
- }
- debug("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.
- * 4bit iomux'es are spread over two registers.
- */
- inc = (iom->type & (IOMUX_WIDTH_4BIT |
- IOMUX_WIDTH_3BIT |
- IOMUX_8WIDTH_2BIT)) ? 8 : 4;
- if (iom->type & IOMUX_SOURCE_PMU)
- pmu_offs += inc;
- 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;
- }
- /* calculate the per-bank recalced_mask */
- for (j = 0; j < ctrl->niomux_recalced; j++) {
- int pin = 0;
- if (ctrl->iomux_recalced[j].num == bank->bank_num) {
- pin = ctrl->iomux_recalced[j].pin;
- bank->recalced_mask |= BIT(pin);
- }
- }
- /* calculate the per-bank route_mask */
- for (j = 0; j < ctrl->niomux_routes; j++) {
- int pin = 0;
- if (ctrl->iomux_routes[j].bank_num == bank->bank_num) {
- pin = ctrl->iomux_routes[j].pin;
- bank->route_mask |= BIT(pin);
- }
- }
- }
- return ctrl;
- }
- int rockchip_pinctrl_probe(struct udevice *dev)
- {
- struct rockchip_pinctrl_priv *priv = dev_get_priv(dev);
- struct rockchip_pin_ctrl *ctrl;
- struct udevice *syscon;
- struct regmap *regmap;
- int ret = 0;
- /* get rockchip grf syscon phandle */
- ret = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, "rockchip,grf",
- &syscon);
- if (ret) {
- debug("unable to find rockchip,grf syscon device (%d)\n", ret);
- return ret;
- }
- /* get grf-reg base address */
- regmap = syscon_get_regmap(syscon);
- if (!regmap) {
- debug("unable to find rockchip grf regmap\n");
- return -ENODEV;
- }
- priv->regmap_base = regmap;
- /* option: get pmu-reg base address */
- ret = uclass_get_device_by_phandle(UCLASS_SYSCON, dev, "rockchip,pmu",
- &syscon);
- if (!ret) {
- /* get pmugrf-reg base address */
- regmap = syscon_get_regmap(syscon);
- if (!regmap) {
- debug("unable to find rockchip pmu regmap\n");
- return -ENODEV;
- }
- priv->regmap_pmu = regmap;
- }
- ctrl = rockchip_pinctrl_get_soc_data(dev);
- if (!ctrl) {
- debug("driver data not available\n");
- return -EINVAL;
- }
- priv->ctrl = ctrl;
- return 0;
- }
|