/*
 * IBM Hot Plug Controller Driver
 *
 * Written By: Chuck Cole, Jyoti Shah, Tong Yu, Irene Zubarev, IBM Corporation
 *
 * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com)
 * Copyright (c) 2001,2002 IBM Corp.
 *
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
 * NON INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * Send feedback to <gregkh@us.ibm.com>
 *
 */

#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/wait.h>
#include <linux/smp_lock.h>
#include "../../arch/i386/pci/pci.h"	/* for struct irq_routing_table */
#include "ibmphp.h"

#define attn_on(sl)  ibmphp_hpc_writeslot (sl, HPC_SLOT_ATTNON)
#define attn_off(sl) ibmphp_hpc_writeslot (sl, HPC_SLOT_ATTNOFF)
#define attn_LED_blink(sl) ibmphp_hpc_writeslot (sl, HPC_SLOT_BLINKLED)
#define get_ctrl_revision(sl, rev) ibmphp_hpc_readslot (sl, READ_REVLEVEL, rev)
#define get_hpc_options(sl, opt) ibmphp_hpc_readslot (sl, READ_HPCOPTIONS, opt)

#define DRIVER_VERSION	"0.6"
#define DRIVER_DESC	"IBM Hot Plug PCI Controller Driver"

int ibmphp_debug;

static int debug;
MODULE_PARM (debug, "i");
MODULE_PARM_DESC (debug, "Debugging mode enabled or not");
MODULE_LICENSE ("GPL");
MODULE_DESCRIPTION (DRIVER_DESC);

static int *ops[MAX_OPS + 1];
struct pci_bus *ibmphp_pci_bus;
static int max_slots;

static int irqs[16];    /* PIC mode IRQ's we're using so far (in case MPS tables don't provide default info for empty slots */

static int init_flag;

/*
static int get_max_adapter_speed_1 (struct hotplug_slot *, u8 *, u8);

static inline int get_max_adapter_speed (struct hotplug_slot *hs, u8 *value)
{
	return get_max_adapter_speed_1 (hs, value, 1);
}
*/
static inline int get_cur_bus_info (struct slot **sl) 
{
	int rc = 1;
	struct slot * slot_cur = *sl;

	debug ("options = %x\n", slot_cur->ctrl->options);
	debug ("revision = %x\n", slot_cur->ctrl->revision);	

	if (READ_BUS_STATUS (slot_cur->ctrl)) 
		rc = ibmphp_hpc_readslot (slot_cur, READ_BUSSTATUS, NULL);
	
	if (rc) 
		return rc;
	  
	slot_cur->bus_on->current_speed = CURRENT_BUS_SPEED (slot_cur->busstatus);
	if (READ_BUS_MODE (slot_cur->ctrl))
		slot_cur->bus_on->current_bus_mode = CURRENT_BUS_MODE (slot_cur->busstatus);
	else
		slot_cur->bus_on->current_bus_mode = 0xFF;

	debug ("busstatus = %x, bus_speed = %x, bus_mode = %x\n", slot_cur->busstatus, slot_cur->bus_on->current_speed, slot_cur->bus_on->current_bus_mode);
	
	*sl = slot_cur;
	return 0;
}

static inline int slot_update (struct slot **sl)
{
	int rc;
 	rc = ibmphp_hpc_readslot (*sl, READ_ALLSTAT, NULL);
	if (rc) 
		return rc;
	if (!init_flag)
		return get_cur_bus_info (sl);
	return rc;
}

static int __init get_max_slots (void)
{
	struct slot * slot_cur;
	struct list_head * tmp;
	u8 slot_count = 0;

	list_for_each (tmp, &ibmphp_slot_head) {
		slot_cur = list_entry (tmp, struct slot, ibm_slot_list);
		/* sometimes the hot-pluggable slots start with 4 (not always from 1 */
		slot_count = max (slot_count, slot_cur->number);
	}
	return slot_count;
}

/* This routine will put the correct slot->device information per slot.  It's
 * called from initialization of the slot structures. It will also assign
 * interrupt numbers per each slot.
 * Parameters: struct slot
 * Returns 0 or errors
 */
int ibmphp_init_devno (struct slot **cur_slot)
{
	struct irq_routing_table *rtable;
	int len;
	int loop;
	int i;

	rtable = pcibios_get_irq_routing_table ();
	if (!rtable) {
		err ("no BIOS routing table...\n");
		return -ENOMEM;
	}

	len = (rtable->size - sizeof (struct irq_routing_table)) / sizeof (struct irq_info);

	if (!len)
		return -1;
	for (loop = 0; loop < len; loop++) {
		if ((*cur_slot)->number == rtable->slots[loop].slot) {
		if ((*cur_slot)->bus == rtable->slots[loop].bus) {
			(*cur_slot)->device = PCI_SLOT (rtable->slots[loop].devfn);
			for (i = 0; i < 4; i++)
				(*cur_slot)->irq[i] = IO_APIC_get_PCI_irq_vector ((int) (*cur_slot)->bus, (int) (*cur_slot)->device, i);

				debug ("(*cur_slot)->irq[0] = %x\n", (*cur_slot)->irq[0]);
				debug ("(*cur_slot)->irq[1] = %x\n", (*cur_slot)->irq[1]);
				debug ("(*cur_slot)->irq[2] = %x\n", (*cur_slot)->irq[2]);
				debug ("(*cur_slot)->irq[3] = %x\n", (*cur_slot)->irq[3]);

				debug ("rtable->exlusive_irqs = %x\n", rtable->exclusive_irqs);
				debug ("rtable->slots[loop].irq[0].bitmap = %x\n", rtable->slots[loop].irq[0].bitmap);
				debug ("rtable->slots[loop].irq[1].bitmap = %x\n", rtable->slots[loop].irq[1].bitmap);
				debug ("rtable->slots[loop].irq[2].bitmap = %x\n", rtable->slots[loop].irq[2].bitmap);
				debug ("rtable->slots[loop].irq[3].bitmap = %x\n", rtable->slots[loop].irq[3].bitmap);

				debug ("rtable->slots[loop].irq[0].link= %x\n", rtable->slots[loop].irq[0].link);
				debug ("rtable->slots[loop].irq[1].link = %x\n", rtable->slots[loop].irq[1].link);
				debug ("rtable->slots[loop].irq[2].link = %x\n", rtable->slots[loop].irq[2].link);
				debug ("rtable->slots[loop].irq[3].link = %x\n", rtable->slots[loop].irq[3].link);
				debug ("end of init_devno\n");
				return 0;
			}
		}
	}

	return -1;
}

static inline int power_on (struct slot *slot_cur)
{
	u8 cmd = HPC_SLOT_ON;
	int retval;

	retval = ibmphp_hpc_writeslot (slot_cur, cmd);
	if (retval) {
		err ("power on failed\n");
		return retval;
	}
	if (CTLR_RESULT (slot_cur->ctrl->status)) {
		err ("command not completed successfully in power_on \n");
		return -EIO;
	}
	long_delay (3 * HZ); /* For ServeRAID cards, and some 66 PCI */
	return 0;
}

static inline int power_off (struct slot *slot_cur)
{
	u8 cmd = HPC_SLOT_OFF;
	int retval;

	retval = ibmphp_hpc_writeslot (slot_cur, cmd);
	if (retval) {
		err ("power off failed \n");
		return retval;
	}
	if (CTLR_RESULT (slot_cur->ctrl->status)) {
		err ("command not completed successfully in power_off \n");
		return -EIO;
	}
	return 0;
}

static int set_attention_status (struct hotplug_slot *hotplug_slot, u8 value)
{
	int rc = 0;
	struct slot *pslot;
	u8 cmd;
	int hpcrc = 0;

	debug ("set_attention_status - Entry hotplug_slot[%lx] value[%x]\n", (ulong) hotplug_slot, value);
	ibmphp_lock_operations ();
	cmd = 0x00;     // avoid compiler warning

	if (hotplug_slot) {
		switch (value) {
		case HPC_SLOT_ATTN_OFF:
			cmd = HPC_SLOT_ATTNOFF;
			break;
		case HPC_SLOT_ATTN_ON:
			cmd = HPC_SLOT_ATTNON;
			break;
		case HPC_SLOT_ATTN_BLINK:
			cmd = HPC_SLOT_BLINKLED;
			break;
		default:
			rc = -ENODEV;
			err ("set_attention_status - Error : invalid input [%x]\n", value);
			break;
		}
		if (rc == 0) {
			pslot = (struct slot *) hotplug_slot->private;
			if (pslot)
				hpcrc = ibmphp_hpc_writeslot (pslot, cmd);
			else
				rc = -ENODEV;
		}
	} else	
		rc = -ENODEV;

	if (hpcrc)
		rc = hpcrc;

	ibmphp_unlock_operations ();

	debug ("set_attention_status - Exit rc[%d]\n", rc);
	return rc;
}

static int get_attention_status (struct hotplug_slot *hotplug_slot, u8 * value)
{
	int rc = -ENODEV;
	struct slot *pslot;
	int hpcrc = 0;
	struct slot myslot;

	debug ("get_attention_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong) hotplug_slot, (ulong) value);
        
	ibmphp_lock_operations ();
	if (hotplug_slot && value) {
		pslot = (struct slot *) hotplug_slot->private;
		if (pslot) {
			memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot));
			hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status));
			if (!hpcrc)
				hpcrc = ibmphp_hpc_readslot (pslot, READ_EXTSLOTSTATUS, &(myslot.ext_status));
			if (!hpcrc) {
				*value = SLOT_ATTN (myslot.status, myslot.ext_status);
				rc = 0;
			}
		}
	} else
		rc = -ENODEV;

	if (hpcrc)
		rc = hpcrc;

	ibmphp_unlock_operations ();
	debug ("get_attention_status - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value);
	return rc;
}

static int get_latch_status (struct hotplug_slot *hotplug_slot, u8 * value)
{
	int rc = -ENODEV;
	struct slot *pslot;
	int hpcrc = 0;
	struct slot myslot;

	debug ("get_latch_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong) hotplug_slot, (ulong) value);
	ibmphp_lock_operations ();
	if (hotplug_slot && value) {
		pslot = (struct slot *) hotplug_slot->private;
		if (pslot) {
			memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot));
			hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status));
			if (!hpcrc) {
				*value = SLOT_LATCH (myslot.status);
				rc = 0;
			}
		}
	} else
		rc = -ENODEV;

	if (hpcrc)
		rc = hpcrc;

	ibmphp_unlock_operations ();
	debug ("get_latch_status - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value);
	return rc;
}


static int get_power_status (struct hotplug_slot *hotplug_slot, u8 * value)
{
	int rc = -ENODEV;
	struct slot *pslot;
	int hpcrc = 0;
	struct slot myslot;

	debug ("get_power_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong) hotplug_slot, (ulong) value);
	ibmphp_lock_operations ();
	if (hotplug_slot && value) {
		pslot = (struct slot *) hotplug_slot->private;
		if (pslot) {
			memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot));
			hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status));
			if (!hpcrc) {
				*value = SLOT_PWRGD (myslot.status);
				rc = 0;
			}
		}
	} else
		rc = -ENODEV;

	if (hpcrc)
		rc = hpcrc;

	ibmphp_unlock_operations ();
	debug ("get_power_status - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value);
	return rc;
}

static int get_adapter_present (struct hotplug_slot *hotplug_slot, u8 * value)
{
	int rc = -ENODEV;
	struct slot *pslot;
	u8 present;
	int hpcrc = 0;
	struct slot myslot;

	debug ("get_adapter_status - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong) hotplug_slot, (ulong) value);
	ibmphp_lock_operations ();
	if (hotplug_slot && value) {
		pslot = (struct slot *) hotplug_slot->private;
		if (pslot) {
			memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot));
			hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status));
			if (!hpcrc) {
				present = SLOT_PRESENT (myslot.status);
				if (present == HPC_SLOT_EMPTY)
					*value = 0;
				else
					*value = 1;
				rc = 0;
			}
		}
	} else
		rc = -ENODEV;
	if (hpcrc)
		rc = hpcrc;

	ibmphp_unlock_operations ();
	debug ("get_adapter_present - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value);
	return rc;
}

static int get_max_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
{
	int rc = -ENODEV;
	struct slot *pslot;
	u8 mode = 0;

	debug ("%s - Entry hotplug_slot[%p] pvalue[%p]\n", __FUNCTION__,
		hotplug_slot, value);

	ibmphp_lock_operations ();

	if (hotplug_slot && value) {
		pslot = (struct slot *) hotplug_slot->private;
		if (pslot) {
			rc = 0;
			mode = pslot->supported_bus_mode;
			*value = pslot->supported_speed; 
			switch (*value) {
			case BUS_SPEED_33:
				break;
			case BUS_SPEED_66:
				if (mode == BUS_MODE_PCIX) 
					*value += 0x01;
				break;
			case BUS_SPEED_100:
			case BUS_SPEED_133:
				*value = pslot->supported_speed + 0x01;
				break;
			default:
				/* Note (will need to change): there would be soon 256, 512 also */
				rc = -ENODEV;
			}
		}
	} else
		rc = -ENODEV;

	ibmphp_unlock_operations ();
	debug ("%s - Exit rc[%d] value[%x]\n", __FUNCTION__, rc, *value);
	return rc;
}

static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_speed *value)
{
	int rc = -ENODEV;
	struct slot *pslot;
	u8 mode = 0;

	debug ("%s - Entry hotplug_slot[%p] pvalue[%p]\n", __FUNCTION__,
		hotplug_slot, value);

	ibmphp_lock_operations ();

	if (hotplug_slot && value) {
		pslot = (struct slot *) hotplug_slot->private;
		if (pslot) {
			rc = get_cur_bus_info (&pslot);
			if (!rc) {
				mode = pslot->bus_on->current_bus_mode;
				*value = pslot->bus_on->current_speed;
				switch (*value) {
				case BUS_SPEED_33:
					break;
				case BUS_SPEED_66:
					if (mode == BUS_MODE_PCIX) 
						*value += 0x01;
					else if (mode == BUS_MODE_PCI)
						;
					else
						*value = PCI_SPEED_UNKNOWN;
					break;
				case BUS_SPEED_100:
				case BUS_SPEED_133:
					*value += 0x01;
					break;
				default:
					/* Note of change: there would also be 256, 512 soon */
					rc = -ENODEV;
				}
			}
		}
	} else
		rc = -ENODEV;

	ibmphp_unlock_operations ();
	debug ("%s - Exit rc[%d] value[%x]\n", __FUNCTION__, rc, *value);
	return rc;
}
/*
static int get_max_adapter_speed_1 (struct hotplug_slot *hotplug_slot, u8 * value, u8 flag)
{
	int rc = -ENODEV;
	struct slot *pslot;
	int hpcrc = 0;
	struct slot myslot;

	debug ("get_max_adapter_speed_1 - Entry hotplug_slot[%lx] pvalue[%lx]\n", (ulong)hotplug_slot, (ulong) value);

	if (flag)
		ibmphp_lock_operations ();

	if (hotplug_slot && value) {
		pslot = (struct slot *) hotplug_slot->private;
		if (pslot) {
			memcpy ((void *) &myslot, (void *) pslot, sizeof (struct slot));
			hpcrc = ibmphp_hpc_readslot (pslot, READ_SLOTSTATUS, &(myslot.status));

			if (!(SLOT_LATCH (myslot.status)) && (SLOT_PRESENT (myslot.status))) {
				hpcrc = ibmphp_hpc_readslot (pslot, READ_EXTSLOTSTATUS, &(myslot.ext_status));
				if (!hpcrc) {
					*value = SLOT_SPEED (myslot.ext_status);
					rc = 0;
				}
			} else {
				*value = MAX_ADAPTER_NONE;
				rc = 0;
			}
                }
        } else
		rc = -ENODEV;

	if (hpcrc)
		rc = hpcrc;

	if (flag)
		ibmphp_unlock_operations ();

	debug ("get_max_adapter_speed_1 - Exit rc[%d] hpcrc[%x] value[%x]\n", rc, hpcrc, *value);
	return rc;
}

static int get_bus_name (struct hotplug_slot *hotplug_slot, char * value)
{
	int rc = -ENODEV;
	struct slot *pslot = NULL;

	debug ("get_bus_name - Entry hotplug_slot[%lx] \n", (ulong)hotplug_slot);

	ibmphp_lock_operations ();

	if (hotplug_slot) {
		pslot = (struct slot *) hotplug_slot->private;
		if (pslot) {
			rc = 0;
			snprintf (value, 100, "Bus %x", pslot->bus);
		}
	} else
		rc = -ENODEV;

	ibmphp_unlock_operations ();
	debug ("get_bus_name - Exit rc[%d] value[%x]\n", rc, *value);
	return rc;
}
*/

/*******************************************************************************
 * This routine will initialize the ops data structure used in the validate
 * function. It will also power off empty slots that are powered on since BIOS
 * leaves those on, albeit disconnected
 ******************************************************************************/
static int __init init_ops (void)
{
	struct slot *slot_cur;
	struct list_head *tmp;
	int retval;
	int rc;
	int j;

	for (j = 0; j < MAX_OPS; j++) {
		ops[j] = (int *) kmalloc ((max_slots + 1) * sizeof (int), GFP_KERNEL);
		memset (ops[j], 0, (max_slots + 1) * sizeof (int));
		if (!ops[j]) {
			err ("out of system memory \n");
			return -ENOMEM;
		}
	}

	ops[ADD][0] = 0;
	ops[REMOVE][0] = 0;
	ops[DETAIL][0] = 0;

	list_for_each (tmp, &ibmphp_slot_head) {
		slot_cur = list_entry (tmp, struct slot, ibm_slot_list);

		if (!slot_cur)
			return -ENODEV;

		debug ("BEFORE GETTING SLOT STATUS, slot # %x\n", slot_cur->number);
		if (slot_cur->ctrl->revision == 0xFF) 
			if (get_ctrl_revision (slot_cur, &slot_cur->ctrl->revision))
				return -1;

		if (slot_cur->bus_on->current_speed == 0xFF) 
			if (get_cur_bus_info (&slot_cur)) 
				return -1;

		if (slot_cur->ctrl->options == 0xFF)
			if (get_hpc_options (slot_cur, &slot_cur->ctrl->options))
				return -1;

		retval = slot_update (&slot_cur);
		if (retval)
			return retval;

		debug ("status = %x, ext_status = %x\n", slot_cur->status, slot_cur->ext_status);
		debug ("SLOT_POWER = %x, SLOT_PRESENT = %x, SLOT_LATCH = %x\n", SLOT_POWER (slot_cur->status), SLOT_PRESENT (slot_cur->status), SLOT_LATCH (slot_cur->status));

		if (!(SLOT_PWRGD (slot_cur->status)) && (SLOT_PRESENT (slot_cur->status)) && !(SLOT_LATCH (slot_cur->status)))
			/* No power, adapter, and latch closed */
			ops[ADD][slot_cur->number] = 1;
		else
			ops[ADD][slot_cur->number] = 0;

		ops[DETAIL][slot_cur->number] = 1;

		if ((SLOT_PWRGD (slot_cur->status)) && (SLOT_PRESENT (slot_cur->status)) && !(SLOT_LATCH (slot_cur->status)))
			/*Power,adapter,latch closed */
			ops[REMOVE][slot_cur->number] = 1;
		else
			ops[REMOVE][slot_cur->number] = 0;

		if ((SLOT_PWRGD (slot_cur->status)) && !(SLOT_PRESENT (slot_cur->status)) && !(SLOT_LATCH (slot_cur->status))) {
			debug ("BEFORE POWER OFF COMMAND\n");
				rc = power_off (slot_cur);
				if (rc)
					return rc;

	/*		retval = slot_update (&slot_cur);
	 *		if (retval)
	 *			return retval;
	 *		ibmphp_update_slot_info (slot_cur);
	 */
		}
	}
	init_flag = 0;
	return 0;
}

/* This operation will check whether the slot is within the bounds and
 * the operation is valid to perform on that slot
 * Parameters: slot, operation
 * Returns: 0 or error codes
 */
static int validate (struct slot *slot_cur, int opn)
{
	int number;
	int retval;

	if (!slot_cur)
		return -ENODEV;
	number = slot_cur->number;
	if ((number > max_slots) || (number < 0))
		return -EBADSLT;
	debug ("slot_number in validate is %d\n", slot_cur->number);

	retval = slot_update (&slot_cur);
	if (retval)
		return retval;

	if (!(SLOT_PWRGD (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_PWRGD (slot_cur->status)) && (SLOT_PRESENT (slot_cur->status))
	    && !(SLOT_LATCH (slot_cur->status)))
		ops[REMOVE][number] = 1;
	else
		ops[REMOVE][number] = 0;

	switch (opn) {
		case ENABLE:
			if (ops[ADD][number])
				return 0;
			break;
		case DISABLE:
			if (ops[REMOVE][number])
				return 0;
			break;
		case DETAIL:
			if (ops[DETAIL][number])
				return 0;
			break;
		default:
			return -EINVAL;
			break;
	}
	err ("validate failed....\n");
	return -EINVAL;
}

/********************************************************************************
 * This routine is for updating the data structures in the hotplug core
 * Parameters: struct slot
 * Returns: 0 or error
 *******************************************************************************/
int ibmphp_update_slot_info (struct slot *slot_cur)
{
	struct hotplug_slot_info *info;
	char buffer[30];
	int rc;
	u8 bus_speed;
	u8 mode;

	info = kmalloc (sizeof (struct hotplug_slot_info), GFP_KERNEL);
	if (!info) {
		err ("out of system memory \n");
		return -ENOMEM;
	}
        
	strncpy (buffer, slot_cur->hotplug_slot->name, 30);
	info->power_status = SLOT_PWRGD (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;
	mode = slot_cur->bus_on->current_bus_mode;

	switch (bus_speed) {
	case BUS_SPEED_33:
		break;
	case BUS_SPEED_66:
		if (mode == BUS_MODE_PCIX) 
			bus_speed += 0x01;
		else if (mode == BUS_MODE_PCI)
			;
		else
			bus_speed = PCI_SPEED_UNKNOWN;
		break;
	case BUS_SPEED_100:
	case BUS_SPEED_133:
		bus_speed += 0x01;
		break;
	default:
		bus_speed = PCI_SPEED_UNKNOWN;
	}

	info->cur_bus_speed = bus_speed;
	info->max_bus_speed = slot_cur->hotplug_slot->info->max_bus_speed;
	// To do: bus_names 
	
	rc = pci_hp_change_slot_info (buffer, info);
	kfree (info);
	return rc;
}


/******************************************************************************
 * This function will return the pci_func, given bus and devfunc, or NULL.  It
 * is called from visit routines
 ******************************************************************************/

static struct pci_func *ibm_slot_find (u8 busno, u8 device, u8 function)
{
	struct pci_func *func_cur;
	struct slot *slot_cur;
	struct list_head * tmp;
	list_for_each (tmp, &ibmphp_slot_head) {
		slot_cur = list_entry (tmp, struct slot, ibm_slot_list);
		if (slot_cur->func) {
			func_cur = slot_cur->func;
			while (func_cur) {
				if ((func_cur->busno == busno) && (func_cur->device == device) && (func_cur->function == function))
					return func_cur;
				func_cur = func_cur->next;
			}
		}
	}
	return NULL;
}

/* This routine is to find the pci_bus from kernel structures.
 * Parameters: bus number
 * Returns : pci_bus *  or NULL if not found
 */
static struct pci_bus *find_bus (u8 busno)
{
	const struct list_head *tmp;
	struct pci_bus *bus;
	debug ("inside find_bus, busno = %x \n", busno);

	list_for_each (tmp, &pci_root_buses) {
		bus = (struct pci_bus *) pci_bus_b (tmp);
		if (bus)
			if (bus->number == busno)
				return bus;
	}
	return NULL;
}

/*************************************************************
 * This routine frees up memory used by struct slot, including
 * the pointers to pci_func, bus, hotplug_slot, controller,
 * and deregistering from the hotplug core
 *************************************************************/
static void free_slots (void)
{
	struct slot *slot_cur;
	struct list_head * tmp;
	struct list_head * next;

	debug ("%s -- enter\n", __FUNCTION__);

	list_for_each_safe (tmp, next, &ibmphp_slot_head) {
	
		slot_cur = list_entry (tmp, struct slot, ibm_slot_list);

		pci_hp_deregister (slot_cur->hotplug_slot);

		if (slot_cur->hotplug_slot) {
			kfree (slot_cur->hotplug_slot);
			slot_cur->hotplug_slot = NULL;
		}

		if (slot_cur->ctrl) 
			slot_cur->ctrl = NULL;
		
		if (slot_cur->bus_on) 
			slot_cur->bus_on = NULL;

		ibmphp_unconfigure_card (&slot_cur, -1);  /* we don't want to actually remove the resources, since free_resources will do just that */

		kfree (slot_cur);
		slot_cur = NULL;
	}
	debug ("%s -- exit\n", __FUNCTION__);
}

static int ibm_is_pci_dev_in_use (struct pci_dev *dev)
{
	int i = 0;
	int inuse = 0;

	if (dev->driver)
		return 1;

	for (i = 0; !dev->driver && !inuse && (i < 6); i++) {

		if (!pci_resource_start (dev, i))
			continue;

		if (pci_resource_flags (dev, i) & IORESOURCE_IO)
			inuse = check_region (pci_resource_start (dev, i), pci_resource_len (dev, i));

		else if (pci_resource_flags (dev, i) & IORESOURCE_MEM)
			inuse = check_mem_region (pci_resource_start (dev, i), pci_resource_len (dev, i));
	}

	return inuse;
}

static int ibm_pci_hp_remove_device (struct pci_dev *dev)
{
	if (ibm_is_pci_dev_in_use (dev)) {
		err ("***Cannot safely power down device -- it appears to be in use***\n");
		return -EBUSY;
	}
	pci_remove_device (dev);
	return 0;
}

static int ibm_unconfigure_visit_pci_dev_phase2 (struct pci_dev_wrapped *wrapped_dev, struct pci_bus_wrapped *wrapped_bus)
{
	struct pci_dev *dev = wrapped_dev->dev;
	struct pci_func *temp_func;
	int i = 0;

	do {
		temp_func = ibm_slot_find (dev->bus->number, dev->devfn >> 3, i++);
	} while (temp_func && (temp_func->function != (dev->devfn & 0x07)));

	if (dev) {
		if (ibm_pci_hp_remove_device (dev) == 0)
			kfree (dev);    /* Now, remove */
		else
			return -1;
	}

	if (temp_func)
		temp_func->dev = NULL;
	else
		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,
};


/*
 * The following function is to fix kernel bug regarding 
 * getting bus entries, here we manually add those primary 
 * bus entries to kernel bus structure whenever apply
 */

static u8 bus_structure_fixup (u8 busno)
{
	struct pci_bus *bus;
	struct pci_dev *dev;
	u16 l;

	if (find_bus (busno) || !(ibmphp_find_same_bus_num (busno)))
		return 1;

	bus = kmalloc (sizeof (*bus), GFP_KERNEL);
	if (!bus) {
		err ("%s - out of memory\n", __FUNCTION__);
		return 1;
	}
	dev = kmalloc (sizeof (*dev), GFP_KERNEL);
	if (!dev) {
		kfree (bus);
		err ("%s - out of memory\n", __FUNCTION__);
		return 1;
	}

	bus->number = busno;
	bus->ops = ibmphp_pci_bus->ops;
	dev->bus = bus;
	for (dev->devfn = 0; dev->devfn < 256; dev->devfn += 8) {
		if (!pci_read_config_word (dev, PCI_VENDOR_ID, &l) &&  l != 0x0000 && l != 0xffff) {
			debug ("%s - Inside bus_struture_fixup() \n", __FUNCTION__);
			pci_scan_bus (busno, ibmphp_pci_bus->ops, NULL);
			break;
		}
	}

	kfree (dev);
	kfree (bus);

	return 0;
}

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;
	int flag = 0;	/* this is to make sure we don't double scan the bus, for bridged devices primarily */

	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 (!(bus_structure_fixup (func->busno)))
		flag = 1;
	if (func->dev == NULL)
		func->dev = pci_find_slot (func->busno, (func->device << 3) | (func->function & 0x7));

	if (func->dev == NULL) {
		dev0.bus = find_bus (func->busno);
		dev0.devfn = ((func->device << 3) + (func->function & 0x7));
		dev0.sysdata = dev0.bus->sysdata;

		func->dev = pci_scan_slot (&dev0);

		if (func->dev == NULL) {
			err ("ERROR... : pci_dev still NULL \n");
			return 0;
		}
	}
	if (!(flag) && (func->dev->hdr_type == PCI_HEADER_TYPE_BRIDGE)) {
		pci_read_config_byte (func->dev, PCI_SECONDARY_BUS, &bus);
		child = (struct pci_bus *) pci_add_new_bus (func->dev->bus, (func->dev), bus);
		pci_do_scan_bus (child);
	}

	temp = func->dev;
	if (temp) {
		wrapped_dev.dev = temp;
		wrapped_bus.bus = temp->bus;
		rc = pci_visit_dev (&configure_functions, &wrapped_dev, &wrapped_bus);
	}
	return rc;
}

/*******************************************************
 * Returns whether the bus is empty or not 
 *******************************************************/
static int is_bus_empty (struct slot * slot_cur)
{
	int rc;
	struct slot * tmp_slot;
	u8 i = slot_cur->bus_on->slot_min;

	while (i <= slot_cur->bus_on->slot_max) {
		if (i == slot_cur->number) {
			i++;
			continue;
		}
		tmp_slot = ibmphp_get_slot_from_physical_num (i);
		rc = slot_update (&tmp_slot);
		if (rc)
			return 0;
		if (SLOT_PRESENT (tmp_slot->status) && SLOT_PWRGD (tmp_slot->status))
			return 0;
		i++;
	}
	return 1;
}

/***********************************************************
 * If the HPC permits and the bus currently empty, tries to set the 
 * bus speed and mode at the maximum card and bus capability
 * Parameters: slot
 * Returns: bus is set (0) or error code
 ***********************************************************/
static int set_bus (struct slot * slot_cur)
{
	int rc;
	u8 speed;
	u8 cmd = 0x0;
	const struct list_head *tmp;
	struct pci_dev * dev;
	int retval;

	debug ("%s - entry slot # %d \n", __FUNCTION__, slot_cur->number);
	if (SET_BUS_STATUS (slot_cur->ctrl) && is_bus_empty (slot_cur)) {
		rc = slot_update (&slot_cur);
		if (rc)
			return rc;
		speed = SLOT_SPEED (slot_cur->ext_status);
		debug ("ext_status = %x, speed = %x\n", slot_cur->ext_status, speed);
		switch (speed) {
		case HPC_SLOT_SPEED_33:
			cmd = HPC_BUS_33CONVMODE;
			break;
		case HPC_SLOT_SPEED_66:
			if (SLOT_PCIX (slot_cur->ext_status)) {
				if ((slot_cur->supported_speed >= BUS_SPEED_66) && (slot_cur->supported_bus_mode == BUS_MODE_PCIX))
					cmd = HPC_BUS_66PCIXMODE;
				else if (!SLOT_BUS_MODE (slot_cur->ext_status))
					/* if max slot/bus capability is 66 pci
					and there's no bus mode mismatch, then
					the adapter supports 66 pci */ 
					cmd = HPC_BUS_66CONVMODE;
				else
					cmd = HPC_BUS_33CONVMODE;
			} else {
				if (slot_cur->supported_speed >= BUS_SPEED_66)
					cmd = HPC_BUS_66CONVMODE;
				else
					cmd = HPC_BUS_33CONVMODE;
			}
			break;
		case HPC_SLOT_SPEED_133:
			switch (slot_cur->supported_speed) {
			case BUS_SPEED_33:
				cmd = HPC_BUS_33CONVMODE;
				break;
			case BUS_SPEED_66:
				if (slot_cur->supported_bus_mode == BUS_MODE_PCIX)
					cmd = HPC_BUS_66PCIXMODE;
				else
					cmd = HPC_BUS_66CONVMODE;
				break;
			case BUS_SPEED_100:
				cmd = HPC_BUS_100PCIXMODE;
				break;
			case BUS_SPEED_133:
				/* This is to take care of the bug in CIOBX chip*/
				list_for_each (tmp, &pci_devices) {
					dev = (struct pci_dev *) pci_dev_g (tmp);
					if (dev) {
						if ((dev->vendor == 0x1166) && (dev->device == 0x0101))
							ibmphp_hpc_writeslot (slot_cur, HPC_BUS_100PCIXMODE);
					}
				}
				cmd = HPC_BUS_133PCIXMODE;
				break;
			default:
				err ("Wrong bus speed \n");
				return -ENODEV;
			}
			break;
		default:
			err ("wrong slot speed \n");
			return -ENODEV;
		}
		debug ("setting bus speed for slot %d, cmd %x\n", slot_cur->number, cmd);
		retval = ibmphp_hpc_writeslot (slot_cur, cmd);
		if (retval) {
			err ("setting bus speed failed\n");
			return retval;
		}
		if (CTLR_RESULT (slot_cur->ctrl->status)) {
			err ("command not completed successfully in set_bus \n");
			return -EIO;
		}
	}
	/* This is for x440, once Brandon fixes the firmware, 
	will not need this delay */
	long_delay (1 * HZ);
	debug ("%s -Exit \n", __FUNCTION__);
	return 0;
}

/* This routine checks the bus limitations that the slot is on from the BIOS.
 * This is used in deciding whether or not to power up the slot.  
 * (electrical/spec limitations. For example, >1 133 MHz or >2 66 PCI cards on
 * same bus) 
 * Parameters: slot
 * Returns: 0 = no limitations, -EINVAL = exceeded limitations on the bus
 */
static int check_limitations (struct slot *slot_cur)
{
	u8 i;
	struct slot * tmp_slot;
	u8 count = 0;
	u8 limitation = 0;

	for (i = slot_cur->bus_on->slot_min; i <= slot_cur->bus_on->slot_max; i++) {
		tmp_slot = ibmphp_get_slot_from_physical_num (i);
		if ((SLOT_PWRGD (tmp_slot->status)) && !(SLOT_CONNECT (tmp_slot->status))) 
			count++;
	}
	get_cur_bus_info (&slot_cur);
	switch (slot_cur->bus_on->current_speed) {
	case BUS_SPEED_33:
		limitation = slot_cur->bus_on->slots_at_33_conv;
		break;
	case BUS_SPEED_66:
		if (slot_cur->bus_on->current_bus_mode == BUS_MODE_PCIX)
			limitation = slot_cur->bus_on->slots_at_66_pcix;
		else
			limitation = slot_cur->bus_on->slots_at_66_conv;
		break;
	case BUS_SPEED_100:
		limitation = slot_cur->bus_on->slots_at_100_pcix;
		break;
	case BUS_SPEED_133:
		limitation = slot_cur->bus_on->slots_at_133_pcix;
		break;
	}

	if ((count + 1) > limitation)
		return -EINVAL;
	return 0;
}

static inline void print_card_capability (struct slot *slot_cur)
{
	info ("capability of the card is ");
	if ((slot_cur->ext_status & CARD_INFO) == PCIX133) 
		info ("   133 MHz PCI-X \n");
	else if ((slot_cur->ext_status & CARD_INFO) == PCIX66)
		info ("    66 MHz PCI-X \n");
	else if ((slot_cur->ext_status & CARD_INFO) == PCI66)
		info ("    66 MHz PCI \n");
	else
		info ("    33 MHz PCI \n");

}

/* This routine will power on the slot, configure the device(s) and find the
 * drivers for them.
 * Parameters: hotplug_slot
 * Returns: 0 or failure codes
 */
static int enable_slot (struct hotplug_slot *hs)
{
	int rc, i, rcpr;
	struct slot *slot_cur;
	u8 function;
	u8 faulted = 0;
	struct pci_func *tmp_func;

	ibmphp_lock_operations ();

	debug ("ENABLING SLOT........ \n");
	slot_cur = (struct slot *) hs->private;

	if ((rc = validate (slot_cur, ENABLE))) {
		err ("validate function failed \n");
		attn_off (slot_cur);	/* need to turn off if was blinking b4 */
		attn_on (slot_cur);
		rc = slot_update (&slot_cur);
		if (rc) {
			ibmphp_unlock_operations();
			return rc;
		}
		ibmphp_update_slot_info (slot_cur);
                ibmphp_unlock_operations ();
		return rc;
	}

	attn_LED_blink (slot_cur);
	
	rc = set_bus (slot_cur);
	if (rc) {
		err ("was not able to set the bus \n");
		attn_off (slot_cur);
		attn_on (slot_cur);
		ibmphp_unlock_operations ();
		return -ENODEV;
	}

	/*-----------------debugging------------------------------*/
	get_cur_bus_info (&slot_cur);
	debug ("the current bus speed right after set_bus = %x \n", slot_cur->bus_on->current_speed); 
	/*----------------------------------------------------------*/

	rc = check_limitations (slot_cur);
	if (rc) {
		err ("Adding this card exceeds the limitations of this bus. \n");
		err ("(i.e., >1 133MHz cards running on same bus, or >2 66 PCI cards running on same bus \n. Try hot-adding into another bus \n");
		attn_off (slot_cur);
		attn_on (slot_cur);

		if (slot_update (&slot_cur)) {
			ibmphp_unlock_operations ();
			return -ENODEV;
		}
		ibmphp_update_slot_info (slot_cur);
		ibmphp_unlock_operations ();
		return -EINVAL;
	}

	rc = power_on (slot_cur);

	if (rc) {
		err ("something wrong when powering up... please see below for details\n");
		/* need to turn off before on, otherwise, blinking overwrites */
		attn_off(slot_cur);
		attn_on (slot_cur);
		if (slot_update (&slot_cur)) {
			attn_off (slot_cur);
			attn_on (slot_cur);
			ibmphp_unlock_operations ();
			return -ENODEV;
		}
		/* Check to see the error of why it failed */
		if ((SLOT_POWER (slot_cur->status)) && !(SLOT_PWRGD (slot_cur->status)))
			err ("power fault occured trying to power up \n");
		else if (SLOT_BUS_SPEED (slot_cur->status)) {
			err ("bus speed mismatch occured.  please check current bus speed and card capability \n");
			print_card_capability (slot_cur);
		} else if (SLOT_BUS_MODE (slot_cur->ext_status)) {
			err ("bus mode mismatch occured.  please check current bus mode and card capability \n");
			print_card_capability (slot_cur);
		}
		ibmphp_update_slot_info (slot_cur);
		ibmphp_unlock_operations ();
		return rc;
	}
	debug ("after power_on\n");
	/*-----------------------debugging---------------------------*/
	get_cur_bus_info (&slot_cur);
	debug ("the current bus speed right after power_on = %x \n", slot_cur->bus_on->current_speed);
	/*----------------------------------------------------------*/

	rc = slot_update (&slot_cur);
	if (rc) {
		attn_off (slot_cur);
		attn_on (slot_cur);
		rcpr = power_off (slot_cur);
		if (rcpr) {
			ibmphp_unlock_operations ();
			return rcpr;
		}
		ibmphp_unlock_operations ();
		return rc;
	}
	
	if (SLOT_POWER (slot_cur->status) && !(SLOT_PWRGD (slot_cur->status))) {
		faulted = 1;
		err ("power fault occured trying to power up... \n");
	} else if (SLOT_POWER (slot_cur->status) && (SLOT_BUS_SPEED (slot_cur->status))) {
		faulted = 1;
		err ("bus speed mismatch occured.  please check current bus speed and card capability \n");
		print_card_capability (slot_cur);
	} 
	/* Don't think this case will happen after above checks... but just in case, for paranoia sake */
	else if (!(SLOT_POWER (slot_cur->status))) {
		err ("power on failed... \n");
		faulted = 1;
	}
	if (faulted) {
		attn_off (slot_cur);	/* need to turn off b4 on */
		attn_on (slot_cur);
		rcpr = power_off (slot_cur);
		if (rcpr) {
			ibmphp_unlock_operations ();
			return rcpr;
		}
			
		if (slot_update (&slot_cur)) {                      
			ibmphp_unlock_operations ();	
			return -ENODEV;
		}
		ibmphp_update_slot_info (slot_cur);
		ibmphp_unlock_operations ();
		return -EINVAL;
	}

	slot_cur->func = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL);
	if (!slot_cur->func) { /* We cannot do update_slot_info here, since no memory for kmalloc n.e.ways, and update_slot_info allocates some */
		err ("out of system memory \n");
		attn_off (slot_cur);
		attn_on (slot_cur);
		rcpr = power_off (slot_cur);
		if (rcpr) {
			ibmphp_unlock_operations ();
			return rcpr;
		}
		ibmphp_unlock_operations ();
		return -ENOMEM;
	}
	memset (slot_cur->func, 0, sizeof (struct pci_func));
	slot_cur->func->busno = slot_cur->bus;
	slot_cur->func->device = slot_cur->device;
	for (i = 0; i < 4; i++)
		slot_cur->func->irq[i] = slot_cur->irq[i];

	debug ("b4 configure_card, slot_cur->bus = %x, slot_cur->device = %x\n", slot_cur->bus, slot_cur->device);

	if (ibmphp_configure_card (slot_cur->func, slot_cur->number)) {
		err ("configure_card was unsuccessful... \n");
		ibmphp_unconfigure_card (&slot_cur, 1); /* true because don't need to actually deallocate resources, just remove references */
		debug ("after unconfigure_card\n");
		slot_cur->func = NULL;
		attn_off (slot_cur);	/* need to turn off in case was blinking */
		attn_on (slot_cur);
		rcpr = power_off (slot_cur);
		if (rcpr) {
			ibmphp_unlock_operations ();
			return rcpr;
		}
		if (slot_update (&slot_cur)) {
			ibmphp_unlock_operations();
			return -ENODEV;
		}
		ibmphp_update_slot_info (slot_cur);
		ibmphp_unlock_operations ();
		return -ENOMEM;
	}
	function = 0x00;
	do {
		tmp_func = ibm_slot_find (slot_cur->bus, slot_cur->func->device, function++);
		if (tmp_func && !(tmp_func->dev))
			ibm_configure_device (tmp_func);
	} while (tmp_func);

	attn_off (slot_cur);
	if (slot_update (&slot_cur)) {
		ibmphp_unlock_operations ();
		return -EFAULT;
	}
	ibmphp_print_test ();
	rc = ibmphp_update_slot_info (slot_cur);
	ibmphp_unlock_operations(); 
	return rc;
}

/**************************************************************
* HOT REMOVING ADAPTER CARD                                   *
* INPUT: POINTER TO THE HOTPLUG SLOT STRUCTURE                *
* OUTPUT: SUCCESS 0 ; FAILURE: UNCONFIGURE , VALIDATE         *
          DISABLE POWER ,                                    *
**************************************************************/
int ibmphp_disable_slot (struct hotplug_slot *hotplug_slot)
{
	int rc;
	struct slot *slot_cur = (struct slot *) hotplug_slot->private;
	u8 flag;
	int parm = 0;

	debug ("DISABLING SLOT... \n"); 
		
	if (slot_cur == NULL) {
		ibmphp_unlock_operations (); 
		return -ENODEV;
	}
	
	if (slot_cur->ctrl == NULL) {
		ibmphp_unlock_operations ();
		return -ENODEV;
	}
	
	flag = slot_cur->flag;	/* to see if got here from polling */
	
	if (flag)
		ibmphp_lock_operations ();
	
	slot_cur->flag = TRUE;

	if (flag == TRUE) {
		rc = validate (slot_cur, DISABLE);	/* checking if powered off already & valid slot # */
		if (rc) {
			/*  Need to turn off if was blinking b4 */
			attn_off (slot_cur);
			attn_on (slot_cur);
			if (slot_update (&slot_cur)) {
				ibmphp_unlock_operations ();
				return -EFAULT;
			}
		
			ibmphp_update_slot_info (slot_cur);
			ibmphp_unlock_operations ();
			return rc;
		}
	}
	attn_LED_blink (slot_cur);

	if (slot_cur->func == NULL) {
		/* We need this for fncs's that were there on bootup */
		slot_cur->func = (struct pci_func *) kmalloc (sizeof (struct pci_func), GFP_KERNEL);
		if (!slot_cur->func) {
			err ("out of system memory \n");
			attn_off (slot_cur);
			attn_on (slot_cur);
			ibmphp_unlock_operations ();
			return -ENOMEM;
		}
		memset (slot_cur->func, 0, sizeof (struct pci_func));
		slot_cur->func->busno = slot_cur->bus;
		slot_cur->func->device = slot_cur->device;
	}

	if ((rc = ibm_unconfigure_device (slot_cur->func))) {
		err ("removing from kernel failed... \n");
		err ("Please check to see if it was statically linked or is in use otherwise. (perhaps the driver is not 'hot-removable')\n");
		attn_off (slot_cur);
		attn_on (slot_cur);
		ibmphp_unlock_operations ();
		return rc;
	}
        
	/* If we got here from latch suddenly opening on operating card or 
	a power fault, there's no power to the card, so cannot
	read from it to determine what resources it occupied.  This operation
	is forbidden anyhow.  The best we can do is remove it from kernel
	lists at least */

	if (!flag) {
		attn_off (slot_cur);
		return 0;
	}

	rc = ibmphp_unconfigure_card (&slot_cur, parm);
	slot_cur->func = NULL;
	debug ("in disable_slot. after unconfigure_card\n");
	if (rc) {
		err ("could not unconfigure card.\n");
		attn_off (slot_cur);	/* need to turn off if was blinking b4 */
		attn_on (slot_cur);

		if (slot_update (&slot_cur)) {
			ibmphp_unlock_operations ();
			return -EFAULT;
		}

		if (flag)
			ibmphp_update_slot_info (slot_cur);
		ibmphp_unlock_operations ();
		return -EFAULT;
	}

	rc = ibmphp_hpc_writeslot (hotplug_slot->private, HPC_SLOT_OFF);
	if (rc) {
		attn_off (slot_cur);
		attn_on (slot_cur);
		if (slot_update (&slot_cur)) {
			ibmphp_unlock_operations ();
			return -EFAULT;
		}

		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;
	}
	rc = ibmphp_update_slot_info (slot_cur);
	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 =		get_max_bus_speed,
	.get_cur_bus_speed =		get_cur_bus_speed,
/*	.get_max_adapter_speed =	get_max_adapter_speed,
	.get_bus_name_status =		get_bus_name,
*/
};

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)
{
	struct pci_bus *bus;
	int i = 0;
	int rc = 0;

	init_flag = 1;

	info (DRIVER_DESC " version: " DRIVER_VERSION "\n");

	ibmphp_pci_bus = kmalloc (sizeof (*ibmphp_pci_bus), GFP_KERNEL);
	if (!ibmphp_pci_bus) {
		err ("out of memory\n");
		return -ENOMEM;
	}

	bus = find_bus (0);
	if (!bus) {
		err ("Can't find the root pci bus, can not continue\n");
		return -ENODEV;
	}
	memcpy (ibmphp_pci_bus, bus, sizeof (*ibmphp_pci_bus));

	ibmphp_debug = debug;

	ibmphp_hpc_initvars ();

	for (i = 0; i < 16; i++)
		irqs[i] = 0;

	if ((rc = ibmphp_access_ebda ())) {
		ibmphp_unload ();
		return rc;
	}
	debug ("after ibmphp_access_ebda ()\n");

	if ((rc = ibmphp_rsrc_init ())) {
		ibmphp_unload ();
		return rc;
	}
	debug ("AFTER Resource & EBDA INITIALIZATIONS\n");

	max_slots = get_max_slots ();
	
	if ((rc = ibmphp_register_pci ())) {
		ibmphp_unload ();
		return rc;
	}

	if (init_ops ()) {
		ibmphp_unload ();
		return -ENODEV;
	}
	ibmphp_print_test ();
	if ((rc = ibmphp_hpc_start_poll_thread ())) {
		ibmphp_unload ();
		return -ENODEV;
	}

	/* if no NVRAM module selected, lock ourselves into memory with a 
	 * module count of -1 so that no one can unload us. */
	MOD_DEC_USE_COUNT;
	return 0;
}

static void __exit ibmphp_exit (void)
{
	ibmphp_hpc_stop_poll_thread ();
	debug ("after polling\n");
	ibmphp_unload ();
	debug ("done\n");
}

module_init (ibmphp_init);
module_exit (ibmphp_exit);
