xref: /freebsd/sys/dev/cardbus/cardbus_cis.c (revision d6f64df91289453012e8b529b1c65d4523921429)
1098ca2bdSWarner Losh /*-
2718cf2ccSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3718cf2ccSPedro F. Giffuni  *
4a7de0b74SWarner Losh  * Copyright (c) 2005-2008, M. Warner Losh
50db7e66cSJonathan Chen  * Copyright (c) 2000,2001 Jonathan Chen.
60db7e66cSJonathan Chen  * All rights reserved.
70db7e66cSJonathan Chen  *
80db7e66cSJonathan Chen  * Redistribution and use in source and binary forms, with or without
90db7e66cSJonathan Chen  * modification, are permitted provided that the following conditions
100db7e66cSJonathan Chen  * are met:
110db7e66cSJonathan Chen  * 1. Redistributions of source code must retain the above copyright
122dd5c91eSWarner Losh  *    notice, this list of conditions and the following disclaimer.
130db7e66cSJonathan Chen  * 2. Redistributions in binary form must reproduce the above copyright
142dd5c91eSWarner Losh  *    notice, this list of conditions and the following disclaimer in the
152dd5c91eSWarner Losh  *    documentation and/or other materials provided with the distribution.
160db7e66cSJonathan Chen  *
170db7e66cSJonathan Chen  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
180db7e66cSJonathan Chen  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
190db7e66cSJonathan Chen  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
202dd5c91eSWarner Losh  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
212dd5c91eSWarner Losh  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
220db7e66cSJonathan Chen  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
230db7e66cSJonathan Chen  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
240db7e66cSJonathan Chen  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
250db7e66cSJonathan Chen  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
260db7e66cSJonathan Chen  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
270db7e66cSJonathan Chen  * SUCH DAMAGE.
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 
52a294cdb6SWarner Losh #include <dev/pccard/pccardvar.h>
53a294cdb6SWarner Losh #include <dev/pccard/pccard_cis.h>
54a294cdb6SWarner Losh 
550db7e66cSJonathan Chen #include <dev/cardbus/cardbusreg.h>
5663fa9f4cSJonathan Chen #include <dev/cardbus/cardbusvar.h>
570db7e66cSJonathan Chen #include <dev/cardbus/cardbus_cis.h>
580db7e66cSJonathan Chen 
59b3889b68SWarner Losh extern int cardbus_cis_debug;
60b3889b68SWarner Losh 
61b3889b68SWarner Losh #define	DPRINTF(a) if (cardbus_cis_debug) printf a
62b3889b68SWarner Losh #define	DEVPRINTF(x) if (cardbus_cis_debug) device_printf x
63b3889b68SWarner Losh 
64c732cf3bSWarner Losh #define CIS_CONFIG_SPACE	(struct resource *)~0UL
65c732cf3bSWarner Losh 
6622acd92bSWarner Losh static int decode_tuple_generic(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_linktarget(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_vers_1(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_funcid(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_manfid(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_funce(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_bar(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_unhandled(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 static int decode_tuple_end(device_t cbdev, device_t child, int id,
9122acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
9247147ce7SWarner Losh     struct tuple_callbacks *info, void *);
9322acd92bSWarner Losh 
94255b159fSJonathan Chen static int	cardbus_read_tuple_conf(device_t cbdev, device_t child,
9566e390feSWarner Losh 		    uint32_t start, uint32_t *off, int *tupleid, int *len,
9666e390feSWarner Losh 		    uint8_t *tupledata);
9763fa9f4cSJonathan Chen static int	cardbus_read_tuple_mem(device_t cbdev, struct resource *res,
9866e390feSWarner Losh 		    uint32_t start, uint32_t *off, int *tupleid, int *len,
9966e390feSWarner Losh 		    uint8_t *tupledata);
100255b159fSJonathan Chen static int	cardbus_read_tuple(device_t cbdev, device_t child,
10166e390feSWarner Losh 		    struct resource *res, uint32_t start, uint32_t *off,
10266e390feSWarner Losh 		    int *tupleid, int *len, uint8_t *tupledata);
10363fa9f4cSJonathan Chen static void	cardbus_read_tuple_finish(device_t cbdev, device_t child,
10463fa9f4cSJonathan Chen 		    int rid, struct resource *res);
10563fa9f4cSJonathan Chen static struct resource	*cardbus_read_tuple_init(device_t cbdev, device_t child,
10666e390feSWarner Losh 		    uint32_t *start, int *rid);
107255b159fSJonathan Chen static int	decode_tuple(device_t cbdev, device_t child, int tupleid,
10866e390feSWarner Losh 		    int len, uint8_t *tupledata, uint32_t start,
10947147ce7SWarner Losh 		    uint32_t *off, struct tuple_callbacks *callbacks,
11047147ce7SWarner Losh 		    void *);
111255b159fSJonathan Chen 
1120c95c705SJonathan Chen #define	MAKETUPLE(NAME,FUNC) { CISTPL_ ## NAME, #NAME, decode_tuple_ ## FUNC }
1130db7e66cSJonathan Chen 
1140db7e66cSJonathan Chen static char *funcnames[] = {
1150db7e66cSJonathan Chen 	"Multi-Functioned",
1160db7e66cSJonathan Chen 	"Memory",
1170db7e66cSJonathan Chen 	"Serial Port",
1180db7e66cSJonathan Chen 	"Parallel Port",
1190db7e66cSJonathan Chen 	"Fixed Disk",
1200db7e66cSJonathan Chen 	"Video Adaptor",
1210db7e66cSJonathan Chen 	"Network Adaptor",
1220db7e66cSJonathan Chen 	"AIMS",
1230db7e66cSJonathan Chen 	"SCSI",
1240db7e66cSJonathan Chen 	"Security"
1250db7e66cSJonathan Chen };
1260db7e66cSJonathan Chen 
127255b159fSJonathan Chen /*
128255b159fSJonathan Chen  * Handler functions for various CIS tuples
129255b159fSJonathan Chen  */
130255b159fSJonathan Chen 
13122acd92bSWarner Losh static int
13222acd92bSWarner Losh decode_tuple_generic(device_t cbdev, device_t child, int id,
13322acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
13447147ce7SWarner Losh     struct tuple_callbacks *info, void *argp)
1350db7e66cSJonathan Chen {
1360db7e66cSJonathan Chen 	int i;
1370db7e66cSJonathan Chen 
13822acd92bSWarner Losh 	if (cardbus_cis_debug) {
1390db7e66cSJonathan Chen 		if (info)
1400db7e66cSJonathan Chen 			printf("TUPLE: %s [%d]:", info->name, len);
1410db7e66cSJonathan Chen 		else
1420db7e66cSJonathan Chen 			printf("TUPLE: Unknown(0x%02x) [%d]:", id, len);
1430db7e66cSJonathan Chen 
1440db7e66cSJonathan Chen 		for (i = 0; i < len; i++) {
1450db7e66cSJonathan Chen 			if (i % 0x10 == 0 && len > 0x10)
1460db7e66cSJonathan Chen 				printf("\n       0x%02x:", i);
1477bec1dd5SJonathan Chen 			printf(" %02x", tupledata[i]);
1480db7e66cSJonathan Chen 		}
1490db7e66cSJonathan Chen 		printf("\n");
15022acd92bSWarner Losh 	}
151a3133b58SWarner Losh 	return (0);
1520db7e66cSJonathan Chen }
1530db7e66cSJonathan Chen 
15422acd92bSWarner Losh static int
15522acd92bSWarner Losh decode_tuple_linktarget(device_t cbdev, device_t child, int id,
15622acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
15747147ce7SWarner Losh     struct tuple_callbacks *info, void *argp)
1580db7e66cSJonathan Chen {
15949f158ccSJonathan Chen 	int i;
16049f158ccSJonathan Chen 
16122acd92bSWarner Losh 	if (cardbus_cis_debug) {
16249f158ccSJonathan Chen 		printf("TUPLE: %s [%d]:", info->name, len);
16349f158ccSJonathan Chen 
16449f158ccSJonathan Chen 		for (i = 0; i < len; i++) {
16549f158ccSJonathan Chen 			if (i % 0x10 == 0 && len > 0x10)
16649f158ccSJonathan Chen 				printf("\n       0x%02x:", i);
16749f158ccSJonathan Chen 			printf(" %02x", tupledata[i]);
16849f158ccSJonathan Chen 		}
16949f158ccSJonathan Chen 		printf("\n");
17022acd92bSWarner Losh 	}
1717bec1dd5SJonathan Chen 	if (len != 3 || tupledata[0] != 'C' || tupledata[1] != 'I' ||
1727bec1dd5SJonathan Chen 	    tupledata[2] != 'S') {
1730db7e66cSJonathan Chen 		printf("Invalid data for CIS Link Target!\n");
174255b159fSJonathan Chen 		decode_tuple_generic(cbdev, child, id, len, tupledata,
17547147ce7SWarner Losh 		    start, off, info, argp);
176a3133b58SWarner Losh 		return (EINVAL);
1770db7e66cSJonathan Chen 	}
178a3133b58SWarner Losh 	return (0);
1790db7e66cSJonathan Chen }
1800db7e66cSJonathan Chen 
18122acd92bSWarner Losh static int
18222acd92bSWarner Losh decode_tuple_vers_1(device_t cbdev, device_t child, int id,
18322acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
18447147ce7SWarner Losh     struct tuple_callbacks *info, void *argp)
1850db7e66cSJonathan Chen {
1860db7e66cSJonathan Chen 	int i;
187fbe9cff1SWarner Losh 
18822acd92bSWarner Losh 	if (cardbus_cis_debug) {
1897bec1dd5SJonathan Chen 		printf("Product version: %d.%d\n", tupledata[0], tupledata[1]);
1900db7e66cSJonathan Chen 		printf("Product name: ");
1910db7e66cSJonathan Chen 		for (i = 2; i < len; i++) {
1927bec1dd5SJonathan Chen 			if (tupledata[i] == '\0')
1930db7e66cSJonathan Chen 				printf(" | ");
1947bec1dd5SJonathan Chen 			else if (tupledata[i] == 0xff)
1950db7e66cSJonathan Chen 				break;
1960db7e66cSJonathan Chen 			else
1977bec1dd5SJonathan Chen 				printf("%c", tupledata[i]);
1980db7e66cSJonathan Chen 		}
1990db7e66cSJonathan Chen 		printf("\n");
20022acd92bSWarner Losh 	}
201a3133b58SWarner Losh 	return (0);
2020db7e66cSJonathan Chen }
2030db7e66cSJonathan Chen 
20422acd92bSWarner Losh static int
20522acd92bSWarner Losh decode_tuple_funcid(device_t cbdev, device_t child, int id,
20622acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
20747147ce7SWarner Losh     struct tuple_callbacks *info, void *argp)
2080db7e66cSJonathan Chen {
209fbe9cff1SWarner Losh 	struct cardbus_devinfo *dinfo = device_get_ivars(child);
21073a1170aSPedro F. Giffuni 	int numnames = nitems(funcnames);
211fbe9cff1SWarner Losh 	int i;
2120db7e66cSJonathan Chen 
21322acd92bSWarner Losh 	if (cardbus_cis_debug) {
2140db7e66cSJonathan Chen 		printf("Functions: ");
2150db7e66cSJonathan Chen 		for (i = 0; i < len; i++) {
2167bec1dd5SJonathan Chen 			if (tupledata[i] < numnames)
2177bec1dd5SJonathan Chen 				printf("%s", funcnames[tupledata[i]]);
2180db7e66cSJonathan Chen 			else
2197bec1dd5SJonathan Chen 				printf("Unknown(%d)", tupledata[i]);
220255b159fSJonathan Chen 			if (i < len - 1)
221255b159fSJonathan Chen 				printf(", ");
2220db7e66cSJonathan Chen 		}
22322acd92bSWarner Losh 		printf("\n");
22422acd92bSWarner Losh 	}
225fbe9cff1SWarner Losh 	if (len > 0)
226fbe9cff1SWarner Losh 		dinfo->funcid = tupledata[0];		/* use first in list */
227a3133b58SWarner Losh 	return (0);
2280db7e66cSJonathan Chen }
2290db7e66cSJonathan Chen 
23022acd92bSWarner Losh static int
23122acd92bSWarner Losh decode_tuple_manfid(device_t cbdev, device_t child, int id,
23222acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
23347147ce7SWarner Losh     struct tuple_callbacks *info, void *argp)
2340db7e66cSJonathan Chen {
235fbe9cff1SWarner Losh 	struct cardbus_devinfo *dinfo = device_get_ivars(child);
2360db7e66cSJonathan Chen 	int i;
237fbe9cff1SWarner Losh 
23822acd92bSWarner Losh 	if (cardbus_cis_debug) {
2390db7e66cSJonathan Chen 		printf("Manufacturer ID: ");
2400db7e66cSJonathan Chen 		for (i = 0; i < len; i++)
2417bec1dd5SJonathan Chen 			printf("%02x", tupledata[i]);
2420db7e66cSJonathan Chen 		printf("\n");
24322acd92bSWarner Losh 	}
244fbe9cff1SWarner Losh 
245fbe9cff1SWarner Losh 	if (len == 5) {
246fbe9cff1SWarner Losh 		dinfo->mfrid = tupledata[1] | (tupledata[2] << 8);
247fbe9cff1SWarner Losh 		dinfo->prodid = tupledata[3] | (tupledata[4] << 8);
248fbe9cff1SWarner Losh 	}
249a3133b58SWarner Losh 	return (0);
2500db7e66cSJonathan Chen }
2510db7e66cSJonathan Chen 
25222acd92bSWarner Losh static int
25322acd92bSWarner Losh decode_tuple_funce(device_t cbdev, device_t child, int id,
25422acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
25547147ce7SWarner Losh     struct tuple_callbacks *info, void *argp)
2560db7e66cSJonathan Chen {
257fbe9cff1SWarner Losh 	struct cardbus_devinfo *dinfo = device_get_ivars(child);
258fbe9cff1SWarner Losh 	int type, i;
259fbe9cff1SWarner Losh 
26022acd92bSWarner Losh 	if (cardbus_cis_debug) {
2610db7e66cSJonathan Chen 		printf("Function Extension: ");
2620db7e66cSJonathan Chen 		for (i = 0; i < len; i++)
2637bec1dd5SJonathan Chen 			printf("%02x", tupledata[i]);
2640db7e66cSJonathan Chen 		printf("\n");
26522acd92bSWarner Losh 	}
266fbe9cff1SWarner Losh 	if (len < 2)			/* too short */
267fbe9cff1SWarner Losh 		return (0);
268fbe9cff1SWarner Losh 	type = tupledata[0];		/* XXX <32 always? */
269fbe9cff1SWarner Losh 	switch (dinfo->funcid) {
270440b5adeSWarner Losh 	case PCCARD_FUNCTION_NETWORK:
271fbe9cff1SWarner Losh 		switch (type) {
272440b5adeSWarner Losh 		case PCCARD_TPLFE_TYPE_LAN_NID:
27322acd92bSWarner Losh 			if (tupledata[1] > sizeof(dinfo->funce.lan.nid)) {
27422acd92bSWarner Losh 				/* ignore, warning? */
27522acd92bSWarner Losh 				return (0);
27622acd92bSWarner Losh 			}
27722acd92bSWarner Losh 			bcopy(tupledata + 2, dinfo->funce.lan.nid,
27822acd92bSWarner Losh 			    tupledata[1]);
279fbe9cff1SWarner Losh 			break;
280fbe9cff1SWarner Losh 		}
281fbe9cff1SWarner Losh 		dinfo->fepresent |= 1<<type;
282fbe9cff1SWarner Losh 		break;
283fbe9cff1SWarner Losh 	}
284a3133b58SWarner Losh 	return (0);
2850db7e66cSJonathan Chen }
2860db7e66cSJonathan Chen 
28722acd92bSWarner Losh static int
28822acd92bSWarner Losh decode_tuple_bar(device_t cbdev, device_t child, int id,
28922acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
29047147ce7SWarner Losh     struct tuple_callbacks *info, void *argp)
2910db7e66cSJonathan Chen {
29263fa9f4cSJonathan Chen 	struct cardbus_devinfo *dinfo = device_get_ivars(child);
2930db7e66cSJonathan Chen 	int type;
2941e962d00SScott Long 	uint8_t reg;
295bee89c73SWarner Losh 	uint32_t bar;
2960db7e66cSJonathan Chen 
297e6e272b9SScott Long 	if (len != 6) {
2981e962d00SScott Long 		device_printf(cbdev, "CIS BAR length not 6 (%d)\n", len);
299e6e272b9SScott Long 		return (EINVAL);
300e6e272b9SScott Long 	}
3011e962d00SScott Long 
3021e962d00SScott Long 	reg = *tupledata;
3031e962d00SScott Long 	len = le32toh(*(uint32_t*)(tupledata + 2));
304c732cf3bSWarner Losh 	if (reg & TPL_BAR_REG_AS)
3050db7e66cSJonathan Chen 		type = SYS_RES_IOPORT;
306c732cf3bSWarner Losh 	else
3070db7e66cSJonathan Chen 		type = SYS_RES_MEMORY;
3081e962d00SScott Long 
3091e962d00SScott Long 	bar = reg & TPL_BAR_REG_ASI_MASK;
3101e962d00SScott Long 	if (bar == 0) {
3111e962d00SScott Long 		device_printf(cbdev, "Invalid BAR type 0 in CIS\n");
3121e962d00SScott Long 		return (EINVAL);	/* XXX Return an error? */
3131e962d00SScott Long 	} else if (bar == 7) {
3141e962d00SScott Long 		/* XXX Should we try to map in Option ROMs? */
315a3133b58SWarner Losh 		return (0);
3160db7e66cSJonathan Chen 	}
3171e962d00SScott Long 
31872d3502eSScott Long 	/* Convert from BAR type to BAR offset */
319495036f2SWarner Losh 	bar = PCIR_BAR(bar - 1);
3201e962d00SScott Long 
32163fa9f4cSJonathan Chen 	if (type == SYS_RES_MEMORY) {
32272d3502eSScott Long 		if (reg & TPL_BAR_REG_PREFETCHABLE)
323f3d3468dSWarner Losh 			dinfo->mprefetchable |= (1 << PCI_RID2BAR(bar));
324a7c43559SWarner Losh 		/*
325e371fa45SWarner Losh 		 * The PC Card spec says we're only supposed to honor this
326e371fa45SWarner Losh 		 * hint when the cardbus bridge is a child of pci0 (the main
327e371fa45SWarner Losh 		 * bus).  The PC Card spec seems to indicate that this should
328e371fa45SWarner Losh 		 * only be done on x86 based machines, which suggests that on
3290d439b5fSJohn Baldwin 		 * non-x86 machines the addresses can be anywhere.  Since the
330e371fa45SWarner Losh 		 * hardware can do it on non-x86 machines, it should be able
331e371fa45SWarner Losh 		 * to do it on x86 machines too.  Therefore, we can and should
332e371fa45SWarner Losh 		 * ignore this hint.  Furthermore, the PC Card spec recommends
333e371fa45SWarner Losh 		 * always allocating memory above 1MB, contradicting the other
334e371fa45SWarner Losh 		 * part of the PC Card spec, it seems.  We make note of it,
335e371fa45SWarner Losh 		 * but otherwise don't use this information.
336a7c43559SWarner Losh 		 *
337e371fa45SWarner Losh 		 * Some Realtek cards have this set in their CIS, but fail
338e371fa45SWarner Losh 		 * to actually work when mapped this way, and experience
339e371fa45SWarner Losh 		 * has shown ignoring this big to be a wise choice.
340e371fa45SWarner Losh 		 *
341e371fa45SWarner Losh 		 * XXX We should cite chapter and verse for standard refs.
342a7c43559SWarner Losh 		 */
34372d3502eSScott Long 		if (reg & TPL_BAR_REG_BELOW1MB)
344f3d3468dSWarner Losh 			dinfo->mbelow1mb |= (1 << PCI_RID2BAR(bar));
3450db7e66cSJonathan Chen 	}
3461e962d00SScott Long 
347a3133b58SWarner Losh 	return (0);
3480db7e66cSJonathan Chen }
3490db7e66cSJonathan Chen 
35022acd92bSWarner Losh static int
35122acd92bSWarner Losh decode_tuple_unhandled(device_t cbdev, device_t child, int id,
35222acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
35347147ce7SWarner Losh     struct tuple_callbacks *info, void *argp)
3540db7e66cSJonathan Chen {
35522acd92bSWarner Losh 	/* Make this message suck less XXX */
3560db7e66cSJonathan Chen 	printf("TUPLE: %s [%d] is unhandled! Bailing...", info->name, len);
35747147ce7SWarner Losh 	return (EINVAL);
3580db7e66cSJonathan Chen }
3590db7e66cSJonathan Chen 
36022acd92bSWarner Losh static int
36122acd92bSWarner Losh decode_tuple_end(device_t cbdev, device_t child, int id,
36222acd92bSWarner Losh     int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
36347147ce7SWarner Losh     struct tuple_callbacks *info, void *argp)
3640db7e66cSJonathan Chen {
365509cfe6fSWarner Losh 	if (cardbus_cis_debug)
3660c95c705SJonathan Chen 		printf("CIS reading done\n");
367a3133b58SWarner Losh 	return (0);
3680db7e66cSJonathan Chen }
3690db7e66cSJonathan Chen 
370255b159fSJonathan Chen /*
371255b159fSJonathan Chen  * Functions to read the a tuple from the card
372255b159fSJonathan Chen  */
373255b159fSJonathan Chen 
37440895595SWarner Losh /*
37540895595SWarner Losh  * Read CIS bytes out of the config space.  We have to read it 4 bytes at a
37640895595SWarner Losh  * time and do the usual mask and shift to return the bytes.  The standard
37740895595SWarner Losh  * defines the byte order to be little endian.  pci_read_config converts it to
37840895595SWarner Losh  * host byte order.  This is why we have no endian conversion functions: the
37940895595SWarner Losh  * shifts wind up being endian neutral.  This is also why we avoid the obvious
38040895595SWarner Losh  * memcpy optimization.
38140895595SWarner Losh  */
3820db7e66cSJonathan Chen static int
38366e390feSWarner Losh cardbus_read_tuple_conf(device_t cbdev, device_t child, uint32_t start,
38466e390feSWarner Losh     uint32_t *off, int *tupleid, int *len, uint8_t *tupledata)
3857bec1dd5SJonathan Chen {
3867bec1dd5SJonathan Chen 	int i, j;
38766e390feSWarner Losh 	uint32_t e;
38866e390feSWarner Losh 	uint32_t loc;
3897bec1dd5SJonathan Chen 
39063fa9f4cSJonathan Chen 	loc = start + *off;
39149f158ccSJonathan Chen 
39240895595SWarner Losh 	e = pci_read_config(child, loc & ~0x3, 4);
39340895595SWarner Losh 	e >>= 8 * (loc & 0x3);
3947bec1dd5SJonathan Chen 	*len = 0;
39549f158ccSJonathan Chen 	for (i = loc, j = -2; j < *len; j++, i++) {
39640895595SWarner Losh 		if ((i & 0x3) == 0)
3977bec1dd5SJonathan Chen 			e = pci_read_config(child, i, 4);
3987bec1dd5SJonathan Chen 		if (j == -2)
3997bec1dd5SJonathan Chen 			*tupleid = 0xff & e;
4007bec1dd5SJonathan Chen 		else if (j == -1)
4017bec1dd5SJonathan Chen 			*len = 0xff & e;
4027bec1dd5SJonathan Chen 		else
4037bec1dd5SJonathan Chen 			tupledata[j] = 0xff & e;
4047bec1dd5SJonathan Chen 		e >>= 8;
4057bec1dd5SJonathan Chen 	}
4067bec1dd5SJonathan Chen 	*off += *len + 2;
407a3133b58SWarner Losh 	return (0);
4087bec1dd5SJonathan Chen }
4097bec1dd5SJonathan Chen 
41040895595SWarner Losh /*
411453130d9SPedro F. Giffuni  * Read the CIS data out of memory.  We indirect through the bus space
41240895595SWarner Losh  * routines to ensure proper byte ordering conversions when necessary.
41340895595SWarner Losh  */
4147bec1dd5SJonathan Chen static int
41566e390feSWarner Losh cardbus_read_tuple_mem(device_t cbdev, struct resource *res, uint32_t start,
41666e390feSWarner Losh     uint32_t *off, int *tupleid, int *len, uint8_t *tupledata)
4170db7e66cSJonathan Chen {
41863fa9f4cSJonathan Chen 	int ret;
4190db7e66cSJonathan Chen 
420f26d7f8eSJohn Baldwin 	*tupleid = bus_read_1(res, start + *off);
421f26d7f8eSJohn Baldwin 	*len = bus_read_1(res, start + *off + 1);
422f26d7f8eSJohn Baldwin 	bus_read_region_1(res, *off + start + 2, tupledata, *len);
42363fa9f4cSJonathan Chen 	ret = 0;
42463fa9f4cSJonathan Chen 	*off += *len + 2;
425a3133b58SWarner Losh 	return (ret);
4260db7e66cSJonathan Chen }
42763fa9f4cSJonathan Chen 
42863fa9f4cSJonathan Chen static int
42963fa9f4cSJonathan Chen cardbus_read_tuple(device_t cbdev, device_t child, struct resource *res,
43066e390feSWarner Losh     uint32_t start, uint32_t *off, int *tupleid, int *len,
43166e390feSWarner Losh     uint8_t *tupledata)
43263fa9f4cSJonathan Chen {
433bee89c73SWarner Losh 	if (res == CIS_CONFIG_SPACE)
434a3133b58SWarner Losh 		return (cardbus_read_tuple_conf(cbdev, child, start, off,
435a3133b58SWarner Losh 		    tupleid, len, tupledata));
436bee89c73SWarner Losh 	return (cardbus_read_tuple_mem(cbdev, res, start, off, tupleid, len,
437bee89c73SWarner Losh 	    tupledata));
43863fa9f4cSJonathan Chen }
43963fa9f4cSJonathan Chen 
44063fa9f4cSJonathan Chen static void
44163fa9f4cSJonathan Chen cardbus_read_tuple_finish(device_t cbdev, device_t child, int rid,
44263fa9f4cSJonathan Chen     struct resource *res)
44363fa9f4cSJonathan Chen {
444c732cf3bSWarner Losh 	if (res != CIS_CONFIG_SPACE) {
445bee89c73SWarner Losh 		bus_release_resource(child, SYS_RES_MEMORY, rid, res);
44606c9ee05SJohn Baldwin 		bus_delete_resource(child, SYS_RES_MEMORY, rid);
44763fa9f4cSJonathan Chen 	}
44863fa9f4cSJonathan Chen }
44963fa9f4cSJonathan Chen 
45063fa9f4cSJonathan Chen static struct resource *
45166e390feSWarner Losh cardbus_read_tuple_init(device_t cbdev, device_t child, uint32_t *start,
45263fa9f4cSJonathan Chen     int *rid)
45363fa9f4cSJonathan Chen {
45463fa9f4cSJonathan Chen 	struct resource *res;
455495036f2SWarner Losh 	uint32_t space;
45663fa9f4cSJonathan Chen 
45741ac33a2SWarner Losh 	space = *start & PCIM_CIS_ASI_MASK;
458495036f2SWarner Losh 	switch (space) {
4594b333740SWarner Losh 	case PCIM_CIS_ASI_CONFIG:
460e6d3b1bdSWarner Losh 		DEVPRINTF((cbdev, "CIS in PCI config space\n"));
461e6e272b9SScott Long 		/* CIS in PCI config space need no initialization */
462c732cf3bSWarner Losh 		return (CIS_CONFIG_SPACE);
46341ac33a2SWarner Losh 	case PCIM_CIS_ASI_BAR0:
46441ac33a2SWarner Losh 	case PCIM_CIS_ASI_BAR1:
46541ac33a2SWarner Losh 	case PCIM_CIS_ASI_BAR2:
46641ac33a2SWarner Losh 	case PCIM_CIS_ASI_BAR3:
46741ac33a2SWarner Losh 	case PCIM_CIS_ASI_BAR4:
46841ac33a2SWarner Losh 	case PCIM_CIS_ASI_BAR5:
46941ac33a2SWarner Losh 		*rid = PCIR_BAR(space - PCIM_CIS_ASI_BAR0);
470e6d3b1bdSWarner Losh 		DEVPRINTF((cbdev, "CIS in BAR %#x\n", *rid));
47163fa9f4cSJonathan Chen 		break;
47241ac33a2SWarner Losh 	case PCIM_CIS_ASI_ROM:
47341ac33a2SWarner Losh 		*rid = PCIR_BIOS;
474e6d3b1bdSWarner Losh 		DEVPRINTF((cbdev, "CIS in option rom\n"));
47563fa9f4cSJonathan Chen 		break;
47663fa9f4cSJonathan Chen 	default:
47763fa9f4cSJonathan Chen 		device_printf(cbdev, "Unable to read CIS: Unknown space: %d\n",
478495036f2SWarner Losh 		    space);
479a3133b58SWarner Losh 		return (NULL);
48063fa9f4cSJonathan Chen 	}
48163fa9f4cSJonathan Chen 
48263fa9f4cSJonathan Chen 	/* allocate the memory space to read CIS */
4830d439b5fSJohn Baldwin 	res = bus_alloc_resource_any(child, SYS_RES_MEMORY, rid,
48447147ce7SWarner Losh 	    rman_make_alignment_flags(4096) | RF_ACTIVE);
48563fa9f4cSJonathan Chen 	if (res == NULL) {
48663fa9f4cSJonathan Chen 		device_printf(cbdev, "Unable to allocate resource "
48763fa9f4cSJonathan Chen 		    "to read CIS.\n");
488a3133b58SWarner Losh 		return (NULL);
48963fa9f4cSJonathan Chen 	}
490da1b038aSJustin Hibbits 	DEVPRINTF((cbdev, "CIS Mapped to %#jx\n",
491da1b038aSJustin Hibbits 	    rman_get_start(res)));
49263fa9f4cSJonathan Chen 
49363fa9f4cSJonathan Chen 	/* Flip to the right ROM image if CIS is in ROM */
49441ac33a2SWarner Losh 	if (space == PCIM_CIS_ASI_ROM) {
49566e390feSWarner Losh 		uint32_t imagesize;
49666e390feSWarner Losh 		uint32_t imagebase = 0;
49766e390feSWarner Losh 		uint32_t pcidata;
49866e390feSWarner Losh 		uint16_t romsig;
49963fa9f4cSJonathan Chen 		int romnum = 0;
500e6e272b9SScott Long 		int imagenum;
50163fa9f4cSJonathan Chen 
50241ac33a2SWarner Losh 		imagenum = (*start & PCIM_CIS_ROM_MASK) >> 28;
50363fa9f4cSJonathan Chen 		for (romnum = 0;; romnum++) {
504f26d7f8eSJohn Baldwin 			romsig = bus_read_2(res,
505e6e272b9SScott Long 			    imagebase + CARDBUS_EXROM_SIGNATURE);
506e6e272b9SScott Long 			if (romsig != 0xaa55) {
50763fa9f4cSJonathan Chen 				device_printf(cbdev, "Bad header in rom %d: "
508e6e272b9SScott Long 				    "[%x] %04x\n", romnum, imagebase +
509e6e272b9SScott Long 				    CARDBUS_EXROM_SIGNATURE, romsig);
510767dff0aSJohn Baldwin 				cardbus_read_tuple_finish(cbdev, child, *rid,
511767dff0aSJohn Baldwin 				    res);
51263fa9f4cSJonathan Chen 				*rid = 0;
513a3133b58SWarner Losh 				return (NULL);
51463fa9f4cSJonathan Chen 			}
515e6e272b9SScott Long 
516e6e272b9SScott Long 			/*
517e6e272b9SScott Long 			 * If this was the Option ROM image that we were
518e6e272b9SScott Long 			 * looking for, then we are done.
519e6e272b9SScott Long 			 */
520e6e272b9SScott Long 			if (romnum == imagenum)
521e6e272b9SScott Long 				break;
522e6e272b9SScott Long 
523e6e272b9SScott Long 			/* Find out where the next Option ROM image is */
524f26d7f8eSJohn Baldwin 			pcidata = imagebase + bus_read_2(res,
525e6e272b9SScott Long 			    imagebase + CARDBUS_EXROM_DATA_PTR);
526f26d7f8eSJohn Baldwin 			imagesize = bus_read_2(res,
527e6e272b9SScott Long 			    pcidata + CARDBUS_EXROM_DATA_IMAGE_LENGTH);
5280db7e66cSJonathan Chen 
5297bec1dd5SJonathan Chen 			if (imagesize == 0) {
5300db7e66cSJonathan Chen 				/*
5310db7e66cSJonathan Chen 				 * XXX some ROMs seem to have this as zero,
5320db7e66cSJonathan Chen 				 * can we assume this means 1 block?
5330db7e66cSJonathan Chen 				 */
534e6e272b9SScott Long 				device_printf(cbdev, "Warning, size of Option "
535e6e272b9SScott Long 				    "ROM image %d is 0 bytes, assuming 512 "
536e6e272b9SScott Long 				    "bytes.\n", romnum);
5370db7e66cSJonathan Chen 				imagesize = 1;
5387bec1dd5SJonathan Chen 			}
539e6e272b9SScott Long 
540e6e272b9SScott Long 			/* Image size is in 512 byte units */
5410db7e66cSJonathan Chen 			imagesize <<= 9;
5420db7e66cSJonathan Chen 
543f26d7f8eSJohn Baldwin 			if ((bus_read_1(res, pcidata +
5441e06ae99SScott Long 			    CARDBUS_EXROM_DATA_INDICATOR) & 0x80) != 0) {
545e6e272b9SScott Long 				device_printf(cbdev, "Cannot find CIS in "
546e6e272b9SScott Long 				    "Option ROM\n");
5473a6b4b04SJohn Baldwin 				cardbus_read_tuple_finish(cbdev, child, *rid,
5483a6b4b04SJohn Baldwin 				    res);
549e6e272b9SScott Long 				*rid = 0;
550a3133b58SWarner Losh 				return (NULL);
5510db7e66cSJonathan Chen 			}
552e6e272b9SScott Long 			imagebase += imagesize;
5530db7e66cSJonathan Chen 		}
55441ac33a2SWarner Losh 		*start = imagebase + (*start & PCIM_CIS_ADDR_MASK);
5550db7e66cSJonathan Chen 	} else {
55641ac33a2SWarner Losh 		*start = *start & PCIM_CIS_ADDR_MASK;
5570db7e66cSJonathan Chen 	}
558e6d3b1bdSWarner Losh 	DEVPRINTF((cbdev, "CIS offset is %#x\n", *start));
559e6e272b9SScott Long 
560a3133b58SWarner Losh 	return (res);
5617bec1dd5SJonathan Chen }
5627bec1dd5SJonathan Chen 
563255b159fSJonathan Chen /*
564255b159fSJonathan Chen  * Dispatch the right handler function per tuple
565255b159fSJonathan Chen  */
566255b159fSJonathan Chen 
5677bec1dd5SJonathan Chen static int
568255b159fSJonathan Chen decode_tuple(device_t cbdev, device_t child, int tupleid, int len,
56966e390feSWarner Losh     uint8_t *tupledata, uint32_t start, uint32_t *off,
57047147ce7SWarner Losh     struct tuple_callbacks *callbacks, void *argp)
5717bec1dd5SJonathan Chen {
5727bec1dd5SJonathan Chen 	int i;
5730c95c705SJonathan Chen 	for (i = 0; callbacks[i].id != CISTPL_GENERIC; i++) {
5740c95c705SJonathan Chen 		if (tupleid == callbacks[i].id)
575a3133b58SWarner Losh 			return (callbacks[i].func(cbdev, child, tupleid, len,
57647147ce7SWarner Losh 			    tupledata, start, off, &callbacks[i], argp));
5777bec1dd5SJonathan Chen 	}
578a3133b58SWarner Losh 	return (callbacks[i].func(cbdev, child, tupleid, len,
57947147ce7SWarner Losh 	    tupledata, start, off, NULL, argp));
5800db7e66cSJonathan Chen }
5810db7e66cSJonathan Chen 
58247147ce7SWarner Losh int
583255b159fSJonathan Chen cardbus_parse_cis(device_t cbdev, device_t child,
58447147ce7SWarner Losh     struct tuple_callbacks *callbacks, void *argp)
5850db7e66cSJonathan Chen {
586*d6f64df9SWarner Losh 	uint8_t *tupledata;
58717ee700bSWarner Losh 	int tupleid = CISTPL_NULL;
5887bec1dd5SJonathan Chen 	int len;
5897bec1dd5SJonathan Chen 	int expect_linktarget;
59066e390feSWarner Losh 	uint32_t start, off;
59163fa9f4cSJonathan Chen 	struct resource *res;
59263fa9f4cSJonathan Chen 	int rid;
5930db7e66cSJonathan Chen 
594*d6f64df9SWarner Losh 	tupledata = malloc(MAXTUPLESIZE, M_DEVBUF, M_WAITOK | M_ZERO);
5957bec1dd5SJonathan Chen 	expect_linktarget = TRUE;
59641ac33a2SWarner Losh 	if ((start = pci_read_config(child, PCIR_CIS, 4)) == 0) {
597e6d3b1bdSWarner Losh 		DEVPRINTF((cbdev, "Warning: CIS pointer is 0: (no CIS)\n"));
598*d6f64df9SWarner Losh 		free(tupledata, M_DEVBUF);
59940895595SWarner Losh 		return (0);
600509cfe6fSWarner Losh 	}
601e6d3b1bdSWarner Losh 	DEVPRINTF((cbdev, "CIS pointer is %#x\n", start));
60249f158ccSJonathan Chen 	off = 0;
60363fa9f4cSJonathan Chen 	res = cardbus_read_tuple_init(cbdev, child, &start, &rid);
604509cfe6fSWarner Losh 	if (res == NULL) {
605509cfe6fSWarner Losh 		device_printf(cbdev, "Unable to allocate resources for CIS\n");
606*d6f64df9SWarner Losh 		free(tupledata, M_DEVBUF);
607a3133b58SWarner Losh 		return (ENXIO);
608509cfe6fSWarner Losh 	}
6091e962d00SScott Long 
6107bec1dd5SJonathan Chen 	do {
61147147ce7SWarner Losh 		if (cardbus_read_tuple(cbdev, child, res, start, &off,
61247147ce7SWarner Losh 		    &tupleid, &len, tupledata) != 0) {
61363fa9f4cSJonathan Chen 			device_printf(cbdev, "Failed to read CIS.\n");
61463fa9f4cSJonathan Chen 			cardbus_read_tuple_finish(cbdev, child, rid, res);
615*d6f64df9SWarner Losh 			free(tupledata, M_DEVBUF);
616a3133b58SWarner Losh 			return (ENXIO);
61763fa9f4cSJonathan Chen 		}
6187bec1dd5SJonathan Chen 
6197bec1dd5SJonathan Chen 		if (expect_linktarget && tupleid != CISTPL_LINKTARGET) {
620255b159fSJonathan Chen 			device_printf(cbdev, "Expecting link target, got 0x%x\n",
6217bec1dd5SJonathan Chen 			    tupleid);
62263fa9f4cSJonathan Chen 			cardbus_read_tuple_finish(cbdev, child, rid, res);
623*d6f64df9SWarner Losh 			free(tupledata, M_DEVBUF);
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);
632*d6f64df9SWarner Losh 			free(tupledata, M_DEVBUF);
633a3133b58SWarner Losh 			return (expect_linktarget);
63463fa9f4cSJonathan Chen 		}
6357bec1dd5SJonathan Chen 	} while (tupleid != CISTPL_END);
63663fa9f4cSJonathan Chen 	cardbus_read_tuple_finish(cbdev, child, rid, res);
637*d6f64df9SWarner Losh 	free(tupledata, M_DEVBUF);
638a3133b58SWarner Losh 	return (0);
6390db7e66cSJonathan Chen }
6400db7e66cSJonathan Chen 
6410c95c705SJonathan Chen int
642255b159fSJonathan Chen cardbus_do_cis(device_t cbdev, device_t child)
6430c95c705SJonathan Chen {
6440c95c705SJonathan Chen 	struct tuple_callbacks init_callbacks[] = {
645a294cdb6SWarner Losh 		MAKETUPLE(LONGLINK_CB,		unhandled),
6460c95c705SJonathan Chen 		MAKETUPLE(INDIRECT,		unhandled),
6470c95c705SJonathan Chen 		MAKETUPLE(LONGLINK_MFC,		unhandled),
6480c95c705SJonathan Chen 		MAKETUPLE(BAR,			bar),
6490c95c705SJonathan Chen 		MAKETUPLE(LONGLINK_A,		unhandled),
6500c95c705SJonathan Chen 		MAKETUPLE(LONGLINK_C,		unhandled),
6510c95c705SJonathan Chen 		MAKETUPLE(LINKTARGET,		linktarget),
6520c95c705SJonathan Chen 		MAKETUPLE(VERS_1,		vers_1),
6530c95c705SJonathan Chen 		MAKETUPLE(MANFID,		manfid),
6540c95c705SJonathan Chen 		MAKETUPLE(FUNCID,		funcid),
6550c95c705SJonathan Chen 		MAKETUPLE(FUNCE,		funce),
6560c95c705SJonathan Chen 		MAKETUPLE(END,			end),
6570c95c705SJonathan Chen 		MAKETUPLE(GENERIC,		generic),
6580c95c705SJonathan Chen 	};
65963fa9f4cSJonathan Chen 
6601acd1e20SWarner Losh 	return (cardbus_parse_cis(cbdev, child, init_callbacks, NULL));
6610c95c705SJonathan Chen }
662