mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-08-22 09:58:52 +09:00
![Joe Hershberger](/assets/img/avatar_default.png)
These are all the files which use the API incorrectly but did not get built using MAKEALL -a powerpc|arm. I have no compiler for them, but the remaining issues should be far less than without this patch. Any outstanding issues are left to the maintainers of boards that use these drivers. Signed-off-by: Joe Hershberger <joe.hershberger@ni.com>
3163 lines
103 KiB
C
3163 lines
103 KiB
C
/*
|
|
* (C) Copyright 2003
|
|
* Ingo Assmus <ingo.assmus@keymile.com>
|
|
*
|
|
* based on - Driver for MV64360X ethernet ports
|
|
* Copyright (C) 2002 rabeeh@galileo.co.il
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
/*
|
|
* mv_eth.c - header file for the polled mode GT ethernet driver
|
|
*/
|
|
#include <common.h>
|
|
#include <net.h>
|
|
#include <malloc.h>
|
|
|
|
#include "mv_eth.h"
|
|
|
|
/* enable Debug outputs */
|
|
|
|
#undef DEBUG_MV_ETH
|
|
|
|
#ifdef DEBUG_MV_ETH
|
|
#define DEBUG
|
|
#define DP(x) x
|
|
#else
|
|
#define DP(x)
|
|
#endif
|
|
|
|
#undef MV64360_CHECKSUM_OFFLOAD
|
|
/*************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* The first part is the high level driver of the gigE ethernet ports. *
|
|
**************************************************************************
|
|
**************************************************************************
|
|
*************************************************************************/
|
|
|
|
/* Definition for configuring driver */
|
|
/* #define UPDATE_STATS_BY_SOFTWARE */
|
|
#undef MV64360_RX_QUEUE_FILL_ON_TASK
|
|
|
|
|
|
/* Constants */
|
|
#define MAGIC_ETH_RUNNING 8031971
|
|
#define MV64360_INTERNAL_SRAM_SIZE _256K
|
|
#define EXTRA_BYTES 32
|
|
#define WRAP ETH_HLEN + 2 + 4 + 16
|
|
#define BUFFER_MTU dev->mtu + WRAP
|
|
#define INT_CAUSE_UNMASK_ALL 0x0007ffff
|
|
#define INT_CAUSE_UNMASK_ALL_EXT 0x0011ffff
|
|
#ifdef MV64360_RX_FILL_ON_TASK
|
|
#define INT_CAUSE_MASK_ALL 0x00000000
|
|
#define INT_CAUSE_CHECK_BITS INT_CAUSE_UNMASK_ALL
|
|
#define INT_CAUSE_CHECK_BITS_EXT INT_CAUSE_UNMASK_ALL_EXT
|
|
#endif
|
|
|
|
/* Read/Write to/from MV64360 internal registers */
|
|
#define MV_REG_READ(offset) my_le32_to_cpu(* (volatile unsigned int *) (INTERNAL_REG_BASE_ADDR + offset))
|
|
#define MV_REG_WRITE(offset,data) *(volatile unsigned int *) (INTERNAL_REG_BASE_ADDR + offset) = my_cpu_to_le32 (data)
|
|
#define MV_SET_REG_BITS(regOffset,bits) ((*((volatile unsigned int*)((INTERNAL_REG_BASE_ADDR) + (regOffset)))) |= ((unsigned int)my_cpu_to_le32(bits)))
|
|
#define MV_RESET_REG_BITS(regOffset,bits) ((*((volatile unsigned int*)((INTERNAL_REG_BASE_ADDR) + (regOffset)))) &= ~((unsigned int)my_cpu_to_le32(bits)))
|
|
|
|
/* Static function declarations */
|
|
static int mv64360_eth_real_open (struct eth_device *eth);
|
|
static int mv64360_eth_real_stop (struct eth_device *eth);
|
|
static struct net_device_stats *mv64360_eth_get_stats (struct eth_device
|
|
*dev);
|
|
static void eth_port_init_mac_tables (ETH_PORT eth_port_num);
|
|
static void mv64360_eth_update_stat (struct eth_device *dev);
|
|
bool db64360_eth_start (struct eth_device *eth);
|
|
unsigned int eth_read_mib_counter (ETH_PORT eth_port_num,
|
|
unsigned int mib_offset);
|
|
int mv64360_eth_receive (struct eth_device *dev);
|
|
|
|
int mv64360_eth_xmit (struct eth_device *, volatile void *packet, int length);
|
|
|
|
#ifndef UPDATE_STATS_BY_SOFTWARE
|
|
static void mv64360_eth_print_stat (struct eth_device *dev);
|
|
#endif
|
|
|
|
extern unsigned int INTERNAL_REG_BASE_ADDR;
|
|
|
|
/*************************************************
|
|
*Helper functions - used inside the driver only *
|
|
*************************************************/
|
|
#ifdef DEBUG_MV_ETH
|
|
void print_globals (struct eth_device *dev)
|
|
{
|
|
printf ("Ethernet PRINT_Globals-Debug function\n");
|
|
printf ("Base Address for ETH_PORT_INFO: %08x\n",
|
|
(unsigned int) dev->priv);
|
|
printf ("Base Address for mv64360_eth_priv: %08x\n",
|
|
(unsigned int) &(((ETH_PORT_INFO *) dev->priv)->
|
|
port_private));
|
|
|
|
printf ("GT Internal Base Address: %08x\n",
|
|
INTERNAL_REG_BASE_ADDR);
|
|
printf ("Base Address for TX-DESCs: %08x Number of allocated Buffers %d\n", (unsigned int) ((ETH_PORT_INFO *) dev->priv)->p_tx_desc_area_base[0], MV64360_TX_QUEUE_SIZE);
|
|
printf ("Base Address for RX-DESCs: %08x Number of allocated Buffers %d\n", (unsigned int) ((ETH_PORT_INFO *) dev->priv)->p_rx_desc_area_base[0], MV64360_RX_QUEUE_SIZE);
|
|
printf ("Base Address for RX-Buffer: %08x allocated Bytes %d\n",
|
|
(unsigned int) ((ETH_PORT_INFO *) dev->priv)->
|
|
p_rx_buffer_base[0],
|
|
(MV64360_RX_QUEUE_SIZE * MV64360_RX_BUFFER_SIZE) + 32);
|
|
printf ("Base Address for TX-Buffer: %08x allocated Bytes %d\n",
|
|
(unsigned int) ((ETH_PORT_INFO *) dev->priv)->
|
|
p_tx_buffer_base[0],
|
|
(MV64360_TX_QUEUE_SIZE * MV64360_TX_BUFFER_SIZE) + 32);
|
|
}
|
|
#endif
|
|
|
|
#define my_cpu_to_le32(x) my_le32_to_cpu((x))
|
|
|
|
unsigned long my_le32_to_cpu (unsigned long x)
|
|
{
|
|
return (((x & 0x000000ffU) << 24) |
|
|
((x & 0x0000ff00U) << 8) |
|
|
((x & 0x00ff0000U) >> 8) | ((x & 0xff000000U) >> 24));
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* mv64360_eth_print_phy_status
|
|
*
|
|
* Prints gigabit ethenret phy status
|
|
*
|
|
* Input : pointer to ethernet interface network device structure
|
|
* Output : N/A
|
|
**********************************************************************/
|
|
|
|
static void mv64360_eth_print_phy_status (struct eth_device *dev)
|
|
{
|
|
struct mv64360_eth_priv *port_private;
|
|
unsigned int port_num;
|
|
ETH_PORT_INFO *ethernet_private = (ETH_PORT_INFO *) dev->priv;
|
|
unsigned int port_status, phy_reg_data;
|
|
|
|
port_private =
|
|
(struct mv64360_eth_priv *) ethernet_private->port_private;
|
|
port_num = port_private->port_num;
|
|
|
|
/* Check Link status on phy */
|
|
eth_port_read_smi_reg (port_num, 1, &phy_reg_data);
|
|
if (!(phy_reg_data & 0x20)) {
|
|
printf ("Ethernet port changed link status to DOWN\n");
|
|
} else {
|
|
port_status =
|
|
MV_REG_READ (MV64360_ETH_PORT_STATUS_REG (port_num));
|
|
printf ("Ethernet status port %d: Link up", port_num);
|
|
printf (", %s",
|
|
(port_status & BIT2) ? "Full Duplex" : "Half Duplex");
|
|
if (port_status & BIT4)
|
|
printf (", Speed 1 Gbps");
|
|
else
|
|
printf (", %s",
|
|
(port_status & BIT5) ? "Speed 100 Mbps" :
|
|
"Speed 10 Mbps");
|
|
printf ("\n");
|
|
}
|
|
}
|
|
|
|
/**********************************************************************
|
|
* u-boot entry functions for mv64360_eth
|
|
*
|
|
**********************************************************************/
|
|
int db64360_eth_probe (struct eth_device *dev)
|
|
{
|
|
return ((int) db64360_eth_start (dev));
|
|
}
|
|
|
|
int db64360_eth_poll (struct eth_device *dev)
|
|
{
|
|
return mv64360_eth_receive (dev);
|
|
}
|
|
|
|
int db64360_eth_transmit(struct eth_device *dev, void *packet, int length)
|
|
{
|
|
mv64360_eth_xmit (dev, packet, length);
|
|
return 0;
|
|
}
|
|
|
|
void db64360_eth_disable (struct eth_device *dev)
|
|
{
|
|
mv64360_eth_stop (dev);
|
|
}
|
|
|
|
|
|
void mv6436x_eth_initialize (bd_t * bis)
|
|
{
|
|
struct eth_device *dev;
|
|
ETH_PORT_INFO *ethernet_private;
|
|
struct mv64360_eth_priv *port_private;
|
|
int devnum, x, temp;
|
|
char *s, *e, buf[64];
|
|
|
|
for (devnum = 0; devnum < MV_ETH_DEVS; devnum++) {
|
|
dev = calloc (sizeof (*dev), 1);
|
|
if (!dev) {
|
|
printf ("%s: mv_enet%d allocation failure, %s\n",
|
|
__FUNCTION__, devnum, "eth_device structure");
|
|
return;
|
|
}
|
|
|
|
/* must be less than sizeof(dev->name) */
|
|
sprintf (dev->name, "mv_enet%d", devnum);
|
|
|
|
#ifdef DEBUG
|
|
printf ("Initializing %s\n", dev->name);
|
|
#endif
|
|
|
|
/* Extract the MAC address from the environment */
|
|
switch (devnum) {
|
|
case 0:
|
|
s = "ethaddr";
|
|
break;
|
|
|
|
case 1:
|
|
s = "eth1addr";
|
|
break;
|
|
|
|
case 2:
|
|
s = "eth2addr";
|
|
break;
|
|
|
|
default: /* this should never happen */
|
|
printf ("%s: Invalid device number %d\n",
|
|
__FUNCTION__, devnum);
|
|
return;
|
|
}
|
|
|
|
temp = getenv_f(s, buf, sizeof (buf));
|
|
s = (temp > 0) ? buf : NULL;
|
|
|
|
#ifdef DEBUG
|
|
printf ("Setting MAC %d to %s\n", devnum, s);
|
|
#endif
|
|
for (x = 0; x < 6; ++x) {
|
|
dev->enetaddr[x] = s ? simple_strtoul (s, &e, 16) : 0;
|
|
if (s)
|
|
s = (*e) ? e + 1 : e;
|
|
}
|
|
/* ronen - set the MAC addr in the HW */
|
|
eth_port_uc_addr_set (devnum, dev->enetaddr, 0);
|
|
|
|
dev->init = (void *) db64360_eth_probe;
|
|
dev->halt = (void *) ethernet_phy_reset;
|
|
dev->send = (void *) db64360_eth_transmit;
|
|
dev->recv = (void *) db64360_eth_poll;
|
|
|
|
ethernet_private =
|
|
calloc (sizeof (*ethernet_private), 1);
|
|
dev->priv = (void *) ethernet_private;
|
|
if (!ethernet_private) {
|
|
printf ("%s: %s allocation failure, %s\n",
|
|
__FUNCTION__, dev->name,
|
|
"Private Device Structure");
|
|
free (dev);
|
|
return;
|
|
}
|
|
/* start with an zeroed ETH_PORT_INFO */
|
|
memset (ethernet_private, 0, sizeof (ETH_PORT_INFO));
|
|
memcpy (ethernet_private->port_mac_addr, dev->enetaddr, 6);
|
|
|
|
/* set pointer to memory for stats data structure etc... */
|
|
port_private =
|
|
calloc (sizeof (*ethernet_private), 1);
|
|
ethernet_private->port_private = (void *)port_private;
|
|
if (!port_private) {
|
|
printf ("%s: %s allocation failure, %s\n",
|
|
__FUNCTION__, dev->name,
|
|
"Port Private Device Structure");
|
|
|
|
free (ethernet_private);
|
|
free (dev);
|
|
return;
|
|
}
|
|
|
|
port_private->stats =
|
|
calloc (sizeof (struct net_device_stats), 1);
|
|
if (!port_private->stats) {
|
|
printf ("%s: %s allocation failure, %s\n",
|
|
__FUNCTION__, dev->name,
|
|
"Net stat Structure");
|
|
|
|
free (port_private);
|
|
free (ethernet_private);
|
|
free (dev);
|
|
return;
|
|
}
|
|
memset (ethernet_private->port_private, 0,
|
|
sizeof (struct mv64360_eth_priv));
|
|
switch (devnum) {
|
|
case 0:
|
|
ethernet_private->port_num = ETH_0;
|
|
break;
|
|
case 1:
|
|
ethernet_private->port_num = ETH_1;
|
|
break;
|
|
case 2:
|
|
ethernet_private->port_num = ETH_2;
|
|
break;
|
|
default:
|
|
printf ("Invalid device number %d\n", devnum);
|
|
break;
|
|
};
|
|
|
|
port_private->port_num = devnum;
|
|
/*
|
|
* Read MIB counter on the GT in order to reset them,
|
|
* then zero all the stats fields in memory
|
|
*/
|
|
mv64360_eth_update_stat (dev);
|
|
memset (port_private->stats, 0,
|
|
sizeof (struct net_device_stats));
|
|
/* Extract the MAC address from the environment */
|
|
switch (devnum) {
|
|
case 0:
|
|
s = "ethaddr";
|
|
break;
|
|
|
|
case 1:
|
|
s = "eth1addr";
|
|
break;
|
|
|
|
case 2:
|
|
s = "eth2addr";
|
|
break;
|
|
|
|
default: /* this should never happen */
|
|
printf ("%s: Invalid device number %d\n",
|
|
__FUNCTION__, devnum);
|
|
return;
|
|
}
|
|
|
|
temp = getenv_f(s, buf, sizeof (buf));
|
|
s = (temp > 0) ? buf : NULL;
|
|
|
|
#ifdef DEBUG
|
|
printf ("Setting MAC %d to %s\n", devnum, s);
|
|
#endif
|
|
for (x = 0; x < 6; ++x) {
|
|
dev->enetaddr[x] = s ? simple_strtoul (s, &e, 16) : 0;
|
|
if (s)
|
|
s = (*e) ? e + 1 : e;
|
|
}
|
|
|
|
DP (printf ("Allocating descriptor and buffer rings\n"));
|
|
|
|
ethernet_private->p_rx_desc_area_base[0] =
|
|
(ETH_RX_DESC *) memalign (16,
|
|
RX_DESC_ALIGNED_SIZE *
|
|
MV64360_RX_QUEUE_SIZE + 1);
|
|
ethernet_private->p_tx_desc_area_base[0] =
|
|
(ETH_TX_DESC *) memalign (16,
|
|
TX_DESC_ALIGNED_SIZE *
|
|
MV64360_TX_QUEUE_SIZE + 1);
|
|
|
|
ethernet_private->p_rx_buffer_base[0] =
|
|
(char *) memalign (16,
|
|
MV64360_RX_QUEUE_SIZE *
|
|
MV64360_TX_BUFFER_SIZE + 1);
|
|
ethernet_private->p_tx_buffer_base[0] =
|
|
(char *) memalign (16,
|
|
MV64360_RX_QUEUE_SIZE *
|
|
MV64360_TX_BUFFER_SIZE + 1);
|
|
|
|
#ifdef DEBUG_MV_ETH
|
|
/* DEBUG OUTPUT prints adresses of globals */
|
|
print_globals (dev);
|
|
#endif
|
|
eth_register (dev);
|
|
|
|
}
|
|
DP (printf ("%s: exit\n", __FUNCTION__));
|
|
|
|
}
|
|
|
|
/**********************************************************************
|
|
* mv64360_eth_open
|
|
*
|
|
* This function is called when openning the network device. The function
|
|
* should initialize all the hardware, initialize cyclic Rx/Tx
|
|
* descriptors chain and buffers and allocate an IRQ to the network
|
|
* device.
|
|
*
|
|
* Input : a pointer to the network device structure
|
|
* / / ronen - changed the output to match net/eth.c needs
|
|
* Output : nonzero of success , zero if fails.
|
|
* under construction
|
|
**********************************************************************/
|
|
|
|
int mv64360_eth_open (struct eth_device *dev)
|
|
{
|
|
return (mv64360_eth_real_open (dev));
|
|
}
|
|
|
|
/* Helper function for mv64360_eth_open */
|
|
static int mv64360_eth_real_open (struct eth_device *dev)
|
|
{
|
|
|
|
unsigned int queue;
|
|
ETH_PORT_INFO *ethernet_private;
|
|
struct mv64360_eth_priv *port_private;
|
|
unsigned int port_num;
|
|
u32 phy_reg_data;
|
|
|
|
ethernet_private = (ETH_PORT_INFO *) dev->priv;
|
|
/* ronen - when we update the MAC env params we only update dev->enetaddr
|
|
see ./net/eth.c eth_set_enetaddr() */
|
|
memcpy (ethernet_private->port_mac_addr, dev->enetaddr, 6);
|
|
|
|
port_private =
|
|
(struct mv64360_eth_priv *) ethernet_private->port_private;
|
|
port_num = port_private->port_num;
|
|
|
|
/* Stop RX Queues */
|
|
MV_REG_WRITE (MV64360_ETH_RECEIVE_QUEUE_COMMAND_REG (port_num),
|
|
0x0000ff00);
|
|
|
|
/* Clear the ethernet port interrupts */
|
|
MV_REG_WRITE (MV64360_ETH_INTERRUPT_CAUSE_REG (port_num), 0);
|
|
MV_REG_WRITE (MV64360_ETH_INTERRUPT_CAUSE_EXTEND_REG (port_num), 0);
|
|
|
|
/* Unmask RX buffer and TX end interrupt */
|
|
MV_REG_WRITE (MV64360_ETH_INTERRUPT_MASK_REG (port_num),
|
|
INT_CAUSE_UNMASK_ALL);
|
|
|
|
/* Unmask phy and link status changes interrupts */
|
|
MV_REG_WRITE (MV64360_ETH_INTERRUPT_EXTEND_MASK_REG (port_num),
|
|
INT_CAUSE_UNMASK_ALL_EXT);
|
|
|
|
/* Set phy address of the port */
|
|
ethernet_private->port_phy_addr = 0x8 + port_num;
|
|
|
|
/* Activate the DMA channels etc */
|
|
eth_port_init (ethernet_private);
|
|
|
|
|
|
/* "Allocate" setup TX rings */
|
|
|
|
for (queue = 0; queue < MV64360_TX_QUEUE_NUM; queue++) {
|
|
unsigned int size;
|
|
|
|
port_private->tx_ring_size[queue] = MV64360_TX_QUEUE_SIZE;
|
|
size = (port_private->tx_ring_size[queue] * TX_DESC_ALIGNED_SIZE); /*size = no of DESCs times DESC-size */
|
|
ethernet_private->tx_desc_area_size[queue] = size;
|
|
|
|
/* first clear desc area completely */
|
|
memset ((void *) ethernet_private->p_tx_desc_area_base[queue],
|
|
0, ethernet_private->tx_desc_area_size[queue]);
|
|
|
|
/* initialize tx desc ring with low level driver */
|
|
if (ether_init_tx_desc_ring
|
|
(ethernet_private, ETH_Q0,
|
|
port_private->tx_ring_size[queue],
|
|
MV64360_TX_BUFFER_SIZE /* Each Buffer is 1600 Byte */ ,
|
|
(unsigned int) ethernet_private->
|
|
p_tx_desc_area_base[queue],
|
|
(unsigned int) ethernet_private->
|
|
p_tx_buffer_base[queue]) == false)
|
|
printf ("### Error initializing TX Ring\n");
|
|
}
|
|
|
|
/* "Allocate" setup RX rings */
|
|
for (queue = 0; queue < MV64360_RX_QUEUE_NUM; queue++) {
|
|
unsigned int size;
|
|
|
|
/* Meantime RX Ring are fixed - but must be configurable by user */
|
|
port_private->rx_ring_size[queue] = MV64360_RX_QUEUE_SIZE;
|
|
size = (port_private->rx_ring_size[queue] *
|
|
RX_DESC_ALIGNED_SIZE);
|
|
ethernet_private->rx_desc_area_size[queue] = size;
|
|
|
|
/* first clear desc area completely */
|
|
memset ((void *) ethernet_private->p_rx_desc_area_base[queue],
|
|
0, ethernet_private->rx_desc_area_size[queue]);
|
|
if ((ether_init_rx_desc_ring
|
|
(ethernet_private, ETH_Q0,
|
|
port_private->rx_ring_size[queue],
|
|
MV64360_RX_BUFFER_SIZE /* Each Buffer is 1600 Byte */ ,
|
|
(unsigned int) ethernet_private->
|
|
p_rx_desc_area_base[queue],
|
|
(unsigned int) ethernet_private->
|
|
p_rx_buffer_base[queue])) == false)
|
|
printf ("### Error initializing RX Ring\n");
|
|
}
|
|
|
|
eth_port_start (ethernet_private);
|
|
|
|
/* Set maximum receive buffer to 9700 bytes */
|
|
MV_REG_WRITE (MV64360_ETH_PORT_SERIAL_CONTROL_REG (port_num),
|
|
(0x5 << 17) |
|
|
(MV_REG_READ
|
|
(MV64360_ETH_PORT_SERIAL_CONTROL_REG (port_num))
|
|
& 0xfff1ffff));
|
|
|
|
/*
|
|
* Set ethernet MTU for leaky bucket mechanism to 0 - this will
|
|
* disable the leaky bucket mechanism .
|
|
*/
|
|
|
|
MV_REG_WRITE (MV64360_ETH_MAXIMUM_TRANSMIT_UNIT (port_num), 0);
|
|
MV_REG_READ (MV64360_ETH_PORT_STATUS_REG (port_num));
|
|
|
|
/* Check Link status on phy */
|
|
eth_port_read_smi_reg (port_num, 1, &phy_reg_data);
|
|
if (!(phy_reg_data & 0x20)) {
|
|
/* Reset PHY */
|
|
if ((ethernet_phy_reset (port_num)) != true) {
|
|
printf ("$$ Warnning: No link on port %d \n",
|
|
port_num);
|
|
return 0;
|
|
} else {
|
|
eth_port_read_smi_reg (port_num, 1, &phy_reg_data);
|
|
if (!(phy_reg_data & 0x20)) {
|
|
printf ("### Error: Phy is not active\n");
|
|
return 0;
|
|
}
|
|
}
|
|
} else {
|
|
mv64360_eth_print_phy_status (dev);
|
|
}
|
|
port_private->eth_running = MAGIC_ETH_RUNNING;
|
|
return 1;
|
|
}
|
|
|
|
|
|
static int mv64360_eth_free_tx_rings (struct eth_device *dev)
|
|
{
|
|
unsigned int queue;
|
|
ETH_PORT_INFO *ethernet_private;
|
|
struct mv64360_eth_priv *port_private;
|
|
unsigned int port_num;
|
|
volatile ETH_TX_DESC *p_tx_curr_desc;
|
|
|
|
ethernet_private = (ETH_PORT_INFO *) dev->priv;
|
|
port_private =
|
|
(struct mv64360_eth_priv *) ethernet_private->port_private;
|
|
port_num = port_private->port_num;
|
|
|
|
/* Stop Tx Queues */
|
|
MV_REG_WRITE (MV64360_ETH_TRANSMIT_QUEUE_COMMAND_REG (port_num),
|
|
0x0000ff00);
|
|
|
|
/* Free TX rings */
|
|
DP (printf ("Clearing previously allocated TX queues... "));
|
|
for (queue = 0; queue < MV64360_TX_QUEUE_NUM; queue++) {
|
|
/* Free on TX rings */
|
|
for (p_tx_curr_desc =
|
|
ethernet_private->p_tx_desc_area_base[queue];
|
|
((unsigned int) p_tx_curr_desc <= (unsigned int)
|
|
ethernet_private->p_tx_desc_area_base[queue] +
|
|
ethernet_private->tx_desc_area_size[queue]);
|
|
p_tx_curr_desc =
|
|
(ETH_TX_DESC *) ((unsigned int) p_tx_curr_desc +
|
|
TX_DESC_ALIGNED_SIZE)) {
|
|
/* this is inside for loop */
|
|
if (p_tx_curr_desc->return_info != 0) {
|
|
p_tx_curr_desc->return_info = 0;
|
|
DP (printf ("freed\n"));
|
|
}
|
|
}
|
|
DP (printf ("Done\n"));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int mv64360_eth_free_rx_rings (struct eth_device *dev)
|
|
{
|
|
unsigned int queue;
|
|
ETH_PORT_INFO *ethernet_private;
|
|
struct mv64360_eth_priv *port_private;
|
|
unsigned int port_num;
|
|
volatile ETH_RX_DESC *p_rx_curr_desc;
|
|
|
|
ethernet_private = (ETH_PORT_INFO *) dev->priv;
|
|
port_private =
|
|
(struct mv64360_eth_priv *) ethernet_private->port_private;
|
|
port_num = port_private->port_num;
|
|
|
|
|
|
/* Stop RX Queues */
|
|
MV_REG_WRITE (MV64360_ETH_RECEIVE_QUEUE_COMMAND_REG (port_num),
|
|
0x0000ff00);
|
|
|
|
/* Free RX rings */
|
|
DP (printf ("Clearing previously allocated RX queues... "));
|
|
for (queue = 0; queue < MV64360_RX_QUEUE_NUM; queue++) {
|
|
/* Free preallocated skb's on RX rings */
|
|
for (p_rx_curr_desc =
|
|
ethernet_private->p_rx_desc_area_base[queue];
|
|
(((unsigned int) p_rx_curr_desc <
|
|
((unsigned int) ethernet_private->
|
|
p_rx_desc_area_base[queue] +
|
|
ethernet_private->rx_desc_area_size[queue])));
|
|
p_rx_curr_desc =
|
|
(ETH_RX_DESC *) ((unsigned int) p_rx_curr_desc +
|
|
RX_DESC_ALIGNED_SIZE)) {
|
|
if (p_rx_curr_desc->return_info != 0) {
|
|
p_rx_curr_desc->return_info = 0;
|
|
DP (printf ("freed\n"));
|
|
}
|
|
}
|
|
DP (printf ("Done\n"));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**********************************************************************
|
|
* mv64360_eth_stop
|
|
*
|
|
* This function is used when closing the network device.
|
|
* It updates the hardware,
|
|
* release all memory that holds buffers and descriptors and release the IRQ.
|
|
* Input : a pointer to the device structure
|
|
* Output : zero if success , nonzero if fails
|
|
*********************************************************************/
|
|
|
|
int mv64360_eth_stop (struct eth_device *dev)
|
|
{
|
|
/* Disable all gigE address decoder */
|
|
MV_REG_WRITE (MV64360_ETH_BASE_ADDR_ENABLE_REG, 0x3f);
|
|
DP (printf ("%s Ethernet stop called ... \n", __FUNCTION__));
|
|
mv64360_eth_real_stop (dev);
|
|
|
|
return 0;
|
|
};
|
|
|
|
/* Helper function for mv64360_eth_stop */
|
|
|
|
static int mv64360_eth_real_stop (struct eth_device *dev)
|
|
{
|
|
ETH_PORT_INFO *ethernet_private;
|
|
struct mv64360_eth_priv *port_private;
|
|
unsigned int port_num;
|
|
|
|
ethernet_private = (ETH_PORT_INFO *) dev->priv;
|
|
port_private =
|
|
(struct mv64360_eth_priv *) ethernet_private->port_private;
|
|
port_num = port_private->port_num;
|
|
|
|
|
|
mv64360_eth_free_tx_rings (dev);
|
|
mv64360_eth_free_rx_rings (dev);
|
|
|
|
eth_port_reset (ethernet_private->port_num);
|
|
/* Disable ethernet port interrupts */
|
|
MV_REG_WRITE (MV64360_ETH_INTERRUPT_CAUSE_REG (port_num), 0);
|
|
MV_REG_WRITE (MV64360_ETH_INTERRUPT_CAUSE_EXTEND_REG (port_num), 0);
|
|
/* Mask RX buffer and TX end interrupt */
|
|
MV_REG_WRITE (MV64360_ETH_INTERRUPT_MASK_REG (port_num), 0);
|
|
/* Mask phy and link status changes interrupts */
|
|
MV_REG_WRITE (MV64360_ETH_INTERRUPT_EXTEND_MASK_REG (port_num), 0);
|
|
MV_RESET_REG_BITS (MV64360_CPU_INTERRUPT0_MASK_HIGH,
|
|
BIT0 << port_num);
|
|
/* Print Network statistics */
|
|
#ifndef UPDATE_STATS_BY_SOFTWARE
|
|
/*
|
|
* Print statistics (only if ethernet is running),
|
|
* then zero all the stats fields in memory
|
|
*/
|
|
if (port_private->eth_running == MAGIC_ETH_RUNNING) {
|
|
port_private->eth_running = 0;
|
|
mv64360_eth_print_stat (dev);
|
|
}
|
|
memset (port_private->stats, 0, sizeof (struct net_device_stats));
|
|
#endif
|
|
DP (printf ("\nEthernet stopped ... \n"));
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* mv64360_eth_start_xmit
|
|
*
|
|
* This function is queues a packet in the Tx descriptor for
|
|
* required port.
|
|
*
|
|
* Input : skb - a pointer to socket buffer
|
|
* dev - a pointer to the required port
|
|
*
|
|
* Output : zero upon success
|
|
**********************************************************************/
|
|
|
|
int mv64360_eth_xmit (struct eth_device *dev, volatile void *dataPtr,
|
|
int dataSize)
|
|
{
|
|
ETH_PORT_INFO *ethernet_private;
|
|
struct mv64360_eth_priv *port_private;
|
|
PKT_INFO pkt_info;
|
|
ETH_FUNC_RET_STATUS status;
|
|
struct net_device_stats *stats;
|
|
ETH_FUNC_RET_STATUS release_result;
|
|
|
|
ethernet_private = (ETH_PORT_INFO *) dev->priv;
|
|
port_private =
|
|
(struct mv64360_eth_priv *) ethernet_private->port_private;
|
|
|
|
stats = port_private->stats;
|
|
|
|
/* Update packet info data structure */
|
|
pkt_info.cmd_sts = ETH_TX_FIRST_DESC | ETH_TX_LAST_DESC; /* DMA owned, first last */
|
|
pkt_info.byte_cnt = dataSize;
|
|
pkt_info.buf_ptr = (unsigned int) dataPtr;
|
|
pkt_info.return_info = 0;
|
|
|
|
status = eth_port_send (ethernet_private, ETH_Q0, &pkt_info);
|
|
if ((status == ETH_ERROR) || (status == ETH_QUEUE_FULL)) {
|
|
printf ("Error on transmitting packet ..");
|
|
if (status == ETH_QUEUE_FULL)
|
|
printf ("ETH Queue is full. \n");
|
|
if (status == ETH_QUEUE_LAST_RESOURCE)
|
|
printf ("ETH Queue: using last available resource. \n");
|
|
goto error;
|
|
}
|
|
|
|
/* Update statistics and start of transmittion time */
|
|
stats->tx_bytes += dataSize;
|
|
stats->tx_packets++;
|
|
|
|
/* Check if packet(s) is(are) transmitted correctly (release everything) */
|
|
do {
|
|
release_result =
|
|
eth_tx_return_desc (ethernet_private, ETH_Q0,
|
|
&pkt_info);
|
|
switch (release_result) {
|
|
case ETH_OK:
|
|
DP (printf ("descriptor released\n"));
|
|
if (pkt_info.cmd_sts & BIT0) {
|
|
printf ("Error in TX\n");
|
|
stats->tx_errors++;
|
|
|
|
}
|
|
break;
|
|
case ETH_RETRY:
|
|
DP (printf ("transmission still in process\n"));
|
|
break;
|
|
|
|
case ETH_ERROR:
|
|
printf ("routine can not access Tx desc ring\n");
|
|
break;
|
|
|
|
case ETH_END_OF_JOB:
|
|
DP (printf ("the routine has nothing to release\n"));
|
|
break;
|
|
default: /* should not happen */
|
|
break;
|
|
}
|
|
} while (release_result == ETH_OK);
|
|
|
|
|
|
return 0; /* success */
|
|
error:
|
|
return 1; /* Failed - higher layers will free the skb */
|
|
}
|
|
|
|
/**********************************************************************
|
|
* mv64360_eth_receive
|
|
*
|
|
* This function is forward packets that are received from the port's
|
|
* queues toward kernel core or FastRoute them to another interface.
|
|
*
|
|
* Input : dev - a pointer to the required interface
|
|
* max - maximum number to receive (0 means unlimted)
|
|
*
|
|
* Output : number of served packets
|
|
**********************************************************************/
|
|
|
|
int mv64360_eth_receive (struct eth_device *dev)
|
|
{
|
|
ETH_PORT_INFO *ethernet_private;
|
|
struct mv64360_eth_priv *port_private;
|
|
PKT_INFO pkt_info;
|
|
struct net_device_stats *stats;
|
|
|
|
|
|
ethernet_private = (ETH_PORT_INFO *) dev->priv;
|
|
port_private =
|
|
(struct mv64360_eth_priv *) ethernet_private->port_private;
|
|
stats = port_private->stats;
|
|
|
|
while ((eth_port_receive (ethernet_private, ETH_Q0, &pkt_info) ==
|
|
ETH_OK)) {
|
|
|
|
#ifdef DEBUG_MV_ETH
|
|
if (pkt_info.byte_cnt != 0) {
|
|
printf ("%s: Received %d byte Packet @ 0x%x\n",
|
|
__FUNCTION__, pkt_info.byte_cnt,
|
|
pkt_info.buf_ptr);
|
|
}
|
|
#endif
|
|
/* Update statistics. Note byte count includes 4 byte CRC count */
|
|
stats->rx_packets++;
|
|
stats->rx_bytes += pkt_info.byte_cnt;
|
|
|
|
/*
|
|
* In case received a packet without first / last bits on OR the error
|
|
* summary bit is on, the packets needs to be dropeed.
|
|
*/
|
|
if (((pkt_info.
|
|
cmd_sts & (ETH_RX_FIRST_DESC | ETH_RX_LAST_DESC)) !=
|
|
(ETH_RX_FIRST_DESC | ETH_RX_LAST_DESC))
|
|
|| (pkt_info.cmd_sts & ETH_ERROR_SUMMARY)) {
|
|
stats->rx_dropped++;
|
|
|
|
printf ("Received packet spread on multiple descriptors\n");
|
|
|
|
/* Is this caused by an error ? */
|
|
if (pkt_info.cmd_sts & ETH_ERROR_SUMMARY) {
|
|
stats->rx_errors++;
|
|
}
|
|
|
|
/* free these descriptors again without forwarding them to the higher layers */
|
|
pkt_info.buf_ptr &= ~0x7; /* realign buffer again */
|
|
pkt_info.byte_cnt = 0x0000; /* Reset Byte count */
|
|
|
|
if (eth_rx_return_buff
|
|
(ethernet_private, ETH_Q0, &pkt_info) != ETH_OK) {
|
|
printf ("Error while returning the RX Desc to Ring\n");
|
|
} else {
|
|
DP (printf ("RX Desc returned to Ring\n"));
|
|
}
|
|
/* /free these descriptors again */
|
|
} else {
|
|
|
|
/* !!! call higher layer processing */
|
|
#ifdef DEBUG_MV_ETH
|
|
printf ("\nNow send it to upper layer protocols (NetReceive) ...\n");
|
|
#endif
|
|
/* let the upper layer handle the packet */
|
|
NetReceive ((uchar *) pkt_info.buf_ptr,
|
|
(int) pkt_info.byte_cnt);
|
|
|
|
/* **************************************************************** */
|
|
/* free descriptor */
|
|
pkt_info.buf_ptr &= ~0x7; /* realign buffer again */
|
|
pkt_info.byte_cnt = 0x0000; /* Reset Byte count */
|
|
DP (printf
|
|
("RX: pkt_info.buf_ptr = %x\n",
|
|
pkt_info.buf_ptr));
|
|
if (eth_rx_return_buff
|
|
(ethernet_private, ETH_Q0, &pkt_info) != ETH_OK) {
|
|
printf ("Error while returning the RX Desc to Ring\n");
|
|
} else {
|
|
DP (printf ("RX Desc returned to Ring\n"));
|
|
}
|
|
|
|
/* **************************************************************** */
|
|
|
|
}
|
|
}
|
|
mv64360_eth_get_stats (dev); /* update statistics */
|
|
return 1;
|
|
}
|
|
|
|
/**********************************************************************
|
|
* mv64360_eth_get_stats
|
|
*
|
|
* Returns a pointer to the interface statistics.
|
|
*
|
|
* Input : dev - a pointer to the required interface
|
|
*
|
|
* Output : a pointer to the interface's statistics
|
|
**********************************************************************/
|
|
|
|
static struct net_device_stats *mv64360_eth_get_stats (struct eth_device *dev)
|
|
{
|
|
ETH_PORT_INFO *ethernet_private;
|
|
struct mv64360_eth_priv *port_private;
|
|
|
|
ethernet_private = (ETH_PORT_INFO *) dev->priv;
|
|
port_private =
|
|
(struct mv64360_eth_priv *) ethernet_private->port_private;
|
|
|
|
mv64360_eth_update_stat (dev);
|
|
|
|
return port_private->stats;
|
|
}
|
|
|
|
|
|
/**********************************************************************
|
|
* mv64360_eth_update_stat
|
|
*
|
|
* Update the statistics structure in the private data structure
|
|
*
|
|
* Input : pointer to ethernet interface network device structure
|
|
* Output : N/A
|
|
**********************************************************************/
|
|
|
|
static void mv64360_eth_update_stat (struct eth_device *dev)
|
|
{
|
|
ETH_PORT_INFO *ethernet_private;
|
|
struct mv64360_eth_priv *port_private;
|
|
struct net_device_stats *stats;
|
|
|
|
ethernet_private = (ETH_PORT_INFO *) dev->priv;
|
|
port_private =
|
|
(struct mv64360_eth_priv *) ethernet_private->port_private;
|
|
stats = port_private->stats;
|
|
|
|
/* These are false updates */
|
|
stats->rx_packets += (unsigned long)
|
|
eth_read_mib_counter (ethernet_private->port_num,
|
|
ETH_MIB_GOOD_FRAMES_RECEIVED);
|
|
stats->tx_packets += (unsigned long)
|
|
eth_read_mib_counter (ethernet_private->port_num,
|
|
ETH_MIB_GOOD_FRAMES_SENT);
|
|
stats->rx_bytes += (unsigned long)
|
|
eth_read_mib_counter (ethernet_private->port_num,
|
|
ETH_MIB_GOOD_OCTETS_RECEIVED_LOW);
|
|
/*
|
|
* Ideally this should be as follows -
|
|
*
|
|
* stats->rx_bytes += stats->rx_bytes +
|
|
* ((unsigned long) ethReadMibCounter (ethernet_private->port_num ,
|
|
* ETH_MIB_GOOD_OCTETS_RECEIVED_HIGH) << 32);
|
|
*
|
|
* But the unsigned long in PowerPC and MIPS are 32bit. So the next read
|
|
* is just a dummy read for proper work of the GigE port
|
|
*/
|
|
(void)eth_read_mib_counter (ethernet_private->port_num,
|
|
ETH_MIB_GOOD_OCTETS_RECEIVED_HIGH);
|
|
stats->tx_bytes += (unsigned long)
|
|
eth_read_mib_counter (ethernet_private->port_num,
|
|
ETH_MIB_GOOD_OCTETS_SENT_LOW);
|
|
(void)eth_read_mib_counter (ethernet_private->port_num,
|
|
ETH_MIB_GOOD_OCTETS_SENT_HIGH);
|
|
stats->rx_errors += (unsigned long)
|
|
eth_read_mib_counter (ethernet_private->port_num,
|
|
ETH_MIB_MAC_RECEIVE_ERROR);
|
|
|
|
/* Rx dropped is for received packet with CRC error */
|
|
stats->rx_dropped +=
|
|
(unsigned long) eth_read_mib_counter (ethernet_private->
|
|
port_num,
|
|
ETH_MIB_BAD_CRC_EVENT);
|
|
stats->multicast += (unsigned long)
|
|
eth_read_mib_counter (ethernet_private->port_num,
|
|
ETH_MIB_MULTICAST_FRAMES_RECEIVED);
|
|
stats->collisions +=
|
|
(unsigned long) eth_read_mib_counter (ethernet_private->
|
|
port_num,
|
|
ETH_MIB_COLLISION) +
|
|
(unsigned long) eth_read_mib_counter (ethernet_private->
|
|
port_num,
|
|
ETH_MIB_LATE_COLLISION);
|
|
/* detailed rx errors */
|
|
stats->rx_length_errors +=
|
|
(unsigned long) eth_read_mib_counter (ethernet_private->
|
|
port_num,
|
|
ETH_MIB_UNDERSIZE_RECEIVED)
|
|
+
|
|
(unsigned long) eth_read_mib_counter (ethernet_private->
|
|
port_num,
|
|
ETH_MIB_OVERSIZE_RECEIVED);
|
|
/* detailed tx errors */
|
|
}
|
|
|
|
#ifndef UPDATE_STATS_BY_SOFTWARE
|
|
/**********************************************************************
|
|
* mv64360_eth_print_stat
|
|
*
|
|
* Update the statistics structure in the private data structure
|
|
*
|
|
* Input : pointer to ethernet interface network device structure
|
|
* Output : N/A
|
|
**********************************************************************/
|
|
|
|
static void mv64360_eth_print_stat (struct eth_device *dev)
|
|
{
|
|
ETH_PORT_INFO *ethernet_private;
|
|
struct mv64360_eth_priv *port_private;
|
|
struct net_device_stats *stats;
|
|
|
|
ethernet_private = (ETH_PORT_INFO *) dev->priv;
|
|
port_private =
|
|
(struct mv64360_eth_priv *) ethernet_private->port_private;
|
|
stats = port_private->stats;
|
|
|
|
/* These are false updates */
|
|
printf ("\n### Network statistics: ###\n");
|
|
printf ("--------------------------\n");
|
|
printf (" Packets received: %ld\n", stats->rx_packets);
|
|
printf (" Packets send: %ld\n", stats->tx_packets);
|
|
printf (" Received bytes: %ld\n", stats->rx_bytes);
|
|
printf (" Send bytes: %ld\n", stats->tx_bytes);
|
|
if (stats->rx_errors != 0)
|
|
printf (" Rx Errors: %ld\n",
|
|
stats->rx_errors);
|
|
if (stats->rx_dropped != 0)
|
|
printf (" Rx dropped (CRC Errors): %ld\n",
|
|
stats->rx_dropped);
|
|
if (stats->multicast != 0)
|
|
printf (" Rx mulicast frames: %ld\n",
|
|
stats->multicast);
|
|
if (stats->collisions != 0)
|
|
printf (" No. of collisions: %ld\n",
|
|
stats->collisions);
|
|
if (stats->rx_length_errors != 0)
|
|
printf (" Rx length errors: %ld\n",
|
|
stats->rx_length_errors);
|
|
}
|
|
#endif
|
|
|
|
/**************************************************************************
|
|
*network_start - Network Kick Off Routine UBoot
|
|
*Inputs :
|
|
*Outputs :
|
|
**************************************************************************/
|
|
|
|
bool db64360_eth_start (struct eth_device *dev)
|
|
{
|
|
return (mv64360_eth_open (dev)); /* calls real open */
|
|
}
|
|
|
|
/*************************************************************************
|
|
**************************************************************************
|
|
**************************************************************************
|
|
* The second part is the low level driver of the gigE ethernet ports. *
|
|
**************************************************************************
|
|
**************************************************************************
|
|
*************************************************************************/
|
|
/*
|
|
* based on Linux code
|
|
* arch/powerpc/galileo/EVB64360/mv64360_eth.c - Driver for MV64360X ethernet ports
|
|
* Copyright (C) 2002 rabeeh@galileo.co.il
|
|
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
/********************************************************************************
|
|
* Marvell's Gigabit Ethernet controller low level driver
|
|
*
|
|
* DESCRIPTION:
|
|
* This file introduce low level API to Marvell's Gigabit Ethernet
|
|
* controller. This Gigabit Ethernet Controller driver API controls
|
|
* 1) Operations (i.e. port init, start, reset etc').
|
|
* 2) Data flow (i.e. port send, receive etc').
|
|
* Each Gigabit Ethernet port is controlled via ETH_PORT_INFO
|
|
* struct.
|
|
* This struct includes user configuration information as well as
|
|
* driver internal data needed for its operations.
|
|
*
|
|
* Supported Features:
|
|
* - This low level driver is OS independent. Allocating memory for
|
|
* the descriptor rings and buffers are not within the scope of
|
|
* this driver.
|
|
* - The user is free from Rx/Tx queue managing.
|
|
* - This low level driver introduce functionality API that enable
|
|
* the to operate Marvell's Gigabit Ethernet Controller in a
|
|
* convenient way.
|
|
* - Simple Gigabit Ethernet port operation API.
|
|
* - Simple Gigabit Ethernet port data flow API.
|
|
* - Data flow and operation API support per queue functionality.
|
|
* - Support cached descriptors for better performance.
|
|
* - Enable access to all four DRAM banks and internal SRAM memory
|
|
* spaces.
|
|
* - PHY access and control API.
|
|
* - Port control register configuration API.
|
|
* - Full control over Unicast and Multicast MAC configurations.
|
|
*
|
|
* Operation flow:
|
|
*
|
|
* Initialization phase
|
|
* This phase complete the initialization of the ETH_PORT_INFO
|
|
* struct.
|
|
* User information regarding port configuration has to be set
|
|
* prior to calling the port initialization routine. For example,
|
|
* the user has to assign the port_phy_addr field which is board
|
|
* depended parameter.
|
|
* In this phase any port Tx/Rx activity is halted, MIB counters
|
|
* are cleared, PHY address is set according to user parameter and
|
|
* access to DRAM and internal SRAM memory spaces.
|
|
*
|
|
* Driver ring initialization
|
|
* Allocating memory for the descriptor rings and buffers is not
|
|
* within the scope of this driver. Thus, the user is required to
|
|
* allocate memory for the descriptors ring and buffers. Those
|
|
* memory parameters are used by the Rx and Tx ring initialization
|
|
* routines in order to curve the descriptor linked list in a form
|
|
* of a ring.
|
|
* Note: Pay special attention to alignment issues when using
|
|
* cached descriptors/buffers. In this phase the driver store
|
|
* information in the ETH_PORT_INFO struct regarding each queue
|
|
* ring.
|
|
*
|
|
* Driver start
|
|
* This phase prepares the Ethernet port for Rx and Tx activity.
|
|
* It uses the information stored in the ETH_PORT_INFO struct to
|
|
* initialize the various port registers.
|
|
*
|
|
* Data flow:
|
|
* All packet references to/from the driver are done using PKT_INFO
|
|
* struct.
|
|
* This struct is a unified struct used with Rx and Tx operations.
|
|
* This way the user is not required to be familiar with neither
|
|
* Tx nor Rx descriptors structures.
|
|
* The driver's descriptors rings are management by indexes.
|
|
* Those indexes controls the ring resources and used to indicate
|
|
* a SW resource error:
|
|
* 'current'
|
|
* This index points to the current available resource for use. For
|
|
* example in Rx process this index will point to the descriptor
|
|
* that will be passed to the user upon calling the receive routine.
|
|
* In Tx process, this index will point to the descriptor
|
|
* that will be assigned with the user packet info and transmitted.
|
|
* 'used'
|
|
* This index points to the descriptor that need to restore its
|
|
* resources. For example in Rx process, using the Rx buffer return
|
|
* API will attach the buffer returned in packet info to the
|
|
* descriptor pointed by 'used'. In Tx process, using the Tx
|
|
* descriptor return will merely return the user packet info with
|
|
* the command status of the transmitted buffer pointed by the
|
|
* 'used' index. Nevertheless, it is essential to use this routine
|
|
* to update the 'used' index.
|
|
* 'first'
|
|
* This index supports Tx Scatter-Gather. It points to the first
|
|
* descriptor of a packet assembled of multiple buffers. For example
|
|
* when in middle of Such packet we have a Tx resource error the
|
|
* 'curr' index get the value of 'first' to indicate that the ring
|
|
* returned to its state before trying to transmit this packet.
|
|
*
|
|
* Receive operation:
|
|
* The eth_port_receive API set the packet information struct,
|
|
* passed by the caller, with received information from the
|
|
* 'current' SDMA descriptor.
|
|
* It is the user responsibility to return this resource back
|
|
* to the Rx descriptor ring to enable the reuse of this source.
|
|
* Return Rx resource is done using the eth_rx_return_buff API.
|
|
*
|
|
* Transmit operation:
|
|
* The eth_port_send API supports Scatter-Gather which enables to
|
|
* send a packet spanned over multiple buffers. This means that
|
|
* for each packet info structure given by the user and put into
|
|
* the Tx descriptors ring, will be transmitted only if the 'LAST'
|
|
* bit will be set in the packet info command status field. This
|
|
* API also consider restriction regarding buffer alignments and
|
|
* sizes.
|
|
* The user must return a Tx resource after ensuring the buffer
|
|
* has been transmitted to enable the Tx ring indexes to update.
|
|
*
|
|
* BOARD LAYOUT
|
|
* This device is on-board. No jumper diagram is necessary.
|
|
*
|
|
* EXTERNAL INTERFACE
|
|
*
|
|
* Prior to calling the initialization routine eth_port_init() the user
|
|
* must set the following fields under ETH_PORT_INFO struct:
|
|
* port_num User Ethernet port number.
|
|
* port_phy_addr User PHY address of Ethernet port.
|
|
* port_mac_addr[6] User defined port MAC address.
|
|
* port_config User port configuration value.
|
|
* port_config_extend User port config extend value.
|
|
* port_sdma_config User port SDMA config value.
|
|
* port_serial_control User port serial control value.
|
|
* *port_virt_to_phys () User function to cast virtual addr to CPU bus addr.
|
|
* *port_private User scratch pad for user specific data structures.
|
|
*
|
|
* This driver introduce a set of default values:
|
|
* PORT_CONFIG_VALUE Default port configuration value
|
|
* PORT_CONFIG_EXTEND_VALUE Default port extend configuration value
|
|
* PORT_SDMA_CONFIG_VALUE Default sdma control value
|
|
* PORT_SERIAL_CONTROL_VALUE Default port serial control value
|
|
*
|
|
* This driver data flow is done using the PKT_INFO struct which is
|
|
* a unified struct for Rx and Tx operations:
|
|
* byte_cnt Tx/Rx descriptor buffer byte count.
|
|
* l4i_chk CPU provided TCP Checksum. For Tx operation only.
|
|
* cmd_sts Tx/Rx descriptor command status.
|
|
* buf_ptr Tx/Rx descriptor buffer pointer.
|
|
* return_info Tx/Rx user resource return information.
|
|
*
|
|
*
|
|
* EXTERNAL SUPPORT REQUIREMENTS
|
|
*
|
|
* This driver requires the following external support:
|
|
*
|
|
* D_CACHE_FLUSH_LINE (address, address offset)
|
|
*
|
|
* This macro applies assembly code to flush and invalidate cache
|
|
* line.
|
|
* address - address base.
|
|
* address offset - address offset
|
|
*
|
|
*
|
|
* CPU_PIPE_FLUSH
|
|
*
|
|
* This macro applies assembly code to flush the CPU pipeline.
|
|
*
|
|
*******************************************************************************/
|
|
/* includes */
|
|
|
|
/* defines */
|
|
/* SDMA command macros */
|
|
#define ETH_ENABLE_TX_QUEUE(tx_queue, eth_port) \
|
|
MV_REG_WRITE(MV64360_ETH_TRANSMIT_QUEUE_COMMAND_REG(eth_port), (1 << tx_queue))
|
|
|
|
#define ETH_DISABLE_TX_QUEUE(tx_queue, eth_port) \
|
|
MV_REG_WRITE(MV64360_ETH_TRANSMIT_QUEUE_COMMAND_REG(eth_port),\
|
|
(1 << (8 + tx_queue)))
|
|
|
|
#define ETH_ENABLE_RX_QUEUE(rx_queue, eth_port) \
|
|
MV_REG_WRITE(MV64360_ETH_RECEIVE_QUEUE_COMMAND_REG(eth_port), (1 << rx_queue))
|
|
|
|
#define ETH_DISABLE_RX_QUEUE(rx_queue, eth_port) \
|
|
MV_REG_WRITE(MV64360_ETH_RECEIVE_QUEUE_COMMAND_REG(eth_port), (1 << (8 + rx_queue)))
|
|
|
|
#define CURR_RFD_GET(p_curr_desc, queue) \
|
|
((p_curr_desc) = p_eth_port_ctrl->p_rx_curr_desc_q[queue])
|
|
|
|
#define CURR_RFD_SET(p_curr_desc, queue) \
|
|
(p_eth_port_ctrl->p_rx_curr_desc_q[queue] = (p_curr_desc))
|
|
|
|
#define USED_RFD_GET(p_used_desc, queue) \
|
|
((p_used_desc) = p_eth_port_ctrl->p_rx_used_desc_q[queue])
|
|
|
|
#define USED_RFD_SET(p_used_desc, queue)\
|
|
(p_eth_port_ctrl->p_rx_used_desc_q[queue] = (p_used_desc))
|
|
|
|
|
|
#define CURR_TFD_GET(p_curr_desc, queue) \
|
|
((p_curr_desc) = p_eth_port_ctrl->p_tx_curr_desc_q[queue])
|
|
|
|
#define CURR_TFD_SET(p_curr_desc, queue) \
|
|
(p_eth_port_ctrl->p_tx_curr_desc_q[queue] = (p_curr_desc))
|
|
|
|
#define USED_TFD_GET(p_used_desc, queue) \
|
|
((p_used_desc) = p_eth_port_ctrl->p_tx_used_desc_q[queue])
|
|
|
|
#define USED_TFD_SET(p_used_desc, queue) \
|
|
(p_eth_port_ctrl->p_tx_used_desc_q[queue] = (p_used_desc))
|
|
|
|
#define FIRST_TFD_GET(p_first_desc, queue) \
|
|
((p_first_desc) = p_eth_port_ctrl->p_tx_first_desc_q[queue])
|
|
|
|
#define FIRST_TFD_SET(p_first_desc, queue) \
|
|
(p_eth_port_ctrl->p_tx_first_desc_q[queue] = (p_first_desc))
|
|
|
|
|
|
/* Macros that save access to desc in order to find next desc pointer */
|
|
#define RX_NEXT_DESC_PTR(p_rx_desc, queue) (ETH_RX_DESC*)(((((unsigned int)p_rx_desc - (unsigned int)p_eth_port_ctrl->p_rx_desc_area_base[queue]) + RX_DESC_ALIGNED_SIZE) % p_eth_port_ctrl->rx_desc_area_size[queue]) + (unsigned int)p_eth_port_ctrl->p_rx_desc_area_base[queue])
|
|
|
|
#define TX_NEXT_DESC_PTR(p_tx_desc, queue) (ETH_TX_DESC*)(((((unsigned int)p_tx_desc - (unsigned int)p_eth_port_ctrl->p_tx_desc_area_base[queue]) + TX_DESC_ALIGNED_SIZE) % p_eth_port_ctrl->tx_desc_area_size[queue]) + (unsigned int)p_eth_port_ctrl->p_tx_desc_area_base[queue])
|
|
|
|
#define LINK_UP_TIMEOUT 100000
|
|
#define PHY_BUSY_TIMEOUT 10000000
|
|
|
|
/* locals */
|
|
|
|
/* PHY routines */
|
|
static void ethernet_phy_set (ETH_PORT eth_port_num, int phy_addr);
|
|
static int ethernet_phy_get (ETH_PORT eth_port_num);
|
|
|
|
/* Ethernet Port routines */
|
|
static void eth_set_access_control (ETH_PORT eth_port_num,
|
|
ETH_WIN_PARAM * param);
|
|
static bool eth_port_uc_addr (ETH_PORT eth_port_num, unsigned char uc_nibble,
|
|
ETH_QUEUE queue, int option);
|
|
#if 0 /* FIXME */
|
|
static bool eth_port_smc_addr (ETH_PORT eth_port_num,
|
|
unsigned char mc_byte,
|
|
ETH_QUEUE queue, int option);
|
|
static bool eth_port_omc_addr (ETH_PORT eth_port_num,
|
|
unsigned char crc8,
|
|
ETH_QUEUE queue, int option);
|
|
#endif
|
|
|
|
static void eth_b_copy (unsigned int src_addr, unsigned int dst_addr,
|
|
int byte_count);
|
|
|
|
void eth_dbg (ETH_PORT_INFO * p_eth_port_ctrl);
|
|
|
|
|
|
typedef enum _memory_bank { BANK0, BANK1, BANK2, BANK3 } MEMORY_BANK;
|
|
u32 mv_get_dram_bank_base_addr (MEMORY_BANK bank)
|
|
{
|
|
u32 result = 0;
|
|
u32 enable = MV_REG_READ (MV64360_BASE_ADDR_ENABLE);
|
|
|
|
if (enable & (1 << bank))
|
|
return 0;
|
|
if (bank == BANK0)
|
|
result = MV_REG_READ (MV64360_CS_0_BASE_ADDR);
|
|
if (bank == BANK1)
|
|
result = MV_REG_READ (MV64360_CS_1_BASE_ADDR);
|
|
if (bank == BANK2)
|
|
result = MV_REG_READ (MV64360_CS_2_BASE_ADDR);
|
|
if (bank == BANK3)
|
|
result = MV_REG_READ (MV64360_CS_3_BASE_ADDR);
|
|
result &= 0x0000ffff;
|
|
result = result << 16;
|
|
return result;
|
|
}
|
|
|
|
u32 mv_get_dram_bank_size (MEMORY_BANK bank)
|
|
{
|
|
u32 result = 0;
|
|
u32 enable = MV_REG_READ (MV64360_BASE_ADDR_ENABLE);
|
|
|
|
if (enable & (1 << bank))
|
|
return 0;
|
|
if (bank == BANK0)
|
|
result = MV_REG_READ (MV64360_CS_0_SIZE);
|
|
if (bank == BANK1)
|
|
result = MV_REG_READ (MV64360_CS_1_SIZE);
|
|
if (bank == BANK2)
|
|
result = MV_REG_READ (MV64360_CS_2_SIZE);
|
|
if (bank == BANK3)
|
|
result = MV_REG_READ (MV64360_CS_3_SIZE);
|
|
result += 1;
|
|
result &= 0x0000ffff;
|
|
result = result << 16;
|
|
return result;
|
|
}
|
|
|
|
u32 mv_get_internal_sram_base (void)
|
|
{
|
|
u32 result;
|
|
|
|
result = MV_REG_READ (MV64360_INTEGRATED_SRAM_BASE_ADDR);
|
|
result &= 0x0000ffff;
|
|
result = result << 16;
|
|
return result;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* eth_port_init - Initialize the Ethernet port driver
|
|
*
|
|
* DESCRIPTION:
|
|
* This function prepares the ethernet port to start its activity:
|
|
* 1) Completes the ethernet port driver struct initialization toward port
|
|
* start routine.
|
|
* 2) Resets the device to a quiescent state in case of warm reboot.
|
|
* 3) Enable SDMA access to all four DRAM banks as well as internal SRAM.
|
|
* 4) Clean MAC tables. The reset status of those tables is unknown.
|
|
* 5) Set PHY address.
|
|
* Note: Call this routine prior to eth_port_start routine and after setting
|
|
* user values in the user fields of Ethernet port control struct (i.e.
|
|
* port_phy_addr).
|
|
*
|
|
* INPUT:
|
|
* ETH_PORT_INFO *p_eth_port_ctrl Ethernet port control struct
|
|
*
|
|
* OUTPUT:
|
|
* See description.
|
|
*
|
|
* RETURN:
|
|
* None.
|
|
*
|
|
*******************************************************************************/
|
|
static void eth_port_init (ETH_PORT_INFO * p_eth_port_ctrl)
|
|
{
|
|
int queue;
|
|
ETH_WIN_PARAM win_param;
|
|
|
|
p_eth_port_ctrl->port_config = PORT_CONFIG_VALUE;
|
|
p_eth_port_ctrl->port_config_extend = PORT_CONFIG_EXTEND_VALUE;
|
|
p_eth_port_ctrl->port_sdma_config = PORT_SDMA_CONFIG_VALUE;
|
|
p_eth_port_ctrl->port_serial_control = PORT_SERIAL_CONTROL_VALUE;
|
|
|
|
p_eth_port_ctrl->port_rx_queue_command = 0;
|
|
p_eth_port_ctrl->port_tx_queue_command = 0;
|
|
|
|
/* Zero out SW structs */
|
|
for (queue = 0; queue < MAX_RX_QUEUE_NUM; queue++) {
|
|
CURR_RFD_SET ((ETH_RX_DESC *) 0x00000000, queue);
|
|
USED_RFD_SET ((ETH_RX_DESC *) 0x00000000, queue);
|
|
p_eth_port_ctrl->rx_resource_err[queue] = false;
|
|
}
|
|
|
|
for (queue = 0; queue < MAX_TX_QUEUE_NUM; queue++) {
|
|
CURR_TFD_SET ((ETH_TX_DESC *) 0x00000000, queue);
|
|
USED_TFD_SET ((ETH_TX_DESC *) 0x00000000, queue);
|
|
FIRST_TFD_SET ((ETH_TX_DESC *) 0x00000000, queue);
|
|
p_eth_port_ctrl->tx_resource_err[queue] = false;
|
|
}
|
|
|
|
eth_port_reset (p_eth_port_ctrl->port_num);
|
|
|
|
/* Set access parameters for DRAM bank 0 */
|
|
win_param.win = ETH_WIN0; /* Use Ethernet window 0 */
|
|
win_param.target = ETH_TARGET_DRAM; /* Window target - DDR */
|
|
win_param.attributes = EBAR_ATTR_DRAM_CS0; /* Enable DRAM bank */
|
|
#ifndef CONFIG_NOT_COHERENT_CACHE
|
|
win_param.attributes |= EBAR_ATTR_DRAM_CACHE_COHERENCY_WB;
|
|
#endif
|
|
win_param.high_addr = 0;
|
|
/* Get bank base */
|
|
win_param.base_addr = mv_get_dram_bank_base_addr (BANK0);
|
|
win_param.size = mv_get_dram_bank_size (BANK0); /* Get bank size */
|
|
if (win_param.size == 0)
|
|
win_param.enable = 0;
|
|
else
|
|
win_param.enable = 1; /* Enable the access */
|
|
win_param.access_ctrl = EWIN_ACCESS_FULL; /* Enable full access */
|
|
|
|
/* Set the access control for address window (EPAPR) READ & WRITE */
|
|
eth_set_access_control (p_eth_port_ctrl->port_num, &win_param);
|
|
|
|
/* Set access parameters for DRAM bank 1 */
|
|
win_param.win = ETH_WIN1; /* Use Ethernet window 1 */
|
|
win_param.target = ETH_TARGET_DRAM; /* Window target - DDR */
|
|
win_param.attributes = EBAR_ATTR_DRAM_CS1; /* Enable DRAM bank */
|
|
#ifndef CONFIG_NOT_COHERENT_CACHE
|
|
win_param.attributes |= EBAR_ATTR_DRAM_CACHE_COHERENCY_WB;
|
|
#endif
|
|
win_param.high_addr = 0;
|
|
/* Get bank base */
|
|
win_param.base_addr = mv_get_dram_bank_base_addr (BANK1);
|
|
win_param.size = mv_get_dram_bank_size (BANK1); /* Get bank size */
|
|
if (win_param.size == 0)
|
|
win_param.enable = 0;
|
|
else
|
|
win_param.enable = 1; /* Enable the access */
|
|
win_param.access_ctrl = EWIN_ACCESS_FULL; /* Enable full access */
|
|
|
|
/* Set the access control for address window (EPAPR) READ & WRITE */
|
|
eth_set_access_control (p_eth_port_ctrl->port_num, &win_param);
|
|
|
|
/* Set access parameters for DRAM bank 2 */
|
|
win_param.win = ETH_WIN2; /* Use Ethernet window 2 */
|
|
win_param.target = ETH_TARGET_DRAM; /* Window target - DDR */
|
|
win_param.attributes = EBAR_ATTR_DRAM_CS2; /* Enable DRAM bank */
|
|
#ifndef CONFIG_NOT_COHERENT_CACHE
|
|
win_param.attributes |= EBAR_ATTR_DRAM_CACHE_COHERENCY_WB;
|
|
#endif
|
|
win_param.high_addr = 0;
|
|
/* Get bank base */
|
|
win_param.base_addr = mv_get_dram_bank_base_addr (BANK2);
|
|
win_param.size = mv_get_dram_bank_size (BANK2); /* Get bank size */
|
|
if (win_param.size == 0)
|
|
win_param.enable = 0;
|
|
else
|
|
win_param.enable = 1; /* Enable the access */
|
|
win_param.access_ctrl = EWIN_ACCESS_FULL; /* Enable full access */
|
|
|
|
/* Set the access control for address window (EPAPR) READ & WRITE */
|
|
eth_set_access_control (p_eth_port_ctrl->port_num, &win_param);
|
|
|
|
/* Set access parameters for DRAM bank 3 */
|
|
win_param.win = ETH_WIN3; /* Use Ethernet window 3 */
|
|
win_param.target = ETH_TARGET_DRAM; /* Window target - DDR */
|
|
win_param.attributes = EBAR_ATTR_DRAM_CS3; /* Enable DRAM bank */
|
|
#ifndef CONFIG_NOT_COHERENT_CACHE
|
|
win_param.attributes |= EBAR_ATTR_DRAM_CACHE_COHERENCY_WB;
|
|
#endif
|
|
win_param.high_addr = 0;
|
|
/* Get bank base */
|
|
win_param.base_addr = mv_get_dram_bank_base_addr (BANK3);
|
|
win_param.size = mv_get_dram_bank_size (BANK3); /* Get bank size */
|
|
if (win_param.size == 0)
|
|
win_param.enable = 0;
|
|
else
|
|
win_param.enable = 1; /* Enable the access */
|
|
win_param.access_ctrl = EWIN_ACCESS_FULL; /* Enable full access */
|
|
|
|
/* Set the access control for address window (EPAPR) READ & WRITE */
|
|
eth_set_access_control (p_eth_port_ctrl->port_num, &win_param);
|
|
|
|
/* Set access parameters for Internal SRAM */
|
|
win_param.win = ETH_WIN4; /* Use Ethernet window 0 */
|
|
win_param.target = EBAR_TARGET_CBS; /* Target - Internal SRAM */
|
|
win_param.attributes = EBAR_ATTR_CBS_SRAM | EBAR_ATTR_CBS_SRAM_BLOCK0;
|
|
win_param.high_addr = 0;
|
|
win_param.base_addr = mv_get_internal_sram_base (); /* Get base addr */
|
|
win_param.size = MV64360_INTERNAL_SRAM_SIZE; /* Get bank size */
|
|
win_param.enable = 1; /* Enable the access */
|
|
win_param.access_ctrl = EWIN_ACCESS_FULL; /* Enable full access */
|
|
|
|
/* Set the access control for address window (EPAPR) READ & WRITE */
|
|
eth_set_access_control (p_eth_port_ctrl->port_num, &win_param);
|
|
|
|
eth_port_init_mac_tables (p_eth_port_ctrl->port_num);
|
|
|
|
ethernet_phy_set (p_eth_port_ctrl->port_num,
|
|
p_eth_port_ctrl->port_phy_addr);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* eth_port_start - Start the Ethernet port activity.
|
|
*
|
|
* DESCRIPTION:
|
|
* This routine prepares the Ethernet port for Rx and Tx activity:
|
|
* 1. Initialize Tx and Rx Current Descriptor Pointer for each queue that
|
|
* has been initialized a descriptor's ring (using ether_init_tx_desc_ring
|
|
* for Tx and ether_init_rx_desc_ring for Rx)
|
|
* 2. Initialize and enable the Ethernet configuration port by writing to
|
|
* the port's configuration and command registers.
|
|
* 3. Initialize and enable the SDMA by writing to the SDMA's
|
|
* configuration and command registers.
|
|
* After completing these steps, the ethernet port SDMA can starts to
|
|
* perform Rx and Tx activities.
|
|
*
|
|
* Note: Each Rx and Tx queue descriptor's list must be initialized prior
|
|
* to calling this function (use ether_init_tx_desc_ring for Tx queues and
|
|
* ether_init_rx_desc_ring for Rx queues).
|
|
*
|
|
* INPUT:
|
|
* ETH_PORT_INFO *p_eth_port_ctrl Ethernet port control struct
|
|
*
|
|
* OUTPUT:
|
|
* Ethernet port is ready to receive and transmit.
|
|
*
|
|
* RETURN:
|
|
* false if the port PHY is not up.
|
|
* true otherwise.
|
|
*
|
|
*******************************************************************************/
|
|
static bool eth_port_start (ETH_PORT_INFO * p_eth_port_ctrl)
|
|
{
|
|
int queue;
|
|
volatile ETH_TX_DESC *p_tx_curr_desc;
|
|
volatile ETH_RX_DESC *p_rx_curr_desc;
|
|
unsigned int phy_reg_data;
|
|
ETH_PORT eth_port_num = p_eth_port_ctrl->port_num;
|
|
|
|
|
|
/* Assignment of Tx CTRP of given queue */
|
|
for (queue = 0; queue < MAX_TX_QUEUE_NUM; queue++) {
|
|
CURR_TFD_GET (p_tx_curr_desc, queue);
|
|
MV_REG_WRITE ((MV64360_ETH_TX_CURRENT_QUEUE_DESC_PTR_0
|
|
(eth_port_num)
|
|
+ (4 * queue)),
|
|
((unsigned int) p_tx_curr_desc));
|
|
|
|
}
|
|
|
|
/* Assignment of Rx CRDP of given queue */
|
|
for (queue = 0; queue < MAX_RX_QUEUE_NUM; queue++) {
|
|
CURR_RFD_GET (p_rx_curr_desc, queue);
|
|
MV_REG_WRITE ((MV64360_ETH_RX_CURRENT_QUEUE_DESC_PTR_0
|
|
(eth_port_num)
|
|
+ (4 * queue)),
|
|
((unsigned int) p_rx_curr_desc));
|
|
|
|
if (p_rx_curr_desc != NULL)
|
|
/* Add the assigned Ethernet address to the port's address table */
|
|
eth_port_uc_addr_set (p_eth_port_ctrl->port_num,
|
|
p_eth_port_ctrl->port_mac_addr,
|
|
queue);
|
|
}
|
|
|
|
/* Assign port configuration and command. */
|
|
MV_REG_WRITE (MV64360_ETH_PORT_CONFIG_REG (eth_port_num),
|
|
p_eth_port_ctrl->port_config);
|
|
|
|
MV_REG_WRITE (MV64360_ETH_PORT_CONFIG_EXTEND_REG (eth_port_num),
|
|
p_eth_port_ctrl->port_config_extend);
|
|
|
|
MV_REG_WRITE (MV64360_ETH_PORT_SERIAL_CONTROL_REG (eth_port_num),
|
|
p_eth_port_ctrl->port_serial_control);
|
|
|
|
MV_SET_REG_BITS (MV64360_ETH_PORT_SERIAL_CONTROL_REG (eth_port_num),
|
|
ETH_SERIAL_PORT_ENABLE);
|
|
|
|
/* Assign port SDMA configuration */
|
|
MV_REG_WRITE (MV64360_ETH_SDMA_CONFIG_REG (eth_port_num),
|
|
p_eth_port_ctrl->port_sdma_config);
|
|
|
|
MV_REG_WRITE (MV64360_ETH_TX_QUEUE_0_TOKEN_BUCKET_COUNT
|
|
(eth_port_num), 0x3fffffff);
|
|
MV_REG_WRITE (MV64360_ETH_TX_QUEUE_0_TOKEN_BUCKET_CONFIG
|
|
(eth_port_num), 0x03fffcff);
|
|
/* Turn off the port/queue bandwidth limitation */
|
|
MV_REG_WRITE (MV64360_ETH_MAXIMUM_TRANSMIT_UNIT (eth_port_num), 0x0);
|
|
|
|
/* Enable port Rx. */
|
|
MV_REG_WRITE (MV64360_ETH_RECEIVE_QUEUE_COMMAND_REG (eth_port_num),
|
|
p_eth_port_ctrl->port_rx_queue_command);
|
|
|
|
/* Check if link is up */
|
|
eth_port_read_smi_reg (eth_port_num, 1, &phy_reg_data);
|
|
|
|
if (!(phy_reg_data & 0x20))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* eth_port_uc_addr_set - This function Set the port Unicast address.
|
|
*
|
|
* DESCRIPTION:
|
|
* This function Set the port Ethernet MAC address.
|
|
*
|
|
* INPUT:
|
|
* ETH_PORT eth_port_num Port number.
|
|
* char * p_addr Address to be set
|
|
* ETH_QUEUE queue Rx queue number for this MAC address.
|
|
*
|
|
* OUTPUT:
|
|
* Set MAC address low and high registers. also calls eth_port_uc_addr()
|
|
* To set the unicast table with the proper information.
|
|
*
|
|
* RETURN:
|
|
* N/A.
|
|
*
|
|
*******************************************************************************/
|
|
static void eth_port_uc_addr_set (ETH_PORT eth_port_num,
|
|
unsigned char *p_addr, ETH_QUEUE queue)
|
|
{
|
|
unsigned int mac_h;
|
|
unsigned int mac_l;
|
|
|
|
mac_l = (p_addr[4] << 8) | (p_addr[5]);
|
|
mac_h = (p_addr[0] << 24) | (p_addr[1] << 16) |
|
|
(p_addr[2] << 8) | (p_addr[3] << 0);
|
|
|
|
MV_REG_WRITE (MV64360_ETH_MAC_ADDR_LOW (eth_port_num), mac_l);
|
|
MV_REG_WRITE (MV64360_ETH_MAC_ADDR_HIGH (eth_port_num), mac_h);
|
|
|
|
/* Accept frames of this address */
|
|
eth_port_uc_addr (eth_port_num, p_addr[5], queue, ACCEPT_MAC_ADDR);
|
|
|
|
return;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* eth_port_uc_addr - This function Set the port unicast address table
|
|
*
|
|
* DESCRIPTION:
|
|
* This function locates the proper entry in the Unicast table for the
|
|
* specified MAC nibble and sets its properties according to function
|
|
* parameters.
|
|
*
|
|
* INPUT:
|
|
* ETH_PORT eth_port_num Port number.
|
|
* unsigned char uc_nibble Unicast MAC Address last nibble.
|
|
* ETH_QUEUE queue Rx queue number for this MAC address.
|
|
* int option 0 = Add, 1 = remove address.
|
|
*
|
|
* OUTPUT:
|
|
* This function add/removes MAC addresses from the port unicast address
|
|
* table.
|
|
*
|
|
* RETURN:
|
|
* true is output succeeded.
|
|
* false if option parameter is invalid.
|
|
*
|
|
*******************************************************************************/
|
|
static bool eth_port_uc_addr (ETH_PORT eth_port_num,
|
|
unsigned char uc_nibble,
|
|
ETH_QUEUE queue, int option)
|
|
{
|
|
unsigned int unicast_reg;
|
|
unsigned int tbl_offset;
|
|
unsigned int reg_offset;
|
|
|
|
/* Locate the Unicast table entry */
|
|
uc_nibble = (0xf & uc_nibble);
|
|
tbl_offset = (uc_nibble / 4) * 4; /* Register offset from unicast table base */
|
|
reg_offset = uc_nibble % 4; /* Entry offset within the above register */
|
|
|
|
switch (option) {
|
|
case REJECT_MAC_ADDR:
|
|
/* Clear accepts frame bit at specified unicast DA table entry */
|
|
unicast_reg =
|
|
MV_REG_READ ((MV64360_ETH_DA_FILTER_UNICAST_TABLE_BASE
|
|
(eth_port_num)
|
|
+ tbl_offset));
|
|
|
|
unicast_reg &= (0x0E << (8 * reg_offset));
|
|
|
|
MV_REG_WRITE ((MV64360_ETH_DA_FILTER_UNICAST_TABLE_BASE
|
|
(eth_port_num)
|
|
+ tbl_offset), unicast_reg);
|
|
break;
|
|
|
|
case ACCEPT_MAC_ADDR:
|
|
/* Set accepts frame bit at unicast DA filter table entry */
|
|
unicast_reg =
|
|
MV_REG_READ ((MV64360_ETH_DA_FILTER_UNICAST_TABLE_BASE
|
|
(eth_port_num)
|
|
+ tbl_offset));
|
|
|
|
unicast_reg |= ((0x01 | queue) << (8 * reg_offset));
|
|
|
|
MV_REG_WRITE ((MV64360_ETH_DA_FILTER_UNICAST_TABLE_BASE
|
|
(eth_port_num)
|
|
+ tbl_offset), unicast_reg);
|
|
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#if 0 /* FIXME */
|
|
/*******************************************************************************
|
|
* eth_port_mc_addr - Multicast address settings.
|
|
*
|
|
* DESCRIPTION:
|
|
* This API controls the MV device MAC multicast support.
|
|
* The MV device supports multicast using two tables:
|
|
* 1) Special Multicast Table for MAC addresses of the form
|
|
* 0x01-00-5E-00-00-XX (where XX is between 0x00 and 0x_fF).
|
|
* The MAC DA[7:0] bits are used as a pointer to the Special Multicast
|
|
* Table entries in the DA-Filter table.
|
|
* In this case, the function calls eth_port_smc_addr() routine to set the
|
|
* Special Multicast Table.
|
|
* 2) Other Multicast Table for multicast of another type. A CRC-8bit
|
|
* is used as an index to the Other Multicast Table entries in the
|
|
* DA-Filter table.
|
|
* In this case, the function calculates the CRC-8bit value and calls
|
|
* eth_port_omc_addr() routine to set the Other Multicast Table.
|
|
* INPUT:
|
|
* ETH_PORT eth_port_num Port number.
|
|
* unsigned char *p_addr Unicast MAC Address.
|
|
* ETH_QUEUE queue Rx queue number for this MAC address.
|
|
* int option 0 = Add, 1 = remove address.
|
|
*
|
|
* OUTPUT:
|
|
* See description.
|
|
*
|
|
* RETURN:
|
|
* true is output succeeded.
|
|
* false if add_address_table_entry( ) failed.
|
|
*
|
|
*******************************************************************************/
|
|
static void eth_port_mc_addr (ETH_PORT eth_port_num,
|
|
unsigned char *p_addr,
|
|
ETH_QUEUE queue, int option)
|
|
{
|
|
unsigned int mac_h;
|
|
unsigned int mac_l;
|
|
unsigned char crc_result = 0;
|
|
int mac_array[48];
|
|
int crc[8];
|
|
int i;
|
|
|
|
|
|
if ((p_addr[0] == 0x01) &&
|
|
(p_addr[1] == 0x00) &&
|
|
(p_addr[2] == 0x5E) && (p_addr[3] == 0x00) && (p_addr[4] == 0x00))
|
|
|
|
eth_port_smc_addr (eth_port_num, p_addr[5], queue, option);
|
|
else {
|
|
/* Calculate CRC-8 out of the given address */
|
|
mac_h = (p_addr[0] << 8) | (p_addr[1]);
|
|
mac_l = (p_addr[2] << 24) | (p_addr[3] << 16) |
|
|
(p_addr[4] << 8) | (p_addr[5] << 0);
|
|
|
|
for (i = 0; i < 32; i++)
|
|
mac_array[i] = (mac_l >> i) & 0x1;
|
|
for (i = 32; i < 48; i++)
|
|
mac_array[i] = (mac_h >> (i - 32)) & 0x1;
|
|
|
|
|
|
crc[0] = mac_array[45] ^ mac_array[43] ^ mac_array[40] ^
|
|
mac_array[39] ^ mac_array[35] ^ mac_array[34] ^
|
|
mac_array[31] ^ mac_array[30] ^ mac_array[28] ^
|
|
mac_array[23] ^ mac_array[21] ^ mac_array[19] ^
|
|
mac_array[18] ^ mac_array[16] ^ mac_array[14] ^
|
|
mac_array[12] ^ mac_array[8] ^ mac_array[7] ^
|
|
mac_array[6] ^ mac_array[0];
|
|
|
|
crc[1] = mac_array[46] ^ mac_array[45] ^ mac_array[44] ^
|
|
mac_array[43] ^ mac_array[41] ^ mac_array[39] ^
|
|
mac_array[36] ^ mac_array[34] ^ mac_array[32] ^
|
|
mac_array[30] ^ mac_array[29] ^ mac_array[28] ^
|
|
mac_array[24] ^ mac_array[23] ^ mac_array[22] ^
|
|
mac_array[21] ^ mac_array[20] ^ mac_array[18] ^
|
|
mac_array[17] ^ mac_array[16] ^ mac_array[15] ^
|
|
mac_array[14] ^ mac_array[13] ^ mac_array[12] ^
|
|
mac_array[9] ^ mac_array[6] ^ mac_array[1] ^
|
|
mac_array[0];
|
|
|
|
crc[2] = mac_array[47] ^ mac_array[46] ^ mac_array[44] ^
|
|
mac_array[43] ^ mac_array[42] ^ mac_array[39] ^
|
|
mac_array[37] ^ mac_array[34] ^ mac_array[33] ^
|
|
mac_array[29] ^ mac_array[28] ^ mac_array[25] ^
|
|
mac_array[24] ^ mac_array[22] ^ mac_array[17] ^
|
|
mac_array[15] ^ mac_array[13] ^ mac_array[12] ^
|
|
mac_array[10] ^ mac_array[8] ^ mac_array[6] ^
|
|
mac_array[2] ^ mac_array[1] ^ mac_array[0];
|
|
|
|
crc[3] = mac_array[47] ^ mac_array[45] ^ mac_array[44] ^
|
|
mac_array[43] ^ mac_array[40] ^ mac_array[38] ^
|
|
mac_array[35] ^ mac_array[34] ^ mac_array[30] ^
|
|
mac_array[29] ^ mac_array[26] ^ mac_array[25] ^
|
|
mac_array[23] ^ mac_array[18] ^ mac_array[16] ^
|
|
mac_array[14] ^ mac_array[13] ^ mac_array[11] ^
|
|
mac_array[9] ^ mac_array[7] ^ mac_array[3] ^
|
|
mac_array[2] ^ mac_array[1];
|
|
|
|
crc[4] = mac_array[46] ^ mac_array[45] ^ mac_array[44] ^
|
|
mac_array[41] ^ mac_array[39] ^ mac_array[36] ^
|
|
mac_array[35] ^ mac_array[31] ^ mac_array[30] ^
|
|
mac_array[27] ^ mac_array[26] ^ mac_array[24] ^
|
|
mac_array[19] ^ mac_array[17] ^ mac_array[15] ^
|
|
mac_array[14] ^ mac_array[12] ^ mac_array[10] ^
|
|
mac_array[8] ^ mac_array[4] ^ mac_array[3] ^
|
|
mac_array[2];
|
|
|
|
crc[5] = mac_array[47] ^ mac_array[46] ^ mac_array[45] ^
|
|
mac_array[42] ^ mac_array[40] ^ mac_array[37] ^
|
|
mac_array[36] ^ mac_array[32] ^ mac_array[31] ^
|
|
mac_array[28] ^ mac_array[27] ^ mac_array[25] ^
|
|
mac_array[20] ^ mac_array[18] ^ mac_array[16] ^
|
|
mac_array[15] ^ mac_array[13] ^ mac_array[11] ^
|
|
mac_array[9] ^ mac_array[5] ^ mac_array[4] ^
|
|
mac_array[3];
|
|
|
|
crc[6] = mac_array[47] ^ mac_array[46] ^ mac_array[43] ^
|
|
mac_array[41] ^ mac_array[38] ^ mac_array[37] ^
|
|
mac_array[33] ^ mac_array[32] ^ mac_array[29] ^
|
|
mac_array[28] ^ mac_array[26] ^ mac_array[21] ^
|
|
mac_array[19] ^ mac_array[17] ^ mac_array[16] ^
|
|
mac_array[14] ^ mac_array[12] ^ mac_array[10] ^
|
|
mac_array[6] ^ mac_array[5] ^ mac_array[4];
|
|
|
|
crc[7] = mac_array[47] ^ mac_array[44] ^ mac_array[42] ^
|
|
mac_array[39] ^ mac_array[38] ^ mac_array[34] ^
|
|
mac_array[33] ^ mac_array[30] ^ mac_array[29] ^
|
|
mac_array[27] ^ mac_array[22] ^ mac_array[20] ^
|
|
mac_array[18] ^ mac_array[17] ^ mac_array[15] ^
|
|
mac_array[13] ^ mac_array[11] ^ mac_array[7] ^
|
|
mac_array[6] ^ mac_array[5];
|
|
|
|
for (i = 0; i < 8; i++)
|
|
crc_result = crc_result | (crc[i] << i);
|
|
|
|
eth_port_omc_addr (eth_port_num, crc_result, queue, option);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* eth_port_smc_addr - Special Multicast address settings.
|
|
*
|
|
* DESCRIPTION:
|
|
* This routine controls the MV device special MAC multicast support.
|
|
* The Special Multicast Table for MAC addresses supports MAC of the form
|
|
* 0x01-00-5E-00-00-XX (where XX is between 0x00 and 0x_fF).
|
|
* The MAC DA[7:0] bits are used as a pointer to the Special Multicast
|
|
* Table entries in the DA-Filter table.
|
|
* This function set the Special Multicast Table appropriate entry
|
|
* according to the argument given.
|
|
*
|
|
* INPUT:
|
|
* ETH_PORT eth_port_num Port number.
|
|
* unsigned char mc_byte Multicast addr last byte (MAC DA[7:0] bits).
|
|
* ETH_QUEUE queue Rx queue number for this MAC address.
|
|
* int option 0 = Add, 1 = remove address.
|
|
*
|
|
* OUTPUT:
|
|
* See description.
|
|
*
|
|
* RETURN:
|
|
* true is output succeeded.
|
|
* false if option parameter is invalid.
|
|
*
|
|
*******************************************************************************/
|
|
static bool eth_port_smc_addr (ETH_PORT eth_port_num,
|
|
unsigned char mc_byte,
|
|
ETH_QUEUE queue, int option)
|
|
{
|
|
unsigned int smc_table_reg;
|
|
unsigned int tbl_offset;
|
|
unsigned int reg_offset;
|
|
|
|
/* Locate the SMC table entry */
|
|
tbl_offset = (mc_byte / 4) * 4; /* Register offset from SMC table base */
|
|
reg_offset = mc_byte % 4; /* Entry offset within the above register */
|
|
queue &= 0x7;
|
|
|
|
switch (option) {
|
|
case REJECT_MAC_ADDR:
|
|
/* Clear accepts frame bit at specified Special DA table entry */
|
|
smc_table_reg =
|
|
MV_REG_READ ((MV64360_ETH_DA_FILTER_SPECIAL_MULTICAST_TABLE_BASE (eth_port_num) + tbl_offset));
|
|
smc_table_reg &= (0x0E << (8 * reg_offset));
|
|
|
|
MV_REG_WRITE ((MV64360_ETH_DA_FILTER_SPECIAL_MULTICAST_TABLE_BASE (eth_port_num) + tbl_offset), smc_table_reg);
|
|
break;
|
|
|
|
case ACCEPT_MAC_ADDR:
|
|
/* Set accepts frame bit at specified Special DA table entry */
|
|
smc_table_reg =
|
|
MV_REG_READ ((MV64360_ETH_DA_FILTER_SPECIAL_MULTICAST_TABLE_BASE (eth_port_num) + tbl_offset));
|
|
smc_table_reg |= ((0x01 | queue) << (8 * reg_offset));
|
|
|
|
MV_REG_WRITE ((MV64360_ETH_DA_FILTER_SPECIAL_MULTICAST_TABLE_BASE (eth_port_num) + tbl_offset), smc_table_reg);
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* eth_port_omc_addr - Multicast address settings.
|
|
*
|
|
* DESCRIPTION:
|
|
* This routine controls the MV device Other MAC multicast support.
|
|
* The Other Multicast Table is used for multicast of another type.
|
|
* A CRC-8bit is used as an index to the Other Multicast Table entries
|
|
* in the DA-Filter table.
|
|
* The function gets the CRC-8bit value from the calling routine and
|
|
* set the Other Multicast Table appropriate entry according to the
|
|
* CRC-8 argument given.
|
|
*
|
|
* INPUT:
|
|
* ETH_PORT eth_port_num Port number.
|
|
* unsigned char crc8 A CRC-8bit (Polynomial: x^8+x^2+x^1+1).
|
|
* ETH_QUEUE queue Rx queue number for this MAC address.
|
|
* int option 0 = Add, 1 = remove address.
|
|
*
|
|
* OUTPUT:
|
|
* See description.
|
|
*
|
|
* RETURN:
|
|
* true is output succeeded.
|
|
* false if option parameter is invalid.
|
|
*
|
|
*******************************************************************************/
|
|
static bool eth_port_omc_addr (ETH_PORT eth_port_num,
|
|
unsigned char crc8,
|
|
ETH_QUEUE queue, int option)
|
|
{
|
|
unsigned int omc_table_reg;
|
|
unsigned int tbl_offset;
|
|
unsigned int reg_offset;
|
|
|
|
/* Locate the OMC table entry */
|
|
tbl_offset = (crc8 / 4) * 4; /* Register offset from OMC table base */
|
|
reg_offset = crc8 % 4; /* Entry offset within the above register */
|
|
queue &= 0x7;
|
|
|
|
switch (option) {
|
|
case REJECT_MAC_ADDR:
|
|
/* Clear accepts frame bit at specified Other DA table entry */
|
|
omc_table_reg =
|
|
MV_REG_READ ((MV64360_ETH_DA_FILTER_OTHER_MULTICAST_TABLE_BASE (eth_port_num) + tbl_offset));
|
|
omc_table_reg &= (0x0E << (8 * reg_offset));
|
|
|
|
MV_REG_WRITE ((MV64360_ETH_DA_FILTER_OTHER_MULTICAST_TABLE_BASE (eth_port_num) + tbl_offset), omc_table_reg);
|
|
break;
|
|
|
|
case ACCEPT_MAC_ADDR:
|
|
/* Set accepts frame bit at specified Other DA table entry */
|
|
omc_table_reg =
|
|
MV_REG_READ ((MV64360_ETH_DA_FILTER_OTHER_MULTICAST_TABLE_BASE (eth_port_num) + tbl_offset));
|
|
omc_table_reg |= ((0x01 | queue) << (8 * reg_offset));
|
|
|
|
MV_REG_WRITE ((MV64360_ETH_DA_FILTER_OTHER_MULTICAST_TABLE_BASE (eth_port_num) + tbl_offset), omc_table_reg);
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
/*******************************************************************************
|
|
* eth_port_init_mac_tables - Clear all entrance in the UC, SMC and OMC tables
|
|
*
|
|
* DESCRIPTION:
|
|
* Go through all the DA filter tables (Unicast, Special Multicast & Other
|
|
* Multicast) and set each entry to 0.
|
|
*
|
|
* INPUT:
|
|
* ETH_PORT eth_port_num Ethernet Port number. See ETH_PORT enum.
|
|
*
|
|
* OUTPUT:
|
|
* Multicast and Unicast packets are rejected.
|
|
*
|
|
* RETURN:
|
|
* None.
|
|
*
|
|
*******************************************************************************/
|
|
static void eth_port_init_mac_tables (ETH_PORT eth_port_num)
|
|
{
|
|
int table_index;
|
|
|
|
/* Clear DA filter unicast table (Ex_dFUT) */
|
|
for (table_index = 0; table_index <= 0xC; table_index += 4)
|
|
MV_REG_WRITE ((MV64360_ETH_DA_FILTER_UNICAST_TABLE_BASE
|
|
(eth_port_num) + table_index), 0);
|
|
|
|
for (table_index = 0; table_index <= 0xFC; table_index += 4) {
|
|
/* Clear DA filter special multicast table (Ex_dFSMT) */
|
|
MV_REG_WRITE ((MV64360_ETH_DA_FILTER_SPECIAL_MULTICAST_TABLE_BASE (eth_port_num) + table_index), 0);
|
|
/* Clear DA filter other multicast table (Ex_dFOMT) */
|
|
MV_REG_WRITE ((MV64360_ETH_DA_FILTER_OTHER_MULTICAST_TABLE_BASE (eth_port_num) + table_index), 0);
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* eth_clear_mib_counters - Clear all MIB counters
|
|
*
|
|
* DESCRIPTION:
|
|
* This function clears all MIB counters of a specific ethernet port.
|
|
* A read from the MIB counter will reset the counter.
|
|
*
|
|
* INPUT:
|
|
* ETH_PORT eth_port_num Ethernet Port number. See ETH_PORT enum.
|
|
*
|
|
* OUTPUT:
|
|
* After reading all MIB counters, the counters resets.
|
|
*
|
|
* RETURN:
|
|
* MIB counter value.
|
|
*
|
|
*******************************************************************************/
|
|
static void eth_clear_mib_counters (ETH_PORT eth_port_num)
|
|
{
|
|
int i;
|
|
|
|
/* Perform dummy reads from MIB counters */
|
|
for (i = ETH_MIB_GOOD_OCTETS_RECEIVED_LOW; i < ETH_MIB_LATE_COLLISION;
|
|
i += 4) {
|
|
(void)MV_REG_READ ((MV64360_ETH_MIB_COUNTERS_BASE
|
|
(eth_port_num) + i));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* eth_read_mib_counter - Read a MIB counter
|
|
*
|
|
* DESCRIPTION:
|
|
* This function reads a MIB counter of a specific ethernet port.
|
|
* NOTE - If read from ETH_MIB_GOOD_OCTETS_RECEIVED_LOW, then the
|
|
* following read must be from ETH_MIB_GOOD_OCTETS_RECEIVED_HIGH
|
|
* register. The same applies for ETH_MIB_GOOD_OCTETS_SENT_LOW and
|
|
* ETH_MIB_GOOD_OCTETS_SENT_HIGH
|
|
*
|
|
* INPUT:
|
|
* ETH_PORT eth_port_num Ethernet Port number. See ETH_PORT enum.
|
|
* unsigned int mib_offset MIB counter offset (use ETH_MIB_... macros).
|
|
*
|
|
* OUTPUT:
|
|
* After reading the MIB counter, the counter resets.
|
|
*
|
|
* RETURN:
|
|
* MIB counter value.
|
|
*
|
|
*******************************************************************************/
|
|
unsigned int eth_read_mib_counter (ETH_PORT eth_port_num,
|
|
unsigned int mib_offset)
|
|
{
|
|
return (MV_REG_READ (MV64360_ETH_MIB_COUNTERS_BASE (eth_port_num)
|
|
+ mib_offset));
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* ethernet_phy_set - Set the ethernet port PHY address.
|
|
*
|
|
* DESCRIPTION:
|
|
* This routine set the ethernet port PHY address according to given
|
|
* parameter.
|
|
*
|
|
* INPUT:
|
|
* ETH_PORT eth_port_num Ethernet Port number. See ETH_PORT enum.
|
|
*
|
|
* OUTPUT:
|
|
* Set PHY Address Register with given PHY address parameter.
|
|
*
|
|
* RETURN:
|
|
* None.
|
|
*
|
|
*******************************************************************************/
|
|
static void ethernet_phy_set (ETH_PORT eth_port_num, int phy_addr)
|
|
{
|
|
unsigned int reg_data;
|
|
|
|
reg_data = MV_REG_READ (MV64360_ETH_PHY_ADDR_REG);
|
|
|
|
reg_data &= ~(0x1F << (5 * eth_port_num));
|
|
reg_data |= (phy_addr << (5 * eth_port_num));
|
|
|
|
MV_REG_WRITE (MV64360_ETH_PHY_ADDR_REG, reg_data);
|
|
|
|
return;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* ethernet_phy_get - Get the ethernet port PHY address.
|
|
*
|
|
* DESCRIPTION:
|
|
* This routine returns the given ethernet port PHY address.
|
|
*
|
|
* INPUT:
|
|
* ETH_PORT eth_port_num Ethernet Port number. See ETH_PORT enum.
|
|
*
|
|
* OUTPUT:
|
|
* None.
|
|
*
|
|
* RETURN:
|
|
* PHY address.
|
|
*
|
|
*******************************************************************************/
|
|
static int ethernet_phy_get (ETH_PORT eth_port_num)
|
|
{
|
|
unsigned int reg_data;
|
|
|
|
reg_data = MV_REG_READ (MV64360_ETH_PHY_ADDR_REG);
|
|
|
|
return ((reg_data >> (5 * eth_port_num)) & 0x1f);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* ethernet_phy_reset - Reset Ethernet port PHY.
|
|
*
|
|
* DESCRIPTION:
|
|
* This routine utilize the SMI interface to reset the ethernet port PHY.
|
|
* The routine waits until the link is up again or link up is timeout.
|
|
*
|
|
* INPUT:
|
|
* ETH_PORT eth_port_num Ethernet Port number. See ETH_PORT enum.
|
|
*
|
|
* OUTPUT:
|
|
* The ethernet port PHY renew its link.
|
|
*
|
|
* RETURN:
|
|
* None.
|
|
*
|
|
*******************************************************************************/
|
|
static bool ethernet_phy_reset (ETH_PORT eth_port_num)
|
|
{
|
|
unsigned int time_out = 50;
|
|
unsigned int phy_reg_data;
|
|
|
|
/* Reset the PHY */
|
|
eth_port_read_smi_reg (eth_port_num, 0, &phy_reg_data);
|
|
phy_reg_data |= 0x8000; /* Set bit 15 to reset the PHY */
|
|
eth_port_write_smi_reg (eth_port_num, 0, phy_reg_data);
|
|
|
|
/* Poll on the PHY LINK */
|
|
do {
|
|
eth_port_read_smi_reg (eth_port_num, 1, &phy_reg_data);
|
|
|
|
if (time_out-- == 0)
|
|
return false;
|
|
}
|
|
while (!(phy_reg_data & 0x20));
|
|
|
|
return true;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* eth_port_reset - Reset Ethernet port
|
|
*
|
|
* DESCRIPTION:
|
|
* This routine resets the chip by aborting any SDMA engine activity and
|
|
* clearing the MIB counters. The Receiver and the Transmit unit are in
|
|
* idle state after this command is performed and the port is disabled.
|
|
*
|
|
* INPUT:
|
|
* ETH_PORT eth_port_num Ethernet Port number. See ETH_PORT enum.
|
|
*
|
|
* OUTPUT:
|
|
* Channel activity is halted.
|
|
*
|
|
* RETURN:
|
|
* None.
|
|
*
|
|
*******************************************************************************/
|
|
static void eth_port_reset (ETH_PORT eth_port_num)
|
|
{
|
|
unsigned int reg_data;
|
|
|
|
/* Stop Tx port activity. Check port Tx activity. */
|
|
reg_data =
|
|
MV_REG_READ (MV64360_ETH_TRANSMIT_QUEUE_COMMAND_REG
|
|
(eth_port_num));
|
|
|
|
if (reg_data & 0xFF) {
|
|
/* Issue stop command for active channels only */
|
|
MV_REG_WRITE (MV64360_ETH_TRANSMIT_QUEUE_COMMAND_REG
|
|
(eth_port_num), (reg_data << 8));
|
|
|
|
/* Wait for all Tx activity to terminate. */
|
|
do {
|
|
/* Check port cause register that all Tx queues are stopped */
|
|
reg_data =
|
|
MV_REG_READ
|
|
(MV64360_ETH_TRANSMIT_QUEUE_COMMAND_REG
|
|
(eth_port_num));
|
|
}
|
|
while (reg_data & 0xFF);
|
|
}
|
|
|
|
/* Stop Rx port activity. Check port Rx activity. */
|
|
reg_data =
|
|
MV_REG_READ (MV64360_ETH_RECEIVE_QUEUE_COMMAND_REG
|
|
(eth_port_num));
|
|
|
|
if (reg_data & 0xFF) {
|
|
/* Issue stop command for active channels only */
|
|
MV_REG_WRITE (MV64360_ETH_RECEIVE_QUEUE_COMMAND_REG
|
|
(eth_port_num), (reg_data << 8));
|
|
|
|
/* Wait for all Rx activity to terminate. */
|
|
do {
|
|
/* Check port cause register that all Rx queues are stopped */
|
|
reg_data =
|
|
MV_REG_READ
|
|
(MV64360_ETH_RECEIVE_QUEUE_COMMAND_REG
|
|
(eth_port_num));
|
|
}
|
|
while (reg_data & 0xFF);
|
|
}
|
|
|
|
|
|
/* Clear all MIB counters */
|
|
eth_clear_mib_counters (eth_port_num);
|
|
|
|
/* Reset the Enable bit in the Configuration Register */
|
|
reg_data =
|
|
MV_REG_READ (MV64360_ETH_PORT_SERIAL_CONTROL_REG
|
|
(eth_port_num));
|
|
reg_data &= ~ETH_SERIAL_PORT_ENABLE;
|
|
MV_REG_WRITE (MV64360_ETH_PORT_SERIAL_CONTROL_REG (eth_port_num),
|
|
reg_data);
|
|
|
|
return;
|
|
}
|
|
|
|
#if 0 /* Not needed here */
|
|
/*******************************************************************************
|
|
* ethernet_set_config_reg - Set specified bits in configuration register.
|
|
*
|
|
* DESCRIPTION:
|
|
* This function sets specified bits in the given ethernet
|
|
* configuration register.
|
|
*
|
|
* INPUT:
|
|
* ETH_PORT eth_port_num Ethernet Port number. See ETH_PORT enum.
|
|
* unsigned int value 32 bit value.
|
|
*
|
|
* OUTPUT:
|
|
* The set bits in the value parameter are set in the configuration
|
|
* register.
|
|
*
|
|
* RETURN:
|
|
* None.
|
|
*
|
|
*******************************************************************************/
|
|
static void ethernet_set_config_reg (ETH_PORT eth_port_num,
|
|
unsigned int value)
|
|
{
|
|
unsigned int eth_config_reg;
|
|
|
|
eth_config_reg =
|
|
MV_REG_READ (MV64360_ETH_PORT_CONFIG_REG (eth_port_num));
|
|
eth_config_reg |= value;
|
|
MV_REG_WRITE (MV64360_ETH_PORT_CONFIG_REG (eth_port_num),
|
|
eth_config_reg);
|
|
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#if 0 /* FIXME */
|
|
/*******************************************************************************
|
|
* ethernet_reset_config_reg - Reset specified bits in configuration register.
|
|
*
|
|
* DESCRIPTION:
|
|
* This function resets specified bits in the given Ethernet
|
|
* configuration register.
|
|
*
|
|
* INPUT:
|
|
* ETH_PORT eth_port_num Ethernet Port number. See ETH_PORT enum.
|
|
* unsigned int value 32 bit value.
|
|
*
|
|
* OUTPUT:
|
|
* The set bits in the value parameter are reset in the configuration
|
|
* register.
|
|
*
|
|
* RETURN:
|
|
* None.
|
|
*
|
|
*******************************************************************************/
|
|
static void ethernet_reset_config_reg (ETH_PORT eth_port_num,
|
|
unsigned int value)
|
|
{
|
|
unsigned int eth_config_reg;
|
|
|
|
eth_config_reg = MV_REG_READ (MV64360_ETH_PORT_CONFIG_EXTEND_REG
|
|
(eth_port_num));
|
|
eth_config_reg &= ~value;
|
|
MV_REG_WRITE (MV64360_ETH_PORT_CONFIG_EXTEND_REG (eth_port_num),
|
|
eth_config_reg);
|
|
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#if 0 /* Not needed here */
|
|
/*******************************************************************************
|
|
* ethernet_get_config_reg - Get the port configuration register
|
|
*
|
|
* DESCRIPTION:
|
|
* This function returns the configuration register value of the given
|
|
* ethernet port.
|
|
*
|
|
* INPUT:
|
|
* ETH_PORT eth_port_num Ethernet Port number. See ETH_PORT enum.
|
|
*
|
|
* OUTPUT:
|
|
* None.
|
|
*
|
|
* RETURN:
|
|
* Port configuration register value.
|
|
*
|
|
*******************************************************************************/
|
|
static unsigned int ethernet_get_config_reg (ETH_PORT eth_port_num)
|
|
{
|
|
unsigned int eth_config_reg;
|
|
|
|
eth_config_reg = MV_REG_READ (MV64360_ETH_PORT_CONFIG_EXTEND_REG
|
|
(eth_port_num));
|
|
return eth_config_reg;
|
|
}
|
|
|
|
#endif
|
|
|
|
/*******************************************************************************
|
|
* eth_port_read_smi_reg - Read PHY registers
|
|
*
|
|
* DESCRIPTION:
|
|
* This routine utilize the SMI interface to interact with the PHY in
|
|
* order to perform PHY register read.
|
|
*
|
|
* INPUT:
|
|
* ETH_PORT eth_port_num Ethernet Port number. See ETH_PORT enum.
|
|
* unsigned int phy_reg PHY register address offset.
|
|
* unsigned int *value Register value buffer.
|
|
*
|
|
* OUTPUT:
|
|
* Write the value of a specified PHY register into given buffer.
|
|
*
|
|
* RETURN:
|
|
* false if the PHY is busy or read data is not in valid state.
|
|
* true otherwise.
|
|
*
|
|
*******************************************************************************/
|
|
static bool eth_port_read_smi_reg (ETH_PORT eth_port_num,
|
|
unsigned int phy_reg, unsigned int *value)
|
|
{
|
|
unsigned int reg_value;
|
|
unsigned int time_out = PHY_BUSY_TIMEOUT;
|
|
int phy_addr;
|
|
|
|
phy_addr = ethernet_phy_get (eth_port_num);
|
|
/* printf(" Phy-Port %d has addess %d \n",eth_port_num, phy_addr );*/
|
|
|
|
/* first check that it is not busy */
|
|
do {
|
|
reg_value = MV_REG_READ (MV64360_ETH_SMI_REG);
|
|
if (time_out-- == 0) {
|
|
return false;
|
|
}
|
|
}
|
|
while (reg_value & ETH_SMI_BUSY);
|
|
|
|
/* not busy */
|
|
|
|
MV_REG_WRITE (MV64360_ETH_SMI_REG,
|
|
(phy_addr << 16) | (phy_reg << 21) |
|
|
ETH_SMI_OPCODE_READ);
|
|
|
|
time_out = PHY_BUSY_TIMEOUT; /* initialize the time out var again */
|
|
|
|
do {
|
|
reg_value = MV_REG_READ (MV64360_ETH_SMI_REG);
|
|
if (time_out-- == 0) {
|
|
return false;
|
|
}
|
|
}
|
|
while ((reg_value & ETH_SMI_READ_VALID) != ETH_SMI_READ_VALID); /* Bit set equ operation done */
|
|
|
|
/* Wait for the data to update in the SMI register */
|
|
#define PHY_UPDATE_TIMEOUT 10000
|
|
for (time_out = 0; time_out < PHY_UPDATE_TIMEOUT; time_out++);
|
|
|
|
reg_value = MV_REG_READ (MV64360_ETH_SMI_REG);
|
|
|
|
*value = reg_value & 0xffff;
|
|
|
|
return true;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* eth_port_write_smi_reg - Write to PHY registers
|
|
*
|
|
* DESCRIPTION:
|
|
* This routine utilize the SMI interface to interact with the PHY in
|
|
* order to perform writes to PHY registers.
|
|
*
|
|
* INPUT:
|
|
* ETH_PORT eth_port_num Ethernet Port number. See ETH_PORT enum.
|
|
* unsigned int phy_reg PHY register address offset.
|
|
* unsigned int value Register value.
|
|
*
|
|
* OUTPUT:
|
|
* Write the given value to the specified PHY register.
|
|
*
|
|
* RETURN:
|
|
* false if the PHY is busy.
|
|
* true otherwise.
|
|
*
|
|
*******************************************************************************/
|
|
static bool eth_port_write_smi_reg (ETH_PORT eth_port_num,
|
|
unsigned int phy_reg, unsigned int value)
|
|
{
|
|
unsigned int reg_value;
|
|
unsigned int time_out = PHY_BUSY_TIMEOUT;
|
|
int phy_addr;
|
|
|
|
phy_addr = ethernet_phy_get (eth_port_num);
|
|
|
|
/* first check that it is not busy */
|
|
do {
|
|
reg_value = MV_REG_READ (MV64360_ETH_SMI_REG);
|
|
if (time_out-- == 0) {
|
|
return false;
|
|
}
|
|
}
|
|
while (reg_value & ETH_SMI_BUSY);
|
|
|
|
/* not busy */
|
|
MV_REG_WRITE (MV64360_ETH_SMI_REG,
|
|
(phy_addr << 16) | (phy_reg << 21) |
|
|
ETH_SMI_OPCODE_WRITE | (value & 0xffff));
|
|
return true;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* eth_set_access_control - Config address decode parameters for Ethernet unit
|
|
*
|
|
* DESCRIPTION:
|
|
* This function configures the address decode parameters for the Gigabit
|
|
* Ethernet Controller according the given parameters struct.
|
|
*
|
|
* INPUT:
|
|
* ETH_PORT eth_port_num Ethernet Port number. See ETH_PORT enum.
|
|
* ETH_WIN_PARAM *param Address decode parameter struct.
|
|
*
|
|
* OUTPUT:
|
|
* An access window is opened using the given access parameters.
|
|
*
|
|
* RETURN:
|
|
* None.
|
|
*
|
|
*******************************************************************************/
|
|
static void eth_set_access_control (ETH_PORT eth_port_num,
|
|
ETH_WIN_PARAM * param)
|
|
{
|
|
unsigned int access_prot_reg;
|
|
|
|
/* Set access control register */
|
|
access_prot_reg = MV_REG_READ (MV64360_ETH_ACCESS_PROTECTION_REG
|
|
(eth_port_num));
|
|
access_prot_reg &= (~(3 << (param->win * 2))); /* clear window permission */
|
|
access_prot_reg |= (param->access_ctrl << (param->win * 2));
|
|
MV_REG_WRITE (MV64360_ETH_ACCESS_PROTECTION_REG (eth_port_num),
|
|
access_prot_reg);
|
|
|
|
/* Set window Size reg (SR) */
|
|
MV_REG_WRITE ((MV64360_ETH_SIZE_REG_0 +
|
|
(ETH_SIZE_REG_GAP * param->win)),
|
|
(((param->size / 0x10000) - 1) << 16));
|
|
|
|
/* Set window Base address reg (BA) */
|
|
MV_REG_WRITE ((MV64360_ETH_BAR_0 + (ETH_BAR_GAP * param->win)),
|
|
(param->target | param->attributes | param->base_addr));
|
|
/* High address remap reg (HARR) */
|
|
if (param->win < 4)
|
|
MV_REG_WRITE ((MV64360_ETH_HIGH_ADDR_REMAP_REG_0 +
|
|
(ETH_HIGH_ADDR_REMAP_REG_GAP * param->win)),
|
|
param->high_addr);
|
|
|
|
/* Base address enable reg (BARER) */
|
|
if (param->enable == 1)
|
|
MV_RESET_REG_BITS (MV64360_ETH_BASE_ADDR_ENABLE_REG,
|
|
(1 << param->win));
|
|
else
|
|
MV_SET_REG_BITS (MV64360_ETH_BASE_ADDR_ENABLE_REG,
|
|
(1 << param->win));
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* ether_init_rx_desc_ring - Curve a Rx chain desc list and buffer in memory.
|
|
*
|
|
* DESCRIPTION:
|
|
* This function prepares a Rx chained list of descriptors and packet
|
|
* buffers in a form of a ring. The routine must be called after port
|
|
* initialization routine and before port start routine.
|
|
* The Ethernet SDMA engine uses CPU bus addresses to access the various
|
|
* devices in the system (i.e. DRAM). This function uses the ethernet
|
|
* struct 'virtual to physical' routine (set by the user) to set the ring
|
|
* with physical addresses.
|
|
*
|
|
* INPUT:
|
|
* ETH_PORT_INFO *p_eth_port_ctrl Ethernet Port Control srtuct.
|
|
* ETH_QUEUE rx_queue Number of Rx queue.
|
|
* int rx_desc_num Number of Rx descriptors
|
|
* int rx_buff_size Size of Rx buffer
|
|
* unsigned int rx_desc_base_addr Rx descriptors memory area base addr.
|
|
* unsigned int rx_buff_base_addr Rx buffer memory area base addr.
|
|
*
|
|
* OUTPUT:
|
|
* The routine updates the Ethernet port control struct with information
|
|
* regarding the Rx descriptors and buffers.
|
|
*
|
|
* RETURN:
|
|
* false if the given descriptors memory area is not aligned according to
|
|
* Ethernet SDMA specifications.
|
|
* true otherwise.
|
|
*
|
|
*******************************************************************************/
|
|
static bool ether_init_rx_desc_ring (ETH_PORT_INFO * p_eth_port_ctrl,
|
|
ETH_QUEUE rx_queue,
|
|
int rx_desc_num,
|
|
int rx_buff_size,
|
|
unsigned int rx_desc_base_addr,
|
|
unsigned int rx_buff_base_addr)
|
|
{
|
|
ETH_RX_DESC *p_rx_desc;
|
|
ETH_RX_DESC *p_rx_prev_desc; /* pointer to link with the last descriptor */
|
|
unsigned int buffer_addr;
|
|
int ix; /* a counter */
|
|
|
|
|
|
p_rx_desc = (ETH_RX_DESC *) rx_desc_base_addr;
|
|
p_rx_prev_desc = p_rx_desc;
|
|
buffer_addr = rx_buff_base_addr;
|
|
|
|
/* Rx desc Must be 4LW aligned (i.e. Descriptor_Address[3:0]=0000). */
|
|
if (rx_buff_base_addr & 0xF)
|
|
return false;
|
|
|
|
/* Rx buffers are limited to 64K bytes and Minimum size is 8 bytes */
|
|
if ((rx_buff_size < 8) || (rx_buff_size > RX_BUFFER_MAX_SIZE))
|
|
return false;
|
|
|
|
/* Rx buffers must be 64-bit aligned. */
|
|
if ((rx_buff_base_addr + rx_buff_size) & 0x7)
|
|
return false;
|
|
|
|
/* initialize the Rx descriptors ring */
|
|
for (ix = 0; ix < rx_desc_num; ix++) {
|
|
p_rx_desc->buf_size = rx_buff_size;
|
|
p_rx_desc->byte_cnt = 0x0000;
|
|
p_rx_desc->cmd_sts =
|
|
ETH_BUFFER_OWNED_BY_DMA | ETH_RX_ENABLE_INTERRUPT;
|
|
p_rx_desc->next_desc_ptr =
|
|
((unsigned int) p_rx_desc) + RX_DESC_ALIGNED_SIZE;
|
|
p_rx_desc->buf_ptr = buffer_addr;
|
|
p_rx_desc->return_info = 0x00000000;
|
|
D_CACHE_FLUSH_LINE (p_rx_desc, 0);
|
|
buffer_addr += rx_buff_size;
|
|
p_rx_prev_desc = p_rx_desc;
|
|
p_rx_desc = (ETH_RX_DESC *)
|
|
((unsigned int) p_rx_desc + RX_DESC_ALIGNED_SIZE);
|
|
}
|
|
|
|
/* Closing Rx descriptors ring */
|
|
p_rx_prev_desc->next_desc_ptr = (rx_desc_base_addr);
|
|
D_CACHE_FLUSH_LINE (p_rx_prev_desc, 0);
|
|
|
|
/* Save Rx desc pointer to driver struct. */
|
|
CURR_RFD_SET ((ETH_RX_DESC *) rx_desc_base_addr, rx_queue);
|
|
USED_RFD_SET ((ETH_RX_DESC *) rx_desc_base_addr, rx_queue);
|
|
|
|
p_eth_port_ctrl->p_rx_desc_area_base[rx_queue] =
|
|
(ETH_RX_DESC *) rx_desc_base_addr;
|
|
p_eth_port_ctrl->rx_desc_area_size[rx_queue] =
|
|
rx_desc_num * RX_DESC_ALIGNED_SIZE;
|
|
|
|
p_eth_port_ctrl->port_rx_queue_command |= (1 << rx_queue);
|
|
|
|
return true;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* ether_init_tx_desc_ring - Curve a Tx chain desc list and buffer in memory.
|
|
*
|
|
* DESCRIPTION:
|
|
* This function prepares a Tx chained list of descriptors and packet
|
|
* buffers in a form of a ring. The routine must be called after port
|
|
* initialization routine and before port start routine.
|
|
* The Ethernet SDMA engine uses CPU bus addresses to access the various
|
|
* devices in the system (i.e. DRAM). This function uses the ethernet
|
|
* struct 'virtual to physical' routine (set by the user) to set the ring
|
|
* with physical addresses.
|
|
*
|
|
* INPUT:
|
|
* ETH_PORT_INFO *p_eth_port_ctrl Ethernet Port Control srtuct.
|
|
* ETH_QUEUE tx_queue Number of Tx queue.
|
|
* int tx_desc_num Number of Tx descriptors
|
|
* int tx_buff_size Size of Tx buffer
|
|
* unsigned int tx_desc_base_addr Tx descriptors memory area base addr.
|
|
* unsigned int tx_buff_base_addr Tx buffer memory area base addr.
|
|
*
|
|
* OUTPUT:
|
|
* The routine updates the Ethernet port control struct with information
|
|
* regarding the Tx descriptors and buffers.
|
|
*
|
|
* RETURN:
|
|
* false if the given descriptors memory area is not aligned according to
|
|
* Ethernet SDMA specifications.
|
|
* true otherwise.
|
|
*
|
|
*******************************************************************************/
|
|
static bool ether_init_tx_desc_ring (ETH_PORT_INFO * p_eth_port_ctrl,
|
|
ETH_QUEUE tx_queue,
|
|
int tx_desc_num,
|
|
int tx_buff_size,
|
|
unsigned int tx_desc_base_addr,
|
|
unsigned int tx_buff_base_addr)
|
|
{
|
|
|
|
ETH_TX_DESC *p_tx_desc;
|
|
ETH_TX_DESC *p_tx_prev_desc;
|
|
unsigned int buffer_addr;
|
|
int ix; /* a counter */
|
|
|
|
|
|
/* save the first desc pointer to link with the last descriptor */
|
|
p_tx_desc = (ETH_TX_DESC *) tx_desc_base_addr;
|
|
p_tx_prev_desc = p_tx_desc;
|
|
buffer_addr = tx_buff_base_addr;
|
|
|
|
/* Tx desc Must be 4LW aligned (i.e. Descriptor_Address[3:0]=0000). */
|
|
if (tx_buff_base_addr & 0xF)
|
|
return false;
|
|
|
|
/* Tx buffers are limited to 64K bytes and Minimum size is 8 bytes */
|
|
if ((tx_buff_size > TX_BUFFER_MAX_SIZE)
|
|
|| (tx_buff_size < TX_BUFFER_MIN_SIZE))
|
|
return false;
|
|
|
|
/* Initialize the Tx descriptors ring */
|
|
for (ix = 0; ix < tx_desc_num; ix++) {
|
|
p_tx_desc->byte_cnt = 0x0000;
|
|
p_tx_desc->l4i_chk = 0x0000;
|
|
p_tx_desc->cmd_sts = 0x00000000;
|
|
p_tx_desc->next_desc_ptr =
|
|
((unsigned int) p_tx_desc) + TX_DESC_ALIGNED_SIZE;
|
|
|
|
p_tx_desc->buf_ptr = buffer_addr;
|
|
p_tx_desc->return_info = 0x00000000;
|
|
D_CACHE_FLUSH_LINE (p_tx_desc, 0);
|
|
buffer_addr += tx_buff_size;
|
|
p_tx_prev_desc = p_tx_desc;
|
|
p_tx_desc = (ETH_TX_DESC *)
|
|
((unsigned int) p_tx_desc + TX_DESC_ALIGNED_SIZE);
|
|
|
|
}
|
|
/* Closing Tx descriptors ring */
|
|
p_tx_prev_desc->next_desc_ptr = tx_desc_base_addr;
|
|
D_CACHE_FLUSH_LINE (p_tx_prev_desc, 0);
|
|
/* Set Tx desc pointer in driver struct. */
|
|
CURR_TFD_SET ((ETH_TX_DESC *) tx_desc_base_addr, tx_queue);
|
|
USED_TFD_SET ((ETH_TX_DESC *) tx_desc_base_addr, tx_queue);
|
|
|
|
/* Init Tx ring base and size parameters */
|
|
p_eth_port_ctrl->p_tx_desc_area_base[tx_queue] =
|
|
(ETH_TX_DESC *) tx_desc_base_addr;
|
|
p_eth_port_ctrl->tx_desc_area_size[tx_queue] =
|
|
(tx_desc_num * TX_DESC_ALIGNED_SIZE);
|
|
|
|
/* Add the queue to the list of Tx queues of this port */
|
|
p_eth_port_ctrl->port_tx_queue_command |= (1 << tx_queue);
|
|
|
|
return true;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* eth_port_send - Send an Ethernet packet
|
|
*
|
|
* DESCRIPTION:
|
|
* This routine send a given packet described by p_pktinfo parameter. It
|
|
* supports transmitting of a packet spaned over multiple buffers. The
|
|
* routine updates 'curr' and 'first' indexes according to the packet
|
|
* segment passed to the routine. In case the packet segment is first,
|
|
* the 'first' index is update. In any case, the 'curr' index is updated.
|
|
* If the routine get into Tx resource error it assigns 'curr' index as
|
|
* 'first'. This way the function can abort Tx process of multiple
|
|
* descriptors per packet.
|
|
*
|
|
* INPUT:
|
|
* ETH_PORT_INFO *p_eth_port_ctrl Ethernet Port Control srtuct.
|
|
* ETH_QUEUE tx_queue Number of Tx queue.
|
|
* PKT_INFO *p_pkt_info User packet buffer.
|
|
*
|
|
* OUTPUT:
|
|
* Tx ring 'curr' and 'first' indexes are updated.
|
|
*
|
|
* RETURN:
|
|
* ETH_QUEUE_FULL in case of Tx resource error.
|
|
* ETH_ERROR in case the routine can not access Tx desc ring.
|
|
* ETH_QUEUE_LAST_RESOURCE if the routine uses the last Tx resource.
|
|
* ETH_OK otherwise.
|
|
*
|
|
*******************************************************************************/
|
|
static ETH_FUNC_RET_STATUS eth_port_send (ETH_PORT_INFO * p_eth_port_ctrl,
|
|
ETH_QUEUE tx_queue,
|
|
PKT_INFO * p_pkt_info)
|
|
{
|
|
volatile ETH_TX_DESC *p_tx_desc_first;
|
|
volatile ETH_TX_DESC *p_tx_desc_curr;
|
|
volatile ETH_TX_DESC *p_tx_next_desc_curr;
|
|
volatile ETH_TX_DESC *p_tx_desc_used;
|
|
unsigned int command_status;
|
|
|
|
/* Do not process Tx ring in case of Tx ring resource error */
|
|
if (p_eth_port_ctrl->tx_resource_err[tx_queue] == true)
|
|
return ETH_QUEUE_FULL;
|
|
|
|
/* Get the Tx Desc ring indexes */
|
|
CURR_TFD_GET (p_tx_desc_curr, tx_queue);
|
|
USED_TFD_GET (p_tx_desc_used, tx_queue);
|
|
|
|
if (p_tx_desc_curr == NULL)
|
|
return ETH_ERROR;
|
|
|
|
/* The following parameters are used to save readings from memory */
|
|
p_tx_next_desc_curr = TX_NEXT_DESC_PTR (p_tx_desc_curr, tx_queue);
|
|
command_status = p_pkt_info->cmd_sts | ETH_ZERO_PADDING | ETH_GEN_CRC;
|
|
|
|
if (command_status & (ETH_TX_FIRST_DESC)) {
|
|
/* Update first desc */
|
|
FIRST_TFD_SET (p_tx_desc_curr, tx_queue);
|
|
p_tx_desc_first = p_tx_desc_curr;
|
|
} else {
|
|
FIRST_TFD_GET (p_tx_desc_first, tx_queue);
|
|
command_status |= ETH_BUFFER_OWNED_BY_DMA;
|
|
}
|
|
|
|
/* Buffers with a payload smaller than 8 bytes must be aligned to 64-bit */
|
|
/* boundary. We use the memory allocated for Tx descriptor. This memory */
|
|
/* located in TX_BUF_OFFSET_IN_DESC offset within the Tx descriptor. */
|
|
if (p_pkt_info->byte_cnt <= 8) {
|
|
printf ("You have failed in the < 8 bytes errata - fixme\n"); /* RABEEH - TBD */
|
|
return ETH_ERROR;
|
|
|
|
p_tx_desc_curr->buf_ptr =
|
|
(unsigned int) p_tx_desc_curr + TX_BUF_OFFSET_IN_DESC;
|
|
eth_b_copy (p_pkt_info->buf_ptr, p_tx_desc_curr->buf_ptr,
|
|
p_pkt_info->byte_cnt);
|
|
} else
|
|
p_tx_desc_curr->buf_ptr = p_pkt_info->buf_ptr;
|
|
|
|
p_tx_desc_curr->byte_cnt = p_pkt_info->byte_cnt;
|
|
p_tx_desc_curr->return_info = p_pkt_info->return_info;
|
|
|
|
if (p_pkt_info->cmd_sts & (ETH_TX_LAST_DESC)) {
|
|
/* Set last desc with DMA ownership and interrupt enable. */
|
|
p_tx_desc_curr->cmd_sts = command_status |
|
|
ETH_BUFFER_OWNED_BY_DMA | ETH_TX_ENABLE_INTERRUPT;
|
|
|
|
if (p_tx_desc_curr != p_tx_desc_first)
|
|
p_tx_desc_first->cmd_sts |= ETH_BUFFER_OWNED_BY_DMA;
|
|
|
|
/* Flush CPU pipe */
|
|
|
|
D_CACHE_FLUSH_LINE ((unsigned int) p_tx_desc_curr, 0);
|
|
D_CACHE_FLUSH_LINE ((unsigned int) p_tx_desc_first, 0);
|
|
CPU_PIPE_FLUSH;
|
|
|
|
/* Apply send command */
|
|
ETH_ENABLE_TX_QUEUE (tx_queue, p_eth_port_ctrl->port_num);
|
|
|
|
/* Finish Tx packet. Update first desc in case of Tx resource error */
|
|
p_tx_desc_first = p_tx_next_desc_curr;
|
|
FIRST_TFD_SET (p_tx_desc_first, tx_queue);
|
|
|
|
} else {
|
|
p_tx_desc_curr->cmd_sts = command_status;
|
|
D_CACHE_FLUSH_LINE ((unsigned int) p_tx_desc_curr, 0);
|
|
}
|
|
|
|
/* Check for ring index overlap in the Tx desc ring */
|
|
if (p_tx_next_desc_curr == p_tx_desc_used) {
|
|
/* Update the current descriptor */
|
|
CURR_TFD_SET (p_tx_desc_first, tx_queue);
|
|
|
|
p_eth_port_ctrl->tx_resource_err[tx_queue] = true;
|
|
return ETH_QUEUE_LAST_RESOURCE;
|
|
} else {
|
|
/* Update the current descriptor */
|
|
CURR_TFD_SET (p_tx_next_desc_curr, tx_queue);
|
|
return ETH_OK;
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* eth_tx_return_desc - Free all used Tx descriptors
|
|
*
|
|
* DESCRIPTION:
|
|
* This routine returns the transmitted packet information to the caller.
|
|
* It uses the 'first' index to support Tx desc return in case a transmit
|
|
* of a packet spanned over multiple buffer still in process.
|
|
* In case the Tx queue was in "resource error" condition, where there are
|
|
* no available Tx resources, the function resets the resource error flag.
|
|
*
|
|
* INPUT:
|
|
* ETH_PORT_INFO *p_eth_port_ctrl Ethernet Port Control srtuct.
|
|
* ETH_QUEUE tx_queue Number of Tx queue.
|
|
* PKT_INFO *p_pkt_info User packet buffer.
|
|
*
|
|
* OUTPUT:
|
|
* Tx ring 'first' and 'used' indexes are updated.
|
|
*
|
|
* RETURN:
|
|
* ETH_ERROR in case the routine can not access Tx desc ring.
|
|
* ETH_RETRY in case there is transmission in process.
|
|
* ETH_END_OF_JOB if the routine has nothing to release.
|
|
* ETH_OK otherwise.
|
|
*
|
|
*******************************************************************************/
|
|
static ETH_FUNC_RET_STATUS eth_tx_return_desc (ETH_PORT_INFO *
|
|
p_eth_port_ctrl,
|
|
ETH_QUEUE tx_queue,
|
|
PKT_INFO * p_pkt_info)
|
|
{
|
|
volatile ETH_TX_DESC *p_tx_desc_used = NULL;
|
|
volatile ETH_TX_DESC *p_tx_desc_first = NULL;
|
|
unsigned int command_status;
|
|
|
|
|
|
/* Get the Tx Desc ring indexes */
|
|
USED_TFD_GET (p_tx_desc_used, tx_queue);
|
|
FIRST_TFD_GET (p_tx_desc_first, tx_queue);
|
|
|
|
|
|
/* Sanity check */
|
|
if (p_tx_desc_used == NULL)
|
|
return ETH_ERROR;
|
|
|
|
command_status = p_tx_desc_used->cmd_sts;
|
|
|
|
/* Still transmitting... */
|
|
if (command_status & (ETH_BUFFER_OWNED_BY_DMA)) {
|
|
D_CACHE_FLUSH_LINE ((unsigned int) p_tx_desc_used, 0);
|
|
return ETH_RETRY;
|
|
}
|
|
|
|
/* Stop release. About to overlap the current available Tx descriptor */
|
|
if ((p_tx_desc_used == p_tx_desc_first) &&
|
|
(p_eth_port_ctrl->tx_resource_err[tx_queue] == false)) {
|
|
D_CACHE_FLUSH_LINE ((unsigned int) p_tx_desc_used, 0);
|
|
return ETH_END_OF_JOB;
|
|
}
|
|
|
|
/* Pass the packet information to the caller */
|
|
p_pkt_info->cmd_sts = command_status;
|
|
p_pkt_info->return_info = p_tx_desc_used->return_info;
|
|
p_tx_desc_used->return_info = 0;
|
|
|
|
/* Update the next descriptor to release. */
|
|
USED_TFD_SET (TX_NEXT_DESC_PTR (p_tx_desc_used, tx_queue), tx_queue);
|
|
|
|
/* Any Tx return cancels the Tx resource error status */
|
|
if (p_eth_port_ctrl->tx_resource_err[tx_queue] == true)
|
|
p_eth_port_ctrl->tx_resource_err[tx_queue] = false;
|
|
|
|
D_CACHE_FLUSH_LINE ((unsigned int) p_tx_desc_used, 0);
|
|
|
|
return ETH_OK;
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* eth_port_receive - Get received information from Rx ring.
|
|
*
|
|
* DESCRIPTION:
|
|
* This routine returns the received data to the caller. There is no
|
|
* data copying during routine operation. All information is returned
|
|
* using pointer to packet information struct passed from the caller.
|
|
* If the routine exhausts Rx ring resources then the resource error flag
|
|
* is set.
|
|
*
|
|
* INPUT:
|
|
* ETH_PORT_INFO *p_eth_port_ctrl Ethernet Port Control srtuct.
|
|
* ETH_QUEUE rx_queue Number of Rx queue.
|
|
* PKT_INFO *p_pkt_info User packet buffer.
|
|
*
|
|
* OUTPUT:
|
|
* Rx ring current and used indexes are updated.
|
|
*
|
|
* RETURN:
|
|
* ETH_ERROR in case the routine can not access Rx desc ring.
|
|
* ETH_QUEUE_FULL if Rx ring resources are exhausted.
|
|
* ETH_END_OF_JOB if there is no received data.
|
|
* ETH_OK otherwise.
|
|
*
|
|
*******************************************************************************/
|
|
static ETH_FUNC_RET_STATUS eth_port_receive (ETH_PORT_INFO * p_eth_port_ctrl,
|
|
ETH_QUEUE rx_queue,
|
|
PKT_INFO * p_pkt_info)
|
|
{
|
|
volatile ETH_RX_DESC *p_rx_curr_desc;
|
|
volatile ETH_RX_DESC *p_rx_next_curr_desc;
|
|
volatile ETH_RX_DESC *p_rx_used_desc;
|
|
unsigned int command_status;
|
|
|
|
/* Do not process Rx ring in case of Rx ring resource error */
|
|
if (p_eth_port_ctrl->rx_resource_err[rx_queue] == true) {
|
|
printf ("\nRx Queue is full ...\n");
|
|
return ETH_QUEUE_FULL;
|
|
}
|
|
|
|
/* Get the Rx Desc ring 'curr and 'used' indexes */
|
|
CURR_RFD_GET (p_rx_curr_desc, rx_queue);
|
|
USED_RFD_GET (p_rx_used_desc, rx_queue);
|
|
|
|
/* Sanity check */
|
|
if (p_rx_curr_desc == NULL)
|
|
return ETH_ERROR;
|
|
|
|
/* The following parameters are used to save readings from memory */
|
|
p_rx_next_curr_desc = RX_NEXT_DESC_PTR (p_rx_curr_desc, rx_queue);
|
|
command_status = p_rx_curr_desc->cmd_sts;
|
|
|
|
/* Nothing to receive... */
|
|
if (command_status & (ETH_BUFFER_OWNED_BY_DMA)) {
|
|
/* DP(printf("Rx: command_status: %08x\n", command_status)); */
|
|
D_CACHE_FLUSH_LINE ((unsigned int) p_rx_curr_desc, 0);
|
|
/* DP(printf("\nETH_END_OF_JOB ...\n"));*/
|
|
return ETH_END_OF_JOB;
|
|
}
|
|
|
|
p_pkt_info->byte_cnt = (p_rx_curr_desc->byte_cnt) - RX_BUF_OFFSET;
|
|
p_pkt_info->cmd_sts = command_status;
|
|
p_pkt_info->buf_ptr = (p_rx_curr_desc->buf_ptr) + RX_BUF_OFFSET;
|
|
p_pkt_info->return_info = p_rx_curr_desc->return_info;
|
|
p_pkt_info->l4i_chk = p_rx_curr_desc->buf_size; /* IP fragment indicator */
|
|
|
|
/* Clean the return info field to indicate that the packet has been */
|
|
/* moved to the upper layers */
|
|
p_rx_curr_desc->return_info = 0;
|
|
|
|
/* Update 'curr' in data structure */
|
|
CURR_RFD_SET (p_rx_next_curr_desc, rx_queue);
|
|
|
|
/* Rx descriptors resource exhausted. Set the Rx ring resource error flag */
|
|
if (p_rx_next_curr_desc == p_rx_used_desc)
|
|
p_eth_port_ctrl->rx_resource_err[rx_queue] = true;
|
|
|
|
D_CACHE_FLUSH_LINE ((unsigned int) p_rx_curr_desc, 0);
|
|
CPU_PIPE_FLUSH;
|
|
return ETH_OK;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* eth_rx_return_buff - Returns a Rx buffer back to the Rx ring.
|
|
*
|
|
* DESCRIPTION:
|
|
* This routine returns a Rx buffer back to the Rx ring. It retrieves the
|
|
* next 'used' descriptor and attached the returned buffer to it.
|
|
* In case the Rx ring was in "resource error" condition, where there are
|
|
* no available Rx resources, the function resets the resource error flag.
|
|
*
|
|
* INPUT:
|
|
* ETH_PORT_INFO *p_eth_port_ctrl Ethernet Port Control srtuct.
|
|
* ETH_QUEUE rx_queue Number of Rx queue.
|
|
* PKT_INFO *p_pkt_info Information on the returned buffer.
|
|
*
|
|
* OUTPUT:
|
|
* New available Rx resource in Rx descriptor ring.
|
|
*
|
|
* RETURN:
|
|
* ETH_ERROR in case the routine can not access Rx desc ring.
|
|
* ETH_OK otherwise.
|
|
*
|
|
*******************************************************************************/
|
|
static ETH_FUNC_RET_STATUS eth_rx_return_buff (ETH_PORT_INFO *
|
|
p_eth_port_ctrl,
|
|
ETH_QUEUE rx_queue,
|
|
PKT_INFO * p_pkt_info)
|
|
{
|
|
volatile ETH_RX_DESC *p_used_rx_desc; /* Where to return Rx resource */
|
|
|
|
/* Get 'used' Rx descriptor */
|
|
USED_RFD_GET (p_used_rx_desc, rx_queue);
|
|
|
|
/* Sanity check */
|
|
if (p_used_rx_desc == NULL)
|
|
return ETH_ERROR;
|
|
|
|
p_used_rx_desc->buf_ptr = p_pkt_info->buf_ptr;
|
|
p_used_rx_desc->return_info = p_pkt_info->return_info;
|
|
p_used_rx_desc->byte_cnt = p_pkt_info->byte_cnt;
|
|
p_used_rx_desc->buf_size = MV64360_RX_BUFFER_SIZE; /* Reset Buffer size */
|
|
|
|
/* Flush the write pipe */
|
|
CPU_PIPE_FLUSH;
|
|
|
|
/* Return the descriptor to DMA ownership */
|
|
p_used_rx_desc->cmd_sts =
|
|
ETH_BUFFER_OWNED_BY_DMA | ETH_RX_ENABLE_INTERRUPT;
|
|
|
|
/* Flush descriptor and CPU pipe */
|
|
D_CACHE_FLUSH_LINE ((unsigned int) p_used_rx_desc, 0);
|
|
CPU_PIPE_FLUSH;
|
|
|
|
/* Move the used descriptor pointer to the next descriptor */
|
|
USED_RFD_SET (RX_NEXT_DESC_PTR (p_used_rx_desc, rx_queue), rx_queue);
|
|
|
|
/* Any Rx return cancels the Rx resource error status */
|
|
if (p_eth_port_ctrl->rx_resource_err[rx_queue] == true)
|
|
p_eth_port_ctrl->rx_resource_err[rx_queue] = false;
|
|
|
|
return ETH_OK;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
* eth_port_set_rx_coal - Sets coalescing interrupt mechanism on RX path
|
|
*
|
|
* DESCRIPTION:
|
|
* This routine sets the RX coalescing interrupt mechanism parameter.
|
|
* This parameter is a timeout counter, that counts in 64 t_clk
|
|
* chunks ; that when timeout event occurs a maskable interrupt
|
|
* occurs.
|
|
* The parameter is calculated using the tClk of the MV-643xx chip
|
|
* , and the required delay of the interrupt in usec.
|
|
*
|
|
* INPUT:
|
|
* ETH_PORT eth_port_num Ethernet port number
|
|
* unsigned int t_clk t_clk of the MV-643xx chip in HZ units
|
|
* unsigned int delay Delay in usec
|
|
*
|
|
* OUTPUT:
|
|
* Interrupt coalescing mechanism value is set in MV-643xx chip.
|
|
*
|
|
* RETURN:
|
|
* The interrupt coalescing value set in the gigE port.
|
|
*
|
|
*******************************************************************************/
|
|
#if 0 /* FIXME */
|
|
static unsigned int eth_port_set_rx_coal (ETH_PORT eth_port_num,
|
|
unsigned int t_clk,
|
|
unsigned int delay)
|
|
{
|
|
unsigned int coal;
|
|
|
|
coal = ((t_clk / 1000000) * delay) / 64;
|
|
/* Set RX Coalescing mechanism */
|
|
MV_REG_WRITE (MV64360_ETH_SDMA_CONFIG_REG (eth_port_num),
|
|
((coal & 0x3fff) << 8) |
|
|
(MV_REG_READ
|
|
(MV64360_ETH_SDMA_CONFIG_REG (eth_port_num))
|
|
& 0xffc000ff));
|
|
return coal;
|
|
}
|
|
|
|
#endif
|
|
/*******************************************************************************
|
|
* eth_port_set_tx_coal - Sets coalescing interrupt mechanism on TX path
|
|
*
|
|
* DESCRIPTION:
|
|
* This routine sets the TX coalescing interrupt mechanism parameter.
|
|
* This parameter is a timeout counter, that counts in 64 t_clk
|
|
* chunks ; that when timeout event occurs a maskable interrupt
|
|
* occurs.
|
|
* The parameter is calculated using the t_cLK frequency of the
|
|
* MV-643xx chip and the required delay in the interrupt in uSec
|
|
*
|
|
* INPUT:
|
|
* ETH_PORT eth_port_num Ethernet port number
|
|
* unsigned int t_clk t_clk of the MV-643xx chip in HZ units
|
|
* unsigned int delay Delay in uSeconds
|
|
*
|
|
* OUTPUT:
|
|
* Interrupt coalescing mechanism value is set in MV-643xx chip.
|
|
*
|
|
* RETURN:
|
|
* The interrupt coalescing value set in the gigE port.
|
|
*
|
|
*******************************************************************************/
|
|
#if 0 /* FIXME */
|
|
static unsigned int eth_port_set_tx_coal (ETH_PORT eth_port_num,
|
|
unsigned int t_clk,
|
|
unsigned int delay)
|
|
{
|
|
unsigned int coal;
|
|
|
|
coal = ((t_clk / 1000000) * delay) / 64;
|
|
/* Set TX Coalescing mechanism */
|
|
MV_REG_WRITE (MV64360_ETH_TX_FIFO_URGENT_THRESHOLD_REG (eth_port_num),
|
|
coal << 4);
|
|
return coal;
|
|
}
|
|
#endif
|
|
|
|
/*******************************************************************************
|
|
* eth_b_copy - Copy bytes from source to destination
|
|
*
|
|
* DESCRIPTION:
|
|
* This function supports the eight bytes limitation on Tx buffer size.
|
|
* The routine will zero eight bytes starting from the destination address
|
|
* followed by copying bytes from the source address to the destination.
|
|
*
|
|
* INPUT:
|
|
* unsigned int src_addr 32 bit source address.
|
|
* unsigned int dst_addr 32 bit destination address.
|
|
* int byte_count Number of bytes to copy.
|
|
*
|
|
* OUTPUT:
|
|
* See description.
|
|
*
|
|
* RETURN:
|
|
* None.
|
|
*
|
|
*******************************************************************************/
|
|
static void eth_b_copy (unsigned int src_addr, unsigned int dst_addr,
|
|
int byte_count)
|
|
{
|
|
/* Zero the dst_addr area */
|
|
*(unsigned int *) dst_addr = 0x0;
|
|
|
|
while (byte_count != 0) {
|
|
*(char *) dst_addr = *(char *) src_addr;
|
|
dst_addr++;
|
|
src_addr++;
|
|
byte_count--;
|
|
}
|
|
}
|