blob: 7e4af441054bb1c3c44c81530b36a0f58bbc3c1b [file] [log] [blame]
/*
* (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>
#ifdef CONFIG_IXP4XX_NPE
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 + CFG_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 + (CFG_CACHELINE_SIZE-1)) & ~(CFG_CACHELINE_SIZE-1);
count++;
if ((npe_alloc_free + size) < npe_alloc_end) {
p = npe_alloc_free;
npe_alloc_free += size;
} else {
printf("%s: 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__);
miiphy_read (dev->name, p_npe->phy_no, PHY_BMSR, &reg_short);
/*
* Wait if PHY is capable of autonegotiation and autonegotiation is not complete
*/
if ((reg_short & PHY_BMSR_AUTN_ABLE) && !(reg_short & PHY_BMSR_AUTN_COMP)) {
puts ("Waiting for PHY auto negotiation to complete");
i = 0;
while (!(reg_short & PHY_BMSR_AUTN_COMP)) {
/*
* Timeout reached ?
*/
if (i > PHY_AUTONEGOTIATE_TIMEOUT) {
puts (" TIMEOUT !\n");
break;
}
if ((i++ % 1000) == 0) {
putc ('.');
miiphy_read (dev->name, p_npe->phy_no, PHY_BMSR, &reg_short);
}
udelay (1000); /* 1 ms */
}
puts (" done\n");
udelay (500000); /* another 500 ms (results in faster booting) */
}
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 +
CFG_CACHELINE_SIZE - 1) & ~(CFG_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 0;
}
if (ixEthAccPortTxDoneCallbackRegister(p_npe->eth_id, npe_tx_callback,
(u32)p_npe) != IX_ETH_ACC_SUCCESS) {
printf("can't register TX callback!\n");
return 0;
}
npe_set_mac_address(dev);
if (ixEthAccPortEnable(p_npe->eth_id) != IX_ETH_ACC_SUCCESS) {
printf("can't enable port!\n");
return 0;
}
p_npe->active = 1;
return 1;
}
#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, volatile 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;
for (eth_num = 0; eth_num < CFG_NPE_NUMS; eth_num++) {
/* See if we can actually bring up the interface, otherwise, skip it */
switch (eth_num) {
default: /* fall through */
case 0:
if (memcmp (bis->bi_enetaddr, "\0\0\0\0\0\0", 6) == 0) {
continue;
}
break;
#ifdef CONFIG_HAS_ETH1
case 1:
if (memcmp (bis->bi_enet1addr, "\0\0\0\0\0\0", 6) == 0) {
continue;
}
break;
#endif
}
/* 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));
switch (eth_num) {
default: /* fall through */
case 0:
memcpy(dev->enetaddr, bis->bi_enetaddr, 6);
p_npe->eth_id = 0;
p_npe->phy_no = CONFIG_PHY_ADDR;
break;
#ifdef CONFIG_HAS_ETH1
case 1:
memcpy(dev->enetaddr, bis->bi_enet1addr, 6);
p_npe->eth_id = 1;
p_npe->phy_no = CONFIG_PHY1_ADDR;
break;
#endif
}
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:
/*
* If it is B0 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 +
CFG_CACHELINE_SIZE - 1)
& ~(CFG_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;
}
#endif /* CONFIG_IXP4XX_NPE */