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