xref: /freebsd/sys/dev/cardbus/cardbus_cis.c (revision aad970f1fee9a2a3e5a0f880be9b87c6193b3bd1)
10db7e66cSJonathan Chen /*
20db7e66cSJonathan Chen  * Copyright (c) 2000,2001 Jonathan Chen.
30db7e66cSJonathan Chen  * All rights reserved.
40db7e66cSJonathan Chen  *
50db7e66cSJonathan Chen  * Redistribution and use in source and binary forms, with or without
60db7e66cSJonathan Chen  * modification, are permitted provided that the following conditions
70db7e66cSJonathan Chen  * are met:
80db7e66cSJonathan Chen  * 1. Redistributions of source code must retain the above copyright
90db7e66cSJonathan Chen  *    notice, this list of conditions, and the following disclaimer,
100db7e66cSJonathan Chen  *    without modification, immediately at the beginning of the file.
110db7e66cSJonathan Chen  * 2. Redistributions in binary form must reproduce the above copyright
120db7e66cSJonathan Chen  *    notice, this list of conditions and the following disclaimer in
130db7e66cSJonathan Chen  *    the documentation and/or other materials provided with the
140db7e66cSJonathan Chen  *    distribution.
150db7e66cSJonathan Chen  *
160db7e66cSJonathan Chen  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
170db7e66cSJonathan Chen  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
180db7e66cSJonathan Chen  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
190db7e66cSJonathan Chen  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
200db7e66cSJonathan Chen  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
210db7e66cSJonathan Chen  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
220db7e66cSJonathan Chen  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
230db7e66cSJonathan Chen  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
240db7e66cSJonathan Chen  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
250db7e66cSJonathan Chen  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
260db7e66cSJonathan Chen  * SUCH DAMAGE.
270db7e66cSJonathan Chen  *
280db7e66cSJonathan Chen  */
290db7e66cSJonathan Chen 
30aad970f1SDavid E. O'Brien #include <sys/cdefs.h>
31aad970f1SDavid E. O'Brien __FBSDID("$FreeBSD$");
32aad970f1SDavid E. O'Brien 
330db7e66cSJonathan Chen /*
340db7e66cSJonathan Chen  * CIS Handling for the Cardbus Bus
350db7e66cSJonathan Chen  */
360db7e66cSJonathan Chen 
370db7e66cSJonathan Chen #include <sys/param.h>
380db7e66cSJonathan Chen #include <sys/systm.h>
390db7e66cSJonathan Chen #include <sys/kernel.h>
400c95c705SJonathan Chen #include <sys/malloc.h>
410db7e66cSJonathan Chen 
420db7e66cSJonathan Chen #include <sys/bus.h>
430db7e66cSJonathan Chen #include <machine/bus.h>
440db7e66cSJonathan Chen #include <machine/resource.h>
450db7e66cSJonathan Chen #include <sys/rman.h>
461e962d00SScott Long #include <sys/endian.h>
470db7e66cSJonathan Chen 
487ba175acSWarner Losh #include <sys/pciio.h>
4963fa9f4cSJonathan Chen #include <dev/pci/pcivar.h>
5063fa9f4cSJonathan Chen #include <dev/pci/pcireg.h>
510db7e66cSJonathan Chen 
520db7e66cSJonathan Chen #include <dev/cardbus/cardbusreg.h>
5363fa9f4cSJonathan Chen #include <dev/cardbus/cardbusvar.h>
540db7e66cSJonathan Chen #include <dev/cardbus/cardbus_cis.h>
550db7e66cSJonathan Chen 
5680f10018STakanori Watanabe #include <dev/pccard/pccardvar.h>
570c95c705SJonathan Chen 
58b3889b68SWarner Losh extern int cardbus_cis_debug;
59b3889b68SWarner Losh 
60b3889b68SWarner Losh #define	DPRINTF(a) if (cardbus_cis_debug) printf a
61b3889b68SWarner Losh #define	DEVPRINTF(x) if (cardbus_cis_debug) device_printf x
62b3889b68SWarner Losh 
6322acd92bSWarner Losh struct tuple_callbacks;
6422acd92bSWarner Losh 
6522acd92bSWarner Losh typedef int (tuple_cb) (device_t cbdev, device_t child, int id, int len,
6622acd92bSWarner Losh 		 uint8_t *tupledata, uint32_t start, uint32_t *off,
6722acd92bSWarner Losh 		 struct tuple_callbacks *info);
680db7e66cSJonathan Chen 
690c95c705SJonathan Chen struct tuple_callbacks {
700c95c705SJonathan Chen 	int	id;
710db7e66cSJonathan Chen 	char	*name;
7222acd92bSWarner Losh 	tuple_cb *func;
730db7e66cSJonathan Chen };
74255b159fSJonathan Chen 
7522acd92bSWarner Losh static int decode_tuple_generic(device_t cbdev, device_t child, int id,
7622acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
7722acd92bSWarner Losh     struct tuple_callbacks *info);
7822acd92bSWarner Losh static int decode_tuple_nothing(device_t cbdev, device_t child, int id,
7922acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
8022acd92bSWarner Losh     struct tuple_callbacks *info);
8122acd92bSWarner Losh static int decode_tuple_copy(device_t cbdev, device_t child, int id,
8222acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
8322acd92bSWarner Losh     struct tuple_callbacks *info);
8422acd92bSWarner Losh static int decode_tuple_linktarget(device_t cbdev, device_t child, int id,
8522acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
8622acd92bSWarner Losh     struct tuple_callbacks *info);
8722acd92bSWarner Losh static int decode_tuple_vers_1(device_t cbdev, device_t child, int id,
8822acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
8922acd92bSWarner Losh     struct tuple_callbacks *info);
9022acd92bSWarner Losh static int decode_tuple_funcid(device_t cbdev, device_t child, int id,
9122acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
9222acd92bSWarner Losh     struct tuple_callbacks *info);
9322acd92bSWarner Losh static int decode_tuple_manfid(device_t cbdev, device_t child, int id,
9422acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
9522acd92bSWarner Losh     struct tuple_callbacks *info);
9622acd92bSWarner Losh static int decode_tuple_funce(device_t cbdev, device_t child, int id,
9722acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
9822acd92bSWarner Losh     struct tuple_callbacks *info);
9922acd92bSWarner Losh static int decode_tuple_bar(device_t cbdev, device_t child, int id,
10022acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
10122acd92bSWarner Losh     struct tuple_callbacks *info);
10222acd92bSWarner Losh static int decode_tuple_unhandled(device_t cbdev, device_t child, int id,
10322acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
10422acd92bSWarner Losh     struct tuple_callbacks *info);
10522acd92bSWarner Losh static int decode_tuple_end(device_t cbdev, device_t child, int id,
10622acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
10722acd92bSWarner Losh     struct tuple_callbacks *info);
10822acd92bSWarner Losh 
109255b159fSJonathan Chen static int	cardbus_read_tuple_conf(device_t cbdev, device_t child,
11066e390feSWarner Losh 		    uint32_t start, uint32_t *off, int *tupleid, int *len,
11166e390feSWarner Losh 		    uint8_t *tupledata);
11263fa9f4cSJonathan Chen static int	cardbus_read_tuple_mem(device_t cbdev, struct resource *res,
11366e390feSWarner Losh 		    uint32_t start, uint32_t *off, int *tupleid, int *len,
11466e390feSWarner Losh 		    uint8_t *tupledata);
115255b159fSJonathan Chen static int	cardbus_read_tuple(device_t cbdev, device_t child,
11666e390feSWarner Losh 		    struct resource *res, uint32_t start, uint32_t *off,
11766e390feSWarner Losh 		    int *tupleid, int *len, uint8_t *tupledata);
11863fa9f4cSJonathan Chen static void	cardbus_read_tuple_finish(device_t cbdev, device_t child,
11963fa9f4cSJonathan Chen 		    int rid, struct resource *res);
12063fa9f4cSJonathan Chen static struct resource	*cardbus_read_tuple_init(device_t cbdev, device_t child,
12166e390feSWarner Losh 		    uint32_t *start, int *rid);
122255b159fSJonathan Chen static int	decode_tuple(device_t cbdev, device_t child, int tupleid,
12366e390feSWarner Losh 		    int len, uint8_t *tupledata, uint32_t start,
12466e390feSWarner Losh 		    uint32_t *off, struct tuple_callbacks *callbacks);
125255b159fSJonathan Chen static int	cardbus_parse_cis(device_t cbdev, device_t child,
126255b159fSJonathan Chen 		    struct tuple_callbacks *callbacks);
12763fa9f4cSJonathan Chen static int	barsort(const void *a, const void *b);
12863fa9f4cSJonathan Chen static int	cardbus_alloc_resources(device_t cbdev, device_t child);
12963fa9f4cSJonathan Chen static void	cardbus_add_map(device_t cbdev, device_t child, int reg);
13063fa9f4cSJonathan Chen static void	cardbus_pickup_maps(device_t cbdev, device_t child);
13163fa9f4cSJonathan Chen 
132255b159fSJonathan Chen 
1330c95c705SJonathan Chen #define	MAKETUPLE(NAME,FUNC) { CISTPL_ ## NAME, #NAME, decode_tuple_ ## FUNC }
1340db7e66cSJonathan Chen 
1350db7e66cSJonathan Chen static char *funcnames[] = {
1360db7e66cSJonathan Chen 	"Multi-Functioned",
1370db7e66cSJonathan Chen 	"Memory",
1380db7e66cSJonathan Chen 	"Serial Port",
1390db7e66cSJonathan Chen 	"Parallel Port",
1400db7e66cSJonathan Chen 	"Fixed Disk",
1410db7e66cSJonathan Chen 	"Video Adaptor",
1420db7e66cSJonathan Chen 	"Network Adaptor",
1430db7e66cSJonathan Chen 	"AIMS",
1440db7e66cSJonathan Chen 	"SCSI",
1450db7e66cSJonathan Chen 	"Security"
1460db7e66cSJonathan Chen };
1470db7e66cSJonathan Chen 
14863fa9f4cSJonathan Chen struct cardbus_quirk {
14966e390feSWarner Losh 	uint32_t devid;	/* Vendor/device of the card */
15063fa9f4cSJonathan Chen 	int	type;
15163fa9f4cSJonathan Chen #define	CARDBUS_QUIRK_MAP_REG	1 /* PCI map register in weird place */
15263fa9f4cSJonathan Chen 	int	arg1;
15363fa9f4cSJonathan Chen 	int	arg2;
15463fa9f4cSJonathan Chen };
15563fa9f4cSJonathan Chen 
15663fa9f4cSJonathan Chen struct cardbus_quirk cardbus_quirks[] = {
15763fa9f4cSJonathan Chen 	{ 0 }
15863fa9f4cSJonathan Chen };
15963fa9f4cSJonathan Chen 
1600c95c705SJonathan Chen static struct cis_tupleinfo *cisread_buf;
1610c95c705SJonathan Chen static int ncisread_buf;
1620c95c705SJonathan Chen 
163255b159fSJonathan Chen /*
164255b159fSJonathan Chen  * Handler functions for various CIS tuples
165255b159fSJonathan Chen  */
166255b159fSJonathan Chen 
16722acd92bSWarner Losh static int
16822acd92bSWarner Losh decode_tuple_generic(device_t cbdev, device_t child, int id,
16922acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
17022acd92bSWarner Losh     struct tuple_callbacks *info)
1710db7e66cSJonathan Chen {
1720db7e66cSJonathan Chen 	int i;
1730db7e66cSJonathan Chen 
17422acd92bSWarner Losh 	if (cardbus_cis_debug) {
1750db7e66cSJonathan Chen 		if (info)
1760db7e66cSJonathan Chen 			printf("TUPLE: %s [%d]:", info->name, len);
1770db7e66cSJonathan Chen 		else
1780db7e66cSJonathan Chen 			printf("TUPLE: Unknown(0x%02x) [%d]:", id, len);
1790db7e66cSJonathan Chen 
1800db7e66cSJonathan Chen 		for (i = 0; i < len; i++) {
1810db7e66cSJonathan Chen 			if (i % 0x10 == 0 && len > 0x10)
1820db7e66cSJonathan Chen 				printf("\n       0x%02x:", i);
1837bec1dd5SJonathan Chen 			printf(" %02x", tupledata[i]);
1840db7e66cSJonathan Chen 		}
1850db7e66cSJonathan Chen 		printf("\n");
18622acd92bSWarner Losh 	}
187a3133b58SWarner Losh 	return (0);
1880db7e66cSJonathan Chen }
1890db7e66cSJonathan Chen 
19022acd92bSWarner Losh static int
19122acd92bSWarner Losh decode_tuple_nothing(device_t cbdev, device_t child, int id,
19222acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
19322acd92bSWarner Losh     struct tuple_callbacks *info)
1940c95c705SJonathan Chen {
195a3133b58SWarner Losh 	return (0);
1960c95c705SJonathan Chen }
1970c95c705SJonathan Chen 
19822acd92bSWarner Losh static int
19922acd92bSWarner Losh decode_tuple_copy(device_t cbdev, device_t child, int id,
20022acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
20122acd92bSWarner Losh     struct tuple_callbacks *info)
2020c95c705SJonathan Chen {
2030c95c705SJonathan Chen 	struct cis_tupleinfo *tmpbuf;
2040c95c705SJonathan Chen 
2050c95c705SJonathan Chen 	tmpbuf = malloc(sizeof(struct cis_tupleinfo) * (ncisread_buf+1),
206a163d034SWarner Losh 	    M_DEVBUF, M_WAITOK);
2070c95c705SJonathan Chen 	if (ncisread_buf > 0) {
2080c95c705SJonathan Chen 		memcpy(tmpbuf, cisread_buf,
2090c95c705SJonathan Chen 		    sizeof(struct cis_tupleinfo) * ncisread_buf);
2100c95c705SJonathan Chen 		free(cisread_buf, M_DEVBUF);
2110c95c705SJonathan Chen 	}
2120c95c705SJonathan Chen 	cisread_buf = tmpbuf;
2130c95c705SJonathan Chen 
2140c95c705SJonathan Chen 	cisread_buf[ncisread_buf].id = id;
2150c95c705SJonathan Chen 	cisread_buf[ncisread_buf].len = len;
216a163d034SWarner Losh 	cisread_buf[ncisread_buf].data = malloc(len, M_DEVBUF, M_WAITOK);
2170c95c705SJonathan Chen 	memcpy(cisread_buf[ncisread_buf].data, tupledata, len);
2180c95c705SJonathan Chen 	ncisread_buf++;
219a3133b58SWarner Losh 	return (0);
2200c95c705SJonathan Chen }
2210c95c705SJonathan Chen 
22222acd92bSWarner Losh static int
22322acd92bSWarner Losh decode_tuple_linktarget(device_t cbdev, device_t child, int id,
22422acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
22522acd92bSWarner Losh     struct tuple_callbacks *info)
2260db7e66cSJonathan Chen {
22749f158ccSJonathan Chen 	int i;
22849f158ccSJonathan Chen 
22922acd92bSWarner Losh 	if (cardbus_cis_debug) {
23049f158ccSJonathan Chen 		printf("TUPLE: %s [%d]:", info->name, len);
23149f158ccSJonathan Chen 
23249f158ccSJonathan Chen 		for (i = 0; i < len; i++) {
23349f158ccSJonathan Chen 			if (i % 0x10 == 0 && len > 0x10)
23449f158ccSJonathan Chen 				printf("\n       0x%02x:", i);
23549f158ccSJonathan Chen 			printf(" %02x", tupledata[i]);
23649f158ccSJonathan Chen 		}
23749f158ccSJonathan Chen 		printf("\n");
23822acd92bSWarner Losh 	}
2397bec1dd5SJonathan Chen 	if (len != 3 || tupledata[0] != 'C' || tupledata[1] != 'I' ||
2407bec1dd5SJonathan Chen 	    tupledata[2] != 'S') {
2410db7e66cSJonathan Chen 		printf("Invalid data for CIS Link Target!\n");
242255b159fSJonathan Chen 		decode_tuple_generic(cbdev, child, id, len, tupledata,
24349f158ccSJonathan Chen 		    start, off, info);
244a3133b58SWarner Losh 		return (EINVAL);
2450db7e66cSJonathan Chen 	}
246a3133b58SWarner Losh 	return (0);
2470db7e66cSJonathan Chen }
2480db7e66cSJonathan Chen 
24922acd92bSWarner Losh static int
25022acd92bSWarner Losh decode_tuple_vers_1(device_t cbdev, device_t child, int id,
25122acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
25222acd92bSWarner Losh     struct tuple_callbacks *info)
2530db7e66cSJonathan Chen {
2540db7e66cSJonathan Chen 	int i;
255fbe9cff1SWarner Losh 
25622acd92bSWarner Losh 	if (cardbus_cis_debug) {
2577bec1dd5SJonathan Chen 		printf("Product version: %d.%d\n", tupledata[0], tupledata[1]);
2580db7e66cSJonathan Chen 		printf("Product name: ");
2590db7e66cSJonathan Chen 		for (i = 2; i < len; i++) {
2607bec1dd5SJonathan Chen 			if (tupledata[i] == '\0')
2610db7e66cSJonathan Chen 				printf(" | ");
2627bec1dd5SJonathan Chen 			else if (tupledata[i] == 0xff)
2630db7e66cSJonathan Chen 				break;
2640db7e66cSJonathan Chen 			else
2657bec1dd5SJonathan Chen 				printf("%c", tupledata[i]);
2660db7e66cSJonathan Chen 		}
2670db7e66cSJonathan Chen 		printf("\n");
26822acd92bSWarner Losh 	}
269a3133b58SWarner Losh 	return (0);
2700db7e66cSJonathan Chen }
2710db7e66cSJonathan Chen 
27222acd92bSWarner Losh static int
27322acd92bSWarner Losh decode_tuple_funcid(device_t cbdev, device_t child, int id,
27422acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
27522acd92bSWarner Losh     struct tuple_callbacks *info)
2760db7e66cSJonathan Chen {
277fbe9cff1SWarner Losh 	struct cardbus_devinfo *dinfo = device_get_ivars(child);
2780db7e66cSJonathan Chen 	int numnames = sizeof(funcnames) / sizeof(funcnames[0]);
279fbe9cff1SWarner Losh 	int i;
2800db7e66cSJonathan Chen 
28122acd92bSWarner Losh 	if (cardbus_cis_debug) {
2820db7e66cSJonathan Chen 		printf("Functions: ");
2830db7e66cSJonathan Chen 		for (i = 0; i < len; i++) {
2847bec1dd5SJonathan Chen 			if (tupledata[i] < numnames)
2857bec1dd5SJonathan Chen 				printf("%s", funcnames[tupledata[i]]);
2860db7e66cSJonathan Chen 			else
2877bec1dd5SJonathan Chen 				printf("Unknown(%d)", tupledata[i]);
288255b159fSJonathan Chen 			if (i < len-1)
289255b159fSJonathan Chen 				printf(", ");
2900db7e66cSJonathan Chen 		}
29122acd92bSWarner Losh 		printf("\n");
29222acd92bSWarner Losh 	}
293fbe9cff1SWarner Losh 	if (len > 0)
294fbe9cff1SWarner Losh 		dinfo->funcid = tupledata[0];		/* use first in list */
295a3133b58SWarner Losh 	return (0);
2960db7e66cSJonathan Chen }
2970db7e66cSJonathan Chen 
29822acd92bSWarner Losh static int
29922acd92bSWarner Losh decode_tuple_manfid(device_t cbdev, device_t child, int id,
30022acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
30122acd92bSWarner Losh     struct tuple_callbacks *info)
3020db7e66cSJonathan Chen {
303fbe9cff1SWarner Losh 	struct cardbus_devinfo *dinfo = device_get_ivars(child);
3040db7e66cSJonathan Chen 	int i;
305fbe9cff1SWarner Losh 
30622acd92bSWarner Losh 	if (cardbus_cis_debug) {
3070db7e66cSJonathan Chen 		printf("Manufacturer ID: ");
3080db7e66cSJonathan Chen 		for (i = 0; i < len; i++)
3097bec1dd5SJonathan Chen 			printf("%02x", tupledata[i]);
3100db7e66cSJonathan Chen 		printf("\n");
31122acd92bSWarner Losh 	}
312fbe9cff1SWarner Losh 
313fbe9cff1SWarner Losh 	if (len == 5) {
314fbe9cff1SWarner Losh 		dinfo->mfrid = tupledata[1] | (tupledata[2] << 8);
315fbe9cff1SWarner Losh 		dinfo->prodid = tupledata[3] | (tupledata[4] << 8);
316fbe9cff1SWarner Losh 	}
317a3133b58SWarner Losh 	return (0);
3180db7e66cSJonathan Chen }
3190db7e66cSJonathan Chen 
32022acd92bSWarner Losh static int
32122acd92bSWarner Losh decode_tuple_funce(device_t cbdev, device_t child, int id,
32222acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
32322acd92bSWarner Losh     struct tuple_callbacks *info)
3240db7e66cSJonathan Chen {
325fbe9cff1SWarner Losh 	struct cardbus_devinfo *dinfo = device_get_ivars(child);
326fbe9cff1SWarner Losh 	int type, i;
327fbe9cff1SWarner Losh 
32822acd92bSWarner Losh 	if (cardbus_cis_debug) {
3290db7e66cSJonathan Chen 		printf("Function Extension: ");
3300db7e66cSJonathan Chen 		for (i = 0; i < len; i++)
3317bec1dd5SJonathan Chen 			printf("%02x", tupledata[i]);
3320db7e66cSJonathan Chen 		printf("\n");
33322acd92bSWarner Losh 	}
334fbe9cff1SWarner Losh 	if (len < 2)			/* too short */
335fbe9cff1SWarner Losh 		return (0);
336fbe9cff1SWarner Losh 	type = tupledata[0];		/* XXX <32 always? */
337fbe9cff1SWarner Losh 	switch (dinfo->funcid) {
338fbe9cff1SWarner Losh 	case TPL_FUNC_SERIAL:
339fbe9cff1SWarner Losh 		if (type == TPL_FUNCE_SER_UART) {	/* NB: len known > 1 */
340fbe9cff1SWarner Losh 			dinfo->funce.sio.type = tupledata[1] & 0x1f;
341fbe9cff1SWarner Losh 		}
342fbe9cff1SWarner Losh 		dinfo->fepresent |= 1<<type;
343fbe9cff1SWarner Losh 		break;
344fbe9cff1SWarner Losh 	case TPL_FUNC_LAN:
345fbe9cff1SWarner Losh 		switch (type) {
346fbe9cff1SWarner Losh 		case TPL_FUNCE_LAN_TECH:
347fbe9cff1SWarner Losh 			dinfo->funce.lan.tech = tupledata[1];	/* XXX mask? */
348fbe9cff1SWarner Losh 			break;
349fbe9cff1SWarner Losh #if 0
350fbe9cff1SWarner Losh 		case TPL_FUNCE_LAN_SPEED:
351fbe9cff1SWarner Losh 			for (i = 0; i < 3; i++) {
352fbe9cff1SWarner Losh 				if (dinfo->funce.lan.speed[i] == 0) {
353fbe9cff1SWarner Losh 					if (len > 4) {
354fbe9cff1SWarner Losh 						dinfo->funce.lan.speed[i] =
355fbe9cff1SWarner Losh 							...;
356fbe9cff1SWarner Losh 					}
357fbe9cff1SWarner Losh 					break;
358fbe9cff1SWarner Losh 				}
359fbe9cff1SWarner Losh 			}
360fbe9cff1SWarner Losh 			break;
361fbe9cff1SWarner Losh #endif
362fbe9cff1SWarner Losh 		case TPL_FUNCE_LAN_MEDIA:
363fbe9cff1SWarner Losh 			for (i = 0; i < 4 && dinfo->funce.lan.media[i]; i++) {
364fbe9cff1SWarner Losh 				if (dinfo->funce.lan.media[i] == 0) {
365fbe9cff1SWarner Losh 					/* NB: len known > 1 */
366fbe9cff1SWarner Losh 					dinfo->funce.lan.media[i] =
367fbe9cff1SWarner Losh 						tupledata[1];	/*XXX? mask */
368fbe9cff1SWarner Losh 					break;
369fbe9cff1SWarner Losh 				}
370fbe9cff1SWarner Losh 			}
371fbe9cff1SWarner Losh 			break;
372fbe9cff1SWarner Losh 		case TPL_FUNCE_LAN_NID:
37322acd92bSWarner Losh 			if (tupledata[1] > sizeof(dinfo->funce.lan.nid)) {
37422acd92bSWarner Losh 				/* ignore, warning? */
37522acd92bSWarner Losh 				return (0);
37622acd92bSWarner Losh 			}
37722acd92bSWarner Losh 			bcopy(tupledata + 2, dinfo->funce.lan.nid,
37822acd92bSWarner Losh 			    tupledata[1]);
379fbe9cff1SWarner Losh 			break;
380fbe9cff1SWarner Losh 		case TPL_FUNCE_LAN_CONN:
381fbe9cff1SWarner Losh 			dinfo->funce.lan.contype = tupledata[1];/*XXX mask? */
382fbe9cff1SWarner Losh 			break;
383fbe9cff1SWarner Losh 		}
384fbe9cff1SWarner Losh 		dinfo->fepresent |= 1<<type;
385fbe9cff1SWarner Losh 		break;
386fbe9cff1SWarner Losh 	}
387a3133b58SWarner Losh 	return (0);
3880db7e66cSJonathan Chen }
3890db7e66cSJonathan Chen 
39022acd92bSWarner Losh static int
39122acd92bSWarner Losh decode_tuple_bar(device_t cbdev, device_t child, int id,
39222acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
39322acd92bSWarner Losh     struct tuple_callbacks *info)
3940db7e66cSJonathan Chen {
39563fa9f4cSJonathan Chen 	struct cardbus_devinfo *dinfo = device_get_ivars(child);
3960db7e66cSJonathan Chen 	int type;
3971e962d00SScott Long 	uint8_t reg;
39872d3502eSScott Long 	uint32_t bar, pci_bar;
3990db7e66cSJonathan Chen 
400e6e272b9SScott Long 	if (len != 6) {
4011e962d00SScott Long 		device_printf(cbdev, "CIS BAR length not 6 (%d)\n", len);
402e6e272b9SScott Long 		return (EINVAL);
403e6e272b9SScott Long 	}
4041e962d00SScott Long 
4051e962d00SScott Long 	reg = *tupledata;
4061e962d00SScott Long 	len = le32toh(*(uint32_t*)(tupledata + 2));
4070db7e66cSJonathan Chen 	if (reg & TPL_BAR_REG_AS) {
4080db7e66cSJonathan Chen 		type = SYS_RES_IOPORT;
4090db7e66cSJonathan Chen 	} else {
4100db7e66cSJonathan Chen 		type = SYS_RES_MEMORY;
4110db7e66cSJonathan Chen 	}
4121e962d00SScott Long 
4131e962d00SScott Long 	bar = reg & TPL_BAR_REG_ASI_MASK;
4141e962d00SScott Long 	if (bar == 0) {
4151e962d00SScott Long 		device_printf(cbdev, "Invalid BAR type 0 in CIS\n");
4161e962d00SScott Long 		return (EINVAL);	/* XXX Return an error? */
4171e962d00SScott Long 	} else if (bar == 7) {
4181e962d00SScott Long 		/* XXX Should we try to map in Option ROMs? */
419a3133b58SWarner Losh 		return (0);
4200db7e66cSJonathan Chen 	}
4211e962d00SScott Long 
42272d3502eSScott Long 	/* Convert from BAR type to BAR offset */
4231e962d00SScott Long 	bar = CARDBUS_BASE0_REG + (bar - 1) * 4;
4241e962d00SScott Long 
42563fa9f4cSJonathan Chen 	if (type == SYS_RES_MEMORY) {
42672d3502eSScott Long 		if (reg & TPL_BAR_REG_PREFETCHABLE)
42763fa9f4cSJonathan Chen 			dinfo->mprefetchable |= BARBIT(bar);
428f9aedaa4SWarner Losh #if 0
42972d3502eSScott Long 		if (reg & TPL_BAR_REG_BELOW1MB)
43063fa9f4cSJonathan Chen 			dinfo->mbelow1mb |= BARBIT(bar);
431f9aedaa4SWarner Losh #endif
4320db7e66cSJonathan Chen 	}
4331e962d00SScott Long 
43472d3502eSScott Long 	/*
43572d3502eSScott Long 	 * Sanity check the BAR length reported in the CIS with the length
43672d3502eSScott Long 	 * encoded in the PCI BAR.  The latter seems to be more reliable.
43772d3502eSScott Long 	 * XXX - This probably belongs elsewhere.
43872d3502eSScott Long 	 */
43972d3502eSScott Long 	pci_write_config(child, bar, 0xffffffff, 4);
44072d3502eSScott Long 	pci_bar = pci_read_config(child, bar, 4);
44172d3502eSScott Long 	if ((pci_bar != 0x0) && (pci_bar != 0xffffffff)) {
44272d3502eSScott Long 		if (type == SYS_RES_MEMORY) {
44372d3502eSScott Long 			pci_bar &= ~0xf;
44472d3502eSScott Long 		} else {
44572d3502eSScott Long 			pci_bar &= ~0x3;
44672d3502eSScott Long 		}
44772d3502eSScott Long 		len = 1 << (ffs(pci_bar) - 1);
44872d3502eSScott Long 	}
44972d3502eSScott Long 
450e6e272b9SScott Long 	DEVPRINTF((cbdev, "Opening BAR: type=%s, bar=%02x, len=%04x%s%s\n",
45163fa9f4cSJonathan Chen 	    (type == SYS_RES_MEMORY) ? "MEM" : "IO", bar, len,
45263fa9f4cSJonathan Chen 	    (type == SYS_RES_MEMORY && dinfo->mprefetchable & BARBIT(bar)) ?
453e6e272b9SScott Long 	    " (Prefetchable)" : "", type == SYS_RES_MEMORY ?
4541e962d00SScott Long 	    ((dinfo->mbelow1mb & BARBIT(bar)) ? " (Below 1Mb)" : "") : ""));
45563fa9f4cSJonathan Chen 
4567ba175acSWarner Losh 	resource_list_add(&dinfo->pci.resources, type, bar, 0UL, ~0UL, len);
457e6e272b9SScott Long 
4589fb92b64SScott Long 	/*
4599fb92b64SScott Long 	 * Mark the appropriate bit in the PCI command register so that
46001f2fb65SWarner Losh 	 * device drivers will know which type of BARs can be used.
4619fb92b64SScott Long 	 */
4629fb92b64SScott Long 	pci_enable_io(child, type);
463a3133b58SWarner Losh 	return (0);
4640db7e66cSJonathan Chen }
4650db7e66cSJonathan Chen 
46622acd92bSWarner Losh static int
46722acd92bSWarner Losh decode_tuple_unhandled(device_t cbdev, device_t child, int id,
46822acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
46922acd92bSWarner Losh     struct tuple_callbacks *info)
4700db7e66cSJonathan Chen {
47122acd92bSWarner Losh 	/* Make this message suck less XXX */
4720db7e66cSJonathan Chen 	printf("TUPLE: %s [%d] is unhandled! Bailing...", info->name, len);
473a3133b58SWarner Losh 	return (-1);
4740db7e66cSJonathan Chen }
4750db7e66cSJonathan Chen 
47622acd92bSWarner Losh static int
47722acd92bSWarner Losh decode_tuple_end(device_t cbdev, device_t child, int id,
47822acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
47922acd92bSWarner Losh     struct tuple_callbacks *info)
4800db7e66cSJonathan Chen {
48122acd92bSWarner Losh 	if (cardbus_cis_debug) {
4820c95c705SJonathan Chen 		printf("CIS reading done\n");
48322acd92bSWarner Losh 	}
484a3133b58SWarner Losh 	return (0);
4850db7e66cSJonathan Chen }
4860db7e66cSJonathan Chen 
487255b159fSJonathan Chen /*
488255b159fSJonathan Chen  * Functions to read the a tuple from the card
489255b159fSJonathan Chen  */
490255b159fSJonathan Chen 
4910db7e66cSJonathan Chen static int
49266e390feSWarner Losh cardbus_read_tuple_conf(device_t cbdev, device_t child, uint32_t start,
49366e390feSWarner Losh     uint32_t *off, int *tupleid, int *len, uint8_t *tupledata)
4947bec1dd5SJonathan Chen {
4957bec1dd5SJonathan Chen 	int i, j;
49666e390feSWarner Losh 	uint32_t e;
49766e390feSWarner Losh 	uint32_t loc;
4987bec1dd5SJonathan Chen 
49963fa9f4cSJonathan Chen 	loc = start + *off;
50049f158ccSJonathan Chen 
50149f158ccSJonathan Chen 	e = pci_read_config(child, loc - loc % 4, 4);
50249f158ccSJonathan Chen 	for (j = loc % 4; j > 0; j--)
5037bec1dd5SJonathan Chen 		e >>= 8;
5047bec1dd5SJonathan Chen 	*len = 0;
50549f158ccSJonathan Chen 	for (i = loc, j = -2; j < *len; j++, i++) {
5067bec1dd5SJonathan Chen 		if (i % 4 == 0)
5077bec1dd5SJonathan Chen 			e = pci_read_config(child, i, 4);
5087bec1dd5SJonathan Chen 		if (j == -2)
5097bec1dd5SJonathan Chen 			*tupleid = 0xff & e;
5107bec1dd5SJonathan Chen 		else if (j == -1)
5117bec1dd5SJonathan Chen 			*len = 0xff & e;
5127bec1dd5SJonathan Chen 		else
5137bec1dd5SJonathan Chen 			tupledata[j] = 0xff & e;
5147bec1dd5SJonathan Chen 		e >>= 8;
5157bec1dd5SJonathan Chen 	}
5167bec1dd5SJonathan Chen 	*off += *len + 2;
517a3133b58SWarner Losh 	return (0);
5187bec1dd5SJonathan Chen }
5197bec1dd5SJonathan Chen 
5207bec1dd5SJonathan Chen static int
52166e390feSWarner Losh cardbus_read_tuple_mem(device_t cbdev, struct resource *res, uint32_t start,
52266e390feSWarner Losh     uint32_t *off, int *tupleid, int *len, uint8_t *tupledata)
5230db7e66cSJonathan Chen {
52463fa9f4cSJonathan Chen 	bus_space_tag_t bt;
52563fa9f4cSJonathan Chen 	bus_space_handle_t bh;
52663fa9f4cSJonathan Chen 	int ret;
5270db7e66cSJonathan Chen 
52863fa9f4cSJonathan Chen 	bt = rman_get_bustag(res);
52963fa9f4cSJonathan Chen 	bh = rman_get_bushandle(res);
5300db7e66cSJonathan Chen 
53163fa9f4cSJonathan Chen 	*tupleid = bus_space_read_1(bt, bh, start + *off);
53263fa9f4cSJonathan Chen 	*len = bus_space_read_1(bt, bh, start + *off + 1);
53363fa9f4cSJonathan Chen 	bus_space_read_region_1(bt, bh, *off + start + 2, tupledata, *len);
53463fa9f4cSJonathan Chen 	ret = 0;
53563fa9f4cSJonathan Chen 	*off += *len + 2;
536a3133b58SWarner Losh 	return (ret);
5370db7e66cSJonathan Chen }
53863fa9f4cSJonathan Chen 
53963fa9f4cSJonathan Chen static int
54063fa9f4cSJonathan Chen cardbus_read_tuple(device_t cbdev, device_t child, struct resource *res,
54166e390feSWarner Losh     uint32_t start, uint32_t *off, int *tupleid, int *len,
54266e390feSWarner Losh     uint8_t *tupledata)
54363fa9f4cSJonathan Chen {
54463fa9f4cSJonathan Chen 	if (res == (struct resource*)~0UL) {
545a3133b58SWarner Losh 		return (cardbus_read_tuple_conf(cbdev, child, start, off,
546a3133b58SWarner Losh 		    tupleid, len, tupledata));
54763fa9f4cSJonathan Chen 	} else {
548a3133b58SWarner Losh 		return (cardbus_read_tuple_mem(cbdev, res, start, off,
549a3133b58SWarner Losh 		    tupleid, len, tupledata));
55063fa9f4cSJonathan Chen 	}
55163fa9f4cSJonathan Chen }
55263fa9f4cSJonathan Chen 
55363fa9f4cSJonathan Chen static void
55463fa9f4cSJonathan Chen cardbus_read_tuple_finish(device_t cbdev, device_t child, int rid,
55563fa9f4cSJonathan Chen     struct resource *res)
55663fa9f4cSJonathan Chen {
55763fa9f4cSJonathan Chen 	if (res != (struct resource*)~0UL) {
55863fa9f4cSJonathan Chen 		bus_release_resource(cbdev, SYS_RES_MEMORY, rid, res);
55963fa9f4cSJonathan Chen 		pci_write_config(child, rid, 0, 4);
56063fa9f4cSJonathan Chen 		PCI_DISABLE_IO(cbdev, child, SYS_RES_MEMORY);
56163fa9f4cSJonathan Chen 	}
56263fa9f4cSJonathan Chen }
56363fa9f4cSJonathan Chen 
56463fa9f4cSJonathan Chen static struct resource *
56566e390feSWarner Losh cardbus_read_tuple_init(device_t cbdev, device_t child, uint32_t *start,
56663fa9f4cSJonathan Chen     int *rid)
56763fa9f4cSJonathan Chen {
56866e390feSWarner Losh 	uint32_t testval;
56966e390feSWarner Losh 	uint32_t size;
57063fa9f4cSJonathan Chen 	struct resource *res;
57163fa9f4cSJonathan Chen 
57263fa9f4cSJonathan Chen 	switch (CARDBUS_CIS_SPACE(*start)) {
57363fa9f4cSJonathan Chen 	case CARDBUS_CIS_ASI_TUPLE:
574e6e272b9SScott Long 		/* CIS in PCI config space need no initialization */
57521677473SWarner Losh 		return ((struct resource*)~0UL);
57663fa9f4cSJonathan Chen 	case CARDBUS_CIS_ASI_BAR0:
57763fa9f4cSJonathan Chen 	case CARDBUS_CIS_ASI_BAR1:
57863fa9f4cSJonathan Chen 	case CARDBUS_CIS_ASI_BAR2:
57963fa9f4cSJonathan Chen 	case CARDBUS_CIS_ASI_BAR3:
58063fa9f4cSJonathan Chen 	case CARDBUS_CIS_ASI_BAR4:
58163fa9f4cSJonathan Chen 	case CARDBUS_CIS_ASI_BAR5:
58263fa9f4cSJonathan Chen 		*rid = CARDBUS_BASE0_REG + (CARDBUS_CIS_SPACE(*start) - 1) * 4;
58363fa9f4cSJonathan Chen 		break;
58463fa9f4cSJonathan Chen 	case CARDBUS_CIS_ASI_ROM:
58563fa9f4cSJonathan Chen 		*rid = CARDBUS_ROM_REG;
586e6e272b9SScott Long #if 0
587e6e272b9SScott Long 		/*
588e6e272b9SScott Long 		 * This mask doesn't contain the bit that actually enables
589e6e272b9SScott Long 		 * the Option ROM.
590e6e272b9SScott Long 		 */
59163fa9f4cSJonathan Chen 		pci_write_config(child, *rid, CARDBUS_ROM_ADDRMASK, 4);
592e6e272b9SScott Long #endif
59363fa9f4cSJonathan Chen 		break;
59463fa9f4cSJonathan Chen 	default:
59563fa9f4cSJonathan Chen 		device_printf(cbdev, "Unable to read CIS: Unknown space: %d\n",
59663fa9f4cSJonathan Chen 		    CARDBUS_CIS_SPACE(*start));
597a3133b58SWarner Losh 		return (NULL);
59863fa9f4cSJonathan Chen 	}
59963fa9f4cSJonathan Chen 
60063fa9f4cSJonathan Chen 	/* figure out how much space we need */
601e6e272b9SScott Long 	pci_write_config(child, *rid, 0xffffffff, 4);
60263fa9f4cSJonathan Chen 	testval = pci_read_config(child, *rid, 4);
603e6e272b9SScott Long 
604e6e272b9SScott Long 	/*
605e6e272b9SScott Long 	 * This bit has a different meaning depending if we are dealing
6061e06ae99SScott Long 	 * with a normal BAR or an Option ROM BAR.
607e6e272b9SScott Long 	 */
608e6e272b9SScott Long 	if (((testval & 0x1) == 0x1) && (*rid != CARDBUS_ROM_REG)) {
60963fa9f4cSJonathan Chen 		device_printf(cbdev, "CIS Space is IO, expecting memory.\n");
610a3133b58SWarner Losh 		return (NULL);
61163fa9f4cSJonathan Chen 	}
612e6e272b9SScott Long 
61363fa9f4cSJonathan Chen 	size = CARDBUS_MAPREG_MEM_SIZE(testval);
614e6e272b9SScott Long 	/* XXX Is this some kind of hack? */
61563fa9f4cSJonathan Chen 	if (size < 4096)
61663fa9f4cSJonathan Chen 		size = 4096;
61763fa9f4cSJonathan Chen 	/* allocate the memory space to read CIS */
61863fa9f4cSJonathan Chen 	res = bus_alloc_resource(cbdev, SYS_RES_MEMORY, rid, 0, ~0, size,
61963fa9f4cSJonathan Chen 	    rman_make_alignment_flags(size) | RF_ACTIVE);
62063fa9f4cSJonathan Chen 	if (res == NULL) {
62163fa9f4cSJonathan Chen 		device_printf(cbdev, "Unable to allocate resource "
62263fa9f4cSJonathan Chen 		    "to read CIS.\n");
623a3133b58SWarner Losh 		return (NULL);
62463fa9f4cSJonathan Chen 	}
62563fa9f4cSJonathan Chen 	pci_write_config(child, *rid,
62663fa9f4cSJonathan Chen 	    rman_get_start(res) | ((*rid == CARDBUS_ROM_REG)?
62763fa9f4cSJonathan Chen 		CARDBUS_ROM_ENABLE : 0),
62863fa9f4cSJonathan Chen 	    4);
62963fa9f4cSJonathan Chen 	PCI_ENABLE_IO(cbdev, child, SYS_RES_MEMORY);
63063fa9f4cSJonathan Chen 
63163fa9f4cSJonathan Chen 	/* Flip to the right ROM image if CIS is in ROM */
63263fa9f4cSJonathan Chen 	if (CARDBUS_CIS_SPACE(*start) == CARDBUS_CIS_ASI_ROM) {
63363fa9f4cSJonathan Chen 		bus_space_tag_t bt;
63463fa9f4cSJonathan Chen 		bus_space_handle_t bh;
63566e390feSWarner Losh 		uint32_t imagesize;
63666e390feSWarner Losh 		uint32_t imagebase = 0;
63766e390feSWarner Losh 		uint32_t pcidata;
63866e390feSWarner Losh 		uint16_t romsig;
63963fa9f4cSJonathan Chen 		int romnum = 0;
640e6e272b9SScott Long 		int imagenum;
64163fa9f4cSJonathan Chen 
64263fa9f4cSJonathan Chen 		bt = rman_get_bustag(res);
64363fa9f4cSJonathan Chen 		bh = rman_get_bushandle(res);
64463fa9f4cSJonathan Chen 
64563fa9f4cSJonathan Chen 		imagenum = CARDBUS_CIS_ASI_ROM_IMAGE(*start);
64663fa9f4cSJonathan Chen 		for (romnum = 0;; romnum++) {
647e6e272b9SScott Long 			romsig = bus_space_read_2(bt, bh,
648e6e272b9SScott Long 			    imagebase + CARDBUS_EXROM_SIGNATURE);
649e6e272b9SScott Long 			if (romsig != 0xaa55) {
65063fa9f4cSJonathan Chen 				device_printf(cbdev, "Bad header in rom %d: "
651e6e272b9SScott Long 				    "[%x] %04x\n", romnum, imagebase +
652e6e272b9SScott Long 				    CARDBUS_EXROM_SIGNATURE, romsig);
65363fa9f4cSJonathan Chen 				bus_release_resource(cbdev, SYS_RES_MEMORY,
65463fa9f4cSJonathan Chen 				    *rid, res);
65563fa9f4cSJonathan Chen 				*rid = 0;
656a3133b58SWarner Losh 				return (NULL);
65763fa9f4cSJonathan Chen 			}
658e6e272b9SScott Long 
659e6e272b9SScott Long 			/*
660e6e272b9SScott Long 			 * If this was the Option ROM image that we were
661e6e272b9SScott Long 			 * looking for, then we are done.
662e6e272b9SScott Long 			 */
663e6e272b9SScott Long 			if (romnum == imagenum)
664e6e272b9SScott Long 				break;
665e6e272b9SScott Long 
666e6e272b9SScott Long 			/* Find out where the next Option ROM image is */
667e6e272b9SScott Long 			pcidata = imagebase + bus_space_read_2(bt, bh,
668e6e272b9SScott Long 			    imagebase + CARDBUS_EXROM_DATA_PTR);
66963fa9f4cSJonathan Chen 			imagesize = bus_space_read_2(bt, bh,
670e6e272b9SScott Long 			    pcidata + CARDBUS_EXROM_DATA_IMAGE_LENGTH);
6710db7e66cSJonathan Chen 
6727bec1dd5SJonathan Chen 			if (imagesize == 0) {
6730db7e66cSJonathan Chen 				/*
6740db7e66cSJonathan Chen 				 * XXX some ROMs seem to have this as zero,
6750db7e66cSJonathan Chen 				 * can we assume this means 1 block?
6760db7e66cSJonathan Chen 				 */
677e6e272b9SScott Long 				device_printf(cbdev, "Warning, size of Option "
678e6e272b9SScott Long 				    "ROM image %d is 0 bytes, assuming 512 "
679e6e272b9SScott Long 				    "bytes.\n", romnum);
6800db7e66cSJonathan Chen 				imagesize = 1;
6817bec1dd5SJonathan Chen 			}
682e6e272b9SScott Long 
683e6e272b9SScott Long 			/* Image size is in 512 byte units */
6840db7e66cSJonathan Chen 			imagesize <<= 9;
6850db7e66cSJonathan Chen 
686e6e272b9SScott Long 			if ((bus_space_read_1(bt, bh, pcidata +
6871e06ae99SScott Long 			    CARDBUS_EXROM_DATA_INDICATOR) & 0x80) != 0) {
688e6e272b9SScott Long 				device_printf(cbdev, "Cannot find CIS in "
689e6e272b9SScott Long 				    "Option ROM\n");
690e6e272b9SScott Long 				bus_release_resource(cbdev, SYS_RES_MEMORY,
691e6e272b9SScott Long 				    *rid, res);
692e6e272b9SScott Long 				*rid = 0;
693a3133b58SWarner Losh 				return (NULL);
6940db7e66cSJonathan Chen 			}
695e6e272b9SScott Long 			imagebase += imagesize;
6960db7e66cSJonathan Chen 		}
697e6e272b9SScott Long 		*start = imagebase + CARDBUS_CIS_ADDR(*start);
6980db7e66cSJonathan Chen 	} else {
699e6e272b9SScott Long 		*start = CARDBUS_CIS_ADDR(*start);
7000db7e66cSJonathan Chen 	}
701e6e272b9SScott Long 
702a3133b58SWarner Losh 	return (res);
7037bec1dd5SJonathan Chen }
7047bec1dd5SJonathan Chen 
705255b159fSJonathan Chen /*
706255b159fSJonathan Chen  * Dispatch the right handler function per tuple
707255b159fSJonathan Chen  */
708255b159fSJonathan Chen 
7097bec1dd5SJonathan Chen static int
710255b159fSJonathan Chen decode_tuple(device_t cbdev, device_t child, int tupleid, int len,
71166e390feSWarner Losh     uint8_t *tupledata, uint32_t start, uint32_t *off,
7120c95c705SJonathan Chen     struct tuple_callbacks *callbacks)
7137bec1dd5SJonathan Chen {
7147bec1dd5SJonathan Chen 	int i;
7150c95c705SJonathan Chen 	for (i = 0; callbacks[i].id != CISTPL_GENERIC; i++) {
7160c95c705SJonathan Chen 		if (tupleid == callbacks[i].id)
717a3133b58SWarner Losh 			return (callbacks[i].func(cbdev, child, tupleid, len,
718a3133b58SWarner Losh 			    tupledata, start, off, &callbacks[i]));
7197bec1dd5SJonathan Chen 	}
7207bec1dd5SJonathan Chen 
72149f158ccSJonathan Chen 	if (tupleid < CISTPL_CUSTOMSTART) {
722255b159fSJonathan Chen 		device_printf(cbdev, "Undefined tuple encountered, "
723255b159fSJonathan Chen 		    "CIS parsing terminated\n");
724a3133b58SWarner Losh 		return (EINVAL);
72549f158ccSJonathan Chen 	}
726a3133b58SWarner Losh 	return (callbacks[i].func(cbdev, child, tupleid, len,
727a3133b58SWarner Losh 	    tupledata, start, off, NULL));
7280db7e66cSJonathan Chen }
7290db7e66cSJonathan Chen 
7300c95c705SJonathan Chen static int
731255b159fSJonathan Chen cardbus_parse_cis(device_t cbdev, device_t child,
7320c95c705SJonathan Chen     struct tuple_callbacks *callbacks)
7330db7e66cSJonathan Chen {
73466e390feSWarner Losh 	uint8_t tupledata[MAXTUPLESIZE];
7357bec1dd5SJonathan Chen 	int tupleid;
7367bec1dd5SJonathan Chen 	int len;
7377bec1dd5SJonathan Chen 	int expect_linktarget;
73866e390feSWarner Losh 	uint32_t start, off;
73963fa9f4cSJonathan Chen 	struct resource *res;
74063fa9f4cSJonathan Chen 	int rid;
7410db7e66cSJonathan Chen 
7420db7e66cSJonathan Chen 	bzero(tupledata, MAXTUPLESIZE);
7437bec1dd5SJonathan Chen 	expect_linktarget = TRUE;
744e6e272b9SScott Long 	if ((start = pci_read_config(child, CARDBUS_CIS_REG, 4)) == 0)
745e6e272b9SScott Long 		return (ENXIO);
74649f158ccSJonathan Chen 	off = 0;
74763fa9f4cSJonathan Chen 	res = cardbus_read_tuple_init(cbdev, child, &start, &rid);
74863fa9f4cSJonathan Chen 	if (res == NULL)
749a3133b58SWarner Losh 		return (ENXIO);
7501e962d00SScott Long 
7517bec1dd5SJonathan Chen 	do {
75263fa9f4cSJonathan Chen 		if (0 != cardbus_read_tuple(cbdev, child, res, start, &off,
75363fa9f4cSJonathan Chen 		    &tupleid, &len, tupledata)) {
75463fa9f4cSJonathan Chen 			device_printf(cbdev, "Failed to read CIS.\n");
75563fa9f4cSJonathan Chen 			cardbus_read_tuple_finish(cbdev, child, rid, res);
756a3133b58SWarner Losh 			return (ENXIO);
75763fa9f4cSJonathan Chen 		}
7587bec1dd5SJonathan Chen 
7597bec1dd5SJonathan Chen 		if (expect_linktarget && tupleid != CISTPL_LINKTARGET) {
760255b159fSJonathan Chen 			device_printf(cbdev, "Expecting link target, got 0x%x\n",
7617bec1dd5SJonathan Chen 			    tupleid);
76263fa9f4cSJonathan Chen 			cardbus_read_tuple_finish(cbdev, child, rid, res);
763a3133b58SWarner Losh 			return (EINVAL);
7647bec1dd5SJonathan Chen 		}
765255b159fSJonathan Chen 		expect_linktarget = decode_tuple(cbdev, child, tupleid, len,
76663fa9f4cSJonathan Chen 		    tupledata, start, &off, callbacks);
76763fa9f4cSJonathan Chen 		if (expect_linktarget != 0) {
76863fa9f4cSJonathan Chen 			cardbus_read_tuple_finish(cbdev, child, rid, res);
769a3133b58SWarner Losh 			return (expect_linktarget);
77063fa9f4cSJonathan Chen 		}
7717bec1dd5SJonathan Chen 	} while (tupleid != CISTPL_END);
77263fa9f4cSJonathan Chen 	cardbus_read_tuple_finish(cbdev, child, rid, res);
773a3133b58SWarner Losh 	return (0);
7740db7e66cSJonathan Chen }
7750db7e66cSJonathan Chen 
77651715fe7SWarner Losh static void
77751715fe7SWarner Losh cardbus_do_res(struct resource_list_entry *rle, device_t child, uint32_t start)
77851715fe7SWarner Losh {
77951715fe7SWarner Losh 	rle->start = start;
78051715fe7SWarner Losh 	rle->end = start + rle->count - 1;
78151715fe7SWarner Losh 	pci_write_config(child, rle->rid, rle->start, 4);
78251715fe7SWarner Losh }
78351715fe7SWarner Losh 
78463fa9f4cSJonathan Chen static int
78563fa9f4cSJonathan Chen barsort(const void *a, const void *b)
78663fa9f4cSJonathan Chen {
78778b226dcSAlfred Perlstein 	return ((*(const struct resource_list_entry * const *)b)->count -
78878b226dcSAlfred Perlstein 	    (*(const struct resource_list_entry * const *)a)->count);
78963fa9f4cSJonathan Chen }
79063fa9f4cSJonathan Chen 
79163fa9f4cSJonathan Chen static int
79263fa9f4cSJonathan Chen cardbus_alloc_resources(device_t cbdev, device_t child)
79363fa9f4cSJonathan Chen {
79463fa9f4cSJonathan Chen 	struct cardbus_devinfo *dinfo = device_get_ivars(child);
79563fa9f4cSJonathan Chen 	int count;
79663fa9f4cSJonathan Chen 	struct resource_list_entry *rle;
79763fa9f4cSJonathan Chen 	struct resource_list_entry **barlist;
79863fa9f4cSJonathan Chen 	int tmp;
79966e390feSWarner Losh 	uint32_t mem_psize = 0, mem_nsize = 0, io_size = 0;
80063fa9f4cSJonathan Chen 	struct resource *res;
80166e390feSWarner Losh 	uint32_t start,end;
80221677473SWarner Losh 	int rid, flags;
80363fa9f4cSJonathan Chen 
80463fa9f4cSJonathan Chen 	count = 0;
80521677473SWarner Losh 	SLIST_FOREACH(rle, &dinfo->pci.resources, link) {
80663fa9f4cSJonathan Chen 		count++;
80721677473SWarner Losh 	}
80863fa9f4cSJonathan Chen 	if (count == 0)
809a3133b58SWarner Losh 		return (0);
81063fa9f4cSJonathan Chen 	barlist = malloc(sizeof(struct resource_list_entry*) * count, M_DEVBUF,
811a163d034SWarner Losh 	    M_WAITOK);
81263fa9f4cSJonathan Chen 	count = 0;
81321677473SWarner Losh 	SLIST_FOREACH(rle, &dinfo->pci.resources, link) {
81463fa9f4cSJonathan Chen 		barlist[count] = rle;
81563fa9f4cSJonathan Chen 		if (rle->type == SYS_RES_IOPORT) {
81663fa9f4cSJonathan Chen 			io_size += rle->count;
81763fa9f4cSJonathan Chen 		} else if (rle->type == SYS_RES_MEMORY) {
81863fa9f4cSJonathan Chen 			if (dinfo->mprefetchable & BARBIT(rle->rid))
81963fa9f4cSJonathan Chen 				mem_psize += rle->count;
82063fa9f4cSJonathan Chen 			else
82163fa9f4cSJonathan Chen 				mem_nsize += rle->count;
82263fa9f4cSJonathan Chen 		}
82363fa9f4cSJonathan Chen 		count++;
82463fa9f4cSJonathan Chen 	}
82563fa9f4cSJonathan Chen 
82663fa9f4cSJonathan Chen 	/*
82763fa9f4cSJonathan Chen 	 * We want to allocate the largest resource first, so that our
82863fa9f4cSJonathan Chen 	 * allocated memory is packed.
82963fa9f4cSJonathan Chen 	 */
83063fa9f4cSJonathan Chen 	qsort(barlist, count, sizeof(struct resource_list_entry*), barsort);
83163fa9f4cSJonathan Chen 
83263fa9f4cSJonathan Chen 	/* Allocate prefetchable memory */
83363fa9f4cSJonathan Chen 	flags = 0;
83463fa9f4cSJonathan Chen 	for (tmp = 0; tmp < count; tmp++) {
83551715fe7SWarner Losh 		rle = barlist[tmp];
83651715fe7SWarner Losh 		if (rle->res == NULL &&
83751715fe7SWarner Losh 		    rle->type == SYS_RES_MEMORY &&
83851715fe7SWarner Losh 		    dinfo->mprefetchable & BARBIT(rle->rid)) {
83951715fe7SWarner Losh 			flags = rman_make_alignment_flags(rle->count);
84063fa9f4cSJonathan Chen 			break;
84163fa9f4cSJonathan Chen 		}
84263fa9f4cSJonathan Chen 	}
84363fa9f4cSJonathan Chen 	if (flags > 0) { /* If any prefetchable memory is requested... */
84463fa9f4cSJonathan Chen 		/*
84563fa9f4cSJonathan Chen 		 * First we allocate one big space for all resources of this
84663fa9f4cSJonathan Chen 		 * type.  We do this because our parent, pccbb, needs to open
84763fa9f4cSJonathan Chen 		 * a window to forward all addresses within the window, and
84863fa9f4cSJonathan Chen 		 * it would be best if nobody else has resources allocated
84963fa9f4cSJonathan Chen 		 * within the window.
85063fa9f4cSJonathan Chen 		 * (XXX: Perhaps there might be a better way to do this?)
85163fa9f4cSJonathan Chen 		 */
85263fa9f4cSJonathan Chen 		rid = 0;
85363fa9f4cSJonathan Chen 		res = bus_alloc_resource(cbdev, SYS_RES_MEMORY, &rid, 0,
85463fa9f4cSJonathan Chen 		    (dinfo->mprefetchable & dinfo->mbelow1mb)?0xFFFFF:~0UL,
85563fa9f4cSJonathan Chen 		    mem_psize, flags);
8566d9fcd03SWarner Losh 		if (res == NULL) {
8576d9fcd03SWarner Losh 			device_printf(cbdev,
8586d9fcd03SWarner Losh 			    "Can't get memory for prefetch mem\n");
859b44f8087SPoul-Henning Kamp 			free(barlist, M_DEVBUF);
8606d9fcd03SWarner Losh 			return (EIO);
8616d9fcd03SWarner Losh 		}
86263fa9f4cSJonathan Chen 		start = rman_get_start(res);
86363fa9f4cSJonathan Chen 		end = rman_get_end(res);
86463fa9f4cSJonathan Chen 		DEVPRINTF((cbdev, "Prefetchable memory at %x-%x\n", start, end));
86563fa9f4cSJonathan Chen 		/*
86663fa9f4cSJonathan Chen 		 * Now that we know the region is free, release it and hand it
86763fa9f4cSJonathan Chen 		 * out piece by piece.
86863fa9f4cSJonathan Chen 		 */
86963fa9f4cSJonathan Chen 		bus_release_resource(cbdev, SYS_RES_MEMORY, rid, res);
87063fa9f4cSJonathan Chen 		for (tmp = 0; tmp < count; tmp++) {
87151715fe7SWarner Losh 			rle = barlist[tmp];
87251715fe7SWarner Losh 			if (rle->type == SYS_RES_MEMORY &&
87351715fe7SWarner Losh 			    dinfo->mprefetchable & BARBIT(rle->rid)) {
87451715fe7SWarner Losh 				cardbus_do_res(rle, child, start);
87551715fe7SWarner Losh 				start += rle->count;
87663fa9f4cSJonathan Chen 			}
87763fa9f4cSJonathan Chen 		}
87821677473SWarner Losh 	}
87963fa9f4cSJonathan Chen 
88063fa9f4cSJonathan Chen 	/* Allocate non-prefetchable memory */
88163fa9f4cSJonathan Chen 	flags = 0;
88263fa9f4cSJonathan Chen 	for (tmp = 0; tmp < count; tmp++) {
88351715fe7SWarner Losh 		rle = barlist[tmp];
88451715fe7SWarner Losh 		if (rle->type == SYS_RES_MEMORY &&
88551715fe7SWarner Losh 		    (dinfo->mprefetchable & BARBIT(rle->rid)) == 0) {
88651715fe7SWarner Losh 			flags = rman_make_alignment_flags(rle->count);
88763fa9f4cSJonathan Chen 			break;
88863fa9f4cSJonathan Chen 		}
88963fa9f4cSJonathan Chen 	}
89063fa9f4cSJonathan Chen 	if (flags > 0) { /* If any non-prefetchable memory is requested... */
89163fa9f4cSJonathan Chen 		/*
89263fa9f4cSJonathan Chen 		 * First we allocate one big space for all resources of this
89363fa9f4cSJonathan Chen 		 * type.  We do this because our parent, pccbb, needs to open
89463fa9f4cSJonathan Chen 		 * a window to forward all addresses within the window, and
89563fa9f4cSJonathan Chen 		 * it would be best if nobody else has resources allocated
89663fa9f4cSJonathan Chen 		 * within the window.
89763fa9f4cSJonathan Chen 		 * (XXX: Perhaps there might be a better way to do this?)
89863fa9f4cSJonathan Chen 		 */
89963fa9f4cSJonathan Chen 		rid = 0;
90063fa9f4cSJonathan Chen 		res = bus_alloc_resource(cbdev, SYS_RES_MEMORY, &rid, 0,
90163fa9f4cSJonathan Chen 		    ((~dinfo->mprefetchable) & dinfo->mbelow1mb)?0xFFFFF:~0UL,
90263fa9f4cSJonathan Chen 		    mem_nsize, flags);
9036d9fcd03SWarner Losh 		if (res == NULL) {
9046d9fcd03SWarner Losh 			device_printf(cbdev,
9056d9fcd03SWarner Losh 			    "Can't get memory for non-prefetch mem\n");
906b44f8087SPoul-Henning Kamp 			free(barlist, M_DEVBUF);
9076d9fcd03SWarner Losh 			return (EIO);
9086d9fcd03SWarner Losh 		}
90963fa9f4cSJonathan Chen 		start = rman_get_start(res);
91063fa9f4cSJonathan Chen 		end = rman_get_end(res);
91163fa9f4cSJonathan Chen 		DEVPRINTF((cbdev, "Non-prefetchable memory at %x-%x\n",
91263fa9f4cSJonathan Chen 		    start, end));
91363fa9f4cSJonathan Chen 		/*
91463fa9f4cSJonathan Chen 		 * Now that we know the region is free, release it and hand it
91563fa9f4cSJonathan Chen 		 * out piece by piece.
91663fa9f4cSJonathan Chen 		 */
91763fa9f4cSJonathan Chen 		bus_release_resource(cbdev, SYS_RES_MEMORY, rid, res);
91863fa9f4cSJonathan Chen 		for (tmp = 0; tmp < count; tmp++) {
91951715fe7SWarner Losh 			rle = barlist[tmp];
92051715fe7SWarner Losh 			if (rle->type == SYS_RES_MEMORY &&
92151715fe7SWarner Losh 			    (dinfo->mprefetchable & BARBIT(rle->rid)) == 0) {
92251715fe7SWarner Losh 				cardbus_do_res(rle, child, start);
92351715fe7SWarner Losh 				start += rle->count;
92463fa9f4cSJonathan Chen 			}
92563fa9f4cSJonathan Chen 		}
92663fa9f4cSJonathan Chen 	}
92763fa9f4cSJonathan Chen 
92863fa9f4cSJonathan Chen 	/* Allocate IO ports */
92963fa9f4cSJonathan Chen 	flags = 0;
93063fa9f4cSJonathan Chen 	for (tmp = 0; tmp < count; tmp++) {
93151715fe7SWarner Losh 		rle = barlist[tmp];
93251715fe7SWarner Losh 		if (rle->type == SYS_RES_IOPORT) {
93351715fe7SWarner Losh 			flags = rman_make_alignment_flags(rle->count);
93463fa9f4cSJonathan Chen 			break;
93563fa9f4cSJonathan Chen 		}
93663fa9f4cSJonathan Chen 	}
93763fa9f4cSJonathan Chen 	if (flags > 0) { /* If any IO port is requested... */
93863fa9f4cSJonathan Chen 		/*
93963fa9f4cSJonathan Chen 		 * First we allocate one big space for all resources of this
94063fa9f4cSJonathan Chen 		 * type.  We do this because our parent, pccbb, needs to open
94163fa9f4cSJonathan Chen 		 * a window to forward all addresses within the window, and
94263fa9f4cSJonathan Chen 		 * it would be best if nobody else has resources allocated
94363fa9f4cSJonathan Chen 		 * within the window.
94463fa9f4cSJonathan Chen 		 * (XXX: Perhaps there might be a better way to do this?)
94563fa9f4cSJonathan Chen 		 */
94663fa9f4cSJonathan Chen 		rid = 0;
94721677473SWarner Losh 		res = bus_alloc_resource(cbdev, SYS_RES_IOPORT, &rid, 0,
94821677473SWarner Losh 		    (dinfo->ibelow1mb)?0xFFFFF:~0UL, io_size, flags);
9496d9fcd03SWarner Losh 		if (res == NULL) {
9506d9fcd03SWarner Losh 			device_printf(cbdev,
9516d9fcd03SWarner Losh 			    "Can't get memory for IO ports\n");
952b44f8087SPoul-Henning Kamp 			free(barlist, M_DEVBUF);
9536d9fcd03SWarner Losh 			return (EIO);
9546d9fcd03SWarner Losh 		}
95563fa9f4cSJonathan Chen 		start = rman_get_start(res);
95663fa9f4cSJonathan Chen 		end = rman_get_end(res);
95763fa9f4cSJonathan Chen 		DEVPRINTF((cbdev, "IO port at %x-%x\n", start, end));
95863fa9f4cSJonathan Chen 		/*
95963fa9f4cSJonathan Chen 		 * Now that we know the region is free, release it and hand it
96021677473SWarner Losh 		 * out piece by piece.
96163fa9f4cSJonathan Chen 		 */
96263fa9f4cSJonathan Chen 		bus_release_resource(cbdev, SYS_RES_IOPORT, rid, res);
96363fa9f4cSJonathan Chen 		for (tmp = 0; tmp < count; tmp++) {
96451715fe7SWarner Losh 			rle = barlist[tmp];
96551715fe7SWarner Losh 			if (rle->type == SYS_RES_IOPORT) {
96651715fe7SWarner Losh 				cardbus_do_res(rle, child, start);
96751715fe7SWarner Losh 				start += rle->count;
96863fa9f4cSJonathan Chen 			}
96963fa9f4cSJonathan Chen 		}
97063fa9f4cSJonathan Chen 	}
97163fa9f4cSJonathan Chen 
97263fa9f4cSJonathan Chen 	/* Allocate IRQ */
97363fa9f4cSJonathan Chen 	rid = 0;
97463fa9f4cSJonathan Chen 	res = bus_alloc_resource(cbdev, SYS_RES_IRQ, &rid, 0, ~0UL, 1,
97563fa9f4cSJonathan Chen 	    RF_SHAREABLE);
9766d9fcd03SWarner Losh 	if (res == NULL) {
9776d9fcd03SWarner Losh 		device_printf(cbdev, "Can't get memory for irq\n");
978b44f8087SPoul-Henning Kamp 		free(barlist, M_DEVBUF);
9796d9fcd03SWarner Losh 		return (EIO);
9806d9fcd03SWarner Losh 	}
98151715fe7SWarner Losh 	start = rman_get_start(res);
98251715fe7SWarner Losh 	end = rman_get_end(res);
98351715fe7SWarner Losh 	bus_release_resource(cbdev, SYS_RES_IRQ, rid, res);
98451715fe7SWarner Losh 	resource_list_add(&dinfo->pci.resources, SYS_RES_IRQ, rid, start, end,
98551715fe7SWarner Losh 	    1);
98621677473SWarner Losh 	dinfo->pci.cfg.intline = rman_get_start(res);
98721677473SWarner Losh 	pci_write_config(child, PCIR_INTLINE, rman_get_start(res), 1);
98863fa9f4cSJonathan Chen 
989214c0b3dSWarner Losh 	free(barlist, M_DEVBUF);
990a3133b58SWarner Losh 	return (0);
99163fa9f4cSJonathan Chen }
99263fa9f4cSJonathan Chen 
99363fa9f4cSJonathan Chen /*
99463fa9f4cSJonathan Chen  * Adding a memory/io resource (sans CIS)
99563fa9f4cSJonathan Chen  */
99663fa9f4cSJonathan Chen 
99763fa9f4cSJonathan Chen static void
99863fa9f4cSJonathan Chen cardbus_add_map(device_t cbdev, device_t child, int reg)
99963fa9f4cSJonathan Chen {
100063fa9f4cSJonathan Chen 	struct cardbus_devinfo *dinfo = device_get_ivars(child);
100121677473SWarner Losh 	struct resource_list_entry *rle;
100266e390feSWarner Losh 	uint32_t size;
100366e390feSWarner Losh 	uint32_t testval;
100463fa9f4cSJonathan Chen 	int type;
100563fa9f4cSJonathan Chen 
100621677473SWarner Losh 	SLIST_FOREACH(rle, &dinfo->pci.resources, link) {
100721677473SWarner Losh 		if (rle->rid == reg)
100863fa9f4cSJonathan Chen 			return;
100921677473SWarner Losh 	}
101063fa9f4cSJonathan Chen 
101163fa9f4cSJonathan Chen 	if (reg == CARDBUS_ROM_REG)
101263fa9f4cSJonathan Chen 		testval = CARDBUS_ROM_ADDRMASK;
101363fa9f4cSJonathan Chen 	else
101463fa9f4cSJonathan Chen 		testval = ~0;
101563fa9f4cSJonathan Chen 
101663fa9f4cSJonathan Chen 	pci_write_config(child, reg, testval, 4);
101763fa9f4cSJonathan Chen 	testval = pci_read_config(child, reg, 4);
101863fa9f4cSJonathan Chen 
101963fa9f4cSJonathan Chen 	if (testval == ~0 || testval == 0)
102063fa9f4cSJonathan Chen 		return;
102163fa9f4cSJonathan Chen 
102263fa9f4cSJonathan Chen 	if ((testval & 1) == 0)
102363fa9f4cSJonathan Chen 		type = SYS_RES_MEMORY;
102463fa9f4cSJonathan Chen 	else
102563fa9f4cSJonathan Chen 		type = SYS_RES_IOPORT;
102663fa9f4cSJonathan Chen 
102763fa9f4cSJonathan Chen 	size = CARDBUS_MAPREG_MEM_SIZE(testval);
102821677473SWarner Losh 	device_printf(cbdev, "Resource not specified in CIS: id=%x, size=%x\n",
102921677473SWarner Losh 	    reg, size);
103021677473SWarner Losh 	resource_list_add(&dinfo->pci.resources, type, reg, 0UL, ~0UL, size);
103163fa9f4cSJonathan Chen }
103263fa9f4cSJonathan Chen 
103363fa9f4cSJonathan Chen static void
103463fa9f4cSJonathan Chen cardbus_pickup_maps(device_t cbdev, device_t child)
103563fa9f4cSJonathan Chen {
103663fa9f4cSJonathan Chen 	struct cardbus_devinfo *dinfo = device_get_ivars(child);
103763fa9f4cSJonathan Chen 	struct cardbus_quirk *q;
103863fa9f4cSJonathan Chen 	int reg;
103963fa9f4cSJonathan Chen 
104063fa9f4cSJonathan Chen 	/*
104163fa9f4cSJonathan Chen 	 * Try to pick up any resources that was not specified in CIS.
104263fa9f4cSJonathan Chen 	 * Some devices (eg, 3c656) does not list all resources required by
104363fa9f4cSJonathan Chen 	 * the driver in its CIS.
104463fa9f4cSJonathan Chen 	 * XXX: should we do this or use quirks?
104563fa9f4cSJonathan Chen 	 */
10467ba175acSWarner Losh 	for (reg = 0; reg < dinfo->pci.cfg.nummaps; reg++) {
104763fa9f4cSJonathan Chen 		cardbus_add_map(cbdev, child, PCIR_MAPS + reg * 4);
104863fa9f4cSJonathan Chen 	}
104963fa9f4cSJonathan Chen 
105063fa9f4cSJonathan Chen 	for (q = &cardbus_quirks[0]; q->devid; q++) {
105121677473SWarner Losh 		if (q->devid == ((dinfo->pci.cfg.device << 16) | dinfo->pci.cfg.vendor)
105263fa9f4cSJonathan Chen 		    && q->type == CARDBUS_QUIRK_MAP_REG) {
105363fa9f4cSJonathan Chen 			cardbus_add_map(cbdev, child, q->arg1);
105463fa9f4cSJonathan Chen 		}
105563fa9f4cSJonathan Chen 	}
105663fa9f4cSJonathan Chen }
105763fa9f4cSJonathan Chen 
10580c95c705SJonathan Chen int
105966e390feSWarner Losh cardbus_cis_read(device_t cbdev, device_t child, uint8_t id,
10600c95c705SJonathan Chen     struct cis_tupleinfo **buff, int *nret)
10610c95c705SJonathan Chen {
10620c95c705SJonathan Chen 	struct tuple_callbacks cisread_callbacks[] = {
10630c95c705SJonathan Chen 		MAKETUPLE(NULL,			nothing),
10640c95c705SJonathan Chen 		/* first entry will be overwritten */
10650c95c705SJonathan Chen 		MAKETUPLE(NULL,			nothing),
10660c95c705SJonathan Chen 		MAKETUPLE(DEVICE,		nothing),
10670c95c705SJonathan Chen 		MAKETUPLE(LONG_LINK_CB,		unhandled),
10680c95c705SJonathan Chen 		MAKETUPLE(INDIRECT,		unhandled),
10690c95c705SJonathan Chen 		MAKETUPLE(CONFIG_CB,		nothing),
10700c95c705SJonathan Chen 		MAKETUPLE(CFTABLE_ENTRY_CB,	nothing),
10710c95c705SJonathan Chen 		MAKETUPLE(LONGLINK_MFC,		unhandled),
10720c95c705SJonathan Chen 		MAKETUPLE(BAR,			nothing),
10730c95c705SJonathan Chen 		MAKETUPLE(PWR_MGMNT,		nothing),
10740c95c705SJonathan Chen 		MAKETUPLE(EXTDEVICE,		nothing),
10750c95c705SJonathan Chen 		MAKETUPLE(CHECKSUM,		nothing),
10760c95c705SJonathan Chen 		MAKETUPLE(LONGLINK_A,		unhandled),
10770c95c705SJonathan Chen 		MAKETUPLE(LONGLINK_C,		unhandled),
10780c95c705SJonathan Chen 		MAKETUPLE(LINKTARGET,		nothing),
10790c95c705SJonathan Chen 		MAKETUPLE(NO_LINK,		nothing),
10800c95c705SJonathan Chen 		MAKETUPLE(VERS_1,		nothing),
10810c95c705SJonathan Chen 		MAKETUPLE(ALTSTR,		nothing),
10820c95c705SJonathan Chen 		MAKETUPLE(DEVICE_A,		nothing),
10830c95c705SJonathan Chen 		MAKETUPLE(JEDEC_C,		nothing),
10840c95c705SJonathan Chen 		MAKETUPLE(JEDEC_A,		nothing),
10850c95c705SJonathan Chen 		MAKETUPLE(CONFIG,		nothing),
10860c95c705SJonathan Chen 		MAKETUPLE(CFTABLE_ENTRY,	nothing),
10870c95c705SJonathan Chen 		MAKETUPLE(DEVICE_OC,		nothing),
10880c95c705SJonathan Chen 		MAKETUPLE(DEVICE_OA,		nothing),
10890c95c705SJonathan Chen 		MAKETUPLE(DEVICE_GEO,		nothing),
10900c95c705SJonathan Chen 		MAKETUPLE(DEVICE_GEO_A,		nothing),
10910c95c705SJonathan Chen 		MAKETUPLE(MANFID,		nothing),
10920c95c705SJonathan Chen 		MAKETUPLE(FUNCID,		nothing),
10930c95c705SJonathan Chen 		MAKETUPLE(FUNCE,		nothing),
10940c95c705SJonathan Chen 		MAKETUPLE(SWIL,			nothing),
10950c95c705SJonathan Chen 		MAKETUPLE(VERS_2,		nothing),
10960c95c705SJonathan Chen 		MAKETUPLE(FORMAT,		nothing),
10970c95c705SJonathan Chen 		MAKETUPLE(GEOMETRY,		nothing),
10980c95c705SJonathan Chen 		MAKETUPLE(BYTEORDER,		nothing),
10990c95c705SJonathan Chen 		MAKETUPLE(DATE,			nothing),
11000c95c705SJonathan Chen 		MAKETUPLE(BATTERY,		nothing),
11010c95c705SJonathan Chen 		MAKETUPLE(ORG,			nothing),
11020c95c705SJonathan Chen 		MAKETUPLE(END,			end),
11030c95c705SJonathan Chen 		MAKETUPLE(GENERIC,		nothing),
11040c95c705SJonathan Chen 	};
11050c95c705SJonathan Chen 	int ret;
11060c95c705SJonathan Chen 
11070c95c705SJonathan Chen 	cisread_callbacks[0].id = id;
11080c95c705SJonathan Chen 	cisread_callbacks[0].name = "COPY";
11090c95c705SJonathan Chen 	cisread_callbacks[0].func = decode_tuple_copy;
11100c95c705SJonathan Chen 	ncisread_buf = 0;
11110c95c705SJonathan Chen 	cisread_buf = NULL;
1112255b159fSJonathan Chen 	ret = cardbus_parse_cis(cbdev, child, cisread_callbacks);
11130c95c705SJonathan Chen 
11140c95c705SJonathan Chen 	*buff = cisread_buf;
11150c95c705SJonathan Chen 	*nret = ncisread_buf;
1116a3133b58SWarner Losh 	return (ret);
11170c95c705SJonathan Chen }
11180c95c705SJonathan Chen 
11190c95c705SJonathan Chen void
1120255b159fSJonathan Chen cardbus_cis_free(device_t cbdev, struct cis_tupleinfo *buff, int *nret)
11210c95c705SJonathan Chen {
11220c95c705SJonathan Chen 	int i;
11236f39832cSPeter Wemm 	for (i = 0; i < *nret; i++)
11240c95c705SJonathan Chen 		free(buff[i].data, M_DEVBUF);
11256f39832cSPeter Wemm 	if (*nret > 0)
11260c95c705SJonathan Chen 		free(buff, M_DEVBUF);
11270c95c705SJonathan Chen }
11280c95c705SJonathan Chen 
11290c95c705SJonathan Chen int
1130255b159fSJonathan Chen cardbus_do_cis(device_t cbdev, device_t child)
11310c95c705SJonathan Chen {
113263fa9f4cSJonathan Chen 	int ret;
11330c95c705SJonathan Chen 	struct tuple_callbacks init_callbacks[] = {
11340c95c705SJonathan Chen 		MAKETUPLE(NULL,			generic),
11350c95c705SJonathan Chen 		MAKETUPLE(DEVICE,		generic),
11360c95c705SJonathan Chen 		MAKETUPLE(LONG_LINK_CB,		unhandled),
11370c95c705SJonathan Chen 		MAKETUPLE(INDIRECT,		unhandled),
11380c95c705SJonathan Chen 		MAKETUPLE(CONFIG_CB,		generic),
11390c95c705SJonathan Chen 		MAKETUPLE(CFTABLE_ENTRY_CB,	generic),
11400c95c705SJonathan Chen 		MAKETUPLE(LONGLINK_MFC,		unhandled),
11410c95c705SJonathan Chen 		MAKETUPLE(BAR,			bar),
11420c95c705SJonathan Chen 		MAKETUPLE(PWR_MGMNT,		generic),
11430c95c705SJonathan Chen 		MAKETUPLE(EXTDEVICE,		generic),
11440c95c705SJonathan Chen 		MAKETUPLE(CHECKSUM,		generic),
11450c95c705SJonathan Chen 		MAKETUPLE(LONGLINK_A,		unhandled),
11460c95c705SJonathan Chen 		MAKETUPLE(LONGLINK_C,		unhandled),
11470c95c705SJonathan Chen 		MAKETUPLE(LINKTARGET,		linktarget),
11480c95c705SJonathan Chen 		MAKETUPLE(NO_LINK,		generic),
11490c95c705SJonathan Chen 		MAKETUPLE(VERS_1,		vers_1),
11500c95c705SJonathan Chen 		MAKETUPLE(ALTSTR,		generic),
11510c95c705SJonathan Chen 		MAKETUPLE(DEVICE_A,		generic),
11520c95c705SJonathan Chen 		MAKETUPLE(JEDEC_C,		generic),
11530c95c705SJonathan Chen 		MAKETUPLE(JEDEC_A,		generic),
11540c95c705SJonathan Chen 		MAKETUPLE(CONFIG,		generic),
11550c95c705SJonathan Chen 		MAKETUPLE(CFTABLE_ENTRY,	generic),
11560c95c705SJonathan Chen 		MAKETUPLE(DEVICE_OC,		generic),
11570c95c705SJonathan Chen 		MAKETUPLE(DEVICE_OA,		generic),
11580c95c705SJonathan Chen 		MAKETUPLE(DEVICE_GEO,		generic),
11590c95c705SJonathan Chen 		MAKETUPLE(DEVICE_GEO_A,		generic),
11600c95c705SJonathan Chen 		MAKETUPLE(MANFID,		manfid),
11610c95c705SJonathan Chen 		MAKETUPLE(FUNCID,		funcid),
11620c95c705SJonathan Chen 		MAKETUPLE(FUNCE,		funce),
11630c95c705SJonathan Chen 		MAKETUPLE(SWIL,			generic),
11640c95c705SJonathan Chen 		MAKETUPLE(VERS_2,		generic),
11650c95c705SJonathan Chen 		MAKETUPLE(FORMAT,		generic),
11660c95c705SJonathan Chen 		MAKETUPLE(GEOMETRY,		generic),
11670c95c705SJonathan Chen 		MAKETUPLE(BYTEORDER,		generic),
11680c95c705SJonathan Chen 		MAKETUPLE(DATE,			generic),
11690c95c705SJonathan Chen 		MAKETUPLE(BATTERY,		generic),
11700c95c705SJonathan Chen 		MAKETUPLE(ORG,			generic),
11710c95c705SJonathan Chen 		MAKETUPLE(END,			end),
11720c95c705SJonathan Chen 		MAKETUPLE(GENERIC,		generic),
11730c95c705SJonathan Chen 	};
117463fa9f4cSJonathan Chen 
117563fa9f4cSJonathan Chen 	ret = cardbus_parse_cis(cbdev, child, init_callbacks);
117663fa9f4cSJonathan Chen 	if (ret < 0)
1177a3133b58SWarner Losh 		return (ret);
117863fa9f4cSJonathan Chen 	cardbus_pickup_maps(cbdev, child);
1179a3133b58SWarner Losh 	return (cardbus_alloc_resources(cbdev, child));
11800c95c705SJonathan Chen }
1181