1 /* $NetBSD: puc.c,v 1.7 2000/07/29 17:43:38 jlam Exp $ */ 2 3 /*- 4 * Copyright (c) 2002 JF Hay. All rights reserved. 5 * Copyright (c) 2000 M. Warner Losh. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 /*- 30 * Copyright (c) 1996, 1998, 1999 31 * Christopher G. Demetriou. All rights reserved. 32 * 33 * Redistribution and use in source and binary forms, with or without 34 * modification, are permitted provided that the following conditions 35 * are met: 36 * 1. Redistributions of source code must retain the above copyright 37 * notice, this list of conditions and the following disclaimer. 38 * 2. Redistributions in binary form must reproduce the above copyright 39 * notice, this list of conditions and the following disclaimer in the 40 * documentation and/or other materials provided with the distribution. 41 * 3. All advertising materials mentioning features or use of this software 42 * must display the following acknowledgement: 43 * This product includes software developed by Christopher G. Demetriou 44 * for the NetBSD Project. 45 * 4. The name of the author may not be used to endorse or promote products 46 * derived from this software without specific prior written permission 47 * 48 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 49 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 50 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 51 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 52 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 53 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 54 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 55 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 56 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 57 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 58 */ 59 60 #include <sys/cdefs.h> 61 __FBSDID("$FreeBSD$"); 62 63 /* 64 * PCI "universal" communication card device driver, glues com, lpt, 65 * and similar ports to PCI via bridge chip often much larger than 66 * the devices being glued. 67 * 68 * Author: Christopher G. Demetriou, May 14, 1998 (derived from NetBSD 69 * sys/dev/pci/pciide.c, revision 1.6). 70 * 71 * These devices could be (and some times are) described as 72 * communications/{serial,parallel}, etc. devices with known 73 * programming interfaces, but those programming interfaces (in 74 * particular the BAR assignments for devices, etc.) in fact are not 75 * particularly well defined. 76 * 77 * After I/we have seen more of these devices, it may be possible 78 * to generalize some of these bits. In particular, devices which 79 * describe themselves as communications/serial/16[45]50, and 80 * communications/parallel/??? might be attached via direct 81 * 'com' and 'lpt' attachments to pci. 82 */ 83 84 #include "opt_puc.h" 85 86 #ifndef PUC_FASTINTR 87 #define PUC_FASTINTR 88 #endif 89 90 #include <sys/param.h> 91 #include <sys/systm.h> 92 #include <sys/kernel.h> 93 #include <sys/bus.h> 94 #include <sys/conf.h> 95 #include <sys/malloc.h> 96 97 #include <machine/bus.h> 98 #include <machine/resource.h> 99 #include <sys/rman.h> 100 101 #include <dev/pci/pcireg.h> 102 #include <dev/pci/pcivar.h> 103 104 #define PUC_ENTRAILS 1 105 #include <dev/puc/pucvar.h> 106 107 struct puc_device { 108 struct resource_list resources; 109 int port; 110 int regshft; 111 u_int serialfreq; 112 u_int subtype; 113 }; 114 115 static void puc_intr(void *arg); 116 117 static int puc_find_free_unit(char *); 118 #ifdef PUC_DEBUG 119 static void puc_print_resource_list(struct resource_list *); 120 #endif 121 122 devclass_t puc_devclass; 123 124 static int 125 puc_port_bar_index(struct puc_softc *sc, int bar) 126 { 127 int i; 128 129 for (i = 0; i < PUC_MAX_BAR; i += 1) { 130 if (!sc->sc_bar_mappings[i].used) 131 break; 132 if (sc->sc_bar_mappings[i].bar == bar) 133 return (i); 134 } 135 if (i == PUC_MAX_BAR) { 136 printf("%s: out of bars!\n", __func__); 137 return (-1); 138 } 139 sc->sc_bar_mappings[i].bar = bar; 140 sc->sc_bar_mappings[i].used = 1; 141 return (i); 142 } 143 144 static int 145 puc_probe_ilr(struct puc_softc *sc, struct resource *res) 146 { 147 u_char t1, t2; 148 int i; 149 150 switch (sc->sc_desc.ilr_type) { 151 case PUC_ILR_TYPE_DIGI: 152 sc->ilr_st = rman_get_bustag(res); 153 sc->ilr_sh = rman_get_bushandle(res); 154 for (i = 0; i < 2 && sc->sc_desc.ilr_offset[i] != 0; i++) { 155 t1 = bus_space_read_1(sc->ilr_st, sc->ilr_sh, 156 sc->sc_desc.ilr_offset[i]); 157 t1 = ~t1; 158 bus_space_write_1(sc->ilr_st, sc->ilr_sh, 159 sc->sc_desc.ilr_offset[i], t1); 160 t2 = bus_space_read_1(sc->ilr_st, sc->ilr_sh, 161 sc->sc_desc.ilr_offset[i]); 162 if (t2 == t1) 163 return (0); 164 } 165 return (1); 166 167 default: 168 break; 169 } 170 return (0); 171 } 172 173 int 174 puc_attach(device_t dev, const struct puc_device_description *desc) 175 { 176 char *typestr; 177 int bidx, childunit, i, irq_setup, ressz, rid, type; 178 struct puc_softc *sc; 179 struct puc_device *pdev; 180 struct resource *res; 181 struct resource_list_entry *rle; 182 bus_space_handle_t bh; 183 184 if (desc == NULL) 185 return (ENXIO); 186 187 sc = (struct puc_softc *)device_get_softc(dev); 188 bzero(sc, sizeof(*sc)); 189 sc->sc_desc = *desc; 190 191 #ifdef PUC_DEBUG 192 bootverbose = 1; 193 194 printf("puc: name: %s\n", sc->sc_desc.name); 195 #endif 196 rid = 0; 197 res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 198 RF_ACTIVE | RF_SHAREABLE); 199 if (!res) 200 return (ENXIO); 201 202 sc->irqres = res; 203 sc->irqrid = rid; 204 #ifdef PUC_FASTINTR 205 irq_setup = BUS_SETUP_INTR(device_get_parent(dev), dev, res, 206 INTR_TYPE_TTY | INTR_FAST, puc_intr, sc, &sc->intr_cookie); 207 if (irq_setup == 0) 208 sc->fastintr = INTR_FAST; 209 else 210 irq_setup = BUS_SETUP_INTR(device_get_parent(dev), dev, res, 211 INTR_TYPE_TTY, puc_intr, sc, &sc->intr_cookie); 212 #else 213 irq_setup = BUS_SETUP_INTR(device_get_parent(dev), dev, res, 214 INTR_TYPE_TTY, puc_intr, sc, &sc->intr_cookie); 215 #endif 216 if (irq_setup != 0) 217 return (ENXIO); 218 219 rid = 0; 220 for (i = 0; PUC_PORT_VALID(sc->sc_desc, i); i++) { 221 if (i > 0 && rid == sc->sc_desc.ports[i].bar) 222 sc->barmuxed = 1; 223 rid = sc->sc_desc.ports[i].bar; 224 bidx = puc_port_bar_index(sc, rid); 225 226 if (bidx < 0 || sc->sc_bar_mappings[bidx].res != NULL) 227 continue; 228 229 type = (sc->sc_desc.ports[i].flags & PUC_FLAGS_MEMORY) 230 ? SYS_RES_MEMORY : SYS_RES_IOPORT; 231 232 res = bus_alloc_resource_any(dev, type, &rid, 233 RF_ACTIVE); 234 if (res == NULL && 235 sc->sc_desc.ports[i].flags & PUC_FLAGS_ALTRES) { 236 type = (type == SYS_RES_IOPORT) 237 ? SYS_RES_MEMORY : SYS_RES_IOPORT; 238 res = bus_alloc_resource_any(dev, type, &rid, 239 RF_ACTIVE); 240 } 241 if (res == NULL) { 242 device_printf(dev, "could not get resource\n"); 243 continue; 244 } 245 sc->sc_bar_mappings[bidx].type = type; 246 sc->sc_bar_mappings[bidx].res = res; 247 248 if (sc->sc_desc.ilr_type != PUC_ILR_TYPE_NONE) { 249 sc->ilr_enabled = puc_probe_ilr(sc, res); 250 if (sc->ilr_enabled) 251 device_printf(dev, "ILR enabled\n"); 252 else 253 device_printf(dev, "ILR disabled\n"); 254 } 255 #ifdef PUC_DEBUG 256 printf("%s rid %d bst %lx, start %lx, end %lx\n", 257 (type == SYS_RES_MEMORY) ? "memory" : "port", rid, 258 (u_long)rman_get_bustag(res), (u_long)rman_get_start(res), 259 (u_long)rman_get_end(res)); 260 #endif 261 } 262 263 if (desc->init != NULL) { 264 i = desc->init(sc); 265 if (i != 0) 266 return (i); 267 } 268 269 for (i = 0; PUC_PORT_VALID(sc->sc_desc, i); i++) { 270 rid = sc->sc_desc.ports[i].bar; 271 bidx = puc_port_bar_index(sc, rid); 272 if (bidx < 0 || sc->sc_bar_mappings[bidx].res == NULL) 273 continue; 274 275 switch (sc->sc_desc.ports[i].type & ~PUC_PORT_SUBTYPE_MASK) { 276 case PUC_PORT_TYPE_COM: 277 typestr = "sio"; 278 break; 279 case PUC_PORT_TYPE_LPT: 280 typestr = "ppc"; 281 break; 282 case PUC_PORT_TYPE_UART: 283 typestr = "uart"; 284 break; 285 default: 286 continue; 287 } 288 switch (sc->sc_desc.ports[i].type & PUC_PORT_SUBTYPE_MASK) { 289 case PUC_PORT_UART_SAB82532: 290 ressz = 64; 291 break; 292 case PUC_PORT_UART_Z8530: 293 ressz = 2; 294 break; 295 default: 296 ressz = 8; 297 break; 298 } 299 pdev = malloc(sizeof(struct puc_device), M_DEVBUF, 300 M_NOWAIT | M_ZERO); 301 if (!pdev) 302 continue; 303 resource_list_init(&pdev->resources); 304 305 /* First fake up an IRQ resource. */ 306 resource_list_add(&pdev->resources, SYS_RES_IRQ, 0, 307 rman_get_start(sc->irqres), rman_get_end(sc->irqres), 308 rman_get_end(sc->irqres) - rman_get_start(sc->irqres) + 1); 309 rle = resource_list_find(&pdev->resources, SYS_RES_IRQ, 0); 310 rle->res = sc->irqres; 311 312 /* Now fake an IOPORT or MEMORY resource */ 313 res = sc->sc_bar_mappings[bidx].res; 314 type = sc->sc_bar_mappings[bidx].type; 315 resource_list_add(&pdev->resources, type, 0, 316 rman_get_start(res) + sc->sc_desc.ports[i].offset, 317 rman_get_start(res) + sc->sc_desc.ports[i].offset 318 + ressz - 1, ressz); 319 rle = resource_list_find(&pdev->resources, type, 0); 320 321 if (sc->barmuxed == 0) { 322 rle->res = sc->sc_bar_mappings[bidx].res; 323 } else { 324 rle->res = rman_secret_puc_alloc_resource(M_WAITOK); 325 if (rle->res == NULL) { 326 free(pdev, M_DEVBUF); 327 return (ENOMEM); 328 } 329 330 rman_set_start(rle->res, rman_get_start(res) + 331 sc->sc_desc.ports[i].offset); 332 rman_set_end(rle->res, rman_get_start(rle->res) + 333 ressz - 1); 334 rman_set_bustag(rle->res, rman_get_bustag(res)); 335 bus_space_subregion(rman_get_bustag(rle->res), 336 rman_get_bushandle(res), 337 sc->sc_desc.ports[i].offset, ressz, 338 &bh); 339 rman_set_bushandle(rle->res, bh); 340 } 341 342 pdev->port = i + 1; 343 pdev->serialfreq = sc->sc_desc.ports[i].serialfreq; 344 pdev->subtype = sc->sc_desc.ports[i].type & 345 PUC_PORT_SUBTYPE_MASK; 346 pdev->regshft = sc->sc_desc.ports[i].regshft; 347 348 childunit = puc_find_free_unit(typestr); 349 if (childunit < 0 && strcmp(typestr, "uart") != 0) { 350 typestr = "uart"; 351 childunit = puc_find_free_unit(typestr); 352 } 353 sc->sc_ports[i].dev = device_add_child(dev, typestr, 354 childunit); 355 if (sc->sc_ports[i].dev == NULL) { 356 if (sc->barmuxed) { 357 bus_space_unmap(rman_get_bustag(rle->res), 358 rman_get_bushandle(rle->res), ressz); 359 rman_secret_puc_free_resource(rle->res); 360 free(pdev, M_DEVBUF); 361 } 362 continue; 363 } 364 device_set_ivars(sc->sc_ports[i].dev, pdev); 365 device_set_desc(sc->sc_ports[i].dev, sc->sc_desc.name); 366 #ifdef PUC_DEBUG 367 printf("puc: type %d, bar %x, offset %x\n", 368 sc->sc_desc.ports[i].type, 369 sc->sc_desc.ports[i].bar, 370 sc->sc_desc.ports[i].offset); 371 puc_print_resource_list(&pdev->resources); 372 #endif 373 device_set_flags(sc->sc_ports[i].dev, 374 sc->sc_desc.ports[i].flags); 375 if (device_probe_and_attach(sc->sc_ports[i].dev) != 0) { 376 if (sc->barmuxed) { 377 bus_space_unmap(rman_get_bustag(rle->res), 378 rman_get_bushandle(rle->res), ressz); 379 rman_secret_puc_free_resource(rle->res); 380 free(pdev, M_DEVBUF); 381 } 382 } 383 } 384 385 #ifdef PUC_DEBUG 386 bootverbose = 0; 387 #endif 388 return (0); 389 } 390 391 static u_int32_t 392 puc_ilr_read(struct puc_softc *sc) 393 { 394 u_int32_t mask; 395 int i; 396 397 mask = 0; 398 switch (sc->sc_desc.ilr_type) { 399 case PUC_ILR_TYPE_DIGI: 400 for (i = 1; i >= 0 && sc->sc_desc.ilr_offset[i] != 0; i--) { 401 mask = (mask << 8) | (bus_space_read_1(sc->ilr_st, 402 sc->ilr_sh, sc->sc_desc.ilr_offset[i]) & 0xff); 403 } 404 break; 405 406 default: 407 mask = 0xffffffff; 408 break; 409 } 410 return (mask); 411 } 412 413 /* 414 * This is an interrupt handler. For boards that can't tell us which 415 * device generated the interrupt it just calls all the registered 416 * handlers sequencially, but for boards that can tell us which 417 * device(s) generated the interrupt it calls only handlers for devices 418 * that actually generated the interrupt. 419 */ 420 static void 421 puc_intr(void *arg) 422 { 423 int i; 424 u_int32_t ilr_mask; 425 struct puc_softc *sc; 426 427 sc = (struct puc_softc *)arg; 428 ilr_mask = sc->ilr_enabled ? puc_ilr_read(sc) : 0xffffffff; 429 for (i = 0; i < PUC_MAX_PORTS; i++) 430 if (sc->sc_ports[i].ihand != NULL && 431 ((ilr_mask >> i) & 0x00000001)) 432 (sc->sc_ports[i].ihand)(sc->sc_ports[i].ihandarg); 433 } 434 435 static int 436 puc_find_free_unit(char *name) 437 { 438 devclass_t dc; 439 int start; 440 int unit; 441 442 unit = 0; 443 start = 0; 444 while (resource_int_value(name, unit, "port", &start) == 0 && 445 start > 0) 446 unit++; 447 dc = devclass_find(name); 448 if (dc == NULL) 449 return (-1); 450 while (devclass_get_device(dc, unit)) 451 unit++; 452 #ifdef PUC_DEBUG 453 printf("puc: Using %s%d\n", name, unit); 454 #endif 455 return (unit); 456 } 457 458 #ifdef PUC_DEBUG 459 static void 460 puc_print_resource_list(struct resource_list *rl) 461 { 462 #if 0 463 struct resource_list_entry *rle; 464 465 printf("print_resource_list: rl %p\n", rl); 466 SLIST_FOREACH(rle, rl, link) 467 printf(" type %x, rid %x start %lx end %lx count %lx\n", 468 rle->type, rle->rid, rle->start, rle->end, rle->count); 469 printf("print_resource_list: end.\n"); 470 #endif 471 } 472 #endif 473 474 struct resource * 475 puc_alloc_resource(device_t dev, device_t child, int type, int *rid, 476 u_long start, u_long end, u_long count, u_int flags) 477 { 478 struct puc_device *pdev; 479 struct resource *retval; 480 struct resource_list *rl; 481 struct resource_list_entry *rle; 482 device_t my_child; 483 484 /* 485 * in the case of a child of child we need to find our immediate child 486 */ 487 for (my_child = child; device_get_parent(my_child) != dev; 488 my_child = device_get_parent(my_child)); 489 490 pdev = device_get_ivars(my_child); 491 rl = &pdev->resources; 492 493 #ifdef PUC_DEBUG 494 printf("puc_alloc_resource: pdev %p, looking for t %x, r %x\n", 495 pdev, type, *rid); 496 puc_print_resource_list(rl); 497 #endif 498 retval = NULL; 499 rle = resource_list_find(rl, type, *rid); 500 if (rle) { 501 #ifdef PUC_DEBUG 502 printf("found rle, %lx, %lx, %lx\n", rle->start, rle->end, 503 rle->count); 504 #endif 505 retval = rle->res; 506 } 507 #ifdef PUC_DEBUG 508 else 509 printf("oops rle is gone\n"); 510 #endif 511 512 return (retval); 513 } 514 515 int 516 puc_release_resource(device_t dev, device_t child, int type, int rid, 517 struct resource *res) 518 { 519 return (0); 520 } 521 522 int 523 puc_get_resource(device_t dev, device_t child, int type, int rid, 524 u_long *startp, u_long *countp) 525 { 526 struct puc_device *pdev; 527 struct resource_list *rl; 528 struct resource_list_entry *rle; 529 530 pdev = device_get_ivars(child); 531 rl = &pdev->resources; 532 533 #ifdef PUC_DEBUG 534 printf("puc_get_resource: pdev %p, looking for t %x, r %x\n", pdev, 535 type, rid); 536 puc_print_resource_list(rl); 537 #endif 538 rle = resource_list_find(rl, type, rid); 539 if (rle) { 540 #ifdef PUC_DEBUG 541 printf("found rle %p,", rle); 542 #endif 543 if (startp != NULL) 544 *startp = rle->start; 545 if (countp != NULL) 546 *countp = rle->count; 547 #ifdef PUC_DEBUG 548 printf(" %lx, %lx\n", rle->start, rle->count); 549 #endif 550 return (0); 551 } else 552 printf("oops rle is gone\n"); 553 return (ENXIO); 554 } 555 556 int 557 puc_setup_intr(device_t dev, device_t child, struct resource *r, int flags, 558 void (*ihand)(void *), void *arg, void **cookiep) 559 { 560 int i; 561 struct puc_softc *sc; 562 563 sc = (struct puc_softc *)device_get_softc(dev); 564 if ((flags & INTR_FAST) != sc->fastintr) 565 return (ENXIO); 566 for (i = 0; PUC_PORT_VALID(sc->sc_desc, i); i++) { 567 if (sc->sc_ports[i].dev == child) { 568 if (sc->sc_ports[i].ihand != 0) 569 return (ENXIO); 570 sc->sc_ports[i].ihand = ihand; 571 sc->sc_ports[i].ihandarg = arg; 572 *cookiep = arg; 573 return (0); 574 } 575 } 576 return (ENXIO); 577 } 578 579 int 580 puc_teardown_intr(device_t dev, device_t child, struct resource *r, 581 void *cookie) 582 { 583 int i; 584 struct puc_softc *sc; 585 586 sc = (struct puc_softc *)device_get_softc(dev); 587 for (i = 0; PUC_PORT_VALID(sc->sc_desc, i); i++) { 588 if (sc->sc_ports[i].dev == child) { 589 sc->sc_ports[i].ihand = NULL; 590 sc->sc_ports[i].ihandarg = NULL; 591 return (0); 592 } 593 } 594 return (ENXIO); 595 } 596 597 int 598 puc_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) 599 { 600 struct puc_device *pdev; 601 602 pdev = device_get_ivars(child); 603 if (pdev == NULL) 604 return (ENOENT); 605 606 switch(index) { 607 case PUC_IVAR_FREQ: 608 *result = pdev->serialfreq; 609 break; 610 case PUC_IVAR_PORT: 611 *result = pdev->port; 612 break; 613 case PUC_IVAR_REGSHFT: 614 *result = pdev->regshft; 615 break; 616 case PUC_IVAR_SUBTYPE: 617 *result = pdev->subtype; 618 break; 619 default: 620 return (ENOENT); 621 } 622 return (0); 623 } 624