803 lines
25 KiB
C
803 lines
25 KiB
C
/******************************************************************************
|
|
* * *** ***
|
|
* *** *** ***
|
|
* *** **** ********** *** ***** *** **** *****
|
|
* ********* ********** *** ********* ************ *********
|
|
* **** *** *** *** *** **** ***
|
|
* *** *** ****** *** *********** *** **** *****
|
|
* *** *** ****** *** ************* *** **** *****
|
|
* *** **** **** *** *** *** **** ***
|
|
* *** ******* ***** ************** ************* *********
|
|
* *** ***** *** ******* ** ** ****** *****
|
|
* t h e r e a l t i m e t a r g e t e x p e r t s
|
|
*
|
|
* http://www.rt-labs.com
|
|
* Copyright (C) 2007. rt-labs AB, Sweden. All rights reserved.
|
|
*------------------------------------------------------------------------------
|
|
* $Id: fec_ecat.c 138 2014-04-11 09:11:48Z rtlfrm $
|
|
*------------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "fec_ecat.h"
|
|
|
|
#include <dev.h>
|
|
#include <kern.h>
|
|
#include <string.h>
|
|
#include <uassert.h>
|
|
#include <eth/fec_buffer.h>
|
|
#include <eth/phy/mii.h>
|
|
#include <bsp.h>
|
|
#include <config.h>
|
|
|
|
#undef RTK_DEBUG /* Print debugging info? */
|
|
#undef RTK_DEBUG_DATA /* Print packets? */
|
|
|
|
#ifdef RTK_DEBUG
|
|
#define DPRINT(...) rprintp ("FEC: "__VA_ARGS__)
|
|
#else
|
|
#define DPRINT(...)
|
|
#endif /* DEBUG */
|
|
|
|
#ifdef RTK_DEBUG_DATA
|
|
#define DUMP_PACKET(p, l) fec_ecat_dump_packet (p, l)
|
|
#else
|
|
#define DUMP_PACKET(p, l)
|
|
#endif /* RTK_DEBUG_DATA */
|
|
|
|
#define ETHERNET_FCS_SIZE_IN_BYTES 4 /* Frame Check Sequence (32-bit CRC) */
|
|
|
|
#define DELAY(ms) task_delay (tick_from_ms (ms) + 1)
|
|
|
|
#define ETH_PHY_ADDRESS 0x1 /* Default jumper setting on TWR-K60F */
|
|
|
|
#define NUM_BUFFERS 2
|
|
#define FEC_ALLIGNED __attribute__((section(".dma"),aligned(16)))
|
|
|
|
/*
|
|
* "The device's system clock is connected to the module clock,
|
|
* as named in the Ethernet chapter. The minimum system clock frequency
|
|
* for 100 Mbps operation is 25 MHz."
|
|
*
|
|
* - Kinetis K60 manual ch. 3.9.1.1 "Ethernet Clocking Options"
|
|
*/
|
|
#define FEC_MIN_MODULE_CLOCK_Hz (25 * 1000 * 1000)
|
|
|
|
/* Note that the core clock and the system clock has the same clock frequency */
|
|
#define FEC_MODULE_CLOCK_Hz CFG_IPG_CLOCK
|
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
typedef struct fec_mac_t
|
|
{
|
|
uint32_t lower; /* octets 0, 1, 2, 3 */
|
|
uint32_t upper; /* octets 4, 5 */
|
|
} fec_mac_t;
|
|
|
|
/* Message Information Block (MIB) Counters */
|
|
typedef struct fec_mib
|
|
{
|
|
uint32_t rmon_t_drop; /* MBAR_ETH + 0x200 */
|
|
uint32_t rmon_t_packets; /* MBAR_ETH + 0x204 */
|
|
uint32_t rmon_t_bc_pkt; /* MBAR_ETH + 0x208 */
|
|
uint32_t rmon_t_mc_pkt; /* MBAR_ETH + 0x20C */
|
|
uint32_t rmon_t_crc_align; /* MBAR_ETH + 0x210 */
|
|
uint32_t rmon_t_undersize; /* MBAR_ETH + 0x214 */
|
|
uint32_t rmon_t_oversize; /* MBAR_ETH + 0x218 */
|
|
uint32_t rmon_t_frag; /* MBAR_ETH + 0x21C */
|
|
uint32_t rmon_t_jab; /* MBAR_ETH + 0x220 */
|
|
uint32_t rmon_t_col; /* MBAR_ETH + 0x224 */
|
|
uint32_t rmon_t_p64; /* MBAR_ETH + 0x228 */
|
|
uint32_t rmon_t_p65to127; /* MBAR_ETH + 0x22C */
|
|
uint32_t rmon_t_p128to255; /* MBAR_ETH + 0x230 */
|
|
uint32_t rmon_t_p256to511; /* MBAR_ETH + 0x234 */
|
|
uint32_t rmon_t_p512to1023; /* MBAR_ETH + 0x238 */
|
|
uint32_t rmon_t_p1024to2047; /* MBAR_ETH + 0x23C */
|
|
uint32_t rmon_t_p_gte2048; /* MBAR_ETH + 0x240 */
|
|
uint32_t rmon_t_octets; /* MBAR_ETH + 0x244 */
|
|
uint32_t ieee_t_drop; /* MBAR_ETH + 0x248 */
|
|
uint32_t ieee_t_frame_ok; /* MBAR_ETH + 0x24C */
|
|
uint32_t ieee_t_1col; /* MBAR_ETH + 0x250 */
|
|
uint32_t ieee_t_mcol; /* MBAR_ETH + 0x254 */
|
|
uint32_t ieee_t_def; /* MBAR_ETH + 0x258 */
|
|
uint32_t ieee_t_lcol; /* MBAR_ETH + 0x25C */
|
|
uint32_t ieee_t_excol; /* MBAR_ETH + 0x260 */
|
|
uint32_t ieee_t_macerr; /* MBAR_ETH + 0x264 */
|
|
uint32_t ieee_t_cserr; /* MBAR_ETH + 0x268 */
|
|
uint32_t ieee_t_sqe; /* MBAR_ETH + 0x26C */
|
|
uint32_t t_fdxfc; /* MBAR_ETH + 0x270 */
|
|
uint32_t ieee_t_octets_ok; /* MBAR_ETH + 0x274 */
|
|
uint32_t res13[2]; /* MBAR_ETH + 0x278-27C */
|
|
uint32_t rmon_r_drop; /* MBAR_ETH + 0x280 */
|
|
uint32_t rmon_r_packets; /* MBAR_ETH + 0x284 */
|
|
uint32_t rmon_r_bc_pkt; /* MBAR_ETH + 0x288 */
|
|
uint32_t rmon_r_mc_pkt; /* MBAR_ETH + 0x28C */
|
|
uint32_t rmon_r_crc_align; /* MBAR_ETH + 0x290 */
|
|
uint32_t rmon_r_undersize; /* MBAR_ETH + 0x294 */
|
|
uint32_t rmon_r_oversize; /* MBAR_ETH + 0x298 */
|
|
uint32_t rmon_r_frag; /* MBAR_ETH + 0x29C */
|
|
uint32_t rmon_r_jab; /* MBAR_ETH + 0x2A0 */
|
|
uint32_t rmon_r_resvd_0; /* MBAR_ETH + 0x2A4 */
|
|
uint32_t rmon_r_p64; /* MBAR_ETH + 0x2A8 */
|
|
uint32_t rmon_r_p65to127; /* MBAR_ETH + 0x2AC */
|
|
uint32_t rmon_r_p128to255; /* MBAR_ETH + 0x2B0 */
|
|
uint32_t rmon_r_p256to511; /* MBAR_ETH + 0x2B4 */
|
|
uint32_t rmon_r_p512to1023; /* MBAR_ETH + 0x2B8 */
|
|
uint32_t rmon_r_p1024to2047; /* MBAR_ETH + 0x2BC */
|
|
uint32_t rmon_r_p_gte2048; /* MBAR_ETH + 0x2C0 */
|
|
uint32_t rmon_r_octets; /* MBAR_ETH + 0x2C4 */
|
|
uint32_t ieee_r_drop; /* MBAR_ETH + 0x2C8 */
|
|
uint32_t ieee_r_frame_ok; /* MBAR_ETH + 0x2CC */
|
|
uint32_t ieee_r_crc; /* MBAR_ETH + 0x2D0 */
|
|
uint32_t ieee_r_align; /* MBAR_ETH + 0x2D4 */
|
|
uint32_t r_macerr; /* MBAR_ETH + 0x2D8 */
|
|
uint32_t r_fdxfc; /* MBAR_ETH + 0x2DC */
|
|
uint32_t ieee_r_octets_ok; /* MBAR_ETH + 0x2E0 */
|
|
uint32_t res14[7]; /* MBAR_ETH + 0x2E4-2FC */
|
|
} fec_mib_t;
|
|
|
|
COMPILETIME_ASSERT (sizeof (fec_mib_t) == 0x100);
|
|
|
|
/* Register read/write struct */
|
|
typedef struct reg_fec
|
|
{
|
|
uint32_t resv0;
|
|
uint32_t eir;
|
|
uint32_t eimr;
|
|
uint32_t resv1;
|
|
uint32_t rdar;
|
|
uint32_t tdar;
|
|
uint32_t resv2[3];
|
|
uint32_t ecr;
|
|
uint32_t resv3[6];
|
|
uint32_t mmfr;
|
|
uint32_t mscr;
|
|
uint32_t resv4[7];
|
|
uint32_t mibc;
|
|
uint32_t resv5[7];
|
|
uint32_t rcr;
|
|
uint32_t resv6[15];
|
|
uint32_t tcr;
|
|
uint32_t resv7[7];
|
|
fec_mac_t pa; /* Physical address. PALR/PAUR */
|
|
uint32_t opd;
|
|
uint32_t resv8[10];
|
|
uint32_t iaur;
|
|
uint32_t ialr;
|
|
uint32_t gaur;
|
|
uint32_t galr;
|
|
uint32_t resv9[7];
|
|
uint32_t tfwr;
|
|
uint32_t resv10;
|
|
uint32_t frbr;
|
|
uint32_t frsr;
|
|
uint32_t resv11[11];
|
|
uint32_t erdsr;
|
|
uint32_t etdsr;
|
|
uint32_t emrbr;
|
|
uint32_t resv14;
|
|
uint32_t rx_section_full; /* Not present on all controllers */
|
|
uint32_t rx_section_empty; /* Not present on all controllers */
|
|
uint32_t rx_almost_empty; /* Not present on all controllers */
|
|
uint32_t rx_almost_full; /* Not present on all controllers */
|
|
uint32_t tx_section_empty; /* Not present on all controllers */
|
|
uint32_t tx_almost_empty; /* Not present on all controllers */
|
|
uint32_t tx_almost_full; /* Not present on all controllers */
|
|
uint32_t resv12[21];
|
|
const fec_mib_t mib;
|
|
uint32_t fec_miigsk_cfgr; /* Not present on all controllers */
|
|
uint32_t resv13;
|
|
uint32_t fec_miigsk_enr; /* Not present on all controllers */
|
|
uint32_t resv15[0x7d];
|
|
fec_mac_t smac[4]; /* Not present on all controllers */
|
|
} reg_fec_t;
|
|
|
|
COMPILETIME_ASSERT (offsetof (reg_fec_t, emrbr) == 0x188);
|
|
COMPILETIME_ASSERT (offsetof (reg_fec_t, tx_almost_full) == 0x1A8);
|
|
COMPILETIME_ASSERT (offsetof (reg_fec_t, fec_miigsk_cfgr) == 0x300);
|
|
COMPILETIME_ASSERT (offsetof (reg_fec_t, smac) == 0x500);
|
|
COMPILETIME_ASSERT (offsetof (reg_fec_t, smac[3]) == 0x518);
|
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
/* Bit definitions and macros for FEC_EIR */
|
|
#define FEC_EIR_CLEAR_ALL (0xFFF80000)
|
|
#define FEC_EIR_HBERR (0x80000000)
|
|
#define FEC_EIR_BABR (0x40000000)
|
|
#define FEC_EIR_BABT (0x20000000)
|
|
#define FEC_EIR_GRA (0x10000000)
|
|
#define FEC_EIR_TXF (0x08000000)
|
|
#define FEC_EIR_TXB (0x04000000)
|
|
#define FEC_EIR_RXF (0x02000000)
|
|
#define FEC_EIR_RXB (0x01000000)
|
|
#define FEC_EIR_MII (0x00800000)
|
|
#define FEC_EIR_EBERR (0x00400000)
|
|
#define FEC_EIR_LC (0x00200000)
|
|
#define FEC_EIR_RL (0x00100000)
|
|
#define FEC_EIR_UN (0x00080000)
|
|
|
|
/* Bit definitions and macros for FEC_RDAR */
|
|
#define FEC_RDAR_R_DES_ACTIVE (0x01000000)
|
|
|
|
/* Bit definitions and macros for FEC_TDAR */
|
|
#define FEC_TDAR_X_DES_ACTIVE (0x01000000)
|
|
|
|
/* Bit definitions and macros for FEC_ECR */
|
|
#define FEC_ECR_DBSWP (0x00000100)
|
|
#define FEC_ECR_ETHER_EN (0x00000002)
|
|
#define FEC_ECR_RESET (0x00000001)
|
|
|
|
/* Bit definitions and macros for FEC_MMFR */
|
|
#define FEC_MMFR_DATA(x) (((x)&0xFFFF))
|
|
#define FEC_MMFR_ST(x) (((x)&0x03)<<30)
|
|
#define FEC_MMFR_ST_01 (0x40000000)
|
|
#define FEC_MMFR_OP_RD (0x20000000)
|
|
#define FEC_MMFR_OP_WR (0x10000000)
|
|
#define FEC_MMFR_PA(x) (((x)&0x1F)<<23)
|
|
#define FEC_MMFR_RA(x) (((x)&0x1F)<<18)
|
|
#define FEC_MMFR_TA(x) (((x)&0x03)<<16)
|
|
#define FEC_MMFR_TA_10 (0x00020000)
|
|
|
|
/* Bit definitions and macros for FEC_MSCR */
|
|
#define FEC_MSCR_DIS_PREAMBLE (0x00000080)
|
|
#define FEC_MSCR_MII_SPEED(x) (((x)&0x3F)<<1)
|
|
|
|
/* Bit definitions and macros for FEC_MIBC */
|
|
#define FEC_MIBC_MIB_DISABLE (0x80000000)
|
|
#define FEC_MIBC_MIB_IDLE (0x40000000)
|
|
|
|
/* Bit definitions and macros for FEC_RCR */
|
|
#define FEC_RCR_GRS (0x80000000)
|
|
#define FEC_RCR_NO_LGTH_CHECK (0x40000000)
|
|
#define FEC_RCR_MAX_FL(x) (((x)&0x7FF)<<16)
|
|
#define FEC_RCR_CNTL_FRM_ENA (0x00008000)
|
|
#define FEC_RCR_CRC_FWD (0x00004000)
|
|
#define FEC_RCR_PAUSE_FWD (0x00002000)
|
|
#define FEC_RCR_PAD_EN (0x00001000)
|
|
#define FEC_RCR_RMII_ECHO (0x00000800)
|
|
#define FEC_RCR_RMII_LOOP (0x00000400)
|
|
#define FEC_RCR_RMII_10T (0x00000200)
|
|
#define FEC_RCR_RMII_MODE (0x00000100)
|
|
#define FEC_RCR_SGMII_ENA (0x00000080)
|
|
#define FEC_RCR_RGMII_ENA (0x00000040)
|
|
#define FEC_RCR_FCE (0x00000020)
|
|
#define FEC_RCR_BC_REJ (0x00000010)
|
|
#define FEC_RCR_PROM (0x00000008)
|
|
#define FEC_RCR_MII_MODE (0x00000004)
|
|
#define FEC_RCR_DRT (0x00000002)
|
|
#define FEC_RCR_LOOP (0x00000001)
|
|
|
|
/* Bit definitions and macros for FEC_TCR */
|
|
#define FEC_TCR_RFC_PAUSE (0x00000010)
|
|
#define FEC_TCR_TFC_PAUSE (0x00000008)
|
|
#define FEC_TCR_FDEN (0x00000004)
|
|
#define FEC_TCR_HBC (0x00000002)
|
|
#define FEC_TCR_GTS (0x00000001)
|
|
|
|
/* Bit definitions and macros for FEC_PAUR */
|
|
#define FEC_PAUR_PADDR2(x) (((x)&0xFFFF)<<16)
|
|
#define FEC_PAUR_TYPE(x) ((x)&0xFFFF)
|
|
|
|
/* Bit definitions and macros for FEC_OPD */
|
|
#define FEC_OPD_PAUSE_DUR(x) (((x)&0x0000FFFF)<<0)
|
|
#define FEC_OPD_OPCODE(x) (((x)&0x0000FFFF)<<16)
|
|
|
|
/* Bit definitions and macros for FEC_TFWR */
|
|
#define FEC_TFWR_STR_FWD BIT (8) /* Present on ENET but not on FEC */
|
|
#define FEC_TFWR_X_WMRK(x) ((x)&0x03)
|
|
#define FEC_TFWR_X_WMRK_64 (0x01)
|
|
#define FEC_TFWR_X_WMRK_128 (0x02)
|
|
#define FEC_TFWR_X_WMRK_192 (0x03)
|
|
|
|
/* Bit definitions and macros for FEC_FRBR */
|
|
#define FEC_FRBR_R_BOUND(x) (((x)&0xFF)<<2)
|
|
|
|
/* Bit definitions and macros for FEC_FRSR */
|
|
#define FEC_FRSR_R_FSTART(x) (((x)&0xFF)<<2)
|
|
|
|
/* Bit definitions and macros for FEC_ERDSR */
|
|
#define FEC_ERDSR_R_DES_START(x) (((x)&0x3FFFFFFF)<<2)
|
|
|
|
/* Bit definitions and macros for FEC_ETDSR */
|
|
#define FEC_ETDSR_X_DES_START(x) (((x)&0x3FFFFFFF)<<2)
|
|
|
|
/* Bit definitions and macros for FEC_EMRBR */
|
|
#define FEC_EMRBR_R_BUF_SIZE(x) (((x)&0x7F)<<4)
|
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
/* the defines of MII operation */
|
|
#define FEC_MII_ST 0x40000000
|
|
#define FEC_MII_OP_OFF 28
|
|
#define FEC_MII_OP_MASK 0x03
|
|
#define FEC_MII_OP_RD 0x02
|
|
#define FEC_MII_OP_WR 0x01
|
|
#define FEC_MII_PA_OFF 23
|
|
#define FEC_MII_PA_MASK 0xFF
|
|
#define FEC_MII_RA_OFF 18
|
|
#define FEC_MII_RA_MASK 0xFF
|
|
#define FEC_MII_TA 0x00020000
|
|
#define FEC_MII_DATA_OFF 0
|
|
#define FEC_MII_DATA_MASK 0x0000FFFF
|
|
|
|
#define FEC_MII_FRAME (FEC_MII_ST | FEC_MII_TA)
|
|
#define FEC_MII_OP(x) (((x) & FEC_MII_OP_MASK) << FEC_MII_OP_OFF)
|
|
#define FEC_MII_PA(pa) (((pa) & FEC_MII_PA_MASK) << FEC_MII_PA_OFF)
|
|
#define FEC_MII_RA(ra) (((ra) & FEC_MII_RA_MASK) << FEC_MII_RA_OFF)
|
|
#define FEC_MII_SET_DATA(v) (((v) & FEC_MII_DATA_MASK) << FEC_MII_DATA_OFF)
|
|
#define FEC_MII_GET_DATA(v) (((v) >> FEC_MII_DATA_OFF) & FEC_MII_DATA_MASK)
|
|
#define FEC_MII_READ(pa, ra) ((FEC_MII_FRAME | FEC_MII_OP(FEC_MII_OP_RD)) |\
|
|
FEC_MII_PA(pa) | FEC_MII_RA(ra))
|
|
#define FEC_MII_WRITE(pa, ra, v) (FEC_MII_FRAME | FEC_MII_OP(FEC_MII_OP_WR)|\
|
|
FEC_MII_PA(pa) | FEC_MII_RA(ra) | FEC_MII_SET_DATA(v))
|
|
|
|
#define FEC_MII_TIMEOUT 10 // 1 * 10 ms
|
|
#define FEC_MII_TICK 1 // 1 ms
|
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
/* Ethernet Transmit and Receive Buffers */
|
|
#define PKT_MAXBUF_SIZE 1518
|
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
typedef enum fec_phy_inteface
|
|
{
|
|
/**
|
|
* MII, Media Independent Interface
|
|
* 4-bit operating at 25 Mhz
|
|
*/
|
|
FEC_PHY_MII,
|
|
/**
|
|
* RMII, Reduced Media Independent Interface
|
|
* 2-bit operating at 50 Mhz
|
|
*/
|
|
FEC_PHY_RMII,
|
|
FEC_PHY_RGMII
|
|
} fec_phy_inteface_t;
|
|
|
|
typedef struct fec_cfg
|
|
{
|
|
uint32_t base;
|
|
uint32_t clock;
|
|
fec_buffer_bd_t * tx_bd_base;
|
|
fec_buffer_bd_t * rx_bd_base;
|
|
fec_phy_inteface_t phy_interface;
|
|
} fec_cfg_t;
|
|
|
|
typedef struct fec
|
|
{
|
|
uint32_t clock;
|
|
fec_buffer_bd_t * tx_bd_base;
|
|
fec_buffer_bd_t * rx_bd_base;
|
|
volatile reg_fec_t *base;
|
|
phy_t * phy;
|
|
fec_phy_inteface_t phy_interface;
|
|
} fec_t;
|
|
|
|
/* Buffer descriptors. */
|
|
static fec_buffer_bd_t fec_tx_bd[NUM_BUFFERS] FEC_ALLIGNED;
|
|
static fec_buffer_bd_t fec_rx_bd[NUM_BUFFERS] FEC_ALLIGNED;
|
|
|
|
/* Data buffers. */
|
|
static uint8_t fec_tx_data[NUM_BUFFERS * TX_BUFFER_SIZE] FEC_ALLIGNED;
|
|
static uint8_t fec_rx_data[NUM_BUFFERS * RX_BUFFER_SIZE] FEC_ALLIGNED;
|
|
|
|
static fec_t *fec;
|
|
|
|
//----------------------------------------------------------------------------//
|
|
|
|
#ifdef RTK_DEBUG
|
|
const char * fec_ecat_link_duplex_name (uint8_t link_state)
|
|
{
|
|
if (link_state & PHY_LINK_OK)
|
|
{
|
|
if (link_state & PHY_LINK_FULL_DUPLEX)
|
|
{
|
|
return "full duplex";
|
|
}
|
|
else
|
|
{
|
|
return "half duplex";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return "no duplex (link down)";
|
|
}
|
|
}
|
|
|
|
const char * fec_ecat_link_speed_name (uint8_t link_state)
|
|
{
|
|
if (link_state & PHY_LINK_OK)
|
|
{
|
|
if (link_state & PHY_LINK_10MBIT)
|
|
{
|
|
return "10 Mbps";
|
|
}
|
|
else if (link_state & PHY_LINK_100MBIT)
|
|
{
|
|
return "100 Mbps";
|
|
}
|
|
else if (link_state & PHY_LINK_1000MBIT)
|
|
{
|
|
return "1 Gbps";
|
|
}
|
|
else
|
|
{
|
|
return "unknown speed";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return "0 Mbps (link down)";
|
|
}
|
|
}
|
|
#endif /* RTK_DEBUG */
|
|
|
|
#ifdef RTK_DEBUG_DATA
|
|
#include <ctype.h>
|
|
static void fec_ecat_dump_packet (const uint8_t * payload, size_t len)
|
|
{
|
|
size_t i, j, n;
|
|
char s[80];
|
|
|
|
ASSERT (payload != NULL);
|
|
|
|
for (i = 0; i < len; i += 16)
|
|
{
|
|
n = 0;
|
|
for (j = 0; j < 16 && (i + j) < len; j++)
|
|
{
|
|
ASSERT (n <= sizeof(s));
|
|
n += rsnprintf (s + n, sizeof(s) - n, "%02x ", payload[i + j]);
|
|
}
|
|
for (; j < 16; j++)
|
|
{
|
|
ASSERT (n <= sizeof(s));
|
|
n += rsnprintf (s + n, sizeof(s) - n, " ");
|
|
}
|
|
ASSERT (n <= sizeof(s));
|
|
n += rsnprintf (s + n, sizeof(s) - n, "|");
|
|
for (j = 0; j < 16 && (i + j) < len; j++)
|
|
{
|
|
uint8_t c = payload[i + j];
|
|
c = (isprint (c)) ? c : '.';
|
|
ASSERT (n <= sizeof(s));
|
|
n += rsnprintf (s + n, sizeof(s) - n, "%c", c);
|
|
}
|
|
ASSERT (n <= sizeof(s));
|
|
n += rsnprintf (s + n, sizeof(s) - n, "|\n");
|
|
ASSERT (n <= sizeof(s));
|
|
DPRINT ("%s", s);
|
|
}
|
|
}
|
|
#endif /* DEBUG_DATA */
|
|
|
|
static uint16_t fec_ecat_read_phy (void * arg, uint8_t address, uint8_t reg)
|
|
{
|
|
fec->base->eir = FEC_EIR_MII; // Clear interrupt.
|
|
fec->base->mmfr = FEC_MII_READ(address, reg); // Write read command.
|
|
while (!(fec->base->eir & FEC_EIR_MII))
|
|
{
|
|
; // Wait for interrupt.
|
|
}
|
|
fec->base->eir = FEC_EIR_MII; // Clear interrupt.
|
|
uint16_t data = FEC_MII_GET_DATA(fec->base->mmfr);
|
|
return data; // Return read data.
|
|
}
|
|
|
|
static void fec_ecat_write_phy (void * arg, uint8_t address, uint8_t reg,
|
|
uint16_t value)
|
|
{
|
|
fec->base->eir = FEC_EIR_MII; // Clear interrupt.
|
|
fec->base->mmfr = FEC_MII_WRITE(address, reg, value); // Write data.
|
|
while (!(fec->base->eir & FEC_EIR_MII))
|
|
{
|
|
; // Wait for interrupt.
|
|
}
|
|
fec->base->eir = FEC_EIR_MII; // Clear interrupt.
|
|
}
|
|
|
|
static void fec_ecat_init_hw (const fec_mac_address_t * mac_address)
|
|
{
|
|
uint32_t mii_speed;
|
|
|
|
/* Hard reset */
|
|
fec->base->ecr = FEC_ECR_RESET;
|
|
while (fec->base->ecr & FEC_ECR_RESET)
|
|
{
|
|
;
|
|
}
|
|
|
|
// Configure MDC clock.
|
|
mii_speed = (fec->clock + 499999) / 5000000;
|
|
fec->base->mscr = (fec->base->mscr & (~0x7E)) | (mii_speed << 1);
|
|
|
|
// Receive control register
|
|
fec->base->rcr = FEC_RCR_MAX_FL(PKT_MAXBUF_SIZE) | FEC_RCR_MII_MODE;
|
|
|
|
// set RMII mode in RCR register.
|
|
if (fec->phy_interface == FEC_PHY_RMII)
|
|
{
|
|
fec->base->rcr |= FEC_RCR_RMII_MODE;
|
|
}
|
|
else if(fec->phy_interface == FEC_PHY_RGMII)
|
|
{
|
|
fec->base->rcr &= ~(FEC_RCR_RMII_MODE | FEC_RCR_MII_MODE );
|
|
fec->base->rcr |= (FEC_RCR_RGMII_ENA | FEC_RCR_MII_MODE);
|
|
}
|
|
|
|
// Reset phy
|
|
if (fec->phy->ops->reset)
|
|
{
|
|
fec->phy->ops->reset (fec->phy);
|
|
}
|
|
|
|
/* Don't receive any unicast frames except those whose destination address
|
|
* equals our physical address.
|
|
*/
|
|
fec->base->iaur = 0;
|
|
fec->base->ialr = 0;
|
|
|
|
/* Receive all multicast frames. */
|
|
fec->base->gaur = UINT32_MAX;
|
|
fec->base->galr = UINT32_MAX;
|
|
|
|
/* Set our physical address. */
|
|
fec->base->pa.lower = (mac_address->octet[0] << 24) +
|
|
(mac_address->octet[1] << 16) +
|
|
(mac_address->octet[2] << 8) +
|
|
(mac_address->octet[3] << 0);
|
|
fec->base->pa.upper = (mac_address->octet[4] << 24) +
|
|
(mac_address->octet[5] << 16) +
|
|
0x8808;
|
|
|
|
/* Start link autonegotiation */
|
|
fec->phy->ops->start (fec->phy);
|
|
}
|
|
|
|
int fec_ecat_send (const void *payload, size_t tot_len)
|
|
{
|
|
fec_buffer_bd_t * bd;
|
|
|
|
/* Frames larger than the maximum Ethernet frame size are not allowed. */
|
|
ASSERT (tot_len <= PKT_MAXBUF_SIZE);
|
|
ASSERT (tot_len <= TX_BUFFER_SIZE);
|
|
|
|
/* Bus errors should never occur, unless the MPU is enabled and forbids
|
|
* the Ethernet MAC DMA from accessing the descriptors or buffers.
|
|
*/
|
|
ASSERT ((fec->base->eir & FEC_EIR_EBERR) == false);
|
|
|
|
/* Allocate a transmit buffer. We wait here until the MAC has released at
|
|
* least one buffer. This could take a couple of microseconds.
|
|
*/
|
|
bd = NULL;
|
|
while (bd == NULL)
|
|
{
|
|
bd = fec_buffer_get_tx ();
|
|
}
|
|
|
|
DPRINT ("out (%u):\n", tot_len);
|
|
DUMP_PACKET (payload, tot_len);
|
|
|
|
/* Copy frame to allocated buffer */
|
|
memcpy (bd->data, payload, tot_len);
|
|
bd->length = tot_len;
|
|
|
|
fec_buffer_produce_tx (bd);
|
|
|
|
/* Wait for previous transmissions to complete.
|
|
*
|
|
* This is a workaround for Freescale Kinetis errata e6358.
|
|
* See "Mask Set Errata for Mask 3N96B".
|
|
*/
|
|
while (fec->base->tdar)
|
|
{
|
|
;
|
|
}
|
|
|
|
/* Transmit frame */
|
|
fec->base->tdar = 1;
|
|
|
|
return tot_len;
|
|
}
|
|
|
|
int fec_ecat_recv (void * buffer, size_t buffer_length)
|
|
{
|
|
fec_buffer_bd_t * bd;
|
|
int return_value;
|
|
size_t frame_length_without_fcs;
|
|
|
|
/* Bus errors should never occur, unless the MPU is enabled and forbids
|
|
* the Ethernet MAC DMA from accessing the descriptors or buffers.
|
|
*/
|
|
ASSERT ((fec->base->eir & FEC_EIR_EBERR) == false);
|
|
|
|
bd = fec_buffer_get_rx ();
|
|
if (bd == NULL)
|
|
{
|
|
/* No frame received. Not an error. */
|
|
return_value = 0;
|
|
return return_value;
|
|
}
|
|
|
|
/* The FCS CRC should not be returned to user, so subtract that. */
|
|
frame_length_without_fcs = bd->length - ETHERNET_FCS_SIZE_IN_BYTES;
|
|
|
|
/* A frame was received. Handle it and then return its buffer to hardware */
|
|
if ((bd->status & BD_RX_L)== 0){
|
|
/* Buffer is not last buffer in frame. This really should never
|
|
* happen as our buffers are large enough to contain any
|
|
* Ethernet frame. Nevertheless, is has been observed to happen.
|
|
* Drop the packet and hope for the best.
|
|
*/
|
|
DPRINT ("recv(): End of frame not found. Status: 0x%x\n", bd->status);
|
|
return_value = -1;
|
|
}
|
|
else if (bd->status & (BD_RX_LG | BD_RX_NO | BD_RX_CR | BD_RX_OV))
|
|
{
|
|
DPRINT ("recv(): Frame is damaged. Status: 0x%x\n", bd->status);
|
|
return_value = -1;
|
|
}
|
|
else if (buffer_length >= frame_length_without_fcs)
|
|
{
|
|
/* No errors detected, so frame should be valid. */
|
|
ASSERT (frame_length_without_fcs > 0);
|
|
|
|
memcpy (buffer, bd->data, frame_length_without_fcs);
|
|
DPRINT ("in (%u):\n", frame_length_without_fcs);
|
|
DUMP_PACKET (buffer, frame_length_without_fcs);
|
|
return_value = frame_length_without_fcs;
|
|
}
|
|
else
|
|
{
|
|
DPRINT ("received_frame: User buffer is too small.\n");
|
|
return_value = -1;
|
|
}
|
|
|
|
/* Tell the HW that there are new free RX buffers. */
|
|
fec_buffer_produce_rx (bd);
|
|
fec->base->rdar = 1;
|
|
|
|
return return_value;
|
|
}
|
|
|
|
static void fec_ecat_hotplug (void)
|
|
{
|
|
uint8_t link_state = 0;
|
|
|
|
/* Disable frame reception/transmission */
|
|
fec->base->ecr &= ~FEC_ECR_ETHER_EN;
|
|
|
|
/* Set duplex mode according to link state */
|
|
|
|
link_state = fec->phy->ops->get_link_state (fec->phy);
|
|
|
|
/* Set duplex */
|
|
if (link_state & PHY_LINK_FULL_DUPLEX)
|
|
{
|
|
fec->base->tcr |= FEC_TCR_FDEN;
|
|
fec->base->rcr &= ~FEC_RCR_DRT;
|
|
}
|
|
else
|
|
{
|
|
fec->base->tcr &= ~FEC_TCR_FDEN;
|
|
fec->base->rcr |= FEC_RCR_DRT;
|
|
}
|
|
|
|
/* Set RMII 10-Mbps mode. */
|
|
if (link_state & PHY_LINK_10MBIT)
|
|
{
|
|
fec->base->rcr |= FEC_RCR_RMII_10T;
|
|
}
|
|
else
|
|
{
|
|
fec->base->rcr &= ~FEC_RCR_RMII_10T;
|
|
}
|
|
|
|
/* Clear any pending interrupt */
|
|
fec->base->eir = 0xffffffff;
|
|
|
|
// Disable all interrupts.
|
|
fec->base->eimr = 0x0;
|
|
|
|
/* Configure the amount of data required in the
|
|
* transmit FIFO before transmission of a frame can begin.
|
|
*/
|
|
/* All data in frame (i.e. store-and-forward mode). */
|
|
fec->base->tfwr = FEC_TFWR_STR_FWD;
|
|
|
|
// Reset buffers.
|
|
fec_buffer_reset ();
|
|
|
|
// FEC_ERDSR - Receive buffer descriptor ring start register
|
|
fec->base->erdsr = (uint32_t)fec->rx_bd_base;
|
|
|
|
// FEC_ETDSR - Transmit buffer descriptor ring start register
|
|
fec->base->etdsr = (uint32_t)fec->tx_bd_base;
|
|
|
|
// FEC_EMRBR - Maximum receive buffer size register
|
|
fec->base->emrbr = RX_BUFFER_SIZE - 1;
|
|
|
|
/* Let controller count frames. Useful for debugging */
|
|
fec->base->mibc = 0x0;
|
|
|
|
/* Now enable the transmit and receive processing.
|
|
*
|
|
* Also let MAC hardware convert DMA descriptor fields between little endian
|
|
* (as accessed by ARM core) and big endian (as used internally by the
|
|
* MAC hardware. Software would otherwise need to do the conversions.
|
|
*/
|
|
fec->base->ecr = FEC_ECR_ETHER_EN | FEC_ECR_DBSWP;
|
|
|
|
/* Indicate that there have been empty receive buffers produced */
|
|
// FEC_RDAR - Receive Descriptor ring - Receive descriptor active register
|
|
fec->base->rdar = 1;
|
|
|
|
DPRINT ("Link up. Speed: %s. Mode: %s.\n", fec_ecat_link_speed_name (link_state),
|
|
fec_ecat_link_duplex_name (link_state));
|
|
}
|
|
|
|
static dev_state_t fec_ecat_probe (void)
|
|
{
|
|
uint8_t link_state;
|
|
|
|
link_state = fec->phy->ops->get_link_state (fec->phy);
|
|
|
|
return (link_state & PHY_LINK_OK) ? StateAttached : StateDetached;
|
|
}
|
|
|
|
COMPILETIME_ASSERT (FEC_MODULE_CLOCK_Hz >= FEC_MIN_MODULE_CLOCK_Hz);
|
|
|
|
int fec_ecat_init (const fec_mac_address_t * mac_address,
|
|
bool phy_loopback_mode)
|
|
{
|
|
dev_state_t state;
|
|
static const fec_cfg_t eth_cfg =
|
|
{
|
|
.base = ENET_BASE,
|
|
.clock = FEC_MODULE_CLOCK_Hz,
|
|
.tx_bd_base = fec_tx_bd,
|
|
.rx_bd_base = fec_rx_bd,
|
|
.phy_interface = FEC_PHY_RMII,
|
|
};
|
|
static phy_cfg_t phy_cfg =
|
|
{
|
|
.address = ETH_PHY_ADDRESS,
|
|
.read = NULL, /* Set by MAC driver */
|
|
.write = NULL, /* Set by MAC driver */
|
|
};
|
|
|
|
phy_cfg.loopback_mode = phy_loopback_mode;
|
|
|
|
/* Initialise buffers and buffer descriptors.*/
|
|
fec_buffer_init_tx (fec_tx_bd, fec_tx_data, NUM_BUFFERS);
|
|
fec_buffer_init_rx (fec_rx_bd, fec_rx_data, NUM_BUFFERS);
|
|
|
|
fec = malloc (sizeof (fec_t));
|
|
UASSERT (fec != NULL, EMEM);
|
|
|
|
/* Initialise driver state */
|
|
fec->rx_bd_base = eth_cfg.rx_bd_base;
|
|
fec->tx_bd_base = eth_cfg.tx_bd_base;
|
|
fec->clock = eth_cfg.clock;
|
|
fec->base = (reg_fec_t *)eth_cfg.base;
|
|
fec->phy_interface = eth_cfg.phy_interface;
|
|
fec->phy = mii_init (&phy_cfg);
|
|
fec->phy->arg = fec;
|
|
fec->phy->read = fec_ecat_read_phy;
|
|
fec->phy->write = fec_ecat_write_phy;
|
|
|
|
/* Initialize hardware */
|
|
fec_ecat_init_hw (mac_address);
|
|
state = StateDetached;
|
|
while (state == StateDetached)
|
|
{
|
|
state = fec_ecat_probe ();
|
|
}
|
|
fec_ecat_hotplug ();
|
|
|
|
return 0;
|
|
}
|