summaryrefslogtreecommitdiffstats
path: root/bsps/powerpc/gen5200/net/network.c
diff options
context:
space:
mode:
Diffstat (limited to 'bsps/powerpc/gen5200/net/network.c')
-rw-r--r--bsps/powerpc/gen5200/net/network.c1581
1 files changed, 1581 insertions, 0 deletions
diff --git a/bsps/powerpc/gen5200/net/network.c b/bsps/powerpc/gen5200/net/network.c
new file mode 100644
index 0000000000..2e11915b26
--- /dev/null
+++ b/bsps/powerpc/gen5200/net/network.c
@@ -0,0 +1,1581 @@
+/*===============================================================*\
+| Project: RTEMS generic MPC5200 BSP |
++-----------------------------------------------------------------+
+| Partially based on the code references which are named below. |
+| Adaptions, modifications, enhancements and any recent parts of |
+| the code are: |
+| Copyright (c) 2005 |
+| Embedded Brains GmbH |
+| Obere Lagerstr. 30 |
+| D-82178 Puchheim |
+| Germany |
+| rtems@embedded-brains.de |
++-----------------------------------------------------------------+
+| The license and distribution terms for this file may be |
+| found in the file LICENSE in this distribution or at |
+| |
+| http://www.rtems.org/license/LICENSE. |
+| |
++-----------------------------------------------------------------+
+| this file contains the networking driver |
+\*===============================================================*/
+/*
+ * RTEMS/TCPIP driver for MPC5200 FEC Ethernet
+ *
+ * Modified for Motorola MPC5200 by Thomas Doerfler, <Thomas.Doerfler@imd-systems.de>
+ * COPYRIGHT (c) 2003, IMD
+ *
+ * Modified for Motorola IceCube (mgt5100) by Peter Rasmussen <prasmus@ipr-engineering.de>
+ * COPYRIGHT (c) 2003, IPR Engineering
+ *
+ * Parts of code are also under property of Driver Information Systems and based
+ * on Motorola Proprietary Information.
+ * COPYRIGHT (c) 2002 MOTOROLA INC.
+ *
+ * Modified for MPC860 by Jay Monkman (jmonkman@frasca.com)
+ *
+ * This supports Ethernet on either SCC1 or the FEC of the MPC860T.
+ * Right now, we only do 10 Mbps, even with the FEC. The function
+ * rtems_enet_driver_attach determines which one to use. Currently,
+ * only one may be used at a time.
+ *
+ * Based on the MC68360 network driver by
+ * W. Eric Norum
+ * Saskatchewan Accelerator Laboratory
+ * University of Saskatchewan
+ * Saskatoon, Saskatchewan, CANADA
+ * eric@skatter.usask.ca
+ *
+ * This supports ethernet on SCC1. Right now, we only do 10 Mbps.
+ *
+ * Modifications by Darlene Stewart <Darlene.Stewart@iit.nrc.ca>
+ * and Charles-Antoine Gauthier <charles.gauthier@iit.nrc.ca>
+ * Copyright (c) 1999, National Research Council of Canada
+ *
+ */
+
+#define __INSIDE_RTEMS_BSD_TCPIP_STACK__ 1
+#define __BSD_VISIBLE 1
+
+#include <rtems.h>
+#include <rtems/error.h>
+#include <rtems/rtems_bsdnet.h>
+#include <rtems/rtems_mii_ioctl.h>
+#include <stdio.h>
+#include <inttypes.h>
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <bsp.h>
+#include <bsp/irq.h>
+#include <bsp/mpc5200.h>
+#include <net/if_var.h>
+#include <errno.h>
+
+#include <bsp/bestcomm/include/ppctypes.h>
+#include <bsp/bestcomm/dma_image.h>
+#include <bsp/bestcomm/bestcomm_glue.h>
+#include <bsp/bestcomm/bestcomm_api.h>
+#include <bsp/bestcomm/task_api/bestcomm_cntrl.h>
+#include <bsp/bestcomm/task_api/tasksetup_bdtable.h>
+
+/* #define ETH_DEBUG */
+
+#define FEC_BD_LAST TASK_BD_TFD
+#define FEC_BD_INT TASK_BD_INT
+#define FEC_BD_READY SDMA_BD_MASK_READY
+
+/*
+ * Number of interfaces supported by this driver
+ */
+#define NIFACES 1
+
+/*
+ * Default number of buffer descriptors set aside for this driver.
+ * The number of transmit buffer descriptors has to be quite large
+ * since a single frame often uses four or more buffer descriptors.
+ */
+#define RX_BUF_COUNT 64
+#define TX_BUF_COUNT 64
+
+#define INET_ADDR_MAX_BUF_SIZE (sizeof "255.255.255.255")
+
+#define FEC_EVENT RTEMS_EVENT_0
+
+/* Task number assignment */
+#define FEC_RECV_TASK_NO TASK_FEC_RX
+#define FEC_XMIT_TASK_NO TASK_FEC_TX
+
+
+/* BD and parameters are stored in SRAM(refer to sdma.h) */
+#define MPC5200_FEC_BD_BASE ETH_BD_BASE
+
+/* FEC transmit watermark settings */
+#define MPC5200_FEC_X_WMRK_64 0x0 /* or 0x1 */
+#define MPC5200_FEC_X_WMRK_128 0x2
+#define MPC5200_FEC_X_WMRK_192 0x3
+
+/* RBD bits definitions */
+#define MPC5200_FEC_RBD_EMPTY 0x8000 /* Buffer is empty */
+#define MPC5200_FEC_RBD_WRAP 0x2000 /* Last BD in ring */
+#define MPC5200_FEC_RBD_INT 0x1000 /* Interrupt */
+#define MPC5200_FEC_RBD_LAST 0x0800 /* Buffer is last in frame(useless) */
+#define MPC5200_FEC_RBD_MISS 0x0100 /* Miss bit for prom mode */
+#define MPC5200_FEC_RBD_BC 0x0080 /* The received frame is broadcast frame */
+#define MPC5200_FEC_RBD_MC 0x0040 /* The received frame is multicast frame */
+#define MPC5200_FEC_RBD_LG 0x0020 /* Frame length violation */
+#define MPC5200_FEC_RBD_NO 0x0010 /* Nonoctet align frame */
+#define MPC5200_FEC_RBD_SH 0x0008 /* Short frame, FEC does not support SH and this bit is always cleared */
+#define MPC5200_FEC_RBD_CR 0x0004 /* CRC error */
+#define MPC5200_FEC_RBD_OV 0x0002 /* Receive FIFO overrun */
+#define MPC5200_FEC_RBD_TR 0x0001 /* The receive frame is truncated */
+#define MPC5200_FEC_RBD_ERR (MPC5200_FEC_RBD_LG | \
+ MPC5200_FEC_RBD_NO | \
+ MPC5200_FEC_RBD_CR | \
+ MPC5200_FEC_RBD_OV | \
+ MPC5200_FEC_RBD_TR)
+
+/* TBD bits definitions */
+#define MPC5200_FEC_TBD_READY 0x8000 /* Buffer is ready */
+#define MPC5200_FEC_TBD_WRAP 0x2000 /* Last BD in ring */
+#define MPC5200_FEC_TBD_INT 0x1000 /* Interrupt */
+#define MPC5200_FEC_TBD_LAST 0x0800 /* Buffer is last in frame */
+#define MPC5200_FEC_TBD_TC 0x0400 /* Transmit the CRC */
+#define MPC5200_FEC_TBD_ABC 0x0200 /* Append bad CRC */
+
+/* MII-related definitios */
+#define MPC5200_FEC_MII_DATA_ST 0x40000000 /* Start of frame delimiter */
+#define MPC5200_FEC_MII_DATA_OP_RD 0x20000000 /* Perform a read operation */
+#define MPC5200_FEC_MII_DATA_OP_WR 0x10000000 /* Perform a write operation */
+#define MPC5200_FEC_MII_DATA_PA_MSK 0x0f800000 /* PHY Address field mask */
+#define MPC5200_FEC_MII_DATA_RA_MSK 0x007c0000 /* PHY Register field mask */
+#define MPC5200_FEC_MII_DATA_TA 0x00020000 /* Turnaround */
+#define MPC5200_FEC_MII_DATA_DATAMSK 0x0000fff /* PHY data field */
+
+#define MPC5200_FEC_MII_DATA_RA_SHIFT 0x12 /* MII Register address bits */
+#define MPC5200_FEC_MII_DATA_PA_SHIFT 0x17 /* MII PHY address bits */
+
+#define FEC_INTR_MASK_USED \
+(FEC_INTR_LCEN |FEC_INTR_CRLEN |\
+ FEC_INTR_XFUNEN|FEC_INTR_XFERREN|FEC_INTR_RFERREN)
+
+typedef enum {
+ FEC_STATE_RESTART_0,
+ FEC_STATE_RESTART_1,
+ FEC_STATE_NORMAL,
+} mpc5200_fec_state;
+
+/*
+ * Device data
+ */
+typedef struct {
+ struct arpcom arpcom;
+ struct mbuf **rxMbuf;
+ struct mbuf **txMbuf;
+ mpc5200_fec_state state;
+ int acceptBroadcast;
+ int rxBdCount;
+ int txBdCount;
+ int txBdHead;
+ int txBdTail;
+ int txBdActiveCount;
+
+ rtems_id rxDaemonTid;
+ rtems_id txDaemonTid;
+
+ unsigned long rxInterrupts;
+ unsigned long rxNotLast;
+ unsigned long rxGiant;
+ unsigned long rxNonOctet;
+ unsigned long rxBadCRC;
+ unsigned long rxFIFOError;
+ unsigned long rxCollision;
+
+ unsigned long txInterrupts;
+ unsigned long txDeferred;
+ unsigned long txLateCollision;
+ unsigned long txUnderrun;
+ unsigned long txFIFOError;
+ unsigned long txMisaligned;
+ unsigned long rxNotFirst;
+ unsigned long txRetryLimit;
+
+ struct rtems_mdio_info mdio;
+ int phyAddr;
+ bool phyInitialized;
+} mpc5200_fec_context;
+
+static mpc5200_fec_context enet_driver[NIFACES];
+
+static void mpc5200_fec_send_event(rtems_id task)
+{
+ rtems_bsdnet_event_send(task, FEC_EVENT);
+}
+
+static void mpc5200_fec_wait_for_event(void)
+{
+ rtems_event_set out;
+ rtems_bsdnet_event_receive(
+ FEC_EVENT,
+ RTEMS_EVENT_ANY | RTEMS_WAIT,
+ RTEMS_NO_TIMEOUT,
+ &out
+ );
+}
+
+static void mpc5200_fec_stop_dma(void)
+{
+ TaskStop(FEC_RECV_TASK_NO);
+ TaskStop(FEC_XMIT_TASK_NO);
+}
+
+static void mpc5200_fec_request_restart(mpc5200_fec_context *self)
+{
+ self->state = FEC_STATE_RESTART_0;
+ mpc5200_fec_send_event(self->txDaemonTid);
+ mpc5200_fec_send_event(self->rxDaemonTid);
+}
+
+static void mpc5200_fec_start_dma_and_controller(void)
+{
+ TaskStart(FEC_RECV_TASK_NO, 1, FEC_RECV_TASK_NO, 1);
+ TaskStart(FEC_XMIT_TASK_NO, 1, FEC_XMIT_TASK_NO, 1);
+
+ mpc5200.ecntrl |= FEC_ECNTRL_OE | FEC_ECNTRL_EN;
+}
+
+/*
+ * Function: MPC5200_eth_addr_filter_set
+ *
+ * Description: Set individual address filter for unicast address and
+ * set physical address registers.
+ *
+ * Returns: void
+ *
+ * Notes:
+ *
+ */
+static void mpc5200_eth_addr_filter_set(mpc5200_fec_context *self) {
+ unsigned char *mac;
+ unsigned char currByte; /* byte for which to compute the CRC */
+ int byte; /* loop - counter */
+ int bit; /* loop - counter */
+ unsigned long crc = 0xffffffff; /* initial value */
+
+ /*
+ * Get the mac address of ethernet controller
+ */
+ mac = (unsigned char *)(&self->arpcom.ac_enaddr);
+
+ /*
+ * The algorithm used is the following:
+ * we loop on each of the six bytes of the provided address,
+ * and we compute the CRC by left-shifting the previous
+ * value by one position, so that each bit in the current
+ * byte of the address may contribute the calculation. If
+ * the latter and the MSB in the CRC are different, then
+ * the CRC value so computed is also ex-ored with the
+ * "polynomium generator". The current byte of the address
+ * is also shifted right by one bit at each iteration.
+ * This is because the CRC generatore in hardware is implemented
+ * as a shift-register with as many ex-ores as the radixes
+ * in the polynomium. This suggests that we represent the
+ * polynomiumm itself as a 32-bit constant.
+ */
+ for(byte = 0; byte < 6; byte++)
+ {
+
+ currByte = mac[byte];
+
+ for(bit = 0; bit < 8; bit++)
+ {
+
+ if((currByte & 0x01) ^ (crc & 0x01))
+ {
+
+ crc >>= 1;
+ crc = crc ^ 0xedb88320;
+
+ }
+ else
+ {
+
+ crc >>= 1;
+
+ }
+
+ currByte >>= 1;
+
+ }
+
+ }
+
+ crc = crc >> 26;
+
+ /*
+ * Set individual hash table register
+ */
+ if(crc >= 32)
+ {
+
+ mpc5200.iaddr1 = (1 << (crc - 32));
+ mpc5200.iaddr2 = 0;
+
+ }
+ else
+ {
+
+ mpc5200.iaddr1 = 0;
+ mpc5200.iaddr2 = (1 << crc);
+
+ }
+
+ /*
+ * Set physical address
+ */
+ mpc5200.paddr1 = (mac[0] << 24) + (mac[1] << 16) + (mac[2] << 8) + mac[3];
+ mpc5200.paddr2 = (mac[4] << 24) + (mac[5] << 16) + 0x8808;
+
+ }
+
+static int mpc5200_eth_mii_transfer(
+ int phyAddr,
+ unsigned regAddr,
+ uint32_t data
+)
+{
+ int timeout = 0xffff;
+
+ mpc5200.ievent = FEC_INTR_MII;
+
+ mpc5200.mii_data = MPC5200_FEC_MII_DATA_ST
+ | MPC5200_FEC_MII_DATA_TA
+ | (phyAddr << MPC5200_FEC_MII_DATA_PA_SHIFT)
+ | (regAddr << MPC5200_FEC_MII_DATA_RA_SHIFT)
+ | data;
+
+ while (timeout > 0 && (mpc5200.ievent & FEC_INTR_MII) == 0) {
+ --timeout;
+ }
+
+ return timeout > 0 ? 0 : -1;
+}
+
+/* FIXME: Make this static, this needs a fix in an application */
+int mpc5200_eth_mii_read(
+ int phyAddr,
+ void *arg,
+ unsigned regAddr,
+ uint32_t *retVal
+)
+{
+ int rv = mpc5200_eth_mii_transfer(
+ phyAddr,
+ regAddr,
+ MPC5200_FEC_MII_DATA_OP_RD
+ );
+
+ *retVal = mpc5200.mii_data & 0xffff;
+
+ return rv;
+}
+
+static int mpc5200_eth_mii_write(
+ int phyAddr,
+ void *arg,
+ unsigned regAddr,
+ uint32_t data
+)
+{
+ return mpc5200_eth_mii_transfer(
+ phyAddr,
+ regAddr,
+ MPC5200_FEC_MII_DATA_OP_WR | data
+ );
+}
+
+
+/*
+ * Reset a running ethernet driver including the hardware FIFOs and the FEC.
+ */
+static void mpc5200_fec_reset(mpc5200_fec_context *self)
+{
+ volatile int delay;
+ /*
+ * Clear FIFO status registers
+ */
+ mpc5200.rfifo_status &= FEC_FIFO_STAT_ERROR;
+ mpc5200.tfifo_status &= FEC_FIFO_STAT_ERROR;
+
+ /*
+ * reset the FIFOs
+ */
+ mpc5200.reset_cntrl = 0x03000000;
+
+ for (delay = 0;delay < 16*4;delay++) {};
+
+ mpc5200.reset_cntrl = 0x01000000;
+
+ /*
+ * Issue a reset command to the FEC chip
+ */
+ mpc5200.ecntrl |= FEC_ECNTRL_RESET;
+
+ /*
+ * wait at least 16 clock cycles
+ */
+ for (delay = 0;delay < 16*4;delay++) {};
+}
+
+
+/*
+ * Function: mpc5200_fec_off
+ *
+ * Description: Stop the FEC and disable the ethernet SmartComm tasks.
+ * This function "turns off" the driver.
+ *
+ * Returns: void
+ *
+ * Notes:
+ *
+ */
+static void mpc5200_fec_off(mpc5200_fec_context *self)
+ {
+ int counter = 0xffff;
+
+
+#if defined(ETH_DEBUG)
+ uint32_t phyStatus;
+ int i;
+
+ for(i = 0; i < 9; i++)
+ {
+
+ mpc5200_eth_mii_read(self->phyAddr, self, i, &phyStatus);
+ printf ("Mii reg %d: 0x%04" PRIx32 "\r\n", i, phyStatus);
+
+ }
+
+ for(i = 16; i < 21; i++)
+ {
+
+ mpc5200_eth_mii_read(self->phyAddr, self, i, &phyStatus);
+ printf ("Mii reg %d: 0x%04" PRIx32 "\r\n", i, phyStatus);
+
+ }
+#endif /* ETH_DEBUG */
+
+ /*
+ * block FEC chip interrupts
+ */
+ mpc5200.imask = 0;
+
+ /*
+ * issue graceful stop command to the FEC transmitter if necessary
+ */
+ mpc5200.x_cntrl |= FEC_XCNTRL_GTS;
+
+ /*
+ * wait for graceful stop to register
+ * FIXME: add rtems_task_wake_after here, if it takes to long
+ */
+ while((counter--) && (!(mpc5200.ievent & FEC_INTR_GRA)));
+
+ mpc5200_fec_stop_dma();
+
+ /*
+ * Disable transmit / receive interrupts
+ */
+ bestcomm_glue_irq_disable(FEC_XMIT_TASK_NO);
+ bestcomm_glue_irq_disable(FEC_RECV_TASK_NO);
+
+ /*
+ * Disable the Ethernet Controller
+ */
+ mpc5200.ecntrl &= ~(FEC_ECNTRL_OE | FEC_ECNTRL_EN);
+}
+
+/*
+ * MPC5200 FEC interrupt handler
+ */
+static void mpc5200_fec_irq_handler(rtems_irq_hdl_param handle)
+{
+ mpc5200_fec_context *self = (mpc5200_fec_context *) handle;
+ volatile uint32_t ievent;
+
+ ievent = mpc5200.ievent;
+
+ mpc5200.ievent = ievent;
+ /*
+ * check errors, update statistics
+ */
+ if (ievent & FEC_INTR_LATE_COL) {
+ self->txLateCollision++;
+ }
+ if (ievent & FEC_INTR_COL_RETRY) {
+ self->txRetryLimit++;
+ }
+ if (ievent & FEC_INTR_XFIFO_UN) {
+ self->txUnderrun++;
+ }
+ if (ievent & FEC_INTR_XFIFO_ERR) {
+ self->txFIFOError++;
+ }
+ if (ievent & FEC_INTR_RFIFO_ERR) {
+ self->rxFIFOError++;
+ }
+ /*
+ * fatal error ocurred?
+ */
+ if (ievent & (FEC_INTR_XFIFO_ERR | FEC_INTR_RFIFO_ERR)) {
+ mpc5200.imask &= ~(FEC_INTR_XFERREN | FEC_INTR_RFERREN);
+ mpc5200_fec_request_restart(self);
+ }
+}
+
+static void mpc5200_smartcomm_rx_irq_handler(void *arg)
+{
+ mpc5200_fec_context *self = arg;
+
+ ++self->rxInterrupts;
+ mpc5200_fec_send_event(self->rxDaemonTid);
+ SDMA_CLEAR_IEVENT(&mpc5200.sdma.IntPend, FEC_RECV_TASK_NO);
+ bestcomm_glue_irq_disable(FEC_RECV_TASK_NO);
+}
+
+static void mpc5200_smartcomm_tx_irq_handler(void *arg)
+{
+ mpc5200_fec_context *self = arg;
+
+ ++self->txInterrupts;
+ mpc5200_fec_send_event(self->txDaemonTid);
+ SDMA_CLEAR_IEVENT(&mpc5200.sdma.IntPend, FEC_XMIT_TASK_NO);
+ bestcomm_glue_irq_disable(FEC_XMIT_TASK_NO);
+}
+
+static void mpc5200_fec_init_mib(mpc5200_fec_context *self)
+{
+ memset(RTEMS_DEVOLATILE(uint8_t *, &mpc5200.RES [0]), 0, 0x2e4);
+ mpc5200.mib_control = 0x40000000;
+}
+
+/*
+ * Function: mpc5200_fec_initialize_hardware
+ *
+ * Description: Configure the MPC5200 FEC registers and enable the
+ * SmartComm tasks. This function "turns on" the driver.
+ *
+ * Returns: void
+ *
+ * Notes:
+ *
+ */
+static void mpc5200_fec_initialize_hardware(mpc5200_fec_context *self)
+{
+ /* We want at most 2.5MHz */
+ uint32_t div = 2 * 2500000;
+ uint32_t mii_speed = (IPB_CLOCK + div - 1) / div;
+
+ /*
+ * Reset mpc5200 FEC
+ */
+ mpc5200_fec_reset(self);
+ mpc5200_fec_init_mib(self);
+
+ /*
+ * Clear FEC-Lite interrupt event register (IEVENT)
+ */
+ mpc5200.ievent = FEC_INTR_CLEAR_ALL;
+
+ /*
+ * Set interrupt mask register
+ */
+ mpc5200.imask = FEC_INTR_MASK_USED;
+ /*
+ * Set FEC-Lite receive control register (R_CNTRL)
+ * frame length=1518, MII mode for 18-wire-transceiver
+ */
+ mpc5200.r_cntrl = ((ETHER_MAX_LEN << FEC_RCNTRL_MAX_FL_SHIFT)
+ | FEC_RCNTRL_FCE
+ | FEC_RCNTRL_MII_MODE);
+
+ /*
+ * Set FEC-Lite transmit control register (X_CNTRL)
+ * full-duplex, heartbeat disabled
+ */
+ mpc5200.x_cntrl = FEC_XCNTRL_FDEN;
+
+
+
+ /*
+ * Set MII_SPEED = IPB clock / (2 * mii_speed))
+ * and do not drop the Preamble.
+ */
+ mpc5200.mii_speed = mii_speed << 1;
+
+ /*
+ * Set Opcode/Pause Duration Register
+ */
+ mpc5200.op_pause = 0x00010020;
+
+ /*
+ * Set Rx FIFO alarm and granularity value
+ */
+ mpc5200.rfifo_cntrl = (FEC_FIFO_CNTRL_FRAME
+ | (0x7 << FEC_FIFO_CNTRL_GR_SHIFT));
+ mpc5200.rfifo_alarm = 0x0000030c;
+
+ /*
+ * Set Tx FIFO granularity value
+ */
+ mpc5200.tfifo_cntrl = (FEC_FIFO_CNTRL_FRAME
+ | (0x7 << FEC_FIFO_CNTRL_GR_SHIFT));
+
+ /*
+ * Set transmit fifo watermark register (X_WMRK), default = 64
+ */
+ mpc5200.tfifo_alarm = 0x00000100; /* 256 bytes */
+ mpc5200.x_wmrk = FEC_XWMRK_256; /* 256 bytes */
+
+ /*
+ * Set individual address filter for unicast address
+ * and set physical address registers.
+ */
+ mpc5200_eth_addr_filter_set(self);
+
+ /*
+ * Set multicast address filter
+ */
+ mpc5200.gaddr1 = 0x00000000;
+ mpc5200.gaddr2 = 0x00000000;
+
+ /*
+ * enable CRC in finite state machine register
+ */
+ mpc5200.xmit_fsm = FEC_FSM_CRC | FEC_FSM_ENFSM;
+}
+
+ /*
+ * Initialize PHY(LXT971A):
+ *
+ * Generally, on power up, the LXT971A reads its configuration
+ * pins to check for forced operation, If not cofigured for
+ * forced operation, it uses auto-negotiation/parallel detection
+ * to automatically determine line operating conditions.
+ * If the PHY device on the other side of the link supports
+ * auto-negotiation, the LXT971A auto-negotiates with it
+ * using Fast Link Pulse(FLP) Bursts. If the PHY partner does not
+ * support auto-negotiation, the LXT971A automatically detects
+ * the presence of either link pulses(10Mbps PHY) or Idle
+ * symbols(100Mbps) and sets its operating conditions accordingly.
+ *
+ * When auto-negotiation is controlled by software, the following
+ * steps are recommended.
+ *
+ * Note:
+ * The physical address is dependent on hardware configuration.
+ *
+ * Returns: void
+ *
+ * Notes:
+ *
+ */
+static void mpc5200_fec_initialize_phy(mpc5200_fec_context *self)
+{
+ if (self->phyInitialized) {
+ return;
+ } else {
+ self->phyInitialized = true;
+ }
+
+ /*
+ * Reset PHY, then delay 300ns
+ */
+ mpc5200_eth_mii_write(self->phyAddr, self, 0x0, 0x8000);
+
+ rtems_task_wake_after(2);
+
+ /* MII100 */
+
+ /*
+ * Set the auto-negotiation advertisement register bits
+ */
+ mpc5200_eth_mii_write(self->phyAddr, self, 0x4, 0x01e1);
+
+ /*
+ * Set MDIO bit 0.12 = 1(&& bit 0.9=1?) to enable auto-negotiation
+ */
+ mpc5200_eth_mii_write(self->phyAddr, self, 0x0, 0x1200);
+
+ /*
+ * Wait for AN completion
+ */
+#if 0
+ int timeout = 0x100;
+ uint32_t phyStatus;
+ do
+ {
+
+ rtems_task_wake_after(2);
+
+ if((timeout--) == 0)
+ {
+
+#if defined(ETH_DEBUG)
+ printf ("MPC5200FEC PHY auto neg failed." "\r\n");
+#endif
+
+ }
+
+ if(mpc5200_eth_mii_read(self->phyAddr, self, 0x1, &phyStatus) != true)
+ {
+
+#if defined(ETH_DEBUG)
+ printf ("MPC5200FEC PHY auto neg failed: 0x%04" PRIx32 ".\r\n", phyStatus);
+#endif
+
+ return;
+
+ }
+
+ } while((phyStatus & 0x0020) != 0x0020);
+
+#endif
+#if ETH_PROMISCOUS_MODE
+ mpc5200.r_cntrl |= 0x00000008; /* set to promiscous mode */
+#endif
+
+#if ETH_LOOP_MODE
+ mpc5200.r_cntrl |= 0x00000001; /* set to loop mode */
+#endif
+
+#if defined(ETH_DEBUG)
+ int i;
+ uint32_t phyStatus;
+ /*
+ * Print PHY registers after initialization.
+ */
+ for(i = 0; i < 9; i++)
+ {
+
+ mpc5200_eth_mii_read(self->phyAddr, self, i, &phyStatus);
+ printf ("Mii reg %d: 0x%04" PRIx32 "\r\n", i, phyStatus);
+
+ }
+
+ for(i = 16; i < 21; i++)
+ {
+
+ mpc5200_eth_mii_read(self->phyAddr, self, i, &phyStatus);
+ printf ("Mii reg %d: 0x%04" PRIx32 "\r\n", i, phyStatus);
+
+ }
+#endif /* ETH_DEBUG */
+
+ }
+
+static void mpc5200_fec_restart(mpc5200_fec_context *self, rtems_id otherDaemon)
+{
+ if (self->state == FEC_STATE_RESTART_1) {
+ mpc5200_fec_initialize_hardware(self);
+ mpc5200_fec_initialize_phy(self);
+ mpc5200_fec_start_dma_and_controller();
+ self->state = FEC_STATE_NORMAL;
+ } else {
+ self->state = FEC_STATE_RESTART_1;
+ }
+
+ mpc5200_fec_send_event(otherDaemon);
+ while (self->state != FEC_STATE_NORMAL) {
+ mpc5200_fec_wait_for_event();
+ }
+}
+
+/*
+ * Send packet (caller provides header).
+ */
+static void mpc5200_fec_tx_start(struct ifnet *ifp)
+ {
+
+ mpc5200_fec_context *self = ifp->if_softc;
+
+ ifp->if_flags |= IFF_OACTIVE;
+
+ mpc5200_fec_send_event(self->txDaemonTid);
+
+ }
+
+static TaskBD1_t *mpc5200_fec_init_tx_dma(int bdCount, struct mbuf **mbufs)
+{
+ TaskSetupParamSet_t param = {
+ .NumBD = bdCount,
+ .Size = {
+ .MaxBuf = ETHER_MAX_LEN
+ },
+ .Initiator = 0,
+ .StartAddrSrc = 0,
+ .IncrSrc = sizeof(uint32_t),
+ .SzSrc = sizeof(uint32_t),
+ .StartAddrDst = (uint32) &mpc5200.tfifo_data,
+ .IncrDst = 0,
+ .SzDst = sizeof(uint32_t)
+ };
+ int bdIndex = 0;
+
+ TaskSetup(FEC_XMIT_TASK_NO, &param);
+
+ for (bdIndex = 0; bdIndex < bdCount; ++bdIndex) {
+ mbufs [bdIndex] = NULL;
+ }
+
+ return (TaskBD1_t *) TaskGetBDRing(FEC_XMIT_TASK_NO);
+}
+
+#if 0
+static void mpc5200_fec_requeue_and_discard_tx_frames(
+ int bdIndex,
+ int bdCount,
+ TaskBD1_t *bdRing,
+ struct mbuf **mbufs
+)
+{
+ int bdStop = bdIndex;
+ struct mbuf *previous = NULL;
+
+ do {
+ struct mbuf *current = NULL;
+ uint32 status = 0;
+ TaskBD1_t *bd = NULL;
+
+ if (bdIndex > 1) {
+ --bdIndex;
+ } else {
+ bdIndex = bdCount - 1;
+ }
+
+ current = mbufs [bdIndex];
+ mbufs [bdIndex] = NULL;
+
+ status = bdRing [bdIndex].Status;
+ bdRing [bdIndex].Status = 0;
+
+ if (current != NULL) {
+ if ((status & FEC_BD_LAST) != 0) {
+ if (previous != NULL) {
+ IF_PREPEND(&ifp->if_snd, previous);
+ }
+ }
+ } else {
+ break;
+ }
+
+ previous = current;
+ } while (bdIndex != bdStop);
+}
+#endif
+
+static void mpc5200_fec_discard_tx_frames(
+ int bdCount,
+ struct mbuf **mbufs
+)
+{
+ int bdIndex = 0;
+
+ for (bdIndex = 0; bdIndex < bdCount; ++bdIndex) {
+ struct mbuf *m = mbufs [bdIndex];
+
+ if (m != NULL) {
+ mbufs [bdIndex] = NULL;
+ m_free(m);
+ }
+ }
+}
+
+static void mpc5200_fec_reset_tx_dma(
+ int bdCount,
+ TaskBD1_t *bdRing,
+ struct mbuf **mbufs,
+ struct mbuf *m
+)
+{
+ TaskStop(FEC_XMIT_TASK_NO);
+ TaskBDReset(FEC_XMIT_TASK_NO);
+ mpc5200_fec_discard_tx_frames(bdCount, mbufs);
+ while (m != NULL) {
+ m = m_free(m);
+ }
+}
+
+static struct mbuf *mpc5200_fec_next_fragment(
+ struct ifnet *ifp,
+ struct mbuf *m,
+ bool *isFirst
+)
+{
+ struct mbuf *n = NULL;
+
+ *isFirst = false;
+
+ while (true) {
+ if (m == NULL) {
+ IF_DEQUEUE(&ifp->if_snd, m);
+
+ if (m != NULL) {
+ *isFirst = true;
+ } else {
+ ifp->if_flags &= ~IFF_OACTIVE;
+
+ return NULL;
+ }
+ }
+
+ if (m->m_len > 0) {
+ break;
+ } else {
+ m = m_free(m);
+ }
+ }
+
+ n = m->m_next;
+ while (n != NULL && n->m_len <= 0) {
+ n = m_free(n);
+ }
+ m->m_next = n;
+
+ return m;
+}
+
+static bool mpc5200_fec_transmit(
+ struct ifnet *ifp,
+ int bdCount,
+ TaskBD1_t *bdRing,
+ struct mbuf **mbufs,
+ int *bdIndexPtr,
+ struct mbuf **mPtr,
+ TaskBD1_t **firstPtr
+)
+{
+ bool bdShortage = false;
+ int bdIndex = *bdIndexPtr;
+ struct mbuf *m = *mPtr;
+ TaskBD1_t *first = *firstPtr;
+
+ while (true) {
+ TaskBD1_t *bd = &bdRing [bdIndex];
+
+ if (bd->Status == 0) {
+ struct mbuf *done = mbufs [bdIndex];
+ bool isFirst = false;
+
+ if (done != NULL) {
+ m_free(done);
+ mbufs [bdIndex] = NULL;
+ }
+
+ m = mpc5200_fec_next_fragment(ifp, m, &isFirst);
+ if (m != NULL) {
+ uint32 status = (uint32) m->m_len;
+
+ mbufs [bdIndex] = m;
+
+ rtems_cache_flush_multiple_data_lines(mtod(m, void *), m->m_len);
+
+ if (isFirst) {
+ first = bd;
+ } else {
+ status |= FEC_BD_READY;
+ }
+
+ bd->DataPtr [0] = mtod(m, uint32);
+
+ if (m->m_next != NULL) {
+ bd->Status = status;
+ } else {
+ bd->Status = status | FEC_BD_INT | FEC_BD_LAST;
+ first->Status |= FEC_BD_READY;
+ SDMA_TASK_ENABLE(SDMA_TCR, FEC_XMIT_TASK_NO);
+ }
+
+ m = m->m_next;
+ } else {
+ break;
+ }
+ } else {
+ bdShortage = true;
+ break;
+ }
+
+ if (bdIndex < bdCount - 1) {
+ ++bdIndex;
+ } else {
+ bdIndex = 0;
+ }
+ }
+
+ *bdIndexPtr = bdIndex;
+ *mPtr = m;
+ *firstPtr = first;
+
+ return bdShortage;
+}
+
+static void mpc5200_fec_tx_daemon(void *arg)
+{
+ mpc5200_fec_context *self = arg;
+ struct ifnet *ifp = &self->arpcom.ac_if;
+ int bdIndex = 0;
+ int bdCount = self->txBdCount;
+ struct mbuf **mbufs = &self->txMbuf [0];
+ struct mbuf *m = NULL;
+ TaskBD1_t *bdRing = mpc5200_fec_init_tx_dma(bdCount, mbufs);
+ TaskBD1_t *first = NULL;
+ bool bdShortage = false;
+
+ while (true) {
+ if (bdShortage) {
+ bestcomm_glue_irq_enable(FEC_XMIT_TASK_NO);
+ }
+ mpc5200_fec_wait_for_event();
+
+ if (self->state != FEC_STATE_NORMAL) {
+ mpc5200_fec_reset_tx_dma(bdCount, bdRing, mbufs, m);
+ mpc5200_fec_restart(self, self->rxDaemonTid);
+ bdIndex = 0;
+ m = NULL;
+ first = NULL;
+ }
+
+ bdShortage = mpc5200_fec_transmit(
+ ifp,
+ bdCount,
+ bdRing,
+ mbufs,
+ &bdIndex,
+ &m,
+ &first
+ );
+ }
+}
+
+static struct mbuf *mpc5200_fec_add_mbuf(struct ifnet *ifp, TaskBD1_t *bd)
+{
+ struct mbuf *m = NULL;
+
+ MGETHDR (m, M_WAIT, MT_DATA);
+ MCLGET (m, M_WAIT);
+ m->m_pkthdr.rcvif = ifp;
+
+ /* XXX: The dcbi operation does not work properly */
+ rtems_cache_flush_multiple_data_lines(mtod(m, void *), ETHER_MAX_LEN);
+
+ bd->DataPtr [0] = mtod(m, uint32);
+ bd->Status = ETHER_MAX_LEN | FEC_BD_READY;
+
+ return m;
+}
+
+static TaskBD1_t *mpc5200_fec_init_rx_dma(
+ struct ifnet *ifp,
+ int bdCount,
+ struct mbuf **mbufs
+)
+{
+ TaskSetupParamSet_t param = {
+ .NumBD = bdCount,
+ .Size = {
+ .MaxBuf = ETHER_MAX_LEN
+ },
+ .Initiator = 0,
+ .StartAddrSrc = (uint32) &mpc5200.rfifo_data,
+ .IncrSrc = 0,
+ .SzSrc = sizeof(uint32_t),
+ .StartAddrDst = 0,
+ .IncrDst = sizeof(uint32_t),
+ .SzDst = sizeof(uint32_t)
+ };
+ TaskBD1_t *bdRing = NULL;
+ int bdIndex = 0;
+
+ TaskSetup(FEC_RECV_TASK_NO, &param);
+ bdRing = (TaskBD1_t *) TaskGetBDRing(FEC_RECV_TASK_NO);
+
+ for (bdIndex = 0; bdIndex < bdCount; ++bdIndex) {
+ mbufs [bdIndex] = mpc5200_fec_add_mbuf(ifp, &bdRing [bdIndex]);
+ }
+
+ return bdRing;
+}
+
+static void mpc5200_fec_reset_rx_dma(int bdCount, TaskBD1_t *bdRing)
+{
+ int bdIndex = 0;
+
+ TaskStop(FEC_RECV_TASK_NO);
+ TaskBDReset(FEC_RECV_TASK_NO);
+
+ for (bdIndex = 0; bdIndex < bdCount; ++bdIndex) {
+ bdRing [bdIndex].Status = ETHER_MAX_LEN | FEC_BD_READY;
+ }
+}
+
+static int mpc5200_fec_ether_input(
+ struct ifnet *ifp,
+ int bdIndex,
+ int bdCount,
+ TaskBD1_t *bdRing,
+ struct mbuf **mbufs
+)
+{
+ while (true) {
+ TaskBD1_t *bd = &bdRing [bdIndex];
+ struct mbuf *m = mbufs [bdIndex];
+ uint32 status = 0;
+ int len = 0;
+ struct ether_header *eh = NULL;
+
+ SDMA_CLEAR_IEVENT(&mpc5200.sdma.IntPend, FEC_RECV_TASK_NO);
+ status = bd->Status;
+
+ if ((status & FEC_BD_READY) != 0) {
+ break;
+ }
+
+ eh = mtod(m, struct ether_header *);
+
+ len = (status & 0xffff) - ETHER_HDR_LEN - ETHER_CRC_LEN;
+ m->m_len = len;
+ m->m_pkthdr.len = len;
+ m->m_data = mtod(m, char *) + ETHER_HDR_LEN;
+
+ ether_input(ifp, eh, m);
+
+ mbufs [bdIndex] = mpc5200_fec_add_mbuf(ifp, bd);
+
+ if (bdIndex < bdCount - 1) {
+ ++bdIndex;
+ } else {
+ bdIndex = 0;
+ }
+ }
+
+ return bdIndex;
+}
+
+static void mpc5200_fec_rx_daemon(void *arg)
+{
+ mpc5200_fec_context *self = arg;
+ struct ifnet *ifp = &self->arpcom.ac_if;
+ int bdIndex = 0;
+ int bdCount = self->rxBdCount;
+ struct mbuf **mbufs = &self->rxMbuf [0];
+ TaskBD1_t *bdRing = mpc5200_fec_init_rx_dma(ifp, bdCount, mbufs);
+
+ while (true) {
+ bestcomm_glue_irq_enable(FEC_RECV_TASK_NO);
+ mpc5200_fec_wait_for_event();
+
+ bdIndex = mpc5200_fec_ether_input(ifp, bdIndex, bdCount, bdRing, mbufs);
+
+ if (self->state != FEC_STATE_NORMAL) {
+ mpc5200_fec_reset_rx_dma(bdCount, bdRing);
+ mpc5200_fec_restart(self, self->txDaemonTid);
+ bdIndex = 0;
+ }
+ }
+}
+
+/*
+ * Initialize and start the device
+ */
+static void mpc5200_fec_init(void *arg)
+{
+ rtems_status_code sc = RTEMS_SUCCESSFUL;
+ mpc5200_fec_context *self = (mpc5200_fec_context *)arg;
+ struct ifnet *ifp = &self->arpcom.ac_if;
+
+ if(self->txDaemonTid == 0)
+ {
+ size_t rxMbufTableSize = self->rxBdCount * sizeof(*self->rxMbuf);
+ size_t txMbufTableSize = self->txBdCount * sizeof(*self->txMbuf);
+
+ /*
+ * Allocate a set of mbuf pointers
+ */
+ self->rxMbuf = malloc(rxMbufTableSize, M_MBUF, M_NOWAIT);
+ self->txMbuf = malloc(txMbufTableSize, M_MBUF, M_NOWAIT);
+
+ if(!self->rxMbuf || !self->txMbuf)
+ rtems_panic ("No memory for mbuf pointers");
+
+ /*
+ * DMA setup
+ */
+ bestcomm_glue_init();
+ mpc5200.sdma.ipr [0] = 4; /* always initiator */
+ mpc5200.sdma.ipr [3] = 6; /* eth rx initiator */
+ mpc5200.sdma.ipr [4] = 5; /* eth tx initiator */
+
+ /*
+ * Set up interrupts
+ */
+ bestcomm_glue_irq_install(FEC_RECV_TASK_NO,
+ mpc5200_smartcomm_rx_irq_handler,
+ self);
+ bestcomm_glue_irq_install(FEC_XMIT_TASK_NO,
+ mpc5200_smartcomm_tx_irq_handler,
+ self);
+ sc = rtems_interrupt_handler_install(
+ BSP_SIU_IRQ_ETH,
+ "FEC",
+ RTEMS_INTERRUPT_UNIQUE,
+ mpc5200_fec_irq_handler,
+ self
+ );
+ if(sc != RTEMS_SUCCESSFUL) {
+ rtems_panic ("Can't attach MPC5x00 FEX interrupt handler\n");
+ }
+
+ /*
+ * Start driver tasks
+ */
+ self->txDaemonTid = rtems_bsdnet_newproc("FEtx", 4096, mpc5200_fec_tx_daemon, self);
+ self->rxDaemonTid = rtems_bsdnet_newproc("FErx", 4096, mpc5200_fec_rx_daemon, self);
+ }
+
+ mpc5200_fec_request_restart(self);
+
+ /*
+ * Set flags appropriately
+ */
+ if(ifp->if_flags & IFF_PROMISC)
+ mpc5200.r_cntrl |= 0x08;
+ else
+ mpc5200.r_cntrl &= ~0x08;
+
+ /*
+ * Tell the world that we're running.
+ */
+ ifp->if_flags |= IFF_RUNNING;
+}
+
+static void enet_stats (mpc5200_fec_context *self)
+{
+ if (self->phyAddr >= 0) {
+ struct ifnet *ifp = &self->arpcom.ac_if;
+ int media = IFM_MAKEWORD(0, 0, 0, self->phyAddr);
+ int rv = (*ifp->if_ioctl)(ifp, SIOCGIFMEDIA, (caddr_t) &media);
+
+ if (rv == 0) {
+ rtems_ifmedia2str(media, NULL, 0);
+ printf ("\n");
+ } else {
+ printf ("PHY communication error\n");
+ }
+ }
+ printf (" Rx Interrupts:%-8lu", self->rxInterrupts);
+ printf (" Rx Not First:%-8lu", self->rxNotFirst);
+ printf (" Rx Not Last:%-8lu\n", self->rxNotLast);
+ printf (" Rx Giant:%-8lu", self->rxGiant);
+ printf (" Rx Non-octet:%-8lu", self->rxNonOctet);
+ printf (" Rx Bad CRC:%-8lu\n", self->rxBadCRC);
+ printf (" Rx FIFO Error:%-8lu", self->rxFIFOError);
+ printf (" Rx Collision:%-8lu", self->rxCollision);
+
+ printf (" Tx Interrupts:%-8lu\n", self->txInterrupts);
+ printf (" Tx Deferred:%-8lu", self->txDeferred);
+ printf (" Tx Late Collision:%-8lu", self->txLateCollision);
+ printf (" Tx Retransmit Limit:%-8lu\n", self->txRetryLimit);
+ printf (" Tx Underrun:%-8lu", self->txUnderrun);
+ printf (" Tx FIFO Error:%-8lu", self->txFIFOError);
+ printf (" Tx Misaligned:%-8lu\n", self->txMisaligned);
+}
+
+static int32_t mpc5200_fec_setMultiFilter(struct ifnet *ifp)
+{
+ /*mpc5200_fec_context *self = ifp->if_softc; */
+ /* XXX anything to do? */
+ return 0;
+}
+
+
+/*
+ * Driver ioctl handler
+ */
+static int mpc5200_fec_ioctl (struct ifnet *ifp, ioctl_command_t command, caddr_t data)
+ {
+ mpc5200_fec_context *self = ifp->if_softc;
+ int error = 0;
+
+ switch(command)
+ {
+
+ case SIOCGIFADDR:
+ case SIOCSIFADDR:
+
+ ether_ioctl(ifp, command, data);
+
+ break;
+
+ case SIOCADDMULTI:
+ case SIOCDELMULTI: {
+ struct ifreq* ifr = (struct ifreq*) data;
+ error = (command == SIOCADDMULTI)
+ ? ether_addmulti(ifr, &self->arpcom)
+ : ether_delmulti(ifr, &self->arpcom);
+
+ if (error == ENETRESET) {
+ if (ifp->if_flags & IFF_RUNNING)
+ error = mpc5200_fec_setMultiFilter(ifp);
+ else
+ error = 0;
+ }
+ break;
+ }
+
+ case SIOCSIFFLAGS:
+
+ switch(ifp->if_flags & (IFF_UP | IFF_RUNNING))
+ {
+
+ case IFF_RUNNING:
+
+ mpc5200_fec_off(self);
+
+ break;
+
+ case IFF_UP:
+
+ mpc5200_fec_init(self);
+
+ break;
+
+ case IFF_UP | IFF_RUNNING:
+
+ mpc5200_fec_off(self);
+ mpc5200_fec_init(self);
+
+ break;
+
+ default:
+ break;
+
+ }
+
+ break;
+
+ case SIOCGIFMEDIA:
+ case SIOCSIFMEDIA:
+ error = rtems_mii_ioctl(&self->mdio, self, command, (int *) data);
+ break;
+
+ case SIO_RTEMS_SHOW_STATS:
+
+ enet_stats(self);
+
+ break;
+
+ /*
+ * FIXME: All sorts of multicast commands need to be added here!
+ */
+ default:
+
+ error = EINVAL;
+
+ break;
+
+ }
+
+ return error;
+
+ }
+
+
+/*
+ * Attach the MPC5200 fec driver to the system
+ */
+static int rtems_mpc5200_fec_driver_attach(struct rtems_bsdnet_ifconfig *config)
+ {
+ mpc5200_fec_context *self;
+ struct ifnet *ifp;
+ int mtu;
+ int unitNumber;
+ char *unitName;
+
+ /*
+ * Parse driver name
+ */
+ if((unitNumber = rtems_bsdnet_parse_driver_name(config, &unitName)) < 0)
+ return 0;
+
+ /*
+ * Is driver free?
+ */
+ if ((unitNumber <= 0) || (unitNumber > NIFACES))
+ {
+
+ printf ("Bad FEC unit number.\n");
+ return 0;
+
+ }
+
+ self = &enet_driver[unitNumber - 1];
+ ifp = &self->arpcom.ac_if;
+
+ if(ifp->if_softc != NULL)
+ {
+
+ printf ("Driver already in use.\n");
+ return 0;
+
+ }
+
+ self->mdio.mdio_r = mpc5200_eth_mii_read;
+ self->mdio.mdio_w = mpc5200_eth_mii_write;
+
+ /*
+ * Process options
+ */
+#if NVRAM_CONFIGURE == 1
+
+ /* Configure from NVRAM */
+ if(addr = nvram->ipaddr)
+ {
+
+ /* We have a non-zero entry, copy the value */
+ if(pAddr = malloc(INET_ADDR_MAX_BUF_SIZE, 0, M_NOWAIT))
+ config->ip_address = (char *)inet_ntop(AF_INET, &addr, pAddr, INET_ADDR_MAX_BUF_SIZE -1);
+ else
+ rtems_panic("Can't allocate ip_address buffer!\n");
+
+ }
+
+ if(addr = nvram->netmask)
+ {
+
+ /* We have a non-zero entry, copy the value */
+ if (pAddr = malloc (INET_ADDR_MAX_BUF_SIZE, 0, M_NOWAIT))
+ config->ip_netmask = (char *)inet_ntop(AF_INET, &addr, pAddr, INET_ADDR_MAX_BUF_SIZE -1);
+ else
+ rtems_panic("Can't allocate ip_netmask buffer!\n");
+
+ }
+
+ /* Ethernet address requires special handling -- it must be copied into
+ * the arpcom struct. The following if construct serves only to give the
+ * User Area NVRAM parameter the highest priority.
+ *
+ * If the ethernet address is specified in NVRAM, go ahead and copy it.
+ * (ETHER_ADDR_LEN = 6 bytes).
+ */
+ if(nvram->enaddr[0] || nvram->enaddr[1] || nvram->enaddr[2])
+ {
+
+ /* Anything in the first three bytes indicates a non-zero entry, copy value */
+ memcpy((void *)self->arpcom.ac_enaddr, &nvram->enaddr, ETHER_ADDR_LEN);
+
+ }
+ else
+ if(config->hardware_address)
+ {
+
+ /* There is no entry in NVRAM, but there is in the ifconfig struct, so use it. */
+ memcpy((void *)self->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN);
+ }
+ else
+ {
+ /* There is no ethernet address provided, so it could be read
+ * from the Ethernet protocol block of SCC1 in DPRAM.
+ */
+ rtems_panic("No Ethernet address specified!\n");
+ }
+
+#else /* NVRAM_CONFIGURE != 1 */
+
+ if(config->hardware_address)
+ {
+
+ memcpy(self->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN);
+
+ }
+ else
+ {
+
+ /* There is no ethernet address provided, so it could be read
+ * from the Ethernet protocol block of SCC1 in DPRAM.
+ */
+ rtems_panic("No Ethernet address specified!\n");
+
+ }
+
+#endif /* NVRAM_CONFIGURE != 1 */
+#ifdef HAS_UBOOT
+ if ((self->arpcom.ac_enaddr[0] == 0) &&
+ (self->arpcom.ac_enaddr[1] == 0) &&
+ (self->arpcom.ac_enaddr[2] == 0)) {
+ memcpy(
+ (void *)self->arpcom.ac_enaddr,
+ bsp_uboot_board_info.bi_enetaddr,
+ ETHER_ADDR_LEN
+ );
+ }
+#endif
+ if(config->mtu)
+ mtu = config->mtu;
+ else
+ mtu = ETHERMTU;
+
+ if(config->rbuf_count)
+ self->rxBdCount = config->rbuf_count;
+ else
+ self->rxBdCount = RX_BUF_COUNT;
+
+ if(config->xbuf_count)
+ self->txBdCount = config->xbuf_count;
+ else
+ self->txBdCount = TX_BUF_COUNT;
+
+ self->acceptBroadcast = !config->ignore_broadcast;
+
+ /*
+ * Set up network interface values
+ */
+ ifp->if_softc = self;
+ ifp->if_unit = unitNumber;
+ ifp->if_name = unitName;
+ ifp->if_mtu = mtu;
+ ifp->if_init = mpc5200_fec_init;
+ ifp->if_ioctl = mpc5200_fec_ioctl;
+ ifp->if_start = mpc5200_fec_tx_start;
+ ifp->if_output = ether_output;
+ ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST;
+ /*ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX;*/
+
+ if(ifp->if_snd.ifq_maxlen == 0)
+ ifp->if_snd.ifq_maxlen = ifqmaxlen;
+
+ /*
+ * Attach the interface
+ */
+ if_attach(ifp);
+
+ ether_ifattach(ifp);
+
+ return 1;
+ }
+
+
+int rtems_mpc5200_fec_driver_attach_detach(struct rtems_bsdnet_ifconfig *config, int attaching)
+{
+ if (attaching) {
+ return rtems_mpc5200_fec_driver_attach(config);
+ }
+ else {
+ return 0;
+ }
+}
+
+