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