1 /* $NetBSD: gayle_pcmcia.c,v 1.24 2011/01/13 22:02:05 phx Exp $ */ 2 3 /* public domain */ 4 5 #include <sys/cdefs.h> 6 __KERNEL_RCSID(0, "$NetBSD: gayle_pcmcia.c,v 1.24 2011/01/13 22:02:05 phx Exp $"); 7 8 /* PCMCIA front-end driver for A1200's and A600's. */ 9 10 #include <sys/param.h> 11 #include <sys/device.h> 12 #include <sys/kernel.h> 13 #include <sys/kthread.h> 14 #include <sys/systm.h> 15 16 #include <uvm/uvm.h> 17 18 #include <dev/pcmcia/pcmciareg.h> 19 #include <dev/pcmcia/pcmciavar.h> 20 21 #include <machine/cpu.h> 22 #include <amiga/amiga/custom.h> 23 #include <amiga/amiga/device.h> 24 #include <amiga/amiga/gayle.h> 25 #include <amiga/amiga/isr.h> 26 27 28 /* There is one of these for each slot. And yes, there is only one slot. */ 29 struct pccard_slot { 30 struct pccard_softc *sc; /* refer to `parent' */ 31 int (*intr_func)(void *); 32 void * intr_arg; 33 struct device *card; 34 int flags; 35 #define SLOT_OCCUPIED 0x01 36 #define SLOT_NEW_CARD_EVENT 0x02 37 }; 38 39 struct pccard_softc { 40 struct device sc_dev; 41 struct bus_space_tag io_space; 42 struct bus_space_tag attr_space; 43 struct bus_space_tag mem_space; 44 struct pccard_slot devs[1]; 45 struct isr intr6; 46 struct isr intr2; 47 }; 48 49 static int pccard_probe(struct device *, struct cfdata *, void *); 50 static void pccard_attach(struct device *, struct device *, void *); 51 static void pccard_attach_slot(struct pccard_slot *); 52 static int pccard_intr6(void *); 53 static int pccard_intr2(void *); 54 static void pccard_kthread(void *); 55 56 static int pcf_mem_alloc(pcmcia_chipset_handle_t, bus_size_t, 57 struct pcmcia_mem_handle *); 58 static void pcf_mem_free(pcmcia_chipset_handle_t, struct pcmcia_mem_handle *); 59 static int pcf_mem_map(pcmcia_chipset_handle_t, int, bus_addr_t, bus_size_t, 60 struct pcmcia_mem_handle *, bus_addr_t *, int *); 61 static void pcf_mem_unmap(pcmcia_chipset_handle_t, int); 62 static int pcf_io_alloc(pcmcia_chipset_handle_t, bus_addr_t, bus_size_t, 63 bus_size_t, struct pcmcia_io_handle *); 64 static void pcf_io_free(pcmcia_chipset_handle_t, struct pcmcia_io_handle *); 65 static int pcf_io_map(pcmcia_chipset_handle_t, int, bus_addr_t, bus_size_t, 66 struct pcmcia_io_handle *, int *); 67 static void pcf_io_unmap(pcmcia_chipset_handle_t, int); 68 static void *pcf_intr_establish(pcmcia_chipset_handle_t, 69 struct pcmcia_function *, int, int (*)(void *), void *); 70 static void pcf_intr_disestablish(pcmcia_chipset_handle_t, void *); 71 static void pcf_socket_enable(pcmcia_chipset_handle_t); 72 static void pcf_socket_disable(pcmcia_chipset_handle_t); 73 static void pcf_socket_settype(pcmcia_chipset_handle_t, int); 74 75 static bsr(pcmio_bsr1, u_int8_t); 76 static bsw(pcmio_bsw1, u_int8_t); 77 static bsrm(pcmio_bsrm1, u_int8_t); 78 static bswm(pcmio_bswm1, u_int8_t); 79 static bsrm(pcmio_bsrr1, u_int8_t); 80 static bswm(pcmio_bswr1, u_int8_t); 81 static bssr(pcmio_bssr1, u_int8_t); 82 static bscr(pcmio_bscr1, u_int8_t); 83 84 CFATTACH_DECL(pccard, sizeof(struct pccard_softc), 85 pccard_probe, pccard_attach, NULL, NULL); 86 87 static struct pcmcia_chip_functions chip_functions = { 88 pcf_mem_alloc, pcf_mem_free, 89 pcf_mem_map, pcf_mem_unmap, 90 pcf_io_alloc, pcf_io_free, 91 pcf_io_map, pcf_io_unmap, 92 pcf_intr_establish, pcf_intr_disestablish, 93 pcf_socket_enable, pcf_socket_disable, 94 pcf_socket_settype 95 }; 96 97 static struct amiga_bus_space_methods pcmio_bs_methods; 98 99 static u_int8_t *reset_card_reg; 100 101 static int 102 pccard_probe(struct device *dev, struct cfdata *cfd, void *aux) 103 { 104 105 return (is_a600() || is_a1200()) && matchname(aux, "pccard"); 106 } 107 108 static void 109 pccard_attach(struct device *parent, struct device *myself, void *aux) 110 { 111 struct pccard_softc *self = (struct pccard_softc *) myself; 112 struct pcmciabus_attach_args paa; 113 vaddr_t pcmcia_base; 114 vaddr_t i; 115 116 printf("\n"); 117 118 gayle_init(); 119 120 pcmcia_base = uvm_km_alloc(kernel_map, 121 GAYLE_PCMCIA_END - GAYLE_PCMCIA_START, 122 0, UVM_KMF_VAONLY | UVM_KMF_NOWAIT); 123 if (pcmcia_base == 0) { 124 printf("attach failed (no virtual memory)\n"); 125 return; 126 } 127 128 for (i = GAYLE_PCMCIA_START; i < GAYLE_PCMCIA_END; i += PAGE_SIZE) 129 pmap_enter(vm_map_pmap(kernel_map), 130 i - GAYLE_PCMCIA_START + pcmcia_base, i, 131 VM_PROT_READ | VM_PROT_WRITE, true); 132 pmap_update(vm_map_pmap(kernel_map)); 133 134 /* override the one-byte access methods for I/O space */ 135 pcmio_bs_methods = amiga_bus_stride_1; 136 pcmio_bs_methods.bsr1 = pcmio_bsr1; 137 pcmio_bs_methods.bsw1 = pcmio_bsw1; 138 pcmio_bs_methods.bsrm1 = pcmio_bsrm1; 139 pcmio_bs_methods.bswm1 = pcmio_bswm1; 140 pcmio_bs_methods.bsrr1 = pcmio_bsrr1; 141 pcmio_bs_methods.bswr1 = pcmio_bswr1; 142 pcmio_bs_methods.bssr1 = pcmio_bssr1; 143 pcmio_bs_methods.bscr1 = pcmio_bscr1; 144 145 reset_card_reg = (u_int8_t *) pcmcia_base + 146 (GAYLE_PCMCIA_RESET - GAYLE_PCMCIA_START); 147 148 self->io_space.base = (bus_addr_t) pcmcia_base + 149 (GAYLE_PCMCIA_IO_START - GAYLE_PCMCIA_START); 150 self->io_space.absm = &pcmio_bs_methods; 151 152 self->attr_space.base = (bus_addr_t) pcmcia_base + 153 (GAYLE_PCMCIA_ATTR_START - GAYLE_PCMCIA_START); 154 self->attr_space.absm = &amiga_bus_stride_1; 155 156 /* XXX we should check if the 4M of common memory are actually 157 * RAM or PCMCIA usable. 158 * For now, we just do as if the 4M were RAM and make common memory 159 * point to attribute memory, which is OK for some I/O cards. 160 */ 161 self->mem_space.base = (bus_addr_t) pcmcia_base; 162 self->mem_space.absm = &amiga_bus_stride_1; 163 164 self->devs[0].sc = self; 165 self->devs[0].intr_func = NULL; 166 self->devs[0].intr_arg = NULL; 167 self->devs[0].flags = 0; 168 169 gayle.pcc_status = 0; 170 gayle.intreq = 0; 171 gayle.pcc_config = 0; 172 gayle.intena &= GAYLE_INT_IDE; 173 174 paa.paa_busname = "pcmcia"; 175 paa.pct = &chip_functions; 176 paa.pch = &self->devs[0]; 177 paa.iobase = 0; 178 paa.iosize = 0; 179 self->devs[0].card = 180 config_found(myself, &paa, simple_devprint); 181 if (self->devs[0].card == NULL) { 182 printf("attach failed, config_found() returned NULL\n"); 183 pmap_remove(kernel_map->pmap, pcmcia_base, 184 pcmcia_base + (GAYLE_PCMCIA_END - GAYLE_PCMCIA_START)); 185 pmap_update(kernel_map->pmap); 186 uvm_deallocate(kernel_map, pcmcia_base, 187 GAYLE_PCMCIA_END - GAYLE_PCMCIA_START); 188 return; 189 } 190 191 self->intr6.isr_intr = pccard_intr6; 192 self->intr6.isr_arg = self; 193 self->intr6.isr_ipl = 6; 194 add_isr(&self->intr6); 195 196 self->intr2.isr_intr = pccard_intr2; 197 self->intr2.isr_arg = self; 198 self->intr2.isr_ipl = 2; 199 add_isr(&self->intr2); 200 201 if (kthread_create(PRI_NONE, 0, NULL, pccard_kthread, self, 202 NULL, "pccard")) { 203 printf("%s: can't create kernel thread\n", 204 self->sc_dev.dv_xname); 205 panic("pccard kthread_create() failed"); 206 } 207 208 gayle.intena |= GAYLE_INT_DETECT | GAYLE_INT_IREQ; 209 210 /* reset the card if it's already there */ 211 if (gayle.pcc_status & GAYLE_CCMEM_DETECT) { 212 volatile u_int8_t x; 213 *reset_card_reg = 0x0; 214 delay(1000); 215 x = *reset_card_reg; 216 gayle.pcc_status = GAYLE_CCMEM_WP | GAYLE_CCIO_SPKR; 217 } 218 219 pccard_attach_slot(&self->devs[0]); 220 } 221 222 static int 223 pccard_intr6(void *arg) 224 { 225 struct pccard_softc *self = arg; 226 227 if (gayle.intreq & GAYLE_INT_DETECT) { 228 gayle.intreq = GAYLE_INT_IDE | GAYLE_INT_STSCHG | 229 GAYLE_INT_SPKR | GAYLE_INT_WP | GAYLE_INT_IREQ; 230 self->devs[0].flags |= SLOT_NEW_CARD_EVENT; 231 return 1; 232 } 233 return 0; 234 } 235 236 static int 237 pccard_intr2(void *arg) 238 { 239 struct pccard_softc *self = arg; 240 struct pccard_slot *slot = &self->devs[0]; 241 242 if (slot->flags & SLOT_NEW_CARD_EVENT) { 243 slot->flags &= ~SLOT_NEW_CARD_EVENT; 244 245 /* reset the registers */ 246 gayle.intreq = GAYLE_INT_IDE | GAYLE_INT_DETECT; 247 gayle.pcc_status = GAYLE_CCMEM_WP | GAYLE_CCIO_SPKR; 248 gayle.pcc_config = 0; 249 pccard_attach_slot(&self->devs[0]); 250 } else { 251 int intreq = gayle.intreq & 252 (GAYLE_INT_STSCHG | GAYLE_INT_WP | GAYLE_INT_IREQ); 253 if (intreq) { 254 gayle.intreq = (intreq ^ 0x2c) | 0xc0; 255 256 return slot->flags & SLOT_OCCUPIED && 257 slot->intr_func != NULL && 258 slot->intr_func(slot->intr_arg); 259 } 260 } 261 return 0; 262 } 263 264 static void 265 pccard_kthread(void *arg) 266 { 267 struct pccard_softc *self = arg; 268 struct pccard_slot *slot = &self->devs[0]; 269 270 for (;;) { 271 int s = spl2(); 272 273 if (slot->flags & SLOT_NEW_CARD_EVENT) { 274 slot->flags &= ~SLOT_NEW_CARD_EVENT; 275 gayle.intreq = 0xc0; 276 277 /* reset the registers */ 278 gayle.intreq = GAYLE_INT_IDE | GAYLE_INT_DETECT; 279 gayle.pcc_status = GAYLE_CCMEM_WP | GAYLE_CCIO_SPKR; 280 gayle.pcc_config = 0; 281 pccard_attach_slot(&self->devs[0]); 282 } 283 splx(s); 284 285 tsleep(slot, PWAIT, "pccthread", hz); 286 } 287 } 288 289 static void 290 pccard_attach_slot(struct pccard_slot *slot) 291 { 292 293 if (!(slot->flags & SLOT_OCCUPIED) && 294 gayle.pcc_status & GAYLE_CCMEM_DETECT) { 295 if (pcmcia_card_attach(slot->card) == 0) 296 slot->flags |= SLOT_OCCUPIED; 297 } 298 } 299 300 static int 301 pcf_mem_alloc(pcmcia_chipset_handle_t pch, bus_size_t bsz, 302 struct pcmcia_mem_handle *pcmh) 303 { 304 struct pccard_slot *slot = (struct pccard_slot *) pch; 305 306 pcmh->memt = &slot->sc->attr_space; 307 pcmh->memh = pcmh->memt->base; 308 return 0; 309 } 310 311 static void 312 pcf_mem_free(pcmcia_chipset_handle_t pch, struct pcmcia_mem_handle *memh) 313 { 314 } 315 316 static int 317 pcf_mem_map(pcmcia_chipset_handle_t pch, int kind, bus_addr_t addr, 318 bus_size_t size, struct pcmcia_mem_handle *pcmh, 319 bus_addr_t *offsetp, int *windowp) 320 { 321 struct pccard_slot *slot = (struct pccard_slot *) pch; 322 323 /* Ignore width requirements */ 324 kind &= ~PCMCIA_WIDTH_MEM_MASK; 325 326 switch (kind) { 327 case PCMCIA_MEM_ATTR: 328 pcmh->memt = &slot->sc->attr_space; 329 break; 330 case PCMCIA_MEM_COMMON: 331 pcmh->memt = &slot->sc->mem_space; 332 break; 333 default: 334 /* This means that this code needs an update/a bugfix */ 335 printf(__FILE__ ": unknown kind %d of PCMCIA memory\n", kind); 336 return 1; 337 } 338 339 bus_space_map(pcmh->memt, addr, size, 0, &pcmh->memh); 340 *offsetp = 0; 341 *windowp = 0; /* unused */ 342 343 return 0; 344 } 345 346 static void 347 pcf_mem_unmap(pcmcia_chipset_handle_t pch, int win) 348 { 349 } 350 351 static int 352 pcf_io_alloc(pcmcia_chipset_handle_t pch, bus_addr_t start, bus_size_t size, 353 bus_size_t align, struct pcmcia_io_handle *pcihp) 354 { 355 struct pccard_slot *slot = (struct pccard_slot *) pch; 356 357 pcihp->iot = &slot->sc->io_space; 358 pcihp->ioh = pcihp->iot->base; 359 return 0; 360 } 361 362 static void 363 pcf_io_free(pcmcia_chipset_handle_t pch, struct pcmcia_io_handle *pcihp) 364 { 365 } 366 367 static int 368 pcf_io_map(pcmcia_chipset_handle_t pch, int width, bus_addr_t offset, 369 bus_size_t size, struct pcmcia_io_handle *pcihp, int *windowp) 370 { 371 struct pccard_slot *slot = (struct pccard_slot *) pch; 372 373 pcihp->iot = &slot->sc->io_space; 374 bus_space_map(pcihp->iot, offset, size, 0, &pcihp->ioh); 375 376 *windowp = 0; /* unused */ 377 return 0; 378 } 379 380 static void 381 pcf_io_unmap(pcmcia_chipset_handle_t pch, int win) 382 { 383 } 384 385 static void * 386 pcf_intr_establish(pcmcia_chipset_handle_t pch, struct pcmcia_function *pf, 387 int ipl, int (*func)(void *), void *arg) 388 { 389 struct pccard_slot *slot = (struct pccard_slot *) pch; 390 int s; 391 392 s = splhigh(); 393 if (slot->intr_func == NULL) { 394 slot->intr_func = func; 395 slot->intr_arg = arg; 396 } else { 397 /* if we are here, we need to put intrs into a list */ 398 printf("ARGH! see " __FILE__ "\n"); 399 slot = NULL; 400 } 401 splx(s); 402 403 return slot; 404 } 405 406 static void 407 pcf_intr_disestablish(pcmcia_chipset_handle_t pch, void *intr_handler) 408 { 409 struct pccard_slot *slot = (struct pccard_slot *) intr_handler; 410 411 if (slot != NULL) { 412 slot->intr_func = NULL; 413 slot->intr_arg = NULL; 414 } 415 } 416 417 static void 418 pcf_socket_enable(pcmcia_chipset_handle_t pch) 419 { 420 } 421 422 static void 423 pcf_socket_disable(pcmcia_chipset_handle_t pch) 424 { 425 } 426 427 static void 428 pcf_socket_settype(pcmcia_chipset_handle_t pch, int type) { 429 } 430 431 static u_int8_t 432 pcmio_bsr1(bus_space_handle_t h, bus_size_t o) 433 { 434 435 return *((volatile u_int8_t *) h + o + (o & 1 ? 0xffff : 0)); 436 } 437 438 static void 439 pcmio_bsw1(bus_space_handle_t h, bus_size_t o, unsigned v) 440 { 441 442 *((volatile u_int8_t *) h + o + (o & 1 ? 0xffff : 0)) = v; 443 } 444 445 static void 446 pcmio_bsrm1(bus_space_handle_t h, bus_size_t o, u_int8_t *p, bus_size_t c) 447 { 448 volatile u_int8_t *src = (volatile u_int8_t *) 449 (h + o + (o & 1 ? 0xffff : 0)); 450 451 452 /* XXX we can (should, must) optimize this if c >= 4 */ 453 for (; c > 0; c--) 454 *p++ = *src; 455 } 456 457 458 static void 459 pcmio_bswm1(bus_space_handle_t h, bus_size_t o, const u_int8_t *p, bus_size_t c) 460 { 461 volatile u_int8_t *dst = (volatile u_int8_t *) 462 (h + o + (o & 1 ? 0xffff : 0)); 463 464 465 /* XXX we can (should, must) optimize this if c >= 4 */ 466 for (; c > 0; c--) 467 *dst = *p++; 468 } 469 470 static void 471 pcmio_bsrr1(bus_space_handle_t h, bus_size_t o, u_int8_t *p, bus_size_t c) 472 { 473 volatile u_int8_t *cp1; 474 volatile u_int8_t *cp2; 475 volatile u_int8_t *temp; 476 477 if (o & 1) { 478 cp1 = (volatile u_int8_t *) h + o + 0x10000; 479 cp2 = (volatile u_int8_t *) h + o; 480 } else { 481 cp1 = (volatile u_int8_t *) h + o; 482 cp2 = (volatile u_int8_t *) h + o + 0x10000 + 2; 483 } 484 485 /* XXX we can (should, must) optimize this if c >= 4 */ 486 for (; c > 0; c--) { 487 *p++ = *cp1; 488 cp1 += 2; 489 490 /* swap pointers - hope gcc generates exg for this ;) */ 491 temp = cp1; 492 cp1 = cp2; 493 cp2 = temp; 494 } 495 } 496 497 498 static void 499 pcmio_bswr1(bus_space_handle_t h, bus_size_t o, const u_int8_t *p, bus_size_t c) 500 { 501 volatile u_int8_t *cp1; 502 volatile u_int8_t *cp2; 503 volatile u_int8_t *temp; 504 505 if (o & 1) { 506 cp1 = (volatile u_int8_t *) h + o + 0x10000; 507 cp2 = (volatile u_int8_t *) h + o; 508 } else { 509 cp1 = (volatile u_int8_t *) h + o; 510 cp2 = (volatile u_int8_t *) h + o + 0x10000 + 2; 511 } 512 513 /* XXX we can (should, must) optimize this if c >= 4 */ 514 for (; c > 0; c--) { 515 *cp1 = *p++; 516 cp1 += 2; 517 518 /* swap pointers - hope gcc generates exg for this ;) */ 519 temp = cp1; 520 cp1 = cp2; 521 cp2 = temp; 522 } 523 } 524 525 void 526 pcmio_bssr1(bus_space_handle_t h, bus_size_t o, unsigned v, bus_size_t c) 527 { 528 529 panic("pcmio_bssr1 is not defined (" __FILE__ ")"); 530 } 531 532 void 533 pcmio_bscr1(bus_space_handle_t h, bus_size_t o, bus_space_handle_t g, 534 bus_size_t q, bus_size_t c) 535 { 536 537 panic("pcmio_bscr1 is not defined (" __FILE__ ")"); 538 } 539