1139731Simp/*- 226159Sse * Copyright (c) 1997, Stefan Esser <se@freebsd.org> 366529Smsmith * Copyright (c) 2000, Michael Smith <msmith@freebsd.org> 466529Smsmith * Copyright (c) 2000, BSDi 526159Sse * All rights reserved. 626159Sse * 726159Sse * Redistribution and use in source and binary forms, with or without 826159Sse * modification, are permitted provided that the following conditions 926159Sse * are met: 1026159Sse * 1. Redistributions of source code must retain the above copyright 1126159Sse * notice unmodified, this list of conditions, and the following 1226159Sse * disclaimer. 1326159Sse * 2. Redistributions in binary form must reproduce the above copyright 1426159Sse * notice, this list of conditions and the following disclaimer in the 1526159Sse * documentation and/or other materials provided with the distribution. 1626159Sse * 1726159Sse * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1826159Sse * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1926159Sse * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2026159Sse * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2126159Sse * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2226159Sse * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2326159Sse * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2426159Sse * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2526159Sse * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2626159Sse * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2726159Sse */ 286104Sse 29118031Sobrien#include <sys/cdefs.h> 30118031Sobrien__FBSDID("$FreeBSD: stable/11/sys/amd64/pci/pci_cfgreg.c 351439 2019-08-23 22:03:50Z jhb $"); 31118031Sobrien 32126926Speter#include <sys/param.h> 336734Sbde#include <sys/systm.h> 3447307Speter#include <sys/bus.h> 35111068Speter#include <sys/lock.h> 36182947Sjhb#include <sys/kernel.h> 37111068Speter#include <sys/mutex.h> 38192342Sjhb#include <sys/sysctl.h> 39100435Simp#include <dev/pci/pcivar.h> 40100435Simp#include <dev/pci/pcireg.h> 41181987Sjhb#include <vm/vm.h> 42181987Sjhb#include <vm/pmap.h> 4366529Smsmith#include <machine/pci_cfgreg.h> 4459294Smsmith 45181987Sjhbenum { 46181987Sjhb CFGMECH_NONE = 0, 47181987Sjhb CFGMECH_1, 48181987Sjhb CFGMECH_PCIE, 49181987Sjhb}; 50181987Sjhb 51182910Sjhbstatic uint32_t pci_docfgregread(int bus, int slot, int func, int reg, 52182910Sjhb int bytes); 53181987Sjhbstatic int pciereg_cfgread(int bus, unsigned slot, unsigned func, 54181987Sjhb unsigned reg, unsigned bytes); 55181987Sjhbstatic void pciereg_cfgwrite(int bus, unsigned slot, unsigned func, 56181987Sjhb unsigned reg, int data, unsigned bytes); 5766529Smsmithstatic int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes); 5866529Smsmithstatic void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes); 5959294Smsmith 60192342SjhbSYSCTL_DECL(_hw_pci); 61192342Sjhb 62181987Sjhbstatic int cfgmech; 63181987Sjhbstatic vm_offset_t pcie_base; 64181987Sjhbstatic int pcie_minbus, pcie_maxbus; 65182910Sjhbstatic uint32_t pcie_badslots; 66111068Speterstatic struct mtx pcicfg_mtx; 67318207SsepheMTX_SYSINIT(pcicfg_mtx, &pcicfg_mtx, "pcicfg_mtx", MTX_SPIN); 68182947Sjhbstatic int mcfg_enable = 1; 69192342SjhbSYSCTL_INT(_hw_pci, OID_AUTO, mcfg, CTLFLAG_RDTUN, &mcfg_enable, 0, 70192342Sjhb "Enable support for PCI-e memory mapped config access"); 71111068Speter 7266529Smsmith/* 7366529Smsmith * Initialise access to PCI configuration space 7466529Smsmith */ 7566529Smsmithint 7666529Smsmithpci_cfgregopen(void) 7759294Smsmith{ 78181987Sjhb uint64_t pciebar; 79181987Sjhb uint16_t did, vid; 8065176Sdfr 81181987Sjhb if (cfgmech != CFGMECH_NONE) 82114349Speter return (1); 83181987Sjhb cfgmech = CFGMECH_1; 84181987Sjhb 85181987Sjhb /* 86181987Sjhb * Grope around in the PCI config space to see if this is a 87181987Sjhb * chipset that is capable of doing memory-mapped config cycles. 88181987Sjhb * This also implies that it can do PCIe extended config cycles. 89181987Sjhb */ 90181987Sjhb 91181987Sjhb /* Check for supported chipsets */ 92181987Sjhb vid = pci_cfgregread(0, 0, 0, PCIR_VENDOR, 2); 93181987Sjhb did = pci_cfgregread(0, 0, 0, PCIR_DEVICE, 2); 94181987Sjhb switch (vid) { 95181987Sjhb case 0x8086: 96181987Sjhb switch (did) { 97181987Sjhb case 0x3590: 98181987Sjhb case 0x3592: 99181987Sjhb /* Intel 7520 or 7320 */ 100181987Sjhb pciebar = pci_cfgregread(0, 0, 0, 0xce, 2) << 16; 101181987Sjhb pcie_cfgregopen(pciebar, 0, 255); 102181987Sjhb break; 103181987Sjhb case 0x2580: 104181987Sjhb case 0x2584: 105181987Sjhb case 0x2590: 106181987Sjhb /* Intel 915, 925, or 915GM */ 107181987Sjhb pciebar = pci_cfgregread(0, 0, 0, 0x48, 4); 108181987Sjhb pcie_cfgregopen(pciebar, 0, 255); 109181987Sjhb break; 110181987Sjhb } 111181987Sjhb } 112181987Sjhb 113114349Speter return (1); 11459294Smsmith} 11559294Smsmith 116182910Sjhbstatic uint32_t 117182910Sjhbpci_docfgregread(int bus, int slot, int func, int reg, int bytes) 118182910Sjhb{ 119182910Sjhb 120182910Sjhb if (cfgmech == CFGMECH_PCIE && 121190386Sjhb (bus >= pcie_minbus && bus <= pcie_maxbus) && 122182910Sjhb (bus != 0 || !(1 << slot & pcie_badslots))) 123182910Sjhb return (pciereg_cfgread(bus, slot, func, reg, bytes)); 124182910Sjhb else 125182910Sjhb return (pcireg_cfgread(bus, slot, func, reg, bytes)); 126182910Sjhb} 127182910Sjhb 12866529Smsmith/* 12969783Smsmith * Read configuration space register 13066529Smsmith */ 13169783Smsmithu_int32_t 13269783Smsmithpci_cfgregread(int bus, int slot, int func, int reg, int bytes) 13369783Smsmith{ 134100435Simp uint32_t line; 13569783Smsmith 136318207Ssephe if (cfgmech == CFGMECH_NONE) 137318207Ssephe return (0xffffffff); 138318207Ssephe 139100435Simp /* 140100435Simp * Some BIOS writers seem to want to ignore the spec and put 141114349Speter * 0 in the intline rather than 255 to indicate none. Some use 142114349Speter * numbers in the range 128-254 to indicate something strange and 143114349Speter * apparently undocumented anywhere. Assume these are completely bogus 144114349Speter * and map them to 255, which the rest of the PCI code recognizes as 145114349Speter * as an invalid IRQ. 146100435Simp */ 147100435Simp if (reg == PCIR_INTLINE && bytes == 1) { 148182910Sjhb line = pci_docfgregread(bus, slot, func, PCIR_INTLINE, 1); 149114349Speter if (line == 0 || line >= 128) 150114349Speter line = PCI_INVALID_IRQ; 151114349Speter return (line); 152100435Simp } 153182910Sjhb return (pci_docfgregread(bus, slot, func, reg, bytes)); 15469783Smsmith} 15569783Smsmith 15666529Smsmith/* 15766529Smsmith * Write configuration space register 15866529Smsmith */ 15966529Smsmithvoid 16066529Smsmithpci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes) 16159294Smsmith{ 162111068Speter 163318207Ssephe if (cfgmech == CFGMECH_NONE) 164318207Ssephe return; 165318207Ssephe 166182910Sjhb if (cfgmech == CFGMECH_PCIE && 167190386Sjhb (bus >= pcie_minbus && bus <= pcie_maxbus) && 168182910Sjhb (bus != 0 || !(1 << slot & pcie_badslots))) 169182910Sjhb pciereg_cfgwrite(bus, slot, func, reg, data, bytes); 170182910Sjhb else 171182910Sjhb pcireg_cfgwrite(bus, slot, func, reg, data, bytes); 17259294Smsmith} 17359294Smsmith 17466529Smsmith/* 17566529Smsmith * Configuration space access using direct register operations 17666529Smsmith */ 17759294Smsmith 17826159Sse/* enable configuration space accesses and return data port address */ 17910887Ssestatic int 18026159Ssepci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) 18126159Sse{ 182100435Simp int dataport = 0; 18310887Sse 184197450Savg if (bus <= PCI_BUSMAX && slot <= PCI_SLOTMAX && func <= PCI_FUNCMAX && 185197450Savg (unsigned)reg <= PCI_REGMAX && bytes != 3 && 186197450Savg (unsigned)bytes <= 4 && (reg & (bytes - 1)) == 0) { 187258780Seadler outl(CONF1_ADDR_PORT, (1U << 31) | (bus << 16) | (slot << 11) 188174050Sjhb | (func << 8) | (reg & ~0x03)); 189174050Sjhb dataport = CONF1_DATA_PORT + (reg & 0x03); 19026159Sse } 191100435Simp return (dataport); 19226159Sse} 1936104Sse 19426159Sse/* disable configuration space accesses */ 1956104Ssestatic void 19626159Ssepci_cfgdisable(void) 19726159Sse{ 198174050Sjhb 199174050Sjhb /* 200174050Sjhb * Do nothing. Writing a 0 to the address port can apparently 201174050Sjhb * confuse some bridges and cause spurious access failures. 202174050Sjhb */ 20326159Sse} 2046104Sse 20559294Smsmithstatic int 20665176Sdfrpcireg_cfgread(int bus, int slot, int func, int reg, int bytes) 20726159Sse{ 208100435Simp int data = -1; 209100435Simp int port; 2107234Sse 211111068Speter mtx_lock_spin(&pcicfg_mtx); 212100435Simp port = pci_cfgenable(bus, slot, func, reg, bytes); 213100435Simp if (port != 0) { 214100435Simp switch (bytes) { 215100435Simp case 1: 216100435Simp data = inb(port); 217100435Simp break; 218100435Simp case 2: 219100435Simp data = inw(port); 220100435Simp break; 221100435Simp case 4: 222100435Simp data = inl(port); 223100435Simp break; 224100435Simp } 225100435Simp pci_cfgdisable(); 22626159Sse } 227111068Speter mtx_unlock_spin(&pcicfg_mtx); 228100435Simp return (data); 22926159Sse} 2307234Sse 23159294Smsmithstatic void 23265176Sdfrpcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 23326159Sse{ 234100435Simp int port; 2356104Sse 236111068Speter mtx_lock_spin(&pcicfg_mtx); 237100435Simp port = pci_cfgenable(bus, slot, func, reg, bytes); 238100435Simp if (port != 0) { 239100435Simp switch (bytes) { 240100435Simp case 1: 241100435Simp outb(port, data); 242100435Simp break; 243100435Simp case 2: 244100435Simp outw(port, data); 245100435Simp break; 246100435Simp case 4: 247100435Simp outl(port, data); 248100435Simp break; 249100435Simp } 250100435Simp pci_cfgdisable(); 25126159Sse } 252111068Speter mtx_unlock_spin(&pcicfg_mtx); 25326159Sse} 254181987Sjhb 255181987Sjhbint 256181987Sjhbpcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus) 257181987Sjhb{ 258182910Sjhb uint32_t val1, val2; 259182910Sjhb int slot; 260181987Sjhb 261182947Sjhb if (!mcfg_enable) 262182947Sjhb return (0); 263182947Sjhb 264181987Sjhb if (minbus != 0) 265181987Sjhb return (0); 266181987Sjhb 267181987Sjhb if (bootverbose) 268181987Sjhb printf("PCIe: Memory Mapped configuration base @ 0x%lx\n", 269181987Sjhb base); 270181987Sjhb 271181987Sjhb /* XXX: We should make sure this really fits into the direct map. */ 272351439Sjhb pcie_base = (vm_offset_t)pmap_mapdev_pciecfg(base, (maxbus + 1) << 20); 273181987Sjhb pcie_minbus = minbus; 274181987Sjhb pcie_maxbus = maxbus; 275181987Sjhb cfgmech = CFGMECH_PCIE; 276182910Sjhb 277182910Sjhb /* 278182910Sjhb * On some AMD systems, some of the devices on bus 0 are 279182910Sjhb * inaccessible using memory-mapped PCI config access. Walk 280182910Sjhb * bus 0 looking for such devices. For these devices, we will 281182910Sjhb * fall back to using type 1 config access instead. 282182910Sjhb */ 283182910Sjhb if (pci_cfgregopen() != 0) { 284197450Savg for (slot = 0; slot <= PCI_SLOTMAX; slot++) { 285182910Sjhb val1 = pcireg_cfgread(0, slot, 0, 0, 4); 286182910Sjhb if (val1 == 0xffffffff) 287182910Sjhb continue; 288182910Sjhb 289182910Sjhb val2 = pciereg_cfgread(0, slot, 0, 0, 4); 290182910Sjhb if (val2 != val1) 291182910Sjhb pcie_badslots |= (1 << slot); 292182910Sjhb } 293182910Sjhb } 294182910Sjhb 295181987Sjhb return (1); 296181987Sjhb} 297181987Sjhb 298243737Sjkim#define PCIE_VADDR(base, reg, bus, slot, func) \ 299243737Sjkim ((base) + \ 300243737Sjkim ((((bus) & 0xff) << 20) | \ 301243737Sjkim (((slot) & 0x1f) << 15) | \ 302243737Sjkim (((func) & 0x7) << 12) | \ 303243737Sjkim ((reg) & 0xfff))) 304243737Sjkim 305241540Savg/* 306241540Savg * AMD BIOS And Kernel Developer's Guides for CPU families starting with 10h 307241540Savg * have a requirement that all accesses to the memory mapped PCI configuration 308241540Savg * space are done using AX class of registers. 309241540Savg * Since other vendors do not currently have any contradicting requirements 310241540Savg * the AMD access pattern is applied universally. 311241540Savg */ 312181987Sjhb 313181987Sjhbstatic int 314181987Sjhbpciereg_cfgread(int bus, unsigned slot, unsigned func, unsigned reg, 315181987Sjhb unsigned bytes) 316181987Sjhb{ 317243712Sjkim vm_offset_t va; 318181987Sjhb int data = -1; 319181987Sjhb 320197450Savg if (bus < pcie_minbus || bus > pcie_maxbus || slot > PCI_SLOTMAX || 321197450Savg func > PCI_FUNCMAX || reg > PCIE_REGMAX) 322181987Sjhb return (-1); 323181987Sjhb 324181987Sjhb va = PCIE_VADDR(pcie_base, reg, bus, slot, func); 325181987Sjhb 326181987Sjhb switch (bytes) { 327181987Sjhb case 4: 328243712Sjkim __asm("movl %1, %0" : "=a" (data) 329243712Sjkim : "m" (*(volatile uint32_t *)va)); 330181987Sjhb break; 331181987Sjhb case 2: 332243712Sjkim __asm("movzwl %1, %0" : "=a" (data) 333243712Sjkim : "m" (*(volatile uint16_t *)va)); 334181987Sjhb break; 335181987Sjhb case 1: 336243712Sjkim __asm("movzbl %1, %0" : "=a" (data) 337243712Sjkim : "m" (*(volatile uint8_t *)va)); 338181987Sjhb break; 339181987Sjhb } 340181987Sjhb 341181987Sjhb return (data); 342181987Sjhb} 343181987Sjhb 344181987Sjhbstatic void 345181987Sjhbpciereg_cfgwrite(int bus, unsigned slot, unsigned func, unsigned reg, int data, 346181987Sjhb unsigned bytes) 347181987Sjhb{ 348243712Sjkim vm_offset_t va; 349181987Sjhb 350197450Savg if (bus < pcie_minbus || bus > pcie_maxbus || slot > PCI_SLOTMAX || 351197450Savg func > PCI_FUNCMAX || reg > PCIE_REGMAX) 352181987Sjhb return; 353181987Sjhb 354181987Sjhb va = PCIE_VADDR(pcie_base, reg, bus, slot, func); 355181987Sjhb 356181987Sjhb switch (bytes) { 357181987Sjhb case 4: 358243712Sjkim __asm("movl %1, %0" : "=m" (*(volatile uint32_t *)va) 359241540Savg : "a" (data)); 360181987Sjhb break; 361181987Sjhb case 2: 362243712Sjkim __asm("movw %1, %0" : "=m" (*(volatile uint16_t *)va) 363243685Sjkim : "a" ((uint16_t)data)); 364181987Sjhb break; 365181987Sjhb case 1: 366243712Sjkim __asm("movb %1, %0" : "=m" (*(volatile uint8_t *)va) 367243685Sjkim : "a" ((uint8_t)data)); 368181987Sjhb break; 369181987Sjhb } 370181987Sjhb} 371