mirror of
https://github.com/torvalds/linux.git
synced 2024-11-16 17:12:06 +00:00
7a54f25cef
This makes it possible to build pci hotplug drivers outside of the main kernel tree, and Sam keeps telling me to move local header files to their proper places... Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
1565 lines
40 KiB
C
1565 lines
40 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"
|
|
#include "../../../arch/i386/pci/pci.h" /* horrible hack showing how processor dependent we are... */
|
|
|
|
|
|
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)
|
|
{
|
|
unsigned char bus;
|
|
struct pci_bus *child;
|
|
int num;
|
|
|
|
if (func->pci_dev == NULL)
|
|
func->pci_dev = pci_find_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_find_slot(func->bus, PCI_DEVFN(func->device, func->function));
|
|
if (func->pci_dev == NULL) {
|
|
dbg("ERROR: pci_dev still null\n");
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (func->pci_dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
|
|
pci_read_config_byte(func->pci_dev, PCI_SECONDARY_BUS, &bus);
|
|
child = (struct pci_bus*) pci_add_new_bus(func->pci_dev->bus, (func->pci_dev), bus);
|
|
pci_do_scan_bus(child);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int cpqhp_unconfigure_device(struct pci_func* func)
|
|
{
|
|
int j;
|
|
|
|
dbg("%s: bus/dev/func = %x/%x/%x\n", __FUNCTION__, func->bus, func->device, func->function);
|
|
|
|
for (j=0; j<8 ; j++) {
|
|
struct pci_dev* temp = pci_find_slot(func->bus, PCI_DEVFN(func->device, j));
|
|
if (temp)
|
|
pci_remove_bus_device(temp);
|
|
}
|
|
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",
|
|
__FUNCTION__, dev_num, bus_num, int_pin, irq_num);
|
|
rc = pcibios_set_irq_routing(fakedev, int_pin - 0x0a, irq_num);
|
|
kfree(fakedev);
|
|
kfree(fakebus);
|
|
dbg("%s: rc %d\n", __FUNCTION__, 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;
|
|
}
|
|
|
|
|
|
/*
|
|
* WTF??? This function isn't in the code, yet a function calls it, but the
|
|
* compiler optimizes it away? strange. Here as a placeholder to keep the
|
|
* compiler happy.
|
|
*/
|
|
static int PCI_ScanBusNonBridge (u8 bus, u8 device)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
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);
|
|
dbg("Recurse on bus_num %d tdevice %d\n", tbus, tdevice);
|
|
if (PCI_ScanBusNonBridge(tbus, tdevice) == 0)
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
static int PCI_GetBusDevHelper(struct controller *ctrl, u8 *bus_num, u8 *dev_num, u8 slot, u8 nobridge)
|
|
{
|
|
struct irq_routing_table *PCIIRQRoutingInfoLength;
|
|
long len;
|
|
long loop;
|
|
u32 work;
|
|
|
|
u8 tbus, tdevice, tslot;
|
|
|
|
PCIIRQRoutingInfoLength = pcibios_get_irq_routing_table();
|
|
if (!PCIIRQRoutingInfoLength)
|
|
return -1;
|
|
|
|
len = (PCIIRQRoutingInfoLength->size -
|
|
sizeof(struct irq_routing_table)) / sizeof(struct irq_info);
|
|
// Make sure I got at least one entry
|
|
if (len == 0) {
|
|
kfree(PCIIRQRoutingInfoLength );
|
|
return -1;
|
|
}
|
|
|
|
for (loop = 0; loop < len; ++loop) {
|
|
tbus = PCIIRQRoutingInfoLength->slots[loop].bus;
|
|
tdevice = PCIIRQRoutingInfoLength->slots[loop].devfn;
|
|
tslot = PCIIRQRoutingInfoLength->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)) {
|
|
kfree(PCIIRQRoutingInfoLength );
|
|
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;
|
|
kfree(PCIIRQRoutingInfoLength );
|
|
return 0;
|
|
}
|
|
} else {
|
|
kfree(PCIIRQRoutingInfoLength );
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
}
|
|
kfree(PCIIRQRoutingInfoLength );
|
|
return -1;
|
|
}
|
|
|
|
|
|
int cpqhp_get_bus_dev (struct controller *ctrl, u8 * bus_num, u8 * dev_num, u8 slot)
|
|
{
|
|
return PCI_GetBusDevHelper(ctrl, bus_num, dev_num, slot, 0); //plain (bridges allowed)
|
|
}
|
|
|
|
|
|
/* 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 busses, 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) { // device in slot
|
|
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) { // P-P 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_find_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;
|
|
}
|
|
|
|
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) { // nothing there.
|
|
function++;
|
|
} else { // Something there
|
|
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 IF (device in slot?)
|
|
else 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;
|
|
}
|
|
} // End of FOR loop
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
/*
|
|
* cpqhp_save_slot_config
|
|
*
|
|
* Saves configuration info for all PCI devices in a given slot
|
|
* including subordinate busses.
|
|
*
|
|
* 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;
|
|
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) { // device in slot
|
|
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;
|
|
|
|
function = 0;
|
|
|
|
do {
|
|
if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI 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;
|
|
|
|
} // End of IF
|
|
|
|
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) { // nothing there.
|
|
function++;
|
|
} else { // Something there
|
|
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++;
|
|
}
|
|
}
|
|
|
|
} while (function < max_functions);
|
|
} // End of IF (device in slot?)
|
|
else {
|
|
return 2;
|
|
}
|
|
|
|
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-PCI 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 (base) { // If this register is implemented
|
|
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) { // PCI-PCI Bridge
|
|
// 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 (base) { // If this register is implemented
|
|
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) { // PCI-PCI 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 (base) { // If this register is implemented
|
|
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
|
|
} else if ((header_type & 0x7F) == 0x00) { // Standard header
|
|
// 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 (base) { // If this register is implemented
|
|
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
|
|
} 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_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-PCI 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) { // PCI-PCI Bridge
|
|
// In order to continue checking, we must program the
|
|
// bus registers in the bridge to respond to accesses
|
|
// for it's 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 (base) { // If this register is implemented
|
|
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", __FUNCTION__);
|
|
|
|
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);
|
|
}
|
|
}
|
|
|