root/usr/src/grub/grub-0.97/netboot/epic100.c

/* epic100.c: A SMC 83c170 EPIC/100 fast ethernet driver for Etherboot */

/* 05/06/2003   timlegge        Fixed relocation and implemented Multicast */
#define LINUX_OUT_MACROS

#include "etherboot.h"
#include "pci.h"
#include "nic.h"
#include "timer.h"
#include "epic100.h"

/* Condensed operations for readability */
#define virt_to_le32desc(addr)  cpu_to_le32(virt_to_bus(addr))
#define le32desc_to_virt(addr)  bus_to_virt(le32_to_cpu(addr))

#define TX_RING_SIZE    2       /* use at least 2 buffers for TX */
#define RX_RING_SIZE    2

#define PKT_BUF_SZ      1536    /* Size of each temporary Tx/Rx buffer.*/

/*
#define DEBUG_RX
#define DEBUG_TX
#define DEBUG_EEPROM
*/

#define EPIC_DEBUG 0    /* debug level */

/* The EPIC100 Rx and Tx buffer descriptors. */
struct epic_rx_desc {
    unsigned long status;
    unsigned long bufaddr;
    unsigned long buflength;
    unsigned long next;
};
/* description of the tx descriptors control bits commonly used */
#define TD_STDFLAGS     TD_LASTDESC

struct epic_tx_desc {
    unsigned long status;
    unsigned long bufaddr;
    unsigned long buflength;
    unsigned long  next;
};

#define delay(nanosec)   do { int _i = 3; while (--_i > 0) \
                                     { __SLOW_DOWN_IO; }} while (0)

static void     epic100_open(void);
static void     epic100_init_ring(void);
static void     epic100_disable(struct dev *dev);
static int      epic100_poll(struct nic *nic, int retrieve);
static void     epic100_transmit(struct nic *nic, const char *destaddr,
                                 unsigned int type, unsigned int len, const char *data);
#ifdef  DEBUG_EEPROM
static int      read_eeprom(int location);
#endif
static int      mii_read(int phy_id, int location);
static void     epic100_irq(struct nic *nic, irq_action_t action);

static int      ioaddr;

static int      command;
static int      intstat;
static int      intmask;
static int      genctl ;
static int      eectl  ;
static int      test   ;
static int      mmctl  ;
static int      mmdata ;
static int      lan0   ;
static int      mc0    ;
static int      rxcon  ;
static int      txcon  ;
static int      prcdar ;
static int      ptcdar ;
static int      eththr ;

static unsigned int     cur_rx, cur_tx;         /* The next free ring entry */
#ifdef  DEBUG_EEPROM
static unsigned short   eeprom[64];
#endif
static signed char      phys[4];                /* MII device addresses. */
static struct epic_rx_desc      rx_ring[RX_RING_SIZE]
        __attribute__ ((aligned(4)));
static struct epic_tx_desc      tx_ring[TX_RING_SIZE]
        __attribute__ ((aligned(4)));
static unsigned char            rx_packet[PKT_BUF_SZ * RX_RING_SIZE];
static unsigned char            tx_packet[PKT_BUF_SZ * TX_RING_SIZE];

/***********************************************************************/
/*                    Externally visible functions                     */
/***********************************************************************/


    static int
epic100_probe(struct dev *dev, struct pci_device *pci)
{
    struct nic *nic = (struct nic *)dev;
    int i;
    unsigned short* ap;
    unsigned int phy, phy_idx;

    if (pci->ioaddr == 0)
        return 0;

    /* Ideally we would detect all network cards in slot order.  That would
       be best done a central PCI probe dispatch, which wouldn't work
       well with the current structure.  So instead we detect just the
       Epic cards in slot order. */

    ioaddr = pci->ioaddr;
    nic->irqno  = 0;
    nic->ioaddr = pci->ioaddr & ~3;

    /* compute all used static epic100 registers address */
    command = ioaddr + COMMAND;         /* Control Register */
    intstat = ioaddr + INTSTAT;         /* Interrupt Status */
    intmask = ioaddr + INTMASK;         /* Interrupt Mask */
    genctl  = ioaddr + GENCTL;          /* General Control */
    eectl   = ioaddr + EECTL;           /* EEPROM Control  */
    test    = ioaddr + TEST;            /* Test register (clocks) */
    mmctl   = ioaddr + MMCTL;           /* MII Management Interface Control */
    mmdata  = ioaddr + MMDATA;          /* MII Management Interface Data */
    lan0    = ioaddr + LAN0;            /* MAC address. (0x40-0x48) */
    mc0     = ioaddr + MC0;             /* Multicast Control */
    rxcon   = ioaddr + RXCON;           /* Receive Control */
    txcon   = ioaddr + TXCON;           /* Transmit Control */
    prcdar  = ioaddr + PRCDAR;          /* PCI Receive Current Descr Address */
    ptcdar  = ioaddr + PTCDAR;          /* PCI Transmit Current Descr Address */
    eththr  = ioaddr + ETHTHR;          /* Early Transmit Threshold */

    /* Reset the chip & bring it out of low-power mode. */
    outl(GC_SOFT_RESET, genctl);

    /* Disable ALL interrupts by setting the interrupt mask. */
    outl(INTR_DISABLE, intmask);

    /*
     * set the internal clocks:
     * Application Note 7.15 says:
     *    In order to set the CLOCK TEST bit in the TEST register,
     *    perform the following:
     *
     *        Write 0x0008 to the test register at least sixteen
     *        consecutive times.
     *
     * The CLOCK TEST bit is Write-Only. Writing it several times
     * consecutively insures a successful write to the bit...
     */

    for (i = 0; i < 16; i++) {
        outl(0x00000008, test);
    }

#ifdef  DEBUG_EEPROM
{
    unsigned short sum = 0;
    unsigned short value;
    for (i = 0; i < 64; i++) {
        value = read_eeprom(i);
        eeprom[i] = value;
        sum += value;
    }
}

#if     (EPIC_DEBUG > 1)
    printf("EEPROM contents\n");
    for (i = 0; i < 64; i++) {
        printf(" %hhX%s", eeprom[i], i % 16 == 15 ? "\n" : "");
    }
#endif
#endif

    /* This could also be read from the EEPROM. */
    ap = (unsigned short*)nic->node_addr;
    for (i = 0; i < 3; i++)
        *ap++ = inw(lan0 + i*4);

    printf(" I/O %#hX %! ", ioaddr, nic->node_addr);

    /* Find the connected MII xcvrs. */
    for (phy = 0, phy_idx = 0; phy < 32 && phy_idx < sizeof(phys); phy++) {
        int mii_status = mii_read(phy, 0);

        if (mii_status != 0xffff  && mii_status != 0x0000) {
            phys[phy_idx++] = phy;
#if     (EPIC_DEBUG > 1)
            printf("MII transceiver found at address %d.\n", phy);
#endif
        }
    }
    if (phy_idx == 0) {
#if     (EPIC_DEBUG > 1)
        printf("***WARNING***: No MII transceiver found!\n");
#endif
        /* Use the known PHY address of the EPII. */
        phys[0] = 3;
    }

    epic100_open();

    dev->disable  = epic100_disable;
    nic->poll     = epic100_poll;
    nic->transmit = epic100_transmit;
    nic->irq      = epic100_irq;

    return 1;
}

static void set_rx_mode(void)
{
        unsigned char mc_filter[8];
        int i;
        memset(mc_filter, 0xff, sizeof(mc_filter));
        outl(0x0C, rxcon);
        for(i = 0; i < 4; i++)
                outw(((unsigned short *)mc_filter)[i], mc0 + i*4);
        return;
}
        
   static void
epic100_open(void)
{
    int mii_reg5;
    int full_duplex = 0;
    unsigned long tmp;

    epic100_init_ring();

    /* Pull the chip out of low-power mode, and set for PCI read multiple. */
    outl(GC_RX_FIFO_THR_64 | GC_MRC_READ_MULT | GC_ONE_COPY, genctl);

    outl(TX_FIFO_THRESH, eththr);

    tmp = TC_EARLY_TX_ENABLE | TX_SLOT_TIME;

    mii_reg5 = mii_read(phys[0], 5);
    if (mii_reg5 != 0xffff && (mii_reg5 & 0x0100)) {
        full_duplex = 1;
        printf(" full-duplex mode");
        tmp |= TC_LM_FULL_DPX;
    } else
        tmp |= TC_LM_NORMAL;

    outl(tmp, txcon);

    /* Give adress of RX and TX ring to the chip */
    outl(virt_to_le32desc(&rx_ring), prcdar);
    outl(virt_to_le32desc(&tx_ring), ptcdar);

    /* Start the chip's Rx process: receive unicast and broadcast */
    set_rx_mode();
    outl(CR_START_RX | CR_QUEUE_RX, command);

    putchar('\n');
}

/* Initialize the Rx and Tx rings. */
    static void
epic100_init_ring(void)
{
    int i;

    cur_rx = cur_tx = 0;

    for (i = 0; i < RX_RING_SIZE; i++) {
        rx_ring[i].status    = cpu_to_le32(RRING_OWN);  /* Owned by Epic chip */
        rx_ring[i].buflength = cpu_to_le32(PKT_BUF_SZ);
        rx_ring[i].bufaddr   = virt_to_bus(&rx_packet[i * PKT_BUF_SZ]);
        rx_ring[i].next      = virt_to_le32desc(&rx_ring[i + 1]) ;
    }
    /* Mark the last entry as wrapping the ring. */
    rx_ring[i-1].next = virt_to_le32desc(&rx_ring[0]);

    /*
     *The Tx buffer descriptor is filled in as needed,
     * but we do need to clear the ownership bit.
     */

    for (i = 0; i < TX_RING_SIZE; i++) {
        tx_ring[i].status  = 0x0000;                    /* Owned by CPU */
        tx_ring[i].buflength = 0x0000 | cpu_to_le32(TD_STDFLAGS << 16);
        tx_ring[i].bufaddr = virt_to_bus(&tx_packet[i * PKT_BUF_SZ]);
        tx_ring[i].next    = virt_to_le32desc(&tx_ring[i + 1]);
    }
        tx_ring[i-1].next    = virt_to_le32desc(&tx_ring[0]);
}

/* function: epic100_transmit
 * This transmits a packet.
 *
 * Arguments: char d[6]:          destination ethernet address.
 *            unsigned short t:   ethernet protocol type.
 *            unsigned short s:   size of the data-part of the packet.
 *            char *p:            the data for the packet.
 * returns:   void.
 */
    static void
epic100_transmit(struct nic *nic, const char *destaddr, unsigned int type,
                 unsigned int len, const char *data)
{
    unsigned short nstype;
    unsigned char *txp;
    int entry;

    /* Calculate the next Tx descriptor entry. */
    entry = cur_tx % TX_RING_SIZE;

    if ((tx_ring[entry].status & TRING_OWN) == TRING_OWN) {
        printf("eth_transmit: Unable to transmit. status=%hX. Resetting...\n",
               tx_ring[entry].status);

        epic100_open();
        return;
    }

    txp = tx_packet + (entry * PKT_BUF_SZ);

    memcpy(txp, destaddr, ETH_ALEN);
    memcpy(txp + ETH_ALEN, nic->node_addr, ETH_ALEN);
    nstype = htons(type);
    memcpy(txp + 12, (char*)&nstype, 2);
    memcpy(txp + ETH_HLEN, data, len);

    len += ETH_HLEN;
        len &= 0x0FFF;
        while(len < ETH_ZLEN)
                txp[len++] = '\0';
    /*
     * Caution: the write order is important here,
     * set the base address with the "ownership"
     * bits last.
     */
    
    tx_ring[entry].buflength |= cpu_to_le32(len);
    tx_ring[entry].status = cpu_to_le32(len << 16) | 
            cpu_to_le32(TRING_OWN);     /* Pass ownership to the chip. */

    cur_tx++;

    /* Trigger an immediate transmit demand. */
    outl(CR_QUEUE_TX, command); 
    
    load_timer2(10*TICKS_PER_MS);         /* timeout 10 ms for transmit */
    while ((le32_to_cpu(tx_ring[entry].status) & (TRING_OWN)) && timer2_running())
        /* Wait */;

    if ((le32_to_cpu(tx_ring[entry].status) & TRING_OWN) != 0)
        printf("Oops, transmitter timeout, status=%hX\n",
            tx_ring[entry].status);
}

/* function: epic100_poll / eth_poll
 * This receives a packet from the network.
 *
 * Arguments: none
 *
 * returns:   1 if a packet was received.
 *            0 if no pacet was received.
 * side effects:
 *            returns the packet in the array nic->packet.
 *            returns the length of the packet in nic->packetlen.
 */

    static int
epic100_poll(struct nic *nic, int retrieve)
{
    int entry;
    int retcode;
    int status;
    entry = cur_rx % RX_RING_SIZE;

    if ((rx_ring[entry].status & cpu_to_le32(RRING_OWN)) == RRING_OWN)
        return (0);

    if ( ! retrieve ) return 1;

    status = le32_to_cpu(rx_ring[entry].status);
    /* We own the next entry, it's a new packet. Send it up. */

#if     (EPIC_DEBUG > 4)
    printf("epic_poll: entry %d status %hX\n", entry, status);
#endif

    cur_rx++;
    if (status & 0x2000) {
        printf("epic_poll: Giant packet\n");
        retcode = 0;
    } else if (status & 0x0006) {
        /* Rx Frame errors are counted in hardware. */
        printf("epic_poll: Frame received with errors\n");
        retcode = 0;
    } else {
        /* Omit the four octet CRC from the length. */
        nic->packetlen = le32_to_cpu((rx_ring[entry].buflength))- 4;
        memcpy(nic->packet, &rx_packet[entry * PKT_BUF_SZ], nic->packetlen);
        retcode = 1;
    }

    /* Clear all error sources. */
    outl(status & INTR_CLEARERRS, intstat);

    /* Give the descriptor back to the chip */
    rx_ring[entry].status = RRING_OWN;

    /* Restart Receiver */
    outl(CR_START_RX | CR_QUEUE_RX, command); 

    return retcode;
}


    static void
epic100_disable(struct dev *dev __unused)
{
        /* Soft reset the chip. */
        outl(GC_SOFT_RESET, genctl);
}

static void epic100_irq(struct nic *nic __unused, irq_action_t action __unused)
{
  switch ( action ) {
  case DISABLE :
    break;
  case ENABLE :
    break;
  case FORCE :
    break;
  }
}

#ifdef  DEBUG_EEPROM
/* Serial EEPROM section. */

/*  EEPROM_Ctrl bits. */
#define EE_SHIFT_CLK    0x04    /* EEPROM shift clock. */
#define EE_CS           0x02    /* EEPROM chip select. */
#define EE_DATA_WRITE   0x08    /* EEPROM chip data in. */
#define EE_WRITE_0      0x01
#define EE_WRITE_1      0x09
#define EE_DATA_READ    0x10    /* EEPROM chip data out. */
#define EE_ENB          (0x0001 | EE_CS)

/* The EEPROM commands include the alway-set leading bit. */
#define EE_WRITE_CMD    (5 << 6)
#define EE_READ_CMD     (6 << 6)
#define EE_ERASE_CMD    (7 << 6)

#define eeprom_delay(n) delay(n)

    static int
read_eeprom(int location)
{
    int i;
    int retval = 0;
    int read_cmd = location | EE_READ_CMD;

    outl(EE_ENB & ~EE_CS, eectl);
    outl(EE_ENB, eectl);

    /* Shift the read command bits out. */
    for (i = 10; i >= 0; i--) {
        short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
        outl(EE_ENB | dataval, eectl);
        eeprom_delay(100);
        outl(EE_ENB | dataval | EE_SHIFT_CLK, eectl);
        eeprom_delay(150);
        outl(EE_ENB | dataval, eectl);  /* Finish EEPROM a clock tick. */
        eeprom_delay(250);
    }
    outl(EE_ENB, eectl);

    for (i = 16; i > 0; i--) {
        outl(EE_ENB | EE_SHIFT_CLK, eectl);
        eeprom_delay(100);
        retval = (retval << 1) | ((inl(eectl) & EE_DATA_READ) ? 1 : 0);
        outl(EE_ENB, eectl);
        eeprom_delay(100);
    }

    /* Terminate the EEPROM access. */
    outl(EE_ENB & ~EE_CS, eectl);
    return retval;
}
#endif


#define MII_READOP      1
#define MII_WRITEOP     2

    static int
mii_read(int phy_id, int location)
{
    int i;

    outl((phy_id << 9) | (location << 4) | MII_READOP, mmctl);
    /* Typical operation takes < 50 ticks. */

    for (i = 4000; i > 0; i--)
        if ((inl(mmctl) & MII_READOP) == 0)
            break;
    return inw(mmdata);
}


static struct pci_id epic100_nics[] = {
PCI_ROM(0x10b8, 0x0005, "epic100",    "SMC EtherPowerII"),              /* SMC 83c170 EPIC/100 */
PCI_ROM(0x10b8, 0x0006, "smc-83c175", "SMC EPIC/C 83c175"),
};

struct pci_driver epic100_driver = {
        .type     = NIC_DRIVER,
        .name     = "EPIC100",
        .probe    = epic100_probe,
        .ids      = epic100_nics,
        .id_count = sizeof(epic100_nics)/sizeof(epic100_nics[0]),
        .class    = 0,
};