[U-Boot] [patch V3] [2/3] ip3912 network driver

Jürgen Schöw juergen at Schoew.net
Sun Jan 4 22:56:16 CET 2009


 drivers/net/Makefile               |    1 +
 drivers/net/ip3912.c               |  659 ++++++++++++++++++++++++++++++++++++
 drivers/net/ip3912.h               |  174 ++++++++++

diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 631336a..7c82880 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -69,6 +69,7 @@ COBJS-$(CONFIG_VSC7385_ENET) += vsc7385.o
 COBJS-$(CONFIG_XILINX_EMAC) += xilinx_emac.o
 COBJS-$(CONFIG_XILINX_EMACLITE) += xilinx_emaclite.o
 COBJS-$(CONFIG_SH_ETHER) += sh_eth.o
+COBJS-$(CONFIG_IP3912_ETHER) += ip3912.o
 
 COBJS	:= $(COBJS-y)
 SRCS	:= $(COBJS:.o=.c)
diff --git a/drivers/net/ip3912.c b/drivers/net/ip3912.c
new file mode 100644
index 0000000..399f0b7
--- /dev/null
+++ b/drivers/net/ip3912.c
@@ -0,0 +1,659 @@
+/*
+ * ip3912 ethernet driver (PNX8181 / firetux)
+ *
+ * (C) Copyright 2007-2009, emlix GmbH, Germany
+ * Juergen Schoew <js at emlix.com>
+ *
+ * (C) Copyright 2008, DSPG Technologies GmbH, Germany
+ * (C) Copyright 2007, NXP Semiconductors Germany GmbH
+ * Matthias Wenzel, <nxp at mazzoo.de>
+ *
+ * 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 <net.h>
+#include <malloc.h>
+
+#include <asm/io.h>
+
+#include "ip3912.h"
+#include <miiphy.h>
+
+#define ALIGN8	__attribute__ ((aligned(8)))
+#define ALIGN4	__attribute__ ((aligned(4)))
+
+/* globals */
+/* ETN rx */
+ALIGN8 rx_descriptor_t	etn_rxdescriptor[CONFIG_SYS_ETN_RX_DESCRIPTOR_NUMBER];
+ALIGN8 rx_status_t	etn_rxstatus[CONFIG_SYS_ETN_RX_DESCRIPTOR_NUMBER];
+
+/* ETN tx */
+ALIGN8 tx_descriptor_t	etn_txdescriptor[CONFIG_SYS_ETN_TX_DESCRIPTOR_NUMBER];
+ALIGN4 tx_status_t	etn_txstatus[CONFIG_SYS_ETN_TX_DESCRIPTOR_NUMBER];
+
+struct ip3912_device {
+	unsigned int		etn_base;
+	unsigned int		phy_base;
+	unsigned char		nr;
+	unsigned char		phy_addr;
+	unsigned char		autonegotiate;
+	unsigned char		speed;
+	unsigned char		duplex;
+	unsigned char		rmii;
+
+	const struct device	*dev;
+	struct eth_device	*netdev;
+};
+
+int ip3912_miiphy_write(char *devname, unsigned char addr,
+				unsigned char reg, unsigned short value)
+{
+	int status = 1;
+	int i = 0;
+	struct eth_device *netdev;
+	struct ip3912_device *ip3912;
+
+	netdev = eth_get_dev();
+	ip3912 = netdev->priv;
+
+	reg &= 0x001f; /* 5 bit PHY register address */
+
+	writel(PHYADDR_TO_REG(addr) | reg, (void *)(ip3912->phy_base
+								+ ETN_MADR));
+	writel(value, (void *)(ip3912->phy_base + ETN_MWTD));
+
+	/* poll for done, max 100ms */
+	while (status && i < 100000) {
+		status = readl((void *)(ip3912->phy_base + ETN_MIND)) & 0x7;
+		udelay(1);
+		i++;
+	}
+
+	if (status) {
+		printf("ERROR: ip3912_miiphy_write(%d) = "
+				"0x%x [phy_addr=%x]\n", reg, status, addr);
+		return -1;
+	} else {
+		debug("### ip3912_miiphy_write(%2.d, 0x%4.4x) success after"
+			" %d cycles [phy_addr=%x]###\n", reg, value, i, addr);
+	}
+
+	return 0;
+}
+
+int ip3912_miiphy_read(char *devname, unsigned char addr,
+				unsigned char reg, unsigned short *value)
+{
+	int i = 0, status = 1;
+	struct eth_device *netdev;
+	struct ip3912_device *ip3912;
+
+	netdev = eth_get_dev();
+	ip3912 = netdev->priv;
+
+	reg &= 0x001f; /* 5 bit PHY register address */
+	writel(PHYADDR_TO_REG(addr) | reg, (void *)(ip3912->phy_base
+								+ ETN_MADR));
+	writel(0x00000001, (void *)(ip3912->phy_base + ETN_MCMD));
+
+	/* poll for done, max 100ms */
+	while (status && i < 100000) {
+		status = readl((void *)(ip3912->phy_base + ETN_MIND)) & 0x7;
+		udelay(1);
+		i++;
+	}
+
+	*value = (unsigned short)readl((void *)(ip3912->phy_base + ETN_MRDD));
+
+	writel(0, (void *)(ip3912->phy_base + ETN_MCMD));	/* stop MII */
+
+	if (status) {
+		printf("ERROR: ip3912_miiphy_read(%d) = 0x%x after %d cycles "
+			"[phy_addr=%x]\n", reg, *value, i, ip3912->phy_addr);
+		return -1;
+	} else {
+		debug("### ip3912_phy_read(%2.d)=0x%4.4x success after %d "
+					"cycles [phy_addr=%x]###\n",
+				reg, *value, i, ip3912->phy_addr);
+	}
+
+	return 0;
+}
+
+#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
+int ip3912_mii_negotiate_phy(void)
+{
+	char *mode;
+	int i;
+	unsigned short value;
+	struct eth_device *netdev;
+	struct ip3912_device *ip3912;
+
+	netdev = eth_get_dev();
+	ip3912 = netdev->priv;
+
+	/* only set phy if exists */
+	ip3912_miiphy_read(netdev->name, ip3912->phy_addr,
+							PHY_PHYIDR1, &value);
+	if (value  == 0xffff)
+		return -1;
+
+	/* get mode from environment */
+	mode = getenv("phymode");
+	if (mode  != NULL) {
+		if (0 == strcmp(mode, "auto")) {
+			ip3912->autonegotiate = 1;
+			ip3912->speed = 100;
+			ip3912->duplex = 1;
+		} else {
+			if (0 == strcmp(mode, "100FD")) {
+				ip3912->speed = 100;
+				ip3912->duplex = 1;
+			}
+			if (0 == strcmp(mode, "100HD")) {
+				ip3912->speed = 100;
+				ip3912->duplex = 0;
+			}
+			if (0 == strcmp(mode, "10FD")) {
+				ip3912->speed = 10;
+				ip3912->duplex = 1;
+			}
+			if (0 == strcmp(mode, "10HD")) {
+				ip3912->speed = 10;
+				ip3912->duplex = 0;
+			}
+			ip3912->autonegotiate = 0;
+		}
+	} else {
+		/* we use 10Mbit FD as fallback */
+		ip3912->autonegotiate = 0;
+		ip3912->speed = 10;
+		ip3912->duplex = 1;
+	}
+
+	/* do autonegotiation */
+	if (ip3912->autonegotiate) {
+		/* 10/100 and FD/HD mode supported, ieee802.3 */
+		ip3912_miiphy_write(netdev->name, ip3912->phy_addr,
+				ETN_PHY_AUTONEG_ADV, ((0xf << 5) | 1));
+		/* force autorenegotiation */
+		ip3912_miiphy_write(netdev->name, ip3912->phy_addr,
+				ETN_PHY_BASIC_CONTROL, ((1 << 13) | (1 << 12) |
+							 (1 << 9) | (1 << 8)));
+	} else {
+		/* only advertise the selected mode */
+		i = 0x1e0;
+		if (ip3912->speed == 100)
+			i &= 0x180;
+		else
+			i &= 0x060;
+		if (ip3912->duplex)
+			i &= 0x140;
+		else
+			i &= 0x0a0;
+		/* set advertise mode */
+		ip3912_miiphy_write(netdev->name, ip3912->phy_addr,
+						ETN_PHY_AUTONEG_ADV, (i|1));
+		/* we set the phy parameter */
+		ip3912_miiphy_write(netdev->name, ip3912->phy_addr,
+			ETN_PHY_BASIC_CONTROL, ((ip3912->duplex ? (1<<8) : 0)
+			| (1 << 9) | ((ip3912->speed == 100) ? (1 << 13) : 0)));
+	}
+
+	/* wait for negotiation finished (max 3.5s) */
+	i = 0;
+	ip3912_miiphy_read(netdev->name, ip3912->phy_addr, ETN_PHY_BASIC_STATUS,
+									&value);
+	while (((value & (1 << 5)) == 0) && (i < 350)) {
+		udelay(10000);
+		i++;
+		ip3912_miiphy_read(netdev->name, ip3912->phy_addr,
+						 ETN_PHY_BASIC_STATUS, &value);
+	}
+	if (i == 350)
+		puts("link negotiation timed out\n");
+
+
+	/* check for link */
+	if (value & (1 << 2)) {
+		/* OK link present */
+		ip3912_miiphy_read(netdev->name, ip3912->phy_addr,
+				ETN_PHY_SPECIAL_MODE_CONTROL_STATUS, &value);
+		ip3912->speed = (value & (1 << 2)) ? 10 : 100;
+		ip3912->duplex = (value & (1 << 4)) ? 1 : 0;
+	}
+
+	/* program the mac */
+	writel((readl((void *)(ip3912->etn_base + ETN_SUPP)) & 0x000018fb) |
+					((ip3912->speed == 100) ? (1 << 8) : 0),
+					(void *)(ip3912->etn_base + ETN_SUPP));
+	writel((readl((void *)(ip3912->etn_base + ETN_MAC2)) & 0x000073fe) |
+			ip3912->duplex,	(void *)(ip3912->etn_base + ETN_MAC2));
+	/*
+	 * release rx-path, tx-path, host registers reset
+	 * set Duplex, enable RMII, enable rx+tx
+	 * no flow control, no frames<64b
+	 */
+	writel(0x00000283 | (ip3912->duplex ? (1 << 10) : 0),
+				(void *)(ip3912->etn_base + ETN_COMMAND));
+
+	udelay(100000);	/* the mac still needs some time to settle 100ms */
+	ip3912_miiphy_read(netdev->name, ip3912->phy_addr,
+				ETN_PHY_SPECIAL_MODE_CONTROL_STATUS, &value);
+	printf(" %s %s negotiation", netdev->name,
+				(value & (1 << 12)) ? "Auto" : "Manual");
+	printf(" (10%s Mbit", (value & (1 << 3)) ? "0" : "");
+	printf(" %sD", (value & (1 << 4)) ? "F" : "H");
+	ip3912_miiphy_read(netdev->name, ip3912->phy_addr, ETN_PHY_BASIC_STATUS,
+									&value);
+	printf(" (%s)\n", (value & 1<<2)
+			? "Link detected" : "No Link detected, trying anyway");
+
+	return 0;
+}
+
+#if defined(CONFIG_DISCOVER_PHY)
+int mii_discover_phy(void)
+{
+	unsigned short id1, id2;
+	int phytype, phyno;
+	struct eth_device *netdev;
+	struct ip3912_device *ip3912;
+
+	netdev = eth_get_dev();
+	ip3912 = netdev->priv;
+
+	for (phyno = 0; phyno <= 31 ; ++phyno) {
+		ip3912_miiphy_read(netdev->name, phyno, PHY_PHYIDR1, &id1);
+		if (id1 != 0xffff) {
+			ip3912_miiphy_read(netdev->name, phyno, PHY_PHYIDR2,
+								&id2);
+			phytype = ((id1 << 16) | id2);
+			puts("Using Transceiver: ");
+			switch (phytype & 0xfffffff0) {
+			case PHY_ID_LXT970:
+						puts("LXT970");
+						break;
+			case PHY_ID_LXT971:
+						puts("LXT971");
+						break;
+			case PHY_ID_82555:
+						puts("82555");
+						break;
+			case PHY_ID_QS6612:
+						puts("QS6612");
+						break;
+			case PHY_ID_AMD79C784:
+						puts("AMD79C784");
+						break;
+			case PHY_ID_LSI80225:
+						puts("LSI L80225");
+						break;
+			case PHY_ID_LSI80225B:
+						puts("LSI L80225/B");
+						break;
+			case PHY_ID_DM9161:
+						puts("Davicom DM9161");
+						break;
+			case PHY_ID_KSM8995M:
+						puts("MICREL KS8995M");
+						break;
+			case PHY_ID_SMSC8700:
+						puts("SMSC Lan 8700");
+						break;
+			default:
+						printf("0x%08x", phytype);
+						break;
+		}
+	    }
+	}
+
+	return 0;
+}
+#endif /* CONFIG_DISCOVER_PHY */
+
+#endif /* defined(CONFIG_MII) || defined(CONFIG_CMD_MII) */
+
+int ip3912_miiphy_initialize(bd_t *bis)
+{
+#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
+	miiphy_register("ip3912", ip3912_miiphy_read, ip3912_miiphy_write);
+#endif
+	return 0;
+}
+
+static int ip3912_init_descriptors(struct eth_device *netdev)
+{
+	struct ip3912_device *ip3912;
+	static void *rxbuf;
+	int i;
+
+	ip3912 = netdev->priv;
+
+	/* fill in pointer in regs */
+	writel((unsigned long)etn_rxdescriptor, (void *)(ip3912->etn_base
+							+ ETN_RXDESCRIPTOR));
+	writel((unsigned long)etn_rxstatus, (void *)(ip3912->etn_base
+							+ ETN_RXSTATUS));
+	writel(0x00000000, (void *)(ip3912->etn_base + ETN_RXCONSUMEINDEX));
+	writel(CONFIG_SYS_ETN_RX_DESCRIPTOR_NUMBER - 1,
+				(void *)(ip3912->etn_base
+						+ ETN_RXDESCRIPTORNUMBER));
+
+	writel((unsigned long)etn_txdescriptor,	(void *)(ip3912->etn_base
+							+ ETN_TXDESCRIPTOR));
+	writel((unsigned long)etn_txstatus, (void *)(ip3912->etn_base
+							+ ETN_TXSTATUS));
+	writel(0x00000000, (void *)(ip3912->etn_base + ETN_TXPRODUCEINDEX));
+	writel(CONFIG_SYS_ETN_TX_DESCRIPTOR_NUMBER - 1,
+			(void *)(ip3912->etn_base + ETN_TXDESCRIPTORNUMBER));
+
+	/* allocate rx-buffers, but only once, we're called multiple times! */
+	if (!rxbuf)
+		rxbuf = malloc(MAX_ETH_FRAME_SIZE
+					* CONFIG_SYS_ETN_RX_DESCRIPTOR_NUMBER);
+	if (!rxbuf) {
+		puts("ERROR: couldn't allocate rx buffers!\n");
+		return -1;
+	}
+
+	for (i = 0; i < CONFIG_SYS_ETN_RX_DESCRIPTOR_NUMBER; i++) {
+		etn_rxdescriptor[i].packet = rxbuf + i * MAX_ETH_FRAME_SIZE;
+		etn_rxdescriptor[i].control = MAX_ETH_FRAME_SIZE
+						- sizeof(unsigned long);
+		etn_rxstatus[i].info = 0;
+		etn_rxstatus[i].hashCRC = 0;
+	}
+
+	for (i = 0; i < CONFIG_SYS_ETN_TX_DESCRIPTOR_NUMBER; i++) {
+		etn_txdescriptor[i].packet = 0;
+		etn_txdescriptor[i].control = 0;
+		etn_txstatus[i].info = 0;
+	}
+	return 0;
+}
+
+void ip3912_setmac(struct eth_device *netdev)
+{
+	struct ip3912_device *ip3912;
+	unsigned char i, use_etn1addr = 0;
+	char *mac_string, *pmac, *end;
+	char tmp[18];
+
+	ip3912 = netdev->priv;
+
+	mac_string = getenv("ethaddr");
+
+	if (ip3912->nr) {
+		/* we use ETN2 */
+		mac_string = getenv("eth1addr");
+		if (!mac_string) {
+			mac_string = getenv("ethaddr");
+			use_etn1addr = 1;
+		}
+	}
+
+	pmac = mac_string;
+	for (i = 0; i < 6; i++) {
+		netdev->enetaddr[i] = pmac ? simple_strtoul(pmac, &end, 16) : 0;
+		if (pmac)
+			pmac = (*end) ? end + 1 : end;
+	}
+
+	if (use_etn1addr) {
+		/* flip last bit of mac address */
+		debug("ip3912_setmac %s flipping last bit\n", netdev->name);
+		if (netdev->enetaddr[5] & 1)
+			netdev->enetaddr[5] &= 0xfe;
+		else
+			netdev->enetaddr[5] |= 0x01;
+		sprintf(tmp, "%02X:%02X:%02X:%02X:%02X:%02X",
+			 netdev->enetaddr[0], netdev->enetaddr[1],
+			 netdev->enetaddr[2], netdev->enetaddr[3],
+			 netdev->enetaddr[4], netdev->enetaddr[5]);
+		setenv("eth1addr", tmp);
+		mac_string = tmp;
+	}
+
+	debug("ip3912_setmac set %s to address %s\n", netdev->name, mac_string);
+
+	writel((netdev->enetaddr[5] << 8) | netdev->enetaddr[4],
+					(void *)(ip3912->etn_base + ETN_SA0));
+	writel((netdev->enetaddr[3] << 8) | netdev->enetaddr[2],
+					(void *)(ip3912->etn_base + ETN_SA1));
+	writel((netdev->enetaddr[1] << 8) | netdev->enetaddr[0],
+					(void *)(ip3912->etn_base + ETN_SA2));
+}
+
+int ip3912_macreset(void)
+{
+	struct eth_device *netdev;
+	struct ip3912_device *ip3912;
+
+	netdev = eth_get_dev();
+	ip3912 = netdev->priv;
+
+	debug("ip3912_macreset resetting %s\n", netdev->name);
+
+	/* reset MAC layer */
+	writel(0x0000cf00, (void *)(ip3912->etn_base + ETN_MAC1));
+	/* release MAC soft reset */
+	writel(0x00000000, (void *)(ip3912->etn_base + ETN_MAC1));
+	/* reset rx-path, tx-path, host registers */
+	writel(0x00000038, (void *)(ip3912->etn_base + ETN_COMMAND));
+	/* reset RMII, 100Mbps MAC, 10Mbps MAC */
+	writel(0x1888, (void *)(ip3912->etn_base + ETN_SUPP));
+	writel(0x1000, (void *)(ip3912->etn_base + ETN_SUPP));
+
+	return 0;
+}
+
+static int ip3912_init(struct eth_device *netdev, bd_t *bd)
+{
+	unsigned char i;
+	struct ip3912_device *ip3912 = netdev->priv;
+
+	/* update mac address in boardinfo */
+	ip3912_setmac(netdev);
+	for (i = 0; i < 6 ; i++)
+		bd->bi_enetaddr[i] = netdev->enetaddr[i];
+
+	/* before enabling the rx-path we need to set up rx-descriptors */
+	if (ip3912_init_descriptors(netdev))
+		return -1;
+
+	/* set max packet length to 1536 bytes */
+	writel(MAX_ETH_FRAME_SIZE, (void *)(ip3912->etn_base + ETN_MAXF));
+	/* full duplex */
+	writel(0x00000023, (void *)(ip3912->etn_base + ETN_MAC2));
+	/* inter packet gap register */
+	writel(0x15, (void *)(ip3912->etn_base + ETN_IPGT));
+	writel(0x12, (void *)(ip3912->etn_base + ETN_IPGR));
+	/* enable rx, receive all frames */
+	writel(0x00000003, (void *)(ip3912->etn_base + ETN_MAC1));
+	/* accept all multicast, broadcast and station packets */
+	writel(0x00000026, (void *)(ip3912->etn_base + ETN_RXFILTERCTRL));
+
+	/* reset MII mgmt, set MII clock */
+	writel(0x0000801c, (void *)(ip3912->etn_base + ETN_MCFG));
+	writel(0x0000001c, (void *)(ip3912->etn_base + ETN_MCFG));
+
+	/* release rx-path, tx-path, host registers reset
+	 * set FullDuplex, enable RMMI, enable rx+tx
+	 * no flow control, no frames<64b
+	 */
+	writel(0x00000683, (void *)(ip3912->etn_base + ETN_COMMAND));
+	ip3912_init_descriptors(netdev);
+
+#ifdef CONFIG_DISCOVER_PHY
+	mii_discover_phy();
+#endif
+#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
+	/* check autonegotiation */
+	ip3912_mii_negotiate_phy();
+#endif
+	return 0;
+}
+
+/* Send a packet */
+static int ip3912_send(struct eth_device *netdev, volatile void *packet,
+								int length)
+{
+	struct ip3912_device *ip3912 = netdev->priv;
+	uint32_t next_packet;
+	uint32_t this_packet;
+	uint32_t last_packet;
+
+	if ((length > MAX_ETH_FRAME_SIZE) || (length <= 0)) {
+		printf("ERROR: cannot transmit a %d bytes frame!\n", length);
+		return -1;
+	}
+
+	this_packet = readl((void *)(ip3912->etn_base + ETN_TXPRODUCEINDEX));
+	next_packet = (this_packet + 1) % CONFIG_SYS_ETN_TX_DESCRIPTOR_NUMBER;
+	last_packet = readl((void *)(ip3912->etn_base + ETN_TXCONSUMEINDEX));
+
+#define ETN_TX_MAX_RETRY	1000000
+	int i = 0;
+	/* wait until the FIFO is ready to accept a new packet */
+
+	while ((this_packet == ((last_packet +
+					CONFIG_SYS_ETN_TX_DESCRIPTOR_NUMBER - 1)
+					% CONFIG_SYS_ETN_TX_DESCRIPTOR_NUMBER))
+						&& (i < ETN_TX_MAX_RETRY)) {
+#ifdef ET_DEBUG
+		/* debug print when FIFO full*/
+		if ((i > 50000) && (!(i % 50000))) {
+			this_packet =
+				readl((void *)(ip3912->etn_base
+							+ ETN_TXPRODUCEINDEX));
+			last_packet =
+				readl((void *)(ip3912->etn_base
+							+ ETN_TXCONSUMEINDEX));
+			printf("this=%3.d, last=%3.d, i=%d\n",
+						this_packet, last_packet, i);
+		}
+#endif
+		i++;
+		last_packet = readl((void *)(ip3912->etn_base
+							+ ETN_TXCONSUMEINDEX));
+	}
+	if (i == ETN_TX_MAX_RETRY) {
+		printf("tx FAILED after %d cycles\n", i);
+		return -1;
+	}
+	if (i)
+		printf("tx after %d cycles\n", i);
+
+	etn_txdescriptor[this_packet].packet  = packet;
+	etn_txdescriptor[this_packet].control = (length - 1) |
+		ETN_CONTROL_INTERRUPT | ETN_CONTROL_LAST;
+
+	/* let the HW know a new packet is ready */
+	writel(next_packet, (void *)(ip3912->etn_base + ETN_TXPRODUCEINDEX));
+
+	return 0;
+}
+
+/* Check for received packets */
+static int ip3912_recv(struct eth_device *netdev)
+{
+	struct ip3912_device *ip3912 = netdev->priv;
+
+	unsigned short rxconsume = (unsigned short)
+		(readl((void *)(ip3912->etn_base + ETN_RXCONSUMEINDEX)));
+	unsigned short rxproduce = (unsigned short)
+		(readl((void *)(ip3912->etn_base + ETN_RXPRODUCEINDEX)));
+	unsigned short psize = 0;
+
+	debug("eth_rx: receive_rsv 0x%08x\n",
+				readl((void *)(ip3912->etn_base + ETN_RSV)));
+	debug("eth_rx: consume 0x%04x produce 0x%04x\n",
+							rxconsume, rxproduce);
+	while (rxconsume != rxproduce) {
+		rxproduce = (unsigned short)(readl((void *)
+				(ip3912->etn_base + ETN_RXPRODUCEINDEX)));
+		psize = (etn_rxstatus[rxconsume].info & 0x07ff) + 1;
+		if (psize > MAX_ETH_FRAME_SIZE) {
+			printf("dropping %d bytes frame (too large)!\n",
+									psize);
+		} else {
+			NetReceive(etn_rxdescriptor[rxconsume].packet, psize);
+		}
+		rxconsume = (rxconsume + 1)
+					% CONFIG_SYS_ETN_RX_DESCRIPTOR_NUMBER;
+		writel(rxconsume,
+			(void *)(ip3912->etn_base + ETN_RXCONSUMEINDEX));
+	}
+	return psize;
+}
+
+static void ip3912_halt(struct eth_device *netdev)
+{
+	struct ip3912_device *ip3912 = netdev->priv;
+
+	/* disable rx-path, tx-path, host registers reset
+	 * set FullDuplex, enable RMMI, disable rx+tx
+	 * no flow control, no frames<64b
+	 */
+	writel(0x000006b8, (void *)(ip3912->etn_base + ETN_COMMAND));
+}
+
+int ip3912_eth_initialize(unsigned char nr, unsigned int etn_base,
+	unsigned int phy_base, unsigned char phy_addr, unsigned char rmii)
+{
+	struct ip3912_device *ip3912;
+	struct eth_device *netdev;
+
+	netdev = malloc(sizeof(struct eth_device));
+	ip3912 = malloc(sizeof(struct ip3912_device));
+	if ((!ip3912) || (!netdev)) {
+		printf("Error: Failed to allocate memory for ETN%d\n", nr + 1);
+		return -1;
+	}
+
+	memset(ip3912, 0, sizeof(struct ip3912_device));
+	memset(netdev, 0, sizeof(struct eth_device));
+
+	ip3912->nr = nr;
+	ip3912->etn_base = etn_base;
+	ip3912->phy_base = phy_base;
+	ip3912->phy_addr = phy_addr;
+	ip3912->autonegotiate = 0;
+	ip3912->rmii = rmii;
+	ip3912->speed = 0;
+	ip3912->duplex = 0;
+	ip3912->netdev = netdev;
+
+	sprintf(netdev->name, "ETN%d", nr + 1);
+	netdev->init = ip3912_init;
+	netdev->send = ip3912_send;
+	netdev->recv = ip3912_recv;
+	netdev->halt = ip3912_halt;
+	netdev->priv = (void *)ip3912;
+
+
+	eth_register(netdev);
+	ip3912_macreset();
+	/* we have to set the mac address, because we have no SROM */
+	ip3912_setmac(netdev);
+
+	return 0;
+}
diff --git a/drivers/net/ip3912.h b/drivers/net/ip3912.h
new file mode 100644
index 0000000..f6343a0
--- /dev/null
+++ b/drivers/net/ip3912.h
@@ -0,0 +1,174 @@
+/*
+ * ip3912 ethernet driver interface (PNX8181 / firetux)
+ *
+ * (C) Copyright 2007-2009, emlix GmbH, Germany
+ * Juergen Schoew <js at emlix.com>
+ *
+ * (C) Copyright 2008, DSPG Technologies GmbH, Germany
+ * (C) Copyright 2007, NXP Semiconductors Germany GmbH
+ * Matthias Wenzel, <nxp at mazzoo.de>
+ *
+ * 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
+ */
+
+/* exported ethernet functions */
+int mii_discover_phy(void);
+
+#define PHYADDR_TO_REG(x)			(x << 8)
+
+/* data types */
+
+/* rx */
+typedef struct{
+	volatile void *packet;
+	volatile unsigned long control;
+} rx_descriptor_t;
+
+typedef struct{
+	volatile unsigned long info;		/* RO */
+	volatile unsigned long hashCRC;		/* RO */
+} rx_status_t;
+
+/* tx */
+typedef struct{
+	volatile void *packet;
+	volatile unsigned long control;
+} tx_descriptor_t;
+
+typedef struct{
+	volatile unsigned long info;		/* RO */
+} tx_status_t;
+
+/* NXP's OUI registered @ IEEE  */
+#define NXP_ETN_OUI			0x006037
+
+/* ip3912 ETN registers */
+#define ETN1_BASE			CONFIG_IP3912_ETN1_BASE
+#define ETN2_BASE			CONFIG_IP3912_ETN2_BASE
+
+/* offsets to base address */
+#define ETN_MAC1			0x0000
+#define ETN_MAC2			0x0004
+#define ETN_IPGT			0x0008
+#define ETN_IPGR			0x000c
+#define ETN_CLRT			0x0010
+#define ETN_MAXF			0x0014
+#define ETN_SUPP			0x0018
+#define ETN_TEST			0x001c
+#define ETN_MCFG			0x0020
+#define ETN_MCMD			0x0024
+#define ETN_MADR			0x0028
+#define ETN_MWTD			0x002c
+#define ETN_MRDD			0x0030
+#define ETN_MIND			0x0034
+#define ETN_SA0				0x0040
+#define ETN_SA1				0x0044
+#define ETN_SA2				0x0048
+#define ETN_COMMAND			0x0100
+#define ETN_STATUS			0x0104
+#define ETN_RXDESCRIPTOR		0x0108
+#define ETN_RXSTATUS			0x010c
+#define ETN_RXDESCRIPTORNUMBER		0x0110
+#define ETN_RXPRODUCEINDEX		0x0114
+#define ETN_RXCONSUMEINDEX		0x0118
+#define ETN_TXDESCRIPTOR		0x011c
+#define ETN_TXSTATUS			0x0120
+#define ETN_TXDESCRIPTORNUMBER		0x0124
+#define ETN_TXPRODUCEINDEX		0x0128
+#define ETN_TXCONSUMEINDEX		0x012c
+#define ETN_TXRTDESCRIPTOR		0x0130
+#define ETN_TXRTSTATUS			0x0134
+#define ETN_TXRTDESCRIPTORNUMBER	0x0138
+#define ETN_TXRTPRODUCEINDEX		0x013c
+#define ETN_TXRTCONSUMEINDEX		0x0140
+#define ETN_QOSTIMEOUT			0x0148
+#define ETN_TSV0			0x0158
+#define ETN_TSV1			0x015c
+#define ETN_RSV				0x0160
+#define ETN_FLOWCONTROLCOUNTER		0x0170
+#define ETN_FLOWCONTROLSTATUS		0x0174
+#define ETN_RXFILTERCTRL		0x0200
+#define ETN_RXFILTERWOLSTATUS		0x0204
+#define ETN_RXFILTERWOLCLEAR		0x0208
+#define ETN_HASHFILTERL			0x0210
+#define ETN_HASHFILTERH			0x0214
+#define ETN_INTSTATUS			0x0fe0
+#define ETN_INTENABLE			0x0fe4
+#define ETN_INTCLEAR			0x0fe8
+#define ETN_INTSET			0x0fec
+#define ETN_POWERDOWN			0x0ff4
+#define ETN_MODULEID			0x0ffc
+
+/* values for control */
+#define ETN_CONTROL_INTERRUPT	0x80000000
+#define ETN_CONTROL_LAST	0x40000000
+#define ETN_CONTROL_CRC		0x20000000
+#define ETN_CONTROL_PAD		0x10000000
+#define ETN_CONTROL_HUGE	0x08000000
+#define ETN_CONTROL_OVERRIDE	0x04000000
+
+/* registers in the SMSC LAN8700 PHY */
+/*
+00 Basic Control Register				Basic
+01 Basic Status Register				Basic
+02 PHY Identifier 1					Extended
+03 PHY Identifier 2					Extended
+04 Auto-Negotiation Advertisement Register		Extended
+05 Auto-Negotiation Link Partner Ability Register	Extended
+06 Auto-Negotiation Expansion Register			Extended
+16 Silicon Revision Register				Vendor-specific
+17 Mode Control/Status Register				Vendor-specific
+18 Special Modes					Vendor-specific
+20 Reserved						Vendor-specific
+21 Reserved						Vendor-specific
+22 Reserved						Vendor-specific
+23 Reserved						Vendor-specific
+27 Control / Status Indication Register			Vendor-specific
+28 Special internal testability controls		Vendor-specific
+29 Interrupt Source Register				Vendor-specific
+30 Interrupt Mask Register				Vendor-specific
+31 PHY Special Control/Status Register			Vendor-specific
+*/
+#define ETN_PHY_BASIC_CONTROL			0x00
+#define ETN_PHY_BASIC_STATUS			0x01
+#define ETN_PHY_ID1				0x02
+#define ETN_PHY_ID2				0x03
+#define ETN_PHY_AUTONEG_ADV			0x04
+#define ETN_PHY_AUTONEG_LINK			0x05
+#define ETN_PHY_AUTONEG_EXP			0x06
+#define ETN_PHY_SILICON				0x10
+#define ETN_PHY_MODE_CONTROL_STATUS		0x11
+#define ETN_PHY_SPECIAL_MODES			0x12
+#define ETN_PHY_CONTROL_STATUS_INDICATION	0x1b
+#define ETN_PHY_INTERNAL_TESTABILITY		0x1c
+#define ETN_PHY_INTERRUPT_SOURCE		0x1d
+#define ETN_PHY_INTERRUPT_MASK			0x1e
+#define ETN_PHY_SPECIAL_MODE_CONTROL_STATUS	0x1f
+
+/* PHY identification */
+#define PHY_ID_LXT970		0x78100000	/* LXT970 */
+#define PHY_ID_LXT971		0x001378e0	/* LXT971 and 972 */
+#define PHY_ID_82555		0x02a80150	/* Intel 82555 */
+#define PHY_ID_QS6612		0x01814400	/* QS6612 */
+#define PHY_ID_AMD79C784	0x00225610	/* AMD 79C784 */
+#define PHY_ID_LSI80225		0x0016f870	/* LSI 80225 */
+#define PHY_ID_LSI80225B	0x0016f880	/* LSI 80225/B */
+#define PHY_ID_DM9161		0x0181B880	/* Davicom DM9161 */
+#define PHY_ID_KSM8995M		0x00221450	/* MICREL KS8995MA */
+#define PHY_ID_SMSC8700		0x0007C0C0	/* SMSC LAN 8700 */
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 197 bytes
Desc: not available
Url : http://lists.denx.de/pipermail/u-boot/attachments/20090104/f3d5d232/attachment-0001.pgp 


More information about the U-Boot mailing list