pci_cfgreg.c revision 181987
1208946Sae/*- 2208946Sae * Copyright (c) 1997, Stefan Esser <se@freebsd.org> 3208946Sae * Copyright (c) 2000, Michael Smith <msmith@freebsd.org> 4208946Sae * Copyright (c) 2000, BSDi 5208946Sae * All rights reserved. 6208946Sae * 7208946Sae * Redistribution and use in source and binary forms, with or without 8208946Sae * modification, are permitted provided that the following conditions 9208946Sae * are met: 10208946Sae * 1. Redistributions of source code must retain the above copyright 11208946Sae * notice unmodified, this list of conditions, and the following 12208946Sae * disclaimer. 13208946Sae * 2. Redistributions in binary form must reproduce the above copyright 14208946Sae * notice, this list of conditions and the following disclaimer in the 15208946Sae * documentation and/or other materials provided with the distribution. 16208946Sae * 17208946Sae * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18208946Sae * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19208946Sae * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20208946Sae * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21208946Sae * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22208946Sae * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23208946Sae * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24208946Sae * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25208946Sae * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26208946Sae * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27208946Sae */ 28208946Sae 29208946Sae#include <sys/cdefs.h> 30208946Sae__FBSDID("$FreeBSD: head/sys/amd64/pci/pci_cfgreg.c 181987 2008-08-22 02:14:23Z jhb $"); 31208946Sae 32208946Sae#include <sys/param.h> 33208989Sae#include <sys/systm.h> 34208946Sae#include <sys/bus.h> 35208946Sae#include <sys/lock.h> 36208989Sae#include <sys/mutex.h> 37208946Sae#include <dev/pci/pcivar.h> 38208946Sae#include <dev/pci/pcireg.h> 39208989Sae#include <vm/vm.h> 40208989Sae#include <vm/pmap.h> 41208946Sae#include <machine/pci_cfgreg.h> 42208946Sae 43208946Saeenum { 44208946Sae CFGMECH_NONE = 0, 45208946Sae CFGMECH_1, 46208946Sae CFGMECH_PCIE, 47208946Sae}; 48208946Sae 49208946Saestatic int pciereg_cfgread(int bus, unsigned slot, unsigned func, 50208946Sae unsigned reg, unsigned bytes); 51208946Saestatic void pciereg_cfgwrite(int bus, unsigned slot, unsigned func, 52208946Sae unsigned reg, int data, unsigned bytes); 53208946Saestatic int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes); 54208946Saestatic void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes); 55208946Sae 56208946Saestatic int cfgmech; 57208946Saestatic vm_offset_t pcie_base; 58208946Saestatic int pcie_minbus, pcie_maxbus; 59208946Saestatic struct mtx pcicfg_mtx; 60208946Sae 61208946Sae/* 62208946Sae * Initialise access to PCI configuration space 63208946Sae */ 64208946Saeint 65208946Saepci_cfgregopen(void) 66208946Sae{ 67208946Sae uint64_t pciebar; 68208946Sae uint16_t did, vid; 69208946Sae 70208946Sae if (cfgmech != CFGMECH_NONE) 71208946Sae return (1); 72208946Sae mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN); 73208989Sae cfgmech = CFGMECH_1; 74208946Sae 75208946Sae /* 76208946Sae * Grope around in the PCI config space to see if this is a 77208946Sae * chipset that is capable of doing memory-mapped config cycles. 78208946Sae * This also implies that it can do PCIe extended config cycles. 79208946Sae */ 80208946Sae 81208946Sae /* Check for supported chipsets */ 82208946Sae vid = pci_cfgregread(0, 0, 0, PCIR_VENDOR, 2); 83208946Sae did = pci_cfgregread(0, 0, 0, PCIR_DEVICE, 2); 84208946Sae switch (vid) { 85208946Sae case 0x8086: 86208946Sae switch (did) { 87208989Sae case 0x3590: 88208946Sae case 0x3592: 89208946Sae /* Intel 7520 or 7320 */ 90208946Sae pciebar = pci_cfgregread(0, 0, 0, 0xce, 2) << 16; 91208946Sae pcie_cfgregopen(pciebar, 0, 255); 92208946Sae break; 93208946Sae case 0x2580: 94208946Sae case 0x2584: 95208946Sae case 0x2590: 96208946Sae /* Intel 915, 925, or 915GM */ 97208946Sae pciebar = pci_cfgregread(0, 0, 0, 0x48, 4); 98208946Sae pcie_cfgregopen(pciebar, 0, 255); 99208946Sae break; 100208989Sae } 101208946Sae } 102208946Sae 103208946Sae return (1); 104208946Sae} 105208946Sae 106208946Sae/* 107208946Sae * Read configuration space register 108 */ 109u_int32_t 110pci_cfgregread(int bus, int slot, int func, int reg, int bytes) 111{ 112 uint32_t line; 113 114 /* 115 * Some BIOS writers seem to want to ignore the spec and put 116 * 0 in the intline rather than 255 to indicate none. Some use 117 * numbers in the range 128-254 to indicate something strange and 118 * apparently undocumented anywhere. Assume these are completely bogus 119 * and map them to 255, which the rest of the PCI code recognizes as 120 * as an invalid IRQ. 121 */ 122 if (reg == PCIR_INTLINE && bytes == 1) { 123 line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1); 124 if (line == 0 || line >= 128) 125 line = PCI_INVALID_IRQ; 126 return (line); 127 } 128 return (pcireg_cfgread(bus, slot, func, reg, bytes)); 129} 130 131/* 132 * Write configuration space register 133 */ 134void 135pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes) 136{ 137 138 pcireg_cfgwrite(bus, slot, func, reg, data, bytes); 139} 140 141/* 142 * Configuration space access using direct register operations 143 */ 144 145/* enable configuration space accesses and return data port address */ 146static int 147pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) 148{ 149 int dataport = 0; 150 151 if (bus <= PCI_BUSMAX && slot < 32 && func <= PCI_FUNCMAX && 152 reg <= PCI_REGMAX && bytes != 3 && (unsigned) bytes <= 4 && 153 (reg & (bytes - 1)) == 0) { 154 outl(CONF1_ADDR_PORT, (1 << 31) | (bus << 16) | (slot << 11) 155 | (func << 8) | (reg & ~0x03)); 156 dataport = CONF1_DATA_PORT + (reg & 0x03); 157 } 158 return (dataport); 159} 160 161/* disable configuration space accesses */ 162static void 163pci_cfgdisable(void) 164{ 165 166 /* 167 * Do nothing. Writing a 0 to the address port can apparently 168 * confuse some bridges and cause spurious access failures. 169 */ 170} 171 172static int 173pcireg_cfgread(int bus, int slot, int func, int reg, int bytes) 174{ 175 int data = -1; 176 int port; 177 178 if (cfgmech == CFGMECH_PCIE) { 179 data = pciereg_cfgread(bus, slot, func, reg, bytes); 180 return (data); 181 } 182 183 mtx_lock_spin(&pcicfg_mtx); 184 port = pci_cfgenable(bus, slot, func, reg, bytes); 185 if (port != 0) { 186 switch (bytes) { 187 case 1: 188 data = inb(port); 189 break; 190 case 2: 191 data = inw(port); 192 break; 193 case 4: 194 data = inl(port); 195 break; 196 } 197 pci_cfgdisable(); 198 } 199 mtx_unlock_spin(&pcicfg_mtx); 200 return (data); 201} 202 203static void 204pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 205{ 206 int port; 207 208 if (cfgmech == CFGMECH_PCIE) { 209 pciereg_cfgwrite(bus, slot, func, reg, data, bytes); 210 return; 211 } 212 213 mtx_lock_spin(&pcicfg_mtx); 214 port = pci_cfgenable(bus, slot, func, reg, bytes); 215 if (port != 0) { 216 switch (bytes) { 217 case 1: 218 outb(port, data); 219 break; 220 case 2: 221 outw(port, data); 222 break; 223 case 4: 224 outl(port, data); 225 break; 226 } 227 pci_cfgdisable(); 228 } 229 mtx_unlock_spin(&pcicfg_mtx); 230} 231 232int 233pcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus) 234{ 235 236 if (minbus != 0) 237 return (0); 238 239 if (bootverbose) 240 printf("PCIe: Memory Mapped configuration base @ 0x%lx\n", 241 base); 242 243 /* XXX: We should make sure this really fits into the direct map. */ 244 pcie_base = (vm_offset_t)pmap_mapdev(base, (maxbus + 1) << 20); 245 pcie_minbus = minbus; 246 pcie_maxbus = maxbus; 247 cfgmech = CFGMECH_PCIE; 248 return (1); 249} 250 251#define PCIE_VADDR(base, reg, bus, slot, func) \ 252 ((base) + \ 253 ((((bus) & 0xff) << 20) | \ 254 (((slot) & 0x1f) << 15) | \ 255 (((func) & 0x7) << 12) | \ 256 ((reg) & 0xfff))) 257 258static int 259pciereg_cfgread(int bus, unsigned slot, unsigned func, unsigned reg, 260 unsigned bytes) 261{ 262 volatile vm_offset_t va; 263 int data = -1; 264 265 if (bus < pcie_minbus || bus > pcie_maxbus || slot >= 32 || 266 func > PCI_FUNCMAX || reg >= 0x1000) 267 return (-1); 268 269 va = PCIE_VADDR(pcie_base, reg, bus, slot, func); 270 271 switch (bytes) { 272 case 4: 273 data = *(volatile uint32_t *)(va); 274 break; 275 case 2: 276 data = *(volatile uint16_t *)(va); 277 break; 278 case 1: 279 data = *(volatile uint8_t *)(va); 280 break; 281 } 282 283 return (data); 284} 285 286static void 287pciereg_cfgwrite(int bus, unsigned slot, unsigned func, unsigned reg, int data, 288 unsigned bytes) 289{ 290 volatile vm_offset_t va; 291 292 if (bus < pcie_minbus || bus > pcie_maxbus || slot >= 32 || 293 func > PCI_FUNCMAX || reg >= 0x1000) 294 return; 295 296 va = PCIE_VADDR(pcie_base, reg, bus, slot, func); 297 298 switch (bytes) { 299 case 4: 300 *(volatile uint32_t *)(va) = data; 301 break; 302 case 2: 303 *(volatile uint16_t *)(va) = data; 304 break; 305 case 1: 306 *(volatile uint8_t *)(va) = data; 307 break; 308 } 309} 310