11f8f7730SSimon Glass /*
21f8f7730SSimon Glass * (C) Copyright 2015 Google, Inc
31f8f7730SSimon Glass *
4d1aef94bSJianqun Xu * (C) Copyright 2008-2020 Rockchip Electronics
51f8f7730SSimon Glass * Peter, Software Engineering, <superpeter.cai@gmail.com>.
6d1aef94bSJianqun Xu * Jianqun Xu, Software Engineering, <jay.xu@rock-chips.com>.
71f8f7730SSimon Glass *
81f8f7730SSimon Glass * SPDX-License-Identifier: GPL-2.0+
91f8f7730SSimon Glass */
101f8f7730SSimon Glass
111f8f7730SSimon Glass #include <common.h>
121f8f7730SSimon Glass #include <dm.h>
13dbff1ed6SJianqun Xu #include <dm/of_access.h>
1448647828SSimon Glass #include <syscon.h>
151221ce45SMasahiro Yamada #include <linux/errno.h>
161f8f7730SSimon Glass #include <asm/gpio.h>
171f8f7730SSimon Glass #include <asm/io.h>
1848647828SSimon Glass #include <asm/arch/clock.h>
1948647828SSimon Glass #include <dm/pinctrl.h>
2048647828SSimon Glass #include <dt-bindings/clock/rk3288-cru.h>
211f8f7730SSimon Glass
22dbff1ed6SJianqun Xu #include "../pinctrl/rockchip/pinctrl-rockchip.h"
231f8f7730SSimon Glass
241f8f7730SSimon Glass #define OFFSET_TO_BIT(bit) (1UL << (bit))
251f8f7730SSimon Glass
26d1aef94bSJianqun Xu #ifdef CONFIG_ROCKCHIP_GPIO_V2
27d1aef94bSJianqun Xu #define REG_L(R) (R##_l)
28d1aef94bSJianqun Xu #define REG_H(R) (R##_h)
29d1aef94bSJianqun Xu #define READ_REG(REG) ((readl(REG_L(REG)) & 0xFFFF) | \
30d1aef94bSJianqun Xu ((readl(REG_H(REG)) & 0xFFFF) << 16))
31d1aef94bSJianqun Xu #define WRITE_REG(REG, VAL) \
32d1aef94bSJianqun Xu {\
33d1aef94bSJianqun Xu writel(((VAL) & 0xFFFF) | 0xFFFF0000, REG_L(REG)); \
34d1aef94bSJianqun Xu writel((((VAL) & 0xFFFF0000) >> 16) | 0xFFFF0000, REG_H(REG));\
35d1aef94bSJianqun Xu }
36d1aef94bSJianqun Xu #define CLRBITS_LE32(REG, MASK) WRITE_REG(REG, READ_REG(REG) & ~(MASK))
37d1aef94bSJianqun Xu #define SETBITS_LE32(REG, MASK) WRITE_REG(REG, READ_REG(REG) | (MASK))
38d1aef94bSJianqun Xu #define CLRSETBITS_LE32(REG, MASK, VAL) WRITE_REG(REG, \
39d1aef94bSJianqun Xu (READ_REG(REG) & ~(MASK)) | (VAL))
40d1aef94bSJianqun Xu
41d1aef94bSJianqun Xu #else
42d1aef94bSJianqun Xu #define READ_REG(REG) readl(REG)
43d1aef94bSJianqun Xu #define WRITE_REG(REG, VAL) writel(VAL, REG)
44d1aef94bSJianqun Xu #define CLRBITS_LE32(REG, MASK) clrbits_le32(REG, MASK)
45d1aef94bSJianqun Xu #define SETBITS_LE32(REG, MASK) setbits_le32(REG, MASK)
46d1aef94bSJianqun Xu #define CLRSETBITS_LE32(REG, MASK, VAL) clrsetbits_le32(REG, MASK, VAL)
47d1aef94bSJianqun Xu #endif
48d1aef94bSJianqun Xu
49d1aef94bSJianqun Xu
501f8f7730SSimon Glass struct rockchip_gpio_priv {
511f8f7730SSimon Glass struct rockchip_gpio_regs *regs;
5248647828SSimon Glass struct udevice *pinctrl;
5348647828SSimon Glass int bank;
541f8f7730SSimon Glass char name[2];
551f8f7730SSimon Glass };
561f8f7730SSimon Glass
rockchip_gpio_direction_input(struct udevice * dev,unsigned offset)571f8f7730SSimon Glass static int rockchip_gpio_direction_input(struct udevice *dev, unsigned offset)
581f8f7730SSimon Glass {
591f8f7730SSimon Glass struct rockchip_gpio_priv *priv = dev_get_priv(dev);
601f8f7730SSimon Glass struct rockchip_gpio_regs *regs = priv->regs;
611f8f7730SSimon Glass
62d1aef94bSJianqun Xu CLRBITS_LE32(®s->swport_ddr, OFFSET_TO_BIT(offset));
631f8f7730SSimon Glass
641f8f7730SSimon Glass return 0;
651f8f7730SSimon Glass }
661f8f7730SSimon Glass
rockchip_gpio_direction_output(struct udevice * dev,unsigned offset,int value)671f8f7730SSimon Glass static int rockchip_gpio_direction_output(struct udevice *dev, unsigned offset,
681f8f7730SSimon Glass int value)
691f8f7730SSimon Glass {
701f8f7730SSimon Glass struct rockchip_gpio_priv *priv = dev_get_priv(dev);
711f8f7730SSimon Glass struct rockchip_gpio_regs *regs = priv->regs;
721f8f7730SSimon Glass int mask = OFFSET_TO_BIT(offset);
731f8f7730SSimon Glass
74d1aef94bSJianqun Xu CLRSETBITS_LE32(®s->swport_dr, mask, value ? mask : 0);
75d1aef94bSJianqun Xu SETBITS_LE32(®s->swport_ddr, mask);
761f8f7730SSimon Glass
771f8f7730SSimon Glass return 0;
781f8f7730SSimon Glass }
791f8f7730SSimon Glass
rockchip_gpio_get_value(struct udevice * dev,unsigned offset)801f8f7730SSimon Glass static int rockchip_gpio_get_value(struct udevice *dev, unsigned offset)
811f8f7730SSimon Glass {
821f8f7730SSimon Glass struct rockchip_gpio_priv *priv = dev_get_priv(dev);
831f8f7730SSimon Glass struct rockchip_gpio_regs *regs = priv->regs;
841f8f7730SSimon Glass
857d0c2c3fSSimon Glass return readl(®s->ext_port) & OFFSET_TO_BIT(offset) ? 1 : 0;
861f8f7730SSimon Glass }
871f8f7730SSimon Glass
rockchip_gpio_set_value(struct udevice * dev,unsigned offset,int value)881f8f7730SSimon Glass static int rockchip_gpio_set_value(struct udevice *dev, unsigned offset,
891f8f7730SSimon Glass int value)
901f8f7730SSimon Glass {
911f8f7730SSimon Glass struct rockchip_gpio_priv *priv = dev_get_priv(dev);
921f8f7730SSimon Glass struct rockchip_gpio_regs *regs = priv->regs;
931f8f7730SSimon Glass int mask = OFFSET_TO_BIT(offset);
941f8f7730SSimon Glass
95d1aef94bSJianqun Xu CLRSETBITS_LE32(®s->swport_dr, mask, value ? mask : 0);
961f8f7730SSimon Glass
971f8f7730SSimon Glass return 0;
981f8f7730SSimon Glass }
991f8f7730SSimon Glass
rockchip_gpio_get_function(struct udevice * dev,unsigned offset)1001f8f7730SSimon Glass static int rockchip_gpio_get_function(struct udevice *dev, unsigned offset)
1011f8f7730SSimon Glass {
10248647828SSimon Glass #ifdef CONFIG_SPL_BUILD
10348647828SSimon Glass return -ENODATA;
10448647828SSimon Glass #else
10548647828SSimon Glass struct rockchip_gpio_priv *priv = dev_get_priv(dev);
10648647828SSimon Glass struct rockchip_gpio_regs *regs = priv->regs;
10748647828SSimon Glass bool is_output;
10848647828SSimon Glass int ret;
10948647828SSimon Glass
11048647828SSimon Glass ret = pinctrl_get_gpio_mux(priv->pinctrl, priv->bank, offset);
111e9c98b3bSJianqun Xu if (ret < 0) {
112e9c98b3bSJianqun Xu dev_err(dev, "fail to get gpio mux %d\n", ret);
11348647828SSimon Glass return ret;
114e9c98b3bSJianqun Xu }
11548647828SSimon Glass
11648647828SSimon Glass /* If it's not 0, then it is not a GPIO */
117e9c98b3bSJianqun Xu if (ret > 0)
11848647828SSimon Glass return GPIOF_FUNC;
119d1aef94bSJianqun Xu
120d1aef94bSJianqun Xu is_output = READ_REG(®s->swport_ddr) & OFFSET_TO_BIT(offset);
12148647828SSimon Glass
12248647828SSimon Glass return is_output ? GPIOF_OUTPUT : GPIOF_INPUT;
12348647828SSimon Glass #endif
1241f8f7730SSimon Glass }
1251f8f7730SSimon Glass
rockchip_gpio_probe(struct udevice * dev)1261f8f7730SSimon Glass static int rockchip_gpio_probe(struct udevice *dev)
1271f8f7730SSimon Glass {
1281f8f7730SSimon Glass struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
1291f8f7730SSimon Glass struct rockchip_gpio_priv *priv = dev_get_priv(dev);
130dbff1ed6SJianqun Xu struct rockchip_pinctrl_priv *pctrl_priv;
131dbff1ed6SJianqun Xu struct rockchip_pin_bank *bank;
132dbff1ed6SJianqun Xu char *end = NULL;
133dbff1ed6SJianqun Xu int id = -1, ret;
1341f8f7730SSimon Glass
13555efd69cSPhilipp Tomsich priv->regs = dev_read_addr_ptr(dev);
1360a5d7f3eSWyon Bi ret = uclass_get_device_by_seq(UCLASS_PINCTRL, 0, &priv->pinctrl);
1370a5d7f3eSWyon Bi if (ret) {
1383f603cbbSSimon Glass ret = uclass_first_device_err(UCLASS_PINCTRL, &priv->pinctrl);
139dbff1ed6SJianqun Xu if (ret) {
140dbff1ed6SJianqun Xu dev_err(dev, "failed to get pinctrl device %d\n", ret);
14148647828SSimon Glass return ret;
1429f32e0d2SJianqun Xu }
1430a5d7f3eSWyon Bi }
1447d13e745SJianqun Xu
145dbff1ed6SJianqun Xu pctrl_priv = dev_get_priv(priv->pinctrl);
146dbff1ed6SJianqun Xu if (!pctrl_priv) {
147dbff1ed6SJianqun Xu dev_err(dev, "failed to get pinctrl priv\n");
148dbff1ed6SJianqun Xu return -EINVAL;
149dbff1ed6SJianqun Xu }
150dbff1ed6SJianqun Xu
151dbff1ed6SJianqun Xu end = strrchr(dev->name, '@');
152dbff1ed6SJianqun Xu if (end)
153dbff1ed6SJianqun Xu id = trailing_strtoln(dev->name, end);
154d3909692SJianqun Xu if (id < 0)
155dbff1ed6SJianqun Xu dev_read_alias_seq(dev, &id);
156dbff1ed6SJianqun Xu
157d3909692SJianqun Xu if (id < 0 || id >= pctrl_priv->ctrl->nr_banks) {
158*42b90858SJoseph Chen dev_err(dev, "nr_banks=%d, bank id=%d invalid\n",
159*42b90858SJoseph Chen pctrl_priv->ctrl->nr_banks, id);
160dbff1ed6SJianqun Xu return -EINVAL;
161dbff1ed6SJianqun Xu }
162dbff1ed6SJianqun Xu
163dbff1ed6SJianqun Xu bank = &pctrl_priv->ctrl->pin_banks[id];
164dbff1ed6SJianqun Xu if (bank->bank_num != id) {
165dbff1ed6SJianqun Xu dev_err(dev, "bank id mismatch with pinctrl\n");
166dbff1ed6SJianqun Xu return -EINVAL;
167dbff1ed6SJianqun Xu }
168dbff1ed6SJianqun Xu
169dbff1ed6SJianqun Xu priv->bank = bank->bank_num;
170dbff1ed6SJianqun Xu uc_priv->gpio_count = bank->nr_pins;
171dbff1ed6SJianqun Xu uc_priv->gpio_base = bank->pin_base;
172dbff1ed6SJianqun Xu uc_priv->bank_name = bank->name;
173dbff1ed6SJianqun Xu
1741f8f7730SSimon Glass return 0;
1751f8f7730SSimon Glass }
1761f8f7730SSimon Glass
1771f8f7730SSimon Glass static const struct dm_gpio_ops gpio_rockchip_ops = {
1781f8f7730SSimon Glass .direction_input = rockchip_gpio_direction_input,
1791f8f7730SSimon Glass .direction_output = rockchip_gpio_direction_output,
1801f8f7730SSimon Glass .get_value = rockchip_gpio_get_value,
1811f8f7730SSimon Glass .set_value = rockchip_gpio_set_value,
1821f8f7730SSimon Glass .get_function = rockchip_gpio_get_function,
1831f8f7730SSimon Glass };
1841f8f7730SSimon Glass
1851f8f7730SSimon Glass static const struct udevice_id rockchip_gpio_ids[] = {
1861f8f7730SSimon Glass { .compatible = "rockchip,gpio-bank" },
1871f8f7730SSimon Glass { }
1881f8f7730SSimon Glass };
1891f8f7730SSimon Glass
1901f8f7730SSimon Glass U_BOOT_DRIVER(gpio_rockchip) = {
1911f8f7730SSimon Glass .name = "gpio_rockchip",
1921f8f7730SSimon Glass .id = UCLASS_GPIO,
1931f8f7730SSimon Glass .of_match = rockchip_gpio_ids,
1941f8f7730SSimon Glass .ops = &gpio_rockchip_ops,
1951f8f7730SSimon Glass .priv_auto_alloc_size = sizeof(struct rockchip_gpio_priv),
1961f8f7730SSimon Glass .probe = rockchip_gpio_probe,
1971f8f7730SSimon Glass };
198