blob: 3ca590700c4ce86e07a15958fe2961b6086ca219 [file] [log] [blame]
Joachim Foerster03d67e12011-10-21 15:48:50 +02001/*
2 * Driver for Altera's PIO ip core
3 *
4 * Copyright (C) 2011 Missing Link Electronics
5 * Joachim Foerster <joachim@missinglinkelectronics.com>
6 *
Wolfgang Denk1a459662013-07-08 09:37:19 +02007 * SPDX-License-Identifier: GPL-2.0+
Joachim Foerster03d67e12011-10-21 15:48:50 +02008 *
9 * To use this driver, in your board's config. header:
10 * #define CONFIG_ALTERA_PIO
11 * #define CONFIG_SYS_ALTERA_PIO_NUM <number-of-pio-cores>
12 * #define CONFIG_SYS_ALTERA_PIO_GPIO_NUM <total-number-of-gpios>
13 * And in your board's early setup routine:
14 * altera_pio_init(<baseaddr>, <width>, 'i'|'o'|'t',
15 * <reset-value>, <neg-mask>, "label");
16 * - 'i'|'o'|'t': PIO is input-only/output-only/tri-state
17 * - <reset-value>: for correct initial status display, output-only
18 * - <neg-mask> is meant to be used to in cases of active-low
19 * GPIOs, such as LEDs and buttons (on/pressed == 0). Each bit
20 * which is 1 in <neg-mask> inverts the corresponding GPIO's value
21 * before set/after get. So: gpio_set_value(gpio, 1) => LED on .
22 *
23 * Do NOT define CONFIG_SYS_GPIO_BASE !
24 *
25 * Optionally, in your board's config. header:
26 * - To force a GPIO numbering scheme like in Linux ...
27 * #define CONFIG_GPIO_DOWNTO_NUMBERING
28 * ... starting with 255 (default)
29 * #define CONFIG_GPIO_DOWNTO_MAX 255
30 */
31#include <common.h>
32#include <asm/io.h>
33#include <asm/gpio.h>
34
35#ifdef CONFIG_GPIO_DOWNTO_NUMBERING
36#ifndef CONFIG_GPIO_DOWNTO_MAX
37#define CONFIG_GPIO_DOWNTO_MAX 255
38#endif
39#endif
40
41#define ALTERA_PIO_DATA 0x0
42#define ALTERA_PIO_DIR 0x4
43
44#define GPIO_LABEL_SIZE 9
45
46
47static struct altera_pio {
48 u32 base;
49 u8 width;
50 char iot;
51 u32 negmask;
52 u32 sh_data;
53 u32 sh_dir;
54 int gidx;
55 char label[GPIO_LABEL_SIZE];
56} pios[CONFIG_SYS_ALTERA_PIO_NUM];
57
58static int pio_num;
59
60static struct altera_pio_gpio {
61 unsigned num;
62 struct altera_pio *pio;
63 char reqlabel[GPIO_LABEL_SIZE];
64} gpios[CONFIG_SYS_ALTERA_PIO_GPIO_NUM];
65
66static int pio_gpio_num;
67
68
69static int altera_pio_gidx(unsigned gpio)
70{
71 int i;
72
73 for (i = 0; i < pio_gpio_num; ++i) {
74 if (gpio == gpios[i].num)
75 break;
76 }
77 if (i >= pio_gpio_num)
78 return -1;
79 return i;
80}
81
82static struct altera_pio *altera_pio_get_and_mask(unsigned gpio, u32 *mask)
83{
84 int gidx = altera_pio_gidx(gpio);
85 if (gidx < 0)
86 return NULL;
87 if (mask)
88 *mask = 1 << (gidx - gpios[gidx].pio->gidx);
89 return gpios[gidx].pio;
90}
91
92#define altera_pio_use_gidx(_gidx, _reqlabel) \
93 { strncpy(gpios[_gidx].reqlabel, _reqlabel, GPIO_LABEL_SIZE); }
94#define altera_pio_unuse_gidx(_gidx) { gpios[_gidx].reqlabel[0] = '\0'; }
95#define altera_pio_is_gidx_used(_gidx) (gpios[_gidx].reqlabel[0] != '\0')
96
97static int altera_pio_gpio_init(struct altera_pio *pio, u8 width)
98{
99 u8 gidx = pio_gpio_num;
100 int i;
101
102 if (!width)
103 return -1;
104 if ((pio_gpio_num + width) > CONFIG_SYS_ALTERA_PIO_GPIO_NUM)
105 return -1;
106
107 for (i = 0; i < width; ++i) {
108#ifdef CONFIG_GPIO_DOWNTO_NUMBERING
109 gpios[pio_gpio_num + i].num = \
110 CONFIG_GPIO_DOWNTO_MAX + 1 - gidx - width + i;
111#else
112 gpios[pio_gpio_num + i].num = pio_gpio_num + i;
113#endif
114 gpios[pio_gpio_num + i].pio = pio;
115 altera_pio_unuse_gidx(pio_gpio_num + i);
116 }
117 pio_gpio_num += width;
118 return gidx;
119}
120
121int altera_pio_init(u32 base, u8 width, char iot, u32 rstval, u32 negmask,
122 const char *label)
123{
124 if (pio_num >= CONFIG_SYS_ALTERA_PIO_NUM)
125 return -1;
126
127 pios[pio_num].base = base;
128 pios[pio_num].width = width;
129 pios[pio_num].iot = iot;
130 switch (iot) {
131 case 'i':
132 /* input only */
133 pios[pio_num].sh_dir = 0;
134 pios[pio_num].sh_data = readl(base + ALTERA_PIO_DATA);
135 break;
136 case 'o':
137 /* output only */
138 pios[pio_num].sh_dir = 0xffffffff & ((1 << width) - 1);
139 pios[pio_num].sh_data = rstval;
140 break;
141 case 't':
142 /* bidir, tri-state */
143 pios[pio_num].sh_dir = readl(base + ALTERA_PIO_DIR);
144 pios[pio_num].sh_data = readl(base + ALTERA_PIO_DATA);
145 break;
146 default:
147 return -1;
148 }
149 pios[pio_num].negmask = negmask & ((1 << width) - 1);
150 pios[pio_num].gidx = altera_pio_gpio_init(&pios[pio_num], width);
151 if (pios[pio_num].gidx < 0)
152 return -1;
153 strncpy(pios[pio_num].label, label, GPIO_LABEL_SIZE);
154 return pio_num++;
155}
156
157void altera_pio_info(void)
158{
159 int i;
160 int j;
161 int gidx;
162 u32 mask;
163
164 for (i = 0; i < pio_num; ++i) {
165 printf("Altera PIO % 2d, @0x%08x, "
166 "width: %u, label: %s\n",
167 i, pios[i].base, pios[i].width, pios[i].label);
168 gidx = pios[i].gidx;
169 for (j = gidx; j < (gidx + pios[i].width); ++j) {
170 mask = 1 << (j - gidx);
171 printf("\tGPIO % 4d: %s %s [%c] %s\n",
172 gpios[j].num,
173 gpios[j].pio->sh_dir & mask ? "out" : " in",
174 gpio_get_value(gpios[j].num) ? "set" : "clr",
175 altera_pio_is_gidx_used(j) ? 'x' : ' ',
176 gpios[j].reqlabel);
177 }
178 }
179}
180
181
182int gpio_request(unsigned gpio, const char *label)
183{
184 int gidx = altera_pio_gidx(gpio);
185 if (gidx < 0)
186 return gidx;
187 if (altera_pio_is_gidx_used(gidx))
188 return -1;
189
190 altera_pio_use_gidx(gidx, label);
191 return 0;
192}
193
194int gpio_free(unsigned gpio)
195{
196 int gidx = altera_pio_gidx(gpio);
197 if (gidx < 0)
198 return gidx;
199 if (!altera_pio_is_gidx_used(gidx))
200 return -1;
201
202 altera_pio_unuse_gidx(gidx);
203 return 0;
204}
205
206int gpio_direction_input(unsigned gpio)
207{
208 u32 mask;
209 struct altera_pio *pio;
210
211 pio = altera_pio_get_and_mask(gpio, &mask);
212 if (!pio)
213 return -1;
214 if (pio->iot == 'o')
215 return -1;
216
217 writel(pio->sh_dir &= ~mask, pio->base + ALTERA_PIO_DIR);
218 return 0;
219}
220
221int gpio_direction_output(unsigned gpio, int value)
222{
223 u32 mask;
224 struct altera_pio *pio;
225
226 pio = altera_pio_get_and_mask(gpio, &mask);
227 if (!pio)
228 return -1;
229 if (pio->iot == 'i')
230 return -1;
231
232 value = (pio->negmask & mask) ? !value : value;
233 if (value)
234 pio->sh_data |= mask;
235 else
236 pio->sh_data &= ~mask;
237 writel(pio->sh_data, pio->base + ALTERA_PIO_DATA);
238 writel(pio->sh_dir |= mask, pio->base + ALTERA_PIO_DIR);
239 return 0;
240}
241
242int gpio_get_value(unsigned gpio)
243{
244 u32 mask;
245 struct altera_pio *pio;
246 u32 val;
247
248 pio = altera_pio_get_and_mask(gpio, &mask);
249 if (!pio)
250 return -1;
251
252 if ((pio->sh_dir & mask) || (pio->iot == 'o'))
253 val = pio->sh_data & mask;
254 else
255 val = readl(pio->base + ALTERA_PIO_DATA) & mask;
256 return (pio->negmask & mask) ? !val : val;
257}
258
259void gpio_set_value(unsigned gpio, int value)
260{
261 u32 mask;
262 struct altera_pio *pio;
263
264 pio = altera_pio_get_and_mask(gpio, &mask);
265 if (!pio)
266 return;
267 if (pio->iot == 'i')
268 return;
269
270 value = (pio->negmask & mask) ? !value : value;
271 if (value)
272 pio->sh_data |= mask;
273 else
274 pio->sh_data &= ~mask;
275 writel(pio->sh_data, pio->base + ALTERA_PIO_DATA);
276 return;
277}
278
279int gpio_is_valid(int number)
280{
281 int gidx = altera_pio_gidx(number);
282
283 if (gidx < 0)
284 return 1;
285 return 0;
286}