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