pci_cfgreg.c revision 165126
1139790Simp/*- 226159Sse * Copyright (c) 1997, Stefan Esser <se@freebsd.org> 366529Smsmith * Copyright (c) 2000, Michael Smith <msmith@freebsd.org> 466529Smsmith * Copyright (c) 2000, BSDi 5138468Sscottl * Copyright (c) 2004, Scott Long <scottl@freebsd.org> 626159Sse * All rights reserved. 726159Sse * 826159Sse * Redistribution and use in source and binary forms, with or without 926159Sse * modification, are permitted provided that the following conditions 1026159Sse * are met: 1126159Sse * 1. Redistributions of source code must retain the above copyright 1226159Sse * notice unmodified, this list of conditions, and the following 1326159Sse * disclaimer. 1426159Sse * 2. Redistributions in binary form must reproduce the above copyright 1526159Sse * notice, this list of conditions and the following disclaimer in the 1626159Sse * documentation and/or other materials provided with the distribution. 1726159Sse * 1826159Sse * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1926159Sse * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2026159Sse * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2126159Sse * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2226159Sse * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2326159Sse * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2426159Sse * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2526159Sse * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2626159Sse * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2726159Sse * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2826159Sse */ 296104Sse 30115706Sobrien#include <sys/cdefs.h> 31115706Sobrien__FBSDID("$FreeBSD: head/sys/i386/pci/pci_cfgreg.c 165126 2006-12-12 19:23:52Z jhb $"); 32115706Sobrien 33152219Simp#include "opt_xbox.h" 34152219Simp 35125981Sjhb#include <sys/param.h> 366734Sbde#include <sys/systm.h> 3747307Speter#include <sys/bus.h> 38111068Speter#include <sys/lock.h> 39111068Speter#include <sys/mutex.h> 40138429Sscottl#include <sys/malloc.h> 41138429Sscottl#include <sys/queue.h> 42100435Simp#include <dev/pci/pcivar.h> 43100435Simp#include <dev/pci/pcireg.h> 4466529Smsmith#include <machine/pci_cfgreg.h> 4559294Smsmith#include <machine/pc/bios.h> 4659294Smsmith 47138429Sscottl#include <vm/vm.h> 48138429Sscottl#include <vm/vm_param.h> 49138429Sscottl#include <vm/vm_kern.h> 50138429Sscottl#include <vm/vm_extern.h> 51138429Sscottl#include <vm/pmap.h> 52138429Sscottl#include <machine/pmap.h> 53138429Sscottl 54152219Simp#ifdef XBOX 55152219Simp#include <machine/xbox.h> 56152219Simp#endif 57152219Simp 58103868Sjhb#define PRVERB(a) do { \ 59103868Sjhb if (bootverbose) \ 60103868Sjhb printf a ; \ 61103868Sjhb} while(0) 6282441Simp 63138429Sscottl#define PCIE_CACHE 8 64138429Sscottlstruct pcie_cfg_elem { 65138429Sscottl TAILQ_ENTRY(pcie_cfg_elem) elem; 66138429Sscottl vm_offset_t vapage; 67138429Sscottl vm_paddr_t papage; 68138429Sscottl}; 69138429Sscottl 70138429Sscottlenum { 71138429Sscottl CFGMECH_NONE = 0, 72138429Sscottl CFGMECH_1, 73138429Sscottl CFGMECH_2, 74138429Sscottl CFGMECH_PCIE, 75138429Sscottl}; 76138429Sscottl 77138429Sscottlstatic TAILQ_HEAD(pcie_cfg_list, pcie_cfg_elem) pcie_list[MAXCPU]; 78138429Sscottlstatic uint32_t pciebar; 7926159Ssestatic int cfgmech; 8026159Ssestatic int devmax; 81138429Sscottlstatic struct mtx pcicfg_mtx; 826104Sse 8366529Smsmithstatic int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes); 8466529Smsmithstatic void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes); 8559294Smsmithstatic int pcireg_cfgopen(void); 8659294Smsmith 87138429Sscottlstatic int pciereg_cfgopen(void); 88138429Sscottlstatic int pciereg_cfgread(int bus, int slot, int func, int reg, 89138429Sscottl int bytes); 90138429Sscottlstatic void pciereg_cfgwrite(int bus, int slot, int func, int reg, 91138429Sscottl int data, int bytes); 92111068Speter 9397694Simp/* 9497694Simp * Some BIOS writers seem to want to ignore the spec and put 9597694Simp * 0 in the intline rather than 255 to indicate none. Some use 9697694Simp * numbers in the range 128-254 to indicate something strange and 9797694Simp * apparently undocumented anywhere. Assume these are completely bogus 9897694Simp * and map them to 255, which means "none". 9997694Simp */ 100131575Sstefanfstatic __inline int 10197694Simppci_i386_map_intline(int line) 10297694Simp{ 103100435Simp if (line == 0 || line >= 128) 104100435Simp return (PCI_INVALID_IRQ); 105100435Simp return (line); 10697694Simp} 10797694Simp 10882441Simpstatic u_int16_t 10982441Simppcibios_get_version(void) 11082441Simp{ 111100435Simp struct bios_regs args; 11282441Simp 113102976Sjhb if (PCIbios.ventry == 0) { 114100435Simp PRVERB(("pcibios: No call entry point\n")); 115100435Simp return (0); 116100435Simp } 117100435Simp args.eax = PCIBIOS_BIOS_PRESENT; 118100435Simp if (bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL))) { 119100435Simp PRVERB(("pcibios: BIOS_PRESENT call failed\n")); 120100435Simp return (0); 121100435Simp } 122100435Simp if (args.edx != 0x20494350) { 123100435Simp PRVERB(("pcibios: BIOS_PRESENT didn't return 'PCI ' in edx\n")); 124100435Simp return (0); 125100435Simp } 126100435Simp return (args.ebx & 0xffff); 12782441Simp} 12882441Simp 12966529Smsmith/* 13066529Smsmith * Initialise access to PCI configuration space 13166529Smsmith */ 13266529Smsmithint 13366529Smsmithpci_cfgregopen(void) 13459294Smsmith{ 135100435Simp static int opened = 0; 136138429Sscottl u_int16_t vid, did; 137111068Speter u_int16_t v; 13865176Sdfr 139100435Simp if (opened) 140100435Simp return(1); 14166529Smsmith 142111068Speter if (pcireg_cfgopen() == 0) 143100435Simp return(0); 14467185Simp 145111068Speter v = pcibios_get_version(); 146111068Speter if (v > 0) 147131398Sjhb PRVERB(("pcibios: BIOS version %x.%02x\n", (v & 0xff00) >> 8, 148131398Sjhb v & 0xff)); 149111068Speter mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN); 150100435Simp opened = 1; 151125981Sjhb 152125981Sjhb /* $PIR requires PCI BIOS 2.10 or greater. */ 153125981Sjhb if (v >= 0x0210) 154125981Sjhb pci_pir_open(); 155138429Sscottl 156138429Sscottl /* 157138429Sscottl * Grope around in the PCI config space to see if this is a 158138429Sscottl * chipset that is capable of doing memory-mapped config cycles. 159138429Sscottl * This also implies that it can do PCIe extended config cycles. 160138429Sscottl */ 161138429Sscottl 162153243Srodrigc /* Check for supported chipsets */ 163165126Sjhb vid = pci_cfgregread(0, 0, 0, PCIR_VENDOR, 2); 164165126Sjhb did = pci_cfgregread(0, 0, 0, PCIR_DEVICE, 2); 165153243Srodrigc if (vid == 0x8086) { 166153243Srodrigc if (did == 0x3590 || did == 0x3592) { 167153243Srodrigc /* Intel 7520 or 7320 */ 168153243Srodrigc pciebar = pci_cfgregread(0, 0, 0, 0xce, 2) << 16; 169153243Srodrigc pciereg_cfgopen(); 170153243Srodrigc } else if (did == 0x2580 || did == 0x2584) { 171153243Srodrigc /* Intel 915 or 925 */ 172153243Srodrigc pciebar = pci_cfgregread(0, 0, 0, 0x48, 4); 173153243Srodrigc pciereg_cfgopen(); 174153243Srodrigc } 175138429Sscottl } 176138429Sscottl 177100435Simp return(1); 17859294Smsmith} 17959294Smsmith 18066529Smsmith/* 18169783Smsmith * Read configuration space register 18266529Smsmith */ 18369783Smsmithu_int32_t 18469783Smsmithpci_cfgregread(int bus, int slot, int func, int reg, int bytes) 18569783Smsmith{ 186100435Simp uint32_t line; 18769783Smsmith 188100435Simp /* 189100435Simp * Some BIOS writers seem to want to ignore the spec and put 190100435Simp * 0 in the intline rather than 255 to indicate none. The rest of 191100435Simp * the code uses 255 as an invalid IRQ. 192100435Simp */ 193100435Simp if (reg == PCIR_INTLINE && bytes == 1) { 194111068Speter line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1); 195121986Sjhb return (pci_i386_map_intline(line)); 196100435Simp } 197121986Sjhb return (pcireg_cfgread(bus, slot, func, reg, bytes)); 19869783Smsmith} 19969783Smsmith 20066529Smsmith/* 20166529Smsmith * Write configuration space register 20266529Smsmith */ 20366529Smsmithvoid 20466529Smsmithpci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes) 20559294Smsmith{ 206111068Speter 207104598Simp pcireg_cfgwrite(bus, slot, func, reg, data, bytes); 20859294Smsmith} 20959294Smsmith 21066529Smsmith/* 21166529Smsmith * Configuration space access using direct register operations 21266529Smsmith */ 21359294Smsmith 21426159Sse/* enable configuration space accesses and return data port address */ 21510887Ssestatic int 21626159Ssepci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) 21726159Sse{ 218100435Simp int dataport = 0; 21910887Sse 220152219Simp#ifdef XBOX 221152219Simp if (arch_i386_is_xbox) { 222152219Simp /* 223152219Simp * The Xbox MCPX chipset is a derivative of the nForce 1 224152219Simp * chipset. It almost has the same bus layout; some devices 225152219Simp * cannot be used, because they have been removed. 226152219Simp */ 227152219Simp 228152219Simp /* 229152219Simp * Devices 00:00.1 and 00:00.2 used to be memory controllers on 230152219Simp * the nForce chipset, but on the Xbox, using them will lockup 231152219Simp * the chipset. 232152219Simp */ 233152219Simp if (bus == 0 && slot == 0 && (func == 1 || func == 2)) 234152219Simp return dataport; 235152219Simp 236152219Simp /* 237152219Simp * Bus 1 only contains a VGA controller at 01:00.0. When you try 238152219Simp * to probe beyond that device, you only get garbage, which 239152219Simp * could cause lockups. 240152219Simp */ 241152219Simp if (bus == 1 && (slot != 0 || func != 0)) 242152219Simp return dataport; 243152219Simp 244152219Simp /* 245152219Simp * Bus 2 used to contain the AGP controller, but the Xbox MCPX 246152219Simp * doesn't have one. Probing it can cause lockups. 247152219Simp */ 248152219Simp if (bus >= 2) 249152219Simp return dataport; 250152219Simp } 251152219Simp#endif 252152219Simp 253100435Simp if (bus <= PCI_BUSMAX 254100435Simp && slot < devmax 255100435Simp && func <= PCI_FUNCMAX 256100435Simp && reg <= PCI_REGMAX 257100435Simp && bytes != 3 258100435Simp && (unsigned) bytes <= 4 259106901Simp && (reg & (bytes - 1)) == 0) { 260100435Simp switch (cfgmech) { 261138429Sscottl case CFGMECH_1: 262100435Simp outl(CONF1_ADDR_PORT, (1 << 31) 263100435Simp | (bus << 16) | (slot << 11) 264100435Simp | (func << 8) | (reg & ~0x03)); 265100435Simp dataport = CONF1_DATA_PORT + (reg & 0x03); 266100435Simp break; 267138429Sscottl case CFGMECH_2: 268100435Simp outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); 269100435Simp outb(CONF2_FORWARD_PORT, bus); 270100435Simp dataport = 0xc000 | (slot << 8) | reg; 271100435Simp break; 272100435Simp } 27326159Sse } 274100435Simp return (dataport); 27526159Sse} 2766104Sse 27726159Sse/* disable configuration space accesses */ 2786104Ssestatic void 27926159Ssepci_cfgdisable(void) 28026159Sse{ 281100435Simp switch (cfgmech) { 282138429Sscottl case CFGMECH_1: 283151644Swpaul outl(CONF1_ADDR_PORT, 0); 284152075Speter break; 285138429Sscottl case CFGMECH_2: 286100435Simp outb(CONF2_ENABLE_PORT, 0); 287151644Swpaul outb(CONF2_FORWARD_PORT, 0); 288100435Simp break; 289100435Simp } 29026159Sse} 2916104Sse 29259294Smsmithstatic int 29365176Sdfrpcireg_cfgread(int bus, int slot, int func, int reg, int bytes) 29426159Sse{ 295100435Simp int data = -1; 296100435Simp int port; 2977234Sse 298138429Sscottl if (cfgmech == CFGMECH_PCIE) { 299138429Sscottl data = pciereg_cfgread(bus, slot, func, reg, bytes); 300138429Sscottl return (data); 301138429Sscottl } 302138429Sscottl 303111068Speter mtx_lock_spin(&pcicfg_mtx); 304100435Simp port = pci_cfgenable(bus, slot, func, reg, bytes); 305100435Simp if (port != 0) { 306100435Simp switch (bytes) { 307100435Simp case 1: 308100435Simp data = inb(port); 309100435Simp break; 310100435Simp case 2: 311100435Simp data = inw(port); 312100435Simp break; 313100435Simp case 4: 314100435Simp data = inl(port); 315100435Simp break; 316100435Simp } 317100435Simp pci_cfgdisable(); 31826159Sse } 319111068Speter mtx_unlock_spin(&pcicfg_mtx); 320100435Simp return (data); 32126159Sse} 3227234Sse 32359294Smsmithstatic void 32465176Sdfrpcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 32526159Sse{ 326100435Simp int port; 3276104Sse 328138429Sscottl if (cfgmech == CFGMECH_PCIE) { 329138429Sscottl pciereg_cfgwrite(bus, slot, func, reg, data, bytes); 330138429Sscottl return; 331138429Sscottl } 332138429Sscottl 333111068Speter mtx_lock_spin(&pcicfg_mtx); 334100435Simp port = pci_cfgenable(bus, slot, func, reg, bytes); 335100435Simp if (port != 0) { 336100435Simp switch (bytes) { 337100435Simp case 1: 338100435Simp outb(port, data); 339100435Simp break; 340100435Simp case 2: 341100435Simp outw(port, data); 342100435Simp break; 343100435Simp case 4: 344100435Simp outl(port, data); 345100435Simp break; 346100435Simp } 347100435Simp pci_cfgdisable(); 34826159Sse } 349111068Speter mtx_unlock_spin(&pcicfg_mtx); 35026159Sse} 3516104Sse 35266529Smsmith/* check whether the configuration mechanism has been correctly identified */ 35310887Ssestatic int 35426159Ssepci_cfgcheck(int maxdev) 35510887Sse{ 356106357Simp uint32_t id, class; 357106357Simp uint8_t header; 358106357Simp uint8_t device; 359111068Speter int port; 36010735Sse 36126159Sse if (bootverbose) 362100435Simp printf("pci_cfgcheck:\tdevice "); 36310960Sse 364100435Simp for (device = 0; device < maxdev; device++) { 365100435Simp if (bootverbose) 366100435Simp printf("%d ", device); 36726159Sse 368111068Speter port = pci_cfgenable(0, device, 0, 0, 4); 369111068Speter id = inl(port); 370106357Simp if (id == 0 || id == 0xffffffff) 371100435Simp continue; 37223415Sse 373111068Speter port = pci_cfgenable(0, device, 0, 8, 4); 374111068Speter class = inl(port) >> 8; 375100435Simp if (bootverbose) 376100435Simp printf("[class=%06x] ", class); 377100435Simp if (class == 0 || (class & 0xf870ff) != 0) 378100435Simp continue; 379100435Simp 380111068Speter port = pci_cfgenable(0, device, 0, 14, 1); 381111068Speter header = inb(port); 382106357Simp if (bootverbose) 383100435Simp printf("[hdr=%02x] ", header); 384100435Simp if ((header & 0x7e) != 0) 385100435Simp continue; 386100435Simp 387100435Simp if (bootverbose) 388100435Simp printf("is there (id=%08x)\n", id); 389100435Simp 390100435Simp pci_cfgdisable(); 391100435Simp return (1); 392100435Simp } 39366529Smsmith if (bootverbose) 394100435Simp printf("-- nothing found\n"); 39523415Sse 39666529Smsmith pci_cfgdisable(); 397100435Simp return (0); 39810887Sse} 39910887Sse 40047307Speterstatic int 40159294Smsmithpcireg_cfgopen(void) 4026104Sse{ 403106357Simp uint32_t mode1res, oldval1; 404106357Simp uint8_t mode2res, oldval2; 4056104Sse 406100435Simp oldval1 = inl(CONF1_ADDR_PORT); 40710960Sse 408100435Simp if (bootverbose) { 409106357Simp printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n", 410100435Simp oldval1); 411100435Simp } 41210960Sse 413100435Simp if ((oldval1 & CONF1_ENABLE_MSK) == 0) { 41410960Sse 415138429Sscottl cfgmech = CFGMECH_1; 416100435Simp devmax = 32; 41710960Sse 418100435Simp outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 419124021Sjhb DELAY(1); 420100435Simp mode1res = inl(CONF1_ADDR_PORT); 421100435Simp outl(CONF1_ADDR_PORT, oldval1); 42210960Sse 423100435Simp if (bootverbose) 424106357Simp printf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n", 425100435Simp mode1res, CONF1_ENABLE_CHK); 4266104Sse 427100435Simp if (mode1res) { 428100435Simp if (pci_cfgcheck(32)) 429100435Simp return (cfgmech); 430100435Simp } 43110807Sse 432100435Simp outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); 433100435Simp mode1res = inl(CONF1_ADDR_PORT); 434100435Simp outl(CONF1_ADDR_PORT, oldval1); 43510887Sse 436100435Simp if (bootverbose) 437106357Simp printf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n", 438100435Simp mode1res, CONF1_ENABLE_CHK1); 43910887Sse 440100435Simp if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { 441100435Simp if (pci_cfgcheck(32)) 442100435Simp return (cfgmech); 443100435Simp } 44411524Sse } 44511524Sse 446100435Simp oldval2 = inb(CONF2_ENABLE_PORT); 44747307Speter 448100435Simp if (bootverbose) { 449100435Simp printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", 450100435Simp oldval2); 451100435Simp } 45247307Speter 453100435Simp if ((oldval2 & 0xf0) == 0) { 45448832Smsmith 455138429Sscottl cfgmech = CFGMECH_2; 456100435Simp devmax = 16; 45748832Smsmith 458100435Simp outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); 459100435Simp mode2res = inb(CONF2_ENABLE_PORT); 460100435Simp outb(CONF2_ENABLE_PORT, oldval2); 46148832Smsmith 462100435Simp if (bootverbose) 463100435Simp printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", 464100435Simp mode2res, CONF2_ENABLE_CHK); 46552480Salc 466100435Simp if (mode2res == CONF2_ENABLE_RES) { 467100435Simp if (bootverbose) 468100435Simp printf("pci_open(2a):\tnow trying mechanism 2\n"); 46949404Speter 470100435Simp if (pci_cfgcheck(16)) 471100435Simp return (cfgmech); 472100435Simp } 47348832Smsmith } 47448832Smsmith 475138429Sscottl cfgmech = CFGMECH_NONE; 476100435Simp devmax = 0; 477100435Simp return (cfgmech); 47848832Smsmith} 47948832Smsmith 480138429Sscottlstatic int 481138429Sscottlpciereg_cfgopen(void) 482138429Sscottl{ 483138429Sscottl struct pcie_cfg_list *pcielist; 484138429Sscottl struct pcie_cfg_elem *pcie_array, *elem; 485138429Sscottl#ifdef SMP 486138429Sscottl struct pcpu *pc; 487138429Sscottl#endif 488138429Sscottl vm_offset_t va; 489138429Sscottl int i; 490138429Sscottl 491138429Sscottl if (bootverbose) 492138429Sscottl printf("Setting up PCIe mappings for BAR 0x%x\n", pciebar); 493138429Sscottl 494138429Sscottl#ifdef SMP 495138429Sscottl SLIST_FOREACH(pc, &cpuhead, pc_allcpu) 496138429Sscottl#endif 497138429Sscottl { 498138429Sscottl 499138429Sscottl pcie_array = malloc(sizeof(struct pcie_cfg_elem) * PCIE_CACHE, 500138429Sscottl M_DEVBUF, M_NOWAIT); 501138429Sscottl if (pcie_array == NULL) 502138429Sscottl return (0); 503138429Sscottl 504138429Sscottl va = kmem_alloc_nofault(kernel_map, PCIE_CACHE * PAGE_SIZE); 505138429Sscottl if (va == 0) { 506138429Sscottl free(pcie_array, M_DEVBUF); 507138429Sscottl return (0); 508138429Sscottl } 509138429Sscottl 510138429Sscottl#ifdef SMP 511138429Sscottl pcielist = &pcie_list[pc->pc_cpuid]; 512138429Sscottl#else 513138429Sscottl pcielist = &pcie_list[0]; 514138429Sscottl#endif 515138429Sscottl TAILQ_INIT(pcielist); 516138429Sscottl for (i = 0; i < PCIE_CACHE; i++) { 517138429Sscottl elem = &pcie_array[i]; 518138429Sscottl elem->vapage = va + (i * PAGE_SIZE); 519138429Sscottl elem->papage = 0; 520138429Sscottl TAILQ_INSERT_HEAD(pcielist, elem, elem); 521138429Sscottl } 522138429Sscottl } 523138429Sscottl 524138429Sscottl 525138429Sscottl cfgmech = CFGMECH_PCIE; 526138429Sscottl devmax = 32; 527138429Sscottl return (1); 528138429Sscottl} 529138429Sscottl 530138429Sscottl#define PCIE_PADDR(bar, reg, bus, slot, func) \ 531138429Sscottl ((bar) | \ 532138429Sscottl (((bus) & 0xff) << 20) | \ 533138429Sscottl (((slot) & 0x1f) << 15) | \ 534138429Sscottl (((func) & 0x7) << 12) | \ 535138429Sscottl ((reg) & 0xfff)) 536138429Sscottl 537138429Sscottl/* 538138429Sscottl * Find an element in the cache that matches the physical page desired, or 539138429Sscottl * create a new mapping from the least recently used element. 540138429Sscottl * A very simple LRU algorithm is used here, does it need to be more 541138429Sscottl * efficient? 542138429Sscottl */ 543138429Sscottlstatic __inline struct pcie_cfg_elem * 544138429Sscottlpciereg_findelem(vm_paddr_t papage) 545138429Sscottl{ 546138429Sscottl struct pcie_cfg_list *pcielist; 547138429Sscottl struct pcie_cfg_elem *elem; 548138429Sscottl 549138429Sscottl pcielist = &pcie_list[PCPU_GET(cpuid)]; 550138429Sscottl TAILQ_FOREACH(elem, pcielist, elem) { 551138429Sscottl if (elem->papage == papage) 552138429Sscottl break; 553138429Sscottl } 554138429Sscottl 555138429Sscottl if (elem == NULL) { 556138429Sscottl elem = TAILQ_LAST(pcielist, pcie_cfg_list); 557138429Sscottl if (elem->papage != 0) { 558138429Sscottl pmap_kremove(elem->vapage); 559138429Sscottl invlpg(elem->vapage); 560138429Sscottl } 561138429Sscottl pmap_kenter(elem->vapage, papage); 562138429Sscottl elem->papage = papage; 563138429Sscottl } 564138429Sscottl 565138429Sscottl if (elem != TAILQ_FIRST(pcielist)) { 566138429Sscottl TAILQ_REMOVE(pcielist, elem, elem); 567138429Sscottl TAILQ_INSERT_HEAD(pcielist, elem, elem); 568138429Sscottl } 569138429Sscottl return (elem); 570138429Sscottl} 571138429Sscottl 572138429Sscottlstatic int 573138429Sscottlpciereg_cfgread(int bus, int slot, int func, int reg, int bytes) 574138429Sscottl{ 575138429Sscottl struct pcie_cfg_elem *elem; 576138429Sscottl volatile vm_offset_t va; 577138429Sscottl vm_paddr_t pa, papage; 578138662Sscottl int data; 579138429Sscottl 580138662Sscottl critical_enter(); 581138429Sscottl pa = PCIE_PADDR(pciebar, reg, bus, slot, func); 582138429Sscottl papage = pa & ~PAGE_MASK; 583138429Sscottl elem = pciereg_findelem(papage); 584138429Sscottl va = elem->vapage | (pa & PAGE_MASK); 585138429Sscottl 586138429Sscottl switch (bytes) { 587138429Sscottl case 4: 588138662Sscottl data = *(volatile uint32_t *)(va); 589138662Sscottl break; 590138429Sscottl case 2: 591138662Sscottl data = *(volatile uint16_t *)(va); 592138662Sscottl break; 593138429Sscottl case 1: 594138662Sscottl data = *(volatile uint8_t *)(va); 595138662Sscottl break; 596138429Sscottl default: 597138429Sscottl panic("pciereg_cfgread: invalid width"); 598138429Sscottl } 599138662Sscottl 600138662Sscottl critical_exit(); 601138662Sscottl return (data); 602138429Sscottl} 603138429Sscottl 604138429Sscottlstatic void 605138429Sscottlpciereg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 606138429Sscottl{ 607138429Sscottl struct pcie_cfg_elem *elem; 608138429Sscottl volatile vm_offset_t va; 609138429Sscottl vm_paddr_t pa, papage; 610138429Sscottl 611138662Sscottl critical_enter(); 612138429Sscottl pa = PCIE_PADDR(pciebar, reg, bus, slot, func); 613138429Sscottl papage = pa & ~PAGE_MASK; 614138429Sscottl elem = pciereg_findelem(papage); 615138429Sscottl va = elem->vapage | (pa & PAGE_MASK); 616138429Sscottl 617138429Sscottl switch (bytes) { 618138429Sscottl case 4: 619138429Sscottl *(volatile uint32_t *)(va) = data; 620138429Sscottl break; 621138429Sscottl case 2: 622138429Sscottl *(volatile uint16_t *)(va) = data; 623138429Sscottl break; 624138429Sscottl case 1: 625138429Sscottl *(volatile uint8_t *)(va) = data; 626138429Sscottl break; 627138429Sscottl default: 628138429Sscottl panic("pciereg_cfgwrite: invalid width"); 629138429Sscottl } 630138662Sscottl 631138662Sscottl critical_exit(); 632138429Sscottl} 633