xref: /openbsd/sys/dev/pcmcia/wdc_pcmcia.c (revision 404b540a)
1 /*	$OpenBSD: wdc_pcmcia.c,v 1.21 2009/10/13 19:33:17 pirofti Exp $	*/
2 /*	$NetBSD: wdc_pcmcia.c,v 1.19 1999/02/19 21:49:43 abs Exp $ */
3 
4 /*-
5  * Copyright (c) 1998 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Charles M. Hannum, by Onno van der Linden and by Manuel Bouyer.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/kernel.h>
36 #include <sys/conf.h>
37 #include <sys/file.h>
38 #include <sys/stat.h>
39 #include <sys/ioctl.h>
40 #include <sys/buf.h>
41 #include <sys/uio.h>
42 #include <sys/malloc.h>
43 #include <sys/device.h>
44 #include <sys/disklabel.h>
45 #include <sys/disk.h>
46 #include <sys/syslog.h>
47 #include <sys/proc.h>
48 
49 #include <uvm/uvm_extern.h>
50 
51 #include <machine/cpu.h>
52 #include <machine/intr.h>
53 #include <machine/bus.h>
54 
55 #include <dev/pcmcia/pcmciareg.h>
56 #include <dev/pcmcia/pcmciavar.h>
57 #include <dev/pcmcia/pcmciadevs.h>
58 
59 #include <dev/ata/atavar.h>
60 #include <dev/ic/wdcvar.h>
61 
62 #define WDC_PCMCIA_REG_NPORTS      8
63 #define WDC_PCMCIA_AUXREG_OFFSET   (WDC_PCMCIA_REG_NPORTS + 6)
64 #define WDC_PCMCIA_AUXREG_NPORTS   2
65 
66 struct wdc_pcmcia_softc {
67 	struct wdc_softc sc_wdcdev;
68 	struct channel_softc *wdc_chanptr;
69 	struct channel_softc wdc_channel;
70 	struct pcmcia_io_handle sc_pioh;
71 	struct pcmcia_io_handle sc_auxpioh;
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 };
79 
80 static int wdc_pcmcia_match(struct device *, void *, void *);
81 static void wdc_pcmcia_attach(struct device *, struct device *, void *);
82 int    wdc_pcmcia_detach(struct device *, int);
83 int    wdc_pcmcia_activate(struct device *, int);
84 
85 struct cfattach wdc_pcmcia_ca = {
86 	sizeof(struct wdc_pcmcia_softc), wdc_pcmcia_match, wdc_pcmcia_attach,
87 	wdc_pcmcia_detach, wdc_pcmcia_activate
88 };
89 
90 struct wdc_pcmcia_product {
91 	u_int16_t	wpp_vendor;	/* vendor ID */
92 	u_int16_t	wpp_product;	/* product ID */
93 	int		wpp_quirk_flag;	/* Quirk flags */
94 #define WDC_PCMCIA_FORCE_16BIT_IO	0x01 /* Don't use PCMCIA_WIDTH_AUTO */
95 #define WDC_PCMCIA_NO_EXTRA_RESETS	0x02 /* Only reset ctrl once */
96 	const char	*wpp_cis_info[4];	/* XXX necessary? */
97 } wdc_pcmcia_pr[] = {
98 
99 	{ /* PCMCIA_VENDOR_DIGITAL XXX */ 0x0100,
100 	  PCMCIA_PRODUCT_DIGITAL_MOBILE_MEDIA_CDROM,
101 	  0, { NULL, "Digital Mobile Media CD-ROM", NULL, NULL }, },
102 
103 	{ PCMCIA_VENDOR_IBM, PCMCIA_PRODUCT_IBM_PORTABLE_CDROM,
104 	  0, { NULL, "Portable CD-ROM Drive", NULL, NULL }, },
105 
106 	{ PCMCIA_VENDOR_HAGIWARASYSCOM, PCMCIA_PRODUCT_INVALID,	/* XXX */
107 	  WDC_PCMCIA_FORCE_16BIT_IO, { NULL, NULL, NULL, NULL }, },
108 
109 	/* The TEAC IDE/Card II is used on the Sony Vaio */
110 	{ PCMCIA_VENDOR_TEAC, PCMCIA_PRODUCT_TEAC_IDECARDII,
111 	  WDC_PCMCIA_NO_EXTRA_RESETS, PCMCIA_CIS_TEAC_IDECARDII },
112 
113 	/*
114 	 * EXP IDE/ATAPI DVD Card use with some DVD players.
115 	 * Does not have a vendor ID or product ID.
116 	 */
117 	{ PCMCIA_VENDOR_INVALID, PCMCIA_PRODUCT_INVALID,
118 	  0, PCMCIA_CIS_EXP_EXPMULTIMEDIA },
119 
120 	/* Mobile Dock 2, which doesn't have vendor ID nor product ID */
121 	{ PCMCIA_VENDOR_INVALID, PCMCIA_PRODUCT_INVALID,
122 	  0, PCMCIA_CIS_SHUTTLE_IDE_ATAPI },
123 
124 	/* Archos MiniCD */
125 	{ PCMCIA_VENDOR_ARCHOS, PCMCIA_PRODUCT_ARCHOS_ARC_ATAPI,
126 	  0, PCMCIA_CIS_ARCHOS_ARC_ATAPI },
127 };
128 
129 struct wdc_pcmcia_disk_device_interface_args {
130 	int ddi_type;		/* interface type */
131 	int ddi_reqfn;		/* function we are requesting iftype */
132 	int ddi_curfn;		/* function we are currently parsing in CIS */
133 };
134 
135 int	wdc_pcmcia_disk_device_interface_callback(struct pcmcia_tuple *,
136 	    void *);
137 int	wdc_pcmcia_disk_device_interface(struct pcmcia_function *);
138 struct wdc_pcmcia_product *
139 	wdc_pcmcia_lookup(struct pcmcia_attach_args *);
140 
141 int	wdc_pcmcia_enable(void *, int);
142 
143 int
144 wdc_pcmcia_disk_device_interface_callback(tuple, arg)
145 	struct pcmcia_tuple *tuple;
146 	void *arg;
147 {
148 	struct wdc_pcmcia_disk_device_interface_args *ddi = arg;
149 
150 	switch (tuple->code) {
151 	case PCMCIA_CISTPL_FUNCID:
152 		ddi->ddi_curfn++;
153 		break;
154 
155 	case PCMCIA_CISTPL_FUNCE:
156 		if (ddi->ddi_reqfn != ddi->ddi_curfn)
157 			break;
158 
159 		/* subcode (disk device interface), data (interface type) */
160 		if (tuple->length < 2)
161 			break;
162 
163 		/* check type */
164 		if (pcmcia_tuple_read_1(tuple, 0) !=
165 		    PCMCIA_TPLFE_TYPE_DISK_DEVICE_INTERFACE)
166 			break;
167 
168 		ddi->ddi_type = pcmcia_tuple_read_1(tuple, 1);
169 		return (1);
170 	}
171 	return (0);
172 }
173 
174 int
175 wdc_pcmcia_disk_device_interface(pf)
176 	struct pcmcia_function *pf;
177 {
178 	struct wdc_pcmcia_disk_device_interface_args ddi;
179 
180 	ddi.ddi_reqfn = pf->number;
181 	ddi.ddi_curfn = -1;
182 	if (pcmcia_scan_cis((struct device *)pf->sc,
183 	    wdc_pcmcia_disk_device_interface_callback, &ddi) > 0)
184 		return (ddi.ddi_type);
185 	else
186 		return (-1);
187 }
188 
189 struct wdc_pcmcia_product *
190 wdc_pcmcia_lookup(pa)
191 	struct pcmcia_attach_args *pa;
192 {
193 	struct wdc_pcmcia_product *wpp;
194 	int i, cis_match;
195 
196 	for (wpp = wdc_pcmcia_pr;
197 	    wpp < &wdc_pcmcia_pr[sizeof(wdc_pcmcia_pr)/sizeof(wdc_pcmcia_pr[0])];
198 	    wpp++)
199 		if ((wpp->wpp_vendor == PCMCIA_VENDOR_INVALID ||
200 		     pa->manufacturer == wpp->wpp_vendor) &&
201 		    (wpp->wpp_product == PCMCIA_PRODUCT_INVALID ||
202 		     pa->product == wpp->wpp_product)) {
203 			cis_match = 1;
204 			for (i = 0; i < 4; i++) {
205 				if (!(wpp->wpp_cis_info[i] == NULL ||
206 				      (pa->card->cis1_info[i] != NULL &&
207 				       strcmp(pa->card->cis1_info[i],
208 					      wpp->wpp_cis_info[i]) == 0)))
209 					cis_match = 0;
210 			}
211 			if (cis_match)
212 				return (wpp);
213 		}
214 
215 	return (NULL);
216 }
217 
218 static int
219 wdc_pcmcia_match(parent, match, aux)
220 	struct device *parent;
221 	void *match, *aux;
222 {
223 	struct pcmcia_attach_args *pa = aux;
224 	struct pcmcia_softc *sc;
225 	int iftype;
226 
227 	if (wdc_pcmcia_lookup(pa) != NULL)
228 		return (1);
229 
230 	if (pa->pf->function == PCMCIA_FUNCTION_DISK) {
231 		sc = pa->pf->sc;
232 
233 		pcmcia_chip_socket_enable(sc->pct, sc->pch);
234 		iftype = wdc_pcmcia_disk_device_interface(pa->pf);
235 		pcmcia_chip_socket_disable(sc->pct, sc->pch);
236 
237 		if (iftype == PCMCIA_TPLFE_DDI_PCCARD_ATA)
238 			return (1);
239 	}
240 
241 	return (0);
242 }
243 
244 static void
245 wdc_pcmcia_attach(parent, self, aux)
246 	struct device *parent;
247 	struct device *self;
248 	void *aux;
249 {
250 	struct wdc_pcmcia_softc *sc = (void *)self;
251 	struct pcmcia_attach_args *pa = aux;
252 	struct pcmcia_config_entry *cfe;
253 	struct wdc_pcmcia_product *wpp;
254 	const char *intrstr;
255 	int quirks;
256 
257 	sc->sc_pf = pa->pf;
258 
259 	for (cfe = SIMPLEQ_FIRST(&pa->pf->cfe_head); cfe != NULL;
260 	    cfe = SIMPLEQ_NEXT(cfe, cfe_list)) {
261 		if (cfe->num_iospace != 1 && cfe->num_iospace != 2)
262 			continue;
263 
264 		if (pcmcia_io_alloc(pa->pf, cfe->iospace[0].start,
265 		    cfe->iospace[0].length,
266 		    cfe->iospace[0].start == 0 ? cfe->iospace[0].length : 0,
267 		    &sc->sc_pioh))
268 			continue;
269 
270 		if (cfe->num_iospace == 2) {
271 			if (!pcmcia_io_alloc(pa->pf, cfe->iospace[1].start,
272 			    cfe->iospace[1].length, 0, &sc->sc_auxpioh))
273 				break;
274 		} else /* num_iospace == 1 */ {
275 			sc->sc_auxpioh.iot = sc->sc_pioh.iot;
276 			if (!bus_space_subregion(sc->sc_pioh.iot,
277 			    sc->sc_pioh.ioh, WDC_PCMCIA_AUXREG_OFFSET,
278 			    WDC_PCMCIA_AUXREG_NPORTS, &sc->sc_auxpioh.ioh))
279 				break;
280 		}
281 		pcmcia_io_free(pa->pf, &sc->sc_pioh);
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 	/*
297 	 * XXX  DEC Mobile Media CDROM is not yet tested whether it works
298 	 * XXX  with PCMCIA_WIDTH_IO16.  HAGIWARA SYS-COM HPC-CF32 doesn't
299 	 * XXX  work with PCMCIA_WIDTH_AUTO.
300 	 * XXX  CANON FC-8M (SANDISK SDCFB 8M) works for both _AUTO and IO16.
301 	 * XXX  So, here is temporary work around.
302 	 */
303 	wpp = wdc_pcmcia_lookup(pa);
304 	if (wpp != NULL)
305 		quirks = wpp->wpp_quirk_flag;
306 	else
307 		quirks = 0;
308 
309 	if (pcmcia_io_map(pa->pf, quirks & WDC_PCMCIA_FORCE_16BIT_IO ?
310 	    PCMCIA_WIDTH_IO16 : PCMCIA_WIDTH_AUTO, 0,
311 	    sc->sc_pioh.size, &sc->sc_pioh, &sc->sc_iowindow)) {
312 		printf(": can't map first i/o space\n");
313 		goto iomap_failed;
314 	}
315 
316 	/*
317 	 * Currently, # of iospace is 1 except DIGITAL Mobile Media CD-ROM.
318 	 * So whether the work around like above is necessary or not
319 	 * is unknown.  XXX.
320 	 */
321 	if (cfe->num_iospace <= 1)
322 		sc->sc_auxiowindow = -1;
323 	else if (pcmcia_io_map(pa->pf, PCMCIA_WIDTH_AUTO, 0,
324 	    sc->sc_auxpioh.size, &sc->sc_auxpioh, &sc->sc_auxiowindow)) {
325 		printf(": can't map second i/o space\n");
326 		goto iomapaux_failed;
327 	}
328 
329 	printf(" port 0x%lx/%lu",
330 	    sc->sc_pioh.addr, (u_long)sc->sc_pioh.size);
331 	if (cfe->num_iospace > 1 && sc->sc_auxpioh.size > 0)
332 		printf(",0x%lx/%lu",
333 		    sc->sc_auxpioh.addr, (u_long)sc->sc_auxpioh.size);
334 
335 	sc->wdc_channel.cmd_iot = sc->sc_pioh.iot;
336 	sc->wdc_channel.cmd_ioh = sc->sc_pioh.ioh;
337 	sc->wdc_channel.ctl_iot = sc->sc_auxpioh.iot;
338 	sc->wdc_channel.ctl_ioh = sc->sc_auxpioh.ioh;
339 	sc->wdc_channel.data32iot = sc->wdc_channel.cmd_iot;
340 	sc->wdc_channel.data32ioh = sc->wdc_channel.cmd_ioh;
341 	sc->sc_wdcdev.cap |= WDC_CAPABILITY_DATA16 | WDC_CAPABILITY_DATA32;
342 	sc->sc_wdcdev.PIO_cap = 0;
343 	sc->wdc_chanptr = &sc->wdc_channel;
344 	sc->sc_wdcdev.channels = &sc->wdc_chanptr;
345 	sc->sc_wdcdev.nchannels = 1;
346 	sc->wdc_channel.channel = 0;
347 	sc->wdc_channel.wdc = &sc->sc_wdcdev;
348 	sc->wdc_channel.ch_queue = malloc(sizeof(struct channel_queue),
349 	    M_DEVBUF, M_NOWAIT);
350 	if (sc->wdc_channel.ch_queue == NULL) {
351 		printf("can't allocate memory for command queue\n");
352 		goto ch_queue_alloc_failed;
353 	}
354 	if (quirks & WDC_PCMCIA_NO_EXTRA_RESETS)
355 		sc->sc_wdcdev.cap |= WDC_CAPABILITY_NO_EXTRA_RESETS;
356 
357 #ifdef notyet
358 	/* We can enable and disable the controller. */
359 	sc->sc_wdcdev.sc_atapi_adapter.scsipi_enable = wdc_pcmcia_enable;
360 
361 	/*
362 	 * Disable the pcmcia function now; wdcattach() will enable
363 	 * us again as it adds references to probe for children.
364 	 */
365 	pcmcia_function_disable(pa->pf);
366 #else
367 	/* Establish the interrupt handler. */
368 	sc->sc_ih = pcmcia_intr_establish(sc->sc_pf, IPL_BIO, wdcintr,
369 	    &sc->wdc_channel, sc->sc_wdcdev.sc_dev.dv_xname);
370 	intrstr = pcmcia_intr_string(sc->sc_pf, sc->sc_ih);
371 	if (*intrstr)
372 		printf(": %s", intrstr);
373 #endif
374 
375 	printf("\n");
376 
377 	sc->sc_flags |= WDC_PCMCIA_ATTACH;
378 	wdcattach(&sc->wdc_channel);
379 	wdc_print_current_modes(&sc->wdc_channel);
380 	sc->sc_flags &= ~WDC_PCMCIA_ATTACH;
381 	return;
382 
383  ch_queue_alloc_failed:
384         /* Unmap our aux i/o window. */
385         if (sc->sc_auxiowindow != -1)
386                 pcmcia_io_unmap(sc->sc_pf, sc->sc_auxiowindow);
387 
388  iomapaux_failed:
389         /* Unmap our i/o window. */
390         pcmcia_io_unmap(sc->sc_pf, sc->sc_iowindow);
391 
392  iomap_failed:
393         /* Disable the function */
394         pcmcia_function_disable(sc->sc_pf);
395 
396  enable_failed:
397         /* Unmap our i/o space. */
398         pcmcia_io_free(sc->sc_pf, &sc->sc_pioh);
399         if (cfe->num_iospace == 2)
400                 pcmcia_io_free(sc->sc_pf, &sc->sc_auxpioh);
401 
402  no_config_entry:
403         sc->sc_iowindow = -1;
404 }
405 
406 int
407 wdc_pcmcia_detach(self, flags)
408 	struct device *self;
409 	int  flags;
410 {
411         struct wdc_pcmcia_softc *sc = (struct wdc_pcmcia_softc *)self;
412         int error;
413 
414         if (sc->sc_iowindow == -1)
415                 /* Nothing to detach */
416                 return (0);
417 
418 	if ((error = wdcdetach(&sc->wdc_channel, flags)) != 0)
419 		return (error);
420 
421         if (sc->wdc_channel.ch_queue != NULL)
422                 free(sc->wdc_channel.ch_queue, M_DEVBUF);
423 
424         /* Unmap our i/o window and i/o space. */
425         pcmcia_io_unmap(sc->sc_pf, sc->sc_iowindow);
426         pcmcia_io_free(sc->sc_pf, &sc->sc_pioh);
427         if (sc->sc_auxiowindow != -1) {
428                 pcmcia_io_unmap(sc->sc_pf, sc->sc_auxiowindow);
429                 pcmcia_io_free(sc->sc_pf, &sc->sc_auxpioh);
430         }
431 
432         return (0);
433 }
434 
435 int
436 wdc_pcmcia_activate(self, act)
437 	struct device *self;
438 	int act;
439 {
440 	struct wdc_pcmcia_softc *sc = (struct wdc_pcmcia_softc *)self;
441 	int rv = 0, s;
442 
443 	if (sc->sc_iowindow == -1)
444 		/* Nothing to activate/deactivate. */
445 		return (0);
446 
447 	s = splbio();
448 	switch (act) {
449 	case DVACT_ACTIVATE:
450 		if (pcmcia_function_enable(sc->sc_pf)) {
451 			printf("%s: couldn't enable PCMCIA function\n",
452 			    sc->sc_wdcdev.sc_dev.dv_xname);
453 			rv = EIO;
454 			break;
455 		}
456 
457 		sc->sc_ih = pcmcia_intr_establish(sc->sc_pf, IPL_BIO,
458 		    wdcintr, &sc->wdc_channel, sc->sc_wdcdev.sc_dev.dv_xname);
459 		if (sc->sc_ih == NULL) {
460 			printf("%s: "
461 			    "couldn't establish interrupt handler\n",
462 			    sc->sc_wdcdev.sc_dev.dv_xname);
463 			pcmcia_function_disable(sc->sc_pf);
464 			rv = EIO;
465 			break;
466 		}
467 
468 		wdcreset(&sc->wdc_channel, VERBOSE);
469 		rv = wdcactivate(self, act);
470 		break;
471 
472 	case DVACT_DEACTIVATE:
473 		pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih);
474 		pcmcia_function_disable(sc->sc_pf);
475 		rv = wdcactivate(self, act);
476 		break;
477 	}
478 	splx(s);
479 	return (rv);
480 }
481 
482 #if 0
483 int
484 wdc_pcmcia_enable(arg, onoff)
485 	void *arg;
486 	int onoff;
487 {
488 	struct wdc_pcmcia_softc *sc = arg;
489 
490 	if (onoff) {
491                 if ((sc->sc_flags & WDC_PCMCIA_ATTACH) == 0) {
492 			if (pcmcia_function_enable(sc->sc_pf)) {
493 				printf("%s: couldn't enable PCMCIA function\n",
494 				    sc->sc_wdcdev.sc_dev.dv_xname);
495 				return (EIO);
496 			}
497 
498 			sc->sc_ih = pcmcia_intr_establish(sc->sc_pf, IPL_BIO,
499 			    wdcintr, &sc->wdc_channel, sc->sc_dev.dv_xname);
500 			if (sc->sc_ih == NULL) {
501 				printf("%s: "
502 				    "couldn't establish interrupt handler\n",
503 				    sc->sc_wdcdev.sc_dev.dv_xname);
504 				pcmcia_function_disable(sc->sc_pf);
505 				return (EIO);
506 			}
507 
508 			wdcreset(&sc->wdc_channel, VERBOSE);
509 		}
510 	} else {
511                 if ((sc->sc_flags & WDC_PCMCIA_ATTACH) == 0)
512 			pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih);
513 		pcmcia_function_disable(sc->sc_pf);
514 	}
515 
516 	return (0);
517 }
518 #endif
519