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