forked from Minki/linux
c4ec84c7db
Multiple race conditions are possible between PCI hotplug and the generic PCI bus rescan and device removal that can be triggered via sysfs. To avoid those race conditions make PCI hotplug use global PCI rescan-remove locking. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
1565 lines
39 KiB
C
1565 lines
39 KiB
C
/*
|
|
* Compaq Hot Plug Controller Driver
|
|
*
|
|
* Copyright (C) 1995,2001 Compaq Computer Corporation
|
|
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
|
|
* Copyright (C) 2001 IBM Corp.
|
|
*
|
|
* All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or (at
|
|
* your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
|
|
* NON INFRINGEMENT. See the GNU General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
* Send feedback to <greg@kroah.com>
|
|
*
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/types.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/workqueue.h>
|
|
#include <linux/proc_fs.h>
|
|
#include <linux/pci.h>
|
|
#include <linux/pci_hotplug.h>
|
|
#include "../pci.h"
|
|
#include "cpqphp.h"
|
|
#include "cpqphp_nvram.h"
|
|
|
|
|
|
u8 cpqhp_nic_irq;
|
|
u8 cpqhp_disk_irq;
|
|
|
|
static u16 unused_IRQ;
|
|
|
|
/*
|
|
* detect_HRT_floating_pointer
|
|
*
|
|
* find the Hot Plug Resource Table in the specified region of memory.
|
|
*
|
|
*/
|
|
static void __iomem *detect_HRT_floating_pointer(void __iomem *begin, void __iomem *end)
|
|
{
|
|
void __iomem *fp;
|
|
void __iomem *endp;
|
|
u8 temp1, temp2, temp3, temp4;
|
|
int status = 0;
|
|
|
|
endp = (end - sizeof(struct hrt) + 1);
|
|
|
|
for (fp = begin; fp <= endp; fp += 16) {
|
|
temp1 = readb(fp + SIG0);
|
|
temp2 = readb(fp + SIG1);
|
|
temp3 = readb(fp + SIG2);
|
|
temp4 = readb(fp + SIG3);
|
|
if (temp1 == '$' &&
|
|
temp2 == 'H' &&
|
|
temp3 == 'R' &&
|
|
temp4 == 'T') {
|
|
status = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!status)
|
|
fp = NULL;
|
|
|
|
dbg("Discovered Hotplug Resource Table at %p\n", fp);
|
|
return fp;
|
|
}
|
|
|
|
|
|
int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func)
|
|
{
|
|
struct pci_bus *child;
|
|
int num;
|
|
|
|
pci_lock_rescan_remove();
|
|
|
|
if (func->pci_dev == NULL)
|
|
func->pci_dev = pci_get_bus_and_slot(func->bus,PCI_DEVFN(func->device, func->function));
|
|
|
|
/* No pci device, we need to create it then */
|
|
if (func->pci_dev == NULL) {
|
|
dbg("INFO: pci_dev still null\n");
|
|
|
|
num = pci_scan_slot(ctrl->pci_dev->bus, PCI_DEVFN(func->device, func->function));
|
|
if (num)
|
|
pci_bus_add_devices(ctrl->pci_dev->bus);
|
|
|
|
func->pci_dev = pci_get_bus_and_slot(func->bus, PCI_DEVFN(func->device, func->function));
|
|
if (func->pci_dev == NULL) {
|
|
dbg("ERROR: pci_dev still null\n");
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
if (func->pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
|
|
pci_hp_add_bridge(func->pci_dev);
|
|
child = func->pci_dev->subordinate;
|
|
if (child)
|
|
pci_bus_add_devices(child);
|
|
}
|
|
|
|
pci_dev_put(func->pci_dev);
|
|
|
|
out:
|
|
pci_unlock_rescan_remove();
|
|
return 0;
|
|
}
|
|
|
|
|
|
int cpqhp_unconfigure_device(struct pci_func* func)
|
|
{
|
|
int j;
|
|
|
|
dbg("%s: bus/dev/func = %x/%x/%x\n", __func__, func->bus, func->device, func->function);
|
|
|
|
pci_lock_rescan_remove();
|
|
for (j=0; j<8 ; j++) {
|
|
struct pci_dev* temp = pci_get_bus_and_slot(func->bus, PCI_DEVFN(func->device, j));
|
|
if (temp) {
|
|
pci_dev_put(temp);
|
|
pci_stop_and_remove_bus_device(temp);
|
|
}
|
|
}
|
|
pci_unlock_rescan_remove();
|
|
return 0;
|
|
}
|
|
|
|
static int PCI_RefinedAccessConfig(struct pci_bus *bus, unsigned int devfn, u8 offset, u32 *value)
|
|
{
|
|
u32 vendID = 0;
|
|
|
|
if (pci_bus_read_config_dword (bus, devfn, PCI_VENDOR_ID, &vendID) == -1)
|
|
return -1;
|
|
if (vendID == 0xffffffff)
|
|
return -1;
|
|
return pci_bus_read_config_dword (bus, devfn, offset, value);
|
|
}
|
|
|
|
|
|
/*
|
|
* cpqhp_set_irq
|
|
*
|
|
* @bus_num: bus number of PCI device
|
|
* @dev_num: device number of PCI device
|
|
* @slot: pointer to u8 where slot number will be returned
|
|
*/
|
|
int cpqhp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num)
|
|
{
|
|
int rc = 0;
|
|
|
|
if (cpqhp_legacy_mode) {
|
|
struct pci_dev *fakedev;
|
|
struct pci_bus *fakebus;
|
|
u16 temp_word;
|
|
|
|
fakedev = kmalloc(sizeof(*fakedev), GFP_KERNEL);
|
|
fakebus = kmalloc(sizeof(*fakebus), GFP_KERNEL);
|
|
if (!fakedev || !fakebus) {
|
|
kfree(fakedev);
|
|
kfree(fakebus);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
fakedev->devfn = dev_num << 3;
|
|
fakedev->bus = fakebus;
|
|
fakebus->number = bus_num;
|
|
dbg("%s: dev %d, bus %d, pin %d, num %d\n",
|
|
__func__, dev_num, bus_num, int_pin, irq_num);
|
|
rc = pcibios_set_irq_routing(fakedev, int_pin - 1, irq_num);
|
|
kfree(fakedev);
|
|
kfree(fakebus);
|
|
dbg("%s: rc %d\n", __func__, rc);
|
|
if (!rc)
|
|
return !rc;
|
|
|
|
/* set the Edge Level Control Register (ELCR) */
|
|
temp_word = inb(0x4d0);
|
|
temp_word |= inb(0x4d1) << 8;
|
|
|
|
temp_word |= 0x01 << irq_num;
|
|
|
|
/* This should only be for x86 as it sets the Edge Level
|
|
* Control Register
|
|
*/
|
|
outb((u8) (temp_word & 0xFF), 0x4d0); outb((u8) ((temp_word &
|
|
0xFF00) >> 8), 0x4d1); rc = 0; }
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
static int PCI_ScanBusForNonBridge(struct controller *ctrl, u8 bus_num, u8 * dev_num)
|
|
{
|
|
u16 tdevice;
|
|
u32 work;
|
|
u8 tbus;
|
|
|
|
ctrl->pci_bus->number = bus_num;
|
|
|
|
for (tdevice = 0; tdevice < 0xFF; tdevice++) {
|
|
/* Scan for access first */
|
|
if (PCI_RefinedAccessConfig(ctrl->pci_bus, tdevice, 0x08, &work) == -1)
|
|
continue;
|
|
dbg("Looking for nonbridge bus_num %d dev_num %d\n", bus_num, tdevice);
|
|
/* Yep we got one. Not a bridge ? */
|
|
if ((work >> 8) != PCI_TO_PCI_BRIDGE_CLASS) {
|
|
*dev_num = tdevice;
|
|
dbg("found it !\n");
|
|
return 0;
|
|
}
|
|
}
|
|
for (tdevice = 0; tdevice < 0xFF; tdevice++) {
|
|
/* Scan for access first */
|
|
if (PCI_RefinedAccessConfig(ctrl->pci_bus, tdevice, 0x08, &work) == -1)
|
|
continue;
|
|
dbg("Looking for bridge bus_num %d dev_num %d\n", bus_num, tdevice);
|
|
/* Yep we got one. bridge ? */
|
|
if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) {
|
|
pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(tdevice, 0), PCI_SECONDARY_BUS, &tbus);
|
|
/* XXX: no recursion, wtf? */
|
|
dbg("Recurse on bus_num %d tdevice %d\n", tbus, tdevice);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
static int PCI_GetBusDevHelper(struct controller *ctrl, u8 *bus_num, u8 *dev_num, u8 slot, u8 nobridge)
|
|
{
|
|
int loop, len;
|
|
u32 work;
|
|
u8 tbus, tdevice, tslot;
|
|
|
|
len = cpqhp_routing_table_length();
|
|
for (loop = 0; loop < len; ++loop) {
|
|
tbus = cpqhp_routing_table->slots[loop].bus;
|
|
tdevice = cpqhp_routing_table->slots[loop].devfn;
|
|
tslot = cpqhp_routing_table->slots[loop].slot;
|
|
|
|
if (tslot == slot) {
|
|
*bus_num = tbus;
|
|
*dev_num = tdevice;
|
|
ctrl->pci_bus->number = tbus;
|
|
pci_bus_read_config_dword (ctrl->pci_bus, *dev_num, PCI_VENDOR_ID, &work);
|
|
if (!nobridge || (work == 0xffffffff))
|
|
return 0;
|
|
|
|
dbg("bus_num %d devfn %d\n", *bus_num, *dev_num);
|
|
pci_bus_read_config_dword (ctrl->pci_bus, *dev_num, PCI_CLASS_REVISION, &work);
|
|
dbg("work >> 8 (%x) = BRIDGE (%x)\n", work >> 8, PCI_TO_PCI_BRIDGE_CLASS);
|
|
|
|
if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) {
|
|
pci_bus_read_config_byte (ctrl->pci_bus, *dev_num, PCI_SECONDARY_BUS, &tbus);
|
|
dbg("Scan bus for Non Bridge: bus %d\n", tbus);
|
|
if (PCI_ScanBusForNonBridge(ctrl, tbus, dev_num) == 0) {
|
|
*bus_num = tbus;
|
|
return 0;
|
|
}
|
|
} else
|
|
return 0;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
int cpqhp_get_bus_dev (struct controller *ctrl, u8 * bus_num, u8 * dev_num, u8 slot)
|
|
{
|
|
/* plain (bridges allowed) */
|
|
return PCI_GetBusDevHelper(ctrl, bus_num, dev_num, slot, 0);
|
|
}
|
|
|
|
|
|
/* More PCI configuration routines; this time centered around hotplug
|
|
* controller
|
|
*/
|
|
|
|
|
|
/*
|
|
* cpqhp_save_config
|
|
*
|
|
* Reads configuration for all slots in a PCI bus and saves info.
|
|
*
|
|
* Note: For non-hot plug buses, the slot # saved is the device #
|
|
*
|
|
* returns 0 if success
|
|
*/
|
|
int cpqhp_save_config(struct controller *ctrl, int busnumber, int is_hot_plug)
|
|
{
|
|
long rc;
|
|
u8 class_code;
|
|
u8 header_type;
|
|
u32 ID;
|
|
u8 secondary_bus;
|
|
struct pci_func *new_slot;
|
|
int sub_bus;
|
|
int FirstSupported;
|
|
int LastSupported;
|
|
int max_functions;
|
|
int function;
|
|
u8 DevError;
|
|
int device = 0;
|
|
int cloop = 0;
|
|
int stop_it;
|
|
int index;
|
|
|
|
/* Decide which slots are supported */
|
|
|
|
if (is_hot_plug) {
|
|
/*
|
|
* is_hot_plug is the slot mask
|
|
*/
|
|
FirstSupported = is_hot_plug >> 4;
|
|
LastSupported = FirstSupported + (is_hot_plug & 0x0F) - 1;
|
|
} else {
|
|
FirstSupported = 0;
|
|
LastSupported = 0x1F;
|
|
}
|
|
|
|
/* Save PCI configuration space for all devices in supported slots */
|
|
ctrl->pci_bus->number = busnumber;
|
|
for (device = FirstSupported; device <= LastSupported; device++) {
|
|
ID = 0xFFFFFFFF;
|
|
rc = pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(device, 0), PCI_VENDOR_ID, &ID);
|
|
|
|
if (ID == 0xFFFFFFFF) {
|
|
if (is_hot_plug) {
|
|
/* Setup slot structure with entry for empty
|
|
* slot
|
|
*/
|
|
new_slot = cpqhp_slot_create(busnumber);
|
|
if (new_slot == NULL)
|
|
return 1;
|
|
|
|
new_slot->bus = (u8) busnumber;
|
|
new_slot->device = (u8) device;
|
|
new_slot->function = 0;
|
|
new_slot->is_a_board = 0;
|
|
new_slot->presence_save = 0;
|
|
new_slot->switch_save = 0;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, 0), 0x0B, &class_code);
|
|
if (rc)
|
|
return rc;
|
|
|
|
rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, 0), PCI_HEADER_TYPE, &header_type);
|
|
if (rc)
|
|
return rc;
|
|
|
|
/* If multi-function device, set max_functions to 8 */
|
|
if (header_type & 0x80)
|
|
max_functions = 8;
|
|
else
|
|
max_functions = 1;
|
|
|
|
function = 0;
|
|
|
|
do {
|
|
DevError = 0;
|
|
if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
|
|
/* Recurse the subordinate bus
|
|
* get the subordinate bus number
|
|
*/
|
|
rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, function), PCI_SECONDARY_BUS, &secondary_bus);
|
|
if (rc) {
|
|
return rc;
|
|
} else {
|
|
sub_bus = (int) secondary_bus;
|
|
|
|
/* Save secondary bus cfg spc
|
|
* with this recursive call.
|
|
*/
|
|
rc = cpqhp_save_config(ctrl, sub_bus, 0);
|
|
if (rc)
|
|
return rc;
|
|
ctrl->pci_bus->number = busnumber;
|
|
}
|
|
}
|
|
|
|
index = 0;
|
|
new_slot = cpqhp_slot_find(busnumber, device, index++);
|
|
while (new_slot &&
|
|
(new_slot->function != (u8) function))
|
|
new_slot = cpqhp_slot_find(busnumber, device, index++);
|
|
|
|
if (!new_slot) {
|
|
/* Setup slot structure. */
|
|
new_slot = cpqhp_slot_create(busnumber);
|
|
if (new_slot == NULL)
|
|
return 1;
|
|
}
|
|
|
|
new_slot->bus = (u8) busnumber;
|
|
new_slot->device = (u8) device;
|
|
new_slot->function = (u8) function;
|
|
new_slot->is_a_board = 1;
|
|
new_slot->switch_save = 0x10;
|
|
/* In case of unsupported board */
|
|
new_slot->status = DevError;
|
|
new_slot->pci_dev = pci_get_bus_and_slot(new_slot->bus, (new_slot->device << 3) | new_slot->function);
|
|
|
|
for (cloop = 0; cloop < 0x20; cloop++) {
|
|
rc = pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(device, function), cloop << 2, (u32 *) & (new_slot-> config_space [cloop]));
|
|
if (rc)
|
|
return rc;
|
|
}
|
|
|
|
pci_dev_put(new_slot->pci_dev);
|
|
|
|
function++;
|
|
|
|
stop_it = 0;
|
|
|
|
/* this loop skips to the next present function
|
|
* reading in Class Code and Header type.
|
|
*/
|
|
while ((function < max_functions) && (!stop_it)) {
|
|
rc = pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(device, function), PCI_VENDOR_ID, &ID);
|
|
if (ID == 0xFFFFFFFF) {
|
|
function++;
|
|
continue;
|
|
}
|
|
rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, function), 0x0B, &class_code);
|
|
if (rc)
|
|
return rc;
|
|
|
|
rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, function), PCI_HEADER_TYPE, &header_type);
|
|
if (rc)
|
|
return rc;
|
|
|
|
stop_it++;
|
|
}
|
|
|
|
} while (function < max_functions);
|
|
} /* End of FOR loop */
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* cpqhp_save_slot_config
|
|
*
|
|
* Saves configuration info for all PCI devices in a given slot
|
|
* including subordinate buses.
|
|
*
|
|
* returns 0 if success
|
|
*/
|
|
int cpqhp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot)
|
|
{
|
|
long rc;
|
|
u8 class_code;
|
|
u8 header_type;
|
|
u32 ID;
|
|
u8 secondary_bus;
|
|
int sub_bus;
|
|
int max_functions;
|
|
int function = 0;
|
|
int cloop = 0;
|
|
int stop_it;
|
|
|
|
ID = 0xFFFFFFFF;
|
|
|
|
ctrl->pci_bus->number = new_slot->bus;
|
|
pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_VENDOR_ID, &ID);
|
|
|
|
if (ID == 0xFFFFFFFF)
|
|
return 2;
|
|
|
|
pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), 0x0B, &class_code);
|
|
pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_HEADER_TYPE, &header_type);
|
|
|
|
if (header_type & 0x80) /* Multi-function device */
|
|
max_functions = 8;
|
|
else
|
|
max_functions = 1;
|
|
|
|
while (function < max_functions) {
|
|
if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
|
|
/* Recurse the subordinate bus */
|
|
pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_SECONDARY_BUS, &secondary_bus);
|
|
|
|
sub_bus = (int) secondary_bus;
|
|
|
|
/* Save the config headers for the secondary
|
|
* bus.
|
|
*/
|
|
rc = cpqhp_save_config(ctrl, sub_bus, 0);
|
|
if (rc)
|
|
return(rc);
|
|
ctrl->pci_bus->number = new_slot->bus;
|
|
|
|
}
|
|
|
|
new_slot->status = 0;
|
|
|
|
for (cloop = 0; cloop < 0x20; cloop++)
|
|
pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), cloop << 2, (u32 *) & (new_slot-> config_space [cloop]));
|
|
|
|
function++;
|
|
|
|
stop_it = 0;
|
|
|
|
/* this loop skips to the next present function
|
|
* reading in the Class Code and the Header type.
|
|
*/
|
|
while ((function < max_functions) && (!stop_it)) {
|
|
pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_VENDOR_ID, &ID);
|
|
|
|
if (ID == 0xFFFFFFFF)
|
|
function++;
|
|
else {
|
|
pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), 0x0B, &class_code);
|
|
pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_HEADER_TYPE, &header_type);
|
|
stop_it++;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* cpqhp_save_base_addr_length
|
|
*
|
|
* Saves the length of all base address registers for the
|
|
* specified slot. this is for hot plug REPLACE
|
|
*
|
|
* returns 0 if success
|
|
*/
|
|
int cpqhp_save_base_addr_length(struct controller *ctrl, struct pci_func * func)
|
|
{
|
|
u8 cloop;
|
|
u8 header_type;
|
|
u8 secondary_bus;
|
|
u8 type;
|
|
int sub_bus;
|
|
u32 temp_register;
|
|
u32 base;
|
|
u32 rc;
|
|
struct pci_func *next;
|
|
int index = 0;
|
|
struct pci_bus *pci_bus = ctrl->pci_bus;
|
|
unsigned int devfn;
|
|
|
|
func = cpqhp_slot_find(func->bus, func->device, index++);
|
|
|
|
while (func != NULL) {
|
|
pci_bus->number = func->bus;
|
|
devfn = PCI_DEVFN(func->device, func->function);
|
|
|
|
/* Check for Bridge */
|
|
pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
|
|
|
|
if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
|
|
pci_bus_read_config_byte (pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus);
|
|
|
|
sub_bus = (int) secondary_bus;
|
|
|
|
next = cpqhp_slot_list[sub_bus];
|
|
|
|
while (next != NULL) {
|
|
rc = cpqhp_save_base_addr_length(ctrl, next);
|
|
if (rc)
|
|
return rc;
|
|
|
|
next = next->next;
|
|
}
|
|
pci_bus->number = func->bus;
|
|
|
|
/* FIXME: this loop is duplicated in the non-bridge
|
|
* case. The two could be rolled together Figure out
|
|
* IO and memory base lengths
|
|
*/
|
|
for (cloop = 0x10; cloop <= 0x14; cloop += 4) {
|
|
temp_register = 0xFFFFFFFF;
|
|
pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register);
|
|
pci_bus_read_config_dword (pci_bus, devfn, cloop, &base);
|
|
/* If this register is implemented */
|
|
if (base) {
|
|
if (base & 0x01L) {
|
|
/* IO base
|
|
* set base = amount of IO space
|
|
* requested
|
|
*/
|
|
base = base & 0xFFFFFFFE;
|
|
base = (~base) + 1;
|
|
|
|
type = 1;
|
|
} else {
|
|
/* memory base */
|
|
base = base & 0xFFFFFFF0;
|
|
base = (~base) + 1;
|
|
|
|
type = 0;
|
|
}
|
|
} else {
|
|
base = 0x0L;
|
|
type = 0;
|
|
}
|
|
|
|
/* Save information in slot structure */
|
|
func->base_length[(cloop - 0x10) >> 2] =
|
|
base;
|
|
func->base_type[(cloop - 0x10) >> 2] = type;
|
|
|
|
} /* End of base register loop */
|
|
|
|
} else if ((header_type & 0x7F) == 0x00) {
|
|
/* Figure out IO and memory base lengths */
|
|
for (cloop = 0x10; cloop <= 0x24; cloop += 4) {
|
|
temp_register = 0xFFFFFFFF;
|
|
pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register);
|
|
pci_bus_read_config_dword (pci_bus, devfn, cloop, &base);
|
|
|
|
/* If this register is implemented */
|
|
if (base) {
|
|
if (base & 0x01L) {
|
|
/* IO base
|
|
* base = amount of IO space
|
|
* requested
|
|
*/
|
|
base = base & 0xFFFFFFFE;
|
|
base = (~base) + 1;
|
|
|
|
type = 1;
|
|
} else {
|
|
/* memory base
|
|
* base = amount of memory
|
|
* space requested
|
|
*/
|
|
base = base & 0xFFFFFFF0;
|
|
base = (~base) + 1;
|
|
|
|
type = 0;
|
|
}
|
|
} else {
|
|
base = 0x0L;
|
|
type = 0;
|
|
}
|
|
|
|
/* Save information in slot structure */
|
|
func->base_length[(cloop - 0x10) >> 2] = base;
|
|
func->base_type[(cloop - 0x10) >> 2] = type;
|
|
|
|
} /* End of base register loop */
|
|
|
|
} else { /* Some other unknown header type */
|
|
}
|
|
|
|
/* find the next device in this slot */
|
|
func = cpqhp_slot_find(func->bus, func->device, index++);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
/*
|
|
* cpqhp_save_used_resources
|
|
*
|
|
* Stores used resource information for existing boards. this is
|
|
* for boards that were in the system when this driver was loaded.
|
|
* this function is for hot plug ADD
|
|
*
|
|
* returns 0 if success
|
|
*/
|
|
int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func)
|
|
{
|
|
u8 cloop;
|
|
u8 header_type;
|
|
u8 secondary_bus;
|
|
u8 temp_byte;
|
|
u8 b_base;
|
|
u8 b_length;
|
|
u16 command;
|
|
u16 save_command;
|
|
u16 w_base;
|
|
u16 w_length;
|
|
u32 temp_register;
|
|
u32 save_base;
|
|
u32 base;
|
|
int index = 0;
|
|
struct pci_resource *mem_node;
|
|
struct pci_resource *p_mem_node;
|
|
struct pci_resource *io_node;
|
|
struct pci_resource *bus_node;
|
|
struct pci_bus *pci_bus = ctrl->pci_bus;
|
|
unsigned int devfn;
|
|
|
|
func = cpqhp_slot_find(func->bus, func->device, index++);
|
|
|
|
while ((func != NULL) && func->is_a_board) {
|
|
pci_bus->number = func->bus;
|
|
devfn = PCI_DEVFN(func->device, func->function);
|
|
|
|
/* Save the command register */
|
|
pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &save_command);
|
|
|
|
/* disable card */
|
|
command = 0x00;
|
|
pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command);
|
|
|
|
/* Check for Bridge */
|
|
pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
|
|
|
|
if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
|
|
/* Clear Bridge Control Register */
|
|
command = 0x00;
|
|
pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, command);
|
|
pci_bus_read_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus);
|
|
pci_bus_read_config_byte(pci_bus, devfn, PCI_SUBORDINATE_BUS, &temp_byte);
|
|
|
|
bus_node = kmalloc(sizeof(*bus_node), GFP_KERNEL);
|
|
if (!bus_node)
|
|
return -ENOMEM;
|
|
|
|
bus_node->base = secondary_bus;
|
|
bus_node->length = temp_byte - secondary_bus + 1;
|
|
|
|
bus_node->next = func->bus_head;
|
|
func->bus_head = bus_node;
|
|
|
|
/* Save IO base and Limit registers */
|
|
pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_BASE, &b_base);
|
|
pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_LIMIT, &b_length);
|
|
|
|
if ((b_base <= b_length) && (save_command & 0x01)) {
|
|
io_node = kmalloc(sizeof(*io_node), GFP_KERNEL);
|
|
if (!io_node)
|
|
return -ENOMEM;
|
|
|
|
io_node->base = (b_base & 0xF0) << 8;
|
|
io_node->length = (b_length - b_base + 0x10) << 8;
|
|
|
|
io_node->next = func->io_head;
|
|
func->io_head = io_node;
|
|
}
|
|
|
|
/* Save memory base and Limit registers */
|
|
pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_BASE, &w_base);
|
|
pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, &w_length);
|
|
|
|
if ((w_base <= w_length) && (save_command & 0x02)) {
|
|
mem_node = kmalloc(sizeof(*mem_node), GFP_KERNEL);
|
|
if (!mem_node)
|
|
return -ENOMEM;
|
|
|
|
mem_node->base = w_base << 16;
|
|
mem_node->length = (w_length - w_base + 0x10) << 16;
|
|
|
|
mem_node->next = func->mem_head;
|
|
func->mem_head = mem_node;
|
|
}
|
|
|
|
/* Save prefetchable memory base and Limit registers */
|
|
pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_BASE, &w_base);
|
|
pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &w_length);
|
|
|
|
if ((w_base <= w_length) && (save_command & 0x02)) {
|
|
p_mem_node = kmalloc(sizeof(*p_mem_node), GFP_KERNEL);
|
|
if (!p_mem_node)
|
|
return -ENOMEM;
|
|
|
|
p_mem_node->base = w_base << 16;
|
|
p_mem_node->length = (w_length - w_base + 0x10) << 16;
|
|
|
|
p_mem_node->next = func->p_mem_head;
|
|
func->p_mem_head = p_mem_node;
|
|
}
|
|
/* Figure out IO and memory base lengths */
|
|
for (cloop = 0x10; cloop <= 0x14; cloop += 4) {
|
|
pci_bus_read_config_dword (pci_bus, devfn, cloop, &save_base);
|
|
|
|
temp_register = 0xFFFFFFFF;
|
|
pci_bus_write_config_dword(pci_bus, devfn, cloop, temp_register);
|
|
pci_bus_read_config_dword(pci_bus, devfn, cloop, &base);
|
|
|
|
temp_register = base;
|
|
|
|
/* If this register is implemented */
|
|
if (base) {
|
|
if (((base & 0x03L) == 0x01)
|
|
&& (save_command & 0x01)) {
|
|
/* IO base
|
|
* set temp_register = amount
|
|
* of IO space requested
|
|
*/
|
|
temp_register = base & 0xFFFFFFFE;
|
|
temp_register = (~temp_register) + 1;
|
|
|
|
io_node = kmalloc(sizeof(*io_node),
|
|
GFP_KERNEL);
|
|
if (!io_node)
|
|
return -ENOMEM;
|
|
|
|
io_node->base =
|
|
save_base & (~0x03L);
|
|
io_node->length = temp_register;
|
|
|
|
io_node->next = func->io_head;
|
|
func->io_head = io_node;
|
|
} else
|
|
if (((base & 0x0BL) == 0x08)
|
|
&& (save_command & 0x02)) {
|
|
/* prefetchable memory base */
|
|
temp_register = base & 0xFFFFFFF0;
|
|
temp_register = (~temp_register) + 1;
|
|
|
|
p_mem_node = kmalloc(sizeof(*p_mem_node),
|
|
GFP_KERNEL);
|
|
if (!p_mem_node)
|
|
return -ENOMEM;
|
|
|
|
p_mem_node->base = save_base & (~0x0FL);
|
|
p_mem_node->length = temp_register;
|
|
|
|
p_mem_node->next = func->p_mem_head;
|
|
func->p_mem_head = p_mem_node;
|
|
} else
|
|
if (((base & 0x0BL) == 0x00)
|
|
&& (save_command & 0x02)) {
|
|
/* prefetchable memory base */
|
|
temp_register = base & 0xFFFFFFF0;
|
|
temp_register = (~temp_register) + 1;
|
|
|
|
mem_node = kmalloc(sizeof(*mem_node),
|
|
GFP_KERNEL);
|
|
if (!mem_node)
|
|
return -ENOMEM;
|
|
|
|
mem_node->base = save_base & (~0x0FL);
|
|
mem_node->length = temp_register;
|
|
|
|
mem_node->next = func->mem_head;
|
|
func->mem_head = mem_node;
|
|
} else
|
|
return(1);
|
|
}
|
|
} /* End of base register loop */
|
|
/* Standard header */
|
|
} else if ((header_type & 0x7F) == 0x00) {
|
|
/* Figure out IO and memory base lengths */
|
|
for (cloop = 0x10; cloop <= 0x24; cloop += 4) {
|
|
pci_bus_read_config_dword(pci_bus, devfn, cloop, &save_base);
|
|
|
|
temp_register = 0xFFFFFFFF;
|
|
pci_bus_write_config_dword(pci_bus, devfn, cloop, temp_register);
|
|
pci_bus_read_config_dword(pci_bus, devfn, cloop, &base);
|
|
|
|
temp_register = base;
|
|
|
|
/* If this register is implemented */
|
|
if (base) {
|
|
if (((base & 0x03L) == 0x01)
|
|
&& (save_command & 0x01)) {
|
|
/* IO base
|
|
* set temp_register = amount
|
|
* of IO space requested
|
|
*/
|
|
temp_register = base & 0xFFFFFFFE;
|
|
temp_register = (~temp_register) + 1;
|
|
|
|
io_node = kmalloc(sizeof(*io_node),
|
|
GFP_KERNEL);
|
|
if (!io_node)
|
|
return -ENOMEM;
|
|
|
|
io_node->base = save_base & (~0x01L);
|
|
io_node->length = temp_register;
|
|
|
|
io_node->next = func->io_head;
|
|
func->io_head = io_node;
|
|
} else
|
|
if (((base & 0x0BL) == 0x08)
|
|
&& (save_command & 0x02)) {
|
|
/* prefetchable memory base */
|
|
temp_register = base & 0xFFFFFFF0;
|
|
temp_register = (~temp_register) + 1;
|
|
|
|
p_mem_node = kmalloc(sizeof(*p_mem_node),
|
|
GFP_KERNEL);
|
|
if (!p_mem_node)
|
|
return -ENOMEM;
|
|
|
|
p_mem_node->base = save_base & (~0x0FL);
|
|
p_mem_node->length = temp_register;
|
|
|
|
p_mem_node->next = func->p_mem_head;
|
|
func->p_mem_head = p_mem_node;
|
|
} else
|
|
if (((base & 0x0BL) == 0x00)
|
|
&& (save_command & 0x02)) {
|
|
/* prefetchable memory base */
|
|
temp_register = base & 0xFFFFFFF0;
|
|
temp_register = (~temp_register) + 1;
|
|
|
|
mem_node = kmalloc(sizeof(*mem_node),
|
|
GFP_KERNEL);
|
|
if (!mem_node)
|
|
return -ENOMEM;
|
|
|
|
mem_node->base = save_base & (~0x0FL);
|
|
mem_node->length = temp_register;
|
|
|
|
mem_node->next = func->mem_head;
|
|
func->mem_head = mem_node;
|
|
} else
|
|
return(1);
|
|
}
|
|
} /* End of base register loop */
|
|
}
|
|
|
|
/* find the next device in this slot */
|
|
func = cpqhp_slot_find(func->bus, func->device, index++);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* cpqhp_configure_board
|
|
*
|
|
* Copies saved configuration information to one slot.
|
|
* this is called recursively for bridge devices.
|
|
* this is for hot plug REPLACE!
|
|
*
|
|
* returns 0 if success
|
|
*/
|
|
int cpqhp_configure_board(struct controller *ctrl, struct pci_func * func)
|
|
{
|
|
int cloop;
|
|
u8 header_type;
|
|
u8 secondary_bus;
|
|
int sub_bus;
|
|
struct pci_func *next;
|
|
u32 temp;
|
|
u32 rc;
|
|
int index = 0;
|
|
struct pci_bus *pci_bus = ctrl->pci_bus;
|
|
unsigned int devfn;
|
|
|
|
func = cpqhp_slot_find(func->bus, func->device, index++);
|
|
|
|
while (func != NULL) {
|
|
pci_bus->number = func->bus;
|
|
devfn = PCI_DEVFN(func->device, func->function);
|
|
|
|
/* Start at the top of config space so that the control
|
|
* registers are programmed last
|
|
*/
|
|
for (cloop = 0x3C; cloop > 0; cloop -= 4)
|
|
pci_bus_write_config_dword (pci_bus, devfn, cloop, func->config_space[cloop >> 2]);
|
|
|
|
pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
|
|
|
|
/* If this is a bridge device, restore subordinate devices */
|
|
if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
|
|
pci_bus_read_config_byte (pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus);
|
|
|
|
sub_bus = (int) secondary_bus;
|
|
|
|
next = cpqhp_slot_list[sub_bus];
|
|
|
|
while (next != NULL) {
|
|
rc = cpqhp_configure_board(ctrl, next);
|
|
if (rc)
|
|
return rc;
|
|
|
|
next = next->next;
|
|
}
|
|
} else {
|
|
|
|
/* Check all the base Address Registers to make sure
|
|
* they are the same. If not, the board is different.
|
|
*/
|
|
|
|
for (cloop = 16; cloop < 40; cloop += 4) {
|
|
pci_bus_read_config_dword (pci_bus, devfn, cloop, &temp);
|
|
|
|
if (temp != func->config_space[cloop >> 2]) {
|
|
dbg("Config space compare failure!!! offset = %x\n", cloop);
|
|
dbg("bus = %x, device = %x, function = %x\n", func->bus, func->device, func->function);
|
|
dbg("temp = %x, config space = %x\n\n", temp, func->config_space[cloop >> 2]);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
func->configured = 1;
|
|
|
|
func = cpqhp_slot_find(func->bus, func->device, index++);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* cpqhp_valid_replace
|
|
*
|
|
* this function checks to see if a board is the same as the
|
|
* one it is replacing. this check will detect if the device's
|
|
* vendor or device id's are the same
|
|
*
|
|
* returns 0 if the board is the same nonzero otherwise
|
|
*/
|
|
int cpqhp_valid_replace(struct controller *ctrl, struct pci_func * func)
|
|
{
|
|
u8 cloop;
|
|
u8 header_type;
|
|
u8 secondary_bus;
|
|
u8 type;
|
|
u32 temp_register = 0;
|
|
u32 base;
|
|
u32 rc;
|
|
struct pci_func *next;
|
|
int index = 0;
|
|
struct pci_bus *pci_bus = ctrl->pci_bus;
|
|
unsigned int devfn;
|
|
|
|
if (!func->is_a_board)
|
|
return(ADD_NOT_SUPPORTED);
|
|
|
|
func = cpqhp_slot_find(func->bus, func->device, index++);
|
|
|
|
while (func != NULL) {
|
|
pci_bus->number = func->bus;
|
|
devfn = PCI_DEVFN(func->device, func->function);
|
|
|
|
pci_bus_read_config_dword (pci_bus, devfn, PCI_VENDOR_ID, &temp_register);
|
|
|
|
/* No adapter present */
|
|
if (temp_register == 0xFFFFFFFF)
|
|
return(NO_ADAPTER_PRESENT);
|
|
|
|
if (temp_register != func->config_space[0])
|
|
return(ADAPTER_NOT_SAME);
|
|
|
|
/* Check for same revision number and class code */
|
|
pci_bus_read_config_dword (pci_bus, devfn, PCI_CLASS_REVISION, &temp_register);
|
|
|
|
/* Adapter not the same */
|
|
if (temp_register != func->config_space[0x08 >> 2])
|
|
return(ADAPTER_NOT_SAME);
|
|
|
|
/* Check for Bridge */
|
|
pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
|
|
|
|
if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
|
|
/* In order to continue checking, we must program the
|
|
* bus registers in the bridge to respond to accesses
|
|
* for its subordinate bus(es)
|
|
*/
|
|
|
|
temp_register = func->config_space[0x18 >> 2];
|
|
pci_bus_write_config_dword (pci_bus, devfn, PCI_PRIMARY_BUS, temp_register);
|
|
|
|
secondary_bus = (temp_register >> 8) & 0xFF;
|
|
|
|
next = cpqhp_slot_list[secondary_bus];
|
|
|
|
while (next != NULL) {
|
|
rc = cpqhp_valid_replace(ctrl, next);
|
|
if (rc)
|
|
return rc;
|
|
|
|
next = next->next;
|
|
}
|
|
|
|
}
|
|
/* Check to see if it is a standard config header */
|
|
else if ((header_type & 0x7F) == PCI_HEADER_TYPE_NORMAL) {
|
|
/* Check subsystem vendor and ID */
|
|
pci_bus_read_config_dword (pci_bus, devfn, PCI_SUBSYSTEM_VENDOR_ID, &temp_register);
|
|
|
|
if (temp_register != func->config_space[0x2C >> 2]) {
|
|
/* If it's a SMART-2 and the register isn't
|
|
* filled in, ignore the difference because
|
|
* they just have an old rev of the firmware
|
|
*/
|
|
if (!((func->config_space[0] == 0xAE100E11)
|
|
&& (temp_register == 0x00L)))
|
|
return(ADAPTER_NOT_SAME);
|
|
}
|
|
/* Figure out IO and memory base lengths */
|
|
for (cloop = 0x10; cloop <= 0x24; cloop += 4) {
|
|
temp_register = 0xFFFFFFFF;
|
|
pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register);
|
|
pci_bus_read_config_dword (pci_bus, devfn, cloop, &base);
|
|
|
|
/* If this register is implemented */
|
|
if (base) {
|
|
if (base & 0x01L) {
|
|
/* IO base
|
|
* set base = amount of IO
|
|
* space requested
|
|
*/
|
|
base = base & 0xFFFFFFFE;
|
|
base = (~base) + 1;
|
|
|
|
type = 1;
|
|
} else {
|
|
/* memory base */
|
|
base = base & 0xFFFFFFF0;
|
|
base = (~base) + 1;
|
|
|
|
type = 0;
|
|
}
|
|
} else {
|
|
base = 0x0L;
|
|
type = 0;
|
|
}
|
|
|
|
/* Check information in slot structure */
|
|
if (func->base_length[(cloop - 0x10) >> 2] != base)
|
|
return(ADAPTER_NOT_SAME);
|
|
|
|
if (func->base_type[(cloop - 0x10) >> 2] != type)
|
|
return(ADAPTER_NOT_SAME);
|
|
|
|
} /* End of base register loop */
|
|
|
|
} /* End of (type 0 config space) else */
|
|
else {
|
|
/* this is not a type 0 or 1 config space header so
|
|
* we don't know how to do it
|
|
*/
|
|
return(DEVICE_TYPE_NOT_SUPPORTED);
|
|
}
|
|
|
|
/* Get the next function */
|
|
func = cpqhp_slot_find(func->bus, func->device, index++);
|
|
}
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* cpqhp_find_available_resources
|
|
*
|
|
* Finds available memory, IO, and IRQ resources for programming
|
|
* devices which may be added to the system
|
|
* this function is for hot plug ADD!
|
|
*
|
|
* returns 0 if success
|
|
*/
|
|
int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_start)
|
|
{
|
|
u8 temp;
|
|
u8 populated_slot;
|
|
u8 bridged_slot;
|
|
void __iomem *one_slot;
|
|
void __iomem *rom_resource_table;
|
|
struct pci_func *func = NULL;
|
|
int i = 10, index;
|
|
u32 temp_dword, rc;
|
|
struct pci_resource *mem_node;
|
|
struct pci_resource *p_mem_node;
|
|
struct pci_resource *io_node;
|
|
struct pci_resource *bus_node;
|
|
|
|
rom_resource_table = detect_HRT_floating_pointer(rom_start, rom_start+0xffff);
|
|
dbg("rom_resource_table = %p\n", rom_resource_table);
|
|
|
|
if (rom_resource_table == NULL)
|
|
return -ENODEV;
|
|
|
|
/* Sum all resources and setup resource maps */
|
|
unused_IRQ = readl(rom_resource_table + UNUSED_IRQ);
|
|
dbg("unused_IRQ = %x\n", unused_IRQ);
|
|
|
|
temp = 0;
|
|
while (unused_IRQ) {
|
|
if (unused_IRQ & 1) {
|
|
cpqhp_disk_irq = temp;
|
|
break;
|
|
}
|
|
unused_IRQ = unused_IRQ >> 1;
|
|
temp++;
|
|
}
|
|
|
|
dbg("cpqhp_disk_irq= %d\n", cpqhp_disk_irq);
|
|
unused_IRQ = unused_IRQ >> 1;
|
|
temp++;
|
|
|
|
while (unused_IRQ) {
|
|
if (unused_IRQ & 1) {
|
|
cpqhp_nic_irq = temp;
|
|
break;
|
|
}
|
|
unused_IRQ = unused_IRQ >> 1;
|
|
temp++;
|
|
}
|
|
|
|
dbg("cpqhp_nic_irq= %d\n", cpqhp_nic_irq);
|
|
unused_IRQ = readl(rom_resource_table + PCIIRQ);
|
|
|
|
temp = 0;
|
|
|
|
if (!cpqhp_nic_irq)
|
|
cpqhp_nic_irq = ctrl->cfgspc_irq;
|
|
|
|
if (!cpqhp_disk_irq)
|
|
cpqhp_disk_irq = ctrl->cfgspc_irq;
|
|
|
|
dbg("cpqhp_disk_irq, cpqhp_nic_irq= %d, %d\n", cpqhp_disk_irq, cpqhp_nic_irq);
|
|
|
|
rc = compaq_nvram_load(rom_start, ctrl);
|
|
if (rc)
|
|
return rc;
|
|
|
|
one_slot = rom_resource_table + sizeof (struct hrt);
|
|
|
|
i = readb(rom_resource_table + NUMBER_OF_ENTRIES);
|
|
dbg("number_of_entries = %d\n", i);
|
|
|
|
if (!readb(one_slot + SECONDARY_BUS))
|
|
return 1;
|
|
|
|
dbg("dev|IO base|length|Mem base|length|Pre base|length|PB SB MB\n");
|
|
|
|
while (i && readb(one_slot + SECONDARY_BUS)) {
|
|
u8 dev_func = readb(one_slot + DEV_FUNC);
|
|
u8 primary_bus = readb(one_slot + PRIMARY_BUS);
|
|
u8 secondary_bus = readb(one_slot + SECONDARY_BUS);
|
|
u8 max_bus = readb(one_slot + MAX_BUS);
|
|
u16 io_base = readw(one_slot + IO_BASE);
|
|
u16 io_length = readw(one_slot + IO_LENGTH);
|
|
u16 mem_base = readw(one_slot + MEM_BASE);
|
|
u16 mem_length = readw(one_slot + MEM_LENGTH);
|
|
u16 pre_mem_base = readw(one_slot + PRE_MEM_BASE);
|
|
u16 pre_mem_length = readw(one_slot + PRE_MEM_LENGTH);
|
|
|
|
dbg("%2.2x | %4.4x | %4.4x | %4.4x | %4.4x | %4.4x | %4.4x |%2.2x %2.2x %2.2x\n",
|
|
dev_func, io_base, io_length, mem_base, mem_length, pre_mem_base, pre_mem_length,
|
|
primary_bus, secondary_bus, max_bus);
|
|
|
|
/* If this entry isn't for our controller's bus, ignore it */
|
|
if (primary_bus != ctrl->bus) {
|
|
i--;
|
|
one_slot += sizeof (struct slot_rt);
|
|
continue;
|
|
}
|
|
/* find out if this entry is for an occupied slot */
|
|
ctrl->pci_bus->number = primary_bus;
|
|
pci_bus_read_config_dword (ctrl->pci_bus, dev_func, PCI_VENDOR_ID, &temp_dword);
|
|
dbg("temp_D_word = %x\n", temp_dword);
|
|
|
|
if (temp_dword != 0xFFFFFFFF) {
|
|
index = 0;
|
|
func = cpqhp_slot_find(primary_bus, dev_func >> 3, 0);
|
|
|
|
while (func && (func->function != (dev_func & 0x07))) {
|
|
dbg("func = %p (bus, dev, fun) = (%d, %d, %d)\n", func, primary_bus, dev_func >> 3, index);
|
|
func = cpqhp_slot_find(primary_bus, dev_func >> 3, index++);
|
|
}
|
|
|
|
/* If we can't find a match, skip this table entry */
|
|
if (!func) {
|
|
i--;
|
|
one_slot += sizeof (struct slot_rt);
|
|
continue;
|
|
}
|
|
/* this may not work and shouldn't be used */
|
|
if (secondary_bus != primary_bus)
|
|
bridged_slot = 1;
|
|
else
|
|
bridged_slot = 0;
|
|
|
|
populated_slot = 1;
|
|
} else {
|
|
populated_slot = 0;
|
|
bridged_slot = 0;
|
|
}
|
|
|
|
|
|
/* If we've got a valid IO base, use it */
|
|
|
|
temp_dword = io_base + io_length;
|
|
|
|
if ((io_base) && (temp_dword < 0x10000)) {
|
|
io_node = kmalloc(sizeof(*io_node), GFP_KERNEL);
|
|
if (!io_node)
|
|
return -ENOMEM;
|
|
|
|
io_node->base = io_base;
|
|
io_node->length = io_length;
|
|
|
|
dbg("found io_node(base, length) = %x, %x\n",
|
|
io_node->base, io_node->length);
|
|
dbg("populated slot =%d \n", populated_slot);
|
|
if (!populated_slot) {
|
|
io_node->next = ctrl->io_head;
|
|
ctrl->io_head = io_node;
|
|
} else {
|
|
io_node->next = func->io_head;
|
|
func->io_head = io_node;
|
|
}
|
|
}
|
|
|
|
/* If we've got a valid memory base, use it */
|
|
temp_dword = mem_base + mem_length;
|
|
if ((mem_base) && (temp_dword < 0x10000)) {
|
|
mem_node = kmalloc(sizeof(*mem_node), GFP_KERNEL);
|
|
if (!mem_node)
|
|
return -ENOMEM;
|
|
|
|
mem_node->base = mem_base << 16;
|
|
|
|
mem_node->length = mem_length << 16;
|
|
|
|
dbg("found mem_node(base, length) = %x, %x\n",
|
|
mem_node->base, mem_node->length);
|
|
dbg("populated slot =%d \n", populated_slot);
|
|
if (!populated_slot) {
|
|
mem_node->next = ctrl->mem_head;
|
|
ctrl->mem_head = mem_node;
|
|
} else {
|
|
mem_node->next = func->mem_head;
|
|
func->mem_head = mem_node;
|
|
}
|
|
}
|
|
|
|
/* If we've got a valid prefetchable memory base, and
|
|
* the base + length isn't greater than 0xFFFF
|
|
*/
|
|
temp_dword = pre_mem_base + pre_mem_length;
|
|
if ((pre_mem_base) && (temp_dword < 0x10000)) {
|
|
p_mem_node = kmalloc(sizeof(*p_mem_node), GFP_KERNEL);
|
|
if (!p_mem_node)
|
|
return -ENOMEM;
|
|
|
|
p_mem_node->base = pre_mem_base << 16;
|
|
|
|
p_mem_node->length = pre_mem_length << 16;
|
|
dbg("found p_mem_node(base, length) = %x, %x\n",
|
|
p_mem_node->base, p_mem_node->length);
|
|
dbg("populated slot =%d \n", populated_slot);
|
|
|
|
if (!populated_slot) {
|
|
p_mem_node->next = ctrl->p_mem_head;
|
|
ctrl->p_mem_head = p_mem_node;
|
|
} else {
|
|
p_mem_node->next = func->p_mem_head;
|
|
func->p_mem_head = p_mem_node;
|
|
}
|
|
}
|
|
|
|
/* If we've got a valid bus number, use it
|
|
* The second condition is to ignore bus numbers on
|
|
* populated slots that don't have PCI-PCI bridges
|
|
*/
|
|
if (secondary_bus && (secondary_bus != primary_bus)) {
|
|
bus_node = kmalloc(sizeof(*bus_node), GFP_KERNEL);
|
|
if (!bus_node)
|
|
return -ENOMEM;
|
|
|
|
bus_node->base = secondary_bus;
|
|
bus_node->length = max_bus - secondary_bus + 1;
|
|
dbg("found bus_node(base, length) = %x, %x\n",
|
|
bus_node->base, bus_node->length);
|
|
dbg("populated slot =%d \n", populated_slot);
|
|
if (!populated_slot) {
|
|
bus_node->next = ctrl->bus_head;
|
|
ctrl->bus_head = bus_node;
|
|
} else {
|
|
bus_node->next = func->bus_head;
|
|
func->bus_head = bus_node;
|
|
}
|
|
}
|
|
|
|
i--;
|
|
one_slot += sizeof (struct slot_rt);
|
|
}
|
|
|
|
/* If all of the following fail, we don't have any resources for
|
|
* hot plug add
|
|
*/
|
|
rc = 1;
|
|
rc &= cpqhp_resource_sort_and_combine(&(ctrl->mem_head));
|
|
rc &= cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head));
|
|
rc &= cpqhp_resource_sort_and_combine(&(ctrl->io_head));
|
|
rc &= cpqhp_resource_sort_and_combine(&(ctrl->bus_head));
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* cpqhp_return_board_resources
|
|
*
|
|
* this routine returns all resources allocated to a board to
|
|
* the available pool.
|
|
*
|
|
* returns 0 if success
|
|
*/
|
|
int cpqhp_return_board_resources(struct pci_func * func, struct resource_lists * resources)
|
|
{
|
|
int rc = 0;
|
|
struct pci_resource *node;
|
|
struct pci_resource *t_node;
|
|
dbg("%s\n", __func__);
|
|
|
|
if (!func)
|
|
return 1;
|
|
|
|
node = func->io_head;
|
|
func->io_head = NULL;
|
|
while (node) {
|
|
t_node = node->next;
|
|
return_resource(&(resources->io_head), node);
|
|
node = t_node;
|
|
}
|
|
|
|
node = func->mem_head;
|
|
func->mem_head = NULL;
|
|
while (node) {
|
|
t_node = node->next;
|
|
return_resource(&(resources->mem_head), node);
|
|
node = t_node;
|
|
}
|
|
|
|
node = func->p_mem_head;
|
|
func->p_mem_head = NULL;
|
|
while (node) {
|
|
t_node = node->next;
|
|
return_resource(&(resources->p_mem_head), node);
|
|
node = t_node;
|
|
}
|
|
|
|
node = func->bus_head;
|
|
func->bus_head = NULL;
|
|
while (node) {
|
|
t_node = node->next;
|
|
return_resource(&(resources->bus_head), node);
|
|
node = t_node;
|
|
}
|
|
|
|
rc |= cpqhp_resource_sort_and_combine(&(resources->mem_head));
|
|
rc |= cpqhp_resource_sort_and_combine(&(resources->p_mem_head));
|
|
rc |= cpqhp_resource_sort_and_combine(&(resources->io_head));
|
|
rc |= cpqhp_resource_sort_and_combine(&(resources->bus_head));
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
/*
|
|
* cpqhp_destroy_resource_list
|
|
*
|
|
* Puts node back in the resource list pointed to by head
|
|
*/
|
|
void cpqhp_destroy_resource_list (struct resource_lists * resources)
|
|
{
|
|
struct pci_resource *res, *tres;
|
|
|
|
res = resources->io_head;
|
|
resources->io_head = NULL;
|
|
|
|
while (res) {
|
|
tres = res;
|
|
res = res->next;
|
|
kfree(tres);
|
|
}
|
|
|
|
res = resources->mem_head;
|
|
resources->mem_head = NULL;
|
|
|
|
while (res) {
|
|
tres = res;
|
|
res = res->next;
|
|
kfree(tres);
|
|
}
|
|
|
|
res = resources->p_mem_head;
|
|
resources->p_mem_head = NULL;
|
|
|
|
while (res) {
|
|
tres = res;
|
|
res = res->next;
|
|
kfree(tres);
|
|
}
|
|
|
|
res = resources->bus_head;
|
|
resources->bus_head = NULL;
|
|
|
|
while (res) {
|
|
tres = res;
|
|
res = res->next;
|
|
kfree(tres);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* cpqhp_destroy_board_resources
|
|
*
|
|
* Puts node back in the resource list pointed to by head
|
|
*/
|
|
void cpqhp_destroy_board_resources (struct pci_func * func)
|
|
{
|
|
struct pci_resource *res, *tres;
|
|
|
|
res = func->io_head;
|
|
func->io_head = NULL;
|
|
|
|
while (res) {
|
|
tres = res;
|
|
res = res->next;
|
|
kfree(tres);
|
|
}
|
|
|
|
res = func->mem_head;
|
|
func->mem_head = NULL;
|
|
|
|
while (res) {
|
|
tres = res;
|
|
res = res->next;
|
|
kfree(tres);
|
|
}
|
|
|
|
res = func->p_mem_head;
|
|
func->p_mem_head = NULL;
|
|
|
|
while (res) {
|
|
tres = res;
|
|
res = res->next;
|
|
kfree(tres);
|
|
}
|
|
|
|
res = func->bus_head;
|
|
func->bus_head = NULL;
|
|
|
|
while (res) {
|
|
tres = res;
|
|
res = res->next;
|
|
kfree(tres);
|
|
}
|
|
}
|