summaryrefslogtreecommitdiff
path: root/drivers/net/npe/npe.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/npe/npe.c')
-rw-r--r--drivers/net/npe/npe.c696
1 files changed, 696 insertions, 0 deletions
diff --git a/drivers/net/npe/npe.c b/drivers/net/npe/npe.c
new file mode 100644
index 0000000..1fe3a95
--- /dev/null
+++ b/drivers/net/npe/npe.c
@@ -0,0 +1,696 @@
+/*
+ * (C) Copyright 2005-2006
+ * Stefan Roese, DENX Software Engineering, sr@denx.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
+ */
+
+#if 0
+#define DEBUG /* define for debug output */
+#endif
+
+#include <config.h>
+#include <common.h>
+#include <net.h>
+#include <miiphy.h>
+#include <malloc.h>
+#include <asm/processor.h>
+#include <asm/arch-ixp/ixp425.h>
+
+#include <IxOsal.h>
+#include <IxEthAcc.h>
+#include <IxEthDB.h>
+#include <IxNpeDl.h>
+#include <IxQMgr.h>
+#include <IxNpeMh.h>
+#include <ix_ossl.h>
+#include <IxFeatureCtrl.h>
+
+#include <npe.h>
+
+static IxQMgrDispatcherFuncPtr qDispatcherFunc = NULL;
+static int npe_exists[NPE_NUM_PORTS];
+static int npe_used[NPE_NUM_PORTS];
+
+/* A little extra so we can align to cacheline. */
+static u8 npe_alloc_pool[NPE_MEM_POOL_SIZE + CONFIG_SYS_CACHELINE_SIZE - 1];
+static u8 *npe_alloc_end;
+static u8 *npe_alloc_free;
+
+static void *npe_alloc(int size)
+{
+ static int count = 0;
+ void *p = NULL;
+
+ size = (size + (CONFIG_SYS_CACHELINE_SIZE-1)) & ~(CONFIG_SYS_CACHELINE_SIZE-1);
+ count++;
+
+ if ((npe_alloc_free + size) < npe_alloc_end) {
+ p = npe_alloc_free;
+ npe_alloc_free += size;
+ } else {
+ printf("npe_alloc: failed (count=%d, size=%d)!\n", count, size);
+ }
+ return p;
+}
+
+/* Not interrupt safe! */
+static void mbuf_enqueue(IX_OSAL_MBUF **q, IX_OSAL_MBUF *new)
+{
+ IX_OSAL_MBUF *m = *q;
+
+ IX_OSAL_MBUF_NEXT_PKT_IN_CHAIN_PTR(new) = NULL;
+
+ if (m) {
+ while(IX_OSAL_MBUF_NEXT_PKT_IN_CHAIN_PTR(m))
+ m = IX_OSAL_MBUF_NEXT_PKT_IN_CHAIN_PTR(m);
+ IX_OSAL_MBUF_NEXT_PKT_IN_CHAIN_PTR(m) = new;
+ } else
+ *q = new;
+}
+
+/* Not interrupt safe! */
+static IX_OSAL_MBUF *mbuf_dequeue(IX_OSAL_MBUF **q)
+{
+ IX_OSAL_MBUF *m = *q;
+ if (m)
+ *q = IX_OSAL_MBUF_NEXT_PKT_IN_CHAIN_PTR(m);
+ return m;
+}
+
+static void reset_tx_mbufs(struct npe* p_npe)
+{
+ IX_OSAL_MBUF *m;
+ int i;
+
+ p_npe->txQHead = NULL;
+
+ for (i = 0; i < CONFIG_DEVS_ETH_INTEL_NPE_MAX_TX_DESCRIPTORS; i++) {
+ m = &p_npe->tx_mbufs[i];
+
+ memset(m, 0, sizeof(*m));
+
+ IX_OSAL_MBUF_MDATA(m) = (void *)&p_npe->tx_pkts[i * NPE_PKT_SIZE];
+ IX_OSAL_MBUF_MLEN(m) = IX_OSAL_MBUF_PKT_LEN(m) = NPE_PKT_SIZE;
+ mbuf_enqueue(&p_npe->txQHead, m);
+ }
+}
+
+static void reset_rx_mbufs(struct npe* p_npe)
+{
+ IX_OSAL_MBUF *m;
+ int i;
+
+ p_npe->rxQHead = NULL;
+
+ HAL_DCACHE_INVALIDATE(p_npe->rx_pkts, NPE_PKT_SIZE *
+ CONFIG_DEVS_ETH_INTEL_NPE_MAX_RX_DESCRIPTORS);
+
+ for (i = 0; i < CONFIG_DEVS_ETH_INTEL_NPE_MAX_RX_DESCRIPTORS; i++) {
+ m = &p_npe->rx_mbufs[i];
+
+ memset(m, 0, sizeof(*m));
+
+ IX_OSAL_MBUF_MDATA(m) = (void *)&p_npe->rx_pkts[i * NPE_PKT_SIZE];
+ IX_OSAL_MBUF_MLEN(m) = IX_OSAL_MBUF_PKT_LEN(m) = NPE_PKT_SIZE;
+
+ if(ixEthAccPortRxFreeReplenish(p_npe->eth_id, m) != IX_SUCCESS) {
+ printf("ixEthAccPortRxFreeReplenish failed for port %d\n", p_npe->eth_id);
+ break;
+ }
+ }
+}
+
+static void init_rx_mbufs(struct npe* p_npe)
+{
+ p_npe->rxQHead = NULL;
+
+ p_npe->rx_pkts = npe_alloc(NPE_PKT_SIZE *
+ CONFIG_DEVS_ETH_INTEL_NPE_MAX_RX_DESCRIPTORS);
+ if (p_npe->rx_pkts == NULL) {
+ printf("alloc of packets failed.\n");
+ return;
+ }
+
+ p_npe->rx_mbufs = (IX_OSAL_MBUF *)
+ npe_alloc(sizeof(IX_OSAL_MBUF) *
+ CONFIG_DEVS_ETH_INTEL_NPE_MAX_RX_DESCRIPTORS);
+ if (p_npe->rx_mbufs == NULL) {
+ printf("alloc of mbufs failed.\n");
+ return;
+ }
+
+ reset_rx_mbufs(p_npe);
+}
+
+static void init_tx_mbufs(struct npe* p_npe)
+{
+ p_npe->tx_pkts = npe_alloc(NPE_PKT_SIZE *
+ CONFIG_DEVS_ETH_INTEL_NPE_MAX_TX_DESCRIPTORS);
+ if (p_npe->tx_pkts == NULL) {
+ printf("alloc of packets failed.\n");
+ return;
+ }
+
+ p_npe->tx_mbufs = (IX_OSAL_MBUF *)
+ npe_alloc(sizeof(IX_OSAL_MBUF) *
+ CONFIG_DEVS_ETH_INTEL_NPE_MAX_TX_DESCRIPTORS);
+ if (p_npe->tx_mbufs == NULL) {
+ printf("alloc of mbufs failed.\n");
+ return;
+ }
+
+ reset_tx_mbufs(p_npe);
+}
+
+/* Convert IX_ETH_PORT_n to IX_NPEMH_NPEID_NPEx */
+static int __eth_to_npe(int eth_id)
+{
+ switch(eth_id) {
+ case IX_ETH_PORT_1:
+ return IX_NPEMH_NPEID_NPEB;
+
+ case IX_ETH_PORT_2:
+ return IX_NPEMH_NPEID_NPEC;
+
+ case IX_ETH_PORT_3:
+ return IX_NPEMH_NPEID_NPEA;
+ }
+ return 0;
+}
+
+/* Poll the CSR machinery. */
+static void npe_poll(int eth_id)
+{
+ if (qDispatcherFunc != NULL) {
+ ixNpeMhMessagesReceive(__eth_to_npe(eth_id));
+ (*qDispatcherFunc)(IX_QMGR_QUELOW_GROUP);
+ }
+}
+
+/* ethAcc RX callback */
+static void npe_rx_callback(u32 cbTag, IX_OSAL_MBUF *m, IxEthAccPortId portid)
+{
+ struct npe* p_npe = (struct npe *)cbTag;
+
+ if (IX_OSAL_MBUF_MLEN(m) > 0) {
+ mbuf_enqueue(&p_npe->rxQHead, m);
+
+ if (p_npe->rx_write == ((p_npe->rx_read-1) & (PKTBUFSRX-1))) {
+ debug("Rx overflow: rx_write=%d rx_read=%d\n",
+ p_npe->rx_write, p_npe->rx_read);
+ } else {
+ debug("Received message #%d (len=%d)\n", p_npe->rx_write,
+ IX_OSAL_MBUF_MLEN(m));
+ memcpy((void *)NetRxPackets[p_npe->rx_write], IX_OSAL_MBUF_MDATA(m),
+ IX_OSAL_MBUF_MLEN(m));
+ p_npe->rx_len[p_npe->rx_write] = IX_OSAL_MBUF_MLEN(m);
+ p_npe->rx_write++;
+ if (p_npe->rx_write == PKTBUFSRX)
+ p_npe->rx_write = 0;
+
+#ifdef CONFIG_PRINT_RX_FRAMES
+ {
+ u8 *ptr = IX_OSAL_MBUF_MDATA(m);
+ int i;
+
+ for (i=0; i<60; i++) {
+ debug("%02x ", *ptr++);
+ }
+ debug("\n");
+ }
+#endif
+ }
+
+ m = mbuf_dequeue(&p_npe->rxQHead);
+ } else {
+ debug("Received frame with length 0!!!\n");
+ m = mbuf_dequeue(&p_npe->rxQHead);
+ }
+
+ /* Now return mbuf to NPE */
+ IX_OSAL_MBUF_MLEN(m) = IX_OSAL_MBUF_PKT_LEN(m) = NPE_PKT_SIZE;
+ IX_OSAL_MBUF_NEXT_BUFFER_IN_PKT_PTR(m) = NULL;
+ IX_OSAL_MBUF_FLAGS(m) = 0;
+
+ if(ixEthAccPortRxFreeReplenish(p_npe->eth_id, m) != IX_SUCCESS) {
+ debug("npe_rx_callback: Error returning mbuf.\n");
+ }
+}
+
+/* ethAcc TX callback */
+static void npe_tx_callback(u32 cbTag, IX_OSAL_MBUF *m)
+{
+ struct npe* p_npe = (struct npe *)cbTag;
+
+ debug("%s\n", __FUNCTION__);
+
+ IX_OSAL_MBUF_MLEN(m) = IX_OSAL_MBUF_PKT_LEN(m) = NPE_PKT_SIZE;
+ IX_OSAL_MBUF_NEXT_BUFFER_IN_PKT_PTR(m) = NULL;
+ IX_OSAL_MBUF_FLAGS(m) = 0;
+
+ mbuf_enqueue(&p_npe->txQHead, m);
+}
+
+
+static int npe_set_mac_address(struct eth_device *dev)
+{
+ struct npe *p_npe = (struct npe *)dev->priv;
+ IxEthAccMacAddr npeMac;
+
+ debug("%s\n", __FUNCTION__);
+
+ /* Set MAC address */
+ memcpy(npeMac.macAddress, dev->enetaddr, 6);
+
+ if (ixEthAccPortUnicastMacAddressSet(p_npe->eth_id, &npeMac) != IX_ETH_ACC_SUCCESS) {
+ printf("Error setting unicast address! %02x:%02x:%02x:%02x:%02x:%02x\n",
+ npeMac.macAddress[0], npeMac.macAddress[1],
+ npeMac.macAddress[2], npeMac.macAddress[3],
+ npeMac.macAddress[4], npeMac.macAddress[5]);
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Boot-time CSR library initialization. */
+static int npe_csr_load(void)
+{
+ int i;
+
+ if (ixQMgrInit() != IX_SUCCESS) {
+ debug("Error initialising queue manager!\n");
+ return 0;
+ }
+
+ ixQMgrDispatcherLoopGet(&qDispatcherFunc);
+
+ if(ixNpeMhInitialize(IX_NPEMH_NPEINTERRUPTS_YES) != IX_SUCCESS) {
+ printf("Error initialising NPE Message handler!\n");
+ return 0;
+ }
+
+ if (npe_used[IX_ETH_PORT_1] && npe_exists[IX_ETH_PORT_1] &&
+ ixNpeDlNpeInitAndStart(IX_NPEDL_NPEIMAGE_NPEB_ETH_LEARN_FILTER_SPAN_FIREWALL_VLAN_QOS)
+ != IX_SUCCESS) {
+ printf("Error downloading firmware to NPE-B!\n");
+ return 0;
+ }
+
+ if (npe_used[IX_ETH_PORT_2] && npe_exists[IX_ETH_PORT_2] &&
+ ixNpeDlNpeInitAndStart(IX_NPEDL_NPEIMAGE_NPEC_ETH_LEARN_FILTER_SPAN_FIREWALL_VLAN_QOS)
+ != IX_SUCCESS) {
+ printf("Error downloading firmware to NPE-C!\n");
+ return 0;
+ }
+
+ /* don't need this for U-Boot */
+ ixFeatureCtrlSwConfigurationWrite(IX_FEATURECTRL_ETH_LEARNING, FALSE);
+
+ if (ixEthAccInit() != IX_ETH_ACC_SUCCESS) {
+ printf("Error initialising Ethernet access driver!\n");
+ return 0;
+ }
+
+ for (i = 0; i < IX_ETH_ACC_NUMBER_OF_PORTS; i++) {
+ if (!npe_used[i] || !npe_exists[i])
+ continue;
+ if (ixEthAccPortInit(i) != IX_ETH_ACC_SUCCESS) {
+ printf("Error initialising Ethernet port%d!\n", i);
+ }
+ if (ixEthAccTxSchedulingDisciplineSet(i, FIFO_NO_PRIORITY) != IX_ETH_ACC_SUCCESS) {
+ printf("Error setting scheduling discipline for port %d.\n", i);
+ }
+ if (ixEthAccPortRxFrameAppendFCSDisable(i) != IX_ETH_ACC_SUCCESS) {
+ printf("Error disabling RX FCS for port %d.\n", i);
+ }
+ if (ixEthAccPortTxFrameAppendFCSEnable(i) != IX_ETH_ACC_SUCCESS) {
+ printf("Error enabling TX FCS for port %d.\n", i);
+ }
+ }
+
+ return 1;
+}
+
+static int npe_init(struct eth_device *dev, bd_t * bis)
+{
+ struct npe *p_npe = (struct npe *)dev->priv;
+ int i;
+ u16 reg_short;
+ int speed;
+ int duplex;
+
+ debug("%s: 1\n", __FUNCTION__);
+
+#ifdef CONFIG_MII_NPE0_FIXEDLINK
+ if (0 == p_npe->eth_id) {
+ speed = CONFIG_MII_NPE0_SPEED;
+ duplex = CONFIG_MII_NPE0_FULLDUPLEX ? FULL : HALF;
+ } else
+#endif
+#ifdef CONFIG_MII_NPE1_FIXEDLINK
+ if (1 == p_npe->eth_id) {
+ speed = CONFIG_MII_NPE1_SPEED;
+ duplex = CONFIG_MII_NPE1_FULLDUPLEX ? FULL : HALF;
+ } else
+#endif
+ {
+ miiphy_read(dev->name, p_npe->phy_no, MII_BMSR, &reg_short);
+
+ /*
+ * Wait if PHY is capable of autonegotiation and
+ * autonegotiation is not complete
+ */
+ if ((reg_short & BMSR_ANEGCAPABLE) &&
+ !(reg_short & BMSR_ANEGCOMPLETE)) {
+ puts("Waiting for PHY auto negotiation to complete");
+ i = 0;
+ while (!(reg_short & BMSR_ANEGCOMPLETE)) {
+ /*
+ * Timeout reached ?
+ */
+ if (i > PHY_AUTONEGOTIATE_TIMEOUT) {
+ puts(" TIMEOUT !\n");
+ break;
+ }
+
+ if ((i++ % 1000) == 0) {
+ putc('.');
+ miiphy_read(dev->name, p_npe->phy_no,
+ MII_BMSR, &reg_short);
+ }
+ udelay(1000); /* 1 ms */
+ }
+ puts(" done\n");
+ /* another 500 ms (results in faster booting) */
+ udelay(500000);
+ }
+ speed = miiphy_speed(dev->name, p_npe->phy_no);
+ duplex = miiphy_duplex(dev->name, p_npe->phy_no);
+ }
+
+ if (p_npe->print_speed) {
+ p_npe->print_speed = 0;
+ printf ("ENET Speed is %d Mbps - %s duplex connection\n",
+ (int) speed, (duplex == HALF) ? "HALF" : "FULL");
+ }
+
+ npe_alloc_end = npe_alloc_pool + sizeof(npe_alloc_pool);
+ npe_alloc_free = (u8 *)(((unsigned)npe_alloc_pool +
+ CONFIG_SYS_CACHELINE_SIZE - 1) & ~(CONFIG_SYS_CACHELINE_SIZE - 1));
+
+ /* initialize mbuf pool */
+ init_rx_mbufs(p_npe);
+ init_tx_mbufs(p_npe);
+
+ if (ixEthAccPortRxCallbackRegister(p_npe->eth_id, npe_rx_callback,
+ (u32)p_npe) != IX_ETH_ACC_SUCCESS) {
+ printf("can't register RX callback!\n");
+ return -1;
+ }
+
+ if (ixEthAccPortTxDoneCallbackRegister(p_npe->eth_id, npe_tx_callback,
+ (u32)p_npe) != IX_ETH_ACC_SUCCESS) {
+ printf("can't register TX callback!\n");
+ return -1;
+ }
+
+ npe_set_mac_address(dev);
+
+ if (ixEthAccPortEnable(p_npe->eth_id) != IX_ETH_ACC_SUCCESS) {
+ printf("can't enable port!\n");
+ return -1;
+ }
+
+ p_npe->active = 1;
+
+ return 0;
+}
+
+#if 0 /* test-only: probably have to deal with it when booting linux (for a clean state) */
+/* Uninitialize CSR library. */
+static void npe_csr_unload(void)
+{
+ ixEthAccUnload();
+ ixEthDBUnload();
+ ixNpeMhUnload();
+ ixQMgrUnload();
+}
+
+/* callback which is used by ethAcc to recover RX buffers when stopping */
+static void npe_rx_stop_callback(u32 cbTag, IX_OSAL_MBUF *m, IxEthAccPortId portid)
+{
+ debug("%s\n", __FUNCTION__);
+}
+
+/* callback which is used by ethAcc to recover TX buffers when stopping */
+static void npe_tx_stop_callback(u32 cbTag, IX_OSAL_MBUF *m)
+{
+ debug("%s\n", __FUNCTION__);
+}
+#endif
+
+static void npe_halt(struct eth_device *dev)
+{
+ struct npe *p_npe = (struct npe *)dev->priv;
+ int i;
+
+ debug("%s\n", __FUNCTION__);
+
+ /* Delay to give time for recovery of mbufs */
+ for (i = 0; i < 100; i++) {
+ npe_poll(p_npe->eth_id);
+ udelay(100);
+ }
+
+#if 0 /* test-only: probably have to deal with it when booting linux (for a clean state) */
+ if (ixEthAccPortRxCallbackRegister(p_npe->eth_id, npe_rx_stop_callback,
+ (u32)p_npe) != IX_ETH_ACC_SUCCESS) {
+ debug("Error registering rx callback!\n");
+ }
+
+ if (ixEthAccPortTxDoneCallbackRegister(p_npe->eth_id, npe_tx_stop_callback,
+ (u32)p_npe) != IX_ETH_ACC_SUCCESS) {
+ debug("Error registering tx callback!\n");
+ }
+
+ if (ixEthAccPortDisable(p_npe->eth_id) != IX_ETH_ACC_SUCCESS) {
+ debug("npe_stop: Error disabling NPEB!\n");
+ }
+
+ /* Delay to give time for recovery of mbufs */
+ for (i = 0; i < 100; i++) {
+ npe_poll(p_npe->eth_id);
+ udelay(10000);
+ }
+
+ /*
+ * For U-Boot only, we are probably launching Linux or other OS that
+ * needs a clean slate for its NPE library.
+ */
+#if 0 /* test-only */
+ for (i = 0; i < IX_ETH_ACC_NUMBER_OF_PORTS; i++) {
+ if (npe_used[i] && npe_exists[i])
+ if (ixNpeDlNpeStopAndReset(__eth_to_npe(i)) != IX_SUCCESS)
+ printf("Failed to stop and reset NPE B.\n");
+ }
+#endif
+
+#endif
+ p_npe->active = 0;
+}
+
+
+static int npe_send(struct eth_device *dev, void *packet, int len)
+{
+ struct npe *p_npe = (struct npe *)dev->priv;
+ u8 *dest;
+ int err;
+ IX_OSAL_MBUF *m;
+
+ debug("%s\n", __FUNCTION__);
+ m = mbuf_dequeue(&p_npe->txQHead);
+ dest = IX_OSAL_MBUF_MDATA(m);
+ IX_OSAL_MBUF_PKT_LEN(m) = IX_OSAL_MBUF_MLEN(m) = len;
+ IX_OSAL_MBUF_NEXT_PKT_IN_CHAIN_PTR(m) = NULL;
+
+ memcpy(dest, (char *)packet, len);
+
+ if ((err = ixEthAccPortTxFrameSubmit(p_npe->eth_id, m, IX_ETH_ACC_TX_DEFAULT_PRIORITY))
+ != IX_ETH_ACC_SUCCESS) {
+ printf("npe_send: Can't submit frame. err[%d]\n", err);
+ mbuf_enqueue(&p_npe->txQHead, m);
+ return 0;
+ }
+
+#ifdef DEBUG_PRINT_TX_FRAMES
+ {
+ u8 *ptr = IX_OSAL_MBUF_MDATA(m);
+ int i;
+
+ for (i=0; i<IX_OSAL_MBUF_MLEN(m); i++) {
+ printf("%02x ", *ptr++);
+ }
+ printf(" (tx-len=%d)\n", IX_OSAL_MBUF_MLEN(m));
+ }
+#endif
+
+ npe_poll(p_npe->eth_id);
+
+ return len;
+}
+
+static int npe_rx(struct eth_device *dev)
+{
+ struct npe *p_npe = (struct npe *)dev->priv;
+
+ debug("%s\n", __FUNCTION__);
+ npe_poll(p_npe->eth_id);
+
+ debug("%s: rx_write=%d rx_read=%d\n", __FUNCTION__, p_npe->rx_write, p_npe->rx_read);
+ while (p_npe->rx_write != p_npe->rx_read) {
+ debug("Reading message #%d\n", p_npe->rx_read);
+ NetReceive(NetRxPackets[p_npe->rx_read], p_npe->rx_len[p_npe->rx_read]);
+ p_npe->rx_read++;
+ if (p_npe->rx_read == PKTBUFSRX)
+ p_npe->rx_read = 0;
+ }
+
+ return 0;
+}
+
+int npe_initialize(bd_t * bis)
+{
+ static int virgin = 0;
+ struct eth_device *dev;
+ int eth_num = 0;
+ struct npe *p_npe = NULL;
+ uchar enetaddr[6];
+
+ for (eth_num = 0; eth_num < CONFIG_SYS_NPE_NUMS; eth_num++) {
+
+ /* See if we can actually bring up the interface, otherwise, skip it */
+#ifdef CONFIG_HAS_ETH1
+ if (eth_num == 1) {
+ if (!eth_getenv_enetaddr("eth1addr", enetaddr))
+ continue;
+ } else
+#endif
+ if (!eth_getenv_enetaddr("ethaddr", enetaddr))
+ continue;
+
+ /* Allocate device structure */
+ dev = (struct eth_device *)malloc(sizeof(*dev));
+ if (dev == NULL) {
+ printf ("%s: Cannot allocate eth_device %d\n", __FUNCTION__, eth_num);
+ return -1;
+ }
+ memset(dev, 0, sizeof(*dev));
+
+ /* Allocate our private use data */
+ p_npe = (struct npe *)malloc(sizeof(struct npe));
+ if (p_npe == NULL) {
+ printf("%s: Cannot allocate private hw data for eth_device %d",
+ __FUNCTION__, eth_num);
+ free(dev);
+ return -1;
+ }
+ memset(p_npe, 0, sizeof(struct npe));
+
+ p_npe->eth_id = eth_num;
+ memcpy(dev->enetaddr, enetaddr, 6);
+#ifdef CONFIG_HAS_ETH1
+ if (eth_num == 1)
+ p_npe->phy_no = CONFIG_PHY1_ADDR;
+ else
+#endif
+ p_npe->phy_no = CONFIG_PHY_ADDR;
+
+ sprintf(dev->name, "NPE%d", eth_num);
+ dev->priv = (void *)p_npe;
+ dev->init = npe_init;
+ dev->halt = npe_halt;
+ dev->send = npe_send;
+ dev->recv = npe_rx;
+
+ p_npe->print_speed = 1;
+
+ if (0 == virgin) {
+ virgin = 1;
+
+ if (ixFeatureCtrlDeviceRead() == IX_FEATURE_CTRL_DEVICE_TYPE_IXP42X) {
+ switch (ixFeatureCtrlProductIdRead() & IX_FEATURE_CTRL_SILICON_STEPPING_MASK) {
+ case IX_FEATURE_CTRL_SILICON_TYPE_B0:
+ default: /* newer than B0 */
+ /*
+ * If it is B0 or newer Silicon, we
+ * only enable port when its
+ * corresponding Eth Coprocessor is
+ * available.
+ */
+ if (ixFeatureCtrlComponentCheck(IX_FEATURECTRL_ETH0) ==
+ IX_FEATURE_CTRL_COMPONENT_ENABLED)
+ npe_exists[IX_ETH_PORT_1] = TRUE;
+
+ if (ixFeatureCtrlComponentCheck(IX_FEATURECTRL_ETH1) ==
+ IX_FEATURE_CTRL_COMPONENT_ENABLED)
+ npe_exists[IX_ETH_PORT_2] = TRUE;
+ break;
+ case IX_FEATURE_CTRL_SILICON_TYPE_A0:
+ /*
+ * If it is A0 Silicon, we enable both as both Eth Coprocessors
+ * are available.
+ */
+ npe_exists[IX_ETH_PORT_1] = TRUE;
+ npe_exists[IX_ETH_PORT_2] = TRUE;
+ break;
+ }
+ } else if (ixFeatureCtrlDeviceRead() == IX_FEATURE_CTRL_DEVICE_TYPE_IXP46X) {
+ if (ixFeatureCtrlComponentCheck(IX_FEATURECTRL_ETH0) ==
+ IX_FEATURE_CTRL_COMPONENT_ENABLED)
+ npe_exists[IX_ETH_PORT_1] = TRUE;
+
+ if (ixFeatureCtrlComponentCheck(IX_FEATURECTRL_ETH1) ==
+ IX_FEATURE_CTRL_COMPONENT_ENABLED)
+ npe_exists[IX_ETH_PORT_2] = TRUE;
+ }
+
+ npe_used[IX_ETH_PORT_1] = 1;
+ npe_used[IX_ETH_PORT_2] = 1;
+
+ npe_alloc_end = npe_alloc_pool + sizeof(npe_alloc_pool);
+ npe_alloc_free = (u8 *)(((unsigned)npe_alloc_pool +
+ CONFIG_SYS_CACHELINE_SIZE - 1)
+ & ~(CONFIG_SYS_CACHELINE_SIZE - 1));
+
+ if (!npe_csr_load())
+ return 0;
+ }
+
+ eth_register(dev);
+
+#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
+ miiphy_register(dev->name, npe_miiphy_read, npe_miiphy_write);
+#endif
+
+ } /* end for each supported device */
+
+ return 1;
+}