www.pudn.com > BSP_pcPentium.rar > sysLn97xEnd.c


/* sysLn97xEnd.c - system configuration module for AMD 79C97x END driver */

/* Copyright 1984-2001 Wind River Systems, Inc. */

/*
modification history
--------------------
01d,11oct01,pai  Now using VM_STATE_MASK_FOR_ALL and VM_STATE_FOR_PCI in
                 sysMmuMapAdd call.  Updated documentation and routines
                 for new device discovery algorithm (SPR# 35716).
01c,09oct01,pai  Corrected variable usage in sysLn97xEndLoad and
                 sysLan97xPciInit.  Conditionally compile PCI_DEV_MMU_MSK
                 and PCI_DEV_ADRS_SIZE.
01b,05oct01,pai  Removed inaccurate commentary in sysLan97xPciInit().
01a,02oct01,pai  Written from sysNetif.  Implemented a new BSP driver load
                 routine that processes multiple physical devices and END
                 units.  This replaces sysLan97xInitStrCook (SPR #35716).
*/

/*
DESCRIPTION
This is the WRS-supplied configuration module for the VxWorks
ln97xEnd (lnPci) END driver.  It initializes device resources and
provides BSP-specific ln97xEnd driver routines for any Am79C970A,
Am79C971, Am79C972, and Am79C973 PCnet-PCI ethernet devices found on
the system.

The number of supported devices that can be configured for a particular
system is finite and is specified by the LN97X_MAX_DEV configuration
constant in this file.  This value, and the internal data structures
using it, can be modified in this file for specific implementations.
*/


#if defined(INCLUDE_LN_97X_END)

/* namespace collisions */

#undef CSR  /* redefined in ln97xEnd.h (temporary fix) */


/* includes */

#include "end.h"
#include "drv/end/ln97xEnd.h"


/* defines */

/* specify the maximum number of physical devices to configure */

#define LN97X_MAX_DEV           (8)

/* AMD 79C97x 10/100Base-TX PCI Ethernet Board Types */

#define AMD_PCI_VENDOR_ID       (0x1022)  /* AMD PCI vendor ID */
#define LN97X_PCI_VENDOR_ID     (0x1022)  /* AMD PCI vendor ID */
#define LN97X_PCI_DEVICE_ID     (0x2000)  /* Am79c97x device ID */

#define LN970_PCI_REV_MASK      (0x10)    /* Am79c970A PCI rev mask */
#define LN971_PCI_REV_MASK      (0x20)    /* Am79c971  PCI rev mask */
#define LN972_PCI_REV_MASK      (0x30)    /* Am79c972  PCI rev mask */
#define LN973_PCI_REV_MASK      (0x40)    /* Am79c973  PCI rev mask */

/* LN_TYPE_97x values are equivalent to (LN97x_PCI_REV_MASK >> 4) */

#define LN_TYPE_970             (1)       /* Am97c970A PCNet-PCI II */
#define LN_TYPE_971             (2)       /* Am97c971  PCNet-Fast   */
#define LN_TYPE_972             (3)       /* Am97c972  PCNet-Fast+  */
#define LN_TYPE_973             (4)       /* Am97c973  PCNet-Fast III */

/* driver configuration flags */

/* The  parameter specifies the offset from which a packet has
 * to be loaded from the beginning of a device buffer.  Normally, this
 * parameter is zero except for architectures which access long words
 * only on aligned addresses.  For these architectures, the value of
 * this  should be 2.
 */

#define LN97X_OFFS_VALUE        (0)       /* driver  value */
#define LN97X_CSR3_VALUE        (0)       /* CSR3 register value */
#define LN97X_RSVD_FLAGS        (0)       /* driver  value */


/* imports */

IMPORT STATUS    sysMmuMapAdd (void * address, UINT len,
                               UINT initialStateMask,
                               UINT initialState);

IMPORT END_OBJ * ln97xEndLoad (char *);


/* locals */

LOCAL int ln97XUnits = 0;  /* the number of physical units found */

/* This string table stores English descriptions of supported devices.
 * LN_TYPE_97x values index the table to obtain board descriptions.
 */

LOCAL const char * ln97xStrDesc [] =
    {
    "AMD 79C97x PCI Enhanced Network Driver",
    "AMD Am79C970A PCnet-PCI II Enhanced Network Driver",
    "AMD Am79C971 PCnet-FAST Enhanced Network Driver",
    "AMD Am79C972 PCnet-FAST+ Enhanced Network Driver",
    "AMD Am79C973 PCnet-FAST III Enhanced Network Driver"
    };

/*
 * This array defines the board-specific PCI resources.  There is one
 * unique END unit associated with one unique physical device recorded
 * in this table.  The END unit number is equivalent to an index into
 * this table.
 */

LOCAL PCI_BOARD_RESOURCE ln97xPciResources [LN97X_MAX_DEV] =
    {
    {NONE, NONE, NONE, AMD_PCI_VENDOR_ID, LN97X_PCI_DEVICE_ID,
     LN970_PCI_REV_MASK, LN_TYPE_970, NONE, NONE,
    {NONE, NONE, NONE, NONE, NONE, NONE}, NULL
    },

    {NONE, NONE, NONE, AMD_PCI_VENDOR_ID, LN97X_PCI_DEVICE_ID,
     LN970_PCI_REV_MASK, LN_TYPE_970, NONE, NONE,
    {NONE, NONE, NONE, NONE, NONE, NONE}, NULL
    },

    {NONE, NONE, NONE, AMD_PCI_VENDOR_ID, LN97X_PCI_DEVICE_ID,
     LN970_PCI_REV_MASK, LN_TYPE_970, NONE, NONE,
    {NONE, NONE, NONE, NONE, NONE, NONE}, NULL
    },

    {NONE, NONE, NONE, AMD_PCI_VENDOR_ID, LN97X_PCI_DEVICE_ID,
     LN970_PCI_REV_MASK, LN_TYPE_970, NONE, NONE,
    {NONE, NONE, NONE, NONE, NONE, NONE}, NULL
    },

    {NONE, NONE, NONE, AMD_PCI_VENDOR_ID, LN97X_PCI_DEVICE_ID,
     LN970_PCI_REV_MASK, LN_TYPE_970, NONE, NONE,
    {NONE, NONE, NONE, NONE, NONE, NONE}, NULL
    },

    {NONE, NONE, NONE, AMD_PCI_VENDOR_ID, LN97X_PCI_DEVICE_ID,
     LN970_PCI_REV_MASK, LN_TYPE_970, NONE, NONE,
    {NONE, NONE, NONE, NONE, NONE, NONE}, NULL
    },

    {NONE, NONE, NONE, AMD_PCI_VENDOR_ID, LN97X_PCI_DEVICE_ID,
     LN970_PCI_REV_MASK, LN_TYPE_970, NONE, NONE,
    {NONE, NONE, NONE, NONE, NONE, NONE}, NULL
    },

    {NONE, NONE, NONE, AMD_PCI_VENDOR_ID, LN97X_PCI_DEVICE_ID,
     LN970_PCI_REV_MASK, LN_TYPE_970, NONE, NONE,
    {NONE, NONE, NONE, NONE, NONE, NONE}, NULL
    }
    };


/* forward declarations */

LOCAL UINT32 sysLn97xDev2Type (UINT32, UINT32, UINT8);



/******************************************************************************
*
* sysLn97xEndLoad - construct a load string and load an Am79C97x device
*
* This routine will be invoked by the MUX for the purpose of loading
* an Am79C97x (lnPci) device with initial parameters.  This routine is
* constructed as an interface wrapper for the driver load routine.
* Thus, the arguments and return values are consistent with any
* xxxEndLoad() routine defined for an END driver and the MUX API.
*
* INTERNAL
* The muxDevLoad() operation calls this routine twice.  A zero length
*  parameter string indicates that this is the first time
* through this routine.  The driver load routine should return the
* driver name in .
*
* On the second pass though this routine, the initialization parameter
* string is constructed.  Note that on the second pass, the 
* consists of a colon-delimeted END device unit number and rudimentary
* initialization string (often empty) constructed from entries in the
* BSP END Device Table such that:
*
*      = ":"
*
* In the process of building the rest of , the prepended unit
* number must be preserved and passed to the driver load routine.  The
*  portion mentioned above is discarded,
* but future versions of this routine may use it.
*
* The complete ln97xEnd driver load string has format:
*
*     :::::
*     :::::
*
* RETURNS: An END object pointer, or NULL on error, or 0 and the name of the
* device if the  was NULL.
*
* SEE ALSO: ln97xEndLoad()
*/
END_OBJ * sysLn97xEndLoad
    (
    char *      pParamStr,   /* pointer to initialization parameter string */
    void *      unused       /* unused optional argument */
    )
    {
    END_OBJ *   pEnd;
    char        paramStr [END_INIT_STR_MAX];

    static const char * const paramTemplate =
        "%d:0x%x:0x%x:0x%x:%d:%d:-1:-1:-1:0x%x:%d:0x%x:%p";


    /* alias local PCI and board resource table addresses */

    PCI_BOARD_RESOURCE * const pciRsrc = ln97xPciResources;


    if (strlen (pParamStr) == 0)
        {
        /* PASS (1)
         * The driver load routine returns the driver name in .
         */

        pEnd = ln97xEndLoad (pParamStr);
        }
    else
        {
        /* PASS (2)
         * The END  number is prepended to .  Construct
         * the rest of the driver load string based on physical devices
         * discovered in sysLan97xPciInit().  When this routine is called
         * to process a particular END  number, use the END 
         * as an index into the PCI "resources" table to build the driver
         * parameter string.
         */

        int    typeIdx;   /* index to the string resource table */

        char * holder  = NULL;
        int    endUnit = atoi (strtok_r (pParamStr, ":", &holder));


        /* is there a PCI resource associated with this END unit ? */

        if (endUnit >= ln97XUnits)
            {
            /* This is an error - no physical devs available to this unit */

            return NULL;
            }


        /* construct an index into the string resource table */

        typeIdx = (pciRsrc[endUnit].boardType);


        /* construct the initialization parameter string */

        sprintf (paramStr, paramTemplate,
                 endUnit,                     /* END unit number */
                 /* pciRsrc[endUnit].bar[1], wzh */    /* memory-mapped IO base */
                 NONE,
                 pciRsrc[endUnit].bar[0],     /* IO address space base */
                 PCI2DRAM_BASE_ADRS,          /* host PCI mem. base */
                 pciRsrc[endUnit].irqvec,     /* IRQ vector */
                 pciRsrc[endUnit].irq,        /* IRQ number */
                 LN97X_CSR3_VALUE,            /* csr3 register value */
                 LN97X_OFFS_VALUE,            /* offset */
                 LN97X_RSVD_FLAGS,            /* flags (reserved) */
                 &ln97xStrDesc[typeIdx]       /* device description */
                );

        if ((pEnd = ln97xEndLoad (paramStr)) == (END_OBJ *) NULL)
            {
            printf ("Error sysLn97xEndLoad: device failed.\n");
            }
        }

    return (pEnd);
    }

/*******************************************************************************
*
* sysLn97xDev2Type - convert a PCI Revision ID to a board type
*
* Given , , and  values read from PCI
* configuration space, this routine will attempt to map the ID to a board
* type value defined in this file.
*
* INTERNAL
* The PCI Vendor and Device IDs are the same for all driver supported
* Am79c97x devices.  When an instance is located, the PCI Revision ID is
* used to determine whether or not the driver can support the device.
*
* The shift and mask operations used to implement this routine are based
* upon the following table which documents the PCI Revison ID register
* values one can expect for the devices we are supporting:
*
* ---------------------------------------------------------------------------
*      PCI Configuration Space Offset: 0x08 PCI Revision ID Register
*
* 31               16  15                0
* xxxx xxxx xxxx xxxx  xxxx xxxx RRRR RRRR
* xxxx xxxx xxxx xxxx  xxxx xxxx 0000 xxxx 0x  Am79C970  PCnet-PCI
* xxxx xxxx xxxx xxxx  xxxx xxxx 0001 xxxx 1x  Am79C970A PCnet-PCI II
* xxxx xxxx xxxx xxxx  xxxx xxxx 0010 xxxx 2x  Am79C971  PCnet-FAST
* xxxx xxxx xxxx xxxx  xxxx xxxx 0011 xxxx 3x  Am79C972  PCnet-FAST+
* xxxx xxxx xxxx xxxx  xxxx xxxx 0100 xxxx 4x  Am79C973  PCnet-FAST III
* xxxx xxxx xxxx xxxx  xxxx xxxx 0100 xxxx 4x  Am79C975  PCnet-FAST III
* xxxx xxxx xxxx xxxx  xxxx xxxx 0101 xxxx 5x  Am79C978  PCnet-Home
* ---------------------------------------------------------------------------
*
* While the driver documentation is not specific on the matter, the
* revision ID is required because the driver supports Am79C970A
* (PCnet-PCI II), but not Am79C970 (PCnet-PCI).  Moreover, AMD's
* documentation indicates that Am79C973 devices and Am79C975 devices
* will be identified as the same revision.
*
* RETURNS:
* A board type value which will be one of
*
* .IP
* LN_TYPE_970
* .IP
* LN_TYPE_971
* .IP
* LN_TYPE_972
* .IP
* LN_TYPE_973
* .LP
*
* BOARD_TYPE_UNKNOWN will be returned if the Revision ID does not map to
* a supported board type.
*
* NOMANUAL
*/
LOCAL UINT32 sysLn97xDev2Type
    (
    UINT32 vendorId,    /* specifies a PCI vendor ID value */
    UINT32 deviceId,    /* specifies a PCI device ID value */
    UINT8  revisionId   /* specifies a PCI revision ID value */
    )
    {
    if ((vendorId == AMD_PCI_VENDOR_ID) &&
        (deviceId == LN97X_PCI_DEVICE_ID))
        {
        /* An AMD Am79C97x board was found.  Check the
         * Revision ID and make sure we support it.
         */

        if ((revisionId & LN970_PCI_REV_MASK) ||
            (revisionId & LN971_PCI_REV_MASK) ||
            (revisionId & LN972_PCI_REV_MASK) ||
            (revisionId & LN973_PCI_REV_MASK))
            {
            /* assume the shifted Revision ID is one of LN_TYPE_97x */

            return (UINT32)(revisionId >> 4);
            }
        }

    return (BOARD_TYPE_UNKNOWN);
    }

/*******************************************************************************
*
* sysLan97xPciInit - initialize a Am79C97x PCI ethernet device
*
*
* This routine performs basic PCI initialization for Am79C97x ethernet
* devices supported by the ln97xEnd END driver.  If supported,  the device
* memory and I/O addresses are mapped into the local CPU address space and
* an internal board-specific PCI resources table is updated with
* information on the board type, memory address, and IO address.
*
* CAVEATS
* This routine must be performed prior to MMU initialization, usrMmuInit().
* If the number of supported Am79c97x physical device instances installed
* on the PCI bus exceeds LN97X_MAX_DEV, then the extra devices will not be
* initialized in this routine.
*
* RETURNS:
* OK, else ERROR when the specified device is not supported, or if
* the device could not be mapped into the local CPU memory space.
*/
STATUS sysLan97xPciInit
    (
    UINT32  pciBus,           /* store a PCI bus number */
    UINT32  pciDevice,        /* store a PCI device number */
    UINT32  pciFunc,          /* store a PCI function number */
    UINT32  vendorId,         /* store a PCI vendor ID */
    UINT32  deviceId,         /* store a PCI device ID */
    UINT8   revisionId        /* store a PCI revision ID */
    )
    {
    UINT32  boardType;        /* store a BSP-specific board type constant */

    UINT32  ioBase;           /* IO base address (BAR 0) */
    UINT32  memIo32;          /* memory-mapped IO address (BAR 1) */
    UINT8   irq;              /* interrupt line number (IRQ) for device */


    /* number of physical units exceeded the number supported ? */

    if (ln97XUnits >= LN97X_MAX_DEV)
        {
        return (ERROR);
        }

    if ((boardType = sysLn97xDev2Type (vendorId, deviceId, revisionId))
        == BOARD_TYPE_UNKNOWN)
        {
        return (ERROR);
        }


    pciConfigInLong  (pciBus, pciDevice, pciFunc,
                      PCI_CFG_BASE_ADDRESS_0, &ioBase);
    pciConfigInLong  (pciBus, pciDevice, pciFunc,
                      PCI_CFG_BASE_ADDRESS_1, &memIo32);

    memIo32 &= PCI_MEMBASE_MASK;
    ioBase  &= PCI_IOBASE_MASK;

    /* map a 4Kb 32-bit non-prefetchable memory address decoder */

    if (sysMmuMapAdd ((void *)(memIo32 & PCI_DEV_MMU_MSK),
        PCI_DEV_ADRS_SIZE, VM_STATE_MASK_FOR_ALL, VM_STATE_FOR_PCI) == ERROR)
        {
        return (ERROR);
        }


    /* read the IRQ number and vector and save to the resource table */

    pciConfigInByte (pciBus, pciDevice, pciFunc,
                     PCI_CFG_DEV_INT_LINE, &irq);


    /* update the board-specific resource table */

    ln97xPciResources[ln97XUnits].bar[0]     = ioBase;
    ln97xPciResources[ln97XUnits].bar[1]     = memIo32;
    ln97xPciResources[ln97XUnits].irq        = irq;
    ln97xPciResources[ln97XUnits].irqvec     = INT_NUM_GET (irq);

    ln97xPciResources[ln97XUnits].vendorID   = vendorId;
    ln97xPciResources[ln97XUnits].deviceID   = deviceId;
    ln97xPciResources[ln97XUnits].revisionID = revisionId;
    ln97xPciResources[ln97XUnits].boardType  = boardType;

    /* enable mapped memory and IO decoders */

    pciConfigOutWord (pciBus, pciDevice, pciFunc, PCI_CFG_COMMAND,
                      PCI_CMD_MEM_ENABLE | PCI_CMD_IO_ENABLE |
                      PCI_CMD_MASTER_ENABLE);

    /* disable sleep mode */

    pciConfigOutByte (pciBus, pciDevice, pciFunc, PCI_CFG_MODE,
                      SLEEP_MODE_DIS);


    ++ln97XUnits;  /* increment number of units initialized */

    return (OK);
    }

/*******************************************************************************
*
* sysLan97xIntEnable - enable Am79C97x ethernet device interrupts
*
* This routine enables Am79C97x interrupts.  This may involve operations on
* interrupt control hardware.
*
* RETURNS: OK or ERROR for invalid arguments.
*/
STATUS sysLan97xIntEnable
    (
    int level           /* level number */
    )
    {
    return (sysIntEnablePIC (level));
    }

/*******************************************************************************
*
* sysLan97xIntDisable - disable Am79C97x ethernet device interrupts
*
* This routine disables Am79C97x interrupts.  This may involve operations on
* interrupt control hardware.
*
* RETURNS: OK or ERROR for invalid arguments.
*/
STATUS sysLan97xIntDisable
    (
    int level           /* level number */
    )
    {
    return (sysIntDisablePIC (level));
    }

/*******************************************************************************
*
* sysLan97xEnetAddrGet - get Am79C97x Ethernet (IEEE station) address
*
* This routine provides a target-specific interface for accessing an
* Am79C97x device Ethernet (IEEE station address) address in the device's
* Address PROM (APROM).  A handle to the specific device control structure
* is specified in the  parameter.  The 6-byte IEEE station
* address will be copied to the memory location specified by the
*  parameter.
*
* INTERNAL
* The 6 bytes of the IEEE station address occupy the first 6 locations of
* the Address PROM space.  The driver must copy the station address from
* the Address PROM space to the initialization block or to CSR12-14 in order
* for the receiver to accept unicast frames directed to the device.
*
* Bytes 14 and 15 of APROM should each be ASCII 'W' (57h).  The above
* requirements must be met in order to be compatible with AMD driver
* software.
*
* The APROM is 32 bytes, and there is no need to read all of that here.
* So, this routine reads half of the APROM to get the Ethernet address
* and test for ASCII 'W's.
*
* RETURNS: OK, or ERROR if could not be obtained.
*/
STATUS sysLan97xEnetAddrGet
    (
    LN_97X_DRV_CTRL *  pDrvCtrl,   /* Driver control */
    char *             enetAdrs
    )
    {
    char          aprom [LN_97X_APROM_SIZE];  /* copy of address PROM space */

    int           numBytes = (LN_97X_APROM_SIZE >> 1);
    register int  ix;


    /* get IO address of unit */

    UINT8 * const ioaddr = (UINT8 * const)(pDrvCtrl->devAdrs);


    /* load APROM into an array */

    if (pDrvCtrl->flags & LS_MODE_MEM_IO_MAP)
        {
        for (ix = 0; ix < numBytes; ++ix)
            {
            aprom[ix] = *(ioaddr + ix);
            }
        }
    else
        {
        for (ix = 0; ix < numBytes; ++ix)
            {
            aprom[ix] = sysInByte ((int)(ioaddr + ix));
            }
        }


    /* check for ASCII 'W's at APROM bytes 14 and 15 */

    if ((aprom [0xe] != 'W') || (aprom [0xf] != 'W'))
        {
        logMsg ("sysLn97xEnetAddrGet:  W's not stored in aprom\n",
                0, 1, 2, 3, 4, 5);

        return ERROR;
        }

    bcopy (aprom, enetAdrs, 6);

    return (OK);
    }

#endif /* INCLUDE_LN_97X_END */