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