]>
Commit | Line | Data |
---|---|---|
0c872ecd TR |
1 | /* |
2 | * Copyright (c) 2009 Wind River Systems, Inc. | |
3 | * Tom Rix <[email protected]> | |
4 | * | |
bcd4d4eb | 5 | * SPDX-License-Identifier: GPL-2.0 |
0c872ecd TR |
6 | * |
7 | * This work is derived from the linux 2.6.27 kernel source | |
8 | * To fetch, use the kernel repository | |
9 | * git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git | |
10 | * Use the v2.6.27 tag. | |
11 | * | |
12 | * Below is the original's header including its copyright | |
13 | * | |
14 | * linux/arch/arm/plat-omap/gpio.c | |
15 | * | |
16 | * Support functions for OMAP GPIO | |
17 | * | |
18 | * Copyright (C) 2003-2005 Nokia Corporation | |
19 | * Written by Juha Yrjölä <[email protected]> | |
0c872ecd TR |
20 | */ |
21 | #include <common.h> | |
5915a2ad | 22 | #include <dm.h> |
0a9e3405 | 23 | #include <fdtdec.h> |
365d6070 | 24 | #include <asm/gpio.h> |
0c872ecd TR |
25 | #include <asm/io.h> |
26 | #include <asm/errno.h> | |
0a9e3405 TR |
27 | #include <malloc.h> |
28 | ||
29 | DECLARE_GLOBAL_DATA_PTR; | |
0c872ecd | 30 | |
81bdc155 SP |
31 | #define OMAP_GPIO_DIR_OUT 0 |
32 | #define OMAP_GPIO_DIR_IN 1 | |
33 | ||
5915a2ad SG |
34 | #ifdef CONFIG_DM_GPIO |
35 | ||
5915a2ad SG |
36 | #define GPIO_PER_BANK 32 |
37 | ||
38 | struct gpio_bank { | |
5915a2ad SG |
39 | /* TODO([email protected]): Can we use a struct here? */ |
40 | void *base; /* address of registers in physical memory */ | |
5915a2ad SG |
41 | }; |
42 | ||
43 | #endif | |
44 | ||
0c872ecd TR |
45 | static inline int get_gpio_index(int gpio) |
46 | { | |
47 | return gpio & 0x1f; | |
48 | } | |
49 | ||
dcee1ab3 | 50 | int gpio_is_valid(int gpio) |
0c872ecd | 51 | { |
87bd05d7 | 52 | return (gpio >= 0) && (gpio < OMAP_MAX_GPIO); |
0c872ecd TR |
53 | } |
54 | ||
25223a68 A |
55 | static void _set_gpio_direction(const struct gpio_bank *bank, int gpio, |
56 | int is_input) | |
0c872ecd TR |
57 | { |
58 | void *reg = bank->base; | |
59 | u32 l; | |
60 | ||
0a9e3405 TR |
61 | reg += OMAP_GPIO_OE; |
62 | ||
0c872ecd TR |
63 | l = __raw_readl(reg); |
64 | if (is_input) | |
65 | l |= 1 << gpio; | |
66 | else | |
67 | l &= ~(1 << gpio); | |
68 | __raw_writel(l, reg); | |
69 | } | |
70 | ||
81bdc155 SP |
71 | /** |
72 | * Get the direction of the GPIO by reading the GPIO_OE register | |
73 | * corresponding to the specified bank. | |
74 | */ | |
75 | static int _get_gpio_direction(const struct gpio_bank *bank, int gpio) | |
0c872ecd | 76 | { |
81bdc155 SP |
77 | void *reg = bank->base; |
78 | u32 v; | |
0c872ecd | 79 | |
0a9e3405 | 80 | reg += OMAP_GPIO_OE; |
81bdc155 SP |
81 | |
82 | v = __raw_readl(reg); | |
83 | ||
84 | if (v & (1 << gpio)) | |
85 | return OMAP_GPIO_DIR_IN; | |
86 | else | |
87 | return OMAP_GPIO_DIR_OUT; | |
0c872ecd TR |
88 | } |
89 | ||
25223a68 A |
90 | static void _set_gpio_dataout(const struct gpio_bank *bank, int gpio, |
91 | int enable) | |
0c872ecd TR |
92 | { |
93 | void *reg = bank->base; | |
94 | u32 l = 0; | |
95 | ||
0a9e3405 TR |
96 | if (enable) |
97 | reg += OMAP_GPIO_SETDATAOUT; | |
98 | else | |
99 | reg += OMAP_GPIO_CLEARDATAOUT; | |
100 | ||
101 | l = 1 << gpio; | |
0c872ecd TR |
102 | __raw_writel(l, reg); |
103 | } | |
104 | ||
d57b6114 SG |
105 | static int _get_gpio_value(const struct gpio_bank *bank, int gpio) |
106 | { | |
107 | void *reg = bank->base; | |
108 | int input; | |
109 | ||
0a9e3405 TR |
110 | input = _get_gpio_direction(bank, gpio); |
111 | switch (input) { | |
112 | case OMAP_GPIO_DIR_IN: | |
113 | reg += OMAP_GPIO_DATAIN; | |
114 | break; | |
115 | case OMAP_GPIO_DIR_OUT: | |
116 | reg += OMAP_GPIO_DATAOUT; | |
d57b6114 SG |
117 | break; |
118 | default: | |
119 | return -1; | |
120 | } | |
121 | ||
122 | return (__raw_readl(reg) & (1 << gpio)) != 0; | |
123 | } | |
124 | ||
5915a2ad SG |
125 | #ifndef CONFIG_DM_GPIO |
126 | ||
d57b6114 SG |
127 | static inline const struct gpio_bank *get_gpio_bank(int gpio) |
128 | { | |
129 | return &omap_gpio_bank[gpio >> 5]; | |
130 | } | |
131 | ||
132 | static int check_gpio(int gpio) | |
133 | { | |
134 | if (!gpio_is_valid(gpio)) { | |
135 | printf("ERROR : check_gpio: invalid GPIO %d\n", gpio); | |
136 | return -1; | |
137 | } | |
138 | return 0; | |
139 | } | |
140 | ||
81bdc155 SP |
141 | /** |
142 | * Set value of the specified gpio | |
143 | */ | |
365d6070 | 144 | int gpio_set_value(unsigned gpio, int value) |
0c872ecd | 145 | { |
25223a68 | 146 | const struct gpio_bank *bank; |
0c872ecd TR |
147 | |
148 | if (check_gpio(gpio) < 0) | |
365d6070 | 149 | return -1; |
0c872ecd | 150 | bank = get_gpio_bank(gpio); |
81bdc155 | 151 | _set_gpio_dataout(bank, get_gpio_index(gpio), value); |
365d6070 JH |
152 | |
153 | return 0; | |
0c872ecd TR |
154 | } |
155 | ||
81bdc155 SP |
156 | /** |
157 | * Get value of the specified gpio | |
158 | */ | |
365d6070 | 159 | int gpio_get_value(unsigned gpio) |
0c872ecd | 160 | { |
25223a68 | 161 | const struct gpio_bank *bank; |
0c872ecd TR |
162 | |
163 | if (check_gpio(gpio) < 0) | |
365d6070 | 164 | return -1; |
0c872ecd | 165 | bank = get_gpio_bank(gpio); |
d57b6114 SG |
166 | |
167 | return _get_gpio_value(bank, get_gpio_index(gpio)); | |
0c872ecd TR |
168 | } |
169 | ||
81bdc155 SP |
170 | /** |
171 | * Set gpio direction as input | |
172 | */ | |
173 | int gpio_direction_input(unsigned gpio) | |
569919d8 | 174 | { |
81bdc155 | 175 | const struct gpio_bank *bank; |
569919d8 JF |
176 | |
177 | if (check_gpio(gpio) < 0) | |
365d6070 | 178 | return -1; |
81bdc155 | 179 | |
569919d8 | 180 | bank = get_gpio_bank(gpio); |
81bdc155 SP |
181 | _set_gpio_direction(bank, get_gpio_index(gpio), 1); |
182 | ||
183 | return 0; | |
569919d8 JF |
184 | } |
185 | ||
81bdc155 SP |
186 | /** |
187 | * Set gpio direction as output | |
188 | */ | |
189 | int gpio_direction_output(unsigned gpio, int value) | |
0c872ecd | 190 | { |
81bdc155 SP |
191 | const struct gpio_bank *bank; |
192 | ||
193 | if (check_gpio(gpio) < 0) | |
365d6070 | 194 | return -1; |
81bdc155 SP |
195 | |
196 | bank = get_gpio_bank(gpio); | |
197 | _set_gpio_dataout(bank, get_gpio_index(gpio), value); | |
198 | _set_gpio_direction(bank, get_gpio_index(gpio), 0); | |
199 | ||
200 | return 0; | |
0c872ecd TR |
201 | } |
202 | ||
81bdc155 SP |
203 | /** |
204 | * Request a gpio before using it. | |
205 | * | |
206 | * NOTE: Argument 'label' is unused. | |
207 | */ | |
365d6070 | 208 | int gpio_request(unsigned gpio, const char *label) |
0c872ecd TR |
209 | { |
210 | if (check_gpio(gpio) < 0) | |
365d6070 | 211 | return -1; |
0c872ecd TR |
212 | |
213 | return 0; | |
214 | } | |
215 | ||
81bdc155 SP |
216 | /** |
217 | * Reset and free the gpio after using it. | |
218 | */ | |
365d6070 | 219 | int gpio_free(unsigned gpio) |
0c872ecd | 220 | { |
365d6070 | 221 | return 0; |
0c872ecd | 222 | } |
5915a2ad SG |
223 | |
224 | #else /* new driver model interface CONFIG_DM_GPIO */ | |
225 | ||
5915a2ad SG |
226 | /* set GPIO pin 'gpio' as an input */ |
227 | static int omap_gpio_direction_input(struct udevice *dev, unsigned offset) | |
228 | { | |
229 | struct gpio_bank *bank = dev_get_priv(dev); | |
5915a2ad SG |
230 | |
231 | /* Configure GPIO direction as input. */ | |
232 | _set_gpio_direction(bank, offset, 1); | |
233 | ||
234 | return 0; | |
235 | } | |
236 | ||
237 | /* set GPIO pin 'gpio' as an output, with polarity 'value' */ | |
238 | static int omap_gpio_direction_output(struct udevice *dev, unsigned offset, | |
239 | int value) | |
240 | { | |
241 | struct gpio_bank *bank = dev_get_priv(dev); | |
5915a2ad SG |
242 | |
243 | _set_gpio_dataout(bank, offset, value); | |
244 | _set_gpio_direction(bank, offset, 0); | |
245 | ||
246 | return 0; | |
247 | } | |
248 | ||
249 | /* read GPIO IN value of pin 'gpio' */ | |
250 | static int omap_gpio_get_value(struct udevice *dev, unsigned offset) | |
251 | { | |
252 | struct gpio_bank *bank = dev_get_priv(dev); | |
5915a2ad SG |
253 | |
254 | return _get_gpio_value(bank, offset); | |
255 | } | |
256 | ||
257 | /* write GPIO OUT value to pin 'gpio' */ | |
258 | static int omap_gpio_set_value(struct udevice *dev, unsigned offset, | |
259 | int value) | |
260 | { | |
261 | struct gpio_bank *bank = dev_get_priv(dev); | |
5915a2ad SG |
262 | |
263 | _set_gpio_dataout(bank, offset, value); | |
264 | ||
265 | return 0; | |
266 | } | |
267 | ||
5915a2ad SG |
268 | static int omap_gpio_get_function(struct udevice *dev, unsigned offset) |
269 | { | |
270 | struct gpio_bank *bank = dev_get_priv(dev); | |
271 | ||
5915a2ad | 272 | /* GPIOF_FUNC is not implemented yet */ |
26c0472c | 273 | if (_get_gpio_direction(bank, offset) == OMAP_GPIO_DIR_OUT) |
5915a2ad SG |
274 | return GPIOF_OUTPUT; |
275 | else | |
276 | return GPIOF_INPUT; | |
277 | } | |
278 | ||
279 | static const struct dm_gpio_ops gpio_omap_ops = { | |
5915a2ad SG |
280 | .direction_input = omap_gpio_direction_input, |
281 | .direction_output = omap_gpio_direction_output, | |
282 | .get_value = omap_gpio_get_value, | |
283 | .set_value = omap_gpio_set_value, | |
284 | .get_function = omap_gpio_get_function, | |
5915a2ad SG |
285 | }; |
286 | ||
287 | static int omap_gpio_probe(struct udevice *dev) | |
288 | { | |
289 | struct gpio_bank *bank = dev_get_priv(dev); | |
290 | struct omap_gpio_platdata *plat = dev_get_platdata(dev); | |
e564f054 | 291 | struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev); |
5915a2ad | 292 | |
0a9e3405 | 293 | uc_priv->bank_name = plat->port_name; |
5915a2ad SG |
294 | uc_priv->gpio_count = GPIO_PER_BANK; |
295 | bank->base = (void *)plat->base; | |
5915a2ad SG |
296 | |
297 | return 0; | |
298 | } | |
299 | ||
0a9e3405 TR |
300 | static int omap_gpio_bind(struct udevice *dev) |
301 | { | |
302 | struct omap_gpio_platdata *plat = dev->platdata; | |
303 | fdt_addr_t base_addr; | |
304 | ||
305 | if (plat) | |
306 | return 0; | |
307 | ||
308 | base_addr = dev_get_addr(dev); | |
309 | if (base_addr == FDT_ADDR_T_NONE) | |
310 | return -ENODEV; | |
311 | ||
312 | /* | |
313 | * TODO: | |
314 | * When every board is converted to driver model and DT is | |
315 | * supported, this can be done by auto-alloc feature, but | |
316 | * not using calloc to alloc memory for platdata. | |
317 | */ | |
318 | plat = calloc(1, sizeof(*plat)); | |
319 | if (!plat) | |
320 | return -ENOMEM; | |
321 | ||
322 | plat->base = base_addr; | |
323 | plat->port_name = fdt_get_name(gd->fdt_blob, dev->of_offset, NULL); | |
324 | dev->platdata = plat; | |
325 | ||
326 | return 0; | |
327 | } | |
328 | ||
329 | static const struct udevice_id omap_gpio_ids[] = { | |
330 | { .compatible = "ti,omap3-gpio" }, | |
331 | { .compatible = "ti,omap4-gpio" }, | |
332 | { .compatible = "ti,am4372-gpio" }, | |
333 | { } | |
334 | }; | |
335 | ||
5915a2ad SG |
336 | U_BOOT_DRIVER(gpio_omap) = { |
337 | .name = "gpio_omap", | |
338 | .id = UCLASS_GPIO, | |
339 | .ops = &gpio_omap_ops, | |
0a9e3405 TR |
340 | .of_match = omap_gpio_ids, |
341 | .bind = omap_gpio_bind, | |
5915a2ad SG |
342 | .probe = omap_gpio_probe, |
343 | .priv_auto_alloc_size = sizeof(struct gpio_bank), | |
344 | }; | |
345 | ||
346 | #endif /* CONFIG_DM_GPIO */ |