Commit 085f1746 authored by Jeff Garzik's avatar Jeff Garzik

Merge mandrakesoft.com:/spare/vanilla/linus-2.5

into mandrakesoft.com:/spare/repo/misc-2.5
parents d7814fa5 08f129d2
...@@ -29,3 +29,14 @@ CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM ...@@ -29,3 +29,14 @@ CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM
When in doubt, say N. When in doubt, say N.
CONFIG_HOTPLUG_PCI_IBM
Say Y here if you have a motherboard with a IBM PCI Hotplug
controller.
This code is also available as a module ( = code which can be
inserted in and removed from the running kernel whenever you want).
The module will be called cpqphp.o. If you want to compile it
as a module, say M here and read <file:Documentation/modules.txt>.
When in doubt, say N.
...@@ -4,9 +4,10 @@ ...@@ -4,9 +4,10 @@
mainmenu_option next_comment mainmenu_option next_comment
comment 'PCI Hotplug Support' comment 'PCI Hotplug Support'
dep_tristate 'Support for PCI Hotplug (EXPERIMENTAL)' CONFIG_HOTPLUG_PCI $CONFIG_DDFS $CONFIG_EXPERIMENTAL dep_tristate 'Support for PCI Hotplug (EXPERIMENTAL)' CONFIG_HOTPLUG_PCI $CONFIG_PCI $CONFIG_EXPERIMENTAL
dep_tristate ' Compaq PCI Hotplug driver' CONFIG_HOTPLUG_PCI_COMPAQ $CONFIG_HOTPLUG_PCI dep_tristate ' Compaq PCI Hotplug driver' CONFIG_HOTPLUG_PCI_COMPAQ $CONFIG_HOTPLUG_PCI $CONFIG_X86
dep_mbool ' Save configuration into NVRAM on Compaq servers' CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM $CONFIG_HOTPLUG_PCI_COMPAQ dep_mbool ' Save configuration into NVRAM on Compaq servers' CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM $CONFIG_HOTPLUG_PCI_COMPAQ
dep_tristate ' IBM PCI Hotplug driver' CONFIG_HOTPLUG_PCI_IBM $CONFIG_HOTPLUG_PCI $CONFIG_X86_IO_APIC $CONFIG_X86
endmenu endmenu
...@@ -4,12 +4,13 @@ ...@@ -4,12 +4,13 @@
O_TARGET := vmlinux-obj.o O_TARGET := vmlinux-obj.o
list-multi := cpqphp.o pci_hotplug.o list-multi := cpqphp.o pci_hotplug.o ibmphp.o
export-objs := pci_hotplug_core.o pci_hotplug_util.o export-objs := pci_hotplug_core.o pci_hotplug_util.o
obj-$(CONFIG_HOTPLUG_PCI) += pci_hotplug.o obj-$(CONFIG_HOTPLUG_PCI) += pci_hotplug.o
obj-$(CONFIG_HOTPLUG_PCI_COMPAQ) += cpqphp.o obj-$(CONFIG_HOTPLUG_PCI_COMPAQ) += cpqphp.o
obj-$(CONFIG_HOTPLUG_PCI_IBM) += ibmphp.o
pci_hotplug-objs := pci_hotplug_core.o \ pci_hotplug-objs := pci_hotplug_core.o \
pci_hotplug_util.o pci_hotplug_util.o
...@@ -19,6 +20,12 @@ cpqphp-objs := cpqphp_core.o \ ...@@ -19,6 +20,12 @@ cpqphp-objs := cpqphp_core.o \
cpqphp_proc.o \ cpqphp_proc.o \
cpqphp_pci.o cpqphp_pci.o
ibmphp-objs := ibmphp_core.o \
ibmphp_ebda.o \
ibmphp_pci.o \
ibmphp_res.o \
ibmphp_hpc.o
ifeq ($(CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM),y) ifeq ($(CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM),y)
cpqphp-objs += cpqphp_nvram.o cpqphp-objs += cpqphp_nvram.o
endif endif
...@@ -32,3 +39,6 @@ pci_hotplug.o: $(pci_hotplug-objs) ...@@ -32,3 +39,6 @@ pci_hotplug.o: $(pci_hotplug-objs)
cpqphp.o: $(cpqphp-objs) cpqphp.o: $(cpqphp-objs)
$(LD) -r -o $@ $(cpqphp-objs) $(LD) -r -o $@ $(cpqphp-objs)
ibmphp.o: $(ibmphp-objs)
$(LD) -r -o $@ $(ibmphp-objs)
...@@ -177,7 +177,7 @@ int cpqhp_proc_remove_ctrl (struct controller *ctrl) ...@@ -177,7 +177,7 @@ int cpqhp_proc_remove_ctrl (struct controller *ctrl)
int cpqhp_proc_init_ctrl (void) int cpqhp_proc_init_ctrl (void)
{ {
ctrl_proc_root = proc_mkdir("driver/hpc", NULL); ctrl_proc_root = proc_mkdir("hpc", proc_root_driver);
if (!ctrl_proc_root) if (!ctrl_proc_root)
return -ENOMEM; return -ENOMEM;
ctrl_proc_root->owner = THIS_MODULE; ctrl_proc_root->owner = THIS_MODULE;
......
#ifndef __IBMPHP_H
#define __IBMPHP_H
/*
* IBM Hot Plug Controller Driver
*
* Written By: Jyoti Shah, Tong Yu, Irene Zubarev, IBM Corporation
*
* Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (c) 2001,2002 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 <gregkh@us.ibm.com>
*
*/
#include "pci_hotplug.h"
extern int ibmphp_debug;
#if !defined(CONFIG_HOTPLUG_PCI_IBM_MODULE)
#define MY_NAME "ibmphpd"
#else
#define MY_NAME THIS_MODULE->name
#endif
#define debug(fmt, arg...) do { if (ibmphp_debug) printk(KERN_DEBUG "%s: " fmt , 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)
/* EBDA stuff */
/***********************************************************
* SLOT CAPABILITY *
***********************************************************/
#define EBDA_SLOT_133_MAX 0x20
#define EBDA_SLOT_100_MAX 0x10
#define EBDA_SLOT_66_MAX 0x02
#define EBDA_SLOT_PCIX_CAP 0x08
/************************************************************
* RESOURE TYPE *
************************************************************/
#define EBDA_RSRC_TYPE_MASK 0x03
#define EBDA_IO_RSRC_TYPE 0x00
#define EBDA_MEM_RSRC_TYPE 0x01
#define EBDA_PFM_RSRC_TYPE 0x03
#define EBDA_RES_RSRC_TYPE 0x02
/*************************************************************
* IO RESTRICTION TYPE *
*************************************************************/
#define EBDA_IO_RESTRI_MASK 0x0c
#define EBDA_NO_RESTRI 0x00
#define EBDA_AVO_VGA_ADDR 0x04
#define EBDA_AVO_VGA_ADDR_AND_ALIA 0x08
#define EBDA_AVO_ISA_ADDR 0x0c
/**************************************************************
* DEVICE TYPE DEF *
**************************************************************/
#define EBDA_DEV_TYPE_MASK 0x10
#define EBDA_PCI_DEV 0x10
#define EBDA_NON_PCI_DEV 0x00
/***************************************************************
* PRIMARY DEF DEFINITION *
***************************************************************/
#define EBDA_PRI_DEF_MASK 0x20
#define EBDA_PRI_PCI_BUS_INFO 0x20
#define EBDA_NORM_DEV_RSRC_INFO 0x00
//--------------------------------------------------------------
// RIO TABLE DATA STRUCTURE
//--------------------------------------------------------------
struct rio_table_hdr {
u8 ver_num;
u8 scal_count;
u8 riodev_count;
u16 offset;
};
//-------------------------------------------------------------
// SCALABILITY DETAIL
//-------------------------------------------------------------
struct scal_detail {
u8 node_id;
u32 cbar;
u8 port0_node_connect;
u8 port0_port_connect;
u8 port1_node_connect;
u8 port1_port_connect;
u8 port2_node_connect;
u8 port2_port_connect;
// struct list_head scal_detail_list;
};
//--------------------------------------------------------------
// RIO DETAIL
//--------------------------------------------------------------
struct rio_detail {
u8 rio_node_id;
u32 bbar;
u8 rio_type;
u8 owner_id;
u8 port0_node_connect;
u8 port0_port_connect;
u8 port1_node_connect;
u8 port1_port_connect;
u8 first_slot_num;
u8 status;
// struct list_head rio_detail_list;
};
/****************************************************************
* HPC DESCRIPTOR NODE *
****************************************************************/
struct ebda_hpc_list {
u8 format;
u16 num_ctlrs;
short phys_addr;
// struct list_head ebda_hpc_list;
};
/*****************************************************************
* IN HPC DATA STRUCTURE, THE ASSOCIATED SLOT AND BUS *
* STRUCTURE *
*****************************************************************/
struct ebda_hpc_slot {
u8 slot_num;
u32 slot_bus_num;
u8 ctl_index;
u8 slot_cap;
};
struct ebda_hpc_bus {
u32 bus_num;
/*
u8 slots_at_33_conv;
u8 slots_at_66_conv;
u8 slots_at_66_pcix;
u8 slots_at_100_pcix;
u8 slots_at_133_pcix;
*/
};
/********************************************************************
* THREE TYPE OF HOT PLUG CONTROLER *
********************************************************************/
struct isa_ctlr_access {
u16 io_start;
u16 io_end;
};
struct pci_ctlr_access {
u8 bus;
u8 dev_fun;
};
struct wpeg_i2c_ctlr_access {
ulong wpegbbar;
u8 i2c_addr;
};
/*************************************************************************
* RSTC DESCRIPTOR NODE *
*************************************************************************/
struct ebda_rsrc_list {
u8 format;
u16 num_entries;
u16 phys_addr;
struct ebda_rsrc_list *next;
};
/***************************************************************************
* PCI RSRC NODE *
***************************************************************************/
struct ebda_pci_rsrc {
u8 rsrc_type;
u8 bus_num;
u8 dev_fun;
ulong start_addr;
ulong end_addr;
struct list_head ebda_pci_rsrc_list;
};
/***********************************************************
* BUS_INFO DATE STRUCTURE *
***********************************************************/
struct bus_info {
u8 slot_min;
u8 slot_max;
u8 slot_count;
u8 busno;
u8 current_speed;
u8 supported_speed;
u8 controller_id;
u8 supported_bus_mode;
u8 current_bus_mode;
u8 index;
struct list_head bus_info_list;
};
/***********************************************************
* GLOBAL VARIABLES *
***********************************************************/
extern struct list_head ibmphp_ebda_pci_rsrc_head;
extern struct list_head ibmphp_slot_head;
/***********************************************************
* FUNCTION PROTOTYPES *
***********************************************************/
extern void ibmphp_free_ebda_hpc_queue (void);
extern int ibmphp_access_ebda (void);
extern struct slot *ibmphp_get_slot_from_physical_num (u8);
extern int ibmphp_get_total_hp_slots (void);
extern void ibmphp_free_ibm_slot (struct slot *);
extern void ibmphp_free_bus_info_queue (void);
extern void ibmphp_free_ebda_pci_rsrc_queue (void);
extern struct bus_info *ibmphp_find_same_bus_num (u32);
extern int ibmphp_get_bus_index (u8);
extern u16 ibmphp_get_total_controllers (void);
/* passed parameters */
#define MEM 0
#define IO 1
#define PFMEM 2
/* bit masks */
#define RESTYPE 0x03
#define IOMASK 0x00 /* will need to take its complement */
#define MMASK 0x01
#define PFMASK 0x03
#define PCIDEVMASK 0x10 /* we should always have PCI devices */
#define PRIMARYBUSMASK 0x20
/* pci specific defines */
#define PCI_VENDOR_ID_NOTVALID 0xFFFF
#define PCI_HEADER_TYPE_MULTIDEVICE 0x80
#define PCI_HEADER_TYPE_MULTIBRIDGE 0x81
#define LATENCY 0x64
#define CACHE 64
#define DEVICEENABLE 0x015F /* CPQ has 0x0157 */
#define IOBRIDGE 0x1000 /* 4k */
#define MEMBRIDGE 0x100000 /* 1M */
/* irqs */
#define SCSI_IRQ 0x09
#define LAN_IRQ 0x0A
#define OTHER_IRQ 0x0B
/* Data Structures */
/* type is of the form x x xx xx
* | | | |_ 00 - I/O, 01 - Memory, 11 - PFMemory
* | | - 00 - No Restrictions, 01 - Avoid VGA, 10 - Avoid
* | | VGA and their aliases, 11 - Avoid ISA
* | - 1 - PCI device, 0 - non pci device
* - 1 - Primary PCI Bus Information (0 if Normal device)
* the IO restrictions [2:3] are only for primary buses
*/
/* we need this struct because there could be several resource blocks
* allocated per primary bus in the EBDA
*/
struct range_node {
int rangeno;
u32 start;
u32 end;
struct range_node *next;
};
struct bus_node {
u8 busno;
int noIORanges;
struct range_node *rangeIO;
int noMemRanges;
struct range_node *rangeMem;
int noPFMemRanges;
struct range_node *rangePFMem;
int needIOUpdate;
int needMemUpdate;
int needPFMemUpdate;
struct resource_node *firstIO; /* first IO resource on the Bus */
struct resource_node *firstMem; /* first memory resource on the Bus */
struct resource_node *firstPFMem; /* first prefetchable memory resource on the Bus */
struct resource_node *firstPFMemFromMem; /* when run out of pfmem available, taking from Mem */
struct list_head bus_list;
};
struct resource_node {
int rangeno;
u8 busno;
u8 devfunc;
u32 start;
u32 end;
u32 len;
int type; /* MEM, IO, PFMEM */
u8 fromMem; /* this is to indicate that the range is from
* from the Memory bucket rather than from PFMem */
struct resource_node *next;
struct resource_node *nextRange; /* for the other mem range on bus */
};
struct res_needed {
u32 mem;
u32 pfmem;
u32 io;
u8 not_correct; /* needed for return */
int devices[32]; /* for device numbers behind this bridge */
};
/* functions */
extern int ibmphp_rsrc_init (void);
extern int ibmphp_add_resource (struct resource_node *);
extern int ibmphp_remove_resource (struct resource_node *);
extern int ibmphp_find_resource (struct bus_node *, u32, struct resource_node **, int);
extern int ibmphp_check_resource (struct resource_node *, u8);
extern int ibmphp_remove_bus (struct bus_node *, u8);
extern void ibmphp_free_resources (void);
extern int ibmphp_add_pfmem_from_mem (struct resource_node *);
extern struct bus_node *ibmphp_find_res_bus (u8);
extern void ibmphp_print_test (void); /* for debugging purposes */
extern void ibmphp_hpc_initvars (void);
extern int ibmphp_hpc_readslot (struct slot *, u8, u8 *);
extern int ibmphp_hpc_writeslot (struct slot *, u8);
extern void ibmphp_lock_operations (void);
extern void ibmphp_unlock_operations (void);
extern int ibmphp_hpc_fillhpslotinfo (struct hotplug_slot *);
extern int ibmphp_hpc_start_poll_thread (void);
extern void ibmphp_hpc_stop_poll_thread (void);
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
// HPC return codes
//----------------------------------------------------------------------------
#define FALSE 0x00
#define TRUE 0x01
#define HPC_ERROR 0xFF
//-----------------------------------------------------------------------------
// BUS INFO
//-----------------------------------------------------------------------------
#define BUS_SPEED 0x30
#define BUS_MODE 0x40
#define BUS_MODE_PCIX 0x01
#define BUS_MODE_PCI 0x00
#define BUS_SPEED_2 0x20
#define BUS_SPEED_1 0x10
#define BUS_SPEED_33 0x00
#define BUS_SPEED_66 0x01
#define BUS_SPEED_100 0x02
#define BUS_SPEED_133 0x03
#define BUS_SPEED_66PCIX 0x04
#define BUS_SPEED_66UNKNOWN 0x05
#define BUS_STATUS_AVAILABLE 0x01
#define BUS_CONTROL_AVAILABLE 0x02
#define SLOT_LATCH_REGS_SUPPORTED 0x10
#define PRGM_MODEL_REV_LEVEL 0xF0
#define MAX_ADAPTER_NONE 0x09
//----------------------------------------------------------------------------
// HPC 'write' operations/commands
//----------------------------------------------------------------------------
// Command Code State Write to reg
// Machine at index
//------------------------- ---- ------- ------------
#define HPC_CTLR_ENABLEIRQ 0x00 // N 15
#define HPC_CTLR_DISABLEIRQ 0x01 // N 15
#define HPC_SLOT_OFF 0x02 // Y 0-14
#define HPC_SLOT_ON 0x03 // Y 0-14
#define HPC_SLOT_ATTNOFF 0x04 // N 0-14
#define HPC_SLOT_ATTNON 0x05 // N 0-14
#define HPC_CTLR_CLEARIRQ 0x06 // N 15
#define HPC_CTLR_RESET 0x07 // Y 15
#define HPC_CTLR_IRQSTEER 0x08 // N 15
#define HPC_BUS_33CONVMODE 0x09 // Y 31-34
#define HPC_BUS_66CONVMODE 0x0A // Y 31-34
#define HPC_BUS_66PCIXMODE 0x0B // Y 31-34
#define HPC_BUS_100PCIXMODE 0x0C // Y 31-34
#define HPC_BUS_133PCIXMODE 0x0D // Y 31-34
#define HPC_ALLSLOT_OFF 0x11 // Y 15
#define HPC_ALLSLOT_ON 0x12 // Y 15
#define HPC_SLOT_BLINKLED 0x13 // N 0-14
//----------------------------------------------------------------------------
// read commands
//----------------------------------------------------------------------------
#define READ_SLOTSTATUS 0x01
#define READ_EXTSLOTSTATUS 0x02
#define READ_BUSSTATUS 0x03
#define READ_CTLRSTATUS 0x04
#define READ_ALLSTAT 0x05
#define READ_ALLSLOT 0x06
#define READ_SLOTLATCHLOWREG 0x07
#define READ_REVLEVEL 0x08
#define READ_HPCOPTIONS 0x09
//----------------------------------------------------------------------------
// slot status
//----------------------------------------------------------------------------
#define HPC_SLOT_POWER 0x01
#define HPC_SLOT_CONNECT 0x02
#define HPC_SLOT_ATTN 0x04
#define HPC_SLOT_PRSNT2 0x08
#define HPC_SLOT_PRSNT1 0x10
#define HPC_SLOT_PWRGD 0x20
#define HPC_SLOT_BUS_SPEED 0x40
#define HPC_SLOT_LATCH 0x80
//----------------------------------------------------------------------------
// HPC_SLOT_POWER status return codes
//----------------------------------------------------------------------------
#define HPC_SLOT_POWER_OFF 0x00
#define HPC_SLOT_POWER_ON 0x01
//----------------------------------------------------------------------------
// HPC_SLOT_CONNECT status return codes
//----------------------------------------------------------------------------
#define HPC_SLOT_CONNECTED 0x00
#define HPC_SLOT_DISCONNECTED 0x01
//----------------------------------------------------------------------------
// HPC_SLOT_ATTN status return codes
//----------------------------------------------------------------------------
#define HPC_SLOT_ATTN_OFF 0x00
#define HPC_SLOT_ATTN_ON 0x01
#define HPC_SLOT_ATTN_BLINK 0x02
//----------------------------------------------------------------------------
// HPC_SLOT_PRSNT status return codes
//----------------------------------------------------------------------------
#define HPC_SLOT_EMPTY 0x00
#define HPC_SLOT_PRSNT_7 0x01
#define HPC_SLOT_PRSNT_15 0x02
#define HPC_SLOT_PRSNT_25 0x03
//----------------------------------------------------------------------------
// HPC_SLOT_PWRGD status return codes
//----------------------------------------------------------------------------
#define HPC_SLOT_PWRGD_FAULT_NONE 0x00
#define HPC_SLOT_PWRGD_GOOD 0x01
//----------------------------------------------------------------------------
// HPC_SLOT_BUS_SPEED status return codes
//----------------------------------------------------------------------------
#define HPC_SLOT_BUS_SPEED_OK 0x00
#define HPC_SLOT_BUS_SPEED_MISM 0x01
//----------------------------------------------------------------------------
// HPC_SLOT_LATCH status return codes
//----------------------------------------------------------------------------
#define HPC_SLOT_LATCH_OPEN 0x01 // NOTE : in PCI spec bit off = open
#define HPC_SLOT_LATCH_CLOSED 0x00 // NOTE : in PCI spec bit on = closed
//----------------------------------------------------------------------------
// extended slot status
//----------------------------------------------------------------------------
#define HPC_SLOT_PCIX 0x01
#define HPC_SLOT_SPEED1 0x02
#define HPC_SLOT_SPEED2 0x04
#define HPC_SLOT_BLINK_ATTN 0x08
#define HPC_SLOT_RSRVD1 0x10
#define HPC_SLOT_RSRVD2 0x20
#define HPC_SLOT_BUS_MODE 0x40
#define HPC_SLOT_RSRVD3 0x80
//----------------------------------------------------------------------------
// HPC_XSLOT_PCIX_CAP status return codes
//----------------------------------------------------------------------------
#define HPC_SLOT_PCIX_NO 0x00
#define HPC_SLOT_PCIX_YES 0x01
//----------------------------------------------------------------------------
// HPC_XSLOT_SPEED status return codes
//----------------------------------------------------------------------------
#define HPC_SLOT_SPEED_33 0x00
#define HPC_SLOT_SPEED_66 0x01
#define HPC_SLOT_SPEED_133 0x02
//----------------------------------------------------------------------------
// HPC_XSLOT_ATTN_BLINK status return codes
//----------------------------------------------------------------------------
#define HPC_SLOT_ATTN_BLINK_OFF 0x00
#define HPC_SLOT_ATTN_BLINK_ON 0x01
//----------------------------------------------------------------------------
// HPC_XSLOT_BUS_MODE status return codes
//----------------------------------------------------------------------------
#define HPC_SLOT_BUS_MODE_OK 0x00
#define HPC_SLOT_BUS_MODE_MISM 0x01
//----------------------------------------------------------------------------
// Controller status
//----------------------------------------------------------------------------
#define HPC_CTLR_WORKING 0x01
#define HPC_CTLR_FINISHED 0x02
#define HPC_CTLR_RESULT0 0x04
#define HPC_CTLR_RESULT1 0x08
#define HPC_CTLR_RESULE2 0x10
#define HPC_CTLR_RESULT3 0x20
#define HPC_CTLR_IRQ_ROUTG 0x40
#define HPC_CTLR_IRQ_PENDG 0x80
//----------------------------------------------------------------------------
// HPC_CTLR_WROKING status return codes
//----------------------------------------------------------------------------
#define HPC_CTLR_WORKING_NO 0x00
#define HPC_CTLR_WORKING_YES 0x01
//----------------------------------------------------------------------------
// HPC_CTLR_FINISHED status return codes
//----------------------------------------------------------------------------
#define HPC_CTLR_FINISHED_NO 0x00
#define HPC_CTLR_FINISHED_YES 0x01
//----------------------------------------------------------------------------
// HPC_CTLR_RESULT status return codes
//----------------------------------------------------------------------------
#define HPC_CTLR_RESULT_SUCCESS 0x00
#define HPC_CTLR_RESULT_FAILED 0x01
#define HPC_CTLR_RESULT_RSVD 0x02
#define HPC_CTLR_RESULT_NORESP 0x03
//----------------------------------------------------------------------------
// macro for slot info
//----------------------------------------------------------------------------
#define SLOT_POWER(s) ((u8) ((s & HPC_SLOT_POWER) \
? HPC_SLOT_POWER_ON : HPC_SLOT_POWER_OFF))
#define SLOT_CONNECT(s) ((u8) ((s & HPC_SLOT_CONNECT) \
? HPC_SLOT_DISCONNECTED : HPC_SLOT_CONNECTED))
#define SLOT_ATTN(s,es) ((u8) ((es & HPC_SLOT_BLINK_ATTN) \
? HPC_SLOT_ATTN_BLINK \
: ((s & HPC_SLOT_ATTN) ? HPC_SLOT_ATTN_ON : HPC_SLOT_ATTN_OFF)))
#define SLOT_PRESENT(s) ((u8) ((s & HPC_SLOT_PRSNT1) \
? ((s & HPC_SLOT_PRSNT2) ? HPC_SLOT_EMPTY : HPC_SLOT_PRSNT_15) \
: ((s & HPC_SLOT_PRSNT2) ? HPC_SLOT_PRSNT_25 : HPC_SLOT_PRSNT_7)))
#define SLOT_PWRGD(s) ((u8) ((s & HPC_SLOT_PWRGD) \
? HPC_SLOT_PWRGD_GOOD : HPC_SLOT_PWRGD_FAULT_NONE))
#define SLOT_BUS_SPEED(s) ((u8) ((s & HPC_SLOT_BUS_SPEED) \
? HPC_SLOT_BUS_SPEED_MISM : HPC_SLOT_BUS_SPEED_OK))
#define SLOT_LATCH(s) ((u8) ((s & HPC_SLOT_LATCH) \
? HPC_SLOT_LATCH_CLOSED : HPC_SLOT_LATCH_OPEN))
#define SLOT_PCIX(es) ((u8) ((es & HPC_SLOT_PCIX) \
? HPC_SLOT_PCIX_YES : HPC_SLOT_PCIX_NO))
#define SLOT_SPEED(es) ((u8) ((es & HPC_SLOT_SPEED2) \
? ((es & HPC_SLOT_SPEED1) ? HPC_SLOT_SPEED_133 \
: HPC_SLOT_SPEED_66) \
: HPC_SLOT_SPEED_33))
#define SLOT_BUS_MODE(es) ((u8) ((es & HPC_SLOT_BUS_MODE) \
? HPC_SLOT_BUS_MODE_MISM : HPC_SLOT_BUS_MODE_OK))
//--------------------------------------------------------------------------
// macro for bus info
//---------------------------------------------------------------------------
#define CURRENT_BUS_SPEED(s) ((u8) (s & BUS_SPEED_2) \
? ((s & BUS_SPEED_1) ? BUS_SPEED_133 : BUS_SPEED_100) \
: ((s & BUS_SPEED_1) ? BUS_SPEED_66 : BUS_SPEED_33))
#define CURRENT_BUS_MODE(s) ((u8) (s & BUS_MODE) ? BUS_MODE_PCIX : BUS_MODE_PCI)
#define READ_BUS_STATUS(s) ((u8) (s->options & BUS_STATUS_AVAILABLE))
#define READ_BUS_MODE(s) ((s->revision & PRGM_MODEL_REV_LEVEL) >= 0x20)
#define SET_BUS_STATUS(s) ((u8) (s->options & BUS_CONTROL_AVAILABLE))
#define READ_SLOT_LATCH(s) ((u8) (s->options & SLOT_LATCH_REGS_SUPPORTED))
//----------------------------------------------------------------------------
// macro for controller info
//----------------------------------------------------------------------------
#define CTLR_WORKING(c) ((u8) ((c & HPC_CTLR_WORKING) \
? HPC_CTLR_WORKING_YES : HPC_CTLR_WORKING_NO))
#define CTLR_FINISHED(c) ((u8) ((c & HPC_CTLR_FINISHED) \
? HPC_CTLR_FINISHED_YES : HPC_CTLR_FINISHED_NO))
#define CTLR_RESULT(c) ((u8) ((c & HPC_CTLR_RESULT1) \
? ((c & HPC_CTLR_RESULT0) ? HPC_CTLR_RESULT_NORESP \
: HPC_CTLR_RESULT_RSVD) \
: ((c & HPC_CTLR_RESULT0) ? HPC_CTLR_RESULT_FAILED \
: HPC_CTLR_RESULT_SUCCESS)))
// command that affect the state machine of HPC
#define NEEDTOCHECK_CMDSTATUS(c) ((c == HPC_SLOT_OFF) || \
(c == HPC_SLOT_ON) || \
(c == HPC_CTLR_RESET) || \
(c == HPC_BUS_33CONVMODE) || \
(c == HPC_BUS_66CONVMODE) || \
(c == HPC_BUS_66PCIXMODE) || \
(c == HPC_BUS_100PCIXMODE) || \
(c == HPC_BUS_133PCIXMODE) || \
(c == HPC_ALLSLOT_OFF) || \
(c == HPC_ALLSLOT_ON))
/* Core part of the driver */
#define ENABLE 1
#define DISABLE 0
#define ADD 0
#define REMOVE 1
#define DETAIL 2
#define MAX_OPS 3
#define CARD_INFO 0x07
#define PCIX133 0x07
#define PCIX66 0x05
#define PCI66 0x04
extern struct pci_ops *ibmphp_pci_root_ops;
/* Variables */
struct pci_func {
struct pci_dev *dev; /* from the OS */
u8 busno;
u8 device;
u8 function;
struct resource_node *io[6];
struct resource_node *mem[6];
struct resource_node *pfmem[6];
struct pci_func *next;
int devices[32]; /* for bridge config */
u8 irq[4]; /* for interrupt config */
u8 bus; /* flag for unconfiguring, to say if PPB */
};
struct slot {
u8 bus;
u8 device;
u8 number;
char name[100];
u32 capabilities;
struct hotplug_slot *hotplug_slot;
struct controller *ctrl;
struct pci_func *func;
u8 irq[4];
u8 flag; /* this is for disable slot and polling */
int bit_mode; /* 0 = 32, 1 = 64 */
u8 ctlr_index;
struct bus_info *bus_on;
struct list_head ibm_slot_list;
u8 status;
u8 ext_status;
u8 busstatus;
};
struct controller {
struct ebda_hpc_slot *slots;
struct ebda_hpc_bus *buses;
u8 revision;
u8 options; /* which options HPC supports */
u8 status;
u8 ctlr_id; /* TONI */
u8 slot_count;
u8 bus_count;
u8 ctlr_relative_id;
u32 irq;
union {
struct isa_ctlr_access isa_ctlr;
struct pci_ctlr_access pci_ctlr;
struct wpeg_i2c_ctlr_access wpeg_ctlr;
} u;
u8 ctlr_type;
struct list_head ebda_hpc_list;
};
/* Functions */
extern int ibmphp_init_devno (struct slot **); /* This function is called from EBDA, so we need it not be static */
extern int ibmphp_disable_slot (struct hotplug_slot *); /* This function is called from HPC, so we need it to not be static */
extern int ibmphp_update_slot_info (struct slot *); /* This function is called from HPC, so we need it to not be be static */
extern int ibmphp_configure_card (struct pci_func *, u8);
extern int ibmphp_unconfigure_card (struct slot **, int);
extern struct hotplug_slot_ops ibmphp_hotplug_slot_ops;
static inline void long_delay (int delay)
{
set_current_state (TASK_INTERRUPTIBLE);
schedule_timeout (delay);
}
#endif //__IBMPHP_H
/*
* IBM Hot Plug Controller Driver
*
* Written By: Chuck Cole, Jyoti Shah, Tong Yu, Irene Zubarev, IBM Corporation
*
* Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (c) 2001,2002 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 <gregkh@us.ibm.com>
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/smp_lock.h>
#include "../../arch/i386/kernel/pci-i386.h" /* for struct irq_routing_table */
#include "ibmphp.h"
#define attn_on(sl) ibmphp_hpc_writeslot (sl, HPC_SLOT_ATTNON)
#define attn_off(sl) ibmphp_hpc_writeslot (sl, HPC_SLOT_ATTNOFF)
#define attn_LED_blink(sl) ibmphp_hpc_writeslot (sl, HPC_SLOT_BLINKLED)
#define get_ctrl_revision(sl, rev) ibmphp_hpc_readslot (sl, READ_REVLEVEL, rev)
#define get_hpc_options(sl, opt) ibmphp_hpc_readslot (sl, READ_HPCOPTIONS, opt)
#define DRIVER_VERSION "0.1"
#define DRIVER_DESC "IBM Hot Plug PCI Controller Driver"
int ibmphp_debug;
static int debug;
MODULE_PARM (debug, "i");
MODULE_PARM_DESC (debug, "Debugging mode enabled or not");
MODULE_LICENSE ("GPL");
MODULE_DESCRIPTION (DRIVER_DESC);
static int *ops[MAX_OPS + 1];
static struct pci_ops *ibmphp_pci_root_ops;
static int max_slots;
static int irqs[16]; /* PIC mode IRQ's we're using so far (in case MPS tables don't provide default info for empty slots */
static int init_flag;
/*
static int get_max_adapter_speed_1 (struct hotplug_slot *, u8 *, u8);
static inline int get_max_adapter_speed (struct hotplug_slot *hs, u8 *value)
{
return get_max_adapter_speed_1 (hs, value, 1);
}
*/
static inline int get_cur_bus_info (struct slot **sl)
{
int rc = 1;
struct slot * slot_cur = *sl;
debug ("options = %x\n", slot_cur->ctrl->options);
debug ("revision = %x\n", slot_cur->ctrl->revision);
if (READ_BUS_STATUS (slot_cur->ctrl))
rc = ibmphp_hpc_readslot (slot_cur, READ_BUSSTATUS, NULL);
if (rc)
return rc;
slot_cur->bus_on->current_speed = CURRENT_BUS_SPEED (slot_cur->busstatus);
if (READ_BUS_MODE (slot_cur->ctrl))
slot_cur->bus_on->current_bus_mode = CURRENT_BUS_MODE (slot_cur->busstatus);
debug ("busstatus = %x, bus_speed = %x, bus_mode = %x\n", slot_cur->busstatus, slot_cur->bus_on->current_speed, slot_cur->bus_on->current_bus_mode);
*sl = slot_cur;
return 0;
}
static inline int slot_update (struct slot **sl)
{
int rc;
rc = ibmphp_hpc_readslot (*sl, READ_ALLSTAT, NULL);
if (rc)
return rc;
if (!init_flag)
return get_cur_bus_info (sl);
return rc;
}
static int get_max_slots (void)
{
struct list_head * tmp;
int slot_count = 0;
list_for_each (tmp, &ibmphp_slot_head)
++slot_count;
return slot_count;
}
/* This routine will put the correct slot->device information per slot. It's
* called from initialization of the slot structures. It will also assign
* interrupt numbers per each slot.
* Parameters: struct slot
* Returns 0 or errors
*/
int ibmphp_init_devno (struct slot **cur_slot)
{
struct irq_routing_table *rtable;
int len;
int loop;
int i;
rtable = pcibios_get_irq_routing_table ();
if (!rtable) {
err ("no BIOS routing table...\n");
return -ENOMEM;
}
len = (rtable->size - sizeof (struct irq_routing_table)) / sizeof (struct irq_info);
if (!len)
return -1;
for (loop = 0; loop < len; loop++) {
if ((*cur_slot)->number == rtable->slots[loop].slot) {
if ((*cur_slot)->bus == rtable->slots[loop].bus) {
(*cur_slot)->device = PCI_SLOT (rtable->slots[loop].devfn);
for (i = 0; i < 4; i++)
(*cur_slot)->irq[i] = IO_APIC_get_PCI_irq_vector ((int) (*cur_slot)->bus, (int) (*cur_slot)->device, i);
debug ("(*cur_slot)->irq[0] = %x\n", (*cur_slot)->irq[0]);
debug ("(*cur_slot)->irq[1] = %x\n", (*cur_slot)->irq[1]);
debug ("(*cur_slot)->irq[2] = %x\n", (*cur_slot)->irq[2]);
debug ("(*cur_slot)->irq[3] = %x\n", (*cur_slot)->irq[3]);
debug ("rtable->exlusive_irqs = %x\n", rtable->exclusive_irqs);
debug ("rtable->slots[loop].irq[0].bitmap = %x\n", rtable->slots[loop].irq[0].bitmap);
debug ("rtable->slots[loop].irq[1].bitmap = %x\n", rtable->slots[loop].irq[1].bitmap);
debug ("rtable->slots[loop].irq[2].bitmap = %x\n", rtable->slots[loop].irq[2].bitmap);
debug ("rtable->slots[loop].irq[3].bitmap = %x\n", rtable->slots[loop].irq[3].bitmap);
debug ("rtable->slots[loop].irq[0].link= %x\n", rtable->slots[loop].irq[0].link);
debug ("rtable->slots[loop].irq[1].link = %x\n", rtable->slots[loop].irq[1].link);
debug ("rtable->slots[loop].irq[2].link = %x\n", rtable->slots[loop].irq[2].link);
debug ("rtable->slots[loop].irq[3].link = %x\n", rtable->slots[loop].irq[3].link);
debug ("end of init_devno\n");
return 0;
}
}
}
return -1;
}
static inline int power_on (struct slot *slot_cur)
{
u8 cmd = HPC_SLOT_ON;
int retval;
retval = ibmphp_hpc_writeslot (slot_cur, cmd);
if (retval) {
err ("power on failed\n");
return retval;
}
if (CTLR_RESULT (slot_cur->ctrl->status)) {
err ("command not completed successfully in power_on \n");
return -EIO;
}
long_delay (3 * HZ); /* For ServeRAID cards, and some 66 PCI */
return 0;
}
static inline int power_off (struct slot *slot_cur)
{
u8 cmd = HPC_SLOT_OFF;
int retval;
retval = ibmphp_hpc_writeslot (slot_cur, cmd);
if (retval) {
err ("power off failed \n");
return retval;
}
if (CTLR_RESULT (slot_cur->ctrl->status)) {
err ("command not completed successfully in power_off \n");
return -EIO;
}
return 0;
}
static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 value)
{
int rc = 0;
struct slot *pslot;
u8 cmd;
int hpcrc = 0;
debug ("set_attention_status - Entry hotplug_slot[%lx] value[%x]\n", (ulong) hotplug_slot, value);
ibmphp_lock_operations ();
cmd = 0x00; // avoid compiler warning
if (hotplug_slot) {
switch (value) {
case HPC_SLOT_ATTN_OFF:
cmd = HPC_SLOT_ATTNOFF;
break;
case HPC_SLOT_ATTN_ON:
cmd = HPC_SLOT_ATTNON;
break;
case HPC_SLOT_ATTN_BLINK:
cmd = HPC_SLOT_BLINKLED;
break;
default:
rc = -ENODEV;
err ("set_attention_status - Error : invalid input [%x]\n", value);
break;
}
if (rc == 0) {
pslot = (struct slot *) hotplug_slot->private;
if (pslot)
hpcrc = ibmphp_hpc_writeslot (pslot, cmd);
else
rc = -ENODEV;
}
} else
rc = -ENODEV;
if (hpcrc)
rc = hpcrc;
ibmphp_unlock_operations ();
debug ("set_attention_status - Exit rc[%d]\n", rc);
return rc;
}
static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 * value)
{
int rc = -ENODEV;
struct slot *pslot;
int hpcrc = 0;
struct slot myslot;
debug ("get_attention_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong) hotplug_slot, (ulong) value);
ibmphp_lock_operations ();
if (hotplug_slot && value) {
pslot = (struct slot *) hotplug_slot->private;
if (pslot) {
memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot));
hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status));
if (!hpcrc)
hpcrc = ibmphp_hpc_readslot (pslot, READ_EXTSLOTSTATUS, &(myslot.ext_status));
if (!hpcrc) {
*value = SLOT_ATTN (myslot.status, myslot.ext_status);
rc = 0;
}
}
} else
rc = -ENODEV;
if (hpcrc)
rc = hpcrc;
ibmphp_unlock_operations ();
debug ("get_attention_status - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value);
return rc;
}
static int get_latch_status (struct hotplug_slot *hotplug_slot, u8 * value)
{
int rc = -ENODEV;
struct slot *pslot;
int hpcrc = 0;
struct slot myslot;
debug ("get_latch_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong) hotplug_slot, (ulong) value);
ibmphp_lock_operations ();
if (hotplug_slot && value) {
pslot = (struct slot *) hotplug_slot->private;
if (pslot) {
memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot));
hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status));
if (!hpcrc) {
*value = SLOT_LATCH (myslot.status);
rc = 0;
}
}
} else
rc = -ENODEV;
if (hpcrc)
rc = hpcrc;
ibmphp_unlock_operations ();
debug ("get_latch_status - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value);
return rc;
}
static int get_power_status (struct hotplug_slot *hotplug_slot, u8 * value)
{
int rc = -ENODEV;
struct slot *pslot;
int hpcrc = 0;
struct slot myslot;
debug ("get_power_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong) hotplug_slot, (ulong) value);
ibmphp_lock_operations ();
if (hotplug_slot && value) {
pslot = (struct slot *) hotplug_slot->private;
if (pslot) {
memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot));
hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status));
if (!hpcrc) {
*value = SLOT_POWER (myslot.status);
rc = 0;
}
}
} else
rc = -ENODEV;
if (hpcrc)
rc = hpcrc;
ibmphp_unlock_operations ();
debug ("get_power_status - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value);
return rc;
}
static int get_adapter_present (struct hotplug_slot *hotplug_slot, u8 * value)
{
int rc = -ENODEV;
struct slot *pslot;
u8 present;
int hpcrc = 0;
struct slot myslot;
debug ("get_adapter_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong) hotplug_slot, (ulong) value);
ibmphp_lock_operations ();
if (hotplug_slot && value) {
pslot = (struct slot *) hotplug_slot->private;
if (pslot) {
memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot));
hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status));
if (!hpcrc) {
present = SLOT_PRESENT (myslot.status);
if (present == HPC_SLOT_EMPTY)
*value = 0;
else
*value = 1;
rc = 0;
}
}
} else
rc = -ENODEV;
if (hpcrc)
rc = hpcrc;
ibmphp_unlock_operations ();
debug ("get_adapter_present - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value);
return rc;
}
/*
static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, u8 * value)
{
int rc = -ENODEV;
struct slot *pslot;
u8 mode = 0;
debug ("get_max_bus_speed - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong)hotplug_slot, (ulong) value);
ibmphp_lock_operations ();
if (hotplug_slot && value) {
pslot = (struct slot *) hotplug_slot->private;
if (pslot) {
rc = 0;
mode = pslot->bus_on->supported_bus_mode;
*value = pslot->bus_on->supported_speed;
*value &= 0x0f;
if (mode == BUS_MODE_PCIX)
*value |= 0x80;
else if (mode == BUS_MODE_PCI)
*value |= 0x40;
else
*value |= 0x20;
}
} else
rc = -ENODEV;
ibmphp_unlock_operations ();
debug ("get_max_bus_speed - Exit rc[%d] value[%x]\n", rc, *value);
return rc;
}
static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, u8 * value)
{
int rc = -ENODEV;
struct slot *pslot;
u8 mode = 0;
debug ("get_cur_bus_speed - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong)hotplug_slot, (ulong) value);
ibmphp_lock_operations ();
if (hotplug_slot && value) {
pslot = (struct slot *) hotplug_slot->private;
if (pslot) {
rc = get_cur_bus_info (&pslot);
if (!rc) {
mode = pslot->bus_on->current_bus_mode;
*value = pslot->bus_on->current_speed;
*value &= 0x0f;
if (mode == BUS_MODE_PCIX)
*value |= 0x80;
else if (mode == BUS_MODE_PCI)
*value |= 0x40;
else
*value |= 0x20;
}
}
} else
rc = -ENODEV;
ibmphp_unlock_operations ();
debug ("get_cur_bus_speed - Exit rc[%d] value[%x]\n", rc, *value);
return rc;
}
static int get_max_adapter_speed_1 (struct hotplug_slot *hotplug_slot, u8 * value, u8 flag)
{
int rc = -ENODEV;
struct slot *pslot;
int hpcrc = 0;
struct slot myslot;
debug ("get_max_adapter_speed - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong)hotplug_slot, (ulong) value);
if (flag)
ibmphp_lock_operations ();
if (hotplug_slot && value) {
pslot = (struct slot *) hotplug_slot->private;
if (pslot) {
memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot));
hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status));
if (!(SLOT_LATCH (myslot.status)) && (SLOT_PRESENT (myslot.status))) {
hpcrc = ibmphp_hpc_readslot (pslot, READ_EXTSLOTSTATUS, &(myslot.ext_status));
if (!hpcrc) {
*value = SLOT_SPEED (myslot.ext_status);
rc = 0;
}
} else {
*value = MAX_ADAPTER_NONE;
rc = 0;
}
}
} else
rc = -ENODEV;
if (hpcrc)
rc = hpcrc;
if (flag)
ibmphp_unlock_operations ();
debug ("get_adapter_present - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value);
return rc;
}
static int get_card_bus_names (struct hotplug_slot *hotplug_slot, char * value)
{
int rc = -ENODEV;
struct slot *pslot = NULL;
struct pci_dev * dev = NULL;
debug ("get_card_bus_names - Entry hotplug_slot[%lx] \n", (ulong)hotplug_slot);
ibmphp_lock_operations ();
if (hotplug_slot) {
pslot = (struct slot *) hotplug_slot->private;
if (pslot) {
rc = 0;
if (pslot->func)
dev = pslot->func->dev;
else
dev = pci_find_slot (pslot->bus, (pslot->device << 3) | (0x00 & 0x7));
if (dev)
snprintf (value, 100, "Bus %d : %s", pslot->bus,dev->name);
else
snprintf (value, 100, "Bus %d", pslot->bus);
}
} else
rc = -ENODEV;
ibmphp_unlock_operations ();
debug ("get_card_bus_names - Exit rc[%d] value[%x]\n", rc, *value);
return rc;
}
*/
/*******************************************************************************
* This routine will initialize the ops data structure used in the validate
* function. It will also power off empty slots that are powered on since BIOS
* leaves those on, albeit disconnected
******************************************************************************/
static int init_ops (void)
{
struct slot *slot_cur;
int retval;
int j;
int rc;
for (j = 0; j < MAX_OPS; j++) {
ops[j] = (int *) kmalloc ((max_slots + 1) * sizeof (int), GFP_KERNEL);
if (!ops[j]) {
err ("out of system memory \n");
return -ENOMEM;
}
}
ops[ADD][0] = 0;
ops[REMOVE][0] = 0;
ops[DETAIL][0] = 0;
for (j = 1; j <= max_slots; j++) {
slot_cur = ibmphp_get_slot_from_physical_num (j);
debug ("BEFORE GETTING SLOT STATUS, slot # %x\n", slot_cur->number);
if (slot_cur->ctrl->revision == 0xFF)
if (get_ctrl_revision (slot_cur, &slot_cur->ctrl->revision))
return -1;
if (slot_cur->bus_on->current_speed == 0xFF)
if (get_cur_bus_info (&slot_cur))
return -1;
if (slot_cur->ctrl->options == 0xFF)
if (get_hpc_options (slot_cur, &slot_cur->ctrl->options))
return -1;
retval = slot_update (&slot_cur);
if (retval)
return retval;
debug ("status = %x, ext_status = %x\n", slot_cur->status, slot_cur->ext_status);
debug ("SLOT_POWER = %x, SLOT_PRESENT = %x, SLOT_LATCH = %x\n", SLOT_POWER (slot_cur->status), SLOT_PRESENT (slot_cur->status), SLOT_LATCH (slot_cur->status));
if (!(SLOT_POWER (slot_cur->status)) && (SLOT_PRESENT (slot_cur->status)) && !(SLOT_LATCH (slot_cur->status)))
/* No power, adapter, and latch closed */
ops[ADD][j] = 1;
else
ops[ADD][j] = 0;
ops[DETAIL][j] = 1;
if ((SLOT_POWER (slot_cur->status)) && (SLOT_PRESENT (slot_cur->status)) && !(SLOT_LATCH (slot_cur->status)))
/*Power,adapter,latch closed */
ops[REMOVE][j] = 1;
else
ops[REMOVE][j] = 0;
if ((SLOT_POWER (slot_cur->status)) && !(SLOT_PRESENT (slot_cur->status)) && !(SLOT_LATCH (slot_cur->status))) {
debug ("BEFORE POWER OFF COMMAND\n");
rc = power_off (slot_cur);
if (rc)
return rc;
/* retval = slot_update (&slot_cur);
* if (retval)
* return retval;
* ibmphp_update_slot_info (slot_cur);
*/
}
}
init_flag = 0;
return 0;
}
/* This operation will check whether the slot is within the bounds and
* the operation is valid to perform on that slot
* Parameters: slot, operation
* Returns: 0 or error codes
*/
static int validate (struct slot *slot_cur, int opn)
{
int number;
int retval;
if (!slot_cur)
return -ENODEV;
number = slot_cur->number;
if ((number > max_slots) || (number < 0))
return -EBADSLT;
debug ("slot_number in validate is %d\n", slot_cur->number);
retval = slot_update (&slot_cur);
if (retval)
return retval;
if (!(SLOT_POWER (slot_cur->status)) && (SLOT_PRESENT (slot_cur->status))
&& !(SLOT_LATCH (slot_cur->status)))
ops[ADD][number] = 1;
else
ops[ADD][number] = 0;
ops[DETAIL][number] = 1;
if ((SLOT_POWER (slot_cur->status)) && (SLOT_PRESENT (slot_cur->status))
&& !(SLOT_LATCH (slot_cur->status)))
ops[REMOVE][number] = 1;
else
ops[REMOVE][number] = 0;
switch (opn) {
case ENABLE:
if (ops[ADD][number])
return 0;
break;
case DISABLE:
if (ops[REMOVE][number])
return 0;
break;
case DETAIL:
if (ops[DETAIL][number])
return 0;
break;
default:
return -EINVAL;
break;
}
err ("validate failed....\n");
return -EINVAL;
}
/********************************************************************************
* This routine is for updating the data structures in the hotplug core
* Parameters: struct slot
* Returns: 0 or error
*******************************************************************************/
int ibmphp_update_slot_info (struct slot *slot_cur)
{
struct hotplug_slot_info *info;
char buffer[10];
int rc;
// u8 bus_speed;
info = kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL);
if (!info) {
err ("out of system memory \n");
return -ENOMEM;
}
snprintf (buffer, 10, "%d", slot_cur->number);
info->power_status = SLOT_POWER (slot_cur->status);
info->attention_status = SLOT_ATTN (slot_cur->status, slot_cur->ext_status);
info->latch_status = SLOT_LATCH (slot_cur->status);
if (!SLOT_PRESENT (slot_cur->status)) {
info->adapter_status = 0;
// info->max_adapter_speed_status = MAX_ADAPTER_NONE;
} else {
info->adapter_status = 1;
// get_max_adapter_speed_1 (slot_cur->hotplug_slot, &info->max_adapter_speed_status, 0);
}
/*
bus_speed = slot_cur->bus_on->current_speed;
bus_speed &= 0x0f;
if (slot_cur->bus_on->current_bus_mode == BUS_MODE_PCIX)
bus_speed |= 0x80;
else if (slot_cur->bus_on->current_bus_mode == BUS_MODE_PCI)
bus_speed |= 0x40;
else
bus_speed |= 0x20;
info->cur_bus_speed_status = bus_speed;
info->max_bus_speed_status = slot_cur->hotplug_slot->info->max_bus_speed_status;
// To do: card_bus_names
*/
rc = pci_hp_change_slot_info (buffer, info);
kfree (info);
return rc;
}
/******************************************************************************
* This function will return the pci_func, given bus and devfunc, or NULL. It
* is called from visit routines
******************************************************************************/
static struct pci_func *ibm_slot_find (u8 busno, u8 device, u8 function)
{
struct pci_func *func_cur;
struct slot *slot_cur;
struct list_head * tmp;
list_for_each (tmp, &ibmphp_slot_head) {
slot_cur = list_entry (tmp, struct slot, ibm_slot_list);
if (slot_cur->func) {
func_cur = slot_cur->func;
while (func_cur) {
if ((func_cur->busno == busno) && (func_cur->device == device) && (func_cur->function == function))
return func_cur;
func_cur = func_cur->next;
}
}
}
return NULL;
}
/* This routine is to find the pci_bus from kernel structures.
* Parameters: bus number
* Returns : pci_bus * or NULL if not found
*/
static struct pci_bus *find_bus (u8 busno)
{
const struct list_head *tmp;
struct pci_bus *bus;
debug ("inside find_bus, busno = %x \n", busno);
list_for_each (tmp, &pci_root_buses) {
bus = (struct pci_bus *) pci_bus_b (tmp);
if (bus)
if (bus->number == busno)
return bus;
}
return NULL;
}
/******************************************************************
* This function is here because we can no longer use pci_root_ops
******************************************************************/
static struct pci_ops *get_root_pci_ops (void)
{
struct pci_bus * bus;
if ((bus = find_bus (0)))
return bus->ops;
return NULL;
}
/*************************************************************
* This routine frees up memory used by struct slot, including
* the pointers to pci_func, bus, hotplug_slot, controller,
* and deregistering from the hotplug core
*************************************************************/
static void free_slots (void)
{
struct slot *slot_cur;
struct list_head * tmp;
struct list_head * next;
list_for_each_safe (tmp, next, &ibmphp_slot_head) {
slot_cur = list_entry (tmp, struct slot, ibm_slot_list);
pci_hp_deregister (slot_cur->hotplug_slot);
if (slot_cur->hotplug_slot) {
kfree (slot_cur->hotplug_slot);
slot_cur->hotplug_slot = NULL;
}
if (slot_cur->ctrl)
slot_cur->ctrl = NULL;
if (slot_cur->bus_on)
slot_cur->bus_on = NULL;
ibmphp_unconfigure_card (&slot_cur, -1); /* we don't want to actually remove the resources, since free_resources will do just that */
kfree (slot_cur);
}
}
static int ibm_is_pci_dev_in_use (struct pci_dev *dev)
{
int i = 0;
int inuse = 0;
if (dev->driver)
return 1;
for (i = 0; !dev->driver && !inuse && (i < 6); i++) {
if (!pci_resource_start (dev, i))
continue;
if (pci_resource_flags (dev, i) & IORESOURCE_IO)
inuse = check_region (pci_resource_start (dev, i), pci_resource_len (dev, i));
else if (pci_resource_flags (dev, i) & IORESOURCE_MEM)
inuse = check_mem_region (pci_resource_start (dev, i), pci_resource_len (dev, i));
}
return inuse;
}
static int ibm_pci_hp_remove_device (struct pci_dev *dev)
{
if (ibm_is_pci_dev_in_use (dev)) {
err ("***Cannot safely power down device -- it appears to be in use***\n");
return -EBUSY;
}
pci_remove_device (dev);
return 0;
}
static int ibm_unconfigure_visit_pci_dev_phase2 (struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_bus)
{
struct pci_dev *dev = wrapped_dev->dev;
struct pci_func *temp_func;
int i = 0;
do {
temp_func = ibm_slot_find (dev->bus->number, dev->devfn >> 3, i++);
} while (temp_func && (temp_func->function != (dev->devfn & 0x07)));
if (dev) {
if (ibm_pci_hp_remove_device (dev) == 0)
kfree (dev); /* Now, remove */
else
return -1;
}
if (temp_func)
temp_func->dev = NULL;
else
err ("No pci_func representation for bus, devfn = %d, %x\n", dev->bus->number, dev->devfn);
return 0;
}
static int ibm_unconfigure_visit_pci_bus_phase2 (struct pci_bus_wrapped *wrapped_bus, struct pci_dev_wrapped *wrapped_dev)
{
struct pci_bus *bus = wrapped_bus->bus;
pci_proc_detach_bus (bus);
/* The cleanup code should live in the kernel... */
bus->self->subordinate = NULL;
/* unlink from parent bus */
list_del (&bus->node);
/* Now, remove */
if (bus)
kfree (bus);
return 0;
}
static int ibm_unconfigure_visit_pci_dev_phase1 (struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_bus)
{
struct pci_dev *dev = wrapped_dev->dev;
debug ("attempting removal of driver for device (%x, %x, %x)\n", dev->bus->number, PCI_SLOT (dev->devfn), PCI_FUNC (dev->devfn));
/* Now, remove the Linux Driver Representation */
if (dev->driver) {
debug ("is there a driver?\n");
if (dev->driver->remove) {
dev->driver->remove (dev);
debug ("driver was properly removed\n");
}
dev->driver = NULL;
}
return ibm_is_pci_dev_in_use (dev);
}
static struct pci_visit ibm_unconfigure_functions_phase1 = {
post_visit_pci_dev: ibm_unconfigure_visit_pci_dev_phase1,
};
static struct pci_visit ibm_unconfigure_functions_phase2 = {
post_visit_pci_bus: ibm_unconfigure_visit_pci_bus_phase2,
post_visit_pci_dev: ibm_unconfigure_visit_pci_dev_phase2,
};
static int ibm_unconfigure_device (struct pci_func *func)
{
int rc = 0;
struct pci_dev_wrapped wrapped_dev;
struct pci_bus_wrapped wrapped_bus;
struct pci_dev *temp;
u8 j;
memset (&wrapped_dev, 0, sizeof (struct pci_dev_wrapped));
memset (&wrapped_bus, 0, sizeof (struct pci_bus_wrapped));
debug ("inside ibm_unconfigure_device\n");
debug ("func->device = %x, func->function = %x\n", func->device, func->function);
debug ("func->device << 3 | 0x0 = %x\n", func->device << 3 | 0x0);
for (j = 0; j < 0x08; j++) {
temp = pci_find_slot (func->busno, (func->device << 3) | j);
if (temp) {
wrapped_dev.dev = temp;
wrapped_bus.bus = temp->bus;
rc = pci_visit_dev (&ibm_unconfigure_functions_phase1, &wrapped_dev, &wrapped_bus);
if (rc)
break;
rc = pci_visit_dev (&ibm_unconfigure_functions_phase2, &wrapped_dev, &wrapped_bus);
if (rc)
break;
}
}
debug ("rc in ibm_unconfigure_device b4 returning is %d \n", rc);
return rc;
}
static int configure_visit_pci_dev (struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_bus)
{
// struct pci_bus *bus = wrapped_bus->bus; /* We don't need this, since we don't create in the else statement */
struct pci_dev *dev = wrapped_dev->dev;
struct pci_func *temp_func;
int i = 0;
do {
temp_func = ibm_slot_find (dev->bus->number, dev->devfn >> 3, i++);
} while (temp_func && (temp_func->function != (dev->devfn & 0x07)));
if (temp_func)
temp_func->dev = dev;
else {
/* This should not really happen, since we create functions
first and then call to configure */
debug (" We shouldn't come here \n");
}
if (temp_func->dev) {
pci_proc_attach_device (temp_func->dev);
pci_announce_device_to_drivers (temp_func->dev);
}
return 0;
}
static struct pci_visit configure_functions = {
visit_pci_dev: configure_visit_pci_dev,
};
static int ibm_configure_device (struct pci_func *func)
{
unsigned char bus;
struct pci_dev dev0;
struct pci_bus *child;
struct pci_dev *temp;
int rc = 0;
struct pci_dev_wrapped wrapped_dev;
struct pci_bus_wrapped wrapped_bus;
memset (&wrapped_dev, 0, sizeof (struct pci_dev_wrapped));
memset (&wrapped_bus, 0, sizeof (struct pci_bus_wrapped));
memset (&dev0, 0, sizeof (struct pci_dev));
if (func->dev == NULL)
func->dev = pci_find_slot (func->busno, (func->device << 3) | (func->function & 0x7));
if (func->dev == NULL) {
dev0.bus = find_bus (func->busno);
dev0.devfn = ((func->device << 3) + (func->function & 0x7));
dev0.sysdata = dev0.bus->sysdata;
func->dev = pci_scan_slot (&dev0);
if (func->dev == NULL) {
err ("ERROR... : pci_dev still NULL \n");
return 0;
}
}
if (func->dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) {
pci_read_config_byte (func->dev, PCI_SECONDARY_BUS, &bus);
child = (struct pci_bus *) pci_add_new_bus (func->dev->bus, (func->dev), bus);
pci_do_scan_bus (child);
}
temp = func->dev;
if (temp) {
wrapped_dev.dev = temp;
wrapped_bus.bus = temp->bus;
rc = pci_visit_dev (&configure_functions, &wrapped_dev, &wrapped_bus);
}
return rc;
}
/*******************************************************
* Returns whether the bus is empty or not
*******************************************************/
static int is_bus_empty (struct slot * slot_cur)
{
int rc;
struct slot * tmp_slot;
u8 i = slot_cur->bus_on->slot_min;
while (i <= slot_cur->bus_on->slot_max) {
if (i == slot_cur->number) {
i++;
continue;
}
tmp_slot = ibmphp_get_slot_from_physical_num (i);
rc = slot_update (&tmp_slot);
if (rc)
return 0;
if (SLOT_PRESENT (tmp_slot->status) && SLOT_POWER (tmp_slot->status))
return 0;
i++;
}
return 1;
}
/***********************************************************
* If the HPC permits and the bus currently empty, tries to set the
* bus speed and mode at the maximum card capability
***********************************************************/
static int set_bus (struct slot * slot_cur)
{
int rc;
u8 speed;
u8 cmd = 0x0;
debug ("%s - entry slot # %d \n", __FUNCTION__, slot_cur->number);
if (SET_BUS_STATUS (slot_cur->ctrl) && is_bus_empty (slot_cur)) {
rc = slot_update (&slot_cur);
if (rc)
return rc;
speed = SLOT_SPEED (slot_cur->ext_status);
debug ("ext_status = %x, speed = %x\n", slot_cur->ext_status, speed);
switch (speed) {
case HPC_SLOT_SPEED_33:
cmd = HPC_BUS_33CONVMODE;
break;
case HPC_SLOT_SPEED_66:
if (SLOT_PCIX (slot_cur->ext_status))
cmd = HPC_BUS_66PCIXMODE;
else
cmd = HPC_BUS_66CONVMODE;
break;
case HPC_SLOT_SPEED_133:
if (slot_cur->bus_on->slot_count > 1)
cmd = HPC_BUS_100PCIXMODE;
else
cmd = HPC_BUS_133PCIXMODE;
break;
default:
err ("wrong slot speed \n");
return -ENODEV;
}
debug ("setting bus speed for slot %d, cmd %x\n", slot_cur->number, cmd);
rc = ibmphp_hpc_writeslot (slot_cur, cmd);
if (rc)
return rc;
}
debug ("%s -Exit \n", __FUNCTION__);
return 0;
}
static inline void print_card_capability (struct slot *slot_cur)
{
info ("capability of the card is ");
if ((slot_cur->ext_status & CARD_INFO) == PCIX133)
info (" 133 MHz PCI-X \n");
else if ((slot_cur->ext_status & CARD_INFO) == PCIX66)
info (" 66 MHz PCI-X \n");
else if ((slot_cur->ext_status & CARD_INFO) == PCI66)
info (" 66 MHz PCI \n");
else
info (" 33 MHz PCI \n");
}
/* This routine will power on the slot, configure the device(s) and find the
* drivers for them.
* Parameters: hotplug_slot
* Returns: 0 or failure codes
*/
static int enable_slot (struct hotplug_slot *hs)
{
int rc, i, rcpr;
struct slot *slot_cur;
u8 function;
u8 faulted = 0;
struct pci_func *tmp_func;
ibmphp_lock_operations ();
debug ("ENABLING SLOT........ \n");
slot_cur = (struct slot *) hs->private;
if ((rc = validate (slot_cur, ENABLE))) {
err ("validate function failed \n");
attn_off (slot_cur); /* need to turn off if was blinking b4 */
attn_on (slot_cur);
rc = slot_update (&slot_cur);
if (rc) {
ibmphp_unlock_operations();
return rc;
}
ibmphp_update_slot_info (slot_cur);
ibmphp_unlock_operations ();
return rc;
}
attn_LED_blink (slot_cur);
rc = set_bus (slot_cur);
if (rc) {
err ("was not able to set the bus \n");
attn_off (slot_cur);
attn_on (slot_cur);
ibmphp_unlock_operations ();
return -ENODEV;
}
rc = power_on (slot_cur);
if (rc) {
err ("something wrong when powering up... please see below for details\n");
/* need to turn off before on, otherwise, blinking overwrites */
attn_off(slot_cur);
attn_on (slot_cur);
if (slot_update (&slot_cur)) {
attn_off (slot_cur);
attn_on (slot_cur);
ibmphp_unlock_operations ();
return -ENODEV;
}
/* Check to see the error of why it failed */
if (!(SLOT_PWRGD (slot_cur->status)))
err ("power fault occured trying to power up \n");
else if (SLOT_BUS_SPEED (slot_cur->status)) {
err ("bus speed mismatch occured. please check current bus speed and card capability \n");
print_card_capability (slot_cur);
} else if (SLOT_BUS_MODE (slot_cur->ext_status))
err ("bus mode mismatch occured. please check current bus mode and card capability \n");
ibmphp_update_slot_info (slot_cur);
ibmphp_unlock_operations ();
return rc;
}
debug ("after power_on\n");
rc = slot_update (&slot_cur);
if (rc) {
attn_off (slot_cur);
attn_on (slot_cur);
rcpr = power_off (slot_cur);
if (rcpr) {
ibmphp_unlock_operations ();
return rcpr;
}
ibmphp_unlock_operations ();
return rc;
}
if (SLOT_POWER (slot_cur->status) && !(SLOT_PWRGD (slot_cur->status))) {
faulted = 1;
err ("power fault occured trying to power up...\n");
} else if (SLOT_POWER (slot_cur->status) && (SLOT_BUS_SPEED (slot_cur->status))) {
faulted = 1;
err ("bus speed mismatch occured. please check current bus speed and card capability \n");
print_card_capability (slot_cur);
}
/* Don't think this case will happen after above checks... but just in case, for paranoia sake */
else if (!(SLOT_POWER (slot_cur->status))) {
err ("power on failed... \n");
faulted = 1;
}
if (faulted) {
attn_off (slot_cur); /* need to turn off b4 on */
attn_on (slot_cur);
rcpr = power_off (slot_cur);
if (rcpr) {
ibmphp_unlock_operations ();
return rcpr;
}
if (slot_update (&slot_cur)) {
ibmphp_unlock_operations ();
return -ENODEV;
}
ibmphp_update_slot_info (slot_cur);
ibmphp_unlock_operations ();
return -EINVAL;
}
slot_cur->func = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL);
if (!slot_cur->func) { /* We cannot do update_slot_info here, since no memory for kmalloc n.e.ways, and update_slot_info allocates some */
err ("out of system memory \n");
attn_off (slot_cur);
attn_on (slot_cur);
rcpr = power_off (slot_cur);
if (rcpr) {
ibmphp_unlock_operations ();
return rcpr;
}
ibmphp_unlock_operations ();
return -ENOMEM;
}
memset (slot_cur->func, 0, sizeof (struct pci_func));
slot_cur->func->busno = slot_cur->bus;
slot_cur->func->device = slot_cur->device;
for (i = 0; i < 4; i++)
slot_cur->func->irq[i] = slot_cur->irq[i];
debug ("b4 configure_card, slot_cur->bus = %x, slot_cur->device = %x\n", slot_cur->bus, slot_cur->device);
if (ibmphp_configure_card (slot_cur->func, slot_cur->number)) {
err ("configure_card was unsuccessful... \n");
ibmphp_unconfigure_card (&slot_cur, 1); /* true because don't need to actually deallocate resources, just remove references */
debug ("after unconfigure_card\n");
slot_cur->func = NULL;
attn_off (slot_cur); /* need to turn off in case was blinking */
attn_on (slot_cur);
rcpr = power_off (slot_cur);
if (rcpr) {
ibmphp_unlock_operations ();
return rcpr;
}
if (slot_update (&slot_cur)) {
ibmphp_unlock_operations();
return -ENODEV;
}
ibmphp_update_slot_info (slot_cur);
ibmphp_unlock_operations ();
return -ENOMEM;
}
function = 0x00;
do {
tmp_func = ibm_slot_find (slot_cur->bus, slot_cur->func->device, function++);
if (tmp_func && !(tmp_func->dev))
ibm_configure_device (tmp_func);
} while (tmp_func);
attn_off (slot_cur);
if (slot_update (&slot_cur)) {
ibmphp_unlock_operations ();
return -EFAULT;
}
ibmphp_print_test ();
rc = ibmphp_update_slot_info (slot_cur);
ibmphp_unlock_operations();
return rc;
}
/**************************************************************
* HOT REMOVING ADAPTER CARD *
* INPUT: POINTER TO THE HOTPLUG SLOT STRUCTURE *
* OUTPUT: SUCCESS 0 ; FAILURE: UNCONFIGURE , VALIDATE *
DISABLE POWER , *
**************************************************************/
int ibmphp_disable_slot (struct hotplug_slot *hotplug_slot)
{
int rc;
struct slot *slot_cur = (struct slot *) hotplug_slot->private;
u8 flag = slot_cur->flag;
slot_cur->flag = TRUE;
debug ("DISABLING SLOT... \n");
ibmphp_lock_operations ();
if (slot_cur == NULL) {
ibmphp_unlock_operations ();
return -ENODEV;
}
if (slot_cur->ctrl == NULL) {
ibmphp_unlock_operations ();
return -ENODEV;
}
if (flag == TRUE) {
rc = validate (slot_cur, DISABLE); /* checking if powered off already & valid slot # */
if (rc) {
/* Need to turn off if was blinking b4 */
attn_off (slot_cur);
attn_on (slot_cur);
if (slot_update (&slot_cur)) {
ibmphp_unlock_operations ();
return -EFAULT;
}
ibmphp_update_slot_info (slot_cur);
ibmphp_unlock_operations ();
return rc;
}
}
attn_LED_blink (slot_cur);
if (slot_cur->func == NULL) {
/* We need this for fncs's that were there on bootup */
slot_cur->func = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL);
if (!slot_cur->func) {
err ("out of system memory \n");
attn_off (slot_cur);
attn_on (slot_cur);
ibmphp_unlock_operations ();
return -ENOMEM;
}
memset (slot_cur->func, 0, sizeof (struct pci_func));
slot_cur->func->busno = slot_cur->bus;
slot_cur->func->device = slot_cur->device;
}
if ((rc = ibm_unconfigure_device (slot_cur->func))) {
err ("removing from kernel failed... \n");
err ("Please check to see if it was statically linked or is in use otherwise. (perhaps the driver is not 'hot-removable')\n");
attn_off (slot_cur);
attn_on (slot_cur);
ibmphp_unlock_operations ();
return rc;
}
rc = ibmphp_unconfigure_card (&slot_cur, 0);
slot_cur->func = NULL;
debug ("in disable_slot. after unconfigure_card \n");
if (rc) {
err ("could not unconfigure card.\n");
attn_off (slot_cur); /* need to turn off if was blinking b4 */
attn_on (slot_cur);
if (slot_update (&slot_cur)) {
ibmphp_unlock_operations ();
return -EFAULT;
}
if (flag)
ibmphp_update_slot_info (slot_cur);
ibmphp_unlock_operations ();
return -EFAULT;
}
rc = ibmphp_hpc_writeslot (hotplug_slot->private, HPC_SLOT_OFF);
if (rc) {
attn_off (slot_cur);
attn_on (slot_cur);
if (slot_update (&slot_cur)) {
ibmphp_unlock_operations ();
return -EFAULT;
}
if (flag)
ibmphp_update_slot_info (slot_cur);
ibmphp_unlock_operations ();
return rc;
}
attn_off (slot_cur);
if (slot_update (&slot_cur)) {
ibmphp_unlock_operations ();
return -EFAULT;
}
if (flag)
rc = ibmphp_update_slot_info (slot_cur);
else
rc = 0;
ibmphp_print_test ();
ibmphp_unlock_operations();
return rc;
}
struct hotplug_slot_ops ibmphp_hotplug_slot_ops = {
owner: THIS_MODULE,
set_attention_status: set_attention_status,
enable_slot: enable_slot,
disable_slot: ibmphp_disable_slot,
hardware_test: NULL,
get_power_status: get_power_status,
get_attention_status: get_attention_status,
get_latch_status: get_latch_status,
get_adapter_status: get_adapter_present,
/* get_max_bus_speed_status: get_max_bus_speed,
get_max_adapter_speed_status: get_max_adapter_speed,
get_cur_bus_speed_status: get_cur_bus_speed,
get_card_bus_names_status: get_card_bus_names,
*/
};
static void ibmphp_unload (void)
{
free_slots ();
debug ("after slots \n");
ibmphp_free_resources ();
debug ("after resources \n");
ibmphp_free_bus_info_queue ();
debug ("after bus info \n");
ibmphp_free_ebda_hpc_queue ();
debug ("after ebda hpc \n");
ibmphp_free_ebda_pci_rsrc_queue ();
debug ("after ebda pci rsrc \n");
}
static int __init ibmphp_init (void)
{
int i = 0;
int rc = 0;
init_flag = 1;
ibmphp_pci_root_ops = get_root_pci_ops ();
if (ibmphp_pci_root_ops == NULL) {
err ("cannot read bus operations... will not be able to read the cards. Please check your system \n");
return -ENODEV;
}
ibmphp_debug = debug;
ibmphp_hpc_initvars ();
for (i = 0; i < 16; i++)
irqs[i] = 0;
if ((rc = ibmphp_access_ebda ())) {
ibmphp_unload ();
return rc;
}
debug ("after ibmphp_access_ebda () \n");
if ((rc = ibmphp_rsrc_init ())) {
ibmphp_unload ();
return rc;
}
debug ("AFTER Resource & EBDA INITIALIZATIONS \n");
max_slots = get_max_slots ();
if (init_ops ()) {
ibmphp_unload ();
return -ENODEV;
}
ibmphp_print_test ();
if ((rc = ibmphp_hpc_start_poll_thread ())) {
ibmphp_unload ();
return -ENODEV;
}
/* lock ourselves into memory with a module count of -1
* so that no one can unload us. */
MOD_DEC_USE_COUNT;
info (DRIVER_DESC " version: " DRIVER_VERSION "\n");
return 0;
}
static void __exit ibmphp_exit (void)
{
ibmphp_hpc_stop_poll_thread ();
debug ("after polling \n");
ibmphp_unload ();
debug ("done \n");
}
module_init (ibmphp_init);
module_exit (ibmphp_exit);
/*
* IBM Hot Plug Controller Driver
*
* Written By: Tong Yu, IBM Corporation
*
* Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (c) 2001,2002 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 <gregkh@us.ibm.com>
*
*/
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/list.h>
#include "ibmphp.h"
/*
* POST builds data blocks(in this data block definition, a char-1
* byte, short(or word)-2 byte, long(dword)-4 byte) in the Extended
* BIOS Data Area which describe the configuration of the hot-plug
* controllers and resources used by the PCI Hot-Plug devices.
*
* This file walks EBDA, maps data block from physical addr,
* reconstruct linked lists about all system resource(MEM, PFM, IO)
* already assigned by POST, as well as linked lists about hot plug
* controllers (ctlr#, slot#, bus&slot features...)
*/
/* Global lists */
LIST_HEAD (ibmphp_ebda_pci_rsrc_head);
LIST_HEAD (ibmphp_slot_head);
/* Local variables */
static struct ebda_hpc_list *hpc_list_ptr;
static struct ebda_rsrc_list *rsrc_list_ptr;
static struct rio_table_hdr *rio_table_ptr;
static LIST_HEAD (ebda_hpc_head);
static LIST_HEAD (bus_info_head);
static void *io_mem;
/* Local functions */
static int ebda_rsrc_controller (void);
static int ebda_rsrc_rsrc (void);
static int ebda_rio_table (void);
static struct slot *alloc_ibm_slot (void)
{
struct slot *slot;
slot = kmalloc (sizeof (struct slot), GFP_KERNEL);
if (!slot)
return NULL;
memset (slot, 0, sizeof (*slot));
return slot;
}
static struct ebda_hpc_list *alloc_ebda_hpc_list (void)
{
struct ebda_hpc_list *list;
list = kmalloc (sizeof (struct ebda_hpc_list), GFP_KERNEL);
if (!list)
return NULL;
memset (list, 0, sizeof (*list));
return list;
}
static struct controller *alloc_ebda_hpc (u32 slot_count, u32 bus_count)
{
struct controller *controller;
struct ebda_hpc_slot *slots;
struct ebda_hpc_bus *buses;
controller = kmalloc (sizeof (struct controller), GFP_KERNEL);
if (!controller)
return NULL;
memset (controller, 0, sizeof (*controller));
slots = kmalloc (sizeof (struct ebda_hpc_slot) * slot_count, GFP_KERNEL);
if (!slots) {
kfree (controller);
return NULL;
}
memset (slots, 0, sizeof (*slots) * slot_count);
controller->slots = slots;
buses = kmalloc (sizeof (struct ebda_hpc_bus) * bus_count, GFP_KERNEL);
if (!buses) {
kfree (controller->slots);
kfree (controller);
return NULL;
}
memset (buses, 0, sizeof (*buses) * bus_count);
controller->buses = buses;
return controller;
}
static void free_ebda_hpc (struct controller *controller)
{
kfree (controller->slots);
controller->slots = NULL;
kfree (controller->buses);
controller->buses = NULL;
kfree (controller);
}
static struct ebda_rsrc_list *alloc_ebda_rsrc_list (void)
{
struct ebda_rsrc_list *list;
list = kmalloc (sizeof (struct ebda_rsrc_list), GFP_KERNEL);
if (!list)
return NULL;
memset (list, 0, sizeof (*list));
return list;
}
static struct ebda_pci_rsrc *alloc_ebda_pci_rsrc (void)
{
struct ebda_pci_rsrc *resource;
resource = kmalloc (sizeof (struct ebda_pci_rsrc), GFP_KERNEL);
if (!resource)
return NULL;
memset (resource, 0, sizeof (*resource));
return resource;
}
static void print_bus_info (void)
{
struct bus_info *ptr;
struct list_head *ptr1;
list_for_each (ptr1, &bus_info_head) {
ptr = list_entry (ptr1, struct bus_info, bus_info_list);
debug ("%s - slot_min = %x\n", __FUNCTION__, ptr->slot_min);
debug ("%s - slot_max = %x\n", __FUNCTION__, ptr->slot_max);
debug ("%s - slot_count = %x\n", __FUNCTION__, ptr->slot_count);
debug ("%s - bus# = %x\n", __FUNCTION__, ptr->busno);
debug ("%s - current_speed = %x\n", __FUNCTION__, ptr->current_speed);
debug ("%s - supported_speed = %x\n", __FUNCTION__, ptr->supported_speed);
debug ("%s - controller_id = %x\n", __FUNCTION__, ptr->controller_id);
debug ("%s - bus_mode = %x\n", __FUNCTION__, ptr->supported_bus_mode);
}
}
static void print_ebda_pci_rsrc (void)
{
struct ebda_pci_rsrc *ptr;
struct list_head *ptr1;
list_for_each (ptr1, &ibmphp_ebda_pci_rsrc_head) {
ptr = list_entry (ptr1, struct ebda_pci_rsrc, ebda_pci_rsrc_list);
debug ("%s - rsrc type: %x bus#: %x dev_func: %x start addr: %lx end addr: %lx\n",
__FUNCTION__, ptr->rsrc_type ,ptr->bus_num, ptr->dev_fun,ptr->start_addr, ptr->end_addr);
}
}
static void print_ebda_hpc (void)
{
struct controller *hpc_ptr;
struct list_head *ptr1;
u16 index;
list_for_each (ptr1, &ebda_hpc_head) {
hpc_ptr = list_entry (ptr1, struct controller, ebda_hpc_list);
for (index = 0; index < hpc_ptr->slot_count; index++) {
debug ("%s - physical slot#: %x\n", __FUNCTION__, hpc_ptr->slots[index].slot_num);
debug ("%s - pci bus# of the slot: %x\n", __FUNCTION__, hpc_ptr->slots[index].slot_bus_num);
debug ("%s - index into ctlr addr: %x\n", __FUNCTION__, hpc_ptr->slots[index].ctl_index);
debug ("%s - cap of the slot: %x\n", __FUNCTION__, hpc_ptr->slots[index].slot_cap);
}
for (index = 0; index < hpc_ptr->bus_count; index++) {
debug ("%s - bus# of each bus controlled by this ctlr: %x\n", __FUNCTION__, hpc_ptr->buses[index].bus_num);
}
debug ("%s - type of hpc: %x\n", __FUNCTION__, hpc_ptr->ctlr_type);
switch (hpc_ptr->ctlr_type) {
case 1:
debug ("%s - bus: %x\n", __FUNCTION__, hpc_ptr->u.pci_ctlr.bus);
debug ("%s - dev_fun: %x\n", __FUNCTION__, hpc_ptr->u.pci_ctlr.dev_fun);
debug ("%s - irq: %x\n", __FUNCTION__, hpc_ptr->irq);
break;
case 0:
debug ("%s - io_start: %x\n", __FUNCTION__, hpc_ptr->u.isa_ctlr.io_start);
debug ("%s - io_end: %x\n", __FUNCTION__, hpc_ptr->u.isa_ctlr.io_end);
debug ("%s - irq: %x\n", __FUNCTION__, hpc_ptr->irq);
break;
case 2:
debug ("%s - wpegbbar: %lx\n", __FUNCTION__, hpc_ptr->u.wpeg_ctlr.wpegbbar);
debug ("%s - i2c_addr: %x\n", __FUNCTION__, hpc_ptr->u.wpeg_ctlr.i2c_addr);
debug ("%s - irq: %x\n", __FUNCTION__, hpc_ptr->irq);
break;
}
}
}
int ibmphp_access_ebda (void)
{
u8 format, num_ctlrs, rio_complete, hs_complete;
u16 ebda_seg, num_entries, next_offset, offset, blk_id, sub_addr, rc, re, rc_id, re_id, base;
rio_complete = 0;
hs_complete = 0;
io_mem = ioremap ((0x40 << 4) + 0x0e, 2);
if (!io_mem )
return -ENOMEM;
ebda_seg = readw (io_mem);
iounmap (io_mem);
debug ("returned ebda segment: %x\n", ebda_seg);
io_mem = ioremap (ebda_seg<<4, 65000);
if (!io_mem )
return -ENOMEM;
next_offset = 0x180;
for (;;) {
offset = next_offset;
next_offset = readw (io_mem + offset); /* offset of next blk */
offset += 2;
if (next_offset == 0) /* 0 indicate it's last blk */
break;
blk_id = readw (io_mem + offset); /* this blk id */
offset += 2;
/* check if it is hot swap block or rio block */
if (blk_id != 0x4853 && blk_id != 0x4752)
continue;
/* found hs table */
if (blk_id == 0x4853) {
debug ("now enter hot swap block---\n");
debug ("hot blk id: %x\n", blk_id);
format = readb (io_mem + offset);
offset += 1;
if (format != 4) {
iounmap (io_mem);
return -ENODEV;
}
debug ("hot blk format: %x\n", format);
/* hot swap sub blk */
base = offset;
sub_addr = base;
re = readw (io_mem + sub_addr); /* next sub blk */
sub_addr += 2;
rc_id = readw (io_mem + sub_addr); /* sub blk id */
sub_addr += 2;
if (rc_id != 0x5243) {
iounmap (io_mem);
return -ENODEV;
}
/* rc sub blk signature */
num_ctlrs = readb (io_mem + sub_addr);
sub_addr += 1;
hpc_list_ptr = alloc_ebda_hpc_list ();
if (!hpc_list_ptr) {
iounmap (io_mem);
return -ENOMEM;
}
hpc_list_ptr->format = format;
hpc_list_ptr->num_ctlrs = num_ctlrs;
hpc_list_ptr->phys_addr = sub_addr; /* offset of RSRC_CONTROLLER blk */
debug ("info about hpc descriptor---\n");
debug ("hot blk format: %x\n", format);
debug ("num of controller: %x\n", num_ctlrs);
debug ("offset of hpc data structure enteries: %x\n ", sub_addr);
sub_addr = base + re; /* re sub blk */
rc = readw (io_mem + sub_addr); /* next sub blk */
sub_addr += 2;
re_id = readw (io_mem + sub_addr); /* sub blk id */
sub_addr += 2;
if (re_id != 0x5245) {
iounmap (io_mem);
return -ENODEV;
}
/* signature of re */
num_entries = readw (io_mem + sub_addr);
sub_addr += 2; /* offset of RSRC_ENTRIES blk */
rsrc_list_ptr = alloc_ebda_rsrc_list ();
if (!rsrc_list_ptr ) {
iounmap (io_mem);
return -ENOMEM;
}
rsrc_list_ptr->format = format;
rsrc_list_ptr->num_entries = num_entries;
rsrc_list_ptr->phys_addr = sub_addr;
debug ("info about rsrc descriptor---\n");
debug ("format: %x\n", format);
debug ("num of rsrc: %x\n", num_entries);
debug ("offset of rsrc data structure enteries: %x\n ", sub_addr);
hs_complete = 1;
}
/* found rio table */
else if (blk_id == 0x4752) {
debug ("now enter io table ---\n");
debug ("rio blk id: %x\n", blk_id);
rio_table_ptr = kmalloc (sizeof (struct rio_table_hdr), GFP_KERNEL);
if (!rio_table_ptr)
return -ENOMEM;
memset (rio_table_ptr, 0, sizeof (struct rio_table_hdr) );
rio_table_ptr->ver_num = readb (io_mem + offset);
rio_table_ptr->scal_count = readb (io_mem + offset + 1);
rio_table_ptr->riodev_count = readb (io_mem + offset + 2);
rio_table_ptr->offset = offset +3 ;
debug ("info about rio table hdr ---\n");
debug ("ver_num: %x\nscal_count: %x\nriodev_count: %x\noffset of rio table: %x\n ", rio_table_ptr->ver_num, rio_table_ptr->scal_count, rio_table_ptr->riodev_count, rio_table_ptr->offset);
rio_complete = 1;
}
if (hs_complete && rio_complete) {
rc = ebda_rsrc_controller ();
if (rc) {
iounmap(io_mem);
return rc;
}
rc = ebda_rsrc_rsrc ();
if (rc) {
iounmap(io_mem);
return rc;
}
rc = ebda_rio_table ();
if (rc) {
iounmap(io_mem);
return rc;
}
iounmap (io_mem);
return 0;
}
}
iounmap (io_mem);
return -ENODEV;
}
/*
* map info (ctlr-id, slot count, slot#.. bus count, bus#, ctlr type...) of
* each hpc from physical address to a list of hot plug controllers based on
* hpc descriptors.
*/
static int ebda_rsrc_controller (void)
{
u16 addr, addr_slot, addr_bus;
u8 ctlr_id, temp, bus_index;
u16 ctlr, slot, bus;
u16 slot_num, bus_num, index;
struct hotplug_slot *hp_slot_ptr;
struct controller *hpc_ptr;
struct ebda_hpc_bus *bus_ptr;
struct ebda_hpc_slot *slot_ptr;
struct bus_info *bus_info_ptr1, *bus_info_ptr2;
int rc;
addr = hpc_list_ptr->phys_addr;
for (ctlr = 0; ctlr < hpc_list_ptr->num_ctlrs; ctlr++) {
bus_index = 1;
ctlr_id = readb (io_mem + addr);
addr += 1;
slot_num = readb (io_mem + addr);
addr += 1;
addr_slot = addr; /* offset of slot structure */
addr += (slot_num * 4);
bus_num = readb (io_mem + addr);
addr += 1;
addr_bus = addr; /* offset of bus */
addr += (bus_num * 9); /* offset of ctlr_type */
temp = readb (io_mem + addr);
addr += 1;
/* init hpc structure */
hpc_ptr = alloc_ebda_hpc (slot_num, bus_num);
if (!hpc_ptr ) {
iounmap (io_mem);
return -ENOMEM;
}
hpc_ptr->ctlr_id = ctlr_id;
hpc_ptr->ctlr_relative_id = ctlr;
hpc_ptr->slot_count = slot_num;
hpc_ptr->bus_count = bus_num;
debug ("now enter ctlr data struture ---\n");
debug ("ctlr id: %x\n", ctlr_id);
debug ("ctlr_relative_id: %x\n", hpc_ptr->ctlr_relative_id);
debug ("count of slots controlled by this ctlr: %x\n", slot_num);
debug ("count of buses controlled by this ctlr: %x\n", bus_num);
/* init slot structure, fetch slot, bus, cap... */
slot_ptr = hpc_ptr->slots;
for (slot = 0; slot < slot_num; slot++) {
slot_ptr->slot_num = readb (io_mem + addr_slot);
slot_ptr->slot_bus_num = readb (io_mem + addr_slot + slot_num);
slot_ptr->ctl_index = readb (io_mem + addr_slot + 2*slot_num);
slot_ptr->slot_cap = readb (io_mem + addr_slot + 3*slot_num);
// create bus_info lined list --- if only one slot per bus: slot_min = slot_max
bus_info_ptr2 = ibmphp_find_same_bus_num (slot_ptr->slot_bus_num);
if (!bus_info_ptr2) {
bus_info_ptr1 = (struct bus_info *) kmalloc (sizeof (struct bus_info), GFP_KERNEL);
if (!bus_info_ptr1) {
iounmap (io_mem);
return -ENOMEM;
}
memset (bus_info_ptr1, 0, sizeof (struct bus_info));
bus_info_ptr1->slot_min = slot_ptr->slot_num;
bus_info_ptr1->slot_max = slot_ptr->slot_num;
bus_info_ptr1->slot_count += 1;
bus_info_ptr1->busno = slot_ptr->slot_bus_num;
bus_info_ptr1->index = bus_index++;
bus_info_ptr1->current_speed = 0xff;
bus_info_ptr1->current_bus_mode = 0xff;
if ( ((slot_ptr->slot_cap) & EBDA_SLOT_133_MAX) == EBDA_SLOT_133_MAX )
bus_info_ptr1->supported_speed = 3;
else if ( ((slot_ptr->slot_cap) & EBDA_SLOT_100_MAX) == EBDA_SLOT_100_MAX )
bus_info_ptr1->supported_speed = 2;
else if ( ((slot_ptr->slot_cap) & EBDA_SLOT_66_MAX) == EBDA_SLOT_66_MAX )
bus_info_ptr1->supported_speed = 1;
bus_info_ptr1->controller_id = hpc_ptr->ctlr_id;
if ( ((slot_ptr->slot_cap) & EBDA_SLOT_PCIX_CAP) == EBDA_SLOT_PCIX_CAP )
bus_info_ptr1->supported_bus_mode = 1;
else
bus_info_ptr1->supported_bus_mode =0;
list_add_tail (&bus_info_ptr1->bus_info_list, &bus_info_head);
} else {
bus_info_ptr2->slot_min = min (bus_info_ptr2->slot_min, slot_ptr->slot_num);
bus_info_ptr2->slot_max = max (bus_info_ptr2->slot_max, slot_ptr->slot_num);
bus_info_ptr2->slot_count += 1;
}
// end of creating the bus_info linked list
slot_ptr++;
addr_slot += 1;
}
/* init bus structure */
bus_ptr = hpc_ptr->buses;
for (bus = 0; bus < bus_num; bus++) {
bus_ptr->bus_num = readb (io_mem + addr_bus);
bus_ptr++;
addr_bus += 1;
}
hpc_ptr->ctlr_type = temp;
switch (hpc_ptr->ctlr_type) {
case 1:
hpc_ptr->u.pci_ctlr.bus = readb (io_mem + addr);
hpc_ptr->u.pci_ctlr.dev_fun = readb (io_mem + addr + 1);
hpc_ptr->irq = readb (io_mem + addr + 2);
addr += 3;
break;
case 0:
hpc_ptr->u.isa_ctlr.io_start = readw (io_mem + addr);
hpc_ptr->u.isa_ctlr.io_end = readw (io_mem + addr + 2);
hpc_ptr->irq = readb (io_mem + addr + 4);
addr += 5;
break;
case 2:
hpc_ptr->u.wpeg_ctlr.wpegbbar = readl (io_mem + addr);
hpc_ptr->u.wpeg_ctlr.i2c_addr = readb (io_mem + addr + 4);
/* following 2 lines for testing purpose */
if (hpc_ptr->u.wpeg_ctlr.i2c_addr == 0)
hpc_ptr->ctlr_type = 4;
hpc_ptr->irq = readb (io_mem + addr + 5);
addr += 6;
break;
case 4:
hpc_ptr->u.wpeg_ctlr.wpegbbar = readl (io_mem + addr);
hpc_ptr->u.wpeg_ctlr.i2c_addr = readb (io_mem + addr + 4);
hpc_ptr->irq = readb (io_mem + addr + 5);
addr += 6;
break;
default:
iounmap (io_mem);
return -ENODEV;
}
/* following 3 line: Now our driver only supports I2c ctlrType */
if ((hpc_ptr->ctlr_type != 2) && (hpc_ptr->ctlr_type != 4)) {
err ("Please run this driver on ibm xseries440\n ");
return -ENODEV;
}
hpc_ptr->revision = 0xff;
hpc_ptr->options = 0xff;
// register slots with hpc core as well as create linked list of ibm slot
for (index = 0; index < hpc_ptr->slot_count; index++) {
hp_slot_ptr = (struct hotplug_slot *) kmalloc (sizeof (struct hotplug_slot), GFP_KERNEL);
if (!hp_slot_ptr) {
iounmap (io_mem);
return -ENOMEM;
}
memset (hp_slot_ptr, 0, sizeof (struct hotplug_slot));
hp_slot_ptr->info = (struct hotplug_slot_info *) kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL);
if (!hp_slot_ptr->info) {
iounmap (io_mem);
kfree (hp_slot_ptr);
return -ENOMEM;
}
memset (hp_slot_ptr->info, 0, sizeof (struct hotplug_slot_info));
hp_slot_ptr->name = (char *) kmalloc (10, GFP_KERNEL);
if (!hp_slot_ptr->name) {
iounmap (io_mem);
kfree (hp_slot_ptr->info);
kfree (hp_slot_ptr);
return -ENOMEM;
}
hp_slot_ptr->private = alloc_ibm_slot ();
if (!hp_slot_ptr->private) {
iounmap (io_mem);
kfree (hp_slot_ptr->name);
kfree (hp_slot_ptr->info);
kfree (hp_slot_ptr);
return -ENOMEM;
}
((struct slot *)hp_slot_ptr->private)->flag = TRUE;
snprintf (hp_slot_ptr->name, 10, "%d", hpc_ptr->slots[index].slot_num);
((struct slot *) hp_slot_ptr->private)->capabilities = hpc_ptr->slots[index].slot_cap;
((struct slot *) hp_slot_ptr->private)->bus = hpc_ptr->slots[index].slot_bus_num;
bus_info_ptr1 = ibmphp_find_same_bus_num (hpc_ptr->slots[index].slot_bus_num);
if (!bus_info_ptr1) {
iounmap (io_mem);
return -ENODEV;
}
((struct slot *) hp_slot_ptr->private)->bus_on = bus_info_ptr1;
bus_info_ptr1 = NULL;
((struct slot *) hp_slot_ptr->private)->ctrl = hpc_ptr;
((struct slot *) hp_slot_ptr->private)->ctlr_index = hpc_ptr->slots[index].ctl_index;
((struct slot *) hp_slot_ptr->private)->number = hpc_ptr->slots[index].slot_num;
((struct slot *) hp_slot_ptr->private)->hotplug_slot = hp_slot_ptr;
rc = ibmphp_hpc_fillhpslotinfo (hp_slot_ptr);
if (rc) {
iounmap (io_mem);
return rc;
}
rc = ibmphp_init_devno ((struct slot **) &hp_slot_ptr->private);
if (rc) {
iounmap (io_mem);
return rc;
}
hp_slot_ptr->ops = &ibmphp_hotplug_slot_ops;
pci_hp_register (hp_slot_ptr);
// end of registering ibm slot with hotplug core
list_add (& ((struct slot *)(hp_slot_ptr->private))->ibm_slot_list, &ibmphp_slot_head);
}
print_bus_info ();
list_add (&hpc_ptr->ebda_hpc_list, &ebda_hpc_head );
} /* each hpc */
print_ebda_hpc ();
return 0;
}
/*
* map info (bus, devfun, start addr, end addr..) of i/o, memory,
* pfm from the physical addr to a list of resource.
*/
static int ebda_rsrc_rsrc (void)
{
u16 addr;
short rsrc;
u8 type, rsrc_type;
struct ebda_pci_rsrc *rsrc_ptr;
addr = rsrc_list_ptr->phys_addr;
debug ("now entering rsrc land\n");
debug ("offset of rsrc: %x\n", rsrc_list_ptr->phys_addr);
for (rsrc = 0; rsrc < rsrc_list_ptr->num_entries; rsrc++) {
type = readb (io_mem + addr);
addr += 1;
rsrc_type = type & EBDA_RSRC_TYPE_MASK;
if (rsrc_type == EBDA_IO_RSRC_TYPE) {
rsrc_ptr = alloc_ebda_pci_rsrc ();
if (!rsrc_ptr) {
iounmap (io_mem);
return -ENOMEM;
}
rsrc_ptr->rsrc_type = type;
rsrc_ptr->bus_num = readb (io_mem + addr);
rsrc_ptr->dev_fun = readb (io_mem + addr + 1);
rsrc_ptr->start_addr = readw (io_mem + addr + 2);
rsrc_ptr->end_addr = readw (io_mem + addr + 4);
addr += 6;
debug ("rsrc from io type ----\n");
debug ("rsrc type: %x bus#: %x dev_func: %x start addr: %lx end addr: %lx\n",
rsrc_ptr->rsrc_type, rsrc_ptr->bus_num, rsrc_ptr->dev_fun, rsrc_ptr->start_addr, rsrc_ptr->end_addr);
list_add (&rsrc_ptr->ebda_pci_rsrc_list, &ibmphp_ebda_pci_rsrc_head);
}
if (rsrc_type == EBDA_MEM_RSRC_TYPE || rsrc_type == EBDA_PFM_RSRC_TYPE) {
rsrc_ptr = alloc_ebda_pci_rsrc ();
if (!rsrc_ptr ) {
iounmap (io_mem);
return -ENOMEM;
}
rsrc_ptr->rsrc_type = type;
rsrc_ptr->bus_num = readb (io_mem + addr);
rsrc_ptr->dev_fun = readb (io_mem + addr + 1);
rsrc_ptr->start_addr = readl (io_mem + addr + 2);
rsrc_ptr->end_addr = readl (io_mem + addr + 6);
addr += 10;
debug ("rsrc from mem or pfm ---\n");
debug ("rsrc type: %x bus#: %x dev_func: %x start addr: %lx end addr: %lx\n",
rsrc_ptr->rsrc_type, rsrc_ptr->bus_num, rsrc_ptr->dev_fun, rsrc_ptr->start_addr, rsrc_ptr->end_addr);
list_add (&rsrc_ptr->ebda_pci_rsrc_list, &ibmphp_ebda_pci_rsrc_head);
}
}
kfree (rsrc_list_ptr);
rsrc_list_ptr = NULL;
print_ebda_pci_rsrc ();
return 0;
}
/*
* map info of scalability details and rio details from physical address
*/
static int ebda_rio_table(void)
{
u16 offset;
u8 i;
struct scal_detail *scal_detail_ptr;
struct rio_detail *rio_detail_ptr;
offset = rio_table_ptr->offset;
for (i = 0; i < rio_table_ptr->scal_count; i++) {
scal_detail_ptr = kmalloc (sizeof (struct scal_detail), GFP_KERNEL );
if (!scal_detail_ptr )
return -ENOMEM;
memset (scal_detail_ptr, 0, sizeof (struct scal_detail) );
scal_detail_ptr->node_id = readb (io_mem + offset);
scal_detail_ptr->cbar = readl (io_mem+ offset + 1);
scal_detail_ptr->port0_node_connect = readb (io_mem + 5);
scal_detail_ptr->port0_port_connect = readb (io_mem + 6);
scal_detail_ptr->port1_node_connect = readb (io_mem + 7);
scal_detail_ptr->port1_port_connect = readb (io_mem + 8);
scal_detail_ptr->port2_node_connect = readb (io_mem + 9);
scal_detail_ptr->port2_port_connect = readb (io_mem + 10);
debug ("node_id: %x\ncbar: %x\nport0_node: %x\nport0_port: %x\nport1_node: %x\nport1_port: %x\nport2_node: %x\nport2_port: %x\n", scal_detail_ptr->node_id, scal_detail_ptr->cbar, scal_detail_ptr->port0_node_connect, scal_detail_ptr->port0_port_connect, scal_detail_ptr->port1_node_connect, scal_detail_ptr->port1_port_connect, scal_detail_ptr->port2_node_connect, scal_detail_ptr->port2_port_connect);
// list_add (&scal_detail_ptr->scal_detail_list, &scal_detail_head);
offset += 11;
}
for (i=0; i < rio_table_ptr->riodev_count; i++) {
rio_detail_ptr = kmalloc (sizeof (struct rio_detail), GFP_KERNEL );
if (!rio_detail_ptr )
return -ENOMEM;
memset (rio_detail_ptr, 0, sizeof (struct rio_detail) );
rio_detail_ptr->rio_node_id = readb (io_mem + offset );
rio_detail_ptr->bbar = readl (io_mem + offset + 1);
rio_detail_ptr->rio_type = readb (io_mem + offset + 5);
rio_detail_ptr->owner_id = readb (io_mem + offset + 6);
rio_detail_ptr->port0_node_connect = readb (io_mem + offset + 7);
rio_detail_ptr->port0_port_connect = readb (io_mem + offset + 8);
rio_detail_ptr->port1_node_connect = readb (io_mem + offset + 9);
rio_detail_ptr->port1_port_connect = readb (io_mem + offset + 10);
rio_detail_ptr->first_slot_num = readb (io_mem + offset + 11);
rio_detail_ptr->status = readb (io_mem + offset + 12);
debug ("rio_node_id: %x\nbbar: %x\nrio_type: %x\nowner_id: %x\nport0_node: %x\nport0_port: %x\nport1_node: %x\nport1_port: %x\nfirst_slot_num: %x\nstatus: %x\n", rio_detail_ptr->rio_node_id, rio_detail_ptr->bbar, rio_detail_ptr->rio_type, rio_detail_ptr->owner_id, rio_detail_ptr->port0_node_connect, rio_detail_ptr->port0_port_connect, rio_detail_ptr->port1_node_connect, rio_detail_ptr->port1_port_connect, rio_detail_ptr->first_slot_num, rio_detail_ptr->status);
offset += 13;
}
return 0;
}
u16 ibmphp_get_total_controllers (void)
{
return hpc_list_ptr->num_ctlrs;
}
struct slot *ibmphp_get_slot_from_physical_num (u8 physical_num)
{
struct slot *slot;
struct list_head *list;
list_for_each (list, &ibmphp_slot_head) {
slot = list_entry (list, struct slot, ibm_slot_list);
if (slot->number == physical_num)
return slot;
}
return NULL;
}
/* To find:
* - the smallest slot number
* - the largest slot number
* - the total number of the slots based on each bus
* (if only one slot per bus slot_min = slot_max )
*/
struct bus_info *ibmphp_find_same_bus_num (u32 num)
{
struct bus_info *ptr;
struct list_head *ptr1;
list_for_each (ptr1, &bus_info_head) {
ptr = list_entry (ptr1, struct bus_info, bus_info_list);
if (ptr->busno == num)
return ptr;
}
return NULL;
}
/* Finding relative bus number, in order to map corresponding
* bus register
*/
int ibmphp_get_bus_index (u8 num)
{
struct bus_info *ptr;
struct list_head *ptr1;
list_for_each (ptr1, &bus_info_head) {
ptr = list_entry (ptr1, struct bus_info, bus_info_list);
if (ptr->busno == num)
return ptr->index;
}
return -ENODEV;
}
void ibmphp_free_bus_info_queue (void)
{
struct bus_info *bus_info;
struct list_head *list;
struct list_head *next;
list_for_each_safe (list, next, &bus_info_head ) {
bus_info = list_entry (list, struct bus_info, bus_info_list);
kfree (bus_info);
}
}
/*
* Calculate the total hot pluggable slots controlled by total hpcs
*/
/*
int ibmphp_get_total_hp_slots (void)
{
struct ebda_hpc *ptr;
int slot_num = 0;
ptr = ebda_hpc_head;
while (ptr != NULL) {
slot_num += ptr->slot_count;
ptr = ptr->next;
}
return slot_num;
}
*/
void ibmphp_free_ebda_hpc_queue (void)
{
struct controller *controller;
struct list_head *list;
struct list_head *next;
list_for_each_safe (list, next, &ebda_hpc_head) {
controller = list_entry (list, struct controller, ebda_hpc_list);
free_ebda_hpc (controller);
}
}
void ibmphp_free_ebda_pci_rsrc_queue (void)
{
struct ebda_pci_rsrc *resource;
struct list_head *list;
struct list_head *next;
list_for_each_safe (list, next, &ibmphp_ebda_pci_rsrc_head) {
resource = list_entry (list, struct ebda_pci_rsrc, ebda_pci_rsrc_list);
kfree (resource);
resource = NULL;
}
}
/*
* IBM Hot Plug Controller Driver
*
* Written By: Jyoti Shah, IBM Corporation
*
* Copyright (c) 2001,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 <gregkh@us.ibm.com>
* <jshah@us.ibm.com>
*
*/
//#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/time.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/smp_lock.h>
#include "ibmphp.h"
#define POLL_NO 0x01
#define POLL_YES 0x00
static int to_debug = FALSE;
#define debug_polling(fmt, arg...) do { if (to_debug) debug (fmt, arg); } while (0)
//----------------------------------------------------------------------------
// timeout values
//----------------------------------------------------------------------------
#define CMD_COMPLETE_TOUT_SEC 60 // give HPC 60 sec to finish cmd
#define HPC_CTLR_WORKING_TOUT 60 // give HPC 60 sec to finish cmd
#define HPC_GETACCESS_TIMEOUT 60 // seconds
#define POLL_INTERVAL_SEC 2 // poll HPC every 2 seconds
#define POLL_LATCH_CNT 5 // poll latch 5 times, then poll slots
//----------------------------------------------------------------------------
// Winnipeg Architected Register Offsets
//----------------------------------------------------------------------------
#define WPG_I2CMBUFL_OFFSET 0x08 // I2C Message Buffer Low
#define WPG_I2CMOSUP_OFFSET 0x10 // I2C Master Operation Setup Reg
#define WPG_I2CMCNTL_OFFSET 0x20 // I2C Master Control Register
#define WPG_I2CPARM_OFFSET 0x40 // I2C Parameter Register
#define WPG_I2CSTAT_OFFSET 0x70 // I2C Status Register
//----------------------------------------------------------------------------
// Winnipeg Store Type commands (Add this commands to the register offset)
//----------------------------------------------------------------------------
#define WPG_I2C_AND 0x1000 // I2C AND operation
#define WPG_I2C_OR 0x2000 // I2C OR operation
//----------------------------------------------------------------------------
// Command set for I2C Master Operation Setup Regisetr
//----------------------------------------------------------------------------
#define WPG_READATADDR_MASK 0x00010000 // read,bytes,I2C shifted,index
#define WPG_WRITEATADDR_MASK 0x40010000 // write,bytes,I2C shifted,index
#define WPG_READDIRECT_MASK 0x10010000
#define WPG_WRITEDIRECT_MASK 0x60010000
//----------------------------------------------------------------------------
// bit masks for I2C Master Control Register
//----------------------------------------------------------------------------
#define WPG_I2CMCNTL_STARTOP_MASK 0x00000002 // Start the Operation
//----------------------------------------------------------------------------
//
//----------------------------------------------------------------------------
#define WPG_I2C_IOREMAP_SIZE 0x2044 // size of linear address interval
#define WPG_CTLR_MAX 0x01 // max controllers
#define WPG_SLOT_MAX 0x06 // max slots
#define WPG_CTLR_SLOT_MAX 0x06 // max slots per controller
#define WPG_FIRST_CTLR 0x00 // index of the controller
//----------------------------------------------------------------------------
// command index
//----------------------------------------------------------------------------
#define WPG_1ST_SLOT_INDEX 0x01 // index - 1st slot for ctlr
#define WPG_CTLR_INDEX 0x0F // index - ctlr
#define WPG_1ST_EXTSLOT_INDEX 0x10 // index - 1st ext slot for ctlr
#define WPG_1ST_BUS_INDEX 0x1F // index - 1st bus for ctlr
//----------------------------------------------------------------------------
// macro utilities
//----------------------------------------------------------------------------
// if bits 20,22,25,26,27,29,30 are OFF return TRUE
#define HPC_I2CSTATUS_CHECK(s) ((u8)((s & 0x00000A76) ? FALSE : TRUE))
// return code 0:poll slots, 1-POLL_LATCH_CNT:poll latch register
#define INCREMENT_POLLCNT(i) ((i < POLL_LATCH_CNT) ? i++ : (i=0))
//----------------------------------------------------------------------------
// global variables
//----------------------------------------------------------------------------
static int ibmphp_shutdown;
static int tid_poll;
static int stop_polling; // 2 values: poll, don't poll
static struct semaphore sem_hpcaccess; // lock access to HPC
static struct semaphore semOperations; // lock all operations and
// access to data structures
static struct semaphore sem_exit; // make sure polling thread goes away
static struct semaphore sem_poll; // make sure poll is idle
//----------------------------------------------------------------------------
// local function prototypes
//----------------------------------------------------------------------------
static u8 ctrl_read (struct controller *, void *, u8);
static u8 ctrl_write (struct controller *, void *, u8, u8);
static u8 hpc_writecmdtoindex (u8, u8);
static u8 hpc_readcmdtoindex (u8, u8);
static void get_hpc_access (void);
static void free_hpc_access (void);
static void poll_hpc (void);
static int update_slot (struct slot *, u8);
static int process_changeinstatus (struct slot *, struct slot *);
static int process_changeinlatch (u8, u8);
static int hpc_poll_thread (void *);
static int hpc_wait_ctlr_notworking (int, struct controller *, void *, u8 *);
//----------------------------------------------------------------------------
/*----------------------------------------------------------------------
* Name: ibmphp_hpc_initvars
*
* Action: initialize semaphores and variables
*---------------------------------------------------------------------*/
void ibmphp_hpc_initvars (void)
{
debug ("%s - Entry\n", __FUNCTION__);
init_MUTEX (&sem_hpcaccess);
init_MUTEX (&semOperations);
init_MUTEX_LOCKED (&sem_exit);
init_MUTEX_LOCKED (&sem_poll);
stop_polling = POLL_YES;
to_debug = FALSE;
ibmphp_shutdown = FALSE;
tid_poll = 0;
debug ("%s - Exit\n", __FUNCTION__);
}
/*----------------------------------------------------------------------
* Name: ctrl_read
*
* Action: read from HPC over I2C
*
*---------------------------------------------------------------------*/
static u8 ctrl_read (struct controller *ctlr_ptr, void *WPGBbar, u8 index)
{
u8 status;
int i;
void *wpg_addr; // base addr + offset
ulong wpg_data, // data to/from WPG LOHI format
ultemp, data; // actual data HILO format
debug_polling ("%s - Entry WPGBbar[%lx] index[%x] \n", __FUNCTION__, (ulong) WPGBbar, index);
//--------------------------------------------------------------------
// READ - step 1
// read at address, byte length, I2C address (shifted), index
// or read direct, byte length, index
if (ctlr_ptr->ctlr_type == 0x02) {
data = WPG_READATADDR_MASK;
// fill in I2C address
ultemp = (ulong) ctlr_ptr->u.wpeg_ctlr.i2c_addr;
ultemp = ultemp >> 1;
data |= (ultemp << 8);
// fill in index
data |= (ulong) index;
} else if (ctlr_ptr->ctlr_type == 0x04) {
data = WPG_READDIRECT_MASK;
// fill in index
ultemp = (ulong) index;
ultemp = ultemp << 8;
data |= ultemp;
} else {
err ("this controller type is not supported \n");
return HPC_ERROR;
}
wpg_data = swab32 (data); // swap data before writing
(ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMOSUP_OFFSET;
writel (wpg_data, wpg_addr);
//--------------------------------------------------------------------
// READ - step 2 : clear the message buffer
data = 0x00000000;
wpg_data = swab32 (data);
(ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMBUFL_OFFSET;
writel (wpg_data, wpg_addr);
//--------------------------------------------------------------------
// READ - step 3 : issue start operation, I2C master control bit 30:ON
// 2020 : [20] OR operation at [20] offset 0x20
data = WPG_I2CMCNTL_STARTOP_MASK;
wpg_data = swab32 (data);
(ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMCNTL_OFFSET + (ulong) WPG_I2C_OR;
writel (wpg_data, wpg_addr);
//--------------------------------------------------------------------
// READ - step 4 : wait until start operation bit clears
i = CMD_COMPLETE_TOUT_SEC;
while (i) {
long_delay (1 * HZ / 100);
(ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMCNTL_OFFSET;
wpg_data = readl (wpg_addr);
data = swab32 (wpg_data);
if (!(data & WPG_I2CMCNTL_STARTOP_MASK))
break;
i--;
}
if (i == 0) {
debug ("%s - Error : WPG timeout\n", __FUNCTION__);
return HPC_ERROR;
}
//--------------------------------------------------------------------
// READ - step 5 : read I2C status register
i = CMD_COMPLETE_TOUT_SEC;
while (i) {
long_delay (1 * HZ / 100);
(ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CSTAT_OFFSET;
wpg_data = readl (wpg_addr);
data = swab32 (wpg_data);
if (HPC_I2CSTATUS_CHECK (data))
break;
i--;
}
if (i == 0) {
debug ("ctrl_read - Exit Error:I2C timeout\n");
return HPC_ERROR;
}
//--------------------------------------------------------------------
// READ - step 6 : get DATA
(ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMBUFL_OFFSET;
wpg_data = readl (wpg_addr);
data = swab32 (wpg_data);
status = (u8) data;
debug_polling ("%s - Exit index[%x] status[%x]\n", __FUNCTION__, index, status);
return (status);
}
/*----------------------------------------------------------------------
* Name: ctrl_write
*
* Action: write to HPC over I2C
*
* Return 0 or error codes
*---------------------------------------------------------------------*/
static u8 ctrl_write (struct controller *ctlr_ptr, void *WPGBbar, u8 index, u8 cmd)
{
u8 rc;
void *wpg_addr; // base addr + offset
ulong wpg_data, // data to/from WPG LOHI format
ultemp, data; // actual data HILO format
int i;
debug_polling ("%s - Entry WPGBbar[%lx] index[%x] cmd[%x]\n",
__FUNCTION__, (ulong) WPGBbar, index, cmd);
rc = 0;
//--------------------------------------------------------------------
// WRITE - step 1
// write at address, byte length, I2C address (shifted), index
// or write direct, byte length, index
data = 0x00000000;
if (ctlr_ptr->ctlr_type == 0x02) {
data = WPG_WRITEATADDR_MASK;
// fill in I2C address
ultemp = (ulong) ctlr_ptr->u.wpeg_ctlr.i2c_addr;
ultemp = ultemp >> 1;
data |= (ultemp << 8);
// fill in index
data |= (ulong) index;
} else if (ctlr_ptr->ctlr_type == 0x04) {
data = WPG_WRITEDIRECT_MASK;
// fill in index
ultemp = (ulong) index;
ultemp = ultemp << 8;
data |= ultemp;
} else {
err ("this controller type is not supported \n");
return HPC_ERROR;
}
wpg_data = swab32 (data); // swap data before writing
(ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMOSUP_OFFSET;
writel (wpg_data, wpg_addr);
//--------------------------------------------------------------------
// WRITE - step 2 : clear the message buffer
data = 0x00000000 | (ulong) cmd;
wpg_data = swab32 (data);
(ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMBUFL_OFFSET;
writel (wpg_data, wpg_addr);
//--------------------------------------------------------------------
// WRITE - step 3 : issue start operation,I2C master control bit 30:ON
// 2020 : [20] OR operation at [20] offset 0x20
data = WPG_I2CMCNTL_STARTOP_MASK;
wpg_data = swab32 (data);
(ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMCNTL_OFFSET + (ulong) WPG_I2C_OR;
writel (wpg_data, wpg_addr);
//--------------------------------------------------------------------
// WRITE - step 4 : wait until start operation bit clears
i = CMD_COMPLETE_TOUT_SEC;
while (i) {
long_delay (1 * HZ / 100);
(ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMCNTL_OFFSET;
wpg_data = readl (wpg_addr);
data = swab32 (wpg_data);
if (!(data & WPG_I2CMCNTL_STARTOP_MASK))
break;
i--;
}
if (i == 0) {
debug ("%s - Exit Error:WPG timeout\n", __FUNCTION__);
rc = HPC_ERROR;
}
//--------------------------------------------------------------------
// WRITE - step 5 : read I2C status register
i = CMD_COMPLETE_TOUT_SEC;
while (i) {
long_delay (1 * HZ / 100);
(ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CSTAT_OFFSET;
wpg_data = readl (wpg_addr);
data = swab32 (wpg_data);
if (HPC_I2CSTATUS_CHECK (data))
break;
i--;
}
if (i == 0) {
debug ("ctrl_read - Error : I2C timeout\n");
rc = HPC_ERROR;
}
debug_polling ("%s Exit rc[%x]\n", __FUNCTION__, rc);
return (rc);
}
/*----------------------------------------------------------------------
* Name: hpc_writecmdtoindex()
*
* Action: convert a write command to proper index within a controller
*
* Return index, HPC_ERROR
*---------------------------------------------------------------------*/
static u8 hpc_writecmdtoindex (u8 cmd, u8 index)
{
u8 rc;
switch (cmd) {
case HPC_CTLR_ENABLEIRQ: // 0x00.N.15
case HPC_CTLR_CLEARIRQ: // 0x06.N.15
case HPC_CTLR_RESET: // 0x07.N.15
case HPC_CTLR_IRQSTEER: // 0x08.N.15
case HPC_CTLR_DISABLEIRQ: // 0x01.N.15
case HPC_ALLSLOT_ON: // 0x11.N.15
case HPC_ALLSLOT_OFF: // 0x12.N.15
rc = 0x0F;
break;
case HPC_SLOT_OFF: // 0x02.Y.0-14
case HPC_SLOT_ON: // 0x03.Y.0-14
case HPC_SLOT_ATTNOFF: // 0x04.N.0-14
case HPC_SLOT_ATTNON: // 0x05.N.0-14
case HPC_SLOT_BLINKLED: // 0x13.N.0-14
rc = index;
break;
case HPC_BUS_33CONVMODE:
case HPC_BUS_66CONVMODE:
case HPC_BUS_66PCIXMODE:
case HPC_BUS_100PCIXMODE:
case HPC_BUS_133PCIXMODE:
rc = index + WPG_1ST_BUS_INDEX - 1;
break;
default:
err ("hpc_writecmdtoindex - Error invalid cmd[%x]\n", cmd);
rc = HPC_ERROR;
}
return rc;
}
/*----------------------------------------------------------------------
* Name: hpc_readcmdtoindex()
*
* Action: convert a read command to proper index within a controller
*
* Return index, HPC_ERROR
*---------------------------------------------------------------------*/
static u8 hpc_readcmdtoindex (u8 cmd, u8 index)
{
u8 rc;
switch (cmd) {
case READ_CTLRSTATUS:
rc = 0x0F;
break;
case READ_SLOTSTATUS:
case READ_ALLSTAT:
rc = index;
break;
case READ_EXTSLOTSTATUS:
rc = index + WPG_1ST_EXTSLOT_INDEX;
break;
case READ_BUSSTATUS:
rc = index + WPG_1ST_BUS_INDEX - 1;
break;
case READ_SLOTLATCHLOWREG:
rc = 0x28;
break;
case READ_REVLEVEL:
rc = 0x25;
break;
case READ_HPCOPTIONS:
rc = 0x27;
break;
default:
rc = HPC_ERROR;
}
return rc;
}
/*----------------------------------------------------------------------
* Name: HPCreadslot()
*
* Action: issue a READ command to HPC
*
* Input: pslot - can not be NULL for READ_ALLSTAT
* pstatus - can be NULL for READ_ALLSTAT
*
* Return 0 or error codes
*---------------------------------------------------------------------*/
int ibmphp_hpc_readslot (struct slot * pslot, u8 cmd, u8 * pstatus)
{
void *wpg_bbar;
struct controller *ctlr_ptr;
struct list_head *pslotlist;
u8 index, status;
int rc = 0;
int busindex;
debug_polling ("%s - Entry pslot[%lx] cmd[%x] pstatus[%lx]\n",
__FUNCTION__, (ulong) pslot, cmd, (ulong) pstatus);
if ((pslot == NULL)
|| ((pstatus == NULL) && (cmd != READ_ALLSTAT) && (cmd != READ_BUSSTATUS))) {
rc = -EINVAL;
err ("%s - Error invalid pointer, rc[%d]\n", __FUNCTION__, rc);
return rc;
}
if (cmd == READ_BUSSTATUS) {
busindex = ibmphp_get_bus_index (pslot->bus);
if (busindex < 0) {
rc = -EINVAL;
err ("%s - Exit Error:invalid bus, rc[%d]\n", __FUNCTION__, rc);
return rc;
} else
index = (u8) busindex;
} else
index = pslot->ctlr_index;
index = hpc_readcmdtoindex (cmd, index);
if (index == HPC_ERROR) {
rc = -EINVAL;
err ("%s - Exit Error:invalid index, rc[%d]\n", __FUNCTION__, rc);
return rc;
}
ctlr_ptr = pslot->ctrl;
get_hpc_access ();
//--------------------------------------------------------------------
// map physical address to logical address
//--------------------------------------------------------------------
wpg_bbar = ioremap (ctlr_ptr->u.wpeg_ctlr.wpegbbar, WPG_I2C_IOREMAP_SIZE);
//--------------------------------------------------------------------
// check controller status before reading
//--------------------------------------------------------------------
rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar, &status);
if (!rc) {
switch (cmd) {
case READ_ALLSTAT:
// update the slot structure
pslot->ctrl->status = status;
pslot->status = ctrl_read (ctlr_ptr, wpg_bbar, index);
rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar,
&status);
if (!rc)
pslot->ext_status = ctrl_read (ctlr_ptr, wpg_bbar, index + WPG_1ST_EXTSLOT_INDEX);
break;
case READ_SLOTSTATUS:
// DO NOT update the slot structure
*pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index);
break;
case READ_EXTSLOTSTATUS:
// DO NOT update the slot structure
*pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index);
break;
case READ_CTLRSTATUS:
// DO NOT update the slot structure
*pstatus = status;
break;
case READ_BUSSTATUS:
pslot->busstatus = ctrl_read (ctlr_ptr, wpg_bbar, index);
break;
case READ_REVLEVEL:
*pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index);
break;
case READ_HPCOPTIONS:
*pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index);
break;
case READ_SLOTLATCHLOWREG:
// DO NOT update the slot structure
*pstatus = ctrl_read (ctlr_ptr, wpg_bbar, index);
break;
// Not used
case READ_ALLSLOT:
list_for_each (pslotlist, &ibmphp_slot_head) {
pslot = list_entry (pslotlist, struct slot, ibm_slot_list);
index = pslot->ctlr_index;
rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr,
wpg_bbar, &status);
if (!rc) {
pslot->status = ctrl_read (ctlr_ptr, wpg_bbar, index);
rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT,
ctlr_ptr, wpg_bbar, &status);
if (!rc)
pslot->ext_status =
ctrl_read (ctlr_ptr, wpg_bbar,
index + WPG_1ST_EXTSLOT_INDEX);
} else {
err ("%s - Error ctrl_read failed\n", __FUNCTION__);
rc = -EINVAL;
break;
}
}
break;
default:
rc = -EINVAL;
break;
}
}
//--------------------------------------------------------------------
// cleanup
//--------------------------------------------------------------------
iounmap (wpg_bbar); // remove physical to logical address mapping
free_hpc_access ();
debug_polling ("%s - Exit rc[%d]\n", __FUNCTION__, rc);
return rc;
}
/*----------------------------------------------------------------------
* Name: ibmphp_hpc_writeslot()
*
* Action: issue a WRITE command to HPC
*---------------------------------------------------------------------*/
int ibmphp_hpc_writeslot (struct slot * pslot, u8 cmd)
{
void *wpg_bbar;
struct controller *ctlr_ptr;
u8 index, status;
int busindex;
u8 done;
int rc = 0;
int timeout;
debug_polling ("%s - Entry pslot[%lx] cmd[%x]\n", __FUNCTION__, (ulong) pslot, cmd);
if (pslot == NULL) {
rc = -EINVAL;
err ("%s - Error Exit rc[%d]\n", __FUNCTION__, rc);
return rc;
}
if ((cmd == HPC_BUS_33CONVMODE) || (cmd == HPC_BUS_66CONVMODE) ||
(cmd == HPC_BUS_66PCIXMODE) || (cmd == HPC_BUS_100PCIXMODE) ||
(cmd == HPC_BUS_133PCIXMODE)) {
busindex = ibmphp_get_bus_index (pslot->bus);
if (busindex < 0) {
rc = -EINVAL;
err ("%s - Exit Error:invalid bus, rc[%d]\n", __FUNCTION__, rc);
return rc;
} else
index = (u8) busindex;
} else
index = pslot->ctlr_index;
index = hpc_writecmdtoindex (cmd, index);
if (index == HPC_ERROR) {
rc = -EINVAL;
err ("%s - Error Exit rc[%d]\n", __FUNCTION__, rc);
return rc;
}
ctlr_ptr = pslot->ctrl;
get_hpc_access ();
//--------------------------------------------------------------------
// map physical address to logical address
//--------------------------------------------------------------------
wpg_bbar = ioremap (ctlr_ptr->u.wpeg_ctlr.wpegbbar, WPG_I2C_IOREMAP_SIZE);
debug ("%s - ctlr id[%x] physical[%lx] logical[%lx] i2c[%x]\n", __FUNCTION__,
ctlr_ptr->ctlr_id, (ulong) (ctlr_ptr->u.wpeg_ctlr.wpegbbar), (ulong) wpg_bbar,
ctlr_ptr->u.wpeg_ctlr.i2c_addr);
//--------------------------------------------------------------------
// check controller status before writing
//--------------------------------------------------------------------
rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar, &status);
if (!rc) {
ctrl_write (ctlr_ptr, wpg_bbar, index, cmd);
//--------------------------------------------------------------------
// check controller is still not working on the command
//--------------------------------------------------------------------
timeout = CMD_COMPLETE_TOUT_SEC;
done = FALSE;
while (!done) {
rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar,
&status);
if (!rc) {
if (NEEDTOCHECK_CMDSTATUS (cmd)) {
if (CTLR_FINISHED (status) == HPC_CTLR_FINISHED_YES)
done = TRUE;
} else
done = TRUE;
}
if (!done) {
long_delay (1 * HZ);
if (timeout < 1) {
done = TRUE;
err ("%s - Error command complete timeout\n", __FUNCTION__);
rc = -EFAULT;
} else
timeout--;
}
}
ctlr_ptr->status = status;
}
// cleanup
iounmap (wpg_bbar); // remove physical to logical address mapping
free_hpc_access ();
debug_polling ("%s - Exit rc[%d]\n", __FUNCTION__, rc);
return rc;
}
/*----------------------------------------------------------------------
* Name: get_hpc_access()
*
* Action: make sure only one process can access HPC at one time
*---------------------------------------------------------------------*/
static void get_hpc_access (void)
{
down (&sem_hpcaccess);
}
/*----------------------------------------------------------------------
* Name: free_hpc_access()
*---------------------------------------------------------------------*/
void free_hpc_access (void)
{
up (&sem_hpcaccess);
}
/*----------------------------------------------------------------------
* Name: ibmphp_lock_operations()
*
* Action: make sure only one process can change the data structure
*---------------------------------------------------------------------*/
void ibmphp_lock_operations (void)
{
down (&semOperations);
stop_polling = POLL_NO;
to_debug = TRUE;
/* waiting for polling to actually stop */
down (&sem_poll);
}
/*----------------------------------------------------------------------
* Name: ibmphp_unlock_operations()
*---------------------------------------------------------------------*/
void ibmphp_unlock_operations (void)
{
debug ("%s - Entry\n", __FUNCTION__);
stop_polling = POLL_YES;
to_debug = FALSE;
up (&semOperations);
debug ("%s - Exit\n", __FUNCTION__);
}
/*----------------------------------------------------------------------
* Name: poll_hpc()
*---------------------------------------------------------------------*/
static void poll_hpc (void)
{
struct slot myslot, *pslot = NULL;
struct list_head *pslotlist;
int rc;
u8 oldlatchlow = 0x00;
u8 curlatchlow = 0x00;
int pollcnt = 0;
u8 ctrl_count = 0x00;
debug ("poll_hpc - Entry\n");
while (!ibmphp_shutdown) {
if (stop_polling) {
debug ("poll_hpc - stop_polling\n");
up (&sem_poll);
/* to prevent deadlock */
if (ibmphp_shutdown)
break;
/* to make the thread sleep */
down (&semOperations);
up (&semOperations);
debug ("poll_hpc - after stop_polling sleep\n");
} else {
if (pollcnt) {
// only poll the latch register
oldlatchlow = curlatchlow;
ctrl_count = 0x00;
list_for_each (pslotlist, &ibmphp_slot_head) {
if (ctrl_count >= ibmphp_get_total_controllers())
break;
pslot = list_entry (pslotlist, struct slot, ibm_slot_list);
if (pslot->ctrl->ctlr_relative_id == ctrl_count) {
ctrl_count++;
if (READ_SLOT_LATCH (pslot->ctrl)) {
rc = ibmphp_hpc_readslot (pslot,
READ_SLOTLATCHLOWREG,
&curlatchlow);
if (oldlatchlow != curlatchlow)
process_changeinlatch (oldlatchlow,
curlatchlow);
}
}
}
} else {
list_for_each (pslotlist, &ibmphp_slot_head) {
if (stop_polling)
break;
pslot = list_entry (pslotlist, struct slot, ibm_slot_list);
// make a copy of the old status
memcpy ((void *) &myslot, (void *) pslot,
sizeof (struct slot));
rc = ibmphp_hpc_readslot (pslot, READ_ALLSTAT, NULL);
if ((myslot.status != pslot->status)
|| (myslot.ext_status != pslot->ext_status))
process_changeinstatus (pslot, &myslot);
}
if (!stop_polling) {
ctrl_count = 0x00;
list_for_each (pslotlist, &ibmphp_slot_head) {
if (ctrl_count >= ibmphp_get_total_controllers())
break;
pslot =
list_entry (pslotlist, struct slot,
ibm_slot_list);
if (pslot->ctrl->ctlr_relative_id == ctrl_count) {
ctrl_count++;
if (READ_SLOT_LATCH (pslot->ctrl))
rc = ibmphp_hpc_readslot (pslot,
READ_SLOTLATCHLOWREG,
&curlatchlow);
}
}
}
}
INCREMENT_POLLCNT (pollcnt);
long_delay (POLL_INTERVAL_SEC * HZ); // snooze
}
}
up (&sem_poll);
up (&sem_exit);
debug ("poll_hpc - Exit\n");
}
/* ----------------------------------------------------------------------
* Name: ibmphp_hpc_fillhpslotinfo(hotplug_slot * phpslot)
*
* Action: fill out the hotplug_slot info
*
* Input: pointer to hotplug_slot
*
* Return
* Value: 0 or error codes
*-----------------------------------------------------------------------*/
int ibmphp_hpc_fillhpslotinfo (struct hotplug_slot *phpslot)
{
int rc = 0;
struct slot *pslot;
if (phpslot && phpslot->private) {
pslot = (struct slot *) phpslot->private;
rc = update_slot (pslot, (u8) TRUE);
if (!rc) {
// power - enabled:1 not:0
phpslot->info->power_status = SLOT_POWER (pslot->status);
// attention - off:0, on:1, blinking:2
phpslot->info->attention_status = SLOT_ATTN (pslot->status, pslot->ext_status);
// latch - open:1 closed:0
phpslot->info->latch_status = SLOT_LATCH (pslot->status);
// pci board - present:1 not:0
if (SLOT_PRESENT (pslot->status))
phpslot->info->adapter_status = 1;
else
phpslot->info->adapter_status = 0;
/*
if (pslot->bus_on->supported_bus_mode
&& (pslot->bus_on->supported_speed == BUS_SPEED_66))
phpslot->info->max_bus_speed_status = BUS_SPEED_66PCIX;
else
phpslot->info->max_bus_speed_status = pslot->bus_on->supported_speed;
*/ } else
rc = -EINVAL;
} else
rc = -EINVAL;
return rc;
}
/*----------------------------------------------------------------------
* Name: update_slot
*
* Action: fill out slot status and extended status, controller status
*
* Input: pointer to slot struct
*---------------------------------------------------------------------*/
static int update_slot (struct slot *pslot, u8 update)
{
int rc = 0;
debug ("%s - Entry pslot[%lx]\n", __FUNCTION__, (ulong) pslot);
rc = ibmphp_hpc_readslot (pslot, READ_ALLSTAT, NULL);
debug ("%s - Exit rc[%d]\n", __FUNCTION__, rc);
return rc;
}
/*----------------------------------------------------------------------
* Name: process_changeinstatus
*
* Action: compare old and new slot status, process the change in status
*
* Input: pointer to slot struct, old slot struct
*
* Return 0 or error codes
* Value:
*
* Side
* Effects: None.
*
* Notes:
*---------------------------------------------------------------------*/
static int process_changeinstatus (struct slot *pslot, struct slot *poldslot)
{
u8 status;
int rc = 0;
u8 disable = FALSE;
u8 update = FALSE;
debug ("process_changeinstatus - Entry pslot[%lx], poldslot[%lx]\n", (ulong) pslot,
(ulong) poldslot);
// bit 0 - HPC_SLOT_POWER
if ((pslot->status & 0x01) != (poldslot->status & 0x01))
/* ????????? DO WE NEED TO UPDATE BUS SPEED INFO HERE ??? */
update = TRUE;
// bit 1 - HPC_SLOT_CONNECT
// ignore
// bit 2 - HPC_SLOT_ATTN
if ((pslot->status & 0x04) != (poldslot->status & 0x04))
update = TRUE;
// bit 3 - HPC_SLOT_PRSNT2
// bit 4 - HPC_SLOT_PRSNT1
if (((pslot->status & 0x08) != (poldslot->status & 0x08))
|| ((pslot->status & 0x10) != (poldslot->status & 0x10)))
update = TRUE;
// bit 5 - HPC_SLOT_PWRGD
if ((pslot->status & 0x20) != (poldslot->status & 0x20))
// OFF -> ON: ignore, ON -> OFF: disable slot
if (poldslot->status & 0x20)
disable = TRUE;
// bit 6 - HPC_SLOT_BUS_SPEED
// ignore
// bit 7 - HPC_SLOT_LATCH
if ((pslot->status & 0x80) != (poldslot->status & 0x80)) {
update = TRUE;
// OPEN -> CLOSE
if (pslot->status & 0x80) {
if (SLOT_POWER (pslot->status)) {
// power goes on and off after closing latch
// check again to make sure power is still ON
long_delay (1 * HZ);
rc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &status);
if (SLOT_POWER (status))
update = TRUE;
else // overwrite power in pslot to OFF
pslot->status &= ~HPC_SLOT_POWER;
}
}
// CLOSE -> OPEN
else if ((SLOT_POWER (poldslot->status) == HPC_SLOT_POWER_ON)
|| (SLOT_CONNECT (poldslot->status) == HPC_SLOT_CONNECTED)) {
disable = TRUE;
}
// else - ignore
}
// bit 4 - HPC_SLOT_BLINK_ATTN
if ((pslot->ext_status & 0x08) != (poldslot->ext_status & 0x08))
update = TRUE;
if (disable) {
debug ("process_changeinstatus - disable slot\n");
pslot->flag = FALSE;
rc = ibmphp_disable_slot (pslot->hotplug_slot);
}
if (update || disable) {
ibmphp_update_slot_info (pslot);
}
debug ("%s - Exit rc[%d] disable[%x] update[%x]\n", __FUNCTION__, rc, disable, update);
return rc;
}
/*----------------------------------------------------------------------
* Name: process_changeinlatch
*
* Action: compare old and new latch reg status, process the change
*
* Input: old and current latch register status
*
* Return 0 or error codes
* Value:
*---------------------------------------------------------------------*/
static int process_changeinlatch (u8 old, u8 new)
{
struct slot myslot, *pslot;
u8 i;
u8 mask;
int rc = 0;
debug ("%s - Entry old[%x], new[%x]\n", __FUNCTION__, old, new);
// bit 0 reserved, 0 is LSB, check bit 1-6 for 6 slots
for (i = 1; i <= 6; i++) {
mask = 0x01 << i;
if ((mask & old) != (mask & new)) {
pslot = ibmphp_get_slot_from_physical_num (i);
if (pslot) {
memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot));
rc = ibmphp_hpc_readslot (pslot, READ_ALLSTAT, NULL);
debug ("%s - call process_changeinstatus for slot[%d]\n", __FUNCTION__, i);
process_changeinstatus (pslot, &myslot);
} else {
rc = -EINVAL;
err ("%s - Error bad pointer for slot[%d]\n", __FUNCTION__, i);
}
}
}
debug ("%s - Exit rc[%d]\n", __FUNCTION__, rc);
return rc;
}
/*----------------------------------------------------------------------
* Name: hpc_poll_thread
*
* Action: polling
*
* Return 0
* Value:
*---------------------------------------------------------------------*/
static int hpc_poll_thread (void *data)
{
debug ("%s - Entry\n", __FUNCTION__);
lock_kernel ();
daemonize ();
reparent_to_init ();
// New name
strcpy (current->comm, "hpc_poll");
unlock_kernel ();
poll_hpc ();
tid_poll = 0;
debug ("%s - Exit\n", __FUNCTION__);
return 0;
}
/*----------------------------------------------------------------------
* Name: ibmphp_hpc_start_poll_thread
*
* Action: start polling thread
*---------------------------------------------------------------------*/
int ibmphp_hpc_start_poll_thread (void)
{
int rc = 0;
debug ("ibmphp_hpc_start_poll_thread - Entry\n");
tid_poll = kernel_thread (hpc_poll_thread, 0, 0);
if (tid_poll < 0) {
err ("ibmphp_hpc_start_poll_thread - Error, thread not started\n");
rc = -1;
}
debug ("ibmphp_hpc_start_poll_thread - Exit tid_poll[%d] rc[%d]\n", tid_poll, rc);
return rc;
}
/*----------------------------------------------------------------------
* Name: ibmphp_hpc_stop_poll_thread
*
* Action: stop polling thread and cleanup
*---------------------------------------------------------------------*/
void ibmphp_hpc_stop_poll_thread (void)
{
debug ("ibmphp_hpc_stop_poll_thread - Entry\n");
ibmphp_shutdown = TRUE;
ibmphp_lock_operations ();
// wait for poll thread to exit
down (&sem_exit);
// cleanup
free_hpc_access ();
ibmphp_unlock_operations ();
up (&sem_poll);
up (&sem_exit);
debug ("ibmphp_hpc_stop_poll_thread - Exit\n");
}
/*----------------------------------------------------------------------
* Name: hpc_wait_ctlr_notworking
*
* Action: wait until the controller is in a not working state
*
* Return 0, HPC_ERROR
* Value:
*---------------------------------------------------------------------*/
static int hpc_wait_ctlr_notworking (int timeout, struct controller *ctlr_ptr, void *wpg_bbar,
u8 * pstatus)
{
int rc = 0;
u8 done = FALSE;
debug_polling ("hpc_wait_ctlr_notworking - Entry timeout[%d]\n", timeout);
while (!done) {
*pstatus = ctrl_read (ctlr_ptr, wpg_bbar, WPG_CTLR_INDEX);
if (*pstatus == HPC_ERROR) {
rc = HPC_ERROR;
done = TRUE;
}
if (CTLR_WORKING (*pstatus) == HPC_CTLR_WORKING_NO)
done = TRUE;
if (!done) {
long_delay (1 * HZ);
if (timeout < 1) {
done = TRUE;
err ("HPCreadslot - Error ctlr timeout\n");
rc = HPC_ERROR;
} else
timeout--;
}
}
debug_polling ("hpc_wait_ctlr_notworking - Exit rc[%x] status[%x]\n", rc, *pstatus);
return rc;
}
/*
* IBM Hot Plug Controller Driver
*
* Written By: Irene Zubarev, IBM Corporation
*
* Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (c) 2001,2002 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 <gregkh@us.ibm.com>
*
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/list.h>
#include "ibmphp.h"
static int configure_device(struct pci_func *);
static int configure_bridge(struct pci_func **, u8);
static struct res_needed *scan_behind_bridge(struct pci_func *, u8);
static int add_new_bus (struct bus_node *, struct resource_node *, struct resource_node *, struct resource_node *, u8);
static u8 find_sec_number (u8 primary_busno, u8 slotno);
/*
* NOTE..... If BIOS doesn't provide default routing, we assign:
* 9 for SCSI, 10 for LAN adapters, and 11 for everything else.
* If adapter is bridged, then we assign 11 to it and devices behind it.
* We also assign the same irq numbers for multi function devices.
* These are PIC mode, so shouldn't matter n.e.ways (hopefully)
*/
static void assign_alt_irq (struct pci_func * cur_func, u8 class_code)
{
int j = 0;
for (j = 0; j < 4; j++) {
if (cur_func->irq[j] == 0xff) {
switch (class_code) {
case PCI_BASE_CLASS_STORAGE:
cur_func->irq[j] = SCSI_IRQ;
break;
case PCI_BASE_CLASS_NETWORK:
cur_func->irq[j] = LAN_IRQ;
break;
default:
cur_func->irq[j] = OTHER_IRQ;
break;
}
}
}
}
/*
* Configures the device to be added (will allocate needed resources if it
* can), the device can be a bridge or a regular pci device, can also be
* multi-functional
*
* Input: function to be added
*
* TO DO: The error case with Multifunction device or multi function bridge,
* if there is an error, will need to go through all previous functions and
* unconfigure....or can add some code into unconfigure_card....
*/
int ibmphp_configure_card (struct pci_func *func, u8 slotno)
{
u16 vendor_id;
u32 class;
u8 class_code;
u8 hdr_type, device, sec_number;
u8 function;
struct pci_func *newfunc; /* for multi devices */
struct pci_func *cur_func, *prev_func;
int rc, i, j;
int cleanup_count;
u8 flag;
u8 valid_device = 0x00; /* to see if we are able to read from card any device info at all */
debug ("inside configure_card, func->busno = %x \n", func->busno);
device = func->device;
cur_func = func;
/* We only get bus and device from IRQ routing table. So at this point,
* func->busno is correct, and func->device contains only device (at the 5
* highest bits)
*/
/* For every function on the card */
for (function = 0x00; function < 0x08; function++) {
cur_func->function = function;
debug ("inside the loop, cur_func->busno = %x, cur_func->device = %x, cur_func->funcion = %x\n", cur_func->busno, device, function);
pci_read_config_word_nodev (ibmphp_pci_root_ops, cur_func->busno, device, function, PCI_VENDOR_ID, &vendor_id);
debug ("vendor_id is %x\n", vendor_id);
if (vendor_id != PCI_VENDOR_ID_NOTVALID) {
/* found correct device!!! */
debug ("found valid device, vendor_id = %x\n", vendor_id);
++valid_device;
/* header: x x x x x x x x
* | |___________|=> 1=PPB bridge, 0=normal device, 2=CardBus Bridge
* |_=> 0 = single function device, 1 = multi-function device
*/
pci_read_config_byte_nodev (ibmphp_pci_root_ops, cur_func->busno, device, function, PCI_HEADER_TYPE, &hdr_type);
pci_read_config_dword_nodev (ibmphp_pci_root_ops, cur_func->busno, device, function, PCI_CLASS_REVISION, &class);
class_code = class >> 24;
debug ("hrd_type = %x, class = %x, class_code %x \n", hdr_type, class, class_code);
class >>= 8; /* to take revision out, class = class.subclass.prog i/f */
if (class == PCI_CLASS_NOT_DEFINED_VGA) {
err ("The device %x is VGA compatible and as is not supported for hot plugging. "
"Please choose another device.\n", cur_func->device);
return -ENODEV;
} else if (class == PCI_CLASS_DISPLAY_VGA) {
err ("The device %x is not supported for hot plugging. "
"Please choose another device.\n", cur_func->device);
return -ENODEV;
}
switch (hdr_type) {
case PCI_HEADER_TYPE_NORMAL:
debug ("single device case.... vendor id = %x, hdr_type = %x, class = %x\n", vendor_id, hdr_type, class);
assign_alt_irq (cur_func, class_code);
if ((rc = configure_device (cur_func)) < 0) {
/* We need to do this in case some other BARs were properly inserted */
err ("was not able to configure devfunc %x on bus %x. \n",
cur_func->device, cur_func->busno);
cleanup_count = 6;
goto error;
}
cur_func->next = NULL;
function = 0x8;
break;
case PCI_HEADER_TYPE_MULTIDEVICE:
assign_alt_irq (cur_func, class_code);
if ((rc = configure_device (cur_func)) < 0) {
/* We need to do this in case some other BARs were properly inserted */
err ("was not able to configure devfunc %x on bus %x...bailing out\n",
cur_func->device, cur_func->busno);
cleanup_count = 6;
goto error;
}
newfunc = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL);
if (!newfunc) {
err ("out of system memory \n");
return -ENOMEM;
}
memset (newfunc, 0, sizeof (struct pci_func));
newfunc->busno = cur_func->busno;
newfunc->device = device;
cur_func->next = newfunc;
cur_func = newfunc;
for (j = 0; j < 4; j++)
newfunc->irq[j] = cur_func->irq[j];
break;
case PCI_HEADER_TYPE_MULTIBRIDGE:
class >>= 8;
if (class != PCI_CLASS_BRIDGE_PCI) {
err ("This %x is not PCI-to-PCI bridge, and as is not supported for hot-plugging. "
"Please insert another card.\n", cur_func->device);
return -ENODEV;
}
assign_alt_irq (cur_func, class_code);
rc = configure_bridge (&cur_func, slotno);
if (rc == -ENODEV) {
err ("You chose to insert Single Bridge, or nested bridges, this is not supported...\n");
err ("Bus %x, devfunc %x \n", cur_func->busno, cur_func->device);
return rc;
}
if (rc) {
/* We need to do this in case some other BARs were properly inserted */
err ("was not able to hot-add PPB properly.\n");
func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */
cleanup_count = 2;
goto error;
}
pci_read_config_byte_nodev (ibmphp_pci_root_ops, cur_func->busno, device, function, PCI_SECONDARY_BUS, &sec_number);
flag = FALSE;
for (i = 0; i < 32; i++) {
if (func->devices[i]) {
newfunc = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL);
if (!newfunc) {
err ("out of system memory \n");
return -ENOMEM;
}
memset (newfunc, 0, sizeof (struct pci_func));
newfunc->busno = sec_number;
newfunc->device = (u8) i;
for (j = 0; j < 4; j++)
newfunc->irq[j] = cur_func->irq[j];
if (flag) {
for (prev_func = cur_func; prev_func->next; prev_func = prev_func->next) ;
prev_func->next = newfunc;
} else
cur_func->next = newfunc;
rc = ibmphp_configure_card (newfunc, slotno);
/* This could only happen if kmalloc failed */
if (rc) {
/* We need to do this in case bridge itself got configured properly, but devices behind it failed */
func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */
cleanup_count = 2;
goto error;
}
flag = TRUE;
}
}
newfunc = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL);
if (!newfunc) {
err ("out of system memory \n");
return -ENOMEM;
}
memset (newfunc, 0, sizeof (struct pci_func));
newfunc->busno = cur_func->busno;
newfunc->device = device;
for (j = 0; j < 4; j++)
newfunc->irq[j] = cur_func->irq[j];
for (prev_func = cur_func; prev_func->next; prev_func = prev_func->next) ;
prev_func->next = newfunc;
cur_func = newfunc;
break;
case PCI_HEADER_TYPE_BRIDGE:
class >>= 8;
debug ("class now is %x\n", class);
if (class != PCI_CLASS_BRIDGE_PCI) {
err ("This %x is not PCI-to-PCI bridge, and as is not supported for hot-plugging. "
"Please insert another card.\n", cur_func->device);
return -ENODEV;
}
assign_alt_irq (cur_func, class_code);
debug ("cur_func->busno b4 configure_bridge is %x\n", cur_func->busno);
rc = configure_bridge (&cur_func, slotno);
if (rc == -ENODEV) {
err ("You chose to insert Single Bridge, or nested bridges, this is not supported...\n");
err ("Bus %x, devfunc %x \n", cur_func->busno, cur_func->device);
return rc;
}
if (rc) {
/* We need to do this in case some other BARs were properly inserted */
func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */
err ("was not able to hot-add PPB properly.\n");
cleanup_count = 2;
goto error;
}
debug ("cur_func->busno = %x, device = %x, function = %x\n", cur_func->busno, device, function);
pci_read_config_byte_nodev (ibmphp_pci_root_ops, cur_func->busno, device, function, PCI_SECONDARY_BUS, &sec_number);
debug ("after configuring bridge..., sec_number = %x\n", sec_number);
flag = FALSE;
for (i = 0; i < 32; i++) {
if (func->devices[i]) {
debug ("inside for loop, device is %x\n", i);
newfunc = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL);
if (!newfunc) {
err (" out of system memory \n");
return -ENOMEM;
}
memset (newfunc, 0, sizeof (struct pci_func));
newfunc->busno = sec_number;
newfunc->device = (u8) i;
for (j = 0; j < 4; j++)
newfunc->irq[j] = cur_func->irq[j];
if (flag) {
for (prev_func = cur_func; prev_func->next; prev_func = prev_func->next) ;
prev_func->next = newfunc;
} else
cur_func->next = newfunc;
rc = ibmphp_configure_card (newfunc, slotno);
/* Again, this case should not happen... For complete paranoia, will need to call remove_bus */
if (rc) {
/* We need to do this in case some other BARs were properly inserted */
func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */
cleanup_count = 2;
goto error;
}
flag = TRUE;
}
}
function = 0x8;
break;
default:
err ("MAJOR PROBLEM!!!!, header type not supported? %x\n", hdr_type);
return -ENXIO;
break;
} /* end of switch */
} /* end of valid device */
} /* end of for */
if (!valid_device) {
err ("Cannot find any valid devices on the card. Or unable to read from card.\n");
return -ENODEV;
}
return 0;
error:
for (i = 0; i < cleanup_count; i++) {
if (cur_func->io[i]) {
ibmphp_remove_resource (cur_func->io[i]);
cur_func->io[i] = NULL;
} else if (cur_func->pfmem[i]) {
ibmphp_remove_resource (cur_func->pfmem[i]);
cur_func->pfmem[i] = NULL;
} else if (cur_func->mem[i]) {
ibmphp_remove_resource (cur_func->mem[i]);
cur_func->mem[i] = NULL;
}
}
return rc;
}
/*
* This function configures the pci BARs of a single device.
* Input: pointer to the pci_func
* Output: configured PCI, 0, or error
*/
static int configure_device (struct pci_func *func)
{
u32 bar[6];
u32 address[] = {
PCI_BASE_ADDRESS_0,
PCI_BASE_ADDRESS_1,
PCI_BASE_ADDRESS_2,
PCI_BASE_ADDRESS_3,
PCI_BASE_ADDRESS_4,
PCI_BASE_ADDRESS_5,
0
};
u8 irq;
int count;
int len[6];
struct resource_node *io[6];
struct resource_node *mem[6];
struct resource_node *mem_tmp;
struct resource_node *pfmem[6];
u8 device;
u8 function;
debug ("%s - inside\n", __FUNCTION__);
device = func->device;
function = func->function;
for (count = 0; address[count]; count++) { /* for 6 BARs */
/* not sure if i need this. per scott, said maybe need smth like this
if devices don't adhere 100% to the spec, so don't want to write
to the reserved bits
pcibios_read_config_byte(cur_func->busno, cur_func->device,
PCI_BASE_ADDRESS_0 + 4 * count, &tmp);
if (tmp & 0x01) // IO
pcibios_write_config_dword(cur_func->busno, cur_func->device,
PCI_BASE_ADDRESS_0 + 4 * count, 0xFFFFFFFD);
else // Memory
pcibios_write_config_dword(cur_func->busno, cur_func->device,
PCI_BASE_ADDRESS_0 + 4 * count, 0xFFFFFFFF);
*/
pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], 0xFFFFFFFF);
pci_read_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], &bar[count]);
if (!bar[count]) /* This BAR is not implemented */
continue;
debug ("Device %x BAR %d wants %x\n", func->device, count, bar[count]);
if (bar[count] & PCI_BASE_ADDRESS_SPACE_IO) {
/* This is IO */
debug ("inside IO SPACE\n");
len[count] = bar[count] & 0xFFFFFFFC;
len[count] = ~len[count] + 1;
debug ("len[count] in IO %x, count %d\n", len[count], count);
io[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
if (!io[count]) {
err ("out of system memory \n");
return -ENOMEM;
}
memset (io[count], 0, sizeof (struct resource_node));
io[count]->type = IO;
io[count]->busno = func->busno;
io[count]->devfunc = ((func->device << 3) | (func->function & 0x7));
io[count]->len = len[count];
if (ibmphp_check_resource(io[count], 0) == 0) {
ibmphp_add_resource (io[count]);
func->io[count] = io[count];
} else {
err ("cannot allocate requested io for bus %x device %x function %x len %x\n",
func->busno, func->device, func->function, len[count]);
kfree (io[count]);
return -EIO;
}
pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], func->io[count]->start);
/* _______________This is for debugging purposes only_____________________ */
debug ("b4 writing, the IO address is %x\n", func->io[count]->start);
pci_read_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], &bar[count]);
debug ("after writing.... the start address is %x\n", bar[count]);
/* _________________________________________________________________________*/
} else {
/* This is Memory */
if (bar[count] & PCI_BASE_ADDRESS_MEM_PREFETCH) {
/* pfmem */
debug ("PFMEM SPACE\n");
len[count] = bar[count] & 0xFFFFFFF0;
len[count] = ~len[count] + 1;
debug ("len[count] in PFMEM %x, count %d\n", len[count], count);
pfmem[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
if (!pfmem[count]) {
err ("out of system memory \n");
return -ENOMEM;
}
memset (pfmem[count], 0, sizeof (struct resource_node));
pfmem[count]->type = PFMEM;
pfmem[count]->busno = func->busno;
pfmem[count]->devfunc = ((func->device << 3) | (func->function & 0x7));
pfmem[count]->len = len[count];
pfmem[count]->fromMem = FALSE;
if (ibmphp_check_resource (pfmem[count], 0) == 0) {
ibmphp_add_resource (pfmem[count]);
func->pfmem[count] = pfmem[count];
} else {
mem_tmp = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
if (!mem_tmp) {
err ("out of system memory \n");
kfree (pfmem[count]);
return -ENOMEM;
}
memset (mem_tmp, 0, sizeof (struct resource_node));
mem_tmp->type = MEM;
mem_tmp->busno = pfmem[count]->busno;
mem_tmp->devfunc = pfmem[count]->devfunc;
mem_tmp->len = pfmem[count]->len;
debug ("there's no pfmem... going into mem.\n");
if (ibmphp_check_resource (mem_tmp, 0) == 0) {
ibmphp_add_resource (mem_tmp);
pfmem[count]->fromMem = TRUE;
pfmem[count]->rangeno = mem_tmp->rangeno;
pfmem[count]->start = mem_tmp->start;
pfmem[count]->end = mem_tmp->end;
ibmphp_add_pfmem_from_mem (pfmem[count]);
func->pfmem[count] = pfmem[count];
} else {
err ("cannot allocate requested pfmem for bus %x, device %x, len %x\n",
func->busno, func->device, len[count]);
kfree (mem_tmp);
kfree (pfmem[count]);
return -EIO;
}
}
pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], func->pfmem[count]->start);
/*_______________This if for debugging purposes only______________________________*/
debug ("b4 writing, start addres is %x\n", func->pfmem[count]->start);
pci_read_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], &bar[count]);
debug ("after writing, start address is %x\n", bar[count]);
/*_________________________________________________________________________________*/
if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) { /* takes up another dword */
debug ("inside the mem 64 case, count %d\n", count);
count += 1;
/* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */
pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], 0x00000000);
}
} else {
/* regular memory */
debug ("REGULAR MEM SPACE\n");
len[count] = bar[count] & 0xFFFFFFF0;
len[count] = ~len[count] + 1;
debug ("len[count] in Mem %x, count %d\n", len[count], count);
mem[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
if (!mem[count]) {
err ("out of system memory \n");
return -ENOMEM;
}
memset (mem[count], 0, sizeof (struct resource_node));
mem[count]->type = MEM;
mem[count]->busno = func->busno;
mem[count]->devfunc = ((func->device << 3) | (func->function & 0x7));
mem[count]->len = len[count];
if (ibmphp_check_resource (mem[count], 0) == 0) {
ibmphp_add_resource (mem[count]);
func->mem[count] = mem[count];
} else {
err ("cannot allocate requested mem for bus %x, device %x, len %x\n",
func->busno, func->device, len[count]);
kfree (mem[count]);
return -EIO;
}
pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], func->mem[count]->start);
/* _______________________This is for debugging purposes only _______________________*/
debug ("b4 writing, start address is %x\n", func->mem[count]->start);
pci_read_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], &bar[count]);
debug ("after writing, the address is %x\n", bar[count]);
/* __________________________________________________________________________________*/
if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) {
/* takes up another dword */
debug ("inside mem 64 case, reg. mem, count %d\n", count);
count += 1;
/* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */
pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], 0x00000000);
}
}
} /* end of mem */
} /* end of for */
func->bus = 0; /* To indicate that this is not a PPB */
pci_read_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_INTERRUPT_PIN, &irq);
if ((irq > 0x00) && (irq < 0x05))
pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_INTERRUPT_LINE, func->irq[irq - 1]);
pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_CACHE_LINE_SIZE, CACHE);
pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_LATENCY_TIMER, LATENCY);
pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_ROM_ADDRESS, 0x00L);
pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_COMMAND, DEVICEENABLE);
return 0;
}
/******************************************************************************
* This routine configures a PCI-2-PCI bridge and the functions behind it
* Parameters: pci_func
* Returns:
******************************************************************************/
static int configure_bridge (struct pci_func **func_passed, u8 slotno)
{
int count;
int i;
int rc;
u8 sec_number;
u8 io_base;
u16 pfmem_base;
u32 bar[2];
u32 len[2];
u8 flag_io = FALSE;
u8 flag_mem = FALSE;
u8 flag_pfmem = FALSE;
u8 need_io_upper = FALSE;
u8 need_pfmem_upper = FALSE;
struct res_needed *amount_needed = NULL;
struct resource_node *io = NULL;
struct resource_node *bus_io[2] = {NULL, NULL};
struct resource_node *mem = NULL;
struct resource_node *bus_mem[2] = {NULL, NULL};
struct resource_node *mem_tmp = NULL;
struct resource_node *pfmem = NULL;
struct resource_node *bus_pfmem[2] = {NULL, NULL};
struct bus_node *bus;
u32 address[] = {
PCI_BASE_ADDRESS_0,
PCI_BASE_ADDRESS_1,
0
};
struct pci_func *func = *func_passed;
u8 function;
u8 device;
u8 irq;
int retval;
debug ("%s - enter\n", __FUNCTION__);
function = func->function;
device = func->device;
/* Configuring necessary info for the bridge so that we could see the devices
* behind it
*/
pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PRIMARY_BUS, func->busno);
/* _____________________For debugging purposes only __________________________
pci_read_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PRIMARY_BUS, &pri_number);
debug ("primary # written into the bridge is %x\n", pri_number);
___________________________________________________________________________*/
/* in EBDA, only get allocated 1 additional bus # per slot */
sec_number = find_sec_number (func->busno, slotno);
if (sec_number == 0xff) {
err ("cannot allocate secondary bus number for the bridged device \n");
return -EINVAL;
}
debug ("after find_sec_number, the number we got is %x\n", sec_number);
debug ("AFTER FIND_SEC_NUMBER, func->busno IS %x\n", func->busno);
pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_SECONDARY_BUS, sec_number);
/* __________________For debugging purposes only __________________________________
pci_read_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_SECONDARY_BUS, &sec_number);
debug ("sec_number after write/read is %x\n", sec_number);
________________________________________________________________________________*/
pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_SUBORDINATE_BUS, sec_number);
/* __________________For debugging purposes only ____________________________________
pci_read_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_SUBORDINATE_BUS, &sec_number);
debug ("subordinate number after write/read is %x\n", sec_number);
__________________________________________________________________________________*/
pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_CACHE_LINE_SIZE, CACHE);
pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_LATENCY_TIMER, LATENCY);
pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_SEC_LATENCY_TIMER, LATENCY);
debug ("func->busno is %x\n", func->busno);
debug ("sec_number after writing is %x\n", sec_number);
/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!NEED TO ADD!!! FAST BACK-TO-BACK ENABLE!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/
/* First we need to allocate mem/io for the bridge itself in case it needs it */
for (count = 0; address[count]; count++) { /* for 2 BARs */
pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], 0xFFFFFFFF);
pci_read_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], &bar[count]);
if (!bar[count]) {
/* This BAR is not implemented */
debug ("so we come here then, eh?, count = %d\n", count);
continue;
}
// tmp_bar = bar[count];
debug ("Bar %d wants %x\n", count, bar[count]);
if (bar[count] & PCI_BASE_ADDRESS_SPACE_IO) {
/* This is IO */
len[count] = bar[count] & 0xFFFFFFFC;
len[count] = ~len[count] + 1;
debug ("len[count] in IO = %x\n", len[count]);
bus_io[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
if (!bus_io[count]) {
err ("out of system memory \n");
retval = -ENOMEM;
goto error;
}
memset (bus_io[count], 0, sizeof (struct resource_node));
bus_io[count]->type = IO;
bus_io[count]->busno = func->busno;
bus_io[count]->devfunc = ((func->device << 3) | (func->function & 0x7));
bus_io[count]->len = len[count];
if (ibmphp_check_resource (bus_io[count], 0) == 0) {
ibmphp_add_resource (bus_io[count]);
func->io[count] = bus_io[count];
} else {
err ("cannot allocate requested io for bus %x, device %x, len %x\n",
func->busno, func->device, len[count]);
kfree (bus_io[count]);
return -EIO;
}
pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], func->io[count]->start);
} else {
/* This is Memory */
if (bar[count] & PCI_BASE_ADDRESS_MEM_PREFETCH) {
/* pfmem */
len[count] = bar[count] & 0xFFFFFFF0;
len[count] = ~len[count] + 1;
debug ("len[count] in PFMEM = %x\n", len[count]);
bus_pfmem[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
if (!bus_pfmem[count]) {
err ("out of system memory \n");
retval = -ENOMEM;
goto error;
}
memset (bus_pfmem[count], 0, sizeof (struct resource_node));
bus_pfmem[count]->type = PFMEM;
bus_pfmem[count]->busno = func->busno;
bus_pfmem[count]->devfunc = ((func->device << 3) | (func->function & 0x7));
bus_pfmem[count]->len = len[count];
bus_pfmem[count]->fromMem = FALSE;
if (ibmphp_check_resource (bus_pfmem[count], 0) == 0) {
ibmphp_add_resource (bus_pfmem[count]);
func->pfmem[count] = bus_pfmem[count];
} else {
mem_tmp = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
if (!mem_tmp) {
err ("out of system memory \n");
retval = -ENOMEM;
goto error;
}
memset (mem_tmp, 0, sizeof (struct resource_node));
mem_tmp->type = MEM;
mem_tmp->busno = bus_pfmem[count]->busno;
mem_tmp->devfunc = bus_pfmem[count]->devfunc;
mem_tmp->len = bus_pfmem[count]->len;
if (ibmphp_check_resource (mem_tmp, 0) == 0) {
ibmphp_add_resource (mem_tmp);
bus_pfmem[count]->fromMem = TRUE;
bus_pfmem[count]->rangeno = mem_tmp->rangeno;
ibmphp_add_pfmem_from_mem (bus_pfmem[count]);
func->pfmem[count] = bus_pfmem[count];
} else {
err ("cannot allocate requested pfmem for bus %x, device %x, len %x\n",
func->busno, func->device, len[count]);
kfree (mem_tmp);
kfree (bus_pfmem[count]);
return -EIO;
}
}
pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], func->pfmem[count]->start);
if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) {
/* takes up another dword */
count += 1;
/* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */
pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], 0x00000000);
}
} else {
/* regular memory */
len[count] = bar[count] & 0xFFFFFFF0;
len[count] = ~len[count] + 1;
debug ("len[count] in Memory is %x\n", len[count]);
bus_mem[count] = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
if (!bus_mem[count]) {
err ("out of system memory \n");
retval = -ENOMEM;
goto error;
}
memset (bus_mem[count], 0, sizeof (struct resource_node));
bus_mem[count]->type = MEM;
bus_mem[count]->busno = func->busno;
bus_mem[count]->devfunc = ((func->device << 3) | (func->function & 0x7));
bus_mem[count]->len = len[count];
if (ibmphp_check_resource (bus_mem[count], 0) == 0) {
ibmphp_add_resource (bus_mem[count]);
func->mem[count] = bus_mem[count];
} else {
err ("cannot allocate requested mem for bus %x, device %x, len %x\n",
func->busno, func->device, len[count]);
kfree (bus_mem[count]);
return -EIO;
}
pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], func->mem[count]->start);
if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) {
/* takes up another dword */
count += 1;
/* on the 2nd dword, write all 0s, since we can't handle them n.e.ways */
pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, address[count], 0x00000000);
}
}
} /* end of mem */
} /* end of for */
/* Now need to see how much space the devices behind the bridge needed */
amount_needed = scan_behind_bridge (func, sec_number);
if (amount_needed == NULL)
return -ENOMEM;
debug ("after coming back from scan_behind_bridge\n");
debug ("amount_needed->not_correct = %x\n", amount_needed->not_correct);
debug ("amount_needed->io = %x\n", amount_needed->io);
debug ("amount_needed->mem = %x\n", amount_needed->mem);
debug ("amount_needed->pfmem = %x\n", amount_needed->pfmem);
if (amount_needed->not_correct) {
debug ("amount_needed is not correct \n");
for (count = 0; address[count]; count++) {
/* for 2 BARs */
if (bus_io[count]) {
ibmphp_remove_resource (bus_io[count]);
func->io[count] = NULL;
} else if (bus_pfmem[count]) {
ibmphp_remove_resource (bus_pfmem[count]);
func->pfmem[count] = NULL;
} else if (bus_mem[count]) {
ibmphp_remove_resource (bus_mem[count]);
func->mem[count] = NULL;
}
}
kfree (amount_needed);
return -ENODEV;
}
if (!amount_needed->io) {
debug ("it doesn't want IO?\n");
flag_io = TRUE;
} else {
debug ("it wants %x IO behind the bridge \n", amount_needed->io);
io = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
if (!io) {
err ("out of system memory \n");
retval = -ENOMEM;
goto error;
}
memset (io, 0, sizeof (struct resource_node));
io->type = IO;
io->busno = func->busno;
io->devfunc = ((func->device << 3) | (func->function & 0x7));
io->len = amount_needed->io;
if (ibmphp_check_resource (io, 1) == 0) {
debug ("were we able to add io\n");
ibmphp_add_resource (io);
flag_io = TRUE;
}
}
if (!amount_needed->mem) {
debug ("it doesn't want n.e.memory?\n");
flag_mem = TRUE;
} else {
debug ("it wants %x memory behind the bridge\n", amount_needed->mem);
mem = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
if (!mem) {
err ("out of system memory \n");
retval = -ENOMEM;
goto error;
}
memset (mem, 0, sizeof (struct resource_node));
mem->type = MEM;
mem->busno = func->busno;
mem->devfunc = ((func->device << 3) | (func->function & 0x7));
mem->len = amount_needed->mem;
if (ibmphp_check_resource (mem, 1) == 0) {
ibmphp_add_resource (mem);
flag_mem = TRUE;
debug ("were we able to add mem\n");
}
}
if (!amount_needed->pfmem) {
debug ("it doesn't want n.e.pfmem mem?\n");
flag_pfmem = TRUE;
} else {
debug ("it wants %x pfmemory behind the bridge\n", amount_needed->pfmem);
pfmem = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
if (!pfmem) {
err ("out of system memory \n");
retval = -ENOMEM;
goto error;
}
memset (pfmem, 0, sizeof (struct resource_node));
pfmem->type = PFMEM;
pfmem->busno = func->busno;
pfmem->devfunc = ((func->device << 3) | (func->function & 0x7));
pfmem->len = amount_needed->pfmem;
pfmem->fromMem = FALSE;
if (ibmphp_check_resource (pfmem, 1) == 0) {
ibmphp_add_resource (pfmem);
flag_pfmem = TRUE;
} else {
mem_tmp = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
if (!mem_tmp) {
err ("out of system memory \n");
retval = -ENOMEM;
goto error;
}
memset (mem_tmp, 0, sizeof (struct resource_node));
mem_tmp->type = MEM;
mem_tmp->busno = pfmem->busno;
mem_tmp->devfunc = pfmem->devfunc;
mem_tmp->len = pfmem->len;
if (ibmphp_check_resource (mem_tmp, 1) == 0) {
ibmphp_add_resource (mem_tmp);
pfmem->fromMem = TRUE;
pfmem->rangeno = mem_tmp->rangeno;
ibmphp_add_pfmem_from_mem (pfmem);
flag_pfmem = TRUE;
}
}
}
debug ("b4 if (flag_io && flag_mem && flag_pfmem)\n");
debug ("flag_io = %x, flag_mem = %x, flag_pfmem = %x\n", flag_io, flag_mem, flag_pfmem);
if (flag_io && flag_mem && flag_pfmem) {
bus = kmalloc (sizeof (struct bus_node), GFP_KERNEL);
if (!bus) {
err ("out of system memory \n");
retval = -ENOMEM;
goto error;
}
memset (bus, 0, sizeof (struct bus_node));
bus->busno = sec_number;
debug ("b4 adding new bus\n");
rc = add_new_bus (bus, io, mem, pfmem, func->busno);
if (rc) {
if (rc == -ENOMEM) {
ibmphp_remove_bus (bus, func->busno);
return rc;
}
retval = rc;
goto error;
}
pci_read_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_IO_BASE, &io_base);
pci_read_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PREF_MEMORY_BASE, &pfmem_base);
if ((io_base & PCI_IO_RANGE_TYPE_MASK) == PCI_IO_RANGE_TYPE_32) {
debug ("io 32\n");
need_io_upper = TRUE;
}
if ((io_base & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) {
debug ("pfmem 64\n");
need_pfmem_upper = TRUE;
}
if (bus->noIORanges) {
pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_IO_BASE, 0x00 | bus->rangeIO->start >> 8);
pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_IO_LIMIT, 0x00 | bus->rangeIO->end >> 8);
/* _______________This is for debugging purposes only ____________________
pci_read_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_IO_BASE, &temp);
debug ("io_base = %x\n", (temp & PCI_IO_RANGE_TYPE_MASK) << 8);
pci_read_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_IO_LIMIT, &temp);
debug ("io_limit = %x\n", (temp & PCI_IO_RANGE_TYPE_MASK) << 8);
________________________________________________________________________*/
if (need_io_upper) { /* since can't support n.e.ways */
pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_IO_BASE_UPPER16, 0x0000);
pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_IO_LIMIT_UPPER16, 0x0000);
}
} else {
pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_IO_BASE, 0x00);
pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_IO_LIMIT, 0x00);
}
if (bus->noMemRanges) {
pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_MEMORY_BASE, 0x0000 | bus->rangeMem->start >> 16);
pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_MEMORY_LIMIT, 0x0000 | bus->rangeMem->end >> 16);
/* ____________________This is for debugging purposes only ________________________
pci_read_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_MEMORY_BASE, &temp);
debug ("mem_base = %x\n", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16);
pci_read_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_MEMORY_LIMIT, &temp);
debug ("mem_limit = %x\n", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16);
__________________________________________________________________________________*/
} else {
pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_MEMORY_BASE, 0xffff);
pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_MEMORY_LIMIT, 0x0000);
}
if (bus->noPFMemRanges) {
pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PREF_MEMORY_BASE, 0x0000 | bus->rangePFMem->start >> 16);
pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PREF_MEMORY_LIMIT, 0x0000 | bus->rangePFMem->end >> 16);
/* __________________________This is for debugging purposes only _______________________
pci_read_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PREF_MEMORY_BASE, &temp);
debug ("pfmem_base = %x", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16);
pci_read_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PREF_MEMORY_LIMIT, &temp);
debug ("pfmem_limit = %x\n", (temp & PCI_MEMORY_RANGE_TYPE_MASK) << 16);
______________________________________________________________________________________*/
if (need_pfmem_upper) { /* since can't support n.e.ways */
pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PREF_BASE_UPPER32, 0x00000000);
pci_write_config_dword_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PREF_LIMIT_UPPER32, 0x00000000);
}
} else {
pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PREF_MEMORY_BASE, 0xffff);
pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_PREF_MEMORY_LIMIT, 0x0000);
}
debug ("b4 writing control information\n");
pci_read_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_INTERRUPT_PIN, &irq);
if ((irq > 0x00) && (irq < 0x05))
pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_INTERRUPT_LINE, func->irq[irq - 1]);
/*
pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_BRIDGE_CONTROL, ctrl);
pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_BRIDGE_CONTROL, PCI_BRIDGE_CTL_PARITY);
pci_write_config_byte_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_BRIDGE_CONTROL, PCI_BRIDGE_CTL_SERR);
*/
pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_COMMAND, DEVICEENABLE);
pci_write_config_word_nodev (ibmphp_pci_root_ops, func->busno, device, function, PCI_BRIDGE_CONTROL, 0x07);
for (i = 0; i < 32; i++) {
if (amount_needed->devices[i]) {
debug ("device where devices[i] is 1 = %x\n", i);
func->devices[i] = 1;
}
}
func->bus = 1; /* For unconfiguring, to indicate it's PPB */
func_passed = &func;
debug ("func->busno b4 returning is %x\n", func->busno);
debug ("func->busno b4 returning in the other structure is %x\n", (*func_passed)->busno);
kfree (amount_needed);
return 0;
} else {
err ("Configuring bridge was unsuccessful... \n");
mem_tmp = NULL;
retval = -EIO;
goto error;
}
error:
if (amount_needed)
kfree (amount_needed);
if (pfmem)
ibmphp_remove_resource (pfmem);
if (io)
ibmphp_remove_resource (io);
if (mem)
ibmphp_remove_resource (mem);
for (i = 0; i < 2; i++) { /* for 2 BARs */
if (bus_io[i]) {
ibmphp_remove_resource (bus_io[i]);
func->io[i] = NULL;
} else if (bus_pfmem[i]) {
ibmphp_remove_resource (bus_pfmem[i]);
func->pfmem[i] = NULL;
} else if (bus_mem[i]) {
ibmphp_remove_resource (bus_mem[i]);
func->mem[i] = NULL;
}
}
return retval;
}
/*****************************************************************************
* This function adds up the amount of resources needed behind the PPB bridge
* and passes it to the configure_bridge function
* Input: bridge function
* Ouput: amount of resources needed
*****************************************************************************/
static struct res_needed *scan_behind_bridge (struct pci_func * func, u8 busno)
{
int count, len[6];
u16 vendor_id;
u8 hdr_type;
u8 device, function;
int howmany = 0; /*this is to see if there are any devices behind the bridge */
u32 bar[6], class;
u32 address[] = {
PCI_BASE_ADDRESS_0,
PCI_BASE_ADDRESS_1,
PCI_BASE_ADDRESS_2,
PCI_BASE_ADDRESS_3,
PCI_BASE_ADDRESS_4,
PCI_BASE_ADDRESS_5,
0
};
struct res_needed *amount;
amount = kmalloc (sizeof (struct res_needed), GFP_KERNEL);
if (amount == NULL)
return NULL;
memset (amount, 0, sizeof (struct res_needed));
debug ("the bus_no behind the bridge is %x\n", busno);
debug ("scanning devices behind the bridge...\n");
for (device = 0; device < 32; device++) {
amount->devices[device] = 0;
for (function = 0; function < 8; function++) {
pci_read_config_word_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_VENDOR_ID, &vendor_id);
if (vendor_id != PCI_VENDOR_ID_NOTVALID) {
/* found correct device!!! */
howmany++;
pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_HEADER_TYPE, &hdr_type);
pci_read_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_CLASS_REVISION, &class);
debug ("hdr_type behind the bridge is %x\n", hdr_type);
if (hdr_type & PCI_HEADER_TYPE_BRIDGE) {
err ("embedded bridges not supported for hot-plugging.\n");
amount->not_correct = TRUE;
return amount;
}
class >>= 8; /* to take revision out, class = class.subclass.prog i/f */
if (class == PCI_CLASS_NOT_DEFINED_VGA) {
err ("The device %x is VGA compatible and as is not supported for hot plugging. "
"Please choose another device.\n", device);
amount->not_correct = TRUE;
return amount;
} else if (class == PCI_CLASS_DISPLAY_VGA) {
err ("The device %x is not supported for hot plugging. "
"Please choose another device.\n", device);
amount->not_correct = TRUE;
return amount;
}
amount->devices[device] = 1;
for (count = 0; address[count]; count++) {
/* for 6 BARs */
/*
pci_read_config_byte_nodev(ibmphp_pci_root_ops, busno, device, function, address[count], &tmp);
if (tmp & 0x01) // IO
pci_write_config_dword_nodev(ibmphp_pci_root_ops, busno, device, function, address[count], 0xFFFFFFFD);
else // MEMORY
pci_write_config_dword_nodev(ibmphp_pci_root_ops, busno, device, function, address[count], 0xFFFFFFFF);
*/
pci_write_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, address[count], 0xFFFFFFFF);
pci_read_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, address[count], &bar[count]);
debug ("what is bar[count]? %x, count = %d\n", bar[count], count);
if (!bar[count]) /* This BAR is not implemented */
continue;
//tmp_bar = bar[count];
debug ("count %d device %x function %x wants %x resources \n", count, device, function, bar[count]);
if (bar[count] & PCI_BASE_ADDRESS_SPACE_IO) {
/* This is IO */
len[count] = bar[count] & 0xFFFFFFFC;
len[count] = ~len[count] + 1;
amount->io += len[count];
} else {
/* This is Memory */
if (bar[count] & PCI_BASE_ADDRESS_MEM_PREFETCH) {
/* pfmem */
len[count] = bar[count] & 0xFFFFFFF0;
len[count] = ~len[count] + 1;
amount->pfmem += len[count];
if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64)
/* takes up another dword */
count += 1;
} else {
/* regular memory */
len[count] = bar[count] & 0xFFFFFFF0;
len[count] = ~len[count] + 1;
amount->mem += len[count];
if (bar[count] & PCI_BASE_ADDRESS_MEM_TYPE_64) {
/* takes up another dword */
count += 1;
}
}
}
} /* end for */
} /* end if (valid) */
} /* end for */
} /* end for */
if (!howmany)
amount->not_correct = TRUE;
else
amount->not_correct = FALSE;
if ((amount->io) && (amount->io < IOBRIDGE))
amount->io = IOBRIDGE;
if ((amount->mem) && (amount->mem < MEMBRIDGE))
amount->mem = MEMBRIDGE;
if ((amount->pfmem) && (amount->pfmem < MEMBRIDGE))
amount->pfmem = MEMBRIDGE;
return amount;
}
/* The following 3 unconfigure_boot_ routines deal with the case when we had the card
* upon bootup in the system, since we don't allocate func to such case, we need to read
* the start addresses from pci config space and then find the corresponding entries in
* our resource lists. The functions return either 0, -ENODEV, or -1 (general failure)
* Change: we also call these functions even if we configured the card ourselves (i.e., not
* the bootup case), since it should work same way
*/
static int unconfigure_boot_device (u8 busno, u8 device, u8 function)
{
u32 start_address;
u32 address[] = {
PCI_BASE_ADDRESS_0,
PCI_BASE_ADDRESS_1,
PCI_BASE_ADDRESS_2,
PCI_BASE_ADDRESS_3,
PCI_BASE_ADDRESS_4,
PCI_BASE_ADDRESS_5,
0
};
int count;
struct resource_node *io;
struct resource_node *mem;
struct resource_node *pfmem;
struct bus_node *bus;
u32 end_address;
u32 temp_end;
u32 size;
u32 tmp_address;
debug ("%s - enter\n", __FUNCTION__);
bus = ibmphp_find_res_bus (busno);
if (!bus) {
debug ("cannot find corresponding bus.\n");
return -EINVAL;
}
for (count = 0; address[count]; count++) { /* for 6 BARs */
pci_read_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, address[count], &start_address);
/* We can do this here, b/c by that time the device driver of the card has been stopped */
pci_write_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, address[count], 0xFFFFFFFF);
pci_read_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, address[count], &size);
pci_write_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, address[count], start_address);
debug ("start_address is %x\n", start_address);
debug ("busno, device, function %x %x %x\n", busno, device, function);
if (!size) {
/* This BAR is not implemented */
debug ("is this bar no implemented?, count = %d\n", count);
continue;
}
tmp_address = start_address;
if (start_address & PCI_BASE_ADDRESS_SPACE_IO) {
/* This is IO */
start_address &= PCI_BASE_ADDRESS_IO_MASK;
size = size & 0xFFFFFFFC;
size = ~size + 1;
end_address = start_address + size - 1;
if (ibmphp_find_resource (bus, start_address, &io, IO) < 0) {
err ("cannot find corresponding IO resource to remove\n");
return -EIO;
}
debug ("io->start = %x\n", io->start);
temp_end = io->end;
start_address = io->end + 1;
ibmphp_remove_resource (io);
/* This is needed b/c of the old I/O restrictions in the BIOS */
while (temp_end < end_address) {
if (ibmphp_find_resource (bus, start_address, &io, IO) < 0) {
err ("cannot find corresponding IO resource to remove\n");
return -EIO;
}
debug ("io->start = %x\n", io->start);
temp_end = io->end;
start_address = io->end + 1;
ibmphp_remove_resource (io);
}
/* ????????? DO WE NEED TO WRITE ANYTHING INTO THE PCI CONFIG SPACE BACK ?????????? */
} else {
/* This is Memory */
if (start_address & PCI_BASE_ADDRESS_MEM_PREFETCH) {
/* pfmem */
start_address &= PCI_BASE_ADDRESS_MEM_MASK;
debug ("start address of pfmem is %x\n", start_address);
if (ibmphp_find_resource (bus, start_address, &pfmem, PFMEM) < 0) {
err ("cannot find corresponding PFMEM resource to remove\n");
return -EIO;
}
if (pfmem)
debug ("pfmem->start = %x\n", pfmem->start);
ibmphp_remove_resource (pfmem);
if (tmp_address & PCI_BASE_ADDRESS_MEM_TYPE_64) {
/* takes up another dword */
count += 1;
}
} else {
/* regular memory */
start_address &= PCI_BASE_ADDRESS_MEM_MASK;
debug ("start address of mem is %x\n", start_address);
if (ibmphp_find_resource (bus, start_address, &mem, MEM) < 0) {
err ("cannot find corresponding MEM resource to remove\n");
return -EIO;
}
if (mem)
debug ("mem->start = %x\n", mem->start);
ibmphp_remove_resource (mem);
if (tmp_address & PCI_BASE_ADDRESS_MEM_TYPE_64) {
/* takes up another dword */
count += 1;
}
}
} /* end of mem */
} /* end of for */
return 0;
}
static int unconfigure_boot_bridge (u8 busno, u8 device, u8 function)
{
int count;
int bus_no, pri_no, sub_no, sec_no = 0;
u32 start_address, tmp_address;
u8 sec_number, sub_number, pri_number;
struct resource_node *io = NULL;
struct resource_node *mem = NULL;
struct resource_node *pfmem = NULL;
struct bus_node *bus;
u32 address[] = {
PCI_BASE_ADDRESS_0,
PCI_BASE_ADDRESS_1,
0
};
bus_no = (int) busno;
debug ("busno is %x\n", busno);
pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_PRIMARY_BUS, &pri_number);
debug ("%s - busno = %x, primary_number = %x\n", __FUNCTION__, busno, pri_number);
pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_SECONDARY_BUS, &sec_number);
debug ("sec_number is %x\n", sec_number);
sec_no = (int) sec_number;
pri_no = (int) pri_number;
if (pri_no != bus_no) {
err ("primary numbers in our structures and pci config space don't match.\n");
return -EINVAL;
}
pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_SECONDARY_BUS, &sec_number);
sec_no = (int) sec_no;
pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_SUBORDINATE_BUS, &sub_number);
sub_no = (int) sub_number;
debug ("sub_no is %d, sec_no is %d\n", sub_no, sec_no);
if (sec_no != sub_number) {
err ("there're more buses behind this bridge. Hot removal is not supported. Please choose another card\n");
return -ENODEV;
}
bus = ibmphp_find_res_bus (sec_number);
debug ("bus->busno is %x\n", bus->busno);
debug ("sec_number is %x\n", sec_number);
if (!bus) {
err ("cannot find Bus structure for the bridged device\n");
return -EINVAL;
}
ibmphp_remove_bus (bus, busno);
for (count = 0; address[count]; count++) {
/* for 2 BARs */
pci_read_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, address[count], &start_address);
if (!start_address) {
/* This BAR is not implemented */
continue;
}
tmp_address = start_address;
if (start_address & PCI_BASE_ADDRESS_SPACE_IO) {
/* This is IO */
start_address &= PCI_BASE_ADDRESS_IO_MASK;
if (ibmphp_find_resource (bus, start_address, &io, IO) < 0) {
err ("cannot find corresponding IO resource to remove\n");
return -EIO;
}
if (io)
debug ("io->start = %x\n", io->start);
ibmphp_remove_resource (io);
/* ????????? DO WE NEED TO WRITE ANYTHING INTO THE PCI CONFIG SPACE BACK ?????????? */
} else {
/* This is Memory */
if (start_address & PCI_BASE_ADDRESS_MEM_PREFETCH) {
/* pfmem */
start_address &= PCI_BASE_ADDRESS_MEM_MASK;
if (ibmphp_find_resource (bus, start_address, &pfmem, PFMEM) < 0) {
err ("cannot find corresponding PFMEM resource to remove\n");
return -EINVAL;
}
if (pfmem)
debug ("pfmem->start = %x\n", pfmem->start);
ibmphp_remove_resource (pfmem);
if (tmp_address & PCI_BASE_ADDRESS_MEM_TYPE_64) {
/* takes up another dword */
count += 1;
}
} else {
/* regular memory */
start_address &= PCI_BASE_ADDRESS_MEM_MASK;
if (ibmphp_find_resource (bus, start_address, &mem, MEM) < 0) {
err ("cannot find corresponding MEM resource to remove\n");
return -EINVAL;
}
if (mem)
debug ("mem->start = %x\n", mem->start);
ibmphp_remove_resource (mem);
if (tmp_address & PCI_BASE_ADDRESS_MEM_TYPE_64) {
/* takes up another dword */
count += 1;
}
}
} /* end of mem */
} /* end of for */
debug ("%s - exiting, returning success\n", __FUNCTION__);
return 0;
}
static int unconfigure_boot_card (struct slot *slot_cur)
{
u16 vendor_id;
u32 class;
u8 hdr_type;
u8 device;
u8 busno;
u8 function;
int rc;
u8 valid_device = 0x00; /* To see if we are ever able to find valid device and read it */
debug ("%s - enter\n", __FUNCTION__);
device = slot_cur->device;
busno = slot_cur->bus;
debug ("b4 for loop, device is %x\n", device);
/* For every function on the card */
for (function = 0x0; function < 0x08; function++) {
pci_read_config_word_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_VENDOR_ID, &vendor_id);
if (vendor_id != PCI_VENDOR_ID_NOTVALID) {
/* found correct device!!! */
++valid_device;
debug ("%s - found correct device\n", __FUNCTION__);
/* header: x x x x x x x x
* | |___________|=> 1=PPB bridge, 0=normal device, 2=CardBus Bridge
* |_=> 0 = single function device, 1 = multi-function device
*/
pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_HEADER_TYPE, &hdr_type);
pci_read_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_CLASS_REVISION, &class);
debug ("hdr_type %x, class %x\n", hdr_type, class);
class >>= 8; /* to take revision out, class = class.subclass.prog i/f */
if (class == PCI_CLASS_NOT_DEFINED_VGA) {
err ("The device %x function %x is VGA compatible and is not supported for hot removing. "
"Please choose another device.\n", device, function);
return -ENODEV;
} else if (class == PCI_CLASS_DISPLAY_VGA) {
err ("The device %x function %x is not supported for hot removing. "
"Please choose another device.\n", device, function);
return -ENODEV;
}
switch (hdr_type) {
case PCI_HEADER_TYPE_NORMAL:
rc = unconfigure_boot_device (busno, device, function);
if (rc) {
err ("was not able to unconfigure device %x func %x on bus %x. bailing out... \n",
device, function, busno);
return rc;
}
function = 0x8;
break;
case PCI_HEADER_TYPE_MULTIDEVICE:
rc = unconfigure_boot_device (busno, device, function);
if (rc) {
err ("was not able to unconfigure device %x func %x on bus %x. bailing out... \n",
device, function, busno);
return rc;
}
break;
case PCI_HEADER_TYPE_BRIDGE:
class >>= 8;
if (class != PCI_CLASS_BRIDGE_PCI) {
err ("This device %x function %x is not PCI-to-PCI bridge, "
"and is not supported for hot-removing. "
"Please try another card.\n", device, function);
return -ENODEV;
}
rc = unconfigure_boot_bridge (busno, device, function);
if (rc != 0) {
err ("was not able to hot-remove PPB properly.\n");
return rc;
}
function = 0x8;
break;
case PCI_HEADER_TYPE_MULTIBRIDGE:
class >>= 8;
if (class != PCI_CLASS_BRIDGE_PCI) {
err ("This device %x function %x is not PCI-to-PCI bridge, "
"and is not supported for hot-removing. "
"Please try another card.\n", device, function);
return -ENODEV;
}
rc = unconfigure_boot_bridge (busno, device, function);
if (rc != 0) {
err ("was not able to hot-remove PPB properly.\n");
return rc;
}
break;
default:
err ("MAJOR PROBLEM!!!! Cannot read device's header \n");
return -1;
break;
} /* end of switch */
} /* end of valid device */
} /* end of for */
if (!valid_device) {
err ("Could not find device to unconfigure. Or could not read the card. \n");
return -1;
}
return 0;
}
/*
* free the resources of the card (multi, single, or bridged)
* Parameters: slot, flag to say if this is for removing entire module or just
* unconfiguring the device
* TO DO: will probably need to add some code in case there was some resource,
* to remove it... this is from when we have errors in the configure_card...
* !!!!!!!!!!!!!!!!!!!!!!!!!FOR BUSES!!!!!!!!!!!!
* Returns: 0, -1, -ENODEV
*/
int ibmphp_unconfigure_card (struct slot **slot_cur, int the_end)
{
int i;
int count;
int rc;
struct slot *sl = *slot_cur;
struct pci_func *cur_func = NULL;
struct pci_func *temp_func;
debug ("%s - enter\n", __FUNCTION__);
if (!the_end) {
/* Need to unconfigure the card */
rc = unconfigure_boot_card (sl);
if ((rc == -ENODEV) || (rc == -EIO) || (rc == -EINVAL)) {
/* In all other cases, will still need to get rid of func structure if it exists */
return rc;
}
}
if (sl->func) {
debug ("do we come in here? \n");
cur_func = sl->func;
while (cur_func) {
/* TO DO: WILL MOST LIKELY NEED TO GET RID OF THE BUS STRUCTURE FROM RESOURCES AS WELL */
if (cur_func->bus) {
/* in other words, it's a PPB */
count = 2;
} else {
count = 6;
}
for (i = 0; i < count; i++) {
if (cur_func->io[count]) {
debug ("io[%d] exists \n", count);
if (the_end > 0)
ibmphp_remove_resource (cur_func->io[count]);
cur_func->io[count] = NULL;
}
if (cur_func->mem[count]) {
debug ("mem[%d] exists \n", count);
if (the_end > 0)
ibmphp_remove_resource (cur_func->mem[count]);
cur_func->mem[count] = NULL;
}
if (cur_func->pfmem[count]) {
debug ("pfmem[%d] exists \n", count);
if (the_end > 0)
ibmphp_remove_resource (cur_func->pfmem[count]);
cur_func->pfmem[count] = NULL;
}
}
temp_func = cur_func->next;
kfree (cur_func);
cur_func = temp_func;
}
}
sl->func = NULL;
*slot_cur = sl;
return 0;
}
/*
* add a new bus resulting from hot-plugging a PPB bridge with devices
*
* Input: bus and the amount of resources needed (we know we can assign those,
* since they've been checked already
* Output: bus added to the correct spot
* 0, -1, error
*/
static int add_new_bus (struct bus_node *bus, struct resource_node *io, struct resource_node *mem, struct resource_node *pfmem, u8 parent_busno)
{
struct range_node *io_range = NULL;
struct range_node *mem_range = NULL;
struct range_node *pfmem_range = NULL;
struct bus_node *cur_bus = NULL;
/* Trying to find the parent bus number */
cur_bus = ibmphp_find_res_bus (parent_busno);
if (!cur_bus) {
err ("strange, cannot find bus which is supposed to be at the system... something is terribly wrong...\n");
return -ENODEV;
}
list_add (&bus->bus_list, &cur_bus->bus_list);
if (io) {
io_range = kmalloc (sizeof (struct range_node), GFP_KERNEL);
if (!io_range) {
err ("out of system memory \n");
return -ENOMEM;
}
memset (io_range, 0, sizeof (struct range_node));
io_range->start = io->start;
io_range->end = io->end;
io_range->rangeno = 1;
bus->noIORanges = 1;
bus->rangeIO = io_range;
}
if (mem) {
mem_range = kmalloc (sizeof (struct range_node), GFP_KERNEL);
if (!mem_range) {
err ("out of system memory \n");
return -ENOMEM;
}
memset (mem_range, 0, sizeof (struct range_node));
mem_range->start = mem->start;
mem_range->end = mem->end;
mem_range->rangeno = 1;
bus->noMemRanges = 1;
bus->rangeMem = mem_range;
}
if (pfmem) {
pfmem_range = kmalloc (sizeof (struct range_node), GFP_KERNEL);
if (!pfmem_range) {
err ("out of system memory \n");
return -ENOMEM;
}
memset (pfmem_range, 0, sizeof (struct range_node));
pfmem_range->start = pfmem->start;
pfmem_range->end = pfmem->end;
pfmem_range->rangeno = 1;
bus->noPFMemRanges = 1;
bus->rangePFMem = pfmem_range;
}
return 0;
}
/*
* find the 1st available bus number for PPB to set as its secondary bus
* Parameters: bus_number of the primary bus
* Returns: bus_number of the secondary bus or 0xff in case of failure
*/
static u8 find_sec_number (u8 primary_busno, u8 slotno)
{
int min, max;
u8 busno;
struct bus_info *bus;
bus = ibmphp_find_same_bus_num (primary_busno);
if (!bus) {
err ("cannot get slot range of the bus from the BIOS\n");
return 0xff;
}
max = bus->slot_max;
min = bus->slot_min;
if ((slotno > max) || (slotno < min)) {
err ("got the wrong range\n");
return 0xff;
}
busno = (u8) (slotno - (u8) min);
busno += primary_busno + 0x01;
if (!ibmphp_find_res_bus (busno))
return busno;
return 0xff;
}
/*
* IBM Hot Plug Controller Driver
*
* Written By: Irene Zubarev, IBM Corporation
*
* Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (c) 2001,2002 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 <gregkh@us.ibm.com>
*
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/list.h>
#include "ibmphp.h"
static int flags = 0; /* for testing */
static void update_resources (struct bus_node *bus_cur, int type, int rangeno);
static int once_over (void);
static int remove_ranges (struct bus_node *, struct bus_node *);
static int update_bridge_ranges (struct bus_node **);
static int add_range (int type, struct range_node *, struct bus_node *);
static void fix_resources (struct bus_node *);
static inline struct bus_node *find_bus_wprev (u8, struct bus_node **, u8);
static LIST_HEAD(gbuses);
static struct bus_node * alloc_error_bus (struct ebda_pci_rsrc * curr)
{
struct bus_node * newbus;
newbus = kmalloc (sizeof (struct bus_node), GFP_KERNEL);
if (!newbus) {
err ("out of system memory \n");
return NULL;
}
memset (newbus, 0, sizeof (struct bus_node));
newbus->busno = curr->bus_num;
list_add_tail (&newbus->bus_list, &gbuses);
return newbus;
}
static struct resource_node * alloc_resources (struct ebda_pci_rsrc * curr)
{
struct resource_node *rs = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
if (!rs) {
err ("out of system memory \n");
return NULL;
}
memset (rs, 0, sizeof (struct resource_node));
rs->busno = curr->bus_num;
rs->devfunc = curr->dev_fun;
rs->start = curr->start_addr;
rs->end = curr->end_addr;
rs->len = curr->end_addr - curr->start_addr + 1;
return rs;
}
static int alloc_bus_range (struct bus_node **new_bus, struct range_node **new_range, struct ebda_pci_rsrc *curr, int flag, u8 first_bus)
{
struct bus_node * newbus;
struct range_node *newrange;
u8 num_ranges = 0;
if (first_bus) {
newbus = kmalloc (sizeof (struct bus_node), GFP_KERNEL);
if (!newbus) {
err ("out of system memory. \n");
return -ENOMEM;
}
memset (newbus, 0, sizeof (struct bus_node));
newbus->busno = curr->bus_num;
} else {
newbus = *new_bus;
switch (flag) {
case MEM:
num_ranges = newbus->noMemRanges;
break;
case PFMEM:
num_ranges = newbus->noPFMemRanges;
break;
case IO:
num_ranges = newbus->noIORanges;
break;
}
}
newrange = kmalloc (sizeof (struct range_node), GFP_KERNEL);
if (!newrange) {
if (first_bus)
kfree (newbus);
err ("out of system memory \n");
return -ENOMEM;
}
memset (newrange, 0, sizeof (struct range_node));
newrange->start = curr->start_addr;
newrange->end = curr->end_addr;
if (first_bus || (!num_ranges))
newrange->rangeno = 1;
else {
/* need to insert our range */
add_range (flag, newrange, newbus);
debug ("%d resource Primary Bus inserted on bus %x [%x - %x]\n", flag, newbus->busno, newrange->start, newrange->end);
}
switch (flag) {
case MEM:
newbus->rangeMem = newrange;
if (first_bus)
newbus->noMemRanges = 1;
else {
debug ("First Memory Primary on bus %x, [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
++newbus->noMemRanges;
fix_resources (newbus);
}
break;
case IO:
newbus->rangeIO = newrange;
if (first_bus)
newbus->noIORanges = 1;
else {
debug ("First IO Primary on bus %x, [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
++newbus->noIORanges;
fix_resources (newbus);
}
break;
case PFMEM:
newbus->rangePFMem = newrange;
if (first_bus)
newbus->noPFMemRanges = 1;
else {
debug ("1st PFMemory Primary on Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
++newbus->noPFMemRanges;
fix_resources (newbus);
}
break;
}
*new_bus = newbus;
*new_range = newrange;
return 0;
}
/* Notes:
* 1. The ranges are ordered. The buses are not ordered. (First come)
*
* 2. If cannot allocate out of PFMem range, allocate from Mem ranges. PFmemFromMem
* are not sorted. (no need since use mem node). To not change the entire code, we
* also add mem node whenever this case happens so as not to change
* ibmphp_check_mem_resource etc (and since it really is taking Mem resource)
*/
/*****************************************************************************
* This is the Resource Management initialization function. It will go through
* the Resource list taken from EBDA and fill in this module's data structures
*
* THIS IS NOT TAKING INTO CONSIDERATION IO RESTRICTIONS OF PRIMARY BUSES,
* SINCE WE'RE GOING TO ASSUME FOR NOW WE DON'T HAVE THOSE ON OUR BUSES FOR NOW
*
* Input: ptr to the head of the resource list from EBDA
* Output: 0, -1 or error codes
***************************************************************************/
int ibmphp_rsrc_init (void)
{
struct ebda_pci_rsrc *curr;
struct range_node *newrange = NULL;
struct bus_node *newbus = NULL;
struct bus_node *bus_cur;
struct bus_node *bus_prev;
struct list_head *tmp;
struct resource_node *new_io = NULL;
struct resource_node *new_mem = NULL;
struct resource_node *new_pfmem = NULL;
int rc;
struct list_head *tmp_ebda;
list_for_each (tmp_ebda, &ibmphp_ebda_pci_rsrc_head) {
curr = list_entry (tmp_ebda, struct ebda_pci_rsrc, ebda_pci_rsrc_list);
if (!(curr->rsrc_type & PCIDEVMASK)) {
/* EBDA still lists non PCI devices, so ignore... */
debug ("this is not a PCI DEVICE in rsrc_init, please take care\n");
// continue;
}
/* this is a primary bus resource */
if (curr->rsrc_type & PRIMARYBUSMASK) {
/* memory */
if ((curr->rsrc_type & RESTYPE) == MMASK) {
/* no bus structure exists in place yet */
if (list_empty (&gbuses)) {
if ((rc = alloc_bus_range (&newbus, &newrange, curr, MEM, 1)))
return rc;
list_add_tail (&newbus->bus_list, &gbuses);
debug ("gbuses = NULL, Memory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
} else {
bus_cur = find_bus_wprev (curr->bus_num, &bus_prev, 1);
/* found our bus */
if (bus_cur) {
rc = alloc_bus_range (&bus_cur, &newrange, curr, MEM, 0);
if (rc)
return rc;
} else {
/* went through all the buses and didn't find ours, need to create a new bus node */
if ((rc = alloc_bus_range (&newbus, &newrange, curr, MEM, 1)))
return rc;
list_add_tail (&newbus->bus_list, &gbuses);
debug ("New Bus, Memory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
}
}
} else if ((curr->rsrc_type & RESTYPE) == PFMASK) {
/* prefetchable memory */
if (list_empty (&gbuses)) {
/* no bus structure exists in place yet */
if ((rc = alloc_bus_range (&newbus, &newrange, curr, PFMEM, 1)))
return rc;
list_add_tail (&newbus->bus_list, &gbuses);
debug ("gbuses = NULL, PFMemory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
} else {
bus_cur = find_bus_wprev (curr->bus_num, &bus_prev, 1);
if (bus_cur) {
/* found our bus */
rc = alloc_bus_range (&bus_cur, &newrange, curr, PFMEM, 0);
if (rc)
return rc;
} else {
/* went through all the buses and didn't find ours, need to create a new bus node */
if ((rc = alloc_bus_range (&newbus, &newrange, curr, PFMEM, 1)))
return rc;
list_add_tail (&newbus->bus_list, &gbuses);
debug ("1st Bus, PFMemory Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
}
}
} else if ((curr->rsrc_type & RESTYPE) == IOMASK) {
/* IO */
if (list_empty (&gbuses)) {
/* no bus structure exists in place yet */
if ((rc = alloc_bus_range (&newbus, &newrange, curr, IO, 1)))
return rc;
list_add_tail (&newbus->bus_list, &gbuses);
debug ("gbuses = NULL, IO Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
} else {
bus_cur = find_bus_wprev (curr->bus_num, &bus_prev, 1);
if (bus_cur) {
rc = alloc_bus_range (&bus_cur, &newrange, curr, IO, 0);
if (rc)
return rc;
} else {
/* went through all the buses and didn't find ours, need to create a new bus node */
if ((rc = alloc_bus_range (&newbus, &newrange, curr, IO, 1)))
return rc;
list_add_tail (&newbus->bus_list, &gbuses);
debug ("1st Bus, IO Primary Bus %x [%x - %x]\n", newbus->busno, newrange->start, newrange->end);
}
}
} else {
; /* type is reserved WHAT TO DO IN THIS CASE???
NOTHING TO DO??? */
}
} else {
/* regular pci device resource */
if ((curr->rsrc_type & RESTYPE) == MMASK) {
/* Memory resource */
new_mem = alloc_resources (curr);
if (!new_mem)
return -ENOMEM;
new_mem->type = MEM;
/*
* if it didn't find the bus, means PCI dev
* came b4 the Primary Bus info, so need to
* create a bus rangeno becomes a problem...
* assign a -1 and then update once the range
* actually appears...
*/
if (ibmphp_add_resource (new_mem) < 0) {
newbus = alloc_error_bus (curr);
if (!newbus)
return -ENOMEM;
newbus->firstMem = new_mem;
++newbus->needMemUpdate;
new_mem->rangeno = -1;
}
debug ("Memory resource for device %x, bus %x, [%x - %x]\n", new_mem->devfunc, new_mem->busno, new_mem->start, new_mem->end);
} else if ((curr->rsrc_type & RESTYPE) == PFMASK) {
/* PFMemory resource */
new_pfmem = alloc_resources (curr);
if (!new_pfmem)
return -ENOMEM;
new_pfmem->type = PFMEM;
new_pfmem->fromMem = FALSE;
if (ibmphp_add_resource (new_pfmem) < 0) {
newbus = alloc_error_bus (curr);
if (!newbus)
return -ENOMEM;
newbus->firstPFMem = new_pfmem;
++newbus->needPFMemUpdate;
new_pfmem->rangeno = -1;
}
debug ("PFMemory resource for device %x, bus %x, [%x - %x]\n", new_pfmem->devfunc, new_pfmem->busno, new_pfmem->start, new_pfmem->end);
} else if ((curr->rsrc_type & RESTYPE) == IOMASK) {
/* IO resource */
new_io = alloc_resources (curr);
if (!new_io)
return -ENOMEM;
new_io->type = IO;
/*
* if it didn't find the bus, means PCI dev
* came b4 the Primary Bus info, so need to
* create a bus rangeno becomes a problem...
* Can assign a -1 and then update once the
* range actually appears...
*/
if (ibmphp_add_resource (new_io) < 0) {
newbus = alloc_error_bus (curr);
if (!newbus)
return -ENOMEM;
newbus->firstIO = new_io;
++newbus->needIOUpdate;
new_io->rangeno = -1;
}
debug ("IO resource for device %x, bus %x, [%x - %x]\n", new_io->devfunc, new_io->busno, new_io->start, new_io->end);
}
}
}
debug ("after the while loop in rsrc_init \n");
list_for_each (tmp, &gbuses) {
bus_cur = list_entry (tmp, struct bus_node, bus_list);
/* This is to get info about PPB resources, since EBDA doesn't put this info into the primary bus info */
rc = update_bridge_ranges (&bus_cur);
if (rc)
return rc;
}
debug ("b4 once_over in rsrc_init \n");
rc = once_over (); /* This is to align ranges (so no -1) */
if (rc)
return rc;
debug ("after once_over in rsrc_init \n");
return 0;
}
/********************************************************************************
* This function adds a range into a sorted list of ranges per bus for a particular
* range type, it then calls another routine to update the range numbers on the
* pci devices' resources for the appropriate resource
*
* Input: type of the resource, range to add, current bus
* Output: 0 or -1, bus and range ptrs
********************************************************************************/
static int add_range (int type, struct range_node *range, struct bus_node *bus_cur)
{
struct range_node *range_cur = NULL;
struct range_node *range_prev;
int count = 0, i_init;
int noRanges = 0;
switch (type) {
case MEM:
range_cur = bus_cur->rangeMem;
noRanges = bus_cur->noMemRanges;
break;
case PFMEM:
range_cur = bus_cur->rangePFMem;
noRanges = bus_cur->noPFMemRanges;
break;
case IO:
range_cur = bus_cur->rangeIO;
noRanges = bus_cur->noIORanges;
break;
}
range_prev = NULL;
while (range_cur) {
if (range->start < range_cur->start)
break;
range_prev = range_cur;
range_cur = range_cur->next;
count = count + 1;
}
if (!count) {
/* our range will go at the beginning of the list */
switch (type) {
case MEM:
bus_cur->rangeMem = range;
break;
case PFMEM:
bus_cur->rangePFMem = range;
break;
case IO:
bus_cur->rangeIO = range;
break;
}
range->next = range_cur;
range->rangeno = 1;
i_init = 0;
} else if (!range_cur) {
/* our range will go at the end of the list */
range->next = NULL;
range_prev->next = range;
range->rangeno = range_prev->rangeno + 1;
return 0;
} else {
/* the range is in the middle */
range_prev->next = range;
range->next = range_cur;
range->rangeno = range_cur->rangeno;
i_init = range_prev->rangeno;
}
for (count = i_init; count < noRanges; ++count) {
++range_cur->rangeno;
range_cur = range_cur->next;
}
update_resources (bus_cur, type, i_init + 1);
return 0;
}
/*******************************************************************************
* This routine goes through the list of resources of type 'type' and updates
* the range numbers that they correspond to. It was called from add_range fnc
*
* Input: bus, type of the resource, the rangeno starting from which to update
******************************************************************************/
static void update_resources (struct bus_node *bus_cur, int type, int rangeno)
{
struct resource_node *res = NULL;
u8 eol = FALSE; /* end of list indicator */
switch (type) {
case MEM:
if (bus_cur->firstMem)
res = bus_cur->firstMem;
break;
case PFMEM:
if (bus_cur->firstPFMem)
res = bus_cur->firstPFMem;
break;
case IO:
if (bus_cur->firstIO)
res = bus_cur->firstIO;
break;
}
if (res) {
while (res) {
if (res->rangeno == rangeno)
break;
if (res->next)
res = res->next;
else if (res->nextRange)
res = res->nextRange;
else {
eol = TRUE;
break;
}
}
if (!eol) {
/* found the range */
while (res) {
++res->rangeno;
res = res->next;
}
}
}
}
static void fix_me (struct resource_node *res, struct bus_node *bus_cur, struct range_node *range)
{
char * str = "";
switch (res->type) {
case IO:
str = "io";
break;
case MEM:
str = "mem";
break;
case PFMEM:
str = "pfmem";
break;
}
while (res) {
if (res->rangeno == -1) {
while (range) {
if ((res->start >= range->start) && (res->end <= range->end)) {
res->rangeno = range->rangeno;
debug ("%s->rangeno in fix_resources is %d\n", str, res->rangeno);
switch (res->type) {
case IO:
--bus_cur->needIOUpdate;
break;
case MEM:
--bus_cur->needMemUpdate;
break;
case PFMEM:
--bus_cur->needPFMemUpdate;
break;
}
break;
}
range = range->next;
}
}
if (res->next)
res = res->next;
else
res = res->nextRange;
}
}
/*****************************************************************************
* This routine reassigns the range numbers to the resources that had a -1
* This case can happen only if upon initialization, resources taken by pci dev
* appear in EBDA before the resources allocated for that bus, since we don't
* know the range, we assign -1, and this routine is called after a new range
* is assigned to see the resources with unknown range belong to the added range
*
* Input: current bus
* Output: none, list of resources for that bus are fixed if can be
*******************************************************************************/
static void fix_resources (struct bus_node *bus_cur)
{
struct range_node *range;
struct resource_node *res;
debug ("%s - bus_cur->busno = %d\n", __FUNCTION__, bus_cur->busno);
if (bus_cur->needIOUpdate) {
res = bus_cur->firstIO;
range = bus_cur->rangeIO;
fix_me (res, bus_cur, range);
}
if (bus_cur->needMemUpdate) {
res = bus_cur->firstMem;
range = bus_cur->rangeMem;
fix_me (res, bus_cur, range);
}
if (bus_cur->needPFMemUpdate) {
res = bus_cur->firstPFMem;
range = bus_cur->rangePFMem;
fix_me (res, bus_cur, range);
}
}
/*******************************************************************************
* This routine adds a resource to the list of resources to the appropriate bus
* based on their resource type and sorted by their starting addresses. It assigns
* the ptrs to next and nextRange if needed.
*
* Input: 3 diff. resources (nulled out if not needed)
* Output: ptrs assigned (to the node)
* 0 or -1
*******************************************************************************/
int ibmphp_add_resource (struct resource_node *res)
{
struct resource_node *res_cur;
struct resource_node *res_prev;
struct bus_node *bus_cur;
struct range_node *range_cur = NULL;
struct resource_node *res_start = NULL;
debug ("%s - enter\n", __FUNCTION__);
bus_cur = find_bus_wprev (res->busno, NULL, 0);
if (!bus_cur) {
/* didn't find a bus, smth's wrong!!! */
err ("no bus in the system, either pci_dev's wrong or allocation failed\n");
return -ENODEV;
}
/* Normal case */
switch (res->type) {
case IO:
range_cur = bus_cur->rangeIO;
res_start = bus_cur->firstIO;
break;
case MEM:
range_cur = bus_cur->rangeMem;
res_start = bus_cur->firstMem;
break;
case PFMEM:
range_cur = bus_cur->rangePFMem;
res_start = bus_cur->firstPFMem;
break;
default:
err ("cannot read the type of the resource to add... problem \n");
return -EINVAL;
}
while (range_cur) {
if ((res->start >= range_cur->start) && (res->end <= range_cur->end)) {
res->rangeno = range_cur->rangeno;
break;
}
range_cur = range_cur->next;
}
/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
* this is again the case of rangeno = -1
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
*/
if (!range_cur) {
switch (res->type) {
case IO:
++bus_cur->needIOUpdate;
break;
case MEM:
++bus_cur->needMemUpdate;
break;
case PFMEM:
++bus_cur->needPFMemUpdate;
break;
}
res->rangeno = -1;
}
debug ("The range is %d\n", res->rangeno);
if (!res_start) {
/* no first{IO,Mem,Pfmem} on the bus, 1st IO/Mem/Pfmem resource ever */
switch (res->type) {
case IO:
bus_cur->firstIO = res;
break;
case MEM:
bus_cur->firstMem = res;
break;
case PFMEM:
bus_cur->firstPFMem = res;
break;
}
res->next = NULL;
res->nextRange = NULL;
} else {
res_cur = res_start;
res_prev = NULL;
debug ("res_cur->rangeno is %d\n", res_cur->rangeno);
while (res_cur) {
if (res_cur->rangeno >= res->rangeno)
break;
res_prev = res_cur;
if (res_cur->next)
res_cur = res_cur->next;
else
res_cur = res_cur->nextRange;
}
if (!res_cur) {
/* at the end of the resource list */
debug ("i should be here, [%x - %x]\n", res->start, res->end);
res_prev->nextRange = res;
res->next = NULL;
res->nextRange = NULL;
} else if (res_cur->rangeno == res->rangeno) {
/* in the same range */
while (res_cur) {
if (res->start < res_cur->start)
break;
res_prev = res_cur;
res_cur = res_cur->next;
}
if (!res_cur) {
/* the last resource in this range */
res_prev->next = res;
res->next = NULL;
res->nextRange = res_prev->nextRange;
res_prev->nextRange = NULL;
} else if (res->start < res_cur->start) {
/* at the beginning or middle of the range */
if (!res_prev) {
switch (res->type) {
case IO:
bus_cur->firstIO = res;
break;
case MEM:
bus_cur->firstMem = res;
break;
case PFMEM:
bus_cur->firstPFMem = res;
break;
}
} else if (res_prev->rangeno == res_cur->rangeno)
res_prev->next = res;
else
res_prev->nextRange = res;
res->next = res_cur;
res->nextRange = NULL;
}
} else {
/* this is the case where it is 1st occurence of the range */
if (!res_prev) {
/* at the beginning of the resource list */
res->next = NULL;
switch (res->type) {
case IO:
res->nextRange = bus_cur->firstIO;
bus_cur->firstIO = res;
break;
case MEM:
res->nextRange = bus_cur->firstMem;
bus_cur->firstMem = res;
break;
case PFMEM:
res->nextRange = bus_cur->firstPFMem;
bus_cur->firstPFMem = res;
break;
}
} else if (res_cur->rangeno > res->rangeno) {
/* in the middle of the resource list */
res_prev->nextRange = res;
res->next = NULL;
res->nextRange = res_cur;
}
}
}
debug ("%s - exit\n", __FUNCTION__);
return 0;
}
/****************************************************************************
* This routine will remove the resource from the list of resources
*
* Input: io, mem, and/or pfmem resource to be deleted
* Ouput: modified resource list
* 0 or error code
****************************************************************************/
int ibmphp_remove_resource (struct resource_node *res)
{
struct bus_node *bus_cur;
struct resource_node *res_cur = NULL;
struct resource_node *res_prev;
struct resource_node *mem_cur;
char * type = "";
bus_cur = find_bus_wprev (res->busno, NULL, 0);
if (!bus_cur) {
err ("cannot find corresponding bus of the io resource to remove "
"bailing out...\n");
return -ENODEV;
}
switch (res->type) {
case IO:
res_cur = bus_cur->firstIO;
type = "io";
break;
case MEM:
res_cur = bus_cur->firstMem;
type = "mem";
break;
case PFMEM:
res_cur = bus_cur->firstPFMem;
type = "pfmem";
break;
default:
err ("unknown type for resource to remove \n");
return -EINVAL;
}
res_prev = NULL;
while (res_cur) {
/* ???????????DO WE _NEED_ TO BE CHECKING FOR END AS WELL?????????? */
if ((res_cur->start == res->start) && (res_cur->end == res->end))
break;
res_prev = res_cur;
if (res_cur->next)
res_cur = res_cur->next;
else
res_cur = res_cur->nextRange;
}
if (!res_cur) {
if (res->type == PFMEM) {
/*
* case where pfmem might be in the PFMemFromMem list
* so will also need to remove the corresponding mem
* entry
*/
res_cur = bus_cur->firstPFMemFromMem;
res_prev = NULL;
while (res_cur) {
if ((res_cur->start == res->start) && (res_cur->end == res->end)) {
mem_cur = bus_cur->firstMem;
while (mem_cur) {
if ((mem_cur->start == res_cur->start)
&& (mem_cur->end == res_cur->end))
break;
if (mem_cur->next)
mem_cur = mem_cur->next;
else
mem_cur = mem_cur->nextRange;
}
if (!mem_cur) {
err ("cannot find corresponding mem node for pfmem...\n");
return -EINVAL;
}
ibmphp_remove_resource (mem_cur);
if (!res_prev)
bus_cur->firstPFMemFromMem = res_cur->next;
else
res_prev->next = res_cur->next;
kfree (res_cur);
return 0;
}
res_prev = res_cur;
if (res_cur->next)
res_cur = res_cur->next;
else
res_cur = res_cur->nextRange;
}
if (!res_cur) {
err ("cannot find pfmem to delete...\n");
return -EINVAL;
}
} else {
err ("the %s resource is not in the list to be deleted...\n", type);
return -EINVAL;
}
}
if (!res_prev) {
/* first device to be deleted */
if (res_cur->next) {
switch (res->type) {
case IO:
bus_cur->firstIO = res_cur->next;
break;
case MEM:
bus_cur->firstMem = res_cur->next;
break;
case PFMEM:
bus_cur->firstPFMem = res_cur->next;
break;
}
} else if (res_cur->nextRange) {
switch (res->type) {
case IO:
bus_cur->firstIO = res_cur->nextRange;
break;
case MEM:
bus_cur->firstMem = res_cur->nextRange;
break;
case PFMEM:
bus_cur->firstPFMem = res_cur->nextRange;
break;
}
} else {
switch (res->type) {
case IO:
bus_cur->firstIO = NULL;
break;
case MEM:
bus_cur->firstMem = NULL;
break;
case PFMEM:
bus_cur->firstPFMem = NULL;
break;
}
}
kfree (res_cur);
return 0;
} else {
if (res_cur->next) {
if (res_prev->rangeno == res_cur->rangeno)
res_prev->next = res_cur->next;
else
res_prev->nextRange = res_cur->next;
} else if (res_cur->nextRange) {
res_prev->next = NULL;
res_prev->nextRange = res_cur->nextRange;
} else {
res_prev->next = NULL;
res_prev->nextRange = NULL;
}
kfree (res_cur);
return 0;
}
return 0;
}
static struct range_node * find_range (struct bus_node *bus_cur, struct resource_node * res)
{
struct range_node * range = NULL;
switch (res->type) {
case IO:
range = bus_cur->rangeIO;
break;
case MEM:
range = bus_cur->rangeMem;
break;
case PFMEM:
range = bus_cur->rangePFMem;
break;
default:
err ("cannot read resource type in find_range \n");
}
while (range) {
if (res->rangeno == range->rangeno)
break;
range = range->next;
}
return range;
}
/*****************************************************************************
* This routine will check to make sure the io/mem/pfmem->len that the device asked for
* can fit w/i our list of available IO/MEM/PFMEM resources. If cannot, returns -EINVAL,
* otherwise, returns 0
*
* Input: resource
* Ouput: the correct start and end address are inputted into the resource node,
* 0 or -EINVAL
*****************************************************************************/
int ibmphp_check_resource (struct resource_node *res, u8 bridge)
{
struct bus_node *bus_cur;
struct range_node *range = NULL;
struct resource_node *res_prev;
struct resource_node *res_cur = NULL;
u32 len_cur = 0, start_cur = 0, len_tmp = 0;
int noranges = 0;
u32 tmp_start; /* this is to make sure start address is divisible by the length needed */
u32 tmp_divide;
u8 flag = FALSE;
if (!res)
return -EINVAL;
if (bridge) {
/* The rules for bridges are different, 4K divisible for IO, 1M for (pf)mem*/
if (res->type == IO)
tmp_divide = IOBRIDGE;
else
tmp_divide = MEMBRIDGE;
} else
tmp_divide = res->len;
bus_cur = find_bus_wprev (res->busno, NULL, 0);
if (!bus_cur) {
/* didn't find a bus, smth's wrong!!! */
err ("no bus in the system, either pci_dev's wrong or allocation failed \n");
return -EINVAL;
}
debug ("%s - enter\n", __FUNCTION__);
debug ("bus_cur->busno is %d\n", bus_cur->busno);
/* This is a quick fix to not mess up with the code very much. i.e.,
* 2000-2fff, len = 1000, but when we compare, we need it to be fff */
res->len -= 1;
switch (res->type) {
case IO:
res_cur = bus_cur->firstIO;
noranges = bus_cur->noIORanges;
break;
case MEM:
res_cur = bus_cur->firstMem;
noranges = bus_cur->noMemRanges;
break;
case PFMEM:
res_cur = bus_cur->firstPFMem;
noranges = bus_cur->noPFMemRanges;
break;
default:
err ("wrong type of resource to check \n");
return -EINVAL;
}
res_prev = NULL;
while (res_cur) {
range = find_range (bus_cur, res_cur);
debug ("%s - rangeno = %d\n", __FUNCTION__, res_cur->rangeno);
if (!range) {
err ("no range for the device exists... bailing out...\n");
return -EINVAL;
}
/* found our range */
if (!res_prev) {
/* first time in the loop */
if ((res_cur->start != range->start) && ((len_tmp = res_cur->start - 1 - range->start) >= res->len)) {
debug ("len_tmp = %x\n", len_tmp);
if ((len_tmp < len_cur) || (len_cur == 0)) {
if ((range->start % tmp_divide) == 0) {
/* just perfect, starting address is divisible by length */
flag = TRUE;
len_cur = len_tmp;
start_cur = range->start;
} else {
/* Needs adjusting */
tmp_start = range->start;
flag = FALSE;
while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) {
if ((tmp_start % tmp_divide) == 0) {
flag = TRUE;
len_cur = len_tmp;
start_cur = tmp_start;
break;
}
tmp_start += tmp_divide - tmp_start % tmp_divide;
if (tmp_start >= res_cur->start - 1)
break;
}
}
if (flag && len_cur == res->len) {
debug ("but we are not here, right?\n");
res->start = start_cur;
res->len += 1; /* To restore the balance */
res->end = res->start + res->len - 1;
return 0;
}
}
}
}
if (!res_cur->next) {
/* last device on the range */
if ((range->end != res_cur->end) && ((len_tmp = range->end - (res_cur->end + 1)) >= res->len)) {
debug ("len_tmp = %x\n", len_tmp);
if ((len_tmp < len_cur) || (len_cur == 0)) {
if (((res_cur->end + 1) % tmp_divide) == 0) {
/* just perfect, starting address is divisible by length */
flag = TRUE;
len_cur = len_tmp;
start_cur = res_cur->end + 1;
} else {
/* Needs adjusting */
tmp_start = res_cur->end + 1;
flag = FALSE;
while ((len_tmp = range->end - tmp_start) >= res->len) {
if ((tmp_start % tmp_divide) == 0) {
flag = TRUE;
len_cur = len_tmp;
start_cur = tmp_start;
break;
}
tmp_start += tmp_divide - tmp_start % tmp_divide;
if (tmp_start >= range->end)
break;
}
}
if (flag && len_cur == res->len) {
res->start = start_cur;
res->len += 1; /* To restore the balance */
res->end = res->start + res->len - 1;
return 0;
}
}
}
}
if (res_prev) {
if (res_prev->rangeno != res_cur->rangeno) {
/* 1st device on this range */
if ((res_cur->start != range->start) &&
((len_tmp = res_cur->start - 1 - range->start) >= res->len)) {
if ((len_tmp < len_cur) || (len_cur == 0)) {
if ((range->start % tmp_divide) == 0) {
/* just perfect, starting address is divisible by length */
flag = TRUE;
len_cur = len_tmp;
start_cur = range->start;
} else {
/* Needs adjusting */
tmp_start = range->start;
flag = FALSE;
while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) {
if ((tmp_start % tmp_divide) == 0) {
flag = TRUE;
len_cur = len_tmp;
start_cur = tmp_start;
break;
}
tmp_start += tmp_divide - tmp_start % tmp_divide;
if (tmp_start >= res_cur->start - 1)
break;
}
}
if (flag && len_cur == res->len) {
res->start = start_cur;
res->len += 1; /* To restore the balance */
res->end = res->start + res->len - 1;
return 0;
}
}
}
} else {
/* in the same range */
if ((len_tmp = res_cur->start - 1 - res_prev->end - 1) >= res->len) {
if ((len_tmp < len_cur) || (len_cur == 0)) {
if (((res_prev->end + 1) % tmp_divide) == 0) {
/* just perfect, starting address's divisible by length */
flag = TRUE;
len_cur = len_tmp;
start_cur = res_prev->end + 1;
} else {
/* Needs adjusting */
tmp_start = res_prev->end + 1;
flag = FALSE;
while ((len_tmp = res_cur->start - 1 - tmp_start) >= res->len) {
if ((tmp_start % tmp_divide) == 0) {
flag = TRUE;
len_cur = len_tmp;
start_cur = tmp_start;
break;
}
tmp_start += tmp_divide - tmp_start % tmp_divide;
if (tmp_start >= res_cur->start - 1)
break;
}
}
if (flag && len_cur == res->len) {
res->start = start_cur;
res->len += 1; /* To restore the balance */
res->end = res->start + res->len - 1;
return 0;
}
}
}
}
}
/* end if (res_prev) */
res_prev = res_cur;
if (res_cur->next)
res_cur = res_cur->next;
else
res_cur = res_cur->nextRange;
} /* end of while */
if (!res_prev) {
/* 1st device ever */
/* need to find appropriate range */
switch (res->type) {
case IO:
range = bus_cur->rangeIO;
break;
case MEM:
range = bus_cur->rangeMem;
break;
case PFMEM:
range = bus_cur->rangePFMem;
break;
}
while (range) {
if ((len_tmp = range->end - range->start) >= res->len) {
if ((len_tmp < len_cur) || (len_cur == 0)) {
if ((range->start % tmp_divide) == 0) {
/* just perfect, starting address's divisible by length */
flag = TRUE;
len_cur = len_tmp;
start_cur = range->start;
} else {
/* Needs adjusting */
tmp_start = range->start;
flag = FALSE;
while ((len_tmp = range->end - tmp_start) >= res->len) {
if ((tmp_start % tmp_divide) == 0) {
flag = TRUE;
len_cur = len_tmp;
start_cur = tmp_start;
break;
}
tmp_start += tmp_divide - tmp_start % tmp_divide;
if (tmp_start >= range->end)
break;
}
}
if (flag && len_cur == res->len) {
res->start = start_cur;
res->len += 1; /* To restore the balance */
res->end = res->start + res->len - 1;
return 0;
}
}
}
range = range->next;
} /* end of while */
if ((!range) && (len_cur == 0)) {
/* have gone through the list of devices and ranges and haven't found n.e.thing */
err ("no appropriate range.. bailing out...\n");
return -EINVAL;
} else if (len_cur) {
res->start = start_cur;
res->len += 1; /* To restore the balance */
res->end = res->start + res->len - 1;
return 0;
}
}
if (!res_cur) {
debug ("prev->rangeno = %d, noranges = %d\n", res_prev->rangeno, noranges);
if (res_prev->rangeno < noranges) {
/* if there're more ranges out there to check */
switch (res->type) {
case IO:
range = bus_cur->rangeIO;
break;
case MEM:
range = bus_cur->rangeMem;
break;
case PFMEM:
range = bus_cur->rangePFMem;
break;
}
while (range) {
if ((len_tmp = range->end - range->start) >= res->len) {
if ((len_tmp < len_cur) || (len_cur == 0)) {
if ((range->start % tmp_divide) == 0) {
/* just perfect, starting address's divisible by length */
flag = TRUE;
len_cur = len_tmp;
start_cur = range->start;
} else {
/* Needs adjusting */
tmp_start = range->start;
flag = FALSE;
while ((len_tmp = range->end - tmp_start) >= res->len) {
if ((tmp_start % tmp_divide) == 0) {
flag = TRUE;
len_cur = len_tmp;
start_cur = tmp_start;
break;
}
tmp_start += tmp_divide - tmp_start % tmp_divide;
if (tmp_start >= range->end)
break;
}
}
if (flag && len_cur == res->len) {
res->start = start_cur;
res->len += 1; /* To restore the balance */
res->end = res->start + res->len - 1;
return 0;
}
}
}
range = range->next;
} /* end of while */
if ((!range) && (len_cur == 0)) {
/* have gone through the list of devices and ranges and haven't found n.e.thing */
err ("no appropriate range.. bailing out...\n");
return -EINVAL;
} else if (len_cur) {
res->start = start_cur;
res->len += 1; /* To restore the balance */
res->end = res->start + res->len - 1;
return 0;
}
} else {
/* no more ranges to check on */
if (len_cur) {
res->start = start_cur;
res->len += 1; /* To restore the balance */
res->end = res->start + res->len - 1;
return 0;
} else {
/* have gone through the list of devices and haven't found n.e.thing */
err ("no appropriate range.. bailing out...\n");
return -EINVAL;
}
}
} /* end if(!res_cur) */
return -EINVAL;
}
/********************************************************************************
* This routine is called from remove_card if the card contained PPB.
* It will remove all the resources on the bus as well as the bus itself
* Input: Bus
* Ouput: 0, -ENODEV
********************************************************************************/
int ibmphp_remove_bus (struct bus_node *bus, u8 parent_busno)
{
struct resource_node *res_cur;
struct resource_node *res_tmp;
struct bus_node *prev_bus;
int rc;
prev_bus = find_bus_wprev (parent_busno, NULL, 0);
if (!prev_bus) {
err ("something terribly wrong. Cannot find parent bus to the one to remove\n");
return -ENODEV;
}
debug ("In ibmphp_remove_bus... prev_bus->busno is %x\n", prev_bus->busno);
rc = remove_ranges (bus, prev_bus);
if (rc)
return rc;
if (bus->firstIO) {
res_cur = bus->firstIO;
while (res_cur) {
res_tmp = res_cur;
if (res_cur->next)
res_cur = res_cur->next;
else
res_cur = res_cur->nextRange;
kfree (res_tmp);
res_tmp = NULL;
}
bus->firstIO = NULL;
}
if (bus->firstMem) {
res_cur = bus->firstMem;
while (res_cur) {
res_tmp = res_cur;
if (res_cur->next)
res_cur = res_cur->next;
else
res_cur = res_cur->nextRange;
kfree (res_tmp);
res_tmp = NULL;
}
bus->firstMem = NULL;
}
if (bus->firstPFMem) {
res_cur = bus->firstPFMem;
while (res_cur) {
res_tmp = res_cur;
if (res_cur->next)
res_cur = res_cur->next;
else
res_cur = res_cur->nextRange;
kfree (res_tmp);
res_tmp = NULL;
}
bus->firstPFMem = NULL;
}
if (bus->firstPFMemFromMem) {
res_cur = bus->firstPFMemFromMem;
while (res_cur) {
res_tmp = res_cur;
res_cur = res_cur->next;
kfree (res_tmp);
res_tmp = NULL;
}
bus->firstPFMemFromMem = NULL;
}
list_del (&bus->bus_list);
kfree (bus);
return 0;
}
/******************************************************************************
* This routine deletes the ranges from a given bus, and the entries from the
* parent's bus in the resources
* Input: current bus, previous bus
* Output: 0, -EINVAL
******************************************************************************/
static int remove_ranges (struct bus_node *bus_cur, struct bus_node *bus_prev)
{
struct range_node *range_cur;
struct range_node *range_tmp;
int i;
struct resource_node *res = NULL;
if (bus_cur->noIORanges) {
range_cur = bus_cur->rangeIO;
for (i = 0; i < bus_cur->noIORanges; i++) {
if (ibmphp_find_resource (bus_prev, range_cur->start, &res, IO) < 0)
return -EINVAL;
ibmphp_remove_resource (res);
range_tmp = range_cur;
range_cur = range_cur->next;
kfree (range_tmp);
range_tmp = NULL;
}
bus_cur->rangeIO = NULL;
}
if (bus_cur->noMemRanges) {
range_cur = bus_cur->rangeMem;
for (i = 0; i < bus_cur->noMemRanges; i++) {
if (ibmphp_find_resource (bus_prev, range_cur->start, &res, MEM) < 0)
return -EINVAL;
ibmphp_remove_resource (res);
range_tmp = range_cur;
range_cur = range_cur->next;
kfree (range_tmp);
range_tmp = NULL;
}
bus_cur->rangeMem = NULL;
}
if (bus_cur->noPFMemRanges) {
range_cur = bus_cur->rangePFMem;
for (i = 0; i < bus_cur->noPFMemRanges; i++) {
if (ibmphp_find_resource (bus_prev, range_cur->start, &res, PFMEM) < 0)
return -EINVAL;
ibmphp_remove_resource (res);
range_tmp = range_cur;
range_cur = range_cur->next;
kfree (range_tmp);
range_tmp = NULL;
}
bus_cur->rangePFMem = NULL;
}
return 0;
}
/*
* find the resource node in the bus
* Input: Resource needed, start address of the resource, type or resource
*/
int ibmphp_find_resource (struct bus_node *bus, u32 start_address, struct resource_node **res, int flag)
{
struct resource_node *res_cur = NULL;
char * type = "";
switch (flag) {
case IO:
res_cur = bus->firstIO;
type = "io";
break;
case MEM:
res_cur = bus->firstMem;
type = "mem";
break;
case PFMEM:
res_cur = bus->firstPFMem;
type = "pfmem";
break;
default:
err ("wrong type of flag \n");
return -EINVAL;
}
while (res_cur) {
if (res_cur->start == start_address) {
*res = res_cur;
break;
}
if (res_cur->next)
res_cur = res_cur->next;
else
res_cur = res_cur->nextRange;
}
if (!res_cur) {
if (flag == PFMEM) {
res_cur = bus->firstPFMemFromMem;
while (res_cur) {
if (res_cur->start == start_address) {
*res = res_cur;
break;
}
res_cur = res_cur->next;
}
if (!res_cur) {
err ("SOS...cannot find %s resource in the bus. \n", type);
return -EINVAL;
}
} else {
err ("SOS... cannot find %s resource in the bus. \n", type);
return -EINVAL;
}
}
if (*res)
debug ("*res->start = %x \n", (*res)->start);
return 0;
}
/***********************************************************************
* This routine will free the resource structures used by the
* system. It is called from cleanup routine for the module
* Parameters: none
* Returns: none
***********************************************************************/
void ibmphp_free_resources (void)
{
struct bus_node *bus_cur = NULL;
struct bus_node *bus_tmp;
struct range_node *range_cur;
struct range_node *range_tmp;
struct resource_node *res_cur;
struct resource_node *res_tmp;
struct list_head *tmp;
struct list_head *next;
int i = 0;
flags = 1;
list_for_each_safe (tmp, next, &gbuses) {
bus_cur = list_entry (tmp, struct bus_node, bus_list);
if (bus_cur->noIORanges) {
range_cur = bus_cur->rangeIO;
for (i = 0; i < bus_cur->noIORanges; i++) {
if (!range_cur)
break;
range_tmp = range_cur;
range_cur = range_cur->next;
kfree (range_tmp);
range_tmp = NULL;
}
}
if (bus_cur->noMemRanges) {
range_cur = bus_cur->rangeMem;
for (i = 0; i < bus_cur->noMemRanges; i++) {
if (!range_cur)
break;
range_tmp = range_cur;
range_cur = range_cur->next;
kfree (range_tmp);
range_tmp = NULL;
}
}
if (bus_cur->noPFMemRanges) {
range_cur = bus_cur->rangePFMem;
for (i = 0; i < bus_cur->noPFMemRanges; i++) {
if (!range_cur)
break;
range_tmp = range_cur;
range_cur = range_cur->next;
kfree (range_tmp);
range_tmp = NULL;
}
}
if (bus_cur->firstIO) {
res_cur = bus_cur->firstIO;
while (res_cur) {
res_tmp = res_cur;
if (res_cur->next)
res_cur = res_cur->next;
else
res_cur = res_cur->nextRange;
kfree (res_tmp);
res_tmp = NULL;
}
bus_cur->firstIO = NULL;
}
if (bus_cur->firstMem) {
res_cur = bus_cur->firstMem;
while (res_cur) {
res_tmp = res_cur;
if (res_cur->next)
res_cur = res_cur->next;
else
res_cur = res_cur->nextRange;
kfree (res_tmp);
res_tmp = NULL;
}
bus_cur->firstMem = NULL;
}
if (bus_cur->firstPFMem) {
res_cur = bus_cur->firstPFMem;
while (res_cur) {
res_tmp = res_cur;
if (res_cur->next)
res_cur = res_cur->next;
else
res_cur = res_cur->nextRange;
kfree (res_tmp);
res_tmp = NULL;
}
bus_cur->firstPFMem = NULL;
}
if (bus_cur->firstPFMemFromMem) {
res_cur = bus_cur->firstPFMemFromMem;
while (res_cur) {
res_tmp = res_cur;
res_cur = res_cur->next;
kfree (res_tmp);
res_tmp = NULL;
}
bus_cur->firstPFMemFromMem = NULL;
}
bus_tmp = bus_cur;
list_del (&bus_cur->bus_list);
kfree (bus_tmp);
bus_tmp = NULL;
}
}
/*********************************************************************************
* This function will go over the PFmem resources to check if the EBDA allocated
* pfmem out of memory buckets of the bus. If so, it will change the range numbers
* and a flag to indicate that this resource is out of memory. It will also move the
* Pfmem out of the pfmem resource list to the PFMemFromMem list, and will create
* a new Mem node
* This routine is called right after initialization
*******************************************************************************/
static int once_over (void)
{
struct resource_node *pfmem_cur;
struct resource_node *pfmem_prev;
struct resource_node *mem;
struct bus_node *bus_cur;
struct list_head *tmp;
list_for_each (tmp, &gbuses) {
bus_cur = list_entry (tmp, struct bus_node, bus_list);
if ((!bus_cur->rangePFMem) && (bus_cur->firstPFMem)) {
for (pfmem_cur = bus_cur->firstPFMem, pfmem_prev = NULL; pfmem_cur; pfmem_prev = pfmem_cur, pfmem_cur = pfmem_cur->next) {
pfmem_cur->fromMem = TRUE;
if (pfmem_prev)
pfmem_prev->next = pfmem_cur->next;
else
bus_cur->firstPFMem = pfmem_cur->next;
if (!bus_cur->firstPFMemFromMem)
pfmem_cur->next = NULL;
else
/* we don't need to sort PFMemFromMem since we're using mem node for
all the real work anyways, so just insert at the beginning of the
list
*/
pfmem_cur->next = bus_cur->firstPFMemFromMem;
bus_cur->firstPFMemFromMem = pfmem_cur;
mem = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
if (!mem) {
err ("out of system memory \n");
return -ENOMEM;
}
memset (mem, 0, sizeof (struct resource_node));
mem->type = MEM;
mem->busno = pfmem_cur->busno;
mem->devfunc = pfmem_cur->devfunc;
mem->start = pfmem_cur->start;
mem->end = pfmem_cur->end;
mem->len = pfmem_cur->len;
if (ibmphp_add_resource (mem) < 0)
err ("Trouble...trouble... EBDA allocated pfmem from mem, but system doesn't display it has this space... unless not PCI device...\n");
pfmem_cur->rangeno = mem->rangeno;
} /* end for pfmem */
} /* end if */
} /* end list_for_each bus */
return 0;
}
int ibmphp_add_pfmem_from_mem (struct resource_node *pfmem)
{
struct bus_node *bus_cur = find_bus_wprev (pfmem->busno, NULL, 0);
if (!bus_cur) {
err ("cannot find bus of pfmem to add...\n");
return -ENODEV;
}
if (bus_cur->firstPFMemFromMem)
pfmem->next = bus_cur->firstPFMemFromMem;
else
pfmem->next = NULL;
bus_cur->firstPFMemFromMem = pfmem;
return 0;
}
/* This routine just goes through the buses to see if the bus already exists.
* It is called from ibmphp_find_sec_number, to find out a secondary bus number for
* bridged cards
* Parameters: bus_number
* Returns: Bus pointer or NULL
*/
struct bus_node *ibmphp_find_res_bus (u8 bus_number)
{
return find_bus_wprev (bus_number, NULL, 0);
}
static inline struct bus_node *find_bus_wprev (u8 bus_number, struct bus_node **prev, u8 flag)
{
struct bus_node *bus_cur;
struct list_head *tmp;
struct list_head *tmp_prev;
list_for_each (tmp, &gbuses) {
tmp_prev = tmp->prev;
bus_cur = list_entry (tmp, struct bus_node, bus_list);
if (flag)
*prev = list_entry (tmp_prev, struct bus_node, bus_list);
if (bus_cur->busno == bus_number)
return bus_cur;
}
return NULL;
}
void ibmphp_print_test (void)
{
int i = 0;
struct bus_node *bus_cur = NULL;
struct range_node *range;
struct resource_node *res;
struct list_head *tmp;
if ((!list_empty(&gbuses)) && flags) {
err ("The GBUSES is not NULL?!?!?!?!?\n");
return;
}
list_for_each (tmp, &gbuses) {
bus_cur = list_entry (tmp, struct bus_node, bus_list);
debug ("This is bus # %d. There are \n", bus_cur->busno);
debug ("IORanges = %d\t", bus_cur->noIORanges);
debug ("MemRanges = %d\t", bus_cur->noMemRanges);
debug ("PFMemRanges = %d\n", bus_cur->noPFMemRanges);
debug ("The IO Ranges are as follows:\n");
if (bus_cur->rangeIO) {
range = bus_cur->rangeIO;
for (i = 0; i < bus_cur->noIORanges; i++) {
debug ("rangeno is %d\n", range->rangeno);
debug ("[%x - %x]\n", range->start, range->end);
range = range->next;
}
}
debug ("The Mem Ranges are as follows:\n");
if (bus_cur->rangeMem) {
range = bus_cur->rangeMem;
for (i = 0; i < bus_cur->noMemRanges; i++) {
debug ("rangeno is %d\n", range->rangeno);
debug ("[%x - %x]\n", range->start, range->end);
range = range->next;
}
}
debug ("The PFMem Ranges are as follows:\n");
if (bus_cur->rangePFMem) {
range = bus_cur->rangePFMem;
for (i = 0; i < bus_cur->noPFMemRanges; i++) {
debug ("rangeno is %d\n", range->rangeno);
debug ("[%x - %x]\n", range->start, range->end);
range = range->next;
}
}
debug ("The resources on this bus are as follows\n");
debug ("IO...\n");
if (bus_cur->firstIO) {
res = bus_cur->firstIO;
while (res) {
debug ("The range # is %d\n", res->rangeno);
debug ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc);
debug ("[%x - %x], len=%x\n", res->start, res->end, res->len);
if (res->next)
res = res->next;
else if (res->nextRange)
res = res->nextRange;
else
break;
}
}
debug ("Mem...\n");
if (bus_cur->firstMem) {
res = bus_cur->firstMem;
while (res) {
debug ("The range # is %d\n", res->rangeno);
debug ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc);
debug ("[%x - %x], len=%x\n", res->start, res->end, res->len);
if (res->next)
res = res->next;
else if (res->nextRange)
res = res->nextRange;
else
break;
}
}
debug ("PFMem...\n");
if (bus_cur->firstPFMem) {
res = bus_cur->firstPFMem;
while (res) {
debug ("The range # is %d\n", res->rangeno);
debug ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc);
debug ("[%x - %x], len=%x\n", res->start, res->end, res->len);
if (res->next)
res = res->next;
else if (res->nextRange)
res = res->nextRange;
else
break;
}
}
debug ("PFMemFromMem...\n");
if (bus_cur->firstPFMemFromMem) {
res = bus_cur->firstPFMemFromMem;
while (res) {
debug ("The range # is %d\n", res->rangeno);
debug ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc);
debug ("[%x - %x], len=%x\n", res->start, res->end, res->len);
res = res->next;
}
}
}
}
/* This routine will read the windows for any PPB we have and update the
* range info for the secondary bus, and will also input this info into
* primary bus, since BIOS doesn't. This is for PPB that are in the system
* on bootup
* Input: primary busno
* Returns: none
* Note: this function doesn't take into account IO restrictions etc,
* so will only work for bridges with no video/ISA devices behind them It
* also will not work for onboard PPB's that can have more than 1 *bus
* behind them All these are TO DO.
* Also need to add more error checkings... (from fnc returns etc)
*/
static int update_bridge_ranges (struct bus_node **bus)
{
u8 sec_busno, device, function, busno, hdr_type, start_io_address, end_io_address;
u16 vendor_id, upper_io_start, upper_io_end, start_mem_address, end_mem_address;
u32 start_address, end_address, upper_start, upper_end;
struct bus_node *bus_sec;
struct bus_node *bus_cur;
struct resource_node *io;
struct resource_node *mem;
struct resource_node *pfmem;
struct range_node *range;
bus_cur = *bus;
busno = bus_cur->busno;
debug ("inside update_bridge_ranges \n");
debug ("bus_cur->busno = %x\n", bus_cur->busno);
for (device = 0; device < 32; device++) {
for (function = 0x00; function < 0x08; function++) {
pci_read_config_word_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_VENDOR_ID, &vendor_id);
if (vendor_id != PCI_VENDOR_ID_NOTVALID) {
/* found correct device!!! */
pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_HEADER_TYPE, &hdr_type);
switch (hdr_type) {
case PCI_HEADER_TYPE_NORMAL:
function = 0x8;
break;
case PCI_HEADER_TYPE_MULTIDEVICE:
break;
case PCI_HEADER_TYPE_BRIDGE:
function = 0x8;
case PCI_HEADER_TYPE_MULTIBRIDGE:
/* We assume here that only 1 bus behind the bridge
TO DO: add functionality for several:
temp = secondary;
while (temp < subordinate) {
...
temp++;
}
*/
pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_SECONDARY_BUS, &sec_busno);
bus_sec = find_bus_wprev (sec_busno, NULL, 0);
pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_IO_BASE, &start_io_address);
pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_IO_LIMIT, &end_io_address);
pci_read_config_word_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_IO_BASE_UPPER16, &upper_io_start);
pci_read_config_word_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_IO_LIMIT_UPPER16, &upper_io_end);
start_address = (start_io_address & PCI_IO_RANGE_MASK) << 8;
start_address |= (upper_io_start << 16);
end_address = (end_io_address & PCI_IO_RANGE_MASK) << 8;
end_address |= (upper_io_end << 16);
if ((start_address) && (start_address <= end_address)) {
range = kmalloc (sizeof (struct range_node), GFP_KERNEL);
if (!range) {
err ("out of system memory \n");
return -ENOMEM;
}
memset (range, 0, sizeof (struct range_node));
range->start = start_address;
range->end = end_address + 0xfff;
if (bus_sec->noIORanges > 0)
add_range (IO, range, bus_sec);
else {
/* 1st IO Range on the bus */
range->rangeno = 1;
bus_sec->rangeIO = range;
}
++bus_sec->noIORanges;
fix_resources (bus_sec);
io = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
if (!io) {
kfree (range);
err ("out of system memory \n");
return -ENOMEM;
}
memset (io, 0, sizeof (struct resource_node));
io->type = IO;
io->busno = bus_cur->busno;
io->devfunc = ((device << 3) | (function & 0x7));
io->start = start_address;
io->end = end_address + 0xfff;
io->len = io->end - io->start + 1;
ibmphp_add_resource (io);
}
pci_read_config_word_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_MEMORY_BASE, &start_mem_address);
pci_read_config_word_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_MEMORY_LIMIT, &end_mem_address);
start_address = 0x00000000 | (start_mem_address & PCI_MEMORY_RANGE_MASK) << 16;
end_address = 0x00000000 | (end_mem_address & PCI_MEMORY_RANGE_MASK) << 16;
if ((start_address) && (start_address <= end_address)) {
range = kmalloc (sizeof (struct range_node), GFP_KERNEL);
if (!range) {
err ("out of system memory \n");
return -ENOMEM;
}
memset (range, 0, sizeof (struct range_node));
range->start = start_address;
range->end = end_address + 0xfffff;
if (bus_sec->noMemRanges > 0)
add_range (MEM, range, bus_sec);
else {
/* 1st Mem Range on the bus */
range->rangeno = 1;
bus_sec->rangeMem = range;
}
++bus_sec->noMemRanges;
fix_resources (bus_sec);
mem = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
if (!mem) {
kfree (range);
err ("out of system memory \n");
return -ENOMEM;
}
memset (mem, 0, sizeof (struct resource_node));
mem->type = MEM;
mem->busno = bus_cur->busno;
mem->devfunc = ((device << 3) | (function & 0x7));
mem->start = start_address;
mem->end = end_address + 0xfffff;
mem->len = mem->end - mem->start + 1;
ibmphp_add_resource (mem);
}
pci_read_config_word_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_PREF_MEMORY_BASE, &start_mem_address);
pci_read_config_word_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_PREF_MEMORY_LIMIT, &end_mem_address);
pci_read_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_PREF_BASE_UPPER32, &upper_start);
pci_read_config_dword_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_PREF_LIMIT_UPPER32, &upper_end);
start_address = 0x00000000 | (start_mem_address & PCI_MEMORY_RANGE_MASK) << 16;
end_address = 0x00000000 | (end_mem_address & PCI_MEMORY_RANGE_MASK) << 16;
#if BITS_PER_LONG == 64
start_address |= ((long) upper_start) << 32;
end_address |= ((long) upper_end) << 32;
#endif
if ((start_address) && (start_address <= end_address)) {
range = kmalloc (sizeof (struct range_node), GFP_KERNEL);
if (!range) {
err ("out of system memory \n");
return -ENOMEM;
}
memset (range, 0, sizeof (struct range_node));
range->start = start_address;
range->end = end_address + 0xfffff;
if (bus_sec->noPFMemRanges > 0)
add_range (PFMEM, range, bus_sec);
else {
/* 1st PFMem Range on the bus */
range->rangeno = 1;
bus_sec->rangePFMem = range;
}
++bus_sec->noPFMemRanges;
fix_resources (bus_sec);
pfmem = kmalloc (sizeof (struct resource_node), GFP_KERNEL);
if (!pfmem) {
kfree (range);
err ("out of system memory \n");
return -ENOMEM;
}
memset (pfmem, 0, sizeof (struct resource_node));
pfmem->type = PFMEM;
pfmem->busno = bus_cur->busno;
pfmem->devfunc = ((device << 3) | (function & 0x7));
pfmem->start = start_address;
pfmem->end = end_address + 0xfffff;
pfmem->len = pfmem->end - pfmem->start + 1;
pfmem->fromMem = FALSE;
ibmphp_add_resource (pfmem);
}
break;
} /* end of switch */
} /* end if vendor */
} /* end for function */
} /* end for device */
bus = &bus_cur;
return 0;
}
/* /*
* PCI HotPlug Controller Core * PCI HotPlug Controller Core
* *
* Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com) * Copyright (c) 2001-2002 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (c) 2001 IBM Corp. * Copyright (c) 2001-2002 IBM Corp.
* *
* All rights reserved. * All rights reserved.
* *
...@@ -23,6 +23,8 @@ ...@@ -23,6 +23,8 @@
* *
* Send feedback to <greg@kroah.com> * Send feedback to <greg@kroah.com>
* *
* Filesystem portion based on work done by Pat Mochel on ddfs/driverfs
*
*/ */
#include <linux/config.h> #include <linux/config.h>
...@@ -54,7 +56,7 @@ ...@@ -54,7 +56,7 @@
/* local variables */ /* local variables */
static int debug; static int debug;
#define DRIVER_VERSION "0.3" #define DRIVER_VERSION "0.4"
#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>" #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>"
#define DRIVER_DESC "PCI Hot Plug PCI Core" #define DRIVER_DESC "PCI Hot Plug PCI Core"
...@@ -74,7 +76,6 @@ struct hotplug_slot_core { ...@@ -74,7 +76,6 @@ struct hotplug_slot_core {
}; };
static struct super_operations pcihpfs_ops; static struct super_operations pcihpfs_ops;
static struct address_space_operations pcihpfs_aops;
static struct file_operations pcihpfs_dir_operations; static struct file_operations pcihpfs_dir_operations;
static struct file_operations default_file_operations; static struct file_operations default_file_operations;
static struct inode_operations pcihpfs_dir_inode_operations; static struct inode_operations pcihpfs_dir_inode_operations;
...@@ -112,7 +113,6 @@ static struct inode *pcihpfs_get_inode (struct super_block *sb, int mode, int de ...@@ -112,7 +113,6 @@ static struct inode *pcihpfs_get_inode (struct super_block *sb, int mode, int de
inode->i_blksize = PAGE_CACHE_SIZE; inode->i_blksize = PAGE_CACHE_SIZE;
inode->i_blocks = 0; inode->i_blocks = 0;
inode->i_rdev = NODEV; inode->i_rdev = NODEV;
inode->i_mapping->a_ops = &pcihpfs_aops;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
switch (mode & S_IFMT) { switch (mode & S_IFMT) {
default: default:
...@@ -154,18 +154,6 @@ static int pcihpfs_create (struct inode *dir, struct dentry *dentry, int mode) ...@@ -154,18 +154,6 @@ static int pcihpfs_create (struct inode *dir, struct dentry *dentry, int mode)
return pcihpfs_mknod (dir, dentry, mode | S_IFREG, 0); return pcihpfs_mknod (dir, dentry, mode | S_IFREG, 0);
} }
static int pcihpfs_link (struct dentry *old_dentry, struct inode *dir,
struct dentry *dentry)
{
struct inode *inode = old_dentry->d_inode;
inode->i_nlink++;
atomic_inc(&inode->i_count);
dget(dentry);
d_instantiate(dentry, inode);
return 0;
}
static inline int pcihpfs_positive (struct dentry *dentry) static inline int pcihpfs_positive (struct dentry *dentry)
{ {
return dentry->d_inode && !d_unhashed(dentry); return dentry->d_inode && !d_unhashed(dentry);
...@@ -196,29 +184,15 @@ static int pcihpfs_unlink (struct inode *dir, struct dentry *dentry) ...@@ -196,29 +184,15 @@ static int pcihpfs_unlink (struct inode *dir, struct dentry *dentry)
if (pcihpfs_empty(dentry)) { if (pcihpfs_empty(dentry)) {
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
lock_kernel();
inode->i_nlink--; inode->i_nlink--;
unlock_kernel();
dput(dentry); dput(dentry);
error = 0; error = 0;
} }
return error; return error;
} }
static int pcihpfs_rename (struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
{
int error = -ENOTEMPTY;
if (pcihpfs_empty(new_dentry)) {
struct inode *inode = new_dentry->d_inode;
if (inode) {
inode->i_nlink--;
dput(new_dentry);
}
error = 0;
}
return error;
}
#define pcihpfs_rmdir pcihpfs_unlink #define pcihpfs_rmdir pcihpfs_unlink
/* default file operations */ /* default file operations */
...@@ -238,6 +212,7 @@ static loff_t default_file_lseek (struct file *file, loff_t offset, int orig) ...@@ -238,6 +212,7 @@ static loff_t default_file_lseek (struct file *file, loff_t offset, int orig)
{ {
loff_t retval = -EINVAL; loff_t retval = -EINVAL;
lock_kernel();
switch(orig) { switch(orig) {
case 0: case 0:
if (offset > 0) { if (offset > 0) {
...@@ -254,6 +229,7 @@ static loff_t default_file_lseek (struct file *file, loff_t offset, int orig) ...@@ -254,6 +229,7 @@ static loff_t default_file_lseek (struct file *file, loff_t offset, int orig)
default: default:
break; break;
} }
unlock_kernel();
return retval; return retval;
} }
...@@ -265,18 +241,9 @@ static int default_open (struct inode *inode, struct file *filp) ...@@ -265,18 +241,9 @@ static int default_open (struct inode *inode, struct file *filp)
return 0; return 0;
} }
static int default_sync_file (struct file *file, struct dentry *dentry, int datasync)
{
return 0;
}
static struct address_space_operations pcihpfs_aops = {
};
static struct file_operations pcihpfs_dir_operations = { static struct file_operations pcihpfs_dir_operations = {
read: generic_read_dir, read: generic_read_dir,
readdir: dcache_readdir, readdir: dcache_readdir,
fsync: default_sync_file,
}; };
static struct file_operations default_file_operations = { static struct file_operations default_file_operations = {
...@@ -284,8 +251,6 @@ static struct file_operations default_file_operations = { ...@@ -284,8 +251,6 @@ static struct file_operations default_file_operations = {
write: default_write_file, write: default_write_file,
open: default_open, open: default_open,
llseek: default_file_lseek, llseek: default_file_lseek,
fsync: default_sync_file,
mmap: generic_file_mmap,
}; };
/* file ops for the "power" files */ /* file ops for the "power" files */
...@@ -296,8 +261,6 @@ static struct file_operations power_file_operations = { ...@@ -296,8 +261,6 @@ static struct file_operations power_file_operations = {
write: power_write_file, write: power_write_file,
open: default_open, open: default_open,
llseek: default_file_lseek, llseek: default_file_lseek,
fsync: default_sync_file,
mmap: generic_file_mmap,
}; };
/* file ops for the "attention" files */ /* file ops for the "attention" files */
...@@ -308,8 +271,6 @@ static struct file_operations attention_file_operations = { ...@@ -308,8 +271,6 @@ static struct file_operations attention_file_operations = {
write: attention_write_file, write: attention_write_file,
open: default_open, open: default_open,
llseek: default_file_lseek, llseek: default_file_lseek,
fsync: default_sync_file,
mmap: generic_file_mmap,
}; };
/* file ops for the "latch" files */ /* file ops for the "latch" files */
...@@ -319,8 +280,6 @@ static struct file_operations latch_file_operations = { ...@@ -319,8 +280,6 @@ static struct file_operations latch_file_operations = {
write: default_write_file, write: default_write_file,
open: default_open, open: default_open,
llseek: default_file_lseek, llseek: default_file_lseek,
fsync: default_sync_file,
mmap: generic_file_mmap,
}; };
/* file ops for the "presence" files */ /* file ops for the "presence" files */
...@@ -330,8 +289,6 @@ static struct file_operations presence_file_operations = { ...@@ -330,8 +289,6 @@ static struct file_operations presence_file_operations = {
write: default_write_file, write: default_write_file,
open: default_open, open: default_open,
llseek: default_file_lseek, llseek: default_file_lseek,
fsync: default_sync_file,
mmap: generic_file_mmap,
}; };
/* file ops for the "test" files */ /* file ops for the "test" files */
...@@ -341,19 +298,15 @@ static struct file_operations test_file_operations = { ...@@ -341,19 +298,15 @@ static struct file_operations test_file_operations = {
write: test_write_file, write: test_write_file,
open: default_open, open: default_open,
llseek: default_file_lseek, llseek: default_file_lseek,
fsync: default_sync_file,
mmap: generic_file_mmap,
}; };
static struct inode_operations pcihpfs_dir_inode_operations = { static struct inode_operations pcihpfs_dir_inode_operations = {
create: pcihpfs_create, create: pcihpfs_create,
lookup: pcihpfs_lookup, lookup: pcihpfs_lookup,
link: pcihpfs_link,
unlink: pcihpfs_unlink, unlink: pcihpfs_unlink,
mkdir: pcihpfs_mkdir, mkdir: pcihpfs_mkdir,
rmdir: pcihpfs_rmdir, rmdir: pcihpfs_rmdir,
mknod: pcihpfs_mknod, mknod: pcihpfs_mknod,
rename: pcihpfs_rename,
}; };
static struct super_operations pcihpfs_ops = { static struct super_operations pcihpfs_ops = {
...@@ -485,7 +438,7 @@ static int pcihpfs_create_by_name (const char *name, mode_t mode, ...@@ -485,7 +438,7 @@ static int pcihpfs_create_by_name (const char *name, mode_t mode,
if (!parent) { if (!parent) {
dbg("Ah! can not find a parent!\n"); dbg("Ah! can not find a parent!\n");
return -EFAULT; return -EINVAL;
} }
*dentry = NULL; *dentry = NULL;
...@@ -686,7 +639,7 @@ static ssize_t power_write_file (struct file *file, const char *ubuff, size_t co ...@@ -686,7 +639,7 @@ static ssize_t power_write_file (struct file *file, const char *ubuff, size_t co
default: default:
err ("Illegal value specified for power\n"); err ("Illegal value specified for power\n");
retval = -EFAULT; retval = -EINVAL;
} }
exit: exit:
...@@ -1019,7 +972,7 @@ int pci_hp_register (struct hotplug_slot *slot) ...@@ -1019,7 +972,7 @@ int pci_hp_register (struct hotplug_slot *slot)
if (slot == NULL) if (slot == NULL)
return -ENODEV; return -ENODEV;
if ((slot->info == NULL) || (slot->ops == NULL)) if ((slot->info == NULL) || (slot->ops == NULL))
return -EFAULT; return -EINVAL;
core = kmalloc (sizeof (struct hotplug_slot_core), GFP_KERNEL); core = kmalloc (sizeof (struct hotplug_slot_core), GFP_KERNEL);
if (!core) if (!core)
...@@ -1030,7 +983,7 @@ int pci_hp_register (struct hotplug_slot *slot) ...@@ -1030,7 +983,7 @@ int pci_hp_register (struct hotplug_slot *slot)
if (get_slot_from_name (slot->name) != NULL) { if (get_slot_from_name (slot->name) != NULL) {
spin_unlock (&list_lock); spin_unlock (&list_lock);
kfree (core); kfree (core);
return -EFAULT; return -EINVAL;
} }
slot->core_priv = core; slot->core_priv = core;
...@@ -1076,6 +1029,12 @@ int pci_hp_deregister (struct hotplug_slot *slot) ...@@ -1076,6 +1029,12 @@ int pci_hp_deregister (struct hotplug_slot *slot)
return 0; return 0;
} }
static inline void update_inode_time (struct inode *inode)
{
if (inode)
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
}
/** /**
* pci_hp_change_slot_info - changes the slot's information structure in the core * pci_hp_change_slot_info - changes the slot's information structure in the core
* @name: the name of the slot whose info has changed * @name: the name of the slot whose info has changed
...@@ -1089,6 +1048,7 @@ int pci_hp_deregister (struct hotplug_slot *slot) ...@@ -1089,6 +1048,7 @@ int pci_hp_deregister (struct hotplug_slot *slot)
int pci_hp_change_slot_info (const char *name, struct hotplug_slot_info *info) int pci_hp_change_slot_info (const char *name, struct hotplug_slot_info *info)
{ {
struct hotplug_slot *temp; struct hotplug_slot *temp;
struct hotplug_slot_core *core;
if (info == NULL) if (info == NULL)
return -ENODEV; return -ENODEV;
...@@ -1100,6 +1060,24 @@ int pci_hp_change_slot_info (const char *name, struct hotplug_slot_info *info) ...@@ -1100,6 +1060,24 @@ int pci_hp_change_slot_info (const char *name, struct hotplug_slot_info *info)
return -ENODEV; return -ENODEV;
} }
/*
* check all fields in the info structure, and update timestamps
* for the files referring to the fields that have now changed.
*/
core = temp->core_priv;
if ((core->power_dentry) &&
(temp->info->power_status != info->power_status))
update_inode_time (core->power_dentry->d_inode);
if ((core->attention_dentry) &&
(temp->info->attention_status != info->attention_status))
update_inode_time (core->attention_dentry->d_inode);
if ((core->latch_dentry) &&
(temp->info->latch_status != info->latch_status))
update_inode_time (core->latch_dentry->d_inode);
if ((core->adapter_dentry) &&
(temp->info->adapter_status != info->adapter_status))
update_inode_time (core->adapter_dentry->d_inode);
memcpy (temp->info, info, sizeof (struct hotplug_slot_info)); memcpy (temp->info, info, sizeof (struct hotplug_slot_info));
spin_unlock (&list_lock); spin_unlock (&list_lock);
return 0; return 0;
......
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