9
0
Fork 0
barebox/drivers/net/at91_ether.c

365 lines
8.9 KiB
C

/*
* Copyright (C) 2009-2012 Jean-Christophe PLAGNIOL-VILLARD <plagnio@jcrosoft.com>
*
* (C) Copyright 2003
* Author : Hamid Ikdoumi (Atmel)
*
* 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.
*
*/
#include <common.h>
#include <net.h>
#include <clock.h>
#include <malloc.h>
#include <driver.h>
#include <xfuncs.h>
#include <init.h>
#include <asm/io.h>
#include <mach/hardware.h>
#include <mach/at91rm9200_emac.h>
#include <mach/board.h>
#include <generated/mach-types.h>
#include <linux/clk.h>
#include <linux/mii.h>
#include <errno.h>
#include <asm/mmu.h>
#include <linux/phy.h>
#include "at91_ether.h"
struct ether_device {
struct eth_device netdev;
struct mii_bus miibus;
struct rbf_t *rbfp;
struct rbf_t *rbfdt;
unsigned char *rbf_framebuf;
int phy_addr;
phy_interface_t interface;
};
#define to_ether(_nd) container_of(_nd, struct ether_device, netdev)
/*
* Enable the MDIO bit in MAC control register
* When not called from an interrupt-handler, access to the PHY must be
* protected by a spinlock.
*/
static void enable_mdi(void)
{
unsigned long ctl;
ctl = at91_emac_read(AT91_EMAC_CTL);
at91_emac_write(AT91_EMAC_CTL, ctl | AT91_EMAC_MPE); /* enable management port */
}
/*
* Disable the MDIO bit in the MAC control register
*/
static void disable_mdi(void)
{
unsigned long ctl;
ctl = at91_emac_read(AT91_EMAC_CTL);
at91_emac_write(AT91_EMAC_CTL, ctl & ~AT91_EMAC_MPE); /* disable management port */
}
/*
* Wait until the PHY operation is complete.
*/
static inline int at91_phy_wait(void)
{
uint64_t start;
start = get_time_ns();
do {
if (is_timeout(start, 2 * MSECOND)) {
puts("at91_ether: MIO timeout\n");
return -1;
}
} while (!(at91_emac_read(AT91_EMAC_SR) & AT91_EMAC_SR_IDLE));
return 0;
}
static int at91_ether_mii_read(struct mii_bus *dev, int addr, int reg)
{
int value;
enable_mdi();
at91_emac_write(AT91_EMAC_MAN, AT91_EMAC_MAN_802_3 | AT91_EMAC_RW_R
| ((addr & 0x1f) << 23) | (reg << 18));
/* Wait until IDLE bit in Network Status register is cleared */
value = at91_phy_wait();
if (value < 0)
goto out;
value = at91_emac_read(AT91_EMAC_MAN) & AT91_EMAC_DATA;
out:
disable_mdi();
return value;
}
static int at91_ether_mii_write(struct mii_bus *dev, int addr, int reg, u16 val)
{
int ret;
enable_mdi();
at91_emac_write(AT91_EMAC_MAN, AT91_EMAC_MAN_802_3 | AT91_EMAC_RW_W
| ((addr & 0x1f) << 23) | (reg << 18) | (val & AT91_EMAC_DATA));
/* Wait until IDLE bit in Network Status register is cleared */
ret = at91_phy_wait();
disable_mdi();
return ret;
}
static void update_linkspeed(struct eth_device *edev)
{
unsigned int mac_cfg;
/* Update the MAC */
mac_cfg = at91_emac_read(AT91_EMAC_CFG) & ~(AT91_EMAC_SPD | AT91_EMAC_FD);
if (edev->phydev->speed == SPEED_100) {
if (edev->phydev->duplex)
mac_cfg |= AT91_EMAC_SPD | AT91_EMAC_FD;
else /* 100 Half Duplex */
mac_cfg |= AT91_EMAC_SPD;
} else {
if (edev->phydev->duplex)
mac_cfg |= AT91_EMAC_FD;
else {} /* 10 Half Duplex */
}
at91_emac_write(AT91_EMAC_CFG, mac_cfg);
}
static int at91_ether_open(struct eth_device *edev)
{
int i;
unsigned long ctl;
struct ether_device *etdev = to_ether(edev);
unsigned char *rbf_framebuf = etdev->rbf_framebuf;
int ret;
ret = phy_device_connect(edev, &etdev->miibus, etdev->phy_addr,
update_linkspeed, 0, etdev->interface);
if (ret)
return ret;
/* Clear internal statistics */
ctl = at91_emac_read(AT91_EMAC_CTL);
at91_emac_write(AT91_EMAC_CTL, ctl | AT91_EMAC_CSR);
/* Init Ethernet buffers */
etdev->rbfp = etdev->rbfdt;
for (i = 0; i < MAX_RX_DESCR; i++) {
etdev->rbfp[i].addr = (unsigned long)rbf_framebuf;
etdev->rbfp[i].size = 0;
rbf_framebuf += MAX_RBUFF_SZ;
}
etdev->rbfp[i - 1].addr |= RBF_WRAP;
/* Program address of descriptor list in Rx Buffer Queue register */
at91_emac_write(AT91_EMAC_RBQP, (unsigned long) etdev->rbfdt);
ctl = at91_emac_read(AT91_EMAC_RSR);
ctl &= ~(AT91_EMAC_RSR_OVR | AT91_EMAC_RSR_REC | AT91_EMAC_RSR_BNA);
at91_emac_write(AT91_EMAC_RSR, ctl);
ctl = at91_emac_read(AT91_EMAC_CFG);
ctl |= AT91_EMAC_CAF | AT91_EMAC_NBC;
at91_emac_write(AT91_EMAC_CFG, ctl);
/* Enable Receive and Transmit */
ctl = at91_emac_read(AT91_EMAC_CTL);
ctl |= AT91_EMAC_RE | AT91_EMAC_TE;
at91_emac_write(AT91_EMAC_CTL, ctl);
return 0;
}
static int at91_ether_send(struct eth_device *edev, void *packet, int length)
{
while (!(at91_emac_read(AT91_EMAC_TSR) & AT91_EMAC_TSR_BNQ));
dma_flush_range((ulong) packet, (ulong)packet + length);
/* Set address of the data in the Transmit Address register */
at91_emac_write(AT91_EMAC_TAR, (unsigned long) packet);
/* Set length of the packet in the Transmit Control register */
at91_emac_write(AT91_EMAC_TCR, length);
while (at91_emac_read(AT91_EMAC_TCR) & 0x7ff);
at91_emac_write(AT91_EMAC_TSR,
at91_emac_read(AT91_EMAC_TSR) | AT91_EMAC_TSR_COMP);
return 0;
}
static int at91_ether_rx(struct eth_device *edev)
{
struct ether_device *etdev = to_ether(edev);
int size;
struct rbf_t *rbfp = etdev->rbfp;
if (!(rbfp->addr & RBF_OWNER))
return 0;
size = rbfp->size & RBF_SIZE;
net_receive(edev, (unsigned char *)(rbfp->addr & RBF_ADDR), size);
rbfp->addr &= ~RBF_OWNER;
if (rbfp->addr & RBF_WRAP)
etdev->rbfp = etdev->rbfdt;
else
etdev->rbfp++;
at91_emac_write(AT91_EMAC_RSR,
at91_emac_read(AT91_EMAC_RSR) | AT91_EMAC_RSR_REC);
return size;
}
static void at91_ether_halt (struct eth_device *edev)
{
unsigned long ctl;
/* Disable Receiver and Transmitter */
ctl = at91_emac_read(AT91_EMAC_CTL);
ctl &= ~(AT91_EMAC_TE | AT91_EMAC_RE);
at91_emac_write(AT91_EMAC_CTL, ctl);
}
static int at91_ether_get_ethaddr(struct eth_device *eth, unsigned char *adr)
{
/* We have no eeprom */
return -1;
}
static int at91_ether_set_ethaddr(struct eth_device *eth, unsigned char *adr)
{
int i;
/* The CSB337 originally used a version of the MicroMonitor bootloader
* which saved Ethernet addresses in the "wrong" order. Operating
* systems (like Linux) know this, and apply a workaround. Replicate
* that MicroMonitor behavior so we avoid needing to make such OS code
* care about which bootloader was used.
*/
if (machine_is_csb337()) {
at91_emac_write(AT91_EMAC_SA2H,
(adr[0] << 8) | (adr[1]));
at91_emac_write(AT91_EMAC_SA2L,
(adr[2] << 24) | (adr[3] << 16)
| (adr[4] << 8) | (adr[5]));
} else {
at91_emac_write(AT91_EMAC_SA2L,
(adr[3] << 24) | (adr[2] << 16)
| (adr[1] << 8) | (adr[0]));
at91_emac_write(AT91_EMAC_SA2H,
(adr[5] << 8) | (adr[4]));
}
for (i = 0; i < 5; i++)
debug ("%02x:", adr[i]);
debug ("%02x\n", adr[5]);
return 0;
}
static int at91_ether_init(struct eth_device *edev)
{
return 0;
}
static int at91_ether_probe(struct device_d *dev)
{
unsigned int mac_cfg;
struct ether_device *ether_dev;
struct eth_device *edev;
struct mii_bus *miibus;
unsigned long ether_hz;
struct clk *pclk;
struct macb_platform_data *pdata;
if (!dev->platform_data) {
printf("at91_ether: no platform_data\n");
return -ENODEV;
}
pdata = dev->platform_data;
ether_dev = xzalloc(sizeof(struct ether_device));
edev = &ether_dev->netdev;
miibus = &ether_dev->miibus;
edev->priv = ether_dev;
edev->init = at91_ether_init;
edev->open = at91_ether_open;
edev->send = at91_ether_send;
edev->recv = at91_ether_rx;
edev->halt = at91_ether_halt;
edev->get_ethaddr = at91_ether_get_ethaddr;
edev->set_ethaddr = at91_ether_set_ethaddr;
ether_dev->rbf_framebuf = dma_alloc_coherent(MAX_RX_DESCR * MAX_RBUFF_SZ);
ether_dev->rbfdt = dma_alloc_coherent(sizeof(struct rbf_t) * MAX_RX_DESCR);
ether_dev->phy_addr = pdata->phy_addr;
miibus->read = at91_ether_mii_read;
miibus->write = at91_ether_mii_write;
/* Sanitize the clocks */
mac_cfg = at91_emac_read(AT91_EMAC_CFG);
pclk = clk_get(dev, "ether_clk");
clk_enable(pclk);
ether_hz = clk_get_rate(pclk);
if (ether_hz > 40000000) {
/* MDIO clock must not exceed 2.5 MHz, so enable MCK divider */
mac_cfg |= AT91_EMAC_CLK_DIV64;
} else {
mac_cfg &= ~AT91_EMAC_CLK;
}
mac_cfg |= AT91_EMAC_CLK_DIV32 | AT91_EMAC_BIG;
if (pdata->phy_interface == PHY_INTERFACE_MODE_RMII) {
ether_dev->interface = PHY_INTERFACE_MODE_RGMII;
mac_cfg |= AT91_EMAC_RMII;
} else {
ether_dev->interface = PHY_INTERFACE_MODE_MII;
}
at91_emac_write(AT91_EMAC_CFG, mac_cfg);
mdiobus_register(miibus);
eth_register(edev);
return 0;
}
static struct driver_d at91_ether_driver = {
.name = "at91_ether",
.probe = at91_ether_probe,
};
device_platform_driver(at91_ether_driver);