Commit c16b4b14 authored by Dely Sy's avatar Dely Sy Committed by Greg Kroah-Hartman

[PATCH] PCI Hotplug: Add SHPC and PCI Express hot-plug drivers

parent 817a68f7
......@@ -122,5 +122,72 @@ config HOTPLUG_PCI_CPCI_GENERIC
When in doubt, say N.
config HOTPLUG_PCI_PCIE
tristate "PCI Express Hotplug driver"
depends on HOTPLUG_PCI
help
Say Y here if you have a motherboard that supports PCI Express Native
Hotplug
To compile this driver as a module, choose M here: the
module will be called pciehp.
When in doubt, say N.
config HOTPLUG_PCI_PCIE_POLL_EVENT_MODE
bool "Use polling mechanism for hot-plug events."
depends on HOTPLUG_PCI_PCIE
help
Say Y here if you want to use the polling mechanism for hot-plug
events.
When in doubt, say N.
config HOTPLUG_PCI_PCIE_PHPRM_NONACPI
bool "Non-ACPI: Use $HPRT for resource/configuration"
depends on HOTPLUG_PCI_PCIE
help
Say Y here if Hotplug resource/configuration information is provided
by platform BIOS $HPRT or bridge resource information, not by ACPI.
When in doubt, say N.
config HOTPLUG_PCI_SHPC
tristate "SHPC PCI Hotplug driver"
depends on HOTPLUG_PCI
help
Say Y here if you have a motherboard with a SHPC PCI Hotplug
controller.
To compile this driver as a module, choose M here: the
module will be called shpchp.
When in doubt, say N.
config HOTPLUG_PCI_SHPC_POLL_EVENT_MODE
bool "Use polling mechanism for hot-plug events"
depends on HOTPLUG_PCI_SHPC
help
Say Y here if you want to use the polling mechanism for hot-plug
events.
When in doubt, say N.
config HOTPLUG_PCI_SHPC_PHPRM_NONACPI
bool "Non-ACPI: Use $HPRT for resource/configuration"
depends on HOTPLUG_PCI_SHPC
help
Say Y here if Hotplug resource/configuration information is provided
by platform BIOS $HPRT or bridge resource information, not by ACPI.
When in doubt, say N.
config HOTPLUG_PCI_SHPC_PHPRM_LEGACY
bool "For AMD SHPC only: Use $HRT for resource/configuration"
depends on HOTPLUG_PCI_SHPC && HOTPLUG_PCI_SHPC_PHPRM_NONACPI
help
Say Y here for AMD SHPC. You have to select this option if you are
using this driver on AMD platform with SHPC.
endmenu
......@@ -9,6 +9,8 @@ obj-$(CONFIG_HOTPLUG_PCI_IBM) += ibmphp.o
obj-$(CONFIG_HOTPLUG_PCI_ACPI) += acpiphp.o
obj-$(CONFIG_HOTPLUG_PCI_CPCI_ZT5550) += cpcihp_zt5550.o
obj-$(CONFIG_HOTPLUG_PCI_CPCI_GENERIC) += cpcihp_generic.o
obj-$(CONFIG_HOTPLUG_PCI_PCIE) += pciehp.o
obj-$(CONFIG_HOTPLUG_PCI_SHPC) += shpchp.o
pci_hotplug-objs := pci_hotplug_core.o
......@@ -33,6 +35,18 @@ acpiphp-objs := acpiphp_core.o \
acpiphp_pci.o \
acpiphp_res.o
pciehp-objs := pciehp_core.o \
pciehp_ctrl.o \
pciehp_pci.o \
pciehp_sysfs.o \
pciehp_hpc.o
shpchp-objs := shpchp_core.o \
shpchp_ctrl.o \
shpchp_pci.o \
shpchp_sysfs.o \
shpchp_hpc.o
ifdef CONFIG_HOTPLUG_PCI_ACPI
EXTRA_CFLAGS += -D_LINUX -I$(TOPDIR)/drivers/acpi
ifdef CONFIG_ACPI_DEBUG
......@@ -43,3 +57,21 @@ endif
ifeq ($(CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM),y)
cpqphp-objs += cpqphp_nvram.o
endif
ifeq ($(CONFIG_HOTPLUG_PCI_PCIE_PHPRM_NONACPI),y)
pciehp-objs += pciehprm_nonacpi.o
else
pciehp-objs += pciehprm_acpi.o
EXTRA_CFLAGS += -D_LINUX -I$(TOPDIR)/drivers/acpi -I$(TOPDIR)/drivers/acpi/include
endif
ifeq ($(CONFIG_HOTPLUG_PCI_SHPC_PHPRM_LEGACY),y)
shpchp-objs += shpchprm_legacy.o
else
ifeq ($(CONFIG_HOTPLUG_PCI_SHPC_PHPRM_NONACPI),y)
shpchp-objs += shpchprm_nonacpi.o
else
shpchp-objs += shpchprm_acpi.o
EXTRA_CFLAGS += -D_LINUX -I$(TOPDIR)/drivers/acpi
endif
endif
......@@ -36,6 +36,9 @@ enum pci_bus_speed {
PCI_SPEED_66MHz_PCIX = 0x02,
PCI_SPEED_100MHz_PCIX = 0x03,
PCI_SPEED_133MHz_PCIX = 0x04,
PCI_SPEED_66MHz_PCIX_ECC = 0x05,
PCI_SPEED_100MHz_PCIX_ECC = 0x06,
PCI_SPEED_133MHz_PCIX_ECC = 0x07,
PCI_SPEED_66MHz_PCIX_266 = 0x09,
PCI_SPEED_100MHz_PCIX_266 = 0x0a,
PCI_SPEED_133MHz_PCIX_266 = 0x0b,
......@@ -45,6 +48,24 @@ enum pci_bus_speed {
PCI_SPEED_UNKNOWN = 0xff,
};
/* These values come from the PCI Express Spec */
enum pcie_link_width {
PCIE_LNK_WIDTH_RESRV = 0x00,
PCIE_LNK_X1 = 0x01,
PCIE_LNK_X2 = 0x02,
PCIE_LNK_X4 = 0x04,
PCIE_LNK_X8 = 0x08,
PCIE_LNK_X12 = 0x0C,
PCIE_LNK_X16 = 0x10,
PCIE_LNK_X32 = 0x20,
PCIE_LNK_WIDTH_UNKNOWN = 0xFF,
};
enum pcie_link_speed {
PCIE_2PT5GB = 0x14,
PCIE_LNK_SPEED_UNKNOWN = 0xFF,
};
struct hotplug_slot;
struct hotplug_slot_attribute {
struct attribute attr;
......
......@@ -129,6 +129,7 @@ static char *pci_bus_speed_strings[] = {
"66 MHz PCIX 533", /* 0x11 */
"100 MHz PCIX 533", /* 0x12 */
"133 MHz PCIX 533", /* 0x13 */
"25 GBps PCI-E", /* 0x14 */
};
#ifdef CONFIG_HOTPLUG_PCI_CPCI
......
/*
* PCI Express 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.
* Copyright (C) 2003-2004 Intel Corporation
*
* 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>, <dely.l.sy@intel.com>
*
*/
#ifndef _PCIEHP_H
#define _PCIEHP_H
#include <linux/types.h>
#include <linux/pci.h>
#include <asm/semaphore.h>
#include <asm/io.h>
#include "pci_hotplug.h"
#if !defined(CONFIG_HOTPLUG_PCI_PCIE_MODULE)
#define MY_NAME "pciehp"
#else
#define MY_NAME THIS_MODULE->name
#endif
extern int pciehp_poll_mode;
extern int pciehp_poll_time;
extern int pciehp_debug;
/*#define dbg(format, arg...) do { if (pciehp_debug) printk(KERN_DEBUG "%s: " format, MY_NAME , ## arg); } while (0)*/
#define dbg(format, arg...) do { if (pciehp_debug) printk("%s: " format, MY_NAME , ## arg); } while (0)
#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME , ## arg)
struct pci_func {
struct pci_func *next;
u8 bus;
u8 device;
u8 function;
u8 is_a_board;
u16 status;
u8 configured;
u8 switch_save;
u8 presence_save;
u32 base_length[0x06];
u8 base_type[0x06];
u16 reserved2;
u32 config_space[0x20];
struct pci_resource *mem_head;
struct pci_resource *p_mem_head;
struct pci_resource *io_head;
struct pci_resource *bus_head;
struct pci_dev* pci_dev;
};
#define SLOT_MAGIC 0x67267321
struct slot {
u32 magic;
struct slot *next;
u8 bus;
u8 device;
u32 number;
u8 is_a_board;
u8 configured;
u8 state;
u8 switch_save;
u8 presence_save;
u32 capabilities;
u16 reserved2;
struct timer_list task_event;
u8 hp_slot;
struct controller *ctrl;
struct hpc_ops *hpc_ops;
struct hotplug_slot *hotplug_slot;
struct list_head slot_list;
};
struct pci_resource {
struct pci_resource * next;
u32 base;
u32 length;
};
struct event_info {
u32 event_type;
u8 hp_slot;
};
struct controller {
struct controller *next;
struct semaphore crit_sect; /* critical section semaphore */
void * hpc_ctlr_handle; /* HPC controller handle */
int num_slots; /* Number of slots on ctlr */
int slot_num_inc; /* 1 or -1 */
struct pci_resource *mem_head;
struct pci_resource *p_mem_head;
struct pci_resource *io_head;
struct pci_resource *bus_head;
struct pci_dev *pci_dev;
struct pci_bus *pci_bus;
struct event_info event_queue[10];
struct slot *slot;
struct hpc_ops *hpc_ops;
wait_queue_head_t queue; /* sleep & wake process */
u8 next_event;
u8 seg;
u8 bus;
u8 device;
u8 function;
u8 rev;
u8 slot_device_offset;
u8 add_support;
enum pci_bus_speed speed;
u32 first_slot; /* First physical slot number */ /* PCIE only has 1 slot */
u8 slot_bus; /* Bus where the slots handled by this controller sit */
u8 push_flag;
u16 ctlrcap;
u16 vendor_id;
};
struct irq_mapping {
u8 barber_pole;
u8 valid_INT;
u8 interrupt[4];
};
struct resource_lists {
struct pci_resource *mem_head;
struct pci_resource *p_mem_head;
struct pci_resource *io_head;
struct pci_resource *bus_head;
struct irq_mapping *irqs;
};
#define INT_BUTTON_IGNORE 0
#define INT_PRESENCE_ON 1
#define INT_PRESENCE_OFF 2
#define INT_SWITCH_CLOSE 3
#define INT_SWITCH_OPEN 4
#define INT_POWER_FAULT 5
#define INT_POWER_FAULT_CLEAR 6
#define INT_BUTTON_PRESS 7
#define INT_BUTTON_RELEASE 8
#define INT_BUTTON_CANCEL 9
#define STATIC_STATE 0
#define BLINKINGON_STATE 1
#define BLINKINGOFF_STATE 2
#define POWERON_STATE 3
#define POWEROFF_STATE 4
#define PCI_TO_PCI_BRIDGE_CLASS 0x00060400
/* Error messages */
#define INTERLOCK_OPEN 0x00000002
#define ADD_NOT_SUPPORTED 0x00000003
#define CARD_FUNCTIONING 0x00000005
#define ADAPTER_NOT_SAME 0x00000006
#define NO_ADAPTER_PRESENT 0x00000009
#define NOT_ENOUGH_RESOURCES 0x0000000B
#define DEVICE_TYPE_NOT_SUPPORTED 0x0000000C
#define WRONG_BUS_FREQUENCY 0x0000000D
#define POWER_FAILURE 0x0000000E
#define REMOVE_NOT_SUPPORTED 0x00000003
#define DISABLE_CARD 1
/*
* error Messages
*/
#define msg_initialization_err "Initialization failure, error=%d\n"
#define msg_HPC_rev_error "Unsupported revision of the PCI hot plug controller found.\n"
#define msg_HPC_non_pcie "The PCI hot plug controller is not supported by this driver.\n"
#define msg_HPC_not_supported "This system is not supported by this version of pciephd mdoule. Upgrade to a newer version of pciehpd\n"
#define msg_unable_to_save "Unable to store PCI hot plug add resource information. This system must be rebooted before adding any PCI devices.\n"
#define msg_button_on "PCI slot #%d - powering on due to button press.\n"
#define msg_button_off "PCI slot #%d - powering off due to button press.\n"
#define msg_button_cancel "PCI slot #%d - action canceled due to button press.\n"
#define msg_button_ignore "PCI slot #%d - button press ignored. (action in progress...)\n"
/* sysfs function for the hotplug controller info */
extern void pciehp_create_ctrl_files (struct controller *ctrl);
/* controller functions */
extern void pciehp_pushbutton_thread (unsigned long event_pointer);
extern int pciehprm_find_available_resources (struct controller *ctrl);
extern int pciehp_event_start_thread (void);
extern void pciehp_event_stop_thread (void);
extern struct pci_func *pciehp_slot_create (unsigned char busnumber);
extern struct pci_func *pciehp_slot_find (unsigned char bus, unsigned char device, unsigned char index);
extern int pciehp_enable_slot (struct slot *slot);
extern int pciehp_disable_slot (struct slot *slot);
extern u8 pciehp_handle_attention_button (u8 hp_slot, void *inst_id);
extern u8 pciehp_handle_switch_change (u8 hp_slot, void *inst_id);
extern u8 pciehp_handle_presence_change (u8 hp_slot, void *inst_id);
extern u8 pciehp_handle_power_fault (u8 hp_slot, void *inst_id);
/* extern void long_delay (int delay); */
/* resource functions */
extern int pciehp_resource_sort_and_combine (struct pci_resource **head);
/* pci functions */
extern int pciehp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num);
/*extern int pciehp_get_bus_dev (struct controller *ctrl, u8 *bus_num, u8 *dev_num, struct slot *slot);*/
extern int pciehp_save_config (struct controller *ctrl, int busnumber, int num_ctlr_slots, int first_device_num);
extern int pciehp_save_used_resources (struct controller *ctrl, struct pci_func * func, int flag);
extern int pciehp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot);
extern void pciehp_destroy_board_resources (struct pci_func * func);
extern int pciehp_return_board_resources (struct pci_func * func, struct resource_lists * resources);
extern void pciehp_destroy_resource_list (struct resource_lists * resources);
extern int pciehp_configure_device (struct controller* ctrl, struct pci_func* func);
extern int pciehp_unconfigure_device (struct pci_func* func);
/* Global variables */
extern struct controller *pciehp_ctrl_list;
extern struct pci_func *pciehp_slot_list[256];
/* Inline functions */
/* Inline functions to check the sanity of a pointer that is passed to us */
static inline int slot_paranoia_check (struct slot *slot, const char *function)
{
if (!slot) {
dbg("%s - slot == NULL", function);
return -1;
}
if (slot->magic != SLOT_MAGIC) {
dbg("%s - bad magic number for slot", function);
return -1;
}
if (!slot->hotplug_slot) {
dbg("%s - slot->hotplug_slot == NULL!", function);
return -1;
}
return 0;
}
static inline struct slot *get_slot (struct hotplug_slot *hotplug_slot, const char *function)
{
struct slot *slot;
if (!hotplug_slot) {
dbg("%s - hotplug_slot == NULL\n", function);
return NULL;
}
slot = (struct slot *)hotplug_slot->private;
if (slot_paranoia_check (slot, function))
return NULL;
return slot;
}
static inline struct slot *pciehp_find_slot (struct controller *ctrl, u8 device)
{
struct slot *p_slot, *tmp_slot = NULL;
if (!ctrl)
return NULL;
p_slot = ctrl->slot;
dbg("p_slot = %p\n", p_slot);
while (p_slot && (p_slot->device != device)) {
tmp_slot = p_slot;
p_slot = p_slot->next;
dbg("In while loop, p_slot = %p\n", p_slot);
}
if (p_slot == NULL) {
err("ERROR: pciehp_find_slot device=0x%x\n", device);
p_slot = tmp_slot;
}
return (p_slot);
}
static inline int wait_for_ctrl_irq (struct controller *ctrl)
{
int retval = 0;
DECLARE_WAITQUEUE(wait, current);
dbg("%s : start\n", __FUNCTION__);
add_wait_queue(&ctrl->queue, &wait);
set_current_state(TASK_INTERRUPTIBLE);
if (!pciehp_poll_mode) {
/* Sleep for up to 1 second */
schedule_timeout(1*HZ);
} else
schedule_timeout(2.5*HZ);
set_current_state(TASK_RUNNING);
remove_wait_queue(&ctrl->queue, &wait);
if (signal_pending(current))
retval = -EINTR;
dbg("%s : end\n", __FUNCTION__);
return retval;
}
/* Puts node back in the resource list pointed to by head */
static inline void return_resource(struct pci_resource **head, struct pci_resource *node)
{
if (!node || !head)
return;
node->next = *head;
*head = node;
}
#define SLOT_NAME_SIZE 10
static inline void make_slot_name(char *buffer, int buffer_size, struct slot *slot)
{
snprintf(buffer, buffer_size, "%d", slot->number);
}
enum php_ctlr_type {
PCI,
ISA,
ACPI
};
typedef u8(*php_intr_callback_t) (unsigned int change_id, void *instance_id);
int pcie_init( struct controller *ctrl, struct pci_dev *pdev,
php_intr_callback_t attention_button_callback,
php_intr_callback_t switch_change_callback,
php_intr_callback_t presence_change_callback,
php_intr_callback_t power_fault_callback);
/* This has no meaning for PCI Express, as there is only 1 slot per port */
int pcie_get_ctlr_slot_config( struct controller *ctrl,
int *num_ctlr_slots,
int *first_device_num,
int *physical_slot_num,
int *updown,
int *flags);
struct hpc_ops {
int (*power_on_slot ) (struct slot *slot);
int (*power_off_slot ) (struct slot *slot);
int (*get_power_status) (struct slot *slot, u8 *status);
int (*get_attention_status) (struct slot *slot, u8 *status);
int (*set_attention_status) (struct slot *slot, u8 status);
int (*get_latch_status) (struct slot *slot, u8 *status);
int (*get_adapter_status) (struct slot *slot, u8 *status);
int (*get_max_bus_speed) (struct slot *slot, enum pci_bus_speed *speed);
int (*get_cur_bus_speed) (struct slot *slot, enum pci_bus_speed *speed);
int (*get_max_lnk_width) (struct slot *slot, enum pcie_link_width *value);
int (*get_cur_lnk_width) (struct slot *slot, enum pcie_link_width *value);
int (*query_power_fault) (struct slot *slot);
void (*green_led_on) (struct slot *slot);
void (*green_led_off) (struct slot *slot);
void (*green_led_blink) (struct slot *slot);
void (*release_ctlr) (struct controller *ctrl);
int (*check_lnk_status) (struct controller *ctrl);
};
#endif /* _PCIEHP_H */
/*
* PCI Express 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.
* Copyright (C) 2003-2004 Intel Corporation
*
* 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>, <dely.l.sy@intel.com>
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include "pciehp.h"
#include "pciehprm.h"
/* Global variables */
int pciehp_debug;
int pciehp_poll_mode;
int pciehp_poll_time;
struct controller *pciehp_ctrl_list; /* = NULL */
struct pci_func *pciehp_slot_list[256];
#define DRIVER_VERSION "0.4"
#define DRIVER_AUTHOR "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>"
#define DRIVER_DESC "PCI Express Hot Plug Controller Driver"
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
MODULE_PARM(pciehp_debug, "i");
MODULE_PARM(pciehp_poll_mode, "i");
MODULE_PARM(pciehp_poll_time, "i");
MODULE_PARM_DESC(pciehp_debug, "Debugging mode enabled or not");
MODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not");
MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds");
#define PCIE_MODULE_NAME "pciehp"
static int pcie_start_thread (void);
static int set_attention_status (struct hotplug_slot *slot, u8 value);
static int enable_slot (struct hotplug_slot *slot);
static int disable_slot (struct hotplug_slot *slot);
static int hardware_test (struct hotplug_slot *slot, u32 value);
static int get_power_status (struct hotplug_slot *slot, u8 *value);
static int get_attention_status (struct hotplug_slot *slot, u8 *value);
static int get_latch_status (struct hotplug_slot *slot, u8 *value);
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
static struct hotplug_slot_ops pciehp_hotplug_slot_ops = {
.owner = THIS_MODULE,
.set_attention_status = set_attention_status,
.enable_slot = enable_slot,
.disable_slot = disable_slot,
.hardware_test = hardware_test,
.get_power_status = get_power_status,
.get_attention_status = get_attention_status,
.get_latch_status = get_latch_status,
.get_adapter_status = get_adapter_status,
.get_max_bus_speed = get_max_bus_speed,
.get_cur_bus_speed = get_cur_bus_speed,
};
static int init_slots(struct controller *ctrl)
{
struct slot *new_slot;
u8 number_of_slots;
u8 slot_device;
u32 slot_number;
int result;
dbg("%s\n",__FUNCTION__);
number_of_slots = ctrl->num_slots;
slot_device = ctrl->slot_device_offset;
slot_number = ctrl->first_slot;
while (number_of_slots) {
new_slot = (struct slot *) kmalloc(sizeof(struct slot), GFP_KERNEL);
if (!new_slot)
return -ENOMEM;
memset(new_slot, 0, sizeof(struct slot));
new_slot->hotplug_slot = kmalloc (sizeof (struct hotplug_slot), GFP_KERNEL);
if (!new_slot->hotplug_slot) {
kfree (new_slot);
return -ENOMEM;
}
memset(new_slot->hotplug_slot, 0, sizeof (struct hotplug_slot));
new_slot->hotplug_slot->info = kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL);
if (!new_slot->hotplug_slot->info) {
kfree (new_slot->hotplug_slot);
kfree (new_slot);
return -ENOMEM;
}
memset(new_slot->hotplug_slot->info, 0, sizeof (struct hotplug_slot_info));
new_slot->hotplug_slot->name = kmalloc (SLOT_NAME_SIZE, GFP_KERNEL);
if (!new_slot->hotplug_slot->name) {
kfree (new_slot->hotplug_slot->info);
kfree (new_slot->hotplug_slot);
kfree (new_slot);
return -ENOMEM;
}
new_slot->magic = SLOT_MAGIC;
new_slot->ctrl = ctrl;
new_slot->bus = ctrl->slot_bus;
new_slot->device = slot_device;
new_slot->hpc_ops = ctrl->hpc_ops;
new_slot->number = ctrl->first_slot;
new_slot->hp_slot = slot_device - ctrl->slot_device_offset;
/* register this slot with the hotplug pci core */
new_slot->hotplug_slot->private = new_slot;
make_slot_name (new_slot->hotplug_slot->name, SLOT_NAME_SIZE, new_slot);
new_slot->hotplug_slot->ops = &pciehp_hotplug_slot_ops;
new_slot->hpc_ops->get_power_status(new_slot, &(new_slot->hotplug_slot->info->power_status));
new_slot->hpc_ops->get_attention_status(new_slot, &(new_slot->hotplug_slot->info->attention_status));
new_slot->hpc_ops->get_latch_status(new_slot, &(new_slot->hotplug_slot->info->latch_status));
new_slot->hpc_ops->get_adapter_status(new_slot, &(new_slot->hotplug_slot->info->adapter_status));
dbg("Registering bus=%x dev=%x hp_slot=%x sun=%x slot_device_offset=%x\n",
new_slot->bus, new_slot->device, new_slot->hp_slot, new_slot->number, ctrl->slot_device_offset);
result = pci_hp_register (new_slot->hotplug_slot);
if (result) {
err ("pci_hp_register failed with error %d\n", result);
kfree (new_slot->hotplug_slot->info);
kfree (new_slot->hotplug_slot->name);
kfree (new_slot->hotplug_slot);
kfree (new_slot);
return result;
}
new_slot->next = ctrl->slot;
ctrl->slot = new_slot;
number_of_slots--;
slot_device++;
slot_number += ctrl->slot_num_inc;
}
return(0);
}
static int cleanup_slots (struct controller * ctrl)
{
struct slot *old_slot, *next_slot;
old_slot = ctrl->slot;
ctrl->slot = NULL;
while (old_slot) {
next_slot = old_slot->next;
pci_hp_deregister (old_slot->hotplug_slot);
kfree(old_slot->hotplug_slot->info);
kfree(old_slot->hotplug_slot->name);
kfree(old_slot->hotplug_slot);
kfree(old_slot);
old_slot = next_slot;
}
return(0);
}
static int get_ctlr_slot_config(struct controller *ctrl)
{
int num_ctlr_slots; /* Not needed; PCI Express has 1 slot per port*/
int first_device_num; /* Not needed */
int physical_slot_num;
int updown; /* Not needed */
int rc;
int flags; /* Not needed */
rc = pcie_get_ctlr_slot_config(ctrl, &num_ctlr_slots, &first_device_num, &physical_slot_num, &updown, &flags);
if (rc) {
err("%s: get_ctlr_slot_config fail for b:d (%x:%x)\n", __FUNCTION__, ctrl->bus, ctrl->device);
return (-1);
}
ctrl->num_slots = num_ctlr_slots; /* PCI Express has 1 slot per port */
ctrl->slot_device_offset = first_device_num;
ctrl->first_slot = physical_slot_num;
ctrl->slot_num_inc = updown; /* Not needed */ /* either -1 or 1 */
dbg("%s: bus(0x%x) num_slot(0x%x) 1st_dev(0x%x) psn(0x%x) updown(%d) for b:d (%x:%x)\n",
__FUNCTION__, ctrl->slot_bus, num_ctlr_slots, first_device_num, physical_slot_num, updown,
ctrl->bus, ctrl->device);
return (0);
}
/*
* set_attention_status - Turns the Amber LED for a slot on, off or blink
*/
static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 status)
{
struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
if (slot == NULL)
return -ENODEV;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
hotplug_slot->info->attention_status = status;
slot->hpc_ops->set_attention_status(slot, status);
return 0;
}
static int enable_slot (struct hotplug_slot *hotplug_slot)
{
struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
if (slot == NULL)
return -ENODEV;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
return pciehp_enable_slot(slot);
}
static int disable_slot (struct hotplug_slot *hotplug_slot)
{
struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
if (slot == NULL)
return -ENODEV;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
return pciehp_disable_slot(slot);
}
static int hardware_test (struct hotplug_slot *hotplug_slot, u32 value)
{
return 0;
}
static int get_power_status (struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
int retval;
if (slot == NULL)
return -ENODEV;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
retval = slot->hpc_ops->get_power_status(slot, value);
if (retval < 0)
*value = hotplug_slot->info->power_status;
return 0;
}
static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
int retval;
if (slot == NULL)
return -ENODEV;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
retval = slot->hpc_ops->get_attention_status(slot, value);
if (retval < 0)
*value = hotplug_slot->info->attention_status;
return 0;
}
static int get_latch_status (struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
int retval;
if (slot == NULL)
return -ENODEV;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
retval = slot->hpc_ops->get_latch_status(slot, value);
if (retval < 0)
*value = hotplug_slot->info->latch_status;
return 0;
}
static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
int retval;
if (slot == NULL)
return -ENODEV;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
retval = slot->hpc_ops->get_adapter_status(slot, value);
if (retval < 0)
*value = hotplug_slot->info->adapter_status;
return 0;
}
static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
{
struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
int retval;
if (slot == NULL)
return -ENODEV;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
retval = slot->hpc_ops->get_max_bus_speed(slot, value);
if (retval < 0)
*value = PCI_SPEED_UNKNOWN;
return 0;
}
static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
{
struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
int retval;
if (slot == NULL)
return -ENODEV;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
retval = slot->hpc_ops->get_cur_bus_speed(slot, value);
if (retval < 0)
*value = PCI_SPEED_UNKNOWN;
return 0;
}
static int pcie_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
int rc;
struct controller *ctrl;
struct slot *t_slot;
int first_device_num = 0 ; /* first PCI device number supported by this PCIE */
int num_ctlr_slots; /* number of slots supported by this HPC */
u8 value;
ctrl = (struct controller *) kmalloc(sizeof(struct controller), GFP_KERNEL);
if (!ctrl) {
err("%s : out of memory\n", __FUNCTION__);
goto err_out_none;
}
memset(ctrl, 0, sizeof(struct controller));
dbg("%s: DRV_thread pid = %d\n", __FUNCTION__, current->pid);
rc = pcie_init(ctrl, pdev,
(php_intr_callback_t) pciehp_handle_attention_button,
(php_intr_callback_t) pciehp_handle_switch_change,
(php_intr_callback_t) pciehp_handle_presence_change,
(php_intr_callback_t) pciehp_handle_power_fault);
if (rc) {
dbg("%s: controller initialization failed\n", PCIE_MODULE_NAME);
goto err_out_free_ctrl;
}
ctrl->pci_dev = pdev;
pci_set_drvdata(pdev, ctrl);
ctrl->pci_bus = kmalloc (sizeof (*ctrl->pci_bus), GFP_KERNEL);
if (!ctrl->pci_bus) {
err("%s: out of memory\n", __FUNCTION__);
rc = -ENOMEM;
goto err_out_unmap_mmio_region;
}
dbg("%s: ctrl->pci_bus %p\n", __FUNCTION__, ctrl->pci_bus);
memcpy (ctrl->pci_bus, pdev->bus, sizeof (*ctrl->pci_bus));
ctrl->bus = pdev->bus->number; /* ctrl bus */
ctrl->slot_bus = pdev->subordinate->number; /* bus controlled by this HPC */
ctrl->device = PCI_SLOT(pdev->devfn);
ctrl->function = PCI_FUNC(pdev->devfn);
dbg("%s: ctrl bus=0x%x, device=%x, function=%x, irq=%x\n", __FUNCTION__,
ctrl->bus, ctrl->device, ctrl->function, pdev->irq);
/*
* Save configuration headers for this and subordinate PCI buses
*/
rc = get_ctlr_slot_config(ctrl);
if (rc) {
err(msg_initialization_err, rc);
goto err_out_free_ctrl_bus;
}
first_device_num = ctrl->slot_device_offset;
num_ctlr_slots = ctrl->num_slots;
/* Store PCI Config Space for all devices on this bus */
dbg("%s: Before calling pciehp_save_config, ctrl->bus %x,ctrl->slot_bus %x\n",
__FUNCTION__,ctrl->bus, ctrl->slot_bus);
rc = pciehp_save_config(ctrl, ctrl->slot_bus, num_ctlr_slots, first_device_num);
if (rc) {
err("%s: unable to save PCI configuration data, error %d\n", __FUNCTION__, rc);
goto err_out_free_ctrl_bus;
}
/* Get IO, memory, and IRQ resources for new devices */
rc = pciehprm_find_available_resources(ctrl);
ctrl->add_support = !rc;
if (rc) {
dbg("pciehprm_find_available_resources = %#x\n", rc);
err("unable to locate PCI configuration resources for hot plug add.\n");
goto err_out_free_ctrl_bus;
}
/* Setup the slot information structures */
rc = init_slots(ctrl);
if (rc) {
err(msg_initialization_err, 6);
goto err_out_free_ctrl_slot;
}
t_slot = pciehp_find_slot(ctrl, first_device_num);
dbg("%s: t_slot %p\n", __FUNCTION__, t_slot);
/* Finish setting up the hot plug ctrl device */
ctrl->next_event = 0;
if (!pciehp_ctrl_list) {
pciehp_ctrl_list = ctrl;
ctrl->next = NULL;
} else {
ctrl->next = pciehp_ctrl_list;
pciehp_ctrl_list = ctrl;
}
/* Wait for exclusive access to hardware */
down(&ctrl->crit_sect);
t_slot->hpc_ops->get_adapter_status(t_slot, &value); /* Check if slot is occupied */
dbg("%s: adpater value %x\n", __FUNCTION__, value);
if (!value) {
rc = t_slot->hpc_ops->power_off_slot(t_slot); /* Power off slot if not occupied*/
if (rc) {
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
goto err_out_free_ctrl_slot;
} else
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
}
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return 0;
err_out_free_ctrl_slot:
cleanup_slots(ctrl);
err_out_free_ctrl_bus:
kfree(ctrl->pci_bus);
err_out_unmap_mmio_region:
ctrl->hpc_ops->release_ctlr(ctrl);
err_out_free_ctrl:
kfree(ctrl);
err_out_none:
return -ENODEV;
}
static int pcie_start_thread(void)
{
int loop;
int retval = 0;
dbg("Initialize + Start the notification/polling mechanism \n");
retval = pciehp_event_start_thread();
if (retval) {
dbg("pciehp_event_start_thread() failed\n");
return retval;
}
dbg("Initialize slot lists\n");
/* One slot list for each bus in the system */
for (loop = 0; loop < 256; loop++) {
pciehp_slot_list[loop] = NULL;
}
return retval;
}
static void unload_pciehpd(void)
{
struct pci_func *next;
struct pci_func *TempSlot;
int loop;
struct controller *ctrl;
struct controller *tctrl;
struct pci_resource *res;
struct pci_resource *tres;
ctrl = pciehp_ctrl_list;
while (ctrl) {
cleanup_slots(ctrl);
res = ctrl->io_head;
while (res) {
tres = res;
res = res->next;
kfree(tres);
}
res = ctrl->mem_head;
while (res) {
tres = res;
res = res->next;
kfree(tres);
}
res = ctrl->p_mem_head;
while (res) {
tres = res;
res = res->next;
kfree(tres);
}
res = ctrl->bus_head;
while (res) {
tres = res;
res = res->next;
kfree(tres);
}
kfree (ctrl->pci_bus);
ctrl->hpc_ops->release_ctlr(ctrl);
tctrl = ctrl;
ctrl = ctrl->next;
kfree(tctrl);
}
for (loop = 0; loop < 256; loop++) {
next = pciehp_slot_list[loop];
while (next != NULL) {
res = next->io_head;
while (res) {
tres = res;
res = res->next;
kfree(tres);
}
res = next->mem_head;
while (res) {
tres = res;
res = res->next;
kfree(tres);
}
res = next->p_mem_head;
while (res) {
tres = res;
res = res->next;
kfree(tres);
}
res = next->bus_head;
while (res) {
tres = res;
res = res->next;
kfree(tres);
}
TempSlot = next;
next = next->next;
kfree(TempSlot);
}
}
/* Stop the notification mechanism */
pciehp_event_stop_thread();
}
static struct pci_device_id pcied_pci_tbl[] = {
{
.class = ((PCI_CLASS_BRIDGE_PCI << 8) | 0x00),
.class_mask = ~0,
.vendor = PCI_ANY_ID,
.device = PCI_ANY_ID,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{ /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE(pci, pcied_pci_tbl);
static struct pci_driver pcie_driver = {
.name = PCIE_MODULE_NAME,
.id_table = pcied_pci_tbl,
.probe = pcie_probe,
/* remove: pcie_remove_one, */
};
static int __init pcied_init(void)
{
int retval = 0;
#ifdef CONFIG_HOTPLUG_PCI_PCIE_POLL_EVENT_MODE
pciehp_poll_mode = 1;
#endif
retval = pcie_start_thread();
if (retval)
goto error_hpc_init;
retval = pciehprm_init(PCI);
if (!retval) {
retval = pci_module_init(&pcie_driver);
dbg("pci_module_init = %d\n", retval);
info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
}
error_hpc_init:
if (retval) {
pciehprm_cleanup();
pciehp_event_stop_thread();
} else
pciehprm_print_pirt();
return retval;
}
static void __exit pcied_cleanup(void)
{
dbg("unload_pciehpd()\n");
unload_pciehpd();
pciehprm_cleanup();
dbg("pci_unregister_driver\n");
pci_unregister_driver(&pcie_driver);
info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n");
}
module_init(pcied_init);
module_exit(pcied_cleanup);
/*
* PCI Express 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.
* Copyright (C) 2003-2004 Intel Corporation
*
* 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>, <dely.l.sy@intel.com>
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/smp_lock.h>
#include <linux/pci.h>
#include "pciehp.h"
#include "pciehprm.h"
static u32 configure_new_device(struct controller *ctrl, struct pci_func *func,
u8 behind_bridge, struct resource_lists *resources, u8 bridge_bus, u8 bridge_dev);
static int configure_new_function( struct controller *ctrl, struct pci_func *func,
u8 behind_bridge, struct resource_lists *resources, u8 bridge_bus, u8 bridge_dev);
static void interrupt_event_handler(struct controller *ctrl);
static struct semaphore event_semaphore; /* mutex for process loop (up if something to process) */
static struct semaphore event_exit; /* guard ensure thread has exited before calling it quits */
static int event_finished;
static unsigned long pushbutton_pending; /* = 0 */
u8 pciehp_handle_attention_button(u8 hp_slot, void *inst_id)
{
struct controller *ctrl = (struct controller *) inst_id;
struct slot *p_slot;
u8 rc = 0;
u8 getstatus;
struct pci_func *func;
struct event_info *taskInfo;
/* Attention Button Change */
dbg("pciehp: Attention button interrupt received.\n");
func = pciehp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0);
/* This is the structure that tells the worker thread what to do */
taskInfo = &(ctrl->event_queue[ctrl->next_event]);
p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save));
p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
ctrl->next_event = (ctrl->next_event + 1) % 10;
taskInfo->hp_slot = hp_slot;
rc++;
/*
* Button pressed - See if need to TAKE ACTION!!!
*/
info("Button pressed on Slot(%d)\n", ctrl->first_slot + hp_slot);
taskInfo->event_type = INT_BUTTON_PRESS;
if ((p_slot->state == BLINKINGON_STATE)
|| (p_slot->state == BLINKINGOFF_STATE)) {
/* Cancel if we are still blinking; this means that we press the
* attention again before the 5 sec. limit expires to cancel hot-add
* or hot-remove
*/
taskInfo->event_type = INT_BUTTON_CANCEL;
info("Button cancel on Slot(%d)\n", ctrl->first_slot + hp_slot);
} else if ((p_slot->state == POWERON_STATE)
|| (p_slot->state == POWEROFF_STATE)) {
/* Ignore if the slot is on power-on or power-off state; this
* means that the previous attention button action to hot-add or
* hot-remove is undergoing
*/
taskInfo->event_type = INT_BUTTON_IGNORE;
info("Button ignore on Slot(%d)\n", ctrl->first_slot + hp_slot);
}
if (rc)
up(&event_semaphore); /* signal event thread that new event is posted */
return 0;
}
u8 pciehp_handle_switch_change(u8 hp_slot, void *inst_id)
{
struct controller *ctrl = (struct controller *) inst_id;
struct slot *p_slot;
u8 rc = 0;
u8 getstatus;
struct pci_func *func;
struct event_info *taskInfo;
/* Switch Change */
dbg("pciehp: Switch interrupt received.\n");
func = pciehp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0);
/* This is the structure that tells the worker thread
* what to do
*/
taskInfo = &(ctrl->event_queue[ctrl->next_event]);
ctrl->next_event = (ctrl->next_event + 1) % 10;
taskInfo->hp_slot = hp_slot;
rc++;
p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save));
p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
if (!getstatus) {
/*
* Switch opened
*/
info("Latch open on Slot(%d)\n", ctrl->first_slot + hp_slot);
func->switch_save = 0;
taskInfo->event_type = INT_SWITCH_OPEN;
} else {
/*
* Switch closed
*/
info("Latch close on Slot(%d)\n", ctrl->first_slot + hp_slot);
func->switch_save = 0x10;
taskInfo->event_type = INT_SWITCH_CLOSE;
}
if (rc)
up(&event_semaphore); /* signal event thread that new event is posted */
return rc;
}
u8 pciehp_handle_presence_change(u8 hp_slot, void *inst_id)
{
struct controller *ctrl = (struct controller *) inst_id;
struct slot *p_slot;
u8 rc = 0;
struct pci_func *func;
struct event_info *taskInfo;
/* Presence Change */
dbg("pciehp: Presence/Notify input change.\n");
func = pciehp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0);
/* This is the structure that tells the worker thread
* what to do
*/
taskInfo = &(ctrl->event_queue[ctrl->next_event]);
ctrl->next_event = (ctrl->next_event + 1) % 10;
taskInfo->hp_slot = hp_slot;
rc++;
p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
/* Switch is open, assume a presence change
* Save the presence state
*/
p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save));
if (func->presence_save) {
/*
* Card Present
*/
taskInfo->event_type = INT_PRESENCE_ON;
} else {
/*
* Not Present
*/
taskInfo->event_type = INT_PRESENCE_OFF;
}
if (rc)
up(&event_semaphore); /* signal event thread that new event is posted */
return rc;
}
u8 pciehp_handle_power_fault(u8 hp_slot, void *inst_id)
{
struct controller *ctrl = (struct controller *) inst_id;
struct slot *p_slot;
u8 rc = 0;
struct pci_func *func;
struct event_info *taskInfo;
/* power fault */
dbg("pciehp: Power fault interrupt received.\n");
func = pciehp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0);
/* this is the structure that tells the worker thread
* what to do
*/
taskInfo = &(ctrl->event_queue[ctrl->next_event]);
ctrl->next_event = (ctrl->next_event + 1) % 10;
taskInfo->hp_slot = hp_slot;
rc++;
p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) {
/*
* power fault Cleared
*/
info("Power fault cleared on Slot(%d)\n", ctrl->first_slot + hp_slot);
func->status = 0x00;
taskInfo->event_type = INT_POWER_FAULT_CLEAR;
} else {
/*
* power fault
*/
info("Power fault on Slot(%d)\n", ctrl->first_slot + hp_slot);
taskInfo->event_type = INT_POWER_FAULT;
/* set power fault status for this board */
func->status = 0xFF;
info("power fault bit %x set\n", hp_slot);
}
if (rc)
up(&event_semaphore); /* signal event thread that new event is posted */
return rc;
}
/*
* sort_by_size
*
* Sorts nodes on the list by their length.
* Smallest first.
*
*/
static int sort_by_size(struct pci_resource **head)
{
struct pci_resource *current_res;
struct pci_resource *next_res;
int out_of_order = 1;
if (!(*head))
return(1);
if (!((*head)->next))
return(0);
while (out_of_order) {
out_of_order = 0;
/* Special case for swapping list head */
if (((*head)->next) &&
((*head)->length > (*head)->next->length)) {
out_of_order++;
current_res = *head;
*head = (*head)->next;
current_res->next = (*head)->next;
(*head)->next = current_res;
}
current_res = *head;
while (current_res->next && current_res->next->next) {
if (current_res->next->length > current_res->next->next->length) {
out_of_order++;
next_res = current_res->next;
current_res->next = current_res->next->next;
current_res = current_res->next;
next_res->next = current_res->next;
current_res->next = next_res;
} else
current_res = current_res->next;
}
} /* End of out_of_order loop */
return(0);
}
/*
* sort_by_max_size
*
* Sorts nodes on the list by their length.
* Largest first.
*
*/
static int sort_by_max_size(struct pci_resource **head)
{
struct pci_resource *current_res;
struct pci_resource *next_res;
int out_of_order = 1;
if (!(*head))
return(1);
if (!((*head)->next))
return(0);
while (out_of_order) {
out_of_order = 0;
/* Special case for swapping list head */
if (((*head)->next) &&
((*head)->length < (*head)->next->length)) {
out_of_order++;
current_res = *head;
*head = (*head)->next;
current_res->next = (*head)->next;
(*head)->next = current_res;
}
current_res = *head;
while (current_res->next && current_res->next->next) {
if (current_res->next->length < current_res->next->next->length) {
out_of_order++;
next_res = current_res->next;
current_res->next = current_res->next->next;
current_res = current_res->next;
next_res->next = current_res->next;
current_res->next = next_res;
} else
current_res = current_res->next;
}
} /* End of out_of_order loop */
return(0);
}
/*
* do_pre_bridge_resource_split
*
* Returns zero or one node of resources that aren't in use
*
*/
static struct pci_resource *do_pre_bridge_resource_split (struct pci_resource **head, struct pci_resource **orig_head, u32 alignment)
{
struct pci_resource *prevnode = NULL;
struct pci_resource *node;
struct pci_resource *split_node;
u32 rc;
u32 temp_dword;
dbg("do_pre_bridge_resource_split\n");
if (!(*head) || !(*orig_head))
return(NULL);
rc = pciehp_resource_sort_and_combine(head);
if (rc)
return(NULL);
if ((*head)->base != (*orig_head)->base)
return(NULL);
if ((*head)->length == (*orig_head)->length)
return(NULL);
/* If we got here, there the bridge requires some of the resource, but
* we may be able to split some off of the front
*/
node = *head;
if (node->length & (alignment -1)) {
/* this one isn't an aligned length, so we'll make a new entry
* and split it up.
*/
split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return(NULL);
temp_dword = (node->length | (alignment-1)) + 1 - alignment;
split_node->base = node->base;
split_node->length = temp_dword;
node->length -= temp_dword;
node->base += split_node->length;
/* Put it in the list */
*head = split_node;
split_node->next = node;
}
if (node->length < alignment) {
return(NULL);
}
/* Now unlink it */
if (*head == node) {
*head = node->next;
node->next = NULL;
} else {
prevnode = *head;
while (prevnode->next != node)
prevnode = prevnode->next;
prevnode->next = node->next;
node->next = NULL;
}
return(node);
}
/*
* do_bridge_resource_split
*
* Returns zero or one node of resources that aren't in use
*
*/
static struct pci_resource *do_bridge_resource_split (struct pci_resource **head, u32 alignment)
{
struct pci_resource *prevnode = NULL;
struct pci_resource *node;
u32 rc;
u32 temp_dword;
if (!(*head))
return(NULL);
rc = pciehp_resource_sort_and_combine(head);
if (rc)
return(NULL);
node = *head;
while (node->next) {
prevnode = node;
node = node->next;
kfree(prevnode);
}
if (node->length < alignment) {
kfree(node);
return(NULL);
}
if (node->base & (alignment - 1)) {
/* Short circuit if adjusted size is too small */
temp_dword = (node->base | (alignment-1)) + 1;
if ((node->length - (temp_dword - node->base)) < alignment) {
kfree(node);
return(NULL);
}
node->length -= (temp_dword - node->base);
node->base = temp_dword;
}
if (node->length & (alignment - 1)) {
/* There's stuff in use after this node */
kfree(node);
return(NULL);
}
return(node);
}
/*
* get_io_resource
*
* this function sorts the resource list by size and then
* returns the first node of "size" length that is not in the
* ISA aliasing window. If it finds a node larger than "size"
* it will split it up.
*
* size must be a power of two.
*/
static struct pci_resource *get_io_resource (struct pci_resource **head, u32 size)
{
struct pci_resource *prevnode;
struct pci_resource *node;
struct pci_resource *split_node = NULL;
u32 temp_dword;
if (!(*head))
return(NULL);
if ( pciehp_resource_sort_and_combine(head) )
return(NULL);
if ( sort_by_size(head) )
return(NULL);
for (node = *head; node; node = node->next) {
if (node->length < size)
continue;
if (node->base & (size - 1)) {
/* this one isn't base aligned properly
so we'll make a new entry and split it up */
temp_dword = (node->base | (size-1)) + 1;
/*/ Short circuit if adjusted size is too small */
if ((node->length - (temp_dword - node->base)) < size)
continue;
split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return(NULL);
split_node->base = node->base;
split_node->length = temp_dword - node->base;
node->base = temp_dword;
node->length -= split_node->length;
/* Put it in the list */
split_node->next = node->next;
node->next = split_node;
} /* End of non-aligned base */
/* Don't need to check if too small since we already did */
if (node->length > size) {
/* this one is longer than we need
so we'll make a new entry and split it up */
split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return(NULL);
split_node->base = node->base + size;
split_node->length = node->length - size;
node->length = size;
/* Put it in the list */
split_node->next = node->next;
node->next = split_node;
} /* End of too big on top end */
/* For IO make sure it's not in the ISA aliasing space */
if (node->base & 0x300L)
continue;
/* If we got here, then it is the right size
Now take it out of the list */
if (*head == node) {
*head = node->next;
} else {
prevnode = *head;
while (prevnode->next != node)
prevnode = prevnode->next;
prevnode->next = node->next;
}
node->next = NULL;
/* Stop looping */
break;
}
return(node);
}
/*
* get_max_resource
*
* Gets the largest node that is at least "size" big from the
* list pointed to by head. It aligns the node on top and bottom
* to "size" alignment before returning it.
* J.I. modified to put max size limits of; 64M->32M->16M->8M->4M->1M
* This is needed to avoid allocating entire ACPI _CRS res to one child bridge/slot.
*/
static struct pci_resource *get_max_resource (struct pci_resource **head, u32 size)
{
struct pci_resource *max;
struct pci_resource *temp;
struct pci_resource *split_node;
u32 temp_dword;
u32 max_size[] = { 0x4000000, 0x2000000, 0x1000000, 0x0800000, 0x0400000, 0x0200000, 0x0100000, 0x00 };
int i;
if (!(*head))
return(NULL);
if (pciehp_resource_sort_and_combine(head))
return(NULL);
if (sort_by_max_size(head))
return(NULL);
for (max = *head;max; max = max->next) {
/* If not big enough we could probably just bail,
instead we'll continue to the next. */
if (max->length < size)
continue;
if (max->base & (size - 1)) {
/* this one isn't base aligned properly
so we'll make a new entry and split it up */
temp_dword = (max->base | (size-1)) + 1;
/* Short circuit if adjusted size is too small */
if ((max->length - (temp_dword - max->base)) < size)
continue;
split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return(NULL);
split_node->base = max->base;
split_node->length = temp_dword - max->base;
max->base = temp_dword;
max->length -= split_node->length;
/* Put it next in the list */
split_node->next = max->next;
max->next = split_node;
}
if ((max->base + max->length) & (size - 1)) {
/* this one isn't end aligned properly at the top
so we'll make a new entry and split it up */
split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return(NULL);
temp_dword = ((max->base + max->length) & ~(size - 1));
split_node->base = temp_dword;
split_node->length = max->length + max->base
- split_node->base;
max->length -= split_node->length;
/* Put it in the list */
split_node->next = max->next;
max->next = split_node;
}
/* Make sure it didn't shrink too much when we aligned it */
if (max->length < size)
continue;
for ( i = 0; max_size[i] > size; i++) {
if (max->length > max_size[i]) {
split_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
break; /* return (NULL); */
split_node->base = max->base + max_size[i];
split_node->length = max->length - max_size[i];
max->length = max_size[i];
/* Put it next in the list */
split_node->next = max->next;
max->next = split_node;
break;
}
}
/* Now take it out of the list */
temp = (struct pci_resource*) *head;
if (temp == max) {
*head = max->next;
} else {
while (temp && temp->next != max) {
temp = temp->next;
}
temp->next = max->next;
}
max->next = NULL;
return(max);
}
/* If we get here, we couldn't find one */
return(NULL);
}
/*
* get_resource
*
* this function sorts the resource list by size and then
* returns the first node of "size" length. If it finds a node
* larger than "size" it will split it up.
*
* size must be a power of two.
*/
static struct pci_resource *get_resource (struct pci_resource **head, u32 size)
{
struct pci_resource *prevnode;
struct pci_resource *node;
struct pci_resource *split_node;
u32 temp_dword;
if (!(*head))
return(NULL);
if ( pciehp_resource_sort_and_combine(head) )
return(NULL);
if ( sort_by_size(head) )
return(NULL);
for (node = *head; node; node = node->next) {
dbg("%s: req_size =0x%x node=%p, base=0x%x, length=0x%x\n",
__FUNCTION__, size, node, node->base, node->length);
if (node->length < size)
continue;
if (node->base & (size - 1)) {
dbg("%s: not aligned\n", __FUNCTION__);
/* this one isn't base aligned properly
so we'll make a new entry and split it up */
temp_dword = (node->base | (size-1)) + 1;
/* Short circuit if adjusted size is too small */
if ((node->length - (temp_dword - node->base)) < size)
continue;
split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return(NULL);
split_node->base = node->base;
split_node->length = temp_dword - node->base;
node->base = temp_dword;
node->length -= split_node->length;
/* Put it in the list */
split_node->next = node->next;
node->next = split_node;
} /* End of non-aligned base */
/* Don't need to check if too small since we already did */
if (node->length > size) {
dbg("%s: too big\n", __FUNCTION__);
/* this one is longer than we need
so we'll make a new entry and split it up */
split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return(NULL);
split_node->base = node->base + size;
split_node->length = node->length - size;
node->length = size;
/* Put it in the list */
split_node->next = node->next;
node->next = split_node;
} /* End of too big on top end */
dbg("%s: got one!!!\n", __FUNCTION__);
/* If we got here, then it is the right size
Now take it out of the list */
if (*head == node) {
*head = node->next;
} else {
prevnode = *head;
while (prevnode->next != node)
prevnode = prevnode->next;
prevnode->next = node->next;
}
node->next = NULL;
/* Stop looping */
break;
}
return(node);
}
/*
* pciehp_resource_sort_and_combine
*
* Sorts all of the nodes in the list in ascending order by
* their base addresses. Also does garbage collection by
* combining adjacent nodes.
*
* returns 0 if success
*/
int pciehp_resource_sort_and_combine(struct pci_resource **head)
{
struct pci_resource *node1;
struct pci_resource *node2;
int out_of_order = 1;
dbg("%s: head = %p, *head = %p\n", __FUNCTION__, head, *head);
if (!(*head))
return(1);
dbg("*head->next = %p\n",(*head)->next);
if (!(*head)->next)
return(0); /* only one item on the list, already sorted! */
dbg("*head->base = 0x%x\n",(*head)->base);
dbg("*head->next->base = 0x%x\n",(*head)->next->base);
while (out_of_order) {
out_of_order = 0;
/* Special case for swapping list head */
if (((*head)->next) &&
((*head)->base > (*head)->next->base)) {
node1 = *head;
(*head) = (*head)->next;
node1->next = (*head)->next;
(*head)->next = node1;
out_of_order++;
}
node1 = (*head);
while (node1->next && node1->next->next) {
if (node1->next->base > node1->next->next->base) {
out_of_order++;
node2 = node1->next;
node1->next = node1->next->next;
node1 = node1->next;
node2->next = node1->next;
node1->next = node2;
} else
node1 = node1->next;
}
} /* End of out_of_order loop */
node1 = *head;
while (node1 && node1->next) {
if ((node1->base + node1->length) == node1->next->base) {
/* Combine */
dbg("8..\n");
node1->length += node1->next->length;
node2 = node1->next;
node1->next = node1->next->next;
kfree(node2);
} else
node1 = node1->next;
}
return(0);
}
/**
* pciehp_slot_create - Creates a node and adds it to the proper bus.
* @busnumber - bus where new node is to be located
*
* Returns pointer to the new node or NULL if unsuccessful
*/
struct pci_func *pciehp_slot_create(u8 busnumber)
{
struct pci_func *new_slot;
struct pci_func *next;
dbg("%s: busnumber %x\n", __FUNCTION__, busnumber);
new_slot = (struct pci_func *) kmalloc(sizeof(struct pci_func), GFP_KERNEL);
if (new_slot == NULL) {
return(new_slot);
}
memset(new_slot, 0, sizeof(struct pci_func));
new_slot->next = NULL;
new_slot->configured = 1;
if (pciehp_slot_list[busnumber] == NULL) {
pciehp_slot_list[busnumber] = new_slot;
} else {
next = pciehp_slot_list[busnumber];
while (next->next != NULL)
next = next->next;
next->next = new_slot;
}
return(new_slot);
}
/*
* slot_remove - Removes a node from the linked list of slots.
* @old_slot: slot to remove
*
* Returns 0 if successful, !0 otherwise.
*/
static int slot_remove(struct pci_func * old_slot)
{
struct pci_func *next;
if (old_slot == NULL)
return(1);
next = pciehp_slot_list[old_slot->bus];
if (next == NULL) {
return(1);
}
if (next == old_slot) {
pciehp_slot_list[old_slot->bus] = old_slot->next;
pciehp_destroy_board_resources(old_slot);
kfree(old_slot);
return(0);
}
while ((next->next != old_slot) && (next->next != NULL)) {
next = next->next;
}
if (next->next == old_slot) {
next->next = old_slot->next;
pciehp_destroy_board_resources(old_slot);
kfree(old_slot);
return(0);
} else
return(2);
}
/**
* bridge_slot_remove - Removes a node from the linked list of slots.
* @bridge: bridge to remove
*
* Returns 0 if successful, !0 otherwise.
*/
static int bridge_slot_remove(struct pci_func *bridge)
{
u8 subordinateBus, secondaryBus;
u8 tempBus;
struct pci_func *next;
if (bridge == NULL)
return(1);
secondaryBus = (bridge->config_space[0x06] >> 8) & 0xFF;
subordinateBus = (bridge->config_space[0x06] >> 16) & 0xFF;
for (tempBus = secondaryBus; tempBus <= subordinateBus; tempBus++) {
next = pciehp_slot_list[tempBus];
while (!slot_remove(next)) {
next = pciehp_slot_list[tempBus];
}
}
next = pciehp_slot_list[bridge->bus];
if (next == NULL) {
return(1);
}
if (next == bridge) {
pciehp_slot_list[bridge->bus] = bridge->next;
kfree(bridge);
return(0);
}
while ((next->next != bridge) && (next->next != NULL)) {
next = next->next;
}
if (next->next == bridge) {
next->next = bridge->next;
kfree(bridge);
return(0);
} else
return(2);
}
/**
* pciehp_slot_find - Looks for a node by bus, and device, multiple functions accessed
* @bus: bus to find
* @device: device to find
* @index: is 0 for first function found, 1 for the second...
*
* Returns pointer to the node if successful, %NULL otherwise.
*/
struct pci_func *pciehp_slot_find(u8 bus, u8 device, u8 index)
{
int found = -1;
struct pci_func *func;
func = pciehp_slot_list[bus];
dbg("%s: bus %x device %x index %x\n",
__FUNCTION__, bus, device, index);
if (func != NULL) {
dbg("%s: func-> bus %x device %x function %x pci_dev %p\n",
__FUNCTION__, func->bus, func->device, func->function,
func->pci_dev);
} else
dbg("%s: func == NULL\n", __FUNCTION__);
if ((func == NULL) || ((func->device == device) && (index == 0)))
return(func);
if (func->device == device)
found++;
while (func->next != NULL) {
func = func->next;
dbg("%s: In while loop, func-> bus %x device %x function %x pci_dev %p\n",
__FUNCTION__, func->bus, func->device, func->function,
func->pci_dev);
if (func->device == device)
found++;
dbg("%s: while loop, found %d, index %d\n", __FUNCTION__,
found, index);
if ((found == index) || (func->function == index)) {
dbg("%s: Found bus %x dev %x func %x\n", __FUNCTION__,
func->bus, func->device, func->function);
return(func);
}
}
return(NULL);
}
static int is_bridge(struct pci_func * func)
{
/* Check the header type */
if (((func->config_space[0x03] >> 16) & 0xFF) == 0x01)
return 1;
else
return 0;
}
/* The following routines constitute the bulk of the
hotplug controller logic
*/
/**
* board_added - Called after a board has been added to the system.
*
* Turns power on for the board
* Configures board
*
*/
static u32 board_added(struct pci_func * func, struct controller * ctrl)
{
u8 hp_slot;
int index;
u32 temp_register = 0xFFFFFFFF;
u32 retval, rc = 0;
struct pci_func *new_func = NULL;
struct slot *p_slot;
struct resource_lists res_lists;
p_slot = pciehp_find_slot(ctrl, func->device);
hp_slot = func->device - ctrl->slot_device_offset;
dbg("%s: func->device, slot_offset, hp_slot = %d, %d ,%d\n", __FUNCTION__, func->device, ctrl->slot_device_offset, hp_slot);
/* Wait for exclusive access to hardware */
down(&ctrl->crit_sect);
/* Power on slot */
rc = p_slot->hpc_ops->power_on_slot(p_slot);
if (rc)
return -1;
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
p_slot->hpc_ops->green_led_blink(p_slot);
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
/* Wait for ~1 second */
dbg("%s: before long_delay\n", __FUNCTION__);
wait_for_ctrl_irq (ctrl);
dbg("%s: afterlong_delay\n", __FUNCTION__);
/* Make this to check for link training status */
rc = p_slot->hpc_ops->check_lnk_status(ctrl);
if (rc) {
err("%s: Failed to check link status\n", __FUNCTION__);
return -1;
}
dbg("%s: func status = %x\n", __FUNCTION__, func->status);
/* Check for a power fault */
if (func->status == 0xFF) {
/* power fault occurred, but it was benign */
temp_register = 0xFFFFFFFF;
dbg("%s: temp register set to %x by power fault\n", __FUNCTION__, temp_register);
rc = POWER_FAILURE;
func->status = 0;
} else {
/* Get vendor/device ID u32 */
rc = pci_bus_read_config_dword (ctrl->pci_dev->subordinate, PCI_DEVFN(func->device, func->function),
PCI_VENDOR_ID, &temp_register);
dbg("%s: pci_bus_read_config_dword returns %d\n", __FUNCTION__, rc);
dbg("%s: temp_register is %x\n", __FUNCTION__, temp_register);
if (rc != 0) {
/* Something's wrong here */
temp_register = 0xFFFFFFFF;
dbg("%s: temp register set to %x by error\n", __FUNCTION__, temp_register);
}
/* Preset return code. It will be changed later if things go okay. */
rc = NO_ADAPTER_PRESENT;
}
/* All F's is an empty slot or an invalid board */
if (temp_register != 0xFFFFFFFF) { /* Check for a board in the slot */
res_lists.io_head = ctrl->io_head;
res_lists.mem_head = ctrl->mem_head;
res_lists.p_mem_head = ctrl->p_mem_head;
res_lists.bus_head = ctrl->bus_head;
res_lists.irqs = NULL;
rc = configure_new_device(ctrl, func, 0, &res_lists, 0, 0);
dbg("%s: back from configure_new_device\n", __FUNCTION__);
ctrl->io_head = res_lists.io_head;
ctrl->mem_head = res_lists.mem_head;
ctrl->p_mem_head = res_lists.p_mem_head;
ctrl->bus_head = res_lists.bus_head;
pciehp_resource_sort_and_combine(&(ctrl->mem_head));
pciehp_resource_sort_and_combine(&(ctrl->p_mem_head));
pciehp_resource_sort_and_combine(&(ctrl->io_head));
pciehp_resource_sort_and_combine(&(ctrl->bus_head));
if (rc) {
/* Wait for exclusive access to hardware */
down(&ctrl->crit_sect);
/* turn off slot, turn on Amber LED, turn off Green LED */
retval = p_slot->hpc_ops->power_off_slot(p_slot);
/* In PCI Express, just power off slot */
if (retval) {
err("%s: Issue of Slot Power Off command failed\n", __FUNCTION__);
return retval;
}
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
p_slot->hpc_ops->green_led_off(p_slot);
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
/* turn on Amber LED */
retval = p_slot->hpc_ops->set_attention_status(p_slot, 1);
if (retval) {
err("%s: Issue of Set Attention Led command failed\n", __FUNCTION__);
return retval;
}
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return(rc);
}
pciehp_save_slot_config(ctrl, func);
func->status = 0;
func->switch_save = 0x10;
func->is_a_board = 0x01;
/* next, we will instantiate the linux pci_dev structures
* (with appropriate driver notification, if already present)
*/
index = 0;
do {
new_func = pciehp_slot_find(ctrl->slot_bus, func->device, index++);
if (new_func && !new_func->pci_dev) {
dbg("%s:call pci_hp_configure_dev, func %x\n",
__FUNCTION__, index);
pciehp_configure_device(ctrl, new_func);
}
} while (new_func);
/* Wait for exclusive access to hardware */
down(&ctrl->crit_sect);
p_slot->hpc_ops->green_led_on(p_slot);
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
} else {
/* Wait for exclusive access to hardware */
down(&ctrl->crit_sect);
/* turn off slot, turn on Amber LED, turn off Green LED */
retval = p_slot->hpc_ops->power_off_slot(p_slot);
/* In PCI Express, just power off slot */
if (retval) {
err("%s: Issue of Slot Power Off command failed\n", __FUNCTION__);
return retval;
}
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
p_slot->hpc_ops->green_led_off(p_slot);
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
/* turn on Amber LED */
retval = p_slot->hpc_ops->set_attention_status(p_slot, 1);
if (retval) {
err("%s: Issue of Set Attention Led command failed\n", __FUNCTION__);
return retval;
}
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return(rc);
}
return 0;
}
/**
* remove_board - Turns off slot and LED's
*
*/
static u32 remove_board(struct pci_func *func, struct controller *ctrl)
{
int index;
u8 skip = 0;
u8 device;
u8 hp_slot;
u32 rc;
struct resource_lists res_lists;
struct pci_func *temp_func;
struct slot *p_slot;
if (func == NULL)
return(1);
if (pciehp_unconfigure_device(func))
return(1);
device = func->device;
hp_slot = func->device - ctrl->slot_device_offset;
p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
dbg("In %s, hp_slot = %d\n", __FUNCTION__, hp_slot);
if ((ctrl->add_support) &&
!(func->bus_head || func->mem_head || func->p_mem_head || func->io_head)) {
/* Here we check to see if we've saved any of the board's
* resources already. If so, we'll skip the attempt to
* determine what's being used.
*/
index = 0;
temp_func = func;
while ((temp_func = pciehp_slot_find(temp_func->bus, temp_func->device, index++))) {
if (temp_func->bus_head || temp_func->mem_head
|| temp_func->p_mem_head || temp_func->io_head) {
skip = 1;
break;
}
}
if (!skip)
rc = pciehp_save_used_resources(ctrl, func, DISABLE_CARD);
}
/* Change status to shutdown */
if (func->is_a_board)
func->status = 0x01;
func->configured = 0;
/* Wait for exclusive access to hardware */
down(&ctrl->crit_sect);
/* power off slot */
rc = p_slot->hpc_ops->power_off_slot(p_slot);
if (rc) {
err("%s: Issue of Slot Disable command failed\n", __FUNCTION__);
return rc;
}
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
/* turn off Green LED */
p_slot->hpc_ops->green_led_off(p_slot);
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
if (ctrl->add_support) {
while (func) {
res_lists.io_head = ctrl->io_head;
res_lists.mem_head = ctrl->mem_head;
res_lists.p_mem_head = ctrl->p_mem_head;
res_lists.bus_head = ctrl->bus_head;
dbg("Returning resources to ctlr lists for (B/D/F) = (%#x/%#x/%#x)\n",
func->bus, func->device, func->function);
pciehp_return_board_resources(func, &res_lists);
ctrl->io_head = res_lists.io_head;
ctrl->mem_head = res_lists.mem_head;
ctrl->p_mem_head = res_lists.p_mem_head;
ctrl->bus_head = res_lists.bus_head;
pciehp_resource_sort_and_combine(&(ctrl->mem_head));
pciehp_resource_sort_and_combine(&(ctrl->p_mem_head));
pciehp_resource_sort_and_combine(&(ctrl->io_head));
pciehp_resource_sort_and_combine(&(ctrl->bus_head));
if (is_bridge(func)) {
dbg("PCI Bridge Hot-Remove s:b:d:f(%02x:%02x:%02x:%02x)\n",
ctrl->seg, func->bus, func->device, func->function);
bridge_slot_remove(func);
} else
dbg("PCI Function Hot-Remove s:b:d:f(%02x:%02x:%02x:%02x)\n",
ctrl->seg, func->bus, func->device, func->function);
slot_remove(func);
func = pciehp_slot_find(ctrl->slot_bus, device, 0);
}
/* Setup slot structure with entry for empty slot */
func = pciehp_slot_create(ctrl->slot_bus);
if (func == NULL) {
return(1);
}
func->bus = ctrl->slot_bus;
func->device = device;
func->function = 0;
func->configured = 0;
func->switch_save = 0x10;
func->is_a_board = 0;
}
return 0;
}
static void pushbutton_helper_thread (unsigned long data)
{
pushbutton_pending = data;
up(&event_semaphore);
}
/* this is the main worker thread */
static int event_thread(void* data)
{
struct controller *ctrl;
lock_kernel();
daemonize("pciehpd_event");
unlock_kernel();
while (1) {
dbg("!!!!event_thread sleeping\n");
down_interruptible (&event_semaphore);
dbg("event_thread woken finished = %d\n", event_finished);
if (event_finished || signal_pending(current))
break;
/* Do stuff here */
if (pushbutton_pending)
pciehp_pushbutton_thread(pushbutton_pending);
else
for (ctrl = pciehp_ctrl_list; ctrl; ctrl=ctrl->next)
interrupt_event_handler(ctrl);
}
dbg("event_thread signals exit\n");
up(&event_exit);
return 0;
}
int pciehp_event_start_thread (void)
{
int pid;
/* initialize our semaphores */
init_MUTEX_LOCKED(&event_exit);
event_finished=0;
init_MUTEX_LOCKED(&event_semaphore);
pid = kernel_thread(event_thread, 0, 0);
if (pid < 0) {
err ("Can't start up our event thread\n");
return -1;
}
dbg("Our event thread pid = %d\n", pid);
return 0;
}
void pciehp_event_stop_thread (void)
{
event_finished = 1;
dbg("event_thread finish command given\n");
up(&event_semaphore);
dbg("wait for event_thread to exit\n");
down(&event_exit);
}
static int update_slot_info (struct slot *slot)
{
struct hotplug_slot_info *info;
/* char buffer[SLOT_NAME_SIZE]; */
int result;
info = kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL);
if (!info)
return -ENOMEM;
/* make_slot_name (&buffer[0], SLOT_NAME_SIZE, slot); */
slot->hpc_ops->get_power_status(slot, &(info->power_status));
slot->hpc_ops->get_attention_status(slot, &(info->attention_status));
slot->hpc_ops->get_latch_status(slot, &(info->latch_status));
slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status));
/* result = pci_hp_change_slot_info(buffer, info); */
result = pci_hp_change_slot_info(slot->hotplug_slot, info);
kfree (info);
return result;
}
static void interrupt_event_handler(struct controller *ctrl)
{
int loop = 0;
int change = 1;
struct pci_func *func;
u8 hp_slot;
u8 getstatus;
struct slot *p_slot;
while (change) {
change = 0;
for (loop = 0; loop < 10; loop++) {
if (ctrl->event_queue[loop].event_type != 0) {
hp_slot = ctrl->event_queue[loop].hp_slot;
func = pciehp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0);
p_slot = pciehp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
dbg("hp_slot %d, func %p, p_slot %p\n", hp_slot, func, p_slot);
if (ctrl->event_queue[loop].event_type == INT_BUTTON_CANCEL) {
dbg("button cancel\n");
del_timer(&p_slot->task_event);
switch (p_slot->state) {
case BLINKINGOFF_STATE:
/* Wait for exclusive access to hardware */
down(&ctrl->crit_sect);
p_slot->hpc_ops->green_led_on(p_slot);
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
p_slot->hpc_ops->set_attention_status(p_slot, 0);
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
break;
case BLINKINGON_STATE:
/* Wait for exclusive access to hardware */
down(&ctrl->crit_sect);
p_slot->hpc_ops->green_led_off(p_slot);
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
p_slot->hpc_ops->set_attention_status(p_slot, 0);
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
break;
default:
warn("Not a valid state\n");
return;
}
info(msg_button_cancel, p_slot->number);
p_slot->state = STATIC_STATE;
}
/* ***********Button Pressed (No action on 1st press...) */
else if (ctrl->event_queue[loop].event_type == INT_BUTTON_PRESS) {
dbg("Button pressed\n");
p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
if (getstatus) {
/* slot is on */
dbg("slot is on\n");
p_slot->state = BLINKINGOFF_STATE;
info(msg_button_off, p_slot->number);
} else {
/* slot is off */
dbg("slot is off\n");
p_slot->state = BLINKINGON_STATE;
info(msg_button_on, p_slot->number);
}
/* Wait for exclusive access to hardware */
down(&ctrl->crit_sect);
/* blink green LED and turn off amber */
p_slot->hpc_ops->green_led_blink(p_slot);
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
p_slot->hpc_ops->set_attention_status(p_slot, 0);
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
init_timer(&p_slot->task_event);
p_slot->task_event.expires = jiffies + 5 * HZ; /* 5 second delay */
p_slot->task_event.function = (void (*)(unsigned long)) pushbutton_helper_thread;
p_slot->task_event.data = (unsigned long) p_slot;
dbg("add_timer p_slot = %p\n", (void *) p_slot);
add_timer(&p_slot->task_event);
}
/***********POWER FAULT********************/
else if (ctrl->event_queue[loop].event_type == INT_POWER_FAULT) {
dbg("power fault\n");
/* Wait for exclusive access to hardware */
down(&ctrl->crit_sect);
p_slot->hpc_ops->set_attention_status(p_slot, 1);
p_slot->hpc_ops->green_led_off(p_slot);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
} else {
/* refresh notification */
if (p_slot)
update_slot_info(p_slot);
}
ctrl->event_queue[loop].event_type = 0;
change = 1;
}
} /* End of FOR loop */
}
return;
}
/**
* pciehp_pushbutton_thread
*
* Scheduled procedure to handle blocking stuff for the pushbuttons
* Handles all pending events and exits.
*
*/
void pciehp_pushbutton_thread (unsigned long slot)
{
struct slot *p_slot = (struct slot *) slot;
u8 getstatus;
int rc;
pushbutton_pending = 0;
if (!p_slot) {
dbg("%s: Error! slot NULL\n", __FUNCTION__);
return;
}
p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
if (getstatus) {
p_slot->state = POWEROFF_STATE;
dbg("In power_down_board, b:d(%x:%x)\n", p_slot->bus, p_slot->device);
if (pciehp_disable_slot(p_slot)) {
/* Wait for exclusive access to hardware */
down(&p_slot->ctrl->crit_sect);
/* Turn on the Attention LED */
rc = p_slot->hpc_ops->set_attention_status(p_slot, 1);
if (rc) {
err("%s: Issue of Set Atten Indicator On command failed\n", __FUNCTION__);
return;
}
/* Wait for the command to complete */
wait_for_ctrl_irq (p_slot->ctrl);
/* Done with exclusive hardware access */
up(&p_slot->ctrl->crit_sect);
}
p_slot->state = STATIC_STATE;
} else {
p_slot->state = POWERON_STATE;
dbg("In add_board, b:d(%x:%x)\n", p_slot->bus, p_slot->device);
if (pciehp_enable_slot(p_slot)) {
/* Wait for exclusive access to hardware */
down(&p_slot->ctrl->crit_sect);
/* Turn off the green LED */
rc = p_slot->hpc_ops->set_attention_status(p_slot, 1);
if (rc) {
err("%s: Issue of Set Atten Indicator On command failed\n", __FUNCTION__);
return;
}
/* Wait for the command to complete */
wait_for_ctrl_irq (p_slot->ctrl);
p_slot->hpc_ops->green_led_off(p_slot);
/* Wait for the command to complete */
wait_for_ctrl_irq (p_slot->ctrl);
/* Done with exclusive hardware access */
up(&p_slot->ctrl->crit_sect);
}
p_slot->state = STATIC_STATE;
}
return;
}
int pciehp_enable_slot (struct slot *p_slot)
{
u8 getstatus = 0;
int rc;
struct pci_func *func;
func = pciehp_slot_find(p_slot->bus, p_slot->device, 0);
if (!func) {
dbg("%s: Error! slot NULL\n", __FUNCTION__);
return (1);
}
/* Check to see if (latch closed, card present, power off) */
down(&p_slot->ctrl->crit_sect);
rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
if (rc || !getstatus) {
info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number);
up(&p_slot->ctrl->crit_sect);
return (0);
}
rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
if (rc || !getstatus) {
info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number);
up(&p_slot->ctrl->crit_sect);
return (0);
}
rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
if (rc || getstatus) {
info("%s: already enabled on slot(%x)\n", __FUNCTION__, p_slot->number);
up(&p_slot->ctrl->crit_sect);
return (0);
}
up(&p_slot->ctrl->crit_sect);
slot_remove(func);
func = pciehp_slot_create(p_slot->bus);
if (func == NULL)
return (1);
func->bus = p_slot->bus;
func->device = p_slot->device;
func->function = 0;
func->configured = 0;
func->is_a_board = 1;
/* We have to save the presence info for these slots */
p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save));
p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
func->switch_save = !getstatus? 0x10:0;
rc = board_added(func, p_slot->ctrl);
if (rc) {
if (is_bridge(func))
bridge_slot_remove(func);
else
slot_remove(func);
/* Setup slot structure with entry for empty slot */
func = pciehp_slot_create(p_slot->bus);
if (func == NULL)
return (1); /* Out of memory */
func->bus = p_slot->bus;
func->device = p_slot->device;
func->function = 0;
func->configured = 0;
func->is_a_board = 1;
/* We have to save the presence info for these slots */
p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save));
p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
func->switch_save = !getstatus? 0x10:0;
}
if (p_slot)
update_slot_info(p_slot);
return rc;
}
int pciehp_disable_slot (struct slot *p_slot)
{
u8 class_code, header_type, BCR;
u8 index = 0;
u8 getstatus = 0;
u32 rc = 0;
int ret = 0;
unsigned int devfn;
struct pci_bus *pci_bus = p_slot->ctrl->pci_dev->subordinate;
struct pci_func *func;
if (!p_slot->ctrl)
return (1);
/* Check to see if (latch closed, card present, power on) */
down(&p_slot->ctrl->crit_sect);
ret = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
if (ret || !getstatus) {
info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number);
up(&p_slot->ctrl->crit_sect);
return (0);
}
ret = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
if (ret || !getstatus) {
info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number);
up(&p_slot->ctrl->crit_sect);
return (0);
}
ret = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
if (ret || !getstatus) {
info("%s: already disabled slot(%x)\n", __FUNCTION__, p_slot->number);
up(&p_slot->ctrl->crit_sect);
return (0);
}
up(&p_slot->ctrl->crit_sect);
func = pciehp_slot_find(p_slot->bus, p_slot->device, index++);
/* Make sure there are no video controllers here
* for all func of p_slot
*/
while (func && !rc) {
pci_bus->number = func->bus;
devfn = PCI_DEVFN(func->device, func->function);
/* Check the Class Code */
rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code);
if (rc)
return rc;
if (class_code == PCI_BASE_CLASS_DISPLAY) {
/* Display/Video adapter (not supported) */
rc = REMOVE_NOT_SUPPORTED;
} else {
/* See if it's a bridge */
rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
if (rc)
return rc;
/* If it's a bridge, check the VGA Enable bit */
if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_BRIDGE_CONTROL, &BCR);
if (rc)
return rc;
/* If the VGA Enable bit is set, remove isn't supported */
if (BCR & PCI_BRIDGE_CTL_VGA) {
rc = REMOVE_NOT_SUPPORTED;
}
}
}
func = pciehp_slot_find(p_slot->bus, p_slot->device, index++);
}
func = pciehp_slot_find(p_slot->bus, p_slot->device, 0);
if ((func != NULL) && !rc) {
rc = remove_board(func, p_slot->ctrl);
} else if (!rc)
rc = 1;
if (p_slot)
update_slot_info(p_slot);
return(rc);
}
/**
* configure_new_device - Configures the PCI header information of one board.
*
* @ctrl: pointer to controller structure
* @func: pointer to function structure
* @behind_bridge: 1 if this is a recursive call, 0 if not
* @resources: pointer to set of resource lists
*
* Returns 0 if success
*
*/
static u32 configure_new_device (struct controller * ctrl, struct pci_func * func,
u8 behind_bridge, struct resource_lists * resources, u8 bridge_bus, u8 bridge_dev)
{
u8 temp_byte, function, max_functions, stop_it;
int rc;
u32 ID;
struct pci_func *new_slot;
struct pci_bus lpci_bus, *pci_bus;
int index;
new_slot = func;
dbg("%s\n", __FUNCTION__);
memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus));
pci_bus = &lpci_bus;
pci_bus->number = func->bus;
/* Check for Multi-function device */
rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(func->device, func->function), 0x0E, &temp_byte);
if (rc) {
dbg("%s: rc = %d\n", __FUNCTION__, rc);
return rc;
}
if (temp_byte & 0x80) /* Multi-function device */
max_functions = 8;
else
max_functions = 1;
function = 0;
do {
rc = configure_new_function(ctrl, new_slot, behind_bridge, resources, bridge_bus, bridge_dev);
if (rc) {
dbg("configure_new_function failed %d\n",rc);
index = 0;
while (new_slot) {
new_slot = pciehp_slot_find(new_slot->bus, new_slot->device, index++);
if (new_slot)
pciehp_return_board_resources(new_slot, resources);
}
return(rc);
}
function++;
stop_it = 0;
/* The following loop skips to the next present function
* and creates a board structure
*/
while ((function < max_functions) && (!stop_it)) {
pci_bus_read_config_dword(pci_bus, PCI_DEVFN(func->device, function), 0x00, &ID);
if (ID == 0xFFFFFFFF) { /* There's nothing there. */
function++;
} else { /* There's something there */
/* Setup slot structure. */
new_slot = pciehp_slot_create(func->bus);
if (new_slot == NULL) {
/* Out of memory */
return(1);
}
new_slot->bus = func->bus;
new_slot->device = func->device;
new_slot->function = function;
new_slot->is_a_board = 1;
new_slot->status = 0;
stop_it++;
}
}
} while (function < max_functions);
dbg("returning from configure_new_device\n");
return 0;
}
/*
* Configuration logic that involves the hotplug data structures and
* their bookkeeping
*/
/**
* configure_new_function - Configures the PCI header information of one device
*
* @ctrl: pointer to controller structure
* @func: pointer to function structure
* @behind_bridge: 1 if this is a recursive call, 0 if not
* @resources: pointer to set of resource lists
*
* Calls itself recursively for bridged devices.
* Returns 0 if success
*
*/
static int configure_new_function (struct controller * ctrl, struct pci_func * func,
u8 behind_bridge, struct resource_lists *resources, u8 bridge_bus, u8 bridge_dev)
{
int cloop;
u8 temp_byte;
u8 device;
u8 class_code;
u16 temp_word;
u32 rc;
u32 temp_register;
u32 base;
u32 ID;
unsigned int devfn;
struct pci_resource *mem_node;
struct pci_resource *p_mem_node;
struct pci_resource *io_node;
struct pci_resource *bus_node;
struct pci_resource *hold_mem_node;
struct pci_resource *hold_p_mem_node;
struct pci_resource *hold_IO_node;
struct pci_resource *hold_bus_node;
struct irq_mapping irqs;
struct pci_func *new_slot;
struct pci_bus lpci_bus, *pci_bus;
struct resource_lists temp_resources;
memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus));
pci_bus = &lpci_bus;
pci_bus->number = func->bus;
devfn = PCI_DEVFN(func->device, func->function);
/* Check for Bridge */
rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &temp_byte);
if (rc)
return rc;
dbg("%s: bus %x dev %x func %x temp_byte = %x\n", __FUNCTION__,
func->bus, func->device, func->function, temp_byte);
if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */
/* set Primary bus */
dbg("set Primary bus = 0x%x\n", func->bus);
rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_PRIMARY_BUS, func->bus);
if (rc)
return rc;
/* find range of busses to use */
bus_node = get_max_resource(&resources->bus_head, 1L);
/* If we don't have any busses to allocate, we can't continue */
if (!bus_node) {
err("Got NO bus resource to use\n");
return -ENOMEM;
}
dbg("Got ranges of buses to use: base:len=0x%x:%x\n", bus_node->base, bus_node->length);
/* set Secondary bus */
dbg("set Secondary bus = 0x%x\n", temp_byte);
dbg("func->bus %x\n", func->bus);
temp_byte = (u8)bus_node->base;
dbg("set Secondary bus = 0x%x\n", temp_byte);
rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, temp_byte);
if (rc)
return rc;
/* set subordinate bus */
temp_byte = (u8)(bus_node->base + bus_node->length - 1);
dbg("set subordinate bus = 0x%x\n", temp_byte);
rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte);
if (rc)
return rc;
/* Set HP parameters (Cache Line Size, Latency Timer) */
rc = pciehprm_set_hpp(ctrl, func, PCI_HEADER_TYPE_BRIDGE);
if (rc)
return rc;
/* Setup the IO, memory, and prefetchable windows */
io_node = get_max_resource(&(resources->io_head), 0x1000L);
if (io_node) {
dbg("io_node(base, len, next) (%x, %x, %p)\n", io_node->base, io_node->length, io_node->next);
}
mem_node = get_max_resource(&(resources->mem_head), 0x100000L);
if (mem_node) {
dbg("mem_node(base, len, next) (%x, %x, %p)\n", mem_node->base, mem_node->length, mem_node->next);
}
if (resources->p_mem_head)
p_mem_node = get_max_resource(&(resources->p_mem_head), 0x100000L);
else {
/*
* In some platform implementation, MEM and PMEM are not
* distinguished, and hence ACPI _CRS has only MEM entries
* for both MEM and PMEM.
*/
dbg("using MEM for PMEM\n");
p_mem_node = get_max_resource(&(resources->mem_head), 0x100000L);
}
if (p_mem_node) {
dbg("p_mem_node(base, len, next) (%x, %x, %p)\n", p_mem_node->base, p_mem_node->length, p_mem_node->next);
}
/* set up the IRQ info */
if (!resources->irqs) {
irqs.barber_pole = 0;
irqs.interrupt[0] = 0;
irqs.interrupt[1] = 0;
irqs.interrupt[2] = 0;
irqs.interrupt[3] = 0;
irqs.valid_INT = 0;
} else {
irqs.barber_pole = resources->irqs->barber_pole;
irqs.interrupt[0] = resources->irqs->interrupt[0];
irqs.interrupt[1] = resources->irqs->interrupt[1];
irqs.interrupt[2] = resources->irqs->interrupt[2];
irqs.interrupt[3] = resources->irqs->interrupt[3];
irqs.valid_INT = resources->irqs->valid_INT;
}
/* set up resource lists that are now aligned on top and bottom
* for anything behind the bridge.
*/
temp_resources.bus_head = bus_node;
temp_resources.io_head = io_node;
temp_resources.mem_head = mem_node;
temp_resources.p_mem_head = p_mem_node;
temp_resources.irqs = &irqs;
/* Make copies of the nodes we are going to pass down so that
* if there is a problem,we can just use these to free resources
*/
hold_bus_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
hold_IO_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
hold_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
hold_p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!hold_bus_node || !hold_IO_node || !hold_mem_node || !hold_p_mem_node) {
if (hold_bus_node)
kfree(hold_bus_node);
if (hold_IO_node)
kfree(hold_IO_node);
if (hold_mem_node)
kfree(hold_mem_node);
if (hold_p_mem_node)
kfree(hold_p_mem_node);
return(1);
}
memcpy(hold_bus_node, bus_node, sizeof(struct pci_resource));
bus_node->base += 1;
bus_node->length -= 1;
bus_node->next = NULL;
/* If we have IO resources copy them and fill in the bridge's
* IO range registers
*/
if (io_node) {
memcpy(hold_IO_node, io_node, sizeof(struct pci_resource));
io_node->next = NULL;
/* set IO base and Limit registers */
RES_CHECK(io_node->base, 8);
temp_byte = (u8)(io_node->base >> 8);
rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_BASE, temp_byte);
RES_CHECK(io_node->base + io_node->length - 1, 8);
temp_byte = (u8)((io_node->base + io_node->length - 1) >> 8);
rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_LIMIT, temp_byte);
} else {
kfree(hold_IO_node);
hold_IO_node = NULL;
}
/* If we have memory resources copy them and fill in the bridge's
* memory range registers. Otherwise, fill in the range
* registers with values that disable them.
*/
if (mem_node) {
memcpy(hold_mem_node, mem_node, sizeof(struct pci_resource));
mem_node->next = NULL;
/* set Mem base and Limit registers */
RES_CHECK(mem_node->base, 16);
temp_word = (u32)(mem_node->base >> 16);
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_BASE, temp_word);
RES_CHECK(mem_node->base + mem_node->length - 1, 16);
temp_word = (u32)((mem_node->base + mem_node->length - 1) >> 16);
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
} else {
temp_word = 0xFFFF;
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_BASE, temp_word);
temp_word = 0x0000;
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
kfree(hold_mem_node);
hold_mem_node = NULL;
}
/* If we have prefetchable memory resources copy them and
* fill in the bridge's memory range registers. Otherwise,
* fill in the range registers with values that disable them.
*/
if (p_mem_node) {
memcpy(hold_p_mem_node, p_mem_node, sizeof(struct pci_resource));
p_mem_node->next = NULL;
/* set Pre Mem base and Limit registers */
RES_CHECK(p_mem_node->base, 16);
temp_word = (u32)(p_mem_node->base >> 16);
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word);
RES_CHECK(p_mem_node->base + p_mem_node->length - 1, 16);
temp_word = (u32)((p_mem_node->base + p_mem_node->length - 1) >> 16);
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
} else {
temp_word = 0xFFFF;
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word);
temp_word = 0x0000;
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
kfree(hold_p_mem_node);
hold_p_mem_node = NULL;
}
/* Adjust this to compensate for extra adjustment in first loop */
irqs.barber_pole--;
rc = 0;
/* Here we actually find the devices and configure them */
for (device = 0; (device <= 0x1F) && !rc; device++) {
irqs.barber_pole = (irqs.barber_pole + 1) & 0x03;
ID = 0xFFFFFFFF;
pci_bus->number = hold_bus_node->base;
pci_bus_read_config_dword (pci_bus, PCI_DEVFN(device, 0), PCI_VENDOR_ID, &ID);
pci_bus->number = func->bus;
if (ID != 0xFFFFFFFF) { /* device Present */
/* Setup slot structure. */
new_slot = pciehp_slot_create(hold_bus_node->base);
if (new_slot == NULL) {
/* Out of memory */
rc = -ENOMEM;
continue;
}
new_slot->bus = hold_bus_node->base;
new_slot->device = device;
new_slot->function = 0;
new_slot->is_a_board = 1;
new_slot->status = 0;
rc = configure_new_device(ctrl, new_slot, 1, &temp_resources, func->bus, func->device);
dbg("configure_new_device rc=0x%x\n",rc);
} /* End of IF (device in slot?) */
} /* End of FOR loop */
if (rc) {
pciehp_destroy_resource_list(&temp_resources);
return_resource(&(resources->bus_head), hold_bus_node);
return_resource(&(resources->io_head), hold_IO_node);
return_resource(&(resources->mem_head), hold_mem_node);
return_resource(&(resources->p_mem_head), hold_p_mem_node);
return(rc);
}
/* save the interrupt routing information */
if (resources->irqs) {
resources->irqs->interrupt[0] = irqs.interrupt[0];
resources->irqs->interrupt[1] = irqs.interrupt[1];
resources->irqs->interrupt[2] = irqs.interrupt[2];
resources->irqs->interrupt[3] = irqs.interrupt[3];
resources->irqs->valid_INT = irqs.valid_INT;
} else if (!behind_bridge) {
/* We need to hook up the interrupts here */
for (cloop = 0; cloop < 4; cloop++) {
if (irqs.valid_INT & (0x01 << cloop)) {
rc = pciehp_set_irq(func->bus, func->device,
0x0A + cloop, irqs.interrupt[cloop]);
if (rc) {
pciehp_destroy_resource_list (&temp_resources);
return_resource(&(resources->bus_head), hold_bus_node);
return_resource(&(resources->io_head), hold_IO_node);
return_resource(&(resources->mem_head), hold_mem_node);
return_resource(&(resources->p_mem_head), hold_p_mem_node);
return rc;
}
}
} /* end of for loop */
}
/* Return unused bus resources
* First use the temporary node to store information for the board
*/
if (hold_bus_node && bus_node && temp_resources.bus_head) {
hold_bus_node->length = bus_node->base - hold_bus_node->base;
hold_bus_node->next = func->bus_head;
func->bus_head = hold_bus_node;
temp_byte = (u8)(temp_resources.bus_head->base - 1);
/* set subordinate bus */
dbg("re-set subordinate bus = 0x%x\n", temp_byte);
rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte);
if (temp_resources.bus_head->length == 0) {
kfree(temp_resources.bus_head);
temp_resources.bus_head = NULL;
} else {
dbg("return bus res of b:d(0x%x:%x) base:len(0x%x:%x)\n",
func->bus, func->device, temp_resources.bus_head->base, temp_resources.bus_head->length);
return_resource(&(resources->bus_head), temp_resources.bus_head);
}
}
/* If we have IO space available and there is some left,
* return the unused portion
*/
if (hold_IO_node && temp_resources.io_head) {
io_node = do_pre_bridge_resource_split(&(temp_resources.io_head),
&hold_IO_node, 0x1000);
/* Check if we were able to split something off */
if (io_node) {
hold_IO_node->base = io_node->base + io_node->length;
RES_CHECK(hold_IO_node->base, 8);
temp_byte = (u8)((hold_IO_node->base) >> 8);
rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_BASE, temp_byte);
return_resource(&(resources->io_head), io_node);
}
io_node = do_bridge_resource_split(&(temp_resources.io_head), 0x1000);
/* Check if we were able to split something off */
if (io_node) {
/* First use the temporary node to store information for the board */
hold_IO_node->length = io_node->base - hold_IO_node->base;
/* If we used any, add it to the board's list */
if (hold_IO_node->length) {
hold_IO_node->next = func->io_head;
func->io_head = hold_IO_node;
RES_CHECK(io_node->base - 1, 8);
temp_byte = (u8)((io_node->base - 1) >> 8);
rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_LIMIT, temp_byte);
return_resource(&(resources->io_head), io_node);
} else {
/* it doesn't need any IO */
temp_byte = 0x00;
rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_LIMIT, temp_byte);
return_resource(&(resources->io_head), io_node);
kfree(hold_IO_node);
}
} else {
/* it used most of the range */
hold_IO_node->next = func->io_head;
func->io_head = hold_IO_node;
}
} else if (hold_IO_node) {
/* it used the whole range */
hold_IO_node->next = func->io_head;
func->io_head = hold_IO_node;
}
/* If we have memory space available and there is some left,
* return the unused portion
*/
if (hold_mem_node && temp_resources.mem_head) {
mem_node = do_pre_bridge_resource_split(&(temp_resources.mem_head), &hold_mem_node, 0x100000L);
/* Check if we were able to split something off */
if (mem_node) {
hold_mem_node->base = mem_node->base + mem_node->length;
RES_CHECK(hold_mem_node->base, 16);
temp_word = (u32)((hold_mem_node->base) >> 16);
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_BASE, temp_word);
return_resource(&(resources->mem_head), mem_node);
}
mem_node = do_bridge_resource_split(&(temp_resources.mem_head), 0x100000L);
/* Check if we were able to split something off */
if (mem_node) {
/* First use the temporary node to store information for the board */
hold_mem_node->length = mem_node->base - hold_mem_node->base;
if (hold_mem_node->length) {
hold_mem_node->next = func->mem_head;
func->mem_head = hold_mem_node;
/* configure end address */
RES_CHECK(mem_node->base - 1, 16);
temp_word = (u32)((mem_node->base - 1) >> 16);
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
/* Return unused resources to the pool */
return_resource(&(resources->mem_head), mem_node);
} else {
/* it doesn't need any Mem */
temp_word = 0x0000;
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
return_resource(&(resources->mem_head), mem_node);
kfree(hold_mem_node);
}
} else {
/* it used most of the range */
hold_mem_node->next = func->mem_head;
func->mem_head = hold_mem_node;
}
} else if (hold_mem_node) {
/* it used the whole range */
hold_mem_node->next = func->mem_head;
func->mem_head = hold_mem_node;
}
/* If we have prefetchable memory space available and there is some
* left at the end, return the unused portion
*/
if (hold_p_mem_node && temp_resources.p_mem_head) {
p_mem_node = do_pre_bridge_resource_split(&(temp_resources.p_mem_head),
&hold_p_mem_node, 0x100000L);
/* Check if we were able to split something off */
if (p_mem_node) {
hold_p_mem_node->base = p_mem_node->base + p_mem_node->length;
RES_CHECK(hold_p_mem_node->base, 16);
temp_word = (u32)((hold_p_mem_node->base) >> 16);
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word);
return_resource(&(resources->p_mem_head), p_mem_node);
}
p_mem_node = do_bridge_resource_split(&(temp_resources.p_mem_head), 0x100000L);
/* Check if we were able to split something off */
if (p_mem_node) {
/* First use the temporary node to store information for the board */
hold_p_mem_node->length = p_mem_node->base - hold_p_mem_node->base;
/* If we used any, add it to the board's list */
if (hold_p_mem_node->length) {
hold_p_mem_node->next = func->p_mem_head;
func->p_mem_head = hold_p_mem_node;
RES_CHECK(p_mem_node->base - 1, 16);
temp_word = (u32)((p_mem_node->base - 1) >> 16);
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
return_resource(&(resources->p_mem_head), p_mem_node);
} else {
/* it doesn't need any PMem */
temp_word = 0x0000;
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
return_resource(&(resources->p_mem_head), p_mem_node);
kfree(hold_p_mem_node);
}
} else {
/* it used the most of the range */
hold_p_mem_node->next = func->p_mem_head;
func->p_mem_head = hold_p_mem_node;
}
} else if (hold_p_mem_node) {
/* it used the whole range */
hold_p_mem_node->next = func->p_mem_head;
func->p_mem_head = hold_p_mem_node;
}
/* We should be configuring an IRQ and the bridge's base address
* registers if it needs them. Although we have never seen such
* a device
*/
pciehprm_enable_card(ctrl, func, PCI_HEADER_TYPE_BRIDGE);
dbg("PCI Bridge Hot-Added s:b:d:f(%02x:%02x:%02x:%02x)\n", ctrl->seg, func->bus, func->device, func->function);
} else if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_NORMAL) {
/* Standard device */
u64 base64;
rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code);
if (class_code == PCI_BASE_CLASS_DISPLAY)
return (DEVICE_TYPE_NOT_SUPPORTED);
/* Figure out IO and memory needs */
for (cloop = PCI_BASE_ADDRESS_0; cloop <= PCI_BASE_ADDRESS_5; cloop += 4) {
temp_register = 0xFFFFFFFF;
rc = pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register);
rc = pci_bus_read_config_dword(pci_bus, devfn, cloop, &temp_register);
dbg("Bar[%x]=0x%x on bus:dev:func(0x%x:%x:%x)\n", cloop, temp_register,
func->bus, func->device, func->function);
if (!temp_register)
continue;
base64 = 0L;
if (temp_register & PCI_BASE_ADDRESS_SPACE_IO) {
/* Map IO */
/* set base = amount of IO space */
base = temp_register & 0xFFFFFFFC;
base = ~base + 1;
dbg("NEED IO length(0x%x)\n", base);
io_node = get_io_resource(&(resources->io_head),(ulong)base);
/* allocate the resource to the board */
if (io_node) {
dbg("Got IO base=0x%x(length=0x%x)\n", io_node->base, io_node->length);
base = (u32)io_node->base;
io_node->next = func->io_head;
func->io_head = io_node;
} else {
err("Got NO IO resource(length=0x%x)\n", base);
return -ENOMEM;
}
} else { /* map MEM */
int prefetchable = 1;
struct pci_resource **res_node = &func->p_mem_head;
char *res_type_str = "PMEM";
u32 temp_register2;
if (!(temp_register & PCI_BASE_ADDRESS_MEM_PREFETCH)) {
prefetchable = 0;
res_node = &func->mem_head;
res_type_str++;
}
base = temp_register & 0xFFFFFFF0;
base = ~base + 1;
switch (temp_register & PCI_BASE_ADDRESS_MEM_TYPE_MASK) {
case PCI_BASE_ADDRESS_MEM_TYPE_32:
dbg("NEED 32 %s bar=0x%x(length=0x%x)\n", res_type_str, temp_register, base);
if (prefetchable && resources->p_mem_head)
mem_node=get_resource(&(resources->p_mem_head), (ulong)base);
else {
if (prefetchable)
dbg("using MEM for PMEM\n");
mem_node=get_resource(&(resources->mem_head), (ulong)base);
}
/* allocate the resource to the board */
if (mem_node) {
base = (u32)mem_node->base;
mem_node->next = *res_node;
*res_node = mem_node;
dbg("Got 32 %s base=0x%x(length=0x%x)\n", res_type_str, mem_node->base,
mem_node->length);
} else {
err("Got NO 32 %s resource(length=0x%x)\n", res_type_str, base);
return -ENOMEM;
}
break;
case PCI_BASE_ADDRESS_MEM_TYPE_64:
rc = pci_bus_read_config_dword(pci_bus, devfn, cloop+4, &temp_register2);
dbg("NEED 64 %s bar=0x%x:%x(length=0x%x)\n", res_type_str, temp_register2,
temp_register, base);
if (prefetchable && resources->p_mem_head)
mem_node = get_resource(&(resources->p_mem_head), (ulong)base);
else {
if (prefetchable)
dbg("using MEM for PMEM\n");
mem_node = get_resource(&(resources->mem_head), (ulong)base);
}
/* allocate the resource to the board */
if (mem_node) {
base64 = mem_node->base;
mem_node->next = *res_node;
*res_node = mem_node;
dbg("Got 64 %s base=0x%x:%x(length=%x)\n", res_type_str, (u32)(base64 >> 32),
(u32)base64, mem_node->length);
} else {
err("Got NO 64 %s resource(length=0x%x)\n", res_type_str, base);
return -ENOMEM;
}
break;
default:
dbg("reserved BAR type=0x%x\n", temp_register);
break;
}
}
if (base64) {
rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, (u32)base64);
cloop += 4;
base64 >>= 32;
if (base64) {
dbg("%s: high dword of base64(0x%x) set to 0\n", __FUNCTION__, (u32)base64);
base64 = 0x0L;
}
rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, (u32)base64);
} else {
rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, base);
}
} /* End of base register loop */
/* disable ROM base Address */
temp_word = 0x00L;
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_ROM_ADDRESS, temp_word);
/* Set HP parameters (Cache Line Size, Latency Timer) */
rc = pciehprm_set_hpp(ctrl, func, PCI_HEADER_TYPE_NORMAL);
if (rc)
return rc;
pciehprm_enable_card(ctrl, func, PCI_HEADER_TYPE_NORMAL);
dbg("PCI function Hot-Added s:b:d:f(%02x:%02x:%02x:%02x)\n", ctrl->seg, func->bus, func->device,
func->function);
} /* End of Not-A-Bridge else */
else {
/* It's some strange type of PCI adapter (Cardbus?) */
return(DEVICE_TYPE_NOT_SUPPORTED);
}
func->configured = 1;
return 0;
}
/*
* PCI Express PCI Hot Plug Driver
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
* Copyright (C) 2003-2004 Intel Corporation
*
* 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>,<dely.l.sy@intel.com>
*
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/pci.h>
#include <asm/system.h>
#include "pciehp.h"
#ifdef DEBUG
#define DBG_K_TRACE_ENTRY ((unsigned int)0x00000001) /* On function entry */
#define DBG_K_TRACE_EXIT ((unsigned int)0x00000002) /* On function exit */
#define DBG_K_INFO ((unsigned int)0x00000004) /* Info messages */
#define DBG_K_ERROR ((unsigned int)0x00000008) /* Error messages */
#define DBG_K_TRACE (DBG_K_TRACE_ENTRY|DBG_K_TRACE_EXIT)
#define DBG_K_STANDARD (DBG_K_INFO|DBG_K_ERROR|DBG_K_TRACE)
/* Redefine this flagword to set debug level */
#define DEBUG_LEVEL DBG_K_STANDARD
#define DEFINE_DBG_BUFFER char __dbg_str_buf[256];
#define DBG_PRINT( dbg_flags, args... ) \
do { \
if ( DEBUG_LEVEL & ( dbg_flags ) ) \
{ \
int len; \
len = sprintf( __dbg_str_buf, "%s:%d: %s: ", \
__FILE__, __LINE__, __FUNCTION__ ); \
sprintf( __dbg_str_buf + len, args ); \
printk( KERN_NOTICE "%s\n", __dbg_str_buf ); \
} \
} while (0)
#define DBG_ENTER_ROUTINE DBG_PRINT (DBG_K_TRACE_ENTRY, "%s", "[Entry]");
#define DBG_LEAVE_ROUTINE DBG_PRINT (DBG_K_TRACE_EXIT, "%s", "[Exit]");
#else
#define DEFINE_DBG_BUFFER
#define DBG_ENTER_ROUTINE
#define DBG_LEAVE_ROUTINE
#endif /* DEBUG */
struct ctrl_reg {
u8 cap_id;
u8 nxt_ptr;
u16 cap_reg;
u32 dev_cap;
u16 dev_ctrl;
u16 dev_status;
u32 lnk_cap;
u16 lnk_ctrl;
u16 lnk_status;
u32 slot_cap;
u16 slot_ctrl;
u16 slot_status;
u16 root_ctrl;
u16 rsvp;
u32 root_status;
} __attribute__ ((packed));
/* offsets to the controller registers based on the above structure layout */
enum ctrl_offsets {
PCIECAPID = offsetof(struct ctrl_reg, cap_id),
NXTCAPPTR = offsetof(struct ctrl_reg, nxt_ptr),
CAPREG = offsetof(struct ctrl_reg, cap_reg),
DEVCAP = offsetof(struct ctrl_reg, dev_cap),
DEVCTRL = offsetof(struct ctrl_reg, dev_ctrl),
DEVSTATUS = offsetof(struct ctrl_reg, dev_status),
LNKCAP = offsetof(struct ctrl_reg, lnk_cap),
LNKCTRL = offsetof(struct ctrl_reg, lnk_ctrl),
LNKSTATUS = offsetof(struct ctrl_reg, lnk_status),
SLOTCAP = offsetof(struct ctrl_reg, slot_cap),
SLOTCTRL = offsetof(struct ctrl_reg, slot_ctrl),
SLOTSTATUS = offsetof(struct ctrl_reg, slot_status),
ROOTCTRL = offsetof(struct ctrl_reg, root_ctrl),
ROOTSTATUS = offsetof(struct ctrl_reg, root_status),
};
static int pcie_cap_base = 0; /* Base of the PCI Express capability item structure */
#define PCIE_CAP_ID ( pcie_cap_base + PCIECAPID )
#define NXT_CAP_PTR ( pcie_cap_base + NXTCAPPTR )
#define CAP_REG ( pcie_cap_base + CAPREG )
#define DEV_CAP ( pcie_cap_base + DEVCAP )
#define DEV_CTRL ( pcie_cap_base + DEVCTRL )
#define DEV_STATUS ( pcie_cap_base + DEVSTATUS )
#define LNK_CAP ( pcie_cap_base + LNKCAP )
#define LNK_CTRL ( pcie_cap_base + LNKCTRL )
#define LNK_STATUS ( pcie_cap_base + LNKSTATUS )
#define SLOT_CAP ( pcie_cap_base + SLOTCAP )
#define SLOT_CTRL ( pcie_cap_base + SLOTCTRL )
#define SLOT_STATUS ( pcie_cap_base + SLOTSTATUS )
#define ROOT_CTRL ( pcie_cap_base + ROOTCTRL )
#define ROOT_STATUS ( pcie_cap_base + ROOTSTATUS )
#define hp_register_read_word(pdev, reg , value) \
pci_read_config_word(pdev, reg, &value)
#define hp_register_read_dword(pdev, reg , value) \
pci_read_config_dword(pdev, reg, &value)
#define hp_register_write_word(pdev, reg , value) \
pci_write_config_word(pdev, reg, value)
#define hp_register_dwrite_word(pdev, reg , value) \
pci_write_config_dword(pdev, reg, value)
/* Field definitions in PCI Express Capabilities Register */
#define CAP_VER 0x000F
#define DEV_PORT_TYPE 0x00F0
#define SLOT_IMPL 0x0100
#define MSG_NUM 0x3E00
/* Device or Port Type */
#define NAT_ENDPT 0x00
#define LEG_ENDPT 0x01
#define ROOT_PORT 0x04
#define UP_STREAM 0x05
#define DN_STREAM 0x06
#define PCIE_PCI_BRDG 0x07
#define PCI_PCIE_BRDG 0x10
/* Field definitions in Device Capabilities Register */
#define DATTN_BUTTN_PRSN 0x1000
#define DATTN_LED_PRSN 0x2000
#define DPWR_LED_PRSN 0x4000
/* Field definitions in Link Capabilities Register */
#define MAX_LNK_SPEED 0x000F
#define MAX_LNK_WIDTH 0x03F0
/* Link Width Encoding */
#define LNK_X1 0x01
#define LNK_X2 0x02
#define LNK_X4 0x04
#define LNK_X8 0x08
#define LNK_X12 0x0C
#define LNK_X16 0x10
#define LNK_X32 0x20
/*Field definitions of Link Status Register */
#define LNK_SPEED 0x000F
#define NEG_LINK_WD 0x03F0
#define LNK_TRN_ERR 0x0400
#define LNK_TRN 0x0800
#define SLOT_CLK_CONF 0x1000
/* Field definitions in Slot Capabilities Register */
#define ATTN_BUTTN_PRSN 0x00000001
#define PWR_CTRL_PRSN 0x00000002
#define MRL_SENS_PRSN 0x00000004
#define ATTN_LED_PRSN 0x00000008
#define PWR_LED_PRSN 0x00000010
#define HP_SUPR_RM 0x00000020
#define HP_CAP 0x00000040
#define SLOT_PWR_VALUE 0x000003F8
#define SLOT_PWR_LIMIT 0x00000C00
#define PSN 0xFFF80000 /* PSN: Physical Slot Number */
/* Field definitions in Slot Control Register */
#define ATTN_BUTTN_ENABLE 0x0001
#define PWR_FAULT_DETECT_ENABLE 0x0002
#define MRL_DETECT_ENABLE 0x0004
#define PRSN_DETECT_ENABLE 0x0008
#define CMD_CMPL_INTR_ENABLE 0x0010
#define HP_INTR_ENABLE 0x0020
#define ATTN_LED_CTRL 0x00C0
#define PWR_LED_CTRL 0x0300
#define PWR_CTRL 0x0400
/* Attention indicator and Power indicator states */
#define LED_ON 0x01
#define LED_BLINK 0x10
#define LED_OFF 0x11
/* Power Control Command */
#define POWER_ON 0
#define POWER_OFF 0x0400
/* Field definitions in Slot Status Register */
#define ATTN_BUTTN_PRESSED 0x0001
#define PWR_FAULT_DETECTED 0x0002
#define MRL_SENS_CHANGED 0x0004
#define PRSN_DETECT_CHANGED 0x0008
#define CMD_COMPLETED 0x0010
#define MRL_STATE 0x0020
#define PRSN_STATE 0x0040
struct php_ctlr_state_s {
struct php_ctlr_state_s *pnext;
struct pci_dev *pci_dev;
unsigned int irq;
unsigned long flags; /* spinlock's */
u32 slot_device_offset;
u32 num_slots;
struct timer_list int_poll_timer; /* Added for poll event */
php_intr_callback_t attention_button_callback;
php_intr_callback_t switch_change_callback;
php_intr_callback_t presence_change_callback;
php_intr_callback_t power_fault_callback;
void *callback_instance_id;
struct ctrl_reg *creg; /* Ptr to controller register space */
};
static spinlock_t hpc_event_lock;
DEFINE_DBG_BUFFER /* Debug string buffer for entire HPC defined here */
static struct php_ctlr_state_s *php_ctlr_list_head = 0; /* HPC state linked list */
static int ctlr_seq_num = 0; /* Controller sequence # */
static spinlock_t list_lock;
static irqreturn_t pcie_isr(int IRQ, void *dev_id, struct pt_regs *regs);
static void start_int_poll_timer(struct php_ctlr_state_s *php_ctlr, int seconds);
/* This is the interrupt polling timeout function. */
static void int_poll_timeout(unsigned long lphp_ctlr)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *)lphp_ctlr;
DBG_ENTER_ROUTINE
if ( !php_ctlr ) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return;
}
/* Poll for interrupt events. regs == NULL => polling */
pcie_isr( 0, (void *)php_ctlr, NULL );
init_timer(&php_ctlr->int_poll_timer);
if (!pciehp_poll_time)
pciehp_poll_time = 2; /* reset timer to poll in 2 secs if user doesn't specify at module installation*/
start_int_poll_timer(php_ctlr, pciehp_poll_time);
return;
}
/* This function starts the interrupt polling timer. */
static void start_int_poll_timer(struct php_ctlr_state_s *php_ctlr, int seconds)
{
if (!php_ctlr) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return;
}
if ( ( seconds <= 0 ) || ( seconds > 60 ) )
seconds = 2; /* Clamp to sane value */
php_ctlr->int_poll_timer.function = &int_poll_timeout;
php_ctlr->int_poll_timer.data = (unsigned long)php_ctlr; /* Instance data */
php_ctlr->int_poll_timer.expires = jiffies + seconds * HZ;
add_timer(&php_ctlr->int_poll_timer);
return;
}
static int pcie_write_cmd(struct slot *slot, u16 cmd)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
int retval = 0;
u16 slot_status;
DBG_ENTER_ROUTINE
dbg("%s : Enter\n", __FUNCTION__);
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status);
if (retval) {
err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__);
return retval;
}
dbg("%s : hp_register_read_word SLOT_STATUS %x\n", __FUNCTION__, slot_status);
if ((slot_status & CMD_COMPLETED) == CMD_COMPLETED ) {
/* After 1 sec and CMD_COMPLETED still not set, just proceed forward to issue
the next command according to spec. Just print out the error message */
dbg("%s : CMD_COMPLETED not clear after 1 sec.\n", __FUNCTION__);
}
retval = hp_register_write_word(php_ctlr->pci_dev, SLOT_CTRL, cmd);
if (retval) {
err("%s : hp_register_write_word SLOT_CTRL failed\n", __FUNCTION__);
return retval;
}
dbg("%s : hp_register_write_word SLOT_CTRL %x\n", __FUNCTION__, cmd);
dbg("%s : Exit\n", __FUNCTION__);
DBG_LEAVE_ROUTINE
return retval;
}
static int hpc_check_lnk_status(struct controller *ctrl)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) ctrl->hpc_ctlr_handle;
u16 lnk_status;
int retval = 0;
DBG_ENTER_ROUTINE
if (!ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
retval = hp_register_read_word(php_ctlr->pci_dev, LNK_STATUS, lnk_status);
if (retval) {
err("%s : hp_register_read_word LNK_STATUS failed\n", __FUNCTION__);
return retval;
}
if ( (lnk_status & (LNK_TRN | LNK_TRN_ERR)) == 0x0C00) {
err("%s : Link Training Error occurs \n", __FUNCTION__);
retval = -1;
return retval;
}
DBG_LEAVE_ROUTINE
return retval;
}
static int hpc_get_attention_status(struct slot *slot, u8 *status)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
u16 slot_ctrl;
u8 atten_led_state;
int retval = 0;
DBG_ENTER_ROUTINE
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl);
if (retval) {
err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
return retval;
}
dbg("%s: SLOT_CTRL %x, value read %x\n", __FUNCTION__,SLOT_CTRL, slot_ctrl);
atten_led_state = (slot_ctrl & ATTN_LED_CTRL) >> 6;
switch (atten_led_state) {
case 0:
*status = 0xFF; /* Reserved */
break;
case 1:
*status = 1; /* On */
break;
case 2:
*status = 2; /* Blink */
break;
case 3:
*status = 0; /* Off */
break;
default:
*status = 0xFF;
break;
}
DBG_LEAVE_ROUTINE
return 0;
}
static int hpc_get_power_status(struct slot * slot, u8 *status)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
u16 slot_ctrl;
u8 pwr_state;
int retval = 0;
DBG_ENTER_ROUTINE
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl);
if (retval) {
err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
return retval;
}
dbg("%s: SLOT_CTRL %x value read %x\n", __FUNCTION__, SLOT_CTRL, slot_ctrl);
pwr_state = (slot_ctrl & PWR_CTRL) >> 10;
switch (pwr_state) {
case 0:
*status = 1;
break;
case 1:
*status = 0;
break;
default:
*status = 0xFF;
break;
}
DBG_LEAVE_ROUTINE
return retval;
}
static int hpc_get_latch_status(struct slot *slot, u8 *status)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
u16 slot_status;
int retval = 0;
DBG_ENTER_ROUTINE
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status);
if (retval) {
err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__);
return retval;
}
*status = (((slot_status & MRL_STATE) >> 5) == 0) ? 0 : 1;
DBG_LEAVE_ROUTINE
return 0;
}
static int hpc_get_adapter_status(struct slot *slot, u8 *status)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
u16 slot_status;
u8 card_state;
int retval = 0;
DBG_ENTER_ROUTINE
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status);
if (retval) {
err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__);
return retval;
}
card_state = (u8)((slot_status & PRSN_STATE) >> 6);
*status = (card_state == 1) ? 1 : 0;
DBG_LEAVE_ROUTINE
return 0;
}
static int hpc_query_power_fault(struct slot * slot)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
u16 slot_status;
u8 pwr_fault;
int retval = 0;
u8 status;
DBG_ENTER_ROUTINE
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status);
if (retval) {
err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__);
return retval;
}
pwr_fault = (u8)((slot_status & PWR_FAULT_DETECTED) >> 1);
status = (pwr_fault != 1) ? 1 : 0;
DBG_LEAVE_ROUTINE
/* Note: Logic 0 => fault */
return status;
}
static int hpc_set_attention_status(struct slot *slot, u8 value)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
u16 slot_cmd = 0;
u16 slot_ctrl;
int rc = 0;
dbg("%s: \n", __FUNCTION__);
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
if (slot->hp_slot >= php_ctlr->num_slots) {
err("%s: Invalid HPC slot number!\n", __FUNCTION__);
return -1;
}
rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl);
if (rc) {
err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
return rc;
}
dbg("%s : hp_register_read_word SLOT_CTRL %x\n", __FUNCTION__, slot_ctrl);
switch (value) {
case 0 : /* turn off */
slot_cmd = (slot_ctrl & ~ATTN_LED_CTRL) | 0x00C0;
break;
case 1: /* turn on */
slot_cmd = (slot_ctrl & ~ATTN_LED_CTRL) | 0x0040;
break;
case 2: /* turn blink */
slot_cmd = (slot_ctrl & ~ATTN_LED_CTRL) | 0x0080;
break;
default:
return -1;
}
if (!pciehp_poll_mode)
slot_cmd = slot_cmd | HP_INTR_ENABLE;
pcie_write_cmd(slot, slot_cmd);
dbg("%s: SLOT_CTRL %x write cmd %x\n", __FUNCTION__, SLOT_CTRL, slot_cmd);
return rc;
}
static void hpc_set_green_led_on(struct slot *slot)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
u16 slot_cmd;
u16 slot_ctrl;
int rc = 0;
dbg("%s: \n", __FUNCTION__);
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return ;
}
if (slot->hp_slot >= php_ctlr->num_slots) {
err("%s: Invalid HPC slot number!\n", __FUNCTION__);
return ;
}
rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl);
if (rc) {
err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
return;
}
dbg("%s : hp_register_read_word SLOT_CTRL %x\n", __FUNCTION__, slot_ctrl);
slot_cmd = (slot_ctrl & ~PWR_LED_CTRL) | 0x0100;
if (!pciehp_poll_mode)
slot_cmd = slot_cmd | HP_INTR_ENABLE;
pcie_write_cmd(slot, slot_cmd);
dbg("%s: SLOT_CTRL %x write cmd %x\n",__FUNCTION__, SLOT_CTRL, slot_cmd);
return;
}
static void hpc_set_green_led_off(struct slot *slot)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
u16 slot_cmd;
u16 slot_ctrl;
int rc = 0;
dbg("%s: \n", __FUNCTION__);
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return ;
}
if (slot->hp_slot >= php_ctlr->num_slots) {
err("%s: Invalid HPC slot number!\n", __FUNCTION__);
return ;
}
rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl);
if (rc) {
err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
return;
}
dbg("%s : hp_register_read_word SLOT_CTRL %x\n", __FUNCTION__, slot_ctrl);
slot_cmd = (slot_ctrl & ~PWR_LED_CTRL) | 0x0300;
if (!pciehp_poll_mode)
slot_cmd = slot_cmd | HP_INTR_ENABLE;
pcie_write_cmd(slot, slot_cmd);
dbg("%s: SLOT_CTRL %x write cmd %x\n", __FUNCTION__, SLOT_CTRL, slot_cmd);
return;
}
static void hpc_set_green_led_blink(struct slot *slot)
{
struct php_ctlr_state_s *php_ctlr =(struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
u16 slot_cmd;
u16 slot_ctrl;
int rc = 0;
dbg("%s: \n", __FUNCTION__);
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return ;
}
if (slot->hp_slot >= php_ctlr->num_slots) {
err("%s: Invalid HPC slot number!\n", __FUNCTION__);
return ;
}
rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl);
if (rc) {
err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
return;
}
dbg("%s : hp_register_read_word SLOT_CTRL %x\n", __FUNCTION__, slot_ctrl);
slot_cmd = (slot_ctrl & ~PWR_LED_CTRL) | 0x0200;
if (!pciehp_poll_mode)
slot_cmd = slot_cmd | HP_INTR_ENABLE;
pcie_write_cmd(slot, slot_cmd);
dbg("%s: SLOT_CTRL %x write cmd %x\n",__FUNCTION__, SLOT_CTRL, slot_cmd);
return;
}
int pcie_get_ctlr_slot_config(struct controller *ctrl,
int *num_ctlr_slots, /* number of slots in this HPC; only 1 in PCIE */
int *first_device_num, /* PCI dev num of the first slot in this PCIE */
int *physical_slot_num, /* phy slot num of the first slot in this PCIE */
int *updown, /* physical_slot_num increament: 1 or -1 */
int *flags)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) ctrl->hpc_ctlr_handle;
u32 slot_cap;
int rc = 0;
DBG_ENTER_ROUTINE
if (!ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
*first_device_num = 0;
*num_ctlr_slots = 1;
rc = hp_register_read_dword(php_ctlr->pci_dev, SLOT_CAP, slot_cap);
if (rc) {
err("%s : hp_register_read_dword SLOT_CAP failed\n", __FUNCTION__);
return -1;
}
*physical_slot_num = slot_cap >> 19;
*updown = -1;
DBG_LEAVE_ROUTINE
return 0;
}
static void hpc_release_ctlr(struct controller *ctrl)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) ctrl->hpc_ctlr_handle;
struct php_ctlr_state_s *p, *p_prev;
DBG_ENTER_ROUTINE
if (!ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return ;
}
if (pciehp_poll_mode) {
del_timer(&php_ctlr->int_poll_timer);
} else {
if (php_ctlr->irq) {
free_irq(php_ctlr->irq, ctrl);
php_ctlr->irq = 0;
}
}
if (php_ctlr->pci_dev)
php_ctlr->pci_dev = 0;
spin_lock(&list_lock);
p = php_ctlr_list_head;
p_prev = NULL;
while (p) {
if (p == php_ctlr) {
if (p_prev)
p_prev->pnext = p->pnext;
else
php_ctlr_list_head = p->pnext;
break;
} else {
p_prev = p;
p = p->pnext;
}
}
spin_unlock(&list_lock);
kfree(php_ctlr);
DBG_LEAVE_ROUTINE
}
static int hpc_power_on_slot(struct slot * slot)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
u16 slot_cmd;
u16 slot_ctrl;
int retval = 0;
DBG_ENTER_ROUTINE
dbg("%s: \n", __FUNCTION__);
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
dbg("%s: slot->hp_slot %x\n", __FUNCTION__, slot->hp_slot);
if (slot->hp_slot >= php_ctlr->num_slots) {
err("%s: Invalid HPC slot number!\n", __FUNCTION__);
return -1;
}
retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl);
if (retval) {
err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
return retval;
}
dbg("%s: SLOT_CTRL %x, value read %xn", __FUNCTION__, SLOT_CTRL,
slot_ctrl);
slot_cmd = (slot_ctrl & ~PWR_CTRL) | POWER_ON;
if (!pciehp_poll_mode)
slot_cmd = slot_cmd | HP_INTR_ENABLE;
retval = pcie_write_cmd(slot, slot_cmd);
if (retval) {
err("%s: Write %x command failed!\n", __FUNCTION__, slot_cmd);
return -1;
}
dbg("%s: SLOT_CTRL %x write cmd %x\n",__FUNCTION__, SLOT_CTRL, slot_cmd);
DBG_LEAVE_ROUTINE
return retval;
}
static int hpc_power_off_slot(struct slot * slot)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
u16 slot_cmd;
u16 slot_ctrl;
int retval = 0;
DBG_ENTER_ROUTINE
dbg("%s: \n", __FUNCTION__);
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
dbg("%s: slot->hp_slot %x\n", __FUNCTION__, slot->hp_slot);
slot->hp_slot = 0;
if (slot->hp_slot >= php_ctlr->num_slots) {
err("%s: Invalid HPC slot number!\n", __FUNCTION__);
return -1;
}
retval = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl);
if (retval) {
err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
return retval;
}
dbg("%s: SLOT_CTRL %x, value read %x\n", __FUNCTION__, SLOT_CTRL,
slot_ctrl);
slot_cmd = (slot_ctrl & ~PWR_CTRL) | POWER_OFF;
if (!pciehp_poll_mode)
slot_cmd = slot_cmd | HP_INTR_ENABLE;
retval = pcie_write_cmd(slot, slot_cmd);
if (retval) {
err("%s: Write command failed!\n", __FUNCTION__);
return -1;
}
dbg("%s: SLOT_CTRL %x write cmd %x\n",__FUNCTION__, SLOT_CTRL, slot_cmd);
DBG_LEAVE_ROUTINE
return retval;
}
static irqreturn_t pcie_isr(int IRQ, void *dev_id, struct pt_regs *regs)
{
struct controller *ctrl = NULL;
struct php_ctlr_state_s *php_ctlr;
u8 schedule_flag = 0;
u16 slot_status, intr_detect, intr_loc;
u16 temp_word;
int hp_slot = 0; /* only 1 slot per PCI Express port */
int rc = 0;
if (!dev_id)
return IRQ_NONE;
if (!pciehp_poll_mode) {
ctrl = (struct controller *)dev_id;
php_ctlr = ctrl->hpc_ctlr_handle;
} else {
php_ctlr = (struct php_ctlr_state_s *) dev_id;
ctrl = (struct controller *)php_ctlr->callback_instance_id;
}
if (!ctrl) {
dbg("%s: dev_id %p ctlr == NULL\n", __FUNCTION__, (void*) dev_id);
return IRQ_NONE;
}
if (!php_ctlr) {
dbg("%s: php_ctlr == NULL\n", __FUNCTION__);
return IRQ_NONE;
}
rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status);
if (rc) {
err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__);
return IRQ_NONE;
}
intr_detect = ( ATTN_BUTTN_PRESSED | PWR_FAULT_DETECTED | MRL_SENS_CHANGED |
PRSN_DETECT_CHANGED | CMD_COMPLETED );
intr_loc = slot_status & intr_detect;
/* Check to see if it was our interrupt */
if ( !intr_loc )
return IRQ_NONE;
dbg("%s: intr_loc %x\n", __FUNCTION__, intr_loc);
/* Mask Hot-plug Interrupt Enable */
if (!pciehp_poll_mode) {
rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, temp_word);
if (rc) {
err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
return IRQ_NONE;;
}
temp_word = (temp_word & ~HP_INTR_ENABLE) | 0x00;
rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_CTRL, temp_word);
if (rc) {
err("%s : hp_register_write_word SLOT_CTRL failed\n", __FUNCTION__);
return IRQ_NONE;;
}
}
if (intr_loc & CMD_COMPLETED) {
/*
* Command Complete Interrupt Pending
*/
dbg("%s: In Command Complete Interrupt Pending\n", __FUNCTION__);
wake_up_interruptible(&ctrl->queue);
}
if ((php_ctlr->switch_change_callback) && (intr_loc & MRL_SENS_CHANGED))
schedule_flag += php_ctlr->switch_change_callback(
hp_slot, php_ctlr->callback_instance_id);
if ((php_ctlr->attention_button_callback) && (intr_loc & ATTN_BUTTN_PRESSED))
schedule_flag += php_ctlr->attention_button_callback(
hp_slot, php_ctlr->callback_instance_id);
if ((php_ctlr->presence_change_callback) && (intr_loc & PRSN_DETECT_CHANGED))
schedule_flag += php_ctlr->presence_change_callback(
hp_slot , php_ctlr->callback_instance_id);
if ((php_ctlr->power_fault_callback) && (intr_loc & PWR_FAULT_DETECTED))
schedule_flag += php_ctlr->power_fault_callback(
hp_slot, php_ctlr->callback_instance_id);
/* Clear all events after serving them */
temp_word = slot_status | 0xff;
rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_STATUS, temp_word);
if (rc) {
err("%s : hp_register_write_word SLOT_STATUS failed\n", __FUNCTION__);
return IRQ_NONE;
}
/* Unmask Hot-plug Interrupt Enable */
if (!pciehp_poll_mode) {
rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, temp_word);
if (rc) {
err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
return IRQ_NONE;
}
temp_word = (temp_word & ~HP_INTR_ENABLE) | HP_INTR_ENABLE;
rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_CTRL, temp_word);
if (rc) {
err("%s : hp_register_write_word SLOT_CTRL failed\n", __FUNCTION__);
return IRQ_NONE;
}
}
return IRQ_HANDLED;
}
static int hpc_get_max_lnk_speed (struct slot *slot, enum pcie_link_speed *value)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
enum pcie_link_speed lnk_speed;
u32 lnk_cap;
int retval = 0;
DBG_ENTER_ROUTINE
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
if (slot->hp_slot >= php_ctlr->num_slots) {
err("%s: Invalid HPC slot number!\n", __FUNCTION__);
return -1;
}
retval = hp_register_read_dword(php_ctlr->pci_dev, LNK_CAP, lnk_cap);
if (retval) {
err("%s : hp_register_read_dword LNK_CAP failed\n", __FUNCTION__);
return retval;
}
switch (lnk_cap & 0x000F) {
case 1:
lnk_speed = PCIE_2PT5GB;
break;
default:
lnk_speed = PCIE_LNK_SPEED_UNKNOWN;
break;
}
*value = lnk_speed;
dbg("Max link speed = %d\n", lnk_speed);
DBG_LEAVE_ROUTINE
return retval;
}
static int hpc_get_max_lnk_width (struct slot *slot, enum pcie_link_width *value)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
enum pcie_link_width lnk_wdth;
u32 lnk_cap;
int retval = 0;
DBG_ENTER_ROUTINE
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
if (slot->hp_slot >= php_ctlr->num_slots) {
err("%s: Invalid HPC slot number!\n", __FUNCTION__);
return -1;
}
retval = hp_register_read_dword(php_ctlr->pci_dev, LNK_CAP, lnk_cap);
if (retval) {
err("%s : hp_register_read_dword LNK_CAP failed\n", __FUNCTION__);
return retval;
}
switch ((lnk_cap & 0x03F0) >> 4){
case 0:
lnk_wdth = PCIE_LNK_WIDTH_RESRV;
break;
case 1:
lnk_wdth = PCIE_LNK_X1;
break;
case 2:
lnk_wdth = PCIE_LNK_X2;
break;
case 4:
lnk_wdth = PCIE_LNK_X4;
break;
case 8:
lnk_wdth = PCIE_LNK_X8;
break;
case 12:
lnk_wdth = PCIE_LNK_X12;
break;
case 16:
lnk_wdth = PCIE_LNK_X16;
break;
case 32:
lnk_wdth = PCIE_LNK_X32;
break;
default:
lnk_wdth = PCIE_LNK_WIDTH_UNKNOWN;
break;
}
*value = lnk_wdth;
dbg("Max link width = %d\n", lnk_wdth);
DBG_LEAVE_ROUTINE
return retval;
}
static int hpc_get_cur_lnk_speed (struct slot *slot, enum pcie_link_speed *value)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
enum pcie_link_speed lnk_speed = PCI_SPEED_UNKNOWN;
int retval = 0;
u16 lnk_status;
DBG_ENTER_ROUTINE
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
if (slot->hp_slot >= php_ctlr->num_slots) {
err("%s: Invalid HPC slot number!\n", __FUNCTION__);
return -1;
}
retval = hp_register_read_word(php_ctlr->pci_dev, LNK_STATUS, lnk_status);
if (retval) {
err("%s : hp_register_read_word LNK_STATUS failed\n", __FUNCTION__);
return retval;
}
switch (lnk_status & 0x0F) {
case 1:
lnk_speed = PCIE_2PT5GB;
break;
default:
lnk_speed = PCIE_LNK_SPEED_UNKNOWN;
break;
}
*value = lnk_speed;
dbg("Current link speed = %d\n", lnk_speed);
DBG_LEAVE_ROUTINE
return retval;
}
static int hpc_get_cur_lnk_width (struct slot *slot, enum pcie_link_width *value)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
enum pcie_link_width lnk_wdth = PCIE_LNK_WIDTH_UNKNOWN;
int retval = 0;
u16 lnk_status;
DBG_ENTER_ROUTINE
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
if (slot->hp_slot >= php_ctlr->num_slots) {
err("%s: Invalid HPC slot number!\n", __FUNCTION__);
return -1;
}
retval = hp_register_read_word(php_ctlr->pci_dev, LNK_STATUS, lnk_status);
if (retval) {
err("%s : hp_register_read_word LNK_STATUS failed\n", __FUNCTION__);
return retval;
}
switch ((lnk_status & 0x03F0) >> 4){
case 0:
lnk_wdth = PCIE_LNK_WIDTH_RESRV;
break;
case 1:
lnk_wdth = PCIE_LNK_X1;
break;
case 2:
lnk_wdth = PCIE_LNK_X2;
break;
case 4:
lnk_wdth = PCIE_LNK_X4;
break;
case 8:
lnk_wdth = PCIE_LNK_X8;
break;
case 12:
lnk_wdth = PCIE_LNK_X12;
break;
case 16:
lnk_wdth = PCIE_LNK_X16;
break;
case 32:
lnk_wdth = PCIE_LNK_X32;
break;
default:
lnk_wdth = PCIE_LNK_WIDTH_UNKNOWN;
break;
}
*value = lnk_wdth;
dbg("Current link width = %d\n", lnk_wdth);
DBG_LEAVE_ROUTINE
return retval;
}
static struct hpc_ops pciehp_hpc_ops = {
.power_on_slot = hpc_power_on_slot,
.power_off_slot = hpc_power_off_slot,
.set_attention_status = hpc_set_attention_status,
.get_power_status = hpc_get_power_status,
.get_attention_status = hpc_get_attention_status,
.get_latch_status = hpc_get_latch_status,
.get_adapter_status = hpc_get_adapter_status,
.get_max_bus_speed = hpc_get_max_lnk_speed,
.get_cur_bus_speed = hpc_get_cur_lnk_speed,
.get_max_lnk_width = hpc_get_max_lnk_width,
.get_cur_lnk_width = hpc_get_cur_lnk_width,
.query_power_fault = hpc_query_power_fault,
.green_led_on = hpc_set_green_led_on,
.green_led_off = hpc_set_green_led_off,
.green_led_blink = hpc_set_green_led_blink,
.release_ctlr = hpc_release_ctlr,
.check_lnk_status = hpc_check_lnk_status,
};
int pcie_init(struct controller * ctrl,
struct pci_dev * pdev,
php_intr_callback_t attention_button_callback,
php_intr_callback_t switch_change_callback,
php_intr_callback_t presence_change_callback,
php_intr_callback_t power_fault_callback)
{
struct php_ctlr_state_s *php_ctlr, *p;
void *instance_id = ctrl;
int rc;
static int first = 1;
u16 temp_word;
u16 cap_reg;
u16 intr_enable;
u32 slot_cap;
int cap_base, saved_cap_base;
u16 slot_status, slot_ctrl;
DBG_ENTER_ROUTINE
spin_lock_init(&list_lock);
php_ctlr = (struct php_ctlr_state_s *) kmalloc(sizeof(struct php_ctlr_state_s), GFP_KERNEL);
if (!php_ctlr) { /* allocate controller state data */
err("%s: HPC controller memory allocation error!\n", __FUNCTION__);
goto abort;
}
memset(php_ctlr, 0, sizeof(struct php_ctlr_state_s));
php_ctlr->pci_dev = pdev; /* save pci_dev in context */
dbg("%s: pdev->vendor %x pdev->device %x\n", __FUNCTION__,
pdev->vendor, pdev->device);
saved_cap_base = pcie_cap_base;
if ((cap_base = pci_find_capability(pdev, PCI_CAP_ID_EXP)) == 0) {
dbg("%s: Can't find PCI_CAP_ID_EXP (0x10)\n", __FUNCTION__);
goto abort_free_ctlr;
}
pcie_cap_base = cap_base;
dbg("%s: pcie_cap_base %x\n", __FUNCTION__, pcie_cap_base);
rc = hp_register_read_word(pdev, CAP_REG, cap_reg);
if (rc) {
err("%s : hp_register_read_word CAP_REG failed\n", __FUNCTION__);
goto abort_free_ctlr;
}
dbg("%s: CAP_REG offset %x cap_reg %x\n", __FUNCTION__, CAP_REG, cap_reg);
if (((cap_reg & SLOT_IMPL) == 0) || ((cap_reg & DEV_PORT_TYPE) != 0x0040)){
dbg("%s : This is not a root port or the port is not connected to a slot\n", __FUNCTION__);
goto abort_free_ctlr;
}
rc = hp_register_read_dword(php_ctlr->pci_dev, SLOT_CAP, slot_cap);
if (rc) {
err("%s : hp_register_read_word CAP_REG failed\n", __FUNCTION__);
goto abort_free_ctlr;
}
dbg("%s: SLOT_CAP offset %x slot_cap %x\n", __FUNCTION__, SLOT_CAP, slot_cap);
if (!(slot_cap & HP_CAP)) {
dbg("%s : This slot is not hot-plug capable\n", __FUNCTION__);
goto abort_free_ctlr;
}
/* For debugging purpose */
rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status);
if (rc) {
err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__);
goto abort_free_ctlr;
}
dbg("%s: SLOT_STATUS offset %x slot_status %x\n", __FUNCTION__, SLOT_STATUS, slot_status);
rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_CTRL, slot_ctrl);
if (rc) {
err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
goto abort_free_ctlr;
}
dbg("%s: SLOT_CTRL offset %x slot_ctrl %x\n", __FUNCTION__, SLOT_CTRL, slot_ctrl);
if (first) {
spin_lock_init(&hpc_event_lock);
first = 0;
}
dbg("pdev = %p: b:d:f:irq=0x%x:%x:%x:%x\n", pdev, pdev->bus->number,
PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn), pdev->irq);
for ( rc = 0; rc < DEVICE_COUNT_RESOURCE; rc++)
if (pci_resource_len(pdev, rc) > 0)
dbg("pci resource[%d] start=0x%lx(len=0x%lx)\n", rc,
pci_resource_start(pdev, rc), pci_resource_len(pdev, rc));
info("HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n", pdev->vendor, pdev->device,
pdev->subsystem_vendor, pdev->subsystem_device);
init_MUTEX(&ctrl->crit_sect);
/* setup wait queue */
init_waitqueue_head(&ctrl->queue);
/* find the IRQ */
php_ctlr->irq = pdev->irq;
dbg("HPC interrupt = %d\n", php_ctlr->irq);
/* Save interrupt callback info */
php_ctlr->attention_button_callback = attention_button_callback;
php_ctlr->switch_change_callback = switch_change_callback;
php_ctlr->presence_change_callback = presence_change_callback;
php_ctlr->power_fault_callback = power_fault_callback;
php_ctlr->callback_instance_id = instance_id;
/* return PCI Controller Info */
php_ctlr->slot_device_offset = 0;
php_ctlr->num_slots = 1;
/* Mask Hot-plug Interrupt Enable */
rc = hp_register_read_word(pdev, SLOT_CTRL, temp_word);
if (rc) {
err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
goto abort_free_ctlr;
}
dbg("%s: SLOT_CTRL %x value read %x\n", __FUNCTION__, SLOT_CTRL, temp_word);
temp_word = (temp_word & ~HP_INTR_ENABLE) | 0x00;
rc = hp_register_write_word(pdev, SLOT_CTRL, temp_word);
if (rc) {
err("%s : hp_register_write_word SLOT_CTRL failed\n", __FUNCTION__);
goto abort_free_ctlr;
}
dbg("%s : Mask HPIE hp_register_write_word SLOT_CTRL %x\n", __FUNCTION__, temp_word);
rc = hp_register_read_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status);
if (rc) {
err("%s : hp_register_read_word SLOT_STATUS failed\n", __FUNCTION__);
goto abort_free_ctlr;
}
dbg("%s: Mask HPIE SLOT_STATUS offset %x reads slot_status %x\n", __FUNCTION__, SLOT_STATUS, slot_status);
rc = hp_register_write_word(php_ctlr->pci_dev, SLOT_STATUS, slot_status);
if (rc) {
err("%s : hp_register_write_word SLOT_STATUS failed\n", __FUNCTION__);
goto abort_free_ctlr;
}
dbg("%s: SLOT_STATUS offset %x writes slot_status %x\n", __FUNCTION__, SLOT_STATUS, slot_status);
if (pciehp_poll_mode) {/* Install interrupt polling code */
/* Install and start the interrupt polling timer */
init_timer(&php_ctlr->int_poll_timer);
start_int_poll_timer( php_ctlr, 10 ); /* start with 10 second delay */
} else {
/* Installs the interrupt handler */
#ifdef CONFIG_PCI_USE_VECTOR
rc = pci_enable_msi(pdev);
if (rc) {
err("Can't get msi for the hotplug controller\n");
dbg("%s: rc = %x\n", __FUNCTION__, rc);
goto abort_free_ctlr;
}
php_ctlr->irq = pdev->irq;
#endif
rc = request_irq(php_ctlr->irq, pcie_isr, SA_SHIRQ, MY_NAME, (void *) ctrl);
dbg("%s: request_irq %d for hpc%d (returns %d)\n", __FUNCTION__, php_ctlr->irq, ctlr_seq_num, rc);
if (rc) {
err("Can't get irq %d for the hotplug controller\n", php_ctlr->irq);
goto abort_free_ctlr;
}
}
rc = hp_register_read_word(pdev, SLOT_CTRL, temp_word);
if (rc) {
err("%s : hp_register_read_word SLOT_CTRL failed\n", __FUNCTION__);
goto abort_free_ctlr;
}
dbg("%s: SLOT_CTRL %x value read %x\n", __FUNCTION__, SLOT_CTRL, temp_word);
intr_enable = ATTN_BUTTN_ENABLE | PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE |
PRSN_DETECT_ENABLE | CMD_CMPL_INTR_ENABLE;
temp_word = (temp_word & ~intr_enable) | intr_enable;
if (pciehp_poll_mode) {
temp_word = (temp_word & ~HP_INTR_ENABLE) | 0x0;
} else {
temp_word = (temp_word & ~HP_INTR_ENABLE) | HP_INTR_ENABLE;
}
dbg("%s: temp_word %x\n", __FUNCTION__, temp_word);
/* Unmask Hot-plug Interrupt Enable for the interrupt notification mechanism case */
rc = hp_register_write_word(pdev, SLOT_CTRL, temp_word);
if (rc) {
err("%s : hp_register_write_word SLOT_CTRL failed\n", __FUNCTION__);
goto abort_free_ctlr;
}
dbg("%s : Unmask HPIE hp_register_write_word SLOT_CTRL with %x\n", __FUNCTION__, temp_word);
/* Add this HPC instance into the HPC list */
spin_lock(&list_lock);
if (php_ctlr_list_head == 0) {
php_ctlr_list_head = php_ctlr;
p = php_ctlr_list_head;
p->pnext = 0;
} else {
p = php_ctlr_list_head;
while (p->pnext)
p = p->pnext;
p->pnext = php_ctlr;
}
spin_unlock(&list_lock);
ctlr_seq_num++;
ctrl->hpc_ctlr_handle = php_ctlr;
ctrl->hpc_ops = &pciehp_hpc_ops;
DBG_LEAVE_ROUTINE
return 0;
/* We end up here for the many possible ways to fail this API. */
abort_free_ctlr:
pcie_cap_base = saved_cap_base;
kfree(php_ctlr);
abort:
DBG_LEAVE_ROUTINE
return -1;
}
/*
* PCI Express 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.
* Copyright (C) 2003-2004 Intel Corporation
*
* 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>, <dely.l.sy@intel.com>
*
*/
#include <linux/config.h>
#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 "../pci.h"
#include "pciehp.h"
#ifndef CONFIG_IA64
#include "../../../arch/i386/pci/pci.h" /* horrible hack showing how processor dependant we are... */
#endif
int pciehp_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));
/* Still NULL ? Well then scan for it ! */
if (func->pci_dev == NULL) {
dbg("%s: pci_dev still null. do pci_scan_slot\n", __FUNCTION__);
num = pci_scan_slot(ctrl->pci_dev->subordinate, PCI_DEVFN(func->device, func->function));
if (num)
pci_bus_add_devices(ctrl->pci_dev->subordinate);
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 pciehp_unconfigure_device(struct pci_func* func)
{
int rc = 0;
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, (func->device << 3) | j);
if (temp) {
pci_remove_bus_device(temp);
}
}
return rc;
}
/*
* pciehp_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 pciehp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num)
{
#if !defined(CONFIG_X86_IO_APIC) && !defined(CONFIG_X86_64)
int rc;
u16 temp_word;
struct pci_dev fakedev;
struct pci_bus fakebus;
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);
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);
#endif
return 0;
}
/* More PCI configuration routines; this time centered around hotplug controller */
/*
* pciehp_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 pciehp_save_config(struct controller *ctrl, int busnumber, int num_ctlr_slots, int first_device_num)
{
int rc;
u8 class_code;
u8 header_type;
u32 ID;
u8 secondary_bus;
struct pci_func *new_slot;
int sub_bus;
int max_functions;
int function;
u8 DevError;
int device = 0;
int cloop = 0;
int stop_it;
int index;
int is_hot_plug = num_ctlr_slots || first_device_num;
struct pci_bus lpci_bus, *pci_bus;
int FirstSupported, LastSupported;
dbg("%s: Enter\n", __FUNCTION__);
memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus));
pci_bus = &lpci_bus;
dbg("%s: num_ctlr_slots = %d, first_device_num = %d\n", __FUNCTION__, num_ctlr_slots, first_device_num);
/* Decide which slots are supported */
if (is_hot_plug) {
/*********************************
* is_hot_plug is the slot mask
*********************************/
FirstSupported = first_device_num;
LastSupported = FirstSupported + num_ctlr_slots - 1;
} else {
FirstSupported = 0;
LastSupported = 0x1F;
}
dbg("FirstSupported = %d, LastSupported = %d\n", FirstSupported, LastSupported);
/* Save PCI configuration space for all devices in supported slots */
dbg("%s: pci_bus->number = %x\n", __FUNCTION__, pci_bus->number);
pci_bus->number = busnumber;
dbg("%s: bus = %x, dev = %x\n", __FUNCTION__, busnumber, device);
for (device = FirstSupported; device <= LastSupported; device++) {
ID = 0xFFFFFFFF;
rc = pci_bus_read_config_dword(pci_bus, PCI_DEVFN(device, 0), PCI_VENDOR_ID, &ID);
dbg("%s: ID = %x\n", __FUNCTION__, ID);
if (ID != 0xFFFFFFFF) { /* device in slot */
dbg("%s: ID = %x\n", __FUNCTION__, ID);
rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, 0), 0x0B, &class_code);
if (rc)
return rc;
rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, 0), PCI_HEADER_TYPE, &header_type);
if (rc)
return rc;
dbg("class_code = %x, header_type = %x\n", class_code, header_type);
/* 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;
dbg("%s: In do loop\n", __FUNCTION__);
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(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 = pciehp_save_config(ctrl, sub_bus, 0, 0);
if (rc)
return rc;
}
}
index = 0;
new_slot = pciehp_slot_find(busnumber, device, index++);
dbg("%s: new_slot = %p bus %x dev %x fun %x\n",
__FUNCTION__, new_slot, busnumber, device, index-1);
while (new_slot && (new_slot->function != (u8) function)) {
new_slot = pciehp_slot_find(busnumber, device, index++);
dbg("%s: while loop, new_slot = %p bus %x dev %x fun %x\n",
__FUNCTION__, new_slot, busnumber, device, index-1);
}
if (!new_slot) {
/* Setup slot structure. */
new_slot = pciehp_slot_create(busnumber);
dbg("%s: if, new_slot = %p bus %x dev %x fun %x\n",
__FUNCTION__, new_slot, busnumber, device, function);
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);
dbg("new_slot->pci_dev = %p\n", new_slot->pci_dev);
for (cloop = 0; cloop < 0x20; cloop++) {
rc = pci_bus_read_config_dword(pci_bus, PCI_DEVFN(device, function), cloop << 2,
(u32 *) & (new_slot->config_space [cloop]));
/* dbg("new_slot->config_space[%x] = %x\n", cloop, 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)) {
dbg("%s: In while loop \n", __FUNCTION__);
rc = pci_bus_read_config_dword(pci_bus, PCI_DEVFN(device, function), PCI_VENDOR_ID, &ID);
if (ID == 0xFFFFFFFF) { /* nothing there. */
function++;
dbg("Nothing there\n");
} else { /* Something there */
rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, function), 0x0B,
&class_code);
if (rc)
return rc;
rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, function), PCI_HEADER_TYPE,
&header_type);
if (rc)
return rc;
dbg("class_code = %x, header_type = %x\n", class_code, header_type);
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 = pciehp_slot_create(busnumber);
if (new_slot == NULL) {
return(1);
}
dbg("new_slot = %p, bus = %x, dev = %x, fun = %x\n", new_slot,
new_slot->bus, new_slot->device, new_slot->function);
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;
}
dbg("%s: End of For loop\n", __FUNCTION__);
} /* End of FOR loop */
dbg("%s: Exit\n", __FUNCTION__);
return(0);
}
/*
* pciehp_save_slot_config
*
* Saves configuration info for all PCI devices in a given slot
* including subordinate busses.
*
* returns 0 if success
*/
int pciehp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot)
{
int 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;
struct pci_bus lpci_bus, *pci_bus;
memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus));
pci_bus = &lpci_bus;
pci_bus->number = new_slot->bus;
ID = 0xFFFFFFFF;
pci_bus_read_config_dword(pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_VENDOR_ID, &ID);
if (ID != 0xFFFFFFFF) { /* device in slot */
pci_bus_read_config_byte(pci_bus, PCI_DEVFN(new_slot->device, 0), 0x0B, &class_code);
pci_bus_read_config_byte(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(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 = pciehp_save_config(ctrl, sub_bus, 0, 0);
if (rc)
return(rc);
} /* End of IF */
new_slot->status = 0;
for (cloop = 0; cloop < 0x20; cloop++) {
pci_bus_read_config_dword(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(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(pci_bus, PCI_DEVFN(new_slot->device, function), 0x0B, &class_code);
pci_bus_read_config_byte(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);
}
/*
* pciehp_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
* if disable == 1(DISABLE_CARD),
* it loops for all functions of the slot and disables them.
* else, it just get resources of the function and return.
*/
int pciehp_save_used_resources (struct controller *ctrl, struct pci_func *func, int disable)
{
u8 cloop;
u8 header_type;
u8 secondary_bus;
u8 temp_byte;
u16 command;
u16 save_command;
u16 w_base, w_length;
u32 temp_register;
u32 save_base;
u32 base, length;
u64 base64 = 0;
int index = 0;
unsigned int devfn;
struct pci_resource *mem_node = NULL;
struct pci_resource *p_mem_node = NULL;
struct pci_resource *t_mem_node;
struct pci_resource *io_node;
struct pci_resource *bus_node;
struct pci_bus lpci_bus, *pci_bus;
memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus));
pci_bus = &lpci_bus;
if (disable)
func = pciehp_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);
if (disable) {
/* 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 */
dbg("Save_used_res of PCI bridge b:d=0x%x:%x, sc=0x%x\n", func->bus, func->device, save_command);
if (disable) {
/* 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 =(struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!bus_node)
return -ENOMEM;
bus_node->base = (ulong)secondary_bus;
bus_node->length = (ulong)(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, &temp_byte);
base = temp_byte;
pci_bus_read_config_byte (pci_bus, devfn, PCI_IO_LIMIT, &temp_byte);
length = temp_byte;
if ((base <= length) && (!disable || (save_command & PCI_COMMAND_IO))) {
io_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!io_node)
return -ENOMEM;
io_node->base = (ulong)(base & PCI_IO_RANGE_MASK) << 8;
io_node->length = (ulong)(length - 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) && (!disable || (save_command & PCI_COMMAND_MEMORY))) {
mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!mem_node)
return -ENOMEM;
mem_node->base = (ulong)w_base << 16;
mem_node->length = (ulong)(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) && (!disable || (save_command & PCI_COMMAND_MEMORY))) {
p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!p_mem_node)
return -ENOMEM;
p_mem_node->base = (ulong)w_base << 16;
p_mem_node->length = (ulong)(w_length - w_base + 0x10) << 16;
p_mem_node->next = func->p_mem_head;
func->p_mem_head = p_mem_node;
}
} else if ((header_type & 0x7F) == PCI_HEADER_TYPE_NORMAL) {
dbg("Save_used_res of PCI adapter b:d=0x%x:%x, sc=0x%x\n", func->bus, func->device, save_command);
/* Figure out IO and memory base lengths */
for (cloop = PCI_BASE_ADDRESS_0; cloop <= PCI_BASE_ADDRESS_5; 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, &temp_register);
if (!disable) {
pci_bus_write_config_dword (pci_bus, devfn, cloop, save_base);
}
if (!temp_register)
continue;
base = temp_register;
if ((base & PCI_BASE_ADDRESS_SPACE_IO) && (!disable || (save_command & PCI_COMMAND_IO))) {
/* IO base */
/* set temp_register = amount of IO space requested */
base = base & 0xFFFFFFFCL;
base = (~base) + 1;
io_node = (struct pci_resource *) kmalloc(sizeof (struct pci_resource), GFP_KERNEL);
if (!io_node)
return -ENOMEM;
io_node->base = (ulong)save_base & PCI_BASE_ADDRESS_IO_MASK;
io_node->length = (ulong)base;
dbg("sur adapter: IO bar=0x%x(length=0x%x)\n", io_node->base, io_node->length);
io_node->next = func->io_head;
func->io_head = io_node;
} else { /* map Memory */
int prefetchable = 1;
/* struct pci_resources **res_node; */
char *res_type_str = "PMEM";
u32 temp_register2;
t_mem_node = (struct pci_resource *) kmalloc(sizeof (struct pci_resource), GFP_KERNEL);
if (!t_mem_node)
return -ENOMEM;
if (!(base & PCI_BASE_ADDRESS_MEM_PREFETCH) && (!disable || (save_command & PCI_COMMAND_MEMORY))) {
prefetchable = 0;
mem_node = t_mem_node;
res_type_str++;
} else
p_mem_node = t_mem_node;
base = base & 0xFFFFFFF0L;
base = (~base) + 1;
switch (temp_register & PCI_BASE_ADDRESS_MEM_TYPE_MASK) {
case PCI_BASE_ADDRESS_MEM_TYPE_32:
if (prefetchable) {
p_mem_node->base = (ulong)save_base & PCI_BASE_ADDRESS_MEM_MASK;
p_mem_node->length = (ulong)base;
dbg("sur adapter: 32 %s bar=0x%x(length=0x%x)\n", res_type_str,
p_mem_node->base, p_mem_node->length);
p_mem_node->next = func->p_mem_head;
func->p_mem_head = p_mem_node;
} else {
mem_node->base = (ulong)save_base & PCI_BASE_ADDRESS_MEM_MASK;
mem_node->length = (ulong)base;
dbg("sur adapter: 32 %s bar=0x%x(length=0x%x)\n", res_type_str,
mem_node->base, mem_node->length);
mem_node->next = func->mem_head;
func->mem_head = mem_node;
}
break;
case PCI_BASE_ADDRESS_MEM_TYPE_64:
pci_bus_read_config_dword(pci_bus, devfn, cloop+4, &temp_register2);
base64 = temp_register2;
base64 = (base64 << 32) | save_base;
if (temp_register2) {
dbg("sur adapter: 64 %s high dword of base64(0x%x:%x) masked to 0\n",
res_type_str, temp_register2, (u32)base64);
base64 &= 0x00000000FFFFFFFFL;
}
if (prefetchable) {
p_mem_node->base = base64 & PCI_BASE_ADDRESS_MEM_MASK;
p_mem_node->length = base;
dbg("sur adapter: 64 %s base=0x%x(len=0x%x)\n", res_type_str,
p_mem_node->base, p_mem_node->length);
p_mem_node->next = func->p_mem_head;
func->p_mem_head = p_mem_node;
} else {
mem_node->base = base64 & PCI_BASE_ADDRESS_MEM_MASK;
mem_node->length = base;
dbg("sur adapter: 64 %s base=0x%x(len=0x%x)\n", res_type_str,
mem_node->base, mem_node->length);
mem_node->next = func->mem_head;
func->mem_head = mem_node;
}
cloop += 4;
break;
default:
dbg("asur: reserved BAR type=0x%x\n", temp_register);
break;
}
}
} /* End of base register loop */
} else { /* Some other unknown header type */
dbg("Save_used_res of PCI unknown type b:d=0x%x:%x. skip.\n", func->bus, func->device);
}
/* find the next device in this slot */
if (!disable)
break;
func = pciehp_slot_find(func->bus, func->device, index++);
}
return(0);
}
/*
* pciehp_return_board_resources
*
* this routine returns all resources allocated to a board to
* the available pool.
*
* returns 0 if success
*/
int pciehp_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 |= pciehp_resource_sort_and_combine(&(resources->mem_head));
rc |= pciehp_resource_sort_and_combine(&(resources->p_mem_head));
rc |= pciehp_resource_sort_and_combine(&(resources->io_head));
rc |= pciehp_resource_sort_and_combine(&(resources->bus_head));
return(rc);
}
/*
* pciehp_destroy_resource_list
*
* Puts node back in the resource list pointed to by head
*/
void pciehp_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);
}
}
/*
* pciehp_destroy_board_resources
*
* Puts node back in the resource list pointed to by head
*/
void pciehp_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);
}
}
/*
* PCI Express Hot Plug Controller Driver
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001,2003 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/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/workqueue.h>
#include <linux/pci.h>
#include "pciehp.h"
/* A few routines that create sysfs entries for the hot plug controller */
static int show_ctrl (struct device *dev, char *buf)
{
struct pci_dev *pci_dev;
struct controller *ctrl;
char * out = buf;
int index;
struct pci_resource *res;
pci_dev = container_of (dev, struct pci_dev, dev);
ctrl = pci_get_drvdata(pci_dev);
out += sprintf(buf, "Free resources: memory\n");
index = 11;
res = ctrl->mem_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
out += sprintf(out, "Free resources: prefetchable memory\n");
index = 11;
res = ctrl->p_mem_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
out += sprintf(out, "Free resources: IO\n");
index = 11;
res = ctrl->io_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
out += sprintf(out, "Free resources: bus numbers\n");
index = 11;
res = ctrl->bus_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
return out - buf;
}
static DEVICE_ATTR (ctrl, S_IRUGO, show_ctrl, NULL);
static int show_dev (struct device *dev, char *buf)
{
struct pci_dev *pci_dev;
struct controller *ctrl;
char * out = buf;
int index;
struct pci_resource *res;
struct pci_func *new_slot;
struct slot *slot;
pci_dev = container_of (dev, struct pci_dev, dev);
ctrl = pci_get_drvdata(pci_dev);
slot=ctrl->slot;
while (slot) {
new_slot = pciehp_slot_find(slot->bus, slot->device, 0);
if (!new_slot)
break;
out += sprintf(out, "assigned resources: memory\n");
index = 11;
res = new_slot->mem_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
out += sprintf(out, "assigned resources: prefetchable memory\n");
index = 11;
res = new_slot->p_mem_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
out += sprintf(out, "assigned resources: IO\n");
index = 11;
res = new_slot->io_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
out += sprintf(out, "assigned resources: bus numbers\n");
index = 11;
res = new_slot->bus_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
slot=slot->next;
}
return out - buf;
}
static DEVICE_ATTR (dev, S_IRUGO, show_dev, NULL);
void pciehp_create_ctrl_files (struct controller *ctrl)
{
device_create_file (&ctrl->pci_dev->dev, &dev_attr_ctrl);
device_create_file (&ctrl->pci_dev->dev, &dev_attr_dev);
}
/*
* PCIEHPRM : PCIEHP Resource Manager for ACPI/non-ACPI platform
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
* Copyright (C) 2003-2004 Intel Corporation
*
* 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>, <dely.l.sy@intel.com>
*
*/
#ifndef _PCIEHPRM_H_
#define _PCIEHPRM_H_
#ifdef CONFIG_HOTPLUG_PCI_PCIE_PHPRM_NONACPI
#include "pciehprm_nonacpi.h"
#endif
int pciehprm_init(enum php_ctlr_type ct);
void pciehprm_cleanup(void);
int pciehprm_print_pirt(void);
void *pciehprm_get_slot(struct slot *slot);
int pciehprm_find_available_resources(struct controller *ctrl);
int pciehprm_set_hpp(struct controller *ctrl, struct pci_func *func, u8 card_type);
void pciehprm_enable_card(struct controller *ctrl, struct pci_func *func, u8 card_type);
#ifdef DEBUG
#define RES_CHECK(this, bits) \
{ if (((this) & (bits - 1))) \
printk("%s:%d ERR: potential res loss!\n", __FUNCTION__, __LINE__); }
#else
#define RES_CHECK(this, bits)
#endif
#endif /* _PCIEHPRM_H_ */
/*
* PCIEHPRM ACPI: PHP Resource Manager for ACPI platform
*
* Copyright (C) 2003-2004 Intel Corporation
*
* 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 <dely.l.sy@intel.com>
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/acpi.h>
#include <linux/efi.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#ifdef CONFIG_IA64
#include <asm/iosapic.h>
#endif
#include <acpi/acpi.h>
#include <acpi/acpi_bus.h>
#include <acpi/actypes.h>
#include "pciehp.h"
#include "pciehprm.h"
#define PCI_MAX_BUS 0x100
#define ACPI_STA_DEVICE_PRESENT 0x01
#define METHOD_NAME__SUN "_SUN"
#define METHOD_NAME__HPP "_HPP"
#define METHOD_NAME_OSHP "OSHP"
#define PHP_RES_BUS 0xA0
#define PHP_RES_IO 0xA1
#define PHP_RES_MEM 0xA2
#define PHP_RES_PMEM 0xA3
#define BRIDGE_TYPE_P2P 0x00
#define BRIDGE_TYPE_HOST 0x01
/* this should go to drivers/acpi/include/ */
struct acpi__hpp {
u8 cache_line_size;
u8 latency_timer;
u8 enable_serr;
u8 enable_perr;
};
struct acpi_php_slot {
struct acpi_php_slot *next;
struct acpi_bridge *bridge;
acpi_handle handle;
int seg;
int bus;
int dev;
int fun;
u32 sun;
struct pci_resource *mem_head;
struct pci_resource *p_mem_head;
struct pci_resource *io_head;
struct pci_resource *bus_head;
void *slot_ops; /* _STA, _EJx, etc */
struct slot *slot;
}; /* per func */
struct acpi_bridge {
struct acpi_bridge *parent;
struct acpi_bridge *next;
struct acpi_bridge *child;
acpi_handle handle;
int seg;
int pbus; /* pdev->bus->number */
int pdevice; /* PCI_SLOT(pdev->devfn) */
int pfunction; /* PCI_DEVFN(pdev->devfn) */
int bus; /* pdev->subordinate->number */
struct acpi__hpp *_hpp;
struct acpi_php_slot *slots;
struct pci_resource *tmem_head; /* total from crs */
struct pci_resource *tp_mem_head; /* total from crs */
struct pci_resource *tio_head; /* total from crs */
struct pci_resource *tbus_head; /* total from crs */
struct pci_resource *mem_head; /* available */
struct pci_resource *p_mem_head; /* available */
struct pci_resource *io_head; /* available */
struct pci_resource *bus_head; /* available */
int scanned;
int type;
};
static struct acpi_bridge *acpi_bridges_head;
static u8 * acpi_path_name( acpi_handle handle)
{
acpi_status status;
static u8 path_name[ACPI_PATHNAME_MAX];
struct acpi_buffer ret_buf = { ACPI_PATHNAME_MAX, path_name };
memset(path_name, 0, sizeof (path_name));
status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &ret_buf);
if (ACPI_FAILURE(status))
return NULL;
else
return path_name;
}
static void acpi_get__hpp ( struct acpi_bridge *ab);
static void acpi_run_oshp ( struct acpi_bridge *ab);
static int acpi_add_slot_to_php_slots(
struct acpi_bridge *ab,
int bus_num,
acpi_handle handle,
u32 adr,
u32 sun
)
{
struct acpi_php_slot *aps;
static long samesun = -1;
aps = (struct acpi_php_slot *) kmalloc (sizeof(struct acpi_php_slot), GFP_KERNEL);
if (!aps) {
err ("acpi_pciehprm: alloc for aps fail\n");
return -1;
}
memset(aps, 0, sizeof(struct acpi_php_slot));
aps->handle = handle;
aps->bus = bus_num;
aps->dev = (adr >> 16) & 0xffff;
aps->fun = adr & 0xffff;
aps->sun = sun;
aps->next = ab->slots; /* cling to the bridge */
aps->bridge = ab;
ab->slots = aps;
ab->scanned += 1;
if (!ab->_hpp)
acpi_get__hpp(ab);
acpi_run_oshp(ab);
if (sun != samesun) {
info("acpi_pciehprm: Slot sun(%x) at s:b:d:f=0x%02x:%02x:%02x:%02x\n",
aps->sun, ab->seg, aps->bus, aps->dev, aps->fun);
samesun = sun;
}
return 0;
}
static void acpi_get__hpp ( struct acpi_bridge *ab)
{
acpi_status status;
u8 nui[4];
struct acpi_buffer ret_buf = { 0, NULL};
union acpi_object *ext_obj, *package;
u8 *path_name = acpi_path_name(ab->handle);
int i, len = 0;
/* get _hpp */
status = acpi_evaluate_object(ab->handle, METHOD_NAME__HPP, NULL, &ret_buf);
switch (status) {
case AE_BUFFER_OVERFLOW:
ret_buf.pointer = kmalloc (ret_buf.length, GFP_KERNEL);
if (!ret_buf.pointer) {
err ("acpi_pciehprm:%s alloc for _HPP fail\n", path_name);
return;
}
status = acpi_evaluate_object(ab->handle, METHOD_NAME__HPP, NULL, &ret_buf);
if (ACPI_SUCCESS(status))
break;
default:
if (ACPI_FAILURE(status)) {
err("acpi_pciehprm:%s _HPP fail=0x%x\n", path_name, status);
return;
}
}
ext_obj = (union acpi_object *) ret_buf.pointer;
if (ext_obj->type != ACPI_TYPE_PACKAGE) {
err ("acpi_pciehprm:%s _HPP obj not a package\n", path_name);
goto free_and_return;
}
len = ext_obj->package.count;
package = (union acpi_object *) ret_buf.pointer;
for ( i = 0; (i < len) || (i < 4); i++) {
ext_obj = (union acpi_object *) &package->package.elements[i];
switch (ext_obj->type) {
case ACPI_TYPE_INTEGER:
nui[i] = (u8)ext_obj->integer.value;
break;
default:
err ("acpi_pciehprm:%s _HPP obj type incorrect\n", path_name);
goto free_and_return;
}
}
ab->_hpp = kmalloc (sizeof (struct acpi__hpp), GFP_KERNEL);
memset(ab->_hpp, 0, sizeof(struct acpi__hpp));
ab->_hpp->cache_line_size = nui[0];
ab->_hpp->latency_timer = nui[1];
ab->_hpp->enable_serr = nui[2];
ab->_hpp->enable_perr = nui[3];
dbg(" _HPP: cache_line_size=0x%x\n", ab->_hpp->cache_line_size);
dbg(" _HPP: latency timer =0x%x\n", ab->_hpp->latency_timer);
dbg(" _HPP: enable SERR =0x%x\n", ab->_hpp->enable_serr);
dbg(" _HPP: enable PERR =0x%x\n", ab->_hpp->enable_perr);
free_and_return:
kfree(ret_buf.pointer);
}
static void acpi_run_oshp ( struct acpi_bridge *ab)
{
acpi_status status;
u8 *path_name = acpi_path_name(ab->handle);
struct acpi_buffer ret_buf = { 0, NULL};
/* run OSHP */
status = acpi_evaluate_object(ab->handle, METHOD_NAME_OSHP, NULL, &ret_buf);
if (ACPI_FAILURE(status)) {
err("acpi_pciehprm:%s OSHP fails=0x%x\n", path_name, status);
} else
dbg("acpi_pciehprm:%s OSHP passes =0x%x\n", path_name, status);
return;
}
static acpi_status acpi_evaluate_crs(
acpi_handle handle,
struct acpi_resource **retbuf
)
{
acpi_status status;
struct acpi_buffer crsbuf;
u8 *path_name = acpi_path_name(handle);
crsbuf.length = 0;
crsbuf.pointer = NULL;
status = acpi_get_current_resources (handle, &crsbuf);
switch (status) {
case AE_BUFFER_OVERFLOW:
break; /* found */
case AE_NOT_FOUND:
dbg("acpi_pciehprm:%s _CRS not found\n", path_name);
return status;
default:
err ("acpi_pciehprm:%s _CRS fail=0x%x\n", path_name, status);
return status;
}
crsbuf.pointer = kmalloc (crsbuf.length, GFP_KERNEL);
if (!crsbuf.pointer) {
err ("acpi_pciehprm: alloc %ld bytes for %s _CRS fail\n", (ulong)crsbuf.length, path_name);
return AE_NO_MEMORY;
}
status = acpi_get_current_resources (handle, &crsbuf);
if (ACPI_FAILURE(status)) {
err("acpi_pciehprm: %s _CRS fail=0x%x.\n", path_name, status);
kfree(crsbuf.pointer);
return status;
}
*retbuf = crsbuf.pointer;
return status;
}
static void free_pci_resource ( struct pci_resource *aprh)
{
struct pci_resource *res, *next;
for (res = aprh; res; res = next) {
next = res->next;
kfree(res);
}
}
static void print_pci_resource ( struct pci_resource *aprh)
{
struct pci_resource *res;
for (res = aprh; res; res = res->next)
dbg(" base= 0x%x length= 0x%x\n", res->base, res->length);
}
static void print_slot_resources( struct acpi_php_slot *aps)
{
if (aps->bus_head) {
dbg(" BUS Resources:\n");
print_pci_resource (aps->bus_head);
}
if (aps->io_head) {
dbg(" IO Resources:\n");
print_pci_resource (aps->io_head);
}
if (aps->mem_head) {
dbg(" MEM Resources:\n");
print_pci_resource (aps->mem_head);
}
if (aps->p_mem_head) {
dbg(" PMEM Resources:\n");
print_pci_resource (aps->p_mem_head);
}
}
static void print_pci_resources( struct acpi_bridge *ab)
{
if (ab->tbus_head) {
dbg(" Total BUS Resources:\n");
print_pci_resource (ab->tbus_head);
}
if (ab->bus_head) {
dbg(" BUS Resources:\n");
print_pci_resource (ab->bus_head);
}
if (ab->tio_head) {
dbg(" Total IO Resources:\n");
print_pci_resource (ab->tio_head);
}
if (ab->io_head) {
dbg(" IO Resources:\n");
print_pci_resource (ab->io_head);
}
if (ab->tmem_head) {
dbg(" Total MEM Resources:\n");
print_pci_resource (ab->tmem_head);
}
if (ab->mem_head) {
dbg(" MEM Resources:\n");
print_pci_resource (ab->mem_head);
}
if (ab->tp_mem_head) {
dbg(" Total PMEM Resources:\n");
print_pci_resource (ab->tp_mem_head);
}
if (ab->p_mem_head) {
dbg(" PMEM Resources:\n");
print_pci_resource (ab->p_mem_head);
}
if (ab->_hpp) {
dbg(" _HPP: cache_line_size=0x%x\n", ab->_hpp->cache_line_size);
dbg(" _HPP: latency timer =0x%x\n", ab->_hpp->latency_timer);
dbg(" _HPP: enable SERR =0x%x\n", ab->_hpp->enable_serr);
dbg(" _HPP: enable PERR =0x%x\n", ab->_hpp->enable_perr);
}
}
static int pciehprm_delete_resource(
struct pci_resource **aprh,
ulong base,
ulong size)
{
struct pci_resource *res;
struct pci_resource *prevnode;
struct pci_resource *split_node;
ulong tbase;
pciehp_resource_sort_and_combine(aprh);
for (res = *aprh; res; res = res->next) {
if (res->base > base)
continue;
if ((res->base + res->length) < (base + size))
continue;
if (res->base < base) {
tbase = base;
if ((res->length - (tbase - res->base)) < size)
continue;
split_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return -ENOMEM;
split_node->base = res->base;
split_node->length = tbase - res->base;
res->base = tbase;
res->length -= split_node->length;
split_node->next = res->next;
res->next = split_node;
}
if (res->length >= size) {
split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return -ENOMEM;
split_node->base = res->base + size;
split_node->length = res->length - size;
res->length = size;
split_node->next = res->next;
res->next = split_node;
}
if (*aprh == res) {
*aprh = res->next;
} else {
prevnode = *aprh;
while (prevnode->next != res)
prevnode = prevnode->next;
prevnode->next = res->next;
}
res->next = NULL;
kfree(res);
break;
}
return 0;
}
static int pciehprm_delete_resources(
struct pci_resource **aprh,
struct pci_resource *this
)
{
struct pci_resource *res;
for (res = this; res; res = res->next)
pciehprm_delete_resource(aprh, res->base, res->length);
return 0;
}
static int pciehprm_add_resource(
struct pci_resource **aprh,
ulong base,
ulong size)
{
struct pci_resource *res;
for (res = *aprh; res; res = res->next) {
if ((res->base + res->length) == base) {
res->length += size;
size = 0L;
break;
}
if (res->next == *aprh)
break;
}
if (size) {
res = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!res) {
err ("acpi_pciehprm: alloc for res fail\n");
return -ENOMEM;
}
memset(res, 0, sizeof (struct pci_resource));
res->base = base;
res->length = size;
res->next = *aprh;
*aprh = res;
}
return 0;
}
static int pciehprm_add_resources(
struct pci_resource **aprh,
struct pci_resource *this
)
{
struct pci_resource *res;
int rc = 0;
for (res = this; res && !rc; res = res->next)
rc = pciehprm_add_resource(aprh, res->base, res->length);
return rc;
}
static void acpi_parse_io (
struct acpi_bridge *ab,
union acpi_resource_data *data
)
{
struct acpi_resource_io *dataio;
dataio = (struct acpi_resource_io *) data;
dbg("Io Resource\n");
dbg(" %d bit decode\n", ACPI_DECODE_16 == dataio->io_decode ? 16:10);
dbg(" Range minimum base: %08X\n", dataio->min_base_address);
dbg(" Range maximum base: %08X\n", dataio->max_base_address);
dbg(" Alignment: %08X\n", dataio->alignment);
dbg(" Range Length: %08X\n", dataio->range_length);
}
static void acpi_parse_fixed_io (
struct acpi_bridge *ab,
union acpi_resource_data *data
)
{
struct acpi_resource_fixed_io *datafio;
datafio = (struct acpi_resource_fixed_io *) data;
dbg("Fixed Io Resource\n");
dbg(" Range base address: %08X", datafio->base_address);
dbg(" Range length: %08X", datafio->range_length);
}
static void acpi_parse_address16_32 (
struct acpi_bridge *ab,
union acpi_resource_data *data,
acpi_resource_type id
)
{
/*
* acpi_resource_address16 == acpi_resource_address32
* acpi_resource_address16 *data16 = (acpi_resource_address16 *) data;
*/
struct acpi_resource_address32 *data32 = (struct acpi_resource_address32 *) data;
struct pci_resource **aprh, **tprh;
if (id == ACPI_RSTYPE_ADDRESS16)
dbg("acpi_pciehprm:16-Bit Address Space Resource\n");
else
dbg("acpi_pciehprm:32-Bit Address Space Resource\n");
switch (data32->resource_type) {
case ACPI_MEMORY_RANGE:
dbg(" Resource Type: Memory Range\n");
aprh = &ab->mem_head;
tprh = &ab->tmem_head;
switch (data32->attribute.memory.cache_attribute) {
case ACPI_NON_CACHEABLE_MEMORY:
dbg(" Type Specific: Noncacheable memory\n");
break;
case ACPI_CACHABLE_MEMORY:
dbg(" Type Specific: Cacheable memory\n");
break;
case ACPI_WRITE_COMBINING_MEMORY:
dbg(" Type Specific: Write-combining memory\n");
break;
case ACPI_PREFETCHABLE_MEMORY:
aprh = &ab->p_mem_head;
dbg(" Type Specific: Prefetchable memory\n");
break;
default:
dbg(" Type Specific: Invalid cache attribute\n");
break;
}
dbg(" Type Specific: Read%s\n", ACPI_READ_WRITE_MEMORY == data32->attribute.memory.read_write_attribute ? "/Write":" Only");
break;
case ACPI_IO_RANGE:
dbg(" Resource Type: I/O Range\n");
aprh = &ab->io_head;
tprh = &ab->tio_head;
switch (data32->attribute.io.range_attribute) {
case ACPI_NON_ISA_ONLY_RANGES:
dbg(" Type Specific: Non-ISA Io Addresses\n");
break;
case ACPI_ISA_ONLY_RANGES:
dbg(" Type Specific: ISA Io Addresses\n");
break;
case ACPI_ENTIRE_RANGE:
dbg(" Type Specific: ISA and non-ISA Io Addresses\n");
break;
default:
dbg(" Type Specific: Invalid range attribute\n");
break;
}
break;
case ACPI_BUS_NUMBER_RANGE:
dbg(" Resource Type: Bus Number Range(fixed)\n");
/* fixup to be compatible with the rest of php driver */
data32->min_address_range++;
data32->address_length--;
aprh = &ab->bus_head;
tprh = &ab->tbus_head;
break;
default:
dbg(" Resource Type: Invalid resource type. Exiting.\n");
return;
}
dbg(" Resource %s\n", ACPI_CONSUMER == data32->producer_consumer ? "Consumer":"Producer");
dbg(" %s decode\n", ACPI_SUB_DECODE == data32->decode ? "Subtractive":"Positive");
dbg(" Min address is %s fixed\n", ACPI_ADDRESS_FIXED == data32->min_address_fixed ? "":"not");
dbg(" Max address is %s fixed\n", ACPI_ADDRESS_FIXED == data32->max_address_fixed ? "":"not");
dbg(" Granularity: %08X\n", data32->granularity);
dbg(" Address range min: %08X\n", data32->min_address_range);
dbg(" Address range max: %08X\n", data32->max_address_range);
dbg(" Address translation offset: %08X\n", data32->address_translation_offset);
dbg(" Address Length: %08X\n", data32->address_length);
if (0xFF != data32->resource_source.index) {
dbg(" Resource Source Index: %X\n", data32->resource_source.index);
/* dbg(" Resource Source: %s\n", data32->resource_source.string_ptr); */
}
pciehprm_add_resource(aprh, data32->min_address_range, data32->address_length);
}
static acpi_status acpi_parse_crs(
struct acpi_bridge *ab,
struct acpi_resource *crsbuf
)
{
acpi_status status = AE_OK;
struct acpi_resource *resource = crsbuf;
u8 count = 0;
u8 done = 0;
while (!done) {
dbg("acpi_pciehprm: PCI bus 0x%x Resource structure %x.\n", ab->bus, count++);
switch (resource->id) {
case ACPI_RSTYPE_IRQ:
dbg("Irq -------- Resource\n");
break;
case ACPI_RSTYPE_DMA:
dbg("DMA -------- Resource\n");
break;
case ACPI_RSTYPE_START_DPF:
dbg("Start DPF -------- Resource\n");
break;
case ACPI_RSTYPE_END_DPF:
dbg("End DPF -------- Resource\n");
break;
case ACPI_RSTYPE_IO:
acpi_parse_io (ab, &resource->data);
break;
case ACPI_RSTYPE_FIXED_IO:
acpi_parse_fixed_io (ab, &resource->data);
break;
case ACPI_RSTYPE_VENDOR:
dbg("Vendor -------- Resource\n");
break;
case ACPI_RSTYPE_END_TAG:
dbg("End_tag -------- Resource\n");
done = 1;
break;
case ACPI_RSTYPE_MEM24:
dbg("Mem24 -------- Resource\n");
break;
case ACPI_RSTYPE_MEM32:
dbg("Mem32 -------- Resource\n");
break;
case ACPI_RSTYPE_FIXED_MEM32:
dbg("Fixed Mem32 -------- Resource\n");
break;
case ACPI_RSTYPE_ADDRESS16:
acpi_parse_address16_32(ab, &resource->data, ACPI_RSTYPE_ADDRESS16);
break;
case ACPI_RSTYPE_ADDRESS32:
acpi_parse_address16_32(ab, &resource->data, ACPI_RSTYPE_ADDRESS32);
break;
case ACPI_RSTYPE_ADDRESS64:
info("Address64 -------- Resource unparsed\n");
break;
case ACPI_RSTYPE_EXT_IRQ:
dbg("Ext Irq -------- Resource\n");
break;
default:
dbg("Invalid -------- resource type 0x%x\n", resource->id);
break;
}
resource = (struct acpi_resource *) ((char *)resource + resource->length);
}
return status;
}
static acpi_status acpi_get_crs( struct acpi_bridge *ab)
{
acpi_status status;
struct acpi_resource *crsbuf;
status = acpi_evaluate_crs(ab->handle, &crsbuf);
if (ACPI_SUCCESS(status)) {
status = acpi_parse_crs(ab, crsbuf);
kfree(crsbuf);
pciehp_resource_sort_and_combine(&ab->bus_head);
pciehp_resource_sort_and_combine(&ab->io_head);
pciehp_resource_sort_and_combine(&ab->mem_head);
pciehp_resource_sort_and_combine(&ab->p_mem_head);
pciehprm_add_resources (&ab->tbus_head, ab->bus_head);
pciehprm_add_resources (&ab->tio_head, ab->io_head);
pciehprm_add_resources (&ab->tmem_head, ab->mem_head);
pciehprm_add_resources (&ab->tp_mem_head, ab->p_mem_head);
}
return status;
}
/* find acpi_bridge downword from ab. */
static struct acpi_bridge *
find_acpi_bridge_by_bus(
struct acpi_bridge *ab,
int seg,
int bus /* pdev->subordinate->number */
)
{
struct acpi_bridge *lab = NULL;
if (!ab)
return NULL;
if ((ab->bus == bus) && (ab->seg == seg))
return ab;
if (ab->child)
lab = find_acpi_bridge_by_bus(ab->child, seg, bus);
if (!lab)
if (ab->next)
lab = find_acpi_bridge_by_bus(ab->next, seg, bus);
return lab;
}
/*
* Build a device tree of ACPI PCI Bridges
*/
static void pciehprm_acpi_register_a_bridge (
struct acpi_bridge **head,
struct acpi_bridge *pab, /* parent bridge to which child bridge is added */
struct acpi_bridge *cab /* child bridge to add */
)
{
struct acpi_bridge *lpab;
struct acpi_bridge *lcab;
lpab = find_acpi_bridge_by_bus(*head, pab->seg, pab->bus);
if (!lpab) {
if (!(pab->type & BRIDGE_TYPE_HOST))
warn("PCI parent bridge s:b(%x:%x) not in list.\n", pab->seg, pab->bus);
pab->next = *head;
*head = pab;
lpab = pab;
}
if ((cab->type & BRIDGE_TYPE_HOST) && (pab == cab))
return;
lcab = find_acpi_bridge_by_bus(*head, cab->seg, cab->bus);
if (lcab) {
if ((pab->bus != lcab->parent->bus) || (lcab->bus != cab->bus))
err("PCI child bridge s:b(%x:%x) in list with diff parent.\n", cab->seg, cab->bus);
return;
} else
lcab = cab;
lcab->parent = lpab;
lcab->next = lpab->child;
lpab->child = lcab;
}
static acpi_status pciehprm_acpi_build_php_slots_callback(
acpi_handle handle,
u32 Level,
void *context,
void **retval
)
{
ulong bus_num;
ulong seg_num;
ulong sun, adr;
ulong padr = 0;
acpi_handle phandle = NULL;
struct acpi_bridge *pab = (struct acpi_bridge *)context;
struct acpi_bridge *lab;
acpi_status status;
u8 *path_name = acpi_path_name(handle);
/* get _SUN */
status = acpi_evaluate_integer(handle, METHOD_NAME__SUN, NULL, &sun);
switch(status) {
case AE_NOT_FOUND:
return AE_OK;
default:
if (ACPI_FAILURE(status)) {
err("acpi_pciehprm:%s _SUN fail=0x%x\n", path_name, status);
return status;
}
}
/* get _ADR. _ADR must exist if _SUN exists */
status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr);
if (ACPI_FAILURE(status)) {
err("acpi_pciehprm:%s _ADR fail=0x%x\n", path_name, status);
return status;
}
dbg("acpi_pciehprm:%s sun=0x%08x adr=0x%08x\n", path_name, (u32)sun, (u32)adr);
status = acpi_get_parent(handle, &phandle);
if (ACPI_FAILURE(status)) {
err("acpi_pciehprm:%s get_parent fail=0x%x\n", path_name, status);
return (status);
}
bus_num = pab->bus;
seg_num = pab->seg;
if (pab->bus == bus_num) {
lab = pab;
} else {
dbg("WARN: pab is not parent\n");
lab = find_acpi_bridge_by_bus(pab, seg_num, bus_num);
if (!lab) {
dbg("acpi_pciehprm: alloc new P2P bridge(%x) for sun(%08x)\n", (u32)bus_num, (u32)sun);
lab = (struct acpi_bridge *)kmalloc(sizeof(struct acpi_bridge), GFP_KERNEL);
if (!lab) {
err("acpi_pciehprm: alloc for ab fail\n");
return AE_NO_MEMORY;
}
memset(lab, 0, sizeof(struct acpi_bridge));
lab->handle = phandle;
lab->pbus = pab->bus;
lab->pdevice = (int)(padr >> 16) & 0xffff;
lab->pfunction = (int)(padr & 0xffff);
lab->bus = (int)bus_num;
lab->scanned = 0;
lab->type = BRIDGE_TYPE_P2P;
pciehprm_acpi_register_a_bridge (&acpi_bridges_head, pab, lab);
} else
dbg("acpi_pciehprm: found P2P bridge(%x) for sun(%08x)\n", (u32)bus_num, (u32)sun);
}
acpi_add_slot_to_php_slots(lab, (int)bus_num, handle, (u32)adr, (u32)sun);
return (status);
}
static int pciehprm_acpi_build_php_slots(
struct acpi_bridge *ab,
u32 depth
)
{
acpi_status status;
u8 *path_name = acpi_path_name(ab->handle);
/* Walk down this pci bridge to get _SUNs if any behind P2P */
status = acpi_walk_namespace ( ACPI_TYPE_DEVICE,
ab->handle,
depth,
pciehprm_acpi_build_php_slots_callback,
ab,
NULL );
if (ACPI_FAILURE(status)) {
dbg("acpi_pciehprm:%s walk for _SUN on pci bridge seg:bus(%x:%x) fail=0x%x\n", path_name, ab->seg, ab->bus, status);
return -1;
}
return 0;
}
static void build_a_bridge(
struct acpi_bridge *pab,
struct acpi_bridge *ab
)
{
u8 *path_name = acpi_path_name(ab->handle);
pciehprm_acpi_register_a_bridge (&acpi_bridges_head, pab, ab);
switch (ab->type) {
case BRIDGE_TYPE_HOST:
dbg("acpi_pciehprm: Registered PCI HOST Bridge(%02x) on s:b:d:f(%02x:%02x:%02x:%02x) [%s]\n",
ab->bus, ab->seg, ab->pbus, ab->pdevice, ab->pfunction, path_name);
break;
case BRIDGE_TYPE_P2P:
dbg("acpi_pciehprm: Registered PCI P2P Bridge(%02x-%02x) on s:b:d:f(%02x:%02x:%02x:%02x) [%s]\n",
ab->pbus, ab->bus, ab->seg, ab->pbus, ab->pdevice, ab->pfunction, path_name);
break;
};
/* build any immediate PHP slots under this pci bridge */
pciehprm_acpi_build_php_slots(ab, 1);
}
static struct acpi_bridge * add_p2p_bridge(
acpi_handle handle,
struct acpi_bridge *pab, /* parent */
ulong adr
)
{
struct acpi_bridge *ab;
struct pci_dev *pdev;
ulong devnum, funcnum;
u8 *path_name = acpi_path_name(handle);
ab = (struct acpi_bridge *) kmalloc (sizeof(struct acpi_bridge), GFP_KERNEL);
if (!ab) {
err("acpi_pciehprm: alloc for ab fail\n");
return NULL;
}
memset(ab, 0, sizeof(struct acpi_bridge));
devnum = (adr >> 16) & 0xffff;
funcnum = adr & 0xffff;
pdev = pci_find_slot(pab->bus, PCI_DEVFN(devnum, funcnum));
if (!pdev || !pdev->subordinate) {
err("acpi_pciehprm:%s is not a P2P Bridge\n", path_name);
kfree(ab);
return NULL;
}
ab->handle = handle;
ab->seg = pab->seg;
ab->pbus = pab->bus; /* or pdev->bus->number */
ab->pdevice = devnum; /* or PCI_SLOT(pdev->devfn) */
ab->pfunction = funcnum; /* or PCI_FUNC(pdev->devfn) */
ab->bus = pdev->subordinate->number;
ab->scanned = 0;
ab->type = BRIDGE_TYPE_P2P;
dbg("acpi_pciehprm: P2P(%x-%x) on pci=b:d:f(%x:%x:%x) acpi=b:d:f(%x:%x:%x) [%s]\n",
pab->bus, ab->bus, pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
pab->bus, (u32)devnum, (u32)funcnum, path_name);
build_a_bridge(pab, ab);
return ab;
}
static acpi_status scan_p2p_bridge(
acpi_handle handle,
u32 Level,
void *context,
void **retval
)
{
struct acpi_bridge *pab = (struct acpi_bridge *)context;
struct acpi_bridge *ab;
acpi_status status;
ulong adr = 0;
u8 *path_name = acpi_path_name(handle);
ulong devnum, funcnum;
struct pci_dev *pdev;
/* get device, function */
status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr);
if (ACPI_FAILURE(status)) {
if (status != AE_NOT_FOUND)
err("acpi_pciehprm:%s _ADR fail=0x%x\n", path_name, status);
return AE_OK;
}
devnum = (adr >> 16) & 0xffff;
funcnum = adr & 0xffff;
pdev = pci_find_slot(pab->bus, PCI_DEVFN(devnum, funcnum));
if (!pdev)
return AE_OK;
if (!pdev->subordinate)
return AE_OK;
ab = add_p2p_bridge(handle, pab, adr);
if (ab) {
status = acpi_walk_namespace ( ACPI_TYPE_DEVICE,
handle,
(u32)1,
scan_p2p_bridge,
ab,
NULL);
if (ACPI_FAILURE(status))
dbg("acpi_pciehprm:%s find_p2p fail=0x%x\n", path_name, status);
}
return AE_OK;
}
static struct acpi_bridge * add_host_bridge(
acpi_handle handle,
ulong segnum,
ulong busnum
)
{
ulong adr = 0;
acpi_status status;
struct acpi_bridge *ab;
u8 *path_name = acpi_path_name(handle);
/* get device, function: host br adr is always 0000 though. */
status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr);
if (ACPI_FAILURE(status)) {
err("acpi_pciehprm:%s _ADR fail=0x%x\n", path_name, status);
return NULL;
}
dbg("acpi_pciehprm: ROOT PCI seg(0x%x)bus(0x%x)dev(0x%x)func(0x%x) [%s]\n", (u32)segnum,
(u32)busnum, (u32)(adr >> 16) & 0xffff, (u32)adr & 0xffff, path_name);
ab = (struct acpi_bridge *) kmalloc (sizeof(struct acpi_bridge), GFP_KERNEL);
if (!ab) {
err("acpi_pciehprm: alloc for ab fail\n");
return NULL;
}
memset(ab, 0, sizeof(struct acpi_bridge));
ab->handle = handle;
ab->seg = (int)segnum;
ab->bus = ab->pbus = (int)busnum;
ab->pdevice = (int)(adr >> 16) & 0xffff;
ab->pfunction = (int)(adr & 0xffff);
ab->scanned = 0;
ab->type = BRIDGE_TYPE_HOST;
/* get root pci bridge's current resources */
status = acpi_get_crs(ab);
if (ACPI_FAILURE(status)) {
err("acpi_pciehprm:%s evaluate _CRS fail=0x%x\n", path_name, status);
kfree(ab);
return NULL;
}
build_a_bridge(ab, ab);
return ab;
}
static acpi_status acpi_scan_from_root_pci_callback (
acpi_handle handle,
u32 Level,
void *context,
void **retval
)
{
ulong segnum = 0;
ulong busnum = 0;
acpi_status status;
struct acpi_bridge *ab;
u8 *path_name = acpi_path_name(handle);
/* get bus number of this pci root bridge */
status = acpi_evaluate_integer(handle, METHOD_NAME__SEG, NULL, &segnum);
if (ACPI_FAILURE(status)) {
if (status != AE_NOT_FOUND) {
err("acpi_pciehprm:%s evaluate _SEG fail=0x%x\n", path_name, status);
return status;
}
segnum = 0;
}
/* get bus number of this pci root bridge */
status = acpi_evaluate_integer(handle, METHOD_NAME__BBN, NULL, &busnum);
if (ACPI_FAILURE(status)) {
err("acpi_pciehprm:%s evaluate _BBN fail=0x%x\n", path_name, status);
return (status);
}
ab = add_host_bridge(handle, segnum, busnum);
if (ab) {
status = acpi_walk_namespace ( ACPI_TYPE_DEVICE,
handle,
1,
scan_p2p_bridge,
ab,
NULL);
if (ACPI_FAILURE(status))
dbg("acpi_pciehprm:%s find_p2p fail=0x%x\n", path_name, status);
}
return AE_OK;
}
static int pciehprm_acpi_scan_pci (void)
{
acpi_status status;
/*
* TBD: traverse LDM device tree with the help of
* unified ACPI augmented for php device population.
*/
status = acpi_get_devices ( PCI_ROOT_HID_STRING,
acpi_scan_from_root_pci_callback,
NULL,
NULL );
if (ACPI_FAILURE(status)) {
err("acpi_pciehprm:get_device PCI ROOT HID fail=0x%x\n", status);
return -1;
}
return 0;
}
int pciehprm_init(enum php_ctlr_type ctlr_type)
{
int rc;
if (ctlr_type != PCI)
return -ENODEV;
dbg("pciehprm ACPI init <enter>\n");
acpi_bridges_head = NULL;
/* construct PCI bus:device tree of acpi_handles */
rc = pciehprm_acpi_scan_pci();
if (rc)
return rc;
dbg("pciehprm ACPI init %s\n", (rc)?"fail":"success");
return rc;
}
static void free_a_slot(struct acpi_php_slot *aps)
{
dbg(" free a php func of slot(0x%02x) on PCI b:d:f=0x%02x:%02x:%02x\n", aps->sun, aps->bus, aps->dev, aps->fun);
free_pci_resource (aps->io_head);
free_pci_resource (aps->bus_head);
free_pci_resource (aps->mem_head);
free_pci_resource (aps->p_mem_head);
kfree(aps);
}
static void free_a_bridge( struct acpi_bridge *ab)
{
struct acpi_php_slot *aps, *next;
switch (ab->type) {
case BRIDGE_TYPE_HOST:
dbg("Free ACPI PCI HOST Bridge(%x) [%s] on s:b:d:f(%x:%x:%x:%x)\n",
ab->bus, acpi_path_name(ab->handle), ab->seg, ab->pbus, ab->pdevice, ab->pfunction);
break;
case BRIDGE_TYPE_P2P:
dbg("Free ACPI PCI P2P Bridge(%x-%x) [%s] on s:b:d:f(%x:%x:%x:%x)\n",
ab->pbus, ab->bus, acpi_path_name(ab->handle), ab->seg, ab->pbus, ab->pdevice, ab->pfunction);
break;
};
/* free slots first */
for (aps = ab->slots; aps; aps = next) {
next = aps->next;
free_a_slot(aps);
}
free_pci_resource (ab->io_head);
free_pci_resource (ab->tio_head);
free_pci_resource (ab->bus_head);
free_pci_resource (ab->tbus_head);
free_pci_resource (ab->mem_head);
free_pci_resource (ab->tmem_head);
free_pci_resource (ab->p_mem_head);
free_pci_resource (ab->tp_mem_head);
kfree(ab);
}
static void pciehprm_free_bridges ( struct acpi_bridge *ab)
{
if (ab->child)
pciehprm_free_bridges (ab->child);
if (ab->next)
pciehprm_free_bridges (ab->next);
free_a_bridge(ab);
}
void pciehprm_cleanup(void)
{
pciehprm_free_bridges (acpi_bridges_head);
}
static int get_number_of_slots (
struct acpi_bridge *ab,
int selfonly
)
{
struct acpi_php_slot *aps;
int prev_slot = -1;
int slot_num = 0;
for ( aps = ab->slots; aps; aps = aps->next)
if (aps->dev != prev_slot) {
prev_slot = aps->dev;
slot_num++;
}
if (ab->child)
slot_num += get_number_of_slots (ab->child, 0);
if (selfonly)
return slot_num;
if (ab->next)
slot_num += get_number_of_slots (ab->next, 0);
return slot_num;
}
static int print_acpi_resources (struct acpi_bridge *ab)
{
struct acpi_php_slot *aps;
int i;
switch (ab->type) {
case BRIDGE_TYPE_HOST:
dbg("PCI HOST Bridge (%x) [%s]\n", ab->bus, acpi_path_name(ab->handle));
break;
case BRIDGE_TYPE_P2P:
dbg("PCI P2P Bridge (%x-%x) [%s]\n", ab->pbus, ab->bus, acpi_path_name(ab->handle));
break;
};
print_pci_resources (ab);
for ( i = -1, aps = ab->slots; aps; aps = aps->next) {
if (aps->dev == i)
continue;
dbg(" Slot sun(%x) s:b:d:f(%02x:%02x:%02x:%02x)\n", aps->sun, aps->seg, aps->bus, aps->dev, aps->fun);
print_slot_resources(aps);
i = aps->dev;
}
if (ab->child)
print_acpi_resources (ab->child);
if (ab->next)
print_acpi_resources (ab->next);
return 0;
}
int pciehprm_print_pirt(void)
{
dbg("PCIEHPRM ACPI Slots\n");
print_acpi_resources (acpi_bridges_head);
return 0;
}
static struct acpi_php_slot * get_acpi_slot (
struct acpi_bridge *ab,
u32 sun
)
{
struct acpi_php_slot *aps = NULL;
for ( aps = ab->slots; aps; aps = aps->next)
if (aps->sun == sun)
return aps;
if (!aps && ab->child) {
aps = (struct acpi_php_slot *)get_acpi_slot (ab->child, sun);
if (aps)
return aps;
}
if (!aps && ab->next) {
aps = (struct acpi_php_slot *)get_acpi_slot (ab->next, sun);
if (aps)
return aps;
}
return aps;
}
void * pciehprm_get_slot(struct slot *slot)
{
struct acpi_bridge *ab = acpi_bridges_head;
struct acpi_php_slot *aps = get_acpi_slot (ab, slot->number);
aps->slot = slot;
dbg("Got acpi slot sun(%x): s:b:d:f(%x:%x:%x:%x)\n", aps->sun, aps->seg, aps->bus, aps->dev, aps->fun);
return (void *)aps;
}
static void pciehprm_dump_func_res( struct pci_func *fun)
{
struct pci_func *func = fun;
if (func->bus_head) {
dbg(": BUS Resources:\n");
print_pci_resource (func->bus_head);
}
if (func->io_head) {
dbg(": IO Resources:\n");
print_pci_resource (func->io_head);
}
if (func->mem_head) {
dbg(": MEM Resources:\n");
print_pci_resource (func->mem_head);
}
if (func->p_mem_head) {
dbg(": PMEM Resources:\n");
print_pci_resource (func->p_mem_head);
}
}
static void pciehprm_dump_ctrl_res( struct controller *ctlr)
{
struct controller *ctrl = ctlr;
if (ctrl->bus_head) {
dbg(": BUS Resources:\n");
print_pci_resource (ctrl->bus_head);
}
if (ctrl->io_head) {
dbg(": IO Resources:\n");
print_pci_resource (ctrl->io_head);
}
if (ctrl->mem_head) {
dbg(": MEM Resources:\n");
print_pci_resource (ctrl->mem_head);
}
if (ctrl->p_mem_head) {
dbg(": PMEM Resources:\n");
print_pci_resource (ctrl->p_mem_head);
}
}
static int pciehprm_get_used_resources (
struct controller *ctrl,
struct pci_func *func
)
{
return pciehp_save_used_resources (ctrl, func, !DISABLE_CARD);
}
static int configure_existing_function(
struct controller *ctrl,
struct pci_func *func
)
{
int rc;
/* see how much resources the func has used. */
rc = pciehprm_get_used_resources (ctrl, func);
if (!rc) {
/* subtract the resources used by the func from ctrl resources */
rc = pciehprm_delete_resources (&ctrl->bus_head, func->bus_head);
rc |= pciehprm_delete_resources (&ctrl->io_head, func->io_head);
rc |= pciehprm_delete_resources (&ctrl->mem_head, func->mem_head);
rc |= pciehprm_delete_resources (&ctrl->p_mem_head, func->p_mem_head);
if (rc)
warn("aCEF: cannot del used resources\n");
} else
err("aCEF: cannot get used resources\n");
return rc;
}
static int bind_pci_resources_to_slots ( struct controller *ctrl)
{
struct pci_func *func;
int busn = ctrl->slot_bus;
int devn, funn;
u32 vid;
for (devn = 0; devn < 32; devn++) {
for (funn = 0; funn < 8; funn++) {
/*
if (devn == ctrl->device && funn == ctrl->function)
continue;
*/
/* find out if this entry is for an occupied slot */
vid = 0xFFFFFFFF;
pci_bus_read_config_dword(ctrl->pci_dev->subordinate, PCI_DEVFN(devn, funn), PCI_VENDOR_ID, &vid);
if (vid != 0xFFFFFFFF) {
dbg("%s: vid = %x\n", __FUNCTION__, vid);
func = pciehp_slot_find(busn, devn, funn);
if (!func)
continue;
configure_existing_function(ctrl, func);
dbg("aCCF:existing PCI 0x%x Func ResourceDump\n", ctrl->bus);
pciehprm_dump_func_res(func);
}
}
}
return 0;
}
static int bind_pci_resources(
struct controller *ctrl,
struct acpi_bridge *ab
)
{
int status = 0;
if (ab->bus_head) {
dbg("bapr: BUS Resources add on PCI 0x%x\n", ab->bus);
status = pciehprm_add_resources (&ctrl->bus_head, ab->bus_head);
if (pciehprm_delete_resources (&ab->bus_head, ctrl->bus_head))
warn("bapr: cannot sub BUS Resource on PCI 0x%x\n", ab->bus);
if (status) {
err("bapr: BUS Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status);
return status;
}
} else
info("bapr: No BUS Resource on PCI 0x%x.\n", ab->bus);
if (ab->io_head) {
dbg("bapr: IO Resources add on PCI 0x%x\n", ab->bus);
status = pciehprm_add_resources (&ctrl->io_head, ab->io_head);
if (pciehprm_delete_resources (&ab->io_head, ctrl->io_head))
warn("bapr: cannot sub IO Resource on PCI 0x%x\n", ab->bus);
if (status) {
err("bapr: IO Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status);
return status;
}
} else
info("bapr: No IO Resource on PCI 0x%x.\n", ab->bus);
if (ab->mem_head) {
dbg("bapr: MEM Resources add on PCI 0x%x\n", ab->bus);
status = pciehprm_add_resources (&ctrl->mem_head, ab->mem_head);
if (pciehprm_delete_resources (&ab->mem_head, ctrl->mem_head))
warn("bapr: cannot sub MEM Resource on PCI 0x%x\n", ab->bus);
if (status) {
err("bapr: MEM Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status);
return status;
}
} else
info("bapr: No MEM Resource on PCI 0x%x.\n", ab->bus);
if (ab->p_mem_head) {
dbg("bapr: PMEM Resources add on PCI 0x%x\n", ab->bus);
status = pciehprm_add_resources (&ctrl->p_mem_head, ab->p_mem_head);
if (pciehprm_delete_resources (&ab->p_mem_head, ctrl->p_mem_head))
warn("bapr: cannot sub PMEM Resource on PCI 0x%x\n", ab->bus);
if (status) {
err("bapr: PMEM Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status);
return status;
}
} else
info("bapr: No PMEM Resource on PCI 0x%x.\n", ab->bus);
return status;
}
static int no_pci_resources( struct acpi_bridge *ab)
{
return !(ab->p_mem_head || ab->mem_head || ab->io_head || ab->bus_head);
}
static int find_pci_bridge_resources (
struct controller *ctrl,
struct acpi_bridge *ab
)
{
int rc = 0;
struct pci_func func;
memset(&func, 0, sizeof(struct pci_func));
func.bus = ab->pbus;
func.device = ab->pdevice;
func.function = ab->pfunction;
func.is_a_board = 1;
/* Get used resources for this PCI bridge */
rc = pciehp_save_used_resources (ctrl, &func, !DISABLE_CARD);
ab->io_head = func.io_head;
ab->mem_head = func.mem_head;
ab->p_mem_head = func.p_mem_head;
ab->bus_head = func.bus_head;
if (ab->bus_head)
pciehprm_delete_resource(&ab->bus_head, ctrl->pci_dev->subordinate->number, 1);
return rc;
}
static int get_pci_resources_from_bridge(
struct controller *ctrl,
struct acpi_bridge *ab
)
{
int rc = 0;
dbg("grfb: Get Resources for PCI 0x%x from actual PCI bridge 0x%x.\n", ctrl->bus, ab->bus);
rc = find_pci_bridge_resources (ctrl, ab);
pciehp_resource_sort_and_combine(&ab->bus_head);
pciehp_resource_sort_and_combine(&ab->io_head);
pciehp_resource_sort_and_combine(&ab->mem_head);
pciehp_resource_sort_and_combine(&ab->p_mem_head);
pciehprm_add_resources (&ab->tbus_head, ab->bus_head);
pciehprm_add_resources (&ab->tio_head, ab->io_head);
pciehprm_add_resources (&ab->tmem_head, ab->mem_head);
pciehprm_add_resources (&ab->tp_mem_head, ab->p_mem_head);
return rc;
}
static int get_pci_resources(
struct controller *ctrl,
struct acpi_bridge *ab
)
{
int rc = 0;
if (no_pci_resources(ab)) {
dbg("spbr:PCI 0x%x has no resources. Get parent resources.\n", ab->bus);
rc = get_pci_resources_from_bridge(ctrl, ab);
}
return rc;
}
/*
* Get resources for this ctrl.
* 1. get total resources from ACPI _CRS or bridge (this ctrl)
* 2. find used resources of existing adapters
* 3. subtract used resources from total resources
*/
int pciehprm_find_available_resources( struct controller *ctrl)
{
int rc = 0;
struct acpi_bridge *ab;
ab = find_acpi_bridge_by_bus(acpi_bridges_head, ctrl->seg, ctrl->pci_dev->subordinate->number);
if (!ab) {
err("pfar:cannot locate acpi bridge of PCI 0x%x.\n", ctrl->pci_dev->subordinate->number);
return -1;
}
if (no_pci_resources(ab)) {
rc = get_pci_resources(ctrl, ab);
if (rc) {
err("pfar:cannot get pci resources of PCI 0x%x.\n", ctrl->pci_dev->subordinate->number);
return -1;
}
}
rc = bind_pci_resources(ctrl, ab);
dbg("pfar:pre-Bind PCI 0x%x Ctrl Resource Dump\n", ctrl->pci_dev->subordinate->number);
pciehprm_dump_ctrl_res(ctrl);
bind_pci_resources_to_slots (ctrl);
dbg("pfar:post-Bind PCI 0x%x Ctrl Resource Dump\n", ctrl->pci_dev->subordinate->number);
pciehprm_dump_ctrl_res(ctrl);
return rc;
}
int pciehprm_set_hpp(
struct controller *ctrl,
struct pci_func *func,
u8 card_type
)
{
struct acpi_bridge *ab;
struct pci_bus lpci_bus, *pci_bus;
int rc = 0;
unsigned int devfn;
u8 cls= 0x08; /* default cache line size */
u8 lt = 0x40; /* default latency timer */
u8 ep = 0;
u8 es = 0;
memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
pci_bus = &lpci_bus;
pci_bus->number = func->bus;
devfn = PCI_DEVFN(func->device, func->function);
ab = find_acpi_bridge_by_bus(acpi_bridges_head, ctrl->seg, ctrl->bus);
if (ab) {
if (ab->_hpp) {
lt = (u8)ab->_hpp->latency_timer;
cls = (u8)ab->_hpp->cache_line_size;
ep = (u8)ab->_hpp->enable_perr;
es = (u8)ab->_hpp->enable_serr;
} else
dbg("_hpp: no _hpp for B/D/F=%#x/%#x/%#x. use default value\n", func->bus, func->device, func->function);
} else
dbg("_hpp: no acpi bridge for B/D/F = %#x/%#x/%#x. use default value\n", func->bus, func->device, func->function);
if (card_type == PCI_HEADER_TYPE_BRIDGE) {
/* set subordinate Latency Timer */
rc |= pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, lt);
}
/* set base Latency Timer */
rc |= pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, lt);
dbg(" set latency timer =0x%02x: %x\n", lt, rc);
rc |= pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, cls);
dbg(" set cache_line_size=0x%02x: %x\n", cls, rc);
return rc;
}
void pciehprm_enable_card(
struct controller *ctrl,
struct pci_func *func,
u8 card_type)
{
u16 command, cmd, bcommand, bcmd;
struct pci_bus lpci_bus, *pci_bus;
struct acpi_bridge *ab;
unsigned int devfn;
int rc;
memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
pci_bus = &lpci_bus;
pci_bus->number = func->bus;
devfn = PCI_DEVFN(func->device, func->function);
rc = pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &command);
if (card_type == PCI_HEADER_TYPE_BRIDGE) {
rc = pci_bus_read_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, &bcommand);
}
cmd = command = command | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE
| PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
bcmd = bcommand = bcommand | PCI_BRIDGE_CTL_NO_ISA;
ab = find_acpi_bridge_by_bus(acpi_bridges_head, ctrl->seg, ctrl->bus);
if (ab) {
if (ab->_hpp) {
if (ab->_hpp->enable_perr) {
command |= PCI_COMMAND_PARITY;
bcommand |= PCI_BRIDGE_CTL_PARITY;
} else {
command &= ~PCI_COMMAND_PARITY;
bcommand &= ~PCI_BRIDGE_CTL_PARITY;
}
if (ab->_hpp->enable_serr) {
command |= PCI_COMMAND_SERR;
bcommand |= PCI_BRIDGE_CTL_SERR;
} else {
command &= ~PCI_COMMAND_SERR;
bcommand &= ~PCI_BRIDGE_CTL_SERR;
}
} else
dbg("no _hpp for B/D/F = %#x/%#x/%#x.\n", func->bus, func->device, func->function);
} else
dbg("no acpi bridge for B/D/F = %#x/%#x/%#x.\n", func->bus, func->device, func->function);
if (command != cmd) {
rc = pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command);
}
if ((card_type == PCI_HEADER_TYPE_BRIDGE) && (bcommand != bcmd)) {
rc = pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, bcommand);
}
}
/*
* PCIEHPRM NONACPI: PHP Resource Manager for Non-ACPI/Legacy platform
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
* Copyright (C) 2003-2004 Intel Corporation
*
* 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>, <dely.l.sy@intel.com>
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#ifdef CONFIG_IA64
#include <asm/iosapic.h>
#endif
#include "pciehp.h"
#include "pciehprm.h"
#include "pciehprm_nonacpi.h"
void pciehprm_cleanup(void)
{
return;
}
int pciehprm_print_pirt(void)
{
return 0;
}
void * pciehprm_get_slot(struct slot *slot)
{
return NULL;
}
int pciehprm_get_physical_slot_number(struct controller *ctrl, u32 *sun, u8 busnum, u8 devnum)
{
*sun = (u8) (ctrl->first_slot);
return 0;
}
static void print_pci_resource ( struct pci_resource *aprh)
{
struct pci_resource *res;
for (res = aprh; res; res = res->next)
dbg(" base= 0x%x length= 0x%x\n", res->base, res->length);
}
static void phprm_dump_func_res( struct pci_func *fun)
{
struct pci_func *func = fun;
if (func->bus_head) {
dbg(": BUS Resources:\n");
print_pci_resource (func->bus_head);
}
if (func->io_head) {
dbg(": IO Resources:\n");
print_pci_resource (func->io_head);
}
if (func->mem_head) {
dbg(": MEM Resources:\n");
print_pci_resource (func->mem_head);
}
if (func->p_mem_head) {
dbg(": PMEM Resources:\n");
print_pci_resource (func->p_mem_head);
}
}
static int phprm_get_used_resources (
struct controller *ctrl,
struct pci_func *func
)
{
return pciehp_save_used_resources (ctrl, func, !DISABLE_CARD);
}
static int phprm_delete_resource(
struct pci_resource **aprh,
ulong base,
ulong size)
{
struct pci_resource *res;
struct pci_resource *prevnode;
struct pci_resource *split_node;
ulong tbase;
pciehp_resource_sort_and_combine(aprh);
for (res = *aprh; res; res = res->next) {
if (res->base > base)
continue;
if ((res->base + res->length) < (base + size))
continue;
if (res->base < base) {
tbase = base;
if ((res->length - (tbase - res->base)) < size)
continue;
split_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return -ENOMEM;
split_node->base = res->base;
split_node->length = tbase - res->base;
res->base = tbase;
res->length -= split_node->length;
split_node->next = res->next;
res->next = split_node;
}
if (res->length >= size) {
split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return -ENOMEM;
split_node->base = res->base + size;
split_node->length = res->length - size;
res->length = size;
split_node->next = res->next;
res->next = split_node;
}
if (*aprh == res) {
*aprh = res->next;
} else {
prevnode = *aprh;
while (prevnode->next != res)
prevnode = prevnode->next;
prevnode->next = res->next;
}
res->next = NULL;
kfree(res);
break;
}
return 0;
}
static int phprm_delete_resources(
struct pci_resource **aprh,
struct pci_resource *this
)
{
struct pci_resource *res;
for (res = this; res; res = res->next)
phprm_delete_resource(aprh, res->base, res->length);
return 0;
}
static int configure_existing_function(
struct controller *ctrl,
struct pci_func *func
)
{
int rc;
/* see how much resources the func has used. */
rc = phprm_get_used_resources (ctrl, func);
if (!rc) {
/* subtract the resources used by the func from ctrl resources */
rc = phprm_delete_resources (&ctrl->bus_head, func->bus_head);
rc |= phprm_delete_resources (&ctrl->io_head, func->io_head);
rc |= phprm_delete_resources (&ctrl->mem_head, func->mem_head);
rc |= phprm_delete_resources (&ctrl->p_mem_head, func->p_mem_head);
if (rc)
warn("aCEF: cannot del used resources\n");
} else
err("aCEF: cannot get used resources\n");
return rc;
}
static int pciehprm_delete_resource(
struct pci_resource **aprh,
ulong base,
ulong size)
{
struct pci_resource *res;
struct pci_resource *prevnode;
struct pci_resource *split_node;
ulong tbase;
pciehp_resource_sort_and_combine(aprh);
for (res = *aprh; res; res = res->next) {
if (res->base > base)
continue;
if ((res->base + res->length) < (base + size))
continue;
if (res->base < base) {
tbase = base;
if ((res->length - (tbase - res->base)) < size)
continue;
split_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return -ENOMEM;
split_node->base = res->base;
split_node->length = tbase - res->base;
res->base = tbase;
res->length -= split_node->length;
split_node->next = res->next;
res->next = split_node;
}
if (res->length >= size) {
split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return -ENOMEM;
split_node->base = res->base + size;
split_node->length = res->length - size;
res->length = size;
split_node->next = res->next;
res->next = split_node;
}
if (*aprh == res) {
*aprh = res->next;
} else {
prevnode = *aprh;
while (prevnode->next != res)
prevnode = prevnode->next;
prevnode->next = res->next;
}
res->next = NULL;
kfree(res);
break;
}
return 0;
}
static int bind_pci_resources_to_slots ( struct controller *ctrl)
{
struct pci_func *func;
int busn = ctrl->slot_bus;
int devn, funn;
u32 vid;
for (devn = 0; devn < 32; devn++) {
for (funn = 0; funn < 8; funn++) {
/*
if (devn == ctrl->device && funn == ctrl->function)
continue;
*/
/* find out if this entry is for an occupied slot */
vid = 0xFFFFFFFF;
pci_bus_read_config_dword(ctrl->pci_dev->subordinate, PCI_DEVFN(devn, funn), PCI_VENDOR_ID, &vid);
if (vid != 0xFFFFFFFF) {
dbg("%s: vid = %x bus %x dev %x fun %x\n", __FUNCTION__,
vid, busn, devn, funn);
func = pciehp_slot_find(busn, devn, funn);
dbg("%s: func = %p\n", __FUNCTION__,func);
if (!func)
continue;
configure_existing_function(ctrl, func);
dbg("aCCF:existing PCI 0x%x Func ResourceDump\n", ctrl->bus);
phprm_dump_func_res(func);
}
}
}
return 0;
}
static void phprm_dump_ctrl_res( struct controller *ctlr)
{
struct controller *ctrl = ctlr;
if (ctrl->bus_head) {
dbg(": BUS Resources:\n");
print_pci_resource (ctrl->bus_head);
}
if (ctrl->io_head) {
dbg(": IO Resources:\n");
print_pci_resource (ctrl->io_head);
}
if (ctrl->mem_head) {
dbg(": MEM Resources:\n");
print_pci_resource (ctrl->mem_head);
}
if (ctrl->p_mem_head) {
dbg(": PMEM Resources:\n");
print_pci_resource (ctrl->p_mem_head);
}
}
/*
* phprm_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 pciehprm_find_available_resources(struct controller *ctrl)
{
struct pci_func func;
u32 rc;
memset(&func, 0, sizeof(struct pci_func));
func.bus = ctrl->bus;
func.device = ctrl->device;
func.function = ctrl->function;
func.is_a_board = 1;
/* Get resources for this PCI bridge */
rc = pciehp_save_used_resources (ctrl, &func, !DISABLE_CARD);
dbg("%s: pciehp_save_used_resources rc = %d\n", __FUNCTION__, rc);
if (func.mem_head)
func.mem_head->next = ctrl->mem_head;
ctrl->mem_head = func.mem_head;
if (func.p_mem_head)
func.p_mem_head->next = ctrl->p_mem_head;
ctrl->p_mem_head = func.p_mem_head;
if (func.io_head)
func.io_head->next = ctrl->io_head;
ctrl->io_head = func.io_head;
if(func.bus_head)
func.bus_head->next = ctrl->bus_head;
ctrl->bus_head = func.bus_head;
if (ctrl->bus_head)
pciehprm_delete_resource(&ctrl->bus_head, ctrl->pci_dev->subordinate->number, 1);
dbg("%s:pre-Bind PCI 0x%x Ctrl Resource Dump\n", __FUNCTION__, ctrl->bus);
phprm_dump_ctrl_res(ctrl);
dbg("%s: before bind_pci_resources_to slots\n", __FUNCTION__);
bind_pci_resources_to_slots (ctrl);
dbg("%s:post-Bind PCI 0x%x Ctrl Resource Dump\n", __FUNCTION__, ctrl->bus);
phprm_dump_ctrl_res(ctrl);
return (rc);
}
int pciehprm_set_hpp(
struct controller *ctrl,
struct pci_func *func,
u8 card_type)
{
u32 rc;
u8 temp_byte;
struct pci_bus lpci_bus, *pci_bus;
unsigned int devfn;
memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
pci_bus = &lpci_bus;
pci_bus->number = func->bus;
devfn = PCI_DEVFN(func->device, func->function);
temp_byte = 0x40; /* hard coded value for LT */
if (card_type == PCI_HEADER_TYPE_BRIDGE) {
/* set subordinate Latency Timer */
rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, temp_byte);
if (rc) {
dbg("%s: set secondary LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__,
func->bus, func->device, func->function);
return rc;
}
}
/* set base Latency Timer */
rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte);
if (rc) {
dbg("%s: set LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function);
return rc;
}
/* set Cache Line size */
temp_byte = 0x08; /* hard coded value for CLS */
rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte);
if (rc) {
dbg("%s: set CLS error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function);
}
/* set enable_perr */
/* set enable_serr */
return rc;
}
void pciehprm_enable_card(
struct controller *ctrl,
struct pci_func *func,
u8 card_type)
{
u16 command, bcommand;
struct pci_bus lpci_bus, *pci_bus;
unsigned int devfn;
int rc;
memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
pci_bus = &lpci_bus;
pci_bus->number = func->bus;
devfn = PCI_DEVFN(func->device, func->function);
rc = pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &command);
command |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR
| PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE
| PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
rc = pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command);
if (card_type == PCI_HEADER_TYPE_BRIDGE) {
rc = pci_bus_read_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, &bcommand);
bcommand |= PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR
| PCI_BRIDGE_CTL_NO_ISA;
rc = pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, bcommand);
}
}
static int legacy_pciehprm_init_pci(void)
{
return 0;
}
int pciehprm_init(enum php_ctlr_type ctrl_type)
{
int retval;
switch (ctrl_type) {
case PCI:
retval = legacy_pciehprm_init_pci();
break;
default:
retval = -ENODEV;
break;
}
return retval;
}
/*
* PCIEHPRM NONACPI: PHP Resource Manager for Non-ACPI/Legacy platform
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
* Copyright (C) 2003-2004 Intel Corporation
*
* 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>, <dely.l.sy@intel.com>
*
*/
#ifndef _PCIEHPRM_NONACPI_H_
#define _PCIEHPRM_NONACPI_H_
struct irq_info {
u8 bus, devfn; /* bus, device and function */
struct {
u8 link; /* IRQ line ID, chipset dependent, 0=not routed */
u16 bitmap; /* Available IRQs */
} __attribute__ ((packed)) irq[4];
u8 slot; /* slot number, 0=onboard */
u8 rfu;
} __attribute__ ((packed));
struct irq_routing_table {
u32 signature; /* PIRQ_SIGNATURE should be here */
u16 version; /* PIRQ_VERSION */
u16 size; /* Table size in bytes */
u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */
u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */
u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */
u32 miniport_data; /* Crap */
u8 rfu[11];
u8 checksum; /* Modulo 256 checksum must give zero */
struct irq_info slots[0];
} __attribute__ ((packed));
#endif /* _PCIEHPRM_NONACPI_H_ */
/*
* Standard Hot Plug Controller Driver
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM
* Copyright (C) 2003-2004 Intel Corporation
*
* 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>,<dely.l.sy@intel.com>
*
*/
#ifndef _SHPCHP_H
#define _SHPCHP_H
#include <linux/types.h>
#include <linux/pci.h>
#include <asm/semaphore.h>
#include <asm/io.h>
#include "pci_hotplug.h"
#if !defined(CONFIG_HOTPLUG_PCI_SHPC_MODULE)
#define MY_NAME "shpchp"
#else
#define MY_NAME THIS_MODULE->name
#endif
extern int shpchp_poll_mode;
extern int shpchp_poll_time;
extern int shpchp_debug;
/*#define dbg(format, arg...) do { if (shpchp_debug) printk(KERN_DEBUG "%s: " format, MY_NAME , ## arg); } while (0)*/
#define dbg(format, arg...) do { if (shpchp_debug) printk("%s: " format, MY_NAME , ## arg); } while (0)
#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME , ## arg)
struct pci_func {
struct pci_func *next;
u8 bus;
u8 device;
u8 function;
u8 is_a_board;
u16 status;
u8 configured;
u8 switch_save;
u8 presence_save;
u32 base_length[0x06];
u8 base_type[0x06];
u16 reserved2;
u32 config_space[0x20];
struct pci_resource *mem_head;
struct pci_resource *p_mem_head;
struct pci_resource *io_head;
struct pci_resource *bus_head;
struct pci_dev* pci_dev;
};
#define SLOT_MAGIC 0x67267321
struct slot {
u32 magic;
struct slot *next;
u8 bus;
u8 device;
u32 number;
u8 is_a_board;
u8 configured;
u8 state;
u8 switch_save;
u8 presence_save;
u32 capabilities;
u16 reserved2;
struct timer_list task_event;
u8 hp_slot;
struct controller *ctrl;
struct hpc_ops *hpc_ops;
struct hotplug_slot *hotplug_slot;
struct list_head slot_list;
};
struct pci_resource {
struct pci_resource * next;
u32 base;
u32 length;
};
struct event_info {
u32 event_type;
u8 hp_slot;
};
struct controller {
struct controller *next;
struct semaphore crit_sect; /* critical section semaphore */
void * hpc_ctlr_handle; /* HPC controller handle */
int num_slots; /* Number of slots on ctlr */
int slot_num_inc; /* 1 or -1 */
struct pci_resource *mem_head;
struct pci_resource *p_mem_head;
struct pci_resource *io_head;
struct pci_resource *bus_head;
struct pci_dev *pci_dev;
struct pci_bus *pci_bus;
struct event_info event_queue[10];
struct slot *slot;
struct hpc_ops *hpc_ops;
wait_queue_head_t queue; /* sleep & wake process */
u8 next_event;
u8 seg;
u8 bus;
u8 device;
u8 function;
u8 rev;
u8 slot_device_offset;
u8 add_support;
enum pci_bus_speed speed;
u32 first_slot; /* First physical slot number */
u8 slot_bus; /* Bus where the slots handled by this controller sit */
u8 push_flag;
u16 ctlrcap;
u16 vendor_id;
};
struct irq_mapping {
u8 barber_pole;
u8 valid_INT;
u8 interrupt[4];
};
struct resource_lists {
struct pci_resource *mem_head;
struct pci_resource *p_mem_head;
struct pci_resource *io_head;
struct pci_resource *bus_head;
struct irq_mapping *irqs;
};
/* Define AMD SHPC ID */
#define PCI_DEVICE_ID_AMD_GOLAM_7450 0x7450
/* Define SHPC CAP ID - not defined in kernel yet */
#define PCI_CAP_ID_SHPC 0x0C
#define INT_BUTTON_IGNORE 0
#define INT_PRESENCE_ON 1
#define INT_PRESENCE_OFF 2
#define INT_SWITCH_CLOSE 3
#define INT_SWITCH_OPEN 4
#define INT_POWER_FAULT 5
#define INT_POWER_FAULT_CLEAR 6
#define INT_BUTTON_PRESS 7
#define INT_BUTTON_RELEASE 8
#define INT_BUTTON_CANCEL 9
#define STATIC_STATE 0
#define BLINKINGON_STATE 1
#define BLINKINGOFF_STATE 2
#define POWERON_STATE 3
#define POWEROFF_STATE 4
#define PCI_TO_PCI_BRIDGE_CLASS 0x00060400
/* Error messages */
#define INTERLOCK_OPEN 0x00000002
#define ADD_NOT_SUPPORTED 0x00000003
#define CARD_FUNCTIONING 0x00000005
#define ADAPTER_NOT_SAME 0x00000006
#define NO_ADAPTER_PRESENT 0x00000009
#define NOT_ENOUGH_RESOURCES 0x0000000B
#define DEVICE_TYPE_NOT_SUPPORTED 0x0000000C
#define WRONG_BUS_FREQUENCY 0x0000000D
#define POWER_FAILURE 0x0000000E
#define REMOVE_NOT_SUPPORTED 0x00000003
#define DISABLE_CARD 1
/*
* error Messages
*/
#define msg_initialization_err "Initialization failure, error=%d\n"
#define msg_HPC_rev_error "Unsupported revision of the PCI hot plug controller found.\n"
#define msg_HPC_non_shpc "The PCI hot plug controller is not supported by this driver.\n"
#define msg_HPC_not_supported "This system is not supported by this version of shpcphd mdoule. Upgrade to a newer version of shpchpd\n"
#define msg_unable_to_save "Unable to store PCI hot plug add resource information. This system must be rebooted before adding any PCI devices.\n"
#define msg_button_on "PCI slot #%d - powering on due to button press.\n"
#define msg_button_off "PCI slot #%d - powering off due to button press.\n"
#define msg_button_cancel "PCI slot #%d - action canceled due to button press.\n"
#define msg_button_ignore "PCI slot #%d - button press ignored. (action in progress...)\n"
/* sysfs functions for the hotplug controller info */
extern void shpchp_create_ctrl_files (struct controller *ctrl);
/* controller functions */
extern void shpchp_pushbutton_thread(unsigned long event_pointer);
extern int shpchprm_find_available_resources(struct controller *ctrl);
extern int shpchp_event_start_thread(void);
extern void shpchp_event_stop_thread(void);
extern struct pci_func *shpchp_slot_create(unsigned char busnumber);
extern struct pci_func *shpchp_slot_find(unsigned char bus, unsigned char device, unsigned char index);
extern int shpchp_enable_slot(struct slot *slot);
extern int shpchp_disable_slot(struct slot *slot);
extern u8 shpchp_handle_attention_button(u8 hp_slot, void *inst_id);
extern u8 shpchp_handle_switch_change(u8 hp_slot, void *inst_id);
extern u8 shpchp_handle_presence_change(u8 hp_slot, void *inst_id);
extern u8 shpchp_handle_power_fault(u8 hp_slot, void *inst_id);
/* resource functions */
extern int shpchp_resource_sort_and_combine(struct pci_resource **head);
/* pci functions */
extern int shpchp_set_irq(u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num);
/*extern int shpchp_get_bus_dev(struct controller *ctrl, u8 *bus_num, u8 *dev_num, struct slot *slot);*/
extern int shpchp_save_config(struct controller *ctrl, int busnumber, int num_ctlr_slots, int first_device_num);
extern int shpchp_save_used_resources(struct controller *ctrl, struct pci_func * func, int flag);
extern int shpchp_save_slot_config(struct controller *ctrl, struct pci_func * new_slot);
extern void shpchp_destroy_board_resources(struct pci_func * func);
extern int shpchp_return_board_resources(struct pci_func * func, struct resource_lists * resources);
extern void shpchp_destroy_resource_list(struct resource_lists * resources);
extern int shpchp_configure_device(struct controller* ctrl, struct pci_func* func);
extern int shpchp_unconfigure_device(struct pci_func* func);
/* Global variables */
extern struct controller *shpchp_ctrl_list;
extern struct pci_func *shpchp_slot_list[256];
/* These are added to support AMD shpc */
extern u8 shpchp_nic_irq;
extern u8 shpchp_disk_irq;
struct ctrl_reg {
volatile u32 base_offset;
volatile u32 slot_avail1;
volatile u32 slot_avail2;
volatile u32 slot_config;
volatile u16 sec_bus_config;
volatile u8 msi_ctrl;
volatile u8 prog_interface;
volatile u16 cmd;
volatile u16 cmd_status;
volatile u32 intr_loc;
volatile u32 serr_loc;
volatile u32 serr_intr_enable;
volatile u32 slot1;
volatile u32 slot2;
volatile u32 slot3;
volatile u32 slot4;
volatile u32 slot5;
volatile u32 slot6;
volatile u32 slot7;
volatile u32 slot8;
volatile u32 slot9;
volatile u32 slot10;
volatile u32 slot11;
volatile u32 slot12;
} __attribute__ ((packed));
/* offsets to the controller registers based on the above structure layout */
enum ctrl_offsets {
BASE_OFFSET = offsetof(struct ctrl_reg, base_offset),
SLOT_AVAIL1 = offsetof(struct ctrl_reg, slot_avail1),
SLOT_AVAIL2 = offsetof(struct ctrl_reg, slot_avail2),
SLOT_CONFIG = offsetof(struct ctrl_reg, slot_config),
SEC_BUS_CONFIG = offsetof(struct ctrl_reg, sec_bus_config),
MSI_CTRL = offsetof(struct ctrl_reg, msi_ctrl),
PROG_INTERFACE = offsetof(struct ctrl_reg, prog_interface),
CMD = offsetof(struct ctrl_reg, cmd),
CMD_STATUS = offsetof(struct ctrl_reg, cmd_status),
INTR_LOC = offsetof(struct ctrl_reg, intr_loc),
SERR_LOC = offsetof(struct ctrl_reg, serr_loc),
SERR_INTR_ENABLE = offsetof(struct ctrl_reg, serr_intr_enable),
SLOT1 = offsetof(struct ctrl_reg, slot1),
SLOT2 = offsetof(struct ctrl_reg, slot2),
SLOT3 = offsetof(struct ctrl_reg, slot3),
SLOT4 = offsetof(struct ctrl_reg, slot4),
SLOT5 = offsetof(struct ctrl_reg, slot5),
SLOT6 = offsetof(struct ctrl_reg, slot6),
SLOT7 = offsetof(struct ctrl_reg, slot7),
SLOT8 = offsetof(struct ctrl_reg, slot8),
SLOT9 = offsetof(struct ctrl_reg, slot9),
SLOT10 = offsetof(struct ctrl_reg, slot10),
SLOT11 = offsetof(struct ctrl_reg, slot11),
SLOT12 = offsetof(struct ctrl_reg, slot12),
};
typedef u8(*php_intr_callback_t) (unsigned int change_id, void *instance_id);
struct php_ctlr_state_s {
struct php_ctlr_state_s *pnext;
struct pci_dev *pci_dev;
unsigned int irq;
unsigned long flags; /* spinlock's */
u32 slot_device_offset;
u32 num_slots;
struct timer_list int_poll_timer; /* Added for poll event */
php_intr_callback_t attention_button_callback;
php_intr_callback_t switch_change_callback;
php_intr_callback_t presence_change_callback;
php_intr_callback_t power_fault_callback;
void *callback_instance_id;
void *creg; /* Ptr to controller register space */
};
/* Inline functions */
/* Inline functions to check the sanity of a pointer that is passed to us */
static inline int slot_paranoia_check (struct slot *slot, const char *function)
{
if (!slot) {
dbg("%s - slot == NULL", function);
return -1;
}
if (slot->magic != SLOT_MAGIC) {
dbg("%s - bad magic number for slot", function);
return -1;
}
if (!slot->hotplug_slot) {
dbg("%s - slot->hotplug_slot == NULL!", function);
return -1;
}
return 0;
}
static inline struct slot *get_slot (struct hotplug_slot *hotplug_slot, const char *function)
{
struct slot *slot;
if (!hotplug_slot) {
dbg("%s - hotplug_slot == NULL\n", function);
return NULL;
}
slot = (struct slot *)hotplug_slot->private;
if (slot_paranoia_check (slot, function))
return NULL;
return slot;
}
static inline struct slot *shpchp_find_slot (struct controller *ctrl, u8 device)
{
struct slot *p_slot, *tmp_slot = NULL;
if (!ctrl)
return NULL;
p_slot = ctrl->slot;
dbg("p_slot = %p\n", p_slot);
while (p_slot && (p_slot->device != device)) {
tmp_slot = p_slot;
p_slot = p_slot->next;
dbg("In while loop, p_slot = %p\n", p_slot);
}
if (p_slot == NULL) {
err("ERROR: shpchp_find_slot device=0x%x\n", device);
p_slot = tmp_slot;
}
return (p_slot);
}
static inline int wait_for_ctrl_irq (struct controller *ctrl)
{
DECLARE_WAITQUEUE(wait, current);
int retval = 0;
dbg("%s : start\n",__FUNCTION__);
add_wait_queue(&ctrl->queue, &wait);
set_current_state(TASK_INTERRUPTIBLE);
if (!shpchp_poll_mode) {
/* Sleep for up to 1 second */
schedule_timeout(1*HZ);
} else {
/* Sleep for up to 1.5 second */
schedule_timeout(1.5*HZ);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&ctrl->queue, &wait);
if (signal_pending(current))
retval = -EINTR;
dbg("%s : end\n", __FUNCTION__);
return retval;
}
/* Puts node back in the resource list pointed to by head */
static inline void return_resource(struct pci_resource **head, struct pci_resource *node)
{
if (!node || !head)
return;
node->next = *head;
*head = node;
}
#define SLOT_NAME_SIZE 10
static inline void make_slot_name(char *buffer, int buffer_size, struct slot *slot)
{
snprintf(buffer, buffer_size, "%d", slot->number);
}
enum php_ctlr_type {
PCI,
ISA,
ACPI
};
int shpc_init( struct controller *ctrl, struct pci_dev *pdev,
php_intr_callback_t attention_button_callback,
php_intr_callback_t switch_change_callback,
php_intr_callback_t presence_change_callback,
php_intr_callback_t power_fault_callback);
int shpc_get_ctlr_slot_config( struct controller *ctrl,
int *num_ctlr_slots,
int *first_device_num,
int *physical_slot_num,
int *updown,
int *flags);
struct hpc_ops {
int (*power_on_slot ) (struct slot *slot);
int (*slot_enable ) (struct slot *slot);
int (*slot_disable ) (struct slot *slot);
int (*enable_all_slots) (struct slot *slot);
int (*pwr_on_all_slots) (struct slot *slot);
int (*set_bus_speed_mode) (struct slot *slot, enum pci_bus_speed speed);
int (*get_power_status) (struct slot *slot, u8 *status);
int (*get_attention_status) (struct slot *slot, u8 *status);
int (*set_attention_status) (struct slot *slot, u8 status);
int (*get_latch_status) (struct slot *slot, u8 *status);
int (*get_adapter_status) (struct slot *slot, u8 *status);
int (*get_max_bus_speed) (struct slot *slot, enum pci_bus_speed *speed);
int (*get_cur_bus_speed) (struct slot *slot, enum pci_bus_speed *speed);
int (*get_adapter_speed) (struct slot *slot, enum pci_bus_speed *speed);
int (*get_mode1_ECC_cap) (struct slot *slot, u8 *mode);
int (*get_prog_int) (struct slot *slot, u8 *prog_int);
int (*query_power_fault) (struct slot *slot);
void (*green_led_on) (struct slot *slot);
void (*green_led_off) (struct slot *slot);
void (*green_led_blink) (struct slot *slot);
void (*release_ctlr) (struct controller *ctrl);
int (*check_cmd_status) (struct controller *ctrl);
};
#endif /* _SHPCHP_H */
/*
* Standard 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.
* Copyright (C) 2003-2004 Intel Corporation
*
* 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>, <dely.l.sy@intel.com>
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#include "shpchp.h"
#include "shpchprm.h"
/* Global variables */
int shpchp_debug;
int shpchp_poll_mode;
int shpchp_poll_time;
struct controller *shpchp_ctrl_list; /* = NULL */
struct pci_func *shpchp_slot_list[256];
#define DRIVER_VERSION "0.4"
#define DRIVER_AUTHOR "Dan Zink <dan.zink@compaq.com>, Greg Kroah-Hartman <greg@kroah.com>, Dely Sy <dely.l.sy@intel.com>"
#define DRIVER_DESC "Standard Hot Plug PCI Controller Driver"
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
MODULE_PARM(shpchp_debug, "i");
MODULE_PARM(shpchp_poll_mode, "i");
MODULE_PARM(shpchp_poll_time, "i");
MODULE_PARM_DESC(shpchp_debug, "Debugging mode enabled or not");
MODULE_PARM_DESC(shpchp_poll_mode, "Using polling mechanism for hot-plug events or not");
MODULE_PARM_DESC(shpchp_poll_time, "Polling mechanism frequency, in seconds");
#define SHPC_MODULE_NAME "shpchp"
static int shpc_start_thread (void);
static int set_attention_status (struct hotplug_slot *slot, u8 value);
static int enable_slot (struct hotplug_slot *slot);
static int disable_slot (struct hotplug_slot *slot);
static int hardware_test (struct hotplug_slot *slot, u32 value);
static int get_power_status (struct hotplug_slot *slot, u8 *value);
static int get_attention_status (struct hotplug_slot *slot, u8 *value);
static int get_latch_status (struct hotplug_slot *slot, u8 *value);
static int get_adapter_status (struct hotplug_slot *slot, u8 *value);
static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value);
static struct hotplug_slot_ops shpchp_hotplug_slot_ops = {
.owner = THIS_MODULE,
.set_attention_status = set_attention_status,
.enable_slot = enable_slot,
.disable_slot = disable_slot,
.hardware_test = hardware_test,
.get_power_status = get_power_status,
.get_attention_status = get_attention_status,
.get_latch_status = get_latch_status,
.get_adapter_status = get_adapter_status,
.get_max_bus_speed = get_max_bus_speed,
.get_cur_bus_speed = get_cur_bus_speed,
};
static int init_slots(struct controller *ctrl)
{
struct slot *new_slot;
u8 number_of_slots;
u8 slot_device;
u32 slot_number, sun;
int result;
dbg("%s\n",__FUNCTION__);
number_of_slots = ctrl->num_slots;
slot_device = ctrl->slot_device_offset;
slot_number = ctrl->first_slot;
while (number_of_slots) {
new_slot = (struct slot *) kmalloc(sizeof(struct slot), GFP_KERNEL);
if (!new_slot)
return -ENOMEM;
memset(new_slot, 0, sizeof(struct slot));
new_slot->hotplug_slot = kmalloc (sizeof (struct hotplug_slot), GFP_KERNEL);
if (!new_slot->hotplug_slot) {
kfree (new_slot);
return -ENOMEM;
}
memset(new_slot->hotplug_slot, 0, sizeof (struct hotplug_slot));
new_slot->hotplug_slot->info = kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL);
if (!new_slot->hotplug_slot->info) {
kfree (new_slot->hotplug_slot);
kfree (new_slot);
return -ENOMEM;
}
memset(new_slot->hotplug_slot->info, 0, sizeof (struct hotplug_slot_info));
new_slot->hotplug_slot->name = kmalloc (SLOT_NAME_SIZE, GFP_KERNEL);
if (!new_slot->hotplug_slot->name) {
kfree (new_slot->hotplug_slot->info);
kfree (new_slot->hotplug_slot);
kfree (new_slot);
return -ENOMEM;
}
new_slot->magic = SLOT_MAGIC;
new_slot->ctrl = ctrl;
new_slot->bus = ctrl->slot_bus;
new_slot->device = slot_device;
new_slot->hpc_ops = ctrl->hpc_ops;
if (shpchprm_get_physical_slot_number(ctrl, &sun, new_slot->bus, new_slot->device)) {
kfree (new_slot->hotplug_slot->info);
kfree (new_slot->hotplug_slot);
kfree (new_slot);
return -ENOMEM;
}
new_slot->number = sun;
new_slot->hp_slot = slot_device - ctrl->slot_device_offset;
/* register this slot with the hotplug pci core */
new_slot->hotplug_slot->private = new_slot;
make_slot_name (new_slot->hotplug_slot->name, SLOT_NAME_SIZE, new_slot);
new_slot->hotplug_slot->ops = &shpchp_hotplug_slot_ops;
new_slot->hpc_ops->get_power_status(new_slot, &(new_slot->hotplug_slot->info->power_status));
new_slot->hpc_ops->get_attention_status(new_slot, &(new_slot->hotplug_slot->info->attention_status));
new_slot->hpc_ops->get_latch_status(new_slot, &(new_slot->hotplug_slot->info->latch_status));
new_slot->hpc_ops->get_adapter_status(new_slot, &(new_slot->hotplug_slot->info->adapter_status));
dbg("Registering bus=%x dev=%x hp_slot=%x sun=%x slot_device_offset=%x\n", new_slot->bus,
new_slot->device, new_slot->hp_slot, new_slot->number, ctrl->slot_device_offset);
result = pci_hp_register (new_slot->hotplug_slot);
if (result) {
err ("pci_hp_register failed with error %d\n", result);
kfree (new_slot->hotplug_slot->info);
kfree (new_slot->hotplug_slot->name);
kfree (new_slot->hotplug_slot);
kfree (new_slot);
return result;
}
new_slot->next = ctrl->slot;
ctrl->slot = new_slot;
number_of_slots--;
slot_device++;
slot_number += ctrl->slot_num_inc;
}
return(0);
}
static int cleanup_slots (struct controller * ctrl)
{
struct slot *old_slot, *next_slot;
old_slot = ctrl->slot;
ctrl->slot = NULL;
while (old_slot) {
next_slot = old_slot->next;
pci_hp_deregister (old_slot->hotplug_slot);
kfree(old_slot->hotplug_slot->info);
kfree(old_slot->hotplug_slot->name);
kfree(old_slot->hotplug_slot);
kfree(old_slot);
old_slot = next_slot;
}
return(0);
}
static int get_ctlr_slot_config(struct controller *ctrl)
{
int num_ctlr_slots;
int first_device_num;
int physical_slot_num;
int updown;
int rc;
int flags;
rc = shpc_get_ctlr_slot_config(ctrl, &num_ctlr_slots, &first_device_num, &physical_slot_num, &updown, &flags);
if (rc) {
err("%s: get_ctlr_slot_config fail for b:d (%x:%x)\n", __FUNCTION__, ctrl->bus, ctrl->device);
return (-1);
}
ctrl->num_slots = num_ctlr_slots;
ctrl->slot_device_offset = first_device_num;
ctrl->first_slot = physical_slot_num;
ctrl->slot_num_inc = updown; /* either -1 or 1 */
dbg("%s: num_slot(0x%x) 1st_dev(0x%x) psn(0x%x) updown(%d) for b:d (%x:%x)\n",
__FUNCTION__, num_ctlr_slots, first_device_num, physical_slot_num, updown, ctrl->bus, ctrl->device);
return (0);
}
/*
* set_attention_status - Turns the Amber LED for a slot on, off or blink
*/
static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 status)
{
struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
if (slot == NULL)
return -ENODEV;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
hotplug_slot->info->attention_status = status;
slot->hpc_ops->set_attention_status(slot, status);
return 0;
}
static int enable_slot (struct hotplug_slot *hotplug_slot)
{
struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
if (slot == NULL)
return -ENODEV;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
return shpchp_enable_slot(slot);
}
static int disable_slot (struct hotplug_slot *hotplug_slot)
{
struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
if (slot == NULL)
return -ENODEV;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
return shpchp_disable_slot(slot);
}
static int hardware_test (struct hotplug_slot *hotplug_slot, u32 value)
{
return 0;
}
static int get_power_status (struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
int retval;
if (slot == NULL)
return -ENODEV;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
retval = slot->hpc_ops->get_power_status(slot, value);
if (retval < 0)
*value = hotplug_slot->info->power_status;
return 0;
}
static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
int retval;
if (slot == NULL)
return -ENODEV;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
retval = slot->hpc_ops->get_attention_status(slot, value);
if (retval < 0)
*value = hotplug_slot->info->attention_status;
return 0;
}
static int get_latch_status (struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
int retval;
if (slot == NULL)
return -ENODEV;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
retval = slot->hpc_ops->get_latch_status(slot, value);
if (retval < 0)
*value = hotplug_slot->info->latch_status;
return 0;
}
static int get_adapter_status (struct hotplug_slot *hotplug_slot, u8 *value)
{
struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
int retval;
if (slot == NULL)
return -ENODEV;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
retval = slot->hpc_ops->get_adapter_status(slot, value);
if (retval < 0)
*value = hotplug_slot->info->adapter_status;
return 0;
}
static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
{
struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
int retval;
if (slot == NULL)
return -ENODEV;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
retval = slot->hpc_ops->get_max_bus_speed(slot, value);
if (retval < 0)
*value = PCI_SPEED_UNKNOWN;
return 0;
}
static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
{
struct slot *slot = get_slot (hotplug_slot, __FUNCTION__);
int retval;
if (slot == NULL)
return -ENODEV;
dbg("%s - physical_slot = %s\n", __FUNCTION__, hotplug_slot->name);
retval = slot->hpc_ops->get_cur_bus_speed(slot, value);
if (retval < 0)
*value = PCI_SPEED_UNKNOWN;
return 0;
}
static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
int rc;
struct controller *ctrl;
struct slot *t_slot;
int first_device_num; /* first PCI device number supported by this SHPC */
int num_ctlr_slots; /* number of slots supported by this SHPC */
ctrl = (struct controller *) kmalloc(sizeof(struct controller), GFP_KERNEL);
if (!ctrl) {
err("%s : out of memory\n", __FUNCTION__);
goto err_out_none;
}
memset(ctrl, 0, sizeof(struct controller));
dbg("DRV_thread pid = %d\n", current->pid);
rc = shpc_init(ctrl, pdev,
(php_intr_callback_t) shpchp_handle_attention_button,
(php_intr_callback_t) shpchp_handle_switch_change,
(php_intr_callback_t) shpchp_handle_presence_change,
(php_intr_callback_t) shpchp_handle_power_fault);
if (rc) {
dbg("%s: controller initialization failed\n", SHPC_MODULE_NAME);
goto err_out_free_ctrl;
}
dbg("%s: controller initialization success\n", __FUNCTION__);
ctrl->pci_dev = pdev; /* pci_dev of the P2P bridge */
pci_set_drvdata(pdev, ctrl);
ctrl->pci_bus = kmalloc (sizeof (*ctrl->pci_bus), GFP_KERNEL);
if (!ctrl->pci_bus) {
err("out of memory\n");
rc = -ENOMEM;
goto err_out_unmap_mmio_region;
}
memcpy (ctrl->pci_bus, pdev->bus, sizeof (*ctrl->pci_bus));
ctrl->bus = pdev->bus->number;
ctrl->slot_bus = pdev->subordinate->number;
ctrl->device = PCI_SLOT(pdev->devfn);
ctrl->function = PCI_FUNC(pdev->devfn);
dbg("ctrl bus=0x%x, device=%x, function=%x, irq=%x\n", ctrl->bus, ctrl->device, ctrl->function, pdev->irq);
/*
* Save configuration headers for this and subordinate PCI buses
*/
rc = get_ctlr_slot_config(ctrl);
if (rc) {
err(msg_initialization_err, rc);
goto err_out_free_ctrl_bus;
}
first_device_num = ctrl->slot_device_offset;
num_ctlr_slots = ctrl->num_slots;
/* Store PCI Config Space for all devices on this bus */
rc = shpchp_save_config(ctrl, ctrl->slot_bus, num_ctlr_slots, first_device_num);
if (rc) {
err("%s: unable to save PCI configuration data, error %d\n", __FUNCTION__, rc);
goto err_out_free_ctrl_bus;
}
/* Get IO, memory, and IRQ resources for new devices */
rc = shpchprm_find_available_resources(ctrl);
ctrl->add_support = !rc;
if (rc) {
dbg("shpchprm_find_available_resources = %#x\n", rc);
err("unable to locate PCI configuration resources for hot plug add.\n");
goto err_out_free_ctrl_bus;
}
/* Setup the slot information structures */
rc = init_slots(ctrl);
if (rc) {
err(msg_initialization_err, 6);
goto err_out_free_ctrl_slot;
}
/* Now hpc_functions (slot->hpc_ops->functions) are ready */
t_slot = shpchp_find_slot(ctrl, first_device_num);
/* Check for operation bus speed */
rc = t_slot->hpc_ops->get_cur_bus_speed(t_slot, &ctrl->speed);
dbg("%s: t_slot->hp_slot %x\n", __FUNCTION__,t_slot->hp_slot);
if (rc || ctrl->speed == PCI_SPEED_UNKNOWN) {
err(SHPC_MODULE_NAME ": Can't get current bus speed. Set to 33MHz PCI.\n");
ctrl->speed = PCI_SPEED_33MHz;
}
/* Finish setting up the hot plug ctrl device */
ctrl->next_event = 0;
if (!shpchp_ctrl_list) {
shpchp_ctrl_list = ctrl;
ctrl->next = NULL;
} else {
ctrl->next = shpchp_ctrl_list;
shpchp_ctrl_list = ctrl;
}
shpchp_create_ctrl_files(ctrl);
return 0;
err_out_free_ctrl_slot:
cleanup_slots(ctrl);
err_out_free_ctrl_bus:
kfree(ctrl->pci_bus);
err_out_unmap_mmio_region:
ctrl->hpc_ops->release_ctlr(ctrl);
err_out_free_ctrl:
kfree(ctrl);
err_out_none:
return -ENODEV;
}
static int shpc_start_thread(void)
{
int loop;
int retval = 0;
dbg("Initialize + Start the notification/polling mechanism \n");
retval = shpchp_event_start_thread();
if (retval) {
dbg("shpchp_event_start_thread() failed\n");
return retval;
}
dbg("Initialize slot lists\n");
/* One slot list for each bus in the system */
for (loop = 0; loop < 256; loop++) {
shpchp_slot_list[loop] = NULL;
}
return retval;
}
static void unload_shpchpd(void)
{
struct pci_func *next;
struct pci_func *TempSlot;
int loop;
struct controller *ctrl;
struct controller *tctrl;
struct pci_resource *res;
struct pci_resource *tres;
ctrl = shpchp_ctrl_list;
while (ctrl) {
cleanup_slots(ctrl);
res = ctrl->io_head;
while (res) {
tres = res;
res = res->next;
kfree(tres);
}
res = ctrl->mem_head;
while (res) {
tres = res;
res = res->next;
kfree(tres);
}
res = ctrl->p_mem_head;
while (res) {
tres = res;
res = res->next;
kfree(tres);
}
res = ctrl->bus_head;
while (res) {
tres = res;
res = res->next;
kfree(tres);
}
kfree (ctrl->pci_bus);
dbg("%s: calling release_ctlr\n", __FUNCTION__);
ctrl->hpc_ops->release_ctlr(ctrl);
tctrl = ctrl;
ctrl = ctrl->next;
kfree(tctrl);
}
for (loop = 0; loop < 256; loop++) {
next = shpchp_slot_list[loop];
while (next != NULL) {
res = next->io_head;
while (res) {
tres = res;
res = res->next;
kfree(tres);
}
res = next->mem_head;
while (res) {
tres = res;
res = res->next;
kfree(tres);
}
res = next->p_mem_head;
while (res) {
tres = res;
res = res->next;
kfree(tres);
}
res = next->bus_head;
while (res) {
tres = res;
res = res->next;
kfree(tres);
}
TempSlot = next;
next = next->next;
kfree(TempSlot);
}
}
/* Stop the notification mechanism */
shpchp_event_stop_thread();
}
static struct pci_device_id shpcd_pci_tbl[] = {
{
.class = ((PCI_CLASS_BRIDGE_PCI << 8) | 0x00),
.class_mask = ~0,
.vendor = PCI_ANY_ID,
.device = PCI_ANY_ID,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},
{ /* end: all zeroes */ }
};
MODULE_DEVICE_TABLE(pci, shpcd_pci_tbl);
static struct pci_driver shpc_driver = {
.name = SHPC_MODULE_NAME,
.id_table = shpcd_pci_tbl,
.probe = shpc_probe,
/* remove: shpc_remove_one, */
};
static int __init shpcd_init(void)
{
int retval = 0;
#ifdef CONFIG_HOTPLUG_PCI_SHPC_POLL_EVENT_MODE
shpchp_poll_mode = 1;
#endif
retval = shpc_start_thread();
if (retval)
goto error_hpc_init;
retval = shpchprm_init(PCI);
if (!retval) {
retval = pci_module_init(&shpc_driver);
dbg("%s: pci_module_init = %d\n", __FUNCTION__, retval);
info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
}
error_hpc_init:
if (retval) {
shpchprm_cleanup();
shpchp_event_stop_thread();
} else
shpchprm_print_pirt();
return retval;
}
static void __exit shpcd_cleanup(void)
{
dbg("unload_shpchpd()\n");
unload_shpchpd();
shpchprm_cleanup();
dbg("pci_unregister_driver\n");
pci_unregister_driver(&shpc_driver);
info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n");
}
module_init(shpcd_init);
module_exit(shpcd_cleanup);
/*
* Standard 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.
* Copyright (C) 2003-2004 Intel Corporation
*
* 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>, <dely.l.sy@intel.com>
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/smp_lock.h>
#include <linux/pci.h>
#include "shpchp.h"
#include "shpchprm.h"
static u32 configure_new_device(struct controller *ctrl, struct pci_func *func,
u8 behind_bridge, struct resource_lists *resources, u8 bridge_bus, u8 bridge_dev);
static int configure_new_function( struct controller *ctrl, struct pci_func *func,
u8 behind_bridge, struct resource_lists *resources, u8 bridge_bus, u8 bridge_dev);
static void interrupt_event_handler(struct controller *ctrl);
static struct semaphore event_semaphore; /* mutex for process loop (up if something to process) */
static struct semaphore event_exit; /* guard ensure thread has exited before calling it quits */
static int event_finished;
static unsigned long pushbutton_pending; /* = 0 */
u8 shpchp_disk_irq;
u8 shpchp_nic_irq;
u8 shpchp_handle_attention_button(u8 hp_slot, void *inst_id)
{
struct controller *ctrl = (struct controller *) inst_id;
struct slot *p_slot;
u8 rc = 0;
u8 getstatus;
struct pci_func *func;
struct event_info *taskInfo;
/* Attention Button Change */
dbg("shpchp: Attention button interrupt received.\n");
func = shpchp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0);
/* This is the structure that tells the worker thread what to do */
taskInfo = &(ctrl->event_queue[ctrl->next_event]);
p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save));
p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
ctrl->next_event = (ctrl->next_event + 1) % 10;
taskInfo->hp_slot = hp_slot;
rc++;
/*
* Button pressed - See if need to TAKE ACTION!!!
*/
info("Button pressed on Slot(%d)\n", ctrl->first_slot + hp_slot);
taskInfo->event_type = INT_BUTTON_PRESS;
if ((p_slot->state == BLINKINGON_STATE)
|| (p_slot->state == BLINKINGOFF_STATE)) {
/* Cancel if we are still blinking; this means that we press the
* attention again before the 5 sec. limit expires to cancel hot-add
* or hot-remove
*/
taskInfo->event_type = INT_BUTTON_CANCEL;
info("Button cancel on Slot(%d)\n", ctrl->first_slot + hp_slot);
} else if ((p_slot->state == POWERON_STATE)
|| (p_slot->state == POWEROFF_STATE)) {
/* Ignore if the slot is on power-on or power-off state; this
* means that the previous attention button action to hot-add or
* hot-remove is undergoing
*/
taskInfo->event_type = INT_BUTTON_IGNORE;
info("Button ignore on Slot(%d)\n", ctrl->first_slot + hp_slot);
}
if (rc)
up(&event_semaphore); /* signal event thread that new event is posted */
return 0;
}
u8 shpchp_handle_switch_change(u8 hp_slot, void *inst_id)
{
struct controller *ctrl = (struct controller *) inst_id;
struct slot *p_slot;
u8 rc = 0;
u8 getstatus;
struct pci_func *func;
struct event_info *taskInfo;
/* Switch Change */
dbg("shpchp: Switch interrupt received.\n");
func = shpchp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0);
/* This is the structure that tells the worker thread
* what to do
*/
taskInfo = &(ctrl->event_queue[ctrl->next_event]);
ctrl->next_event = (ctrl->next_event + 1) % 10;
taskInfo->hp_slot = hp_slot;
rc++;
p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save));
p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
if (!getstatus) {
/*
* Switch opened
*/
info("Latch open on Slot(%d)\n", ctrl->first_slot + hp_slot);
func->switch_save = 0;
taskInfo->event_type = INT_SWITCH_OPEN;
} else {
/*
* Switch closed
*/
info("Latch close on Slot(%d)\n", ctrl->first_slot + hp_slot);
func->switch_save = 0x10;
taskInfo->event_type = INT_SWITCH_CLOSE;
}
if (rc)
up(&event_semaphore); /* signal event thread that new event is posted */
return rc;
}
u8 shpchp_handle_presence_change(u8 hp_slot, void *inst_id)
{
struct controller *ctrl = (struct controller *) inst_id;
struct slot *p_slot;
u8 rc = 0;
/*u8 temp_byte;*/
struct pci_func *func;
struct event_info *taskInfo;
/* Presence Change */
dbg("shpchp: Presence/Notify input change.\n");
func = shpchp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0);
/* This is the structure that tells the worker thread
* what to do
*/
taskInfo = &(ctrl->event_queue[ctrl->next_event]);
ctrl->next_event = (ctrl->next_event + 1) % 10;
taskInfo->hp_slot = hp_slot;
rc++;
p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
/*
* Save the presence state
*/
p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save));
if (func->presence_save) {
/*
* Card Present
*/
taskInfo->event_type = INT_PRESENCE_ON;
} else {
/*
* Not Present
*/
taskInfo->event_type = INT_PRESENCE_OFF;
}
if (rc)
up(&event_semaphore); /* signal event thread that new event is posted */
return rc;
}
u8 shpchp_handle_power_fault(u8 hp_slot, void *inst_id)
{
struct controller *ctrl = (struct controller *) inst_id;
struct slot *p_slot;
u8 rc = 0;
struct pci_func *func;
struct event_info *taskInfo;
/* Power fault */
dbg("shpchp: Power fault interrupt received.\n");
func = shpchp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0);
/* This is the structure that tells the worker thread
* what to do
*/
taskInfo = &(ctrl->event_queue[ctrl->next_event]);
ctrl->next_event = (ctrl->next_event + 1) % 10;
taskInfo->hp_slot = hp_slot;
rc++;
p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
if ( !(p_slot->hpc_ops->query_power_fault(p_slot))) {
/*
* Power fault Cleared
*/
info("Power fault cleared on Slot(%d)\n", ctrl->first_slot + hp_slot);
func->status = 0x00;
taskInfo->event_type = INT_POWER_FAULT_CLEAR;
} else {
/*
* Power fault
*/
info("Power fault on Slot(%d)\n", ctrl->first_slot + hp_slot);
taskInfo->event_type = INT_POWER_FAULT;
/* set power fault status for this board */
func->status = 0xFF;
info("power fault bit %x set\n", hp_slot);
}
if (rc)
up(&event_semaphore); /* signal event thread that new event is posted */
return rc;
}
/*
* sort_by_size
*
* Sorts nodes on the list by their length.
* Smallest first.
*
*/
static int sort_by_size(struct pci_resource **head)
{
struct pci_resource *current_res;
struct pci_resource *next_res;
int out_of_order = 1;
if (!(*head))
return(1);
if (!((*head)->next))
return(0);
while (out_of_order) {
out_of_order = 0;
/* Special case for swapping list head */
if (((*head)->next) &&
((*head)->length > (*head)->next->length)) {
out_of_order++;
current_res = *head;
*head = (*head)->next;
current_res->next = (*head)->next;
(*head)->next = current_res;
}
current_res = *head;
while (current_res->next && current_res->next->next) {
if (current_res->next->length > current_res->next->next->length) {
out_of_order++;
next_res = current_res->next;
current_res->next = current_res->next->next;
current_res = current_res->next;
next_res->next = current_res->next;
current_res->next = next_res;
} else
current_res = current_res->next;
}
} /* End of out_of_order loop */
return(0);
}
/*
* sort_by_max_size
*
* Sorts nodes on the list by their length.
* Largest first.
*
*/
static int sort_by_max_size(struct pci_resource **head)
{
struct pci_resource *current_res;
struct pci_resource *next_res;
int out_of_order = 1;
if (!(*head))
return(1);
if (!((*head)->next))
return(0);
while (out_of_order) {
out_of_order = 0;
/* Special case for swapping list head */
if (((*head)->next) &&
((*head)->length < (*head)->next->length)) {
out_of_order++;
current_res = *head;
*head = (*head)->next;
current_res->next = (*head)->next;
(*head)->next = current_res;
}
current_res = *head;
while (current_res->next && current_res->next->next) {
if (current_res->next->length < current_res->next->next->length) {
out_of_order++;
next_res = current_res->next;
current_res->next = current_res->next->next;
current_res = current_res->next;
next_res->next = current_res->next;
current_res->next = next_res;
} else
current_res = current_res->next;
}
} /* End of out_of_order loop */
return(0);
}
/*
* do_pre_bridge_resource_split
*
* Returns zero or one node of resources that aren't in use
*
*/
static struct pci_resource *do_pre_bridge_resource_split (struct pci_resource **head, struct pci_resource **orig_head, u32 alignment)
{
struct pci_resource *prevnode = NULL;
struct pci_resource *node;
struct pci_resource *split_node;
u32 rc;
u32 temp_dword;
dbg("do_pre_bridge_resource_split\n");
if (!(*head) || !(*orig_head))
return(NULL);
rc = shpchp_resource_sort_and_combine(head);
if (rc)
return(NULL);
if ((*head)->base != (*orig_head)->base)
return(NULL);
if ((*head)->length == (*orig_head)->length)
return(NULL);
/* If we got here, there the bridge requires some of the resource, but
* we may be able to split some off of the front
*/
node = *head;
if (node->length & (alignment -1)) {
/* This one isn't an aligned length, so we'll make a new entry
* and split it up.
*/
split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return(NULL);
temp_dword = (node->length | (alignment-1)) + 1 - alignment;
split_node->base = node->base;
split_node->length = temp_dword;
node->length -= temp_dword;
node->base += split_node->length;
/* Put it in the list */
*head = split_node;
split_node->next = node;
}
if (node->length < alignment) {
return(NULL);
}
/* Now unlink it */
if (*head == node) {
*head = node->next;
node->next = NULL;
} else {
prevnode = *head;
while (prevnode->next != node)
prevnode = prevnode->next;
prevnode->next = node->next;
node->next = NULL;
}
return(node);
}
/*
* do_bridge_resource_split
*
* Returns zero or one node of resources that aren't in use
*
*/
static struct pci_resource *do_bridge_resource_split (struct pci_resource **head, u32 alignment)
{
struct pci_resource *prevnode = NULL;
struct pci_resource *node;
u32 rc;
u32 temp_dword;
if (!(*head))
return(NULL);
rc = shpchp_resource_sort_and_combine(head);
if (rc)
return(NULL);
node = *head;
while (node->next) {
prevnode = node;
node = node->next;
kfree(prevnode);
}
if (node->length < alignment) {
kfree(node);
return(NULL);
}
if (node->base & (alignment - 1)) {
/* Short circuit if adjusted size is too small */
temp_dword = (node->base | (alignment-1)) + 1;
if ((node->length - (temp_dword - node->base)) < alignment) {
kfree(node);
return(NULL);
}
node->length -= (temp_dword - node->base);
node->base = temp_dword;
}
if (node->length & (alignment - 1)) {
/* There's stuff in use after this node */
kfree(node);
return(NULL);
}
return(node);
}
/*
* get_io_resource
*
* this function sorts the resource list by size and then
* returns the first node of "size" length that is not in the
* ISA aliasing window. If it finds a node larger than "size"
* it will split it up.
*
* size must be a power of two.
*/
static struct pci_resource *get_io_resource (struct pci_resource **head, u32 size)
{
struct pci_resource *prevnode;
struct pci_resource *node;
struct pci_resource *split_node = NULL;
u32 temp_dword;
if (!(*head))
return(NULL);
if ( shpchp_resource_sort_and_combine(head) )
return(NULL);
if ( sort_by_size(head) )
return(NULL);
for (node = *head; node; node = node->next) {
if (node->length < size)
continue;
if (node->base & (size - 1)) {
/* This one isn't base aligned properly
so we'll make a new entry and split it up */
temp_dword = (node->base | (size-1)) + 1;
/*/ Short circuit if adjusted size is too small */
if ((node->length - (temp_dword - node->base)) < size)
continue;
split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return(NULL);
split_node->base = node->base;
split_node->length = temp_dword - node->base;
node->base = temp_dword;
node->length -= split_node->length;
/* Put it in the list */
split_node->next = node->next;
node->next = split_node;
} /* End of non-aligned base */
/* Don't need to check if too small since we already did */
if (node->length > size) {
/* This one is longer than we need
so we'll make a new entry and split it up */
split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return(NULL);
split_node->base = node->base + size;
split_node->length = node->length - size;
node->length = size;
/* Put it in the list */
split_node->next = node->next;
node->next = split_node;
} /* End of too big on top end */
/* For IO make sure it's not in the ISA aliasing space */
if (node->base & 0x300L)
continue;
/* If we got here, then it is the right size
Now take it out of the list */
if (*head == node) {
*head = node->next;
} else {
prevnode = *head;
while (prevnode->next != node)
prevnode = prevnode->next;
prevnode->next = node->next;
}
node->next = NULL;
/* Stop looping */
break;
}
return(node);
}
/*
* get_max_resource
*
* Gets the largest node that is at least "size" big from the
* list pointed to by head. It aligns the node on top and bottom
* to "size" alignment before returning it.
* J.I. modified to put max size limits of; 64M->32M->16M->8M->4M->1M
* This is needed to avoid allocating entire ACPI _CRS res to one child bridge/slot.
*/
static struct pci_resource *get_max_resource (struct pci_resource **head, u32 size)
{
struct pci_resource *max;
struct pci_resource *temp;
struct pci_resource *split_node;
u32 temp_dword;
u32 max_size[] = { 0x4000000, 0x2000000, 0x1000000, 0x0800000, 0x0400000, 0x0200000, 0x0100000, 0x00 };
int i;
if (!(*head))
return(NULL);
if (shpchp_resource_sort_and_combine(head))
return(NULL);
if (sort_by_max_size(head))
return(NULL);
for (max = *head;max; max = max->next) {
/* If not big enough we could probably just bail,
instead we'll continue to the next. */
if (max->length < size)
continue;
if (max->base & (size - 1)) {
/* This one isn't base aligned properly
so we'll make a new entry and split it up */
temp_dword = (max->base | (size-1)) + 1;
/* Short circuit if adjusted size is too small */
if ((max->length - (temp_dword - max->base)) < size)
continue;
split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return(NULL);
split_node->base = max->base;
split_node->length = temp_dword - max->base;
max->base = temp_dword;
max->length -= split_node->length;
/* Put it next in the list */
split_node->next = max->next;
max->next = split_node;
}
if ((max->base + max->length) & (size - 1)) {
/* This one isn't end aligned properly at the top
so we'll make a new entry and split it up */
split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return(NULL);
temp_dword = ((max->base + max->length) & ~(size - 1));
split_node->base = temp_dword;
split_node->length = max->length + max->base
- split_node->base;
max->length -= split_node->length;
/* Put it in the list */
split_node->next = max->next;
max->next = split_node;
}
/* Make sure it didn't shrink too much when we aligned it */
if (max->length < size)
continue;
for ( i = 0; max_size[i] > size; i++) {
if (max->length > max_size[i]) {
split_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
break; /* return (NULL); */
split_node->base = max->base + max_size[i];
split_node->length = max->length - max_size[i];
max->length = max_size[i];
/* Put it next in the list */
split_node->next = max->next;
max->next = split_node;
break;
}
}
/* Now take it out of the list */
temp = (struct pci_resource*) *head;
if (temp == max) {
*head = max->next;
} else {
while (temp && temp->next != max) {
temp = temp->next;
}
temp->next = max->next;
}
max->next = NULL;
return(max);
}
/* If we get here, we couldn't find one */
return(NULL);
}
/*
* get_resource
*
* this function sorts the resource list by size and then
* returns the first node of "size" length. If it finds a node
* larger than "size" it will split it up.
*
* size must be a power of two.
*/
static struct pci_resource *get_resource (struct pci_resource **head, u32 size)
{
struct pci_resource *prevnode;
struct pci_resource *node;
struct pci_resource *split_node;
u32 temp_dword;
if (!(*head))
return(NULL);
if ( shpchp_resource_sort_and_combine(head) )
return(NULL);
if ( sort_by_size(head) )
return(NULL);
for (node = *head; node; node = node->next) {
dbg("%s: req_size =0x%x node=%p, base=0x%x, length=0x%x\n",
__FUNCTION__, size, node, node->base, node->length);
if (node->length < size)
continue;
if (node->base & (size - 1)) {
dbg("%s: not aligned\n", __FUNCTION__);
/* this one isn't base aligned properly
so we'll make a new entry and split it up */
temp_dword = (node->base | (size-1)) + 1;
/* Short circuit if adjusted size is too small */
if ((node->length - (temp_dword - node->base)) < size)
continue;
split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return(NULL);
split_node->base = node->base;
split_node->length = temp_dword - node->base;
node->base = temp_dword;
node->length -= split_node->length;
/* Put it in the list */
split_node->next = node->next;
node->next = split_node;
} /* End of non-aligned base */
/* Don't need to check if too small since we already did */
if (node->length > size) {
dbg("%s: too big\n", __FUNCTION__);
/* this one is longer than we need
so we'll make a new entry and split it up */
split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return(NULL);
split_node->base = node->base + size;
split_node->length = node->length - size;
node->length = size;
/* Put it in the list */
split_node->next = node->next;
node->next = split_node;
} /* End of too big on top end */
dbg("%s: got one!!!\n", __FUNCTION__);
/* If we got here, then it is the right size
Now take it out of the list */
if (*head == node) {
*head = node->next;
} else {
prevnode = *head;
while (prevnode->next != node)
prevnode = prevnode->next;
prevnode->next = node->next;
}
node->next = NULL;
/* Stop looping */
break;
}
return(node);
}
/*
* shpchp_resource_sort_and_combine
*
* Sorts all of the nodes in the list in ascending order by
* their base addresses. Also does garbage collection by
* combining adjacent nodes.
*
* returns 0 if success
*/
int shpchp_resource_sort_and_combine(struct pci_resource **head)
{
struct pci_resource *node1;
struct pci_resource *node2;
int out_of_order = 1;
dbg("%s: head = %p, *head = %p\n", __FUNCTION__, head, *head);
if (!(*head))
return(1);
dbg("*head->next = %p\n",(*head)->next);
if (!(*head)->next)
return(0); /* only one item on the list, already sorted! */
dbg("*head->base = 0x%x\n",(*head)->base);
dbg("*head->next->base = 0x%x\n",(*head)->next->base);
while (out_of_order) {
out_of_order = 0;
/* Special case for swapping list head */
if (((*head)->next) &&
((*head)->base > (*head)->next->base)) {
node1 = *head;
(*head) = (*head)->next;
node1->next = (*head)->next;
(*head)->next = node1;
out_of_order++;
}
node1 = (*head);
while (node1->next && node1->next->next) {
if (node1->next->base > node1->next->next->base) {
out_of_order++;
node2 = node1->next;
node1->next = node1->next->next;
node1 = node1->next;
node2->next = node1->next;
node1->next = node2;
} else
node1 = node1->next;
}
} /* End of out_of_order loop */
node1 = *head;
while (node1 && node1->next) {
if ((node1->base + node1->length) == node1->next->base) {
/* Combine */
dbg("8..\n");
node1->length += node1->next->length;
node2 = node1->next;
node1->next = node1->next->next;
kfree(node2);
} else
node1 = node1->next;
}
return(0);
}
/**
* shpchp_slot_create - Creates a node and adds it to the proper bus.
* @busnumber - bus where new node is to be located
*
* Returns pointer to the new node or NULL if unsuccessful
*/
struct pci_func *shpchp_slot_create(u8 busnumber)
{
struct pci_func *new_slot;
struct pci_func *next;
new_slot = (struct pci_func *) kmalloc(sizeof(struct pci_func), GFP_KERNEL);
if (new_slot == NULL) {
return(new_slot);
}
memset(new_slot, 0, sizeof(struct pci_func));
new_slot->next = NULL;
new_slot->configured = 1;
if (shpchp_slot_list[busnumber] == NULL) {
shpchp_slot_list[busnumber] = new_slot;
} else {
next = shpchp_slot_list[busnumber];
while (next->next != NULL)
next = next->next;
next->next = new_slot;
}
return(new_slot);
}
/*
* slot_remove - Removes a node from the linked list of slots.
* @old_slot: slot to remove
*
* Returns 0 if successful, !0 otherwise.
*/
static int slot_remove(struct pci_func * old_slot)
{
struct pci_func *next;
if (old_slot == NULL)
return(1);
next = shpchp_slot_list[old_slot->bus];
if (next == NULL) {
return(1);
}
if (next == old_slot) {
shpchp_slot_list[old_slot->bus] = old_slot->next;
shpchp_destroy_board_resources(old_slot);
kfree(old_slot);
return(0);
}
while ((next->next != old_slot) && (next->next != NULL)) {
next = next->next;
}
if (next->next == old_slot) {
next->next = old_slot->next;
shpchp_destroy_board_resources(old_slot);
kfree(old_slot);
return(0);
} else
return(2);
}
/**
* bridge_slot_remove - Removes a node from the linked list of slots.
* @bridge: bridge to remove
*
* Returns 0 if successful, !0 otherwise.
*/
static int bridge_slot_remove(struct pci_func *bridge)
{
u8 subordinateBus, secondaryBus;
u8 tempBus;
struct pci_func *next;
if (bridge == NULL)
return(1);
secondaryBus = (bridge->config_space[0x06] >> 8) & 0xFF;
subordinateBus = (bridge->config_space[0x06] >> 16) & 0xFF;
for (tempBus = secondaryBus; tempBus <= subordinateBus; tempBus++) {
next = shpchp_slot_list[tempBus];
while (!slot_remove(next)) {
next = shpchp_slot_list[tempBus];
}
}
next = shpchp_slot_list[bridge->bus];
if (next == NULL) {
return(1);
}
if (next == bridge) {
shpchp_slot_list[bridge->bus] = bridge->next;
kfree(bridge);
return(0);
}
while ((next->next != bridge) && (next->next != NULL)) {
next = next->next;
}
if (next->next == bridge) {
next->next = bridge->next;
kfree(bridge);
return(0);
} else
return(2);
}
/**
* shpchp_slot_find - Looks for a node by bus, and device, multiple functions accessed
* @bus: bus to find
* @device: device to find
* @index: is 0 for first function found, 1 for the second...
*
* Returns pointer to the node if successful, %NULL otherwise.
*/
struct pci_func *shpchp_slot_find(u8 bus, u8 device, u8 index)
{
int found = -1;
struct pci_func *func;
func = shpchp_slot_list[bus];
if ((func == NULL) || ((func->device == device) && (index == 0)))
return(func);
if (func->device == device)
found++;
while (func->next != NULL) {
func = func->next;
if (func->device == device)
found++;
if (found == index)
return(func);
}
return(NULL);
}
static int is_bridge(struct pci_func * func)
{
/* Check the header type */
if (((func->config_space[0x03] >> 16) & 0xFF) == 0x01)
return 1;
else
return 0;
}
/* The following routines constitute the bulk of the
hotplug controller logic
*/
/**
* board_added - Called after a board has been added to the system.
*
* Turns power on for the board
* Configures board
*
*/
static u32 board_added(struct pci_func * func, struct controller * ctrl)
{
u8 hp_slot, slot;
u8 slots_not_empty = 0;
int index;
u32 temp_register = 0xFFFFFFFF;
u32 retval, rc = 0;
struct pci_func *new_func = NULL;
struct pci_func *t_func = NULL;
struct slot *p_slot, *pslot;
struct resource_lists res_lists;
enum pci_bus_speed adapter_speed, bus_speed, max_bus_speed;
u8 pi, mode;
p_slot = shpchp_find_slot(ctrl, func->device);
hp_slot = func->device - ctrl->slot_device_offset;
dbg("%s: func->device, slot_offset, hp_slot = %d, %d ,%d\n", __FUNCTION__, func->device, ctrl->slot_device_offset, hp_slot);
/* Wait for exclusive access to hardware */
down(&ctrl->crit_sect);
/* Power on slot without connecting to bus */
rc = p_slot->hpc_ops->power_on_slot(p_slot);
if (rc) {
err("%s: Failed to power on slot\n", __FUNCTION__);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return -1;
}
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
rc = p_slot->hpc_ops->check_cmd_status(ctrl);
if (rc) {
err("%s: Failed to power on slot, error code(%d)\n", __FUNCTION__, rc);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return -1;
}
rc = p_slot->hpc_ops->get_adapter_speed(p_slot, &adapter_speed);
/* 0 = PCI 33Mhz, 1 = PCI 66 Mhz, 2 = PCI-X 66 PA, 4 = PCI-X 66 ECC, */
/* 5 = PCI-X 133 PA, 7 = PCI-X 133 ECC, 0xa = PCI-X 133 Mhz 266, */
/* 0xd = PCI-X 133 Mhz 533 */
/* This encoding is different from the one used in cur_bus_speed & */
/* max_bus_speed */
if (rc || adapter_speed == PCI_SPEED_UNKNOWN) {
err("%s: Can't get adapter speed or bus mode mismatch\n", __FUNCTION__);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return WRONG_BUS_FREQUENCY;
}
rc = p_slot->hpc_ops->get_cur_bus_speed(p_slot, &bus_speed);
if (rc || bus_speed == PCI_SPEED_UNKNOWN) {
err("%s: Can't get bus operation speed\n", __FUNCTION__);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return WRONG_BUS_FREQUENCY;
}
rc = p_slot->hpc_ops->get_max_bus_speed(p_slot, &max_bus_speed);
if (rc || max_bus_speed == PCI_SPEED_UNKNOWN) {
err("%s: Can't get max bus operation speed\n", __FUNCTION__);
max_bus_speed = bus_speed;
}
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
rc = p_slot->hpc_ops->get_prog_int(p_slot, &pi);
if (rc) {
err("%s: Can't get controller programming interface, set it to 1\n", __FUNCTION__);
pi = 1;
}
if (pi == 2) {
for ( slot = 0; slot < ctrl->num_slots; slot++) {
if (slot != hp_slot) {
pslot = shpchp_find_slot(ctrl, slot + ctrl->slot_device_offset);
t_func = shpchp_slot_find(pslot->bus, pslot->device, 0);
slots_not_empty |= t_func->is_a_board;
}
}
switch (adapter_speed) {
case PCI_SPEED_133MHz_PCIX_533:
case PCI_SPEED_133MHz_PCIX_266:
if ((( bus_speed < 0xa ) || (bus_speed < 0xd)) && (max_bus_speed > bus_speed) &&
((max_bus_speed <= 0xa) || (max_bus_speed <= 0xd)) && (!slots_not_empty)) {
/* Wait for exclusive access to hardware */
down(&ctrl->crit_sect);
rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, max_bus_speed);
if (rc) {
err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return WRONG_BUS_FREQUENCY;
}
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
rc = p_slot->hpc_ops->check_cmd_status(ctrl);
if (rc) {
err("%s: Can't set bus speed/mode in the case of adapter & bus mismatch\n",
__FUNCTION__);
err("%s: Error code (%d)\n", __FUNCTION__, rc);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return WRONG_BUS_FREQUENCY;
}
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
}
break;
case PCI_SPEED_133MHz_PCIX_ECC:
case PCI_SPEED_133MHz_PCIX:
rc = p_slot->hpc_ops->get_mode1_ECC_cap(p_slot, &mode);
if (rc) {
err("%s: PI is 1 \n", __FUNCTION__);
return WRONG_BUS_FREQUENCY;
}
if (mode) { /* Bus - Mode 1 ECC */
if (bus_speed > 0x7) {
err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bus_speed, adapter_speed);
return WRONG_BUS_FREQUENCY;
}
if ((bus_speed < 0x7) && (max_bus_speed <= 0x7) &&
(bus_speed < max_bus_speed) && (!slots_not_empty)) {
/* Wait for exclusive access to hardware */
down(&ctrl->crit_sect);
rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, max_bus_speed);
if (rc) {
err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return WRONG_BUS_FREQUENCY;
}
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
rc = p_slot->hpc_ops->check_cmd_status(ctrl);
if (rc) {
err("%s: Can't set bus speed/mode in the case of adapter & bus mismatch\n",
__FUNCTION__);
err("%s: Error code (%d)\n", __FUNCTION__, rc);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return WRONG_BUS_FREQUENCY;
}
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
}
} else {
if ((bus_speed > 0x4) || (max_bus_speed > 0x4)) {
err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bus_speed, adapter_speed);
return WRONG_BUS_FREQUENCY;
}
if ((bus_speed < 0x4) && (max_bus_speed <= 0x4) &&
(bus_speed < max_bus_speed) && (!slots_not_empty)) {
/* Wait for exclusive access to hardware */
down(&ctrl->crit_sect);
rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, max_bus_speed);
if (rc) {
err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return WRONG_BUS_FREQUENCY;
}
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
rc = p_slot->hpc_ops->check_cmd_status(ctrl);
if (rc) {
err("%s: Can't set bus speed/mode in the case of adapter & bus mismatch\n",
__FUNCTION__);
err("%s: Error code (%d)\n", __FUNCTION__, rc);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return WRONG_BUS_FREQUENCY;
}
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
}
}
break;
case PCI_SPEED_66MHz_PCIX_ECC:
case PCI_SPEED_66MHz_PCIX:
rc = p_slot->hpc_ops->get_mode1_ECC_cap(p_slot, &mode);
if (rc) {
err("%s: PI is 1 \n", __FUNCTION__);
return WRONG_BUS_FREQUENCY;
}
if (mode) { /* Bus - Mode 1 ECC */
if (bus_speed > 0x5) {
err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bus_speed, adapter_speed);
return WRONG_BUS_FREQUENCY;
}
if ((bus_speed < 0x5) && (max_bus_speed <= 0x5) &&
(bus_speed < max_bus_speed) && (!slots_not_empty)) {
/* Wait for exclusive access to hardware */
down(&ctrl->crit_sect);
rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, max_bus_speed);
if (rc) {
err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return WRONG_BUS_FREQUENCY;
}
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
rc = p_slot->hpc_ops->check_cmd_status(ctrl);
if (rc) {
err("%s: Can't set bus speed/mode in the case of adapter & bus mismatch\n",
__FUNCTION__);
err("%s: Error code (%d)\n", __FUNCTION__, rc);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return WRONG_BUS_FREQUENCY;
}
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
}
} else {
if ((bus_speed > 0x2) || (max_bus_speed > 0x2)) {
err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bus_speed, adapter_speed);
return WRONG_BUS_FREQUENCY;
}
if ((bus_speed < 0x2) && (max_bus_speed <= 0x2) &&
(bus_speed < max_bus_speed) && (!slots_not_empty)) {
/* Wait for exclusive access to hardware */
down(&ctrl->crit_sect);
rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, max_bus_speed);
if (rc) {
err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return WRONG_BUS_FREQUENCY;
}
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
rc = p_slot->hpc_ops->check_cmd_status(ctrl);
if (rc) {
err("%s: Can't set bus speed/mode in the case of adapter & bus mismatch\n",
__FUNCTION__);
err("%s: Error code (%d)\n", __FUNCTION__, rc);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return WRONG_BUS_FREQUENCY;
}
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
}
}
break;
case PCI_SPEED_66MHz:
if (bus_speed > 0x1) {
err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bus_speed, adapter_speed);
return WRONG_BUS_FREQUENCY;
}
if (bus_speed == 0x1)
;
if ((bus_speed == 0x0) && ( max_bus_speed == 0x1)) {
/* Wait for exclusive access to hardware */
down(&ctrl->crit_sect);
rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, max_bus_speed);
if (rc) {
err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return WRONG_BUS_FREQUENCY;
}
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
rc = p_slot->hpc_ops->check_cmd_status(ctrl);
if (rc) {
err("%s: Can't set bus speed/mode in the case of adapter & bus mismatch\n",
__FUNCTION__);
err("%s: Error code (%d)\n", __FUNCTION__, rc);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return WRONG_BUS_FREQUENCY;
}
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
}
break;
case PCI_SPEED_33MHz:
if (bus_speed > 0x0) {
err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bus_speed, adapter_speed);
return WRONG_BUS_FREQUENCY;
}
break;
default:
err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bus_speed, adapter_speed);
return WRONG_BUS_FREQUENCY;
}
} else {
/* if adpater_speed == bus_speed, nothing to do here */
if (adapter_speed != bus_speed) {
for ( slot = 0; slot < ctrl->num_slots; slot++) {
if (slot != hp_slot) {
pslot = shpchp_find_slot(ctrl, slot + ctrl->slot_device_offset);
t_func = shpchp_slot_find(pslot->bus, pslot->device, 0);
slots_not_empty |= t_func->is_a_board;
}
}
if (slots_not_empty != 0) { /* Other slots on the same bus are occupied */
if ( adapter_speed < bus_speed ) {
err("%s: speed of bus %x and adapter %x mismatch\n", __FUNCTION__, bus_speed, adapter_speed);
return WRONG_BUS_FREQUENCY;
}
/* Do nothing if adapter_speed >= bus_speed */
}
}
if ((adapter_speed != bus_speed) && (slots_not_empty == 0)) {
/* Other slots on the same bus are empty */
rc = p_slot->hpc_ops->get_max_bus_speed(p_slot, &max_bus_speed);
if (rc || max_bus_speed == PCI_SPEED_UNKNOWN) {
err("%s: Can't get max bus operation speed\n", __FUNCTION__);
max_bus_speed = bus_speed;
}
if (max_bus_speed == bus_speed) {
/* if adapter_speed >= bus_speed, do nothing */
if (adapter_speed < bus_speed) {
/*
* Try to lower bus speed to accommodate the adapter if other slots
* on the same controller are empty
*/
/* Wait for exclusive access to hardware */
down(&ctrl->crit_sect);
rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, adapter_speed);
if (rc) {
err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__);
return WRONG_BUS_FREQUENCY;
}
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
rc = p_slot->hpc_ops->check_cmd_status(ctrl);
if (rc) {
err("%s: Can't set bus speed/mode in the case of adapter & bus mismatch\n",
__FUNCTION__);
err("%s: Error code (%d)\n", __FUNCTION__, rc);
return WRONG_BUS_FREQUENCY;
}
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
}
} else {
/* Wait for exclusive access to hardware */
down(&ctrl->crit_sect);
/* max_bus_speed != bus_speed. Note: max_bus_speed should be > than bus_speed */
if (adapter_speed < max_bus_speed)
rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, adapter_speed);
else
rc = p_slot->hpc_ops->set_bus_speed_mode(p_slot, max_bus_speed);
if (rc) {
err("%s: Issue of set bus speed mode command failed\n", __FUNCTION__);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return WRONG_BUS_FREQUENCY;
}
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
rc = p_slot->hpc_ops->check_cmd_status(ctrl);
if (rc) {
err("%s: Can't set bus speed/mode in the case of adapter & bus mismatch\n",
__FUNCTION__);
err("%s: Error code (%d)\n", __FUNCTION__, rc);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return WRONG_BUS_FREQUENCY;
}
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
}
}
}
/* Wait for exclusive access to hardware */
down(&ctrl->crit_sect);
/* turn on board, blink green LED, turn off Amber LED */
rc = p_slot->hpc_ops->slot_enable(p_slot);
if (rc) {
err("%s: Issue of Slot Enable command failed\n", __FUNCTION__);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return rc;
}
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
rc = p_slot->hpc_ops->check_cmd_status(ctrl);
if (rc) {
err("%s: Failed to enable slot, error code(%d)\n", __FUNCTION__, rc);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return rc;
}
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
/* Wait for ~1 second */
dbg("%s: before long_delay\n", __FUNCTION__);
wait_for_ctrl_irq (ctrl);
dbg("%s: afterlong_delay\n", __FUNCTION__);
dbg("%s: func status = %x\n", __FUNCTION__, func->status);
/* Check for a power fault */
if (func->status == 0xFF) {
/* power fault occurred, but it was benign */
temp_register = 0xFFFFFFFF;
dbg("%s: temp register set to %x by power fault\n", __FUNCTION__, temp_register);
rc = POWER_FAILURE;
func->status = 0;
} else {
/* Get vendor/device ID u32 */
rc = pci_bus_read_config_dword (ctrl->pci_dev->subordinate, PCI_DEVFN(func->device, func->function),
PCI_VENDOR_ID, &temp_register);
dbg("%s: pci_bus_read_config_dword returns %d\n", __FUNCTION__, rc);
dbg("%s: temp_register is %x\n", __FUNCTION__, temp_register);
if (rc != 0) {
/* Something's wrong here */
temp_register = 0xFFFFFFFF;
dbg("%s: temp register set to %x by error\n", __FUNCTION__, temp_register);
}
/* Preset return code. It will be changed later if things go okay. */
rc = NO_ADAPTER_PRESENT;
}
/* All F's is an empty slot or an invalid board */
if (temp_register != 0xFFFFFFFF) { /* Check for a board in the slot */
res_lists.io_head = ctrl->io_head;
res_lists.mem_head = ctrl->mem_head;
res_lists.p_mem_head = ctrl->p_mem_head;
res_lists.bus_head = ctrl->bus_head;
res_lists.irqs = NULL;
rc = configure_new_device(ctrl, func, 0, &res_lists, 0, 0);
dbg("%s: back from configure_new_device\n", __FUNCTION__);
ctrl->io_head = res_lists.io_head;
ctrl->mem_head = res_lists.mem_head;
ctrl->p_mem_head = res_lists.p_mem_head;
ctrl->bus_head = res_lists.bus_head;
shpchp_resource_sort_and_combine(&(ctrl->mem_head));
shpchp_resource_sort_and_combine(&(ctrl->p_mem_head));
shpchp_resource_sort_and_combine(&(ctrl->io_head));
shpchp_resource_sort_and_combine(&(ctrl->bus_head));
if (rc) {
/* Wait for exclusive access to hardware */
down(&ctrl->crit_sect);
/* turn off slot, turn on Amber LED, turn off Green LED */
retval = p_slot->hpc_ops->slot_disable(p_slot);
if (retval) {
err("%s: Issue of Slot Enable command failed\n", __FUNCTION__);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return retval;
}
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
retval = p_slot->hpc_ops->check_cmd_status(ctrl);
if (retval) {
err("%s: Failed to disable slot, error code(%d)\n", __FUNCTION__, rc);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return retval;
}
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return(rc);
}
shpchp_save_slot_config(ctrl, func);
func->status = 0;
func->switch_save = 0x10;
func->is_a_board = 0x01;
/* next, we will instantiate the linux pci_dev structures
* (with appropriate driver notification, if already present)
*/
index = 0;
do {
new_func = shpchp_slot_find(ctrl->slot_bus, func->device, index++);
if (new_func && !new_func->pci_dev) {
dbg("%s:call pci_hp_configure_dev\n", __FUNCTION__);
shpchp_configure_device(ctrl, new_func);
}
} while (new_func);
/* Wait for exclusive access to hardware */
down(&ctrl->crit_sect);
p_slot->hpc_ops->green_led_on(p_slot);
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
} else {
/* Wait for exclusive access to hardware */
down(&ctrl->crit_sect);
/* turn off slot, turn on Amber LED, turn off Green LED */
rc = p_slot->hpc_ops->slot_disable(p_slot);
if (rc) {
err("%s: Issue of Slot Disable command failed\n", __FUNCTION__);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return rc;
}
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
rc = p_slot->hpc_ops->check_cmd_status(ctrl);
if (rc) {
err("%s: Failed to disable slot, error code(%d)\n", __FUNCTION__, rc);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return rc;
}
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return(rc);
}
return 0;
}
/**
* remove_board - Turns off slot and LED's
*
*/
static u32 remove_board(struct pci_func *func, struct controller *ctrl)
{
int index;
u8 skip = 0;
u8 device;
u8 hp_slot;
u32 rc;
struct resource_lists res_lists;
struct pci_func *temp_func;
struct slot *p_slot;
if (func == NULL)
return(1);
if (shpchp_unconfigure_device(func))
return(1);
device = func->device;
hp_slot = func->device - ctrl->slot_device_offset;
p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
dbg("In %s, hp_slot = %d\n", __FUNCTION__, hp_slot);
if ((ctrl->add_support) &&
!(func->bus_head || func->mem_head || func->p_mem_head || func->io_head)) {
/* Here we check to see if we've saved any of the board's
* resources already. If so, we'll skip the attempt to
* determine what's being used.
*/
index = 0;
temp_func = func;
while ((temp_func = shpchp_slot_find(temp_func->bus, temp_func->device, index++))) {
if (temp_func->bus_head || temp_func->mem_head
|| temp_func->p_mem_head || temp_func->io_head) {
skip = 1;
break;
}
}
if (!skip)
rc = shpchp_save_used_resources(ctrl, func, DISABLE_CARD);
}
/* Change status to shutdown */
if (func->is_a_board)
func->status = 0x01;
func->configured = 0;
/* Wait for exclusive access to hardware */
down(&ctrl->crit_sect);
/* turn off slot, turn on Amber LED, turn off Green LED */
rc = p_slot->hpc_ops->slot_disable(p_slot);
if (rc) {
err("%s: Issue of Slot Disable command failed\n", __FUNCTION__);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return rc;
}
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
rc = p_slot->hpc_ops->check_cmd_status(ctrl);
if (rc) {
err("%s: Failed to disable slot, error code(%d)\n", __FUNCTION__, rc);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return rc;
}
rc = p_slot->hpc_ops->set_attention_status(p_slot, 0);
if (rc) {
err("%s: Issue of Set Attention command failed\n", __FUNCTION__);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
return rc;
}
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
if (ctrl->add_support) {
while (func) {
res_lists.io_head = ctrl->io_head;
res_lists.mem_head = ctrl->mem_head;
res_lists.p_mem_head = ctrl->p_mem_head;
res_lists.bus_head = ctrl->bus_head;
dbg("Returning resources to ctlr lists for (B/D/F) = (%#x/%#x/%#x)\n", func->bus,
func->device, func->function);
shpchp_return_board_resources(func, &res_lists);
ctrl->io_head = res_lists.io_head;
ctrl->mem_head = res_lists.mem_head;
ctrl->p_mem_head = res_lists.p_mem_head;
ctrl->bus_head = res_lists.bus_head;
shpchp_resource_sort_and_combine(&(ctrl->mem_head));
shpchp_resource_sort_and_combine(&(ctrl->p_mem_head));
shpchp_resource_sort_and_combine(&(ctrl->io_head));
shpchp_resource_sort_and_combine(&(ctrl->bus_head));
if (is_bridge(func)) {
dbg("PCI Bridge Hot-Remove s:b:d:f(%02x:%02x:%02x:%02x)\n", ctrl->seg, func->bus,
func->device, func->function);
bridge_slot_remove(func);
} else
dbg("PCI Function Hot-Remove s:b:d:f(%02x:%02x:%02x:%02x)\n", ctrl->seg, func->bus,
func->device, func->function);
slot_remove(func);
func = shpchp_slot_find(ctrl->slot_bus, device, 0);
}
/* Setup slot structure with entry for empty slot */
func = shpchp_slot_create(ctrl->slot_bus);
if (func == NULL) {
return(1);
}
func->bus = ctrl->slot_bus;
func->device = device;
func->function = 0;
func->configured = 0;
func->switch_save = 0x10;
func->is_a_board = 0;
}
return 0;
}
static void pushbutton_helper_thread (unsigned long data)
{
pushbutton_pending = data;
up(&event_semaphore);
}
/* this is the main worker thread */
static int event_thread(void* data)
{
struct controller *ctrl;
lock_kernel();
daemonize("shpchpd_event");
unlock_kernel();
while (1) {
dbg("!!!!event_thread sleeping\n");
down_interruptible (&event_semaphore);
dbg("event_thread woken finished = %d\n", event_finished);
if (event_finished || signal_pending(current))
break;
/* Do stuff here */
if (pushbutton_pending)
shpchp_pushbutton_thread(pushbutton_pending);
else
for (ctrl = shpchp_ctrl_list; ctrl; ctrl=ctrl->next)
interrupt_event_handler(ctrl);
}
dbg("event_thread signals exit\n");
up(&event_exit);
return 0;
}
int shpchp_event_start_thread (void)
{
int pid;
/* initialize our semaphores */
init_MUTEX_LOCKED(&event_exit);
event_finished=0;
init_MUTEX_LOCKED(&event_semaphore);
pid = kernel_thread(event_thread, 0, 0);
if (pid < 0) {
err ("Can't start up our event thread\n");
return -1;
}
dbg("Our event thread pid = %d\n", pid);
return 0;
}
void shpchp_event_stop_thread (void)
{
event_finished = 1;
dbg("event_thread finish command given\n");
up(&event_semaphore);
dbg("wait for event_thread to exit\n");
down(&event_exit);
}
static int update_slot_info (struct slot *slot)
{
struct hotplug_slot_info *info;
int result;
info = kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL);
if (!info)
return -ENOMEM;
slot->hpc_ops->get_power_status(slot, &(info->power_status));
slot->hpc_ops->get_attention_status(slot, &(info->attention_status));
slot->hpc_ops->get_latch_status(slot, &(info->latch_status));
slot->hpc_ops->get_adapter_status(slot, &(info->adapter_status));
result = pci_hp_change_slot_info(slot->hotplug_slot, info);
kfree (info);
return result;
}
static void interrupt_event_handler(struct controller *ctrl)
{
int loop = 0;
int change = 1;
struct pci_func *func;
u8 hp_slot;
u8 getstatus;
struct slot *p_slot;
dbg("%s:\n", __FUNCTION__);
while (change) {
change = 0;
for (loop = 0; loop < 10; loop++) {
if (ctrl->event_queue[loop].event_type != 0) {
dbg("%s:loop %x event_type %x\n", __FUNCTION__, loop,
ctrl->event_queue[loop].event_type);
hp_slot = ctrl->event_queue[loop].hp_slot;
func = shpchp_slot_find(ctrl->slot_bus, (hp_slot + ctrl->slot_device_offset), 0);
p_slot = shpchp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset);
dbg("%s: hp_slot %d, func %p, p_slot %p\n", __FUNCTION__, hp_slot, func, p_slot);
if (ctrl->event_queue[loop].event_type == INT_BUTTON_CANCEL) {
dbg("%s: button cancel\n", __FUNCTION__);
del_timer(&p_slot->task_event);
switch (p_slot->state) {
case BLINKINGOFF_STATE:
/* Wait for exclusive access to hardware */
down(&ctrl->crit_sect);
p_slot->hpc_ops->green_led_on(p_slot);
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
p_slot->hpc_ops->set_attention_status(p_slot, 0);
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
break;
case BLINKINGON_STATE:
/* Wait for exclusive access to hardware */
down(&ctrl->crit_sect);
p_slot->hpc_ops->green_led_off(p_slot);
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
p_slot->hpc_ops->set_attention_status(p_slot, 0);
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
break;
default:
warn("Not a valid state\n");
return;
}
info(msg_button_cancel, p_slot->number);
p_slot->state = STATIC_STATE;
} else if (ctrl->event_queue[loop].event_type == INT_BUTTON_PRESS) {
/* Button Pressed (No action on 1st press...) */
dbg("%s: Button pressed\n", __FUNCTION__);
p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
if (getstatus) {
/* slot is on */
dbg("%s: slot is on\n", __FUNCTION__);
p_slot->state = BLINKINGOFF_STATE;
info(msg_button_off, p_slot->number);
} else {
/* slot is off */
dbg("%s: slot is off\n", __FUNCTION__);
p_slot->state = BLINKINGON_STATE;
info(msg_button_on, p_slot->number);
}
/* Wait for exclusive access to hardware */
down(&ctrl->crit_sect);
/* blink green LED and turn off amber */
p_slot->hpc_ops->green_led_blink(p_slot);
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
p_slot->hpc_ops->set_attention_status(p_slot, 0);
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
init_timer(&p_slot->task_event);
p_slot->task_event.expires = jiffies + 5 * HZ; /* 5 second delay */
p_slot->task_event.function = (void (*)(unsigned long)) pushbutton_helper_thread;
p_slot->task_event.data = (unsigned long) p_slot;
dbg("%s: add_timer p_slot = %p\n", __FUNCTION__,(void *) p_slot);
add_timer(&p_slot->task_event);
} else if (ctrl->event_queue[loop].event_type == INT_POWER_FAULT) {
/***********POWER FAULT********************/
dbg("%s: power fault\n", __FUNCTION__);
/* Wait for exclusive access to hardware */
down(&ctrl->crit_sect);
p_slot->hpc_ops->set_attention_status(p_slot, 1);
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
p_slot->hpc_ops->green_led_off(p_slot);
/* Wait for the command to complete */
wait_for_ctrl_irq (ctrl);
/* Done with exclusive hardware access */
up(&ctrl->crit_sect);
} else {
/* refresh notification */
if (p_slot)
update_slot_info(p_slot);
}
ctrl->event_queue[loop].event_type = 0;
change = 1;
}
} /* End of FOR loop */
}
return;
}
/**
* shpchp_pushbutton_thread
*
* Scheduled procedure to handle blocking stuff for the pushbuttons
* Handles all pending events and exits.
*
*/
void shpchp_pushbutton_thread (unsigned long slot)
{
struct slot *p_slot = (struct slot *) slot;
u8 getstatus;
int rc;
pushbutton_pending = 0;
if (!p_slot) {
dbg("%s: Error! slot NULL\n", __FUNCTION__);
return;
}
p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
if (getstatus) {
p_slot->state = POWEROFF_STATE;
dbg("In power_down_board, b:d(%x:%x)\n", p_slot->bus, p_slot->device);
if (shpchp_disable_slot(p_slot)) {
/* Wait for exclusive access to hardware */
down(&p_slot->ctrl->crit_sect);
/* Turn on the Attention LED */
rc = p_slot->hpc_ops->set_attention_status(p_slot, 1);
if (rc) {
err("%s: Issue of Set Atten Indicator On command failed\n", __FUNCTION__);
return;
}
/* Wait for the command to complete */
wait_for_ctrl_irq (p_slot->ctrl);
/* Done with exclusive hardware access */
up(&p_slot->ctrl->crit_sect);
}
p_slot->state = STATIC_STATE;
} else {
p_slot->state = POWERON_STATE;
dbg("In add_board, b:d(%x:%x)\n", p_slot->bus, p_slot->device);
if (shpchp_enable_slot(p_slot)) {
/* Wait for exclusive access to hardware */
down(&p_slot->ctrl->crit_sect);
/* Turn off the green LED */
rc = p_slot->hpc_ops->set_attention_status(p_slot, 1);
if (rc) {
err("%s: Issue of Set Atten Indicator On command failed\n", __FUNCTION__);
return;
}
/* Wait for the command to complete */
wait_for_ctrl_irq (p_slot->ctrl);
p_slot->hpc_ops->green_led_off(p_slot);
/* Wait for the command to complete */
wait_for_ctrl_irq (p_slot->ctrl);
/* Done with exclusive hardware access */
up(&p_slot->ctrl->crit_sect);
}
p_slot->state = STATIC_STATE;
}
return;
}
int shpchp_enable_slot (struct slot *p_slot)
{
u8 getstatus = 0;
int rc;
struct pci_func *func;
func = shpchp_slot_find(p_slot->bus, p_slot->device, 0);
if (!func) {
dbg("%s: Error! slot NULL\n", __FUNCTION__);
return (1);
}
/* Check to see if (latch closed, card present, power off) */
down(&p_slot->ctrl->crit_sect);
rc = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
if (rc || !getstatus) {
info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number);
up(&p_slot->ctrl->crit_sect);
return (0);
}
rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
if (rc || !getstatus) {
info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number);
up(&p_slot->ctrl->crit_sect);
return (0);
}
rc = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
if (rc || getstatus) {
info("%s: already enabled on slot(%x)\n", __FUNCTION__, p_slot->number);
up(&p_slot->ctrl->crit_sect);
return (0);
}
up(&p_slot->ctrl->crit_sect);
slot_remove(func);
func = shpchp_slot_create(p_slot->bus);
if (func == NULL)
return (1);
func->bus = p_slot->bus;
func->device = p_slot->device;
func->function = 0;
func->configured = 0;
func->is_a_board = 1;
/* We have to save the presence info for these slots */
p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save));
p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
func->switch_save = !getstatus? 0x10:0;
rc = board_added(func, p_slot->ctrl);
if (rc) {
if (is_bridge(func))
bridge_slot_remove(func);
else
slot_remove(func);
/* Setup slot structure with entry for empty slot */
func = shpchp_slot_create(p_slot->bus);
if (func == NULL)
return (1); /* Out of memory */
func->bus = p_slot->bus;
func->device = p_slot->device;
func->function = 0;
func->configured = 0;
func->is_a_board = 1;
/* We have to save the presence info for these slots */
p_slot->hpc_ops->get_adapter_status(p_slot, &(func->presence_save));
p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
func->switch_save = !getstatus? 0x10:0;
}
if (p_slot)
update_slot_info(p_slot);
return rc;
}
int shpchp_disable_slot (struct slot *p_slot)
{
u8 class_code, header_type, BCR;
u8 index = 0;
u8 getstatus = 0;
u32 rc = 0;
int ret = 0;
unsigned int devfn;
struct pci_bus *pci_bus = p_slot->ctrl->pci_dev->subordinate;
struct pci_func *func;
if (!p_slot->ctrl)
return (1);
/* Check to see if (latch closed, card present, power on) */
down(&p_slot->ctrl->crit_sect);
ret = p_slot->hpc_ops->get_adapter_status(p_slot, &getstatus);
if (ret || !getstatus) {
info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number);
up(&p_slot->ctrl->crit_sect);
return (0);
}
ret = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
if (ret || !getstatus) {
info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number);
up(&p_slot->ctrl->crit_sect);
return (0);
}
ret = p_slot->hpc_ops->get_power_status(p_slot, &getstatus);
if (ret || !getstatus) {
info("%s: already disabled slot(%x)\n", __FUNCTION__, p_slot->number);
up(&p_slot->ctrl->crit_sect);
return (0);
}
up(&p_slot->ctrl->crit_sect);
func = shpchp_slot_find(p_slot->bus, p_slot->device, index++);
/* Make sure there are no video controllers here
* for all func of p_slot
*/
while (func && !rc) {
pci_bus->number = func->bus;
devfn = PCI_DEVFN(func->device, func->function);
/* Check the Class Code */
rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code);
if (rc)
return rc;
if (class_code == PCI_BASE_CLASS_DISPLAY) {
/* Display/Video adapter (not supported) */
rc = REMOVE_NOT_SUPPORTED;
} else {
/* See if it's a bridge */
rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type);
if (rc)
return rc;
/* If it's a bridge, check the VGA Enable bit */
if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) {
rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_BRIDGE_CONTROL, &BCR);
if (rc)
return rc;
/* If the VGA Enable bit is set, remove isn't supported */
if (BCR & PCI_BRIDGE_CTL_VGA) {
rc = REMOVE_NOT_SUPPORTED;
}
}
}
func = shpchp_slot_find(p_slot->bus, p_slot->device, index++);
}
func = shpchp_slot_find(p_slot->bus, p_slot->device, 0);
if ((func != NULL) && !rc) {
rc = remove_board(func, p_slot->ctrl);
} else if (!rc)
rc = 1;
if (p_slot)
update_slot_info(p_slot);
return(rc);
}
/**
* configure_new_device - Configures the PCI header information of one board.
*
* @ctrl: pointer to controller structure
* @func: pointer to function structure
* @behind_bridge: 1 if this is a recursive call, 0 if not
* @resources: pointer to set of resource lists
*
* Returns 0 if success
*
*/
static u32 configure_new_device (struct controller * ctrl, struct pci_func * func,
u8 behind_bridge, struct resource_lists * resources, u8 bridge_bus, u8 bridge_dev)
{
u8 temp_byte, function, max_functions, stop_it;
int rc;
u32 ID;
struct pci_func *new_slot;
struct pci_bus lpci_bus, *pci_bus;
int index;
new_slot = func;
dbg("%s\n", __FUNCTION__);
memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus));
pci_bus = &lpci_bus;
pci_bus->number = func->bus;
/* Check for Multi-function device */
rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(func->device, func->function), 0x0E, &temp_byte);
if (rc) {
dbg("%s: rc = %d\n", __FUNCTION__, rc);
return rc;
}
if (temp_byte & 0x80) /* Multi-function device */
max_functions = 8;
else
max_functions = 1;
function = 0;
do {
rc = configure_new_function(ctrl, new_slot, behind_bridge, resources, bridge_bus, bridge_dev);
if (rc) {
dbg("configure_new_function failed %d\n",rc);
index = 0;
while (new_slot) {
new_slot = shpchp_slot_find(new_slot->bus, new_slot->device, index++);
if (new_slot)
shpchp_return_board_resources(new_slot, resources);
}
return(rc);
}
function++;
stop_it = 0;
/* The following loop skips to the next present function
* and creates a board structure
*/
while ((function < max_functions) && (!stop_it)) {
pci_bus_read_config_dword(pci_bus, PCI_DEVFN(func->device, function), 0x00, &ID);
if (ID == 0xFFFFFFFF) { /* There's nothing there. */
function++;
} else { /* There's something there */
/* Setup slot structure. */
new_slot = shpchp_slot_create(func->bus);
if (new_slot == NULL) {
/* Out of memory */
return(1);
}
new_slot->bus = func->bus;
new_slot->device = func->device;
new_slot->function = function;
new_slot->is_a_board = 1;
new_slot->status = 0;
stop_it++;
}
}
} while (function < max_functions);
dbg("returning from configure_new_device\n");
return 0;
}
/*
* Configuration logic that involves the hotplug data structures and
* their bookkeeping
*/
/**
* configure_new_function - Configures the PCI header information of one device
*
* @ctrl: pointer to controller structure
* @func: pointer to function structure
* @behind_bridge: 1 if this is a recursive call, 0 if not
* @resources: pointer to set of resource lists
*
* Calls itself recursively for bridged devices.
* Returns 0 if success
*
*/
static int configure_new_function (struct controller * ctrl, struct pci_func * func,
u8 behind_bridge, struct resource_lists *resources, u8 bridge_bus, u8 bridge_dev)
{
int cloop;
u8 temp_byte;
u8 device;
u8 class_code;
u16 temp_word;
u32 rc;
u32 temp_register;
u32 base;
u32 ID;
unsigned int devfn;
struct pci_resource *mem_node;
struct pci_resource *p_mem_node;
struct pci_resource *io_node;
struct pci_resource *bus_node;
struct pci_resource *hold_mem_node;
struct pci_resource *hold_p_mem_node;
struct pci_resource *hold_IO_node;
struct pci_resource *hold_bus_node;
struct irq_mapping irqs;
struct pci_func *new_slot;
struct pci_bus lpci_bus, *pci_bus;
struct resource_lists temp_resources;
#if defined(CONFIG_X86_64)
u8 IRQ=0;
#endif
memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus));
pci_bus = &lpci_bus;
pci_bus->number = func->bus;
devfn = PCI_DEVFN(func->device, func->function);
/* Check for Bridge */
rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &temp_byte);
if (rc)
return rc;
if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */
/* set Primary bus */
dbg("set Primary bus = 0x%x\n", func->bus);
rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_PRIMARY_BUS, func->bus);
if (rc)
return rc;
/* find range of busses to use */
bus_node = get_max_resource(&resources->bus_head, 1L);
/* If we don't have any busses to allocate, we can't continue */
if (!bus_node) {
err("Got NO bus resource to use\n");
return -ENOMEM;
}
dbg("Got ranges of buses to use: base:len=0x%x:%x\n", bus_node->base, bus_node->length);
/* set Secondary bus */
temp_byte = (u8)bus_node->base;
dbg("set Secondary bus = 0x%x\n", temp_byte);
rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, temp_byte);
if (rc)
return rc;
/* set subordinate bus */
temp_byte = (u8)(bus_node->base + bus_node->length - 1);
dbg("set subordinate bus = 0x%x\n", temp_byte);
rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte);
if (rc)
return rc;
/* Set HP parameters (Cache Line Size, Latency Timer) */
rc = shpchprm_set_hpp(ctrl, func, PCI_HEADER_TYPE_BRIDGE);
if (rc)
return rc;
/* Setup the IO, memory, and prefetchable windows */
io_node = get_max_resource(&(resources->io_head), 0x1000L);
if (io_node) {
dbg("io_node(base, len, next) (%x, %x, %p)\n", io_node->base, io_node->length, io_node->next);
}
mem_node = get_max_resource(&(resources->mem_head), 0x100000L);
if (mem_node) {
dbg("mem_node(base, len, next) (%x, %x, %p)\n", mem_node->base, mem_node->length, mem_node->next);
}
if (resources->p_mem_head)
p_mem_node = get_max_resource(&(resources->p_mem_head), 0x100000L);
else {
/*
* In some platform implementation, MEM and PMEM are not
* distinguished, and hence ACPI _CRS has only MEM entries
* for both MEM and PMEM.
*/
dbg("using MEM for PMEM\n");
p_mem_node = get_max_resource(&(resources->mem_head), 0x100000L);
}
if (p_mem_node) {
dbg("p_mem_node(base, len, next) (%x, %x, %p)\n", p_mem_node->base, p_mem_node->length, p_mem_node->next);
}
/* set up the IRQ info */
if (!resources->irqs) {
irqs.barber_pole = 0;
irqs.interrupt[0] = 0;
irqs.interrupt[1] = 0;
irqs.interrupt[2] = 0;
irqs.interrupt[3] = 0;
irqs.valid_INT = 0;
} else {
irqs.barber_pole = resources->irqs->barber_pole;
irqs.interrupt[0] = resources->irqs->interrupt[0];
irqs.interrupt[1] = resources->irqs->interrupt[1];
irqs.interrupt[2] = resources->irqs->interrupt[2];
irqs.interrupt[3] = resources->irqs->interrupt[3];
irqs.valid_INT = resources->irqs->valid_INT;
}
/* set up resource lists that are now aligned on top and bottom
* for anything behind the bridge.
*/
temp_resources.bus_head = bus_node;
temp_resources.io_head = io_node;
temp_resources.mem_head = mem_node;
temp_resources.p_mem_head = p_mem_node;
temp_resources.irqs = &irqs;
/* Make copies of the nodes we are going to pass down so that
* if there is a problem,we can just use these to free resources
*/
hold_bus_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
hold_IO_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
hold_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
hold_p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!hold_bus_node || !hold_IO_node || !hold_mem_node || !hold_p_mem_node) {
if (hold_bus_node)
kfree(hold_bus_node);
if (hold_IO_node)
kfree(hold_IO_node);
if (hold_mem_node)
kfree(hold_mem_node);
if (hold_p_mem_node)
kfree(hold_p_mem_node);
return(1);
}
memcpy(hold_bus_node, bus_node, sizeof(struct pci_resource));
bus_node->base += 1;
bus_node->length -= 1;
bus_node->next = NULL;
/* If we have IO resources copy them and fill in the bridge's
* IO range registers
*/
if (io_node) {
memcpy(hold_IO_node, io_node, sizeof(struct pci_resource));
io_node->next = NULL;
/* set IO base and Limit registers */
RES_CHECK(io_node->base, 8);
temp_byte = (u8)(io_node->base >> 8);
rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_BASE, temp_byte);
RES_CHECK(io_node->base + io_node->length - 1, 8);
temp_byte = (u8)((io_node->base + io_node->length - 1) >> 8);
rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_LIMIT, temp_byte);
} else {
kfree(hold_IO_node);
hold_IO_node = NULL;
}
/* If we have memory resources copy them and fill in the bridge's
* memory range registers. Otherwise, fill in the range
* registers with values that disable them.
*/
if (mem_node) {
memcpy(hold_mem_node, mem_node, sizeof(struct pci_resource));
mem_node->next = NULL;
/* set Mem base and Limit registers */
RES_CHECK(mem_node->base, 16);
temp_word = (u32)(mem_node->base >> 16);
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_BASE, temp_word);
RES_CHECK(mem_node->base + mem_node->length - 1, 16);
temp_word = (u32)((mem_node->base + mem_node->length - 1) >> 16);
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
} else {
temp_word = 0xFFFF;
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_BASE, temp_word);
temp_word = 0x0000;
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
kfree(hold_mem_node);
hold_mem_node = NULL;
}
/* If we have prefetchable memory resources copy them and
* fill in the bridge's memory range registers. Otherwise,
* fill in the range registers with values that disable them.
*/
if (p_mem_node) {
memcpy(hold_p_mem_node, p_mem_node, sizeof(struct pci_resource));
p_mem_node->next = NULL;
/* set Pre Mem base and Limit registers */
RES_CHECK(p_mem_node->base, 16);
temp_word = (u32)(p_mem_node->base >> 16);
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word);
RES_CHECK(p_mem_node->base + p_mem_node->length - 1, 16);
temp_word = (u32)((p_mem_node->base + p_mem_node->length - 1) >> 16);
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
} else {
temp_word = 0xFFFF;
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word);
temp_word = 0x0000;
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
kfree(hold_p_mem_node);
hold_p_mem_node = NULL;
}
/* Adjust this to compensate for extra adjustment in first loop */
irqs.barber_pole--;
rc = 0;
/* Here we actually find the devices and configure them */
for (device = 0; (device <= 0x1F) && !rc; device++) {
irqs.barber_pole = (irqs.barber_pole + 1) & 0x03;
ID = 0xFFFFFFFF;
pci_bus->number = hold_bus_node->base;
pci_bus_read_config_dword (pci_bus, PCI_DEVFN(device, 0), PCI_VENDOR_ID, &ID);
pci_bus->number = func->bus;
if (ID != 0xFFFFFFFF) { /* device Present */
/* Setup slot structure. */
new_slot = shpchp_slot_create(hold_bus_node->base);
if (new_slot == NULL) {
/* Out of memory */
rc = -ENOMEM;
continue;
}
new_slot->bus = hold_bus_node->base;
new_slot->device = device;
new_slot->function = 0;
new_slot->is_a_board = 1;
new_slot->status = 0;
rc = configure_new_device(ctrl, new_slot, 1, &temp_resources, func->bus, func->device);
dbg("configure_new_device rc=0x%x\n",rc);
} /* End of IF (device in slot?) */
} /* End of FOR loop */
if (rc) {
shpchp_destroy_resource_list(&temp_resources);
return_resource(&(resources->bus_head), hold_bus_node);
return_resource(&(resources->io_head), hold_IO_node);
return_resource(&(resources->mem_head), hold_mem_node);
return_resource(&(resources->p_mem_head), hold_p_mem_node);
return(rc);
}
/* save the interrupt routing information */
if (resources->irqs) {
resources->irqs->interrupt[0] = irqs.interrupt[0];
resources->irqs->interrupt[1] = irqs.interrupt[1];
resources->irqs->interrupt[2] = irqs.interrupt[2];
resources->irqs->interrupt[3] = irqs.interrupt[3];
resources->irqs->valid_INT = irqs.valid_INT;
} else if (!behind_bridge) {
/* We need to hook up the interrupts here */
for (cloop = 0; cloop < 4; cloop++) {
if (irqs.valid_INT & (0x01 << cloop)) {
rc = shpchp_set_irq(func->bus, func->device,
0x0A + cloop, irqs.interrupt[cloop]);
if (rc) {
shpchp_destroy_resource_list (&temp_resources);
return_resource(&(resources->bus_head), hold_bus_node);
return_resource(&(resources->io_head), hold_IO_node);
return_resource(&(resources->mem_head), hold_mem_node);
return_resource(&(resources->p_mem_head), hold_p_mem_node);
return rc;
}
}
} /* end of for loop */
}
/* Return unused bus resources
* First use the temporary node to store information for the board
*/
if (hold_bus_node && bus_node && temp_resources.bus_head) {
hold_bus_node->length = bus_node->base - hold_bus_node->base;
hold_bus_node->next = func->bus_head;
func->bus_head = hold_bus_node;
temp_byte = (u8)(temp_resources.bus_head->base - 1);
/* set subordinate bus */
dbg("re-set subordinate bus = 0x%x\n", temp_byte);
rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_SUBORDINATE_BUS, temp_byte);
if (temp_resources.bus_head->length == 0) {
kfree(temp_resources.bus_head);
temp_resources.bus_head = NULL;
} else {
dbg("return bus res of b:d(0x%x:%x) base:len(0x%x:%x)\n",
func->bus, func->device, temp_resources.bus_head->base, temp_resources.bus_head->length);
return_resource(&(resources->bus_head), temp_resources.bus_head);
}
}
/* If we have IO space available and there is some left,
* return the unused portion
*/
if (hold_IO_node && temp_resources.io_head) {
io_node = do_pre_bridge_resource_split(&(temp_resources.io_head),
&hold_IO_node, 0x1000);
/* Check if we were able to split something off */
if (io_node) {
hold_IO_node->base = io_node->base + io_node->length;
RES_CHECK(hold_IO_node->base, 8);
temp_byte = (u8)((hold_IO_node->base) >> 8);
rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_BASE, temp_byte);
return_resource(&(resources->io_head), io_node);
}
io_node = do_bridge_resource_split(&(temp_resources.io_head), 0x1000);
/* Check if we were able to split something off */
if (io_node) {
/* First use the temporary node to store information for the board */
hold_IO_node->length = io_node->base - hold_IO_node->base;
/* If we used any, add it to the board's list */
if (hold_IO_node->length) {
hold_IO_node->next = func->io_head;
func->io_head = hold_IO_node;
RES_CHECK(io_node->base - 1, 8);
temp_byte = (u8)((io_node->base - 1) >> 8);
rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_IO_LIMIT, temp_byte);
return_resource(&(resources->io_head), io_node);
} else {
/* it doesn't need any IO */
temp_byte = 0x00;
rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_IO_LIMIT, temp_byte);
return_resource(&(resources->io_head), io_node);
kfree(hold_IO_node);
}
} else {
/* it used most of the range */
hold_IO_node->next = func->io_head;
func->io_head = hold_IO_node;
}
} else if (hold_IO_node) {
/* it used the whole range */
hold_IO_node->next = func->io_head;
func->io_head = hold_IO_node;
}
/* If we have memory space available and there is some left,
* return the unused portion
*/
if (hold_mem_node && temp_resources.mem_head) {
mem_node = do_pre_bridge_resource_split(&(temp_resources.mem_head), &hold_mem_node, 0x100000L);
/* Check if we were able to split something off */
if (mem_node) {
hold_mem_node->base = mem_node->base + mem_node->length;
RES_CHECK(hold_mem_node->base, 16);
temp_word = (u32)((hold_mem_node->base) >> 16);
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_BASE, temp_word);
return_resource(&(resources->mem_head), mem_node);
}
mem_node = do_bridge_resource_split(&(temp_resources.mem_head), 0x100000L);
/* Check if we were able to split something off */
if (mem_node) {
/* First use the temporary node to store information for the board */
hold_mem_node->length = mem_node->base - hold_mem_node->base;
if (hold_mem_node->length) {
hold_mem_node->next = func->mem_head;
func->mem_head = hold_mem_node;
/* configure end address */
RES_CHECK(mem_node->base - 1, 16);
temp_word = (u32)((mem_node->base - 1) >> 16);
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
/* Return unused resources to the pool */
return_resource(&(resources->mem_head), mem_node);
} else {
/* it doesn't need any Mem */
temp_word = 0x0000;
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_MEMORY_LIMIT, temp_word);
return_resource(&(resources->mem_head), mem_node);
kfree(hold_mem_node);
}
} else {
/* it used most of the range */
hold_mem_node->next = func->mem_head;
func->mem_head = hold_mem_node;
}
} else if (hold_mem_node) {
/* it used the whole range */
hold_mem_node->next = func->mem_head;
func->mem_head = hold_mem_node;
}
/* If we have prefetchable memory space available and there is some
* left at the end, return the unused portion
*/
if (hold_p_mem_node && temp_resources.p_mem_head) {
p_mem_node = do_pre_bridge_resource_split(&(temp_resources.p_mem_head),
&hold_p_mem_node, 0x100000L);
/* Check if we were able to split something off */
if (p_mem_node) {
hold_p_mem_node->base = p_mem_node->base + p_mem_node->length;
RES_CHECK(hold_p_mem_node->base, 16);
temp_word = (u32)((hold_p_mem_node->base) >> 16);
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_BASE, temp_word);
return_resource(&(resources->p_mem_head), p_mem_node);
}
p_mem_node = do_bridge_resource_split(&(temp_resources.p_mem_head), 0x100000L);
/* Check if we were able to split something off */
if (p_mem_node) {
/* First use the temporary node to store information for the board */
hold_p_mem_node->length = p_mem_node->base - hold_p_mem_node->base;
/* If we used any, add it to the board's list */
if (hold_p_mem_node->length) {
hold_p_mem_node->next = func->p_mem_head;
func->p_mem_head = hold_p_mem_node;
RES_CHECK(p_mem_node->base - 1, 16);
temp_word = (u32)((p_mem_node->base - 1) >> 16);
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
return_resource(&(resources->p_mem_head), p_mem_node);
} else {
/* it doesn't need any PMem */
temp_word = 0x0000;
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word);
return_resource(&(resources->p_mem_head), p_mem_node);
kfree(hold_p_mem_node);
}
} else {
/* it used the most of the range */
hold_p_mem_node->next = func->p_mem_head;
func->p_mem_head = hold_p_mem_node;
}
} else if (hold_p_mem_node) {
/* it used the whole range */
hold_p_mem_node->next = func->p_mem_head;
func->p_mem_head = hold_p_mem_node;
}
/* We should be configuring an IRQ and the bridge's base address
* registers if it needs them. Although we have never seen such
* a device
*/
shpchprm_enable_card(ctrl, func, PCI_HEADER_TYPE_BRIDGE);
dbg("PCI Bridge Hot-Added s:b:d:f(%02x:%02x:%02x:%02x)\n", ctrl->seg, func->bus, func->device, func->function);
} else if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_NORMAL) {
/* Standard device */
u64 base64;
rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code);
if (class_code == PCI_BASE_CLASS_DISPLAY)
return (DEVICE_TYPE_NOT_SUPPORTED);
/* Figure out IO and memory needs */
for (cloop = PCI_BASE_ADDRESS_0; cloop <= PCI_BASE_ADDRESS_5; cloop += 4) {
temp_register = 0xFFFFFFFF;
rc = pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register);
rc = pci_bus_read_config_dword(pci_bus, devfn, cloop, &temp_register);
dbg("Bar[%x]=0x%x on bus:dev:func(0x%x:%x:%x)\n", cloop, temp_register, func->bus, func->device,
func->function);
if (!temp_register)
continue;
base64 = 0L;
if (temp_register & PCI_BASE_ADDRESS_SPACE_IO) {
/* Map IO */
/* set base = amount of IO space */
base = temp_register & 0xFFFFFFFC;
base = ~base + 1;
dbg("NEED IO length(0x%x)\n", base);
io_node = get_io_resource(&(resources->io_head),(ulong)base);
/* allocate the resource to the board */
if (io_node) {
dbg("Got IO base=0x%x(length=0x%x)\n", io_node->base, io_node->length);
base = (u32)io_node->base;
io_node->next = func->io_head;
func->io_head = io_node;
} else {
err("Got NO IO resource(length=0x%x)\n", base);
return -ENOMEM;
}
} else { /* map MEM */
int prefetchable = 1;
struct pci_resource **res_node = &func->p_mem_head;
char *res_type_str = "PMEM";
u32 temp_register2;
if (!(temp_register & PCI_BASE_ADDRESS_MEM_PREFETCH)) {
prefetchable = 0;
res_node = &func->mem_head;
res_type_str++;
}
base = temp_register & 0xFFFFFFF0;
base = ~base + 1;
switch (temp_register & PCI_BASE_ADDRESS_MEM_TYPE_MASK) {
case PCI_BASE_ADDRESS_MEM_TYPE_32:
dbg("NEED 32 %s bar=0x%x(length=0x%x)\n", res_type_str, temp_register, base);
if (prefetchable && resources->p_mem_head)
mem_node=get_resource(&(resources->p_mem_head), (ulong)base);
else {
if (prefetchable)
dbg("using MEM for PMEM\n");
mem_node=get_resource(&(resources->mem_head), (ulong)base);
}
/* allocate the resource to the board */
if (mem_node) {
base = (u32)mem_node->base;
mem_node->next = *res_node;
*res_node = mem_node;
dbg("Got 32 %s base=0x%x(length=0x%x)\n", res_type_str, mem_node->base,
mem_node->length);
} else {
err("Got NO 32 %s resource(length=0x%x)\n", res_type_str, base);
return -ENOMEM;
}
break;
case PCI_BASE_ADDRESS_MEM_TYPE_64:
rc = pci_bus_read_config_dword(pci_bus, devfn, cloop+4, &temp_register2);
dbg("NEED 64 %s bar=0x%x:%x(length=0x%x)\n", res_type_str, temp_register2,
temp_register, base);
if (prefetchable && resources->p_mem_head)
mem_node = get_resource(&(resources->p_mem_head), (ulong)base);
else {
if (prefetchable)
dbg("using MEM for PMEM\n");
mem_node = get_resource(&(resources->mem_head), (ulong)base);
}
/* allocate the resource to the board */
if (mem_node) {
base64 = mem_node->base;
mem_node->next = *res_node;
*res_node = mem_node;
dbg("Got 64 %s base=0x%x:%x(length=%x)\n", res_type_str, (u32)(base64 >> 32),
(u32)base64, mem_node->length);
} else {
err("Got NO 64 %s resource(length=0x%x)\n", res_type_str, base);
return -ENOMEM;
}
break;
default:
dbg("reserved BAR type=0x%x\n", temp_register);
break;
}
}
if (base64) {
rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, (u32)base64);
cloop += 4;
base64 >>= 32;
if (base64) {
dbg("%s: high dword of base64(0x%x) set to 0\n", __FUNCTION__, (u32)base64);
base64 = 0x0L;
}
rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, (u32)base64);
} else {
rc = pci_bus_write_config_dword(pci_bus, devfn, cloop, base);
}
} /* End of base register loop */
#if defined(CONFIG_X86_64)
/* Figure out which interrupt pin this function uses */
rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_INTERRUPT_PIN, &temp_byte);
/* If this function needs an interrupt and we are behind a bridge
and the pin is tied to something that's alread mapped,
set this one the same
*/
if (temp_byte && resources->irqs &&
(resources->irqs->valid_INT &
(0x01 << ((temp_byte + resources->irqs->barber_pole - 1) & 0x03)))) {
/* We have to share with something already set up */
IRQ = resources->irqs->interrupt[(temp_byte + resources->irqs->barber_pole - 1) & 0x03];
} else {
/* Program IRQ based on card type */
rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code);
if (class_code == PCI_BASE_CLASS_STORAGE) {
IRQ = shpchp_disk_irq;
} else {
IRQ = shpchp_nic_irq;
}
}
/* IRQ Line */
rc = pci_bus_write_config_byte (pci_bus, devfn, PCI_INTERRUPT_LINE, IRQ);
if (!behind_bridge) {
rc = shpchp_set_irq(func->bus, func->device, temp_byte + 0x09, IRQ);
if (rc)
return(1);
} else {
/* TBD - this code may also belong in the other clause of this If statement */
resources->irqs->interrupt[(temp_byte + resources->irqs->barber_pole - 1) & 0x03] = IRQ;
resources->irqs->valid_INT |= 0x01 << (temp_byte + resources->irqs->barber_pole - 1) & 0x03;
}
#endif
/* Disable ROM base Address */
temp_word = 0x00L;
rc = pci_bus_write_config_word (pci_bus, devfn, PCI_ROM_ADDRESS, temp_word);
/* Set HP parameters (Cache Line Size, Latency Timer) */
rc = shpchprm_set_hpp(ctrl, func, PCI_HEADER_TYPE_NORMAL);
if (rc)
return rc;
shpchprm_enable_card(ctrl, func, PCI_HEADER_TYPE_NORMAL);
dbg("PCI function Hot-Added s:b:d:f(%02x:%02x:%02x:%02x)\n", ctrl->seg, func->bus, func->device, func->function);
} /* End of Not-A-Bridge else */
else {
/* It's some strange type of PCI adapter (Cardbus?) */
return(DEVICE_TYPE_NOT_SUPPORTED);
}
func->configured = 1;
return 0;
}
/*
* Standard PCI Hot Plug Driver
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
* Copyright (C) 2003-2004 Intel Corporation
*
* 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>,<dely.l.sy@intel.com>
*
*/
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/pci.h>
#include <asm/system.h>
#include "shpchp.h"
#ifdef DEBUG
#define DBG_K_TRACE_ENTRY ((unsigned int)0x00000001) /* On function entry */
#define DBG_K_TRACE_EXIT ((unsigned int)0x00000002) /* On function exit */
#define DBG_K_INFO ((unsigned int)0x00000004) /* Info messages */
#define DBG_K_ERROR ((unsigned int)0x00000008) /* Error messages */
#define DBG_K_TRACE (DBG_K_TRACE_ENTRY|DBG_K_TRACE_EXIT)
#define DBG_K_STANDARD (DBG_K_INFO|DBG_K_ERROR|DBG_K_TRACE)
/* Redefine this flagword to set debug level */
#define DEBUG_LEVEL DBG_K_STANDARD
#define DEFINE_DBG_BUFFER char __dbg_str_buf[256];
#define DBG_PRINT( dbg_flags, args... ) \
do { \
if ( DEBUG_LEVEL & ( dbg_flags ) ) \
{ \
int len; \
len = sprintf( __dbg_str_buf, "%s:%d: %s: ", \
__FILE__, __LINE__, __FUNCTION__ ); \
sprintf( __dbg_str_buf + len, args ); \
printk( KERN_NOTICE "%s\n", __dbg_str_buf ); \
} \
} while (0)
#define DBG_ENTER_ROUTINE DBG_PRINT (DBG_K_TRACE_ENTRY, "%s", "[Entry]");
#define DBG_LEAVE_ROUTINE DBG_PRINT (DBG_K_TRACE_EXIT, "%s", "[Exit]");
#else
#define DEFINE_DBG_BUFFER
#define DBG_ENTER_ROUTINE
#define DBG_LEAVE_ROUTINE
#endif /* DEBUG */
/* Slot Available Register I field definition */
#define SLOT_33MHZ 0x0000001f
#define SLOT_66MHZ_PCIX 0x00001f00
#define SLOT_100MHZ_PCIX 0x001f0000
#define SLOT_133MHZ_PCIX 0x1f000000
/* Slot Available Register II field definition */
#define SLOT_66MHZ 0x0000001f
#define SLOT_66MHZ_PCIX_266 0x00000f00
#define SLOT_100MHZ_PCIX_266 0x0000f000
#define SLOT_133MHZ_PCIX_266 0x000f0000
#define SLOT_66MHZ_PCIX_533 0x00f00000
#define SLOT_100MHZ_PCIX_533 0x0f000000
#define SLOT_133MHZ_PCIX_533 0xf0000000
/* Secondary Bus Configuration Register */
/* For PI = 1, Bits 0 to 2 have been encoded as follows to show current bus speed/mode */
#define PCI_33MHZ 0x0
#define PCI_66MHZ 0x1
#define PCIX_66MHZ 0x2
#define PCIX_100MHZ 0x3
#define PCIX_133MHZ 0x4
/* For PI = 2, Bits 0 to 3 have been encoded as follows to show current bus speed/mode */
#define PCI_33MHZ 0x0
#define PCI_66MHZ 0x1
#define PCIX_66MHZ 0x2
#define PCIX_100MHZ 0x3
#define PCIX_133MHZ 0x4
#define PCIX_66MHZ_ECC 0x5
#define PCIX_100MHZ_ECC 0x6
#define PCIX_133MHZ_ECC 0x7
#define PCIX_66MHZ_266 0x8
#define PCIX_100MHZ_266 0x9
#define PCIX_133MHZ_266 0x0a
#define PCIX_66MHZ_533 0x0b
#define PCIX_100MHZ_533 0x0c
#define PCIX_133MHZ_533 0x0d
/* Slot Configuration */
#define SLOT_NUM 0x0000001F
#define FIRST_DEV_NUM 0x00001F00
#define PSN 0x07FF0000
#define UPDOWN 0x20000000
#define MRLSENSOR 0x40000000
#define ATTN_BUTTON 0x80000000
/* Slot Status Field Definitions */
/* Slot State */
#define PWR_ONLY 0x0001
#define ENABLED 0x0002
#define DISABLED 0x0003
/* Power Indicator State */
#define PWR_LED_ON 0x0004
#define PWR_LED_BLINK 0x0008
#define PWR_LED_OFF 0x000c
/* Attention Indicator State */
#define ATTEN_LED_ON 0x0010
#define ATTEN_LED_BLINK 0x0020
#define ATTEN_LED_OFF 0x0030
/* Power Fault */
#define pwr_fault 0x0040
/* Attention Button */
#define ATTEN_BUTTON 0x0080
/* MRL Sensor */
#define MRL_SENSOR 0x0100
/* 66 MHz Capable */
#define IS_66MHZ_CAP 0x0200
/* PRSNT1#/PRSNT2# */
#define SLOT_EMP 0x0c00
/* PCI-X Capability */
#define NON_PCIX 0x0000
#define PCIX_66 0x1000
#define PCIX_133 0x3000
#define PCIX_266 0x4000 /* For PI = 2 only */
#define PCIX_533 0x5000 /* For PI = 2 only */
/* SHPC 'write' operations/commands */
/* Slot operation - 0x00h to 0x3Fh */
#define NO_CHANGE 0x00
/* Slot state - Bits 0 & 1 of controller command register */
#define SET_SLOT_PWR 0x01
#define SET_SLOT_ENABLE 0x02
#define SET_SLOT_DISABLE 0x03
/* Power indicator state - Bits 2 & 3 of controller command register*/
#define SET_PWR_ON 0x04
#define SET_PWR_BLINK 0x08
#define SET_PWR_OFF 0x0C
/* Attention indicator state - Bits 4 & 5 of controller command register*/
#define SET_ATTN_ON 0x010
#define SET_ATTN_BLINK 0x020
#define SET_ATTN_OFF 0x030
/* Set bus speed/mode A - 0x40h to 0x47h */
#define SETA_PCI_33MHZ 0x40
#define SETA_PCI_66MHZ 0x41
#define SETA_PCIX_66MHZ 0x42
#define SETA_PCIX_100MHZ 0x43
#define SETA_PCIX_133MHZ 0x44
#define RESERV_1 0x45
#define RESERV_2 0x46
#define RESERV_3 0x47
/* Set bus speed/mode B - 0x50h to 0x5fh */
#define SETB_PCI_33MHZ 0x50
#define SETB_PCI_66MHZ 0x51
#define SETB_PCIX_66MHZ_PM 0x52
#define SETB_PCIX_100MHZ_PM 0x53
#define SETB_PCIX_133MHZ_PM 0x54
#define SETB_PCIX_66MHZ_EM 0x55
#define SETB_PCIX_100MHZ_EM 0x56
#define SETB_PCIX_133MHZ_EM 0x57
#define SETB_PCIX_66MHZ_266 0x58
#define SETB_PCIX_100MHZ_266 0x59
#define SETB_PCIX_133MHZ_266 0x5a
#define SETB_PCIX_66MHZ_533 0x5b
#define SETB_PCIX_100MHZ_533 0x5c
#define SETB_PCIX_133MHZ_533 0x5d
/* Power-on all slots - 0x48h */
#define SET_PWR_ON_ALL 0x48
/* Enable all slots - 0x49h */
#define SET_ENABLE_ALL 0x49
/* SHPC controller command error code */
#define SWITCH_OPEN 0x1
#define INVALID_CMD 0x2
#define INVALID_SPEED_MODE 0x4
/* For accessing SHPC Working Register Set */
#define DWORD_SELECT 0x2
#define DWORD_DATA 0x4
#define BASE_OFFSET 0x0
/* Field Offset in Logical Slot Register - byte boundary */
#define SLOT_EVENT_LATCH 0x2
#define SLOT_SERR_INT_MASK 0x3
static spinlock_t hpc_event_lock;
DEFINE_DBG_BUFFER /* Debug string buffer for entire HPC defined here */
static struct php_ctlr_state_s *php_ctlr_list_head = 0; /* HPC state linked list */
static int ctlr_seq_num = 0; /* Controller sequenc # */
static spinlock_t list_lock;
static irqreturn_t shpc_isr(int IRQ, void *dev_id, struct pt_regs *regs);
static void start_int_poll_timer(struct php_ctlr_state_s *php_ctlr, int seconds);
/* This is the interrupt polling timeout function. */
static void int_poll_timeout(unsigned long lphp_ctlr)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *)lphp_ctlr;
DBG_ENTER_ROUTINE
if ( !php_ctlr ) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return;
}
/* Poll for interrupt events. regs == NULL => polling */
shpc_isr( 0, (void *)php_ctlr, NULL );
init_timer(&php_ctlr->int_poll_timer);
if (!shpchp_poll_time)
shpchp_poll_time = 2; /* reset timer to poll in 2 secs if user doesn't specify at module installation*/
start_int_poll_timer(php_ctlr, shpchp_poll_time);
return;
}
/* This function starts the interrupt polling timer. */
static void start_int_poll_timer(struct php_ctlr_state_s *php_ctlr, int seconds)
{
if (!php_ctlr) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return;
}
if ( ( seconds <= 0 ) || ( seconds > 60 ) )
seconds = 2; /* Clamp to sane value */
php_ctlr->int_poll_timer.function = &int_poll_timeout;
php_ctlr->int_poll_timer.data = (unsigned long)php_ctlr; /* Instance data */
php_ctlr->int_poll_timer.expires = jiffies + seconds * HZ;
add_timer(&php_ctlr->int_poll_timer);
return;
}
static int shpc_write_cmd(struct slot *slot, u8 t_slot, u8 cmd)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
u16 cmd_status;
int retval = 0;
u16 temp_word;
int i;
DBG_ENTER_ROUTINE
if (!php_ctlr) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
for (i = 0; i < 10; i++) {
cmd_status = readw(php_ctlr->creg + CMD_STATUS);
if (!(cmd_status & 0x1))
break;
/* Check every 0.1 sec for a total of 1 sec*/
set_current_state(TASK_INTERRUPTIBLE);
schedule_timeout(HZ/10);
}
cmd_status = readw(php_ctlr->creg + CMD_STATUS);
if (cmd_status & 0x1) {
/* After 1 sec and and the controller is still busy */
err("%s : Controller is still busy after 1 sec.\n", __FUNCTION__);
return -1;
}
++t_slot;
temp_word = (t_slot << 8) | (cmd & 0xFF);
dbg("%s: t_slot %x cmd %x\n", __FUNCTION__, t_slot, cmd);
/* To make sure the Controller Busy bit is 0 before we send out the
* command.
*/
writew(temp_word, php_ctlr->creg + CMD);
dbg("%s: temp_word written %x\n", __FUNCTION__, temp_word);
DBG_LEAVE_ROUTINE
return retval;
}
static int hpc_check_cmd_status(struct controller *ctrl)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) ctrl->hpc_ctlr_handle;
u16 cmd_status;
int retval = 0;
DBG_ENTER_ROUTINE
if (!ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
cmd_status = readw(php_ctlr->creg + CMD_STATUS) & 0x000F;
switch (cmd_status >> 1) {
case 0:
retval = 0;
break;
case 1:
retval = SWITCH_OPEN;
err("%s: Switch opened!\n", __FUNCTION__);
break;
case 2:
retval = INVALID_CMD;
err("%s: Invalid HPC command!\n", __FUNCTION__);
break;
case 4:
retval = INVALID_SPEED_MODE;
err("%s: Invalid bus speed/mode!\n", __FUNCTION__);
break;
default:
retval = cmd_status;
}
DBG_LEAVE_ROUTINE
return retval;
}
static int hpc_get_attention_status(struct slot *slot, u8 *status)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
u32 slot_reg;
u16 slot_status;
u8 atten_led_state;
DBG_ENTER_ROUTINE
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
slot_reg = readl(php_ctlr->creg + SLOT1 + 4*(slot->hp_slot));
slot_status = (u16) slot_reg;
atten_led_state = (slot_status & 0x0030) >> 4;
switch (atten_led_state) {
case 0:
*status = 0xFF; /* Reserved */
break;
case 1:
*status = 1; /* On */
break;
case 2:
*status = 2; /* Blink */
break;
case 3:
*status = 0; /* Off */
break;
default:
*status = 0xFF;
break;
}
DBG_LEAVE_ROUTINE
return 0;
}
static int hpc_get_power_status(struct slot * slot, u8 *status)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
u32 slot_reg;
u16 slot_status;
u8 slot_state;
int retval = 0;
DBG_ENTER_ROUTINE
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
slot_reg = readl(php_ctlr->creg + SLOT1 + 4*(slot->hp_slot));
slot_status = (u16) slot_reg;
slot_state = (slot_status & 0x0003);
switch (slot_state) {
case 0:
*status = 0xFF;
break;
case 1:
*status = 2; /* Powered only */
break;
case 2:
*status = 1; /* Enabled */
break;
case 3:
*status = 0; /* Disabled */
break;
default:
*status = 0xFF;
break;
}
DBG_LEAVE_ROUTINE
return retval;
}
static int hpc_get_latch_status(struct slot *slot, u8 *status)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
u32 slot_reg;
u16 slot_status;
DBG_ENTER_ROUTINE
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
slot_reg = readl(php_ctlr->creg + SLOT1 + 4*(slot->hp_slot));
slot_status = (u16)slot_reg;
*status = ((slot_status & 0x0100) == 0) ? 1 : 0;
DBG_LEAVE_ROUTINE
return 0;
}
static int hpc_get_adapter_status(struct slot *slot, u8 *status)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
u32 slot_reg;
u16 slot_status;
u8 card_state;
DBG_ENTER_ROUTINE
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
slot_reg = readl(php_ctlr->creg + SLOT1 + 4*(slot->hp_slot));
slot_status = (u16)slot_reg;
card_state = (u8)((slot_status & 0x0C00) >> 10);
*status = (card_state != 0x3) ? 1 : 0;
DBG_LEAVE_ROUTINE
return 0;
}
static int hpc_get_prog_int(struct slot *slot, u8 *prog_int)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
DBG_ENTER_ROUTINE
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
*prog_int = readb(php_ctlr->creg + PROG_INTERFACE);
DBG_LEAVE_ROUTINE
return 0;
}
static int hpc_get_adapter_speed(struct slot *slot, enum pci_bus_speed *value)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
u32 slot_reg;
u16 slot_status, sec_bus_status;
u8 m66_cap, pcix_cap, pi;
int retval = 0;
DBG_ENTER_ROUTINE
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
if (slot->hp_slot >= php_ctlr->num_slots) {
err("%s: Invalid HPC slot number!\n", __FUNCTION__);
return -1;
}
pi = readb(php_ctlr->creg + PROG_INTERFACE);
slot_reg = readl(php_ctlr->creg + SLOT1 + 4*(slot->hp_slot));
dbg("%s: pi = %d, slot_reg = %x\n", __FUNCTION__, pi, slot_reg);
slot_status = (u16) slot_reg;
dbg("%s: slot_status = %x\n", __FUNCTION__, slot_status);
sec_bus_status = readw(php_ctlr->creg + SEC_BUS_CONFIG);
pcix_cap = (u8) ((slot_status & 0x3000) >> 12);
dbg("%s: pcix_cap = %x\n", __FUNCTION__, pcix_cap);
m66_cap = (u8) ((slot_status & 0x0200) >> 9);
dbg("%s: m66_cap = %x\n", __FUNCTION__, m66_cap);
if (pi == 2) {
switch (pcix_cap) {
case 0:
*value = m66_cap ? PCI_SPEED_66MHz : PCI_SPEED_33MHz;
break;
case 1:
*value = PCI_SPEED_66MHz_PCIX;
break;
case 3:
*value = PCI_SPEED_133MHz_PCIX;
break;
case 4:
*value = PCI_SPEED_133MHz_PCIX_266;
break;
case 5:
*value = PCI_SPEED_133MHz_PCIX_533;
break;
case 2: /* Reserved */
default:
*value = PCI_SPEED_UNKNOWN;
retval = -ENODEV;
break;
}
} else {
switch (pcix_cap) {
case 0:
*value = m66_cap ? PCI_SPEED_66MHz : PCI_SPEED_33MHz;
break;
case 1:
*value = PCI_SPEED_66MHz_PCIX;
break;
case 3:
*value = PCI_SPEED_133MHz_PCIX;
break;
case 2: /* Reserved */
default:
*value = PCI_SPEED_UNKNOWN;
retval = -ENODEV;
break;
}
}
dbg("Adapter speed = %d\n", *value);
DBG_LEAVE_ROUTINE
return retval;
}
static int hpc_get_mode1_ECC_cap(struct slot *slot, u8 *mode)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
u16 sec_bus_status;
u8 pi;
int retval = 0;
DBG_ENTER_ROUTINE
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
pi = readb(php_ctlr->creg + PROG_INTERFACE);
sec_bus_status = readw(php_ctlr->creg + SEC_BUS_CONFIG);
if (pi == 2) {
*mode = (sec_bus_status & 0x0100) >> 7;
} else {
retval = -1;
}
dbg("Mode 1 ECC cap = %d\n", *mode);
DBG_LEAVE_ROUTINE
return retval;
}
static int hpc_query_power_fault(struct slot * slot)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
u32 slot_reg;
u16 slot_status;
u8 pwr_fault_state, status;
DBG_ENTER_ROUTINE
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
slot_reg = readl(php_ctlr->creg + SLOT1 + 4*(slot->hp_slot));
slot_status = (u16) slot_reg;
pwr_fault_state = (slot_status & 0x0040) >> 7;
status = (pwr_fault_state == 1) ? 0 : 1;
DBG_LEAVE_ROUTINE
/* Note: Logic 0 => fault */
return status;
}
static int hpc_set_attention_status(struct slot *slot, u8 value)
{
struct php_ctlr_state_s *php_ctlr =(struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
u8 slot_cmd = 0;
int rc = 0;
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
if (slot->hp_slot >= php_ctlr->num_slots) {
err("%s: Invalid HPC slot number!\n", __FUNCTION__);
return -1;
}
switch (value) {
case 0 :
slot_cmd = 0x30; /* OFF */
break;
case 1:
slot_cmd = 0x10; /* ON */
break;
case 2:
slot_cmd = 0x20; /* BLINK */
break;
default:
return -1;
}
shpc_write_cmd(slot, slot->hp_slot, slot_cmd);
return rc;
}
static void hpc_set_green_led_on(struct slot *slot)
{
struct php_ctlr_state_s *php_ctlr =(struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
u8 slot_cmd;
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return ;
}
if (slot->hp_slot >= php_ctlr->num_slots) {
err("%s: Invalid HPC slot number!\n", __FUNCTION__);
return ;
}
slot_cmd = 0x04;
shpc_write_cmd(slot, slot->hp_slot, slot_cmd);
return;
}
static void hpc_set_green_led_off(struct slot *slot)
{
struct php_ctlr_state_s *php_ctlr =(struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
u8 slot_cmd;
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return ;
}
if (slot->hp_slot >= php_ctlr->num_slots) {
err("%s: Invalid HPC slot number!\n", __FUNCTION__);
return ;
}
slot_cmd = 0x0C;
shpc_write_cmd(slot, slot->hp_slot, slot_cmd);
return;
}
static void hpc_set_green_led_blink(struct slot *slot)
{
struct php_ctlr_state_s *php_ctlr =(struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
u8 slot_cmd;
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return ;
}
if (slot->hp_slot >= php_ctlr->num_slots) {
err("%s: Invalid HPC slot number!\n", __FUNCTION__);
return ;
}
slot_cmd = 0x08;
shpc_write_cmd(slot, slot->hp_slot, slot_cmd);
return;
}
int shpc_get_ctlr_slot_config(struct controller *ctrl,
int *num_ctlr_slots, /* number of slots in this HPC */
int *first_device_num, /* PCI dev num of the first slot in this SHPC */
int *physical_slot_num, /* phy slot num of the first slot in this SHPC */
int *updown, /* physical_slot_num increament: 1 or -1 */
int *flags)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) ctrl->hpc_ctlr_handle;
DBG_ENTER_ROUTINE
if (!ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
*first_device_num = php_ctlr->slot_device_offset; /* Obtained in shpc_init() */
*num_ctlr_slots = php_ctlr->num_slots; /* Obtained in shpc_init() */
*physical_slot_num = (readl(php_ctlr->creg + SLOT_CONFIG) & PSN) >> 16;
dbg("%s: physical_slot_num = %x\n", __FUNCTION__, *physical_slot_num);
*updown = ((readl(php_ctlr->creg + SLOT_CONFIG) & UPDOWN ) >> 29) ? 1 : -1;
DBG_LEAVE_ROUTINE
return 0;
}
static void hpc_release_ctlr(struct controller *ctrl)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) ctrl->hpc_ctlr_handle;
struct php_ctlr_state_s *p, *p_prev;
DBG_ENTER_ROUTINE
if (!ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return ;
}
if (shpchp_poll_mode) {
del_timer(&php_ctlr->int_poll_timer);
} else {
if (php_ctlr->irq) {
free_irq(php_ctlr->irq, ctrl);
php_ctlr->irq = 0;
}
}
if (php_ctlr->pci_dev) {
dbg("%s: before calling iounmap & release_mem_region\n", __FUNCTION__);
iounmap(php_ctlr->creg);
release_mem_region(pci_resource_start(php_ctlr->pci_dev, 0), pci_resource_len(php_ctlr->pci_dev, 0));
dbg("%s: before calling iounmap & release_mem_region\n", __FUNCTION__);
php_ctlr->pci_dev = 0;
}
spin_lock(&list_lock);
p = php_ctlr_list_head;
p_prev = NULL;
while (p) {
if (p == php_ctlr) {
if (p_prev)
p_prev->pnext = p->pnext;
else
php_ctlr_list_head = p->pnext;
break;
} else {
p_prev = p;
p = p->pnext;
}
}
spin_unlock(&list_lock);
kfree(php_ctlr);
DBG_LEAVE_ROUTINE
}
static int hpc_power_on_slot(struct slot * slot)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
u8 slot_cmd;
int retval = 0;
DBG_ENTER_ROUTINE
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
if (slot->hp_slot >= php_ctlr->num_slots) {
err("%s: Invalid HPC slot number!\n", __FUNCTION__);
return -1;
}
slot_cmd = 0x01;
retval = shpc_write_cmd(slot, slot->hp_slot, slot_cmd);
if (retval) {
err("%s: Write command failed!\n", __FUNCTION__);
return -1;
}
DBG_LEAVE_ROUTINE
return retval;
}
static int hpc_slot_enable(struct slot * slot)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
u8 slot_cmd;
int retval = 0;
DBG_ENTER_ROUTINE
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
if (slot->hp_slot >= php_ctlr->num_slots) {
err("%s: Invalid HPC slot number!\n", __FUNCTION__);
return -1;
}
/* 3A => Slot - Enable, Power Indicator - Blink, Attention Indicator - Off */
slot_cmd = 0x3A;
retval = shpc_write_cmd(slot, slot->hp_slot, slot_cmd);
if (retval) {
err("%s: Write command failed!\n", __FUNCTION__);
return -1;
}
DBG_LEAVE_ROUTINE
return retval;
}
static int hpc_slot_disable(struct slot * slot)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
u8 slot_cmd;
int retval = 0;
DBG_ENTER_ROUTINE
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
if (slot->hp_slot >= php_ctlr->num_slots) {
err("%s: Invalid HPC slot number!\n", __FUNCTION__);
return -1;
}
/* 1F => Slot - Disable, Power Indicator - Off, Attention Indicator - On */
slot_cmd = 0x1F;
retval = shpc_write_cmd(slot, slot->hp_slot, slot_cmd);
if (retval) {
err("%s: Write command failed!\n", __FUNCTION__);
return -1;
}
DBG_LEAVE_ROUTINE
return retval;
}
static int hpc_enable_all_slots( struct slot *slot )
{
int retval = 0;
DBG_ENTER_ROUTINE
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
retval = shpc_write_cmd(slot, 0, SET_ENABLE_ALL);
if (retval) {
err("%s: Write command failed!\n", __FUNCTION__);
return -1;
}
DBG_LEAVE_ROUTINE
return retval;
}
static int hpc_pwr_on_all_slots(struct slot *slot)
{
int retval = 0;
DBG_ENTER_ROUTINE
retval = shpc_write_cmd(slot, 0, SET_PWR_ON_ALL);
if (retval) {
err("%s: Write command failed!\n", __FUNCTION__);
return -1;
}
DBG_LEAVE_ROUTINE
return retval;
}
static int hpc_set_bus_speed_mode(struct slot * slot, enum pci_bus_speed value)
{
u8 slot_cmd;
u8 pi;
int retval = 0;
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
DBG_ENTER_ROUTINE
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
pi = readb(php_ctlr->creg + PROG_INTERFACE);
if (pi == 1) {
switch (value) {
case 0:
slot_cmd = SETA_PCI_33MHZ;
break;
case 1:
slot_cmd = SETA_PCI_66MHZ;
break;
case 2:
slot_cmd = SETA_PCIX_66MHZ;
break;
case 3:
slot_cmd = SETA_PCIX_100MHZ;
break;
case 4:
slot_cmd = SETA_PCIX_133MHZ;
break;
default:
slot_cmd = PCI_SPEED_UNKNOWN;
retval = -ENODEV;
return retval;
}
} else {
switch (value) {
case 0:
slot_cmd = SETB_PCI_33MHZ;
break;
case 1:
slot_cmd = SETB_PCI_66MHZ;
break;
case 2:
slot_cmd = SETB_PCIX_66MHZ_PM;
break;
case 3:
slot_cmd = SETB_PCIX_100MHZ_PM;
break;
case 4:
slot_cmd = SETB_PCIX_133MHZ_PM;
break;
case 5:
slot_cmd = SETB_PCIX_66MHZ_EM;
break;
case 6:
slot_cmd = SETB_PCIX_100MHZ_EM;
break;
case 7:
slot_cmd = SETB_PCIX_133MHZ_EM;
break;
case 8:
slot_cmd = SETB_PCIX_66MHZ_266;
break;
case 0x9:
slot_cmd = SETB_PCIX_100MHZ_266;
break;
case 0xa:
slot_cmd = SETB_PCIX_133MHZ_266;
break;
case 0xb:
slot_cmd = SETB_PCIX_66MHZ_533;
break;
case 0xc:
slot_cmd = SETB_PCIX_100MHZ_533;
break;
case 0xd:
slot_cmd = SETB_PCIX_133MHZ_533;
break;
default:
slot_cmd = PCI_SPEED_UNKNOWN;
retval = -ENODEV;
return retval;
}
}
retval = shpc_write_cmd(slot, 0, slot_cmd);
if (retval) {
err("%s: Write command failed!\n", __FUNCTION__);
return -1;
}
DBG_LEAVE_ROUTINE
return retval;
}
static irqreturn_t shpc_isr(int IRQ, void *dev_id, struct pt_regs *regs)
{
struct controller *ctrl = NULL;
struct php_ctlr_state_s *php_ctlr;
u8 schedule_flag = 0;
u8 temp_byte;
u32 temp_dword, intr_loc, intr_loc2;
int hp_slot;
if (!dev_id)
return IRQ_NONE;
if (!shpchp_poll_mode) {
ctrl = (struct controller *)dev_id;
php_ctlr = ctrl->hpc_ctlr_handle;
} else
php_ctlr = (struct php_ctlr_state_s *) dev_id;
if (!php_ctlr || !php_ctlr->creg)
return IRQ_NONE;
/* Check to see if it was our interrupt */
intr_loc = readl(php_ctlr->creg + INTR_LOC);
if (!intr_loc)
return IRQ_NONE;
dbg("%s: shpc_isr proceeds\n", __FUNCTION__);
dbg("%s: intr_loc = %x\n",__FUNCTION__, intr_loc);
/* Mask Global Interrupt Mask - see implementation note on p. 139 */
/* of SHPC spec rev 1.0*/
temp_dword = readl(php_ctlr->creg + SERR_INTR_ENABLE);
dbg("%s: Before masking global interrupt, temp_dword = %x\n",
__FUNCTION__, temp_dword);
temp_dword |= 0x00000001;
dbg("%s: After masking global interrupt, temp_dword = %x\n",
__FUNCTION__, temp_dword);
writel(temp_dword, php_ctlr->creg + SERR_INTR_ENABLE);
intr_loc2 = readl(php_ctlr->creg + INTR_LOC);
dbg("%s: intr_loc2 = %x\n",__FUNCTION__, intr_loc2);
if (intr_loc & 0x0001) {
/*
* Command Complete Interrupt Pending
* RO only - clear by writing 0 to the Command Completion
* Detect bit in Controller SERR-INT register
*/
temp_dword = readl(php_ctlr->creg + SERR_INTR_ENABLE);
dbg("%s: Before clearing CCIP, temp_dword = %x\n",
__FUNCTION__, temp_dword);
temp_dword &= 0xfffeffff;
dbg("%s: After clearing CCIP, temp_dword = %x\n",
__FUNCTION__, temp_dword);
writel(temp_dword, php_ctlr->creg + SERR_INTR_ENABLE);
wake_up_interruptible(&ctrl->queue);
}
if ((intr_loc = (intr_loc >> 1)) == 0) {
/* Unmask Global Interrupt Mask */
temp_dword = readl(php_ctlr->creg + SERR_INTR_ENABLE);
dbg("%s: 1-Before unmasking global interrupt, temp_dword = %x\n",
__FUNCTION__, temp_dword);
temp_dword &= 0xfffffffe;
dbg("%s: 1-After unmasking global interrupt, temp_dword = %x\n",
__FUNCTION__, temp_dword);
writel(temp_dword, php_ctlr->creg + SERR_INTR_ENABLE);
return IRQ_NONE;
}
for (hp_slot = 0; hp_slot < ctrl->num_slots; hp_slot++) {
/* To find out which slot has interrupt pending */
if ((intr_loc >> hp_slot) & 0x01) {
temp_dword = readl(php_ctlr->creg + SLOT1 + (4*hp_slot));
dbg("%s: Slot %x with intr, temp_dword = %x\n",
__FUNCTION__, hp_slot, temp_dword);
temp_byte = (temp_dword >> 16) & 0xFF;
dbg("%s: Slot with intr, temp_byte = %x\n",
__FUNCTION__, temp_byte);
if ((php_ctlr->switch_change_callback) && (temp_byte & 0x08))
schedule_flag += php_ctlr->switch_change_callback(
hp_slot, php_ctlr->callback_instance_id);
if ((php_ctlr->attention_button_callback) && (temp_byte & 0x04))
schedule_flag += php_ctlr->attention_button_callback(
hp_slot, php_ctlr->callback_instance_id);
if ((php_ctlr->presence_change_callback) && (temp_byte & 0x01))
schedule_flag += php_ctlr->presence_change_callback(
hp_slot , php_ctlr->callback_instance_id);
if ((php_ctlr->power_fault_callback) && (temp_byte & 0x12))
schedule_flag += php_ctlr->power_fault_callback(
hp_slot, php_ctlr->callback_instance_id);
/* Clear all slot events */
temp_dword = 0xe01fffff;
dbg("%s: Clearing slot events, temp_dword = %x\n",
__FUNCTION__, temp_dword);
writel(temp_dword, php_ctlr->creg + SLOT1 + (4*hp_slot));
intr_loc2 = readl(php_ctlr->creg + INTR_LOC);
dbg("%s: intr_loc2 = %x\n",__FUNCTION__, intr_loc2);
}
}
/* Unmask Global Interrupt Mask */
temp_dword = readl(php_ctlr->creg + SERR_INTR_ENABLE);
dbg("%s: 2-Before unmasking global interrupt, temp_dword = %x\n",
__FUNCTION__, temp_dword);
temp_dword &= 0xfffffffe;
dbg("%s: 2-After unmasking global interrupt, temp_dword = %x\n",
__FUNCTION__, temp_dword);
writel(temp_dword, php_ctlr->creg + SERR_INTR_ENABLE);
return IRQ_HANDLED;
}
static int hpc_get_max_bus_speed (struct slot *slot, enum pci_bus_speed *value)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
enum pci_bus_speed bus_speed = PCI_SPEED_UNKNOWN;
int retval = 0;
u8 pi;
u32 slot_avail1, slot_avail2;
int slot_num;
DBG_ENTER_ROUTINE
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
if (slot->hp_slot >= php_ctlr->num_slots) {
err("%s: Invalid HPC slot number!\n", __FUNCTION__);
return -1;
}
pi = readb(php_ctlr->creg + PROG_INTERFACE);
slot_avail1 = readl(php_ctlr->creg + SLOT_AVAIL1);
slot_avail2 = readl(php_ctlr->creg + SLOT_AVAIL2);
if (pi == 2) {
if ((slot_num = ((slot_avail2 & SLOT_133MHZ_PCIX_533) >> 27) ) != 0 )
bus_speed = PCIX_133MHZ_533;
else if ((slot_num = ((slot_avail2 & SLOT_100MHZ_PCIX_533) >> 23) ) != 0 )
bus_speed = PCIX_100MHZ_533;
else if ((slot_num = ((slot_avail2 & SLOT_66MHZ_PCIX_533) >> 19) ) != 0 )
bus_speed = PCIX_66MHZ_533;
else if ((slot_num = ((slot_avail2 & SLOT_133MHZ_PCIX_266) >> 15) ) != 0 )
bus_speed = PCIX_133MHZ_266;
else if ((slot_num = ((slot_avail2 & SLOT_100MHZ_PCIX_266) >> 11) ) != 0 )
bus_speed = PCIX_100MHZ_266;
else if ((slot_num = ((slot_avail2 & SLOT_66MHZ_PCIX_266) >> 7) ) != 0 )
bus_speed = PCIX_66MHZ_266;
else if ((slot_num = ((slot_avail1 & SLOT_133MHZ_PCIX) >> 23) ) != 0 )
bus_speed = PCIX_133MHZ;
else if ((slot_num = ((slot_avail1 & SLOT_100MHZ_PCIX) >> 15) ) != 0 )
bus_speed = PCIX_100MHZ;
else if ((slot_num = ((slot_avail1 & SLOT_66MHZ_PCIX) >> 7) ) != 0 )
bus_speed = PCIX_66MHZ;
else if ((slot_num = (slot_avail2 & SLOT_66MHZ)) != 0 )
bus_speed = PCI_66MHZ;
else if ((slot_num = (slot_avail1 & SLOT_33MHZ)) != 0 )
bus_speed = PCI_33MHZ;
else bus_speed = PCI_SPEED_UNKNOWN;
} else {
if ((slot_num = ((slot_avail1 & SLOT_133MHZ_PCIX) >> 23) ) != 0 )
bus_speed = PCIX_133MHZ;
else if ((slot_num = ((slot_avail1 & SLOT_100MHZ_PCIX) >> 15) ) != 0 )
bus_speed = PCIX_100MHZ;
else if ((slot_num = ((slot_avail1 & SLOT_66MHZ_PCIX) >> 7) ) != 0 )
bus_speed = PCIX_66MHZ;
else if ((slot_num = (slot_avail2 & SLOT_66MHZ)) != 0 )
bus_speed = PCI_66MHZ;
else if ((slot_num = (slot_avail1 & SLOT_33MHZ)) != 0 )
bus_speed = PCI_33MHZ;
else bus_speed = PCI_SPEED_UNKNOWN;
}
*value = bus_speed;
dbg("Max bus speed = %d\n", bus_speed);
DBG_LEAVE_ROUTINE
return retval;
}
static int hpc_get_cur_bus_speed (struct slot *slot, enum pci_bus_speed *value)
{
struct php_ctlr_state_s *php_ctlr = (struct php_ctlr_state_s *) slot->ctrl->hpc_ctlr_handle;
enum pci_bus_speed bus_speed = PCI_SPEED_UNKNOWN;
u16 sec_bus_status;
int retval = 0;
u8 pi;
DBG_ENTER_ROUTINE
if (!slot->ctrl->hpc_ctlr_handle) {
err("%s: Invalid HPC controller handle!\n", __FUNCTION__);
return -1;
}
if (slot->hp_slot >= php_ctlr->num_slots) {
err("%s: Invalid HPC slot number!\n", __FUNCTION__);
return -1;
}
pi = readb(php_ctlr->creg + PROG_INTERFACE);
sec_bus_status = readw(php_ctlr->creg + SEC_BUS_CONFIG);
if (pi == 2) {
switch (sec_bus_status & 0x000f) {
case 0:
bus_speed = PCI_SPEED_33MHz;
break;
case 1:
bus_speed = PCI_SPEED_66MHz;
break;
case 2:
bus_speed = PCI_SPEED_66MHz_PCIX;
break;
case 3:
bus_speed = PCI_SPEED_100MHz_PCIX;
break;
case 4:
bus_speed = PCI_SPEED_133MHz_PCIX;
break;
case 5:
bus_speed = PCI_SPEED_66MHz_PCIX_ECC;
break;
case 6:
bus_speed = PCI_SPEED_100MHz_PCIX_ECC;
break;
case 7:
bus_speed = PCI_SPEED_133MHz_PCIX_ECC;
break;
case 8:
bus_speed = PCI_SPEED_66MHz_PCIX_266;
break;
case 9:
bus_speed = PCI_SPEED_100MHz_PCIX_266;
break;
case 0xa:
bus_speed = PCI_SPEED_133MHz_PCIX_266;
break;
case 0xb:
bus_speed = PCI_SPEED_66MHz_PCIX_533;
break;
case 0xc:
bus_speed = PCI_SPEED_100MHz_PCIX_533;
break;
case 0xd:
bus_speed = PCI_SPEED_133MHz_PCIX_533;
break;
case 0xe:
case 0xf:
default:
bus_speed = PCI_SPEED_UNKNOWN;
break;
}
} else {
/* In the case where pi is undefined, default it to 1 */
switch (sec_bus_status & 0x0007) {
case 0:
bus_speed = PCI_SPEED_33MHz;
break;
case 1:
bus_speed = PCI_SPEED_66MHz;
break;
case 2:
bus_speed = PCI_SPEED_66MHz_PCIX;
break;
case 3:
bus_speed = PCI_SPEED_100MHz_PCIX;
break;
case 4:
bus_speed = PCI_SPEED_133MHz_PCIX;
break;
case 5:
bus_speed = PCI_SPEED_UNKNOWN; /* Reserved */
break;
case 6:
bus_speed = PCI_SPEED_UNKNOWN; /* Reserved */
break;
case 7:
bus_speed = PCI_SPEED_UNKNOWN; /* Reserved */
break;
default:
bus_speed = PCI_SPEED_UNKNOWN;
break;
}
}
*value = bus_speed;
dbg("Current bus speed = %d\n", bus_speed);
DBG_LEAVE_ROUTINE
return retval;
}
static struct hpc_ops shpchp_hpc_ops = {
.power_on_slot = hpc_power_on_slot,
.slot_enable = hpc_slot_enable,
.slot_disable = hpc_slot_disable,
.enable_all_slots = hpc_enable_all_slots,
.pwr_on_all_slots = hpc_pwr_on_all_slots,
.set_bus_speed_mode = hpc_set_bus_speed_mode,
.set_attention_status = hpc_set_attention_status,
.get_power_status = hpc_get_power_status,
.get_attention_status = hpc_get_attention_status,
.get_latch_status = hpc_get_latch_status,
.get_adapter_status = hpc_get_adapter_status,
.get_max_bus_speed = hpc_get_max_bus_speed,
.get_cur_bus_speed = hpc_get_cur_bus_speed,
.get_adapter_speed = hpc_get_adapter_speed,
.get_mode1_ECC_cap = hpc_get_mode1_ECC_cap,
.get_prog_int = hpc_get_prog_int,
.query_power_fault = hpc_query_power_fault,
.green_led_on = hpc_set_green_led_on,
.green_led_off = hpc_set_green_led_off,
.green_led_blink = hpc_set_green_led_blink,
.release_ctlr = hpc_release_ctlr,
.check_cmd_status = hpc_check_cmd_status,
};
int shpc_init(struct controller * ctrl,
struct pci_dev * pdev,
php_intr_callback_t attention_button_callback,
php_intr_callback_t switch_change_callback,
php_intr_callback_t presence_change_callback,
php_intr_callback_t power_fault_callback)
{
struct php_ctlr_state_s *php_ctlr, *p;
void *instance_id = ctrl;
int rc;
u8 hp_slot;
static int first = 1;
u32 shpc_cap_offset, shpc_base_offset;
u32 tempdword, slot_reg;
u16 vendor_id, device_id;
u8 i;
DBG_ENTER_ROUTINE
spin_lock_init(&list_lock);
php_ctlr = (struct php_ctlr_state_s *) kmalloc(sizeof(struct php_ctlr_state_s), GFP_KERNEL);
if (!php_ctlr) { /* allocate controller state data */
err("%s: HPC controller memory allocation error!\n", __FUNCTION__);
goto abort;
}
memset(php_ctlr, 0, sizeof(struct php_ctlr_state_s));
php_ctlr->pci_dev = pdev; /* save pci_dev in context */
rc = pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor_id);
dbg("%s: Vendor ID: %x\n",__FUNCTION__, vendor_id);
if (rc) {
err("%s: unable to read PCI configuration data\n", __FUNCTION__);
goto abort_free_ctlr;
}
rc = pci_read_config_word(pdev, PCI_DEVICE_ID, &device_id);
dbg("%s: Device ID: %x\n",__FUNCTION__, device_id);
if (rc) {
err("%s: unable to read PCI configuration data\n", __FUNCTION__);
goto abort_free_ctlr;
}
if ((vendor_id == PCI_VENDOR_ID_AMD) || (device_id == PCI_DEVICE_ID_AMD_GOLAM_7450)) {
shpc_base_offset = 0; /* amd shpc driver doesn't use this; assume 0 */
} else {
if ((shpc_cap_offset = pci_find_capability(pdev, PCI_CAP_ID_SHPC)) == 0) {
err("%s : shpc_cap_offset == 0\n", __FUNCTION__);
goto abort_free_ctlr;
}
rc = pci_write_config_byte(pdev, (u8)shpc_cap_offset + DWORD_SELECT , BASE_OFFSET);
if (rc) {
err("%s : pci_word_config_byte failed\n", __FUNCTION__);
goto abort_free_ctlr;
}
rc = pci_read_config_dword(pdev, (u8)shpc_cap_offset + DWORD_DATA, &shpc_base_offset);
if (rc) {
err("%s : pci_read_config_dword failed\n", __FUNCTION__);
goto abort_free_ctlr;
}
for (i = 0; i <= 14; i++) {
rc = pci_write_config_byte(pdev, (u8)shpc_cap_offset + DWORD_SELECT , i);
if (rc) {
err("%s : pci_word_config_byte failed\n", __FUNCTION__);
goto abort_free_ctlr;
}
rc = pci_read_config_dword(pdev, (u8)shpc_cap_offset + DWORD_DATA, &tempdword);
if (rc) {
err("%s : pci_read_config_dword failed\n", __FUNCTION__);
goto abort_free_ctlr;
}
dbg("%s: offset %d: tempdword %x\n", __FUNCTION__,i, tempdword);
}
}
if (first) {
spin_lock_init(&hpc_event_lock);
first = 0;
}
dbg("pdev = %p: b:d:f:irq=0x%x:%x:%x:%x\n", pdev, pdev->bus->number, PCI_SLOT(pdev->devfn),
PCI_FUNC(pdev->devfn), pdev->irq);
for ( rc = 0; rc < DEVICE_COUNT_RESOURCE; rc++)
if (pci_resource_len(pdev, rc) > 0)
dbg("pci resource[%d] start=0x%lx(len=0x%lx), shpc_base_offset %x\n", rc,
pci_resource_start(pdev, rc), pci_resource_len(pdev, rc), shpc_base_offset);
info("HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n", pdev->vendor, pdev->device, pdev->subsystem_vendor,
pdev->subsystem_device);
if (!request_mem_region(pci_resource_start(pdev, 0) + shpc_base_offset, pci_resource_len(pdev, 0), MY_NAME)) {
err("%s: cannot reserve MMIO region\n", __FUNCTION__);
goto abort_free_ctlr;
}
php_ctlr->creg = (struct ctrl_reg *)
ioremap(pci_resource_start(pdev, 0) + shpc_base_offset, pci_resource_len(pdev, 0));
if (!php_ctlr->creg) {
err("%s: cannot remap MMIO region %lx @ %lx\n", __FUNCTION__, pci_resource_len(pdev, 0),
pci_resource_start(pdev, 0) + shpc_base_offset);
release_mem_region(pci_resource_start(pdev, 0) + shpc_base_offset, pci_resource_len(pdev, 0));
goto abort_free_ctlr;
}
dbg("%s: php_ctlr->creg %p\n", __FUNCTION__, php_ctlr->creg);
dbg("%s: physical addr %p\n", __FUNCTION__, (void*)pci_resource_start(pdev, 0));
init_MUTEX(&ctrl->crit_sect);
/* Setup wait queue */
init_waitqueue_head(&ctrl->queue);
/* Find the IRQ */
php_ctlr->irq = pdev->irq;
dbg("HPC interrupt = %d\n", php_ctlr->irq);
/* Save interrupt callback info */
php_ctlr->attention_button_callback = attention_button_callback;
php_ctlr->switch_change_callback = switch_change_callback;
php_ctlr->presence_change_callback = presence_change_callback;
php_ctlr->power_fault_callback = power_fault_callback;
php_ctlr->callback_instance_id = instance_id;
/* Return PCI Controller Info */
php_ctlr->slot_device_offset = (readl(php_ctlr->creg + SLOT_CONFIG) & FIRST_DEV_NUM ) >> 8;
php_ctlr->num_slots = readl(php_ctlr->creg + SLOT_CONFIG) & SLOT_NUM;
dbg("%s: slot_device_offset %x\n", __FUNCTION__, php_ctlr->slot_device_offset);
dbg("%s: num_slots %x\n", __FUNCTION__, php_ctlr->num_slots);
/* Mask Global Interrupt Mask & Command Complete Interrupt Mask */
tempdword = readl(php_ctlr->creg + SERR_INTR_ENABLE);
dbg("%s: SERR_INTR_ENABLE = %x\n", __FUNCTION__, tempdword);
tempdword = 0x0003000f;
writel(tempdword, php_ctlr->creg + SERR_INTR_ENABLE);
tempdword = readl(php_ctlr->creg + SERR_INTR_ENABLE);
dbg("%s: SERR_INTR_ENABLE = %x\n", __FUNCTION__, tempdword);
/* Mask the MRL sensor SERR Mask of individual slot in
* Slot SERR-INT Mask & clear all the existing event if any
*/
for (hp_slot = 0; hp_slot < php_ctlr->num_slots; hp_slot++) {
slot_reg = readl(php_ctlr->creg + SLOT1 + 4*hp_slot );
dbg("%s: Default Logical Slot Register %d value %x\n", __FUNCTION__,
hp_slot, slot_reg);
tempdword = 0xffffffff;
writel(tempdword, php_ctlr->creg + SLOT1 + (4*hp_slot));
}
if (shpchp_poll_mode) {/* Install interrupt polling code */
/* Install and start the interrupt polling timer */
init_timer(&php_ctlr->int_poll_timer);
start_int_poll_timer( php_ctlr, 10 ); /* start with 10 second delay */
} else {
/* Installs the interrupt handler */
#ifdef CONFIG_PCI_USE_VECTOR
rc = pci_enable_msi(pdev);
if (rc) {
err("Can't get msi for the hotplug controller\n");
dbg("%s: rc = %x\n", __FUNCTION__, rc);
goto abort_free_ctlr;
}
php_ctlr->irq = pdev->irq;
#endif
rc = request_irq(php_ctlr->irq, shpc_isr, SA_SHIRQ, MY_NAME, (void *) ctrl);
dbg("%s: request_irq %d for hpc%d (returns %d)\n", __FUNCTION__, php_ctlr->irq, ctlr_seq_num, rc);
if (rc) {
err("Can't get irq %d for the hotplug controller\n", php_ctlr->irq);
goto abort_free_ctlr;
}
/* Execute OSHP method here */
}
dbg("%s: Before adding HPC to HPC list\n", __FUNCTION__);
/* Add this HPC instance into the HPC list */
spin_lock(&list_lock);
if (php_ctlr_list_head == 0) {
php_ctlr_list_head = php_ctlr;
p = php_ctlr_list_head;
p->pnext = 0;
} else {
p = php_ctlr_list_head;
while (p->pnext)
p = p->pnext;
p->pnext = php_ctlr;
}
spin_unlock(&list_lock);
ctlr_seq_num++;
ctrl->hpc_ctlr_handle = php_ctlr;
ctrl->hpc_ops = &shpchp_hpc_ops;
for (hp_slot = 0; hp_slot < php_ctlr->num_slots; hp_slot++) {
slot_reg = readl(php_ctlr->creg + SLOT1 + 4*hp_slot );
dbg("%s: Default Logical Slot Register %d value %x\n", __FUNCTION__,
hp_slot, slot_reg);
tempdword = 0xe01fffff;
writel(tempdword, php_ctlr->creg + SLOT1 + (4*hp_slot));
}
if (!shpchp_poll_mode) {
/* Unmask all general input interrupts and SERR */
tempdword = readl(php_ctlr->creg + SERR_INTR_ENABLE);
tempdword = 0x0000000a;
writel(tempdword, php_ctlr->creg + SERR_INTR_ENABLE);
tempdword = readl(php_ctlr->creg + SERR_INTR_ENABLE);
dbg("%s: SERR_INTR_ENABLE = %x\n", __FUNCTION__, tempdword);
}
dbg("%s: Leaving shpc_init\n", __FUNCTION__);
DBG_LEAVE_ROUTINE
return 0;
/* We end up here for the many possible ways to fail this API. */
abort_free_ctlr:
kfree(php_ctlr);
abort:
DBG_LEAVE_ROUTINE
return -1;
}
/*
* Standard 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.
* Copyright (C) 2003-2004 Intel Corporation
*
* 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>, <dely.l.sy@intel.com>
*
*/
#include <linux/config.h>
#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 "../pci.h"
#include "shpchp.h"
#ifndef CONFIG_IA64
#include "../../../arch/i386/pci/pci.h" /* horrible hack showing how processor dependant we are... */
#endif
int shpchp_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));
/* Still NULL ? Well then scan for it ! */
if (func->pci_dev == NULL) {
num = pci_scan_slot(ctrl->pci_dev->subordinate, PCI_DEVFN(func->device, func->function));
if (num)
dbg("%s: subordiante %p number %x\n", __FUNCTION__, ctrl->pci_dev->subordinate,
ctrl->pci_dev->subordinate->number);
pci_bus_add_devices(ctrl->pci_dev->subordinate);
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 shpchp_unconfigure_device(struct pci_func* func)
{
int rc = 0;
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, (func->device << 3) | j);
if (temp) {
pci_remove_bus_device(temp);
}
}
return rc;
}
/*
* shpchp_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 shpchp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num)
{
#if !defined(CONFIG_X86_IO_APIC) && !defined(CONFIG_X86_64)
int rc;
u16 temp_word;
struct pci_dev fakedev;
struct pci_bus fakebus;
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);
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);
#endif
return 0;
}
/* More PCI configuration routines; this time centered around hotplug controller */
/*
* shpchp_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 shpchp_save_config(struct controller *ctrl, int busnumber, int num_ctlr_slots, int first_device_num)
{
int 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;
int is_hot_plug = num_ctlr_slots || first_device_num;
struct pci_bus lpci_bus, *pci_bus;
dbg("%s: num_ctlr_slots = %d, first_device_num = %d\n", __FUNCTION__, num_ctlr_slots, first_device_num);
memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus));
pci_bus = &lpci_bus;
dbg("%s: num_ctlr_slots = %d, first_device_num = %d\n", __FUNCTION__, num_ctlr_slots, first_device_num);
/* Decide which slots are supported */
if (is_hot_plug) {
/*********************************
* is_hot_plug is the slot mask
*********************************/
FirstSupported = first_device_num;
LastSupported = FirstSupported + num_ctlr_slots - 1;
} else {
FirstSupported = 0;
LastSupported = 0x1F;
}
dbg("FirstSupported = %d, LastSupported = %d\n", FirstSupported, LastSupported);
/* Save PCI configuration space for all devices in supported slots */
pci_bus->number = busnumber;
for (device = FirstSupported; device <= LastSupported; device++) {
ID = 0xFFFFFFFF;
rc = pci_bus_read_config_dword(pci_bus, PCI_DEVFN(device, 0), PCI_VENDOR_ID, &ID);
if (ID != 0xFFFFFFFF) { /* device in slot */
rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, 0), 0x0B, &class_code);
if (rc)
return rc;
rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, 0), PCI_HEADER_TYPE, &header_type);
if (rc)
return rc;
dbg("class_code = %x, header_type = %x\n", class_code, header_type);
/* 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(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 = shpchp_save_config(ctrl, sub_bus, 0, 0);
if (rc)
return rc;
}
}
index = 0;
new_slot = shpchp_slot_find(busnumber, device, index++);
dbg("new_slot = %p\n", new_slot);
while (new_slot && (new_slot->function != (u8) function)) {
new_slot = shpchp_slot_find(busnumber, device, index++);
dbg("new_slot = %p\n", new_slot);
}
if (!new_slot) {
/* Setup slot structure. */
new_slot = shpchp_slot_create(busnumber);
dbg("new_slot = %p\n", new_slot);
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);
dbg("new_slot->pci_dev = %p\n", new_slot->pci_dev);
for (cloop = 0; cloop < 0x20; cloop++) {
rc = pci_bus_read_config_dword(pci_bus, PCI_DEVFN(device, function),
cloop << 2, (u32 *) & (new_slot->config_space [cloop]));
/* dbg("new_slot->config_space[%x] = %x\n", cloop, 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(pci_bus, PCI_DEVFN(device, function), PCI_VENDOR_ID, &ID);
if (ID == 0xFFFFFFFF) { /* nothing there. */
function++;
dbg("Nothing there\n");
} else { /* Something there */
rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, function),
0x0B, &class_code);
if (rc)
return rc;
rc = pci_bus_read_config_byte(pci_bus, PCI_DEVFN(device, function),
PCI_HEADER_TYPE, &header_type);
if (rc)
return rc;
dbg("class_code = %x, header_type = %x\n", class_code, header_type);
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 = shpchp_slot_create(busnumber);
if (new_slot == NULL) {
return(1);
}
dbg("new_slot = %p\n", new_slot);
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);
}
/*
* shpchp_save_slot_config
*
* Saves configuration info for all PCI devices in a given slot
* including subordinate busses.
*
* returns 0 if success
*/
int shpchp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot)
{
int 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;
struct pci_bus lpci_bus, *pci_bus;
memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus));
pci_bus = &lpci_bus;
pci_bus->number = new_slot->bus;
ID = 0xFFFFFFFF;
pci_bus_read_config_dword(pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_VENDOR_ID, &ID);
if (ID != 0xFFFFFFFF) { /* device in slot */
pci_bus_read_config_byte(pci_bus, PCI_DEVFN(new_slot->device, 0), 0x0B, &class_code);
pci_bus_read_config_byte(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(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 = shpchp_save_config(ctrl, sub_bus, 0, 0);
if (rc)
return(rc);
} /* End of IF */
new_slot->status = 0;
for (cloop = 0; cloop < 0x20; cloop++) {
pci_bus_read_config_dword(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(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(pci_bus, PCI_DEVFN(new_slot->device, function), 0x0B, &class_code);
pci_bus_read_config_byte(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);
}
/*
* shpchp_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
* if disable == 1(DISABLE_CARD),
* it loops for all functions of the slot and disables them.
* else, it just get resources of the function and return.
*/
int shpchp_save_used_resources (struct controller *ctrl, struct pci_func *func, int disable)
{
u8 cloop;
u8 header_type;
u8 secondary_bus;
u8 temp_byte;
u16 command;
u16 save_command;
u16 w_base, w_length;
u32 temp_register;
u32 save_base;
u32 base, length;
u64 base64 = 0;
int index = 0;
unsigned int devfn;
struct pci_resource *mem_node = NULL;
struct pci_resource *p_mem_node = NULL;
struct pci_resource *t_mem_node;
struct pci_resource *io_node;
struct pci_resource *bus_node;
struct pci_bus lpci_bus, *pci_bus;
memcpy(&lpci_bus, ctrl->pci_dev->subordinate, sizeof(lpci_bus));
pci_bus = &lpci_bus;
if (disable)
func = shpchp_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);
if (disable) {
/* 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 */
dbg("Save_used_res of PCI bridge b:d=0x%x:%x, sc=0x%x\n", func->bus, func->device, save_command);
if (disable) {
/* 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 =(struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!bus_node)
return -ENOMEM;
bus_node->base = (ulong)secondary_bus;
bus_node->length = (ulong)(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, &temp_byte);
base = temp_byte;
pci_bus_read_config_byte (pci_bus, devfn, PCI_IO_LIMIT, &temp_byte);
length = temp_byte;
if ((base <= length) && (!disable || (save_command & PCI_COMMAND_IO))) {
io_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!io_node)
return -ENOMEM;
io_node->base = (ulong)(base & PCI_IO_RANGE_MASK) << 8;
io_node->length = (ulong)(length - 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) && (!disable || (save_command & PCI_COMMAND_MEMORY))) {
mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!mem_node)
return -ENOMEM;
mem_node->base = (ulong)w_base << 16;
mem_node->length = (ulong)(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) && (!disable || (save_command & PCI_COMMAND_MEMORY))) {
p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!p_mem_node)
return -ENOMEM;
p_mem_node->base = (ulong)w_base << 16;
p_mem_node->length = (ulong)(w_length - w_base + 0x10) << 16;
p_mem_node->next = func->p_mem_head;
func->p_mem_head = p_mem_node;
}
} else if ((header_type & 0x7F) == PCI_HEADER_TYPE_NORMAL) {
dbg("Save_used_res of PCI adapter b:d=0x%x:%x, sc=0x%x\n", func->bus, func->device, save_command);
/* Figure out IO and memory base lengths */
for (cloop = PCI_BASE_ADDRESS_0; cloop <= PCI_BASE_ADDRESS_5; 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, &temp_register);
if (!disable) {
pci_bus_write_config_dword (pci_bus, devfn, cloop, save_base);
}
if (!temp_register)
continue;
base = temp_register;
if ((base & PCI_BASE_ADDRESS_SPACE_IO) && (!disable || (save_command & PCI_COMMAND_IO))) {
/* IO base */
/* set temp_register = amount of IO space requested */
base = base & 0xFFFFFFFCL;
base = (~base) + 1;
io_node = (struct pci_resource *) kmalloc(sizeof (struct pci_resource), GFP_KERNEL);
if (!io_node)
return -ENOMEM;
io_node->base = (ulong)save_base & PCI_BASE_ADDRESS_IO_MASK;
io_node->length = (ulong)base;
dbg("sur adapter: IO bar=0x%x(length=0x%x)\n", io_node->base, io_node->length);
io_node->next = func->io_head;
func->io_head = io_node;
} else { /* map Memory */
int prefetchable = 1;
/* struct pci_resources **res_node; */
char *res_type_str = "PMEM";
u32 temp_register2;
t_mem_node = (struct pci_resource *) kmalloc(sizeof (struct pci_resource), GFP_KERNEL);
if (!t_mem_node)
return -ENOMEM;
if (!(base & PCI_BASE_ADDRESS_MEM_PREFETCH) && (!disable || (save_command & PCI_COMMAND_MEMORY))) {
prefetchable = 0;
mem_node = t_mem_node;
res_type_str++;
} else
p_mem_node = t_mem_node;
base = base & 0xFFFFFFF0L;
base = (~base) + 1;
switch (temp_register & PCI_BASE_ADDRESS_MEM_TYPE_MASK) {
case PCI_BASE_ADDRESS_MEM_TYPE_32:
if (prefetchable) {
p_mem_node->base = (ulong)save_base & PCI_BASE_ADDRESS_MEM_MASK;
p_mem_node->length = (ulong)base;
dbg("sur adapter: 32 %s bar=0x%x(length=0x%x)\n", res_type_str,
p_mem_node->base, p_mem_node->length);
p_mem_node->next = func->p_mem_head;
func->p_mem_head = p_mem_node;
} else {
mem_node->base = (ulong)save_base & PCI_BASE_ADDRESS_MEM_MASK;
mem_node->length = (ulong)base;
dbg("sur adapter: 32 %s bar=0x%x(length=0x%x)\n", res_type_str,
mem_node->base, mem_node->length);
mem_node->next = func->mem_head;
func->mem_head = mem_node;
}
break;
case PCI_BASE_ADDRESS_MEM_TYPE_64:
pci_bus_read_config_dword(pci_bus, devfn, cloop+4, &temp_register2);
base64 = temp_register2;
base64 = (base64 << 32) | save_base;
if (temp_register2) {
dbg("sur adapter: 64 %s high dword of base64(0x%x:%x) masked to 0\n",
res_type_str, temp_register2, (u32)base64);
base64 &= 0x00000000FFFFFFFFL;
}
if (prefetchable) {
p_mem_node->base = base64 & PCI_BASE_ADDRESS_MEM_MASK;
p_mem_node->length = base;
dbg("sur adapter: 64 %s base=0x%x(len=0x%x)\n", res_type_str,
p_mem_node->base, p_mem_node->length);
p_mem_node->next = func->p_mem_head;
func->p_mem_head = p_mem_node;
} else {
mem_node->base = base64 & PCI_BASE_ADDRESS_MEM_MASK;
mem_node->length = base;
dbg("sur adapter: 64 %s base=0x%x(len=0x%x)\n", res_type_str,
mem_node->base, mem_node->length);
mem_node->next = func->mem_head;
func->mem_head = mem_node;
}
cloop += 4;
break;
default:
dbg("asur: reserved BAR type=0x%x\n", temp_register);
break;
}
}
} /* End of base register loop */
} else { /* Some other unknown header type */
dbg("Save_used_res of PCI unknown type b:d=0x%x:%x. skip.\n", func->bus, func->device);
}
/* find the next device in this slot */
if (!disable)
break;
func = shpchp_slot_find(func->bus, func->device, index++);
}
return(0);
}
/*
* shpchp_return_board_resources
*
* this routine returns all resources allocated to a board to
* the available pool.
*
* returns 0 if success
*/
int shpchp_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 |= shpchp_resource_sort_and_combine(&(resources->mem_head));
rc |= shpchp_resource_sort_and_combine(&(resources->p_mem_head));
rc |= shpchp_resource_sort_and_combine(&(resources->io_head));
rc |= shpchp_resource_sort_and_combine(&(resources->bus_head));
return(rc);
}
/*
* shpchp_destroy_resource_list
*
* Puts node back in the resource list pointed to by head
*/
void shpchp_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);
}
}
/*
* shpchp_destroy_board_resources
*
* Puts node back in the resource list pointed to by head
*/
void shpchp_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);
}
}
/*
* Compaq Hot Plug Controller Driver
*
* Copyright (c) 1995,2001 Compaq Computer Corporation
* Copyright (c) 2001,2003 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/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/workqueue.h>
#include <linux/pci.h>
#include "shpchp.h"
/* A few routines that create sysfs entries for the hot plug controller */
static int show_ctrl (struct device *dev, char *buf)
{
struct pci_dev *pci_dev;
struct controller *ctrl;
char * out = buf;
int index;
struct pci_resource *res;
pci_dev = container_of (dev, struct pci_dev, dev);
ctrl = pci_get_drvdata(pci_dev);
out += sprintf(buf, "Free resources: memory\n");
index = 11;
res = ctrl->mem_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
out += sprintf(out, "Free resources: prefetchable memory\n");
index = 11;
res = ctrl->p_mem_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
out += sprintf(out, "Free resources: IO\n");
index = 11;
res = ctrl->io_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
out += sprintf(out, "Free resources: bus numbers\n");
index = 11;
res = ctrl->bus_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
return out - buf;
}
static DEVICE_ATTR (ctrl, S_IRUGO, show_ctrl, NULL);
static int show_dev (struct device *dev, char *buf)
{
struct pci_dev *pci_dev;
struct controller *ctrl;
char * out = buf;
int index;
struct pci_resource *res;
struct pci_func *new_slot;
struct slot *slot;
pci_dev = container_of (dev, struct pci_dev, dev);
ctrl = pci_get_drvdata(pci_dev);
slot=ctrl->slot;
while (slot) {
new_slot = shpchp_slot_find(slot->bus, slot->device, 0);
if (!new_slot)
break;
out += sprintf(out, "assigned resources: memory\n");
index = 11;
res = new_slot->mem_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
out += sprintf(out, "assigned resources: prefetchable memory\n");
index = 11;
res = new_slot->p_mem_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
out += sprintf(out, "assigned resources: IO\n");
index = 11;
res = new_slot->io_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
out += sprintf(out, "assigned resources: bus numbers\n");
index = 11;
res = new_slot->bus_head;
while (res && index--) {
out += sprintf(out, "start = %8.8x, length = %8.8x\n", res->base, res->length);
res = res->next;
}
slot=slot->next;
}
return out - buf;
}
static DEVICE_ATTR (dev, S_IRUGO, show_dev, NULL);
void shpchp_create_ctrl_files (struct controller *ctrl)
{
device_create_file (&ctrl->pci_dev->dev, &dev_attr_ctrl);
device_create_file (&ctrl->pci_dev->dev, &dev_attr_dev);
}
/*
* SHPCHPRM : SHPCHP Resource Manager for ACPI/non-ACPI platform
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
* Copyright (C) 2003-2004 Intel Corporation
*
* 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>, <dely.l.sy@intel.com>
*
*/
#ifndef _SHPCHPRM_H_
#define _SHPCHPRM_H_
#ifdef CONFIG_HOTPLUG_PCI_SHPC_PHPRM_LEGACY
#include "shpchprm_legacy.h"
#else
#include "shpchprm_nonacpi.h"
#endif
int shpchprm_init(enum php_ctlr_type ct);
void shpchprm_cleanup(void);
int shpchprm_print_pirt(void);
void *shpchprm_get_slot(struct slot *slot);
int shpchprm_find_available_resources(struct controller *ctrl);
int shpchprm_set_hpp(struct controller *ctrl, struct pci_func *func, u8 card_type);
void shpchprm_enable_card(struct controller *ctrl, struct pci_func *func, u8 card_type);
int shpchprm_get_physical_slot_number(struct controller *ctrl, u32 *sun, u8 busnum, u8 devnum);
#ifdef DEBUG
#define RES_CHECK(this, bits) \
{ if (((this) & (bits - 1))) \
printk("%s:%d ERR: potential res loss!\n", __FUNCTION__, __LINE__); }
#else
#define RES_CHECK(this, bits)
#endif
#endif /* _SHPCHPRM_H_ */
/*
* SHPCHPRM ACPI: PHP Resource Manager for ACPI platform
*
* Copyright (C) 2003-2004 Intel Corporation
*
* 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 <dely.l.sy@intel.com>
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/acpi.h>
#include <linux/efi.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#ifdef CONFIG_IA64
#include <asm/iosapic.h>
#endif
#include <acpi/acpi.h>
#include <acpi/acpi_bus.h>
#include <acpi/actypes.h>
#include "shpchp.h"
#include "shpchprm.h"
#define PCI_MAX_BUS 0x100
#define ACPI_STA_DEVICE_PRESENT 0x01
#define METHOD_NAME__SUN "_SUN"
#define METHOD_NAME__HPP "_HPP"
#define METHOD_NAME_OSHP "OSHP"
#define PHP_RES_BUS 0xA0
#define PHP_RES_IO 0xA1
#define PHP_RES_MEM 0xA2
#define PHP_RES_PMEM 0xA3
#define BRIDGE_TYPE_P2P 0x00
#define BRIDGE_TYPE_HOST 0x01
/* this should go to drivers/acpi/include/ */
struct acpi__hpp {
u8 cache_line_size;
u8 latency_timer;
u8 enable_serr;
u8 enable_perr;
};
struct acpi_php_slot {
struct acpi_php_slot *next;
struct acpi_bridge *bridge;
acpi_handle handle;
int seg;
int bus;
int dev;
int fun;
u32 sun;
struct pci_resource *mem_head;
struct pci_resource *p_mem_head;
struct pci_resource *io_head;
struct pci_resource *bus_head;
void *slot_ops; /* _STA, _EJx, etc */
struct slot *slot;
}; /* per func */
struct acpi_bridge {
struct acpi_bridge *parent;
struct acpi_bridge *next;
struct acpi_bridge *child;
acpi_handle handle;
int seg;
int pbus; /* pdev->bus->number */
int pdevice; /* PCI_SLOT(pdev->devfn) */
int pfunction; /* PCI_DEVFN(pdev->devfn) */
int bus; /* pdev->subordinate->number */
struct acpi__hpp *_hpp;
struct acpi_php_slot *slots;
struct pci_resource *tmem_head; /* total from crs */
struct pci_resource *tp_mem_head; /* total from crs */
struct pci_resource *tio_head; /* total from crs */
struct pci_resource *tbus_head; /* total from crs */
struct pci_resource *mem_head; /* available */
struct pci_resource *p_mem_head; /* available */
struct pci_resource *io_head; /* available */
struct pci_resource *bus_head; /* available */
int scanned;
int type;
};
static struct acpi_bridge *acpi_bridges_head;
static u8 * acpi_path_name( acpi_handle handle)
{
acpi_status status;
static u8 path_name[ACPI_PATHNAME_MAX];
struct acpi_buffer ret_buf = { ACPI_PATHNAME_MAX, path_name };
memset(path_name, 0, sizeof (path_name));
status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &ret_buf);
if (ACPI_FAILURE(status))
return NULL;
else
return path_name;
}
static void acpi_get__hpp ( struct acpi_bridge *ab);
static void acpi_run_oshp ( struct acpi_bridge *ab);
static int acpi_add_slot_to_php_slots(
struct acpi_bridge *ab,
int bus_num,
acpi_handle handle,
u32 adr,
u32 sun
)
{
struct acpi_php_slot *aps;
static long samesun = -1;
aps = (struct acpi_php_slot *) kmalloc (sizeof(struct acpi_php_slot), GFP_KERNEL);
if (!aps) {
err ("acpi_shpchprm: alloc for aps fail\n");
return -1;
}
memset(aps, 0, sizeof(struct acpi_php_slot));
aps->handle = handle;
aps->bus = bus_num;
aps->dev = (adr >> 16) & 0xffff;
aps->fun = adr & 0xffff;
aps->sun = sun;
aps->next = ab->slots; /* cling to the bridge */
aps->bridge = ab;
ab->slots = aps;
ab->scanned += 1;
if (!ab->_hpp)
acpi_get__hpp(ab);
acpi_run_oshp(ab);
if (sun != samesun) {
info("acpi_shpchprm: Slot sun(%x) at s:b:d:f=0x%02x:%02x:%02x:%02x\n", aps->sun, ab->seg,
aps->bus, aps->dev, aps->fun);
samesun = sun;
}
return 0;
}
static void acpi_get__hpp ( struct acpi_bridge *ab)
{
acpi_status status;
u8 nui[4];
struct acpi_buffer ret_buf = { 0, NULL};
union acpi_object *ext_obj, *package;
u8 *path_name = acpi_path_name(ab->handle);
int i, len = 0;
/* get _hpp */
status = acpi_evaluate_object(ab->handle, METHOD_NAME__HPP, NULL, &ret_buf);
switch (status) {
case AE_BUFFER_OVERFLOW:
ret_buf.pointer = kmalloc (ret_buf.length, GFP_KERNEL);
if (!ret_buf.pointer) {
err ("acpi_shpchprm:%s alloc for _HPP fail\n", path_name);
return;
}
status = acpi_evaluate_object(ab->handle, METHOD_NAME__HPP, NULL, &ret_buf);
if (ACPI_SUCCESS(status))
break;
default:
if (ACPI_FAILURE(status)) {
err("acpi_shpchprm:%s _HPP fail=0x%x\n", path_name, status);
return;
}
}
ext_obj = (union acpi_object *) ret_buf.pointer;
if (ext_obj->type != ACPI_TYPE_PACKAGE) {
err ("acpi_shpchprm:%s _HPP obj not a package\n", path_name);
goto free_and_return;
}
len = ext_obj->package.count;
package = (union acpi_object *) ret_buf.pointer;
for ( i = 0; (i < len) || (i < 4); i++) {
ext_obj = (union acpi_object *) &package->package.elements[i];
switch (ext_obj->type) {
case ACPI_TYPE_INTEGER:
nui[i] = (u8)ext_obj->integer.value;
break;
default:
err ("acpi_shpchprm:%s _HPP obj type incorrect\n", path_name);
goto free_and_return;
}
}
ab->_hpp = kmalloc (sizeof (struct acpi__hpp), GFP_KERNEL);
memset(ab->_hpp, 0, sizeof(struct acpi__hpp));
ab->_hpp->cache_line_size = nui[0];
ab->_hpp->latency_timer = nui[1];
ab->_hpp->enable_serr = nui[2];
ab->_hpp->enable_perr = nui[3];
dbg(" _HPP: cache_line_size=0x%x\n", ab->_hpp->cache_line_size);
dbg(" _HPP: latency timer =0x%x\n", ab->_hpp->latency_timer);
dbg(" _HPP: enable SERR =0x%x\n", ab->_hpp->enable_serr);
dbg(" _HPP: enable PERR =0x%x\n", ab->_hpp->enable_perr);
free_and_return:
kfree(ret_buf.pointer);
}
static void acpi_run_oshp ( struct acpi_bridge *ab)
{
acpi_status status;
u8 *path_name = acpi_path_name(ab->handle);
struct acpi_buffer ret_buf = { 0, NULL};
/* run OSHP */
status = acpi_evaluate_object(ab->handle, METHOD_NAME_OSHP, NULL, &ret_buf);
if (ACPI_FAILURE(status)) {
err("acpi_pciehprm:%s OSHP fails=0x%x\n", path_name, status);
} else
dbg("acpi_pciehprm:%s OSHP passes =0x%x\n", path_name, status);
return;
}
static acpi_status acpi_evaluate_crs(
acpi_handle handle,
struct acpi_resource **retbuf
)
{
acpi_status status;
struct acpi_buffer crsbuf;
u8 *path_name = acpi_path_name(handle);
crsbuf.length = 0;
crsbuf.pointer = NULL;
status = acpi_get_current_resources (handle, &crsbuf);
switch (status) {
case AE_BUFFER_OVERFLOW:
break; /* found */
case AE_NOT_FOUND:
dbg("acpi_shpchprm:%s _CRS not found\n", path_name);
return status;
default:
err ("acpi_shpchprm:%s _CRS fail=0x%x\n", path_name, status);
return status;
}
crsbuf.pointer = kmalloc (crsbuf.length, GFP_KERNEL);
if (!crsbuf.pointer) {
err ("acpi_shpchprm: alloc %ld bytes for %s _CRS fail\n", (ulong)crsbuf.length, path_name);
return AE_NO_MEMORY;
}
status = acpi_get_current_resources (handle, &crsbuf);
if (ACPI_FAILURE(status)) {
err("acpi_shpchprm: %s _CRS fail=0x%x.\n", path_name, status);
kfree(crsbuf.pointer);
return status;
}
*retbuf = crsbuf.pointer;
return status;
}
static void free_pci_resource ( struct pci_resource *aprh)
{
struct pci_resource *res, *next;
for (res = aprh; res; res = next) {
next = res->next;
kfree(res);
}
}
static void print_pci_resource ( struct pci_resource *aprh)
{
struct pci_resource *res;
for (res = aprh; res; res = res->next)
dbg(" base= 0x%x length= 0x%x\n", res->base, res->length);
}
static void print_slot_resources( struct acpi_php_slot *aps)
{
if (aps->bus_head) {
dbg(" BUS Resources:\n");
print_pci_resource (aps->bus_head);
}
if (aps->io_head) {
dbg(" IO Resources:\n");
print_pci_resource (aps->io_head);
}
if (aps->mem_head) {
dbg(" MEM Resources:\n");
print_pci_resource (aps->mem_head);
}
if (aps->p_mem_head) {
dbg(" PMEM Resources:\n");
print_pci_resource (aps->p_mem_head);
}
}
static void print_pci_resources( struct acpi_bridge *ab)
{
if (ab->tbus_head) {
dbg(" Total BUS Resources:\n");
print_pci_resource (ab->tbus_head);
}
if (ab->bus_head) {
dbg(" BUS Resources:\n");
print_pci_resource (ab->bus_head);
}
if (ab->tio_head) {
dbg(" Total IO Resources:\n");
print_pci_resource (ab->tio_head);
}
if (ab->io_head) {
dbg(" IO Resources:\n");
print_pci_resource (ab->io_head);
}
if (ab->tmem_head) {
dbg(" Total MEM Resources:\n");
print_pci_resource (ab->tmem_head);
}
if (ab->mem_head) {
dbg(" MEM Resources:\n");
print_pci_resource (ab->mem_head);
}
if (ab->tp_mem_head) {
dbg(" Total PMEM Resources:\n");
print_pci_resource (ab->tp_mem_head);
}
if (ab->p_mem_head) {
dbg(" PMEM Resources:\n");
print_pci_resource (ab->p_mem_head);
}
if (ab->_hpp) {
dbg(" _HPP: cache_line_size=0x%x\n", ab->_hpp->cache_line_size);
dbg(" _HPP: latency timer =0x%x\n", ab->_hpp->latency_timer);
dbg(" _HPP: enable SERR =0x%x\n", ab->_hpp->enable_serr);
dbg(" _HPP: enable PERR =0x%x\n", ab->_hpp->enable_perr);
}
}
static int shpchprm_delete_resource(
struct pci_resource **aprh,
ulong base,
ulong size)
{
struct pci_resource *res;
struct pci_resource *prevnode;
struct pci_resource *split_node;
ulong tbase;
shpchp_resource_sort_and_combine(aprh);
for (res = *aprh; res; res = res->next) {
if (res->base > base)
continue;
if ((res->base + res->length) < (base + size))
continue;
if (res->base < base) {
tbase = base;
if ((res->length - (tbase - res->base)) < size)
continue;
split_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return -ENOMEM;
split_node->base = res->base;
split_node->length = tbase - res->base;
res->base = tbase;
res->length -= split_node->length;
split_node->next = res->next;
res->next = split_node;
}
if (res->length >= size) {
split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return -ENOMEM;
split_node->base = res->base + size;
split_node->length = res->length - size;
res->length = size;
split_node->next = res->next;
res->next = split_node;
}
if (*aprh == res) {
*aprh = res->next;
} else {
prevnode = *aprh;
while (prevnode->next != res)
prevnode = prevnode->next;
prevnode->next = res->next;
}
res->next = NULL;
kfree(res);
break;
}
return 0;
}
static int shpchprm_delete_resources(
struct pci_resource **aprh,
struct pci_resource *this
)
{
struct pci_resource *res;
for (res = this; res; res = res->next)
shpchprm_delete_resource(aprh, res->base, res->length);
return 0;
}
static int shpchprm_add_resource(
struct pci_resource **aprh,
ulong base,
ulong size)
{
struct pci_resource *res;
for (res = *aprh; res; res = res->next) {
if ((res->base + res->length) == base) {
res->length += size;
size = 0L;
break;
}
if (res->next == *aprh)
break;
}
if (size) {
res = kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!res) {
err ("acpi_shpchprm: alloc for res fail\n");
return -ENOMEM;
}
memset(res, 0, sizeof (struct pci_resource));
res->base = base;
res->length = size;
res->next = *aprh;
*aprh = res;
}
return 0;
}
static int shpchprm_add_resources(
struct pci_resource **aprh,
struct pci_resource *this
)
{
struct pci_resource *res;
int rc = 0;
for (res = this; res && !rc; res = res->next)
rc = shpchprm_add_resource(aprh, res->base, res->length);
return rc;
}
static void acpi_parse_io (
struct acpi_bridge *ab,
union acpi_resource_data *data
)
{
struct acpi_resource_io *dataio;
dataio = (struct acpi_resource_io *) data;
dbg("Io Resource\n");
dbg(" %d bit decode\n", ACPI_DECODE_16 == dataio->io_decode ? 16:10);
dbg(" Range minimum base: %08X\n", dataio->min_base_address);
dbg(" Range maximum base: %08X\n", dataio->max_base_address);
dbg(" Alignment: %08X\n", dataio->alignment);
dbg(" Range Length: %08X\n", dataio->range_length);
}
static void acpi_parse_fixed_io (
struct acpi_bridge *ab,
union acpi_resource_data *data
)
{
struct acpi_resource_fixed_io *datafio;
datafio = (struct acpi_resource_fixed_io *) data;
dbg("Fixed Io Resource\n");
dbg(" Range base address: %08X", datafio->base_address);
dbg(" Range length: %08X", datafio->range_length);
}
static void acpi_parse_address16_32 (
struct acpi_bridge *ab,
union acpi_resource_data *data,
acpi_resource_type id
)
{
/*
* acpi_resource_address16 == acpi_resource_address32
* acpi_resource_address16 *data16 = (acpi_resource_address16 *) data;
*/
struct acpi_resource_address32 *data32 = (struct acpi_resource_address32 *) data;
struct pci_resource **aprh, **tprh;
if (id == ACPI_RSTYPE_ADDRESS16)
dbg("acpi_shpchprm:16-Bit Address Space Resource\n");
else
dbg("acpi_shpchprm:32-Bit Address Space Resource\n");
switch (data32->resource_type) {
case ACPI_MEMORY_RANGE:
dbg(" Resource Type: Memory Range\n");
aprh = &ab->mem_head;
tprh = &ab->tmem_head;
switch (data32->attribute.memory.cache_attribute) {
case ACPI_NON_CACHEABLE_MEMORY:
dbg(" Type Specific: Noncacheable memory\n");
break;
case ACPI_CACHABLE_MEMORY:
dbg(" Type Specific: Cacheable memory\n");
break;
case ACPI_WRITE_COMBINING_MEMORY:
dbg(" Type Specific: Write-combining memory\n");
break;
case ACPI_PREFETCHABLE_MEMORY:
aprh = &ab->p_mem_head;
dbg(" Type Specific: Prefetchable memory\n");
break;
default:
dbg(" Type Specific: Invalid cache attribute\n");
break;
}
dbg(" Type Specific: Read%s\n", ACPI_READ_WRITE_MEMORY == data32->attribute.memory.read_write_attribute ? "/Write":" Only");
break;
case ACPI_IO_RANGE:
dbg(" Resource Type: I/O Range\n");
aprh = &ab->io_head;
tprh = &ab->tio_head;
switch (data32->attribute.io.range_attribute) {
case ACPI_NON_ISA_ONLY_RANGES:
dbg(" Type Specific: Non-ISA Io Addresses\n");
break;
case ACPI_ISA_ONLY_RANGES:
dbg(" Type Specific: ISA Io Addresses\n");
break;
case ACPI_ENTIRE_RANGE:
dbg(" Type Specific: ISA and non-ISA Io Addresses\n");
break;
default:
dbg(" Type Specific: Invalid range attribute\n");
break;
}
break;
case ACPI_BUS_NUMBER_RANGE:
dbg(" Resource Type: Bus Number Range(fixed)\n");
/* fixup to be compatible with the rest of php driver */
data32->min_address_range++;
data32->address_length--;
aprh = &ab->bus_head;
tprh = &ab->tbus_head;
break;
default:
dbg(" Resource Type: Invalid resource type. Exiting.\n");
return;
}
dbg(" Resource %s\n", ACPI_CONSUMER == data32->producer_consumer ? "Consumer":"Producer");
dbg(" %s decode\n", ACPI_SUB_DECODE == data32->decode ? "Subtractive":"Positive");
dbg(" Min address is %s fixed\n", ACPI_ADDRESS_FIXED == data32->min_address_fixed ? "":"not");
dbg(" Max address is %s fixed\n", ACPI_ADDRESS_FIXED == data32->max_address_fixed ? "":"not");
dbg(" Granularity: %08X\n", data32->granularity);
dbg(" Address range min: %08X\n", data32->min_address_range);
dbg(" Address range max: %08X\n", data32->max_address_range);
dbg(" Address translation offset: %08X\n", data32->address_translation_offset);
dbg(" Address Length: %08X\n", data32->address_length);
if (0xFF != data32->resource_source.index) {
dbg(" Resource Source Index: %X\n", data32->resource_source.index);
/* dbg(" Resource Source: %s\n", data32->resource_source.string_ptr); */
}
shpchprm_add_resource(aprh, data32->min_address_range, data32->address_length);
}
static acpi_status acpi_parse_crs(
struct acpi_bridge *ab,
struct acpi_resource *crsbuf
)
{
acpi_status status = AE_OK;
struct acpi_resource *resource = crsbuf;
u8 count = 0;
u8 done = 0;
while (!done) {
dbg("acpi_shpchprm: PCI bus 0x%x Resource structure %x.\n", ab->bus, count++);
switch (resource->id) {
case ACPI_RSTYPE_IRQ:
dbg("Irq -------- Resource\n");
break;
case ACPI_RSTYPE_DMA:
dbg("DMA -------- Resource\n");
break;
case ACPI_RSTYPE_START_DPF:
dbg("Start DPF -------- Resource\n");
break;
case ACPI_RSTYPE_END_DPF:
dbg("End DPF -------- Resource\n");
break;
case ACPI_RSTYPE_IO:
acpi_parse_io (ab, &resource->data);
break;
case ACPI_RSTYPE_FIXED_IO:
acpi_parse_fixed_io (ab, &resource->data);
break;
case ACPI_RSTYPE_VENDOR:
dbg("Vendor -------- Resource\n");
break;
case ACPI_RSTYPE_END_TAG:
dbg("End_tag -------- Resource\n");
done = 1;
break;
case ACPI_RSTYPE_MEM24:
dbg("Mem24 -------- Resource\n");
break;
case ACPI_RSTYPE_MEM32:
dbg("Mem32 -------- Resource\n");
break;
case ACPI_RSTYPE_FIXED_MEM32:
dbg("Fixed Mem32 -------- Resource\n");
break;
case ACPI_RSTYPE_ADDRESS16:
acpi_parse_address16_32(ab, &resource->data, ACPI_RSTYPE_ADDRESS16);
break;
case ACPI_RSTYPE_ADDRESS32:
acpi_parse_address16_32(ab, &resource->data, ACPI_RSTYPE_ADDRESS32);
break;
case ACPI_RSTYPE_ADDRESS64:
info("Address64 -------- Resource unparsed\n");
break;
case ACPI_RSTYPE_EXT_IRQ:
dbg("Ext Irq -------- Resource\n");
break;
default:
dbg("Invalid -------- resource type 0x%x\n", resource->id);
break;
}
resource = (struct acpi_resource *) ((char *)resource + resource->length);
}
return status;
}
static acpi_status acpi_get_crs( struct acpi_bridge *ab)
{
acpi_status status;
struct acpi_resource *crsbuf;
status = acpi_evaluate_crs(ab->handle, &crsbuf);
if (ACPI_SUCCESS(status)) {
status = acpi_parse_crs(ab, crsbuf);
kfree(crsbuf);
shpchp_resource_sort_and_combine(&ab->bus_head);
shpchp_resource_sort_and_combine(&ab->io_head);
shpchp_resource_sort_and_combine(&ab->mem_head);
shpchp_resource_sort_and_combine(&ab->p_mem_head);
shpchprm_add_resources (&ab->tbus_head, ab->bus_head);
shpchprm_add_resources (&ab->tio_head, ab->io_head);
shpchprm_add_resources (&ab->tmem_head, ab->mem_head);
shpchprm_add_resources (&ab->tp_mem_head, ab->p_mem_head);
}
return status;
}
/* find acpi_bridge downword from ab. */
static struct acpi_bridge *
find_acpi_bridge_by_bus(
struct acpi_bridge *ab,
int seg,
int bus /* pdev->subordinate->number */
)
{
struct acpi_bridge *lab = NULL;
if (!ab)
return NULL;
if ((ab->bus == bus) && (ab->seg == seg))
return ab;
if (ab->child)
lab = find_acpi_bridge_by_bus(ab->child, seg, bus);
if (!lab)
if (ab->next)
lab = find_acpi_bridge_by_bus(ab->next, seg, bus);
return lab;
}
/*
* Build a device tree of ACPI PCI Bridges
*/
static void shpchprm_acpi_register_a_bridge (
struct acpi_bridge **head,
struct acpi_bridge *pab, /* parent bridge to which child bridge is added */
struct acpi_bridge *cab /* child bridge to add */
)
{
struct acpi_bridge *lpab;
struct acpi_bridge *lcab;
lpab = find_acpi_bridge_by_bus(*head, pab->seg, pab->bus);
if (!lpab) {
if (!(pab->type & BRIDGE_TYPE_HOST))
warn("PCI parent bridge s:b(%x:%x) not in list.\n", pab->seg, pab->bus);
pab->next = *head;
*head = pab;
lpab = pab;
}
if ((cab->type & BRIDGE_TYPE_HOST) && (pab == cab))
return;
lcab = find_acpi_bridge_by_bus(*head, cab->seg, cab->bus);
if (lcab) {
if ((pab->bus != lcab->parent->bus) || (lcab->bus != cab->bus))
err("PCI child bridge s:b(%x:%x) in list with diff parent.\n", cab->seg, cab->bus);
return;
} else
lcab = cab;
lcab->parent = lpab;
lcab->next = lpab->child;
lpab->child = lcab;
}
static acpi_status shpchprm_acpi_build_php_slots_callback(
acpi_handle handle,
u32 Level,
void *context,
void **retval
)
{
ulong bus_num;
ulong seg_num;
ulong sun, adr;
ulong padr = 0;
acpi_handle phandle = NULL;
struct acpi_bridge *pab = (struct acpi_bridge *)context;
struct acpi_bridge *lab;
acpi_status status;
u8 *path_name = acpi_path_name(handle);
/* get _SUN */
status = acpi_evaluate_integer(handle, METHOD_NAME__SUN, NULL, &sun);
switch(status) {
case AE_NOT_FOUND:
return AE_OK;
default:
if (ACPI_FAILURE(status)) {
err("acpi_shpchprm:%s _SUN fail=0x%x\n", path_name, status);
return status;
}
}
/* get _ADR. _ADR must exist if _SUN exists */
status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr);
if (ACPI_FAILURE(status)) {
err("acpi_shpchprm:%s _ADR fail=0x%x\n", path_name, status);
return status;
}
dbg("acpi_shpchprm:%s sun=0x%08x adr=0x%08x\n", path_name, (u32)sun, (u32)adr);
status = acpi_get_parent(handle, &phandle);
if (ACPI_FAILURE(status)) {
err("acpi_shpchprm:%s get_parent fail=0x%x\n", path_name, status);
return (status);
}
bus_num = pab->bus;
seg_num = pab->seg;
if (pab->bus == bus_num) {
lab = pab;
} else {
dbg("WARN: pab is not parent\n");
lab = find_acpi_bridge_by_bus(pab, seg_num, bus_num);
if (!lab) {
dbg("acpi_shpchprm: alloc new P2P bridge(%x) for sun(%08x)\n", (u32)bus_num, (u32)sun);
lab = (struct acpi_bridge *)kmalloc(sizeof(struct acpi_bridge), GFP_KERNEL);
if (!lab) {
err("acpi_shpchprm: alloc for ab fail\n");
return AE_NO_MEMORY;
}
memset(lab, 0, sizeof(struct acpi_bridge));
lab->handle = phandle;
lab->pbus = pab->bus;
lab->pdevice = (int)(padr >> 16) & 0xffff;
lab->pfunction = (int)(padr & 0xffff);
lab->bus = (int)bus_num;
lab->scanned = 0;
lab->type = BRIDGE_TYPE_P2P;
shpchprm_acpi_register_a_bridge (&acpi_bridges_head, pab, lab);
} else
dbg("acpi_shpchprm: found P2P bridge(%x) for sun(%08x)\n", (u32)bus_num, (u32)sun);
}
acpi_add_slot_to_php_slots(lab, (int)bus_num, handle, (u32)adr, (u32)sun);
return (status);
}
static int shpchprm_acpi_build_php_slots(
struct acpi_bridge *ab,
u32 depth
)
{
acpi_status status;
u8 *path_name = acpi_path_name(ab->handle);
/* Walk down this pci bridge to get _SUNs if any behind P2P */
status = acpi_walk_namespace ( ACPI_TYPE_DEVICE,
ab->handle,
depth,
shpchprm_acpi_build_php_slots_callback,
ab,
NULL );
if (ACPI_FAILURE(status)) {
dbg("acpi_shpchprm:%s walk for _SUN on pci bridge seg:bus(%x:%x) fail=0x%x\n", path_name, ab->seg, ab->bus, status);
return -1;
}
return 0;
}
static void build_a_bridge(
struct acpi_bridge *pab,
struct acpi_bridge *ab
)
{
u8 *path_name = acpi_path_name(ab->handle);
shpchprm_acpi_register_a_bridge (&acpi_bridges_head, pab, ab);
switch (ab->type) {
case BRIDGE_TYPE_HOST:
dbg("acpi_shpchprm: Registered PCI HOST Bridge(%02x) on s:b:d:f(%02x:%02x:%02x:%02x) [%s]\n",
ab->bus, ab->seg, ab->pbus, ab->pdevice, ab->pfunction, path_name);
break;
case BRIDGE_TYPE_P2P:
dbg("acpi_shpchprm: Registered PCI P2P Bridge(%02x-%02x) on s:b:d:f(%02x:%02x:%02x:%02x) [%s]\n",
ab->pbus, ab->bus, ab->seg, ab->pbus, ab->pdevice, ab->pfunction, path_name);
break;
};
/* build any immediate PHP slots under this pci bridge */
shpchprm_acpi_build_php_slots(ab, 1);
}
static struct acpi_bridge * add_p2p_bridge(
acpi_handle handle,
struct acpi_bridge *pab, /* parent */
ulong adr
)
{
struct acpi_bridge *ab;
struct pci_dev *pdev;
ulong devnum, funcnum;
u8 *path_name = acpi_path_name(handle);
ab = (struct acpi_bridge *) kmalloc (sizeof(struct acpi_bridge), GFP_KERNEL);
if (!ab) {
err("acpi_shpchprm: alloc for ab fail\n");
return NULL;
}
memset(ab, 0, sizeof(struct acpi_bridge));
devnum = (adr >> 16) & 0xffff;
funcnum = adr & 0xffff;
pdev = pci_find_slot(pab->bus, PCI_DEVFN(devnum, funcnum));
if (!pdev || !pdev->subordinate) {
err("acpi_shpchprm:%s is not a P2P Bridge\n", path_name);
kfree(ab);
return NULL;
}
ab->handle = handle;
ab->seg = pab->seg;
ab->pbus = pab->bus; /* or pdev->bus->number */
ab->pdevice = devnum; /* or PCI_SLOT(pdev->devfn) */
ab->pfunction = funcnum; /* or PCI_FUNC(pdev->devfn) */
ab->bus = pdev->subordinate->number;
ab->scanned = 0;
ab->type = BRIDGE_TYPE_P2P;
dbg("acpi_shpchprm: P2P(%x-%x) on pci=b:d:f(%x:%x:%x) acpi=b:d:f(%x:%x:%x) [%s]\n",
pab->bus, ab->bus, pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
pab->bus, (u32)devnum, (u32)funcnum, path_name);
build_a_bridge(pab, ab);
return ab;
}
static acpi_status scan_p2p_bridge(
acpi_handle handle,
u32 Level,
void *context,
void **retval
)
{
struct acpi_bridge *pab = (struct acpi_bridge *)context;
struct acpi_bridge *ab;
acpi_status status;
ulong adr = 0;
u8 *path_name = acpi_path_name(handle);
ulong devnum, funcnum;
struct pci_dev *pdev;
/* get device, function */
status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr);
if (ACPI_FAILURE(status)) {
if (status != AE_NOT_FOUND)
err("acpi_shpchprm:%s _ADR fail=0x%x\n", path_name, status);
return AE_OK;
}
devnum = (adr >> 16) & 0xffff;
funcnum = adr & 0xffff;
pdev = pci_find_slot(pab->bus, PCI_DEVFN(devnum, funcnum));
if (!pdev)
return AE_OK;
if (!pdev->subordinate)
return AE_OK;
ab = add_p2p_bridge(handle, pab, adr);
if (ab) {
status = acpi_walk_namespace ( ACPI_TYPE_DEVICE,
handle,
(u32)1,
scan_p2p_bridge,
ab,
NULL);
if (ACPI_FAILURE(status))
dbg("acpi_shpchprm:%s find_p2p fail=0x%x\n", path_name, status);
}
return AE_OK;
}
static struct acpi_bridge * add_host_bridge(
acpi_handle handle,
ulong segnum,
ulong busnum
)
{
ulong adr = 0;
acpi_status status;
struct acpi_bridge *ab;
u8 *path_name = acpi_path_name(handle);
/* get device, function: host br adr is always 0000 though. */
status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &adr);
if (ACPI_FAILURE(status)) {
err("acpi_shpchprm:%s _ADR fail=0x%x\n", path_name, status);
return NULL;
}
dbg("acpi_shpchprm: ROOT PCI seg(0x%x)bus(0x%x)dev(0x%x)func(0x%x) [%s]\n", (u32)segnum, (u32)busnum,
(u32)(adr >> 16) & 0xffff, (u32)adr & 0xffff, path_name);
ab = (struct acpi_bridge *) kmalloc (sizeof(struct acpi_bridge), GFP_KERNEL);
if (!ab) {
err("acpi_shpchprm: alloc for ab fail\n");
return NULL;
}
memset(ab, 0, sizeof(struct acpi_bridge));
ab->handle = handle;
ab->seg = (int)segnum;
ab->bus = ab->pbus = (int)busnum;
ab->pdevice = (int)(adr >> 16) & 0xffff;
ab->pfunction = (int)(adr & 0xffff);
ab->scanned = 0;
ab->type = BRIDGE_TYPE_HOST;
/* get root pci bridge's current resources */
status = acpi_get_crs(ab);
if (ACPI_FAILURE(status)) {
err("acpi_shpchprm:%s evaluate _CRS fail=0x%x\n", path_name, status);
kfree(ab);
return NULL;
}
build_a_bridge(ab, ab);
return ab;
}
static acpi_status acpi_scan_from_root_pci_callback (
acpi_handle handle,
u32 Level,
void *context,
void **retval
)
{
ulong segnum = 0;
ulong busnum = 0;
acpi_status status;
struct acpi_bridge *ab;
u8 *path_name = acpi_path_name(handle);
/* get bus number of this pci root bridge */
status = acpi_evaluate_integer(handle, METHOD_NAME__SEG, NULL, &segnum);
if (ACPI_FAILURE(status)) {
if (status != AE_NOT_FOUND) {
err("acpi_shpchprm:%s evaluate _SEG fail=0x%x\n", path_name, status);
return status;
}
segnum = 0;
}
/* get bus number of this pci root bridge */
status = acpi_evaluate_integer(handle, METHOD_NAME__BBN, NULL, &busnum);
if (ACPI_FAILURE(status)) {
err("acpi_shpchprm:%s evaluate _BBN fail=0x%x\n", path_name, status);
return (status);
}
ab = add_host_bridge(handle, segnum, busnum);
if (ab) {
status = acpi_walk_namespace ( ACPI_TYPE_DEVICE,
handle,
1,
scan_p2p_bridge,
ab,
NULL);
if (ACPI_FAILURE(status))
dbg("acpi_shpchprm:%s find_p2p fail=0x%x\n", path_name, status);
}
return AE_OK;
}
static int shpchprm_acpi_scan_pci (void)
{
acpi_status status;
/*
* TBD: traverse LDM device tree with the help of
* unified ACPI augmented for php device population.
*/
status = acpi_get_devices ( PCI_ROOT_HID_STRING,
acpi_scan_from_root_pci_callback,
NULL,
NULL );
if (ACPI_FAILURE(status)) {
err("acpi_shpchprm:get_device PCI ROOT HID fail=0x%x\n", status);
return -1;
}
return 0;
}
int shpchprm_init(enum php_ctlr_type ctlr_type)
{
int rc;
if (ctlr_type != PCI)
return -ENODEV;
dbg("shpchprm ACPI init <enter>\n");
acpi_bridges_head = NULL;
/* construct PCI bus:device tree of acpi_handles */
rc = shpchprm_acpi_scan_pci();
if (rc)
return rc;
dbg("shpchprm ACPI init %s\n", (rc)?"fail":"success");
return rc;
}
static void free_a_slot(struct acpi_php_slot *aps)
{
dbg(" free a php func of slot(0x%02x) on PCI b:d:f=0x%02x:%02x:%02x\n", aps->sun, aps->bus, aps->dev, aps->fun);
free_pci_resource (aps->io_head);
free_pci_resource (aps->bus_head);
free_pci_resource (aps->mem_head);
free_pci_resource (aps->p_mem_head);
kfree(aps);
}
static void free_a_bridge( struct acpi_bridge *ab)
{
struct acpi_php_slot *aps, *next;
switch (ab->type) {
case BRIDGE_TYPE_HOST:
dbg("Free ACPI PCI HOST Bridge(%x) [%s] on s:b:d:f(%x:%x:%x:%x)\n",
ab->bus, acpi_path_name(ab->handle), ab->seg, ab->pbus, ab->pdevice, ab->pfunction);
break;
case BRIDGE_TYPE_P2P:
dbg("Free ACPI PCI P2P Bridge(%x-%x) [%s] on s:b:d:f(%x:%x:%x:%x)\n",
ab->pbus, ab->bus, acpi_path_name(ab->handle), ab->seg, ab->pbus, ab->pdevice, ab->pfunction);
break;
};
/* free slots first */
for (aps = ab->slots; aps; aps = next) {
next = aps->next;
free_a_slot(aps);
}
free_pci_resource (ab->io_head);
free_pci_resource (ab->tio_head);
free_pci_resource (ab->bus_head);
free_pci_resource (ab->tbus_head);
free_pci_resource (ab->mem_head);
free_pci_resource (ab->tmem_head);
free_pci_resource (ab->p_mem_head);
free_pci_resource (ab->tp_mem_head);
kfree(ab);
}
static void shpchprm_free_bridges ( struct acpi_bridge *ab)
{
if (ab->child)
shpchprm_free_bridges (ab->child);
if (ab->next)
shpchprm_free_bridges (ab->next);
free_a_bridge(ab);
}
void shpchprm_cleanup(void)
{
shpchprm_free_bridges (acpi_bridges_head);
}
static int get_number_of_slots (
struct acpi_bridge *ab,
int selfonly
)
{
struct acpi_php_slot *aps;
int prev_slot = -1;
int slot_num = 0;
for ( aps = ab->slots; aps; aps = aps->next)
if (aps->dev != prev_slot) {
prev_slot = aps->dev;
slot_num++;
}
if (ab->child)
slot_num += get_number_of_slots (ab->child, 0);
if (selfonly)
return slot_num;
if (ab->next)
slot_num += get_number_of_slots (ab->next, 0);
return slot_num;
}
static int print_acpi_resources (struct acpi_bridge *ab)
{
struct acpi_php_slot *aps;
int i;
switch (ab->type) {
case BRIDGE_TYPE_HOST:
dbg("PCI HOST Bridge (%x) [%s]\n", ab->bus, acpi_path_name(ab->handle));
break;
case BRIDGE_TYPE_P2P:
dbg("PCI P2P Bridge (%x-%x) [%s]\n", ab->pbus, ab->bus, acpi_path_name(ab->handle));
break;
};
print_pci_resources (ab);
for ( i = -1, aps = ab->slots; aps; aps = aps->next) {
if (aps->dev == i)
continue;
dbg(" Slot sun(%x) s:b:d:f(%02x:%02x:%02x:%02x)\n", aps->sun, aps->seg, aps->bus, aps->dev, aps->fun);
print_slot_resources(aps);
i = aps->dev;
}
if (ab->child)
print_acpi_resources (ab->child);
if (ab->next)
print_acpi_resources (ab->next);
return 0;
}
int shpchprm_print_pirt(void)
{
dbg("SHPCHPRM ACPI Slots\n");
print_acpi_resources (acpi_bridges_head);
return 0;
}
static struct acpi_php_slot * get_acpi_slot (
struct acpi_bridge *ab,
u32 sun
)
{
struct acpi_php_slot *aps = NULL;
for ( aps = ab->slots; aps; aps = aps->next)
if (aps->sun == sun)
return aps;
if (!aps && ab->child) {
aps = (struct acpi_php_slot *)get_acpi_slot (ab->child, sun);
if (aps)
return aps;
}
if (!aps && ab->next) {
aps = (struct acpi_php_slot *)get_acpi_slot (ab->next, sun);
if (aps)
return aps;
}
return aps;
}
void * shpchprm_get_slot(struct slot *slot)
{
struct acpi_bridge *ab = acpi_bridges_head;
struct acpi_php_slot *aps = get_acpi_slot (ab, slot->number);
aps->slot = slot;
dbg("Got acpi slot sun(%x): s:b:d:f(%x:%x:%x:%x)\n", aps->sun, aps->seg, aps->bus, aps->dev, aps->fun);
return (void *)aps;
}
static void shpchprm_dump_func_res( struct pci_func *fun)
{
struct pci_func *func = fun;
if (func->bus_head) {
dbg(": BUS Resources:\n");
print_pci_resource (func->bus_head);
}
if (func->io_head) {
dbg(": IO Resources:\n");
print_pci_resource (func->io_head);
}
if (func->mem_head) {
dbg(": MEM Resources:\n");
print_pci_resource (func->mem_head);
}
if (func->p_mem_head) {
dbg(": PMEM Resources:\n");
print_pci_resource (func->p_mem_head);
}
}
static void shpchprm_dump_ctrl_res( struct controller *ctlr)
{
struct controller *ctrl = ctlr;
if (ctrl->bus_head) {
dbg(": BUS Resources:\n");
print_pci_resource (ctrl->bus_head);
}
if (ctrl->io_head) {
dbg(": IO Resources:\n");
print_pci_resource (ctrl->io_head);
}
if (ctrl->mem_head) {
dbg(": MEM Resources:\n");
print_pci_resource (ctrl->mem_head);
}
if (ctrl->p_mem_head) {
dbg(": PMEM Resources:\n");
print_pci_resource (ctrl->p_mem_head);
}
}
static int shpchprm_get_used_resources (
struct controller *ctrl,
struct pci_func *func
)
{
return shpchp_save_used_resources (ctrl, func, !DISABLE_CARD);
}
static int configure_existing_function(
struct controller *ctrl,
struct pci_func *func
)
{
int rc;
/* see how much resources the func has used. */
rc = shpchprm_get_used_resources (ctrl, func);
if (!rc) {
/* subtract the resources used by the func from ctrl resources */
rc = shpchprm_delete_resources (&ctrl->bus_head, func->bus_head);
rc |= shpchprm_delete_resources (&ctrl->io_head, func->io_head);
rc |= shpchprm_delete_resources (&ctrl->mem_head, func->mem_head);
rc |= shpchprm_delete_resources (&ctrl->p_mem_head, func->p_mem_head);
if (rc)
warn("aCEF: cannot del used resources\n");
} else
err("aCEF: cannot get used resources\n");
return rc;
}
static int bind_pci_resources_to_slots ( struct controller *ctrl)
{
struct pci_func *func;
int busn = ctrl->bus;
int devn, funn;
u32 vid;
for (devn = 0; devn < 32; devn++) {
for (funn = 0; funn < 8; funn++) {
if (devn == ctrl->device && funn == ctrl->function)
continue;
/* find out if this entry is for an occupied slot */
vid = 0xFFFFFFFF;
pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(devn, funn), PCI_VENDOR_ID, &vid);
if (vid != 0xFFFFFFFF) {
func = shpchp_slot_find(busn, devn, funn);
if (!func)
continue;
configure_existing_function(ctrl, func);
dbg("aCCF:existing PCI 0x%x Func ResourceDump\n", ctrl->bus);
shpchprm_dump_func_res(func);
}
}
}
return 0;
}
static int bind_pci_resources(
struct controller *ctrl,
struct acpi_bridge *ab
)
{
int status = 0;
if (ab->bus_head) {
dbg("bapr: BUS Resources add on PCI 0x%x\n", ab->bus);
status = shpchprm_add_resources (&ctrl->bus_head, ab->bus_head);
if (shpchprm_delete_resources (&ab->bus_head, ctrl->bus_head))
warn("bapr: cannot sub BUS Resource on PCI 0x%x\n", ab->bus);
if (status) {
err("bapr: BUS Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status);
return status;
}
} else
info("bapr: No BUS Resource on PCI 0x%x.\n", ab->bus);
if (ab->io_head) {
dbg("bapr: IO Resources add on PCI 0x%x\n", ab->bus);
status = shpchprm_add_resources (&ctrl->io_head, ab->io_head);
if (shpchprm_delete_resources (&ab->io_head, ctrl->io_head))
warn("bapr: cannot sub IO Resource on PCI 0x%x\n", ab->bus);
if (status) {
err("bapr: IO Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status);
return status;
}
} else
info("bapr: No IO Resource on PCI 0x%x.\n", ab->bus);
if (ab->mem_head) {
dbg("bapr: MEM Resources add on PCI 0x%x\n", ab->bus);
status = shpchprm_add_resources (&ctrl->mem_head, ab->mem_head);
if (shpchprm_delete_resources (&ab->mem_head, ctrl->mem_head))
warn("bapr: cannot sub MEM Resource on PCI 0x%x\n", ab->bus);
if (status) {
err("bapr: MEM Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status);
return status;
}
} else
info("bapr: No MEM Resource on PCI 0x%x.\n", ab->bus);
if (ab->p_mem_head) {
dbg("bapr: PMEM Resources add on PCI 0x%x\n", ab->bus);
status = shpchprm_add_resources (&ctrl->p_mem_head, ab->p_mem_head);
if (shpchprm_delete_resources (&ab->p_mem_head, ctrl->p_mem_head))
warn("bapr: cannot sub PMEM Resource on PCI 0x%x\n", ab->bus);
if (status) {
err("bapr: PMEM Resource add on PCI 0x%x: fail=0x%x\n", ab->bus, status);
return status;
}
} else
info("bapr: No PMEM Resource on PCI 0x%x.\n", ab->bus);
return status;
}
static int no_pci_resources( struct acpi_bridge *ab)
{
return !(ab->p_mem_head || ab->mem_head || ab->io_head || ab->bus_head);
}
static int find_pci_bridge_resources (
struct controller *ctrl,
struct acpi_bridge *ab
)
{
int rc = 0;
struct pci_func func;
memset(&func, 0, sizeof(struct pci_func));
func.bus = ab->pbus;
func.device = ab->pdevice;
func.function = ab->pfunction;
func.is_a_board = 1;
/* Get used resources for this PCI bridge */
rc = shpchp_save_used_resources (ctrl, &func, !DISABLE_CARD);
ab->io_head = func.io_head;
ab->mem_head = func.mem_head;
ab->p_mem_head = func.p_mem_head;
ab->bus_head = func.bus_head;
if (ab->bus_head)
shpchprm_delete_resource(&ab->bus_head, ctrl->bus, 1);
return rc;
}
static int get_pci_resources_from_bridge(
struct controller *ctrl,
struct acpi_bridge *ab
)
{
int rc = 0;
dbg("grfb: Get Resources for PCI 0x%x from actual PCI bridge 0x%x.\n", ctrl->bus, ab->bus);
rc = find_pci_bridge_resources (ctrl, ab);
shpchp_resource_sort_and_combine(&ab->bus_head);
shpchp_resource_sort_and_combine(&ab->io_head);
shpchp_resource_sort_and_combine(&ab->mem_head);
shpchp_resource_sort_and_combine(&ab->p_mem_head);
shpchprm_add_resources (&ab->tbus_head, ab->bus_head);
shpchprm_add_resources (&ab->tio_head, ab->io_head);
shpchprm_add_resources (&ab->tmem_head, ab->mem_head);
shpchprm_add_resources (&ab->tp_mem_head, ab->p_mem_head);
return rc;
}
static int get_pci_resources(
struct controller *ctrl,
struct acpi_bridge *ab
)
{
int rc = 0;
if (no_pci_resources(ab)) {
dbg("spbr:PCI 0x%x has no resources. Get parent resources.\n", ab->bus);
rc = get_pci_resources_from_bridge(ctrl, ab);
}
return rc;
}
int shpchprm_get_physical_slot_number(struct controller *ctrl, u32 *sun, u8 busnum, u8 devnum)
{
int offset = devnum - ctrl->slot_device_offset;
dbg("%s: ctrl->slot_num_inc %d, offset %d\n", __FUNCTION__, ctrl->slot_num_inc, offset);
*sun = (u8) (ctrl->first_slot + ctrl->slot_num_inc *offset);
return 0;
}
/*
* Get resources for this ctrl.
* 1. get total resources from ACPI _CRS or bridge (this ctrl)
* 2. find used resources of existing adapters
* 3. subtract used resources from total resources
*/
int shpchprm_find_available_resources( struct controller *ctrl)
{
int rc = 0;
struct acpi_bridge *ab;
ab = find_acpi_bridge_by_bus(acpi_bridges_head, ctrl->seg, ctrl->pci_dev->subordinate->number);
if (!ab) {
err("pfar:cannot locate acpi bridge of PCI 0x%x.\n", ctrl->pci_dev->subordinate->number);
return -1;
}
if (no_pci_resources(ab)) {
rc = get_pci_resources(ctrl, ab);
if (rc) {
err("pfar:cannot get pci resources of PCI 0x%x.\n",ctrl->pci_dev->subordinate->number);
return -1;
}
}
rc = bind_pci_resources(ctrl, ab);
dbg("pfar:pre-Bind PCI 0x%x Ctrl Resource Dump\n", ctrl->pci_dev->subordinate->number);
shpchprm_dump_ctrl_res(ctrl);
bind_pci_resources_to_slots (ctrl);
dbg("pfar:post-Bind PCI 0x%x Ctrl Resource Dump\n", ctrl->pci_dev->subordinate->number);
shpchprm_dump_ctrl_res(ctrl);
return rc;
}
int shpchprm_set_hpp(
struct controller *ctrl,
struct pci_func *func,
u8 card_type
)
{
struct acpi_bridge *ab;
struct pci_bus lpci_bus, *pci_bus;
int rc = 0;
unsigned int devfn;
u8 cls= 0x08; /* default cache line size */
u8 lt = 0x40; /* default latency timer */
u8 ep = 0;
u8 es = 0;
memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
pci_bus = &lpci_bus;
pci_bus->number = func->bus;
devfn = PCI_DEVFN(func->device, func->function);
ab = find_acpi_bridge_by_bus(acpi_bridges_head, ctrl->seg, ctrl->bus);
if (ab) {
if (ab->_hpp) {
lt = (u8)ab->_hpp->latency_timer;
cls = (u8)ab->_hpp->cache_line_size;
ep = (u8)ab->_hpp->enable_perr;
es = (u8)ab->_hpp->enable_serr;
} else
dbg("_hpp: no _hpp for B/D/F=%#x/%#x/%#x. use default value\n", func->bus, func->device, func->function);
} else
dbg("_hpp: no acpi bridge for B/D/F = %#x/%#x/%#x. use default value\n", func->bus, func->device, func->function);
if (card_type == PCI_HEADER_TYPE_BRIDGE) {
/* set subordinate Latency Timer */
rc |= pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, lt);
}
/* set base Latency Timer */
rc |= pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, lt);
dbg(" set latency timer =0x%02x: %x\n", lt, rc);
rc |= pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, cls);
dbg(" set cache_line_size=0x%02x: %x\n", cls, rc);
return rc;
}
void shpchprm_enable_card(
struct controller *ctrl,
struct pci_func *func,
u8 card_type)
{
u16 command, cmd, bcommand, bcmd;
struct pci_bus lpci_bus, *pci_bus;
struct acpi_bridge *ab;
unsigned int devfn;
int rc;
memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
pci_bus = &lpci_bus;
pci_bus->number = func->bus;
devfn = PCI_DEVFN(func->device, func->function);
rc = pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &command);
if (card_type == PCI_HEADER_TYPE_BRIDGE) {
rc = pci_bus_read_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, &bcommand);
}
cmd = command = command | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE
| PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
bcmd = bcommand = bcommand | PCI_BRIDGE_CTL_NO_ISA;
ab = find_acpi_bridge_by_bus(acpi_bridges_head, ctrl->seg, ctrl->bus);
if (ab) {
if (ab->_hpp) {
if (ab->_hpp->enable_perr) {
command |= PCI_COMMAND_PARITY;
bcommand |= PCI_BRIDGE_CTL_PARITY;
} else {
command &= ~PCI_COMMAND_PARITY;
bcommand &= ~PCI_BRIDGE_CTL_PARITY;
}
if (ab->_hpp->enable_serr) {
command |= PCI_COMMAND_SERR;
bcommand |= PCI_BRIDGE_CTL_SERR;
} else {
command &= ~PCI_COMMAND_SERR;
bcommand &= ~PCI_BRIDGE_CTL_SERR;
}
} else
dbg("no _hpp for B/D/F = %#x/%#x/%#x.\n", func->bus, func->device, func->function);
} else
dbg("no acpi bridge for B/D/F = %#x/%#x/%#x.\n", func->bus, func->device, func->function);
if (command != cmd) {
rc = pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command);
}
if ((card_type == PCI_HEADER_TYPE_BRIDGE) && (bcommand != bcmd)) {
rc = pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, bcommand);
}
}
/*
* SHPCHPRM Legacy: PHP Resource Manager for Non-ACPI/Legacy platform
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
* Copyright (C) 2003-2004 Intel Corporation
*
* 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>,<dely.l.sy@intel.com>
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#ifdef CONFIG_IA64
#include <asm/iosapic.h>
#endif
#include "shpchp.h"
#include "shpchprm.h"
#include "shpchprm_legacy.h"
static void *shpchp_rom_start;
static u16 unused_IRQ;
void shpchprm_cleanup()
{
if (shpchp_rom_start)
iounmap(shpchp_rom_start);
}
int shpchprm_print_pirt()
{
return 0;
}
void * shpchprm_get_slot(struct slot *slot)
{
return NULL;
}
int shpchprm_get_physical_slot_number(struct controller *ctrl, u32 *sun, u8 busnum, u8 devnum)
{
int offset = devnum - ctrl->slot_device_offset;
*sun = (u8) (ctrl->first_slot + ctrl->slot_num_inc * offset);
return 0;
}
/* Find the Hot Plug Resource Table in the specified region of memory */
static void *detect_HRT_floating_pointer(void *begin, void *end)
{
void *fp;
void *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;
}
#if link_available
/*
* Links available memory, IO, and IRQ resources for programming
* devices which may be added to the system
*
* Returns 0 if success
*/
static int
link_available_resources (
struct controller *ctrl,
struct pci_func *func,
int index )
{
return shpchp_save_used_resources (ctrl, func, !DISABLE_CARD);
}
#endif
/*
* shpchprm_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 shpchprm_find_available_resources(struct controller *ctrl)
{
u8 populated_slot;
u8 bridged_slot;
void *one_slot;
struct pci_func *func = NULL;
int i = 10, index = 0;
u32 temp_dword, rc;
ulong temp_ulong;
struct pci_resource *mem_node;
struct pci_resource *p_mem_node;
struct pci_resource *io_node;
struct pci_resource *bus_node;
void *rom_resource_table;
struct pci_bus lpci_bus, *pci_bus;
u8 cfgspc_irq, temp;
memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
pci_bus = &lpci_bus;
rom_resource_table = detect_HRT_floating_pointer(shpchp_rom_start, shpchp_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) {
shpchp_disk_irq = temp;
break;
}
unused_IRQ = unused_IRQ >> 1;
temp++;
}
dbg("shpchp_disk_irq= %d\n", shpchp_disk_irq);
unused_IRQ = unused_IRQ >> 1;
temp++;
while (unused_IRQ) {
if (unused_IRQ & 1) {
shpchp_nic_irq = temp;
break;
}
unused_IRQ = unused_IRQ >> 1;
temp++;
}
dbg("shpchp_nic_irq= %d\n", shpchp_nic_irq);
unused_IRQ = readl(rom_resource_table + PCIIRQ);
temp = 0;
pci_read_config_byte(ctrl->pci_dev, PCI_INTERRUPT_LINE, &cfgspc_irq);
if (!shpchp_nic_irq) {
shpchp_nic_irq = cfgspc_irq;
}
if (!shpchp_disk_irq) {
shpchp_disk_irq = cfgspc_irq;
}
dbg("shpchp_disk_irq, shpchp_nic_irq= %d, %d\n", shpchp_disk_irq, shpchp_nic_irq);
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|MEMbase|length|PM 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->slot_bus) {
i--;
one_slot += sizeof(struct slot_rt);
continue;
}
/* find out if this entry is for an occupied slot */
temp_dword = 0xFFFFFFFF;
pci_bus->number = primary_bus;
pci_bus_read_config_dword(pci_bus, dev_func, PCI_VENDOR_ID, &temp_dword);
dbg("temp_D_word = %x\n", temp_dword);
if (temp_dword != 0xFFFFFFFF) {
index = 0;
func = shpchp_slot_find(primary_bus, dev_func >> 3, 0);
while (func && (func->function != (dev_func & 0x07))) {
dbg("func = %p b:d:f(%x:%x:%x)\n", func, primary_bus, dev_func >> 3, index);
func = shpchp_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;
}
dbg("slot populated =%s \n", populated_slot?"yes":"no");
/* If we've got a valid IO base, use it */
temp_ulong = io_base + io_length;
if ((io_base) && (temp_ulong <= 0x10000)) {
io_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!io_node)
return -ENOMEM;
io_node->base = (ulong)io_base;
io_node->length = (ulong)io_length;
dbg("found io_node(base, length) = %x, %x\n", io_node->base, io_node->length);
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_ulong = mem_base + mem_length;
if ((mem_base) && (temp_ulong <= 0x10000)) {
mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!mem_node)
return -ENOMEM;
mem_node->base = (ulong)mem_base << 16;
mem_node->length = (ulong)(mem_length << 16);
dbg("found mem_node(base, length) = %x, %x\n", mem_node->base, mem_node->length);
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_ulong = pre_mem_base + pre_mem_length;
if ((pre_mem_base) && (temp_ulong <= 0x10000)) {
p_mem_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!p_mem_node)
return -ENOMEM;
p_mem_node->base = (ulong)pre_mem_base << 16;
p_mem_node->length = (ulong)pre_mem_length << 16;
dbg("found p_mem_node(base, length) = %x, %x\n", p_mem_node->base, p_mem_node->length);
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 = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!bus_node)
return -ENOMEM;
bus_node->base = (ulong)secondary_bus;
bus_node->length = (ulong)(max_bus - secondary_bus + 1);
dbg("found bus_node(base, length) = %x, %x\n", bus_node->base, bus_node->length);
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;
}
}
#if link_available
++index;
while (index < 8) {
if (((func = shpchp_slot_find(primary_bus, dev_func >> 3, index)) != NULL) && populated_slot)
rc = link_available_resources(ctrl, func, index);
if (rc)
break;
++index;
}
#endif
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 &= shpchp_resource_sort_and_combine(&(ctrl->mem_head));
rc &= shpchp_resource_sort_and_combine(&(ctrl->p_mem_head));
rc &= shpchp_resource_sort_and_combine(&(ctrl->io_head));
rc &= shpchp_resource_sort_and_combine(&(ctrl->bus_head));
return (rc);
}
int shpchprm_set_hpp(
struct controller *ctrl,
struct pci_func *func,
u8 card_type)
{
u32 rc;
u8 temp_byte;
struct pci_bus lpci_bus, *pci_bus;
unsigned int devfn;
memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
pci_bus = &lpci_bus;
pci_bus->number = func->bus;
devfn = PCI_DEVFN(func->device, func->function);
temp_byte = 0x40; /* hard coded value for LT */
if (card_type == PCI_HEADER_TYPE_BRIDGE) {
/* set subordinate Latency Timer */
rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, temp_byte);
if (rc) {
dbg("%s: set secondary LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus,
func->device, func->function);
return rc;
}
}
/* set base Latency Timer */
rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte);
if (rc) {
dbg("%s: set LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function);
return rc;
}
/* set Cache Line size */
temp_byte = 0x08; /* hard coded value for CLS */
rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte);
if (rc) {
dbg("%s: set CLS error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function);
}
/* set enable_perr */
/* set enable_serr */
return rc;
}
void shpchprm_enable_card(
struct controller *ctrl,
struct pci_func *func,
u8 card_type)
{
u16 command, bcommand;
struct pci_bus lpci_bus, *pci_bus;
unsigned int devfn;
int rc;
memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
pci_bus = &lpci_bus;
pci_bus->number = func->bus;
devfn = PCI_DEVFN(func->device, func->function);
rc = pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &command);
command |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR
| PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE
| PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
rc = pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command);
if (card_type == PCI_HEADER_TYPE_BRIDGE) {
rc = pci_bus_read_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, &bcommand);
bcommand |= PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR
| PCI_BRIDGE_CTL_NO_ISA;
rc = pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, bcommand);
}
}
static int legacy_shpchprm_init_pci(void)
{
shpchp_rom_start = (u8 *) ioremap(ROM_PHY_ADDR, ROM_PHY_LEN);
if (!shpchp_rom_start) {
err("Could not ioremap memory region for ROM\n");
return -EIO;
}
return 0;
}
int shpchprm_init(enum php_ctlr_type ctrl_type)
{
int retval;
switch (ctrl_type) {
case PCI:
retval = legacy_shpchprm_init_pci();
break;
default:
retval = -ENODEV;
break;
}
return retval;
}
/*
* SHPCHPRM Legacy: PHP Resource Manager for Non-ACPI/Legacy platform using HRT
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
* Copyright (C) 2003-2004 Intel Corporation
*
* 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>, <dely.l.sy@intel.com>
*
*/
#ifndef _SHPCHPRM_LEGACY_H_
#define _SHPCHPRM_LEGACY_H_
#define ROM_PHY_ADDR 0x0F0000
#define ROM_PHY_LEN 0x00FFFF
struct slot_rt {
u8 dev_func;
u8 primary_bus;
u8 secondary_bus;
u8 max_bus;
u16 io_base;
u16 io_length;
u16 mem_base;
u16 mem_length;
u16 pre_mem_base;
u16 pre_mem_length;
} __attribute__ ((packed));
/* offsets to the hotplug slot resource table registers based on the above structure layout */
enum slot_rt_offsets {
DEV_FUNC = offsetof(struct slot_rt, dev_func),
PRIMARY_BUS = offsetof(struct slot_rt, primary_bus),
SECONDARY_BUS = offsetof(struct slot_rt, secondary_bus),
MAX_BUS = offsetof(struct slot_rt, max_bus),
IO_BASE = offsetof(struct slot_rt, io_base),
IO_LENGTH = offsetof(struct slot_rt, io_length),
MEM_BASE = offsetof(struct slot_rt, mem_base),
MEM_LENGTH = offsetof(struct slot_rt, mem_length),
PRE_MEM_BASE = offsetof(struct slot_rt, pre_mem_base),
PRE_MEM_LENGTH = offsetof(struct slot_rt, pre_mem_length),
};
struct hrt {
char sig0;
char sig1;
char sig2;
char sig3;
u16 unused_IRQ;
u16 PCIIRQ;
u8 number_of_entries;
u8 revision;
u16 reserved1;
u32 reserved2;
} __attribute__ ((packed));
/* offsets to the hotplug resource table registers based on the above structure layout */
enum hrt_offsets {
SIG0 = offsetof(struct hrt, sig0),
SIG1 = offsetof(struct hrt, sig1),
SIG2 = offsetof(struct hrt, sig2),
SIG3 = offsetof(struct hrt, sig3),
UNUSED_IRQ = offsetof(struct hrt, unused_IRQ),
PCIIRQ = offsetof(struct hrt, PCIIRQ),
NUMBER_OF_ENTRIES = offsetof(struct hrt, number_of_entries),
REVISION = offsetof(struct hrt, revision),
HRT_RESERVED1 = offsetof(struct hrt, reserved1),
HRT_RESERVED2 = offsetof(struct hrt, reserved2),
};
struct irq_info {
u8 bus, devfn; /* bus, device and function */
struct {
u8 link; /* IRQ line ID, chipset dependent, 0=not routed */
u16 bitmap; /* Available IRQs */
} __attribute__ ((packed)) irq[4];
u8 slot; /* slot number, 0=onboard */
u8 rfu;
} __attribute__ ((packed));
struct irq_routing_table {
u32 signature; /* PIRQ_SIGNATURE should be here */
u16 version; /* PIRQ_VERSION */
u16 size; /* Table size in bytes */
u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */
u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */
u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */
u32 miniport_data; /* Crap */
u8 rfu[11];
u8 checksum; /* Modulo 256 checksum must give zero */
struct irq_info slots[0];
} __attribute__ ((packed));
#endif /* _SHPCHPRM_LEGACY_H_ */
/*
* SHPCHPRM NONACPI: PHP Resource Manager for Non-ACPI/Legacy platform
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
* Copyright (C) 2003-2004 Intel Corporation
*
* 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>, <dely.l.sy@intel.com>
*
*/
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <asm/uaccess.h>
#ifdef CONFIG_IA64
#include <asm/iosapic.h>
#endif
#include "shpchp.h"
#include "shpchprm.h"
#include "shpchprm_nonacpi.h"
void shpchprm_cleanup(void)
{
return;
}
int shpchprm_print_pirt(void)
{
return 0;
}
void * shpchprm_get_slot(struct slot *slot)
{
return NULL;
}
int shpchprm_get_physical_slot_number(struct controller *ctrl, u32 *sun, u8 busnum, u8 devnum)
{
int offset = devnum - ctrl->slot_device_offset;
dbg("%s: ctrl->slot_num_inc %d, offset %d\n", __FUNCTION__, ctrl->slot_num_inc, offset);
*sun = (u8) (ctrl->first_slot + ctrl->slot_num_inc * offset);
return 0;
}
static void print_pci_resource ( struct pci_resource *aprh)
{
struct pci_resource *res;
for (res = aprh; res; res = res->next)
dbg(" base= 0x%x length= 0x%x\n", res->base, res->length);
}
static void phprm_dump_func_res( struct pci_func *fun)
{
struct pci_func *func = fun;
if (func->bus_head) {
dbg(": BUS Resources:\n");
print_pci_resource (func->bus_head);
}
if (func->io_head) {
dbg(": IO Resources:\n");
print_pci_resource (func->io_head);
}
if (func->mem_head) {
dbg(": MEM Resources:\n");
print_pci_resource (func->mem_head);
}
if (func->p_mem_head) {
dbg(": PMEM Resources:\n");
print_pci_resource (func->p_mem_head);
}
}
static int phprm_get_used_resources (
struct controller *ctrl,
struct pci_func *func
)
{
return shpchp_save_used_resources (ctrl, func, !DISABLE_CARD);
}
static int phprm_delete_resource(
struct pci_resource **aprh,
ulong base,
ulong size)
{
struct pci_resource *res;
struct pci_resource *prevnode;
struct pci_resource *split_node;
ulong tbase;
shpchp_resource_sort_and_combine(aprh);
for (res = *aprh; res; res = res->next) {
if (res->base > base)
continue;
if ((res->base + res->length) < (base + size))
continue;
if (res->base < base) {
tbase = base;
if ((res->length - (tbase - res->base)) < size)
continue;
split_node = (struct pci_resource *) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return -ENOMEM;
split_node->base = res->base;
split_node->length = tbase - res->base;
res->base = tbase;
res->length -= split_node->length;
split_node->next = res->next;
res->next = split_node;
}
if (res->length >= size) {
split_node = (struct pci_resource*) kmalloc(sizeof(struct pci_resource), GFP_KERNEL);
if (!split_node)
return -ENOMEM;
split_node->base = res->base + size;
split_node->length = res->length - size;
res->length = size;
split_node->next = res->next;
res->next = split_node;
}
if (*aprh == res) {
*aprh = res->next;
} else {
prevnode = *aprh;
while (prevnode->next != res)
prevnode = prevnode->next;
prevnode->next = res->next;
}
res->next = NULL;
kfree(res);
break;
}
return 0;
}
static int phprm_delete_resources(
struct pci_resource **aprh,
struct pci_resource *this
)
{
struct pci_resource *res;
for (res = this; res; res = res->next)
phprm_delete_resource(aprh, res->base, res->length);
return 0;
}
static int configure_existing_function(
struct controller *ctrl,
struct pci_func *func
)
{
int rc;
/* see how much resources the func has used. */
rc = phprm_get_used_resources (ctrl, func);
if (!rc) {
/* subtract the resources used by the func from ctrl resources */
rc = phprm_delete_resources (&ctrl->bus_head, func->bus_head);
rc |= phprm_delete_resources (&ctrl->io_head, func->io_head);
rc |= phprm_delete_resources (&ctrl->mem_head, func->mem_head);
rc |= phprm_delete_resources (&ctrl->p_mem_head, func->p_mem_head);
if (rc)
warn("aCEF: cannot del used resources\n");
} else
err("aCEF: cannot get used resources\n");
return rc;
}
static int bind_pci_resources_to_slots ( struct controller *ctrl)
{
struct pci_func *func;
int busn = ctrl->slot_bus;
int devn, funn;
u32 vid;
for (devn = 0; devn < 32; devn++) {
for (funn = 0; funn < 8; funn++) {
/*
if (devn == ctrl->device && funn == ctrl->function)
continue;
*/
/* find out if this entry is for an occupied slot */
vid = 0xFFFFFFFF;
pci_bus_read_config_dword(ctrl->pci_dev->subordinate, PCI_DEVFN(devn, funn), PCI_VENDOR_ID, &vid);
if (vid != 0xFFFFFFFF) {
func = shpchp_slot_find(busn, devn, funn);
if (!func)
continue;
configure_existing_function(ctrl, func);
dbg("aCCF:existing PCI 0x%x Func ResourceDump\n", ctrl->bus);
phprm_dump_func_res(func);
}
}
}
return 0;
}
static void phprm_dump_ctrl_res( struct controller *ctlr)
{
struct controller *ctrl = ctlr;
if (ctrl->bus_head) {
dbg(": BUS Resources:\n");
print_pci_resource (ctrl->bus_head);
}
if (ctrl->io_head) {
dbg(": IO Resources:\n");
print_pci_resource (ctrl->io_head);
}
if (ctrl->mem_head) {
dbg(": MEM Resources:\n");
print_pci_resource (ctrl->mem_head);
}
if (ctrl->p_mem_head) {
dbg(": PMEM Resources:\n");
print_pci_resource (ctrl->p_mem_head);
}
}
/*
* phprm_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 shpchprm_find_available_resources(struct controller *ctrl)
{
struct pci_func func;
u32 rc;
memset(&func, 0, sizeof(struct pci_func));
func.bus = ctrl->bus;
func.device = ctrl->device;
func.function = ctrl->function;
func.is_a_board = 1;
/* Get resources for this PCI bridge */
rc = shpchp_save_used_resources (ctrl, &func, !DISABLE_CARD);
dbg("%s: shpchp_save_used_resources rc = %d\n", __FUNCTION__, rc);
if (func.mem_head)
func.mem_head->next = ctrl->mem_head;
ctrl->mem_head = func.mem_head;
if (func.p_mem_head)
func.p_mem_head->next = ctrl->p_mem_head;
ctrl->p_mem_head = func.p_mem_head;
if (func.io_head)
func.io_head->next = ctrl->io_head;
ctrl->io_head = func.io_head;
if(func.bus_head)
func.bus_head->next = ctrl->bus_head;
ctrl->bus_head = func.bus_head;
if (ctrl->bus_head)
phprm_delete_resource(&ctrl->bus_head, ctrl->pci_dev->subordinate->number, 1);
dbg("%s:pre-Bind PCI 0x%x Ctrl Resource Dump\n", __FUNCTION__, ctrl->bus);
phprm_dump_ctrl_res(ctrl);
bind_pci_resources_to_slots (ctrl);
dbg("%s:post-Bind PCI 0x%x Ctrl Resource Dump\n", __FUNCTION__, ctrl->bus);
phprm_dump_ctrl_res(ctrl);
/* If all of the following fail, we don't have any resources for hot plug add */
rc = 1;
rc &= shpchp_resource_sort_and_combine(&(ctrl->mem_head));
rc &= shpchp_resource_sort_and_combine(&(ctrl->p_mem_head));
rc &= shpchp_resource_sort_and_combine(&(ctrl->io_head));
rc &= shpchp_resource_sort_and_combine(&(ctrl->bus_head));
return (rc);
}
int shpchprm_set_hpp(
struct controller *ctrl,
struct pci_func *func,
u8 card_type)
{
u32 rc;
u8 temp_byte;
struct pci_bus lpci_bus, *pci_bus;
unsigned int devfn;
memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
pci_bus = &lpci_bus;
pci_bus->number = func->bus;
devfn = PCI_DEVFN(func->device, func->function);
temp_byte = 0x40; /* hard coded value for LT */
if (card_type == PCI_HEADER_TYPE_BRIDGE) {
/* set subordinate Latency Timer */
rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_SEC_LATENCY_TIMER, temp_byte);
if (rc) {
dbg("%s: set secondary LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus,
func->device, func->function);
return rc;
}
}
/* set base Latency Timer */
rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_LATENCY_TIMER, temp_byte);
if (rc) {
dbg("%s: set LT error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function);
return rc;
}
/* set Cache Line size */
temp_byte = 0x08; /* hard coded value for CLS */
rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_CACHE_LINE_SIZE, temp_byte);
if (rc) {
dbg("%s: set CLS error. b:d:f(%02x:%02x:%02x)\n", __FUNCTION__, func->bus, func->device, func->function);
}
/* set enable_perr */
/* set enable_serr */
return rc;
}
void shpchprm_enable_card(
struct controller *ctrl,
struct pci_func *func,
u8 card_type)
{
u16 command, bcommand;
struct pci_bus lpci_bus, *pci_bus;
unsigned int devfn;
int rc;
memcpy(&lpci_bus, ctrl->pci_bus, sizeof(lpci_bus));
pci_bus = &lpci_bus;
pci_bus->number = func->bus;
devfn = PCI_DEVFN(func->device, func->function);
rc = pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &command);
command |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR
| PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE
| PCI_COMMAND_IO | PCI_COMMAND_MEMORY;
rc = pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command);
if (card_type == PCI_HEADER_TYPE_BRIDGE) {
rc = pci_bus_read_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, &bcommand);
bcommand |= PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR
| PCI_BRIDGE_CTL_NO_ISA;
rc = pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, bcommand);
}
}
static int legacy_shpchprm_init_pci(void)
{
return 0;
}
int shpchprm_init(enum php_ctlr_type ctrl_type)
{
int retval;
switch (ctrl_type) {
case PCI:
retval = legacy_shpchprm_init_pci();
break;
default:
retval = -ENODEV;
break;
}
return retval;
}
/*
* SHPCHPRM NONACPI: PHP Resource Manager for Non-ACPI/Legacy platform
*
* Copyright (C) 1995,2001 Compaq Computer Corporation
* Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001 IBM Corp.
* Copyright (C) 2003-2004 Intel Corporation
*
* 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>, <dely.l.sy@intel.com>
*
*/
#ifndef _SHPCHPRM_NONACPI_H_
#define _SHPCHPRM_NONACPI_H_
struct irq_info {
u8 bus, devfn; /* bus, device and function */
struct {
u8 link; /* IRQ line ID, chipset dependent, 0=not routed */
u16 bitmap; /* Available IRQs */
} __attribute__ ((packed)) irq[4];
u8 slot; /* slot number, 0=onboard */
u8 rfu;
} __attribute__ ((packed));
struct irq_routing_table {
u32 signature; /* PIRQ_SIGNATURE should be here */
u16 version; /* PIRQ_VERSION */
u16 size; /* Table size in bytes */
u8 rtr_bus, rtr_devfn; /* Where the interrupt router lies */
u16 exclusive_irqs; /* IRQs devoted exclusively to PCI usage */
u16 rtr_vendor, rtr_device; /* Vendor and device ID of interrupt router */
u32 miniport_data; /* Crap */
u8 rfu[11];
u8 checksum; /* Modulo 256 checksum must give zero */
struct irq_info slots[0];
} __attribute__ ((packed));
#endif /* _SHPCHPRM_NONACPI_H_ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment