if_atlas.c

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

C
1,055
字号
//==========================================================================////      dev/if_atlas.c////      Ethernet device driver for MIPS Atlas using Philips SAA9730////==========================================================================//####ECOSGPLCOPYRIGHTBEGIN####// -------------------------------------------// This file is part of eCos, the Embedded Configurable Operating System.// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.// Copyright (C) 2003 Nick Garnett <nickg@calivar.com>//// 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 the copyright// holders.// -------------------------------------------//####ECOSGPLCOPYRIGHTEND####//####BSDCOPYRIGHTBEGIN####//// -------------------------------------------//// Portions of this software may have been derived from OpenBSD or other sources,// and are covered by the appropriate copyright disclaimers included herein.//// -------------------------------------------////####BSDCOPYRIGHTEND####//==========================================================================//#####DESCRIPTIONBEGIN####//// Author(s):    msalter// Contributors: msalter, nickg// Date:         2000-12-06// Purpose:      // Description:  hardware driver for SAA9730 ethernet//              ////####DESCRIPTIONEND####////==========================================================================// Ethernet device driver for MIPS Atlas// Based on SAA9730#include <pkgconf/system.h>#include <pkgconf/devs_eth_mips_atlas.h>#include <pkgconf/io_eth_drivers.h>#ifdef CYGPKG_NET#include <pkgconf/net.h>#include <cyg/kernel/kapi.h>#endif#include <cyg/infra/cyg_type.h>#include <cyg/hal/hal_arch.h>#include <cyg/hal/hal_endian.h>#include <cyg/hal/hal_intr.h>#include <cyg/hal/hal_cache.h>#include <cyg/hal/hal_if.h>#include <cyg/infra/diag.h>#include <cyg/hal/drv_api.h>#include <cyg/io/eth/netdev.h>#include <cyg/io/eth/eth_drv.h>#ifdef CYGPKG_IO_PCI#include <cyg/io/pci.h>// So we can check the validity of the PCI window against the MLTs opinion,// and thereby what the malloc heap consumes willy-nilly:#include CYGHWR_MEMORY_LAYOUT_H#else#error "Need PCI package here"#endif#ifndef CYGSEM_MIPS_ATLAS_SET_ESA#ifdef CYGPKG_REDBOOT#include <pkgconf/redboot.h>#ifdef CYGSEM_REDBOOT_FLASH_CONFIG#include <redboot.h>#include <flash_config.h>RedBoot_config_option("Network hardware address [MAC]",                      atlas_esa,                      ALWAYS_ENABLED, true,                      CONFIG_ESA, 0    );#endif#endif#endif// SAA9730 LAN definitions#include "saa9730.h"// Exported statistics and the like#include <cyg/io/eth/eth_drv_stats.h>#ifndef CYGPKG_REDBOOT//#define DEBUG#endif#define db_printf diag_printf#define ETHER_ADDR_LEN 6static unsigned poll_count = 0;  // for bug workaroundstatic void __tx_poll(struct eth_drv_sc *sc);struct saa9730_priv_data {    int			active;    cyg_uint32		vector;    cyg_handle_t  	interrupt_handle;    cyg_interrupt	interrupt_object;    cyg_uint32		devid;             // PCI device id    cyg_uint32 		base;              // PCI memory map base    void                *ndp;    // index of next RX buffer    cyg_uint8		next_rx_bindex;    // index of next packet within RX buffer    cyg_uint8		next_rx_pindex;    // index of next TX buffer    cyg_uint8		next_tx_bindex;    // index of next packet within TX buffer    cyg_uint8		next_tx_pindex;	    cyg_uint32		*tx_buffer[SAA9730_BUFFERS][SAA9730_TXPKTS_PER_BUFFER];    cyg_uint32		*rx_buffer[SAA9730_BUFFERS][SAA9730_RXPKTS_PER_BUFFER];    int                 tx_busy;    unsigned long	tx_key[SAA9730_BUFFERS][SAA9730_TXPKTS_PER_BUFFER];    int			tx_used[SAA9730_BUFFERS];} saa9730_priv_data;ETH_DRV_SC(atlas_sc,           &saa9730_priv_data, // Driver specific data           "eth0",             // Name for this interface           saa9730_start,           saa9730_stop,           saa9730_control,           saa9730_can_send,           saa9730_send,           saa9730_recv,           saa9730_deliver,     // "pseudoDSR" called from fast net thread           saa9730_poll,           saa9730_int_vector);NETDEVTAB_ENTRY(atlas_netdev,                 "atlas",                 atlas_saa9730_init,                 &atlas_sc);#ifdef CYGSEM_MIPS_ATLAS_SET_ESAstatic unsigned char enaddr[] = CYGDAT_MIPS_ATLAS_ESA;#elsestatic unsigned char enaddr[ETHER_ADDR_LEN];#endifstatic void saa9730_poll(struct eth_drv_sc *sc);// This ISR is called when the ethernet interrupt occursstatic intsaa9730_isr(cyg_vector_t vector, cyg_addrword_t data){    struct saa9730_priv_data *spd = (struct saa9730_priv_data *)data;    unsigned long __base = spd->base;    #ifndef CYGPKG_REDBOOT        SAA9730_EVM_IER_SW &= ~(SAA9730_EVM_LAN_INT|SAA9730_EVM_MASTER);    SAA9730_EVM_ISR = SAA9730_EVM_LAN_INT;    cyg_drv_interrupt_mask(vector);#endif#ifdef DEBUG    db_printf("saa9730_isr\n");#endif    return (CYG_ISR_HANDLED|CYG_ISR_CALL_DSR);  // Run the DSR}#ifndef CYGPKG_REDBOOTstatic void saa9730_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data){    struct saa9730_priv_data *spd = (struct saa9730_priv_data *)data;    struct cyg_netdevtab_entry *ndp = (struct cyg_netdevtab_entry *)(spd->ndp);    struct eth_drv_sc *sc = (struct eth_drv_sc *)(ndp->device_instance);#ifdef DEBUG    db_printf("saa9730_dsr\n");    #endif    eth_drv_dsr(vector, count, (cyg_addrword_t)sc);}#endifstatic intsaa9730_int_vector(struct eth_drv_sc *sc){    struct saa9730_priv_data *spd = (struct saa9730_priv_data *)sc->driver_private;    return spd->vector;}static void__init_buffers(struct saa9730_priv_data *spd){    extern char cyg_io_atlas_2kbuffers[];    cyg_uint32 *bufp = (cyg_uint32 *)CYGARC_UNCACHED_ADDRESS((unsigned)cyg_io_atlas_2kbuffers);    int i, j;    for (i = 0; i < SAA9730_BUFFERS; i++) {        for (j = 0; j < SAA9730_RXPKTS_PER_BUFFER; j++) {	    memset(bufp, 0, 2048);            spd->rx_buffer[i][j]   = bufp;            bufp += SAA9730_PACKET_SIZE/sizeof(*bufp);        }    }    for (i = 0; i < SAA9730_BUFFERS; i++) {        for (j = 0; j < SAA9730_TXPKTS_PER_BUFFER; j++) {	    memset(bufp, 0, 2048);	    *bufp = CYG_CPU_TO_LE32(TX_EMPTY);            spd->tx_buffer[i][j] = bufp;            bufp += SAA9730_PACKET_SIZE/sizeof(*bufp);	}    }    spd->next_rx_pindex = 0;    spd->next_rx_bindex = 0;    spd->next_tx_pindex = 0;    spd->next_tx_bindex = 0;}static void__select_buffer(struct saa9730_priv_data *spd, int buf_nr){    unsigned long __base = spd->base;    cyg_uint32 *p;    int i;    // Enable RX buffer    for (i = 0; i < SAA9730_RXPKTS_PER_BUFFER; i++) {        p = spd->rx_buffer[buf_nr][i];        *p = CYG_CPU_TO_LE32(RX_READY);    }    if (buf_nr)        SAA9730_OK2USE |= SAA9730_OK2USE_RXB;    else        SAA9730_OK2USE |= SAA9730_OK2USE_RXA;}static void__init_cam(struct saa9730_priv_data *spd){    unsigned long __base = spd->base;    cyg_uint32 abuf[3];  // room for 2 copies of mac address    int i,j,cam_offset;    // make 2 contiguous copies of mac addr    memcpy((char *)abuf, enaddr, 6);    memcpy((char *)abuf + 6, enaddr, 6);    // Setting up the address compare regs is weird because you have    // to access by word addresses even though addresses don't have    // an integral number of words.    cam_offset = 0;    for (i = 0; i < SAA9730_CAM_ENTRIES; i++) {	for (j = 0; j < 3; j++, cam_offset++) {	    SAA9730_CAMADR = cam_offset;	    SAA9730_CAMDAT = CYG_CPU_TO_BE32(abuf[j]);	}    }}static void__stop_dma(struct saa9730_priv_data *spd){    unsigned long __base = spd->base;    // Stop DMA    SAA9730_DMACTL &= ~(SAA9730_DMACTL_ENRX | SAA9730_DMACTL_ENTX);    // Stop tx/rx    SAA9730_TXCTL &= ~SAA9730_TXCTL_ENTX;    SAA9730_RXCTL &= ~SAA9730_RXCTL_ENRX;    // Set DMA and MAC reset bits    SAA9730_DMATST |= SAA9730_DMATST_RESET;    SAA9730_MACCTL |= SAA9730_MACCTL_RESET;}static void__init_dma(struct saa9730_priv_data *spd){    unsigned long __base = spd->base;    __stop_dma(spd);    // reset DMA engine    SAA9730_DMATST |= SAA9730_DMATST_RESET;    // setup buffers    SAA9730_TXBUFA = CYGARC_PHYSICAL_ADDRESS((unsigned long)spd->tx_buffer[0][0]);    SAA9730_TXBUFB = CYGARC_PHYSICAL_ADDRESS((unsigned long)spd->tx_buffer[1][0]);    SAA9730_RXBUFA = CYGARC_PHYSICAL_ADDRESS((unsigned long)spd->rx_buffer[0][0]);    SAA9730_RXBUFB = CYGARC_PHYSICAL_ADDRESS((unsigned long)spd->rx_buffer[1][0]);    SAA9730_PKTCNT = ((SAA9730_TXPKTS_PER_BUFFER << 24) |		      (SAA9730_TXPKTS_PER_BUFFER << 16) |		      (SAA9730_RXPKTS_PER_BUFFER <<  8) |		      (SAA9730_RXPKTS_PER_BUFFER <<  0));    SAA9730_OK2USE = 0;    __select_buffer(spd, 0);    // initialize DMA control register    SAA9730_DMACTL = SAA9730_DMACTL_BLKINT |	             SAA9730_DMACTL_MAXXFER_ANY |	             SAA9730_DMACTL_ENDIAN_LITTLE;    SAA9730_DMACTL |= SAA9730_DMACTL_RXINT;    SAA9730_DMACTL |= (1<<SAA9730_DMACTL_RXINTCNT_SHIFT);    SAA9730_DMACTL &= ~SAA9730_DMACTL_BLKINT;#ifndef CYGPKG_REDBOOT    SAA9730_DMACTL |= SAA9730_DMACTL_TXINT;#endif        SAA9730_TIMOUT = 200;    // accept broadcast packets */    SAA9730_CAMCTL = SAA9730_CAMCTL_BROADCAST |	             SAA9730_CAMCTL_COMPARE;    SAA9730_TXCTL = 0;    SAA9730_RXCTL |= SAA9730_RXCTL_STRIPCRC;    SAA9730_CAMENA = 1;}static void__check_mii(struct saa9730_priv_data *spd){    unsigned long __base = spd->base;    cyg_uint32 opmode;#ifdef DEBUG    db_printf("__check_mii\n");    #endif        // spin till station is not busy    while (SAA9730_MDCTL & SAA9730_MDCTL_BUSY)	;    // set PHY address = 'STATUS'    SAA9730_MDCTL = SAA9730_MDCTL_BUSY |	            (PHY_ADDRESS << SAA9730_MDCTL_PHY_SHIFT)  |	            PHY_STATUS;    // spin till station is not busy    while (SAA9730_MDCTL & SAA9730_MDCTL_BUSY)	;    hal_delay_us(1000);    // check the link status    if (SAA9730_MDDATA & PHY_STATUS_LINK_UP) {	SAA9730_MDCTL = SAA9730_MDCTL_BUSY |	                (PHY_ADDRESS << SAA9730_MDCTL_PHY_SHIFT)  |	                PHY_REG31;	// spin till station is not busy	while (SAA9730_MDCTL & SAA9730_MDCTL_BUSY)	    ;	hal_delay_us(1000);        opmode = (SAA9730_MDDATA & PHY_REG31_OPMODE_MSK) >> PHY_REG31_OPMODE_SHIFT;#ifdef DEBUG	db_printf("MII mode %d\n", opmode);#endif	if ((opmode == OPMODE_10BASET_FULLDUPLEX) ||	    (opmode == OPMODE_100BASEX_FULLDUPLEX))            SAA9730_MACCTL = SAA9730_MACCTL_CONMODE_FORCE_MII | SAA9730_MACCTL_FULLDUP;        else            SAA9730_MACCTL = SAA9730_MACCTL_CONMODE_FORCE_MII;    }#ifdef DEBUG    else	db_printf("Link is down\n");#endif}static voidsaa9730_reset(struct saa9730_priv_data *spd){    unsigned long __base = spd->base;    __init_buffers(spd);    __base = spd->base;        // Stop DMA    SAA9730_DMACTL &= ~(SAA9730_DMACTL_ENRX | SAA9730_DMACTL_ENTX);    // Stop tx/rx    SAA9730_TXCTL &= ~SAA9730_TXCTL_ENTX;    SAA9730_RXCTL &= ~SAA9730_RXCTL_ENRX;    // Set DMA and MAC reset bits    SAA9730_DMATST |= SAA9730_DMATST_RESET;    SAA9730_MACCTL |= SAA9730_MACCTL_RESET;    __init_cam(spd);    __init_dma(spd);    __check_mii(spd);    spd->tx_busy = 0;}static bool atlas_saa9730_init(struct cyg_netdevtab_entry *tab){    static int initialized = 0; // only probe PCI et al *once*    struct eth_drv_sc *sc = (struct eth_drv_sc *)tab->device_instance;    struct saa9730_priv_data *spd = (struct saa9730_priv_data *)sc->driver_private;#ifdef DEBUG    db_printf("atlas_saa9730_init\n");#endif    if (0 == initialized) {	cyg_pci_device_id devid;	cyg_pci_device dev_info;	cyg_pci_init();	devid = CYG_PCI_NULL_DEVID;        if (cyg_pci_find_device(CYG_PCI_VENDOR_PHILIPS, 0x9730, &devid) ) {	    spd->devid = devid;            cyg_pci_get_device_info(devid, &dev_info);            if (!cyg_pci_configure_device(&dev_info)) {#ifdef DEBUG                db_printf("Failed to configure eth device\n");#endif		return false;	    }	    //  Philips SAA9730 implements only one function memory mapped	    //  into a single contigous memory region.	    //            //  According to spec. the BAR#1 is to be used for memory mapped IO.	    spd->base = dev_info.base_map[1];	    // FIXME! All IO units share an interrupt	    spd->vector = CYGNUM_HAL_INTERRUPT_INTB;	    // Setup timing stuff	    cyg_hal_plf_pci_cfg_write_byte(CYG_PCI_DEV_GET_BUS(devid),					   CYG_PCI_DEV_GET_DEVFN(devid),		                           CYG_PCI_CFG_LATENCY_TIMER, 0x20);	    cyg_hal_plf_pci_cfg_write_byte(CYG_PCI_DEV_GET_BUS(devid),					   CYG_PCI_DEV_GET_DEVFN(devid),		                           CYG_PCI_CFG_MIN_GNT, 9);	    cyg_hal_plf_pci_cfg_write_byte(CYG_PCI_DEV_GET_BUS(devid),					   CYG_PCI_DEV_GET_DEVFN(devid),		                           CYG_PCI_CFG_MAX_LAT, 24);	    #ifdef DEBUG            db_printf("eth0 found: bus[%d] dev[%d] base[%x] vector[%d]\n",		      CYG_PCI_DEV_GET_BUS(devid),		      CYG_PCI_DEV_GET_DEV(CYG_PCI_DEV_GET_DEVFN(devid)),		      spd->base, spd->vector);#endif            spd->ndp = tab;            #ifndef CYGPKG_REDBOOT	    cyg_drv_interrupt_create(		    spd->vector,                    0,                  // Priority - unused                    (CYG_ADDRWORD)spd,  // Data item passed to ISR & DSR                    saa9730_isr,            // ISR                    saa9730_dsr,            // DSR                    &spd->interrupt_handle, // handle to intr obj                    &spd->interrupt_object ); // space for int obj	    cyg_drv_interrupt_attach(spd->interrupt_handle);	    cyg_drv_interrupt_acknowledge(spd->vector);	    cyg_drv_interrupt_unmask(spd->vector);#endif            {                // When in Redboot we want to get RX interrupts. These                // will be picked up by the default interrupt handler and                // checked for ^C.

⌨️ 快捷键说明

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