1/* $NetBSD: plumpcmcia.c,v 1.32 2023/09/10 21:13:20 andvar 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.32 2023/09/10 21:13:20 andvar 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 61int plumpcmcia_match(device_t, cfdata_t, void *); 62void plumpcmcia_attach(device_t, device_t, void *); 63int plumpcmcia_print(void *, const char *); 64 65int plumpcmcia_power(void *, int, long, void *); 66 67struct plumpcmcia_softc; 68 69struct plumpcmcia_handle { 70 /* parent */ 71 device_t ph_parent; 72 /* child */ 73 device_t 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 111enum plumpcmcia_event_type { 112 PLUM_PCMCIA_EVENT_INSERT, 113 PLUM_PCMCIA_EVENT_REMOVE, 114}; 115 116struct 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 123struct plumpcmcia_softc { 124 device_t 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 lwp_t *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 142static void plumpcmcia_attach_socket(struct plumpcmcia_handle *); 143static int plumpcmcia_chip_mem_alloc(pcmcia_chipset_handle_t, bus_size_t, 144 struct pcmcia_mem_handle *); 145static void plumpcmcia_chip_mem_free(pcmcia_chipset_handle_t, 146 struct pcmcia_mem_handle *); 147static 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 *); 149static void plumpcmcia_chip_mem_unmap(pcmcia_chipset_handle_t, int); 150static int plumpcmcia_chip_io_alloc(pcmcia_chipset_handle_t, bus_addr_t, 151 bus_size_t, bus_size_t, struct pcmcia_io_handle *); 152static void plumpcmcia_chip_io_free(pcmcia_chipset_handle_t, 153 struct pcmcia_io_handle *); 154static int plumpcmcia_chip_io_map(pcmcia_chipset_handle_t, int, bus_addr_t, 155 bus_size_t, struct pcmcia_io_handle *, int *); 156static void plumpcmcia_chip_io_unmap(pcmcia_chipset_handle_t, int); 157static void plumpcmcia_chip_socket_enable(pcmcia_chipset_handle_t); 158static void plumpcmcia_chip_socket_disable(pcmcia_chipset_handle_t); 159static void plumpcmcia_chip_socket_settype(pcmcia_chipset_handle_t, int); 160static void *plumpcmcia_chip_intr_establish(pcmcia_chipset_handle_t, 161 struct pcmcia_function *, int, int (*)(void *), void *); 162static void plumpcmcia_chip_intr_disestablish(pcmcia_chipset_handle_t, void *); 163static void plumpcmcia_wait_ready( struct plumpcmcia_handle *); 164static void plumpcmcia_chip_do_mem_map(struct plumpcmcia_handle *, int); 165static void plumpcmcia_chip_do_io_map(struct plumpcmcia_handle *, int); 166 167static 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 185static struct plumpcmcia_event __event_queue_pool[PLUM_PCMCIA_EVENT_QUEUE_MAX]; 186static struct plumpcmcia_event *plumpcmcia_event_alloc(void); 187static void plumpcmcia_event_free(struct plumpcmcia_event *); 188static void plum_csc_intr_setup(struct plumpcmcia_softc *, 189 struct plumpcmcia_handle *, int); 190static int plum_csc_intr(void *); 191static void plumpcmcia_event_thread(void *); 192 193#ifdef PLUMPCMCIA_DEBUG 194/* debug */ 195#define __DEBUG_FUNC __attribute__((__unused__)) 196static void __ioareadump(plumreg_t) __DEBUG_FUNC; 197static void __memareadump(plumreg_t) __DEBUG_FUNC; 198static void plumpcmcia_dump(struct plumpcmcia_softc *) __DEBUG_FUNC; 199#endif /* PLUMPCMCIA_DEBUG */ 200 201CFATTACH_DECL_NEW(plumpcmcia, sizeof(struct plumpcmcia_softc), 202 plumpcmcia_match, plumpcmcia_attach, NULL, NULL); 203 204int 205plumpcmcia_match(device_t parent, cfdata_t cf, void *aux) 206{ 207 return (1); 208} 209 210void 211plumpcmcia_attach(device_t parent, device_t self, void *aux) 212{ 213 struct plum_attach_args *pa = aux; 214 struct plumpcmcia_softc *sc = device_private(self); 215 struct plumpcmcia_handle *ph; 216 int error __diagused; 217 218 sc->sc_dev = self; 219 sc->sc_pc = pa->pa_pc; 220 sc->sc_regt = pa->pa_regt; 221 222 /* map register area */ 223 if (bus_space_map(sc->sc_regt, PLUM_PCMCIA_REGBASE, 224 PLUM_PCMCIA_REGSIZE, 0, &sc->sc_regh)) { 225 printf(": register map failed\n"); 226 } 227 228 /* power control */ 229 plumpcmcia_power(sc, 0, 0, (void *)PWR_RESUME); 230 /* Add a hard power hook to power saving */ 231#if notyet 232 sc->sc_powerhook = config_hook(CONFIG_HOOK_PMEVENT, 233 CONFIG_HOOK_PMEVENT_HARDPOWER, 234 CONFIG_HOOK_SHARE, 235 plumpcmcia_power, sc); 236 if (sc->sc_powerhook == 0) 237 printf(": WARNING unable to establish hard power hook"); 238#endif 239 printf("\n"); 240 241 /* Slot0/1 CSC event queue */ 242 SIMPLEQ_INIT (&sc->sc_event_head); 243 error = kthread_create(PRI_NONE, 0, NULL, plumpcmcia_event_thread, 244 sc, &sc->sc_event_thread, "%s", device_xname(self)); 245 KASSERT(error == 0); 246 247 /* Slot 0 */ 248 ph = &sc->sc_ph[0]; 249 ph->ph_plum_irq = PLUM_INT_C1IO; 250 ph->ph_memarea = PLUM_PCMCIA_MEMWINCTRL_MAP_AREA1; 251 ph->ph_membase = PLUM_PCMCIA_MEMBASE1; 252 ph->ph_memsize = PLUM_PCMCIA_MEMSIZE1; 253 ph->ph_ioarea = PLUM_PCMCIA_IOWINADDRCTRL_AREA1; 254 ph->ph_iobase = PLUM_PCMCIA_IOBASE1; 255 ph->ph_iosize = PLUM_PCMCIA_IOSIZE1; 256 ph->ph_regt = sc->sc_regt; 257 bus_space_subregion(sc->sc_regt, sc->sc_regh, 258 PLUM_PCMCIA_REGSPACE_SLOT0, 259 PLUM_PCMCIA_REGSPACE_SIZE, 260 &ph->ph_regh); 261 ph->ph_iot = pa->pa_iot; 262 ph->ph_memt = pa->pa_iot; 263 ph->ph_parent = self; 264 265 plum_csc_intr_setup(sc, ph, PLUM_INT_C1SC); 266 plum_power_establish(sc->sc_pc, PLUM_PWR_PCC1); 267 plumpcmcia_attach_socket(ph); 268 269 /* Slot 1 */ 270 ph = &sc->sc_ph[1]; 271 ph->ph_plum_irq = PLUM_INT_C2IO; 272 ph->ph_memarea = PLUM_PCMCIA_MEMWINCTRL_MAP_AREA2; 273 ph->ph_membase = PLUM_PCMCIA_MEMBASE2; 274 ph->ph_memsize = PLUM_PCMCIA_MEMSIZE2; 275 ph->ph_ioarea = PLUM_PCMCIA_IOWINADDRCTRL_AREA2; 276 ph->ph_iobase = PLUM_PCMCIA_IOBASE2; 277 ph->ph_iosize = PLUM_PCMCIA_IOSIZE2; 278 ph->ph_regt = sc->sc_regt; 279 bus_space_subregion(sc->sc_regt, sc->sc_regh, 280 PLUM_PCMCIA_REGSPACE_SLOT1, 281 PLUM_PCMCIA_REGSPACE_SIZE, 282 &ph->ph_regh); 283 ph->ph_iot = pa->pa_iot; 284 ph->ph_memt = pa->pa_iot; 285 ph->ph_parent = self; 286 287 plum_csc_intr_setup(sc, ph, PLUM_INT_C2SC); 288 plum_power_establish(sc->sc_pc, PLUM_PWR_PCC2); 289 plumpcmcia_attach_socket(ph); 290} 291 292int 293plumpcmcia_print(void *arg, const char *pnp) 294{ 295 if (pnp) { 296 aprint_normal("pcmcia at %s", pnp); 297 } 298 299 return (UNCONF); 300} 301 302static void 303plumpcmcia_attach_socket(struct plumpcmcia_handle *ph) 304{ 305 struct pcmciabus_attach_args paa; 306 307 paa.paa_busname = "pcmcia"; 308 paa.pct = (pcmcia_chipset_tag_t)&plumpcmcia_functions; 309 paa.pch = (pcmcia_chipset_handle_t)ph; 310 311 if ((ph->ph_pcmcia = config_found(ph->ph_parent, &paa, plumpcmcia_print, 312 CFARGS_NONE))) { 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 326static void * 327plumpcmcia_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 = device_private(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 344static void 345plumpcmcia_chip_intr_disestablish(pcmcia_chipset_handle_t pch, void *ih) 346{ 347 struct plumpcmcia_handle *ph = pch; 348 struct plumpcmcia_softc *sc = device_private(ph->ph_parent); 349 350 plum_intr_disestablish(sc->sc_pc, ih); 351} 352 353static int 354plumpcmcia_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 385static void 386plumpcmcia_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 393static int 394plumpcmcia_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 440static void 441plumpcmcia_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 489static void 490plumpcmcia_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 504static int 505plumpcmcia_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 544static int 545plumpcmcia_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 const 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 589static void 590plumpcmcia_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 639static void 640plumpcmcia_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", (unsigned)pcihp->ioh, 650 (unsigned)pcihp->size); 651} 652 653static void 654plumpcmcia_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 676static void 677plumpcmcia_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 699static void 700plumpcmcia_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 790static void 791plumpcmcia_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 device_xname(ph->ph_parent), 809 ((type == PCMCIA_IFTYPE_IO) ? "io" : "mem"), reg); 810 811 plum_conf_write(regt, regh, PLUM_PCMCIA_GENCTRL, reg); 812} 813 814static void 815plumpcmcia_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 839static void 840plum_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 __diagused; 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 863static int 864plum_csc_intr(void *arg) 865{ 866 struct plumpcmcia_handle *ph = arg; 867 struct plumpcmcia_softc *sc = device_private(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", __func__); 878 } else { 879 DPRINTF("%s: unhandled csc event. 0x%02x\n", 880 __func__, 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", __func__, 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 910static struct plumpcmcia_event * 911plumpcmcia_event_alloc(void) 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 return NULL; 922} 923 924static void 925plumpcmcia_event_free(struct plumpcmcia_event *pe) 926{ 927 /* I assume context is already locked */ 928 pe->__queued = 0; 929} 930 931static void 932plumpcmcia_event_thread(void *arg) 933{ 934 struct plumpcmcia_softc *sc = arg; 935 struct plumpcmcia_event *pe; 936 int s; 937 938 while (/*CONSTCOND*/1) { /* XXX shutdown. -uch */ 939 tsleep(sc, PWAIT, "CSC wait", 0); 940 s = spltty(); 941 while ((pe = SIMPLEQ_FIRST(&sc->sc_event_head))) { 942 splx(s); 943 switch (pe->pe_type) { 944 default: 945 printf("%s: unknown event.\n", __func__); 946 break; 947 case PLUM_PCMCIA_EVENT_INSERT: 948 DPRINTF("%s: insert event.\n", __func__); 949 pcmcia_card_attach(pe->pe_ph->ph_pcmcia); 950 break; 951 case PLUM_PCMCIA_EVENT_REMOVE: 952 DPRINTF("%s: remove event.\n", __func__); 953 pcmcia_card_detach(pe->pe_ph->ph_pcmcia, 954 DETACH_FORCE); 955 break; 956 } 957 s = spltty(); 958 SIMPLEQ_REMOVE_HEAD(&sc->sc_event_head, pe_link); 959 plumpcmcia_event_free(pe); 960 } 961 splx(s); 962 } 963 /* NOTREACHED */ 964} 965 966/* power XXX notyet */ 967int 968plumpcmcia_power(void *ctx, int type, long id, void *msg) 969{ 970 struct plumpcmcia_softc *sc = ctx; 971 bus_space_tag_t regt = sc->sc_regt; 972 bus_space_handle_t regh = sc->sc_regh; 973 int why = (int)msg; 974 975 switch (why) { 976 case PWR_RESUME: 977 DPRINTF("%s: ON\n", device_xname(sc->sc_dev)); 978 /* power on */ 979 plum_conf_write(regt, regh, PLUM_PCMCIA_CARDPWRCTRL, 980 PLUM_PCMCIA_CARDPWRCTRL_ON); 981 break; 982 case PWR_SUSPEND: 983 /* FALLTHROUGH */ 984 case PWR_STANDBY: 985 plum_conf_write(regt, regh, PLUM_PCMCIA_CARDPWRCTRL, 986 PLUM_PCMCIA_CARDPWRCTRL_OFF); 987 DPRINTF("%s: OFF\n", device_xname(sc->sc_dev)); 988 break; 989 } 990 991 return (0); 992} 993 994#ifdef PLUMPCMCIA_DEBUG 995static void 996__ioareadump(plumreg_t reg) 997{ 998 999 if (reg & PLUM_PCMCIA_IOWINADDRCTRL_AREA2) { 1000 printf("I/O Area 2\n"); 1001 } else { 1002 printf("I/O Area 1\n"); 1003 } 1004} 1005 1006static void 1007__memareadump(plumreg_t reg) 1008{ 1009 int maparea; 1010 1011 maparea = PLUM_PCMCIA_MEMWINCTRL_MAP(reg); 1012 switch (maparea) { 1013 case PLUM_PCMCIA_MEMWINCTRL_MAP_AREA1: 1014 printf("MEM Area1\n"); 1015 break; 1016 case PLUM_PCMCIA_MEMWINCTRL_MAP_AREA2: 1017 printf("MEM Area2\n"); 1018 break; 1019 case PLUM_PCMCIA_MEMWINCTRL_MAP_AREA3: 1020 printf("MEM Area3\n"); 1021 break; 1022 case PLUM_PCMCIA_MEMWINCTRL_MAP_AREA4: 1023 printf("MEM Area4\n"); 1024 break; 1025 } 1026} 1027 1028static void 1029plumpcmcia_dump(struct plumpcmcia_softc *sc) 1030{ 1031 bus_space_tag_t regt = sc->sc_regt; 1032 bus_space_handle_t regh = sc->sc_regh; 1033 plumreg_t reg; 1034 1035 int i, j; 1036 1037 __memareadump(plum_conf_read(regt, regh, PLUM_PCMCIA_MEMWIN0CTRL)); 1038 __memareadump(plum_conf_read(regt, regh, PLUM_PCMCIA_MEMWIN1CTRL)); 1039 __memareadump(plum_conf_read(regt, regh, PLUM_PCMCIA_MEMWIN2CTRL)); 1040 __memareadump(plum_conf_read(regt, regh, PLUM_PCMCIA_MEMWIN3CTRL)); 1041 __memareadump(plum_conf_read(regt, regh, PLUM_PCMCIA_MEMWIN4CTRL)); 1042 1043 __ioareadump(plum_conf_read(regt, regh, PLUM_PCMCIA_IOWIN0ADDRCTRL)); 1044 __ioareadump(plum_conf_read(regt, regh, PLUM_PCMCIA_IOWIN1ADDRCTRL)); 1045 1046 for (j = 0; j < 2; j++) { 1047 printf("[slot %d]\n", j); 1048 for (i = 0; i < 0x120; i += 4) { 1049 reg = plum_conf_read(sc->sc_regt, sc->sc_regh, 1050 i + 0x800 * j); 1051 printf("%03x %08x", i, reg); 1052 dbg_bit_print(reg); 1053 } 1054 } 1055 printf("\n"); 1056} 1057#endif /* PLUMPCMCIA_DEBUG */ 1058