/**
 * @file avmarm.c
 *
 * AvmArm - Common functions for ARM based AVM boards C2 and C4.
 *
 * Copyright: 2000-2003 Thomas Wintergerst. All rights reserved.
 *
 * $FreeBSD$
 * $Id: avmarm.c,v 1.1.2.1 2006/02/28 21:35:08 thomas Exp $
 * Project  CAPI for BSD
 * Target   avmaic - CAPI manager driver for AVM active ISDN controllers
 * @date    01.01.2000
 * @author  "Thomas Wintergerst" <twinterg@gmx.de>
 * <p>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * </p>
 */

#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");

/* system includes */
#include <sys/param.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/errno.h>
#include <machine/bus.h>
#include <sys/bus.h>
#include <sys/rman.h>
#include <sys/endian.h> /* htole32(), le32toh() */
#include <vm/vm.h>
#include <vm/pmap.h>
#include <machine/resource.h>
#include <machine/clock.h>

/* import includes */
#include <c4b/kcapimgr/capi_drv.h>

#define __AVMARM__

/* local includes */
#include <c4b/avmaic/avmaic_var.h>
#include <c4b/avmaic/avmlli.h>
#include <c4b/avmaic/avmio.h>
#include <c4b/avmaic/avmdma.h>
#include <c4b/avmaic/avmaic_misc.h>
#include <c4b/avmaic/avmarm.h>





/* === public definitions ================================================ */





/* === private definitions =============================================== */





/* --- definitions for registers of ARM-based controllers --- */

#define AVMAIC_DC21285_DRAM_A0MR        0x40000000
#define AVMAIC_DC21285_DRAM_A1MR        0x40004000
#define AVMAIC_DC21285_DRAM_A2MR        0x40008000
#define AVMAIC_DC21285_DRAM_A3MR        0x4000C000
#define AVMAIC_DC21285_ARMCSR_BASE      0x42000000

#define AVMAIC_CAS_OFFSET               0x88

#define AVMAIC_ARM_REG_PCI_OUT_INT_STATUS       0x30
#define AVMAIC_ARM_REG_PCI_OUT_INT_MASK         0x34
#define AVMAIC_ARM_REG_MAILBOX_0                0x50
#define AVMAIC_ARM_REG_MAILBOX_1                0x54
#define AVMAIC_ARM_REG_MAILBOX_2                0x58
#define AVMAIC_ARM_REG_MAILBOX_3                0x5C
#define AVMAIC_ARM_REG_DOORBELL                 0x60
#define AVMAIC_ARM_REG_DOORBELL_SETUP           0x64

#define AVMAIC_CHAN_1_CONTROL           0x090
#define AVMAIC_CHAN_2_CONTROL           0x0B0
#define AVMAIC_DRAM_TIMING              0x10C
#define AVMAIC_DRAM_ADDR_SIZE_0         0x110
#define AVMAIC_DRAM_ADDR_SIZE_1         0x114
#define AVMAIC_DRAM_ADDR_SIZE_2         0x118
#define AVMAIC_DRAM_ADDR_SIZE_3         0x11C
#define AVMAIC_SA_CONTROL               0x13C
#define AVMAIC_XBUS_CYCLE               0x148
#define AVMAIC_XBUS_STROBE              0x14C
#define AVMAIC_DBELL_PCI_MASK           0x150
#define AVMAIC_DBELL_SA_MASK            0x154

#define AVMAIC_ARM_REG_MBOX_PEEK_POKE   AVMAIC_ARM_REG_MAILBOX_0

#define AVMAIC_DBELL_ADDR               0x01
#define AVMAIC_DBELL_DATA               0x02
#define AVMAIC_DBELL_RNWR               0x40
#define AVMAIC_DBELL_INIT               0x80

#define AVMAIC_ARM_REG_MBOX_UP_ADDR     AVMAIC_ARM_REG_MAILBOX_0
#define AVMAIC_ARM_REG_MBOX_UP_LEN      AVMAIC_ARM_REG_MAILBOX_1
#define AVMAIC_ARM_REG_MBOX_DOWN_ADDR   AVMAIC_ARM_REG_MAILBOX_2
#define AVMAIC_ARM_REG_MBOX_DOWN_LEN    AVMAIC_ARM_REG_MAILBOX_3

#define AVMAIC_DBELL_UP_HOST    0x00000100
#define AVMAIC_DBELL_UP_ARM     0x00000200
#define AVMAIC_DBELL_DOWN_HOST  0x00000400
#define AVMAIC_DBELL_DOWN_ARM   0x00000800
#define AVMAIC_DBELL_RESET_HOST 0x40000000
#define AVMAIC_DBELL_RESET_ARM  0x80000000

#define AVMAIC_DRAM_TIMING_DEF  0x001A01A5
#define AVMAIC_DRAM_AD_SZ_DEF0  0x00000045
#define AVMAIC_DRAM_AD_SZ_NULL  0x00000000

#define AVMAIC_SA_CTL_ALLRIGHT  0x64AA0271

#define AVMAIC_INIT_XBUS_CYCLE  0x100016DB
#define AVMAIC_INIT_XBUS_STROBE 0xF1F1F1F1





/* === prototypes for private functions ================================== */





/* --- functions for direct communication with the board --- */

/* read one discrete 32-bit value from a memory i/o location */
static __inline u_int32_t avmarm_mem_readl
   (bus_space_tag_t    memt,
    bus_space_handle_t memh,
    u_int32_t          ulOffset);

/* write one discrete 32-bit value to a memory i/o location */
static __inline void avmarm_mem_writel
   (bus_space_tag_t    memt,
    bus_space_handle_t memh,
    u_int32_t          ulOffset,
    u_int32_t          ulValue);

/**
 * Wait for board to be able to accept commands.
 *
 * @param memt                  I: Bus tag for i/o operations.
 * @param memh                  I: Bus handle for i/o operations.
 * @param iTimeout              I: Timeout in ticks (i.e. "<seconds> * HZ") for
 *                                 the operation.
 *
 * @retval 0                    Success, the board is able to accept commands.
 * @retval Else                 Failure, the board is not ready.
 */
static int avmarm_wait_for_doorbell
   (bus_space_tag_t    memt,
    bus_space_handle_t memh,
    int                iTimeout);

/* write a 32-bit value to a board-internal memory location */
static int avmarm_peek                  /* 0: peek operation successful
                                         * else: failure
                                         */
   (bus_space_tag_t     memt,           /* I: bus tag for communication */
    bus_space_handle_t  memh,           /* I: bus handle for communication */
    u_int32_t           ulOffset,       /* I: the internal memory address to
                                         *    read from
                                         */
    u_int32_t          *pulValue);      /* O: the value read if result 0 */

/* read a 32-bit value from a board-internal memory location */
static int avmarm_poke                  /* 0: poke operation successful
                                         * else: failure
                                         */
   (bus_space_tag_t    memt,            /* I: bus tag for communication */
    bus_space_handle_t memh,            /* I: bus handle for communication */
    u_int32_t          ulAddr,          /* I: the internel memory address to
                                         *    write to
                                         */
    u_int32_t          ulValue);        /* I: the value to write */



/* --- performing board specific parts of CAPI calls --- */

/* register a CAPI application at a controller */
static unsigned avmarm_app_register
   (AvmAicSc_t       *pSc,
    AvmAicPortData_t *pPortData,
    unsigned          uMaxLogicalConnections,
    unsigned          uMaxBDataBlocks,
    unsigned          uMaxBDataLen,
    unsigned          uApplID);

/* release a CAPI application at a controller */
static unsigned avmarm_app_release
   (AvmAicSc_t       *pSc,
    AvmAicPortData_t *pPortData,
    unsigned          uApplID);

/* send a CAPI message to the controller */
static unsigned avmarm_put_message
   (AvmAicSc_t       *pSc,
    AvmAicPortData_t *pPortData,
    unsigned          uApplID,
    struct mbuf      *pmbMsg);

/* reset and download a controller */
static unsigned avmarm_reset_ctlr
                                        /* CAPI_OK or CAPI error value */
   (AvmAicSc_t *pSc,                    /* I: softc structure ident. the board
                                         */
    size_t               nNumDataBlocks,/* I: Number of data blocks to be sent
                                         *    to the controller; length of
                                         *    paDataBlocks
                                         */
    CAPICtlrDataBlock_t *paDataBlocks); /* I: Array of data blocks to be sent
                                         *    to the controller
                                         */



/* --- downloading software and configuration to a board --- */

/* download the firmware to the board */
static unsigned avmarm_load_firmware    /* CAPI_OK: download successful
                                         * else: failure
                                         */
   (AvmAicSc_t    *pSc,                 /* I: the softc struct. of the board */
    size_t         nLenData,            /* I: the length of the firmware at
                                         *    paucData
                                         */
    unsigned char *paucData);           /* I: the firmware data */

/* download the configuration to the board */
static unsigned avmarm_load_config      /* CAPI_OK: download successful
                                         * else: failure
                                         */
   (AvmAicSc_t    *pSc,                 /* I: the softc struct. of the board */
    size_t         nlenData,            /* I: the length of the configuration
                                         *    data at paucData
                                         */
    unsigned char *paucData);           /* I: the configuration data */



/* --- handling interrupt calls of the controllers --- */

/* the interrupt routine for a ARM-based board */
static void avmarm_intr
   (AvmAicSc_t *pSc);

/**
 * Enqueue a part of a configuration message for a controller.
 *
 * @pre This function must be called with exclusive board access.
 *
 * This function creates a message for the board with four bytes of content. As
 * this is part of the configuration download procedure, there will be some
 * amount of messages to be sent to the board. To not let the system run out of
 * mbufs, after enqueueing the message and trying to send it to the board, we
 * wait some time releasing the access mutex. This will give the interrupt
 * routine a chance to handle the delivery of the message, so the mbufs are
 * likely to be released right before this function returns.
 *
 * The access mutex will be released a short time, but the in-operation flag
 * will remain set. So no other operation can intrude. Only the interrupt
 * routine may get board access.
 *
 * @param pSc                   I: The softc structure for the board.
 * @param pauc                  I: A byte array with at least 4 bytes.
 *
 * @retval CAPI_OK              Message part enqueued successfully.
 * @retval Else                 Failure.
 */
static unsigned avmarm_send_config_binval
   (AvmAicSc_t    *pSc,
    unsigned char *pauc);

/* enqueue the send init command for a controller */
static unsigned avmarm_send_init
   (AvmAicSc_t *pSc);

/* enqueue the send pollack command for a controller */
static unsigned avmarm_send_pollack
   (AvmAicSc_t *pSc);

/* start the transfer of pending messages to the controller if not busy */
static void avmarm_start_tx
   (AvmAicSc_t *pSc);

/* receive and handle a pending message from the controller */
static void avmarm_handle_rx
   (AvmAicSc_t *pSc);



/* --- miscellaneous helper functions --- */

/**
 * Check for working C2 or C4 at given memory and i/o port resource.
 *
 * @param dev                   I: The device to check.
 * @param pResMem               I: The memory address to check.
 * @param pResIo                I: The i/o port resource to check.
 *
 * @retval 0                    A C2 or C4 was successfully identified at memory
 *                              and i/o port specified.
 * @retval -1                   There is no C2 or C4 at memory and i/o port or
 *                              it is not working.
 */
static int avmarm_detect
   (device_t         dev,
    struct resource *pResMem,
    struct resource *pResIo);

/* initialize the softc structure */
static int avmarm_init_softc            /* 0: structure initialized successfully
                                         * errno value: failure
                                         */
   (device_t    dev,                    /* I: the device to initialize */
    AvmAicSc_t *pSc);                   /* I: the softc structure of the device
                                         *    to initialize
                                         */





/* === definition of inline functions ==================================== */





/*
        read one discrete 32-bit value from a memory i/o location
        ---------------------------------------------------------
*/

static __inline u_int32_t avmarm_mem_readl
   (bus_space_tag_t    memt,
    bus_space_handle_t memh,
    u_int32_t          ulOffset)
{
   return (le32toh (bus_space_read_4 (memt, memh, ulOffset)));
} /* avmarm_mem_readl */





/*
        write one discrete 32-bit value to a memory i/o location
        --------------------------------------------------------
*/

static __inline void avmarm_mem_writel
   (bus_space_tag_t    memt,
    bus_space_handle_t memh,
    u_int32_t          ulOffset,
    u_int32_t          ulValue)
{
   bus_space_write_4 (memt, memh, ulOffset, htole32 (ulValue));
} /* avmarm_mem_writel */





/* === definition of public functions ==================================== */





/*
        attach an ARM based board (C2 or C4)
        ------------------------------------
        
        Parameters:
           dev                  I: the device to attach
           cardType             I: the card type to attach (C2 or C4)
           
        Result:
           0                    card successfully attached
           errno value          error attaching board
*/

int avmarm_attach
   (device_t         dev,
    AvmAicCardType_t cardType)
{
   AvmAicSc_t       *pSc = device_get_softc (dev);
   int               iRes;
   unsigned long     ulBase;
   unsigned long     ulCount;
   
   DBG (LOG_TRACE, pSc->iUnit, "Attaching \"%s\"", device_get_desc (dev));

   /* fill the softc structure */
   pSc->iUnit = device_get_unit (dev);
   pSc->cardType = cardType;
   iRes = avmarm_init_softc (dev, pSc);
   if (iRes != 0)
   {
      avmmisc_detach (dev);
      return (iRes);
   }

   /* allocate required i/o range */
   pSc->resInfo.iRidIo = 0x14;
   iRes = bus_get_resource (dev, SYS_RES_IOPORT, pSc->resInfo.iRidIo,
                            &ulBase, &ulCount);
   if (iRes != 0)
   {
      device_printf (dev, "Attach failed, unable to get i/o port range\n");
      avmmisc_detach (dev);
      return (iRes);
   }
   pSc->resInfo.iIoBase = (int) ulBase;
   pSc->resInfo.pResIo = bus_alloc_resource
                            (dev, SYS_RES_IOPORT,
                             &(pSc->resInfo.iRidIo),
                             0UL, ~0UL, 1, RF_ACTIVE);
   if (! pSc->resInfo.pResIo)
   {
      device_printf (dev,
                     "Attach failed, unable to allocate i/o port range 0x%04X-0x%04X\n",
                     pSc->resInfo.iIoBase,
                     (unsigned int) rman_get_end (pSc->resInfo.pResIo));
      avmmisc_detach (dev);
      return (ENXIO);
   }
   pSc->resInfo.ioTag    = rman_get_bustag (pSc->resInfo.pResIo);
   pSc->resInfo.ioHandle = rman_get_bushandle (pSc->resInfo.pResIo);
   
   /* allocate i/o memory range */
   pSc->resInfo.iRidMem = 0x10;
   iRes = bus_get_resource (dev, SYS_RES_MEMORY, pSc->resInfo.iRidMem,
                            &ulBase, &ulCount);
   if (iRes != 0)
   {
      device_printf (dev, "Attach failed, unable to get i/o memory range\n");
      avmmisc_detach (dev);
      return (iRes);
   }
   pSc->resInfo.iMemBase = (int) ulBase;
   pSc->resInfo.pResMem = bus_alloc_resource
                             (dev, SYS_RES_MEMORY, &(pSc->resInfo.iRidMem),
                              0UL, ~0UL, 1, RF_ACTIVE);
   if (! pSc->resInfo.pResMem)
   {
      device_printf (dev,
                     "Attach failed, unable to allocate i/o memory at 0x%08X\n",
                     pSc->resInfo.iMemBase);
      avmmisc_detach (dev);
      return (iRes);
   }
   pSc->resInfo.memTag    = rman_get_bustag (pSc->resInfo.pResMem);
   pSc->resInfo.memHandle = rman_get_bushandle (pSc->resInfo.pResMem);
   
   /* allocate required irq */
   pSc->resInfo.iRidIrq = 0;
   iRes = bus_get_resource (dev, SYS_RES_IRQ, pSc->resInfo.iRidIrq,
                            &ulBase, &ulCount);
   if (iRes != 0)
   {
      device_printf (dev, "Attach failed, unable to get irq\n");
      avmmisc_detach (dev);
      return (ENXIO);
   }
   pSc->resInfo.iIrq = (int) ulBase;
   pSc->resInfo.pResIrq = bus_alloc_resource
                             (dev, SYS_RES_IRQ, &(pSc->resInfo.iRidIrq),
                              0UL, ~0UL, 1, RF_ACTIVE | RF_SHAREABLE);
   if (! pSc->resInfo.pResIrq)
   {
      device_printf (dev, "Attach failed, unable to allocate irq %d\n",
                     pSc->resInfo.iIrq);
      avmmisc_detach (dev);
      return (ENXIO);
   }

   /* check for working card at assigned resources */
   if (avmarm_detect (dev, pSc->resInfo.pResMem, pSc->resInfo.pResIo) != 0)
   {
      avmmisc_detach (dev);
      return (ENXIO);
   }

   /* register interrupt routine */
   (void) bus_setup_intr (dev, pSc->resInfo.pResIrq,
                          INTR_TYPE_NET | INTR_MPSAFE,
                          (driver_intr_t *) avmarm_intr, pSc,
                          &(pSc->resInfo.hIrqCookie));

   /* reset card to get into defined state */
   avmarm_reset (pSc->resInfo.memTag, pSc->resInfo.memHandle);
   pSc->ulDmaIcsr = 0;
   
   /* get card revision number */
   pSc->iClass = avmio_inp (pSc->resInfo.ioTag,
                            pSc->resInfo.ioHandle,
                            AVMAIC_REG_ANALYSE);
   pSc->iRevision = avmio_inp (pSc->resInfo.ioTag,
                               pSc->resInfo.ioHandle,
                               AVMAIC_REG_REVISION);

   DBG (LOG_INFO, pSc->iUnit,
        "ISDN controller \"%s\" identified, class %d, revision %d",
	pSc->szCardName, pSc->iClass, pSc->iRevision);

   return (0);
} /* avmarm_attach */





/*
        reset an ARM based board
        ------------------------
*/

void avmarm_reset
   (bus_space_tag_t    memt,
    bus_space_handle_t memh)
{
   struct timeval tvStart;
   struct timeval tvCurr;
   int            iCurrTime;
   int            iTimeout;
   
   avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_DOORBELL,
                      AVMAIC_DBELL_RESET_ARM);
   DELAY (AVMAIC_IO_POLL_DELAY);

   /* timeout is 10s in ticks */
   iTimeout = 10 * hz;
   getmicrotime (&tvStart);
   iCurrTime = 0;
   
   while (avmarm_mem_readl (memt, memh,
                            AVMAIC_ARM_REG_DOORBELL) != 0xFFFFFFFF &&
          iTimeout >= iCurrTime)
   {
      DELAY (AVMAIC_IO_POLL_DELAY);
      avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_DOORBELL,
                         AVMAIC_DBELL_ADDR);
      DELAY (AVMAIC_IO_POLL_DELAY);

      getmicrotime (&tvCurr);
      timevalsub (&tvCurr, &tvStart);
      iCurrTime = tvCurr.tv_usec * hz / 1000000 + tvCurr.tv_sec * hz;
   }
   if (iTimeout >= iCurrTime &&
       avmarm_mem_readl (memt, memh, AVMAIC_ARM_REG_DOORBELL) != 0xFFFFFFFF)
   {
      DBG0 (LOG_ERROR,
            "Tag 0x%03X, handle 0x%03X: Timeout waiting for acknowledge of board reset (%d ticks)",
            (unsigned) memt, (unsigned) memh, iCurrTime);
      /* Note: We proceed even on failure. After a reset normal operation is
       *       suspended. And before any normal operation will be tried, the
       *       must be downloaded which is likely to fail if the reset failed
       *       before.
       */
   }
   
   (void) avmarm_poke (memt, memh,
                       AVMAIC_DC21285_ARMCSR_BASE + AVMAIC_CHAN_1_CONTROL, 0);
   (void) avmarm_poke (memt, memh,
                       AVMAIC_DC21285_ARMCSR_BASE + AVMAIC_CHAN_2_CONTROL, 0);
   
} /* avmarm_reset */





/* === definition of private functions =================================== */





/**
 * Wait for board to be able to accept commands.
 *
 * @param memt                  I: Bus tag for i/o operations.
 * @param memh                  I: Bus handle for i/o operations.
 * @param iTimeout              I: Timeout in ticks (i.e. "<seconds> * HZ") for
 *                                 the operation.
 *
 * @retval 0                    Success, the board is able to accept commands.
 * @retval Else                 Failure, the board is not ready.
 */

static int avmarm_wait_for_doorbell
   (bus_space_tag_t     memt,
    bus_space_handle_t  memh,
    int                 iTimeout)
{
   struct timeval tvStart;
   struct timeval tvCurr;
   int            iCurrTime;

   getmicrotime (&tvStart);
   iCurrTime = 0;
   while (avmarm_mem_readl (memt, memh,
                            AVMAIC_ARM_REG_DOORBELL) != 0xFFFFFFFF &&
          iTimeout >= iCurrTime)
   {
      DELAY (AVMAIC_IO_POLL_DELAY);
      getmicrotime (&tvCurr);
      timevalsub (&tvCurr, &tvStart);
      iCurrTime = tvCurr.tv_usec * hz / 1000000 + tvCurr.tv_sec * hz;
   }
   if (iTimeout < iCurrTime &&
       avmarm_mem_readl (memt, memh, AVMAIC_ARM_REG_DOORBELL) != 0xFFFFFFFF)
   {
      DBG0 (LOG_ERROR,
            "Tag 0x%03X, handle 0x%03X: Timeout waiting for \"doorbell\" (%d ticks)",
            (unsigned) memt, (unsigned) memh, iCurrTime);
      return (-1);
   }
   return (0);
} /* avmarm_wait_for_doorbell */





/*
        write a 32-bit value to a board-internal memory location
        --------------------------------------------------------
        
        Parameters:
           memt                 I: bus tag for communication
           memh                 I: bus handle for communication
           ulAddr               I: the internal memory address to read from
           pulValue             O: the value read if result 0

        Result:
           0                    peek operation successful, *pulValue valid
           else                 failure, *pulValue undefined
*/

static int avmarm_peek
   (bus_space_tag_t     memt,
    bus_space_handle_t  memh,
    u_int32_t           ulAddr,
    u_int32_t          *pulValue)
{
   if (avmarm_wait_for_doorbell (memt, memh, hz / 10) != 0)
   {
      return (-1);
   }
   
   avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_MBOX_PEEK_POKE, ulAddr);
   avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_DOORBELL,
                      AVMAIC_DBELL_RNWR | AVMAIC_DBELL_ADDR);

   if (avmarm_wait_for_doorbell (memt, memh, hz / 10) != 0)
   {
      return (-1);
   }
   
   *pulValue = avmarm_mem_readl (memt, memh, AVMAIC_ARM_REG_MBOX_PEEK_POKE);
   
   return (0);
} /* avmarm_peek */





/*
        read a 32-bit value from a board-internal memory location
        ---------------------------------------------------------
        
        Parameters:
           memt                 I: bus tag for communication
           memh                 I: bus handle for communication
           ulAddr               I: the internal memory address to write to
           pulValue             I: the value to write

        Result:
           0                    poke operation successful
           else                 failure
*/

static int avmarm_poke
   (bus_space_tag_t    memt,
    bus_space_handle_t memh,
    u_int32_t          ulAddr,
    u_int32_t          ulValue)
{
   if (avmarm_wait_for_doorbell (memt, memh, hz / 10) != 0)
   {
      return (-1);
   }
   
   avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_MBOX_PEEK_POKE, ulAddr);
   avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_DOORBELL,
                      AVMAIC_DBELL_ADDR);

   if (avmarm_wait_for_doorbell (memt, memh, hz / 10) != 0)
   {
      return (-1);
   }
   
   avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_MBOX_PEEK_POKE, ulValue);
   avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_DOORBELL,
                      AVMAIC_DBELL_DATA | AVMAIC_DBELL_ADDR);

   return (0);
} /* avmarm_poke */





/*
        download the firmware to the board
        ----------------------------------
        Note: The firmware data must be written bytewise regardless of its byte
              order. But the poke function expects its value argument in host
              byte order and converts it into little endian order as expected by
              the board. So for the download operation each 32-bit value must be
              converted to host byte order before beeing passed to the poke
              function.
        
        Parameters:
           pSc                  I: the softc structure of the board
           nLenData             I: the length of the firmware at paucData
           paucData             I: the firmware data

        Result:
           CAPI_OK              download successful
           else                 failure
*/

static unsigned avmarm_load_firmware
   (AvmAicSc_t    *pSc,
    size_t         nLenData,
    unsigned char *paucData)
{
   bus_space_tag_t    memt = pSc->resInfo.memTag;
   bus_space_handle_t memh = pSc->resInfo.memHandle;
   unsigned int       uLoadOffset;
   size_t             nLeft;
   u_int32_t          ul;

   if (nLenData == 0 || ! paucData)
   {
      printf ("%s%d: %s: Illegal arguments: nLenData=%zu, paucData=0x%p\n",
	      pSc->szDriverName, pSc->iUnit, __FUNCTION__, nLenData, paucData);
      return (CME_INVALID_PARAM);
   }

   uLoadOffset = 0;
   nLeft = nLenData;
   while (nLeft >= sizeof (u_int32_t))
   {
      ul = *((u_int32_t *) paucData);
      ul = le32toh (ul);
      if (avmarm_poke (memt, memh, uLoadOffset, ul) != 0)
      {
         printf ("%s%d: Failed to write firmware to controller (still %zu of %zu bytes to write)\n",
	         pSc->szDriverName, pSc->iUnit, nLeft, nLenData);
         return (CME_OS_RESOURCE_ERROR);
      }
      nLeft -= sizeof (u_int32_t);
      paucData += sizeof (u_int32_t);
      uLoadOffset += sizeof (u_int32_t);
   }
   
   if (nLeft > 0)
   {
      ul = 0;
      memcpy (&ul, paucData, nLeft);
      ul = le32toh (ul);
      if (avmarm_poke (memt, memh, uLoadOffset, ul) != 0)
      {
         printf ("%s%d: Failed to write firmware to controller (still %zu of %zu bytes to write)\n",
	         pSc->szDriverName, pSc->iUnit, nLeft, nLenData);
         return (CME_OS_RESOURCE_ERROR);
      }
   }
   
   return (CAPI_OK);
} /* avmarm_load_firmware */





/*
        download the configuration to the board
        ---------------------------------------
        
        Parameters:
           pSc                  I: the softc structure of the board
           nLenData             I: the length of the configuration data at
                                   paucData
           paucData             I: the configuration data

        Result:
           CAPI_OK              download successful
           else                 failure
*/

static unsigned avmarm_load_config
   (AvmAicSc_t    *pSc,
    size_t         nLenData,
    unsigned char *paucData)
{
   size_t        nLeft;
   unsigned char auc [sizeof (u_int32_t)];
   
   *((u_int32_t *) auc) = htole32 (1);
   if (avmarm_send_config_binval (pSc, auc) != CAPI_OK)
   {
      printf ("%s%d: Failed to write configuration to controller (first word)\n",
              pSc->szDriverName, pSc->iUnit);
      return (CME_OS_RESOURCE_ERROR);
   }
   *((u_int32_t *) auc) = htole32 (nLenData);
   if (avmarm_send_config_binval (pSc, auc) != CAPI_OK)
   {
      printf ("%s%d: Failed to write configuration to controller (data length)\n",
              pSc->szDriverName, pSc->iUnit);
      return (CME_OS_RESOURCE_ERROR);
   }
   
   nLeft = nLenData;
   while (nLeft >= sizeof (u_int32_t))
   {
      if (avmarm_send_config_binval (pSc, paucData) != CAPI_OK)
      {
         printf ("%s%d: Failed to write configuration to controller (still %zu byte of %zu to write)\n",
                 pSc->szDriverName, pSc->iUnit, nLeft, nLenData);
         return (CME_OS_RESOURCE_ERROR);
      }
      paucData += sizeof (u_int32_t);
      nLeft -= sizeof (u_int32_t);
   }
   
   if (nLeft > 0)
   {
      memset (auc, 0, sizeof (auc));
      memcpy (auc, paucData, nLeft);
      if (avmarm_send_config_binval (pSc, auc) != CAPI_OK)
      {
         printf ("%s%d: Failed to write configuration to controller (still %zu bytes of %zu to write)\n",
                 pSc->szDriverName, pSc->iUnit, nLeft, nLenData);
         return (CME_OS_RESOURCE_ERROR);
      }
   }
   
   return (CAPI_OK);
} /* avmarm_load_config */





/*
        register a CAPI application at a controller
        -------------------------------------------
*/

static unsigned avmarm_app_register
   (AvmAicSc_t       *pSc,
    AvmAicPortData_t *pPortData,
    unsigned          uMaxLogicalConnections,
    unsigned          uMaxBDataBlocks,
    unsigned          uMaxBDataLen,
    unsigned          uApplID)
{
   struct mbuf          *pmb;
   AvmLliSendRegister_t *pSendRegMsg;
   unsigned              uSizeMsgBuf;

   /* get mbuf for register message to controller */
   pmb = kcapi_get_mbuf (2 + 1 + sizeof (AvmLliSendRegister_t));
   if (! pmb)
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Unable to send %s: Out of mbufs", "AVMAIC_SEND_REGISTER");
      return (CME_OS_RESOURCE_ERROR);
   }
   
   /* fill the mbuf with the controller message; first two bytes 0 means "no
    * CAPI message", for CAPI messages this were the message length
    */
   uSizeMsgBuf = (uMaxLogicalConnections + 1) * 1024;
   mtod (pmb, unsigned short *) [0] = 0;
   mtod (pmb, unsigned char *) [2] = AVMAIC_SEND_REGISTER;
   pSendRegMsg = (AvmLliSendRegister_t *) &((mtod (pmb, unsigned char *)) [3]);
   C_PUT_DWORD (pSendRegMsg->dwApplID, uApplID);
   C_PUT_DWORD (pSendRegMsg->dwSizeMsgBuffer, uSizeMsgBuf);
   C_PUT_DWORD (pSendRegMsg->dwNumB3Connections, uMaxLogicalConnections);
   C_PUT_DWORD (pSendRegMsg->dwNumB3Blocks, uMaxBDataBlocks);
   C_PUT_DWORD (pSendRegMsg->dwB3Size, uMaxBDataLen);
   pmb->m_len = 2 + 1 + sizeof (AvmLliSendRegister_t);;
   
   /* insert controller message into queue for controller */
   if (_IF_QFULL (&(pSc->sendQueue)))
   {
      kcapi_free_mbuf (pmb);
      DBG (LOG_TRACE, pSc->iUnit,
           "Unable to enqueue %s: Send queue full", "AVMAIC_SEND_REGISTER");
      return (CME_PUT_QUEUE_FULL);
   }
   _IF_ENQUEUE (&(pSc->sendQueue), pmb);
   DBG (LOG_DEBUG, pSc->iUnit, "Message %s enqueued", "AVMAIC_SEND_REGISTER");
   
   /* send next message to controller if it is not busy */
   avmarm_start_tx (pSc);
   
   /* suppress warnings */
   (void) pPortData;
   
   return (CAPI_OK);
} /* avmarm_app_register */





/*
        release a CAPI application at a controller
        ------------------------------------------
*/

static unsigned avmarm_app_release
   (AvmAicSc_t       *pSc,
    AvmAicPortData_t *pPortData,
    unsigned          uApplID)
{
   struct mbuf         *pmb;
   AvmLliSendRelease_t *pSendRelMsg;

   /* get mbuf for register message to controller */
   pmb = kcapi_get_mbuf (2 + 1 + sizeof (AvmLliSendRelease_t));
   if (! pmb)
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Unable to send %s: Out of mbufs", "AVMAIC_SEND_RELEASE");
      return (CME_OS_RESOURCE_ERROR);
   }
   
   /* fill the mbuf with the controller message; first two bytes 0 means "no
    * CAPI message", for CAPI messages this were the message length
    */
   mtod (pmb, unsigned short *) [0] = 0;
   mtod (pmb, unsigned char *) [2] = AVMAIC_SEND_RELEASE;
   pSendRelMsg = (AvmLliSendRelease_t *) &((mtod (pmb, unsigned char *)) [3]);
   C_PUT_DWORD (pSendRelMsg->dwApplID, uApplID);
   pmb->m_len = 2 + 1 + sizeof (AvmLliSendRelease_t);
   
   /* prepare to wait for completion of release operation */
   pSc->fWaitingForRc = 1;
   
   /* insert controller message into queue for controller */
   if (_IF_QFULL (&(pSc->sendQueue)))
   {
      pSc->fWaitingForRc = 0;
      kcapi_free_mbuf (pmb);
      DBG (LOG_TRACE, pSc->iUnit,
           "Unable to enqueue %s: Send queue full", "AVMAIC_SEND_RELEASE");
      return (CME_PUT_QUEUE_FULL);
   }
   _IF_ENQUEUE (&(pSc->sendQueue), pmb);
   DBG (LOG_DEBUG, pSc->iUnit, "Message %s enqueued", "AVMAIC_SEND_RELEASE");
   
   /* send next message to controller if it is not busy */
   avmarm_start_tx (pSc);
   
   /* now wait for completion of release operation */
   (void) cv_timedwait (&(pSc->cvNotify), &(pSc->mtxAccess), 30 * hz);
   pSc->fWaitingForRc = 0;
   
   return (CAPI_OK);
} /* avmarm_app_release */





/**
 * Send a CAPI message to the controller.
 */

static unsigned avmarm_put_message
   (AvmAicSc_t       *pSc,
    AvmAicPortData_t *pPortData,
    unsigned          uApplID,
    struct mbuf      *pmbMsg)
{
   unsigned  uCapiCmd;
   u_int32_t dwCid;

   /* extract some data needed later */
   uCapiCmd = CAPI_GET_CMD (mtod (pmbMsg, CAPIMsg_t *));
   dwCid = CAPI_GET_CID (mtod (pmbMsg, CAPIMsg_t *));
   
   /* if the controller is currently busy, the message must be rejected */
   if (pSc->state != AVMAIC_STATE_READY ||
       pPortData->state != AVMAIC_STATE_READY)
   {
      DBG (LOG_DEBUG, pSc->iUnit,
           "Port %u: Unable to enqueue CAPI message 0x%04X: Controller %u busy",
           pPortData->uPortIdx, uCapiCmd, pPortData->uUniqueCapiCtlrNum);
      return (CME_PUT_QUEUE_FULL);
   }
   
   /* insert controller message into queue for controller */
   if (_IF_QFULL (&(pSc->sendQueue)))
   {
      DBG (LOG_TRACE, pSc->iUnit,
           "Unable to enqueue CAPI message 0x%04X: Send queue full", uCapiCmd);
      return (CME_PUT_QUEUE_FULL);
   }
   _IF_ENQUEUE (&(pSc->sendQueue), pmbMsg);
   DBG (LOG_DEBUG, pSc->iUnit, "CAPI message 0x%04X enqueued", uCapiCmd);
   
   /* send next message to controller if it is not busy */
   avmarm_start_tx (pSc);
   
   return (CAPI_OK);
} /* avmarm_put_message */





/*
        reset and download a controller
        -------------------------------
        
        Parameters:
           pSc                  I: softc structure ident. the board
           nNumDataBlocks       I: Number of data blocks to be sent to the
                                   controller; length of paDataBlocks
           paDataBlocks         I: Array of data blocks to be sent to the
                                   controller

        Result:
           CAPI_OK or CAPI error value
*/

static unsigned avmarm_reset_ctlr
   (AvmAicSc_t          *pSc,
    size_t               nNumDataBlocks,
    CAPICtlrDataBlock_t *paDataBlocks)
{
   bus_space_tag_t     memt = pSc->resInfo.memTag;
   bus_space_handle_t  memh = pSc->resInfo.memHandle;
   AvmAicPortData_t   *pPortData;
   struct mbuf        *pmb;
   unsigned            uRes;
   int                 i;

   if (nNumDataBlocks > 0)
   {
      DBG (LOG_INFO, pSc->iUnit,
           "Load firmware to controller %u, length of software %zu bytes",
	        pSc->aPortData [0].uUniqueCapiCtlrNum,
           paDataBlocks [0].nLenDataBlock);
      if (nNumDataBlocks > 1)
      {
         DBG (LOG_INFO, pSc->iUnit,
              "Length of configuration data %zu bytes",
              paDataBlocks [1].nLenDataBlock);
      }
   }
   else
   {
      DBG (LOG_INFO, pSc->iUnit, "Unloading controller %u",
	        pSc->aPortData [0].uUniqueCapiCtlrNum);
   }

   /* perform a complete reset and prepare to wait for the completion event
    * through the interrupt routine
    */
   pSc->uRc = 0xFFFFFFFF;
   pSc->fWaitingForRc = 1;
   pSc->state = AVMAIC_STATE_INITIALIZING;
   pSc->nPortsInitialized = 0;
   avmarm_reset (memt, memh);
   /*avmarm_reset (memt, memh);*/

   /* clean the send queue if there are still messages queued */
   pmb = NULL;
   do
   {
      _IF_DEQUEUE (&(pSc->sendQueue), pmb);
      kcapi_free_mbuf (pmb);
   } while (pmb != NULL);
   
   /* We must now wait for the reset operation to complete. If the board was
    * initialized before, the interrupt routine will be called. We must give it
    * a chance to complete the call. But if the board was not initialized, the
    * interrupt routine will not be called. So we cannot rely on the interrupt
    * routine to signal the condition variable. We can only wait for some time
    * and hope all pending calls are completed after the wait operation.
    */
   (void) cv_timedwait (&(pSc->cvNotify), &(pSc->mtxAccess), hz);
   
   /* if only reset was requested: ready */
   if (nNumDataBlocks <= 0)
   {
      pSc->state = AVMAIC_STATE_DOWN;
      DBG (LOG_INFO, pSc->iUnit, "Unloading controller successful");
      return (CAPI_OK);
   }
   
   /* so at least one data block is specified, treat it as the firmware file and
    * load it onto the controller
    */
   uRes = avmarm_load_firmware (pSc,
                                paDataBlocks [0].nLenDataBlock,
                                paDataBlocks [0].paucDataBlock);
   if (uRes != CAPI_OK)
   {
      avmarm_reset (memt, memh);
      pSc->state = AVMAIC_STATE_DOWN;
      return (uRes);
   }
   DBG (LOG_INFO, pSc->iUnit, "Firmware successfully loaded");

   /* now start communication with the controller; this is needed to send the
    * configuration and other messages to the controller
    */
   pSc->ulDmaIcsr = 0;
   avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_MBOX_UP_LEN, 0);
   avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_MBOX_DOWN_LEN, 0);
   avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_DOORBELL, AVMAIC_DBELL_INIT);
   DELAY (1 * 1000);
   avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_DOORBELL,
                      AVMAIC_DBELL_UP_HOST |
                         AVMAIC_DBELL_DOWN_HOST |
                         AVMAIC_DBELL_RESET_HOST);
   avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_PCI_OUT_INT_MASK, 0x08);
   pSc->ulRecvMsgBufLength = 0;
   bus_dmamap_sync (pSc->dmaRecv.dmat, pSc->dmaRecv.map, BUS_DMASYNC_PREREAD);
   avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_MBOX_UP_ADDR,
                      pSc->dmaRecv.ulPhysAddr);
   avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_MBOX_UP_LEN,
                      AVMAIC_MSGBUF_SIZE);
   avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_DOORBELL, AVMAIC_DBELL_UP_ARM);
   DBG (LOG_DEBUG, pSc->iUnit,
        "Board starts receiving messages to addr 0x%p, length %u (phys 0x%08zX - 0x%08zX)",
        pSc->dmaRecv.pVirtAddr, (unsigned) AVMAIC_MSGBUF_SIZE,
        (size_t) (pSc->dmaRecv.ulPhysAddr),
        (size_t) (pSc->dmaRecv.ulPhysAddr + AVMAIC_MSGBUF_SIZE));
   
   /* if a second data block is specified, it must be the controller
    * configuration; send it to the controller
    */
   if (nNumDataBlocks > 1)
   {
      uRes = avmarm_load_config (pSc,
                                 paDataBlocks [1].nLenDataBlock,
                                 paDataBlocks [1].paucDataBlock);
      if (uRes != CAPI_OK)
      {
         avmarm_reset (memt, memh);
      	pSc->state = AVMAIC_STATE_DOWN;
         return (uRes);
      }
      DBG (LOG_INFO, pSc->iUnit, "Configuration successfully loaded");
   }

   /* initialize some port specific data before sending AVMAIC_SEND_INIT */
   for (i = 0; i < pSc->nPorts; i++)
   {
      pPortData = &(pSc->aPortData [i]);
      pPortData->state = AVMAIC_STATE_INITIALIZING;
      bzero (pPortData->acVersionBuf, sizeof (pPortData->acVersionBuf));
   }

   /* after the SEND_INIT command the board will be ready for operation, but
    * before sending the first message to the controller we must wait until the
    * first AVMAIC_RECEIVE_START that will set the state to ready
    */
   pSc->state = AVMAIC_STATE_BUSY;

   /* initialize controller software with number of applications and logical
    * connections
    */
   DBG (LOG_TRACE, pSc->iUnit,
        "Send message AVMAIC_SEND_INIT to the controller");
   pSc->uRc = 0xFFFFFFFF;
   pSc->fWaitingForRc = 1;
   uRes = avmarm_send_init (pSc);
   if (uRes != CAPI_OK)
   {
      pSc->fWaitingForRc = 0;
      avmarm_reset (memt, memh);
      pSc->state = AVMAIC_STATE_DOWN;
      printf ("%s%d: Unable to do controller initialization after download, controller busy\n",
              pSc->szDriverName, pSc->iUnit);
      return (uRes);
   }

   /* wait for controller response to initialization request */
   (void) cv_timedwait (&(pSc->cvNotify), &(pSc->mtxAccess), 10 * hz);
   pSc->fWaitingForRc = 0;
   if (pSc->uRc != CAPI_OK)
   {
      pSc->state = AVMAIC_STATE_DOWN;
      avmarm_reset (memt, memh);
      uRes = pSc->uRc;
      printf ("%s%d: Controller initialization after download failed\n",
	      pSc->szDriverName, pSc->iUnit);
      if (uRes == 0xFFFFFFFF)
      {
         return (CRE_OS_RESOURCE_ERROR);
      }
      return (uRes);
   }

   /* the version buffer of the port data structure is filled now, translate it
    * into a better usable format
    */
   for (i = 0; i < pSc->nPorts; i++)
   {
      pPortData = &(pSc->aPortData [i]);
      avmmisc_parse_version (pPortData);
      switch (pPortData->abCapiProfile [45])
      {
         case 0:
            /* the card type is delivered by the controller, just log it */
            DBG (LOG_INFO, pSc->iUnit, "Board delivers card type \"%s\"",
                 pPortData->szCardType);
            break;

         case 6:
            /* card claims to be B1 PCI v3.0 */
            if (pSc->cardType != AVMAIC_CARD_TYPE_B1_PCI_V3)
            {
               printf ("%s%d: board claims to be \"%s\" but was detected as \"%s\"\n",
                       pSc->szDriverName, pSc->iUnit,
                       avmmisc_get_card_type_name (AVMAIC_CARD_TYPE_B1_PCI_V3),
                       avmmisc_get_card_type_name (pSc->cardType));
            }
            break;

         case 7:
            /* card claims to be B1 PCI v4.0 */
            if (pSc->cardType != AVMAIC_CARD_TYPE_B1_PCI_V4)
            {
               printf ("%s%d: Board claims to be \"%s\" but was detected as \"%s\"\n",
                       pSc->szDriverName, pSc->iUnit,
                       avmmisc_get_card_type_name (AVMAIC_CARD_TYPE_B1_PCI_V4),
                       avmmisc_get_card_type_name (pSc->cardType));
            }
            break;

         case 3:
            /* card type should be (unsupported) B1 PCMCIA */
         case 4:
            /* card type should be (unsupported) M1 */
         case 5:
            /* card type should be (unsupported) M2 */
         default:
            break;
      }
   }

   DBG (LOG_INFO, pSc->iUnit,
        "Controller successfully loaded and initialized");

   return (CAPI_OK);
} /* avmarm_reset_ctlr */





/*
        the interrupt routine for a ARM-based board
        -------------------------------------------
*/

void avmarm_intr
   (AvmAicSc_t *pSc)
{
   bus_space_tag_t    memt = pSc->resInfo.memTag;
   bus_space_handle_t memh = pSc->resInfo.memHandle;
   u_int32_t          ulStatus;
   
   DBG (LOG_IRQ, pSc->iUnit, "Interrupt handler called");

   mtx_lock (&(pSc->mtxAccess));
   if (pSc->fOpInProgress != 0 && ! pSc->fWaitingForRc)
   {
      DBG (LOG_DEBUG, pSc->iUnit, "Operation still in progress");
   }
   if (pSc->fIntrActive != 0)
   {
      DBG (LOG_ERROR, pSc->iUnit, "Nested interrupt handler call");
      return;
   }
   pSc->fIntrActive = 1;
   
   /* the status register content is needed for operation */
   ulStatus = avmarm_mem_readl (memt, memh, AVMAIC_ARM_REG_DOORBELL);
   
   /* check for signal for a board reset */
   if ((ulStatus & AVMAIC_DBELL_RESET_HOST) != 0)
   {
      int               i;
      AvmAicPortData_t *pPortData;
      
      avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_PCI_OUT_INT_MASK, 0x0C);
      pSc->nPortsInitialized = 0;
      for (i = 0; i < pSc->nPorts; i++)
      {
         pPortData = &(pSc->aPortData [i]);
         pPortData->state = AVMAIC_STATE_DOWN;
      }
      pSc->state = AVMAIC_STATE_DOWN;
      DBG (LOG_INFO, pSc->iUnit, "Got board reset");

      /* if someone is waiting for an acknowledgement, tell him success */
      if (pSc->fWaitingForRc)
      {
         pSc->fWaitingForRc = 0;
         pSc->uRc = CAPI_OK;
         cv_broadcast (&(pSc->cvNotify));
      }
      
      pSc->fIntrActive = 0;
      mtx_unlock (&(pSc->mtxAccess));
      return;
   }
   
   /* check if the interrupt is really caused by our hardware */
   ulStatus &= (AVMAIC_DBELL_UP_HOST | AVMAIC_DBELL_DOWN_HOST);
   if (ulStatus == 0)
   {
      /* must be other hardware that caused the interrupt */
      pSc->fIntrActive = 0;
      mtx_unlock (&(pSc->mtxAccess));
      return;
   }
   avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_DOORBELL, ulStatus);
   
   /* check receive irq, i.e. if we received some bytes */
   if ((ulStatus & AVMAIC_DBELL_UP_HOST) != 0)
   {
      /* acknowledge the received data and stop the board from writing to the
       * receive buffer
       */
      pSc->ulRecvMsgBufLength =
         avmarm_mem_readl (memt, memh, AVMAIC_ARM_REG_MBOX_UP_LEN);
      avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_MBOX_UP_LEN, 0);
      
      /* handle the received message */
      avmarm_handle_rx (pSc);
      
      /* let the board transfer a new message to the receive buffer */
      pSc->ulRecvMsgBufLength = 0;
      bus_dmamap_sync (pSc->dmaRecv.dmat, pSc->dmaRecv.map,
                       BUS_DMASYNC_PREREAD);
      avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_MBOX_UP_ADDR,
                         pSc->dmaRecv.ulPhysAddr);
      avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_MBOX_UP_LEN,
                         AVMAIC_MSGBUF_SIZE);
      avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_DOORBELL,
                         AVMAIC_DBELL_UP_ARM);
      DBG (LOG_IRQ, pSc->iUnit,
           "Board starts receiving messages to addr 0x%p, length %u (phys 0x%08zX - 0x%08zX)",
           pSc->dmaRecv.pVirtAddr, (unsigned) AVMAIC_MSGBUF_SIZE,
           (size_t) (pSc->dmaRecv.ulPhysAddr),
           (size_t) (pSc->dmaRecv.ulPhysAddr + AVMAIC_MSGBUF_SIZE));
   }
   
   /* check for send irq, i.e. if the controller is able to accept a new message
    */
   if ((ulStatus & AVMAIC_DBELL_DOWN_HOST) != 0)
   {
      pSc->ulDmaIcsr &= ~AVMAIC_DBELL_DOWN_ARM;
      
      /* transfer any pending message to the controller */
      avmarm_start_tx (pSc);
   }
/* The following code from the linux driver could be a mistake. If the board
 * is ready sending a data block but has not signaled this with an interrupt,
 * we would send out the next message now. But an interrupt may already be
 * pending and so the next time we would think, the newly tranfered block is
 * already sent and overwrite it with the third in line. But I am not sure.
 */
#if 1
   else if ((pSc->ulDmaIcsr & AVMAIC_DBELL_DOWN_HOST) != 0)
   {
      if (avmarm_mem_readl (memt, memh, AVMAIC_ARM_REG_MBOX_DOWN_LEN) == 0)
      {
         pSc->ulDmaIcsr &= ~AVMAIC_DBELL_DOWN_ARM;
      
         /* transfer any pending message to the controller */
         avmarm_start_tx (pSc);
      }
   }
#endif /* 0 */

   pSc->fIntrActive = 0;
   mtx_unlock (&(pSc->mtxAccess));
   
} /* avmarm_intr */





/**
 * Enqueue a part of a configuration message for a controller.
 *
 * @pre This function must be called with exclusive board access.
 *
 * This function creates a message for the board with four bytes of content. As
 * this is part of the configuration download procedure, there will be some
 * amount of messages to be sent to the board. To not let the system run out of
 * mbufs, after enqueueing the message and trying to send it to the board, we
 * wait some time releasing the access mutex. This will give the interrupt
 * routine a chance to handle the delivery of the message, so the mbufs are
 * likely to be released right before this function returns.
 *
 * The access mutex will be released a short time, but the in-operation flag
 * will remain set. So no other operation can intrude. Only the interrupt
 * routine may get board access.
 *
 * @param pSc                   I: The softc structure for the board.
 * @param pauc                  I: A byte array with at least 4 bytes.
 *
 * @retval CAPI_OK              Message part enqueued successfully.
 * @retval Else                 Failure.
 */

static unsigned avmarm_send_config_binval
   (AvmAicSc_t    *pSc,
    unsigned char *pauc)
{
   struct mbuf *pmb;

   /* get mbuf for message to controller */
   pmb = kcapi_get_mbuf (2 + 1 + sizeof (u_int32_t));
   if (! pmb)
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Unable to send %s: Out of mbufs", "AVMAIC_SEND_CONFIG");
      return (CME_OS_RESOURCE_ERROR);
   }
   
   /* fill the mbuf with the controller message; first two bytes 0 means "no
    * CAPI message", for CAPI messages this were the message length
    */
   mtod (pmb, u_int16_t *) [0] = 0;
   mtod (pmb, unsigned char *) [2] = AVMAIC_SEND_CONFIG;
   mtod (pmb, unsigned char *) [3] = pauc [0];
   mtod (pmb, unsigned char *) [4] = pauc [1];
   mtod (pmb, unsigned char *) [5] = pauc [2];
   mtod (pmb, unsigned char *) [6] = pauc [3];
   pmb->m_len = 7;
   
   /* insert controller message into queue for controller */
   if (_IF_QFULL (&(pSc->sendQueue)))
   {
      kcapi_free_mbuf (pmb);
      DBG (LOG_TRACE, pSc->iUnit,
           "Unable to enqueue %s: Send queue full", "AVMAIC_SEND_CONFIG");
      return (CME_PUT_QUEUE_FULL);
   }
   _IF_ENQUEUE (&(pSc->sendQueue), pmb);
   DBG (LOG_DEBUG, pSc->iUnit, "Message %s enqueued", "AVMAIC_SEND_CONFIG");
   
   /* send next message to controller if it is not busy */
   avmarm_start_tx (pSc);

   /* wait some time to let the board accept the message and to let the
    * interrupt routine tell us when it is ready (if it is quick enough)
    */
   pSc->fWaitingForRc = 1;
   (void) cv_timedwait (&(pSc->cvNotify), &(pSc->mtxAccess), hz / 10);
   pSc->fWaitingForRc = 0;
   
   return (CAPI_OK);
} /* avmarm_send_config_binval */





/*
        enqueue the send init command for a controller
        ----------------------------------------------
*/

static unsigned avmarm_send_init
   (AvmAicSc_t *pSc)
{
   struct mbuf      *pmb;
   AvmLliSendInit_t *pSendInitMsg;

   /* get mbuf for message to controller */
   pmb = kcapi_get_mbuf (2 + 1 + sizeof (AvmLliSendInit_t));
   if (! pmb)
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Unable to send %s: Out of mbufs", "AVMAIC_SEND_INIT");
      return (CME_OS_RESOURCE_ERROR);
   }
   
   /* fill the mbuf with the controller message; first two bytes 0 means "no
    * CAPI message", for CAPI messages this were the message length
    */
   mtod (pmb, u_int16_t *) [0] = 0;
   mtod (pmb, unsigned char *) [2] = AVMAIC_SEND_INIT;
   pSendInitMsg = (AvmLliSendInit_t *) &((mtod (pmb, unsigned char *)) [3]);
   C_PUT_DWORD (pSendInitMsg->dwNumApplications, e_iAvmAicMaxApplications);
   C_PUT_DWORD (pSendInitMsg->dwNumNccis,
                e_iAvmAicMaxNcciPerChannel * 30 /*pSc->aPortData [0].nBChns*/);
   C_PUT_DWORD (pSendInitMsg->dwBoardNumber,
                pSc->aPortData [0].uUniqueCapiCtlrNum - 1);
   pmb->m_len = 2 + 1 + sizeof (AvmLliSendInit_t);
   
   /* insert controller message into queue for controller */
   if (_IF_QFULL (&(pSc->sendQueue)))
   {
      kcapi_free_mbuf (pmb);
      DBG (LOG_TRACE, pSc->iUnit,
           "Unable to enqueue %s: Send queue full", "AVMAIC_SEND_INIT");
      return (CME_PUT_QUEUE_FULL);
   }
   _IF_ENQUEUE (&(pSc->sendQueue), pmb);
   DBG (LOG_DEBUG, pSc->iUnit, "Message %s enqueued", "AVMAIC_SEND_INIT");
   
   /* send next message to controller if it is not busy */
   avmarm_start_tx (pSc);
   
   return (CAPI_OK);
} /* avmarm_send_init */





/*
        enqueue the send pollack command for a controller
        -------------------------------------------------
*/

static unsigned avmarm_send_pollack
   (AvmAicSc_t *pSc)
{
   struct mbuf *pmb;

   /* get mbuf for message to controller */
   pmb = kcapi_get_mbuf (2 + 1);
   if (! pmb)
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Unable to send %s: Out of mbufs", "AVMAIC_SEND_POLLACK");
      return (CME_OS_RESOURCE_ERROR);
   }
   
   /* fill the mbuf with the controller message; first two bytes 0 means "no
    * CAPI message", for CAPI messages this were the message length
    */
   mtod (pmb, u_int16_t *) [0] = 0;
   mtod (pmb, unsigned char *) [2] = AVMAIC_SEND_POLLACK;
   pmb->m_len = 3;
   
   /* insert controller message into queue for controller */
   if (_IF_QFULL (&(pSc->sendQueue)))
   {
      kcapi_free_mbuf (pmb);
      DBG (LOG_TRACE, pSc->iUnit,
           "Unable to enqueue %s: Send queue full", "AVMAIC_SEND_POLLACK");
      return (CME_PUT_QUEUE_FULL);
   }
   _IF_ENQUEUE (&(pSc->sendQueue), pmb);
   DBG (LOG_IRQ, pSc->iUnit, "Message %s enqueued", "AVMAIC_SEND_POLLACK");
   
   /* send next message to controller if it is not busy */
   avmarm_start_tx (pSc);
   
   return (CAPI_OK);
} /* avmarm_send_pollack */





/*
        start the transfer of pending messages to the controller if not busy
        --------------------------------------------------------------------
        Note: This function may only be called with privilege level SPLC4B().
*/

static void avmarm_start_tx
   (AvmAicSc_t *pSc)
{
   bus_space_tag_t     memt = pSc->resInfo.memTag;
   bus_space_handle_t  memh = pSc->resInfo.memHandle;
   struct mbuf        *pmbMsg;
   struct mbuf        *pmbData;
   u_int32_t           ulTxLen;
   unsigned char      *p;
   
   /* if the transmitter is already busy accepting a message: wait, do nothing
    * here
    */
   if ((pSc->ulDmaIcsr & AVMAIC_DBELL_DOWN_ARM) != 0)
   {
      DBG (LOG_IRQ, pSc->iUnit, "Transmitter busy");
      return;
   }
   
   /* the board is ready to accept a new message, dequeue one and send it */
   _IF_DEQUEUE (&(pSc->sendQueue), pmbMsg);
   if (! pmbMsg)
   {
      /* no message in queue, ready */
      DBG (LOG_IRQ, pSc->iUnit, "Queue empty");
      
      /* if someone is waiting to proceed on an empty queue, wake him up (can
       * happen during coniguration download to the board)
       */
      if (pSc->fWaitingForRc && pSc->state == AVMAIC_STATE_INITIALIZING)
      {
         pSc->fWaitingForRc = 0;
         cv_broadcast (&(pSc->cvNotify));
      }
      return;
   }
   
   /* distinguish between CAPI messages and control messages for the board;
    * if the first two bytes of a message are zero, it is a control message
    */
   if (mtod (pmbMsg, unsigned short *) [0] == 0)
   {
      bcopy (mtod (pmbMsg, unsigned char *) + 2, pSc->dmaSend.pVirtAddr,
             pmbMsg->m_len - 2);
      p = pSc->dmaSend.pVirtAddr + pmbMsg->m_len - 2;
   }
   /* else: this is a CAPI message and the first two bytes are the message
    * length
    */
   else
   {
      unsigned uCmd;
      size_t   nMsgLen;
      
      /* For security reasons we send Data-B3-Requests without the 64bit data
       * block pointer. The controller might not like messages that long. The
       * CAPI manager checked the message for validity before this function is
       * called.
       */
      uCmd = CAPI_GET_CMD (mtod (pmbMsg, CAPIMsg_t *));
      if (uCmd == CAPI_REQUEST (C_DATA_B3))
      {
         nMsgLen = sizeof (CAPIMsgHead_t) + sizeof (CAPIDataB332Req_t);
         if (nMsgLen > pmbMsg->m_len)
         {
            nMsgLen = pmbMsg->m_len;
         }
      }
      else
      {
         nMsgLen = pmbMsg->m_len;
      }
      
      /* leave one byte for the message command stored later */
      p = avmmisc_mem_put_slice (&(pSc->dmaSend.pVirtAddr [1]),
                                 mtod (pmbMsg, unsigned char *),
                                 nMsgLen);
      
      /* for Data-B3-Requests the data block must follow the CAPI message */
      if (CAPI_GET_CMD (mtod (pmbMsg, CAPIMsg_t *)) == CAPI_REQUEST (C_DATA_B3))
      {
         /* store the message command to use */
         pSc->dmaSend.pVirtAddr [0] = AVMAIC_SEND_DATA_B3_REQ;
         
         pmbData = pmbMsg->m_next;
         if (pmbData && pmbData->m_len > 0)
         {
            p = avmmisc_mem_put_slice (p, mtod (pmbData, unsigned char *),
                                       pmbData->m_len);
         }
         else
         {
            DBG (LOG_TRACE, pSc->iUnit,
                 "Got Data-B3-Request without data block");
            p = avmmisc_mem_put_slice (p, NULL, 0);
         }
      }
      else
      {
         /* store the message command to use */
         pSc->dmaSend.pVirtAddr [0] = AVMAIC_SEND_MESSAGE;
      }
   }
   /* the send buffer is now filled and p points after the last byte written */
   DBG ((pSc->dmaSend.pVirtAddr [0] == AVMAIC_SEND_POLLACK)
           ? LOG_IRQ : LOG_DEBUG,
        pSc->iUnit, "Send buffer filled with message 0x%02X",
        (int) (pSc->dmaSend.pVirtAddr [0]));
   
   /* the mbuf(s) is/are no longer needed */
   kcapi_free_mbuf (pmbMsg);
   
   /* save the length of the send buffer */
   pSc->ulSendMsgBufLength = (unsigned long) (p - pSc->dmaSend.pVirtAddr);
   
   /* determine the length rounded up to 4-byte allignment */
   ulTxLen = (pSc->ulSendMsgBufLength + 3) & ~0x00000003;
   
   /* tell the board to accept the new message */
   bus_dmamap_sync (pSc->dmaSend.dmat, pSc->dmaSend.map, BUS_DMASYNC_PREWRITE);
   avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_MBOX_DOWN_ADDR,
                      pSc->dmaSend.ulPhysAddr);
   avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_MBOX_DOWN_LEN, ulTxLen);
   pSc->ulDmaIcsr |= AVMAIC_DBELL_DOWN_ARM;
   avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_DOORBELL,
                      AVMAIC_DBELL_DOWN_ARM);

   DBG ((pSc->dmaSend.pVirtAddr [0] == AVMAIC_SEND_POLLACK)
           ? LOG_IRQ : LOG_DEBUG,
        pSc->iUnit,
        "Board starts accepting message 0x%02X, length %lu (%lu) from addr 0x%p (phys 0x%08zX - 0x%08zX)",
        (int) (pSc->dmaSend.pVirtAddr [0]),
        (unsigned long) pSc->ulSendMsgBufLength, (unsigned long) ulTxLen,
        pSc->dmaSend.pVirtAddr,
        (size_t) (pSc->dmaSend.ulPhysAddr),
        (size_t) (pSc->dmaSend.ulPhysAddr + pSc->ulSendMsgBufLength));

} /* avmarm_start_tx */





/*
        receive and handle a pending message from the controller
        --------------------------------------------------------
        This function is called after a complete message was received from the
        controller. The message is expected to be in the buffer at
        pSc->paucRecvMsgBuf, the length is in pSc->ulRecvMsgBufLength. The first
        byte of the message is the message indicator identifying the information
        in the following bytes.
*/

static void avmarm_handle_rx
   (AvmAicSc_t *pSc)
{
   unsigned char ucCmd;
   
   bus_dmamap_sync (pSc->dmaRecv.dmat, pSc->dmaRecv.map, BUS_DMASYNC_POSTREAD);

   ucCmd = pSc->paucRecvMsgBuf [0];
   switch (ucCmd)
   {
      case AVMAIC_RECEIVE_MESSAGE:
         avmdma_receive_message (pSc);
         break;

      case AVMAIC_RECEIVE_DATA_B3_IND:
         avmdma_receive_data_b3_ind (pSc);
         break;

      case AVMAIC_RECEIVE_START:
         if (pSc->state == AVMAIC_STATE_BUSY)
         {
            pSc->state = AVMAIC_STATE_READY;
            DBG (LOG_TRACE, pSc->iUnit, "Ready to receive data again (%s)",
                 "AVMAIC_RECEIVE_START");
         }
         else
         {
            DBG (LOG_IRQ, pSc->iUnit, "Still ready to receive data (%s)",
                 "AVMAIC_RECEIVE_START");
         }
         (void) avmarm_send_pollack (pSc);
         break;

      case AVMAIC_RECEIVE_STOP:
         DBG (LOG_TRACE, pSc->iUnit, "Unable to receive data (%s)",
              "AVMAIC_RECEIVE_STOP");
         if (pSc->state == AVMAIC_STATE_READY)
         {
            pSc->state = AVMAIC_STATE_BUSY;
         }
         break;

      case AVMAIC_RECEIVE_NEW_NCCI:
         avmdma_receive_new_ncci (pSc);
         break;

      case AVMAIC_RECEIVE_FREE_NCCI:
         avmdma_receive_free_ncci (pSc);
         break;

      case AVMAIC_RECEIVE_INIT:
         avmdma_receive_init (pSc);
         break;

      case AVMAIC_RECEIVE_TASK_READY:
         avmdma_receive_task_ready (pSc);
         break;

      case AVMAIC_RECEIVE_POLL:
         printf ("%s%d: %s: Got unexpected message AVMAIC_RECEIVE_POLL\n",
                 pSc->szDriverName, pSc->iUnit, __FUNCTION__);
         break;

      case AVMAIC_RECEIVE_DEBUGMSG:
         avmdma_receive_debugmsg (pSc);
         break;

      case AVMAIC_RECEIVE_POLLDWORD:
         printf ("%s%d: %s: Got unexpected message AVMAIC_RECEIVE_POLLDWORD\n",
                 pSc->szDriverName, pSc->iUnit, __FUNCTION__);
         break;

      default:
         printf ("%s%d: %s: Unknown message 0x%X from controller\n",
                 pSc->szDriverName, pSc->iUnit, __FUNCTION__, (int) ucCmd);
         break;
   }

} /* avmarm_handle_rx */





/**
 * Check for working C2 or C4 at given memory and i/o port resource.
 *
 * @param dev                   I: The device to check.
 * @param pResMem               I: The memory address to check.
 * @param pResIo                I: The i/o port resource to check.
 *
 * @retval 0                    A C2 or C4 was successfully identified at memory
 *                              and i/o port specified.
 * @retval -1                   There is no C2 or C4 at memory and i/o port or
 *                              it is not working.
 */

static int avmarm_detect
   (device_t         dev,
    struct resource *pResMem,
    struct resource *pResIo)
{
   bus_space_tag_t    memt;
   bus_space_handle_t memh;
   bus_space_tag_t    iot;
   bus_space_handle_t ioh;
   int                iUnit;
   u_int32_t          ul;
   struct timeval     tvStart;
   struct timeval     tvCurr;
   int                iCurrTime;
   int                iTimeout;

   /* resource tag and handle are needed further on */
   memt  = rman_get_bustag (pResMem);
   memh  = rman_get_bushandle (pResMem);
   iot   = rman_get_bustag (pResIo);
   ioh   = rman_get_bushandle (pResIo);
   iUnit = device_get_unit (dev);
   
   DBG (LOG_DEBUG, iUnit, "Check PCI_OUT_INT_MASK");
   avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_PCI_OUT_INT_MASK, 0x0C);
   ul = avmarm_mem_readl (memt, memh, AVMAIC_ARM_REG_PCI_OUT_INT_MASK);
   if (ul != 0x0C)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", PCI_OUT_INT_MASK register failed (%u)\n",
                     device_get_desc (dev), (unsigned) ul);
      return (-1);
   }
   
   DBG (LOG_DEBUG, iUnit, "Reset board");
   avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_DOORBELL,
                      AVMAIC_DBELL_RESET_ARM);
   DELAY (AVMAIC_IO_POLL_DELAY);
   getmicrotime (&tvStart);
   iCurrTime = 0;
   iTimeout = 10 * hz;
   while (avmarm_mem_readl (memt, memh, AVMAIC_ARM_REG_DOORBELL) != 0xFFFFFFFF &&
          iTimeout >= iCurrTime)
   {
      DELAY (AVMAIC_IO_POLL_DELAY);
      avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_DOORBELL,
                         AVMAIC_DBELL_ADDR);
      DELAY (AVMAIC_IO_POLL_DELAY);

      getmicrotime (&tvCurr);
      timevalsub (&tvCurr, &tvStart);
      iCurrTime = tvCurr.tv_usec * hz / 1000000 + tvCurr.tv_sec * hz;
   }
   if (iTimeout < iCurrTime &&
       avmarm_mem_readl (memt, memh, AVMAIC_ARM_REG_DOORBELL) != 0xFFFFFFFF)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", resetting board unsuccessful (timeout)\n",
                     device_get_desc (dev));
      return (-1);
   }
   (void) avmarm_poke (memt, memh,
                       AVMAIC_DC21285_ARMCSR_BASE + AVMAIC_CHAN_1_CONTROL, 0);
   (void) avmarm_poke (memt, memh,
                       AVMAIC_DC21285_ARMCSR_BASE + AVMAIC_CHAN_2_CONTROL, 0);

   DBG (LOG_DEBUG, iUnit, "Check access of mailbox 0");
   avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_MAILBOX_0, 0x55AA55AA);
   ul = avmarm_mem_readl (memt, memh, AVMAIC_ARM_REG_MAILBOX_0);
   if (ul != 0x55AA55AA)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", first check of mailbox 0 failed (%u)\n",
                     device_get_desc (dev), (unsigned) ul);
      return (-1);
   }
   avmarm_mem_writel (memt, memh, AVMAIC_ARM_REG_MAILBOX_0, 0xAA55AA55);
   ul = avmarm_mem_readl (memt, memh, AVMAIC_ARM_REG_MAILBOX_0);
   if (ul != 0xAA55AA55)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", second check of mailbox 0 failed (%u)\n",
                     device_get_desc (dev), (unsigned) ul);
      return (-1);
   }
   
   DBG (LOG_DEBUG, iUnit, "Configure board interface");
   if (avmarm_poke (memt, memh,
                    AVMAIC_DC21285_ARMCSR_BASE + AVMAIC_DBELL_SA_MASK, 0) != 0)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", setting DBELL_SA_MASK failed\n",
                     device_get_desc (dev));
      return (-1);
   }
   if (avmarm_poke (memt, memh,
                    AVMAIC_DC21285_ARMCSR_BASE + AVMAIC_DBELL_PCI_MASK, 0) != 0)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", setting DBELL_PCI_MASK failed\n",
                     device_get_desc (dev));
      return (-1);
   }
   if (avmarm_poke (memt, memh,
                    AVMAIC_DC21285_ARMCSR_BASE + AVMAIC_SA_CONTROL,
                    AVMAIC_SA_CTL_ALLRIGHT) != 0)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", setting SA_CONTROL failed\n",
                     device_get_desc (dev));
      return (-1);
   }
   if (avmarm_poke (memt, memh,
                    AVMAIC_DC21285_ARMCSR_BASE + AVMAIC_XBUS_CYCLE,
                    AVMAIC_INIT_XBUS_CYCLE) != 0)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", setting XBUS_CYCLE failed\n",
                     device_get_desc (dev));
      return (-1);
   }
   if (avmarm_poke (memt, memh,
                    AVMAIC_DC21285_ARMCSR_BASE + AVMAIC_XBUS_STROBE,
                    AVMAIC_INIT_XBUS_STROBE) != 0)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", setting XBUS_STROBE failed\n",
                     device_get_desc (dev));
      return (-1);
   }
   if (avmarm_poke (memt, memh,
                    AVMAIC_DC21285_ARMCSR_BASE + AVMAIC_DRAM_TIMING, 0) != 0)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", initialization of DRAM_TIMING failed\n",
                     device_get_desc (dev));
      return (-1);
   }
   DELAY (1 * 1000);
   
   if (avmarm_peek (memt, memh, AVMAIC_DC21285_DRAM_A0MR, &ul) != 0)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", reading DRAM_A0MR failed\n",
                     device_get_desc (dev));
      return (-1);
   }
   if (avmarm_peek (memt, memh, AVMAIC_DC21285_DRAM_A1MR, &ul) != 0)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", reading DRAM_A1MR failed\n",
                     device_get_desc (dev));
      return (-1);
   }
   if (avmarm_peek (memt, memh, AVMAIC_DC21285_DRAM_A2MR, &ul) != 0)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", reading DRAM_A2MR failed\n",
                     device_get_desc (dev));
      return (-1);
   }
   if (avmarm_peek (memt, memh, AVMAIC_DC21285_DRAM_A3MR, &ul) != 0)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", reading DRAM_A3MR failed\n",
                     device_get_desc (dev));
      return (0);
   }
   
   if (avmarm_poke (memt, memh,
                    AVMAIC_DC21285_DRAM_A0MR + AVMAIC_CAS_OFFSET, 0) != 0)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", setting CAS_OFFSET for DRAM_A0MR failed\n",
                     device_get_desc (dev));
      return (-1);
   }
   if (avmarm_poke (memt, memh,
                    AVMAIC_DC21285_DRAM_A1MR + AVMAIC_CAS_OFFSET, 0) != 0)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", setting CAS_OFFSET for DRAM_A1MR failed\n",
                     device_get_desc (dev));
      return (-1);
   }
   if (avmarm_poke (memt, memh,
                    AVMAIC_DC21285_DRAM_A2MR + AVMAIC_CAS_OFFSET, 0) != 0)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", setting CAS_OFFSET for DRAM_A2MR failed\n",
                     device_get_desc (dev));
      return (-1);
   }
   if (avmarm_poke (memt, memh,
                    AVMAIC_DC21285_DRAM_A3MR + AVMAIC_CAS_OFFSET, 0) != 0)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", setting CAS_OFFSET for DRAM_A3MR failed\n",
                     device_get_desc (dev));
      return (-1);
   }
   DELAY (1 * 1000);
   
   if (avmarm_poke (memt, memh, AVMAIC_DC21285_ARMCSR_BASE + AVMAIC_DRAM_TIMING,
                    AVMAIC_DRAM_TIMING_DEF) != 0)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", setting default DRAM_TIMING failed\n",
                     device_get_desc (dev));
      return (-1);
   }
   if (avmarm_poke (memt, memh,
                    AVMAIC_DC21285_ARMCSR_BASE + AVMAIC_DRAM_ADDR_SIZE_0,
                    AVMAIC_DRAM_AD_SZ_DEF0) != 0)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", setting DRAM_ADDR_SIZE_0 failed\n",
                     device_get_desc (dev));
      return (-1);
   }
   if (avmarm_poke (memt, memh,
                    AVMAIC_DC21285_ARMCSR_BASE + AVMAIC_DRAM_ADDR_SIZE_1,
                    AVMAIC_DRAM_AD_SZ_NULL) != 0)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", setting DRAM_ADDR_SIZE_1 failed\n",
                     device_get_desc (dev));
      return (-1);
   }
   if (avmarm_poke (memt, memh,
                    AVMAIC_DC21285_ARMCSR_BASE + AVMAIC_DRAM_ADDR_SIZE_2,
                    AVMAIC_DRAM_AD_SZ_NULL) != 0)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", setting DRAM_ADDR_SIZE_2 failed\n",
                     device_get_desc (dev));
      return (-1);
   }
   if (avmarm_poke (memt, memh,
                    AVMAIC_DC21285_ARMCSR_BASE + AVMAIC_DRAM_ADDR_SIZE_3,
                    AVMAIC_DRAM_AD_SZ_NULL) != 0)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", setting DRAM_ADDR_SIZE_3 failed\n",
                     device_get_desc (dev));
      return (-1);
   }
   
   DBG (LOG_DEBUG, iUnit, "Perform transputer test");
   if (avmarm_poke (memt, memh, 0x00000000, 0x11111111) != 0 ||
       avmarm_poke (memt, memh, 0x00400000, 0x22222222) != 0 ||
       avmarm_poke (memt, memh, 0x00800000, 0x33333333) != 0 ||
       avmarm_poke (memt, memh, 0x00C00000, 0x44444444) != 0)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", first write sequence of transputer test failed\n",
                     device_get_desc (dev));
      return (-1);
   }
   if (avmarm_peek (memt, memh, 0x00000000, &ul) != 0 || ul != 0x11111111 ||
       avmarm_peek (memt, memh, 0x00400000, &ul) != 0 || ul != 0x22222222 ||
       avmarm_peek (memt, memh, 0x00800000, &ul) != 0 || ul != 0x33333333 ||
       avmarm_peek (memt, memh, 0x00C00000, &ul) != 0 || ul != 0x44444444)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", first read sequence of transputer test failed\n",
                     device_get_desc (dev));
      return (-1);
   }
   if (avmarm_poke (memt, memh, 0x00000000, 0x55555555) != 0 ||
       avmarm_poke (memt, memh, 0x00400000, 0x66666666) != 0 ||
       avmarm_poke (memt, memh, 0x00800000, 0x77777777) != 0 ||
       avmarm_poke (memt, memh, 0x00C00000, 0x88888888) != 0)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", second write sequence of transputer test failed\n",
                     device_get_desc (dev));
      return (-1);
   }
   if (avmarm_peek (memt, memh, 0x00000000, &ul) != 0 || ul != 0x55555555 ||
       avmarm_peek (memt, memh, 0x00400000, &ul) != 0 || ul != 0x66666666 ||
       avmarm_peek (memt, memh, 0x00800000, &ul) != 0 || ul != 0x77777777 ||
       avmarm_peek (memt, memh, 0x00C00000, &ul) != 0 || ul != 0x88888888)
   {
      device_printf (dev,
                     "Probe failed for card type \"%s\", second read sequence of transputer test failed\n",
                     device_get_desc (dev));
      return (-1);
   }
   
   /* if we reach this point, the card was successfully identified */
   DBG (LOG_DEBUG, iUnit, "All checks successful");
   return (0);
} /* avmarm_detect */





/*
        initialize the softc structure
        ------------------------------
        
        Parameters:
           dev                  I: the device to initialize
           pSc                  I: the softc structure of the device to
                                   initialize

        Result:
           0                    structure initialized successfully
           errno value          failure
*/

static int avmarm_init_softc
   (device_t    dev,
    AvmAicSc_t *pSc)
{
   AvmAicPortData_t *pPortData;
   int               i;
   int               iRes;

   pSc->pfnAppRegister = avmarm_app_register;
   pSc->pfnAppRelease  = avmarm_app_release;
   pSc->pfnPutMessage  = avmarm_put_message;
   pSc->pfnResetCtlr   = avmarm_reset_ctlr;
   
   if (pSc->cardType == AVMAIC_CARD_TYPE_C2)
   {
      pSc->nPorts = 2;
   }
   else if (pSc->cardType == AVMAIC_CARD_TYPE_C4)
   {
      pSc->nPorts = 4;
   }
   else
   {
      pSc->nPorts = 1;
   }
   snprintf (pSc->szCardName, sizeof (pSc->szCardName),
             "AVMC%zu-%d", pSc->nPorts, pSc->iUnit + 1);
   strncpy (pSc->szDriverName, device_get_driver (dev)->name,
            sizeof (pSc->szDriverName) - 1);
   pSc->state = AVMAIC_STATE_DOWN;
   pSc->nPortsInitialized = 0;
   for (i = 0; (size_t) i < pSc->nPorts; i++)
   {
      pPortData = &(pSc->aPortData [i]);
      pPortData->pSc                = pSc;
      pPortData->uPortIdx           = (unsigned) i;
      pPortData->state              = AVMAIC_STATE_DOWN;
      snprintf (pPortData->szCtlrPortName, sizeof (pPortData->szCtlrPortName),
                "%s/%u",
                pSc->szCardName, pPortData->uPortIdx + 1);
      pPortData->uUniqueCapiCtlrNum = 0;
      pPortData->uDrvCapiCtlrNum    = 0;
      pPortData->nBChns             = 2;
   }
   pSc->ulRecvMsgBufLength = 0;
   pSc->ulSendMsgBufLength = 0;
   iRes = avmmisc_init_busdma (dev, pSc);
   if (iRes != 0)
   {
      return (iRes);
   }
   pSc->ulDmaIcsr = 0;
   pSc->sendQueue.ifq_len = 0;
   pSc->sendQueue.ifq_maxlen = pSc->nPorts * pSc->aPortData [0].nBChns * 16;
   pSc->sendQueue.ifq_drops = 0;

   /* The number of B-channels must be copied to the profile. So it is possible
    * to register the controller at i4b before the software is downloaded.
    */
   for (i = 0; (size_t) i < pSc->nPorts; i++)
   {
      pPortData = &(pSc->aPortData [i]);
      pPortData->abCapiProfile [2] = (pPortData->nBChns & 0xFF);
      pPortData->abCapiProfile [3] = ((pPortData->nBChns & 0xFF00) >> 8);
   }

   pSc->fIntrActive   = 0;
   pSc->fOpInProgress = 0;
   pSc->fWaitingForRc = 0;
   pSc->uRc           = CAPI_OK;
   
   mtx_init (&(pSc->mtxAccess), AVMMISC_ACCESS_MUTEX_MSG, NULL, MTX_DEF);
   cv_init (&(pSc->cvNotify), AVMMISC_NOTIFY_CONDITION_MSG);
   
   return (0);
} /* avmarm_init_softc */
