xref: /openbsd/sys/arch/luna88k/cbus/cbus.c (revision 4bdff4be)
1 /*	$OpenBSD: cbus.c,v 1.9 2021/03/11 11:16:58 jsg Exp $	*/
2 
3 /*
4  * Copyright (c) 2014 Kenji Aoyama.
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /*
20  * PC-9801 extension board slot bus ('C-bus') driver for LUNA-88K2.
21  */
22 
23 #include <sys/param.h>
24 #include <sys/device.h>
25 #include <sys/malloc.h>
26 #include <sys/systm.h>
27 
28 #include <machine/asm_macro.h>	/* ff1() */
29 #include <machine/autoconf.h>
30 #include <machine/board.h>	/* PC_BASE */
31 
32 #include <luna88k/cbus/cbusvar.h>
33 #include <luna88k/luna88k/isr.h>	/* isrlink_autovec() */
34 
35 #if 0
36 #define CBUS_DEBUG
37 #endif
38 
39 #include "ne.h"
40 #include "necsb.h"
41 #include "pcic.h"
42 
43 static struct cbus_attach_args cbus_devs[] = {
44 #if NNE > 0
45 	/* NE-2000 compatible ethernet */
46 	{ "ne",    -1, -1, -1, -1, -1 },
47 #endif
48 #if NNECSB > 0
49 	/* PC-9801-86 sound board */
50 	{ "necsb", -1, -1, -1, -1, -1 },
51 #endif
52 #if NPCIC > 0
53 	/* PC-9801-102 & PC-9821X[AE]-01 PCMCIA board */
54 	{ "pcic",  -1, -1, -1, -1, -1 },
55 #endif
56 	/* C-bus "generic" driver */
57 	{ "pcex",  -1, -1, -1, -1, -1 }
58 };
59 
60 /*
61  * C-bus interrupt status register
62  */
63 #define CBUS_INTR_STAT_REG	(PC_BASE + 0x1100000)
64 volatile u_int8_t *cbus_isreg = (u_int8_t *)CBUS_INTR_STAT_REG;
65 
66 /* autoconf stuff */
67 int cbus_match(struct device *, void *, void *);
68 void cbus_attach(struct device *, struct device *, void *);
69 int cbus_print(void *, const char *);
70 
71 struct cbus_softc {
72 	struct device sc_dev;
73 	struct cbus_isr_t cbus_isr[NCBUSISR];
74 	u_int8_t registered;
75 };
76 
77 const struct cfattach cbus_ca = {
78 	sizeof(struct cbus_softc), cbus_match, cbus_attach
79 };
80 
81 struct cfdriver cbus_cd = {
82 	NULL, "cbus", DV_DULL
83 };
84 
85 /* prototypes */
86 void cbus_isrdispatch(int);
87 int cbus_intr(void *);
88 
89 int
90 cbus_match(struct device *parent, void *cf, void *aux)
91 {
92 	struct mainbus_attach_args *ma = aux;
93 
94 	if (strcmp(ma->ma_name, cbus_cd.cd_name))
95 		return 0;
96 #if 0
97 	if (badaddr((vaddr_t)ma->ma_addr, 4))
98 		return 0;
99 #endif
100 	return 1;
101 }
102 
103 void
104 cbus_attach(struct device *parent, struct device *self, void *args)
105 {
106 	struct cbus_softc *sc = (struct cbus_softc *)self;
107 	struct mainbus_attach_args *ma = args;
108 	int i;
109 
110 	for (i = 0; i < NCBUSISR; i++) {
111 		struct cbus_isr_t *ci = &sc->cbus_isr[i];
112 		ci->isr_func = NULL;
113 		ci->isr_arg = NULL;
114 		ci->isr_intlevel = ci->isr_ipl = -1;
115 		/* clearing interrupt flags (INT0-INT6) */
116 		*cbus_isreg = (u_int8_t)(6 - i);
117 	}
118 	sc->registered = 0x00;
119 
120 	/* register C-bus interrupt service routine on mainbus */
121 	isrlink_autovec(cbus_intr, (void *)self, ma->ma_ilvl,
122 	    ISRPRI_TTY, self->dv_xname);
123 
124 	printf("\n");
125 
126 	for (i = 0; i < sizeof(cbus_devs)/sizeof(cbus_devs[0]); i++)
127 		config_found(self, &cbus_devs[i], cbus_print);
128 
129 	return;
130 }
131 
132 int
133 cbus_print(void *aux, const char *pnp)
134 {
135 	struct cbus_attach_args *caa = aux;
136 
137 	if (pnp)
138 		printf("%s at %s", caa->ca_name, pnp);	/* not configured */
139 	if (caa->ca_iobase != -1)
140 		printf(" port 0x%x", caa->ca_iobase);
141 	if (caa->ca_maddr != -1)
142 		printf(" addr 0x%x", caa->ca_maddr);
143 	if (caa->ca_int != -1)
144 		printf(" int %d", caa->ca_int);
145 
146 	return UNCONF;
147 }
148 
149 /*
150  * Register a C-bus interrupt service routine.
151  */
152 int
153 cbus_isrlink(int (*func)(void *), void *arg, int intlevel, int ipl,
154     const char *name)
155 {
156 	struct cbus_softc *sc = NULL;
157 	struct cbus_isr_t *ci;
158 
159 	if (cbus_cd.cd_ndevs != 0)
160 		sc = cbus_cd.cd_devs[0];
161 	if (sc == NULL)
162 		panic("cbus_isrlink: can't find cbus_softc");
163 
164 #ifdef DIAGNOSTIC
165 	if (intlevel < 0 || intlevel >= NCBUSISR) {
166 		printf("cbus_isrlink: bad INT level %d\n", intlevel);
167 		return -1;
168 	}
169 #endif
170 
171 	ci = &sc->cbus_isr[intlevel];
172 
173 	if (ci->isr_func != NULL) {
174 		printf("cbus_isrlink: isr already assigned on INT%d\n",
175 		    intlevel);
176 		return -1;
177 	}
178 
179 	/* set the entry */
180 	ci->isr_func = func;
181 	ci->isr_arg = arg;
182 	ci->isr_intlevel = intlevel;
183 	ci->isr_ipl = ipl;
184 	evcount_attach(&ci->isr_count, name, &ci->isr_intlevel);
185 	sc->registered |= (1 << (6 - intlevel));
186 #ifdef CBUS_DEBUG
187 	printf("cbus_isrlink: sc->registered = 0x%02x\n", sc->registered);
188 #endif
189 
190 	return 0;
191 }
192 
193 /*
194  * Unregister a C-bus interrupt service routine.
195  */
196 int
197 cbus_isrunlink(int (*func)(void *), int intlevel)
198 {
199 	struct cbus_softc *sc = NULL;
200 	struct cbus_isr_t *ci;
201 
202 	if (cbus_cd.cd_ndevs != 0)
203 		sc = cbus_cd.cd_devs[0];
204 	if (sc == NULL)
205 		panic("cbus_isrunlink: can't find cbus_softc");
206 
207 #ifdef DIAGNOSTIC
208 	if (intlevel < 0 || intlevel >= NCBUSISR) {
209 		printf("cbus_isrunlink: bad INT level %d\n", intlevel);
210 		return -1;
211 	}
212 #endif
213 
214 	ci = &sc->cbus_isr[intlevel];
215 
216 	if (ci->isr_func == NULL) {
217 		printf("cbus_isrunlink: isr not assigned on INT%d\n", intlevel);
218 		return -1;
219 	}
220 
221 	/* reset the entry */
222 	ci->isr_func = NULL;
223 	ci->isr_arg = NULL;
224 	ci->isr_intlevel = ci->isr_ipl = -1;
225 	evcount_detach(&ci->isr_count);
226 	sc->registered &= ~(1 << (6 - intlevel));
227 
228 	/* clear interrupt flags */
229 	*cbus_isreg = (u_int8_t)(6 - intlevel);
230 #ifdef CBUS_DEBUG
231 	printf("cbus_isrunlink: sc->registered = 0x%02x\n", sc->registered);
232 #endif
233 
234 	return 0;
235 }
236 
237 /*
238  * Dispatch C-bus interrupt service routines.
239  */
240 void
241 cbus_isrdispatch(int intlevel)
242 {
243 	int rc, s;
244 	static int straycount, unexpected;
245 	struct cbus_softc *sc = NULL;
246 	struct cbus_isr_t *ci;
247 
248 	if (cbus_cd.cd_ndevs != 0)
249 		sc = cbus_cd.cd_devs[0];
250 	if (sc == NULL)
251 		panic("cbus_isrdispatch: can't find cbus_softc");
252 
253 #ifdef DIAGNOSTIC
254 	if (intlevel < 0 || intlevel >= NCBUSISR)
255 		panic("cbus_isrdispatch: bad INT level 0x%d", intlevel);
256 #endif
257 
258 	ci = &sc->cbus_isr[intlevel];
259 
260 	if (ci->isr_func == NULL) {
261 		printf("cbus_isrdispatch: INT%d unexpected\n", intlevel);
262 		if (++unexpected > 10)
263 			panic("too many unexpected interrupts");
264 		return;
265 	}
266 
267 	s = splraise(ci->isr_ipl);
268 	rc = ci->isr_func(ci->isr_arg);
269 	splx(s);
270 
271 	if (rc != 0)
272 		ci->isr_count.ec_count++;
273 
274 	if (rc)
275 		straycount = 0;
276 	else if (++straycount > 50)
277 		panic("cbus_isrdispatch: too many stray interrupts");
278 	else
279 		printf("cbus_isrdispatch: stray INT%d, IPL=%d\n", intlevel,
280 		    ci->isr_ipl);
281 }
282 
283 /*
284  * Return registered status of interrupt service routines.
285  */
286 u_int8_t
287 cbus_intr_registered(void)
288 {
289 	struct cbus_softc *sc = NULL;
290 
291 	if (cbus_cd.cd_ndevs != 0)
292 		sc = cbus_cd.cd_devs[0];
293 	if (sc == NULL)
294 		panic("cbus_intr_used: can't find cbus_softc");
295 
296 	return sc->registered;
297 }
298 
299 /*
300  * Note about interrupt on PC-9801 extension board slot
301  *
302  * PC-9801 extension board slot bus (so-called 'C-bus' in Japan) use 8 own
303  * interrupt levels, INT0-INT6, and NMI.  On LUNA-88K2, they all trigger
304  * level 4 interrupt on mainbus, so we need to check the dedicated interrupt
305  * status register to know which C-bus interrupt is occurred.
306  *
307  * The interrupt status register for C-bus is located at
308  * (u_int8_t *)CBUS_INTR_STAT. Each bit of the register becomes 0 when
309  * corresponding C-bus interrupt has occurred, otherwise 1.
310  *
311  * bit 7 = NMI(?)
312  * bit 6 = INT0
313  * bit 5 = INT1
314  *  :
315  * bit 0 = INT6
316  *
317  * To clear the C-bus interrupt flag, write the corresponding 'bit' number
318  * (as u_int_8) to the register.  For example, if you want to clear INT1,
319  * you should write '5' like:
320  *   *(u_int8_t *)CBUS_INTR_STAT = 5;
321  */
322 
323 /*
324  * Interrupt handler on mainbus.
325  */
326 int
327 cbus_intr(void *arg)
328 {
329 	struct cbus_softc *sc = (struct cbus_softc *)arg;
330 	u_int8_t intr_status;
331 	int n;
332 
333 	/*
334 	 * LUNA-88K2's interrupt level 4 is shared with other devices,
335 	 * such as le(4), for example.  So we check:
336 	 * - the value of our C-bus interrupt status register, and
337 	 * - if the INT level is what we are looking for.
338 	 */
339 	intr_status = *cbus_isreg & sc->registered;
340 	if (intr_status == sc->registered) return 0;	/* Not for me */
341 
342 #ifdef CBUS_DEBUG
343 	printf("cbus_intr: called, *cbus_isreg=0x%02x, registered = 0x%02x\n",
344 	    *cbus_isreg, sc->registered);
345 #endif
346 	/* Make the bit pattern that we should process */
347 	intr_status = intr_status ^ sc->registered;
348 #ifdef CBUS_DEBUG
349 	printf("cbus_intr: processing 0x%02x\n", intr_status);
350 #endif
351 
352 	/* Process, and clear each interrupt flag */
353 	while ((n = ff1(intr_status)) != 32) {
354 		cbus_isrdispatch(6 - n);
355 		*cbus_isreg = (u_int8_t)n;
356 		intr_status &= ~(1 << n);
357 	}
358 
359 	return 1;
360 }
361