]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - drivers/gpio/sunxi_gpio.c
57b78e55e23b9ef048bb4edd69ad110443ec9384
[karo-tx-uboot.git] / drivers / gpio / sunxi_gpio.c
1 /*
2  * (C) Copyright 2012 Henrik Nordstrom <henrik@henriknordstrom.net>
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 <tangliang@allwinnertech.com>
9  *
10  * SPDX-License-Identifier:     GPL-2.0+
11  */
12
13 #include <common.h>
14 #include <dm.h>
15 #include <errno.h>
16 #include <fdtdec.h>
17 #include <malloc.h>
18 #include <asm/arch/gpio.h>
19 #include <asm/io.h>
20 #include <asm/gpio.h>
21 #include <dm/device-internal.h>
22
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
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
79         return 0;
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;
105
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 }
121 #endif
122
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
137 #ifdef CONFIG_DM_GPIO
138 /* TODO(sjg@chromium.org): Remove this function and use device tree */
139 int sunxi_name_to_gpio(const char *name)
140 {
141         unsigned int gpio;
142         int ret;
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
156         ret = gpio_lookup_name(name, NULL, NULL, &gpio);
157
158         return ret ? ret : gpio;
159 }
160
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
238         name = malloc(3);
239         if (name) {
240                 name[0] = 'P';
241                 name[1] = 'A' + bank;
242                 name[2] = '\0';
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);
251         struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
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;
269         int bank, no_banks, ret, start;
270
271         /* If this is a child device, there is nothing to do here */
272         if (plat)
273                 return 0;
274
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
288         ctlr = (struct sunxi_gpio_reg *)fdtdec_get_addr(gd->fdt_blob,
289                                                    parent->of_offset, "reg");
290         for (bank = 0; bank < no_banks; bank++) {
291                 struct sunxi_gpio_platdata *plat;
292                 struct udevice *dev;
293
294                 plat = calloc(1, sizeof(*plat));
295                 if (!plat)
296                         return -ENOMEM;
297                 plat->regs = &ctlr->gpio_bank[bank];
298                 plat->bank_name = gpio_bank_name(start + bank);
299                 plat->gpio_count = SUNXI_GPIOS_PER_BANK;
300
301                 ret = device_bind(parent, parent->driver,
302                                         plat->bank_name, plat, -1, &dev);
303                 if (ret)
304                         return ret;
305                 dev->of_offset = parent->of_offset;
306         }
307
308         return 0;
309 }
310
311 static const struct udevice_id sunxi_gpio_ids[] = {
312         { .compatible = "allwinner,sun4i-a10-pinctrl" },
313         { .compatible = "allwinner,sun5i-a10s-pinctrl" },
314         { .compatible = "allwinner,sun5i-a13-pinctrl" },
315         { .compatible = "allwinner,sun6i-a31-pinctrl" },
316         { .compatible = "allwinner,sun6i-a31s-pinctrl" },
317         { .compatible = "allwinner,sun7i-a20-pinctrl" },
318         { .compatible = "allwinner,sun8i-a23-pinctrl" },
319         { .compatible = "allwinner,sun8i-a33-pinctrl" },
320         { .compatible = "allwinner,sun9i-a80-pinctrl" },
321         { .compatible = "allwinner,sun6i-a31-r-pinctrl" },
322         { .compatible = "allwinner,sun8i-a23-r-pinctrl" },
323         { }
324 };
325
326 U_BOOT_DRIVER(gpio_sunxi) = {
327         .name   = "gpio_sunxi",
328         .id     = UCLASS_GPIO,
329         .ops    = &gpio_sunxi_ops,
330         .of_match = sunxi_gpio_ids,
331         .bind   = gpio_sunxi_bind,
332         .probe  = gpio_sunxi_probe,
333 };
334 #endif