/* ia_flip.c */

/* Copyright (c) 2000  James M. Westall, Dept of Computer Science,
 *                     Clemson University, Clemson SC 29634 USA
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * To obtain a copy of the GNU General Public License write to the
 * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * This software is derived from software developed by the Interphase
 * corporation and released by them in September 2000. The software
 * released by Interphase carried the following copyright notice:
 *
 *
 *                          Copyright (C) 1993
 *                Interphase Corporation, Dallas, TX 75234
 *                          All Rights Reserved
 *
 * This code contains confidential information and  trade  secrets  of
 * Interphase Corporation which shall not be reproduced or transferred
 * to other programs or disclosed to others or used for  manufacturing
 * or any other purpose without prior written permission of Interphase
 * Corporation.  Use of copyright notice is precautionary and does not
 * imply publication or intent thereof.
 */

/*
 * FILE: ia_flip.c
 *
 * DESCRIPTION:
 *   This file contains all the device driver entry points
 *   the main interrupt handler
 *
 * FUNCTIONS:
 * EXPORTED:
 *   see below.
 *
 * STATIC:
 *   fsee below.
 *
 * GLOBAL DATA:
 *
 */

#include "ia_defs.h"

#define MEM_VALID 0xfffffff0
#define IA5515_MAJOR 17

MODULE_AUTHOR("Mike Westall <westall@cs.clemson.edu>");
MODULE_PARM(interface, "1i");

static int interface = 0;

extern struct timeval xtime;

ia_softc_t *softc;

/*
 * Configuration
 */
uint_t   ia_debug = 0;        /* If set -> Print info useful for debug */
uint_t   ia_xoff_disable = 0; /* If set -> Do not enable link XON/XOFF */
uint_t   ia_revb = 1;         /* If set -> Initialize for Rev-B hardware */
uint_t   ia_stm = 0;          /* If set -> STM-1 mode (Europe's SONET) */
uint_t   ia_rx_putq = 0;      /* If set -> Do a putq from intr. on RX */
uint_t   ia_tx_vpi = 0;       /* VPI for transmission */
uint_t   ia_aal5 = 1;

/* A character device interface for initialization */
/* and management operations is provided here.     */

static struct file_operations ia5515_fops =
{
#if LINUX_VERSION_CODE >= 0x20312
   NULL,          /* module owner       */
#endif
   NULL,          /* lseek              */
   NULL,          /* read               */
   NULL,          /* write              */
   NULL,          /* readdir            */
   NULL,          /* select             */
   ia_ioctl,      /* ioctl              */
   NULL,          /* mmap               */
   ia_open,       /* open               */
   NULL,          /* flush              */
   ia_release,    /* release            */
   NULL,          /* fsync              */
   NULL,          /* fasync             */
   NULL,          /* check_media_change */
   NULL           /* revalidate         */
};

static void
ia_rx_free (void *rx_bufp)
{

}

/*F
 * FUNCTION: ia_intr
 *
 * DESCRIPTION:
 * This is the main interrupt routine. Read the SMUX interrupt
 * status register and determine which of the 3 sources are
 * interrupting. Call other routines to do the real work.
 *
 * ARGUMENTS:
 * arg   Pointer to the device statue structure
 *
 * ENVIRONMENT:
 * Called when an interrupt is received from the controller
 *
 * RETURN:
 * DDI_INTR_CLAIMED if out controller interrupting
 * DDI_INTR_UNCLAIMED if our controller not interrupting
 *
 * NOTES:
 * SMUX interrupt 0 - Addgen interrupt
 * SMUX interrupt 1 - F-FRED/Front-End interrupt
 * SMUX interrupt 2 - R-FRED interrupt
 *
 */
void
ia_intr(
int     irq,
void    *arg)
{
   ia_softc_t  *softc = (ia_softc_t *)arg;
   ffred_t     *ffred = softc->brd_regs.ffred;
   rfred_t     *rfred = softc->brd_regs.rfred;
   ia_suni_t   *suni  = softc->brd_regs.suni;
   uint_t      flip_intr_status;
   uint_t      ffred_intr_status;
   uint_t      rfred_intr_status;
   unsigned int lockflags;


/*
 * Check to see if our controller in interrupting.
 */
   flip_intr_status = ia_get32(&softc->brd_regs.flipper->fl_status);
   flip_intr_status &= ~FL_STAT_RESERVED;

#ifdef DBG_INTR
   printk("IA 5515: I stat is %x \n", flip_intr_status);
#endif

/*
 * If this is not our interrupt... return now.
 */
   if (flip_intr_status == 0)
   {
      return;
   }

   softc->driver_stat.interrupts++;
/*
 * We run this stuff in a loop so that if another interrupt becomes
 * pending while we handle this one, we can go ahead and process it
 */

   while (flip_intr_status != 0)
   {
   /*
    * R-FRED interrupt?
    */
      if (flip_intr_status & FL_STAT_RFREDINT)
      {
#ifdef DBG_INTR
         printk("IA 5515: RFred int stat is %x \n", rfred_intr_status);
#endif
         spin_lock_irqsave(&softc->recvlock, lockflags);
         softc->driver_stat.rfred_intr++;
         ia_rfred_handler(softc);
         spin_unlock_irqrestore(&softc->recvlock, lockflags);
      }

   /*
    * F-FRED interrupt?
    */

      if (flip_intr_status & FL_STAT_FFREDINT)
      {
         uint_t ffred_intr_status;

         softc->driver_stat.tfred_intr++;
         ffred_intr_status = ia_getw(&ffred->intr_status_reg);
         ffred_intr_status &= 0xffff;
#ifdef DBG_INTR
         printk("IA 5515: FFred int stat is %x \n", ffred_intr_status);
#endif
         ia_tx_tcq_intr(softc);
         /*
          * Has TCQ changed state from empty to non empty?
          */
         if (ffred_intr_status & F_TCQ_NOT_EMPTY)
         {
         /* printk("IA 5515: FFred TCQ interrupt \n"); */
         }
         else
         {
#ifdef DBG_INTR
            printk("IA 5515: unknown F-FRED intr \n");
#endif
         }
      }

   /*
    * Front-End interrupt?
    */
      if (flip_intr_status & FL_STAT_FEINT)
      {
         printk("SUNI stat was %x \n",
                        ia_get32(&suni->suni_master_intr_stat) & 0x7f);
         if (ia_get32 (&suni->suni_master_intr_stat) & 0x7f)
         {
            softc->driver_stat.suni_intr++;
           /* ia_suni_intr (softc); */
         }
         else
         {
            printk("IA 5515: unknown Front-end intr");
         }
      }

   /*
    * Receive DLE interrupt?
    */
      if (flip_intr_status & FL_STAT_DLERINT)
      {
         spin_lock_irqsave(&softc->recvlock, lockflags);
         softc->driver_stat.addgen_intr++;
         ia_put32 (&softc->brd_regs.flipper->fl_status, FL_STAT_DLERINT);
#ifdef DBG_INTR
         printk("IA 5515: DLE Receive interrupt \n");
#endif
         ia_host_rx_intr(softc);
         spin_unlock_irqrestore(&softc->recvlock, lockflags);
      }

   /*
    * Transmit DLE interrupt?
    */
      if (flip_intr_status & FL_STAT_DLETINT)
      {
         ia_put32(&softc->brd_regs.flipper->fl_status, FL_STAT_DLETINT);
#ifdef DBG_INTR
         printk("IA 5515: DLE Tx interrupt \n");
#endif
      }

      if (flip_intr_status & FL_STAT_ERRINT)
      {
         printk("IA 5515: FATAL PCI Error cmd/stat is is %x \n",
                    softc->brd_regs.config->fl_cfg_cmd_status);
         ia_card_reset(softc);
         break;

         ia_put32(&softc->brd_regs.flipper->fl_status, FL_STAT_ERRINT);
         if (ia_debug)
            printk("IA 5515: ia_intr: unknown error intr \n");
      }
      flip_intr_status = softc->brd_regs.flipper->fl_status;
      flip_intr_status &= ~FL_STAT_RESERVED;
   } while (flip_intr_status);

   return;
}

/* Give back allocated resources and shut the board down */

ia_close(
ia_softc_t *softc)
{
   ia_dma_t *dma;

   dma = &softc->tx_dma;

   if (dma->taddr)
      kfree(dma->taddr);

   dma = &softc->rx_dma;

   if (dma->taddr)
      kfree(dma->taddr);

   dma = &softc->tx_list_dma;
   if (dma->taddr)
      kfree(dma->taddr);

   dma = &softc->rx_list_dma;
   if (dma->taddr)
      kfree(dma->taddr);

   unregister_chrdev(IA5515_MAJOR, "ia5515");
   free_irq(softc->irq, softc);
   iounmap(softc->pci_base_v);

   kfree(softc);

}

/**/
/* Init VC structures used in managing standalone */
/* driver PVCs..                                  */

ia_init_pvcs(
ia_softc_t *softc)
{
   int i;
   ia_pvc_desc_t *pvc;

   pvc = softc->pvctab + 1;

   for (i = 1; i < NUM_PVCS; i++)
   {
#if LINUX_VERSION_CODE >= 0x20303
      init_waitqueue_head(&pvc->input_wait);
#else
      pvc->input_wait = NULL;
#endif
      pvc->rdybufhead = NULL;
      pvc->rdybuftail = NULL;
      pvc += 1;

   }
}
/*F
 * FUNCTION: ia_alloc_buffers
 *
 * DESCRIPTION:
 *   Allocate and dma map transmit and receive data buffers.
 *
 * ARGUMENTS:
 *   softc     device state structure
 *
 * ENVIRONMENT:
 *   Only called from ia_attach
 *
 * RETURN:
 * IA_SUCCESS  if able to allocate all the resources
 * IA_FAIL  on failure
 *
 * NOTES:
 *
 */
/**/
/* Current strategy relies upon permanently allocated kernel buffers */
/* through which all packets pass.. Yes, it does cost an extra copy. */
/* But its simple, reliable, and was used in the Solaris driver from */
/* which this stuff was derived. One day (maybe) we'll fix it.       */

int
ia_alloc_buffers (
ia_softc_t  *softc)
{
   caddr_t dmac_address;
   int     dmac_size;
   ia_dma_t *dma;
   int      err;
   size_t   total_len;
   int      offset;
   int      i, j;
   int      tx_buf_size;
   int      rx_buf_size;

/* TX buffers are used only for building CS trailer */
/* RX buffers are not presently used at all.        */

   tx_buf_size = 16;
   rx_buf_size = 16;

/*
 *------------------------
 * Set up transmit buffers
 *------------------------
 */
   dma = &softc->tx_dma;

   dma->len = tx_buf_size * softc->num_tx_bufs + 64;

#ifdef DBG_INIT
   printk("IA 5515: Preparing to alloc %d Tx bufs of len %d \n",
                    softc->num_tx_bufs, tx_buf_size);
#endif

   dma->addr = kmalloc(dma->len, GFP_DMA);
   dma->mem_alloc_done = 1;
   dma->taddr = dma->addr;

   offset = ROUND_UP(dma->addr, 64) - (int)dma->addr;
   dma->addr += offset;
   dma->real_len = dma->len - offset;
   dmac_size = dma->real_len;
   dmac_address = (caddr_t)virt_to_bus(dma->addr);

#ifdef DBG_INIT
   printk("IA 5515: TX pool at virt %x phys %x \n",
                    dma->addr, dmac_address);
#endif

   for (i = 0; i < softc->num_tx_bufs; i++)
   {
      softc->tx_buf[i].addr = dma->addr + i * tx_buf_size;
      softc->tx_buf[i].offset = i * tx_buf_size;

      total_len = tx_buf_size;
      for (j = 0; j < MAX_SGE; j++)
      {
         softc->tx_buf[i].daddr[j] = dmac_address;
#ifdef DBG_INIT
         printk("IA 5515: Tx %3d %3d %6d %6d \n",
                          i, j, dmac_address, dmac_size);
#endif
         if (dmac_size >= total_len)
         {
            softc->tx_buf[i].dlen[j] = total_len;
            dmac_address += total_len;
            dmac_size -= total_len;
            break;
         }
      }

      if (j == MAX_SGE)
      {
         printk("IA 5515: Tx DMA buffer mapping failure \n");
         return IA_FAIL;
      }
   }


/*
 *-----------------------
 * Set up receive buffers
 *-----------------------
 */
   dma = &softc->rx_dma;
   dma->len = rx_buf_size * NUM_RX_BUFS + 64;

#ifdef DBG_INIT
   printk("IA 5515: Preparing to alloc %d Rx bufs of len %d \n",
                    NUM_RX_BUFS, rx_buf_size);
#endif

   dma->addr = kmalloc(dma->len, GFP_DMA);
   dma->taddr = dma->addr;
   dma->mem_alloc_done = 1;

   offset = ROUND_UP(dma->addr, 64) - (int)dma->addr;
   dma->addr += offset;
   dma->real_len = dma->len - offset;

   dmac_size = dma->real_len;
   dmac_address = (caddr_t)virt_to_bus(dma->addr);
#ifdef DBG_INIT
   printk("IA 5515: Rx pool at virt %x phys %x \n",
                    dma->addr, dmac_address);
#endif

   for (i = 0; i < NUM_RX_BUFS; i++)
   {
      softc->rx_buf[i].addr = dma->addr + i * rx_buf_size;
      softc->rx_buf[i].offset = i * rx_buf_size;
      softc->rx_buf[i].softc = softc;
      softc->rx_buf[i].index = i;
      softc->rx_buf[i].next = &softc->rx_buf[i+1];

      total_len = rx_buf_size;
      for (j = 0; j < MAX_SGE; j++)
      {
         softc->rx_buf[i].daddr[j] = dmac_address;
#ifdef DBG_INIT
         printk("IA 5515: Rx %3d %3d %6d %6d \n",
                          i, j, dmac_address, dmac_size);
#endif
         if (dmac_size >= total_len)
         {
            softc->rx_buf[i].dlen[j] = total_len;
            dmac_address += total_len;
            dmac_size -= total_len;
            break;
         }
         else
         {
            j = MAX_SGE;
            break;
         }
      }

      if (j == MAX_SGE)
      {
         printk("IA 5515: Rx DMA buffer mapping failure \n");
         return IA_FAIL;
      }
   }
   softc->rx_buf[i-1].next = NULL;
   softc->rx_buf_free      = &softc->rx_buf[0];
   softc->rx_buf_freetail  = &softc->rx_buf[i-1];
   softc->rx_buf_dmahead   = NULL;
   softc->rx_buf_dmatail   = NULL;

   return IA_SUCCESS;
}


/**/
/* Initialization driver function */

ia_init(ia_softc_t *softc)
{

   caddr_t        vaddr;
   unsigned short command;
   unsigned short status;
   int            error;
   int            i;
   int            val;

   ia_card_type(softc);

#if LINUX_VERSION_CODE >= 0x20312
   softc->real_base = softc->ia_dev->resource[0].start;
#else
   softc->real_base = softc->ia_dev->base_address[0];
#endif

   softc->irq = softc->ia_dev->irq;

   printk("IA 5515: Memory base is %x \n", softc->real_base);
   printk("IA 5515: IRQ         is %x \n", softc->irq);

   if (pci_write_config_dword(softc->ia_dev,
         PCI_BASE_ADDRESS_0, 0xffffffff) !=  PCIBIOS_SUCCESSFUL)
   {
      printk("IA 5515: Base address write failed \n");
      return -EINVAL;
   }

   if(pci_read_config_dword(softc->ia_dev, PCI_BASE_ADDRESS_0,
            &(softc->pci_map_size)) != PCIBIOS_SUCCESSFUL)
   {
      printk("IA 5515: Base address read failed \n");
      return -EINVAL;
   }

   softc->pci_map_size &= PCI_BASE_ADDRESS_MEM_MASK;
   softc->pci_map_size = ~softc->pci_map_size + 1;
   printk("IA 5515: Memory size is %x \n", softc->pci_map_size);

/* Restore correct base address */

   if(pci_write_config_dword(softc->ia_dev, PCI_BASE_ADDRESS_0,
            softc->real_base) != PCIBIOS_SUCCESSFUL)
   {
      printk("IA 5515: Base address rewrite failed \n");
      return -EINVAL;
   }

 /* strip flags (last 4 bits )  ---> mask with 0xfffffff0 */

   softc->real_base &= MEM_VALID;

/* Enable the board */

   error = pci_read_config_word(softc->ia_dev, PCI_COMMAND, &command);

   command |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
   if ((error = pci_write_config_word(softc->ia_dev,
                                         PCI_COMMAND,  command)))
   {
      printk("IA 5515: Activate board failed  \n");
      return error;
   }

/*
 * Delay before doing any mem accesses
 */
   udelay(1000);

#ifdef DBG_INIT
   error = pci_read_config_word(softc->ia_dev, PCI_STATUS, &status);
   printk("IA 5515: ia_init: PCI STATUS is %x \n", status);

   pci_read_config_word(softc->ia_dev,  PCI_COMMAND, &command);
   printk("IA 5515: PCI command word is now %x\n", command);
   printk("IA 5515: Remapping from %x for %x \n",
                       softc->real_base, softc->pci_map_size);
#endif

/* map the physical address to a virtual address */

   vaddr  = softc->pci_base_v =
               (caddr_t)ioremap((unsigned long)softc->real_base,
                          softc->pci_map_size);

   if (!vaddr)
   {
      printk("IA 5515: ioremap failed... goodbye \n");
      return(-1);
   }

#ifdef DBG_INIT
   printk("IA 5515: Board mapped at virtual %x \n", vaddr);
#endif

   softc->ffred_map.f_num_vc = F_NUM_VC_128K;
   softc->brd_regs.ffred     = (ffred_t *)(vaddr + FFRED_OFFSET);
   softc->brd_regs.rfred     = (rfred_t *)(vaddr + RFRED_OFFSET);
   softc->brd_regs.ctrl      = (uint_t *)(vaddr + FECR_OFFSET);
   softc->brd_regs.suni      = (ia_suni_t *)(vaddr + SUNI_OFFSET);
   softc->brd_regs.flipper   = (ia_flip_t *)(vaddr + FLIP_OFFSET);
   softc->brd_regs.config    = (fl_config_t *)(vaddr + CONFIG_OFFSET);
   softc->brd_regs.tx_tc     = (uint_t *)(vaddr + TX_TC_OFFSET);
   softc->brd_regs.rx_tc     = (uint_t *)(vaddr + RX_TC_OFFSET);
   softc->brd_regs.ffred_mem = (caddr_t)(vaddr + FFRED_MEM_OFFSET);
   softc->brd_regs.rfred_mem = (caddr_t)(vaddr + RFRED_MEM_OFFSET);

   memset(softc->brd_regs.ffred_mem, 0, 1024);
   softc->brd_regs_ph.buf_mem = 0;
   softc->brd_regs_ph.ffred_mem = 0;
   softc->brd_regs_ph.rfred_mem = 0;

/*
 * Find the hardware type (FE type and buffer memory size)
 */
   ia_hw_type(softc);

/*
 * Allocate memory for board buffers
 */

   error = ia_alloc_buffers(softc);
   if (error == IA_FAIL)
   {
      printk("IA 5515: Exiting due to buffer init error \n");
      return(-1);
   }

/*
 * Initialize Flipper and DMA
 */

#ifdef DBG_INIT
   printk("IA 5515: Calling flip_init \n");
#endif

   error = ia_flip_init(softc);
   if (error == IA_FAIL)
   {
      printk("IA 5515: Exiting due to flipper init error \n");
      return(-1);
   }

#ifdef DBG_INIT
   printk("IA 5515: Calling fred_init \n");
#endif

   error = ia_ffred_init(softc);
   if (error == IA_FAIL)
   {
      printk("IA 5515: Exiting due to ffredr init error \n");
      return(-1);
   }

   error = ia_rfred_init(softc);
   if (error == IA_FAIL)
   {
      printk("IA 5515: Exiting due to rfredr init error \n");
      return(-1);
   }

/*
 * Clear front end reset.
 */
   ia_or32(softc->brd_regs.ctrl, FECR_RESET);
   udelay((clock_t)1000);

/*
 * Initialize SUNI
 */
   ia_suni_init (softc);

/*
 * Get/Set MAC address
 */
   val = ia_get32(&softc->brd_regs.flipper->fl_mac1);

   softc->mac_addr[0] = (val      ) & 0xff;
   softc->mac_addr[1] = (val >>  8) & 0xff;
   softc->mac_addr[2] = (val >> 16) & 0xff;
   softc->mac_addr[3] = (val >> 24) & 0xff;
   val = ia_get32(&softc->brd_regs.flipper->fl_mac2);
   softc->mac_addr[4] = (val      ) & 0xff;
   softc->mac_addr[5] = (val >>  8) & 0xff;
   printk("IA 5515: MAC address ");
   for (i=0; i<6; i++)
      printk("%02x:",softc->mac_addr[i]);
   printk("\n");
   return(0);
}

char msg[] = "Hello 5515";

/**/
/* Main entry point for module initialization */

int init_module(void)
{

   unsigned char     bus = 0;
   unsigned char     devfn = 0;
   struct   pci_dev *pci_dev;
   struct   pci_dev *ia_dev;
   int               found = 0;
   int               rc;
   aal_parms_t       aal;
   int               i;
   u32               stat;

   printk("IA 5515: Alloc buffers is at %x \n", ia_alloc_buffers);

/* See if we can find a board */

   printk("IA 5515: Module initialization beginning \n");
   if (!pci_present())
   {
      printk("IA 5515: No PCI Bios!\n");
      return(-1);
   }

   printk("IA 5515: Bios search for device \n");

   pci_dev = NULL;
   while (pci_dev = pci_find_device(PCI_VENDOR_ID_IPHASE,
                         PCI_DEVICE_ID_IPHASE_5515, pci_dev))
   {
      ia_dev = pci_dev;
      found += 1;
   }

/* For now.. don't want to worry about multiple board support */

   if (found != 1)
   {
      printk("IA 5515: Found %d adapters \n" , found);
      printk("IA 5515: Exactly ONE adapters is required \n");
      return(-1);
   }

   bus = ia_dev->bus->number;
   devfn = ia_dev->devfn;

   printk("IA 5515: Found 1 adapters at bus %d devfn %d  \n",
                        ia_dev->bus->number, ia_dev->devfn);

/* Allocate the device management data structure ... this */
/* thing is referred to as "softc" throughout             */

   softc = kmalloc(sizeof(ia_softc_t), GFP_KERNEL);
   if (softc == 0)
   {
      printk("IA 5515: softc memory allocation failed... goodbye \n");
      return(-1);
   }
   memset(softc, 0, sizeof(ia_softc_t));
   softc->ia_dev = ia_dev;
#ifdef DBG_INIT
   printk("IA 5515: softc at %x  \n", softc);
#endif


/* Attempt to initialize the device */

   rc = ia_init(softc);
   if (rc)
   {
      printk("IA5515: Device initialization failed.. goodbye \n");
      return(-1);
   }

   rc = request_irq(ia_dev->irq, (void *)ia_intr,
                       SA_INTERRUPT | SA_SHIRQ, "ia5515", softc);
   if (rc)
   {
      printk("IA 5515: Can't allocate IRQ %d. Code was %d \n",
                         ia_dev->irq, rc);
      return -EIO;
   }

   udelay(100);

#ifdef DBG_INIT
   printk("IA 5515: PCI CMD/STAT is %x \n",
                    softc->brd_regs.config->fl_cfg_cmd_status);
   printk("IA 5515: Flipper control reg is %x \n",
                    softc->brd_regs.flipper->fl_ctrl);
   printk("IA 5515: Flipper status  reg is %x \n",
                    softc->brd_regs.flipper->fl_status);

   printk("IA 5515: FFred  interrupt reg is %x \n",
                    ia_getw(&softc->brd_regs.ffred->intr_status_reg));
   printk("IA 5515: FFred  control reg is %x \n",
                    ia_getw(&softc->brd_regs.ffred->cmd_reg));
   printk("IA 5515: FFred  state   reg is %x \n",
                    ia_getw(&softc->brd_regs.ffred->state_reg));

   printk("IA 5515: FFred  prq rd ptr is %x \n",
                    ia_getw(&softc->brd_regs.ffred->prq_rd_ptr));
   printk("IA 5515: FFred  prq wt ptr is %x \n",
                    ia_getw(&softc->brd_regs.ffred->prq_wr_ptr));
   printk("IA 5515: FFred  prq st ptr is %x \n",
                    ia_getw(&softc->brd_regs.ffred->prq_st_adr));
   printk("IA 5515: FFred  prq ed ptr is %x \n",
                    ia_getw(&softc->brd_regs.ffred->prq_ed_adr));

   printk("IA 5515: FFred  tcq rd ptr is %x \n",
                    ia_getw(&softc->brd_regs.ffred->tcq_rd_ptr));
   printk("IA 5515: FFred  tcq wt ptr is %x \n",
                    ia_getw(&softc->brd_regs.ffred->tcq_wr_ptr));
   printk("IA 5515: FFred  tcq st ptr is %x \n",
                    ia_getw(&softc->brd_regs.ffred->tcq_st_adr));
   printk("IA 5515: FFred  tcq ed ptr is %x \n",
                    ia_getw(&softc->brd_regs.ffred->tcq_ed_adr));

#endif
/* Initialize locks */

   spin_lock_init(&softc->xmitlock);
   spin_lock_init(&softc->recvlock);
   spin_lock_init(&softc->xramlock);

/* Register ourselves with the Linux ATM stack... or export an ioctl() */
/* character device interface                                          */

   if (rc = ia5515_register(softc, interface))
   {
      printk( "IA_5515: cannot register atm device %d\n", rc);
      return(-1);
   }

   if (rc = register_chrdev(IA5515_MAJOR, "ia5515", &ia5515_fops))
   {
      printk( "IA_5515: cannot register device /dev/ia5515: received %d\n", rc);
      return(-1);
   }

   softc->last_tx_vcc = 32;
   for (i = 0; i < NUM_VCCS; i++)
      skb_queue_head_init(&softc->vcctab[i].tx_backlog);


   softc->tod = &xtime;
   do_gettimeofday(&softc->tv);

   printk("IA_5515: Intialized at %d.%d \n", softc->tv.tv_sec,
                                             softc->tv.tv_usec);

/* ia_close(softc); */

   return(0);
}

int cleanup_module(void)
{
   ia5515_deregister(softc);
   ia_card_reset (softc);
   udelay(500);
   ia_close(softc);
}
