/**
 * @file ix1a_ppci.c
 *
 * IX1a-PrimaryPCI - Routines and specifications for IX1 Primary PCI boards.
 *
 * Copyright: 2004 Thomas Wintergerst. All rights reserved.
 *
 * $Id: ix1a_ppci.c,v 1.1.2.1 2006/02/28 21:35:22 thomas Exp $
 * $Project:    CAPI for BSD $
 * $Target:     ix1a - CAPI manager driver for IX1 active ISDN controllers $
 * @date        15.12.2004
 * @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/errno.h>
#include <sys/kernel.h>
#include <sys/endian.h>
#include <sys/mbuf.h>
#include <machine/bus.h>
#include <sys/rman.h>
#include <sys/bus.h>
#include <machine/resource.h>

/* Import includes */

#define __IX1A_PPCI__

/* Local includes */
#include <c4b/ix1a/ix1a_var.h>
#include <c4b/ix1a/ix1a_misc.h>
#include <c4b/ix1a/ix1a_ppci.h>
#include <c4b/ix1a/ix1a_hwdefs.h>
#include <c4b/ix1a/ix1a_board_params.h>
#include <c4b/ix1a/ix1a_shm.h>





/* === Private definitions =============================================== */





/**
 * Data structure to save the state of the decoding address of the Galileo
 * chip.
 */
typedef struct
{
   u_int32_t dwGtCS3LowDecodeAddr;
   u_int32_t dwGtCS3HighDecodeAddr;
} Ix1aPpciSaveMmrConfig_t;



/* --- Definitions to configure the Galileo chip and the DRAM --- */

/** Enumeration for the devices available on a Primary board. */
typedef enum
{
   IX1A_PPCI_DEV_Dram0,         /**< DRAM bank 0, or GT-64010(A). */
   IX1A_PPCI_DEV_Dram1,         /**< DRAM bank 1, or GT-64010(A), can not used. */
   IX1A_PPCI_DEV_Dram2,         /**< DRAM bank 2, or GT-64010(A). */
   IX1A_PPCI_DEV_Dram3,         /**< DRAM bank 3, or GT-64010(A), can not used. */
   IX1A_PPCI_DEV_Gt64010,       /**< GT-64010 memory map base address. */
   IX1A_PPCI_DEV_PciIo,         /**< PCI base access window for I/O accesses. */
   IX1A_PPCI_DEV_PciMem,        /**< PCI base access window for Memory accesses. */
   IX1A_PPCI_DEV_Falc,          /**< FALC base address, GT-64010(A) DEV0. */
   IX1A_PPCI_DEV_Uart,          /**< UART base address, GT-64010(A) DEV1. */
   IX1A_PPCI_DEV_Mcs,           /**< MCS base address, GT-64010(A) DEV2. */
   IX1A_PPCI_DEV_Fmic,          /**<
                                 * FMIC base address, GT-64010(A) DEV3 and PLD
                                 * decoding.
                                 */
   IX1A_PPCI_DEV_SCreg,         /**<
                                 * Status & Control Register base address,
                                 * GT-64010(A) DEV3 and PLD decoding.
                                 */
   IX1A_PPCI_DEV_Rreg,          /**<
                                 * Reset Register base address, GT-64010(A)
                                 * DEV3 and PLD decoding.
                                 * */
   IX1A_PPCI_DEV_Ereg,          /**<
                                 * External Register base address, GT-64010(A)
                                 * DEV3 and PLD decoding.
                                 */
   IX1A_PPCI_DEV_Flash,         /**<
                                 * Flash Memory minimum 128 kByte, up to 512
                                 * KByte, GT-64010(A) DEVBOOT.
                                 */
   IX1A_PPCI_DEV_MaxDev         /**< Maximum Count of device elements. */
} Ix1a_PriDevs_t;

/** @name SPECIAL DEFINEMENTS FOR GT-64010(A) DEVICE MAPPING */
/** @{ */
#define IX1A_PPCI_DEVMAP_GT_RAM0        0
#define IX1A_PPCI_DEVMAP_GT_RAM1        1
#define IX1A_PPCI_DEVMAP_GT_RAM2        2
#define IX1A_PPCI_DEVMAP_GT_RAM3        3
#define IX1A_PPCI_DEVMAP_GT_DEV0        4
#define IX1A_PPCI_DEVMAP_GT_DEV1        5
#define IX1A_PPCI_DEVMAP_GT_DEV2        6
#define IX1A_PPCI_DEVMAP_GT_DEV3        7
#define IX1A_PPCI_DEVMAP_GT_BOOT        8
#define IX1A_PPCI_DEVMAP_GT_MAXDEV      9
/** @} */

/** @name SPECIAL DEFINEMENTS FOR GT-64010(A) DEVICE SEGMENT MAPPING */
/** @{ */
#define IX1A_PPCI_SEGMAP_GT_RAM01      0
#define IX1A_PPCI_SEGMAP_GT_RAM23      1
#define IX1A_PPCI_SEGMAP_GT_DEV02      2
#define IX1A_PPCI_SEGMAP_GT_DEV3B      3
#define IX1A_PPCI_SEGMAP_GT_PCIO       4
#define IX1A_PPCI_SEGMAP_GT_PCME       5
#define IX1A_PPCI_SEGMAP_GT_MAXSEG     6
/** @} */

/** Kernel 1 segment, direct mapped. */
#define IX1A_PPCI_KERNL1        0xA0000000UL

#define IX1A_PPCI_SEGONLY       1
#define IX1A_PPCI_TOP           0xFFFFFFFF
#define IX1A_PPCI_NOMAP         -1
#define IX1A_PPCI_NOSEG         -1
#define IX1A_PPCI_DEFAULT       0
#define IX1A_PPCI_SEGONLY       1

#define IX1A_PPCI_GT_RAMRFSH    256     /* 256 Refresh cycles, ... */
#define IX1A_PPCI_GT_RAMRFSHMASK 0x00073FFF

#define IX1A_PPCI_GT_RAMCONF    0x448   /* DRAM bank 0..3 Refresh and other configuration */

#define IX1A_PPCI_GT_RAM01CPL   0x008   /* DRAM bank 0,1 CPU start address */
#define IX1A_PPCI_GT_RAM01CPH   0x010   /* DRAM bank 0,1 CPU end address */

#define IX1A_PPCI_GT_RAM0LOW    0x400   /* DRAM bank 0 start address */
#define IX1A_PPCI_GT_RAM0HIGH   0x404   /* DRAM bank 0 end address */
#define IX1A_PPCI_GT_RAM0PARM   0x44C   /* DRAM bank 0 cycle paramters */

#define IX1A_PPCI_GT_RAM1LOW    0x408   /* DRAM bank 1 start address */
#define IX1A_PPCI_GT_RAM1HIGH   0x40C   /* DRAM bank 1 end address */
#define IX1A_PPCI_GT_RAM1PARM   0x450   /* DRAM bank 1 cycle paramters */

#define IX1A_PPCI_GT_RAM23CPL   0x018   /* DRAM bank 2,3 CPU start address */
#define IX1A_PPCI_GT_RAM23CPH   0x020   /* DRAM bank 2,3 CPU end address */
#define IX1A_PPCI_GT_RAM2LOW    0x410   /* DRAM bank 2 start address */
#define IX1A_PPCI_GT_RAM2HIGH   0x414   /* DRAM bank 2 end address */
#define IX1A_PPCI_GT_RAM2PARM   0x454   /* DRAM bank 2 cycle paramters */

#define IX1A_PPCI_GT_RAM3LOW    0x418   /* DRAM bank 3 start address */
#define IX1A_PPCI_GT_RAM3HIGH   0x41C   /* DRAM bank 3 end address */
#define IX1A_PPCI_GT_RAM3PARM   0x458   /* DRAM bank 3 cycle paramters */

#define IX1A_PPCI_GT_DEV02CPL   0x028   /* Device bank 0,1,2 CPU start address */
#define IX1A_PPCI_GT_DEV02CPH   0x030   /* Device bank 0,1,2 CPU end address */
#define IX1A_PPCI_GT_DEV0LOW    0x420   /* Device bank 0 start address */
#define IX1A_PPCI_GT_DEV0HIGH   0x424   /* Device bank 0 end address */
#define IX1A_PPCI_GT_DEV0PARM   0x45C   /* Device bank 0 cycle paramters */

#define IX1A_PPCI_GT_DEV1LOW    0x428   /* Device bank 1 start address */
#define IX1A_PPCI_GT_DEV1HIGH   0x42c   /* Device bank 1 end address */
#define IX1A_PPCI_GT_DEV1PARM   0x460   /* Device bank 1 cycle paramters */

#define IX1A_PPCI_GT_DEV2LOW    0x430   /* Device bank 2 start address */
#define IX1A_PPCI_GT_DEV2HIGH   0x434   /* Device bank 2 end address */
#define IX1A_PPCI_GT_DEV2PARM   0x464   /* Device bank 2 cycle paramters */

#define IX1A_PPCI_GT_DEV3BCPL   0x038   /* Device bank 3 and boot bank start address */
#define IX1A_PPCI_GT_DEV3BCPH   0x040   /* Device bank 3 and boot bank end address */
#define IX1A_PPCI_GT_DEV3LOW    0x438   /* Device bank 3 start address */
#define IX1A_PPCI_GT_DEV3HIGH   0x43c   /* Device bank 3 end address */
#define IX1A_PPCI_GT_DEV3PARM   0x468   /* Device bank 3 cycle paramters */

#define IX1A_PPCI_GT_BOOTLOW    0x440   /* Boot-bank start address */
#define IX1A_PPCI_GT_BOOTHIGH   0x444   /* Boot-bank end address */
#define IX1A_PPCI_GT_BOOTPARM   0x46C   /* Boot-bank cycle paramters */

#define IX1A_PPCI_GT_PCICONF    0xC04   /* PCI Time-Out and Retry definitions */
#define IX1A_PPCI_GT_PCIIOCPL   0x048   /* PCI I/O access low address, CPU side */
#define IX1A_PPCI_GT_PCIIOCPH   0x050   /* PCI I/O access high address, CPU side */
#define IX1A_PPCI_GT_PCIMEMPL   0x058   /* PCI Memory access low address, CPU side */
#define IX1A_PPCI_GT_PCIMEMPH   0x060   /* PCI Memory access high address, CPU side */

#define IX1A_PPCI_GT_NOCONF     0

typedef struct
{
   unsigned long  ulBase;       /* Base address */
   Ix1a_PriDevs_t eDevice;      /* PCI Primary device name, coded as in PriDevs */
   unsigned long  ulSize;       /* Memory Size of device */
   int            iDevMap;      /* Device Subdecoding selection */
   unsigned long  ulConf;       /* device configuration in GT-64010(A) device address space */
   unsigned long  ulMask;       /* device configuration mask */
   int            iDevSeg;      /* Device Segment selection in GT-64010(A) device map */
   int            iAttrib;      /* special attributes for device type */
} Ix1aPpciDevDscType_t;

/** GLOBAL DEVICE DFINITIONS. */
/** @{ */
#define IX1A_PPCI_GT_DEV32      0x16634BBA      /* Standard 32 bit Device Configuration */
#define IX1A_PPCI_GT_DEV8       0x16434BBA      /* Standard  8 bit Device Configuration */
#define IX1A_PPCI_GT_DEVMASK    0x42BFFFFF      /* Device Configuration Mask, bits which should be modified */
#define IX1A_PPCI_DEV_8BIT      IX1A_PPCI_GT_DEV8,  IX1A_PPCI_GT_DEVMASK /* 8 bit device structure definition */
#define IX1A_PPCI_DEV_32BIT     IX1A_PPCI_GT_DEV32, IX1A_PPCI_GT_DEVMASK /* 32 bit device structure definition */
/** @} */

/** GLOBAL PCI DFINITIONS. */
/** @{ */
#define IX1A_PPCI_GT_PCITO      0x000AFFFF      /* PCI Time-Out 256 cycle 1st acess, 128 cycle next access */
#define IX1A_PPCI_GT_PCITOMASK  0x00FFFFFF      /* PCI Time-Out mask */
#define IX1A_PPCI_PCI_CONF      IX1A_PPCI_GT_PCITO, IX1A_PPCI_GT_PCITOMASK  /* PCI device structure definition */
/** @} */

/** GLOBAL DRAM DFINITIONS. */
/** @{ */
#define IX1A_PPCI_GT_RAMCYCMASK 0x1FF
#define IX1A_PPCI_GT_RAMCYCP    0x50            /* 1CAS, 2RAS, 64bit */
#define IX1A_PPCI_RAM_CONFP     IX1A_PPCI_GT_RAMCYCP, IX1A_PPCI_GT_RAMCYCMASK /* PCI device structure definition */
/** @} */

#define IX1A_PPCI_NO_CONF       0, 0

#define IX1A_PPCI_MB0           0x00000000UL
#define IX1A_PPCI_MB1           0x00100000UL
#define IX1A_PPCI_MB2           0x00200000UL
#define IX1A_PPCI_MB4           0x00400000UL
#define IX1A_PPCI_MB8           0x00800000UL

static const Ix1aPpciDevDscType_t g_DscFlash [IX1A_PPCI_DEV_MaxDev] =
   {
      { 0x1FC00000, IX1A_PPCI_DEV_Flash,   IX1A_PPCI_MB1,  IX1A_PPCI_DEVMAP_GT_BOOT, IX1A_PPCI_DEV_8BIT,  IX1A_PPCI_SEGMAP_GT_DEV3B, IX1A_PPCI_DEFAULT },
      { 0x1F600000, IX1A_PPCI_DEV_SCreg,   IX1A_PPCI_MB2,  IX1A_PPCI_DEVMAP_GT_DEV3, IX1A_PPCI_DEV_32BIT, IX1A_PPCI_SEGMAP_GT_DEV3B, IX1A_PPCI_DEFAULT },
      { 0x1F400000, IX1A_PPCI_DEV_Rreg,    IX1A_PPCI_MB2,  IX1A_PPCI_DEVMAP_GT_DEV3, IX1A_PPCI_DEV_32BIT, IX1A_PPCI_SEGMAP_GT_DEV3B, IX1A_PPCI_DEFAULT },
      { 0x1F200000, IX1A_PPCI_DEV_Ereg,    IX1A_PPCI_MB2,  IX1A_PPCI_DEVMAP_GT_DEV3, IX1A_PPCI_DEV_32BIT, IX1A_PPCI_SEGMAP_GT_DEV3B, IX1A_PPCI_DEFAULT },
      { 0x1F000000, IX1A_PPCI_DEV_Fmic,    IX1A_PPCI_MB2,  IX1A_PPCI_DEVMAP_GT_DEV3, IX1A_PPCI_DEV_32BIT, IX1A_PPCI_SEGMAP_GT_DEV3B, IX1A_PPCI_DEFAULT },
      { 0x1E500000, IX1A_PPCI_DEV_Mcs,     IX1A_PPCI_MB1,  IX1A_PPCI_DEVMAP_GT_DEV2, IX1A_PPCI_DEV_32BIT, IX1A_PPCI_SEGMAP_GT_DEV02, IX1A_PPCI_DEFAULT },
      { 0x1E300000, IX1A_PPCI_DEV_Uart,    IX1A_PPCI_MB1,  IX1A_PPCI_DEVMAP_GT_DEV1, IX1A_PPCI_DEV_32BIT, IX1A_PPCI_SEGMAP_GT_DEV02, IX1A_PPCI_DEFAULT },
      { 0x1E000000, IX1A_PPCI_DEV_Falc,    IX1A_PPCI_MB1,  IX1A_PPCI_DEVMAP_GT_DEV0, IX1A_PPCI_DEV_32BIT, IX1A_PPCI_SEGMAP_GT_DEV02, IX1A_PPCI_DEFAULT },
      { 0x16000000, IX1A_PPCI_DEV_PciMem,  IX1A_PPCI_MB4,  IX1A_PPCI_NOMAP,          IX1A_PPCI_PCI_CONF,  IX1A_PPCI_SEGMAP_GT_PCME,  IX1A_PPCI_DEFAULT },
      { 0x15000000, IX1A_PPCI_DEV_PciIo,   IX1A_PPCI_MB4,  IX1A_PPCI_NOMAP,          IX1A_PPCI_PCI_CONF,  IX1A_PPCI_SEGMAP_GT_PCIO,  IX1A_PPCI_DEFAULT },
      { 0x14000000, IX1A_PPCI_DEV_Gt64010, IX1A_PPCI_MB1,  IX1A_PPCI_NOMAP,          IX1A_PPCI_NO_CONF,   IX1A_PPCI_NOSEG,           IX1A_PPCI_DEFAULT },
      { 0x00800000, IX1A_PPCI_DEV_Dram3,   IX1A_PPCI_MB0,  IX1A_PPCI_DEVMAP_GT_RAM3, IX1A_PPCI_RAM_CONFP, IX1A_PPCI_SEGMAP_GT_RAM23, IX1A_PPCI_DEFAULT },
      { 0x00800000, IX1A_PPCI_DEV_Dram2,   IX1A_PPCI_MB8,  IX1A_PPCI_DEVMAP_GT_RAM2, IX1A_PPCI_RAM_CONFP, IX1A_PPCI_SEGMAP_GT_RAM23, IX1A_PPCI_SEGONLY },
      { 0x00000000, IX1A_PPCI_DEV_Dram1,   IX1A_PPCI_MB0,  IX1A_PPCI_DEVMAP_GT_RAM1, IX1A_PPCI_RAM_CONFP, IX1A_PPCI_SEGMAP_GT_RAM01, IX1A_PPCI_DEFAULT },
      { 0x00000000, IX1A_PPCI_DEV_Dram0,   IX1A_PPCI_MB8,  IX1A_PPCI_DEVMAP_GT_RAM0, IX1A_PPCI_RAM_CONFP, IX1A_PPCI_SEGMAP_GT_RAM01, IX1A_PPCI_SEGONLY }
   };

typedef struct
{
   char          *pszName;      /* Device name */
   unsigned long  ulConf;       /* Device Configuration Register I/O Address */
   unsigned int   uLow;         /* Device Low Address range Register I/O Address */
   unsigned long  ulLowMask;    /* Mask all valid Low address bits */
   unsigned int   uHigh;        /* Device High Address range Register I/O Address */
   unsigned long  ulHighMask;   /* Mask all valid High address bits */
   unsigned int   uShift;       /* address shift */
} Ix1aGtAccDsc_t;

#define IX1A_PPCI_GT_X_DEVMASK  0x000000FF
#define IX1A_PPCI_GT_X_DEVSHIFT 20
#define IX1A_PPCI_GT_SEGLMASK   0x00007FFF
#define IX1A_PPCI_GT_SEGHMASK   0x0000007F
#define IX1A_PPCI_GT_SEGSHIFT   21

/** Device Map addresses. */
static const Ix1aGtAccDsc_t g_GT_Dev [IX1A_PPCI_DEVMAP_GT_MAXDEV] =
{ 
   { "RAM0",  IX1A_PPCI_GT_RAM0PARM, IX1A_PPCI_GT_RAM0LOW, IX1A_PPCI_GT_X_DEVMASK, IX1A_PPCI_GT_RAM0HIGH, IX1A_PPCI_GT_X_DEVMASK, IX1A_PPCI_GT_X_DEVSHIFT },
   { "RAM1",  IX1A_PPCI_GT_RAM1PARM, IX1A_PPCI_GT_RAM1LOW, IX1A_PPCI_GT_X_DEVMASK, IX1A_PPCI_GT_RAM1HIGH, IX1A_PPCI_GT_X_DEVMASK, IX1A_PPCI_GT_X_DEVSHIFT },
   { "RAM2",  IX1A_PPCI_GT_RAM2PARM, IX1A_PPCI_GT_RAM2LOW, IX1A_PPCI_GT_X_DEVMASK, IX1A_PPCI_GT_RAM2HIGH, IX1A_PPCI_GT_X_DEVMASK, IX1A_PPCI_GT_X_DEVSHIFT },
   { "RAM3",  IX1A_PPCI_GT_RAM3PARM, IX1A_PPCI_GT_RAM3LOW, IX1A_PPCI_GT_X_DEVMASK, IX1A_PPCI_GT_RAM3HIGH, IX1A_PPCI_GT_X_DEVMASK, IX1A_PPCI_GT_X_DEVSHIFT },
   { "DEV0",  IX1A_PPCI_GT_DEV0PARM, IX1A_PPCI_GT_DEV0LOW, IX1A_PPCI_GT_X_DEVMASK, IX1A_PPCI_GT_DEV0HIGH, IX1A_PPCI_GT_X_DEVMASK, IX1A_PPCI_GT_X_DEVSHIFT },
   { "DEV1",  IX1A_PPCI_GT_DEV1PARM, IX1A_PPCI_GT_DEV1LOW, IX1A_PPCI_GT_X_DEVMASK, IX1A_PPCI_GT_DEV1HIGH, IX1A_PPCI_GT_X_DEVMASK, IX1A_PPCI_GT_X_DEVSHIFT },
   { "DEV2",  IX1A_PPCI_GT_DEV2PARM, IX1A_PPCI_GT_DEV2LOW, IX1A_PPCI_GT_X_DEVMASK, IX1A_PPCI_GT_DEV2HIGH, IX1A_PPCI_GT_X_DEVMASK, IX1A_PPCI_GT_X_DEVSHIFT },
   { "DEV3",  IX1A_PPCI_GT_DEV3PARM, IX1A_PPCI_GT_DEV3LOW, IX1A_PPCI_GT_X_DEVMASK, IX1A_PPCI_GT_DEV3HIGH, IX1A_PPCI_GT_X_DEVMASK, IX1A_PPCI_GT_X_DEVSHIFT },
   { "BOOT",  IX1A_PPCI_GT_BOOTPARM, IX1A_PPCI_GT_BOOTLOW, IX1A_PPCI_GT_X_DEVMASK, IX1A_PPCI_GT_BOOTHIGH, IX1A_PPCI_GT_X_DEVMASK, IX1A_PPCI_GT_X_DEVSHIFT }
};

/** CPU Segment Device addresses. */
static const Ix1aGtAccDsc_t g_GT_CSeg [IX1A_PPCI_SEGMAP_GT_MAXSEG] =
{
   { "RAM01",   IX1A_PPCI_GT_NOCONF,  IX1A_PPCI_GT_RAM01CPL, IX1A_PPCI_GT_SEGLMASK, IX1A_PPCI_GT_RAM01CPH, IX1A_PPCI_GT_SEGHMASK, IX1A_PPCI_GT_SEGSHIFT },
   { "RAM23",   IX1A_PPCI_GT_NOCONF,  IX1A_PPCI_GT_RAM23CPL, IX1A_PPCI_GT_SEGLMASK, IX1A_PPCI_GT_RAM23CPH, IX1A_PPCI_GT_SEGHMASK, IX1A_PPCI_GT_SEGSHIFT },
   { "DEV02",   IX1A_PPCI_GT_NOCONF,  IX1A_PPCI_GT_DEV02CPL, IX1A_PPCI_GT_SEGLMASK, IX1A_PPCI_GT_DEV02CPH, IX1A_PPCI_GT_SEGHMASK, IX1A_PPCI_GT_SEGSHIFT },
   { "DEV3B",   IX1A_PPCI_GT_NOCONF,  IX1A_PPCI_GT_DEV3BCPL, IX1A_PPCI_GT_SEGLMASK, IX1A_PPCI_GT_DEV3BCPH, IX1A_PPCI_GT_SEGHMASK, IX1A_PPCI_GT_SEGSHIFT },
   { "PCI I/O", IX1A_PPCI_GT_PCICONF, IX1A_PPCI_GT_PCIIOCPL, IX1A_PPCI_GT_SEGLMASK, IX1A_PPCI_GT_PCIIOCPH, IX1A_PPCI_GT_SEGHMASK, IX1A_PPCI_GT_SEGSHIFT },
   { "PCI MEM", IX1A_PPCI_GT_PCICONF, IX1A_PPCI_GT_PCIMEMPL, IX1A_PPCI_GT_SEGLMASK, IX1A_PPCI_GT_PCIMEMPH, IX1A_PPCI_GT_SEGHMASK, IX1A_PPCI_GT_SEGSHIFT }
};

/** Global Mips Device Discriptor Table. */
static unsigned long g_MipsBase [IX1A_PPCI_DEV_MaxDev];





/* === Definitions for public declarations =============================== */





/* === Prototypes for private functions ================================== */





/**
 * Interrupt routine for Primary PCI boards.
 *
 * @param pSc                   I/O: The softc structure for the board raising
 *                                 the interrupt.
 *
 * @return Nothing.
 */
static void ix1appci_interrupt_handler
   (Ix1aSc_t *pSc);

/**
 * Check if an interrupt is pending for a board.
 *
 * This function is called to check if the board specified has raised an
 * interrupt and is waiting for an acknowledge.
 *
 * @param pSc                   I/O: The board to check for a pending
 *                                 interrupt.
 *
 * @retval 0                    No interrupt is pending from the board.
 * @retval 1                    The board raised an interrupt and is waiting
 *                              for an acknowledge.
 */
static int ix1appci_is_int_pending
   (Ix1aSc_t *pSc);

/**
 * Acknowledge a pending interrupt to a board.
 *
 * This function is called to acknowledge a pending interrupt to the board
 * specified.
 *
 * @param pSc                   I/O: The board to acknowledge the interrupt to.
 *
 * @return Nothing.
 */
static void ix1appci_int_ack
   (Ix1aSc_t *pSc);

/**
 * Enable a Primary PCI board after disabling it.
 *
 * @note This routine expects the board to have been correctly initialised
 *       before disabling. So no attempt is made to determine the correctness
 *       of the board type.
 *
 * @param pSc                   I/O: The softc structure for the board to
 *                                 enable.
 *
 * @retval 0                    The board was successfully enabled.
 * @retval Else                 Enabling the board failed, the result is an
 *                              errno value.
 */
static int ix1appci_enable_board
   (Ix1aSc_t *pSc);

/**
 * Acquire access to the MMR resource part through the Galileo configuration.
 *
 * CS[3] must be remapped to be located in MMR physical window (host address
 * space).
 */
static void ix1appci_acquire_mmr
   (Ix1aSc_t                *pSc,
    Ix1aPpciSaveMmrConfig_t *pSaveMmrConfig);

/**
 * Restore the address decoding after accessing the MMR resource part.
 *
 * CS[3] must be remapped to be located in MIPS' address space.
 */
static void ix1appci_restore_mmr
   (Ix1aSc_t                      *pSc,
    const Ix1aPpciSaveMmrConfig_t *pSaveMmrConfig);

/**
 * Get write access to the reset register of the MMR resource part.
 *
 * @param pSc                   I/O: The softc structure for the board.
 *
 * @retval 0                    Write access successfully obtained.
 * @retval Else                 Failure.
 */
static int ix1appci_write_rr_unlock_sequence
   (Ix1aSc_t *pSc);

/**
 * Write a specific byte to the MMR reset register.
 *
 * This function is only called while downloading the board. We set the both
 * LEDs to yellow before writing to the reset register.
 *
 * @param pSc                   I/O: The softc structure for the board.
 * @param bToWrite              I: The byte to write.
 *
 * @retval 0                    The byte was written successfully.
 * @retval Else                 The write operation failed.
 */
static int ix1appci_write_mmr_rr
   (Ix1aSc_t *pSc,
    u_int8_t  bToWrite);

/**
 * Prepare the board for sending the firmware code.
 *
 * This operation sets LED1 to red and clears the first 2MB of the shared
 * memory resource. The memory will parly be overwritten with the firmware and
 * the line port parameters.
 *
 * @param pSc                   I/O: The softc structure for the board context.
 *
 * @return Nothing.
 */
static void ix1appci_prepare_for_firmware
   (Ix1aSc_t *pSc);

/**
 * Send the firmware code to the board.
 *
 * @param pSc                   I/O: The softc structure for the board context.
 * @param pbFirmwareCode        I: The address of the firmware code to send.
 * @param nLenFirmwareCode      I: The number of firmware code bytes to send.
 *
 * @retval 0                    Sending the firmware code was successful.
 * @retval Else                 Failure, the board did not respone to the
 *                              loading operations.
 */
static int ix1appci_send_firmware
   (Ix1aSc_t       *pSc,
    const u_int8_t *pbFirmwareCode,
    size_t          nLenFirmwareCode);

/**
 * Send line settings to the board.
 *
 * @param pSc                   I/O: The softc structure for the board context.
 * @param pbSettings            I: The address of the settings data to send.
 * @param nLenSettings          I: The number of settings bytes to send.
 *
 * @retval 0                    Sending the line settings was successful.
 * @retval Else                 Failure, the board did not respone to the
 *                              sending operation.
 */
static int ix1appci_send_port_params
   (Ix1aSc_t *pSc,
    u_int8_t *pbSettings,
    size_t    nLenSettings);

/**
 * Start the board firmware after a successful download operation.
 *
 * @param pSc                   I/O: The softc structure for the board context.
 *
 * @retval 0                    The board is now ready for operation.
 * @retval Else                 Starting the firmware failed, the board is in
 *                              undefined state.
 */
static int ix1appci_start_firmware
   (Ix1aSc_t *pSc);

/**
 * Program the Galileo chip for operation.
 *
 * Magic code, taken unchanged from the ITK / Digi Linux driver. Notes from the
 * original source code:
 *
 * If a Boot-Flash will be used, the standard GT-64010(A) DRAM and FLASH
 * configuration can be used for the start up.
 *
 * If the Primary-PCI is booted without any Boot-Flash present, some devices
 * will be remapped for the CPU for proper working. These are the DRAM bank 0
 * and bank 1 and naturally the Flash EPROM address space. With all SIMM DRAM
 * modules the DRAM bank 0 must be present. But only double sided SIMM DRAMs
 * has the second bank 1. So the bank 0 will be relocated as a mirrored devices
 * just 4 MByte under the Cold Boot Reset vector of the MIPS CPU. The standard
 * startup software will so find RAM "under" the standard Cold Boot Vector of
 * 0x1FC0.0000 and the system will also work with only one memory bank present.
 * If an additional DRAM bank or double sided SIMM will be present, it can be
 * relocated from the standard Memory start-up. And if unprogrammed
 * Flash-Memory will be present, it is relocated just under the upper RAM start
 * up.
 *
 * Shared Resources:
 * We use the RAM01 and RAM23 devices as shared devices from Host and Target:
 * Always shared devices can only be decoded from the GT-64010(A) by the first
 * master decoding registers, the PCI or CPU decoding. The device or
 * subdecoding register of RAM0 and RAM2 must be initialisated to the full
 * availible address range. So here a sub decoding is not possible but also not
 * needed, because the RAM1 and RAM3 are not supported from the hardware of the
 * PCI primary.
 *
 * CPU Halt, Start or Reset:
 * To reset or halt the target MIPS CPU from a PC via the PCI bus the DEV3
 * device must be relocated to the special PCI address range.
 *
 * Device accesses from Host PC via PCI bus:
 * Before a memory mapped device of the GT-64010(A) can be used from the host
 * PC via the PCI bus, the GT-64010(A) should be reconfigurated with new Time
 * Out values.
 *
 * ConfGt:
 * Configurates the GT-64010(A) from Host-PC side, instead of the target CPU.
 * As long as no Flash-EPROM start-up is present and as long as the GT-64010(A)
 * is shown with its orginal PCI-Device-ID, use this short code to configurate
 * the GT-64010(A).
 *
 * Memory Map:
 * The structure describes, which device will be mapped to which Base address.
 * The GT-64010(A) device segments and subsegments (maps) will be checked and
 * calculated from the device descriptor table. If no device overlapping is
 * found, ...
 * 
 * @param pSc                   I/O: The softc structure for the board.
 *
 * @retval 0                    The Galileo chip was successfully configured.
 * @retval Else                 Failure, the board is not operationable.
 */
static int ix1appci_pc_conf_gt
   (Ix1aSc_t *pSc);

/**
 * Writes the full configuration into the specified GT-64010(A) device
 * configuration registers and also read back all the values.
 *
 * @param pSc                   I/O: The softc structure for the board.
 * @param aDsc                  I: Descriptor type.
 * @param iDevice               I: The Device number, which is specified in
 *                                 galileo.h. It access the static structure
 *                                 GT_DevIoAdr.
 * @param ulParam               I: The specified Device Parameters as unsigned
 *                                 long data word (32 bit).
 * @param ulMask                I: A bit mask, which specified the bits inside
 *                                 the Device Parameter word which has to be
 *                                 altered. All the '1' bits inside the maks
 *                                 word will be modified and all the '0' bits
 *                                 in the mask word will be read and NOT
 *                                 modified write back.
 * @param ulBase                I: The base address of the specified Device as
 *                                 unsigned long address (32 bit).
 * @param ulTop                 I: The size or address range of the specified
 *                                 Device as unsigned long size (32 bit).
 *
 * @retval 0                    Operation was successful.
 * @retval Else                 Failure, result is an errno value.
 */
static int ix1appci_gt_wr_conf
   (Ix1aSc_t             *pSc,
    const Ix1aGtAccDsc_t  aDsc [],
    int                   iDevice,
    unsigned long         ulParam,
    unsigned long         ulMask,
    unsigned long         ulBase,
    unsigned long         ulTop);

/**
 * Writes a value to IoAdr, but alters only the masked bits in mask.
 *
 * @param pSc                   I/O: The softc structure for the board.
 * @param ulIoAddr              I/O: Address of a I/O mapped device.
 * @param ulParam               I: Paramter or data to write to the I/O address
 *                                 as unsigned long data word (32 bit).
 * @param ulMask                I: A bit mask, which specified the bits inside
 *                                 the Device Parameter word which. Has to be
 *                                 altered. All the '1' bits inside the maks
 *                                 word will be modified and all the '0' bits
 *                                 in the mask word will be read and NOT
 *                                 modified write back.
 *
 * @retval 0                    Operation was successful.
 * @retval Else                 Failure, result is an errno value.
 */
static int ix1appci_gt_wr_reg
   (Ix1aSc_t      *pSc,
    unsigned long  ulIoAddr,
    unsigned long  ulParam,
    unsigned long  ulMask,
    char          *pszRegName);





/* === Implementation of public functions ================================ */





/**
 *  Initialise and fill the resource info structure for a Primary PCI board.
 *
 * This function will initialise the resource info structure for Primary PCI
 * boards and acquire the needed resources.
 *
 * @param dev                   I: The device entry for the board to register.
 * @param pResInfo              I/O: The resource info structure to fill.
 *
 * @retval 0                    All resources could be acquired.
 * @retval Else                 At least one resource could not be acquired.
 *                              In fact no resource is required in this case.
 */

int ix1appci_init_resinfo
   (device_t            dev,
    Ix1aResourceInfo_t *pResInfo)
{
   unsigned long ulBase;
   unsigned long ulCount;
   int           iRes;

   pResInfo->iRidMemShm = 0x10;
   iRes = bus_get_resource (dev, SYS_RES_MEMORY, pResInfo->iRidMemShm,
                            &ulBase, &ulCount);
   if (iRes != 0)
   {
      device_printf (dev,
                     "Unable to get shared memory range (error code %d)\n",
                     iRes);
      return (ENXIO);
   }
   pResInfo->ulMemBaseShm = ulBase;
   pResInfo->ulMemSizeShm = ulCount;
   DBG (LOG_DEBUG, device_get_unit (dev),
        "Shared memory resource info: 0x%08lX / 0x%lX (expected size 0x%lX)",
        pResInfo->ulMemBaseShm, pResInfo->ulMemSizeShm,
        (unsigned long) IX1A_PPCI_SHARED_MEMORY_SIZE);
   if (ulCount > IX1A_PPCI_SHARED_MEMORY_SIZE)
   {
      pResInfo->ulMemSizeShm = IX1A_PPCI_SHARED_MEMORY_SIZE;
   }
#if 0 /* seems to generate panics */
   iRes = bus_set_resource (dev, SYS_RES_MEMORY, pResInfo->iRidMemShm,
                            pResInfo->ulMemBaseShm, pResInfo->ulMemSizeShm);
   if (iRes != 0)
   {
      device_printf (dev,
                     "Error modifying the shared memory resource address from 0x%08lX/%lu to 0x%08lX/%lu: %d\n",
                     ulBase, ulCount, pResInfo->ulMemBaseShm,
                     pResInfo->ulMemSizeShm, iRes);
      return (ENXIO);
   }
#endif /* 0 */
   
   pResInfo->iRidMemGal = 0x20;
   iRes = bus_get_resource (dev, SYS_RES_MEMORY, pResInfo->iRidMemGal,
                            &ulBase, &ulCount);
   if (iRes != 0)
   {
      device_printf (dev,
                     "Unable to get GAL memory range (error code %d)\n",
                     iRes);
      return (ENXIO);
   }
   pResInfo->ulMemBaseGal = ulBase;
   pResInfo->ulMemSizeGal = ulCount;
   DBG (LOG_DEBUG, device_get_unit (dev),
        "GAL memory resource info: 0x%08lX / 0x%lX (expected size 0x%lX)",
        pResInfo->ulMemBaseGal, pResInfo->ulMemSizeGal,
        (unsigned long) IX1A_PPCI_GAL_MEMORY_SIZE);
   if (ulCount > IX1A_PPCI_GAL_MEMORY_SIZE)
   {
      pResInfo->ulMemSizeGal = IX1A_PPCI_GAL_MEMORY_SIZE;
   }
#if 0 /* seems to generate panics */
   iRes = bus_set_resource (dev, SYS_RES_MEMORY, pResInfo->iRidMemGal,
                            pResInfo->ulMemBaseGal, pResInfo->ulMemSizeGal);
   if (iRes != 0)
   {
      device_printf (dev,
                     "Error modifying the GAL memory resource address from 0x%08lX/%lu to 0x%08lX/%lu: %d\n",
                     ulBase, ulCount,
                     pResInfo->ulMemBaseGal, pResInfo->ulMemSizeGal, iRes);
      return (ENXIO);
   }
#endif /* 0 */
   
   pResInfo->iRidMemMmr = 0x1C;
   iRes = bus_get_resource (dev, SYS_RES_MEMORY, pResInfo->iRidMemMmr,
                            &ulBase, &ulCount);
   if (iRes != 0)
   {
      device_printf (dev,
                     "Unable to get MMR memory range (error code %d)\n",
                     iRes);
      return (ENXIO);
   }
   pResInfo->ulMemBaseMmr = ulBase;
   pResInfo->ulMemSizeMmr = ulCount;
   DBG (LOG_DEBUG, device_get_unit (dev),
        "MMR memory resource info: 0x%08lX / 0x%lX (expected size 0x%lX)",
        pResInfo->ulMemBaseMmr, pResInfo->ulMemSizeMmr,
        (unsigned long) IX1A_PPCI_MMR_SIZE);
   if (ulCount > IX1A_PPCI_MMR_SIZE)
   {
      pResInfo->ulMemSizeMmr = IX1A_PPCI_MMR_SIZE;
   }
#if 0 /* seems to generate panics */
   iRes = bus_set_resource (dev, SYS_RES_MEMORY, pResInfo->iRidMemMmr,
                            pResInfo->ulMemBaseMmr, pResInfo->ulMemSizeMmr);
   if (iRes != 0)
   {
      device_printf (dev,
                     "Error modifying the MMR resource address from 0x%08lX/%lu to 0x%08lX/%lu: %d\n",
                     ulBase, ulCount,
                     pResInfo->ulMemBaseMmr, pResInfo->ulMemSizeMmr, iRes);
      return (ENXIO);
   }
#endif /* 0 */
   
   pResInfo->iRidIrq = 0;
   iRes = bus_get_resource (dev, SYS_RES_IRQ, pResInfo->iRidIrq,
                            &ulBase, &ulCount);
   if (iRes != 0)
   {
      device_printf (dev,
                     "Unable to get IRQ number (error code %d)\n",
                     iRes);
      return (ENXIO);
   }
   pResInfo->iIrq = (int) ulBase;
   
   pResInfo->pResMemShm = bus_alloc_resource
                             (dev, SYS_RES_MEMORY, &(pResInfo->iRidMemShm),
                              0UL, ~0UL, 1, RF_ACTIVE);
   if (pResInfo->pResMemShm == NULL)
   {
      device_printf (dev,
                     "Unable to allocate shared memory range at 0x%06lX\n",
                     pResInfo->ulMemBaseShm);
      (void) ix1amisc_release_resinfo (dev, pResInfo);
      return (ENXIO);
   }
   pResInfo->memTagShm    = rman_get_bustag (pResInfo->pResMemShm);
   pResInfo->memHandleShm = rman_get_bushandle (pResInfo->pResMemShm);
   
   pResInfo->pResMemGal = bus_alloc_resource
                             (dev, SYS_RES_MEMORY, &(pResInfo->iRidMemGal),
                              0UL, ~0UL, 1, RF_ACTIVE);
   if (pResInfo->pResMemGal == NULL)
   {
      device_printf (dev,
                     "Unable to allocate GAL memory range at 0x%06lX\n",
                     pResInfo->ulMemBaseGal);
      (void) ix1amisc_release_resinfo (dev, pResInfo);
      return (ENXIO);
   }
   pResInfo->memTagGal    = rman_get_bustag (pResInfo->pResMemGal);
   pResInfo->memHandleGal = rman_get_bushandle (pResInfo->pResMemGal);
   
   pResInfo->pResMemMmr = bus_alloc_resource
                             (dev, SYS_RES_MEMORY, &(pResInfo->iRidMemMmr),
                              0UL, ~0UL, 1, RF_ACTIVE);
   if (pResInfo->pResMemMmr == NULL)
   {
      device_printf (dev,
                     "Unable to allocate MMR memory range at 0x%06lX\n",
                     pResInfo->ulMemBaseMmr);
      (void) ix1amisc_release_resinfo (dev, pResInfo);
      return (ENXIO);
   }
   pResInfo->memTagMmr    = rman_get_bustag (pResInfo->pResMemMmr);
   pResInfo->memHandleMmr = rman_get_bushandle (pResInfo->pResMemMmr);
   
   pResInfo->pResIrq = bus_alloc_resource
                          (dev, SYS_RES_IRQ, &(pResInfo->iRidIrq),
                           0UL, ~0UL, 1, RF_ACTIVE | RF_SHAREABLE);
   if (pResInfo->pResIrq == NULL)
   {
      device_printf (dev, "Unable to allocate irq %d\n",
                     pResInfo->iIrq);
      (void) ix1amisc_release_resinfo (dev, pResInfo);
      return (ENXIO);
   }
   /* Note: The interrupt routine will be setup by the caller when apropriate
    */
                            
   return (0);       
} /* ix1appci_init_resinfo */





/**
 * Initialise Primary PCI board for operation.
 *
 * @param dev                   I: The device representing the board.
 * @param pSc                   I/O: The softc structure for the board.
 *
 * @retval 0                    The tests were successful, board exists and is
 *                              working.
 * @retval Else                 Error occurred, board does not exist or is not
 *                              working.
 */

int ix1appci_init_board
   (device_t  dev,
    Ix1aSc_t *pSc)
{
   bus_space_tag_t         tGal = pSc->resInfo.memTagGal;
   bus_space_handle_t      hGal = pSc->resInfo.memHandleGal;
   bus_space_tag_t         tMmr = pSc->resInfo.memTagMmr;
   bus_space_handle_t      hMmr = pSc->resInfo.memHandleMmr;
   bus_space_tag_t         tShm = pSc->resInfo.memTagShm;
   bus_space_handle_t      hShm = pSc->resInfo.memHandleShm;
   int                     iLoops;
   Ix1aPpciSaveMmrConfig_t saveMmrConfig;
   int                     fSuccess;
   u_int8_t                b1;
   u_int8_t                b2;
   unsigned                u;
   int                     iRes;
   
   DBG (LOG_DEBUG, pSc->iUnit, "Start board initialisation");
   
   /* initialise the Galileo chip */
   bus_space_write_4 (tGal, hGal, IX1A_PPCI_GAL_PCICONF, htole32 (0x00007FFF));
   
   /* loop for configuring the board's hardware */
   iLoops = 0;
   fSuccess = 0;
   while (! fSuccess && ++iLoops <= 10)
   {
      /* put the board into stop mode */
      ix1appci_acquire_mmr (pSc, &saveMmrConfig);
      
      /* set LED0 to red */
      bus_space_write_1 (tMmr, hMmr, IX1A_PPCI_MMR_LED, 0x10);
      
      /* gain write access to the MMR reset register */
      fSuccess = (ix1appci_write_rr_unlock_sequence (pSc) == 0);
      if (fSuccess)
      {
         /* set the cpu to stop mode */
         b1 = 0x41;
         bus_space_write_1 (tMmr, hMmr, IX1A_PPCI_MMR_RESET, b1);
         b2 = bus_space_read_1 (tMmr, hMmr, IX1A_PPCI_MMR_RESET);
         if ((b1 & 0xF0) == (b2 & 0xF0))
         {
            /* reset the status/control register : Bit 1 set to 0 to set the
             * relais in "loop" state
             */
            bus_space_write_1 (tMmr, hMmr, IX1A_PPCI_MMR_STATUSCONTROL, 0x01);
            b2 = bus_space_read_1 (tMmr, hMmr, IX1A_PPCI_MMR_STATUSCONTROL);
            if ((b2 & 0x01) != 0x01)
            {
               DBG (LOG_TRACE, pSc->iUnit,
                    "Unable to reset status/control register, wrote 0x01, read 0x%02X",
                    (unsigned) b2);
               fSuccess = 0;
            }
         }
         else
         {
            DBG (LOG_TRACE, pSc->iUnit,
                 "Unable to set cpu to stop mode (reset register), wrote 0x%02X, read 0x%02X",
                 (unsigned) b1, (unsigned) b2);
            fSuccess = 0;
         }
      }
      
      ix1appci_restore_mmr (pSc, &saveMmrConfig);
   }
   if (! fSuccess)
   {
      device_printf (dev,
                     "Unable to initialise board, all %d attempts failed\n",
                     iLoops);
      ix1appci_disable_board (pSc);
      return (EIO);
   }
   DBG (LOG_DEBUG, pSc->iUnit,
        "Hardware initialisation succeeded after %d attempts",
        iLoops);
   
   /* configure the Galileo chip */
   iRes = ix1appci_pc_conf_gt (pSc);
   if (iRes != 0)
   {
      device_printf (dev,
                     "Unable to initialise board, Galileo chip or DRAM configuration failed");
      ix1appci_disable_board (pSc);
      return (iRes);
   }
   
   /* set the MIPS interrupt mask */
   bus_space_write_4 (tGal, hGal, IX1A_PPCI_GAL_CPUINTMASK,
                      IX1A_PPCI_GAL_CPUINT_BIT);
   
   /* set the host interrupt mask */
   bus_space_write_4 (tGal, hGal, IX1A_PPCI_GAL_PCIINTMASK,
                      IX1A_PPCI_GAL_PCIINT_BIT);
   
   /* now check the shared memory */
   /* Note: We cannot use the regular functions for reading from and writing to
    *       the shared memory, because they always add the offset to the CAPI
    *       region of the shared memory. So we directly use the low-level bus
    *       functions.
    */
   DBG (LOG_DEBUG, pSc->iUnit, "Start checking shared memory");
   bus_space_write_2 (tShm, hShm, 0x0, IX1A_SHM_TEST_WORD_LOW);
   bus_space_write_2 (tShm, hShm,
                      pSc->resInfo.ulMemSizeShm - 2, IX1A_SHM_TEST_WORD_HIGH);
   u = (unsigned) (bus_space_read_2 (tShm, hShm, 0x0));
   if (u != IX1A_SHM_TEST_WORD_LOW)
   {
      device_printf (dev,
                     "Shared memory error, wrote 0x%04X to address 0x%04X, but read 0x%04X\n",
                     IX1A_SHM_TEST_WORD_LOW, 0x0, u);
      ix1appci_disable_board (pSc);
      return (EIO);
   }
   u = (unsigned) (bus_space_read_2
                      (tShm, hShm, pSc->resInfo.ulMemSizeShm - 2));
   if (u != IX1A_SHM_TEST_WORD_HIGH)
   {
      device_printf (dev,
                     "Shared memory error, wrote 0x%04X to address 0x%04lX, but read 0x%04X\n",
                     IX1A_SHM_TEST_WORD_HIGH,
                     pSc->resInfo.ulMemSizeShm - 2, u);
      ix1appci_disable_board (pSc);
      return (EIO);
   }
   DBG (LOG_DEBUG, pSc->iUnit, "Shared memory test successful");
   
   /* finally enable the interrupt routine to handle interrupts from the board
    */
   if (! pSc->resInfo.fIrqSetup)
   {
      iRes = bus_setup_intr (dev, pSc->resInfo.pResIrq,
                             INTR_TYPE_NET | INTR_MPSAFE,
                             (driver_intr_t *) ix1appci_interrupt_handler, pSc,
                             &(pSc->resInfo.hIrqCookie));
      if (iRes != 0)
      {
         device_printf (dev, "Error setting up interrupt handler: %d\n",
                        iRes);
         ix1appci_disable_board (pSc);
         return (iRes);
      }
      pSc->resInfo.fIrqSetup = 1;
   }

   DBG (LOG_TRACE, pSc->iUnit, "Board initialised successfully");
   return (0);
} /* ix1appci_init_board */





/**
 * Disable a Primary PCI board.
 *
 * @param pSc                   I/O: The softc structure for the board to
 *                                 disable.
 *
 * @return Nothing.
 */

void ix1appci_disable_board
   (Ix1aSc_t *pSc)
{
   bus_space_tag_t         tMmr;
   bus_space_handle_t      hMmr;
   Ix1aPpciSaveMmrConfig_t saveMmrConfig;
   int                     iLoops;
   int                     fSuccess;
   u_int8_t                b1;
   u_int8_t                b2;
   
   /* if the resource structure is not initialised, the board should already
    * or still be disabled, no action needed here
    */
   if (pSc->resInfo.pResMemShm == NULL ||
       pSc->resInfo.pResMemGal == NULL ||
       pSc->resInfo.pResMemMmr == NULL)
   {
      DBG (LOG_DEBUG, pSc->iUnit,
           "Resources not initialised, board is expected to already be disabled");
      return;
   }
   tMmr = pSc->resInfo.memTagMmr;
   hMmr = pSc->resInfo.memHandleMmr;

   DBG (LOG_DEBUG, pSc->iUnit, "Start disabling board");
   
   /* loop for disabling hardware until success */
   iLoops   = 0;
   fSuccess = 0;
   while (! fSuccess && ++iLoops <= 10)
   {
      /* put the board into stop mode */
      ix1appci_acquire_mmr (pSc, &saveMmrConfig);
      
      /* turn off the LEDs */
      bus_space_write_1 (tMmr, hMmr, IX1A_PPCI_MMR_LED, 0x0F);
      
      /* gain write access to the MMR reset register */
      fSuccess = (ix1appci_write_rr_unlock_sequence (pSc) == 0);
      if (fSuccess)
      {
         /* set the cpu to stop mode */
         b1 = 0x41;
         bus_space_write_1 (tMmr, hMmr, IX1A_PPCI_MMR_RESET, b1);
         b2 = bus_space_read_1 (tMmr, hMmr, IX1A_PPCI_MMR_RESET);
         if ((b1 & 0xF0) == (b2 & 0xF0))
         {
            /* reset the status/control register : Bit 1 set to 0 to set the
             * relais in "loop" state
             */
            bus_space_write_1 (tMmr, hMmr, IX1A_PPCI_MMR_STATUSCONTROL, 0xFD);
         }
         else
         {
            DBG (LOG_TRACE, pSc->iUnit,
                 "Unable to set cpu to stop mode (reset register), wrote 0x%02X, read 0x%02X",
                 (unsigned) b1, (unsigned) b2);
            fSuccess = 0;
         }
      }
      
      ix1appci_restore_mmr (pSc, &saveMmrConfig);
   }
   if (! fSuccess)
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Unable to disable board after %d loops",
           iLoops);
      /* there is no way to react on this situation, so no error return is
       * needed
       */
      return;
   }
   
   DBG (LOG_DEBUG, pSc->iUnit, "Board disabled, %d loop(s) needed",
        iLoops);
   
} /* ix1appci_disable_board */





/**
 * Perform a download operation for a Primary PCI board.
 *
 * @param pSc                   I/O: The softc structure for the board.
 * @param nNumDataBlocks        I: The number of data blocks for the download
 *                                 operation. If not only a board reset is
 *                                 requested, there must be three data blocks
 *                                 at paDataBlocks.
 * @param paDataBlocks          I: The array of data blocks for the download
 *                                 operation. For a pure reset operation no
 *                                 data block is specified. Else there must be
 *                                 three data blocks: Primary bootstrap,
 *                                 firmware and line configuration.
 *
 * @retval 0                    The reset or download operation was successful.
 * @retval Else                 The reset or download operation failed, result
 *                              is an errno value.
 */

int ix1appci_download_board
   (Ix1aSc_t                  *pSc,
    size_t                     nNumDataBlocks,
    const CAPICtlrDataBlock_t *paDataBlocks)
{
   const u_int8_t           *pFirmwareCode;
   size_t                    nLenFirmwareCode;
   Ix1aFirmwareParamBlock_t  paramBlock;
   int                       iRes;
   
   /* check the parameters, three data blocks are expected, but the third is
    * optional and the first must be an empty block because the Primary board
    * needs no boot code file
    */
   if (nNumDataBlocks < 2 || paDataBlocks == NULL ||
       paDataBlocks [0].nLenDataBlock != 0 ||
       paDataBlocks [0].paucDataBlock != NULL ||
       paDataBlocks [1].nLenDataBlock == 0 ||
       paDataBlocks [1].paucDataBlock == NULL)
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Invalid data block parameters for download operation");
      return (EINVAL);
   }

   /* check the firmware file (2nd block), must start with two strings,
    * followed by a parameter block of fixed size
    */
   iRes = ix1amisc_check_firmware_code (pSc,
                                        paDataBlocks [1].paucDataBlock,
                                        paDataBlocks [1].nLenDataBlock,
                                        &paramBlock,
                                        &pFirmwareCode,
                                        &nLenFirmwareCode);
   if (iRes != 0)
   {
      return (iRes);
   }

   /* if there is a third data block, it must be the board configuration and of
    * correct length
    */
   if (nNumDataBlocks >= 3 &&
       paDataBlocks [2].nLenDataBlock != 0 &&
       paDataBlocks [2].paucDataBlock != NULL)
   {
      size_t nNumLinesProvided;
      
      if (paDataBlocks [2].nLenDataBlock != sizeof (Ix1aBoardParameters_t))
      {
         DBG (LOG_ERROR, pSc->iUnit,
              "Invalid board parameters data block for download operation (length %zu bytes, %zu expected)",
              paDataBlocks [2].nLenDataBlock, sizeof (Ix1aBoardParameters_t));
         return (EINVAL);
      }
      nNumLinesProvided =
         (size_t) le32toh (((Ix1aBoardParameters_t *)
                               (paDataBlocks [2].paucDataBlock))->dwNumLines);
      if (1 != nNumLinesProvided)
      {
         DBG (LOG_ERROR, pSc->iUnit,
              "Invalid board parameters data block for download operation (number of lines is %zu, must be )",
              nNumLinesProvided);
         return (EINVAL);
      }
   }
   
   /* enable the board in case it is in disabled state */
   if (pSc->state <= IX1A_STATE_INITIALISING)
   {
      iRes = ix1appci_enable_board (pSc);
      if (iRes != 0)
      {
         return (iRes);
      }
      pSc->state = IX1A_STATE_DOWN;
      DBG (LOG_INFO, pSc->iUnit, "Board state set to DOWN");
   }
   
   /* stop the board and set it to boot mode: CPU-stop, FMIC, FALC, M32=reset,
    * set both LEDs to yellow
    */
   pSc->state = IX1A_STATE_SET_BOOT_MODE;
   DBG (LOG_INFO, pSc->iUnit, "Board state set to SET-BOOT-MODE");
   ix1appci_write_mmr_rr (pSc, 0xB1);
   
   /* prepare board for firmware download */
   ix1appci_prepare_for_firmware (pSc);
   
   /* send the firmware in relatively small blocks to the board, each block is
    * signaled through an interrupt to the board and acknowledged by an
    * interrupt from the board
    */
   iRes = ix1appci_send_firmware (pSc, pFirmwareCode, nLenFirmwareCode);
   if (iRes != 0)
   {
      return (iRes);
   }
   
   /* write the adapter parameters (3rd data block) to the shared memory */
   if (nNumDataBlocks >= 3 &&
       paDataBlocks [2].nLenDataBlock != 0 &&
       paDataBlocks [2].paucDataBlock != NULL)
   {
      iRes = ix1appci_send_port_params
                (pSc,
                 paDataBlocks [2].paucDataBlock,
                 paDataBlocks [2].nLenDataBlock);
      if (iRes != 0)
      {
         return (iRes);
      }
   }
   
   /* now start the firmware on the board */
   iRes = ix1appci_start_firmware (pSc);
   if (iRes != 0)
   {
      return (iRes);
   }
   
   /* after the firmware startup we must setup some memory locations for later
    * CAPI message exchange
    */
   iRes = ix1ashm_firmware_started_ppci (pSc);
   if (iRes != 0)
   {
      return (iRes);
   }
   
   /* now the board is ready for operation */
   pSc->state = IX1A_STATE_READY;
   DBG (LOG_INFO, pSc->iUnit, "Board ready for (CAPI) operation");
   return (0);
} /* ix1appci_download_board */





/**
 * Read a byte from a shared memory address of a Primary PCI board.
 *
 * @param pSc                   I/O: The softc structure for the board.
 * @param uAddr                 I: The shared memory address to read from.
 * @param puValue               O: The byte read from the board as an unsigned
 *                                 integer, only valid if the result is 0.
 *
 * @retval 0                    The value was read successfully and stored into
 *                              *puValue.
 * @retval Else                 An errno value on failure.
 */

int ix1appci_shm_read_byte
   (Ix1aSc_t *pSc,
    unsigned  uAddr,
    unsigned *puValue)
{
   /* add the CAPI shared memory base to the given address */
   uAddr += IX1A_SHM_OFS_CAPI_SHM_PRIM;
   
   /* now read the value at the specified offset */
   *puValue = (unsigned) (bus_space_read_1 (pSc->resInfo.memTagShm,
                                            pSc->resInfo.memHandleShm,
                                            uAddr));
   
   DBG (LOG_DEBUG, pSc->iUnit,
        "Read byte 0x%02X from shared memory address 0x%06X",
        *puValue, uAddr);
   return (0);
} /* ix1appci_shm_read_byte */





/**
 * Read a 16bit word from a shared memory address of a Primary PCI board.
 *
 * @param pSc                   I/O: The softc structure for the board.
 * @param uAddr                 I: The shared memory address to read from.
 * @param puValue               O: The word read from the board, only valid if
 *                                 the result is 0.
 *
 * @retval 0                    The value was read successfully and stored into
 *                              *puValue.
 * @retval Else                 An errno value on failure.
 */

int ix1appci_shm_read_word
   (Ix1aSc_t *pSc,
    unsigned  uAddr,
    unsigned *puValue)
{
   /* add the CAPI shared memory base to the given address */
   uAddr += IX1A_SHM_OFS_CAPI_SHM_PRIM;
   
   /* check if an even address was specified */
   if ((uAddr & 0x01) != 0 || puValue == NULL)
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Invalid shared memory address 0x%06X to read a word from",
           uAddr);
      return (EINVAL);
   }
   
   /* now read the value at the specified offset */
   *puValue = le16toh (bus_space_read_2 (pSc->resInfo.memTagShm,
                                         pSc->resInfo.memHandleShm,
                                         uAddr));
   
   DBG (LOG_DEBUG, pSc->iUnit,
        "Read word 0x%04X from shared memory address 0x%06X",
        *puValue, uAddr);
   return (0);
} /* ix1appci_shm_read_word */





/**
 * Read a block of data from a shared memory address of a Primary PCI board.
 *
 * @param pSc                   I/O: The softc structure for the board.
 * @param uAddr                 I: The shared memory address to read from.
 * @param pbData                I: The address for returing the data read.
 * @param nLenData              I: The length in bytes of the data block to
 *                                 read.
 *
 * @retval 0                    The data block was read successfully.
 * @retval Else                 Failure, result is an errno value.
 */

int ix1appci_shm_read_block
   (Ix1aSc_t *pSc,
    unsigned  uAddr,
    u_int8_t *pbData,
    size_t    nLenData)
{
   bus_space_tag_t    t = pSc->resInfo.memTagShm;
   bus_space_handle_t h = pSc->resInfo.memHandleShm;
   unsigned           uOrgAddr;
   size_t             nOrgLen;
   size_t             n;
   
   /* add the CAPI shared memory base to the given address */
   uAddr += IX1A_SHM_OFS_CAPI_SHM_PRIM;
   uOrgAddr = uAddr;
   nOrgLen  = nLenData;
   
   /* if an odd address was specified, read the first byte */
   if ((uAddr & 0x000001) != 0 && nLenData >= 1)
   {
      *pbData = bus_space_read_1 (t, h, uAddr);
      ++pbData;
      ++uAddr;
      --nLenData;
   }
   
   /* if we now have an address on an even but no on a four-byte boundary, read
    * the next word
    */
   if ((uAddr & 0x000003) != 0 && nLenData >= sizeof (u_int16_t))
   {
      *((u_int16_t *) pbData) = bus_space_read_2 (t, h, uAddr);
      pbData += sizeof (u_int16_t);
      uAddr += sizeof (u_int16_t);
      nLenData -= sizeof (u_int16_t);
   }

   /* now read the rest of the block as a bunch of four-byte values */
   if (nLenData >= sizeof (u_int32_t))
   {
      n = (nLenData >> 2);
      bus_space_read_region_4 (t, h, uAddr, (u_int32_t *) pbData, n);
      n <<= 2;
      pbData += n;
      uAddr += n;
      nLenData -= n;
   }
   
   /* if there is still a word remaining to read, read it as a word */
   if (nLenData >= sizeof (u_int16_t))
   {
      *((u_int16_t *) pbData) = bus_space_read_2 (t, h, uAddr);
      pbData += sizeof (u_int16_t);
      uAddr += sizeof (u_int16_t);
      nLenData -= sizeof (u_int16_t);
   }
   
   /* if there is still a single byte remaining to read, read it as a byte */
   if (nLenData > 0)
   {
      *pbData = bus_space_read_2 (t, h, uAddr);
      --nLenData;
   }
   if (nLenData > 0)
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Error reading 0x%04zX bytes from shared memory address 0x%06X, %zu bytes left to read (internal error)",
           nOrgLen, uOrgAddr, nLenData);
      return (EIO);
   }
   
   DBG (LOG_DEBUG, pSc->iUnit,
        "Read block of length 0x%04zX from shared memory address 0x%06X",
        nOrgLen, uOrgAddr);
   return (0);
} /* ix1appci_shm_read_block */





/**
 * Write a 16bit word to a shared memory address of a Primary PCI board.
 *
 * @param pSc                   I/O: The softc structure for the board.
 * @param uAddr                 I: The shared memory address to write to.
 * @param uValue                I: The word to write.
 *
 * @retval 0                    The value was successfully written.
 * @retval Else                 An errno value on failure.
 */

int ix1appci_shm_write_word
   (Ix1aSc_t *pSc,
    unsigned  uAddr,
    unsigned  uValue)
{
   /* add the CAPI shared memory base to the given address */
   uAddr += IX1A_SHM_OFS_CAPI_SHM_PRIM;
   
   /* check if an even address was specified */
   if ((uAddr & 0x01) != 0)
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Invalid shared memory address 0x%06X to write a word to",
           uAddr);
      return (EINVAL);
   }
   
   /* now write the value to the specified offset */
   bus_space_write_2 (pSc->resInfo.memTagShm,
                      pSc->resInfo.memHandleShm,
                      uAddr,
                      htole16 ((u_int16_t) uValue));
   
   DBG (LOG_DEBUG, pSc->iUnit,
        "Wrote word 0x%04X to shared memory address 0x%06X",
        uValue, uAddr);
   return (0);
} /* ix1appci_shm_write_word */





/**
 * Write a block of data to a shared memory address of a Primary PCI board.
 *
 * @param pSc                   I/O: The softc structure for the board.
 * @param uAddr                 I: The shared memory address to write to.
 * @param pbData                I: The address of the data block to write.
 * @param nLenData              I: The length in bytes of the data block to
 *                                 write.
 *
 * @retval 0                    The data block was written successfully.
 * @retval Else                 Failure, result is an errno value.
 */

int ix1appci_shm_write_block
   (Ix1aSc_t       *pSc,
    unsigned        uAddr,
    const u_int8_t *pbData,
    size_t          nLenData)
{
   bus_space_tag_t    t = pSc->resInfo.memTagShm;
   bus_space_handle_t h = pSc->resInfo.memHandleShm;
   unsigned           uOrgAddr;
   size_t             nOrgLen;
   size_t             n;
   
   /* add the CAPI shared memory base to the given address */
   uAddr += IX1A_SHM_OFS_CAPI_SHM_PRIM;
   uOrgAddr = uAddr;
   nOrgLen  = nLenData;
   
   /* if an odd address was specified, write the first byte */
   if ((uAddr & 0x000001) != 0 && nLenData >= 1)
   {
      bus_space_write_1 (t, h, uAddr, *pbData);
      ++pbData;
      ++uAddr;
      --nLenData;
   }
   
   /* if we now have an address on an even but no on a four-byte boundary,
    * write the next word
    */
   if ((uAddr & 0x000003) != 0 && nLenData >= sizeof (u_int16_t))
   {
      bus_space_write_2 (t, h, uAddr, *((const u_int16_t *) pbData));
      pbData += sizeof (u_int16_t);
      uAddr += sizeof (u_int16_t);
      nLenData -= sizeof (u_int16_t);
   }

   /* now write the rest of the block as a bunch of four-byte values */
   if (nLenData >= sizeof (u_int32_t))
   {
      n = nLenData >> 2;
      bus_space_write_region_4 (t, h, uAddr, (const u_int32_t *) pbData, n);
      n <<= 2;
      pbData += n;
      uAddr += n;
      nLenData -= n;
   }
   
   /* if there is still a word remaining to write, write it as a word */
   if (nLenData >= sizeof (u_int16_t))
   {
      bus_space_write_2 (t, h, uAddr, *((const u_int16_t *) pbData));
      pbData += sizeof (u_int16_t);
      uAddr += sizeof (u_int16_t);
      nLenData -= sizeof (u_int16_t);
   }
   
   /* if there is still a single byte remaining to write, write it as a byte */
   if (nLenData > 0)
   {
      bus_space_write_1 (t, h, uAddr, *pbData);
      --nLenData;
   }
   if (nLenData > 0)
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Error writing 0x%04zX bytes to shared memory address 0x%06X, %zu bytes left to write (internal error)",
           nOrgLen, uOrgAddr, nLenData);
      return (EIO);
   }
   
   DBG (LOG_DEBUG, pSc->iUnit,
        "Wrote block of length 0x%04zX to shared memory address 0x%06X",
        nOrgLen, uOrgAddr);
   return (0);
} /* ix1appci_shm_write_block */





/* === Implementation of private functions =============================== */





/**
 * Interrupt routine for Primary PCI boards.
 *
 * @param pSc                   I/O: The softc structure for the board raising
 *                                 the interrupt.
 *
 * @return Nothing.
 */

static void ix1appci_interrupt_handler
   (Ix1aSc_t *pSc)
{
   DBG (LOG_IRQ, pSc->iUnit, "Interrupt handler called");
        
   mtx_lock (&(pSc->mtxAccess));
   if (pSc->fOpInProgress != 0 && ! pSc->fWaitingForRc)
   {
      /* Note: This may happen because for each interrupt we loop to get
       *       several messages if available. But the board raises an interrupt
       *       for every message delivered. So after fetching more than one
       *       message the next interrupt may occur before a result waiting
       *       function call took its result and so this operation is still in
       *       progress, although its return code is already stored to be
       *       fetched.
       */
      DBG (LOG_DEBUG, pSc->iUnit, "Operation still in progress");
   }
   if (pSc->fIntrActive != 0)
   {
      mtx_unlock (&(pSc->mtxAccess));
      DBG (LOG_ERROR, pSc->iUnit, "Nested interrupt handler call");
      return;
   }
   pSc->fIntrActive = 1;
   
   /* check if this board really delivered the interrupt */
   if (! ix1appci_is_int_pending (pSc))
   {
      pSc->fIntrActive = 0;
      mtx_unlock (&(pSc->mtxAccess));
      DBG (LOG_IRQ, pSc->iUnit, "No interrupt pending");
      return;
   }
   
   /* acknowledge the pending interrupt to the board */
   ix1appci_int_ack (pSc);
   
   /* if there is someone waiting for the board to deliver an interrupt, tell
    * him it has happened
    */
   if (pSc->fOpInProgress && pSc->fWaitingForRc)
   {
      pSc->uRc = CAPI_OK;
      pSc->fWaitingForRc = 0;
      cv_broadcast (&(pSc->cvNotify));

      DBG (LOG_DEBUG, pSc->iUnit, "Woken up waiting thread");
      
      /* we proceed nevertheless with handling CAPI messages from the board */
   }
   
   /* if the board is in READY state or above, we now must read all available
    * CAPI message out of the shared memory
    */
   if (pSc->state >= IX1A_STATE_READY)
   {
      bus_space_tag_t    t;
      bus_space_handle_t h;
      u_int16_t          w;
      
      /* update the watchdog word */
      t = pSc->resInfo.memTagShm;
      h = pSc->resInfo.memHandleShm;
      w = bus_space_read_2 (t, h,
                            IX1A_SHM_OFS_CAPI_SHM_PRIM +
                               IX1A_SHM_OFS_CAPIPRIM_BEAT_BOARD);
      bus_space_write_2 (t, h,
                         IX1A_SHM_OFS_CAPI_SHM_PRIM +
                            IX1A_SHM_OFS_CAPIPRIM_BEAT_PC,
                         w);
      DBG (LOG_IRQ, pSc->iUnit,
           "Watchdog word updated, value 0x%04X",
           (unsigned) w);

      ix1ashm_read_capi_messages (pSc);
   }

   pSc->fIntrActive = 0;
   mtx_unlock (&(pSc->mtxAccess));
   
   DBG (LOG_IRQ, pSc->iUnit, "Left interrupt handler");
        
} /* ix1appci_interrupt_handler */





/**
 * Check if an interrupt is pending for a board.
 *
 * This function is called to check if the board specified has raised an
 * interrupt and is waiting for an acknowledge.
 *
 * @param pSc                   I/O: The board to check for a pending
 *                                 interrupt.
 *
 * @retval 0                    No interrupt is pending from the board.
 * @retval 1                    The board raised an interrupt and is waiting
 *                              for an acknowledge.
 */

static int ix1appci_is_int_pending
   (Ix1aSc_t *pSc)
{
   u_int32_t dwMask;
   u_int32_t dwInt;
   
   /* check whether any of the enabled interrupt bits caused the actual
    * interrupt
    */
   dwMask = bus_space_read_4 (pSc->resInfo.memTagGal,
                              pSc->resInfo.memHandleGal,
                              IX1A_PPCI_GAL_PCIINTMASK);
   dwInt = bus_space_read_4 (pSc->resInfo.memTagGal,
                             pSc->resInfo.memHandleGal,
                             IX1A_PPCI_GAL_INT);
   if ((dwMask & dwInt) == 0)
   {
      /* no match, this board did not generate an interrupt */
      return (0);
   }
   return (1);
} /* ix1appci_is_int_pending */





/**
 * Acknowledge a pending interrupt to a board.
 *
 * This function is called to acknowledge a pending interrupt to the board
 * specified.
 *
 * @param pSc                   I/O: The board to acknowledge the interrupt to.
 *
 * @return Nothing.
 */

static void ix1appci_int_ack
   (Ix1aSc_t *pSc)
{
   /* set the PC interrupt mask register */
   bus_space_write_4 (pSc->resInfo.memTagGal, pSc->resInfo.memHandleGal,
                      IX1A_PPCI_GAL_PCIINTMASK,
                      htole32 (IX1A_PPCI_GAL_PCIINT_BIT));

   /* reset the causing interrupt bit 21, all other bits to 1; especially PCI
    * interrupt bits 26-29 must be 1 to avoid sending an interrupt to MIPS
    */
   bus_space_write_4 (pSc->resInfo.memTagGal, pSc->resInfo.memHandleGal,
                      IX1A_PPCI_GAL_INT,
                      htole32 (~IX1A_PPCI_GAL_PCIINT_BIT));

} /* ix1appci_int_ack */





/**
 * Enable a Primary PCI board after disabling it.
 *
 * @note This routine expects the board to have been correctly initialised
 *       before disabling. So no attempt is made to determine the correctness
 *       of the board type.
 *
 * @param pSc                   I/O: The softc structure for the board to
 *                                 enable.
 *
 * @retval 0                    The board was successfully enabled.
 * @retval Else                 Enabling the board failed, the result is an
 *                              errno value.
 */

static int ix1appci_enable_board
   (Ix1aSc_t *pSc)
{
   bus_space_tag_t         tGal = pSc->resInfo.memTagGal;
   bus_space_handle_t      hGal = pSc->resInfo.memHandleGal;
   bus_space_tag_t         tMmr = pSc->resInfo.memTagMmr;
   bus_space_handle_t      hMmr = pSc->resInfo.memHandleMmr;
   int                     iLoops;
   Ix1aPpciSaveMmrConfig_t saveMmrConfig;
   int                     fSuccess;
   u_int8_t                b1;
   u_int8_t                b2;
   int                     iRes;
   
   DBG (LOG_DEBUG, pSc->iUnit, "Start enabling board");
   
   /* initialise the Galileo chip */
   bus_space_write_4 (tGal, hGal, IX1A_PPCI_GAL_PCICONF, htole32 (0x00007FFF));
   
   /* loop for configuring the board's hardware */
   iLoops = 0;
   fSuccess = 0;
   while (! fSuccess && ++iLoops <= 10)
   {
      /* put the board into stop mode */
      ix1appci_acquire_mmr (pSc, &saveMmrConfig);
      
      /* set LED0 to red */
      bus_space_write_1 (tMmr, hMmr, IX1A_PPCI_MMR_LED, 0x10);
      
      /* gain write access to the MMR reset register */
      fSuccess = (ix1appci_write_rr_unlock_sequence (pSc) == 0);
      if (fSuccess)
      {
         /* set the cpu to stop mode */
         b1 = 0x41;
         bus_space_write_1 (tMmr, hMmr, IX1A_PPCI_MMR_RESET, b1);
         b2 = bus_space_read_1 (tMmr, hMmr, IX1A_PPCI_MMR_RESET);
         if ((b1 & 0xF0) == (b2 & 0xF0))
         {
            /* reset the status/control register : Bit 1 set to 0 to set the
             * relais in "loop" state
             */
            bus_space_write_1 (tMmr, hMmr, IX1A_PPCI_MMR_STATUSCONTROL, 0x01);
            b2 = bus_space_read_1 (tMmr, hMmr, IX1A_PPCI_MMR_STATUSCONTROL);
            if ((b2 & 0x01) != 0x01)
            {
               DBG (LOG_TRACE, pSc->iUnit,
                    "Unable to reset status/control register, wrote 0x01, read 0x%02X",
                    (unsigned) b2);
               fSuccess = 0;
            }
         }
         else
         {
            DBG (LOG_TRACE, pSc->iUnit,
                 "Unable to set cpu to stop mode (reset register), wrote 0x%02X, read 0x%02X",
                 (unsigned) b1, (unsigned) b2);
            fSuccess = 0;
         }
      }
      
      ix1appci_restore_mmr (pSc, &saveMmrConfig);
   }
   if (! fSuccess)
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Unable to enable board, all %d attempts failed\n",
           iLoops);
      ix1appci_disable_board (pSc);
      return (EIO);
   }
   DBG (LOG_DEBUG, pSc->iUnit,
        "Hardware enabled succeeded after %d attempts",
        iLoops);
   
   /* configure the Galileo chip */
   iRes = ix1appci_pc_conf_gt (pSc);
   if (iRes != 0)
   {
      printf ("Unable to enable board, Galileo chip or DRAM configuration failed");
      ix1appci_disable_board (pSc);
      return (iRes);
   }
   
   /* set the MIPS interrupt mask */
   bus_space_write_4 (tGal, hGal, IX1A_PPCI_GAL_CPUINTMASK,
                      IX1A_PPCI_GAL_CPUINT_BIT);
   
   /* set the host interrupt mask */
   bus_space_write_4 (tGal, hGal, IX1A_PPCI_GAL_PCIINTMASK,
                      IX1A_PPCI_GAL_PCIINT_BIT);
   
   DBG (LOG_TRACE, pSc->iUnit, "Board enabled successfully");
   return (0);
} /* ix1appci_enable_board */





/**
 * Acquire access to the MMR resource part through the Galileo configuration.
 *
 * CS[3] must be remapped to be located in MMR physical window (host address
 * space).
 */

static void ix1appci_acquire_mmr
   (Ix1aSc_t                *pSc,
    Ix1aPpciSaveMmrConfig_t *pSaveMmrConfig)
{
   bus_space_tag_t    t = pSc->resInfo.memTagGal;
   bus_space_handle_t h = pSc->resInfo.memHandleGal;
   u_int32_t          dw;
   
   /* first save the old configuration */
   pSaveMmrConfig->dwGtCS3LowDecodeAddr =
      bus_space_read_4 (t, h, IX1A_PPCI_GAL_DEV3START);
   pSaveMmrConfig->dwGtCS3HighDecodeAddr =
      bus_space_read_4 (t, h, IX1A_PPCI_GAL_DEV3END);

   /* CS[3] low decode address, sets start of window */
   dw = pSc->resInfo.ulMemBaseMmr >> 20;
   bus_space_write_4 (t, h, IX1A_PPCI_GAL_DEV3START, htole32 (dw & 0xFF));
   
   /* CS[3] high decode address, sets end of window */
   dw = (pSc->resInfo.ulMemBaseMmr + IX1A_PPCI_MMR_SIZE) >> 20;
   bus_space_write_4 (t, h, IX1A_PPCI_GAL_DEV3END, htole32 ((dw - 1) & 0xFF));
   
} /* ix1appci_acquire_mmr */





/**
 * Restore the address decoding after accessing the MMR resource part.
 *
 * CS[3] must be remapped to be located in MIPS' address space.
 */

static void ix1appci_restore_mmr
   (Ix1aSc_t                      *pSc,
    const Ix1aPpciSaveMmrConfig_t *pSaveMmrConfig)
{
   bus_space_tag_t    t = pSc->resInfo.memTagGal;
   bus_space_handle_t h = pSc->resInfo.memHandleGal;
   
   bus_space_write_4 (t, h, IX1A_PPCI_GAL_DEV3START,
                      pSaveMmrConfig->dwGtCS3LowDecodeAddr);
   bus_space_write_4 (t, h, IX1A_PPCI_GAL_DEV3END,
                      pSaveMmrConfig->dwGtCS3HighDecodeAddr);

} /* ix1appci_restore_mmr */





/**
 * Get write access to the reset register of the MMR resource part.
 */

static int ix1appci_write_rr_unlock_sequence
   (Ix1aSc_t *pSc)
{
   static u_int8_t    abUnlockSeq [] = { 0xF0, 0x55, 0xAA, 0x02 };
   bus_space_tag_t    t = pSc->resInfo.memTagMmr;
   bus_space_handle_t h = pSc->resInfo.memHandleMmr;
   u_int8_t           bOrg;
   u_int8_t           bNew;
   int                iLoops;
   int                fSuccess;
   size_t             n;
   
   /* save the current value of the reset register */
   bOrg = bus_space_read_1 (t, h, IX1A_PPCI_MMR_RESET);
   
   iLoops   = 0;
   fSuccess = 0;
   while (! fSuccess && ++iLoops <= 10)
   {
      for (fSuccess = 1, n = 0; n < ARRAY_COUNT (abUnlockSeq); ++n)
      {
         /* write the next byte to the reset register */
         bus_space_write_1 (t, h, IX1A_PPCI_MMR_RESET, abUnlockSeq [n]);
         
         /* the reset register is write-only, so we should always get the
          * original value when reading the register after writing to it
          */
         bNew = bus_space_read_1 (t, h, IX1A_PPCI_MMR_RESET);
         if (bOrg != bNew)
         {
            DBG (LOG_ERROR, pSc->iUnit,
                 "Error reading from reset register, got value 0x%02X, expected 0x%02X (loop run %d, sequence index %zu)",
                 (unsigned) bNew, (unsigned) bOrg, iLoops, n);
            fSuccess = 0;
         }
      }
   }
   if (! fSuccess)
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Unable to obtain write access to MMR reset register, all %d loop runs failed",
           iLoops);
      return (EIO);
   }
   DBG (LOG_DEBUG, pSc->iUnit,
        "Write access to MMR reset register successfully obtained after %d loop runs",
        iLoops);
   
   return (0);
} /* ix1appci_write_rr_unlock_sequence */





/**
 * Write a specific byte to the MMR reset register.
 *
 * This function is only called while downloading the board. We set the both
 * LEDs to yellow before writing to the reset register.
 *
 * @param pSc                   I/O: The softc structure for the board.
 * @param bToWrite              I: The byte to write.
 *
 * @retval 0                    The byte was written successfully.
 * @retval Else                 The write operation failed.
 */

static int ix1appci_write_mmr_rr
   (Ix1aSc_t *pSc,
    u_int8_t  bToWrite)
{
   bus_space_tag_t         t = pSc->resInfo.memTagMmr;
   bus_space_handle_t      h = pSc->resInfo.memHandleMmr;
   Ix1aPpciSaveMmrConfig_t saveMmrConfig;
   u_int8_t                bTmp;
   int                     iLoops;
   int                     fSuccess;
   
   iLoops   = 0;
   fSuccess = 0;
   while (! fSuccess && ++iLoops <= 10)
   {
      ix1appci_acquire_mmr (pSc, &saveMmrConfig);
      
      /* set both LEDs to yellow */
      bus_space_write_1 (t, h, IX1A_PPCI_MMR_LED, 0xF0);
      
      /* gain write access to the MMR reset register */
      fSuccess = (ix1appci_write_rr_unlock_sequence (pSc) == 0);
      if (fSuccess)
      {
         /* reset cpu */
         bus_space_write_1 (t, h, IX1A_PPCI_MMR_RESET, bToWrite);
         bTmp = bus_space_read_1 (t, h, IX1A_PPCI_MMR_RESET);
         if ((bTmp & 0xF0) != (bToWrite & 0xF0))
         {
            DBG (LOG_TRACE, pSc->iUnit,
                 "Error writing to the MMR reset register, wrote 0x%02X, read 0x%02X",
                 (unsigned) bToWrite, (unsigned) bTmp);
            fSuccess = 0;
         }
      }
      
      ix1appci_restore_mmr (pSc, &saveMmrConfig);
   }
   if (! fSuccess)
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Error writing 0x%02X to the MMR reset register, %d loop runs failed",
           (unsigned) bToWrite, iLoops);
      return (EIO);
   }
   DBG (LOG_DEBUG, pSc->iUnit,
        "Wrote 0x%02X successfully to the MMR reset register",
        (unsigned) bToWrite);
   
   return (0);
} /* ix1appci_write_mmr_rr */





/**
 * Prepare the board for sending the firmware code.
 *
 * This operation sets LED1 to red and clears the first 2MB of the shared
 * memory resource. The memory will parly be overwritten with the firmware and
 * the line port parameters.
 *
 * @param pSc                   I/O: The softc structure for the board context.
 *
 * @return Nothing.
 */

static void ix1appci_prepare_for_firmware
   (Ix1aSc_t *pSc)
{
   Ix1aPpciSaveMmrConfig_t saveMmrConfig;
   
   /* set LED1 to red */
   ix1appci_acquire_mmr (pSc, &saveMmrConfig);
   bus_space_write_1 (pSc->resInfo.memTagMmr, pSc->resInfo.memHandleMmr,
                      IX1A_PPCI_MMR_LED, 0x40);
   ix1appci_restore_mmr (pSc, &saveMmrConfig);
   
   /* clear the first 2MB (0x00200000) of the shared memory resource */
   bus_space_set_region_4 (pSc->resInfo.memTagShm, pSc->resInfo.memHandleShm,
                           0x00000000, 0x00, 0x00200000 >> 2);
   
   DBG (LOG_DEBUG, pSc->iUnit, "Board prepared for firmware download");
   
} /* ix1appci_prepare_for_firmware */





/**
 * Send the firmware code to the board.
 *
 * @param pSc                   I/O: The softc structure for the board context.
 * @param pbFirmwareCode        I: The address of the firmware code to send.
 * @param nLenFirmwareCode      I: The number of firmware code bytes to send.
 *
 * @retval 0                    Sending the firmware code was successful.
 * @retval Else                 Failure, the board did not respone to the
 *                              loading operations.
 */

static int ix1appci_send_firmware
   (Ix1aSc_t       *pSc,
    const u_int8_t *pbFirmwareCode,
    size_t          nLenFirmwareCode)
{
   pSc->state = IX1A_STATE_FIRMWARE_LOADING;
   DBG (LOG_DEBUG, pSc->iUnit,
        "Set board state to FIRMWARE-LOADING, send 0x%08zX firmware bytes",
        nLenFirmwareCode);
   
   bus_space_write_region_1 (pSc->resInfo.memTagShm, pSc->resInfo.memHandleShm,
                             0x00000000, pbFirmwareCode, nLenFirmwareCode);
   
   DBG (LOG_INFO, pSc->iUnit, "Firmware written to board, 0x%08zX bytes",
        nLenFirmwareCode);
   return (0);
} /* ix1appci_send_firmware */





/**
 * Send line settings to the board.
 *
 * @param pSc                   I/O: The softc structure for the board context.
 * @param pbSettings            I: The address of the settings data to send.
 * @param nLenSettings          I: The number of settings bytes to send.
 *
 * @retval 0                    Sending the line settings was successful.
 * @retval Else                 Failure, the board did not respone to the
 *                              sending operation.
 */

static int ix1appci_send_port_params
   (Ix1aSc_t *pSc,
    u_int8_t *pbSettings,
    size_t    nLenSettings)
{
   DBG (LOG_DEBUG, pSc->iUnit,
        "Write %zu bytes for adapter settings to shared memory",
        nLenSettings);

   /* print out excerpt from the line parameters */
   if (e_iIx1aLogLevel >= LOG_TRACE)
   {
      Ix1aBoardParameters_t           *p =
         (Ix1aBoardParameters_t *) pbSettings;
      Ix1aBoardLineConfigParameters_t *pl;
      int                              i;
      
      DBG (LOG_TRACE, pSc->iUnit, "Line port parameters version: 0x%02X",
           (unsigned) le32toh (p->dwVersion));
      DBG (LOG_TRACE, pSc->iUnit, "Line port parameters size:    0x%04X",
           (unsigned) le32toh (p->dwSize));
      DBG (LOG_TRACE, pSc->iUnit, "Board settings:");
      DBG (LOG_TRACE, pSc->iUnit,
           "   MVIP type:    %lu",
           (unsigned long) le32toh (p->general.dwMvipType));
      DBG (LOG_TRACE, pSc->iUnit,
           "   Coding law:   %lu",
           (unsigned long) le32toh (p->general.dwCodingLaw));
      DBG (LOG_TRACE, pSc->iUnit,
           "   Country code: %lu",
           (unsigned long) le32toh (p->general.dwMultimodemCountryCode));
      DBG (LOG_TRACE, pSc->iUnit,
           "   Compression:  %lu",
           (unsigned long) le32toh (p->comp.dwCompMode));

      for (i = 0; (size_t) i < (size_t) le32toh (p->dwNumLines); ++i)
      {
         pl = &(p->aLineParams [i]);
         
         DBG (LOG_TRACE, pSc->iUnit, "Settings for port %d:", i);
         
         DBG (LOG_TRACE, pSc->iUnit,
              "   Line access:        %lu",
              (unsigned long) le32toh (pl [i].interface.dwLineAccess));
         DBG (LOG_TRACE, pSc->iUnit,
              "   Line type:          %lu",
              (unsigned long) le32toh (pl [i].interface.dwLineType));
         DBG (LOG_TRACE, pSc->iUnit,
              "   D-channel protocol: %lu",
              (unsigned long) le32toh (pl [i].dchan.dwDProtocol));
         DBG (LOG_TRACE, pSc->iUnit,
              "   TE or NT side:      %lu",
              (unsigned long) le32toh (pl [i].interface.dwNtTeSide));
         DBG (LOG_TRACE, pSc->iUnit,
              "   TEI type:           %lu",
              (unsigned long) le32toh (pl [i].dchan.dwTeiType));
         DBG (LOG_TRACE, pSc->iUnit,
              "   TEI value:          %lu",
              (unsigned long) le32toh (pl [i].dchan.dwTeiValue));
         DBG (LOG_TRACE, pSc->iUnit,
              "   Clock generator:    %lu",
              (unsigned long) le32toh (pl [i].interface.dwClockGenerator));
      }
   }
   
   bus_space_write_region_1
      (pSc->resInfo.memTagShm, pSc->resInfo.memHandleShm,
       IX1A_SHM_OFS_CAPI_SHM_PRIM + IX1A_SHM_OFS_ADAPTER_PARAMS,
       IX1A_SHM_ADAPTER_PARAMS_MAGIC_STRING,
       sizeof (IX1A_SHM_ADAPTER_PARAMS_MAGIC_STRING));
   bus_space_write_region_1
      (pSc->resInfo.memTagShm, pSc->resInfo.memHandleShm,
       (IX1A_SHM_OFS_CAPI_SHM_PRIM +
           IX1A_SHM_OFS_ADAPTER_PARAMS +
           sizeof (IX1A_SHM_ADAPTER_PARAMS_MAGIC_STRING)) & ~1,
       pbSettings, nLenSettings);

   DBG (LOG_DEBUG, pSc->iUnit, "Adapter settings written successfully");
   return (0);
} /* ix1appci_send_port_params */





/**
 * Start the board firmware after a successful download operation.
 *
 * @param pSc                   I/O: The softc structure for the board context.
 *
 * @retval 0                    The board is now ready for operation.
 * @retval Else                 Starting the firmware failed, the board is in
 *                              undefined state.
 */

static int ix1appci_start_firmware
   (Ix1aSc_t *pSc)
{
   bus_space_tag_t    t = pSc->resInfo.memTagShm;
   bus_space_handle_t h = pSc->resInfo.memHandleShm;
   u_int16_t          aw [4];
   int                iCount;
   u_int8_t           bFwStatus;
   
   pSc->state = IX1A_STATE_FIRMWARE_STARTING;
   DBG (LOG_DEBUG, pSc->iUnit, "Set board state to FIRMWARE-STARTING");
   
   /* clear the firmware ready flag; will be set by the board if firmware was
    * successfully started
    */
   bus_space_set_region_2 (t, h,
                           IX1A_SHM_OFS_CAPI_SHM_PRIM +
                              IX1A_SHM_OFS_CAPIPRIM_BOARD_STATE,
                           0x00, 4);

   /* start the firmware: CPU-reset, FMIC, FALC, M32=reset */
   DBG (LOG_DEBUG, pSc->iUnit,
        "Firmware ready flag cleared, start board");
   ix1appci_write_mmr_rr (pSc, 0xB3);
   
   /* check every 100ms if the board is running, total timeout is about 30s */
   DBG (LOG_DEBUG, pSc->iUnit,
        "Board start command sent, poll for firmware ready");
   iCount = 300;
   do
   {
      /* wait for 100ms */
      (void) msleep (pSc, &(pSc->mtxAccess),
                     PZERO, IX1AMISC_FW_READY_WAIT_STRING, hz / 10);
      --iCount;
      
      /* read the firmware ready flag */
      /* Note: For memory mapped i/o this cannot fail. */
      (void) ix1appci_shm_read_block (pSc, 0, (u_int8_t *) aw, sizeof (aw));
      
      /* check the just read board status value (first byte, low byte of the
       * first word in little endian byte order)
       */
      bFwStatus = le16toh (aw [0]) & 0xFF;
      if (bFwStatus == IX1A_FW_STATUS_STARTUP_IN_PROGRESS)
      {
         /* board is still starting, continue waiting */
         continue;
      }
      else if (bFwStatus == IX1A_FW_STATUS_WORKING)
      {
         /* board is running, perform last check after the loop */
         break;
      }
      else if (bFwStatus == IX1A_FW_STATUS_WARNING_CONFIGURATION)
      {
         /* board is working but rejected the line configuration, will
          * nevertheless assume working board (if last check succeeds)
          */
         printf ("%s%d: WARNING: Board did not accept line configuration\n",
                 pSc->szDriverName, pSc->iUnit);
         break;
      }
      else
      {
         /* any other value denotes firmware start failure, no need to wait
          * further
          */
         DBG (LOG_ERROR, pSc->iUnit,
              "Starting firmware failed, board delivered firmware status 0x%02X",
              (unsigned) bFwStatus);
         return (EIO);
      }
      
   } while (iCount > 0);
   
   /* check for the string "ITK" starting at the 2nd word of the block read
    * from shared memory during the loop
    */
   if (bcmp (IX1A_FW_READY_STRING, (const u_int8_t *) &(aw [2]),
             sizeof (IX1A_FW_READY_STRING) - 1) != 0)
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Starting firmware failed, board did not deliver string \"%s\", found \"%.*s\"",
           IX1A_FW_READY_STRING, (int) sizeof (IX1A_FW_READY_STRING) - 1,
           (const char *) &(aw [1]));
      return (EIO);
   }

   /* if we reach this point, the board is ready and working (maybe with
    * configuration warnings)
    */
   DBG (LOG_INFO, pSc->iUnit, "Board successfully started");
   return (0);
} /* ix1appci_start_firmware */





/**
 * Program the Galileo chip for operation.
 *
 * Magic code, taken unchanged from the ITK / Digi Linux driver. Notes from the
 * original source code:
 *
 * If a Boot-Flash will be used, the standard GT-64010(A) DRAM and FLASH
 * configuration can be used for the start up.
 *
 * If the Primary-PCI is booted without any Boot-Flash present, some devices
 * will be remapped for the CPU for proper working. These are the DRAM bank 0
 * and bank 1 and naturally the Flash EPROM address space. With all SIMM DRAM
 * modules the DRAM bank 0 must be present. But only double sided SIMM DRAMs
 * has the second bank 1. So the bank 0 will be relocated as a mirrored devices
 * just 4 MByte under the Cold Boot Reset vector of the MIPS CPU. The standard
 * startup software will so find RAM "under" the standard Cold Boot Vector of
 * 0x1FC0.0000 and the system will also work with only one memory bank present.
 * If an additional DRAM bank or double sided SIMM will be present, it can be
 * relocated from the standard Memory start-up. And if unprogrammed
 * Flash-Memory will be present, it is relocated just under the upper RAM start
 * up.
 *
 * Shared Resources:
 * We use the RAM01 and RAM23 devices as shared devices from Host and Target:
 * Always shared devices can only be decoded from the GT-64010(A) by the first
 * master decoding registers, the PCI or CPU decoding. The device or
 * subdecoding register of RAM0 and RAM2 must be initialisated to the full
 * availible address range. So here a sub decoding is not possible but also not
 * needed, because the RAM1 and RAM3 are not supported from the hardware of the
 * PCI primary.
 *
 * CPU Halt, Start or Reset:
 * To reset or halt the target MIPS CPU from a PC via the PCI bus the DEV3
 * device must be relocated to the special PCI address range.
 *
 * Device accesses from Host PC via PCI bus:
 * Before a memory mapped device of the GT-64010(A) can be used from the host
 * PC via the PCI bus, the GT-64010(A) should be reconfigurated with new Time
 * Out values.
 *
 * ConfGt:
 * Configurates the GT-64010(A) from Host-PC side, instead of the target CPU.
 * As long as no Flash-EPROM start-up is present and as long as the GT-64010(A)
 * is shown with its orginal PCI-Device-ID, use this short code to configurate
 * the GT-64010(A).
 *
 * Memory Map:
 * The structure describes, which device will be mapped to which Base address.
 * The GT-64010(A) device segments and subsegments (maps) will be checked and
 * calculated from the device descriptor table. If no device overlapping is
 * found, ...
 * 
 * @param pSc                   I/O: The softc structure for the board.
 *
 * @retval 0                    The Galileo chip was successfully configured.
 * @retval Else                 Failure, the board is not operationable.
 */

static int ix1appci_pc_conf_gt
   (Ix1aSc_t *pSc)
{
   int           iDevSeg   = IX1A_PPCI_SEGMAP_GT_MAXSEG;
   int           iDevMap   = IX1A_PPCI_DEVMAP_GT_MAXDEV;
   unsigned long ulSegBase = 0;/* Device segment Base address */
   unsigned long ulSegTop  = 0; /* Device segment Top address */
   unsigned long ulMapBase = 0;/* Device memory map Base address */
   unsigned long ulMapTop  = 0; /* Device memory map Top address */
   int           i;
   int           iRes;

   for (i = 0; i < IX1A_PPCI_DEV_MaxDev; ++i)
   {
      /* write Device Table */
      g_MipsBase [g_DscFlash [i].eDevice] =
         g_DscFlash [i].ulBase + IX1A_PPCI_KERNL1;
      
      if (g_DscFlash [i].ulSize != 0)
      {
         if (g_DscFlash [i].iDevSeg != iDevSeg)
         {
            /* we have a new segment */

            /* remember segment */
            iDevSeg = g_DscFlash [i].iDevSeg;
            /* define Base address */
            ulSegTop = ulSegBase = g_DscFlash [i].ulBase;
            /* add offset */
            ulSegTop += g_DscFlash [i].ulSize;
         }
         else
         {
            if (ulSegBase < g_DscFlash [i].ulBase)
            {
               /* adjust upper memory area */
               if (ulSegTop > g_DscFlash [i].ulBase)
               {
                  /* sorry, Memory area overlapping */
                  DBG (LOG_ERROR, pSc->iUnit,
                       "Memory segment address overlapping in line %d",
                       i + 1);
                  return (EIO);
               }
               /* define new top of Segment address */
               ulSegTop = g_DscFlash [i].ulBase + g_DscFlash [i].ulSize;
            }
            else
            {
               if (ulSegBase < (g_DscFlash [i].ulBase + g_DscFlash [i].ulSize))
               {
                  /* sorry, Memory area overlapping */
                  DBG (LOG_ERROR, pSc->iUnit,
                       "Memory segment address overlapping in line %d",
                       i + 1);
                  return (EIO);
               }
               /* define new Segment base address */
               ulSegBase = g_DscFlash [i].ulBase;
            }
         }

         if ((g_DscFlash [i].iDevSeg != IX1A_PPCI_NOSEG) &&
             (i + 1 >= IX1A_PPCI_DEV_MaxDev ||
              g_DscFlash [i].iDevSeg != g_DscFlash [i + 1].iDevSeg))
         {
            iRes = ix1appci_gt_wr_conf
                      (pSc, g_GT_CSeg, g_DscFlash [i].iDevSeg,
                       g_DscFlash [i].ulConf, g_DscFlash [i].ulMask,
                       ulSegBase, ulSegTop - 1);
            if (iRes != 0)
            {
               return (iRes);
            }
         }
      } /* of define Segments */
      
      if (g_DscFlash [i].ulSize != 0)
      {
         if (g_DscFlash [i].iDevMap != iDevMap)
         {
            /* we have a new subsegment */

            /* remember subsegment mapping */
            iDevMap = g_DscFlash [i].iDevMap;
            /* define Base address */
            ulMapTop = ulMapBase = g_DscFlash [i].ulBase;
            /* add offset */
            ulMapTop += g_DscFlash [i].ulSize;
         }
         else
         {
            if (ulMapBase < g_DscFlash [i].ulBase)
            {
               /* adjust upper memory area */
               if (ulMapTop > g_DscFlash [i].ulBase)
               {
                  /* sorry, Memory area overlapping */
                  DBG (LOG_ERROR, pSc->iUnit,
                       "Memory map address overlapping in line %d",
                       i + 1);
                  return (EIO);
               }
               /* define new top of Subsegment address */
               ulMapTop = g_DscFlash [i].ulBase + g_DscFlash [i].ulSize;
            }
            else
            {
               if (ulMapBase < (g_DscFlash [i].ulBase + g_DscFlash [i].ulSize))
               {
                  /* sorry, Memory area overlapping */
                  DBG (LOG_ERROR, pSc->iUnit,
                       "Memory map address overlapping in line %d",
                       i + 1);
                  return (EIO);
               }
               /* define new Segment base address */
               ulMapBase = g_DscFlash [i].ulBase;
            }
         }

         if (g_DscFlash [i].iDevMap != IX1A_PPCI_NOMAP &&
             (i + 1 >= IX1A_PPCI_DEV_MaxDev ||
              g_DscFlash [i].iDevMap != g_DscFlash [i + 1].iDevMap))
         {
            if (g_DscFlash [i].iAttrib == IX1A_PPCI_SEGONLY)
            {
               iRes = ix1appci_gt_wr_conf
                         (pSc, g_GT_Dev, g_DscFlash [i].iDevMap,
                          g_DscFlash [i].ulConf, g_DscFlash [i].ulMask,
                          0, IX1A_PPCI_TOP);
               if (iRes != 0)
               {
                  return (iRes);
               }
            }
            else
            {
               iRes = ix1appci_gt_wr_conf
                         (pSc, g_GT_Dev, g_DscFlash [i].iDevMap,
                          g_DscFlash [i].ulConf, g_DscFlash [i].ulMask,
                          ulMapBase, ulMapTop - 1);
               if (iRes != 0)
               {
                  return (iRes);
               }
            }
         }
      }
      else if (g_DscFlash [i].iDevMap != IX1A_PPCI_NOMAP &&
               (i + 1 >= IX1A_PPCI_DEV_MaxDev ||
                g_DscFlash [i].iDevMap != g_DscFlash [i + 1].iDevMap))
      {
         iRes = ix1appci_gt_wr_conf
                   (pSc, g_GT_Dev, g_DscFlash [i].iDevMap,
                    g_DscFlash [i].ulConf, g_DscFlash [i].ulMask, 0, 0);
         if (iRes != 0)
         {
            return (iRes);
         }
      }
   }

   return (ix1appci_gt_wr_reg
              (pSc, IX1A_PPCI_GT_RAMCONF,
               IX1A_PPCI_GT_RAMRFSH, IX1A_PPCI_GT_RAMRFSHMASK, "DRAM"));
} /* ix1appci_pc_conf_gt */





/**
 * Writes the full configuration into the specified GT-64010(A) device
 * configuration registers and also read back all the values.
 *
 * @param pSc                   I/O: The softc structure for the board.
 * @param aDsc                  I: Descriptor type.
 * @param iDevice               I: The Device number, which is specified in
 *                                 galileo.h. It access the static structure
 *                                 GT_DevIoAdr.
 * @param ulParam               I: The specified Device Parameters as unsigned
 *                                 long data word (32 bit).
 * @param ulMask                I: A bit mask, which specified the bits inside
 *                                 the Device Parameter word which has to be
 *                                 altered. All the '1' bits inside the maks
 *                                 word will be modified and all the '0' bits
 *                                 in the mask word will be read and NOT
 *                                 modified write back.
 * @param ulBase                I: The base address of the specified Device as
 *                                 unsigned long address (32 bit).
 * @param ulTop                 I: The size or address range of the specified
 *                                 Device as unsigned long size (32 bit).
 *
 * @retval 0                    Operation was successful.
 * @retval Else                 Failure, result is an errno value.
 */

static int ix1appci_gt_wr_conf
   (Ix1aSc_t             *pSc,
    const Ix1aGtAccDsc_t  aDsc [],
    int                   iDevice,
    unsigned long         ulParam,
    unsigned long         ulMask,
    unsigned long         ulBase,
    unsigned long         ulTop)
{
   bus_space_tag_t    t = pSc->resInfo.memTagGal;
   bus_space_handle_t h = pSc->resInfo.memHandleGal;
   unsigned long      ulTmp;    /* temporary data */
  
   if (ulTop == ulBase)
   {
      ulBase = 0xFFFFFFFFUL;
      ulTop  = 0x00000000UL;
   }
   
   ulTmp = ((ulBase >> aDsc [iDevice].uShift) & aDsc [iDevice].ulLowMask);
   bus_space_write_4 (t, h, aDsc [iDevice].uLow, ulTmp);
   ulTmp = ((ulTop >> aDsc [iDevice].uShift) & aDsc [iDevice].ulHighMask);
   bus_space_write_4 (t, h, aDsc [iDevice].uHigh, ulTmp);
   
   if (aDsc [iDevice].ulConf != IX1A_PPCI_GT_NOCONF)
   {
      /* load paramter */
      ulTmp = bus_space_read_4 (t, h, aDsc [iDevice].ulConf);
      ulParam = (ulTmp & ~ulMask) | (ulParam & ulMask);
      bus_space_write_4 (t, h, aDsc [iDevice].ulConf, ulParam);
   }
   
   /* read data back */
   ulTmp = bus_space_read_4 (t, h, aDsc [iDevice].uLow);
   if (ulTmp != ((ulBase >> aDsc [iDevice].uShift) & aDsc [iDevice].ulLowMask))
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Could not write \"%s\" start address, read 0x%08lX, expected 0x%08lX",
           aDsc [iDevice].pszName, ulTmp,
           (ulBase >> aDsc [iDevice].uShift) & aDsc [iDevice].ulLowMask);
      return (EIO);
   }
   
   /* read data back */
   ulTmp = bus_space_read_4 (t, h, aDsc [iDevice].uHigh);
   if (ulTmp != ((ulTop >> aDsc [iDevice].uShift) & aDsc [iDevice].ulHighMask))
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Could not write \"%s\" end address, read 0x%08lX, expected 0x%08lX",
           aDsc [iDevice].pszName, ulTmp,
           (ulTop >> aDsc [iDevice].uShift) & aDsc [iDevice].ulHighMask);
      return (EIO);
   }
   
   if (aDsc [iDevice].ulConf != IX1A_PPCI_GT_NOCONF)
   {
      /* read data back */
      ulTmp = bus_space_read_4 (t, h, aDsc [iDevice].ulConf);
      if (ulTmp != ulParam)
      {
         DBG (LOG_ERROR, pSc->iUnit,
              "Could not write \"%s\" paramters, read 0x%08lX, expected 0x%08lX",
              aDsc [iDevice].pszName, ulTmp, ulParam);
         return (EIO);
      }
   }

   return (0);
} /* ix1appci_gt_wr_conf */





/**
 * Writes a value to IoAdr, but alters only the masked bits in mask.
 *
 * @param pSc                   I/O: The softc structure for the board.
 * @param ulIoAddr              I/O: Address of a I/O mapped device.
 * @param ulParam               I: Paramter or data to write to the I/O address
 *                                 as unsigned long data word (32 bit).
 * @param ulMask                I: A bit mask, which specified the bits inside
 *                                 the Device Parameter word which. Has to be
 *                                 altered. All the '1' bits inside the maks
 *                                 word will be modified and all the '0' bits
 *                                 in the mask word will be read and NOT
 *                                 modified write back.
 *
 * @retval 0                    Operation was successful.
 * @retval Else                 Failure, result is an errno value.
 */

static int ix1appci_gt_wr_reg
   (Ix1aSc_t      *pSc,
    unsigned long  ulIoAddr,
    unsigned long  ulParam,
    unsigned long  ulMask,
    char          *pszRegName)
{
   bus_space_tag_t    t = pSc->resInfo.memTagGal;
   bus_space_handle_t h = pSc->resInfo.memHandleGal;
   unsigned long      ulTmp;    /* temporary data */

   /* load paramter */
   ulTmp = bus_space_read_4 (t, h, ulIoAddr);
   ulParam = (ulTmp & ~ulMask) | (ulParam & ulMask);
   bus_space_write_4 (t, h, ulIoAddr, ulParam);
   
   /* read data back */
   ulTmp = bus_space_read_4 (t, h, ulIoAddr);
   if ((ulTmp & ulMask) != (ulParam & ulMask))
   {
      DBG (LOG_ERROR, pSc->iUnit,
           "Could not write \"%s\" paramters, read 0x%08lX, expected 0x%08lX, masked with 0x%08lX",
           pszRegName, ulTmp, ulParam, ulMask);
      return (EIO);
   }
   
   return  (0);
} /* ix1appci_gt_wr_reg */
