1 /* 2 * Copyright (c) 1997, Stefan Esser <se@freebsd.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice unmodified, this list of conditions, and the following 10 * disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * 26 * $Id: pcibus.c,v 1.38 1997/05/26 21:25:24 se Exp $ 27 * 28 */ 29 30 #include <sys/types.h> 31 #include <sys/systm.h> 32 33 #include <pci/pcireg.h> 34 #include <pci/pcivar.h> 35 #include <i386/isa/pcibus.h> 36 37 #ifdef PCI_COMPAT 38 /* XXX this is a terrible hack, which keeps the Tekram AMD SCSI driver happy */ 39 #define cfgmech pci_mechanism 40 int cfgmech; 41 #else 42 static int cfgmech; 43 #endif /* PCI_COMPAT */ 44 static int devmax; 45 46 /* enable configuration space accesses and return data port address */ 47 48 static int 49 pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) 50 { 51 int dataport = 0; 52 53 if (bus <= PCI_BUSMAX 54 && slot < devmax 55 && func <= PCI_FUNCMAX 56 && reg <= PCI_REGMAX 57 && bytes != 3 58 && (unsigned) bytes <= 4 59 && (reg & (bytes -1)) == 0) { 60 switch (cfgmech) { 61 case 1: 62 outl(CONF1_ADDR_PORT, (1 << 31) 63 | (bus << 16) | (slot << 11) 64 | (func << 8) | (reg & ~0x03)); 65 dataport = CONF1_DATA_PORT + (reg & 0x03); 66 break; 67 case 2: 68 outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); 69 outb(CONF2_FORWARD_PORT, bus); 70 dataport = 0xc000 | (slot << 8) | reg; 71 break; 72 } 73 } 74 return (dataport); 75 } 76 77 /* disable configuration space accesses */ 78 79 static void 80 pci_cfgdisable(void) 81 { 82 switch (cfgmech) { 83 case 1: 84 outl(CONF1_ADDR_PORT, 0); 85 break; 86 case 2: 87 outb(CONF2_ENABLE_PORT, 0); 88 outb(CONF2_FORWARD_PORT, 0); 89 break; 90 } 91 } 92 93 /* read configuration space register */ 94 95 int 96 pci_cfgread(pcicfgregs *cfg, int reg, int bytes) 97 { 98 int data = -1; 99 int port; 100 101 port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes); 102 103 if (port != 0) { 104 switch (bytes) { 105 case 1: 106 data = inb(port); 107 break; 108 case 2: 109 data = inw(port); 110 break; 111 case 4: 112 data = inl(port); 113 break; 114 } 115 pci_cfgdisable(); 116 } 117 return (data); 118 } 119 120 /* write configuration space register */ 121 122 void 123 pci_cfgwrite(pcicfgregs *cfg, int reg, int data, int bytes) 124 { 125 int port; 126 127 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 146 static int 147 pci_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 188 int 189 pci_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