2007-07-05 16:01:18 +00:00
|
|
|
/*
|
2012-03-30 04:47:52 +00:00
|
|
|
* Copyright (C) 2009-2012 Jean-Christophe PLAGNIOL-VILLARD <plagnio@jcrosoft.com>
|
|
|
|
*
|
2007-07-05 16:01:18 +00:00
|
|
|
* (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.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2012-03-30 04:47:52 +00:00
|
|
|
#include <common.h>
|
2007-07-05 16:01:18 +00:00
|
|
|
#include <net.h>
|
2012-03-30 04:47:52 +00:00
|
|
|
#include <clock.h>
|
2007-07-05 16:01:36 +00:00
|
|
|
#include <malloc.h>
|
2007-07-05 16:01:25 +00:00
|
|
|
#include <driver.h>
|
2012-03-30 04:47:52 +00:00
|
|
|
#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>
|
2012-08-09 07:49:51 +00:00
|
|
|
#include <linux/phy.h>
|
2012-03-30 04:47:52 +00:00
|
|
|
|
|
|
|
#include "at91_ether.h"
|
|
|
|
|
|
|
|
struct ether_device {
|
|
|
|
struct eth_device netdev;
|
2012-08-09 07:49:51 +00:00
|
|
|
struct mii_bus miibus;
|
2012-03-30 04:47:52 +00:00
|
|
|
struct rbf_t *rbfp;
|
|
|
|
struct rbf_t *rbfdt;
|
|
|
|
unsigned char *rbf_framebuf;
|
2012-08-09 07:49:51 +00:00
|
|
|
int phy_addr;
|
|
|
|
phy_interface_t interface;
|
2012-03-30 04:47:52 +00:00
|
|
|
};
|
|
|
|
#define to_ether(_nd) container_of(_nd, struct ether_device, netdev)
|
2007-07-05 16:01:18 +00:00
|
|
|
|
|
|
|
/*
|
2012-03-30 04:47:52 +00:00
|
|
|
* 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.
|
2007-07-05 16:01:18 +00:00
|
|
|
*/
|
2012-03-30 04:47:52 +00:00
|
|
|
static void enable_mdi(void)
|
2007-07-05 16:01:18 +00:00
|
|
|
{
|
2012-03-30 04:47:52 +00:00
|
|
|
unsigned long ctl;
|
|
|
|
|
|
|
|
ctl = at91_emac_read(AT91_EMAC_CTL);
|
|
|
|
at91_emac_write(AT91_EMAC_CTL, ctl | AT91_EMAC_MPE); /* enable management port */
|
2007-07-05 16:01:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2012-03-30 04:47:52 +00:00
|
|
|
* Disable the MDIO bit in the MAC control register
|
2007-07-05 16:01:18 +00:00
|
|
|
*/
|
2012-03-30 04:47:52 +00:00
|
|
|
static void disable_mdi(void)
|
2007-07-05 16:01:18 +00:00
|
|
|
{
|
2012-03-30 04:47:52 +00:00
|
|
|
unsigned long ctl;
|
2007-07-05 16:01:18 +00:00
|
|
|
|
2012-03-30 04:47:52 +00:00
|
|
|
ctl = at91_emac_read(AT91_EMAC_CTL);
|
|
|
|
at91_emac_write(AT91_EMAC_CTL, ctl & ~AT91_EMAC_MPE); /* disable management port */
|
|
|
|
}
|
2007-07-05 16:01:18 +00:00
|
|
|
|
|
|
|
/*
|
2012-03-30 04:47:52 +00:00
|
|
|
* Wait until the PHY operation is complete.
|
2007-07-05 16:01:18 +00:00
|
|
|
*/
|
2012-03-30 04:47:52 +00:00
|
|
|
static inline int at91_phy_wait(void)
|
2007-07-05 16:01:18 +00:00
|
|
|
{
|
2012-03-30 04:47:52 +00:00
|
|
|
uint64_t start;
|
2007-07-05 16:01:18 +00:00
|
|
|
|
2012-03-30 04:47:52 +00:00
|
|
|
start = get_time_ns();
|
2007-07-05 16:01:18 +00:00
|
|
|
|
2012-03-30 04:47:52 +00:00
|
|
|
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));
|
2007-07-05 16:01:18 +00:00
|
|
|
|
2012-03-30 04:47:52 +00:00
|
|
|
return 0;
|
2007-07-05 16:01:18 +00:00
|
|
|
}
|
|
|
|
|
2012-08-09 07:49:51 +00:00
|
|
|
static int at91_ether_mii_read(struct mii_bus *dev, int addr, int reg)
|
2007-07-05 16:01:18 +00:00
|
|
|
{
|
2012-03-30 04:47:52 +00:00
|
|
|
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;
|
2007-07-05 16:01:18 +00:00
|
|
|
|
2012-03-30 04:47:52 +00:00
|
|
|
out:
|
|
|
|
disable_mdi();
|
2007-07-05 16:01:18 +00:00
|
|
|
|
2012-03-30 04:47:52 +00:00
|
|
|
return value;
|
2007-07-05 16:01:18 +00:00
|
|
|
}
|
|
|
|
|
2012-08-09 07:49:51 +00:00
|
|
|
static int at91_ether_mii_write(struct mii_bus *dev, int addr, int reg, u16 val)
|
2007-07-05 16:01:22 +00:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
2012-03-30 04:47:52 +00:00
|
|
|
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));
|
2007-07-05 16:01:18 +00:00
|
|
|
|
2012-03-30 04:47:52 +00:00
|
|
|
/* Wait until IDLE bit in Network Status register is cleared */
|
|
|
|
ret = at91_phy_wait();
|
2007-07-05 16:01:18 +00:00
|
|
|
|
2012-03-30 04:47:52 +00:00
|
|
|
disable_mdi();
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2012-08-09 07:49:51 +00:00
|
|
|
static void update_linkspeed(struct eth_device *edev)
|
2012-03-30 04:47:52 +00:00
|
|
|
{
|
|
|
|
unsigned int mac_cfg;
|
|
|
|
|
|
|
|
/* Update the MAC */
|
|
|
|
mac_cfg = at91_emac_read(AT91_EMAC_CFG) & ~(AT91_EMAC_SPD | AT91_EMAC_FD);
|
2012-08-09 07:49:51 +00:00
|
|
|
if (edev->phydev->speed == SPEED_100) {
|
|
|
|
if (edev->phydev->duplex)
|
2012-03-30 04:47:52 +00:00
|
|
|
mac_cfg |= AT91_EMAC_SPD | AT91_EMAC_FD;
|
|
|
|
else /* 100 Half Duplex */
|
|
|
|
mac_cfg |= AT91_EMAC_SPD;
|
2007-07-05 16:01:18 +00:00
|
|
|
} else {
|
2012-08-09 07:49:51 +00:00
|
|
|
if (edev->phydev->duplex)
|
2012-03-30 04:47:52 +00:00
|
|
|
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;
|
2012-08-09 07:49:51 +00:00
|
|
|
int ret;
|
2012-03-30 04:47:52 +00:00
|
|
|
|
2012-08-09 07:49:51 +00:00
|
|
|
ret = phy_device_connect(edev, &etdev->miibus, etdev->phy_addr,
|
|
|
|
update_linkspeed, 0, etdev->interface);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
2012-03-30 04:47:52 +00:00
|
|
|
|
|
|
|
/* 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;
|
2007-07-05 16:01:18 +00:00
|
|
|
}
|
2012-03-30 04:47:52 +00:00
|
|
|
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);
|
|
|
|
|
2007-07-05 16:01:23 +00:00
|
|
|
return 0;
|
2007-07-05 16:01:18 +00:00
|
|
|
}
|
|
|
|
|
2012-03-30 04:47:52 +00:00
|
|
|
static int at91_ether_send(struct eth_device *edev, void *packet, int length)
|
2007-07-05 16:01:18 +00:00
|
|
|
{
|
2012-03-30 04:47:52 +00:00
|
|
|
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);
|
|
|
|
|
2007-07-05 16:01:18 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-03-30 04:47:52 +00:00
|
|
|
static int at91_ether_rx(struct eth_device *edev)
|
2007-07-05 16:01:18 +00:00
|
|
|
{
|
2012-03-30 04:47:52 +00:00
|
|
|
struct ether_device *etdev = to_ether(edev);
|
2007-07-05 16:01:18 +00:00
|
|
|
int size;
|
2012-03-30 04:47:52 +00:00
|
|
|
struct rbf_t *rbfp = etdev->rbfp;
|
2007-07-05 16:01:18 +00:00
|
|
|
|
|
|
|
if (!(rbfp->addr & RBF_OWNER))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
size = rbfp->size & RBF_SIZE;
|
2012-03-30 04:47:52 +00:00
|
|
|
|
|
|
|
net_receive((unsigned char *)(rbfp->addr & RBF_ADDR), size);
|
2007-07-05 16:01:18 +00:00
|
|
|
|
|
|
|
rbfp->addr &= ~RBF_OWNER;
|
|
|
|
if (rbfp->addr & RBF_WRAP)
|
2012-03-30 04:47:52 +00:00
|
|
|
etdev->rbfp = etdev->rbfdt;
|
2007-07-05 16:01:18 +00:00
|
|
|
else
|
2012-03-30 04:47:52 +00:00
|
|
|
etdev->rbfp++;
|
2007-07-05 16:01:18 +00:00
|
|
|
|
2012-03-30 04:47:52 +00:00
|
|
|
at91_emac_write(AT91_EMAC_RSR,
|
|
|
|
at91_emac_read(AT91_EMAC_RSR) | AT91_EMAC_RSR_REC);
|
2007-07-05 16:01:18 +00:00
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2012-03-30 04:47:52 +00:00
|
|
|
static void at91_ether_halt (struct eth_device *edev)
|
2007-07-05 16:01:18 +00:00
|
|
|
{
|
2012-03-30 04:47:52 +00:00
|
|
|
unsigned long ctl;
|
2007-07-05 16:01:18 +00:00
|
|
|
|
2012-03-30 04:47:52 +00:00
|
|
|
/* 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);
|
2007-07-05 16:01:18 +00:00
|
|
|
}
|
|
|
|
|
2012-03-30 04:47:52 +00:00
|
|
|
static int at91_ether_get_ethaddr(struct eth_device *eth, unsigned char *adr)
|
2007-07-05 16:01:22 +00:00
|
|
|
{
|
|
|
|
/* We have no eeprom */
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-03-30 04:47:52 +00:00
|
|
|
static int at91_ether_set_ethaddr(struct eth_device *eth, unsigned char *adr)
|
2007-07-05 16:01:22 +00:00
|
|
|
{
|
2007-07-05 16:01:36 +00:00
|
|
|
int i;
|
2007-07-05 16:01:23 +00:00
|
|
|
|
2012-03-30 04:47:52 +00:00
|
|
|
/* 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]));
|
|
|
|
}
|
2007-07-05 16:01:22 +00:00
|
|
|
|
2007-07-05 16:01:23 +00:00
|
|
|
for (i = 0; i < 5; i++)
|
2012-03-30 04:47:52 +00:00
|
|
|
debug ("%02x:", adr[i]);
|
|
|
|
debug ("%02x\n", adr[5]);
|
|
|
|
|
|
|
|
return 0;
|
2007-07-05 16:01:22 +00:00
|
|
|
}
|
|
|
|
|
2012-03-30 04:47:52 +00:00
|
|
|
static int at91_ether_init(struct eth_device *edev)
|
2007-07-05 16:01:36 +00:00
|
|
|
{
|
2012-03-30 04:47:52 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2007-07-05 16:01:36 +00:00
|
|
|
|
2012-03-30 04:47:52 +00:00
|
|
|
static int at91_ether_probe(struct device_d *dev)
|
|
|
|
{
|
|
|
|
unsigned int mac_cfg;
|
|
|
|
struct ether_device *ether_dev;
|
|
|
|
struct eth_device *edev;
|
2012-08-09 07:49:51 +00:00
|
|
|
struct mii_bus *miibus;
|
2012-03-30 04:47:52 +00:00
|
|
|
unsigned long ether_hz;
|
|
|
|
struct clk *pclk;
|
2013-03-15 23:26:00 +00:00
|
|
|
struct macb_platform_data *pdata;
|
2012-03-30 04:47:52 +00:00
|
|
|
|
|
|
|
if (!dev->platform_data) {
|
|
|
|
printf("at91_ether: no platform_data\n");
|
|
|
|
return -ENODEV;
|
2007-07-05 16:01:36 +00:00
|
|
|
}
|
|
|
|
|
2012-03-30 04:47:52 +00:00
|
|
|
pdata = dev->platform_data;
|
|
|
|
|
|
|
|
ether_dev = xzalloc(sizeof(struct ether_device));
|
|
|
|
|
|
|
|
edev = ðer_dev->netdev;
|
2012-08-09 07:49:51 +00:00
|
|
|
miibus = ðer_dev->miibus;
|
2012-03-30 04:47:52 +00:00
|
|
|
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);
|
|
|
|
|
2012-08-09 07:49:51 +00:00
|
|
|
ether_dev->phy_addr = pdata->phy_addr;
|
|
|
|
miibus->read = at91_ether_mii_read;
|
|
|
|
miibus->write = at91_ether_mii_write;
|
2012-03-30 04:47:52 +00:00
|
|
|
|
|
|
|
/* 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;
|
|
|
|
}
|
2007-07-05 16:01:36 +00:00
|
|
|
|
2012-03-30 04:47:52 +00:00
|
|
|
mac_cfg |= AT91_EMAC_CLK_DIV32 | AT91_EMAC_BIG;
|
2007-07-05 16:01:36 +00:00
|
|
|
|
2013-01-28 22:27:27 +00:00
|
|
|
if (pdata->phy_interface == PHY_INTERFACE_MODE_RMII) {
|
2012-08-09 07:49:51 +00:00
|
|
|
ether_dev->interface = PHY_INTERFACE_MODE_RGMII;
|
2012-03-30 04:47:52 +00:00
|
|
|
mac_cfg |= AT91_EMAC_RMII;
|
2012-08-09 07:49:51 +00:00
|
|
|
} else {
|
|
|
|
ether_dev->interface = PHY_INTERFACE_MODE_MII;
|
|
|
|
}
|
2007-07-05 16:01:36 +00:00
|
|
|
|
2012-03-30 04:47:52 +00:00
|
|
|
at91_emac_write(AT91_EMAC_CFG, mac_cfg);
|
2007-07-05 16:01:36 +00:00
|
|
|
|
2012-08-09 07:49:51 +00:00
|
|
|
mdiobus_register(miibus);
|
2007-07-05 16:01:36 +00:00
|
|
|
eth_register(edev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2007-07-05 16:01:22 +00:00
|
|
|
|
2012-03-30 04:47:52 +00:00
|
|
|
static struct driver_d at91_ether_driver = {
|
|
|
|
.name = "at91_ether",
|
|
|
|
.probe = at91_ether_probe,
|
|
|
|
};
|
2013-02-12 18:08:57 +00:00
|
|
|
device_platform_driver(at91_ether_driver);
|