puc.c revision 119418
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: head/sys/dev/puc/puc.c 119418 2003-08-24 17:55:58Z obrien $"); 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: head/sys/dev/puc/puc.c 119418 2003-08-24 17:55:58Z obrien $"); 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 106struct puc_device { 107 struct resource_list resources; 108 u_int serialfreq; 109}; 110 111static void puc_intr(void *arg); 112 113static int puc_find_free_unit(char *); 114#ifdef PUC_DEBUG 115static void puc_print_resource_list(struct resource_list *); 116#endif 117 118devclass_t puc_devclass; 119 120static int 121puc_port_bar_index(struct puc_softc *sc, int bar) 122{ 123 int i; 124 125 for (i = 0; i < PUC_MAX_BAR; i += 1) { 126 if (!sc->sc_bar_mappings[i].used) 127 break; 128 if (sc->sc_bar_mappings[i].bar == bar) 129 return (i); 130 } 131 sc->sc_bar_mappings[i].bar = bar; 132 sc->sc_bar_mappings[i].used = 1; 133 return (i); 134} 135 136static int 137puc_probe_ilr(struct puc_softc *sc, struct resource *res) 138{ 139 u_char t1, t2; 140 int i; 141 142 switch (sc->sc_desc->ilr_type) { 143 case PUC_ILR_TYPE_DIGI: 144 sc->ilr_st = rman_get_bustag(res); 145 sc->ilr_sh = rman_get_bushandle(res); 146 for (i = 0; i < 2 && sc->sc_desc->ilr_offset[i] != 0; i++) { 147 t1 = bus_space_read_1(sc->ilr_st, sc->ilr_sh, 148 sc->sc_desc->ilr_offset[i]); 149 t1 = ~t1; 150 bus_space_write_1(sc->ilr_st, sc->ilr_sh, 151 sc->sc_desc->ilr_offset[i], t1); 152 t2 = bus_space_read_1(sc->ilr_st, sc->ilr_sh, 153 sc->sc_desc->ilr_offset[i]); 154 if (t2 == t1) 155 return (0); 156 } 157 return (1); 158 159 default: 160 break; 161 } 162 return (0); 163} 164 165int 166puc_attach(device_t dev, const struct puc_device_description *desc) 167{ 168 char *typestr; 169 int bidx, childunit, i, irq_setup, rid, type; 170 struct puc_softc *sc; 171 struct puc_device *pdev; 172 struct resource *res; 173 struct resource_list_entry *rle; 174 175 sc = (struct puc_softc *)device_get_softc(dev); 176 bzero(sc, sizeof(*sc)); 177 sc->sc_desc = desc; 178 if (sc->sc_desc == NULL) 179 return (ENXIO); 180 181#ifdef PUC_DEBUG 182 bootverbose = 1; 183 184 printf("puc: name: %s\n", sc->sc_desc->name); 185#endif 186 rid = 0; 187 res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 188 RF_ACTIVE | RF_SHAREABLE); 189 if (!res) 190 return (ENXIO); 191 192 sc->irqres = res; 193 sc->irqrid = rid; 194#ifdef PUC_FASTINTR 195 irq_setup = BUS_SETUP_INTR(device_get_parent(dev), dev, res, 196 INTR_TYPE_TTY | INTR_FAST, puc_intr, sc, &sc->intr_cookie); 197 if (irq_setup == 0) 198 sc->fastintr = INTR_FAST; 199 else 200 irq_setup = BUS_SETUP_INTR(device_get_parent(dev), dev, res, 201 INTR_TYPE_TTY, puc_intr, sc, &sc->intr_cookie); 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#endif 206 if (irq_setup != 0) 207 return (ENXIO); 208 209 rid = 0; 210 for (i = 0; PUC_PORT_VALID(sc->sc_desc, i); i++) { 211 if (i > 0 && rid == sc->sc_desc->ports[i].bar) 212 sc->barmuxed = 1; 213 rid = sc->sc_desc->ports[i].bar; 214 bidx = puc_port_bar_index(sc, rid); 215 216 if (sc->sc_bar_mappings[bidx].res != NULL) 217 continue; 218 219 type = (sc->sc_desc->ports[i].flags & PUC_FLAGS_MEMORY) 220 ? SYS_RES_MEMORY : SYS_RES_IOPORT; 221 222 res = bus_alloc_resource(dev, type, &rid, 0ul, ~0ul, 1, 223 RF_ACTIVE); 224 if (res == NULL) { 225 printf("could not get resource\n"); 226 continue; 227 } 228 sc->sc_bar_mappings[bidx].type = type; 229 sc->sc_bar_mappings[bidx].res = res; 230 231 if (sc->sc_desc->ilr_type != PUC_ILR_TYPE_NONE) { 232 sc->ilr_enabled = puc_probe_ilr(sc, res); 233 if (sc->ilr_enabled) 234 device_printf(dev, "ILR enabled\n"); 235 else 236 device_printf(dev, "ILR disabled\n"); 237 } 238#ifdef PUC_DEBUG 239 printf("%s rid %d bst %x, start %x, end %x\n", 240 (type == SYS_RES_MEMORY) ? "memory" : "port", rid, 241 (u_int)rman_get_bustag(res), (u_int)rman_get_start(res), 242 (u_int)rman_get_end(res)); 243#endif 244 } 245 246 if (desc->init != NULL) { 247 i = desc->init(sc); 248 if (i != 0) 249 return (i); 250 } 251 252 for (i = 0; PUC_PORT_VALID(sc->sc_desc, i); i++) { 253 rid = sc->sc_desc->ports[i].bar; 254 bidx = puc_port_bar_index(sc, rid); 255 if (sc->sc_bar_mappings[bidx].res == NULL) 256 continue; 257 258 switch (sc->sc_desc->ports[i].type) { 259 case PUC_PORT_TYPE_COM: 260 typestr = "sio"; 261 break; 262 case PUC_PORT_TYPE_LPT: 263 typestr = "ppc"; 264 break; 265 default: 266 continue; 267 } 268 pdev = malloc(sizeof(struct puc_device), M_DEVBUF, 269 M_NOWAIT | M_ZERO); 270 if (!pdev) 271 continue; 272 resource_list_init(&pdev->resources); 273 274 /* First fake up an IRQ resource. */ 275 resource_list_add(&pdev->resources, SYS_RES_IRQ, 0, 276 rman_get_start(sc->irqres), rman_get_end(sc->irqres), 277 rman_get_end(sc->irqres) - rman_get_start(sc->irqres) + 1); 278 rle = resource_list_find(&pdev->resources, SYS_RES_IRQ, 0); 279 rle->res = sc->irqres; 280 281 /* Now fake an IOPORT or MEMORY resource */ 282 res = sc->sc_bar_mappings[bidx].res; 283 type = sc->sc_bar_mappings[bidx].type; 284 resource_list_add(&pdev->resources, type, 0, 285 rman_get_start(res) + sc->sc_desc->ports[i].offset, 286 rman_get_start(res) + sc->sc_desc->ports[i].offset + 8 - 1, 287 8); 288 rle = resource_list_find(&pdev->resources, type, 0); 289 290 if (sc->barmuxed == 0) { 291 rle->res = sc->sc_bar_mappings[bidx].res; 292 } else { 293 rle->res = malloc(sizeof(struct resource), M_DEVBUF, 294 M_WAITOK | M_ZERO); 295 if (rle->res == NULL) { 296 free(pdev, M_DEVBUF); 297 return (ENOMEM); 298 } 299 300 rle->res->r_start = rman_get_start(res) + 301 sc->sc_desc->ports[i].offset; 302 rle->res->r_end = rle->res->r_start + 8 - 1; 303 rle->res->r_bustag = rman_get_bustag(res); 304 bus_space_subregion(rle->res->r_bustag, 305 rman_get_bushandle(res), 306 sc->sc_desc->ports[i].offset, 8, 307 &rle->res->r_bushandle); 308 } 309 310 pdev->serialfreq = sc->sc_desc->ports[i].serialfreq; 311 312 childunit = puc_find_free_unit(typestr); 313 sc->sc_ports[i].dev = device_add_child(dev, typestr, childunit); 314 if (sc->sc_ports[i].dev == NULL) { 315 if (sc->barmuxed) { 316 bus_space_unmap(rman_get_bustag(rle->res), 317 rman_get_bushandle(rle->res), 8); 318 free(rle->res, M_DEVBUF); 319 free(pdev, M_DEVBUF); 320 } 321 continue; 322 } 323 device_set_ivars(sc->sc_ports[i].dev, pdev); 324 device_set_desc(sc->sc_ports[i].dev, sc->sc_desc->name); 325 if (!bootverbose) 326 device_quiet(sc->sc_ports[i].dev); 327#ifdef PUC_DEBUG 328 printf("puc: type %d, bar %x, offset %x\n", 329 sc->sc_desc->ports[i].type, 330 sc->sc_desc->ports[i].bar, 331 sc->sc_desc->ports[i].offset); 332 puc_print_resource_list(&pdev->resources); 333#endif 334 device_set_flags(sc->sc_ports[i].dev, 335 sc->sc_desc->ports[i].flags); 336 if (device_probe_and_attach(sc->sc_ports[i].dev) != 0) { 337 if (sc->barmuxed) { 338 bus_space_unmap(rman_get_bustag(rle->res), 339 rman_get_bushandle(rle->res), 340 8); 341 free(rle->res, M_DEVBUF); 342 free(pdev, M_DEVBUF); 343 } 344 } 345 } 346 347#ifdef PUC_DEBUG 348 bootverbose = 0; 349#endif 350 return (0); 351} 352 353static u_int32_t 354puc_ilr_read(struct puc_softc *sc) 355{ 356 u_int32_t mask; 357 int i; 358 359 mask = 0; 360 switch (sc->sc_desc->ilr_type) { 361 case PUC_ILR_TYPE_DIGI: 362 for (i = 1; i >= 0 && sc->sc_desc->ilr_offset[i] != 0; i--) { 363 mask = (mask << 8) | (bus_space_read_1(sc->ilr_st, 364 sc->ilr_sh, sc->sc_desc->ilr_offset[i]) & 0xff); 365 } 366 break; 367 368 default: 369 mask = 0xffffffff; 370 break; 371 } 372 return (mask); 373} 374 375/* 376 * This is an interrupt handler. For boards that can't tell us which 377 * device generated the interrupt it just calls all the registered 378 * handlers sequencially, but for boards that can tell us which 379 * device(s) generated the interrupt it calls only handlers for devices 380 * that actually generated the interrupt. 381 */ 382static void 383puc_intr(void *arg) 384{ 385 int i; 386 u_int32_t ilr_mask; 387 struct puc_softc *sc; 388 389 sc = (struct puc_softc *)arg; 390 ilr_mask = sc->ilr_enabled ? puc_ilr_read(sc) : 0xffffffff; 391 for (i = 0; i < PUC_MAX_PORTS; i++) 392 if (sc->sc_ports[i].ihand != NULL && 393 ((ilr_mask >> i) & 0x00000001)) 394 (sc->sc_ports[i].ihand)(sc->sc_ports[i].ihandarg); 395} 396 397const struct puc_device_description * 398puc_find_description(uint32_t vend, uint32_t prod, uint32_t svend, 399 uint32_t sprod) 400{ 401 int i; 402 403#define checkreg(val, index) \ 404 (((val) & puc_devices[i].rmask[(index)]) == puc_devices[i].rval[(index)]) 405 406 for (i = 0; puc_devices[i].name != NULL; i++) { 407 if (checkreg(vend, PUC_REG_VEND) && 408 checkreg(prod, PUC_REG_PROD) && 409 checkreg(svend, PUC_REG_SVEND) && 410 checkreg(sprod, PUC_REG_SPROD)) 411 return (&puc_devices[i]); 412 } 413 414#undef checkreg 415 416 return (NULL); 417} 418 419static int 420puc_find_free_unit(char *name) 421{ 422 devclass_t dc; 423 int start; 424 int unit; 425 426 unit = 0; 427 start = 0; 428 while (resource_int_value(name, unit, "port", &start) == 0 && 429 start > 0) 430 unit++; 431 dc = devclass_find(name); 432 if (dc == NULL) 433 return (-1); 434 while (devclass_get_device(dc, unit)) 435 unit++; 436#ifdef PUC_DEBUG 437 printf("puc: Using %s%d\n", name, unit); 438#endif 439 return (unit); 440} 441 442#ifdef PUC_DEBUG 443static void 444puc_print_resource_list(struct resource_list *rl) 445{ 446#if 0 447 struct resource_list_entry *rle; 448 449 printf("print_resource_list: rl %p\n", rl); 450 SLIST_FOREACH(rle, rl, link) 451 printf(" type %x, rid %x start %x end %x count %x\n", 452 rle->type, rle->rid, rle->start, rle->end, rle->count); 453 printf("print_resource_list: end.\n"); 454#endif 455} 456#endif 457 458struct resource * 459puc_alloc_resource(device_t dev, device_t child, int type, int *rid, 460 u_long start, u_long end, u_long count, u_int flags) 461{ 462 struct puc_device *pdev; 463 struct resource *retval; 464 struct resource_list *rl; 465 struct resource_list_entry *rle; 466 device_t my_child; 467 468 /* 469 * in the case of a child of child we need to find our immediate child 470 */ 471 for (my_child = child; device_get_parent(my_child) != dev; 472 my_child = device_get_parent(my_child)); 473 474 pdev = device_get_ivars(my_child); 475 rl = &pdev->resources; 476 477#ifdef PUC_DEBUG 478 printf("puc_alloc_resource: pdev %p, looking for t %x, r %x\n", 479 pdev, type, *rid); 480 puc_print_resource_list(rl); 481#endif 482 retval = NULL; 483 rle = resource_list_find(rl, type, *rid); 484 if (rle) { 485 start = rle->start; 486 end = rle->end; 487 count = rle->count; 488#ifdef PUC_DEBUG 489 printf("found rle, %lx, %lx, %lx\n", start, end, count); 490#endif 491 retval = rle->res; 492 } 493#ifdef PUC_DEBUG 494 else 495 printf("oops rle is gone\n"); 496#endif 497 498 return (retval); 499} 500 501int 502puc_release_resource(device_t dev, device_t child, int type, int rid, 503 struct resource *res) 504{ 505 return (0); 506} 507 508int 509puc_get_resource(device_t dev, device_t child, int type, int rid, 510 u_long *startp, u_long *countp) 511{ 512 struct puc_device *pdev; 513 struct resource_list *rl; 514 struct resource_list_entry *rle; 515 516 pdev = device_get_ivars(child); 517 rl = &pdev->resources; 518 519#ifdef PUC_DEBUG 520 printf("puc_get_resource: pdev %p, looking for t %x, r %x\n", pdev, 521 type, rid); 522 puc_print_resource_list(rl); 523#endif 524 rle = resource_list_find(rl, type, rid); 525 if (rle) { 526#ifdef PUC_DEBUG 527 printf("found rle %p,", rle); 528#endif 529 if (startp != NULL) 530 *startp = rle->start; 531 if (countp != NULL) 532 *countp = rle->count; 533#ifdef PUC_DEBUG 534 printf(" %lx, %lx\n", rle->start, rle->count); 535#endif 536 return (0); 537 } else 538 printf("oops rle is gone\n"); 539 return (ENXIO); 540} 541 542int 543puc_setup_intr(device_t dev, device_t child, struct resource *r, int flags, 544 void (*ihand)(void *), void *arg, void **cookiep) 545{ 546 int i; 547 struct puc_softc *sc; 548 549 sc = (struct puc_softc *)device_get_softc(dev); 550 if ((flags & INTR_FAST) != sc->fastintr) 551 return (ENXIO); 552 for (i = 0; PUC_PORT_VALID(sc->sc_desc, i); i++) { 553 if (sc->sc_ports[i].dev == child) { 554 if (sc->sc_ports[i].ihand != 0) 555 return (ENXIO); 556 sc->sc_ports[i].ihand = ihand; 557 sc->sc_ports[i].ihandarg = arg; 558 *cookiep = arg; 559 return (0); 560 } 561 } 562 return (ENXIO); 563} 564 565int 566puc_teardown_intr(device_t dev, device_t child, struct resource *r, 567 void *cookie) 568{ 569 int i; 570 struct puc_softc *sc; 571 572 sc = (struct puc_softc *)device_get_softc(dev); 573 for (i = 0; PUC_PORT_VALID(sc->sc_desc, i); i++) { 574 if (sc->sc_ports[i].dev == child) { 575 sc->sc_ports[i].ihand = NULL; 576 sc->sc_ports[i].ihandarg = NULL; 577 return (0); 578 } 579 } 580 return (ENXIO); 581} 582 583int 584puc_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) 585{ 586 struct puc_device *pdev; 587 588 pdev = device_get_ivars(child); 589 if (pdev == NULL) 590 return (ENOENT); 591 592 switch(index) { 593 case PUC_IVAR_FREQ: 594 *result = pdev->serialfreq; 595 break; 596 default: 597 return (ENOENT); 598 } 599 return (0); 600} 601