if_ppc405.c
来自「开放源码实时操作系统源码.」· C语言 代码 · 共 688 行 · 第 1/2 页
C
688 行
// Frame gap
EMAC0_IPGVR = 8;
// Enable MAL
CYGARC_MTDCR(MAL0_TXCASR, MAL_CASR_C0);
CYGARC_MTDCR(MAL0_RXCASR, MAL_CASR_C0);
// Reset all interrupts
EMAC0_ISR = 0xFFFFFFFF;
#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
qi->ints = 0;
#endif
// Enable interface
EMAC0_MR0 |= (EMAC0_MR0_RXE|EMAC0_MR0_TXE);
// Restore interrupts
HAL_RESTORE_INTERRUPTS(int_state);
return true;
}
//
// Initialize the interface - performed at system startup
// This function must set up the interface, including arranging to
// handle interrupts, etc, so that it may be "started" cheaply later.
//
static bool
ppc405_eth_init(struct cyg_netdevtab_entry *tab)
{
struct eth_drv_sc *sc = (struct eth_drv_sc *)tab->device_instance;
struct ppc405_eth_info *qi = (struct ppc405_eth_info *)sc->driver_private;
bool esa_ok;
unsigned char enaddr[6];
ppc405_eth_stop(sc); // Make sure it's not running yet
#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
// Set up to handle interrupts
EMAC_INTERRUPT_HANDLER(CYGNUM_HAL_INTERRUPT_MAL_SERR, mal_serr);
EMAC_INTERRUPT_HANDLER(CYGNUM_HAL_INTERRUPT_MAL_TX_EOB, mal_txeob);
EMAC_INTERRUPT_HANDLER(CYGNUM_HAL_INTERRUPT_MAL_RX_EOB, mal_rxeob);
EMAC_INTERRUPT_HANDLER(CYGNUM_HAL_INTERRUPT_MAL_TX_DE, mal_txde);
EMAC_INTERRUPT_HANDLER(CYGNUM_HAL_INTERRUPT_MAL_RX_DE, mal_rxde);
EMAC_INTERRUPT_HANDLER(CYGNUM_HAL_INTERRUPT_EMAC0, emac);
#endif
// Get physical device address
#ifdef CYGPKG_REDBOOT
#ifdef CYGSEM_REDBOOT_FLASH_CONFIG
esa_ok = flash_get_config(qi->esa_key, enaddr, CONFIG_ESA);
#else
esa_ok = false;
#endif
#else
esa_ok = CYGACC_CALL_IF_FLASH_CFG_OP(CYGNUM_CALL_IF_FLASH_CFG_GET,
qi->esa_key, enaddr, CONFIG_ESA);
#endif
if (!esa_ok) {
// Can't figure out ESA
os_printf("PPC405_ETH - Warning! ESA unknown\n");
memcpy(&enaddr, qi->enaddr, sizeof(enaddr));
}
memcpy(qi->cfg_enaddr, enaddr, sizeof(enaddr));
// Configure the device
if (!ppc405_eth_reset(sc, enaddr, 0)) {
return false;
}
// Initialize upper level driver
(sc->funs->eth_drv->init)(sc, (unsigned char *)&enaddr);
return true;
}
//
// This function is called to shut down the interface.
//
static void
ppc405_eth_stop(struct eth_drv_sc *sc)
{
EMAC0_MR0 &= ~(EMAC0_MR0_RXE|EMAC0_MR0_TXE);
}
//
// This function is called to "start up" the interface. It may be called
// multiple times, even when the hardware is already running. It will be
// called whenever something "hardware oriented" changes and should leave
// the hardware ready to send/receive packets.
//
static void
ppc405_eth_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
{
EMAC0_MR0 |= (EMAC0_MR0_RXE|EMAC0_MR0_TXE);
}
//
// This function is called for low level "control" operations
//
static int
ppc405_eth_control(struct eth_drv_sc *sc, unsigned long key,
void *data, int length)
{
os_printf("%s.%d\n", __FUNCTION__, __LINE__);
return 1;
#if 0
#ifdef ETH_DRV_SET_MC_ALL
struct ppc405_eth_info *qi = (struct ppc405_eth_info *)sc->driver_private;
volatile struct ppc405 *ppc405 = qi->ppc405;
#endif
switch (key) {
case ETH_DRV_SET_MAC_ADDRESS:
return 0;
break;
#ifdef ETH_DRV_SET_MC_ALL
case ETH_DRV_SET_MC_ALL:
case ETH_DRV_SET_MC_LIST:
ppc405->RxControl &= ~RxControl_PROM;
ppc405->hash[0] = 0xFFFFFFFF;
ppc405->hash[1] = 0xFFFFFFFF;
return 0;
break;
#endif
default:
return 1;
break;
}
#endif
}
//
// This function is called to see if another packet can be sent.
// It should return the number of packets which can be handled.
// Zero should be returned if the interface is busy and can not send any more.
//
static int
ppc405_eth_can_send(struct eth_drv_sc *sc)
{
struct ppc405_eth_info *qi = (struct ppc405_eth_info *)sc->driver_private;
return (qi->txactive < CYGNUM_DEVS_ETH_POWERPC_PPC405_TxNUM);
}
//
// This routine is called to send data to the hardware.
static void
ppc405_eth_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len,
int total_len, unsigned long key)
{
struct ppc405_eth_info *qi = (struct ppc405_eth_info *)sc->driver_private;
volatile mal_bd_t *txbd;
volatile char *bp;
int i, txindex;
// Find a free buffer
txbd = qi->txbd;
// Set up buffer
bp = (volatile char *)CYGARC_UNCACHED_ADDRESS(txbd->buffer);
for (i = 0; i < sg_len; i++) {
memcpy((void *)bp, (void *)sg_list[i].buf, sg_list[i].len);
bp += sg_list[i].len;
}
txbd->length = total_len;
txindex = ((unsigned long)txbd - (unsigned long)qi->tbase) / sizeof(*txbd);
qi->txkey[txindex] = key;
// Send it on it's way
txbd->status = (txbd->status & MAL_BD_W) | MAL_BD_R | MAL_BD_L | MAL_BD_I |
MAL_BD_TX_GFCS | MAL_BD_TX_GPAD;
qi->txactive++;
EMAC0_TMR0 = EMAC0_TMR0_GNP0; // Start channel 0
// Remember the next buffer to try
if (txbd->status & MAL_BD_W) {
qi->txbd = qi->tbase;
} else {
qi->txbd = txbd+1;
}
}
//
// This function is called when a packet has been received. It's job is
// to prepare to unload the packet from the hardware. Once the length of
// the packet is known, the upper layer of the driver can be told. When
// the upper layer is ready to unload the packet, the internal function
// 'ppc405_eth_recv' will be called to actually fetch it from the hardware.
//
static void
ppc405_eth_RxEvent(struct eth_drv_sc *sc)
{
struct ppc405_eth_info *qi = (struct ppc405_eth_info *)sc->driver_private;
volatile mal_bd_t *rxbd, *rxfirst;
rxbd = rxfirst = qi->rnext;
while ((rxbd->status & MAL_BD_R) == 0) {
qi->rxbd = rxbd; // Save for callback
(sc->funs->eth_drv->recv)(sc, rxbd->length-4); // Adjust for FCS
if (rxbd->status & MAL_BD_W) {
rxbd = qi->rbase;
} else {
rxbd++;
}
}
// Remember where we left off
qi->rnext = (mal_bd_t *)rxbd;
}
//
// This function is called as a result of the "eth_drv_recv()" call above.
// It's job is to actually fetch data for a packet from the hardware once
// memory buffers have been allocated for the packet. Note that the buffers
// may come in pieces, using a scatter-gather list. This allows for more
// efficient processing in the upper layers of the stack.
//
static void
ppc405_eth_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len)
{
struct ppc405_eth_info *qi = (struct ppc405_eth_info *)sc->driver_private;
unsigned char *bp;
int i;
bp = (unsigned char *)CYGARC_UNCACHED_ADDRESS(qi->rxbd->buffer);
for (i = 0; i < sg_len; i++) {
if (sg_list[i].buf != 0) {
memcpy((void *)sg_list[i].buf, bp, sg_list[i].len);
bp += sg_list[i].len;
}
}
qi->rxbd->status = (qi->rxbd->status & (MAL_BD_W|MAL_BD_I)) | MAL_BD_R;
}
static void
ppc405_eth_TxEvent(struct eth_drv_sc *sc)
{
struct ppc405_eth_info *qi = (struct ppc405_eth_info *)sc->driver_private;
volatile mal_bd_t *txbd;
int key, txindex;
txbd = qi->tnext;
while ((txbd->status & (MAL_BD_R|MAL_BD_I)) == MAL_BD_I) {
txindex = ((unsigned long)txbd - (unsigned long)qi->tbase) / sizeof(*txbd);
if ((key = qi->txkey[txindex]) != 0) {
qi->txkey[txindex] = 0;
(sc->funs->eth_drv->tx_done)(sc, key, 0);
}
qi->txactive -= 1;
txbd->status &= MAL_BD_W; // Only preserve wrap bit
if ((txbd->status & MAL_BD_W) != 0) {
txbd = qi->tbase;
} else {
txbd++;
}
if (txbd == qi->tnext) {
break; // Went through whole list
}
}
// Remember where we left off
qi->tnext = (mal_bd_t *)txbd;
}
//
// Interrupt processing
//
int dump_mal0_esr = 0;
static void
ppc405_eth_int(struct eth_drv_sc *sc)
{
struct ppc405_eth_info *qi = (struct ppc405_eth_info *)sc->driver_private;
unsigned long event, tx_event, rx_event, tx_deir, rx_deir;
bool need_reset = false;
CYGARC_MFDCR(MAL0_TXEOBISR, tx_event);
if (tx_event != 0) {
ppc405_eth_TxEvent(sc);
CYGARC_MTDCR(MAL0_TXEOBISR, tx_event);
}
CYGARC_MFDCR(MAL0_RXEOBISR, rx_event);
if (rx_event != 0) {
ppc405_eth_RxEvent(sc);
CYGARC_MTDCR(MAL0_RXEOBISR, rx_event);
}
if ((event = EMAC0_ISR) != 0) {
if ((event & ~(EMAC0_ISR_SE0|EMAC0_ISR_SE1)) != 0) {
// Error other than signal quality
os_printf("EMAC0_ISR: %x\n", event);
if ((event & (EMAC0_ISR_TE0|EMAC0_ISR_TE1)) != 0) {
// Some problem with transmit - should be easily recoverable
CYGARC_MTDCR(MAL0_TXCASR, MAL_CASR_C0);
qi->tnext = qi->tbase;
}
if ((event & (EMAC0_ISR_OVR|EMAC0_ISR_BP|EMAC0_ISR_RP|EMAC0_ISR_ALE|EMAC0_ISR_BFCS)) != 0) {
// Rx errors - reset device
need_reset = true;
}
}
EMAC0_ISR = event; // Reset the bits we handled
}
CYGARC_MFDCR(MAL0_ESR, event);
if ((event & MAL_ESR_INT_MASK) != 0) {
CYGARC_MFDCR(MAL0_TXDEIR, tx_deir);
CYGARC_MFDCR(MAL0_RXDEIR, rx_deir);
if (dump_mal0_esr) {
os_printf("MAL0_ESR: %x, Tx: %x, Rx: %x\n", event, tx_deir, rx_deir);
os_printf("Tx buffer headers\n");
diag_dump_buf((void *)qi->tbase, qi->txnum*sizeof(mal_bd_t));
os_printf("Rx buffer headers\n");
diag_dump_buf((void *)qi->rbase, qi->rxnum*sizeof(mal_bd_t));
}
if (tx_deir != 0) {
// Fix Tx descriptor problems
CYGARC_MTDCR(MAL0_TXDEIR, tx_deir); // Clear interrupt indicator
CYGARC_MTDCR(MAL0_TXCASR, MAL_CASR_C0);
qi->tnext = qi->tbase;
}
if (rx_deir != 0) {
// Fix Rx descriptor problems
CYGARC_MTDCR(MAL0_RXDEIR, rx_deir); // Clear interrupt indicator
CYGARC_MTDCR(MAL0_RXCASR, MAL_CASR_C0);
qi->rnext = qi->rbase;
}
CYGARC_MTDCR(MAL0_ESR, event); // Clear events just handled
}
if (need_reset) {
// Something has gone awry - try resetting the device
os_printf("\n... PPC405 ethernet - hard reset after failure\n");
ppc405_eth_stop(sc);
if (!ppc405_eth_reset(sc, qi->cfg_enaddr, 0)) {
os_printf("!! Failed? !!\n");
}
}
}
//
// Interrupt vector
//
static int
ppc405_eth_int_vector(struct eth_drv_sc *sc)
{
struct ppc405_eth_info *qi = (struct ppc405_eth_info *)sc->driver_private;
return qi->int_vector;
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?