Home | History | Annotate | Line # | Download | only in marvell
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