1 /* $NetBSD: pci.c,v 1.5 2011/03/10 21:11:49 phx Exp $ */ 2 3 /*- 4 * Copyright (c) 2007 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Tohru Nishimura. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/param.h> 33 34 #include <lib/libsa/stand.h> 35 36 37 #define MAXNDEVS 32 38 39 #include "globals.h" 40 41 static unsigned cfgread(int, int, int, int); 42 static void cfgwrite(int, int, int, int, unsigned); 43 static void _buswalk(int, 44 int (*)(int, int, int, unsigned long), unsigned long); 45 static int _pcilookup(int, 46 int (*)(int, int, int, unsigned long), unsigned long, 47 struct pcidev *, int, int); 48 static int deviceinit(int, int, int, unsigned long); 49 static void memassign(int, int, int); 50 static int devmatch(int, int, int, unsigned long); 51 static int clsmatch(int, int, int, unsigned long); 52 53 unsigned memstart, memlimit; 54 unsigned iostart, iolimit; 55 unsigned maxbus; 56 57 void 58 pcisetup(void) 59 { 60 61 memstart = PCI_MEMBASE; 62 memlimit = PCI_MEMLIMIT; 63 iostart = PCI_IOBASE; 64 iolimit = PCI_IOLIMIT; 65 maxbus = 0; 66 67 (void)_buswalk(0, deviceinit, 0UL); 68 } 69 70 int 71 pcifinddev(unsigned vend, unsigned prod, unsigned *tag) 72 { 73 unsigned pciid; 74 struct pcidev target; 75 76 pciid = PCI_DEVICE(vend, prod); 77 if (_pcilookup(0, devmatch, pciid, &target, 0, 1)) { 78 *tag = target.bdf; 79 return 0; 80 } 81 *tag = ~0; 82 return -1; 83 } 84 85 int 86 pcilookup(type, list, max) 87 unsigned type; 88 struct pcidev *list; 89 int max; 90 { 91 92 return _pcilookup(0, clsmatch, type, list, 0, max); 93 } 94 95 unsigned 96 pcimaketag(int b, int d, int f) 97 { 98 99 return (1U << 31) | (b << 16) | (d << 11) | (f << 8); 100 } 101 102 void 103 pcidecomposetag(unsigned tag, int *b, int *d, int *f) 104 { 105 106 if (b != NULL) 107 *b = (tag >> 16) & 0xff; 108 if (d != NULL) 109 *d = (tag >> 11) & 0x1f; 110 if (f != NULL) 111 *f = (tag >> 8) & 0x7; 112 return; 113 } 114 115 unsigned 116 pcicfgread(unsigned tag, int off) 117 { 118 unsigned cfg; 119 120 cfg = tag | (off &~ 03); 121 iohtole32(CONFIG_ADDR, cfg); 122 return iole32toh(CONFIG_DATA); 123 } 124 125 void 126 pcicfgwrite(unsigned tag, int off, unsigned val) 127 { 128 unsigned cfg; 129 130 cfg = tag | (off &~ 03); 131 iohtole32(CONFIG_ADDR, cfg); 132 iohtole32(CONFIG_DATA, val); 133 } 134 135 static unsigned 136 cfgread(int b, int d, int f, int off) 137 { 138 unsigned cfg; 139 140 off &= ~03; 141 cfg = (1U << 31) | (b << 16) | (d << 11) | (f << 8) | off | 0; 142 iohtole32(CONFIG_ADDR, cfg); 143 return iole32toh(CONFIG_DATA); 144 } 145 146 static void 147 cfgwrite(int b, int d, int f, int off, unsigned val) 148 { 149 unsigned cfg; 150 151 off &= ~03; 152 cfg = (1U << 31) | (b << 16) | (d << 11) | (f << 8) | off | 0; 153 iohtole32(CONFIG_ADDR, cfg); 154 iohtole32(CONFIG_DATA, val); 155 } 156 157 static void 158 _buswalk(int bus, int (*proc)(int, int, int, unsigned long), unsigned long data) 159 { 160 int device, function, nfunctions; 161 unsigned pciid, bhlcr; 162 163 for (device = 0; device < MAXNDEVS; device++) { 164 pciid = cfgread(bus, device, 0, PCI_ID_REG); 165 if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID) 166 continue; 167 if (PCI_VENDOR(pciid) == 0) 168 continue; 169 bhlcr = cfgread(bus, device, 0, PCI_BHLC_REG); 170 nfunctions = (PCI_HDRTYPE_MULTIFN(bhlcr)) ? 8 : 1; 171 for (function = 0; function < nfunctions; function++) { 172 pciid = cfgread(bus, device, function, PCI_ID_REG); 173 if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID) 174 continue; 175 if (PCI_VENDOR(pciid) == 0) 176 continue; 177 178 if ((*proc)(bus, device, function, data) != 0) 179 goto out; /* early exit */ 180 } 181 } 182 out:; 183 } 184 185 static int 186 deviceinit(int bus, int dev, int func, unsigned long data) 187 { 188 unsigned val; 189 190 /* 0x00 */ 191 #ifdef DEBUG 192 printf("%02d:%02d:%02d:", bus, dev, func); 193 val = cfgread(bus, dev, func, PCI_ID_REG); 194 printf(" chip %04x.%04x", val & 0xffff, val>>16); 195 val = cfgread(bus, dev, func, 0x2c); 196 printf(" card %04x.%04x", val & 0xffff, val>>16); 197 val = cfgread(bus, dev, func, PCI_CLASS_REG); 198 printf(" rev %02x class %02x.%02x.%02x", 199 PCI_REVISION(val), (val>>24), (val>>16) & 0xff, 200 PCI_INTERFACE(val)); 201 val = cfgread(bus, dev, func, PCI_BHLC_REG); 202 printf(" hdr %02x\n", (val>>16) & 0xff); 203 #endif 204 205 /* 0x04 */ 206 val = cfgread(bus, dev, func, PCI_COMMAND_STATUS_REG); 207 val |= 0xffff0107; /* enable IO,MEM,MASTER,SERR */ 208 cfgwrite(bus, dev, func, 0x04, val); 209 210 /* 0x0c */ 211 val = 0x80 << 8 | 0x08 /* 32B cache line */; 212 cfgwrite(bus, dev, func, PCI_BHLC_REG, val); 213 214 /* 0x3c */ 215 val = cfgread(bus, dev, func, 0x3c) & ~0xff; 216 val |= dev; /* assign IDSEL */ 217 cfgwrite(bus, dev, func, 0x3c, val); 218 219 /* skip legacy mode IDE controller BAR assignment */ 220 val = cfgread(bus, dev, func, PCI_CLASS_REG); 221 if (PCI_CLASS(val) == PCI_CLASS_IDE && (PCI_INTERFACE(val) & 0x05) == 0) 222 return 0; 223 224 memassign(bus, dev, func); 225 226 /* descending toward PCI-PCI bridge */ 227 if ((cfgread(bus, dev, func, PCI_CLASS_REG) >> 16) == PCI_CLASS_PPB) { 228 unsigned new; 229 230 /* 0x18 */ 231 new = (maxbus += 1); 232 val = (0xff << 16) | (new << 8) | bus; 233 cfgwrite(bus, dev, func, 0x18, val); 234 235 /* 0x1c and 0x30 */ 236 val = (iostart + (0xfff)) & ~0xfff; /* 4KB boundary */ 237 iostart = val; 238 val = 0xffff0000 | (iolimit & 0xf000) | (val & 0xf000) >> 8; 239 cfgwrite(bus, dev, func, 0x1c, val); 240 val = (iolimit & 0xffff0000) | (val & 0xffff0000) >> 16; 241 cfgwrite(bus, dev, func, 0x30, val); 242 243 /* 0x20 */ 244 val = (memstart + 0xfffff) &~ 0xfffff; /* 1MB boundary */ 245 memstart = val; 246 val = (memlimit & 0xffff0000) | (val & 0xffff0000) >> 16; 247 cfgwrite(bus, dev, func, 0x20, val); 248 249 /* redo 0x04 */ 250 val = cfgread(bus, dev, func, 0x04); 251 val |= 0xffff0107; 252 cfgwrite(bus, dev, func, 0x04, val); 253 254 _buswalk(new, deviceinit, data); 255 256 /* adjust 0x18 */ 257 val = cfgread(bus, dev, func, 0x18); 258 val = (maxbus << 16) | (val & 0xffff); 259 cfgwrite(bus, dev, func, 0x18, val); 260 } 261 return 0; 262 } 263 264 static void 265 memassign(int bus, int dev, int func) 266 { 267 unsigned val, maxbar, mapr, req, mapbase, size; 268 269 val = cfgread(bus, dev, func, 0x0c); 270 switch (PCI_HDRTYPE_TYPE(val)) { 271 case 0: 272 maxbar = 0x10 + 6 * 4; break; 273 case 1: 274 maxbar = 0x10 + 2 * 4; break; 275 default: 276 maxbar = 0x10 + 1 * 4; break; 277 } 278 for (mapr = 0x10; mapr < maxbar; mapr += 4) { 279 cfgwrite(bus, dev, func, mapr, 0xffffffff); 280 val = cfgread(bus, dev, func, mapr); 281 if (val & 01) { 282 /* PCI IO space */ 283 req = ~(val & 0xfffffffc) + 1; 284 if (req & (req - 1)) /* power of 2 */ 285 continue; 286 if (req == 0) /* ever exists */ 287 continue; 288 size = (req > 0x10) ? req : 0x10; 289 mapbase = (iostart + size - 1) & ~(size - 1); 290 if (mapbase + size > iolimit) 291 continue; 292 293 iostart = mapbase + size; 294 /* establish IO space */ 295 cfgwrite(bus, dev, func, mapr, mapbase | 01); 296 } 297 else { 298 /* PCI memory space */ 299 req = ~(val & 0xfffffff0) + 1; 300 if (req & (req - 1)) /* power of 2 */ 301 continue; 302 if (req == 0) /* ever exists */ 303 continue; 304 val &= 0x6; 305 if (val == 2 || val == 6) 306 continue; 307 size = (req > 0x1000) ? req : 0x1000; 308 mapbase = (memstart + size - 1) & ~(size - 1); 309 if (mapbase + size > memlimit) 310 continue; 311 312 memstart = mapbase + size; 313 /* establish memory space */ 314 cfgwrite(bus, dev, func, mapr, mapbase); 315 if (val == 4) { 316 mapr += 4; 317 cfgwrite(bus, dev, func, mapr, 0); 318 } 319 } 320 DPRINTF(("%s base %x size %x\n", (val & 01) ? "i/o" : "mem", 321 mapbase, size)); 322 } 323 } 324 325 static int 326 devmatch(int bus, int dev, int func, unsigned long data) 327 { 328 unsigned pciid; 329 330 pciid = cfgread(bus, dev, func, PCI_ID_REG); 331 return (pciid == (unsigned)data); 332 } 333 334 static int 335 clsmatch(int bus, int dev, int func, unsigned long data) 336 { 337 unsigned class; 338 339 class = cfgread(bus, dev, func, PCI_CLASS_REG); 340 return PCI_CLASS(class) == (unsigned)data; 341 } 342 343 static int 344 _pcilookup(int bus, int (*match)(int, int, int, unsigned long), unsigned long data, struct pcidev *list, int index, int limit) 345 { 346 int device, function, nfuncs; 347 unsigned pciid, bhlcr, class; 348 349 for (device = 0; device < MAXNDEVS; device++) { 350 pciid = cfgread(bus, device, 0, PCI_ID_REG); 351 if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID) 352 continue; 353 if (PCI_VENDOR(pciid) == 0) 354 continue; 355 class = cfgread(bus, device, 0, PCI_CLASS_REG); 356 if (PCI_CLASS(class) == PCI_CLASS_PPB) { 357 /* exploring bus beyond PCI-PCI bridge */ 358 index = _pcilookup(bus + 1, 359 match, data, list, index, limit); 360 if (index >= limit) 361 goto out; 362 continue; 363 } 364 bhlcr = cfgread(bus, device, 0, PCI_BHLC_REG); 365 nfuncs = (PCI_HDRTYPE_MULTIFN(bhlcr)) ? 8 : 1; 366 for (function = 0; function < nfuncs; function++) { 367 pciid = cfgread(bus, device, function, PCI_ID_REG); 368 if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID) 369 continue; 370 if (PCI_VENDOR(pciid) == 0) 371 continue; 372 if ((*match)(bus, device, function, data)) { 373 list[index].pvd = pciid; 374 list[index].bdf = 375 pcimaketag(bus, device, function); 376 index += 1; 377 if (index >= limit) 378 goto out; 379 } 380 } 381 } 382 out: 383 return index; 384 } 385