1 /* $NetBSD: wdc_pcmcia.c,v 1.48 2002/03/31 13:27:32 martin Exp $ */ 2 3 /*- 4 * Copyright (c) 1998 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Charles M. Hannum, by Onno van der Linden and by Manuel Bouyer. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 #include <sys/cdefs.h> 40 __KERNEL_RCSID(0, "$NetBSD: wdc_pcmcia.c,v 1.48 2002/03/31 13:27:32 martin Exp $"); 41 42 #include <sys/param.h> 43 #include <sys/device.h> 44 #include <sys/malloc.h> 45 #include <sys/systm.h> 46 47 #include <machine/bus.h> 48 #include <machine/intr.h> 49 50 #include <dev/pcmcia/pcmciareg.h> 51 #include <dev/pcmcia/pcmciavar.h> 52 #include <dev/pcmcia/pcmciadevs.h> 53 54 #include <dev/ata/atavar.h> 55 #include <dev/ic/wdcvar.h> 56 57 #define WDC_PCMCIA_REG_NPORTS 8 58 #define WDC_PCMCIA_AUXREG_OFFSET (WDC_PCMCIA_REG_NPORTS + 6) 59 #define WDC_PCMCIA_AUXREG_NPORTS 2 60 61 struct wdc_pcmcia_softc { 62 struct wdc_softc sc_wdcdev; 63 struct channel_softc *wdc_chanptr; 64 struct channel_softc wdc_channel; 65 struct pcmcia_io_handle sc_pioh; 66 struct pcmcia_io_handle sc_auxpioh; 67 struct pcmcia_mem_handle sc_pmembaseh; 68 struct pcmcia_mem_handle sc_pmemh; 69 struct pcmcia_mem_handle sc_auxpmemh; 70 int sc_memwindow; 71 int sc_iowindow; 72 int sc_auxiowindow; 73 void *sc_ih; 74 struct pcmcia_function *sc_pf; 75 int sc_flags; 76 #define WDC_PCMCIA_ATTACH 0x0001 77 #define WDC_PCMCIA_MEMMODE 0x0002 78 }; 79 80 static int wdc_pcmcia_match __P((struct device *, struct cfdata *, void *)); 81 static void wdc_pcmcia_attach __P((struct device *, struct device *, void *)); 82 static int wdc_pcmcia_detach __P((struct device *, int)); 83 84 struct cfattach wdc_pcmcia_ca = { 85 sizeof(struct wdc_pcmcia_softc), wdc_pcmcia_match, wdc_pcmcia_attach, 86 wdc_pcmcia_detach, wdcactivate 87 }; 88 89 const struct wdc_pcmcia_product { 90 u_int32_t wpp_vendor; /* vendor ID */ 91 u_int32_t wpp_product; /* product ID */ 92 int wpp_quirk_flag; /* Quirk flags */ 93 #define WDC_PCMCIA_NO_EXTRA_RESETS 0x02 /* Only reset ctrl once */ 94 const char *wpp_cis_info[4]; /* XXX necessary? */ 95 const char *wpp_name; /* product name */ 96 } wdc_pcmcia_products[] = { 97 98 { /* PCMCIA_VENDOR_DIGITAL XXX */ 0x0100, 99 PCMCIA_PRODUCT_DIGITAL_MOBILE_MEDIA_CDROM, 100 0, { NULL, "Digital Mobile Media CD-ROM", NULL, NULL }, 101 PCMCIA_STR_DIGITAL_MOBILE_MEDIA_CDROM }, 102 103 { PCMCIA_VENDOR_IBM, 104 PCMCIA_PRODUCT_IBM_PORTABLE_CDROM, 105 0, { NULL, "PCMCIA Portable CD-ROM Drive", NULL, NULL }, 106 PCMCIA_STR_IBM_PORTABLE_CDROM }, 107 108 /* The TEAC IDE/Card II is used on the Sony Vaio */ 109 { PCMCIA_VENDOR_TEAC, 110 PCMCIA_PRODUCT_TEAC_IDECARDII, 111 WDC_PCMCIA_NO_EXTRA_RESETS, 112 PCMCIA_CIS_TEAC_IDECARDII, 113 PCMCIA_STR_TEAC_IDECARDII }, 114 115 /* 116 * A fujitsu rebranded panasonic drive that reports 117 * itself as function "scsi", disk interface 0 118 */ 119 { PCMCIA_VENDOR_PANASONIC, 120 PCMCIA_PRODUCT_PANASONIC_KXLC005, 121 0, 122 PCMCIA_CIS_PANASONIC_KXLC005, 123 PCMCIA_STR_PANASONIC_KXLC005 }, 124 125 /* 126 * EXP IDE/ATAPI DVD Card use with some DVD players. 127 * Does not have a vendor ID or product ID. 128 */ 129 { -1, 130 -1, 131 0, 132 PCMCIA_CIS_EXP_EXPMULTIMEDIA, 133 PCMCIA_STR_EXP_EXPMULTIMEDIA }, 134 135 /* Mobile Dock 2, neither vendor ID nor product ID */ 136 { -1, -1, 0, 137 { "SHUTTLE TECHNOLOGY LTD.", "PCCARD-IDE/ATAPI Adapter", NULL, NULL}, 138 "SHUTTLE TECHNOLOGY IDE/ATAPI Adapter" 139 }, 140 141 /* Toshiba Portege 3110 CD, neither vendor ID nor product ID */ 142 { -1, -1, 0, 143 { "FREECOM", "PCCARD-IDE", NULL, NULL}, 144 "FREECOM PCCARD-IDE" 145 }, 146 147 /* Random CD-ROM, (badged AMACOM), neither vendor ID nor product ID */ 148 { -1, -1, 0, 149 { "PCMCIA", "CD-ROM", NULL, NULL}, 150 "PCMCIA CD-ROM" 151 }, 152 153 /* IO DATA CBIDE2, with neither vendor ID nor product ID */ 154 { -1, -1, 0, 155 PCMCIA_CIS_IODATA_CBIDE2, 156 PCMCIA_STR_IODATA_CBIDE2 157 }, 158 159 /* 160 * Novac PCMCIA-IDE Card for HD530P IDE Box, 161 * with neither vendor ID nor product ID 162 */ 163 { -1, -1, 0, 164 { "PCMCIA", "PnPIDE", NULL, NULL}, 165 "Novac PCCARD-IDE" 166 }, 167 168 { 0, 0, 0, { NULL, NULL, NULL, NULL}, NULL } 169 }; 170 171 const struct wdc_pcmcia_product * 172 wdc_pcmcia_lookup __P((struct pcmcia_attach_args *)); 173 174 int wdc_pcmcia_enable __P((struct device *, int)); 175 176 const struct wdc_pcmcia_product * 177 wdc_pcmcia_lookup(pa) 178 struct pcmcia_attach_args *pa; 179 { 180 const struct wdc_pcmcia_product *wpp; 181 int i, cis_match; 182 183 for (wpp = wdc_pcmcia_products; wpp->wpp_name != NULL; wpp++) 184 if ((wpp->wpp_vendor == -1 || 185 pa->manufacturer == wpp->wpp_vendor) && 186 (wpp->wpp_product == -1 || 187 pa->product == wpp->wpp_product)) { 188 cis_match = 1; 189 for (i = 0; i < 4; i++) { 190 if (!(wpp->wpp_cis_info[i] == NULL || 191 (pa->card->cis1_info[i] != NULL && 192 strcmp(pa->card->cis1_info[i], 193 wpp->wpp_cis_info[i]) == 0))) 194 cis_match = 0; 195 } 196 if (cis_match) 197 return (wpp); 198 } 199 200 return (NULL); 201 } 202 203 static int 204 wdc_pcmcia_match(parent, match, aux) 205 struct device *parent; 206 struct cfdata *match; 207 void *aux; 208 { 209 struct pcmcia_attach_args *pa = aux; 210 211 if (pa->pf->function == PCMCIA_FUNCTION_DISK && 212 pa->pf->pf_funce_disk_interface == PCMCIA_TPLFE_DDI_PCCARD_ATA) { 213 return 10; 214 } 215 216 if (wdc_pcmcia_lookup(pa) != NULL) 217 return (1); 218 219 return (0); 220 } 221 222 static void 223 wdc_pcmcia_attach(parent, self, aux) 224 struct device *parent; 225 struct device *self; 226 void *aux; 227 { 228 struct wdc_pcmcia_softc *sc = (void *)self; 229 struct pcmcia_attach_args *pa = aux; 230 struct pcmcia_config_entry *cfe; 231 const struct wdc_pcmcia_product *wpp; 232 bus_size_t offset; 233 int quirks; 234 235 sc->sc_pf = pa->pf; 236 237 for (cfe = SIMPLEQ_FIRST(&pa->pf->cfe_head); cfe != NULL; 238 cfe = SIMPLEQ_NEXT(cfe, cfe_list)) { 239 if (cfe->num_iospace != 1 && cfe->num_iospace != 2) 240 continue; 241 242 if (pcmcia_io_alloc(pa->pf, cfe->iospace[0].start, 243 cfe->iospace[0].length, 244 cfe->iospace[0].start == 0 ? cfe->iospace[0].length : 0, 245 &sc->sc_pioh)) 246 continue; 247 248 if (cfe->num_iospace == 2) { 249 if (!pcmcia_io_alloc(pa->pf, cfe->iospace[1].start, 250 cfe->iospace[1].length, 0, &sc->sc_auxpioh)) 251 break; 252 } else /* num_iospace == 1 */ { 253 sc->sc_auxpioh.iot = sc->sc_pioh.iot; 254 if (!bus_space_subregion(sc->sc_pioh.iot, 255 sc->sc_pioh.ioh, WDC_PCMCIA_AUXREG_OFFSET, 256 WDC_PCMCIA_AUXREG_NPORTS, &sc->sc_auxpioh.ioh)) 257 break; 258 } 259 pcmcia_io_free(pa->pf, &sc->sc_pioh); 260 } 261 262 /* 263 * Compact Flash memory mapped mode 264 * CF+ and CompactFlash Spec. Rev 1.4, 6.1.3 Memory Mapped Addressing. 265 * http://www.compactflash.org/cfspc1_4.pdf 266 */ 267 if (cfe == NULL) { 268 SIMPLEQ_FOREACH(cfe, &pa->pf->cfe_head, cfe_list) { 269 if (cfe->iftype != PCMCIA_IFTYPE_MEMORY) 270 continue; 271 if (pcmcia_mem_alloc(pa->pf, cfe->memspace[0].length, 272 &sc->sc_pmembaseh) == 0) { 273 sc->sc_flags |= WDC_PCMCIA_MEMMODE; 274 break; 275 } 276 } 277 } 278 279 if (cfe == NULL) { 280 printf(": can't handle card info\n"); 281 goto no_config_entry; 282 } 283 284 /* Enable the card. */ 285 pcmcia_function_init(pa->pf, cfe); 286 if (pcmcia_function_enable(pa->pf)) { 287 printf(": function enable failed\n"); 288 goto enable_failed; 289 } 290 291 wpp = wdc_pcmcia_lookup(pa); 292 if (wpp != NULL) 293 quirks = wpp->wpp_quirk_flag; 294 else 295 quirks = 0; 296 297 if (sc->sc_flags & WDC_PCMCIA_MEMMODE) { 298 if (pcmcia_mem_map(pa->pf, PCMCIA_MEM_COMMON, 0, 299 sc->sc_pmembaseh.size, &sc->sc_pmembaseh, &offset, 300 &sc->sc_memwindow)) { 301 printf(": can't map memory space\n"); 302 goto map_failed; 303 } 304 305 sc->sc_pmemh.memt = sc->sc_pmembaseh.memt; 306 if (offset == 0) { 307 sc->sc_pmemh.memh = sc->sc_pmembaseh.memh; 308 } else { 309 if (bus_space_subregion(sc->sc_pmemh.memt, 310 sc->sc_pmembaseh.memh, offset, 311 WDC_PCMCIA_REG_NPORTS, &sc->sc_pmemh.memh)) 312 goto mapaux_failed; 313 } 314 315 sc->sc_auxpmemh.memt = sc->sc_pmemh.memt; 316 if (bus_space_subregion(sc->sc_pmemh.memt, 317 sc->sc_pmemh.memh, WDC_PCMCIA_AUXREG_OFFSET, 318 WDC_PCMCIA_AUXREG_NPORTS, &sc->sc_auxpmemh.memh)) 319 goto mapaux_failed; 320 321 printf(" memory mapped mode"); 322 } else { 323 if (pcmcia_io_map(pa->pf, PCMCIA_WIDTH_AUTO, 0, 324 sc->sc_pioh.size, &sc->sc_pioh, &sc->sc_iowindow)) { 325 printf(": can't map first I/O space\n"); 326 goto map_failed; 327 } 328 } 329 330 if (cfe->num_iospace <= 1 || sc->sc_flags & WDC_PCMCIA_MEMMODE) 331 sc->sc_auxiowindow = -1; 332 else if (pcmcia_io_map(pa->pf, PCMCIA_WIDTH_AUTO, 0, 333 sc->sc_auxpioh.size, &sc->sc_auxpioh, &sc->sc_auxiowindow)) { 334 printf(": can't map second I/O space\n"); 335 goto mapaux_failed; 336 } 337 338 if ((wpp != NULL) && (wpp->wpp_name != NULL)) 339 printf(": %s", wpp->wpp_name); 340 341 printf("\n"); 342 343 sc->sc_wdcdev.cap |= WDC_CAPABILITY_DATA16; 344 if (sc->sc_flags & WDC_PCMCIA_MEMMODE) { 345 sc->wdc_channel.cmd_iot = sc->sc_pmemh.memt; 346 sc->wdc_channel.cmd_ioh = sc->sc_pmemh.memh; 347 sc->wdc_channel.ctl_iot = sc->sc_auxpmemh.memt; 348 sc->wdc_channel.ctl_ioh = sc->sc_auxpmemh.memh; 349 } else { 350 sc->wdc_channel.cmd_iot = sc->sc_pioh.iot; 351 sc->wdc_channel.cmd_ioh = sc->sc_pioh.ioh; 352 sc->wdc_channel.ctl_iot = sc->sc_auxpioh.iot; 353 sc->wdc_channel.ctl_ioh = sc->sc_auxpioh.ioh; 354 sc->sc_wdcdev.cap |= WDC_CAPABILITY_DATA32; 355 } 356 sc->wdc_channel.data32iot = sc->wdc_channel.cmd_iot; 357 sc->wdc_channel.data32ioh = sc->wdc_channel.cmd_ioh; 358 sc->sc_wdcdev.cap |= WDC_CAPABILITY_SINGLE_DRIVE; 359 sc->sc_wdcdev.PIO_cap = 0; 360 sc->wdc_chanptr = &sc->wdc_channel; 361 sc->sc_wdcdev.channels = &sc->wdc_chanptr; 362 sc->sc_wdcdev.nchannels = 1; 363 sc->wdc_channel.channel = 0; 364 sc->wdc_channel.wdc = &sc->sc_wdcdev; 365 sc->wdc_channel.ch_queue = malloc(sizeof(struct channel_queue), 366 M_DEVBUF, M_NOWAIT); 367 if (sc->wdc_channel.ch_queue == NULL) { 368 printf("%s: can't allocate memory for command queue\n", 369 sc->sc_wdcdev.sc_dev.dv_xname); 370 goto ch_queue_alloc_failed; 371 } 372 if (quirks & WDC_PCMCIA_NO_EXTRA_RESETS) 373 sc->sc_wdcdev.cap |= WDC_CAPABILITY_NO_EXTRA_RESETS; 374 375 /* We can enable and disable the controller. */ 376 sc->sc_wdcdev.sc_atapi_adapter._generic.adapt_enable = 377 wdc_pcmcia_enable; 378 379 sc->sc_flags |= WDC_PCMCIA_ATTACH; 380 wdcattach(&sc->wdc_channel); /* should return an error XXX */ 381 sc->sc_flags &= ~WDC_PCMCIA_ATTACH; 382 return; 383 384 ch_queue_alloc_failed: 385 /* Unmap our aux i/o window. */ 386 if (!(sc->sc_flags & WDC_PCMCIA_MEMMODE) && (sc->sc_auxiowindow != -1)) 387 pcmcia_io_unmap(sc->sc_pf, sc->sc_auxiowindow); 388 389 mapaux_failed: 390 /* Unmap our i/o window. */ 391 if (sc->sc_flags & WDC_PCMCIA_MEMMODE) 392 pcmcia_mem_unmap(sc->sc_pf, sc->sc_memwindow); 393 else 394 pcmcia_io_unmap(sc->sc_pf, sc->sc_iowindow); 395 396 map_failed: 397 /* Disable the function */ 398 pcmcia_function_disable(sc->sc_pf); 399 400 enable_failed: 401 /* Unmap our i/o space. */ 402 if (sc->sc_flags & WDC_PCMCIA_MEMMODE) { 403 pcmcia_mem_free(sc->sc_pf, &sc->sc_pmembaseh); 404 } else { 405 pcmcia_io_free(sc->sc_pf, &sc->sc_pioh); 406 if (cfe->num_iospace == 2) 407 pcmcia_io_free(sc->sc_pf, &sc->sc_auxpioh); 408 } 409 no_config_entry: 410 sc->sc_iowindow = -1; 411 } 412 413 int 414 wdc_pcmcia_detach(self, flags) 415 struct device *self; 416 int flags; 417 { 418 struct wdc_pcmcia_softc *sc = (struct wdc_pcmcia_softc *)self; 419 int error; 420 421 if (sc->sc_iowindow == -1) 422 /* Nothing to detach */ 423 return (0); 424 425 if ((error = wdcdetach(self, flags)) != 0) 426 return (error); 427 428 if (sc->wdc_channel.ch_queue != NULL) 429 free(sc->wdc_channel.ch_queue, M_DEVBUF); 430 431 /* Unmap our i/o window and i/o space. */ 432 if (sc->sc_flags & WDC_PCMCIA_MEMMODE) { 433 pcmcia_mem_unmap(sc->sc_pf, sc->sc_memwindow); 434 pcmcia_mem_free(sc->sc_pf, &sc->sc_pmembaseh); 435 } else { 436 pcmcia_io_unmap(sc->sc_pf, sc->sc_iowindow); 437 pcmcia_io_free(sc->sc_pf, &sc->sc_pioh); 438 if (sc->sc_auxiowindow != -1) { 439 pcmcia_io_unmap(sc->sc_pf, sc->sc_auxiowindow); 440 pcmcia_io_free(sc->sc_pf, &sc->sc_auxpioh); 441 } 442 } 443 444 return (0); 445 } 446 447 int 448 wdc_pcmcia_enable(self, onoff) 449 struct device *self; 450 int onoff; 451 { 452 struct wdc_pcmcia_softc *sc = (void *)self; 453 454 if (onoff) { 455 /* See the comment in aic_pcmcia_enable */ 456 if ((sc->sc_flags & WDC_PCMCIA_ATTACH) == 0) { 457 /* Establish the interrupt handler. */ 458 sc->sc_ih = pcmcia_intr_establish(sc->sc_pf, IPL_BIO, 459 wdcintr, &sc->wdc_channel); 460 if (sc->sc_ih == NULL) { 461 printf("%s: " 462 "couldn't establish interrupt handler\n", 463 sc->sc_wdcdev.sc_dev.dv_xname); 464 return (EIO); 465 } 466 467 if (pcmcia_function_enable(sc->sc_pf)) { 468 printf("%s: couldn't enable PCMCIA function\n", 469 sc->sc_wdcdev.sc_dev.dv_xname); 470 pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih); 471 return (EIO); 472 } 473 wdcreset(&sc->wdc_channel, VERBOSE); 474 } 475 } else { 476 pcmcia_function_disable(sc->sc_pf); 477 if ((sc->sc_flags & WDC_PCMCIA_ATTACH) == 0) 478 pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih); 479 } 480 481 return (0); 482 } 483