if_ppc405.c
来自「开放源码实时操作系统源码.」· C语言 代码 · 共 688 行 · 第 1/2 页
C
688 行
//==========================================================================
//
// dev/if_ppc405.c
//
// Ethernet device driver for PowerPC PPC405 boards
//
//==========================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
// Copyright (C) 2002, 2003, 2005 Gary Thomas
//
// eCos is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 or (at your option) any later version.
//
// eCos is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//
// You should have received a copy of the GNU General Public License along
// with eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//==========================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s): gthomas
// Contributors: gthomas
// Date: 2003-08-15
// Purpose:
// Description: hardware driver for PPC405
//
//
//####DESCRIPTIONEND####
//
//==========================================================================
// Ethernet device driver for PPC405
#include <pkgconf/system.h>
#include <pkgconf/devs_eth_powerpc_ppc405.h>
#include <pkgconf/io_eth_drivers.h>
#ifdef CYGPKG_NET
#include <pkgconf/net.h>
#endif
#include <cyg/infra/cyg_type.h>
#include <cyg/infra/diag.h>
#include <cyg/hal/hal_arch.h>
#include <cyg/hal/hal_cache.h>
#include <cyg/hal/hal_intr.h>
#include <cyg/hal/drv_api.h>
#include <cyg/hal/hal_if.h>
#include <cyg/hal/ppc_regs.h>
#include <cyg/io/eth/netdev.h>
#include <cyg/io/eth/eth_drv.h>
#include <cyg/io/eth_phy.h>
//
// PHY access functions
//
static void ppc405_eth_phy_init(void);
static void ppc405_eth_phy_put_reg(int reg, int phy, unsigned short data);
static bool ppc405_eth_phy_get_reg(int reg, int phy, unsigned short *val);
#include "ppc405_enet.h"
#include CYGDAT_DEVS_PPC405_ETH_INL
#define os_printf diag_printf
// For fetching the ESA from RedBoot
#include <cyg/hal/hal_if.h>
#ifndef CONFIG_ESA
#define CONFIG_ESA 6
#endif
static void ppc405_eth_int(struct eth_drv_sc *data);
#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
static cyg_interrupt ppc405_emac_interrupt;
static cyg_handle_t ppc405_emac_interrupt_handle;
static cyg_interrupt ppc405_mal_txeob_interrupt;
static cyg_handle_t ppc405_mal_txeob_interrupt_handle;
static cyg_interrupt ppc405_mal_rxeob_interrupt;
static cyg_handle_t ppc405_mal_rxeob_interrupt_handle;
static cyg_interrupt ppc405_mal_txde_interrupt;
static cyg_handle_t ppc405_mal_txde_interrupt_handle;
static cyg_interrupt ppc405_mal_rxde_interrupt;
static cyg_handle_t ppc405_mal_rxde_interrupt_handle;
static cyg_interrupt ppc405_mal_serr_interrupt;
static cyg_handle_t ppc405_mal_serr_interrupt_handle;
#define EMAC_INTERRUPT_HANDLER(_int_,_hdlr_) \
cyg_drv_interrupt_create(_int_, \
0, \
(cyg_addrword_t)sc, /* Data item passed to interrupt handler */ \
(cyg_ISR_t *)ppc405_eth_isr, \
(cyg_DSR_t *)eth_drv_dsr, \
&ppc405_##_hdlr_##_interrupt_handle, \
&ppc405_##_hdlr_##_interrupt); \
cyg_drv_interrupt_attach(ppc405_##_hdlr_##_interrupt_handle); \
cyg_drv_interrupt_acknowledge(_int_); \
cyg_drv_interrupt_unmask(_int_);
// This ISR is called when the ethernet interrupt occurs
static int
ppc405_eth_isr(cyg_vector_t vector, cyg_addrword_t data, HAL_SavedRegisters *regs)
{
struct eth_drv_sc *sc = (struct eth_drv_sc *)data;
struct ppc405_eth_info *qi = (struct ppc405_eth_info *)sc->driver_private;
cyg_drv_interrupt_mask(CYGNUM_HAL_INTERRUPT_MAL_SERR);
cyg_drv_interrupt_mask(CYGNUM_HAL_INTERRUPT_MAL_TX_EOB);
cyg_drv_interrupt_mask(CYGNUM_HAL_INTERRUPT_MAL_RX_EOB);
cyg_drv_interrupt_mask(CYGNUM_HAL_INTERRUPT_MAL_TX_DE);
cyg_drv_interrupt_mask(CYGNUM_HAL_INTERRUPT_MAL_RX_DE);
cyg_drv_interrupt_mask(CYGNUM_HAL_INTERRUPT_EMAC0);
qi->ints = vector;
return (CYG_ISR_HANDLED|CYG_ISR_CALL_DSR); // Run the DSR
}
#endif
// Deliver function (ex-DSR) handles the ethernet [logical] processing
static void
ppc405_eth_deliver(struct eth_drv_sc *sc)
{
#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
struct ppc405_eth_info *qi = (struct ppc405_eth_info *)sc->driver_private;
cyg_uint32 old_ints;
#endif
ppc405_eth_int(sc);
#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIRED
// Allow interrupts to happen again
HAL_DISABLE_INTERRUPTS(old_ints);
cyg_drv_interrupt_acknowledge(qi->ints);
cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_MAL_SERR);
cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_MAL_TX_EOB);
cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_MAL_RX_EOB);
cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_MAL_TX_DE);
cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_MAL_RX_DE);
cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_EMAC0);
HAL_RESTORE_INTERRUPTS(old_ints);
#endif
}
//
// PHY unit access
//
static void
ppc405_eth_phy_init(void)
{
// Set up MII hardware - nothing to do on this platform
}
static void
ppc405_eth_phy_put_reg(int reg, int phy, unsigned short data)
{
unsigned long reg_val;
reg_val = EMAC0_STACR_STAC_WRITE | EMAC0_STACR_OPBC_66;
reg_val |= (phy << EMAC0_STACR_PCDA_SHIFT) | reg;
reg_val |= (data << EMAC0_STACR_PHYD_SHIFT);
#ifdef PHY_DEBUG
os_printf("PHY PUT - reg: %d, phy: %d, val: %04x [%08x]\n", reg, phy, data, reg_val);
#endif
while ((EMAC0_STACR & EMAC0_STACR_OC) == 0) ; // Wait for MII free
EMAC0_STACR = reg_val;
while ((EMAC0_STACR & EMAC0_STACR_OC) == 0) ; // Wait for MII complete
}
static bool
ppc405_eth_phy_get_reg(int reg, int phy, unsigned short *val)
{
unsigned long reg_val;
reg_val = EMAC0_STACR_STAC_READ | EMAC0_STACR_OPBC_66;
reg_val |= (phy << EMAC0_STACR_PCDA_SHIFT) | reg;
#ifdef PHY_DEBUG
os_printf("PHY GET - reg: %d, phy: %d [%08x] = ", reg, phy, reg_val);
#endif
while ((EMAC0_STACR & EMAC0_STACR_OC) == 0) ; // Wait for MII free
EMAC0_STACR = reg_val;
while ((EMAC0_STACR & EMAC0_STACR_OC) == 0) ; // Wait for MII complete
if ((EMAC0_STACR & EMAC0_STACR_PHYE) == 0) {
// Operation completed with no error
*val = (EMAC0_STACR & EMAC0_STACR_PHYD) >> EMAC0_STACR_PHYD_SHIFT;
#ifdef PHY_DEBUG
os_printf("%04x\n", *val);
#endif
return true;
} else {
// No response
#ifdef PHY_DEBUG
os_printf("***ERROR***\n");
#endif
return false;
}
}
//
// [re]Initialize the ethernet controller
// Done separately since shutting down the device requires a
// full reconfiguration when re-enabling.
// when
static bool
ppc405_eth_reset(struct eth_drv_sc *sc, unsigned char *enaddr, int flags)
{
struct ppc405_eth_info *qi = (struct ppc405_eth_info *)sc->driver_private;
volatile mal_bd_t *rxbd, *RxBD, *txbd, *TxBD;
unsigned char *RxBUF, *TxBUF;
int i, int_state;
unsigned long mal_status, mode;
unsigned short phy_state = 0;
// Ignore unless device is idle/stopped
if ((EMAC0_MR0 & (EMAC0_MR0_RXI|EMAC0_MR0_TXI)) != (EMAC0_MR0_RXI|EMAC0_MR0_TXI)) {
return true;
}
// Make sure interrupts are off while we mess with the device
HAL_DISABLE_INTERRUPTS(int_state);
// Reset EMAC controller
EMAC0_MR0 |= EMAC0_MR0_SRST;
i = 0;
while ((EMAC0_MR0 & EMAC0_MR0_SRST) != 0) {
if (++i >= 500000) {
os_printf("PPC405 Ethernet does not reset\n");
HAL_RESTORE_INTERRUPTS(int_state);
return false;
}
}
TxBD = qi->txbd_table;
txbd = (mal_bd_t *)CYGARC_UNCACHED_ADDRESS(TxBD);
RxBD = qi->rxbd_table;
rxbd = (mal_bd_t *)CYGARC_UNCACHED_ADDRESS(RxBD);
qi->tbase = qi->txbd = qi->tnext = txbd;
qi->rbase = qi->rxbd = qi->rnext = rxbd;
qi->txactive = 0;
RxBUF = qi->rxbuf;
TxBUF = qi->txbuf;
// setup buffer descriptors
for (i = 0; i < CYGNUM_DEVS_ETH_POWERPC_PPC405_RxNUM; i++) {
rxbd->length = 0;
rxbd->buffer = (unsigned long)RxBUF;
rxbd->status = MAL_BD_R | MAL_BD_I;
RxBUF += CYGNUM_DEVS_ETH_POWERPC_PPC405_BUFSIZE;
rxbd++;
}
rxbd--;
rxbd->status |= MAL_BD_W; // Last buffer
for (i = 0; i < CYGNUM_DEVS_ETH_POWERPC_PPC405_TxNUM; i++) {
txbd->length = 0;
txbd->buffer = (unsigned long)TxBUF;
txbd->status = 0;
TxBUF += CYGNUM_DEVS_ETH_POWERPC_PPC405_BUFSIZE;
txbd++;
}
txbd--;
txbd->status |= MAL_BD_W; // Last buffer
// Tell memory access layer where the buffer descriptors are
CYGARC_MTDCR(MAL0_TXCARR, MAL_CASR_C0|MAL_CASR_C1); // Disable/reset channel #0 & #1
CYGARC_MTDCR(MAL0_RXCARR, MAL_CASR_C0); // Disable/reset channel #0
CYGARC_MTDCR(MAL0_CFG, MAL_CFG_SR);
i = 0;
CYGARC_MFDCR(MAL0_CFG, mal_status);
while ((mal_status & MAL_CFG_SR) != 0) {
if (++i >= 500000) {
os_printf("PPC405 MAL does not reset\n");
HAL_RESTORE_INTERRUPTS(int_state);
return false;
}
}
CYGARC_MTDCR(MAL0_CFG, MAL_CFG_PLBB | MAL_CFG_OPBBL | MAL_CFG_LEA | MAL_CFG_PLBT_DEFAULT);
CYGARC_MTDCR(MAL0_TXCTP0R, TxBD);
CYGARC_MTDCR(MAL0_RXCTP0R, RxBD);
CYGARC_MTDCR(MAL0_RXBS0, (CYGNUM_DEVS_ETH_POWERPC_PPC405_BUFSIZE/16)); // Receive buffer size
// Set device physical address (ESA)
EMAC0_IAHR = (enaddr[0]<<8) | (enaddr[1]<<0);
EMAC0_IALR = (enaddr[2]<<24) | (enaddr[3]<<16) | (enaddr[4]<<8) | (enaddr[5]<<0);
// Operating mode
if (!_eth_phy_init(qi->phy)) {
return false;
}
phy_state = _eth_phy_state(qi->phy);
os_printf("PPC405 ETH: ");
mode = EMAC0_MR1_RFS_4096 | EMAC0_MR1_TFS_2048 | EMAC0_MR1_TR0_MULTI | EMAC0_MR1_APP;
if ((phy_state & ETH_PHY_STAT_LINK) != 0) {
if ((phy_state & ETH_PHY_STAT_100MB) != 0) {
// Link can handle 100Mb
mode |= EMAC0_MR1_MF_100MB;
os_printf("100Mb");
if ((phy_state & ETH_PHY_STAT_FDX) != 0) {
mode |= EMAC0_MR1_FDE | EMAC0_MR1_EIFC | EMAC0_MR1_IST;
os_printf("/Full Duplex");
}
} else {
// Assume 10Mb, half duplex
mode |= EMAC0_MR1_MF_10MB;
os_printf("10Mb");
}
} else {
os_printf("/***NO LINK***");
return false;
}
os_printf("\n");
EMAC0_MR1 = mode;
// Configure receiver
EMAC0_RMR = EMAC0_RMR_IAE | EMAC0_RMR_BAE | EMAC0_RMR_RRP | EMAC0_RMR_RFP;
// Transmit threshold to 256 bytes
EMAC0_TRTR = ((256/EMAC0_TRTR_TRT_SCALE)-1) << EMAC0_TRTR_TRT_SHIFT;
// Receive FIFO watermarks
EMAC0_RWMR = (0x1F<<EMAC0_RWMR_RLWM_SHIFT) | (0x10<<EMAC0_RWMR_RHWM_SHIFT);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?