All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jens Scharsig <esw@bus-elektronik.de>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH] AT92RM9200 EMAC driver for NET MULTI API
Date: Thu, 08 Oct 2009 14:42:24 +0200	[thread overview]
Message-ID: <4ACDDE30.8030107@bus-elektronik.de> (raw)

This patch adds a new net api driver for AT91RM9200 EMAC

* adds NET_MULTI api EMAC (CONFIG_DRIVER_AT91EMAC)
* generic PHY initialization 

Signed-off-by: Jens Scharsig <esw@bus-elektronik.de>
---

This patch was tested with full MII interface and LXT971 Phy on our 
upcomming new board.
I have also tested the RMII interface with DM9161 Phy on a CARMEVA-board.

I have no u-boot supported AT91RM9200 boards. 

My appeal to all  AT91RM9200 boards maintainers: Please test

Changes to make your board use NET_MULTI api: 

  * include/configs/<board>.h
    1. change CONFIG_DRIVER_ETHER to CONFIG_DRIVER_AT91EMAC
    2. define CONFIG_NET_MULTI
    3. set CONFIG_SYS_RX_ETH_BUFFER, if you want more than standard rx buffers
    4. define CONFIG_AT91C_USE_RMII, if you board use RMII interface

  * add following code to board file
    #ifdef CONFIG_DRIVER_AT91EMAC
    int board_eth_init(bd_t *bis)
    {
	    int rc = 0;
	    rc = at91emac_register(bis, AT91C_BASE_EMAC);
	    return rc;
    }
    #endif
   
diff --git a/README b/README
index dee0e67..00d33be 100644
--- a/README
+++ b/README
@@ -817,6 +817,16 @@ The following options need to be configured:
 
 - NETWORK Support (other):
 
+		CONFIG_DRIVER_AT91EMAC
+		Support for AT91RM9200 EMAC.
+
+			CONFIG_RMII
+			Define this to use reduced MII inteface
+
+			CONFIG_DRIVER_AT91EMAC_QUIET
+			If this defined, the driver is quiet.
+			The driver doen't show link status messages.
+
 		CONFIG_DRIVER_LAN91C96
 		Support for SMSC's LAN91C96 chips.
 
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index fc9887b..3899052 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -27,6 +27,7 @@ LIB	:= $(obj)libnet.a
 
 COBJS-$(CONFIG_DRIVER_3C589) += 3c589.o
 COBJS-$(CONFIG_PPC4xx_EMAC) += 4xx_enet.o
+COBJS-$(CONFIG_DRIVER_AT91EMAC) += at91_emac.o
 COBJS-$(CONFIG_DRIVER_AX88180) += ax88180.o
 COBJS-$(CONFIG_BCM570x) += bcm570x.o bcm570x_autoneg.o 5701rls.o
 COBJS-$(CONFIG_BFIN_MAC) += bfin_mac.o
diff --git a/drivers/net/at91_emac.c b/drivers/net/at91_emac.c
new file mode 100644
index 0000000..269559c
--- /dev/null
+++ b/drivers/net/at91_emac.c
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) 2009 BuS Elektronik GmbH & Co. KG
+ * Jens Scharsig (esw at bus-elektronik.de)
+ *
+ * (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.
+ *
+ * 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 <asm/arch/hardware.h>
+#include <net.h>
+#include <netdev.h>
+#include <malloc.h>
+#include <miiphy.h>
+#include <linux/mii.h>
+
+#undef MII_DEBUG
+#undef ET_DEBUG
+
+#if (CONFIG_SYS_RX_ETH_BUFFER > 1024)
+#error AT91 EMAC supports max 1024 RX buffers. \
+	Please decrease the CONFIG_SYS_RX_ETH_BUFFER value
+#endif
+
+/* MDIO clock must not exceed 2.5 MHz, so enable MCK divider */
+#if (AT91C_MASTER_CLOCK > 80000000)
+	#define	HCLK_DIV AT91C_EMAC_CLK_HCLK_64
+#elif (AT91C_MASTER_CLOCK > 40000000)
+	#define	HCLK_DIV AT91C_EMAC_CLK_HCLK_32
+#elif (AT91C_MASTER_CLOCK > 20000000)
+	#define	HCLK_DIV AT91C_EMAC_CLK_HCLK_16
+#else
+	#define	HCLK_DIV AT91C_EMAC_CLK_HCLK_8
+#endif
+
+#ifdef ET_DEBUG
+#define DEBUG_AT91EAMC(...)	printf(__VA_ARGS__);
+#else
+#define DEBUG_AT91EAMC(...)
+#endif
+
+#ifdef MII_DEBUG
+#define DEBUG_AT91PHY(...)	printf(__VA_ARGS__);
+#else
+#define DEBUG_AT91PHY(...)
+#endif
+
+#ifndef CONFIG_DRIVER_AT91EMAC_QUIET
+#define VERBOSEP(...)	printf(__VA_ARGS__);
+#else
+#define VERBOSEP(...)
+#endif
+
+#define RBF_ADDR      0xfffffffc
+#define RBF_OWNER     (1<<0)
+#define RBF_WRAP      (1<<1)
+#define RBF_BROADCAST (1<<31)
+#define RBF_MULTICAST (1<<30)
+#define RBF_UNICAST   (1<<29)
+#define RBF_EXTERNAL  (1<<28)
+#define RBF_UNKOWN    (1<<27)
+#define RBF_SIZE      0x07ff
+#define RBF_LOCAL4    (1<<26)
+#define RBF_LOCAL3    (1<<25)
+#define RBF_LOCAL2    (1<<24)
+#define RBF_LOCAL1    (1<<23)
+
+#define RBF_FRAMEMAX CONFIG_SYS_RX_ETH_BUFFER
+#define RBF_FRAMELEN 0x600
+
+typedef struct {
+	unsigned long addr, size;
+} rbf_t;
+
+typedef struct {
+	rbf_t 		rbfdt[RBF_FRAMEMAX];
+	unsigned long	rbindex;
+} emac_device;
+
+void at91emac_EnableMDIO(AT91PS_EMAC at91mac)
+{
+	/* Mac CTRL reg set for MDIO enable */
+	at91mac->EMAC_CTL |= AT91C_EMAC_MPE;	/* Management port enable */
+}
+
+void at91emac_DisableMDIO(AT91PS_EMAC at91mac)
+{
+	/* Mac CTRL reg set for MDIO disable */
+	at91mac->EMAC_CTL &= ~AT91C_EMAC_MPE;	/* Management port disable */
+}
+
+int  at91emac_read(AT91PS_EMAC at91mac, unsigned char addr,
+		unsigned char reg, unsigned short *value)
+{
+	at91emac_EnableMDIO(at91mac);
+
+	at91mac->EMAC_MAN =	(AT91C_EMAC_HIGH & ~AT91C_EMAC_LOW) |
+				(AT91C_EMAC_RW_R) | (reg << 18) |
+				(AT91C_EMAC_CODE_802_3);
+	udelay(10000);
+	*value = (unsigned short) at91mac->EMAC_MAN;
+
+	at91emac_DisableMDIO(at91mac);
+
+	DEBUG_AT91PHY("AT91PHY read %x REG(%d)=%x\n", at91mac, reg, *value)
+
+	return 0;
+}
+
+int  at91emac_write(AT91PS_EMAC at91mac, unsigned char addr,
+		unsigned char reg, unsigned short value)
+{
+	DEBUG_AT91PHY("AT91PHY write %x REG(%d)=%x\n", at91mac, reg, &value)
+
+	at91emac_EnableMDIO(at91mac);
+	at91mac->EMAC_MAN =	(AT91C_EMAC_HIGH & ~AT91C_EMAC_LOW) |
+				(AT91C_EMAC_CODE_802_3) | AT91C_EMAC_RW_W |
+				(reg << 18) | value;
+
+	udelay(10000);
+
+	at91emac_DisableMDIO(at91mac);
+	return 0;
+}
+
+#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
+
+int  at91emac_mii_read(char *devname, unsigned char addr,
+		unsigned char reg, unsigned short *value)
+{
+	struct eth_device *netdev;
+	netdev = eth_get_dev_by_name(devname);
+	at91emac_read((AT91PS_EMAC) netdev->iobase, addr, reg, value);
+	return 0;
+}
+
+
+int  at91emac_mii_write(char *devname, unsigned char addr,
+		unsigned char reg, unsigned short value)
+{
+	struct eth_device *netdev;
+	netdev = eth_get_dev_by_name(devname);
+
+	at91emac_write((AT91PS_EMAC) netdev->iobase, addr, reg, value);
+
+	return 0;
+}
+
+#endif
+
+static int at91emac_phy_reset(struct eth_device *netdev)
+{
+	AT91PS_EMAC at91mac;
+
+	int i;
+	u16 status, adv;
+
+	at91mac = (AT91PS_EMAC) netdev->iobase;
+
+	adv = ADVERTISE_CSMA | ADVERTISE_ALL;
+	at91emac_write(at91mac, 0, MII_ADVERTISE, adv);
+	VERBOSEP("%s: Starting autonegotiation...\n", netdev->name);
+	at91emac_write(at91mac, 0, MII_BMCR, (BMCR_ANENABLE
+					 | BMCR_ANRESTART));
+
+	for (i = 0; i < 100000 / 100; i++) {
+		at91emac_read(at91mac, 0, MII_BMSR, &status);
+		if (status & BMSR_ANEGCOMPLETE)
+			break;
+		udelay(100);
+	}
+
+	if (status & BMSR_ANEGCOMPLETE) {
+		VERBOSEP("%s: Autonegotiation complete\n", netdev->name);
+	} else {
+		printf("%s: Autonegotiation timed out (status=0x%04x)\n",
+		       netdev->name, status);
+		return 1;
+	}
+	return 0;
+}
+
+static int at91emac_phy_init(struct eth_device *netdev)
+{
+	u16 phy_id, status, adv, lpa;
+	int media, speed, duplex;
+	int i;
+	AT91PS_EMAC at91mac;
+
+	at91mac = (AT91PS_EMAC) netdev->iobase;
+
+	/* Check if the PHY is up to snuff... */
+	at91emac_read(at91mac, 0, MII_PHYSID1, &phy_id);
+	if (phy_id == 0xffff) {
+		printf("%s: No PHY present\n", netdev->name);
+		return 1;
+	}
+
+	at91emac_read(at91mac, 0, MII_BMSR, &status);
+
+	if (!(status & BMSR_LSTATUS)) {
+		/* Try to re-negotiate if we don't have link already. */
+		if (at91emac_phy_reset(netdev))
+			return 2;
+
+		for (i = 0; i < 100000 / 100; i++) {
+			at91emac_read(at91mac, 0, MII_BMSR, &status);
+			if (status & BMSR_LSTATUS)
+				break;
+			udelay(100);
+		}
+	}
+	if (!(status & BMSR_LSTATUS)) {
+		VERBOSEP("%s: link down\n", netdev->name, status);
+		return 3;
+	} else {
+		at91emac_read(at91mac, 0, MII_ADVERTISE, &adv);
+		at91emac_read(at91mac, 0, MII_LPA, &lpa);
+		media = mii_nway_result(lpa & adv);
+		speed = (media & (ADVERTISE_100FULL | ADVERTISE_100HALF)
+			 ? 1 : 0);
+		duplex = (media & ADVERTISE_FULL) ? 1 : 0;
+		VERBOSEP("%s: link up, %sMbps %s-duplex\n",
+		       netdev->name,
+		       speed ? "100" : "10",
+		       duplex ? "full" : "half",
+		       lpa);
+	}
+	return 0;
+}
+
+int at91emac_UpdateLinkSpeed(AT91PS_EMAC at91mac)
+{
+	unsigned short stat1;
+
+	at91emac_read(at91mac, 0, MII_BMSR, &stat1);
+
+	if (!(stat1 & BMSR_LSTATUS))	/* link status up? */
+		return 1;
+
+	if (stat1 & BMSR_100FULL) {
+		/*set Emac for 100BaseTX and Full Duplex  */
+		at91mac->EMAC_CFG |= AT91C_EMAC_SPD | AT91C_EMAC_FD;
+		return 0;
+	}
+
+	if (stat1 & BMSR_10FULL) {
+		/*set MII for 10BaseT and Full Duplex  */
+		at91mac->EMAC_CFG = (at91mac->EMAC_CFG &
+				~(AT91C_EMAC_SPD | AT91C_EMAC_FD))
+				| AT91C_EMAC_FD;
+		return 0;
+	}
+
+	if (stat1 & BMSR_100HALF) {
+		/*set MII for 100BaseTX and Half Duplex  */
+		at91mac->EMAC_CFG = (at91mac->EMAC_CFG &
+				~(AT91C_EMAC_SPD | AT91C_EMAC_FD))
+				| AT91C_EMAC_SPD;
+		return 0;
+	}
+
+	if (stat1 & BMSR_10HALF) {
+		/*set MII for 10BaseT and Half Duplex  */
+		at91mac->EMAC_CFG &= ~(AT91C_EMAC_SPD | AT91C_EMAC_FD);
+		return 0;
+	}
+	return 1;
+}
+
+static int at91emac_init(struct eth_device *netdev, bd_t *bd)
+{
+	int i;
+	emac_device *emac;
+	AT91PS_EMAC at91mac;
+	at91mac = (AT91PS_EMAC) netdev->iobase;
+	emac	= (emac_device *) netdev->priv;
+
+	/* PIO Disable Register */
+	*AT91C_PIOA_PDR =
+		AT91C_PA16_EMDIO | AT91C_PA15_EMDC | AT91C_PA14_ERXER |
+		AT91C_PA13_ERX1 | AT91C_PA12_ERX0 | AT91C_PA11_ECRS_ECRSDV |
+		AT91C_PA10_ETX1 | AT91C_PA9_ETX0 |
+		AT91C_PA8_ETXEN | AT91C_PA7_ETXCK_EREFCK;
+	*AT91C_PIOA_ASR =
+		AT91C_PA16_EMDIO | AT91C_PA15_EMDC | AT91C_PA14_ERXER |
+		AT91C_PA13_ERX1 | AT91C_PA12_ERX0 | AT91C_PA11_ECRS_ECRSDV |
+		AT91C_PA10_ETX1 | AT91C_PA9_ETX0 |
+		AT91C_PA8_ETXEN | AT91C_PA7_ETXCK_EREFCK;
+#ifdef CONFIG_RMII
+	*AT91C_PIOB_PDR = AT91C_PB19_ERXCK;
+	*AT91C_PIOB_BSR = AT91C_PB19_ERXCK;
+#else
+	*AT91C_PIOB_PDR = AT91C_PB19_ERXCK | AT91C_PB18_ECOL |
+		AT91C_PB17_ERXDV | AT91C_PB16_ERX3 | AT91C_PB15_ERX2 |
+		AT91C_PB14_ETXER | AT91C_PB13_ETX3 | AT91C_PB12_ETX2;
+
+	*AT91C_PIOB_BSR = AT91C_PB19_ERXCK | AT91C_PB18_ECOL |
+		AT91C_PB17_ERXDV | AT91C_PB16_ERX3 | AT91C_PB15_ERX2 |
+		AT91C_PB14_ETXER | AT91C_PB13_ETX3 | AT91C_PB12_ETX2;
+#endif
+
+	*AT91C_PMC_PCER = 1 << AT91C_ID_EMAC;	/* Peripheral Clock Enable */
+	at91mac->EMAC_CFG |= AT91C_EMAC_CSR;	/* Clear statistics */
+
+	/* Init Ethernet buffers */
+	for (i = 0; i < RBF_FRAMEMAX; i++) {
+		emac->rbfdt[i].addr = (unsigned long) NetRxPackets[i];
+		emac->rbfdt[i].size = 0;
+	}
+	emac->rbfdt[RBF_FRAMEMAX - 1].addr |= RBF_WRAP;
+	emac->rbindex = 0;
+
+	at91mac->EMAC_SA2L = cpu_to_le32(*((u32 *)netdev->enetaddr));
+	at91mac->EMAC_SA2H = cpu_to_le16(*((u16 *)(netdev->enetaddr + 4)));
+	DEBUG_AT91EAMC("init MAC-ADDR %x%x \n",
+		at91mac->EMAC_SA2H, at91mac->EMAC_SA2L);
+
+	at91mac->EMAC_RBQP = (long) (&(emac->rbfdt[0]));
+	at91mac->EMAC_RSR &=
+		~(AT91C_EMAC_RSR_OVR | AT91C_EMAC_REC | AT91C_EMAC_BNA);
+
+	at91mac->EMAC_CFG =
+		(at91mac->EMAC_CFG | AT91C_EMAC_CAF | AT91C_EMAC_NBC)
+		& ~AT91C_EMAC_CLK;
+
+	at91mac->EMAC_CFG =
+		(at91mac->EMAC_CFG & ~AT91C_EMAC_CLK) |
+		AT91C_EMAC_CAF | AT91C_EMAC_NBC |
+		HCLK_DIV;
+
+#ifdef CONFIG_RMII
+	at91mac->EMAC_CFG |= AT91C_EMAC_RMII;
+#endif
+	at91mac->EMAC_CTL |= AT91C_EMAC_TE | AT91C_EMAC_RE;
+
+	if (!at91emac_phy_init(netdev)) {
+		at91emac_UpdateLinkSpeed(at91mac);
+		return 0;
+	}
+	return 1;
+}
+
+static void at91emac_halt(struct eth_device *netdev)
+{
+	AT91PS_EMAC at91mac;
+
+	at91mac = (AT91PS_EMAC) netdev->iobase;
+	at91mac->EMAC_CTL &= ~(AT91C_EMAC_TE | AT91C_EMAC_RE);
+	DEBUG_AT91EAMC("halt MAC-ADDR \n");
+}
+
+static int at91emac_send(struct eth_device *netdev, volatile void *packet,
+		     int length)
+{
+	AT91PS_EMAC at91mac;
+	at91mac = (AT91PS_EMAC) netdev->iobase;
+	while (!(at91mac->EMAC_TSR & AT91C_EMAC_BNQ))
+		;
+	at91mac->EMAC_TAR = (long) packet;
+	at91mac->EMAC_TCR = length;
+	while (at91mac->EMAC_TCR & 0x7ff)
+		;
+	DEBUG_AT91EAMC("Send %d \n", length);
+	at91mac->EMAC_TSR |= AT91C_EMAC_COMP;
+	return 0;
+}
+
+static int at91emac_recv(struct eth_device *netdev)
+{
+	emac_device *emac;
+	AT91PS_EMAC at91mac;
+	rbf_t *rbfp;
+	int size;
+
+	at91mac = (AT91PS_EMAC) netdev->iobase;
+	emac	= (emac_device *) netdev->priv;
+
+	rbfp = &emac->rbfdt[emac->rbindex];
+	while (rbfp->addr & RBF_OWNER)	{
+		size = rbfp->size & RBF_SIZE;
+		NetReceive(NetRxPackets[emac->rbindex], size);
+
+		DEBUG_AT91EAMC("Recv[%d]: %d bytes @ %x \n",
+			emac->rbindex, size, rbfp->addr);
+
+		rbfp->addr &= ~RBF_OWNER;
+		rbfp->size = 0;
+		if (emac->rbindex < (RBF_FRAMEMAX-1))
+			emac->rbindex++;
+		else
+			emac->rbindex = 0;
+
+		rbfp = &(emac->rbfdt[emac->rbindex]);
+		if (!(rbfp->addr & RBF_OWNER))
+			at91mac->EMAC_RSR |= AT91C_EMAC_REC;
+	}
+
+	if (at91mac->EMAC_ISR & AT91C_EMAC_RBNA) {
+		/* EMAC silicon bug 41.3.1 workaround 1 */
+		at91mac->EMAC_CTL &= ~AT91C_EMAC_RE;
+		at91mac->EMAC_CTL |= AT91C_EMAC_RE;
+		emac->rbindex = 0;
+		printf("%s: reset receiver (EMAC dead lock bug)\n",
+			netdev->name);
+	}
+	return 0;
+}
+
+int at91emac_register(bd_t *bis, unsigned long iobase)
+{
+	emac_device *emac;
+	emac_device *emacfix;
+	struct eth_device *dev;
+
+	emac = malloc(sizeof(*emac)+512);
+	if (emac == NULL)
+		return 1;
+	dev = malloc(sizeof(*dev));
+	if (dev == NULL) {
+		free(emac);
+		return 1;
+	}
+	/* alignment as per Errata (64 bytes) is insufficient! */
+	emacfix = (emac_device *) (((unsigned long) emac + 0x1ff) & 0xFFFFFE00);
+	memset(emacfix, 0, sizeof(emac_device));
+
+	memset(dev, 0, sizeof(*dev));
+#ifndef CONFIG_RMII
+	sprintf(dev->name, "AT91 EMAC");
+#else
+	sprintf(dev->name, "AT91 EMAC RMII");
+#endif
+	dev->iobase = iobase;
+	dev->priv = emacfix;
+	dev->init = at91emac_init;
+	dev->halt = at91emac_halt;
+	dev->send = at91emac_send;
+	dev->recv = at91emac_recv;
+
+	eth_register(dev);
+
+#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
+	miiphy_register(dev->name, at91emac_mii_read, at91emac_mii_write);
+#endif
+	return 1;
+}
+
diff --git a/include/netdev.h b/include/netdev.h
index a91368e..f771105 100644
--- a/include/netdev.h
+++ b/include/netdev.h
@@ -41,6 +41,7 @@ int board_eth_init(bd_t *bis);
 int cpu_eth_init(bd_t *bis);
 
 /* Driver initialization prototypes */
+int at91emac_register(bd_t *bis, unsigned long iobase);
 int au1x00_enet_initialize(bd_t*);
 int bfin_EMAC_initialize(bd_t *bis);
 int cs8900_initialize(u8 dev_num, int base_addr);

             reply	other threads:[~2009-10-08 12:42 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-10-08 12:42 Jens Scharsig [this message]
2009-11-22 22:36 ` [U-Boot] [PATCH] AT92RM9200 EMAC driver for NET MULTI API Wolfgang Denk
2009-12-06 19:36   ` Jens Scharsig
2009-12-07  3:33     ` Ben Warren

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=4ACDDE30.8030107@bus-elektronik.de \
    --to=esw@bus-elektronik.de \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.