pci_cfgreg.c revision 26174
1274955Ssvnmir/* 2274955Ssvnmir * Copyright (c) 1997, Stefan Esser <se@freebsd.org> 3274955Ssvnmir * All rights reserved. 4274955Ssvnmir * 5274955Ssvnmir * Redistribution and use in source and binary forms, with or without 6274955Ssvnmir * modification, are permitted provided that the following conditions 7274955Ssvnmir * are met: 8274955Ssvnmir * 1. Redistributions of source code must retain the above copyright 9274955Ssvnmir * notice unmodified, this list of conditions, and the following 10274955Ssvnmir * disclaimer. 11274955Ssvnmir * 2. Redistributions in binary form must reproduce the above copyright 12274955Ssvnmir * notice, this list of conditions and the following disclaimer in the 13274955Ssvnmir * documentation and/or other materials provided with the distribution. 14274955Ssvnmir * 15274955Ssvnmir * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16274955Ssvnmir * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17274955Ssvnmir * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18274955Ssvnmir * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19274955Ssvnmir * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20274955Ssvnmir * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21274955Ssvnmir * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22274955Ssvnmir * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23274955Ssvnmir * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24274955Ssvnmir * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25274955Ssvnmir * 26274955Ssvnmir * $Id: pcibus.c,v 1.38 1997/05/26 21:25:24 se Exp $ 27274955Ssvnmir * 28274955Ssvnmir */ 29274955Ssvnmir 30274955Ssvnmir#include <sys/types.h> 31274955Ssvnmir#include <sys/systm.h> 32274955Ssvnmir 33274955Ssvnmir#include <pci/pcireg.h> 34274955Ssvnmir#include <pci/pcivar.h> 35274955Ssvnmir#include <i386/isa/pcibus.h> 36274955Ssvnmir 37274955Ssvnmir#ifdef PCI_COMPAT 38274955Ssvnmir/* XXX this is a terrible hack, which keeps the Tekram AMD SCSI driver happy */ 39274955Ssvnmir#define cfgmech pci_mechanism 40274955Ssvnmirint cfgmech; 41274955Ssvnmir#else 42274955Ssvnmirstatic int cfgmech; 43274955Ssvnmir#endif /* PCI_COMPAT */ 44274955Ssvnmirstatic int devmax; 45274955Ssvnmir 46274955Ssvnmir/* enable configuration space accesses and return data port address */ 47274955Ssvnmir 48274955Ssvnmirstatic int 49274955Ssvnmirpci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) 50274955Ssvnmir{ 51274955Ssvnmir int dataport = 0; 52274955Ssvnmir 53274955Ssvnmir if (bus <= PCI_BUSMAX 54274955Ssvnmir && slot < devmax 55274955Ssvnmir && func <= PCI_FUNCMAX 56274955Ssvnmir && reg <= PCI_REGMAX 57274955Ssvnmir && bytes != 3 58274955Ssvnmir && (unsigned) bytes <= 4 59274955Ssvnmir && (reg & (bytes -1)) == 0) { 60274955Ssvnmir switch (cfgmech) { 61274955Ssvnmir case 1: 62274955Ssvnmir outl(CONF1_ADDR_PORT, (1 << 31) 63274955Ssvnmir | (bus << 16) | (slot << 11) 64274955Ssvnmir | (func << 8) | (reg & ~0x03)); 65274955Ssvnmir dataport = CONF1_DATA_PORT + (reg & 0x03); 66274955Ssvnmir break; 67274955Ssvnmir case 2: 68274955Ssvnmir outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); 69274955Ssvnmir outb(CONF2_FORWARD_PORT, bus); 70274955Ssvnmir dataport = 0xc000 | (slot << 8) | reg; 71274955Ssvnmir break; 72274955Ssvnmir } 73274955Ssvnmir } 74274955Ssvnmir return (dataport); 75274955Ssvnmir} 76274955Ssvnmir 77274955Ssvnmir/* disable configuration space accesses */ 78274955Ssvnmir 79274955Ssvnmirstatic void 80274955Ssvnmirpci_cfgdisable(void) 81274955Ssvnmir{ 82274955Ssvnmir switch (cfgmech) { 83274955Ssvnmir case 1: 84274955Ssvnmir outl(CONF1_ADDR_PORT, 0); 85274955Ssvnmir break; 86274955Ssvnmir case 2: 87274955Ssvnmir outb(CONF2_ENABLE_PORT, 0); 88274955Ssvnmir outb(CONF2_FORWARD_PORT, 0); 89274955Ssvnmir break; 90274955Ssvnmir } 91274955Ssvnmir} 92274955Ssvnmir 93274955Ssvnmir/* read configuration space register */ 94274955Ssvnmir 95274955Ssvnmirint 96274955Ssvnmirpci_cfgread(pcicfgregs *cfg, int reg, int bytes) 97274955Ssvnmir{ 98274955Ssvnmir int data = -1; 99274955Ssvnmir int port; 100274955Ssvnmir 101274955Ssvnmir port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes); 102274955Ssvnmir 103274955Ssvnmir if (port != 0) { 104274955Ssvnmir switch (bytes) { 105274955Ssvnmir case 1: 106274955Ssvnmir data = inb(port); 107274955Ssvnmir break; 108274955Ssvnmir case 2: 109274955Ssvnmir data = inw(port); 110274955Ssvnmir break; 111274955Ssvnmir case 4: 112274955Ssvnmir data = inl(port); 113274955Ssvnmir break; 114274955Ssvnmir } 115274955Ssvnmir pci_cfgdisable(); 116274955Ssvnmir } 117274955Ssvnmir return (data); 118274955Ssvnmir} 119274955Ssvnmir 120274955Ssvnmir/* write configuration space register */ 121274955Ssvnmir 122274955Ssvnmirvoid 123274955Ssvnmirpci_cfgwrite(pcicfgregs *cfg, int reg, int data, int bytes) 124274955Ssvnmir{ 125274955Ssvnmir int port; 126274955Ssvnmir 127274955Ssvnmir port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes); 128 if (port != 0) { 129 switch (bytes) { 130 case 1: 131 outb(port, data); 132 break; 133 case 2: 134 outw(port, data); 135 break; 136 case 4: 137 outl(port, data); 138 break; 139 } 140 pci_cfgdisable(); 141 } 142} 143 144/* check whether the configuration mechanism has been correct identified */ 145 146static int 147pci_cfgcheck(int maxdev) 148{ 149 u_char device; 150 151 if (bootverbose) 152 printf("pci_cfgcheck:\tdevice "); 153 154 for (device = 0; device < maxdev; device++) { 155 unsigned id, class, header; 156 if (bootverbose) 157 printf("%d ", device); 158 159 id = inl(pci_cfgenable(0, device, 0, 0, 4)); 160 if (id == 0 || id == -1) 161 continue; 162 163 class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8; 164 if (bootverbose) 165 printf("[class=%06x] ", class); 166 if (class == 0 || (class & 0xf8f0ff) != 0) 167 continue; 168 169 header = inb(pci_cfgenable(0, device, 0, 14, 1)); 170 if (bootverbose) 171 printf("[hdr=%02x] ", header); 172 if ((header & 0x7e) != 0) 173 continue; 174 175 if (bootverbose) 176 printf("is there (id=%08x)\n", id); 177 178 pci_cfgdisable(); 179 return (1); 180 } 181 if (bootverbose) 182 printf("-- nothing found\n"); 183 184 pci_cfgdisable(); 185 return (0); 186} 187 188int 189pci_cfgopen(void) 190{ 191 unsigned long mode1res,oldval1; 192 unsigned char mode2res,oldval2; 193 194 oldval1 = inl(CONF1_ADDR_PORT); 195 196 if (bootverbose) { 197 printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n", 198 oldval1); 199 } 200 201 if ((oldval1 & CONF1_ENABLE_MSK) == 0) { 202 203 cfgmech = 1; 204 devmax = 32; 205 206 outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 207 outb(CONF1_ADDR_PORT +3, 0); 208 mode1res = inl(CONF1_ADDR_PORT); 209 outl(CONF1_ADDR_PORT, oldval1); 210 211 if (bootverbose) 212 printf("pci_open(1a):\tmode1res=0x%08lx (0x%08lx)\n", 213 mode1res, CONF1_ENABLE_CHK); 214 215 if (mode1res) { 216 if (pci_cfgcheck(32)) 217 return (cfgmech); 218 } 219 220 outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); 221 mode1res = inl(CONF1_ADDR_PORT); 222 outl(CONF1_ADDR_PORT, oldval1); 223 224 if (bootverbose) 225 printf("pci_open(1b):\tmode1res=0x%08lx (0x%08lx)\n", 226 mode1res, CONF1_ENABLE_CHK1); 227 228 if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { 229 if (pci_cfgcheck(32)) 230 return (cfgmech); 231 } 232 } 233 234 oldval2 = inb(CONF2_ENABLE_PORT); 235 236 if (bootverbose) { 237 printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", 238 oldval2); 239 } 240 241 if ((oldval2 & 0xf0) == 0) { 242 243 cfgmech = 2; 244 devmax = 16; 245 246 outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); 247 mode2res = inb(CONF2_ENABLE_PORT); 248 outb(CONF2_ENABLE_PORT, oldval2); 249 250 if (bootverbose) 251 printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", 252 mode2res, CONF2_ENABLE_CHK); 253 254 if (mode2res == CONF2_ENABLE_RES) { 255 if (bootverbose) 256 printf("pci_open(2a):\tnow trying mechanism 2\n"); 257 258 if (pci_cfgcheck(16)) 259 return (cfgmech); 260 } 261 } 262 263 cfgmech = 0; 264 devmax = 0; 265 return (cfgmech); 266} 267