1 /* $NetBSD: plumpcmcia.c,v 1.19 2005/12/11 12:17:33 christos Exp $ */ 2 3 /* 4 * Copyright (c) 1999, 2000 UCHIYAMA Yasushi. All rights reserved. 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/cdefs.h> 34 __KERNEL_RCSID(0, "$NetBSD: plumpcmcia.c,v 1.19 2005/12/11 12:17:33 christos Exp $"); 35 36 #include <sys/param.h> 37 #include <sys/systm.h> 38 #include <sys/device.h> 39 #include <sys/kthread.h> 40 41 #include <machine/bus.h> 42 #include <machine/config_hook.h> 43 #include <machine/bus_space_hpcmips.h> 44 45 #include <dev/pcmcia/pcmciareg.h> 46 #include <dev/pcmcia/pcmciavar.h> 47 #include <dev/pcmcia/pcmciachip.h> 48 49 #include <hpcmips/tx/tx39var.h> 50 #include <hpcmips/dev/plumvar.h> 51 #include <hpcmips/dev/plumicuvar.h> 52 #include <hpcmips/dev/plumpowervar.h> 53 #include <hpcmips/dev/plumpcmciareg.h> 54 55 #ifdef PLUMPCMCIA_DEBUG 56 #define DPRINTF_ENABLE 57 #define DPRINTF_DEBUG plumpcmcia_debug 58 #endif 59 #include <machine/debug.h> 60 61 int plumpcmcia_match(struct device *, struct cfdata *, void *); 62 void plumpcmcia_attach(struct device *, struct device *, void *); 63 int plumpcmcia_print(void *, const char *); 64 65 int plumpcmcia_power(void *, int, long, void *); 66 67 struct plumpcmcia_softc; 68 69 struct plumpcmcia_handle { 70 /* parent */ 71 struct device *ph_parent; 72 /* child */ 73 struct device *ph_pcmcia; 74 75 /* PCMCIA controller register space */ 76 bus_space_tag_t ph_regt; 77 bus_space_handle_t ph_regh; 78 79 /* I/O port space */ 80 int ph_ioarea; /* not PCMCIA window */ 81 struct { 82 bus_addr_t pi_addr; 83 bus_size_t pi_size; 84 int pi_width; 85 } ph_io[PLUM_PCMCIA_IO_WINS]; 86 int ph_ioalloc; 87 bus_space_tag_t ph_iot; 88 bus_space_handle_t ph_ioh; 89 bus_addr_t ph_iobase; 90 bus_size_t ph_iosize; 91 92 /* I/O Memory space */ 93 int ph_memarea; /* not PCMCIA window */ 94 struct { 95 bus_addr_t pm_addr; 96 bus_size_t pm_size; 97 int32_t pm_offset; 98 int pm_kind; 99 } ph_mem[PLUM_PCMCIA_MEM_WINS]; 100 int ph_memalloc; 101 bus_space_tag_t ph_memt; 102 bus_space_handle_t ph_memh; 103 bus_addr_t ph_membase; 104 bus_size_t ph_memsize; 105 106 /* Card interrupt handler */ 107 int ph_plum_irq; 108 void *ph_card_ih; 109 }; 110 111 enum plumpcmcia_event_type { 112 PLUM_PCMCIA_EVENT_INSERT, 113 PLUM_PCMCIA_EVENT_REMOVE, 114 }; 115 116 struct plumpcmcia_event { 117 int __queued; 118 enum plumpcmcia_event_type pe_type; 119 struct plumpcmcia_handle *pe_ph; 120 SIMPLEQ_ENTRY(plumpcmcia_event) pe_link; 121 }; 122 123 struct plumpcmcia_softc { 124 struct device sc_dev; 125 plum_chipset_tag_t sc_pc; 126 127 /* Register space */ 128 bus_space_tag_t sc_regt; 129 bus_space_handle_t sc_regh; 130 131 /* power management hook */ 132 void *sc_powerhook; 133 134 /* CSC event */ 135 struct proc *sc_event_thread; 136 SIMPLEQ_HEAD (, plumpcmcia_event) sc_event_head; 137 138 /* for each slot */ 139 struct plumpcmcia_handle sc_ph[PLUMPCMCIA_NSLOTS]; 140 }; 141 142 static void plumpcmcia_attach_socket(struct plumpcmcia_handle *); 143 static int plumpcmcia_chip_mem_alloc(pcmcia_chipset_handle_t, bus_size_t, 144 struct pcmcia_mem_handle *); 145 static void plumpcmcia_chip_mem_free(pcmcia_chipset_handle_t, 146 struct pcmcia_mem_handle *); 147 static int plumpcmcia_chip_mem_map(pcmcia_chipset_handle_t, int, bus_addr_t, 148 bus_size_t, struct pcmcia_mem_handle *, bus_size_t *, int *); 149 static void plumpcmcia_chip_mem_unmap(pcmcia_chipset_handle_t, int); 150 static int plumpcmcia_chip_io_alloc(pcmcia_chipset_handle_t, bus_addr_t, 151 bus_size_t, bus_size_t, struct pcmcia_io_handle *); 152 static void plumpcmcia_chip_io_free(pcmcia_chipset_handle_t, 153 struct pcmcia_io_handle *); 154 static int plumpcmcia_chip_io_map(pcmcia_chipset_handle_t, int, bus_addr_t, 155 bus_size_t, struct pcmcia_io_handle *, int *); 156 static void plumpcmcia_chip_io_unmap(pcmcia_chipset_handle_t, int); 157 static void plumpcmcia_chip_socket_enable(pcmcia_chipset_handle_t); 158 static void plumpcmcia_chip_socket_disable(pcmcia_chipset_handle_t); 159 static void plumpcmcia_chip_socket_settype(pcmcia_chipset_handle_t, int); 160 static void *plumpcmcia_chip_intr_establish(pcmcia_chipset_handle_t, 161 struct pcmcia_function *, int, int (*)(void *), void *); 162 static void plumpcmcia_chip_intr_disestablish(pcmcia_chipset_handle_t, void *); 163 static void plumpcmcia_wait_ready( struct plumpcmcia_handle *); 164 static void plumpcmcia_chip_do_mem_map(struct plumpcmcia_handle *, int); 165 static void plumpcmcia_chip_do_io_map(struct plumpcmcia_handle *, int); 166 167 static struct pcmcia_chip_functions plumpcmcia_functions = { 168 plumpcmcia_chip_mem_alloc, 169 plumpcmcia_chip_mem_free, 170 plumpcmcia_chip_mem_map, 171 plumpcmcia_chip_mem_unmap, 172 plumpcmcia_chip_io_alloc, 173 plumpcmcia_chip_io_free, 174 plumpcmcia_chip_io_map, 175 plumpcmcia_chip_io_unmap, 176 plumpcmcia_chip_intr_establish, 177 plumpcmcia_chip_intr_disestablish, 178 plumpcmcia_chip_socket_enable, 179 plumpcmcia_chip_socket_disable, 180 plumpcmcia_chip_socket_settype, 181 }; 182 183 /* CSC */ 184 #define PLUM_PCMCIA_EVENT_QUEUE_MAX 5 185 static struct plumpcmcia_event __event_queue_pool[PLUM_PCMCIA_EVENT_QUEUE_MAX]; 186 static struct plumpcmcia_event *plumpcmcia_event_alloc(void); 187 static void plumpcmcia_event_free(struct plumpcmcia_event *); 188 static void plum_csc_intr_setup(struct plumpcmcia_softc *, 189 struct plumpcmcia_handle *, int); 190 static int plum_csc_intr(void *); 191 static void plumpcmcia_create_event_thread(void *); 192 static void plumpcmcia_event_thread(void *); 193 194 #ifdef PLUMPCMCIA_DEBUG 195 /* debug */ 196 #define __DEBUG_FUNC __attribute__((__unused__)) 197 static void __ioareadump(plumreg_t) __DEBUG_FUNC; 198 static void __memareadump(plumreg_t) __DEBUG_FUNC; 199 static void plumpcmcia_dump(struct plumpcmcia_softc *) __DEBUG_FUNC; 200 #endif /* PLUMPCMCIA_DEBUG */ 201 202 CFATTACH_DECL(plumpcmcia, sizeof(struct plumpcmcia_softc), 203 plumpcmcia_match, plumpcmcia_attach, NULL, NULL); 204 205 int 206 plumpcmcia_match(struct device *parent, struct cfdata *cf, void *aux) 207 { 208 return (1); 209 } 210 211 void 212 plumpcmcia_attach(struct device *parent, struct device *self, void *aux) 213 { 214 struct plum_attach_args *pa = aux; 215 struct plumpcmcia_softc *sc = (void*)self; 216 struct plumpcmcia_handle *ph; 217 218 sc->sc_pc = pa->pa_pc; 219 sc->sc_regt = pa->pa_regt; 220 221 /* map register area */ 222 if (bus_space_map(sc->sc_regt, PLUM_PCMCIA_REGBASE, 223 PLUM_PCMCIA_REGSIZE, 0, &sc->sc_regh)) { 224 printf(": register map failed\n"); 225 } 226 227 /* power control */ 228 plumpcmcia_power(sc, 0, 0, (void *)PWR_RESUME); 229 /* Add a hard power hook to power saving */ 230 #if notyet 231 sc->sc_powerhook = config_hook(CONFIG_HOOK_PMEVENT, 232 CONFIG_HOOK_PMEVENT_HARDPOWER, 233 CONFIG_HOOK_SHARE, 234 plumpcmcia_power, sc); 235 if (sc->sc_powerhook == 0) 236 printf(": WARNING unable to establish hard power hook"); 237 #endif 238 printf("\n"); 239 240 /* Slot0/1 CSC event queue */ 241 SIMPLEQ_INIT (&sc->sc_event_head); 242 kthread_create(plumpcmcia_create_event_thread, sc); 243 244 /* Slot 0 */ 245 ph = &sc->sc_ph[0]; 246 ph->ph_plum_irq = PLUM_INT_C1IO; 247 ph->ph_memarea = PLUM_PCMCIA_MEMWINCTRL_MAP_AREA1; 248 ph->ph_membase = PLUM_PCMCIA_MEMBASE1; 249 ph->ph_memsize = PLUM_PCMCIA_MEMSIZE1; 250 ph->ph_ioarea = PLUM_PCMCIA_IOWINADDRCTRL_AREA1; 251 ph->ph_iobase = PLUM_PCMCIA_IOBASE1; 252 ph->ph_iosize = PLUM_PCMCIA_IOSIZE1; 253 ph->ph_regt = sc->sc_regt; 254 bus_space_subregion(sc->sc_regt, sc->sc_regh, 255 PLUM_PCMCIA_REGSPACE_SLOT0, 256 PLUM_PCMCIA_REGSPACE_SIZE, 257 &ph->ph_regh); 258 ph->ph_iot = pa->pa_iot; 259 ph->ph_memt = pa->pa_iot; 260 ph->ph_parent = (void*)sc; 261 262 plum_csc_intr_setup(sc, ph, PLUM_INT_C1SC); 263 plum_power_establish(sc->sc_pc, PLUM_PWR_PCC1); 264 plumpcmcia_attach_socket(ph); 265 266 /* Slot 1 */ 267 ph = &sc->sc_ph[1]; 268 ph->ph_plum_irq = PLUM_INT_C2IO; 269 ph->ph_memarea = PLUM_PCMCIA_MEMWINCTRL_MAP_AREA2; 270 ph->ph_membase = PLUM_PCMCIA_MEMBASE2; 271 ph->ph_memsize = PLUM_PCMCIA_MEMSIZE2; 272 ph->ph_ioarea = PLUM_PCMCIA_IOWINADDRCTRL_AREA2; 273 ph->ph_iobase = PLUM_PCMCIA_IOBASE2; 274 ph->ph_iosize = PLUM_PCMCIA_IOSIZE2; 275 ph->ph_regt = sc->sc_regt; 276 bus_space_subregion(sc->sc_regt, sc->sc_regh, 277 PLUM_PCMCIA_REGSPACE_SLOT1, 278 PLUM_PCMCIA_REGSPACE_SIZE, 279 &ph->ph_regh); 280 ph->ph_iot = pa->pa_iot; 281 ph->ph_memt = pa->pa_iot; 282 ph->ph_parent = (void*)sc; 283 284 plum_csc_intr_setup(sc, ph, PLUM_INT_C2SC); 285 plum_power_establish(sc->sc_pc, PLUM_PWR_PCC2); 286 plumpcmcia_attach_socket(ph); 287 } 288 289 int 290 plumpcmcia_print(void *arg, const char *pnp) 291 { 292 if (pnp) { 293 aprint_normal("pcmcia at %s", pnp); 294 } 295 296 return (UNCONF); 297 } 298 299 static void 300 plumpcmcia_attach_socket(struct plumpcmcia_handle *ph) 301 { 302 struct pcmciabus_attach_args paa; 303 struct plumpcmcia_softc *sc = (void*)ph->ph_parent; 304 305 paa.paa_busname = "pcmcia"; 306 paa.pct = (pcmcia_chipset_tag_t)&plumpcmcia_functions; 307 paa.pch = (pcmcia_chipset_handle_t)ph; 308 paa.iobase = 0; 309 paa.iosize = ph->ph_iosize; 310 311 if ((ph->ph_pcmcia = config_found_ia((void*)sc, "pcmciabus", &paa, 312 plumpcmcia_print))) { 313 /* Enable slot */ 314 plum_conf_write(ph->ph_regt, ph->ph_regh, 315 PLUM_PCMCIA_SLOTCTRL, 316 PLUM_PCMCIA_SLOTCTRL_ENABLE); 317 /* Support 3.3V card & enable Voltage Sense Status */ 318 plum_conf_write(ph->ph_regt, ph->ph_regh, 319 PLUM_PCMCIA_FUNCCTRL, 320 PLUM_PCMCIA_FUNCCTRL_VSSEN | 321 PLUM_PCMCIA_FUNCCTRL_3VSUPPORT); 322 pcmcia_card_attach(ph->ph_pcmcia); 323 } 324 } 325 326 static void * 327 plumpcmcia_chip_intr_establish(pcmcia_chipset_handle_t pch, 328 struct pcmcia_function *pf, int ipl, 329 int (*ih_fun)(void *), void *ih_arg) 330 { 331 struct plumpcmcia_handle *ph = (void*)pch; 332 struct plumpcmcia_softc *sc = (void*)ph->ph_parent; 333 334 if (!(ph->ph_card_ih = 335 plum_intr_establish(sc->sc_pc, ph->ph_plum_irq, 336 IST_EDGE, IPL_BIO, ih_fun, ih_arg))) { 337 printf("plumpcmcia_chip_intr_establish: can't establish\n"); 338 return (0); 339 } 340 341 return (ph->ph_card_ih); 342 } 343 344 static void 345 plumpcmcia_chip_intr_disestablish(pcmcia_chipset_handle_t pch, void *ih) 346 { 347 struct plumpcmcia_handle *ph = (void*)pch; 348 struct plumpcmcia_softc *sc = (void*)ph->ph_parent; 349 350 plum_intr_disestablish(sc->sc_pc, ih); 351 } 352 353 static int 354 plumpcmcia_chip_mem_alloc(pcmcia_chipset_handle_t pch, bus_size_t size, 355 struct pcmcia_mem_handle *pcmhp) 356 { 357 struct plumpcmcia_handle *ph = (void*)pch; 358 bus_size_t realsize; 359 360 /* convert size to PCIC pages */ 361 realsize = ((size + (PLUM_PCMCIA_MEM_PAGESIZE - 1)) / 362 PLUM_PCMCIA_MEM_PAGESIZE) * PLUM_PCMCIA_MEM_PAGESIZE; 363 364 if (bus_space_alloc(ph->ph_memt, ph->ph_membase, 365 ph->ph_membase + ph->ph_memsize, 366 realsize, PLUM_PCMCIA_MEM_PAGESIZE, 367 0, 0, 0, &pcmhp->memh)) { 368 return (1); 369 } 370 371 pcmhp->memt = ph->ph_memt; 372 /* Address offset from MEM area base */ 373 pcmhp->addr = pcmhp->memh - ph->ph_membase - 374 ((struct bus_space_tag_hpcmips*)ph->ph_memt)->base; 375 pcmhp->size = size; 376 pcmhp->realsize = realsize; 377 378 DPRINTF(("plumpcmcia_chip_mem_alloc: size %#x->%#x addr %#x->%#x\n", 379 (unsigned)size, (unsigned)realsize, (unsigned)pcmhp->addr, 380 (unsigned)pcmhp->memh)); 381 382 return (0); 383 } 384 385 static void 386 plumpcmcia_chip_mem_free(pcmcia_chipset_handle_t pch, 387 struct pcmcia_mem_handle *pcmhp) 388 { 389 390 bus_space_free(pcmhp->memt, pcmhp->memh, pcmhp->size); 391 } 392 393 static int 394 plumpcmcia_chip_mem_map(pcmcia_chipset_handle_t pch, int kind, 395 bus_addr_t card_addr, bus_size_t size, 396 struct pcmcia_mem_handle *pcmhp, 397 bus_size_t *offsetp, int *windowp) 398 { 399 struct plumpcmcia_handle *ph = (void*)pch; 400 bus_addr_t busaddr; 401 int32_t card_offset; 402 int i, win; 403 404 for (win = -1, i = 0; i < PLUM_PCMCIA_MEM_WINS; i++) { 405 if ((ph->ph_memalloc & (1 << i)) == 0) { 406 win = i; 407 ph->ph_memalloc |= (1 << i); 408 break; 409 } 410 } 411 if (win == -1) { 412 DPRINTF(("plumpcmcia_chip_mem_map: no window\n")); 413 return (1); 414 } 415 416 busaddr = pcmhp->addr; 417 418 *offsetp = card_addr % PLUM_PCMCIA_MEM_PAGESIZE; 419 card_addr -= *offsetp; 420 size += *offsetp - 1; 421 *windowp = win; 422 card_offset = (((int32_t)card_addr) - ((int32_t)busaddr)); 423 424 DPRINTF(("plumpcmcia_chip_mem_map window %d bus %#x(kv:%#x)+%#x" 425 " size %#x at card addr %#x offset %#x\n", win, 426 (unsigned)busaddr, (unsigned)pcmhp->memh, (unsigned)*offsetp, 427 (unsigned)size, (unsigned)card_addr, (unsigned)card_offset)); 428 429 ph->ph_mem[win].pm_addr = busaddr; 430 ph->ph_mem[win].pm_size = size; 431 ph->ph_mem[win].pm_offset = card_offset; 432 ph->ph_mem[win].pm_kind = kind; 433 ph->ph_memalloc |= (1 << win); 434 435 plumpcmcia_chip_do_mem_map(ph, win); 436 437 return (0); 438 } 439 440 static void 441 plumpcmcia_chip_do_mem_map(struct plumpcmcia_handle *ph, int win) 442 { 443 bus_space_tag_t regt = ph->ph_regt; 444 bus_space_handle_t regh = ph->ph_regh; 445 plumreg_t reg, addr, offset, size; 446 447 if (win < 0 || win > 4) { 448 panic("plumpcmcia_chip_do_mem_map: bogus window %d", win); 449 } 450 451 addr = (ph->ph_mem[win].pm_addr) >> PLUM_PCMCIA_MEM_SHIFT; 452 size = (ph->ph_mem[win].pm_size) >> PLUM_PCMCIA_MEM_SHIFT; 453 offset = (ph->ph_mem[win].pm_offset) >> PLUM_PCMCIA_MEM_SHIFT; 454 455 /* Attribute memory or not */ 456 reg = ph->ph_mem[win].pm_kind == PCMCIA_MEM_ATTR ? 457 PLUM_PCMCIA_MEMWINCTRL_REGACTIVE : 0; 458 459 /* Notify I/O area to select for PCMCIA controller */ 460 reg = PLUM_PCMCIA_MEMWINCTRL_MAP_SET(reg, ph->ph_memarea); 461 462 /* Zero wait & 16bit access */ 463 reg |= (PLUM_PCMCIA_MEMWINCTRL_ZERO_WS | 464 PLUM_PCMCIA_MEMWINCTRL_DATASIZE16); 465 plum_conf_write(regt, regh, PLUM_PCMCIA_MEMWINCTRL(win), reg); 466 467 /* Map Host <-> PC-Card address */ 468 469 /* host-side */ 470 plum_conf_write(regt, regh, PLUM_PCMCIA_MEMWINSTARTADDR(win), 471 addr); 472 plum_conf_write(regt, regh, PLUM_PCMCIA_MEMWINSTOPADDR(win), 473 addr + size); 474 475 /* card-side */ 476 plum_conf_write(regt, regh, PLUM_PCMCIA_MEMWINOFSADDR(win), offset); 477 478 /* Enable memory window */ 479 reg = plum_conf_read(regt, regh, PLUM_PCMCIA_WINEN); 480 reg |= PLUM_PCMCIA_WINEN_MEM(win); 481 plum_conf_write(regt, regh, PLUM_PCMCIA_WINEN, reg); 482 483 DPRINTF(("plumpcmcia_chip_do_mem_map: window:%d %#x(%#x)+%#x\n", 484 win, offset, addr, size)); 485 486 delay(100); 487 } 488 489 static void 490 plumpcmcia_chip_mem_unmap(pcmcia_chipset_handle_t pch, int window) 491 { 492 struct plumpcmcia_handle *ph = (void*)pch; 493 bus_space_tag_t regt = ph->ph_regt; 494 bus_space_handle_t regh = ph->ph_regh; 495 plumreg_t reg; 496 497 reg = plum_conf_read(regt, regh, PLUM_PCMCIA_WINEN); 498 reg &= ~PLUM_PCMCIA_WINEN_MEM(window); 499 plum_conf_write(regt, regh, PLUM_PCMCIA_WINEN, reg); 500 501 ph->ph_memalloc &= ~(1 << window); 502 } 503 504 static int 505 plumpcmcia_chip_io_alloc(pcmcia_chipset_handle_t pch, bus_addr_t start, 506 bus_size_t size, bus_size_t align, 507 struct pcmcia_io_handle *pcihp) 508 { 509 struct plumpcmcia_handle *ph = (void*)pch; 510 511 DPRINTF(("plumpcmcia_chip_io_alloc: start=%#x size=%#x ", 512 (unsigned)start, (unsigned)size)); 513 if (start) { 514 if (bus_space_map(ph->ph_iot, ph->ph_iobase + start, 515 size, 0, &pcihp->ioh)) { 516 DPRINTF(("bus_space_map failed\n")); 517 return (1); 518 } 519 pcihp->flags = 0; 520 pcihp->addr = start; 521 DPRINTF(("(mapped) %#x+%#x\n", (unsigned)start, 522 (unsigned)size)); 523 } else { 524 if (bus_space_alloc(ph->ph_iot, ph->ph_iobase, 525 ph->ph_iobase + ph->ph_iosize, size, 526 align, 0, 0, 0, &pcihp->ioh)) { 527 DPRINTF(("bus_space_alloc failed\n")); 528 return 1; 529 } 530 /* Address offset from IO area base */ 531 pcihp->addr = pcihp->ioh - ph->ph_iobase - 532 ((struct bus_space_tag_hpcmips*)ph->ph_iot)->base; 533 pcihp->flags = PCMCIA_IO_ALLOCATED; 534 DPRINTF(("(allocated) %#x+%#x\n", (unsigned)pcihp->addr, 535 (unsigned)size)); 536 } 537 538 pcihp->iot = ph->ph_iot; 539 pcihp->size = size; 540 541 return (0); 542 } 543 544 static int 545 plumpcmcia_chip_io_map(pcmcia_chipset_handle_t pch, int width, 546 bus_addr_t offset, bus_size_t size, 547 struct pcmcia_io_handle *pcihp, int *windowp) 548 { 549 #ifdef PLUMPCMCIA_DEBUG 550 static char *width_names[] = { "auto", "io8", "io16" }; 551 #endif /* PLUMPCMCIA_DEBUG */ 552 struct plumpcmcia_handle *ph = (void*)pch; 553 bus_addr_t winofs; 554 int i, win; 555 556 winofs = pcihp->addr + offset; 557 558 if (winofs > 0x3ff) { 559 printf("plumpcmcia_chip_io_map: WARNING port %#lx > 0x3ff\n", 560 winofs); 561 } 562 563 for (win = -1, i = 0; i < PLUM_PCMCIA_IO_WINS; i++) { 564 if ((ph->ph_ioalloc & (1 << i)) == 0) { 565 win = i; 566 ph->ph_ioalloc |= (1 << i); 567 break; 568 } 569 } 570 if (win == -1) { 571 DPRINTF(("plumpcmcia_chip_io_map: no window\n")); 572 return (1); 573 } 574 *windowp = win; 575 576 ph->ph_io[win].pi_addr = winofs; 577 ph->ph_io[win].pi_size = size; 578 ph->ph_io[win].pi_width = width; 579 580 plumpcmcia_chip_do_io_map(ph, win); 581 582 DPRINTF(("plumpcmcia_chip_io_map: %#x(kv:%#x)+%#x %s\n", 583 (unsigned)offset, (unsigned)pcihp->ioh, (unsigned)size, 584 width_names[width])); 585 586 return (0); 587 } 588 589 static void 590 plumpcmcia_chip_do_io_map(struct plumpcmcia_handle *ph, int win) 591 { 592 bus_space_tag_t regt = ph->ph_regt; 593 bus_space_handle_t regh = ph->ph_regh; 594 plumreg_t reg; 595 bus_addr_t addr; 596 bus_size_t size; 597 int shift; 598 plumreg_t ioctlbits[3] = { 599 PLUM_PCMCIA_IOWINCTRL_IOCS16SRC, 600 0, 601 PLUM_PCMCIA_IOWINCTRL_DATASIZE16 602 }; 603 604 if (win < 0 || win > 1) { 605 panic("plumpcmcia_chip_do_io_map: bogus window %d", win); 606 } 607 608 addr = ph->ph_io[win].pi_addr; 609 size = ph->ph_io[win].pi_size; 610 611 /* Notify I/O area to select for PCMCIA controller */ 612 plum_conf_write(regt, regh, PLUM_PCMCIA_IOWINADDRCTRL(win), 613 ph->ph_ioarea); 614 615 /* Start/Stop addr */ 616 plum_conf_write(regt, regh, PLUM_PCMCIA_IOWINSTARTADDR(win), addr); 617 plum_conf_write(regt, regh, PLUM_PCMCIA_IOWINSTOPADDR(win), 618 addr + size - 1); 619 620 /* Set bus width */ 621 reg = plum_conf_read(regt, regh, PLUM_PCMCIA_IOWINCTRL); 622 shift = win == 0 ? PLUM_PCMCIA_IOWINCTRL_WIN0SHIFT : 623 PLUM_PCMCIA_IOWINCTRL_WIN1SHIFT; 624 625 reg &= ~(PLUM_PCMCIA_IOWINCTRL_WINMASK << shift); 626 reg |= ((ioctlbits[ph->ph_io[win].pi_width] | 627 PLUM_PCMCIA_IOWINCTRL_ZEROWAIT) << shift); 628 plum_conf_write(regt, regh, PLUM_PCMCIA_IOWINCTRL, reg); 629 630 /* Enable window */ 631 reg = plum_conf_read(regt, regh, PLUM_PCMCIA_WINEN); 632 reg |= (win == 0 ? PLUM_PCMCIA_WINEN_IO0 : 633 PLUM_PCMCIA_WINEN_IO1); 634 plum_conf_write(regt, regh, PLUM_PCMCIA_WINEN, reg); 635 636 delay(100); 637 } 638 639 static void 640 plumpcmcia_chip_io_free(pcmcia_chipset_handle_t pch, 641 struct pcmcia_io_handle *pcihp) 642 { 643 if (pcihp->flags & PCMCIA_IO_ALLOCATED) { 644 bus_space_free(pcihp->iot, pcihp->ioh, pcihp->size); 645 } else { 646 bus_space_unmap(pcihp->iot, pcihp->ioh, pcihp->size); 647 } 648 649 DPRINTF(("plumpcmcia_chip_io_free %#x+%#x\n", pcihp->ioh, 650 (unsigned)pcihp->size)); 651 } 652 653 static void 654 plumpcmcia_chip_io_unmap(pcmcia_chipset_handle_t pch, int window) 655 { 656 struct plumpcmcia_handle *ph = (void*)pch; 657 bus_space_tag_t regt = ph->ph_regt; 658 bus_space_handle_t regh = ph->ph_regh; 659 plumreg_t reg; 660 661 reg = plum_conf_read(regt, regh, PLUM_PCMCIA_WINEN); 662 switch (window) { 663 default: 664 panic("plumpcmcia_chip_io_unmap: bogus window"); 665 case 0: 666 reg &= ~PLUM_PCMCIA_WINEN_IO0; 667 break; 668 case 1: 669 reg &= ~PLUM_PCMCIA_WINEN_IO1; 670 break; 671 } 672 plum_conf_write(regt, regh, PLUM_PCMCIA_WINEN, reg); 673 ph->ph_ioalloc &= ~(1 << window); 674 } 675 676 static void 677 plumpcmcia_wait_ready(struct plumpcmcia_handle *ph) 678 { 679 bus_space_tag_t regt = ph->ph_regt; 680 bus_space_handle_t regh = ph->ph_regh; 681 int i; 682 683 for (i = 0; i < 10000; i++) { 684 if ((plum_conf_read(regt, regh, PLUM_PCMCIA_STATUS) & 685 PLUM_PCMCIA_STATUS_READY) && 686 (plum_conf_read(regt, regh, PLUM_PCMCIA_STATUS) & 687 PLUM_PCMCIA_STATUS_PWROK)) { 688 return; 689 } 690 delay(500); 691 692 if ((i > 5000) && (i % 100 == 99)) { 693 printf("."); 694 } 695 } 696 printf("plumpcmcia_wait_ready: failed\n"); 697 } 698 699 static void 700 plumpcmcia_chip_socket_enable(pcmcia_chipset_handle_t pch) 701 { 702 struct plumpcmcia_handle *ph = (void *)pch; 703 bus_space_tag_t regt = ph->ph_regt; 704 bus_space_handle_t regh = ph->ph_regh; 705 plumreg_t reg, power; 706 int win; 707 708 /* this bit is mostly stolen from pcic_attach_card */ 709 710 /* set card type to memory to disable interrupts */ 711 reg = plum_conf_read(regt, regh, PLUM_PCMCIA_GENCTRL); 712 reg &= ~PLUM_PCMCIA_GENCTRL_CARDTYPE_MASK; 713 plum_conf_write(regt, regh, PLUM_PCMCIA_GENCTRL, reg); 714 715 /* zero out the address windows */ 716 plum_conf_write(regt, regh, PLUM_PCMCIA_WINEN, 0); 717 718 /* power down the socket to reset it, clear the card reset pin */ 719 plum_conf_write(regt, regh, PLUM_PCMCIA_PWRCTRL, 0); 720 721 /* 722 * wait 300ms until power fails (Tpf). Then, wait 100ms since 723 * we are changing Vcc (Toff). 724 */ 725 delay((300 + 100) * 1000); 726 727 /* 728 * power up the socket 729 */ 730 /* detect voltage */ 731 reg = plum_conf_read(regt, regh, PLUM_PCMCIA_GENCTRL2); 732 if ((reg & PLUM_PCMCIA_GENCTRL2_VCC5V) == 733 PLUM_PCMCIA_GENCTRL2_VCC5V) { 734 power = PLUM_PCMCIA_PWRCTRL_VCC_CTRLBIT1; /* 5V */ 735 } else { 736 power = PLUM_PCMCIA_PWRCTRL_VCC_CTRLBIT0; /* 3.3V */ 737 } 738 739 plum_conf_write(regt, regh, PLUM_PCMCIA_PWRCTRL, 740 PLUM_PCMCIA_PWRCTRL_DISABLE_RESETDRV | 741 power | 742 PLUM_PCMCIA_PWRCTRL_PWR_ENABLE); 743 744 /* 745 * wait 100ms until power raise (Tpr) and 20ms to become 746 * stable (Tsu(Vcc)). 747 * 748 * some machines require some more time to be settled 749 * (300ms is added here). 750 */ 751 delay((100 + 20 + 300) * 1000); 752 753 plum_conf_write(regt, regh, PLUM_PCMCIA_PWRCTRL, 754 PLUM_PCMCIA_PWRCTRL_DISABLE_RESETDRV | 755 power | 756 PLUM_PCMCIA_PWRCTRL_OE | 757 PLUM_PCMCIA_PWRCTRL_PWR_ENABLE); 758 plum_conf_write(regt, regh, PLUM_PCMCIA_GENCTRL, 0); 759 760 /* 761 * hold RESET at least 10us. 762 */ 763 delay(10); 764 765 /* clear the reset flag */ 766 plum_conf_write(regt, regh, PLUM_PCMCIA_GENCTRL, 767 PLUM_PCMCIA_GENCTRL_RESET); 768 769 /* wait 20ms as per pc card standard (r2.01) section 4.3.6 */ 770 771 delay(20000); 772 773 /* wait for the chip to finish initializing */ 774 plumpcmcia_wait_ready(ph); 775 776 /* reinstall all the memory and io mappings */ 777 for (win = 0; win < PLUM_PCMCIA_MEM_WINS; win++) { 778 if (ph->ph_memalloc & (1 << win)) { 779 plumpcmcia_chip_do_mem_map(ph, win); 780 } 781 } 782 783 for (win = 0; win < PLUM_PCMCIA_IO_WINS; win++) { 784 if (ph->ph_ioalloc & (1 << win)) { 785 plumpcmcia_chip_do_io_map(ph, win); 786 } 787 } 788 } 789 790 static void 791 plumpcmcia_chip_socket_settype(pcmcia_chipset_handle_t pch, int type) 792 { 793 struct plumpcmcia_handle *ph = (void *)pch; 794 bus_space_tag_t regt = ph->ph_regt; 795 bus_space_handle_t regh = ph->ph_regh; 796 plumreg_t reg; 797 798 /* set the card type */ 799 800 reg = plum_conf_read(regt, regh, PLUM_PCMCIA_GENCTRL); 801 reg &= ~PLUM_PCMCIA_GENCTRL_CARDTYPE_MASK; 802 if (type == PCMCIA_IFTYPE_IO) 803 reg |= PLUM_PCMCIA_GENCTRL_CARDTYPE_IO; 804 else 805 reg |= PLUM_PCMCIA_GENCTRL_CARDTYPE_MEM; 806 807 DPRINTF(("%s: plumpcmcia_chip_socket_enable type %s %02x\n", 808 ph->ph_parent->dv_xname, 809 ((cardtype == PCMCIA_IFTYPE_IO) ? "io" : "mem"), reg)); 810 811 plum_conf_write(regt, regh, PLUM_PCMCIA_GENCTRL, reg); 812 } 813 814 static void 815 plumpcmcia_chip_socket_disable(pcmcia_chipset_handle_t pch) 816 { 817 struct plumpcmcia_handle *ph = (void *)pch; 818 bus_space_tag_t regt = ph->ph_regt; 819 bus_space_handle_t regh = ph->ph_regh; 820 plumreg_t reg; 821 822 /* set card type to memory to disable interrupts */ 823 reg = plum_conf_read(regt, regh, PLUM_PCMCIA_GENCTRL); 824 reg &= ~PLUM_PCMCIA_GENCTRL_CARDTYPE_MASK; 825 plum_conf_write(regt, regh, PLUM_PCMCIA_GENCTRL, reg); 826 827 /* zero out the address windows */ 828 plum_conf_write(regt, regh, PLUM_PCMCIA_WINEN, 0); 829 830 /* power down the socket */ 831 plum_conf_write(regt, regh, PLUM_PCMCIA_PWRCTRL, 0); 832 833 /* 834 * wait 300ms until power fails (Tpf). 835 */ 836 delay(300 * 1000); 837 } 838 839 static void 840 plum_csc_intr_setup(struct plumpcmcia_softc *sc, struct plumpcmcia_handle *ph, 841 int irq) 842 { 843 bus_space_tag_t regt = ph->ph_regt; 844 bus_space_handle_t regh = ph->ph_regh; 845 plumreg_t reg; 846 void *ih; 847 848 /* enable CARD DETECT ENABLE only */ 849 plum_conf_write(regt, regh, PLUM_PCMCIA_CSCINT, 850 PLUM_PCMCIA_CSCINT_CARD_DETECT); 851 852 /* don't use explicit writeback csc interrupt status */ 853 reg = plum_conf_read(regt, regh, PLUM_PCMCIA_GLOBALCTRL); 854 reg &= ~PLUM_PCMCIA_GLOBALCTRL_EXPLICIT_WB_CSC_INT; 855 plum_conf_write(regt, regh, PLUM_PCMCIA_GLOBALCTRL, reg); 856 857 /* install interrupt handler (don't fail) */ 858 ih = plum_intr_establish(sc->sc_pc, irq, IST_EDGE, IPL_TTY, 859 plum_csc_intr, ph); 860 KASSERT(ih != 0); 861 } 862 863 static int 864 plum_csc_intr(void *arg) 865 { 866 struct plumpcmcia_handle *ph = arg; 867 struct plumpcmcia_softc *sc = (void *)ph->ph_parent; 868 struct plumpcmcia_event *pe; 869 bus_space_tag_t regt = ph->ph_regt; 870 bus_space_handle_t regh = ph->ph_regh; 871 plumreg_t reg; 872 int flag; 873 874 /* read and clear interrupt status */ 875 reg = plum_conf_read(regt, regh, PLUM_PCMCIA_CSCINT_STAT); 876 if (reg & PLUM_PCMCIA_CSCINT_CARD_DETECT) { 877 DPRINTF(("%s: card status change.\n", __FUNCTION__)); 878 } else { 879 DPRINTF(("%s: unhandled csc event. 0x%02x\n", 880 __FUNCTION__, reg)); 881 return (0); 882 } 883 884 /* inquire card status (insert or remove) */ 885 reg = plum_conf_read(regt, regh, PLUM_PCMCIA_STATUS); 886 reg &= (PLUM_PCMCIA_STATUS_CD1 | PLUM_PCMCIA_STATUS_CD2); 887 if (reg == (PLUM_PCMCIA_STATUS_CD1 | PLUM_PCMCIA_STATUS_CD2)) { 888 /* insert */ 889 flag = PLUM_PCMCIA_EVENT_INSERT; 890 } else { 891 /* remove */ 892 flag = PLUM_PCMCIA_EVENT_REMOVE; 893 } 894 895 /* queue event to event thread and wakeup. */ 896 pe = plumpcmcia_event_alloc(); 897 if (pe == 0) { 898 printf("%s: event FIFO overflow (%d).\n", __FUNCTION__, 899 PLUM_PCMCIA_EVENT_QUEUE_MAX); 900 return (0); 901 } 902 pe->pe_type = flag; 903 pe->pe_ph = ph; 904 SIMPLEQ_INSERT_TAIL(&sc->sc_event_head, pe, pe_link); 905 wakeup(sc); 906 907 return (0); 908 } 909 910 static struct plumpcmcia_event * 911 plumpcmcia_event_alloc() 912 { 913 int i; 914 /* I assume called from interrupt context only. so don't lock */ 915 for (i = 0; i < PLUM_PCMCIA_EVENT_QUEUE_MAX; i++) { 916 if (!__event_queue_pool[i].__queued) { 917 __event_queue_pool[i].__queued = 1; 918 return (&__event_queue_pool[i]); 919 } 920 } 921 922 return (0); 923 } 924 925 static void 926 plumpcmcia_event_free(struct plumpcmcia_event *pe) 927 { 928 /* I assume context is already locked */ 929 pe->__queued = 0; 930 } 931 932 static void 933 plumpcmcia_create_event_thread(void *arg) 934 { 935 struct plumpcmcia_softc *sc = arg; 936 int error; 937 938 error = kthread_create1(plumpcmcia_event_thread, sc, 939 &sc->sc_event_thread, "%s", 940 sc->sc_dev.dv_xname); 941 KASSERT(error == 0); 942 } 943 944 static void 945 plumpcmcia_event_thread(void *arg) 946 { 947 struct plumpcmcia_softc *sc = arg; 948 struct plumpcmcia_event *pe; 949 int s; 950 951 while (/*CONSTCOND*/1) { /* XXX shutdown. -uch */ 952 tsleep(sc, PWAIT, "CSC wait", 0); 953 s = spltty(); 954 while ((pe = SIMPLEQ_FIRST(&sc->sc_event_head))) { 955 splx(s); 956 switch (pe->pe_type) { 957 default: 958 printf("%s: unknown event.\n", __FUNCTION__); 959 break; 960 case PLUM_PCMCIA_EVENT_INSERT: 961 DPRINTF(("%s: insert event.\n", __FUNCTION__)); 962 pcmcia_card_attach(pe->pe_ph->ph_pcmcia); 963 break; 964 case PLUM_PCMCIA_EVENT_REMOVE: 965 DPRINTF(("%s: remove event.\n", __FUNCTION__)); 966 pcmcia_card_detach(pe->pe_ph->ph_pcmcia, 967 DETACH_FORCE); 968 break; 969 } 970 s = spltty(); 971 SIMPLEQ_REMOVE_HEAD(&sc->sc_event_head, pe_link); 972 plumpcmcia_event_free(pe); 973 } 974 splx(s); 975 } 976 /* NOTREACHED */ 977 } 978 979 /* power XXX notyet */ 980 int 981 plumpcmcia_power(void *ctx, int type, long id, void *msg) 982 { 983 struct plumpcmcia_softc *sc = ctx; 984 bus_space_tag_t regt = sc->sc_regt; 985 bus_space_handle_t regh = sc->sc_regh; 986 int why = (int)msg; 987 988 switch (why) { 989 case PWR_RESUME: 990 DPRINTF(("%s: ON\n", sc->sc_dev.dv_xname)); 991 /* power on */ 992 plum_conf_write(regt, regh, PLUM_PCMCIA_CARDPWRCTRL, 993 PLUM_PCMCIA_CARDPWRCTRL_ON); 994 break; 995 case PWR_SUSPEND: 996 /* FALLTHROUGH */ 997 case PWR_STANDBY: 998 plum_conf_write(regt, regh, PLUM_PCMCIA_CARDPWRCTRL, 999 PLUM_PCMCIA_CARDPWRCTRL_OFF); 1000 DPRINTF(("%s: OFF\n", sc->sc_dev.dv_xname)); 1001 break; 1002 } 1003 1004 return (0); 1005 } 1006 1007 #ifdef PLUMPCMCIA_DEBUG 1008 static void 1009 __ioareadump(plumreg_t reg) 1010 { 1011 1012 if (reg & PLUM_PCMCIA_IOWINADDRCTRL_AREA2) { 1013 printf("I/O Area 2\n"); 1014 } else { 1015 printf("I/O Area 1\n"); 1016 } 1017 } 1018 1019 static void 1020 __memareadump(plumreg_t reg) 1021 { 1022 int maparea; 1023 1024 maparea = PLUM_PCMCIA_MEMWINCTRL_MAP(reg); 1025 switch (maparea) { 1026 case PLUM_PCMCIA_MEMWINCTRL_MAP_AREA1: 1027 printf("MEM Area1\n"); 1028 break; 1029 case PLUM_PCMCIA_MEMWINCTRL_MAP_AREA2: 1030 printf("MEM Area2\n"); 1031 break; 1032 case PLUM_PCMCIA_MEMWINCTRL_MAP_AREA3: 1033 printf("MEM Area3\n"); 1034 break; 1035 case PLUM_PCMCIA_MEMWINCTRL_MAP_AREA4: 1036 printf("MEM Area4\n"); 1037 break; 1038 } 1039 } 1040 1041 static void 1042 plumpcmcia_dump(struct plumpcmcia_softc *sc) 1043 { 1044 bus_space_tag_t regt = sc->sc_regt; 1045 bus_space_handle_t regh = sc->sc_regh; 1046 plumreg_t reg; 1047 1048 int i, j; 1049 1050 __memareadump(plum_conf_read(regt, regh, PLUM_PCMCIA_MEMWIN0CTRL)); 1051 __memareadump(plum_conf_read(regt, regh, PLUM_PCMCIA_MEMWIN1CTRL)); 1052 __memareadump(plum_conf_read(regt, regh, PLUM_PCMCIA_MEMWIN2CTRL)); 1053 __memareadump(plum_conf_read(regt, regh, PLUM_PCMCIA_MEMWIN3CTRL)); 1054 __memareadump(plum_conf_read(regt, regh, PLUM_PCMCIA_MEMWIN4CTRL)); 1055 1056 __ioareadump(plum_conf_read(regt, regh, PLUM_PCMCIA_IOWIN0ADDRCTRL)); 1057 __ioareadump(plum_conf_read(regt, regh, PLUM_PCMCIA_IOWIN1ADDRCTRL)); 1058 1059 for (j = 0; j < 2; j++) { 1060 printf("[slot %d]\n", j); 1061 for (i = 0; i < 0x120; i += 4) { 1062 reg = plum_conf_read(sc->sc_regt, sc->sc_regh, 1063 i + 0x800 * j); 1064 printf("%03x %08x", i, reg); 1065 dbg_bit_print(reg); 1066 } 1067 } 1068 printf("\n"); 1069 } 1070 #endif /* PLUMPCMCIA_DEBUG */ 1071