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 <j...@emlix.com>
+ *
+ * (C) Copyright 2008, DSPG Technologies GmbH, Germany
+ * (C) Copyright 2007, NXP Semiconductors Germany GmbH
+ * Matthias Wenzel, <n...@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 <j...@emlix.com>
+ *
+ * (C) Copyright 2008, DSPG Technologies GmbH, Germany
+ * (C) Copyright 2007, NXP Semiconductors Germany GmbH
+ * Matthias Wenzel, <n...@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 */

Attachment: signature.asc
Description: PGP signature

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to