/* PCI (Auto) configuration Library. Setup PCI configuration space and IRQ. * * COPYRIGHT (c) 2010 Cobham Gaisler AB. * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.com/license/LICENSE. */ #include #include #include #include /* Configure headers */ #define PCI_CFG_AUTO_LIB #include #include #include /* #define DEBUG */ #ifdef DEBUG #define DBG(x...) printk(x) #else #define DBG(x...) #endif /* PCI Library * (For debugging it might be good to use other functions or the driver's * directly) */ #define PCI_CFG_R8(dev, args...) pci_cfg_r8(dev, args) #define PCI_CFG_R16(dev, args...) pci_cfg_r16(dev, args) #define PCI_CFG_R32(dev, args...) pci_cfg_r32(dev, args) #define PCI_CFG_W8(dev, args...) pci_cfg_w8(dev, args) #define PCI_CFG_W16(dev, args...) pci_cfg_w16(dev, args) #define PCI_CFG_W32(dev, args...) pci_cfg_w32(dev, args) /* Number of PCI buses */ extern int pci_bus_cnt; int pci_config_auto_initialized = 0; /* Configuration setup */ struct pci_auto_setup pci_auto_cfg; /* Insert BAR into the sorted resources list. The BARs are sorted on the * BAR size/alignment need. */ static void pci_res_insert(struct pci_res **root, struct pci_res *res) { struct pci_res *curr, *last; unsigned long curr_size_resulting_boundary, size_resulting_boundary; unsigned long boundary, size; res->start = 0; res->end = 0; boundary = res->boundary; size = res->size; /* Insert the resources depending on the boundary needs * Normally the boundary=size of the BAR, however when * PCI bridges are involved the bridge's boundary may be * smaller than the size due to the fact that a bridge * may have different-sized BARs behind, the largest BAR * (also the BAR with the largest boundary) will decide * the alignment need. */ last = NULL; curr = *root; /* Order List after boundary, the boundary is maintained * when the size is on an equal boundary, normally it is * but may not be with bridges. So in second hand it is * sorted after resulting boundary - the boundary after * the resource. */ while (curr && (curr->boundary >= boundary)) { if (curr->boundary == boundary) { /* Find Resulting boundary of size */ size_resulting_boundary = 1; while ((size & size_resulting_boundary) == 0) size_resulting_boundary = size_resulting_boundary << 1; /* Find Resulting boundary of curr->size */ curr_size_resulting_boundary = 1; while ((curr->size & curr_size_resulting_boundary) == 0) curr_size_resulting_boundary = curr_size_resulting_boundary << 1; if (size_resulting_boundary >= curr_size_resulting_boundary) break; } last = curr; curr = curr->next; } if (last == NULL) { /* Insert first in list */ res->next = *root; *root = res; } else { last->next = res; res->next = curr; } } #ifdef DEBUG void pci_res_list_print(struct pci_res *root) { if (root == NULL) return; printf("RESOURCE LIST:\n"); while (root) { printf(" SIZE: 0x%08x, BOUNDARY: 0x%08x\n", root->size, root->boundary); root = root->next; } } #endif /* Reorder a size/alignment ordered resources list. The idea is to * avoid unused due to alignment/size restriction. * * NOTE: The first element is always untouched. * NOTE: If less than three elements in list, nothing will be done * * Normally a BAR has the same alignment requirements as the size of the * BAR. However, when bridges are involved the alignment need may be smaller * than the size, because a bridge resource consist or multiple BARs. * For example, say that a bridge with a 256Mb and a 16Mb BAR is found, then * the alignment is required to be 256Mb but the size 256+16Mb. * * In order to minimize dead space on the bus, the boundary ordered list * is reordered, example: * BUS0 * | BUS1 * |------------| * | |-- BAR0: SIZE=256Mb, ALIGNMENT=256MB * | |-- BAR1: SIZE=16Mb, ALIGNMENT=16MB * | | * | | * | | * | | BUS2 (BAR_BRIDGE1: SIZE=256+16, ALIGNEMENT=256) * | |----------| * | | |-- BAR2: SIZE=256Mb, ALIGNMENT=256Mb * | | |-- BAR3: SIZE=16Mb, ALIGNMENT=16MB * * A alignment/boundary ordered list of BUS1 will look like: * - BAR_BRIDGE1 * - BAR0 (ALIGMENT NEED 256Mb) * - BAR1 * * However, Between BAR_BRIDGE1 and BAR0 will be a unused hole of 256-16Mb. * We can put BAR1 before BAR0 to avoid the problem. */ static void pci_res_reorder(struct pci_res *root) { struct pci_res *curr, *last, *curr2, *last2; unsigned int start, start_next, hole_size, hole_boundary; if (root == NULL) return; /* Make up a start address with the boundary of the * First element. */ start = root->boundary + root->size; last = root; curr = root->next; while (curr) { /* Find start address of resource */ start_next = (start + (curr->boundary - 1)) & ~(curr->boundary - 1); /* Find hole size, the unsed space in between last resource * and next */ hole_size = start_next - start; /* Find Boundary of START */ hole_boundary = 1; while ((start & hole_boundary) == 0) hole_boundary = hole_boundary<<1; /* Detect dead hole */ if (hole_size > 0) { /* Step through list and try to find a resource that * can fit into hole. Take into account hole start * boundary and hole size. */ last2 = curr; curr2 = curr->next; while (curr2) { if ((curr2->boundary <= hole_boundary) && (curr2->size <= hole_size)) { /* Found matching resource. Move it * first in the hole. Then rescan, now * that the hole has changed in * size/boundary. */ last2->next = curr2->next; curr2->next = curr; last->next = curr2; /* New Start address */ start_next = (start + (curr2->boundary - 1)) & ~(curr2->boundary - 1); /* Since we inserted the resource before * curr we need to re-evaluate curr one * more, more resources may fit into the * shrunken hole. */ curr = curr2; break; } last2 = curr2; curr2 = curr2->next; } } /* No hole or nothing fit into hole. */ start = start_next; last = curr; curr = curr->next; } } /* Find the total size required in PCI address space needed by a resource list*/ static unsigned int pci_res_size(struct pci_res *root) { struct pci_res *curr; unsigned int size; /* Get total size of all resources */ size = 0; curr = root; while (curr) { size = (size + (curr->boundary - 1)) & ~(curr->boundary - 1); size += curr->size; curr = curr->next; } return size; } #if 0 /* not used for now */ /* Free a device and secondary bus if device is a bridge */ static void pci_dev_free(struct pci_dev *dev) { struct pci_dev *subdev; struct pci_bus *bus; if (dev->flags & PCI_DEV_BRIDGE) { bus = (struct pci_bus *)dev; for (subdev = bus->devs; subdev ; subdev = subdev->next) pci_dev_free(dev); } free(dev); } #endif static struct pci_dev *pci_dev_create(int isbus) { void *ptr; int size; if (isbus) size = sizeof(struct pci_bus); else size = sizeof(struct pci_dev); ptr = malloc(size); if (!ptr) rtems_fatal_error_occurred(RTEMS_NO_MEMORY); memset(ptr, 0, size); return ptr; } static void pci_find_devs(struct pci_bus *bus) { uint32_t id, tmp; uint8_t header; int slot, func, fail; struct pci_dev *dev, **listptr; struct pci_bus *bridge; pci_dev_t pcidev; DBG("Scanning bus %d\n", bus->num); listptr = &bus->devs; for (slot = 0; slot <= PCI_SLOTMAX; slot++) { /* Slot address */ pcidev = PCI_DEV(bus->num, slot, 0); for (func = 0; func <= PCI_FUNCMAX; func++, pcidev++) { fail = PCI_CFG_R32(pcidev, PCIR_VENDOR, &id); if (fail || id == 0xffffffff || id == 0) { /* * This slot is empty */ if (func == 0) break; else continue; } DBG("Found PCIDEV 0x%x at (bus %x, slot %x, func %x)\n", id, bus, slot, func); /* Set command to reset values, it disables bus * mastering and address responses. */ PCI_CFG_W16(pcidev, PCIR_COMMAND, 0); /* Clear any already set status bits */ PCI_CFG_W16(pcidev, PCIR_STATUS, 0xf900); /* Set latency timer to 64 */ PCI_CFG_W8(pcidev, PCIR_LATTIMER, 64); PCI_CFG_R32(pcidev, PCIR_REVID, &tmp); tmp >>= 16; dev = pci_dev_create(tmp == PCID_PCI2PCI_BRIDGE); *listptr = dev; listptr = &dev->next; dev->busdevfun = pcidev; dev->bus = bus; PCI_CFG_R16(pcidev, PCIR_VENDOR, &dev->vendor); PCI_CFG_R16(pcidev, PCIR_DEVICE, &dev->device); PCI_CFG_R32(pcidev, PCIR_REVID, &dev->classrev); if (tmp == PCID_PCI2PCI_BRIDGE) { DBG("Found PCI-PCI Bridge 0x%x at " "(bus %x, slot %x, func %x)\n", id, bus, slot, func); dev->flags = PCI_DEV_BRIDGE; dev->subvendor = 0; dev->subdevice = 0; bridge = (struct pci_bus *)dev; bridge->num = bus->sord + 1; bridge->pri = bus->num; bridge->sord = bus->sord + 1; /* Configure bridge (no support for 64-bit) */ PCI_CFG_W32(pcidev, 0x28, 0); PCI_CFG_W32(pcidev, 0x2C, 0); tmp = (64 << 24) | (0xff << 16) | (bridge->num << 8) | bridge->pri; PCI_CFG_W32(pcidev, PCIR_PRIBUS_1, tmp); /* Scan Secondary Bus */ pci_find_devs(bridge); /* sord might have been updated */ PCI_CFG_W8(pcidev, 0x1a, bridge->sord); bus->sord = bridge->sord; DBG("PCI-PCI BRIDGE: Primary %x, Secondary %x, " "Subordinate %x\n", bridge->pri, bridge->num, bridge->sord); } else { /* Disable Cardbus CIS Pointer */ PCI_CFG_W32(pcidev, PCIR_CIS, 0); /* Devices have subsytem device and vendor ID */ PCI_CFG_R16(pcidev, PCIR_SUBVEND_0, &dev->subvendor); PCI_CFG_R16(pcidev, PCIR_SUBDEV_0, &dev->subdevice); } /* Stop if not a multi-function device */ if (func == 0) { pci_cfg_r8(pcidev, PCIR_HDRTYPE, &header); if ((header & PCIM_MFDEV) == 0) break; } } } } static void pci_find_bar(struct pci_dev *dev, int bar) { uint32_t size, disable, mask; struct pci_res *res = &dev->resources[bar]; pci_dev_t pcidev = dev->busdevfun; int ofs; #ifdef DEBUG char *str; #define DBG_SET_STR(str, val) str = (val) #else #define DBG_SET_STR(str, val) #endif DBG("Bus: %x, Slot: %x, function: %x, bar%d\n", PCI_DEV_EXPAND(pcidev), bar); res->bar = bar; if (bar == DEV_RES_ROM) { if (dev->flags & PCI_DEV_BRIDGE) ofs = PCIR_BIOS_1; else ofs = PCIR_BIOS; disable = 0; /* ROM BARs have a unique enable bit per BAR */ } else { ofs = PCIR_BAR(0) + (bar << 2); disable = pci_invalid_address; } PCI_CFG_W32(pcidev, ofs, 0xffffffff); PCI_CFG_R32(pcidev, ofs, &size); PCI_CFG_W32(pcidev, ofs, disable); if (size == 0 || size == 0xffffffff) return; if (bar == DEV_RES_ROM) { mask = PCIM_BIOS_ADDR_MASK; DBG_SET_STR(str, "ROM"); if (dev->bus->flags & PCI_BUS_MEM) res->flags = PCI_RES_MEM; else res->flags = PCI_RES_MEMIO; } else if (((size & 0x1) == 0) && (size & 0x6)) { /* unsupported Memory type */ PCI_CFG_W32(pcidev, ofs, 0); return; } else { mask = ~0xf; if (size & 0x1) { /* I/O */ mask = ~0x3; res->flags = PCI_RES_IO; DBG_SET_STR(str, "I/O"); if (size & 0xffff0000) res->flags |= PCI_RES_IO32; /* Limit size of I/O space to 256 byte */ size |= 0xffffff00; if ((dev->bus->flags & PCI_BUS_IO) == 0) { res->flags |= PCI_RES_FAIL; dev->flags |= PCI_DEV_RES_FAIL; } } else { /* Memory. We convert Prefetchable Memory BARs to Memory * BARs in case the Bridge does not support prefetchable * memory. */ if ((size & 0x8) && (dev->bus->flags & PCI_BUS_MEM)) { /* Prefetchable and Bus supports it */ res->flags = PCI_RES_MEM; DBG_SET_STR(str, "MEM"); } else { res->flags = PCI_RES_MEMIO; DBG_SET_STR(str, "MEMIO"); } } } size &= mask; res->size = ~size + 1; res->boundary = ~size + 1; DBG("Bus: %x, Slot: %x, function: %x, %s bar%d size: %x\n", PCI_DEV_EXPAND(pcidev), str, bar, res->size); } static int pci_find_res_dev(struct pci_dev *dev, void *unused) { struct pci_bus *bridge; uint32_t tmp; uint16_t tmp16; pci_dev_t pcidev = dev->busdevfun; int i, maxbars; if (dev->flags & PCI_DEV_BRIDGE) { /* PCI-PCI Bridge */ bridge = (struct pci_bus *)dev; /* Only 2 Bridge BARs */ maxbars = 2; /* Probe Bridge Spaces (MEMIO space always implemented), the * probe disables all space-decoding at the same time */ PCI_CFG_W32(pcidev, 0x30, 0); PCI_CFG_W16(pcidev, 0x1c, 0x00f0); PCI_CFG_R16(pcidev, 0x1c, &tmp16); if (tmp16 != 0) { bridge->flags |= PCI_BUS_IO; if (tmp16 & 0x1) bridge->flags |= PCI_BUS_IO32; } PCI_CFG_W32(pcidev, 0x24, 0x0000ffff); PCI_CFG_R32(pcidev, 0x24, &tmp); if (tmp != 0) bridge->flags |= PCI_BUS_MEM; PCI_CFG_W32(pcidev, 0x20, 0x0000ffff); bridge->flags |= PCI_BUS_MEMIO; } else { /* Normal PCI Device as max 6 BARs */ maxbars = 6; } /* Probe BARs */ for (i = 0; i < maxbars; i++) pci_find_bar(dev, i); pci_find_bar(dev, DEV_RES_ROM); return 0; } static int pci_add_res_dev(struct pci_dev *dev, void *arg); static void pci_add_res_bus(struct pci_bus *bus, int type) { int tindex = type - 1; /* Clear old resources */ bus->busres[tindex] = NULL; /* Add resources of devices behind bridge if bridge supports * resource type. If MEM space not supported by bridge, they are * converted to MEMIO in the process. */ if (!((type == PCI_BUS_IO) && ((bus->flags & PCI_BUS_IO) == 0))) { pci_for_each_child(bus, pci_add_res_dev, (void *)type, 0); /* Reorder Bus resources to fit more optimally (avoid dead * PCI space). Currently they are sorted by boundary and size. * * This is especially important when multiple buses (bridges) * are present. */ pci_res_reorder(bus->busres[tindex]); } } static int pci_add_res_dev(struct pci_dev *dev, void *arg) { int tindex, type = (int)arg; struct pci_bus *bridge; struct pci_res *res, *first_busres; int i; uint32_t bbound; /* Type index in Bus resource */ tindex = type - 1; if (dev->flags & PCI_DEV_BRIDGE) { /* PCI-PCI Bridge. Add all sub-bus resources first */ bridge = (struct pci_bus *)dev; /* Add all child device's resources to this type */ pci_add_res_bus(bridge, type); /* Propagate the resources from child bus to BAR on * this bus, by adding a "fake" BAR per type. */ res = &bridge->dev.resources[BUS_RES_START + tindex]; res->bar = BUS_RES_START + tindex; res->start = 0; res->end = 0; res->flags = 0; /* mark BAR resource not available */ first_busres = bridge->busres[tindex]; if (first_busres) { res->flags = type; res->size = pci_res_size(first_busres); res->boundary = first_busres->boundary; if (type == PCI_RES_IO) { bbound = 0x1000; /* Bridge I/O min 4KB */ } else { bbound = 0x100000; /* Bridge MEM min 1MB */ /* Convert MEM to MEMIO if not supported by * this bridge */ if ((bridge->flags & PCI_BUS_MEM) == 0) res->flags = PCI_RES_MEMIO; } /* Fulfil minimum bridge boundary */ if (res->boundary < bbound) res->boundary = bbound; /* Make sure that size is atleast bridge boundary */ if (res->size > bbound && (res->size & (bbound-1))) res->size = (res->size | (bbound-1)) + 1; } } /* Normal PCI Device as max 6 BARs and a ROM Bar. * Insert BARs into the sorted resource list. */ for (i = 0; i < DEV_RES_CNT; i++) { res = &dev->resources[i]; if ((res->flags & PCI_RES_TYPE_MASK) != type) continue; pci_res_insert(&dev->bus->busres[tindex], res); } return 0; } /* Function assumes that base is properly aligned to the requirement of the * largest BAR in the system. */ static uint32_t pci_alloc_res(struct pci_bus *bus, int type, uint32_t start, uint32_t end) { struct pci_dev *dev; struct pci_res *res, **prev_next; unsigned long starttmp; struct pci_bus *bridge; int removed, sec_type; /* The resources are sorted on their size (size and alignment is the * same) */ prev_next = &bus->busres[type - 1]; while ((res = *prev_next) != NULL) { dev = RES2DEV(res); removed = 0; /* Align start to this reource's need, only needed after * a bridge resource has been allocated. */ starttmp = (start + (res->boundary-1)) & ~(res->boundary-1); if ((starttmp + res->size - 1) > end) { /* Not enough memory available for this resource */ printk("PCI[%x:%x:%x]: DEV BAR%d (%d): no resource " "assigned\n", PCI_DEV_EXPAND(dev->busdevfun), res->bar, res->flags & PCI_RES_TYPE_MASK); res->start = res->end = 0; /* If this resources is a bridge window to the * secondary bus, the secondary resources are not * changed which has the following effect: * I/O : Will never be assigned * MEMIO : Will never be assigned * MEM : Will stay marked as MEM, but bridge window * is changed into MEMIO, when the window is * assigned a MEMIO address the secondary * resources will also be assigned. */ if (type == PCI_RES_MEM) { /* Try prefetchable as non-prefetchable mem */ res->flags &= ~PCI_RES_MEM_PREFETCH; /* Remove resource from MEM list, ideally we * should regenerate this list in order to fit * the comming BARs more optimially... */ *prev_next = res->next; /* We should not update prev_next here since * we just removed the resource from the list */ removed = 1; } else { res->flags |= PCI_RES_FAIL; dev->flags |= PCI_DEV_RES_FAIL; } } else { start = starttmp; res->start = start; res->end = start + res->size; /* "Virtual BAR" on a bridge? A bridge resource need all * its child devices resources allocated */ if ((res->bar != DEV_RES_ROM) && (dev->flags & PCI_DEV_BRIDGE) && (res->bar >= BUS_RES_START)) { bridge = (struct pci_bus *)dev; /* If MEM bar was changed into a MEMIO the * secondary MEM resources are still set to MEM, */ if (type == PCI_BUS_MEMIO && res->bar == BRIDGE_RES_MEM) sec_type = PCI_RES_MEM; else sec_type = type; pci_alloc_res(bridge, sec_type, res->start, res->end); } start += res->size; } if (removed == 0) prev_next = &res->next; } return start; } static void pci_set_bar(struct pci_dev *dev, int residx) { uint32_t tmp; uint16_t tmp16; pci_dev_t pcidev; struct pci_res *res; int is_bridge, ofs; res = &dev->resources[residx]; pcidev = dev->busdevfun; if ((res->flags == 0) || (res->flags & PCI_RES_FAIL)) return; is_bridge = dev->flags & PCI_DEV_BRIDGE; if (res->bar == DEV_RES_ROM) { /* ROM: 32-bit prefetchable memory BAR */ if (is_bridge) ofs = PCIR_BIOS_1; else ofs = PCIR_BIOS; PCI_CFG_W32(pcidev, ofs, res->start | PCIM_BIOS_ENABLE); DBG("PCI[%x:%x:%x]: ROM BAR: 0x%x-0x%x\n", PCI_DEV_EXPAND(pcidev), res->start, res->end); } else if (is_bridge && (res->bar == BRIDGE_RES_IO)) { /* PCI Bridge I/O BAR */ DBG("PCI[%x:%x:%x]: BAR 1C: 0x%x-0x%x\n", PCI_DEV_EXPAND(pcidev), res->start, res->end); /* Limit and Base */ tmp16 = ((res->end-1) & 0x0000f000) | ((res->start & 0x0000f000) >> 8); tmp = ((res->end-1) & 0xffff0000) | (res->start >> 16); DBG("PCI[%x:%x:%x]: BRIDGE BAR 0x%x: 0x%08x [0x30: 0x%x]\n", PCI_DEV_EXPAND(pcidev), 0x1C, tmp, tmp2); PCI_CFG_W16(pcidev, 0x1C, tmp16); PCI_CFG_W32(pcidev, 0x30, tmp); } else if (is_bridge && (res->bar >= BRIDGE_RES_MEMIO)) { /* PCI Bridge MEM and MEMIO Space */ /* Limit and Base */ tmp = ((res->end-1) & 0xfff00000) | (res->start >> 16); DBG("PCI[%x:%x:%x]: BRIDGE BAR 0x%x: 0x%08x\n", PCI_DEV_EXPAND(pcidev), 0x20 + (res->bar-BRIDGE_RES_MEMIO)*4, tmp); PCI_CFG_W32(pcidev, 0x20+(res->bar-BRIDGE_RES_MEMIO)*4, tmp); } else { /* PCI Device */ DBG("PCI[%x:%x:%x]: DEV BAR%d: 0x%08x\n", PCI_DEV_EXPAND(pcidev), res->bar, res->start); ofs = PCIR_BAR(0) + res->bar*4; PCI_CFG_W32(pcidev, ofs, res->start); } /* Enable Memory or I/O responses */ if ((res->flags & PCI_RES_TYPE_MASK) == PCI_RES_IO) pci_io_enable(pcidev); else pci_mem_enable(pcidev); /* Enable Master if bridge */ if (is_bridge) pci_master_enable(pcidev); } static int pci_set_res_dev(struct pci_dev *dev, void *unused) { int i, maxbars; if (dev->flags & PCI_DEV_BRIDGE) maxbars = 2 + 3; /* 2 BARs + 3 Bridge-Windows "Virtual BARs" */ else maxbars = 6; /* Normal PCI Device as max 6 BARs. */ /* Set BAR resources with previous allocated values */ for (i = 0; i < maxbars; i++) pci_set_bar(dev, i); pci_set_bar(dev, DEV_RES_ROM); return 0; } /* Route IRQ through PCI-PCI Bridges */ static int pci_route_irq(pci_dev_t dev, int irq_pin) { int slot_grp; if (PCI_DEV_BUS(dev) == 0) return irq_pin; slot_grp = PCI_DEV_SLOT(dev) & 0x3; return (((irq_pin - 1) + slot_grp) & 0x3) + 1; } /* Put assigned system IRQ into PCI interrupt line information field. * This is to make it possible for drivers to read system IRQ / Vector from * configuration space later on. * * 1. Get Interrupt PIN * 2. Route PIN to host bridge * 3. Get System interrupt number assignment for PIN * 4. Set Interrupt LINE */ static int pci_set_irq_dev(struct pci_dev *dev, void *cfg) { struct pci_auto_setup *autocfg = cfg; uint8_t irq_pin, irq_line, *psysirq; pci_dev_t pcidev; psysirq = &dev->sysirq; pcidev = dev->busdevfun; PCI_CFG_R8(pcidev, PCIR_INTPIN, &irq_pin); /* perform IRQ routing until we reach host bridge */ while (dev->bus && irq_pin != 0) { irq_pin = autocfg->irq_route(dev->busdevfun, irq_pin); dev = &dev->bus->dev; } /* Get IRQ from PIN on PCI bus0 */ if (irq_pin != 0 && autocfg->irq_map) irq_line = autocfg->irq_map(dev->busdevfun, irq_pin); else irq_line = 0; *psysirq = irq_line; /* Set System Interrupt/Vector for device. 0 means no-IRQ */ PCI_CFG_W8(pcidev, PCIR_INTLINE, irq_line); return 0; } /* This routine assumes that PCI access library has been successfully * initialized. All information about the PCI bus needed is found in * the pci_auto_cfg structure passed on by pci_config_register(). * * The PCI buses are enumerated as bridges are found, PCI devices are * setup with BARs and IRQs, etc. */ int pci_config_auto(void) { uint32_t end; uint32_t startmemio, startmem, startio; struct pci_auto_setup *autocfg = &pci_auto_cfg; #ifdef DEBUG uint32_t endmemio, endmem, endio; uint32_t start; #endif if (pci_config_auto_initialized == 0) return -1; /* no config given to library */ #ifdef DEBUG DBG("\n--- PCI MEMORY AVAILABLE ---\n"); if (autocfg->mem_size) { start = autocfg->mem_start; end = autocfg->mem_start + autocfg->mem_size - 1; DBG(" MEM AVAIL [0x%08x-0x%08x]\n", start, end); } else { /* One big memory space */ DBG(" MEM share the space with MEMIO\n"); } /* no-prefetchable memory space need separate memory space. * For example PCI controller maps this region non-cachable. */ start = autocfg->memio_start; end = autocfg->memio_start + autocfg->memio_size - 1; DBG(" MEMIO AVAIL [0x%08x-0x%08x]\n", start, end); if (autocfg->io_size) { start = autocfg->io_start; end = autocfg->io_start + autocfg->io_size - 1; DBG(" I/O AVAIL [0x%08x-0x%08x]\n", start, end); } else { DBG(" I/O Space not available\n"); } #endif /* Init Host-Bridge */ memset(&pci_hb, 0, sizeof(pci_hb)); pci_hb.dev.flags = PCI_DEV_BRIDGE; if (autocfg->memio_size <= 0) return -1; pci_hb.flags = PCI_BUS_MEMIO; if (autocfg->mem_size) pci_hb.flags |= PCI_BUS_MEM; if (autocfg->io_size) pci_hb.flags |= PCI_BUS_IO; /* Find all PCI devices/functions on all buses. The buses will be * enumrated (assigned a unique PCI Bus ID 0..255). */ DBG("\n--- PCI SCANNING ---\n"); pci_find_devs(&pci_hb); pci_bus_cnt = pci_hb.sord + 1; if (pci_hb.devs == NULL) return 0; pci_system_type = PCI_SYSTEM_HOST; /* Find all resources (MEM/MEMIO/IO BARs) of all devices/functions * on all buses. * * Device resources behind bridges which does not support prefetchable * memory are already marked as non-prefetchable memory. * Devices which as I/O resources behind a bridge that do not support * I/O space are marked DISABLED. * * All BARs and Bridge Spaces are disabled after this. Only the ones * that are allocated an address are initilized later on. */ DBG("\n\n--- PCI RESOURCES ---\n"); pci_for_each_dev(pci_find_res_dev, 0); /* Add all device's resources to bus and sort them to fit in the PCI * Window. The device resources are propagated upwards through bridges * by adding a "virtual" BAR (boundary != BAR size). * * We wait with MEMIO (non-prefetchable memory) resources to after MEM * resources have been allocated, so that MEM resources can be changed * into MEMIO resources if not enough space. */ pci_add_res_bus(&pci_hb, PCI_RES_IO); pci_add_res_bus(&pci_hb, PCI_RES_MEM); /* Start assigning found resource according to the sorted order. */ /* Allocate resources to I/O areas */ if (pci_hb.busres[BUS_RES_IO]) { startio = autocfg->io_start; end = startio + autocfg->io_size; #ifdef DEBUG endio = #endif pci_alloc_res(&pci_hb, PCI_RES_IO, startio, end); } /* Allocate resources to prefetchable memory */ if (pci_hb.busres[BUS_RES_MEM]) { startmem = autocfg->mem_start; end = startmem + autocfg->mem_size; #ifdef DEBUG endmem = #endif pci_alloc_res(&pci_hb, PCI_RES_MEM, startmem, end); } /* Add non-prefetchable memory resources and not fitting prefetchable * memory resources. * * Some prefetchable memory resources may not have fitted into PCI * window. Prefetchable memory can be mapped into non-prefetchable * memory window. The failing BARs have been marked as MEMIO instead. */ pci_add_res_bus(&pci_hb, PCI_RES_MEMIO); /* Allocate resources to non-prefetchable memory */ if (pci_hb.busres[BUS_RES_MEMIO]) { startmemio = autocfg->memio_start; end = startmemio + autocfg->memio_size; #ifdef DEBUG endmemio = #endif pci_alloc_res(&pci_hb, PCI_RES_MEMIO, startmemio, end); } DBG("\n--- PCI ALLOCATED SPACE RANGES ---\n"); DBG(" MEM NON-PREFETCHABLE: [0x%08x-0x%08x]\n", startmemio, endmemio); DBG(" MEM PREFETCHABLE: [0x%08x-0x%08x]\n", startmem, endmem); DBG(" I/O: [0x%08x-0x%08x]\n", startio, endio); /* Set all allocated BARs and Bridge Windows */ pci_for_each_dev(pci_set_res_dev, NULL); /* Initialize IRQs of all devices. According to the PCI-PCI bridge * specification the IRQs are routed differently depending on slot * number. Drivers can override the default routing if a motherboard * requires it. */ if ((autocfg->options & CFGOPT_NOSETUP_IRQ) == 0) { if (autocfg->irq_route == NULL) /* use standard irq routing */ autocfg->irq_route = pci_route_irq; pci_for_each_dev(pci_set_irq_dev, autocfg); } DBG("PCI resource allocation done\n"); return 0; } void pci_config_auto_register(void *config) { pci_config_auto_initialized = 1; memcpy(&pci_auto_cfg, config, sizeof(struct pci_auto_setup)); }