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