u-boot-brain/drivers/net/smc911x.c
Mike Frysinger 03f3d8d3b3 lan91c96/smc91111/smc911x: get mac address from environment
The environment is the canonical storage location of the mac address, so
we're killing off the global data location and moving everything to
querying the env directly.

Also, do not bother checking the EEPROM if the env is setup.  This
simplifies the code greatly.

Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Signed-off-by: Wolfgang Dnek <wd@denx.de>
CC: Ben Warren <biggerbadderben@gmail.com>
CC: Rolf Offermanns <rof@sysgo.de>
CC: Erik Stahlman <erik@vt.edu>
CC: Daris A Nevil <dnevil@snmc.com>
CC: Sascha Hauer <s.hauer@pengutronix.de>
2009-03-20 22:39:11 +01:00

252 lines
5.6 KiB
C

/*
* SMSC LAN9[12]1[567] Network driver
*
* (c) 2007 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <command.h>
#include <net.h>
#include <miiphy.h>
#include "smc911x.h"
u32 pkt_data_pull(u32 addr) \
__attribute__ ((weak, alias ("smc911x_reg_read")));
void pkt_data_push(u32 addr, u32 val) \
__attribute__ ((weak, alias ("smc911x_reg_write")));
#define mdelay(n) udelay((n)*1000)
static int smx911x_handle_mac_address(bd_t *bd)
{
unsigned long addrh, addrl;
uchar m[6];
/* if the environment has a valid mac address then use it */
if (!eth_getenv_enetaddr("ethaddr", m)) {
/* if not, try to get one from the eeprom */
addrh = smc911x_get_mac_csr(ADDRH);
addrl = smc911x_get_mac_csr(ADDRL);
m[0] = (addrl ) & 0xff;
m[1] = (addrl >> 8 ) & 0xff;
m[2] = (addrl >> 16 ) & 0xff;
m[3] = (addrl >> 24 ) & 0xff;
m[4] = (addrh ) & 0xff;
m[5] = (addrh >> 8 ) & 0xff;
/* we get 0xff when there is no eeprom connected */
if ((m[0] & m[1] & m[2] & m[3] & m[4] & m[5]) == 0xff) {
printf(DRIVERNAME ": no valid mac address in environment "
"and no eeprom found\n");
return -1;
}
eth_setenv_enetaddr("ethaddr", m);
}
printf(DRIVERNAME ": MAC %pM\n", m);
return 0;
}
static int smc911x_miiphy_read(u8 phy, u8 reg, u16 *val)
{
while (smc911x_get_mac_csr(MII_ACC) & MII_ACC_MII_BUSY)
;
smc911x_set_mac_csr(MII_ACC, phy << 11 | reg << 6 | MII_ACC_MII_BUSY);
while (smc911x_get_mac_csr(MII_ACC) & MII_ACC_MII_BUSY)
;
*val = smc911x_get_mac_csr(MII_DATA);
return 0;
}
static int smc911x_miiphy_write(u8 phy, u8 reg, u16 val)
{
while (smc911x_get_mac_csr(MII_ACC) & MII_ACC_MII_BUSY)
;
smc911x_set_mac_csr(MII_DATA, val);
smc911x_set_mac_csr(MII_ACC,
phy << 11 | reg << 6 | MII_ACC_MII_BUSY | MII_ACC_MII_WRITE);
while (smc911x_get_mac_csr(MII_ACC) & MII_ACC_MII_BUSY)
;
return 0;
}
static int smc911x_phy_reset(void)
{
u32 reg;
reg = smc911x_reg_read(PMT_CTRL);
reg &= ~0xfffff030;
reg |= PMT_CTRL_PHY_RST;
smc911x_reg_write(PMT_CTRL, reg);
mdelay(100);
return 0;
}
static void smc911x_phy_configure(void)
{
int timeout;
u16 status;
smc911x_phy_reset();
smc911x_miiphy_write(1, PHY_BMCR, PHY_BMCR_RESET);
mdelay(1);
smc911x_miiphy_write(1, PHY_ANAR, 0x01e1);
smc911x_miiphy_write(1, PHY_BMCR, PHY_BMCR_AUTON | PHY_BMCR_RST_NEG);
timeout = 5000;
do {
mdelay(1);
if ((timeout--) == 0)
goto err_out;
if (smc911x_miiphy_read(1, PHY_BMSR, &status) != 0)
goto err_out;
} while (!(status & PHY_BMSR_LS));
printf(DRIVERNAME ": phy initialized\n");
return;
err_out:
printf(DRIVERNAME ": autonegotiation timed out\n");
}
static void smc911x_enable(void)
{
/* Enable TX */
smc911x_reg_write(HW_CFG, 8 << 16 | HW_CFG_SF);
smc911x_reg_write(GPT_CFG, GPT_CFG_TIMER_EN | 10000);
smc911x_reg_write(TX_CFG, TX_CFG_TX_ON);
/* no padding to start of packets */
smc911x_reg_write(RX_CFG, 0);
smc911x_set_mac_csr(MAC_CR, MAC_CR_TXEN | MAC_CR_RXEN | MAC_CR_HBDIS);
}
int eth_init(bd_t *bd)
{
printf(DRIVERNAME ": initializing\n");
if (smc911x_detect_chip())
goto err_out;
smc911x_reset();
/* Configure the PHY, initialize the link state */
smc911x_phy_configure();
if (smx911x_handle_mac_address(bd))
goto err_out;
/* Turn on Tx + Rx */
smc911x_enable();
return 0;
err_out:
return -1;
}
int eth_send(volatile void *packet, int length)
{
u32 *data = (u32*)packet;
u32 tmplen;
u32 status;
smc911x_reg_write(TX_DATA_FIFO, TX_CMD_A_INT_FIRST_SEG | TX_CMD_A_INT_LAST_SEG | length);
smc911x_reg_write(TX_DATA_FIFO, length);
tmplen = (length + 3) / 4;
while (tmplen--)
pkt_data_push(TX_DATA_FIFO, *data++);
/* wait for transmission */
while (!((smc911x_reg_read(TX_FIFO_INF) & TX_FIFO_INF_TSUSED) >> 16));
/* get status. Ignore 'no carrier' error, it has no meaning for
* full duplex operation
*/
status = smc911x_reg_read(TX_STATUS_FIFO) & (TX_STS_LOC | TX_STS_LATE_COLL |
TX_STS_MANY_COLL | TX_STS_MANY_DEFER | TX_STS_UNDERRUN);
if (!status)
return 0;
printf(DRIVERNAME ": failed to send packet: %s%s%s%s%s\n",
status & TX_STS_LOC ? "TX_STS_LOC " : "",
status & TX_STS_LATE_COLL ? "TX_STS_LATE_COLL " : "",
status & TX_STS_MANY_COLL ? "TX_STS_MANY_COLL " : "",
status & TX_STS_MANY_DEFER ? "TX_STS_MANY_DEFER " : "",
status & TX_STS_UNDERRUN ? "TX_STS_UNDERRUN" : "");
return -1;
}
void eth_halt(void)
{
smc911x_reset();
}
int eth_rx(void)
{
u32 *data = (u32 *)NetRxPackets[0];
u32 pktlen, tmplen;
u32 status;
if ((smc911x_reg_read(RX_FIFO_INF) & RX_FIFO_INF_RXSUSED) >> 16) {
status = smc911x_reg_read(RX_STATUS_FIFO);
pktlen = (status & RX_STS_PKT_LEN) >> 16;
smc911x_reg_write(RX_CFG, 0);
tmplen = (pktlen + 2+ 3) / 4;
while (tmplen--)
*data++ = pkt_data_pull(RX_DATA_FIFO);
if (status & RX_STS_ES)
printf(DRIVERNAME
": dropped bad packet. Status: 0x%08x\n",
status);
else
NetReceive(NetRxPackets[0], pktlen);
}
return 0;
}