summaryrefslogtreecommitdiffstats
path: root/arch/m68k/mach-mcfv4e/fec.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/m68k/mach-mcfv4e/fec.c')
-rw-r--r--arch/m68k/mach-mcfv4e/fec.c1440
1 files changed, 1440 insertions, 0 deletions
diff --git a/arch/m68k/mach-mcfv4e/fec.c b/arch/m68k/mach-mcfv4e/fec.c
new file mode 100644
index 0000000000..551f00f005
--- /dev/null
+++ b/arch/m68k/mach-mcfv4e/fec.c
@@ -0,0 +1,1440 @@
+/*
+ * File: fec.c
+ * Purpose: Driver for the Fast Ethernet Controller (FEC)
+ *
+ * Notes:
+ */
+#include <common.h>
+#include <linux/types.h>
+
+#include <asm/arch/mcf54xx-regs.h>
+#include <asm/proc/mcdapi/MCD_dma.h>
+#include <asm/proc/net/net.h>
+#include <asm/proc/fecbd.h>
+#include <asm/proc/fec.h>
+#include <asm/proc/dma_utils.h>
+
+
+#define TRUE 1
+#define FALSE 0
+#define ASSERT(x) if (!(x)) hang();
+#define nop() __asm__ __volatile__("nop\n")
+
+
+FEC_EVENT_LOG fec_log[2];
+
+/*
+ * Write a value to a PHY's MII register.
+ *
+ * Parameters:
+ * ch FEC channel
+ * phy_addr Address of the PHY.
+ * reg_addr Address of the register in the PHY.
+ * data Data to be written to the PHY register.
+ *
+ * Return Values:
+ * 1 on failure
+ * 0 on success.
+ *
+ * Please refer to your PHY manual for registers and their meanings.
+ * mii_write() polls for the FEC's MII interrupt event (which should
+ * be masked from the interrupt handler) and clears it. If after a
+ * suitable amount of time the event isn't triggered, a value of 0
+ * is returned.
+ */
+int
+fec_mii_write(uint8_t ch, uint8_t phy_addr, uint8_t reg_addr, uint16_t data)
+{
+ int timeout;
+ uint32_t eimr;
+
+ ASSERT(ch == 0 || ch == 1);
+
+ /*
+ * Clear the MII interrupt bit
+ */
+ MCF_FEC_EIR(ch) = MCF_FEC_EIR_MII;
+
+ /*
+ * Write to the MII Management Frame Register to kick-off
+ * the MII write
+ */
+ MCF_FEC_MMFR(ch) = 0
+ | MCF_FEC_MMFR_ST_01
+ | MCF_FEC_MMFR_OP_WRITE
+ | MCF_FEC_MMFR_PA(phy_addr)
+ | MCF_FEC_MMFR_RA(reg_addr)
+ | MCF_FEC_MMFR_TA_10
+ | MCF_FEC_MMFR_DATA(data);
+
+ /*
+ * Mask the MII interrupt
+ */
+ eimr = MCF_FEC_EIMR(ch);
+ MCF_FEC_EIMR(ch) &= ~MCF_FEC_EIMR_MII;
+
+ /*
+ * Poll for the MII interrupt (interrupt should be masked)
+ */
+ for (timeout = 0; timeout < FEC_MII_TIMEOUT; timeout++)
+ {
+ if (MCF_FEC_EIR(ch) & MCF_FEC_EIR_MII)
+ break;
+ }
+ if(timeout == FEC_MII_TIMEOUT)
+ return 1;
+
+ /*
+ * Clear the MII interrupt bit
+ */
+ MCF_FEC_EIR(ch) = MCF_FEC_EIR_MII;
+
+ /*
+ * Restore the EIMR
+ */
+ MCF_FEC_EIMR(ch) = eimr;
+
+ return 0;
+}
+
+/*
+ * Read a value from a PHY's MII register.
+ *
+ * Parameters:
+ * ch FEC channel
+ * phy_addr Address of the PHY.
+ * reg_addr Address of the register in the PHY.
+ * data Pointer to storage for the Data to be read
+ * from the PHY register (passed by reference)
+ *
+ * Return Values:
+ * 1 on failure
+ * 0 on success.
+ *
+ * Please refer to your PHY manual for registers and their meanings.
+ * mii_read() polls for the FEC's MII interrupt event (which should
+ * be masked from the interrupt handler) and clears it. If after a
+ * suitable amount of time the event isn't triggered, a value of 0
+ * is returned.
+ */
+int
+fec_mii_read(uint8_t ch, uint8_t phy_addr, uint8_t reg_addr, uint16_t *data)
+{
+ int timeout;
+
+ ASSERT(ch == 0 || ch == 1);
+
+ /*
+ * Clear the MII interrupt bit
+ */
+ MCF_FEC_EIR(ch) = MCF_FEC_EIR_MII;
+
+ /*
+ * Write to the MII Management Frame Register to kick-off
+ * the MII read
+ */
+ MCF_FEC_MMFR(ch) = 0
+ | MCF_FEC_MMFR_ST_01
+ | MCF_FEC_MMFR_OP_READ
+ | MCF_FEC_MMFR_PA(phy_addr)
+ | MCF_FEC_MMFR_RA(reg_addr)
+ | MCF_FEC_MMFR_TA_10;
+
+ /*
+ * Poll for the MII interrupt (interrupt should be masked)
+ */
+ for (timeout = 0; timeout < FEC_MII_TIMEOUT; timeout++)
+ {
+ if (MCF_FEC_EIR(ch) & MCF_FEC_EIR_MII)
+ break;
+ }
+
+ if(timeout == FEC_MII_TIMEOUT)
+ return 1;
+
+ /*
+ * Clear the MII interrupt bit
+ */
+ MCF_FEC_EIR(ch) = MCF_FEC_EIR_MII;
+
+ *data = (uint16_t)(MCF_FEC_MMFR(ch) & 0x0000FFFF);
+
+ return 0;
+}
+
+/*
+ * Initialize the MII interface controller
+ *
+ * Parameters:
+ * ch FEC channel
+ * sys_clk System Clock Frequency (in MHz)
+ */
+void
+fec_mii_init(uint8_t ch, uint32_t sys_clk)
+{
+ ASSERT(ch == 0 || ch == 1);
+
+ /*
+ * Initialize the MII clock (EMDC) frequency
+ *
+ * Desired MII clock is 2.5MHz
+ * MII Speed Setting = System_Clock / (2.5MHz * 2)
+ * (plus 1 to make sure we round up)
+ */
+ MCF_FEC_MSCR(ch) = MCF_FEC_MSCR_MII_SPEED((sys_clk/5)+1);
+}
+
+/* Initialize the MIB counters
+ *
+ * Parameters:
+ * ch FEC channel
+ */
+void
+fec_mib_init(uint8_t ch)
+{
+ ASSERT(ch == 0 || ch == 1);
+//To do
+}
+
+/* Display the MIB counters
+ *
+ * Parameters:
+ * ch FEC channel
+ */
+void
+fec_mib_dump(uint8_t ch)
+{
+ ASSERT(ch == 0 || ch == 1);
+//To do
+}
+
+/* Initialize the FEC log
+ *
+ * Parameters:
+ * ch FEC channel
+ */
+void
+fec_log_init(uint8_t ch)
+{
+ ASSERT(ch == 0 || ch == 1);
+ memset(&fec_log[ch],0,sizeof(FEC_EVENT_LOG));
+}
+
+/* Display the FEC log
+ *
+ * Parameters:
+ * ch FEC channel
+ */
+void
+fec_log_dump(uint8_t ch)
+{
+ ASSERT(ch == 0 || ch == 1);
+ printf("\n FEC%d Log\n---------------\n",ch);
+ printf("Total: %4d\n",fec_log[ch].total);
+ printf("hberr: %4d\n",fec_log[ch].hberr);
+ printf("babr: %4d\n",fec_log[ch].babr);
+ printf("babt: %4d\n",fec_log[ch].babt);
+ printf("gra: %4d\n",fec_log[ch].gra);
+ printf("txf: %4d\n",fec_log[ch].txf);
+ printf("mii: %4d\n",fec_log[ch].mii);
+ printf("lc: %4d\n",fec_log[ch].lc);
+ printf("rl: %4d\n",fec_log[ch].rl);
+ printf("xfun: %4d\n",fec_log[ch].xfun);
+ printf("xferr: %4d\n",fec_log[ch].xferr);
+ printf("rferr: %4d\n",fec_log[ch].rferr);
+ printf("dtxf: %4d\n",fec_log[ch].dtxf);
+ printf("drxf: %4d\n",fec_log[ch].drxf);
+ printf("\nRFSW:\n");
+ printf("inv: %4d\n",fec_log[ch].rfsw_inv);
+ printf("m: %4d\n",fec_log[ch].rfsw_m);
+ printf("bc: %4d\n",fec_log[ch].rfsw_bc);
+ printf("mc: %4d\n",fec_log[ch].rfsw_mc);
+ printf("lg: %4d\n",fec_log[ch].rfsw_lg);
+ printf("no: %4d\n",fec_log[ch].rfsw_no);
+ printf("cr: %4d\n",fec_log[ch].rfsw_cr);
+ printf("ov: %4d\n",fec_log[ch].rfsw_ov);
+ printf("tr: %4d\n",fec_log[ch].rfsw_tr);
+ printf("---------------\n\n");
+}
+
+/*
+ * Display some of the registers for debugging
+ *
+ * Parameters:
+ * ch FEC channel
+ */
+void
+fec_debug_dump(uint8_t ch)
+{
+ printf("\n------------- FEC%d -------------\n",ch);
+ printf("EIR %08x \n",MCF_FEC_EIR(ch));
+ printf("EIMR %08x \n",MCF_FEC_EIMR(ch));
+ printf("ECR %08x \n",MCF_FEC_ECR(ch));
+ printf("RCR %08x \n",MCF_FEC_RCR(ch));
+ printf("R_HASH %08x \n",MCF_FEC_R_HASH(ch));
+ printf("TCR %08x \n",MCF_FEC_TCR(ch));
+ printf("FECTFWR %08x \n",MCF_FEC_FECTFWR(ch));
+ printf("FECRFSR %08x \n",MCF_FEC_FECRFSR(ch));
+ printf("FECRFCR %08x \n",MCF_FEC_FECRFCR(ch));
+ printf("FECRLRFP %08x \n",MCF_FEC_FECRLRFP(ch));
+ printf("FECRLWFP %08x \n",MCF_FEC_FECRLWFP(ch));
+ printf("FECRFAR %08x \n",MCF_FEC_FECRFAR(ch));
+ printf("FECRFRP %08x \n",MCF_FEC_FECRFRP(ch));
+ printf("FECRFWP %08x \n",MCF_FEC_FECRFWP(ch));
+ printf("FECTFSR %08x \n",MCF_FEC_FECTFSR(ch));
+ printf("FECTFCR %08x \n",MCF_FEC_FECTFCR(ch));
+ printf("FECTLRFP %08x \n",MCF_FEC_FECTLRFP(ch));
+ printf("FECTLWFP %08x \n",MCF_FEC_FECTLWFP(ch));
+ printf("FECTFAR %08x \n",MCF_FEC_FECTFAR(ch));
+ printf("FECTFRP %08x \n",MCF_FEC_FECTFRP(ch));
+ printf("FECTFWP %08x \n",MCF_FEC_FECTFWP(ch));
+ printf("FRST %08x \n",MCF_FEC_FRST(ch));
+ printf("--------------------------------\n\n");
+}
+
+/*
+ * Set the duplex on the selected FEC controller
+ *
+ * Parameters:
+ * ch FEC channel
+ * duplex FEC_MII_FULL_DUPLEX or FEC_MII_HALF_DUPLEX
+ */
+void
+fec_duplex (uint8_t ch, uint8_t duplex)
+{
+ ASSERT(ch == 0 || ch == 1);
+
+ switch (duplex)
+ {
+ case FEC_MII_HALF_DUPLEX:
+ MCF_FEC_RCR(ch) |= MCF_FEC_RCR_DRT;
+ MCF_FEC_TCR(ch) &= (uint32_t)~MCF_FEC_TCR_FDEN;
+ break;
+ case FEC_MII_FULL_DUPLEX:
+ default:
+ MCF_FEC_RCR(ch) &= (uint32_t)~MCF_FEC_RCR_DRT;
+ MCF_FEC_TCR(ch) |= MCF_FEC_TCR_FDEN;
+ break;
+ }
+}
+
+/*
+ * Generate the hash table settings for the given address
+ *
+ * Parameters:
+ * addr 48-bit (6 byte) Address to generate the hash for
+ *
+ * Return Value:
+ * The 6 most significant bits of the 32-bit CRC result
+ */
+uint8_t
+fec_hash_address(const uint8_t *addr)
+{
+ uint32_t crc;
+ uint8_t byte;
+ int i, j;
+
+ crc = 0xFFFFFFFF;
+ for(i=0; i<6; ++i)
+ {
+ byte = addr[i];
+ for(j=0; j<8; ++j)
+ {
+ if((byte & 0x01)^(crc & 0x01))
+ {
+ crc >>= 1;
+ crc = crc ^ 0xEDB88320;
+ }
+ else
+ crc >>= 1;
+ byte >>= 1;
+ }
+ }
+ return (uint8_t)(crc >> 26);
+}
+
+/*
+ * Set the Physical (Hardware) Address and the Individual Address
+ * Hash in the selected FEC
+ *
+ * Parameters:
+ * ch FEC channel
+ * pa Physical (Hardware) Address for the selected FEC
+ */
+void
+fec_set_address (uint8_t ch, const uint8_t *pa)
+{
+ uint8_t crc;
+
+ ASSERT(ch == 0 || ch == 1);
+
+ /*
+ * Set the Physical Address
+ */
+ MCF_FEC_PALR(ch) = (uint32_t)((pa[0]<<24) | (pa[1]<<16) | (pa[2]<<8) | pa[3]);
+ MCF_FEC_PAUR(ch) = (uint32_t)((pa[4]<<24) | (pa[5]<<16));
+
+ /*
+ * Calculate and set the hash for given Physical Address
+ * in the Individual Address Hash registers
+ */
+ crc = fec_hash_address(pa);
+ if(crc >= 32)
+ MCF_FEC_IAUR(ch) |= (uint32_t)(1 << (crc - 32));
+ else
+ MCF_FEC_IALR(ch) |= (uint32_t)(1 << crc);
+}
+
+/*
+ * Reset the selected FEC controller
+ *
+ * Parameters:
+ * ch FEC channel
+ */
+void
+fec_reset (uint8_t ch)
+{
+ int i;
+
+ ASSERT(ch == 0 || ch == 1);
+
+ /* Clear any events in the FIFO status registers */
+ MCF_FEC_FECRFSR(ch) = (0
+ | MCF_FEC_FECRFSR_OF
+ | MCF_FEC_FECRFSR_UF
+ | MCF_FEC_FECRFSR_RXW
+ | MCF_FEC_FECRFSR_FAE
+ | MCF_FEC_FECRFSR_IP);
+ MCF_FEC_FECTFSR(ch) = (0
+ | MCF_FEC_FECRFSR_OF
+ | MCF_FEC_FECRFSR_UF
+ | MCF_FEC_FECRFSR_RXW
+ | MCF_FEC_FECRFSR_FAE
+ | MCF_FEC_FECRFSR_IP);
+
+ /* Reset the FIFOs */
+ MCF_FEC_FRST(ch) |= MCF_FEC_FRST_SW_RST;
+ MCF_FEC_FRST(ch) &= ~MCF_FEC_FRST_SW_RST;
+
+ /* Set the Reset bit and clear the Enable bit */
+ MCF_FEC_ECR(ch) = MCF_FEC_ECR_RESET;
+
+ /* Wait at least 8 clock cycles */
+ for (i=0; i<10; ++i)
+ nop();
+}
+
+/*
+ * Initialize the selected FEC
+ *
+ * Parameters:
+ * ch FEC channel
+ * mode External interface mode (MII, 7-wire, or internal loopback)
+ * pa Physical (Hardware) Address for the selected FEC
+ */
+void
+fec_init (uint8_t ch, uint8_t mode, const uint8_t *pa)
+{
+ ASSERT(ch == 0 || ch == 1);
+
+ /*
+ * Enable all the external interface signals
+ */
+ if (mode == FEC_MODE_7WIRE)
+ {
+ if (ch == 1)
+ MCF_GPIO_PAR_FECI2CIRQ |= MCF_GPIO_PAR_FECI2CIRQ_PAR_E17;
+ else
+ MCF_GPIO_PAR_FECI2CIRQ |= MCF_GPIO_PAR_FECI2CIRQ_PAR_E07;
+ }
+ else if (mode == FEC_MODE_MII)
+ {
+ if (ch == 1)
+ MCF_GPIO_PAR_FECI2CIRQ |= 0
+ | MCF_GPIO_PAR_FECI2CIRQ_PAR_E1MDC_EMDC
+ | MCF_GPIO_PAR_FECI2CIRQ_PAR_E1MDIO_EMDIO
+ | MCF_GPIO_PAR_FECI2CIRQ_PAR_E1MII
+ | MCF_GPIO_PAR_FECI2CIRQ_PAR_E17;
+ else
+ MCF_GPIO_PAR_FECI2CIRQ |= 0
+ | MCF_GPIO_PAR_FECI2CIRQ_PAR_E0MDC
+ | MCF_GPIO_PAR_FECI2CIRQ_PAR_E0MDIO
+ | MCF_GPIO_PAR_FECI2CIRQ_PAR_E0MII
+ | MCF_GPIO_PAR_FECI2CIRQ_PAR_E07;
+ }
+
+ /*
+ * Clear the Individual and Group Address Hash registers
+ */
+ MCF_FEC_IALR(ch) = 0;
+ MCF_FEC_IAUR(ch) = 0;
+ MCF_FEC_GALR(ch) = 0;
+ MCF_FEC_GAUR(ch) = 0;
+
+ /*
+ * Set the Physical Address for the selected FEC
+ */
+ fec_set_address(ch, pa);
+
+ /*
+ * Mask all FEC interrupts
+ */
+ MCF_FEC_EIMR(ch) = MCF_FEC_EIMR_MASK_ALL;
+
+ /*
+ * Clear all FEC interrupt events
+ */
+ MCF_FEC_EIR(ch) = MCF_FEC_EIR_CLEAR_ALL;
+
+ /*
+ * Initialize the Receive Control Register
+ */
+ MCF_FEC_RCR(ch) = 0
+ | MCF_FEC_RCR_MAX_FL(ETH_MAX_FRM)
+ #ifdef FEC_PROMISCUOUS
+ | MCF_FEC_RCR_PROM
+ #endif
+ | MCF_FEC_RCR_FCE;
+
+ if (mode == FEC_MODE_MII)
+ MCF_FEC_RCR(ch) |= MCF_FEC_RCR_MII_MODE;
+
+ else if (mode == FEC_MODE_LOOPBACK)
+ MCF_FEC_RCR(ch) |= MCF_FEC_RCR_LOOP;
+
+ /*
+ * Initialize the Transmit Control Register
+ */
+ MCF_FEC_TCR(ch) = MCF_FEC_TCR_FDEN;
+
+ /*
+ * Set Rx FIFO alarm and granularity
+ */
+ MCF_FEC_FECRFCR(ch) = 0
+ | MCF_FEC_FECRFCR_FRM
+ | MCF_FEC_FECRFCR_RXW_MSK
+ | MCF_FEC_FECRFCR_GR(7);
+ MCF_FEC_FECRFAR(ch) = MCF_FEC_FECRFAR_ALARM(768);
+
+ /*
+ * Set Tx FIFO watermark, alarm and granularity
+ */
+ MCF_FEC_FECTFCR(ch) = 0
+ | MCF_FEC_FECTFCR_FRM
+ | MCF_FEC_FECTFCR_TXW_MSK
+ | MCF_FEC_FECTFCR_GR(7);
+ MCF_FEC_FECTFAR(ch) = MCF_FEC_FECTFAR_ALARM(256);
+ MCF_FEC_FECTFWR(ch) = MCF_FEC_FECTFWR_X_WMRK_256;
+
+ /*
+ * Enable the transmitter to append the CRC
+ */
+ MCF_FEC_CTCWR(ch) = 0
+ | MCF_FEC_CTCWR_TFCW
+ | MCF_FEC_CTCWR_CRC;
+}
+
+/*
+ * Start the FEC Rx DMA task
+ *
+ * Parameters:
+ * ch FEC channel
+ * rxbd First Rx buffer descriptor in the chain
+ */
+void
+fec_rx_start(uint8_t ch, int8_t *rxbd)
+{
+ uint32_t initiator;
+ int channel, result;
+
+ ASSERT(ch == 0 || ch == 1);
+
+ /*
+ * Make the initiator assignment
+ */
+ result = dma_set_initiator(DMA_FEC_RX(ch));
+ ASSERT(result == 0);
+
+ /*
+ * Grab the initiator number
+ */
+ initiator = dma_get_initiator(DMA_FEC_RX(ch));
+
+ /*
+ * Determine the DMA channel running the task for the
+ * selected FEC
+ */
+ channel = dma_set_channel(DMA_FEC_RX(ch),
+ (ch == 0) ? fec0_rx_frame : fec1_rx_frame);
+ ASSERT(channel != -1);
+
+ /*
+ * Start the Rx DMA task
+ */
+ /*
+ * Start the Rx DMA task
+ */
+ MCD_startDma(channel,
+ (s8*)rxbd,
+ 0,
+ (s8*)MCF_FEC_FECRFDR_ADDR(ch),
+ 0,
+ RX_BUF_SZ,
+ 0,
+ initiator,
+ FECRX_DMA_PRI(ch),
+ 0
+ | MCD_FECRX_DMA
+ | MCD_INTERRUPT
+ | MCD_TT_FLAGS_CW
+ | MCD_TT_FLAGS_RL
+ | MCD_TT_FLAGS_SP
+ ,
+ 0
+ | MCD_NO_CSUM
+ | MCD_NO_BYTE_SWAP
+ );
+}
+
+/*
+ * Continue the Rx DMA task
+ *
+ * This routine is called after the DMA task has halted after
+ * encountering an Rx buffer descriptor that wasn't marked as
+ * ready. There is no harm in calling the DMA continue routine
+ * if the DMA is not halted.
+ *
+ * Parameters:
+ * ch FEC channel
+ */
+void
+fec_rx_continue(uint8_t ch)
+{
+ int channel;
+
+ ASSERT(ch == 0 || ch == 1);
+
+ /*
+ * Determine the DMA channel running the task for the
+ * selected FEC
+ */
+ channel = dma_get_channel(DMA_FEC_RX(ch));
+ ASSERT(channel != -1);
+
+ /*
+ * Continue/restart the DMA task
+ */
+ MCD_continDma(channel);
+}
+
+/*
+ * Stop all frame receptions on the selected FEC
+ *
+ * Parameters:
+ * ch FEC channel
+ */
+void
+fec_rx_stop (uint8_t ch)
+{
+ uint32_t mask;
+ int channel;
+
+ ASSERT(ch == 0 || ch == 1);
+
+ /* Save off the EIMR value */
+ mask = MCF_FEC_EIMR(ch);
+
+ /* Mask all interrupts */
+ MCF_FEC_EIMR(ch) = 0;
+
+ /*
+ * Determine the DMA channel running the task for the
+ * selected FEC
+ */
+ channel = dma_get_channel(DMA_FEC_RX(ch));
+ ASSERT(channel != -1);
+
+ /* Kill the FEC Rx DMA task */
+ MCD_killDma(channel);
+
+ /*
+ * Free up the FEC requestor from the software maintained
+ * initiator list
+ */
+ dma_free_initiator(DMA_FEC_RX(ch));
+
+ /* Free up the DMA channel */
+ dma_free_channel(DMA_FEC_RX(ch));
+
+ /* Restore the interrupt mask register value */
+ MCF_FEC_EIMR(ch) = mask;
+}
+
+/*
+ * Receive Frame interrupt handler - this handler is called by the
+ * DMA interrupt handler indicating that a packet was successfully
+ * transferred out of the Rx FIFO.
+ *
+ * Parameters:
+ * nif Pointer to Network Interface structure
+ * ch FEC channel
+ */
+NBUF *
+fec_rx_frame(uint8_t ch, NIF *nif)
+{
+// ETH_HDR *eth_hdr;
+ FECBD *pRxBD;
+ NBUF *cur_nbuf, *new_nbuf;
+ int keep;
+
+ while ((pRxBD = fecbd_rx_alloc(ch)) != NULL)
+ {
+ fec_log[ch].drxf++;
+ keep = TRUE;
+
+ /*
+ * Check the Receive Frame Status Word for errors
+ * - The L bit should always be set
+ * - No undefined bits should be set
+ * - The upper 5 bits of the length should be cleared
+ */
+ if (!(pRxBD->status & RX_BD_L) || (pRxBD->status & 0x0608)
+ || (pRxBD->length & 0xF800))
+ {
+ keep = FALSE;
+ fec_log[ch].rfsw_inv++;
+ }
+ else if (pRxBD->status & RX_BD_ERROR)
+ {
+ keep = FALSE;
+ if (pRxBD->status & RX_BD_NO)
+ fec_log[ch].rfsw_no++;
+ if (pRxBD->status & RX_BD_CR)
+ fec_log[ch].rfsw_cr++;
+ if (pRxBD->status & RX_BD_OV)
+ fec_log[ch].rfsw_ov++;
+ if (pRxBD->status & RX_BD_TR)
+ fec_log[ch].rfsw_tr++;
+ }
+ else
+ {
+ if (pRxBD->status & RX_BD_LG)
+ fec_log[ch].rfsw_lg++;
+ if (pRxBD->status & RX_BD_M)
+ fec_log[ch].rfsw_m++;
+ if (pRxBD->status & RX_BD_BC)
+ fec_log[ch].rfsw_bc++;
+ if (pRxBD->status & RX_BD_MC)
+ fec_log[ch].rfsw_mc++;
+ }
+
+ if (keep)
+ {
+ /*
+ * Pull the network buffer off the Rx ring queue
+ */
+ cur_nbuf = nbuf_remove(NBUF_RX_RING);
+ ASSERT(cur_nbuf);
+ ASSERT(cur_nbuf->data == pRxBD->data);
+
+ /*
+ * Copy the buffer descriptor information to the network buffer
+ */
+// cur_nbuf->length = (pRxBD->length - (ETH_HDR_LEN + ETH_CRC_LEN));
+// cur_nbuf->offset = ETH_HDR_LEN;
+ cur_nbuf->length = (pRxBD->length - (ETH_CRC_LEN));
+ cur_nbuf->offset = 0;
+
+ /*
+ * Get a new buffer pointer for this buffer descriptor
+ */
+ new_nbuf = nbuf_alloc();
+ if (new_nbuf == NULL)
+ {
+ #ifdef CONFIG_DRIVER_NET_MCF54XX_DEBUG
+ printf("nbuf_alloc() failed\n");
+ #endif
+ /*
+ * Can't allocate a new network buffer, so we
+ * have to trash the received data and reuse the buffer
+ * hoping that some buffers will free up in the system
+ * and this frame will be re-transmitted by the host
+ */
+ pRxBD->length = RX_BUF_SZ;
+ pRxBD->status &= (RX_BD_W | RX_BD_INTERRUPT);
+ pRxBD->status |= RX_BD_E;
+ nbuf_add(NBUF_RX_RING, cur_nbuf);
+ fec_rx_continue(ch);
+ continue;
+ }
+
+ /*
+ * Add the new network buffer to the Rx ring queue
+ */
+ nbuf_add(NBUF_RX_RING, new_nbuf);
+
+ /*
+ * Re-initialize the buffer descriptor - pointing it
+ * to the new data buffer. The previous data buffer
+ * will be passed up the stack
+ */
+ pRxBD->data = new_nbuf->data;
+ pRxBD->length = RX_BUF_SZ;
+ pRxBD->status &= (RX_BD_W | RX_BD_INTERRUPT);
+ pRxBD->status |= RX_BD_E;
+
+
+ /*
+ * Let the DMA know that there is a new Rx BD (in case the
+ * ring was full and the DMA was waiting for an empty one)
+ */
+ fec_rx_continue(ch);
+
+ /*
+ * Get pointer to the frame data inside the network buffer
+ */
+// eth_hdr = (ETH_HDR *)cur_nbuf->data;
+
+ /*
+ * Pass the received packet up the network stack if the
+ * protocol is supported in our network interface (NIF)
+ */
+//FIXME if (nif_protocol_exist(nif,eth_hdr->type))
+// {
+// nif_protocol_handler(nif, eth_hdr->type, cur_nbuf);
+// }
+// else
+// nbuf_free(cur_nbuf);
+ return(cur_nbuf);
+ }
+ else
+ {
+ /*
+ * This frame isn't a keeper
+ * Reset the status and length, but don't need to get another
+ * buffer since we are trashing the data in the current one
+ */
+ pRxBD->length = RX_BUF_SZ;
+ pRxBD->status &= (RX_BD_W | RX_BD_INTERRUPT);
+ pRxBD->status |= RX_BD_E;
+
+ /*
+ * Move the current buffer from the beginning to the end of the
+ * Rx ring queue
+ */
+ cur_nbuf = nbuf_remove(NBUF_RX_RING);
+ nbuf_add(NBUF_RX_RING, cur_nbuf);
+
+ /*
+ * Let the DMA know that there are new Rx BDs (in case
+ * it is waiting for an empty one)
+ */
+ fec_rx_continue(ch);
+ }
+ }
+ return NULL;
+}
+
+void
+fec0_rx_frame(void)
+{
+// extern NIF nif1;
+// fec_rx_frame(0, 0);
+}
+
+void
+fec1_rx_frame(void)
+{
+// extern NIF nif1;
+// fec_rx_frame(1, 0);
+}
+
+/*
+ * Start the FEC Tx DMA task
+ *
+ * Parameters:
+ * ch FEC channel
+ * txbd First Tx buffer descriptor in the chain
+ */
+void
+fec_tx_start(uint8_t ch, int8_t *txbd)
+{
+ uint32_t initiator;
+ int channel, result;
+ void fec0_tx_frame(void);
+ void fec1_tx_frame(void);
+
+ /*
+ * Make the initiator assignment
+ */
+ result = dma_set_initiator(DMA_FEC_TX(ch));
+ ASSERT(result == 0);
+
+ /*
+ * Grab the initiator number
+ */
+ initiator = dma_get_initiator(DMA_FEC_TX(ch));
+ ASSERT(initiator != 0);
+
+ /*
+ * Determine the DMA channel running the task for the
+ * selected FEC
+ */
+ channel = dma_set_channel(DMA_FEC_TX(ch),
+ (ch == 0) ? fec0_tx_frame : fec1_tx_frame);
+ ASSERT(channel != -1);
+
+ /*
+ * Start the Tx DMA task
+ */
+ MCD_startDma(channel,
+ (s8*)txbd,
+ 0,
+ (s8*)MCF_FEC_FECTFDR_ADDR(ch),
+ 0,
+ ETH_MTU,
+ 0,
+ initiator,
+ FECTX_DMA_PRI(ch),
+ 0
+ | MCD_FECTX_DMA
+ | MCD_INTERRUPT
+ | MCD_TT_FLAGS_CW
+ | MCD_TT_FLAGS_RL
+ | MCD_TT_FLAGS_SP
+ ,
+ 0
+ | MCD_NO_CSUM
+ | MCD_NO_BYTE_SWAP
+ );
+}
+
+/*
+ * Continue the Tx DMA task
+ *
+ * This routine is called after the DMA task has halted after
+ * encountering an Tx buffer descriptor that wasn't marked as
+ * ready. There is no harm in calling the continue DMA routine
+ * if the DMA was not paused.
+ *
+ * Parameters:
+ * ch FEC channel
+ */
+void
+fec_tx_continue(uint8_t ch)
+{
+ int channel;
+
+ /*
+ * Determine the DMA channel running the task for the
+ * selected FEC
+ */
+ channel = dma_get_channel(DMA_FEC_TX(ch));
+ ASSERT(channel > 0);
+
+ /*
+ * Continue/restart the DMA task
+ */
+ MCD_continDma((int)channel);
+}
+
+/*
+ * Stop all transmissions on the selected FEC and kill the DMA task
+ *
+ * Parameters:
+ * ch FEC channel
+ */
+void
+fec_tx_stop (uint8_t ch)
+{
+ uint32_t mask;
+ int channel;
+
+ ASSERT(ch == 0 || ch == 1);
+
+ /* Save off the EIMR value */
+ mask = MCF_FEC_EIMR(ch);
+
+ /* Mask all interrupts */
+ MCF_FEC_EIMR(ch) = 0;
+
+ /* If the Ethernet is still enabled... */
+ if (MCF_FEC_ECR(ch) & MCF_FEC_ECR_ETHER_EN)
+ {
+ /* Issue the Graceful Transmit Stop */
+ MCF_FEC_TCR(ch) |= MCF_FEC_TCR_GTS;
+
+ /* Wait for the Graceful Stop Complete interrupt */
+ while(!(MCF_FEC_EIR(ch) & MCF_FEC_EIR_GRA))
+ {
+ if (!(MCF_FEC_ECR(ch) & MCF_FEC_ECR_ETHER_EN))
+ break;
+ }
+
+ /* Clear the Graceful Stop Complete interrupt */
+ MCF_FEC_EIR(ch) = MCF_FEC_EIR_GRA;
+ }
+
+ /*
+ * Determine the DMA channel running the task for the
+ * selected FEC
+ */
+ channel = dma_get_channel(DMA_FEC_TX(ch));
+ ASSERT(channel > 0);
+
+ /* Kill the FEC Tx DMA task */
+ MCD_killDma(channel);
+
+ /*
+ * Free up the FEC requestor from the software maintained
+ * initiator list
+ */
+ dma_free_initiator(DMA_FEC_TX(ch));
+
+ /* Free up the DMA channel */
+ dma_free_channel(DMA_FEC_TX(ch));
+
+ /* Restore the interrupt mask register value */
+ MCF_FEC_EIMR(ch) = mask;
+}
+
+/*
+ * Trasmit Frame interrupt handler - this handler is called by the
+ * DMA interrupt handler indicating that a packet was successfully
+ * transferred to the Tx FIFO.
+ *
+ * Parameters:
+ * ch FEC channel
+ */
+void
+fec_tx_frame(uint8_t ch)
+{
+ FECBD *pTxBD;
+ NBUF *pNbuf;
+
+ while ((pTxBD = fecbd_tx_free(ch)) != NULL)
+ {
+ fec_log[ch].dtxf++;
+
+ /*
+ * Grab the network buffer associated with this buffer descriptor
+ */
+ pNbuf = nbuf_remove(NBUF_TX_RING);
+ ASSERT(pNbuf);
+ ASSERT(pNbuf->data == pTxBD->data);
+
+ /*
+ * Free up the network buffer that was just transmitted
+ */
+ nbuf_free(pNbuf);
+
+ /*
+ * Re-initialize the Tx BD
+ */
+ pTxBD->data = NULL;
+ pTxBD->length = 0;
+ }
+}
+
+void
+fec0_tx_frame(void)
+{
+ fec_tx_frame(0);
+}
+
+void
+fec1_tx_frame(void)
+{
+ fec_tx_frame(1);
+}
+
+/*
+ * Send a packet out the selected FEC
+ *
+ * Parameters:
+ * ch FEC channel
+ * nif Pointer to Network Interface (NIF) structure
+ * dst Destination MAC Address
+ * src Source MAC Address
+ * type Ethernet Frame Type
+ * length Number of bytes to be transmitted (doesn't include type,
+ * src, or dest byte count)
+ * pkt Pointer packet network buffer
+ *
+ * Return Value:
+ * 1 success
+ * 0 otherwise
+ */
+int
+fec_send (uint8_t ch, NIF *nif, uint8_t *dst, uint8_t *src, uint16_t type, NBUF *nbuf)
+{
+ FECBD *pTxBD;
+ ASSERT(ch == 0 || ch == 1);
+
+ /* Check the length */
+ if ((nbuf->length + ETH_HDR_LEN) > ETH_MTU)
+ return 0;
+
+ /*
+ * Copy the destination address, source address, and Ethernet
+ * type into the packet
+ */
+// memcpy(&nbuf->data[0], dst, 6);
+// memcpy(&nbuf->data[6], src, 6);
+// memcpy(&nbuf->data[12], &type, 2);
+
+ /*
+ * Grab the next available Tx Buffer Descriptor
+ */
+ while ((pTxBD = fecbd_tx_alloc(ch)) == NULL) {};
+
+ /*
+ * Put the network buffer into the Tx waiting queue
+ */
+ nbuf_add(NBUF_TX_RING, nbuf);
+
+ /*
+ * Setup the buffer descriptor for transmission
+ */
+ pTxBD->data = nbuf->data;
+ pTxBD->length = nbuf->length; // + ETH_HDR_LEN;
+ pTxBD->status |= (TX_BD_R | TX_BD_L);
+
+ /*
+ * Continue the Tx DMA task (in case it was waiting for a new
+ * TxBD to be ready
+ */
+ fec_tx_continue(ch);
+
+ return 1;
+}
+
+int
+fec0_send(NIF *nif, uint8_t *dst, uint8_t *src, uint16_t type, NBUF *nbuf)
+{
+ return fec_send(0, nif, dst, src, type, nbuf);
+}
+
+int
+fec1_send(NIF *nif, uint8_t *dst, uint8_t *src, uint16_t type, NBUF *nbuf)
+{
+ return fec_send(1, nif, dst, src, type, nbuf);
+}
+
+/*
+ * Enable interrupts on the selected FEC
+ *
+ * Parameters:
+ * ch FEC channel
+ * pri Interrupt Priority
+ * lvl Interrupt Level
+ */
+void
+fec_irq_enable(uint8_t ch, uint8_t lvl, uint8_t pri)
+{
+ ASSERT(ch == 0 || ch == 1);
+ ASSERT(lvl > 0 && lvl < 8);
+ ASSERT(pri < 8);
+
+ /*
+ * Setup the appropriate ICR
+ */
+ MCF_INTC_ICRn((ch == 0) ? 39 : 38) = (uint8_t)(0
+ | MCF_INTC_ICRn_IP(pri)
+ | MCF_INTC_ICRn_IL(lvl));
+
+ /*
+ * Clear any pending FEC interrupt events
+ */
+ MCF_FEC_EIR(ch) = MCF_FEC_EIR_CLEAR_ALL;
+
+ /*
+ * Unmask all FEC interrupts
+ */
+ MCF_FEC_EIMR(ch) = MCF_FEC_EIMR_UNMASK_ALL;
+
+ /*
+ * Unmask the FEC interrupt in the interrupt controller
+ */
+ if (ch == 0)
+ MCF_INTC_IMRH &= ~MCF_INTC_IMRH_INT_MASK39;
+ else
+ MCF_INTC_IMRH &= ~MCF_INTC_IMRH_INT_MASK38;
+}
+
+/*
+ * Disable interrupts on the selected FEC
+ *
+ * Parameters:
+ * ch FEC channel
+ */
+void
+fec_irq_disable(uint8_t ch)
+{
+ ASSERT(ch == 0 || ch == 1);
+
+ /*
+ * Mask all FEC interrupts
+ */
+ MCF_FEC_EIMR(ch) = MCF_FEC_EIMR_MASK_ALL;
+
+ /*
+ * Mask the FEC interrupt in the interrupt controller
+ */
+ if (ch == 0)
+ MCF_INTC_IMRH |= MCF_INTC_IMRH_INT_MASK39;
+ else
+ MCF_INTC_IMRH |= MCF_INTC_IMRH_INT_MASK38;
+}
+
+/*
+ * FEC interrupt handler
+ * All interrupts are multiplexed into a single vector for each
+ * FEC module. The lower level interrupt handler passes in the
+ * channel to this handler. Note that the receive interrupt is
+ * generated by the Multi-channel DMA FEC Rx task.
+ *
+ * Parameters:
+ * ch FEC channel
+ */
+static void
+fec_irq_handler(uint8_t ch)
+{
+ uint32_t event, eir;
+
+ /*
+ * Determine which interrupt(s) asserted by AND'ing the
+ * pending interrupts with those that aren't masked.
+ */
+ eir = MCF_FEC_EIR(ch);
+ event = eir & MCF_FEC_EIMR(ch);
+
+ #ifdef CONFIG_DRIVER_NET_MCF54XX_DEBUG
+ if (event != eir)
+ printf("Pending but not enabled: 0x%08X\n",(event ^ eir));
+ #endif
+
+ /*
+ * Clear the event(s) in the EIR immediately
+ */
+ MCF_FEC_EIR(ch) = event;
+
+ if (event & MCF_FEC_EIR_RFERR)
+ {
+ fec_log[ch].total++;
+ fec_log[ch].rferr++;
+ #ifdef CONFIG_DRIVER_NET_MCF54XX_DEBUG
+ printf("RFERR\n");
+ printf("FECRFSR%d = 0x%08x\n",ch,MCF_FEC_FECRFSR(ch));
+ fec_eth_stop(ch);
+ #endif
+ }
+ if (event & MCF_FEC_EIR_XFERR)
+ {
+ fec_log[ch].total++;
+ fec_log[ch].xferr++;
+ #ifdef CONFIG_DRIVER_NET_MCF54XX_DEBUG
+ printf("XFERR\n");
+ #endif
+ }
+ if (event & MCF_FEC_EIR_XFUN)
+ {
+ fec_log[ch].total++;
+ fec_log[ch].xfun++;
+ #ifdef CONFIG_DRIVER_NET_MCF54XX_DEBUG
+ printf("XFUN\n");
+ fec_eth_stop(ch);
+ #endif
+ }
+ if (event & MCF_FEC_EIR_RL)
+ {
+ fec_log[ch].total++;
+ fec_log[ch].rl++;
+ #ifdef CONFIG_DRIVER_NET_MCF54XX_DEBUG
+ printf("RL\n");
+ #endif
+ }
+ if (event & MCF_FEC_EIR_LC)
+ {
+ fec_log[ch].total++;
+ fec_log[ch].lc++;
+ #ifdef CONFIG_DRIVER_NET_MCF54XX_DEBUG
+ printf("LC\n");
+ #endif
+ }
+ if (event & MCF_FEC_EIR_MII)
+ {
+ fec_log[ch].mii++;
+ }
+ if (event & MCF_FEC_EIR_TXF)
+ {
+ fec_log[ch].txf++;
+ }
+ if (event & MCF_FEC_EIR_GRA)
+ {
+ fec_log[ch].gra++;
+ }
+ if (event & MCF_FEC_EIR_BABT)
+ {
+ fec_log[ch].total++;
+ fec_log[ch].babt++;
+ #ifdef CONFIG_DRIVER_NET_MCF54XX_DEBUG
+ printf("BABT\n");
+ #endif
+ }
+ if (event & MCF_FEC_EIR_BABR)
+ {
+ fec_log[ch].total++;
+ fec_log[ch].babr++;
+ #ifdef CONFIG_DRIVER_NET_MCF54XX_DEBUG
+ printf("BABR\n");
+ #endif
+ }
+ if (event & MCF_FEC_EIR_HBERR)
+ {
+ fec_log[ch].total++;
+ fec_log[ch].hberr++;
+ #ifdef CONFIG_DRIVER_NET_MCF54XX_DEBUG
+ printf("HBERR\n");
+ #endif
+ }
+}
+
+int
+fec0_interrupt_handler(void* arg1, void* arg2)
+{
+ (void) arg1;
+ (void) arg2;
+ fec_irq_handler(0);
+ return 1;
+}
+
+int
+fec1_interrupt_handler(void* arg1, void* arg2)
+{
+ (void) arg1;
+ (void) arg2;
+ fec_irq_handler(1);
+ return 1;
+}
+
+/*
+ * Configure the selected Ethernet port and enable all operations
+ *
+ * Parameters:
+ * ch FEC channel
+ * trcvr Transceiver mode (MII, 7-Wire or internal loopback)
+ * speed Maximum operating speed (MII only)
+ * duplex Full or Half-duplex (MII only)
+ * mac Physical (MAC) Address
+ */
+void
+fec_eth_setup(uint8_t ch, uint8_t trcvr, uint8_t speed, uint8_t duplex, const uint8_t *mac)
+{
+ ASSERT(ch == 0 || ch == 1);
+
+ /*
+ * Disable FEC interrupts
+ */
+ fec_irq_disable(ch);
+
+ /*
+ * Initialize the event log
+ */
+ fec_log_init(ch);
+
+ /*
+ * Initialize the network buffers and fec buffer descriptors
+ */
+ nbuf_init();
+ fecbd_init(ch);
+
+ /*
+ * Initialize the FEC
+ */
+ fec_reset(ch);
+ fec_init(ch,trcvr,mac);
+
+ if (trcvr == FEC_MODE_MII)
+ {
+ /*
+ * Initialize the MII interface
+ */
+ fec_mii_init(ch, CFG_SYSTEM_CORE_CLOCK);
+ }
+
+ /*
+ * Initialize and enable FEC interrupts
+ */
+ fec_irq_enable(ch, FEC_INTC_LVL(ch), FEC_INTC_PRI(ch));
+
+ /*
+ * Enable the multi-channel DMA tasks
+ */
+ fec_rx_start(ch, (int8_t*)fecbd_get_start(ch,Rx));
+ fec_tx_start(ch, (int8_t*)fecbd_get_start(ch,Tx));
+
+ /*
+ * Enable the FEC channel
+ */
+ MCF_FEC_ECR(ch) |= MCF_FEC_ECR_ETHER_EN;
+}
+/*
+ * Reset the selected Ethernet port
+ *
+ * Parameters:
+ * ch FEC channel
+ */
+void
+fec_eth_reset(uint8_t ch)
+{
+// To do
+}
+
+/*
+ * Stop the selected Ethernet port
+ *
+ * Parameters:
+ * ch FEC channel
+ */
+void
+fec_eth_stop(uint8_t ch)
+{
+ int level;
+
+ /*
+ * Disable interrupts
+ */
+ level = asm_set_ipl(7);
+
+ /*
+ * Gracefully disable the receiver and transmitter
+ */
+ fec_tx_stop(ch);
+ fec_rx_stop(ch);
+
+ /*
+ * Disable FEC interrupts
+ */
+ fec_irq_disable(ch);
+
+ /*
+ * Disable the FEC channel
+ */
+ MCF_FEC_ECR(ch) &= ~MCF_FEC_ECR_ETHER_EN;
+
+ #ifdef CONFIG_DRIVER_NET_MCF54XX_DEBUG
+ nbuf_debug_dump();
+ fec_log_dump(ch);
+ #endif
+
+ /*
+ * Flush the network buffers
+ */
+ nbuf_flush();
+
+ /*
+ * Restore interrupt level
+ */
+ asm_set_ipl(level);
+}