in hotplug/ibmphp_res.c [1906:2119]
static int __init update_bridge_ranges(struct bus_node **bus)
{
u8 sec_busno, device, function, hdr_type, start_io_address, end_io_address;
u16 vendor_id, upper_io_start, upper_io_end, start_mem_address, end_mem_address;
u32 start_address, end_address, upper_start, upper_end;
struct bus_node *bus_sec;
struct bus_node *bus_cur;
struct resource_node *io;
struct resource_node *mem;
struct resource_node *pfmem;
struct range_node *range;
unsigned int devfn;
bus_cur = *bus;
if (!bus_cur)
return -ENODEV;
ibmphp_pci_bus->number = bus_cur->busno;
debug("inside %s\n", __func__);
debug("bus_cur->busno = %x\n", bus_cur->busno);
for (device = 0; device < 32; device++) {
for (function = 0x00; function < 0x08; function++) {
devfn = PCI_DEVFN(device, function);
pci_bus_read_config_word(ibmphp_pci_bus, devfn, PCI_VENDOR_ID, &vendor_id);
if (vendor_id != PCI_VENDOR_ID_NOTVALID) {
/* found correct device!!! */
pci_bus_read_config_byte(ibmphp_pci_bus, devfn, PCI_HEADER_TYPE, &hdr_type);
switch (hdr_type) {
case PCI_HEADER_TYPE_NORMAL:
function = 0x8;
break;
case PCI_HEADER_TYPE_MULTIDEVICE:
break;
case PCI_HEADER_TYPE_BRIDGE:
function = 0x8;
fallthrough;
case PCI_HEADER_TYPE_MULTIBRIDGE:
/* We assume here that only 1 bus behind the bridge
TO DO: add functionality for several:
temp = secondary;
while (temp < subordinate) {
...
temp++;
}
*/
pci_bus_read_config_byte(ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_busno);
bus_sec = find_bus_wprev(sec_busno, NULL, 0);
/* this bus structure doesn't exist yet, PPB was configured during previous loading of ibmphp */
if (!bus_sec) {
bus_sec = alloc_error_bus(NULL, sec_busno, 1);
/* the rest will be populated during NVRAM call */
return 0;
}
pci_bus_read_config_byte(ibmphp_pci_bus, devfn, PCI_IO_BASE, &start_io_address);
pci_bus_read_config_byte(ibmphp_pci_bus, devfn, PCI_IO_LIMIT, &end_io_address);
pci_bus_read_config_word(ibmphp_pci_bus, devfn, PCI_IO_BASE_UPPER16, &upper_io_start);
pci_bus_read_config_word(ibmphp_pci_bus, devfn, PCI_IO_LIMIT_UPPER16, &upper_io_end);
start_address = (start_io_address & PCI_IO_RANGE_MASK) << 8;
start_address |= (upper_io_start << 16);
end_address = (end_io_address & PCI_IO_RANGE_MASK) << 8;
end_address |= (upper_io_end << 16);
if ((start_address) && (start_address <= end_address)) {
range = kzalloc(sizeof(struct range_node), GFP_KERNEL);
if (!range)
return -ENOMEM;
range->start = start_address;
range->end = end_address + 0xfff;
if (bus_sec->noIORanges > 0) {
if (!range_exists_already(range, bus_sec, IO)) {
add_bus_range(IO, range, bus_sec);
++bus_sec->noIORanges;
} else {
kfree(range);
range = NULL;
}
} else {
/* 1st IO Range on the bus */
range->rangeno = 1;
bus_sec->rangeIO = range;
++bus_sec->noIORanges;
}
fix_resources(bus_sec);
if (ibmphp_find_resource(bus_cur, start_address, &io, IO)) {
io = kzalloc(sizeof(struct resource_node), GFP_KERNEL);
if (!io) {
kfree(range);
return -ENOMEM;
}
io->type = IO;
io->busno = bus_cur->busno;
io->devfunc = ((device << 3) | (function & 0x7));
io->start = start_address;
io->end = end_address + 0xfff;
io->len = io->end - io->start + 1;
ibmphp_add_resource(io);
}
}
pci_bus_read_config_word(ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, &start_mem_address);
pci_bus_read_config_word(ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, &end_mem_address);
start_address = 0x00000000 | (start_mem_address & PCI_MEMORY_RANGE_MASK) << 16;
end_address = 0x00000000 | (end_mem_address & PCI_MEMORY_RANGE_MASK) << 16;
if ((start_address) && (start_address <= end_address)) {
range = kzalloc(sizeof(struct range_node), GFP_KERNEL);
if (!range)
return -ENOMEM;
range->start = start_address;
range->end = end_address + 0xfffff;
if (bus_sec->noMemRanges > 0) {
if (!range_exists_already(range, bus_sec, MEM)) {
add_bus_range(MEM, range, bus_sec);
++bus_sec->noMemRanges;
} else {
kfree(range);
range = NULL;
}
} else {
/* 1st Mem Range on the bus */
range->rangeno = 1;
bus_sec->rangeMem = range;
++bus_sec->noMemRanges;
}
fix_resources(bus_sec);
if (ibmphp_find_resource(bus_cur, start_address, &mem, MEM)) {
mem = kzalloc(sizeof(struct resource_node), GFP_KERNEL);
if (!mem) {
kfree(range);
return -ENOMEM;
}
mem->type = MEM;
mem->busno = bus_cur->busno;
mem->devfunc = ((device << 3) | (function & 0x7));
mem->start = start_address;
mem->end = end_address + 0xfffff;
mem->len = mem->end - mem->start + 1;
ibmphp_add_resource(mem);
}
}
pci_bus_read_config_word(ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, &start_mem_address);
pci_bus_read_config_word(ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &end_mem_address);
pci_bus_read_config_dword(ibmphp_pci_bus, devfn, PCI_PREF_BASE_UPPER32, &upper_start);
pci_bus_read_config_dword(ibmphp_pci_bus, devfn, PCI_PREF_LIMIT_UPPER32, &upper_end);
start_address = 0x00000000 | (start_mem_address & PCI_MEMORY_RANGE_MASK) << 16;
end_address = 0x00000000 | (end_mem_address & PCI_MEMORY_RANGE_MASK) << 16;
#if BITS_PER_LONG == 64
start_address |= ((long) upper_start) << 32;
end_address |= ((long) upper_end) << 32;
#endif
if ((start_address) && (start_address <= end_address)) {
range = kzalloc(sizeof(struct range_node), GFP_KERNEL);
if (!range)
return -ENOMEM;
range->start = start_address;
range->end = end_address + 0xfffff;
if (bus_sec->noPFMemRanges > 0) {
if (!range_exists_already(range, bus_sec, PFMEM)) {
add_bus_range(PFMEM, range, bus_sec);
++bus_sec->noPFMemRanges;
} else {
kfree(range);
range = NULL;
}
} else {
/* 1st PFMem Range on the bus */
range->rangeno = 1;
bus_sec->rangePFMem = range;
++bus_sec->noPFMemRanges;
}
fix_resources(bus_sec);
if (ibmphp_find_resource(bus_cur, start_address, &pfmem, PFMEM)) {
pfmem = kzalloc(sizeof(struct resource_node), GFP_KERNEL);
if (!pfmem) {
kfree(range);
return -ENOMEM;
}
pfmem->type = PFMEM;
pfmem->busno = bus_cur->busno;
pfmem->devfunc = ((device << 3) | (function & 0x7));
pfmem->start = start_address;
pfmem->end = end_address + 0xfffff;
pfmem->len = pfmem->end - pfmem->start + 1;
pfmem->fromMem = 0;
ibmphp_add_resource(pfmem);
}
}
break;
} /* end of switch */
} /* end if vendor */
} /* end for function */
} /* end for device */
bus = &bus_cur;
return 0;
}