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