forked from Imagelibrary/rtems
Add and use <machine/rtems-bsd-kernel-space.h> and <machine/rtems-bsd-user-space.h> similar to the libbsd to avoid command line defines and defines scattered throught the code base. Simplify cpukit/libnetworking/Makefile.am. Update #3375.
1581 lines
37 KiB
C
1581 lines
37 KiB
C
/*===============================================================*\
|
|
| 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
|
|
*
|
|
*/
|
|
|
|
#include <machine/rtems-bsd-kernel-space.h>
|
|
|
|
#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, ¶m);
|
|
|
|
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, ¶m);
|
|
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;
|
|
}
|
|
}
|
|
|
|
|