1/*	$NetBSD: pcib.c,v 1.23 2011/07/08 18:48:59 matt 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.23 2011/07/08 18:48:59 matt 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/malloc.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
66const 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
85struct pcib_intrhead {
86	LIST_HEAD(, evbmips_intrhand) intr_q;
87	struct evcnt intr_count;
88	int intr_type;
89};
90
91struct 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
115int	pcib_match(device_t, cfdata_t, void *);
116void	pcib_attach(device_t, device_t, void *);
117
118CFATTACH_DECL_NEW(pcib, sizeof(struct pcib_softc),
119    pcib_match, pcib_attach, NULL, NULL);
120
121void	pcib_isa_attach_hook(device_t, device_t, struct isabus_attach_args *);
122void	pcib_isa_detach_hook(isa_chipset_tag_t, device_t);
123
124int	pcib_intr(void *);
125
126void	pcib_bridge_callback(device_t);
127
128const struct evcnt *pcib_isa_intr_evcnt(void *, int);
129void	*pcib_isa_intr_establish(void *, int, int, int,
130	    int (*)(void *), void *);
131void	pcib_isa_intr_disestablish(void *, void *);
132int	pcib_isa_intr_alloc(void *, int, int, int *);
133
134void	pcib_set_icus(struct pcib_softc *);
135
136int
137pcib_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
148void
149pcib_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
290void
291pcib_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	(void) config_found_ia(sc->sc_dev, "isabus", &iba, isabusprint);
321}
322
323void
324pcib_isa_attach_hook(device_t parent, device_t self,
325    struct isabus_attach_args *iba)
326{
327
328	/* Nothing to do. */
329}
330
331void
332pcib_isa_detach_hook(isa_chipset_tag_t ic, device_t self)
333{
334
335	/* Nothing to do. */
336}
337
338void
339pcib_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
359int
360pcib_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
405const struct evcnt *
406pcib_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
418void *
419pcib_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 = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT);
453	if (ih == NULL)
454		return (NULL);
455
456	ih->ih_func = func;
457	ih->ih_arg = arg;
458	ih->ih_irq = irq;
459	ih->ih_irqmap = NULL;
460
461	s = splhigh();
462
463	/* Insert the handler into the table. */
464	LIST_INSERT_HEAD(&sc->sc_intrtab[irq].intr_q, ih, ih_q);
465	sc->sc_intrtab[irq].intr_type = type;
466
467	/* Enable it, set trigger mode. */
468	sc->sc_imask &= ~(1 << irq);
469	if (sc->sc_intrtab[irq].intr_type == IST_LEVEL)
470		sc->sc_elcr |= (1 << irq);
471	else
472		sc->sc_elcr &= ~(1 << irq);
473
474	pcib_set_icus(sc);
475
476	splx(s);
477
478	return (ih);
479}
480
481void
482pcib_isa_intr_disestablish(void *v, void *arg)
483{
484	struct pcib_softc *sc = v;
485	struct evbmips_intrhand *ih = arg;
486	int s;
487
488#if defined(ALGOR_P5064)
489	if (p5064_isa_to_irqmap[ih->ih_irq] != -1) {
490		isa_intr_disestablish(sc->sc_parent_ic, ih);
491		return;
492	}
493#endif
494
495	s = splhigh();
496
497	LIST_REMOVE(ih, ih_q);
498
499	/* If there are no more handlers on this IRQ, disable it. */
500	if (LIST_FIRST(&sc->sc_intrtab[ih->ih_irq].intr_q) == NULL) {
501		sc->sc_imask |= (1 << ih->ih_irq);
502		pcib_set_icus(sc);
503	}
504
505	splx(s);
506
507	free(ih, M_DEVBUF);
508}
509
510int
511pcib_isa_intr_alloc(void *v, int mask, int type, int *irq)
512{
513	struct pcib_softc *sc = v;
514	int i, tmp, bestirq, count;
515	struct evbmips_intrhand *ih;
516
517	if (type == IST_NONE)
518		panic("pcib_intr_alloc: bogus type");
519
520	bestirq = -1;
521	count = -1;
522
523	mask &= ~sc->sc_reserved;
524
525#if 0
526	printf("pcib_intr_alloc: mask = 0x%04x\n", mask);
527#endif
528
529	for (i = 0; i < 16; i++) {
530		if ((mask & (1 << i)) == 0)
531			continue;
532
533		switch (sc->sc_intrtab[i].intr_type) {
534		case IST_NONE:
535			/*
536			 * If nothing's using the IRQ, just return it.
537			 */
538			*irq = i;
539			return (0);
540
541		case IST_EDGE:
542		case IST_LEVEL:
543			if (type != sc->sc_intrtab[i].intr_type)
544				continue;
545			/*
546			 * If the IRQ is sharable, count the number of
547			 * other handlers, and if it's smaller than the
548			 * last IRQ like this, remember it.
549			 */
550			tmp = 0;
551			for (ih = LIST_FIRST(&sc->sc_intrtab[i].intr_q);
552			     ih != NULL; ih = LIST_NEXT(ih, ih_q))
553				tmp++;
554			if (bestirq == -1 || count > tmp) {
555				bestirq = i;
556				count = tmp;
557			}
558			break;
559
560		case IST_PULSE:
561			/* This just isn't sharable. */
562			continue;
563		}
564	}
565
566	if (bestirq == -1)
567		return (1);
568
569	*irq = bestirq;
570	return (0);
571}
572