xref: /freebsd/sys/dev/cardbus/cardbus_cis.c (revision f26d7f8e958e57186c0b7382fcea9f22b9bd6932)
1098ca2bdSWarner Losh /*-
2a7de0b74SWarner Losh  * Copyright (c) 2005-2008, M. Warner Losh
30db7e66cSJonathan Chen  * Copyright (c) 2000,2001 Jonathan Chen.
40db7e66cSJonathan Chen  * All rights reserved.
50db7e66cSJonathan Chen  *
60db7e66cSJonathan Chen  * Redistribution and use in source and binary forms, with or without
70db7e66cSJonathan Chen  * modification, are permitted provided that the following conditions
80db7e66cSJonathan Chen  * are met:
90db7e66cSJonathan Chen  * 1. Redistributions of source code must retain the above copyright
102dd5c91eSWarner Losh  *    notice, this list of conditions and the following disclaimer.
110db7e66cSJonathan Chen  * 2. Redistributions in binary form must reproduce the above copyright
122dd5c91eSWarner Losh  *    notice, this list of conditions and the following disclaimer in the
132dd5c91eSWarner Losh  *    documentation and/or other materials provided with the distribution.
140db7e66cSJonathan Chen  *
150db7e66cSJonathan Chen  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
160db7e66cSJonathan Chen  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
170db7e66cSJonathan Chen  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
182dd5c91eSWarner Losh  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
192dd5c91eSWarner Losh  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
200db7e66cSJonathan Chen  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
210db7e66cSJonathan Chen  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
220db7e66cSJonathan Chen  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
230db7e66cSJonathan Chen  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
240db7e66cSJonathan Chen  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
250db7e66cSJonathan Chen  * SUCH DAMAGE.
260db7e66cSJonathan Chen  */
270db7e66cSJonathan Chen 
28aad970f1SDavid E. O'Brien #include <sys/cdefs.h>
29aad970f1SDavid E. O'Brien __FBSDID("$FreeBSD$");
30aad970f1SDavid E. O'Brien 
310db7e66cSJonathan Chen /*
320db7e66cSJonathan Chen  * CIS Handling for the Cardbus Bus
330db7e66cSJonathan Chen  */
340db7e66cSJonathan Chen 
350db7e66cSJonathan Chen #include <sys/param.h>
360db7e66cSJonathan Chen #include <sys/systm.h>
370db7e66cSJonathan Chen #include <sys/kernel.h>
380c95c705SJonathan Chen #include <sys/malloc.h>
390db7e66cSJonathan Chen 
400db7e66cSJonathan Chen #include <sys/bus.h>
410db7e66cSJonathan Chen #include <machine/bus.h>
420db7e66cSJonathan Chen #include <machine/resource.h>
430db7e66cSJonathan Chen #include <sys/rman.h>
441e962d00SScott Long #include <sys/endian.h>
450db7e66cSJonathan Chen 
467ba175acSWarner Losh #include <sys/pciio.h>
4763fa9f4cSJonathan Chen #include <dev/pci/pcivar.h>
4863fa9f4cSJonathan Chen #include <dev/pci/pcireg.h>
490db7e66cSJonathan Chen 
50a294cdb6SWarner Losh #include <dev/pccard/pccardvar.h>
51a294cdb6SWarner Losh #include <dev/pccard/pccard_cis.h>
52a294cdb6SWarner Losh 
530db7e66cSJonathan Chen #include <dev/cardbus/cardbusreg.h>
5463fa9f4cSJonathan Chen #include <dev/cardbus/cardbusvar.h>
550db7e66cSJonathan Chen #include <dev/cardbus/cardbus_cis.h>
560db7e66cSJonathan Chen 
57b3889b68SWarner Losh extern int cardbus_cis_debug;
58b3889b68SWarner Losh 
59b3889b68SWarner Losh #define	DPRINTF(a) if (cardbus_cis_debug) printf a
60b3889b68SWarner Losh #define	DEVPRINTF(x) if (cardbus_cis_debug) device_printf x
61b3889b68SWarner Losh 
62c732cf3bSWarner Losh #define CIS_CONFIG_SPACE	(struct resource *)~0UL
63c732cf3bSWarner Losh 
6422acd92bSWarner Losh static int decode_tuple_generic(device_t cbdev, device_t child, int id,
6522acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
6647147ce7SWarner Losh     struct tuple_callbacks *info, void *);
6722acd92bSWarner Losh static int decode_tuple_linktarget(device_t cbdev, device_t child, int id,
6822acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
6947147ce7SWarner Losh     struct tuple_callbacks *info, void *);
7022acd92bSWarner Losh static int decode_tuple_vers_1(device_t cbdev, device_t child, int id,
7122acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
7247147ce7SWarner Losh     struct tuple_callbacks *info, void *);
7322acd92bSWarner Losh static int decode_tuple_funcid(device_t cbdev, device_t child, int id,
7422acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
7547147ce7SWarner Losh     struct tuple_callbacks *info, void *);
7622acd92bSWarner Losh static int decode_tuple_manfid(device_t cbdev, device_t child, int id,
7722acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
7847147ce7SWarner Losh     struct tuple_callbacks *info, void *);
7922acd92bSWarner Losh static int decode_tuple_funce(device_t cbdev, device_t child, int id,
8022acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
8147147ce7SWarner Losh     struct tuple_callbacks *info, void *);
8222acd92bSWarner Losh static int decode_tuple_bar(device_t cbdev, device_t child, int id,
8322acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
8447147ce7SWarner Losh     struct tuple_callbacks *info, void *);
8522acd92bSWarner Losh static int decode_tuple_unhandled(device_t cbdev, device_t child, int id,
8622acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
8747147ce7SWarner Losh     struct tuple_callbacks *info, void *);
8822acd92bSWarner Losh static int decode_tuple_end(device_t cbdev, device_t child, int id,
8922acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
9047147ce7SWarner Losh     struct tuple_callbacks *info, void *);
9122acd92bSWarner Losh 
92255b159fSJonathan Chen static int	cardbus_read_tuple_conf(device_t cbdev, device_t child,
9366e390feSWarner Losh 		    uint32_t start, uint32_t *off, int *tupleid, int *len,
9466e390feSWarner Losh 		    uint8_t *tupledata);
9563fa9f4cSJonathan Chen static int	cardbus_read_tuple_mem(device_t cbdev, struct resource *res,
9666e390feSWarner Losh 		    uint32_t start, uint32_t *off, int *tupleid, int *len,
9766e390feSWarner Losh 		    uint8_t *tupledata);
98255b159fSJonathan Chen static int	cardbus_read_tuple(device_t cbdev, device_t child,
9966e390feSWarner Losh 		    struct resource *res, uint32_t start, uint32_t *off,
10066e390feSWarner Losh 		    int *tupleid, int *len, uint8_t *tupledata);
10163fa9f4cSJonathan Chen static void	cardbus_read_tuple_finish(device_t cbdev, device_t child,
10263fa9f4cSJonathan Chen 		    int rid, struct resource *res);
10363fa9f4cSJonathan Chen static struct resource	*cardbus_read_tuple_init(device_t cbdev, device_t child,
10466e390feSWarner Losh 		    uint32_t *start, int *rid);
105255b159fSJonathan Chen static int	decode_tuple(device_t cbdev, device_t child, int tupleid,
10666e390feSWarner Losh 		    int len, uint8_t *tupledata, uint32_t start,
10747147ce7SWarner Losh 		    uint32_t *off, struct tuple_callbacks *callbacks,
10847147ce7SWarner Losh 		    void *);
109255b159fSJonathan Chen 
1100c95c705SJonathan Chen #define	MAKETUPLE(NAME,FUNC) { CISTPL_ ## NAME, #NAME, decode_tuple_ ## FUNC }
1110db7e66cSJonathan Chen 
1120db7e66cSJonathan Chen static char *funcnames[] = {
1130db7e66cSJonathan Chen 	"Multi-Functioned",
1140db7e66cSJonathan Chen 	"Memory",
1150db7e66cSJonathan Chen 	"Serial Port",
1160db7e66cSJonathan Chen 	"Parallel Port",
1170db7e66cSJonathan Chen 	"Fixed Disk",
1180db7e66cSJonathan Chen 	"Video Adaptor",
1190db7e66cSJonathan Chen 	"Network Adaptor",
1200db7e66cSJonathan Chen 	"AIMS",
1210db7e66cSJonathan Chen 	"SCSI",
1220db7e66cSJonathan Chen 	"Security"
1230db7e66cSJonathan Chen };
1240db7e66cSJonathan Chen 
125255b159fSJonathan Chen /*
126255b159fSJonathan Chen  * Handler functions for various CIS tuples
127255b159fSJonathan Chen  */
128255b159fSJonathan Chen 
12922acd92bSWarner Losh static int
13022acd92bSWarner Losh decode_tuple_generic(device_t cbdev, device_t child, int id,
13122acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
13247147ce7SWarner Losh     struct tuple_callbacks *info, void *argp)
1330db7e66cSJonathan Chen {
1340db7e66cSJonathan Chen 	int i;
1350db7e66cSJonathan Chen 
13622acd92bSWarner Losh 	if (cardbus_cis_debug) {
1370db7e66cSJonathan Chen 		if (info)
1380db7e66cSJonathan Chen 			printf("TUPLE: %s [%d]:", info->name, len);
1390db7e66cSJonathan Chen 		else
1400db7e66cSJonathan Chen 			printf("TUPLE: Unknown(0x%02x) [%d]:", id, len);
1410db7e66cSJonathan Chen 
1420db7e66cSJonathan Chen 		for (i = 0; i < len; i++) {
1430db7e66cSJonathan Chen 			if (i % 0x10 == 0 && len > 0x10)
1440db7e66cSJonathan Chen 				printf("\n       0x%02x:", i);
1457bec1dd5SJonathan Chen 			printf(" %02x", tupledata[i]);
1460db7e66cSJonathan Chen 		}
1470db7e66cSJonathan Chen 		printf("\n");
14822acd92bSWarner Losh 	}
149a3133b58SWarner Losh 	return (0);
1500db7e66cSJonathan Chen }
1510db7e66cSJonathan Chen 
15222acd92bSWarner Losh static int
15322acd92bSWarner Losh decode_tuple_linktarget(device_t cbdev, device_t child, int id,
15422acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
15547147ce7SWarner Losh     struct tuple_callbacks *info, void *argp)
1560db7e66cSJonathan Chen {
15749f158ccSJonathan Chen 	int i;
15849f158ccSJonathan Chen 
15922acd92bSWarner Losh 	if (cardbus_cis_debug) {
16049f158ccSJonathan Chen 		printf("TUPLE: %s [%d]:", info->name, len);
16149f158ccSJonathan Chen 
16249f158ccSJonathan Chen 		for (i = 0; i < len; i++) {
16349f158ccSJonathan Chen 			if (i % 0x10 == 0 && len > 0x10)
16449f158ccSJonathan Chen 				printf("\n       0x%02x:", i);
16549f158ccSJonathan Chen 			printf(" %02x", tupledata[i]);
16649f158ccSJonathan Chen 		}
16749f158ccSJonathan Chen 		printf("\n");
16822acd92bSWarner Losh 	}
1697bec1dd5SJonathan Chen 	if (len != 3 || tupledata[0] != 'C' || tupledata[1] != 'I' ||
1707bec1dd5SJonathan Chen 	    tupledata[2] != 'S') {
1710db7e66cSJonathan Chen 		printf("Invalid data for CIS Link Target!\n");
172255b159fSJonathan Chen 		decode_tuple_generic(cbdev, child, id, len, tupledata,
17347147ce7SWarner Losh 		    start, off, info, argp);
174a3133b58SWarner Losh 		return (EINVAL);
1750db7e66cSJonathan Chen 	}
176a3133b58SWarner Losh 	return (0);
1770db7e66cSJonathan Chen }
1780db7e66cSJonathan Chen 
17922acd92bSWarner Losh static int
18022acd92bSWarner Losh decode_tuple_vers_1(device_t cbdev, device_t child, int id,
18122acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
18247147ce7SWarner Losh     struct tuple_callbacks *info, void *argp)
1830db7e66cSJonathan Chen {
1840db7e66cSJonathan Chen 	int i;
185fbe9cff1SWarner Losh 
18622acd92bSWarner Losh 	if (cardbus_cis_debug) {
1877bec1dd5SJonathan Chen 		printf("Product version: %d.%d\n", tupledata[0], tupledata[1]);
1880db7e66cSJonathan Chen 		printf("Product name: ");
1890db7e66cSJonathan Chen 		for (i = 2; i < len; i++) {
1907bec1dd5SJonathan Chen 			if (tupledata[i] == '\0')
1910db7e66cSJonathan Chen 				printf(" | ");
1927bec1dd5SJonathan Chen 			else if (tupledata[i] == 0xff)
1930db7e66cSJonathan Chen 				break;
1940db7e66cSJonathan Chen 			else
1957bec1dd5SJonathan Chen 				printf("%c", tupledata[i]);
1960db7e66cSJonathan Chen 		}
1970db7e66cSJonathan Chen 		printf("\n");
19822acd92bSWarner Losh 	}
199a3133b58SWarner Losh 	return (0);
2000db7e66cSJonathan Chen }
2010db7e66cSJonathan Chen 
20222acd92bSWarner Losh static int
20322acd92bSWarner Losh decode_tuple_funcid(device_t cbdev, device_t child, int id,
20422acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
20547147ce7SWarner Losh     struct tuple_callbacks *info, void *argp)
2060db7e66cSJonathan Chen {
207fbe9cff1SWarner Losh 	struct cardbus_devinfo *dinfo = device_get_ivars(child);
2080db7e66cSJonathan Chen 	int numnames = sizeof(funcnames) / sizeof(funcnames[0]);
209fbe9cff1SWarner Losh 	int i;
2100db7e66cSJonathan Chen 
21122acd92bSWarner Losh 	if (cardbus_cis_debug) {
2120db7e66cSJonathan Chen 		printf("Functions: ");
2130db7e66cSJonathan Chen 		for (i = 0; i < len; i++) {
2147bec1dd5SJonathan Chen 			if (tupledata[i] < numnames)
2157bec1dd5SJonathan Chen 				printf("%s", funcnames[tupledata[i]]);
2160db7e66cSJonathan Chen 			else
2177bec1dd5SJonathan Chen 				printf("Unknown(%d)", tupledata[i]);
218255b159fSJonathan Chen 			if (i < len - 1)
219255b159fSJonathan Chen 				printf(", ");
2200db7e66cSJonathan Chen 		}
22122acd92bSWarner Losh 		printf("\n");
22222acd92bSWarner Losh 	}
223fbe9cff1SWarner Losh 	if (len > 0)
224fbe9cff1SWarner Losh 		dinfo->funcid = tupledata[0];		/* use first in list */
225a3133b58SWarner Losh 	return (0);
2260db7e66cSJonathan Chen }
2270db7e66cSJonathan Chen 
22822acd92bSWarner Losh static int
22922acd92bSWarner Losh decode_tuple_manfid(device_t cbdev, device_t child, int id,
23022acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
23147147ce7SWarner Losh     struct tuple_callbacks *info, void *argp)
2320db7e66cSJonathan Chen {
233fbe9cff1SWarner Losh 	struct cardbus_devinfo *dinfo = device_get_ivars(child);
2340db7e66cSJonathan Chen 	int i;
235fbe9cff1SWarner Losh 
23622acd92bSWarner Losh 	if (cardbus_cis_debug) {
2370db7e66cSJonathan Chen 		printf("Manufacturer ID: ");
2380db7e66cSJonathan Chen 		for (i = 0; i < len; i++)
2397bec1dd5SJonathan Chen 			printf("%02x", tupledata[i]);
2400db7e66cSJonathan Chen 		printf("\n");
24122acd92bSWarner Losh 	}
242fbe9cff1SWarner Losh 
243fbe9cff1SWarner Losh 	if (len == 5) {
244fbe9cff1SWarner Losh 		dinfo->mfrid = tupledata[1] | (tupledata[2] << 8);
245fbe9cff1SWarner Losh 		dinfo->prodid = tupledata[3] | (tupledata[4] << 8);
246fbe9cff1SWarner Losh 	}
247a3133b58SWarner Losh 	return (0);
2480db7e66cSJonathan Chen }
2490db7e66cSJonathan Chen 
25022acd92bSWarner Losh static int
25122acd92bSWarner Losh decode_tuple_funce(device_t cbdev, device_t child, int id,
25222acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
25347147ce7SWarner Losh     struct tuple_callbacks *info, void *argp)
2540db7e66cSJonathan Chen {
255fbe9cff1SWarner Losh 	struct cardbus_devinfo *dinfo = device_get_ivars(child);
256fbe9cff1SWarner Losh 	int type, i;
257fbe9cff1SWarner Losh 
25822acd92bSWarner Losh 	if (cardbus_cis_debug) {
2590db7e66cSJonathan Chen 		printf("Function Extension: ");
2600db7e66cSJonathan Chen 		for (i = 0; i < len; i++)
2617bec1dd5SJonathan Chen 			printf("%02x", tupledata[i]);
2620db7e66cSJonathan Chen 		printf("\n");
26322acd92bSWarner Losh 	}
264fbe9cff1SWarner Losh 	if (len < 2)			/* too short */
265fbe9cff1SWarner Losh 		return (0);
266fbe9cff1SWarner Losh 	type = tupledata[0];		/* XXX <32 always? */
267fbe9cff1SWarner Losh 	switch (dinfo->funcid) {
268440b5adeSWarner Losh 	case PCCARD_FUNCTION_NETWORK:
269fbe9cff1SWarner Losh 		switch (type) {
270440b5adeSWarner Losh 		case PCCARD_TPLFE_TYPE_LAN_NID:
27122acd92bSWarner Losh 			if (tupledata[1] > sizeof(dinfo->funce.lan.nid)) {
27222acd92bSWarner Losh 				/* ignore, warning? */
27322acd92bSWarner Losh 				return (0);
27422acd92bSWarner Losh 			}
27522acd92bSWarner Losh 			bcopy(tupledata + 2, dinfo->funce.lan.nid,
27622acd92bSWarner Losh 			    tupledata[1]);
277fbe9cff1SWarner Losh 			break;
278fbe9cff1SWarner Losh 		}
279fbe9cff1SWarner Losh 		dinfo->fepresent |= 1<<type;
280fbe9cff1SWarner Losh 		break;
281fbe9cff1SWarner Losh 	}
282a3133b58SWarner Losh 	return (0);
2830db7e66cSJonathan Chen }
2840db7e66cSJonathan Chen 
28522acd92bSWarner Losh static int
28622acd92bSWarner Losh decode_tuple_bar(device_t cbdev, device_t child, int id,
28722acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
28847147ce7SWarner Losh     struct tuple_callbacks *info, void *argp)
2890db7e66cSJonathan Chen {
29063fa9f4cSJonathan Chen 	struct cardbus_devinfo *dinfo = device_get_ivars(child);
2910db7e66cSJonathan Chen 	int type;
2921e962d00SScott Long 	uint8_t reg;
293bee89c73SWarner Losh 	uint32_t bar;
2940db7e66cSJonathan Chen 
295e6e272b9SScott Long 	if (len != 6) {
2961e962d00SScott Long 		device_printf(cbdev, "CIS BAR length not 6 (%d)\n", len);
297e6e272b9SScott Long 		return (EINVAL);
298e6e272b9SScott Long 	}
2991e962d00SScott Long 
3001e962d00SScott Long 	reg = *tupledata;
3011e962d00SScott Long 	len = le32toh(*(uint32_t*)(tupledata + 2));
302c732cf3bSWarner Losh 	if (reg & TPL_BAR_REG_AS)
3030db7e66cSJonathan Chen 		type = SYS_RES_IOPORT;
304c732cf3bSWarner Losh 	else
3050db7e66cSJonathan Chen 		type = SYS_RES_MEMORY;
3061e962d00SScott Long 
3071e962d00SScott Long 	bar = reg & TPL_BAR_REG_ASI_MASK;
3081e962d00SScott Long 	if (bar == 0) {
3091e962d00SScott Long 		device_printf(cbdev, "Invalid BAR type 0 in CIS\n");
3101e962d00SScott Long 		return (EINVAL);	/* XXX Return an error? */
3111e962d00SScott Long 	} else if (bar == 7) {
3121e962d00SScott Long 		/* XXX Should we try to map in Option ROMs? */
313a3133b58SWarner Losh 		return (0);
3140db7e66cSJonathan Chen 	}
3151e962d00SScott Long 
31672d3502eSScott Long 	/* Convert from BAR type to BAR offset */
317495036f2SWarner Losh 	bar = PCIR_BAR(bar - 1);
3181e962d00SScott Long 
31963fa9f4cSJonathan Chen 	if (type == SYS_RES_MEMORY) {
32072d3502eSScott Long 		if (reg & TPL_BAR_REG_PREFETCHABLE)
321f3d3468dSWarner Losh 			dinfo->mprefetchable |= (1 << PCI_RID2BAR(bar));
322a7c43559SWarner Losh 		/*
323e371fa45SWarner Losh 		 * The PC Card spec says we're only supposed to honor this
324e371fa45SWarner Losh 		 * hint when the cardbus bridge is a child of pci0 (the main
325e371fa45SWarner Losh 		 * bus).  The PC Card spec seems to indicate that this should
326e371fa45SWarner Losh 		 * only be done on x86 based machines, which suggests that on
327e371fa45SWarner Losh 		 * non-x86 machines the adddresses can be anywhere.  Since the
328e371fa45SWarner Losh 		 * hardware can do it on non-x86 machines, it should be able
329e371fa45SWarner Losh 		 * to do it on x86 machines too.  Therefore, we can and should
330e371fa45SWarner Losh 		 * ignore this hint.  Furthermore, the PC Card spec recommends
331e371fa45SWarner Losh 		 * always allocating memory above 1MB, contradicting the other
332e371fa45SWarner Losh 		 * part of the PC Card spec, it seems.  We make note of it,
333e371fa45SWarner Losh 		 * but otherwise don't use this information.
334a7c43559SWarner Losh 		 *
335e371fa45SWarner Losh 		 * Some Realtek cards have this set in their CIS, but fail
336e371fa45SWarner Losh 		 * to actually work when mapped this way, and experience
337e371fa45SWarner Losh 		 * has shown ignoring this big to be a wise choice.
338e371fa45SWarner Losh 		 *
339e371fa45SWarner Losh 		 * XXX We should cite chapter and verse for standard refs.
340a7c43559SWarner Losh 		 */
34172d3502eSScott Long 		if (reg & TPL_BAR_REG_BELOW1MB)
342f3d3468dSWarner Losh 			dinfo->mbelow1mb |= (1 << PCI_RID2BAR(bar));
3430db7e66cSJonathan Chen 	}
3441e962d00SScott Long 
345a3133b58SWarner Losh 	return (0);
3460db7e66cSJonathan Chen }
3470db7e66cSJonathan Chen 
34822acd92bSWarner Losh static int
34922acd92bSWarner Losh decode_tuple_unhandled(device_t cbdev, device_t child, int id,
35022acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
35147147ce7SWarner Losh     struct tuple_callbacks *info, void *argp)
3520db7e66cSJonathan Chen {
35322acd92bSWarner Losh 	/* Make this message suck less XXX */
3540db7e66cSJonathan Chen 	printf("TUPLE: %s [%d] is unhandled! Bailing...", info->name, len);
35547147ce7SWarner Losh 	return (EINVAL);
3560db7e66cSJonathan Chen }
3570db7e66cSJonathan Chen 
35822acd92bSWarner Losh static int
35922acd92bSWarner Losh decode_tuple_end(device_t cbdev, device_t child, int id,
36022acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
36147147ce7SWarner Losh     struct tuple_callbacks *info, void *argp)
3620db7e66cSJonathan Chen {
363509cfe6fSWarner Losh 	if (cardbus_cis_debug)
3640c95c705SJonathan Chen 		printf("CIS reading done\n");
365a3133b58SWarner Losh 	return (0);
3660db7e66cSJonathan Chen }
3670db7e66cSJonathan Chen 
368255b159fSJonathan Chen /*
369255b159fSJonathan Chen  * Functions to read the a tuple from the card
370255b159fSJonathan Chen  */
371255b159fSJonathan Chen 
3720db7e66cSJonathan Chen static int
37366e390feSWarner Losh cardbus_read_tuple_conf(device_t cbdev, device_t child, uint32_t start,
37466e390feSWarner Losh     uint32_t *off, int *tupleid, int *len, uint8_t *tupledata)
3757bec1dd5SJonathan Chen {
3767bec1dd5SJonathan Chen 	int i, j;
37766e390feSWarner Losh 	uint32_t e;
37866e390feSWarner Losh 	uint32_t loc;
3797bec1dd5SJonathan Chen 
38063fa9f4cSJonathan Chen 	loc = start + *off;
38149f158ccSJonathan Chen 
38249f158ccSJonathan Chen 	e = pci_read_config(child, loc - loc % 4, 4);
38349f158ccSJonathan Chen 	for (j = loc % 4; j > 0; j--)
3847bec1dd5SJonathan Chen 		e >>= 8;
3857bec1dd5SJonathan Chen 	*len = 0;
38649f158ccSJonathan Chen 	for (i = loc, j = -2; j < *len; j++, i++) {
3877bec1dd5SJonathan Chen 		if (i % 4 == 0)
3887bec1dd5SJonathan Chen 			e = pci_read_config(child, i, 4);
3897bec1dd5SJonathan Chen 		if (j == -2)
3907bec1dd5SJonathan Chen 			*tupleid = 0xff & e;
3917bec1dd5SJonathan Chen 		else if (j == -1)
3927bec1dd5SJonathan Chen 			*len = 0xff & e;
3937bec1dd5SJonathan Chen 		else
3947bec1dd5SJonathan Chen 			tupledata[j] = 0xff & e;
3957bec1dd5SJonathan Chen 		e >>= 8;
3967bec1dd5SJonathan Chen 	}
3977bec1dd5SJonathan Chen 	*off += *len + 2;
398a3133b58SWarner Losh 	return (0);
3997bec1dd5SJonathan Chen }
4007bec1dd5SJonathan Chen 
4017bec1dd5SJonathan Chen static int
40266e390feSWarner Losh cardbus_read_tuple_mem(device_t cbdev, struct resource *res, uint32_t start,
40366e390feSWarner Losh     uint32_t *off, int *tupleid, int *len, uint8_t *tupledata)
4040db7e66cSJonathan Chen {
40563fa9f4cSJonathan Chen 	int ret;
4060db7e66cSJonathan Chen 
407f26d7f8eSJohn Baldwin 	*tupleid = bus_read_1(res, start + *off);
408f26d7f8eSJohn Baldwin 	*len = bus_read_1(res, start + *off + 1);
409f26d7f8eSJohn Baldwin 	bus_read_region_1(res, *off + start + 2, tupledata, *len);
41063fa9f4cSJonathan Chen 	ret = 0;
41163fa9f4cSJonathan Chen 	*off += *len + 2;
412a3133b58SWarner Losh 	return (ret);
4130db7e66cSJonathan Chen }
41463fa9f4cSJonathan Chen 
41563fa9f4cSJonathan Chen static int
41663fa9f4cSJonathan Chen cardbus_read_tuple(device_t cbdev, device_t child, struct resource *res,
41766e390feSWarner Losh     uint32_t start, uint32_t *off, int *tupleid, int *len,
41866e390feSWarner Losh     uint8_t *tupledata)
41963fa9f4cSJonathan Chen {
420bee89c73SWarner Losh 	if (res == CIS_CONFIG_SPACE)
421a3133b58SWarner Losh 		return (cardbus_read_tuple_conf(cbdev, child, start, off,
422a3133b58SWarner Losh 		    tupleid, len, tupledata));
423bee89c73SWarner Losh 	return (cardbus_read_tuple_mem(cbdev, res, start, off, tupleid, len,
424bee89c73SWarner Losh 	    tupledata));
42563fa9f4cSJonathan Chen }
42663fa9f4cSJonathan Chen 
42763fa9f4cSJonathan Chen static void
42863fa9f4cSJonathan Chen cardbus_read_tuple_finish(device_t cbdev, device_t child, int rid,
42963fa9f4cSJonathan Chen     struct resource *res)
43063fa9f4cSJonathan Chen {
431c732cf3bSWarner Losh 	if (res != CIS_CONFIG_SPACE) {
432bee89c73SWarner Losh 		bus_release_resource(child, SYS_RES_MEMORY, rid, res);
43347147ce7SWarner Losh 		if (rid == PCIM_CIS_ASI_ROM)
43447147ce7SWarner Losh 			pci_write_config(child, rid, pci_read_config(child,
43547147ce7SWarner Losh 			    rid, 4) & ~PCIR_BIOS, 4);
43663fa9f4cSJonathan Chen 	}
43763fa9f4cSJonathan Chen }
43863fa9f4cSJonathan Chen 
43963fa9f4cSJonathan Chen static struct resource *
44066e390feSWarner Losh cardbus_read_tuple_init(device_t cbdev, device_t child, uint32_t *start,
44163fa9f4cSJonathan Chen     int *rid)
44263fa9f4cSJonathan Chen {
44363fa9f4cSJonathan Chen 	struct resource *res;
444495036f2SWarner Losh 	uint32_t space;
44563fa9f4cSJonathan Chen 
44641ac33a2SWarner Losh 	space = *start & PCIM_CIS_ASI_MASK;
447495036f2SWarner Losh 	switch (space) {
4484b333740SWarner Losh 	case PCIM_CIS_ASI_CONFIG:
449e6d3b1bdSWarner Losh 		DEVPRINTF((cbdev, "CIS in PCI config space\n"));
450e6e272b9SScott Long 		/* CIS in PCI config space need no initialization */
451c732cf3bSWarner Losh 		return (CIS_CONFIG_SPACE);
45241ac33a2SWarner Losh 	case PCIM_CIS_ASI_BAR0:
45341ac33a2SWarner Losh 	case PCIM_CIS_ASI_BAR1:
45441ac33a2SWarner Losh 	case PCIM_CIS_ASI_BAR2:
45541ac33a2SWarner Losh 	case PCIM_CIS_ASI_BAR3:
45641ac33a2SWarner Losh 	case PCIM_CIS_ASI_BAR4:
45741ac33a2SWarner Losh 	case PCIM_CIS_ASI_BAR5:
45841ac33a2SWarner Losh 		*rid = PCIR_BAR(space - PCIM_CIS_ASI_BAR0);
459e6d3b1bdSWarner Losh 		DEVPRINTF((cbdev, "CIS in BAR %#x\n", *rid));
46063fa9f4cSJonathan Chen 		break;
46141ac33a2SWarner Losh 	case PCIM_CIS_ASI_ROM:
46241ac33a2SWarner Losh 		*rid = PCIR_BIOS;
463e6d3b1bdSWarner Losh 		DEVPRINTF((cbdev, "CIS in option rom\n"));
46463fa9f4cSJonathan Chen 		break;
46563fa9f4cSJonathan Chen 	default:
46663fa9f4cSJonathan Chen 		device_printf(cbdev, "Unable to read CIS: Unknown space: %d\n",
467495036f2SWarner Losh 		    space);
468a3133b58SWarner Losh 		return (NULL);
46963fa9f4cSJonathan Chen 	}
47063fa9f4cSJonathan Chen 
47163fa9f4cSJonathan Chen 	/* allocate the memory space to read CIS */
472bee89c73SWarner Losh 	res = bus_alloc_resource(child, SYS_RES_MEMORY, rid, 0, ~0, 1,
47347147ce7SWarner Losh 	    rman_make_alignment_flags(4096) | RF_ACTIVE);
47463fa9f4cSJonathan Chen 	if (res == NULL) {
47563fa9f4cSJonathan Chen 		device_printf(cbdev, "Unable to allocate resource "
47663fa9f4cSJonathan Chen 		    "to read CIS.\n");
477a3133b58SWarner Losh 		return (NULL);
47863fa9f4cSJonathan Chen 	}
479e6d3b1bdSWarner Losh 	DEVPRINTF((cbdev, "CIS Mapped to %#lx\n", rman_get_start(res)));
48047147ce7SWarner Losh 	if (*rid == PCIR_BIOS)
48147147ce7SWarner Losh 		pci_write_config(child, *rid,
48247147ce7SWarner Losh 		    rman_get_start(res) | PCIM_BIOS_ENABLE, 4);
48363fa9f4cSJonathan Chen 
48463fa9f4cSJonathan Chen 	/* Flip to the right ROM image if CIS is in ROM */
48541ac33a2SWarner Losh 	if (space == PCIM_CIS_ASI_ROM) {
48666e390feSWarner Losh 		uint32_t imagesize;
48766e390feSWarner Losh 		uint32_t imagebase = 0;
48866e390feSWarner Losh 		uint32_t pcidata;
48966e390feSWarner Losh 		uint16_t romsig;
49063fa9f4cSJonathan Chen 		int romnum = 0;
491e6e272b9SScott Long 		int imagenum;
49263fa9f4cSJonathan Chen 
49341ac33a2SWarner Losh 		imagenum = (*start & PCIM_CIS_ROM_MASK) >> 28;
49463fa9f4cSJonathan Chen 		for (romnum = 0;; romnum++) {
495f26d7f8eSJohn Baldwin 			romsig = bus_read_2(res,
496e6e272b9SScott Long 			    imagebase + CARDBUS_EXROM_SIGNATURE);
497e6e272b9SScott Long 			if (romsig != 0xaa55) {
49863fa9f4cSJonathan Chen 				device_printf(cbdev, "Bad header in rom %d: "
499e6e272b9SScott Long 				    "[%x] %04x\n", romnum, imagebase +
500e6e272b9SScott Long 				    CARDBUS_EXROM_SIGNATURE, romsig);
50189558ee8SWarner Losh 				bus_release_resource(child, SYS_RES_MEMORY,
50263fa9f4cSJonathan Chen 				    *rid, res);
50363fa9f4cSJonathan Chen 				*rid = 0;
504a3133b58SWarner Losh 				return (NULL);
50563fa9f4cSJonathan Chen 			}
506e6e272b9SScott Long 
507e6e272b9SScott Long 			/*
508e6e272b9SScott Long 			 * If this was the Option ROM image that we were
509e6e272b9SScott Long 			 * looking for, then we are done.
510e6e272b9SScott Long 			 */
511e6e272b9SScott Long 			if (romnum == imagenum)
512e6e272b9SScott Long 				break;
513e6e272b9SScott Long 
514e6e272b9SScott Long 			/* Find out where the next Option ROM image is */
515f26d7f8eSJohn Baldwin 			pcidata = imagebase + bus_read_2(res,
516e6e272b9SScott Long 			    imagebase + CARDBUS_EXROM_DATA_PTR);
517f26d7f8eSJohn Baldwin 			imagesize = bus_read_2(res,
518e6e272b9SScott Long 			    pcidata + CARDBUS_EXROM_DATA_IMAGE_LENGTH);
5190db7e66cSJonathan Chen 
5207bec1dd5SJonathan Chen 			if (imagesize == 0) {
5210db7e66cSJonathan Chen 				/*
5220db7e66cSJonathan Chen 				 * XXX some ROMs seem to have this as zero,
5230db7e66cSJonathan Chen 				 * can we assume this means 1 block?
5240db7e66cSJonathan Chen 				 */
525e6e272b9SScott Long 				device_printf(cbdev, "Warning, size of Option "
526e6e272b9SScott Long 				    "ROM image %d is 0 bytes, assuming 512 "
527e6e272b9SScott Long 				    "bytes.\n", romnum);
5280db7e66cSJonathan Chen 				imagesize = 1;
5297bec1dd5SJonathan Chen 			}
530e6e272b9SScott Long 
531e6e272b9SScott Long 			/* Image size is in 512 byte units */
5320db7e66cSJonathan Chen 			imagesize <<= 9;
5330db7e66cSJonathan Chen 
534f26d7f8eSJohn Baldwin 			if ((bus_read_1(res, pcidata +
5351e06ae99SScott Long 			    CARDBUS_EXROM_DATA_INDICATOR) & 0x80) != 0) {
536e6e272b9SScott Long 				device_printf(cbdev, "Cannot find CIS in "
537e6e272b9SScott Long 				    "Option ROM\n");
53889558ee8SWarner Losh 				bus_release_resource(child, SYS_RES_MEMORY,
539e6e272b9SScott Long 				    *rid, res);
540e6e272b9SScott Long 				*rid = 0;
541a3133b58SWarner Losh 				return (NULL);
5420db7e66cSJonathan Chen 			}
543e6e272b9SScott Long 			imagebase += imagesize;
5440db7e66cSJonathan Chen 		}
54541ac33a2SWarner Losh 		*start = imagebase + (*start & PCIM_CIS_ADDR_MASK);
5460db7e66cSJonathan Chen 	} else {
54741ac33a2SWarner Losh 		*start = *start & PCIM_CIS_ADDR_MASK;
5480db7e66cSJonathan Chen 	}
549e6d3b1bdSWarner Losh 	DEVPRINTF((cbdev, "CIS offset is %#x\n", *start));
550e6e272b9SScott Long 
551a3133b58SWarner Losh 	return (res);
5527bec1dd5SJonathan Chen }
5537bec1dd5SJonathan Chen 
554255b159fSJonathan Chen /*
555255b159fSJonathan Chen  * Dispatch the right handler function per tuple
556255b159fSJonathan Chen  */
557255b159fSJonathan Chen 
5587bec1dd5SJonathan Chen static int
559255b159fSJonathan Chen decode_tuple(device_t cbdev, device_t child, int tupleid, int len,
56066e390feSWarner Losh     uint8_t *tupledata, uint32_t start, uint32_t *off,
56147147ce7SWarner Losh     struct tuple_callbacks *callbacks, void *argp)
5627bec1dd5SJonathan Chen {
5637bec1dd5SJonathan Chen 	int i;
5640c95c705SJonathan Chen 	for (i = 0; callbacks[i].id != CISTPL_GENERIC; i++) {
5650c95c705SJonathan Chen 		if (tupleid == callbacks[i].id)
566a3133b58SWarner Losh 			return (callbacks[i].func(cbdev, child, tupleid, len,
56747147ce7SWarner Losh 			    tupledata, start, off, &callbacks[i], argp));
5687bec1dd5SJonathan Chen 	}
569a3133b58SWarner Losh 	return (callbacks[i].func(cbdev, child, tupleid, len,
57047147ce7SWarner Losh 	    tupledata, start, off, NULL, argp));
5710db7e66cSJonathan Chen }
5720db7e66cSJonathan Chen 
57347147ce7SWarner Losh int
574255b159fSJonathan Chen cardbus_parse_cis(device_t cbdev, device_t child,
57547147ce7SWarner Losh     struct tuple_callbacks *callbacks, void *argp)
5760db7e66cSJonathan Chen {
57766e390feSWarner Losh 	uint8_t tupledata[MAXTUPLESIZE];
57817ee700bSWarner Losh 	int tupleid = CISTPL_NULL;
5797bec1dd5SJonathan Chen 	int len;
5807bec1dd5SJonathan Chen 	int expect_linktarget;
58166e390feSWarner Losh 	uint32_t start, off;
58263fa9f4cSJonathan Chen 	struct resource *res;
58363fa9f4cSJonathan Chen 	int rid;
5840db7e66cSJonathan Chen 
5850db7e66cSJonathan Chen 	bzero(tupledata, MAXTUPLESIZE);
5867bec1dd5SJonathan Chen 	expect_linktarget = TRUE;
58741ac33a2SWarner Losh 	if ((start = pci_read_config(child, PCIR_CIS, 4)) == 0) {
588e6d3b1bdSWarner Losh 		DEVPRINTF((cbdev, "Warning: CIS pointer is 0: (no CIS)\n"));
589e6e272b9SScott Long 		return (ENXIO);
590509cfe6fSWarner Losh 	}
591e6d3b1bdSWarner Losh 	DEVPRINTF((cbdev, "CIS pointer is %#x\n", start));
59249f158ccSJonathan Chen 	off = 0;
59363fa9f4cSJonathan Chen 	res = cardbus_read_tuple_init(cbdev, child, &start, &rid);
594509cfe6fSWarner Losh 	if (res == NULL) {
595509cfe6fSWarner Losh 		device_printf(cbdev, "Unable to allocate resources for CIS\n");
596a3133b58SWarner Losh 		return (ENXIO);
597509cfe6fSWarner Losh 	}
5981e962d00SScott Long 
5997bec1dd5SJonathan Chen 	do {
60047147ce7SWarner Losh 		if (cardbus_read_tuple(cbdev, child, res, start, &off,
60147147ce7SWarner Losh 		    &tupleid, &len, tupledata) != 0) {
60263fa9f4cSJonathan Chen 			device_printf(cbdev, "Failed to read CIS.\n");
60363fa9f4cSJonathan Chen 			cardbus_read_tuple_finish(cbdev, child, rid, res);
604a3133b58SWarner Losh 			return (ENXIO);
60563fa9f4cSJonathan Chen 		}
6067bec1dd5SJonathan Chen 
6077bec1dd5SJonathan Chen 		if (expect_linktarget && tupleid != CISTPL_LINKTARGET) {
608255b159fSJonathan Chen 			device_printf(cbdev, "Expecting link target, got 0x%x\n",
6097bec1dd5SJonathan Chen 			    tupleid);
61063fa9f4cSJonathan Chen 			cardbus_read_tuple_finish(cbdev, child, rid, res);
611a3133b58SWarner Losh 			return (EINVAL);
6127bec1dd5SJonathan Chen 		}
613255b159fSJonathan Chen 		expect_linktarget = decode_tuple(cbdev, child, tupleid, len,
61447147ce7SWarner Losh 		    tupledata, start, &off, callbacks, argp);
61563fa9f4cSJonathan Chen 		if (expect_linktarget != 0) {
616509cfe6fSWarner Losh 			device_printf(cbdev, "Parsing failed with %d\n",
617509cfe6fSWarner Losh 			    expect_linktarget);
61863fa9f4cSJonathan Chen 			cardbus_read_tuple_finish(cbdev, child, rid, res);
619a3133b58SWarner Losh 			return (expect_linktarget);
62063fa9f4cSJonathan Chen 		}
6217bec1dd5SJonathan Chen 	} while (tupleid != CISTPL_END);
62263fa9f4cSJonathan Chen 	cardbus_read_tuple_finish(cbdev, child, rid, res);
623a3133b58SWarner Losh 	return (0);
6240db7e66cSJonathan Chen }
6250db7e66cSJonathan Chen 
6260c95c705SJonathan Chen int
627255b159fSJonathan Chen cardbus_do_cis(device_t cbdev, device_t child)
6280c95c705SJonathan Chen {
6290c95c705SJonathan Chen 	struct tuple_callbacks init_callbacks[] = {
630a294cdb6SWarner Losh 		MAKETUPLE(LONGLINK_CB,		unhandled),
6310c95c705SJonathan Chen 		MAKETUPLE(INDIRECT,		unhandled),
6320c95c705SJonathan Chen 		MAKETUPLE(LONGLINK_MFC,		unhandled),
6330c95c705SJonathan Chen 		MAKETUPLE(BAR,			bar),
6340c95c705SJonathan Chen 		MAKETUPLE(LONGLINK_A,		unhandled),
6350c95c705SJonathan Chen 		MAKETUPLE(LONGLINK_C,		unhandled),
6360c95c705SJonathan Chen 		MAKETUPLE(LINKTARGET,		linktarget),
6370c95c705SJonathan Chen 		MAKETUPLE(VERS_1,		vers_1),
6380c95c705SJonathan Chen 		MAKETUPLE(MANFID,		manfid),
6390c95c705SJonathan Chen 		MAKETUPLE(FUNCID,		funcid),
6400c95c705SJonathan Chen 		MAKETUPLE(FUNCE,		funce),
6410c95c705SJonathan Chen 		MAKETUPLE(END,			end),
6420c95c705SJonathan Chen 		MAKETUPLE(GENERIC,		generic),
6430c95c705SJonathan Chen 	};
64463fa9f4cSJonathan Chen 
6451acd1e20SWarner Losh 	return (cardbus_parse_cis(cbdev, child, init_callbacks, NULL));
6460c95c705SJonathan Chen }
647