/**************************************************************************** * * HWI_PCI2.C : Part of the FASTMAC TOOL-KIT (FTK) * * HARDWARE INTERFACE MODULE FOR PCI CARDS * * Copyright (c) Madge Networks Ltd. 1994 * * COMPANY CONFIDENTIAL * * ***************************************************************************** * * The purpose of the Hardware Interface (HWI) is to supply an adapter card * independent interface to any driver. It performs nearly all of the * functions that involve affecting SIF registers on the adapter cards. * This includes downloading code to, initializing, and removing adapters. * * The HWI_PCIT.C module contains the routines specific to the Smart 16/4 * PCI(BM) based on the Madge PCI ASIC, this card supports Pseudo DMA, and * bus master DMA. * ***************************************************************************** /*--------------------------------------------------------------------------- | | DEFINITIONS | ---------------------------------------------------------------------------*/ #define PCI_PCI2_DEVICE_ID 2 #include "ftk_defs.h" /*--------------------------------------------------------------------------- | | MODULE ENTRY POINTS | ---------------------------------------------------------------------------*/ #include "ftk_intr.h" /* routines internal to FTK */ #include "ftk_extr.h" /* routines provided or used by external FTK user */ BYTE bEepromByteStore; BYTE bLastDataBit; #ifndef FTK_NO_PCI2 /*--------------------------------------------------------------------------- | | LOCAL PROCEDURES | ---------------------------------------------------------------------------*/ local WBOOLEAN hwi_pci2_read_node_address( ADAPTER * adapter ); local WORD hwi_at24_read_a_word( ADAPTER * adapter, WORD word_address ); #ifndef FTK_NO_PROBE /**************************************************************************** * * hwi_pci2_probe_card * ================== * * * PARAMETERS (passed by hwi_probe_adapter) : * ========================================== * * PROBE * resources * * resources is an array structures used to identify and record specific * information about adapters found. * * UINT length * * length is the number of structures pointed to by reources. * * WORD * valid_locations * * valid_locations is normally an array of IO locations to examine for the * presence of an adapter. However for PCI adapters the io location is read * from the BIOS, so this array can remain empty. * * UINT number_locations * * This is the number of IO locations in the above list. * * BODY : * ====== * * The hwi_pci2_probe_card routine is called by hwi_probe_adapter. It * reads the id registers to find the type of card and also reads the IRQ. * * * RETURNS : * ========= * * The routine returns the number of adapters found, or PROBE_FAILURE if * there's a problem. * ****************************************************************************/ #ifdef FTK_INIT_FUNCTION #pragma FTK_INIT_FUNCTION(hwi_pci2_probe_card) #endif export UINT hwi_pci2_probe_card( PROBE * Resources, UINT NumberOfResources, WORD * IOMask, UINT NumberIO ) { WORD i; WORD Handle; if (!sys_pci_valid_machine()) { return 0; } for (i=0;iadapter_handle; WORD sif_base; WORD ring_speed; WORD wHardFeatures; BYTE bTemp; /* * These things can all be assumed for the PCI2 Card. */ adapter->adapter_card_type = ADAPTER_CARD_TYPE_16_4_PCI2; adapter->adapter_card_revision = ADAPTER_CARD_16_4_PCI2; adapter->edge_triggered_ints = FALSE; adapter->mc32_config = 0; /* * Start off by assuming we will use pseudo DMA. */ adapter->EaglePsDMA = TRUE; /* * Save IO locations of SIF registers. */ sif_base = adapter->io_location + PCI2_SIF_OFFSET; adapter->sif_dat = sif_base + EAGLE_SIFDAT; adapter->sif_datinc = sif_base + EAGLE_SIFDAT_INC; adapter->sif_adr = sif_base + EAGLE_SIFADR; adapter->sif_int = sif_base + EAGLE_SIFINT; adapter->sif_acl = sif_base + EAGLE_SIFACL; adapter->sif_adx = sif_base + EAGLE_SIFADX; adapter->sif_dmalen = sif_base + EAGLE_DMALEN; adapter->sif_sdmadat = sif_base + EAGLE_SDMADAT; adapter->sif_sdmaadr = sif_base + EAGLE_SDMAADR; adapter->sif_sdmaadx = sif_base + EAGLE_SDMAADX; adapter->io_range = PCI2_IO_RANGE; /* * RESET the Card */ #ifndef FTK_NO_IO_ENABLE macro_enable_io(adapter); #endif /* * Clear all the RESET bits, wait at least 14uS and then assert all three * , turning on one at a time, in the following sequence * - Set CHIP_NRES * - Set SIF_NRES * - Set FIFO_NRES */ bTemp = sys_insb( adapter_handle, (WORD) (adapter->io_location + PCI2_RESET)); bTemp &= ~(PCI2_CHIP_NRES | PCI2_FIFO_NRES | PCI2_SIF_NRES); sys_outsb( adapter_handle, (WORD) (adapter->io_location + PCI2_RESET), bTemp); /* * Wait 14 uS before taking it out of reset. */ sys_wait_at_least_microseconds(14); bTemp |= PCI2_CHIP_NRES; sys_outsb( adapter_handle, (WORD) (adapter->io_location + PCI2_RESET), bTemp); bTemp |= PCI2_SIF_NRES; sys_outsb( adapter_handle, (WORD) (adapter->io_location + PCI2_RESET), bTemp); bTemp |= PCI2_FIFO_NRES; sys_outsb(adapter_handle, (WORD) (adapter->io_location + PCI2_RESET), bTemp); if (!hwi_pci2_read_node_address(adapter)) { #ifndef FTK_NO_IO_ENABLE macro_disable_io(adapter); #endif adapter->error_record.type = ERROR_TYPE_HWI; adapter->error_record.value = HWI_E_05_ADAPTER_NOT_FOUND; return FALSE; } /* * If this is 1 the card is @ 4Mbit/s 0 16MBit/s. */ ring_speed = hwi_at24_read_a_word(adapter, PCI2_EEPROM_RING_SPEED); /* * Get the amount of RAM from the serial EEPROM (in units of 128k). */ adapter->adapter_ram_size = hwi_at24_read_a_word( adapter, PCI2_EEPROM_RAM_SIZE) * 128; /* * This flag tells us if the card supports DMA, and if it supports release * 4.31 software. */ wHardFeatures = hwi_at24_read_a_word(adapter, PCI2_HWF2); #define RELEASE_431 1 #ifdef RELEASE_431 if (!(wHardFeatures & PCI2_HW2_431_READY)) { /* * This card does not support Release 4.31 software. */ adapter->error_record.type = ERROR_TYPE_HWI; adapter->error_record.value = HWI_E_05_ADAPTER_NOT_FOUND; return FALSE; } #endif if (adapter->transfer_mode == DMA_DATA_TRANSFER_MODE) { /* * Does this card support DMA ??? */ if (wHardFeatures & PCI2_BUS_MASTER_ONLY) { adapter->EaglePsDMA = FALSE; /* * Need to set the MASTER ENABLE bit in the PCI Command register * otherwise DMA will not work and will hang the machine. * The BIOS should do this, but some don't. */ sys_pci_read_config_byte( adapter_handle, PCI_CONFIG_COMMAND, &bTemp); bTemp |= PCI_CONFIG_BUS_MASTER_ENABLE; sys_pci_write_config_byte( adapter_handle, PCI_CONFIG_COMMAND, bTemp); } else { #ifndef FTK_NO_IO_ENABLE macro_disable_io(adapter); #endif adapter->error_record.type = ERROR_TYPE_HWI; adapter->error_record.value = HWI_E_06_CANNOT_USE_DMA; return FALSE; } } /* * If we've using pseudo DMA then we need a software handshake. */ if (adapter->EaglePsDMA) { adapter->mc32_config = MC_AND_ISACP_USE_PIO; } /* * Set the ring speed. If the user has specified a value then we will * use that, otherwise we will use the value read from the EEPROM. */ /* * These NSEL bits are different to other cards, bit 0 must be ~bit1 * There is a missing NOT gate in the ASIC. */ if (adapter->set_ring_speed == 16) { adapter->nselout_bits = 0; } else if (adapter->set_ring_speed == 4) { adapter->nselout_bits = 2; } else if (ring_speed == PCI2_EEPROM_4MBITS) { adapter->nselout_bits = 2; } else { adapter->nselout_bits = 0; } /* * Set the interrupt control register to enable SIF interrupts and * PCI Error interrupts, although the ISR does nowt about the latter * at present. */ sys_outsb( adapter->adapter_handle, (WORD) (adapter->io_location + PCI2_INTERRUPT_CONTROL), PCI2_SINTEN | PCI2_PCI_ERR_EN); /* * Halt the Eagle prior to downloading the MAC code - this will also * write the interrupt bits into the SIFACL register, where the MAC can * find them. */ hwi_halt_eagle(adapter); /* * Download code to adapter. * View download image as a sequence of download records. * Pass address of routine to set up DIO addresses on PCI cards. * If routine fails return failure (error record already filled in). */ if (!hwi_download_code( adapter, (DOWNLOAD_RECORD *) download_image, hwi_pci2_set_dio_address)) { #ifndef FTK_NO_IO_ENABLE macro_disable_io(adapter); #endif return FALSE; } /* * Restart the Eagle to initiate bring up diagnostics. */ hwi_start_eagle(adapter); /* * Wait for a valid bring up code, may wait 3 seconds. */ if (!hwi_get_bring_up_code(adapter)) { #ifndef FTK_NO_IO_ENABLE macro_disable_io(adapter); #endif return FALSE; } /* * Set DIO address to point to EAGLE DATA page 0x10000L. */ hwi_pci2_set_dio_address(adapter, DIO_LOCATION_EAGLE_DATA_PAGE); /* * Get the ring speed, from the Eagle DIO space. */ adapter->ring_speed = hwi_get_ring_speed(adapter); /* * Set maximum frame size from the ring speed. */ adapter->max_frame_size = hwi_get_max_frame_size(adapter); /* * If not in polling mode then set up interrupts. * interrupts_on field is used when disabling interrupts for adapter. */ if (adapter->interrupt_number != POLLING_INTERRUPTS_MODE) { adapter->interrupts_on = sys_enable_irq_channel( adapter_handle, adapter->interrupt_number); if (!adapter->interrupts_on) { adapter->error_record.type = ERROR_TYPE_HWI; adapter->error_record.value = HWI_E_0B_FAIL_IRQ_ENABLE; #ifndef FTK_NO_IO_ENABLE macro_disable_io(adapter); #endif return FALSE; } } else { adapter->interrupts_on = TRUE; } /* * Return successfully. */ #ifndef FTK_NO_IO_ENABLE macro_disable_io(adapter); #endif return TRUE; } /**************************************************************************** * * hwi_pci2_interrupt_handler * ========================= * * * PARAMETERS (passed by hwi_interrupt_entry) : * ========================================== * * ADAPTER * adapter * * This structure is used to identify and record specific information about * the required adapter. * * * BODY : * ====== * * The hwi_pci2_interrupt_handler routine is called, when an interrupt * occurs, by hwi_interrupt_entry. It checks to see if a particular card * has interrupted. The interrupt could be generated by the SIF for either * a PIO data transfer or a normal condition (received frame, SRB complete, * ARB indication etc). Note it could in fact be the case that no interrupt * has occured on the particular adapter being checked. * * On normal SIF interrupts, the interrupt is acknowledged and cleared. The * value in the SIF interrupt register is recorded in order to pass it to * the driver_interrupt_entry routine (along with the adapter details). * * On PseudoDMA interrupts, the length, direction and physical address of * the transfer is determined. A system provided routine is called to do * the data transfer itself. * * * RETURNS : * ========= * * The routine always successfully completes. * ****************************************************************************/ #ifdef FTK_IRQ_FUNCTION #pragma FTK_IRQ_FUNCTION(hwi_pci2_interrupt_handler) #endif export void hwi_pci2_interrupt_handler( ADAPTER * adapter ) { ADAPTER_HANDLE adapter_handle = adapter->adapter_handle; WORD sifacl; WORD sifint_value; WORD sifint_tmp; WORD pio_addr_lo; WBOOLEAN sifint_occurred = FALSE; WBOOLEAN pioint_occurred = FALSE; DWORD pio_addr_hi; WORD pio_len_bytes; WBOOLEAN pio_from_adapter; BYTE bInt; BYTE FAR * pio_address; WORD saved_sifadr; #ifndef FTK_NO_IO_ENABLE macro_enable_io(adapter); #endif /* * Check for SIF interrupt or PIO interrupt. */ /* * Read SIFINT, and then re-read to make sure value is stable. */ sifint_value = sys_insw(adapter_handle, adapter->sif_int); do { sifint_tmp = sifint_value; sifint_value = sys_insw(adapter_handle, adapter->sif_int); } while (sifint_tmp != sifint_value); /* * Given the SIFINT value, we can check one of the bits in it to see * if that is what caused the interrupt. */ if ((sifint_value & EAGLE_SIFINT_SYSTEM) != 0) { /* * A SIF interrupt has occurred. * This could be an SRB free, an adapter check or a received frame * interrupt. * Note that we do not process it yet - we wait until we have EOI'd * the interrupt controller. */ sifint_occurred = TRUE; /* * Clear EAGLE_SIFINT_HOST_IRQ to acknowledge interrupt at SIF. */ sys_outsw(adapter_handle, adapter->sif_int, 0); /* * Call driver with details of SIF interrupt. */ driver_interrupt_entry(adapter_handle, adapter, sifint_value); } /* * Now read the SIFACL register to check for a PseudoDMA interrupt. */ if (adapter->transfer_mode != DMA_DATA_TRANSFER_MODE) { sifacl = sys_insw(adapter_handle, adapter->sif_acl); if ((sifacl & EAGLE_SIFACL_SWHRQ) != 0) { /* * PIO interrupt has occurred. Transfer data to/from adapter. */ pioint_occurred = TRUE; /* * Preserve SIFADR. */ saved_sifadr = sys_insw(adapter_handle, adapter->sif_adr); /* * Start the software handshake. */ sys_outsw(adapter_handle, adapter->sif_adr, DIO_LOCATION_DMA_CONTROL); sys_outsw(adapter_handle, adapter->sif_dat, 0); /* * By writing the SWHLDA bit, we "start" the transfer, * causing the SDMA registers to mapped in. */ macro_setw_bit( adapter_handle, adapter->sif_acl, EAGLE_SIFACL_SWHLDA); /* * Determine what direction the data transfer is to take place in. */ pio_from_adapter = sys_insw( adapter_handle, adapter->sif_acl) & EAGLE_SIFACL_SWDDIR; pio_len_bytes = sys_insw( adapter_handle, adapter->sif_dmalen); pio_addr_lo = sys_insw( adapter_handle, adapter->sif_sdmaadr); pio_addr_hi = (DWORD) sys_insw( adapter_handle, adapter->sif_sdmaadx); pio_address = (BYTE FAR *) ((pio_addr_hi << 16) | ((DWORD) pio_addr_lo)); /* * Do the actual data transfer. */ /* * Note that Fastmac only copies whole WORDs to DWORD boundaries. * FastmacPlus, however, can transfer any length to any address. */ if (pio_from_adapter) { /* * Transfer into host memory from adapter. */ /* * First, check if host address is on an odd byte boundary. */ if ((card_t)pio_address % 2) { pio_len_bytes--; *(pio_address++) = sys_insb( adapter_handle, (WORD) (adapter->sif_sdmadat + 1)); } /* * Now transfer the bulk of the data. */ sys_rep_insw( adapter_handle, adapter->sif_sdmadat, pio_address, (WORD) (pio_len_bytes >> 1)); /* * Finally transfer any trailing byte. */ if (pio_len_bytes % 2) { *(pio_address+pio_len_bytes - 1) = sys_insb(adapter_handle, adapter->sif_sdmadat); } } else { /* * Transfer into adapter memory from the host. */ if ((card_t)pio_address % 2) { pio_len_bytes--; sys_outsb( adapter_handle, (WORD) (adapter->sif_sdmadat + 1), *(pio_address++)); } sys_rep_outsw( adapter_handle, adapter->sif_sdmadat, pio_address, (WORD) (pio_len_bytes >> 1)); if (pio_len_bytes % 2) { sys_outsb( adapter_handle, adapter->sif_sdmadat, *(pio_address+pio_len_bytes-1)); } } /* * Wait for SWHLDA to go low, it is not safe to access * normal SIF registers until this is the case. */ do { sifacl = sys_insw(adapter_handle, adapter->sif_acl); } while (sifacl & EAGLE_SIFACL_SWHLDA); /* * Finish the software handshake. We need a dummy ready so that * the SIF can stabalise. */ sys_outsw(adapter_handle, adapter->sif_adr, DIO_LOCATION_DMA_CONTROL); sys_insw(adapter_handle, adapter->sif_dat); sys_outsw(adapter_handle, adapter->sif_adr, DIO_LOCATION_DMA_CONTROL); sys_outsw(adapter_handle, adapter->sif_dat, 0xFFFF); /* * Restore SIFDR. */ sys_outsw(adapter_handle, adapter->sif_adr, saved_sifadr); } } if (pioint_occurred || sifint_occurred) { #ifndef FTK_NO_CLEAR_IRQ /* * Acknowledge/clear interrupt at interrupt controller. */ sys_clear_controller_interrupt( adapter_handle, adapter->interrupt_number); #endif } else { bInt = sys_insb( adapter->adapter_handle, (WORD) (adapter->io_location + PCI2_INTERRUPT_STATUS)); if (bInt & PCI2_PCI_INT) { #ifndef FTK_NO_CLEAR_IRQ sys_clear_controller_interrupt( adapter_handle, adapter->interrupt_number); #endif } } /* * Let system know we have finished accessing the IO ports. */ #ifndef FTK_NO_IO_ENABLE macro_disable_io(adapter); #endif } /**************************************************************************** * * hwi_pci2_remove_card * =================== * * * PARAMETERS (passed by hwi_remove_card) * ====================================== * * ADAPTER * adapter * * This structure is used to identify and record specific information about * the required adapter. * * * BODY : * ====== * * The hwi_smart16_remove_card routine is called by hwi_remove_adapter. It * disables interrupts if they are being used. It also resets the adapter. * * * RETURNS : * ========= * * The routine always successfully completes. * ****************************************************************************/ #ifdef FTK_RES_FUNCTION #pragma FTK_RES_FUNCTION(hwi_pci2_remove_card) #endif export void hwi_pci2_remove_card( ADAPTER * adapter ) { ADAPTER_HANDLE adapter_handle = adapter->adapter_handle; WORD wGenConAddr = adapter->io_location + PCI_GENERAL_CONTROL_REG; WORD sifacl; BYTE bTemp; /* * Interrupt must be disabled at adapter before unpatching interrupt. * Even in polling mode we must turn off interrupts at adapter. */ #ifndef FTK_NO_IO_ENABLE macro_enable_io(adapter); #endif sifacl = sys_insw(adapter_handle, adapter->sif_acl); sifacl = (sifacl & ~(EAGLE_SIFACL_PSDMAEN | EAGLE_SIFACL_SINTEN)); sys_outsw(adapter_handle, adapter->sif_acl, sifacl); if (adapter->interrupts_on) { if (adapter->interrupt_number != POLLING_INTERRUPTS_MODE) { sys_disable_irq_channel( adapter_handle, adapter->interrupt_number); } adapter->interrupts_on = FALSE; } /* * RESET the Eagle */ bTemp = sys_insb( adapter_handle, (WORD) (adapter->io_location + PCI2_RESET)); bTemp &= ~(PCI2_CHIP_NRES | PCI2_FIFO_NRES | PCI2_SIF_NRES); sys_outsb( adapter_handle, (WORD) (adapter->io_location + PCI2_RESET), bTemp); /* * Wait 14 uS before taking it out of reset. */ sys_wait_at_least_microseconds(14); bTemp |= (PCI2_CHIP_NRES | PCI2_FIFO_NRES | PCI2_SIF_NRES); sys_outsb( adapter_handle, (WORD) (adapter->io_location + PCI2_RESET), bTemp); #ifndef FTK_NO_IO_ENABLE macro_disable_io(adapter); #endif } /**************************************************************************** * * hwi_pci2_set_dio_address * ======================= * * The hwi_pci2_set_dio_address routine is used, with PCI cards, for * putting a 32 bit DIO address into the SIF DIO address and extended DIO * address registers. Note that the extended address register should be * loaded first. * ****************************************************************************/ #ifdef FTK_INIT_FUNCTION #pragma FTK_INIT_FUNCTION(hwi_pci2_set_dio_address) #endif export void hwi_pci2_set_dio_address( ADAPTER * adapter, DWORD dio_address ) { ADAPTER_HANDLE adapter_handle = adapter->adapter_handle; WORD sif_dio_adr = adapter->sif_adr; WORD sif_dio_adrx = adapter->sif_adx; /* * Load extended DIO address register with top 16 bits of address. * Always load extended address register first. */ sys_outsw( adapter_handle, sif_dio_adrx, (WORD)(dio_address >> 16)); /* * Load DIO address register with low 16 bits of address. */ sys_outsw( adapter_handle, sif_dio_adr, (WORD)(dio_address & 0x0000FFFF)); } /*--------------------------------------------------------------------------- | | LOCAL PROCEDURES | ---------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------- | | hwi_pci2_read_node_address | ========================== | | The hwi_pci2_read_node_address routine reads in the node address from | the SEEPROM, and checks that it is a valid Madge node address. | ---------------------------------------------------------------------------*/ #ifdef FTK_INIT_FUNCTION #pragma FTK_INIT_FUNCTION(hwi_pci2_read_node_address) #endif local WBOOLEAN hwi_pci2_read_node_address( ADAPTER * adapter ) { WORD temp; temp = hwi_at24_read_a_word(adapter, PCIT_EEPROM_BIA_WORD0); adapter->permanent_address.byte[0] = (BYTE) ((temp ) & 0x00ff); adapter->permanent_address.byte[1] = (BYTE) ((temp >> 8) & 0x00ff); temp = hwi_at24_read_a_word(adapter, PCIT_EEPROM_BIA_WORD1); adapter->permanent_address.byte[2] = (BYTE) ((temp ) & 0x00ff); adapter->permanent_address.byte[3] = (BYTE) ((temp >> 8) & 0x00ff); temp = hwi_at24_read_a_word(adapter, PCIT_EEPROM_BIA_WORD2); adapter->permanent_address.byte[4] = (BYTE) ((temp ) & 0x00ff); adapter->permanent_address.byte[5] = (BYTE) ((temp >> 8) & 0x00ff); return TRUE; } /*************************************************************************** * * * Local routines for accessing the AT93AT24 Serial EEPROM, this is the same* * EEPROM fitted to the PNP card and the PCI 2 card. * * * * The routines are 'nicked' from the PCI-T card with just write_bits and * * input having been changed to reflect I/O through I/O space not PCI config* * space. * * * ***************************************************************************/ local void hwi_at24_delay( ADAPTER * adapter ); local void hwi_at24_set_clk( ADAPTER * adapter ); local void hwi_at24_clr_clk( ADAPTER * adapter ); local void hwi_at24_twitch_clk( ADAPTER * adapter ); local void hwi_at24_start_bit( ADAPTER * adapter ); local void hwi_at24_stop_bit( ADAPTER * adapter ); local WBOOLEAN hwi_at24_wait_ack( ADAPTER * adapter ); local WBOOLEAN hwi_at24_dummy_wait_ack( ADAPTER * adapter ); /************************************************************************ * Read the 3 EEPROM bits * * Inputs : Adapter structure. * * Outputs : Value read from control register. ***********************************************************************/ #ifdef FTK_INIT_FUNCTION #pragma FTK_INIT_FUNCTION(hwi_at24_input) #endif local BYTE hwi_at24_input(ADAPTER * adapter) { BYTE bInput; bInput = sys_insb( adapter->adapter_handle, (WORD) (adapter->io_location + PCI2_SEEPROM_CONTROL)); /* * Store the 5 bits which don't interrest us */ bEepromByteStore = bInput & (BYTE)0xF8; return bInput; } /************************************************************************ * Write to the 3 EEPROM bits * * Inputs : Adapter structure. * The data to be written. * * Outputs : None. ***********************************************************************/ #ifdef FTK_INIT_FUNCTION #pragma FTK_INIT_FUNCTION(hwi_at24_write_bits) #endif local void hwi_at24_write_bits(ADAPTER * adapter, BYTE bValue) { BYTE bTemp; /* * Restore the 5 bits we were not interested in from the previous read */ bTemp |= bEepromByteStore; sys_outsb( adapter->adapter_handle, (WORD) (adapter->io_location + PCI2_SEEPROM_CONTROL), bValue); } /************************************************************************ * * Write to the three EEPROM bits. * * We have to store the DATA bit as we cannot definately read it back. * * Inputs : Adapter structure. * The data to be written. * * Outputs : None. ***********************************************************************/ #ifdef FTK_INIT_FUNCTION #pragma FTK_INIT_FUNCTION(hwi_at24_output) #endif local void hwi_at24_output(ADAPTER * adapter, BYTE bValue) { bLastDataBit = bValue & (BYTE)AT24_IO_DATA; hwi_at24_write_bits(adapter, bValue); } /************************************************************************ * * Write to the three EEPROM bits. * * Set the DATA bit to the most recent written bit. * * Inputs : Adapter structure. * The data to be written. * * Outputs : None. ***********************************************************************/ #ifdef FTK_INIT_FUNCTION #pragma FTK_INIT_FUNCTION(hwi_at24_output_preserve_data) #endif local void hwi_at24_output_preserve_data(ADAPTER * adapter, BYTE bValue) { bValue &= ~AT24_IO_DATA; bValue |= bLastDataBit; hwi_at24_write_bits(adapter, bValue); } /************************************************************************ * Delay to allow for serial device timing issues * * Inputs : Adapter structure * * Outputs : None ************************************************************************/ #ifdef FTK_INIT_FUNCTION #pragma FTK_INIT_FUNCTION(hwi_at24_delay) #endif local void hwi_at24_delay(ADAPTER * adapter) { UINT i; for (i = 0; i < 100; i++) { sys_insb(adapter->adapter_handle, adapter->io_location); } } /************************************************************************ * Set the serial device clock bit * * Inputs : Adapter structure * * Outputs : None ************************************************************************/ #ifdef FTK_INIT_FUNCTION #pragma FTK_INIT_FUNCTION(hwi_at24_set_clk) #endif local void hwi_at24_set_clk(ADAPTER * adapter) { BYTE temp; temp = hwi_at24_input(adapter); temp |= AT24_IO_CLOCK; hwi_at24_output_preserve_data(adapter, temp); } /************************************************************************ * * Clears the serial device clock bit * * Inputs : Adapter structure * * Outputs : None ************************************************************************/ #ifdef FTK_INIT_FUNCTION #pragma FTK_INIT_FUNCTION(hwi_at24_clr_clk) #endif local void hwi_at24_clr_clk(ADAPTER * adapter) { BYTE temp; temp = hwi_at24_input(adapter); temp &= ~AT24_IO_CLOCK; hwi_at24_output_preserve_data(adapter, temp); return; } /************************************************************************ * * hwi_at24_read_data * Read a data bit from the serial EEPROM. It is assumed that the clock is low * on entry to this routine. The data bit is forced high to allow access to * the data from the EEPROM, then the clock is toggled, with a read of the * data in the middle. * * Beware! The latched data bit will be set on exit. * ************************************************************************/ #ifdef FTK_INIT_FUNCTION #pragma FTK_INIT_FUNCTION(hwi_at24_read_data) #endif local BYTE hwi_at24_read_data(ADAPTER * adapter) { BYTE bData; /* * Set the latched data bit to disconnect us from the data line. */ bData = AT24_IO_ENABLE | AT24_IO_DATA; hwi_at24_output(adapter, bData); /* * Set the clk bit to enable the data line. */ hwi_at24_set_clk(adapter); /* * Read the data bit. */ bData = hwi_at24_input(adapter); /* * Get the Data bit into bit 0. */ bData &= AT24_IO_DATA; bData >>= 1; /* * Clear clock again. */ hwi_at24_clr_clk(adapter); return bData; } /************************************************************************ * * hwi_at24_write_data * * Write a data bit to the serial EEPROM. No clock toggle is performed. * ************************************************************************/ #ifdef FTK_INIT_FUNCTION #pragma FTK_INIT_FUNCTION(hwi_at24_write_data) #endif local void hwi_at24_write_data(ADAPTER * adapter, BYTE bData) { BYTE bTemp; /* * The bit value is in position 0, get it into position 1 */ bData <<= 1; /* * Not strictly neccessary, but I'm paranoid. */ bData &= AT24_IO_DATA; bTemp = hwi_at24_input(adapter); bTemp &= ~AT24_IO_DATA; bTemp |= bData; hwi_at24_output(adapter, bTemp); } /************************************************************************ * * hwi_at24_enable_eeprom * * Must be called at the start of eeprom access to ensure we can pull low * the data and clock pins on the EEPROM. Forces the clock signal low, as part * of the strategy of routines assuming the clock is low on entry to them. * * Inputs : Adapter structure * * Outputs : None ************************************************************************/ #ifdef FTK_INIT_FUNCTION #pragma FTK_INIT_FUNCTION(hwi_at24_enable_eeprom) #endif local void hwi_at24_enable_eeprom(ADAPTER * adapter) { BYTE temp; temp = hwi_at24_input(adapter); temp |= AT24_IO_ENABLE; hwi_at24_output(adapter, temp); } /************************************************************************ * * hwi_at24_start_bit * * Send a "START bit" to the serial EEPROM. This involves toggling the * clock bit low to high, with data set on the rising edge and cleared on the * falling edge. Assumes clock is low and EEPROM enabled on entry. * * Inputs : Adapter structure * * Outputs : None ************************************************************************/ #ifdef FTK_INIT_FUNCTION #pragma FTK_INIT_FUNCTION(hwi_at24_start_bit) #endif local void hwi_at24_start_bit(ADAPTER * adapter) { BYTE bData; bData = AT24_IO_ENABLE | AT24_IO_DATA; /* * Set the Data bit */ hwi_at24_output(adapter, bData); hwi_at24_set_clk(adapter); /* * Clear the Data bit. */ bData = AT24_IO_ENABLE | AT24_IO_CLOCK; hwi_at24_output(adapter, bData); hwi_at24_clr_clk(adapter); } /************************************************************************ * * hwi_at24_stop_bit * * Send a "STOP bit" to the serial EEPROM. This involves toggling the * clock bit low to high, with data clear on the rising edge and set on the * falling edge. Assumes clock is low and EEPROM enabled on entry. * Inputs : Adapter structure * * Outputs : None ************************************************************************/ #ifdef FTK_INIT_FUNCTION #pragma FTK_INIT_FUNCTION(hwi_at24_stop_bit) #endif local void hwi_at24_stop_bit(ADAPTER * adapter) { BYTE bData; bData = AT24_IO_ENABLE; /* * Clear the Data Bit */ hwi_at24_output(adapter, bData); hwi_at24_set_clk(adapter); /* * Set the Data Bit. */ bData |= (AT24_IO_DATA | AT24_IO_CLOCK); hwi_at24_output(adapter, bData); hwi_at24_clr_clk(adapter); } /************************************************************************ * * hwi_at24_wait_ack * * Wait for an ack from the EEPROM. * Outputs : TRUE or FALSE ************************************************************************/ #ifdef FTK_INIT_FUNCTION #pragma FTK_INIT_FUNCTION(hwi_at24_wait_ack) #endif local WBOOLEAN hwi_at24_wait_ack(ADAPTER * adapter) { WBOOLEAN Acked = FALSE; UINT i; BYTE bData; for (i = 0; i < 10; i++) { bData = hwi_at24_read_data(adapter); bData &= 1; if (!bData) { Acked = TRUE; break; } } return Acked; } /************************************************************************ * * hwi_at24_dummy_wait_ack * * Wait for a negative ack from the EEPROM. * * Outputs : TRUE or FALSE ************************************************************************/ #ifdef FTK_INIT_FUNCTION #pragma FTK_INIT_FUNCTION(hwi_at24_dummy_wait_ack) #endif local WBOOLEAN hwi_at24_dummy_wait_ack(ADAPTER * adapter) { WBOOLEAN Acked = FALSE; UINT i; BYTE bData; for (i = 0; i < 10; i++) { bData = hwi_at24_read_data(adapter); if (bData & 1) { Acked = TRUE; break; } } return Acked; } /************************************************************************ * * hwi_at24_serial_read_bits * * Read a Byte from the serial EEPROM. * * NB This routine gets 8 bits from the EEPROM, but relies upon commands * having been sent to the EEPROM 1st. In order to read a byte use * hwi_at24_receive_data. * * Outputs : None ************************************************************************/ #ifdef FTK_INIT_FUNCTION #pragma FTK_INIT_FUNCTION(hwi_at24_serial_read_bits) #endif local BYTE hwi_at24_serial_read_bits(ADAPTER * adapter) { BYTE bData = 0; BYTE bBit; UINT i; for (i = 0; i < 8; i++) { /* * The EEPROM clocks data out MSB first. */ bBit = hwi_at24_read_data(adapter); bData <<= 1; bData |= bBit; } return bData; } /************************************************************************ * * hwi_at24_serial_write_bits * * Send 8 bits to the serial EEPROM. * * Outputs : None ************************************************************************/ #ifdef FTK_INIT_FUNCTION #pragma FTK_INIT_FUNCTION(hwi_at24_serial_write_bits) #endif local void hwi_at24_serial_write_bits(ADAPTER * adapter, BYTE bData) { BYTE bBit; UINT i; for (i = 0; i < 8; i++) { bBit = (BYTE)(bData >> (7-i)); bBit &= 1; hwi_at24_write_data(adapter, bBit); /* * Toggle the clock line to pass the data to the device. */ hwi_at24_set_clk(adapter); hwi_at24_clr_clk(adapter); } } /************************************************************************ * * hwi_at24_serial_send_cmd * * Send a command to the serial EEPROM. * * Outputs : TRUE if sent OK ************************************************************************/ #ifdef FTK_INIT_FUNCTION #pragma FTK_INIT_FUNCTION(hwi_at24_serial_send_cmd) #endif local WBOOLEAN hwi_at24_serial_send_cmd(ADAPTER * adapter, BYTE bCommand) { UINT i = 0; WBOOLEAN Sent = FALSE; while ((i < 40) && (Sent == FALSE)) { i++; /* * Wake the device up. */ hwi_at24_start_bit(adapter); hwi_at24_serial_write_bits(adapter, bCommand); Sent = hwi_at24_wait_ack(adapter); } return Sent; } /************************************************************************ * * hwi_at24_serial_send_cmd_addr * * Send a command and address to the serial EEPROM. * * Outputs : TRUE if sent OK ************************************************************************/ #ifdef FTK_INIT_FUNCTION #pragma FTK_INIT_FUNCTION(hwi_at24_serial_send_cmd_addr) #endif local WBOOLEAN hwi_at24_serial_send_cmd_addr(ADAPTER * adapter, BYTE bCommand, BYTE bAddr) { WBOOLEAN RetCode; RetCode = hwi_at24_serial_send_cmd(adapter, bCommand); if (RetCode) { hwi_at24_serial_write_bits(adapter, bAddr); RetCode = hwi_at24_wait_ack(adapter); } return RetCode; } /************************************************************************ * * hwi_at24_serial_receive_data * * Having set up the address we want to read from, read the data. * * Outputs : Data read back. * ************************************************************************/ #ifdef FTK_INIT_FUNCTION #pragma FTK_INIT_FUNCTION(hwi_at24_serial_receive_data) #endif local BYTE hwi_at24_serial_receive_data(ADAPTER * adapter) { BYTE bData; WBOOLEAN Acked; bData = hwi_at24_serial_read_bits(adapter); Acked = hwi_at24_dummy_wait_ack(adapter); if (!Acked) { bData = 0xFF; } return bData; } /************************************************************************ * * hwi_at24_serial_read_byte * * Read a byte of data from the specified ROM address * * Outputs : Data read back. * ************************************************************************/ #ifdef FTK_INIT_FUNCTION #pragma FTK_INIT_FUNCTION(hwi_at24_serial_read_byte) #endif local BYTE hwi_at24_serial_read_byte(ADAPTER * adapter, BYTE bAddr) { BYTE bData; hwi_at24_enable_eeprom(adapter); /* * Send the serial device a dummy WRITE command * that contains the address of the byte we want to * read ! */ hwi_at24_serial_send_cmd_addr(adapter, AT24_WRITE_CMD, bAddr); /* * Send the read command */ hwi_at24_serial_send_cmd(adapter, AT24_READ_CMD); /* * Read the data */ bData = hwi_at24_serial_receive_data(adapter); /* * Deselect the EEPROM */ hwi_at24_stop_bit(adapter); return bData; } /************************************************************************ * * hwi_at24_read_a_word * * Read a word of data from the specified ROM address * * Outputs : Data read back. * ************************************************************************/ #ifdef FTK_INIT_FUNCTION #pragma FTK_INIT_FUNCTION(hwi_at24_read_a_word) #endif local WORD hwi_at24_read_a_word(ADAPTER * adapter, WORD word_address) { WORD wData; BYTE bLoByte; BYTE bByteAddress = (BYTE)((word_address * 2)&0xFF); bLoByte = hwi_at24_serial_read_byte(adapter, bByteAddress); wData = (WORD) hwi_at24_serial_read_byte( adapter, (BYTE)(bByteAddress + 1)); wData <<= 8; wData |= bLoByte; return wData; } #endif /******* End of HWI_PCI2.C **************************************************/