xref: /netbsd-src/sys/arch/algor/pci/pcib.c (revision a5175f1e986253dff5bbe596a6ad8c22a091e4b6)
1 /*	$NetBSD: pcib.c,v 1.29 2024/02/08 20:11:55 andvar Exp $	*/
2 
3 /*-
4  * Copyright (c) 2000, 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 #include <sys/cdefs.h>			/* RCS ID & Copyright macro defns */
33 
34 __KERNEL_RCSID(0, "$NetBSD: pcib.c,v 1.29 2024/02/08 20:11:55 andvar Exp $");
35 
36 #include "opt_algor_p5064.h"
37 #include "opt_algor_p6032.h"
38 
39 #include <sys/param.h>
40 #include <sys/bus.h>
41 #include <sys/device.h>
42 #include <sys/intr.h>
43 #include <sys/kernel.h>
44 #include <sys/kmem.h>
45 #include <sys/systm.h>
46 
47 #include <algor/autoconf.h>
48 
49 #include <dev/isa/isareg.h>
50 #include <dev/isa/isavar.h>
51 
52 #include <dev/pci/pcireg.h>
53 #include <dev/pci/pcivar.h>
54 #include <dev/pci/pcidevs.h>
55 
56 #include <dev/ic/i8259reg.h>
57 
58 #ifdef ALGOR_P5064
59 #include <algor/algor/algor_p5064var.h>
60 #endif
61 
62 #ifdef ALGOR_P6032
63 #include <algor/algor/algor_p6032var.h>
64 #endif
65 
66 const char * const pcib_intrnames[16] = {
67 	"irq 0",
68 	"irq 1",
69 	"irq 2",
70 	"irq 3",
71 	"irq 4",
72 	"irq 5",
73 	"irq 6",
74 	"irq 7",
75 	"irq 8",
76 	"irq 9",
77 	"irq 10",
78 	"irq 11",
79 	"irq 12",
80 	"irq 13",
81 	"irq 14",
82 	"irq 15",
83 };
84 
85 struct pcib_intrhead {
86 	LIST_HEAD(, evbmips_intrhand) intr_q;
87 	struct evcnt intr_count;
88 	int intr_type;
89 };
90 
91 struct pcib_softc {
92 	device_t	sc_dev;
93 
94 	bus_space_tag_t	sc_iot;
95 	bus_space_handle_t sc_ioh_icu1;
96 	bus_space_handle_t sc_ioh_icu2;
97 	bus_space_handle_t sc_ioh_elcr;
98 
99 	struct mips_isa_chipset sc_ic;
100 
101 	struct pcib_intrhead sc_intrtab[16];
102 
103 	u_int16_t	sc_imask;
104 	u_int16_t	sc_elcr;
105 
106 #if defined(ALGOR_P5064)
107 	isa_chipset_tag_t sc_parent_ic;
108 #endif
109 
110 	u_int16_t	sc_reserved;
111 
112 	void		*sc_ih;
113 };
114 
115 int	pcib_match(device_t, cfdata_t, void *);
116 void	pcib_attach(device_t, device_t, void *);
117 
118 CFATTACH_DECL_NEW(pcib, sizeof(struct pcib_softc),
119     pcib_match, pcib_attach, NULL, NULL);
120 
121 void	pcib_isa_attach_hook(device_t, device_t, struct isabus_attach_args *);
122 void	pcib_isa_detach_hook(isa_chipset_tag_t, device_t);
123 
124 int	pcib_intr(void *);
125 
126 void	pcib_bridge_callback(device_t);
127 
128 const struct evcnt *pcib_isa_intr_evcnt(void *, int);
129 void	*pcib_isa_intr_establish(void *, int, int, int,
130 	    int (*)(void *), void *);
131 void	pcib_isa_intr_disestablish(void *, void *);
132 int	pcib_isa_intr_alloc(void *, int, int, int *);
133 
134 void	pcib_set_icus(struct pcib_softc *);
135 
136 int
pcib_match(device_t parent,cfdata_t match,void * aux)137 pcib_match(device_t parent, cfdata_t match, void *aux)
138 {
139 	struct pci_attach_args *pa = aux;
140 
141 	if (PCI_CLASS(pa->pa_class) == PCI_CLASS_BRIDGE &&
142 	    PCI_SUBCLASS(pa->pa_class) == PCI_SUBCLASS_BRIDGE_ISA)
143 		return (1);
144 
145 	return (0);
146 }
147 
148 void
pcib_attach(device_t parent,device_t self,void * aux)149 pcib_attach(device_t parent, device_t self, void *aux)
150 {
151 	struct pcib_softc *sc = device_private(self);
152 	struct pci_attach_args *pa = aux;
153 	char devinfo[256];
154 	int i;
155 
156 	pci_devinfo(pa->pa_id, pa->pa_class, 0, devinfo, sizeof(devinfo));
157 	aprint_normal(": %s (rev. 0x%02x)\n", devinfo,
158 	    PCI_REVISION(pa->pa_class));
159 
160 	sc->sc_dev = self;
161 	sc->sc_iot = pa->pa_iot;
162 
163 	/*
164 	 * Map the PIC/ELCR registers.
165 	 */
166 	if (bus_space_map(sc->sc_iot, 0x4d0, 2, 0, &sc->sc_ioh_elcr) != 0)
167 		aprint_error_dev(self, "unable to map ELCR registers\n");
168 	if (bus_space_map(sc->sc_iot, IO_ICU1, 2, 0, &sc->sc_ioh_icu1) != 0)
169 		aprint_error_dev(self, "unable to map ICU1 registers\n");
170 	if (bus_space_map(sc->sc_iot, IO_ICU2, 2, 0, &sc->sc_ioh_icu2) != 0)
171 		aprint_error_dev(self, "unable to map ICU2 registers\n");
172 
173 	/* All interrupts default to "masked off". */
174 	sc->sc_imask = 0xffff;
175 
176 	/* All interrupts default to edge-triggered. */
177 	sc->sc_elcr = 0;
178 
179 	/*
180 	 * Initialize the 8259s.
181 	 */
182 
183 	/* reset, program device, 4 bytes */
184 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu1, PIC_ICW1,
185 	    ICW1_SELECT | ICW1_IC4);
186 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu1, PIC_ICW2,
187 	    ICW2_VECTOR(0)/*XXX*/);
188 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu1, PIC_ICW3,
189 	    ICW3_CASCADE(2));
190 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu1, PIC_ICW4,
191 	    ICW4_8086);
192 
193 	/* mask all interrupts */
194 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu1, PIC_OCW1,
195 	    sc->sc_imask & 0xff);
196 
197 	/* enable special mask mode */
198 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu1, PIC_OCW3,
199 	    OCW3_SELECT | OCW3_SSMM | OCW3_SMM);
200 
201 	/* read IRR by default */
202 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu1, PIC_OCW3,
203 	    OCW3_SELECT | OCW3_RR);
204 
205 	/* reset; program device, 4 bytes */
206 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu2, PIC_ICW1,
207 	    ICW1_SELECT | ICW1_IC4);
208 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu2, PIC_ICW2,
209 	    ICW2_VECTOR(0)/*XXX*/);
210 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu2, PIC_ICW3,
211 	    ICW3_SIC(2));
212 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu2, PIC_ICW4,
213 	    ICW4_8086);
214 
215 	/* mask all interrupts */
216 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu2, PIC_OCW1,
217 	    (sc->sc_imask >> 8) & 0xff);
218 
219 	/* enable special mask mode */
220 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu2, PIC_OCW3,
221 	    OCW3_SELECT | OCW3_SSMM | OCW3_SMM);
222 
223 	/* read IRR by default */
224 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu2, PIC_OCW3,
225 	    OCW3_SELECT | OCW3_RR);
226 
227 	/*
228 	 * Default all interrupts to edge-triggered.
229 	 */
230 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_elcr, 0,
231 	    sc->sc_elcr & 0xff);
232 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_elcr, 1,
233 	    (sc->sc_elcr >> 8) & 0xff);
234 
235 	/*
236 	 * Some ISA interrupts are reserved for devices that
237 	 * we know are hard-wired to certain IRQs.
238 	 */
239 	sc->sc_reserved =
240 		(1U << 0) |	/* timer */
241 		(1U << 1) |	/* keyboard controller */
242 		(1U << 2) |	/* PIC cascade */
243 		(1U << 3) |	/* COM 2 */
244 		(1U << 4) |	/* COM 1 */
245 		(1U << 6) |	/* floppy */
246 		(1U << 7) |	/* centronics */
247 		(1U << 8) |	/* RTC */
248 		(1U << 12) |	/* keyboard controller */
249 		(1U << 14) |	/* IDE 0 */
250 		(1U << 15);	/* IDE 1 */
251 
252 #if defined(ALGOR_P5064)
253 	/*
254 	 * Some "ISA" interrupts are a little wacky, wired up directly
255 	 * to the P-5064 interrupt controller.
256 	 */
257 	sc->sc_parent_ic = &p5064_configuration.ac_ic;
258 #endif /* ALGOR_P5064 */
259 
260 	/* Set up our ISA chipset. */
261 	sc->sc_ic.ic_v = sc;
262 	sc->sc_ic.ic_intr_evcnt = pcib_isa_intr_evcnt;
263 	sc->sc_ic.ic_intr_establish = pcib_isa_intr_establish;
264 	sc->sc_ic.ic_intr_disestablish = pcib_isa_intr_disestablish;
265 	sc->sc_ic.ic_intr_alloc = pcib_isa_intr_alloc;
266 
267 	/* Initialize our interrupt table. */
268 	for (i = 0; i < 16; i++) {
269 		LIST_INIT(&sc->sc_intrtab[i].intr_q);
270 		evcnt_attach_dynamic(&sc->sc_intrtab[i].intr_count,
271 		    EVCNT_TYPE_INTR, NULL, "pcib", pcib_intrnames[i]);
272 		sc->sc_intrtab[i].intr_type = IST_NONE;
273 	}
274 
275 	/* Hook up our interrupt handler. */
276 #if defined(ALGOR_P5064)
277 	sc->sc_ih = (*algor_intr_establish)(P5064_IRQ_ISABRIDGE,
278 	    pcib_intr, sc);
279 #elif defined(ALGOR_P6032)
280 	sc->sc_ih = (*algor_intr_establish)(P6032_IRQ_ISABRIDGE,
281 	    pcib_intr, sc);
282 #endif
283 	if (sc->sc_ih == NULL)
284 		printf("%s: WARNING: unable to register interrupt handler\n",
285 		    device_xname(sc->sc_dev));
286 
287 	config_defer(self, pcib_bridge_callback);
288 }
289 
290 void
pcib_bridge_callback(device_t self)291 pcib_bridge_callback(device_t self)
292 {
293 	struct pcib_softc *sc = device_private(self);
294 	struct isabus_attach_args iba;
295 
296 	memset(&iba, 0, sizeof(iba));
297 
298 #if defined(ALGOR_P5064)
299 	    {
300 		struct p5064_config *acp = &p5064_configuration;
301 
302 		iba.iba_iot = &acp->ac_iot;
303 		iba.iba_memt = &acp->ac_memt;
304 		iba.iba_dmat = &acp->ac_isa_dmat;
305 	    }
306 #elif defined(ALGOR_P6032)
307 	    {
308 		struct p6032_config *acp = &p6032_configuration;
309 
310 		iba.iba_iot = &acp->ac_iot;
311 		iba.iba_memt = &acp->ac_memt;
312 		iba.iba_dmat = &acp->ac_isa_dmat;
313 	    }
314 #endif
315 
316 	iba.iba_ic = &sc->sc_ic;
317 	iba.iba_ic->ic_attach_hook = pcib_isa_attach_hook;
318 	iba.iba_ic->ic_detach_hook = pcib_isa_detach_hook;
319 
320 	config_found(sc->sc_dev, &iba, isabusprint, CFARGS_NONE);
321 }
322 
323 void
pcib_isa_attach_hook(device_t parent,device_t self,struct isabus_attach_args * iba)324 pcib_isa_attach_hook(device_t parent, device_t self,
325     struct isabus_attach_args *iba)
326 {
327 
328 	/* Nothing to do. */
329 }
330 
331 void
pcib_isa_detach_hook(isa_chipset_tag_t ic,device_t self)332 pcib_isa_detach_hook(isa_chipset_tag_t ic, device_t self)
333 {
334 
335 	/* Nothing to do. */
336 }
337 
338 void
pcib_set_icus(struct pcib_softc * sc)339 pcib_set_icus(struct pcib_softc *sc)
340 {
341 
342 	/* Enable the cascade IRQ (2) if 8-15 is enabled. */
343 	if ((sc->sc_imask & 0xff00) != 0xff00)
344 		sc->sc_imask &= ~(1U << 2);
345 	else
346 		sc->sc_imask |= (1U << 2);
347 
348 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu1, PIC_OCW1,
349 	    sc->sc_imask & 0xff);
350 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu2, PIC_OCW1,
351 	    (sc->sc_imask >> 8) & 0xff);
352 
353 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_elcr, 0,
354 	    sc->sc_elcr & 0xff);
355 	bus_space_write_1(sc->sc_iot, sc->sc_ioh_elcr, 1,
356 	    (sc->sc_elcr >> 8) & 0xff);
357 }
358 
359 int
pcib_intr(void * v)360 pcib_intr(void *v)
361 {
362 	struct pcib_softc *sc = v;
363 	struct evbmips_intrhand *ih;
364 	int irq;
365 
366 	for (;;) {
367 		bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu1, PIC_OCW3,
368 		    OCW3_SELECT | OCW3_POLL);
369 		irq = bus_space_read_1(sc->sc_iot, sc->sc_ioh_icu1, PIC_OCW3);
370 		if ((irq & OCW3_POLL_PENDING) == 0)
371 			return (1);
372 
373 		irq = OCW3_POLL_IRQ(irq);
374 
375 		if (irq == 2) {
376 			bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu2,
377 			    PIC_OCW3, OCW3_SELECT | OCW3_POLL);
378 			irq = bus_space_read_1(sc->sc_iot, sc->sc_ioh_icu2,
379 			    PIC_OCW3);
380 			if (irq & OCW3_POLL_PENDING)
381 				irq = OCW3_POLL_IRQ(irq) + 8;
382 			else
383 				irq = 2;
384 		}
385 
386 		sc->sc_intrtab[irq].intr_count.ev_count++;
387 		for (ih = LIST_FIRST(&sc->sc_intrtab[irq].intr_q);
388 		     ih != NULL; ih = LIST_NEXT(ih, ih_q)) {
389 			(*ih->ih_func)(ih->ih_arg);
390 		}
391 
392 		/* Send a specific EOI to the 8259. */
393 		if (irq > 7) {
394 			bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu2,
395 			    PIC_OCW2, OCW2_SELECT | OCW2_EOI | OCW2_SL |
396 			    OCW2_ILS(irq & 7));
397 			irq = 2;
398 		}
399 
400 		bus_space_write_1(sc->sc_iot, sc->sc_ioh_icu1, PIC_OCW2,
401 		    OCW2_SELECT | OCW2_EOI | OCW2_SL | OCW2_ILS(irq));
402 	}
403 }
404 
405 const struct evcnt *
pcib_isa_intr_evcnt(void * v,int irq)406 pcib_isa_intr_evcnt(void *v, int irq)
407 {
408 	struct pcib_softc *sc = v;
409 
410 #if defined(ALGOR_P5064)
411 	if (p5064_isa_to_irqmap[irq] != -1)
412 		return (isa_intr_evcnt(sc->sc_parent_ic, irq));
413 #endif
414 
415 	return (&sc->sc_intrtab[irq].intr_count);
416 }
417 
418 void *
pcib_isa_intr_establish(void * v,int irq,int type,int level,int (* func)(void *),void * arg)419 pcib_isa_intr_establish(void *v, int irq, int type, int level,
420     int (*func)(void *), void *arg)
421 {
422 	struct pcib_softc *sc = v;
423 	struct evbmips_intrhand *ih;
424 	int s;
425 
426 	if (irq > 15 || irq == 2 || type == IST_NONE)
427 		panic("pcib_isa_intr_establish: bad irq or type");
428 
429 #if defined(ALGOR_P5064)
430 	if (p5064_isa_to_irqmap[irq] != -1)
431 		return (isa_intr_establish(sc->sc_parent_ic, irq, type,
432 		    level, func, arg));
433 #endif
434 
435 	switch (sc->sc_intrtab[irq].intr_type) {
436 	case IST_NONE:
437 		sc->sc_intrtab[irq].intr_type = type;
438 		break;
439 
440 	case IST_EDGE:
441 	case IST_LEVEL:
442 		if (type == sc->sc_intrtab[irq].intr_type)
443 			break;
444 		/* FALLTHROUGH */
445 	case IST_PULSE:
446 		/*
447 		 * We can't share interrupts in this case.
448 		 */
449 		return (NULL);
450 	}
451 
452 	ih = kmem_alloc(sizeof(*ih), KM_SLEEP);
453 	ih->ih_func = func;
454 	ih->ih_arg = arg;
455 	ih->ih_irq = irq;
456 	ih->ih_irqmap = NULL;
457 
458 	s = splhigh();
459 
460 	/* Insert the handler into the table. */
461 	LIST_INSERT_HEAD(&sc->sc_intrtab[irq].intr_q, ih, ih_q);
462 	sc->sc_intrtab[irq].intr_type = type;
463 
464 	/* Enable it, set trigger mode. */
465 	sc->sc_imask &= ~(1 << irq);
466 	if (sc->sc_intrtab[irq].intr_type == IST_LEVEL)
467 		sc->sc_elcr |= (1 << irq);
468 	else
469 		sc->sc_elcr &= ~(1 << irq);
470 
471 	pcib_set_icus(sc);
472 
473 	splx(s);
474 
475 	return (ih);
476 }
477 
478 void
pcib_isa_intr_disestablish(void * v,void * arg)479 pcib_isa_intr_disestablish(void *v, void *arg)
480 {
481 	struct pcib_softc *sc = v;
482 	struct evbmips_intrhand *ih = arg;
483 	int s;
484 
485 #if defined(ALGOR_P5064)
486 	if (p5064_isa_to_irqmap[ih->ih_irq] != -1) {
487 		isa_intr_disestablish(sc->sc_parent_ic, ih);
488 		return;
489 	}
490 #endif
491 
492 	s = splhigh();
493 
494 	LIST_REMOVE(ih, ih_q);
495 
496 	/* If there are no more handlers on this IRQ, disable it. */
497 	if (LIST_FIRST(&sc->sc_intrtab[ih->ih_irq].intr_q) == NULL) {
498 		sc->sc_imask |= (1 << ih->ih_irq);
499 		pcib_set_icus(sc);
500 	}
501 
502 	splx(s);
503 
504 	kmem_free(ih, sizeof(*ih));
505 }
506 
507 int
pcib_isa_intr_alloc(void * v,int mask,int type,int * irq)508 pcib_isa_intr_alloc(void *v, int mask, int type, int *irq)
509 {
510 	struct pcib_softc *sc = v;
511 	int i, tmp, bestirq, count;
512 	struct evbmips_intrhand *ih;
513 
514 	if (type == IST_NONE)
515 		panic("pcib_intr_alloc: bogus type");
516 
517 	bestirq = -1;
518 	count = -1;
519 
520 	mask &= ~sc->sc_reserved;
521 
522 #if 0
523 	printf("pcib_intr_alloc: mask = 0x%04x\n", mask);
524 #endif
525 
526 	for (i = 0; i < 16; i++) {
527 		if ((mask & (1 << i)) == 0)
528 			continue;
529 
530 		switch (sc->sc_intrtab[i].intr_type) {
531 		case IST_NONE:
532 			/*
533 			 * If nothing's using the IRQ, just return it.
534 			 */
535 			*irq = i;
536 			return (0);
537 
538 		case IST_EDGE:
539 		case IST_LEVEL:
540 			if (type != sc->sc_intrtab[i].intr_type)
541 				continue;
542 			/*
543 			 * If the IRQ is shareable, count the number of
544 			 * other handlers, and if it's smaller than the
545 			 * last IRQ like this, remember it.
546 			 */
547 			tmp = 0;
548 			for (ih = LIST_FIRST(&sc->sc_intrtab[i].intr_q);
549 			     ih != NULL; ih = LIST_NEXT(ih, ih_q))
550 				tmp++;
551 			if (bestirq == -1 || count > tmp) {
552 				bestirq = i;
553 				count = tmp;
554 			}
555 			break;
556 
557 		case IST_PULSE:
558 			/* This just isn't shareable. */
559 			continue;
560 		}
561 	}
562 
563 	if (bestirq == -1)
564 		return (1);
565 
566 	*irq = bestirq;
567 	return (0);
568 }
569