| /* Gaisler.com GRETH 10/100/1000 Ethernet MAC driver |
| * |
| * Driver use polling mode (no Interrupt) |
| * |
| * (C) Copyright 2007 |
| * Daniel Hellstrom, Gaisler Research, daniel@gaisler.com |
| * |
| * 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 <malloc.h> |
| #include <asm/processor.h> |
| #include <ambapp.h> |
| #include <asm/leon.h> |
| |
| /* #define DEBUG */ |
| |
| #include "greth.h" |
| |
| /* Default to 3s timeout on autonegotiation */ |
| #ifndef GRETH_PHY_TIMEOUT_MS |
| #define GRETH_PHY_TIMEOUT_MS 3000 |
| #endif |
| |
| /* ByPass Cache when reading regs */ |
| #define GRETH_REGLOAD(addr) SPARC_NOCACHE_READ(addr) |
| /* Write-through cache ==> no bypassing needed on writes */ |
| #define GRETH_REGSAVE(addr,data) (*(unsigned int *)(addr) = (data)) |
| #define GRETH_REGORIN(addr,data) GRETH_REGSAVE(addr,GRETH_REGLOAD(addr)|data) |
| #define GRETH_REGANDIN(addr,data) GRETH_REGSAVE(addr,GRETH_REGLOAD(addr)&data) |
| |
| #define GRETH_RXBD_CNT 4 |
| #define GRETH_TXBD_CNT 1 |
| |
| #define GRETH_RXBUF_SIZE 1540 |
| #define GRETH_BUF_ALIGN 4 |
| #define GRETH_RXBUF_EFF_SIZE \ |
| ( (GRETH_RXBUF_SIZE&~(GRETH_BUF_ALIGN-1))+GRETH_BUF_ALIGN ) |
| |
| typedef struct { |
| greth_regs *regs; |
| int irq; |
| struct eth_device *dev; |
| |
| /* Hardware info */ |
| unsigned char phyaddr; |
| int gbit_mac; |
| |
| /* Current operating Mode */ |
| int gb; /* GigaBit */ |
| int fd; /* Full Duplex */ |
| int sp; /* 10/100Mbps speed (1=100,0=10) */ |
| int auto_neg; /* Auto negotiate done */ |
| |
| unsigned char hwaddr[6]; /* MAC Address */ |
| |
| /* Descriptors */ |
| greth_bd *rxbd_base, *rxbd_max; |
| greth_bd *txbd_base, *txbd_max; |
| |
| greth_bd *rxbd_curr; |
| |
| /* rx buffers in rx descriptors */ |
| void *rxbuf_base; /* (GRETH_RXBUF_SIZE+ALIGNBYTES) * GRETH_RXBD_CNT */ |
| |
| /* unused for gbit_mac, temp buffer for sending packets with unligned |
| * start. |
| * Pointer to packet allocated with malloc. |
| */ |
| void *txbuf; |
| |
| struct { |
| /* rx status */ |
| unsigned int rx_packets, |
| rx_crc_errors, rx_frame_errors, rx_length_errors, rx_errors; |
| |
| /* tx stats */ |
| unsigned int tx_packets, |
| tx_latecol_errors, |
| tx_underrun_errors, tx_limit_errors, tx_errors; |
| } stats; |
| } greth_priv; |
| |
| /* Read MII register 'addr' from core 'regs' */ |
| static int read_mii(int addr, volatile greth_regs * regs) |
| { |
| while (GRETH_REGLOAD(®s->mdio) & GRETH_MII_BUSY) { |
| } |
| |
| GRETH_REGSAVE(®s->mdio, (0 << 11) | ((addr & 0x1F) << 6) | 2); |
| |
| while (GRETH_REGLOAD(®s->mdio) & GRETH_MII_BUSY) { |
| } |
| |
| if (!(GRETH_REGLOAD(®s->mdio) & GRETH_MII_NVALID)) { |
| return (GRETH_REGLOAD(®s->mdio) >> 16) & 0xFFFF; |
| } else { |
| return -1; |
| } |
| } |
| |
| static void write_mii(int addr, int data, volatile greth_regs * regs) |
| { |
| while (GRETH_REGLOAD(®s->mdio) & GRETH_MII_BUSY) { |
| } |
| |
| GRETH_REGSAVE(®s->mdio, |
| ((data & 0xFFFF) << 16) | (0 << 11) | ((addr & 0x1F) << 6) |
| | 1); |
| |
| while (GRETH_REGLOAD(®s->mdio) & GRETH_MII_BUSY) { |
| } |
| |
| } |
| |
| /* init/start hardware and allocate descriptor buffers for rx side |
| * |
| */ |
| int greth_init(struct eth_device *dev, bd_t * bis) |
| { |
| int i; |
| |
| greth_priv *greth = dev->priv; |
| greth_regs *regs = greth->regs; |
| #ifdef DEBUG |
| printf("greth_init\n"); |
| #endif |
| |
| GRETH_REGSAVE(®s->control, 0); |
| |
| if (!greth->rxbd_base) { |
| |
| /* allocate descriptors */ |
| greth->rxbd_base = (greth_bd *) |
| memalign(0x1000, GRETH_RXBD_CNT * sizeof(greth_bd)); |
| greth->txbd_base = (greth_bd *) |
| memalign(0x1000, GRETH_RXBD_CNT * sizeof(greth_bd)); |
| |
| /* allocate buffers to all descriptors */ |
| greth->rxbuf_base = |
| malloc(GRETH_RXBUF_EFF_SIZE * GRETH_RXBD_CNT); |
| } |
| |
| /* initate rx decriptors */ |
| for (i = 0; i < GRETH_RXBD_CNT; i++) { |
| greth->rxbd_base[i].addr = (unsigned int) |
| greth->rxbuf_base + (GRETH_RXBUF_EFF_SIZE * i); |
| /* enable desciptor & set wrap bit if last descriptor */ |
| if (i >= (GRETH_RXBD_CNT - 1)) { |
| greth->rxbd_base[i].stat = GRETH_BD_EN | GRETH_BD_WR; |
| } else { |
| greth->rxbd_base[i].stat = GRETH_BD_EN; |
| } |
| } |
| |
| /* initiate indexes */ |
| greth->rxbd_curr = greth->rxbd_base; |
| greth->rxbd_max = greth->rxbd_base + (GRETH_RXBD_CNT - 1); |
| greth->txbd_max = greth->txbd_base + (GRETH_TXBD_CNT - 1); |
| /* |
| * greth->txbd_base->addr = 0; |
| * greth->txbd_base->stat = GRETH_BD_WR; |
| */ |
| |
| /* initate tx decriptors */ |
| for (i = 0; i < GRETH_TXBD_CNT; i++) { |
| greth->txbd_base[i].addr = 0; |
| /* enable desciptor & set wrap bit if last descriptor */ |
| if (i >= (GRETH_RXBD_CNT - 1)) { |
| greth->txbd_base[i].stat = GRETH_BD_WR; |
| } else { |
| greth->txbd_base[i].stat = 0; |
| } |
| } |
| |
| /**** SET HARDWARE REGS ****/ |
| |
| /* Set pointer to tx/rx descriptor areas */ |
| GRETH_REGSAVE(®s->rx_desc_p, (unsigned int)&greth->rxbd_base[0]); |
| GRETH_REGSAVE(®s->tx_desc_p, (unsigned int)&greth->txbd_base[0]); |
| |
| /* Enable Transmitter, GRETH will now scan descriptors for packets |
| * to transmitt */ |
| #ifdef DEBUG |
| printf("greth_init: enabling receiver\n"); |
| #endif |
| GRETH_REGORIN(®s->control, GRETH_RXEN); |
| |
| return 0; |
| } |
| |
| /* Initiate PHY to a relevant speed |
| * return: |
| * - 0 = success |
| * - 1 = timeout/fail |
| */ |
| int greth_init_phy(greth_priv * dev, bd_t * bis) |
| { |
| greth_regs *regs = dev->regs; |
| int tmp, tmp1, tmp2, i; |
| unsigned int start, timeout; |
| |
| /* X msecs to ticks */ |
| timeout = usec2ticks(GRETH_PHY_TIMEOUT_MS * 1000); |
| |
| /* Get system timer0 current value |
| * Total timeout is 5s |
| */ |
| start = get_timer(0); |
| |
| /* get phy control register default values */ |
| |
| while ((tmp = read_mii(0, regs)) & 0x8000) { |
| if (get_timer(start) > timeout) |
| return 1; /* Fail */ |
| } |
| |
| /* reset PHY and wait for completion */ |
| write_mii(0, 0x8000 | tmp, regs); |
| |
| while (((tmp = read_mii(0, regs))) & 0x8000) { |
| if (get_timer(start) > timeout) |
| return 1; /* Fail */ |
| } |
| |
| /* Check if PHY is autoneg capable and then determine operating |
| * mode, otherwise force it to 10 Mbit halfduplex |
| */ |
| dev->gb = 0; |
| dev->fd = 0; |
| dev->sp = 0; |
| dev->auto_neg = 0; |
| if (!((tmp >> 12) & 1)) { |
| write_mii(0, 0, regs); |
| } else { |
| /* wait for auto negotiation to complete and then check operating mode */ |
| dev->auto_neg = 1; |
| i = 0; |
| while (!(((tmp = read_mii(1, regs)) >> 5) & 1)) { |
| if (get_timer(start) > timeout) { |
| printf("Auto negotiation timed out. " |
| "Selecting default config\n"); |
| tmp = read_mii(0, regs); |
| dev->gb = ((tmp >> 6) & 1) |
| && !((tmp >> 13) & 1); |
| dev->sp = !((tmp >> 6) & 1) |
| && ((tmp >> 13) & 1); |
| dev->fd = (tmp >> 8) & 1; |
| goto auto_neg_done; |
| } |
| } |
| if ((tmp >> 8) & 1) { |
| tmp1 = read_mii(9, regs); |
| tmp2 = read_mii(10, regs); |
| if ((tmp1 & GRETH_MII_EXTADV_1000FD) && |
| (tmp2 & GRETH_MII_EXTPRT_1000FD)) { |
| dev->gb = 1; |
| dev->fd = 1; |
| } |
| if ((tmp1 & GRETH_MII_EXTADV_1000HD) && |
| (tmp2 & GRETH_MII_EXTPRT_1000HD)) { |
| dev->gb = 1; |
| dev->fd = 0; |
| } |
| } |
| if ((dev->gb == 0) || ((dev->gb == 1) && (dev->gbit_mac == 0))) { |
| tmp1 = read_mii(4, regs); |
| tmp2 = read_mii(5, regs); |
| if ((tmp1 & GRETH_MII_100TXFD) && |
| (tmp2 & GRETH_MII_100TXFD)) { |
| dev->sp = 1; |
| dev->fd = 1; |
| } |
| if ((tmp1 & GRETH_MII_100TXHD) && |
| (tmp2 & GRETH_MII_100TXHD)) { |
| dev->sp = 1; |
| dev->fd = 0; |
| } |
| if ((tmp1 & GRETH_MII_10FD) && (tmp2 & GRETH_MII_10FD)) { |
| dev->fd = 1; |
| } |
| if ((dev->gb == 1) && (dev->gbit_mac == 0)) { |
| dev->gb = 0; |
| dev->fd = 0; |
| write_mii(0, dev->sp << 13, regs); |
| } |
| } |
| |
| } |
| auto_neg_done: |
| #ifdef DEBUG |
| printf("%s GRETH Ethermac at [0x%x] irq %d. Running \ |
| %d Mbps %s duplex\n", dev->gbit_mac ? "10/100/1000" : "10/100", (unsigned int)(regs), (unsigned int)(dev->irq), dev->gb ? 1000 : (dev->sp ? 100 : 10), dev->fd ? "full" : "half"); |
| #endif |
| /* Read out PHY info if extended registers are available */ |
| if (tmp & 1) { |
| tmp1 = read_mii(2, regs); |
| tmp2 = read_mii(3, regs); |
| tmp1 = (tmp1 << 6) | ((tmp2 >> 10) & 0x3F); |
| tmp = tmp2 & 0xF; |
| |
| tmp2 = (tmp2 >> 4) & 0x3F; |
| #ifdef DEBUG |
| printf("PHY: Vendor %x Device %x Revision %d\n", tmp1, |
| tmp2, tmp); |
| #endif |
| } else { |
| printf("PHY info not available\n"); |
| } |
| |
| /* set speed and duplex bits in control register */ |
| GRETH_REGORIN(®s->control, |
| (dev->gb << 8) | (dev->sp << 7) | (dev->fd << 4)); |
| |
| return 0; |
| } |
| |
| void greth_halt(struct eth_device *dev) |
| { |
| greth_priv *greth; |
| greth_regs *regs; |
| int i; |
| #ifdef DEBUG |
| printf("greth_halt\n"); |
| #endif |
| if (!dev || !dev->priv) |
| return; |
| |
| greth = dev->priv; |
| regs = greth->regs; |
| |
| if (!regs) |
| return; |
| |
| /* disable receiver/transmitter by clearing the enable bits */ |
| GRETH_REGANDIN(®s->control, ~(GRETH_RXEN | GRETH_TXEN)); |
| |
| /* reset rx/tx descriptors */ |
| if (greth->rxbd_base) { |
| for (i = 0; i < GRETH_RXBD_CNT; i++) { |
| greth->rxbd_base[i].stat = |
| (i >= (GRETH_RXBD_CNT - 1)) ? GRETH_BD_WR : 0; |
| } |
| } |
| |
| if (greth->txbd_base) { |
| for (i = 0; i < GRETH_TXBD_CNT; i++) { |
| greth->txbd_base[i].stat = |
| (i >= (GRETH_TXBD_CNT - 1)) ? GRETH_BD_WR : 0; |
| } |
| } |
| } |
| |
| int greth_send(struct eth_device *dev, volatile void *eth_data, int data_length) |
| { |
| greth_priv *greth = dev->priv; |
| greth_regs *regs = greth->regs; |
| greth_bd *txbd; |
| void *txbuf; |
| unsigned int status; |
| #ifdef DEBUG |
| printf("greth_send\n"); |
| #endif |
| /* send data, wait for data to be sent, then return */ |
| if (((unsigned int)eth_data & (GRETH_BUF_ALIGN - 1)) |
| && !greth->gbit_mac) { |
| /* data not aligned as needed by GRETH 10/100, solve this by allocating 4 byte aligned buffer |
| * and copy data to before giving it to GRETH. |
| */ |
| if (!greth->txbuf) { |
| greth->txbuf = malloc(GRETH_RXBUF_SIZE); |
| #ifdef DEBUG |
| printf("GRETH: allocated aligned tx-buf\n"); |
| #endif |
| } |
| |
| txbuf = greth->txbuf; |
| |
| /* copy data info buffer */ |
| memcpy((char *)txbuf, (char *)eth_data, data_length); |
| |
| /* keep buffer to next time */ |
| } else { |
| txbuf = (void *)eth_data; |
| } |
| /* get descriptor to use, only 1 supported... hehe easy */ |
| txbd = greth->txbd_base; |
| |
| /* setup descriptor to wrap around to it self */ |
| txbd->addr = (unsigned int)txbuf; |
| txbd->stat = GRETH_BD_EN | GRETH_BD_WR | data_length; |
| |
| /* Remind Core which descriptor to use when sending */ |
| GRETH_REGSAVE(®s->tx_desc_p, (unsigned int)txbd); |
| |
| /* initate send by enabling transmitter */ |
| GRETH_REGORIN(®s->control, GRETH_TXEN); |
| |
| /* Wait for data to be sent */ |
| while ((status = GRETH_REGLOAD(&txbd->stat)) & GRETH_BD_EN) { |
| ; |
| } |
| |
| /* was the packet transmitted succesfully? */ |
| if (status & GRETH_TXBD_ERR_AL) { |
| greth->stats.tx_limit_errors++; |
| } |
| |
| if (status & GRETH_TXBD_ERR_UE) { |
| greth->stats.tx_underrun_errors++; |
| } |
| |
| if (status & GRETH_TXBD_ERR_LC) { |
| greth->stats.tx_latecol_errors++; |
| } |
| |
| if (status & |
| (GRETH_TXBD_ERR_LC | GRETH_TXBD_ERR_UE | GRETH_TXBD_ERR_AL)) { |
| /* any error */ |
| greth->stats.tx_errors++; |
| return -1; |
| } |
| |
| /* bump tx packet counter */ |
| greth->stats.tx_packets++; |
| |
| /* return succefully */ |
| return 0; |
| } |
| |
| int greth_recv(struct eth_device *dev) |
| { |
| greth_priv *greth = dev->priv; |
| greth_regs *regs = greth->regs; |
| greth_bd *rxbd; |
| unsigned int status, len = 0, bad; |
| unsigned char *d; |
| int enable = 0; |
| int i; |
| #ifdef DEBUG |
| /* printf("greth_recv\n"); */ |
| #endif |
| /* Receive One packet only, but clear as many error packets as there are |
| * available. |
| */ |
| { |
| /* current receive descriptor */ |
| rxbd = greth->rxbd_curr; |
| |
| /* get status of next received packet */ |
| status = GRETH_REGLOAD(&rxbd->stat); |
| |
| bad = 0; |
| |
| /* stop if no more packets received */ |
| if (status & GRETH_BD_EN) { |
| goto done; |
| } |
| #ifdef DEBUG |
| printf("greth_recv: packet 0x%lx, 0x%lx, len: %d\n", |
| (unsigned int)rxbd, status, status & GRETH_BD_LEN); |
| #endif |
| |
| /* Check status for errors. |
| */ |
| if (status & GRETH_RXBD_ERR_FT) { |
| greth->stats.rx_length_errors++; |
| bad = 1; |
| } |
| if (status & (GRETH_RXBD_ERR_AE | GRETH_RXBD_ERR_OE)) { |
| greth->stats.rx_frame_errors++; |
| bad = 1; |
| } |
| if (status & GRETH_RXBD_ERR_CRC) { |
| greth->stats.rx_crc_errors++; |
| bad = 1; |
| } |
| if (bad) { |
| greth->stats.rx_errors++; |
| printf |
| ("greth_recv: Bad packet (%d, %d, %d, 0x%08x, %d)\n", |
| greth->stats.rx_length_errors, |
| greth->stats.rx_frame_errors, |
| greth->stats.rx_crc_errors, status, |
| greth->stats.rx_packets); |
| /* print all rx descriptors */ |
| for (i = 0; i < GRETH_RXBD_CNT; i++) { |
| printf("[%d]: Stat=0x%lx, Addr=0x%lx\n", i, |
| GRETH_REGLOAD(&greth->rxbd_base[i].stat), |
| GRETH_REGLOAD(&greth->rxbd_base[i]. |
| addr)); |
| } |
| } else { |
| /* Process the incoming packet. */ |
| len = status & GRETH_BD_LEN; |
| d = (char *)rxbd->addr; |
| #ifdef DEBUG |
| printf |
| ("greth_recv: new packet, length: %d. data: %x %x %x %x %x %x %x %x\n", |
| len, d[0], d[1], d[2], d[3], d[4], d[5], d[6], |
| d[7]); |
| #endif |
| /* flush all data cache to make sure we're not reading old packet data */ |
| sparc_dcache_flush_all(); |
| |
| /* pass packet on to network subsystem */ |
| NetReceive((void *)d, len); |
| |
| /* bump stats counters */ |
| greth->stats.rx_packets++; |
| |
| /* bad is now 0 ==> will stop loop */ |
| } |
| |
| /* reenable descriptor to receive more packet with this descriptor, wrap around if needed */ |
| rxbd->stat = |
| GRETH_BD_EN | |
| (((unsigned int)greth->rxbd_curr >= |
| (unsigned int)greth->rxbd_max) ? GRETH_BD_WR : 0); |
| enable = 1; |
| |
| /* increase index */ |
| greth->rxbd_curr = |
| ((unsigned int)greth->rxbd_curr >= |
| (unsigned int)greth->rxbd_max) ? greth-> |
| rxbd_base : (greth->rxbd_curr + 1); |
| |
| }; |
| |
| if (enable) { |
| GRETH_REGORIN(®s->control, GRETH_RXEN); |
| } |
| done: |
| /* return positive length of packet or 0 if non recieved */ |
| return len; |
| } |
| |
| void greth_set_hwaddr(greth_priv * greth, unsigned char *mac) |
| { |
| /* save new MAC address */ |
| greth->dev->enetaddr[0] = greth->hwaddr[0] = mac[0]; |
| greth->dev->enetaddr[1] = greth->hwaddr[1] = mac[1]; |
| greth->dev->enetaddr[2] = greth->hwaddr[2] = mac[2]; |
| greth->dev->enetaddr[3] = greth->hwaddr[3] = mac[3]; |
| greth->dev->enetaddr[4] = greth->hwaddr[4] = mac[4]; |
| greth->dev->enetaddr[5] = greth->hwaddr[5] = mac[5]; |
| greth->regs->esa_msb = (mac[0] << 8) | mac[1]; |
| greth->regs->esa_lsb = |
| (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | mac[5]; |
| #ifdef DEBUG |
| printf("GRETH: New MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n", |
| mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); |
| #endif |
| } |
| |
| int greth_initialize(bd_t * bis) |
| { |
| greth_priv *greth; |
| ambapp_apbdev apbdev; |
| struct eth_device *dev; |
| int i; |
| char *addr_str, *end; |
| unsigned char addr[6]; |
| #ifdef DEBUG |
| printf("Scanning for GRETH\n"); |
| #endif |
| /* Find Device & IRQ via AMBA Plug&Play information */ |
| if (ambapp_apb_first(VENDOR_GAISLER, GAISLER_ETHMAC, &apbdev) != 1) { |
| return -1; /* GRETH not found */ |
| } |
| |
| greth = (greth_priv *) malloc(sizeof(greth_priv)); |
| dev = (struct eth_device *)malloc(sizeof(struct eth_device)); |
| memset(dev, 0, sizeof(struct eth_device)); |
| memset(greth, 0, sizeof(greth_priv)); |
| |
| greth->regs = (greth_regs *) apbdev.address; |
| greth->irq = apbdev.irq; |
| #ifdef DEBUG |
| printf("Found GRETH at 0x%lx, irq %d\n", greth->regs, greth->irq); |
| #endif |
| dev->priv = (void *)greth; |
| dev->iobase = (unsigned int)greth->regs; |
| dev->init = greth_init; |
| dev->halt = greth_halt; |
| dev->send = greth_send; |
| dev->recv = greth_recv; |
| greth->dev = dev; |
| |
| /* Reset Core */ |
| GRETH_REGSAVE(&greth->regs->control, GRETH_RESET); |
| |
| /* Wait for core to finish reset cycle */ |
| while (GRETH_REGLOAD(&greth->regs->control) & GRETH_RESET) ; |
| |
| /* Get the phy address which assumed to have been set |
| correctly with the reset value in hardware */ |
| greth->phyaddr = (GRETH_REGLOAD(&greth->regs->mdio) >> 11) & 0x1F; |
| |
| /* Check if mac is gigabit capable */ |
| greth->gbit_mac = (GRETH_REGLOAD(&greth->regs->control) >> 27) & 1; |
| |
| /* Make descriptor string */ |
| if (greth->gbit_mac) { |
| sprintf(dev->name, "GRETH 10/100/GB"); |
| } else { |
| sprintf(dev->name, "GRETH 10/100"); |
| } |
| |
| /* initiate PHY, select speed/duplex depending on connected PHY */ |
| if (greth_init_phy(greth, bis)) { |
| /* Failed to init PHY (timedout) */ |
| return -1; |
| } |
| |
| /* Register Device to EtherNet subsystem */ |
| eth_register(dev); |
| |
| /* Get MAC address */ |
| if ((addr_str = getenv("ethaddr")) != NULL) { |
| for (i = 0; i < 6; i++) { |
| addr[i] = |
| addr_str ? simple_strtoul(addr_str, &end, 16) : 0; |
| if (addr_str) { |
| addr_str = (*end) ? end + 1 : end; |
| } |
| } |
| } else { |
| /* HW Address not found in environment, Set default HW address */ |
| addr[0] = GRETH_HWADDR_0; /* MSB */ |
| addr[1] = GRETH_HWADDR_1; |
| addr[2] = GRETH_HWADDR_2; |
| addr[3] = GRETH_HWADDR_3; |
| addr[4] = GRETH_HWADDR_4; |
| addr[5] = GRETH_HWADDR_5; /* LSB */ |
| } |
| |
| /* set and remember MAC address */ |
| greth_set_hwaddr(greth, addr); |
| |
| return 0; |
| } |