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