1 /* $NetBSD: pci.c,v 1.4 2011/02/14 06:21:29 nisimura 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, 0x00); 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, 0x08); 198 printf(" rev %02x class %02x.%02x.%02x", 199 val & 0xff, (val>>24), (val>>16) & 0xff, (val>>8) & 0xff); 200 val = cfgread(bus, dev, func, 0x0c); 201 printf(" hdr %02x\n", (val>>16) & 0xff); 202 #endif 203 204 /* 0x04 */ 205 val = cfgread(bus, dev, func, 0x04); 206 val |= 0xffff0107; /* enable IO,MEM,MASTER,SERR */ 207 cfgwrite(bus, dev, func, 0x04, val); 208 209 /* 0x0c */ 210 val = 0x80 << 8 | 0x08 /* 32B cache line */; 211 cfgwrite(bus, dev, func, 0x0c, val); 212 213 /* 0x3c */ 214 val = cfgread(bus, dev, func, 0x3c) & ~0xff; 215 val |= dev; /* assign IDSEL */ 216 cfgwrite(bus, dev, func, 0x3c, val); 217 218 /* skip legacy mode IDE controller BAR assignment */ 219 val = cfgread(bus, dev, func, PCI_CLASS_REG); 220 if ((val >> 16) == PCI_CLASS_IDE && ((val >> 8) & 0x05) == 0) 221 return 0; 222 223 memassign(bus, dev, func); 224 225 /* descending toward PCI-PCI bridge */ 226 if ((cfgread(bus, dev, func, 0x08) >> 16) == PCI_CLASS_PPB) { 227 unsigned new; 228 229 /* 0x18 */ 230 new = (maxbus += 1); 231 val = (0xff << 16) | (new << 8) | bus; 232 cfgwrite(bus, dev, func, 0x18, val); 233 234 /* 0x1c and 0x30 */ 235 val = (iostart + (0xfff)) & ~0xfff; /* 4KB boundary */ 236 iostart = val; 237 val = 0xffff0000 | (iolimit & 0xf000) | (val & 0xf000) >> 8; 238 cfgwrite(bus, dev, func, 0x1c, val); 239 val = (iolimit & 0xffff0000) | (val & 0xffff0000) >> 16; 240 cfgwrite(bus, dev, func, 0x30, val); 241 242 /* 0x20 */ 243 val = (memstart + 0xfffff) &~ 0xfffff; /* 1MB boundary */ 244 memstart = val; 245 val = (memlimit & 0xffff0000) | (val & 0xffff0000) >> 16; 246 cfgwrite(bus, dev, func, 0x20, val); 247 248 /* redo 0x04 */ 249 val = cfgread(bus, dev, func, 0x04); 250 val |= 0xffff0107; 251 cfgwrite(bus, dev, func, 0x04, val); 252 253 _buswalk(new, deviceinit, data); 254 255 /* adjust 0x18 */ 256 val = cfgread(bus, dev, func, 0x18); 257 val = (maxbus << 16) | (val & 0xffff); 258 cfgwrite(bus, dev, func, 0x18, val); 259 } 260 return 0; 261 } 262 263 static void 264 memassign(int bus, int dev, int func) 265 { 266 unsigned val, maxbar, mapr, req, mapbase, size; 267 268 val = cfgread(bus, dev, func, 0x0c); 269 switch (PCI_HDRTYPE_TYPE(val)) { 270 case 0: 271 maxbar = 0x10 + 6 * 4; break; 272 case 1: 273 maxbar = 0x10 + 2 * 4; break; 274 default: 275 maxbar = 0x10 + 1 * 4; break; 276 } 277 for (mapr = 0x10; mapr < maxbar; mapr += 4) { 278 cfgwrite(bus, dev, func, mapr, 0xffffffff); 279 val = cfgread(bus, dev, func, mapr); 280 if (val & 01) { 281 /* PCI IO space */ 282 req = ~(val & 0xfffffffc) + 1; 283 if (req & (req - 1)) /* power of 2 */ 284 continue; 285 if (req == 0) /* ever exists */ 286 continue; 287 size = (req > 0x10) ? req : 0x10; 288 mapbase = (iostart + size - 1) & ~(size - 1); 289 if (mapbase + size > iolimit) 290 continue; 291 292 iostart = mapbase + size; 293 /* establish IO space */ 294 cfgwrite(bus, dev, func, mapr, mapbase | 01); 295 } 296 else { 297 /* PCI memory space */ 298 req = ~(val & 0xfffffff0) + 1; 299 if (req & (req - 1)) /* power of 2 */ 300 continue; 301 if (req == 0) /* ever exists */ 302 continue; 303 val &= 0x6; 304 if (val == 2 || val == 6) 305 continue; 306 size = (req > 0x1000) ? req : 0x1000; 307 mapbase = (memstart + size - 1) & ~(size - 1); 308 if (mapbase + size > memlimit) 309 continue; 310 311 memstart = mapbase + size; 312 /* establish memory space */ 313 cfgwrite(bus, dev, func, mapr, mapbase); 314 if (val == 4) { 315 mapr += 4; 316 cfgwrite(bus, dev, func, mapr, 0); 317 } 318 } 319 DPRINTF(("%s base %x size %x\n", (val & 01) ? "i/o" : "mem", 320 mapbase, size)); 321 } 322 } 323 324 static int 325 devmatch(int bus, int dev, int func, unsigned long data) 326 { 327 unsigned pciid; 328 329 pciid = cfgread(bus, dev, func, PCI_ID_REG); 330 return (pciid == (unsigned)data); 331 } 332 333 static int 334 clsmatch(int bus, int dev, int func, unsigned long data) 335 { 336 unsigned class; 337 338 class = cfgread(bus, dev, func, PCI_CLASS_REG); 339 return ((class >> 16) == (unsigned)data); 340 } 341 342 static int 343 _pcilookup(int bus, int (*match)(int, int, int, unsigned long), unsigned long data, struct pcidev *list, int index, int limit) 344 { 345 int device, function, nfuncs; 346 unsigned pciid, bhlcr, class; 347 348 for (device = 0; device < MAXNDEVS; device++) { 349 pciid = cfgread(bus, device, 0, PCI_ID_REG); 350 if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID) 351 continue; 352 if (PCI_VENDOR(pciid) == 0) 353 continue; 354 class = cfgread(bus, device, 0, PCI_CLASS_REG); 355 if ((class >> 16) == PCI_CLASS_PPB) { 356 /* exploring bus beyond PCI-PCI bridge */ 357 index = _pcilookup(bus + 1, 358 match, data, list, index, limit); 359 if (index >= limit) 360 goto out; 361 continue; 362 } 363 bhlcr = cfgread(bus, device, 0, PCI_BHLC_REG); 364 nfuncs = (PCI_HDRTYPE_MULTIFN(bhlcr)) ? 8 : 1; 365 for (function = 0; function < nfuncs; function++) { 366 pciid = cfgread(bus, device, function, PCI_ID_REG); 367 if (PCI_VENDOR(pciid) == PCI_VENDOR_INVALID) 368 continue; 369 if (PCI_VENDOR(pciid) == 0) 370 continue; 371 if ((*match)(bus, device, function, data)) { 372 list[index].pvd = pciid; 373 list[index].bdf = 374 pcimaketag(bus, device, function); 375 index += 1; 376 if (index >= limit) 377 goto out; 378 } 379 } 380 } 381 out: 382 return index; 383 } 384