10cbe3b6a4
These are all the files which use the API incorrectly but did not get built using MAKEALL -a powerpc|arm. I have no compiler for them, but the remaining issues should be far less than without this patch. Any outstanding issues are left to the maintainers of boards that use these drivers. Signed-off-by: Joe Hershberger <joe.hershberger@ni.com>
568 lines
13 KiB
C
568 lines
13 KiB
C
/*
|
|
* Faraday FTGMAC100 Ethernet
|
|
*
|
|
* (C) Copyright 2009 Faraday Technology
|
|
* Po-Yu Chuang <ratbert@faraday-tech.com>
|
|
*
|
|
* (C) Copyright 2010 Andes Technology
|
|
* Macpaul Lin <macpaul@andestech.com>
|
|
*
|
|
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <common.h>
|
|
#include <malloc.h>
|
|
#include <net.h>
|
|
#include <asm/io.h>
|
|
#include <linux/mii.h>
|
|
|
|
#include "ftgmac100.h"
|
|
|
|
#define ETH_ZLEN 60
|
|
|
|
/* RBSR - hw default init value is also 0x640 */
|
|
#define RBSR_DEFAULT_VALUE 0x640
|
|
|
|
/* PKTBUFSTX/PKTBUFSRX must both be power of 2 */
|
|
#define PKTBUFSTX 4 /* must be power of 2 */
|
|
|
|
struct ftgmac100_data {
|
|
struct ftgmac100_txdes txdes[PKTBUFSTX];
|
|
struct ftgmac100_rxdes rxdes[PKTBUFSRX];
|
|
int tx_index;
|
|
int rx_index;
|
|
int phy_addr;
|
|
};
|
|
|
|
/*
|
|
* struct mii_bus functions
|
|
*/
|
|
static int ftgmac100_mdiobus_read(struct eth_device *dev, int phy_addr,
|
|
int regnum)
|
|
{
|
|
struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase;
|
|
int phycr;
|
|
int i;
|
|
|
|
phycr = readl(&ftgmac100->phycr);
|
|
|
|
/* preserve MDC cycle threshold */
|
|
phycr &= FTGMAC100_PHYCR_MDC_CYCTHR_MASK;
|
|
|
|
phycr |= FTGMAC100_PHYCR_PHYAD(phy_addr)
|
|
| FTGMAC100_PHYCR_REGAD(regnum)
|
|
| FTGMAC100_PHYCR_MIIRD;
|
|
|
|
writel(phycr, &ftgmac100->phycr);
|
|
|
|
for (i = 0; i < 10; i++) {
|
|
phycr = readl(&ftgmac100->phycr);
|
|
|
|
if ((phycr & FTGMAC100_PHYCR_MIIRD) == 0) {
|
|
int data;
|
|
|
|
data = readl(&ftgmac100->phydata);
|
|
return FTGMAC100_PHYDATA_MIIRDATA(data);
|
|
}
|
|
|
|
mdelay(10);
|
|
}
|
|
|
|
debug("mdio read timed out\n");
|
|
return -1;
|
|
}
|
|
|
|
static int ftgmac100_mdiobus_write(struct eth_device *dev, int phy_addr,
|
|
int regnum, u16 value)
|
|
{
|
|
struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase;
|
|
int phycr;
|
|
int data;
|
|
int i;
|
|
|
|
phycr = readl(&ftgmac100->phycr);
|
|
|
|
/* preserve MDC cycle threshold */
|
|
phycr &= FTGMAC100_PHYCR_MDC_CYCTHR_MASK;
|
|
|
|
phycr |= FTGMAC100_PHYCR_PHYAD(phy_addr)
|
|
| FTGMAC100_PHYCR_REGAD(regnum)
|
|
| FTGMAC100_PHYCR_MIIWR;
|
|
|
|
data = FTGMAC100_PHYDATA_MIIWDATA(value);
|
|
|
|
writel(data, &ftgmac100->phydata);
|
|
writel(phycr, &ftgmac100->phycr);
|
|
|
|
for (i = 0; i < 10; i++) {
|
|
phycr = readl(&ftgmac100->phycr);
|
|
|
|
if ((phycr & FTGMAC100_PHYCR_MIIWR) == 0) {
|
|
debug("(phycr & FTGMAC100_PHYCR_MIIWR) == 0: " \
|
|
"phy_addr: %x\n", phy_addr);
|
|
return 0;
|
|
}
|
|
|
|
mdelay(1);
|
|
}
|
|
|
|
debug("mdio write timed out\n");
|
|
return -1;
|
|
}
|
|
|
|
int ftgmac100_phy_read(struct eth_device *dev, int addr, int reg, u16 *value)
|
|
{
|
|
*value = ftgmac100_mdiobus_read(dev , addr, reg);
|
|
|
|
if (*value == -1)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ftgmac100_phy_write(struct eth_device *dev, int addr, int reg, u16 value)
|
|
{
|
|
if (ftgmac100_mdiobus_write(dev, addr, reg, value) == -1)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ftgmac100_phy_reset(struct eth_device *dev)
|
|
{
|
|
struct ftgmac100_data *priv = dev->priv;
|
|
int i;
|
|
u16 status, adv;
|
|
|
|
adv = ADVERTISE_CSMA | ADVERTISE_ALL;
|
|
|
|
ftgmac100_phy_write(dev, priv->phy_addr, MII_ADVERTISE, adv);
|
|
|
|
printf("%s: Starting autonegotiation...\n", dev->name);
|
|
|
|
ftgmac100_phy_write(dev, priv->phy_addr,
|
|
MII_BMCR, (BMCR_ANENABLE | BMCR_ANRESTART));
|
|
|
|
for (i = 0; i < 100000 / 100; i++) {
|
|
ftgmac100_phy_read(dev, priv->phy_addr, MII_BMSR, &status);
|
|
|
|
if (status & BMSR_ANEGCOMPLETE)
|
|
break;
|
|
mdelay(1);
|
|
}
|
|
|
|
if (status & BMSR_ANEGCOMPLETE) {
|
|
printf("%s: Autonegotiation complete\n", dev->name);
|
|
} else {
|
|
printf("%s: Autonegotiation timed out (status=0x%04x)\n",
|
|
dev->name, status);
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int ftgmac100_phy_init(struct eth_device *dev)
|
|
{
|
|
struct ftgmac100_data *priv = dev->priv;
|
|
|
|
int phy_addr;
|
|
u16 phy_id, status, adv, lpa, stat_ge;
|
|
int media, speed, duplex;
|
|
int i;
|
|
|
|
/* Check if the PHY is up to snuff... */
|
|
for (phy_addr = 0; phy_addr < CONFIG_PHY_MAX_ADDR; phy_addr++) {
|
|
|
|
ftgmac100_phy_read(dev, phy_addr, MII_PHYSID1, &phy_id);
|
|
|
|
/*
|
|
* When it is unable to found PHY,
|
|
* the interface usually return 0xffff or 0x0000
|
|
*/
|
|
if (phy_id != 0xffff && phy_id != 0x0) {
|
|
printf("%s: found PHY at 0x%02x\n",
|
|
dev->name, phy_addr);
|
|
priv->phy_addr = phy_addr;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (phy_id == 0xffff || phy_id == 0x0) {
|
|
printf("%s: no PHY present\n", dev->name);
|
|
return 0;
|
|
}
|
|
|
|
ftgmac100_phy_read(dev, priv->phy_addr, MII_BMSR, &status);
|
|
|
|
if (!(status & BMSR_LSTATUS)) {
|
|
/* Try to re-negotiate if we don't have link already. */
|
|
ftgmac100_phy_reset(dev);
|
|
|
|
for (i = 0; i < 100000 / 100; i++) {
|
|
ftgmac100_phy_read(dev, priv->phy_addr,
|
|
MII_BMSR, &status);
|
|
if (status & BMSR_LSTATUS)
|
|
break;
|
|
udelay(100);
|
|
}
|
|
}
|
|
|
|
if (!(status & BMSR_LSTATUS)) {
|
|
printf("%s: link down\n", dev->name);
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_FTGMAC100_EGIGA
|
|
/* 1000 Base-T Status Register */
|
|
ftgmac100_phy_read(dev, priv->phy_addr,
|
|
MII_STAT1000, &stat_ge);
|
|
|
|
speed = (stat_ge & (LPA_1000FULL | LPA_1000HALF)
|
|
? 1 : 0);
|
|
|
|
duplex = ((stat_ge & LPA_1000FULL)
|
|
? 1 : 0);
|
|
|
|
if (speed) { /* Speed is 1000 */
|
|
printf("%s: link up, 1000bps %s-duplex\n",
|
|
dev->name, duplex ? "full" : "half");
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
ftgmac100_phy_read(dev, priv->phy_addr, MII_ADVERTISE, &adv);
|
|
ftgmac100_phy_read(dev, priv->phy_addr, MII_LPA, &lpa);
|
|
|
|
media = mii_nway_result(lpa & adv);
|
|
speed = (media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ? 1 : 0);
|
|
duplex = (media & ADVERTISE_FULL) ? 1 : 0;
|
|
|
|
printf("%s: link up, %sMbps %s-duplex\n",
|
|
dev->name, speed ? "100" : "10", duplex ? "full" : "half");
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int ftgmac100_update_link_speed(struct eth_device *dev)
|
|
{
|
|
struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase;
|
|
struct ftgmac100_data *priv = dev->priv;
|
|
|
|
unsigned short stat_fe;
|
|
unsigned short stat_ge;
|
|
unsigned int maccr;
|
|
|
|
#ifdef CONFIG_FTGMAC100_EGIGA
|
|
/* 1000 Base-T Status Register */
|
|
ftgmac100_phy_read(dev, priv->phy_addr, MII_STAT1000, &stat_ge);
|
|
#endif
|
|
|
|
ftgmac100_phy_read(dev, priv->phy_addr, MII_BMSR, &stat_fe);
|
|
|
|
if (!(stat_fe & BMSR_LSTATUS)) /* link status up? */
|
|
return 0;
|
|
|
|
/* read MAC control register and clear related bits */
|
|
maccr = readl(&ftgmac100->maccr) &
|
|
~(FTGMAC100_MACCR_GIGA_MODE |
|
|
FTGMAC100_MACCR_FAST_MODE |
|
|
FTGMAC100_MACCR_FULLDUP);
|
|
|
|
#ifdef CONFIG_FTGMAC100_EGIGA
|
|
if (stat_ge & LPA_1000FULL) {
|
|
/* set gmac for 1000BaseTX and Full Duplex */
|
|
maccr |= FTGMAC100_MACCR_GIGA_MODE | FTGMAC100_MACCR_FULLDUP;
|
|
}
|
|
|
|
if (stat_ge & LPA_1000HALF) {
|
|
/* set gmac for 1000BaseTX and Half Duplex */
|
|
maccr |= FTGMAC100_MACCR_GIGA_MODE;
|
|
}
|
|
#endif
|
|
|
|
if (stat_fe & BMSR_100FULL) {
|
|
/* set MII for 100BaseTX and Full Duplex */
|
|
maccr |= FTGMAC100_MACCR_FAST_MODE | FTGMAC100_MACCR_FULLDUP;
|
|
}
|
|
|
|
if (stat_fe & BMSR_10FULL) {
|
|
/* set MII for 10BaseT and Full Duplex */
|
|
maccr |= FTGMAC100_MACCR_FULLDUP;
|
|
}
|
|
|
|
if (stat_fe & BMSR_100HALF) {
|
|
/* set MII for 100BaseTX and Half Duplex */
|
|
maccr |= FTGMAC100_MACCR_FAST_MODE;
|
|
}
|
|
|
|
if (stat_fe & BMSR_10HALF) {
|
|
/* set MII for 10BaseT and Half Duplex */
|
|
/* we have already clear these bits, do nothing */
|
|
;
|
|
}
|
|
|
|
/* update MII config into maccr */
|
|
writel(maccr, &ftgmac100->maccr);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Reset MAC
|
|
*/
|
|
static void ftgmac100_reset(struct eth_device *dev)
|
|
{
|
|
struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase;
|
|
|
|
debug("%s()\n", __func__);
|
|
|
|
writel(FTGMAC100_MACCR_SW_RST, &ftgmac100->maccr);
|
|
|
|
while (readl(&ftgmac100->maccr) & FTGMAC100_MACCR_SW_RST)
|
|
;
|
|
}
|
|
|
|
/*
|
|
* Set MAC address
|
|
*/
|
|
static void ftgmac100_set_mac(struct eth_device *dev,
|
|
const unsigned char *mac)
|
|
{
|
|
struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase;
|
|
unsigned int maddr = mac[0] << 8 | mac[1];
|
|
unsigned int laddr = mac[2] << 24 | mac[3] << 16 | mac[4] << 8 | mac[5];
|
|
|
|
debug("%s(%x %x)\n", __func__, maddr, laddr);
|
|
|
|
writel(maddr, &ftgmac100->mac_madr);
|
|
writel(laddr, &ftgmac100->mac_ladr);
|
|
}
|
|
|
|
static void ftgmac100_set_mac_from_env(struct eth_device *dev)
|
|
{
|
|
eth_getenv_enetaddr("ethaddr", dev->enetaddr);
|
|
|
|
ftgmac100_set_mac(dev, dev->enetaddr);
|
|
}
|
|
|
|
/*
|
|
* disable transmitter, receiver
|
|
*/
|
|
static void ftgmac100_halt(struct eth_device *dev)
|
|
{
|
|
struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase;
|
|
|
|
debug("%s()\n", __func__);
|
|
|
|
writel(0, &ftgmac100->maccr);
|
|
}
|
|
|
|
static int ftgmac100_init(struct eth_device *dev, bd_t *bd)
|
|
{
|
|
struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase;
|
|
struct ftgmac100_data *priv = dev->priv;
|
|
struct ftgmac100_txdes *txdes = priv->txdes;
|
|
struct ftgmac100_rxdes *rxdes = priv->rxdes;
|
|
unsigned int maccr;
|
|
int i;
|
|
|
|
debug("%s()\n", __func__);
|
|
|
|
/* set the ethernet address */
|
|
ftgmac100_set_mac_from_env(dev);
|
|
|
|
/* disable all interrupts */
|
|
writel(0, &ftgmac100->ier);
|
|
|
|
/* initialize descriptors */
|
|
priv->tx_index = 0;
|
|
priv->rx_index = 0;
|
|
|
|
txdes[PKTBUFSTX - 1].txdes0 = FTGMAC100_TXDES0_EDOTR;
|
|
rxdes[PKTBUFSRX - 1].rxdes0 = FTGMAC100_RXDES0_EDORR;
|
|
|
|
for (i = 0; i < PKTBUFSTX; i++) {
|
|
/* TXBUF_BADR */
|
|
txdes[i].txdes3 = 0;
|
|
txdes[i].txdes1 = 0;
|
|
}
|
|
|
|
for (i = 0; i < PKTBUFSRX; i++) {
|
|
/* RXBUF_BADR */
|
|
rxdes[i].rxdes3 = (unsigned int)NetRxPackets[i];
|
|
rxdes[i].rxdes0 &= ~FTGMAC100_RXDES0_RXPKT_RDY;
|
|
}
|
|
|
|
/* transmit ring */
|
|
writel((unsigned int)txdes, &ftgmac100->txr_badr);
|
|
|
|
/* receive ring */
|
|
writel((unsigned int)rxdes, &ftgmac100->rxr_badr);
|
|
|
|
/* poll receive descriptor automatically */
|
|
writel(FTGMAC100_APTC_RXPOLL_CNT(1), &ftgmac100->aptc);
|
|
|
|
/* config receive buffer size register */
|
|
writel(FTGMAC100_RBSR_SIZE(RBSR_DEFAULT_VALUE), &ftgmac100->rbsr);
|
|
|
|
/* enable transmitter, receiver */
|
|
maccr = FTGMAC100_MACCR_TXMAC_EN |
|
|
FTGMAC100_MACCR_RXMAC_EN |
|
|
FTGMAC100_MACCR_TXDMA_EN |
|
|
FTGMAC100_MACCR_RXDMA_EN |
|
|
FTGMAC100_MACCR_CRC_APD |
|
|
FTGMAC100_MACCR_FULLDUP |
|
|
FTGMAC100_MACCR_RX_RUNT |
|
|
FTGMAC100_MACCR_RX_BROADPKT;
|
|
|
|
writel(maccr, &ftgmac100->maccr);
|
|
|
|
if (!ftgmac100_phy_init(dev)) {
|
|
if (!ftgmac100_update_link_speed(dev))
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Get a data block via Ethernet
|
|
*/
|
|
static int ftgmac100_recv(struct eth_device *dev)
|
|
{
|
|
struct ftgmac100_data *priv = dev->priv;
|
|
struct ftgmac100_rxdes *curr_des;
|
|
unsigned short rxlen;
|
|
|
|
curr_des = &priv->rxdes[priv->rx_index];
|
|
|
|
if (!(curr_des->rxdes0 & FTGMAC100_RXDES0_RXPKT_RDY))
|
|
return -1;
|
|
|
|
if (curr_des->rxdes0 & (FTGMAC100_RXDES0_RX_ERR |
|
|
FTGMAC100_RXDES0_CRC_ERR |
|
|
FTGMAC100_RXDES0_FTL |
|
|
FTGMAC100_RXDES0_RUNT |
|
|
FTGMAC100_RXDES0_RX_ODD_NB)) {
|
|
return -1;
|
|
}
|
|
|
|
rxlen = FTGMAC100_RXDES0_VDBC(curr_des->rxdes0);
|
|
|
|
debug("%s(): RX buffer %d, %x received\n",
|
|
__func__, priv->rx_index, rxlen);
|
|
|
|
/* pass the packet up to the protocol layers. */
|
|
NetReceive((void *)curr_des->rxdes3, rxlen);
|
|
|
|
/* release buffer to DMA */
|
|
curr_des->rxdes0 &= ~FTGMAC100_RXDES0_RXPKT_RDY;
|
|
|
|
priv->rx_index = (priv->rx_index + 1) % PKTBUFSRX;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Send a data block via Ethernet
|
|
*/
|
|
static int ftgmac100_send(struct eth_device *dev, void *packet, int length)
|
|
{
|
|
struct ftgmac100 *ftgmac100 = (struct ftgmac100 *)dev->iobase;
|
|
struct ftgmac100_data *priv = dev->priv;
|
|
struct ftgmac100_txdes *curr_des = &priv->txdes[priv->tx_index];
|
|
int start;
|
|
|
|
if (curr_des->txdes0 & FTGMAC100_TXDES0_TXDMA_OWN) {
|
|
debug("%s(): no TX descriptor available\n", __func__);
|
|
return -1;
|
|
}
|
|
|
|
debug("%s(%x, %x)\n", __func__, (int)packet, length);
|
|
|
|
length = (length < ETH_ZLEN) ? ETH_ZLEN : length;
|
|
|
|
/* initiate a transmit sequence */
|
|
curr_des->txdes3 = (unsigned int)packet; /* TXBUF_BADR */
|
|
|
|
/* only one descriptor on TXBUF */
|
|
curr_des->txdes0 &= FTGMAC100_TXDES0_EDOTR;
|
|
curr_des->txdes0 |= FTGMAC100_TXDES0_FTS |
|
|
FTGMAC100_TXDES0_LTS |
|
|
FTGMAC100_TXDES0_TXBUF_SIZE(length) |
|
|
FTGMAC100_TXDES0_TXDMA_OWN ;
|
|
|
|
/* start transmit */
|
|
writel(1, &ftgmac100->txpd);
|
|
|
|
/* wait for transfer to succeed */
|
|
start = get_timer(0);
|
|
while (curr_des->txdes0 & FTGMAC100_TXDES0_TXDMA_OWN) {
|
|
if (get_timer(0) >= 5) {
|
|
debug("%s(): timed out\n", __func__);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
debug("%s(): packet sent\n", __func__);
|
|
|
|
priv->tx_index = (priv->tx_index + 1) % PKTBUFSTX;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int ftgmac100_initialize(bd_t *bd)
|
|
{
|
|
struct eth_device *dev;
|
|
struct ftgmac100_data *priv;
|
|
|
|
dev = malloc(sizeof *dev);
|
|
if (!dev) {
|
|
printf("%s(): failed to allocate dev\n", __func__);
|
|
goto out;
|
|
}
|
|
|
|
/* Transmit and receive descriptors should align to 16 bytes */
|
|
priv = memalign(16, sizeof(struct ftgmac100_data));
|
|
if (!priv) {
|
|
printf("%s(): failed to allocate priv\n", __func__);
|
|
goto free_dev;
|
|
}
|
|
|
|
memset(dev, 0, sizeof(*dev));
|
|
memset(priv, 0, sizeof(*priv));
|
|
|
|
sprintf(dev->name, "FTGMAC100");
|
|
dev->iobase = CONFIG_FTGMAC100_BASE;
|
|
dev->init = ftgmac100_init;
|
|
dev->halt = ftgmac100_halt;
|
|
dev->send = ftgmac100_send;
|
|
dev->recv = ftgmac100_recv;
|
|
dev->priv = priv;
|
|
|
|
eth_register(dev);
|
|
|
|
ftgmac100_reset(dev);
|
|
|
|
return 1;
|
|
|
|
free_dev:
|
|
free(dev);
|
|
out:
|
|
return 0;
|
|
}
|