1 /* $NetBSD: frodo.c,v 1.30 2011/02/12 22:48:04 tsutsui Exp $ */ 2 3 /*- 4 * Copyright (c) 1997, 1998, 1999 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Copyright (c) 1997 Michael Smith. All rights reserved. 34 * 35 * Redistribution and use in source and binary forms, with or without 36 * modification, are permitted provided that the following conditions 37 * are met: 38 * 1. Redistributions of source code must retain the above copyright 39 * notice, this list of conditions and the following disclaimer. 40 * 2. Redistributions in binary form must reproduce the above copyright 41 * notice, this list of conditions and the following disclaimer in the 42 * documentation and/or other materials provided with the distribution. 43 * 44 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 45 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 46 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 47 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 48 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 49 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 50 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 51 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 52 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 53 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 54 * SUCH DAMAGE. 55 */ 56 57 /* 58 * Support for the "Frodo" (a.k.a. "Apollo Utility") chip found 59 * in HP Apollo 9000/4xx workstations, as well as 9000/382 controllers. 60 */ 61 62 #include <sys/cdefs.h> 63 __KERNEL_RCSID(0, "$NetBSD: frodo.c,v 1.30 2011/02/12 22:48:04 tsutsui Exp $"); 64 65 #define _HP300_INTR_H_PRIVATE 66 67 #include <sys/param.h> 68 #include <sys/systm.h> 69 #include <sys/kernel.h> 70 #include <sys/syslog.h> 71 #include <sys/device.h> 72 73 #include <machine/bus.h> 74 #include <machine/cpu.h> 75 #include <machine/intr.h> 76 #include <machine/hp300spu.h> 77 78 #include <hp300/dev/intiovar.h> 79 80 #include <hp300/dev/frodoreg.h> 81 #include <hp300/dev/frodovar.h> 82 83 /* 84 * Description of a Frodo interrupt handler. 85 */ 86 struct frodo_interhand { 87 int (*ih_fn)(void *); 88 void *ih_arg; 89 int ih_priority; 90 }; 91 92 struct frodo_softc { 93 device_t sc_dev; /* generic device glue */ 94 volatile uint8_t *sc_regs; /* register base */ 95 struct frodo_interhand sc_intr[FRODO_NINTR]; /* interrupt handlers */ 96 int sc_ipl; 97 void *sc_ih; /* out interrupt cookie */ 98 int sc_refcnt; /* number of interrupt refs */ 99 struct bus_space_tag sc_tag; /* bus space tag */ 100 }; 101 102 static int frodomatch(device_t, cfdata_t, void *); 103 static void frodoattach(device_t, device_t, void *); 104 105 static int frodoprint(void *, const char *); 106 static int frodosubmatch(device_t, cfdata_t, const int *, void *); 107 108 static int frodointr(void *); 109 110 static void frodo_imask(struct frodo_softc *, uint16_t, uint16_t); 111 112 CFATTACH_DECL_NEW(frodo, sizeof(struct frodo_softc), 113 frodomatch, frodoattach, NULL, NULL); 114 115 static const struct frodo_device frodo_subdevs[] = { 116 { "dnkbd", FRODO_APCI_OFFSET(0), FRODO_INTR_APCI0 }, 117 { "com", FRODO_APCI_OFFSET(1), FRODO_INTR_APCI1 }, 118 { "com", FRODO_APCI_OFFSET(2), FRODO_INTR_APCI2 }, 119 { "com", FRODO_APCI_OFFSET(3), FRODO_INTR_APCI3 }, 120 { NULL, 0, 0 } 121 }; 122 123 static int 124 frodomatch(device_t parent, cfdata_t cf, void *aux) 125 { 126 struct intio_attach_args *ia = aux; 127 static int frodo_matched = 0; 128 129 /* only allow one instance */ 130 if (frodo_matched) 131 return 0; 132 133 if (strcmp(ia->ia_modname, "frodo") != 0) 134 return 0; 135 136 if (badaddr((void *)ia->ia_addr)) 137 return 0; 138 139 frodo_matched = 1; 140 return 1; 141 } 142 143 static void 144 frodoattach(device_t parent, device_t self, void *aux) 145 { 146 struct frodo_softc *sc = device_private(self); 147 struct intio_attach_args *ia = aux; 148 bus_space_tag_t bst = &sc->sc_tag; 149 const struct frodo_device *fd; 150 struct frodo_attach_args fa; 151 152 sc->sc_dev = self; 153 sc->sc_regs = (volatile uint8_t *)ia->ia_addr; 154 sc->sc_ipl = ia->ia_ipl; 155 156 if ((FRODO_READ(sc, FRODO_IISR) & FRODO_IISR_SERVICE) == 0) 157 aprint_error(": service mode enabled"); 158 aprint_normal("\n"); 159 160 /* Initialize bus_space_tag_t for frodo */ 161 frodo_init_bus_space(bst); 162 163 /* Clear all of the interrupt handlers. */ 164 memset(sc->sc_intr, 0, sizeof(sc->sc_intr)); 165 sc->sc_refcnt = 0; 166 167 /* 168 * Disable all of the interrupt lines; we reenable them 169 * as subdevices attach. 170 */ 171 frodo_imask(sc, 0, 0xffff); 172 173 /* Clear any pending interrupts. */ 174 FRODO_WRITE(sc, FRODO_PIC_PU, 0xff); 175 FRODO_WRITE(sc, FRODO_PIC_PL, 0xff); 176 177 /* Set interrupt polarities. */ 178 FRODO_WRITE(sc, FRODO_PIO_IPR, 0x10); 179 180 /* ...and configure for edge triggering. */ 181 FRODO_WRITE(sc, FRODO_PIO_IELR, 0xcf); 182 183 /* 184 * We defer hooking up our interrupt handler until 185 * a subdevice hooks up theirs. 186 */ 187 sc->sc_ih = NULL; 188 189 /* ... and attach subdevices. */ 190 for (fd = frodo_subdevs; fd->fd_name != NULL; fd++) { 191 /* 192 * Skip the first serial port if we're not a 425e; 193 * it's mapped to the DCA at select code 9 on all 194 * other models. 195 */ 196 if (fd->fd_offset == FRODO_APCI_OFFSET(1) && 197 mmuid != MMUID_425_E) 198 continue; 199 fa.fa_name = fd->fd_name; 200 fa.fa_bst = bst; 201 fa.fa_base = ia->ia_iobase; 202 fa.fa_offset = fd->fd_offset; 203 fa.fa_line = fd->fd_line; 204 config_found_sm_loc(self, "frodo", NULL, &fa, frodoprint, 205 frodosubmatch); 206 } 207 } 208 209 static int 210 frodosubmatch(device_t parent, cfdata_t cf, const int *ldesc, void *aux) 211 { 212 struct frodo_attach_args *fa = aux; 213 214 if (cf->frodocf_offset != FRODO_UNKNOWN_OFFSET && 215 cf->frodocf_offset != fa->fa_offset) 216 return 0; 217 218 return config_match(parent, cf, aux); 219 } 220 221 static int 222 frodoprint(void *aux, const char *pnp) 223 { 224 struct frodo_attach_args *fa = aux; 225 226 if (pnp) 227 aprint_normal("%s at %s", fa->fa_name, pnp); 228 aprint_normal(" offset 0x%x", fa->fa_offset); 229 return UNCONF; 230 } 231 232 void 233 frodo_intr_establish(device_t frdev, int (*func)(void *), void *arg, 234 int line, int priority) 235 { 236 struct frodo_softc *sc = device_private(frdev); 237 struct hp300_intrhand *ih = sc->sc_ih; 238 239 if (line < 0 || line >= FRODO_NINTR) { 240 aprint_error_dev(frdev, "bad interrupt line %d\n", line); 241 goto lose; 242 } 243 if (sc->sc_intr[line].ih_fn != NULL) { 244 aprint_error_dev(frdev, "interrupt line %d already used\n", 245 line); 246 goto lose; 247 } 248 249 /* Install the handler. */ 250 sc->sc_intr[line].ih_fn = func; 251 sc->sc_intr[line].ih_arg = arg; 252 sc->sc_intr[line].ih_priority = priority; 253 254 /* 255 * If this is the first one, establish the frodo 256 * interrupt handler. If not, reestablish at a 257 * higher priority if necessary. 258 */ 259 if (ih == NULL || ih->ih_priority < priority) { 260 if (ih != NULL) 261 intr_disestablish(ih); 262 sc->sc_ih = intr_establish(frodointr, sc, sc->sc_ipl, priority); 263 } 264 265 sc->sc_refcnt++; 266 267 /* Enable the interrupt line. */ 268 frodo_imask(sc, (1 << line), 0); 269 return; 270 lose: 271 panic("frodo_intr_establish"); 272 } 273 274 void 275 frodo_intr_disestablish(device_t frdev, int line) 276 { 277 struct frodo_softc *sc = device_private(frdev); 278 struct hp300_intrhand *ih = sc->sc_ih; 279 int newpri; 280 281 if (sc->sc_intr[line].ih_fn == NULL) { 282 printf("%s: no handler for line %d\n", 283 device_xname(sc->sc_dev), line); 284 panic("frodo_intr_disestablish"); 285 } 286 287 sc->sc_intr[line].ih_fn = NULL; 288 frodo_imask(sc, 0, (1 << line)); 289 290 /* If this was the last, unhook ourselves. */ 291 if (sc->sc_refcnt-- == 1) { 292 intr_disestablish(ih); 293 return; 294 } 295 296 /* Lower our priority, if appropriate. */ 297 for (newpri = 0, line = 0; line < FRODO_NINTR; line++) 298 if (sc->sc_intr[line].ih_fn != NULL && 299 sc->sc_intr[line].ih_priority > newpri) 300 newpri = sc->sc_intr[line].ih_priority; 301 302 if (newpri != ih->ih_priority) { 303 intr_disestablish(ih); 304 sc->sc_ih = intr_establish(frodointr, sc, sc->sc_ipl, newpri); 305 } 306 } 307 308 static int 309 frodointr(void *arg) 310 { 311 struct frodo_softc *sc = arg; 312 struct frodo_interhand *fih; 313 int line, taken = 0; 314 315 /* Any interrupts pending? */ 316 if (FRODO_GETPEND(sc) == 0) 317 return 0; 318 319 do { 320 /* 321 * Get pending interrupt; this also clears it for us. 322 */ 323 line = FRODO_IPEND(sc); 324 fih = &sc->sc_intr[line]; 325 if (fih->ih_fn == NULL || 326 (*fih->ih_fn)(fih->ih_arg) == 0) 327 printf("%s: spurious interrupt on line %d\n", 328 device_xname(sc->sc_dev), line); 329 if (taken++ > 100) 330 panic("frodointr: looping!"); 331 } while (FRODO_GETPEND(sc) != 0); 332 333 return 1; 334 } 335 336 static void 337 frodo_imask(struct frodo_softc *sc, uint16_t set, uint16_t clear) 338 { 339 uint16_t imask; 340 341 imask = FRODO_GETMASK(sc); 342 343 imask |= set; 344 imask &= ~clear; 345 346 FRODO_SETMASK(sc, imask); 347 } 348 349 /* 350 * frodo chip specific bus_space(9) support functions. 351 */ 352 static uint8_t frodo_bus_space_read_sparse_1(bus_space_tag_t, 353 bus_space_handle_t, bus_size_t); 354 static void frodo_bus_space_write_sparse_1(bus_space_tag_t, 355 bus_space_handle_t, bus_size_t, uint8_t); 356 357 static void frodo_bus_space_read_multi_sparse_1(bus_space_tag_t, 358 bus_space_handle_t, bus_size_t, uint8_t *, bus_size_t); 359 static void frodo_bus_space_write_multi_sparse_1(bus_space_tag_t, 360 bus_space_handle_t, bus_size_t, const uint8_t *, bus_size_t); 361 362 static void frodo_bus_space_read_region_sparse_1(bus_space_tag_t, 363 bus_space_handle_t, bus_size_t, uint8_t *, bus_size_t); 364 static void frodo_bus_space_write_region_sparse_1(bus_space_tag_t, 365 bus_space_handle_t, bus_size_t, const uint8_t *, bus_size_t); 366 367 static void frodo_bus_space_set_multi_sparse_1(bus_space_tag_t, 368 bus_space_handle_t, bus_size_t, uint8_t, bus_size_t); 369 370 static void frodo_bus_space_set_region_sparse_1(bus_space_tag_t, 371 bus_space_handle_t, bus_size_t, uint8_t, bus_size_t); 372 373 /* 374 * frodo_init_bus_space(): 375 * Initialize bus_space functions in bus_space_tag_t 376 * for frodo devices which have sparse address space. 377 */ 378 void 379 frodo_init_bus_space(bus_space_tag_t bst) 380 { 381 382 memset(bst, 0, sizeof(struct bus_space_tag)); 383 bst->bustype = HP300_BUS_SPACE_INTIO; 384 385 bst->bsr1 = frodo_bus_space_read_sparse_1; 386 bst->bsw1 = frodo_bus_space_write_sparse_1; 387 388 bst->bsrm1 = frodo_bus_space_read_multi_sparse_1; 389 bst->bswm1 = frodo_bus_space_write_multi_sparse_1; 390 391 bst->bsrr1 = frodo_bus_space_read_region_sparse_1; 392 bst->bswr1 = frodo_bus_space_write_region_sparse_1; 393 394 bst->bssm1 = frodo_bus_space_set_multi_sparse_1; 395 396 bst->bssr1 = frodo_bus_space_set_region_sparse_1; 397 } 398 399 static uint8_t 400 frodo_bus_space_read_sparse_1(bus_space_tag_t bst, bus_space_handle_t bsh, 401 bus_size_t offset) 402 { 403 404 return *(volatile uint8_t *)(bsh + (offset << 2)); 405 } 406 407 static void 408 frodo_bus_space_write_sparse_1(bus_space_tag_t bst, bus_space_handle_t bsh, 409 bus_size_t offset, uint8_t val) 410 { 411 412 *(volatile uint8_t *)(bsh + (offset << 2)) = val; 413 } 414 415 static void 416 frodo_bus_space_read_multi_sparse_1(bus_space_tag_t bst, bus_space_handle_t bsh, 417 bus_size_t offset, uint8_t *addr, bus_size_t len) 418 { 419 420 __asm volatile ( 421 " movl %0,%%a0 ;\n" 422 " movl %1,%%a1 ;\n" 423 " movl %2,%%d0 ;\n" 424 "1: movb %%a0@,%%a1@+ ;\n" 425 " subql #1,%%d0 ;\n" 426 " jne 1b" 427 : 428 : "r" (bsh + (offset << 2)), "g" (addr), "g" (len) 429 : "%a0","%a1","%d0"); 430 } 431 432 static void 433 frodo_bus_space_write_multi_sparse_1(bus_space_tag_t bst, 434 bus_space_handle_t bsh, bus_size_t offset, const uint8_t *addr, 435 bus_size_t len) 436 { 437 438 __asm volatile ( 439 " movl %0,%%a0 ;\n" 440 " movl %1,%%a1 ;\n" 441 " movl %2,%%d0 ;\n" 442 "1: movb %%a1@+,%%a0@ ;\n" 443 " subql #1,%%d0 ;\n" 444 " jne 1b" 445 : 446 : "r" (bsh + (offset << 2)), "g" (addr), "g" (len) 447 : "%a0","%a1","%d0"); 448 } 449 450 static void 451 frodo_bus_space_read_region_sparse_1(bus_space_tag_t bst, 452 bus_space_handle_t bsh, bus_size_t offset, uint8_t *addr, bus_size_t len) 453 { 454 __asm volatile ( 455 " movl %0,%%a0 ;\n" 456 " movl %1,%%a1 ;\n" 457 " movl %2,%%d0 ;\n" 458 "1: movb %%a0@,%%a1@+ ;\n" 459 " addql #4,%%a0 ;\n" 460 " subql #1,%%d0 ;\n" 461 " jne 1b" 462 : 463 : "r" (bsh + (offset << 2)), "g" (addr), "g" (len) 464 : "%a0","%a1","%d0"); 465 } 466 467 static void 468 frodo_bus_space_write_region_sparse_1(bus_space_tag_t bst, 469 bus_space_handle_t bsh, bus_size_t offset, const uint8_t *addr, 470 bus_size_t len) 471 { 472 473 __asm volatile ( 474 " movl %0,%%a0 ;\n" 475 " movl %1,%%a1 ;\n" 476 " movl %2,%%d0 ;\n" 477 "1: movb %%a1@+,%%a0@ ;\n" 478 " addql #4,%%a0 ;\n" 479 " subql #1,%%d0 ;\n" 480 " jne 1b" 481 : 482 : "r" (bsh + (offset << 2)), "g" (addr), "g" (len) 483 : "%a0","%a1","%d0"); 484 } 485 486 static void 487 frodo_bus_space_set_multi_sparse_1(bus_space_tag_t bst, bus_space_handle_t bsh, 488 bus_size_t offset, uint8_t val, bus_size_t count) 489 { 490 __asm volatile ( 491 " movl %0,%%a0 ;\n" 492 " movl %1,%%d1 ;\n" 493 " movl %2,%%d0 ;\n" 494 "1: movb %%d1,%%a0@ ;\n" 495 " subql #1,%%d0 ;\n" 496 " jne 1b" 497 : 498 : "r" (bsh + (offset << 2)), "g" (val), "g" (count) 499 : "%a0","%d0","%d1"); 500 } 501 502 static void 503 frodo_bus_space_set_region_sparse_1(bus_space_tag_t bst, bus_space_handle_t bsh, 504 bus_size_t offset, uint8_t val, bus_size_t count) 505 { 506 507 __asm volatile ( 508 " movl %0,%%a0 ;\n" 509 " movl %1,%%d1 ;\n" 510 " movl %2,%%d0 ;\n" 511 "1: movb %%d1,%%a0@ ;\n" 512 " addql #4,%%a0 ;\n" 513 " subql #1,%%d0 ;\n" 514 " jne 1b" 515 : 516 : "r" (bsh + (offset << 2)), "g" (val), "g" (count) 517 : "%a0","%d0","%d1"); 518 } 519