static int configure_bridge()

in hotplug/ibmphp_pci.c [543:1038]


static int configure_bridge(struct pci_func **func_passed, u8 slotno)
{
	int count;
	int i;
	int rc;
	u8 sec_number;
	u8 io_base;
	u16 pfmem_base;
	u32 bar[2];
	u32 len[2];
	u8 flag_io = 0;
	u8 flag_mem = 0;
	u8 flag_pfmem = 0;
	u8 need_io_upper = 0;
	u8 need_pfmem_upper = 0;
	struct res_needed *amount_needed = NULL;
	struct resource_node *io = NULL;
	struct resource_node *bus_io[2] = {NULL, NULL};
	struct resource_node *mem = NULL;
	struct resource_node *bus_mem[2] = {NULL, NULL};
	struct resource_node *mem_tmp = NULL;
	struct resource_node *pfmem = NULL;
	struct resource_node *bus_pfmem[2] = {NULL, NULL};
	struct bus_node *bus;
	u32 address[] = {
		PCI_BASE_ADDRESS_0,
		PCI_BASE_ADDRESS_1,
		0
	};
	struct pci_func *func = *func_passed;
	unsigned int devfn;
	u8 irq;
	int retval;

	debug("%s - enter\n", __func__);

	devfn = PCI_DEVFN(func->function, func->device);
	ibmphp_pci_bus->number = func->busno;

	/* Configuring necessary info for the bridge so that we could see the devices
	 * behind it
	 */

	pci_bus_write_config_byte(ibmphp_pci_bus, devfn, PCI_PRIMARY_BUS, func->busno);

	/* _____________________For debugging purposes only __________________________
	pci_bus_config_byte(ibmphp_pci_bus, devfn, PCI_PRIMARY_BUS, &pri_number);
	debug("primary # written into the bridge is %x\n", pri_number);
	 ___________________________________________________________________________*/

	/* in EBDA, only get allocated 1 additional bus # per slot */
	sec_number = find_sec_number(func->busno, slotno);
	if (sec_number == 0xff) {
		err("cannot allocate secondary bus number for the bridged device\n");
		return -EINVAL;
	}

	debug("after find_sec_number, the number we got is %x\n", sec_number);
	debug("AFTER FIND_SEC_NUMBER, func->busno IS %x\n", func->busno);

	pci_bus_write_config_byte(ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, sec_number);

	/* __________________For debugging purposes only __________________________________
	pci_bus_read_config_byte(ibmphp_pci_bus, devfn, PCI_SECONDARY_BUS, &sec_number);
	debug("sec_number after write/read is %x\n", sec_number);
	 ________________________________________________________________________________*/

	pci_bus_write_config_byte(ibmphp_pci_bus, devfn, PCI_SUBORDINATE_BUS, sec_number);

	/* __________________For debugging purposes only ____________________________________
	pci_bus_read_config_byte(ibmphp_pci_bus, devfn, PCI_SUBORDINATE_BUS, &sec_number);
	debug("subordinate number after write/read is %x\n", sec_number);
	 __________________________________________________________________________________*/

	pci_bus_write_config_byte(ibmphp_pci_bus, devfn, PCI_CACHE_LINE_SIZE, CACHE);
	pci_bus_write_config_byte(ibmphp_pci_bus, devfn, PCI_LATENCY_TIMER, LATENCY);
	pci_bus_write_config_byte(ibmphp_pci_bus, devfn, PCI_SEC_LATENCY_TIMER, LATENCY);

	debug("func->busno is %x\n", func->busno);
	debug("sec_number after writing is %x\n", sec_number);


	/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	   !!!!!!!!!!!!!!!NEED TO ADD!!!  FAST BACK-TO-BACK ENABLE!!!!!!!!!!!!!!!!!!!!
	   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/


	/* First we need to allocate mem/io for the bridge itself in case it needs it */
	for (count = 0; address[count]; count++) {	/* for 2 BARs */
		pci_bus_write_config_dword(ibmphp_pci_bus, devfn, address[count], 0xFFFFFFFF);
		pci_bus_read_config_dword(ibmphp_pci_bus, devfn, address[count], &bar[count]);

		if (!bar[count]) {
			/* This BAR is not implemented */
			debug("so we come here then, eh?, count = %d\n", count);
			continue;
		}
		//  tmp_bar = bar[count];

		debug("Bar %d wants %x\n", count, bar[count]);

		if (bar[count] & PCI_BASE_ADDRESS_SPACE_IO) {
			/* This is IO */
			len[count] = bar[count] & 0xFFFFFFFC;
			len[count] = ~len[count] + 1;

			debug("len[count] in IO = %x\n", len[count]);

			bus_io[count] = kzalloc(sizeof(struct resource_node), GFP_KERNEL);

			if (!bus_io[count]) {
				retval = -ENOMEM;
				goto error;
			}
			bus_io[count]->type = IO;
			bus_io[count]->busno = func->busno;
			bus_io[count]->devfunc = PCI_DEVFN(func->device,
							func->function);
			bus_io[count]->len = len[count];
			if (ibmphp_check_resource(bus_io[count], 0) == 0) {
				ibmphp_add_resource(bus_io[count]);
				func->io[count] = bus_io[count];
			} else {
				err("cannot allocate requested io for bus %x, device %x, len %x\n",
				     func->busno, func->device, len[count]);
				kfree(bus_io[count]);
				return -EIO;
			}

			pci_bus_write_config_dword(ibmphp_pci_bus, devfn, address[count], func->io[count]->start);

		} else {
			/* This is Memory */
			if (bar[count] & PCI_BASE_ADDRESS_MEM_PREFETCH) {
				/* pfmem */
				len[count] = bar[count] & 0xFFFFFFF0;
				len[count] = ~len[count] + 1;

				debug("len[count] in PFMEM = %x\n", len[count]);

				bus_pfmem[count] = kzalloc(sizeof(struct resource_node), GFP_KERNEL);
				if (!bus_pfmem[count]) {
					retval = -ENOMEM;
					goto error;
				}
				bus_pfmem[count]->type = PFMEM;
				bus_pfmem[count]->busno = func->busno;
				bus_pfmem[count]->devfunc = PCI_DEVFN(func->device,
							func->function);
				bus_pfmem[count]->len = len[count];
				bus_pfmem[count]->fromMem = 0;
				if (ibmphp_check_resource(bus_pfmem[count], 0) == 0) {
					ibmphp_add_resource(bus_pfmem[count]);
					func->pfmem[count] = bus_pfmem[count];
				} else {
					mem_tmp = kzalloc(sizeof(*mem_tmp), GFP_KERNEL);
					if (!mem_tmp) {
						retval = -ENOMEM;
						goto error;
					}
					mem_tmp->type = MEM;
					mem_tmp->busno = bus_pfmem[count]->busno;
					mem_tmp->devfunc = bus_pfmem[count]->devfunc;
					mem_tmp->len = bus_pfmem[count]->len;
					if (ibmphp_check_resource(mem_tmp, 0) == 0) {
						ibmphp_add_resource(mem_tmp);
						bus_pfmem[count]->fromMem = 1;
						bus_pfmem[count]->rangeno = mem_tmp->rangeno;
						ibmphp_add_pfmem_from_mem(bus_pfmem[count]);
						func->pfmem[count] = bus_pfmem[count];
					} else {
						err("cannot allocate requested pfmem for bus %x, device %x, len %x\n",
						     func->busno, func->device, len[count]);
						kfree(mem_tmp);
						kfree(bus_pfmem[count]);
						return -EIO;
					}
				}

				pci_bus_write_config_dword(ibmphp_pci_bus, devfn, address[count], func->pfmem[count]->start);

				if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) {
					/* takes up another dword */
					count += 1;
					/* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */
					pci_bus_write_config_dword(ibmphp_pci_bus, devfn, address[count], 0x00000000);

				}
			} else {
				/* regular memory */
				len[count] = bar[count] & 0xFFFFFFF0;
				len[count] = ~len[count] + 1;

				debug("len[count] in Memory is %x\n", len[count]);

				bus_mem[count] = kzalloc(sizeof(struct resource_node), GFP_KERNEL);
				if (!bus_mem[count]) {
					retval = -ENOMEM;
					goto error;
				}
				bus_mem[count]->type = MEM;
				bus_mem[count]->busno = func->busno;
				bus_mem[count]->devfunc = PCI_DEVFN(func->device,
							func->function);
				bus_mem[count]->len = len[count];
				if (ibmphp_check_resource(bus_mem[count], 0) == 0) {
					ibmphp_add_resource(bus_mem[count]);
					func->mem[count] = bus_mem[count];
				} else {
					err("cannot allocate requested mem for bus %x, device %x, len %x\n",
					     func->busno, func->device, len[count]);
					kfree(bus_mem[count]);
					return -EIO;
				}

				pci_bus_write_config_dword(ibmphp_pci_bus, devfn, address[count], func->mem[count]->start);

				if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) {
					/* takes up another dword */
					count += 1;
					/* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */
					pci_bus_write_config_dword(ibmphp_pci_bus, devfn, address[count], 0x00000000);

				}
			}
		}		/* end of mem */
	}			/* end of for  */

	/* Now need to see how much space the devices behind the bridge needed */
	amount_needed = scan_behind_bridge(func, sec_number);
	if (amount_needed == NULL)
		return -ENOMEM;

	ibmphp_pci_bus->number = func->busno;
	debug("after coming back from scan_behind_bridge\n");
	debug("amount_needed->not_correct = %x\n", amount_needed->not_correct);
	debug("amount_needed->io = %x\n", amount_needed->io);
	debug("amount_needed->mem = %x\n", amount_needed->mem);
	debug("amount_needed->pfmem =  %x\n", amount_needed->pfmem);

	if (amount_needed->not_correct) {
		debug("amount_needed is not correct\n");
		for (count = 0; address[count]; count++) {
			/* for 2 BARs */
			if (bus_io[count]) {
				ibmphp_remove_resource(bus_io[count]);
				func->io[count] = NULL;
			} else if (bus_pfmem[count]) {
				ibmphp_remove_resource(bus_pfmem[count]);
				func->pfmem[count] = NULL;
			} else if (bus_mem[count]) {
				ibmphp_remove_resource(bus_mem[count]);
				func->mem[count] = NULL;
			}
		}
		kfree(amount_needed);
		return -ENODEV;
	}

	if (!amount_needed->io) {
		debug("it doesn't want IO?\n");
		flag_io = 1;
	} else {
		debug("it wants %x IO behind the bridge\n", amount_needed->io);
		io = kzalloc(sizeof(*io), GFP_KERNEL);

		if (!io) {
			retval = -ENOMEM;
			goto error;
		}
		io->type = IO;
		io->busno = func->busno;
		io->devfunc = PCI_DEVFN(func->device, func->function);
		io->len = amount_needed->io;
		if (ibmphp_check_resource(io, 1) == 0) {
			debug("were we able to add io\n");
			ibmphp_add_resource(io);
			flag_io = 1;
		}
	}

	if (!amount_needed->mem) {
		debug("it doesn't want n.e.memory?\n");
		flag_mem = 1;
	} else {
		debug("it wants %x memory behind the bridge\n", amount_needed->mem);
		mem = kzalloc(sizeof(*mem), GFP_KERNEL);
		if (!mem) {
			retval = -ENOMEM;
			goto error;
		}
		mem->type = MEM;
		mem->busno = func->busno;
		mem->devfunc = PCI_DEVFN(func->device, func->function);
		mem->len = amount_needed->mem;
		if (ibmphp_check_resource(mem, 1) == 0) {
			ibmphp_add_resource(mem);
			flag_mem = 1;
			debug("were we able to add mem\n");
		}
	}

	if (!amount_needed->pfmem) {
		debug("it doesn't want n.e.pfmem mem?\n");
		flag_pfmem = 1;
	} else {
		debug("it wants %x pfmemory behind the bridge\n", amount_needed->pfmem);
		pfmem = kzalloc(sizeof(*pfmem), GFP_KERNEL);
		if (!pfmem) {
			retval = -ENOMEM;
			goto error;
		}
		pfmem->type = PFMEM;
		pfmem->busno = func->busno;
		pfmem->devfunc = PCI_DEVFN(func->device, func->function);
		pfmem->len = amount_needed->pfmem;
		pfmem->fromMem = 0;
		if (ibmphp_check_resource(pfmem, 1) == 0) {
			ibmphp_add_resource(pfmem);
			flag_pfmem = 1;
		} else {
			mem_tmp = kzalloc(sizeof(*mem_tmp), GFP_KERNEL);
			if (!mem_tmp) {
				retval = -ENOMEM;
				goto error;
			}
			mem_tmp->type = MEM;
			mem_tmp->busno = pfmem->busno;
			mem_tmp->devfunc = pfmem->devfunc;
			mem_tmp->len = pfmem->len;
			if (ibmphp_check_resource(mem_tmp, 1) == 0) {
				ibmphp_add_resource(mem_tmp);
				pfmem->fromMem = 1;
				pfmem->rangeno = mem_tmp->rangeno;
				ibmphp_add_pfmem_from_mem(pfmem);
				flag_pfmem = 1;
			}
		}
	}

	debug("b4 if (flag_io && flag_mem && flag_pfmem)\n");
	debug("flag_io = %x, flag_mem = %x, flag_pfmem = %x\n", flag_io, flag_mem, flag_pfmem);

	if (flag_io && flag_mem && flag_pfmem) {
		/* If on bootup, there was a bridged card in this slot,
		 * then card was removed and ibmphp got unloaded and loaded
		 * back again, there's no way for us to remove the bus
		 * struct, so no need to kmalloc, can use existing node
		 */
		bus = ibmphp_find_res_bus(sec_number);
		if (!bus) {
			bus = kzalloc(sizeof(*bus), GFP_KERNEL);
			if (!bus) {
				retval = -ENOMEM;
				goto error;
			}
			bus->busno = sec_number;
			debug("b4 adding new bus\n");
			rc = add_new_bus(bus, io, mem, pfmem, func->busno);
		} else if (!(bus->rangeIO) && !(bus->rangeMem) && !(bus->rangePFMem))
			rc = add_new_bus(bus, io, mem, pfmem, 0xFF);
		else {
			err("expected bus structure not empty?\n");
			retval = -EIO;
			goto error;
		}
		if (rc) {
			if (rc == -ENOMEM) {
				ibmphp_remove_bus(bus, func->busno);
				kfree(amount_needed);
				return rc;
			}
			retval = rc;
			goto error;
		}
		pci_bus_read_config_byte(ibmphp_pci_bus, devfn, PCI_IO_BASE, &io_base);
		pci_bus_read_config_word(ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, &pfmem_base);

		if ((io_base & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) {
			debug("io 32\n");
			need_io_upper = 1;
		}
		if ((pfmem_base & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) {
			debug("pfmem 64\n");
			need_pfmem_upper = 1;
		}

		if (bus->noIORanges) {
			pci_bus_write_config_byte(ibmphp_pci_bus, devfn, PCI_IO_BASE, 0x00 | bus->rangeIO->start >> 8);
			pci_bus_write_config_byte(ibmphp_pci_bus, devfn, PCI_IO_LIMIT, 0x00 | bus->rangeIO->end >> 8);

			/* _______________This is for debugging purposes only ____________________
			pci_bus_read_config_byte(ibmphp_pci_bus, devfn, PCI_IO_BASE, &temp);
			debug("io_base = %x\n", (temp & PCI_IO_RANGE_TYPE_MASK) << 8);
			pci_bus_read_config_byte(ibmphp_pci_bus, devfn, PCI_IO_LIMIT, &temp);
			debug("io_limit = %x\n", (temp & PCI_IO_RANGE_TYPE_MASK) << 8);
			 ________________________________________________________________________*/

			if (need_io_upper) {	/* since can't support n.e.ways */
				pci_bus_write_config_word(ibmphp_pci_bus, devfn, PCI_IO_BASE_UPPER16, 0x0000);
				pci_bus_write_config_word(ibmphp_pci_bus, devfn, PCI_IO_LIMIT_UPPER16, 0x0000);
			}
		} else {
			pci_bus_write_config_byte(ibmphp_pci_bus, devfn, PCI_IO_BASE, 0x00);
			pci_bus_write_config_byte(ibmphp_pci_bus, devfn, PCI_IO_LIMIT, 0x00);
		}

		if (bus->noMemRanges) {
			pci_bus_write_config_word(ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, 0x0000 | bus->rangeMem->start >> 16);
			pci_bus_write_config_word(ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, 0x0000 | bus->rangeMem->end >> 16);

			/* ____________________This is for debugging purposes only ________________________
			pci_bus_read_config_word(ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, &temp);
			debug("mem_base = %x\n", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16);
			pci_bus_read_config_word(ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, &temp);
			debug("mem_limit = %x\n", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16);
			 __________________________________________________________________________________*/

		} else {
			pci_bus_write_config_word(ibmphp_pci_bus, devfn, PCI_MEMORY_BASE, 0xffff);
			pci_bus_write_config_word(ibmphp_pci_bus, devfn, PCI_MEMORY_LIMIT, 0x0000);
		}
		if (bus->noPFMemRanges) {
			pci_bus_write_config_word(ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, 0x0000 | bus->rangePFMem->start >> 16);
			pci_bus_write_config_word(ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, 0x0000 | bus->rangePFMem->end >> 16);

			/* __________________________This is for debugging purposes only _______________________
			pci_bus_read_config_word(ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, &temp);
			debug("pfmem_base = %x", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16);
			pci_bus_read_config_word(ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &temp);
			debug("pfmem_limit = %x\n", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16);
			 ______________________________________________________________________________________*/

			if (need_pfmem_upper) {	/* since can't support n.e.ways */
				pci_bus_write_config_dword(ibmphp_pci_bus, devfn, PCI_PREF_BASE_UPPER32, 0x00000000);
				pci_bus_write_config_dword(ibmphp_pci_bus, devfn, PCI_PREF_LIMIT_UPPER32, 0x00000000);
			}
		} else {
			pci_bus_write_config_word(ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_BASE, 0xffff);
			pci_bus_write_config_word(ibmphp_pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, 0x0000);
		}

		debug("b4 writing control information\n");

		pci_bus_read_config_byte(ibmphp_pci_bus, devfn, PCI_INTERRUPT_PIN, &irq);
		if ((irq > 0x00) && (irq < 0x05))
			pci_bus_write_config_byte(ibmphp_pci_bus, devfn, PCI_INTERRUPT_LINE, func->irq[irq - 1]);
		/*
		pci_bus_write_config_byte(ibmphp_pci_bus, devfn, PCI_BRIDGE_CONTROL, ctrl);
		pci_bus_write_config_byte(ibmphp_pci_bus, devfn, PCI_BRIDGE_CONTROL, PCI_BRIDGE_CTL_PARITY);
		pci_bus_write_config_byte(ibmphp_pci_bus, devfn, PCI_BRIDGE_CONTROL, PCI_BRIDGE_CTL_SERR);
		 */

		pci_bus_write_config_word(ibmphp_pci_bus, devfn, PCI_COMMAND, DEVICEENABLE);
		pci_bus_write_config_word(ibmphp_pci_bus, devfn, PCI_BRIDGE_CONTROL, 0x07);
		for (i = 0; i < 32; i++) {
			if (amount_needed->devices[i]) {
				debug("device where devices[i] is 1 = %x\n", i);
				func->devices[i] = 1;
			}
		}
		func->bus = 1;	/* For unconfiguring, to indicate it's PPB */
		func_passed = &func;
		debug("func->busno b4 returning is %x\n", func->busno);
		debug("func->busno b4 returning in the other structure is %x\n", (*func_passed)->busno);
		kfree(amount_needed);
		return 0;
	} else {
		err("Configuring bridge was unsuccessful...\n");
		mem_tmp = NULL;
		retval = -EIO;
		goto error;
	}

error:
	kfree(amount_needed);
	if (pfmem)
		ibmphp_remove_resource(pfmem);
	if (io)
		ibmphp_remove_resource(io);
	if (mem)
		ibmphp_remove_resource(mem);
	for (i = 0; i < 2; i++) {	/* for 2 BARs */
		if (bus_io[i]) {
			ibmphp_remove_resource(bus_io[i]);
			func->io[i] = NULL;
		} else if (bus_pfmem[i]) {
			ibmphp_remove_resource(bus_pfmem[i]);
			func->pfmem[i] = NULL;
		} else if (bus_mem[i]) {
			ibmphp_remove_resource(bus_mem[i]);
			func->mem[i] = NULL;
		}
	}
	return retval;
}