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