1 /* $NetBSD: aupcmcia.c,v 1.4 2007/10/17 19:55:35 garbled Exp $ */ 2 3 /*- 4 * Copyright (c) 2006 Itronix Inc. 5 * All rights reserved. 6 * 7 * Written by Garrett D'Amore for Itronix Inc. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. The name of Itronix Inc. may not be used to endorse 18 * or promote products derived from this software without specific 19 * prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY 25 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 28 * ON ANY THEORY OF LIABILITY, WHETHER IN 29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 * POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 /* #include "opt_pci.h" */ 35 /* #include "pci.h" */ 36 37 #include <sys/cdefs.h> 38 __KERNEL_RCSID(0, "$NetBSD: aupcmcia.c,v 1.4 2007/10/17 19:55:35 garbled Exp $"); 39 40 #include <sys/types.h> 41 #include <sys/param.h> 42 #include <sys/systm.h> 43 #include <sys/errno.h> 44 #include <sys/kernel.h> 45 #include <sys/kthread.h> 46 47 #include <dev/pcmcia/pcmciareg.h> 48 #include <dev/pcmcia/pcmciavar.h> 49 #include <dev/pcmcia/pcmciachip.h> 50 51 #include <mips/alchemy/include/au_himem_space.h> 52 #include <mips/alchemy/include/aubusvar.h> 53 #include <mips/alchemy/include/aureg.h> 54 #include <mips/alchemy/include/auvar.h> 55 56 #include <mips/alchemy/dev/aupcmciareg.h> 57 #include <mips/alchemy/dev/aupcmciavar.h> 58 59 /* 60 * Borrow PCMCIADEBUG for now. Generally aupcmcia is the only PCMCIA 61 * host on these machines anyway. 62 */ 63 #ifdef PCMCIADEBUG 64 int aupcm_debug = 1; 65 #define DPRINTF(arg) if (aupcm_debug) printf arg 66 #else 67 #define DPRINTF(arg) 68 #endif 69 70 /* 71 * And for information about mappings, etc. use this one. 72 */ 73 #ifdef AUPCMCIANOISY 74 #define NOISY(arg) printf arg 75 #else 76 #define NOISY(arg) 77 #endif 78 79 /* 80 * Note, we use prefix "aupcm" instead of "aupcmcia", even though our 81 * driver is the latter, mostly because my fingers have trouble typing 82 * the former. "aupcm" should be sufficiently unique to avoid 83 * confusion. 84 */ 85 86 static int aupcm_mem_alloc(pcmcia_chipset_handle_t, bus_size_t, 87 struct pcmcia_mem_handle *); 88 static void aupcm_mem_free(pcmcia_chipset_handle_t, 89 struct pcmcia_mem_handle *); 90 static int aupcm_mem_map(pcmcia_chipset_handle_t, int, bus_addr_t, 91 bus_size_t, struct pcmcia_mem_handle *, bus_size_t *, int *); 92 static void aupcm_mem_unmap(pcmcia_chipset_handle_t, int); 93 94 static int aupcm_io_alloc(pcmcia_chipset_handle_t, bus_addr_t, bus_size_t, 95 bus_size_t, struct pcmcia_io_handle *); 96 static void aupcm_io_free(pcmcia_chipset_handle_t, struct pcmcia_io_handle *); 97 static int aupcm_io_map(pcmcia_chipset_handle_t, int, bus_addr_t, 98 bus_size_t, struct pcmcia_io_handle *, int *); 99 static void aupcm_io_unmap(pcmcia_chipset_handle_t, int); 100 static void *aupcm_intr_establish(pcmcia_chipset_handle_t, 101 struct pcmcia_function *, int, int (*)(void *), void *); 102 static void aupcm_intr_disestablish(pcmcia_chipset_handle_t, void *); 103 104 static void aupcm_slot_enable(pcmcia_chipset_handle_t); 105 static void aupcm_slot_disable(pcmcia_chipset_handle_t); 106 static void aupcm_slot_settype(pcmcia_chipset_handle_t, int); 107 108 static int aupcm_match(struct device *, struct cfdata *, void *); 109 static void aupcm_attach(struct device *, struct device *, void *); 110 111 static void aupcm_event_thread(void *); 112 static int aupcm_card_intr(void *); 113 static void aupcm_softintr(void *); 114 static int aupcm_print(void *, const char *); 115 116 struct aupcm_slot { 117 struct aupcm_softc *as_softc; 118 int as_slot; 119 int as_status; 120 int as_enabled; 121 int (*as_intr)(void *); 122 int as_card_irq; 123 int as_status_irq; 124 void *as_intrarg; 125 void *as_softint; 126 void *as_hardint; 127 const char *as_name; 128 bus_addr_t as_offset; 129 struct mips_bus_space as_iot; 130 struct mips_bus_space as_attrt; 131 struct mips_bus_space as_memt; 132 void *as_wins[AUPCMCIA_NWINS]; 133 134 struct device *as_pcmcia; 135 }; 136 137 /* this structure needs to be exposed... */ 138 struct aupcm_softc { 139 struct device sc_dev; 140 pcmcia_chipset_tag_t sc_pct; 141 142 void (*sc_slot_enable)(int); 143 void (*sc_slot_disable)(int); 144 int (*sc_slot_status)(int); 145 146 paddr_t sc_base; 147 148 int sc_wake; 149 lwp_t *sc_thread; 150 151 int sc_nslots; 152 struct aupcm_slot sc_slots[AUPCMCIA_NSLOTS]; 153 }; 154 155 static struct pcmcia_chip_functions aupcm_functions = { 156 aupcm_mem_alloc, 157 aupcm_mem_free, 158 aupcm_mem_map, 159 aupcm_mem_unmap, 160 161 aupcm_io_alloc, 162 aupcm_io_free, 163 aupcm_io_map, 164 aupcm_io_unmap, 165 166 aupcm_intr_establish, 167 aupcm_intr_disestablish, 168 169 aupcm_slot_enable, 170 aupcm_slot_disable, 171 aupcm_slot_settype, 172 }; 173 174 static struct mips_bus_space aupcm_memt; 175 176 CFATTACH_DECL(aupcmcia, sizeof (struct aupcm_softc), 177 aupcm_match, aupcm_attach, NULL, NULL); 178 179 int 180 aupcm_match(struct device *parent, struct cfdata *cf, void *aux) 181 { 182 struct aubus_attach_args *aa = aux; 183 static int found = 0; 184 185 if (found) 186 return 0; 187 188 if (strcmp(aa->aa_name, "aupcmcia") != 0) 189 return 0; 190 191 found = 1; 192 193 return 1; 194 } 195 196 void 197 aupcm_attach(struct device *parent, struct device *self, void *aux) 198 { 199 /* struct aubus_attach_args *aa = aux; */ 200 struct aupcm_softc *sc = (struct aupcm_softc *)self; 201 static int done = 0; 202 int slot; 203 struct aupcmcia_machdep *md; 204 205 /* initialize bus space */ 206 if (done) { 207 /* there can be only one. */ 208 return; 209 } 210 211 done = 1; 212 /* 213 * PCMCIA memory can live within pretty much the entire 32-bit 214 * space, modulo 64 MB wraps. We don't have to support coexisting 215 * DMA. 216 */ 217 au_himem_space_init(&aupcm_memt, "pcmciamem", 218 PCMCIA_BASE, AUPCMCIA_ATTR_OFFSET, 0xffffffff, 219 AU_HIMEM_SPACE_LITTLE_ENDIAN); 220 221 if ((md = aupcmcia_machdep()) == NULL) { 222 printf("\n%s:unable to get machdep structure\n", 223 sc->sc_dev.dv_xname); 224 return; 225 } 226 227 sc->sc_nslots = md->am_nslots; 228 sc->sc_slot_enable = md->am_slot_enable; 229 sc->sc_slot_disable = md->am_slot_disable; 230 sc->sc_slot_status = md->am_slot_status; 231 232 printf(": Alchemy PCMCIA, %d slots\n", sc->sc_nslots); 233 234 sc->sc_pct = (pcmcia_chipset_tag_t)&aupcm_functions; 235 236 for (slot = 0; slot < sc->sc_nslots; slot++) { 237 struct aupcm_slot *sp; 238 struct pcmciabus_attach_args paa; 239 240 sp = &sc->sc_slots[slot]; 241 sp->as_softc = sc; 242 243 sp->as_slot = slot; 244 sp->as_name = md->am_slot_name(slot); 245 sp->as_offset = md->am_slot_offset(slot); 246 sp->as_card_irq = md->am_slot_irq(slot, AUPCMCIA_IRQ_CARD); 247 sp->as_status_irq = md->am_slot_irq(slot, 248 AUPCMCIA_IRQ_INSERT); 249 250 au_himem_space_init(&sp->as_attrt, "pcmciaattr", 251 PCMCIA_BASE + sp->as_offset + AUPCMCIA_ATTR_OFFSET, 252 0, AUPCMCIA_MAP_SIZE, AU_HIMEM_SPACE_LITTLE_ENDIAN); 253 254 au_himem_space_init(&sp->as_memt, "pcmciamem", 255 PCMCIA_BASE + sp->as_offset + AUPCMCIA_MEM_OFFSET, 256 0, AUPCMCIA_MAP_SIZE, AU_HIMEM_SPACE_LITTLE_ENDIAN); 257 258 au_himem_space_init(&sp->as_iot, "pcmciaio", 259 PCMCIA_BASE + sp->as_offset + AUPCMCIA_IO_OFFSET, 260 0, AUPCMCIA_MAP_SIZE, 261 AU_HIMEM_SPACE_LITTLE_ENDIAN | AU_HIMEM_SPACE_IO); 262 263 sp->as_status = 0; 264 265 paa.paa_busname = "pcmcia"; 266 paa.pct = sc->sc_pct; 267 paa.pch = (pcmcia_chipset_handle_t)sp; 268 269 paa.iobase = 0; 270 paa.iosize = AUPCMCIA_MAP_SIZE; 271 272 sp->as_pcmcia = config_found(&sc->sc_dev, &paa, aupcm_print); 273 274 /* if no pcmcia, make sure slot is powered down */ 275 if (sp->as_pcmcia == NULL) { 276 aupcm_slot_disable(sp); 277 continue; 278 } 279 280 /* this makes sure we probe the slot */ 281 sc->sc_wake |= (1 << slot); 282 } 283 284 /* 285 * XXX: this would be an excellent time time to establish a handler 286 * for the card insertion interrupt, but that's edge triggered, and 287 * au_icu.c won't support it right now. We poll in the event thread 288 * for now. Start by initializing it now. 289 */ 290 if (kthread_create(PRI_NONE, 0, NULL, aupcm_event_thread, sc, 291 &sc->sc_thread, "%s", sc->sc_dev.dv_xname) != 0) 292 panic("%s: unable to create event kthread", 293 sc->sc_dev.dv_xname); 294 } 295 296 int 297 aupcm_print(void *aux, const char *pnp) 298 { 299 struct pcmciabus_attach_args *paa = aux; 300 struct aupcm_slot *sp = paa->pch; 301 302 printf(" socket %d irq %d, %s", sp->as_slot, sp->as_card_irq, 303 sp->as_name); 304 305 return (UNCONF); 306 } 307 308 void * 309 aupcm_intr_establish(pcmcia_chipset_handle_t pch, 310 struct pcmcia_function *pf, int level, int (*handler)(void *), void *arg) 311 { 312 struct aupcm_slot *sp = (struct aupcm_slot *)pch; 313 int s; 314 315 /* 316 * Hmm. perhaps this intr should be a list. well, PCMCIA 317 * devices generally only have one interrupt, and so should 318 * generally have only one handler. So we leave it for now. 319 * (Other PCMCIA bus drivers do it this way.) 320 */ 321 sp->as_intr = handler; 322 sp->as_intrarg = arg; 323 324 /* 325 * XXX: pil must be a software interrupt level. That 326 * automatically implies that it is lower than any other 327 * hardware interrupts. So trying to figure out which level 328 * (IPL_SOFTNET, IPL_SOFTSERIAL, etc.) doesn't really do 329 * anything for us. 330 */ 331 sp->as_softint = softintr_establish(IPL_SOFT, aupcm_softintr, sp); 332 333 /* set up hard interrupt handler for the card IRQs */ 334 s = splhigh(); 335 sp->as_hardint = au_intr_establish(sp->as_card_irq, 0, 336 IPL_TTY, IST_LEVEL_LOW, aupcm_card_intr, sp); 337 /* if card is not powered up, then leave the IRQ masked */ 338 if (!sp->as_enabled) { 339 au_intr_disable(sp->as_card_irq); 340 } 341 splx(s); 342 343 return (sp->as_softint); 344 } 345 346 void 347 aupcm_intr_disestablish(pcmcia_chipset_handle_t pch, void *ih) 348 { 349 struct aupcm_slot *sp = (struct aupcm_slot *)pch; 350 351 KASSERT(sp->as_softint == ih); 352 /* KASSERT(sp->as_hardint); */ 353 /* set up hard interrupt handler for the card IRQs */ 354 355 au_intr_disestablish(sp->as_hardint); 356 sp->as_hardint = 0; 357 358 softintr_disestablish(ih); 359 sp->as_softint = 0; 360 sp->as_intr = NULL; 361 sp->as_intrarg = NULL; 362 } 363 364 /* 365 * FYI: Hot detach of PCMCIA is supposedly safe because H/W doesn't 366 * fault on accesses to missing hardware. 367 */ 368 void 369 aupcm_event_thread(void *arg) 370 { 371 struct aupcm_softc *sc = arg; 372 struct aupcm_slot *sp; 373 int s, i, attach, detach; 374 375 for (;;) { 376 s = splhigh(); 377 if (sc->sc_wake == 0) { 378 splx(s); 379 /* 380 * XXX: Currently, the au_icu.c lacks support 381 * for edge-triggered interrupts. So we 382 * cannot really use the status change 383 * inerrupts. For now we poll (once per sec). 384 * FYI, Linux does it this way, and they *do* 385 * have support for edge triggered interrupts. 386 * Go figure. 387 */ 388 tsleep(&sc->sc_wake, PWAIT, "aupcm_event", hz); 389 } 390 sc->sc_wake = 0; 391 392 attach = detach = 0; 393 for (i = 0; i < sc->sc_nslots; i++) { 394 sp = &sc->sc_slots[i]; 395 396 if (sc->sc_slot_status(sp->as_slot) != 0) { 397 if (!sp->as_status) { 398 DPRINTF(("%s: card %d insertion\n", 399 sc->sc_dev.dv_xname, i)); 400 attach |= (1 << i); 401 sp->as_status = 1; 402 } 403 } else { 404 if (sp->as_status) { 405 DPRINTF(("%s: card %d removal\n", 406 sc->sc_dev.dv_xname, i)); 407 detach |= (1 << i); 408 sp->as_status = 0; 409 } 410 } 411 } 412 splx(s); 413 414 for (i = 0; i < sc->sc_nslots; i++) { 415 sp = &sc->sc_slots[i]; 416 417 if (detach & (1 << i)) { 418 aupcm_slot_disable(sp); 419 pcmcia_card_detach(sp->as_pcmcia, 420 DETACH_FORCE); 421 } else if (attach & (1 << i)) { 422 /* 423 * until the function is enabled, don't 424 * honor interrupts 425 */ 426 sp->as_enabled = 0; 427 au_intr_disable(sp->as_card_irq); 428 pcmcia_card_attach(sp->as_pcmcia); 429 } 430 } 431 } 432 } 433 434 #if 0 435 void 436 aupcm_status_intr(void *arg) 437 { 438 int s; 439 struct aupcm_softc *sc = arg; 440 441 s = splhigh(); 442 443 /* kick the status thread so it does its bit */ 444 sc->sc_wake = 1; 445 wakeup(&sc->sc_wake); 446 447 splx(s); 448 } 449 #endif 450 451 int 452 aupcm_card_intr(void *arg) 453 { 454 struct aupcm_slot *sp = arg; 455 456 /* disable the hard interrupt for now */ 457 au_intr_disable(sp->as_card_irq); 458 459 if (sp->as_intr != NULL) { 460 softintr_schedule(sp->as_softint); 461 } 462 463 return 1; 464 } 465 466 void 467 aupcm_softintr(void *arg) 468 { 469 struct aupcm_slot *sp = arg; 470 int s; 471 472 sp->as_intr(sp->as_intrarg); 473 474 s = splhigh(); 475 476 if (sp->as_intr && sp->as_enabled) { 477 au_intr_enable(sp->as_card_irq); 478 } 479 480 splx(s); 481 } 482 483 void 484 aupcm_slot_enable(pcmcia_chipset_handle_t pch) 485 { 486 struct aupcm_slot *sp = (struct aupcm_slot *)pch; 487 int s; 488 489 /* no interrupts while we reset the card, please */ 490 if (sp->as_intr) 491 au_intr_disable(sp->as_card_irq); 492 493 /* 494 * XXX: should probably lock to make sure slot_disable and 495 * enable not called together. However, i believe that the 496 * event thread basically serializes them anyway. 497 */ 498 499 sp->as_softc->sc_slot_enable(sp->as_slot); 500 /* card is powered up now, honor device interrupts */ 501 502 s = splhigh(); 503 sp->as_enabled = 1; 504 if (sp->as_intr) 505 au_intr_enable(sp->as_card_irq); 506 splx(s); 507 } 508 509 void 510 aupcm_slot_disable(pcmcia_chipset_handle_t pch) 511 { 512 struct aupcm_slot *sp = (struct aupcm_slot *)pch; 513 int s; 514 515 s = splhigh(); 516 au_intr_disable(sp->as_card_irq); 517 sp->as_enabled = 0; 518 splx(s); 519 520 sp->as_softc->sc_slot_disable(sp->as_slot); 521 } 522 523 void 524 aupcm_slot_settype(pcmcia_chipset_handle_t pch, int type) 525 { 526 /* we do nothing now : type == PCMCIA_IFTYPE_IO */ 527 } 528 529 int 530 aupcm_mem_alloc(pcmcia_chipset_handle_t pch, bus_size_t size, 531 struct pcmcia_mem_handle *pcmh) 532 { 533 pcmh->memt = NULL; 534 pcmh->size = pcmh->realsize = size; 535 pcmh->addr = 0; 536 pcmh->mhandle = 0; 537 538 return 0; 539 } 540 541 void 542 aupcm_mem_free(pcmcia_chipset_handle_t pch, struct pcmcia_mem_handle *pcmh) 543 { 544 /* nothing to do */ 545 } 546 547 int 548 aupcm_mem_map(pcmcia_chipset_handle_t pch, int kind, bus_addr_t addr, 549 bus_size_t size, struct pcmcia_mem_handle *pcmh, bus_size_t *offsetp, 550 int *windowp) 551 { 552 struct aupcm_slot *sp = (struct aupcm_slot *)pch; 553 int win, err; 554 int s; 555 556 s = splhigh(); 557 for (win = 0; win < AUPCMCIA_NWINS; win++) { 558 if (sp->as_wins[win] == NULL) { 559 sp->as_wins[win] = pcmh; 560 break; 561 } 562 } 563 splx(s); 564 565 if (win >= AUPCMCIA_NWINS) { 566 return ENOMEM; 567 } 568 569 if (kind & PCMCIA_MEM_ATTR) { 570 pcmh->memt = &sp->as_attrt; 571 NOISY(("mapping ATTR addr %x size %x\n", (uint32_t)addr, 572 (uint32_t)size)); 573 } else { 574 pcmh->memt = &sp->as_memt; 575 NOISY(("mapping MEMORY addr %x size %x\n", (uint32_t)addr, 576 (uint32_t)size)); 577 } 578 579 if ((size + addr) > (64 * 1024 * 1024)) 580 return EINVAL; 581 582 pcmh->size = size; 583 584 err = bus_space_map(pcmh->memt, addr, size, 0, &pcmh->memh); 585 if (err != 0) { 586 sp->as_wins[win] = NULL; 587 return err; 588 } 589 *offsetp = 0; 590 *windowp = win; 591 592 return 0; 593 } 594 595 void 596 aupcm_mem_unmap(pcmcia_chipset_handle_t pch, int win) 597 { 598 struct aupcm_slot *sp = (struct aupcm_slot *)pch; 599 struct pcmcia_mem_handle *pcmh; 600 601 pcmh = (struct pcmcia_mem_handle *)sp->as_wins[win]; 602 sp->as_wins[win] = NULL; 603 604 NOISY(("memory umap virtual %x\n", (uint32_t)pcmh->memh)); 605 bus_space_unmap(pcmh->memt, pcmh->memh, pcmh->size); 606 pcmh->memt = NULL; 607 } 608 609 int 610 aupcm_io_alloc(pcmcia_chipset_handle_t pch, bus_addr_t start, 611 bus_size_t size, bus_size_t align, struct pcmcia_io_handle *pih) 612 { 613 struct aupcm_slot *sp = (struct aupcm_slot *)pch; 614 bus_space_handle_t bush; 615 int err; 616 617 pih->iot = &sp->as_iot; 618 pih->size = size; 619 pih->flags = 0; 620 621 /* 622 * start from the initial offset - this gets us a slot 623 * specific address, while still leaving the addresses more or 624 * less zero-based which is required for x86-style device 625 * drivers. 626 */ 627 err = bus_space_alloc(pih->iot, start, 0x100000, 628 size, align, 0, 0, &pih->addr, &bush); 629 NOISY(("start = %x, addr = %x, size = %x, bush = %x\n", 630 (uint32_t)start, (uint32_t)pih->addr, (uint32_t)size, 631 (uint32_t)bush)); 632 633 /* and we convert it back */ 634 if (err == 0) { 635 pih->ihandle = (void *)bush; 636 } 637 638 return (err); 639 } 640 641 void 642 aupcm_io_free(pcmcia_chipset_handle_t pch, struct pcmcia_io_handle *pih) 643 { 644 bus_space_free(pih->iot, (bus_space_handle_t)pih->ihandle, 645 pih->size); 646 } 647 648 int 649 aupcm_io_map(pcmcia_chipset_handle_t pch, int width, bus_addr_t offset, 650 bus_size_t size, struct pcmcia_io_handle *pih, int *windowp) 651 { 652 int err; 653 654 err = bus_space_subregion(pih->iot, (bus_space_handle_t)pih->ihandle, 655 offset, size, &pih->ioh); 656 NOISY(("io map offset = %x, size = %x, ih = %x, hdl=%x\n", 657 (uint32_t)offset, (uint32_t)size, 658 (uint32_t)pih->ihandle, (uint32_t)pih->ioh)); 659 660 return err; 661 } 662 663 void 664 aupcm_io_unmap(pcmcia_chipset_handle_t pch, int win) 665 { 666 /* We mustn't unmap/free subregion bus space! */ 667 NOISY(("io unmap\n")); 668 } 669