pccard.c revision 55500
1/*	$NetBSD: pcmcia.c,v 1.13 1998/12/24 04:51:59 marc Exp $	*/
2/* $FreeBSD: head/sys/dev/pccard/pccard.c 55500 2000-01-06 07:30:28Z imp $ */
3
4/*
5 * Copyright (c) 1997 Marc Horowitz.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *	This product includes software developed by Marc Horowitz.
18 * 4. The name of the author may not be used to endorse or promote products
19 *    derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include <sys/param.h>
34#include <sys/systm.h>
35#include <sys/malloc.h>
36#include <sys/module.h>
37#include <sys/kernel.h>
38#include <sys/queue.h>
39#include <sys/types.h>
40
41#include <sys/bus.h>
42#include <machine/bus.h>
43#include <sys/rman.h>
44#include <machine/resource.h>
45
46#include <dev/pccard/pccardreg.h>
47#include <dev/pccard/pccardchip.h>
48#include <dev/pccard/pccardvar.h>
49
50#include "power_if.h"
51
52#define PCCARDDEBUG
53
54#ifdef PCCARDDEBUG
55int	pccard_debug = 1;
56#define	DPRINTF(arg) if (pccard_debug) printf arg
57#define	DEVPRINTF(arg) if (pccard_debug) device_printf arg
58int	pccardintr_debug = 0;
59/* this is done this way to avoid doing lots of conditionals
60   at interrupt level.  */
61#define PCCARD_CARD_INTR (pccardintr_debug?pccard_card_intrdebug:pccard_card_intr)
62#else
63#define	DPRINTF(arg)
64#define	DEVPRINTF(arg)
65#define PCCARD_CARD_INTR (pccard_card_intr)
66#endif
67
68#ifdef PCCARDVERBOSE
69int	pccard_verbose = 1;
70#else
71int	pccard_verbose = 0;
72#endif
73
74int	pccard_print(void *, const char *);
75
76int pccard_card_intr(void *);
77#ifdef PCCARDDEBUG
78int pccard_card_intrdebug(void *);
79#endif
80
81/* XXX Shouldn't be touching hardware, that's a layering violation */
82/* XXX imp */
83int
84pccard_ccr_read(pf, ccr)
85	struct pccard_function *pf;
86	int ccr;
87{
88	return (bus_space_read_1(pf->pf_ccrt, pf->pf_ccrh,
89	    pf->pf_ccr_offset + ccr));
90}
91
92void
93pccard_ccr_write(pf, ccr, val)
94	struct pccard_function *pf;
95	int ccr;
96	int val;
97{
98
99	if ((pf->ccr_mask) & (1 << (ccr / 2))) {
100		bus_space_write_1(pf->pf_ccrt, pf->pf_ccrh,
101		    pf->pf_ccr_offset + ccr, val);
102	}
103}
104
105int
106pccard_card_attach(device_t dev)
107{
108	struct pccard_softc *sc = (struct pccard_softc *)
109	    device_get_softc(dev);
110	struct pccard_function *pf;
111	int attached;
112
113	DEVPRINTF((dev, "pccard_card_attach\n"));
114	/*
115	 * this is here so that when socket_enable calls gettype, trt happens
116	 */
117	STAILQ_INIT(&sc->card.pf_head);
118
119	DEVPRINTF((dev, "chip_socket_enable\n"));
120	POWER_ENABLE_SOCKET(device_get_parent(dev), dev);
121
122	DEVPRINTF((dev, "read_cis\n"));
123	pccard_read_cis(sc);
124
125	DEVPRINTF((dev, "chip_socket_disable\n"));
126	POWER_DISABLE_SOCKET(device_get_parent(dev), dev);
127
128	DEVPRINTF((dev, "check_cis_quirks\n"));
129	pccard_check_cis_quirks(dev);
130
131	/*
132	 * bail now if the card has no functions, or if there was an error in
133	 * the cis.
134	 */
135
136	if (sc->card.error)
137		return (1);
138	if (STAILQ_EMPTY(&sc->card.pf_head))
139		return (1);
140
141	if (pccard_verbose)
142		pccard_print_cis(dev);
143
144	attached = 0;
145
146	DEVPRINTF((dev, "functions scanning\n"));
147	STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) {
148		if (STAILQ_EMPTY(&pf->cfe_head))
149			continue;
150
151#ifdef DIAGNOSTIC
152		if (pf->child != NULL) {
153			device_printf(sc->dev,
154			    "%s still attached to function %d!\n",
155			    device_get_name(pf->child), pf->number);
156			panic("pccard_card_attach");
157		}
158#endif
159		pf->sc = sc;
160		pf->child = NULL;
161		pf->cfe = NULL;
162		pf->ih_fct = NULL;
163		pf->ih_arg = NULL;
164	}
165
166	STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) {
167		if (STAILQ_EMPTY(&pf->cfe_head))
168			continue;
169
170#if XXX
171		if (attach_child()) {
172			attached++;
173
174			DEVPRINTF((sc->dev, "function %d CCR at %d "
175			     "offset %lx: %x %x %x %x, %x %x %x %x, %x\n",
176			     pf->number, pf->pf_ccr_window, pf->pf_ccr_offset,
177			     pccard_ccr_read(pf, 0x00),
178			pccard_ccr_read(pf, 0x02), pccard_ccr_read(pf, 0x04),
179			pccard_ccr_read(pf, 0x06), pccard_ccr_read(pf, 0x0A),
180			pccard_ccr_read(pf, 0x0C), pccard_ccr_read(pf, 0x0E),
181			pccard_ccr_read(pf, 0x10), pccard_ccr_read(pf, 0x12)));
182		}
183#endif
184	}
185
186	return (attached ? 0 : 1);
187}
188
189void
190pccard_card_detach(device_t dev, int flags)
191{
192	struct pccard_softc *sc = (struct pccard_softc *)
193	    device_get_softc(dev);
194	struct pccard_function *pf;
195#if XXX
196	int error;
197#endif
198
199	/*
200	 * We are running on either the PCCARD socket's event thread
201	 * or in user context detaching a device by user request.
202	 */
203	STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) {
204		if (STAILQ_FIRST(&pf->cfe_head) == NULL)
205			continue;
206		if (pf->child == NULL)
207			continue;
208		DEVPRINTF((sc->dev, "detaching %s (function %d)\n",
209		    device_get_name(pf->child), pf->number));
210#if XXX
211		if ((error = config_detach(pf->child, flags)) != 0) {
212			device_printf(sc->dev,
213			    "error %d detaching %s (function %d)\n",
214			    error, device_get_name(pf->child), pf->number);
215		} else
216			pf->child = NULL;
217#endif
218	}
219}
220
221void
222pccard_card_deactivate(device_t dev)
223{
224	struct pccard_softc *sc = (struct pccard_softc *)
225	    device_get_softc(dev);
226	struct pccard_function *pf;
227
228	/*
229	 * We're in the chip's card removal interrupt handler.
230	 * Deactivate the child driver.  The PCCARD socket's
231	 * event thread will run later to finish the detach.
232	 */
233	STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) {
234		if (STAILQ_FIRST(&pf->cfe_head) == NULL)
235			continue;
236		if (pf->child == NULL)
237			continue;
238		DEVPRINTF((sc->dev, "deactivating %s (function %d)\n",
239		    device_get_name(pf->child), pf->number));
240#if XXX
241		config_deactivate(pf->child);
242#endif
243	}
244}
245
246int
247pccard_card_gettype(device_t dev)
248{
249	struct pccard_softc *sc = (struct pccard_softc *)
250	    device_get_softc(dev);
251	struct pccard_function *pf;
252
253	/*
254	 * set the iftype to memory if this card has no functions (not yet
255	 * probed), or only one function, and that is not initialized yet or
256	 * that is memory.
257	 */
258	pf = STAILQ_FIRST(&sc->card.pf_head);
259	if (pf == NULL ||
260	    (STAILQ_NEXT(pf, pf_list) == NULL &&
261	    (pf->cfe == NULL || pf->cfe->iftype == PCCARD_IFTYPE_MEMORY)))
262		return (PCCARD_IFTYPE_MEMORY);
263	else
264		return (PCCARD_IFTYPE_IO);
265}
266
267/*
268 * Initialize a PCCARD function.  May be called as long as the function is
269 * disabled.
270 */
271void
272pccard_function_init(pf, cfe)
273	struct pccard_function *pf;
274	struct pccard_config_entry *cfe;
275{
276	if (pf->pf_flags & PFF_ENABLED)
277		panic("pccard_function_init: function is enabled");
278
279	/* Remember which configuration entry we are using. */
280	pf->cfe = cfe;
281}
282
283/* Enable a PCCARD function */
284int
285pccard_function_enable(pf)
286	struct pccard_function *pf;
287{
288	struct pccard_function *tmp;
289	int reg;
290
291	if (pf->cfe == NULL)
292		panic("pccard_function_enable: function not initialized");
293
294	/*
295	 * Increase the reference count on the socket, enabling power, if
296	 * necessary.
297	 */
298	if (pf->sc->sc_enabled_count++ == 0)
299		POWER_ENABLE_SOCKET(device_get_parent(pf->sc->dev),
300		    pf->sc->dev);
301	DEVPRINTF((pf->sc->dev, "++enabled_count = %d\n",
302	    pf->sc->sc_enabled_count));
303
304	if (pf->pf_flags & PFF_ENABLED) {
305		/*
306		 * Don't do anything if we're already enabled.
307		 */
308		return (0);
309	}
310
311	/*
312	 * it's possible for different functions' CCRs to be in the same
313	 * underlying page.  Check for that.
314	 */
315
316	STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) {
317		if ((tmp->pf_flags & PFF_ENABLED) &&
318		    (pf->ccr_base >= (tmp->ccr_base - tmp->pf_ccr_offset)) &&
319		    ((pf->ccr_base + PCCARD_CCR_SIZE) <=
320		     (tmp->ccr_base - tmp->pf_ccr_offset +
321		      tmp->pf_ccr_realsize))) {
322			pf->pf_ccrt = tmp->pf_ccrt;
323			pf->pf_ccrh = tmp->pf_ccrh;
324			pf->pf_ccr_realsize = tmp->pf_ccr_realsize;
325
326			/*
327			 * pf->pf_ccr_offset = (tmp->pf_ccr_offset -
328			 * tmp->ccr_base) + pf->ccr_base;
329			 */
330			pf->pf_ccr_offset =
331			    (tmp->pf_ccr_offset + pf->ccr_base) -
332			    tmp->ccr_base;
333			pf->pf_ccr_window = tmp->pf_ccr_window;
334			break;
335		}
336	}
337
338	if (tmp == NULL) {
339		if (pccard_mem_alloc(pf, PCCARD_CCR_SIZE, &pf->pf_pcmh))
340			goto bad;
341
342		if (pccard_mem_map(pf, PCCARD_MEM_ATTR, pf->ccr_base,
343		    PCCARD_CCR_SIZE, &pf->pf_pcmh, &pf->pf_ccr_offset,
344		    &pf->pf_ccr_window)) {
345			pccard_mem_free(pf, &pf->pf_pcmh);
346			goto bad;
347		}
348	}
349
350	reg = (pf->cfe->number & PCCARD_CCR_OPTION_CFINDEX);
351	reg |= PCCARD_CCR_OPTION_LEVIREQ;
352	if (pccard_mfc(pf->sc)) {
353		reg |= (PCCARD_CCR_OPTION_FUNC_ENABLE |
354			PCCARD_CCR_OPTION_ADDR_DECODE);
355		if (pf->ih_fct)
356			reg |= PCCARD_CCR_OPTION_IREQ_ENABLE;
357
358	}
359	pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg);
360
361	reg = 0;
362
363	if ((pf->cfe->flags & PCCARD_CFE_IO16) == 0)
364		reg |= PCCARD_CCR_STATUS_IOIS8;
365	if (pf->cfe->flags & PCCARD_CFE_AUDIO)
366		reg |= PCCARD_CCR_STATUS_AUDIO;
367	pccard_ccr_write(pf, PCCARD_CCR_STATUS, reg);
368
369	pccard_ccr_write(pf, PCCARD_CCR_SOCKETCOPY, 0);
370
371	if (pccard_mfc(pf->sc)) {
372		long tmp, iosize;
373
374		tmp = pf->pf_mfc_iomax - pf->pf_mfc_iobase;
375		/* round up to nearest (2^n)-1 */
376		for (iosize = 1; iosize < tmp; iosize <<= 1)
377			;
378		iosize--;
379
380		pccard_ccr_write(pf, PCCARD_CCR_IOBASE0,
381				 pf->pf_mfc_iobase & 0xff);
382		pccard_ccr_write(pf, PCCARD_CCR_IOBASE1,
383				 (pf->pf_mfc_iobase >> 8) & 0xff);
384		pccard_ccr_write(pf, PCCARD_CCR_IOBASE2, 0);
385		pccard_ccr_write(pf, PCCARD_CCR_IOBASE3, 0);
386
387		pccard_ccr_write(pf, PCCARD_CCR_IOSIZE, iosize);
388	}
389
390#ifdef PCCARDDEBUG
391	if (pccard_debug) {
392		STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) {
393			device_printf(tmp->sc->dev,
394			    "function %d CCR at %d offset %x: "
395			    "%x %x %x %x, %x %x %x %x, %x\n",
396			    tmp->number, tmp->pf_ccr_window,
397			    tmp->pf_ccr_offset,
398			    pccard_ccr_read(tmp, 0x00),
399			    pccard_ccr_read(tmp, 0x02),
400			    pccard_ccr_read(tmp, 0x04),
401			    pccard_ccr_read(tmp, 0x06),
402			    pccard_ccr_read(tmp, 0x0A),
403			    pccard_ccr_read(tmp, 0x0C),
404			    pccard_ccr_read(tmp, 0x0E),
405			    pccard_ccr_read(tmp, 0x10),
406			    pccard_ccr_read(tmp, 0x12));
407		}
408	}
409#endif
410
411	pf->pf_flags |= PFF_ENABLED;
412	return (0);
413
414 bad:
415	/*
416	 * Decrement the reference count, and power down the socket, if
417	 * necessary.
418	 */
419	if (--pf->sc->sc_enabled_count == 0)
420		POWER_DISABLE_SOCKET(device_get_parent(pf->sc->dev),
421		    pf->sc->dev);
422	DEVPRINTF((pf->sc->dev, "--enabled_count = %d\n",
423		 pf->sc->sc_enabled_count));
424
425	return (1);
426}
427
428/* Disable PCCARD function. */
429void
430pccard_function_disable(pf)
431	struct pccard_function *pf;
432{
433	struct pccard_function *tmp;
434
435	if (pf->cfe == NULL)
436		panic("pccard_function_enable: function not initialized");
437
438	if ((pf->pf_flags & PFF_ENABLED) == 0) {
439		/*
440		 * Don't do anything if we're already disabled.
441		 */
442		return;
443	}
444
445	/*
446	 * it's possible for different functions' CCRs to be in the same
447	 * underlying page.  Check for that.  Note we mark us as disabled
448	 * first to avoid matching ourself.
449	 */
450
451	pf->pf_flags &= ~PFF_ENABLED;
452	STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) {
453		if ((tmp->pf_flags & PFF_ENABLED) &&
454		    (pf->ccr_base >= (tmp->ccr_base - tmp->pf_ccr_offset)) &&
455		    ((pf->ccr_base + PCCARD_CCR_SIZE) <=
456		(tmp->ccr_base - tmp->pf_ccr_offset + tmp->pf_ccr_realsize)))
457			break;
458	}
459
460	/* Not used by anyone else; unmap the CCR. */
461	if (tmp == NULL) {
462		pccard_mem_unmap(pf, pf->pf_ccr_window);
463		pccard_mem_free(pf, &pf->pf_pcmh);
464	}
465
466	/*
467	 * Decrement the reference count, and power down the socket, if
468	 * necessary.
469	 */
470	if (--pf->sc->sc_enabled_count == 0)
471		POWER_DISABLE_SOCKET(device_get_parent(pf->sc->dev),
472		    pf->sc->dev);
473	DEVPRINTF((pf->sc->dev, "--enabled_count = %d\n",
474	    pf->sc->sc_enabled_count));
475}
476
477int
478pccard_io_map(pf, width, offset, size, pcihp, windowp)
479	struct pccard_function *pf;
480	int width;
481	bus_addr_t offset;
482	bus_size_t size;
483	struct pccard_io_handle *pcihp;
484	int *windowp;
485{
486	int reg;
487
488	if (pccard_chip_io_map(pf->sc->pct, pf->sc->pch,
489	    width, offset, size, pcihp, windowp))
490		return (1);
491
492	/*
493	 * XXX in the multifunction multi-iospace-per-function case, this
494	 * needs to cooperate with io_alloc to make sure that the spaces
495	 * don't overlap, and that the ccr's are set correctly
496	 */
497
498	if (pccard_mfc(pf->sc)) {
499		long tmp, iosize;
500
501		if (pf->pf_mfc_iomax == 0) {
502			pf->pf_mfc_iobase = pcihp->addr + offset;
503			pf->pf_mfc_iomax = pf->pf_mfc_iobase + size;
504		} else {
505			/* this makes the assumption that nothing overlaps */
506			if (pf->pf_mfc_iobase > pcihp->addr + offset)
507				pf->pf_mfc_iobase = pcihp->addr + offset;
508			if (pf->pf_mfc_iomax < pcihp->addr + offset + size)
509				pf->pf_mfc_iomax = pcihp->addr + offset + size;
510		}
511
512		tmp = pf->pf_mfc_iomax - pf->pf_mfc_iobase;
513		/* round up to nearest (2^n)-1 */
514		for (iosize = 1; iosize >= tmp; iosize <<= 1)
515			;
516		iosize--;
517
518		pccard_ccr_write(pf, PCCARD_CCR_IOBASE0,
519				 pf->pf_mfc_iobase & 0xff);
520		pccard_ccr_write(pf, PCCARD_CCR_IOBASE1,
521				 (pf->pf_mfc_iobase >> 8) & 0xff);
522		pccard_ccr_write(pf, PCCARD_CCR_IOBASE2, 0);
523		pccard_ccr_write(pf, PCCARD_CCR_IOBASE3, 0);
524
525		pccard_ccr_write(pf, PCCARD_CCR_IOSIZE, iosize);
526
527		reg = pccard_ccr_read(pf, PCCARD_CCR_OPTION);
528		reg |= PCCARD_CCR_OPTION_ADDR_DECODE;
529		pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg);
530	}
531	return (0);
532}
533
534void
535pccard_io_unmap(pf, window)
536	struct pccard_function *pf;
537	int window;
538{
539
540	pccard_chip_io_unmap(pf->sc->pct, pf->sc->pch, window);
541
542	/* XXX Anything for multi-function cards? */
543}
544
545void *
546pccard_intr_establish(pf, ipl, ih_fct, ih_arg)
547	struct pccard_function *pf;
548	int ipl;
549	int (*ih_fct)(void *);
550	void *ih_arg;
551{
552	void *ret;
553
554	/* behave differently if this is a multifunction card */
555
556	if (pccard_mfc(pf->sc)) {
557		int s, ihcnt, hiipl, reg;
558		struct pccard_function *pf2;
559
560		/*
561		 * mask all the ipl's which are already used by this card,
562		 * and find the highest ipl number (lowest priority)
563		 */
564
565		ihcnt = 0;
566		s = 0;		/* this is only here to keep the compiler
567				   happy */
568		hiipl = 0;	/* this is only here to keep the compiler
569				   happy */
570
571		STAILQ_FOREACH(pf2, &pf->sc->card.pf_head, pf_list) {
572			if (pf2->ih_fct) {
573				DEVPRINTF((pf2->sc->dev,
574				    "function %d has ih_fct %p\n",
575				    pf2->number, pf2->ih_fct));
576
577				if (ihcnt == 0) {
578					hiipl = pf2->ih_ipl;
579				} else {
580					if (pf2->ih_ipl > hiipl)
581						hiipl = pf2->ih_ipl;
582				}
583
584				ihcnt++;
585			}
586		}
587
588		/*
589		 * establish the real interrupt, changing the ipl if
590		 * necessary
591		 */
592
593		if (ihcnt == 0) {
594#ifdef DIAGNOSTIC
595			if (pf->sc->ih != NULL)
596				panic("card has intr handler, but no function does");
597#endif
598			s = splhigh();
599
600			/* set up the handler for the new function */
601
602			pf->ih_fct = ih_fct;
603			pf->ih_arg = ih_arg;
604			pf->ih_ipl = ipl;
605
606			pf->sc->ih = pccard_chip_intr_establish(pf->sc->pct,
607			    pf->sc->pch, pf, ipl, PCCARD_CARD_INTR, pf->sc);
608			splx(s);
609		} else if (ipl > hiipl) {
610#ifdef DIAGNOSTIC
611			if (pf->sc->ih == NULL)
612				panic("functions have ih, but the card does not");
613#endif
614
615			/* XXX need #ifdef for splserial on x86 */
616			s = splhigh();
617
618			pccard_chip_intr_disestablish(pf->sc->pct, pf->sc->pch,
619						      pf->sc->ih);
620
621			/* set up the handler for the new function */
622			pf->ih_fct = ih_fct;
623			pf->ih_arg = ih_arg;
624			pf->ih_ipl = ipl;
625
626			pf->sc->ih = pccard_chip_intr_establish(pf->sc->pct,
627			    pf->sc->pch, pf, ipl, PCCARD_CARD_INTR, pf->sc);
628
629			splx(s);
630		} else {
631			s = splhigh();
632
633			/* set up the handler for the new function */
634
635			pf->ih_fct = ih_fct;
636			pf->ih_arg = ih_arg;
637			pf->ih_ipl = ipl;
638
639			splx(s);
640		}
641
642		ret = pf->sc->ih;
643
644		if (ret != NULL) {
645			reg = pccard_ccr_read(pf, PCCARD_CCR_OPTION);
646			reg |= PCCARD_CCR_OPTION_IREQ_ENABLE;
647			pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg);
648
649			reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS);
650			reg |= PCCARD_CCR_STATUS_INTRACK;
651			pccard_ccr_write(pf, PCCARD_CCR_STATUS, reg);
652		}
653	} else {
654		ret = pccard_chip_intr_establish(pf->sc->pct, pf->sc->pch,
655		    pf, ipl, ih_fct, ih_arg);
656	}
657
658	return (ret);
659}
660
661void
662pccard_intr_disestablish(pf, ih)
663	struct pccard_function *pf;
664	void *ih;
665{
666	/* behave differently if this is a multifunction card */
667
668	if (pccard_mfc(pf->sc)) {
669		int s, ihcnt, hiipl;
670		struct pccard_function *pf2;
671
672		/*
673		 * mask all the ipl's which are already used by this card,
674		 * and find the highest ipl number (lowest priority).  Skip
675		 * the current function.
676		 */
677
678		ihcnt = 0;
679		s = 0;		/* this is only here to keep the compipler
680				   happy */
681		hiipl = 0;	/* this is only here to keep the compipler
682				   happy */
683
684		STAILQ_FOREACH(pf2, &pf->sc->card.pf_head, pf_list) {
685			if (pf2 == pf)
686				continue;
687
688			if (pf2->ih_fct) {
689				if (ihcnt == 0) {
690					hiipl = pf2->ih_ipl;
691				} else {
692					if (pf2->ih_ipl > hiipl)
693						hiipl = pf2->ih_ipl;
694				}
695				ihcnt++;
696			}
697		}
698
699		/*
700		 * if the ih being removed is lower priority than the lowest
701		 * priority remaining interrupt, up the priority.
702		 */
703
704		/* ihcnt is the number of interrupt handlers *not* including
705		   the one about to be removed. */
706
707		if (ihcnt == 0) {
708			int reg;
709
710#ifdef DIAGNOSTIC
711			if (pf->sc->ih == NULL)
712				panic("disestablishing last function, but card has no ih");
713#endif
714			pccard_chip_intr_disestablish(pf->sc->pct, pf->sc->pch,
715			    pf->sc->ih);
716
717			reg = pccard_ccr_read(pf, PCCARD_CCR_OPTION);
718			reg &= ~PCCARD_CCR_OPTION_IREQ_ENABLE;
719			pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg);
720
721			pf->ih_fct = NULL;
722			pf->ih_arg = NULL;
723
724			pf->sc->ih = NULL;
725		} else if (pf->ih_ipl > hiipl) {
726#ifdef DIAGNOSTIC
727			if (pf->sc->ih == NULL)
728				panic("changing ih ipl, but card has no ih");
729#endif
730			/* XXX need #ifdef for splserial on x86 */
731			s = splhigh();
732
733			pccard_chip_intr_disestablish(pf->sc->pct, pf->sc->pch,
734			    pf->sc->ih);
735			pf->sc->ih = pccard_chip_intr_establish(pf->sc->pct,
736			    pf->sc->pch, pf, hiipl, PCCARD_CARD_INTR, pf->sc);
737
738			/* null out the handler for this function */
739
740			pf->ih_fct = NULL;
741			pf->ih_arg = NULL;
742
743			splx(s);
744		} else {
745			s = splhigh();
746
747			pf->ih_fct = NULL;
748			pf->ih_arg = NULL;
749
750			splx(s);
751		}
752	} else {
753		pccard_chip_intr_disestablish(pf->sc->pct, pf->sc->pch, ih);
754	}
755}
756
757int
758pccard_card_intr(arg)
759	void *arg;
760{
761	struct pccard_softc *sc = arg;
762	struct pccard_function *pf;
763	int reg, ret, ret2;
764
765	ret = 0;
766
767	STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) {
768		if (pf->ih_fct != NULL &&
769		    (pf->ccr_mask & (1 << (PCCARD_CCR_STATUS / 2)))) {
770			reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS);
771			if (reg & PCCARD_CCR_STATUS_INTR) {
772				ret2 = (*pf->ih_fct)(pf->ih_arg);
773				if (ret2 != 0 && ret == 0)
774					ret = ret2;
775				reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS);
776				pccard_ccr_write(pf, PCCARD_CCR_STATUS,
777				    reg & ~PCCARD_CCR_STATUS_INTR);
778			}
779		}
780	}
781
782	return (ret);
783}
784
785#ifdef PCCARDDEBUG
786int
787pccard_card_intrdebug(arg)
788	void *arg;
789{
790	struct pccard_softc *sc = arg;
791	struct pccard_function *pf;
792	int reg, ret, ret2;
793
794	ret = 0;
795
796	STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) {
797		device_printf(sc->dev,
798		    "intr flags=%x fct=%d cor=%02x csr=%02x pin=%02x",
799		    pf->pf_flags, pf->number,
800		    pccard_ccr_read(pf, PCCARD_CCR_OPTION),
801		    pccard_ccr_read(pf, PCCARD_CCR_STATUS),
802		    pccard_ccr_read(pf, PCCARD_CCR_PIN));
803		if (pf->ih_fct != NULL &&
804		    (pf->ccr_mask & (1 << (PCCARD_CCR_STATUS / 2)))) {
805			reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS);
806			if (reg & PCCARD_CCR_STATUS_INTR) {
807				ret2 = (*pf->ih_fct)(pf->ih_arg);
808				if (ret2 != 0 && ret == 0)
809					ret = ret2;
810				reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS);
811				printf("; csr %02x->%02x",
812				    reg, reg & ~PCCARD_CCR_STATUS_INTR);
813				pccard_ccr_write(pf, PCCARD_CCR_STATUS,
814				    reg & ~PCCARD_CCR_STATUS_INTR);
815			}
816		}
817		printf("\n");
818	}
819
820	return (ret);
821}
822#endif
823
824#define PCCARD_NPORT	2
825#define PCCARD_NMEM	5
826#define PCCARD_NIRQ	1
827#define PCCARD_NDRQ	0
828
829static int
830pccard_add_children(device_t dev, int busno)
831{
832	return 0;
833}
834
835static int
836pccard_probe(device_t dev)
837{
838	device_set_desc(dev, "PC Card bus -- newconfig version");
839	return pccard_add_children(dev, device_get_unit(dev));
840}
841
842static void
843pccard_print_resources(struct resource_list *rl, const char *name, int type,
844    int count, const char *format)
845{
846	struct resource_list_entry *rle;
847	int printed;
848	int i;
849
850	printed = 0;
851	for (i = 0; i < count; i++) {
852		rle = resource_list_find(rl, type, i);
853		if (rle) {
854			if (printed == 0)
855				printf(" %s ", name);
856			else if (printed > 0)
857				printf(",");
858			printed++;
859			printf(format, rle->start);
860			if (rle->count > 1) {
861				printf("-");
862				printf(format, rle->start + rle->count - 1);
863			}
864		} else if (i > 3) {
865			/* check the first few regardless */
866			break;
867		}
868	}
869}
870
871static int
872pccard_print_child(device_t dev, device_t child)
873{
874	struct pccard_ivar *devi = (struct pccard_ivar *) device_get_ivars(child);
875	struct resource_list *rl = &devi->resources;
876	int retval = 0;
877
878	retval += bus_print_child_header(dev, child);
879	retval += printf(" at");
880
881	if (devi) {
882		pccard_print_resources(rl, "port", SYS_RES_IOPORT,
883		    PCCARD_NPORT, "%#lx");
884		pccard_print_resources(rl, "iomem", SYS_RES_MEMORY,
885		    PCCARD_NMEM, "%#lx");
886		pccard_print_resources(rl, "irq", SYS_RES_IRQ, PCCARD_NIRQ,
887		    "%ld");
888		pccard_print_resources(rl, "drq", SYS_RES_DRQ, PCCARD_NDRQ,
889		    "%ld");
890		retval += printf(" slot %d", devi->slotnum);
891	}
892
893	retval += bus_print_child_footer(dev, child);
894
895	return (retval);
896}
897
898static int
899pccard_set_resource(device_t dev, device_t child, int type, int rid,
900		 u_long start, u_long count)
901{
902	struct pccard_ivar *devi = (struct pccard_ivar *) device_get_ivars(child);
903	struct resource_list *rl = &devi->resources;
904
905	if (type != SYS_RES_IOPORT && type != SYS_RES_MEMORY
906	    && type != SYS_RES_IRQ && type != SYS_RES_DRQ)
907		return EINVAL;
908	if (rid < 0)
909		return EINVAL;
910	if (type == SYS_RES_IOPORT && rid >= PCCARD_NPORT)
911		return EINVAL;
912	if (type == SYS_RES_MEMORY && rid >= PCCARD_NMEM)
913		return EINVAL;
914	if (type == SYS_RES_IRQ && rid >= PCCARD_NIRQ)
915		return EINVAL;
916	if (type == SYS_RES_DRQ && rid >= PCCARD_NDRQ)
917		return EINVAL;
918
919	resource_list_add(rl, type, rid, start, start + count - 1, count);
920
921	return 0;
922}
923
924static int
925pccard_get_resource(device_t dev, device_t child, int type, int rid,
926    u_long *startp, u_long *countp)
927{
928	struct pccard_ivar *devi = (struct pccard_ivar *) device_get_ivars(child);
929	struct resource_list *rl = &devi->resources;
930	struct resource_list_entry *rle;
931
932	rle = resource_list_find(rl, type, rid);
933	if (!rle)
934		return ENOENT;
935
936	if (startp)
937		*startp = rle->start;
938	if (countp)
939		*countp = rle->count;
940
941	return 0;
942}
943
944static void
945pccard_delete_resource(device_t dev, device_t child, int type, int rid)
946{
947	struct pccard_ivar *devi = (struct pccard_ivar *) device_get_ivars(child);
948	struct resource_list *rl = &devi->resources;
949	resource_list_delete(rl, type, rid);
950}
951
952static device_method_t pccard_methods[] = {
953	/* Device interface */
954	DEVMETHOD(device_probe,		pccard_probe),
955	DEVMETHOD(device_attach,	bus_generic_attach),
956	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
957	DEVMETHOD(device_suspend,	bus_generic_suspend),
958	DEVMETHOD(device_resume,	bus_generic_resume),
959
960	/* Bus interface */
961	DEVMETHOD(bus_print_child,	pccard_print_child),
962	DEVMETHOD(bus_driver_added,	bus_generic_driver_added),
963	DEVMETHOD(bus_alloc_resource,	bus_generic_alloc_resource),
964	DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
965	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
966	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
967	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
968	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
969	DEVMETHOD(bus_set_resource,	pccard_set_resource),
970	DEVMETHOD(bus_get_resource,	pccard_get_resource),
971	DEVMETHOD(bus_delete_resource,	pccard_delete_resource),
972
973	{ 0, 0 }
974};
975
976static driver_t pccard_driver = {
977	"pccard",
978	pccard_methods,
979	1,			/* no softc */
980};
981
982devclass_t	pccard_devclass;
983
984DRIVER_MODULE(pccard, pcic, pccard_driver, pccard_devclass, 0, 0);
985DRIVER_MODULE(pccard, pc98pcic, pccard_driver, pccard_devclass, 0, 0);
986DRIVER_MODULE(pccard, pccbb, pccard_driver, pccard_devclass, 0, 0);
987DRIVER_MODULE(pccard, tcic, pccard_driver, pccard_devclass, 0, 0);
988