if_rhine.c
来自「eCos操作系统源码」· C语言 代码 · 共 1,320 行 · 第 1/3 页
C
1,320 行
//==========================================================================//// dev/if_rhine.c//// Ethernet device driver for VIA RHINE compatible controllers////==========================================================================//####ECOSGPLCOPYRIGHTBEGIN####// -------------------------------------------// This file is part of eCos, the Embedded Configurable Operating System.// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.//// 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####//####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): jskov, based on pcnet driver// Contributors: gthomas, jskov, hmt// Date: 2001-05-30// Purpose: // Description: hardware driver for VIA Rhine ethernet//// FIXME: Make endian safe// Make use of virtual addressing for memory shared over PCI// (see _ADDR_MASK). // Link failure not detected for some reason.////####DESCRIPTIONEND####////==========================================================================#include <pkgconf/system.h>#include <pkgconf/devs_eth_via_rhine.h>#include <pkgconf/io_eth_drivers.h>#include <cyg/infra/cyg_type.h>#include <cyg/hal/hal_arch.h>#include <cyg/hal/hal_intr.h>#include <cyg/infra/cyg_ass.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_NET#include <pkgconf/net.h>#include <cyg/kernel/kapi.h>#include <net/if.h> /* Needed for struct ifnet */#include <pkgconf/io_eth_drivers.h>#endif#include CYGHWR_MEMORY_LAYOUT_H#ifdef CYGPKG_IO_PCI#include <cyg/io/pci.h>#else#error "Need PCI package here"#endif#define _BUF_SIZE 1544#ifdef CYGPKG_INFRA_DEBUG// Then we log, OOI, the number of times we get a bad packet number// from the tx done fifo.int rhine_txfifo_good = 0;int rhine_txfifo_bad = 0;#endif#include "via_rhine.h"#define __WANT_DEVS#include CYGDAT_DEVS_ETH_VIA_RHINE_INL#undef __WANT_DEVSstatic void rhine_poll(struct eth_drv_sc *sc);// This ISR is called when the ethernet interrupt occursstatic cyg_uint32rhine_isr(cyg_vector_t vector, cyg_addrword_t data){ struct rhine_priv_data *cpd = (struct rhine_priv_data *)data; DEBUG_FUNCTION(); INCR_STAT( interrupts ); cyg_drv_interrupt_mask(cpd->interrupt); cyg_drv_interrupt_acknowledge(cpd->interrupt); return (CYG_ISR_HANDLED|CYG_ISR_CALL_DSR); // Run the DSR}static voidrhine_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data){ // This conditioning out is necessary because of explicit calls to this // DSR - which would not ever be called in the case of a polled mode // usage ie. in RedBoot.#ifdef CYGPKG_IO_ETH_DRIVERS_NET struct rhine_priv_data* cpd = (struct rhine_priv_data *)data; struct cyg_netdevtab_entry *ndp = (struct cyg_netdevtab_entry *)(cpd->ndp); struct eth_drv_sc *sc = (struct eth_drv_sc *)(ndp->device_instance); // but here, it must be a *sc: eth_drv_dsr( vector, count, (cyg_addrword_t)sc );#else# ifndef CYGPKG_REDBOOT# error Empty Rhine ethernet DSR is compiled. Is this what you want?# endif#endif}// The deliver function (ex-DSR) handles the ethernet [logical] processingstatic voidrhine_deliver(struct eth_drv_sc *sc){ struct rhine_priv_data *cpd = (struct rhine_priv_data *)sc->driver_private; DEBUG_FUNCTION(); // Service the interrupt: rhine_poll(sc); // Allow interrupts to happen again cyg_drv_interrupt_unmask(cpd->interrupt);}static intrhine_int_vector(struct eth_drv_sc *sc){ struct rhine_priv_data *cpd = (struct rhine_priv_data *)sc->driver_private; return (cpd->interrupt);}// ------------------------------------------------------------------------// Physical interface#if 0 // fix warning since this isn't actually usedstatic voidrhine_write_MII(struct rhine_priv_data *cpd, int id, int reg, cyg_uint16 value){ cyg_uint8 stat; int i = 1000; // Wait for a previous access to complete (within reason) do { HAL_PCI_IO_READ_UINT8(cpd->base + RHINE_MIICR, stat); } while ((stat & (RHINE_MIICR_RCMD | RHINE_MIICR_WCMD)) && i-- > 0); HAL_PCI_IO_WRITE_UINT8(cpd->base + RHINE_MIICR, 0); HAL_PCI_IO_WRITE_UINT8(cpd->base + RHINE_PHYADR, id); HAL_PCI_IO_WRITE_UINT8(cpd->base + RHINE_MIIAD, reg); HAL_PCI_IO_WRITE_UINT16(cpd->base + RHINE_MIIDATA, value); HAL_PCI_IO_WRITE_UINT8(cpd->base + RHINE_MIICR, RHINE_MIICR_WCMD);}#endifstatic intrhine_read_MII(struct rhine_priv_data *cpd, int id, int reg){ int i = 1000; cyg_uint8 stat; cyg_uint16 val; // Wait for a previous access to complete (within reason) do { HAL_PCI_IO_READ_UINT8(cpd->base + RHINE_MIICR, stat); } while ((stat & (RHINE_MIICR_RCMD | RHINE_MIICR_WCMD)) && i-- > 0); HAL_PCI_IO_WRITE_UINT8(cpd->base + RHINE_MIICR, 0); HAL_PCI_IO_WRITE_UINT8(cpd->base + RHINE_PHYADR, id); HAL_PCI_IO_WRITE_UINT8(cpd->base + RHINE_MIIAD, reg); HAL_PCI_IO_WRITE_UINT8(cpd->base + RHINE_MIICR, RHINE_MIICR_RCMD); i = 1000; do { HAL_PCI_IO_READ_UINT8(cpd->base + RHINE_MIICR, stat); } while ((stat & RHINE_MIICR_RCMD) && i-- > 0); HAL_PCI_IO_READ_UINT16(cpd->base + RHINE_MIIDATA, val); return val;}// ------------------------------------------------------------------------// Memory management//// Simply carve off from the front of the PCI mapped window into real memorystatic cyg_uint32 rhine_heap_size;static cyg_uint8 *rhine_heap_base;static cyg_uint8 *rhine_heap_free;static void*pciwindow_mem_alloc(int size){ void *p_memory; int _size = size; CYG_ASSERT( (CYGHWR_VIA_RHINE_PCI_MEM_MAP_BASE <= (int)rhine_heap_free) && ((CYGHWR_VIA_RHINE_PCI_MEM_MAP_BASE + CYGHWR_VIA_RHINE_PCI_MEM_MAP_SIZE) > (int)rhine_heap_free) && (0 < rhine_heap_size) && (CYGHWR_VIA_RHINE_PCI_MEM_MAP_SIZE >= rhine_heap_size) && (CYGHWR_VIA_RHINE_PCI_MEM_MAP_BASE == (int)rhine_heap_base), "Heap variables corrupted" ); p_memory = (void *)0; size = (size + 3) & ~3; if ( (rhine_heap_free+size) < (rhine_heap_base+rhine_heap_size) ) { cyg_uint32 *p; p_memory = (void *)rhine_heap_free; rhine_heap_free += size; for ( p = (cyg_uint32 *)p_memory; _size > 0; _size -= 4 ) *p++ = 0; }#if DEBUG & 9 diag_printf("Allocated %d bytes at %08x\n", size, p_memory);#endif return p_memory;}static cyg_pci_match_func find_rhine_match_func;static cyg_boolfind_rhine_match_func( cyg_uint16 v, cyg_uint16 d, cyg_uint32 c, void *p ){#if DEBUG & 9 diag_printf("PCI match vendor %04x device %04x\n", v, d);#endif return (0x1106 == v) && // vendor: VIA ((0x3065 == d) || // device: DL10030A (0x3043 == d)); // device: VT86C100A}static intpci_init_find_rhines( void ){ cyg_pci_device_id devid; cyg_pci_device dev_info; cyg_uint16 cmd; int device_index; int found_devices = 0; DEBUG_FUNCTION();#ifdef CYGARC_UNCACHED_ADDRESS CYG_ASSERT( CYGARC_UNCACHED_ADDRESS((CYG_ADDRWORD)CYGMEM_SECTION_pci_window) == CYGHWR_VIA_RHINE_PCI_MEM_MAP_BASE, "PCI window configured does not match PCI memory section base" );#else CYG_ASSERT( (CYG_ADDRWORD)CYGMEM_SECTION_pci_window == CYGHWR_VIA_RHINE_PCI_MEM_MAP_BASE, "PCI window configured does not match PCI memory section base" );#endif CYG_ASSERT( CYGMEM_SECTION_pci_window_SIZE == CYGHWR_VIA_RHINE_PCI_MEM_MAP_SIZE, "PCI window configured does not match PCI memory section size" ); if (#ifdef CYGARC_UNCACHED_ADDRESS CYGARC_UNCACHED_ADDRESS((CYG_ADDRWORD)CYGMEM_SECTION_pci_window) !=#else (CYG_ADDRWORD)CYGMEM_SECTION_pci_window !=#endif CYGHWR_VIA_RHINE_PCI_MEM_MAP_BASE || CYGMEM_SECTION_pci_window_SIZE != CYGHWR_VIA_RHINE_PCI_MEM_MAP_SIZE ) {#if DEBUG & 8 diag_printf("pci_init_find_rhines(): PCI window misconfigured\n");#endif return 0; } // First initialize the heap in PCI window'd memory rhine_heap_size = CYGHWR_VIA_RHINE_PCI_MEM_MAP_SIZE; rhine_heap_base = (cyg_uint8 *)CYGHWR_VIA_RHINE_PCI_MEM_MAP_BASE; rhine_heap_free = rhine_heap_base; cyg_pci_init();#if DEBUG & 8 diag_printf("Finished cyg_pci_init();\n");#endif devid = CYG_PCI_NULL_DEVID; for (device_index = 0; device_index < CYGNUM_DEVS_ETH_VIA_RHINE_DEV_COUNT; device_index++) { struct rhine_priv_data* cpd = rhine_priv_array[device_index]; cpd->index = device_index; // See above for find_rhine_match_func - it selects any of several // variants. This is necessary in case we have multiple mixed-type // devices on one board in arbitrary orders. if (cyg_pci_find_matching( &find_rhine_match_func, NULL, &devid )) {#if DEBUG & 8 diag_printf("eth%d = rhine\n", device_index);#endif cyg_pci_get_device_info(devid, &dev_info); cpd->interrupt_handle = 0; // Flag not attached. if (cyg_pci_translate_interrupt(&dev_info, &cpd->interrupt)) {#if DEBUG & 8 diag_printf(" Wired to HAL vector %d\n", cpd->interrupt);#endif cyg_drv_interrupt_create( cpd->interrupt, 1, // Priority - unused (cyg_addrword_t)cpd,// Data item passed to ISR & DSR rhine_isr, // ISR rhine_dsr, // DSR &cpd->interrupt_handle, // handle to intr obj &cpd->interrupt_object ); // space for int obj cyg_drv_interrupt_attach(cpd->interrupt_handle); // Don't unmask the interrupt yet, that could get us into a // race. } else { cpd->interrupt = 0;#if DEBUG & 8 diag_printf(" Does not generate interrupts.\n");#endif } if (cyg_pci_configure_device(&dev_info)) {#if DEBUG & 8 int i; diag_printf("Found device on bus %d, devfn 0x%02x:\n", CYG_PCI_DEV_GET_BUS(devid), CYG_PCI_DEV_GET_DEVFN(devid)); if (dev_info.command & CYG_PCI_CFG_COMMAND_ACTIVE) { diag_printf(" Note that board is active. Probed" " sizes and CPU addresses invalid!\n"); } diag_printf(" Vendor 0x%04x", dev_info.vendor); diag_printf("\n Device 0x%04x", dev_info.device); diag_printf("\n Command 0x%04x, Status 0x%04x\n", dev_info.command, dev_info.status); diag_printf(" Class/Rev 0x%08x", dev_info.class_rev); diag_printf("\n Header 0x%02x\n", dev_info.header_type); diag_printf(" SubVendor 0x%04x, Sub ID 0x%04x\n", dev_info.header.normal.sub_vendor, dev_info.header.normal.sub_id); for(i = 0; i < CYG_PCI_MAX_BAR; i++) { diag_printf(" BAR[%d] 0x%08x /", i, dev_info.base_address[i]); diag_printf(" probed size 0x%08x / CPU addr 0x%08x\n", dev_info.base_size[i], dev_info.base_map[i]); } diag_printf(" eth%d configured\n", device_index);#endif found_devices++; cpd->found = 1; cpd->active = 0; cpd->devid = devid; cpd->base = (unsigned char*) dev_info.base_map[0];#if DEBUG & 8 diag_printf(" I/O address = 0x%08x\n", cpd->base);#endif // Don't use cyg_pci_set_device_info since it clears // some of the fields we want to print out below. cyg_pci_read_config_uint16(dev_info.devid, CYG_PCI_CFG_COMMAND, &cmd); cmd |= (CYG_PCI_CFG_COMMAND_IO // enable I/O space | CYG_PCI_CFG_COMMAND_MEMORY // enable memory space | CYG_PCI_CFG_COMMAND_MASTER); // enable bus master cyg_pci_write_config_uint16(dev_info.devid, CYG_PCI_CFG_COMMAND, cmd); // Extra init code needed for D-Link controller. This // is snuffed from the Linux driver and was provided // by D-Link. I've been unable to find documentation // for the part. if (0x3065 == dev_info.device) { cyg_uint8 tmp;#if DEBUG & 8 diag_printf("Pre-reset init code for D-Link.\n");#endif HAL_PCI_IO_READ_UINT8(cpd->base+RHINE_STICKYHW, tmp); tmp &= 0xfc; HAL_PCI_IO_WRITE_UINT8(cpd->base+RHINE_STICKYHW, tmp); HAL_PCI_IO_WRITE_UINT8(cpd->base+RHINE_WOL_CG_CLR, 0x80); HAL_PCI_IO_WRITE_UINT8(cpd->base+RHINE_WOL_CR_CLR, 0xff); HAL_PCI_IO_WRITE_UINT8(cpd->base+RHINE_PWR_CSR_CLR, 0xff); } // Now the PCI part of the device is configured, reset // it. This should make it safe to enable the // interrupt HAL_PCI_IO_WRITE_UINT8(cpd->base+RHINE_CR1, RHINE_CR1_SRST);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?