xref: /openbsd/sys/dev/pcmcia/wdc_pcmcia.c (revision 891d7ab6)
1 /*	$OpenBSD: wdc_pcmcia.c,v 1.27 2011/07/03 15:47:17 matthew 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
142 wdc_pcmcia_disk_device_interface_callback(tuple, arg)
143 	struct pcmcia_tuple *tuple;
144 	void *arg;
145 {
146 	struct wdc_pcmcia_disk_device_interface_args *ddi = arg;
147 
148 	switch (tuple->code) {
149 	case PCMCIA_CISTPL_FUNCID:
150 		ddi->ddi_curfn++;
151 		break;
152 
153 	case PCMCIA_CISTPL_FUNCE:
154 		if (ddi->ddi_reqfn != ddi->ddi_curfn)
155 			break;
156 
157 		/* subcode (disk device interface), data (interface type) */
158 		if (tuple->length < 2)
159 			break;
160 
161 		/* check type */
162 		if (pcmcia_tuple_read_1(tuple, 0) !=
163 		    PCMCIA_TPLFE_TYPE_DISK_DEVICE_INTERFACE)
164 			break;
165 
166 		ddi->ddi_type = pcmcia_tuple_read_1(tuple, 1);
167 		return (1);
168 	}
169 	return (0);
170 }
171 
172 int
173 wdc_pcmcia_disk_device_interface(pf)
174 	struct pcmcia_function *pf;
175 {
176 	struct wdc_pcmcia_disk_device_interface_args ddi;
177 
178 	ddi.ddi_reqfn = pf->number;
179 	ddi.ddi_curfn = -1;
180 	if (pcmcia_scan_cis((struct device *)pf->sc,
181 	    wdc_pcmcia_disk_device_interface_callback, &ddi) > 0)
182 		return (ddi.ddi_type);
183 	else
184 		return (-1);
185 }
186 
187 struct wdc_pcmcia_product *
188 wdc_pcmcia_lookup(pa)
189 	struct pcmcia_attach_args *pa;
190 {
191 	struct wdc_pcmcia_product *wpp;
192 	int i, cis_match;
193 
194 	for (wpp = wdc_pcmcia_pr;
195 	    wpp < &wdc_pcmcia_pr[nitems(wdc_pcmcia_pr)];
196 	    wpp++)
197 		if ((wpp->wpp_vendor == PCMCIA_VENDOR_INVALID ||
198 		     pa->manufacturer == wpp->wpp_vendor) &&
199 		    (wpp->wpp_product == PCMCIA_PRODUCT_INVALID ||
200 		     pa->product == wpp->wpp_product)) {
201 			cis_match = 1;
202 			for (i = 0; i < 4; i++) {
203 				if (!(wpp->wpp_cis_info[i] == NULL ||
204 				      (pa->card->cis1_info[i] != NULL &&
205 				       strcmp(pa->card->cis1_info[i],
206 					      wpp->wpp_cis_info[i]) == 0)))
207 					cis_match = 0;
208 			}
209 			if (cis_match)
210 				return (wpp);
211 		}
212 
213 	return (NULL);
214 }
215 
216 static int
217 wdc_pcmcia_match(parent, match, aux)
218 	struct device *parent;
219 	void *match, *aux;
220 {
221 	struct pcmcia_attach_args *pa = aux;
222 	struct pcmcia_softc *sc;
223 	int iftype;
224 
225 	if (wdc_pcmcia_lookup(pa) != NULL)
226 		return (1);
227 
228 	if (pa->pf->function == PCMCIA_FUNCTION_DISK) {
229 		sc = pa->pf->sc;
230 
231 		pcmcia_chip_socket_enable(sc->pct, sc->pch);
232 		iftype = wdc_pcmcia_disk_device_interface(pa->pf);
233 		pcmcia_chip_socket_disable(sc->pct, sc->pch);
234 
235 		if (iftype == PCMCIA_TPLFE_DDI_PCCARD_ATA)
236 			return (1);
237 	}
238 
239 	return (0);
240 }
241 
242 static void
243 wdc_pcmcia_attach(parent, self, aux)
244 	struct device *parent;
245 	struct device *self;
246 	void *aux;
247 {
248 	struct wdc_pcmcia_softc *sc = (void *)self;
249 	struct pcmcia_attach_args *pa = aux;
250 	struct pcmcia_config_entry *cfe;
251 	struct wdc_pcmcia_product *wpp;
252 	const char *intrstr;
253 	int quirks;
254 
255 	sc->sc_pf = pa->pf;
256 
257 	for (cfe = SIMPLEQ_FIRST(&pa->pf->cfe_head); cfe != NULL;
258 	    cfe = SIMPLEQ_NEXT(cfe, cfe_list)) {
259 		if (cfe->num_iospace != 1 && cfe->num_iospace != 2)
260 			continue;
261 
262 		if (pcmcia_io_alloc(pa->pf, cfe->iospace[0].start,
263 		    cfe->iospace[0].length,
264 		    cfe->iospace[0].start == 0 ? cfe->iospace[0].length : 0,
265 		    &sc->sc_pioh))
266 			continue;
267 
268 		if (cfe->num_iospace == 2) {
269 			if (!pcmcia_io_alloc(pa->pf, cfe->iospace[1].start,
270 			    cfe->iospace[1].length, 0, &sc->sc_auxpioh))
271 				break;
272 		} else /* num_iospace == 1 */ {
273 			sc->sc_auxpioh.iot = sc->sc_pioh.iot;
274 			if (!bus_space_subregion(sc->sc_pioh.iot,
275 			    sc->sc_pioh.ioh, WDC_PCMCIA_AUXREG_OFFSET,
276 			    WDC_PCMCIA_AUXREG_NPORTS, &sc->sc_auxpioh.ioh))
277 				break;
278 		}
279 		pcmcia_io_free(pa->pf, &sc->sc_pioh);
280 	}
281 
282 	if (cfe == NULL) {
283 		printf(": can't handle card info\n");
284 		goto no_config_entry;
285 	}
286 
287 	/* Enable the card. */
288 	pcmcia_function_init(pa->pf, cfe);
289 	if (pcmcia_function_enable(pa->pf)) {
290 		printf(": function enable failed\n");
291 		goto enable_failed;
292 	}
293 
294 	/*
295 	 * XXX  DEC Mobile Media CDROM is not yet tested whether it works
296 	 * XXX  with PCMCIA_WIDTH_IO16.  HAGIWARA SYS-COM HPC-CF32 doesn't
297 	 * XXX  work with PCMCIA_WIDTH_AUTO.
298 	 * XXX  CANON FC-8M (SANDISK SDCFB 8M) works for both _AUTO and IO16.
299 	 * XXX  So, here is temporary work around.
300 	 */
301 	wpp = wdc_pcmcia_lookup(pa);
302 	if (wpp != NULL)
303 		quirks = wpp->wpp_quirk_flag;
304 	else
305 		quirks = 0;
306 
307 	if (pcmcia_io_map(pa->pf, quirks & WDC_PCMCIA_FORCE_16BIT_IO ?
308 	    PCMCIA_WIDTH_IO16 : PCMCIA_WIDTH_AUTO, 0,
309 	    sc->sc_pioh.size, &sc->sc_pioh, &sc->sc_iowindow)) {
310 		printf(": can't map first i/o space\n");
311 		goto iomap_failed;
312 	}
313 
314 	/*
315 	 * Currently, # of iospace is 1 except DIGITAL Mobile Media CD-ROM.
316 	 * So whether the work around like above is necessary or not
317 	 * is unknown.  XXX.
318 	 */
319 	if (cfe->num_iospace <= 1)
320 		sc->sc_auxiowindow = -1;
321 	else if (pcmcia_io_map(pa->pf, PCMCIA_WIDTH_AUTO, 0,
322 	    sc->sc_auxpioh.size, &sc->sc_auxpioh, &sc->sc_auxiowindow)) {
323 		printf(": can't map second i/o space\n");
324 		goto iomapaux_failed;
325 	}
326 
327 	printf(" port 0x%lx/%lu",
328 	    sc->sc_pioh.addr, (u_long)sc->sc_pioh.size);
329 	if (cfe->num_iospace > 1 && sc->sc_auxpioh.size > 0)
330 		printf(",0x%lx/%lu",
331 		    sc->sc_auxpioh.addr, (u_long)sc->sc_auxpioh.size);
332 
333 	sc->wdc_channel.cmd_iot = sc->sc_pioh.iot;
334 	sc->wdc_channel.cmd_ioh = sc->sc_pioh.ioh;
335 	sc->wdc_channel.ctl_iot = sc->sc_auxpioh.iot;
336 	sc->wdc_channel.ctl_ioh = sc->sc_auxpioh.ioh;
337 	sc->wdc_channel.data32iot = sc->wdc_channel.cmd_iot;
338 	sc->wdc_channel.data32ioh = sc->wdc_channel.cmd_ioh;
339 	sc->sc_wdcdev.cap |= WDC_CAPABILITY_DATA16 | WDC_CAPABILITY_DATA32;
340 	sc->sc_wdcdev.PIO_cap = 0;
341 	sc->wdc_chanptr = &sc->wdc_channel;
342 	sc->sc_wdcdev.channels = &sc->wdc_chanptr;
343 	sc->sc_wdcdev.nchannels = 1;
344 	sc->wdc_channel.channel = 0;
345 	sc->wdc_channel.wdc = &sc->sc_wdcdev;
346 	sc->wdc_channel.ch_queue = wdc_alloc_queue();
347 	if (sc->wdc_channel.ch_queue == NULL) {
348 		printf("cannot allocate channel queue\n");
349 		goto ch_queue_alloc_failed;
350 	}
351 	if (quirks & WDC_PCMCIA_NO_EXTRA_RESETS)
352 		sc->sc_wdcdev.cap |= WDC_CAPABILITY_NO_EXTRA_RESETS;
353 
354 	/* Establish the interrupt handler. */
355 	sc->sc_ih = pcmcia_intr_establish(sc->sc_pf, IPL_BIO, wdcintr,
356 	    &sc->wdc_channel, sc->sc_wdcdev.sc_dev.dv_xname);
357 	intrstr = pcmcia_intr_string(sc->sc_pf, sc->sc_ih);
358 	if (*intrstr)
359 		printf(": %s", intrstr);
360 
361 	printf("\n");
362 
363 	sc->sc_flags |= WDC_PCMCIA_ATTACH;
364 	wdcattach(&sc->wdc_channel);
365 	wdc_print_current_modes(&sc->wdc_channel);
366 	sc->sc_flags &= ~WDC_PCMCIA_ATTACH;
367 	return;
368 
369  ch_queue_alloc_failed:
370         /* Unmap our aux i/o window. */
371         if (sc->sc_auxiowindow != -1)
372                 pcmcia_io_unmap(sc->sc_pf, sc->sc_auxiowindow);
373 
374  iomapaux_failed:
375         /* Unmap our i/o window. */
376         pcmcia_io_unmap(sc->sc_pf, sc->sc_iowindow);
377 
378  iomap_failed:
379         /* Disable the function */
380         pcmcia_function_disable(sc->sc_pf);
381 
382  enable_failed:
383         /* Unmap our i/o space. */
384         pcmcia_io_free(sc->sc_pf, &sc->sc_pioh);
385         if (cfe->num_iospace == 2)
386                 pcmcia_io_free(sc->sc_pf, &sc->sc_auxpioh);
387 
388  no_config_entry:
389         sc->sc_iowindow = -1;
390 }
391 
392 int
393 wdc_pcmcia_detach(self, flags)
394 	struct device *self;
395 	int  flags;
396 {
397         struct wdc_pcmcia_softc *sc = (struct wdc_pcmcia_softc *)self;
398         int error;
399 
400         if (sc->sc_iowindow == -1)
401                 /* Nothing to detach */
402                 return (0);
403 
404 	if ((error = wdcdetach(&sc->wdc_channel, flags)) != 0)
405 		return (error);
406 
407         if (sc->wdc_channel.ch_queue != NULL)
408                 wdc_free_queue(sc->wdc_channel.ch_queue);
409 
410         /* Unmap our i/o window and i/o space. */
411         pcmcia_io_unmap(sc->sc_pf, sc->sc_iowindow);
412         pcmcia_io_free(sc->sc_pf, &sc->sc_pioh);
413         if (sc->sc_auxiowindow != -1) {
414                 pcmcia_io_unmap(sc->sc_pf, sc->sc_auxiowindow);
415                 pcmcia_io_free(sc->sc_pf, &sc->sc_auxpioh);
416         }
417 
418         return (0);
419 }
420 
421 int
422 wdc_pcmcia_activate(self, act)
423 	struct device *self;
424 	int act;
425 {
426 	struct wdc_pcmcia_softc *sc = (struct wdc_pcmcia_softc *)self;
427 	int rv = 0;
428 
429 	if (sc->sc_iowindow == -1)
430 		/* Nothing to activate/deactivate. */
431 		return (0);
432 
433 	switch (act) {
434 	case DVACT_QUIESCE:
435 		rv = config_activate_children(self, act);
436 		break;
437 	case DVACT_SUSPEND:
438 		rv = config_activate_children(self, act);
439 		if (sc->sc_ih)
440 			pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih);
441 		sc->sc_ih = NULL;
442 		pcmcia_function_disable(sc->sc_pf);
443 		break;
444 	case DVACT_RESUME:
445 		pcmcia_function_enable(sc->sc_pf);
446 		sc->sc_ih = pcmcia_intr_establish(sc->sc_pf, IPL_BIO,
447 		    wdcintr, &sc->wdc_channel, sc->sc_wdcdev.sc_dev.dv_xname);
448 		wdcreset(&sc->wdc_channel, VERBOSE);
449 		rv = config_activate_children(self, act);
450 		break;
451 	case DVACT_DEACTIVATE:
452 		rv = config_activate_children(self, act);
453 		if (sc->sc_ih)
454 			pcmcia_intr_disestablish(sc->sc_pf, sc->sc_ih);
455 		sc->sc_ih = NULL;
456 		pcmcia_function_disable(sc->sc_pf);
457 		break;
458 	}
459 	return (rv);
460 }
461