[a31845f7] | 1 | /* PCI (Auto) configuration Library. Setup PCI configuration space and IRQ. |
---|
| 2 | * |
---|
[71e8a5c] | 3 | * COPYRIGHT (c) 2010 Cobham Gaisler AB. |
---|
[a31845f7] | 4 | * |
---|
| 5 | * The license and distribution terms for this file may be |
---|
| 6 | * found in the file LICENSE in this distribution or at |
---|
[e53daed] | 7 | * http://www.rtems.org/license/LICENSE. |
---|
[a31845f7] | 8 | */ |
---|
| 9 | |
---|
| 10 | #include <rtems.h> |
---|
| 11 | #include <stdlib.h> |
---|
| 12 | #include <rtems/bspIo.h> |
---|
| 13 | #include <string.h> |
---|
| 14 | |
---|
| 15 | /* Configure headers */ |
---|
| 16 | #define PCI_CFG_AUTO_LIB |
---|
| 17 | |
---|
| 18 | #include <pci.h> |
---|
| 19 | #include <pci/access.h> |
---|
| 20 | #include <pci/cfg.h> |
---|
| 21 | |
---|
[f4bf22c] | 22 | #include "pci_internal.h" |
---|
| 23 | |
---|
[a31845f7] | 24 | /* #define DEBUG */ |
---|
| 25 | |
---|
| 26 | #ifdef DEBUG |
---|
| 27 | #define DBG(x...) printk(x) |
---|
| 28 | #else |
---|
| 29 | #define DBG(x...) |
---|
| 30 | #endif |
---|
| 31 | |
---|
| 32 | /* PCI Library |
---|
| 33 | * (For debugging it might be good to use other functions or the driver's |
---|
| 34 | * directly) |
---|
| 35 | */ |
---|
| 36 | #define PCI_CFG_R8(dev, args...) pci_cfg_r8(dev, args) |
---|
| 37 | #define PCI_CFG_R16(dev, args...) pci_cfg_r16(dev, args) |
---|
| 38 | #define PCI_CFG_R32(dev, args...) pci_cfg_r32(dev, args) |
---|
| 39 | #define PCI_CFG_W8(dev, args...) pci_cfg_w8(dev, args) |
---|
| 40 | #define PCI_CFG_W16(dev, args...) pci_cfg_w16(dev, args) |
---|
| 41 | #define PCI_CFG_W32(dev, args...) pci_cfg_w32(dev, args) |
---|
| 42 | |
---|
| 43 | int pci_config_auto_initialized = 0; |
---|
| 44 | |
---|
| 45 | /* Configuration setup */ |
---|
| 46 | struct pci_auto_setup pci_auto_cfg; |
---|
| 47 | |
---|
| 48 | /* Insert BAR into the sorted resources list. The BARs are sorted on the |
---|
| 49 | * BAR size/alignment need. |
---|
| 50 | */ |
---|
| 51 | static void pci_res_insert(struct pci_res **root, struct pci_res *res) |
---|
| 52 | { |
---|
| 53 | struct pci_res *curr, *last; |
---|
| 54 | unsigned long curr_size_resulting_boundary, size_resulting_boundary; |
---|
| 55 | unsigned long boundary, size; |
---|
| 56 | |
---|
| 57 | res->start = 0; |
---|
| 58 | res->end = 0; |
---|
| 59 | boundary = res->boundary; |
---|
| 60 | size = res->size; |
---|
| 61 | |
---|
| 62 | /* Insert the resources depending on the boundary needs |
---|
| 63 | * Normally the boundary=size of the BAR, however when |
---|
| 64 | * PCI bridges are involved the bridge's boundary may be |
---|
[1f4f1e2e] | 65 | * smaller than the size due to the fact that a bridge |
---|
[a31845f7] | 66 | * may have different-sized BARs behind, the largest BAR |
---|
| 67 | * (also the BAR with the largest boundary) will decide |
---|
| 68 | * the alignment need. |
---|
| 69 | */ |
---|
| 70 | last = NULL; |
---|
| 71 | curr = *root; |
---|
| 72 | |
---|
| 73 | /* Order List after boundary, the boundary is maintained |
---|
| 74 | * when the size is on an equal boundary, normally it is |
---|
| 75 | * but may not be with bridges. So in second hand it is |
---|
| 76 | * sorted after resulting boundary - the boundary after |
---|
| 77 | * the resource. |
---|
| 78 | */ |
---|
| 79 | while (curr && (curr->boundary >= boundary)) { |
---|
| 80 | if (curr->boundary == boundary) { |
---|
| 81 | /* Find Resulting boundary of size */ |
---|
| 82 | size_resulting_boundary = 1; |
---|
| 83 | while ((size & size_resulting_boundary) == 0) |
---|
| 84 | size_resulting_boundary = |
---|
| 85 | size_resulting_boundary << 1; |
---|
| 86 | |
---|
| 87 | /* Find Resulting boundary of curr->size */ |
---|
| 88 | curr_size_resulting_boundary = 1; |
---|
| 89 | while ((curr->size & curr_size_resulting_boundary) == 0) |
---|
| 90 | curr_size_resulting_boundary = |
---|
| 91 | curr_size_resulting_boundary << 1; |
---|
| 92 | |
---|
| 93 | if (size_resulting_boundary >= |
---|
| 94 | curr_size_resulting_boundary) |
---|
| 95 | break; |
---|
| 96 | } |
---|
| 97 | last = curr; |
---|
| 98 | curr = curr->next; |
---|
| 99 | } |
---|
| 100 | |
---|
| 101 | if (last == NULL) { |
---|
| 102 | /* Insert first in list */ |
---|
| 103 | res->next = *root; |
---|
| 104 | *root = res; |
---|
| 105 | } else { |
---|
| 106 | last->next = res; |
---|
| 107 | res->next = curr; |
---|
| 108 | } |
---|
| 109 | } |
---|
| 110 | |
---|
| 111 | #ifdef DEBUG |
---|
| 112 | void pci_res_list_print(struct pci_res *root) |
---|
| 113 | { |
---|
| 114 | if (root == NULL) |
---|
| 115 | return; |
---|
| 116 | |
---|
| 117 | printf("RESOURCE LIST:\n"); |
---|
| 118 | while (root) { |
---|
| 119 | printf(" SIZE: 0x%08x, BOUNDARY: 0x%08x\n", root->size, |
---|
| 120 | root->boundary); |
---|
| 121 | root = root->next; |
---|
| 122 | } |
---|
| 123 | } |
---|
| 124 | #endif |
---|
| 125 | |
---|
| 126 | /* Reorder a size/alignment ordered resources list. The idea is to |
---|
| 127 | * avoid unused due to alignment/size restriction. |
---|
| 128 | * |
---|
| 129 | * NOTE: The first element is always untouched. |
---|
| 130 | * NOTE: If less than three elements in list, nothing will be done |
---|
| 131 | * |
---|
| 132 | * Normally a BAR has the same alignment requirements as the size of the |
---|
[1f4f1e2e] | 133 | * BAR. However, when bridges are involved the alignment need may be smaller |
---|
| 134 | * than the size, because a bridge resource consist or multiple BARs. |
---|
[a31845f7] | 135 | * For example, say that a bridge with a 256Mb and a 16Mb BAR is found, then |
---|
| 136 | * the alignment is required to be 256Mb but the size 256+16Mb. |
---|
| 137 | * |
---|
[1f4f1e2e] | 138 | * In order to minimize dead space on the bus, the boundary ordered list |
---|
[a31845f7] | 139 | * is reordered, example: |
---|
| 140 | * BUS0 |
---|
| 141 | * | BUS1 |
---|
| 142 | * |------------| |
---|
| 143 | * | |-- BAR0: SIZE=256Mb, ALIGNMENT=256MB |
---|
| 144 | * | |-- BAR1: SIZE=16Mb, ALIGNMENT=16MB |
---|
| 145 | * | | |
---|
| 146 | * | | |
---|
| 147 | * | | |
---|
| 148 | * | | BUS2 (BAR_BRIDGE1: SIZE=256+16, ALIGNEMENT=256) |
---|
| 149 | * | |----------| |
---|
| 150 | * | | |-- BAR2: SIZE=256Mb, ALIGNMENT=256Mb |
---|
| 151 | * | | |-- BAR3: SIZE=16Mb, ALIGNMENT=16MB |
---|
| 152 | * |
---|
[1f4f1e2e] | 153 | * A alignment/boundary ordered list of BUS1 will look like: |
---|
[a31845f7] | 154 | * - BAR_BRIDGE1 |
---|
| 155 | * - BAR0 (ALIGMENT NEED 256Mb) |
---|
| 156 | * - BAR1 |
---|
| 157 | * |
---|
| 158 | * However, Between BAR_BRIDGE1 and BAR0 will be a unused hole of 256-16Mb. |
---|
| 159 | * We can put BAR1 before BAR0 to avoid the problem. |
---|
| 160 | */ |
---|
| 161 | static void pci_res_reorder(struct pci_res *root) |
---|
| 162 | { |
---|
| 163 | struct pci_res *curr, *last, *curr2, *last2; |
---|
| 164 | unsigned int start, start_next, hole_size, hole_boundary; |
---|
| 165 | |
---|
| 166 | if (root == NULL) |
---|
| 167 | return; |
---|
| 168 | |
---|
| 169 | /* Make up a start address with the boundary of the |
---|
| 170 | * First element. |
---|
| 171 | */ |
---|
| 172 | start = root->boundary + root->size; |
---|
| 173 | last = root; |
---|
| 174 | curr = root->next; |
---|
| 175 | while (curr) { |
---|
| 176 | |
---|
| 177 | /* Find start address of resource */ |
---|
| 178 | start_next = (start + (curr->boundary - 1)) & |
---|
| 179 | ~(curr->boundary - 1); |
---|
| 180 | |
---|
[1f4f1e2e] | 181 | /* Find hole size, the unsed space in between last resource |
---|
| 182 | * and next */ |
---|
[a31845f7] | 183 | hole_size = start_next - start; |
---|
| 184 | |
---|
| 185 | /* Find Boundary of START */ |
---|
| 186 | hole_boundary = 1; |
---|
| 187 | while ((start & hole_boundary) == 0) |
---|
| 188 | hole_boundary = hole_boundary<<1; |
---|
| 189 | |
---|
| 190 | /* Detect dead hole */ |
---|
| 191 | if (hole_size > 0) { |
---|
| 192 | /* Step through list and try to find a resource that |
---|
| 193 | * can fit into hole. Take into account hole start |
---|
| 194 | * boundary and hole size. |
---|
| 195 | */ |
---|
| 196 | last2 = curr; |
---|
| 197 | curr2 = curr->next; |
---|
| 198 | while (curr2) { |
---|
| 199 | if ((curr2->boundary <= hole_boundary) && |
---|
| 200 | (curr2->size <= hole_size)) { |
---|
| 201 | /* Found matching resource. Move it |
---|
| 202 | * first in the hole. Then rescan, now |
---|
| 203 | * that the hole has changed in |
---|
| 204 | * size/boundary. |
---|
| 205 | */ |
---|
| 206 | last2->next = curr2->next; |
---|
| 207 | curr2->next = curr; |
---|
| 208 | last->next = curr2; |
---|
| 209 | |
---|
| 210 | /* New Start address */ |
---|
| 211 | start_next = (start + |
---|
| 212 | (curr2->boundary - 1)) & |
---|
| 213 | ~(curr2->boundary - 1); |
---|
| 214 | /* Since we inserted the resource before |
---|
| 215 | * curr we need to re-evaluate curr one |
---|
| 216 | * more, more resources may fit into the |
---|
| 217 | * shrunken hole. |
---|
| 218 | */ |
---|
| 219 | curr = curr2; |
---|
| 220 | break; |
---|
| 221 | } |
---|
| 222 | last2 = curr2; |
---|
| 223 | curr2 = curr2->next; |
---|
| 224 | } |
---|
| 225 | } |
---|
| 226 | |
---|
[1f4f1e2e] | 227 | /* No hole or nothing fit into hole. */ |
---|
[a31845f7] | 228 | start = start_next; |
---|
| 229 | |
---|
| 230 | last = curr; |
---|
| 231 | curr = curr->next; |
---|
| 232 | } |
---|
| 233 | } |
---|
| 234 | |
---|
| 235 | /* Find the total size required in PCI address space needed by a resource list*/ |
---|
| 236 | static unsigned int pci_res_size(struct pci_res *root) |
---|
| 237 | { |
---|
| 238 | struct pci_res *curr; |
---|
| 239 | unsigned int size; |
---|
| 240 | |
---|
| 241 | /* Get total size of all resources */ |
---|
| 242 | size = 0; |
---|
| 243 | curr = root; |
---|
| 244 | while (curr) { |
---|
| 245 | size = (size + (curr->boundary - 1)) & ~(curr->boundary - 1); |
---|
| 246 | size += curr->size; |
---|
| 247 | curr = curr->next; |
---|
| 248 | } |
---|
| 249 | |
---|
| 250 | return size; |
---|
| 251 | } |
---|
| 252 | |
---|
| 253 | #if 0 /* not used for now */ |
---|
| 254 | /* Free a device and secondary bus if device is a bridge */ |
---|
| 255 | static void pci_dev_free(struct pci_dev *dev) |
---|
| 256 | { |
---|
| 257 | struct pci_dev *subdev; |
---|
| 258 | struct pci_bus *bus; |
---|
| 259 | |
---|
| 260 | if (dev->flags & PCI_DEV_BRIDGE) { |
---|
| 261 | bus = (struct pci_bus *)dev; |
---|
| 262 | for (subdev = bus->devs; subdev ; subdev = subdev->next) |
---|
| 263 | pci_dev_free(dev); |
---|
| 264 | } |
---|
| 265 | |
---|
| 266 | free(dev); |
---|
| 267 | } |
---|
| 268 | #endif |
---|
| 269 | |
---|
[f9fbb333] | 270 | static struct pci_dev *pci_dev_create(int isbus) |
---|
| 271 | { |
---|
| 272 | void *ptr; |
---|
| 273 | int size; |
---|
| 274 | |
---|
| 275 | if (isbus) |
---|
| 276 | size = sizeof(struct pci_bus); |
---|
| 277 | else |
---|
| 278 | size = sizeof(struct pci_dev); |
---|
| 279 | |
---|
| 280 | ptr = calloc(1, size); |
---|
| 281 | if (!ptr) |
---|
| 282 | rtems_fatal_error_occurred(RTEMS_NO_MEMORY); |
---|
| 283 | return ptr; |
---|
| 284 | } |
---|
| 285 | |
---|
[a31845f7] | 286 | static void pci_find_devs(struct pci_bus *bus) |
---|
| 287 | { |
---|
| 288 | uint32_t id, tmp; |
---|
| 289 | uint8_t header; |
---|
| 290 | int slot, func, fail; |
---|
| 291 | struct pci_dev *dev, **listptr; |
---|
| 292 | struct pci_bus *bridge; |
---|
| 293 | pci_dev_t pcidev; |
---|
| 294 | |
---|
| 295 | DBG("Scanning bus %d\n", bus->num); |
---|
| 296 | |
---|
| 297 | listptr = &bus->devs; |
---|
[c1c37a1] | 298 | for (slot = 0; slot <= PCI_SLOTMAX; slot++) { |
---|
[a31845f7] | 299 | |
---|
| 300 | /* Slot address */ |
---|
| 301 | pcidev = PCI_DEV(bus->num, slot, 0); |
---|
| 302 | |
---|
[c1c37a1] | 303 | for (func = 0; func <= PCI_FUNCMAX; func++, pcidev++) { |
---|
[a31845f7] | 304 | |
---|
[c1c37a1] | 305 | fail = PCI_CFG_R32(pcidev, PCIR_VENDOR, &id); |
---|
[a31845f7] | 306 | if (fail || id == 0xffffffff || id == 0) { |
---|
| 307 | /* |
---|
| 308 | * This slot is empty |
---|
| 309 | */ |
---|
| 310 | if (func == 0) |
---|
| 311 | break; |
---|
| 312 | else |
---|
| 313 | continue; |
---|
| 314 | } |
---|
| 315 | |
---|
| 316 | DBG("Found PCIDEV 0x%x at (bus %x, slot %x, func %x)\n", |
---|
| 317 | id, bus, slot, func); |
---|
| 318 | |
---|
| 319 | /* Set command to reset values, it disables bus |
---|
| 320 | * mastering and address responses. |
---|
| 321 | */ |
---|
[c1c37a1] | 322 | PCI_CFG_W16(pcidev, PCIR_COMMAND, 0); |
---|
[a31845f7] | 323 | |
---|
| 324 | /* Clear any already set status bits */ |
---|
[c1c37a1] | 325 | PCI_CFG_W16(pcidev, PCIR_STATUS, 0xf900); |
---|
[a31845f7] | 326 | |
---|
| 327 | /* Set latency timer to 64 */ |
---|
[c1c37a1] | 328 | PCI_CFG_W8(pcidev, PCIR_LATTIMER, 64); |
---|
[a31845f7] | 329 | |
---|
[c1c37a1] | 330 | PCI_CFG_R32(pcidev, PCIR_REVID, &tmp); |
---|
[a31845f7] | 331 | tmp >>= 16; |
---|
[c1c37a1] | 332 | dev = pci_dev_create(tmp == PCID_PCI2PCI_BRIDGE); |
---|
[a31845f7] | 333 | *listptr = dev; |
---|
| 334 | listptr = &dev->next; |
---|
| 335 | |
---|
| 336 | dev->busdevfun = pcidev; |
---|
| 337 | dev->bus = bus; |
---|
[c1c37a1] | 338 | PCI_CFG_R16(pcidev, PCIR_VENDOR, &dev->vendor); |
---|
| 339 | PCI_CFG_R16(pcidev, PCIR_DEVICE, &dev->device); |
---|
| 340 | PCI_CFG_R32(pcidev, PCIR_REVID, &dev->classrev); |
---|
[a31845f7] | 341 | |
---|
[c1c37a1] | 342 | if (tmp == PCID_PCI2PCI_BRIDGE) { |
---|
[a31845f7] | 343 | DBG("Found PCI-PCI Bridge 0x%x at " |
---|
| 344 | "(bus %x, slot %x, func %x)\n", |
---|
| 345 | id, bus, slot, func); |
---|
| 346 | dev->flags = PCI_DEV_BRIDGE; |
---|
| 347 | dev->subvendor = 0; |
---|
| 348 | dev->subdevice = 0; |
---|
| 349 | bridge = (struct pci_bus *)dev; |
---|
| 350 | bridge->num = bus->sord + 1; |
---|
| 351 | bridge->pri = bus->num; |
---|
| 352 | bridge->sord = bus->sord + 1; |
---|
| 353 | |
---|
| 354 | /* Configure bridge (no support for 64-bit) */ |
---|
| 355 | PCI_CFG_W32(pcidev, 0x28, 0); |
---|
| 356 | PCI_CFG_W32(pcidev, 0x2C, 0); |
---|
| 357 | tmp = (64 << 24) | (0xff << 16) | |
---|
| 358 | (bridge->num << 8) | bridge->pri; |
---|
[c1c37a1] | 359 | PCI_CFG_W32(pcidev, PCIR_PRIBUS_1, tmp); |
---|
[a31845f7] | 360 | |
---|
| 361 | /* Scan Secondary Bus */ |
---|
| 362 | pci_find_devs(bridge); |
---|
| 363 | |
---|
| 364 | /* sord might have been updated */ |
---|
| 365 | PCI_CFG_W8(pcidev, 0x1a, bridge->sord); |
---|
| 366 | bus->sord = bridge->sord; |
---|
| 367 | |
---|
| 368 | DBG("PCI-PCI BRIDGE: Primary %x, Secondary %x, " |
---|
| 369 | "Subordinate %x\n", |
---|
| 370 | bridge->pri, bridge->num, bridge->sord); |
---|
| 371 | } else { |
---|
| 372 | /* Disable Cardbus CIS Pointer */ |
---|
[c1c37a1] | 373 | PCI_CFG_W32(pcidev, PCIR_CIS, 0); |
---|
[a31845f7] | 374 | |
---|
| 375 | /* Devices have subsytem device and vendor ID */ |
---|
[c1c37a1] | 376 | PCI_CFG_R16(pcidev, PCIR_SUBVEND_0, |
---|
[a31845f7] | 377 | &dev->subvendor); |
---|
[c1c37a1] | 378 | PCI_CFG_R16(pcidev, PCIR_SUBDEV_0, |
---|
[a31845f7] | 379 | &dev->subdevice); |
---|
| 380 | } |
---|
| 381 | |
---|
| 382 | /* Stop if not a multi-function device */ |
---|
| 383 | if (func == 0) { |
---|
[c1c37a1] | 384 | pci_cfg_r8(pcidev, PCIR_HDRTYPE, &header); |
---|
| 385 | if ((header & PCIM_MFDEV) == 0) |
---|
[a31845f7] | 386 | break; |
---|
| 387 | } |
---|
| 388 | } |
---|
| 389 | } |
---|
| 390 | } |
---|
| 391 | |
---|
| 392 | static void pci_find_bar(struct pci_dev *dev, int bar) |
---|
| 393 | { |
---|
| 394 | uint32_t size, disable, mask; |
---|
| 395 | struct pci_res *res = &dev->resources[bar]; |
---|
| 396 | pci_dev_t pcidev = dev->busdevfun; |
---|
| 397 | int ofs; |
---|
| 398 | #ifdef DEBUG |
---|
| 399 | char *str; |
---|
| 400 | #define DBG_SET_STR(str, val) str = (val) |
---|
| 401 | #else |
---|
| 402 | #define DBG_SET_STR(str, val) |
---|
| 403 | #endif |
---|
| 404 | |
---|
| 405 | DBG("Bus: %x, Slot: %x, function: %x, bar%d\n", |
---|
| 406 | PCI_DEV_EXPAND(pcidev), bar); |
---|
| 407 | |
---|
| 408 | res->bar = bar; |
---|
| 409 | if (bar == DEV_RES_ROM) { |
---|
| 410 | if (dev->flags & PCI_DEV_BRIDGE) |
---|
[c1c37a1] | 411 | ofs = PCIR_BIOS_1; |
---|
[a31845f7] | 412 | else |
---|
[c1c37a1] | 413 | ofs = PCIR_BIOS; |
---|
[a31845f7] | 414 | disable = 0; /* ROM BARs have a unique enable bit per BAR */ |
---|
| 415 | } else { |
---|
[c1c37a1] | 416 | ofs = PCIR_BAR(0) + (bar << 2); |
---|
[a31845f7] | 417 | disable = pci_invalid_address; |
---|
| 418 | } |
---|
| 419 | |
---|
| 420 | PCI_CFG_W32(pcidev, ofs, 0xffffffff); |
---|
| 421 | PCI_CFG_R32(pcidev, ofs, &size); |
---|
| 422 | PCI_CFG_W32(pcidev, ofs, disable); |
---|
| 423 | |
---|
| 424 | if (size == 0 || size == 0xffffffff) |
---|
| 425 | return; |
---|
| 426 | if (bar == DEV_RES_ROM) { |
---|
[c1c37a1] | 427 | mask = PCIM_BIOS_ADDR_MASK; |
---|
[a31845f7] | 428 | DBG_SET_STR(str, "ROM"); |
---|
| 429 | if (dev->bus->flags & PCI_BUS_MEM) |
---|
| 430 | res->flags = PCI_RES_MEM; |
---|
| 431 | else |
---|
| 432 | res->flags = PCI_RES_MEMIO; |
---|
| 433 | } else if (((size & 0x1) == 0) && (size & 0x6)) { |
---|
| 434 | /* unsupported Memory type */ |
---|
| 435 | PCI_CFG_W32(pcidev, ofs, 0); |
---|
| 436 | return; |
---|
| 437 | } else { |
---|
| 438 | mask = ~0xf; |
---|
| 439 | if (size & 0x1) { |
---|
| 440 | /* I/O */ |
---|
| 441 | mask = ~0x3; |
---|
| 442 | res->flags = PCI_RES_IO; |
---|
| 443 | DBG_SET_STR(str, "I/O"); |
---|
| 444 | if (size & 0xffff0000) |
---|
| 445 | res->flags |= PCI_RES_IO32; |
---|
| 446 | /* Limit size of I/O space to 256 byte */ |
---|
| 447 | size |= 0xffffff00; |
---|
| 448 | if ((dev->bus->flags & PCI_BUS_IO) == 0) { |
---|
| 449 | res->flags |= PCI_RES_FAIL; |
---|
| 450 | dev->flags |= PCI_DEV_RES_FAIL; |
---|
| 451 | } |
---|
| 452 | } else { |
---|
| 453 | /* Memory. We convert Prefetchable Memory BARs to Memory |
---|
| 454 | * BARs in case the Bridge does not support prefetchable |
---|
| 455 | * memory. |
---|
| 456 | */ |
---|
| 457 | if ((size & 0x8) && (dev->bus->flags & PCI_BUS_MEM)) { |
---|
| 458 | /* Prefetchable and Bus supports it */ |
---|
| 459 | res->flags = PCI_RES_MEM; |
---|
| 460 | DBG_SET_STR(str, "MEM"); |
---|
| 461 | } else { |
---|
| 462 | res->flags = PCI_RES_MEMIO; |
---|
| 463 | DBG_SET_STR(str, "MEMIO"); |
---|
| 464 | } |
---|
| 465 | } |
---|
| 466 | } |
---|
| 467 | size &= mask; |
---|
| 468 | res->size = ~size + 1; |
---|
| 469 | res->boundary = ~size + 1; |
---|
| 470 | |
---|
| 471 | DBG("Bus: %x, Slot: %x, function: %x, %s bar%d size: %x\n", |
---|
| 472 | PCI_DEV_EXPAND(pcidev), str, bar, res->size); |
---|
| 473 | } |
---|
| 474 | |
---|
| 475 | static int pci_find_res_dev(struct pci_dev *dev, void *unused) |
---|
| 476 | { |
---|
| 477 | struct pci_bus *bridge; |
---|
| 478 | uint32_t tmp; |
---|
| 479 | uint16_t tmp16; |
---|
| 480 | pci_dev_t pcidev = dev->busdevfun; |
---|
| 481 | int i, maxbars; |
---|
| 482 | |
---|
| 483 | if (dev->flags & PCI_DEV_BRIDGE) { |
---|
| 484 | /* PCI-PCI Bridge */ |
---|
| 485 | bridge = (struct pci_bus *)dev; |
---|
| 486 | |
---|
| 487 | /* Only 2 Bridge BARs */ |
---|
| 488 | maxbars = 2; |
---|
| 489 | |
---|
| 490 | /* Probe Bridge Spaces (MEMIO space always implemented), the |
---|
| 491 | * probe disables all space-decoding at the same time |
---|
| 492 | */ |
---|
| 493 | PCI_CFG_W32(pcidev, 0x30, 0); |
---|
| 494 | PCI_CFG_W16(pcidev, 0x1c, 0x00f0); |
---|
| 495 | PCI_CFG_R16(pcidev, 0x1c, &tmp16); |
---|
| 496 | if (tmp16 != 0) { |
---|
| 497 | bridge->flags |= PCI_BUS_IO; |
---|
| 498 | if (tmp16 & 0x1) |
---|
| 499 | bridge->flags |= PCI_BUS_IO32; |
---|
| 500 | } |
---|
| 501 | |
---|
| 502 | PCI_CFG_W32(pcidev, 0x24, 0x0000ffff); |
---|
| 503 | PCI_CFG_R32(pcidev, 0x24, &tmp); |
---|
| 504 | if (tmp != 0) |
---|
| 505 | bridge->flags |= PCI_BUS_MEM; |
---|
| 506 | |
---|
| 507 | PCI_CFG_W32(pcidev, 0x20, 0x0000ffff); |
---|
| 508 | bridge->flags |= PCI_BUS_MEMIO; |
---|
| 509 | } else { |
---|
| 510 | /* Normal PCI Device as max 6 BARs */ |
---|
| 511 | maxbars = 6; |
---|
| 512 | } |
---|
| 513 | |
---|
| 514 | /* Probe BARs */ |
---|
| 515 | for (i = 0; i < maxbars; i++) |
---|
| 516 | pci_find_bar(dev, i); |
---|
| 517 | pci_find_bar(dev, DEV_RES_ROM); |
---|
| 518 | |
---|
| 519 | return 0; |
---|
| 520 | } |
---|
| 521 | |
---|
| 522 | static int pci_add_res_dev(struct pci_dev *dev, void *arg); |
---|
| 523 | |
---|
| 524 | static void pci_add_res_bus(struct pci_bus *bus, int type) |
---|
| 525 | { |
---|
| 526 | int tindex = type - 1; |
---|
| 527 | |
---|
| 528 | /* Clear old resources */ |
---|
| 529 | bus->busres[tindex] = NULL; |
---|
| 530 | |
---|
| 531 | /* Add resources of devices behind bridge if bridge supports |
---|
| 532 | * resource type. If MEM space not supported by bridge, they are |
---|
| 533 | * converted to MEMIO in the process. |
---|
| 534 | */ |
---|
| 535 | if (!((type == PCI_BUS_IO) && ((bus->flags & PCI_BUS_IO) == 0))) { |
---|
| 536 | pci_for_each_child(bus, pci_add_res_dev, (void *)type, 0); |
---|
| 537 | |
---|
| 538 | /* Reorder Bus resources to fit more optimally (avoid dead |
---|
| 539 | * PCI space). Currently they are sorted by boundary and size. |
---|
| 540 | * |
---|
| 541 | * This is especially important when multiple buses (bridges) |
---|
| 542 | * are present. |
---|
| 543 | */ |
---|
| 544 | pci_res_reorder(bus->busres[tindex]); |
---|
| 545 | } |
---|
| 546 | } |
---|
| 547 | |
---|
| 548 | static int pci_add_res_dev(struct pci_dev *dev, void *arg) |
---|
| 549 | { |
---|
| 550 | int tindex, type = (int)arg; |
---|
| 551 | struct pci_bus *bridge; |
---|
| 552 | struct pci_res *res, *first_busres; |
---|
| 553 | int i; |
---|
| 554 | uint32_t bbound; |
---|
| 555 | |
---|
| 556 | /* Type index in Bus resource */ |
---|
| 557 | tindex = type - 1; |
---|
| 558 | |
---|
| 559 | if (dev->flags & PCI_DEV_BRIDGE) { |
---|
| 560 | /* PCI-PCI Bridge. Add all sub-bus resources first */ |
---|
| 561 | bridge = (struct pci_bus *)dev; |
---|
| 562 | |
---|
| 563 | /* Add all child device's resources to this type */ |
---|
| 564 | pci_add_res_bus(bridge, type); |
---|
| 565 | |
---|
| 566 | /* Propagate the resources from child bus to BAR on |
---|
| 567 | * this bus, by adding a "fake" BAR per type. |
---|
| 568 | */ |
---|
| 569 | res = &bridge->dev.resources[BUS_RES_START + tindex]; |
---|
| 570 | res->bar = BUS_RES_START + tindex; |
---|
| 571 | res->start = 0; |
---|
| 572 | res->end = 0; |
---|
| 573 | res->flags = 0; /* mark BAR resource not available */ |
---|
| 574 | first_busres = bridge->busres[tindex]; |
---|
| 575 | if (first_busres) { |
---|
| 576 | res->flags = type; |
---|
| 577 | res->size = pci_res_size(first_busres); |
---|
| 578 | res->boundary = first_busres->boundary; |
---|
| 579 | if (type == PCI_RES_IO) { |
---|
| 580 | bbound = 0x1000; /* Bridge I/O min 4KB */ |
---|
| 581 | } else { |
---|
| 582 | bbound = 0x100000; /* Bridge MEM min 1MB */ |
---|
| 583 | |
---|
| 584 | /* Convert MEM to MEMIO if not supported by |
---|
| 585 | * this bridge |
---|
| 586 | */ |
---|
| 587 | if ((bridge->flags & PCI_BUS_MEM) == 0) |
---|
| 588 | res->flags = PCI_RES_MEMIO; |
---|
| 589 | } |
---|
| 590 | /* Fulfil minimum bridge boundary */ |
---|
| 591 | if (res->boundary < bbound) |
---|
| 592 | res->boundary = bbound; |
---|
| 593 | /* Make sure that size is atleast bridge boundary */ |
---|
| 594 | if (res->size > bbound && (res->size & (bbound-1))) |
---|
| 595 | res->size = (res->size | (bbound-1)) + 1; |
---|
| 596 | } |
---|
| 597 | } |
---|
| 598 | |
---|
| 599 | /* Normal PCI Device as max 6 BARs and a ROM Bar. |
---|
| 600 | * Insert BARs into the sorted resource list. |
---|
| 601 | */ |
---|
| 602 | for (i = 0; i < DEV_RES_CNT; i++) { |
---|
| 603 | res = &dev->resources[i]; |
---|
| 604 | if ((res->flags & PCI_RES_TYPE_MASK) != type) |
---|
| 605 | continue; |
---|
| 606 | pci_res_insert(&dev->bus->busres[tindex], res); |
---|
| 607 | } |
---|
| 608 | |
---|
| 609 | return 0; |
---|
| 610 | } |
---|
| 611 | |
---|
| 612 | /* Function assumes that base is properly aligned to the requirement of the |
---|
| 613 | * largest BAR in the system. |
---|
| 614 | */ |
---|
| 615 | static uint32_t pci_alloc_res(struct pci_bus *bus, int type, |
---|
| 616 | uint32_t start, uint32_t end) |
---|
| 617 | { |
---|
| 618 | struct pci_dev *dev; |
---|
| 619 | struct pci_res *res, **prev_next; |
---|
| 620 | unsigned long starttmp; |
---|
| 621 | struct pci_bus *bridge; |
---|
| 622 | int removed, sec_type; |
---|
| 623 | |
---|
| 624 | /* The resources are sorted on their size (size and alignment is the |
---|
| 625 | * same) |
---|
| 626 | */ |
---|
| 627 | prev_next = &bus->busres[type - 1]; |
---|
| 628 | while ((res = *prev_next) != NULL) { |
---|
| 629 | |
---|
| 630 | dev = RES2DEV(res); |
---|
| 631 | removed = 0; |
---|
| 632 | |
---|
| 633 | /* Align start to this reource's need, only needed after |
---|
| 634 | * a bridge resource has been allocated. |
---|
| 635 | */ |
---|
| 636 | starttmp = (start + (res->boundary-1)) & ~(res->boundary-1); |
---|
| 637 | |
---|
| 638 | if ((starttmp + res->size - 1) > end) { |
---|
| 639 | /* Not enough memory available for this resource */ |
---|
| 640 | printk("PCI[%x:%x:%x]: DEV BAR%d (%d): no resource " |
---|
| 641 | "assigned\n", |
---|
| 642 | PCI_DEV_EXPAND(dev->busdevfun), |
---|
| 643 | res->bar, res->flags & PCI_RES_TYPE_MASK); |
---|
| 644 | res->start = res->end = 0; |
---|
| 645 | |
---|
| 646 | /* If this resources is a bridge window to the |
---|
| 647 | * secondary bus, the secondary resources are not |
---|
| 648 | * changed which has the following effect: |
---|
| 649 | * I/O : Will never be assigned |
---|
| 650 | * MEMIO : Will never be assigned |
---|
| 651 | * MEM : Will stay marked as MEM, but bridge window |
---|
| 652 | * is changed into MEMIO, when the window is |
---|
| 653 | * assigned a MEMIO address the secondary |
---|
| 654 | * resources will also be assigned. |
---|
| 655 | */ |
---|
| 656 | |
---|
| 657 | if (type == PCI_RES_MEM) { |
---|
| 658 | /* Try prefetchable as non-prefetchable mem */ |
---|
| 659 | res->flags &= ~PCI_RES_MEM_PREFETCH; |
---|
| 660 | /* Remove resource from MEM list, ideally we |
---|
| 661 | * should regenerate this list in order to fit |
---|
| 662 | * the comming BARs more optimially... |
---|
| 663 | */ |
---|
| 664 | *prev_next = res->next; |
---|
| 665 | /* We should not update prev_next here since |
---|
| 666 | * we just removed the resource from the list |
---|
| 667 | */ |
---|
| 668 | removed = 1; |
---|
| 669 | } else { |
---|
| 670 | res->flags |= PCI_RES_FAIL; |
---|
| 671 | dev->flags |= PCI_DEV_RES_FAIL; |
---|
| 672 | } |
---|
| 673 | } else { |
---|
| 674 | start = starttmp; |
---|
| 675 | |
---|
| 676 | res->start = start; |
---|
| 677 | res->end = start + res->size; |
---|
| 678 | |
---|
| 679 | /* "Virtual BAR" on a bridge? A bridge resource need all |
---|
| 680 | * its child devices resources allocated |
---|
| 681 | */ |
---|
| 682 | if ((res->bar != DEV_RES_ROM) && |
---|
| 683 | (dev->flags & PCI_DEV_BRIDGE) && |
---|
| 684 | (res->bar >= BUS_RES_START)) { |
---|
| 685 | bridge = (struct pci_bus *)dev; |
---|
| 686 | /* If MEM bar was changed into a MEMIO the |
---|
| 687 | * secondary MEM resources are still set to MEM, |
---|
| 688 | */ |
---|
| 689 | if (type == PCI_BUS_MEMIO && |
---|
| 690 | res->bar == BRIDGE_RES_MEM) |
---|
| 691 | sec_type = PCI_RES_MEM; |
---|
| 692 | else |
---|
| 693 | sec_type = type; |
---|
| 694 | |
---|
| 695 | pci_alloc_res(bridge, sec_type, res->start, |
---|
| 696 | res->end); |
---|
| 697 | } |
---|
| 698 | |
---|
| 699 | start += res->size; |
---|
| 700 | } |
---|
| 701 | if (removed == 0) |
---|
| 702 | prev_next = &res->next; |
---|
| 703 | } |
---|
| 704 | |
---|
| 705 | return start; |
---|
| 706 | } |
---|
| 707 | |
---|
| 708 | static void pci_set_bar(struct pci_dev *dev, int residx) |
---|
| 709 | { |
---|
| 710 | uint32_t tmp; |
---|
| 711 | uint16_t tmp16; |
---|
| 712 | pci_dev_t pcidev; |
---|
| 713 | struct pci_res *res; |
---|
| 714 | int is_bridge, ofs; |
---|
| 715 | |
---|
| 716 | res = &dev->resources[residx]; |
---|
| 717 | pcidev = dev->busdevfun; |
---|
| 718 | |
---|
| 719 | if ((res->flags == 0) || (res->flags & PCI_RES_FAIL)) |
---|
| 720 | return; |
---|
| 721 | |
---|
| 722 | is_bridge = dev->flags & PCI_DEV_BRIDGE; |
---|
| 723 | |
---|
| 724 | if (res->bar == DEV_RES_ROM) { |
---|
| 725 | /* ROM: 32-bit prefetchable memory BAR */ |
---|
| 726 | if (is_bridge) |
---|
[c1c37a1] | 727 | ofs = PCIR_BIOS_1; |
---|
[a31845f7] | 728 | else |
---|
[c1c37a1] | 729 | ofs = PCIR_BIOS; |
---|
| 730 | PCI_CFG_W32(pcidev, ofs, res->start | PCIM_BIOS_ENABLE); |
---|
[a31845f7] | 731 | DBG("PCI[%x:%x:%x]: ROM BAR: 0x%x-0x%x\n", |
---|
| 732 | PCI_DEV_EXPAND(pcidev), res->start, res->end); |
---|
| 733 | } else if (is_bridge && (res->bar == BRIDGE_RES_IO)) { |
---|
| 734 | /* PCI Bridge I/O BAR */ |
---|
| 735 | DBG("PCI[%x:%x:%x]: BAR 1C: 0x%x-0x%x\n", |
---|
| 736 | PCI_DEV_EXPAND(pcidev), res->start, res->end); |
---|
| 737 | |
---|
| 738 | /* Limit and Base */ |
---|
| 739 | tmp16 = ((res->end-1) & 0x0000f000) | |
---|
| 740 | ((res->start & 0x0000f000) >> 8); |
---|
| 741 | tmp = ((res->end-1) & 0xffff0000) | (res->start >> 16); |
---|
| 742 | |
---|
| 743 | DBG("PCI[%x:%x:%x]: BRIDGE BAR 0x%x: 0x%08x [0x30: 0x%x]\n", |
---|
| 744 | PCI_DEV_EXPAND(pcidev), 0x1C, tmp, tmp2); |
---|
| 745 | PCI_CFG_W16(pcidev, 0x1C, tmp16); |
---|
| 746 | PCI_CFG_W32(pcidev, 0x30, tmp); |
---|
| 747 | } else if (is_bridge && (res->bar >= BRIDGE_RES_MEMIO)) { |
---|
| 748 | /* PCI Bridge MEM and MEMIO Space */ |
---|
| 749 | |
---|
| 750 | /* Limit and Base */ |
---|
| 751 | tmp = ((res->end-1) & 0xfff00000) | (res->start >> 16); |
---|
| 752 | |
---|
| 753 | DBG("PCI[%x:%x:%x]: BRIDGE BAR 0x%x: 0x%08x\n", |
---|
| 754 | PCI_DEV_EXPAND(pcidev), |
---|
| 755 | 0x20 + (res->bar-BRIDGE_RES_MEMIO)*4, tmp); |
---|
| 756 | PCI_CFG_W32(pcidev, 0x20+(res->bar-BRIDGE_RES_MEMIO)*4, tmp); |
---|
| 757 | } else { |
---|
| 758 | /* PCI Device */ |
---|
| 759 | DBG("PCI[%x:%x:%x]: DEV BAR%d: 0x%08x\n", |
---|
| 760 | PCI_DEV_EXPAND(pcidev), res->bar, res->start); |
---|
[c1c37a1] | 761 | ofs = PCIR_BAR(0) + res->bar*4; |
---|
[a31845f7] | 762 | PCI_CFG_W32(pcidev, ofs, res->start); |
---|
| 763 | } |
---|
| 764 | |
---|
| 765 | /* Enable Memory or I/O responses */ |
---|
| 766 | if ((res->flags & PCI_RES_TYPE_MASK) == PCI_RES_IO) |
---|
| 767 | pci_io_enable(pcidev); |
---|
| 768 | else |
---|
| 769 | pci_mem_enable(pcidev); |
---|
| 770 | |
---|
| 771 | /* Enable Master if bridge */ |
---|
| 772 | if (is_bridge) |
---|
| 773 | pci_master_enable(pcidev); |
---|
| 774 | } |
---|
| 775 | |
---|
| 776 | static int pci_set_res_dev(struct pci_dev *dev, void *unused) |
---|
| 777 | { |
---|
| 778 | int i, maxbars; |
---|
| 779 | |
---|
| 780 | if (dev->flags & PCI_DEV_BRIDGE) |
---|
| 781 | maxbars = 2 + 3; /* 2 BARs + 3 Bridge-Windows "Virtual BARs" */ |
---|
| 782 | else |
---|
| 783 | maxbars = 6; /* Normal PCI Device as max 6 BARs. */ |
---|
| 784 | |
---|
| 785 | /* Set BAR resources with previous allocated values */ |
---|
| 786 | for (i = 0; i < maxbars; i++) |
---|
| 787 | pci_set_bar(dev, i); |
---|
| 788 | pci_set_bar(dev, DEV_RES_ROM); |
---|
| 789 | |
---|
| 790 | return 0; |
---|
| 791 | } |
---|
| 792 | |
---|
| 793 | /* Route IRQ through PCI-PCI Bridges */ |
---|
| 794 | static int pci_route_irq(pci_dev_t dev, int irq_pin) |
---|
| 795 | { |
---|
| 796 | int slot_grp; |
---|
| 797 | |
---|
| 798 | if (PCI_DEV_BUS(dev) == 0) |
---|
| 799 | return irq_pin; |
---|
| 800 | |
---|
| 801 | slot_grp = PCI_DEV_SLOT(dev) & 0x3; |
---|
| 802 | |
---|
| 803 | return (((irq_pin - 1) + slot_grp) & 0x3) + 1; |
---|
| 804 | } |
---|
| 805 | |
---|
| 806 | /* Put assigned system IRQ into PCI interrupt line information field. |
---|
| 807 | * This is to make it possible for drivers to read system IRQ / Vector from |
---|
| 808 | * configuration space later on. |
---|
| 809 | * |
---|
| 810 | * 1. Get Interrupt PIN |
---|
| 811 | * 2. Route PIN to host bridge |
---|
| 812 | * 3. Get System interrupt number assignment for PIN |
---|
| 813 | * 4. Set Interrupt LINE |
---|
| 814 | */ |
---|
| 815 | static int pci_set_irq_dev(struct pci_dev *dev, void *cfg) |
---|
| 816 | { |
---|
| 817 | struct pci_auto_setup *autocfg = cfg; |
---|
| 818 | uint8_t irq_pin, irq_line, *psysirq; |
---|
| 819 | pci_dev_t pcidev; |
---|
| 820 | |
---|
| 821 | psysirq = &dev->sysirq; |
---|
| 822 | pcidev = dev->busdevfun; |
---|
[c1c37a1] | 823 | PCI_CFG_R8(pcidev, PCIR_INTPIN, &irq_pin); |
---|
[a31845f7] | 824 | |
---|
| 825 | /* perform IRQ routing until we reach host bridge */ |
---|
| 826 | while (dev->bus && irq_pin != 0) { |
---|
| 827 | irq_pin = autocfg->irq_route(dev->busdevfun, irq_pin); |
---|
| 828 | dev = &dev->bus->dev; |
---|
| 829 | } |
---|
| 830 | |
---|
| 831 | /* Get IRQ from PIN on PCI bus0 */ |
---|
| 832 | if (irq_pin != 0 && autocfg->irq_map) |
---|
| 833 | irq_line = autocfg->irq_map(dev->busdevfun, irq_pin); |
---|
| 834 | else |
---|
| 835 | irq_line = 0; |
---|
| 836 | |
---|
| 837 | *psysirq = irq_line; |
---|
| 838 | |
---|
| 839 | /* Set System Interrupt/Vector for device. 0 means no-IRQ */ |
---|
[c1c37a1] | 840 | PCI_CFG_W8(pcidev, PCIR_INTLINE, irq_line); |
---|
[a31845f7] | 841 | |
---|
| 842 | return 0; |
---|
| 843 | } |
---|
| 844 | |
---|
| 845 | /* This routine assumes that PCI access library has been successfully |
---|
| 846 | * initialized. All information about the PCI bus needed is found in |
---|
[1f4f1e2e] | 847 | * the pci_auto_cfg structure passed on by pci_config_register(). |
---|
[a31845f7] | 848 | * |
---|
| 849 | * The PCI buses are enumerated as bridges are found, PCI devices are |
---|
| 850 | * setup with BARs and IRQs, etc. |
---|
| 851 | */ |
---|
| 852 | int pci_config_auto(void) |
---|
| 853 | { |
---|
| 854 | uint32_t end; |
---|
| 855 | uint32_t startmemio, startmem, startio; |
---|
| 856 | struct pci_auto_setup *autocfg = &pci_auto_cfg; |
---|
| 857 | #ifdef DEBUG |
---|
| 858 | uint32_t endmemio, endmem, endio; |
---|
| 859 | uint32_t start; |
---|
| 860 | #endif |
---|
| 861 | |
---|
| 862 | if (pci_config_auto_initialized == 0) |
---|
| 863 | return -1; /* no config given to library */ |
---|
| 864 | |
---|
| 865 | #ifdef DEBUG |
---|
| 866 | DBG("\n--- PCI MEMORY AVAILABLE ---\n"); |
---|
| 867 | if (autocfg->mem_size) { |
---|
| 868 | start = autocfg->mem_start; |
---|
| 869 | end = autocfg->mem_start + autocfg->mem_size - 1; |
---|
| 870 | DBG(" MEM AVAIL [0x%08x-0x%08x]\n", start, end); |
---|
| 871 | } else { |
---|
| 872 | /* One big memory space */ |
---|
| 873 | DBG(" MEM share the space with MEMIO\n"); |
---|
| 874 | } |
---|
| 875 | /* no-prefetchable memory space need separate memory space. |
---|
| 876 | * For example PCI controller maps this region non-cachable. |
---|
| 877 | */ |
---|
| 878 | start = autocfg->memio_start; |
---|
| 879 | end = autocfg->memio_start + autocfg->memio_size - 1; |
---|
| 880 | DBG(" MEMIO AVAIL [0x%08x-0x%08x]\n", start, end); |
---|
| 881 | if (autocfg->io_size) { |
---|
| 882 | start = autocfg->io_start; |
---|
| 883 | end = autocfg->io_start + autocfg->io_size - 1; |
---|
| 884 | DBG(" I/O AVAIL [0x%08x-0x%08x]\n", start, end); |
---|
| 885 | } else { |
---|
| 886 | DBG(" I/O Space not available\n"); |
---|
| 887 | } |
---|
| 888 | #endif |
---|
| 889 | |
---|
| 890 | /* Init Host-Bridge */ |
---|
| 891 | memset(&pci_hb, 0, sizeof(pci_hb)); |
---|
| 892 | pci_hb.dev.flags = PCI_DEV_BRIDGE; |
---|
| 893 | if (autocfg->memio_size <= 0) |
---|
| 894 | return -1; |
---|
| 895 | pci_hb.flags = PCI_BUS_MEMIO; |
---|
| 896 | if (autocfg->mem_size) |
---|
| 897 | pci_hb.flags |= PCI_BUS_MEM; |
---|
| 898 | if (autocfg->io_size) |
---|
| 899 | pci_hb.flags |= PCI_BUS_IO; |
---|
| 900 | |
---|
| 901 | /* Find all PCI devices/functions on all buses. The buses will be |
---|
| 902 | * enumrated (assigned a unique PCI Bus ID 0..255). |
---|
| 903 | */ |
---|
| 904 | DBG("\n--- PCI SCANNING ---\n"); |
---|
| 905 | pci_find_devs(&pci_hb); |
---|
| 906 | pci_bus_cnt = pci_hb.sord + 1; |
---|
| 907 | if (pci_hb.devs == NULL) |
---|
| 908 | return 0; |
---|
| 909 | |
---|
| 910 | pci_system_type = PCI_SYSTEM_HOST; |
---|
| 911 | |
---|
| 912 | /* Find all resources (MEM/MEMIO/IO BARs) of all devices/functions |
---|
| 913 | * on all buses. |
---|
| 914 | * |
---|
| 915 | * Device resources behind bridges which does not support prefetchable |
---|
| 916 | * memory are already marked as non-prefetchable memory. |
---|
| 917 | * Devices which as I/O resources behind a bridge that do not support |
---|
| 918 | * I/O space are marked DISABLED. |
---|
| 919 | * |
---|
| 920 | * All BARs and Bridge Spaces are disabled after this. Only the ones |
---|
| 921 | * that are allocated an address are initilized later on. |
---|
| 922 | */ |
---|
| 923 | DBG("\n\n--- PCI RESOURCES ---\n"); |
---|
| 924 | pci_for_each_dev(pci_find_res_dev, 0); |
---|
| 925 | |
---|
| 926 | /* Add all device's resources to bus and sort them to fit in the PCI |
---|
| 927 | * Window. The device resources are propagated upwards through bridges |
---|
| 928 | * by adding a "virtual" BAR (boundary != BAR size). |
---|
| 929 | * |
---|
| 930 | * We wait with MEMIO (non-prefetchable memory) resources to after MEM |
---|
| 931 | * resources have been allocated, so that MEM resources can be changed |
---|
| 932 | * into MEMIO resources if not enough space. |
---|
| 933 | */ |
---|
| 934 | pci_add_res_bus(&pci_hb, PCI_RES_IO); |
---|
| 935 | pci_add_res_bus(&pci_hb, PCI_RES_MEM); |
---|
| 936 | |
---|
| 937 | /* Start assigning found resource according to the sorted order. */ |
---|
| 938 | |
---|
| 939 | /* Allocate resources to I/O areas */ |
---|
| 940 | if (pci_hb.busres[BUS_RES_IO]) { |
---|
| 941 | startio = autocfg->io_start; |
---|
| 942 | end = startio + autocfg->io_size; |
---|
| 943 | #ifdef DEBUG |
---|
| 944 | endio = |
---|
| 945 | #endif |
---|
| 946 | pci_alloc_res(&pci_hb, PCI_RES_IO, startio, end); |
---|
| 947 | } |
---|
| 948 | |
---|
| 949 | /* Allocate resources to prefetchable memory */ |
---|
| 950 | if (pci_hb.busres[BUS_RES_MEM]) { |
---|
| 951 | startmem = autocfg->mem_start; |
---|
| 952 | end = startmem + autocfg->mem_size; |
---|
| 953 | #ifdef DEBUG |
---|
| 954 | endmem = |
---|
| 955 | #endif |
---|
| 956 | pci_alloc_res(&pci_hb, PCI_RES_MEM, startmem, end); |
---|
| 957 | } |
---|
| 958 | |
---|
| 959 | /* Add non-prefetchable memory resources and not fitting prefetchable |
---|
| 960 | * memory resources. |
---|
| 961 | * |
---|
| 962 | * Some prefetchable memory resources may not have fitted into PCI |
---|
| 963 | * window. Prefetchable memory can be mapped into non-prefetchable |
---|
| 964 | * memory window. The failing BARs have been marked as MEMIO instead. |
---|
| 965 | */ |
---|
| 966 | pci_add_res_bus(&pci_hb, PCI_RES_MEMIO); |
---|
| 967 | |
---|
| 968 | /* Allocate resources to non-prefetchable memory */ |
---|
| 969 | if (pci_hb.busres[BUS_RES_MEMIO]) { |
---|
| 970 | startmemio = autocfg->memio_start; |
---|
| 971 | end = startmemio + autocfg->memio_size; |
---|
| 972 | #ifdef DEBUG |
---|
| 973 | endmemio = |
---|
| 974 | #endif |
---|
| 975 | pci_alloc_res(&pci_hb, PCI_RES_MEMIO, startmemio, end); |
---|
| 976 | } |
---|
| 977 | |
---|
| 978 | DBG("\n--- PCI ALLOCATED SPACE RANGES ---\n"); |
---|
| 979 | DBG(" MEM NON-PREFETCHABLE: [0x%08x-0x%08x]\n", startmemio, endmemio); |
---|
| 980 | DBG(" MEM PREFETCHABLE: [0x%08x-0x%08x]\n", startmem, endmem); |
---|
| 981 | DBG(" I/O: [0x%08x-0x%08x]\n", startio, endio); |
---|
| 982 | |
---|
| 983 | /* Set all allocated BARs and Bridge Windows */ |
---|
| 984 | pci_for_each_dev(pci_set_res_dev, NULL); |
---|
| 985 | |
---|
| 986 | /* Initialize IRQs of all devices. According to the PCI-PCI bridge |
---|
| 987 | * specification the IRQs are routed differently depending on slot |
---|
| 988 | * number. Drivers can override the default routing if a motherboard |
---|
| 989 | * requires it. |
---|
| 990 | */ |
---|
| 991 | if ((autocfg->options & CFGOPT_NOSETUP_IRQ) == 0) { |
---|
| 992 | if (autocfg->irq_route == NULL) /* use standard irq routing */ |
---|
| 993 | autocfg->irq_route = pci_route_irq; |
---|
| 994 | pci_for_each_dev(pci_set_irq_dev, autocfg); |
---|
| 995 | } |
---|
| 996 | |
---|
| 997 | DBG("PCI resource allocation done\n"); |
---|
| 998 | |
---|
| 999 | return 0; |
---|
| 1000 | } |
---|
| 1001 | |
---|
| 1002 | void pci_config_auto_register(void *config) |
---|
| 1003 | { |
---|
| 1004 | pci_config_auto_initialized = 1; |
---|
| 1005 | memcpy(&pci_auto_cfg, config, sizeof(struct pci_auto_setup)); |
---|
| 1006 | } |
---|