1 /* $NetBSD: wdc_pcmcia.c,v 1.52 2002/10/02 16:52:22 thorpej 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.52 2002/10/02 16:52:22 thorpej 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 CFATTACH_DECL(wdc_pcmcia, sizeof(struct wdc_pcmcia_softc), 85 wdc_pcmcia_match, wdc_pcmcia_attach, wdc_pcmcia_detach, wdcactivate); 86 87 const struct wdc_pcmcia_product { 88 u_int32_t wpp_vendor; /* vendor ID */ 89 u_int32_t wpp_product; /* product ID */ 90 int wpp_quirk_flag; /* Quirk flags */ 91 #define WDC_PCMCIA_NO_EXTRA_RESETS 0x02 /* Only reset ctrl once */ 92 const char *wpp_cis_info[4]; /* XXX necessary? */ 93 const char *wpp_name; /* product name */ 94 } wdc_pcmcia_products[] = { 95 96 { /* PCMCIA_VENDOR_DIGITAL XXX */ 0x0100, 97 PCMCIA_PRODUCT_DIGITAL_MOBILE_MEDIA_CDROM, 98 0, { NULL, "Digital Mobile Media CD-ROM", NULL, NULL }, 99 PCMCIA_STR_DIGITAL_MOBILE_MEDIA_CDROM }, 100 101 { PCMCIA_VENDOR_IBM, 102 PCMCIA_PRODUCT_IBM_PORTABLE_CDROM, 103 0, { NULL, "PCMCIA Portable CD-ROM Drive", NULL, NULL }, 104 PCMCIA_STR_IBM_PORTABLE_CDROM }, 105 106 /* The TEAC IDE/Card II is used on the Sony Vaio */ 107 { PCMCIA_VENDOR_TEAC, 108 PCMCIA_PRODUCT_TEAC_IDECARDII, 109 WDC_PCMCIA_NO_EXTRA_RESETS, 110 PCMCIA_CIS_TEAC_IDECARDII, 111 PCMCIA_STR_TEAC_IDECARDII }, 112 113 /* 114 * A fujitsu rebranded panasonic drive that reports 115 * itself as function "scsi", disk interface 0 116 */ 117 { PCMCIA_VENDOR_PANASONIC, 118 PCMCIA_PRODUCT_PANASONIC_KXLC005, 119 0, 120 PCMCIA_CIS_PANASONIC_KXLC005, 121 PCMCIA_STR_PANASONIC_KXLC005 }, 122 123 /* 124 * EXP IDE/ATAPI DVD Card use with some DVD players. 125 * Does not have a vendor ID or product ID. 126 */ 127 { -1, 128 -1, 129 0, 130 PCMCIA_CIS_EXP_EXPMULTIMEDIA, 131 PCMCIA_STR_EXP_EXPMULTIMEDIA }, 132 133 /* Mobile Dock 2, neither vendor ID nor product ID */ 134 { -1, -1, 0, 135 { "SHUTTLE TECHNOLOGY LTD.", "PCCARD-IDE/ATAPI Adapter", NULL, NULL}, 136 "SHUTTLE TECHNOLOGY IDE/ATAPI Adapter" 137 }, 138 139 /* Toshiba Portege 3110 CD, neither vendor ID nor product ID */ 140 { -1, -1, 0, 141 { "FREECOM", "PCCARD-IDE", NULL, NULL}, 142 "FREECOM PCCARD-IDE" 143 }, 144 145 /* Random CD-ROM, (badged AMACOM), neither vendor ID nor product ID */ 146 { -1, -1, 0, 147 { "PCMCIA", "CD-ROM", NULL, NULL}, 148 "PCMCIA CD-ROM" 149 }, 150 151 /* IO DATA CBIDE2, with neither vendor ID nor product ID */ 152 { -1, -1, 0, 153 PCMCIA_CIS_IODATA_CBIDE2, 154 PCMCIA_STR_IODATA_CBIDE2 155 }, 156 157 /* 158 * Novac PCMCIA-IDE Card for HD530P IDE Box, 159 * with neither vendor ID nor product ID 160 */ 161 { -1, -1, 0, 162 { "PCMCIA", "PnPIDE", NULL, NULL}, 163 "Novac PCCARD-IDE" 164 }, 165 166 { 0, 0, 0, { NULL, NULL, NULL, NULL}, NULL } 167 }; 168 169 const struct wdc_pcmcia_product * 170 wdc_pcmcia_lookup __P((struct pcmcia_attach_args *)); 171 172 int wdc_pcmcia_enable __P((struct device *, int)); 173 174 const struct wdc_pcmcia_product * 175 wdc_pcmcia_lookup(pa) 176 struct pcmcia_attach_args *pa; 177 { 178 const struct wdc_pcmcia_product *wpp; 179 int i, cis_match; 180 181 for (wpp = wdc_pcmcia_products; wpp->wpp_name != NULL; wpp++) 182 if ((wpp->wpp_vendor == -1 || 183 pa->manufacturer == wpp->wpp_vendor) && 184 (wpp->wpp_product == -1 || 185 pa->product == wpp->wpp_product)) { 186 cis_match = 1; 187 for (i = 0; i < 4; i++) { 188 if (!(wpp->wpp_cis_info[i] == NULL || 189 (pa->card->cis1_info[i] != NULL && 190 strcmp(pa->card->cis1_info[i], 191 wpp->wpp_cis_info[i]) == 0))) 192 cis_match = 0; 193 } 194 if (cis_match) 195 return (wpp); 196 } 197 198 return (NULL); 199 } 200 201 static int 202 wdc_pcmcia_match(parent, match, aux) 203 struct device *parent; 204 struct cfdata *match; 205 void *aux; 206 { 207 struct pcmcia_attach_args *pa = aux; 208 209 if (pa->pf->function == PCMCIA_FUNCTION_DISK && 210 pa->pf->pf_funce_disk_interface == PCMCIA_TPLFE_DDI_PCCARD_ATA) { 211 return 10; 212 } 213 214 if (wdc_pcmcia_lookup(pa) != NULL) 215 return (1); 216 217 return (0); 218 } 219 220 static void 221 wdc_pcmcia_attach(parent, self, aux) 222 struct device *parent; 223 struct device *self; 224 void *aux; 225 { 226 struct wdc_pcmcia_softc *sc = (void *)self; 227 struct pcmcia_attach_args *pa = aux; 228 struct pcmcia_config_entry *cfe; 229 const struct wdc_pcmcia_product *wpp; 230 bus_size_t offset; 231 int quirks; 232 233 sc->sc_pf = pa->pf; 234 235 SIMPLEQ_FOREACH(cfe, &pa->pf->cfe_head, cfe_list) { 236 if (cfe->num_iospace != 1 && cfe->num_iospace != 2) 237 continue; 238 239 if (pcmcia_io_alloc(pa->pf, cfe->iospace[0].start, 240 cfe->iospace[0].length, 241 cfe->iospace[0].start == 0 ? cfe->iospace[0].length : 0, 242 &sc->sc_pioh)) 243 continue; 244 245 if (cfe->num_iospace == 2) { 246 if (!pcmcia_io_alloc(pa->pf, cfe->iospace[1].start, 247 cfe->iospace[1].length, 0, &sc->sc_auxpioh)) 248 break; 249 } else /* num_iospace == 1 */ { 250 sc->sc_auxpioh.iot = sc->sc_pioh.iot; 251 if (!bus_space_subregion(sc->sc_pioh.iot, 252 sc->sc_pioh.ioh, WDC_PCMCIA_AUXREG_OFFSET, 253 WDC_PCMCIA_AUXREG_NPORTS, &sc->sc_auxpioh.ioh)) 254 break; 255 } 256 pcmcia_io_free(pa->pf, &sc->sc_pioh); 257 } 258 259 /* 260 * Compact Flash memory mapped mode 261 * CF+ and CompactFlash Spec. Rev 1.4, 6.1.3 Memory Mapped Addressing. 262 * http://www.compactflash.org/cfspc1_4.pdf 263 */ 264 if (cfe == NULL) { 265 SIMPLEQ_FOREACH(cfe, &pa->pf->cfe_head, cfe_list) { 266 if (cfe->iftype != PCMCIA_IFTYPE_MEMORY) 267 continue; 268 if (pcmcia_mem_alloc(pa->pf, cfe->memspace[0].length, 269 &sc->sc_pmembaseh) == 0) { 270 sc->sc_flags |= WDC_PCMCIA_MEMMODE; 271 break; 272 } 273 } 274 } 275 276 if (cfe == NULL) { 277 printf(": can't handle card info\n"); 278 goto no_config_entry; 279 } 280 281 /* Enable the card. */ 282 pcmcia_function_init(pa->pf, cfe); 283 if (pcmcia_function_enable(pa->pf)) { 284 printf(": function enable failed\n"); 285 goto enable_failed; 286 } 287 288 wpp = wdc_pcmcia_lookup(pa); 289 if (wpp != NULL) 290 quirks = wpp->wpp_quirk_flag; 291 else 292 quirks = 0; 293 294 if (sc->sc_flags & WDC_PCMCIA_MEMMODE) { 295 if (pcmcia_mem_map(pa->pf, PCMCIA_MEM_COMMON, 0, 296 sc->sc_pmembaseh.size, &sc->sc_pmembaseh, &offset, 297 &sc->sc_memwindow)) { 298 printf(": can't map memory space\n"); 299 goto map_failed; 300 } 301 302 sc->sc_pmemh.memt = sc->sc_pmembaseh.memt; 303 if (offset == 0) { 304 sc->sc_pmemh.memh = sc->sc_pmembaseh.memh; 305 } else { 306 if (bus_space_subregion(sc->sc_pmemh.memt, 307 sc->sc_pmembaseh.memh, offset, 308 WDC_PCMCIA_REG_NPORTS, &sc->sc_pmemh.memh)) 309 goto mapaux_failed; 310 } 311 312 sc->sc_auxpmemh.memt = sc->sc_pmemh.memt; 313 if (bus_space_subregion(sc->sc_pmemh.memt, 314 sc->sc_pmemh.memh, WDC_PCMCIA_AUXREG_OFFSET, 315 WDC_PCMCIA_AUXREG_NPORTS, &sc->sc_auxpmemh.memh)) 316 goto mapaux_failed; 317 318 printf(" memory mapped mode"); 319 } else { 320 if (pcmcia_io_map(pa->pf, PCMCIA_WIDTH_AUTO, 0, 321 sc->sc_pioh.size, &sc->sc_pioh, &sc->sc_iowindow)) { 322 printf(": can't map first I/O space\n"); 323 goto map_failed; 324 } 325 } 326 327 if (cfe->num_iospace <= 1 || sc->sc_flags & WDC_PCMCIA_MEMMODE) 328 sc->sc_auxiowindow = -1; 329 else if (pcmcia_io_map(pa->pf, PCMCIA_WIDTH_AUTO, 0, 330 sc->sc_auxpioh.size, &sc->sc_auxpioh, &sc->sc_auxiowindow)) { 331 printf(": can't map second I/O space\n"); 332 goto mapaux_failed; 333 } 334 335 if ((wpp != NULL) && (wpp->wpp_name != NULL)) 336 printf(": %s", wpp->wpp_name); 337 338 printf("\n"); 339 340 sc->sc_wdcdev.cap |= WDC_CAPABILITY_DATA16; 341 if (sc->sc_flags & WDC_PCMCIA_MEMMODE) { 342 sc->wdc_channel.cmd_iot = sc->sc_pmemh.memt; 343 sc->wdc_channel.cmd_ioh = sc->sc_pmemh.memh; 344 sc->wdc_channel.ctl_iot = sc->sc_auxpmemh.memt; 345 sc->wdc_channel.ctl_ioh = sc->sc_auxpmemh.memh; 346 } else { 347 sc->wdc_channel.cmd_iot = sc->sc_pioh.iot; 348 sc->wdc_channel.cmd_ioh = sc->sc_pioh.ioh; 349 sc->wdc_channel.ctl_iot = sc->sc_auxpioh.iot; 350 sc->wdc_channel.ctl_ioh = sc->sc_auxpioh.ioh; 351 sc->sc_wdcdev.cap |= WDC_CAPABILITY_DATA32; 352 } 353 sc->wdc_channel.data32iot = sc->wdc_channel.cmd_iot; 354 sc->wdc_channel.data32ioh = sc->wdc_channel.cmd_ioh; 355 sc->sc_wdcdev.cap |= WDC_CAPABILITY_SINGLE_DRIVE; 356 sc->sc_wdcdev.PIO_cap = 0; 357 sc->wdc_chanptr = &sc->wdc_channel; 358 sc->sc_wdcdev.channels = &sc->wdc_chanptr; 359 sc->sc_wdcdev.nchannels = 1; 360 sc->wdc_channel.channel = 0; 361 sc->wdc_channel.wdc = &sc->sc_wdcdev; 362 sc->wdc_channel.ch_queue = malloc(sizeof(struct channel_queue), 363 M_DEVBUF, M_NOWAIT); 364 if (sc->wdc_channel.ch_queue == NULL) { 365 printf("%s: can't allocate memory for command queue\n", 366 sc->sc_wdcdev.sc_dev.dv_xname); 367 goto ch_queue_alloc_failed; 368 } 369 if (quirks & WDC_PCMCIA_NO_EXTRA_RESETS) 370 sc->sc_wdcdev.cap |= WDC_CAPABILITY_NO_EXTRA_RESETS; 371 372 /* We can enable and disable the controller. */ 373 sc->sc_wdcdev.sc_atapi_adapter._generic.adapt_enable = 374 wdc_pcmcia_enable; 375 376 sc->sc_flags |= WDC_PCMCIA_ATTACH; 377 wdcattach(&sc->wdc_channel); /* should return an error XXX */ 378 sc->sc_flags &= ~WDC_PCMCIA_ATTACH; 379 return; 380 381 ch_queue_alloc_failed: 382 /* Unmap our aux i/o window. */ 383 if (!(sc->sc_flags & WDC_PCMCIA_MEMMODE) && (sc->sc_auxiowindow != -1)) 384 pcmcia_io_unmap(sc->sc_pf, sc->sc_auxiowindow); 385 386 mapaux_failed: 387 /* Unmap our i/o window. */ 388 if (sc->sc_flags & WDC_PCMCIA_MEMMODE) 389 pcmcia_mem_unmap(sc->sc_pf, sc->sc_memwindow); 390 else 391 pcmcia_io_unmap(sc->sc_pf, sc->sc_iowindow); 392 393 map_failed: 394 /* Disable the function */ 395 pcmcia_function_disable(sc->sc_pf); 396 397 enable_failed: 398 /* Unmap our i/o space. */ 399 if (sc->sc_flags & WDC_PCMCIA_MEMMODE) { 400 pcmcia_mem_free(sc->sc_pf, &sc->sc_pmembaseh); 401 } else { 402 pcmcia_io_free(sc->sc_pf, &sc->sc_pioh); 403 if (cfe->num_iospace == 2) 404 pcmcia_io_free(sc->sc_pf, &sc->sc_auxpioh); 405 } 406 no_config_entry: 407 sc->sc_iowindow = -1; 408 } 409 410 int 411 wdc_pcmcia_detach(self, flags) 412 struct device *self; 413 int flags; 414 { 415 struct wdc_pcmcia_softc *sc = (struct wdc_pcmcia_softc *)self; 416 int error; 417 418 if (sc->sc_iowindow == -1) 419 /* Nothing to detach */ 420 return (0); 421 422 if ((error = wdcdetach(self, flags)) != 0) 423 return (error); 424 425 if (sc->wdc_channel.ch_queue != NULL) 426 free(sc->wdc_channel.ch_queue, M_DEVBUF); 427 428 /* Unmap our i/o window and i/o space. */ 429 if (sc->sc_flags & WDC_PCMCIA_MEMMODE) { 430 pcmcia_mem_unmap(sc->sc_pf, sc->sc_memwindow); 431 pcmcia_mem_free(sc->sc_pf, &sc->sc_pmembaseh); 432 } else { 433 pcmcia_io_unmap(sc->sc_pf, sc->sc_iowindow); 434 pcmcia_io_free(sc->sc_pf, &sc->sc_pioh); 435 if (sc->sc_auxiowindow != -1) { 436 pcmcia_io_unmap(sc->sc_pf, sc->sc_auxiowindow); 437 pcmcia_io_free(sc->sc_pf, &sc->sc_auxpioh); 438 } 439 } 440 441 return (0); 442 } 443 444 int 445 wdc_pcmcia_enable(self, onoff) 446 struct device *self; 447 int onoff; 448 { 449 struct wdc_pcmcia_softc *sc = (void *)self; 450 451 if (onoff) { 452 /* See the comment in aic_pcmcia_enable */ 453 if ((sc->sc_flags & WDC_PCMCIA_ATTACH) == 0) { 454 /* Establish the interrupt handler. */ 455 sc->sc_ih = pcmcia_intr_establish(sc->sc_pf, IPL_BIO, 456 wdcintr, &sc->wdc_channel); 457 if (sc->sc_ih == NULL) { 458 printf("%s: " 459 "couldn't establish interrupt handler\n", 460 sc->sc_wdcdev.sc_dev.dv_xname); 461 return (EIO); 462 } 463 464 if (pcmcia_function_enable(sc->sc_pf)) { 465 printf("%s: couldn't enable PCMCIA function\n", 466 sc->sc_wdcdev.sc_dev.dv_xname); 467 pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih); 468 return (EIO); 469 } 470 wdcreset(&sc->wdc_channel, VERBOSE); 471 } 472 } else { 473 pcmcia_function_disable(sc->sc_pf); 474 if ((sc->sc_flags & WDC_PCMCIA_ATTACH) == 0) 475 pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih); 476 } 477 478 return (0); 479 } 480