pci_cfgreg.c revision 114349
1189251Ssam/* 2189251Ssam * Copyright (c) 1997, Stefan Esser <se@freebsd.org> 3189251Ssam * Copyright (c) 2000, Michael Smith <msmith@freebsd.org> 4189251Ssam * Copyright (c) 2000, BSDi 5189251Ssam * All rights reserved. 6189251Ssam * 7189251Ssam * Redistribution and use in source and binary forms, with or without 8189251Ssam * modification, are permitted provided that the following conditions 9189251Ssam * are met: 10189251Ssam * 1. Redistributions of source code must retain the above copyright 11189251Ssam * notice unmodified, this list of conditions, and the following 12189251Ssam * disclaimer. 13189251Ssam * 2. Redistributions in binary form must reproduce the above copyright 14189251Ssam * notice, this list of conditions and the following disclaimer in the 15189251Ssam * documentation and/or other materials provided with the distribution. 16189251Ssam * 17189251Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18189251Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19189251Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20189251Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21189251Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22189251Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23189251Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24189251Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25189251Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26189251Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27189251Ssam * 28189251Ssam * $FreeBSD: head/sys/amd64/pci/pci_cfgreg.c 114349 2003-05-01 01:05:25Z peter $ 29189251Ssam * 30189251Ssam */ 31189251Ssam 32189251Ssam#include <sys/param.h> /* XXX trim includes */ 33189251Ssam#include <sys/systm.h> 34189251Ssam#include <sys/bus.h> 35189251Ssam#include <sys/kernel.h> 36189251Ssam#include <sys/module.h> 37189251Ssam#include <sys/malloc.h> 38189251Ssam#include <sys/lock.h> 39189251Ssam#include <sys/mutex.h> 40189251Ssam#include <vm/vm.h> 41189251Ssam#include <vm/pmap.h> 42189251Ssam#include <machine/md_var.h> 43189251Ssam#include <dev/pci/pcivar.h> 44189251Ssam#include <dev/pci/pcireg.h> 45189251Ssam#include <isa/isavar.h> 46189251Ssam#include <machine/pci_cfgreg.h> 47189251Ssam 48189251Ssam#include "pcib_if.h" 49189251Ssam 50189251Ssamstatic int cfgmech; 51189251Ssamstatic int devmax; 52189251Ssam 53189251Ssamstatic int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes); 54189251Ssamstatic void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes); 55189251Ssamstatic int pcireg_cfgopen(void); 56189251Ssam 57189251Ssamstatic struct mtx pcicfg_mtx; 58189251Ssam 59189251Ssam/* 60189251Ssam * Initialise access to PCI configuration space 61189251Ssam */ 62189251Ssamint 63189251Ssampci_cfgregopen(void) 64189251Ssam{ 65189251Ssam static int opened = 0; 66189251Ssam 67189251Ssam if (opened) 68189251Ssam return (1); 69189251Ssam if (pcireg_cfgopen() == 0) 70189251Ssam return (0); 71189251Ssam mtx_init(&pcicfg_mtx, "pcicfg", NULL, MTX_SPIN); 72189251Ssam opened = 1; 73189251Ssam return (1); 74189251Ssam} 75189251Ssam 76189251Ssam/* 77189251Ssam * Read configuration space register 78189251Ssam */ 79189251Ssamu_int32_t 80189251Ssampci_cfgregread(int bus, int slot, int func, int reg, int bytes) 81189251Ssam{ 82189251Ssam uint32_t line; 83189251Ssam 84189251Ssam /* 85189251Ssam * Some BIOS writers seem to want to ignore the spec and put 86189251Ssam * 0 in the intline rather than 255 to indicate none. Some use 87189251Ssam * numbers in the range 128-254 to indicate something strange and 88189251Ssam * apparently undocumented anywhere. Assume these are completely bogus 89189251Ssam * and map them to 255, which the rest of the PCI code recognizes as 90189251Ssam * as an invalid IRQ. 91189251Ssam */ 92189251Ssam if (reg == PCIR_INTLINE && bytes == 1) { 93189251Ssam line = pcireg_cfgread(bus, slot, func, PCIR_INTLINE, 1); 94189251Ssam if (line == 0 || line >= 128) 95189251Ssam line = PCI_INVALID_IRQ; 96189251Ssam return (line); 97189251Ssam } 98189251Ssam return (pcireg_cfgread(bus, slot, func, reg, bytes)); 99189251Ssam} 100189251Ssam 101189251Ssam/* 102189251Ssam * Write configuration space register 103189251Ssam */ 104189251Ssamvoid 105189251Ssampci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes) 106189251Ssam{ 107189251Ssam 108189251Ssam pcireg_cfgwrite(bus, slot, func, reg, data, bytes); 109189251Ssam} 110189251Ssam 111189251Ssam/* 112189251Ssam * Route a PCI interrupt 113189251Ssam */ 114189251Ssamint 115189251Ssampci_cfgintr(int bus, int device, int pin, int oldirq) 116189251Ssam{ 117189251Ssam 118189251Ssam printf("pci_cfgintr: can't route an interrupt to %d:%d INT%c without ACPI\n", bus, 119189251Ssam device, 'A' + pin - 1); 120189251Ssam return (PCI_INVALID_IRQ); 121189251Ssam} 122189251Ssam 123189251Ssam/* 124189251Ssam * Configuration space access using direct register operations 125189251Ssam */ 126189251Ssam 127189251Ssam/* enable configuration space accesses and return data port address */ 128189251Ssamstatic int 129189251Ssampci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) 130189251Ssam{ 131189251Ssam int dataport = 0; 132189251Ssam 133189251Ssam if (bus <= PCI_BUSMAX 134189251Ssam && slot < devmax 135189251Ssam && func <= PCI_FUNCMAX 136189251Ssam && reg <= PCI_REGMAX 137189251Ssam && bytes != 3 138189251Ssam && (unsigned) bytes <= 4 139189251Ssam && (reg & (bytes - 1)) == 0) { 140189251Ssam switch (cfgmech) { 141189251Ssam case 1: 142189251Ssam outl(CONF1_ADDR_PORT, (1 << 31) 143189251Ssam | (bus << 16) | (slot << 11) 144189251Ssam | (func << 8) | (reg & ~0x03)); 145189251Ssam dataport = CONF1_DATA_PORT + (reg & 0x03); 146189251Ssam break; 147189251Ssam case 2: 148189251Ssam outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); 149189251Ssam outb(CONF2_FORWARD_PORT, bus); 150189251Ssam dataport = 0xc000 | (slot << 8) | reg; 151189251Ssam break; 152189251Ssam } 153189251Ssam } 154189251Ssam return (dataport); 155189251Ssam} 156189251Ssam 157189251Ssam/* disable configuration space accesses */ 158189251Ssamstatic void 159189251Ssampci_cfgdisable(void) 160189251Ssam{ 161189251Ssam switch (cfgmech) { 162189251Ssam case 1: 163189251Ssam outl(CONF1_ADDR_PORT, 0); 164189251Ssam break; 165189251Ssam case 2: 166189251Ssam outb(CONF2_ENABLE_PORT, 0); 167189251Ssam outb(CONF2_FORWARD_PORT, 0); 168189251Ssam break; 169189251Ssam } 170189251Ssam} 171189251Ssam 172189251Ssamstatic int 173189251Ssampcireg_cfgread(int bus, int slot, int func, int reg, int bytes) 174189251Ssam{ 175189251Ssam int data = -1; 176189251Ssam int port; 177189251Ssam 178189251Ssam mtx_lock_spin(&pcicfg_mtx); 179189251Ssam port = pci_cfgenable(bus, slot, func, reg, bytes); 180189251Ssam if (port != 0) { 181189251Ssam switch (bytes) { 182189251Ssam case 1: 183189251Ssam data = inb(port); 184189251Ssam break; 185189251Ssam case 2: 186189251Ssam data = inw(port); 187189251Ssam break; 188189251Ssam case 4: 189189251Ssam data = inl(port); 190189251Ssam break; 191189251Ssam } 192189251Ssam pci_cfgdisable(); 193189251Ssam } 194189251Ssam mtx_unlock_spin(&pcicfg_mtx); 195189251Ssam return (data); 196189251Ssam} 197189251Ssam 198189251Ssamstatic void 199189251Ssampcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 200189251Ssam{ 201189251Ssam int port; 202189251Ssam 203189251Ssam mtx_lock_spin(&pcicfg_mtx); 204189251Ssam port = pci_cfgenable(bus, slot, func, reg, bytes); 205189251Ssam if (port != 0) { 206189251Ssam switch (bytes) { 207189251Ssam case 1: 208189251Ssam outb(port, data); 209189251Ssam break; 210189251Ssam case 2: 211189251Ssam outw(port, data); 212189251Ssam break; 213189251Ssam case 4: 214189251Ssam outl(port, data); 215189251Ssam break; 216189251Ssam } 217189251Ssam pci_cfgdisable(); 218189251Ssam } 219189251Ssam mtx_unlock_spin(&pcicfg_mtx); 220189251Ssam} 221189251Ssam 222189251Ssam/* check whether the configuration mechanism has been correctly identified */ 223189251Ssamstatic int 224189251Ssampci_cfgcheck(int maxdev) 225189251Ssam{ 226189251Ssam uint32_t id, class; 227189251Ssam uint8_t header; 228189251Ssam uint8_t device; 229189251Ssam int port; 230189251Ssam 231189251Ssam if (bootverbose) 232189251Ssam printf("pci_cfgcheck:\tdevice "); 233189251Ssam 234189251Ssam for (device = 0; device < maxdev; device++) { 235189251Ssam if (bootverbose) 236189251Ssam printf("%d ", device); 237189251Ssam 238189251Ssam port = pci_cfgenable(0, device, 0, 0, 4); 239189251Ssam id = inl(port); 240189251Ssam if (id == 0 || id == 0xffffffff) 241189251Ssam continue; 242189251Ssam 243189251Ssam port = pci_cfgenable(0, device, 0, 8, 4); 244189251Ssam class = inl(port) >> 8; 245189251Ssam if (bootverbose) 246189251Ssam printf("[class=%06x] ", class); 247189251Ssam if (class == 0 || (class & 0xf870ff) != 0) 248189251Ssam continue; 249189251Ssam 250189251Ssam port = pci_cfgenable(0, device, 0, 14, 1); 251189251Ssam header = inb(port); 252189251Ssam if (bootverbose) 253189251Ssam printf("[hdr=%02x] ", header); 254189251Ssam if ((header & 0x7e) != 0) 255189251Ssam continue; 256189251Ssam 257189251Ssam if (bootverbose) 258189251Ssam printf("is there (id=%08x)\n", id); 259189251Ssam 260189251Ssam pci_cfgdisable(); 261189251Ssam return (1); 262189251Ssam } 263189251Ssam if (bootverbose) 264189251Ssam printf("-- nothing found\n"); 265189251Ssam 266189251Ssam pci_cfgdisable(); 267189251Ssam return (0); 268189251Ssam} 269189251Ssam 270189251Ssamstatic int 271189251Ssampcireg_cfgopen(void) 272189251Ssam{ 273189251Ssam uint32_t mode1res, oldval1; 274189251Ssam uint8_t mode2res, oldval2; 275189251Ssam 276189251Ssam oldval1 = inl(CONF1_ADDR_PORT); 277189251Ssam 278189251Ssam if (bootverbose) { 279189251Ssam printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08x\n", 280189251Ssam oldval1); 281189251Ssam } 282189251Ssam 283189251Ssam if ((oldval1 & CONF1_ENABLE_MSK) == 0) { 284189251Ssam 285189251Ssam cfgmech = 1; 286189251Ssam devmax = 32; 287189251Ssam 288189251Ssam outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 289189251Ssam outb(CONF1_ADDR_PORT + 3, 0); 290189251Ssam mode1res = inl(CONF1_ADDR_PORT); 291189251Ssam outl(CONF1_ADDR_PORT, oldval1); 292189251Ssam 293189251Ssam if (bootverbose) 294189251Ssam printf("pci_open(1a):\tmode1res=0x%08x (0x%08lx)\n", 295189251Ssam mode1res, CONF1_ENABLE_CHK); 296189251Ssam 297189251Ssam if (mode1res) { 298189251Ssam if (pci_cfgcheck(32)) 299189251Ssam return (cfgmech); 300189251Ssam } 301189251Ssam 302189251Ssam outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); 303189251Ssam mode1res = inl(CONF1_ADDR_PORT); 304189251Ssam outl(CONF1_ADDR_PORT, oldval1); 305189251Ssam 306189251Ssam if (bootverbose) 307189251Ssam printf("pci_open(1b):\tmode1res=0x%08x (0x%08lx)\n", 308189251Ssam mode1res, CONF1_ENABLE_CHK1); 309189251Ssam 310189251Ssam if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { 311189251Ssam if (pci_cfgcheck(32)) 312189251Ssam return (cfgmech); 313189251Ssam } 314189251Ssam } 315189251Ssam 316189251Ssam oldval2 = inb(CONF2_ENABLE_PORT); 317189251Ssam 318189251Ssam if (bootverbose) { 319189251Ssam printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", 320189251Ssam oldval2); 321189251Ssam } 322189251Ssam 323189251Ssam if ((oldval2 & 0xf0) == 0) { 324189251Ssam 325189251Ssam cfgmech = 2; 326189251Ssam devmax = 16; 327189251Ssam 328189251Ssam outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); 329189251Ssam mode2res = inb(CONF2_ENABLE_PORT); 330189251Ssam outb(CONF2_ENABLE_PORT, oldval2); 331189251Ssam 332189251Ssam if (bootverbose) 333189251Ssam printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", 334189251Ssam mode2res, CONF2_ENABLE_CHK); 335189251Ssam 336189251Ssam if (mode2res == CONF2_ENABLE_RES) { 337189251Ssam if (bootverbose) 338189251Ssam printf("pci_open(2a):\tnow trying mechanism 2\n"); 339189251Ssam 340189251Ssam if (pci_cfgcheck(16)) 341189251Ssam return (cfgmech); 342189251Ssam } 343189251Ssam } 344189251Ssam 345189251Ssam cfgmech = 0; 346189251Ssam devmax = 0; 347189251Ssam return (cfgmech); 348189251Ssam} 349189251Ssam