1098ca2bdSWarner Losh /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni *
4f86e6000SWarner Losh * Copyright (c) 2000,2001 Jonathan Chen All rights reserved.
5f86e6000SWarner Losh * Copyright (c) 2005-2008 M. Warner Losh <imp@FreeBSD.org>
6f86e6000SWarner Losh *
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>
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 *);
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
decode_tuple_generic(device_t cbdev,device_t child,int id,int len,uint8_t * tupledata,uint32_t start,uint32_t * off,struct tuple_callbacks * info,void * argp)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
decode_tuple_linktarget(device_t cbdev,device_t child,int id,int len,uint8_t * tupledata,uint32_t start,uint32_t * off,struct tuple_callbacks * info,void * argp)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
decode_tuple_vers_1(device_t cbdev,device_t child,int id,int len,uint8_t * tupledata,uint32_t start,uint32_t * off,struct tuple_callbacks * info,void * argp)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
decode_tuple_funcid(device_t cbdev,device_t child,int id,int len,uint8_t * tupledata,uint32_t start,uint32_t * off,struct tuple_callbacks * info,void * argp)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);
20773a1170aSPedro F. Giffuni int numnames = nitems(funcnames);
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
decode_tuple_manfid(device_t cbdev,device_t child,int id,int len,uint8_t * tupledata,uint32_t start,uint32_t * off,struct tuple_callbacks * info,void * argp)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
decode_tuple_funce(device_t cbdev,device_t child,int id,int len,uint8_t * tupledata,uint32_t start,uint32_t * off,struct tuple_callbacks * info,void * argp)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
decode_tuple_bar(device_t cbdev,device_t child,int id,int len,uint8_t * tupledata,uint32_t start,uint32_t * off,struct tuple_callbacks * info,void * argp)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));
321a7c43559SWarner Losh /*
322e371fa45SWarner Losh * The PC Card spec says we're only supposed to honor this
323e371fa45SWarner Losh * hint when the cardbus bridge is a child of pci0 (the main
324e371fa45SWarner Losh * bus). The PC Card spec seems to indicate that this should
325e371fa45SWarner Losh * only be done on x86 based machines, which suggests that on
3260d439b5fSJohn Baldwin * non-x86 machines the addresses can be anywhere. Since the
327e371fa45SWarner Losh * hardware can do it on non-x86 machines, it should be able
328e371fa45SWarner Losh * to do it on x86 machines too. Therefore, we can and should
329e371fa45SWarner Losh * ignore this hint. Furthermore, the PC Card spec recommends
330e371fa45SWarner Losh * always allocating memory above 1MB, contradicting the other
331e371fa45SWarner Losh * part of the PC Card spec, it seems. We make note of it,
332e371fa45SWarner Losh * but otherwise don't use this information.
333a7c43559SWarner Losh *
334e371fa45SWarner Losh * Some Realtek cards have this set in their CIS, but fail
335e371fa45SWarner Losh * to actually work when mapped this way, and experience
336e371fa45SWarner Losh * has shown ignoring this big to be a wise choice.
337e371fa45SWarner Losh *
338e371fa45SWarner Losh * XXX We should cite chapter and verse for standard refs.
339a7c43559SWarner Losh */
34072d3502eSScott Long if (reg & TPL_BAR_REG_BELOW1MB)
341f3d3468dSWarner Losh dinfo->mbelow1mb |= (1 << PCI_RID2BAR(bar));
3420db7e66cSJonathan Chen }
3431e962d00SScott Long
344a3133b58SWarner Losh return (0);
3450db7e66cSJonathan Chen }
3460db7e66cSJonathan Chen
34722acd92bSWarner Losh static int
decode_tuple_unhandled(device_t cbdev,device_t child,int id,int len,uint8_t * tupledata,uint32_t start,uint32_t * off,struct tuple_callbacks * info,void * argp)34822acd92bSWarner Losh decode_tuple_unhandled(device_t cbdev, device_t child, int id,
34922acd92bSWarner Losh int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
35047147ce7SWarner Losh struct tuple_callbacks *info, void *argp)
3510db7e66cSJonathan Chen {
35222acd92bSWarner Losh /* Make this message suck less XXX */
3530db7e66cSJonathan Chen printf("TUPLE: %s [%d] is unhandled! Bailing...", info->name, len);
35447147ce7SWarner Losh return (EINVAL);
3550db7e66cSJonathan Chen }
3560db7e66cSJonathan Chen
35722acd92bSWarner Losh static int
decode_tuple_end(device_t cbdev,device_t child,int id,int len,uint8_t * tupledata,uint32_t start,uint32_t * off,struct tuple_callbacks * info,void * argp)35822acd92bSWarner Losh decode_tuple_end(device_t cbdev, device_t child, int id,
35922acd92bSWarner Losh int len, uint8_t *tupledata, uint32_t start, uint32_t *off,
36047147ce7SWarner Losh struct tuple_callbacks *info, void *argp)
3610db7e66cSJonathan Chen {
362509cfe6fSWarner Losh if (cardbus_cis_debug)
3630c95c705SJonathan Chen printf("CIS reading done\n");
364a3133b58SWarner Losh return (0);
3650db7e66cSJonathan Chen }
3660db7e66cSJonathan Chen
367255b159fSJonathan Chen /*
368255b159fSJonathan Chen * Functions to read the a tuple from the card
369255b159fSJonathan Chen */
370255b159fSJonathan Chen
37140895595SWarner Losh /*
37240895595SWarner Losh * Read CIS bytes out of the config space. We have to read it 4 bytes at a
37340895595SWarner Losh * time and do the usual mask and shift to return the bytes. The standard
37440895595SWarner Losh * defines the byte order to be little endian. pci_read_config converts it to
37540895595SWarner Losh * host byte order. This is why we have no endian conversion functions: the
37640895595SWarner Losh * shifts wind up being endian neutral. This is also why we avoid the obvious
37740895595SWarner Losh * memcpy optimization.
37840895595SWarner Losh */
3790db7e66cSJonathan Chen static int
cardbus_read_tuple_conf(device_t cbdev,device_t child,uint32_t start,uint32_t * off,int * tupleid,int * len,uint8_t * tupledata)38066e390feSWarner Losh cardbus_read_tuple_conf(device_t cbdev, device_t child, uint32_t start,
38166e390feSWarner Losh uint32_t *off, int *tupleid, int *len, uint8_t *tupledata)
3827bec1dd5SJonathan Chen {
3837bec1dd5SJonathan Chen int i, j;
38466e390feSWarner Losh uint32_t e;
38566e390feSWarner Losh uint32_t loc;
3867bec1dd5SJonathan Chen
38763fa9f4cSJonathan Chen loc = start + *off;
38849f158ccSJonathan Chen
38940895595SWarner Losh e = pci_read_config(child, loc & ~0x3, 4);
39040895595SWarner Losh e >>= 8 * (loc & 0x3);
3917bec1dd5SJonathan Chen *len = 0;
39249f158ccSJonathan Chen for (i = loc, j = -2; j < *len; j++, i++) {
39340895595SWarner Losh if ((i & 0x3) == 0)
3947bec1dd5SJonathan Chen e = pci_read_config(child, i, 4);
3957bec1dd5SJonathan Chen if (j == -2)
3967bec1dd5SJonathan Chen *tupleid = 0xff & e;
3977bec1dd5SJonathan Chen else if (j == -1)
3987bec1dd5SJonathan Chen *len = 0xff & e;
3997bec1dd5SJonathan Chen else
4007bec1dd5SJonathan Chen tupledata[j] = 0xff & e;
4017bec1dd5SJonathan Chen e >>= 8;
4027bec1dd5SJonathan Chen }
4037bec1dd5SJonathan Chen *off += *len + 2;
404a3133b58SWarner Losh return (0);
4057bec1dd5SJonathan Chen }
4067bec1dd5SJonathan Chen
40740895595SWarner Losh /*
408453130d9SPedro F. Giffuni * Read the CIS data out of memory. We indirect through the bus space
40940895595SWarner Losh * routines to ensure proper byte ordering conversions when necessary.
41040895595SWarner Losh */
4117bec1dd5SJonathan Chen static int
cardbus_read_tuple_mem(device_t cbdev,struct resource * res,uint32_t start,uint32_t * off,int * tupleid,int * len,uint8_t * tupledata)41266e390feSWarner Losh cardbus_read_tuple_mem(device_t cbdev, struct resource *res, uint32_t start,
41366e390feSWarner Losh uint32_t *off, int *tupleid, int *len, uint8_t *tupledata)
4140db7e66cSJonathan Chen {
41563fa9f4cSJonathan Chen int ret;
4160db7e66cSJonathan Chen
417f26d7f8eSJohn Baldwin *tupleid = bus_read_1(res, start + *off);
418f26d7f8eSJohn Baldwin *len = bus_read_1(res, start + *off + 1);
419f26d7f8eSJohn Baldwin bus_read_region_1(res, *off + start + 2, tupledata, *len);
42063fa9f4cSJonathan Chen ret = 0;
42163fa9f4cSJonathan Chen *off += *len + 2;
422a3133b58SWarner Losh return (ret);
4230db7e66cSJonathan Chen }
42463fa9f4cSJonathan Chen
42563fa9f4cSJonathan Chen static int
cardbus_read_tuple(device_t cbdev,device_t child,struct resource * res,uint32_t start,uint32_t * off,int * tupleid,int * len,uint8_t * tupledata)42663fa9f4cSJonathan Chen cardbus_read_tuple(device_t cbdev, device_t child, struct resource *res,
42766e390feSWarner Losh uint32_t start, uint32_t *off, int *tupleid, int *len,
42866e390feSWarner Losh uint8_t *tupledata)
42963fa9f4cSJonathan Chen {
430bee89c73SWarner Losh if (res == CIS_CONFIG_SPACE)
431a3133b58SWarner Losh return (cardbus_read_tuple_conf(cbdev, child, start, off,
432a3133b58SWarner Losh tupleid, len, tupledata));
433bee89c73SWarner Losh return (cardbus_read_tuple_mem(cbdev, res, start, off, tupleid, len,
434bee89c73SWarner Losh tupledata));
43563fa9f4cSJonathan Chen }
43663fa9f4cSJonathan Chen
43763fa9f4cSJonathan Chen static void
cardbus_read_tuple_finish(device_t cbdev,device_t child,int rid,struct resource * res)43863fa9f4cSJonathan Chen cardbus_read_tuple_finish(device_t cbdev, device_t child, int rid,
43963fa9f4cSJonathan Chen struct resource *res)
44063fa9f4cSJonathan Chen {
441c732cf3bSWarner Losh if (res != CIS_CONFIG_SPACE) {
442bee89c73SWarner Losh bus_release_resource(child, SYS_RES_MEMORY, rid, res);
44306c9ee05SJohn Baldwin bus_delete_resource(child, SYS_RES_MEMORY, rid);
44463fa9f4cSJonathan Chen }
44563fa9f4cSJonathan Chen }
44663fa9f4cSJonathan Chen
44763fa9f4cSJonathan Chen static struct resource *
cardbus_read_tuple_init(device_t cbdev,device_t child,uint32_t * start,int * rid)44866e390feSWarner Losh cardbus_read_tuple_init(device_t cbdev, device_t child, uint32_t *start,
44963fa9f4cSJonathan Chen int *rid)
45063fa9f4cSJonathan Chen {
45163fa9f4cSJonathan Chen struct resource *res;
452495036f2SWarner Losh uint32_t space;
45363fa9f4cSJonathan Chen
45441ac33a2SWarner Losh space = *start & PCIM_CIS_ASI_MASK;
455495036f2SWarner Losh switch (space) {
4564b333740SWarner Losh case PCIM_CIS_ASI_CONFIG:
457e6d3b1bdSWarner Losh DEVPRINTF((cbdev, "CIS in PCI config space\n"));
458e6e272b9SScott Long /* CIS in PCI config space need no initialization */
459c732cf3bSWarner Losh return (CIS_CONFIG_SPACE);
46041ac33a2SWarner Losh case PCIM_CIS_ASI_BAR0:
46141ac33a2SWarner Losh case PCIM_CIS_ASI_BAR1:
46241ac33a2SWarner Losh case PCIM_CIS_ASI_BAR2:
46341ac33a2SWarner Losh case PCIM_CIS_ASI_BAR3:
46441ac33a2SWarner Losh case PCIM_CIS_ASI_BAR4:
46541ac33a2SWarner Losh case PCIM_CIS_ASI_BAR5:
46641ac33a2SWarner Losh *rid = PCIR_BAR(space - PCIM_CIS_ASI_BAR0);
467e6d3b1bdSWarner Losh DEVPRINTF((cbdev, "CIS in BAR %#x\n", *rid));
46863fa9f4cSJonathan Chen break;
46941ac33a2SWarner Losh case PCIM_CIS_ASI_ROM:
47041ac33a2SWarner Losh *rid = PCIR_BIOS;
471e6d3b1bdSWarner Losh DEVPRINTF((cbdev, "CIS in option rom\n"));
47263fa9f4cSJonathan Chen break;
47363fa9f4cSJonathan Chen default:
47463fa9f4cSJonathan Chen device_printf(cbdev, "Unable to read CIS: Unknown space: %d\n",
475495036f2SWarner Losh space);
476a3133b58SWarner Losh return (NULL);
47763fa9f4cSJonathan Chen }
47863fa9f4cSJonathan Chen
47963fa9f4cSJonathan Chen /* allocate the memory space to read CIS */
4800d439b5fSJohn Baldwin res = bus_alloc_resource_any(child, SYS_RES_MEMORY, rid,
48147147ce7SWarner Losh rman_make_alignment_flags(4096) | RF_ACTIVE);
48263fa9f4cSJonathan Chen if (res == NULL) {
48363fa9f4cSJonathan Chen device_printf(cbdev, "Unable to allocate resource "
48463fa9f4cSJonathan Chen "to read CIS.\n");
485a3133b58SWarner Losh return (NULL);
48663fa9f4cSJonathan Chen }
487da1b038aSJustin Hibbits DEVPRINTF((cbdev, "CIS Mapped to %#jx\n",
488da1b038aSJustin Hibbits rman_get_start(res)));
48963fa9f4cSJonathan Chen
49063fa9f4cSJonathan Chen /* Flip to the right ROM image if CIS is in ROM */
49141ac33a2SWarner Losh if (space == PCIM_CIS_ASI_ROM) {
49266e390feSWarner Losh uint32_t imagesize;
49366e390feSWarner Losh uint32_t imagebase = 0;
49466e390feSWarner Losh uint32_t pcidata;
49566e390feSWarner Losh uint16_t romsig;
49663fa9f4cSJonathan Chen int romnum = 0;
497e6e272b9SScott Long int imagenum;
49863fa9f4cSJonathan Chen
49941ac33a2SWarner Losh imagenum = (*start & PCIM_CIS_ROM_MASK) >> 28;
50063fa9f4cSJonathan Chen for (romnum = 0;; romnum++) {
501f26d7f8eSJohn Baldwin romsig = bus_read_2(res,
502e6e272b9SScott Long imagebase + CARDBUS_EXROM_SIGNATURE);
503e6e272b9SScott Long if (romsig != 0xaa55) {
50463fa9f4cSJonathan Chen device_printf(cbdev, "Bad header in rom %d: "
505e6e272b9SScott Long "[%x] %04x\n", romnum, imagebase +
506e6e272b9SScott Long CARDBUS_EXROM_SIGNATURE, romsig);
507767dff0aSJohn Baldwin cardbus_read_tuple_finish(cbdev, child, *rid,
508767dff0aSJohn Baldwin res);
50963fa9f4cSJonathan Chen *rid = 0;
510a3133b58SWarner Losh return (NULL);
51163fa9f4cSJonathan Chen }
512e6e272b9SScott Long
513e6e272b9SScott Long /*
514e6e272b9SScott Long * If this was the Option ROM image that we were
515e6e272b9SScott Long * looking for, then we are done.
516e6e272b9SScott Long */
517e6e272b9SScott Long if (romnum == imagenum)
518e6e272b9SScott Long break;
519e6e272b9SScott Long
520e6e272b9SScott Long /* Find out where the next Option ROM image is */
521f26d7f8eSJohn Baldwin pcidata = imagebase + bus_read_2(res,
522e6e272b9SScott Long imagebase + CARDBUS_EXROM_DATA_PTR);
523f26d7f8eSJohn Baldwin imagesize = bus_read_2(res,
524e6e272b9SScott Long pcidata + CARDBUS_EXROM_DATA_IMAGE_LENGTH);
5250db7e66cSJonathan Chen
5267bec1dd5SJonathan Chen if (imagesize == 0) {
5270db7e66cSJonathan Chen /*
5280db7e66cSJonathan Chen * XXX some ROMs seem to have this as zero,
5290db7e66cSJonathan Chen * can we assume this means 1 block?
5300db7e66cSJonathan Chen */
531e6e272b9SScott Long device_printf(cbdev, "Warning, size of Option "
532e6e272b9SScott Long "ROM image %d is 0 bytes, assuming 512 "
533e6e272b9SScott Long "bytes.\n", romnum);
5340db7e66cSJonathan Chen imagesize = 1;
5357bec1dd5SJonathan Chen }
536e6e272b9SScott Long
537e6e272b9SScott Long /* Image size is in 512 byte units */
5380db7e66cSJonathan Chen imagesize <<= 9;
5390db7e66cSJonathan Chen
540f26d7f8eSJohn Baldwin if ((bus_read_1(res, pcidata +
5411e06ae99SScott Long CARDBUS_EXROM_DATA_INDICATOR) & 0x80) != 0) {
542e6e272b9SScott Long device_printf(cbdev, "Cannot find CIS in "
543e6e272b9SScott Long "Option ROM\n");
5443a6b4b04SJohn Baldwin cardbus_read_tuple_finish(cbdev, child, *rid,
5453a6b4b04SJohn Baldwin res);
546e6e272b9SScott Long *rid = 0;
547a3133b58SWarner Losh return (NULL);
5480db7e66cSJonathan Chen }
549e6e272b9SScott Long imagebase += imagesize;
5500db7e66cSJonathan Chen }
55141ac33a2SWarner Losh *start = imagebase + (*start & PCIM_CIS_ADDR_MASK);
5520db7e66cSJonathan Chen } else {
55341ac33a2SWarner Losh *start = *start & PCIM_CIS_ADDR_MASK;
5540db7e66cSJonathan Chen }
555e6d3b1bdSWarner Losh DEVPRINTF((cbdev, "CIS offset is %#x\n", *start));
556e6e272b9SScott Long
557a3133b58SWarner Losh return (res);
5587bec1dd5SJonathan Chen }
5597bec1dd5SJonathan Chen
560255b159fSJonathan Chen /*
561255b159fSJonathan Chen * Dispatch the right handler function per tuple
562255b159fSJonathan Chen */
563255b159fSJonathan Chen
5647bec1dd5SJonathan Chen static int
decode_tuple(device_t cbdev,device_t child,int tupleid,int len,uint8_t * tupledata,uint32_t start,uint32_t * off,struct tuple_callbacks * callbacks,void * argp)565255b159fSJonathan Chen decode_tuple(device_t cbdev, device_t child, int tupleid, int len,
56666e390feSWarner Losh uint8_t *tupledata, uint32_t start, uint32_t *off,
56747147ce7SWarner Losh struct tuple_callbacks *callbacks, void *argp)
5687bec1dd5SJonathan Chen {
5697bec1dd5SJonathan Chen int i;
5700c95c705SJonathan Chen for (i = 0; callbacks[i].id != CISTPL_GENERIC; i++) {
5710c95c705SJonathan Chen if (tupleid == callbacks[i].id)
572a3133b58SWarner Losh return (callbacks[i].func(cbdev, child, tupleid, len,
57347147ce7SWarner Losh tupledata, start, off, &callbacks[i], argp));
5747bec1dd5SJonathan Chen }
575a3133b58SWarner Losh return (callbacks[i].func(cbdev, child, tupleid, len,
57647147ce7SWarner Losh tupledata, start, off, NULL, argp));
5770db7e66cSJonathan Chen }
5780db7e66cSJonathan Chen
57947147ce7SWarner Losh int
cardbus_parse_cis(device_t cbdev,device_t child,struct tuple_callbacks * callbacks,void * argp)580255b159fSJonathan Chen cardbus_parse_cis(device_t cbdev, device_t child,
58147147ce7SWarner Losh struct tuple_callbacks *callbacks, void *argp)
5820db7e66cSJonathan Chen {
583d6f64df9SWarner Losh uint8_t *tupledata;
58417ee700bSWarner Losh int tupleid = CISTPL_NULL;
5857bec1dd5SJonathan Chen int len;
5867bec1dd5SJonathan Chen int expect_linktarget;
58766e390feSWarner Losh uint32_t start, off;
58863fa9f4cSJonathan Chen struct resource *res;
58963fa9f4cSJonathan Chen int rid;
5900db7e66cSJonathan Chen
591d6f64df9SWarner Losh tupledata = malloc(MAXTUPLESIZE, M_DEVBUF, M_WAITOK | M_ZERO);
5927bec1dd5SJonathan Chen expect_linktarget = TRUE;
59341ac33a2SWarner Losh if ((start = pci_read_config(child, PCIR_CIS, 4)) == 0) {
594e6d3b1bdSWarner Losh DEVPRINTF((cbdev, "Warning: CIS pointer is 0: (no CIS)\n"));
595d6f64df9SWarner Losh free(tupledata, M_DEVBUF);
59640895595SWarner Losh return (0);
597509cfe6fSWarner Losh }
598e6d3b1bdSWarner Losh DEVPRINTF((cbdev, "CIS pointer is %#x\n", start));
59949f158ccSJonathan Chen off = 0;
60063fa9f4cSJonathan Chen res = cardbus_read_tuple_init(cbdev, child, &start, &rid);
601509cfe6fSWarner Losh if (res == NULL) {
602509cfe6fSWarner Losh device_printf(cbdev, "Unable to allocate resources for CIS\n");
603d6f64df9SWarner Losh free(tupledata, M_DEVBUF);
604a3133b58SWarner Losh return (ENXIO);
605509cfe6fSWarner Losh }
6061e962d00SScott Long
6077bec1dd5SJonathan Chen do {
60847147ce7SWarner Losh if (cardbus_read_tuple(cbdev, child, res, start, &off,
60947147ce7SWarner Losh &tupleid, &len, tupledata) != 0) {
61063fa9f4cSJonathan Chen device_printf(cbdev, "Failed to read CIS.\n");
61163fa9f4cSJonathan Chen cardbus_read_tuple_finish(cbdev, child, rid, res);
612d6f64df9SWarner Losh free(tupledata, M_DEVBUF);
613a3133b58SWarner Losh return (ENXIO);
61463fa9f4cSJonathan Chen }
6157bec1dd5SJonathan Chen
6167bec1dd5SJonathan Chen if (expect_linktarget && tupleid != CISTPL_LINKTARGET) {
617255b159fSJonathan Chen device_printf(cbdev, "Expecting link target, got 0x%x\n",
6187bec1dd5SJonathan Chen tupleid);
61963fa9f4cSJonathan Chen cardbus_read_tuple_finish(cbdev, child, rid, res);
620d6f64df9SWarner Losh free(tupledata, M_DEVBUF);
621a3133b58SWarner Losh return (EINVAL);
6227bec1dd5SJonathan Chen }
623255b159fSJonathan Chen expect_linktarget = decode_tuple(cbdev, child, tupleid, len,
62447147ce7SWarner Losh tupledata, start, &off, callbacks, argp);
62563fa9f4cSJonathan Chen if (expect_linktarget != 0) {
626509cfe6fSWarner Losh device_printf(cbdev, "Parsing failed with %d\n",
627509cfe6fSWarner Losh expect_linktarget);
62863fa9f4cSJonathan Chen cardbus_read_tuple_finish(cbdev, child, rid, res);
629d6f64df9SWarner Losh free(tupledata, M_DEVBUF);
630a3133b58SWarner Losh return (expect_linktarget);
63163fa9f4cSJonathan Chen }
6327bec1dd5SJonathan Chen } while (tupleid != CISTPL_END);
63363fa9f4cSJonathan Chen cardbus_read_tuple_finish(cbdev, child, rid, res);
634d6f64df9SWarner Losh free(tupledata, M_DEVBUF);
635a3133b58SWarner Losh return (0);
6360db7e66cSJonathan Chen }
6370db7e66cSJonathan Chen
6380c95c705SJonathan Chen int
cardbus_do_cis(device_t cbdev,device_t child)639255b159fSJonathan Chen cardbus_do_cis(device_t cbdev, device_t child)
6400c95c705SJonathan Chen {
6410c95c705SJonathan Chen struct tuple_callbacks init_callbacks[] = {
642a294cdb6SWarner Losh MAKETUPLE(LONGLINK_CB, unhandled),
6430c95c705SJonathan Chen MAKETUPLE(INDIRECT, unhandled),
6440c95c705SJonathan Chen MAKETUPLE(LONGLINK_MFC, unhandled),
6450c95c705SJonathan Chen MAKETUPLE(BAR, bar),
6460c95c705SJonathan Chen MAKETUPLE(LONGLINK_A, unhandled),
6470c95c705SJonathan Chen MAKETUPLE(LONGLINK_C, unhandled),
6480c95c705SJonathan Chen MAKETUPLE(LINKTARGET, linktarget),
6490c95c705SJonathan Chen MAKETUPLE(VERS_1, vers_1),
6500c95c705SJonathan Chen MAKETUPLE(MANFID, manfid),
6510c95c705SJonathan Chen MAKETUPLE(FUNCID, funcid),
6520c95c705SJonathan Chen MAKETUPLE(FUNCE, funce),
6530c95c705SJonathan Chen MAKETUPLE(END, end),
6540c95c705SJonathan Chen MAKETUPLE(GENERIC, generic),
6550c95c705SJonathan Chen };
65663fa9f4cSJonathan Chen
6571acd1e20SWarner Losh return (cardbus_parse_cis(cbdev, child, init_callbacks, NULL));
6580c95c705SJonathan Chen }
659