if_ppc405.c

来自「eCos操作系统源码」· C语言 代码 · 共 673 行 · 第 1/2 页

C
673
字号
//==========================================================================////      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 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#endifstatic void          ppc405_eth_int(struct eth_drv_sc *data);#ifdef CYGINT_IO_ETH_INT_SUPPORT_REQUIREDstatic 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 occursstatic intppc405_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] processingstatic voidppc405_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 boolppc405_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;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?