pci_kn20aa.c revision 1.7
1/*	$OpenBSD: pci_kn20aa.c,v 1.7 1996/10/30 22:40:06 niklas Exp $	*/
2/*	$NetBSD: pci_kn20aa.c,v 1.18 1996/10/13 03:00:12 christos Exp $	*/
3
4/*
5 * Copyright (c) 1995, 1996 Carnegie-Mellon University.
6 * All rights reserved.
7 *
8 * Author: Chris G. Demetriou
9 *
10 * Permission to use, copy, modify and distribute this software and
11 * its documentation is hereby granted, provided that both the copyright
12 * notice and this permission notice appear in all copies of the
13 * software, derivative works or modified versions, and any portions
14 * thereof, and that both notices appear in supporting documentation.
15 *
16 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
17 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
18 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
19 *
20 * Carnegie Mellon requests users of this software to return to
21 *
22 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
23 *  School of Computer Science
24 *  Carnegie Mellon University
25 *  Pittsburgh PA 15213-3890
26 *
27 * any improvements or extensions that they make and grant Carnegie the
28 * rights to redistribute these changes.
29 */
30
31#include <sys/types.h>
32#include <sys/param.h>
33#include <sys/time.h>
34#include <sys/systm.h>
35#include <sys/errno.h>
36#include <sys/malloc.h>
37#include <sys/device.h>
38#include <sys/syslog.h>
39
40#include <vm/vm.h>
41
42#include <machine/autoconf.h>
43
44#include <dev/pci/pcireg.h>
45#include <dev/pci/pcivar.h>
46
47#include <alpha/pci/ciareg.h>
48#include <alpha/pci/ciavar.h>
49
50#include <alpha/pci/pci_kn20aa.h>
51
52#ifndef EVCNT_COUNTERS
53#include <machine/intrcnt.h>
54#endif
55
56#include "sio.h"
57#if NSIO
58#include <alpha/pci/siovar.h>
59#endif
60
61int	dec_kn20aa_intr_map __P((void *, pcitag_t, int, int,
62	    pci_intr_handle_t *));
63const char *dec_kn20aa_intr_string __P((void *, pci_intr_handle_t));
64void	*dec_kn20aa_intr_establish __P((void *, pci_intr_handle_t,
65	    int, int (*func)(void *), void *, char *));
66void	dec_kn20aa_intr_disestablish __P((void *, void *));
67
68#define	KN20AA_PCEB_IRQ	31
69#define	KN20AA_MAX_IRQ	32
70#define	PCI_STRAY_MAX	5
71
72struct kn20aa_intrhand {
73	TAILQ_ENTRY(kn20aa_intrhand) ih_q;
74        int     (*ih_fun) __P((void *));
75        void    *ih_arg;
76        u_long  ih_count;
77        int     ih_level;
78};
79TAILQ_HEAD(kn20aa_intrchain, kn20aa_intrhand);
80
81struct kn20aa_intrchain kn20aa_pci_intrs[KN20AA_MAX_IRQ];
82int	kn20aa_pci_strayintrcnt[KN20AA_MAX_IRQ];
83#ifdef EVCNT_COUNTERS
84struct evcnt kn20aa_intr_evcnt;
85#endif
86
87void	kn20aa_pci_strayintr __P((int irq));
88void	kn20aa_iointr __P((void *framep, unsigned long vec));
89void	kn20aa_enable_intr __P((int irq));
90void	kn20aa_disable_intr __P((int irq));
91struct kn20aa_intrhand *kn20aa_attach_intr __P((struct kn20aa_intrchain *,
92			    int, int (*) (void *), void *));
93
94void
95pci_kn20aa_pickintr(ccp)
96	struct cia_config *ccp;
97{
98	int i;
99	bus_chipset_tag_t bc = &ccp->cc_bc;
100	pci_chipset_tag_t pc = &ccp->cc_pc;
101
102	for (i = 0; i < KN20AA_MAX_IRQ; i++)
103		TAILQ_INIT(&kn20aa_pci_intrs[i]);
104
105        pc->pc_intr_v = ccp;
106        pc->pc_intr_map = dec_kn20aa_intr_map;
107        pc->pc_intr_string = dec_kn20aa_intr_string;
108        pc->pc_intr_establish = dec_kn20aa_intr_establish;
109        pc->pc_intr_disestablish = dec_kn20aa_intr_disestablish;
110
111#if NSIO
112	sio_intr_setup(bc);
113#endif
114
115	set_iointr(kn20aa_iointr);
116
117#if NSIO
118	kn20aa_enable_intr(KN20AA_PCEB_IRQ);
119#if 0 /* XXX init PCEB interrupt handler? */
120	kn20aa_attach_intr(&kn20aa_pci_intrs[KN20AA_PCEB_IRQ], ???, ???, ???);
121#endif
122#endif
123}
124
125int
126dec_kn20aa_intr_map(ccv, bustag, buspin, line, ihp)
127        void *ccv;
128        pcitag_t bustag;
129        int buspin, line;
130        pci_intr_handle_t *ihp;
131{
132	struct cia_config *ccp = ccv;
133	pci_chipset_tag_t pc = &ccp->cc_pc;
134	int device;
135	int kn20aa_irq;
136
137        if (buspin == 0) {
138                /* No IRQ used. */
139                return 1;
140        }
141        if (buspin > 4) {
142                printf("pci_map_int: bad interrupt pin %d\n", buspin);
143                return 1;
144        }
145
146	/*
147	 * Slot->interrupt translation.  Appears to work, though it
148	 * may not hold up forever.
149	 *
150	 * The DEC engineers who did this hardware obviously engaged
151	 * in random drug testing.
152	 */
153	pci_decompose_tag(pc, bustag, NULL, &device, NULL);
154	switch (device) {
155	case 11:
156	case 12:
157		kn20aa_irq = ((device - 11) + 0) * 4;
158		break;
159
160	case 7:
161		kn20aa_irq = 8;
162		break;
163
164	case 9:
165		kn20aa_irq = 12;
166		break;
167
168	case 6:					/* 21040 on AlphaStation 500 */
169		kn20aa_irq = 13;
170		break;
171
172	case 8:
173		kn20aa_irq = 16;
174		break;
175
176	default:
177#ifdef KN20AA_BOGUS_IRQ_FROB
178		*ihp = 0xdeadbeef;
179		printf("\n\n BOGUS INTERRUPT MAPPING: dev %d, pin %d\n",
180		    device, buspin);
181		return (0);
182#endif
183		panic("pci_kn20aa_map_int: invalid device number %d\n",
184		    device);
185	}
186
187	kn20aa_irq += buspin - 1;
188	if (kn20aa_irq > KN20AA_MAX_IRQ)
189		panic("pci_kn20aa_map_int: kn20aa_irq too large (%d)\n",
190		    kn20aa_irq);
191
192	*ihp = kn20aa_irq;
193	return (0);
194}
195
196const char *
197dec_kn20aa_intr_string(ccv, ih)
198	void *ccv;
199	pci_intr_handle_t ih;
200{
201        static char irqstr[15];          /* 11 + 2 + NULL + sanity */
202
203#ifdef KN20AA_BOGUS_IRQ_FROB
204	if (ih == 0xdeadbeef) {
205		sprintf(irqstr, "BOGUS");
206		return (irqstr);
207	}
208#endif
209        if (ih > KN20AA_MAX_IRQ)
210                panic("dec_kn20aa_a50_intr_string: bogus kn20aa IRQ 0x%x\n",
211		    ih);
212
213        sprintf(irqstr, "kn20aa irq %d", ih);
214        return (irqstr);
215}
216
217void *
218dec_kn20aa_intr_establish(ccv, ih, level, func, arg, name)
219        void *ccv, *arg;
220        pci_intr_handle_t ih;
221        int level;
222        int (*func) __P((void *));
223	char *name;
224{
225	void *cookie;
226
227#ifdef KN20AA_BOGUS_IRQ_FROB
228	if (ih == 0xdeadbeef) {
229		int i;
230		char chars[10];
231
232		printf("dec_kn20aa_intr_establish: BOGUS IRQ\n");
233		do {
234			printf("IRQ to enable? ");
235			getstr(chars, 10);
236			i = atoi(chars);
237		} while (i < 0 || i > 32);
238		printf("ENABLING IRQ %d\n", i);
239		kn20aa_enable_intr(i);
240		return ((void *)0xbabefacedeadbeef);
241	}
242#endif
243        if (ih > KN20AA_MAX_IRQ)
244                panic("dec_kn20aa_intr_establish: bogus kn20aa IRQ 0x%x\n",
245		    ih);
246
247	cookie = kn20aa_attach_intr(&kn20aa_pci_intrs[ih], level, func, arg);
248	kn20aa_enable_intr(ih);
249	return (cookie);
250}
251
252void
253dec_kn20aa_intr_disestablish(ccv, cookie)
254        void *ccv, *cookie;
255{
256	panic("dec_kn20aa_intr_disestablish not implemented"); /* XXX */
257}
258
259/*
260 * caught a stray interrupt; notify if not too many seen already.
261 */
262void
263kn20aa_pci_strayintr(irq)
264	int irq;
265{
266
267	kn20aa_pci_strayintrcnt[irq]++;
268	if (kn20aa_pci_strayintrcnt[irq] == PCI_STRAY_MAX)
269		kn20aa_disable_intr(irq);
270
271	log(LOG_ERR, "stray kn20aa irq %d\n", irq);
272	if (kn20aa_pci_strayintrcnt[irq] == PCI_STRAY_MAX)
273		log(LOG_ERR, "disabling interrupts on kn20aa irq %d\n", irq);
274}
275
276void
277kn20aa_iointr(framep, vec)
278	void *framep;
279	unsigned long vec;
280{
281	struct kn20aa_intrhand *ih;
282	int irq, handled;
283
284	if (vec >= 0x900) {
285		if (vec >= 0x900 + (KN20AA_MAX_IRQ << 4))
286			panic("kn20aa_iointr: vec 0x%x out of range\n", vec);
287		irq = (vec - 0x900) >> 4;
288
289#ifdef EVCNT_COUNTERS
290		kn20aa_intr_evcnt.ev_count++;
291#else
292		if (KN20AA_MAX_IRQ != INTRCNT_KN20AA_IRQ_LEN)
293			panic("kn20aa interrupt counter sizes inconsistent");
294		intrcnt[INTRCNT_KN20AA_IRQ + irq]++;
295#endif
296
297		for (ih = kn20aa_pci_intrs[irq].tqh_first, handled = 0;
298		    ih != NULL; ih = ih->ih_q.tqe_next) {
299			int rv;
300
301			rv = (*ih->ih_fun)(ih->ih_arg);
302
303			ih->ih_count++;
304			handled = handled || (rv != 0);
305		}
306		if (!handled)
307			kn20aa_pci_strayintr(irq);
308		return;
309	}
310	if (vec >= 0x800) {
311#if NSIO
312		sio_iointr(framep, vec);
313#endif
314		return;
315	}
316	panic("kn20aa_iointr: weird vec 0x%x\n", vec);
317}
318
319void
320kn20aa_enable_intr(irq)
321	int irq;
322{
323
324	/*
325	 * From disassembling small bits of the OSF/1 kernel:
326	 * the following appears to enable a given interrupt request.
327	 * "blech."  I'd give valuable body parts for better docs or
328	 * for a good decompiler.
329	 */
330	alpha_mb();
331	REGVAL(0x8780000000L + 0x40L) |= (1 << irq);	/* XXX */
332	alpha_mb();
333}
334
335void
336kn20aa_disable_intr(irq)
337	int irq;
338{
339
340	alpha_mb();
341	REGVAL(0x8780000000L + 0x40L) &= ~(1 << irq);	/* XXX */
342	alpha_mb();
343}
344
345struct kn20aa_intrhand *
346kn20aa_attach_intr(chain, level, func, arg)
347	struct kn20aa_intrchain *chain;
348	int level;
349	int (*func) __P((void *));
350	void *arg;
351{
352	struct kn20aa_intrhand *nintrhand;
353
354	nintrhand = (struct kn20aa_intrhand *)
355	    malloc(sizeof *nintrhand, M_DEVBUF, M_WAITOK);
356
357        nintrhand->ih_fun = func;
358        nintrhand->ih_arg = arg;
359        nintrhand->ih_count = 0;
360        nintrhand->ih_level = level;
361	TAILQ_INSERT_TAIL(chain, nintrhand, ih_q);
362
363	return (nintrhand);
364}
365