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.41 1997/12/20 09:04:25 se Exp $ 27 * 28 */ 29 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 #include <sys/bus.h> 33 #include <sys/kernel.h> 34 35 #include <pci/pcivar.h> 36 #include <i386/isa/pcibus.h> 37 38 #ifdef PCI_COMPAT 39 /* XXX this is a terrible hack, which keeps the Tekram AMD SCSI driver happy */ 40 #define cfgmech pci_mechanism 41 int cfgmech; 42 #else 43 static int cfgmech; 44 #endif /* PCI_COMPAT */ 45 static int devmax; 46 47 /* enable configuration space accesses and return data port address */ 48 49 static int 50 pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) 51 { 52 int dataport = 0; 53 54 if (bus <= PCI_BUSMAX 55 && slot < devmax 56 && func <= PCI_FUNCMAX 57 && reg <= PCI_REGMAX 58 && bytes != 3 59 && (unsigned) bytes <= 4 60 && (reg & (bytes -1)) == 0) { 61 switch (cfgmech) { 62 case 1: 63 outl(CONF1_ADDR_PORT, (1 << 31) 64 | (bus << 16) | (slot << 11) 65 | (func << 8) | (reg & ~0x03)); 66 dataport = CONF1_DATA_PORT + (reg & 0x03); 67 break; 68 case 2: 69 outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); 70 outb(CONF2_FORWARD_PORT, bus); 71 dataport = 0xc000 | (slot << 8) | reg; 72 break; 73 } 74 } 75 return (dataport); 76 } 77 78 /* disable configuration space accesses */ 79 80 static void 81 pci_cfgdisable(void) 82 { 83 switch (cfgmech) { 84 case 1: 85 outl(CONF1_ADDR_PORT, 0); 86 break; 87 case 2: 88 outb(CONF2_ENABLE_PORT, 0); 89 outb(CONF2_FORWARD_PORT, 0); 90 break; 91 } 92 } 93 94 /* read configuration space register */ 95 96 int 97 pci_cfgread(pcicfgregs *cfg, int reg, int bytes) 98 { 99 int data = -1; 100 int port; 101 102 port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes); 103 104 if (port != 0) { 105 switch (bytes) { 106 case 1: 107 data = inb(port); 108 break; 109 case 2: 110 data = inw(port); 111 break; 112 case 4: 113 data = inl(port); 114 break; 115 } 116 pci_cfgdisable(); 117 } 118 return (data); 119 } 120 121 /* write configuration space register */ 122 123 void 124 pci_cfgwrite(pcicfgregs *cfg, int reg, int data, int bytes) 125 { 126 int port; 127 128 port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes); 129 if (port != 0) { 130 switch (bytes) { 131 case 1: 132 outb(port, data); 133 break; 134 case 2: 135 outw(port, data); 136 break; 137 case 4: 138 outl(port, data); 139 break; 140 } 141 pci_cfgdisable(); 142 } 143 } 144 145 /* check whether the configuration mechanism has been correct identified */ 146 147 static int 148 pci_cfgcheck(int maxdev) 149 { 150 u_char device; 151 152 if (bootverbose) 153 printf("pci_cfgcheck:\tdevice "); 154 155 for (device = 0; device < maxdev; device++) { 156 unsigned id, class, header; 157 if (bootverbose) 158 printf("%d ", device); 159 160 id = inl(pci_cfgenable(0, device, 0, 0, 4)); 161 if (id == 0 || id == -1) 162 continue; 163 164 class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8; 165 if (bootverbose) 166 printf("[class=%06x] ", class); 167 if (class == 0 || (class & 0xf870ff) != 0) 168 continue; 169 170 header = inb(pci_cfgenable(0, device, 0, 14, 1)); 171 if (bootverbose) 172 printf("[hdr=%02x] ", header); 173 if ((header & 0x7e) != 0) 174 continue; 175 176 if (bootverbose) 177 printf("is there (id=%08x)\n", id); 178 179 pci_cfgdisable(); 180 return (1); 181 } 182 if (bootverbose) 183 printf("-- nothing found\n"); 184 185 pci_cfgdisable(); 186 return (0); 187 } 188 189 static int 190 pci_cfgopen(void) 191 { 192 unsigned long mode1res,oldval1; 193 unsigned char mode2res,oldval2; 194 195 oldval1 = inl(CONF1_ADDR_PORT); 196 197 if (bootverbose) { 198 printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n", 199 oldval1); 200 } 201 202 if ((oldval1 & CONF1_ENABLE_MSK) == 0) { 203 204 cfgmech = 1; 205 devmax = 32; 206 207 outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 208 outb(CONF1_ADDR_PORT +3, 0); 209 mode1res = inl(CONF1_ADDR_PORT); 210 outl(CONF1_ADDR_PORT, oldval1); 211 212 if (bootverbose) 213 printf("pci_open(1a):\tmode1res=0x%08lx (0x%08lx)\n", 214 mode1res, CONF1_ENABLE_CHK); 215 216 if (mode1res) { 217 if (pci_cfgcheck(32)) 218 return (cfgmech); 219 } 220 221 outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); 222 mode1res = inl(CONF1_ADDR_PORT); 223 outl(CONF1_ADDR_PORT, oldval1); 224 225 if (bootverbose) 226 printf("pci_open(1b):\tmode1res=0x%08lx (0x%08lx)\n", 227 mode1res, CONF1_ENABLE_CHK1); 228 229 if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { 230 if (pci_cfgcheck(32)) 231 return (cfgmech); 232 } 233 } 234 235 oldval2 = inb(CONF2_ENABLE_PORT); 236 237 if (bootverbose) { 238 printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", 239 oldval2); 240 } 241 242 if ((oldval2 & 0xf0) == 0) { 243 244 cfgmech = 2; 245 devmax = 16; 246 247 outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); 248 mode2res = inb(CONF2_ENABLE_PORT); 249 outb(CONF2_ENABLE_PORT, oldval2); 250 251 if (bootverbose) 252 printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", 253 mode2res, CONF2_ENABLE_CHK); 254 255 if (mode2res == CONF2_ENABLE_RES) { 256 if (bootverbose) 257 printf("pci_open(2a):\tnow trying mechanism 2\n"); 258 259 if (pci_cfgcheck(16)) 260 return (cfgmech); 261 } 262 } 263 264 cfgmech = 0; 265 devmax = 0; 266 return (cfgmech); 267 } 268 269 static devclass_t pcib_devclass; 270 271 static int 272 nexus_pcib_probe(device_t dev) 273 { 274 if (pci_cfgopen() != 0) { 275 device_set_desc(dev, "PCI host bus adapter"); 276 277 device_add_child(dev, "pci", 0, 0); 278 return 0; 279 } 280 return ENXIO; 281 } 282 283 static device_method_t nexus_pcib_methods[] = { 284 /* Device interface */ 285 DEVMETHOD(device_probe, nexus_pcib_probe), 286 DEVMETHOD(device_attach, bus_generic_attach), 287 DEVMETHOD(device_shutdown, bus_generic_shutdown), 288 DEVMETHOD(device_suspend, bus_generic_suspend), 289 DEVMETHOD(device_resume, bus_generic_resume), 290 291 /* Bus interface */ 292 DEVMETHOD(bus_print_child, bus_generic_print_child), 293 DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource), 294 DEVMETHOD(bus_release_resource, bus_generic_release_resource), 295 DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), 296 DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), 297 DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), 298 DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), 299 300 { 0, 0 } 301 }; 302 303 static driver_t nexus_pcib_driver = { 304 "pcib", 305 nexus_pcib_methods, 306 1, 307 }; 308 309 DRIVER_MODULE(pcib, nexus, nexus_pcib_driver, pcib_devclass, 0, 0); 310