diff -Nru a/arch/i386/kernel/i386_ksyms.c b/arch/i386/kernel/i386_ksyms.c --- a/arch/i386/kernel/i386_ksyms.c Thu Jan 17 15:25:49 2002 +++ b/arch/i386/kernel/i386_ksyms.c Thu Jan 17 15:25:49 2002 @@ -144,6 +144,10 @@ EXPORT_SYMBOL(flush_tlb_page); #endif +#ifdef CONFIG_X86_IO_APIC +EXPORT_SYMBOL(IO_APIC_get_PCI_irq_vector); +#endif + #ifdef CONFIG_MCA EXPORT_SYMBOL(machine_id); #endif diff -Nru a/drivers/hotplug/Config.in b/drivers/hotplug/Config.in --- a/drivers/hotplug/Config.in Thu Jan 17 15:25:47 2002 +++ b/drivers/hotplug/Config.in Thu Jan 17 15:25:47 2002 @@ -4,9 +4,10 @@ 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_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 diff -Nru a/drivers/hotplug/Makefile b/drivers/hotplug/Makefile --- a/drivers/hotplug/Makefile Thu Jan 17 15:25:49 2002 +++ b/drivers/hotplug/Makefile Thu Jan 17 15:25:49 2002 @@ -4,12 +4,13 @@ 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 @@ -19,6 +20,12 @@ 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 @@ -31,4 +38,7 @@ cpqphp.o: $(cpqphp-objs) $(LD) -r -o $@ $(cpqphp-objs) + +ibmphp.o: $(ibmphp-objs) + $(LD) -r -o $@ $(ibmphp-objs) diff -Nru a/drivers/hotplug/ibmphp.h b/drivers/hotplug/ibmphp.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/hotplug/ibmphp.h Thu Jan 17 15:25:50 2002 @@ -0,0 +1,721 @@ +#ifndef __IBMPHP_H +#define __IBMPHP_H + +/* + * IBM Hot Plug Controller Driver + * + * 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 + * + */ + +#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 + + +/**************************************************************** +* 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; + 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 int ibmphp_ebda_rsrc_controller (void); +extern int ibmphp_ebda_rsrc_rsrc (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 u8 ibmphp_get_total_controllers (void); + +#define MAX_CTRL ibmphp_get_total_controllers () + +/* Resource Management part of the device driver return types */ + +#define RESERROR 1 +#define NOTSUPPORTED 2 + +/* 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 VENDORNOTVALID 0xFFFF +#define SINGLEDEVICE 0x00 +#define SINGLEBRIDGE 0x01 +#define MULTIDEVICE 0x80 +#define MULTIBRIDGE 0x81 +#define BRIDGE 0x01 + +#define VGACOMPATIBLE 0x0001 +#define DISPLAYVGA 0x0300 +#define PCI2PCIBRIDGE 0x0604 +#define SCSI 0x01 +#define LAN 0x02 + +#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 function prototypes +//---------------------------------------------------------------------------- + +extern void ibmphp_hpc_initvars (void); + +extern u8 ibmphp_hpcreadslot (struct slot *, u8, u8 *); +extern u8 ibmphp_hpcwriteslot (struct slot *, u8); + +extern u8 ibmphp_lock_operations (int); +extern void ibmphp_unlock_operations (void); + +extern u8 ibmphp_hpcfillhpslotinfo (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_SUCCESS 0x00 +#define HPC_FAILURE 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 0x0F +#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(s) ((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) >= 2) + +#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; + 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; +extern void ibmphp_long_delay (int); + +#endif //__IBMPHP_H + diff -Nru a/drivers/hotplug/ibmphp_core.c b/drivers/hotplug/ibmphp_core.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/hotplug/ibmphp_core.c Thu Jan 17 15:25:50 2002 @@ -0,0 +1,1341 @@ +/* + * IBM Hot Plug Controller Driver + * + * 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 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../arch/i386/kernel/pci-i386.h" /* for struct irq_routing_table */ +#include "ibmphp.h" + +#define attn_on(sl) ibmphp_hpcwriteslot(sl, HPC_SLOT_ATTNON) +#define attn_off(sl) ibmphp_hpcwriteslot(sl, HPC_SLOT_ATTNOFF) +#define attn_LED_blink(sl) ibmphp_hpcwriteslot (sl, HPC_SLOT_BLINKLED) +#define get_ctrl_revision(sl, rev) ibmphp_hpcreadslot (sl, READ_REVLEVEL, rev) +#define get_hpc_options(sl, opt) ibmphp_hpcreadslot (sl, READ_HPCOPTIONS, opt) + +int ibmphp_debug; + +static int debug; +MODULE_PARM (debug, "i"); +MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); +MODULE_LICENSE ("GPL"); + +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_hpcreadslot (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_hpcreadslot (*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) { + printk ("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; + } /* end if */ + } /* end if */ + } /* end for */ + + return -1; +} + +static inline int power_on (struct slot *slot_cur) +{ + u8 cmd = HPC_SLOT_ON; + if (ibmphp_hpcwriteslot (slot_cur, cmd)) { + printk ("power on failed\n"); + return -1; + } + if (CTLR_RESULT (slot_cur->ctrl->status)) { + printk ("command not completed successfully in power_on \n"); + return -1; + } + ibmphp_long_delay (3 * HZ); + return 0; +} + +static inline int power_off (struct slot *slot_cur) +{ + u8 cmd = HPC_SLOT_OFF; + if (ibmphp_hpcwriteslot (slot_cur, cmd)) { + printk ("power off failed \n"); + return -1; + } + if (CTLR_RESULT (slot_cur->ctrl->status)) { + printk ("command not completed successfully in power_off \n"); + return -1; + } + return 0; +} + +static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 value) +{ + int rc = 0; + struct slot *pslot; + + u8 cmd, hpcrc = HPC_SUCCESS; + + debug ("set_attention_status - Entry hotplug_slot[%lx] value[%x]\n", (ulong) hotplug_slot, value); + ibmphp_lock_operations (0); + 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; + printk ("set_attention_status - Error : invalid input [%x]\n", value); + break; + } + if (rc == 0) { + pslot = (struct slot *) hotplug_slot->private; + if (pslot) + hpcrc = ibmphp_hpcwriteslot (pslot, cmd); + else + rc = -ENODEV; + } + } else + rc = -ENODEV; + + if (hpcrc != HPC_SUCCESS) + rc = -ENODEV; + + 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; + u8 hpcrc = HPC_SUCCESS; + struct slot myslot; + + debug ("get_attention_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong) hotplug_slot, (ulong) value); + + ibmphp_lock_operations (0); + if (hotplug_slot && value) { + pslot = (struct slot *) hotplug_slot->private; + if (pslot) { + memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot)); + hpcrc = ibmphp_hpcreadslot (pslot, READ_SLOTSTATUS, &(myslot.status)); + if (hpcrc == HPC_SUCCESS) + hpcrc = ibmphp_hpcreadslot (pslot, READ_EXTSLOTSTATUS, &(myslot.ext_status)); + if (hpcrc == HPC_SUCCESS) { + *value = SLOT_ATTN (myslot.status, myslot.ext_status); + rc = 0; + } + } + } else + rc = -ENODEV; + + if (hpcrc != HPC_SUCCESS) + rc = -ENODEV; + + 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; + u8 hpcrc = HPC_SUCCESS; + struct slot myslot; + + debug ("get_latch_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong) hotplug_slot, (ulong) value); + ibmphp_lock_operations (0); + if (hotplug_slot && value) { + pslot = (struct slot *) hotplug_slot->private; + if (pslot) { + memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot)); + hpcrc = ibmphp_hpcreadslot (pslot, READ_SLOTSTATUS, &(myslot.status)); + if (hpcrc == HPC_SUCCESS) { + *value = SLOT_LATCH (myslot.status); + rc = 0; + } + } + } else + rc = -ENODEV; + + if (hpcrc != HPC_SUCCESS) + rc = -ENODEV; + + 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; + u8 hpcrc = HPC_SUCCESS; + struct slot myslot; + + debug ("get_power_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong) hotplug_slot, (ulong) value); + ibmphp_lock_operations (0); + if (hotplug_slot && value) { + pslot = (struct slot *) hotplug_slot->private; + if (pslot) { + memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot)); + hpcrc = ibmphp_hpcreadslot (pslot, READ_SLOTSTATUS, &(myslot.status)); + if (hpcrc == HPC_SUCCESS) { + *value = SLOT_POWER (myslot.status); + rc = 0; + } + } + } else + rc = -ENODEV; + + if (hpcrc != HPC_SUCCESS) + rc = -ENODEV; + + 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; + u8 hpcrc = HPC_SUCCESS; + struct slot myslot; + + debug ("get_adapter_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong) hotplug_slot, (ulong) value); + ibmphp_lock_operations (0); + if (hotplug_slot && value) { + pslot = (struct slot *) hotplug_slot->private; + if (pslot) { + memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot)); + hpcrc = ibmphp_hpcreadslot (pslot, READ_SLOTSTATUS, &(myslot.status)); + if (hpcrc == HPC_SUCCESS) { + present = SLOT_PRESENT (myslot.status); + if (present == HPC_SLOT_EMPTY) + *value = 0; + else + *value = 1; + rc = 0; + } + } + } else + rc = -ENODEV; + if (hpcrc != HPC_SUCCESS) + rc = -ENODEV; + + 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 (0); + + 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 (0); + + 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; + u8 hpcrc = HPC_SUCCESS; + 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 (0); + + if (hotplug_slot && value) { + pslot = (struct slot *) hotplug_slot->private; + if (pslot) { + memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot)); + hpcrc = ibmphp_hpcreadslot (pslot, READ_SLOTSTATUS, &(myslot.status)); + + if (!(SLOT_LATCH (myslot.status)) && (SLOT_PRESENT (myslot.status))) { + hpcrc = ibmphp_hpcreadslot (pslot, READ_EXTSLOTSTATUS, &(myslot.ext_status)); + if (hpcrc == HPC_SUCCESS) { + *value = SLOT_SPEED (myslot.ext_status); + rc = 0; + } + } else { + *value = MAX_ADAPTER_NONE; + rc = 0; + } + } + } else + rc = -ENODEV; + + if (hpcrc != HPC_SUCCESS) + rc = -ENODEV; + + if (flag) + ibmphp_unlock_operations (); + + debug ("get_adapter_present - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *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 j; + + for (j = 0; j < MAX_OPS; j++) { + ops[j] = (int *) kmalloc ((max_slots + 1) * sizeof (int), GFP_KERNEL); + if (!ops[j]) { + printk ("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; + + if (slot_update (&slot_cur)) + return -1; + + 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"); + if (power_off (slot_cur) == -1) + return -1; + + /* if (slot_update (&slot_cur)) + return -1; + 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; + if (!slot_cur) + return -EFAULT; + number = slot_cur->number; + if ((number > max_slots) || (number < 0)) + return -EBADSLT; + debug ("slot_number in validate is %d\n", slot_cur->number); + + if (slot_update (&slot_cur)) + return -1; + + 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 -EFAULT; + break; + } + printk ("validate failed....\n"); + return -EFAULT; +} + +/******************************************************************************** + * 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) { + printk ("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; +*/ + 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; + + list_for_each (tmp, &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)) { + printk ("***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 + debug ("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) { + printk ("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; +} + +static inline void print_card_capability(struct slot *slot_cur) +{ + printk ("capability of the card is "); + if ((slot_cur->ext_status & CARD_INFO) == PCIX133) + printk ("133 MHz PCI-X \n"); + else if ((slot_cur->ext_status & CARD_INFO) == PCIX66) + printk ("66 MHz PCI-X \n"); + else if ((slot_cur->ext_status & CARD_INFO) == PCI66) + printk ("66 MHz PCI \n"); + else + printk ("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; + struct slot *slot_cur; + u8 function; + u8 faulted = 0; + struct pci_func *tmp_func; + + ibmphp_lock_operations (0); + + printk ("ENABLING SLOT........ \n"); + slot_cur = (struct slot *) hs->private; + + if ((rc = validate (slot_cur, ENABLE))) { + printk ("validate function failed \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; + } + ibmphp_update_slot_info (slot_cur); + ibmphp_unlock_operations (); + return rc; + } + + attn_LED_blink (slot_cur); + + rc = power_on (slot_cur); + + if (rc == -1) { + printk ("smth wrong when powering up... please see below for details\n"); + 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 -EFAULT; + } + /* Check to see the error of why it failed */ + if (!(SLOT_PWRGD (slot_cur->status))) + printk ("power fault occured trying to power up \n"); + else if (SLOT_BUS_SPEED (slot_cur->status)) { + printk ("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)) + printk ("bus mode mismatch occured. please check current bus mode and card capability \n"); + + ibmphp_update_slot_info (slot_cur); + ibmphp_unlock_operations (); + return -1; /* WHAT IS THE ERROR MESSAGE HERE?????? */ + } + debug ("after power_on\n"); + + if (slot_update (&slot_cur)) { + attn_off (slot_cur); + attn_on (slot_cur); + if (power_off (slot_cur) == -1) { + ibmphp_unlock_operations (); + return -EFAULT; + } + ibmphp_unlock_operations (); + return -1; + } + + if (SLOT_POWER (slot_cur->status) && !(SLOT_PWRGD (slot_cur->status))) { + faulted = 1; + printk ("power fault occured trying to power up...\n"); + } else if (SLOT_POWER (slot_cur->status) && (SLOT_BUS_SPEED (slot_cur->status))) { + faulted = 1; + printk ("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))) { + printk ("power on failed... \n"); + faulted = 1; + } + if (faulted) { + attn_off (slot_cur); /* need to turn off b4 on */ + attn_on (slot_cur); + if (power_off (slot_cur) == -1) { + ibmphp_unlock_operations (); + return -EFAULT; + } + + if (slot_update (&slot_cur)) { + ibmphp_unlock_operations (); + return -EFAULT; + } + 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 */ + printk ("out of system memory \n"); + attn_off (slot_cur); + attn_on (slot_cur); + if (power_off (slot_cur) == -1) { + ibmphp_unlock_operations (); + return -EFAULT; + } + 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)) { + printk ("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); + if (power_off (slot_cur) == -1) { + ibmphp_unlock_operations (); + return -EFAULT; + } + if (slot_update (&slot_cur)) { + ibmphp_unlock_operations(); + return -EFAULT; + } + 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; + printk ("DISABLING SLOT... \n"); + + ibmphp_lock_operations (0); + 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) { + printk ("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))) { + printk ("removing from kernel failed... \nPlease 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) { + printk ("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_hpcwriteslot (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, +*/ +}; + +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, rc = 0; + init_flag = 1; +#ifndef CONFIG_X86_IO_APIC + printk ("Please include APIC support. \n"); + return -EFAULT; +#endif + ibmphp_pci_root_ops = get_root_pci_ops (); + if (ibmphp_pci_root_ops == NULL) { + printk ("cannot read bus operations... will not be able to read the cards. Please check your system \n"); + return -EFAULT; + } + + ibmphp_debug = debug; + ibmphp_hpc_initvars (); + + for (i = 0; i < 16; i++) + irqs[i] = 0; + if (ibmphp_access_ebda ()) + return -ENODEV; + debug ("after acess_ebda() \n"); + if ((rc = ibmphp_ebda_rsrc_rsrc ())) { + ibmphp_unload (); + return rc; + } + debug ("after ibmphp_ebda_rsrc_rsrc \n"); + if ((rc = ibmphp_ebda_rsrc_controller ())) { + ibmphp_unload (); + return -EFAULT; + } + debug ("after ibmphp_ebda_rsrc_controller \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 -EFAULT; + } + ibmphp_print_test (); + if ((rc = ibmphp_hpc_start_poll_thread ())) { + ibmphp_unload (); + return -EFAULT; + } + return 0; +} + +static void __exit ibmphp_exit (void) +{ + printk ("WARNING!!!!! DO NOT REMOVE THIS MODULE IF YOU PLAN TO INSMOD IT" + " IN THE FUTURE WITHOUT REBOOT....NEXT LOAD WILL BE CORRUPT\n"); + printk ("cleaning up... "); + ibmphp_hpc_stop_poll_thread (); + debug ("after polling \n"); + ibmphp_unload (); + printk ("done \n"); +} + +module_init (ibmphp_init); +module_exit (ibmphp_exit); diff -Nru a/drivers/hotplug/ibmphp_ebda.c b/drivers/hotplug/ibmphp_ebda.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/hotplug/ibmphp_ebda.c Thu Jan 17 15:25:50 2002 @@ -0,0 +1,1034 @@ +/* + * IBM Hot Plug Controller Driver + * + * 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 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#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 VARIABLES * +**********************************************************/ + +static struct ebda_hpc_list *hpc_list_ptr; +static struct ebda_rsrc_list *rsrc_list_ptr; +static LIST_HEAD (ebda_hpc_head); +static LIST_HEAD (bus_info_head); +static u16 ebda_seg; + +LIST_HEAD (ibmphp_ebda_pci_rsrc_head); + +LIST_HEAD (ibmphp_slot_head); + +/********************************************************* +* STATIC FUNCTION PROTOTYPES * +*********************************************************/ + +static struct ebda_hpc_list *alloc_ebda_hpc_list (void); +static struct controller *alloc_ebda_hpc (u32, u32); +static void free_ebda_hpc (struct controller *); +static struct ebda_rsrc_list *alloc_ebda_rsrc_list (void); +static struct ebda_pci_rsrc *alloc_ebda_pci_rsrc (void); +static struct slot *alloc_ibm_slot (void); +static void print_bus_info (void); +static void print_ebda_pci_rsrc(void); +static void print_ebda_hpc (void); + +int ibmphp_access_ebda (void) +{ + u8 format, num_ctlrs; + u16 num_entries, next_offset, offset, blk_id, sub_addr, rc, re, rc_id, re_id, base; + void *io_mem; + + io_mem = ioremap ((0x40 << 4) + 0x0e, 2); + if (io_mem == NULL) + return -ENOMEM; + ebda_seg = readw (io_mem); + iounmap (io_mem); + debug ("RETURNED EBDA SEGMENT: %x\n", ebda_seg); + next_offset = 0x180; + + for (;;) { /* hot swap block */ + offset = next_offset; + io_mem = ioremap ((ebda_seg << 4) + offset, 2); + if (io_mem == NULL) + return -ENOMEM; + next_offset = readw (io_mem); /* offset of next blk */ + iounmap (io_mem); + + offset += 2; + if (next_offset == 0) /* 0 indicate it's last blk */ + break; + io_mem = ioremap ((ebda_seg << 4) + offset, 2); + if (io_mem == NULL) + return -ENOMEM; + blk_id = readw (io_mem); /* this blk id */ + iounmap (io_mem); + + offset += 2; + /* check if it is hot swap block */ + if (blk_id != 0x4853) + continue; + debug ("NOW ENTER HOT SWAP BLOCK---\n"); + debug ("HOT BLK ID: %x\n", blk_id); + io_mem = ioremap ((ebda_seg << 4) + offset, 1); /* found HS blk */ + if (io_mem == NULL) + return -ENOMEM; + format = readb (io_mem); + iounmap (io_mem); + + offset += 1; + if (format != 4) + return -ENODEV; + debug ("HOT BLK FORMAT: %x\n", format); + /* hot swap sub blk */ + base = offset; + + sub_addr = base; + io_mem = ioremap ((ebda_seg << 4) + sub_addr, 2); + if (io_mem == NULL) + return -ENOMEM; + re = readw (io_mem); /* next sub blk */ + iounmap (io_mem); + + sub_addr += 2; + io_mem = ioremap ((ebda_seg << 4) + sub_addr, 2); + if (io_mem == NULL) + return -ENOMEM; + rc_id = readw (io_mem); /* sub blk id */ + iounmap (io_mem); + + sub_addr += 2; + if (rc_id != 0x5243) + return -ENODEV; + + /* rc sub blk signature */ + io_mem = ioremap ((ebda_seg << 4) + sub_addr, 1); + if (io_mem == NULL) + return -ENOMEM; + num_ctlrs = readb (io_mem); + iounmap (io_mem); + + sub_addr += 1; + hpc_list_ptr = alloc_ebda_hpc_list (); + if (hpc_list_ptr == NULL) + 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\nNUM OF CONTROLLER: %x\nOFFSET OF HPC DATA STRUCTURE ENTERIES: %x\n ", format, num_ctlrs, sub_addr); + + sub_addr = base + re; /* re sub blk */ + io_mem = ioremap ((ebda_seg << 4) + sub_addr, 2); + if (io_mem == NULL) + return -ENOMEM; + rc = readw (io_mem); /* next sub blk */ + iounmap (io_mem); + + sub_addr += 2; + io_mem = ioremap ((ebda_seg << 4) + sub_addr, 2); + if (io_mem == NULL) + return -ENOMEM; + re_id = readw (io_mem); /* sub blk id */ + iounmap (io_mem); + + sub_addr += 2; + if (re_id != 0x5245) + return -ENODEV; + + /* signature of re */ + io_mem = ioremap ((ebda_seg << 4) + sub_addr, 2); + if (io_mem == NULL) + return -ENOMEM; + num_entries = readw (io_mem); + iounmap (io_mem); + + sub_addr += 2; /* offset of RSRC_ENTRIES blk */ + rsrc_list_ptr = alloc_ebda_rsrc_list (); + if (rsrc_list_ptr == NULL) + 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\nNUM OF RSRC: %x\nOFFSET OF RSRC DATA STRUCTURE ENTERIES: %x\n ", format, num_entries, sub_addr); + + return 0; + } + 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 * +* INPUT: VOID * +* OUTPUT: 0--SUCCESS, OTHER--- FAILURE * +**********************************************************/ + +int ibmphp_ebda_rsrc_controller (void) +{ + u16 addr, addr_slot, addr_bus; + u8 ctlr_id, temp; + u16 ctlr, slot, bus; + u16 slot_num, bus_num, index; + void *io_mem, *io_mem1; + 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++) { + io_mem = ioremap ((ebda_seg << 4) + addr, 2); /* map 2 bytes for 2 field */ + if (io_mem == NULL) + return -ENOMEM; + ctlr_id = readb (io_mem); + slot_num = readb (io_mem + 1); + iounmap (io_mem); + + addr += 2; + addr_slot = addr; /* offset of slot structure */ + addr += (slot_num * 4); + + io_mem = ioremap ((ebda_seg << 4) + addr, 1); + if (io_mem == NULL) + return -ENOMEM; + bus_num = readb (io_mem); + iounmap (io_mem); + + addr += 1; + addr_bus = addr; + addr += (bus_num * 9); /* offset of ctlr_type */ + io_mem = ioremap ((ebda_seg << 4) + addr, 1); + if (io_mem == NULL) + return -ENOMEM; + temp = readb (io_mem); + iounmap (io_mem); + + addr += 1; + /* init hpc structure */ + hpc_ptr = alloc_ebda_hpc (slot_num, bus_num); + if (hpc_ptr == NULL) + 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\nCTLR_RELATIVE_ID: %x\nCOUNT OF SLOTS CONTROLLED BY THIS CTLR: %x\nCOUNT OF BUSES CONTROLLED BY THIS CTLR: %x\n", ctlr_id, hpc_ptr->ctlr_relative_id, slot_num, bus_num); + + /* init slot structure */ + io_mem1 = ioremap ((ebda_seg << 4) + addr_slot, (slot_num * 4)); + if (io_mem1 == NULL) { + free_ebda_hpc (hpc_ptr); + return -ENOMEM; + } + io_mem = io_mem1; + /* fetch slot, bus, cap... */ + slot_ptr = hpc_ptr->slots; + for (slot = 0; slot < slot_num; slot++) { + slot_ptr->slot_num = readb (io_mem); + slot_ptr->slot_bus_num = readb (io_mem + slot_num); + slot_ptr->ctl_index = readb (io_mem + 2*slot_num); + slot_ptr->slot_cap = readb (io_mem + 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) + 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->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++; + io_mem++; + } + iounmap (io_mem1); + io_mem = NULL; + + /* init bus structure */ + io_mem1 = ioremap ((ebda_seg << 4) + addr_bus, (bus_num * 9)); + if (io_mem1 == NULL) { + free_ebda_hpc (hpc_ptr); + return -ENOMEM; + } + io_mem = io_mem1; + + bus_ptr = hpc_ptr->buses; + for (bus = 0; bus < bus_num; bus++) { + bus_ptr->bus_num = readb (io_mem); + bus_ptr++; + io_mem++; + } + iounmap (io_mem1); + io_mem = NULL; + + hpc_ptr->ctlr_type = temp; + switch (hpc_ptr->ctlr_type) { + case 1: + io_mem = ioremap ((ebda_seg << 4) + addr, 1); + if (io_mem == NULL) + return -ENOMEM; + hpc_ptr->u.pci_ctlr.bus = readb (io_mem); + iounmap (io_mem); + + addr += 1; + io_mem = ioremap ((ebda_seg << 4) + addr, 1); + if (io_mem == NULL) + return -ENOMEM; + hpc_ptr->u.pci_ctlr.dev_fun = readb (io_mem); + iounmap (io_mem); + + addr += 1; + io_mem = ioremap ((ebda_seg << 4) + addr, 1); + if (io_mem == NULL) + return -ENOMEM; + hpc_ptr->irq = readb (io_mem); + iounmap (io_mem); + + addr += 1; + break; + + case 0: + io_mem = ioremap ((ebda_seg << 4) + addr, 2); + if (io_mem == NULL) + return -ENOMEM; + hpc_ptr->u.isa_ctlr.io_start = readw (io_mem); + iounmap (io_mem); + addr += 2; + + io_mem = ioremap ((ebda_seg << 4) + addr, 2); + if (io_mem == NULL) + return -ENOMEM; + hpc_ptr->u.isa_ctlr.io_end = readw (io_mem); + iounmap (io_mem); + + addr += 2; + io_mem = ioremap ((ebda_seg << 4) + addr, 1); + if (io_mem == NULL) + return -ENOMEM; + hpc_ptr->irq = readb (io_mem); + iounmap (io_mem); + + addr += 1; + break; + + case 2: + io_mem = ioremap ((ebda_seg << 4) + addr, 4); + if (io_mem == NULL) + return -ENOMEM; + hpc_ptr->u.wpeg_ctlr.wpegbbar = readl (io_mem); + iounmap (io_mem); + + addr += 4; + io_mem = ioremap ((ebda_seg << 4) + addr, 1); + if (io_mem == NULL) + return -ENOMEM; + hpc_ptr->u.wpeg_ctlr.i2c_addr = readb (io_mem); + iounmap (io_mem); + + addr += 1; + io_mem = ioremap ((ebda_seg << 4) + addr, 1); + if (io_mem == NULL) + return -ENOMEM; + hpc_ptr->irq = readb (io_mem); + iounmap (io_mem); + + addr += 1; + break; + + default: + 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) + 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) { + 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) { + kfree (hp_slot_ptr->info); + kfree (hp_slot_ptr); + return -ENOMEM; + } + + hp_slot_ptr->private = alloc_ibm_slot (); + if (!hp_slot_ptr->private) { + 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) + 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_hpcfillhpslotinfo (hp_slot_ptr); + if (rc) + return rc; + + rc = ibmphp_init_devno ((struct slot **) &hp_slot_ptr->private); + if (rc) + 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 * +*********************************************************/ + +int ibmphp_ebda_rsrc_rsrc (void) +{ + + u16 addr; + short rsrc; + void *io_mem; + 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++) { + io_mem = ioremap ((ebda_seg << 4) + addr, 1); + if (io_mem == NULL) + return -ENOMEM; + type = readb (io_mem); + iounmap (io_mem); + + 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 == NULL) + return -ENOMEM; + rsrc_ptr->rsrc_type = type; + + io_mem = ioremap ((ebda_seg << 4) + addr, 1); + if (io_mem == NULL) + return -ENOMEM; + rsrc_ptr->bus_num = readb (io_mem); + iounmap (io_mem); + + addr += 1; + io_mem = ioremap ((ebda_seg << 4) + addr, 1); + if (io_mem == NULL) + return -ENOMEM; + rsrc_ptr->dev_fun = readb (io_mem); + iounmap (io_mem); + + addr += 1; + io_mem = ioremap ((ebda_seg << 4) + addr, 2); + rsrc_ptr->start_addr = readw (io_mem); + iounmap (io_mem); + + addr += 2; + io_mem = ioremap ((ebda_seg << 4) + addr, 2); + if (io_mem == NULL) + return -ENOMEM; + rsrc_ptr->end_addr = readw (io_mem); + iounmap (io_mem); + + addr += 2; + // 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 == NULL) + return -ENOMEM; + rsrc_ptr->rsrc_type = type; + + io_mem = ioremap ((ebda_seg << 4) + addr, 1); + if (io_mem == NULL) + return -ENOMEM; + rsrc_ptr->bus_num = readb (io_mem); + iounmap (io_mem); + + addr += 1; + io_mem = ioremap ((ebda_seg << 4) + addr, 1); + if (io_mem == NULL) + return -ENOMEM; + rsrc_ptr->dev_fun = readb (io_mem); + iounmap (io_mem); + + addr += 1; + io_mem = ioremap ((ebda_seg << 4) + addr, 4); + if (io_mem == NULL) + return -ENOMEM; + rsrc_ptr->start_addr = readl (io_mem); + iounmap (io_mem); + + addr += 4; + io_mem = ioremap ((ebda_seg << 4) + addr, 4); + if (io_mem == NULL) + return -ENOMEM; + rsrc_ptr->end_addr = readl (io_mem); + iounmap (io_mem); + + addr += 4; + // 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; +} + + +/*********************************************************** +* FIND THE TOTAL NUMBER OF CONTROLLERS * +* INPUT: VOID * +* OUTPUT: TOTAL NUMBERS OF CONTROLLERS * +***********************************************************/ + +u8 ibmphp_get_total_controllers (void) +{ + u8 num; + num = hpc_list_ptr->num_ctlrs; + return num; +} + + +/*********************************************************** +* FIND THE SLOT NODE GIVEN PHYSICAL SLOT NUMBER * +* INPUT: PHYSICAL SLOT NUMBER * +* OUTPUT: POINTER TO THE SLOT STRUCTURE CORESPONDING TO * +* THAT PHYSICAL SLOT NUMBER OR NULL * +***********************************************************/ + +struct slot *ibmphp_get_slot_from_physical_num (u8 physical_num) +{ + struct slot *ptr; + struct list_head *ptr1; + + list_for_each (ptr1, &ibmphp_slot_head) { + ptr = list_entry (ptr1, struct slot, ibm_slot_list); + if (ptr->number == physical_num) + return ptr; + } + return NULL; +} + +/********************************************************** +* THE FOLLOWING FUNCTIONS IS TO GET (1)THE SMALLEST SLOT * +* NUMBER (2) THE LARGEST SLOT NUMBER (3) 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; +} + +int ibmphp_get_bus_index (u8 num) +{ + struct bus_info *ptr; + struct list_head *ptr1; + int count = 1; + + list_for_each (ptr1, &bus_info_head) { + ptr = list_entry (ptr1, struct bus_info, bus_info_list); + if (ptr->busno == num) + return count; + count += 1; + } + return -ENODEV; + +} + + +/*************************************************************** +* DEALLOCATED THE MEMO OF BUS_INFO LIST * +* INPUT: NONE * +* OUTPUT: NONE * +***************************************************************/ + +void ibmphp_free_bus_info_queue (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); + kfree (ptr); + } +} + + +/*********************************************************** +* CALCULATE THE TOTAL HOT PLUGGABLE SLOTS CONTROLLED * +* BY TOTAL HPCs * +* INPUT: NONE * +* OUTPUT: TOTAL HP SLOT NUMBERS * +***********************************************************/ +/* +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; +} +*/ + +//----------------------------------------------------------------------- +// OPERATION ON HPC DESCRIPTOR +//----------------------------------------------------------------------- + +/******************************************************** +* ALLOCATE MEMO FOR ONE HPC DESCRIPTOR NODE * +* INPUT: NONE * +* RETURN: POINTER TO THE ALLOCATED MEMO OF HPC * +* DESCRIPTOR NODE * +********************************************************/ + +static struct ebda_hpc_list *alloc_ebda_hpc_list (void) +{ + struct ebda_hpc_list *ptr; + ptr = (struct ebda_hpc_list *) kmalloc (sizeof (struct ebda_hpc_list), GFP_KERNEL); + return (ptr); +} + + +//---------------------------------------------------------------------- +// OPERATION ON HPC +//---------------------------------------------------------------------- + +/*********************************************************** +* ALLOCATE MEMO FOR HPC DATA STRUCTURE AND ITS * +* ASSOCIATED SLOT AND BUS STRUCTURE * +* INPUT: SLOT_COUNT(COUNT OF SLOTS CONTROLLED BY THIS * +* CONTROLLER; BUS_COUNT(COUNT OF BUSSES CONTROLLED BY THIS * +* CONTROLLER * +* RETURN: POINTER TO THE ALLOCATED MEMO OF HPC NODE * +***********************************************************/ + +static struct controller *alloc_ebda_hpc (u32 slot_count, u32 bus_count) +{ + struct controller *hpc_ptr; + struct ebda_hpc_slot *slot_ptr; + struct ebda_hpc_bus *bus_ptr; + + slot_ptr = NULL; + bus_ptr = NULL; + + hpc_ptr = (struct controller *) kmalloc (sizeof (struct controller), GFP_KERNEL); + if (!hpc_ptr) { + return NULL; + } + memset (hpc_ptr, 0, sizeof (struct controller)); + + slot_ptr = kmalloc (sizeof (struct ebda_hpc_slot) * slot_count, GFP_KERNEL); + if (!slot_ptr) { + kfree (hpc_ptr); + return NULL; + } + memset (slot_ptr, 0, sizeof (struct ebda_hpc_slot) * slot_count); + hpc_ptr->slots = slot_ptr; + + bus_ptr = kmalloc (sizeof (struct ebda_hpc_bus) * bus_count, GFP_KERNEL); + if (!bus_ptr) { + kfree (hpc_ptr->slots); + kfree (hpc_ptr); + return NULL; + } + memset (bus_ptr, 0, sizeof (struct ebda_hpc_bus) * bus_count); + hpc_ptr->buses = bus_ptr; + + return (hpc_ptr); +} + +/************************************************************* +* FREE MEMO FOR HPC DATA STRUCTURE AND ITS ASSOCIATED * +* SLOT AND BUS STRUCTURES * +* INPUT: POINTER TO THE MEMO OF HPC NODE * +* RETURN: NONE * +*************************************************************/ + +static void free_ebda_hpc (struct controller *ptr) +{ + kfree (ptr->slots); + ptr->slots = NULL; + kfree (ptr->buses); + ptr->buses = NULL; + kfree (ptr); + ptr = NULL; +} + +/************************************************************* +* DEALLOCATED MEMO FOR THE LINKED LIST OF HPC * +* INPUT: NONE * +* OUTPUT: NONE + * +*************************************************************/ + +void ibmphp_free_ebda_hpc_queue (void) +{ + struct controller *hpc_ptr; + struct list_head *ptr1; + + list_for_each(ptr1, &ebda_hpc_head) { + hpc_ptr = list_entry (ptr1, struct controller, ebda_hpc_list); + free_ebda_hpc (hpc_ptr); + } +} + + +//-------------------------------------------------------------------- +// OPERATION ON PCI RSRC DESCRIPTOR +//-------------------------------------------------------------------- + +/************************************************************* +* ALLOCATE MEMO FOR ONE PCI RSRC DESCRIPTOR NODE * +* INPUT: NONE * +* OUTPUT: POINTER TO THE ALLOCATED MEMO OF THE PCI RSRC * +* DESCRI. NODE * +*************************************************************/ + +static struct ebda_rsrc_list *alloc_ebda_rsrc_list (void) +{ + struct ebda_rsrc_list *ptr; + ptr = (struct ebda_rsrc_list *) kmalloc (sizeof (struct ebda_rsrc_list), GFP_KERNEL); + return (ptr); +} + + +//-------------------------------------------------------------------- +// OPERATION ON PCI RSRC +//-------------------------------------------------------------------- + +/************************************************************** +* ALLOCATE MEMO FOR ONE PCI RSRC NODE * +* INPUT: NONE * +* OUTPUT: POINTER TO THE ALLOCATED MEMO OF PCI RSRC NODE * +**************************************************************/ + +static struct ebda_pci_rsrc *alloc_ebda_pci_rsrc (void) +{ + struct ebda_pci_rsrc *ptr; + ptr = (struct ebda_pci_rsrc *) kmalloc (sizeof (struct ebda_pci_rsrc), GFP_KERNEL); + return (ptr); +} + + +/************************************************************* +* DEALLOCATED MEMO FOR THE LINKED LIST OF PCI RSRC NODE * +* INPUT: NONE * +* OUTPUT: NONE * +*************************************************************/ + +void ibmphp_free_ebda_pci_rsrc_queue (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); + + kfree(ptr); + ptr = NULL; + } +} + +//----------------------------------------------------------------------- +// OPERATION ON IBM SLOT STRUCTURE +//----------------------------------------------------------------------- + +/************************************************************** +* ALLOCATION MEMO FOR THE IBM SLOT STRUCTURE * +* INPUT: NONE * +* OUTPUT: POINTER TO THE ALLOCATED MEMO OF IBM SLOT STRUCT * +**************************************************************/ + +static struct slot +*alloc_ibm_slot (void) +{ + struct slot *ptr_slot; + + ptr_slot = (struct slot *) kmalloc (sizeof (struct slot), GFP_KERNEL); + if (ptr_slot == NULL) + return (ptr_slot); + memset (ptr_slot, 0, sizeof (struct slot)); + +// ptr_ctlr = (struct controller *) kmalloc (sizeof (struct controller), GFP_KERNEL); +// if (ptr_ctlr == NULL) { +// kfree (ptr_slot); +// return NULL; +// } +// memset (ptr_ctlr, 0, sizeof (struct controller)); +// ptr_slot->ctrl = ptr_ctlr; + +// ptr_func = (struct pci_func *)kmalloc( sizeof(struct pci_func), GFP_KERNEL ); +// if(ptr_func == NULL) { +// kfree(ptr_slot); +// return NULL; +// } +// memset(ptr_func, 0, sizeof(struct pci_func) ); +// ptr_slot->func = ptr_func; +// Irene allocats memo for pci_func + return ptr_slot; + +} + +/************************************************************** +* DEALLOCATED THE MEMO OF ONE IBM SLOT DATA STRUCTURE * +* INPUT: POINTER TO THE IBM SLOT DATA STRUCTURE * +* OUTPUT: NONE * +**************************************************************/ +/* +void free_ibm_slot (struct slot *ptr) +{ + if (ptr != NULL) { + kfree (ptr->ctrl); + //kfree(ptr->func); + kfree (ptr); + } + ptr = NULL; +} +*/ + + +/**************************************************************** +* print bus_info list +****************************************************************/ + +static void print_bus_info (void) +{ + struct bus_info *ptr; + struct list_head *ptr1; + + printk("\n THIS IS FROM PRINT_BUS_INFO \n"); + list_for_each (ptr1, &bus_info_head) { + ptr = list_entry (ptr1, struct bus_info, bus_info_list); + debug ("slot_min = %x\n", ptr->slot_min); + debug ("slot_max = %x\n", ptr->slot_max); + debug ("slot_count = %x\n", ptr->slot_count); + debug ("bus# = %x\n", ptr->busno); + + debug ("current_speed = %x\n", ptr->current_speed); + debug ("supported_speed = %x\n", ptr->supported_speed); + debug ("controller_id = %x\n", ptr->controller_id); + debug ("bus_mode = %x\n", ptr->supported_bus_mode); + } + printk(" THIS IS END OF PRINT_BUS_INFO "); +} + + + +/**************************************************************** +* print ebda_pci_rsrc * +****************************************************************/ + +static void print_ebda_pci_rsrc (void) +{ + struct ebda_pci_rsrc *ptr; + struct list_head *ptr1; + + printk("\n THIS MESSAGE FROM PRINT_EBDA_PCI_RSRC \n"); + list_for_each(ptr1, &ibmphp_ebda_pci_rsrc_head) { + ptr = list_entry (ptr1, struct ebda_pci_rsrc, ebda_pci_rsrc_list); + debug("RSRC TYPE: %x BUS#: %x DEV_FUNC: %x START ADDR: %lx END ADDR: %lx\n", ptr->rsrc_type ,ptr->bus_num, ptr->dev_fun,ptr->start_addr, ptr->end_addr); + } + printk(" THIS IS END OF PRINT_EBDA_PCI_RSRC "); +} + + +/**************************************************************** +* print ebda_hpc * +****************************************************************/ + +static void print_ebda_hpc (void) +{ + struct controller *hpc_ptr; + struct list_head *ptr1; + u16 index; + + printk("\n THIS MESSAGE FROM PRINT_EBDA_HPC \n"); + 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 ("PHYSICAL SLOT#: %x\nPCI BUS# OF THE SLOT: %x\nINDEX INTO CTLR ADDR: %x\nCAP OF THE SLOT: %x\n", hpc_ptr->slots[index].slot_num, hpc_ptr->slots[index].slot_bus_num, hpc_ptr->slots[index].ctl_index, hpc_ptr->slots[index].slot_cap); + } + + for (index = 0; index < hpc_ptr->bus_count; index++) { debug ("BUS# OF EACH BUS CONTROLLED BY THIS CTLR: %x\n", hpc_ptr->buses[index].bus_num); + } + + switch (hpc_ptr->ctlr_type) { + case 1: + debug ("TYPE OF HPC: %x\nBUS: %x\nDEV_FUN: %x\nIRQ: %x\n", hpc_ptr->ctlr_type, hpc_ptr->u.pci_ctlr.bus, hpc_ptr->u.pci_ctlr.dev_fun, hpc_ptr->irq); + break; + + case 0: + debug ("TYPE OF HPC: %x\nIO_START: %x\nIO_END: %x\nIRQ: %x\n", hpc_ptr->ctlr_type, hpc_ptr->u.isa_ctlr.io_start, hpc_ptr->u.isa_ctlr.io_end, hpc_ptr->irq); + break; + + case 2: + debug ("TYPE OF HPC: %x\nWPEGBBAR: %lx\nI2C_ADDR: %x\nIRQ: %x\n", hpc_ptr->ctlr_type, hpc_ptr->u.wpeg_ctlr.wpegbbar, hpc_ptr->u.wpeg_ctlr.i2c_addr, hpc_ptr->irq); + break; + } + } + printk(" THIS IS END OF PRINT_EBDA_HPC "); +} + + + + + + + + diff -Nru a/drivers/hotplug/ibmphp_hpc.c b/drivers/hotplug/ibmphp_hpc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/hotplug/ibmphp_hpc.c Thu Jan 17 15:25:50 2002 @@ -0,0 +1,1418 @@ +/* + * IBM Hot Plug Controller Driver + * + * 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 + * + * + */ + +#include +#include +#include +#include +#include +#include +#include "ibmphp.h" + +//---------------------------------------------------------------------------- +// 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 + +//---------------------------------------------------------------------------- +// 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 +//---------------------------------------------------------------------------- +#define SWAPLONG(l) (ulong) ( ((l)<<24) | (((l)&0xFF000000)>>24)\ + | (((l)&0xFF00)<<8) | (((l)&0xFF0000)>>8) ) + +// 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; +static struct semaphore sem_hpcaccess; // lock access to HPC +static struct semaphore semOperations; // lock all operations and + // access to data structures + +//---------------------------------------------------------------------------- +// things needed for the long_delay function +//---------------------------------------------------------------------------- +static struct semaphore delay_sem; +static wait_queue_head_t delay_wait; + +//---------------------------------------------------------------------------- +// global function prototypes +//---------------------------------------------------------------------------- +extern void ibmphp_hpc_initvars (void); + +extern u8 ibmphp_hpcreadslot (struct slot *, u8, u8 *); +extern u8 ibmphp_hpcwriteslot (struct slot *, u8); + +extern u8 ibmphp_lock_operations (int); +extern void ibmphp_unlock_operations (void); + +extern u8 ibmphp_hpcfillhpslotinfo (struct hotplug_slot *); +extern int ibmphp_hpc_start_poll_thread (void); +extern void ibmphp_hpc_stop_poll_thread (void); + +//---------------------------------------------------------------------------- +// local function prototypes +//---------------------------------------------------------------------------- +static u8 i2c_read (struct controller *, void *, u8); +static u8 i2c_write (struct controller *, void *, u8, u8); +static u8 hpc_writecmdtoindex (u8, u8); +static u8 hpc_readcmdtoindex (u8, u8); +static u8 get_hpc_access (int); +static void free_hpc_access (void); +static void poll_hpc (void); +static u8 update_slot (struct slot *, u8); +static u8 process_changeinstatus (struct slot *, struct slot *); +static u8 process_changeinlatch (u8, u8); +static int hpc_poll_thread (void *); +static u8 hpc_wait_ctlr_notworking (int, struct controller *, void *, u8 *); +//---------------------------------------------------------------------------- + + +/*---------------------------------------------------------------------- +* Name: ibmphp_hpc_initvars +* +* Action: initialize semaphores and variables +* +* Input: +* +* Return None. +* Value: +* +* Side +* Effects: None. +* +* Notes: +*---------------------------------------------------------------------*/ +void ibmphp_hpc_initvars (void) +{ + debug ("ibmphp_hpc_initvars - Entry\n"); + + init_MUTEX (&delay_sem); + init_MUTEX (&sem_hpcaccess); + init_MUTEX (&semOperations); + stop_polling = FALSE; + ibmphp_shutdown = FALSE; + tid_poll = 0; + + debug ("ibmphp_hpc_initvars - Exit\n"); + return; +} + +/*---------------------------------------------------------------------- +* Name: i2c_read +* +* Action: read from HPC over I2C +* +* Input: +* +* Return status +* Value: +* +* Side +* Effects: None. +* +* Notes: +*---------------------------------------------------------------------*/ +static u8 i2c_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 + + if (stop_polling) + debug ("i2c_read - Entry ptr_ctlr[%lx] WPGBbar[%lx] index[%x]\n", (ulong) ctlr_ptr, + (ulong) WPGBbar, index); + + //-------------------------------------------------------------------- + // READ - step 1 + + //read at address, byte length, I2C address (shifted), index + 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; + + wpg_data = SWAPLONG (data); // swap data before writing + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMOSUP_OFFSET; + //debug("i2c_read - write at addr[%lx] wpg_data[%lx]\n", + // (ulong)wpg_addr, wpg_data); + writel (wpg_data, wpg_addr); + // junk mdelay (100); + + //-------------------------------------------------------------------- + // READ - step 2 : clear the message buffer + data = 0x00000000; + wpg_data = SWAPLONG (data); + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMBUFL_OFFSET; + //debug("i2c_read - write at addr[%lx] wpg_data[%lx]\n", + // (ulong)wpg_addr, wpg_data); + writel (wpg_data, wpg_addr); + // junk mdelay (100); + + //-------------------------------------------------------------------- + // READ - step 3 : OR I2C parm register (may need later for timeout) + // wpg_data = 0x00000000; + //(ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CPARM_OFFSET + // + (ulong) WPG_I2C_OR; + //debug("i2c_read - write at addr[%lx] wpg_data[%lx]\n", + // (ulong)wpg_addr, wpg_data); + //writel(wpg_data, wpg_addr); + + //-------------------------------------------------------------------- + // READ - step 4 : issue start operation, I2C master control bit 30:ON + // 2020 : [20] OR operation at [20] offset 0x20 + data = WPG_I2CMCNTL_STARTOP_MASK; + wpg_data = SWAPLONG (data); + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMCNTL_OFFSET + (ulong) WPG_I2C_OR; + //debug("i2c_read - write at addr[%lx] wpg_data[%lx]\n", + // (ulong)wpg_addr, wpg_data); + writel (wpg_data, wpg_addr); + // junk mdelay (100); + + //debug("i2c_read debug - entering while loop\n"); + + //-------------------------------------------------------------------- + // READ - step 5 : wait until start operation bit clears + i = (CMD_COMPLETE_TOUT_SEC); + while (i) { + ibmphp_long_delay (1 * HZ / 100); + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMCNTL_OFFSET; + wpg_data = readl (wpg_addr); + data = SWAPLONG (wpg_data); + if (!(data & WPG_I2CMCNTL_STARTOP_MASK)) + break; + i--; + } + if (i == 0) { + debug ("i2c_read - Error : WPG timeout\n"); + return HPC_ERROR; + } + //-------------------------------------------------------------------- + // READ - step 6 : read I2C status register + i = CMD_COMPLETE_TOUT_SEC; + while (i) { + ibmphp_long_delay (1 * HZ / 100); + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CSTAT_OFFSET; + wpg_data = readl (wpg_addr); + data = SWAPLONG (wpg_data); + if (HPC_I2CSTATUS_CHECK (data)) + break; + i--; + } + if (i == 0) { + debug ("i2c_read - Exit Error:I2C timeout\n"); + return HPC_FAILURE; + } + //debug("i2c_read - read at addr[%lx] wpg_data[%lx]\n", + // (ulong)wpg_addr, wpg_data); + + //-------------------------------------------------------------------- + // READ - step 7 : get DATA + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMBUFL_OFFSET; + wpg_data = readl (wpg_addr); + data = SWAPLONG (wpg_data); + //debug("i2c_read - read at addr[%lx] wpg_data[%lx]\n", + // (ulong)wpg_addr, wpg_data); + + //-------------------------------------------------------------------- + status = (u8) data; + + if (stop_polling) + debug ("i2c_read - Exit index[%x] status[%x]\n", index, status); + return (status); +} + +/*---------------------------------------------------------------------- +* Name: i2c_write +* +* Action: write to HPC over I2C +* +* Input: +* +* Return HPC_SUCCESS, HPC_FAILURE +* Value: +* +* Side +* Effects: None. +* +* Notes: +*---------------------------------------------------------------------*/ +static u8 i2c_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 ("i2c_write - Entry ctlr_ptr[%lx] WPGBbar[%lx] index[%x] cmd[%x]\n", (ulong) ctlr_ptr, + (ulong) WPGBbar, index, cmd); + + rc = HPC_SUCCESS; + //-------------------------------------------------------------------- + // WRITE - step 1 + + //write at address, byte length, I2C address (shifted), index + 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; + + wpg_data = SWAPLONG (data); // swap data before writing + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMOSUP_OFFSET; + //debug("i2c_write - write at addr[%lx] wpg_data[%lx]\n", + // (ulong)wpg_addr, wpg_data); + writel (wpg_data, wpg_addr); + // junk mdelay (100); + + //-------------------------------------------------------------------- + // WRITE - step 2 : clear the message buffer + data = 0x00000000 | (ulong) cmd; + wpg_data = SWAPLONG (data); + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMBUFL_OFFSET; + //debug("i2c_write - write at addr[%lx] wpg_data[%lx]\n", + // (ulong)wpg_addr, wpg_data); + writel (wpg_data, wpg_addr); + // junk mdelay (100); + + //-------------------------------------------------------------------- + // WRITE - step 3 : OR I2C parm register (may need later for timeout) + // wpg_data = 0x00000000; + //(ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CPARM_OFFSET + // + (ulong) WPG_I2C_OR; + //debug("i2c_write - write at addr[%lx] wpg_data[%lx]\n", + // (ulong)wpg_addr, wpg_data); + //writel(wpg_data, wpg_addr); + + //-------------------------------------------------------------------- + // WRITE - step 4 : issue start operation,I2C master control bit 30:ON + // 2020 : [20] OR operation at [20] offset 0x20 + data = WPG_I2CMCNTL_STARTOP_MASK; + wpg_data = SWAPLONG (data); + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMCNTL_OFFSET + (ulong) WPG_I2C_OR; + //debug("i2c_write - write at addr[%lx] wpg_data[%lx]\n", + // (ulong)wpg_addr, wpg_data); + writel (wpg_data, wpg_addr); + // junk mdelay (100); + + //debug("i2c_write debug - entering while loop\n"); + + //-------------------------------------------------------------------- + // WRITE - step 5 : wait until start operation bit clears + i = CMD_COMPLETE_TOUT_SEC; + while (i) { + ibmphp_long_delay (1 * HZ / 100); + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CMCNTL_OFFSET; + wpg_data = readl (wpg_addr); + data = SWAPLONG (wpg_data); + if (!(data & WPG_I2CMCNTL_STARTOP_MASK)) + break; + i--; + } + if (i == 0) { + debug ("i2c_read - Exit Error:WPG timeout\n"); + rc = HPC_FAILURE; + } + //debug("i2c_write - read at addr[%lx] wpg_data[%lx]\n", + // (ulong)wpg_addr, wpg_data); + + //-------------------------------------------------------------------- + // WRITE - step 6 : read I2C status register + i = CMD_COMPLETE_TOUT_SEC; + while (i) { + ibmphp_long_delay (1 * HZ / 100); + (ulong) wpg_addr = (ulong) WPGBbar + (ulong) WPG_I2CSTAT_OFFSET; + wpg_data = readl (wpg_addr); + data = SWAPLONG (wpg_data); + if (HPC_I2CSTATUS_CHECK (data)) + break; + i--; + } + if (i == 0) { + debug ("i2c_read - Error : I2C timeout\n"); + rc = HPC_FAILURE; + } + //debug("i2c_write - read at addr[%lx] wpg_data[%lx]\n", + // (ulong)wpg_addr, wpg_data); + + debug ("i2c_write - Exit I2C rc[%x]\n", rc); + return (rc); +} + +/*---------------------------------------------------------------------- +* Name: hpc_writecmdtoindex() +* +* Action: convert a write command to proper index within a controller +* +* Input: +* +* Return index, HPC_ERROR +* Value: +* +* Side +* Effects: None. +* +* Notes: +*---------------------------------------------------------------------*/ +static u8 hpc_writecmdtoindex (u8 cmd, u8 index) +{ + u8 rc = HPC_SUCCESS; + + debug ("hpc_writecmdtoindex - Entry cmd[%x] index[%x]\n", cmd, index); + 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; + break; + + default: + debug ("hpc_writecmdtoindex - Error invalid cmd[%x]\n", cmd); + rc = HPC_ERROR; + } + + debug ("hpc_writecmdtoindex - Exit rc[%x]\n", rc); + return rc; +} + +/*---------------------------------------------------------------------- +* Name: hpc_readcmdtoindex() +* +* Action: convert a read command to proper index within a controller +* +* Input: +* +* Return index, HPC_ERROR +* Value: +* +* Side +* Effects: None. +* +* Notes: +*---------------------------------------------------------------------*/ +static u8 hpc_readcmdtoindex (u8 cmd, u8 index) +{ + u8 rc = HPC_SUCCESS; + + // NEXTREL : validate index + if (stop_polling) + debug ("hpc_readcmdtoindex - Entry cmd[%x] index[%x]\n", cmd, index); + 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; + } + if (stop_polling) + debug ("hpc_readcmdtoindex - Exit rc[%x]\n", rc); + 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 HPC_SUCCESS, HPC_FAILURE +* Value: +* +* Side +* Effects: None. +* +* Notes: +*---------------------------------------------------------------------*/ +u8 ibmphp_hpcreadslot (struct slot * pslot, u8 cmd, u8 * pstatus) +{ + void *wpg_bbar; + struct controller *ctlr_ptr; + struct list_head *pslotlist; + u8 index, status; + u8 rc = HPC_SUCCESS; + int busindex; + + if (stop_polling) + debug ("ibmphp_hpcreadslot - Entry pslot[%lx] cmd[%x] pstatus[%lx]\n", + (ulong) pslot, cmd, (ulong) pstatus); + + if ((pslot == NULL) + || ((pstatus == NULL) && (cmd != READ_ALLSTAT) && (cmd != READ_BUSSTATUS))) { + rc = HPC_FAILURE; + debug ("ibmphp_hpcreadslot - Error invalid pointer, rc[%x]\n", rc); + return rc; + } + + if (cmd == READ_BUSSTATUS) { + busindex = ibmphp_get_bus_index (pslot->bus); + if (busindex < 0) { + rc = HPC_FAILURE; + debug ("ibmphp_hpcreadslot - Exit Error:invalid bus, rc[%x]\n", rc); + return rc; + } else + index = (u8) busindex; + } else + index = pslot->ctlr_index; + + index = hpc_readcmdtoindex (cmd, index); + + if (index == HPC_ERROR) { + rc = HPC_FAILURE; + debug ("ibmphp_hpcreadslot - Exit Error:invalid index, rc[%x]\n", rc); + return rc; + } + + ctlr_ptr = pslot->ctrl; + + rc = get_hpc_access ((int) HPC_GETACCESS_TIMEOUT); + if (rc == HPC_ERROR) { + rc = HPC_FAILURE; + debug ("ibmphp_hpcreadslot - Error in get_hpc_access, rc[%x]\n", rc); + free_hpc_access (); + return rc; + } + //-------------------------------------------------------------------- + // map physical address to logical address + //-------------------------------------------------------------------- + wpg_bbar = ioremap (ctlr_ptr->u.wpeg_ctlr.wpegbbar, WPG_I2C_IOREMAP_SIZE); + + //debug ("ibmphp_hpcreadslot - ctlr id[%x] physical[%lx] logical[%lx] i2c[%x]\n", ctlr_ptr->ctlr_id, ctlr_ptr->u.wpeg_ctlr.wpegbbar, (ulong) wpg_bbar, ctlr_ptr->u.wpeg_ctlr.i2c_addr); + + //-------------------------------------------------------------------- + // check controller status before reading + //-------------------------------------------------------------------- + rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar, &status); + if (rc == HPC_SUCCESS) { + switch (cmd) { + case READ_ALLSTAT: + // update the slot structure + pslot->ctrl->status = status; + pslot->status = i2c_read (ctlr_ptr, wpg_bbar, index); + rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, ctlr_ptr, wpg_bbar, + &status); + if (rc == HPC_SUCCESS) + pslot->ext_status = i2c_read (ctlr_ptr, wpg_bbar, index + WPG_1ST_EXTSLOT_INDEX); + + break; + + case READ_SLOTSTATUS: + // DO NOT update the slot structure + *pstatus = i2c_read (ctlr_ptr, wpg_bbar, index); + break; + + case READ_EXTSLOTSTATUS: + // DO NOT update the slot structure + *pstatus = i2c_read (ctlr_ptr, wpg_bbar, index); + break; + + case READ_CTLRSTATUS: + // DO NOT update the slot structure + *pstatus = status; + break; + + case READ_BUSSTATUS: + pslot->busstatus = i2c_read (ctlr_ptr, wpg_bbar, index); + break; + case READ_REVLEVEL: + *pstatus = i2c_read (ctlr_ptr, wpg_bbar, index); + break; + case READ_HPCOPTIONS: + *pstatus = i2c_read (ctlr_ptr, wpg_bbar, index); + break; + case READ_SLOTLATCHLOWREG: + // DO NOT update the slot structure + *pstatus = i2c_read (ctlr_ptr, wpg_bbar, index); + break; + + // Not used + case READ_ALLSLOT: + //for (pslot = ibmphp_slot_head; pslot != NULL; pslot = pslot->next) { + 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 == HPC_SUCCESS) { + pslot->status = i2c_read (ctlr_ptr, wpg_bbar, index); + rc = hpc_wait_ctlr_notworking (HPC_CTLR_WORKING_TOUT, + ctlr_ptr, wpg_bbar, &status); + if (rc == HPC_SUCCESS) + pslot->ext_status = + i2c_read (ctlr_ptr, wpg_bbar, + index + WPG_1ST_EXTSLOT_INDEX); + } else { + debug ("ibmphp_hpcreadslot - Error i2c_read failed\n"); + break; + } + } + break; + default: + rc = HPC_FAILURE; + break; + } + } + //-------------------------------------------------------------------- + // cleanup + //-------------------------------------------------------------------- + iounmap (wpg_bbar); // remove physical to logical address mapping + free_hpc_access (); + + if (stop_polling) + debug ("ibmphp_hpcreadslot - Exit rc[%x]\n", rc); + return rc; +} + +/*---------------------------------------------------------------------- +* Name: ibmphp_hpcwriteslot() +* +* Action: issue a WRITE command to HPC +* +* Input: +* +* Return +* Value: +* +* Side +* Effects: None. +* +* Notes: +*---------------------------------------------------------------------*/ +u8 ibmphp_hpcwriteslot (struct slot * pslot, u8 cmd) +{ + void *wpg_bbar; + struct controller *ctlr_ptr; + u8 index, status; + u8 done; + u8 rc = HPC_SUCCESS; + int timeout; + + debug ("ibmphp_hpcwriteslot - Entry pslot[%lx] cmd[%x]\n", (ulong) pslot, cmd); + if (pslot == NULL) { + rc = HPC_FAILURE; + debug ("ibmphp_hpcwritelot - Exit rc[%x]\n", rc); + return rc; + } + + index = pslot->ctlr_index; + index = hpc_writecmdtoindex (cmd, index); + if (index == HPC_ERROR) { + rc = HPC_FAILURE; + debug ("ibmphp_hpcwriteslot - Exit rc[%x]\n", rc); + return rc; + } + + ctlr_ptr = pslot->ctrl; + + rc = get_hpc_access ((int) HPC_GETACCESS_TIMEOUT); + if (rc == HPC_ERROR) { + rc = HPC_FAILURE; + debug ("ibmphp_hpcwriteslot - Exit rc[%x]\n", rc); + free_hpc_access (); + return rc; + } + //-------------------------------------------------------------------- + // map physical address to logical address + //-------------------------------------------------------------------- + wpg_bbar = ioremap (ctlr_ptr->u.wpeg_ctlr.wpegbbar, WPG_I2C_IOREMAP_SIZE); + + debug ("ibmphp_hpcwriteslot - ctlr id[%x] physical[%lx] logical[%lx] i2c[%x]\n", + 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 == HPC_SUCCESS) { + + i2c_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 == HPC_SUCCESS) { + if (NEEDTOCHECK_CMDSTATUS (cmd)) { + if (CTLR_FINISHED (status) == HPC_CTLR_FINISHED_YES) + done = TRUE; + } else + done = TRUE; + } + if (!done) { + ibmphp_long_delay (1 * HZ); + if (timeout < 1) { + done = TRUE; + debug ("HPCwriteslot - Error command complete timeout\n"); + rc = HPC_FAILURE; + } else + timeout--; + } + } + ctlr_ptr->status = status; + } + // cleanup + iounmap (wpg_bbar); // remove physical to logical address mapping + free_hpc_access (); + + debug ("ibmphp_hpcwriteslot - Exit rc[%x]\n", rc); + return rc; +} + +/*---------------------------------------------------------------------- +* Name: get_hpc_access() +* +* Action: make sure only one process can access HPC at one time +* +* Input: timout value in milliseconds +* +* Return +* Value: +* +* Side +* Effects: None. +* +* Notes: +*---------------------------------------------------------------------*/ +static u8 get_hpc_access (int timeout) +{ + u8 rc = HPC_SUCCESS; + if (stop_polling) + debug ("get_hpc_access - Entry\n"); + down (&sem_hpcaccess); + if (stop_polling) + debug ("get_hpc_access - Exit rc[%x]\n", rc); + return rc; +} + +/*---------------------------------------------------------------------- +* Name: free_hpc_access() +* +* Action: +* +* Input: +* +* Return +* Value: +* +* Side +* Effects: None. +* +* Notes: +*---------------------------------------------------------------------*/ +void free_hpc_access () +{ + if (stop_polling) + debug ("free_hpc_access - Entry\n"); + up (&sem_hpcaccess); + if (stop_polling) + debug ("free_hpc_access - Exit\n"); + return; +} + +/*---------------------------------------------------------------------- +* Name: ibmphp_lock_operations() +* +* Action: make sure only one process can change the data structure +* +* Input: timout value in milliseconds +* +* Return +* Value: +* +* Side +* Effects: None. +* +* Notes: +*---------------------------------------------------------------------*/ +u8 ibmphp_lock_operations (int timeout) +{ + u8 rc = HPC_SUCCESS; + + debug ("ibmphp_lock_operations - Entry timeout[%d]\n", timeout); + down (&semOperations); + stop_polling = TRUE; + while (stop_polling != 0xFF) { + rc++; + if (rc > 100) { + debug ("ibmphp_lock_operations - Error timeout\n"); + break; + } + } + debug ("ibmphp_lock_operations - Exit rc[%x]\n", rc); + return rc; +} + +/*---------------------------------------------------------------------- +* Name: ibmphp_unlock_operations() +* +* Action: +* +* Input: +* +* Return +* Value: +* +* Side +* Effects: None. +* +* Notes: +*---------------------------------------------------------------------*/ +void ibmphp_unlock_operations () +{ + debug ("ibmphp_unlock_operations - Entry\n"); + stop_polling = FALSE; + up (&semOperations); + debug ("ibmphp_unlock_operations - Exit\n"); + return; +} + +/*---------------------------------------------------------------------- +* Name: poll_hpc() +* +* Action: +* +* Input: +* +* Return +* Value: +* +* Side +* Effects: None. +* +* Notes: +*---------------------------------------------------------------------*/ +static void poll_hpc (void) +{ + struct slot myslot, *pslot = NULL; + struct list_head *pslotlist; + u8 rc; + u8 oldlatchlow = 0x00; + u8 curlatchlow = 0x00; + int pollcnt = 0; + int temp = 0; + u8 ctrl_count = 0x00; + + debug ("poll_hpc - Entry\n"); + + while (ibmphp_shutdown == FALSE) { + // debug ("in while temp[%x] pollcnt[%d]\n", temp, pollcnt); + if (stop_polling) { + debug ("poll_hpc - stop_polling\n"); + stop_polling = 0xFF; + /* 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 >= MAX_CTRL) + 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_hpcreadslot (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_hpcreadslot (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 >= MAX_CTRL) + 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_hpcreadslot (pslot, + READ_SLOTLATCHLOWREG, + &curlatchlow); + } + } + } + } + INCREMENT_POLLCNT (pollcnt); + ibmphp_long_delay (POLL_INTERVAL_SEC * HZ); // snooze + } + temp++; + } + stop_polling = 0xFF; + debug ("poll_hpc - Exit\n"); +} + + +/*---------------------------------------------------------------------- +* Name: long_delay() +* delay is in jiffies to wait for +* +* Action: +* +* Input: delay - is in jiffies to wait for +* +* Return +* Value: None. +* +* Side +* Effects: None. +* +* Notes: only allow 1 customer into the delay queue at once +* this is for _huge_ delays. +* +*---------------------------------------------------------------------*/ +void ibmphp_long_delay (int delay) +{ + DECLARE_WAITQUEUE (wait, current); + + /* only allow 1 customer into the delay queue at once + * yes this makes some people wait even longer, but who really cares? + * this is for _huge_ delays to make the hardware happy as the + * signals bounce around + */ + down (&delay_sem); + + init_waitqueue_head (&delay_wait); + + add_wait_queue (&delay_wait, &wait); + set_current_state (TASK_INTERRUPTIBLE); + schedule_timeout (delay); + remove_wait_queue (&delay_wait, &wait); + set_current_state (TASK_RUNNING); + + up (&delay_sem); +} + + +// ---------------------------------------------------------------------- +// Name: ibmphp_hpcfillhpslotinfo(hotplug_slot * phpslot) +// +// Action: fill out the hotplug_slot info +// +// Input: pointer to hotplug_slot +// +// Return +// Value: HPC_SUCCESS, HPC_FAILURE +// +// Side +// Effects: None. +// +// Notes: None. +// ----------------------------------------------------------------------- + +u8 ibmphp_hpcfillhpslotinfo (struct hotplug_slot *phpslot) +{ + u8 rc = HPC_SUCCESS; + struct slot *pslot; + +// debug("ibmphp_hpcfillhpslotinfo - Entry hotplug_slot[%lx]\n",(ulong)phpslot); + + if (phpslot && phpslot->private) { + pslot = (struct slot *) phpslot->private; + rc = update_slot (pslot, (u8) TRUE); + if (rc == HPC_SUCCESS) { + + // 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 = HPC_FAILURE; + } else + rc = HPC_FAILURE; + + if (rc == HPC_SUCCESS) + debug + ("ibmphp_hpcfillhpslotinfo - Exit rc[%x] power[%x] attn[%x] latch[%x] adapter[%x]\n", + rc, phpslot->info->power_status, phpslot->info->attention_status, + phpslot->info->latch_status, phpslot->info->adapter_status); + else + debug ("ibmphp_hpcfillhpslotinfo - Exit rc[%x]\n", rc); + return rc; +} + +/*---------------------------------------------------------------------- +* Name: update_slot +* +* Action: fill out slot status and extended status, controller status +* +* Input: pointer to slot struct +* +* Return HPC_SUCCESS, HPC_FAILURE +* Value: +* +* Side +* Effects: None. +* +* Notes: +*---------------------------------------------------------------------*/ +static u8 update_slot (struct slot *pslot, u8 update) +{ + u8 rc = HPC_SUCCESS; + + debug ("update_slot - Entry pslot[%lx]\n", (ulong) pslot); + rc = ibmphp_hpcreadslot (pslot, READ_ALLSTAT, NULL); + //rc = ibmphp_hpcreadslot(pslot, READ_ALLSLOT, NULL); + + debug ("update_slot - Exit rc[%x]\n", 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 HPC_SUCCESS, HPC_FAILURE +* Value: +* +* Side +* Effects: None. +* +* Notes: +*---------------------------------------------------------------------*/ +static u8 process_changeinstatus (struct slot *pslot, struct slot *poldslot) +{ + u8 status; + u8 rc = HPC_SUCCESS; + 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 + ibmphp_long_delay (1 * HZ); + rc = ibmphp_hpcreadslot (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 ("process_changeinstatus - Exit rc[%x] disable[%x] update[%x]\n", 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 HPC_SUCCESS, HPC_FAILURE +* Value: +* +* Side +* Effects: None. +* +* Notes: +*---------------------------------------------------------------------*/ +static u8 process_changeinlatch (u8 old, u8 new) +{ + struct slot myslot, *pslot; + u8 i; + u8 mask; + u8 rc = HPC_SUCCESS; + + debug ("process_changeinlatch - Entry old[%x], new[%x]\n", 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_hpcreadslot (pslot, READ_ALLSTAT, NULL); + debug + ("process_changeinlatch - call process_changeinstatus for slot[%d]\n", i); + process_changeinstatus (pslot, &myslot); + } else { + rc = HPC_FAILURE; + debug ("process_changeinlatch - Error bad pointer for slot[%d]\n", i); + } + } + } + debug ("process_changeinlatch - Exit rc[%x]\n", rc); + return rc; +} + +/*---------------------------------------------------------------------- +* Name: hpc_poll_thread +* +* Action: polling +* +* Input: +* +* Return HPC_SUCCESS, HPC_FAILURE +* Value: +* +* Side +* Effects: None. +* +* Notes: +*---------------------------------------------------------------------*/ +static int hpc_poll_thread (void *data) +{ + debug ("hpc_poll_thread - Entry\n"); + lock_kernel (); + daemonize (); + + // New name + strcpy (current->comm, "hpc_poll"); + + unlock_kernel (); + + poll_hpc (); + + tid_poll = 0; + debug ("hpc_poll_thread - Exit\n"); + return 0; +} + + +/*---------------------------------------------------------------------- +* Name: ibmphp_hpc_start_poll_thread +* +* Action: start polling thread +* +* Input: +* +* Return HPC_SUCCESS, HPC_FAILURE +* Value: +* +* Side +* Effects: None. +* +* Notes: +*---------------------------------------------------------------------*/ +int ibmphp_hpc_start_poll_thread (void) +{ + int rc = HPC_SUCCESS; + + debug ("ibmphp_hpc_start_poll_thread - Entry\n"); + + tid_poll = kernel_thread (hpc_poll_thread, 0, 0); + if (tid_poll < 0) { + debug ("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 +* +* Input: +* +* Return HPC_SUCCESS, HPC_FAILURE +* Value: +* +* Side +* Effects: None. +* +* Notes: +*---------------------------------------------------------------------*/ +void ibmphp_hpc_stop_poll_thread (void) +{ + debug ("ibmphp_hpc_stop_poll_thread - Entry\n"); + + ibmphp_shutdown = TRUE; + ibmphp_lock_operations (0); + + // wait for poll thread to exit + while (tid_poll) + ibmphp_long_delay (1 * HZ); + + // cleanup + free_hpc_access (); + ibmphp_unlock_operations (); + + debug ("ibmphp_hpc_stop_poll_thread - Exit\n"); + return; +} + +/*---------------------------------------------------------------------- +* Name: hpc_wait_ctlr_notworking +* +* Action: wait until the controller is in a not working state +* +* Input: +* +* Return HPC_SUCCESS, HPC_FAILURE +* Value: +* +* Side +* Effects: None. +* +* Notes: +*---------------------------------------------------------------------*/ +static u8 hpc_wait_ctlr_notworking (int timeout, struct controller *ctlr_ptr, void *wpg_bbar, + u8 * pstatus) +{ + u8 rc = HPC_SUCCESS; + u8 done = FALSE; + + if (stop_polling) + debug ("hpc_wait_ctlr_notworking - Entry timeout[%d]\n", timeout); + while (!done) { + *pstatus = i2c_read (ctlr_ptr, wpg_bbar, WPG_CTLR_INDEX); + if (*pstatus == HPC_ERROR) { + rc = HPC_FAILURE; + done = TRUE; + } + if (CTLR_WORKING (*pstatus) == HPC_CTLR_WORKING_NO) + done = TRUE; + if (!done) { + ibmphp_long_delay (1 * HZ); + if (timeout < 1) { + done = TRUE; + debug ("HPCreadslot - Error ctlr timeout\n"); + rc = HPC_FAILURE; + } else + timeout--; + } + } + if (stop_polling) + debug ("hpc_wait_ctlr_notworking - Exit rc[%x] status[%x]\n", rc, *pstatus); + return rc; +} diff -Nru a/drivers/hotplug/ibmphp_pci.c b/drivers/hotplug/ibmphp_pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/hotplug/ibmphp_pci.c Thu Jan 17 15:25:50 2002 @@ -0,0 +1,1646 @@ +/* + * IBM Hot Plug Controller Driver + * + * 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 + * + */ + +#include +#include +#include +#include +#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 unconfigure_boot_card(struct slot *); +static int unconfigure_boot_device(u8, u8, u8); +static int unconfigure_boot_bridge(u8, u8, 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, u8); + +/* 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 inline 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 SCSI: + cur_func->irq[j] = SCSI_IRQ; + break; + case LAN: + cur_func->irq[j] = LAN_IRQ; + break; + default: + cur_func->irq[j] = OTHER_IRQ; + break; + } + } + } +} + + +/*********************************************************************** + * This routine will free the resources used in the configure_bridge + * or other configure functions if an error occured. The reason we don't + * pass mem_tmp is b/c if there was pfmem, then removing pfmem resource + * will also remove the associated mem from our structures + ***********************************************************************/ +static void free_me (struct res_needed *amount_needed, struct resource_node *io, struct resource_node *mem, struct resource_node *pfmem, struct resource_node **bus_io, struct resource_node **bus_mem, struct resource_node **bus_pfmem, int count, struct pci_func * func) + +{ + int i = 0; + if (amount_needed) + kfree (amount_needed); + if (pfmem) { + ibmphp_remove_resource (pfmem); + pfmem = NULL; + } + if (io) { + ibmphp_remove_resource (io); + io = NULL; + } + if (mem) { + ibmphp_remove_resource (mem); + mem = NULL; + } + for (i = 0; i < count; 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; + } + } /* end for */ +} + +/************************************************************************* + * This routine will configure 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.... + * Output: RESERROR, NOTSUPPORTED, -1 (GENERAL FAILURE), 0 + *************************************************************************/ +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; + 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 != VENDORNOTVALID) { /* 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 == VGACOMPATIBLE) { + printk ("The device %x is VGA compatible and as such not supported for hot plugging. Please choose another device.\n", cur_func->device); + return NOTSUPPORTED; + } else if (class == DISPLAYVGA) { + printk ("The device %x is not supported for hot plugging. Please choose another device.\n", cur_func->device); + return NOTSUPPORTED; + } + switch (hdr_type) { + case SINGLEDEVICE: + 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 */ + free_me (NULL, NULL, NULL, NULL, cur_func->io, cur_func->mem, cur_func->pfmem, 6, cur_func); + printk ("was not able to configure devfunc %x on bus %x. \n", cur_func->device, cur_func->busno); + return rc; + } + cur_func->next = NULL; + function = 0x8; + break; + case 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 */ + + free_me (NULL, NULL, NULL, NULL, cur_func->io, cur_func->mem, cur_func->pfmem, 6, cur_func); + printk ("was not able to configure devfunc %x on bus %x...bailing out\n", cur_func->device, cur_func->busno); + return rc; + } + newfunc = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL); + if (!newfunc) { + printk ("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 MULTIBRIDGE: + class >>= 8; + if (class != PCI2PCIBRIDGE) { + printk ("This %x is not PCI-to-PCI bridge, and as such not supported for hot-plugging. Please insert another card.\n", cur_func->device); + return NOTSUPPORTED; + } + assign_alt_irq (cur_func, class_code); + rc = configure_bridge (&cur_func, slotno); + if ((rc == RESERROR) || (rc < 0)) { + /* We need to do this in case some other BARs were properly inserted */ + free_me (NULL, NULL, NULL, NULL, cur_func->io, cur_func->mem, cur_func->pfmem, 2, cur_func); + func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */ + printk ("was not able to hot-add PPB properly.\n"); + return rc; + } + + if (rc == NOTSUPPORTED) { + printk ("You chose to insert Single Bridge, or nested bridges, this is not supported...\n"); + printk ("Bus %x, devfunc %x \n", cur_func->busno, cur_func->device); + return rc; + } + 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) { + printk ("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 */ + + free_me (NULL, NULL, NULL, NULL, cur_func->io, cur_func->mem, cur_func->pfmem, 2, cur_func); + func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */ + return rc; + } + flag = TRUE; + } + } + + newfunc = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL); + if (!newfunc) { + printk ("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 SINGLEBRIDGE: + class >>= 8; + debug ("class now is %x\n", class); + if (class != PCI2PCIBRIDGE) { + printk ("This %x is not PCI-to-PCI bridge, and as such not supported for hot-plugging. Please insert another card.\n", cur_func->device); + return NOTSUPPORTED; + } + + 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 == RESERROR) || (rc < 0)) { + /* We need to do this in case some other BARs were properly inserted */ + free_me (NULL, NULL, NULL, NULL, cur_func->io, cur_func->mem, cur_func->pfmem, 2, cur_func); + func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */ + printk ("was not able to hot-add PPB properly.\n"); + return rc; + } + if (rc == NOTSUPPORTED) { + printk ("You chose to insert Single Bridge, or nested bridges, this is not supported...\n"); + printk ("Bus %x, devfunc %x \n", cur_func->busno, cur_func->device); + return rc; + } + 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) { + printk (" 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 */ + free_me (NULL, NULL, NULL, NULL, cur_func->io, cur_func->mem, cur_func->pfmem, 2, cur_func); + func->bus = 1; /* To indicate to the unconfigure function that this is a PPB */ + return rc; + } + flag = TRUE; + } + } + + function = 0x8; + break; + default: + printk ("MAJOR PROBLEM!!!!, header type not supported? %x\n", hdr_type); + return -1; + break; + } /* end of switch */ + } /* end of valid device */ + } /* end of for */ + + if (!valid_device) { + printk ("Cannot find any valid devices on the card. Or unable to read from card.\n"); + return -1; + } + + return 0; +} + +/****************************************************************************** + * This function configures the pci BARs of a single device. + * Input: pointer to the pci_func + * Output: configured PCI, 0, RESERROR + ******************************************************************************/ +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, len[6]; + struct resource_node *io[6]; + struct resource_node *mem[6]; + struct resource_node *mem_tmp; + struct resource_node *pfmem[6]; + u8 device, function; + + debug ("inside configure_device\n"); + + 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]) { + printk ("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 { + printk ("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 -1; + } + 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]) { + printk ("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) { + printk ("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 { + printk ("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 -1; + } + } + + 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]) { + printk ("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 { + printk ("cannot allocate requested mem for bus %x, device %x, len %x\n", func->busno, func->device, len[count]); + kfree (mem[count]); + return -1; + } + 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, i, rc; + u8 sec_number, io_base; //, pri_number; + u16 pfmem_base; + u32 bar[2], len[2]; + u8 flag_io = FALSE, flag_mem = FALSE, flag_pfmem = FALSE; + u8 need_io_upper = FALSE, 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, device, irq; + // u32 temp; + function = func->function; + device = func->device; + + debug ("inside configure_bridge\n"); + + /* 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) { + printk ("cannot allocate secondary bus number for the bridged device \n"); + return -1; + } + + 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]) { + printk ("out of system memory \n"); + free_me (amount_needed, io, mem, pfmem, bus_io, bus_mem, bus_pfmem, 2, func); + return -ENOMEM; + } + 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 { + printk ("cannot allocate requested io for bus %x, device %x, len %x\n", func->busno, func->device, len[count]); + kfree (bus_io[count]); + return RESERROR; + } + + 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]) { + printk("out of system memory \n"); + free_me (amount_needed, io, mem, pfmem, bus_io, bus_mem, bus_pfmem, 2, func); + return -ENOMEM; + } + 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) { + printk ("out of system memory \n"); + free_me (amount_needed, io, mem, pfmem, bus_io, bus_mem, bus_pfmem, 2, func); + return -ENOMEM; + } + 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 { + printk ("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 RESERROR; + } + } + + 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]) { + printk ("out of system memory \n"); + free_me (amount_needed, io, mem, pfmem, bus_io, bus_mem, bus_pfmem, 2, func); + return -ENOMEM; + } + 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 { + printk ("cannot allocate requested mem for bus %x, device %x, len %x\n", func->busno, func->device, len[count]); + kfree (bus_mem[count]); + return RESERROR; + } + + 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 { + ibmphp_remove_resource (bus_mem[count]); + func->mem[count] = NULL; + } + } /* end for */ + kfree (amount_needed); + return NOTSUPPORTED; + } /* end if */ + + 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) { + printk ("out of system memory \n"); + free_me (amount_needed, io, mem, pfmem, bus_io, bus_mem, bus_pfmem, 2, func); + return -ENOMEM; + } + 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) { + printk ("out of system memory \n"); + free_me (amount_needed, io, mem, pfmem, bus_io, bus_mem, bus_pfmem, 2, func); + return -ENOMEM; + } + 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) { + printk ("out of system memory \n"); + free_me (amount_needed, io, mem, pfmem, bus_io, bus_mem, bus_pfmem, 2, func); + return -ENOMEM; + } + 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) { + printk ("out of system memory \n"); + free_me (amount_needed, io, mem, pfmem, bus_io, bus_mem, bus_pfmem, 2, func); + return -ENOMEM; + } + 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) { + printk ("out of system memory \n"); + free_me (amount_needed, io, mem, pfmem, bus_io, bus_mem, bus_pfmem, 2, func); + return -ENOMEM; + } + 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 == -1) { + free_me (amount_needed, io, mem, pfmem, bus_io, bus_mem, bus_pfmem, 2, func); + return rc; + } else if (rc == -ENOMEM) { + ibmphp_remove_bus (bus, func->busno); + return rc; + } + 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 { + printk ("Configuring bridge was unsuccessful... \n"); + free_me (amount_needed, io, mem, pfmem, bus_io, bus_mem, bus_pfmem, 2, func); + mem_tmp = NULL; + return RESERROR; + } +} +/***************************************************************************** + * 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 != VENDORNOTVALID) { /* 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 & BRIDGE) { + printk ("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 == VGACOMPATIBLE) { + printk ("The device %x is VGA compatible and as such not supported for hot plugging. Please choose another device.\n", device); + amount->not_correct = TRUE; + return amount; + } else if (class == DISPLAYVGA) { + printk ("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; +} + + +/******************************************************************************* + * This function will 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, NOTSUPPORTED + *******************************************************************************/ +int ibmphp_unconfigure_card (struct slot **slot_cur, int the_end) +{ + int i, count, rc; + struct slot *sl = *slot_cur; + struct pci_func *cur_func = NULL; +// struct io_node *io; +// struct mem_node *mem; +// struct pf_mem_node *pfmem; + + debug ("in unconfigure_card \n"); + + if (!the_end) { /* Need to unconfigure the card */ + rc = unconfigure_boot_card (sl); + if ((rc == NOTSUPPORTED) | (rc == RESERROR)) /* 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; + } + } /* end for */ + + kfree (cur_func); + cur_func = cur_func->next; + + } /* end while */ + } + + sl->func = NULL; + *slot_cur = sl; + return 0; + +} + +/* 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, NOTSUPPORTED, 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_card (struct slot *slot_cur) +{ + u16 vendor_id; + u32 class; + u8 hdr_type, device, busno; + u8 function; + int rc; + u8 valid_device = 0x00; /* To see if we are ever able to find valid device and read it */ + + debug ("in unconfigure_boot_card\n"); + + 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 != VENDORNOTVALID) { /* found correct device!!! */ + + ++valid_device; + + debug ("found correct device in unconfigure_boot_card\n"); + + /* 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 == VGACOMPATIBLE) { + printk ("The device %x function %x is VGA compatible and as such not supported for hot removing. Please choose another device.\n", device, function); + return NOTSUPPORTED; + } else if (class == DISPLAYVGA) { + printk ("The device %x function %x is not supported for hot removing. Please choose another device.\n", device, function); + return NOTSUPPORTED; + } + + switch (hdr_type) { + case SINGLEDEVICE: + if (unconfigure_boot_device (busno, device, function) != 0) { + printk ("was not able to unconfigure device %x func %x on bus %x. bailing out... \n", device, function, busno); + return RESERROR; + } + function = 0x8; + break; + case MULTIDEVICE: + if (unconfigure_boot_device (busno, device, function) != 0) { + printk ("was not able to unconfigure device %x func %x on bus %x. bailing out... \n", device, function, busno); + return RESERROR; + } + break; + case SINGLEBRIDGE: + class >>= 8; + if (class != PCI2PCIBRIDGE) { + printk ("This device %x function %x is not PCI-to-PCI bridge, and as such not supported for hot-removing. Please try another card.\n", device, function); + return NOTSUPPORTED; + } + rc = unconfigure_boot_bridge (busno, device, function); + if (rc != 0) { + printk ("was not able to hot-remove PPB properly.\n"); + return rc; + } + + function = 0x8; + break; + case MULTIBRIDGE: + class >>= 8; + if (class != PCI2PCIBRIDGE) { + printk ("This device %x function %x is not PCI-to-PCI bridge, and as such not supported for hot-removing. Please try another card.\n", device, function); + return NOTSUPPORTED; + } + rc = unconfigure_boot_bridge (busno, device, function); + if (rc != 0) { + printk ("was not able to hot-remove PPB properly.\n"); + return rc; + } + break; + default: + printk ("MAJOR PROBLEM!!!! Cannot read device's header \n"); + return -1; + break; + } /* end of switch */ + } /* end of valid device */ + } /* end of for */ + + if (!valid_device) { + printk("Could not find device to unconfigure. Or could not read the card. \n"); + return -1; + } + return 0; +} + +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, temp_end, size; + u32 tmp_address; + + debug ("in unconfigure_boot_device\n"); + + bus = ibmphp_find_res_bus (busno); + if (!bus) { + printk ("cannot find corresponding bus.\n"); + return -1; + } + + 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) { + printk ("cannot find corresponding IO resource to remove\n"); + return -1; + } + 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) { + printk ("cannot find corresponding IO resource to remove\n"); + return -1; + } + 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) { + printk ("cannot find corresponding PFMEM resource to remove\n"); + return -1; + } + 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) { + printk ("cannot find corresponding MEM resource to remove\n"); + return -1; + } + 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 ("in unconfigure_boot_bridge, busno = %x, primary_number = %x\n", 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) { + printk ("primary numbers in our structures and pci config space don't match.\n"); + return RESERROR; + } + + 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) { + printk ("there're more buses behind this bridge. Hot removal is not supported. Please choose another card\n"); + return NOTSUPPORTED; + } + + 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) { + printk ("cannot find Bus structure for the bridged device\n"); + return RESERROR; + } + + 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) { + printk ("cannot find corresponding IO resource to remove\n"); + return RESERROR; + } + 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) { + printk ("cannot find corresponding PFMEM resource to remove\n"); + return RESERROR; + } + 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) { + printk ("cannot find corresponding MEM resource to remove\n"); + return RESERROR; + } + 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 ("end of unconfigure_boot_bridge, returning success\n"); + return 0; +} + +/********************************************************************************* + * This function adds 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) { + printk ("strange, cannot find bus which is supposed to be at the system... smth terribly wrong...\n"); + return -1; + } + + list_add (&bus->bus_list, &cur_bus->bus_list); + + if (io) { + io_range = kmalloc (sizeof (struct range_node), GFP_KERNEL); + if (!io_range) { + printk ("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) { + printk ("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) { + printk ("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; +} + +/* This routine will 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 -1 in case of failure + */ + +static u8 find_sec_number (u8 primary_busno, u8 slotno) +{ + int min, max; + u8 busno; + struct bus_info *ptr; + ptr = ibmphp_find_same_bus_num (primary_busno); + if (!ptr) { + printk ("cannot get slot range of the bus from the BIOS\n"); + return -1; + } + max = ptr->slot_max; + min = ptr->slot_min; + if ((slotno > max) || (slotno < min)) { + printk ("got the wrong range\n"); + return -1; + } + busno = (u8) (slotno - (u8) min); + busno += primary_busno + 0x01; + if (!ibmphp_find_res_bus (busno)) + return busno; + return -1; +} diff -Nru a/drivers/hotplug/ibmphp_res.c b/drivers/hotplug/ibmphp_res.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/hotplug/ibmphp_res.c Thu Jan 17 15:25:50 2002 @@ -0,0 +1,2022 @@ +/* + * IBM Hot Plug Controller Driver + * + * 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 + * + */ + +#include +#include +#include +#include +#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 inline int alloc_error_bus (struct bus_node **new_bus, struct ebda_pci_rsrc * curr) +{ + struct bus_node * newbus; + + newbus = kmalloc (sizeof (struct bus_node), GFP_KERNEL); + if (!newbus) { + printk ("out of system memory \n"); + return -ENOMEM; + } + + memset (newbus, 0, sizeof (struct bus_node)); + newbus->busno = curr->bus_num; + list_add_tail (&newbus->bus_list, &gbuses); + *new_bus = newbus; + return 0; +} + +static int inline alloc_resources (struct resource_node ** res, struct ebda_pci_rsrc * curr) +{ + struct resource_node *rs = kmalloc (sizeof (struct resource_node), GFP_KERNEL); + if (!rs) { + *res = NULL; + printk ("out of system memory \n"); + return -ENOMEM; + } + 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; + *res = rs; + return 0; +} +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) { + printk (" 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); + printk(" 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 () +{ + 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; + } + + if (curr->rsrc_type & PRIMARYBUSMASK) { /* this is a primary bus resource */ + if ((curr->rsrc_type & RESTYPE) == MMASK) { /* memory */ + if (list_empty (&gbuses)) { /* no bus structure exists in place yet */ + 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); + if (bus_cur) { /* found our bus */ + 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); +/*!!!!for debug */ + if (bus_prev) + printk ("bus_prev->busno = %x\n", bus_prev->busno); +/* !!! for debug */ + 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 */ + rc = alloc_resources (&new_mem, curr); + if (rc) + return rc; + 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) { + if ((rc = alloc_error_bus (&newbus, curr))) + return rc; + 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 */ + rc = alloc_resources (&new_pfmem, curr); + if (rc) + return rc; + new_pfmem->type = PFMEM; + new_pfmem->fromMem = FALSE; + if (ibmphp_add_resource (new_pfmem) < 0) { + if ((rc = alloc_error_bus (&newbus, curr))) + return rc; + 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 */ + rc = alloc_resources (&new_io, curr); + if (rc) + return rc; + 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) { + if ((rc = alloc_error_bus (&newbus, curr))) + return rc; + 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); + rc = update_bridge_ranges (&bus_cur); /* This is to get info about PPB resources, + since EBDA doesn't put this + info into the primary bus info */ + 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 inline 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 ("inside fix_resources, bus_cur->busno = %d\n", 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 ("inside ibmphp_add_resource\n"); + + bus_cur = find_bus_wprev (res->busno, NULL, 0); + + if (!bus_cur) { /* didn't find a bus, smth's wrong!!! */ + printk ("no bus in the system, either pci_dev's wrong or allocation failed\n"); + return -1; + } + + /* 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: + printk ("cannot read the type of the resource to add... problem \n"); + return -1; + } + 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; + } /* end of while */ + + 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 ("end of ibmphp_add_resource\n"); + 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 -1 + ****************************************************************************/ +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) { + printk ("cannot find corresponding bus of the io resource to remove "); + printk ("bailing out...\n"); + return -1; + } + + 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: + printk ("unknown type for resource to remove \n"); + return -1; + } + 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) { + printk ("cannot find corresponding mem node for pfmem...\n"); + return -1; + } + + 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) { + printk ("cannot find pfmem to delete...\n"); + return -1; + } + } else { + printk ("the %s resource is not in the list to be deleted...\n", type); + return -1; + } + } + 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 inline 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: + printk ("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 RESERROR, + * otherwise, returns 0 + * + * Input: resource + * Ouput: the correct start and end address are inputted into the resource node, + * 0 or RESERROR + *****************************************************************************/ + +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 -1; + + 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!!! */ + printk ("no bus in the system, either pci_dev's wrong or allocation failed \n"); + return RESERROR; + } + + debug ("inside ibmphp_check_resource\n"); + debug ("bus_cur->busno is %d\n", bus_cur->busno); + + res->len -= 1; /* 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 */ + + 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: + printk ("wrong type of resource to check \n"); + return RESERROR; + } + res_prev = NULL; + + while (res_cur) { + range = find_range (bus_cur, res_cur); + debug ("the rangeno in ibmphp_check_resource is %d\n", res_cur->rangeno); + + if (!range) { + printk ("no range for the device exists... bailing out...\n"); + return RESERROR; + } + + /* 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 */ + printk ("no appropriate range.. bailing out...\n"); + return RESERROR; + } 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 */ + printk ("no appropriate range.. bailing out...\n"); + return RESERROR; + } 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 */ + printk ("no appropriate range.. bailing out...\n"); + return RESERROR; + } + } + } /* end if(!res_cur) */ + return RESERROR; +} + +/******************************************************************************** + * 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, -1 + ********************************************************************************/ + +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; + + prev_bus = find_bus_wprev (parent_busno, NULL, 0); + + if (!prev_bus) { + printk ("smth terribly wrong. Cannot find parent bus to the one to remove\n"); + return -1; + } + + debug ("In ibmphp_remove_bus... prev_bus->busno is %x\n", prev_bus->busno); + + remove_ranges (bus, prev_bus); + + 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, -1 + ******************************************************************************/ +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 -1; + 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 -1; + + 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 -1; + + 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; +} + +/******************************************************************************** + * This routine will find the resource node in the bus + * Input: Resource needed, start address of the resource, type or resource + * Ouput: 0, -1, the resource passed as parameter gets assigned + *******************************************************************************/ + +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: + printk ("wrong type of flag \n"); + return -1; + } + + 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) { + printk ("SOS...cannot find %s resource in the bus. \n", type); + return -1; + } + } else { + printk ("SOS... cannot find %s resource in the bus. \n", type); + return -1; + } + } + + 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; + int i = 0; + flags = 1; + + list_for_each (tmp, &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) { + printk ("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) + debug ("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) { + debug ("cannot find bus of pfmem to add...\n"); + return -1; + } + + 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; +} + +/************************************************** + * This function is for debugging purposes only + *************************************************/ +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) { + printk ("The GBUSES is not NULL?!?!?!?!?\n"); + return; + } + + list_for_each (tmp, &gbuses) { + bus_cur = list_entry (tmp, struct bus_node, bus_list); + printk ("This is bus # %d. There are \n", bus_cur->busno); + printk ("IORanges = %d\t", bus_cur->noIORanges); + printk ("MemRanges = %d\t", bus_cur->noMemRanges); + printk ("PFMemRanges = %d\n", bus_cur->noPFMemRanges); + printk ("The IO Ranges are as follows:\n"); + if (bus_cur->rangeIO) { + range = bus_cur->rangeIO; + for (i = 0; i < bus_cur->noIORanges; i++) { + printk ("rangeno is %d\n", range->rangeno); + printk ("[%x - %x]\n", range->start, range->end); + range = range->next; + } + } + + printk ("The Mem Ranges are as follows:\n"); + if (bus_cur->rangeMem) { + range = bus_cur->rangeMem; + for (i = 0; i < bus_cur->noMemRanges; i++) { + printk ("rangeno is %d\n", range->rangeno); + printk ("[%x - %x]\n", range->start, range->end); + range = range->next; + } + } + + printk ("The PFMem Ranges are as follows:\n"); + + if (bus_cur->rangePFMem) { + range = bus_cur->rangePFMem; + for (i = 0; i < bus_cur->noPFMemRanges; i++) { + printk ("rangeno is %d\n", range->rangeno); + printk ("[%x - %x]\n", range->start, range->end); + range = range->next; + } + } + + printk ("The resources on this bus are as follows\n"); + + printk ("IO...\n"); + if (bus_cur->firstIO) { + res = bus_cur->firstIO; + while (res) { + printk ("The range # is %d\n", res->rangeno); + printk ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc); + printk ("[%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; + } + } + printk ("Mem...\n"); + if (bus_cur->firstMem) { + res = bus_cur->firstMem; + while (res) { + printk ("The range # is %d\n", res->rangeno); + printk ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc); + printk ("[%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; + } + } + printk ("PFMem...\n"); + if (bus_cur->firstPFMem) { + res = bus_cur->firstPFMem; + while (res) { + printk ("The range # is %d\n", res->rangeno); + printk ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc); + printk ("[%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; + } + } + + printk ("PFMemFromMem...\n"); + if (bus_cur->firstPFMemFromMem) { + res = bus_cur->firstPFMemFromMem; + while (res) { + printk ("The range # is %d\n", res->rangeno); + printk ("The bus, devfnc is %d, %x\n", res->busno, res->devfunc); + printk ("[%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 + * None: 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 != VENDORNOTVALID) { /* found correct device!!! */ + pci_read_config_byte_nodev (ibmphp_pci_root_ops, busno, device, function, PCI_HEADER_TYPE, &hdr_type); + + switch (hdr_type) { + case SINGLEDEVICE: + function = 0x8; + break; + case MULTIDEVICE: + break; + case SINGLEBRIDGE: + function = 0x8; + case 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) { + printk ("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); + printk ("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) { + printk ("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); + printk ("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) { + printk ("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); + printk ("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; +}