blob: a7419308b8f9a8986f700cec857adf8da2cfb67c [file] [log] [blame]
/*******************************************************************************
Copyright (C) Marvell International Ltd. and its affiliates
********************************************************************************
Marvell GPL License Option
If you received this File from Marvell, you may opt to use, redistribute and/or
modify this File in accordance with the terms and conditions of the General
Public License Version 2, June 1991 (the "GPL License"), a copy of which is
available along with the File in the license.txt file or by writing to the Free
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 or
on the worldwide web at http://www.gnu.org/licenses/gpl.txt.
THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE IMPLIED
WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY
DISCLAIMED. The GPL License provides additional details about this warranty
disclaimer/
*******************************************************************************/
#include <common.h>
#include <command.h>
#include <net.h>
#include <malloc.h>
#if defined(MV_INCLUDE_GIG_ETH)
#include "mvOs.h"
#include "mvSysHwConfig.h"
#include "pp2/gbe/mvPp2Gbe.h"
#include "pp2/bm/mvBm.h"
#include "pp2/prs/mvPp2Prs.h"
#include "pp2/prs/mvPp2PrsHw.h"
#include "pp2/cls/mvPp2Classifier.h"
#include "gpp/mvGppRegs.h"
#include "eth-phy/mvEthPhy.h"
#include "ethSwitch/mvSwitch.h"
#include "mvBoardEnvLib.h"
#include "mvSysPp2Api.h"
/******************************************************
* driver internal definitions -- *
******************************************************/
/* use only tx-queue0 and rx-queue0 */
#define EGIGA_DEF_TXQ 0
#define EGIGA_DEF_TXP 0
#define EGIGA_DEF_RXQ 0
/* rx buffer size */
#define BUFF_HDR_OFFS 32
#define BM_ALIGN 32
#define ETH_HLEN 14
#define WRAP (2 + ETH_HLEN + 4) /* 2(HW hdr) 14(MAC hdr) 4(CRC) 32(buffer header) */
#define MTU 1500
#define RX_PKT_SIZE (MTU + WRAP)
#define RX_BUFFER_SIZE (RX_PKT_SIZE + BUFF_HDR_OFFS + BM_ALIGN)
/* rings length */
#define EGIGA_TXQ_HWF_LEN 16
#define EGIGA_TXQ_LEN 32
#define EGIGA_RXQ_LEN 32
#define EGIGA_TX_DESC_ALIGN 0x1F
/* BM configuration */
#define EGIGA_BM_POOL 0
#define EGIGA_BM_SIZE 128
#define NAMESIZE 16
typedef struct _egigaPriv {
int port;
MV_VOID *halPriv;
MV_BOOL devInit;
MV_BOOL devEnable;
} egigaPriv;
typedef struct _packetInfo {
MV_U8 *bufVirtPtr;
MV_ULONG bufPhysAddr;
MV_U32 dataSize;
} pktInfo;
/******************************************************
* functions prototype -- *
******************************************************/
static int mv_eth_bm_init(MV_VOID);
static int mv_eth_bm_start(MV_VOID);
static void mv_eth_bm_stop(MV_VOID);
static int mvEgigaLoad(int port, char *name, char *enet_addr);
static int mvEgigaInit(struct eth_device *dev, bd_t *p);
static int mvEgigaHalt(struct eth_device *dev);
static int mvEgigaTx(struct eth_device *dev, volatile MV_VOID *packet, int len);
static int mvEgigaRx(struct eth_device *dev);
/***********************************************************
* mv_eth_bm_init -- *
* initialize BM pool to bu used by all ports *
***********************************************************/
static int mv_eth_bm_init(MV_VOID)
{
MV_STATUS status;
unsigned char *pool_addr, *pool_addr_phys;
mvBmInit();
pool_addr = mvOsIoUncachedMalloc(NULL,
(sizeof(MV_U32) * EGIGA_BM_SIZE) + MV_BM_POOL_PTR_ALIGN, (MV_ULONG *)&pool_addr_phys, NULL);
if (!pool_addr) {
printf("Can't alloc %d bytes for pool #%d\n", sizeof(MV_U32) * EGIGA_BM_SIZE, EGIGA_BM_POOL);
return -1;
}
if (MV_IS_NOT_ALIGN((MV_ULONG)pool_addr_phys, MV_BM_POOL_PTR_ALIGN))
pool_addr_phys = (unsigned char *)MV_ALIGN_UP((MV_ULONG)pool_addr_phys, MV_BM_POOL_PTR_ALIGN);
status = mvBmPoolInit(EGIGA_BM_POOL, (MV_U32 *)pool_addr, (MV_ULONG)pool_addr_phys, EGIGA_BM_SIZE);
if (status != MV_OK) {
printf("Can't init #%d BM pool. status=%d\n", EGIGA_BM_POOL, status);
return -1;
}
#ifdef CONFIG_MV_ETH_PP2_1
/* Disable BM priority */
mvPp2WrReg(MV_BM_PRIO_CTRL_REG, 0);
#endif
mvBmPoolControl(EGIGA_BM_POOL, MV_START);
mvPp2BmPoolBufSizeSet(EGIGA_BM_POOL, RX_BUFFER_SIZE);
return 0;
}
/***********************************************************
* mv_eth_bm_start -- *
* enable and fill BM pool *
***********************************************************/
static int mv_eth_bm_start(MV_VOID)
{
unsigned char *buff, *buff_phys;
int i;
mvBmPoolControl(EGIGA_BM_POOL, MV_START);
mvPp2BmPoolBufSizeSet(EGIGA_BM_POOL, RX_BUFFER_SIZE);
/* fill BM pool with buffers */
for (i = 0; i < EGIGA_BM_SIZE; i++) {
buff = (unsigned char *)malloc(RX_BUFFER_SIZE);
if (!buff)
return -1;
buff_phys = (unsigned char *)MV_ALIGN_UP((MV_ULONG)buff, BM_ALIGN);
mvBmPoolPut(EGIGA_BM_POOL, (MV_ULONG)buff_phys, (MV_ULONG)buff);
}
return 0;
}
/***********************************************************
* mv_eth_bm_stop -- *
* empty BM pool and stop its activity *
***********************************************************/
static void mv_eth_bm_stop(MV_VOID)
{
int i;
unsigned char *buff;
for (i = 0; i < EGIGA_BM_SIZE; i++) {
buff = (unsigned char *)mvBmPoolGet(EGIGA_BM_POOL, NULL);
free(buff);
}
mvBmPoolControl(EGIGA_BM_POOL, MV_STOP);
}
/***********************************************************
* mv_eth_initialize -- *
* main driver initialization. loading the interfaces. *
***********************************************************/
int mv_eth_initialize(bd_t *bis)
{
int port;
MV_8 *enet_addr;
MV_8 name[NAMESIZE+1];
MV_8 enetvar[9];
/* HAL init + port power up + port win init */
mvSysPp2Init();
mv_eth_bm_init();
/* Parser default initialization */
if (mvPrsDefaultInit())
printf("Warning PARSER default init failed\n");
if (mvPp2ClassifierDefInit())
printf("Warning Classifier defauld init failed\n");
if (mvPp2AggrTxqInit(0/*cpu*/, EGIGA_TXQ_LEN) == NULL) {
printf("Error failed to init aggr TXQ\n");
return -1;
}
for (port = 0; port < mvCtrlEthMaxPortGet(); port++) {
if (MV_PP2_IS_PON_PORT(port) || mvBoardIsPortLoopback(port))
continue;
if (mvBoardIsEthActive(port) == MV_FALSE)
continue;
if (mvCtrlPwrClckGet(ETH_GIG_UNIT_ID, port) == MV_FALSE)
continue;
/* interface name */
sprintf(name, "egiga%d", port);
/* interface MAC addr extract */
sprintf(enetvar, port ? "eth%daddr" : "ethaddr", port);
enet_addr = getenv(enetvar);
mvEgigaLoad(port, name, enet_addr);
}
return 0;
}
/***********************************************************
* mvEgigaLoad -- *
* load a network interface into uboot network core. *
* initialize sw structures e.g. private, rings, etc. *
***********************************************************/
static int mvEgigaLoad(int port, char *name, char *enet_addr)
{
struct eth_device *dev = NULL;
egigaPriv *priv = NULL;
/* First disable GMAC */
mvGmacPortDisable(priv->port);
dev = malloc(sizeof(struct eth_device));
if (!dev) {
printf("%s: %s falied to alloc eth_device (error)\n", __func__, name);
goto error;
}
priv = malloc(sizeof(egigaPriv));
if (!priv) {
printf("%s: %s falied to alloc egiga_priv (error)\n", __func__, name);
goto error;
}
memset(priv, 0, sizeof(egigaPriv));
/* init device methods */
memcpy(dev->name, name, NAMESIZE);
mvMacStrToHex(enet_addr, (MV_U8 *)(dev->enetaddr));
dev->init = (void *)mvEgigaInit;
dev->halt = (void *)mvEgigaHalt;
dev->send = (void *)mvEgigaTx;
dev->recv = (void *)mvEgigaRx;
dev->priv = priv;
dev->iobase = 0;
dev->write_hwaddr = 0;
priv->port = port;
priv->devInit = MV_FALSE;
priv->devEnable = MV_FALSE;
/* register the interface */
eth_register(dev);
return 0;
error:
printf("%s: %s load failed\n", __func__, name);
if (priv)
free(dev->priv);
if (dev)
free(dev);
return -1;
}
static int mvEgigaInit(struct eth_device *dev, bd_t *p)
{
egigaPriv *priv = dev->priv;
MV_STATUS status;
int i, phys_queue, phys_port;
MV_BOARD_MAC_SPEED mac_speed;
MV_ETH_PORT_SPEED speed = 0;
MV_32 phy_addr;
static MV_U8 mac_bcast[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
mv_eth_bm_start();
/* init each port only once */
if (priv->devInit != MV_TRUE) {
/* port power up - release from reset */
if (!MV_PP2_IS_PON_PORT(priv->port)) {
phy_addr = mvBoardPhyAddrGet(priv->port);
if (phy_addr != -1) {
mvGmacPhyAddrSet(priv->port, phy_addr);
mvEthPhyInit(priv->port, MV_FALSE);
}
mvGmacPortPowerUp(priv->port,
mvBoardIsPortInSgmii(priv->port),
MV_FALSE/*mvBoardIsPortInRgmii(priv->port)*/);
}
/* init the hal -- create internal port control structure and descriptor rings, */
/* open address decode windows, disable rx and tx operations. mask interrupts. */
priv->halPriv = mvPp2PortInit(priv->port, priv->port * CONFIG_MV_ETH_RXQ, CONFIG_MV_ETH_RXQ, NULL);
if (!priv->halPriv) {
printf("falied to init eth port (error)\n");
goto error;
}
/* after init port - init rx & tx */
MV_PP2_PHYS_RXQ_CTRL *rxqCtrl = mvPp2RxqInit(priv->port, EGIGA_DEF_RXQ, EGIGA_RXQ_LEN);
if (!rxqCtrl) {
printf("Rxq Init Failed\n");
goto error;
}
MV_PP2_PHYS_TXQ_CTRL *txqCtrl =
mvPp2TxqInit(priv->port, EGIGA_DEF_TXP, EGIGA_DEF_TXQ, EGIGA_TXQ_LEN, EGIGA_TXQ_HWF_LEN);
if (!txqCtrl) {
printf("Txq Init Failed\n");
goto error;
}
mvPp2RxqBmLongPoolSet(priv->port, EGIGA_DEF_RXQ, EGIGA_BM_POOL);
mvPp2RxqBmShortPoolSet(priv->port, EGIGA_DEF_RXQ, EGIGA_BM_POOL);
mvPp2RxqNonOccupDescAdd(priv->port, EGIGA_DEF_RXQ, EGIGA_RXQ_LEN);
mvPp2TxpMaxTxSizeSet(priv->port, EGIGA_DEF_TXP, RX_PKT_SIZE);
priv->devInit = MV_TRUE;
}
mac_speed = mvBoardMacSpeedGet(priv->port);
if (mac_speed != BOARD_MAC_SPEED_AUTO) {
switch (mac_speed) {
case BOARD_MAC_SPEED_10M:
speed = MV_ETH_SPEED_10;
break;
case BOARD_MAC_SPEED_100M:
speed = MV_ETH_SPEED_100;
break;
case BOARD_MAC_SPEED_1000M:
case BOARD_MAC_SPEED_2000M:
speed = MV_ETH_SPEED_1000;
break;
default:
/* do nothing */
break;
}
if (mvGmacForceLinkModeSet(priv->port, MV_TRUE, MV_FALSE)) {
printf("mvEthForceLinkModeSet failed\n");
goto error;
}
if (mvGmacSpeedDuplexSet(priv->port, speed, MV_ETH_DUPLEX_FULL)) {
printf("mvEthSpeedDuplexSet failed\n");
goto error;
}
if (mvGmacFlowCtrlSet(priv->port, MV_ETH_FC_DISABLE)) {
printf("mvEthFlowCtrlSet failed\n");
goto error;
}
}
/* allow new packets to RXQs */
mvPp2PortIngressEnable(priv->port, 1);
/* classifier port default config */
phys_queue = mvPp2LogicRxqToPhysRxq(priv->port, EGIGA_DEF_RXQ);
phys_port = MV_PPV2_PORT_PHYS(priv->port);
mvPp2ClsHwPortDefConfig(phys_port, 0, FLOWID_DEF(phys_port), phys_queue);
if (mvPrsMacDaAccept(phys_port, mac_bcast, 1 /*add*/)) {
printf("%s: mvPrsMacDaAccept failed\n", dev->name);
return -1;
}
if (mvPrsMacDaAccept(phys_port, dev->enetaddr, 1 /*add*/)) {
printf("%s: mvPrsMacDaAccept failed\n", dev->name);
return -1;
}
if (mvPrsDefFlow(phys_port)) {
printf("%s: mvPp2PrsDefFlow failed\n", dev->name);
return -1;
}
/* start the hal - rx/tx activity */
/* Check if link is up for 2 Sec */
for (i = 1; i < 100; i++) {
status = mvPp2PortEnable(priv->port, 1);
if (status == MV_OK) {
priv->devEnable = MV_TRUE;
break;
}
mvOsDelay(20);
}
if (status != MV_OK) {
printf("%s: %s mvPp2PortEnable failed (error)\n", __func__, dev->name);
goto error;
}
mvPp2PortEgressEnable(priv->port, 1);
return 1;
error:
if (priv->devInit)
mvEgigaHalt(dev);
printf("%s: %s failed\n", __func__, dev->name);
return -1;
}
static int mvEgigaHalt(struct eth_device *dev)
{
egigaPriv *priv = dev->priv;
if (priv->devInit == MV_TRUE && priv->devEnable == MV_TRUE) {
/* stop new packets from arriving to RXQs */
mvPp2PortIngressEnable(priv->port, 0);
/* stop the port activity, mask all interrupts */
if (mvPp2PortEnable(priv->port, 0) != MV_OK)
printf("mvPp2PortDisable failed (error)\n");
priv->devEnable = MV_FALSE;
}
mv_eth_bm_stop();
mvGmacPortDisable(priv->port);
return 0;
}
static int mvEgigaTx(struct eth_device *dev, volatile void *buf, int len)
{
egigaPriv *priv = dev->priv;
MV_U32 timeout = 0;
int txDone;
MV_PP2_AGGR_TXQ_CTRL *pAggrTxq;
PP2_TX_DESC *pDesc;
if (priv->devInit != MV_TRUE || priv->devEnable != MV_TRUE)
return 0; /* port is not initialized or not enabled */
pAggrTxq = mvPp2AggrTxqHndlGet(0);
/* get next descriptor */
pDesc = mvPp2AggrTxqNextDescGet(pAggrTxq);
if (pDesc == NULL) {
printf("No available descriptors\n");
goto error;
}
/* set descriptor fields */
pDesc->command = 0 | PP2_TX_L4_CSUM_NOT | PP2_TX_F_DESC_MASK | PP2_TX_L_DESC_MASK;
pDesc->dataSize = len;
pDesc->pktOffset = (MV_U32)buf & EGIGA_TX_DESC_ALIGN;
pDesc->bufPhysAddr = (MV_U32)buf & (~EGIGA_TX_DESC_ALIGN);
pDesc->bufCookie = (MV_U32)buf;
pDesc->physTxq = MV_PPV2_TXQ_PHYS(priv->port, EGIGA_DEF_TXP, EGIGA_DEF_TXQ);
mvOsCacheFlush(NULL, (void *)buf, len);
#if defined(MV_CPU_BE)
mvNetaTxqDescSwap(pDesc);//TODO
#endif /* MV_CPU_BE */
mvOsCacheLineFlush(NULL, (void *)pDesc);
/* send */
mvPp2AggrTxqPendDescAdd(1);
/* Enable TXQ drain */
mvPp2TxqDrainSet(priv->port, EGIGA_DEF_TXP, EGIGA_DEF_TXQ, MV_TRUE);
/* Tx done processing */
/* wait for agrregated to physical TXQ transfer */
txDone = mvPp2AggrTxqPendDescNumGet(0);
while (txDone) {
if (timeout++ > 10000) {
printf("timeout: packet not sent from aggregated to phys TXQ\n");
goto error;
}
txDone = mvPp2AggrTxqPendDescNumGet(0);
}
/* Disable TXQ drain */
mvPp2TxqDrainSet(priv->port, EGIGA_DEF_TXP, EGIGA_DEF_TXQ, MV_FALSE);
timeout = 0;
txDone = mvPp2TxqSentDescProc(priv->port, EGIGA_DEF_TXP, EGIGA_DEF_TXQ);
/* wait for packet to be transmitted */
while (!txDone) {
if (timeout++ > 10000) {
printf("timeout: packet not sent\n");
goto error;
}
txDone = mvPp2TxqSentDescProc(priv->port, EGIGA_DEF_TXP, EGIGA_DEF_TXQ);
}
/* txDone has increased - hw sent packet */
return 0;
error:
printf("%s: %s failed\n", __func__, dev->name);
/* mvNetaTxpReset(priv->port, EGIGA_DEF_TXP); */
return 1;
}
static int mvEgigaRx(struct eth_device *dev)
{
egigaPriv *priv = dev->priv;
MV_U8 *pkt;
int packets_done = 0;
int num_recieved_packets, pool_id;
MV_U32 status;
MV_PP2_PHYS_RXQ_CTRL *pRxq;
PP2_RX_DESC *pDesc;
if (priv->devInit != MV_TRUE || priv->devEnable != MV_TRUE)
return 0; /* port is not initialized or not enabled */
pRxq = mvPp2RxqHndlGet(priv->port, EGIGA_DEF_RXQ);
num_recieved_packets = mvPp2RxqBusyDescNumGet(priv->port, EGIGA_DEF_RXQ);
packets_done = num_recieved_packets;
while (num_recieved_packets--) {
pDesc = mvPp2RxqNextDescGet(pRxq);
/* cache invalidate - descriptor */
mvOsCacheLineInv(NULL, pDesc);
#if defined(MV_CPU_BE)
mvNetaRxqDescSwap(pDesc);//TODO
#endif /* MV_CPU_BE */
status = pDesc->status;
/* drop packets with error or with buffer header (MC, SG) */
if ((status & PP2_RX_BUF_HDR_MASK) || (status & PP2_RX_ES_MASK)) {
#if defined(MV_CPU_BE)
mvNetaRxqDescSwap(pDesc);//TODO
#endif /* MV_CPU_BE */
mvOsCacheLineFlushInv(NULL, pDesc);
continue;
}
/* TODO: drop fragmented packets */
/* cache invalidate - packet */
mvOsCacheInvalidate(NULL, (void *)pDesc->bufPhysAddr, RX_BUFFER_SIZE);
/* give packet to stack - skip on first 2 bytes + buffer header */
pkt = ((MV_U8 *)pDesc->bufPhysAddr) + 2 + BUFF_HDR_OFFS;
NetReceive(pkt, (int)pDesc->dataSize - 2);
/* refill: pass packet back to BM */
pool_id = (status & PP2_RX_BM_POOL_ALL_MASK) >> PP2_RX_BM_POOL_ID_OFFS;
mvBmPoolPut(pool_id, (MV_ULONG) pDesc->bufPhysAddr, (MV_ULONG) pDesc->bufCookie);
/* cache invalidate - packet */
#if defined(MV_CPU_BE)
mvNetaRxqDescSwap(pDesc);//TODO
#endif /* MV_CPU_BE */
mvOsCacheInvalidate(NULL, (void *)pDesc->bufPhysAddr, RX_BUFFER_SIZE);
}
/* cache invalidate - descriptor */
mvOsCacheLineInv(NULL, pDesc);
mvPp2RxqDescNumUpdate(priv->port, EGIGA_DEF_RXQ, packets_done, packets_done);
return 0;
}
#endif /* #if defined (MV_INCLUDE_GIG_ETH) */