pccard.c revision 55500
1/* $NetBSD: pcmcia.c,v 1.13 1998/12/24 04:51:59 marc Exp $ */ 2/* $FreeBSD: head/sys/dev/pccard/pccard.c 55500 2000-01-06 07:30:28Z imp $ */ 3 4/* 5 * Copyright (c) 1997 Marc Horowitz. 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 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by Marc Horowitz. 18 * 4. The name of the author may not be used to endorse or promote products 19 * derived from this software without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33#include <sys/param.h> 34#include <sys/systm.h> 35#include <sys/malloc.h> 36#include <sys/module.h> 37#include <sys/kernel.h> 38#include <sys/queue.h> 39#include <sys/types.h> 40 41#include <sys/bus.h> 42#include <machine/bus.h> 43#include <sys/rman.h> 44#include <machine/resource.h> 45 46#include <dev/pccard/pccardreg.h> 47#include <dev/pccard/pccardchip.h> 48#include <dev/pccard/pccardvar.h> 49 50#include "power_if.h" 51 52#define PCCARDDEBUG 53 54#ifdef PCCARDDEBUG 55int pccard_debug = 1; 56#define DPRINTF(arg) if (pccard_debug) printf arg 57#define DEVPRINTF(arg) if (pccard_debug) device_printf arg 58int pccardintr_debug = 0; 59/* this is done this way to avoid doing lots of conditionals 60 at interrupt level. */ 61#define PCCARD_CARD_INTR (pccardintr_debug?pccard_card_intrdebug:pccard_card_intr) 62#else 63#define DPRINTF(arg) 64#define DEVPRINTF(arg) 65#define PCCARD_CARD_INTR (pccard_card_intr) 66#endif 67 68#ifdef PCCARDVERBOSE 69int pccard_verbose = 1; 70#else 71int pccard_verbose = 0; 72#endif 73 74int pccard_print(void *, const char *); 75 76int pccard_card_intr(void *); 77#ifdef PCCARDDEBUG 78int pccard_card_intrdebug(void *); 79#endif 80 81/* XXX Shouldn't be touching hardware, that's a layering violation */ 82/* XXX imp */ 83int 84pccard_ccr_read(pf, ccr) 85 struct pccard_function *pf; 86 int ccr; 87{ 88 return (bus_space_read_1(pf->pf_ccrt, pf->pf_ccrh, 89 pf->pf_ccr_offset + ccr)); 90} 91 92void 93pccard_ccr_write(pf, ccr, val) 94 struct pccard_function *pf; 95 int ccr; 96 int val; 97{ 98 99 if ((pf->ccr_mask) & (1 << (ccr / 2))) { 100 bus_space_write_1(pf->pf_ccrt, pf->pf_ccrh, 101 pf->pf_ccr_offset + ccr, val); 102 } 103} 104 105int 106pccard_card_attach(device_t dev) 107{ 108 struct pccard_softc *sc = (struct pccard_softc *) 109 device_get_softc(dev); 110 struct pccard_function *pf; 111 int attached; 112 113 DEVPRINTF((dev, "pccard_card_attach\n")); 114 /* 115 * this is here so that when socket_enable calls gettype, trt happens 116 */ 117 STAILQ_INIT(&sc->card.pf_head); 118 119 DEVPRINTF((dev, "chip_socket_enable\n")); 120 POWER_ENABLE_SOCKET(device_get_parent(dev), dev); 121 122 DEVPRINTF((dev, "read_cis\n")); 123 pccard_read_cis(sc); 124 125 DEVPRINTF((dev, "chip_socket_disable\n")); 126 POWER_DISABLE_SOCKET(device_get_parent(dev), dev); 127 128 DEVPRINTF((dev, "check_cis_quirks\n")); 129 pccard_check_cis_quirks(dev); 130 131 /* 132 * bail now if the card has no functions, or if there was an error in 133 * the cis. 134 */ 135 136 if (sc->card.error) 137 return (1); 138 if (STAILQ_EMPTY(&sc->card.pf_head)) 139 return (1); 140 141 if (pccard_verbose) 142 pccard_print_cis(dev); 143 144 attached = 0; 145 146 DEVPRINTF((dev, "functions scanning\n")); 147 STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { 148 if (STAILQ_EMPTY(&pf->cfe_head)) 149 continue; 150 151#ifdef DIAGNOSTIC 152 if (pf->child != NULL) { 153 device_printf(sc->dev, 154 "%s still attached to function %d!\n", 155 device_get_name(pf->child), pf->number); 156 panic("pccard_card_attach"); 157 } 158#endif 159 pf->sc = sc; 160 pf->child = NULL; 161 pf->cfe = NULL; 162 pf->ih_fct = NULL; 163 pf->ih_arg = NULL; 164 } 165 166 STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { 167 if (STAILQ_EMPTY(&pf->cfe_head)) 168 continue; 169 170#if XXX 171 if (attach_child()) { 172 attached++; 173 174 DEVPRINTF((sc->dev, "function %d CCR at %d " 175 "offset %lx: %x %x %x %x, %x %x %x %x, %x\n", 176 pf->number, pf->pf_ccr_window, pf->pf_ccr_offset, 177 pccard_ccr_read(pf, 0x00), 178 pccard_ccr_read(pf, 0x02), pccard_ccr_read(pf, 0x04), 179 pccard_ccr_read(pf, 0x06), pccard_ccr_read(pf, 0x0A), 180 pccard_ccr_read(pf, 0x0C), pccard_ccr_read(pf, 0x0E), 181 pccard_ccr_read(pf, 0x10), pccard_ccr_read(pf, 0x12))); 182 } 183#endif 184 } 185 186 return (attached ? 0 : 1); 187} 188 189void 190pccard_card_detach(device_t dev, int flags) 191{ 192 struct pccard_softc *sc = (struct pccard_softc *) 193 device_get_softc(dev); 194 struct pccard_function *pf; 195#if XXX 196 int error; 197#endif 198 199 /* 200 * We are running on either the PCCARD socket's event thread 201 * or in user context detaching a device by user request. 202 */ 203 STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { 204 if (STAILQ_FIRST(&pf->cfe_head) == NULL) 205 continue; 206 if (pf->child == NULL) 207 continue; 208 DEVPRINTF((sc->dev, "detaching %s (function %d)\n", 209 device_get_name(pf->child), pf->number)); 210#if XXX 211 if ((error = config_detach(pf->child, flags)) != 0) { 212 device_printf(sc->dev, 213 "error %d detaching %s (function %d)\n", 214 error, device_get_name(pf->child), pf->number); 215 } else 216 pf->child = NULL; 217#endif 218 } 219} 220 221void 222pccard_card_deactivate(device_t dev) 223{ 224 struct pccard_softc *sc = (struct pccard_softc *) 225 device_get_softc(dev); 226 struct pccard_function *pf; 227 228 /* 229 * We're in the chip's card removal interrupt handler. 230 * Deactivate the child driver. The PCCARD socket's 231 * event thread will run later to finish the detach. 232 */ 233 STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { 234 if (STAILQ_FIRST(&pf->cfe_head) == NULL) 235 continue; 236 if (pf->child == NULL) 237 continue; 238 DEVPRINTF((sc->dev, "deactivating %s (function %d)\n", 239 device_get_name(pf->child), pf->number)); 240#if XXX 241 config_deactivate(pf->child); 242#endif 243 } 244} 245 246int 247pccard_card_gettype(device_t dev) 248{ 249 struct pccard_softc *sc = (struct pccard_softc *) 250 device_get_softc(dev); 251 struct pccard_function *pf; 252 253 /* 254 * set the iftype to memory if this card has no functions (not yet 255 * probed), or only one function, and that is not initialized yet or 256 * that is memory. 257 */ 258 pf = STAILQ_FIRST(&sc->card.pf_head); 259 if (pf == NULL || 260 (STAILQ_NEXT(pf, pf_list) == NULL && 261 (pf->cfe == NULL || pf->cfe->iftype == PCCARD_IFTYPE_MEMORY))) 262 return (PCCARD_IFTYPE_MEMORY); 263 else 264 return (PCCARD_IFTYPE_IO); 265} 266 267/* 268 * Initialize a PCCARD function. May be called as long as the function is 269 * disabled. 270 */ 271void 272pccard_function_init(pf, cfe) 273 struct pccard_function *pf; 274 struct pccard_config_entry *cfe; 275{ 276 if (pf->pf_flags & PFF_ENABLED) 277 panic("pccard_function_init: function is enabled"); 278 279 /* Remember which configuration entry we are using. */ 280 pf->cfe = cfe; 281} 282 283/* Enable a PCCARD function */ 284int 285pccard_function_enable(pf) 286 struct pccard_function *pf; 287{ 288 struct pccard_function *tmp; 289 int reg; 290 291 if (pf->cfe == NULL) 292 panic("pccard_function_enable: function not initialized"); 293 294 /* 295 * Increase the reference count on the socket, enabling power, if 296 * necessary. 297 */ 298 if (pf->sc->sc_enabled_count++ == 0) 299 POWER_ENABLE_SOCKET(device_get_parent(pf->sc->dev), 300 pf->sc->dev); 301 DEVPRINTF((pf->sc->dev, "++enabled_count = %d\n", 302 pf->sc->sc_enabled_count)); 303 304 if (pf->pf_flags & PFF_ENABLED) { 305 /* 306 * Don't do anything if we're already enabled. 307 */ 308 return (0); 309 } 310 311 /* 312 * it's possible for different functions' CCRs to be in the same 313 * underlying page. Check for that. 314 */ 315 316 STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) { 317 if ((tmp->pf_flags & PFF_ENABLED) && 318 (pf->ccr_base >= (tmp->ccr_base - tmp->pf_ccr_offset)) && 319 ((pf->ccr_base + PCCARD_CCR_SIZE) <= 320 (tmp->ccr_base - tmp->pf_ccr_offset + 321 tmp->pf_ccr_realsize))) { 322 pf->pf_ccrt = tmp->pf_ccrt; 323 pf->pf_ccrh = tmp->pf_ccrh; 324 pf->pf_ccr_realsize = tmp->pf_ccr_realsize; 325 326 /* 327 * pf->pf_ccr_offset = (tmp->pf_ccr_offset - 328 * tmp->ccr_base) + pf->ccr_base; 329 */ 330 pf->pf_ccr_offset = 331 (tmp->pf_ccr_offset + pf->ccr_base) - 332 tmp->ccr_base; 333 pf->pf_ccr_window = tmp->pf_ccr_window; 334 break; 335 } 336 } 337 338 if (tmp == NULL) { 339 if (pccard_mem_alloc(pf, PCCARD_CCR_SIZE, &pf->pf_pcmh)) 340 goto bad; 341 342 if (pccard_mem_map(pf, PCCARD_MEM_ATTR, pf->ccr_base, 343 PCCARD_CCR_SIZE, &pf->pf_pcmh, &pf->pf_ccr_offset, 344 &pf->pf_ccr_window)) { 345 pccard_mem_free(pf, &pf->pf_pcmh); 346 goto bad; 347 } 348 } 349 350 reg = (pf->cfe->number & PCCARD_CCR_OPTION_CFINDEX); 351 reg |= PCCARD_CCR_OPTION_LEVIREQ; 352 if (pccard_mfc(pf->sc)) { 353 reg |= (PCCARD_CCR_OPTION_FUNC_ENABLE | 354 PCCARD_CCR_OPTION_ADDR_DECODE); 355 if (pf->ih_fct) 356 reg |= PCCARD_CCR_OPTION_IREQ_ENABLE; 357 358 } 359 pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg); 360 361 reg = 0; 362 363 if ((pf->cfe->flags & PCCARD_CFE_IO16) == 0) 364 reg |= PCCARD_CCR_STATUS_IOIS8; 365 if (pf->cfe->flags & PCCARD_CFE_AUDIO) 366 reg |= PCCARD_CCR_STATUS_AUDIO; 367 pccard_ccr_write(pf, PCCARD_CCR_STATUS, reg); 368 369 pccard_ccr_write(pf, PCCARD_CCR_SOCKETCOPY, 0); 370 371 if (pccard_mfc(pf->sc)) { 372 long tmp, iosize; 373 374 tmp = pf->pf_mfc_iomax - pf->pf_mfc_iobase; 375 /* round up to nearest (2^n)-1 */ 376 for (iosize = 1; iosize < tmp; iosize <<= 1) 377 ; 378 iosize--; 379 380 pccard_ccr_write(pf, PCCARD_CCR_IOBASE0, 381 pf->pf_mfc_iobase & 0xff); 382 pccard_ccr_write(pf, PCCARD_CCR_IOBASE1, 383 (pf->pf_mfc_iobase >> 8) & 0xff); 384 pccard_ccr_write(pf, PCCARD_CCR_IOBASE2, 0); 385 pccard_ccr_write(pf, PCCARD_CCR_IOBASE3, 0); 386 387 pccard_ccr_write(pf, PCCARD_CCR_IOSIZE, iosize); 388 } 389 390#ifdef PCCARDDEBUG 391 if (pccard_debug) { 392 STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) { 393 device_printf(tmp->sc->dev, 394 "function %d CCR at %d offset %x: " 395 "%x %x %x %x, %x %x %x %x, %x\n", 396 tmp->number, tmp->pf_ccr_window, 397 tmp->pf_ccr_offset, 398 pccard_ccr_read(tmp, 0x00), 399 pccard_ccr_read(tmp, 0x02), 400 pccard_ccr_read(tmp, 0x04), 401 pccard_ccr_read(tmp, 0x06), 402 pccard_ccr_read(tmp, 0x0A), 403 pccard_ccr_read(tmp, 0x0C), 404 pccard_ccr_read(tmp, 0x0E), 405 pccard_ccr_read(tmp, 0x10), 406 pccard_ccr_read(tmp, 0x12)); 407 } 408 } 409#endif 410 411 pf->pf_flags |= PFF_ENABLED; 412 return (0); 413 414 bad: 415 /* 416 * Decrement the reference count, and power down the socket, if 417 * necessary. 418 */ 419 if (--pf->sc->sc_enabled_count == 0) 420 POWER_DISABLE_SOCKET(device_get_parent(pf->sc->dev), 421 pf->sc->dev); 422 DEVPRINTF((pf->sc->dev, "--enabled_count = %d\n", 423 pf->sc->sc_enabled_count)); 424 425 return (1); 426} 427 428/* Disable PCCARD function. */ 429void 430pccard_function_disable(pf) 431 struct pccard_function *pf; 432{ 433 struct pccard_function *tmp; 434 435 if (pf->cfe == NULL) 436 panic("pccard_function_enable: function not initialized"); 437 438 if ((pf->pf_flags & PFF_ENABLED) == 0) { 439 /* 440 * Don't do anything if we're already disabled. 441 */ 442 return; 443 } 444 445 /* 446 * it's possible for different functions' CCRs to be in the same 447 * underlying page. Check for that. Note we mark us as disabled 448 * first to avoid matching ourself. 449 */ 450 451 pf->pf_flags &= ~PFF_ENABLED; 452 STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) { 453 if ((tmp->pf_flags & PFF_ENABLED) && 454 (pf->ccr_base >= (tmp->ccr_base - tmp->pf_ccr_offset)) && 455 ((pf->ccr_base + PCCARD_CCR_SIZE) <= 456 (tmp->ccr_base - tmp->pf_ccr_offset + tmp->pf_ccr_realsize))) 457 break; 458 } 459 460 /* Not used by anyone else; unmap the CCR. */ 461 if (tmp == NULL) { 462 pccard_mem_unmap(pf, pf->pf_ccr_window); 463 pccard_mem_free(pf, &pf->pf_pcmh); 464 } 465 466 /* 467 * Decrement the reference count, and power down the socket, if 468 * necessary. 469 */ 470 if (--pf->sc->sc_enabled_count == 0) 471 POWER_DISABLE_SOCKET(device_get_parent(pf->sc->dev), 472 pf->sc->dev); 473 DEVPRINTF((pf->sc->dev, "--enabled_count = %d\n", 474 pf->sc->sc_enabled_count)); 475} 476 477int 478pccard_io_map(pf, width, offset, size, pcihp, windowp) 479 struct pccard_function *pf; 480 int width; 481 bus_addr_t offset; 482 bus_size_t size; 483 struct pccard_io_handle *pcihp; 484 int *windowp; 485{ 486 int reg; 487 488 if (pccard_chip_io_map(pf->sc->pct, pf->sc->pch, 489 width, offset, size, pcihp, windowp)) 490 return (1); 491 492 /* 493 * XXX in the multifunction multi-iospace-per-function case, this 494 * needs to cooperate with io_alloc to make sure that the spaces 495 * don't overlap, and that the ccr's are set correctly 496 */ 497 498 if (pccard_mfc(pf->sc)) { 499 long tmp, iosize; 500 501 if (pf->pf_mfc_iomax == 0) { 502 pf->pf_mfc_iobase = pcihp->addr + offset; 503 pf->pf_mfc_iomax = pf->pf_mfc_iobase + size; 504 } else { 505 /* this makes the assumption that nothing overlaps */ 506 if (pf->pf_mfc_iobase > pcihp->addr + offset) 507 pf->pf_mfc_iobase = pcihp->addr + offset; 508 if (pf->pf_mfc_iomax < pcihp->addr + offset + size) 509 pf->pf_mfc_iomax = pcihp->addr + offset + size; 510 } 511 512 tmp = pf->pf_mfc_iomax - pf->pf_mfc_iobase; 513 /* round up to nearest (2^n)-1 */ 514 for (iosize = 1; iosize >= tmp; iosize <<= 1) 515 ; 516 iosize--; 517 518 pccard_ccr_write(pf, PCCARD_CCR_IOBASE0, 519 pf->pf_mfc_iobase & 0xff); 520 pccard_ccr_write(pf, PCCARD_CCR_IOBASE1, 521 (pf->pf_mfc_iobase >> 8) & 0xff); 522 pccard_ccr_write(pf, PCCARD_CCR_IOBASE2, 0); 523 pccard_ccr_write(pf, PCCARD_CCR_IOBASE3, 0); 524 525 pccard_ccr_write(pf, PCCARD_CCR_IOSIZE, iosize); 526 527 reg = pccard_ccr_read(pf, PCCARD_CCR_OPTION); 528 reg |= PCCARD_CCR_OPTION_ADDR_DECODE; 529 pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg); 530 } 531 return (0); 532} 533 534void 535pccard_io_unmap(pf, window) 536 struct pccard_function *pf; 537 int window; 538{ 539 540 pccard_chip_io_unmap(pf->sc->pct, pf->sc->pch, window); 541 542 /* XXX Anything for multi-function cards? */ 543} 544 545void * 546pccard_intr_establish(pf, ipl, ih_fct, ih_arg) 547 struct pccard_function *pf; 548 int ipl; 549 int (*ih_fct)(void *); 550 void *ih_arg; 551{ 552 void *ret; 553 554 /* behave differently if this is a multifunction card */ 555 556 if (pccard_mfc(pf->sc)) { 557 int s, ihcnt, hiipl, reg; 558 struct pccard_function *pf2; 559 560 /* 561 * mask all the ipl's which are already used by this card, 562 * and find the highest ipl number (lowest priority) 563 */ 564 565 ihcnt = 0; 566 s = 0; /* this is only here to keep the compiler 567 happy */ 568 hiipl = 0; /* this is only here to keep the compiler 569 happy */ 570 571 STAILQ_FOREACH(pf2, &pf->sc->card.pf_head, pf_list) { 572 if (pf2->ih_fct) { 573 DEVPRINTF((pf2->sc->dev, 574 "function %d has ih_fct %p\n", 575 pf2->number, pf2->ih_fct)); 576 577 if (ihcnt == 0) { 578 hiipl = pf2->ih_ipl; 579 } else { 580 if (pf2->ih_ipl > hiipl) 581 hiipl = pf2->ih_ipl; 582 } 583 584 ihcnt++; 585 } 586 } 587 588 /* 589 * establish the real interrupt, changing the ipl if 590 * necessary 591 */ 592 593 if (ihcnt == 0) { 594#ifdef DIAGNOSTIC 595 if (pf->sc->ih != NULL) 596 panic("card has intr handler, but no function does"); 597#endif 598 s = splhigh(); 599 600 /* set up the handler for the new function */ 601 602 pf->ih_fct = ih_fct; 603 pf->ih_arg = ih_arg; 604 pf->ih_ipl = ipl; 605 606 pf->sc->ih = pccard_chip_intr_establish(pf->sc->pct, 607 pf->sc->pch, pf, ipl, PCCARD_CARD_INTR, pf->sc); 608 splx(s); 609 } else if (ipl > hiipl) { 610#ifdef DIAGNOSTIC 611 if (pf->sc->ih == NULL) 612 panic("functions have ih, but the card does not"); 613#endif 614 615 /* XXX need #ifdef for splserial on x86 */ 616 s = splhigh(); 617 618 pccard_chip_intr_disestablish(pf->sc->pct, pf->sc->pch, 619 pf->sc->ih); 620 621 /* set up the handler for the new function */ 622 pf->ih_fct = ih_fct; 623 pf->ih_arg = ih_arg; 624 pf->ih_ipl = ipl; 625 626 pf->sc->ih = pccard_chip_intr_establish(pf->sc->pct, 627 pf->sc->pch, pf, ipl, PCCARD_CARD_INTR, pf->sc); 628 629 splx(s); 630 } else { 631 s = splhigh(); 632 633 /* set up the handler for the new function */ 634 635 pf->ih_fct = ih_fct; 636 pf->ih_arg = ih_arg; 637 pf->ih_ipl = ipl; 638 639 splx(s); 640 } 641 642 ret = pf->sc->ih; 643 644 if (ret != NULL) { 645 reg = pccard_ccr_read(pf, PCCARD_CCR_OPTION); 646 reg |= PCCARD_CCR_OPTION_IREQ_ENABLE; 647 pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg); 648 649 reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS); 650 reg |= PCCARD_CCR_STATUS_INTRACK; 651 pccard_ccr_write(pf, PCCARD_CCR_STATUS, reg); 652 } 653 } else { 654 ret = pccard_chip_intr_establish(pf->sc->pct, pf->sc->pch, 655 pf, ipl, ih_fct, ih_arg); 656 } 657 658 return (ret); 659} 660 661void 662pccard_intr_disestablish(pf, ih) 663 struct pccard_function *pf; 664 void *ih; 665{ 666 /* behave differently if this is a multifunction card */ 667 668 if (pccard_mfc(pf->sc)) { 669 int s, ihcnt, hiipl; 670 struct pccard_function *pf2; 671 672 /* 673 * mask all the ipl's which are already used by this card, 674 * and find the highest ipl number (lowest priority). Skip 675 * the current function. 676 */ 677 678 ihcnt = 0; 679 s = 0; /* this is only here to keep the compipler 680 happy */ 681 hiipl = 0; /* this is only here to keep the compipler 682 happy */ 683 684 STAILQ_FOREACH(pf2, &pf->sc->card.pf_head, pf_list) { 685 if (pf2 == pf) 686 continue; 687 688 if (pf2->ih_fct) { 689 if (ihcnt == 0) { 690 hiipl = pf2->ih_ipl; 691 } else { 692 if (pf2->ih_ipl > hiipl) 693 hiipl = pf2->ih_ipl; 694 } 695 ihcnt++; 696 } 697 } 698 699 /* 700 * if the ih being removed is lower priority than the lowest 701 * priority remaining interrupt, up the priority. 702 */ 703 704 /* ihcnt is the number of interrupt handlers *not* including 705 the one about to be removed. */ 706 707 if (ihcnt == 0) { 708 int reg; 709 710#ifdef DIAGNOSTIC 711 if (pf->sc->ih == NULL) 712 panic("disestablishing last function, but card has no ih"); 713#endif 714 pccard_chip_intr_disestablish(pf->sc->pct, pf->sc->pch, 715 pf->sc->ih); 716 717 reg = pccard_ccr_read(pf, PCCARD_CCR_OPTION); 718 reg &= ~PCCARD_CCR_OPTION_IREQ_ENABLE; 719 pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg); 720 721 pf->ih_fct = NULL; 722 pf->ih_arg = NULL; 723 724 pf->sc->ih = NULL; 725 } else if (pf->ih_ipl > hiipl) { 726#ifdef DIAGNOSTIC 727 if (pf->sc->ih == NULL) 728 panic("changing ih ipl, but card has no ih"); 729#endif 730 /* XXX need #ifdef for splserial on x86 */ 731 s = splhigh(); 732 733 pccard_chip_intr_disestablish(pf->sc->pct, pf->sc->pch, 734 pf->sc->ih); 735 pf->sc->ih = pccard_chip_intr_establish(pf->sc->pct, 736 pf->sc->pch, pf, hiipl, PCCARD_CARD_INTR, pf->sc); 737 738 /* null out the handler for this function */ 739 740 pf->ih_fct = NULL; 741 pf->ih_arg = NULL; 742 743 splx(s); 744 } else { 745 s = splhigh(); 746 747 pf->ih_fct = NULL; 748 pf->ih_arg = NULL; 749 750 splx(s); 751 } 752 } else { 753 pccard_chip_intr_disestablish(pf->sc->pct, pf->sc->pch, ih); 754 } 755} 756 757int 758pccard_card_intr(arg) 759 void *arg; 760{ 761 struct pccard_softc *sc = arg; 762 struct pccard_function *pf; 763 int reg, ret, ret2; 764 765 ret = 0; 766 767 STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { 768 if (pf->ih_fct != NULL && 769 (pf->ccr_mask & (1 << (PCCARD_CCR_STATUS / 2)))) { 770 reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS); 771 if (reg & PCCARD_CCR_STATUS_INTR) { 772 ret2 = (*pf->ih_fct)(pf->ih_arg); 773 if (ret2 != 0 && ret == 0) 774 ret = ret2; 775 reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS); 776 pccard_ccr_write(pf, PCCARD_CCR_STATUS, 777 reg & ~PCCARD_CCR_STATUS_INTR); 778 } 779 } 780 } 781 782 return (ret); 783} 784 785#ifdef PCCARDDEBUG 786int 787pccard_card_intrdebug(arg) 788 void *arg; 789{ 790 struct pccard_softc *sc = arg; 791 struct pccard_function *pf; 792 int reg, ret, ret2; 793 794 ret = 0; 795 796 STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) { 797 device_printf(sc->dev, 798 "intr flags=%x fct=%d cor=%02x csr=%02x pin=%02x", 799 pf->pf_flags, pf->number, 800 pccard_ccr_read(pf, PCCARD_CCR_OPTION), 801 pccard_ccr_read(pf, PCCARD_CCR_STATUS), 802 pccard_ccr_read(pf, PCCARD_CCR_PIN)); 803 if (pf->ih_fct != NULL && 804 (pf->ccr_mask & (1 << (PCCARD_CCR_STATUS / 2)))) { 805 reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS); 806 if (reg & PCCARD_CCR_STATUS_INTR) { 807 ret2 = (*pf->ih_fct)(pf->ih_arg); 808 if (ret2 != 0 && ret == 0) 809 ret = ret2; 810 reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS); 811 printf("; csr %02x->%02x", 812 reg, reg & ~PCCARD_CCR_STATUS_INTR); 813 pccard_ccr_write(pf, PCCARD_CCR_STATUS, 814 reg & ~PCCARD_CCR_STATUS_INTR); 815 } 816 } 817 printf("\n"); 818 } 819 820 return (ret); 821} 822#endif 823 824#define PCCARD_NPORT 2 825#define PCCARD_NMEM 5 826#define PCCARD_NIRQ 1 827#define PCCARD_NDRQ 0 828 829static int 830pccard_add_children(device_t dev, int busno) 831{ 832 return 0; 833} 834 835static int 836pccard_probe(device_t dev) 837{ 838 device_set_desc(dev, "PC Card bus -- newconfig version"); 839 return pccard_add_children(dev, device_get_unit(dev)); 840} 841 842static void 843pccard_print_resources(struct resource_list *rl, const char *name, int type, 844 int count, const char *format) 845{ 846 struct resource_list_entry *rle; 847 int printed; 848 int i; 849 850 printed = 0; 851 for (i = 0; i < count; i++) { 852 rle = resource_list_find(rl, type, i); 853 if (rle) { 854 if (printed == 0) 855 printf(" %s ", name); 856 else if (printed > 0) 857 printf(","); 858 printed++; 859 printf(format, rle->start); 860 if (rle->count > 1) { 861 printf("-"); 862 printf(format, rle->start + rle->count - 1); 863 } 864 } else if (i > 3) { 865 /* check the first few regardless */ 866 break; 867 } 868 } 869} 870 871static int 872pccard_print_child(device_t dev, device_t child) 873{ 874 struct pccard_ivar *devi = (struct pccard_ivar *) device_get_ivars(child); 875 struct resource_list *rl = &devi->resources; 876 int retval = 0; 877 878 retval += bus_print_child_header(dev, child); 879 retval += printf(" at"); 880 881 if (devi) { 882 pccard_print_resources(rl, "port", SYS_RES_IOPORT, 883 PCCARD_NPORT, "%#lx"); 884 pccard_print_resources(rl, "iomem", SYS_RES_MEMORY, 885 PCCARD_NMEM, "%#lx"); 886 pccard_print_resources(rl, "irq", SYS_RES_IRQ, PCCARD_NIRQ, 887 "%ld"); 888 pccard_print_resources(rl, "drq", SYS_RES_DRQ, PCCARD_NDRQ, 889 "%ld"); 890 retval += printf(" slot %d", devi->slotnum); 891 } 892 893 retval += bus_print_child_footer(dev, child); 894 895 return (retval); 896} 897 898static int 899pccard_set_resource(device_t dev, device_t child, int type, int rid, 900 u_long start, u_long count) 901{ 902 struct pccard_ivar *devi = (struct pccard_ivar *) device_get_ivars(child); 903 struct resource_list *rl = &devi->resources; 904 905 if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY 906 && type != SYS_RES_IRQ && type != SYS_RES_DRQ) 907 return EINVAL; 908 if (rid < 0) 909 return EINVAL; 910 if (type == SYS_RES_IOPORT && rid >= PCCARD_NPORT) 911 return EINVAL; 912 if (type == SYS_RES_MEMORY && rid >= PCCARD_NMEM) 913 return EINVAL; 914 if (type == SYS_RES_IRQ && rid >= PCCARD_NIRQ) 915 return EINVAL; 916 if (type == SYS_RES_DRQ && rid >= PCCARD_NDRQ) 917 return EINVAL; 918 919 resource_list_add(rl, type, rid, start, start + count - 1, count); 920 921 return 0; 922} 923 924static int 925pccard_get_resource(device_t dev, device_t child, int type, int rid, 926 u_long *startp, u_long *countp) 927{ 928 struct pccard_ivar *devi = (struct pccard_ivar *) device_get_ivars(child); 929 struct resource_list *rl = &devi->resources; 930 struct resource_list_entry *rle; 931 932 rle = resource_list_find(rl, type, rid); 933 if (!rle) 934 return ENOENT; 935 936 if (startp) 937 *startp = rle->start; 938 if (countp) 939 *countp = rle->count; 940 941 return 0; 942} 943 944static void 945pccard_delete_resource(device_t dev, device_t child, int type, int rid) 946{ 947 struct pccard_ivar *devi = (struct pccard_ivar *) device_get_ivars(child); 948 struct resource_list *rl = &devi->resources; 949 resource_list_delete(rl, type, rid); 950} 951 952static device_method_t pccard_methods[] = { 953 /* Device interface */ 954 DEVMETHOD(device_probe, pccard_probe), 955 DEVMETHOD(device_attach, bus_generic_attach), 956 DEVMETHOD(device_shutdown, bus_generic_shutdown), 957 DEVMETHOD(device_suspend, bus_generic_suspend), 958 DEVMETHOD(device_resume, bus_generic_resume), 959 960 /* Bus interface */ 961 DEVMETHOD(bus_print_child, pccard_print_child), 962 DEVMETHOD(bus_driver_added, bus_generic_driver_added), 963 DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), 964 DEVMETHOD(bus_release_resource, bus_generic_release_resource), 965 DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 966 DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 967 DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 968 DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 969 DEVMETHOD(bus_set_resource, pccard_set_resource), 970 DEVMETHOD(bus_get_resource, pccard_get_resource), 971 DEVMETHOD(bus_delete_resource, pccard_delete_resource), 972 973 { 0, 0 } 974}; 975 976static driver_t pccard_driver = { 977 "pccard", 978 pccard_methods, 979 1, /* no softc */ 980}; 981 982devclass_t pccard_devclass; 983 984DRIVER_MODULE(pccard, pcic, pccard_driver, pccard_devclass, 0, 0); 985DRIVER_MODULE(pccard, pc98pcic, pccard_driver, pccard_devclass, 0, 0); 986DRIVER_MODULE(pccard, pccbb, pccard_driver, pccard_devclass, 0, 0); 987DRIVER_MODULE(pccard, tcic, pccard_driver, pccard_devclass, 0, 0); 988