📄 greth.c
字号:
/* 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,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -