+ busses_count = pci_bus + 1;
+}
+
+static void pci_bios_bus_get_bar(struct pci_bus *bus, int bdf, int bar,
+ u32 *val, u32 *size)
+{
+ u32 ofs = pci_bar(bdf, bar);
+ u32 old = pci_config_readl(bdf, ofs);
+ u32 mask;
+
+ if (bar == PCI_ROM_SLOT) {
+ mask = PCI_ROM_ADDRESS_MASK;
+ pci_config_writel(bdf, ofs, mask);
+ } else {
+ if (old & PCI_BASE_ADDRESS_SPACE_IO)
+ mask = PCI_BASE_ADDRESS_IO_MASK;
+ else
+ mask = PCI_BASE_ADDRESS_MEM_MASK;
+ pci_config_writel(bdf, ofs, ~0);
+ }
+ *val = pci_config_readl(bdf, ofs);
+ pci_config_writel(bdf, ofs, old);
+ *size = (~(*val & mask)) + 1;
+}
+
+static void pci_bios_bus_reserve(struct pci_bus *bus, int type, u32 size)
+{
+ u32 index;
+
+ index = pci_size_to_index(size, type);
+ size = pci_index_to_size(index, type);
+ bus->r[type].count[index]++;
+ bus->r[type].sum += size;
+ if (bus->r[type].max < size)
+ bus->r[type].max = size;
+}
+
+static u32 pci_bios_bus_get_addr(struct pci_bus *bus, int type, u32 size)
+{
+ u32 index, addr;
+
+ index = pci_size_to_index(size, type);
+ addr = bus->r[type].bases[index];
+ bus->r[type].bases[index] += pci_index_to_size(index, type);
+ return addr;
+}
+
+static void pci_bios_check_device(struct pci_bus *bus, struct pci_device *dev)
+{
+ u16 bdf = dev->bdf;
+ u32 limit;
+ int i,type;
+
+ if (dev->class == PCI_CLASS_BRIDGE_PCI) {
+ if (dev->secondary_bus >= busses_count) {
+ /* should never trigger */
+ dprintf(1, "PCI: bus count too small (%d), skipping bus #%d\n",
+ busses_count, dev->secondary_bus);
+ return;
+ }
+ struct pci_bus *s = busses + dev->secondary_bus;
+ pci_bios_check_device_in_bus(dev->secondary_bus);
+ for (type = 0; type < PCI_REGION_TYPE_COUNT; type++) {
+ limit = (type == PCI_REGION_TYPE_IO) ?
+ PCI_BRIDGE_IO_MIN : PCI_BRIDGE_MEM_MIN;
+ s->r[type].size = s->r[type].sum;
+ if (s->r[type].size < limit)
+ s->r[type].size = limit;
+ s->r[type].size = pci_size_roundup(s->r[type].size);
+ pci_bios_bus_reserve(bus, type, s->r[type].size);
+ }
+ dprintf(1, "PCI: secondary bus %d sizes: io %x, mem %x, prefmem %x\n",
+ dev->secondary_bus,
+ s->r[PCI_REGION_TYPE_IO].size,
+ s->r[PCI_REGION_TYPE_MEM].size,
+ s->r[PCI_REGION_TYPE_PREFMEM].size);
+ return;
+ }
+
+ for (i = 0; i < PCI_NUM_REGIONS; i++) {
+ u32 val, size;
+ pci_bios_bus_get_bar(bus, bdf, i, &val, &size);
+ if (val == 0) {
+ continue;
+ }
+ pci_bios_bus_reserve(bus, pci_addr_to_type(val), size);
+ dev->bars[i].addr = val;
+ dev->bars[i].size = size;
+ dev->bars[i].is64 = (!(val & PCI_BASE_ADDRESS_SPACE_IO) &&
+ (val & PCI_BASE_ADDRESS_MEM_TYPE_MASK) == PCI_BASE_ADDRESS_MEM_TYPE_64);
+
+ if (dev->bars[i].is64) {
+ i++;
+ }
+ }
+}
+
+static void pci_bios_map_device(struct pci_bus *bus, struct pci_device *dev)
+{
+ int type, i;
+
+ if (dev->class == PCI_CLASS_BRIDGE_PCI) {
+ if (dev->secondary_bus >= busses_count) {
+ return;
+ }
+ struct pci_bus *s = busses + dev->secondary_bus;
+
+ for (type = 0; type < PCI_REGION_TYPE_COUNT; type++) {
+ s->r[type].base = pci_bios_bus_get_addr(bus, type, s->r[type].size);
+ }
+ dprintf(1, "PCI: init bases bus %d (secondary)\n", dev->secondary_bus);
+ pci_bios_init_bus_bases(s);
+ /* TODO: commit assignments */
+
+ pci_bios_map_device_in_bus(dev->secondary_bus);
+ return;
+ }
+
+ for (i = 0; i < PCI_NUM_REGIONS; i++) {
+ u32 addr;
+ if (dev->bars[i].addr == 0) {
+ continue;
+ }
+
+ addr = pci_bios_bus_get_addr(bus, pci_addr_to_type(dev->bars[i].addr),
+ dev->bars[i].size);
+ dprintf(1, " bar %d, addr %x, size %x [%s]\n",
+ i, addr, dev->bars[i].size,
+ dev->bars[i].addr & PCI_BASE_ADDRESS_SPACE_IO ? "io" : "mem");
+ /* TODO: commit assignments */
+
+ if (dev->bars[i].is64) {
+ i++;
+ }
+ }
+}
+
+static void pci_bios_check_device_in_bus(int bus)
+{
+ struct pci_device *pci;
+
+ dprintf(1, "PCI: check devices bus %d\n", bus);
+ foreachpci(pci) {
+ if (pci_bdf_to_bus(pci->bdf) != bus)
+ continue;
+ pci_bios_check_device(&busses[bus], pci);
+ }
+}
+
+static void pci_bios_map_device_in_bus(int bus)
+{
+ struct pci_device *pci;
+
+ foreachpci(pci) {
+ if (pci_bdf_to_bus(pci->bdf) != bus)
+ continue;
+ dprintf(1, "PCI: map device bus %d, bfd 0x%x\n", bus, pci->bdf);
+ pci_bios_map_device(&busses[bus], pci);
+ }
+}
+
+static void pci_bios_init_bus_bases(struct pci_bus *bus)
+{
+ u32 base, newbase, size;
+ int type, i;
+
+ for (type = 0; type < PCI_REGION_TYPE_COUNT; type++) {
+ dprintf(1, " type %s max %x sum %x base %x\n", region_type_name[type],
+ bus->r[type].max, bus->r[type].sum, bus->r[type].base);
+ base = bus->r[type].base;
+ for (i = ARRAY_SIZE(bus->r[type].count)-1; i >= 0; i--) {
+ size = pci_index_to_size(i, type);
+ if (!bus->r[type].count[i])
+ continue;
+ newbase = base + size * bus->r[type].count[i];
+ dprintf(1, " size %8x: %d bar(s), %8x -> %8x\n",
+ size, bus->r[type].count[i], base, newbase - 1);
+ bus->r[type].bases[i] = base;
+ base = newbase;
+ }
+ }
+}
+
+#define ROOT_BASE(top, sum, align) ALIGN_DOWN((top)-(sum),(align))
+
+static int pci_bios_init_root_regions(u32 start, u32 end)
+{
+ struct pci_bus *bus = &busses[0];
+
+ bus->r[PCI_REGION_TYPE_IO].base = 0xc000;
+
+ if (bus->r[PCI_REGION_TYPE_MEM].sum < bus->r[PCI_REGION_TYPE_PREFMEM].sum) {
+ bus->r[PCI_REGION_TYPE_MEM].base =
+ ROOT_BASE(end,
+ bus->r[PCI_REGION_TYPE_MEM].sum,
+ bus->r[PCI_REGION_TYPE_MEM].max);
+ bus->r[PCI_REGION_TYPE_PREFMEM].base =
+ ROOT_BASE(bus->r[PCI_REGION_TYPE_MEM].base,
+ bus->r[PCI_REGION_TYPE_PREFMEM].sum,
+ bus->r[PCI_REGION_TYPE_PREFMEM].max);
+ if (bus->r[PCI_REGION_TYPE_PREFMEM].base >= start) {
+ return 0;
+ }
+ } else {
+ bus->r[PCI_REGION_TYPE_PREFMEM].base =
+ ROOT_BASE(end,
+ bus->r[PCI_REGION_TYPE_PREFMEM].sum,
+ bus->r[PCI_REGION_TYPE_PREFMEM].max);
+ bus->r[PCI_REGION_TYPE_MEM].base =
+ ROOT_BASE(bus->r[PCI_REGION_TYPE_PREFMEM].base,
+ bus->r[PCI_REGION_TYPE_MEM].sum,
+ bus->r[PCI_REGION_TYPE_MEM].max);
+ if (bus->r[PCI_REGION_TYPE_MEM].base >= start) {
+ return 0;
+ }
+ }
+ return -1;