mvsocgpp.c revision 1.1.2.2 1 /* $NetBSD: mvsocgpp.c,v 1.1.2.2 2010/10/09 03:31:40 yamt Exp $ */
2 /*
3 * Copyright (c) 2008, 2010 KIYOHARA Takashi
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
19 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
23 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
24 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include <sys/cdefs.h>
29 __KERNEL_RCSID(0, "$NetBSD: mvsocgpp.c,v 1.1.2.2 2010/10/09 03:31:40 yamt Exp $");
30
31 #include "gpio.h"
32
33 #define _INTR_PRIVATE
34
35 #include <sys/param.h>
36 #include <sys/bus.h>
37 #include <sys/device.h>
38 #include <sys/errno.h>
39 #include <sys/evcnt.h>
40 #include <sys/gpio.h>
41 #include <sys/kmem.h>
42
43 #include <machine/intr.h>
44
45 #include <arm/marvell/mvsocreg.h>
46 #include <arm/marvell/mvsocvar.h>
47 #include <arm/marvell/mvsocgppreg.h>
48 #include <arm/marvell/mvsocgppvar.h>
49 #include <arm/pic/picvar.h>
50
51 #include <dev/marvell/marvellvar.h>
52
53 #if NGPIO > 0
54 #include <sys/gpio.h>
55 #include <dev/gpio/gpiovar.h>
56 #endif
57
58 #define MVSOCGPP_DUMPREG
59
60 #define MVSOCGPP_READ(sc, reg) \
61 bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))
62 #define MVSOCGPP_WRITE(sc, reg, val) \
63 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
64
65 struct mvsocgpp_softc {
66 device_t sc_dev;
67
68 bus_space_tag_t sc_iot;
69 bus_space_handle_t sc_ioh;
70
71 struct mvsocgpp_pic {
72 struct pic_softc gpio_pic;
73 int group;
74 uint32_t edge;
75 uint32_t level;
76 } *sc_pic;
77
78 #if NGPIO > 0
79 struct gpio_chipset_tag sc_gpio_chipset;
80 gpio_pin_t *sc_pins;
81 #endif
82 };
83
84 static int mvsocgpp_match(device_t, struct cfdata *, void *);
85 static void mvsocgpp_attach(device_t, device_t, void *);
86
87 #ifdef MVSOCGPP_DUMPREG
88 static void mvsocgpp_dump_reg(struct mvsocgpp_softc *);
89 #endif
90
91 static void gpio_pic_unblock_irqs(struct pic_softc *, size_t, uint32_t);
92 static void gpio_pic_block_irqs(struct pic_softc *, size_t, uint32_t);
93 static int gpio_pic_find_pending_irqs(struct pic_softc *);
94 static void gpio_pic_establish_irq(struct pic_softc *, struct intrsource *);
95
96 static struct pic_ops gpio_pic_ops = {
97 .pic_unblock_irqs = gpio_pic_unblock_irqs,
98 .pic_block_irqs = gpio_pic_block_irqs,
99 .pic_find_pending_irqs = gpio_pic_find_pending_irqs,
100 .pic_establish_irq = gpio_pic_establish_irq,
101 };
102
103 static struct mvsocgpp_softc *mvsocgpp_softc; /* One unit per One SoC */
104 int gpp_irqbase = 0;
105 int gpp_npins = 0;
106
107
108 CFATTACH_DECL_NEW(mvsocgpp, sizeof(struct mvsocgpp_softc),
109 mvsocgpp_match, mvsocgpp_attach, NULL, NULL);
110
111
112 /* ARGSUSED */
113 static int
114 mvsocgpp_match(device_t parent, struct cfdata *match, void *aux)
115 {
116 struct marvell_attach_args *mva = aux;
117
118 if (strcmp(mva->mva_name, match->cf_name) != 0)
119 return 0;
120 if (mva->mva_offset == MVA_OFFSET_DEFAULT ||
121 mva->mva_irq == MVA_IRQ_DEFAULT)
122 return 0;
123
124 mva->mva_size = MVSOC_GPP_SIZE;
125 return 1;
126 }
127
128 /* ARGSUSED */
129 static void
130 mvsocgpp_attach(device_t parent, device_t self, void *aux)
131 {
132 struct mvsocgpp_softc *sc = device_private(self);
133 struct marvell_attach_args *mva = aux;
134 struct pic_softc *gpio_pic;
135 #if NGPIO > 0
136 struct gpiobus_attach_args gba;
137 gpio_pin_t *pins;
138 uint32_t dir, valin, valout, polarity, mask;
139 #endif
140 int i, j;
141 void *ih;
142
143 aprint_normal(": Marvell SoC General Purpose I/O Port Interface\n");
144 aprint_naive("\n");
145
146 sc->sc_dev = self;
147 sc->sc_iot = mva->mva_iot;
148 /* Map I/O registers for oriongpp */
149 if (bus_space_subregion(mva->mva_iot, mva->mva_ioh,
150 mva->mva_offset, mva->mva_size, &sc->sc_ioh)) {
151 aprint_error_dev(self, "can't map registers\n");
152 return;
153 }
154
155 if (gpp_npins > 0)
156 aprint_normal_dev(self, "%d gpio pins\n", gpp_npins);
157 else {
158 aprint_error_dev(self, "gpp_npins not initialized\n");
159 return;
160 }
161
162 mvsocgpp_softc = sc;
163
164 for (i = 0; i < gpp_npins; i += 32)
165 MVSOCGPP_WRITE(sc, MVSOCGPP_GPIOIC(i), 0);
166
167 sc->sc_pic =
168 kmem_zalloc(sizeof(struct mvsocgpp_pic) * gpp_npins / 8, KM_SLEEP);
169 for (i = 0, j = 0; i < gpp_npins; i += 8, j++) {
170 gpio_pic = &(sc->sc_pic + j)->gpio_pic;
171 gpio_pic->pic_ops = &gpio_pic_ops;
172 snprintf(gpio_pic->pic_name, sizeof(gpio_pic->pic_name),
173 "%s[%d:%d]", device_xname(self), i + 7, i);
174 gpio_pic->pic_maxsources =
175 (gpp_npins - i) > 8 ? 8 : gpp_npins - i;
176 pic_add(gpio_pic, gpp_irqbase + i);
177 aprint_normal_dev(self, "interrupts %d..%d",
178 gpp_irqbase + i, gpp_irqbase + i + 7);
179 ih = intr_establish(mva->mva_irq + j,
180 IPL_HIGH, IST_LEVEL_HIGH, pic_handle_intr, gpio_pic);
181 aprint_normal(", intr %d\n", mva->mva_irq + j);
182
183 (sc->sc_pic + j)->group = j;
184 }
185
186 #ifdef MVSOCGPP_DUMPREG
187 mvsocgpp_dump_reg(sc);
188 #endif
189
190 #if NGPIO > 0
191 sc->sc_pins = kmem_alloc(sizeof(gpio_pin_t) * gpp_npins, KM_SLEEP);
192
193 for (i = 0; i < gpp_npins; i += 32) {
194 dir = MVSOCGPP_READ(sc, MVSOCGPP_GPIODOEC(i));
195 valin = MVSOCGPP_READ(sc, MVSOCGPP_GPIODI(i));
196 valout = MVSOCGPP_READ(sc, MVSOCGPP_GPIODO(i));
197 polarity = MVSOCGPP_READ(sc, MVSOCGPP_GPIODIP(i));
198 }
199 for (i = 0, mask = 1; i < gpp_npins; i++, mask <<= 1) {
200 pins = &sc->sc_pins[i];
201 pins->pin_num = i;
202 pins->pin_caps =
203 (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_INVIN);
204 if(dir & mask) {
205 pins->pin_flags = GPIO_PIN_INPUT;
206 pins->pin_state =
207 (valin & mask) ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
208 } else {
209 pins->pin_flags = GPIO_PIN_OUTPUT;
210 pins->pin_state =
211 (valout & mask) ? GPIO_PIN_HIGH : GPIO_PIN_LOW;
212 }
213 }
214 sc->sc_gpio_chipset.gp_cookie = sc;
215 sc->sc_gpio_chipset.gp_pin_read = mvsocgpp_pin_read;
216 sc->sc_gpio_chipset.gp_pin_write = mvsocgpp_pin_write;
217 sc->sc_gpio_chipset.gp_pin_ctl = mvsocgpp_pin_ctl;
218 gba.gba_gc = &sc->sc_gpio_chipset;
219 gba.gba_pins = sc->sc_pins;
220 gba.gba_npins = gpp_npins;
221 config_found_ia(self, "gpiobus", &gba, gpiobus_print);
222 #endif
223 }
224
225 /*
226 * arch/arm/pic functions.
227 */
228
229 static void
230 gpio_pic_unblock_irqs(struct pic_softc *pic, size_t irqbase, uint32_t irq_mask)
231 {
232 struct mvsocgpp_softc *sc = mvsocgpp_softc;
233 struct mvsocgpp_pic *mvsocgpp_pic = (struct mvsocgpp_pic *)pic;
234 uint32_t mask;
235 int pin = mvsocgpp_pic->group << 3;
236
237 MVSOCGPP_WRITE(sc, MVSOCGPP_GPIOIC(pin),
238 MVSOCGPP_READ(sc, MVSOCGPP_GPIOIC(pin)) & ~irq_mask);
239 if (irq_mask & mvsocgpp_pic->edge) {
240 mask = MVSOCGPP_READ(sc, MVSOCGPP_GPIOIM(pin));
241 mask |= (irq_mask & mvsocgpp_pic->edge);
242 MVSOCGPP_WRITE(sc, MVSOCGPP_GPIOIM(pin), mask);
243 }
244 if (irq_mask & mvsocgpp_pic->level) {
245 mask = MVSOCGPP_READ(sc, MVSOCGPP_GPIOILM(pin));
246 mask |= (irq_mask & mvsocgpp_pic->level);
247 MVSOCGPP_WRITE(sc, MVSOCGPP_GPIOILM(pin), mask);
248 }
249 }
250
251 /* ARGSUSED */
252 static void
253 gpio_pic_block_irqs(struct pic_softc *pic, size_t irqbase, uint32_t irq_mask)
254 {
255 struct mvsocgpp_softc *sc = mvsocgpp_softc;
256 struct mvsocgpp_pic *mvsocgpp_pic = (struct mvsocgpp_pic *)pic;
257 int pin = mvsocgpp_pic->group << 3;
258
259 MVSOCGPP_WRITE(sc, MVSOCGPP_GPIOIM(pin),
260 MVSOCGPP_READ(sc, MVSOCGPP_GPIOIM(pin)) & ~irq_mask);
261 MVSOCGPP_WRITE(sc, MVSOCGPP_GPIOILM(pin),
262 MVSOCGPP_READ(sc, MVSOCGPP_GPIOILM(pin)) & ~irq_mask);
263 }
264
265 static int
266 gpio_pic_find_pending_irqs(struct pic_softc *pic)
267 {
268 struct mvsocgpp_softc *sc = mvsocgpp_softc;
269 struct mvsocgpp_pic *mvsocgpp_pic = (struct mvsocgpp_pic *)pic;
270 uint32_t pending;
271 int pin = mvsocgpp_pic->group << 3;
272
273 pending = MVSOCGPP_READ(sc, MVSOCGPP_GPIOIC(pin));
274 pending &= (0xff << mvsocgpp_pic->group);
275 pending &= (MVSOCGPP_READ(sc, MVSOCGPP_GPIOIM(pin)) |
276 MVSOCGPP_READ(sc, MVSOCGPP_GPIOILM(pin)));
277 if (pending == 0)
278 return 0;
279 pic_mark_pending_sources(pic, 0, pending);
280 return 1;
281 }
282
283 static void
284 gpio_pic_establish_irq(struct pic_softc *pic, struct intrsource *is)
285 {
286 struct mvsocgpp_softc *sc = mvsocgpp_softc;
287 struct mvsocgpp_pic *mvsocgpp_pic = (struct mvsocgpp_pic *)pic;
288 uint32_t im, ilm, mask;
289 int type, pin;
290
291 type = is->is_type;
292 pin = pic->pic_irqbase + is->is_irq - gpp_irqbase;
293 mask = MVSOCGPP_GPIOPIN(pin);
294
295 switch (type) {
296 case IST_LEVEL_LOW:
297 case IST_EDGE_FALLING:
298 mvsocgpp_pin_ctl(NULL, pin, GPIO_PIN_INPUT | GPIO_PIN_INVIN);
299 break;
300
301 case IST_LEVEL_HIGH:
302 case IST_EDGE_RISING:
303 mvsocgpp_pin_ctl(NULL, pin, GPIO_PIN_INPUT);
304 break;
305
306 default:
307 panic("unknwon interrupt type %d for pin %d.\n", type, pin);
308 }
309
310 im = MVSOCGPP_READ(sc, MVSOCGPP_GPIOIM(pin));
311 ilm = MVSOCGPP_READ(sc, MVSOCGPP_GPIOILM(pin));
312 switch (type) {
313 case IST_EDGE_FALLING:
314 case IST_EDGE_RISING:
315 im |= mask;
316 ilm &= ~mask;
317 mvsocgpp_pic->edge |= mask;
318 mvsocgpp_pic->level &= ~mask;
319 break;
320
321 case IST_LEVEL_LOW:
322 case IST_LEVEL_HIGH:
323 im &= ~mask;
324 ilm |= mask;
325 mvsocgpp_pic->edge &= ~mask;
326 mvsocgpp_pic->level |= mask;
327 break;
328 }
329 MVSOCGPP_WRITE(sc, MVSOCGPP_GPIOIM(pin), im);
330 MVSOCGPP_WRITE(sc, MVSOCGPP_GPIOILM(pin), ilm);
331 }
332
333
334 /*
335 * gpio(4) functions, and can call you.
336 */
337
338 /* ARGSUSED */
339 int
340 mvsocgpp_pin_read(void *arg, int pin)
341 {
342 struct mvsocgpp_softc *sc = mvsocgpp_softc;
343 uint32_t val;
344
345 KASSERT(sc != NULL);
346
347 val = MVSOCGPP_READ(sc, MVSOCGPP_GPIODI(pin));
348 return (val & MVSOCGPP_GPIOPIN(pin)) != 0;
349 }
350
351 /* ARGSUSED */
352 void
353 mvsocgpp_pin_write(void *arg, int pin, int value)
354 {
355 struct mvsocgpp_softc *sc = mvsocgpp_softc;
356 uint32_t old, new, mask = MVSOCGPP_GPIOPIN(pin);
357
358 KASSERT(sc != NULL);
359
360 old = MVSOCGPP_READ(sc, MVSOCGPP_GPIODO(pin));
361 if (value)
362 new = old | mask;
363 else
364 new = old & ~mask;
365 if (new != old)
366 MVSOCGPP_WRITE(sc, MVSOCGPP_GPIODO(pin), new);
367 }
368
369 /* ARGSUSED */
370 void
371 mvsocgpp_pin_ctl(void *arg, int pin, int flags)
372 {
373 struct mvsocgpp_softc *sc = mvsocgpp_softc;
374 uint32_t old, new, mask = MVSOCGPP_GPIOPIN(pin);
375
376 KASSERT(sc != NULL);
377
378 old = MVSOCGPP_READ(sc, MVSOCGPP_GPIODOEC(pin));
379 switch (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) {
380 case GPIO_PIN_INPUT:
381 new = old | mask;
382 break;
383
384 case GPIO_PIN_OUTPUT:
385 new = old & ~mask;
386 break;
387
388 default:
389 return;
390 }
391 if (new != old)
392 MVSOCGPP_WRITE(sc, MVSOCGPP_GPIODOEC(pin), new);
393
394 /* Blink every 2^24 TCLK */
395 old = MVSOCGPP_READ(sc, MVSOCGPP_GPIOBE(pin));
396 if (flags & GPIO_PIN_PULSATE)
397 new = old | mask;
398 else
399 new = old & ~mask;
400 if (new != old)
401 MVSOCGPP_WRITE(sc, MVSOCGPP_GPIOBE(pin), new);
402
403 old = MVSOCGPP_READ(sc, MVSOCGPP_GPIODIP(pin));
404 if (flags & GPIO_PIN_INVIN)
405 new = old | mask;
406 else
407 new = old & ~mask;
408 if (new != old)
409 MVSOCGPP_WRITE(sc, MVSOCGPP_GPIODIP(pin), new);
410 }
411
412
413 #ifdef MVSOCGPP_DUMPREG
414 static void
415 mvsocgpp_dump_reg(struct mvsocgpp_softc *sc)
416 {
417
418 aprint_normal_dev(sc->sc_dev, " Data Out: \t0x%08x\n",
419 MVSOCGPP_READ(sc, MVSOCGPP_GPIODO(0)));
420 aprint_normal_dev(sc->sc_dev, " Data Out Enable Control: \t0x%08x\n",
421 MVSOCGPP_READ(sc, MVSOCGPP_GPIODOEC(0)));
422 aprint_normal_dev(sc->sc_dev, " Data Blink Enable: \t0x%08x\n",
423 MVSOCGPP_READ(sc, MVSOCGPP_GPIOBE(0)));
424 aprint_normal_dev(sc->sc_dev, " Data In Polarity: \t0x%08x\n",
425 MVSOCGPP_READ(sc, MVSOCGPP_GPIODIP(0)));
426 aprint_normal_dev(sc->sc_dev, " Data In: \t0x%08x\n",
427 MVSOCGPP_READ(sc, MVSOCGPP_GPIODI(0)));
428 aprint_normal_dev(sc->sc_dev, " Interrupt Cause: \t0x%08x\n",
429 MVSOCGPP_READ(sc, MVSOCGPP_GPIOIC(0)));
430 aprint_normal_dev(sc->sc_dev, " Interrupt Mask: \t0x%08x\n",
431 MVSOCGPP_READ(sc, MVSOCGPP_GPIOIM(0)));
432 aprint_normal_dev(sc->sc_dev, " Interrupt Level Mask: \t0x%08x\n",
433 MVSOCGPP_READ(sc, MVSOCGPP_GPIOILM(0)));
434
435 if (gpp_npins <= 32)
436 return;
437
438 aprint_normal_dev(sc->sc_dev, " High Data Out: \t0x%08x\n",
439 MVSOCGPP_READ(sc, MVSOCGPP_GPIODO(32)));
440 aprint_normal_dev(sc->sc_dev, " High Data Out Enable Ctrl:\t0x%08x\n",
441 MVSOCGPP_READ(sc, MVSOCGPP_GPIODOEC(32)));
442 aprint_normal_dev(sc->sc_dev, " High Blink Enable: \t0x%08x\n",
443 MVSOCGPP_READ(sc, MVSOCGPP_GPIOBE(32)));
444 aprint_normal_dev(sc->sc_dev, " High Data In Polarity: \t0x%08x\n",
445 MVSOCGPP_READ(sc, MVSOCGPP_GPIODIP(32)));
446 aprint_normal_dev(sc->sc_dev, " High Data In: \t0x%08x\n",
447 MVSOCGPP_READ(sc, MVSOCGPP_GPIODI(32)));
448 aprint_normal_dev(sc->sc_dev, " High Interrupt Cause: \t0x%08x\n",
449 MVSOCGPP_READ(sc, MVSOCGPP_GPIOIC(32)));
450 aprint_normal_dev(sc->sc_dev, " High Interrupt Mask: \t0x%08x\n",
451 MVSOCGPP_READ(sc, MVSOCGPP_GPIOIM(32)));
452 aprint_normal_dev(sc->sc_dev, " High Interrupt Level Mask:\t0x%08x\n",
453 MVSOCGPP_READ(sc, MVSOCGPP_GPIOILM(32)));
454 }
455 #endif
456