summaryrefslogblamecommitdiffstats
path: root/main/dev/smsc911x.c
blob: 418313ac8b8d67e78202f180d078dd43d574a2ac (plain) (tree)
1
2
3
4
5
6
7
8



                                                                            
  

                                                
                





















                                                                            
                                               




                                  
                                               












                                                                            
                                               





                                  
                                               






                                                                            
                        



                                                                            
                                                      




                                                                    
                                                      













                                                                            
                                                      




                                      

                                                   

                                              
                                                      














                                                                            
                                 





                                 
                                        











                                                                            
 





























                                                                            



                                              
                          



                                              
                          



                                              
                          



                                              















                                                                   


                                  




                                               
                                                     





                                                   
                                              
















                                                     
             











































































                                                                            
                         
 


                            
 
                              
 


                        
 

                                  
 
     
 



                             
 


                                                  
 

                            
 

             















                                                                            
                                     




                                                              
                   

                 

                                               





                                
                        
                                      
                                              



                         
                                                 

                                   
                                                                 



                 
 


                          
//==========================================================================
//
// smsc911x.c
//
//
//
// Author(s):    Jay Monkman <jtm@lopingdog.com>
// Contributors:
// Date:         06-07-2007
// Description:  Driver for the SMSC 911x and 912x families of
//               ethernet controllers
//
//--------------------------------------------------------------------------

#include "config.h"
#if INCLUDE_ETHERNET
#include "cpuio.h"
#include "stddefs.h"
#include "genlib.h"
#include "ether.h"
#include "smsc911x.h"


//--------------------------------------------------------------------------
//  smsc911x_mac_read()
//
//  Reads a register mapped through the MAC_CSR register
//--------------------------------------------------------------------------
static ulong smsc911x_mac_read(int reg)
{
    while(MAC_CSR_CMD & MAC_CSR_CMD_CSR_BUSY) {
        continue;
    }

    MAC_CSR_CMD = MAC_RD_CMD(reg);

    while(MAC_CSR_CMD & MAC_CSR_CMD_CSR_BUSY) {
        continue;
    }

    return MAC_CSR_DATA;
}

//--------------------------------------------------------------------------
//  smsc911x_mac_write()
//
//  Writes a register mapped through the MAC_CSR register
//--------------------------------------------------------------------------
static void smsc911x_mac_write(int reg, ulong val)
{
    while(MAC_CSR_CMD & MAC_CSR_CMD_CSR_BUSY) {
        continue;
    }

    MAC_CSR_DATA = val;
    MAC_CSR_CMD = MAC_WR_CMD(reg);

    while(MAC_CSR_CMD & MAC_CSR_CMD_CSR_BUSY) {
        continue;
    }
}

//--------------------------------------------------------------------------
//  smsc911x_phy_read()
//
//  Reads a PHY register
//--------------------------------------------------------------------------
#if 0
static ulong smsc911x_phy_read(int reg)
{
    while(smsc911x_mac_read(MII_ACC) & MII_ACC_BUSY) {
        continue;
    }

    smsc911x_mac_write(MII_ACC, MII_ACC_ADDR(1) | MII_ACC_REG(reg));

    while(smsc911x_mac_read(MII_ACC) & MII_ACC_BUSY) {
        continue;
    }

    return smsc911x_mac_read(MII_DATA);
}
#endif

//--------------------------------------------------------------------------
//  smsc911x_phy_write()
//
//  Writes a PHY register
//--------------------------------------------------------------------------
static void smsc911x_phy_write(int reg, ushort val)
{
    while(smsc911x_mac_read(MII_ACC) & MII_ACC_BUSY) {
        continue;
    }

    smsc911x_mac_write(MII_DATA, val);

    smsc911x_mac_write(MII_ACC, (MII_ACC_ADDR(1) |
                                 MII_ACC_REG(reg) |
                                 MII_ACC_WR));

    while(smsc911x_mac_read(MII_ACC) & MII_ACC_BUSY) {
        continue;
    }
}

//--------------------------------------------------------------------------
// smsc911x_reset()
//
// This function performs a soft reset of the controller
//--------------------------------------------------------------------------
void smsc911x_reset(void)
{

    HW_CFG |= HW_CFG_SRST;

    /* Wait for it */
    while(HW_CFG & HW_CFG_SRST) {
        continue;
    }

    PMT_CTRL |= PMT_CTRL_PHY_RST;

    /* Wait for it */
    while(PMT_CTRL & PMT_CTRL_PHY_RST) {
        continue;
    }
}

//--------------------------------------------------------------------------
// smsc911x_set_mac()
//
// This function puts the mac address from BinEnetAddr into the smsc911x.
//--------------------------------------------------------------------------
static void smsc911x_set_mac(void)
{
    ulong i;

    i = ((BinEnetAddr[5] << 8) |
         (BinEnetAddr[4] << 0));
    smsc911x_mac_write(ADDRH, i);


    i = ((BinEnetAddr[3] << 24) |
         (BinEnetAddr[2] << 16) |
         (BinEnetAddr[1] <<  8) |
         (BinEnetAddr[0] <<  0));
    smsc911x_mac_write(ADDRL, i);

}

//--------------------------------------------------------------------------
// smsc911x_init()
//
//  This code initializes the ethernet controller.
//--------------------------------------------------------------------------
int smsc911x_init(void)
{
    ulong val;

    /* Set the PHY clock config */
    HW_CFG = (HW_CFG & ~HW_CFG_PHY_CLK_MASK) | HW_CFG_PHY_CLK_INT;

    /* reset the controller, PHY */
    smsc911x_reset();

    switch(ID_REV) {
    case ID_REV_CHIP_9118:
        if(EtherVerbose & SHOW_DRIVER_DEBUG) {
            printf("Found SMSC9118\n");
        }
        break;
    case ID_REV_CHIP_9211:
        if(EtherVerbose & SHOW_DRIVER_DEBUG) {
            printf("Found SMSC9211\n");
        }
        break;
    case ID_REV_CHIP_9215:
        if(EtherVerbose & SHOW_DRIVER_DEBUG) {
            printf("Found SMSC9215\n");
        }
        break;
    case ID_REV_CHIP_9218:
        if(EtherVerbose & SHOW_DRIVER_DEBUG) {
            printf("Found SMSC9218\n");
        }
        break;
    default:
        printf("Unidentified Ethernet controller: 0x%x\n", ID_REV);
        return -1;
    }

    // set the mac address
    smsc911x_set_mac();

    /* These came from SMSC's example driver */
    HW_CFG = (HW_CFG & ~HW_CFG_TX_FIF_MASK) | HW_CFG_TX_FIF_SZ(5);

    AFC_CFG = (AFC_CFG_AFC_HI(0x6e) |
               AFC_CFG_AFC_LO(0x37) |
               AFC_CFG_BACK_DUR(0x4));

    /* Configure the GPIOs as LEDs */
    GPIO_CFG = (GPIO_CFG_LED3_EN |
                GPIO_CFG_LED2_EN |
                GPIO_CFG_LED1_EN |
                GPIO_CFG_GPIOBUF(2) |
                GPIO_CFG_GPIOBUF(1) |
                GPIO_CFG_GPIOBUF(0));

    /* Configure PHY to advertise all speeds */
    smsc911x_phy_write(PHY_ANAR, (PHY_ANAR_100TX_FD |
                                  PHY_ANAR_100TX |
                                  PHY_ANAR_10T_FD |
                                  PHY_ANAR_10T |
                                  PHY_ANAR_SF));

    /* Enable auto negotiation */
    smsc911x_phy_write(PHY_BCR, (PHY_BCR_ANE |
                                 PHY_BCR_RAN));

    /* Set the controller to buffer entire packets */
    HW_CFG |= HW_CFG_SF;

    /* Configure FIFO thresholds */
    FIFO_INT = FIFO_INT_TDAL(0xff);

    /* Enable the MAC */
    val = smsc911x_mac_read(MAC_CR);
    val  |= (MAC_CR_TXEN | MAC_CR_RXEN);
    smsc911x_mac_write(MAC_CR, val);

    TX_CFG |= TX_CFG_TX_ON;

    RX_CFG = 0;

    return 0;
}

void
smsc911x_enable_multicast_reception(void)
{
    ulong val;
    val = smsc911x_mac_read(MAC_CR);
    val  |= MAC_CR_MCPAS;
    smsc911x_mac_write(MAC_CR, val);
}

void
smsc911x_disable_multicast_reception(void)
{
    ulong val;
    val = smsc911x_mac_read(MAC_CR);
    val  &= ~MAC_CR_MCPAS;
    smsc911x_mac_write(MAC_CR, val);
}

void
smsc911x_enable_promiscuous_reception(void)
{
    ulong val;
    val = smsc911x_mac_read(MAC_CR);
    val  |= MAC_CR_PRMS;
    smsc911x_mac_write(MAC_CR, val);
}

void
smsc911x_disable_promiscuous_reception(void)
{
    ulong val;
    val = smsc911x_mac_read(MAC_CR);
    val  &= ~MAC_CR_PRMS;
    smsc911x_mac_write(MAC_CR, val);
}

void
smsc911x_enable_broadcast_reception(void)
{
    ulong val;
    val = smsc911x_mac_read(MAC_CR);
    val  |= MAC_CR_BCAST;
    smsc911x_mac_write(MAC_CR, val);
}

void
smsc911x_disable_broadcast_reception(void)
{
    ulong val;
    val = smsc911x_mac_read(MAC_CR);
    val  &= ~MAC_CR_BCAST;
    smsc911x_mac_write(MAC_CR, val);
}


//--------------------------------------------------------------------------
// smsc911x_tx()
//
// This function transmits a packet
//--------------------------------------------------------------------------
int smsc911x_tx(uchar *txbuf, ulong len)
{
    int avail;
    ulong cmda;
    ulong cmdb;
    int i;
    ulong *p;
    vulong tmp __attribute__((unused));

    tmp = TX_STATUS_FIFO_PORT;

    /* Wait until space is available for the packet */
    do {
        avail = TX_FIFO_INF & TX_FIFO_TDFREE_MASK;
    } while(avail < len);

    cmda = (TX_CMD_FS |
            TX_CMD_LS |
            TX_CMD_BS(len));

    cmdb = TX_CMD_PKTLEN(len);

    TX_FIFO_PORT = cmda;
    TX_FIFO_PORT = cmdb;
    p = (ulong *)txbuf;

    for(i = 0; i < (len/4); i++) {
        TX_FIFO_PORT = p[i];

    }

    if((len & 0x3) != 0) {
        int index = len & ~3;
        int num = len & 3;
        ulong last = 0;

        for(i = 0; i < num; i++) {
            last |= (txbuf[index + i] << (i * 8));
        }

        TX_FIFO_PORT = last;
    }


    return 0;
}

//--------------------------------------------------------------------------
// smsc911x_rx()
//
// This function checks to see if the smsc911x has a receive buffer
// ready.  If so, it copies it into the buffer pointed to by pktbuf
//--------------------------------------------------------------------------
int smsc911x_rx(uchar *pktbuf)
{
    ulong status;
    int i;
    int size;
    ulong inf = RX_FIFO_INF;
    ulong *p = (ulong *)pktbuf;

    if(((inf >> 16) & 0xffff) == 0) {
        return 0;
    }

    status = RX_STATUS_FIFO_PORT;
    size = (status & RX_STATUS_PL_MASK) >> RX_STATUS_PL_SHIFT;
    if(size == 0) {
        return 0;
    }
    if((status & RX_STATUS_ES) == 0) {
        for(i = 0; i < ((size + 3) / 4); i++) {
            p[i] = RX_FIFO_PORT;
        }

        return size - 4;
    } else {
        /* Fast forward */
        if(size >= 16) {
            RX_DP_CTL = RX_DP_RX_FFWD;
            while(RX_DP_CTL & RX_DP_RX_FFWD) {
                continue;
            }
        } else {
            ulong tmp;
            for(i = 0; i < ((size + 3)/4); i++) {
                tmp = RX_FIFO_PORT;
            }
            tmp = tmp;  // eliminate 'set-but-not-unused' warning
        }
        return 0;
    }
}


#endif // INCLUDE_ETHERNET