xref: /netbsd-src/sys/arch/algor/algor/algor_p4032_intr.c (revision d48f14661dda8638fee055ba15d35bdfb29b9fa8)
1 /*	$NetBSD: algor_p4032_intr.c,v 1.11 2006/04/15 16:13:24 simonb Exp $	*/
2 
3 /*-
4  * Copyright (c) 2001 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the NetBSD
21  *	Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 /*
40  * Platform-specific interrupt support for the Algorithmics P-4032.
41  *
42  * The Algorithmics P-4032 has an interrupt controller that is pretty
43  * flexible -- it can take an interrupt source and route it to an
44  * arbitrary MIPS CPU hardware interrupt pin.
45  */
46 
47 #include <sys/cdefs.h>
48 __KERNEL_RCSID(0, "$NetBSD: algor_p4032_intr.c,v 1.11 2006/04/15 16:13:24 simonb Exp $");
49 
50 #include "opt_ddb.h"
51 
52 #include <sys/param.h>
53 #include <sys/queue.h>
54 #include <sys/malloc.h>
55 #include <sys/systm.h>
56 #include <sys/device.h>
57 #include <sys/kernel.h>
58 
59 #include <machine/bus.h>
60 #include <machine/autoconf.h>
61 #include <machine/intr.h>
62 
63 #include <mips/locore.h>
64 
65 #include <dev/ic/mc146818reg.h>
66 
67 #include <algor/algor/algor_p4032reg.h>
68 #include <algor/algor/algor_p4032var.h>
69 
70 #include <algor/algor/clockvar.h>
71 
72 #include <dev/pci/pcireg.h>
73 #include <dev/pci/pcivar.h>
74 
75 #define	REGVAL(x)	*((volatile u_int32_t *)(MIPS_PHYS_TO_KSEG1((x))))
76 
77 struct p4032_irqreg {
78 	bus_addr_t	addr;
79 	u_int32_t	val;
80 };
81 
82 #define	IRQREG_8BIT		0
83 #define	IRQREG_ERROR		1
84 #define	IRQREG_PCI		2
85 #define	NIRQREG			3
86 
87 struct p4032_irqreg p4032_irqregs[NIRQREG] = {
88 	{ P4032_IRR0,		0 },
89 	{ P4032_IRR1,		0 },
90 	{ P4032_IRR2,		0 },
91 };
92 
93 #define	NSTEERREG		3
94 
95 struct p4032_irqreg p4032_irqsteer[NSTEERREG] = {
96 	{ P4032_XBAR0,		0 },
97 	{ P4032_XBAR1,		0 },
98 	{ P4032_XBAR2,		0 },
99 };
100 
101 #define	NPCIIRQS		4
102 
103 /* See algor_p4032var.h */
104 #define	N8BITIRQS		8
105 
106 #define	IRQMAP_PCIBASE		0
107 #define	IRQMAP_8BITBASE		NPCIIRQS
108 #define	NIRQMAPS		(IRQMAP_8BITBASE + N8BITIRQS)
109 
110 const char *p4032_intrnames[NIRQMAPS] = {
111 	/*
112 	 * PCI INTERRUPTS
113 	 */
114 	"PCIIRQ 0",
115 	"PCIIRQ 1",
116 	"PCIIRQ 2",
117 	"PCIIRQ 3",
118 
119 	/*
120 	 * 8-BIT DEVICE INTERRUPTS
121 	 */
122 	"PCI ctlr",
123 	"floppy",
124 	"pckbc",
125 	"com 1",
126 	"com 2",
127 	"centronics",
128 	"gpio",
129 	"mcclock",
130 };
131 
132 struct p4032_irqmap {
133 	int	irqidx;
134 	int	cpuintr;
135 	int	irqreg;
136 	int	irqbit;
137 	int	xbarreg;
138 	int	xbarshift;
139 };
140 
141 const struct p4032_irqmap p4032_irqmap[NIRQMAPS] = {
142 	/*
143 	 * PCI INTERRUPTS
144 	 */
145 	/* PCIIRQ 0 */
146 	{ 0,			0,
147 	  IRQREG_PCI,		IRR2_PCIIRQ0,
148 	  2,			0 },
149 
150 	/* PCIIRQ 1 */
151 	{ 1,			0,
152 	  IRQREG_PCI,		IRR2_PCIIRQ1,
153 	  2,			2 },
154 
155 	/* PCIIRQ 2 */
156 	{ 2,			0,
157 	  IRQREG_PCI,		IRR2_PCIIRQ2,
158 	  2,			4 },
159 
160 	/* PCIIRQ 3 */
161 	{ 3,			0,
162 	  IRQREG_PCI,		IRR2_PCIIRQ3,
163 	  2,			6 },
164 
165 	/*
166 	 * 8-BIT DEVICE INTERRUPTS
167 	 */
168 	{ P4032_IRQ_PCICTLR,	1,
169 	  IRQREG_8BIT,		IRR0_PCICTLR,
170 	  0,			0 },
171 
172 	{ P4032_IRQ_FLOPPY,	1,
173 	  IRQREG_8BIT,		IRR0_FLOPPY,
174 	  0,			2 },
175 
176 	{ P4032_IRQ_PCKBC,	1,
177 	  IRQREG_8BIT,		IRR0_PCKBC,
178 	  0,			4 },
179 
180 	{ P4032_IRQ_COM1,	1,
181 	  IRQREG_8BIT,		IRR0_COM1,
182 	  0,			6 },
183 
184 	{ P4032_IRQ_COM2,	1,
185 	  IRQREG_8BIT,		IRR0_COM2,
186 	  1,			0 },
187 
188 	{ P4032_IRQ_LPT,	1,
189 	  IRQREG_8BIT,		IRR0_LPT,
190 	  1,			2 },
191 
192 	{ P4032_IRQ_GPIO,	1,
193 	  IRQREG_8BIT,		IRR0_GPIO,
194 	  1,			4 },
195 
196 	{ P4032_IRQ_RTC,	1,
197 	  IRQREG_8BIT,		IRR0_RTC,
198 	  1,			6 },
199 };
200 
201 struct p4032_intrhead {
202 	struct evcnt intr_count;
203 	int intr_refcnt;
204 };
205 struct p4032_intrhead p4032_intrtab[NIRQMAPS];
206 
207 #define	NINTRS			2	/* MIPS INT0 - INT1 */
208 
209 /*
210  * This is a mask of bits to clear in the SR when we go to a
211  * given software interrupt priority level.
212  * Hardware ipls are port/board specific.
213  */
214 const uint32_t mips_ipl_si_to_sr[_IPL_NSOFT] = {
215 	MIPS_SOFT_INT_MASK_0,			/* IPL_SOFT */
216 	MIPS_SOFT_INT_MASK_0,			/* IPL_SOFTCLOCK */
217 	MIPS_SOFT_INT_MASK_1,			/* IPL_SOFTNET */
218 	MIPS_SOFT_INT_MASK_1,			/* IPL_SOFTSERIAL */
219 };
220 
221 struct p4032_cpuintr {
222 	LIST_HEAD(, algor_intrhand) cintr_list;
223 	struct evcnt cintr_count;
224 };
225 
226 struct p4032_cpuintr p4032_cpuintrs[NINTRS];
227 const char *p4032_cpuintrnames[NINTRS] = {
228 	"int 0 (pci)",
229 	"int 1 (8-bit)",
230 };
231 
232 const char *p4032_intrgroups[NINTRS] = {
233 	"pci",
234 	"8-bit",
235 };
236 
237 void	*algor_p4032_intr_establish(int, int (*)(void *), void *);
238 void	algor_p4032_intr_disestablish(void *);
239 
240 int	algor_p4032_pci_intr_map(struct pci_attach_args *, pci_intr_handle_t *);
241 const char *algor_p4032_pci_intr_string(void *, pci_intr_handle_t);
242 const struct evcnt *algor_p4032_pci_intr_evcnt(void *, pci_intr_handle_t);
243 void	*algor_p4032_pci_intr_establish(void *, pci_intr_handle_t, int,
244 	    int (*)(void *), void *);
245 void	algor_p4032_pci_intr_disestablish(void *, void *);
246 void	algor_p4032_pci_conf_interrupt(void *, int, int, int, int, int *);
247 
248 void	algor_p4032_iointr(u_int32_t, u_int32_t, u_int32_t, u_int32_t);
249 
250 void
251 algor_p4032_intr_init(struct p4032_config *acp)
252 {
253 	const struct p4032_irqmap *irqmap;
254 	int i;
255 
256 	for (i = 0; i < NIRQREG; i++)
257 		REGVAL(p4032_irqregs[i].addr) = p4032_irqregs[i].val;
258 
259 	for (i = 0; i < NINTRS; i++) {
260 		LIST_INIT(&p4032_cpuintrs[i].cintr_list);
261 		evcnt_attach_dynamic(&p4032_cpuintrs[i].cintr_count,
262 		    EVCNT_TYPE_INTR, NULL, "mips", p4032_cpuintrnames[i]);
263 	}
264 	evcnt_attach_static(&mips_int5_evcnt);
265 
266 	for (i = 0; i < NIRQMAPS; i++) {
267 		irqmap = &p4032_irqmap[i];
268 
269 		p4032_irqsteer[irqmap->xbarreg].val |=
270 		    irqmap->cpuintr << irqmap->xbarshift;
271 
272 		evcnt_attach_dynamic(&p4032_intrtab[i].intr_count,
273 		    EVCNT_TYPE_INTR, NULL, p4032_intrgroups[irqmap->cpuintr],
274 		    p4032_intrnames[i]);
275 	}
276 
277 	for (i = 0; i < NSTEERREG; i++)
278 		REGVAL(p4032_irqsteer[i].addr) = p4032_irqsteer[i].val;
279 
280 	acp->ac_pc.pc_intr_v = NULL;
281 	acp->ac_pc.pc_intr_map = algor_p4032_pci_intr_map;
282 	acp->ac_pc.pc_intr_string = algor_p4032_pci_intr_string;
283 	acp->ac_pc.pc_intr_evcnt = algor_p4032_pci_intr_evcnt;
284 	acp->ac_pc.pc_intr_establish = algor_p4032_pci_intr_establish;
285 	acp->ac_pc.pc_intr_disestablish = algor_p4032_pci_intr_disestablish;
286 	acp->ac_pc.pc_conf_interrupt = algor_p4032_pci_conf_interrupt;
287 	acp->ac_pc.pc_pciide_compat_intr_establish = NULL;
288 
289 	algor_intr_establish = algor_p4032_intr_establish;
290 	algor_intr_disestablish = algor_p4032_intr_disestablish;
291 	algor_iointr = algor_p4032_iointr;
292 }
293 
294 void
295 algor_p4032_cal_timer(bus_space_tag_t st, bus_space_handle_t sh)
296 {
297 	u_long ctrdiff[4], startctr, endctr, cps;
298 	u_int32_t irr;
299 	int i;
300 
301 	/* Disable interrupts first. */
302 	bus_space_write_1(st, sh, 0, MC_REGB);
303 	bus_space_write_1(st, sh, 1, MC_REGB_SQWE | MC_REGB_BINARY |
304 	    MC_REGB_24HR);
305 
306 	/* Initialize for 16Hz. */
307 	bus_space_write_1(st, sh, 0, MC_REGA);
308 	bus_space_write_1(st, sh, 1, MC_BASE_32_KHz | MC_RATE_16_Hz);
309 
310 	REGVAL(P4032_IRR0) = IRR0_RTC;
311 
312 	/* Run the loop an extra time to prime the cache. */
313 	for (cps = 0, i = 0; i < 4; i++) {
314 		led_display('h', 'z', '0' + i, ' ');
315 
316 		/* Enable the interrupt. */
317 		bus_space_write_1(st, sh, 0, MC_REGB);
318 		bus_space_write_1(st, sh, 1, MC_REGB_PIE | MC_REGB_SQWE |
319 		    MC_REGB_BINARY | MC_REGB_24HR);
320 
321 		/* Wait for it to happen. */
322 		startctr = mips3_cp0_count_read();
323 		do {
324 			irr = REGVAL(P4032_IRR0);
325 			endctr = mips3_cp0_count_read();
326 		} while ((irr & IRR0_RTC) == 0);
327 
328 		/* ACK. */
329 		bus_space_write_1(st, sh, 0, MC_REGC);
330 		(void) bus_space_read_1(st, sh, 1);
331 
332 		/* Disable. */
333 		bus_space_write_1(st, sh, 0, MC_REGB);
334 		bus_space_write_1(st, sh, 1, MC_REGB_SQWE | MC_REGB_BINARY |
335 		    MC_REGB_24HR);
336 
337 		ctrdiff[i] = endctr - startctr;
338 	}
339 
340 	REGVAL(P4032_IRR0) = 0;
341 
342 	/* Compute the number of cycles per second. */
343 	cps = ((ctrdiff[2] + ctrdiff[3]) / 2) * 16;
344 
345 	/* Compute the number of ticks for hz. */
346 	cycles_per_hz = cps / hz;
347 
348 	/* Compute the delay divisor. */
349 	delay_divisor = (cps / 1000000) / 2;
350 
351 	printf("Timer calibration: %lu cycles/sec [(%lu, %lu) * 16]\n",
352 	    cps, ctrdiff[2], ctrdiff[3]);
353 	printf("CPU clock speed = %lu.%02luMHz "
354 	    "(hz cycles = %lu, delay divisor = %u)\n",
355 	    cps / 1000000, (cps % 1000000) / 10000,
356 	    cycles_per_hz, delay_divisor);
357 }
358 
359 void *
360 algor_p4032_intr_establish(int irq, int (*func)(void *), void *arg)
361 {
362 	const struct p4032_irqmap *irqmap;
363 	struct algor_intrhand *ih;
364 	int s;
365 
366 	irqmap = &p4032_irqmap[irq];
367 
368 	KASSERT(irq == irqmap->irqidx);
369 
370 	ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT);
371 	if (ih == NULL)
372 		return (NULL);
373 
374 	ih->ih_func = func;
375 	ih->ih_arg = arg;
376 	ih->ih_irq = 0;
377 	ih->ih_irqmap = irqmap;
378 
379 	s = splhigh();
380 
381 	/*
382 	 * First, link it into the tables.
383 	 */
384 	LIST_INSERT_HEAD(&p4032_cpuintrs[irqmap->cpuintr].cintr_list,
385 	    ih, ih_q);
386 
387 	/*
388 	 * Now enable it.
389 	 */
390 	if (p4032_intrtab[irqmap->irqidx].intr_refcnt++ == 0) {
391 		p4032_irqregs[irqmap->irqreg].val |= irqmap->irqbit;
392 		REGVAL(p4032_irqregs[irqmap->irqreg].addr) =
393 		    p4032_irqregs[irqmap->irqreg].val;
394 	}
395 
396 	splx(s);
397 
398 	return (ih);
399 }
400 
401 void
402 algor_p4032_intr_disestablish(void *cookie)
403 {
404 	const struct p4032_irqmap *irqmap;
405 	struct algor_intrhand *ih = cookie;
406 	int s;
407 
408 	irqmap = ih->ih_irqmap;
409 
410 	s = splhigh();
411 
412 	/*
413 	 * First, remove it from the table.
414 	 */
415 	LIST_REMOVE(ih, ih_q);
416 
417 	/*
418 	 * Now, disable it, if there is nothing remaining on the
419 	 * list.
420 	 */
421 	if (p4032_intrtab[irqmap->irqidx].intr_refcnt-- == 1) {
422 		p4032_irqregs[irqmap->irqreg].val &= ~irqmap->irqbit;
423 		REGVAL(p4032_irqregs[irqmap->irqreg].addr) =
424 		    p4032_irqregs[irqmap->irqreg].val;
425 	}
426 
427 	splx(s);
428 
429 	free(ih, M_DEVBUF);
430 }
431 
432 void
433 algor_p4032_iointr(u_int32_t status, u_int32_t cause, u_int32_t pc,
434     u_int32_t ipending)
435 {
436 	const struct p4032_irqmap *irqmap;
437 	struct algor_intrhand *ih;
438 	int level, i;
439 	u_int32_t irr[NIRQREG];
440 
441 	/* Check for ERROR interrupts. */
442 	if (ipending & MIPS_INT_MASK_4) {
443 		irr[IRQREG_ERROR] = REGVAL(p4032_irqregs[IRQREG_ERROR].addr);
444 		if (irr[IRQREG_ERROR] & IRR1_BUSERR)
445 			printf("WARNING: Bus error\n");
446 		if (irr[IRQREG_ERROR] & IRR1_POWERFAIL)
447 			printf("WARNING: Power failure\n");
448 		if (irr[IRQREG_ERROR] & IRR1_DEBUG) {
449 #ifdef DDB
450 			printf("Debug switch -- entering debugger\n");
451 			led_display('D','D','B',' ');
452 			Debugger();
453 			led_display('N','B','S','D');
454 #else
455 			printf("Debug switch ignored -- "
456 			    "no debugger configured\n");
457 #endif
458 		}
459 
460 		/* Clear them. */
461 		REGVAL(p4032_irqregs[IRQREG_ERROR].addr) = irr[IRQREG_ERROR];
462 	}
463 
464 	/* Do floppy DMA request interrupts. */
465 	if (ipending & MIPS_INT_MASK_3) {
466 		/*
467 		 * XXX Hi, um, yah, we need to deal with
468 		 * XXX the floppy interrupt here.
469 		 */
470 
471 		cause &= ~MIPS_INT_MASK_3;
472 		_splset(MIPS_SR_INT_IE |
473 		    ((status & ~cause) & MIPS_HARD_INT_MASK));
474 	}
475 
476 	/*
477 	 * Read the interrupt pending registers, mask them with the
478 	 * ones we have enabled, and service them in order of decreasing
479 	 * priority.
480 	 */
481 	for (i = 0; i < NIRQREG; i++) {
482 		if (i == IRQREG_ERROR)
483 			continue;
484 		irr[i] = REGVAL(p4032_irqregs[i].addr) & p4032_irqregs[i].val;
485 	}
486 
487 	for (level = (NINTRS - 1); level >= 0; level--) {
488 		if ((ipending & (MIPS_INT_MASK_0 << level)) == 0)
489 			continue;
490 		p4032_cpuintrs[level].cintr_count.ev_count++;
491 		for (ih = LIST_FIRST(&p4032_cpuintrs[level].cintr_list);
492 		     ih != NULL; ih = LIST_NEXT(ih, ih_q)) {
493 			irqmap = ih->ih_irqmap;
494 			if (irr[irqmap->irqreg] & irqmap->irqbit) {
495 				p4032_intrtab[
496 				    irqmap->irqidx].intr_count.ev_count++;
497 				(*ih->ih_func)(ih->ih_arg);
498 			}
499 		}
500 		cause &= ~(MIPS_INT_MASK_0 << level);
501 	}
502 
503 	/* Re-enable anything that we have processed. */
504 	_splset(MIPS_SR_INT_IE | ((status & ~cause) & MIPS_HARD_INT_MASK));
505 }
506 
507 /*****************************************************************************
508  * PCI interrupt support
509  *****************************************************************************/
510 
511 int
512 algor_p4032_pci_intr_map(struct pci_attach_args *pa,
513     pci_intr_handle_t *ihp)
514 {
515 	static const int pciirqmap[6/*device*/][4/*pin*/] = {
516 		{ 1, -1, -1, -1 },		/* 5: Ethernet */
517 		{ 2, 3, 0, 1 },			/* 6: PCI slot 1 */
518 		{ 3, 0, 1, 2 },			/* 7: PCI slot 2 */
519 		{ 0, -1, -1, -1 },		/* 8: SCSI */
520 		{ -1, -1, -1, -1 },		/* 9: not used */
521 		{ 0, 1, 2, 3 },			/* 10: custom connector */
522 	};
523 	pcitag_t bustag = pa->pa_intrtag;
524 	int buspin = pa->pa_intrpin;
525 	pci_chipset_tag_t pc = pa->pa_pc;
526 	int device, irq;
527 
528 	if (buspin == 0) {
529 		/* No IRQ used. */
530 		return (1);
531 	}
532 
533 	if (buspin > 4) {
534 		printf("algor_p4032_pci_intr_map: bad interrupt pin %d\n",
535 		    buspin);
536 		return (1);
537 	}
538 
539 	pci_decompose_tag(pc, bustag, NULL, &device, NULL);
540 	if (device < 5 || device > 10) {
541 		printf("algor_p4032_pci_intr_map: bad device %d\n",
542 		    device);
543 		return (1);
544 	}
545 
546 	irq = pciirqmap[device - 5][buspin - 1];
547 	if (irq == -1) {
548 		printf("algor_p4032_pci_intr_map: no mapping for "
549 		    "device %d pin %d\n", device, buspin);
550 		return (1);
551 	}
552 
553 	*ihp = irq;
554 	return (0);
555 }
556 
557 const char *
558 algor_p4032_pci_intr_string(void *v, pci_intr_handle_t ih)
559 {
560 
561 	if (ih >= NPCIIRQS)
562 		panic("algor_p4032_intr_string: bogus IRQ %ld", ih);
563 
564 	return (p4032_intrnames[ih]);
565 }
566 
567 const struct evcnt *
568 algor_p4032_pci_intr_evcnt(void *v, pci_intr_handle_t ih)
569 {
570 
571 	return (&p4032_intrtab[ih].intr_count);
572 }
573 
574 void *
575 algor_p4032_pci_intr_establish(void *v, pci_intr_handle_t ih, int level,
576     int (*func)(void *), void *arg)
577 {
578 
579 	if (ih >= NPCIIRQS)
580 		panic("algor_p4032_intr_establish: bogus IRQ %ld", ih);
581 
582 	return (algor_p4032_intr_establish(ih, func, arg));
583 }
584 
585 void
586 algor_p4032_pci_intr_disestablish(void *v, void *cookie)
587 {
588 
589 	return (algor_p4032_intr_disestablish(cookie));
590 }
591 
592 void
593 algor_p4032_pci_conf_interrupt(void *v, int bus, int dev, int pin, int swiz,
594     int *iline)
595 {
596 
597 	/*
598 	 * We actually don't need to do anything; everything is handled
599 	 * in pci_intr_map().
600 	 */
601 	*iline = 0;
602 }
603