Home | History | Annotate | Line # | Download | only in marvell
pic_discovery.c revision 1.2
      1 /*	$NetBSD: pic_discovery.c,v 1.2 2007/10/17 19:56:42 garbled Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2002 Allegro Networks, Inc., Wasabi Systems, Inc.
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  * 3. All advertising materials mentioning features or use of this software
     16  *    must display the following acknowledgement:
     17  *      This product includes software developed for the NetBSD Project by
     18  *      Allegro Networks, Inc., and Wasabi Systems, Inc.
     19  * 4. The name of Allegro Networks, Inc. may not be used to endorse
     20  *    or promote products derived from this software without specific prior
     21  *    written permission.
     22  * 5. The name of Wasabi Systems, Inc. may not be used to endorse
     23  *    or promote products derived from this software without specific prior
     24  *    written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY ALLEGRO NETWORKS, INC. AND
     27  * WASABI SYSTEMS, INC. ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
     28  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
     29  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     30  * IN NO EVENT SHALL EITHER ALLEGRO NETWORKS, INC. OR WASABI SYSTEMS, INC.
     31  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     32  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     33  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     34  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     35  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     36  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     37  * POSSIBILITY OF SUCH DAMAGE.
     38  */
     39 
     40 /*
     41  * extintr.c - external interrupt management for discovery
     42  *
     43  *	Interrupts are software-prioritized and preempting,
     44  *	they are only actually masked when pending.
     45  *	this allows avoiding slow, off-CPU mask reprogramming for spl/splx.
     46  *	When a lower priority interrupt preempts a high one,
     47  *	it is pended and masked.  Masks are re-enabled after service.
     48  *
     49  *	`ci->ci_cpl'   is a "priority level" not a bitmask.
     50  *	`irq'   is a bit number in the 128 bit imask_t which reflects
     51  *		the GT-64260 Main Cause register pair (64 bits), and
     52  *		GPP Cause register (32 bits) interrupts.
     53  *
     54  *	Intra IPL dispatch order is defined in cause_irq()
     55  *
     56  *	Summary bits in cause registers are not valid IRQs
     57  *
     58  * 	Within a cause register bit vector ISRs are called in
     59  *	order of IRQ (descending).
     60  *
     61  *	When IRQs are shared, ISRs are called in order on the queue
     62  *	(which is arbitrarily first-established-first-served).
     63  *
     64  *	GT64260 GPP setup is for edge-triggered interrupts.
     65  *	We maintain a mask of level-triggered type IRQs
     66  *	and gather unlatched level from the GPP Value register.
     67  *
     68  *	Software interrupts are just like regular IRQs,
     69  *	they are established, pended, and dispatched exactly the
     70  *	same as HW interrupts.
     71  *
     72  *	128 bit imask_t operations could be implemented with Altivec
     73  *	("vand", "vor", etc however no "vcntlzw" or "vffs"....)
     74  *
     75  * creation	Tue Feb  6 17:27:18 PST 2001	cliff
     76  */
     77 
     78 #include <sys/cdefs.h>
     79 __KERNEL_RCSID(0, "$NetBSD: pic_discovery.c,v 1.2 2007/10/17 19:56:42 garbled Exp $");
     80 
     81 #include "opt_marvell.h"
     82 #include "opt_kgdb.h"
     83 
     84 #include <sys/param.h>
     85 #include <sys/systm.h>
     86 #include <sys/types.h>
     87 #include <sys/malloc.h>
     88 #include <sys/kernel.h>
     89 
     90 #include <machine/psl.h>
     91 #include <machine/bus.h>
     92 #include <machine/cpu.h>
     93 #include <machine/intr.h>
     94 #include <powerpc/pic/picvar.h>
     95 #ifdef KGDB
     96 #include <machine/db_machdep.h>
     97 #endif
     98 #include <dev/marvell/gtreg.h>
     99 #include <dev/marvell/gtvar.h>
    100 #include <dev/marvell/gtintrreg.h>
    101 
    102 #include <uvm/uvm_extern.h>
    103 
    104 #if defined(DEBUG) && defined(DDB)
    105 #endif
    106 
    107 #ifdef DEBUG
    108 # define STATIC
    109 int intrdebug = 0;
    110 # define DPRINTF(x)		do { if (intrdebug) printf x ; } while (0)
    111 # define DPRINTFN(n, x)		do { if (intrdebug > (n)) printf x ; } while (0)
    112 #else
    113 # define STATIC static
    114 # define DPRINTF(x)
    115 # define DPRINTFN(n, x)
    116 #endif
    117 
    118 #ifdef DIAGNOSTIC
    119 # define DIAGPRF(x)		printf x
    120 #else
    121 # define DIAGPRF(x)
    122 #endif
    123 
    124 #define ILLEGAL_IRQ(x) (((x) < 0) || ((x) >= NIRQ) || \
    125 		 ((1<<((x)&IMASK_BITMASK)) & imres.bits[(x)>>IMASK_WORDSHIFT]))
    126 
    127 extern struct powerpc_bus_space gt_mem_bs_tag;
    128 extern bus_space_handle_t gt_memh;
    129 
    130 static const char intr_source_strings[NIRQ][16] = {
    131 	"unknown 0",	"dev",		"dma",		"cpu",
    132 	"idma 01",	"idma 23",	"idma 45",	"idma 67",
    133 	"timer 01",	"timer 23",	"timer 45",	"timer 67",
    134 	"pci0-0",	"pci0-1",	"pci0-2",	"pci0-3",
    135 /*16*/	"pci1-0",	"ecc",		"pci1-1",	"pci1-2",
    136 	"pci1-3",	"pci0-outl",	"pci0-outh",	"pci1-outl",
    137 	"pci1-outh",	"unknown 25",	"pci0-inl",	"pci0-inh",
    138 	"pci1-inl",	"pci1-inh",	"unknown 30",	"unknown 31",
    139 /*32*/	"ether 0",	"ether 1",	"ether 2",	"unknown 35",
    140 	"sdma",		"iic",		"unknown 38",	"brg",
    141 	"mpsc 0",	"unknown 41",	"mpsc 1",	"comm",
    142 	"unknown 44",	"unknown 45",	"unknown 46",	"unknown 47",
    143 /*48*/	"unknown 48",	"unknown 49",	"unknown 50",	"unknown 51",
    144 	"unknown 52",	"unknown 53",	"unknown 54",	"unknown 55",
    145 	"gppsum 0",	"gppsum 1",	"gppsum 2",	"gppsum 3",
    146 	"unknown 60",	"unknown 61",	"unknown 62",	"unknown 63",
    147 };
    148 
    149 struct discovery_pic_ops {
    150 	struct pic_ops dsc_pic;
    151 	bus_space_tag_t dsc_memt;
    152 	bus_space_tag_t dsc_memh;
    153 	uint32_t dsc_interrupt_mask[2];
    154 	uint8_t dsc_priority[64];
    155 	uint8_t dsc_maxpriority[64];
    156 };
    157 
    158 struct gpp_pic_ops {
    159 	struct pic_ops gpp_pic;
    160 	bus_space_tag_t gpp_memt;
    161 	bus_space_handle_t gpp_memh;
    162 	uint32_t gpp_interrupt_mask;
    163 	uint8_t gpp_priority[32];
    164 	uint8_t gpp_maxpriority[32];
    165 };
    166 
    167 static void
    168 gpp_source_name(struct pic_ops *pic, int irq, char *name, size_t len)
    169 {
    170 	snprintf(name, len, "gpp %d", irq);
    171 }
    172 
    173 #define GPP_RES ~GT_MPP_INTERRUPTS	/* from config */
    174 
    175 static int
    176 gpp_get_irq(struct pic_ops *pic)
    177 {
    178 	struct gpppic_ops * const gpp = (void *)pic;
    179 	uint32_t mask;
    180 	int maybe_irq = -1;
    181 	int maybe_priority = IPL_NONE;
    182 
    183 #ifdef GPP_EDGE
    184 	mask = bus_space_read_4(&gpp->gpp_memt, gpp->gpp_memh, GT_GPP_Interrupt_Cause);
    185 #else
    186 	mask = bus_space_read_4(&gpp->gpp_memt, gpp->gpp_memh, GT_GPP_Value);
    187 #endif
    188 	mask &= gpp->gpp_interrupt_mask;
    189 	if (mask == 0)
    190 		return NO_IRQ;
    191 
    192 	while (mask != 0) {
    193 		int irq = 32 - __builtin_clz(mask);
    194 		if (gpp->gpp_priority[irq] > maybe_irq) {
    195 			maybe_irq = irq;
    196 			maybe_priority = gpp->gpp_priority[irq];
    197 			if (maybe_priority > gpp->gpp_maxpriority[irq])
    198 				break;
    199 		}
    200 		mask &= ~(1 << irq);
    201 	}
    202 	/*
    203 	 * We now have the highest priority IRQ.
    204 	 */
    205 	KASSERT(maybe_irq >= 0);
    206 #ifdef GPP_EDGE
    207 	mask = 1 << maybe_irq;
    208 	bus_space_write_4(&gpp->gpp_memt, gpp->gpp_memh, GT_GPP_Interrupt_Cause, mask);
    209 #endif
    210 
    211 	return maybe_irq;
    212 }
    213 
    214 static void
    215 gpp_establish_irq(struct pic_ops *pic, int irq, int type, int pri)
    216 {
    217 	struct gpppic_ops * const gpp = (void *)pic;
    218 	const uint32_t mask = 1 << irq;
    219 
    220 	KASSERT((unsigned) irq < 32);
    221 #ifdef GPP_EDGE
    222 	KASSERT(type == IST_EDGE);
    223 #else
    224 	KASSERT(type == IST_LEVEL);
    225 #endif
    226 
    227 	/*
    228 	 * Set pin to input and active low.
    229 	 */
    230 	val = bus_space_read_4(gpp->gpp_memt, gpp->gpp_memh, GT_GPP_IO_Control);
    231 	val &= ~mask;
    232 	bus_space_write_4(gpp->gpp_memt, gpp->gpp_memh, GT_GPP_IO_Control, val);
    233 
    234 	val = bus_space_read_4(gpp->gpp_memt, gpp->gpp_memh, GT_GPP_Level_Control);
    235 	val |= mask;
    236 	bus_space_write_4(&gpp->gpp_memt, gpp->gpp_memh, GT_GPP_Level_Control, val);
    237 
    238 	gpp->gpp_priority[irq] = pri;
    239 
    240 	/*
    241 	 * recalculate the maxpriority of an interrupt.  This is highest
    242 	 * priority interrupt from itself to gpp0.
    243 	 */
    244 	pri = IPL_NONE;
    245 	for (i = 0; i < __arraycount(gpp->gpp_priority); i++) {
    246 		if (gpp->gpp_priority[i] > pri)
    247 			pri = gpp->gpp_priority[i];
    248 		gpp->gpp_maxpriority[i] = pri;
    249 	}
    250 }
    251 
    252 static void
    253 gpp_enable_irq(struct pic_ops *pic, int irq, int type)
    254 {
    255 	struct gpppic_ops * const gpp = (void *)pic;
    256 	const uint32_t mask = 1 << irq;
    257 
    258 	KASSERT(type == IST_LEVEL);
    259 	KASSERT(gpp->gpp_priority[irq] != IPL_NONE);
    260 	gpp->gpp_interrupt_mask |= mask;
    261 	bus_space_write_4(&gpp->gpp_memt, gpp->gpp_memh, GT_GPP_Interrupt_Mask,
    262 	    gpp->gpp_interrupt_mask);
    263 }
    264 
    265 static void
    266 gpp_disable_irq(struct pic_ops *pic, int irq)
    267 {
    268 	struct gpppic_ops * const gpp = (void *)pic;
    269 	const uint32_t mask = 1 << irq;
    270 
    271 	gpp->gpp_interrupt_mask &= ~mask;
    272 	bus_space_write_4(&gpp->gpp_memt, gpp->gpp_memh, GT_GPP_Interrupt_Mask,
    273 	    gpp->gpp_interrupt_mask);
    274 }
    275 
    276 static void
    277 gpp_ack_irq(struct pic_ops *pic, int irq)
    278 {
    279 }
    280 
    281 static struct pic_ops *
    282 gpp_pic_setup(bus_space_tag_t memt, bus_space_handle_t memh)
    283 {
    284 	struct gpppic_ops * gpp;
    285 	uint32_t val;
    286 
    287 	gpp = malloc(sizeof(*gpp), M_DEVBUF, M_NOWAIT|M_ZERO);
    288 	if (!gpp)
    289 		panic("gpp_pic_setup: malloc(%zu) failed", sizeof(*gpp));
    290 
    291 	gpp->gpp_memt = memt;
    292 	gpp->gpp_memh = memh;
    293 	gpp->gpp_pic.pic_get_irq = gpp_get_irq;
    294 	gpp->gpp_pic.pic_enable_irq = gpp_enable_irq;
    295 	gpp->gpp_pic.pic_reenable_irq = gpp_enable_irq;
    296 	gpp->gpp_pic.pic_disable_irq = gpp_disable_irq;
    297 	gpp->gpp_pic.pic_ack_irq = gpp_ack_irq;
    298 	gpp->gpp_pic.pic_establish_irq = gpp_establish_irq;
    299 	gpp->gpp_pic.pic_source_name = gpp_source_name;
    300 
    301 	/*
    302 	 * Force GPP interrupts to be level sensitive.
    303 	 */
    304 	val = bus_space_read_4(&gpp->gpp_memt, gpp->gpp_memh, 0xf300);
    305 	bus_space_write_4(&gpp->gpp_memt, gpp->gpp_memh, 0xf300, val | 0x400);
    306 
    307 	pic_add(&gpp->gpp_pic);
    308 
    309 	return &gpp->gpp_pic;
    310 }
    311 
    312 static void
    313 discovery_source_name(struct pic_ops *pic, int irq, char *name, size_t len)
    314 {
    315 	strlcpy(name, discovery_intr_source_strings[irq], len);
    316 }
    317 
    318 static int
    319 discovery_get_irq(struct pic_ops *pic)
    320 {
    321 	struct discoverypic_ops * const dsc = (void *)pic;
    322 	uint32_t mask;
    323 	int irq_base = 0;
    324 	int maybe_irq = -1;
    325 	int maybe_priority = IPL_NONE;
    326 
    327 	mask = bus_space_read_4(&dsc->dsc_memt, dsc->dsc_memh, ICR_CSC);
    328 	if (!(mask & CSC_STAT))
    329 		return NO_IRQ;
    330 	irq_base = (mask & CSC_SEL) ? 32 : 0;
    331 	mask &= dsc->dsc_interrupt_mask[(mask & CSC_SEL) ? 1 : 0];
    332 	while (mask != 0) {
    333 		int irq = 32 - __builtin_clz(mask);
    334 		if (dsc->dsc_priority[irq_base + irq] > maybe_irq) {
    335 			maybe_irq = irq_base + irq;
    336 			maybe_priority = dsc->dsc_priority[irq_base + irq];
    337 			if (maybe_priority > dsc->dsc_maxpriority[irq_base + irq])
    338 				break;
    339 		}
    340 		mask &= ~(1 << irq);
    341 	}
    342 	/*
    343 	 * We now have the highest priority IRQ (except it's cascaded).
    344 	 */
    345 	KASSERT(maybe_irq >= 0);
    346 	return maybe_irq;
    347 }
    348 
    349 static void
    350 discovery_establish_irq(struct pic_ops *pic, int irq, int type, int pri)
    351 {
    352 	struct discoverypic_ops * const dsc = (void *)pic;
    353 
    354 	KASSERT((unsigned) irq < 32);
    355 #ifdef GPP_EDGE
    356 	KASSERT(type == IST_EDGE);
    357 #else
    358 	KASSERT(type == IST_LEVEL);
    359 #endif
    360 
    361 	dsc->dsc_priority[irq] = pri;
    362 
    363 	/*
    364 	 * recalculate the maxpriority of an interrupt.  This is highest
    365 	 * priority interrupt from itself to irq 0.
    366 	 */
    367 	pri = IPL_NONE;
    368 	for (i = 0; i < __arraycount(dsc->dsc_priority); i++) {
    369 		if (dsc->dsc_priority[i] > pri)
    370 			pri = dsc->dsc_priority[i];
    371 		dsc->dsc_maxpriority[i] = pri;
    372 	}
    373 }
    374 
    375 static void
    376 discovery_enable_irq(struct pic_ops *pic, int irq, int type)
    377 {
    378 	struct discoverypic_ops * const dsc = (void *)pic;
    379 	const uint32_t mask = 1 << (irq & 31);
    380 
    381 	KASSERT(type == IST_LEVEL);
    382 	KASSERT(dsc->dsc_priority[irq] != IPL_NONE);
    383 	if (irq < 32) {
    384 		dsc->dsc_interrupt_mask[0] |= mask;
    385 		bus_space_write_4(&dsc->dsc_memt, dsc->dsc_memh,
    386 		    ICR_MIC_LO, dsc->dsc_interrupt_mask[0]);
    387 	} else {
    388 		dsc->dsc_interrupt_mask[1] |= mask;
    389 		bus_space_write_4(&dsc->dsc_memt, dsc->dsc_memh,
    390 		    ICR_MIC_HI, dsc->dsc_interrupt_mask[1]);
    391 	}
    392 }
    393 
    394 static void
    395 discovery_disable_irq(struct pic_ops *pic, int irq)
    396 {
    397 	struct discoverypic_ops * const dsc = (void *)pic;
    398 	const uint32_t mask = 1 << (irq & 31);
    399 
    400 	if (irq < 32) {
    401 		dsc->dsc_interrupt_mask[0] &= ~mask;
    402 		bus_space_write_4(&dsc->dsc_memt, dsc->dsc_memh,
    403 		    ICR_MIC_LO, dsc->dsc_interrupt_mask[0]);
    404 	} else {
    405 		dsc->dsc_interrupt_mask[1] &= ~mask;
    406 		bus_space_write_4(&dsc->dsc_memt, dsc->dsc_memh,
    407 		    ICR_MIC_HI, dsc->dsc_interrupt_mask[1]);
    408 	}
    409 }
    410 
    411 static void
    412 discovery_ack_irq(struct pic_ops *pic, int irq)
    413 {
    414 }
    415 
    416 void
    417 discoverypic_setup(bus_space_tag_t memt, bus_space_handle_t memh)
    418 {
    419 	struct discoverypic_ops *dsc;
    420 	uint32_t val;
    421 
    422 	dsc = malloc(sizeof(*dsc), M_DEVBUF, M_NOWAIT|M_ZERO);
    423 	if (!dsc)
    424 		panic("dsc_pic_setup: malloc(%zu) failed", sizeof(*dsc));
    425 
    426 	dsc->dsc_memt = memt;
    427 	dsc->dsc_memh = memh;
    428 	dsc->dsc_pic.pic_get_irq = dsc_get_irq;
    429 	dsc->dsc_pic.pic_enable_irq = dsc_enable_irq;
    430 	dsc->dsc_pic.pic_reenable_irq = dsc_enable_irq;
    431 	dsc->dsc_pic.pic_disable_irq = dsc_disable_irq;
    432 	dsc->dsc_pic.pic_ack_irq = dsc_ack_irq;
    433 	dsc->dsc_pic.pic_establish_irq = dsc_establish_irq;
    434 	dsc->dsc_pic.pic_source_name = dsc_source_name;
    435 
    436 	pic_add(&dsc->dsc_pic);
    437 	KASSERT(dsc->dsc_pic.pic_intrbase == 0);
    438 
    439 	pic = dscpic_setup(memt, memh);
    440 	intr_establish(dsc->dsc_pic.pic_intrbase + IRQ_GPP7_0,
    441 	    IST_LEVEL, IPL_NONE, pic_handle_intr, pic);
    442 	intr_establish(dsc->dsc_pic.pic_intrbase + IRQ_GPP15_8,
    443 	    IST_LEVEL, IPL_NONE, pic_handle_intr, pic);
    444 	intr_establish(dsc->dsc_pic.pic_intrbase + IRQ_GPP23_16,
    445 	    IST_LEVEL, IPL_NONE, pic_handle_intr, pic);
    446 	intr_establish(dsc->dsc_pic.pic_intrbase + IRQ_GPP31_24,
    447 	    IST_LEVEL, IPL_NONE, pic_handle_intr, pic);
    448 }
    449