10db7e66cSJonathan Chen /* 20db7e66cSJonathan Chen * Copyright (c) 2000,2001 Jonathan Chen. 30db7e66cSJonathan Chen * All rights reserved. 40db7e66cSJonathan Chen * 50db7e66cSJonathan Chen * Redistribution and use in source and binary forms, with or without 60db7e66cSJonathan Chen * modification, are permitted provided that the following conditions 70db7e66cSJonathan Chen * are met: 80db7e66cSJonathan Chen * 1. Redistributions of source code must retain the above copyright 90db7e66cSJonathan Chen * notice, this list of conditions, and the following disclaimer, 100db7e66cSJonathan Chen * without modification, immediately at the beginning of the file. 110db7e66cSJonathan Chen * 2. Redistributions in binary form must reproduce the above copyright 120db7e66cSJonathan Chen * notice, this list of conditions and the following disclaimer in 130db7e66cSJonathan Chen * the documentation and/or other materials provided with the 140db7e66cSJonathan Chen * distribution. 150db7e66cSJonathan Chen * 160db7e66cSJonathan Chen * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 170db7e66cSJonathan Chen * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 180db7e66cSJonathan Chen * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 190db7e66cSJonathan Chen * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR 200db7e66cSJonathan Chen * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 210db7e66cSJonathan Chen * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 220db7e66cSJonathan Chen * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 230db7e66cSJonathan Chen * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 240db7e66cSJonathan Chen * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 250db7e66cSJonathan Chen * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 260db7e66cSJonathan Chen * SUCH DAMAGE. 270db7e66cSJonathan Chen * 280db7e66cSJonathan Chen * $FreeBSD$ 290db7e66cSJonathan Chen */ 300db7e66cSJonathan Chen 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> 440db7e66cSJonathan Chen 457ba175acSWarner Losh #include <sys/pciio.h> 4663fa9f4cSJonathan Chen #include <dev/pci/pcivar.h> 4763fa9f4cSJonathan Chen #include <dev/pci/pcireg.h> 480db7e66cSJonathan Chen 490db7e66cSJonathan Chen #include <dev/cardbus/cardbusreg.h> 5063fa9f4cSJonathan Chen #include <dev/cardbus/cardbusvar.h> 510db7e66cSJonathan Chen #include <dev/cardbus/cardbus_cis.h> 520db7e66cSJonathan Chen 5380f10018STakanori Watanabe #include <dev/pccard/pccardvar.h> 540c95c705SJonathan Chen 55b3889b68SWarner Losh extern int cardbus_cis_debug; 56b3889b68SWarner Losh 57b3889b68SWarner Losh #define DPRINTF(a) if (cardbus_cis_debug) printf a 58b3889b68SWarner Losh #define DEVPRINTF(x) if (cardbus_cis_debug) device_printf x 59b3889b68SWarner Losh 600c95c705SJonathan Chen #define DECODE_PARAMS \ 61255b159fSJonathan Chen (device_t cbdev, device_t child, int id, int len, \ 6263fa9f4cSJonathan Chen u_int8_t *tupledata, u_int32_t start, u_int32_t *off, \ 630c95c705SJonathan Chen struct tuple_callbacks *info) 640db7e66cSJonathan Chen 650c95c705SJonathan Chen struct tuple_callbacks { 660c95c705SJonathan Chen int id; 670db7e66cSJonathan Chen char *name; 680c95c705SJonathan Chen int (*func) DECODE_PARAMS; 690db7e66cSJonathan Chen }; 70255b159fSJonathan Chen 71255b159fSJonathan Chen #define DECODE_PROTOTYPE(NAME) static int decode_tuple_ ## NAME DECODE_PARAMS 72255b159fSJonathan Chen DECODE_PROTOTYPE(generic); 73255b159fSJonathan Chen DECODE_PROTOTYPE(nothing); 74255b159fSJonathan Chen DECODE_PROTOTYPE(copy); 75255b159fSJonathan Chen DECODE_PROTOTYPE(linktarget); 76255b159fSJonathan Chen DECODE_PROTOTYPE(vers_1); 77255b159fSJonathan Chen DECODE_PROTOTYPE(funcid); 78255b159fSJonathan Chen DECODE_PROTOTYPE(manfid); 79255b159fSJonathan Chen DECODE_PROTOTYPE(funce); 80255b159fSJonathan Chen DECODE_PROTOTYPE(bar); 81255b159fSJonathan Chen DECODE_PROTOTYPE(unhandled); 82255b159fSJonathan Chen DECODE_PROTOTYPE(end); 83255b159fSJonathan Chen static int cardbus_read_tuple_conf(device_t cbdev, device_t child, 8463fa9f4cSJonathan Chen u_int32_t start, u_int32_t *off, int *tupleid, int *len, 85255b159fSJonathan Chen u_int8_t *tupledata); 8663fa9f4cSJonathan Chen static int cardbus_read_tuple_mem(device_t cbdev, struct resource *res, 8763fa9f4cSJonathan Chen u_int32_t start, u_int32_t *off, int *tupleid, int *len, 88255b159fSJonathan Chen u_int8_t *tupledata); 89255b159fSJonathan Chen static int cardbus_read_tuple(device_t cbdev, device_t child, 9063fa9f4cSJonathan Chen struct resource *res, u_int32_t start, u_int32_t *off, 9163fa9f4cSJonathan Chen int *tupleid, int *len, u_int8_t *tupledata); 9263fa9f4cSJonathan Chen static void cardbus_read_tuple_finish(device_t cbdev, device_t child, 9363fa9f4cSJonathan Chen int rid, struct resource *res); 9463fa9f4cSJonathan Chen static struct resource *cardbus_read_tuple_init(device_t cbdev, device_t child, 9563fa9f4cSJonathan Chen u_int32_t *start, int *rid); 96255b159fSJonathan Chen static int decode_tuple(device_t cbdev, device_t child, int tupleid, 9763fa9f4cSJonathan Chen int len, u_int8_t *tupledata, u_int32_t start, 98255b159fSJonathan Chen u_int32_t *off, struct tuple_callbacks *callbacks); 99255b159fSJonathan Chen static int cardbus_parse_cis(device_t cbdev, device_t child, 100255b159fSJonathan Chen struct tuple_callbacks *callbacks); 10163fa9f4cSJonathan Chen static int barsort(const void *a, const void *b); 10263fa9f4cSJonathan Chen static int cardbus_alloc_resources(device_t cbdev, device_t child); 10363fa9f4cSJonathan Chen static void cardbus_add_map(device_t cbdev, device_t child, int reg); 10463fa9f4cSJonathan Chen static void cardbus_pickup_maps(device_t cbdev, device_t child); 10563fa9f4cSJonathan Chen 106255b159fSJonathan Chen 1070c95c705SJonathan Chen #define MAKETUPLE(NAME,FUNC) { CISTPL_ ## NAME, #NAME, decode_tuple_ ## FUNC } 1080db7e66cSJonathan Chen 1090db7e66cSJonathan Chen static char *funcnames[] = { 1100db7e66cSJonathan Chen "Multi-Functioned", 1110db7e66cSJonathan Chen "Memory", 1120db7e66cSJonathan Chen "Serial Port", 1130db7e66cSJonathan Chen "Parallel Port", 1140db7e66cSJonathan Chen "Fixed Disk", 1150db7e66cSJonathan Chen "Video Adaptor", 1160db7e66cSJonathan Chen "Network Adaptor", 1170db7e66cSJonathan Chen "AIMS", 1180db7e66cSJonathan Chen "SCSI", 1190db7e66cSJonathan Chen "Security" 1200db7e66cSJonathan Chen }; 1210db7e66cSJonathan Chen 12263fa9f4cSJonathan Chen struct cardbus_quirk { 12363fa9f4cSJonathan Chen u_int32_t devid; /* Vendor/device of the card */ 12463fa9f4cSJonathan Chen int type; 12563fa9f4cSJonathan Chen #define CARDBUS_QUIRK_MAP_REG 1 /* PCI map register in weird place */ 12663fa9f4cSJonathan Chen int arg1; 12763fa9f4cSJonathan Chen int arg2; 12863fa9f4cSJonathan Chen }; 12963fa9f4cSJonathan Chen 13063fa9f4cSJonathan Chen struct cardbus_quirk cardbus_quirks[] = { 13163fa9f4cSJonathan Chen { 0 } 13263fa9f4cSJonathan Chen }; 13363fa9f4cSJonathan Chen 1340c95c705SJonathan Chen static struct cis_tupleinfo *cisread_buf; 1350c95c705SJonathan Chen static int ncisread_buf; 1360c95c705SJonathan Chen 137255b159fSJonathan Chen /* 138255b159fSJonathan Chen * Handler functions for various CIS tuples 139255b159fSJonathan Chen */ 140255b159fSJonathan Chen 1410db7e66cSJonathan Chen DECODE_PROTOTYPE(generic) 1420db7e66cSJonathan Chen { 1430db7e66cSJonathan Chen #ifdef CARDBUS_DEBUG 1440db7e66cSJonathan Chen int i; 1450db7e66cSJonathan Chen 1460db7e66cSJonathan Chen if (info) 1470db7e66cSJonathan Chen printf("TUPLE: %s [%d]:", info->name, len); 1480db7e66cSJonathan Chen else 1490db7e66cSJonathan Chen printf("TUPLE: Unknown(0x%02x) [%d]:", id, len); 1500db7e66cSJonathan Chen 1510db7e66cSJonathan Chen for (i = 0; i < len; i++) { 1520db7e66cSJonathan Chen if (i % 0x10 == 0 && len > 0x10) 1530db7e66cSJonathan Chen printf("\n 0x%02x:", i); 1547bec1dd5SJonathan Chen printf(" %02x", tupledata[i]); 1550db7e66cSJonathan Chen } 1560db7e66cSJonathan Chen printf("\n"); 1570db7e66cSJonathan Chen #endif 158a3133b58SWarner Losh return (0); 1590db7e66cSJonathan Chen } 1600db7e66cSJonathan Chen 1610c95c705SJonathan Chen DECODE_PROTOTYPE(nothing) 1620c95c705SJonathan Chen { 163a3133b58SWarner Losh return (0); 1640c95c705SJonathan Chen } 1650c95c705SJonathan Chen 1660c95c705SJonathan Chen DECODE_PROTOTYPE(copy) 1670c95c705SJonathan Chen { 1680c95c705SJonathan Chen struct cis_tupleinfo *tmpbuf; 1690c95c705SJonathan Chen 1700c95c705SJonathan Chen tmpbuf = malloc(sizeof(struct cis_tupleinfo) * (ncisread_buf+1), 1710c95c705SJonathan Chen M_DEVBUF, M_WAITOK); 1720c95c705SJonathan Chen if (ncisread_buf > 0) { 1730c95c705SJonathan Chen memcpy(tmpbuf, cisread_buf, 1740c95c705SJonathan Chen sizeof(struct cis_tupleinfo) * ncisread_buf); 1750c95c705SJonathan Chen free(cisread_buf, M_DEVBUF); 1760c95c705SJonathan Chen } 1770c95c705SJonathan Chen cisread_buf = tmpbuf; 1780c95c705SJonathan Chen 1790c95c705SJonathan Chen cisread_buf[ncisread_buf].id = id; 1800c95c705SJonathan Chen cisread_buf[ncisread_buf].len = len; 1810c95c705SJonathan Chen cisread_buf[ncisread_buf].data = malloc(len, M_DEVBUF, M_WAITOK); 1820c95c705SJonathan Chen memcpy(cisread_buf[ncisread_buf].data, tupledata, len); 1830c95c705SJonathan Chen ncisread_buf++; 184a3133b58SWarner Losh return (0); 1850c95c705SJonathan Chen } 1860c95c705SJonathan Chen 1870db7e66cSJonathan Chen DECODE_PROTOTYPE(linktarget) 1880db7e66cSJonathan Chen { 18949f158ccSJonathan Chen #ifdef CARDBUS_DEBUG 19049f158ccSJonathan Chen int i; 19149f158ccSJonathan Chen 19249f158ccSJonathan Chen printf("TUPLE: %s [%d]:", info->name, len); 19349f158ccSJonathan Chen 19449f158ccSJonathan Chen for (i = 0; i < len; i++) { 19549f158ccSJonathan Chen if (i % 0x10 == 0 && len > 0x10) 19649f158ccSJonathan Chen printf("\n 0x%02x:", i); 19749f158ccSJonathan Chen printf(" %02x", tupledata[i]); 19849f158ccSJonathan Chen } 19949f158ccSJonathan Chen printf("\n"); 20049f158ccSJonathan Chen #endif 2017bec1dd5SJonathan Chen if (len != 3 || tupledata[0] != 'C' || tupledata[1] != 'I' || 2027bec1dd5SJonathan Chen tupledata[2] != 'S') { 2030db7e66cSJonathan Chen printf("Invalid data for CIS Link Target!\n"); 204255b159fSJonathan Chen decode_tuple_generic(cbdev, child, id, len, tupledata, 20549f158ccSJonathan Chen start, off, info); 206a3133b58SWarner Losh return (EINVAL); 2070db7e66cSJonathan Chen } 208a3133b58SWarner Losh return (0); 2090db7e66cSJonathan Chen } 2100db7e66cSJonathan Chen 2110db7e66cSJonathan Chen DECODE_PROTOTYPE(vers_1) 2120db7e66cSJonathan Chen { 2130db7e66cSJonathan Chen int i; 2147bec1dd5SJonathan Chen printf("Product version: %d.%d\n", tupledata[0], tupledata[1]); 2150db7e66cSJonathan Chen printf("Product name: "); 2160db7e66cSJonathan Chen for (i = 2; i < len; i++) { 2177bec1dd5SJonathan Chen if (tupledata[i] == '\0') 2180db7e66cSJonathan Chen printf(" | "); 2197bec1dd5SJonathan Chen else if (tupledata[i] == 0xff) 2200db7e66cSJonathan Chen break; 2210db7e66cSJonathan Chen else 2227bec1dd5SJonathan Chen printf("%c", tupledata[i]); 2230db7e66cSJonathan Chen } 2240db7e66cSJonathan Chen printf("\n"); 225a3133b58SWarner Losh return (0); 2260db7e66cSJonathan Chen } 2270db7e66cSJonathan Chen 2280db7e66cSJonathan Chen DECODE_PROTOTYPE(funcid) 2290db7e66cSJonathan Chen { 2300db7e66cSJonathan Chen int i; 2310db7e66cSJonathan Chen int numnames = sizeof(funcnames) / sizeof(funcnames[0]); 2320db7e66cSJonathan Chen 2330db7e66cSJonathan Chen printf("Functions: "); 2340db7e66cSJonathan Chen for (i = 0; i < len; i++) { 2357bec1dd5SJonathan Chen if (tupledata[i] < numnames) 2367bec1dd5SJonathan Chen printf("%s", funcnames[tupledata[i]]); 2370db7e66cSJonathan Chen else 2387bec1dd5SJonathan Chen printf("Unknown(%d)", tupledata[i]); 239255b159fSJonathan Chen if (i < len-1) 240255b159fSJonathan Chen printf(", "); 2410db7e66cSJonathan Chen } 2420db7e66cSJonathan Chen printf("\n"); 243a3133b58SWarner Losh return (0); 2440db7e66cSJonathan Chen } 2450db7e66cSJonathan Chen 2460db7e66cSJonathan Chen DECODE_PROTOTYPE(manfid) 2470db7e66cSJonathan Chen { 2480db7e66cSJonathan Chen int i; 2490db7e66cSJonathan Chen printf("Manufacturer ID: "); 2500db7e66cSJonathan Chen for (i = 0; i < len; i++) 2517bec1dd5SJonathan Chen printf("%02x", tupledata[i]); 2520db7e66cSJonathan Chen printf("\n"); 253a3133b58SWarner Losh return (0); 2540db7e66cSJonathan Chen } 2550db7e66cSJonathan Chen 2560db7e66cSJonathan Chen DECODE_PROTOTYPE(funce) 2570db7e66cSJonathan Chen { 2580db7e66cSJonathan Chen int i; 2590db7e66cSJonathan Chen printf("Function Extension: "); 2600db7e66cSJonathan Chen for (i = 0; i < len; i++) 2617bec1dd5SJonathan Chen printf("%02x", tupledata[i]); 2620db7e66cSJonathan Chen printf("\n"); 263a3133b58SWarner Losh return (0); 2640db7e66cSJonathan Chen } 2650db7e66cSJonathan Chen 2660db7e66cSJonathan Chen DECODE_PROTOTYPE(bar) 2670db7e66cSJonathan Chen { 26863fa9f4cSJonathan Chen struct cardbus_devinfo *dinfo = device_get_ivars(child); 2690db7e66cSJonathan Chen int type; 2700db7e66cSJonathan Chen int reg; 2710db7e66cSJonathan Chen u_int32_t bar; 2720db7e66cSJonathan Chen 273e6e272b9SScott Long if (len != 6) { 274e6e272b9SScott Long printf("*** ERROR *** BAR length not 6 (%d)\n", len); 275e6e272b9SScott Long return (EINVAL); 276e6e272b9SScott Long } 2777bec1dd5SJonathan Chen reg = *(u_int16_t*)tupledata; 2787bec1dd5SJonathan Chen len = *(u_int32_t*)(tupledata + 2); 2790db7e66cSJonathan Chen if (reg & TPL_BAR_REG_AS) { 2800db7e66cSJonathan Chen type = SYS_RES_IOPORT; 2810db7e66cSJonathan Chen } else { 2820db7e66cSJonathan Chen type = SYS_RES_MEMORY; 2830db7e66cSJonathan Chen } 2840db7e66cSJonathan Chen bar = (reg & TPL_BAR_REG_ASI_MASK) - 1; 285255b159fSJonathan Chen if (bar < 0 || bar > 5 || 286255b159fSJonathan Chen (type == SYS_RES_IOPORT && bar == 5)) { 287255b159fSJonathan Chen device_printf(cbdev, "Invalid BAR number: %02x(%02x)\n", 2880db7e66cSJonathan Chen reg, bar); 289a3133b58SWarner Losh return (0); 2900db7e66cSJonathan Chen } 2910db7e66cSJonathan Chen bar = CARDBUS_BASE0_REG + bar * 4; 29263fa9f4cSJonathan Chen if (type == SYS_RES_MEMORY) { 29363fa9f4cSJonathan Chen if (bar & TPL_BAR_REG_PREFETCHABLE) 29463fa9f4cSJonathan Chen dinfo->mprefetchable |= BARBIT(bar); 29563fa9f4cSJonathan Chen if (bar & TPL_BAR_REG_BELOW1MB) 29663fa9f4cSJonathan Chen dinfo->mbelow1mb |= BARBIT(bar); 29763fa9f4cSJonathan Chen } else if (type == SYS_RES_IOPORT) { 29863fa9f4cSJonathan Chen if (bar & TPL_BAR_REG_BELOW1MB) 29963fa9f4cSJonathan Chen dinfo->ibelow1mb |= BARBIT(bar); 3000db7e66cSJonathan Chen } 301e6e272b9SScott Long DEVPRINTF((cbdev, "Opening BAR: type=%s, bar=%02x, len=%04x%s%s\n", 30263fa9f4cSJonathan Chen (type == SYS_RES_MEMORY) ? "MEM" : "IO", bar, len, 30363fa9f4cSJonathan Chen (type == SYS_RES_MEMORY && dinfo->mprefetchable & BARBIT(bar)) ? 304e6e272b9SScott Long " (Prefetchable)" : "", type == SYS_RES_MEMORY ? 305e6e272b9SScott Long ((dinfo->mbelow1mb & BARBIT(bar)) ? " (Below 1Mb)" : "") : 306e6e272b9SScott Long (dinfo->ibelow1mb & BARBIT(bar)) ? " (Below 1Mb)" : "" )); 30763fa9f4cSJonathan Chen 3087ba175acSWarner Losh resource_list_add(&dinfo->pci.resources, type, bar, 0UL, ~0UL, len); 309e6e272b9SScott Long 310a3133b58SWarner Losh return (0); 3110db7e66cSJonathan Chen } 3120db7e66cSJonathan Chen 3130db7e66cSJonathan Chen DECODE_PROTOTYPE(unhandled) 3140db7e66cSJonathan Chen { 3150db7e66cSJonathan Chen printf("TUPLE: %s [%d] is unhandled! Bailing...", info->name, len); 316a3133b58SWarner Losh return (-1); 3170db7e66cSJonathan Chen } 3180db7e66cSJonathan Chen 3190db7e66cSJonathan Chen DECODE_PROTOTYPE(end) 3200db7e66cSJonathan Chen { 3210c95c705SJonathan Chen printf("CIS reading done\n"); 322a3133b58SWarner Losh return (0); 3230db7e66cSJonathan Chen } 3240db7e66cSJonathan Chen 325255b159fSJonathan Chen /* 326255b159fSJonathan Chen * Functions to read the a tuple from the card 327255b159fSJonathan Chen */ 328255b159fSJonathan Chen 3290db7e66cSJonathan Chen static int 33063fa9f4cSJonathan Chen cardbus_read_tuple_conf(device_t cbdev, device_t child, u_int32_t start, 331255b159fSJonathan Chen u_int32_t *off, int *tupleid, int *len, u_int8_t *tupledata) 3327bec1dd5SJonathan Chen { 3337bec1dd5SJonathan Chen int i, j; 3347bec1dd5SJonathan Chen u_int32_t e; 33549f158ccSJonathan Chen u_int32_t loc; 3367bec1dd5SJonathan Chen 33763fa9f4cSJonathan Chen loc = start + *off; 33849f158ccSJonathan Chen 33949f158ccSJonathan Chen e = pci_read_config(child, loc - loc % 4, 4); 34049f158ccSJonathan Chen for (j = loc % 4; j > 0; j--) 3417bec1dd5SJonathan Chen e >>= 8; 3427bec1dd5SJonathan Chen *len = 0; 34349f158ccSJonathan Chen for (i = loc, j = -2; j < *len; j++, i++) { 3447bec1dd5SJonathan Chen if (i % 4 == 0) 3457bec1dd5SJonathan Chen e = pci_read_config(child, i, 4); 3467bec1dd5SJonathan Chen if (j == -2) 3477bec1dd5SJonathan Chen *tupleid = 0xff & e; 3487bec1dd5SJonathan Chen else if (j == -1) 3497bec1dd5SJonathan Chen *len = 0xff & e; 3507bec1dd5SJonathan Chen else 3517bec1dd5SJonathan Chen tupledata[j] = 0xff & e; 3527bec1dd5SJonathan Chen e >>= 8; 3537bec1dd5SJonathan Chen } 3547bec1dd5SJonathan Chen *off += *len + 2; 355a3133b58SWarner Losh return (0); 3567bec1dd5SJonathan Chen } 3577bec1dd5SJonathan Chen 3587bec1dd5SJonathan Chen static int 35963fa9f4cSJonathan Chen cardbus_read_tuple_mem(device_t cbdev, struct resource *res, u_int32_t start, 360255b159fSJonathan Chen u_int32_t *off, int *tupleid, int *len, u_int8_t *tupledata) 3610db7e66cSJonathan Chen { 36263fa9f4cSJonathan Chen bus_space_tag_t bt; 36363fa9f4cSJonathan Chen bus_space_handle_t bh; 36463fa9f4cSJonathan Chen int ret; 3650db7e66cSJonathan Chen 36663fa9f4cSJonathan Chen bt = rman_get_bustag(res); 36763fa9f4cSJonathan Chen bh = rman_get_bushandle(res); 3680db7e66cSJonathan Chen 36963fa9f4cSJonathan Chen *tupleid = bus_space_read_1(bt, bh, start + *off); 37063fa9f4cSJonathan Chen *len = bus_space_read_1(bt, bh, start + *off + 1); 37163fa9f4cSJonathan Chen bus_space_read_region_1(bt, bh, *off + start + 2, tupledata, *len); 37263fa9f4cSJonathan Chen ret = 0; 37363fa9f4cSJonathan Chen *off += *len + 2; 374a3133b58SWarner Losh return (ret); 3750db7e66cSJonathan Chen } 37663fa9f4cSJonathan Chen 37763fa9f4cSJonathan Chen static int 37863fa9f4cSJonathan Chen cardbus_read_tuple(device_t cbdev, device_t child, struct resource *res, 37963fa9f4cSJonathan Chen u_int32_t start, u_int32_t *off, int *tupleid, int *len, 38063fa9f4cSJonathan Chen u_int8_t *tupledata) 38163fa9f4cSJonathan Chen { 38263fa9f4cSJonathan Chen if (res == (struct resource*)~0UL) { 383a3133b58SWarner Losh return (cardbus_read_tuple_conf(cbdev, child, start, off, 384a3133b58SWarner Losh tupleid, len, tupledata)); 38563fa9f4cSJonathan Chen } else { 386a3133b58SWarner Losh return (cardbus_read_tuple_mem(cbdev, res, start, off, 387a3133b58SWarner Losh tupleid, len, tupledata)); 38863fa9f4cSJonathan Chen } 38963fa9f4cSJonathan Chen } 39063fa9f4cSJonathan Chen 39163fa9f4cSJonathan Chen static void 39263fa9f4cSJonathan Chen cardbus_read_tuple_finish(device_t cbdev, device_t child, int rid, 39363fa9f4cSJonathan Chen struct resource *res) 39463fa9f4cSJonathan Chen { 39563fa9f4cSJonathan Chen if (res != (struct resource*)~0UL) { 39663fa9f4cSJonathan Chen bus_release_resource(cbdev, SYS_RES_MEMORY, rid, res); 39763fa9f4cSJonathan Chen pci_write_config(child, rid, 0, 4); 39863fa9f4cSJonathan Chen PCI_DISABLE_IO(cbdev, child, SYS_RES_MEMORY); 39963fa9f4cSJonathan Chen } 40063fa9f4cSJonathan Chen } 40163fa9f4cSJonathan Chen 40263fa9f4cSJonathan Chen static struct resource * 40363fa9f4cSJonathan Chen cardbus_read_tuple_init(device_t cbdev, device_t child, u_int32_t *start, 40463fa9f4cSJonathan Chen int *rid) 40563fa9f4cSJonathan Chen { 40663fa9f4cSJonathan Chen u_int32_t testval; 40763fa9f4cSJonathan Chen u_int32_t size; 40863fa9f4cSJonathan Chen struct resource *res; 40963fa9f4cSJonathan Chen 41063fa9f4cSJonathan Chen switch (CARDBUS_CIS_SPACE(*start)) { 41163fa9f4cSJonathan Chen case CARDBUS_CIS_ASI_TUPLE: 412e6e272b9SScott Long /* CIS in PCI config space need no initialization */ 41321677473SWarner Losh return ((struct resource*)~0UL); 41463fa9f4cSJonathan Chen case CARDBUS_CIS_ASI_BAR0: 41563fa9f4cSJonathan Chen case CARDBUS_CIS_ASI_BAR1: 41663fa9f4cSJonathan Chen case CARDBUS_CIS_ASI_BAR2: 41763fa9f4cSJonathan Chen case CARDBUS_CIS_ASI_BAR3: 41863fa9f4cSJonathan Chen case CARDBUS_CIS_ASI_BAR4: 41963fa9f4cSJonathan Chen case CARDBUS_CIS_ASI_BAR5: 42063fa9f4cSJonathan Chen *rid = CARDBUS_BASE0_REG + (CARDBUS_CIS_SPACE(*start) - 1) * 4; 42163fa9f4cSJonathan Chen break; 42263fa9f4cSJonathan Chen case CARDBUS_CIS_ASI_ROM: 42363fa9f4cSJonathan Chen *rid = CARDBUS_ROM_REG; 424e6e272b9SScott Long #if 0 425e6e272b9SScott Long /* 426e6e272b9SScott Long * This mask doesn't contain the bit that actually enables 427e6e272b9SScott Long * the Option ROM. 428e6e272b9SScott Long */ 42963fa9f4cSJonathan Chen pci_write_config(child, *rid, CARDBUS_ROM_ADDRMASK, 4); 430e6e272b9SScott Long #endif 43163fa9f4cSJonathan Chen break; 43263fa9f4cSJonathan Chen default: 43363fa9f4cSJonathan Chen device_printf(cbdev, "Unable to read CIS: Unknown space: %d\n", 43463fa9f4cSJonathan Chen CARDBUS_CIS_SPACE(*start)); 435a3133b58SWarner Losh return (NULL); 43663fa9f4cSJonathan Chen } 43763fa9f4cSJonathan Chen 43863fa9f4cSJonathan Chen /* figure out how much space we need */ 439e6e272b9SScott Long pci_write_config(child, *rid, 0xffffffff, 4); 44063fa9f4cSJonathan Chen testval = pci_read_config(child, *rid, 4); 441e6e272b9SScott Long 442e6e272b9SScott Long /* 443e6e272b9SScott Long * This bit has a different meaning depending if we are dealing 4441e06ae99SScott Long * with a normal BAR or an Option ROM BAR. 445e6e272b9SScott Long */ 446e6e272b9SScott Long if (((testval & 0x1) == 0x1) && (*rid != CARDBUS_ROM_REG)) { 44763fa9f4cSJonathan Chen device_printf(cbdev, "CIS Space is IO, expecting memory.\n"); 448a3133b58SWarner Losh return (NULL); 44963fa9f4cSJonathan Chen } 450e6e272b9SScott Long 45163fa9f4cSJonathan Chen size = CARDBUS_MAPREG_MEM_SIZE(testval); 452e6e272b9SScott Long /* XXX Is this some kind of hack? */ 45363fa9f4cSJonathan Chen if (size < 4096) 45463fa9f4cSJonathan Chen size = 4096; 45563fa9f4cSJonathan Chen /* allocate the memory space to read CIS */ 45663fa9f4cSJonathan Chen res = bus_alloc_resource(cbdev, SYS_RES_MEMORY, rid, 0, ~0, size, 45763fa9f4cSJonathan Chen rman_make_alignment_flags(size) | RF_ACTIVE); 45863fa9f4cSJonathan Chen if (res == NULL) { 45963fa9f4cSJonathan Chen device_printf(cbdev, "Unable to allocate resource " 46063fa9f4cSJonathan Chen "to read CIS.\n"); 461a3133b58SWarner Losh return (NULL); 46263fa9f4cSJonathan Chen } 46363fa9f4cSJonathan Chen pci_write_config(child, *rid, 46463fa9f4cSJonathan Chen rman_get_start(res) | ((*rid == CARDBUS_ROM_REG)? 46563fa9f4cSJonathan Chen CARDBUS_ROM_ENABLE : 0), 46663fa9f4cSJonathan Chen 4); 46763fa9f4cSJonathan Chen PCI_ENABLE_IO(cbdev, child, SYS_RES_MEMORY); 46863fa9f4cSJonathan Chen 46963fa9f4cSJonathan Chen /* Flip to the right ROM image if CIS is in ROM */ 47063fa9f4cSJonathan Chen if (CARDBUS_CIS_SPACE(*start) == CARDBUS_CIS_ASI_ROM) { 47163fa9f4cSJonathan Chen bus_space_tag_t bt; 47263fa9f4cSJonathan Chen bus_space_handle_t bh; 47363fa9f4cSJonathan Chen u_int32_t imagesize; 474e6e272b9SScott Long u_int32_t imagebase = 0; 475e6e272b9SScott Long u_int32_t pcidata; 476e6e272b9SScott Long u_int16_t romsig; 47763fa9f4cSJonathan Chen int romnum = 0; 478e6e272b9SScott Long int imagenum; 47963fa9f4cSJonathan Chen 48063fa9f4cSJonathan Chen bt = rman_get_bustag(res); 48163fa9f4cSJonathan Chen bh = rman_get_bushandle(res); 48263fa9f4cSJonathan Chen 48363fa9f4cSJonathan Chen imagenum = CARDBUS_CIS_ASI_ROM_IMAGE(*start); 48463fa9f4cSJonathan Chen for (romnum = 0;; romnum++) { 485e6e272b9SScott Long romsig = bus_space_read_2(bt, bh, 486e6e272b9SScott Long imagebase + CARDBUS_EXROM_SIGNATURE); 487e6e272b9SScott Long if (romsig != 0xaa55) { 48863fa9f4cSJonathan Chen device_printf(cbdev, "Bad header in rom %d: " 489e6e272b9SScott Long "[%x] %04x\n", romnum, imagebase + 490e6e272b9SScott Long CARDBUS_EXROM_SIGNATURE, romsig); 49163fa9f4cSJonathan Chen bus_release_resource(cbdev, SYS_RES_MEMORY, 49263fa9f4cSJonathan Chen *rid, res); 49363fa9f4cSJonathan Chen *rid = 0; 494a3133b58SWarner Losh return (NULL); 49563fa9f4cSJonathan Chen } 496e6e272b9SScott Long 497e6e272b9SScott Long /* 498e6e272b9SScott Long * If this was the Option ROM image that we were 499e6e272b9SScott Long * looking for, then we are done. 500e6e272b9SScott Long */ 501e6e272b9SScott Long if (romnum == imagenum) 502e6e272b9SScott Long break; 503e6e272b9SScott Long 504e6e272b9SScott Long /* Find out where the next Option ROM image is */ 505e6e272b9SScott Long pcidata = imagebase + bus_space_read_2(bt, bh, 506e6e272b9SScott Long imagebase + CARDBUS_EXROM_DATA_PTR); 50763fa9f4cSJonathan Chen imagesize = bus_space_read_2(bt, bh, 508e6e272b9SScott Long pcidata + CARDBUS_EXROM_DATA_IMAGE_LENGTH); 5090db7e66cSJonathan Chen 5107bec1dd5SJonathan Chen if (imagesize == 0) { 5110db7e66cSJonathan Chen /* 5120db7e66cSJonathan Chen * XXX some ROMs seem to have this as zero, 5130db7e66cSJonathan Chen * can we assume this means 1 block? 5140db7e66cSJonathan Chen */ 515e6e272b9SScott Long device_printf(cbdev, "Warning, size of Option " 516e6e272b9SScott Long "ROM image %d is 0 bytes, assuming 512 " 517e6e272b9SScott Long "bytes.\n", romnum); 5180db7e66cSJonathan Chen imagesize = 1; 5197bec1dd5SJonathan Chen } 520e6e272b9SScott Long 521e6e272b9SScott Long /* Image size is in 512 byte units */ 5220db7e66cSJonathan Chen imagesize <<= 9; 5230db7e66cSJonathan Chen 524e6e272b9SScott Long if ((bus_space_read_1(bt, bh, pcidata + 5251e06ae99SScott Long CARDBUS_EXROM_DATA_INDICATOR) & 0x80) != 0) { 526e6e272b9SScott Long device_printf(cbdev, "Cannot find CIS in " 527e6e272b9SScott Long "Option ROM\n"); 528e6e272b9SScott Long bus_release_resource(cbdev, SYS_RES_MEMORY, 529e6e272b9SScott Long *rid, res); 530e6e272b9SScott Long *rid = 0; 531a3133b58SWarner Losh return (NULL); 5320db7e66cSJonathan Chen } 533e6e272b9SScott Long imagebase += imagesize; 5340db7e66cSJonathan Chen } 535e6e272b9SScott Long *start = imagebase + CARDBUS_CIS_ADDR(*start); 5360db7e66cSJonathan Chen } else { 537e6e272b9SScott Long *start = CARDBUS_CIS_ADDR(*start); 5380db7e66cSJonathan Chen } 539e6e272b9SScott Long 540a3133b58SWarner Losh return (res); 5417bec1dd5SJonathan Chen } 5427bec1dd5SJonathan Chen 543255b159fSJonathan Chen /* 544255b159fSJonathan Chen * Dispatch the right handler function per tuple 545255b159fSJonathan Chen */ 546255b159fSJonathan Chen 5477bec1dd5SJonathan Chen static int 548255b159fSJonathan Chen decode_tuple(device_t cbdev, device_t child, int tupleid, int len, 54963fa9f4cSJonathan Chen u_int8_t *tupledata, u_int32_t start, u_int32_t *off, 5500c95c705SJonathan Chen struct tuple_callbacks *callbacks) 5517bec1dd5SJonathan Chen { 5527bec1dd5SJonathan Chen int i; 5530c95c705SJonathan Chen for (i = 0; callbacks[i].id != CISTPL_GENERIC; i++) { 5540c95c705SJonathan Chen if (tupleid == callbacks[i].id) 555a3133b58SWarner Losh return (callbacks[i].func(cbdev, child, tupleid, len, 556a3133b58SWarner Losh tupledata, start, off, &callbacks[i])); 5577bec1dd5SJonathan Chen } 5587bec1dd5SJonathan Chen 55949f158ccSJonathan Chen if (tupleid < CISTPL_CUSTOMSTART) { 560255b159fSJonathan Chen device_printf(cbdev, "Undefined tuple encountered, " 561255b159fSJonathan Chen "CIS parsing terminated\n"); 562a3133b58SWarner Losh return (EINVAL); 56349f158ccSJonathan Chen } 564a3133b58SWarner Losh return (callbacks[i].func(cbdev, child, tupleid, len, 565a3133b58SWarner Losh tupledata, start, off, NULL)); 5660db7e66cSJonathan Chen } 5670db7e66cSJonathan Chen 5680c95c705SJonathan Chen static int 569255b159fSJonathan Chen cardbus_parse_cis(device_t cbdev, device_t child, 5700c95c705SJonathan Chen struct tuple_callbacks *callbacks) 5710db7e66cSJonathan Chen { 5720db7e66cSJonathan Chen u_int8_t tupledata[MAXTUPLESIZE]; 5737bec1dd5SJonathan Chen int tupleid; 5747bec1dd5SJonathan Chen int len; 5757bec1dd5SJonathan Chen int expect_linktarget; 57649f158ccSJonathan Chen u_int32_t start, off; 57763fa9f4cSJonathan Chen struct resource *res; 57863fa9f4cSJonathan Chen int rid; 5790db7e66cSJonathan Chen 5800db7e66cSJonathan Chen bzero(tupledata, MAXTUPLESIZE); 5817bec1dd5SJonathan Chen expect_linktarget = TRUE; 582e6e272b9SScott Long if ((start = pci_read_config(child, CARDBUS_CIS_REG, 4)) == 0) 583e6e272b9SScott Long return (ENXIO); 58449f158ccSJonathan Chen off = 0; 58563fa9f4cSJonathan Chen res = cardbus_read_tuple_init(cbdev, child, &start, &rid); 58663fa9f4cSJonathan Chen if (res == NULL) 587a3133b58SWarner Losh return (ENXIO); 5887bec1dd5SJonathan Chen do { 58963fa9f4cSJonathan Chen if (0 != cardbus_read_tuple(cbdev, child, res, start, &off, 59063fa9f4cSJonathan Chen &tupleid, &len, tupledata)) { 59163fa9f4cSJonathan Chen device_printf(cbdev, "Failed to read CIS.\n"); 59263fa9f4cSJonathan Chen cardbus_read_tuple_finish(cbdev, child, rid, res); 593a3133b58SWarner Losh return (ENXIO); 59463fa9f4cSJonathan Chen } 5957bec1dd5SJonathan Chen 5967bec1dd5SJonathan Chen if (expect_linktarget && tupleid != CISTPL_LINKTARGET) { 597255b159fSJonathan Chen device_printf(cbdev, "Expecting link target, got 0x%x\n", 5987bec1dd5SJonathan Chen tupleid); 59963fa9f4cSJonathan Chen cardbus_read_tuple_finish(cbdev, child, rid, res); 600a3133b58SWarner Losh return (EINVAL); 6017bec1dd5SJonathan Chen } 602255b159fSJonathan Chen expect_linktarget = decode_tuple(cbdev, child, tupleid, len, 60363fa9f4cSJonathan Chen tupledata, start, &off, callbacks); 60463fa9f4cSJonathan Chen if (expect_linktarget != 0) { 60563fa9f4cSJonathan Chen cardbus_read_tuple_finish(cbdev, child, rid, res); 606a3133b58SWarner Losh return (expect_linktarget); 60763fa9f4cSJonathan Chen } 6087bec1dd5SJonathan Chen } while (tupleid != CISTPL_END); 60963fa9f4cSJonathan Chen cardbus_read_tuple_finish(cbdev, child, rid, res); 610a3133b58SWarner Losh return (0); 6110db7e66cSJonathan Chen } 6120db7e66cSJonathan Chen 61363fa9f4cSJonathan Chen static int 61463fa9f4cSJonathan Chen barsort(const void *a, const void *b) 61563fa9f4cSJonathan Chen { 61678b226dcSAlfred Perlstein return ((*(const struct resource_list_entry * const *)b)->count - 61778b226dcSAlfred Perlstein (*(const struct resource_list_entry * const *)a)->count); 61863fa9f4cSJonathan Chen } 61963fa9f4cSJonathan Chen 62063fa9f4cSJonathan Chen static int 62163fa9f4cSJonathan Chen cardbus_alloc_resources(device_t cbdev, device_t child) 62263fa9f4cSJonathan Chen { 62363fa9f4cSJonathan Chen struct cardbus_devinfo *dinfo = device_get_ivars(child); 62463fa9f4cSJonathan Chen int count; 62563fa9f4cSJonathan Chen struct resource_list_entry *rle; 62663fa9f4cSJonathan Chen struct resource_list_entry **barlist; 62763fa9f4cSJonathan Chen int tmp; 62863fa9f4cSJonathan Chen u_int32_t mem_psize = 0, mem_nsize = 0, io_size = 0; 62963fa9f4cSJonathan Chen struct resource *res; 63063fa9f4cSJonathan Chen u_int32_t start,end; 63121677473SWarner Losh int rid, flags; 63263fa9f4cSJonathan Chen 63363fa9f4cSJonathan Chen count = 0; 63421677473SWarner Losh SLIST_FOREACH(rle, &dinfo->pci.resources, link) { 63563fa9f4cSJonathan Chen count++; 63621677473SWarner Losh } 63763fa9f4cSJonathan Chen if (count == 0) 638a3133b58SWarner Losh return (0); 63963fa9f4cSJonathan Chen barlist = malloc(sizeof(struct resource_list_entry*) * count, M_DEVBUF, 64063fa9f4cSJonathan Chen M_WAITOK); 64163fa9f4cSJonathan Chen count = 0; 64221677473SWarner Losh SLIST_FOREACH(rle, &dinfo->pci.resources, link) { 64363fa9f4cSJonathan Chen barlist[count] = rle; 64463fa9f4cSJonathan Chen if (rle->type == SYS_RES_IOPORT) { 64563fa9f4cSJonathan Chen io_size += rle->count; 64663fa9f4cSJonathan Chen } else if (rle->type == SYS_RES_MEMORY) { 64763fa9f4cSJonathan Chen if (dinfo->mprefetchable & BARBIT(rle->rid)) 64863fa9f4cSJonathan Chen mem_psize += rle->count; 64963fa9f4cSJonathan Chen else 65063fa9f4cSJonathan Chen mem_nsize += rle->count; 65163fa9f4cSJonathan Chen } 65263fa9f4cSJonathan Chen count++; 65363fa9f4cSJonathan Chen } 65463fa9f4cSJonathan Chen 65563fa9f4cSJonathan Chen /* 65663fa9f4cSJonathan Chen * We want to allocate the largest resource first, so that our 65763fa9f4cSJonathan Chen * allocated memory is packed. 65863fa9f4cSJonathan Chen */ 65963fa9f4cSJonathan Chen qsort(barlist, count, sizeof(struct resource_list_entry*), barsort); 66063fa9f4cSJonathan Chen 66163fa9f4cSJonathan Chen /* Allocate prefetchable memory */ 66263fa9f4cSJonathan Chen flags = 0; 66363fa9f4cSJonathan Chen for (tmp = 0; tmp < count; tmp++) { 66463fa9f4cSJonathan Chen if (barlist[tmp]->res == NULL && 66563fa9f4cSJonathan Chen barlist[tmp]->type == SYS_RES_MEMORY && 66663fa9f4cSJonathan Chen dinfo->mprefetchable & BARBIT(barlist[tmp]->rid)) { 66763fa9f4cSJonathan Chen flags = rman_make_alignment_flags(barlist[tmp]->count); 66863fa9f4cSJonathan Chen break; 66963fa9f4cSJonathan Chen } 67063fa9f4cSJonathan Chen } 67163fa9f4cSJonathan Chen if (flags > 0) { /* If any prefetchable memory is requested... */ 67263fa9f4cSJonathan Chen /* 67363fa9f4cSJonathan Chen * First we allocate one big space for all resources of this 67463fa9f4cSJonathan Chen * type. We do this because our parent, pccbb, needs to open 67563fa9f4cSJonathan Chen * a window to forward all addresses within the window, and 67663fa9f4cSJonathan Chen * it would be best if nobody else has resources allocated 67763fa9f4cSJonathan Chen * within the window. 67863fa9f4cSJonathan Chen * (XXX: Perhaps there might be a better way to do this?) 67963fa9f4cSJonathan Chen */ 68063fa9f4cSJonathan Chen rid = 0; 68163fa9f4cSJonathan Chen res = bus_alloc_resource(cbdev, SYS_RES_MEMORY, &rid, 0, 68263fa9f4cSJonathan Chen (dinfo->mprefetchable & dinfo->mbelow1mb)?0xFFFFF:~0UL, 68363fa9f4cSJonathan Chen mem_psize, flags); 68463fa9f4cSJonathan Chen start = rman_get_start(res); 68563fa9f4cSJonathan Chen end = rman_get_end(res); 68663fa9f4cSJonathan Chen DEVPRINTF((cbdev, "Prefetchable memory at %x-%x\n", start, end)); 68763fa9f4cSJonathan Chen /* 68863fa9f4cSJonathan Chen * Now that we know the region is free, release it and hand it 68963fa9f4cSJonathan Chen * out piece by piece. 69063fa9f4cSJonathan Chen */ 69163fa9f4cSJonathan Chen bus_release_resource(cbdev, SYS_RES_MEMORY, rid, res); 69263fa9f4cSJonathan Chen for (tmp = 0; tmp < count; tmp++) { 69363fa9f4cSJonathan Chen if (barlist[tmp]->res == NULL && 69463fa9f4cSJonathan Chen barlist[tmp]->type == SYS_RES_MEMORY && 69563fa9f4cSJonathan Chen dinfo->mprefetchable & BARBIT(barlist[tmp]->rid)) { 69663fa9f4cSJonathan Chen barlist[tmp]->res = bus_alloc_resource(cbdev, 69763fa9f4cSJonathan Chen barlist[tmp]->type, 69863fa9f4cSJonathan Chen &barlist[tmp]->rid, start, end, 69963fa9f4cSJonathan Chen barlist[tmp]->count, 70063fa9f4cSJonathan Chen rman_make_alignment_flags( 70163fa9f4cSJonathan Chen barlist[tmp]->count)); 70263fa9f4cSJonathan Chen if (barlist[tmp]->res == NULL) { 70363fa9f4cSJonathan Chen mem_nsize += barlist[tmp]->count; 70463fa9f4cSJonathan Chen dinfo->mprefetchable &= 70563fa9f4cSJonathan Chen ~BARBIT(barlist[tmp]->rid); 70663fa9f4cSJonathan Chen DEVPRINTF((cbdev, "Cannot pre-allocate " 70763fa9f4cSJonathan Chen "prefetchable memory, will try as " 70863fa9f4cSJonathan Chen "non-prefetchable.\n")); 70921677473SWarner Losh } else { 71063fa9f4cSJonathan Chen barlist[tmp]->start = 71163fa9f4cSJonathan Chen rman_get_start(barlist[tmp]->res); 71263fa9f4cSJonathan Chen barlist[tmp]->end = 71363fa9f4cSJonathan Chen rman_get_end(barlist[tmp]->res); 71463fa9f4cSJonathan Chen pci_write_config(child, 71563fa9f4cSJonathan Chen barlist[tmp]->rid, 71663fa9f4cSJonathan Chen barlist[tmp]->start, 4); 71763fa9f4cSJonathan Chen DEVPRINTF((cbdev, "Prefetchable memory " 71863fa9f4cSJonathan Chen "rid=%x at %lx-%lx\n", 71963fa9f4cSJonathan Chen barlist[tmp]->rid, 72063fa9f4cSJonathan Chen barlist[tmp]->start, 72163fa9f4cSJonathan Chen barlist[tmp]->end)); 72263fa9f4cSJonathan Chen } 72363fa9f4cSJonathan Chen } 72463fa9f4cSJonathan Chen } 72521677473SWarner Losh } 72663fa9f4cSJonathan Chen 72763fa9f4cSJonathan Chen /* Allocate non-prefetchable memory */ 72863fa9f4cSJonathan Chen flags = 0; 72963fa9f4cSJonathan Chen for (tmp = 0; tmp < count; tmp++) { 73063fa9f4cSJonathan Chen if (barlist[tmp]->res == NULL && 73163fa9f4cSJonathan Chen barlist[tmp]->type == SYS_RES_MEMORY) { 73263fa9f4cSJonathan Chen flags = rman_make_alignment_flags(barlist[tmp]->count); 73363fa9f4cSJonathan Chen break; 73463fa9f4cSJonathan Chen } 73563fa9f4cSJonathan Chen } 73663fa9f4cSJonathan Chen if (flags > 0) { /* If any non-prefetchable memory is requested... */ 73763fa9f4cSJonathan Chen /* 73863fa9f4cSJonathan Chen * First we allocate one big space for all resources of this 73963fa9f4cSJonathan Chen * type. We do this because our parent, pccbb, needs to open 74063fa9f4cSJonathan Chen * a window to forward all addresses within the window, and 74163fa9f4cSJonathan Chen * it would be best if nobody else has resources allocated 74263fa9f4cSJonathan Chen * within the window. 74363fa9f4cSJonathan Chen * (XXX: Perhaps there might be a better way to do this?) 74463fa9f4cSJonathan Chen */ 74563fa9f4cSJonathan Chen rid = 0; 74663fa9f4cSJonathan Chen res = bus_alloc_resource(cbdev, SYS_RES_MEMORY, &rid, 0, 74763fa9f4cSJonathan Chen ((~dinfo->mprefetchable) & dinfo->mbelow1mb)?0xFFFFF:~0UL, 74863fa9f4cSJonathan Chen mem_nsize, flags); 74963fa9f4cSJonathan Chen start = rman_get_start(res); 75063fa9f4cSJonathan Chen end = rman_get_end(res); 75163fa9f4cSJonathan Chen DEVPRINTF((cbdev, "Non-prefetchable memory at %x-%x\n", 75263fa9f4cSJonathan Chen start, end)); 75363fa9f4cSJonathan Chen /* 75463fa9f4cSJonathan Chen * Now that we know the region is free, release it and hand it 75563fa9f4cSJonathan Chen * out piece by piece. 75663fa9f4cSJonathan Chen */ 75763fa9f4cSJonathan Chen bus_release_resource(cbdev, SYS_RES_MEMORY, rid, res); 75863fa9f4cSJonathan Chen for (tmp = 0; tmp < count; tmp++) { 75963fa9f4cSJonathan Chen if (barlist[tmp]->res == NULL && 76063fa9f4cSJonathan Chen barlist[tmp]->type == SYS_RES_MEMORY) { 76163fa9f4cSJonathan Chen barlist[tmp]->res = bus_alloc_resource(cbdev, 76263fa9f4cSJonathan Chen barlist[tmp]->type, &barlist[tmp]->rid, 76363fa9f4cSJonathan Chen start, end, barlist[tmp]->count, 76463fa9f4cSJonathan Chen rman_make_alignment_flags( 76563fa9f4cSJonathan Chen barlist[tmp]->count)); 76663fa9f4cSJonathan Chen if (barlist[tmp]->res == NULL) { 76763fa9f4cSJonathan Chen DEVPRINTF((cbdev, "Cannot pre-allocate " 76863fa9f4cSJonathan Chen "memory for cardbus device\n")); 769214c0b3dSWarner Losh free(barlist, M_DEVBUF); 7707ba175acSWarner Losh return (ENOMEM); 77163fa9f4cSJonathan Chen } 77263fa9f4cSJonathan Chen barlist[tmp]->start = 77363fa9f4cSJonathan Chen rman_get_start(barlist[tmp]->res); 77463fa9f4cSJonathan Chen barlist[tmp]->end = rman_get_end( 77563fa9f4cSJonathan Chen barlist[tmp]->res); 77663fa9f4cSJonathan Chen pci_write_config(child, barlist[tmp]->rid, 77763fa9f4cSJonathan Chen barlist[tmp]->start, 4); 77863fa9f4cSJonathan Chen DEVPRINTF((cbdev, "Non-prefetchable memory " 77963fa9f4cSJonathan Chen "rid=%x at %lx-%lx (%lx)\n", 78063fa9f4cSJonathan Chen barlist[tmp]->rid, barlist[tmp]->start, 78163fa9f4cSJonathan Chen barlist[tmp]->end, barlist[tmp]->count)); 78263fa9f4cSJonathan Chen } 78363fa9f4cSJonathan Chen } 78463fa9f4cSJonathan Chen } 78563fa9f4cSJonathan Chen 78663fa9f4cSJonathan Chen /* Allocate IO ports */ 78763fa9f4cSJonathan Chen flags = 0; 78863fa9f4cSJonathan Chen for (tmp = 0; tmp < count; tmp++) { 78963fa9f4cSJonathan Chen if (barlist[tmp]->res == NULL && 79063fa9f4cSJonathan Chen barlist[tmp]->type == SYS_RES_IOPORT) { 79163fa9f4cSJonathan Chen flags = rman_make_alignment_flags(barlist[tmp]->count); 79263fa9f4cSJonathan Chen break; 79363fa9f4cSJonathan Chen } 79463fa9f4cSJonathan Chen } 79563fa9f4cSJonathan Chen if (flags > 0) { /* If any IO port is requested... */ 79663fa9f4cSJonathan Chen /* 79763fa9f4cSJonathan Chen * First we allocate one big space for all resources of this 79863fa9f4cSJonathan Chen * type. We do this because our parent, pccbb, needs to open 79963fa9f4cSJonathan Chen * a window to forward all addresses within the window, and 80063fa9f4cSJonathan Chen * it would be best if nobody else has resources allocated 80163fa9f4cSJonathan Chen * within the window. 80263fa9f4cSJonathan Chen * (XXX: Perhaps there might be a better way to do this?) 80363fa9f4cSJonathan Chen */ 80463fa9f4cSJonathan Chen rid = 0; 80521677473SWarner Losh res = bus_alloc_resource(cbdev, SYS_RES_IOPORT, &rid, 0, 80621677473SWarner Losh (dinfo->ibelow1mb)?0xFFFFF:~0UL, io_size, flags); 80763fa9f4cSJonathan Chen start = rman_get_start(res); 80863fa9f4cSJonathan Chen end = rman_get_end(res); 80963fa9f4cSJonathan Chen DEVPRINTF((cbdev, "IO port at %x-%x\n", start, end)); 81063fa9f4cSJonathan Chen /* 81163fa9f4cSJonathan Chen * Now that we know the region is free, release it and hand it 81221677473SWarner Losh * out piece by piece. 81363fa9f4cSJonathan Chen */ 81463fa9f4cSJonathan Chen bus_release_resource(cbdev, SYS_RES_IOPORT, rid, res); 81563fa9f4cSJonathan Chen for (tmp = 0; tmp < count; tmp++) { 81663fa9f4cSJonathan Chen if (barlist[tmp]->res == NULL && 81763fa9f4cSJonathan Chen barlist[tmp]->type == SYS_RES_IOPORT) { 81863fa9f4cSJonathan Chen barlist[tmp]->res = bus_alloc_resource(cbdev, 81963fa9f4cSJonathan Chen barlist[tmp]->type, &barlist[tmp]->rid, 82063fa9f4cSJonathan Chen start, end, barlist[tmp]->count, 82163fa9f4cSJonathan Chen rman_make_alignment_flags( 82263fa9f4cSJonathan Chen barlist[tmp]->count)); 82363fa9f4cSJonathan Chen if (barlist[tmp]->res == NULL) { 82463fa9f4cSJonathan Chen DEVPRINTF((cbdev, "Cannot pre-allocate " 82563fa9f4cSJonathan Chen "IO port for cardbus device\n")); 826214c0b3dSWarner Losh free(barlist, M_DEVBUF); 827a3133b58SWarner Losh return (ENOMEM); 82863fa9f4cSJonathan Chen } 82963fa9f4cSJonathan Chen barlist[tmp]->start = 83063fa9f4cSJonathan Chen rman_get_start(barlist[tmp]->res); 83163fa9f4cSJonathan Chen barlist[tmp]->end = 83263fa9f4cSJonathan Chen rman_get_end(barlist[tmp]->res); 83363fa9f4cSJonathan Chen pci_write_config(child, barlist[tmp]->rid, 83463fa9f4cSJonathan Chen barlist[tmp]->start, 4); 83563fa9f4cSJonathan Chen DEVPRINTF((cbdev, "IO port rid=%x at %lx-%lx\n", 83663fa9f4cSJonathan Chen barlist[tmp]->rid, barlist[tmp]->start, 83763fa9f4cSJonathan Chen barlist[tmp]->end)); 83863fa9f4cSJonathan Chen } 83963fa9f4cSJonathan Chen } 84063fa9f4cSJonathan Chen } 84163fa9f4cSJonathan Chen 84263fa9f4cSJonathan Chen /* Allocate IRQ */ 84363fa9f4cSJonathan Chen rid = 0; 84463fa9f4cSJonathan Chen res = bus_alloc_resource(cbdev, SYS_RES_IRQ, &rid, 0, ~0UL, 1, 84563fa9f4cSJonathan Chen RF_SHAREABLE); 84621677473SWarner Losh resource_list_add(&dinfo->pci.resources, SYS_RES_IRQ, rid, 84721677473SWarner Losh rman_get_start(res), rman_get_end(res), 1); 84821677473SWarner Losh rle = resource_list_find(&dinfo->pci.resources, SYS_RES_IRQ, rid); 84921677473SWarner Losh rle->res = res; 85021677473SWarner Losh dinfo->pci.cfg.intline = rman_get_start(res); 85121677473SWarner Losh pci_write_config(child, PCIR_INTLINE, rman_get_start(res), 1); 85263fa9f4cSJonathan Chen 853214c0b3dSWarner Losh free(barlist, M_DEVBUF); 854a3133b58SWarner Losh return (0); 85563fa9f4cSJonathan Chen } 85663fa9f4cSJonathan Chen 85763fa9f4cSJonathan Chen /* 85863fa9f4cSJonathan Chen * Adding a memory/io resource (sans CIS) 85963fa9f4cSJonathan Chen */ 86063fa9f4cSJonathan Chen 86163fa9f4cSJonathan Chen static void 86263fa9f4cSJonathan Chen cardbus_add_map(device_t cbdev, device_t child, int reg) 86363fa9f4cSJonathan Chen { 86463fa9f4cSJonathan Chen struct cardbus_devinfo *dinfo = device_get_ivars(child); 86521677473SWarner Losh struct resource_list_entry *rle; 86663fa9f4cSJonathan Chen u_int32_t size; 86763fa9f4cSJonathan Chen u_int32_t testval; 86863fa9f4cSJonathan Chen int type; 86963fa9f4cSJonathan Chen 87021677473SWarner Losh SLIST_FOREACH(rle, &dinfo->pci.resources, link) { 87121677473SWarner Losh if (rle->rid == reg) 87263fa9f4cSJonathan Chen return; 87321677473SWarner Losh } 87463fa9f4cSJonathan Chen 87563fa9f4cSJonathan Chen if (reg == CARDBUS_ROM_REG) 87663fa9f4cSJonathan Chen testval = CARDBUS_ROM_ADDRMASK; 87763fa9f4cSJonathan Chen else 87863fa9f4cSJonathan Chen testval = ~0; 87963fa9f4cSJonathan Chen 88063fa9f4cSJonathan Chen pci_write_config(child, reg, testval, 4); 88163fa9f4cSJonathan Chen testval = pci_read_config(child, reg, 4); 88263fa9f4cSJonathan Chen 88363fa9f4cSJonathan Chen if (testval == ~0 || testval == 0) 88463fa9f4cSJonathan Chen return; 88563fa9f4cSJonathan Chen 88663fa9f4cSJonathan Chen if ((testval & 1) == 0) 88763fa9f4cSJonathan Chen type = SYS_RES_MEMORY; 88863fa9f4cSJonathan Chen else 88963fa9f4cSJonathan Chen type = SYS_RES_IOPORT; 89063fa9f4cSJonathan Chen 89163fa9f4cSJonathan Chen size = CARDBUS_MAPREG_MEM_SIZE(testval); 89221677473SWarner Losh device_printf(cbdev, "Resource not specified in CIS: id=%x, size=%x\n", 89321677473SWarner Losh reg, size); 89421677473SWarner Losh resource_list_add(&dinfo->pci.resources, type, reg, 0UL, ~0UL, size); 89563fa9f4cSJonathan Chen } 89663fa9f4cSJonathan Chen 89763fa9f4cSJonathan Chen static void 89863fa9f4cSJonathan Chen cardbus_pickup_maps(device_t cbdev, device_t child) 89963fa9f4cSJonathan Chen { 90063fa9f4cSJonathan Chen struct cardbus_devinfo *dinfo = device_get_ivars(child); 90163fa9f4cSJonathan Chen struct cardbus_quirk *q; 90263fa9f4cSJonathan Chen int reg; 90363fa9f4cSJonathan Chen 90463fa9f4cSJonathan Chen /* 90563fa9f4cSJonathan Chen * Try to pick up any resources that was not specified in CIS. 90663fa9f4cSJonathan Chen * Some devices (eg, 3c656) does not list all resources required by 90763fa9f4cSJonathan Chen * the driver in its CIS. 90863fa9f4cSJonathan Chen * XXX: should we do this or use quirks? 90963fa9f4cSJonathan Chen */ 9107ba175acSWarner Losh for (reg = 0; reg < dinfo->pci.cfg.nummaps; reg++) { 91163fa9f4cSJonathan Chen cardbus_add_map(cbdev, child, PCIR_MAPS + reg * 4); 91263fa9f4cSJonathan Chen } 91363fa9f4cSJonathan Chen 91463fa9f4cSJonathan Chen for (q = &cardbus_quirks[0]; q->devid; q++) { 91521677473SWarner Losh if (q->devid == ((dinfo->pci.cfg.device << 16) | dinfo->pci.cfg.vendor) 91663fa9f4cSJonathan Chen && q->type == CARDBUS_QUIRK_MAP_REG) { 91763fa9f4cSJonathan Chen cardbus_add_map(cbdev, child, q->arg1); 91863fa9f4cSJonathan Chen } 91963fa9f4cSJonathan Chen } 92063fa9f4cSJonathan Chen } 92163fa9f4cSJonathan Chen 9220c95c705SJonathan Chen int 923255b159fSJonathan Chen cardbus_cis_read(device_t cbdev, device_t child, u_int8_t id, 9240c95c705SJonathan Chen struct cis_tupleinfo **buff, int *nret) 9250c95c705SJonathan Chen { 9260c95c705SJonathan Chen struct tuple_callbacks cisread_callbacks[] = { 9270c95c705SJonathan Chen MAKETUPLE(NULL, nothing), 9280c95c705SJonathan Chen /* first entry will be overwritten */ 9290c95c705SJonathan Chen MAKETUPLE(NULL, nothing), 9300c95c705SJonathan Chen MAKETUPLE(DEVICE, nothing), 9310c95c705SJonathan Chen MAKETUPLE(LONG_LINK_CB, unhandled), 9320c95c705SJonathan Chen MAKETUPLE(INDIRECT, unhandled), 9330c95c705SJonathan Chen MAKETUPLE(CONFIG_CB, nothing), 9340c95c705SJonathan Chen MAKETUPLE(CFTABLE_ENTRY_CB, nothing), 9350c95c705SJonathan Chen MAKETUPLE(LONGLINK_MFC, unhandled), 9360c95c705SJonathan Chen MAKETUPLE(BAR, nothing), 9370c95c705SJonathan Chen MAKETUPLE(PWR_MGMNT, nothing), 9380c95c705SJonathan Chen MAKETUPLE(EXTDEVICE, nothing), 9390c95c705SJonathan Chen MAKETUPLE(CHECKSUM, nothing), 9400c95c705SJonathan Chen MAKETUPLE(LONGLINK_A, unhandled), 9410c95c705SJonathan Chen MAKETUPLE(LONGLINK_C, unhandled), 9420c95c705SJonathan Chen MAKETUPLE(LINKTARGET, nothing), 9430c95c705SJonathan Chen MAKETUPLE(NO_LINK, nothing), 9440c95c705SJonathan Chen MAKETUPLE(VERS_1, nothing), 9450c95c705SJonathan Chen MAKETUPLE(ALTSTR, nothing), 9460c95c705SJonathan Chen MAKETUPLE(DEVICE_A, nothing), 9470c95c705SJonathan Chen MAKETUPLE(JEDEC_C, nothing), 9480c95c705SJonathan Chen MAKETUPLE(JEDEC_A, nothing), 9490c95c705SJonathan Chen MAKETUPLE(CONFIG, nothing), 9500c95c705SJonathan Chen MAKETUPLE(CFTABLE_ENTRY, nothing), 9510c95c705SJonathan Chen MAKETUPLE(DEVICE_OC, nothing), 9520c95c705SJonathan Chen MAKETUPLE(DEVICE_OA, nothing), 9530c95c705SJonathan Chen MAKETUPLE(DEVICE_GEO, nothing), 9540c95c705SJonathan Chen MAKETUPLE(DEVICE_GEO_A, nothing), 9550c95c705SJonathan Chen MAKETUPLE(MANFID, nothing), 9560c95c705SJonathan Chen MAKETUPLE(FUNCID, nothing), 9570c95c705SJonathan Chen MAKETUPLE(FUNCE, nothing), 9580c95c705SJonathan Chen MAKETUPLE(SWIL, nothing), 9590c95c705SJonathan Chen MAKETUPLE(VERS_2, nothing), 9600c95c705SJonathan Chen MAKETUPLE(FORMAT, nothing), 9610c95c705SJonathan Chen MAKETUPLE(GEOMETRY, nothing), 9620c95c705SJonathan Chen MAKETUPLE(BYTEORDER, nothing), 9630c95c705SJonathan Chen MAKETUPLE(DATE, nothing), 9640c95c705SJonathan Chen MAKETUPLE(BATTERY, nothing), 9650c95c705SJonathan Chen MAKETUPLE(ORG, nothing), 9660c95c705SJonathan Chen MAKETUPLE(END, end), 9670c95c705SJonathan Chen MAKETUPLE(GENERIC, nothing), 9680c95c705SJonathan Chen }; 9690c95c705SJonathan Chen int ret; 9700c95c705SJonathan Chen 9710c95c705SJonathan Chen cisread_callbacks[0].id = id; 9720c95c705SJonathan Chen cisread_callbacks[0].name = "COPY"; 9730c95c705SJonathan Chen cisread_callbacks[0].func = decode_tuple_copy; 9740c95c705SJonathan Chen ncisread_buf = 0; 9750c95c705SJonathan Chen cisread_buf = NULL; 976255b159fSJonathan Chen ret = cardbus_parse_cis(cbdev, child, cisread_callbacks); 9770c95c705SJonathan Chen 9780c95c705SJonathan Chen *buff = cisread_buf; 9790c95c705SJonathan Chen *nret = ncisread_buf; 980a3133b58SWarner Losh return (ret); 9810c95c705SJonathan Chen } 9820c95c705SJonathan Chen 9830c95c705SJonathan Chen void 984255b159fSJonathan Chen cardbus_cis_free(device_t cbdev, struct cis_tupleinfo *buff, int *nret) 9850c95c705SJonathan Chen { 9860c95c705SJonathan Chen int i; 9876f39832cSPeter Wemm for (i = 0; i < *nret; i++) 9880c95c705SJonathan Chen free(buff[i].data, M_DEVBUF); 9896f39832cSPeter Wemm if (*nret > 0) 9900c95c705SJonathan Chen free(buff, M_DEVBUF); 9910c95c705SJonathan Chen } 9920c95c705SJonathan Chen 9930c95c705SJonathan Chen int 994255b159fSJonathan Chen cardbus_do_cis(device_t cbdev, device_t child) 9950c95c705SJonathan Chen { 99663fa9f4cSJonathan Chen int ret; 9970c95c705SJonathan Chen struct tuple_callbacks init_callbacks[] = { 9980c95c705SJonathan Chen MAKETUPLE(NULL, generic), 9990c95c705SJonathan Chen MAKETUPLE(DEVICE, generic), 10000c95c705SJonathan Chen MAKETUPLE(LONG_LINK_CB, unhandled), 10010c95c705SJonathan Chen MAKETUPLE(INDIRECT, unhandled), 10020c95c705SJonathan Chen MAKETUPLE(CONFIG_CB, generic), 10030c95c705SJonathan Chen MAKETUPLE(CFTABLE_ENTRY_CB, generic), 10040c95c705SJonathan Chen MAKETUPLE(LONGLINK_MFC, unhandled), 10050c95c705SJonathan Chen MAKETUPLE(BAR, bar), 10060c95c705SJonathan Chen MAKETUPLE(PWR_MGMNT, generic), 10070c95c705SJonathan Chen MAKETUPLE(EXTDEVICE, generic), 10080c95c705SJonathan Chen MAKETUPLE(CHECKSUM, generic), 10090c95c705SJonathan Chen MAKETUPLE(LONGLINK_A, unhandled), 10100c95c705SJonathan Chen MAKETUPLE(LONGLINK_C, unhandled), 10110c95c705SJonathan Chen MAKETUPLE(LINKTARGET, linktarget), 10120c95c705SJonathan Chen MAKETUPLE(NO_LINK, generic), 10130c95c705SJonathan Chen MAKETUPLE(VERS_1, vers_1), 10140c95c705SJonathan Chen MAKETUPLE(ALTSTR, generic), 10150c95c705SJonathan Chen MAKETUPLE(DEVICE_A, generic), 10160c95c705SJonathan Chen MAKETUPLE(JEDEC_C, generic), 10170c95c705SJonathan Chen MAKETUPLE(JEDEC_A, generic), 10180c95c705SJonathan Chen MAKETUPLE(CONFIG, generic), 10190c95c705SJonathan Chen MAKETUPLE(CFTABLE_ENTRY, generic), 10200c95c705SJonathan Chen MAKETUPLE(DEVICE_OC, generic), 10210c95c705SJonathan Chen MAKETUPLE(DEVICE_OA, generic), 10220c95c705SJonathan Chen MAKETUPLE(DEVICE_GEO, generic), 10230c95c705SJonathan Chen MAKETUPLE(DEVICE_GEO_A, generic), 10240c95c705SJonathan Chen MAKETUPLE(MANFID, manfid), 10250c95c705SJonathan Chen MAKETUPLE(FUNCID, funcid), 10260c95c705SJonathan Chen MAKETUPLE(FUNCE, funce), 10270c95c705SJonathan Chen MAKETUPLE(SWIL, generic), 10280c95c705SJonathan Chen MAKETUPLE(VERS_2, generic), 10290c95c705SJonathan Chen MAKETUPLE(FORMAT, generic), 10300c95c705SJonathan Chen MAKETUPLE(GEOMETRY, generic), 10310c95c705SJonathan Chen MAKETUPLE(BYTEORDER, generic), 10320c95c705SJonathan Chen MAKETUPLE(DATE, generic), 10330c95c705SJonathan Chen MAKETUPLE(BATTERY, generic), 10340c95c705SJonathan Chen MAKETUPLE(ORG, generic), 10350c95c705SJonathan Chen MAKETUPLE(END, end), 10360c95c705SJonathan Chen MAKETUPLE(GENERIC, generic), 10370c95c705SJonathan Chen }; 103863fa9f4cSJonathan Chen 103963fa9f4cSJonathan Chen ret = cardbus_parse_cis(cbdev, child, init_callbacks); 104063fa9f4cSJonathan Chen if (ret < 0) 1041a3133b58SWarner Losh return (ret); 104263fa9f4cSJonathan Chen cardbus_pickup_maps(cbdev, child); 1043a3133b58SWarner Losh return (cardbus_alloc_resources(cbdev, child)); 10440c95c705SJonathan Chen } 1045