/*
 * Emulex LP6000/8000 SCSI-FCP
 * Written by Charles R. Anderson <cra@wpi.edu>,
 *            Russ Savela <savela@wpi.edu>,
 *            Frank Sweetser <rasmusin@wpi.edu>,
 *            John V. Baboval <jbaboval@wpi.edu,
 *            Josh Huber <huberj@wpi.edu>.
 *
 * Based on QLogic ISP1020 SCSI-FCP driver by Erik H. Moe, ehm@cris.com,
 * Renamed and updated to 1.3.x by Michael Griffith <grif@cs.ucr.edu>,
 * Modified by Chris Loveland <cwl@iol.unh.edu> to support the ISP2100.
 *
 * Also includes portions from the aic7xxx driver by John Aycock,
 * Daniel M. Eischen (deischen@iworks.InterWorks.org), and Justin T. Gibbs.
 *
 * Copyright (c) 1998-9 Charles R. Anderson, Russ Savela, and Frank Sweetser
 * Copyright (c) 1999 Josh Huber and John Baboval
 * Portions Copyright (c) 1995 Erik H. Moe
 * Portions Copyright (c) 1994 John Aycock
 * Portions Copyright (c) 1994-1997 Justin Gibbs.
 * 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, 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.  See the GNU
 * General Public License for more details. */

/*
  $Id: emulexfc.c,v 1.23 2000/04/27 11:31:20 huberj Exp $

  $Log: emulexfc.c,v $
  Revision 1.23  2000/04/27 11:31:20  huberj
  added readme file

  Revision 1.22  2000/02/07 00:27:42  jbaboval
  IOCB working properly.
  Remote N-Port Login being sent, but response not yet processed.
  Working on Interrupt handler to do just that.

  Revision 1.21  2000/02/05 19:47:32  jbaboval

  Not much changed yet...

  Revision 1.20  1999/11/23 20:31:55  jbaboval
  Added more SCSI commands.. Still IOCB probs.

  Revision 1.19  1999/11/22 21:18:28  jbaboval
  Beginings of SCSI command implementation. Bad IOCB ring command code apparently.

  Revision 1.18  1999/11/17 21:12:03  jbaboval
  Done with basic card setup and card operations. Last commit before begining block driver coding.

  Revision 1.17  1999/11/15 00:02:22  huberj
  Reset should be working properly on both the 8000 and the 6000
  The SLI1/2 detection code doesn't seem to be functioning properly...

  Revision 1.16  1999/11/14 21:25:20  jbaboval
  started to set up SLI-2 way of doing things.
  added a SLI-2 detection section in the mbox_read_rev function, as
  well as adding the proper output for SLI version informaion.

  Revision 1.15  1999/11/14 17:44:58  jbaboval
  Both LP6000 & LP8000 reset cleanly (No MBOX hacks necissary)
  Fixed Parse error in mbox_read_nvram()

  Revision 1.14  1999/11/14 06:16:30  huberj
  hey, neat things done here.
  Got the READ_REV command to output useful info.
  General code cleanup (mostly reset/init stuff)

  Revision 1.13  1999/11/13 19:07:22  jbaboval
  CARD RESET WORKS!!

  No more rebooting! Happy day!

  Revision 1.12  1999/11/13 18:25:15  jbaboval
  last commit before new documentation.

  Revision 1.11  1999/11/09 21:55:03  huberj
  fixed cvs log tag

*/

/*
 * Describe all debugging code!
 */


#if defined(MODULE)
#include <linux/module.h>
#endif

#include <asm/io.h>
#include <asm/irq.h>
#include <asm/spinlock.h>
#include <linux/unistd.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/smp.h>
#include <linux/init.h>         /* for __initdata and __initfunc */
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/sched.h>
#include <linux/malloc.h>       /* for kmalloc() */
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/blk.h>
#include <linux/interrupt.h>

#include "sd.h"
#include "hosts.h"
#include "emulexfc.h"

/* shouldn't these be in pci.h?  every other pci device's ids are... */
#if !defined(PCI_VENDOR_ID_EMULEX)
#define PCI_VENDOR_ID_EMULEX            0x10df
#define PCI_DEVICE_ID_EMULEX_LP6000     0x1ae5
#define PCI_DEVICE_ID_EMULEX_LP8000     0xf800
#endif

/* Configuration section *****************************************************/

/* Set the following macro to 1 to reload the LP6000's firmware. */
#define RELOAD_FIRMWARE		0

/*  Macros used for debugging */
#define DEBUG_LP6000		1
/* #define DEBUG_LP6000_INTR	1 */
#define DEBUG_LP6000_SETUP	1 
#define DEBUG_LP6000_INIT       1
#define DEBUG_LP6000_MBOX       1
#define DEBUG_CONFIG_RING       0
/* #define TRACE_LP		1 */
#define DEFAULT_LOOP_COUNT	1000000
#define RUSS_DEBUG 1


/* End Configuration section *************************************************/


int fabric = 0;  /* this has to be zero.  Set with MODULE_PARAM(fabric, "i") */

#if TRACE_LP
# define TRACE_BUF_LEN	(32*1024)

struct {
	u_long		next;
	struct {
		u_long		time;
		u_int		index;
		u_int		addr;
		u_char *	name;
	} buf[TRACE_BUF_LEN];
} trace;
#define TRACE(w, i, a)						\
{								\
	unsigned long flags;					\
        spinlock_t lp6k_trace_lock = SPIN_LOCK_UNLOCKED         \
                                                                \
	spin_lock_irqsave(&lp6k_trace_lock, flags);		\
	trace.buf[trace.next].name  = (w);			\
	trace.buf[trace.next].time  = jiffies;			\
	trace.buf[trace.next].index = (i);			\
	trace.buf[trace.next].addr  = (long) (a);		\
	trace.next = (trace.next + 1) & (TRACE_BUF_LEN - 1);	\
	spin_unlock_irqrestore(&lp6k_trace_lock, flags)		\
}
#else
# define TRACE(w, i, a)
#endif

#if DEBUG_LP6000
#define ENTER(x)	printk(KERN_INFO "emulexfc: entering %s()\n", x);
#define LEAVE(x)	printk(KERN_INFO "emulexfc: leaving %s()\n", x);
#define DEBUG(x)	x
#else
#define ENTER(x)
#define LEAVE(x)
#define DEBUG(x)
#endif /* DEBUG_LP6000 */

#if DEBUG_LP6000_INTR
#define ENTER_INTR(x)	printk(KERN_INFO "emulexfc: entering %s()\n", x);
#define LEAVE_INTR(x)	printk(KERN_INFO "emulexfc: leaving %s()\n", x);
#define DEBUG_INTR(x)	x
#else
#define ENTER_INTR(x)
#define LEAVE_INTR(x)
#define DEBUG_INTR(x)
#endif /* DEBUG LP6000_INTR */

/* [frank] do these queue length functions actually apply at all
 * to this card?  IIRC, the SLIM has a queue deptch of 2048 entries,
 * so we should be able to present a fairly deep queue depth */

/* queue length's _must_ be power of two: */
#define QUEUE_DEPTH(in, out, ql)	((in - out) & (ql))
#define REQ_QUEUE_DEPTH(in, out)	QUEUE_DEPTH(in, out, 		     \
						    EMULEXFC_REQ_QUEUE_LEN)
#define RES_QUEUE_DEPTH(in, out)	QUEUE_DEPTH(in, out, RES_QUEUE_LEN)

static void	lp6000_enable_irqs(struct lp6000_hostdata *);
static void	lp6000_disable_irqs(struct lp6000_hostdata *);
static int      lp6000_register(Scsi_Host_Template *, struct lp6000_hostdata *, int unit);
static int      lp6000_pci_init(struct pci_dev *, struct lp6000_hostdata *);
static int      lp6000_init(struct lp6000_hostdata *);

int init_login(struct lp6000_hostdata* instance, volatile void* buffer, lp6000_bde* bde, struct lp6000_iocb *login_iocb, struct lp6000_iocb *reply_iocb);
int initialize_link(struct lp6000_hostdata* instance, volatile void* buffer);
int configure_link(struct lp6000_hostdata* instance);
int configure_rings(struct lp6000_hostdata* instance);
int configure_port(struct lp6000_hostdata *instance);
int partition_slim(struct lp6000_hostdata* instance);
int firefly_read_nvram(struct lp6000_hostdata* instance);
int firefly_load_firmware(struct lp6000_hostdata* instance);
int firefly_chipset_init(struct lp6000_hostdata *instance);
int enable_interrupts(struct lp6000_hostdata *instance);

/* functions for each mailbox command */
int mbox_read_nvparms(struct lp6000_hostdata *, struct mbox *);
int mbox_read_rconfig(struct lp6000_hostdata *, struct mbox *, int);
int mbox_reset_ring(struct lp6000_hostdata *, struct mbox *, int);
int mbox_restart(struct lp6000_hostdata *, struct mbox *);
int mbox_read_rev(struct lp6000_hostdata *, struct mbox *);
int mbox_config_port(struct lp6000_hostdata *instance, struct mbox *mailbox);
int get_biu_vers(struct lp6000_hostdata *instance);
void print_sli2_vers(u32 reg);
void dump_resp_rings(struct lp6000_hostdata *instance);
void dump_cmnd_rings(struct lp6000_hostdata *instance);

#ifdef MODULE
static int      lp6000_unregister(struct lp6000_hostdata *);
#endif
static int	lp6000_mbox_command(struct lp6000_hostdata *, struct mbox *); 
static int      lp6000_iocb_command(struct lp6000_hostdata *, 
                                    struct lp6000_iocb *,
                                    unsigned int);
static int      lp6000_iocb_getrsp(struct lp6000_hostdata *,
                                   struct lp6000_iocb *,
                                   unsigned int ring);
static void	lp6000_intr_handler(int, void *, struct pt_regs *);
static void	do_lp6000_intr_handler(int, void *, struct pt_regs *);
static void     lp6000_do_bh_queue(void *);

#if USE_NVRAM_DEFAULTS
static int	lp6000_get_defaults(struct Scsi_Host *);
static int	lp6000_verify_nvram(struct Scsi_Host *);
static u_short	lp6000_read_nvram_word(struct Scsi_Host *, u_short);
#endif

#if DEBUG_LP6000
static void	lp6000_print_scsi_cmd(Scsi_Cmnd *);
#endif
#if DEBUG_LP6000_INTR
static void	lp6000_print_status_entry(struct Status_Entry *);
#endif

/*
 * /proc directory entry
 */
static struct proc_dir_entry proc_scsi_lp6000 = {
	PROC_SCSI_EMULEXFC, 6, "lp6000",
	S_IFDIR | S_IRUGO | S_IXUGO, 2
};

/* enable interrupts only on link, errors, and iocb rings 0-3 */

#define INTERRUPT_MASK  0xD0000001

/* these functions don't lock the card - the calling functions 
 * have to know what they're doing anyways */

static inline void lp6000_enable_irqs(struct lp6000_hostdata *instance)
{
        OUTL(CTRL_REG, INTERRUPT_MASK);
}


static inline void lp6000_disable_irqs(struct lp6000_hostdata *instance)
{
        OUTL(CTRL_REG, INTERRUPT_MASK);
}

/*+F*************************************************************************
 * Function:
 *   lp6000_setup
 *
 * Description:
 *   Handle Linux boot parameters
 *-F*************************************************************************/


/*+F*************************************************************************
 * Function:
 *   lp6000_detect
 *
 * Description:
 *   Detect and register all Emulex LP6000 hosts.
 *   Called by SCSI mid-level driver upon module load.
 *-F*************************************************************************/
__initfunc(
int
lp6000_detect(Scsi_Host_Template *template)
)
{
	int found = 0, registered = 0, i;
	struct lp6000_hostdata *first_hostdata = NULL,
                               *hostdata = NULL,
                               *next_hostdata = NULL;
	struct pci_dev *pdev = NULL;

	ENTER("lp6000_detect");

	template->proc_dir = &proc_scsi_lp6000;

	if (pci_present() == 0) {
		printk(KERN_ERR "emulexfc: PCI not present\n");
		return 0;
	}

        /*
         * Search for all Emulex LP6000/8000 PCI cards and put them
         * in a linked list of hostdata structs
         */

        /* first, find the LP8000's */
	while ((pdev = pci_find_device(PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_EMULEX_LP8000, pdev))) {
		if ( (hostdata = kmalloc(sizeof(struct lp6000_hostdata), GFP_ATOMIC)) != NULL) {
                        memset(hostdata, 0, sizeof(struct lp6000_hostdata));
                        
                        hostdata->cmnd_queue = NULL;
                        hostdata->queue_lock = SPIN_LOCK_UNLOCKED;
                        hostdata->sli = 1;

                        /*
                         * Initialize PCI resources
                         */
                        if (lp6000_pci_init(pdev, hostdata) == 0) {
                                /*
                                 * link this device into the list of hosts found so far
                                 */
                                if (first_hostdata == NULL)
                                        first_hostdata = hostdata;
                                else {
                                        next_hostdata = first_hostdata;
                                        while(next_hostdata->next != NULL)
                                                next_hostdata = next_hostdata->next;
                                        next_hostdata->next = hostdata;
                                        next_hostdata->next->next = NULL;
                                }
                                found++;
                        }
                } /* Found an Emulex PCI device. */
                else /* Well, we found one, but we couldn't get any memory. */
                {
                        printk(KERN_INFO "emulexfc: Found lp8000\n");
                        printk(KERN_ERR "emulexfc: Unable to allocate device memory, skipping.\n");
                }
        } /* end while pci_find_device */

        pdev = NULL;

        /* next, the LP6000's */
	while ((pdev = pci_find_device(PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_EMULEX_LP6000, pdev))) {
		if ( (hostdata = kmalloc(sizeof(struct lp6000_hostdata), GFP_ATOMIC)) != NULL) {
                        memset(hostdata, 0, sizeof(struct lp6000_hostdata));
                        
                        hostdata->cmnd_queue = NULL;
                        hostdata->queue_lock = SPIN_LOCK_UNLOCKED;
                        hostdata->sli = 1;
                        for(i = 0; i < MAX_TARGETS; i++)
                                hostdata->rpi[i] = 0;

                        /*
                         * Initialize PCI resources
                         */
                        if (lp6000_pci_init(pdev, hostdata) == 0) {
                                /*
                                 * link this device into the list of hosts found so far
                                 */
                                if (first_hostdata == NULL)
                                        first_hostdata = hostdata;
                                else {
                                        next_hostdata = first_hostdata;
                                        while(next_hostdata->next != NULL)
                                                next_hostdata = next_hostdata->next;
                                        next_hostdata->next = hostdata;
                                        next_hostdata->next->next = NULL;
                                }
                                found++;
                        }
                } /* Found an Emulex PCI device. */
                else /* Well, we found one, but we couldn't get any memory. */
                {
                        printk(KERN_INFO "emulexfc: Found lp6000\n");
                        printk(KERN_ERR "emulexfc: Unable to allocate device memory, skipping.\n");
                }
        } /* end while pci_find_device */

#ifdef DEBUG_LP6000_SETUP
        printk(KERN_INFO "emulexfc: Found %d lp6/8000 cards\n", found);
#endif

        /*
         * Register the devices we found with the kernel,
         * freeing the linked list as we go.
         */
        hostdata = first_hostdata;
        while(hostdata != NULL) {
                if (lp6000_register(template, hostdata, registered) == 0)
                        registered++;
                
                next_hostdata = hostdata->next;
                kfree(hostdata);
                hostdata = next_hostdata;
        }

#ifdef DEBUG_LP6000_SETUP
        printk(KERN_INFO "emulexfc: Registered %d lp6/8000 hosts\n", registered);
#endif

	LEAVE("lp6000_detect");

        return registered;
}

__initfunc(
static int lp6000_pci_init(struct pci_dev *pdev, struct lp6000_hostdata *hostdata)
)
{
        /* unsigned short command; */

#ifdef DEBUG_LP6000_SETUP
        printk(KERN_INFO "emulexfc: lp6000 at PCI %d/%d\n",
               PCI_SLOT(pdev->devfn),
               PCI_FUNC(pdev->devfn));
#endif

        /* Record vital PCI information  */

        hostdata->pci_dev = pdev;
        hostdata->irq = pdev->irq;
        /* SLIM base address is 64-bits returned in indexes 0 and 1,
           but this is only a 32-bit PCI card.  How???  Will ignore
           high-order 32-bits for now as they are 0's anyway... FIXME */
        hostdata->slim_base = (pdev->base_address[0] & PCI_BASE_ADDRESS_MEM_MASK);
        hostdata->reg_base = (pdev->base_address[2] & PCI_BASE_ADDRESS_MEM_MASK);
        hostdata->io_port = (pdev->base_address[4] & PCI_BASE_ADDRESS_IO_MASK);

        /* Check/set PCI capabilities */
#if 0
        pci_read_config_word(pdev, PCI_COMMAND, &command);

#ifdef DEBUG_LP6000_SETUP
        printk(KERN_INFO "emulexfc: Initial PCI_COMMAND value was 0x%x\n",
               (int)command);
#endif
#endif
        pci_set_master(pdev);

#if 0
        /* is the below even neccessary, with the
         * pci_set_master above? */        
        command |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY | PCI_COMMAND_IO;
        pci_write_config_word(pdev, PCI_COMMAND, command);
#endif        

        /* Remap PCI memory */
        hostdata->slim_base = (unsigned long) ioremap(hostdata->slim_base, SLIM_SIZE);      /* SLIM memory */
        hostdata->reg_base = (unsigned long) ioremap_nocache(hostdata->reg_base, REG_SIZE); /* Memory-mapped registers */

        /* if reg_base is really the registers memory-mapped, we have 2 alternatives:
           use either I/O mapped or memory-mapped I/O.  I/O mapped I/O uses
           inb, outb, etc. and accesses the ISA ioport 64K space.  Memory-mapped I/O
           uses readb, writeb, etc. and accesses the ioremapped PCI memory space directly,
           which is faster.  All this pertains to only register accesses, of course.  The
           SLIM is always memory mapped.  */

        return 0;
}
        
/*+F*************************************************************************
 * Function:
 *   lp6000_register
 *
 * Description:
 *   Register an Emulex LP6000 SCSI controller with the kernel.
 *-F*************************************************************************/
__initfunc(
static int
lp6000_register(Scsi_Host_Template *template, struct lp6000_hostdata *hd, int unit)
)
{
	struct Scsi_Host *instance = NULL;
        struct lp6000_hostdata *reg_hd;
        int status;

        ENTER("lp6000_register");
        
        /*
         * Allocate a storage area by registering us with the mid-level
         * SCSI layer.
         */
        instance = scsi_register(template, sizeof(struct lp6000_hostdata));

        /* Initialize instance's hostdata and copy passed in hostdata to it */
        if (instance != NULL) {
                reg_hd = (struct lp6000_hostdata *) instance->hostdata;
                memset(reg_hd, 0, sizeof(struct lp6000_hostdata));
                *reg_hd = *hd;
                reg_hd->host = instance;
                reg_hd->unit = unit;

                instance->max_id = MAX_TARGETS;
                instance->max_lun = MAX_LUNS;
                instance->base = (unsigned char *) hd->slim_base;
                instance->io_port = hd->io_port;
                instance->n_io_port = REG_SIZE;
                instance->irq = hd->irq;
                instance->unique_id = unit;
        } else {
                return -1;
        }
        
        /* Check I/O region */
        if (check_region(instance->io_port, REG_SIZE)) {
                printk(KERN_ERR "emulexfc: I/O region 0x%lx-0x%lx already in use\n",
                       instance->io_port, instance->io_port + REG_SIZE);
                return -1;
        }
        
        /* Lock out other contenders for our i/o space. */
        request_region(instance->io_port, REG_SIZE, "emulexfc");

        /* Check/get interrupt request line */
        if (request_irq(instance->irq, do_lp6000_intr_handler, SA_INTERRUPT | SA_SHIRQ,
                        "emulexfc", instance->hostdata))
        {
                printk(KERN_ERR "emulexfc: interrupt %d already in use\n",
                       instance->irq);
                return -1;
        }

        /* ok, now that we have verified and acquired our irq and io resources,
           print it out */
        printk(KERN_INFO "emulexfc-%d: using irq %d, io 0x%lx-0x%lx, memory 0x%lx-0x%lx, reg mem 0x%lx-0x%lx\n",
               unit, reg_hd->irq, reg_hd->io_port, reg_hd->io_port + REG_SIZE,
               reg_hd->slim_base, reg_hd->slim_base + SLIM_SIZE,
               reg_hd->reg_base, reg_hd->reg_base + REG_SIZE);

        printk(KERN_INFO "(scsi%d) <LP6000> found on PCI bus %d slot %d function %d\n",
               instance->host_no, reg_hd->pci_dev->bus->number,
               PCI_SLOT(reg_hd->pci_dev->devfn),
               PCI_FUNC(reg_hd->pci_dev->devfn));

        /* Here is where we do our FireFly chipset initialization */
        status = lp6000_init(reg_hd);

	LEAVE("lp6000_register");

	return status;
}

/*+F*************************************************************************
 * Function:
 *   lp6000_release
 *
 * Description:
 *   Free the passed in Scsi_Host memory structures.
 *   Called by SCSI mid-level driver prior to unloading the module.
 *-F*************************************************************************/

#ifdef MODULE
int
lp6000_release(struct Scsi_Host *host)
{
	struct lp6000_hostdata *hostdata = (struct lp6000_hostdata *) host->hostdata;

	ENTER("lp6000_release");

#ifdef DEBUG_LP6000_SETUP
        printk(KERN_INFO "emulexfc: Released scsi host %d\n", hostdata->host->host_no);
#endif

        lp6000_unregister(hostdata);

        iounmap((void *) hostdata->slim_base);
        iounmap((void *) hostdata->reg_base);

	LEAVE("lp6000_release");

	return 1;
}
#endif

#ifdef MODULE
static int
lp6000_unregister(struct lp6000_hostdata *hostdata)
{
        struct mbox mailbox;

        ENTER("lp6000_unregister");

	/* Turn off interrupts here, shutdown FireFly, etc. */

/* shutdown link :) */

        mailbox.cmd = MBOX_DOWN_LINK;
        mailbox.wsize = 32;
        mailbox.rsize = 32;
        memset(mailbox.params, 0, 32*4);
        
        lp6000_mbox_command(hostdata, &mailbox);
        if (mailbox.status != 0) {
                printk("FAILED TO BRING DOWN LINK!\n");
        }
        else
        {
                printk("emulexfc: Link down.\n");
        }

        /* Free our IRQ, memory, and I/O resources */
        if(hostdata->irq)
                free_irq(hostdata->irq, hostdata);
	release_region(hostdata->io_port, REG_SIZE);


        LEAVE("lp6000_unregister");

        return 0;
}
#endif

/****************************************************************************
 * Function:
 *    lp6000_init
 *
 * Description:
 *    Initialize chipset for CSH and FCP ULPs.
 * 
 ****************************************************************************/

/* TODO:  clean up the error handling, in particular, freeing of
 * memory when something blows up */

int 
lp6000_init(struct lp6000_hostdata *instance)
{
        volatile void *buffer = NULL; 
        lp6000_bde *bde = NULL;
        struct lp6000_iocb *login_iocb = NULL, *reply_iocb = NULL;
        int retval = 0;


        /* TODO: Remove the evil, nasty, unnecissary, lazy ass goto's! */

        ENTER("lp6000_init");

        retval = firefly_chipset_init(instance);
        if(retval)
        {
                printk("emulexfc: firefly_chipset_init() failed\n");
                goto INIT_ERROR;
        }

        retval = firefly_load_firmware(instance);
        if(retval)
        {
                printk("emulexfc: firefly_load_firmware() failed\n");
                goto INIT_ERROR;
        }

        retval = firefly_read_nvram(instance);
        if(retval)
        {
                printk("emulexfc: firefly_read_nvram() failed\n");
                goto INIT_ERROR;
        }

        /* configure the port */
        retval = partition_slim(instance);
        if(retval < 0)
        {
                printk("emulexfc: partition_slim() failed: %d\n", retval);
                goto INIT_ERROR;
        }
        
        retval = configure_rings(instance);
        if(retval)
        {
                printk("emulexfc: configure_rings() failed\n");
                goto INIT_ERROR;
        }

        retval = configure_link(instance);
        if(retval)
        {
                printk("emulexfc: configure_link() failed\n");
        }
//        retval = enable_interrupts(instance);
        if(retval)
        {
                printk("emulexfc: enable_interrupts() failed\n");
                goto INIT_ERROR;
        }

        retval = initialize_link(instance, buffer);
        if(retval)
        {
                printk("emulexfc: initialize_link() failed\n");
                goto INIT_ERROR;
        }

        retval = init_login(instance, buffer, bde, login_iocb, reply_iocb);
        if(retval)
        {
                printk("emulexfc: init_login() failed\n");
                goto INIT_ERROR;
        }

        goto DONE;
        INIT_ERROR:
        /* cleanup stuff */

        /* lets keep in mind the following remark when we wonder why things are broken -John */

        /* [russ] let's be a little more casual about
         * cleaning up memory since:
         * (A) who gives a shit if we just crashed 
         * and
         * (B) if it works, I'll still have something to fix
         */
#if 0
        if(buffer)
                kfree(buffer);

        bde = (lp6000_bde *)login_iocb;
        if(bde->addr_low)
                kfree(bus_to_virt(bde->addr_low));
        bde += sizeof(lp6000_bde);
        if(bde->addr_low)
                kfree(bus_to_virt(bde->addr_low));

        if(login_iocb)
                kfree(login_iocb);
        
        if(reply_iocb)
                kfree(reply_iocb);

#endif
 DONE:
        /* spin_unlock_irqrestore(&instance->spin_lock, flags); */
        return(retval);
}


/**************************************************************************
 * Function: firefly_chipset_init
 *
 * Description: lp6000 initilization
 *    STEP 1
 *    FireFly Chipset Initialization
 ***************************************************************************/

int firefly_chipset_init(struct lp6000_hostdata *instance)
{

        int i;
        int retval = 0;
        unsigned int init = 0;
        u32 stat_reg = 0;
        struct mbox mailbox;

#if DEBUG_LP6000_INIT
        lp6000_dumpwords((void *) instance->reg_base, 4, DUMP_MMIO);
        printk(KERN_INFO "emulexfc: Setting INITFF\n");
#endif

        /* set the IP (initialize port) bit in the control register */
        OUTL(CTRL_REG, CTRL_INITFF);

        printk("ctl register: %08x\n", INL(CTRL_REG));

        /* clear the IP bit in the control register if BIU V1 in LP6000 */
        if((instance->pci_dev->device == PCI_DEVICE_ID_EMULEX_LP6000) &&
           (get_biu_vers(instance) == 1)) {
                printk("emulexfc: clearing IP bit (BIU V1 && LP6000)\n");
                OUTL(CTRL_REG, INL(CTRL_REG) & ~CTRL_INITFF);
        }

        printk("checking for STATUS_MB\n");

        /* check status register 4 times in one second */
        i = 0;
        while ((INL(STATUS_REG) & STATUS_MB) == 0) {
#if DEBUG_LP6000_INIT
                lp6000_dumpwords((void *)instance->reg_base, 4, DUMP_MMIO);
                printk(KERN_INFO "emulexfc: status reg = %08x\n", INL(STATUS_REG));  
#endif
                if (i++ == 8) {
                        printk("status reg = %08x\n", INL(STATUS_REG));
                        printk(KERN_ERR "emulexfc: timed out waiting for MBOX ready status\n");
                        retval = -1;
                        return(retval);
                }
                mdelay(250);
        }
        
        printk("emulexfc: mbox_restart returned 0x%08x\n", mbox_restart(instance, &mailbox));
        printk("status register: %08x\n", INL(STATUS_REG));
        
        printk("checking for STATUS_RDY\n");

        /* check status register 4 times in one second */
        i = 0;
        while ((INL(STATUS_REG) & STATUS_RDY) == 0) {
#if DEBUG_LP6000_INIT
                lp6000_dumpwords((void *)instance->reg_base, 4, DUMP_MMIO);
                printk(KERN_INFO "emulexfc: status reg = %08x\n", INL(STATUS_REG));  
#endif
                /* 5 second timeout max for copper medium,
                   for fiber-optic connections, a timeout of 15 seconds would be
                   required. Need someone to test this for us
                */
                if (i++ == 4) {
                        printk("status reg = %08x\n", INL(STATUS_REG));
                        printk(KERN_ERR "emulexfc: timed out waiting for FireFly ready status\n");
                        retval = -1;
                        return(retval);
                }
                mdelay(250);
        }

        printk("emulexfc: card reset successful\n");

        /* check for errors */
        stat_reg = INL(STATUS_REG);

        if((stat_reg & 0xFF000000) != 0) {
                if(stat_reg & STATUS_ER1) {
                        printk("emulexfc: FireFly RBUS Parity Error\n");
                }

                if(stat_reg & STATUS_ER2) {
                        printk("emulexfc: FireFly IBUS Parity Error\n");
                }
                
                if(stat_reg & STATUS_ER3) {
                        printk("emulexfc: Host Bus error (PCI/SBUS)\n");
                        /* if we get this error, then we have to check
                           another register for the specific bus error.
                           make sure we do this later! */
                }
                
                if(stat_reg & STATUS_ER4) {
                        printk("emulexfc: Internal Sequence Manager Error\n");
                }
                
                if(stat_reg & STATUS_ER5) {
                        printk("emulexfc: Internal BIU Error\n");
                }
                
                if(stat_reg & STATUS_ER6) {
                        printk("emulexfc: Internal ENDIC+ Error\n");
                }
                if(stat_reg & STATUS_ER7) {
                        printk("emulexfc: Internal Context SRAM Error\n");
                }
                
                if(stat_reg & STATUS_ER8) {
                        printk("emulexfc: Internal Buffer SRAM Error\n");
                }
                return -1;
        }

        printk(KERN_INFO "emulexfc: success with %08x\n", init);
        
        /* Configure Interrupt Sources */
        
        /* when we start working on interrupts, we'll probably want to use
           interrupts for everything...mmmm...cookies
        */

        /* Clear control and host attention registers
           
           notes about clearing host attn. register:
           set bit = 1 to clear for this register
           we CLEAR (set 1) all bits, including reserved ones
           because we don't want to enable (as yet) unspecified behavior.
           This could happen because the contenents of this register
           are undefined after POST (power-on self-test)
        */
        OUTL(CTRL_REG, 0);
        OUTL(HOST_ATT_REG, 0xFFFFFFFF);

        /* Set any interrupt bits we want */

        printk("HSTCTL Bits 0x%08x\n", INL(CTRL_REG));
        printk("HSTATT bits 0x%08x\n", INL(HOST_ATT_REG));

        printk("HSTSTAT: 0x%08x\n", INL(STATUS_REG));

        return(0);
} /* end: firefly_chipset_init */

/*************************************************************************
 * Function: firefly_load_firmware 
 *
 * Description: lp6000 init
 *    STEP 2
 *    Load new RISC Firmware
 *************************************************************************/

int firefly_load_firmware(struct lp6000_hostdata* instance)
{

        /* loading firmware is not recommended by Emulex at this time */
        return(0);
}

/*************************************************************************
 * Function: firefly_read_nvram
 *
 * Description:lp6000 init
 *    STEP 2.5
 *    Read NVRAM Parameters -- must be done before PART_SLIM
 *************************************************************************/

int firefly_read_nvram(struct lp6000_hostdata* instance)
{
        struct mbox mailbox;

        if(mbox_read_nvparms(instance, &mailbox) != 0) {
                return -1;
        }
        
        if(mbox_read_rev(instance, &mailbox) != 0) {
                return -1;
        }
        
        return(0);
}
#define ENTRIES_PER_RING  50

/*****************************************************************************
 * Function: configure_port 
 *
 * Description: lp6000 init
 *    STEP 3
 *    Sets up IOCB rings, and sets card in SLI-2 (host side IOCB) mode.
 *****************************************************************************/

int configure_port(struct lp6000_hostdata *instance) 
{
        /* allocate memory for the hostside mailbox and PCB

           FIXME: Not freed anywhere yet. */

        /* I'm allocating this memory all at once and then dividing it up here
           so as to not waste two pages of kernel memory. This means that when you
           free the mailbox, the PCB will be freed too! Don't do this until we unload! */

        struct mbox mailbox;

        instance->mailbox = kmalloc(64*4, (GFP_KERNEL | GFP_DMA));
        instance->pcb = kmalloc(24*4, (GFP_KERNEL | GFP_DMA));
        memset(instance->mailbox, 0, 64*4);
        memset(instance->pcb, 0, 24*4);
        
        /* allocate hostside IOCB rings */

        instance->iocb_cmnd = kmalloc(1920, (GFP_KERNEL | GFP_DMA));
        instance->iocb_resp = kmalloc(1920, (GFP_KERNEL | GFP_DMA));

        /* Set up PCB */

        instance->pcb[0] = (0x01 << 24); /* Port type: SLI-2 w/SLI-1 compat. */
        instance->pcb[0] = (instance->pcb[0] | (0x03 << 16)); /* Feature level: Full SLI-2 */
        instance->pcb[0] = (instance->pcb[0] | 0x02); /* Max IOCB ring number */
        instance->pcb[1] = 64; /* #of Words in Hostside Mailbox */
        instance->pcb[2] = virt_to_bus((void *)instance->mailbox); /* Physical address of hostside mailbox */
        *(instance->pcb+16) = virt_to_bus((void *)instance->iocb_cmnd); /* Physical address of host IOCB */
        *(instance->pcb+24) = virt_to_bus((void *)instance->iocb_resp); /* Physical address of port IOCB */
        instance->pcb[8] = ENTRIES_PER_RING;
        *(instance->pcb+36) = virt_to_bus((void *)instance->iocb_cmnd);
        instance->pcb[11] = ENTRIES_PER_RING;
        *(instance->pcb+48) = virt_to_bus((void *)instance->iocb_resp);
        instance->pcb[14] = ENTRIES_PER_RING;
        *(instance->pcb+60) = virt_to_bus((void *)instance->iocb_cmnd+(ENTRIES_PER_RING*8*4));
        instance->pcb[17] = ENTRIES_PER_RING;
        *(instance->pcb+72) = virt_to_bus((void *)instance->iocb_resp+(ENTRIES_PER_RING*8*4));
        instance->pcb[20] = ENTRIES_PER_RING;
        *(instance->pcb+84) = virt_to_bus((void *)instance->iocb_cmnd+(2*ENTRIES_PER_RING*8*4));
        instance->pcb[23] = ENTRIES_PER_RING;
        *(instance->pcb+96) = virt_to_bus((void *)instance->iocb_resp+(2*ENTRIES_PER_RING*8*4));

        /* Done setting up PCB */

        if(mbox_config_port(instance, &mailbox) == 0)
                instance->sli = 2;
        else {
                firefly_chipset_init(instance);
                if(partition_slim(instance) != 0)
                        return -1;
                else 
                        instance->sli = 1;
        }
        printk("emulexfc: SLI = %d\n", instance->sli);
        return(instance->sli);
}

/*****************************************************************************
 * Function: partition_slim (for SLI-1 only)
 *
 * Description: lp6000 init
 *    STEP 3
 *    Sets up IOCB rings, and sets card in SLI-1 (port side IOCB) mode.
 *****************************************************************************/

int partition_slim(struct lp6000_hostdata* instance)
{

        /* Build PART_SLIM mailbox command
         * We want 3 rings--one for CSH, one for FCP_CMNDs and one for FCP_DATA
         * Each ring will have 20 CMD and 20 RSP IOCB's
         * These rings will use up the entire space in the SLIM
         */

        int retval = 0;


        struct mbox mailbox;

        retval = mbox_read_rconfig(instance, &mailbox, 0);

        if (1)
        {
                mailbox.cmd = MBOX_PART_SLIM;
                mailbox.wsize = 32;
                mailbox.rsize = 32;
                memset(mailbox.params, 0, 32*4);
                
                /* Pause so partition slim doesn't fail */
                mdelay(500);
                
                /* number of IOCB rings (CMD/RSP pairs) */
                mailbox.params[1]  =  1;         
                /* number of IOCB entries per CMD ring for CSH */
                mailbox.params[2]  = 50;  
                /* number of IOCB entries per RSP ring for CSH */
                mailbox.params[3]  = 50;         
#if 0

                /* number of IOCB entries per CMD ring for FCP_CMND */
                mailbox.params[4]  = ENTRIES_PER_RING;
                /* number of IOCB entries per RSP ring for FCP_CMND */
                mailbox.params[5]  = ENTRIES_PER_RING;
                /* number of IOCB entries per CMD ring for FCP_DATA */
                mailbox.params[6]  = ENTRIES_PER_RING;
                /* number of IOCB entries per RSP ring for FCP_DATA */
                mailbox.params[7]  = ENTRIES_PER_RING;
#endif
                /* number of IOCB entries for IOCB ring 3 (0) */
                mailbox.params[4] = 0;
                mailbox.params[5] = 0;
                mailbox.params[6] = 0;
                mailbox.params[7] = 0;
                mailbox.params[8]  = 0;
                mailbox.params[9]  = 0;
                
                if (lp6000_mbox_command(instance, &mailbox) != 0) {
                        printk(KERN_ERR "emulexfc: MBOX_PART_SLIM failed: status = %d\n", mailbox.status);
                        retval = -1;
                        return(retval);
                }
                
                /* save iocb rings */
                instance->iocb_rings[CSH_CMND] = mailbox.params[2] >> 16;
                instance->iocb_rings[CSH_RESP] = mailbox.params[3] >> 16;
#if 0
                instance->iocb_rings[FCP_CMND_CMND] = mailbox.params[4] >> 16;
                instance->iocb_rings[FCP_CMND_RESP] = mailbox.params[5] >> 16;
                instance->iocb_rings[FCP_DATA_CMND] = mailbox.params[6] >> 16;
                instance->iocb_rings[FCP_DATA_RESP] = mailbox.params[7] >> 16;
#endif                
                
        }
        else if(retval == 0x0C01 || retval == 0)
        {
                instance->iocb_rings[CSH_CMND] = (mailbox.params[9] >> 16);
                instance->iocb_rings[CSH_RESP] = (mailbox.params[10] >> 16);

                retval = mbox_read_rconfig(instance, &mailbox, 1);
                if(retval != 0 && retval != 0x0C01)
                {
                        printk("emulexfc: MBOX_READ_RCONFIG 1 returned 0x%08x\n", retval);
                        return(retval);
                }
                printk("emulexfc: retval = %d\n", retval);
                instance->iocb_rings[FCP_CMND_CMND] = mailbox.params[9] >> 16;
                instance->iocb_rings[FCP_CMND_RESP] = mailbox.params[10] >> 16;                

                retval = mbox_read_rconfig(instance, &mailbox, 2);
                if(retval != 0 && retval != 0x0C01)
                {
                        printk("emulexfc: MBOX_READ_RCONFIG 1 returned 0x%08x\n", retval);
                        return(retval);
                }
                instance->iocb_rings[FCP_DATA_CMND] = mailbox.params[9] >> 16;
                instance->iocb_rings[FCP_DATA_RESP] = mailbox.params[10] >> 16;                
                retval = 0;
        }
        instance->iocb_cur[0] = 0;
        instance->iocb_cur[1] = 0;
        instance->iocb_cur[2] = 0;
        instance->iocb_cur[3] = 0;
        instance->iocb_cur[4] = 0;
        instance->iocb_cur[5] = 0;

        if(retval == 0)
        {
                printk("emulexfc: partitioned %d CMD IOCBs at 0x%0x\n",
                       50,
                       instance->iocb_rings[CSH_CMND]);
                printk("emulexfc: partitioned %d RSP IOCBs at 0x%0x\n",
                       50,
                       instance->iocb_rings[CSH_RESP]);
#if 0
                printk("emulexfc: partitioned %d CMD IOCBs at 0x%0x\n",
                       ENTRIES_PER_RING,
                       instance->iocb_rings[FCP_CMND_CMND]);
                printk("emulexfc: partitioned %d RSP IOCBs at 0x%0x\n",
                       ENTRIES_PER_RING,
                       instance->iocb_rings[FCP_CMND_RESP]);
                printk("emulexfc: partitioned %d CMD IOCBs at 0x%0x\n",
                       ENTRIES_PER_RING,
                       instance->iocb_rings[FCP_DATA_CMND]);
                printk("emulexfc: partitioned %d RSP IOCBs at 0x%0x\n",
                       ENTRIES_PER_RING,
                       instance->iocb_rings[FCP_DATA_RESP]);
#endif
        }

        return(retval);

}

/**************************************************************
 * Function: configure_rings
 *
 * Description: lp6000 init
 *    STEP 4
 *    Configure rings
 ***************************************************************************/

int configure_rings(struct lp6000_hostdata* instance)
{

        int retval = 0;

        struct mbox mailbox;

        /* Configure CSH ring to receive R_CTL/TYPEs that do not match any other rings */
#if 0
#if DEBUG_RING_CONFIG
        printk(KERN_INFO "emulexfc: configured IOCB ring 0 for CSH\n");
#endif
        /* Configure FCP_CMND ring to receive R_CTL/TYPEs that match FCP_CMND */

        mailbox.cmd = MBOX_CONFIG_RING;
        mailbox.wsize = 32;
        mailbox.rsize = 32;
        memset(mailbox.params, 0, 32*4);

        mailbox.params[1] = (2 << 8) | 1;       /* ring profile 2, ring number 1 */
        mailbox.params[2] = 0;                  /* use default 256 max open exchanges */

        if (lp6000_mbox_command(instance, &mailbox) != 0) {
                printk(KERN_ERR "emulexfc: MBOX_CONFIG_RING1 failed: status = 0x%08x\n", mailbox.status);
                retval = -1;
                return(retval);
        }
#if DEBUG_RING_CONFIG
        printk(KERN_INFO "emulexfc: configured IOCB ring 1 for FCP_CMND\n");
#endif
        /* Configure FCP_DATA ring to receive R_CTL/TYPEs that match FCP_DATA */
        mailbox.cmd = MBOX_CONFIG_RING;
        mailbox.wsize = 32;
        mailbox.rsize = 32;
        memset(mailbox.params, 0, 32*4);
        mailbox.params[1] = (3 << 8) | 2;       /* ring profile 3, ring number 2 */
        mailbox.params[2] = 0;                  /* use default 256 max open exchanges */
        if (lp6000_mbox_command(instance, &mailbox) != 0) {
                printk(KERN_ERR "emulexfc: MBOX_CONFIG_RING2 failed: status = %d\n", mailbox.status);
                retval = -1;
                return(retval);
        }
#endif
        mailbox.cmd = MBOX_CONFIG_RING;
        mailbox.wsize = 32;
        mailbox.rsize = 32;
        memset(mailbox.params, 0, 32*4);

        mailbox.params[1] = ((1 << 24) | (4 << 8) | 0);       /* ring profile 4, ring number 0 */
        mailbox.params[2] = 0;                  /* use default 256 max open exchanges */

        if (lp6000_mbox_command(instance, &mailbox) != 0) {
                printk(KERN_ERR "emulexfc: MBOX_CONFIG_RING0 failed: status = %d\n", mailbox.status);
                retval = -1;
                return(retval);
        }

#if DEBUG_RING_CONFIG
        printk(KERN_INFO "emulexfc: configured IOCB ring 2 for FCP_DATA\n");
#endif

        printk("emulexfc: configure_rings succesful\n");
        return(0);

}


/****************************************************************************
 * Function: configure_link
 *
 * Description: lp6000 init
 *    STEP 5
 *    Configure link
 ****************************************************************************/
int configure_link(struct lp6000_hostdata* instance)
{

        /* we can skip this step for now and use the default link timers */
        struct mbox mailbox;
        int retval = 0;
        
        mailbox.cmd = MBOX_CONFIG_LINK;
        mailbox.wsize = 32;
        mailbox.rsize = 32;
        memset(mailbox.params, 0, 32*4);
        
        mailbox.params[11] = 0x3 << 30;
        
        lp6000_mbox_command(instance, &mailbox);
                if (mailbox.status != 0) {
                retval = -1;
                return(retval);
        }

        return(0);
}

/****************************************************************************
 * Function: initialize_link
 *
 * Description: lp6000 init
 *    STEP 6
 *    Initialize link
 ****************************************************************************/

int initialize_link(struct lp6000_hostdata* instance, volatile void* buffer)
{
        struct mbox mailbox;
        int retval = 0, i;
        unsigned int event_tag;

        /* send INIT_LINK mbox command */
        mailbox.cmd = MBOX_INIT_LINK;
        mailbox.wsize = 32;
        mailbox.rsize = 32;
        memset(mailbox.params, 0, 32*4);
        mailbox.params[1] = 0;                  /* no LIP Selective Reset desired */
        mailbox.params[2] = 0;                  /* no Fabric Assigned AL_PA
                                                   attempt FC-AL then P-to-P initialization */
        lp6000_mbox_command(instance, &mailbox);
        if (mailbox.status != 0) {
                switch(mailbox.status) {
                case 0x500:
                        printk(KERN_ERR "emulexfc: MBOX_INIT_LINK failed: Link initialization failure\n");
                        break;
                case 0x501:
                        printk(KERN_ERR "emulexfc: MBOX_INIT_LINK failed: Open link failure\n");
                        break;
                case 0x502:
                        printk(KERN_ERR "emulexfc: MBOX_INIT_LINK failed: Invalid AL_PA attempted\n");
                        break;
                default:
                        printk(KERN_ERR "emulexfc: MBOX_INIT_LINK failed: status = %d\n", mailbox.status);
                        break;
                }
                retval = -1;
                return(retval);
        }
        printk("emulexfc: Link initialized\n");

        /* wait for link attention for at most 100 ms */

        printk("emulexfc: waiting for link attention\n");

        i = 0;
        while ((INL(HOST_ATT_REG) & HOST_LATT) == 0) {
                
                if (i++ == 750) {
                        printk(KERN_ERR "emulexfc: timed out waiting for link attention\n");
                        retval = -1;
                        return(retval);
                }
                mdelay(10);
        }

        /* issue READ_LA mbox command */
        mailbox.cmd = MBOX_READ_LA;
        mailbox.wsize = 32;
        mailbox.rsize = 32;
        memset(mailbox.params, 0, 257*4);
        if ((buffer = kmalloc(128, GFP_ATOMIC | GFP_DMA)) == NULL) {
                printk(KERN_ERR "emulexfc: unable to allocate AL_PA Position Map buffer\n");
                retval = -ENOMEM;
                return(retval);
        }
        memset((void *)buffer, 0, 128);
        mailbox.params[4] = (__u32)virt_to_bus(buffer);        /* AL_PA Position Map buffer address */
        mailbox.params[5] = 128;                        /* 128 byte buffer size */
        lp6000_dumpwords((void *)buffer, 32, DUMP_MEMORY);
        lp6000_mbox_command(instance, &mailbox);
        if (mailbox.status != 0) {
                switch(mailbox.status) {
                case 0x1500:
                        printk(KERN_ERR "emulexfc: MBOX_READ_LA failed: No link attention pending\n");
                        break;
                case 0x1501:
                        printk(KERN_ERR "emulexfc: MBOX_READ_LA failed: input Event Tag is less than current Link event counter\n");
                        break;
                default:
                        printk(KERN_ERR "emulexfc: MBOX_READ_LA failed: status = %d\n", mailbox.status);
                        break;
                }
                retval = -1;
                return(retval);
        }
        event_tag = mailbox.params[1];

        printk("emulexfc: Link parameters:\n");
        printk("emulexfc: Event Tag             = 0x%0x\n",
               event_tag);
        printk("emulexfc: FC-AL Port Bypass     = %s\n",
               ((mailbox.params[2] >> 9) & 0x01)?"True":"False");
        printk("emulexfc: FC-AL Implicit Logout = %s\n",
               ((mailbox.params[2] >> 8) & 0x01)?"True":"False");
        printk("emulexfc: Attention Type        = %s\n",
               (((mailbox.params[2] >>  0) & 0xff)==0x01)?"Link up.":"Link Down.");
        printk("emulexfc: AL_PA Granted:          0x%0x\n",
               (mailbox.params[3] >> 24) & 0xff);
        printk("emulexfc: AL_PA length:           0x%0x\n",
               (mailbox.params[5] % 0xffffff));
        instance->al_pa = (mailbox.params[3] >> 16) & 0xff;
        printk("emulexfc: LIP AL_PS             = 0x%0x\n",
               (mailbox.params[3] >> 16) & 0xff);
        printk("emulexfc: LIP Type              = 0x%0x\n",
               (mailbox.params[3] >>  8) & 0xff);
        printk("emulexfc: Topology              = %s\n",
               ((((mailbox.params[3] >>  0) & 0xff) == 2)?"Arbitrated Loop":"PtP/Fabric"));
        printk("emulexfc: LU=%d, TF=%d\n", mailbox.params[7] >> 31, (mailbox.params[7] >> 30) & 0x1);
        instance->topology = (mailbox.params[3] >> 0) & 0xff;

        lp6000_dumpwords((void *)buffer, 32, DUMP_MEMORY);

        /* SAVE the AL_PA Position Map */

        instance->al_pa = (mailbox.params[3] >>24) & 0xff;

        /* issue CLEAR_LA mbox command */
        mailbox.cmd = MBOX_CLEAR_LA;
        mailbox.wsize = 32;
        mailbox.rsize = 32;
        memset(mailbox.params, 0, 32*4);
        mailbox.params[1] = event_tag;
        lp6000_mbox_command(instance, &mailbox);
        if (mailbox.status != 0) {
                switch(mailbox.status) {
                case 0x1600:
                        printk(KERN_ERR "emulexfc: MBOX_CLEAR_LA failed: No Link Attention Pending\n");
                        break;
                default:
                        printk(KERN_ERR "emulexfc: MBOX_CLEAR_LA failed: status = %d\n", mailbox.status);
                        break;
                }
                retval = -1;
                return(retval);
        }


        /* clear link attention by writing a one */
        OUTL(HOST_ATT_REG, HOST_LATT);
        mdelay(500);

        printk("emulexfc: HOST_ATT_REG = %x\n", INL(HOST_ATT_REG));

        return(0);
}
    
/*********************************************************************
 * Function: init_login
 *
 * Description: lp6000 init
 *    STEP 7
 *    Fabric Login
 *********************************************************************/

int init_login(struct lp6000_hostdata* instance, volatile void* buffer, lp6000_bde* bde, struct lp6000_iocb *login_iocb, struct lp6000_iocb *reply_iocb)
{
        struct mbox mailbox;
        int retval = 0;
        unsigned int *bdl1, *bdl2, *qbdl;

        /*
         * 7.1 Issue MBOX_READ_SPARM to generate a FLOGI payload
         */

        /* You need to do something here! Otherwise you don't know exactly what the 
           topology is. (See, you NEED to do this if you don't have a fabric! Don't have a cow Russ :P)
           You also need to execute a REG_LOGIN mbox command here for all available D_IDs to scan
           the bus. This will generate a LOGI payload for an f_Port (Fabric) or an
           N_Port (Other) device as well as an RPI (Remote Port Indicator) for each device that
           exists. */

        mailbox.cmd = MBOX_READ_SPARM;
        mailbox.wsize = 32;
        mailbox.rsize = 32;
        memset(mailbox.params, 0, 32*4);
        if ((bdl1 = kmalloc(0x74, (GFP_ATOMIC | GFP_DMA))) == NULL) {
                printk(KERN_ERR "emulexfc: unable to allocate LOGI/ACC payload buffer\n");
                retval = -ENOMEM;
                return(retval);
        }
        memset((void *)bdl1, 0, 0x74);
        mailbox.params[4] = 0x70;
        mailbox.params[3] = virt_to_bus(bdl1+4);
        lp6000_mbox_command(instance, &mailbox);
        if (mailbox.status != 0) {
                printk(KERN_ERR "emulexfc: MBOX_READ_SPARM failed: status = 0x%04x\n", mailbox.status);
                return(-1);
        }
        printk("emulexfc: Acquired FLOGI payload:\n");
                

        if((qbdl = kmalloc(0x74, GFP_ATOMIC | GFP_DMA)) == NULL)
        {
                printk(KERN_ERR "emulexfc: unable to allocate qbdl\n");
                retval = -ENOMEM;
                return(retval);
        }
        memset(qbdl, 0, 0x74);
        /*
         * 7.2 Issue ELS_REQUEST_CR IOCB command
         */
        if((login_iocb = kmalloc(sizeof(struct lp6000_iocb), GFP_ATOMIC)) == NULL){
                printk(KERN_ERR "emulexfc: unable to allocate login_iocb\n");
                retval = -ENOMEM;
                return(retval);
        }
        memset(login_iocb, 0, sizeof(struct lp6000_iocb));
        
        login_iocb->payload[0] = 0x74;
        login_iocb->payload[1] = virt_to_bus(qbdl);

        login_iocb->misc1 = 0x00;
        login_iocb->misc2 = 0x04;
        login_iocb->command = QUE_RING_BUF_CN;
        login_iocb->io_tag = 0x01;
        login_iocb->context_tag = 0;

        lp6000_iocb_command(instance, login_iocb, 0);  /* correct ring? */

         /* guessing size of LOGI and ACC payloads
         * all the docs say is that the exact size of them
         * isn't enforced :P */

        bdl1[0] = 0x00000004;
        bdl1[1] = 0x00000909;
        bdl1[2] = 0x00080088;
        bdl1[3] = 0xFFFFFF00;
        bdl1[4] = 0x00000000;

        lp6000_dumpwords(bdl1, 29, DUMP_MEMORY); 
        
        login_iocb->payload[1] = 0x70;
        login_iocb->payload[0] = virt_to_bus(bdl1);
        bdl2 = (unsigned int*)kmalloc(0xFFF, GFP_KERNEL | GFP_DMA);
        if (bdl2 == NULL)
        {
                printk(KERN_ERR "emulexfc: unable to allocate LOGI BDE\n");
                retval = -ENOMEM;
                return(retval);
        }
        memset(bdl2, 0, 0xFFF);
        login_iocb->payload[3] = 0xFFF;
        login_iocb->payload[2] = virt_to_bus(bdl2);

        /* ***** setup the LOGI buffer ***** */
        login_iocb->misc1 = 0x01;
        login_iocb->misc2 = 0x08;
        login_iocb->command = ELS_REQUEST_CR;
        login_iocb->io_tag = 0x02;
        login_iocb->context_tag = 0;
        /* If FC-AL, we know there is no fabric, so login to the N_Port instead */
        login_iocb->payload[4] = ((((mailbox.params[3] >>  0) & 0xff) == 2)?instance->al_pa | (1 << 24):(1 << 24));
        login_iocb->payload[5] = 0x01;  /* D_ID field according to documentation */

        if ((reply_iocb = (struct lp6000_iocb *)kmalloc(sizeof(struct lp6000_iocb), 
                                                        GFP_KERNEL | GFP_DMA)) == NULL){
                printk(KERN_ERR "emulexfc: unable to allocate replay IOCB\n");
                retval = -ENOMEM;
                return(retval);
        }


        lp6000_iocb_command(instance, login_iocb, 0);  /* correct ring? */
        
        printk("emulexfc: Waiting for ELS_REQUEST_CX\n");
        /*
         * 7.3 Wait for ELS_REQUEST_CX response
         */

        printk("emulexfc: host-att-reg = 0x%x\n", INL(HOST_ATT_REG));

        memset(reply_iocb, 0, sizeof(reply_iocb));

        if(lp6000_iocb_getrsp(instance, reply_iocb, 0)){
                printk(KERN_ERR "Error retrieving login RESP IOCB\n");
                retval = -EIO;
                return(retval);
        }


        printk("emulexfc: bdl2 contains 0x%08x\n", *((__u32*)bdl2));
#if 0
        printk("emulexfc: bdl2 contains 0x%08x\n", *((__u32*)bdl2+4));
        printk("emulexfc: bdl2 contains 0x%08x\n", *((__u32*)bdl2+8));
        printk("emulexfc: bdl2 contains 0x%08x\n", *((__u32*)bdl2+12));
#endif

        /*
         * 7.4 Check return status from ELS_REQUEST_CX,
         * retry if necessary
         */
        
        lp6000_dumpwords(qbdl, 0x74/4, DUMP_MEMORY);

        if(reply_iocb->command != ELS_REQUEST_CX){
                printk("emulexfc: got wrong response 0x%08x in login response\n", reply_iocb->command);
                printk("emulexfc: Local Reject (error detected by port): 0x%0x\n", reply_iocb->payload[4]);
                printk("emulexfc: command is 0x%02x\n", reply_iocb->command);
                printk("emulexfc: status was 0x%01x\n", ((reply_iocb->misc2 >> 4) & 0xf));
                printk("emulexfc: io_tag was 0x%04x\n", reply_iocb->io_tag);
                retval = -EIO;
                /* dump_resp_rings(instance); */
                return(retval);
        }
        
        switch((reply_iocb->misc2 >> 4) & 0xf)
        {
        case LOCAL_REJECT:
                printk("emulexfc: Local Reject (error detected by port): 0x%0x\n", reply_iocb->payload[4]);
                return(-1);
                break;
        case FCP_RSP_FAILURE:
                printk("emulexfc: Link layer success, but FCP/SCSI Error: 0x%0x\n", reply_iocb->payload[4]);
                return(-1);
                break;
        case FABRIC_BSY:
        case NPORT_BSY:  /* sleep 1 second and try again */
                printk("emulexfc: got _BSY error, wating and retrying\n");
                mdelay(1000);
                return(-1);
                break;
        case NPORT_RJT:
        case FABRIC_RJT:
                printk("emulexfc: got N/FPORT_RJT error, retrying login\n");
                login_iocb->payload[4] = (reply_iocb->payload[4] & 0xffffff);
                return(-1);
                break;
        case LS_RJT:
                printk("emulexfc: got and LS_RJT error: 0x%x\n", reply_iocb->payload[4]);
                return(-1);
        default:
                printk("emulexfc: ELS_REQ status was 0x%08x\n", (reply_iocb->misc2 >> 4) & 0xf);
                break;
        }

        
        /*
         * 7.5 Save information from ELS_REQUEST_CX response
         */

        printk("emulexfc: Port Name returned = %s\n", (char *) &bdl2[4]);

        /*
         * 7.6 If Fabric port, issue CONFIG_LINK to register
         * local D_ID, fabric timers, etc.
         */

        /*
         * 7.7 Issue REG_LOGIN to register the N/F port login
         */
        
        /* Free LOGI/ACC payload buffer */

        memset(instance->rpi, 0, 0x7F * 2);

        mailbox.cmd = MBOX_REG_LOGIN;
        mailbox.wsize = 32;
        mailbox.rsize = 32;
        mailbox.params[2] = 0x02; /* Our array... To test for now. */
        
        retval = lp6000_mbox_command(instance, &mailbox);
        if(retval)
        {
                printk("emulexfc: REG_LOGIN returned 0x%04x\n", retval);
        }

        instance->rpi[0x02] = mailbox.params[1] & 0xFFFF;
               
        printk("emulexfc: Array RPI = 0x%04x\n", instance->rpi[0x02]);
        return(-1); //die

//        return(retval);

}

/****************************************************************************
 * Function:
 *   enable_interrupts
 ****************************************************************************/

int enable_interrupts(struct lp6000_hostdata *instance)
{
        /* Later we'll want to enable the Link Attention Interrupt, but for now
           this is as much as we should try to chew. After this works we can try that.
        */
/*
  OUTL(CTRL_REG, CTRL_R2INT_ENA | CTRL_R1INT_ENA | CTRL_R0INT_ENA);
*/

//        OUTL(CTRL_LAINT_ENA);
        return(0);
}


/*+F*************************************************************************
 * Function:
 *   lp6000_info, taken from aic7xxx_info
 *
 * Description:
 *   Return a string describing the driver.
 *-F*************************************************************************/
const char *
lp6000_info(struct Scsi_Host *host)
{
	static char buf[128];
	struct lp6000_hostdata *hostdata;

	ENTER("lp6000_info");

	hostdata = (struct lp6000_hostdata *) host->hostdata;
	sprintf(buf,
		"Emulex LP6000 SCSI on PCI bus %02x device %02x irq %d mem base 0x%lx "
                "reg base 0x%lx io base 0x%lx",
		hostdata->pci_dev->bus->number, hostdata->pci_dev->devfn,  hostdata->irq, 
		hostdata->slim_base, hostdata->reg_base, hostdata->io_port);
     
	LEAVE("lp6000_info");

	return buf;
}


/*+F*************************************************************************
 * Function:
 *   lp6000_queuecommand
 *
 * Description:
 *   Queue scsi command.
 *   The middle SCSI layer ensures that queuecommand never gets invoked
 *   concurrently with itself or the interrupt handler (though the
 *   interrupt handler may call this routine as part of
 *   request-completion handling).
 *-F*************************************************************************/
int
lp6000_queuecommand(Scsi_Cmnd *Cmnd, void (*done)(Scsi_Cmnd *))
{
        unsigned long flags;
	struct Scsi_Host *host;
	struct lp6000_hostdata *hostdata;
        lp6000_Scsi_Cmnd *cmnd, *current_cmnd;

	ENTER("lp6000_queuecommand");


	host = Cmnd->host;
	hostdata = (struct lp6000_hostdata *) host->hostdata;
	Cmnd->scsi_done = done;

	DEBUG(lp6000_print_scsi_cmd(Cmnd));

        /* will do error checking soon as i figure out how to pass the
         * error back properly.... :P */
        cmnd = (lp6000_Scsi_Cmnd *)kmalloc(sizeof(lp6000_Scsi_Cmnd), 
                                           GFP_KERNEL);
        cmnd->Cmnd = Cmnd;
        cmnd->next = NULL;
        cmnd->done = done;
        cmnd->tqueue.next = NULL;
        cmnd->tqueue.sync = 0;
        cmnd->tqueue.routine = lp6000_do_bh_queue;
        cmnd->tqueue.data = hostdata;

        spin_lock_irqsave(&hostdata->queue_lock, flags);
        if(hostdata->cmnd_queue){
                current_cmnd = hostdata->cmnd_queue;
                while(current_cmnd->next)
                        current_cmnd = current_cmnd->next;
                current_cmnd->next = cmnd;
        }else{
                hostdata->cmnd_queue = cmnd;
        }  
        spin_unlock_irqrestore(&hostdata->queue_lock, flags);
        queue_task(&cmnd->tqueue, &tq_immediate);
        mark_bh(IMMEDIATE_BH);
        LEAVE("lp6000_queuecommand");
        return 0;
}

/*+F*************************************************************************
 * Function:
 *   lp6000_do_bh_queue
 *
 * Description:
 *   bottom half handler to process scsi commands
 *   from lp6000_queuecommand
 *-F*************************************************************************/
void lp6000_do_bh_queue(void *hostdata_in){
        unsigned long flags;
        lp6000_Scsi_Cmnd *cmnd;
        struct lp6000_hostdata *hostdata =
                (struct lp6000_hostdata *)hostdata_in;
        struct lp6000_iocb iocb;
        static __u16 io_tag = 5;
        __u32 fcp_cmnd[8], fcp_resp[8], *fcp_data, retval;
        

        /* first, remove the queue entry */
        spin_lock_irqsave(&hostdata->queue_lock, flags);

        fcp_data = kmalloc(96*4, (GFP_KERNEL | GFP_DMA));
        memset(fcp_cmnd, 0, 4*8);
        memset(fcp_resp, 0, 4*8);

        if(hostdata->cmnd_queue){
                cmnd = hostdata->cmnd_queue;
                hostdata->cmnd_queue = hostdata->cmnd_queue->next;
                spin_unlock_irqrestore(&hostdata->queue_lock, flags);
        }else{
                DEBUG(printk(KERN_ERR "emulexfc: lp6000_do_bh_queue called with empty queue\n"));
                spin_unlock_irqrestore(&hostdata->queue_lock, flags);
                return;
        }

        switch(cmnd->Cmnd->cmnd[0]){
        case READ_CAPACITY:
                if(hostdata->rpi[cmnd->Cmnd->target] == 0)
                {
                        cmnd->Cmnd->result = CHECK_CONDITION;
                        cmnd->Cmnd->sense_buffer[0] = NO_SENSE;
                }
                else
                {
                        iocb.command = FCP_ICMND_CR;
                        iocb.misc2 = (1 << 1) | (2 << 2) | (0 << 4);
                        iocb.misc1 = 1;
                        iocb.io_tag = io_tag++;
                        iocb.context_tag = hostdata->rpi[cmnd->Cmnd->target];
                        iocb.payload[5] = 0;
                        iocb.payload[4] = 0;
                        fcp_cmnd[0] = cmnd->Cmnd->lun;
                        fcp_cmnd[3] = READ_CAPACITY | (cmnd->Cmnd->lun << 13);
                        iocb.payload[0] = virt_to_bus(&fcp_cmnd);
                        iocb.payload[1] = (4*8);
                        iocb.payload[2] = virt_to_bus(&fcp_resp);
                        iocb.payload[3] = (4*8);
                        printk("emulexfc: sending IOCB READ_CAPICITY command.\n");
                        retval = lp6000_iocb_command(hostdata, &iocb, 0);
                        /* FIXME, look up sane error to return */
                        if(retval)
                        {
                                printk("emulexfc: Died sending READ_CAPACITY 0x%04x\n", retval);
                                cmnd->Cmnd->result = CHECK_CONDITION;
                                cmnd->Cmnd->sense_buffer[0] = NO_SENSE;
                        }
                        else {
                                retval = lp6000_iocb_getrsp(hostdata, &iocb, 0);
                                if(retval)
                                {
                                        printk("emulexfc: Died recieving READ_CAPACITY rsp 0x%04x\n", retval);
                                        cmnd->Cmnd->result = CHECK_CONDITION;
                                        cmnd->Cmnd->sense_buffer[0] = NO_SENSE;
                                }else {
                                        printk("emulexfc: Recieved 0x%06x LBA\n", fcp_resp[0]); 
                                        cmnd->Cmnd->result = GOOD;
                                        cmnd->Cmnd->sense_buffer[0] = NO_SENSE;
                                        cmnd->Cmnd->buffer = fcp_data;
                                        cmnd->Cmnd->bufflen = (4*8);
                                }
                        }
                }                        
                break;
        case INQUIRY:
                if(hostdata->rpi[cmnd->Cmnd->target] == 0)
                {
                        cmnd->Cmnd->result = CHECK_CONDITION;
                        cmnd->Cmnd->sense_buffer[0] = NO_SENSE;
                }
                else
                {
                        iocb.command = FCP_IREAD_CR;
                        iocb.misc2 = (0 << 1) | (2 << 2) | (0 << 4);
                        iocb.misc1 = 1;
                        iocb.io_tag = io_tag;
                        iocb.context_tag = hostdata->rpi[cmnd->Cmnd->target];
                        iocb.payload[5] = 0;
                        iocb.payload[4] = 0;
                        fcp_cmnd[0] = cmnd->Cmnd->lun;
                        fcp_cmnd[3] = INQUIRY;
                        fcp_cmnd[4] = (96*4);
                        iocb.payload[0] = virt_to_bus(&fcp_cmnd);
                        iocb.payload[1] = (4*8);
                        iocb.payload[2] = virt_to_bus(&fcp_resp);
                        iocb.payload[3] = (4*8);
                        printk("emulexfc: sending SCSI INQUIRY command.\n");
                        retval = lp6000_iocb_command(hostdata, &iocb, 0);
                        iocb.command = IOCB_CONTINUE_CN;
                        iocb.misc2 = (1 << 1) | (1 << 2) | (0 << 4);
                        iocb.misc1 = 1;
                        iocb.io_tag = io_tag++;
                        iocb.context_tag = hostdata->rpi[cmnd->Cmnd->target];
                        iocb.payload[5] = 0;
                        iocb.payload[4] = 0;
                        fcp_cmnd[0] = cmnd->Cmnd->lun;
                        fcp_cmnd[3] = INQUIRY;
                        fcp_cmnd[4] = (96*4);
                        iocb.payload[0] = virt_to_bus(fcp_data);
                        iocb.payload[1] = (96*4);
                        printk("emulexfc: sending SCSI INQUIRY CONT command.\n" );
                        retval = lp6000_iocb_command(hostdata, &iocb, 0);
                        /* FIXME, look up sane error to return */
                        if(retval)
                        {
                                printk("emulexfc: Died sending INQUIRY 0x%04x\n", retval);
                                cmnd->Cmnd->result = CHECK_CONDITION;
                                cmnd->Cmnd->sense_buffer[0] = NO_SENSE;
                        }
                        else {
                                retval = lp6000_iocb_getrsp(hostdata, &iocb, 0);
                                if(retval)
                                {
                                        printk("emulexfc: Died recieving INQUIRY rsp 0x%04x\n", retval);
                                        cmnd->Cmnd->result = CHECK_CONDITION;
                                        cmnd->Cmnd->sense_buffer[0] = NO_SENSE;
                                }else {
                                        printk("emulexfc: cmd is now 0x%02x\n", iocb.command);
                                        cmnd->Cmnd->result = GOOD;
                                        cmnd->Cmnd->sense_buffer[0] = NO_SENSE;
                                        cmnd->Cmnd->buffer = fcp_data;
                                        cmnd->Cmnd->bufflen = (96*4);
                                }
                        }
                }                        
                break;
        case TEST_UNIT_READY:
                if(hostdata->rpi[cmnd->Cmnd->target] == 0)
                {
                        cmnd->Cmnd->result = CHECK_CONDITION;
                        cmnd->Cmnd->sense_buffer[0] = NO_SENSE;
                }
                else
                {
                        iocb.command = FCP_ICMND_CR;
                        iocb.misc2 = (1 << 1) | (2 << 2) | (0 << 4);
                        iocb.misc1 = 1;
                        iocb.io_tag = io_tag++;
                        iocb.context_tag = hostdata->rpi[cmnd->Cmnd->target];
                        iocb.payload[5] = 0;
                        iocb.payload[4] = 0;
                        fcp_cmnd[0] = cmnd->Cmnd->lun;
                        fcp_cmnd[3] = READ_CAPACITY;
                        iocb.payload[0] = virt_to_bus(&fcp_cmnd);
                        iocb.payload[1] = (8*4);
                        iocb.payload[2] = virt_to_bus(&fcp_resp);
                        iocb.payload[3] = (8*4);
                        printk("emulexfc: sending IOCB TEST_UNIT_READY command: %x\n", cmnd->Cmnd->target);
                        retval = lp6000_iocb_command(hostdata, &iocb, 0);
                        /* FIXME, look up sane error to return */
                        if(retval)
                        {
                                printk("emulexfc: Died sending TEST_UNIT_READY 0x%04x\n", retval);
                                cmnd->Cmnd->result = CHECK_CONDITION;
                                cmnd->Cmnd->sense_buffer[0] = NO_SENSE;
                        }
                        else {
                                retval = lp6000_iocb_getrsp(hostdata, &iocb, 0);
                                if(retval)
                                {
                                        printk("emulexfc: Died recieving TEST_UNIT_READY rsp 0x%04x\n", retval);
                                        cmnd->Cmnd->result = CHECK_CONDITION;
                                        cmnd->Cmnd->sense_buffer[0] = NO_SENSE;
                                }else {
                                        if(iocb.command != FCP_ICMND_CX)
                                        {
                                                printk("emulexfc: cmd is now 0x%02x\n", iocb.command);
                                                printk("emulexfc: io_tag is now 0x%04x\n", iocb.io_tag);
                                                cmnd->Cmnd->result = CHECK_CONDITION;
                                                cmnd->Cmnd->sense_buffer[0] = NO_SENSE;
                                        }
                                        else
                                        {
                                                printk("emulexfc: TUR command success\n");
                                                cmnd->Cmnd->result = GOOD;
                                                cmnd->Cmnd->sense_buffer[0] = NO_SENSE;
                                        }
                                }
                        }
                }                        
                break;
        default:
                printk("Recieved SCSI command 0x%02x and don't know what to do with it!\n", cmnd->Cmnd->cmnd[0]);
        }
/* This should be done by the interrupt handler when the command is finished,
   hence being commented out here. */

        cmnd->done(cmnd->Cmnd);
        kfree(cmnd);
        return;
}

#define ASYNC_EVENT_INTERRUPT	0x01

void do_lp6000_intr_handler(int irq, void *dev_id, struct pt_regs *regs)
{
       unsigned long flags;

       spin_lock_irqsave(&io_request_lock, flags);
       lp6000_intr_handler(irq, dev_id, regs);
       spin_unlock_irqrestore(&io_request_lock, flags);
}

/*
 * do the bottom half processing.  again, does nothing
 * atm - this is going to be one of the core functions here.
 */

void lp6000_do_intr_bh(void *hostdata_in){
        unsigned long flags, reg_temp;
        lp6000_intr_task *task;
        struct lp6000_hostdata *hostdata =
                (struct lp6000_hostdata *)hostdata_in;

        spin_lock_irqsave(&hostdata->intr_queue, flags);
        if(hostdata->intr_queue){
                task = hostdata->intr_queue;
                hostdata->intr_queue = hostdata->intr_queue->next;
                reg_temp = INL(HOST_ATT_REG);
                spin_unlock_irqrestore(&hostdata->intr_queue_lock, flags);

                /* Check to see if it's a Ring Attention event (otherwise it's an error... Link Events were handled already...) */
                if(reg_temp & 0xFFFF)
                {
                        int ring = 0;
                        struct lp6000_iocb *iocb;

                        /* Which ring? (always do zero first... Just becaause... This may be bad.) */
                        switch(reg_temp)
                        {
                        case HC_R0ATT:
                                ring = 0;
                                break;
                        case HC_R1ATT:
                                ring = 1;
                                break;
                        case HC_R2ATT:
                                ring = 2;
                                break;
                        case HC_R3ATT:
                                ring;
                                break;
                        default:
                                printk("emulexfc: Shouldn't be able to get here. %s Line: %d\n", __FILE__, __LINE__);
                        } 
                        
                        if(lp6000_iocb_resp(hostdata, iocb, ring) != 0)
                        {
                                /* there was an error, and I haven't decided what we're going to handle that yet. */
                                printk("emulexfc: Oh shit! error getting IOCB resp. What to do...\n");
                        }

                        /* Is this a host owned response? */
                        /* What responses aren't host owned, and if we get one why is the card bothering *us* with it? */

                        /* Are there continuation entries after this? */

                        if(IOCB_GET_LE(iocb) == 0) /* The LE bit stands for Last Entry */
                        {
                                
                        }
                        
        }else{
                DEBUG(printk(KERN_ERR "emulexfc: lp6000_do_intr_bh called with empty intr_queue\n"));
                spin_unlock_irqrestore(&hostdata->intr_queue_lock, flags);
                return;
        }
        
        kfree(task);
        return;
}

/* 
 * take the interrupt, and queue up a bottom half to process it.
 * very barebones now, needs more functionality.
 */
void lp6000_intr_handler(int irq, void *dev_id, struct pt_regs *regs)
{
	struct Scsi_Host *host = dev_id;
	struct lp6000_hostdata *instance;
        struct mbox mailbox;
        void *buffer;
        int retval,event_tag;
        

	ENTER_INTR("lp6000_intr_handler");

	instance  = (struct lp6000_hostdata *)host->hostdata;

	DEBUG_INTR(printk(KERN_INFO "emulexfc: interrupt on line %d\n", irq));
        

        /* Ok, the big thing here is to clear the link 
           interrupt.   This *has* to be done by calling 
           READ_LA and then CLEAR_LA with the event_tag,
           otherwise you get interrupts forever...
        */

        /* try to force every interrupt clear... */
        /*    OUTL(HOST_ATT_REG, 0xFFFFFFFF); */
        
//       OUTL(HOST_ATT_REG, 0x1);
/*        printk("Interrupt:  0x%x\n", INL(HOST_ATT_REG)); */

        //      if(INL(HOST_ATT_REG) & 0x01) {
        //      OUTL(HOST_ATT_REG, 0x01);
        //}

/*        printk("Intr: 0x%x\n",(INL(HOST_ATT_REG))); */
        
                
                if (INL(HOST_ATT_REG) & HOST_LATT) 
                { 
                        /*  if (0){ */
                        printk ("got link intr\n");
                        /* issue READ_LA mbox command */
                        mailbox.cmd = MBOX_READ_LA;
                        mailbox.wsize = 32;
                        mailbox.rsize = 32;
                        memset(mailbox.params, 0, 32*4);
                        if ((buffer = kmalloc(128, GFP_ATOMIC)) == NULL) {
                                printk(KERN_ERR "emulexfc: unable to allocate AL_PA Position Map buffer\n");
                                retval = -ENOMEM;
                                goto INIT_ERROR;
                        }
                        mailbox.params[4] = virt_to_bus(buffer);        /* AL_PA Position Map buffer address */
                        mailbox.params[5] = 128;                        /* 128 byte buffer size */
                        lp6000_mbox_command(instance, &mailbox);
                        if (mailbox.status != 0) {
                                switch(mailbox.status) {
                                case 0x1600:
                                        printk(KERN_ERR "emulexfc: MBOX_READ_LA failed: No link attention pending\n");
                                        break;
                                case 0x1601:
                                        printk(KERN_ERR "emulexfc: MBOX_READ_LA failed: input Event Tag is less than current Link event counter\n");
                                        break;
                                default:
                                        printk(KERN_ERR "emulexfc: MBOX_READ_LA failed: status = %d\n", mailbox.status);
                                        break;
                                }
                                retval = -1;
                                goto INIT_ERROR;
                        }
                        event_tag = mailbox.params[1];
                        
                        printk(KERN_INFO "emulexfc: Link parameters:\n");
                        printk(KERN_INFO "emulexfc: Event Tag             = 0x%0x\n",
                               event_tag);
                        printk(KERN_INFO "emulexfc: FC-AL Port Bypass     = %d\n",
                               (mailbox.params[2] >> 10) & 0x01);
                        printk(KERN_INFO "emulexfc: FC-AL Implicit Logout = %d\n",
                               (mailbox.params[2] >>  9) & 0x01);
                        printk(KERN_INFO "emulexfc: Attention Type        = %d\n",
                               (mailbox.params[2] >>  0) & 0xff);
                        printk(KERN_INFO "emulexfc: AL_PA Granted         = 0x%0x\n",
                               (mailbox.params[3] >> 24) & 0xff);
                        printk(KERN_INFO "emulexfc: LIP AL_PS             = 0x%0x\n",
                               (mailbox.params[3] >> 16) & 0xff);
                        printk(KERN_INFO "emulexfc: LIP Type              = 0x%0x\n",
                               (mailbox.params[3] >>  8) & 0xff);
                        printk(KERN_INFO "emulexfc: Topology              = 0x%0x\n",
                               (mailbox.params[3] >>  0) & 0xff);
                
                        /* dump the AL_PA Position Map */
                        printk(KERN_INFO "emulexfc: AL_PA Position Map:\n");
                        lp6000_dumpwords((void *)buffer, 128/4, DUMP_MEMORY);
                        kfree(buffer);
                        buffer = NULL;
                
                        /* issue CLEAR_LA mbox command */
                        mailbox.cmd = MBOX_CLEAR_LA;
                        mailbox.wsize = 32;
                        mailbox.rsize = 32;
                        memset(mailbox.params, 0, 32*4);
                        mailbox.params[1] = event_tag;
                        lp6000_mbox_command(instance, &mailbox);
                        if (mailbox.status != 0) {
                                switch(mailbox.status) {
                                case 0x1500:
                                        printk(KERN_ERR "emulexfc: MBOX_CLEAR_LA failed: No Link Attention Pending\n");
                                        break;
                                default:
                                        printk(KERN_ERR "emulexfc: MBOX_CLEAR_LA failed: status = %d\n", mailbox.status);
                                        break;
                                }
                                retval = -1;
                                goto INIT_ERROR;
                        }
                }
                
 INIT_ERROR:        
  new_task = (lp6000_intr_task *)kmalloc(sizeof(lp6000_intr_task),
  GFP_ATOMIC);
  new_task->next = NULL;
  new_task->tqueue.next = NULL;
  new_task->tqueue.sync = 0;
  new_task->tqueue.routine = lp6000_do_intr_bh;
  new_task->tqueue.data = hostdata;

  spin_lock_irqsave(&hostdata->intr_queue_lock, flags);
  if(hostdata->intr_queue){
  cur_task = hostdata->intr_queue;
  while(cur_task->next)
  cur_task = cur_task->next;
  cur_task->next = new_task;
  }else{
  hostdata->intr_queue = new_task;
  }
  spin_unlock_irqrestore(&hostdata->queue_lock, flags);
  queue_task(&new_task->tqueue, &tq_immediate);
  mark_bh(IMMEDIATE_BH);
  LEAVE_INTR("lp6000_intr_handler");
        
}


int lp6000_abort(Scsi_Cmnd *Cmnd)
{
        //	u_short param[8];
	struct Scsi_Host *host;
	struct lp6000_hostdata *hostdata;
	int return_status = SCSI_ABORT_SUCCESS;
        //	u_int cmdaddr = virt_to_bus(Cmnd);

	ENTER("lp6000_abort");

	host = Cmnd->host;
	hostdata = (struct lp6000_hostdata *) host->hostdata;

	LEAVE("lp6000_abort");

	return return_status;
}


int lp6000_reset(Scsi_Cmnd *Cmnd, unsigned int reset_flags)
{
        //	u_short param[8];
	struct Scsi_Host *host;
	struct lp6000_hostdata *hostdata;
	int return_status = SCSI_RESET_SUCCESS;

	ENTER("lp6000_reset");

	host = Cmnd->host;
	hostdata = (struct lp6000_hostdata *) host->hostdata;

	LEAVE("lp6000_reset");

	return return_status;;
}

/* return disk geometry for given SCSI device */
int lp6000_biosparam(Disk *disk, kdev_t n, int ip[])
{
	int size = disk->capacity;

	ENTER("lp6000_biosparam");

	ip[0] = 64;
	ip[1] = 32;
	ip[2] = size >> 11;
	if (ip[2] > 1024) {
		ip[0] = 255;
		ip[1] = 63;
		ip[2] = size / (ip[0] * ip[1]);
		if (ip[2] > 1023)
			ip[2] = 1023;
	}

	LEAVE("lp6000_biosparam");

	return 0;
}

/*+F*************************************************************************
 * Function:
 *   lp6000_iocb_command
 *
 * Description:
 *   send a command to the designated IOCB ring
 *
 *   DO NOT set the own bit (word 7, bit 0) in the command you're sending
 *   the calling function must ensure that the id tag in the command is
 *    unique
 *   this function does not handle continuation entries for you, either
 *   it is up to the calling function to kfree the mem allocated
 *-F*************************************************************************/

static int lp6000_iocb_command(struct lp6000_hostdata *instance,
                               struct lp6000_iocb *iocb,
                               unsigned int ring){
        unsigned long flags;
        unsigned int  i = 0, temp;

        ENTER("lp6000_iocb_command");


        /* dump_cmnd_rings(instance); */

        printk("emulexfc: IOCB command\n");
        /* unlike the mbox commands, we don't have to do size checking,
         * as the iocb commands are always 32 bytes */

        spin_lock_irqsave(&instance->spin_lock, flags);
        
        /* wait for the OWN bit in the current IOCB to clear
         * in theory, this should rarely happen anyways, as the card
         * can store up to 2048 entries in SRAM */
        while( READL((instance->iocb_rings[ring*2] + (instance->iocb_cur[ring*2] * 32))+(4*7)) & IOCB_OWN ){
                if( i++ >= 20){
                        printk("emulexfc: timed out waiting for IOCB ready status\n");
                        printk("emulexfc: word 7 was: 0x%08x\n",instance->iocb_rings[ring*2]+(4*7));
                        return(-1);
                }
                mdelay(100);
        }

        /* write out the command to the appropriate entry */
        for(i = 0 ; i <= 5 ; i++)
                WRITEL((instance->iocb_rings[ring*2] +
                        (instance->iocb_cur[ring*2] * 32) +
                        (i*4)), 
                       iocb->payload[i]);
        temp = (iocb->context_tag << 16) | iocb->io_tag;
        WRITEL((instance->iocb_rings[ring*2] +
                (instance->iocb_cur[ring*2] * 32) +
                (6*4)),
               temp);
        temp = 0;
        temp = (iocb->misc1 << 16) | (iocb->command << 8) | (iocb->misc2);
        printk("emulexfc: IOCB Command struct word7 = %08x\n", temp);
        WRITEL((instance->iocb_rings[ring*2] +
                (instance->iocb_cur[ring*2] * 32) +
                 (7*4)),
               temp);

        /* dump_cmnd_rings(instance); */

        /* set the OWN bit in the entry */
        WRITEL((instance->iocb_rings[ring*2] +
                (instance->iocb_cur[ring*2] * 32) +
                (7 * 4)),
                temp | IOCB_OWN);

        /* if we got ambitious, we could set the RxATT bit of the chipset
         * attention register */

        /* update our location in the ring */
        if(instance->iocb_cur[ring*2] == (ENTRIES_PER_RING - 1))
                instance->iocb_cur[ring*2] = 0;
        else
                instance->iocb_cur[ring*2]++;

        spin_unlock_irqrestore(&instance->spin_lock, flags);

        LEAVE("lp6000_iocb_command");
        return(0);
}

/*+F*************************************************************************
 * Function:
 *  lp6000_iocb_getrsp
 *
 * Description:
 *   retrieve an IOCB from the designated response ring
 *   returns info kmalloc()'d struct lp6000_iocb
 *
 * recieving IOCB entries *really* should use interrupts, so this
 *  function should only ever get called in response to a bottom
 *  half
 *-F*************************************************************************/

static int lp6000_iocb_getrsp(struct lp6000_hostdata *instance,
                               struct lp6000_iocb *iocb,
                               unsigned int ring){
        unsigned long flags;
        int timeout = 0;
        int i; 
        unsigned int temp;

        ENTER("lp6000_iocb_getrsp");

        /* unlike the mbox commands, we don't have to do size checking,
         * as the iocb commands are always 32 bytes */
#define MEGA_DEBUG
#ifdef MEGA_DEBUG

#endif

        spin_lock_irqsave(&instance->spin_lock, flags);
        
        while((INL(HOST_ATT_REG) & (HC_R0ATT << (ring*4))) == 0)
        {
                if( timeout++ == 200){
                        printk(KERN_ERR "emulexfc: timed out waiting for IOCB ready status\n");
                        return -EIO;
                }
                mdelay(100);
        }
        printk("emulexfc: host-att-reg = 0x%08x\n", INL(HOST_ATT_REG));
        OUTL(HOST_ATT_REG, INL(HOST_ATT_REG) | HC_R0ATT | HC_R0CE_RSP | HC_R0RE_REQ | HOST_R0ERR);

        /* Sick kludge to find the initial host get pointer */
        /* Heh, turns out this was the actual way to do it... */
        while(READL(instance->iocb_rings[(ring*2)+1] +
                    (instance->iocb_cur[(ring*2)+1] * 32) +
                    (7 * 4)) & IOCB_OWN)
        {
                printk(".");
                instance->iocb_cur[(ring*2)+1] = (instance->iocb_cur[(ring*2)+1] + 1) % ENTRIES_PER_RING;
        }
        printk("\n");


        OUTL(HOST_ATT_REG, (HC_R0ATT << (ring*4)));

        for(i = 0 ; i <= 5 ; i++) {
                iocb->payload[i] = READL(instance->iocb_rings[(ring*2)+1] +
                                      (instance->iocb_cur[(ring*2)+1] * 32) +
                                      (i*4));
        }
        temp = READL(instance->iocb_rings[(ring*2)+1] +
                     (instance->iocb_cur[(ring*2)+1] * 32) +
                     (6*4));
        iocb->context_tag = (temp >> 16) & 0xFFFF; 
        iocb->io_tag = temp & 0xFFFF;
        temp = READL(instance->iocb_rings[(ring*2)+1] +
                     (instance->iocb_cur[(ring*2)+1] * 32) +
                     (7*4));
        dump_resp_rings(instance);
        iocb->command = (temp >> 8) & 0xFF;
        iocb->misc2 = (temp & 0xFF);

        WRITEL(instance->iocb_rings[(ring*2)+1] +
               (instance->iocb_cur[(ring*2)+1] * 32) +
                (7*4), IOCB_OWN);

        /* increment ring pointer */
        instance->iocb_cur[(ring*2)+1] = (instance->iocb_cur[(ring*2)+1] + 1) % ENTRIES_PER_RING;

        spin_unlock_irqrestore(&instance->spin_lock, flags);
        
        LEAVE("lp6000_iocb_getrsp");
        return 0;
}


/*
 *  This has nothing to do with the AIC78XX driver, so I'm changing the 
 *  comment here.
 */
static int lp6000_mbox_command(struct lp6000_hostdata *instance, struct mbox *m) {
        unsigned int i, timeout;

        if ((m->rsize > MBOX_MAX_SIZE) || (m->rsize < 1) ||
            (m->wsize > MBOX_MAX_SIZE) || (m->wsize < 1)) {
                printk(KERN_ERR "emulexfc: recieved oversized mailbox command\n");
                return(-1);
        }
        
        if(instance->sli == 1)
        {

                /* check that the mailbox is ready, owned by us,
                 * and not in use by another driver
                 */
                timeout = 0;
                while ( ((INL(STATUS_REG) & STATUS_MB) == 0) ||
                        ((READL(0) & (MBOX_OWNER | MBOX_HCONT)) != 0) ) {
                        
                        if (timeout++ == 4) {
                                printk(KERN_ERR "emulexfc: timed out waiting for MBOX ready status\n");
                                return(-1);
                        }
                        mdelay(250); 
                }

                /* set host contention bit to lock out others from the mbox
                   and set mailbox command */

                WRITEL(0, ((m->cmd << 8) | MBOX_HCONT));

                /* fill in the mailbox command parameters */ 
                for(i = 1; i < m->wsize; i++)
                        WRITEL(i*4, m->params[i]);

                /* with the command now written out to
                   the SLIM, we set the own bit */
                WRITEL(0, READL(0) | MBOX_OWNER);

        }
        else if(instance->sli == 2)
        {
                printk("emulexfc: Using hostside mailbox.\n");

                /* check that the mailbox is ready, owned by us,
                 * and not in use by another driver
                 */
                timeout = 0;
                while ( ((INL(STATUS_REG) & STATUS_MB) == 0) ||
                        ((instance->mailbox[0] & (MBOX_OWNER | MBOX_HCONT)) != 0) ) {
                        
                        if (timeout++ == 4) {
                                printk(KERN_ERR "emulexfc: timed out waiting for MBOX ready status\n");
                                return(-1);
                        }
                        mdelay(250); 
                }

                /* set host contention bit to lock out others from the mbox
                   and set mailbox command */

                instance->mailbox[0] = ((m->cmd << 8) | MBOX_HCONT);

                /* fill in the mailbox command parameters */ 
                for(i = 1; i < m->wsize; i++)
                        instance->mailbox[i] = m->params[i];

                /* with the command now written out to
                   the SLIM, we set the own bit */
                instance->mailbox[i] = (instance->mailbox[0] | MBOX_OWNER);

        }

        OUTL(CHIP_ATT_REG, HC_MBATT);  // set chip mailbox att...


        if(instance->sli == 1)
        {
                timeout = 0;
                while ( (INL(HOST_ATT_REG) & HC_MBATT) == 0) {
                        if (timeout++ == 4) {
                                printk(KERN_ERR "emulexfc: timed out waiting for MBOX command completion status\n");
                                return(-1);
                        }
                        mdelay(250);
        }

        /* Reset Host MBATT Flag before processing the data */

        OUTL(HOST_ATT_REG, HC_MBATT);

                /* copy mailbox back to our data buffer */
                m->status = READL(0) >> 16;
                for(i = 0; i < m->rsize; i++)
                        m->params[i] = READL(i*4);
                
                /* clear host contention bit to allow others
                   access to the mailbox */
                WRITEL(0, READL(0) & ~MBOX_HCONT);
        }
        else if(instance->sli == 2)
        {
                timeout = 0;
                while ( (instance->mailbox[0] & MBOX_OWNER) == 0) {
                        if (timeout++ == 4) {
                                printk(KERN_ERR "emulexfc: timed out waiting for MBOX command completion status\n");
                                return(-1);
                        }
                        mdelay(250);
        }

        /* Reset Host MBATT Flag before processing the data */

        OUTL(HOST_ATT_REG, HC_MBATT);

                /* copy mailbox back to our data buffer */
                m->status = instance->mailbox[0] >> 16;
                for(i = 0; i < m->rsize; i++)
                        m->params[i] = instance->mailbox[i];
                
                /* clear host contention bit to allow others
                   access to the mailbox */
                instance->mailbox[0] = instance->mailbox[0] & ~MBOX_HCONT;
        }
	return m->status;
}


#if DEBUG_LP6000

void lp6000_print_scsi_cmd(Scsi_Cmnd *cmd)
{
	int i;

	printk(KERN_INFO "emulexfc: target = 0x%02x, lun = 0x%02x, cmd_len = 0x%02x\n",
	       cmd->target, cmd->lun, cmd->cmd_len);
	printk(KERN_INFO "emulexfc: command = ");
	for (i = 0; i < cmd->cmd_len; i++)
		printk("0x%02x ", cmd->cmnd[i]);
	printk("\n");
}

#endif /* DEBUG_LP6000 */

void lp6000_dumpwords(void *p, int num, int type)
{
        int i;
        int linecount;
        int offset;
        u32 word;
        
        for (offset = 0; offset < num; offset += 8) {
                /* print offset */
                printk(KERN_INFO "%04x: ", offset);

                /* calculate how many words to display in this line */
                linecount = ((num - offset) >= 8 ? 8 : (num - offset));

                /* print hex words */
                for (i = 0; i < linecount; i++) {
                        switch(type) {
                        case DUMP_MEMORY:
                                word = (u32) *( (u32*)(p + (offset + i)*4) );
                                break;
                        case DUMP_MMIO:
                                word = (u32) readl( (u32*)(p + (offset + i)*4) );
                                break;
                        case DUMP_IO:
                                word = (u32) inl( (unsigned long)(p + (offset + i)*4) );
                                break;
                        default:
                                word = 0xDEADBEEF; /* for error detection :) */
                                break;
                        }
                        printk("%08x ", word);
                }                
                printk("\n");
        }
}

int mbox_read_rconfig(struct lp6000_hostdata *instance, struct mbox *mailbox, int ring_no)
{
        mailbox->cmd = MBOX_READ_RCONFIG;
        mailbox->wsize = 32;
        mailbox->rsize = 32;
        memset(mailbox->params, 0, 32*4);

        mailbox->params[1] = ring_no;
        return(lp6000_mbox_command(instance, mailbox));
}

int mbox_reset_ring(struct lp6000_hostdata *instance, struct mbox *mailbox, int ring_no)
{
        mailbox->cmd = MBOX_RESET_RING;
        mailbox->wsize = 32;
        mailbox->rsize = 32;
        memset(mailbox->params, 0, 32*4);

        mailbox->params[1] = ring_no;

        if (lp6000_mbox_command(instance, mailbox) != 0) 
                printk(KERN_ERR "emulexfc: MBOX_RESET_RING %d failed: status = 0x%08x\n", ring_no, mailbox->status);
        
        return(mailbox->status);
}

int mbox_restart(struct lp6000_hostdata *instance, struct mbox *m)
{
        unsigned int i, timeout;

        m->cmd = MBOX_RESTART;
        m->wsize = 32;
        m->rsize = 32;
        memset(m->params, 0, 32*4);

        m->params[1] = 0;

        if ((m->rsize > MBOX_MAX_SIZE) || (m->rsize < 1) ||
            (m->wsize > MBOX_MAX_SIZE) || (m->wsize < 1)) {
                printk(KERN_ERR "emulexfc: recieved oversized mailbox command\n");
                return(-1);
        }
        
        /* check that the mailbox is ready, owned by us,
         * and not in use by another driver
         */
        timeout = 0;
        while ( ((INL(STATUS_REG) & STATUS_MB) == 0) ||
                ((READL(0) & MBOX_OWNER) != 0) ) {
               
                if (timeout++ == 4) {
                        printk(KERN_ERR "emulexfc: timed out waiting for MBOX ready status\n");
                        return(-1);
                }
                mdelay(250); 
        }

        /* set host contention bit to lock out others from the mbox
           and set mailbox command */
        WRITEL(0, ((m->cmd << 8) | MBOX_HCONT));

        /* fill in the mailbox command parameters */ 
        for(i = 1; i < m->wsize; i++)
                WRITEL(i*4, m->params[i]);

        /* with the command now written out to
           the SLIM, we set the own bit */
        WRITEL(0, READL(0) | MBOX_OWNER);

        /* writing the mailbox attention bit would 
           be a smart move here. HC_MBATT */

        OUTL(CHIP_ATT_REG, HC_MBATT);  // set chip mailbox att...

        /* FIXME for now, we can be happy just
           waiting for the OWN bit to clear  */
        timeout = 0;
        while ( (INL(HOST_ATT_REG) & HC_MBATT) == 0) {
                if (timeout++ == 4*5) {
                        printk(KERN_ERR "emulexfc: timed out waiting for MBOX command completion status\n");
                        return(-1);
                }
                mdelay(250);
        }

        /* Reset Host MBATT Flag before processing the data */

        OUTL(HOST_ATT_REG, HC_MBATT);

        /* copy mailbox back to our data buffer */
        m->status = READL(0) >> 16;
        for(i = 0; i < m->rsize; i++)
                m->params[i] = READL(i*4);

        /* clear host contention bit to allow others
           access to the mailbox */
        WRITEL(0, READL(0) & ~MBOX_HCONT);

	return m->status;
}

int mbox_read_nvparms(struct lp6000_hostdata *instance, struct mbox *mailbox) {
        int retval;

        mailbox->cmd = MBOX_READ_NVPARMS;
        mailbox->wsize = 32;
        mailbox->rsize = 32;
        memset(mailbox->params, 0, 257*4);

        if (lp6000_mbox_command(instance, mailbox) != 0) {
                printk(KERN_ERR "emulexfc: MBOX_READ_NVPARMS failed: status = %d\n",
                       mailbox->status);
                retval = -1;
                return(retval);
        }
        printk(KERN_INFO "emulexfc: N_PORT name    = 0x%0x%0x\n",
               mailbox->params[5], mailbox->params[6]);
        printk(KERN_INFO "emulexfc: NODE name      = 0x%0x%0x\n",
               mailbox->params[7], mailbox->params[8]);
        printk(KERN_INFO "emulexfc: Preferred D_ID = 0x%0x\n",
               mailbox->params[9] >> 8);
        printk(KERN_INFO "emulexfc: Hard AL_PA     = 0x%0x\n",
               mailbox->params[9] & 0xff);

        return(mailbox->status);
}

int mbox_read_rev(struct lp6000_hostdata *instance, struct mbox *mailbox) {
        int retval;

        mailbox->cmd = MBOX_READ_REV;
        mailbox->wsize = 32;
        mailbox->rsize = 32;
        memset(mailbox->params, 0, 32*4);

        /* specify that we want SLI-1 and SLI-2 information. */
/*        mailbox->params[1] = (1 << 31);*/

        if (lp6000_mbox_command(instance, mailbox) != 0) {
                printk(KERN_ERR "emulexfc: MBOX_READ_REV failed: status = %d\n",
                       mailbox->status);
                retval = -1;
                return(retval);
        }
        

        printk("Card supports SLI level: %d\n", instance->sli);

        if(instance->sli == 1) {
                printk("BIU rev: %d\n", mailbox->params[2] & 0xF);
                printk("Sequence Manager rev: %x\n",mailbox->params[3]);
                printk("Version %d.%d.%d%c, S/N: %d\n", 
                       (mailbox->params[4] >> 24),
                       (mailbox->params[4] >> 20) & 0xF,
                       (mailbox->params[4] >> 16) & 0xF,
                       (mailbox->params[4] >> 8) & 0xFF,
                       (mailbox->params[4]) && 0xFF);
                printk("ENDEC+ rev: %x\n",mailbox->params[6]);
                printk("FC-PH level: %d-%d\n",
                       mailbox->params[5] & 0xFF,
                       (mailbox->params[5] >> 8) & 0xFF);
                printk("POST-Kernel version %d.%d.%d%c, S/N: %d\n",
                       (mailbox->params[7] >> 24),
                       (mailbox->params[7] >> 20) & 0xF,
                       (mailbox->params[7] >> 16) & 0xF,
                       (mailbox->params[7] >> 8) & 0xFF,
                       (mailbox->params[7]) && 0xFF);
                printk("Operational firmware rev: %d\n", mailbox->params[8]);
                printk("Desc: %s\n", (char *)&mailbox->params[9]);
                
        } else if(instance->sli == 2) {
                printk("BIU rev: %x\n", mailbox->params[2] & 0xF);
                printk("Sequence Manager rev: %x\n",mailbox->params[3]);
                printk("Operational Firmware: ");
                print_sli2_vers(mailbox->params[4]);

                printk("ENDEC+ rev: %x\n",mailbox->params[6]);
                printk("Feature level: %d-%d\nFC-PH level: %d-%d\n",
                       (mailbox->params[5] >> 16) & 0xFF,
                       (mailbox->params[5] >> 24) & 0xFF,
                       mailbox->params[5] & 0xFF,
                       (mailbox->params[5] >> 8) & 0xFF);
                printk("POST-Kernel rev information: ");
                print_sli2_vers(mailbox->params[7]);
                printk("Operational firmware rev: ");
                print_sli2_vers(mailbox->params[8]);
                printk("Desc: %s\n", (char *)&mailbox->params[9]);
                printk("SLI-2 Firmware: ");
                print_sli2_vers(mailbox->params[18]);
        }
        return mailbox->status;
}

void print_sli2_vers(u32 reg) {
        u32 temp;

        switch(reg >> 24) {
        case 2:
                printk("Initial operational firmware load");
                break;
        case 6:
                printk("SLI-1 operational firmware feature overlay");
                break;
        case 7:
                printk("SLI-2 operational firmware feature overlay");
                break;
        }
        printk(", Program ID: %d, version: %d.%d.%d",
               (reg >> 16) & 0xFF,
               (reg >> 12) & 0xF,
               (reg >> 8) & 0xF,
               (reg >> 6) & 3);  /* only 2 bits for the fix version! */
        
        temp = reg & 0xF; /* distro count */
        switch((reg >> 4) & 3) {
        case 0:
                printk("N%d", temp);
                break;
        case 1:
                printk("A%d", temp);
                break;
        case 2:
                printk("B%d", temp);
                break;
        case 3:
                if(temp > 0) {
                        printk("X%d", temp);
                }
                break;
        default:
                printk("?%d", temp);
                break;
        }
        printk("\n");
}

int get_biu_vers(struct lp6000_hostdata *instance) {
        struct mbox mailbox;
        int retval;

        mailbox.cmd = MBOX_READ_REV;
        mailbox.wsize = 32;
        mailbox.rsize = 32;
        memset(mailbox.params, 0, 32*4);

        if (lp6000_mbox_command(instance, &mailbox) != 0) {
                printk(KERN_ERR "emulexfc: MBOX_READ_REV failed: status = %d\n",
                       mailbox.status);
                retval = -1;
        }

        printk("BIU rev: %x\n", mailbox.params[2]);

        /* the BIU version is stored in mailbox word 2 */
        return mailbox.params[2];
}

int mbox_config_port(struct lp6000_hostdata *instance, struct mbox *mailbox)
{
        mailbox->cmd = MBOX_CONFIG_PORT;
        mailbox->wsize = 32;
        mailbox->rsize = 32;
        memset(mailbox->params, 0, 32*4);

        mailbox->params[2] = (int) instance->pcb;

        if(lp6000_mbox_command(instance, mailbox) != 0) {
                printk("emulexfc: CONFIG_PORT failed, ");
                switch(mailbox->status)
                {
                case 0x0001:
                        printk("Invalid number of rings specified.\n");
                        break;
                case 0x0010:
                        printk("Invalid number of IOCBs specified.\n");
                        break;
                case 0x0014:
                        printk("PCB not 32-bit word aligned.\n");
                        break;
                default:
                        printk("Undocumented error 0x%08x.\n", (instance->mailbox[0] >> 16));
                }
        }

        return(mailbox->status);
}

void dump_resp_rings(struct lp6000_hostdata *instance)
{
        struct lp6000_iocb iocb;
        unsigned int temp, i, j;

        for(j=0; j< ENTRIES_PER_RING; j++)
        {
                for(i=0; i<=5; i++)
                        iocb.payload[i] = READL(instance->iocb_rings[1] +
                                                (j * 32) +
                                                (i * 4));
                temp = READL(instance->iocb_rings[1] +
                             (j * 32) +
                             (6*4));
                iocb.context_tag = ((temp >> 16) & 0xffff);
                iocb.io_tag = (temp & 0xffff);
                temp = READL(instance->iocb_rings[1] +
                             (j * 32) +
                             (7*4));
                iocb.misc2 = (temp & 0xff);
                iocb.command = ((temp >> 8) & 0xff);
                iocb.misc1 = ((temp >> 16) & 0xff);
                
                printk("emulexfc: Ring 0:%d = 0x%02x, 0x%04x, 0x%04x, 0x%04x\n", j, iocb.command, iocb.io_tag, iocb.context_tag, (iocb.misc2 >> 4));
        }
#if 0               
        for(i=0; i<=5; i++)
                iocb.payload[i] = READL(instance->iocb_rings[3] +
                                         (instance->iocb_cur[3] * 32) +
                                         (i * 4));
        temp = READL(instance->iocb_rings[3] +
                     (instance->iocb_cur[3] * 32) +
                     (6*4));
        iocb.context_tag = ((temp >> 16) & 0xffff);
        iocb.io_tag = (temp & 0xffff);
        temp = READL(instance->iocb_rings[3] +
                     (instance->iocb_cur[3] * 32) +
                     (6*4));
        iocb.misc2 = (temp & 0xff);
        iocb.command = ((temp >> 8) & 0xff);
        iocb.misc1 = ((temp >> 16) & 0xff);
        
        printk("emulexfc: Ring 1 command     = 0x%02x\n", iocb.command);
        printk("emulexfc: Ring 1 io_tag      = 0x%04x\n", iocb.io_tag);
        printk("emulexfc: Ring 1 context_tag = 0x%04x\n", iocb.context_tag);
        printk("emulexfc: Ring 1 status      = 0x%04x\n", (iocb.misc2 >> 4));
        
        for(i=0; i<=5; i++)
                iocb.payload[2] = READL(instance->iocb_rings[5] +
                                         (instance->iocb_cur[5] * 32) +
                                         (i * 4));
        temp = READL(instance->iocb_rings[5] +
                     (instance->iocb_cur[5] * 32) +
                     (6*4));
        iocb.context_tag = ((temp >> 16) & 0xffff);
        iocb.io_tag = (temp & 0xffff);
        temp = READL(instance->iocb_rings[5] +
                     (instance->iocb_cur[5] * 32) +
                     (6*4));
        iocb.misc2 = (temp & 0xff);
        iocb.command = ((temp >> 8) & 0xff);
        iocb.misc1 = ((temp >> 16) & 0xff);
        
        printk("emulexfc: Ring 2 command     = 0x%02x\n", iocb.command);
        printk("emulexfc: Ring 2 io_tag      = 0x%04x\n", iocb.io_tag);
        printk("emulexfc: Ring 2 context_tag = 0x%04x\n", iocb.context_tag);
        printk("emulexfc: Ring 2 status      = 0x%04x\n", (iocb.misc2 >> 4));
#endif

        return;
}

void dump_cmnd_rings(struct lp6000_hostdata *instance)
{
        struct lp6000_iocb iocb;
        unsigned int temp, i, j;

        for(j=0; j< ENTRIES_PER_RING; j++)
        {
                for(i=0; i<=5; i++)
                        iocb.payload[i] = READL(instance->iocb_rings[0] +
                                                (j * 32) +
                                                (i * 4));
                temp = READL(instance->iocb_rings[0] +
                             (j * 32) +
                             (6*4));
                iocb.context_tag = ((temp >> 16) & 0xffff);
                iocb.io_tag = (temp & 0xffff);
                temp = READL(instance->iocb_rings[0] +
                             (j * 32) +
                             (7*4));
                iocb.misc2 = (temp & 0xff);
                iocb.command = ((temp >> 8) & 0xff);
                iocb.misc1 = ((temp >> 16) & 0xff);
                
                printk("emulexfc: CMDRing 0:%d = 0x%02x, 0x%04x, 0x%04x, 0x%04x\n", j, iocb.command, iocb.io_tag, iocb.context_tag, (iocb.misc2 >> 4));
        }
        
        return;
}



#ifdef MODULE
Scsi_Host_Template driver_template = EMULEXFC;

#include "scsi_module.c"
#endif /* MODULE */

/*
 * Overrides for Emacs so that we almost follow Linus's tabbing style.
 * Emacs will notice this stuff at the end of the file and automatically
 * adjust the settings for this buffer only.  This must remain at the end
 * of the file.
 * ---------------------------------------------------------------------------
 * Local variables:
 * c-indent-level: 4
 * c-brace-imaginary-offset: 0
 * c-brace-offset: -4
 * c-argdecl-indent: 4
 * c-label-offset: -4
 * c-continued-statement-offset: 4
 * c-continued-brace-offset: 0
 * indent-tabs-mode: nil
 * tab-width: 8
 * End:
 */

