mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-30 00:21:06 +00:00
1fd92db83d
Update the naming convention used in the network stack functions and variables that Ethernet drivers use to interact with it. This cleans up the temporary hacks that were added to this interface along with the DM support. This patch has a few remaining checkpatch.pl failures that would be out of the scope of this patch to fix (drivers that are in gross violation of checkpatch.pl). Signed-off-by: Joe Hershberger <joe.hershberger@ni.com> Acked-by: Simon Glass <sjg@chromium.org>
393 lines
9.7 KiB
C
393 lines
9.7 KiB
C
/*
|
|
* Dave Ethernet Controller driver
|
|
*
|
|
* Copyright (C) 2008 Dave S.r.l. <www.dave.eu>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
#include <common.h>
|
|
|
|
#ifndef CONFIG_DNET_AUTONEG_TIMEOUT
|
|
#define CONFIG_DNET_AUTONEG_TIMEOUT 5000000 /* default value */
|
|
#endif
|
|
|
|
#include <net.h>
|
|
#include <malloc.h>
|
|
#include <linux/mii.h>
|
|
|
|
#include <miiphy.h>
|
|
#include <asm/io.h>
|
|
#include <asm/unaligned.h>
|
|
|
|
#include "dnet.h"
|
|
|
|
struct dnet_device {
|
|
struct dnet_registers *regs;
|
|
const struct device *dev;
|
|
struct eth_device netdev;
|
|
unsigned short phy_addr;
|
|
};
|
|
|
|
/* get struct dnet_device from given struct netdev */
|
|
#define to_dnet(_nd) container_of(_nd, struct dnet_device, netdev)
|
|
|
|
/* function for reading internal MAC register */
|
|
u16 dnet_readw_mac(struct dnet_device *dnet, u16 reg)
|
|
{
|
|
u16 data_read;
|
|
|
|
/* issue a read */
|
|
writel(reg, &dnet->regs->MACREG_ADDR);
|
|
|
|
/* since a read/write op to the MAC is very slow,
|
|
* we must wait before reading the data */
|
|
udelay(1);
|
|
|
|
/* read data read from the MAC register */
|
|
data_read = readl(&dnet->regs->MACREG_DATA);
|
|
|
|
/* all done */
|
|
return data_read;
|
|
}
|
|
|
|
/* function for writing internal MAC register */
|
|
void dnet_writew_mac(struct dnet_device *dnet, u16 reg, u16 val)
|
|
{
|
|
/* load data to write */
|
|
writel(val, &dnet->regs->MACREG_DATA);
|
|
|
|
/* issue a write */
|
|
writel(reg | DNET_INTERNAL_WRITE, &dnet->regs->MACREG_ADDR);
|
|
|
|
/* since a read/write op to the MAC is very slow,
|
|
* we must wait before exiting */
|
|
udelay(1);
|
|
}
|
|
|
|
static void dnet_mdio_write(struct dnet_device *dnet, u8 reg, u16 value)
|
|
{
|
|
u16 tmp;
|
|
|
|
debug(DRIVERNAME "dnet_mdio_write %02x:%02x <- %04x\n",
|
|
dnet->phy_addr, reg, value);
|
|
|
|
while (!(dnet_readw_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG) &
|
|
DNET_INTERNAL_GMII_MNG_CMD_FIN))
|
|
;
|
|
|
|
/* prepare for a write operation */
|
|
tmp = (1 << 13);
|
|
|
|
/* only 5 bits allowed for register offset */
|
|
reg &= 0x1f;
|
|
|
|
/* prepare reg_value for a write */
|
|
tmp |= (dnet->phy_addr << 8);
|
|
tmp |= reg;
|
|
|
|
/* write data to write first */
|
|
dnet_writew_mac(dnet, DNET_INTERNAL_GMII_MNG_DAT_REG, value);
|
|
|
|
/* write control word */
|
|
dnet_writew_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG, tmp);
|
|
|
|
while (!(dnet_readw_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG) &
|
|
DNET_INTERNAL_GMII_MNG_CMD_FIN))
|
|
;
|
|
}
|
|
|
|
static u16 dnet_mdio_read(struct dnet_device *dnet, u8 reg)
|
|
{
|
|
u16 value;
|
|
|
|
while (!(dnet_readw_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG) &
|
|
DNET_INTERNAL_GMII_MNG_CMD_FIN))
|
|
;
|
|
|
|
/* only 5 bits allowed for register offset*/
|
|
reg &= 0x1f;
|
|
|
|
/* prepare reg_value for a read */
|
|
value = (dnet->phy_addr << 8);
|
|
value |= reg;
|
|
|
|
/* write control word */
|
|
dnet_writew_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG, value);
|
|
|
|
/* wait for end of transfer */
|
|
while (!(dnet_readw_mac(dnet, DNET_INTERNAL_GMII_MNG_CTL_REG) &
|
|
DNET_INTERNAL_GMII_MNG_CMD_FIN))
|
|
;
|
|
|
|
value = dnet_readw_mac(dnet, DNET_INTERNAL_GMII_MNG_DAT_REG);
|
|
|
|
debug(DRIVERNAME "dnet_mdio_read %02x:%02x <- %04x\n",
|
|
dnet->phy_addr, reg, value);
|
|
|
|
return value;
|
|
}
|
|
|
|
static int dnet_send(struct eth_device *netdev, void *packet, int length)
|
|
{
|
|
struct dnet_device *dnet = to_dnet(netdev);
|
|
int i, wrsz;
|
|
unsigned int *bufp;
|
|
unsigned int tx_cmd;
|
|
|
|
debug(DRIVERNAME "[%s] Sending %u bytes\n", __func__, length);
|
|
|
|
bufp = (unsigned int *) (((u32)packet) & 0xFFFFFFFC);
|
|
wrsz = (u32)length + 3;
|
|
wrsz += ((u32)packet) & 0x3;
|
|
wrsz >>= 2;
|
|
tx_cmd = ((((unsigned int)(packet)) & 0x03) << 16) | (u32)length;
|
|
|
|
/* check if there is enough room for the current frame */
|
|
if (wrsz < (DNET_FIFO_SIZE - readl(&dnet->regs->TX_FIFO_WCNT))) {
|
|
for (i = 0; i < wrsz; i++)
|
|
writel(*bufp++, &dnet->regs->TX_DATA_FIFO);
|
|
/*
|
|
* inform MAC that a packet's written and ready
|
|
* to be shipped out
|
|
*/
|
|
writel(tx_cmd, &dnet->regs->TX_LEN_FIFO);
|
|
} else {
|
|
printf(DRIVERNAME "No free space (actual %d, required %d "
|
|
"(words))\n", DNET_FIFO_SIZE -
|
|
readl(&dnet->regs->TX_FIFO_WCNT), wrsz);
|
|
}
|
|
|
|
/* No one cares anyway */
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int dnet_recv(struct eth_device *netdev)
|
|
{
|
|
struct dnet_device *dnet = to_dnet(netdev);
|
|
unsigned int *data_ptr;
|
|
int pkt_len, poll, i;
|
|
u32 cmd_word;
|
|
|
|
debug("Waiting for pkt (polling)\n");
|
|
poll = 50;
|
|
while ((readl(&dnet->regs->RX_FIFO_WCNT) >> 16) == 0) {
|
|
udelay(10); /* wait 10 usec */
|
|
if (--poll == 0)
|
|
return 0; /* no pkt available */
|
|
}
|
|
|
|
cmd_word = readl(&dnet->regs->RX_LEN_FIFO);
|
|
pkt_len = cmd_word & 0xFFFF;
|
|
|
|
debug("Got pkt with size %d bytes\n", pkt_len);
|
|
|
|
if (cmd_word & 0xDF180000)
|
|
printf("%s packet receive error %x\n", __func__, cmd_word);
|
|
|
|
data_ptr = (unsigned int *)net_rx_packets[0];
|
|
|
|
for (i = 0; i < (pkt_len + 3) >> 2; i++)
|
|
*data_ptr++ = readl(&dnet->regs->RX_DATA_FIFO);
|
|
|
|
/* ok + 5 ?? */
|
|
net_process_received_packet(net_rx_packets[0], pkt_len + 5);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void dnet_set_hwaddr(struct eth_device *netdev)
|
|
{
|
|
struct dnet_device *dnet = to_dnet(netdev);
|
|
u16 tmp;
|
|
|
|
tmp = get_unaligned_be16(netdev->enetaddr);
|
|
dnet_writew_mac(dnet, DNET_INTERNAL_MAC_ADDR_0_REG, tmp);
|
|
tmp = get_unaligned_be16(&netdev->enetaddr[2]);
|
|
dnet_writew_mac(dnet, DNET_INTERNAL_MAC_ADDR_1_REG, tmp);
|
|
tmp = get_unaligned_be16(&netdev->enetaddr[4]);
|
|
dnet_writew_mac(dnet, DNET_INTERNAL_MAC_ADDR_2_REG, tmp);
|
|
}
|
|
|
|
static void dnet_phy_reset(struct dnet_device *dnet)
|
|
{
|
|
struct eth_device *netdev = &dnet->netdev;
|
|
int i;
|
|
u16 status, adv;
|
|
|
|
adv = ADVERTISE_CSMA | ADVERTISE_ALL;
|
|
dnet_mdio_write(dnet, MII_ADVERTISE, adv);
|
|
printf("%s: Starting autonegotiation...\n", netdev->name);
|
|
dnet_mdio_write(dnet, MII_BMCR, (BMCR_ANENABLE
|
|
| BMCR_ANRESTART));
|
|
|
|
for (i = 0; i < CONFIG_DNET_AUTONEG_TIMEOUT / 100; i++) {
|
|
status = dnet_mdio_read(dnet, MII_BMSR);
|
|
if (status & BMSR_ANEGCOMPLETE)
|
|
break;
|
|
udelay(100);
|
|
}
|
|
|
|
if (status & BMSR_ANEGCOMPLETE)
|
|
printf("%s: Autonegotiation complete\n", netdev->name);
|
|
else
|
|
printf("%s: Autonegotiation timed out (status=0x%04x)\n",
|
|
netdev->name, status);
|
|
}
|
|
|
|
static int dnet_phy_init(struct dnet_device *dnet)
|
|
{
|
|
struct eth_device *netdev = &dnet->netdev;
|
|
u16 phy_id, status, adv, lpa;
|
|
int media, speed, duplex;
|
|
int i;
|
|
u32 ctl_reg;
|
|
|
|
/* Find a PHY */
|
|
for (i = 0; i < 32; i++) {
|
|
dnet->phy_addr = i;
|
|
phy_id = dnet_mdio_read(dnet, MII_PHYSID1);
|
|
if (phy_id != 0xffff) {
|
|
/* ok we found it */
|
|
printf("Found PHY at address %d PHYID (%04x:%04x)\n",
|
|
i, phy_id,
|
|
dnet_mdio_read(dnet, MII_PHYSID2));
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Check if the PHY is up to snuff... */
|
|
phy_id = dnet_mdio_read(dnet, MII_PHYSID1);
|
|
if (phy_id == 0xffff) {
|
|
printf("%s: No PHY present\n", netdev->name);
|
|
return -1;
|
|
}
|
|
|
|
status = dnet_mdio_read(dnet, MII_BMSR);
|
|
if (!(status & BMSR_LSTATUS)) {
|
|
/* Try to re-negotiate if we don't have link already. */
|
|
dnet_phy_reset(dnet);
|
|
|
|
for (i = 0; i < CONFIG_DNET_AUTONEG_TIMEOUT / 100; i++) {
|
|
status = dnet_mdio_read(dnet, MII_BMSR);
|
|
if (status & BMSR_LSTATUS)
|
|
break;
|
|
udelay(100);
|
|
}
|
|
}
|
|
|
|
if (!(status & BMSR_LSTATUS)) {
|
|
printf("%s: link down (status: 0x%04x)\n",
|
|
netdev->name, status);
|
|
return -1;
|
|
} else {
|
|
adv = dnet_mdio_read(dnet, MII_ADVERTISE);
|
|
lpa = dnet_mdio_read(dnet, MII_LPA);
|
|
media = mii_nway_result(lpa & adv);
|
|
speed = (media & (ADVERTISE_100FULL | ADVERTISE_100HALF)
|
|
? 1 : 0);
|
|
duplex = (media & ADVERTISE_FULL) ? 1 : 0;
|
|
/* 1000BaseT ethernet is not supported */
|
|
printf("%s: link up, %sMbps %s-duplex (lpa: 0x%04x)\n",
|
|
netdev->name,
|
|
speed ? "100" : "10",
|
|
duplex ? "full" : "half",
|
|
lpa);
|
|
|
|
ctl_reg = dnet_readw_mac(dnet, DNET_INTERNAL_RXTX_CONTROL_REG);
|
|
|
|
if (duplex)
|
|
ctl_reg &= ~(DNET_INTERNAL_RXTX_CONTROL_ENABLEHALFDUP);
|
|
else
|
|
ctl_reg |= DNET_INTERNAL_RXTX_CONTROL_ENABLEHALFDUP;
|
|
|
|
dnet_writew_mac(dnet, DNET_INTERNAL_RXTX_CONTROL_REG, ctl_reg);
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int dnet_init(struct eth_device *netdev, bd_t *bd)
|
|
{
|
|
struct dnet_device *dnet = to_dnet(netdev);
|
|
u32 config;
|
|
|
|
/*
|
|
* dnet_halt should have been called at some point before now,
|
|
* so we'll assume the controller is idle.
|
|
*/
|
|
|
|
/* set hardware address */
|
|
dnet_set_hwaddr(netdev);
|
|
|
|
if (dnet_phy_init(dnet) < 0)
|
|
return -1;
|
|
|
|
/* flush rx/tx fifos */
|
|
writel(DNET_SYS_CTL_RXFIFOFLUSH | DNET_SYS_CTL_TXFIFOFLUSH,
|
|
&dnet->regs->SYS_CTL);
|
|
udelay(1000);
|
|
writel(0, &dnet->regs->SYS_CTL);
|
|
|
|
config = dnet_readw_mac(dnet, DNET_INTERNAL_RXTX_CONTROL_REG);
|
|
|
|
config |= DNET_INTERNAL_RXTX_CONTROL_RXPAUSE |
|
|
DNET_INTERNAL_RXTX_CONTROL_RXBROADCAST |
|
|
DNET_INTERNAL_RXTX_CONTROL_DROPCONTROL |
|
|
DNET_INTERNAL_RXTX_CONTROL_DISCFXFCS;
|
|
|
|
dnet_writew_mac(dnet, DNET_INTERNAL_RXTX_CONTROL_REG, config);
|
|
|
|
/* Enable TX and RX */
|
|
dnet_writew_mac(dnet, DNET_INTERNAL_MODE_REG,
|
|
DNET_INTERNAL_MODE_RXEN | DNET_INTERNAL_MODE_TXEN);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void dnet_halt(struct eth_device *netdev)
|
|
{
|
|
struct dnet_device *dnet = to_dnet(netdev);
|
|
|
|
/* Disable TX and RX */
|
|
dnet_writew_mac(dnet, DNET_INTERNAL_MODE_REG, 0);
|
|
}
|
|
|
|
int dnet_eth_initialize(int id, void *regs, unsigned int phy_addr)
|
|
{
|
|
struct dnet_device *dnet;
|
|
struct eth_device *netdev;
|
|
unsigned int dev_capa;
|
|
|
|
dnet = malloc(sizeof(struct dnet_device));
|
|
if (!dnet) {
|
|
printf("Error: Failed to allocate memory for DNET%d\n", id);
|
|
return -1;
|
|
}
|
|
memset(dnet, 0, sizeof(struct dnet_device));
|
|
|
|
netdev = &dnet->netdev;
|
|
|
|
dnet->regs = (struct dnet_registers *)regs;
|
|
dnet->phy_addr = phy_addr;
|
|
|
|
sprintf(netdev->name, "dnet%d", id);
|
|
netdev->init = dnet_init;
|
|
netdev->halt = dnet_halt;
|
|
netdev->send = dnet_send;
|
|
netdev->recv = dnet_recv;
|
|
|
|
dev_capa = readl(&dnet->regs->VERCAPS) & 0xFFFF;
|
|
debug("%s: has %smdio, %sirq, %sgigabit, %sdma \n", netdev->name,
|
|
(dev_capa & DNET_HAS_MDIO) ? "" : "no ",
|
|
(dev_capa & DNET_HAS_IRQ) ? "" : "no ",
|
|
(dev_capa & DNET_HAS_GIGABIT) ? "" : "no ",
|
|
(dev_capa & DNET_HAS_DMA) ? "" : "no ");
|
|
|
|
eth_register(netdev);
|
|
|
|
return 0;
|
|
}
|