]>
Commit | Line | Data |
---|---|---|
0c872ecd TR |
1 | /* |
2 | * Copyright (c) 2009 Wind River Systems, Inc. | |
3 | * Tom Rix <Tom.Rix@windriver.com> | |
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ä <juha.yrjola@nokia.com> | |
0c872ecd TR |
20 | */ |
21 | #include <common.h> | |
5915a2ad | 22 | #include <dm.h> |
0a9e3405 | 23 | #include <fdtdec.h> |
365d6070 | 24 | #include <asm/gpio.h> |
0c872ecd | 25 | #include <asm/io.h> |
1221ce45 | 26 | #include <linux/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(sjg@chromium.org): 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 | { | |
4d686041 | 302 | struct omap_gpio_platdata *plat = dev_get_platdata(dev); |
0a9e3405 TR |
303 | fdt_addr_t base_addr; |
304 | ||
305 | if (plat) | |
306 | return 0; | |
307 | ||
a821c4af | 308 | base_addr = devfdt_get_addr(dev); |
0a9e3405 | 309 | if (base_addr == FDT_ADDR_T_NONE) |
7c84319a | 310 | return -EINVAL; |
0a9e3405 TR |
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. | |
4d686041 SG |
317 | * |
318 | * For example am33xx_gpio uses platform data rather than device tree. | |
319 | * | |
320 | * NOTE: DO NOT COPY this code if you are using device tree. | |
0a9e3405 TR |
321 | */ |
322 | plat = calloc(1, sizeof(*plat)); | |
323 | if (!plat) | |
324 | return -ENOMEM; | |
325 | ||
326 | plat->base = base_addr; | |
e160f7d4 | 327 | plat->port_name = fdt_get_name(gd->fdt_blob, dev_of_offset(dev), NULL); |
0a9e3405 TR |
328 | dev->platdata = plat; |
329 | ||
330 | return 0; | |
331 | } | |
332 | ||
333 | static const struct udevice_id omap_gpio_ids[] = { | |
334 | { .compatible = "ti,omap3-gpio" }, | |
335 | { .compatible = "ti,omap4-gpio" }, | |
336 | { .compatible = "ti,am4372-gpio" }, | |
337 | { } | |
338 | }; | |
339 | ||
5915a2ad SG |
340 | U_BOOT_DRIVER(gpio_omap) = { |
341 | .name = "gpio_omap", | |
342 | .id = UCLASS_GPIO, | |
343 | .ops = &gpio_omap_ops, | |
0a9e3405 TR |
344 | .of_match = omap_gpio_ids, |
345 | .bind = omap_gpio_bind, | |
5915a2ad SG |
346 | .probe = omap_gpio_probe, |
347 | .priv_auto_alloc_size = sizeof(struct gpio_bank), | |
348 | }; | |
349 | ||
350 | #endif /* CONFIG_DM_GPIO */ |