📄 5272fec.c
字号:
/*
* Copyright (c) 2001-2003, Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
*
* Author: David Haas dhaas@alum.rpi.edu
*
*/
/* This is an ethernet driver for the internal fec in the Coldfire MCF5272.
The driver has been written to use ISRs for Receive Frame and Transmit Frame
Complete.
*/
#include "lwip/debug.h"
#include "lwip/opt.h"
#include "lwip/def.h"
#include "lwip/mem.h"
#include "lwip/pbuf.h"
#include "lwip/stats.h"
#include "lwip/sys.h"
#include "netif/etharp.h"
#include "arch/mcf5272.h"
/* Sizing the descriptor rings will depend upon how many pbufs you have available
* and how big they are. Also on how many frames you might want to input before dropping
* frames. Generally it is a good idea to buffer one tcp window. This means that
* you won't get a tcp retransmit and your tcp transmissions will be reasonably fast.
*/
#define NUM_RXBDS 64 // Number of receive descriptor rings
#define NUM_TXBDS 32 // Number of transmit descriptor rings
/* Define those to better describe your network interface. */
#define IFNAME0 'e'
#define IFNAME1 't'
/* Define interface MTU size. We set this to 1518, since this is the max
Size of an ethernet frame without VLAN support. */
#define MTU_FEC 1518
PACK_STRUCT_BEGIN
struct rxbd
{
u16_t flags;
u16_t data_len;
u8_t *p_buf;
};
PACK_STRUCT_END
typedef struct rxbd rxbd_t;
PACK_STRUCT_BEGIN
struct txbd
{
u16_t flags;
u16_t data_len;
u8_t *p_buf;
};
PACK_STRUCT_END
typedef struct txbd txbd_t;
ALIGN_STRUCT_8_BEGIN
struct mcf5272if
{
rxbd_t rxbd_a[NUM_RXBDS]; // Rx descriptor ring. Must be aligned to double-word
txbd_t txbd_a[NUM_TXBDS]; // Tx descriptor ring. Must be aligned to double-word
struct pbuf *rx_pbuf_a[NUM_RXBDS]; // Array of pbufs corresponding to payloads in rx desc ring.
struct pbuf *tx_pbuf_a[NUM_TXBDS]; // Array of pbufs corresponding to payloads in tx desc ring.
unsigned int rx_remove; // Index that driver will remove next rx frame from.
unsigned int rx_insert; // Index that driver will insert next empty rx buffer.
unsigned int tx_insert; // Index that driver will insert next tx frame to.
unsigned int tx_remove; // Index that driver will clean up next tx buffer.
unsigned int tx_free; // Number of free transmit descriptors.
unsigned int rx_buf_len; // number of bytes in a rx buffer (that we can use).
MCF5272_IMM *imm; // imm address. All register accesses use this as base.
struct eth_addr *ethaddr;
struct netif *netif;
};
ALIGN_STRUCT_END
typedef struct mcf5272if mcf5272if_t;
#define INC_RX_BD_INDEX(idx) do { if (++idx >= NUM_RXBDS) idx = 0; } while (0)
#define INC_TX_BD_INDEX(idx) do { if (++idx >= NUM_TXBDS) idx = 0; } while (0)
#define DEC_TX_BD_INDEX(idx) do { if (idx-- == 0) idx = NUM_TXBDS-1; } while (0)
static mcf5272if_t *mcf5272if;
static sys_sem_t tx_sem;
u32_t phy;
typedef struct mcf5272if mcf5272if_t;
/*-----------------------------------------------------------------------------------*/
static void
fill_rx_ring(mcf5272if_t *mcf5272)
{
struct pbuf *p;
struct rxbd *p_rxbd;
int i = mcf5272->rx_insert;
void *new_payload;
u32_t u_p_pay;
/* Try and fill as many receive buffers as we can */
while (mcf5272->rx_pbuf_a[i] == 0)
{
p = pbuf_alloc(PBUF_RAW, (u16_t) mcf5272->rx_buf_len, PBUF_POOL);
if (p == 0)
/* No pbufs, so can't refill ring */
return;
/* Align payload start to be divisible by 16 as required by HW */
u_p_pay = (u32_t) p->payload;
new_payload = p->payload = (void *) (((u_p_pay + 15) / 16) * 16);
mcf5272->rx_pbuf_a[i] = p;
p_rxbd = &mcf5272->rxbd_a[i];
p_rxbd->p_buf = (u8_t *) new_payload;
p_rxbd->flags = (p_rxbd->flags & MCF5272_FEC_RX_BD_W) | MCF5272_FEC_RX_BD_E;
INC_RX_BD_INDEX(mcf5272->rx_insert);
i = mcf5272->rx_insert;
}
}
/*-----------------------------------------------------------------------------------*/
static void
enable_fec(mcf5272if_t *mcf5272)
{
MCF5272_IMM *imm = mcf5272->imm;
int i;
/* Initialize empty tx descriptor ring */
for(i = 0; i < NUM_TXBDS-1; i++)
mcf5272->txbd_a[i].flags = 0;
/* Set wrap bit for last descriptor */
mcf5272->txbd_a[i].flags = MCF5272_FEC_TX_BD_W;
/* initialize tx indexes */
mcf5272->tx_remove = mcf5272->tx_insert = 0;
mcf5272->tx_free = NUM_TXBDS;
/* Initialize empty rx descriptor ring */
for (i = 0; i < NUM_RXBDS-1; i++)
mcf5272->rxbd_a[i].flags = 0;
/* Set wrap bit for last descriptor */
mcf5272->rxbd_a[i].flags = MCF5272_FEC_RX_BD_W;
/* Initialize rx indexes */
mcf5272->rx_remove = mcf5272->rx_insert = 0;
/* Fill receive descriptor ring */
fill_rx_ring(mcf5272);
/* Enable FEC */
MCF5272_WR_FEC_ECR(imm, (MCF5272_FEC_ECR_ETHER_EN));// | 0x2000000));
/* Indicate that there have been empty receive buffers produced */
MCF5272_WR_FEC_RDAR(imm,1);
}
/*-----------------------------------------------------------------------------------*/
static void
disable_fec(mcf5272if_t *mcf5272)
{
MCF5272_IMM *imm = mcf5272->imm;
int i;
u32_t value;
u32_t old_level;
/* We need to disable interrupts here, It is important when dealing with shared
registers. */
old_level = sys_arch_protect();
/* First disable the FEC interrupts. Do it in the appropriate ICR register. */
value = MCF5272_RD_SIM_ICR3(imm);
MCF5272_WR_SIM_ICR3(imm, (value & ~(MCF5272_SIM_ICR_ERX_IL(7) |
MCF5272_SIM_ICR_ETX_IL(7) |
MCF5272_SIM_ICR_ENTC_IL(7))));
/* Now we can restore interrupts. This is because we can assume that
* we are single threaded here (only 1 thread will be calling disable_fec
* for THIS interface). */
sys_arch_unprotect(old_level);
/* Release all buffers attached to the descriptors. Since the driver
* ALWAYS zeros the pbuf array locations and descriptors when buffers are
* removed, we know we just have to free any non-zero descriptors */
for (i = 0; i < NUM_RXBDS; i++)
if (mcf5272->rx_pbuf_a[i])
{
pbuf_free(mcf5272->rx_pbuf_a[i]);
mcf5272->rx_pbuf_a[i] = 0;
mcf5272->rxbd_a->p_buf = 0;
}
for (i = 0; i < NUM_TXBDS; i++)
if (mcf5272->tx_pbuf_a[i])
{
pbuf_free(mcf5272->tx_pbuf_a[i]);
mcf5272->tx_pbuf_a[i] = 0;
mcf5272->txbd_a->p_buf = 0;
}
/* Reset the FEC - equivalent to a hard reset */
MCF5272_WR_FEC_ECR(imm,MCF5272_FEC_ECR_RESET);
/* Wait for the reset sequence to complete, it should take about 16 clock cycles */
i = 0;
while (MCF5272_RD_FEC_ECR(imm) & MCF5272_FEC_ECR_RESET)
{
if (++i > 100)
abort();
}
/* Disable all FEC interrupts by clearing the IMR register */
MCF5272_WR_FEC_IMR(imm,0);
/* Clear any interrupts by setting all bits in the EIR register */
MCF5272_WR_FEC_EIR(imm,0xFFFFFFFF);
}
/*-----------------------------------------------------------------------------------*
* Function called by receive LISR to disable fec tx interrupt
*-----------------------------------------------------------------------------------*/
static void
mcf5272_dis_tx_int(void)
{
mcf5272if_t *mcf5272 = mcf5272if;
MCF5272_IMM *imm = mcf5272->imm;
u32_t value;
value = MCF5272_RD_FEC_IMR(imm);
/* Clear rx interrupt bit */
MCF5272_WR_FEC_IMR(imm, (value & ~MCF5272_FEC_IMR_TXFEN));
return;
}
/*-----------------------------------------------------------------------------------*
*-----------------------------------------------------------------------------------*/
static void mcf5272fec_tx_hisr(void)
{
/* Just signal task that it can run and cleanup */
sys_sem_signal(tx_sem);
}
/*-----------------------------------------------------------------------------------*
This function must be run as a task, since it ends up calling free() through pbuf_free()
*-----------------------------------------------------------------------------------*/
static void
mcf5272fec_tx_cleanup(void)
{
struct pbuf *p;
mcf5272if_t *mcf5272 = mcf5272if;
MCF5272_IMM *imm = mcf5272->imm;
u32_t value;
u32_t old_level;
unsigned int tx_remove_sof;
unsigned int tx_remove_eof;
unsigned int i;
u16_t flags;
tx_remove_sof = tx_remove_eof = mcf5272->tx_remove;
/* We must protect reading the flags and then reading the buffer pointer. They must
both be read together. */
old_level = sys_arch_protect();
/* Loop, looking for completed buffers at eof */
while ((((flags = mcf5272->txbd_a[tx_remove_eof].flags) & MCF5272_FEC_TX_BD_R) == 0) &&
(mcf5272->tx_pbuf_a[tx_remove_eof] != 0))
{
/* See if this is last buffer in frame */
if ((flags & MCF5272_FEC_TX_BD_L) != 0)
{
i = tx_remove_eof;
/* This frame is complete. Take the frame off backwards */
do
{
p = mcf5272->tx_pbuf_a[i];
mcf5272->tx_pbuf_a[i] = 0;
mcf5272->txbd_a[i].p_buf = 0;
mcf5272->tx_free++;
if (i != tx_remove_sof)
DEC_TX_BD_INDEX(i);
else
break;
} while (1);
sys_arch_unprotect(old_level);
pbuf_free(p); // Will be head of chain
old_level = sys_arch_protect();
/* Look at next descriptor */
INC_TX_BD_INDEX(tx_remove_eof);
tx_remove_sof = tx_remove_eof;
}
else
INC_TX_BD_INDEX(tx_remove_eof);
}
mcf5272->tx_remove = tx_remove_sof;
/* clear interrupt status for tx interrupt */
MCF5272_WR_FEC_EIR(imm, MCF5272_FEC_EIR_TXF);
value = MCF5272_RD_FEC_IMR(imm);
/* Set tx interrupt bit again */
MCF5272_WR_FEC_IMR(imm, (value | MCF5272_FEC_IMR_TXFEN));
/* Now we can re-enable higher priority interrupts again */
sys_arch_unprotect(old_level);
}
/*-----------------------------------------------------------------------------------*
void low_level_output(mcf5272if_t *mcf5272, struct pbuf *p)
Output pbuf chain to hardware. It is assumed that there is a complete and correct
ethernet frame in p. The only buffering we have in this system is in the
hardware descriptor ring. If there is no room on the ring, then drop the frame.
*-----------------------------------------------------------------------------------*/
static err_t
low_level_output(struct netif *netif, struct pbuf *p)
{
struct pbuf *q;
mcf5272if_t *mcf5272 = netif->state;
MCF5272_IMM *imm = mcf5272->imm;
int num_desc;
int num_free;
unsigned int tx_insert_sof, tx_insert_eof;
unsigned int i;
u32_t old_level;
/* Make sure that there are no PBUF_REF buffers in the chain. These buffers
have to be freed immediately and this ethernet driver puts the buffers on
the dma chain, so they get freed later */
p = pbuf_take(p);
/* Interrupts are disabled through this whole thing to support multi-threading
* transmit calls. Also this function might be called from an ISR. */
old_level = sys_arch_protect();
/* Determine number of descriptors needed */
num_desc = pbuf_clen(p);
if (num_desc > mcf5272->tx_free)
{
/* Drop the frame, we have no place to put it */
#ifdef LINK_STATS
lwip_stats.link.memerr++;
#endif
sys_arch_unprotect(old_level);
return ERR_MEM;
} else {
/* Increment use count on pbuf */
pbuf_ref(p);
/* Put buffers on descriptor ring, but don't mark them as ready yet */
tx_insert_eof = tx_insert_sof = mcf5272->tx_insert;
q = p;
do
{
mcf5272->tx_free--;
mcf5272->tx_pbuf_a[tx_insert_eof] = q;
mcf5272->txbd_a[tx_insert_eof].p_buf = q->payload;
mcf5272->txbd_a[tx_insert_eof].data_len = q->len;
q = q->next;
if (q)
INC_TX_BD_INDEX(tx_insert_eof);
} while (q);
/* Go backwards through descriptor ring setting flags */
i = tx_insert_eof;
do
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -