pccard.c revision 52506
152506Simp/*	$NetBSD: pcmcia.c,v 1.13 1998/12/24 04:51:59 marc Exp $	*/
252506Simp/* $FreeBSD: head/sys/dev/pccard/pccard.c 52506 1999-10-26 06:52:31Z imp $ */
352506Simp
452506Simp/*
552506Simp * Copyright (c) 1997 Marc Horowitz.  All rights reserved.
652506Simp *
752506Simp * Redistribution and use in source and binary forms, with or without
852506Simp * modification, are permitted provided that the following conditions
952506Simp * are met:
1052506Simp * 1. Redistributions of source code must retain the above copyright
1152506Simp *    notice, this list of conditions and the following disclaimer.
1252506Simp * 2. Redistributions in binary form must reproduce the above copyright
1352506Simp *    notice, this list of conditions and the following disclaimer in the
1452506Simp *    documentation and/or other materials provided with the distribution.
1552506Simp * 3. All advertising materials mentioning features or use of this software
1652506Simp *    must display the following acknowledgement:
1752506Simp *	This product includes software developed by Marc Horowitz.
1852506Simp * 4. The name of the author may not be used to endorse or promote products
1952506Simp *    derived from this software without specific prior written permission.
2052506Simp *
2152506Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2252506Simp * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2352506Simp * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2452506Simp * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2552506Simp * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2652506Simp * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2752506Simp * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2852506Simp * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2952506Simp * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3052506Simp * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3152506Simp */
3252506Simp
3352506Simp#include <sys/param.h>
3452506Simp#include <sys/systm.h>
3552506Simp#include <sys/malloc.h>
3652506Simp#include <sys/module.h>
3752506Simp#include <sys/kernel.h>
3852506Simp#include <sys/queue.h>
3952506Simp#include <sys/types.h>
4052506Simp
4152506Simp#include <sys/bus.h>
4252506Simp#include <machine/bus.h>
4352506Simp#include <sys/rman.h>
4452506Simp#include <machine/resource.h>
4552506Simp
4652506Simp#include <dev/pccard/pccardreg.h>
4752506Simp#include <dev/pccard/pccardchip.h>
4852506Simp#include <dev/pccard/pccardvar.h>
4952506Simp
5052506Simp#ifdef PCCARDDEBUG
5152506Simpint	pccard_debug = 0;
5252506Simp#define	DPRINTF(arg) if (pccard_debug) printf arg
5352506Simpint	pccardintr_debug = 0;
5452506Simp/* this is done this way to avoid doing lots of conditionals
5552506Simp   at interrupt level.  */
5652506Simp#define PCCARD_CARD_INTR (pccardintr_debug?pccard_card_intrdebug:pccard_card_intr)
5752506Simp#else
5852506Simp#define	DPRINTF(arg)
5952506Simp#define PCCARD_CARD_INTR (pccard_card_intr)
6052506Simp#endif
6152506Simp
6252506Simp#ifdef PCCARDVERBOSE
6352506Simpint	pccard_verbose = 1;
6452506Simp#else
6552506Simpint	pccard_verbose = 0;
6652506Simp#endif
6752506Simp
6852506Simpint	pccard_print __P((void *, const char *));
6952506Simp
7052506Simpstatic __inline void pccard_socket_enable __P((pccard_chipset_tag_t,
7152506Simp					     pccard_chipset_handle_t *));
7252506Simpstatic __inline void pccard_socket_disable __P((pccard_chipset_tag_t,
7352506Simp					      pccard_chipset_handle_t *));
7452506Simp
7552506Simpint pccard_card_intr __P((void *));
7652506Simp#ifdef PCCARDDEBUG
7752506Simpint pccard_card_intrdebug __P((void *));
7852506Simp#endif
7952506Simp
8052506Simpint
8152506Simppccard_ccr_read(pf, ccr)
8252506Simp	struct pccard_function *pf;
8352506Simp	int ccr;
8452506Simp{
8552506Simp
8652506Simp	return (bus_space_read_1(pf->pf_ccrt, pf->pf_ccrh,
8752506Simp	    pf->pf_ccr_offset + ccr));
8852506Simp}
8952506Simp
9052506Simpvoid
9152506Simppccard_ccr_write(pf, ccr, val)
9252506Simp	struct pccard_function *pf;
9352506Simp	int ccr;
9452506Simp	int val;
9552506Simp{
9652506Simp
9752506Simp	if ((pf->ccr_mask) & (1 << (ccr / 2))) {
9852506Simp		bus_space_write_1(pf->pf_ccrt, pf->pf_ccrh,
9952506Simp		    pf->pf_ccr_offset + ccr, val);
10052506Simp	}
10152506Simp}
10252506Simp
10352506Simpstatic int
10452506Simppccard_card_attach(device_t dev)
10552506Simp{
10652506Simp	struct pccard_softc *sc = (struct pccard_softc *)
10752506Simp	    device_get_softc(dev);
10852506Simp	struct pccard_function *pf;
10952506Simp	struct pccard_attach_args paa;
11052506Simp	int attached;
11152506Simp
11252506Simp	/*
11352506Simp	 * this is here so that when socket_enable calls gettype, trt happens
11452506Simp	 */
11552506Simp	STAILQ_INIT(&sc->card.pf_head);
11652506Simp
11752506Simp	pccard_chip_socket_enable(sc->pct, sc->pch);
11852506Simp
11952506Simp	pccard_read_cis(sc);
12052506Simp
12152506Simp	pccard_chip_socket_disable(sc->pct, sc->pch);
12252506Simp
12352506Simp	pccard_check_cis_quirks(dev);
12452506Simp
12552506Simp	/*
12652506Simp	 * bail now if the card has no functions, or if there was an error in
12752506Simp	 * the cis.
12852506Simp	 */
12952506Simp
13052506Simp	if (sc->card.error)
13152506Simp		return (1);
13252506Simp	if (STAILQ_EMPTY(&sc->card.pf_head))
13352506Simp		return (1);
13452506Simp
13552506Simp	if (pccard_verbose)
13652506Simp		pccard_print_cis(dev);
13752506Simp
13852506Simp	attached = 0;
13952506Simp
14052506Simp	STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) {
14152506Simp		if (STAILQ_EMPTY(&pf->cfe_head))
14252506Simp			continue;
14352506Simp
14452506Simp#ifdef DIAGNOSTIC
14552506Simp		if (pf->child != NULL) {
14652506Simp			printf("%s: %s still attached to function %d!\n",
14752506Simp			    sc->dev.dv_xname, pf->child->dv_xname,
14852506Simp			    pf->number);
14952506Simp			panic("pccard_card_attach");
15052506Simp		}
15152506Simp#endif
15252506Simp		pf->sc = sc;
15352506Simp		pf->child = NULL;
15452506Simp		pf->cfe = NULL;
15552506Simp		pf->ih_fct = NULL;
15652506Simp		pf->ih_arg = NULL;
15752506Simp	}
15852506Simp
15952506Simp	STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) {
16052506Simp		if (STAILQ_EMPTY(&pf->cfe_head))
16152506Simp			continue;
16252506Simp
16352506Simp		paa.manufacturer = sc->card.manufacturer;
16452506Simp		paa.product = sc->card.product;
16552506Simp		paa.card = &sc->card;
16652506Simp		paa.pf = pf;
16752506Simp
16852506Simp#if XXX
16952506Simp		if (attach_child()) {
17052506Simp			attached++;
17152506Simp
17252506Simp			DPRINTF(("%s: function %d CCR at %d "
17352506Simp			     "offset %lx: %x %x %x %x, %x %x %x %x, %x\n",
17452506Simp			     sc->dev.dv_xname, pf->number,
17552506Simp			     pf->pf_ccr_window, pf->pf_ccr_offset,
17652506Simp			     pccard_ccr_read(pf, 0x00),
17752506Simp			pccard_ccr_read(pf, 0x02), pccard_ccr_read(pf, 0x04),
17852506Simp			pccard_ccr_read(pf, 0x06), pccard_ccr_read(pf, 0x0A),
17952506Simp			pccard_ccr_read(pf, 0x0C), pccard_ccr_read(pf, 0x0E),
18052506Simp			pccard_ccr_read(pf, 0x10), pccard_ccr_read(pf, 0x12)));
18152506Simp		}
18252506Simp#endif
18352506Simp	}
18452506Simp
18552506Simp	return (attached ? 0 : 1);
18652506Simp}
18752506Simp
18852506Simpstatic void
18952506Simppccard_card_detach(device_t dev, int flags)
19052506Simp{
19152506Simp	struct pccard_softc *sc = (struct pccard_softc *)
19252506Simp	    device_get_softc(dev);
19352506Simp	struct pccard_function *pf;
19452506Simp#if XXX
19552506Simp	int error;
19652506Simp#endif
19752506Simp
19852506Simp	/*
19952506Simp	 * We are running on either the PCCARD socket's event thread
20052506Simp	 * or in user context detaching a device by user request.
20152506Simp	 */
20252506Simp	STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) {
20352506Simp		if (STAILQ_FIRST(&pf->cfe_head) == NULL)
20452506Simp			continue;
20552506Simp		if (pf->child == NULL)
20652506Simp			continue;
20752506Simp		DPRINTF(("%s: detaching %s (function %d)\n",
20852506Simp		    sc->dev.dv_xname, pf->child->dv_xname, pf->number));
20952506Simp#if XXX
21052506Simp		if ((error = config_detach(pf->child, flags)) != 0) {
21152506Simp			printf("%s: error %d detaching %s (function %d)\n",
21252506Simp			    sc->dev.dv_xname, error, pf->child->dv_xname,
21352506Simp			    pf->number);
21452506Simp		} else
21552506Simp			pf->child = NULL;
21652506Simp#endif
21752506Simp	}
21852506Simp}
21952506Simp
22052506Simpstatic void
22152506Simppccard_card_deactivate(device_t dev)
22252506Simp{
22352506Simp	struct pccard_softc *sc = (struct pccard_softc *)
22452506Simp	    device_get_softc(dev);
22552506Simp	struct pccard_function *pf;
22652506Simp
22752506Simp	/*
22852506Simp	 * We're in the chip's card removal interrupt handler.
22952506Simp	 * Deactivate the child driver.  The PCCARD socket's
23052506Simp	 * event thread will run later to finish the detach.
23152506Simp	 */
23252506Simp	STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) {
23352506Simp		if (STAILQ_FIRST(&pf->cfe_head) == NULL)
23452506Simp			continue;
23552506Simp		if (pf->child == NULL)
23652506Simp			continue;
23752506Simp		DPRINTF(("%s: deactivating %s (function %d)\n",
23852506Simp		    sc->dev.dv_xname, pf->child->dv_xname, pf->number));
23952506Simp#if XXX
24052506Simp		config_deactivate(pf->child);
24152506Simp#endif
24252506Simp	}
24352506Simp}
24452506Simp
24552506Simpstatic int
24652506Simppccard_card_gettype(device_t dev)
24752506Simp{
24852506Simp	struct pccard_softc *sc = (struct pccard_softc *)
24952506Simp	    device_get_softc(dev);
25052506Simp	struct pccard_function *pf;
25152506Simp
25252506Simp	/*
25352506Simp	 * set the iftype to memory if this card has no functions (not yet
25452506Simp	 * probed), or only one function, and that is not initialized yet or
25552506Simp	 * that is memory.
25652506Simp	 */
25752506Simp	pf = STAILQ_FIRST(&sc->card.pf_head);
25852506Simp	if (pf == NULL ||
25952506Simp	    (STAILQ_NEXT(pf, pf_list) == NULL &&
26052506Simp	    (pf->cfe == NULL || pf->cfe->iftype == PCCARD_IFTYPE_MEMORY)))
26152506Simp		return (PCCARD_IFTYPE_MEMORY);
26252506Simp	else
26352506Simp		return (PCCARD_IFTYPE_IO);
26452506Simp}
26552506Simp
26652506Simp/*
26752506Simp * Initialize a PCCARD function.  May be called as long as the function is
26852506Simp * disabled.
26952506Simp */
27052506Simpvoid
27152506Simppccard_function_init(pf, cfe)
27252506Simp	struct pccard_function *pf;
27352506Simp	struct pccard_config_entry *cfe;
27452506Simp{
27552506Simp	if (pf->pf_flags & PFF_ENABLED)
27652506Simp		panic("pccard_function_init: function is enabled");
27752506Simp
27852506Simp	/* Remember which configuration entry we are using. */
27952506Simp	pf->cfe = cfe;
28052506Simp}
28152506Simp
28252506Simpstatic __inline void pccard_socket_enable(pct, pch)
28352506Simp     pccard_chipset_tag_t pct;
28452506Simp     pccard_chipset_handle_t *pch;
28552506Simp{
28652506Simp	pccard_chip_socket_enable(pct, pch);
28752506Simp}
28852506Simp
28952506Simpstatic __inline void pccard_socket_disable(pct, pch)
29052506Simp     pccard_chipset_tag_t pct;
29152506Simp     pccard_chipset_handle_t *pch;
29252506Simp{
29352506Simp	pccard_chip_socket_disable(pct, pch);
29452506Simp}
29552506Simp
29652506Simp/* Enable a PCCARD function */
29752506Simpint
29852506Simppccard_function_enable(pf)
29952506Simp	struct pccard_function *pf;
30052506Simp{
30152506Simp	struct pccard_function *tmp;
30252506Simp	int reg;
30352506Simp
30452506Simp	if (pf->cfe == NULL)
30552506Simp		panic("pccard_function_enable: function not initialized");
30652506Simp
30752506Simp	/*
30852506Simp	 * Increase the reference count on the socket, enabling power, if
30952506Simp	 * necessary.
31052506Simp	 */
31152506Simp	if (pf->sc->sc_enabled_count++ == 0)
31252506Simp		pccard_chip_socket_enable(pf->sc->pct, pf->sc->pch);
31352506Simp	DPRINTF(("%s: ++enabled_count = %d\n", pf->sc->dev.dv_xname,
31452506Simp		 pf->sc->sc_enabled_count));
31552506Simp
31652506Simp	if (pf->pf_flags & PFF_ENABLED) {
31752506Simp		/*
31852506Simp		 * Don't do anything if we're already enabled.
31952506Simp		 */
32052506Simp		return (0);
32152506Simp	}
32252506Simp
32352506Simp	/*
32452506Simp	 * it's possible for different functions' CCRs to be in the same
32552506Simp	 * underlying page.  Check for that.
32652506Simp	 */
32752506Simp
32852506Simp	STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) {
32952506Simp		if ((tmp->pf_flags & PFF_ENABLED) &&
33052506Simp		    (pf->ccr_base >= (tmp->ccr_base - tmp->pf_ccr_offset)) &&
33152506Simp		    ((pf->ccr_base + PCCARD_CCR_SIZE) <=
33252506Simp		     (tmp->ccr_base - tmp->pf_ccr_offset +
33352506Simp		      tmp->pf_ccr_realsize))) {
33452506Simp			pf->pf_ccrt = tmp->pf_ccrt;
33552506Simp			pf->pf_ccrh = tmp->pf_ccrh;
33652506Simp			pf->pf_ccr_realsize = tmp->pf_ccr_realsize;
33752506Simp
33852506Simp			/*
33952506Simp			 * pf->pf_ccr_offset = (tmp->pf_ccr_offset -
34052506Simp			 * tmp->ccr_base) + pf->ccr_base;
34152506Simp			 */
34252506Simp			pf->pf_ccr_offset =
34352506Simp			    (tmp->pf_ccr_offset + pf->ccr_base) -
34452506Simp			    tmp->ccr_base;
34552506Simp			pf->pf_ccr_window = tmp->pf_ccr_window;
34652506Simp			break;
34752506Simp		}
34852506Simp	}
34952506Simp
35052506Simp	if (tmp == NULL) {
35152506Simp		if (pccard_mem_alloc(pf, PCCARD_CCR_SIZE, &pf->pf_pcmh))
35252506Simp			goto bad;
35352506Simp
35452506Simp		if (pccard_mem_map(pf, PCCARD_MEM_ATTR, pf->ccr_base,
35552506Simp		    PCCARD_CCR_SIZE, &pf->pf_pcmh, &pf->pf_ccr_offset,
35652506Simp		    &pf->pf_ccr_window)) {
35752506Simp			pccard_mem_free(pf, &pf->pf_pcmh);
35852506Simp			goto bad;
35952506Simp		}
36052506Simp	}
36152506Simp
36252506Simp	reg = (pf->cfe->number & PCCARD_CCR_OPTION_CFINDEX);
36352506Simp	reg |= PCCARD_CCR_OPTION_LEVIREQ;
36452506Simp	if (pccard_mfc(pf->sc)) {
36552506Simp		reg |= (PCCARD_CCR_OPTION_FUNC_ENABLE |
36652506Simp			PCCARD_CCR_OPTION_ADDR_DECODE);
36752506Simp		if (pf->ih_fct)
36852506Simp			reg |= PCCARD_CCR_OPTION_IREQ_ENABLE;
36952506Simp
37052506Simp	}
37152506Simp	pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg);
37252506Simp
37352506Simp	reg = 0;
37452506Simp
37552506Simp	if ((pf->cfe->flags & PCCARD_CFE_IO16) == 0)
37652506Simp		reg |= PCCARD_CCR_STATUS_IOIS8;
37752506Simp	if (pf->cfe->flags & PCCARD_CFE_AUDIO)
37852506Simp		reg |= PCCARD_CCR_STATUS_AUDIO;
37952506Simp	pccard_ccr_write(pf, PCCARD_CCR_STATUS, reg);
38052506Simp
38152506Simp	pccard_ccr_write(pf, PCCARD_CCR_SOCKETCOPY, 0);
38252506Simp
38352506Simp	if (pccard_mfc(pf->sc)) {
38452506Simp		long tmp, iosize;
38552506Simp
38652506Simp		tmp = pf->pf_mfc_iomax - pf->pf_mfc_iobase;
38752506Simp		/* round up to nearest (2^n)-1 */
38852506Simp		for (iosize = 1; iosize < tmp; iosize <<= 1)
38952506Simp			;
39052506Simp		iosize--;
39152506Simp
39252506Simp		pccard_ccr_write(pf, PCCARD_CCR_IOBASE0,
39352506Simp				 pf->pf_mfc_iobase & 0xff);
39452506Simp		pccard_ccr_write(pf, PCCARD_CCR_IOBASE1,
39552506Simp				 (pf->pf_mfc_iobase >> 8) & 0xff);
39652506Simp		pccard_ccr_write(pf, PCCARD_CCR_IOBASE2, 0);
39752506Simp		pccard_ccr_write(pf, PCCARD_CCR_IOBASE3, 0);
39852506Simp
39952506Simp		pccard_ccr_write(pf, PCCARD_CCR_IOSIZE, iosize);
40052506Simp	}
40152506Simp
40252506Simp#ifdef PCCARDDEBUG
40352506Simp	if (pccard_debug) {
40452506Simp		STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) {
40552506Simp			printf("%s: function %d CCR at %d offset %lx: "
40652506Simp			       "%x %x %x %x, %x %x %x %x, %x\n",
40752506Simp			       tmp->sc->dev.dv_xname, tmp->number,
40852506Simp			       tmp->pf_ccr_window, tmp->pf_ccr_offset,
40952506Simp			       pccard_ccr_read(tmp, 0x00),
41052506Simp			       pccard_ccr_read(tmp, 0x02),
41152506Simp			       pccard_ccr_read(tmp, 0x04),
41252506Simp			       pccard_ccr_read(tmp, 0x06),
41352506Simp
41452506Simp			       pccard_ccr_read(tmp, 0x0A),
41552506Simp			       pccard_ccr_read(tmp, 0x0C),
41652506Simp			       pccard_ccr_read(tmp, 0x0E),
41752506Simp			       pccard_ccr_read(tmp, 0x10),
41852506Simp
41952506Simp			       pccard_ccr_read(tmp, 0x12));
42052506Simp		}
42152506Simp	}
42252506Simp#endif
42352506Simp
42452506Simp	pf->pf_flags |= PFF_ENABLED;
42552506Simp	return (0);
42652506Simp
42752506Simp bad:
42852506Simp	/*
42952506Simp	 * Decrement the reference count, and power down the socket, if
43052506Simp	 * necessary.
43152506Simp	 */
43252506Simp	if (--pf->sc->sc_enabled_count == 0)
43352506Simp		pccard_chip_socket_disable(pf->sc->pct, pf->sc->pch);
43452506Simp	DPRINTF(("%s: --enabled_count = %d\n", pf->sc->dev.dv_xname,
43552506Simp		 pf->sc->sc_enabled_count));
43652506Simp
43752506Simp	return (1);
43852506Simp}
43952506Simp
44052506Simp/* Disable PCCARD function. */
44152506Simpvoid
44252506Simppccard_function_disable(pf)
44352506Simp	struct pccard_function *pf;
44452506Simp{
44552506Simp	struct pccard_function *tmp;
44652506Simp
44752506Simp	if (pf->cfe == NULL)
44852506Simp		panic("pccard_function_enable: function not initialized");
44952506Simp
45052506Simp	if ((pf->pf_flags & PFF_ENABLED) == 0) {
45152506Simp		/*
45252506Simp		 * Don't do anything if we're already disabled.
45352506Simp		 */
45452506Simp		return;
45552506Simp	}
45652506Simp
45752506Simp	/*
45852506Simp	 * it's possible for different functions' CCRs to be in the same
45952506Simp	 * underlying page.  Check for that.  Note we mark us as disabled
46052506Simp	 * first to avoid matching ourself.
46152506Simp	 */
46252506Simp
46352506Simp	pf->pf_flags &= ~PFF_ENABLED;
46452506Simp	STAILQ_FOREACH(tmp, &pf->sc->card.pf_head, pf_list) {
46552506Simp		if ((tmp->pf_flags & PFF_ENABLED) &&
46652506Simp		    (pf->ccr_base >= (tmp->ccr_base - tmp->pf_ccr_offset)) &&
46752506Simp		    ((pf->ccr_base + PCCARD_CCR_SIZE) <=
46852506Simp		(tmp->ccr_base - tmp->pf_ccr_offset + tmp->pf_ccr_realsize)))
46952506Simp			break;
47052506Simp	}
47152506Simp
47252506Simp	/* Not used by anyone else; unmap the CCR. */
47352506Simp	if (tmp == NULL) {
47452506Simp		pccard_mem_unmap(pf, pf->pf_ccr_window);
47552506Simp		pccard_mem_free(pf, &pf->pf_pcmh);
47652506Simp	}
47752506Simp
47852506Simp	/*
47952506Simp	 * Decrement the reference count, and power down the socket, if
48052506Simp	 * necessary.
48152506Simp	 */
48252506Simp	if (--pf->sc->sc_enabled_count == 0)
48352506Simp		pccard_chip_socket_disable(pf->sc->pct, pf->sc->pch);
48452506Simp	DPRINTF(("%s: --enabled_count = %d\n", pf->sc->dev.dv_xname,
48552506Simp		 pf->sc->sc_enabled_count));
48652506Simp}
48752506Simp
48852506Simpint
48952506Simppccard_io_map(pf, width, offset, size, pcihp, windowp)
49052506Simp	struct pccard_function *pf;
49152506Simp	int width;
49252506Simp	bus_addr_t offset;
49352506Simp	bus_size_t size;
49452506Simp	struct pccard_io_handle *pcihp;
49552506Simp	int *windowp;
49652506Simp{
49752506Simp	int reg;
49852506Simp
49952506Simp	if (pccard_chip_io_map(pf->sc->pct, pf->sc->pch,
50052506Simp	    width, offset, size, pcihp, windowp))
50152506Simp		return (1);
50252506Simp
50352506Simp	/*
50452506Simp	 * XXX in the multifunction multi-iospace-per-function case, this
50552506Simp	 * needs to cooperate with io_alloc to make sure that the spaces
50652506Simp	 * don't overlap, and that the ccr's are set correctly
50752506Simp	 */
50852506Simp
50952506Simp	if (pccard_mfc(pf->sc)) {
51052506Simp		long tmp, iosize;
51152506Simp
51252506Simp		if (pf->pf_mfc_iomax == 0) {
51352506Simp			pf->pf_mfc_iobase = pcihp->addr + offset;
51452506Simp			pf->pf_mfc_iomax = pf->pf_mfc_iobase + size;
51552506Simp		} else {
51652506Simp			/* this makes the assumption that nothing overlaps */
51752506Simp			if (pf->pf_mfc_iobase > pcihp->addr + offset)
51852506Simp				pf->pf_mfc_iobase = pcihp->addr + offset;
51952506Simp			if (pf->pf_mfc_iomax < pcihp->addr + offset + size)
52052506Simp				pf->pf_mfc_iomax = pcihp->addr + offset + size;
52152506Simp		}
52252506Simp
52352506Simp		tmp = pf->pf_mfc_iomax - pf->pf_mfc_iobase;
52452506Simp		/* round up to nearest (2^n)-1 */
52552506Simp		for (iosize = 1; iosize >= tmp; iosize <<= 1)
52652506Simp			;
52752506Simp		iosize--;
52852506Simp
52952506Simp		pccard_ccr_write(pf, PCCARD_CCR_IOBASE0,
53052506Simp				 pf->pf_mfc_iobase & 0xff);
53152506Simp		pccard_ccr_write(pf, PCCARD_CCR_IOBASE1,
53252506Simp				 (pf->pf_mfc_iobase >> 8) & 0xff);
53352506Simp		pccard_ccr_write(pf, PCCARD_CCR_IOBASE2, 0);
53452506Simp		pccard_ccr_write(pf, PCCARD_CCR_IOBASE3, 0);
53552506Simp
53652506Simp		pccard_ccr_write(pf, PCCARD_CCR_IOSIZE, iosize);
53752506Simp
53852506Simp		reg = pccard_ccr_read(pf, PCCARD_CCR_OPTION);
53952506Simp		reg |= PCCARD_CCR_OPTION_ADDR_DECODE;
54052506Simp		pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg);
54152506Simp	}
54252506Simp	return (0);
54352506Simp}
54452506Simp
54552506Simpvoid
54652506Simppccard_io_unmap(pf, window)
54752506Simp	struct pccard_function *pf;
54852506Simp	int window;
54952506Simp{
55052506Simp
55152506Simp	pccard_chip_io_unmap(pf->sc->pct, pf->sc->pch, window);
55252506Simp
55352506Simp	/* XXX Anything for multi-function cards? */
55452506Simp}
55552506Simp
55652506Simpvoid *
55752506Simppccard_intr_establish(pf, ipl, ih_fct, ih_arg)
55852506Simp	struct pccard_function *pf;
55952506Simp	int ipl;
56052506Simp	int (*ih_fct) __P((void *));
56152506Simp	void *ih_arg;
56252506Simp{
56352506Simp	void *ret;
56452506Simp
56552506Simp	/* behave differently if this is a multifunction card */
56652506Simp
56752506Simp	if (pccard_mfc(pf->sc)) {
56852506Simp		int s, ihcnt, hiipl, reg;
56952506Simp		struct pccard_function *pf2;
57052506Simp
57152506Simp		/*
57252506Simp		 * mask all the ipl's which are already used by this card,
57352506Simp		 * and find the highest ipl number (lowest priority)
57452506Simp		 */
57552506Simp
57652506Simp		ihcnt = 0;
57752506Simp		s = 0;		/* this is only here to keep the compiler
57852506Simp				   happy */
57952506Simp		hiipl = 0;	/* this is only here to keep the compiler
58052506Simp				   happy */
58152506Simp
58252506Simp		STAILQ_FOREACH(pf2, &pf->sc->card.pf_head, pf_list) {
58352506Simp			if (pf2->ih_fct) {
58452506Simp				DPRINTF(("%s: function %d has ih_fct %p\n",
58552506Simp					 pf->sc->dev.dv_xname, pf2->number,
58652506Simp					 pf2->ih_fct));
58752506Simp
58852506Simp				if (ihcnt == 0) {
58952506Simp					hiipl = pf2->ih_ipl;
59052506Simp				} else {
59152506Simp					if (pf2->ih_ipl > hiipl)
59252506Simp						hiipl = pf2->ih_ipl;
59352506Simp				}
59452506Simp
59552506Simp				ihcnt++;
59652506Simp			}
59752506Simp		}
59852506Simp
59952506Simp		/*
60052506Simp		 * establish the real interrupt, changing the ipl if
60152506Simp		 * necessary
60252506Simp		 */
60352506Simp
60452506Simp		if (ihcnt == 0) {
60552506Simp#ifdef DIAGNOSTIC
60652506Simp			if (pf->sc->ih != NULL)
60752506Simp				panic("card has intr handler, but no function does");
60852506Simp#endif
60952506Simp			s = splhigh();
61052506Simp
61152506Simp			/* set up the handler for the new function */
61252506Simp
61352506Simp			pf->ih_fct = ih_fct;
61452506Simp			pf->ih_arg = ih_arg;
61552506Simp			pf->ih_ipl = ipl;
61652506Simp
61752506Simp			pf->sc->ih = pccard_chip_intr_establish(pf->sc->pct,
61852506Simp			    pf->sc->pch, pf, ipl, PCCARD_CARD_INTR, pf->sc);
61952506Simp			splx(s);
62052506Simp		} else if (ipl > hiipl) {
62152506Simp#ifdef DIAGNOSTIC
62252506Simp			if (pf->sc->ih == NULL)
62352506Simp				panic("functions have ih, but the card does not");
62452506Simp#endif
62552506Simp
62652506Simp			/* XXX need #ifdef for splserial on x86 */
62752506Simp			s = splhigh();
62852506Simp
62952506Simp			pccard_chip_intr_disestablish(pf->sc->pct, pf->sc->pch,
63052506Simp						      pf->sc->ih);
63152506Simp
63252506Simp			/* set up the handler for the new function */
63352506Simp			pf->ih_fct = ih_fct;
63452506Simp			pf->ih_arg = ih_arg;
63552506Simp			pf->ih_ipl = ipl;
63652506Simp
63752506Simp			pf->sc->ih = pccard_chip_intr_establish(pf->sc->pct,
63852506Simp			    pf->sc->pch, pf, ipl, PCCARD_CARD_INTR, pf->sc);
63952506Simp
64052506Simp			splx(s);
64152506Simp		} else {
64252506Simp			s = splhigh();
64352506Simp
64452506Simp			/* set up the handler for the new function */
64552506Simp
64652506Simp			pf->ih_fct = ih_fct;
64752506Simp			pf->ih_arg = ih_arg;
64852506Simp			pf->ih_ipl = ipl;
64952506Simp
65052506Simp			splx(s);
65152506Simp		}
65252506Simp
65352506Simp		ret = pf->sc->ih;
65452506Simp
65552506Simp		if (ret != NULL) {
65652506Simp			reg = pccard_ccr_read(pf, PCCARD_CCR_OPTION);
65752506Simp			reg |= PCCARD_CCR_OPTION_IREQ_ENABLE;
65852506Simp			pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg);
65952506Simp
66052506Simp			reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS);
66152506Simp			reg |= PCCARD_CCR_STATUS_INTRACK;
66252506Simp			pccard_ccr_write(pf, PCCARD_CCR_STATUS, reg);
66352506Simp		}
66452506Simp	} else {
66552506Simp		ret = pccard_chip_intr_establish(pf->sc->pct, pf->sc->pch,
66652506Simp		    pf, ipl, ih_fct, ih_arg);
66752506Simp	}
66852506Simp
66952506Simp	return (ret);
67052506Simp}
67152506Simp
67252506Simpvoid
67352506Simppccard_intr_disestablish(pf, ih)
67452506Simp	struct pccard_function *pf;
67552506Simp	void *ih;
67652506Simp{
67752506Simp	/* behave differently if this is a multifunction card */
67852506Simp
67952506Simp	if (pccard_mfc(pf->sc)) {
68052506Simp		int s, ihcnt, hiipl;
68152506Simp		struct pccard_function *pf2;
68252506Simp
68352506Simp		/*
68452506Simp		 * mask all the ipl's which are already used by this card,
68552506Simp		 * and find the highest ipl number (lowest priority).  Skip
68652506Simp		 * the current function.
68752506Simp		 */
68852506Simp
68952506Simp		ihcnt = 0;
69052506Simp		s = 0;		/* this is only here to keep the compipler
69152506Simp				   happy */
69252506Simp		hiipl = 0;	/* this is only here to keep the compipler
69352506Simp				   happy */
69452506Simp
69552506Simp		STAILQ_FOREACH(pf2, &pf->sc->card.pf_head, pf_list) {
69652506Simp			if (pf2 == pf)
69752506Simp				continue;
69852506Simp
69952506Simp			if (pf2->ih_fct) {
70052506Simp				if (ihcnt == 0) {
70152506Simp					hiipl = pf2->ih_ipl;
70252506Simp				} else {
70352506Simp					if (pf2->ih_ipl > hiipl)
70452506Simp						hiipl = pf2->ih_ipl;
70552506Simp				}
70652506Simp				ihcnt++;
70752506Simp			}
70852506Simp		}
70952506Simp
71052506Simp		/*
71152506Simp		 * if the ih being removed is lower priority than the lowest
71252506Simp		 * priority remaining interrupt, up the priority.
71352506Simp		 */
71452506Simp
71552506Simp		/* ihcnt is the number of interrupt handlers *not* including
71652506Simp		   the one about to be removed. */
71752506Simp
71852506Simp		if (ihcnt == 0) {
71952506Simp			int reg;
72052506Simp
72152506Simp#ifdef DIAGNOSTIC
72252506Simp			if (pf->sc->ih == NULL)
72352506Simp				panic("disestablishing last function, but card has no ih");
72452506Simp#endif
72552506Simp			pccard_chip_intr_disestablish(pf->sc->pct, pf->sc->pch,
72652506Simp			    pf->sc->ih);
72752506Simp
72852506Simp			reg = pccard_ccr_read(pf, PCCARD_CCR_OPTION);
72952506Simp			reg &= ~PCCARD_CCR_OPTION_IREQ_ENABLE;
73052506Simp			pccard_ccr_write(pf, PCCARD_CCR_OPTION, reg);
73152506Simp
73252506Simp			pf->ih_fct = NULL;
73352506Simp			pf->ih_arg = NULL;
73452506Simp
73552506Simp			pf->sc->ih = NULL;
73652506Simp		} else if (pf->ih_ipl > hiipl) {
73752506Simp#ifdef DIAGNOSTIC
73852506Simp			if (pf->sc->ih == NULL)
73952506Simp				panic("changing ih ipl, but card has no ih");
74052506Simp#endif
74152506Simp			/* XXX need #ifdef for splserial on x86 */
74252506Simp			s = splhigh();
74352506Simp
74452506Simp			pccard_chip_intr_disestablish(pf->sc->pct, pf->sc->pch,
74552506Simp			    pf->sc->ih);
74652506Simp			pf->sc->ih = pccard_chip_intr_establish(pf->sc->pct,
74752506Simp			    pf->sc->pch, pf, hiipl, PCCARD_CARD_INTR, pf->sc);
74852506Simp
74952506Simp			/* null out the handler for this function */
75052506Simp
75152506Simp			pf->ih_fct = NULL;
75252506Simp			pf->ih_arg = NULL;
75352506Simp
75452506Simp			splx(s);
75552506Simp		} else {
75652506Simp			s = splhigh();
75752506Simp
75852506Simp			pf->ih_fct = NULL;
75952506Simp			pf->ih_arg = NULL;
76052506Simp
76152506Simp			splx(s);
76252506Simp		}
76352506Simp	} else {
76452506Simp		pccard_chip_intr_disestablish(pf->sc->pct, pf->sc->pch, ih);
76552506Simp	}
76652506Simp}
76752506Simp
76852506Simpint
76952506Simppccard_card_intr(arg)
77052506Simp	void *arg;
77152506Simp{
77252506Simp	struct pccard_softc *sc = arg;
77352506Simp	struct pccard_function *pf;
77452506Simp	int reg, ret, ret2;
77552506Simp
77652506Simp	ret = 0;
77752506Simp
77852506Simp	STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) {
77952506Simp		if (pf->ih_fct != NULL &&
78052506Simp		    (pf->ccr_mask & (1 << (PCCARD_CCR_STATUS / 2)))) {
78152506Simp			reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS);
78252506Simp			if (reg & PCCARD_CCR_STATUS_INTR) {
78352506Simp				ret2 = (*pf->ih_fct)(pf->ih_arg);
78452506Simp				if (ret2 != 0 && ret == 0)
78552506Simp					ret = ret2;
78652506Simp				reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS);
78752506Simp				pccard_ccr_write(pf, PCCARD_CCR_STATUS,
78852506Simp				    reg & ~PCCARD_CCR_STATUS_INTR);
78952506Simp			}
79052506Simp		}
79152506Simp	}
79252506Simp
79352506Simp	return (ret);
79452506Simp}
79552506Simp
79652506Simp#ifdef PCCARDDEBUG
79752506Simpint
79852506Simppccard_card_intrdebug(arg)
79952506Simp	void *arg;
80052506Simp{
80152506Simp	struct pccard_softc *sc = arg;
80252506Simp	struct pccard_function *pf;
80352506Simp	int reg, ret, ret2;
80452506Simp
80552506Simp	ret = 0;
80652506Simp
80752506Simp	STAILQ_FOREACH(pf, &sc->card.pf_head, pf_list) {
80852506Simp		printf("%s: intr flags=%x fct=%d cor=%02x csr=%02x pin=%02x",
80952506Simp		       sc->dev.dv_xname, pf->pf_flags, pf->number,
81052506Simp		       pccard_ccr_read(pf, PCCARD_CCR_OPTION),
81152506Simp		       pccard_ccr_read(pf, PCCARD_CCR_STATUS),
81252506Simp		       pccard_ccr_read(pf, PCCARD_CCR_PIN));
81352506Simp		if (pf->ih_fct != NULL &&
81452506Simp		    (pf->ccr_mask & (1 << (PCCARD_CCR_STATUS / 2)))) {
81552506Simp			reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS);
81652506Simp			if (reg & PCCARD_CCR_STATUS_INTR) {
81752506Simp				ret2 = (*pf->ih_fct)(pf->ih_arg);
81852506Simp				if (ret2 != 0 && ret == 0)
81952506Simp					ret = ret2;
82052506Simp				reg = pccard_ccr_read(pf, PCCARD_CCR_STATUS);
82152506Simp				printf("; csr %02x->%02x",
82252506Simp				    reg, reg & ~PCCARD_CCR_STATUS_INTR);
82352506Simp				pccard_ccr_write(pf, PCCARD_CCR_STATUS,
82452506Simp				    reg & ~PCCARD_CCR_STATUS_INTR);
82552506Simp			}
82652506Simp		}
82752506Simp		printf("\n");
82852506Simp	}
82952506Simp
83052506Simp	return (ret);
83152506Simp}
83252506Simp#endif
83352506Simp
83452506Simpstatic int
83552506Simppccard_add_children(device_t dev, int busno)
83652506Simp{
83752506Simp	device_add_child(dev, NULL, -1, NULL);
83852506Simp	return 0;
83952506Simp}
84052506Simp
84152506Simpstatic int
84252506Simppccard_probe(device_t dev)
84352506Simp{
84452506Simp	device_set_desc(dev, "PC Card bus -- newconfig version");
84552506Simp	return pccard_add_children(dev, device_get_unit(dev));
84652506Simp}
84752506Simp
84852506Simpstatic device_method_t pccard_methods[] = {
84952506Simp	/* Device interface */
85052506Simp	DEVMETHOD(device_probe,		pccard_probe),
85152506Simp	DEVMETHOD(device_attach,	bus_generic_attach),
85252506Simp	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
85352506Simp	DEVMETHOD(device_suspend,	bus_generic_suspend),
85452506Simp	DEVMETHOD(device_resume,	bus_generic_resume),
85552506Simp
85652506Simp	/* Bus interface */
85752506Simp#if 0
85852506Simp	DEVMETHOD(bus_print_child,	pccard_print_child),
85952506Simp#endif
86052506Simp	DEVMETHOD(bus_driver_added,	bus_generic_driver_added),
86152506Simp#if 0
86252506Simp	DEVMETHOD(bus_alloc_resource,	pccard_alloc_resource),
86352506Simp	DEVMETHOD(bus_release_resource,	pccard_release_resource),
86452506Simp#endif
86552506Simp	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
86652506Simp	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
86752506Simp	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
86852506Simp	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
86952506Simp#if 0
87052506Simp	DEVMETHOD(bus_set_resource,	pccard_set_resource),
87152506Simp	DEVMETHOD(bus_get_resource,	pccard_get_resource),
87252506Simp	DEVMETHOD(bus_delete_resource,	pccard_delete_resource),
87352506Simp#endif
87452506Simp
87552506Simp	{ 0, 0 }
87652506Simp};
87752506Simp
87852506Simpstatic driver_t pccard_driver = {
87952506Simp	"pccard",
88052506Simp	pccard_methods,
88152506Simp	1,			/* no softc */
88252506Simp};
88352506Simp
88452506Simpdevclass_t	pccard_devclass;
88552506Simp
88652506SimpDRIVER_MODULE(pccard, pcicx, pccard_driver, pccard_devclass, 0, 0);
88752506SimpDRIVER_MODULE(pccard, pc98pcic, pccard_driver, pccard_devclass, 0, 0);
88852506SimpDRIVER_MODULE(pccard, pccbb, pccard_driver, pccard_devclass, 0, 0);
889