u-boot-brain/cpu/ppc4xx/4xx_enet.c
Stefan Roese 353f2688b4 ppc4xx: Add initial AMCC Haleakala PPC405EXr eval board support
The Haleakala is nearly identical with the Kilauea eval board. The only
difference is that the 405EXr only supports one EMAC and one PCIe
interface. This patch adds support for the Haleakala board by using
the identical image for Kilauea and Haleakala. The distinction is done
by comparing the PVR.

Signed-off-by: Stefan Roese <sr@denx.de>
2007-10-31 21:20:51 +01:00

1818 lines
51 KiB
C

/*-----------------------------------------------------------------------------+
*
* This source code has been made available to you by IBM on an AS-IS
* basis. Anyone receiving this source is licensed under IBM
* copyrights to use it in any way he or she deems fit, including
* copying it, modifying it, compiling it, and redistributing it either
* with or without modifications. No license under IBM patents or
* patent applications is to be implied by the copyright license.
*
* Any user of this software should understand that IBM cannot provide
* technical support for this software and will not be responsible for
* any consequences resulting from the use of this software.
*
* Any person who transfers this source code or any derivative work
* must include the IBM copyright notice, this paragraph, and the
* preceding two paragraphs in the transferred software.
*
* COPYRIGHT I B M CORPORATION 1995
* LICENSED MATERIAL - PROGRAM PROPERTY OF I B M
*-----------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------+
*
* File Name: enetemac.c
*
* Function: Device driver for the ethernet EMAC3 macro on the 405GP.
*
* Author: Mark Wisner
*
* Change Activity-
*
* Date Description of Change BY
* --------- --------------------- ---
* 05-May-99 Created MKW
* 27-Jun-99 Clean up JWB
* 16-Jul-99 Added MAL error recovery and better IP packet handling MKW
* 29-Jul-99 Added Full duplex support MKW
* 06-Aug-99 Changed names for Mal CR reg MKW
* 23-Aug-99 Turned off SYE when running at 10Mbs MKW
* 24-Aug-99 Marked descriptor empty after call_xlc MKW
* 07-Sep-99 Set MAL RX buffer size reg to ENET_MAX_MTU_ALIGNED / 16 MCG
* to avoid chaining maximum sized packets. Push starting
* RX descriptor address up to the next cache line boundary.
* 16-Jan-00 Added support for booting with IP of 0x0 MKW
* 15-Mar-00 Updated enetInit() to enable broadcast addresses in the
* EMAC_RXM register. JWB
* 12-Mar-01 anne-sophie.harnois@nextream.fr
* - Variables are compatible with those already defined in
* include/net.h
* - Receive buffer descriptor ring is used to send buffers
* to the user
* - Info print about send/received/handled packet number if
* INFO_405_ENET is set
* 17-Apr-01 stefan.roese@esd-electronics.com
* - MAL reset in "eth_halt" included
* - Enet speed and duplex output now in one line
* 08-May-01 stefan.roese@esd-electronics.com
* - MAL error handling added (eth_init called again)
* 13-Nov-01 stefan.roese@esd-electronics.com
* - Set IST bit in EMAC_M1 reg upon 100MBit or full duplex
* 04-Jan-02 stefan.roese@esd-electronics.com
* - Wait for PHY auto negotiation to complete added
* 06-Feb-02 stefan.roese@esd-electronics.com
* - Bug fixed in waiting for auto negotiation to complete
* 26-Feb-02 stefan.roese@esd-electronics.com
* - rx and tx buffer descriptors now allocated (no fixed address
* used anymore)
* 17-Jun-02 stefan.roese@esd-electronics.com
* - MAL error debug printf 'M' removed (rx de interrupt may
* occur upon many incoming packets with only 4 rx buffers).
*-----------------------------------------------------------------------------*
* 17-Nov-03 travis.sawyer@sandburst.com
* - ported from 405gp_enet.c to utilized upto 4 EMAC ports
* in the 440GX. This port should work with the 440GP
* (2 EMACs) also
* 15-Aug-05 sr@denx.de
* - merged 405gp_enet.c and 440gx_enet.c to generic 4xx_enet.c
now handling all 4xx cpu's.
*-----------------------------------------------------------------------------*/
#include <config.h>
#include <common.h>
#include <net.h>
#include <asm/processor.h>
#include <commproc.h>
#include <ppc4xx.h>
#include <ppc4xx_enet.h>
#include <405_mal.h>
#include <miiphy.h>
#include <malloc.h>
#include "vecnum.h"
/*
* Only compile for platform with AMCC EMAC ethernet controller and
* network support enabled.
* Remark: CONFIG_405 describes Xilinx PPC405 FPGA without EMAC controller!
*/
#if defined(CONFIG_CMD_NET) && !defined(CONFIG_405) && !defined(CONFIG_IOP480)
#if !(defined(CONFIG_MII) || defined(CONFIG_CMD_MII))
#error "CONFIG_MII has to be defined!"
#endif
#if defined(CONFIG_NETCONSOLE) && !defined(CONFIG_NET_MULTI)
#error "CONFIG_NET_MULTI has to be defined for NetConsole"
#endif
#define EMAC_RESET_TIMEOUT 1000 /* 1000 ms reset timeout */
#define PHY_AUTONEGOTIATE_TIMEOUT 4000 /* 4000 ms autonegotiate timeout */
/* Ethernet Transmit and Receive Buffers */
/* AS.HARNOIS
* In the same way ENET_MAX_MTU and ENET_MAX_MTU_ALIGNED are set from
* PKTSIZE and PKTSIZE_ALIGN (include/net.h)
*/
#define ENET_MAX_MTU PKTSIZE
#define ENET_MAX_MTU_ALIGNED PKTSIZE_ALIGN
/*-----------------------------------------------------------------------------+
* Defines for MAL/EMAC interrupt conditions as reported in the UIC (Universal
* Interrupt Controller).
*-----------------------------------------------------------------------------*/
#define MAL_UIC_ERR ( UIC_MAL_SERR | UIC_MAL_TXDE | UIC_MAL_RXDE)
#define MAL_UIC_DEF (UIC_MAL_RXEOB | MAL_UIC_ERR)
#define EMAC_UIC_DEF UIC_ENET
#define EMAC_UIC_DEF1 UIC_ENET1
#define SEL_UIC_DEF(p) (p ? UIC_ENET1 : UIC_ENET )
#undef INFO_4XX_ENET
#define BI_PHYMODE_NONE 0
#define BI_PHYMODE_ZMII 1
#define BI_PHYMODE_RGMII 2
#define BI_PHYMODE_GMII 3
#define BI_PHYMODE_RTBI 4
#define BI_PHYMODE_TBI 5
#if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \
defined(CONFIG_405EX)
#define BI_PHYMODE_SMII 6
#define BI_PHYMODE_MII 7
#endif
#if defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \
defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \
defined(CONFIG_405EX)
#define SDR0_MFR_ETH_CLK_SEL_V(n) ((0x01<<27) / (n+1))
#endif
/*-----------------------------------------------------------------------------+
* Global variables. TX and RX descriptors and buffers.
*-----------------------------------------------------------------------------*/
/* IER globals */
static uint32_t mal_ier;
#if !defined(CONFIG_NET_MULTI)
struct eth_device *emac0_dev = NULL;
#endif
/*
* Get count of EMAC devices (doesn't have to be the max. possible number
* supported by the cpu)
*
* CONFIG_BOARD_EMAC_COUNT added so now a "dynamic" way to configure the
* EMAC count is possible. As it is needed for the Kilauea/Haleakala
* 405EX/405EXr eval board, using the same binary.
*/
#if defined(CONFIG_BOARD_EMAC_COUNT)
#define LAST_EMAC_NUM board_emac_count()
#else /* CONFIG_BOARD_EMAC_COUNT */
#if defined(CONFIG_HAS_ETH3)
#define LAST_EMAC_NUM 4
#elif defined(CONFIG_HAS_ETH2)
#define LAST_EMAC_NUM 3
#elif defined(CONFIG_HAS_ETH1)
#define LAST_EMAC_NUM 2
#else
#define LAST_EMAC_NUM 1
#endif
#endif /* CONFIG_BOARD_EMAC_COUNT */
/* normal boards start with EMAC0 */
#if !defined(CONFIG_EMAC_NR_START)
#define CONFIG_EMAC_NR_START 0
#endif
#if defined(CONFIG_405EX) || defined(CONFIG_440EPX)
#define ETH_IRQ_NUM(dev) (VECNUM_ETH0 + ((dev)))
#else
#define ETH_IRQ_NUM(dev) (VECNUM_ETH0 + ((dev) * 2))
#endif
/*-----------------------------------------------------------------------------+
* Prototypes and externals.
*-----------------------------------------------------------------------------*/
static void enet_rcv (struct eth_device *dev, unsigned long malisr);
int enetInt (struct eth_device *dev);
static void mal_err (struct eth_device *dev, unsigned long isr,
unsigned long uic, unsigned long maldef,
unsigned long mal_errr);
static void emac_err (struct eth_device *dev, unsigned long isr);
extern int phy_setup_aneg (char *devname, unsigned char addr);
extern int emac4xx_miiphy_read (char *devname, unsigned char addr,
unsigned char reg, unsigned short *value);
extern int emac4xx_miiphy_write (char *devname, unsigned char addr,
unsigned char reg, unsigned short value);
int board_emac_count(void);
/*-----------------------------------------------------------------------------+
| ppc_4xx_eth_halt
| Disable MAL channel, and EMACn
+-----------------------------------------------------------------------------*/
static void ppc_4xx_eth_halt (struct eth_device *dev)
{
EMAC_4XX_HW_PST hw_p = dev->priv;
uint32_t failsafe = 10000;
#if defined(CONFIG_440SPE) || \
defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \
defined(CONFIG_405EX)
unsigned long mfr;
#endif
out32 (EMAC_IER + hw_p->hw_addr, 0x00000000); /* disable emac interrupts */
/* 1st reset MAL channel */
/* Note: writing a 0 to a channel has no effect */
#if defined(CONFIG_405EP) || defined(CONFIG_440EP) || defined(CONFIG_440GR)
mtdcr (maltxcarr, (MAL_CR_MMSR >> (hw_p->devnum * 2)));
#else
mtdcr (maltxcarr, (MAL_CR_MMSR >> hw_p->devnum));
#endif
mtdcr (malrxcarr, (MAL_CR_MMSR >> hw_p->devnum));
/* wait for reset */
while (mfdcr (malrxcasr) & (MAL_CR_MMSR >> hw_p->devnum)) {
udelay (1000); /* Delay 1 MS so as not to hammer the register */
failsafe--;
if (failsafe == 0)
break;
}
/* EMAC RESET */
#if defined(CONFIG_440SPE) || \
defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \
defined(CONFIG_405EX)
/* provide clocks for EMAC internal loopback */
mfsdr (sdr_mfr, mfr);
mfr |= SDR0_MFR_ETH_CLK_SEL_V(hw_p->devnum);
mtsdr(sdr_mfr, mfr);
#endif
out32 (EMAC_M0 + hw_p->hw_addr, EMAC_M0_SRST);
#if defined(CONFIG_440SPE) || \
defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \
defined(CONFIG_405EX)
/* remove clocks for EMAC internal loopback */
mfsdr (sdr_mfr, mfr);
mfr &= ~SDR0_MFR_ETH_CLK_SEL_V(hw_p->devnum);
mtsdr(sdr_mfr, mfr);
#endif
#ifndef CONFIG_NETCONSOLE
hw_p->print_speed = 1; /* print speed message again next time */
#endif
return;
}
#if defined (CONFIG_440GX)
int ppc_4xx_eth_setup_bridge(int devnum, bd_t * bis)
{
unsigned long pfc1;
unsigned long zmiifer;
unsigned long rmiifer;
mfsdr(sdr_pfc1, pfc1);
pfc1 = SDR0_PFC1_EPS_DECODE(pfc1);
zmiifer = 0;
rmiifer = 0;
switch (pfc1) {
case 1:
zmiifer |= ZMII_FER_RMII << ZMII_FER_V(0);
zmiifer |= ZMII_FER_RMII << ZMII_FER_V(1);
zmiifer |= ZMII_FER_RMII << ZMII_FER_V(2);
zmiifer |= ZMII_FER_RMII << ZMII_FER_V(3);
bis->bi_phymode[0] = BI_PHYMODE_ZMII;
bis->bi_phymode[1] = BI_PHYMODE_ZMII;
bis->bi_phymode[2] = BI_PHYMODE_ZMII;
bis->bi_phymode[3] = BI_PHYMODE_ZMII;
break;
case 2:
zmiifer |= ZMII_FER_SMII << ZMII_FER_V(0);
zmiifer |= ZMII_FER_SMII << ZMII_FER_V(1);
zmiifer |= ZMII_FER_SMII << ZMII_FER_V(2);
zmiifer |= ZMII_FER_SMII << ZMII_FER_V(3);
bis->bi_phymode[0] = BI_PHYMODE_ZMII;
bis->bi_phymode[1] = BI_PHYMODE_ZMII;
bis->bi_phymode[2] = BI_PHYMODE_ZMII;
bis->bi_phymode[3] = BI_PHYMODE_ZMII;
break;
case 3:
zmiifer |= ZMII_FER_RMII << ZMII_FER_V(0);
rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(2);
bis->bi_phymode[0] = BI_PHYMODE_ZMII;
bis->bi_phymode[1] = BI_PHYMODE_NONE;
bis->bi_phymode[2] = BI_PHYMODE_RGMII;
bis->bi_phymode[3] = BI_PHYMODE_NONE;
break;
case 4:
zmiifer |= ZMII_FER_SMII << ZMII_FER_V(0);
zmiifer |= ZMII_FER_SMII << ZMII_FER_V(1);
rmiifer |= RGMII_FER_RGMII << RGMII_FER_V (2);
rmiifer |= RGMII_FER_RGMII << RGMII_FER_V (3);
bis->bi_phymode[0] = BI_PHYMODE_ZMII;
bis->bi_phymode[1] = BI_PHYMODE_ZMII;
bis->bi_phymode[2] = BI_PHYMODE_RGMII;
bis->bi_phymode[3] = BI_PHYMODE_RGMII;
break;
case 5:
zmiifer |= ZMII_FER_SMII << ZMII_FER_V (0);
zmiifer |= ZMII_FER_SMII << ZMII_FER_V (1);
zmiifer |= ZMII_FER_SMII << ZMII_FER_V (2);
rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(3);
bis->bi_phymode[0] = BI_PHYMODE_ZMII;
bis->bi_phymode[1] = BI_PHYMODE_ZMII;
bis->bi_phymode[2] = BI_PHYMODE_ZMII;
bis->bi_phymode[3] = BI_PHYMODE_RGMII;
break;
case 6:
zmiifer |= ZMII_FER_SMII << ZMII_FER_V (0);
zmiifer |= ZMII_FER_SMII << ZMII_FER_V (1);
rmiifer |= RGMII_FER_RGMII << RGMII_FER_V(2);
bis->bi_phymode[0] = BI_PHYMODE_ZMII;
bis->bi_phymode[1] = BI_PHYMODE_ZMII;
bis->bi_phymode[2] = BI_PHYMODE_RGMII;
break;
case 0:
default:
zmiifer = ZMII_FER_MII << ZMII_FER_V(devnum);
rmiifer = 0x0;
bis->bi_phymode[0] = BI_PHYMODE_ZMII;
bis->bi_phymode[1] = BI_PHYMODE_ZMII;
bis->bi_phymode[2] = BI_PHYMODE_ZMII;
bis->bi_phymode[3] = BI_PHYMODE_ZMII;
break;
}
/* Ensure we setup mdio for this devnum and ONLY this devnum */
zmiifer |= (ZMII_FER_MDI) << ZMII_FER_V(devnum);
out32 (ZMII_FER, zmiifer);
out32 (RGMII_FER, rmiifer);
return ((int)pfc1);
}
#endif /* CONFIG_440_GX */
#if defined(CONFIG_440EPX) || defined(CONFIG_440GRX)
int ppc_4xx_eth_setup_bridge(int devnum, bd_t * bis)
{
unsigned long zmiifer=0x0;
unsigned long pfc1;
mfsdr(sdr_pfc1, pfc1);
pfc1 &= SDR0_PFC1_SELECT_MASK;
switch (pfc1) {
case SDR0_PFC1_SELECT_CONFIG_2:
/* 1 x GMII port */
out32 (ZMII_FER, 0x00);
out32 (RGMII_FER, 0x00000037);
bis->bi_phymode[0] = BI_PHYMODE_GMII;
bis->bi_phymode[1] = BI_PHYMODE_NONE;
break;
case SDR0_PFC1_SELECT_CONFIG_4:
/* 2 x RGMII ports */
out32 (ZMII_FER, 0x00);
out32 (RGMII_FER, 0x00000055);
bis->bi_phymode[0] = BI_PHYMODE_RGMII;
bis->bi_phymode[1] = BI_PHYMODE_RGMII;
break;
case SDR0_PFC1_SELECT_CONFIG_6:
/* 2 x SMII ports */
out32 (ZMII_FER,
((ZMII_FER_SMII) << ZMII_FER_V(0)) |
((ZMII_FER_SMII) << ZMII_FER_V(1)));
out32 (RGMII_FER, 0x00000000);
bis->bi_phymode[0] = BI_PHYMODE_SMII;
bis->bi_phymode[1] = BI_PHYMODE_SMII;
break;
case SDR0_PFC1_SELECT_CONFIG_1_2:
/* only 1 x MII supported */
out32 (ZMII_FER, (ZMII_FER_MII) << ZMII_FER_V(0));
out32 (RGMII_FER, 0x00000000);
bis->bi_phymode[0] = BI_PHYMODE_MII;
bis->bi_phymode[1] = BI_PHYMODE_NONE;
break;
default:
break;
}
/* Ensure we setup mdio for this devnum and ONLY this devnum */
zmiifer = in32 (ZMII_FER);
zmiifer |= (ZMII_FER_MDI) << ZMII_FER_V(devnum);
out32 (ZMII_FER, zmiifer);
return ((int)0x0);
}
#endif /* CONFIG_440EPX */
#if defined(CONFIG_405EX)
int ppc_4xx_eth_setup_bridge(int devnum, bd_t * bis)
{
u32 gmiifer = 0;
/*
* Right now only 2*RGMII is supported. Please extend when needed.
* sr - 2007-09-19
*/
switch (1) {
case 1:
/* 2 x RGMII ports */
out32 (RGMII_FER, 0x00000055);
bis->bi_phymode[0] = BI_PHYMODE_RGMII;
bis->bi_phymode[1] = BI_PHYMODE_RGMII;
break;
case 2:
/* 2 x SMII ports */
break;
default:
break;
}
/* Ensure we setup mdio for this devnum and ONLY this devnum */
gmiifer = in32(RGMII_FER);
gmiifer |= (1 << (19-devnum));
out32 (RGMII_FER, gmiifer);
return ((int)0x0);
}
#endif /* CONFIG_405EX */
static int ppc_4xx_eth_init (struct eth_device *dev, bd_t * bis)
{
int i, j;
unsigned long reg = 0;
unsigned long msr;
unsigned long speed;
unsigned long duplex;
unsigned long failsafe;
unsigned mode_reg;
unsigned short devnum;
unsigned short reg_short;
#if defined(CONFIG_440GX) || \
defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \
defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \
defined(CONFIG_405EX)
sys_info_t sysinfo;
#if defined(CONFIG_440GX) || defined(CONFIG_440SPE) || \
defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \
defined(CONFIG_405EX)
int ethgroup = -1;
#endif
#endif
#if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \
defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \
defined(CONFIG_405EX)
unsigned long mfr;
#endif
EMAC_4XX_HW_PST hw_p = dev->priv;
/* before doing anything, figure out if we have a MAC address */
/* if not, bail */
if (memcmp (dev->enetaddr, "\0\0\0\0\0\0", 6) == 0) {
printf("ERROR: ethaddr not set!\n");
return -1;
}
#if defined(CONFIG_440GX) || \
defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \
defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \
defined(CONFIG_405EX)
/* Need to get the OPB frequency so we can access the PHY */
get_sys_info (&sysinfo);
#endif
msr = mfmsr ();
mtmsr (msr & ~(MSR_EE)); /* disable interrupts */
devnum = hw_p->devnum;
#ifdef INFO_4XX_ENET
/* AS.HARNOIS
* We should have :
* hw_p->stats.pkts_handled <= hw_p->stats.pkts_rx <= hw_p->stats.pkts_handled+PKTBUFSRX
* In the most cases hw_p->stats.pkts_handled = hw_p->stats.pkts_rx, but it
* is possible that new packets (without relationship with
* current transfer) have got the time to arrived before
* netloop calls eth_halt
*/
printf ("About preceeding transfer (eth%d):\n"
"- Sent packet number %d\n"
"- Received packet number %d\n"
"- Handled packet number %d\n",
hw_p->devnum,
hw_p->stats.pkts_tx,
hw_p->stats.pkts_rx, hw_p->stats.pkts_handled);
hw_p->stats.pkts_tx = 0;
hw_p->stats.pkts_rx = 0;
hw_p->stats.pkts_handled = 0;
hw_p->print_speed = 1; /* print speed message again next time */
#endif
hw_p->tx_err_index = 0; /* Transmit Error Index for tx_err_log */
hw_p->rx_err_index = 0; /* Receive Error Index for rx_err_log */
hw_p->rx_slot = 0; /* MAL Receive Slot */
hw_p->rx_i_index = 0; /* Receive Interrupt Queue Index */
hw_p->rx_u_index = 0; /* Receive User Queue Index */
hw_p->tx_slot = 0; /* MAL Transmit Slot */
hw_p->tx_i_index = 0; /* Transmit Interrupt Queue Index */
hw_p->tx_u_index = 0; /* Transmit User Queue Index */
#if defined(CONFIG_440) && !defined(CONFIG_440SP) && !defined(CONFIG_440SPE)
/* set RMII mode */
/* NOTE: 440GX spec states that mode is mutually exclusive */
/* NOTE: Therefore, disable all other EMACS, since we handle */
/* NOTE: only one emac at a time */
reg = 0;
out32 (ZMII_FER, 0);
udelay (100);
#if defined(CONFIG_440EP) || defined(CONFIG_440GR)
out32 (ZMII_FER, (ZMII_FER_RMII | ZMII_FER_MDI) << ZMII_FER_V (devnum));
#elif defined(CONFIG_440GX) || defined(CONFIG_440EPX) || defined(CONFIG_440GRX)
ethgroup = ppc_4xx_eth_setup_bridge(devnum, bis);
#elif defined(CONFIG_440GP)
/* set RMII mode */
out32 (ZMII_FER, ZMII_RMII | ZMII_MDI0);
#else
if ((devnum == 0) || (devnum == 1)) {
out32 (ZMII_FER, (ZMII_FER_SMII | ZMII_FER_MDI) << ZMII_FER_V (devnum));
} else { /* ((devnum == 2) || (devnum == 3)) */
out32 (ZMII_FER, ZMII_FER_MDI << ZMII_FER_V (devnum));
out32 (RGMII_FER, ((RGMII_FER_RGMII << RGMII_FER_V (2)) |
(RGMII_FER_RGMII << RGMII_FER_V (3))));
}
#endif
out32 (ZMII_SSR, ZMII_SSR_SP << ZMII_SSR_V(devnum));
#endif /* defined(CONFIG_440) && !defined(CONFIG_440SP) */
#if defined(CONFIG_405EX)
ethgroup = ppc_4xx_eth_setup_bridge(devnum, bis);
#endif
__asm__ volatile ("eieio");
/* reset emac so we have access to the phy */
#if defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \
defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \
defined(CONFIG_405EX)
/* provide clocks for EMAC internal loopback */
mfsdr (sdr_mfr, mfr);
mfr |= SDR0_MFR_ETH_CLK_SEL_V(devnum);
mtsdr(sdr_mfr, mfr);
#endif
out32 (EMAC_M0 + hw_p->hw_addr, EMAC_M0_SRST);
__asm__ volatile ("eieio");
failsafe = 1000;
while ((in32 (EMAC_M0 + hw_p->hw_addr) & (EMAC_M0_SRST)) && failsafe) {
udelay (1000);
failsafe--;
}
if (failsafe <= 0)
printf("\nProblem resetting EMAC!\n");
#if defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \
defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \
defined(CONFIG_405EX)
/* remove clocks for EMAC internal loopback */
mfsdr (sdr_mfr, mfr);
mfr &= ~SDR0_MFR_ETH_CLK_SEL_V(devnum);
mtsdr(sdr_mfr, mfr);
#endif
#if defined(CONFIG_440GX) || \
defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \
defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \
defined(CONFIG_405EX)
/* Whack the M1 register */
mode_reg = 0x0;
mode_reg &= ~0x00000038;
if (sysinfo.freqOPB <= 50000000);
else if (sysinfo.freqOPB <= 66666667)
mode_reg |= EMAC_M1_OBCI_66;
else if (sysinfo.freqOPB <= 83333333)
mode_reg |= EMAC_M1_OBCI_83;
else if (sysinfo.freqOPB <= 100000000)
mode_reg |= EMAC_M1_OBCI_100;
else
mode_reg |= EMAC_M1_OBCI_GT100;
out32 (EMAC_M1 + hw_p->hw_addr, mode_reg);
#endif /* defined(CONFIG_440GX) || defined(CONFIG_440SP) */
/* wait for PHY to complete auto negotiation */
reg_short = 0;
#ifndef CONFIG_CS8952_PHY
switch (devnum) {
case 0:
reg = CONFIG_PHY_ADDR;
break;
#if defined (CONFIG_PHY1_ADDR)
case 1:
reg = CONFIG_PHY1_ADDR;
break;
#endif
#if defined (CONFIG_440GX)
case 2:
reg = CONFIG_PHY2_ADDR;
break;
case 3:
reg = CONFIG_PHY3_ADDR;
break;
#endif
default:
reg = CONFIG_PHY_ADDR;
break;
}
bis->bi_phynum[devnum] = reg;
#if defined(CONFIG_PHY_RESET)
/*
* Reset the phy, only if its the first time through
* otherwise, just check the speeds & feeds
*/
if (hw_p->first_init == 0) {
#if defined(CONFIG_M88E1111_PHY)
miiphy_write (dev->name, reg, 0x14, 0x0ce3);
miiphy_write (dev->name, reg, 0x18, 0x4101);
miiphy_write (dev->name, reg, 0x09, 0x0e00);
miiphy_write (dev->name, reg, 0x04, 0x01e1);
#endif
miiphy_reset (dev->name, reg);
#if defined(CONFIG_440GX) || \
defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \
defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \
defined(CONFIG_405EX)
#if defined(CONFIG_CIS8201_PHY)
/*
* Cicada 8201 PHY needs to have an extended register whacked
* for RGMII mode.
*/
if (((devnum == 2) || (devnum == 3)) && (4 == ethgroup)) {
#if defined(CONFIG_CIS8201_SHORT_ETCH)
miiphy_write (dev->name, reg, 23, 0x1300);
#else
miiphy_write (dev->name, reg, 23, 0x1000);
#endif
/*
* Vitesse VSC8201/Cicada CIS8201 errata:
* Interoperability problem with Intel 82547EI phys
* This work around (provided by Vitesse) changes
* the default timer convergence from 8ms to 12ms
*/
miiphy_write (dev->name, reg, 0x1f, 0x2a30);
miiphy_write (dev->name, reg, 0x08, 0x0200);
miiphy_write (dev->name, reg, 0x1f, 0x52b5);
miiphy_write (dev->name, reg, 0x02, 0x0004);
miiphy_write (dev->name, reg, 0x01, 0x0671);
miiphy_write (dev->name, reg, 0x00, 0x8fae);
miiphy_write (dev->name, reg, 0x1f, 0x2a30);
miiphy_write (dev->name, reg, 0x08, 0x0000);
miiphy_write (dev->name, reg, 0x1f, 0x0000);
/* end Vitesse/Cicada errata */
}
#endif
#if defined(CONFIG_ET1011C_PHY)
/*
* Agere ET1011c PHY needs to have an extended register whacked
* for RGMII mode.
*/
if (((devnum == 2) || (devnum ==3)) && (4 == ethgroup)) {
miiphy_read (dev->name, reg, 0x16, &reg_short);
reg_short &= ~(0x7);
reg_short |= 0x6; /* RGMII DLL Delay*/
miiphy_write (dev->name, reg, 0x16, reg_short);
miiphy_read (dev->name, reg, 0x17, &reg_short);
reg_short &= ~(0x40);
miiphy_write (dev->name, reg, 0x17, reg_short);
miiphy_write(dev->name, reg, 0x1c, 0x74f0);
}
#endif
#endif
/* Start/Restart autonegotiation */
phy_setup_aneg (dev->name, reg);
udelay (1000);
}
#endif /* defined(CONFIG_PHY_RESET) */
miiphy_read (dev->name, reg, PHY_BMSR, &reg_short);
/*
* Wait if PHY is capable of autonegotiation and autonegotiation is not complete
*/
if ((reg_short & PHY_BMSR_AUTN_ABLE)
&& !(reg_short & PHY_BMSR_AUTN_COMP)) {
puts ("Waiting for PHY auto negotiation to complete");
i = 0;
while (!(reg_short & PHY_BMSR_AUTN_COMP)) {
/*
* Timeout reached ?
*/
if (i > PHY_AUTONEGOTIATE_TIMEOUT) {
puts (" TIMEOUT !\n");
break;
}
if ((i++ % 1000) == 0) {
putc ('.');
}
udelay (1000); /* 1 ms */
miiphy_read (dev->name, reg, PHY_BMSR, &reg_short);
}
puts (" done\n");
udelay (500000); /* another 500 ms (results in faster booting) */
}
#endif /* #ifndef CONFIG_CS8952_PHY */
speed = miiphy_speed (dev->name, reg);
duplex = miiphy_duplex (dev->name, reg);
if (hw_p->print_speed) {
hw_p->print_speed = 0;
printf ("ENET Speed is %d Mbps - %s duplex connection (EMAC%d)\n",
(int) speed, (duplex == HALF) ? "HALF" : "FULL",
hw_p->devnum);
}
#if defined(CONFIG_440) && !defined(CONFIG_440SP) && !defined(CONFIG_440SPE) && \
!defined(CONFIG_440EPX) && !defined(CONFIG_440GRX)
#if defined(CONFIG_440EP) || defined(CONFIG_440GR)
mfsdr(sdr_mfr, reg);
if (speed == 100) {
reg = (reg & ~SDR0_MFR_ZMII_MODE_MASK) | SDR0_MFR_ZMII_MODE_RMII_100M;
} else {
reg = (reg & ~SDR0_MFR_ZMII_MODE_MASK) | SDR0_MFR_ZMII_MODE_RMII_10M;
}
mtsdr(sdr_mfr, reg);
#endif
/* Set ZMII/RGMII speed according to the phy link speed */
reg = in32 (ZMII_SSR);
if ( (speed == 100) || (speed == 1000) )
out32 (ZMII_SSR, reg | (ZMII_SSR_SP << ZMII_SSR_V (devnum)));
else
out32 (ZMII_SSR, reg & (~(ZMII_SSR_SP << ZMII_SSR_V (devnum))));
if ((devnum == 2) || (devnum == 3)) {
if (speed == 1000)
reg = (RGMII_SSR_SP_1000MBPS << RGMII_SSR_V (devnum));
else if (speed == 100)
reg = (RGMII_SSR_SP_100MBPS << RGMII_SSR_V (devnum));
else if (speed == 10)
reg = (RGMII_SSR_SP_10MBPS << RGMII_SSR_V (devnum));
else {
printf("Error in RGMII Speed\n");
return -1;
}
out32 (RGMII_SSR, reg);
}
#endif /* defined(CONFIG_440) && !defined(CONFIG_440SP) */
#if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \
defined(CONFIG_405EX)
if (speed == 1000)
reg = (RGMII_SSR_SP_1000MBPS << RGMII_SSR_V (devnum));
else if (speed == 100)
reg = (RGMII_SSR_SP_100MBPS << RGMII_SSR_V (devnum));
else if (speed == 10)
reg = (RGMII_SSR_SP_10MBPS << RGMII_SSR_V (devnum));
else {
printf("Error in RGMII Speed\n");
return -1;
}
out32 (RGMII_SSR, reg);
#endif
/* set the Mal configuration reg */
#if defined(CONFIG_440GX) || \
defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \
defined(CONFIG_440SP) || defined(CONFIG_440SPE) || \
defined(CONFIG_405EX)
mtdcr (malmcr, MAL_CR_PLBB | MAL_CR_OPBBL | MAL_CR_LEA |
MAL_CR_PLBLT_DEFAULT | MAL_CR_EOPIE | 0x00330000);
#else
mtdcr (malmcr, MAL_CR_PLBB | MAL_CR_OPBBL | MAL_CR_LEA | MAL_CR_PLBLT_DEFAULT);
/* Errata 1.12: MAL_1 -- Disable MAL bursting */
if (get_pvr() == PVR_440GP_RB) {
mtdcr (malmcr, mfdcr(malmcr) & ~MAL_CR_PLBB);
}
#endif
/* Free "old" buffers */
if (hw_p->alloc_tx_buf)
free (hw_p->alloc_tx_buf);
if (hw_p->alloc_rx_buf)
free (hw_p->alloc_rx_buf);
/*
* Malloc MAL buffer desciptors, make sure they are
* aligned on cache line boundary size
* (401/403/IOP480 = 16, 405 = 32)
* and doesn't cross cache block boundaries.
*/
hw_p->alloc_tx_buf =
(mal_desc_t *) malloc ((sizeof (mal_desc_t) * NUM_TX_BUFF) +
((2 * CFG_CACHELINE_SIZE) - 2));
if (NULL == hw_p->alloc_tx_buf)
return -1;
if (((int) hw_p->alloc_tx_buf & CACHELINE_MASK) != 0) {
hw_p->tx =
(mal_desc_t *) ((int) hw_p->alloc_tx_buf +
CFG_CACHELINE_SIZE -
((int) hw_p->
alloc_tx_buf & CACHELINE_MASK));
} else {
hw_p->tx = hw_p->alloc_tx_buf;
}
hw_p->alloc_rx_buf =
(mal_desc_t *) malloc ((sizeof (mal_desc_t) * NUM_RX_BUFF) +
((2 * CFG_CACHELINE_SIZE) - 2));
if (NULL == hw_p->alloc_rx_buf) {
free(hw_p->alloc_tx_buf);
hw_p->alloc_tx_buf = NULL;
return -1;
}
if (((int) hw_p->alloc_rx_buf & CACHELINE_MASK) != 0) {
hw_p->rx =
(mal_desc_t *) ((int) hw_p->alloc_rx_buf +
CFG_CACHELINE_SIZE -
((int) hw_p->
alloc_rx_buf & CACHELINE_MASK));
} else {
hw_p->rx = hw_p->alloc_rx_buf;
}
for (i = 0; i < NUM_TX_BUFF; i++) {
hw_p->tx[i].ctrl = 0;
hw_p->tx[i].data_len = 0;
if (hw_p->first_init == 0) {
hw_p->txbuf_ptr =
(char *) malloc (ENET_MAX_MTU_ALIGNED);
if (NULL == hw_p->txbuf_ptr) {
free(hw_p->alloc_rx_buf);
free(hw_p->alloc_tx_buf);
hw_p->alloc_rx_buf = NULL;
hw_p->alloc_tx_buf = NULL;
for(j = 0; j < i; j++) {
free(hw_p->tx[i].data_ptr);
hw_p->tx[i].data_ptr = NULL;
}
}
}
hw_p->tx[i].data_ptr = hw_p->txbuf_ptr;
if ((NUM_TX_BUFF - 1) == i)
hw_p->tx[i].ctrl |= MAL_TX_CTRL_WRAP;
hw_p->tx_run[i] = -1;
#if 0
printf ("TX_BUFF %d @ 0x%08lx\n", i,
(ulong) hw_p->tx[i].data_ptr);
#endif
}
for (i = 0; i < NUM_RX_BUFF; i++) {
hw_p->rx[i].ctrl = 0;
hw_p->rx[i].data_len = 0;
/* rx[i].data_ptr = (char *) &rx_buff[i]; */
hw_p->rx[i].data_ptr = (char *) NetRxPackets[i];
if ((NUM_RX_BUFF - 1) == i)
hw_p->rx[i].ctrl |= MAL_RX_CTRL_WRAP;
hw_p->rx[i].ctrl |= MAL_RX_CTRL_EMPTY | MAL_RX_CTRL_INTR;
hw_p->rx_ready[i] = -1;
#if 0
printf ("RX_BUFF %d @ 0x%08lx\n", i, (ulong) hw_p->rx[i].data_ptr);
#endif
}
reg = 0x00000000;
reg |= dev->enetaddr[0]; /* set high address */
reg = reg << 8;
reg |= dev->enetaddr[1];
out32 (EMAC_IAH + hw_p->hw_addr, reg);
reg = 0x00000000;
reg |= dev->enetaddr[2]; /* set low address */
reg = reg << 8;
reg |= dev->enetaddr[3];
reg = reg << 8;
reg |= dev->enetaddr[4];
reg = reg << 8;
reg |= dev->enetaddr[5];
out32 (EMAC_IAL + hw_p->hw_addr, reg);
switch (devnum) {
case 1:
/* setup MAL tx & rx channel pointers */
#if defined (CONFIG_405EP) || defined (CONFIG_440EP) || defined (CONFIG_440GR)
mtdcr (maltxctp2r, hw_p->tx);
#else
mtdcr (maltxctp1r, hw_p->tx);
#endif
#if defined(CONFIG_440)
mtdcr (maltxbattr, 0x0);
mtdcr (malrxbattr, 0x0);
#endif
mtdcr (malrxctp1r, hw_p->rx);
/* set RX buffer size */
mtdcr (malrcbs1, ENET_MAX_MTU_ALIGNED / 16);
break;
#if defined (CONFIG_440GX)
case 2:
/* setup MAL tx & rx channel pointers */
mtdcr (maltxbattr, 0x0);
mtdcr (malrxbattr, 0x0);
mtdcr (maltxctp2r, hw_p->tx);
mtdcr (malrxctp2r, hw_p->rx);
/* set RX buffer size */
mtdcr (malrcbs2, ENET_MAX_MTU_ALIGNED / 16);
break;
case 3:
/* setup MAL tx & rx channel pointers */
mtdcr (maltxbattr, 0x0);
mtdcr (maltxctp3r, hw_p->tx);
mtdcr (malrxbattr, 0x0);
mtdcr (malrxctp3r, hw_p->rx);
/* set RX buffer size */
mtdcr (malrcbs3, ENET_MAX_MTU_ALIGNED / 16);
break;
#endif /* CONFIG_440GX */
case 0:
default:
/* setup MAL tx & rx channel pointers */
#if defined(CONFIG_440)
mtdcr (maltxbattr, 0x0);
mtdcr (malrxbattr, 0x0);
#endif
mtdcr (maltxctp0r, hw_p->tx);
mtdcr (malrxctp0r, hw_p->rx);
/* set RX buffer size */
mtdcr (malrcbs0, ENET_MAX_MTU_ALIGNED / 16);
break;
}
/* Enable MAL transmit and receive channels */
#if defined(CONFIG_405EP) || defined(CONFIG_440EP) || defined(CONFIG_440GR)
mtdcr (maltxcasr, (MAL_TXRX_CASR >> (hw_p->devnum*2)));
#else
mtdcr (maltxcasr, (MAL_TXRX_CASR >> hw_p->devnum));
#endif
mtdcr (malrxcasr, (MAL_TXRX_CASR >> hw_p->devnum));
/* set transmit enable & receive enable */
out32 (EMAC_M0 + hw_p->hw_addr, EMAC_M0_TXE | EMAC_M0_RXE);
/* set receive fifo to 4k and tx fifo to 2k */
mode_reg = in32 (EMAC_M1 + hw_p->hw_addr);
mode_reg |= EMAC_M1_RFS_4K | EMAC_M1_TX_FIFO_2K;
/* set speed */
if (speed == _1000BASET) {
#if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \
defined(CONFIG_440SP) || defined(CONFIG_440SPE)
unsigned long pfc1;
mfsdr (sdr_pfc1, pfc1);
pfc1 |= SDR0_PFC1_EM_1000;
mtsdr (sdr_pfc1, pfc1);
#endif
mode_reg = mode_reg | EMAC_M1_MF_1000MBPS | EMAC_M1_IST;
} else if (speed == _100BASET)
mode_reg = mode_reg | EMAC_M1_MF_100MBPS | EMAC_M1_IST;
else
mode_reg = mode_reg & ~0x00C00000; /* 10 MBPS */
if (duplex == FULL)
mode_reg = mode_reg | 0x80000000 | EMAC_M1_IST;
out32 (EMAC_M1 + hw_p->hw_addr, mode_reg);
/* Enable broadcast and indvidual address */
/* TBS: enabling runts as some misbehaved nics will send runts */
out32 (EMAC_RXM + hw_p->hw_addr, EMAC_RMR_BAE | EMAC_RMR_IAE);
/* we probably need to set the tx mode1 reg? maybe at tx time */
/* set transmit request threshold register */
out32 (EMAC_TRTR + hw_p->hw_addr, 0x18000000); /* 256 byte threshold */
/* set receive low/high water mark register */
#if defined(CONFIG_440)
/* 440s has a 64 byte burst length */
out32 (EMAC_RX_HI_LO_WMARK + hw_p->hw_addr, 0x80009000);
#else
/* 405s have a 16 byte burst length */
out32 (EMAC_RX_HI_LO_WMARK + hw_p->hw_addr, 0x0f002000);
#endif /* defined(CONFIG_440) */
out32 (EMAC_TXM1 + hw_p->hw_addr, 0xf8640000);
/* Set fifo limit entry in tx mode 0 */
out32 (EMAC_TXM0 + hw_p->hw_addr, 0x00000003);
/* Frame gap set */
out32 (EMAC_I_FRAME_GAP_REG + hw_p->hw_addr, 0x00000008);
/* Set EMAC IER */
hw_p->emac_ier = EMAC_ISR_PTLE | EMAC_ISR_BFCS | EMAC_ISR_ORE | EMAC_ISR_IRE;
if (speed == _100BASET)
hw_p->emac_ier = hw_p->emac_ier | EMAC_ISR_SYE;
out32 (EMAC_ISR + hw_p->hw_addr, 0xffffffff); /* clear pending interrupts */
out32 (EMAC_IER + hw_p->hw_addr, hw_p->emac_ier);
if (hw_p->first_init == 0) {
/*
* Connect interrupt service routines
*/
irq_install_handler(ETH_IRQ_NUM(hw_p->devnum),
(interrupt_handler_t *) enetInt, dev);
}
mtmsr (msr); /* enable interrupts again */
hw_p->bis = bis;
hw_p->first_init = 1;
return (1);
}
static int ppc_4xx_eth_send (struct eth_device *dev, volatile void *ptr,
int len)
{
struct enet_frame *ef_ptr;
ulong time_start, time_now;
unsigned long temp_txm0;
EMAC_4XX_HW_PST hw_p = dev->priv;
ef_ptr = (struct enet_frame *) ptr;
/*-----------------------------------------------------------------------+
* Copy in our address into the frame.
*-----------------------------------------------------------------------*/
(void) memcpy (ef_ptr->source_addr, dev->enetaddr, ENET_ADDR_LENGTH);
/*-----------------------------------------------------------------------+
* If frame is too long or too short, modify length.
*-----------------------------------------------------------------------*/
/* TBS: where does the fragment go???? */
if (len > ENET_MAX_MTU)
len = ENET_MAX_MTU;
/* memcpy ((void *) &tx_buff[tx_slot], (const void *) ptr, len); */
memcpy ((void *) hw_p->txbuf_ptr, (const void *) ptr, len);
/*-----------------------------------------------------------------------+
* set TX Buffer busy, and send it
*-----------------------------------------------------------------------*/
hw_p->tx[hw_p->tx_slot].ctrl = (MAL_TX_CTRL_LAST |
EMAC_TX_CTRL_GFCS | EMAC_TX_CTRL_GP) &
~(EMAC_TX_CTRL_ISA | EMAC_TX_CTRL_RSA);
if ((NUM_TX_BUFF - 1) == hw_p->tx_slot)
hw_p->tx[hw_p->tx_slot].ctrl |= MAL_TX_CTRL_WRAP;
hw_p->tx[hw_p->tx_slot].data_len = (short) len;
hw_p->tx[hw_p->tx_slot].ctrl |= MAL_TX_CTRL_READY;
__asm__ volatile ("eieio");
out32 (EMAC_TXM0 + hw_p->hw_addr,
in32 (EMAC_TXM0 + hw_p->hw_addr) | EMAC_TXM0_GNP0);
#ifdef INFO_4XX_ENET
hw_p->stats.pkts_tx++;
#endif
/*-----------------------------------------------------------------------+
* poll unitl the packet is sent and then make sure it is OK
*-----------------------------------------------------------------------*/
time_start = get_timer (0);
while (1) {
temp_txm0 = in32 (EMAC_TXM0 + hw_p->hw_addr);
/* loop until either TINT turns on or 3 seconds elapse */
if ((temp_txm0 & EMAC_TXM0_GNP0) != 0) {
/* transmit is done, so now check for errors
* If there is an error, an interrupt should
* happen when we return
*/
time_now = get_timer (0);
if ((time_now - time_start) > 3000) {
return (-1);
}
} else {
return (len);
}
}
}
#if defined (CONFIG_440) || defined(CONFIG_405EX)
#if defined(CONFIG_440SP) || defined(CONFIG_440SPE)
/*
* Hack: On 440SP all enet irq sources are located on UIC1
* Needs some cleanup. --sr
*/
#define UIC0MSR uic1msr
#define UIC0SR uic1sr
#else
#define UIC0MSR uic0msr
#define UIC0SR uic0sr
#endif
#if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \
defined(CONFIG_405EX)
#define UICMSR_ETHX uic0msr
#define UICSR_ETHX uic0sr
#else
#define UICMSR_ETHX uic1msr
#define UICSR_ETHX uic1sr
#endif
int enetInt (struct eth_device *dev)
{
int serviced;
int rc = -1; /* default to not us */
unsigned long mal_isr;
unsigned long emac_isr = 0;
unsigned long mal_rx_eob;
unsigned long my_uic0msr, my_uic1msr;
unsigned long my_uicmsr_ethx;
#if defined(CONFIG_440GX)
unsigned long my_uic2msr;
#endif
EMAC_4XX_HW_PST hw_p;
/*
* Because the mal is generic, we need to get the current
* eth device
*/
#if defined(CONFIG_NET_MULTI)
dev = eth_get_dev();
#else
dev = emac0_dev;
#endif
hw_p = dev->priv;
/* enter loop that stays in interrupt code until nothing to service */
do {
serviced = 0;
my_uic0msr = mfdcr (UIC0MSR);
my_uic1msr = mfdcr (uic1msr);
#if defined(CONFIG_440GX)
my_uic2msr = mfdcr (uic2msr);
#endif
my_uicmsr_ethx = mfdcr (UICMSR_ETHX);
if (!(my_uic0msr & (UIC_MRE | UIC_MTE))
&& !(my_uic1msr & (UIC_MS | UIC_MTDE | UIC_MRDE))
&& !(my_uicmsr_ethx & (UIC_ETH0 | UIC_ETH1))) {
/* not for us */
return (rc);
}
#if defined (CONFIG_440GX)
if (!(my_uic0msr & (UIC_MRE | UIC_MTE))
&& !(my_uic2msr & (UIC_ETH2 | UIC_ETH3))) {
/* not for us */
return (rc);
}
#endif
/* get and clear controller status interrupts */
/* look at Mal and EMAC interrupts */
if ((my_uic0msr & (UIC_MRE | UIC_MTE))
|| (my_uic1msr & (UIC_MS | UIC_MTDE | UIC_MRDE))) {
/* we have a MAL interrupt */
mal_isr = mfdcr (malesr);
/* look for mal error */
if (my_uic1msr & (UIC_MS | UIC_MTDE | UIC_MRDE)) {
mal_err (dev, mal_isr, my_uic1msr, MAL_UIC_DEF, MAL_UIC_ERR);
serviced = 1;
rc = 0;
}
}
/* port by port dispatch of emac interrupts */
if (hw_p->devnum == 0) {
if (UIC_ETH0 & my_uicmsr_ethx) { /* look for EMAC errors */
emac_isr = in32 (EMAC_ISR + hw_p->hw_addr);
if ((hw_p->emac_ier & emac_isr) != 0) {
emac_err (dev, emac_isr);
serviced = 1;
rc = 0;
}
}
if ((hw_p->emac_ier & emac_isr)
|| (my_uic1msr & (UIC_MS | UIC_MTDE | UIC_MRDE))) {
mtdcr (UIC0SR, UIC_MRE | UIC_MTE); /* Clear */
mtdcr (uic1sr, UIC_MS | UIC_MTDE | UIC_MRDE); /* Clear */
mtdcr (UICSR_ETHX, UIC_ETH0); /* Clear */
return (rc); /* we had errors so get out */
}
}
#if !defined(CONFIG_440SP)
if (hw_p->devnum == 1) {
if (UIC_ETH1 & my_uicmsr_ethx) { /* look for EMAC errors */
emac_isr = in32 (EMAC_ISR + hw_p->hw_addr);
if ((hw_p->emac_ier & emac_isr) != 0) {
emac_err (dev, emac_isr);
serviced = 1;
rc = 0;
}
}
if ((hw_p->emac_ier & emac_isr)
|| (my_uic1msr & (UIC_MS | UIC_MTDE | UIC_MRDE))) {
mtdcr (UIC0SR, UIC_MRE | UIC_MTE); /* Clear */
mtdcr (uic1sr, UIC_MS | UIC_MTDE | UIC_MRDE); /* Clear */
mtdcr (UICSR_ETHX, UIC_ETH1); /* Clear */
return (rc); /* we had errors so get out */
}
}
#if defined (CONFIG_440GX)
if (hw_p->devnum == 2) {
if (UIC_ETH2 & my_uic2msr) { /* look for EMAC errors */
emac_isr = in32 (EMAC_ISR + hw_p->hw_addr);
if ((hw_p->emac_ier & emac_isr) != 0) {
emac_err (dev, emac_isr);
serviced = 1;
rc = 0;
}
}
if ((hw_p->emac_ier & emac_isr)
|| (my_uic1msr & (UIC_MS | UIC_MTDE | UIC_MRDE))) {
mtdcr (UIC0SR, UIC_MRE | UIC_MTE); /* Clear */
mtdcr (uic1sr, UIC_MS | UIC_MTDE | UIC_MRDE); /* Clear */
mtdcr (uic2sr, UIC_ETH2);
return (rc); /* we had errors so get out */
}
}
if (hw_p->devnum == 3) {
if (UIC_ETH3 & my_uic2msr) { /* look for EMAC errors */
emac_isr = in32 (EMAC_ISR + hw_p->hw_addr);
if ((hw_p->emac_ier & emac_isr) != 0) {
emac_err (dev, emac_isr);
serviced = 1;
rc = 0;
}
}
if ((hw_p->emac_ier & emac_isr)
|| (my_uic1msr & (UIC_MS | UIC_MTDE | UIC_MRDE))) {
mtdcr (UIC0SR, UIC_MRE | UIC_MTE); /* Clear */
mtdcr (uic1sr, UIC_MS | UIC_MTDE | UIC_MRDE); /* Clear */
mtdcr (uic2sr, UIC_ETH3);
return (rc); /* we had errors so get out */
}
}
#endif /* CONFIG_440GX */
#endif /* !CONFIG_440SP */
/* handle MAX TX EOB interrupt from a tx */
if (my_uic0msr & UIC_MTE) {
mal_rx_eob = mfdcr (maltxeobisr);
mtdcr (maltxeobisr, mal_rx_eob);
mtdcr (UIC0SR, UIC_MTE);
}
/* handle MAL RX EOB interupt from a receive */
/* check for EOB on valid channels */
if (my_uic0msr & UIC_MRE) {
mal_rx_eob = mfdcr (malrxeobisr);
if ((mal_rx_eob & (0x80000000 >> hw_p->devnum)) != 0) { /* call emac routine for channel x */
/* clear EOB
mtdcr(malrxeobisr, mal_rx_eob); */
enet_rcv (dev, emac_isr);
/* indicate that we serviced an interrupt */
serviced = 1;
rc = 0;
}
}
mtdcr (UIC0SR, UIC_MRE); /* Clear */
mtdcr (uic1sr, UIC_MS | UIC_MTDE | UIC_MRDE); /* Clear */
switch (hw_p->devnum) {
case 0:
mtdcr (UICSR_ETHX, UIC_ETH0);
break;
case 1:
mtdcr (UICSR_ETHX, UIC_ETH1);
break;
#if defined (CONFIG_440GX)
case 2:
mtdcr (uic2sr, UIC_ETH2);
break;
case 3:
mtdcr (uic2sr, UIC_ETH3);
break;
#endif /* CONFIG_440GX */
default:
break;
}
} while (serviced);
return (rc);
}
#else /* CONFIG_440 */
int enetInt (struct eth_device *dev)
{
int serviced;
int rc = -1; /* default to not us */
unsigned long mal_isr;
unsigned long emac_isr = 0;
unsigned long mal_rx_eob;
unsigned long my_uicmsr;
EMAC_4XX_HW_PST hw_p;
/*
* Because the mal is generic, we need to get the current
* eth device
*/
#if defined(CONFIG_NET_MULTI)
dev = eth_get_dev();
#else
dev = emac0_dev;
#endif
hw_p = dev->priv;
/* enter loop that stays in interrupt code until nothing to service */
do {
serviced = 0;
my_uicmsr = mfdcr (uicmsr);
if ((my_uicmsr & (MAL_UIC_DEF | EMAC_UIC_DEF)) == 0) { /* not for us */
return (rc);
}
/* get and clear controller status interrupts */
/* look at Mal and EMAC interrupts */
if ((MAL_UIC_DEF & my_uicmsr) != 0) { /* we have a MAL interrupt */
mal_isr = mfdcr (malesr);
/* look for mal error */
if ((my_uicmsr & MAL_UIC_ERR) != 0) {
mal_err (dev, mal_isr, my_uicmsr, MAL_UIC_DEF, MAL_UIC_ERR);
serviced = 1;
rc = 0;
}
}
/* port by port dispatch of emac interrupts */
if ((SEL_UIC_DEF(hw_p->devnum) & my_uicmsr) != 0) { /* look for EMAC errors */
emac_isr = in32 (EMAC_ISR + hw_p->hw_addr);
if ((hw_p->emac_ier & emac_isr) != 0) {
emac_err (dev, emac_isr);
serviced = 1;
rc = 0;
}
}
if (((hw_p->emac_ier & emac_isr) != 0) || ((MAL_UIC_ERR & my_uicmsr) != 0)) {
mtdcr (uicsr, MAL_UIC_DEF | SEL_UIC_DEF(hw_p->devnum)); /* Clear */
return (rc); /* we had errors so get out */
}
/* handle MAX TX EOB interrupt from a tx */
if (my_uicmsr & UIC_MAL_TXEOB) {
mal_rx_eob = mfdcr (maltxeobisr);
mtdcr (maltxeobisr, mal_rx_eob);
mtdcr (uicsr, UIC_MAL_TXEOB);
}
/* handle MAL RX EOB interupt from a receive */
/* check for EOB on valid channels */
if (my_uicmsr & UIC_MAL_RXEOB)
{
mal_rx_eob = mfdcr (malrxeobisr);
if ((mal_rx_eob & (0x80000000 >> hw_p->devnum)) != 0) { /* call emac routine for channel x */
/* clear EOB
mtdcr(malrxeobisr, mal_rx_eob); */
enet_rcv (dev, emac_isr);
/* indicate that we serviced an interrupt */
serviced = 1;
rc = 0;
}
}
mtdcr (uicsr, MAL_UIC_DEF|EMAC_UIC_DEF|EMAC_UIC_DEF1); /* Clear */
#if defined(CONFIG_405EZ)
mtsdr (sdricintstat, SDR_ICRX_STAT | SDR_ICTX0_STAT | SDR_ICTX1_STAT);
#endif /* defined(CONFIG_405EZ) */
}
while (serviced);
return (rc);
}
#endif /* CONFIG_440 */
/*-----------------------------------------------------------------------------+
* MAL Error Routine
*-----------------------------------------------------------------------------*/
static void mal_err (struct eth_device *dev, unsigned long isr,
unsigned long uic, unsigned long maldef,
unsigned long mal_errr)
{
EMAC_4XX_HW_PST hw_p = dev->priv;
mtdcr (malesr, isr); /* clear interrupt */
/* clear DE interrupt */
mtdcr (maltxdeir, 0xC0000000);
mtdcr (malrxdeir, 0x80000000);
#ifdef INFO_4XX_ENET
printf ("\nMAL error occured.... ISR = %lx UIC = = %lx MAL_DEF = %lx MAL_ERR= %lx \n", isr, uic, maldef, mal_errr);
#endif
eth_init (hw_p->bis); /* start again... */
}
/*-----------------------------------------------------------------------------+
* EMAC Error Routine
*-----------------------------------------------------------------------------*/
static void emac_err (struct eth_device *dev, unsigned long isr)
{
EMAC_4XX_HW_PST hw_p = dev->priv;
printf ("EMAC%d error occured.... ISR = %lx\n", hw_p->devnum, isr);
out32 (EMAC_ISR + hw_p->hw_addr, isr);
}
/*-----------------------------------------------------------------------------+
* enet_rcv() handles the ethernet receive data
*-----------------------------------------------------------------------------*/
static void enet_rcv (struct eth_device *dev, unsigned long malisr)
{
struct enet_frame *ef_ptr;
unsigned long data_len;
unsigned long rx_eob_isr;
EMAC_4XX_HW_PST hw_p = dev->priv;
int handled = 0;
int i;
int loop_count = 0;
rx_eob_isr = mfdcr (malrxeobisr);
if ((0x80000000 >> hw_p->devnum) & rx_eob_isr) {
/* clear EOB */
mtdcr (malrxeobisr, rx_eob_isr);
/* EMAC RX done */
while (1) { /* do all */
i = hw_p->rx_slot;
if ((MAL_RX_CTRL_EMPTY & hw_p->rx[i].ctrl)
|| (loop_count >= NUM_RX_BUFF))
break;
loop_count++;
handled++;
data_len = (unsigned long) hw_p->rx[i].data_len; /* Get len */
if (data_len) {
if (data_len > ENET_MAX_MTU) /* Check len */
data_len = 0;
else {
if (EMAC_RX_ERRORS & hw_p->rx[i].ctrl) { /* Check Errors */
data_len = 0;
hw_p->stats.rx_err_log[hw_p->
rx_err_index]
= hw_p->rx[i].ctrl;
hw_p->rx_err_index++;
if (hw_p->rx_err_index ==
MAX_ERR_LOG)
hw_p->rx_err_index =
0;
} /* emac_erros */
} /* data_len < max mtu */
} /* if data_len */
if (!data_len) { /* no data */
hw_p->rx[i].ctrl |= MAL_RX_CTRL_EMPTY; /* Free Recv Buffer */
hw_p->stats.data_len_err++; /* Error at Rx */
}
/* !data_len */
/* AS.HARNOIS */
/* Check if user has already eaten buffer */
/* if not => ERROR */
else if (hw_p->rx_ready[hw_p->rx_i_index] != -1) {
if (hw_p->is_receiving)
printf ("ERROR : Receive buffers are full!\n");
break;
} else {
hw_p->stats.rx_frames++;
hw_p->stats.rx += data_len;
ef_ptr = (struct enet_frame *) hw_p->rx[i].
data_ptr;
#ifdef INFO_4XX_ENET
hw_p->stats.pkts_rx++;
#endif
/* AS.HARNOIS
* use ring buffer
*/
hw_p->rx_ready[hw_p->rx_i_index] = i;
hw_p->rx_i_index++;
if (NUM_RX_BUFF == hw_p->rx_i_index)
hw_p->rx_i_index = 0;
hw_p->rx_slot++;
if (NUM_RX_BUFF == hw_p->rx_slot)
hw_p->rx_slot = 0;
/* AS.HARNOIS
* free receive buffer only when
* buffer has been handled (eth_rx)
rx[i].ctrl |= MAL_RX_CTRL_EMPTY;
*/
} /* if data_len */
} /* while */
} /* if EMACK_RXCHL */
}
static int ppc_4xx_eth_rx (struct eth_device *dev)
{
int length;
int user_index;
unsigned long msr;
EMAC_4XX_HW_PST hw_p = dev->priv;
hw_p->is_receiving = 1; /* tell driver */
for (;;) {
/* AS.HARNOIS
* use ring buffer and
* get index from rx buffer desciptor queue
*/
user_index = hw_p->rx_ready[hw_p->rx_u_index];
if (user_index == -1) {
length = -1;
break; /* nothing received - leave for() loop */
}
msr = mfmsr ();
mtmsr (msr & ~(MSR_EE));
length = hw_p->rx[user_index].data_len;
/* Pass the packet up to the protocol layers. */
/* NetReceive(NetRxPackets[rxIdx], length - 4); */
/* NetReceive(NetRxPackets[i], length); */
NetReceive (NetRxPackets[user_index], length - 4);
/* Free Recv Buffer */
hw_p->rx[user_index].ctrl |= MAL_RX_CTRL_EMPTY;
/* Free rx buffer descriptor queue */
hw_p->rx_ready[hw_p->rx_u_index] = -1;
hw_p->rx_u_index++;
if (NUM_RX_BUFF == hw_p->rx_u_index)
hw_p->rx_u_index = 0;
#ifdef INFO_4XX_ENET
hw_p->stats.pkts_handled++;
#endif
mtmsr (msr); /* Enable IRQ's */
}
hw_p->is_receiving = 0; /* tell driver */
return length;
}
int ppc_4xx_eth_initialize (bd_t * bis)
{
static int virgin = 0;
struct eth_device *dev;
int eth_num = 0;
EMAC_4XX_HW_PST hw = NULL;
u8 ethaddr[4 + CONFIG_EMAC_NR_START][6];
u32 hw_addr[4];
#if defined(CONFIG_440GX)
unsigned long pfc1;
mfsdr (sdr_pfc1, pfc1);
pfc1 &= ~(0x01e00000);
pfc1 |= 0x01200000;
mtsdr (sdr_pfc1, pfc1);
#endif
/* first clear all mac-addresses */
for (eth_num = 0; eth_num < LAST_EMAC_NUM; eth_num++)
memcpy(ethaddr[eth_num], "\0\0\0\0\0\0", 6);
for (eth_num = 0; eth_num < LAST_EMAC_NUM; eth_num++) {
switch (eth_num) {
default: /* fall through */
case 0:
memcpy(ethaddr[eth_num + CONFIG_EMAC_NR_START],
bis->bi_enetaddr, 6);
hw_addr[eth_num] = 0x0;
break;
#ifdef CONFIG_HAS_ETH1
case 1:
memcpy(ethaddr[eth_num + CONFIG_EMAC_NR_START],
bis->bi_enet1addr, 6);
hw_addr[eth_num] = 0x100;
break;
#endif
#ifdef CONFIG_HAS_ETH2
case 2:
memcpy(ethaddr[eth_num + CONFIG_EMAC_NR_START],
bis->bi_enet2addr, 6);
hw_addr[eth_num] = 0x400;
break;
#endif
#ifdef CONFIG_HAS_ETH3
case 3:
memcpy(ethaddr[eth_num + CONFIG_EMAC_NR_START],
bis->bi_enet3addr, 6);
hw_addr[eth_num] = 0x600;
break;
#endif
}
}
/* set phy num and mode */
bis->bi_phynum[0] = CONFIG_PHY_ADDR;
bis->bi_phymode[0] = 0;
#if defined(CONFIG_PHY1_ADDR)
bis->bi_phynum[1] = CONFIG_PHY1_ADDR;
bis->bi_phymode[1] = 0;
#endif
#if defined(CONFIG_440GX)
bis->bi_phynum[2] = CONFIG_PHY2_ADDR;
bis->bi_phynum[3] = CONFIG_PHY3_ADDR;
bis->bi_phymode[2] = 2;
bis->bi_phymode[3] = 2;
#endif
#if defined(CONFIG_440GX) || \
defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \
defined(CONFIG_405EX)
ppc_4xx_eth_setup_bridge(0, bis);
#endif
for (eth_num = 0; eth_num < LAST_EMAC_NUM; eth_num++) {
/*
* See if we can actually bring up the interface,
* otherwise, skip it
*/
if (memcmp (ethaddr[eth_num], "\0\0\0\0\0\0", 6) == 0) {
bis->bi_phymode[eth_num] = BI_PHYMODE_NONE;
continue;
}
/* Allocate device structure */
dev = (struct eth_device *) malloc (sizeof (*dev));
if (dev == NULL) {
printf ("ppc_4xx_eth_initialize: "
"Cannot allocate eth_device %d\n", eth_num);
return (-1);
}
memset(dev, 0, sizeof(*dev));
/* Allocate our private use data */
hw = (EMAC_4XX_HW_PST) malloc (sizeof (*hw));
if (hw == NULL) {
printf ("ppc_4xx_eth_initialize: "
"Cannot allocate private hw data for eth_device %d",
eth_num);
free (dev);
return (-1);
}
memset(hw, 0, sizeof(*hw));
hw->hw_addr = hw_addr[eth_num];
memcpy (dev->enetaddr, ethaddr[eth_num], 6);
hw->devnum = eth_num;
hw->print_speed = 1;
sprintf (dev->name, "ppc_4xx_eth%d", eth_num - CONFIG_EMAC_NR_START);
dev->priv = (void *) hw;
dev->init = ppc_4xx_eth_init;
dev->halt = ppc_4xx_eth_halt;
dev->send = ppc_4xx_eth_send;
dev->recv = ppc_4xx_eth_rx;
if (0 == virgin) {
/* set the MAL IER ??? names may change with new spec ??? */
#if defined(CONFIG_440SPE) || \
defined(CONFIG_440EPX) || defined(CONFIG_440GRX) || \
defined(CONFIG_405EX)
mal_ier =
MAL_IER_PT | MAL_IER_PRE | MAL_IER_PWE |
MAL_IER_DE | MAL_IER_OTE | MAL_IER_OE | MAL_IER_PE ;
#else
mal_ier =
MAL_IER_DE | MAL_IER_NE | MAL_IER_TE |
MAL_IER_OPBE | MAL_IER_PLBE;
#endif
mtdcr (malesr, 0xffffffff); /* clear pending interrupts */
mtdcr (maltxdeir, 0xffffffff); /* clear pending interrupts */
mtdcr (malrxdeir, 0xffffffff); /* clear pending interrupts */
mtdcr (malier, mal_ier);
/* install MAL interrupt handler */
irq_install_handler (VECNUM_MS,
(interrupt_handler_t *) enetInt,
dev);
irq_install_handler (VECNUM_MTE,
(interrupt_handler_t *) enetInt,
dev);
irq_install_handler (VECNUM_MRE,
(interrupt_handler_t *) enetInt,
dev);
irq_install_handler (VECNUM_TXDE,
(interrupt_handler_t *) enetInt,
dev);
irq_install_handler (VECNUM_RXDE,
(interrupt_handler_t *) enetInt,
dev);
virgin = 1;
}
#if defined(CONFIG_NET_MULTI)
eth_register (dev);
#else
emac0_dev = dev;
#endif
#if defined(CONFIG_NET_MULTI)
#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
miiphy_register (dev->name,
emac4xx_miiphy_read, emac4xx_miiphy_write);
#endif
#endif
} /* end for each supported device */
return (1);
}
#if !defined(CONFIG_NET_MULTI)
void eth_halt (void) {
if (emac0_dev) {
ppc_4xx_eth_halt(emac0_dev);
free(emac0_dev);
emac0_dev = NULL;
}
}
int eth_init (bd_t *bis)
{
ppc_4xx_eth_initialize(bis);
if (emac0_dev) {
return ppc_4xx_eth_init(emac0_dev, bis);
} else {
printf("ERROR: ethaddr not set!\n");
return -1;
}
}
int eth_send(volatile void *packet, int length)
{
return (ppc_4xx_eth_send(emac0_dev, packet, length));
}
int eth_rx(void)
{
return (ppc_4xx_eth_rx(emac0_dev));
}
int emac4xx_miiphy_initialize (bd_t * bis)
{
#if defined(CONFIG_MII) || defined(CONFIG_CMD_MII)
miiphy_register ("ppc_4xx_eth0",
emac4xx_miiphy_read, emac4xx_miiphy_write);
#endif
return 0;
}
#endif /* !defined(CONFIG_NET_MULTI) */
#endif