]>
Commit | Line | Data |
---|---|---|
abce2c62 IC |
1 | /* |
2 | * (C) Copyright 2012 Henrik Nordstrom <[email protected]> | |
3 | * | |
4 | * Based on earlier arch/arm/cpu/armv7/sunxi/gpio.c: | |
5 | * | |
6 | * (C) Copyright 2007-2011 | |
7 | * Allwinner Technology Co., Ltd. <www.allwinnertech.com> | |
8 | * Tom Cubie <[email protected]> | |
9 | * | |
10 | * SPDX-License-Identifier: GPL-2.0+ | |
11 | */ | |
12 | ||
13 | #include <common.h> | |
7aa97485 SG |
14 | #include <dm.h> |
15 | #include <errno.h> | |
16 | #include <fdtdec.h> | |
17 | #include <malloc.h> | |
2fcf033d | 18 | #include <asm/arch/gpio.h> |
abce2c62 IC |
19 | #include <asm/io.h> |
20 | #include <asm/gpio.h> | |
7aa97485 | 21 | #include <dm/device-internal.h> |
abce2c62 | 22 | |
7aa97485 SG |
23 | DECLARE_GLOBAL_DATA_PTR; |
24 | ||
25 | #define SUNXI_GPIOS_PER_BANK SUNXI_GPIO_A_NR | |
26 | ||
27 | struct sunxi_gpio_platdata { | |
28 | struct sunxi_gpio *regs; | |
29 | const char *bank_name; /* Name of bank, e.g. "B" */ | |
30 | int gpio_count; | |
31 | }; | |
32 | ||
33 | #ifndef CONFIG_DM_GPIO | |
abce2c62 IC |
34 | static int sunxi_gpio_output(u32 pin, u32 val) |
35 | { | |
36 | u32 dat; | |
37 | u32 bank = GPIO_BANK(pin); | |
38 | u32 num = GPIO_NUM(pin); | |
39 | struct sunxi_gpio *pio = BANK_TO_GPIO(bank); | |
40 | ||
41 | dat = readl(&pio->dat); | |
42 | if (val) | |
43 | dat |= 0x1 << num; | |
44 | else | |
45 | dat &= ~(0x1 << num); | |
46 | ||
47 | writel(dat, &pio->dat); | |
48 | ||
49 | return 0; | |
50 | } | |
51 | ||
52 | static int sunxi_gpio_input(u32 pin) | |
53 | { | |
54 | u32 dat; | |
55 | u32 bank = GPIO_BANK(pin); | |
56 | u32 num = GPIO_NUM(pin); | |
57 | struct sunxi_gpio *pio = BANK_TO_GPIO(bank); | |
58 | ||
59 | dat = readl(&pio->dat); | |
60 | dat >>= num; | |
61 | ||
62 | return dat & 0x1; | |
63 | } | |
64 | ||
65 | int gpio_request(unsigned gpio, const char *label) | |
66 | { | |
67 | return 0; | |
68 | } | |
69 | ||
70 | int gpio_free(unsigned gpio) | |
71 | { | |
72 | return 0; | |
73 | } | |
74 | ||
75 | int gpio_direction_input(unsigned gpio) | |
76 | { | |
77 | sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_INPUT); | |
78 | ||
b0c4ae1a | 79 | return 0; |
abce2c62 IC |
80 | } |
81 | ||
82 | int gpio_direction_output(unsigned gpio, int value) | |
83 | { | |
84 | sunxi_gpio_set_cfgpin(gpio, SUNXI_GPIO_OUTPUT); | |
85 | ||
86 | return sunxi_gpio_output(gpio, value); | |
87 | } | |
88 | ||
89 | int gpio_get_value(unsigned gpio) | |
90 | { | |
91 | return sunxi_gpio_input(gpio); | |
92 | } | |
93 | ||
94 | int gpio_set_value(unsigned gpio, int value) | |
95 | { | |
96 | return sunxi_gpio_output(gpio, value); | |
97 | } | |
98 | ||
99 | int sunxi_name_to_gpio(const char *name) | |
100 | { | |
101 | int group = 0; | |
102 | int groupsize = 9 * 32; | |
103 | long pin; | |
104 | char *eptr; | |
6c727e09 | 105 | |
abce2c62 IC |
106 | if (*name == 'P' || *name == 'p') |
107 | name++; | |
108 | if (*name >= 'A') { | |
109 | group = *name - (*name > 'a' ? 'a' : 'A'); | |
110 | groupsize = 32; | |
111 | name++; | |
112 | } | |
113 | ||
114 | pin = simple_strtol(name, &eptr, 10); | |
115 | if (!*name || *eptr) | |
116 | return -1; | |
117 | if (pin < 0 || pin > groupsize || group >= 9) | |
118 | return -1; | |
119 | return group * 32 + pin; | |
120 | } | |
7aa97485 SG |
121 | #endif |
122 | ||
746c087b HG |
123 | int sunxi_name_to_gpio_bank(const char *name) |
124 | { | |
125 | int group = 0; | |
126 | ||
127 | if (*name == 'P' || *name == 'p') | |
128 | name++; | |
129 | if (*name >= 'A') { | |
130 | group = *name - (*name > 'a' ? 'a' : 'A'); | |
131 | return group; | |
132 | } | |
133 | ||
134 | return -1; | |
135 | } | |
136 | ||
7aa97485 | 137 | #ifdef CONFIG_DM_GPIO |
a5ab8838 SG |
138 | /* TODO([email protected]): Remove this function and use device tree */ |
139 | int sunxi_name_to_gpio(const char *name) | |
140 | { | |
141 | unsigned int gpio; | |
142 | int ret; | |
f9b7a04b HG |
143 | #if !defined CONFIG_SPL_BUILD && defined CONFIG_AXP_GPIO |
144 | char lookup[8]; | |
145 | ||
146 | if (strcasecmp(name, "AXP0-VBUS-DETECT") == 0) { | |
147 | sprintf(lookup, SUNXI_GPIO_AXP0_PREFIX "%d", | |
148 | SUNXI_GPIO_AXP0_VBUS_DETECT); | |
149 | name = lookup; | |
150 | } else if (strcasecmp(name, "AXP0-VBUS-ENABLE") == 0) { | |
151 | sprintf(lookup, SUNXI_GPIO_AXP0_PREFIX "%d", | |
152 | SUNXI_GPIO_AXP0_VBUS_ENABLE); | |
153 | name = lookup; | |
154 | } | |
155 | #endif | |
a5ab8838 SG |
156 | ret = gpio_lookup_name(name, NULL, NULL, &gpio); |
157 | ||
158 | return ret ? ret : gpio; | |
159 | } | |
160 | ||
7aa97485 SG |
161 | static int sunxi_gpio_direction_input(struct udevice *dev, unsigned offset) |
162 | { | |
163 | struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); | |
164 | ||
165 | sunxi_gpio_set_cfgbank(plat->regs, offset, SUNXI_GPIO_INPUT); | |
166 | ||
167 | return 0; | |
168 | } | |
169 | ||
170 | static int sunxi_gpio_direction_output(struct udevice *dev, unsigned offset, | |
171 | int value) | |
172 | { | |
173 | struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); | |
174 | u32 num = GPIO_NUM(offset); | |
175 | ||
176 | sunxi_gpio_set_cfgbank(plat->regs, offset, SUNXI_GPIO_OUTPUT); | |
177 | clrsetbits_le32(&plat->regs->dat, 1 << num, value ? (1 << num) : 0); | |
178 | ||
179 | return 0; | |
180 | } | |
181 | ||
182 | static int sunxi_gpio_get_value(struct udevice *dev, unsigned offset) | |
183 | { | |
184 | struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); | |
185 | u32 num = GPIO_NUM(offset); | |
186 | unsigned dat; | |
187 | ||
188 | dat = readl(&plat->regs->dat); | |
189 | dat >>= num; | |
190 | ||
191 | return dat & 0x1; | |
192 | } | |
193 | ||
194 | static int sunxi_gpio_set_value(struct udevice *dev, unsigned offset, | |
195 | int value) | |
196 | { | |
197 | struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); | |
198 | u32 num = GPIO_NUM(offset); | |
199 | ||
200 | clrsetbits_le32(&plat->regs->dat, 1 << num, value ? (1 << num) : 0); | |
201 | return 0; | |
202 | } | |
203 | ||
204 | static int sunxi_gpio_get_function(struct udevice *dev, unsigned offset) | |
205 | { | |
206 | struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); | |
207 | int func; | |
208 | ||
209 | func = sunxi_gpio_get_cfgbank(plat->regs, offset); | |
210 | if (func == SUNXI_GPIO_OUTPUT) | |
211 | return GPIOF_OUTPUT; | |
212 | else if (func == SUNXI_GPIO_INPUT) | |
213 | return GPIOF_INPUT; | |
214 | else | |
215 | return GPIOF_FUNC; | |
216 | } | |
217 | ||
218 | static const struct dm_gpio_ops gpio_sunxi_ops = { | |
219 | .direction_input = sunxi_gpio_direction_input, | |
220 | .direction_output = sunxi_gpio_direction_output, | |
221 | .get_value = sunxi_gpio_get_value, | |
222 | .set_value = sunxi_gpio_set_value, | |
223 | .get_function = sunxi_gpio_get_function, | |
224 | }; | |
225 | ||
226 | /** | |
227 | * Returns the name of a GPIO bank | |
228 | * | |
229 | * GPIO banks are named A, B, C, ... | |
230 | * | |
231 | * @bank: Bank number (0, 1..n-1) | |
232 | * @return allocated string containing the name | |
233 | */ | |
234 | static char *gpio_bank_name(int bank) | |
235 | { | |
236 | char *name; | |
237 | ||
07ce60f3 | 238 | name = malloc(3); |
7aa97485 | 239 | if (name) { |
07ce60f3 SG |
240 | name[0] = 'P'; |
241 | name[1] = 'A' + bank; | |
242 | name[2] = '\0'; | |
7aa97485 SG |
243 | } |
244 | ||
245 | return name; | |
246 | } | |
247 | ||
248 | static int gpio_sunxi_probe(struct udevice *dev) | |
249 | { | |
250 | struct sunxi_gpio_platdata *plat = dev_get_platdata(dev); | |
e564f054 | 251 | struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); |
7aa97485 SG |
252 | |
253 | /* Tell the uclass how many GPIOs we have */ | |
254 | if (plat) { | |
255 | uc_priv->gpio_count = plat->gpio_count; | |
256 | uc_priv->bank_name = plat->bank_name; | |
257 | } | |
258 | ||
259 | return 0; | |
260 | } | |
261 | /** | |
262 | * We have a top-level GPIO device with no actual GPIOs. It has a child | |
263 | * device for each Sunxi bank. | |
264 | */ | |
265 | static int gpio_sunxi_bind(struct udevice *parent) | |
266 | { | |
267 | struct sunxi_gpio_platdata *plat = parent->platdata; | |
268 | struct sunxi_gpio_reg *ctlr; | |
fbf10ae9 | 269 | int bank, no_banks, ret, start; |
7aa97485 SG |
270 | |
271 | /* If this is a child device, there is nothing to do here */ | |
272 | if (plat) | |
273 | return 0; | |
274 | ||
fbf10ae9 HG |
275 | if (fdt_node_check_compatible(gd->fdt_blob, parent->of_offset, |
276 | "allwinner,sun6i-a31-r-pinctrl") == 0) { | |
277 | start = 'L' - 'A'; | |
278 | no_banks = 2; /* L & M */ | |
279 | } else if (fdt_node_check_compatible(gd->fdt_blob, parent->of_offset, | |
280 | "allwinner,sun8i-a23-r-pinctrl") == 0) { | |
281 | start = 'L' - 'A'; | |
282 | no_banks = 1; /* L only */ | |
283 | } else { | |
284 | start = 0; | |
285 | no_banks = SUNXI_GPIO_BANKS; | |
286 | } | |
287 | ||
4e9838c1 | 288 | ctlr = (struct sunxi_gpio_reg *)dev_get_addr(parent); |
fbf10ae9 | 289 | for (bank = 0; bank < no_banks; bank++) { |
7aa97485 SG |
290 | struct sunxi_gpio_platdata *plat; |
291 | struct udevice *dev; | |
292 | ||
293 | plat = calloc(1, sizeof(*plat)); | |
294 | if (!plat) | |
295 | return -ENOMEM; | |
296 | plat->regs = &ctlr->gpio_bank[bank]; | |
fbf10ae9 | 297 | plat->bank_name = gpio_bank_name(start + bank); |
7aa97485 SG |
298 | plat->gpio_count = SUNXI_GPIOS_PER_BANK; |
299 | ||
300 | ret = device_bind(parent, parent->driver, | |
301 | plat->bank_name, plat, -1, &dev); | |
302 | if (ret) | |
303 | return ret; | |
304 | dev->of_offset = parent->of_offset; | |
305 | } | |
306 | ||
307 | return 0; | |
308 | } | |
309 | ||
310 | static const struct udevice_id sunxi_gpio_ids[] = { | |
11d52a9d HG |
311 | { .compatible = "allwinner,sun4i-a10-pinctrl" }, |
312 | { .compatible = "allwinner,sun5i-a10s-pinctrl" }, | |
313 | { .compatible = "allwinner,sun5i-a13-pinctrl" }, | |
314 | { .compatible = "allwinner,sun6i-a31-pinctrl" }, | |
315 | { .compatible = "allwinner,sun6i-a31s-pinctrl" }, | |
7aa97485 | 316 | { .compatible = "allwinner,sun7i-a20-pinctrl" }, |
11d52a9d | 317 | { .compatible = "allwinner,sun8i-a23-pinctrl" }, |
a52783e1 | 318 | { .compatible = "allwinner,sun8i-a33-pinctrl" }, |
11d52a9d | 319 | { .compatible = "allwinner,sun9i-a80-pinctrl" }, |
fbf10ae9 HG |
320 | { .compatible = "allwinner,sun6i-a31-r-pinctrl" }, |
321 | { .compatible = "allwinner,sun8i-a23-r-pinctrl" }, | |
7aa97485 SG |
322 | { } |
323 | }; | |
324 | ||
325 | U_BOOT_DRIVER(gpio_sunxi) = { | |
326 | .name = "gpio_sunxi", | |
327 | .id = UCLASS_GPIO, | |
328 | .ops = &gpio_sunxi_ops, | |
329 | .of_match = sunxi_gpio_ids, | |
330 | .bind = gpio_sunxi_bind, | |
331 | .probe = gpio_sunxi_probe, | |
332 | }; | |
333 | #endif |