Written by Irene Zubarev, Tong Yu, Jyoti Shah, Chuck Cole, and me.
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.
+
mainmenu_option next_comment
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_tristate ' IBM PCI Hotplug driver' CONFIG_HOTPLUG_PCI_IBM $CONFIG_HOTPLUG_PCI $CONFIG_X86_IO_APIC $CONFIG_X86
endmenu
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
obj-$(CONFIG_HOTPLUG_PCI) += pci_hotplug.o
obj-$(CONFIG_HOTPLUG_PCI_COMPAQ) += cpqphp.o
+obj-$(CONFIG_HOTPLUG_PCI_IBM) += ibmphp.o
pci_hotplug-objs := pci_hotplug_core.o \
pci_hotplug_util.o
cpqphp_proc.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)
cpqphp-objs += cpqphp_nvram.o
endif
cpqphp.o: $(cpqphp-objs)
$(LD) -r -o $@ $(cpqphp-objs)
+ibmphp.o: $(ibmphp-objs)
+ $(LD) -r -o $@ $(ibmphp-objs)
+
--- /dev/null
+#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
+
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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;
+ }
+}
+
--- /dev/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;
+}
--- /dev/null
+/*
+ * 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;
+}
+
--- /dev/null
+/*
+ * 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;
+}