diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-23 09:53:31 +0200 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2018-04-23 15:18:44 +0200 |
commit | 031df3914990db0336a0d386fb53558b05de467e (patch) | |
tree | 4661e22f0cdb3f9d06879f0194b77c75f62bac79 /bsps/powerpc/gen5200/net/network.c | |
parent | bsps: Move interrupt controller support to bsps (diff) | |
download | rtems-031df3914990db0336a0d386fb53558b05de467e.tar.bz2 |
bsps: Move legacy network drivers to bsps
This patch is a part of the BSP source reorganization.
Update #3285.
Diffstat (limited to 'bsps/powerpc/gen5200/net/network.c')
-rw-r--r-- | bsps/powerpc/gen5200/net/network.c | 1581 |
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, ¶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; + } +} + + |