xref: /netbsd-src/sys/arch/algor/algor/algor_p4032_intr.c (revision 267197ec1eebfcb9810ea27a89625b6ddf68e3e7)
1 /*	$NetBSD: algor_p4032_intr.c,v 1.18 2008/02/03 05:31:17 tsutsui 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.18 2008/02/03 05:31:17 tsutsui 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 #include <sys/cpu.h>
59 
60 #include <machine/bus.h>
61 #include <machine/autoconf.h>
62 #include <machine/intr.h>
63 
64 #include <mips/locore.h>
65 
66 #include <dev/ic/mc146818reg.h>
67 
68 #include <algor/algor/algor_p4032reg.h>
69 #include <algor/algor/algor_p4032var.h>
70 
71 #include <dev/pci/pcireg.h>
72 #include <dev/pci/pcivar.h>
73 
74 #define	REGVAL(x)	*((volatile u_int32_t *)(MIPS_PHYS_TO_KSEG1((x))))
75 
76 struct p4032_irqreg {
77 	bus_addr_t	addr;
78 	u_int32_t	val;
79 };
80 
81 #define	IRQREG_8BIT		0
82 #define	IRQREG_ERROR		1
83 #define	IRQREG_PCI		2
84 #define	NIRQREG			3
85 
86 struct p4032_irqreg p4032_irqregs[NIRQREG] = {
87 	{ P4032_IRR0,		0 },
88 	{ P4032_IRR1,		0 },
89 	{ P4032_IRR2,		0 },
90 };
91 
92 #define	NSTEERREG		3
93 
94 struct p4032_irqreg p4032_irqsteer[NSTEERREG] = {
95 	{ P4032_XBAR0,		0 },
96 	{ P4032_XBAR1,		0 },
97 	{ P4032_XBAR2,		0 },
98 };
99 
100 #define	NPCIIRQS		4
101 
102 /* See algor_p4032var.h */
103 #define	N8BITIRQS		8
104 
105 #define	IRQMAP_PCIBASE		0
106 #define	IRQMAP_8BITBASE		NPCIIRQS
107 #define	NIRQMAPS		(IRQMAP_8BITBASE + N8BITIRQS)
108 
109 const char *p4032_intrnames[NIRQMAPS] = {
110 	/*
111 	 * PCI INTERRUPTS
112 	 */
113 	"PCIIRQ 0",
114 	"PCIIRQ 1",
115 	"PCIIRQ 2",
116 	"PCIIRQ 3",
117 
118 	/*
119 	 * 8-BIT DEVICE INTERRUPTS
120 	 */
121 	"PCI ctlr",
122 	"floppy",
123 	"pckbc",
124 	"com 1",
125 	"com 2",
126 	"centronics",
127 	"gpio",
128 	"mcclock",
129 };
130 
131 struct p4032_irqmap {
132 	int	irqidx;
133 	int	cpuintr;
134 	int	irqreg;
135 	int	irqbit;
136 	int	xbarreg;
137 	int	xbarshift;
138 };
139 
140 const struct p4032_irqmap p4032_irqmap[NIRQMAPS] = {
141 	/*
142 	 * PCI INTERRUPTS
143 	 */
144 	/* PCIIRQ 0 */
145 	{ 0,			0,
146 	  IRQREG_PCI,		IRR2_PCIIRQ0,
147 	  2,			0 },
148 
149 	/* PCIIRQ 1 */
150 	{ 1,			0,
151 	  IRQREG_PCI,		IRR2_PCIIRQ1,
152 	  2,			2 },
153 
154 	/* PCIIRQ 2 */
155 	{ 2,			0,
156 	  IRQREG_PCI,		IRR2_PCIIRQ2,
157 	  2,			4 },
158 
159 	/* PCIIRQ 3 */
160 	{ 3,			0,
161 	  IRQREG_PCI,		IRR2_PCIIRQ3,
162 	  2,			6 },
163 
164 	/*
165 	 * 8-BIT DEVICE INTERRUPTS
166 	 */
167 	{ P4032_IRQ_PCICTLR,	1,
168 	  IRQREG_8BIT,		IRR0_PCICTLR,
169 	  0,			0 },
170 
171 	{ P4032_IRQ_FLOPPY,	1,
172 	  IRQREG_8BIT,		IRR0_FLOPPY,
173 	  0,			2 },
174 
175 	{ P4032_IRQ_PCKBC,	1,
176 	  IRQREG_8BIT,		IRR0_PCKBC,
177 	  0,			4 },
178 
179 	{ P4032_IRQ_COM1,	1,
180 	  IRQREG_8BIT,		IRR0_COM1,
181 	  0,			6 },
182 
183 	{ P4032_IRQ_COM2,	1,
184 	  IRQREG_8BIT,		IRR0_COM2,
185 	  1,			0 },
186 
187 	{ P4032_IRQ_LPT,	1,
188 	  IRQREG_8BIT,		IRR0_LPT,
189 	  1,			2 },
190 
191 	{ P4032_IRQ_GPIO,	1,
192 	  IRQREG_8BIT,		IRR0_GPIO,
193 	  1,			4 },
194 
195 	{ P4032_IRQ_RTC,	1,
196 	  IRQREG_8BIT,		IRR0_RTC,
197 	  1,			6 },
198 };
199 
200 struct p4032_intrhead {
201 	struct evcnt intr_count;
202 	int intr_refcnt;
203 };
204 struct p4032_intrhead p4032_intrtab[NIRQMAPS];
205 
206 #define	NINTRS			2	/* MIPS INT0 - INT1 */
207 
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 (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 	/* Update CPU frequency values */
331 	cps = ((ctrdiff[2] + ctrdiff[3]) / 2) * 16;
332 	/* XXX mips_cpu_flags isn't set here; assume CPU_MIPS_DOUBLE_COUNT */
333 	curcpu()->ci_cpu_freq = cps * 2;
334 	curcpu()->ci_cycles_per_hz = (curcpu()->ci_cpu_freq + hz / 2) / hz;
335 	curcpu()->ci_divisor_delay =
336 	    ((curcpu()->ci_cpu_freq + (1000000 / 2)) / 1000000);
337 	/* XXX assume CPU_MIPS_DOUBLE_COUNT */
338 	curcpu()->ci_cycles_per_hz /= 2;
339 	curcpu()->ci_divisor_delay /= 2;
340 	MIPS_SET_CI_RECIPROCAL(curcpu());
341 
342 	printf("Timer calibration: %lu cycles/sec [(%lu, %lu) * 16]\n",
343 	    cps, ctrdiff[2], ctrdiff[3]);
344 	printf("CPU clock speed = %lu.%02luMHz "
345 	    "(hz cycles = %lu, delay divisor = %lu)\n",
346 	    curcpu()->ci_cpu_freq / 1000000,
347 	    (curcpu()->ci_cpu_freq % 1000000) / 10000,
348 	    curcpu()->ci_cycles_per_hz, curcpu()->ci_divisor_delay);
349 }
350 
351 void *
352 algor_p4032_intr_establish(int irq, int (*func)(void *), void *arg)
353 {
354 	const struct p4032_irqmap *irqmap;
355 	struct algor_intrhand *ih;
356 	int s;
357 
358 	irqmap = &p4032_irqmap[irq];
359 
360 	KASSERT(irq == irqmap->irqidx);
361 
362 	ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT);
363 	if (ih == NULL)
364 		return (NULL);
365 
366 	ih->ih_func = func;
367 	ih->ih_arg = arg;
368 	ih->ih_irq = 0;
369 	ih->ih_irqmap = irqmap;
370 
371 	s = splhigh();
372 
373 	/*
374 	 * First, link it into the tables.
375 	 */
376 	LIST_INSERT_HEAD(&p4032_cpuintrs[irqmap->cpuintr].cintr_list,
377 	    ih, ih_q);
378 
379 	/*
380 	 * Now enable it.
381 	 */
382 	if (p4032_intrtab[irqmap->irqidx].intr_refcnt++ == 0) {
383 		p4032_irqregs[irqmap->irqreg].val |= irqmap->irqbit;
384 		REGVAL(p4032_irqregs[irqmap->irqreg].addr) =
385 		    p4032_irqregs[irqmap->irqreg].val;
386 	}
387 
388 	splx(s);
389 
390 	return (ih);
391 }
392 
393 void
394 algor_p4032_intr_disestablish(void *cookie)
395 {
396 	const struct p4032_irqmap *irqmap;
397 	struct algor_intrhand *ih = cookie;
398 	int s;
399 
400 	irqmap = ih->ih_irqmap;
401 
402 	s = splhigh();
403 
404 	/*
405 	 * First, remove it from the table.
406 	 */
407 	LIST_REMOVE(ih, ih_q);
408 
409 	/*
410 	 * Now, disable it, if there is nothing remaining on the
411 	 * list.
412 	 */
413 	if (p4032_intrtab[irqmap->irqidx].intr_refcnt-- == 1) {
414 		p4032_irqregs[irqmap->irqreg].val &= ~irqmap->irqbit;
415 		REGVAL(p4032_irqregs[irqmap->irqreg].addr) =
416 		    p4032_irqregs[irqmap->irqreg].val;
417 	}
418 
419 	splx(s);
420 
421 	free(ih, M_DEVBUF);
422 }
423 
424 void
425 algor_p4032_iointr(u_int32_t status, u_int32_t cause, u_int32_t pc,
426     u_int32_t ipending)
427 {
428 	const struct p4032_irqmap *irqmap;
429 	struct algor_intrhand *ih;
430 	int level, i;
431 	u_int32_t irr[NIRQREG];
432 
433 	/* Check for ERROR interrupts. */
434 	if (ipending & MIPS_INT_MASK_4) {
435 		irr[IRQREG_ERROR] = REGVAL(p4032_irqregs[IRQREG_ERROR].addr);
436 		if (irr[IRQREG_ERROR] & IRR1_BUSERR)
437 			printf("WARNING: Bus error\n");
438 		if (irr[IRQREG_ERROR] & IRR1_POWERFAIL)
439 			printf("WARNING: Power failure\n");
440 		if (irr[IRQREG_ERROR] & IRR1_DEBUG) {
441 #ifdef DDB
442 			printf("Debug switch -- entering debugger\n");
443 			led_display('D','D','B',' ');
444 			Debugger();
445 			led_display('N','B','S','D');
446 #else
447 			printf("Debug switch ignored -- "
448 			    "no debugger configured\n");
449 #endif
450 		}
451 
452 		/* Clear them. */
453 		REGVAL(p4032_irqregs[IRQREG_ERROR].addr) = irr[IRQREG_ERROR];
454 	}
455 
456 	/* Do floppy DMA request interrupts. */
457 	if (ipending & MIPS_INT_MASK_3) {
458 		/*
459 		 * XXX Hi, um, yah, we need to deal with
460 		 * XXX the floppy interrupt here.
461 		 */
462 
463 		cause &= ~MIPS_INT_MASK_3;
464 		_splset(MIPS_SR_INT_IE |
465 		    ((status & ~cause) & MIPS_HARD_INT_MASK));
466 	}
467 
468 	/*
469 	 * Read the interrupt pending registers, mask them with the
470 	 * ones we have enabled, and service them in order of decreasing
471 	 * priority.
472 	 */
473 	for (i = 0; i < NIRQREG; i++) {
474 		if (i == IRQREG_ERROR)
475 			continue;
476 		irr[i] = REGVAL(p4032_irqregs[i].addr) & p4032_irqregs[i].val;
477 	}
478 
479 	for (level = (NINTRS - 1); level >= 0; level--) {
480 		if ((ipending & (MIPS_INT_MASK_0 << level)) == 0)
481 			continue;
482 		p4032_cpuintrs[level].cintr_count.ev_count++;
483 		for (ih = LIST_FIRST(&p4032_cpuintrs[level].cintr_list);
484 		     ih != NULL; ih = LIST_NEXT(ih, ih_q)) {
485 			irqmap = ih->ih_irqmap;
486 			if (irr[irqmap->irqreg] & irqmap->irqbit) {
487 				p4032_intrtab[
488 				    irqmap->irqidx].intr_count.ev_count++;
489 				(*ih->ih_func)(ih->ih_arg);
490 			}
491 		}
492 		cause &= ~(MIPS_INT_MASK_0 << level);
493 	}
494 
495 	/* Re-enable anything that we have processed. */
496 	_splset(MIPS_SR_INT_IE | ((status & ~cause) & MIPS_HARD_INT_MASK));
497 }
498 
499 /*****************************************************************************
500  * PCI interrupt support
501  *****************************************************************************/
502 
503 int
504 algor_p4032_pci_intr_map(struct pci_attach_args *pa,
505     pci_intr_handle_t *ihp)
506 {
507 	static const int pciirqmap[6/*device*/][4/*pin*/] = {
508 		{ 1, -1, -1, -1 },		/* 5: Ethernet */
509 		{ 2, 3, 0, 1 },			/* 6: PCI slot 1 */
510 		{ 3, 0, 1, 2 },			/* 7: PCI slot 2 */
511 		{ 0, -1, -1, -1 },		/* 8: SCSI */
512 		{ -1, -1, -1, -1 },		/* 9: not used */
513 		{ 0, 1, 2, 3 },			/* 10: custom connector */
514 	};
515 	pcitag_t bustag = pa->pa_intrtag;
516 	int buspin = pa->pa_intrpin;
517 	pci_chipset_tag_t pc = pa->pa_pc;
518 	int device, irq;
519 
520 	if (buspin == 0) {
521 		/* No IRQ used. */
522 		return (1);
523 	}
524 
525 	if (buspin > 4) {
526 		printf("algor_p4032_pci_intr_map: bad interrupt pin %d\n",
527 		    buspin);
528 		return (1);
529 	}
530 
531 	pci_decompose_tag(pc, bustag, NULL, &device, NULL);
532 	if (device < 5 || device > 10) {
533 		printf("algor_p4032_pci_intr_map: bad device %d\n",
534 		    device);
535 		return (1);
536 	}
537 
538 	irq = pciirqmap[device - 5][buspin - 1];
539 	if (irq == -1) {
540 		printf("algor_p4032_pci_intr_map: no mapping for "
541 		    "device %d pin %d\n", device, buspin);
542 		return (1);
543 	}
544 
545 	*ihp = irq;
546 	return (0);
547 }
548 
549 const char *
550 algor_p4032_pci_intr_string(void *v, pci_intr_handle_t ih)
551 {
552 
553 	if (ih >= NPCIIRQS)
554 		panic("algor_p4032_intr_string: bogus IRQ %ld", ih);
555 
556 	return (p4032_intrnames[ih]);
557 }
558 
559 const struct evcnt *
560 algor_p4032_pci_intr_evcnt(void *v, pci_intr_handle_t ih)
561 {
562 
563 	return (&p4032_intrtab[ih].intr_count);
564 }
565 
566 void *
567 algor_p4032_pci_intr_establish(void *v, pci_intr_handle_t ih, int level,
568     int (*func)(void *), void *arg)
569 {
570 
571 	if (ih >= NPCIIRQS)
572 		panic("algor_p4032_intr_establish: bogus IRQ %ld", ih);
573 
574 	return (algor_p4032_intr_establish(ih, func, arg));
575 }
576 
577 void
578 algor_p4032_pci_intr_disestablish(void *v, void *cookie)
579 {
580 
581 	return (algor_p4032_intr_disestablish(cookie));
582 }
583 
584 void
585 algor_p4032_pci_conf_interrupt(void *v, int bus, int dev, int pin, int swiz,
586     int *iline)
587 {
588 
589 	/*
590 	 * We actually don't need to do anything; everything is handled
591 	 * in pci_intr_map().
592 	 */
593 	*iline = 0;
594 }
595