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