/* * File: fec.c * Purpose: Driver for the Fast Ethernet Controller (FEC) * * Notes: */ #include "net.h" #include "fec.h" #include "fecbd.h" #include "exceptions.h" #include "interrupts.h" #include "MCF5475.h" #include "MCD_dma.h" #include "mcd_initiators.h" #include "dma.h" #include "bas_string.h" #include "bas_printf.h" #include "util.h" #include "wait.h" #include "am79c874.h" //#include "bcm5222.h" #include #if defined(MACHINE_FIREBEE) #include "firebee.h" #elif defined(MACHINE_M5484LITE) #include "m5484l.h" #elif defined(MACHINE_M54455) #include "m54455.h" #else #error Unknown machine! #endif #define DBG_FEC #ifdef DBG_FEC #define dbg(format, arg...) do { xprintf("DEBUG: %s(): " format, __FUNCTION__, ##arg); } while (0) #else #define dbg(format, arg...) do { ; } while (0) #endif /* DBG_FEC */ 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; /* * 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++) { wait(1); if (MCF_FEC_EIR(ch) & MCF_FEC_EIR_MII) break; } if (timeout == FEC_MII_TIMEOUT) return 0; /* * Clear the MII interrupt bit */ MCF_FEC_EIR(ch) = MCF_FEC_EIR_MII; /* * Restore the EIMR */ MCF_FEC_EIMR(ch) = eimr; return 1; } /* * 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; /* * 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++) { wait(1); if (MCF_FEC_EIR(ch) & MCF_FEC_EIR_MII) break; } if (timeout == FEC_MII_TIMEOUT) return 0; /* * Clear the MII interrupt bit */ MCF_FEC_EIR(ch) = MCF_FEC_EIR_MII; *data = (uint16_t)(MCF_FEC_MMFR(ch) & 0x0000FFFF); return 1; } /* * 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) { /* * 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) { //To do } /* * Display the MIB counters * * Parameters: * ch FEC channel */ void fec_mib_dump(uint8_t ch) { //To do } /* * Initialize the FEC log * * Parameters: * ch FEC channel */ void fec_log_init(uint8_t ch) { memset(&fec_log[ch], 0, sizeof(FEC_EVENT_LOG)); } /* * Display the FEC log * * Parameters: * ch FEC channel */ void fec_log_dump(uint8_t ch) { dbg("\r\n FEC%d Log\r\n", __FUNCTION__, ch); dbg(" ---------------\r\n", __FUNCTION__); dbg(" Total: %4d\r\n", fec_log[ch].total); dbg(" hberr: %4d\r\n", fec_log[ch].hberr); dbg(" babr: %4d\r\n", fec_log[ch].babr); dbg(" babt: %4d\r\n", fec_log[ch].babt); dbg(" gra: %4d\r\n", fec_log[ch].gra); dbg(" txf: %4d\r\n", fec_log[ch].txf); dbg(" mii: %4d\r\n", fec_log[ch].mii); dbg(" lc: %4d\r\n", fec_log[ch].lc); dbg(" rl: %4d\r\n", fec_log[ch].rl); dbg(" xfun: %4d\r\n", fec_log[ch].xfun); dbg(" xferr: %4d\r\n", fec_log[ch].xferr); dbg(" rferr: %4d\r\n", fec_log[ch].rferr); dbg(" dtxf: %4d\r\n", fec_log[ch].dtxf); dbg(" drxf: %4d\r\n", fec_log[ch].drxf); dbg(" \r\nRFSW:\r\n"); dbg(" inv: %4d\r\n", fec_log[ch].rfsw_inv); dbg(" m: %4d\r\n", fec_log[ch].rfsw_m); dbg(" bc: %4d\r\n", fec_log[ch].rfsw_bc); dbg(" mc: %4d\r\n", fec_log[ch].rfsw_mc); dbg(" lg: %4d\r\n", fec_log[ch].rfsw_lg); dbg(" no: %4d\r\n", fec_log[ch].rfsw_no); dbg(" cr: %4d\r\n", fec_log[ch].rfsw_cr); dbg(" ov: %4d\r\n", fec_log[ch].rfsw_ov); dbg(" tr: %4d\r\n", fec_log[ch].rfsw_tr); dbg(" ---------------\r\n\r\n"); } /* * Display some of the registers for debugging * * Parameters: * ch FEC channel */ void fec_debug_dump(uint8_t ch) { dbg("\r\n------------- FEC%d -------------\r\n",ch); dbg("EIR %08x \r\n", MCF_FEC_EIR(ch)); dbg("EIMR %08x \r\n", MCF_FEC_EIMR(ch)); dbg("ECR %08x \r\n", MCF_FEC_ECR(ch)); dbg("RCR %08x \r\n", MCF_FEC_RCR(ch)); dbg("R_HASH %08x \r\n", MCF_FEC_RHR_HASH(ch)); dbg("TCR %08x \r\n", MCF_FEC_TCR(ch)); dbg("FECTFWR %08x \r\n", MCF_FEC_FECTFWR(ch)); dbg("FECRFSR %08x \r\n", MCF_FEC_FECRFSR(ch)); dbg("FECRFCR %08x \r\n", MCF_FEC_FECRFCR(ch)); dbg("FECRLRFP %08x \r\n", MCF_FEC_FECRLRFP(ch)); dbg("FECRLWFP %08x \r\n", MCF_FEC_FECRLWFP(ch)); dbg("FECRFAR %08x \r\n", MCF_FEC_FECRFAR(ch)); dbg("FECRFRP %08x \r\n", MCF_FEC_FECRFRP(ch)); dbg("FECRFWP %08x \r\n", MCF_FEC_FECRFWP(ch)); dbg("FECTFSR %08x \r\n", MCF_FEC_FECTFSR(ch)); dbg("FECTFCR %08x \r\n", MCF_FEC_FECTFCR(ch)); dbg("FECTLRFP %08x \r\n", MCF_FEC_FECTLRFP(ch)); dbg("FECTLWFP %08x \r\n", MCF_FEC_FECTLWFP(ch)); dbg("FECTFAR %08x \r\n", MCF_FEC_FECTFAR(ch)); dbg("FECTFRP %08x \r\n", MCF_FEC_FECTFRP(ch)); dbg("FECTFWP %08x \r\n", MCF_FEC_FECTFWP(ch)); dbg("FRST %08x \r\n", MCF_FEC_FECFRST(ch)); dbg("--------------------------------\r\n\r\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) { 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; /* * Set the Physical Address */ MCF_FEC_PALR(ch) = (uint32_t) ((pa[0] << 24) | (pa[1] << 16) | (pa[2] << 8) | pa[3]); MCF_FEC_PAHR(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; /* 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_FECTFSR_OF | MCF_FEC_FECTFSR_UF | MCF_FEC_FECTFSR_TXW | MCF_FEC_FECTFSR_FAE | MCF_FEC_FECTFSR_IP); /* Reset the FIFOs */ MCF_FEC_FECFRST(ch) |= MCF_FEC_FECFRST_SW_RST; MCF_FEC_FECFRST(ch) &= ~MCF_FEC_FECFRST_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) { /* * Enable all the external interface signals */ if (mode == FEC_MODE_7WIRE) { if (ch == 1) MCF_PAD_PAR_FECI2CIRQ |= MCF_PAD_PAR_FECI2CIRQ_PAR_E17; else MCF_PAD_PAR_FECI2CIRQ |= MCF_PAD_PAR_FECI2CIRQ_PAR_E07; } else if (mode == FEC_MODE_MII) { if (ch == 1) MCF_PAD_PAR_FECI2CIRQ |= 0 | MCF_PAD_PAR_FECI2CIRQ_PAR_E1MDC_E1MDC | MCF_PAD_PAR_FECI2CIRQ_PAR_E1MDIO_E1MDIO | MCF_PAD_PAR_FECI2CIRQ_PAR_E1MII | MCF_PAD_PAR_FECI2CIRQ_PAR_E17; else MCF_PAD_PAR_FECI2CIRQ |= 0 | MCF_PAD_PAR_FECI2CIRQ_PAR_E0MDC | MCF_PAD_PAR_FECI2CIRQ_PAR_E0MDIO | MCF_PAD_PAR_FECI2CIRQ_PAR_E0MII | MCF_PAD_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) | MCF_FEC_RCR_PROM | 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_FRMEN | 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_FRMEN | MCF_FEC_FECTFCR_TXW_MASK | 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_FECCTCWR(ch) = 0 | MCF_FEC_FECCTCWR_TFCW | MCF_FEC_FECCTCWR_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; #ifdef DBG_FEC int res; #endif /* * Make the initiator assignment */ #if defined(DBG_FEC) res = #else (void) #endif dma_set_initiator(DMA_FEC_RX(ch)); dbg("dma_set_initiator(DMA_FEC_RX(%d)): %d\r\n", ch, res); /* * Grab the initiator number */ initiator = dma_get_initiator(DMA_FEC_RX(ch)); dbg("dma_get_initiator(DMA_FEC_RX(%d)) = %d\r\n", ch, initiator); /* * 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); dbg("DMA channel for FEC%1d: %d\r\n", ch, channel); /* * Start the Rx DMA task */ MCD_startDma(channel, (int8_t *) rxbd, 0, (int8_t *) MCF_FEC_FECRFDR(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 ); dbg("Rx DMA task for FEC%1d started\r\n", ch); } /* * 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; /* * Determine the DMA channel running the task for the * selected FEC */ channel = dma_get_channel(DMA_FEC_RX(ch)); dbg("RX DMA channel for FEC%1d is %d\r\n", ch, channel); /* * Continue/restart the DMA task */ MCD_continDma(channel); dbg("RX dma on channel %d continued\r\n", 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; /* 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)); /* 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 */ void fec_rx_frame(uint8_t ch, NIF *nif) { ETH_HDR *eth_hdr; FECBD *pRxBD; NBUF *cur_nbuf, *new_nbuf; int keep; dbg("started\r\n"); 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); /* * 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; /* * Get a new buffer pointer for this buffer descriptor */ new_nbuf = nbuf_alloc(); if (new_nbuf == NULL) { dbg("nbuf_alloc() failed\n"); /* * 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) */ if (nif_protocol_exist(nif, eth_hdr->type)) { hexdump((uint8_t *) eth_hdr, ETH_MAX_FRM); nif_protocol_handler(nif, eth_hdr->type, cur_nbuf); } else { nbuf_free(cur_nbuf); dbg("got unsupported packet %d, trashed it\r\n", eth_hdr->type); } } 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); } } } void fec0_rx_frame(void) { extern NIF nif1; fec_rx_frame(0, &nif1); } void fec1_rx_frame(void) { extern NIF nif1; fec_rx_frame(1, &nif1); } /* * 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; void fec0_tx_frame(void); void fec1_tx_frame(void); #ifdef DBG_FEC int res; #endif /* * Make the initiator assignment */ #ifdef DBG_FEC res = #else (void) #endif dma_set_initiator(DMA_FEC_TX(ch)); dbg("dma_set_initiator(%d) = %d\r\n", ch, res); /* * Grab the initiator number */ initiator = dma_get_initiator(DMA_FEC_TX(ch)); dbg("dma_get_initiator(%d) = %d\r\n", ch, initiator); /* * 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); dbg("dma_set_channel(%d, ...) = %d\r\n", ch, channel); /* * Start the Tx DMA task */ MCD_startDma(channel, (int8_t *) txbd, 0, (int8_t*) MCF_FEC_FECTFDR(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 ); dbg("DMA tx task started\r\n"); } /* * 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)); dbg("dma_get_channel(DMA_FEC_TX(%d)) = %d\r\n", ch, channel); /* * Continue/restart the DMA task */ MCD_continDma(channel); dbg("DMA TX task continue\r\n"); } /* * 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; /* 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)); /* 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; bool is_empty = true; dbg("\r\n"); 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); /* * Free up the network buffer that was just transmitted */ nbuf_free(pNbuf); dbg("free buffer %p from TX ring\r\n", pNbuf); /* * Re-initialize the Tx BD */ pTxBD->data = NULL; pTxBD->length = 0; is_empty = false; } if (is_empty) dbg("transmit queue was empty!\r\n"); } 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; /* Check the length */ if ((nbuf->length + ETH_HDR_LEN) > ETH_MTU) { dbg("nbuf->length (%d) + ETH_HDR_LEN (%d) exceeds ETH_MTU (%d)\r\n", 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) { /* * Setup the appropriate ICR */ MCF_INTC_ICR((ch == 0) ? 39 : 38) = MCF_INTC_ICR_IP(pri) | MCF_INTC_ICR_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) { /* * 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); if (event != eir) dbg("pending but not enabled: 0x%08x\r\n", (event ^ eir)); /* * 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++; dbg("RFERR\r\n"); dbg("FECRFSR%d = 0x%08x\r\n", ch, MCF_FEC_FECRFSR(ch)); //fec_eth_stop(ch); } if (event & MCF_FEC_EIR_XFERR) { fec_log[ch].total++; fec_log[ch].xferr++; dbg("XFERR\r\n"); } if (event & MCF_FEC_EIR_XFUN) { fec_log[ch].total++; fec_log[ch].xfun++; dbg("XFUN\r\n"); //fec_eth_stop(ch); } if (event & MCF_FEC_EIR_RL) { fec_log[ch].total++; fec_log[ch].rl++; dbg("RL\r\n"); } if (event & MCF_FEC_EIR_LC) { fec_log[ch].total++; fec_log[ch].lc++; dbg("LC\r\n"); } if (event & MCF_FEC_EIR_MII) { fec_log[ch].mii++; dbg("MII\r\n"); } if (event & MCF_FEC_EIR_TXF) { fec_log[ch].txf++; dbg("TXF\r\n"); } if (event & MCF_FEC_EIR_GRA) { fec_log[ch].gra++; dbg("GRA\r\n"); } if (event & MCF_FEC_EIR_BABT) { fec_log[ch].total++; fec_log[ch].babt++; dbg("BABT\r\n"); } if (event & MCF_FEC_EIR_BABR) { fec_log[ch].total++; fec_log[ch].babr++; dbg("BABR\r\n"); } if (event & MCF_FEC_EIR_HBERR) { fec_log[ch].total++; fec_log[ch].hberr++; dbg("HBERR\r\n"); } } /* * handler for FEC interrupts * arg2 is a pointer to the nif in this case */ int fec0_interrupt_handler(void* arg1, void* arg2) { (void) arg1; /* not used */ (void) arg2; fec_irq_handler(0); return 1; } int fec1_interrupt_handler(void* arg1, void* arg2) { (void) arg1; /* not used */ (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) { /* * 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 */ #if defined(MACHINE_FIREBEE) if (am79c874_init(0, 0, speed, duplex)) dbg("PHY init completed\r\n"); else dbg("PHY init failed\r\n"); #elif defined(MACHINE_M548X) bcm_5222_init(0, 0, speed, duplex); #else fec_mii_init(ch, SYSCLK / 1000); #endif /* MACHINE_FIREBEE */ } /* * 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 = set_ipl(7); dbg("fec %d stopped\r\n", ch); /* * 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 DBG_FEC nbuf_debug_dump(); fec_log_dump(ch); #endif /* * Flush the network buffers */ nbuf_flush(); /* * Restore interrupt level */ set_ipl(level); }