
From: prafulla_wadaskar prafulla@marvell.com
Contributors: Yotam Admon yotam@marvell.com Michael Blostein <michaelbl@marvell.com
Signed-off-by: prafulla_wadaskar prafulla@marvell.com Reviewed by: Ronen Shitrit rshitrit@marvell.com --- cpu/arm926ejs/kirkwood/Makefile | 1 + cpu/arm926ejs/kirkwood/egiga.c | 2169 +++++++++++++++++++++++++++++++++++ cpu/arm926ejs/kirkwood/egiga.h | 707 ++++++++++++ cpu/arm926ejs/kirkwood/egiga_regs.h | 161 +++ cpu/arm926ejs/kirkwood/kwcore.h | 2 + net/eth.c | 4 + 6 files changed, 3044 insertions(+), 0 deletions(-) create mode 100644 cpu/arm926ejs/kirkwood/egiga.c create mode 100644 cpu/arm926ejs/kirkwood/egiga.h create mode 100644 cpu/arm926ejs/kirkwood/egiga_regs.h
diff --git a/cpu/arm926ejs/kirkwood/Makefile b/cpu/arm926ejs/kirkwood/Makefile index 41ac8d7..3e20898 100644 --- a/cpu/arm926ejs/kirkwood/Makefile +++ b/cpu/arm926ejs/kirkwood/Makefile @@ -30,6 +30,7 @@ COBJS-y = timer.o COBJS-y += serial.o COBJS-y += kwcore.o COBJS-y += dram.o +COBJS-y += egiga.o COBJS-$(CONFIG_KIRKWOOD_SPI) += spi.o
SOBJS = soc_init.o diff --git a/cpu/arm926ejs/kirkwood/egiga.c b/cpu/arm926ejs/kirkwood/egiga.c new file mode 100644 index 0000000..609ee0c --- /dev/null +++ b/cpu/arm926ejs/kirkwood/egiga.c @@ -0,0 +1,2169 @@ +/* + * (C) Copyright 2009 + * Marvell Semiconductor <www.marvell.com> + * Prafulla Wadaskar prafulla@marvell.com + * + * (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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +/* + * mv_eth.c - header file for the polled mode GT ethernet driver + */ +#ifndef KWEGIGA_DEBUG +#define KWEGIGA_DEBUG 0 +#endif +#define DEBUG_PRINT KWEGIGA_DEBUG + +#include <common.h> +#include <net.h> +#include <malloc.h> +#include <debug_prints.h> +#include <../board/Marvell/common/ppc_error_no.h> + +#if defined (CONFIG_KIRKWOOD_EGIGA) + +/* In case SRAM is cache coherent or non-cacheable */ +#define CONFIG_NOT_COHERENT_CACHE +#define D_CACHE_FLUSH_LINE(addr, offset) ; +#define CPU_PIPE_FLUSH { __asm__ __volatile__ ("nop;nop;nop;nop;nop;nop;nop");} + +#include "egiga.h" + +/* PHY_BASE_ADR is board specific and can be configured */ +#if defined (CONFIG_PHY_BASE_ADR) +#define PHY_BASE_ADR CONFIG_PHY_BASE_ADR +#else +#define PHY_BASE_ADR 0x08 /* default phy base addr */ +#endif + +/************************************************************************* +* The first part is the high level driver of the gigE ethernet ports. * +*************************************************************************/ + +/* Definition for configuring driver */ +#undef UPDATE_STATS_BY_SOFTWARE + +/* Constants */ +#define MAGIC_ETH_RUNNING 8031971 +#define KW_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 KW_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 + +/* + * PHY link detection and printing is not a dependency of egiga controller + * By default phy link is assumed to be UP + * if you want to detect link by querying phy then, + * configure CONFIG_PHY_LINK_DETECT in board header file + * and provide these functions in board specific code + */ +#ifndef CONFIG_PHY_LINK_DETECT +#define eth_phy_link_status(x) (TRUE) +#define egiga_print_phy_status(x) +#else +static bool eth_phy_link_status(ETH_PORT prt_num); +static void egiga_print_phy_status(struct eth_device *dev); +#endif + +/* + * eth_smi_reg_read - Read from ethernet phy register. + * + * INPUT: + * @phy_adr - Phy address. + * @reg_ofs - Phy register offset. + * + * This function reads ethernet phy register. + * REturns 16bit phy register value, or 0xffff on error + */ +int eth_smi_reg_read(u32 eth_port_num, u32 phy_adr, u32 reg_ofs, u16 * data) +{ + u32 smi_reg; + volatile u32 timeout; + + /* check parameters */ + if ((phy_adr << ETH_PHY_SMI_DEV_ADDR_OFFS) & ~ETH_PHY_SMI_DEV_ADDR_MASK) { + error_print("Illegal PHY device address %d", phy_adr); + return -EFAULT; + } + if ((reg_ofs << KW_ETH_SMI_REG_ADDR_OFFS) & ~KW_ETH_SMI_REG_ADDR_MASK) { + error_print("Illegal PHY register offset %d", reg_ofs); + return -EFAULT; + } + + timeout = ETH_PHY_SMI_TIMEOUT; + /* wait till the SMI is not busy */ + do { + /* read smi register */ + smi_reg = KW_REG_READ(KW_ETH_SMI_REG(eth_port_num)); + if (timeout-- == 0) { + error_print("SMI busy timeout"); + return -EFAULT; + } + } while (smi_reg & ETH_PHY_SMI_BUSY_MASK); + + /* fill the phy address and regiser offset and read opcode */ + smi_reg = + (phy_adr << ETH_PHY_SMI_DEV_ADDR_OFFS) | (reg_ofs << + KW_ETH_SMI_REG_ADDR_OFFS) + | ETH_PHY_SMI_OPCODE_READ; + + /* write the smi register */ + KW_REG_WRITE(KW_ETH_SMI_REG(eth_port_num), smi_reg); + + timeout = ETH_PHY_SMI_TIMEOUT; + + /*wait till readed value is ready */ + do { + /* read smi register */ + smi_reg = KW_REG_READ(KW_ETH_SMI_REG(eth_port_num)); + + if (timeout-- == 0) { + error_print("SMI read-valid timeout"); + return -EFAULT; + } + } while (!(smi_reg & ETH_PHY_SMI_READ_VALID_MASK)); + + /* Wait for the data to update in the SMI register */ + for (timeout = 0; timeout < ETH_PHY_SMI_TIMEOUT; timeout++) ; + + *data = + (u16) (KW_REG_READ(KW_ETH_SMI_REG(eth_port_num)) & + ETH_PHY_SMI_DATA_MASK); + + debug_print8("Reg(phyadr %d, off %d) Phy-value = %04x", phy_adr, + reg_ofs, *data); + + return 0; +} + +/* + * eth_smi_reg_write - Write to ethernet phy register. + * + * @phy_adr - Phy address. + * @reg_ofs - Phy register offset. + * @data - 16bit data. + * + * This function write to ethernet phy register. + * Returns 0 if write succeed, -EINVAL on bad parameters , MV_ERROR on error . + * -ETIME on timeout + */ +int eth_smi_reg_write(u32 eth_port_num, u32 phy_adr, u32 reg_ofs, u16 data) +{ + u32 smi_reg; + volatile u32 timeout; + + /* check parameters */ + if ((phy_adr << ETH_PHY_SMI_DEV_ADDR_OFFS) & ~ETH_PHY_SMI_DEV_ADDR_MASK) { + error_print("Illegal phy address"); + return -EINVAL; + } + if ((reg_ofs << KW_ETH_SMI_REG_ADDR_OFFS) & ~KW_ETH_SMI_REG_ADDR_MASK) { + error_print("Illegal register offset"); + return -EINVAL; + } + + timeout = ETH_PHY_SMI_TIMEOUT; + + /* wait till the SMI is not busy */ + do { + /* read smi register */ + smi_reg = KW_REG_READ(KW_ETH_SMI_REG(eth_port_num)); + if (timeout-- == 0) { + error_print("SMI busy timeout"); + return -ETIME; + } + } while (smi_reg & ETH_PHY_SMI_BUSY_MASK); + + /* fill the phy address and regiser offset and write opcode and data */ + smi_reg = (data << ETH_PHY_SMI_DATA_OFFS); + smi_reg |= + (phy_adr << ETH_PHY_SMI_DEV_ADDR_OFFS) | (reg_ofs << + KW_ETH_SMI_REG_ADDR_OFFS); + smi_reg &= ~ETH_PHY_SMI_OPCODE_READ; + + /* write the smi register */ + KW_REG_WRITE(KW_ETH_SMI_REG(eth_port_num), smi_reg); + + return 0; + +} + +static int egiga_free_tx_rings(struct eth_device *dev) +{ + u32 queue; + ETH_PORT_INFO *ethernet_private; + struct egiga_priv *port_private; + u32 port_num; + volatile ETH_TX_DESC *p_tx_curr_desc; + + ethernet_private = (ETH_PORT_INFO *) dev->priv; + port_private = (struct egiga_priv *)ethernet_private->port_private; + port_num = port_private->port_num; + + /* Stop Tx Queues */ + KW_REG_WRITE(KW_ETH_TRANSMIT_QUEUE_COMMAND_REG(port_num), 0x0000ff00); + + /* Free TX rings */ + debug_print("Clearing previously allocated TX queues... "); + for (queue = 0; queue < KW_TX_QUEUE_NUM; queue++) { + /* Free on TX rings */ + for (p_tx_curr_desc = + ethernet_private->p_tx_desc_area_base[queue]; + ((u32)p_tx_curr_desc <= (u32) + ethernet_private->p_tx_desc_area_base[queue] + + ethernet_private->tx_desc_area_size[queue]); + p_tx_curr_desc = + (ETH_TX_DESC *) ((u32)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; + debug_print("freed"); + } + } + debug_print("Done"); + } + return 0; +} + +static int egiga_free_rx_rings(struct eth_device *dev) +{ + u32 queue; + ETH_PORT_INFO *ethernet_private; + struct egiga_priv *port_private; + u32 port_num; + volatile ETH_RX_DESC *p_rx_curr_desc; + + ethernet_private = (ETH_PORT_INFO *) dev->priv; + port_private = (struct egiga_priv *)ethernet_private->port_private; + port_num = port_private->port_num; + + /* Stop RX Queues */ + KW_REG_WRITE(KW_ETH_RECEIVE_QUEUE_COMMAND_REG(port_num), 0x0000ff00); + + /* Free RX rings */ + debug_print("Clearing previously allocated RX queues... "); + for (queue = 0; queue < KW_RX_QUEUE_NUM; queue++) { + /* Free preallocated skb's on RX rings */ + for (p_rx_curr_desc = + ethernet_private->p_rx_desc_area_base[queue]; + (((u32)p_rx_curr_desc < + ((u32)ethernet_private-> + p_rx_desc_area_base[queue] + + ethernet_private->rx_desc_area_size[queue]))); + p_rx_curr_desc = + (ETH_RX_DESC *) ((u32)p_rx_curr_desc + + RX_DESC_ALIGNED_SIZE)) { + if (p_rx_curr_desc->return_info != 0) { + p_rx_curr_desc->return_info = 0; + debug_print("freed"); + } + } + debug_print("Done"); + } + return 0; +} + +static void eth_phy_set_addr(ETH_PORT eth_port_num, int phy_addr) +{ + KW_REG_WRITE(KW_ETH_PHY_ADDR_REG(eth_port_num), phy_addr); +} + +#ifdef UPDATE_STATS_BY_SOFTWARE +/********************************************************************** + * egiga_print_stat + * + * Update the statistics structure in the private data structure + * + * Input : pointer to ethernet interface network device structure + * Output : N/A + **********************************************************************/ + +static void egiga_print_stat(struct eth_device *dev) +{ + ETH_PORT_INFO *ethernet_private; + struct egiga_priv *port_private; + struct net_device_stats *stats; + u32 port_num; + + ethernet_private = (ETH_PORT_INFO *) dev->priv; + port_private = (struct egiga_priv *)ethernet_private->port_private; + port_num = port_private->port_num; + 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 + +/* Helper function for egiga_stop */ +static int egiga_real_stop(struct eth_device *dev) +{ + ETH_PORT_INFO *ethernet_private; + struct egiga_priv *port_private; + u32 port_num; + + ethernet_private = (ETH_PORT_INFO *) dev->priv; + port_private = (struct egiga_priv *)ethernet_private->port_private; + port_num = port_private->port_num; + + egiga_free_tx_rings(dev); + egiga_free_rx_rings(dev); + + eth_port_reset(ethernet_private->port_num); + /* Disable ethernet port interrupts */ + KW_REG_WRITE(KW_ETH_INTERRUPT_CAUSE_REG(port_num), 0); + KW_REG_WRITE(KW_ETH_INTERRUPT_CAUSE_EXTEND_REG(port_num), 0); + /* Mask RX buffer and TX end interrupt */ + KW_REG_WRITE(KW_ETH_INTERRUPT_MASK_REG(port_num), 0); + /* Mask phy and link status changes interrupts */ + KW_REG_WRITE(KW_ETH_INTERRUPT_EXTEND_MASK_REG(port_num), 0); + /* make sure CPU interrupts are disabled + KW_REG_BITS_RESET (KW_INTERRUPT0_MASK_HIGH(port_num), + BIT0 << port_num); + */ + + /* Print Network statistics */ +#ifdef 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; + egiga_print_stat(dev); + } + memset(port_private->stats, 0, sizeof(struct net_device_stats)); +#endif + debug_print("Ethernet stopped ... "); + return 0; +} + +/********************************************************************** + * kw_egiga_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 kw_egiga_stop(struct eth_device *dev) +{ + ETH_PORT_INFO *ethernet_private; + struct egiga_priv *port_private; + u32 port_num; + + ethernet_private = (ETH_PORT_INFO *) dev->priv; + port_private = (struct egiga_priv *)ethernet_private->port_private; + port_num = port_private->port_num; + + debug_print_ftrace(); + /* Disable all gigE address decoder */ + KW_REG_WRITE(KW_ETH_BASE_ADDR_ENABLE_REG(port_num), 0x3f); + egiga_real_stop(dev); + + return 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; + u32 dummy; + + /* Perform dummy reads from MIB counters */ + for (i = ETH_MIB_GOOD_OCTETS_RECEIVED_LOW; i < ETH_MIB_LATE_COLLISION; + i += 4) + dummy = KW_REG_READ((KW_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. +* u32 mib_offset MIB counter offset (use ETH_MIB_... macros). +* +* OUTPUT: +* After reading the MIB counter, the counter resets. +* +* RETURN: +* MIB counter value. +* +*******************************************************************************/ +u32 eth_read_mib_counter(ETH_PORT eth_port_num, + u32 mib_offset) +{ + return (KW_REG_READ((KW_ETH_MIB_COUNTERS_BASE(eth_port_num) + + mib_offset))); +} + + +/********************************************************************** + * egiga_update_stat + * + * Update the statistics structure in the private data structure + * + * Input : pointer to ethernet interface network device structure + * Output : N/A + **********************************************************************/ + +static void egiga_update_stat(struct eth_device *dev) +{ + ETH_PORT_INFO *ethernet_private; + struct egiga_priv *port_private; + struct net_device_stats *stats; + u32 port_num; + volatile u32 dummy; + + ethernet_private = (ETH_PORT_INFO *) dev->priv; + port_private = (struct egiga_priv *)ethernet_private->port_private; + port_num = port_private->port_num; + 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 + */ + dummy = 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); + dummy = 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 */ +} + +/********************************************************************** + * egiga_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 *egiga_get_stats(struct eth_device *dev) +{ + ETH_PORT_INFO *ethernet_private; + struct egiga_priv *port_private; + u32 port_num; + + ethernet_private = (ETH_PORT_INFO *) dev->priv; + port_private = (struct egiga_priv *)ethernet_private->port_private; + port_num = port_private->port_num; + + egiga_update_stat(dev); + + return port_private->stats; +} + +/************************************************************************* +* The second part is the low level driver of the gigE ethernet ports. * +*************************************************************************/ + +/* defines */ +/* SDMA command macros */ +#define ETH_ENABLE_TX_QUEUE(tx_queue, eth_port) \ + KW_REG_WRITE(KW_ETH_TRANSMIT_QUEUE_COMMAND_REG(eth_port), (1 << tx_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*)(((((u32)p_rx_desc - \ + (u32)p_eth_port_ctrl->p_rx_desc_area_base[queue]) + \ + RX_DESC_ALIGNED_SIZE) % p_eth_port_ctrl->rx_desc_area_size[queue]) + \ + (u32)p_eth_port_ctrl->p_rx_desc_area_base[queue]) + +#define TX_NEXT_DESC_PTR(p_tx_desc, queue) ( \ + ETH_TX_DESC*)(((((u32)p_tx_desc - \ + (u32)p_eth_port_ctrl->p_tx_desc_area_base[queue]) + \ + TX_DESC_ALIGNED_SIZE) % p_eth_port_ctrl->tx_desc_area_size[queue]) + \ + (u32)p_eth_port_ctrl->p_tx_desc_area_base[queue]) + +/******************************************************************************* + * 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) +{ + u32 access_prot_reg; + + debug_print_ftrace(); + /* Set access control register */ + access_prot_reg = KW_REG_READ(KW_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)); + KW_REG_WRITE(KW_ETH_ACCESS_PROTECTION_REG(eth_port_num), + access_prot_reg); + + /* Set window Size reg (SR) */ + KW_REG_WRITE((KW_ETH_SIZE_REG_0(eth_port_num) + + (ETH_SIZE_REG_GAP * param->win)), + (((param->size / 0x10000) - 1) << 16)); + + /* Set window Base address reg (BA) */ + KW_REG_WRITE((KW_ETH_BAR_0(eth_port_num) + (ETH_BAR_GAP * param->win)), + (param->target | param->attributes | param->base_addr)); + /* High address remap reg (HARR) */ + if (param->win < 4) + KW_REG_WRITE((KW_ETH_HIGH_ADDR_REMAP_REG_0(eth_port_num) + + (ETH_HIGH_ADDR_REMAP_REG_GAP * param->win)), + param->high_addr); + + /* Base address enable reg (BARER) */ + if (param->enable == 1) + KW_REG_BITS_RESET(KW_ETH_BASE_ADDR_ENABLE_REG(eth_port_num), + (1 << param->win)); + else + KW_REG_BITS_SET(KW_ETH_BASE_ADDR_ENABLE_REG(eth_port_num), + (1 << param->win)); +} + +static void eth_set_access_control_for_dram(u32 port_num) +{ + ETH_WIN_PARAM win_param; + int i; + + for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { + /* Set access parameters for DRAM bank i */ + win_param.win = i; /* Use Ethernet window i */ + win_param.target = ETH_TARGET_DRAM; /* Window target - DDR */ + win_param.access_ctrl = EWIN_ACCESS_FULL; /* Enable full access */ + win_param.high_addr = 0; + /* Get bank base */ + win_param.base_addr = kw_get_dram_bank_base_addr(i); + win_param.size = kw_get_dram_bank_size(i); /* Get bank size */ + if (win_param.size == 0) + win_param.enable = 0; + else + win_param.enable = 1; /* Enable the access */ + switch(i) { + case 0: + /* Enable DRAM bank 0 */ + win_param.attributes = EBAR_ATTR_DRAM_CS0; + break; + case 1: + /* Enable DRAM bank 1 */ + win_param.attributes = EBAR_ATTR_DRAM_CS1; + break; + case 2: + /* Enable DRAM bank 2 */ + win_param.attributes = EBAR_ATTR_DRAM_CS2; + break; + case 3: + /* Enable DRAM bank 3 */ + win_param.attributes = EBAR_ATTR_DRAM_CS3; + break; + default: + /* invalide bank, disable access */ + win_param.enable = 0; + win_param.attributes = 0; + break; + } +#ifndef CONFIG_NOT_COHERENT_CACHE + win_param.attributes |= EBAR_ATTR_DRAM_CACHE_COHERENCY_WB; +#endif + /* Set the access control for address window (EPAPR) READ & WRITE */ + eth_set_access_control(port_num, &win_param); + } +} + +/******************************************************************************* +* 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) + KW_REG_WRITE((KW_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) */ + KW_REG_WRITE((KW_ETH_DA_FILTER_SPECIAL_MULTICAST_TABLE_BASE + (eth_port_num) + table_index), 0); + /* Clear DA filter other multicast table (Ex_dFOMT) */ + KW_REG_WRITE((KW_ETH_DA_FILTER_OTHER_MULTICAST_TABLE_BASE + (eth_port_num) + table_index), 0); + } +} + +/******************************************************************************* +* 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; + u32 port_num = p_eth_port_ctrl->port_num; + + 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; + + /* Stop RX Queues */ + KW_REG_WRITE(KW_ETH_RECEIVE_QUEUE_COMMAND_REG(port_num), 0x0000ff00); + + /* Clear the ethernet port interrupts */ + KW_REG_WRITE(KW_ETH_INTERRUPT_CAUSE_REG(port_num), 0); + KW_REG_WRITE(KW_ETH_INTERRUPT_CAUSE_EXTEND_REG(port_num), 0); + + /* Unmask RX buffer and TX end interrupt */ + KW_REG_WRITE(KW_ETH_INTERRUPT_MASK_REG(port_num), INT_CAUSE_UNMASK_ALL); + + /* Unmask phy and link status changes interrupts */ + KW_REG_WRITE(KW_ETH_INTERRUPT_EXTEND_MASK_REG(port_num), + INT_CAUSE_UNMASK_ALL_EXT); + + /* 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(port_num); + + eth_set_access_control_for_dram(port_num); + + eth_port_init_mac_tables(port_num); +} + + +/******************************************************************************* +* 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 kw_egiga_init_tx_desc_ring +* for Tx and kw_egiga_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 kw_egiga_init_tx_desc_ring for Tx queues and +* kw_egiga_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 void 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; + 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); + KW_REG_WRITE((KW_ETH_TX_CURRENT_QUEUE_DESC_PTR_0(eth_port_num) + + (4 * queue)), ((u32)p_tx_curr_desc)); + + } + + /* Assignment of Rx CRDB of given queue */ + for (queue = 0; queue < MAX_RX_QUEUE_NUM; queue++) { + CURR_RFD_GET(p_rx_curr_desc, queue); + KW_REG_WRITE((KW_ETH_RX_CURRENT_QUEUE_DESC_PTR_0(eth_port_num) + + (4 * queue)), ((u32)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. */ + KW_REG_WRITE(KW_ETH_PORT_CONFIG_REG(eth_port_num), + p_eth_port_ctrl->port_config); + + KW_REG_WRITE(KW_ETH_PORT_CONFIG_EXTEND_REG(eth_port_num), + p_eth_port_ctrl->port_config_extend); + + KW_REG_WRITE(KW_ETH_PORT_SERIAL_CONTROL0_REG(eth_port_num), + p_eth_port_ctrl->port_serial_control); + + KW_REG_BITS_SET(KW_ETH_PORT_SERIAL_CONTROL0_REG(eth_port_num), + ETH_SERIAL_PORT_ENABLE); + + /* Assign port SDMA configuration */ + KW_REG_WRITE(KW_ETH_SDMA_CONFIG_REG(eth_port_num), + p_eth_port_ctrl->port_sdma_config); + + KW_REG_WRITE(KW_ETH_TX_QUEUE_0_TOKEN_BUCKET_COUNT + (eth_port_num), 0x3fffffff); + KW_REG_WRITE(KW_ETH_TX_QUEUE_0_TOKEN_BUCKET_CONFIG + (eth_port_num), 0x03fffcff); + /* Turn off the port/queue bandwidth limitation */ + KW_REG_WRITE(KW_ETH_MAXIMUM_TRANSMIT_UNIT(eth_port_num), 0x0); + + /* Enable port Rx. */ + KW_REG_WRITE(KW_ETH_RECEIVE_QUEUE_COMMAND_REG(eth_port_num), + p_eth_port_ctrl->port_rx_queue_command); + + /* Set maximum receive buffer to 9700 bytes */ + KW_REG_WRITE(KW_ETH_PORT_SERIAL_CONTROL0_REG(eth_port_num), + (0x5 << 17) | + (KW_REG_READ(KW_ETH_PORT_SERIAL_CONTROL0_REG(eth_port_num)) + & 0xfff1ffff)); + + /* + * Set ethernet MTU for leaky bucket mechanism to 0 - this will + * disable the leaky bucket mechanism . + */ + KW_REG_WRITE(KW_ETH_MAXIMUM_TRANSMIT_UNIT(eth_port_num), 0); +} + +/******************************************************************************* +* 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. +* u8 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, + u8 uc_nibble, + ETH_QUEUE queue, int option) +{ + u32 unicast_reg; + u32 tbl_offset; + u32 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 = + KW_REG_READ((KW_ETH_DA_FILTER_UNICAST_TABLE_BASE + (eth_port_num) + + tbl_offset)); + + unicast_reg &= (0xFF << (8 * reg_offset)); + + KW_REG_WRITE((KW_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 = + KW_REG_READ((KW_ETH_DA_FILTER_UNICAST_TABLE_BASE + (eth_port_num) + + tbl_offset)); + + unicast_reg &= (0xFF << (8 * reg_offset)); + unicast_reg |= ((0x01 | (queue << 1)) << (8 * reg_offset)); + + KW_REG_WRITE((KW_ETH_DA_FILTER_UNICAST_TABLE_BASE(eth_port_num) + + tbl_offset), unicast_reg); + + break; + + default: + 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, + u8 *p_addr, ETH_QUEUE queue) +{ + u32 mac_h; + u32 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); + + KW_REG_WRITE(KW_ETH_MAC_ADDR_LOW(eth_port_num), mac_l); + KW_REG_WRITE(KW_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_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) +{ + u32 reg_data, i; + + /* Stop Tx port activity. Check port Tx activity. */ + reg_data = KW_REG_READ(KW_ETH_TRANSMIT_QUEUE_COMMAND_REG(eth_port_num)); + + if (reg_data & 0xFF) { + /* Issue stop command for active channels only */ + KW_REG_WRITE(KW_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 = + KW_REG_READ + (KW_ETH_TRANSMIT_QUEUE_COMMAND_REG(eth_port_num)); + } + while (reg_data & 0xFF); + } + + /* Stop Rx port activity. Check port Rx activity. */ + reg_data = KW_REG_READ(KW_ETH_RECEIVE_QUEUE_COMMAND_REG(eth_port_num)); + + if (reg_data & 0xFF) { + /* Issue stop command for active channels only */ + KW_REG_WRITE(KW_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 = + KW_REG_READ + (KW_ETH_RECEIVE_QUEUE_COMMAND_REG(eth_port_num)); + } + while (reg_data & 0xFF); + } + + /* Clear all MIB counters */ + eth_clear_mib_counters(eth_port_num); + + /* Enable port in the Configuration Register */ + KW_REG_BITS_RESET(KW_ETH_PORT_SERIAL_CONTROL0_REG(eth_port_num), + ETH_SERIAL_PORT_ENABLE); + /* Set port of active in the Configuration Register */ + KW_REG_BITS_RESET(KW_ETH_PORT_SERIAL_CONTROL1_REG(eth_port_num), BIT4); +#ifdef CONFIG_SYS_MII_MODE + /* Set MMI interface up */ + KW_REG_BITS_RESET(KW_ETH_PORT_SERIAL_CONTROL1_REG(eth_port_num), BIT3); +#endif + + for (i = 0; i < 4000; i++) ; + return; +} + +/******************************************************************************* + * kw_egiga_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 + * u32 rx_desc_base_addr Rx descriptors memory area base addr. + * u32 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 kw_egiga_init_rx_desc_ring(ETH_PORT_INFO * p_eth_port_ctrl, + ETH_QUEUE rx_queue, + int rx_desc_num, + int rx_buff_size, + u32 rx_desc_base_addr, + u32 rx_buff_base_addr) +{ + ETH_RX_DESC *p_rx_desc; + ETH_RX_DESC *p_rx_prev_desc; /* pointer to link with the last descriptor */ + u32 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 = + ((u32)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 *) + ((u32)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; +} + +/******************************************************************************* + * kw_egiga_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 + * u32 tx_desc_base_addr Tx descriptors memory area base addr. + * u32 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 kw_egiga_init_tx_desc_ring(ETH_PORT_INFO * p_eth_port_ctrl, + ETH_QUEUE tx_queue, + int tx_desc_num, + int tx_buff_size, + u32 tx_desc_base_addr, + u32 tx_buff_base_addr) +{ + + ETH_TX_DESC *p_tx_desc; + ETH_TX_DESC *p_tx_prev_desc; + u32 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 = + ((u32)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 *) + ((u32)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_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: + * u32 src_addr 32 bit source address. + * u32 dst_addr 32 bit destination address. + * int byte_count Number of bytes to copy. + * + * OUTPUT: + * See description. + * + * RETURN: + * None. + * + *******************************************************************************/ +static void eth_b_copy(u32 src_addr, u32 dst_addr, + int byte_count) +{ + /* Zero the dst_addr area */ + *(u32 *)dst_addr = 0x0; + + while (byte_count != 0) { + *(char *)dst_addr = *(char *)src_addr; + dst_addr++; + src_addr++; + byte_count--; + } +} + +/******************************************************************************* + * 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; + u32 command_status; + +#ifdef CONFIG_TX_PKT_DISPLAY + { + u16 pcnt = p_pkt_info->byte_cnt; + u8 *prndt = (char *)p_pkt_info->buf_ptr; + printf("cnt=%d ", pcnt); + while (pcnt) { + printf("%02x,", prndt[0]); + prndt++; + pcnt--; + } + printf(" pckend \n"); + + } +#endif + /* 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 = + (u32)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((u32)p_tx_desc_curr, 0); + D_CACHE_FLUSH_LINE((u32)p_tx_desc_first, 0); + CPU_PIPE_FLUSH; + + /* Apply send command */ + ETH_ENABLE_TX_QUEUE(tx_queue, p_eth_port_ctrl->port_num); + debug_print("packet xmitted"); + + /* 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((u32)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; + u32 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((u32)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((u32)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((u32)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; + u32 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) { + error_print("Rx Queue is full ..."); + 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)) { + /*debug_print("Rx: command_status: %08x", command_status); */ + D_CACHE_FLUSH_LINE((u32)p_rx_curr_desc, 0); + /*debug_print("ETH_END_OF_JOB ..."); */ + 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((u32)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 = KW_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((u32)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; +} + +/* + * helper function used for kirkwood_egiga_initialize + */ +void print_globals(struct eth_device *dev) +{ + debug_print("Ethernet PRINT_Globals-Debug function"); + debug_print("Base Address for ETH_PORT_INFO: %08x", + (u32)dev->priv); + debug_print("Base Address for egiga_priv: %08x", + (u32)&(((ETH_PORT_INFO *) dev->priv)->port_private)); + + debug_print + ("Base Address for TX-DESCs: %08x Number of allocated Buffers %d", + (u32)((ETH_PORT_INFO *) dev->priv)-> + p_tx_desc_area_base[0], KW_TX_QUEUE_SIZE); + debug_print + ("Base Address for RX-DESCs: %08x Number of allocated Buffers %d", + (u32)((ETH_PORT_INFO *) dev->priv)-> + p_rx_desc_area_base[0], KW_RX_QUEUE_SIZE); + debug_print("Base Address for RX-Buffer: %08x allocated Bytes %d", + (u32)((ETH_PORT_INFO *) dev->priv)-> + p_rx_buffer_base[0], + (KW_RX_QUEUE_SIZE * KW_RX_BUFFER_SIZE) + 32); + debug_print("Base Address for TX-Buffer: %08x allocated Bytes %d", + (u32)((ETH_PORT_INFO *) dev->priv)-> + p_tx_buffer_base[0], + (KW_TX_QUEUE_SIZE * KW_TX_BUFFER_SIZE) + 32); +} + + +/* + * u-boot entry functions + */ +static int kw_egiga_init(struct eth_device *dev) +{ + + u32 queue; + ETH_PORT_INFO *ethernet_private; + struct egiga_priv *port_private; + u32 port_num; + + debug_print_ftrace(); + + 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 egiga_priv *)ethernet_private->port_private; + port_num = port_private->port_num; + + /* Activate the DMA channels etc */ + eth_port_init(ethernet_private); + + /* "Allocate" setup TX rings */ + + for (queue = 0; queue < KW_TX_QUEUE_NUM; queue++) { + u32 size; + + port_private->tx_ring_size[queue] = KW_TX_QUEUE_SIZE; + /*size = no of DESCs times DESC-size */ + size = + (port_private->tx_ring_size[queue] * TX_DESC_ALIGNED_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 (kw_egiga_init_tx_desc_ring + (ethernet_private, ETH_Q0, + port_private->tx_ring_size[queue], + KW_TX_BUFFER_SIZE /* Each Buffer is 1600 Byte */ , + (u32)ethernet_private-> + p_tx_desc_area_base[queue], + (u32)ethernet_private-> + p_tx_buffer_base[queue]) == false) + error_print("### initializing TX Ring"); + } + + /* "Allocate" setup RX rings */ + for (queue = 0; queue < KW_RX_QUEUE_NUM; queue++) { + u32 size; + + /* Meantime RX Ring are fixed - but must be configurable by user */ + port_private->rx_ring_size[queue] = KW_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 ((kw_egiga_init_rx_desc_ring + (ethernet_private, ETH_Q0, + port_private->rx_ring_size[queue], + KW_RX_BUFFER_SIZE /* Each Buffer is 1600 Byte */ , + (u32)ethernet_private-> + p_rx_desc_area_base[queue], + (u32)ethernet_private-> + p_rx_buffer_base[queue])) == false) + error_print("### initializing RX Ring"); + } + + eth_port_start(ethernet_private); + + if (!eth_phy_link_status(port_num)) { + warn_print("No link on port %d", port_num); + return 0; + } else { + egiga_print_phy_status(port_num); + } + port_private->eth_running = MAGIC_ETH_RUNNING; + return 1; +} + +/********************************************************************** + * egiga_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 kw_egiga_xmit(struct eth_device *dev, volatile void *dataPtr, int dataSize) +{ + ETH_PORT_INFO *ethernet_private; + struct egiga_priv *port_private; + u32 port_num; + PKT_INFO pkt_info; + ETH_FUNC_RET_STATUS status; + struct net_device_stats *stats; + ETH_FUNC_RET_STATUS release_result; + + debug_print_ftrace(); + ethernet_private = (ETH_PORT_INFO *) dev->priv; + port_private = (struct egiga_priv *)ethernet_private->port_private; + port_num = port_private->port_num; + + stats = port_private->stats; + + /* Update packet info data structure */ + pkt_info.cmd_sts = ETH_TX_FIRST_DESC | ETH_TX_LAST_DESC; /* first last */ + pkt_info.cmd_sts |= ETH_BUFFER_OWNED_BY_DMA; /* DMA owned */ + + pkt_info.byte_cnt = dataSize; + pkt_info.buf_ptr = (u32)dataPtr; + pkt_info.return_info = 0; + + status = eth_port_send(ethernet_private, ETH_Q0, &pkt_info); + if ((status == ETH_ERROR) || (status == ETH_QUEUE_FULL)) { + error_print("On transmitting packet .."); + if (status == ETH_QUEUE_FULL) + error_print("ETH Queue is full."); + if (status == ETH_QUEUE_LAST_RESOURCE) + error_print("ETH Queue: using last available resource."); + 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: + debug_print("descriptor released"); + if (pkt_info.cmd_sts & BIT0) { + error_print("in TX"); + stats->tx_errors++; + + } + break; + case ETH_RETRY: + debug_print("transmission still in process"); + break; + + case ETH_ERROR: + error_print("routine can not access Tx desc ring"); + break; + + case ETH_END_OF_JOB: + debug_print("the routine has nothing to release"); + break; + default: /* should not happen */ + break; + } + } while (release_result == ETH_OK); + + return 0; /* success */ + error: + return 1; /* Failed - higher layers will free the skb */ +} + +/********************************************************************** + * kw_egiga_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 kw_egiga_receive(struct eth_device *dev) +{ + ETH_PORT_INFO *ethernet_private; + struct egiga_priv *port_private; + u32 port_num; + PKT_INFO pkt_info; + struct net_device_stats *stats; + + ethernet_private = (ETH_PORT_INFO *) dev->priv; + port_private = (struct egiga_priv *)ethernet_private->port_private; + port_num = port_private->port_num; + stats = port_private->stats; + + while ((eth_port_receive(ethernet_private, ETH_Q0, &pkt_info) == + ETH_OK)) { + + if (pkt_info.byte_cnt != 0) { + debug_print("%s: Received %d byte Packet @ 0x%x", + __FUNCTION__, pkt_info.byte_cnt, + pkt_info.buf_ptr); + } + /* 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 { + debug_print("RX Desc returned to Ring"); + } + /* /free these descriptors again */ + } else { + + /* !!! call higher layer processing */ + debug_print("\nNow send it to upper layer protocols (NetReceive) ..."); + + /* 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 */ + debug_print("RX: pkt_info.buf_ptr = %x", + pkt_info.buf_ptr); + if (eth_rx_return_buff + (ethernet_private, ETH_Q0, &pkt_info) != ETH_OK) { + error_print + ("while returning the RX Desc to Ring"); + } else { + debug_print("RX Desc returned to Ring"); + } + /* free descriptor code end */ + } + } + egiga_get_stats(dev); /* update statistics */ + return 1; +} + +/* + * Called from net/eth.c + */ +int kirkwood_egiga_initialize(bd_t * bis) +{ + struct eth_device *dev; + ETH_PORT_INFO *ethernet_private; + struct egiga_priv *port_private; + int devnum, x, temp; + char *s, *e, buf[64]; + u8 used_ports[MAX_ETH_DEVS] = CONFIG_KIRKWOOD_EGIGA_PORTS; + + debug_print_ftrace(); + for (devnum = 0; devnum < MAX_ETH_DEVS; devnum++) { + /*skip if port is configured not to use */ + if (used_ports[devnum] == FALSE) + continue; + + dev = calloc(sizeof(*dev), 1); + if (!dev) { + printf("%s: mv_enet%d allocation failure, %s\n", + __FUNCTION__, devnum, "eth_device structure"); + return -1; + } + + /* must be less than NAMESIZE (16) */ + sprintf(dev->name, "egiga%d", devnum); + + debug_print("Initializing %s", dev->name); + + /* Extract the MAC address from the environment */ + switch (devnum) { + case 0: + s = "ethaddr"; + break; + + case 1: + s = "eth1addr"; + break; + + default: /* this should never happen */ + printf("%s: Invalid device number %d\n", + __FUNCTION__, devnum); + return -1; + } + + e = getenv(s); + if (!e) { + /* Generate Ramdom MAC addresses if not set */ + sprintf(buf, "00:50:43:%02x:%02x:%02x", + get_random_hex(), get_random_hex(), + get_random_hex()); + setenv(s, buf); + } + + temp = getenv_r(s, buf, sizeof(buf)); + s = (temp > 0) ? buf : NULL; + + debug_print("Setting MAC %d to %s", devnum, s); + /* Assume port is pre-powered up */ + /* eth_port_power_up(devnum); */ + + for (x = 0; x < 6; ++x) { + dev->enetaddr[x] = s ? simple_strtoul(s, &e, 16) : 0; + if (s) + s = (*e) ? e + 1 : e; + } + /* set the MAC addr in the HW */ + eth_port_uc_addr_set(devnum, dev->enetaddr, 0); + + dev->init = (void *)kw_egiga_init; + dev->halt = (void *)kw_egiga_stop; + dev->send = (void *)kw_egiga_xmit; + dev->recv = (void *)kw_egiga_receive; + + 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 -1; + } + /* 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 -1; + } + + 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 -1; + } + memset(ethernet_private->port_private, 0, + sizeof(struct egiga_priv)); + switch (devnum) { + case 0: + ethernet_private->port_num = ETH_0; + break; + case 1: + ethernet_private->port_num = ETH_1; + 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 + */ + egiga_update_stat(dev); + memset(port_private->stats, 0, sizeof(struct net_device_stats)); + + debug_print("Allocating descriptor and buffer rings"); + + ethernet_private->p_rx_desc_area_base[0] = + (ETH_RX_DESC *) memalign(16, + RX_DESC_ALIGNED_SIZE * + KW_RX_QUEUE_SIZE + 1); + ethernet_private->p_tx_desc_area_base[0] = + (ETH_TX_DESC *) memalign(16, + TX_DESC_ALIGNED_SIZE * + KW_TX_QUEUE_SIZE + 1); + + ethernet_private->p_rx_buffer_base[0] = + (char *)memalign(16, + KW_RX_QUEUE_SIZE * KW_TX_BUFFER_SIZE + 1); + ethernet_private->p_tx_buffer_base[0] = + (char *)memalign(16, + KW_RX_QUEUE_SIZE * KW_TX_BUFFER_SIZE + 1); + + /* DEBUG OUTPUT prints adresses of globals */ + print_globals(dev); + + eth_register(dev); + + /* Set phy address of the port */ + ethernet_private->port_phy_addr = + PHY_BASE_ADR + ethernet_private->port_num; + eth_phy_set_addr(ethernet_private->port_num, + ethernet_private->port_phy_addr); + + } + return 0; +} + +#endif /* CONFIG_KIRKWOOD_EGIGA */ diff --git a/cpu/arm926ejs/kirkwood/egiga.h b/cpu/arm926ejs/kirkwood/egiga.h new file mode 100644 index 0000000..83dc4b3 --- /dev/null +++ b/cpu/arm926ejs/kirkwood/egiga.h @@ -0,0 +1,707 @@ +/* + * (C) Copyright 2009 + * Marvell Semiconductor <www.marvell.com> + * Prafulla Wadaskar prafulla@marvell.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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef __EGIGA_H__ +#define __EGIGA_H__ + +#define MAX_ETH_DEVS 2 /*controller has two ports */ + +#include <asm/types.h> +#include <asm/byteorder.h> +#include <common.h> +#include <net.h> + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +/* Port attributes */ +/* Max queue number is 8 for controller but here configured 1 */ +#define MAX_RX_QUEUE_NUM 1 +#define MAX_TX_QUEUE_NUM 1 +/* Use one TX queue and one RX queue */ +#define KW_TX_QUEUE_NUM 1 +#define KW_RX_QUEUE_NUM 1 + +/* + * Number of RX / TX descriptors on RX / TX rings. + * Note that allocating RX descriptors is done by allocating the RX + * ring AND a preallocated RX buffers (skb's) for each descriptor. + * The TX descriptors only allocates the TX descriptors ring, + * with no pre allocated TX buffers (skb's are allocated by higher layers. + */ + +/* TX/RX parameters can be passed externally */ +#ifdef CONFIG_ETH_TXQUEUE_SIZE +#define KW_TX_QUEUE_SIZE CONFIG_ETH_TXQUEUE_SIZE +#else +#define KW_TX_QUEUE_SIZE 4 +#endif + +#ifdef CONFIG_ETH_RXQUEUE_SIZE +#define KW_RX_QUEUE_SIZE CONFIG_ETH_RXQUEUE_SIZE +#else +#define KW_RX_QUEUE_SIZE 4 +#endif + +#ifdef CONFIG_ETH_RXBUFFER_SIZE +#define KW_RX_BUFFER_SIZE CONFIG_ETH_RXBUFFER_SIZE +#else +#define KW_RX_BUFFER_SIZE 1600 +#endif + +#ifdef CONFIG_ETH_TXBUFFER_SIZE +#define KW_TX_BUFFER_SIZE CONFIG_ETH_TXBUFFER_SIZE +#else +#define KW_TX_BUFFER_SIZE 1600 +#endif + +#define RX_BUFFER_MAX_SIZE 0xFFFF +#define TX_BUFFER_MAX_SIZE 0xFFFF /* Buffers are limited to 64k */ +#define RX_BUFFER_MIN_SIZE 0x8 +#define TX_BUFFER_MIN_SIZE 0x8 + +/* + * Network device statistics. Akin to the 2.0 ether stats but + * with byte counters. + */ +struct net_device_stats { + u32 rx_packets; /* total packets received */ + u32 tx_packets; /* total packets transmitted */ + u32 rx_bytes; /* total bytes received */ + u32 tx_bytes; /* total bytes transmitted */ + u32 rx_errors; /* bad packets received */ + u32 tx_errors; /* packet transmit problems */ + u32 rx_dropped; /* no space in linux buffers */ + u32 tx_dropped; /* no space available in linux */ + u32 multicast; /* multicast packets received */ + u32 collisions; + /* detailed rx_errors: */ + u32 rx_length_errors; + u32 rx_over_errors; /* receiver ring buff overflow */ + u32 rx_crc_errors; /* recved pkt with crc error */ + u32 rx_frame_errors; /* recv'd frame alignment error */ + u32 rx_fifo_errors; /* recv'r fifo overrun */ + u32 rx_missed_errors; /* receiver missed packet */ + /* detailed tx_errors */ + u32 tx_aborted_errors; + u32 tx_carrier_errors; + u32 tx_fifo_errors; + u32 tx_heartbeat_errors; + u32 tx_window_errors; + /* for cslip etc */ + u32 rx_compressed; + u32 tx_compressed; +}; + +/* Private data structure used for ethernet device */ +struct egiga_priv { + u32 port_num; + struct net_device_stats *stats; + /* to buffer area aligned */ + char *p_eth_tx_buffer[KW_TX_QUEUE_SIZE + 1]; + char *p_eth_rx_buffer[KW_RX_QUEUE_SIZE + 1]; + /* Size of Tx Ring per queue */ + u32 tx_ring_size[MAX_TX_QUEUE_NUM]; + /* Size of Rx Ring per queue */ + u32 rx_ring_size[MAX_RX_QUEUE_NUM]; + /* Magic Number for Ethernet running */ + u32 eth_running; +}; + +/* + * The second part is the low level driver of the gigE ethernet ports. + */ + +/******************************************************************************** + * Header File for : network interface header + * + * DESCRIPTION: + * This header file contains macros typedefs and function declaration for + * the Marvell Gig Bit Ethernet Controller. + * + * DEPENDENCIES: + * None. + * + *******************************************************************************/ + +/* defines */ + +/* Default port configuration value */ +#define PORT_CONFIG_VALUE \ + ETH_UNICAST_NORMAL_MODE | \ + ETH_DEFAULT_RX_QUEUE_0 | \ + ETH_DEFAULT_RX_ARP_QUEUE_0 | \ + ETH_RECEIVE_BC_IF_NOT_IP_OR_ARP | \ + ETH_RECEIVE_BC_IF_IP | \ + ETH_RECEIVE_BC_IF_ARP | \ + ETH_CAPTURE_TCP_FRAMES_DIS | \ + ETH_CAPTURE_UDP_FRAMES_DIS | \ + ETH_DEFAULT_RX_TCP_QUEUE_0 | \ + ETH_DEFAULT_RX_UDP_QUEUE_0 | \ + ETH_DEFAULT_RX_BPDU_QUEUE_0 + +/* Default port extend configuration value */ +#define PORT_CONFIG_EXTEND_VALUE \ + ETH_SPAN_BPDU_PACKETS_AS_NORMAL | \ + ETH_PARTITION_DISABLE | \ + ETH_TX_CRC_GENERATION_ENABLE + +/* Default sdma control value */ +#ifdef CONFIG_NOT_COHERENT_CACHE +#define PORT_SDMA_CONFIG_VALUE \ + ETH_RX_BURST_SIZE_16_64BIT | \ + ETH_BLM_RX_NO_SWAP | \ + ETH_BLM_TX_NO_SWAP | \ + GT_ETH_IPG_INT_RX(0) | \ + ETH_TX_BURST_SIZE_16_64BIT; +#else +#define PORT_SDMA_CONFIG_VALUE \ + ETH_RX_BURST_SIZE_4_64BIT | \ + GT_ETH_IPG_INT_RX(0) | \ + ETH_TX_BURST_SIZE_4_64BIT; +#endif + +#define GT_ETH_IPG_INT_RX(value) \ + ((value & 0x3fff) << 8) + +/* Default port serial control value */ +#define PORT_SERIAL_CONTROL_VALUE_TMP \ + ETH_FORCE_LINK_PASS | \ + ETH_DISABLE_AUTO_NEG_FOR_DUPLX | \ + ETH_DISABLE_AUTO_NEG_FOR_FLOW_CTRL | \ + ETH_ADV_NO_FLOW_CTRL | \ + ETH_FORCE_FC_MODE_NO_PAUSE_DIS_TX | \ + ETH_FORCE_BP_MODE_NO_JAM | \ + BIT9 | \ + ETH_DO_NOT_FORCE_LINK_FAIL | \ + ETH_DISABLE_AUTO_NEG_SPEED_GMII | \ + ETH_DTE_ADV_0 | \ + ETH_MIIPHY_MAC_MODE | \ + ETH_AUTO_NEG_NO_CHANGE | \ + ETH_MAX_RX_PACKET_1552BYTE | \ + ETH_CLR_EXT_LOOPBACK | \ + ETH_SET_FULL_DUPLEX_MODE | \ + ETH_DISABLE_FLOW_CTRL_TX_RX_IN_FULL_DUPLEX | \ + ETH_SET_MII_SPEED_TO_100 + +#ifdef CONFIG_SYS_MII_MODE +#define PORT_SERIAL_CONTROL_VALUE \ + PORT_SERIAL_CONTROL_VALUE_TMP | \ + ETH_SET_GMII_SPEED_TO_10_100 +#else +#define PORT_SERIAL_CONTROL_VALUE \ + PORT_SERIAL_CONTROL_VALUE_TMP | \ + ETH_SET_GMII_SPEED_TO_1000 +#endif + +/* Tx WRR confoguration macros */ +#define PORT_MAX_TRAN_UNIT 0x24 /* MTU register (default) 9KByte */ +#define PORT_MAX_TOKEN_BUCKET_SIZE 0x_fFFF /* PMTBS register (default) */ +#define PORT_TOKEN_RATE 1023 /* PTTBRC register (default) */ +/* MAC accepet/reject macros */ +#define ACCEPT_MAC_ADDR 0 +#define REJECT_MAC_ADDR 1 +/* Size of a Tx/Rx descriptor used in chain list data structure */ +#define RX_DESC_ALIGNED_SIZE 0x20 +#define TX_DESC_ALIGNED_SIZE 0x20 +/* An offest in Tx descriptors to store data for buffers less than 8 Bytes */ +#define TX_BUF_OFFSET_IN_DESC 0x18 +/* Buffer offset from buffer pointer */ +#define RX_BUF_OFFSET 0x2 +/* Gap define */ +#define ETH_BAR_GAP 0x8 +#define ETH_SIZE_REG_GAP 0x8 +#define ETH_HIGH_ADDR_REMAP_REG_GAP 0x4 +#define ETH_PORT_ACCESS_CTRL_GAP 0x4 +/* MIB Counters register definitions */ +#define ETH_MIB_GOOD_OCTETS_RECEIVED_LOW 0x0 +#define ETH_MIB_GOOD_OCTETS_RECEIVED_HIGH 0x4 +#define ETH_MIB_BAD_OCTETS_RECEIVED 0x8 +#define ETH_MIB_INTERNAL_MAC_TRANSMIT_ERR 0xc +#define ETH_MIB_GOOD_FRAMES_RECEIVED 0x10 +#define ETH_MIB_BAD_FRAMES_RECEIVED 0x14 +#define ETH_MIB_BROADCAST_FRAMES_RECEIVED 0x18 +#define ETH_MIB_MULTICAST_FRAMES_RECEIVED 0x1c +#define ETH_MIB_FRAMES_64_OCTETS 0x20 +#define ETH_MIB_FRAMES_65_TO_127_OCTETS 0x24 +#define ETH_MIB_FRAMES_128_TO_255_OCTETS 0x28 +#define ETH_MIB_FRAMES_256_TO_511_OCTETS 0x2c +#define ETH_MIB_FRAMES_512_TO_1023_OCTETS 0x30 +#define ETH_MIB_FRAMES_1024_TO_MAX_OCTETS 0x34 +#define ETH_MIB_GOOD_OCTETS_SENT_LOW 0x38 +#define ETH_MIB_GOOD_OCTETS_SENT_HIGH 0x3c +#define ETH_MIB_GOOD_FRAMES_SENT 0x40 +#define ETH_MIB_EXCESSIVE_COLLISION 0x44 +#define ETH_MIB_MULTICAST_FRAMES_SENT 0x48 +#define ETH_MIB_BROADCAST_FRAMES_SENT 0x4c +#define ETH_MIB_UNREC_MAC_CONTROL_RECEIVED 0x50 +#define ETH_MIB_FC_SENT 0x54 +#define ETH_MIB_GOOD_FC_RECEIVED 0x58 +#define ETH_MIB_BAD_FC_RECEIVED 0x5c +#define ETH_MIB_UNDERSIZE_RECEIVED 0x60 +#define ETH_MIB_FRAGMENTS_RECEIVED 0x64 +#define ETH_MIB_OVERSIZE_RECEIVED 0x68 +#define ETH_MIB_JABBER_RECEIVED 0x6c +#define ETH_MIB_MAC_RECEIVE_ERROR 0x70 +#define ETH_MIB_BAD_CRC_EVENT 0x74 +#define ETH_MIB_COLLISION 0x78 +#define ETH_MIB_LATE_COLLISION 0x7c + +/* Port serial status reg (PSR) */ +#define ETH_INTERFACE_GMII_MII 0 +#define ETH_INTERFACE_PCM BIT0 +#define ETH_LINK_IS_DOWN 0 +#define ETH_LINK_IS_UP BIT1 +#define ETH_PORT_AT_HALF_DUPLEX 0 +#define ETH_PORT_AT_FULL_DUPLEX BIT2 +#define ETH_RX_FLOW_CTRL_DISABLED 0 +#define ETH_RX_FLOW_CTRL_ENBALED BIT3 +#define ETH_GMII_SPEED_100_10 0 +#define ETH_GMII_SPEED_1000 BIT4 +#define ETH_MII_SPEED_10 0 +#define ETH_MII_SPEED_100 BIT5 +#define ETH_NO_TX 0 +#define ETH_TX_IN_PROGRESS BIT7 +#define ETH_BYPASS_NO_ACTIVE 0 +#define ETH_BYPASS_ACTIVE BIT8 +#define ETH_PORT_NOT_AT_PARTITION_STATE 0 +#define ETH_PORT_AT_PARTITION_STATE BIT9 +#define ETH_PORT_TX_FIFO_NOT_EMPTY 0 +#define ETH_PORT_TX_FIFO_EMPTY BIT10 + +/* These macros describes the Port configuration reg (Px_cR) bits */ +#define ETH_UNICAST_NORMAL_MODE 0 +#define ETH_UNICAST_PROMISCUOUS_MODE BIT0 +#define ETH_DEFAULT_RX_QUEUE_0 0 +#define ETH_DEFAULT_RX_QUEUE_1 BIT1 +#define ETH_DEFAULT_RX_QUEUE_2 BIT2 +#define ETH_DEFAULT_RX_QUEUE_3 (BIT2 | BIT1) +#define ETH_DEFAULT_RX_QUEUE_4 BIT3 +#define ETH_DEFAULT_RX_QUEUE_5 (BIT3 | BIT1) +#define ETH_DEFAULT_RX_QUEUE_6 (BIT3 | BIT2) +#define ETH_DEFAULT_RX_QUEUE_7 (BIT3 | BIT2 | BIT1) +#define ETH_DEFAULT_RX_ARP_QUEUE_0 0 +#define ETH_DEFAULT_RX_ARP_QUEUE_1 BIT4 +#define ETH_DEFAULT_RX_ARP_QUEUE_2 BIT5 +#define ETH_DEFAULT_RX_ARP_QUEUE_3 (BIT5 | BIT4) +#define ETH_DEFAULT_RX_ARP_QUEUE_4 BIT6 +#define ETH_DEFAULT_RX_ARP_QUEUE_5 (BIT6 | BIT4) +#define ETH_DEFAULT_RX_ARP_QUEUE_6 (BIT6 | BIT5) +#define ETH_DEFAULT_RX_ARP_QUEUE_7 (BIT6 | BIT5 | BIT4) +#define ETH_RECEIVE_BC_IF_NOT_IP_OR_ARP 0 +#define ETH_REJECT_BC_IF_NOT_IP_OR_ARP BIT7 +#define ETH_RECEIVE_BC_IF_IP 0 +#define ETH_REJECT_BC_IF_IP BIT8 +#define ETH_RECEIVE_BC_IF_ARP 0 +#define ETH_REJECT_BC_IF_ARP BIT9 +#define ETH_TX_AM_NO_UPDATE_ERROR_SUMMARY BIT12 +#define ETH_CAPTURE_TCP_FRAMES_DIS 0 +#define ETH_CAPTURE_TCP_FRAMES_EN BIT14 +#define ETH_CAPTURE_UDP_FRAMES_DIS 0 +#define ETH_CAPTURE_UDP_FRAMES_EN BIT15 +#define ETH_DEFAULT_RX_TCP_QUEUE_0 0 +#define ETH_DEFAULT_RX_TCP_QUEUE_1 BIT16 +#define ETH_DEFAULT_RX_TCP_QUEUE_2 BIT17 +#define ETH_DEFAULT_RX_TCP_QUEUE_3 (BIT17 | BIT16) +#define ETH_DEFAULT_RX_TCP_QUEUE_4 BIT18 +#define ETH_DEFAULT_RX_TCP_QUEUE_5 (BIT18 | BIT16) +#define ETH_DEFAULT_RX_TCP_QUEUE_6 (BIT18 | BIT17) +#define ETH_DEFAULT_RX_TCP_QUEUE_7 (BIT18 | BIT17 | BIT16) +#define ETH_DEFAULT_RX_UDP_QUEUE_0 0 +#define ETH_DEFAULT_RX_UDP_QUEUE_1 BIT19 +#define ETH_DEFAULT_RX_UDP_QUEUE_2 BIT20 +#define ETH_DEFAULT_RX_UDP_QUEUE_3 (BIT20 | BIT19) +#define ETH_DEFAULT_RX_UDP_QUEUE_4 (BIT21 +#define ETH_DEFAULT_RX_UDP_QUEUE_5 (BIT21 | BIT19) +#define ETH_DEFAULT_RX_UDP_QUEUE_6 (BIT21 | BIT20) +#define ETH_DEFAULT_RX_UDP_QUEUE_7 (BIT21 | BIT20 | BIT19) +#define ETH_DEFAULT_RX_BPDU_QUEUE_0 0 +#define ETH_DEFAULT_RX_BPDU_QUEUE_1 BIT22 +#define ETH_DEFAULT_RX_BPDU_QUEUE_2 BIT23 +#define ETH_DEFAULT_RX_BPDU_QUEUE_3 (BIT23 | BIT22) +#define ETH_DEFAULT_RX_BPDU_QUEUE_4 BIT24 +#define ETH_DEFAULT_RX_BPDU_QUEUE_5 (BIT24 | BIT22) +#define ETH_DEFAULT_RX_BPDU_QUEUE_6 (BIT24 | BIT23) +#define ETH_DEFAULT_RX_BPDU_QUEUE_7 (BIT24 | BIT23 | BIT22) +#define ETH_DEFAULT_RX_TCP_CHKSUM_MODE BIT25 + +/* These macros describes the Port configuration extend reg (Px_cXR) bits*/ +#define ETH_CLASSIFY_EN BIT0 +#define ETH_SPAN_BPDU_PACKETS_AS_NORMAL 0 +#define ETH_SPAN_BPDU_PACKETS_TO_RX_QUEUE_7 BIT1 +#define ETH_PARTITION_DISABLE 0 +#define ETH_PARTITION_ENABLE BIT2 +#define ETH_TX_CRC_GENERATION_ENABLE 0 +#define ETH_TX_CRC_GENERATION_DISABLE BIT3 + +/* Tx/Rx queue command reg (RQCR/TQCR)*/ +#define ETH_QUEUE_0_ENABLE BIT0 +#define ETH_QUEUE_1_ENABLE BIT1 +#define ETH_QUEUE_2_ENABLE BIT2 +#define ETH_QUEUE_3_ENABLE BIT3 +#define ETH_QUEUE_4_ENABLE BIT4 +#define ETH_QUEUE_5_ENABLE BIT5 +#define ETH_QUEUE_6_ENABLE BIT6 +#define ETH_QUEUE_7_ENABLE BIT7 +#define ETH_QUEUE_0_DISABLE BIT8 +#define ETH_QUEUE_1_DISABLE BIT9 +#define ETH_QUEUE_2_DISABLE BIT10 +#define ETH_QUEUE_3_DISABLE BIT11 +#define ETH_QUEUE_4_DISABLE BIT12 +#define ETH_QUEUE_5_DISABLE BIT13 +#define ETH_QUEUE_6_DISABLE BIT14 +#define ETH_QUEUE_7_DISABLE BIT15 + +/* These macros describes the Port Sdma configuration reg (SDCR) bits */ +#define ETH_RIFB BIT0 +#define ETH_RX_BURST_SIZE_1_64BIT 0 +#define ETH_RX_BURST_SIZE_2_64BIT BIT1 +#define ETH_RX_BURST_SIZE_4_64BIT BIT2 +#define ETH_RX_BURST_SIZE_8_64BIT (BIT2 | BIT1) +#define ETH_RX_BURST_SIZE_16_64BIT BIT3 +#define ETH_BLM_RX_NO_SWAP BIT4 +#define ETH_BLM_RX_BYTE_SWAP 0 +#define ETH_BLM_TX_NO_SWAP BIT5 +#define ETH_BLM_TX_BYTE_SWAP 0 +#define ETH_DESCRIPTORS_BYTE_SWAP BIT6 +#define ETH_DESCRIPTORS_NO_SWAP 0 +#define ETH_TX_BURST_SIZE_1_64BIT 0 +#define ETH_TX_BURST_SIZE_2_64BIT BIT22 +#define ETH_TX_BURST_SIZE_4_64BIT BIT23 +#define ETH_TX_BURST_SIZE_8_64BIT (BIT23 | BIT22) +#define ETH_TX_BURST_SIZE_16_64BIT BIT24 + +/* These macros describes the Port serial control reg (PSCR) bits */ +#define ETH_SERIAL_PORT_DISABLE 0 +#define ETH_SERIAL_PORT_ENABLE BIT0 +#define ETH_FORCE_LINK_PASS BIT1 +#define ETH_DO_NOT_FORCE_LINK_PASS 0 +#define ETH_ENABLE_AUTO_NEG_FOR_DUPLX 0 +#define ETH_DISABLE_AUTO_NEG_FOR_DUPLX BIT2 +#define ETH_ENABLE_AUTO_NEG_FOR_FLOW_CTRL 0 +#define ETH_DISABLE_AUTO_NEG_FOR_FLOW_CTRL BIT3 +#define ETH_ADV_NO_FLOW_CTRL 0 +#define ETH_ADV_SYMMETRIC_FLOW_CTRL BIT4 +#define ETH_FORCE_FC_MODE_NO_PAUSE_DIS_TX 0 +#define ETH_FORCE_FC_MODE_TX_PAUSE_DIS BIT5 +#define ETH_FORCE_BP_MODE_NO_JAM 0 +#define ETH_FORCE_BP_MODE_JAM_TX BIT7 +#define ETH_FORCE_BP_MODE_JAM_TX_ON_RX_ERR BIT8 +#define ETH_FORCE_LINK_FAIL 0 +#define ETH_DO_NOT_FORCE_LINK_FAIL BIT10 +#define ETH_DISABLE_AUTO_NEG_SPEED_GMII BIT13 +#define ETH_ENABLE_AUTO_NEG_SPEED_GMII 0 +#define ETH_DTE_ADV_0 0 +#define ETH_DTE_ADV_1 BIT14 +#define ETH_MIIPHY_MAC_MODE 0 +#define ETH_MIIPHY_PHY_MODE BIT15 +#define ETH_AUTO_NEG_NO_CHANGE 0 +#define ETH_RESTART_AUTO_NEG BIT16 +#define ETH_MAX_RX_PACKET_1518BYTE 0 +#define ETH_MAX_RX_PACKET_1522BYTE BIT17 +#define ETH_MAX_RX_PACKET_1552BYTE BIT18 +#define ETH_MAX_RX_PACKET_9022BYTE (BIT18 | BIT17) +#define ETH_MAX_RX_PACKET_9192BYTE BIT19 +#define ETH_MAX_RX_PACKET_9700BYTE (BIT19 | BIT17) +#define ETH_SET_EXT_LOOPBACK BIT20 +#define ETH_CLR_EXT_LOOPBACK 0 +#define ETH_SET_FULL_DUPLEX_MODE BIT21 +#define ETH_SET_HALF_DUPLEX_MODE 0 +#define ETH_ENABLE_FLOW_CTRL_TX_RX_IN_FULL_DUPLEX BIT22 +#define ETH_DISABLE_FLOW_CTRL_TX_RX_IN_FULL_DUPLEX 0 +#define ETH_SET_GMII_SPEED_TO_10_100 0 +#define ETH_SET_GMII_SPEED_TO_1000 BIT23 +#define ETH_SET_MII_SPEED_TO_10 0 +#define ETH_SET_MII_SPEED_TO_100 BIT24 + +/* SMI register fields (KW_ETH_SMI_REG) */ +#define ETH_PHY_SMI_TIMEOUT 10000 +#define ETH_PHY_SMI_DATA_OFFS 0 /* Data */ +#define ETH_PHY_SMI_DATA_MASK (0xffff << ETH_PHY_SMI_DATA_OFFS) +#define ETH_PHY_SMI_DEV_ADDR_OFFS 16 /* PHY device address */ +#define ETH_PHY_SMI_DEV_ADDR_MASK (0x1f << ETH_PHY_SMI_DEV_ADDR_OFFS) +#define KW_ETH_SMI_REG_ADDR_OFFS 21 /* PHY device register address */ +#define KW_ETH_SMI_REG_ADDR_MASK (0x1f << KW_ETH_SMI_REG_ADDR_OFFS) +#define ETH_PHY_SMI_OPCODE_OFFS 26 /* Write/Read opcode */ +#define ETH_PHY_SMI_OPCODE_MASK (3 << ETH_PHY_SMI_OPCODE_OFFS) +#define ETH_PHY_SMI_OPCODE_WRITE (0 << ETH_PHY_SMI_OPCODE_OFFS) +#define ETH_PHY_SMI_OPCODE_READ (1 << ETH_PHY_SMI_OPCODE_OFFS) +#define ETH_PHY_SMI_READ_VALID_MASK BIT27 /* Read Valid */ +#define ETH_PHY_SMI_BUSY_MASK BIT28 /* Busy */ + +/* SDMA command status fields macros */ +/* Tx & Rx descriptors status */ +#define ETH_ERROR_SUMMARY (BIT0) + +/* Tx & Rx descriptors command */ +#define ETH_BUFFER_OWNED_BY_DMA (BIT31) + +/* Tx descriptors status */ +#define ETH_LC_ERROR (0 ) +#define ETH_UR_ERROR (BIT1 ) +#define ETH_RL_ERROR (BIT2 ) +#define ETH_LLC_SNAP_FORMAT (BIT9 ) + +/* Rx descriptors status */ +#define ETH_CRC_ERROR (0 ) +#define ETH_OVERRUN_ERROR (BIT1 ) +#define ETH_MAX_FRAME_LENGTH_ERROR (BIT2 ) +#define ETH_RESOURCE_ERROR ((BIT2 | BIT1)) +#define ETH_VLAN_TAGGED (BIT19) +#define ETH_BPDU_FRAME (BIT20) +#define ETH_TCP_FRAME_OVER_IP_V_4 (0 ) +#define ETH_UDP_FRAME_OVER_IP_V_4 (BIT21) +#define ETH_OTHER_FRAME_TYPE (BIT22) +#define ETH_LAYER_2_IS_ETH_V_2 (BIT23) +#define ETH_FRAME_TYPE_IP_V_4 (BIT24) +#define ETH_FRAME_HEADER_OK (BIT25) +#define ETH_RX_LAST_DESC (BIT26) +#define ETH_RX_FIRST_DESC (BIT27) +#define ETH_UNKNOWN_DESTINATION_ADDR (BIT28) +#define ETH_RX_ENABLE_INTERRUPT (BIT29) +#define ETH_LAYER_4_CHECKSUM_OK (BIT30) + +/* Rx descriptors byte count */ +#define ETH_FRAME_FRAGMENTED (BIT2) + +/* Tx descriptors command */ +#define ETH_LAYER_4_CHECKSUM_FIRST_DESC (BIT10) +#define ETH_FRAME_SET_TO_VLAN (BIT15) +#define ETH_TCP_FRAME (0 ) +#define ETH_UDP_FRAME (BIT16) +#define ETH_GEN_TCP_UDP_CHECKSUM (BIT17) +#define ETH_GEN_IP_V_4_CHECKSUM (BIT18) +#define ETH_ZERO_PADDING (BIT19) +#define ETH_TX_LAST_DESC (BIT20) +#define ETH_TX_FIRST_DESC (BIT21) +#define ETH_GEN_CRC (BIT22) +#define ETH_TX_ENABLE_INTERRUPT (BIT23) +#define ETH_AUTO_MODE (BIT30) + +/* Address decode parameters */ +/* Ethernet Base Address Register bits */ +#define EBAR_TARGET_DRAM 0x00000000 +#define EBAR_TARGET_DEVICE 0x00000001 +#define EBAR_TARGET_CBS 0x00000002 +#define EBAR_TARGET_PCI0 0x00000003 +#define EBAR_TARGET_PCI1 0x00000004 +#define EBAR_TARGET_CUNIT 0x00000005 +#define EBAR_TARGET_AUNIT 0x00000006 +#define EBAR_TARGET_GUNIT 0x00000007 + +/* Window attributes */ +#define EBAR_ATTR_DRAM_CS0 0x00000E00 +#define EBAR_ATTR_DRAM_CS1 0x00000D00 +#define EBAR_ATTR_DRAM_CS2 0x00000B00 +#define EBAR_ATTR_DRAM_CS3 0x00000700 + +/* DRAM Target interface */ +#define EBAR_ATTR_DRAM_NO_CACHE_COHERENCY 0x00000000 +#define EBAR_ATTR_DRAM_CACHE_COHERENCY_WT 0x00001000 +#define EBAR_ATTR_DRAM_CACHE_COHERENCY_WB 0x00002000 + +/* Device Bus Target interface */ +#define EBAR_ATTR_DEVICE_DEVCS0 0x00001E00 +#define EBAR_ATTR_DEVICE_DEVCS1 0x00001D00 +#define EBAR_ATTR_DEVICE_DEVCS2 0x00001B00 +#define EBAR_ATTR_DEVICE_DEVCS3 0x00001700 +#define EBAR_ATTR_DEVICE_BOOTCS3 0x00000F00 + +/* PCI Target interface */ +#define EBAR_ATTR_PCI_BYTE_SWAP 0x00000000 +#define EBAR_ATTR_PCI_NO_SWAP 0x00000100 +#define EBAR_ATTR_PCI_BYTE_WORD_SWAP 0x00000200 +#define EBAR_ATTR_PCI_WORD_SWAP 0x00000300 +#define EBAR_ATTR_PCI_NO_SNOOP_NOT_ASSERT 0x00000000 +#define EBAR_ATTR_PCI_NO_SNOOP_ASSERT 0x00000400 +#define EBAR_ATTR_PCI_IO_SPACE 0x00000000 +#define EBAR_ATTR_PCI_MEMORY_SPACE 0x00000800 +#define EBAR_ATTR_PCI_REQ64_FORCE 0x00000000 +#define EBAR_ATTR_PCI_REQ64_SIZE 0x00001000 + +/* Window access control */ +#define EWIN_ACCESS_NOT_ALLOWED 0 +#define EWIN_ACCESS_READ_ONLY BIT0 +#define EWIN_ACCESS_FULL (BIT1 | BIT0) + +/* typedefs */ + +typedef enum _eth_func_ret_status { + ETH_OK, /* Returned as expected. */ + ETH_ERROR, /* Fundamental error. */ + ETH_RETRY, /* Could not process request. Try later. */ + ETH_END_OF_JOB, /* Ring has nothing to process. */ + ETH_QUEUE_FULL, /* Ring resource error. */ + ETH_QUEUE_LAST_RESOURCE /* Ring resources about to exhaust. */ +} ETH_FUNC_RET_STATUS; + +typedef enum _eth_queue { + ETH_Q0 = 0, + ETH_Q1 = 1, + ETH_Q2 = 2, + ETH_Q3 = 3, + ETH_Q4 = 4, + ETH_Q5 = 5, + ETH_Q6 = 6, + ETH_Q7 = 7 +} ETH_QUEUE; + +typedef enum _addr_win { + ETH_WIN0, + ETH_WIN1, + ETH_WIN2, + ETH_WIN3, + ETH_WIN4, + ETH_WIN5 +} ETH_ADDR_WIN; + +typedef enum _eth_target { + ETH_TARGET_DRAM, + ETH_TARGET_DEVICE, + ETH_TARGET_CBS, + ETH_TARGET_PCI0, + ETH_TARGET_PCI1 +} ETH_TARGET; + +typedef struct _eth_rx_desc { + u32 cmd_sts; /* Descriptor command status */ + u16 buf_size; /* Buffer size */ + u16 byte_cnt; /* Descriptor buffer byte count */ + u32 buf_ptr; /* Descriptor buffer pointer */ + u32 next_desc_ptr; /* Next descriptor pointer */ + u32 return_info; /* User resource return information */ +} ETH_RX_DESC; + +typedef struct _eth_tx_desc { + u32 cmd_sts; /* Descriptor command status */ + u16 l4i_chk; /* CPU provided TCP Checksum */ + u16 byte_cnt; /* Descriptor buffer byte count */ + u32 buf_ptr; /* Descriptor buffer pointer */ + u32 next_desc_ptr; /* Next descriptor pointer */ + u32 return_info; /* User resource return information */ +} ETH_TX_DESC; + +/* + * Unified struct for Rx and Tx operations. The user is not required to + * be familier with neither Tx nor Rx descriptors. + */ +typedef struct _pkt_info { + u32 cmd_sts; /* Descriptor command status */ + u16 byte_cnt; /* Descriptor buffer byte count */ + u16 l4i_chk; /* Tx CPU provided TCP Checksum */ + u32 buf_ptr; /* Descriptor buffer pointer */ + u32 return_info; /* User resource return information */ +} PKT_INFO; + +typedef struct _eth_win_param { + ETH_ADDR_WIN win; /* Window number. See ETH_ADDR_WIN enum */ + ETH_TARGET target; /* System targets. See ETH_TARGET enum */ + u16 attributes; /* BAR attributes. See above macros */ + u32 base_addr; /* Window base address in u32 form */ + u32 high_addr; /* Window high address in u32 form */ + u32 size; /* Size in MBytes. Must be % 64Kbyte. */ + bool enable; /* Enable/disable access to the window. */ + u16 access_ctrl; /*Access ctrl register. see above macros */ +} ETH_WIN_PARAM; + +/* Ethernet port specific infomation */ +typedef struct _eth_port_ctrl { + ETH_PORT port_num; /* User Ethernet port number */ + int port_phy_addr; /* User phy address of Ethrnet port */ + u8 port_mac_addr[6]; /* User defined port MAC address. */ + u32 port_config; /* User port configuration value */ + u32 port_config_extend; /* User port config extend value */ + u32 port_sdma_config; /* User port SDMA config value */ + u32 port_serial_control; /* User port serial control value */ + u32 port_tx_queue_command; /* Port active Tx queues summary */ + u32 port_rx_queue_command; /* Port active Rx queues summary */ + + /* User function to cast virtual address to CPU bus address */ + u32 (*port_virt_to_phys) (u32 addr); + /* User scratch pad for user specific data structures */ + void *port_private; + + bool rx_resource_err[MAX_RX_QUEUE_NUM]; /* Rx ring resource err flag */ + bool tx_resource_err[MAX_TX_QUEUE_NUM]; /* Tx ring resource err flag */ + + /* Tx/Rx rings managment indexes fields. For driver use */ + + /* Next available Rx resource */ + volatile ETH_RX_DESC *p_rx_curr_desc_q[MAX_RX_QUEUE_NUM]; + /* Returning Rx resource */ + volatile ETH_RX_DESC *p_rx_used_desc_q[MAX_RX_QUEUE_NUM]; + + /* Next available Tx resource */ + volatile ETH_TX_DESC *p_tx_curr_desc_q[MAX_TX_QUEUE_NUM]; + /* Returning Tx resource */ + volatile ETH_TX_DESC *p_tx_used_desc_q[MAX_TX_QUEUE_NUM]; + /* An extra Tx index to support txmt of multiple buffs per packet */ + volatile ETH_TX_DESC *p_tx_first_desc_q[MAX_TX_QUEUE_NUM]; + + /* Tx/Rx rings size and base variables fields. For driver use */ + + volatile ETH_RX_DESC *p_rx_desc_area_base[MAX_RX_QUEUE_NUM]; + u32 rx_desc_area_size[MAX_RX_QUEUE_NUM]; + char *p_rx_buffer_base[MAX_RX_QUEUE_NUM]; + + volatile ETH_TX_DESC *p_tx_desc_area_base[MAX_TX_QUEUE_NUM]; + u32 tx_desc_area_size[MAX_TX_QUEUE_NUM]; + char *p_tx_buffer_base[MAX_TX_QUEUE_NUM]; + +} ETH_PORT_INFO; + + +/* Low level driver function calls */ +static void eth_clear_mib_counters(ETH_PORT eth_port_num); + +/* Port operation control routines */ +static void eth_port_init(ETH_PORT_INFO * p_eth_port_ctrl); +static void eth_port_reset(ETH_PORT eth_port_num); +static void eth_port_start(ETH_PORT_INFO * p_eth_port_ctrl); + +/* Port MAC address routines */ +static void eth_port_uc_addr_set(ETH_PORT eth_port_num, + u8 *p_addr, ETH_QUEUE queue); + +/* Port data flow control routines */ +static ETH_FUNC_RET_STATUS eth_port_send(ETH_PORT_INFO * p_eth_port_ctrl, + ETH_QUEUE tx_queue, + PKT_INFO * p_pkt_info); +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); +static ETH_FUNC_RET_STATUS eth_port_receive(ETH_PORT_INFO * p_eth_port_ctrl, + ETH_QUEUE rx_queue, + PKT_INFO * p_pkt_info); +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); +#endif /* __EGIGA_H__ */ diff --git a/cpu/arm926ejs/kirkwood/egiga_regs.h b/cpu/arm926ejs/kirkwood/egiga_regs.h new file mode 100644 index 0000000..a24fa04 --- /dev/null +++ b/cpu/arm926ejs/kirkwood/egiga_regs.h @@ -0,0 +1,161 @@ +/* + * (C) Copyright 2009 + * Marvell Semiconductor <www.marvell.com> + * Prafulla Wadaskar prafulla@marvell.com + * + * (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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#ifndef __EGIGA_REGS_H__ +#define __EGIGA_REGS_H__ + +/* + *Ethernet Controller Registers + */ +#define KW_ETH_PHY_ADDR_REG(port) (0x72000 + (port<<14)) +#define KW_ETH_SMI_REG(port) (0x72004 + (port<<14)) +#define KW_ETH_UNIT_DEFAULT_ADDR_REG(port) (0x72008 + (port<<14)) +#define KW_ETH_UNIT_DEFAULTID_REG(port) (0x7200c + (port<<14)) +#define KW_ETH_UNIT_INTERRUPT_CAUSE_REG(port) (0x72080 + (port<<14)) +#define KW_ETH_UNIT_INTERRUPT_MASK_REG(port) (0x72084 + (port<<14)) +#define KW_ETH_UNIT_ERROR_ADDR_REG(port) (0x72094 + (port<<14)) +#define KW_ETH_INTADR_ERROR_ADDR_REG(port) (0x72098 + (port<<14)) +#define KW_ETH_UNIT_CTRL_REG(port) (0x720b0 + (port<<14)) +#define KW_ETH_BAR_0(port) (0x72200 + (port<<14)) +#define KW_ETH_BAR_1(port) (0x72208 + (port<<14)) +#define KW_ETH_BAR_2(port) (0x72210 + (port<<14)) +#define KW_ETH_BAR_3(port) (0x72218 + (port<<14)) +#define KW_ETH_BAR_4(port) (0x72220 + (port<<14)) +#define KW_ETH_BAR_5(port) (0x72228 + (port<<14)) +#define KW_ETH_SIZE_REG_0(port) (0x72204 + (port<<14)) +#define KW_ETH_SIZE_REG_1(port) (0x7220c + (port<<14)) +#define KW_ETH_SIZE_REG_2(port) (0x72214 + (port<<14)) +#define KW_ETH_SIZE_REG_3(port) (0x7221c + (port<<14)) +#define KW_ETH_SIZE_REG_4(port) (0x72224 + (port<<14)) +#define KW_ETH_SIZE_REG_5(port) (0x7222c + (port<<14)) +#define KW_ETH_HIGH_ADDR_REMAP_REG_0(port) (0x72280 + (port<<14)) +#define KW_ETH_HIGH_ADDR_REMAP_REG_1(port) (0x72284 + (port<<14)) +#define KW_ETH_HIGH_ADDR_REMAP_REG_2(port) (0x72288 + (port<<14)) +#define KW_ETH_HIGH_ADDR_REMAP_REG_3(port) (0x7228c + (port<<14)) +#define KW_ETH_BASE_ADDR_ENABLE_REG(port) (0x72290 + (port<<14)) +#define KW_ETH_ACCESS_PROTECTION_REG(port) (0x72294 + (port<<14)) +#define KW_ETH_PORT_CONFIG_REG(port) (0x72400 + (port<<14)) +#define KW_ETH_PORT_CONFIG_EXTEND_REG(port) (0x72404 + (port<<14)) +#define KW_ETH_MII_SERIAL_PARAMETRS_REG(port) (0x72408 + (port<<14)) +#define KW_ETH_VLAN_ETHERTYPE_REG(port) (0x72410 + (port<<14)) +#define KW_ETH_MAC_ADDR_LOW(port) (0x72414 + (port<<14)) +#define KW_ETH_MAC_ADDR_HIGH(port) (0x72418 + (port<<14)) +#define KW_ETH_SDMA_CONFIG_REG(port) (0x7241c + (port<<14)) +#define KW_ETH_DSCP_0(port) (0x72420 + (port<<14)) +#define KW_ETH_DSCP_1(port) (0x72424 + (port<<14)) +#define KW_ETH_DSCP_2(port) (0x72428 + (port<<14)) +#define KW_ETH_DSCP_3(port) (0x7242c + (port<<14)) +#define KW_ETH_DSCP_4(port) (0x72430 + (port<<14)) +#define KW_ETH_DSCP_5(port) (0x72434 + (port<<14)) +#define KW_ETH_DSCP_6(port) (0x72438 + (port<<14)) +#define KW_ETH_PORT_SERIAL_CONTROL0_REG(port) (0x7243c + (port<<14)) +#define KW_ETH_VLAN_PRIORITY_TAG_TO_PRIORITY(port) (0x72440 + (port<<14)) +#define KW_ETH_PORT_STATUS0_REG(port) (0x72444 + (port<<14)) +#define KW_ETH_PORT_SERIAL_CONTROL1_REG(port) (0x7244c + (port<<14)) +#define KW_ETH_PORT_STATUS1_REG(port) (0x72450 + (port<<14)) +#define KW_ETH_MARVELL_HEADER_REG(port) (0x72454 + (port<<14)) +#define KW_ETH_INTERRUPT_CAUSE_REG(port) (0x72460 + (port<<14)) +#define KW_ETH_INTERRUPT_CAUSE_EXTEND_REG(port) (0x72464 + (port<<14)) +#define KW_ETH_INTERRUPT_MASK_REG(port) (0x72468 + (port<<14)) +#define KW_ETH_INTERRUPT_EXTEND_MASK_REG(port) (0x7246c + (port<<14)) +#define KW_ETH_TX_FIFO_URGENT_THRESHOLD_REG(port) (0x72474 + (port<<14)) +#define KW_ETH_RX_MINIMAL_FRAME_SIZE_REG(port) (0x7247c + (port<<14)) +#define KW_ETH_RX_DISCARDED_FRAMES_COUNTER(port) (0x72484 + (port<<14)) +#define KW_ETH_PORT_OVERRUN_FRM_COUNTER_REG(port) (0x72488 + (port<<14)) +#define KW_ETH_PORT_INTERNAL_ADDR_ERROR_REG(port) (0x72494 + (port<<14)) +#define KW_ETH_RX_CURRENT_QUEUE_DESC_PTR_0(port) (0x7260c + (port<<14)) +#define KW_ETH_RX_CURRENT_QUEUE_DESC_PTR_1(port) (0x7261c + (port<<14)) +#define KW_ETH_RX_CURRENT_QUEUE_DESC_PTR_2(port) (0x7262c + (port<<14)) +#define KW_ETH_RX_CURRENT_QUEUE_DESC_PTR_3(port) (0x7263c + (port<<14)) +#define KW_ETH_RX_CURRENT_QUEUE_DESC_PTR_4(port) (0x7264c + (port<<14)) +#define KW_ETH_RX_CURRENT_QUEUE_DESC_PTR_5(port) (0x7265c + (port<<14)) +#define KW_ETH_RX_CURRENT_QUEUE_DESC_PTR_6(port) (0x7266c + (port<<14)) +#define KW_ETH_RX_CURRENT_QUEUE_DESC_PTR_7(port) (0x7267c + (port<<14)) +#define KW_ETH_RECEIVE_QUEUE_COMMAND_REG(port) (0x72680 + (port<<14)) +#define KW_ETH_CURRENT_SERVED_TX_DESC_PTR(port) (0x72684 + (port<<14)) +#define KW_ETH_TX_CURRENT_QUEUE_DESC_PTR_0(port) (0x726c0 + (port<<14)) +#define KW_ETH_TX_CURRENT_QUEUE_DESC_PTR_1(port) (0x726c4 + (port<<14)) +#define KW_ETH_TX_CURRENT_QUEUE_DESC_PTR_2(port) (0x726c8 + (port<<14)) +#define KW_ETH_TX_CURRENT_QUEUE_DESC_PTR_3(port) (0x726cc + (port<<14)) +#define KW_ETH_TX_CURRENT_QUEUE_DESC_PTR_4(port) (0x726d0 + (port<<14)) +#define KW_ETH_TX_CURRENT_QUEUE_DESC_PTR_5(port) (0x726d4 + (port<<14)) +#define KW_ETH_TX_CURRENT_QUEUE_DESC_PTR_6(port) (0x726d8 + (port<<14)) +#define KW_ETH_TX_CURRENT_QUEUE_DESC_PTR_7(port) (0x726dc + (port<<14)) +#define KW_ETH_DA_FILTER_SPECIAL_MULTICAST_TABLE_BASE(port) (0x73400 + (port<<14)) +#define KW_ETH_DA_FILTER_OTHER_MULTICAST_TABLE_BASE(port) (0x73500 + (port<<14)) +#define KW_ETH_DA_FILTER_UNICAST_TABLE_BASE(port) (0x73600 + (port<<14)) +#define KW_ETH_TRANSMIT_QUEUE_COMMAND_REG(port) (0x72448 + (port<<14)) +#define KW_ETH_TX_QUEUE_FIXED_PRIORITY(port) (0x724dc + (port<<14)) +#define KW_ETH_PORT_TX_TOKEN_BUCKET_RATE_CONFIG(port) (0x724e0 + (port<<14)) +#define KW_ETH_MAXIMUM_TRANSMIT_UNIT(port) (0x724e8 + (port<<14)) +#define KW_ETH_PORT_MAXIMUM_TOKEN_BUCKET_SIZE(port) (0x724ec + (port<<14)) +#define KW_ETH_TX_QUEUE_0_TOKEN_BUCKET_COUNT(port) (0x72700 + (port<<14)) +#define KW_ETH_TX_QUEUE_1_TOKEN_BUCKET_COUNT(port) (0x72710 + (port<<14)) +#define KW_ETH_TX_QUEUE_2_TOKEN_BUCKET_COUNT(port) (0x72720 + (port<<14)) +#define KW_ETH_TX_QUEUE_3_TOKEN_BUCKET_COUNT(port) (0x72730 + (port<<14)) +#define KW_ETH_TX_QUEUE_4_TOKEN_BUCKET_COUNT(port) (0x72740 + (port<<14)) +#define KW_ETH_TX_QUEUE_5_TOKEN_BUCKET_COUNT(port) (0x72750 + (port<<14)) +#define KW_ETH_TX_QUEUE_6_TOKEN_BUCKET_COUNT(port) (0x72760 + (port<<14)) +#define KW_ETH_TX_QUEUE_7_TOKEN_BUCKET_COUNT(port) (0x72770 + (port<<14)) +#define KW_ETH_TX_QUEUE_0_TOKEN_BUCKET_CONFIG(port) (0x72704 + (port<<14)) +#define KW_ETH_TX_QUEUE_1_TOKEN_BUCKET_CONFIG(port) (0x72714 + (port<<14)) +#define KW_ETH_TX_QUEUE_2_TOKEN_BUCKET_CONFIG(port) (0x72724 + (port<<14)) +#define KW_ETH_TX_QUEUE_3_TOKEN_BUCKET_CONFIG(port) (0x72734 + (port<<14)) +#define KW_ETH_TX_QUEUE_4_TOKEN_BUCKET_CONFIG(port) (0x72744 + (port<<14)) +#define KW_ETH_TX_QUEUE_5_TOKEN_BUCKET_CONFIG(port) (0x72754 + (port<<14)) +#define KW_ETH_TX_QUEUE_6_TOKEN_BUCKET_CONFIG(port) (0x72764 + (port<<14)) +#define KW_ETH_TX_QUEUE_7_TOKEN_BUCKET_CONFIG(port) (0x72774 + (port<<14)) +#define KW_ETH_TX_QUEUE_0_ARBITER_CONFIG(port) (0x72708 + (port<<14)) +#define KW_ETH_TX_QUEUE_1_ARBITER_CONFIG(port) (0x72718 + (port<<14)) +#define KW_ETH_TX_QUEUE_2_ARBITER_CONFIG(port) (0x72728 + (port<<14)) +#define KW_ETH_TX_QUEUE_3_ARBITER_CONFIG(port) (0x72738 + (port<<14)) +#define KW_ETH_TX_QUEUE_4_ARBITER_CONFIG(port) (0x72748 + (port<<14)) +#define KW_ETH_TX_QUEUE_5_ARBITER_CONFIG(port) (0x72758 + (port<<14)) +#define KW_ETH_TX_QUEUE_6_ARBITER_CONFIG(port) (0x72768 + (port<<14)) +#define KW_ETH_TX_QUEUE_7_ARBITER_CONFIG(port) (0x72778 + (port<<14)) +#define KW_ETH_PORT_TX_TOKEN_BUCKET_COUNT(port) (0x72780 + (port<<14)) + +#define KW_ETH_MIB_COUNTERS_BASE(port) (0x73000 + (port<<14)) + +/* Type defs */ +#ifndef __ASSEMBLY__ +typedef enum _eth_port { + ETH_0 = 0, + ETH_1 = 1, +} ETH_PORT; /* SOC has two Gbe Ports */ + +/* smi register read/write function can be used by switch/phy */ +int eth_smi_reg_read(unsigned int eth_port_num, unsigned int phy_adr, + unsigned int reg_ofs, unsigned short *data); +int eth_smi_reg_write(unsigned int eth_port_num, unsigned int phy_adr, + unsigned int reg_ofs, unsigned short data); +#endif + +#endif /* __EGIGA_REGS_H__ */ diff --git a/cpu/arm926ejs/kirkwood/kwcore.h b/cpu/arm926ejs/kirkwood/kwcore.h index 0ddc6a8..50043d8 100644 --- a/cpu/arm926ejs/kirkwood/kwcore.h +++ b/cpu/arm926ejs/kirkwood/kwcore.h @@ -87,6 +87,8 @@ #define KW_REG_TMR_RELOAD (0x20310) #define KW_REG_TMR_VAL (0x20314)
+#include "egiga_regs.h" + /* * Macros * CPU architecture dependent I/O read/write diff --git a/net/eth.c b/net/eth.c index 4bbf84b..77fa8a5 100644 --- a/net/eth.c +++ b/net/eth.c @@ -73,6 +73,7 @@ int board_eth_init(bd_t *bis) __attribute((weak, alias("__def_eth_init")));
extern int mv6436x_eth_initialize(bd_t *); extern int mv6446x_eth_initialize(bd_t *); +extern int kirkwood_egiga_initialize(bd_t*);
#ifdef CONFIG_API extern void (*push_packet)(volatile void *, int); @@ -205,6 +206,9 @@ int eth_initialize(bd_t *bis) #if defined(CONFIG_DB64460) || defined(CONFIG_P3Mx) mv6446x_eth_initialize(bis); #endif +#ifdef CONFIG_KIRKWOOD_EGIGA + kirkwood_egiga_initialize(bis); +#endif if (!eth_devices) { puts ("No ethernet found.\n"); show_boot_progress (-64);