static int __init update_bridge_ranges()

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;
}