puc.c revision 142531
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: head/sys/dev/puc/puc.c 142531 2005-02-26 00:22:52Z marius $"); 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#define __RMAN_RESOURCE_VISIBLE /* Shouldn't be there */ 85#include "opt_puc.h" 86#include <sys/param.h> 87#include <sys/systm.h> 88#include <sys/kernel.h> 89#include <sys/bus.h> 90#include <sys/conf.h> 91#include <sys/malloc.h> 92 93#include <machine/bus.h> 94#include <machine/resource.h> 95#include <sys/rman.h> 96 97#include <dev/pci/pcireg.h> 98#include <dev/pci/pcivar.h> 99 100#ifdef __sparc64__ 101#include <sparc64/fhc/fhcreg.h> 102#endif 103 104#define PUC_ENTRAILS 1 105#include <dev/puc/pucvar.h> 106 107struct puc_device { 108 struct resource_list resources; 109 int port; 110 int regshft; 111 u_int serialfreq; 112 u_int subtype; 113}; 114 115static void puc_intr(void *arg); 116 117static int puc_find_free_unit(char *); 118#ifdef PUC_DEBUG 119static void puc_print_resource_list(struct resource_list *); 120#endif 121 122devclass_t puc_devclass; 123 124static int 125puc_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 144static int 145puc_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 173int 174puc_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 197 rid = 0; 198#ifdef __sparc64__ 199 if (strcmp(device_get_name(device_get_parent(dev)), "fhc") == 0) 200 rid = FHC_UART; 201#endif 202 res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 203 RF_ACTIVE | RF_SHAREABLE); 204 if (!res) 205 return (ENXIO); 206 207 sc->irqres = res; 208 sc->irqrid = rid; 209#ifdef PUC_FASTINTR 210 irq_setup = BUS_SETUP_INTR(device_get_parent(dev), dev, res, 211 INTR_TYPE_TTY | INTR_FAST, puc_intr, sc, &sc->intr_cookie); 212 if (irq_setup == 0) 213 sc->fastintr = INTR_FAST; 214 else 215 irq_setup = BUS_SETUP_INTR(device_get_parent(dev), dev, res, 216 INTR_TYPE_TTY, puc_intr, sc, &sc->intr_cookie); 217#else 218 irq_setup = BUS_SETUP_INTR(device_get_parent(dev), dev, res, 219 INTR_TYPE_TTY, puc_intr, sc, &sc->intr_cookie); 220#endif 221 if (irq_setup != 0) 222 return (ENXIO); 223 224 rid = 0; 225 for (i = 0; PUC_PORT_VALID(sc->sc_desc, i); i++) { 226 if (i > 0 && rid == sc->sc_desc.ports[i].bar) 227 sc->barmuxed = 1; 228 rid = sc->sc_desc.ports[i].bar; 229 bidx = puc_port_bar_index(sc, rid); 230 231 if (bidx < 0 || sc->sc_bar_mappings[bidx].res != NULL) 232 continue; 233 234 type = (sc->sc_desc.ports[i].flags & PUC_FLAGS_MEMORY) 235 ? SYS_RES_MEMORY : SYS_RES_IOPORT; 236 237 res = bus_alloc_resource_any(dev, type, &rid, 238 RF_ACTIVE); 239 if (res == NULL && 240 sc->sc_desc.ports[i].flags & PUC_FLAGS_ALTRES) { 241 type = (type == SYS_RES_IOPORT) 242 ? SYS_RES_MEMORY : SYS_RES_IOPORT; 243 res = bus_alloc_resource_any(dev, type, &rid, 244 RF_ACTIVE); 245 } 246 if (res == NULL) { 247 device_printf(dev, "could not get resource\n"); 248 continue; 249 } 250 sc->sc_bar_mappings[bidx].type = type; 251 sc->sc_bar_mappings[bidx].res = res; 252 253 if (sc->sc_desc.ilr_type != PUC_ILR_TYPE_NONE) { 254 sc->ilr_enabled = puc_probe_ilr(sc, res); 255 if (sc->ilr_enabled) 256 device_printf(dev, "ILR enabled\n"); 257 else 258 device_printf(dev, "ILR disabled\n"); 259 } 260#ifdef PUC_DEBUG 261 printf("%s rid %d bst %lx, start %lx, end %lx\n", 262 (type == SYS_RES_MEMORY) ? "memory" : "port", rid, 263 (u_long)rman_get_bustag(res), (u_long)rman_get_start(res), 264 (u_long)rman_get_end(res)); 265#endif 266 } 267 268 if (desc->init != NULL) { 269 i = desc->init(sc); 270 if (i != 0) 271 return (i); 272 } 273 274 for (i = 0; PUC_PORT_VALID(sc->sc_desc, i); i++) { 275 rid = sc->sc_desc.ports[i].bar; 276 bidx = puc_port_bar_index(sc, rid); 277 if (bidx < 0 || sc->sc_bar_mappings[bidx].res == NULL) 278 continue; 279 280 switch (sc->sc_desc.ports[i].type & ~PUC_PORT_SUBTYPE_MASK) { 281 case PUC_PORT_TYPE_COM: 282 typestr = "sio"; 283 break; 284 case PUC_PORT_TYPE_LPT: 285 typestr = "ppc"; 286 break; 287 case PUC_PORT_TYPE_UART: 288 typestr = "uart"; 289 break; 290 default: 291 continue; 292 } 293 switch (sc->sc_desc.ports[i].type & PUC_PORT_SUBTYPE_MASK) { 294 case PUC_PORT_UART_SAB82532: 295 ressz = 64; 296 break; 297 case PUC_PORT_UART_Z8530: 298 ressz = 2; 299 break; 300 default: 301 ressz = 8; 302 break; 303 } 304 pdev = malloc(sizeof(struct puc_device), M_DEVBUF, 305 M_NOWAIT | M_ZERO); 306 if (!pdev) 307 continue; 308 resource_list_init(&pdev->resources); 309 310 /* First fake up an IRQ resource. */ 311 resource_list_add(&pdev->resources, SYS_RES_IRQ, 0, 312 rman_get_start(sc->irqres), rman_get_end(sc->irqres), 313 rman_get_end(sc->irqres) - rman_get_start(sc->irqres) + 1); 314 rle = resource_list_find(&pdev->resources, SYS_RES_IRQ, 0); 315 rle->res = sc->irqres; 316 317 /* Now fake an IOPORT or MEMORY resource */ 318 res = sc->sc_bar_mappings[bidx].res; 319 type = sc->sc_bar_mappings[bidx].type; 320 resource_list_add(&pdev->resources, type, 0, 321 rman_get_start(res) + sc->sc_desc.ports[i].offset, 322 rman_get_start(res) + sc->sc_desc.ports[i].offset 323 + ressz - 1, ressz); 324 rle = resource_list_find(&pdev->resources, type, 0); 325 326 if (sc->barmuxed == 0) { 327 rle->res = sc->sc_bar_mappings[bidx].res; 328 } else { 329 rle->res = malloc(sizeof(struct resource), M_DEVBUF, 330 M_WAITOK | M_ZERO); 331 if (rle->res == NULL) { 332 free(pdev, M_DEVBUF); 333 return (ENOMEM); 334 } 335 336 rman_set_start(rle->res, rman_get_start(res) + 337 sc->sc_desc.ports[i].offset); 338 rman_set_end(rle->res, rman_get_start(rle->res) + 339 ressz - 1); 340 rman_set_bustag(rle->res, rman_get_bustag(res)); 341 bus_space_subregion(rman_get_bustag(rle->res), 342 rman_get_bushandle(res), 343 sc->sc_desc.ports[i].offset, ressz, 344 &bh); 345 rman_set_bushandle(rle->res, bh); 346 } 347 348 pdev->port = i + 1; 349 pdev->serialfreq = sc->sc_desc.ports[i].serialfreq; 350 pdev->subtype = sc->sc_desc.ports[i].type & 351 PUC_PORT_SUBTYPE_MASK; 352 pdev->regshft = sc->sc_desc.ports[i].regshft; 353 354 childunit = puc_find_free_unit(typestr); 355 if (childunit < 0 && strcmp(typestr, "uart") != 0) { 356 typestr = "uart"; 357 childunit = puc_find_free_unit(typestr); 358 } 359 sc->sc_ports[i].dev = device_add_child(dev, typestr, 360 childunit); 361 if (sc->sc_ports[i].dev == NULL) { 362 if (sc->barmuxed) { 363 bus_space_unmap(rman_get_bustag(rle->res), 364 rman_get_bushandle(rle->res), ressz); 365 free(rle->res, M_DEVBUF); 366 free(pdev, M_DEVBUF); 367 } 368 continue; 369 } 370 device_set_ivars(sc->sc_ports[i].dev, pdev); 371 device_set_desc(sc->sc_ports[i].dev, sc->sc_desc.name); 372#ifdef PUC_DEBUG 373 printf("puc: type %d, bar %x, offset %x\n", 374 sc->sc_desc.ports[i].type, 375 sc->sc_desc.ports[i].bar, 376 sc->sc_desc.ports[i].offset); 377 puc_print_resource_list(&pdev->resources); 378#endif 379 device_set_flags(sc->sc_ports[i].dev, 380 sc->sc_desc.ports[i].flags); 381 if (device_probe_and_attach(sc->sc_ports[i].dev) != 0) { 382 if (sc->barmuxed) { 383 bus_space_unmap(rman_get_bustag(rle->res), 384 rman_get_bushandle(rle->res), ressz); 385 free(rle->res, M_DEVBUF); 386 free(pdev, M_DEVBUF); 387 } 388 } 389 } 390 391#ifdef PUC_DEBUG 392 bootverbose = 0; 393#endif 394 return (0); 395} 396 397static u_int32_t 398puc_ilr_read(struct puc_softc *sc) 399{ 400 u_int32_t mask; 401 int i; 402 403 mask = 0; 404 switch (sc->sc_desc.ilr_type) { 405 case PUC_ILR_TYPE_DIGI: 406 for (i = 1; i >= 0 && sc->sc_desc.ilr_offset[i] != 0; i--) { 407 mask = (mask << 8) | (bus_space_read_1(sc->ilr_st, 408 sc->ilr_sh, sc->sc_desc.ilr_offset[i]) & 0xff); 409 } 410 break; 411 412 default: 413 mask = 0xffffffff; 414 break; 415 } 416 return (mask); 417} 418 419/* 420 * This is an interrupt handler. For boards that can't tell us which 421 * device generated the interrupt it just calls all the registered 422 * handlers sequencially, but for boards that can tell us which 423 * device(s) generated the interrupt it calls only handlers for devices 424 * that actually generated the interrupt. 425 */ 426static void 427puc_intr(void *arg) 428{ 429 int i; 430 u_int32_t ilr_mask; 431 struct puc_softc *sc; 432 433 sc = (struct puc_softc *)arg; 434 ilr_mask = sc->ilr_enabled ? puc_ilr_read(sc) : 0xffffffff; 435 for (i = 0; i < PUC_MAX_PORTS; i++) 436 if (sc->sc_ports[i].ihand != NULL && 437 ((ilr_mask >> i) & 0x00000001)) 438 (sc->sc_ports[i].ihand)(sc->sc_ports[i].ihandarg); 439} 440 441static int 442puc_find_free_unit(char *name) 443{ 444 devclass_t dc; 445 int start; 446 int unit; 447 448 unit = 0; 449 start = 0; 450 while (resource_int_value(name, unit, "port", &start) == 0 && 451 start > 0) 452 unit++; 453 dc = devclass_find(name); 454 if (dc == NULL) 455 return (-1); 456 while (devclass_get_device(dc, unit)) 457 unit++; 458#ifdef PUC_DEBUG 459 printf("puc: Using %s%d\n", name, unit); 460#endif 461 return (unit); 462} 463 464#ifdef PUC_DEBUG 465static void 466puc_print_resource_list(struct resource_list *rl) 467{ 468#if 0 469 struct resource_list_entry *rle; 470 471 printf("print_resource_list: rl %p\n", rl); 472 SLIST_FOREACH(rle, rl, link) 473 printf(" type %x, rid %x start %lx end %lx count %lx\n", 474 rle->type, rle->rid, rle->start, rle->end, rle->count); 475 printf("print_resource_list: end.\n"); 476#endif 477} 478#endif 479 480struct resource * 481puc_alloc_resource(device_t dev, device_t child, int type, int *rid, 482 u_long start, u_long end, u_long count, u_int flags) 483{ 484 struct puc_device *pdev; 485 struct resource *retval; 486 struct resource_list *rl; 487 struct resource_list_entry *rle; 488 device_t my_child; 489 490 /* 491 * in the case of a child of child we need to find our immediate child 492 */ 493 for (my_child = child; device_get_parent(my_child) != dev; 494 my_child = device_get_parent(my_child)); 495 496 pdev = device_get_ivars(my_child); 497 rl = &pdev->resources; 498 499#ifdef PUC_DEBUG 500 printf("puc_alloc_resource: pdev %p, looking for t %x, r %x\n", 501 pdev, type, *rid); 502 puc_print_resource_list(rl); 503#endif 504 retval = NULL; 505 rle = resource_list_find(rl, type, *rid); 506 if (rle) { 507#ifdef PUC_DEBUG 508 printf("found rle, %lx, %lx, %lx\n", rle->start, rle->end, 509 rle->count); 510#endif 511 retval = rle->res; 512 } 513#ifdef PUC_DEBUG 514 else 515 printf("oops rle is gone\n"); 516#endif 517 518 return (retval); 519} 520 521int 522puc_release_resource(device_t dev, device_t child, int type, int rid, 523 struct resource *res) 524{ 525 return (0); 526} 527 528int 529puc_get_resource(device_t dev, device_t child, int type, int rid, 530 u_long *startp, u_long *countp) 531{ 532 struct puc_device *pdev; 533 struct resource_list *rl; 534 struct resource_list_entry *rle; 535 536 pdev = device_get_ivars(child); 537 rl = &pdev->resources; 538 539#ifdef PUC_DEBUG 540 printf("puc_get_resource: pdev %p, looking for t %x, r %x\n", pdev, 541 type, rid); 542 puc_print_resource_list(rl); 543#endif 544 rle = resource_list_find(rl, type, rid); 545 if (rle) { 546#ifdef PUC_DEBUG 547 printf("found rle %p,", rle); 548#endif 549 if (startp != NULL) 550 *startp = rle->start; 551 if (countp != NULL) 552 *countp = rle->count; 553#ifdef PUC_DEBUG 554 printf(" %lx, %lx\n", rle->start, rle->count); 555#endif 556 return (0); 557 } else 558 printf("oops rle is gone\n"); 559 return (ENXIO); 560} 561 562int 563puc_setup_intr(device_t dev, device_t child, struct resource *r, int flags, 564 void (*ihand)(void *), void *arg, void **cookiep) 565{ 566 int i; 567 struct puc_softc *sc; 568 569 sc = (struct puc_softc *)device_get_softc(dev); 570 if ((flags & INTR_FAST) != sc->fastintr) 571 return (ENXIO); 572 for (i = 0; PUC_PORT_VALID(sc->sc_desc, i); i++) { 573 if (sc->sc_ports[i].dev == child) { 574 if (sc->sc_ports[i].ihand != 0) 575 return (ENXIO); 576 sc->sc_ports[i].ihand = ihand; 577 sc->sc_ports[i].ihandarg = arg; 578 *cookiep = arg; 579 return (0); 580 } 581 } 582 return (ENXIO); 583} 584 585int 586puc_teardown_intr(device_t dev, device_t child, struct resource *r, 587 void *cookie) 588{ 589 int i; 590 struct puc_softc *sc; 591 592 sc = (struct puc_softc *)device_get_softc(dev); 593 for (i = 0; PUC_PORT_VALID(sc->sc_desc, i); i++) { 594 if (sc->sc_ports[i].dev == child) { 595 sc->sc_ports[i].ihand = NULL; 596 sc->sc_ports[i].ihandarg = NULL; 597 return (0); 598 } 599 } 600 return (ENXIO); 601} 602 603int 604puc_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) 605{ 606 struct puc_device *pdev; 607 608 pdev = device_get_ivars(child); 609 if (pdev == NULL) 610 return (ENOENT); 611 612 switch(index) { 613 case PUC_IVAR_FREQ: 614 *result = pdev->serialfreq; 615 break; 616 case PUC_IVAR_PORT: 617 *result = pdev->port; 618 break; 619 case PUC_IVAR_REGSHFT: 620 *result = pdev->regshft; 621 break; 622 case PUC_IVAR_SUBTYPE: 623 *result = pdev->subtype; 624 break; 625 default: 626 return (ENOENT); 627 } 628 return (0); 629} 630