pci_cfgreg.c revision 181933
1100894Srwatson/*- 2100894Srwatson * Copyright (c) 1997, Stefan Esser <se@freebsd.org> 3100894Srwatson * Copyright (c) 2000, Michael Smith <msmith@freebsd.org> 4100894Srwatson * Copyright (c) 2000, BSDi 5100894Srwatson * Copyright (c) 2004, Scott Long <scottl@freebsd.org> 6100894Srwatson * All rights reserved. 7100894Srwatson * 8100894Srwatson * Redistribution and use in source and binary forms, with or without 9100894Srwatson * modification, are permitted provided that the following conditions 10106392Srwatson * are met: 11106392Srwatson * 1. Redistributions of source code must retain the above copyright 12106392Srwatson * notice unmodified, this list of conditions, and the following 13106392Srwatson * disclaimer. 14100894Srwatson * 2. Redistributions in binary form must reproduce the above copyright 15100894Srwatson * notice, this list of conditions and the following disclaimer in the 16100894Srwatson * documentation and/or other materials provided with the distribution. 17100894Srwatson * 18100894Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19100894Srwatson * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20100894Srwatson * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21100894Srwatson * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22100894Srwatson * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23100894Srwatson * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24100894Srwatson * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25100894Srwatson * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26100894Srwatson * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27100894Srwatson * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28100894Srwatson */ 29100894Srwatson 30100894Srwatson#include <sys/cdefs.h> 31100894Srwatson__FBSDID("$FreeBSD: head/sys/i386/pci/pci_cfgreg.c 181933 2008-08-20 18:18:17Z jhb $"); 32100894Srwatson 33100894Srwatson#include "opt_xbox.h" 34100894Srwatson 35100894Srwatson#include <sys/param.h> 36100894Srwatson#include <sys/systm.h> 37100894Srwatson#include <sys/bus.h> 38100894Srwatson#include <sys/lock.h> 39100894Srwatson#include <sys/mutex.h> 40100894Srwatson#include <sys/malloc.h> 41100894Srwatson#include <sys/queue.h> 42100894Srwatson#include <dev/pci/pcivar.h> 43100894Srwatson#include <dev/pci/pcireg.h> 44100894Srwatson#include <machine/pci_cfgreg.h> 45100894Srwatson#include <machine/pc/bios.h> 46104300Sphk 47101173Srwatson#include <vm/vm.h> 48100894Srwatson#include <vm/vm_param.h> 49106856Srwatson#include <vm/vm_kern.h> 50100979Srwatson#include <vm/vm_extern.h> 51106468Srwatson#include <vm/pmap.h> 52100979Srwatson#include <machine/pmap.h> 53100979Srwatson 54102949Sbde#ifdef XBOX 55100979Srwatson#include <machine/xbox.h> 56100979Srwatson#endif 57101712Srwatson 58100979Srwatson#define PRVERB(a) do { \ 59100979Srwatson if (bootverbose) \ 60100894Srwatson printf a ; \ 61100894Srwatson} while(0) 62100979Srwatson 63100979Srwatson#define PCIE_CACHE 8 64100979Srwatsonstruct pcie_cfg_elem { 65100979Srwatson TAILQ_ENTRY(pcie_cfg_elem) elem; 66100979Srwatson vm_offset_t vapage; 67100979Srwatson vm_paddr_t papage; 68100979Srwatson}; 69100979Srwatson 70100894Srwatsonenum { 71100979Srwatson CFGMECH_NONE = 0, 72100979Srwatson CFGMECH_1, 73100979Srwatson CFGMECH_2, 74100979Srwatson CFGMECH_PCIE, 75100979Srwatson}; 76100979Srwatson 77100979Srwatsonstatic TAILQ_HEAD(pcie_cfg_list, pcie_cfg_elem) pcie_list[MAXCPU]; 78100979Srwatsonstatic uint64_t pciebar; 79100979Srwatsonstatic int cfgmech; 80100979Srwatsonstatic int devmax; 81100979Srwatsonstatic struct mtx pcicfg_mtx; 82100979Srwatson 83100979Srwatsonstatic int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes); 84100979Srwatsonstatic void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes); 85100979Srwatson#ifndef XEN 86100979Srwatsonstatic int pcireg_cfgopen(void); 87100979Srwatson 88100979Srwatsonstatic int pciereg_cfgopen(void); 89101712Srwatson#endif 90101712Srwatsonstatic int pciereg_cfgread(int bus, int slot, int func, int reg, 91101712Srwatson int bytes); 92101712Srwatsonstatic void pciereg_cfgwrite(int bus, int slot, int func, int reg, 93101712Srwatson int data, int bytes); 94101712Srwatson 95101712Srwatson/* 96100979Srwatson * Some BIOS writers seem to want to ignore the spec and put 97100979Srwatson * 0 in the intline rather than 255 to indicate none. Some use 98100979Srwatson * numbers in the range 128-254 to indicate something strange and 99100979Srwatson * apparently undocumented anywhere. Assume these are completely bogus 100104517Srwatson * and map them to 255, which means "none". 101100979Srwatson */ 102100979Srwatsonstatic __inline int 103100979Srwatsonpci_i386_map_intline(int line) 104105497Srwatson{ 105100979Srwatson if (line == 0 || line >= 128) 106100979Srwatson return (PCI_INVALID_IRQ); 107100979Srwatson return (line); 108100979Srwatson} 109100979Srwatson 110105959Srwatson#ifndef XEN 111105959Srwatsonstatic u_int16_t 112105959Srwatsonpcibios_get_version(void) 113105959Srwatson{ 114105959Srwatson struct bios_regs args; 115100979Srwatson 116100979Srwatson if (PCIbios.ventry == 0) { 117105988Srwatson PRVERB(("pcibios: No call entry point\n")); 118105988Srwatson return (0); 119105988Srwatson } 120105988Srwatson args.eax = PCIBIOS_BIOS_PRESENT; 121105988Srwatson if (bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL))) { 122105988Srwatson PRVERB(("pcibios: BIOS_PRESENT call failed\n")); 123100979Srwatson return (0); 124100979Srwatson } 125100979Srwatson if (args.edx != 0x20494350) { 126100979Srwatson PRVERB(("pcibios: BIOS_PRESENT didn't return 'PCI ' in edx\n")); 127100979Srwatson return (0); 128100979Srwatson } 129100979Srwatson return (args.ebx & 0xffff); 130100979Srwatson} 131100979Srwatson#endif 132100979Srwatson 133103513Srwatson/* 134103513Srwatson * Initialise access to PCI configuration space 135103513Srwatson */ 136104236Srwatsonint 137103513Srwatsonpci_cfgregopen(void) 138100979Srwatson{ 139100979Srwatson#ifdef XEN 140100979Srwatson return (0); 141100979Srwatson#else 142100979Srwatson static int opened = 0; 143100979Srwatson u_int16_t vid, did; 144100979Srwatson u_int16_t v; 145100979Srwatson 146100979Srwatson if (opened) 147100979Srwatson return(1); 148106045Srwatson 149106045Srwatson if (pcireg_cfgopen() == 0) 150106045Srwatson return(0); 151106045Srwatson 152106025Srwatson v = pcibios_get_version(); 153106045Srwatson if (v > 0) 154103514Srwatson PRVERB(("pcibios: BIOS version %x.%02x\n", (v & 0xff00) >> 8, 155103514Srwatson v & 0xff)); 156104236Srwatson mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN); 157103514Srwatson opened = 1; 158103136Srwatson 159103136Srwatson /* $PIR requires PCI BIOS 2.10 or greater. */ 160103136Srwatson if (v >= 0x0210) 161103136Srwatson pci_pir_open(); 162101892Srwatson 163100979Srwatson /* 164100979Srwatson * Grope around in the PCI config space to see if this is a 165100979Srwatson * chipset that is capable of doing memory-mapped config cycles. 166100979Srwatson * This also implies that it can do PCIe extended config cycles. 167101988Srwatson */ 168104268Srwatson 169104268Srwatson /* Check for supported chipsets */ 170104268Srwatson vid = pci_cfgregread(0, 0, 0, PCIR_VENDOR, 2); 171104268Srwatson did = pci_cfgregread(0, 0, 0, PCIR_DEVICE, 2); 172104268Srwatson switch (vid) { 173104268Srwatson case 0x8086: 174104268Srwatson switch (did) { 175104268Srwatson case 0x3590: 176104268Srwatson case 0x3592: 177104268Srwatson /* Intel 7520 or 7320 */ 178104517Srwatson pciebar = pci_cfgregread(0, 0, 0, 0xce, 2) << 16; 179104517Srwatson pciereg_cfgopen(); 180104517Srwatson break; 181100979Srwatson case 0x2580: 182100979Srwatson case 0x2584: 183100979Srwatson case 0x2590: 184104517Srwatson /* Intel 915, 925, or 915GM */ 185104517Srwatson pciebar = pci_cfgregread(0, 0, 0, 0x48, 4); 186100979Srwatson pciereg_cfgopen(); 187104517Srwatson break; 188100979Srwatson case 0x25d0: 189104517Srwatson case 0x25d4: 190100979Srwatson case 0x25d8: 191104517Srwatson /* Intel 5000Z/V/P */ 192100979Srwatson pciebar = pci_cfgregread(0, 16, 0, 0x64, 4) << 16; 193104517Srwatson pciereg_cfgopen(); 194100979Srwatson break; 195104517Srwatson } 196100979Srwatson } 197104517Srwatson 198100979Srwatson return(1); 199104517Srwatson#endif 200100979Srwatson} 201104517Srwatson 202100979Srwatson/* 203104517Srwatson * Read configuration space register 204100979Srwatson */ 205104517Srwatsonu_int32_t 206100979Srwatsonpci_cfgregread(int bus, int slot, int func, int reg, int bytes) 207101988Srwatson{ 208100979Srwatson uint32_t line; 209100979Srwatson 210100979Srwatson /* 211100979Srwatson * Some BIOS writers seem to want to ignore the spec and put 212100979Srwatson * 0 in the intline rather than 255 to indicate none. The rest of 213104546Srwatson * the code uses 255 as an invalid IRQ. 214104546Srwatson */ 215100979Srwatson if (reg == PCIR_INTLINE && bytes == 1) { 216100979Srwatson line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1); 217100979Srwatson return (pci_i386_map_intline(line)); 218104541Srwatson } 219104541Srwatson return (pcireg_cfgread(bus, slot, func, reg, bytes)); 220105988Srwatson} 221105988Srwatson 222105988Srwatson/* 223100979Srwatson * Write configuration space register 224105694Srwatson */ 225100979Srwatsonvoid 226100979Srwatsonpci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes) 227106856Srwatson{ 228106856Srwatson 229106856Srwatson pcireg_cfgwrite(bus, slot, func, reg, data, bytes); 230106856Srwatson} 231106856Srwatson 232106856Srwatson/* 233106856Srwatson * Configuration space access using direct register operations 234106856Srwatson */ 235106856Srwatson 236106856Srwatson/* enable configuration space accesses and return data port address */ 237106856Srwatsonstatic int 238106856Srwatsonpci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) 239100979Srwatson{ 240100979Srwatson int dataport = 0; 241106856Srwatson 242100979Srwatson#ifdef XBOX 243100979Srwatson if (arch_i386_is_xbox) { 244100979Srwatson /* 245106856Srwatson * The Xbox MCPX chipset is a derivative of the nForce 1 246106856Srwatson * chipset. It almost has the same bus layout; some devices 247106856Srwatson * cannot be used, because they have been removed. 248106856Srwatson */ 249106856Srwatson 250106856Srwatson /* 251106856Srwatson * Devices 00:00.1 and 00:00.2 used to be memory controllers on 252106856Srwatson * the nForce chipset, but on the Xbox, using them will lockup 253106856Srwatson * the chipset. 254106856Srwatson */ 255106856Srwatson if (bus == 0 && slot == 0 && (func == 1 || func == 2)) 256106856Srwatson return dataport; 257106856Srwatson 258106856Srwatson /* 259106856Srwatson * Bus 1 only contains a VGA controller at 01:00.0. When you try 260106856Srwatson * to probe beyond that device, you only get garbage, which 261106856Srwatson * could cause lockups. 262106856Srwatson */ 263106856Srwatson if (bus == 1 && (slot != 0 || func != 0)) 264106856Srwatson return dataport; 265106856Srwatson 266106856Srwatson /* 267106856Srwatson * Bus 2 used to contain the AGP controller, but the Xbox MCPX 268106856Srwatson * doesn't have one. Probing it can cause lockups. 269106856Srwatson */ 270106856Srwatson if (bus >= 2) 271106856Srwatson return dataport; 272106856Srwatson } 273106856Srwatson#endif 274106856Srwatson 275106856Srwatson if (bus <= PCI_BUSMAX 276100979Srwatson && slot < devmax 277100979Srwatson && func <= PCI_FUNCMAX 278100979Srwatson && reg <= PCI_REGMAX 279100979Srwatson && bytes != 3 280100979Srwatson && (unsigned) bytes <= 4 281100979Srwatson && (reg & (bytes - 1)) == 0) { 282100979Srwatson switch (cfgmech) { 283100979Srwatson case CFGMECH_1: 284100979Srwatson outl(CONF1_ADDR_PORT, (1 << 31) 285106856Srwatson | (bus << 16) | (slot << 11) 286106856Srwatson | (func << 8) | (reg & ~0x03)); 287106856Srwatson dataport = CONF1_DATA_PORT + (reg & 0x03); 288100979Srwatson break; 289100979Srwatson case CFGMECH_2: 290100979Srwatson outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); 291100979Srwatson outb(CONF2_FORWARD_PORT, bus); 292100979Srwatson dataport = 0xc000 | (slot << 8) | reg; 293100979Srwatson break; 294100979Srwatson } 295100979Srwatson } 296100979Srwatson return (dataport); 297100979Srwatson} 298100979Srwatson 299100979Srwatson/* disable configuration space accesses */ 300100979Srwatsonstatic void 301100979Srwatsonpci_cfgdisable(void) 302100979Srwatson{ 303100979Srwatson switch (cfgmech) { 304100979Srwatson case CFGMECH_1: 305100979Srwatson /* 306100979Srwatson * Do nothing for the config mechanism 1 case. 307100979Srwatson * Writing a 0 to the address port can apparently 308100979Srwatson * confuse some bridges and cause spurious 309100979Srwatson * access failures. 310100979Srwatson */ 311100979Srwatson break; 312100979Srwatson case CFGMECH_2: 313100979Srwatson outb(CONF2_ENABLE_PORT, 0); 314100979Srwatson break; 315100979Srwatson } 316100979Srwatson} 317100979Srwatson 318100979Srwatsonstatic int 319100979Srwatsonpcireg_cfgread(int bus, int slot, int func, int reg, int bytes) 320100979Srwatson{ 321100979Srwatson int data = -1; 322100979Srwatson int port; 323100979Srwatson 324100979Srwatson if (cfgmech == CFGMECH_PCIE) { 325100979Srwatson data = pciereg_cfgread(bus, slot, func, reg, bytes); 326100979Srwatson return (data); 327100979Srwatson } 328100979Srwatson 329100979Srwatson mtx_lock_spin(&pcicfg_mtx); 330100979Srwatson port = pci_cfgenable(bus, slot, func, reg, bytes); 331105694Srwatson if (port != 0) { 332105694Srwatson switch (bytes) { 333105694Srwatson case 1: 334105694Srwatson data = inb(port); 335105694Srwatson break; 336105694Srwatson case 2: 337105694Srwatson data = inw(port); 338105694Srwatson break; 339105694Srwatson case 4: 340105694Srwatson data = inl(port); 341105694Srwatson break; 342105694Srwatson } 343105694Srwatson pci_cfgdisable(); 344105694Srwatson } 345105694Srwatson mtx_unlock_spin(&pcicfg_mtx); 346105694Srwatson return (data); 347105694Srwatson} 348105694Srwatson 349105694Srwatsonstatic void 350105694Srwatsonpcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 351105694Srwatson{ 352105694Srwatson int port; 353105694Srwatson 354105694Srwatson if (cfgmech == CFGMECH_PCIE) { 355105694Srwatson pciereg_cfgwrite(bus, slot, func, reg, data, bytes); 356105694Srwatson return; 357105694Srwatson } 358105694Srwatson 359105694Srwatson mtx_lock_spin(&pcicfg_mtx); 360105694Srwatson port = pci_cfgenable(bus, slot, func, reg, bytes); 361105694Srwatson if (port != 0) { 362105694Srwatson switch (bytes) { 363105694Srwatson case 1: 364105694Srwatson outb(port, data); 365105694Srwatson break; 366105694Srwatson case 2: 367105694Srwatson outw(port, data); 368105694Srwatson break; 369105694Srwatson case 4: 370105694Srwatson outl(port, data); 371105694Srwatson break; 372105694Srwatson } 373105694Srwatson pci_cfgdisable(); 374105694Srwatson } 375105694Srwatson mtx_unlock_spin(&pcicfg_mtx); 376105694Srwatson} 377105694Srwatson 378105694Srwatson#ifndef XEN 379105694Srwatson/* check whether the configuration mechanism has been correctly identified */ 380105694Srwatsonstatic int 381105694Srwatsonpci_cfgcheck(int maxdev) 382105694Srwatson{ 383105694Srwatson uint32_t id, class; 384105694Srwatson uint8_t header; 385105694Srwatson uint8_t device; 386105694Srwatson int port; 387105694Srwatson 388105694Srwatson if (bootverbose) 389105694Srwatson printf("pci_cfgcheck:\tdevice "); 390105694Srwatson 391105694Srwatson for (device = 0; device < maxdev; device++) { 392105694Srwatson if (bootverbose) 393105694Srwatson printf("%d ", device); 394105694Srwatson 395105694Srwatson port = pci_cfgenable(0, device, 0, 0, 4); 396105694Srwatson id = inl(port); 397105694Srwatson if (id == 0 || id == 0xffffffff) 398105694Srwatson continue; 399105694Srwatson 400105694Srwatson port = pci_cfgenable(0, device, 0, 8, 4); 401105694Srwatson class = inl(port) >> 8; 402105694Srwatson if (bootverbose) 403105694Srwatson printf("[class=%06x] ", class); 404105694Srwatson if (class == 0 || (class & 0xf870ff) != 0) 405105694Srwatson continue; 406105694Srwatson 407105694Srwatson port = pci_cfgenable(0, device, 0, 14, 1); 408105694Srwatson header = inb(port); 409105694Srwatson if (bootverbose) 410105694Srwatson printf("[hdr=%02x] ", header); 411105694Srwatson if ((header & 0x7e) != 0) 412105694Srwatson continue; 413105694Srwatson 414105694Srwatson if (bootverbose) 415105694Srwatson printf("is there (id=%08x)\n", id); 416105694Srwatson 417105694Srwatson pci_cfgdisable(); 418105694Srwatson return (1); 419105694Srwatson } 420100979Srwatson if (bootverbose) 421100979Srwatson printf("-- nothing found\n"); 422100979Srwatson 423100979Srwatson pci_cfgdisable(); 424100979Srwatson return (0); 425100979Srwatson} 426100979Srwatson 427100979Srwatsonstatic int 428100979Srwatsonpcireg_cfgopen(void) 429100979Srwatson{ 430100979Srwatson uint32_t mode1res, oldval1; 431100979Srwatson uint8_t mode2res, oldval2; 432100979Srwatson 433100979Srwatson /* Check for type #1 first. */ 434100979Srwatson oldval1 = inl(CONF1_ADDR_PORT); 435100979Srwatson 436100979Srwatson if (bootverbose) { 437100979Srwatson printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n", 438100979Srwatson oldval1); 439100979Srwatson } 440100979Srwatson 441100979Srwatson cfgmech = CFGMECH_1; 442100979Srwatson devmax = 32; 443100979Srwatson 444100979Srwatson outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 445100979Srwatson DELAY(1); 446100979Srwatson mode1res = inl(CONF1_ADDR_PORT); 447100979Srwatson outl(CONF1_ADDR_PORT, oldval1); 448100979Srwatson 449100979Srwatson if (bootverbose) 450100979Srwatson printf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n", mode1res, 451100979Srwatson CONF1_ENABLE_CHK); 452100979Srwatson 453100979Srwatson if (mode1res) { 454100979Srwatson if (pci_cfgcheck(32)) 455100979Srwatson return (cfgmech); 456100979Srwatson } 457100979Srwatson 458100979Srwatson outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); 459100979Srwatson mode1res = inl(CONF1_ADDR_PORT); 460100979Srwatson outl(CONF1_ADDR_PORT, oldval1); 461100894Srwatson 462100979Srwatson if (bootverbose) 463100979Srwatson printf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n", mode1res, 464100979Srwatson CONF1_ENABLE_CHK1); 465100979Srwatson 466100979Srwatson if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { 467100979Srwatson if (pci_cfgcheck(32)) 468100979Srwatson return (cfgmech); 469100979Srwatson } 470100979Srwatson 471100979Srwatson /* Type #1 didn't work, so try type #2. */ 472100979Srwatson oldval2 = inb(CONF2_ENABLE_PORT); 473100979Srwatson 474100979Srwatson if (bootverbose) { 475100979Srwatson printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", 476100979Srwatson oldval2); 477100979Srwatson } 478100979Srwatson 479100979Srwatson if ((oldval2 & 0xf0) == 0) { 480100979Srwatson 481100979Srwatson cfgmech = CFGMECH_2; 482100979Srwatson devmax = 16; 483100979Srwatson 484100979Srwatson outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); 485100979Srwatson mode2res = inb(CONF2_ENABLE_PORT); 486100979Srwatson outb(CONF2_ENABLE_PORT, oldval2); 487100979Srwatson 488100979Srwatson if (bootverbose) 489100979Srwatson printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", 490100979Srwatson mode2res, CONF2_ENABLE_CHK); 491100979Srwatson 492100979Srwatson if (mode2res == CONF2_ENABLE_RES) { 493100979Srwatson if (bootverbose) 494100979Srwatson printf("pci_open(2a):\tnow trying mechanism 2\n"); 495100979Srwatson 496100979Srwatson if (pci_cfgcheck(16)) 497100979Srwatson return (cfgmech); 498100979Srwatson } 499100979Srwatson } 500100979Srwatson 501100979Srwatson /* Nothing worked, so punt. */ 502106856Srwatson cfgmech = CFGMECH_NONE; 503100979Srwatson devmax = 0; 504100979Srwatson return (cfgmech); 505100979Srwatson} 506100979Srwatson 507100979Srwatsonstatic int 508100979Srwatsonpciereg_cfgopen(void) 509100979Srwatson{ 510100979Srwatson struct pcie_cfg_list *pcielist; 511100979Srwatson struct pcie_cfg_elem *pcie_array, *elem; 512100979Srwatson#ifdef SMP 513100979Srwatson struct pcpu *pc; 514100979Srwatson#endif 515100979Srwatson vm_offset_t va; 516100979Srwatson int i; 517100979Srwatson 518100979Srwatson#ifndef PAE 519100979Srwatson if (pciebar >= 0x100000000) { 520100979Srwatson if (bootverbose) 521100979Srwatson printf( 522100979Srwatson "PCI: Memory Mapped PCI configuration area base 0x%jx too high\n", 523100979Srwatson (uintmax_t)pciebar); 524100979Srwatson pciebar = 0; 525100979Srwatson return (0); 526100979Srwatson } 527100979Srwatson#endif 528100979Srwatson 529100979Srwatson if (bootverbose) 530100979Srwatson printf("Setting up PCIe mappings for BAR 0x%jx\n", 531100979Srwatson (uintmax_t)pciebar); 532100979Srwatson 533100979Srwatson#ifdef SMP 534100979Srwatson SLIST_FOREACH(pc, &cpuhead, pc_allcpu) 535100979Srwatson#endif 536100979Srwatson { 537104520Srwatson 538104520Srwatson pcie_array = malloc(sizeof(struct pcie_cfg_elem) * PCIE_CACHE, 539104520Srwatson M_DEVBUF, M_NOWAIT); 540104520Srwatson if (pcie_array == NULL) 541104520Srwatson return (0); 542106856Srwatson 543104520Srwatson va = kmem_alloc_nofault(kernel_map, PCIE_CACHE * PAGE_SIZE); 544104520Srwatson if (va == 0) { 545104520Srwatson free(pcie_array, M_DEVBUF); 546104520Srwatson return (0); 547100979Srwatson } 548100979Srwatson 549100979Srwatson#ifdef SMP 550100979Srwatson pcielist = &pcie_list[pc->pc_cpuid]; 551104520Srwatson#else 552104520Srwatson pcielist = &pcie_list[0]; 553100979Srwatson#endif 554104520Srwatson TAILQ_INIT(pcielist); 555100979Srwatson for (i = 0; i < PCIE_CACHE; i++) { 556104520Srwatson elem = &pcie_array[i]; 557104520Srwatson elem->vapage = va + (i * PAGE_SIZE); 558104520Srwatson elem->papage = 0; 559104520Srwatson TAILQ_INSERT_HEAD(pcielist, elem, elem); 560104520Srwatson } 561104520Srwatson } 562100979Srwatson 563104520Srwatson 564100979Srwatson cfgmech = CFGMECH_PCIE; 565100979Srwatson devmax = 32; 566100979Srwatson return (1); 567100979Srwatson} 568106856Srwatson#endif /* !XEN */ 569106856Srwatson 570100979Srwatson#define PCIE_PADDR(bar, reg, bus, slot, func) \ 571100979Srwatson ((bar) | \ 572100979Srwatson (((bus) & 0xff) << 20) | \ 573100979Srwatson (((slot) & 0x1f) << 15) | \ 574100979Srwatson (((func) & 0x7) << 12) | \ 575100979Srwatson ((reg) & 0xfff)) 576100979Srwatson 577100979Srwatson/* 578100979Srwatson * Find an element in the cache that matches the physical page desired, or 579100979Srwatson * create a new mapping from the least recently used element. 580100979Srwatson * A very simple LRU algorithm is used here, does it need to be more 581100979Srwatson * efficient? 582100979Srwatson */ 583100979Srwatsonstatic __inline struct pcie_cfg_elem * 584100979Srwatsonpciereg_findelem(vm_paddr_t papage) 585100979Srwatson{ 586100979Srwatson struct pcie_cfg_list *pcielist; 587100979Srwatson struct pcie_cfg_elem *elem; 588100979Srwatson 589100979Srwatson pcielist = &pcie_list[PCPU_GET(cpuid)]; 590100979Srwatson TAILQ_FOREACH(elem, pcielist, elem) { 591100979Srwatson if (elem->papage == papage) 592100979Srwatson break; 593100979Srwatson } 594100979Srwatson 595100979Srwatson if (elem == NULL) { 596100979Srwatson elem = TAILQ_LAST(pcielist, pcie_cfg_list); 597100979Srwatson if (elem->papage != 0) { 598100979Srwatson pmap_kremove(elem->vapage); 599100979Srwatson invlpg(elem->vapage); 600100979Srwatson } 601100979Srwatson pmap_kenter(elem->vapage, papage); 602100979Srwatson elem->papage = papage; 603100979Srwatson } 604100979Srwatson 605100979Srwatson if (elem != TAILQ_FIRST(pcielist)) { 606100979Srwatson TAILQ_REMOVE(pcielist, elem, elem); 607100979Srwatson TAILQ_INSERT_HEAD(pcielist, elem, elem); 608100979Srwatson } 609100979Srwatson return (elem); 610100979Srwatson} 611100979Srwatson 612100979Srwatsonstatic int 613100979Srwatsonpciereg_cfgread(int bus, int slot, int func, int reg, int bytes) 614100979Srwatson{ 615104521Srwatson struct pcie_cfg_elem *elem; 616104521Srwatson volatile vm_offset_t va; 617104521Srwatson vm_paddr_t pa, papage; 618104521Srwatson int data; 619104521Srwatson 620104521Srwatson critical_enter(); 621104521Srwatson pa = PCIE_PADDR(pciebar, reg, bus, slot, func); 622104521Srwatson papage = pa & ~PAGE_MASK; 623104521Srwatson elem = pciereg_findelem(papage); 624104521Srwatson va = elem->vapage | (pa & PAGE_MASK); 625104521Srwatson 626104521Srwatson switch (bytes) { 627104521Srwatson case 4: 628104521Srwatson data = *(volatile uint32_t *)(va); 629104521Srwatson break; 630104521Srwatson case 2: 631104521Srwatson data = *(volatile uint16_t *)(va); 632104521Srwatson break; 633104521Srwatson case 1: 634100979Srwatson data = *(volatile uint8_t *)(va); 635104527Srwatson break; 636104521Srwatson default: 637104521Srwatson panic("pciereg_cfgread: invalid width"); 638104527Srwatson } 639104527Srwatson 640104521Srwatson critical_exit(); 641104527Srwatson return (data); 642104521Srwatson} 643104521Srwatson 644104521Srwatsonstatic void 645105694Srwatsonpciereg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 646105694Srwatson{ 647104521Srwatson struct pcie_cfg_elem *elem; 648104521Srwatson volatile vm_offset_t va; 649105694Srwatson vm_paddr_t pa, papage; 650105694Srwatson 651104521Srwatson critical_enter(); 652104521Srwatson pa = PCIE_PADDR(pciebar, reg, bus, slot, func); 653104521Srwatson papage = pa & ~PAGE_MASK; 654104521Srwatson elem = pciereg_findelem(papage); 655104521Srwatson va = elem->vapage | (pa & PAGE_MASK); 656104521Srwatson 657105694Srwatson switch (bytes) { 658105694Srwatson case 4: 659105694Srwatson *(volatile uint32_t *)(va) = data; 660105694Srwatson break; 661105694Srwatson case 2: 662105694Srwatson *(volatile uint16_t *)(va) = data; 663105694Srwatson break; 664104527Srwatson case 1: 665104521Srwatson *(volatile uint8_t *)(va) = data; 666104521Srwatson break; 667104527Srwatson default: 668104527Srwatson panic("pciereg_cfgwrite: invalid width"); 669104521Srwatson } 670104527Srwatson 671104521Srwatson critical_exit(); 672104521Srwatson} 673104521Srwatson