blob: 24ba12dd820e68199499e53b05b13889e130d6c3 [file] [log] [blame]
Tom Rini10e47792018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Simon Glass8fa4d5a2015-08-30 16:55:27 -06002/*
3 * (C) Copyright 2015 Google, Inc
4 *
5 * (C) Copyright 2008-2014 Rockchip Electronics
6 * Peter, Software Engineering, <superpeter.cai@gmail.com>.
Simon Glass8fa4d5a2015-08-30 16:55:27 -06007 */
8
Simon Glass8fa4d5a2015-08-30 16:55:27 -06009#include <dm.h>
Simon Glass9c73e742016-01-21 19:44:09 -070010#include <syscon.h>
Masahiro Yamada56a931c2016-09-21 11:28:55 +090011#include <linux/errno.h>
Simon Glass8fa4d5a2015-08-30 16:55:27 -060012#include <asm/gpio.h>
Kever Yang9fbe17c2019-03-28 11:01:23 +080013#include <asm/arch-rockchip/clock.h>
Jonas Karlmanc8078412023-03-19 18:39:51 +000014#include <asm/arch-rockchip/hardware.h>
Kever Yang9fbe17c2019-03-28 11:01:23 +080015#include <asm/arch-rockchip/gpio.h>
Simon Glass9c73e742016-01-21 19:44:09 -070016#include <dm/pinctrl.h>
Jonas Karlmanc8078412023-03-19 18:39:51 +000017#include <dm/read.h>
18#include <dt-bindings/pinctrl/rockchip.h>
19
20#define SWPORT_DR 0x0000
21#define SWPORT_DDR 0x0004
22#define EXT_PORT 0x0050
23#define SWPORT_DR_L 0x0000
24#define SWPORT_DR_H 0x0004
25#define SWPORT_DDR_L 0x0008
26#define SWPORT_DDR_H 0x000C
27#define EXT_PORT_V2 0x0070
28#define VER_ID_V2 0x0078
Simon Glass8fa4d5a2015-08-30 16:55:27 -060029
30enum {
31 ROCKCHIP_GPIOS_PER_BANK = 32,
32};
33
Simon Glass8fa4d5a2015-08-30 16:55:27 -060034struct rockchip_gpio_priv {
Jonas Karlmanc8078412023-03-19 18:39:51 +000035 void __iomem *regs;
Simon Glass9c73e742016-01-21 19:44:09 -070036 struct udevice *pinctrl;
37 int bank;
Simon Glass8fa4d5a2015-08-30 16:55:27 -060038 char name[2];
Jonas Karlmanc8078412023-03-19 18:39:51 +000039 u32 version;
Simon Glass8fa4d5a2015-08-30 16:55:27 -060040};
41
Jonas Karlmanc8078412023-03-19 18:39:51 +000042static int rockchip_gpio_get_value(struct udevice *dev, unsigned offset)
Simon Glass8fa4d5a2015-08-30 16:55:27 -060043{
44 struct rockchip_gpio_priv *priv = dev_get_priv(dev);
Jonas Karlmanc8078412023-03-19 18:39:51 +000045 u32 mask = BIT(offset), data;
Simon Glass8fa4d5a2015-08-30 16:55:27 -060046
Jonas Karlmanc8078412023-03-19 18:39:51 +000047 if (priv->version)
48 data = readl(priv->regs + EXT_PORT_V2);
49 else
50 data = readl(priv->regs + EXT_PORT);
Simon Glass8fa4d5a2015-08-30 16:55:27 -060051
Jonas Karlmanc8078412023-03-19 18:39:51 +000052 return (data & mask) ? 1 : 0;
Simon Glass8fa4d5a2015-08-30 16:55:27 -060053}
54
Jonas Karlmanc8078412023-03-19 18:39:51 +000055static int rockchip_gpio_set_value(struct udevice *dev, unsigned offset,
56 int value)
Simon Glass8fa4d5a2015-08-30 16:55:27 -060057{
58 struct rockchip_gpio_priv *priv = dev_get_priv(dev);
Jonas Karlmanc8078412023-03-19 18:39:51 +000059 u32 mask = BIT(offset), data = value ? mask : 0;
Simon Glass8fa4d5a2015-08-30 16:55:27 -060060
Jonas Karlmanc8078412023-03-19 18:39:51 +000061 if (priv->version && offset >= 16)
62 rk_clrsetreg(priv->regs + SWPORT_DR_H, mask >> 16, data >> 16);
63 else if (priv->version)
64 rk_clrsetreg(priv->regs + SWPORT_DR_L, mask, data);
65 else
66 clrsetbits_le32(priv->regs + SWPORT_DR, mask, data);
Simon Glass8fa4d5a2015-08-30 16:55:27 -060067
68 return 0;
69}
70
Jonas Karlmanc8078412023-03-19 18:39:51 +000071static int rockchip_gpio_direction_input(struct udevice *dev, unsigned offset)
Simon Glass8fa4d5a2015-08-30 16:55:27 -060072{
73 struct rockchip_gpio_priv *priv = dev_get_priv(dev);
Jonas Karlmanc8078412023-03-19 18:39:51 +000074 u32 mask = BIT(offset);
75
76 if (priv->version && offset >= 16)
77 rk_clrreg(priv->regs + SWPORT_DDR_H, mask >> 16);
78 else if (priv->version)
79 rk_clrreg(priv->regs + SWPORT_DDR_L, mask);
80 else
81 clrbits_le32(priv->regs + SWPORT_DDR, mask);
Simon Glass8fa4d5a2015-08-30 16:55:27 -060082
Jonas Karlmanc8078412023-03-19 18:39:51 +000083 return 0;
Simon Glass8fa4d5a2015-08-30 16:55:27 -060084}
85
Jonas Karlmanc8078412023-03-19 18:39:51 +000086static int rockchip_gpio_direction_output(struct udevice *dev, unsigned offset,
87 int value)
Simon Glass8fa4d5a2015-08-30 16:55:27 -060088{
89 struct rockchip_gpio_priv *priv = dev_get_priv(dev);
Jonas Karlmanc8078412023-03-19 18:39:51 +000090 u32 mask = BIT(offset);
91
92 rockchip_gpio_set_value(dev, offset, value);
Simon Glass8fa4d5a2015-08-30 16:55:27 -060093
Jonas Karlmanc8078412023-03-19 18:39:51 +000094 if (priv->version && offset >= 16)
95 rk_setreg(priv->regs + SWPORT_DDR_H, mask >> 16);
96 else if (priv->version)
97 rk_setreg(priv->regs + SWPORT_DDR_L, mask);
98 else
99 setbits_le32(priv->regs + SWPORT_DDR, mask);
Simon Glass8fa4d5a2015-08-30 16:55:27 -0600100
101 return 0;
102}
103
104static int rockchip_gpio_get_function(struct udevice *dev, unsigned offset)
105{
Simon Glass9c73e742016-01-21 19:44:09 -0700106 struct rockchip_gpio_priv *priv = dev_get_priv(dev);
Jonas Karlmanc8078412023-03-19 18:39:51 +0000107 u32 mask = BIT(offset), data;
Simon Glass9c73e742016-01-21 19:44:09 -0700108 int ret;
109
Jonas Karlmanc8078412023-03-19 18:39:51 +0000110 if (CONFIG_IS_ENABLED(PINCTRL)) {
111 ret = pinctrl_get_gpio_mux(priv->pinctrl, priv->bank, offset);
112 if (ret < 0)
113 return ret;
114 else if (ret != RK_FUNC_GPIO)
115 return GPIOF_FUNC;
116 }
117
118 if (priv->version && offset >= 16)
119 data = readl(priv->regs + SWPORT_DDR_H) << 16;
120 else if (priv->version)
121 data = readl(priv->regs + SWPORT_DDR_L);
122 else
123 data = readl(priv->regs + SWPORT_DDR);
Simon Glass9c73e742016-01-21 19:44:09 -0700124
Jonas Karlmanc8078412023-03-19 18:39:51 +0000125 return (data & mask) ? GPIOF_OUTPUT : GPIOF_INPUT;
Simon Glass8fa4d5a2015-08-30 16:55:27 -0600126}
127
Simon Glassf642c532019-01-21 14:53:34 -0700128/* Simple SPL interface to GPIOs */
129#ifdef CONFIG_SPL_BUILD
130
131enum {
132 PULL_NONE_1V8 = 0,
133 PULL_DOWN_1V8 = 1,
134 PULL_UP_1V8 = 3,
135};
136
137int spl_gpio_set_pull(void *vregs, uint gpio, int pull)
138{
139 u32 *regs = vregs;
140 uint val;
141
142 regs += gpio >> GPIO_BANK_SHIFT;
143 gpio &= GPIO_OFFSET_MASK;
144 switch (pull) {
145 case GPIO_PULL_UP:
146 val = PULL_UP_1V8;
147 break;
148 case GPIO_PULL_DOWN:
149 val = PULL_DOWN_1V8;
150 break;
151 case GPIO_PULL_NORMAL:
152 default:
153 val = PULL_NONE_1V8;
154 break;
155 }
156 clrsetbits_le32(regs, 3 << (gpio * 2), val << (gpio * 2));
157
158 return 0;
159}
160
161int spl_gpio_output(void *vregs, uint gpio, int value)
162{
163 struct rockchip_gpio_regs * const regs = vregs;
164
165 clrsetbits_le32(&regs->swport_dr, 1 << gpio, value << gpio);
166
167 /* Set direction */
168 clrsetbits_le32(&regs->swport_ddr, 1 << gpio, 1 << gpio);
169
170 return 0;
171}
172#endif /* CONFIG_SPL_BUILD */
173
Simon Glass8fa4d5a2015-08-30 16:55:27 -0600174static int rockchip_gpio_probe(struct udevice *dev)
175{
176 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
177 struct rockchip_gpio_priv *priv = dev_get_priv(dev);
Chris Morgan01c69eb2023-02-13 16:27:34 -0600178 struct ofnode_phandle_args args;
Simon Glass8fa4d5a2015-08-30 16:55:27 -0600179 char *end;
Simon Glass9c73e742016-01-21 19:44:09 -0700180 int ret;
Simon Glass8fa4d5a2015-08-30 16:55:27 -0600181
Philipp Tomsich66482052017-09-11 22:04:24 +0200182 priv->regs = dev_read_addr_ptr(dev);
Jonas Karlmanc8078412023-03-19 18:39:51 +0000183
184 if (CONFIG_IS_ENABLED(PINCTRL)) {
185 ret = uclass_first_device_err(UCLASS_PINCTRL, &priv->pinctrl);
186 if (ret)
187 return ret;
188 }
Simon Glass9c73e742016-01-21 19:44:09 -0700189
Chris Morgan01c69eb2023-02-13 16:27:34 -0600190 /*
191 * If "gpio-ranges" is present in the devicetree use it to parse
192 * the GPIO bank ID, otherwise use the legacy method.
193 */
194 ret = ofnode_parse_phandle_with_args(dev_ofnode(dev),
195 "gpio-ranges", NULL, 3,
196 0, &args);
197 if (!ret || ret != -ENOENT) {
198 uc_priv->gpio_count = args.args[2];
Johan Jonker7fd2fdb2023-03-19 16:02:18 +0100199 priv->bank = args.args[1] / ROCKCHIP_GPIOS_PER_BANK;
Chris Morgan01c69eb2023-02-13 16:27:34 -0600200 } else {
201 uc_priv->gpio_count = ROCKCHIP_GPIOS_PER_BANK;
Jonas Karlmanf54f8ea2024-02-17 00:22:35 +0000202 ret = dev_read_alias_seq(dev, &priv->bank);
203 if (ret) {
204 end = strrchr(dev->name, '@');
205 priv->bank = trailing_strtoln(dev->name, end);
206 }
Chris Morgan01c69eb2023-02-13 16:27:34 -0600207 }
208
Simon Glass9c73e742016-01-21 19:44:09 -0700209 priv->name[0] = 'A' + priv->bank;
Simon Glass8fa4d5a2015-08-30 16:55:27 -0600210 uc_priv->bank_name = priv->name;
211
Jonas Karlmanc8078412023-03-19 18:39:51 +0000212 priv->version = readl(priv->regs + VER_ID_V2);
213
Simon Glass8fa4d5a2015-08-30 16:55:27 -0600214 return 0;
215}
216
217static const struct dm_gpio_ops gpio_rockchip_ops = {
218 .direction_input = rockchip_gpio_direction_input,
219 .direction_output = rockchip_gpio_direction_output,
220 .get_value = rockchip_gpio_get_value,
221 .set_value = rockchip_gpio_set_value,
222 .get_function = rockchip_gpio_get_function,
Simon Glass8fa4d5a2015-08-30 16:55:27 -0600223};
224
225static const struct udevice_id rockchip_gpio_ids[] = {
226 { .compatible = "rockchip,gpio-bank" },
227 { }
228};
229
Walter Lozano2901ac62020-06-25 01:10:04 -0300230U_BOOT_DRIVER(rockchip_gpio_bank) = {
231 .name = "rockchip_gpio_bank",
Simon Glass8fa4d5a2015-08-30 16:55:27 -0600232 .id = UCLASS_GPIO,
233 .of_match = rockchip_gpio_ids,
234 .ops = &gpio_rockchip_ops,
Simon Glass8a2b47f2020-12-03 16:55:17 -0700235 .priv_auto = sizeof(struct rockchip_gpio_priv),
Simon Glass8fa4d5a2015-08-30 16:55:27 -0600236 .probe = rockchip_gpio_probe,
237};