]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
abce2c62 IC |
2 | /* |
3 | * (C) Copyright 2012 Henrik Nordstrom <[email protected]> | |
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 <[email protected]> | |
abce2c62 IC |
10 | */ |
11 | ||
7aa97485 SG |
12 | #include <dm.h> |
13 | #include <errno.h> | |
14 | #include <fdtdec.h> | |
15 | #include <malloc.h> | |
abce2c62 IC |
16 | #include <asm/io.h> |
17 | #include <asm/gpio.h> | |
4694dc56 | 18 | #include <dt-bindings/gpio/gpio.h> |
207ed0a3 | 19 | #include <sunxi_gpio.h> |
abce2c62 | 20 | |
20b78c55 AP |
21 | /* |
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 | ||
20b78c55 AP |
32 | #define GPIO_BANK(pin) ((pin) >> 5) |
33 | #define GPIO_NUM(pin) ((pin) & 0x1f) | |
34 | ||
30097ee3 | 35 | #define GPIO_CFG_REG_OFFSET 0x00 |
20b78c55 AP |
36 | #define GPIO_CFG_INDEX(pin) (((pin) & 0x1f) >> 3) |
37 | #define GPIO_CFG_OFFSET(pin) ((((pin) & 0x1f) & 0x7) << 2) | |
38 | ||
30097ee3 AP |
39 | #define GPIO_DAT_REG_OFFSET 0x10 |
40 | ||
41 | #define GPIO_DRV_REG_OFFSET 0x14 | |
452369cd AP |
42 | |
43 | /* Newer SoCs use a slightly different register layout */ | |
44 | #ifdef CONFIG_SUNXI_NEW_PINCTRL | |
45 | /* pin drive strength: 4 bits per pin */ | |
46 | #define GPIO_DRV_INDEX(pin) ((pin) / 8) | |
47 | #define GPIO_DRV_OFFSET(pin) (((pin) % 8) * 4) | |
48 | ||
49 | #define GPIO_PULL_REG_OFFSET 0x24 | |
50 | ||
51 | #else /* older generation pin controllers */ | |
52 | /* pin drive strength: 2 bits per pin */ | |
53 | #define GPIO_DRV_INDEX(pin) ((pin) / 16) | |
54 | #define GPIO_DRV_OFFSET(pin) (((pin) % 16) * 2) | |
20b78c55 | 55 | |
30097ee3 | 56 | #define GPIO_PULL_REG_OFFSET 0x1c |
452369cd AP |
57 | #endif |
58 | ||
20b78c55 AP |
59 | #define GPIO_PULL_INDEX(pin) (((pin) & 0x1f) >> 4) |
60 | #define GPIO_PULL_OFFSET(pin) ((((pin) & 0x1f) & 0xf) << 1) | |
61 | ||
30097ee3 AP |
62 | static void* BANK_TO_GPIO(int bank) |
63 | { | |
64 | void *pio_base; | |
65 | ||
66 | if (bank < SUNXI_GPIO_L) { | |
67 | pio_base = (void *)(uintptr_t)SUNXI_PIO_BASE; | |
68 | } else { | |
69 | pio_base = (void *)(uintptr_t)SUNXI_R_PIO_BASE; | |
70 | bank -= SUNXI_GPIO_L; | |
71 | } | |
72 | ||
73 | return pio_base + bank * SUNXI_PINCTRL_BANK_SIZE; | |
74 | } | |
75 | ||
76 | void sunxi_gpio_set_cfgbank(void *bank_base, int pin_offset, u32 val) | |
20b78c55 | 77 | { |
30097ee3 AP |
78 | u32 index = GPIO_CFG_INDEX(pin_offset); |
79 | u32 offset = GPIO_CFG_OFFSET(pin_offset); | |
20b78c55 | 80 | |
30097ee3 AP |
81 | clrsetbits_le32(bank_base + GPIO_CFG_REG_OFFSET + index * 4, |
82 | 0xfU << offset, val << offset); | |
20b78c55 AP |
83 | } |
84 | ||
85 | void sunxi_gpio_set_cfgpin(u32 pin, u32 val) | |
86 | { | |
87 | u32 bank = GPIO_BANK(pin); | |
30097ee3 | 88 | void *pio = BANK_TO_GPIO(bank); |
20b78c55 | 89 | |
30097ee3 | 90 | sunxi_gpio_set_cfgbank(pio, GPIO_NUM(pin), val); |
20b78c55 AP |
91 | } |
92 | ||
30097ee3 | 93 | int sunxi_gpio_get_cfgbank(void *bank_base, int pin_offset) |
20b78c55 | 94 | { |
30097ee3 AP |
95 | u32 index = GPIO_CFG_INDEX(pin_offset); |
96 | u32 offset = GPIO_CFG_OFFSET(pin_offset); | |
20b78c55 AP |
97 | u32 cfg; |
98 | ||
30097ee3 | 99 | cfg = readl(bank_base + GPIO_CFG_REG_OFFSET + index * 4); |
20b78c55 AP |
100 | cfg >>= offset; |
101 | ||
102 | return cfg & 0xf; | |
103 | } | |
104 | ||
105 | int sunxi_gpio_get_cfgpin(u32 pin) | |
106 | { | |
107 | u32 bank = GPIO_BANK(pin); | |
30097ee3 | 108 | void *bank_base = BANK_TO_GPIO(bank); |
20b78c55 | 109 | |
30097ee3 | 110 | return sunxi_gpio_get_cfgbank(bank_base, GPIO_NUM(pin)); |
20b78c55 AP |
111 | } |
112 | ||
30097ee3 | 113 | static void sunxi_gpio_set_value_bank(void *bank_base, int pin, bool set) |
316ec7ff AP |
114 | { |
115 | u32 mask = 1U << pin; | |
116 | ||
30097ee3 AP |
117 | clrsetbits_le32(bank_base + GPIO_DAT_REG_OFFSET, |
118 | set ? 0 : mask, set ? mask : 0); | |
316ec7ff AP |
119 | } |
120 | ||
30097ee3 | 121 | static int sunxi_gpio_get_value_bank(void *bank_base, int pin) |
316ec7ff | 122 | { |
30097ee3 | 123 | return !!(readl(bank_base + GPIO_DAT_REG_OFFSET) & (1U << pin)); |
316ec7ff AP |
124 | } |
125 | ||
20b78c55 AP |
126 | void sunxi_gpio_set_drv(u32 pin, u32 val) |
127 | { | |
128 | u32 bank = GPIO_BANK(pin); | |
30097ee3 | 129 | void *bank_base = BANK_TO_GPIO(bank); |
20b78c55 | 130 | |
30097ee3 | 131 | sunxi_gpio_set_drv_bank(bank_base, GPIO_NUM(pin), val); |
20b78c55 AP |
132 | } |
133 | ||
30097ee3 | 134 | void sunxi_gpio_set_drv_bank(void *bank_base, u32 pin_offset, u32 val) |
20b78c55 | 135 | { |
30097ee3 AP |
136 | u32 index = GPIO_DRV_INDEX(pin_offset); |
137 | u32 offset = GPIO_DRV_OFFSET(pin_offset); | |
20b78c55 | 138 | |
30097ee3 AP |
139 | clrsetbits_le32(bank_base + GPIO_DRV_REG_OFFSET + index * 4, |
140 | 0x3U << offset, val << offset); | |
20b78c55 AP |
141 | } |
142 | ||
143 | void sunxi_gpio_set_pull(u32 pin, u32 val) | |
144 | { | |
145 | u32 bank = GPIO_BANK(pin); | |
30097ee3 | 146 | void *bank_base = BANK_TO_GPIO(bank); |
20b78c55 | 147 | |
30097ee3 | 148 | sunxi_gpio_set_pull_bank(bank_base, GPIO_NUM(pin), val); |
20b78c55 AP |
149 | } |
150 | ||
30097ee3 | 151 | void sunxi_gpio_set_pull_bank(void *bank_base, int pin_offset, u32 val) |
20b78c55 | 152 | { |
30097ee3 AP |
153 | u32 index = GPIO_PULL_INDEX(pin_offset); |
154 | u32 offset = GPIO_PULL_OFFSET(pin_offset); | |
20b78c55 | 155 | |
30097ee3 AP |
156 | clrsetbits_le32(bank_base + GPIO_PULL_REG_OFFSET + index * 4, |
157 | 0x3U << offset, val << offset); | |
20b78c55 AP |
158 | } |
159 | ||
20b78c55 AP |
160 | /* =========== Non-DM code, used by the SPL. ============ */ |
161 | ||
bcee8d67 | 162 | #if !CONFIG_IS_ENABLED(DM_GPIO) |
316ec7ff | 163 | static void sunxi_gpio_set_value(u32 pin, bool set) |
abce2c62 | 164 | { |
abce2c62 | 165 | u32 bank = GPIO_BANK(pin); |
30097ee3 | 166 | void *pio = BANK_TO_GPIO(bank); |
abce2c62 | 167 | |
316ec7ff | 168 | sunxi_gpio_set_value_bank(pio, GPIO_NUM(pin), set); |
abce2c62 IC |
169 | } |
170 | ||
316ec7ff | 171 | static int sunxi_gpio_get_value(u32 pin) |
abce2c62 | 172 | { |
abce2c62 | 173 | u32 bank = GPIO_BANK(pin); |
30097ee3 | 174 | void *pio = BANK_TO_GPIO(bank); |
abce2c62 | 175 | |
316ec7ff | 176 | return sunxi_gpio_get_value_bank(pio, GPIO_NUM(pin)); |
abce2c62 IC |
177 | } |
178 | ||
179 | int gpio_request(unsigned gpio, const char *label) | |
180 | { | |
181 | return 0; | |
182 | } | |
183 | ||
184 | int gpio_free(unsigned gpio) | |
185 | { | |
186 | return 0; | |
187 | } | |
188 | ||
189 | int gpio_direction_input(unsigned gpio) | |
190 | { | |
191 | sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_INPUT); | |
192 | ||
b0c4ae1a | 193 | return 0; |
abce2c62 IC |
194 | } |
195 | ||
196 | int gpio_direction_output(unsigned gpio, int value) | |
197 | { | |
198 | sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_OUTPUT); | |
316ec7ff | 199 | sunxi_gpio_set_value(gpio, value); |
abce2c62 | 200 | |
316ec7ff | 201 | return 0; |
abce2c62 IC |
202 | } |
203 | ||
204 | int gpio_get_value(unsigned gpio) | |
205 | { | |
316ec7ff | 206 | return sunxi_gpio_get_value(gpio); |
abce2c62 IC |
207 | } |
208 | ||
209 | int gpio_set_value(unsigned gpio, int value) | |
210 | { | |
316ec7ff AP |
211 | sunxi_gpio_set_value(gpio, value); |
212 | ||
213 | return 0; | |
abce2c62 IC |
214 | } |
215 | ||
216 | int sunxi_name_to_gpio(const char *name) | |
217 | { | |
218 | int group = 0; | |
219 | int groupsize = 9 * 32; | |
220 | long pin; | |
221 | char *eptr; | |
6c727e09 | 222 | |
abce2c62 IC |
223 | if (*name == 'P' || *name == 'p') |
224 | name++; | |
225 | if (*name >= 'A') { | |
226 | group = *name - (*name > 'a' ? 'a' : 'A'); | |
227 | groupsize = 32; | |
228 | name++; | |
229 | } | |
230 | ||
231 | pin = simple_strtol(name, &eptr, 10); | |
232 | if (!*name || *eptr) | |
233 | return -1; | |
234 | if (pin < 0 || pin > groupsize || group >= 9) | |
235 | return -1; | |
236 | return group * 32 + pin; | |
237 | } | |
20b78c55 AP |
238 | #endif /* !DM_GPIO */ |
239 | ||
240 | /* =========== DM code, used by U-Boot proper. ============ */ | |
7aa97485 | 241 | |
bcee8d67 | 242 | #if CONFIG_IS_ENABLED(DM_GPIO) |
a5ab8838 SG |
243 | /* TODO([email protected]): Remove this function and use device tree */ |
244 | int sunxi_name_to_gpio(const char *name) | |
245 | { | |
246 | unsigned int gpio; | |
247 | int ret; | |
371dc068 | 248 | #if !defined CONFIG_XPL_BUILD && defined CONFIG_AXP_GPIO |
f9b7a04b HG |
249 | char lookup[8]; |
250 | ||
09cbd385 | 251 | if (strcasecmp(name, "AXP0-VBUS-ENABLE") == 0) { |
f9b7a04b HG |
252 | sprintf(lookup, SUNXI_GPIO_AXP0_PREFIX "%d", |
253 | SUNXI_GPIO_AXP0_VBUS_ENABLE); | |
254 | name = lookup; | |
255 | } | |
256 | #endif | |
a5ab8838 SG |
257 | ret = gpio_lookup_name(name, NULL, NULL, &gpio); |
258 | ||
259 | return ret ? ret : gpio; | |
260 | } | |
261 | ||
7aa97485 SG |
262 | static int sunxi_gpio_get_value(struct udevice *dev, unsigned offset) |
263 | { | |
8a8d24bd | 264 | struct sunxi_gpio_plat *plat = dev_get_plat(dev); |
7aa97485 | 265 | |
316ec7ff | 266 | return sunxi_gpio_get_value_bank(plat->regs, offset); |
7aa97485 SG |
267 | } |
268 | ||
7aa97485 SG |
269 | static int sunxi_gpio_get_function(struct udevice *dev, unsigned offset) |
270 | { | |
8a8d24bd | 271 | struct sunxi_gpio_plat *plat = dev_get_plat(dev); |
7aa97485 SG |
272 | int func; |
273 | ||
274 | func = sunxi_gpio_get_cfgbank(plat->regs, offset); | |
275 | if (func == SUNXI_GPIO_OUTPUT) | |
276 | return GPIOF_OUTPUT; | |
277 | else if (func == SUNXI_GPIO_INPUT) | |
278 | return GPIOF_INPUT; | |
279 | else | |
280 | return GPIOF_FUNC; | |
281 | } | |
282 | ||
4694dc56 | 283 | static int sunxi_gpio_xlate(struct udevice *dev, struct gpio_desc *desc, |
3a57123e | 284 | struct ofnode_phandle_args *args) |
4694dc56 CYT |
285 | { |
286 | int ret; | |
287 | ||
288 | ret = device_get_child(dev, args->args[0], &desc->dev); | |
289 | if (ret) | |
290 | return ret; | |
291 | desc->offset = args->args[1]; | |
35ae126c SH |
292 | desc->flags = gpio_flags_xlate(args->args[2]); |
293 | ||
294 | return 0; | |
295 | } | |
296 | ||
297 | static int sunxi_gpio_set_flags(struct udevice *dev, unsigned int offset, | |
298 | ulong flags) | |
299 | { | |
300 | struct sunxi_gpio_plat *plat = dev_get_plat(dev); | |
301 | ||
302 | if (flags & GPIOD_IS_OUT) { | |
303 | u32 value = !!(flags & GPIOD_IS_OUT_ACTIVE); | |
35ae126c | 304 | |
316ec7ff | 305 | sunxi_gpio_set_value_bank(plat->regs, offset, value); |
35ae126c SH |
306 | sunxi_gpio_set_cfgbank(plat->regs, offset, SUNXI_GPIO_OUTPUT); |
307 | } else if (flags & GPIOD_IS_IN) { | |
308 | u32 pull = 0; | |
309 | ||
310 | if (flags & GPIOD_PULL_UP) | |
311 | pull = 1; | |
312 | else if (flags & GPIOD_PULL_DOWN) | |
313 | pull = 2; | |
314 | sunxi_gpio_set_pull_bank(plat->regs, offset, pull); | |
315 | sunxi_gpio_set_cfgbank(plat->regs, offset, SUNXI_GPIO_INPUT); | |
316 | } | |
4694dc56 CYT |
317 | |
318 | return 0; | |
319 | } | |
320 | ||
7aa97485 | 321 | static const struct dm_gpio_ops gpio_sunxi_ops = { |
7aa97485 | 322 | .get_value = sunxi_gpio_get_value, |
7aa97485 | 323 | .get_function = sunxi_gpio_get_function, |
4694dc56 | 324 | .xlate = sunxi_gpio_xlate, |
35ae126c | 325 | .set_flags = sunxi_gpio_set_flags, |
7aa97485 SG |
326 | }; |
327 | ||
7aa97485 SG |
328 | static int gpio_sunxi_probe(struct udevice *dev) |
329 | { | |
8a8d24bd | 330 | struct sunxi_gpio_plat *plat = dev_get_plat(dev); |
e564f054 | 331 | struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); |
7aa97485 SG |
332 | |
333 | /* Tell the uclass how many GPIOs we have */ | |
334 | if (plat) { | |
b799eabc | 335 | uc_priv->gpio_count = SUNXI_GPIOS_PER_BANK; |
7aa97485 SG |
336 | uc_priv->bank_name = plat->bank_name; |
337 | } | |
338 | ||
339 | return 0; | |
340 | } | |
6f82fac2 | 341 | |
7aa97485 SG |
342 | U_BOOT_DRIVER(gpio_sunxi) = { |
343 | .name = "gpio_sunxi", | |
344 | .id = UCLASS_GPIO, | |
7aa97485 | 345 | .probe = gpio_sunxi_probe, |
b799eabc | 346 | .ops = &gpio_sunxi_ops, |
7aa97485 | 347 | }; |
bcee8d67 | 348 | #endif /* DM_GPIO */ |