xref: /freebsd/sys/dev/cardbus/cardbus_cis.c (revision bee89c738641238c228e0c415978681678d91b79)
1098ca2bdSWarner Losh /*-
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
92dd5c91eSWarner Losh  *    notice, this list of conditions and the following disclaimer.
100db7e66cSJonathan Chen  * 2. Redistributions in binary form must reproduce the above copyright
112dd5c91eSWarner Losh  *    notice, this list of conditions and the following disclaimer in the
122dd5c91eSWarner Losh  *    documentation and/or other materials provided with the distribution.
130db7e66cSJonathan Chen  *
140db7e66cSJonathan Chen  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
150db7e66cSJonathan Chen  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
160db7e66cSJonathan Chen  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
172dd5c91eSWarner Losh  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
182dd5c91eSWarner Losh  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
190db7e66cSJonathan Chen  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
200db7e66cSJonathan Chen  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
210db7e66cSJonathan Chen  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
220db7e66cSJonathan Chen  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
230db7e66cSJonathan Chen  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
240db7e66cSJonathan Chen  * SUCH DAMAGE.
250db7e66cSJonathan Chen  */
260db7e66cSJonathan Chen 
27aad970f1SDavid E. O'Brien #include <sys/cdefs.h>
28aad970f1SDavid E. O'Brien __FBSDID("$FreeBSD$");
29aad970f1SDavid E. O'Brien 
300db7e66cSJonathan Chen /*
310db7e66cSJonathan Chen  * CIS Handling for the Cardbus Bus
320db7e66cSJonathan Chen  */
330db7e66cSJonathan Chen 
340db7e66cSJonathan Chen #include <sys/param.h>
350db7e66cSJonathan Chen #include <sys/systm.h>
360db7e66cSJonathan Chen #include <sys/kernel.h>
370c95c705SJonathan Chen #include <sys/malloc.h>
380db7e66cSJonathan Chen 
390db7e66cSJonathan Chen #include <sys/bus.h>
400db7e66cSJonathan Chen #include <machine/bus.h>
410db7e66cSJonathan Chen #include <machine/resource.h>
420db7e66cSJonathan Chen #include <sys/rman.h>
431e962d00SScott Long #include <sys/endian.h>
440db7e66cSJonathan Chen 
457ba175acSWarner Losh #include <sys/pciio.h>
4663fa9f4cSJonathan Chen #include <dev/pci/pcivar.h>
4763fa9f4cSJonathan Chen #include <dev/pci/pcireg.h>
480db7e66cSJonathan Chen 
49a294cdb6SWarner Losh #include <dev/pccard/pccardvar.h>
50a294cdb6SWarner Losh #include <dev/pccard/pccard_cis.h>
51a294cdb6SWarner Losh 
520db7e66cSJonathan Chen #include <dev/cardbus/cardbusreg.h>
5363fa9f4cSJonathan Chen #include <dev/cardbus/cardbusvar.h>
540db7e66cSJonathan Chen #include <dev/cardbus/cardbus_cis.h>
550db7e66cSJonathan Chen 
56b3889b68SWarner Losh extern int cardbus_cis_debug;
57b3889b68SWarner Losh 
58b3889b68SWarner Losh #define	DPRINTF(a) if (cardbus_cis_debug) printf a
59b3889b68SWarner Losh #define	DEVPRINTF(x) if (cardbus_cis_debug) device_printf x
60b3889b68SWarner Losh 
61c732cf3bSWarner Losh #define CIS_CONFIG_SPACE	(struct resource *)~0UL
62c732cf3bSWarner Losh 
6322acd92bSWarner Losh static int decode_tuple_generic(device_t cbdev, device_t child, int id,
6422acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
6547147ce7SWarner Losh     struct tuple_callbacks *info, void *);
6622acd92bSWarner Losh static int decode_tuple_linktarget(device_t cbdev, device_t child, int id,
6722acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
6847147ce7SWarner Losh     struct tuple_callbacks *info, void *);
6922acd92bSWarner Losh static int decode_tuple_vers_1(device_t cbdev, device_t child, int id,
7022acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
7147147ce7SWarner Losh     struct tuple_callbacks *info, void *);
7222acd92bSWarner Losh static int decode_tuple_funcid(device_t cbdev, device_t child, int id,
7322acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
7447147ce7SWarner Losh     struct tuple_callbacks *info, void *);
7522acd92bSWarner Losh static int decode_tuple_manfid(device_t cbdev, device_t child, int id,
7622acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
7747147ce7SWarner Losh     struct tuple_callbacks *info, void *);
7822acd92bSWarner Losh static int decode_tuple_funce(device_t cbdev, device_t child, int id,
7922acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
8047147ce7SWarner Losh     struct tuple_callbacks *info, void *);
8122acd92bSWarner Losh static int decode_tuple_bar(device_t cbdev, device_t child, int id,
8222acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
8347147ce7SWarner Losh     struct tuple_callbacks *info, void *);
8422acd92bSWarner Losh static int decode_tuple_unhandled(device_t cbdev, device_t child, int id,
8522acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
8647147ce7SWarner Losh     struct tuple_callbacks *info, void *);
8722acd92bSWarner Losh static int decode_tuple_end(device_t cbdev, device_t child, int id,
8822acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
8947147ce7SWarner Losh     struct tuple_callbacks *info, void *);
9022acd92bSWarner Losh 
91255b159fSJonathan Chen static int	cardbus_read_tuple_conf(device_t cbdev, device_t child,
9266e390feSWarner Losh 		    uint32_t start, uint32_t *off, int *tupleid, int *len,
9366e390feSWarner Losh 		    uint8_t *tupledata);
9463fa9f4cSJonathan Chen static int	cardbus_read_tuple_mem(device_t cbdev, struct resource *res,
9566e390feSWarner Losh 		    uint32_t start, uint32_t *off, int *tupleid, int *len,
9666e390feSWarner Losh 		    uint8_t *tupledata);
97255b159fSJonathan Chen static int	cardbus_read_tuple(device_t cbdev, device_t child,
9866e390feSWarner Losh 		    struct resource *res, uint32_t start, uint32_t *off,
9966e390feSWarner Losh 		    int *tupleid, int *len, uint8_t *tupledata);
10063fa9f4cSJonathan Chen static void	cardbus_read_tuple_finish(device_t cbdev, device_t child,
10163fa9f4cSJonathan Chen 		    int rid, struct resource *res);
10263fa9f4cSJonathan Chen static struct resource	*cardbus_read_tuple_init(device_t cbdev, device_t child,
10366e390feSWarner Losh 		    uint32_t *start, int *rid);
104255b159fSJonathan Chen static int	decode_tuple(device_t cbdev, device_t child, int tupleid,
10566e390feSWarner Losh 		    int len, uint8_t *tupledata, uint32_t start,
10647147ce7SWarner Losh 		    uint32_t *off, struct tuple_callbacks *callbacks,
10747147ce7SWarner Losh 		    void *);
108255b159fSJonathan Chen 
1090c95c705SJonathan Chen #define	MAKETUPLE(NAME,FUNC) { CISTPL_ ## NAME, #NAME, decode_tuple_ ## FUNC }
1100db7e66cSJonathan Chen 
1110db7e66cSJonathan Chen static char *funcnames[] = {
1120db7e66cSJonathan Chen 	"Multi-Functioned",
1130db7e66cSJonathan Chen 	"Memory",
1140db7e66cSJonathan Chen 	"Serial Port",
1150db7e66cSJonathan Chen 	"Parallel Port",
1160db7e66cSJonathan Chen 	"Fixed Disk",
1170db7e66cSJonathan Chen 	"Video Adaptor",
1180db7e66cSJonathan Chen 	"Network Adaptor",
1190db7e66cSJonathan Chen 	"AIMS",
1200db7e66cSJonathan Chen 	"SCSI",
1210db7e66cSJonathan Chen 	"Security"
1220db7e66cSJonathan Chen };
1230db7e66cSJonathan Chen 
124255b159fSJonathan Chen /*
125255b159fSJonathan Chen  * Handler functions for various CIS tuples
126255b159fSJonathan Chen  */
127255b159fSJonathan Chen 
12822acd92bSWarner Losh static int
12922acd92bSWarner Losh decode_tuple_generic(device_t cbdev, device_t child, int id,
13022acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
13147147ce7SWarner Losh     struct tuple_callbacks *info, void *argp)
1320db7e66cSJonathan Chen {
1330db7e66cSJonathan Chen 	int i;
1340db7e66cSJonathan Chen 
13522acd92bSWarner Losh 	if (cardbus_cis_debug) {
1360db7e66cSJonathan Chen 		if (info)
1370db7e66cSJonathan Chen 			printf("TUPLE: %s [%d]:", info->name, len);
1380db7e66cSJonathan Chen 		else
1390db7e66cSJonathan Chen 			printf("TUPLE: Unknown(0x%02x) [%d]:", id, len);
1400db7e66cSJonathan Chen 
1410db7e66cSJonathan Chen 		for (i = 0; i < len; i++) {
1420db7e66cSJonathan Chen 			if (i % 0x10 == 0 && len > 0x10)
1430db7e66cSJonathan Chen 				printf("\n       0x%02x:", i);
1447bec1dd5SJonathan Chen 			printf(" %02x", tupledata[i]);
1450db7e66cSJonathan Chen 		}
1460db7e66cSJonathan Chen 		printf("\n");
14722acd92bSWarner Losh 	}
148a3133b58SWarner Losh 	return (0);
1490db7e66cSJonathan Chen }
1500db7e66cSJonathan Chen 
15122acd92bSWarner Losh static int
15222acd92bSWarner Losh decode_tuple_linktarget(device_t cbdev, device_t child, int id,
15322acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
15447147ce7SWarner Losh     struct tuple_callbacks *info, void *argp)
1550db7e66cSJonathan Chen {
15649f158ccSJonathan Chen 	int i;
15749f158ccSJonathan Chen 
15822acd92bSWarner Losh 	if (cardbus_cis_debug) {
15949f158ccSJonathan Chen 		printf("TUPLE: %s [%d]:", info->name, len);
16049f158ccSJonathan Chen 
16149f158ccSJonathan Chen 		for (i = 0; i < len; i++) {
16249f158ccSJonathan Chen 			if (i % 0x10 == 0 && len > 0x10)
16349f158ccSJonathan Chen 				printf("\n       0x%02x:", i);
16449f158ccSJonathan Chen 			printf(" %02x", tupledata[i]);
16549f158ccSJonathan Chen 		}
16649f158ccSJonathan Chen 		printf("\n");
16722acd92bSWarner Losh 	}
1687bec1dd5SJonathan Chen 	if (len != 3 || tupledata[0] != 'C' || tupledata[1] != 'I' ||
1697bec1dd5SJonathan Chen 	    tupledata[2] != 'S') {
1700db7e66cSJonathan Chen 		printf("Invalid data for CIS Link Target!\n");
171255b159fSJonathan Chen 		decode_tuple_generic(cbdev, child, id, len, tupledata,
17247147ce7SWarner Losh 		    start, off, info, argp);
173a3133b58SWarner Losh 		return (EINVAL);
1740db7e66cSJonathan Chen 	}
175a3133b58SWarner Losh 	return (0);
1760db7e66cSJonathan Chen }
1770db7e66cSJonathan Chen 
17822acd92bSWarner Losh static int
17922acd92bSWarner Losh decode_tuple_vers_1(device_t cbdev, device_t child, int id,
18022acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
18147147ce7SWarner Losh     struct tuple_callbacks *info, void *argp)
1820db7e66cSJonathan Chen {
1830db7e66cSJonathan Chen 	int i;
184fbe9cff1SWarner Losh 
18522acd92bSWarner Losh 	if (cardbus_cis_debug) {
1867bec1dd5SJonathan Chen 		printf("Product version: %d.%d\n", tupledata[0], tupledata[1]);
1870db7e66cSJonathan Chen 		printf("Product name: ");
1880db7e66cSJonathan Chen 		for (i = 2; i < len; i++) {
1897bec1dd5SJonathan Chen 			if (tupledata[i] == '\0')
1900db7e66cSJonathan Chen 				printf(" | ");
1917bec1dd5SJonathan Chen 			else if (tupledata[i] == 0xff)
1920db7e66cSJonathan Chen 				break;
1930db7e66cSJonathan Chen 			else
1947bec1dd5SJonathan Chen 				printf("%c", tupledata[i]);
1950db7e66cSJonathan Chen 		}
1960db7e66cSJonathan Chen 		printf("\n");
19722acd92bSWarner Losh 	}
198a3133b58SWarner Losh 	return (0);
1990db7e66cSJonathan Chen }
2000db7e66cSJonathan Chen 
20122acd92bSWarner Losh static int
20222acd92bSWarner Losh decode_tuple_funcid(device_t cbdev, device_t child, int id,
20322acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
20447147ce7SWarner Losh     struct tuple_callbacks *info, void *argp)
2050db7e66cSJonathan Chen {
206fbe9cff1SWarner Losh 	struct cardbus_devinfo *dinfo = device_get_ivars(child);
2070db7e66cSJonathan Chen 	int numnames = sizeof(funcnames) / sizeof(funcnames[0]);
208fbe9cff1SWarner Losh 	int i;
2090db7e66cSJonathan Chen 
21022acd92bSWarner Losh 	if (cardbus_cis_debug) {
2110db7e66cSJonathan Chen 		printf("Functions: ");
2120db7e66cSJonathan Chen 		for (i = 0; i < len; i++) {
2137bec1dd5SJonathan Chen 			if (tupledata[i] < numnames)
2147bec1dd5SJonathan Chen 				printf("%s", funcnames[tupledata[i]]);
2150db7e66cSJonathan Chen 			else
2167bec1dd5SJonathan Chen 				printf("Unknown(%d)", tupledata[i]);
217255b159fSJonathan Chen 			if (i < len - 1)
218255b159fSJonathan Chen 				printf(", ");
2190db7e66cSJonathan Chen 		}
22022acd92bSWarner Losh 		printf("\n");
22122acd92bSWarner Losh 	}
222fbe9cff1SWarner Losh 	if (len > 0)
223fbe9cff1SWarner Losh 		dinfo->funcid = tupledata[0];		/* use first in list */
224a3133b58SWarner Losh 	return (0);
2250db7e66cSJonathan Chen }
2260db7e66cSJonathan Chen 
22722acd92bSWarner Losh static int
22822acd92bSWarner Losh decode_tuple_manfid(device_t cbdev, device_t child, int id,
22922acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
23047147ce7SWarner Losh     struct tuple_callbacks *info, void *argp)
2310db7e66cSJonathan Chen {
232fbe9cff1SWarner Losh 	struct cardbus_devinfo *dinfo = device_get_ivars(child);
2330db7e66cSJonathan Chen 	int i;
234fbe9cff1SWarner Losh 
23522acd92bSWarner Losh 	if (cardbus_cis_debug) {
2360db7e66cSJonathan Chen 		printf("Manufacturer ID: ");
2370db7e66cSJonathan Chen 		for (i = 0; i < len; i++)
2387bec1dd5SJonathan Chen 			printf("%02x", tupledata[i]);
2390db7e66cSJonathan Chen 		printf("\n");
24022acd92bSWarner Losh 	}
241fbe9cff1SWarner Losh 
242fbe9cff1SWarner Losh 	if (len == 5) {
243fbe9cff1SWarner Losh 		dinfo->mfrid = tupledata[1] | (tupledata[2] << 8);
244fbe9cff1SWarner Losh 		dinfo->prodid = tupledata[3] | (tupledata[4] << 8);
245fbe9cff1SWarner Losh 	}
246a3133b58SWarner Losh 	return (0);
2470db7e66cSJonathan Chen }
2480db7e66cSJonathan Chen 
24922acd92bSWarner Losh static int
25022acd92bSWarner Losh decode_tuple_funce(device_t cbdev, device_t child, int id,
25122acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
25247147ce7SWarner Losh     struct tuple_callbacks *info, void *argp)
2530db7e66cSJonathan Chen {
254fbe9cff1SWarner Losh 	struct cardbus_devinfo *dinfo = device_get_ivars(child);
255fbe9cff1SWarner Losh 	int type, i;
256fbe9cff1SWarner Losh 
25722acd92bSWarner Losh 	if (cardbus_cis_debug) {
2580db7e66cSJonathan Chen 		printf("Function Extension: ");
2590db7e66cSJonathan Chen 		for (i = 0; i < len; i++)
2607bec1dd5SJonathan Chen 			printf("%02x", tupledata[i]);
2610db7e66cSJonathan Chen 		printf("\n");
26222acd92bSWarner Losh 	}
263fbe9cff1SWarner Losh 	if (len < 2)			/* too short */
264fbe9cff1SWarner Losh 		return (0);
265fbe9cff1SWarner Losh 	type = tupledata[0];		/* XXX <32 always? */
266fbe9cff1SWarner Losh 	switch (dinfo->funcid) {
267440b5adeSWarner Losh 	case PCCARD_FUNCTION_NETWORK:
268fbe9cff1SWarner Losh 		switch (type) {
269440b5adeSWarner Losh 		case PCCARD_TPLFE_TYPE_LAN_NID:
27022acd92bSWarner Losh 			if (tupledata[1] > sizeof(dinfo->funce.lan.nid)) {
27122acd92bSWarner Losh 				/* ignore, warning? */
27222acd92bSWarner Losh 				return (0);
27322acd92bSWarner Losh 			}
27422acd92bSWarner Losh 			bcopy(tupledata + 2, dinfo->funce.lan.nid,
27522acd92bSWarner Losh 			    tupledata[1]);
276fbe9cff1SWarner Losh 			break;
277fbe9cff1SWarner Losh 		}
278fbe9cff1SWarner Losh 		dinfo->fepresent |= 1<<type;
279fbe9cff1SWarner Losh 		break;
280fbe9cff1SWarner Losh 	}
281a3133b58SWarner Losh 	return (0);
2820db7e66cSJonathan Chen }
2830db7e66cSJonathan Chen 
28422acd92bSWarner Losh static int
28522acd92bSWarner Losh decode_tuple_bar(device_t cbdev, device_t child, int id,
28622acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
28747147ce7SWarner Losh     struct tuple_callbacks *info, void *argp)
2880db7e66cSJonathan Chen {
28963fa9f4cSJonathan Chen 	struct cardbus_devinfo *dinfo = device_get_ivars(child);
2900db7e66cSJonathan Chen 	int type;
2911e962d00SScott Long 	uint8_t reg;
292bee89c73SWarner Losh 	uint32_t bar;
2930db7e66cSJonathan Chen 
294e6e272b9SScott Long 	if (len != 6) {
2951e962d00SScott Long 		device_printf(cbdev, "CIS BAR length not 6 (%d)\n", len);
296e6e272b9SScott Long 		return (EINVAL);
297e6e272b9SScott Long 	}
2981e962d00SScott Long 
2991e962d00SScott Long 	reg = *tupledata;
3001e962d00SScott Long 	len = le32toh(*(uint32_t*)(tupledata + 2));
301c732cf3bSWarner Losh 	if (reg & TPL_BAR_REG_AS)
3020db7e66cSJonathan Chen 		type = SYS_RES_IOPORT;
303c732cf3bSWarner Losh 	else
3040db7e66cSJonathan Chen 		type = SYS_RES_MEMORY;
3051e962d00SScott Long 
3061e962d00SScott Long 	bar = reg & TPL_BAR_REG_ASI_MASK;
3071e962d00SScott Long 	if (bar == 0) {
3081e962d00SScott Long 		device_printf(cbdev, "Invalid BAR type 0 in CIS\n");
3091e962d00SScott Long 		return (EINVAL);	/* XXX Return an error? */
3101e962d00SScott Long 	} else if (bar == 7) {
3111e962d00SScott Long 		/* XXX Should we try to map in Option ROMs? */
312a3133b58SWarner Losh 		return (0);
3130db7e66cSJonathan Chen 	}
3141e962d00SScott Long 
31572d3502eSScott Long 	/* Convert from BAR type to BAR offset */
316495036f2SWarner Losh 	bar = PCIR_BAR(bar - 1);
3171e962d00SScott Long 
31863fa9f4cSJonathan Chen 	if (type == SYS_RES_MEMORY) {
31972d3502eSScott Long 		if (reg & TPL_BAR_REG_PREFETCHABLE)
320f3d3468dSWarner Losh 			dinfo->mprefetchable |= (1 << PCI_RID2BAR(bar));
321f9aedaa4SWarner Losh #if 0
322a7c43559SWarner Losh 		/*
323a7c43559SWarner Losh 		 * XXX: It appears from a careful reading of the spec
324a7c43559SWarner Losh 		 * that we're not supposed to honor this when the bridge
325a7c43559SWarner Losh 		 * is not on the main system bus.  PCI spec doesn't appear
326a7c43559SWarner Losh 		 * to allow for memory ranges not listed in the bridge's
327a7c43559SWarner Losh 		 * decode range to be decoded.  The PC Card spec seems to
328a7c43559SWarner Losh 		 * indicate that this should only be done on x86 based
329a7c43559SWarner Losh 		 * machines, which seems to imply that on non-x86 machines
330a7c43559SWarner Losh 		 * the adddresses can be anywhere.  This further implies that
331a7c43559SWarner Losh 		 * since the hardware can do it on non-x86 machines, it should
332a7c43559SWarner Losh 		 * be able to do it on x86 machines.  Therefore, we can and
333a7c43559SWarner Losh 		 * should ignore this hint.  Furthermore, the PC Card spec
334a7c43559SWarner Losh 		 * recommends always allocating memory above 1MB, contradicting
335a7c43559SWarner Losh 		 * the other part of the PC Card spec.
336a7c43559SWarner Losh 		 *
337a7c43559SWarner Losh 		 * NetBSD ignores this bit, but it also ignores the
338a7c43559SWarner Losh 		 * prefetchable bit too, so that's not an indication of
339a7c43559SWarner Losh 		 * correctness.
340a7c43559SWarner Losh 		 */
34172d3502eSScott Long 		if (reg & TPL_BAR_REG_BELOW1MB)
342f3d3468dSWarner Losh 			dinfo->mbelow1mb |= (1 << PCI_RID2BAR(bar));
343f9aedaa4SWarner Losh #endif
3440db7e66cSJonathan Chen 	}
3451e962d00SScott Long 
346a3133b58SWarner Losh 	return (0);
3470db7e66cSJonathan Chen }
3480db7e66cSJonathan Chen 
34922acd92bSWarner Losh static int
35022acd92bSWarner Losh decode_tuple_unhandled(device_t cbdev, device_t child, int id,
35122acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
35247147ce7SWarner Losh     struct tuple_callbacks *info, void *argp)
3530db7e66cSJonathan Chen {
35422acd92bSWarner Losh 	/* Make this message suck less XXX */
3550db7e66cSJonathan Chen 	printf("TUPLE: %s [%d] is unhandled! Bailing...", info->name, len);
35647147ce7SWarner Losh 	return (EINVAL);
3570db7e66cSJonathan Chen }
3580db7e66cSJonathan Chen 
35922acd92bSWarner Losh static int
36022acd92bSWarner Losh decode_tuple_end(device_t cbdev, device_t child, int id,
36122acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
36247147ce7SWarner Losh     struct tuple_callbacks *info, void *argp)
3630db7e66cSJonathan Chen {
364509cfe6fSWarner Losh 	if (cardbus_cis_debug)
3650c95c705SJonathan Chen 		printf("CIS reading done\n");
366a3133b58SWarner Losh 	return (0);
3670db7e66cSJonathan Chen }
3680db7e66cSJonathan Chen 
369255b159fSJonathan Chen /*
370255b159fSJonathan Chen  * Functions to read the a tuple from the card
371255b159fSJonathan Chen  */
372255b159fSJonathan Chen 
3730db7e66cSJonathan Chen static int
37466e390feSWarner Losh cardbus_read_tuple_conf(device_t cbdev, device_t child, uint32_t start,
37566e390feSWarner Losh     uint32_t *off, int *tupleid, int *len, uint8_t *tupledata)
3767bec1dd5SJonathan Chen {
3777bec1dd5SJonathan Chen 	int i, j;
37866e390feSWarner Losh 	uint32_t e;
37966e390feSWarner Losh 	uint32_t loc;
3807bec1dd5SJonathan Chen 
38163fa9f4cSJonathan Chen 	loc = start + *off;
38249f158ccSJonathan Chen 
38349f158ccSJonathan Chen 	e = pci_read_config(child, loc - loc % 4, 4);
38449f158ccSJonathan Chen 	for (j = loc % 4; j > 0; j--)
3857bec1dd5SJonathan Chen 		e >>= 8;
3867bec1dd5SJonathan Chen 	*len = 0;
38749f158ccSJonathan Chen 	for (i = loc, j = -2; j < *len; j++, i++) {
3887bec1dd5SJonathan Chen 		if (i % 4 == 0)
3897bec1dd5SJonathan Chen 			e = pci_read_config(child, i, 4);
3907bec1dd5SJonathan Chen 		if (j == -2)
3917bec1dd5SJonathan Chen 			*tupleid = 0xff & e;
3927bec1dd5SJonathan Chen 		else if (j == -1)
3937bec1dd5SJonathan Chen 			*len = 0xff & e;
3947bec1dd5SJonathan Chen 		else
3957bec1dd5SJonathan Chen 			tupledata[j] = 0xff & e;
3967bec1dd5SJonathan Chen 		e >>= 8;
3977bec1dd5SJonathan Chen 	}
3987bec1dd5SJonathan Chen 	*off += *len + 2;
399a3133b58SWarner Losh 	return (0);
4007bec1dd5SJonathan Chen }
4017bec1dd5SJonathan Chen 
4027bec1dd5SJonathan Chen static int
40366e390feSWarner Losh cardbus_read_tuple_mem(device_t cbdev, struct resource *res, uint32_t start,
40466e390feSWarner Losh     uint32_t *off, int *tupleid, int *len, uint8_t *tupledata)
4050db7e66cSJonathan Chen {
40663fa9f4cSJonathan Chen 	bus_space_tag_t bt;
40763fa9f4cSJonathan Chen 	bus_space_handle_t bh;
40863fa9f4cSJonathan Chen 	int ret;
4090db7e66cSJonathan Chen 
41063fa9f4cSJonathan Chen 	bt = rman_get_bustag(res);
41163fa9f4cSJonathan Chen 	bh = rman_get_bushandle(res);
4120db7e66cSJonathan Chen 
41363fa9f4cSJonathan Chen 	*tupleid = bus_space_read_1(bt, bh, start + *off);
41463fa9f4cSJonathan Chen 	*len = bus_space_read_1(bt, bh, start + *off + 1);
41563fa9f4cSJonathan Chen 	bus_space_read_region_1(bt, bh, *off + start + 2, tupledata, *len);
41663fa9f4cSJonathan Chen 	ret = 0;
41763fa9f4cSJonathan Chen 	*off += *len + 2;
418a3133b58SWarner Losh 	return (ret);
4190db7e66cSJonathan Chen }
42063fa9f4cSJonathan Chen 
42163fa9f4cSJonathan Chen static int
42263fa9f4cSJonathan Chen cardbus_read_tuple(device_t cbdev, device_t child, struct resource *res,
42366e390feSWarner Losh     uint32_t start, uint32_t *off, int *tupleid, int *len,
42466e390feSWarner Losh     uint8_t *tupledata)
42563fa9f4cSJonathan Chen {
426bee89c73SWarner Losh 	if (res == CIS_CONFIG_SPACE)
427a3133b58SWarner Losh 		return (cardbus_read_tuple_conf(cbdev, child, start, off,
428a3133b58SWarner Losh 		    tupleid, len, tupledata));
429bee89c73SWarner Losh 	return (cardbus_read_tuple_mem(cbdev, res, start, off, tupleid, len,
430bee89c73SWarner Losh 	    tupledata));
43163fa9f4cSJonathan Chen }
43263fa9f4cSJonathan Chen 
43363fa9f4cSJonathan Chen static void
43463fa9f4cSJonathan Chen cardbus_read_tuple_finish(device_t cbdev, device_t child, int rid,
43563fa9f4cSJonathan Chen     struct resource *res)
43663fa9f4cSJonathan Chen {
437c732cf3bSWarner Losh 	if (res != CIS_CONFIG_SPACE) {
438bee89c73SWarner Losh 		bus_release_resource(child, SYS_RES_MEMORY, rid, res);
43947147ce7SWarner Losh 		if (rid == PCIM_CIS_ASI_ROM)
44047147ce7SWarner Losh 			pci_write_config(child, rid, pci_read_config(child,
44147147ce7SWarner Losh 			    rid, 4) & ~PCIR_BIOS, 4);
44263fa9f4cSJonathan Chen 	}
44363fa9f4cSJonathan Chen }
44463fa9f4cSJonathan Chen 
44563fa9f4cSJonathan Chen static struct resource *
44666e390feSWarner Losh cardbus_read_tuple_init(device_t cbdev, device_t child, uint32_t *start,
44763fa9f4cSJonathan Chen     int *rid)
44863fa9f4cSJonathan Chen {
44963fa9f4cSJonathan Chen 	struct resource *res;
450495036f2SWarner Losh 	uint32_t space;
45163fa9f4cSJonathan Chen 
45241ac33a2SWarner Losh 	space = *start & PCIM_CIS_ASI_MASK;
453495036f2SWarner Losh 	switch (space) {
45441ac33a2SWarner Losh 	case PCIM_CIS_ASI_TUPLE:
45547147ce7SWarner Losh 		if (cardbus_cis_debug)
45647147ce7SWarner Losh 			device_printf(cbdev, "CIS in PCI config space\n");
457e6e272b9SScott Long 		/* CIS in PCI config space need no initialization */
458c732cf3bSWarner Losh 		return (CIS_CONFIG_SPACE);
45941ac33a2SWarner Losh 	case PCIM_CIS_ASI_BAR0:
46041ac33a2SWarner Losh 	case PCIM_CIS_ASI_BAR1:
46141ac33a2SWarner Losh 	case PCIM_CIS_ASI_BAR2:
46241ac33a2SWarner Losh 	case PCIM_CIS_ASI_BAR3:
46341ac33a2SWarner Losh 	case PCIM_CIS_ASI_BAR4:
46441ac33a2SWarner Losh 	case PCIM_CIS_ASI_BAR5:
46541ac33a2SWarner Losh 		*rid = PCIR_BAR(space - PCIM_CIS_ASI_BAR0);
46647147ce7SWarner Losh 		if (cardbus_cis_debug)
46747147ce7SWarner Losh 			device_printf(cbdev, "CIS in BAR %#x\n", *rid);
46863fa9f4cSJonathan Chen 		break;
46941ac33a2SWarner Losh 	case PCIM_CIS_ASI_ROM:
47041ac33a2SWarner Losh 		*rid = PCIR_BIOS;
47147147ce7SWarner Losh 		if (cardbus_cis_debug)
47247147ce7SWarner Losh 			device_printf(cbdev, "CIS in option rom\n");
47363fa9f4cSJonathan Chen 		break;
47463fa9f4cSJonathan Chen 	default:
47563fa9f4cSJonathan Chen 		device_printf(cbdev, "Unable to read CIS: Unknown space: %d\n",
476495036f2SWarner Losh 		    space);
477a3133b58SWarner Losh 		return (NULL);
47863fa9f4cSJonathan Chen 	}
47963fa9f4cSJonathan Chen 
48063fa9f4cSJonathan Chen 	/* allocate the memory space to read CIS */
481bee89c73SWarner Losh 	res = bus_alloc_resource(child, SYS_RES_MEMORY, rid, 0, ~0, 1,
48247147ce7SWarner Losh 	    rman_make_alignment_flags(4096) | RF_ACTIVE);
48363fa9f4cSJonathan Chen 	if (res == NULL) {
48463fa9f4cSJonathan Chen 		device_printf(cbdev, "Unable to allocate resource "
48563fa9f4cSJonathan Chen 		    "to read CIS.\n");
486a3133b58SWarner Losh 		return (NULL);
48763fa9f4cSJonathan Chen 	}
48847147ce7SWarner Losh 	if (*rid == PCIR_BIOS)
48947147ce7SWarner Losh 		pci_write_config(child, *rid,
49047147ce7SWarner Losh 		    rman_get_start(res) | PCIM_BIOS_ENABLE, 4);
49163fa9f4cSJonathan Chen 
49263fa9f4cSJonathan Chen 	/* Flip to the right ROM image if CIS is in ROM */
49341ac33a2SWarner Losh 	if (space == PCIM_CIS_ASI_ROM) {
49463fa9f4cSJonathan Chen 		bus_space_tag_t bt;
49563fa9f4cSJonathan Chen 		bus_space_handle_t bh;
49666e390feSWarner Losh 		uint32_t imagesize;
49766e390feSWarner Losh 		uint32_t imagebase = 0;
49866e390feSWarner Losh 		uint32_t pcidata;
49966e390feSWarner Losh 		uint16_t romsig;
50063fa9f4cSJonathan Chen 		int romnum = 0;
501e6e272b9SScott Long 		int imagenum;
50263fa9f4cSJonathan Chen 
50363fa9f4cSJonathan Chen 		bt = rman_get_bustag(res);
50463fa9f4cSJonathan Chen 		bh = rman_get_bushandle(res);
50563fa9f4cSJonathan Chen 
50641ac33a2SWarner Losh 		imagenum = (*start & PCIM_CIS_ROM_MASK) >> 28;
50763fa9f4cSJonathan Chen 		for (romnum = 0;; romnum++) {
508e6e272b9SScott Long 			romsig = bus_space_read_2(bt, bh,
509e6e272b9SScott Long 			    imagebase + CARDBUS_EXROM_SIGNATURE);
510e6e272b9SScott Long 			if (romsig != 0xaa55) {
51163fa9f4cSJonathan Chen 				device_printf(cbdev, "Bad header in rom %d: "
512e6e272b9SScott Long 				    "[%x] %04x\n", romnum, imagebase +
513e6e272b9SScott Long 				    CARDBUS_EXROM_SIGNATURE, romsig);
51463fa9f4cSJonathan Chen 				bus_release_resource(cbdev, SYS_RES_MEMORY,
51563fa9f4cSJonathan Chen 				    *rid, res);
51663fa9f4cSJonathan Chen 				*rid = 0;
517a3133b58SWarner Losh 				return (NULL);
51863fa9f4cSJonathan Chen 			}
519e6e272b9SScott Long 
520e6e272b9SScott Long 			/*
521e6e272b9SScott Long 			 * If this was the Option ROM image that we were
522e6e272b9SScott Long 			 * looking for, then we are done.
523e6e272b9SScott Long 			 */
524e6e272b9SScott Long 			if (romnum == imagenum)
525e6e272b9SScott Long 				break;
526e6e272b9SScott Long 
527e6e272b9SScott Long 			/* Find out where the next Option ROM image is */
528e6e272b9SScott Long 			pcidata = imagebase + bus_space_read_2(bt, bh,
529e6e272b9SScott Long 			    imagebase + CARDBUS_EXROM_DATA_PTR);
53063fa9f4cSJonathan Chen 			imagesize = bus_space_read_2(bt, bh,
531e6e272b9SScott Long 			    pcidata + CARDBUS_EXROM_DATA_IMAGE_LENGTH);
5320db7e66cSJonathan Chen 
5337bec1dd5SJonathan Chen 			if (imagesize == 0) {
5340db7e66cSJonathan Chen 				/*
5350db7e66cSJonathan Chen 				 * XXX some ROMs seem to have this as zero,
5360db7e66cSJonathan Chen 				 * can we assume this means 1 block?
5370db7e66cSJonathan Chen 				 */
538e6e272b9SScott Long 				device_printf(cbdev, "Warning, size of Option "
539e6e272b9SScott Long 				    "ROM image %d is 0 bytes, assuming 512 "
540e6e272b9SScott Long 				    "bytes.\n", romnum);
5410db7e66cSJonathan Chen 				imagesize = 1;
5427bec1dd5SJonathan Chen 			}
543e6e272b9SScott Long 
544e6e272b9SScott Long 			/* Image size is in 512 byte units */
5450db7e66cSJonathan Chen 			imagesize <<= 9;
5460db7e66cSJonathan Chen 
547e6e272b9SScott Long 			if ((bus_space_read_1(bt, bh, pcidata +
5481e06ae99SScott Long 			    CARDBUS_EXROM_DATA_INDICATOR) & 0x80) != 0) {
549e6e272b9SScott Long 				device_printf(cbdev, "Cannot find CIS in "
550e6e272b9SScott Long 				    "Option ROM\n");
551e6e272b9SScott Long 				bus_release_resource(cbdev, SYS_RES_MEMORY,
552e6e272b9SScott Long 				    *rid, res);
553e6e272b9SScott Long 				*rid = 0;
554a3133b58SWarner Losh 				return (NULL);
5550db7e66cSJonathan Chen 			}
556e6e272b9SScott Long 			imagebase += imagesize;
5570db7e66cSJonathan Chen 		}
55841ac33a2SWarner Losh 		*start = imagebase + (*start & PCIM_CIS_ADDR_MASK);
5590db7e66cSJonathan Chen 	} else {
56041ac33a2SWarner Losh 		*start = *start & PCIM_CIS_ADDR_MASK;
5610db7e66cSJonathan Chen 	}
562e6e272b9SScott Long 
563a3133b58SWarner Losh 	return (res);
5647bec1dd5SJonathan Chen }
5657bec1dd5SJonathan Chen 
566255b159fSJonathan Chen /*
567255b159fSJonathan Chen  * Dispatch the right handler function per tuple
568255b159fSJonathan Chen  */
569255b159fSJonathan Chen 
5707bec1dd5SJonathan Chen static int
571255b159fSJonathan Chen decode_tuple(device_t cbdev, device_t child, int tupleid, int len,
57266e390feSWarner Losh     uint8_t *tupledata, uint32_t start, uint32_t *off,
57347147ce7SWarner Losh     struct tuple_callbacks *callbacks, void *argp)
5747bec1dd5SJonathan Chen {
5757bec1dd5SJonathan Chen 	int i;
5760c95c705SJonathan Chen 	for (i = 0; callbacks[i].id != CISTPL_GENERIC; i++) {
5770c95c705SJonathan Chen 		if (tupleid == callbacks[i].id)
578a3133b58SWarner Losh 			return (callbacks[i].func(cbdev, child, tupleid, len,
57947147ce7SWarner Losh 			    tupledata, start, off, &callbacks[i], argp));
5807bec1dd5SJonathan Chen 	}
581a3133b58SWarner Losh 	return (callbacks[i].func(cbdev, child, tupleid, len,
58247147ce7SWarner Losh 	    tupledata, start, off, NULL, argp));
5830db7e66cSJonathan Chen }
5840db7e66cSJonathan Chen 
58547147ce7SWarner Losh int
586255b159fSJonathan Chen cardbus_parse_cis(device_t cbdev, device_t child,
58747147ce7SWarner Losh     struct tuple_callbacks *callbacks, void *argp)
5880db7e66cSJonathan Chen {
58966e390feSWarner Losh 	uint8_t tupledata[MAXTUPLESIZE];
5907bec1dd5SJonathan Chen 	int tupleid;
5917bec1dd5SJonathan Chen 	int len;
5927bec1dd5SJonathan Chen 	int expect_linktarget;
59366e390feSWarner Losh 	uint32_t start, off;
59463fa9f4cSJonathan Chen 	struct resource *res;
59563fa9f4cSJonathan Chen 	int rid;
5960db7e66cSJonathan Chen 
5970db7e66cSJonathan Chen 	bzero(tupledata, MAXTUPLESIZE);
5987bec1dd5SJonathan Chen 	expect_linktarget = TRUE;
59941ac33a2SWarner Losh 	if ((start = pci_read_config(child, PCIR_CIS, 4)) == 0) {
600509cfe6fSWarner Losh 		device_printf(cbdev, "CIS pointer is 0!\n");
601e6e272b9SScott Long 		return (ENXIO);
602509cfe6fSWarner Losh 	}
60347147ce7SWarner Losh 	if (cardbus_cis_debug)
60447147ce7SWarner Losh 		device_printf(cbdev, "CIS pointer is %#x\n", start);
60549f158ccSJonathan Chen 	off = 0;
60663fa9f4cSJonathan Chen 	res = cardbus_read_tuple_init(cbdev, child, &start, &rid);
607509cfe6fSWarner Losh 	if (res == NULL) {
608509cfe6fSWarner Losh 		device_printf(cbdev, "Unable to allocate resources for CIS\n");
609a3133b58SWarner Losh 		return (ENXIO);
610509cfe6fSWarner Losh 	}
6111e962d00SScott Long 
6127bec1dd5SJonathan Chen 	do {
61347147ce7SWarner Losh 		if (cardbus_read_tuple(cbdev, child, res, start, &off,
61447147ce7SWarner Losh 		    &tupleid, &len, tupledata) != 0) {
61563fa9f4cSJonathan Chen 			device_printf(cbdev, "Failed to read CIS.\n");
61663fa9f4cSJonathan Chen 			cardbus_read_tuple_finish(cbdev, child, rid, res);
617a3133b58SWarner Losh 			return (ENXIO);
61863fa9f4cSJonathan Chen 		}
6197bec1dd5SJonathan Chen 
6207bec1dd5SJonathan Chen 		if (expect_linktarget && tupleid != CISTPL_LINKTARGET) {
621255b159fSJonathan Chen 			device_printf(cbdev, "Expecting link target, got 0x%x\n",
6227bec1dd5SJonathan Chen 			    tupleid);
62363fa9f4cSJonathan Chen 			cardbus_read_tuple_finish(cbdev, child, rid, res);
624a3133b58SWarner Losh 			return (EINVAL);
6257bec1dd5SJonathan Chen 		}
626255b159fSJonathan Chen 		expect_linktarget = decode_tuple(cbdev, child, tupleid, len,
62747147ce7SWarner Losh 		    tupledata, start, &off, callbacks, argp);
62863fa9f4cSJonathan Chen 		if (expect_linktarget != 0) {
629509cfe6fSWarner Losh 			device_printf(cbdev, "Parsing failed with %d\n",
630509cfe6fSWarner Losh 			    expect_linktarget);
63163fa9f4cSJonathan Chen 			cardbus_read_tuple_finish(cbdev, child, rid, res);
632a3133b58SWarner Losh 			return (expect_linktarget);
63363fa9f4cSJonathan Chen 		}
6347bec1dd5SJonathan Chen 	} while (tupleid != CISTPL_END);
63563fa9f4cSJonathan Chen 	cardbus_read_tuple_finish(cbdev, child, rid, res);
636a3133b58SWarner Losh 	return (0);
6370db7e66cSJonathan Chen }
6380db7e66cSJonathan Chen 
6390c95c705SJonathan Chen int
640255b159fSJonathan Chen cardbus_do_cis(device_t cbdev, device_t child)
6410c95c705SJonathan Chen {
64263fa9f4cSJonathan Chen 	int ret;
6430c95c705SJonathan Chen 	struct tuple_callbacks init_callbacks[] = {
644a294cdb6SWarner Losh 		MAKETUPLE(LONGLINK_CB,		unhandled),
6450c95c705SJonathan Chen 		MAKETUPLE(INDIRECT,		unhandled),
6460c95c705SJonathan Chen 		MAKETUPLE(LONGLINK_MFC,		unhandled),
6470c95c705SJonathan Chen 		MAKETUPLE(BAR,			bar),
6480c95c705SJonathan Chen 		MAKETUPLE(LONGLINK_A,		unhandled),
6490c95c705SJonathan Chen 		MAKETUPLE(LONGLINK_C,		unhandled),
6500c95c705SJonathan Chen 		MAKETUPLE(LINKTARGET,		linktarget),
6510c95c705SJonathan Chen 		MAKETUPLE(VERS_1,		vers_1),
6520c95c705SJonathan Chen 		MAKETUPLE(MANFID,		manfid),
6530c95c705SJonathan Chen 		MAKETUPLE(FUNCID,		funcid),
6540c95c705SJonathan Chen 		MAKETUPLE(FUNCE,		funce),
6550c95c705SJonathan Chen 		MAKETUPLE(END,			end),
6560c95c705SJonathan Chen 		MAKETUPLE(GENERIC,		generic),
6570c95c705SJonathan Chen 	};
65863fa9f4cSJonathan Chen 
65947147ce7SWarner Losh 	ret = cardbus_parse_cis(cbdev, child, init_callbacks, NULL);
66063fa9f4cSJonathan Chen 	if (ret < 0)
661a3133b58SWarner Losh 		return (ret);
662440b5adeSWarner Losh 	return 0;
6630c95c705SJonathan Chen }
664