xref: /openbsd/sys/arch/luna88k/cbus/cbus.c (revision 264ca280)
1 /*	$OpenBSD: cbus.c,v 1.6 2016/06/13 23:51:58 dlg 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 "necsb.h"
40 #include "pcic.h"
41 
42 static struct cbus_attach_args cbus_devs[] = {
43 #if NNECSB > 0
44 	{ "necsb",	-1 },	/* PC-9801-86 sound board */
45 #endif
46 #if NPCIC > 0
47 	{ "pcic",	-1 },	/* PC-9801-102 & PC-9821X[AE]-01 PCMCIA board */
48 #endif
49 	{ "pcex",	-1 }	/* C-bus "generic" driver */
50 };
51 
52 /*
53  * C-bus interrupt status register
54  */
55 #define CBUS_INTR_STAT_REG	(PC_BASE + 0x1100000)
56 volatile u_int8_t *cbus_isreg = (u_int8_t *)CBUS_INTR_STAT_REG;
57 
58 /* autoconf stuff */
59 int cbus_match(struct device *, void *, void *);
60 void cbus_attach(struct device *, struct device *, void *);
61 int cbus_print(void *, const char *);
62 
63 struct cbus_softc {
64 	struct device sc_dev;
65 	struct cbus_isr_t cbus_isr[NCBUSISR];
66 	u_int8_t registered;
67 };
68 
69 const struct cfattach cbus_ca = {
70 	sizeof(struct cbus_softc), cbus_match, cbus_attach
71 };
72 
73 struct cfdriver cbus_cd = {
74 	NULL, "cbus", DV_DULL
75 };
76 
77 /* prototypes */
78 void cbus_isrdispatch(int);
79 int cbus_intr(void *);
80 
81 int
82 cbus_match(struct device *parent, void *cf, void *aux)
83 {
84 	struct mainbus_attach_args *ma = aux;
85 
86 	if (strcmp(ma->ma_name, cbus_cd.cd_name))
87 		return 0;
88 #if 0
89 	if (badaddr((vaddr_t)ma->ma_addr, 4))
90 		return 0;
91 #endif
92 	return 1;
93 }
94 
95 void
96 cbus_attach(struct device *parent, struct device *self, void *args)
97 {
98 	struct cbus_softc *sc = (struct cbus_softc *)self;
99 	struct mainbus_attach_args *ma = args;
100 	int i;
101 
102 	for (i = 0; i < NCBUSISR; i++) {
103 		struct cbus_isr_t *ci = &sc->cbus_isr[i];
104 		ci->isr_func = NULL;
105 		ci->isr_arg = NULL;
106 		ci->isr_intlevel = ci->isr_ipl = -1;
107 		/* clearing interrupt flags (INT0-INT6) */
108 		*cbus_isreg = (u_int8_t)(6 - i);
109 	}
110 	sc->registered = 0x00;
111 
112 	/* register C-bus interrupt service routine on mainbus */
113 	isrlink_autovec(cbus_intr, (void *)self, ma->ma_ilvl,
114 	    ISRPRI_TTY, self->dv_xname);
115 
116 	printf("\n");
117 
118 	for (i = 0; i < sizeof(cbus_devs)/sizeof(cbus_devs[0]); i++)
119 		config_found(self, &cbus_devs[i], cbus_print);
120 
121 	return;
122 }
123 
124 int
125 cbus_print(void *aux, const char *pnp)
126 {
127 	struct cbus_attach_args *caa = aux;
128 
129 	if (pnp)
130 		printf("%s at %s", caa->ca_name, pnp);	/* not configured */
131 	if (caa->ca_intlevel != -1)
132 		printf(" int %d", caa->ca_intlevel);
133 
134 	return UNCONF;
135 }
136 
137 /*
138  * Register a C-bus interrupt service routine.
139  */
140 int
141 cbus_isrlink(int (*func)(void *), void *arg, int intlevel, int ipl,
142     const char *name)
143 {
144 	struct cbus_softc *sc = NULL;
145 	struct cbus_isr_t *ci;
146 
147 	if (cbus_cd.cd_ndevs != 0)
148 		sc = cbus_cd.cd_devs[0];
149 	if (sc == NULL)
150 		panic("cbus_isrlink: can't find cbus_softc");
151 
152 #ifdef DIAGNOSTIC
153 	if (intlevel < 0 || intlevel >= NCBUSISR) {
154 		printf("cbus_isrlink: bad INT level %d\n", intlevel);
155 		return -1;
156 	}
157 #endif
158 
159 	ci = &sc->cbus_isr[intlevel];
160 
161 	if (ci->isr_func != NULL) {
162 		printf("cbus_isrlink: isr already assigned on INT%d\n",
163 		    intlevel);
164 		return -1;
165 	}
166 
167 	/* set the entry */
168 	ci->isr_func = func;
169 	ci->isr_arg = arg;
170 	ci->isr_intlevel = intlevel;
171 	ci->isr_ipl = ipl;
172 	evcount_attach(&ci->isr_count, name, &ci->isr_intlevel);
173 	sc->registered |= (1 << (6 - intlevel));
174 #ifdef CBUS_DEBUG
175 	printf("cbus_isrlink: sc->registered = 0x%02x\n", sc->registered);
176 #endif
177 
178 	return 0;
179 }
180 
181 /*
182  * Unregister a C-bus interrupt service routine.
183  */
184 int
185 cbus_isrunlink(int (*func)(void *), int intlevel)
186 {
187 	struct cbus_softc *sc = NULL;
188 	struct cbus_isr_t *ci;
189 
190 	if (cbus_cd.cd_ndevs != 0)
191 		sc = cbus_cd.cd_devs[0];
192 	if (sc == NULL)
193 		panic("cbus_isrunlink: can't find cbus_softc");
194 
195 #ifdef DIAGNOSTIC
196 	if (intlevel < 0 || intlevel >= NCBUSISR) {
197 		printf("cbus_isrunlink: bad INT level %d\n", intlevel);
198 		return -1;
199 	}
200 #endif
201 
202 	ci = &sc->cbus_isr[intlevel];
203 
204 	if (ci->isr_func == NULL) {
205 		printf("cbus_isrunlink: isr not assigned on INT%d\n", intlevel);
206 		return -1;
207 	}
208 
209 	/* reset the entry */
210 	ci->isr_func = NULL;
211 	ci->isr_arg = NULL;
212 	ci->isr_intlevel = ci->isr_ipl = -1;
213 	evcount_detach(&ci->isr_count);
214 	sc->registered &= ~(1 << (6 - intlevel));
215 
216 	/* clear interrupt flags */
217 	*cbus_isreg = (u_int8_t)(6 - intlevel);
218 #ifdef CBUS_DEBUG
219 	printf("cbus_isrunlink: sc->registered = 0x%02x\n", sc->registered);
220 #endif
221 
222 	return 0;
223 }
224 
225 /*
226  * Dispatch C-bus interrupt service routines.
227  */
228 void
229 cbus_isrdispatch(int intlevel)
230 {
231 	int rc, s;
232 	static int straycount, unexpected;
233 	struct cbus_softc *sc = NULL;
234 	struct cbus_isr_t *ci;
235 
236 	if (cbus_cd.cd_ndevs != 0)
237 		sc = cbus_cd.cd_devs[0];
238 	if (sc == NULL)
239 		panic("cbus_isrdispatch: can't find cbus_softc");
240 
241 #ifdef DIAGNOSTIC
242 	if (intlevel < 0 || intlevel >= NCBUSISR)
243 		panic("cbus_isrdispatch: bad INT level 0x%d", intlevel);
244 #endif
245 
246 	ci = &sc->cbus_isr[intlevel];
247 
248 	if (ci->isr_func == NULL) {
249 		printf("cbus_isrdispatch: INT%d unexpected\n", intlevel);
250 		if (++unexpected > 10)
251 			panic("too many unexpected interrupts");
252 		return;
253 	}
254 
255 	s = splraise(ci->isr_ipl);
256 	rc = ci->isr_func(ci->isr_arg);
257 	splx(s);
258 
259 	if (rc != 0)
260 		ci->isr_count.ec_count++;
261 
262 	if (rc)
263 		straycount = 0;
264 	else if (++straycount > 50)
265 		panic("cbus_isrdispatch: too many stray interrupts");
266 	else
267 		printf("cbus_isrdispatch: stray INT%d, IPL=%d\n", intlevel,
268 		    ci->isr_ipl);
269 }
270 
271 /*
272  * Return registered status of interrupt service routines.
273  */
274 u_int8_t
275 cbus_intr_registered(void)
276 {
277 	struct cbus_softc *sc = NULL;
278 
279 	if (cbus_cd.cd_ndevs != 0)
280 		sc = cbus_cd.cd_devs[0];
281 	if (sc == NULL)
282 		panic("cbus_intr_used: can't find cbus_softc");
283 
284 	return sc->registered;
285 }
286 
287 /*
288  * Note about interrupt on PC-9801 extension board slot
289  *
290  * PC-9801 extension board slot bus (so-called 'C-bus' in Japan) use 8 own
291  * interrupt levels, INT0-INT6, and NMI.  On LUNA-88K2, they all trigger
292  * level 4 interrupt on mainbus, so we need to check the dedicated interrupt
293  * status register to know which C-bus interrupt is occurred.
294  *
295  * The interrupt status register for C-bus is located at
296  * (u_int8_t *)CBUS_INTR_STAT. Each bit of the register becomes 0 when
297  * corresponding C-bus interrupt has occurred, otherwise 1.
298  *
299  * bit 7 = NMI(?)
300  * bit 6 = INT0
301  * bit 5 = INT1
302  *  :
303  * bit 0 = INT6
304  *
305  * To clear the C-bus interrupt flag, write the corresponding 'bit' number
306  * (as u_int_8) to the register.  For example, if you want to clear INT1,
307  * you should write '5' like:
308  *   *(u_int8_t *)CBUS_INTR_STAT = 5;
309  */
310 
311 /*
312  * Interrupt handler on mainbus.
313  */
314 int
315 cbus_intr(void *arg)
316 {
317 	struct cbus_softc *sc = (struct cbus_softc *)arg;
318 	u_int8_t intr_status;
319 	int n;
320 
321 	/*
322 	 * LUNA-88K2's interrupt level 4 is shared with other devices,
323 	 * such as le(4), for example.  So we check:
324 	 * - the value of our C-bus interrupt status register, and
325 	 * - if the INT level is what we are looking for.
326 	 */
327 	intr_status = *cbus_isreg & sc->registered;
328 	if (intr_status == sc->registered) return 0;	/* Not for me */
329 
330 #ifdef CBUS_DEBUG
331 	printf("cbus_intr: called, *cbus_isreg=0x%02x, registered = 0x%02x\n",
332 	    *cbus_isreg, sc->registered);
333 #endif
334 	/* Make the bit pattern that we should proces */
335 	intr_status = intr_status ^ sc->registered;
336 #ifdef CBUS_DEBUG
337 	printf("cbus_intr: processing 0x%02x\n", intr_status);
338 #endif
339 
340 	/* Process, and clear each interrupt flag */
341 	while ((n = ff1(intr_status)) != 32) {
342 		cbus_isrdispatch(6 - n);
343 		*cbus_isreg = (u_int8_t)n;
344 		intr_status &= ~(1 << n);
345 	}
346 
347 	return 1;
348 }
349