xref: /netbsd-src/sys/arch/algor/algor/algor_p4032_intr.c (revision 23c8222edbfb0f0932d88a8351d3a0cf817dfb9e)
1 /*	$NetBSD: algor_p4032_intr.c,v 1.8 2003/07/14 22:57:46 lukem 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.8 2003/07/14 22:57:46 lukem 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 struct p4032_cpuintr {
210 	LIST_HEAD(, algor_intrhand) cintr_list;
211 	struct evcnt cintr_count;
212 };
213 
214 struct p4032_cpuintr p4032_cpuintrs[NINTRS];
215 const char *p4032_cpuintrnames[NINTRS] = {
216 	"int 0 (pci)",
217 	"int 1 (8-bit)",
218 };
219 
220 const char *p4032_intrgroups[NINTRS] = {
221 	"pci",
222 	"8-bit",
223 };
224 
225 void	*algor_p4032_intr_establish(int, int (*)(void *), void *);
226 void	algor_p4032_intr_disestablish(void *);
227 
228 int	algor_p4032_pci_intr_map(struct pci_attach_args *, pci_intr_handle_t *);
229 const char *algor_p4032_pci_intr_string(void *, pci_intr_handle_t);
230 const struct evcnt *algor_p4032_pci_intr_evcnt(void *, pci_intr_handle_t);
231 void	*algor_p4032_pci_intr_establish(void *, pci_intr_handle_t, int,
232 	    int (*)(void *), void *);
233 void	algor_p4032_pci_intr_disestablish(void *, void *);
234 void	algor_p4032_pci_conf_interrupt(void *, int, int, int, int, int *);
235 
236 void	algor_p4032_iointr(u_int32_t, u_int32_t, u_int32_t, u_int32_t);
237 
238 void
239 algor_p4032_intr_init(struct p4032_config *acp)
240 {
241 	const struct p4032_irqmap *irqmap;
242 	int i;
243 
244 	for (i = 0; i < NIRQREG; i++)
245 		REGVAL(p4032_irqregs[i].addr) = p4032_irqregs[i].val;
246 
247 	for (i = 0; i < NINTRS; i++) {
248 		LIST_INIT(&p4032_cpuintrs[i].cintr_list);
249 		evcnt_attach_dynamic(&p4032_cpuintrs[i].cintr_count,
250 		    EVCNT_TYPE_INTR, NULL, "mips", p4032_cpuintrnames[i]);
251 	}
252 	evcnt_attach_static(&mips_int5_evcnt);
253 
254 	for (i = 0; i < NIRQMAPS; i++) {
255 		irqmap = &p4032_irqmap[i];
256 
257 		p4032_irqsteer[irqmap->xbarreg].val |=
258 		    irqmap->cpuintr << irqmap->xbarshift;
259 
260 		evcnt_attach_dynamic(&p4032_intrtab[i].intr_count,
261 		    EVCNT_TYPE_INTR, NULL, p4032_intrgroups[irqmap->cpuintr],
262 		    p4032_intrnames[i]);
263 	}
264 
265 	for (i = 0; i < NSTEERREG; i++)
266 		REGVAL(p4032_irqsteer[i].addr) = p4032_irqsteer[i].val;
267 
268 	acp->ac_pc.pc_intr_v = NULL;
269 	acp->ac_pc.pc_intr_map = algor_p4032_pci_intr_map;
270 	acp->ac_pc.pc_intr_string = algor_p4032_pci_intr_string;
271 	acp->ac_pc.pc_intr_evcnt = algor_p4032_pci_intr_evcnt;
272 	acp->ac_pc.pc_intr_establish = algor_p4032_pci_intr_establish;
273 	acp->ac_pc.pc_intr_disestablish = algor_p4032_pci_intr_disestablish;
274 	acp->ac_pc.pc_conf_interrupt = algor_p4032_pci_conf_interrupt;
275 	acp->ac_pc.pc_pciide_compat_intr_establish = NULL;
276 
277 	algor_intr_establish = algor_p4032_intr_establish;
278 	algor_intr_disestablish = algor_p4032_intr_disestablish;
279 	algor_iointr = algor_p4032_iointr;
280 }
281 
282 void
283 algor_p4032_cal_timer(bus_space_tag_t st, bus_space_handle_t sh)
284 {
285 	u_long ctrdiff[4], startctr, endctr, cps;
286 	u_int32_t irr;
287 	int i;
288 
289 	/* Disable interrupts first. */
290 	bus_space_write_1(st, sh, 0, MC_REGB);
291 	bus_space_write_1(st, sh, 1, MC_REGB_SQWE | MC_REGB_BINARY |
292 	    MC_REGB_24HR);
293 
294 	/* Initialize for 16Hz. */
295 	bus_space_write_1(st, sh, 0, MC_REGA);
296 	bus_space_write_1(st, sh, 1, MC_BASE_32_KHz | MC_RATE_16_Hz);
297 
298 	REGVAL(P4032_IRR0) = IRR0_RTC;
299 
300 	/* Run the loop an extra time to prime the cache. */
301 	for (cps = 0, i = 0; i < 4; i++) {
302 		led_display('h', 'z', '0' + i, ' ');
303 
304 		/* Enable the interrupt. */
305 		bus_space_write_1(st, sh, 0, MC_REGB);
306 		bus_space_write_1(st, sh, 1, MC_REGB_PIE | MC_REGB_SQWE |
307 		    MC_REGB_BINARY | MC_REGB_24HR);
308 
309 		/* Wait for it to happen. */
310 		startctr = mips3_cp0_count_read();
311 		do {
312 			irr = REGVAL(P4032_IRR0);
313 			endctr = mips3_cp0_count_read();
314 		} while ((irr & IRR0_RTC) == 0);
315 
316 		/* ACK. */
317 		bus_space_write_1(st, sh, 0, MC_REGC);
318 		(void) bus_space_read_1(st, sh, 1);
319 
320 		/* Disable. */
321 		bus_space_write_1(st, sh, 0, MC_REGB);
322 		bus_space_write_1(st, sh, 1, MC_REGB_SQWE | MC_REGB_BINARY |
323 		    MC_REGB_24HR);
324 
325 		ctrdiff[i] = endctr - startctr;
326 	}
327 
328 	REGVAL(P4032_IRR0) = 0;
329 
330 	/* Compute the number of cycles per second. */
331 	cps = ((ctrdiff[2] + ctrdiff[3]) / 2) * 16;
332 
333 	/* Compute the number of ticks for hz. */
334 	cycles_per_hz = cps / hz;
335 
336 	/* Compute the delay divisor. */
337 	delay_divisor = (cps / 1000000) / 2;
338 
339 	printf("Timer calibration: %lu cycles/sec [(%lu, %lu) * 16]\n",
340 	    cps, ctrdiff[2], ctrdiff[3]);
341 	printf("CPU clock speed = %lu.%02luMHz "
342 	    "(hz cycles = %lu, delay divisor = %u)\n",
343 	    cps / 1000000, (cps % 1000000) / 10000,
344 	    cycles_per_hz, delay_divisor);
345 }
346 
347 void *
348 algor_p4032_intr_establish(int irq, int (*func)(void *), void *arg)
349 {
350 	const struct p4032_irqmap *irqmap;
351 	struct algor_intrhand *ih;
352 	int s;
353 
354 	irqmap = &p4032_irqmap[irq];
355 
356 	KASSERT(irq == irqmap->irqidx);
357 
358 	ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT);
359 	if (ih == NULL)
360 		return (NULL);
361 
362 	ih->ih_func = func;
363 	ih->ih_arg = arg;
364 	ih->ih_irq = 0;
365 	ih->ih_irqmap = irqmap;
366 
367 	s = splhigh();
368 
369 	/*
370 	 * First, link it into the tables.
371 	 */
372 	LIST_INSERT_HEAD(&p4032_cpuintrs[irqmap->cpuintr].cintr_list,
373 	    ih, ih_q);
374 
375 	/*
376 	 * Now enable it.
377 	 */
378 	if (p4032_intrtab[irqmap->irqidx].intr_refcnt++ == 0) {
379 		p4032_irqregs[irqmap->irqreg].val |= irqmap->irqbit;
380 		REGVAL(p4032_irqregs[irqmap->irqreg].addr) =
381 		    p4032_irqregs[irqmap->irqreg].val;
382 	}
383 
384 	splx(s);
385 
386 	return (ih);
387 }
388 
389 void
390 algor_p4032_intr_disestablish(void *cookie)
391 {
392 	const struct p4032_irqmap *irqmap;
393 	struct algor_intrhand *ih = cookie;
394 	int s;
395 
396 	irqmap = ih->ih_irqmap;
397 
398 	s = splhigh();
399 
400 	/*
401 	 * First, remove it from the table.
402 	 */
403 	LIST_REMOVE(ih, ih_q);
404 
405 	/*
406 	 * Now, disable it, if there is nothing remaining on the
407 	 * list.
408 	 */
409 	if (p4032_intrtab[irqmap->irqidx].intr_refcnt-- == 1) {
410 		p4032_irqregs[irqmap->irqreg].val &= ~irqmap->irqbit;
411 		REGVAL(p4032_irqregs[irqmap->irqreg].addr) =
412 		    p4032_irqregs[irqmap->irqreg].val;
413 	}
414 
415 	splx(s);
416 
417 	free(ih, M_DEVBUF);
418 }
419 
420 void
421 algor_p4032_iointr(u_int32_t status, u_int32_t cause, u_int32_t pc,
422     u_int32_t ipending)
423 {
424 	const struct p4032_irqmap *irqmap;
425 	struct algor_intrhand *ih;
426 	int level, i;
427 	u_int32_t irr[NIRQREG];
428 
429 	/* Check for ERROR interrupts. */
430 	if (ipending & MIPS_INT_MASK_4) {
431 		irr[IRQREG_ERROR] = REGVAL(p4032_irqregs[IRQREG_ERROR].addr);
432 		if (irr[IRQREG_ERROR] & IRR1_BUSERR)
433 			printf("WARNING: Bus error\n");
434 		if (irr[IRQREG_ERROR] & IRR1_POWERFAIL)
435 			printf("WARNING: Power failure\n");
436 		if (irr[IRQREG_ERROR] & IRR1_DEBUG) {
437 #ifdef DDB
438 			printf("Debug switch -- entering debugger\n");
439 			led_display('D','D','B',' ');
440 			Debugger();
441 			led_display('N','B','S','D');
442 #else
443 			printf("Debug switch ignored -- "
444 			    "no debugger configured\n");
445 #endif
446 		}
447 
448 		/* Clear them. */
449 		REGVAL(p4032_irqregs[IRQREG_ERROR].addr) = irr[IRQREG_ERROR];
450 	}
451 
452 	/* Do floppy DMA request interrupts. */
453 	if (ipending & MIPS_INT_MASK_3) {
454 		/*
455 		 * XXX Hi, um, yah, we need to deal with
456 		 * XXX the floppy interrupt here.
457 		 */
458 
459 		cause &= ~MIPS_INT_MASK_3;
460 		_splset(MIPS_SR_INT_IE |
461 		    ((status & ~cause) & MIPS_HARD_INT_MASK));
462 	}
463 
464 	/*
465 	 * Read the interrupt pending registers, mask them with the
466 	 * ones we have enabled, and service them in order of decreasing
467 	 * priority.
468 	 */
469 	for (i = 0; i < NIRQREG; i++) {
470 		if (i == IRQREG_ERROR)
471 			continue;
472 		irr[i] = REGVAL(p4032_irqregs[i].addr) & p4032_irqregs[i].val;
473 	}
474 
475 	for (level = (NINTRS - 1); level >= 0; level--) {
476 		if ((ipending & (MIPS_INT_MASK_0 << level)) == 0)
477 			continue;
478 		p4032_cpuintrs[level].cintr_count.ev_count++;
479 		for (ih = LIST_FIRST(&p4032_cpuintrs[level].cintr_list);
480 		     ih != NULL; ih = LIST_NEXT(ih, ih_q)) {
481 			irqmap = ih->ih_irqmap;
482 			if (irr[irqmap->irqreg] & irqmap->irqbit) {
483 				p4032_intrtab[
484 				    irqmap->irqidx].intr_count.ev_count++;
485 				(*ih->ih_func)(ih->ih_arg);
486 			}
487 		}
488 		cause &= ~(MIPS_INT_MASK_0 << level);
489 	}
490 
491 	/* Re-enable anything that we have processed. */
492 	_splset(MIPS_SR_INT_IE | ((status & ~cause) & MIPS_HARD_INT_MASK));
493 }
494 
495 /*****************************************************************************
496  * PCI interrupt support
497  *****************************************************************************/
498 
499 int
500 algor_p4032_pci_intr_map(struct pci_attach_args *pa,
501     pci_intr_handle_t *ihp)
502 {
503 	static const int pciirqmap[6/*device*/][4/*pin*/] = {
504 		{ 1, -1, -1, -1 },		/* 5: Ethernet */
505 		{ 2, 3, 0, 1 },			/* 6: PCI slot 1 */
506 		{ 3, 0, 1, 2 },			/* 7: PCI slot 2 */
507 		{ 0, -1, -1, -1 },		/* 8: SCSI */
508 		{ -1, -1, -1, -1 },		/* 9: not used */
509 		{ 0, 1, 2, 3 },			/* 10: custom connector */
510 	};
511 	pcitag_t bustag = pa->pa_intrtag;
512 	int buspin = pa->pa_intrpin;
513 	pci_chipset_tag_t pc = pa->pa_pc;
514 	int device, irq;
515 
516 	if (buspin == 0) {
517 		/* No IRQ used. */
518 		return (1);
519 	}
520 
521 	if (buspin > 4) {
522 		printf("algor_p4032_pci_intr_map: bad interrupt pin %d\n",
523 		    buspin);
524 		return (1);
525 	}
526 
527 	pci_decompose_tag(pc, bustag, NULL, &device, NULL);
528 	if (device < 5 || device > 10) {
529 		printf("algor_p4032_pci_intr_map: bad device %d\n",
530 		    device);
531 		return (1);
532 	}
533 
534 	irq = pciirqmap[device - 5][buspin - 1];
535 	if (irq == -1) {
536 		printf("algor_p4032_pci_intr_map: no mapping for "
537 		    "device %d pin %d\n", device, buspin);
538 		return (1);
539 	}
540 
541 	*ihp = irq;
542 	return (0);
543 }
544 
545 const char *
546 algor_p4032_pci_intr_string(void *v, pci_intr_handle_t ih)
547 {
548 
549 	if (ih >= NPCIIRQS)
550 		panic("algor_p4032_intr_string: bogus IRQ %ld", ih);
551 
552 	return (p4032_intrnames[ih]);
553 }
554 
555 const struct evcnt *
556 algor_p4032_pci_intr_evcnt(void *v, pci_intr_handle_t ih)
557 {
558 
559 	return (&p4032_intrtab[ih].intr_count);
560 }
561 
562 void *
563 algor_p4032_pci_intr_establish(void *v, pci_intr_handle_t ih, int level,
564     int (*func)(void *), void *arg)
565 {
566 
567 	if (ih >= NPCIIRQS)
568 		panic("algor_p4032_intr_establish: bogus IRQ %ld", ih);
569 
570 	return (algor_p4032_intr_establish(ih, func, arg));
571 }
572 
573 void
574 algor_p4032_pci_intr_disestablish(void *v, void *cookie)
575 {
576 
577 	return (algor_p4032_intr_disestablish(cookie));
578 }
579 
580 void
581 algor_p4032_pci_conf_interrupt(void *v, int bus, int dev, int pin, int swiz,
582     int *iline)
583 {
584 
585 	/*
586 	 * We actually don't need to do anything; everything is handled
587 	 * in pci_intr_map().
588 	 */
589 	*iline = 0;
590 }
591