blob: 3ca833316b5c930fa590613f3eb13843d5c39983 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: GPL-2.0+
Ian Campbellabce2c62014-06-05 19:00:15 +01002/*
3 * (C) Copyright 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
4 *
5 * Based on earlier arch/arm/cpu/armv7/sunxi/gpio.c:
6 *
7 * (C) Copyright 2007-2011
8 * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
9 * Tom Cubie <tangliang@allwinnertech.com>
Ian Campbellabce2c62014-06-05 19:00:15 +010010 */
11
12#include <common.h>
Simon Glass7aa97482014-10-30 20:25:49 -060013#include <dm.h>
14#include <errno.h>
15#include <fdtdec.h>
16#include <malloc.h>
Ian Campbellabce2c62014-06-05 19:00:15 +010017#include <asm/io.h>
18#include <asm/gpio.h>
Chen-Yu Tsai4694dc52016-07-22 16:12:59 +080019#include <dt-bindings/gpio/gpio.h>
Ian Campbellabce2c62014-06-05 19:00:15 +010020
Andre Przywara20b78c52022-09-06 10:36:38 +010021/*
22 * =======================================================================
23 * Low level GPIO/pin controller access functions, to be shared by non-DM
24 * SPL code and the DM pinctrl/GPIO drivers.
25 * The functions ending in "bank" take a base pointer to a GPIO bank, and
26 * the pin offset is relative to that bank.
27 * The functions without "bank" in their name take a linear GPIO number,
28 * covering all ports, and starting at 0 for PortA.
29 * =======================================================================
30 */
31
32#define BANK_TO_GPIO(bank) (((bank) < SUNXI_GPIO_L) ? \
33 &((struct sunxi_gpio_reg *)SUNXI_PIO_BASE)->gpio_bank[bank] : \
34 &((struct sunxi_gpio_reg *)SUNXI_R_PIO_BASE)->gpio_bank[(bank) - SUNXI_GPIO_L])
35
36#define GPIO_BANK(pin) ((pin) >> 5)
37#define GPIO_NUM(pin) ((pin) & 0x1f)
38
39#define GPIO_CFG_INDEX(pin) (((pin) & 0x1f) >> 3)
40#define GPIO_CFG_OFFSET(pin) ((((pin) & 0x1f) & 0x7) << 2)
41
42#define GPIO_DRV_INDEX(pin) (((pin) & 0x1f) >> 4)
43#define GPIO_DRV_OFFSET(pin) ((((pin) & 0x1f) & 0xf) << 1)
44
45#define GPIO_PULL_INDEX(pin) (((pin) & 0x1f) >> 4)
46#define GPIO_PULL_OFFSET(pin) ((((pin) & 0x1f) & 0xf) << 1)
47
48void sunxi_gpio_set_cfgbank(struct sunxi_gpio *pio, int bank_offset, u32 val)
49{
50 u32 index = GPIO_CFG_INDEX(bank_offset);
51 u32 offset = GPIO_CFG_OFFSET(bank_offset);
52
53 clrsetbits_le32(&pio->cfg[index], 0xf << offset, val << offset);
54}
55
56void sunxi_gpio_set_cfgpin(u32 pin, u32 val)
57{
58 u32 bank = GPIO_BANK(pin);
59 struct sunxi_gpio *pio = BANK_TO_GPIO(bank);
60
61 sunxi_gpio_set_cfgbank(pio, pin, val);
62}
63
64int sunxi_gpio_get_cfgbank(struct sunxi_gpio *pio, int bank_offset)
65{
66 u32 index = GPIO_CFG_INDEX(bank_offset);
67 u32 offset = GPIO_CFG_OFFSET(bank_offset);
68 u32 cfg;
69
70 cfg = readl(&pio->cfg[index]);
71 cfg >>= offset;
72
73 return cfg & 0xf;
74}
75
76int sunxi_gpio_get_cfgpin(u32 pin)
77{
78 u32 bank = GPIO_BANK(pin);
79 struct sunxi_gpio *pio = BANK_TO_GPIO(bank);
80
81 return sunxi_gpio_get_cfgbank(pio, pin);
82}
83
Andre Przywara316ec7f2022-09-06 10:07:18 +010084static void sunxi_gpio_set_value_bank(struct sunxi_gpio *pio,
85 int pin, bool set)
86{
87 u32 mask = 1U << pin;
88
89 clrsetbits_le32(&pio->dat, set ? 0 : mask, set ? mask : 0);
90}
91
92static int sunxi_gpio_get_value_bank(struct sunxi_gpio *pio, int pin)
93{
94 return !!(readl(&pio->dat) & (1U << pin));
95}
96
Andre Przywara20b78c52022-09-06 10:36:38 +010097void sunxi_gpio_set_drv(u32 pin, u32 val)
98{
99 u32 bank = GPIO_BANK(pin);
100 struct sunxi_gpio *pio = BANK_TO_GPIO(bank);
101
102 sunxi_gpio_set_drv_bank(pio, pin, val);
103}
104
105void sunxi_gpio_set_drv_bank(struct sunxi_gpio *pio, u32 bank_offset, u32 val)
106{
107 u32 index = GPIO_DRV_INDEX(bank_offset);
108 u32 offset = GPIO_DRV_OFFSET(bank_offset);
109
110 clrsetbits_le32(&pio->drv[index], 0x3 << offset, val << offset);
111}
112
113void sunxi_gpio_set_pull(u32 pin, u32 val)
114{
115 u32 bank = GPIO_BANK(pin);
116 struct sunxi_gpio *pio = BANK_TO_GPIO(bank);
117
118 sunxi_gpio_set_pull_bank(pio, pin, val);
119}
120
121void sunxi_gpio_set_pull_bank(struct sunxi_gpio *pio, int bank_offset, u32 val)
122{
123 u32 index = GPIO_PULL_INDEX(bank_offset);
124 u32 offset = GPIO_PULL_OFFSET(bank_offset);
125
126 clrsetbits_le32(&pio->pull[index], 0x3 << offset, val << offset);
127}
128
129
130/* =========== Non-DM code, used by the SPL. ============ */
131
Simon Glassbcee8d62019-12-06 21:41:35 -0700132#if !CONFIG_IS_ENABLED(DM_GPIO)
Andre Przywara316ec7f2022-09-06 10:07:18 +0100133static void sunxi_gpio_set_value(u32 pin, bool set)
Ian Campbellabce2c62014-06-05 19:00:15 +0100134{
Ian Campbellabce2c62014-06-05 19:00:15 +0100135 u32 bank = GPIO_BANK(pin);
Ian Campbellabce2c62014-06-05 19:00:15 +0100136 struct sunxi_gpio *pio = BANK_TO_GPIO(bank);
137
Andre Przywara316ec7f2022-09-06 10:07:18 +0100138 sunxi_gpio_set_value_bank(pio, GPIO_NUM(pin), set);
Ian Campbellabce2c62014-06-05 19:00:15 +0100139}
140
Andre Przywara316ec7f2022-09-06 10:07:18 +0100141static int sunxi_gpio_get_value(u32 pin)
Ian Campbellabce2c62014-06-05 19:00:15 +0100142{
Ian Campbellabce2c62014-06-05 19:00:15 +0100143 u32 bank = GPIO_BANK(pin);
Ian Campbellabce2c62014-06-05 19:00:15 +0100144 struct sunxi_gpio *pio = BANK_TO_GPIO(bank);
145
Andre Przywara316ec7f2022-09-06 10:07:18 +0100146 return sunxi_gpio_get_value_bank(pio, GPIO_NUM(pin));
Ian Campbellabce2c62014-06-05 19:00:15 +0100147}
148
149int gpio_request(unsigned gpio, const char *label)
150{
151 return 0;
152}
153
154int gpio_free(unsigned gpio)
155{
156 return 0;
157}
158
159int gpio_direction_input(unsigned gpio)
160{
161 sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_INPUT);
162
Axel Linb0c4ae12014-12-20 11:41:25 +0800163 return 0;
Ian Campbellabce2c62014-06-05 19:00:15 +0100164}
165
166int gpio_direction_output(unsigned gpio, int value)
167{
168 sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_OUTPUT);
Andre Przywara316ec7f2022-09-06 10:07:18 +0100169 sunxi_gpio_set_value(gpio, value);
Ian Campbellabce2c62014-06-05 19:00:15 +0100170
Andre Przywara316ec7f2022-09-06 10:07:18 +0100171 return 0;
Ian Campbellabce2c62014-06-05 19:00:15 +0100172}
173
174int gpio_get_value(unsigned gpio)
175{
Andre Przywara316ec7f2022-09-06 10:07:18 +0100176 return sunxi_gpio_get_value(gpio);
Ian Campbellabce2c62014-06-05 19:00:15 +0100177}
178
179int gpio_set_value(unsigned gpio, int value)
180{
Andre Przywara316ec7f2022-09-06 10:07:18 +0100181 sunxi_gpio_set_value(gpio, value);
182
183 return 0;
Ian Campbellabce2c62014-06-05 19:00:15 +0100184}
185
186int sunxi_name_to_gpio(const char *name)
187{
188 int group = 0;
189 int groupsize = 9 * 32;
190 long pin;
191 char *eptr;
Hans de Goede6c727e02014-12-24 19:34:38 +0100192
Ian Campbellabce2c62014-06-05 19:00:15 +0100193 if (*name == 'P' || *name == 'p')
194 name++;
195 if (*name >= 'A') {
196 group = *name - (*name > 'a' ? 'a' : 'A');
197 groupsize = 32;
198 name++;
199 }
200
201 pin = simple_strtol(name, &eptr, 10);
202 if (!*name || *eptr)
203 return -1;
204 if (pin < 0 || pin > groupsize || group >= 9)
205 return -1;
206 return group * 32 + pin;
207}
Andre Przywara20b78c52022-09-06 10:36:38 +0100208#endif /* !DM_GPIO */
209
210/* =========== DM code, used by U-Boot proper. ============ */
Simon Glass7aa97482014-10-30 20:25:49 -0600211
Simon Glassbcee8d62019-12-06 21:41:35 -0700212#if CONFIG_IS_ENABLED(DM_GPIO)
Simon Glassa5ab8832015-04-18 11:33:43 -0600213/* TODO(sjg@chromium.org): Remove this function and use device tree */
214int sunxi_name_to_gpio(const char *name)
215{
216 unsigned int gpio;
217 int ret;
Hans de Goedef9b7a042015-04-22 11:31:22 +0200218#if !defined CONFIG_SPL_BUILD && defined CONFIG_AXP_GPIO
219 char lookup[8];
Simon Glassa5ab8832015-04-18 11:33:43 -0600220
Samuel Holland09cbd382023-01-22 17:46:22 -0600221 if (strcasecmp(name, "AXP0-VBUS-ENABLE") == 0) {
Hans de Goedef9b7a042015-04-22 11:31:22 +0200222 sprintf(lookup, SUNXI_GPIO_AXP0_PREFIX "%d",
223 SUNXI_GPIO_AXP0_VBUS_ENABLE);
224 name = lookup;
225 }
226#endif
Simon Glassa5ab8832015-04-18 11:33:43 -0600227 ret = gpio_lookup_name(name, NULL, NULL, &gpio);
228
229 return ret ? ret : gpio;
230}
231
Simon Glass7aa97482014-10-30 20:25:49 -0600232static int sunxi_gpio_get_value(struct udevice *dev, unsigned offset)
233{
Simon Glass8a8d24b2020-12-03 16:55:23 -0700234 struct sunxi_gpio_plat *plat = dev_get_plat(dev);
Simon Glass7aa97482014-10-30 20:25:49 -0600235
Andre Przywara316ec7f2022-09-06 10:07:18 +0100236 return sunxi_gpio_get_value_bank(plat->regs, offset);
Simon Glass7aa97482014-10-30 20:25:49 -0600237}
238
Simon Glass7aa97482014-10-30 20:25:49 -0600239static int sunxi_gpio_get_function(struct udevice *dev, unsigned offset)
240{
Simon Glass8a8d24b2020-12-03 16:55:23 -0700241 struct sunxi_gpio_plat *plat = dev_get_plat(dev);
Simon Glass7aa97482014-10-30 20:25:49 -0600242 int func;
243
244 func = sunxi_gpio_get_cfgbank(plat->regs, offset);
245 if (func == SUNXI_GPIO_OUTPUT)
246 return GPIOF_OUTPUT;
247 else if (func == SUNXI_GPIO_INPUT)
248 return GPIOF_INPUT;
249 else
250 return GPIOF_FUNC;
251}
252
Chen-Yu Tsai4694dc52016-07-22 16:12:59 +0800253static int sunxi_gpio_xlate(struct udevice *dev, struct gpio_desc *desc,
Simon Glass3a571232017-05-18 20:09:18 -0600254 struct ofnode_phandle_args *args)
Chen-Yu Tsai4694dc52016-07-22 16:12:59 +0800255{
256 int ret;
257
258 ret = device_get_child(dev, args->args[0], &desc->dev);
259 if (ret)
260 return ret;
261 desc->offset = args->args[1];
Samuel Holland35ae1262021-10-20 23:52:56 -0500262 desc->flags = gpio_flags_xlate(args->args[2]);
263
264 return 0;
265}
266
267static int sunxi_gpio_set_flags(struct udevice *dev, unsigned int offset,
268 ulong flags)
269{
270 struct sunxi_gpio_plat *plat = dev_get_plat(dev);
271
272 if (flags & GPIOD_IS_OUT) {
273 u32 value = !!(flags & GPIOD_IS_OUT_ACTIVE);
Samuel Holland35ae1262021-10-20 23:52:56 -0500274
Andre Przywara316ec7f2022-09-06 10:07:18 +0100275 sunxi_gpio_set_value_bank(plat->regs, offset, value);
Samuel Holland35ae1262021-10-20 23:52:56 -0500276 sunxi_gpio_set_cfgbank(plat->regs, offset, SUNXI_GPIO_OUTPUT);
277 } else if (flags & GPIOD_IS_IN) {
278 u32 pull = 0;
279
280 if (flags & GPIOD_PULL_UP)
281 pull = 1;
282 else if (flags & GPIOD_PULL_DOWN)
283 pull = 2;
284 sunxi_gpio_set_pull_bank(plat->regs, offset, pull);
285 sunxi_gpio_set_cfgbank(plat->regs, offset, SUNXI_GPIO_INPUT);
286 }
Chen-Yu Tsai4694dc52016-07-22 16:12:59 +0800287
288 return 0;
289}
290
Simon Glass7aa97482014-10-30 20:25:49 -0600291static const struct dm_gpio_ops gpio_sunxi_ops = {
Simon Glass7aa97482014-10-30 20:25:49 -0600292 .get_value = sunxi_gpio_get_value,
Simon Glass7aa97482014-10-30 20:25:49 -0600293 .get_function = sunxi_gpio_get_function,
Chen-Yu Tsai4694dc52016-07-22 16:12:59 +0800294 .xlate = sunxi_gpio_xlate,
Samuel Holland35ae1262021-10-20 23:52:56 -0500295 .set_flags = sunxi_gpio_set_flags,
Simon Glass7aa97482014-10-30 20:25:49 -0600296};
297
Simon Glass7aa97482014-10-30 20:25:49 -0600298static int gpio_sunxi_probe(struct udevice *dev)
299{
Simon Glass8a8d24b2020-12-03 16:55:23 -0700300 struct sunxi_gpio_plat *plat = dev_get_plat(dev);
Simon Glasse564f052015-03-05 12:25:20 -0700301 struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
Simon Glass7aa97482014-10-30 20:25:49 -0600302
303 /* Tell the uclass how many GPIOs we have */
304 if (plat) {
Samuel Hollandb799eab2021-08-12 20:09:43 -0500305 uc_priv->gpio_count = SUNXI_GPIOS_PER_BANK;
Simon Glass7aa97482014-10-30 20:25:49 -0600306 uc_priv->bank_name = plat->bank_name;
307 }
308
309 return 0;
310}
Stephen Warren6f82fac2016-05-11 15:26:25 -0600311
Simon Glass7aa97482014-10-30 20:25:49 -0600312U_BOOT_DRIVER(gpio_sunxi) = {
313 .name = "gpio_sunxi",
314 .id = UCLASS_GPIO,
Simon Glass7aa97482014-10-30 20:25:49 -0600315 .probe = gpio_sunxi_probe,
Samuel Hollandb799eab2021-08-12 20:09:43 -0500316 .ops = &gpio_sunxi_ops,
Simon Glass7aa97482014-10-30 20:25:49 -0600317};
Simon Glassbcee8d62019-12-06 21:41:35 -0700318#endif /* DM_GPIO */