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 bus_space_tag_t bt; 40663fa9f4cSJonathan Chen bus_space_handle_t bh; 40763fa9f4cSJonathan Chen int ret; 4080db7e66cSJonathan Chen 40963fa9f4cSJonathan Chen bt = rman_get_bustag(res); 41063fa9f4cSJonathan Chen bh = rman_get_bushandle(res); 4110db7e66cSJonathan Chen 41263fa9f4cSJonathan Chen *tupleid = bus_space_read_1(bt, bh, start + *off); 41363fa9f4cSJonathan Chen *len = bus_space_read_1(bt, bh, start + *off + 1); 41463fa9f4cSJonathan Chen bus_space_read_region_1(bt, bh, *off + start + 2, tupledata, *len); 41563fa9f4cSJonathan Chen ret = 0; 41663fa9f4cSJonathan Chen *off += *len + 2; 417a3133b58SWarner Losh return (ret); 4180db7e66cSJonathan Chen } 41963fa9f4cSJonathan Chen 42063fa9f4cSJonathan Chen static int 42163fa9f4cSJonathan Chen cardbus_read_tuple(device_t cbdev, device_t child, struct resource *res, 42266e390feSWarner Losh uint32_t start, uint32_t *off, int *tupleid, int *len, 42366e390feSWarner Losh uint8_t *tupledata) 42463fa9f4cSJonathan Chen { 425bee89c73SWarner Losh if (res == CIS_CONFIG_SPACE) 426a3133b58SWarner Losh return (cardbus_read_tuple_conf(cbdev, child, start, off, 427a3133b58SWarner Losh tupleid, len, tupledata)); 428bee89c73SWarner Losh return (cardbus_read_tuple_mem(cbdev, res, start, off, tupleid, len, 429bee89c73SWarner Losh tupledata)); 43063fa9f4cSJonathan Chen } 43163fa9f4cSJonathan Chen 43263fa9f4cSJonathan Chen static void 43363fa9f4cSJonathan Chen cardbus_read_tuple_finish(device_t cbdev, device_t child, int rid, 43463fa9f4cSJonathan Chen struct resource *res) 43563fa9f4cSJonathan Chen { 436c732cf3bSWarner Losh if (res != CIS_CONFIG_SPACE) { 437bee89c73SWarner Losh bus_release_resource(child, SYS_RES_MEMORY, rid, res); 43847147ce7SWarner Losh if (rid == PCIM_CIS_ASI_ROM) 43947147ce7SWarner Losh pci_write_config(child, rid, pci_read_config(child, 44047147ce7SWarner Losh rid, 4) & ~PCIR_BIOS, 4); 44163fa9f4cSJonathan Chen } 44263fa9f4cSJonathan Chen } 44363fa9f4cSJonathan Chen 44463fa9f4cSJonathan Chen static struct resource * 44566e390feSWarner Losh cardbus_read_tuple_init(device_t cbdev, device_t child, uint32_t *start, 44663fa9f4cSJonathan Chen int *rid) 44763fa9f4cSJonathan Chen { 44863fa9f4cSJonathan Chen struct resource *res; 449495036f2SWarner Losh uint32_t space; 45063fa9f4cSJonathan Chen 45141ac33a2SWarner Losh space = *start & PCIM_CIS_ASI_MASK; 452495036f2SWarner Losh switch (space) { 4534b333740SWarner Losh case PCIM_CIS_ASI_CONFIG: 454e6d3b1bdSWarner Losh DEVPRINTF((cbdev, "CIS in PCI config space\n")); 455e6e272b9SScott Long /* CIS in PCI config space need no initialization */ 456c732cf3bSWarner Losh return (CIS_CONFIG_SPACE); 45741ac33a2SWarner Losh case PCIM_CIS_ASI_BAR0: 45841ac33a2SWarner Losh case PCIM_CIS_ASI_BAR1: 45941ac33a2SWarner Losh case PCIM_CIS_ASI_BAR2: 46041ac33a2SWarner Losh case PCIM_CIS_ASI_BAR3: 46141ac33a2SWarner Losh case PCIM_CIS_ASI_BAR4: 46241ac33a2SWarner Losh case PCIM_CIS_ASI_BAR5: 46341ac33a2SWarner Losh *rid = PCIR_BAR(space - PCIM_CIS_ASI_BAR0); 464e6d3b1bdSWarner Losh DEVPRINTF((cbdev, "CIS in BAR %#x\n", *rid)); 46563fa9f4cSJonathan Chen break; 46641ac33a2SWarner Losh case PCIM_CIS_ASI_ROM: 46741ac33a2SWarner Losh *rid = PCIR_BIOS; 468e6d3b1bdSWarner Losh DEVPRINTF((cbdev, "CIS in option rom\n")); 46963fa9f4cSJonathan Chen break; 47063fa9f4cSJonathan Chen default: 47163fa9f4cSJonathan Chen device_printf(cbdev, "Unable to read CIS: Unknown space: %d\n", 472495036f2SWarner Losh space); 473a3133b58SWarner Losh return (NULL); 47463fa9f4cSJonathan Chen } 47563fa9f4cSJonathan Chen 47663fa9f4cSJonathan Chen /* allocate the memory space to read CIS */ 477bee89c73SWarner Losh res = bus_alloc_resource(child, SYS_RES_MEMORY, rid, 0, ~0, 1, 47847147ce7SWarner Losh rman_make_alignment_flags(4096) | RF_ACTIVE); 47963fa9f4cSJonathan Chen if (res == NULL) { 48063fa9f4cSJonathan Chen device_printf(cbdev, "Unable to allocate resource " 48163fa9f4cSJonathan Chen "to read CIS.\n"); 482a3133b58SWarner Losh return (NULL); 48363fa9f4cSJonathan Chen } 484e6d3b1bdSWarner Losh DEVPRINTF((cbdev, "CIS Mapped to %#lx\n", rman_get_start(res))); 48547147ce7SWarner Losh if (*rid == PCIR_BIOS) 48647147ce7SWarner Losh pci_write_config(child, *rid, 48747147ce7SWarner Losh rman_get_start(res) | PCIM_BIOS_ENABLE, 4); 48863fa9f4cSJonathan Chen 48963fa9f4cSJonathan Chen /* Flip to the right ROM image if CIS is in ROM */ 49041ac33a2SWarner Losh if (space == PCIM_CIS_ASI_ROM) { 49163fa9f4cSJonathan Chen bus_space_tag_t bt; 49263fa9f4cSJonathan Chen bus_space_handle_t bh; 49366e390feSWarner Losh uint32_t imagesize; 49466e390feSWarner Losh uint32_t imagebase = 0; 49566e390feSWarner Losh uint32_t pcidata; 49666e390feSWarner Losh uint16_t romsig; 49763fa9f4cSJonathan Chen int romnum = 0; 498e6e272b9SScott Long int imagenum; 49963fa9f4cSJonathan Chen 50063fa9f4cSJonathan Chen bt = rman_get_bustag(res); 50163fa9f4cSJonathan Chen bh = rman_get_bushandle(res); 50263fa9f4cSJonathan Chen 50341ac33a2SWarner Losh imagenum = (*start & PCIM_CIS_ROM_MASK) >> 28; 50463fa9f4cSJonathan Chen for (romnum = 0;; romnum++) { 505e6e272b9SScott Long romsig = bus_space_read_2(bt, bh, 506e6e272b9SScott Long imagebase + CARDBUS_EXROM_SIGNATURE); 507e6e272b9SScott Long if (romsig != 0xaa55) { 50863fa9f4cSJonathan Chen device_printf(cbdev, "Bad header in rom %d: " 509e6e272b9SScott Long "[%x] %04x\n", romnum, imagebase + 510e6e272b9SScott Long CARDBUS_EXROM_SIGNATURE, romsig); 51189558ee8SWarner Losh bus_release_resource(child, SYS_RES_MEMORY, 51263fa9f4cSJonathan Chen *rid, res); 51363fa9f4cSJonathan Chen *rid = 0; 514a3133b58SWarner Losh return (NULL); 51563fa9f4cSJonathan Chen } 516e6e272b9SScott Long 517e6e272b9SScott Long /* 518e6e272b9SScott Long * If this was the Option ROM image that we were 519e6e272b9SScott Long * looking for, then we are done. 520e6e272b9SScott Long */ 521e6e272b9SScott Long if (romnum == imagenum) 522e6e272b9SScott Long break; 523e6e272b9SScott Long 524e6e272b9SScott Long /* Find out where the next Option ROM image is */ 525e6e272b9SScott Long pcidata = imagebase + bus_space_read_2(bt, bh, 526e6e272b9SScott Long imagebase + CARDBUS_EXROM_DATA_PTR); 52763fa9f4cSJonathan Chen imagesize = bus_space_read_2(bt, bh, 528e6e272b9SScott Long pcidata + CARDBUS_EXROM_DATA_IMAGE_LENGTH); 5290db7e66cSJonathan Chen 5307bec1dd5SJonathan Chen if (imagesize == 0) { 5310db7e66cSJonathan Chen /* 5320db7e66cSJonathan Chen * XXX some ROMs seem to have this as zero, 5330db7e66cSJonathan Chen * can we assume this means 1 block? 5340db7e66cSJonathan Chen */ 535e6e272b9SScott Long device_printf(cbdev, "Warning, size of Option " 536e6e272b9SScott Long "ROM image %d is 0 bytes, assuming 512 " 537e6e272b9SScott Long "bytes.\n", romnum); 5380db7e66cSJonathan Chen imagesize = 1; 5397bec1dd5SJonathan Chen } 540e6e272b9SScott Long 541e6e272b9SScott Long /* Image size is in 512 byte units */ 5420db7e66cSJonathan Chen imagesize <<= 9; 5430db7e66cSJonathan Chen 544e6e272b9SScott Long if ((bus_space_read_1(bt, bh, pcidata + 5451e06ae99SScott Long CARDBUS_EXROM_DATA_INDICATOR) & 0x80) != 0) { 546e6e272b9SScott Long device_printf(cbdev, "Cannot find CIS in " 547e6e272b9SScott Long "Option ROM\n"); 54889558ee8SWarner Losh bus_release_resource(child, SYS_RES_MEMORY, 549e6e272b9SScott Long *rid, res); 550e6e272b9SScott Long *rid = 0; 551a3133b58SWarner Losh return (NULL); 5520db7e66cSJonathan Chen } 553e6e272b9SScott Long imagebase += imagesize; 5540db7e66cSJonathan Chen } 55541ac33a2SWarner Losh *start = imagebase + (*start & PCIM_CIS_ADDR_MASK); 5560db7e66cSJonathan Chen } else { 55741ac33a2SWarner Losh *start = *start & PCIM_CIS_ADDR_MASK; 5580db7e66cSJonathan Chen } 559e6d3b1bdSWarner Losh DEVPRINTF((cbdev, "CIS offset is %#x\n", *start)); 560e6e272b9SScott Long 561a3133b58SWarner Losh return (res); 5627bec1dd5SJonathan Chen } 5637bec1dd5SJonathan Chen 564255b159fSJonathan Chen /* 565255b159fSJonathan Chen * Dispatch the right handler function per tuple 566255b159fSJonathan Chen */ 567255b159fSJonathan Chen 5687bec1dd5SJonathan Chen static int 569255b159fSJonathan Chen decode_tuple(device_t cbdev, device_t child, int tupleid, int len, 57066e390feSWarner Losh uint8_t *tupledata, uint32_t start, uint32_t *off, 57147147ce7SWarner Losh struct tuple_callbacks *callbacks, void *argp) 5727bec1dd5SJonathan Chen { 5737bec1dd5SJonathan Chen int i; 5740c95c705SJonathan Chen for (i = 0; callbacks[i].id != CISTPL_GENERIC; i++) { 5750c95c705SJonathan Chen if (tupleid == callbacks[i].id) 576a3133b58SWarner Losh return (callbacks[i].func(cbdev, child, tupleid, len, 57747147ce7SWarner Losh tupledata, start, off, &callbacks[i], argp)); 5787bec1dd5SJonathan Chen } 579a3133b58SWarner Losh return (callbacks[i].func(cbdev, child, tupleid, len, 58047147ce7SWarner Losh tupledata, start, off, NULL, argp)); 5810db7e66cSJonathan Chen } 5820db7e66cSJonathan Chen 58347147ce7SWarner Losh int 584255b159fSJonathan Chen cardbus_parse_cis(device_t cbdev, device_t child, 58547147ce7SWarner Losh struct tuple_callbacks *callbacks, void *argp) 5860db7e66cSJonathan Chen { 58766e390feSWarner Losh uint8_t tupledata[MAXTUPLESIZE]; 58817ee700bSWarner Losh int tupleid = CISTPL_NULL; 5897bec1dd5SJonathan Chen int len; 5907bec1dd5SJonathan Chen int expect_linktarget; 59166e390feSWarner Losh uint32_t start, off; 59263fa9f4cSJonathan Chen struct resource *res; 59363fa9f4cSJonathan Chen int rid; 5940db7e66cSJonathan Chen 5950db7e66cSJonathan Chen bzero(tupledata, MAXTUPLESIZE); 5967bec1dd5SJonathan Chen expect_linktarget = TRUE; 59741ac33a2SWarner Losh if ((start = pci_read_config(child, PCIR_CIS, 4)) == 0) { 598e6d3b1bdSWarner Losh DEVPRINTF((cbdev, "Warning: CIS pointer is 0: (no CIS)\n")); 599e6e272b9SScott Long return (ENXIO); 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"); 606a3133b58SWarner Losh return (ENXIO); 607509cfe6fSWarner Losh } 6081e962d00SScott Long 6097bec1dd5SJonathan Chen do { 61047147ce7SWarner Losh if (cardbus_read_tuple(cbdev, child, res, start, &off, 61147147ce7SWarner Losh &tupleid, &len, tupledata) != 0) { 61263fa9f4cSJonathan Chen device_printf(cbdev, "Failed to read CIS.\n"); 61363fa9f4cSJonathan Chen cardbus_read_tuple_finish(cbdev, child, rid, res); 614a3133b58SWarner Losh return (ENXIO); 61563fa9f4cSJonathan Chen } 6167bec1dd5SJonathan Chen 6177bec1dd5SJonathan Chen if (expect_linktarget && tupleid != CISTPL_LINKTARGET) { 618255b159fSJonathan Chen device_printf(cbdev, "Expecting link target, got 0x%x\n", 6197bec1dd5SJonathan Chen tupleid); 62063fa9f4cSJonathan Chen cardbus_read_tuple_finish(cbdev, child, rid, res); 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); 629a3133b58SWarner Losh return (expect_linktarget); 63063fa9f4cSJonathan Chen } 6317bec1dd5SJonathan Chen } while (tupleid != CISTPL_END); 63263fa9f4cSJonathan Chen cardbus_read_tuple_finish(cbdev, child, rid, res); 633a3133b58SWarner Losh return (0); 6340db7e66cSJonathan Chen } 6350db7e66cSJonathan Chen 6360c95c705SJonathan Chen int 637255b159fSJonathan Chen cardbus_do_cis(device_t cbdev, device_t child) 6380c95c705SJonathan Chen { 6390c95c705SJonathan Chen struct tuple_callbacks init_callbacks[] = { 640a294cdb6SWarner Losh MAKETUPLE(LONGLINK_CB, unhandled), 6410c95c705SJonathan Chen MAKETUPLE(INDIRECT, unhandled), 6420c95c705SJonathan Chen MAKETUPLE(LONGLINK_MFC, unhandled), 6430c95c705SJonathan Chen MAKETUPLE(BAR, bar), 6440c95c705SJonathan Chen MAKETUPLE(LONGLINK_A, unhandled), 6450c95c705SJonathan Chen MAKETUPLE(LONGLINK_C, unhandled), 6460c95c705SJonathan Chen MAKETUPLE(LINKTARGET, linktarget), 6470c95c705SJonathan Chen MAKETUPLE(VERS_1, vers_1), 6480c95c705SJonathan Chen MAKETUPLE(MANFID, manfid), 6490c95c705SJonathan Chen MAKETUPLE(FUNCID, funcid), 6500c95c705SJonathan Chen MAKETUPLE(FUNCE, funce), 6510c95c705SJonathan Chen MAKETUPLE(END, end), 6520c95c705SJonathan Chen MAKETUPLE(GENERIC, generic), 6530c95c705SJonathan Chen }; 65463fa9f4cSJonathan Chen 6551acd1e20SWarner Losh return (cardbus_parse_cis(cbdev, child, init_callbacks, NULL)); 6560c95c705SJonathan Chen } 657