diff options
Diffstat (limited to 'arch/m68k/mach-mcfv4e/fec.c')
-rw-r--r-- | arch/m68k/mach-mcfv4e/fec.c | 1440 |
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); +} |