URL
https://opencores.org/ocsvn/openrisc/openrisc/trunk
Subversion Repositories openrisc
[/] [openrisc/] [trunk/] [rtos/] [ecos-3.0/] [packages/] [devs/] [eth/] [rltk/] [8139/] [current/] [src/] [if_8139.c] - Rev 863
Go to most recent revision | Compare with Previous | Blame | View Log
//========================================================================== // // if_8139.c // // RealTek 8139 ethernet driver // //========================================================================== // ####ECOSGPLCOPYRIGHTBEGIN#### // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. // Copyright (C) 1998, 1999, 2000, 2001, 2002, 2009 Free Software Foundation, 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., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 v2. // // This exception does not invalidate any other reasons why a work based // on this file might be covered by the GNU General Public License. // ------------------------------------------- // ####ECOSGPLCOPYRIGHTEND#### //========================================================================== //#####DESCRIPTIONBEGIN#### // // Author(s): Eric Doenges // Contributors: Chris Nimmers, Gary Thomas, Andy Dyer // Date: 2003-07-09 // Purpose: // Description: hardware driver for RealTek 8139 ethernet // Notes: This is a very basic driver that will send and receive // packets and not much else. A lot of features of the 8139 // are not supported (power management, MII interface, // access to the serial eeprom, 'twister tuning', etc.). // // Many of the configuration options (like media type and // speed) the 8139 has are taken directly from the serial // eeprom and are not currently configurable from this driver. // // I've tentatively added some code to handle cache coherency // issues for platforms that do not have a separate address // space for uncached memory access and do not do cache // snooping for PCI bus masters. This code can be activated by // defining CYGPKG_DEVS_ETH_RLTK_8139_SOFTWARE_CACHE_COHERENCY // in the platform specific .inl file. Note that if // CYGPKG_DEVS_ETH_RLTK_8139_SOFTWARE_CACHE_COHERENCY is // defined, the .inl file is also responsible for making sure // the receive and transmit buffers are located in memory in // such a way that flushing or invalidating cache lines for // these buffers will not affect any other buffers. See the // README in the doc directory for some suggestions on how // to do this. // // Since the official data sheet for this chip is a bit // vague, I had to look at the Linux and OpenBSD drivers to // understand the basic workings of the chip; however, I have // not copied any code from those drivers to avoid tainting // eCos' license. // // FIXME: // //####DESCRIPTIONEND#### // //========================================================================== #include <pkgconf/system.h> #ifdef CYGPKG_IO_ETH_DRIVERS #include <pkgconf/io_eth_drivers.h> #endif #include <pkgconf/devs_eth_rltk_8139.h> #include <cyg/infra/cyg_type.h> #include <cyg/infra/cyg_ass.h> #include <cyg/infra/diag.h> #include <cyg/hal/hal_arch.h> #include <cyg/hal/drv_api.h> #include <cyg/hal/hal_cache.h> #include <cyg/io/eth/netdev.h> #include <cyg/io/eth/eth_drv.h> #include <string.h> /* for memset */ #ifdef CYGPKG_IO_PCI #include <cyg/io/pci.h> #else #error "This driver requires the PCI package (CYGPKG_IO_PCI)" #endif #include <cyg/io/pci.h> /* Necessary for memory mapping macros */ #include CYGHWR_MEMORY_LAYOUT_H /* Check if we should be dumping debug information or not */ #if defined CYGDBG_DEVS_ETH_RLTK_8139_CHATTER \ && (CYGDBG_DEVS_ETH_RLTK_8139_CHATTER > 0) #define DEBUG_RLTK8139_DRIVER #endif #include "if_8139.h" /* Which interrupts we will handle */ #define RLTK8139_IRQ (IR_SERR|IR_FOVW|IR_RXOVW|IR_TER|IR_TOK|IR_RER|IR_ROK|IR_FUN) /* Allow platform-specific configuration of the driver */ #ifndef CYGDAT_DEVS_ETH_RLTK_8139_INL #error "CYGDAT_DEVS_ETH_RLTK_8139_INL not defined" #else #include CYGDAT_DEVS_ETH_RLTK_8139_INL #endif #ifndef CYGHWR_RLTK_8139_PLF_INIT #define CYGHWR_RLTK_8139_PLF_INIT(sc) do {} while(0) #endif /* * If software cache coherency is required, the HAL_DCACHE_INVALIDATE * hal macro must be defined as well. */ #ifdef CYGPKG_DEVS_ETH_RLTK_8139_SOFTWARE_CACHE_COHERENCY #if !defined HAL_DCACHE_INVALIDATE || !defined HAL_DCACHE_FLUSH #error "HAL_DCACHE_INVALIDATE/HAL_DCACHE_FLUSH not defined for this platform but CYGPKG_DEVS_ETH_RLTK_8139_CACHE_COHERENCY was defined." #endif #endif /* Local driver function declarations */ #ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE static cyg_uint32 rltk8139_isr(cyg_vector_t vector, cyg_addrword_t data); #endif #ifdef ETH_DRV_SET_MC_LIST static cyg_uint32 ether_crc(cyg_uint8 *data, int length); static void rltk8139_set_multicast_list(Rltk8139_t *rltk8139_info, struct eth_drv_mc_list *mc_list); #endif static void rltk8139_reset(struct eth_drv_sc *sc); static bool rltk8139_init(struct cyg_netdevtab_entry *tab); static void rltk8139_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags); static void rltk8139_stop(struct eth_drv_sc *sc); static int rltk8139_control(struct eth_drv_sc *sc, unsigned long key, void *data, int data_length); static int rltk8139_can_send(struct eth_drv_sc *sc); static void rltk8139_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len, int total_len, unsigned long key); static void rltk8139_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len); static void rltk8139_deliver(struct eth_drv_sc *sc); static void rltk8139_poll(struct eth_drv_sc *sc); static int rltk8139_int_vector(struct eth_drv_sc *sc); #ifdef CYGPKG_DEVS_ETH_RLTK_8139_WRITE_EEPROM static cyg_uint16 rltk8139_eeprom_read( char *cpErAddr, cyg_uint8 Cmd, cyg_uint8 RomAdr ); static void rltk8139_eeprom_write( char *cpErAddr, cyg_uint8 Cmd, cyg_uint8 RomAdr, cyg_uint16 SrcData ); #endif #ifdef DEBUG_RLTK8139_DRIVER void rltk8139_print_state(struct eth_drv_sc *sc); #endif #ifdef CYGPKG_DEVS_ETH_RLTK_8139_WRITE_EEPROM #define EEPROM_CMD_READ 0x06 // eeprom read command #define EEPROM_CMD_WRITE 0x05 // eeprom write command #define EEPROM_PG_ON 0x80 #define EEPROM_PG_EECS 0x08 #define EEPROM_PG_EESK 0x04 #define EEPROM_PG_EEDI 0x02 #define EEPROM_PG_EEDO 0x01 #define EEPROM_WR_BUSY_RETRIES 100 // ready wait re-try count #define EEPROM_MASK(_param0_,_param1_) ((_param0_ & _param1_) ? EEPROM_PG_EEDI : 0 ) #define EEPROM_WR_DATA(_addr_,_txdata_) HAL_WRITE_UINT8(_addr_,_txdata_); #define EEPROM_WR_DATAPULSE(_addr_,_txdata_) HAL_WRITE_UINT8(_addr_,_txdata_); \ cyg_thread_delay(0); \ HAL_WRITE_UINT8(_addr_,_txdata_ | EEPROM_PG_EESK); #define EEPROM_RD_DATA(_addr_,_txdata_,_rxdata_) { \ cyg_uint16 read_data; \ HAL_WRITE_UINT8(_addr_,_txdata_); \ cyg_thread_delay(1); \ HAL_READ_UINT8(_addr_, read_data); \ _rxdata_ <<= 1; \ _rxdata_ |= ((read_data & EEPROM_PG_EEDO) ? 0x0001 : 0x0000 ); \ HAL_WRITE_UINT8(_addr_,_txdata_ | EEPROM_PG_EESK); } #endif /* * Define inline functions to access the card. This will also handle * endianess issues in one place. This code was lifted from the eCos * i82559 driver. */ #if (CYG_BYTEORDER == CYG_MSBFIRST) #define HAL_CTOLE32(x) ((((x) & 0xff) << 24) | (((x) & 0xff00) << 8) | (((x) & 0xff0000) >> 8) | (((x) >> 24) & 0xff)) #define HAL_LE32TOC(x) ((((x) & 0xff) << 24) | (((x) & 0xff00) << 8) | (((x) & 0xff0000) >> 8) | (((x) >> 24) & 0xff)) #define HAL_CTOLE16(x) ((((x) & 0xff) << 8) | (((x) & 0xff00) >> 8)) #define HAL_LE16TOC(x) ((((x) & 0xff) << 8) | (((x) & 0xff00) >> 8)) static inline void OUTB(cyg_uint8 value, cyg_uint32 io_address) { HAL_WRITE_UINT8( io_address, value); } static inline void OUTW(cyg_uint16 value, cyg_uint32 io_address) { HAL_WRITE_UINT16(io_address, (((value & 0xff) << 8) | ((value & 0xff00) >> 8)) ); } static inline void OUTL(cyg_uint32 value, cyg_uint32 io_address) { HAL_WRITE_UINT32(io_address, ((((value) & 0xff) << 24) | (((value) & 0xff00) << 8) | (((value) & 0xff0000) >> 8) | (((value) >> 24) & 0xff)) ); } static inline cyg_uint8 INB(cyg_uint32 io_address) { cyg_uint8 d; HAL_READ_UINT8(io_address, d); return d; } static inline cyg_uint16 INW(cyg_uint32 io_address) { cyg_uint16 d; HAL_READ_UINT16( io_address, d ); return (((d & 0xff) << 8) | ((d & 0xff00) >> 8)); } static inline cyg_uint32 INL(cyg_uint32 io_address) { cyg_uint32 d; HAL_READ_UINT32(io_address, d); return ((((d) & 0xff) << 24) | (((d) & 0xff00) << 8) | (((d) & 0xff0000) >> 8) | (((d) >> 24) & 0xff)); } #else // Maintaining the same styleee as above... #define HAL_CTOLE32(x) ((((x)))) #define HAL_LE32TOC(x) ((((x)))) #define HAL_CTOLE16(x) ((((x)))) #define HAL_LE16TOC(x) ((((x)))) static inline void OUTB(cyg_uint8 value, cyg_uint32 io_address) { HAL_WRITE_UINT8( io_address, value ); } static inline void OUTW(cyg_uint16 value, cyg_uint32 io_address) { HAL_WRITE_UINT16( io_address, value ); } static inline void OUTL(cyg_uint32 value, cyg_uint32 io_address) { HAL_WRITE_UINT32( io_address, value ); } static inline cyg_uint8 INB(cyg_uint32 io_address) { cyg_uint8 _t_; HAL_READ_UINT8( io_address, _t_ ); return _t_; } static inline cyg_uint16 INW(cyg_uint32 io_address) { cyg_uint16 _t_; HAL_READ_UINT16( io_address, _t_ ); return _t_; } static inline cyg_uint32 INL(cyg_uint32 io_address) { cyg_uint32 _t_; HAL_READ_UINT32( io_address, _t_ ); return _t_; } #endif // byteorder /* * Table of all known PCI device/vendor ID combinations for the RealTek 8139. * Add them as you get to know them. */ #define CYGNUM_DEVS_ETH_RLTK_8139_KNOWN_ALIASES 4 static pci_identifier_t known_8139_aliases[CYGNUM_DEVS_ETH_RLTK_8139_KNOWN_ALIASES] = { { 0x1186, 0x1300, NULL }, /* D-Link DFE-530TX+ PCI card */ { 0x10ec, 0x8139, NULL }, /* This is the official RealTek vendor/device code of 8139D(L) */ { 0x11db, 0x1234, NULL}, /* SEGA DreamCast BroadBandAdapter */ { 0x10ec, 0x8129, NULL } /* This is the official RealTek vendor/device code of 8139C(L) */ }; /* * Check if the device description matches a known 8139 alias. */ static cyg_bool rltk8139_find_match_func(cyg_uint16 vendor_id, cyg_uint16 device_id, cyg_uint32 class_id, void *p) { int i; for (i = 0; i < CYGNUM_DEVS_ETH_RLTK_8139_KNOWN_ALIASES; ++i) { if (((known_8139_aliases[i].vendor_id == PCI_ANY_ID) || (known_8139_aliases[i].vendor_id == vendor_id)) && ((known_8139_aliases[i].device_id == PCI_ANY_ID) || (known_8139_aliases[i].device_id == device_id))) return true; } return false; } /* * Find the Nth 8139 device on all attached PCI busses and do some initial * PCI-type initialization. Also setup the interrupt for use in eCos. */ static bool rltk8139_find(int n_th, struct eth_drv_sc *sc) { Rltk8139_t *rltk8139_info; cyg_pci_device_id pci_device_id; cyg_pci_device pci_device_info; cyg_uint16 command; int found = -1; /* Go through all PCI devices until we find the Nth 8139 chip */ pci_device_id = CYG_PCI_NULL_DEVID; do { if (!cyg_pci_find_matching(&rltk8139_find_match_func, NULL, &pci_device_id)) return false; else found += 1; } while (found != n_th); /* Save device ID in driver private data in case we ever need it again */ rltk8139_info = (Rltk8139_t *)(sc->driver_private); rltk8139_info->pci_device_id = pci_device_id; /* Now that we have found the device, we can extract some data about it */ cyg_pci_get_device_info(pci_device_id, &pci_device_info); /* Get the assigned interrupt and set up ISR and DSR for it. */ if (cyg_pci_translate_interrupt(&pci_device_info, &rltk8139_info->vector)) { #ifdef DEBUG_RLTK8139_DRIVER diag_printf(" Wired to HAL interrupt vector %d\n", rltk8139_info->vector); #endif #ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE /* * Note that we use the generic eth_drv_dsr routine instead of * our own. */ cyg_drv_interrupt_create(rltk8139_info->vector, rltk8139_info->isr_priority, (CYG_ADDRWORD)sc, rltk8139_isr, eth_drv_dsr, &rltk8139_info->interrupt_handle, &rltk8139_info->interrupt); cyg_drv_interrupt_attach(rltk8139_info->interrupt_handle); #endif } else { #ifdef DEBUG_RLTK8139_DRIVER diag_printf(" Does not generate interrupts.\n"); #endif } /* * Call 'cyg_pci_configure_device' for those platforms that do not * configure the PCI bus during HAL initialization. According to Nick * Garnett, there are good reasons to not configure PCI devices during HAL * initialization. Also according to Nick, calling * 'cyg_pci_configure_device' for devices already configured should have no * effect and thus is safe to do. */ if (cyg_pci_configure_device(&pci_device_info)) { #ifdef DEBUG_RLTK8139_DRIVER int i; diag_printf("Found device #%d on bus %d, devfn 0x%02x:\n", n_th, CYG_PCI_DEV_GET_BUS(pci_device_id), CYG_PCI_DEV_GET_DEVFN(pci_device_id)); if (pci_device_info.command & CYG_PCI_CFG_COMMAND_ACTIVE) diag_printf(" Note that board is active. Probed" " sizes and CPU addresses are invalid!\n"); diag_printf(" Vendor 0x%04x", pci_device_info.vendor); diag_printf("\n Device 0x%04x", pci_device_info.device); diag_printf("\n Command 0x%04x, Status 0x%04x\n", pci_device_info.command, pci_device_info.status) ; diag_printf(" Class/Rev 0x%08x", pci_device_info.class_rev); diag_printf("\n Header 0x%02x\n", pci_device_info.header_type); diag_printf(" SubVendor 0x%04x, Sub ID 0x%04x\n", pci_device_info.header.normal.sub_vendor, pci_device_info.header.normal.sub_id); for(i = 0; i < CYG_PCI_MAX_BAR; i++) { diag_printf(" BAR[%d] 0x%08x /", i, pci_device_info.base_address[i]); diag_printf(" probed size 0x%08x / CPU addr 0x%08x\n", pci_device_info.base_size[i], pci_device_info.base_map[i]); } #endif } else { #ifdef DEBUG_RLTK8139_DRIVER diag_printf("Failed to configure 8139 device #%d\n", n_th); #endif return false; } /* * Enable memory mapped and port based I/O and busmastering. We currently * only support IO space accesses; memory mapping is enabled so that bit * DVRLOAD in CONFIG1 is cleared automatically. * * NOTE: it seems that for some configurations/HALs, the device is already * activated at this point, even though eCos' documentation suggests * it shouldn't be. At least in my case, this is not a problem. */ cyg_pci_read_config_uint16(pci_device_info.devid, CYG_PCI_CFG_COMMAND, &command); command |= (CYG_PCI_CFG_COMMAND_IO | CYG_PCI_CFG_COMMAND_MEMORY | CYG_PCI_CFG_COMMAND_MASTER); cyg_pci_write_config_uint16(pci_device_info.devid, CYG_PCI_CFG_COMMAND, command); /* * This is the base address used to talk to the device. The 8139's IOAR * and MEMAR registers are BAR0 and BAR1, respectively. */ rltk8139_info->base_address = pci_device_info.base_map[0]; /* * Read the MAC address. The RealTek data sheet seems to claim this should * only be read in 4 byte accesses, but the code below works just fine. */ for (found = 0; found < 6; ++found) rltk8139_info->mac[found] = INB(rltk8139_info->base_address + IDR0 + found); /* * This is the indicator for "uses an interrupt". The code was lifted * from the eCos Intel 82559 driver. */ if (rltk8139_info->interrupt_handle != 0) { cyg_drv_interrupt_acknowledge(rltk8139_info->vector); #ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE cyg_drv_interrupt_unmask(rltk8139_info->vector); #endif } return true; } #ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE /* * Interrupt service routine. We do not clear the interrupt status bits * (since this should really only be done after handling whatever caused * the interrupt, and that is done in the '_deliver' routine), but instead * clear the interrupt mask. * * If we are sharing interrupts with other devices, we have two options * (configurable): * * 1. Mask the interrupt vector completly. Personally I think this is a bad * idea because the other devices sharing this interrupt are also masked * until the network thread gets around to calling the '_deliver' routine. * * 2. Use the interrupt mask register in the 8139 to mask just the interrupt * request coming from the 8139. This way, the other devices' requests * can still be serviced. */ static cyg_uint32 rltk8139_isr(cyg_vector_t vector, cyg_addrword_t data) { Rltk8139_t *rltk8139_info; #ifdef CYGPKG_DEVS_ETH_RLTK_8139_SHARE_INTERRUPTS cyg_uint16 isr; #endif rltk8139_info = (Rltk8139_t *)(((struct eth_drv_sc *)data)->driver_private); #ifdef CYGPKG_DEVS_ETH_RLTK_8139_SHARE_INTERRUPTS /* * If interrupt sharing is enabled, check if the interrupt is really * intended for us. Note that while the RealTek data sheet claims that * reading the interrupt status register will clear all it's bits, * this is not true, so we can read it without problems. */ if (!(isr = INW(rltk8139_info->base_address + ISR))) return 0; #endif #ifdef CYGPKG_DEVS_ETH_RLTK_8139_MASK_INTERRUPTS_IN_8139 /* Clear the interrupt mask to stop the current request. */ OUTW(0, rltk8139_info->base_address + IMR); #else /* Mask the interrupt */ cyg_interrupt_mask(vector); #endif /* Acknowledge the interrupt for those platforms were this is necessary */ cyg_interrupt_acknowledge(vector); return (CYG_ISR_HANDLED | CYG_ISR_CALL_DSR); } #endif /* ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE */ /* * Reset the chip. This function is not exported to higher level software. */ static void rltk8139_reset(struct eth_drv_sc *sc) { rltk8139_stop(sc); rltk8139_start(sc, NULL, 0); } #ifdef ETH_DRV_SET_MC_LIST /* * I assume (hope !) that this is identical to Ethernet's CRC algorithm * specified in IEEE 802.3. It does seem to calculate the same CRC that * the 8139 itself does, so I think it is correct. * Note that while Ethernet's polynomial is usually specified as 04C11DB7, * we must use EDB88320 because we shift the bits to the left, not the right. * (See ftp://ftp.rocksoft.com/papers/crc_v3.txt for a good description of * CRC algorithms). */ static cyg_uint32 ether_crc(cyg_uint8 *data, int length) { int bit; cyg_uint32 crc = 0xFFFFFFFFU; while (length-- > 0) { crc ^= *data++; for (bit = 0; bit < 8; ++bit) { if (crc & 1) crc = (crc >> 1) ^ 0xEDB88320U; else crc = (crc >> 1); } } return crc ^ 0xFFFFFFFFU; } /* * Set up multicast filtering. The way I understand existing driver code * (Linux and OpenBSD), the 8139 calculates the ethernet CRC of * incoming addresses and uses the top 6 bits as an index into a hash * table. If the corresponding bit is set in MAR0..7, the address is * accepted. */ static void rltk8139_set_multicast_list(Rltk8139_t *rltk8139_info, struct eth_drv_mc_list *mc_list) { cyg_uint32 mar[2], hash; int i; /* If 'mc_list' is NULL, accept all multicast packets. */ if (!mc_list) { mar[0] = 0xFFFFFFFFU; mar[1] = 0xFFFFFFFFU; } else { mar[0] = 0; mar[1] = 0; for (i = 0; i < mc_list->len; ++i) { hash = ether_crc(&mc_list->addrs[i][0], ETHER_ADDR_LEN) >> 26; mar[hash >> 5] |= (1 << (hash & 0x1F)); } } /* Program the new filter values */ OUTL(mar[0], rltk8139_info->base_address + MAR0); OUTL(mar[1], rltk8139_info->base_address + MAR4); } #endif /* ifdef ETH_DRV_SET_MC_LIST */ /* * Initialize the network interface. Since the chips is reset by calling * _stop() and _start(), any code that will never need to be executed more * than once after system startup should go here. */ static bool rltk8139_init(struct cyg_netdevtab_entry *tab) { struct eth_drv_sc *sc; Rltk8139_t *rltk8139_info; sc = (struct eth_drv_sc *)(tab->device_instance); rltk8139_info = (Rltk8139_t *)(sc->driver_private); /* * Initialize the eCos PCI library. According to the documentation, it * is safe to call this function multiple times, so we call it just to * be sure it has been done. */ cyg_pci_init(); /* * Scan the PCI bus for the specified chip. The '_find' function will also * do some basic PCI initialization. */ if (!rltk8139_find(rltk8139_info->device_num, sc)) { #ifdef DEBUG_RLTK8139_DRIVER diag_printf("rltk8139_init: could not find RealTek 8139 chip #%d.\n", rltk8139_info->device_num); #endif return false; } /* platform depends initialize */ CYGHWR_RLTK_8139_PLF_INIT(sc); /* * The initial tx threshold is set here to prevent it from being reset * with every _start(). */ rltk8139_info->tx_threshold = 3; /* Initialize upper level driver */ (sc->funs->eth_drv->init)(sc, rltk8139_info->mac); return true; } /* * (Re)Start the chip, initializing data structures and enabling the * transmitter and receiver. Currently, 'flags' is unused by eCos. */ static void rltk8139_start(struct eth_drv_sc *sc, unsigned char *enaddr, int flags) { Rltk8139_t *rltk8139_info; int i; #ifdef DEBUG_RLTK8139_DRIVER diag_printf("rltk8139_start(%s)\n", sc->dev_name); #endif rltk8139_info = (Rltk8139_t *)(sc->driver_private); /* * Reset the chip. Existing driver code implies that this may take up * to 10ms; since I don't know under what exact circumstances this code may * be called I busy wait here regardless. */ OUTB(RST, rltk8139_info->base_address + CR); while (INB(rltk8139_info->base_address + CR) & RST); #ifdef DEBUG_RLTK8139_DRIVER diag_printf("rltk8139_start(%s): 8139 was successfully reset.\n", sc->dev_name); #endif /* * Clear the key storage area. These keys are used by the eCos networking * support code to keep track of individual packets. */ for (i = 0; i < NUM_TX_DESC; ++i) rltk8139_info->tx_desc_key[i] = 0; /* Initialize transmission buffer control */ rltk8139_info->tx_free_desc = 0; rltk8139_info->tx_num_free_desc = NUM_TX_DESC; /* * Set the requested MAC address if enaddr != NULL. This is a special * feature of my '_start' function since it's used to reset the chip after * errors as well. */ if (enaddr != NULL) { for (i = 0; i < 6; ++i) { rltk8139_info->mac[i] = enaddr[i]; OUTB(enaddr[i], rltk8139_info->base_address + IDR0 + i); } } /* * Now setup the transmission and reception buffers. These could be done * in _init() and kept around, but putting them here fits better logically. */ OUTL(CYGARC_PCI_DMA_ADDRESS((cyg_uint32)(rltk8139_info->tx_buffer + 0 * TX_BUF_SIZE)), rltk8139_info->base_address + TSAD0); OUTL(CYGARC_PCI_DMA_ADDRESS((cyg_uint32)(rltk8139_info->tx_buffer + 1 * TX_BUF_SIZE)), rltk8139_info->base_address + TSAD1); OUTL(CYGARC_PCI_DMA_ADDRESS((cyg_uint32)(rltk8139_info->tx_buffer + 2 * TX_BUF_SIZE)), rltk8139_info->base_address + TSAD2); OUTL(CYGARC_PCI_DMA_ADDRESS((cyg_uint32)(rltk8139_info->tx_buffer + 3 * TX_BUF_SIZE)), rltk8139_info->base_address + TSAD3); OUTL(CYGARC_PCI_DMA_ADDRESS((cyg_uint32)rltk8139_info->rx_ring), rltk8139_info->base_address + RBSTART); /* * Enable the transmitter and receiver, then clear the missed packet * counter. */ OUTB(INB(rltk8139_info->base_address + CR) | (TE|RE), rltk8139_info->base_address + CR); OUTL(0, rltk8139_info->base_address + MPC); /* * It seems the receiver and transmitter configuration can only * be set after the transmitter/receiver have been enabled. */ OUTL(TXCFG, rltk8139_info->base_address + TCR); OUTL(RXCFG | AM, rltk8139_info->base_address + RCR); /* * Enable the transmitter and receiver again. I'm not sure why this is * necessary; the Linux driver does it so we do it here just to be on * the safe side. */ OUTB(INB(rltk8139_info->base_address + CR) | (TE|RE), rltk8139_info->base_address + CR); #ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE /* * If this driver was not compiled in stand alone (without interrupts) * mode, enable interrupts. */ OUTW(RLTK8139_IRQ, rltk8139_info->base_address + IMR); #endif #ifdef DEBUG_RLTK8139_DRIVER rltk8139_print_state(sc); #endif } /* * Stop the chip, disabling the transmitter and receiver. */ static void rltk8139_stop(struct eth_drv_sc *sc) { Rltk8139_t *rltk8139_info; #ifdef DEBUG_RLTK8139_DRIVER diag_printf("rltk8139_stop(%s)\n", sc->dev_name); #endif rltk8139_info = (Rltk8139_t *)(sc->driver_private); /* Disable receiver and transmitter */ OUTB(INB(rltk8139_info->base_address + CR) & ~(TE|RE), rltk8139_info->base_address + CR); /* Mask all interrupts */ OUTW(0, rltk8139_info->base_address + IMR); } /* * 8139 control function. Unlike a 'real' ioctl function, this function is * not required to tell the caller why a request failed, only that it did * (see the eCos documentation). */ static int rltk8139_control(struct eth_drv_sc *sc, unsigned long key, void *data, int data_length) { int i; Rltk8139_t *rltk8139_info; #ifdef CYGPKG_DEVS_ETH_RLTK_8139_WRITE_EEPROM cyg_uint16 WrData, RdData; cyg_uint8 *pSrcData; int iRetry; #endif #ifdef DEBUG_RLTK8139_DRIVER diag_printf("rltk8139_control(%08x, %lx)\n", sc, key); #endif rltk8139_info = (Rltk8139_t *)(sc->driver_private); switch (key) { #ifdef ETH_DRV_SET_MAC_ADDRESS case ETH_DRV_SET_MAC_ADDRESS: if ( 6 != data_length ) return 1; /* Set the mac address */ for (i = 0; i < 6; ++i) { rltk8139_info->mac[i] = *(((cyg_uint8 *)data) + i); OUTB(rltk8139_info->mac[i], rltk8139_info->base_address + IDR0 + i); } #ifdef CYGPKG_DEVS_ETH_RLTK_8139_WRITE_EEPROM pSrcData = (cyg_uint8 *)data; for(i = 0; i < 3; i++){ WrData = ((cyg_uint16)(*pSrcData++)) & 0xff; WrData |= ((cyg_uint16)(*pSrcData++)) << 8; for( iRetry = 0; iRetry < 3; iRetry++){ rltk8139_eeprom_write( (char *)(rltk8139_info->base_address + CR9346), EEPROM_CMD_WRITE, i + 7, WrData); RdData = rltk8139_eeprom_read( (char *)(rltk8139_info->base_address + CR9346), EEPROM_CMD_READ, i + 7); if( RdData == WrData ){ break; } } } #endif return 0; #endif #ifdef ETH_DRV_GET_MAC_ADDRESS case ETH_DRV_GET_MAC_ADDRESS: if (6 != data_length) return 1; memcpy(data, rltk8139_info->mac, 6); return 0; #endif #ifdef ETH_DRV_GET_IF_STATS_UD case ETH_DRV_GET_IF_STATS_UD: // UD == UPDATE //ETH_STATS_INIT( sc ); // so UPDATE the statistics structure #endif // drop through #ifdef ETH_DRV_GET_IF_STATS case ETH_DRV_GET_IF_STATS: #endif #if defined(ETH_DRV_GET_IF_STATS) || defined (ETH_DRV_GET_IF_STATS_UD) break; #endif #ifdef ETH_DRV_SET_MC_LIST case ETH_DRV_SET_MC_LIST: /* * Program the 8139's multicast filter register. If the eth_drv_mc_list * contains at least one element, set the accept multicast bit in the * receive config register. */ rltk8139_set_multicast_list(rltk8139_info, (struct eth_drv_mc_list *)data); if (((struct eth_drv_mc_list *)data)->len > 0) OUTL(INL(rltk8139_info->base_address + RCR) | AM, rltk8139_info->base_address + RCR); else OUTL(INL(rltk8139_info->base_address + RCR) & ~AM, rltk8139_info->base_address + RCR); return 0; #endif // ETH_DRV_SET_MC_LIST #ifdef ETH_DRV_SET_MC_ALL case ETH_DRV_SET_MC_ALL: /* * Set the accept multicast bit in the receive config register and * program the multicast filter to accept all addresses. */ rltk8139_set_multicast_list(rltk8139_info, NULL); OUTL(INL(rltk8139_info->base_address + RCR) | AM, rltk8139_info->base_address + RCR); return 0; #endif // ETH_DRV_SET_MC_ALL default: return 1; } return 1; } /* * Check if a new packet can be sent. */ static int rltk8139_can_send(struct eth_drv_sc *sc) { return ((Rltk8139_t *)(sc->driver_private))->tx_num_free_desc; } /* * Send a packet over the wire. */ static void rltk8139_send(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len, int total_len, unsigned long key) { Rltk8139_t *rltk8139_info; cyg_uint8 *tx_buffer; struct eth_drv_sg *last_sg; int desc; rltk8139_info = (Rltk8139_t *)(sc->driver_private); #ifdef DEBUG_RLTK8139_DRIVER diag_printf("rltk8139_send(%s, %08x, %d, %d, %08lx)\n", sc->dev_name, sg_list, sg_len, total_len, key); #endif CYG_ASSERT(total_len <= TX_BUF_SIZE, "packet too long"); /* * Get the next free descriptor to send. We lock out all interrupts * and scheduling because we really, really do not want to be interrupted * at this point. * * IMPORTANT NOTE: the RealTek data sheet does not really make this clear, * but when they talk about a 'ring' of transmit descriptors, they * _really_ mean it, i.e. you _must_ use descriptor #1 after descriptor * #0 even if transmission of descriptor #0 has already completed. */ cyg_drv_isr_lock(); /* * Sanity check to see if '_send' was called even though there is no free * descriptor. This is probably unnecessary. */ if (rltk8139_info->tx_num_free_desc == 0) { cyg_drv_isr_unlock(); #ifdef DEBUG_RLTK8139_DRIVER diag_printf("rltk8139_send(%s): no free descriptor available\n", sc->dev_name); #endif return; } /* * Get the free descriptor and advance the descriptor counter modulo * TX_NUM_DESC. We assume that TX_NUM_DESC will always be a power of 2. */ desc = rltk8139_info->tx_free_desc; rltk8139_info->tx_free_desc = (rltk8139_info->tx_free_desc + 1) & (NUM_TX_DESC - 1); /* Decrement the number of free descriptors */ rltk8139_info->tx_num_free_desc -= 1; /* Reenable interrupts at this point */ cyg_drv_isr_unlock(); /* * Determine the buffer memory to use and tell the hardware about it. * Since we use fixed buffer addresses, we do not need to set up TSADx. * Memorize the key so we can call the tx_done callback correctly. * * While it would be possible to set TSADx to the packet directly if * it is stored in a linear memory area with 32 bit alignment, it seems * this happens so seldomly that it's simply not worth the extra * runtime check. */ tx_buffer = (cyg_uint8 *)CYGARC_UNCACHED_ADDRESS(rltk8139_info->tx_buffer + TX_BUF_SIZE * desc); rltk8139_info->tx_desc_key[desc] = key; /* * Copy the data to the designated position. Note that unlike the eCos * Intel 82559 driver, we simply assume that all the scatter/gather list * elements' lengths will add up to total_len exactly, and don't check * to make sure. */ for (last_sg = &sg_list[sg_len]; sg_list < last_sg; ++sg_list) { memcpy(tx_buffer, (void *)(sg_list->buf), sg_list->len); tx_buffer += sg_list->len; } /* * Make sure the packet has the minimum ethernet packet size, padding * with zeros if necessary. */ if (total_len < MIN_ETH_FRAME_SIZE) { memset(tx_buffer, 0, MIN_ETH_FRAME_SIZE - total_len); total_len = MIN_ETH_FRAME_SIZE; } /* * Flush the data cache here if necessary. This ensures the 8139 can * read the correct data from the transmit buffer. */ #ifdef CYGPKG_DEVS_ETH_RLTK_8139_SOFTWARE_CACHE_COHERENCY HAL_DCACHE_FLUSH(rltk8139_info->tx_buffer + TX_BUF_SIZE * desc, total_len); #endif /* * Now setup the correct transmit descriptor to actually send the data. * The early TX threshold is incremented by the driver until we reach a * size that prevents transmit underruns. (An earlier attempt to calculate * this parameter from the packet size didn't work). */ OUTL((rltk8139_info->tx_threshold << ERTXTH_SHIFT) | (total_len & SIZE), rltk8139_info->base_address + TSD0 + (desc<<2)); } /* * This routine actually retrieves data from the receive ring by * copying it into the specified scatter/gather buffers. Again, * we assume the scatter/gather list is OK and don't check against * total length. */ static void rltk8139_recv(struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len) { Rltk8139_t *rltk8139_info; struct eth_drv_sg *last_sg; cyg_uint8 *rx_buffer; rltk8139_info = (Rltk8139_t *)(sc->driver_private); rx_buffer = rltk8139_info->rx_current; /* * Invalidate the cache line(s) mapped to the receive buffer * if necessary. */ #ifdef CYGPKG_DEVS_ETH_RLTK_8139_SOFTWARE_CACHE_COHERENCY HAL_DCACHE_INVALIDATE(rx_buffer, rltk8139_info->rx_size); #endif for (last_sg = &sg_list[sg_len]; sg_list < last_sg; ++sg_list) { if (sg_list->buf != (CYG_ADDRESS)0) { memcpy((void *)(sg_list->buf), rx_buffer, sg_list->len); rx_buffer += sg_list->len; } } } /* * This function does all the heavy lifting associated with interrupts. */ static void rltk8139_deliver(struct eth_drv_sc *sc) { Rltk8139_t *rltk8139_info; cyg_uint16 status, pci_status; cyg_uint32 tsd; int desc; rltk8139_info = (Rltk8139_t *)(sc->driver_private); /* * The RealTek data sheet claims that reading the ISR will clear * it. This is incorrect; to clear a bit in the ISR, a '1' must be * written to the ISR instead. We immediately clear the interrupt * bits at this point. */ status = INW(rltk8139_info->base_address + ISR); OUTW(status, rltk8139_info->base_address + ISR); #ifdef DEBUG_RLTK8139_DRIVER diag_printf("rltk8139_deliver(%s): %04x\n", sc->dev_name, status); #endif /* * Check for a PCI error. This seems like a very serious error to * me, so we will reset the chip and hope for the best. */ if (status & IR_SERR) { cyg_pci_read_config_uint16(rltk8139_info->pci_device_id, CYG_PCI_CFG_STATUS, &pci_status); cyg_pci_write_config_uint16(rltk8139_info->pci_device_id, CYG_PCI_CFG_STATUS, pci_status); #ifdef DEBUG_RLTK8139_DRIVER diag_printf("rltk8139_deliver(%s): PCI error %04x\n", sc->dev_name, pci_status); #endif rltk8139_reset(sc); return; } /* Check for transmission complete (with errors or not) */ if ((status & IR_TER) || (status & IR_TOK)) { /* * Figure out which descriptors' status must be checked. We lock out * interrupts while manipulating the descriptor list because we do not * want to be interrupted at this point. */ while (1) { cyg_drv_isr_lock(); /* Check if all descriptors are ready, in which case we are done. */ if (rltk8139_info->tx_num_free_desc >= NUM_TX_DESC) { cyg_drv_isr_unlock(); break; } desc = (rltk8139_info->tx_free_desc - (NUM_TX_DESC - rltk8139_info->tx_num_free_desc)) & (NUM_TX_DESC - 1); cyg_drv_isr_unlock(); /* Get the current status of the descriptor */ tsd = INL(rltk8139_info->base_address + TSD0 + (desc<<2)); /* * If a transmit FIFO underrun occurred, increment the threshold * value. */ if ((tsd & TUN) && (rltk8139_info->tx_threshold < 64)) rltk8139_info->tx_threshold += 1; /* * Check if a transmission completed OK. RealTek's data sheet implies * that a successful transmission that experiences underrun will only * set TUN. This is not true; TOK is set for all successful * transmissions. */ if (tsd & TOK) { (sc->funs->eth_drv->tx_done)(sc, rltk8139_info->tx_desc_key[desc], 0); } else if (tsd & TABT) { /* * Set the CLRABT bit in TCR. Since I haven't encountered any * transmission aborts so far, I don't really know if this code * will work or not. */ OUTL(INL(rltk8139_info->base_address + TCR) & CLRABT, rltk8139_info->base_address + TCR); (sc->funs->eth_drv->tx_done)(sc, rltk8139_info->tx_desc_key[desc], -1); } else { /* * This descriptor is not ready. Since the descriptors are used * in a ring, this means that no more descriptors are ready. */ break; } /* * Clear the saved key value. This is not really necessary, since * the key value is never used to determine if a descriptor is * valid or not. However, clearing it is a tidier IMO. */ rltk8139_info->tx_desc_key[desc] = 0; /* * Increment the free descriptor count and go through the loop again * to see if more descriptors are ready. */ cyg_drv_isr_lock(); rltk8139_info->tx_num_free_desc += 1; cyg_drv_isr_unlock(); } } if (status & IR_ROK) { /* * Received a frame. Note that '_deliver' does not actually copy any * data; it just determines how many bytes are available and tells * the generic ethernet driver about it, which then calls into * the '_recv' function to copy the data. */ cyg_uint16 rx_pos; cyg_uint32 header, length; /* * CAPR contains the index into the receive buffer. It is controlled * completely in software. For some reason, CAPR points 16 bytes * before the actual start of the packet. */ rx_pos = (INW(rltk8139_info->base_address + CAPR) + 16) % RX_BUF_LEN; /* * Loop until the rx buffer is empty. I have no idea how the 8139 * determines if the buffer still contains a packet; it may check * that CAPR points 16 bytes before CBR. */ while (!(INB(rltk8139_info->base_address + CR) & BUFE)) { /* * Invalidate the data cache for the cache line containing the header * if necessary. */ #ifdef CYGPKG_DEVS_ETH_RLTK_8139_SOFTWARE_CACHE_COHERENCY HAL_DCACHE_INVALIDATE(&rltk8139_info->rx_ring[rx_pos], sizeof(cyg_uint32)); #endif /* * The 8139 prepends each packet with a 32 bit packet header that * contains a 16 bit length and 16 bit status field, in little-endian * byte order. */ header = HAL_LE32TOC(*((volatile cyg_uint32 *)CYGARC_UNCACHED_ADDRESS(&rltk8139_info->rx_ring[rx_pos]))); /* * If the 8139 is still copying data for this packet into the * receive ring, it will set packet length to 0xfff0. This shouldn't * ever happen because we do not use early receive. */ if ((header >> 16) == 0xFFF0) break; /* * Since the 8139 appends the ethernet CRC to every packet, we * must subtract 4 from the length to get the true packet length. */ length = (header >> 16) - 4; /* * Check if the packet was received correctly. The OpenBSD driver * resets the chip if this is not the case; we attempt to salvage * following packets by doing nothing. */ if (!(header & HDR_ROK)) { } else { /* * Packet was received OK. Determine from where to start copying * bytes. This is saved in the driver private area so rlt8139_recv * doesn't have to redetermine this information. Then, inform * the generic ethernet driver about the packet. */ rltk8139_info->rx_current = (cyg_uint8 *)CYGARC_UNCACHED_ADDRESS(rltk8139_info->rx_ring + rx_pos + 4); rltk8139_info->rx_size = length; /* Tell eCos about the packet */ (sc->funs->eth_drv->recv)(sc, length); } /* * Update CAPR. CAPR must be aligned to a 32 bit boundary, and should * point 16 bytes before it's actual position. */ rx_pos = ((rx_pos + length + 8 + 3) & ~3) % RX_BUF_LEN; OUTW((rx_pos - 16) % RX_BUF_LEN, rltk8139_info->base_address + CAPR); } } if (status & IR_RXOVW) { /* * In case of a receive buffer overflow, the RealTek data sheet claims we * should update CAPR and then write a '1' to ROK in ISR. However, none of * the other 8139 drivers I have looked at do this, so we will just reset * the chip instead. */ #ifdef DEBUG_RLTK8139_DRIVER diag_printf("rltk8139_deliver(%s): receive buffer overflow\n", sc->dev_name); #endif rltk8139_reset(sc); return; } if (status & IR_FOVW) { /* * Rx FIFO overflow. According to RealTek's data sheet, this is cleared * by writing a '1' to RXOVW. Again, none of the 8139 drivers I have * seen actually do this, so we reset the chip instead. */ #ifdef DEBUG_RLTK8139_DRIVER diag_printf("rltk8139_deliver(%s): receive FIFO overflow\n", sc->dev_name); #endif rltk8139_reset(sc); return; } if (status & IR_FUN) { /* * Packet underrun or link change interrupt. * A cable was packet underrun or re-connected ? */ #ifdef DEBUG_RLTK8139_DRIVER diag_printf("rltk8139_deliver(%s): packet underrun or link change\n", sc->dev_name); #endif } #ifndef CYGPKG_IO_ETH_DRIVERS_STAND_ALONE /* Finally, reenable interrupts */ #ifdef CYGPKG_DEVS_ETH_RLTK_8139_MASK_INTERRUPTS_IN_8139 OUTW(RLTK8139_IRQ, rltk8139_info->base_address + IMR); #else cyg_interrupt_unmask(rltk8139_info->vector); #endif #endif } /* * '_poll' does the same thing as '_deliver'. It is called periodically when * the ethernet driver is operated in non-interrupt mode, for instance by * RedBoot. */ static void rltk8139_poll(struct eth_drv_sc *sc) { Rltk8139_t *rltk8139_info; cyg_uint16 isr; #ifdef DEBUG_RLTK8139_DRIVER diag_printf("rltk8139_poll(%s)\n", sc->dev_name); #endif rltk8139_info = (Rltk8139_t *)(sc->driver_private); /* * Get the current interrupt status. If anything changed, call * _deliver. */ if ((isr = INW(rltk8139_info->base_address + ISR))) rltk8139_deliver(sc); } /* * Return the interrupt vector used by this device. */ static int rltk8139_int_vector(struct eth_drv_sc *sc) { return ((Rltk8139_t *)(sc->driver_private))->vector; } /* * Quick and dirty register dump. This is somewhat dangerous, since * we read the register space 32 bits at a time, regardless of actual * register sizes. */ #ifdef DEBUG_RLTK8139_DRIVER void rltk8139_print_state(struct eth_drv_sc *sc) { int i; Rltk8139_t *rltk8139_info; rltk8139_info = (Rltk8139_t *)(sc->driver_private); for (i = IDR0; i < FFER; i += 16) { diag_printf("8139 reg address 0x%02x = 0x%08x", i, INL(rltk8139_info->base_address + i)); diag_printf(" 0x%08x", INL(rltk8139_info->base_address + i + 4)); diag_printf(" 0x%08x", INL(rltk8139_info->base_address + i + 8)); diag_printf(" 0x%08x\n", INL(rltk8139_info->base_address + i + 12)); } } #endif #ifdef CYGPKG_DEVS_ETH_RLTK_8139_WRITE_EEPROM /* * Read mac_address from EEPROM. */ static cyg_uint16 rltk8139_eeprom_read( char *rtl_addr, cyg_uint8 eeprom_cmd, cyg_uint8 eeprom_addr ) { cyg_uint8 read_data; // read from eeprom bit data cyg_uint8 org_param; // original register parameter cyg_uint8 mask_bit8; // mask bit int icount; // for loop counter // get old parameter HAL_READ_UINT8( rtl_addr, org_param ); // ready EEPROM_WR_DATAPULSE( rtl_addr, EEPROM_PG_ON ); // set command mask_bit8 = 0x04; for(icount = 0; icount < 3; icount++){ EEPROM_WR_DATAPULSE( rtl_addr, EEPROM_PG_ON | EEPROM_PG_EECS | EEPROM_MASK( mask_bit8, eeprom_cmd )); mask_bit8 >>= 1; } // set address mask_bit8 = 0x20; for(icount = 0; icount < 6; icount++){ EEPROM_WR_DATAPULSE( rtl_addr, EEPROM_PG_ON | EEPROM_PG_EECS | EEPROM_MASK( mask_bit8, eeprom_addr )); mask_bit8 >>= 1; } // read data read_data = 0; for(icount = 0; icount < 16; icount++){ EEPROM_RD_DATA( rtl_addr, EEPROM_PG_ON | EEPROM_PG_EECS, read_data ); } // close EEPROM_WR_DATA( rtl_addr, EEPROM_PG_ON | EEPROM_PG_EECS ); EEPROM_WR_DATA( rtl_addr, EEPROM_PG_ON ); // put old parameter HAL_WRITE_UINT8(rtl_addr, org_param); return(read_data); } /* * Write in mac_address at EEPROM. */ static void rltk8139_eeprom_write( char *rtl_addr, cyg_uint8 eeprom_cmd, cyg_uint8 eeprom_addr, cyg_uint16 src_data ) { cyg_uint8 read_data; // read from eeprom bit data cyg_uint8 org_param; // original register parameter cyg_uint8 mask_bit8; // mask bit cyg_uint16 mask_bit16; // mask bit for write data int icount; // for loop counter // get old parameter HAL_READ_UINT8( rtl_addr, org_param ); // ready EEPROM_WR_DATA( rtl_addr, EEPROM_PG_ON ); EEPROM_WR_DATA( rtl_addr, EEPROM_PG_ON | EEPROM_PG_EESK ); EEPROM_WR_DATA( rtl_addr, EEPROM_PG_ON ); // set EWEN (eeprom write enable) EEPROM_WR_DATAPULSE( rtl_addr, EEPROM_PG_ON | EEPROM_PG_EECS | EEPROM_PG_EEDI ); EEPROM_WR_DATAPULSE( rtl_addr, EEPROM_PG_ON | EEPROM_PG_EECS ); EEPROM_WR_DATAPULSE( rtl_addr, EEPROM_PG_ON | EEPROM_PG_EECS ); EEPROM_WR_DATAPULSE( rtl_addr, EEPROM_PG_ON | EEPROM_PG_EECS | EEPROM_PG_EEDI ); EEPROM_WR_DATAPULSE( rtl_addr, EEPROM_PG_ON | EEPROM_PG_EECS | EEPROM_PG_EEDI ); EEPROM_WR_DATAPULSE( rtl_addr, EEPROM_PG_ON | EEPROM_PG_EECS ); EEPROM_WR_DATAPULSE( rtl_addr, EEPROM_PG_ON | EEPROM_PG_EECS ); EEPROM_WR_DATAPULSE( rtl_addr, EEPROM_PG_ON | EEPROM_PG_EECS ); EEPROM_WR_DATAPULSE( rtl_addr, EEPROM_PG_ON | EEPROM_PG_EECS ); EEPROM_WR_DATAPULSE( rtl_addr, EEPROM_PG_ON ); // set command mask_bit8 = 0x04; for(icount = 0; icount < 3; icount++){ EEPROM_WR_DATAPULSE( rtl_addr, EEPROM_PG_ON | EEPROM_PG_EECS | EEPROM_MASK( mask_bit8, eeprom_cmd )); mask_bit8 >>= 1; } // set address mask_bit8 = 0x20; for(icount = 0; icount < 6; icount++){ EEPROM_WR_DATAPULSE( rtl_addr, EEPROM_PG_ON | EEPROM_PG_EECS | EEPROM_MASK( mask_bit8, eeprom_addr )); mask_bit8 >>= 1; } // set data mask_bit16 = 0x8000; for(icount = 0; icount < 16; icount++){ EEPROM_WR_DATAPULSE( rtl_addr, EEPROM_PG_ON | EEPROM_PG_EECS | EEPROM_MASK( mask_bit16, src_data )); mask_bit16 >>= 1; } // close EEPROM_WR_DATA( rtl_addr, EEPROM_PG_ON ); EEPROM_WR_DATA( rtl_addr, EEPROM_PG_ON | EEPROM_PG_EESK ); EEPROM_WR_DATA( rtl_addr, EEPROM_PG_ON ); // write ready check EEPROM_WR_DATA( rtl_addr, EEPROM_PG_ON | EEPROM_PG_EECS ); // wait busy disable icount = 0; while( 1 ){ cyg_thread_delay( 1 ); HAL_WRITE_UINT8( rtl_addr, EEPROM_PG_ON | EEPROM_PG_EECS | EEPROM_PG_EESK ); HAL_WRITE_UINT8( rtl_addr, EEPROM_PG_ON | EEPROM_PG_EECS ); HAL_READ_UINT8( rtl_addr, read_data ); if(( read_data & EEPROM_PG_EEDO ) != 0 ){ break; } if( icount++ >= EEPROM_WR_BUSY_RETRIES ){ diag_printf("EEPROM write wait error adr=0x%02x data=0x%04x\n", eeprom_addr, src_data ); break; } } HAL_WRITE_UINT8( rtl_addr, EEPROM_PG_ON ); // put old parameter HAL_WRITE_UINT8( rtl_addr, org_param ); } #endif
Go to most recent revision | Compare with Previous | Blame | View Log