summaryrefslogtreecommitdiffstats
path: root/private/ntos/ndis/madge/driver/hwi_pci.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--private/ntos/ndis/madge/driver/hwi_pci.c1567
1 files changed, 1567 insertions, 0 deletions
diff --git a/private/ntos/ndis/madge/driver/hwi_pci.c b/private/ntos/ndis/madge/driver/hwi_pci.c
new file mode 100644
index 000000000..7eb19fec0
--- /dev/null
+++ b/private/ntos/ndis/madge/driver/hwi_pci.c
@@ -0,0 +1,1567 @@
+/****************************************************************************
+*
+* HWI_PCI.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_PCI.C module contains the routines specific to the Smart 16/4 PCI
+* card which supports MMIO and pseudo DMA.
+*
+*****************************************************************************
+
+/*---------------------------------------------------------------------------
+|
+| DEFINITIONS
+|
+---------------------------------------------------------------------------*/
+
+#define PCI_PCI1_DEVICE_ID 1
+
+#define INT_PCIMMIO_RX 0x40
+#define INT_PCIMMIO_TX 0x20
+#define INT_PCIMMIO (INT_PCIMMIO_RX | INT_PCIMMIO_TX)
+
+#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 */
+
+#ifndef FTK_NO_PCI
+
+/*---------------------------------------------------------------------------
+|
+| LOCAL PROCEDURES
+|
+---------------------------------------------------------------------------*/
+
+local void
+pci_c46_write_bit(
+ ADAPTER * adapter,
+ WORD mask,
+ WBOOLEAN set_bit
+ );
+
+local void
+pci_c46_twitch_clock(
+ ADAPTER * adapter
+ );
+
+local WORD
+pci_c46_read_data(
+ ADAPTER * adapter
+ );
+
+local WBOOLEAN
+hwi_pci_read_node_address(
+ ADAPTER * adapter
+ );
+
+local WORD
+hwi_pci_read_eeprom_word(
+ ADAPTER * adapter,
+ WORD word_address
+ );
+
+#ifndef FTK_NO_PROBE
+/****************************************************************************
+*
+* hwi_pci_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_pci_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_pci_probe_card)
+#endif
+
+export UINT
+hwi_pci_probe_card(
+ PROBE * Resources,
+ UINT NumberOfResources,
+ WORD * IOMask,
+ UINT NumberIO
+ )
+{
+ WORD i;
+ WORD Handle;
+ DWORD MMIOAddress;
+
+ if (!sys_pci_valid_machine())
+ {
+ return 0;
+ }
+
+ for (i=0;i<NumberOfResources;i++)
+ {
+ if (!sys_pci_find_card(&Handle, i,PCI_PCI1_DEVICE_ID))
+ {
+ break;
+ }
+
+ Resources[i].adapter_card_bus_type = ADAPTER_CARD_PCI_BUS_TYPE;
+
+ if (!sys_pci_get_io_base(Handle, &(Resources[i].io_location)))
+ {
+ return PROBE_FAILURE;
+ }
+
+ if (!sys_pci_get_irq(Handle, &(Resources[i].interrupt_number)))
+ {
+ return PROBE_FAILURE;
+ }
+
+ if (!sys_pci_get_mem(Handle, &MMIOAddress))
+ {
+ /*
+ * The user wants MMIO and we weren't given any Memory
+ */
+
+ return PROBE_FAILURE;
+ }
+
+ /*
+ * Convert the address to virtual addressing.
+ */
+
+ Resources[i].mmio_base_address = sys_phys_to_virt(0, MMIOAddress);
+
+ /*
+ * We can't read the memory size from the serial EEPROM until the
+ * hwi_pci_install_card function is called.
+ */
+
+ Resources[i].adapter_ram_size = 512;
+
+ Resources[i].adapter_card_type = ADAPTER_CARD_TYPE_16_4_PCI;
+
+ Resources[i].transfer_mode = PIO_DATA_TRANSFER_MODE;
+
+ }
+
+ return i;
+}
+#endif
+
+/****************************************************************************
+*
+* hwi_pci_install_card
+* ====================
+*
+*
+* PARAMETERS (passed by hwi_install_adapter) :
+* ============================================
+*
+* ADAPTER * adapter
+*
+* This structure is used to identify and record specific information about
+* the required adapter.
+*
+* DOWNLOAD_IMAGE * download_image
+*
+* This is the code to be downloaded to the adapter. The image must be of
+* the correct type i.e. must be downloadable into the adapter. If the
+* pointer is 0 downloading is not done.
+*
+*
+* BODY :
+* ======
+*
+* hwi_pci_install_card is called by hwi_install_adapter. It sets up
+* the adapter card and downloads the required code to it. Firstly, it
+* checks there is a valid adapter at the required IO address by reading
+* the node address from the BIA PROM. It then sets up and checks various
+* on-board registers for correct operation.
+*
+* Then, it halts the EAGLE, downloads the code, restarts the EAGLE and
+* waits up to 3 seconds for a valid bring-up code. If interrupts are
+* required, these are enabled by operating system specific calls.
+* The adapter is set up for Eagle Pseudo-DMA, since real DMA is not used.
+*
+*
+* RETURNS :
+* =========
+*
+* The routine returns TRUE if it succeeds. If this routine fails (returns
+* FALSE) then a subsequent call to driver_explain_error, with the adapter
+* handle corresponding to the adapter parameter used here, will give an
+* explanation.
+*
+****************************************************************************/
+
+#ifdef FTK_INIT_FUNCTION
+#pragma FTK_INIT_FUNCTION(hwi_pci_install_card)
+#endif
+
+export WBOOLEAN
+hwi_pci_install_card(
+ ADAPTER * adapter,
+ DOWNLOAD_IMAGE * download_image
+ )
+{
+ ADAPTER_HANDLE adapter_handle = adapter->adapter_handle;
+ WORD control_reg;
+ WORD control_value;
+ WORD sif_base;
+ WORD ring_speed;
+
+ /*
+ * These things can all be assumed for the PCI Card.
+ */
+
+ adapter->adapter_card_type = ADAPTER_CARD_TYPE_16_4_PCI;
+ adapter->adapter_card_revision = ADAPTER_CARD_16_4_PCI;
+ adapter->edge_triggered_ints = FALSE;
+ adapter->mc32_config = 0;
+
+ /*
+ * Start off by assuming we will use pseudo DMA.
+ */
+
+ adapter->EaglePsDMA = TRUE;
+
+ /*
+ * If we are supposed to use MMIO then enable it.
+ */
+
+ if (adapter->transfer_mode == MMIO_DATA_TRANSFER_MODE)
+ {
+ adapter->mc32_config = PCI1_ENABLE_MMIO;
+ adapter->EaglePsDMA = FALSE;
+ }
+ else
+ {
+ /*
+ * If we've using pseudo DMA then we need a software handshake.
+ */
+ adapter->mc32_config = MC_AND_ISACP_USE_PIO;
+ }
+
+ /*
+ * Save IO locations of SIF registers.
+ */
+
+ sif_base = adapter->io_location + PCI_FIRST_SIF_REGISTER;
+
+ adapter->sif_dat = sif_base + PCI_SIFDAT;
+ adapter->sif_datinc = sif_base + PCI_SIFDAT_INC;
+ adapter->sif_adr = sif_base + PCI_SIFADR;
+ adapter->sif_int = sif_base + PCI_SIFINT;
+ adapter->sif_acl = sif_base + PCI_SIFACL;
+ adapter->sif_adx = sif_base + PCI_SIFADX;
+ adapter->sif_dmalen = sif_base + PCI_DMALEN;
+ adapter->sif_sdmadat = sif_base + PCI_SDMADAT;
+ adapter->sif_sdmaadr = sif_base + PCI_SDMAADR;
+ adapter->sif_sdmaadx = sif_base + PCI_SDMAADX;
+
+ adapter->io_range = PCI_IO_RANGE;
+
+ /*
+ * Set up a pointer to the general control register.
+ */
+
+ control_reg = adapter->io_location + PCI_GENERAL_CONTROL_REG;
+
+#ifndef FTK_NO_IO_ENABLE
+ macro_enable_io(adapter);
+#endif
+
+ /*
+ * Toggle the Reset bit to Reset the Eagle and take it out of reset
+ */
+
+ control_value = 0;
+ sys_outsw(
+ adapter_handle,
+ control_reg,
+ control_value);
+
+ sys_wait_at_least_microseconds(28);
+
+ control_value = PCI1_SRESET;
+ sys_outsw(
+ adapter_handle,
+ control_reg,
+ control_value);
+
+ /*
+ * Read the node address.
+ */
+
+ if (!hwi_pci_read_node_address(adapter))
+ {
+ adapter->error_record.type = ERROR_TYPE_HWI;
+ adapter->error_record.value = HWI_E_05_ADAPTER_NOT_FOUND;
+#ifndef FTK_NO_IO_ENABLE
+ macro_disable_io(adapter);
+#endif
+ return FALSE;
+ }
+
+ ring_speed = hwi_pci_read_eeprom_word(adapter, PCI_EEPROM_RING_SPEED);
+
+ /*
+ * Get the amount of RAM from the serial EEPROM (in units of 128k).
+ */
+
+ adapter->adapter_ram_size = hwi_pci_read_eeprom_word(
+ adapter,
+ PCI_EEPROM_RAM_SIZE) * 128;
+
+ /*
+ * 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.
+ */
+
+ if (adapter->set_ring_speed == 16)
+ {
+ control_value &= ~PCI1_RSPEED_4MBPS;
+ }
+ else if (adapter->set_ring_speed == 4)
+ {
+ control_value |= PCI1_RSPEED_4MBPS;
+ }
+ else if (ring_speed == PCI_EEPROM_16MBPS)
+ {
+ control_value &= ~PCI1_RSPEED_4MBPS;
+ }
+ else
+ {
+ control_value |= PCI1_RSPEED_4MBPS;
+ }
+
+ control_value |= PCI1_RSPEED_VALID;
+ sys_outsw(
+ adapter_handle,
+ control_reg,
+ control_value);
+
+ /*
+ * 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_pci_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_pci_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_pci_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_pci_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_pci_interrupt_handler)
+#endif
+
+export void
+hwi_pci_interrupt_handler(
+ ADAPTER * adapter
+ )
+{
+ ADAPTER_HANDLE adapter_handle = adapter->adapter_handle;
+ WORD sifacl;
+ WORD sifint_value;
+ WORD sifint_tmp;
+ WBOOLEAN sifint_occurred = FALSE;
+ WBOOLEAN pioint_occurred = FALSE;
+ WORD pio_addr_lo;
+ DWORD pio_addr_hi;
+ WORD pio_len_bytes;
+ WBOOLEAN pio_from_adapter;
+ UINT i;
+ BYTE FAR * pio_address;
+ WORD saved_sifadr;
+ WORD mmio_alignment;
+ BYTE FAR * mmio_addr;
+ DWORD mmio_dword;
+
+#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);
+ }
+
+ if (adapter->EaglePsDMA == TRUE)
+ {
+ /*
+ * Now read the SIFACL register to check for a PseudoDMA interrupt.
+ */
+
+ 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;
+
+ /*
+ * Using any PCI card, a software handshake must occur so that the MAC
+ * does not try to initiate another transfer until a point has been reached on
+ * the host at which the transfer has completed. If not, the following could
+ * happen:
+ *
+ * - The host requests the last word/byte of a receive from the card.
+ * - The SIF does not has the data ready.
+ * - Control of the bus is given to a SCSI card.
+ * - It bursts/does nothing in bus master mode for 16 microseconds.
+ * - The data becomes ready early on during these 16 microseconds and
+ * as a result the card software beleives that the transfer has completed.
+ * - The card software continues and sets up another PsDMA transfer.
+ * - The SCSI card finishes, but the PdDMA length is now incorrect and
+ * all is lost.
+ *
+ */
+
+ saved_sifadr = sys_insw(adapter_handle, adapter->sif_adr);
+
+ /*
+ * Set the PIO_HANDSHAKE word to 0
+ */
+ 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);
+
+ /*
+ * Now output 0xFFFF to the PIO_HANDSHAKE word, to signal the DMA is complete.
+ */
+ sys_outsw(adapter_handle, adapter->sif_dat, 0xFFFF );
+
+ /*
+ * Restore the saved SIF address.
+ */
+ sys_outsw(adapter_handle, adapter->sif_adr, saved_sifadr);
+
+
+ }
+ }
+ else if ((sifint_value & INT_PCIMMIO) != 0 && sifint_occurred)
+ {
+ /*
+ * We have an MMIO interrupt so we can
+ * assume that we've not had an ordinary SIF interrupt.
+ */
+
+ sifint_occurred = FALSE;
+
+ /*
+ * PIO interrupt has occurred.Transfer data to/from adapter.
+ */
+
+ pioint_occurred = TRUE;
+
+ /*
+ * Preserve the contents of SIFADR.
+ */
+
+ saved_sifadr = sys_insw(adapter_handle,
+ adapter->sif_adr);
+
+ sys_outsw(
+ adapter_handle,
+ adapter->sif_adr,
+ DIO_LOCATION_DMA_POINTER);
+
+ pio_addr_lo = sys_insw(
+ adapter_handle,
+ adapter->sif_datinc);
+
+ pio_addr_hi = sys_insw(
+ adapter_handle,
+ adapter->sif_datinc);
+
+ pio_address = (BYTE FAR *) ((((DWORD) pio_addr_hi) << 16) |
+ pio_addr_lo);
+
+ pio_len_bytes = sys_insw(
+ adapter_handle,
+ adapter->sif_datinc);
+
+ mmio_alignment = sys_insw(
+ adapter_handle,
+ adapter->sif_datinc);
+
+ sys_outsw(
+ adapter_handle,
+ adapter->sif_adr,
+ DIO_LOCATION_DMA_CONTROL);
+
+ if (pio_len_bytes != 0)
+ {
+ mmio_addr = (BYTE FAR *) adapter->mmio_base_address;
+
+ if ((sifint_value & INT_PCIMMIO_RX) != 0)
+ {
+ /*
+ * Receive.
+ */
+
+ /*
+ * First off need to do a dummy read to initialise Hardware
+ * Read into a global variable to stop the optimiser from
+ * optimising out the statement.
+ */
+
+ sys_movsd_from(
+ adapter_handle,
+ (DWORD)mmio_addr,
+ (DWORD)(&mmio_dword));
+
+ /*
+ * Take care of the first DWORD, which may not all be valid
+ * data.
+ */
+
+ if (mmio_alignment != 0)
+ {
+ sys_movsd_from(
+ adapter_handle,
+ (DWORD)mmio_addr,
+ (DWORD)(&mmio_dword));
+
+ mmio_addr += 4;
+
+ for (i = mmio_alignment;
+ ((i < 4) && (pio_len_bytes > 0));
+ i++)
+ {
+ *pio_address = *(((BYTE FAR *) &mmio_dword) + i);
+ pio_address++;
+ pio_len_bytes--;
+ }
+ }
+
+ /*
+ * Transfer the bulk of the DWORDs.
+ */
+
+ if (pio_len_bytes >= 4)
+ {
+ sys_rep_movsd_from(
+ adapter_handle,
+ (DWORD)mmio_addr,
+ (DWORD)pio_address,
+ (WORD) (pio_len_bytes & 0xfffc));
+
+ pio_address += (pio_len_bytes & 0xfffc);
+ mmio_addr += (pio_len_bytes & 0xfffc);
+ pio_len_bytes &= 0x0003;
+ }
+
+ /*
+ * Deal with any trailing bytes in the last DWORD.
+ */
+
+ if (pio_len_bytes > 0)
+ {
+ sys_movsd_from(
+ adapter_handle,
+ (DWORD)mmio_addr,
+ (DWORD)(&mmio_dword));
+
+ for(i = 0; i < pio_len_bytes; i++)
+ {
+ *pio_address = *(((BYTE FAR *) &mmio_dword) + i);
+ pio_address++;
+ }
+ }
+
+ /*
+ * Write the handshake value.
+ */
+
+ sys_outsw(
+ adapter_handle,
+ adapter->sif_dat,
+ 0xffff);
+ }
+ else
+ {
+ /*
+ * Transmit.
+ */
+
+ switch (mmio_alignment)
+ {
+ /*
+ * If the alignment is 0 then there is a whole DWORD
+ * to copy so we don't do anything and let the following
+ * code handle it.
+ */
+
+ case 0:
+ break;
+
+ /*
+ * If the alignment is 1 then we can't do anything
+ * we cannot write 3 bytes in one go.
+ */
+
+ case 1:
+ adapter->error_record.type = ERROR_TYPE_HWI;
+ adapter->error_record.value = HWI_E_16_PCI_3BYTE_PROBLEM;
+
+ return;
+
+ /*
+ * If the alignment is 2 then we must transfer a word
+ * unless there is only one byte of data.
+ */
+
+ case 2:
+ if (pio_len_bytes == 1)
+ {
+ *(mmio_addr + 2) = *pio_address;
+ pio_address++;
+ pio_len_bytes--;
+ }
+ else
+ {
+ *((WORD FAR *) (mmio_addr + 2)) =
+ *((WORD FAR *) pio_address);
+ pio_address += 2;
+ pio_len_bytes -= 2;
+ mmio_addr += 4;
+ }
+
+ break;
+
+ /*
+ * If the alignment is 3 then we must transfer a byte.
+ */
+
+ case 3:
+ *(mmio_addr + 3) = *pio_address;
+ pio_address++;
+ pio_len_bytes--;
+ mmio_addr += 4;
+
+ break;
+ }
+
+ /*
+ * Transfer the bulk of the DWORDs.
+ */
+
+ if (pio_len_bytes >= 4)
+ {
+ sys_rep_movsd_to(
+ adapter_handle,
+ (DWORD)pio_address,
+ (DWORD)mmio_addr,
+ (WORD) (pio_len_bytes & 0xfffc));
+
+ pio_address += (pio_len_bytes & 0xfffc);
+ mmio_addr += (pio_len_bytes & 0xfffc);
+ pio_len_bytes &= 0x0003;
+ }
+
+ /*
+ * Transfer the remainder of the data.
+ */
+
+ switch (pio_len_bytes)
+ {
+ /*
+ * There may be nothing left to do.
+ */
+
+ case 0:
+ break;
+
+ /*
+ * If there is 1 byte left the just do a single byte
+ * copy.
+ */
+
+ case 1:
+ *mmio_addr = *pio_address;
+
+ break;
+
+ /*
+ * If there are two bytes left then do a word copy.
+ */
+
+ case 2:
+ *((WORD FAR *) mmio_addr) = *((WORD FAR *) pio_address);
+
+ break;
+
+ /*
+ * If there are 3 bytes left then we have a slight
+ * problem as we cannot do a single write of 3 bytes.
+ * Fortunately there should always be some space left
+ * at the end of a buffer on the adapter.
+ */
+
+ case 3:
+ sys_movsd_to(
+ adapter_handle,
+ (DWORD)pio_address,
+ (DWORD)mmio_addr);
+
+ break;
+ }
+
+ /*
+ * There now follows an extended handshake, to
+ * workaround the SAM upload hardware bug on the PCI I. The
+ * host must not make any DIO access until the upload has
+ * finished. In order to achieve this we:
+ *
+ * a) write 05555h to the DMA_CONTROL, to indicate that we've
+ * completed the MMIO write.
+ *
+ * b) poll DMA_CONTROL until it becomes 0AAAAh, indicating
+ * that the adapter is now about to do the upload.
+ *
+ * c) write 0FFFFh to DMA_CONTROL, to indicate that we've
+ * completed reading from DIO space.
+ *
+ * d) wait 'x' microseconds while the adapter does
+ * the upload.
+ *
+ * We make sure that we're not reading any cached
+ * value from the data_reg by writing to the sifaddr
+ * beforehand.
+ */
+
+ sys_outsw(
+ adapter_handle,
+ adapter->sif_dat,
+ 0x5555);
+
+ do
+ {
+ sys_outsw(
+ adapter_handle,
+ adapter->sif_adr,
+ DIO_LOCATION_DMA_CONTROL);
+ }
+ while (sys_insw(adapter_handle, adapter->sif_dat) != 0xaaaa);
+
+ sys_outsw(
+ adapter_handle,
+ adapter->sif_adr,
+ DIO_LOCATION_DMA_CONTROL);
+
+ sys_outsw(
+ adapter_handle,
+ adapter->sif_dat,
+ 0xffff);
+
+ sys_wait_at_least_microseconds(4);
+ }
+ }
+
+ /*
+ * Restore SIFADR.
+ */
+
+ sys_outsw(
+ adapter_handle,
+ adapter->sif_adr,
+ saved_sifadr);
+ }
+
+ /*
+ * Now that we have finished acknowledging the interrupt at the card,
+ * we acknowledge it at the interrupt controller.
+ */
+
+#ifndef FTK_NO_CLEAR_IRQ
+
+ if (sifint_occurred || pioint_occurred)
+ {
+ /*
+ * Acknowledge/clear interrupt at interrupt controller.
+ */
+
+ sys_clear_controller_interrupt(
+ adapter_handle,
+ adapter->interrupt_number);
+ }
+
+#endif
+
+ /*
+ * Only now do we do driver specific processing of SIF interrupts.
+ */
+
+ if (sifint_occurred)
+ {
+ /*
+ * Call driver with details of SIF interrupt.
+ */
+
+ driver_interrupt_entry(adapter_handle, adapter, sifint_value);
+ }
+
+ /*
+ * Let system know we have finished accessing the IO ports.
+ */
+
+#ifndef FTK_NO_IO_ENABLE
+ macro_disable_io(adapter);
+#endif
+}
+
+
+/****************************************************************************
+*
+* hwi_pci_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_pci_remove_card)
+#endif
+
+export void
+hwi_pci_remove_card(
+ ADAPTER * adapter
+ )
+{
+ ADAPTER_HANDLE adapter_handle = adapter->adapter_handle;
+ WORD wGenConAddr = adapter->io_location +
+ PCI_GENERAL_CONTROL_REG;
+ WORD wControl;
+ WORD sifacl;
+
+ /*
+ * 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
+ */
+
+ wControl = sys_insw(adapter_handle, wGenConAddr);
+ wControl &= ~PCI1_SRESET;
+ sys_outsw(adapter_handle, wGenConAddr, wControl);
+
+#ifndef FTK_NO_IO_ENABLE
+ macro_disable_io(adapter);
+#endif
+}
+
+
+/****************************************************************************
+*
+* hwi_pci_set_dio_address
+* =======================
+*
+* The hwi_pci_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_pci_set_dio_address)
+#endif
+
+export void
+hwi_pci_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
+|
+---------------------------------------------------------------------------*/
+
+
+/*---------------------------------------------------------------------------
+|
+| pci_c46_write_bit
+| ==================
+|
+| Write a bit to the SEEPROM control register.
+|
+---------------------------------------------------------------------------*/
+
+#ifdef FTK_INIT_FUNCTION
+#pragma FTK_INIT_FUNCTION(pci_c46_write_bit)
+#endif
+
+local void
+pci_c46_write_bit(
+ ADAPTER * adapter,
+ WORD mask,
+ WBOOLEAN set_bit
+ )
+{
+ WORD ctrl_reg;
+
+ /*
+ * Get the current value of the SEEPROM control register.
+ */
+
+ ctrl_reg = sys_insb(
+ adapter->adapter_handle,
+ (WORD) (adapter->io_location + PCI_SEEPROM_CONTROL_REG));
+
+ /*
+ * Some bits cannot be read back from the SEEPROM control register once
+ * they have been written, so we must keep track of them ourself.
+ */
+
+ ctrl_reg |= adapter->c46_bits;
+
+ /*
+ * Clear or set the bit.
+ */
+
+ if (set_bit)
+ {
+ ctrl_reg |= mask;
+ }
+ else
+ {
+ ctrl_reg &= ~mask;
+ }
+
+ /*
+ * Write the data to the SEEPROM control register.
+ */
+
+ sys_outsb(
+ adapter->adapter_handle,
+ (WORD) (adapter->io_location + PCI_SEEPROM_CONTROL_REG),
+ (BYTE) ctrl_reg);
+
+ /*
+ * Remember the bits that we cannot read back.
+ */
+
+ adapter->c46_bits = ctrl_reg & BITS_TO_REMEMBER;
+
+ /*
+ * Wait for a bit.
+ */
+
+ sys_wait_at_least_microseconds(10);
+
+ /*
+ * And read the SEEPROM control register back so that the data gets
+ * latched properly.
+ */
+
+ sys_insb(
+ adapter->adapter_handle,
+ (WORD) (adapter->io_location + PCI_SEEPROM_CONTROL_REG));
+}
+
+
+/*---------------------------------------------------------------------------
+|
+| pci_c46_twitch_clock
+| ====================
+|
+| Toggle the SEEPROM clock.
+|
+---------------------------------------------------------------------------*/
+
+#ifdef FTK_INIT_FUNCTION
+#pragma FTK_INIT_FUNCTION(pci_c46_twitch_clock)
+#endif
+
+local void
+pci_c46_twitch_clock(
+ ADAPTER * adapter
+ )
+{
+ pci_c46_write_bit(adapter, PCI1_BIA_CLK, TRUE);
+ pci_c46_write_bit(adapter, PCI1_BIA_CLK, FALSE);
+}
+
+
+/*---------------------------------------------------------------------------
+|
+| pci_c46_read_data
+| =================
+|
+| Read a data bit from the SEEPROM control register
+|
+---------------------------------------------------------------------------*/
+
+#ifdef FTK_INIT_FUNCTION
+#pragma FTK_INIT_FUNCTION(pci_c46_read_data)
+#endif
+
+local WORD
+pci_c46_read_data(
+ ADAPTER * adapter
+ )
+{
+ return sys_insb(
+ adapter->adapter_handle,
+ (WORD) (adapter->io_location + PCI_SEEPROM_CONTROL_REG)
+ ) & PCI1_BIA_DIN;
+}
+
+
+/*---------------------------------------------------------------------------
+|
+| hwi_pci_read_eeprom_word
+| ========================
+|
+| hwi_pci_read_eeprom_word takes the address of the word to be read
+| from the AT93C46 serial EEPROM, write to the IO ports a magic sequence
+| to switch the EEPROM to reading mode and finally read the required word
+| and return.
+|
+---------------------------------------------------------------------------*/
+
+#ifdef FTK_INIT_FUNCTION
+#pragma FTK_INIT_FUNCTION(hwi_pci_read_eeprom_word)
+#endif
+
+local WORD
+hwi_pci_read_eeprom_word(
+ ADAPTER * adapter,
+ WORD word_address
+ )
+{
+ WORD i;
+ WORD cmd_word = PCI_C46_START_BIT | PCI_C46_READ_CMD;
+ WORD tmp_word;
+
+ /*
+ * Concatenate the address to command word.
+ */
+
+ cmd_word |= (WORD)((word_address & PCI_C46_ADDR_MASK) <<
+ PCI_C46_ADDR_SHIFT);
+
+ /*
+ * Clear data in bit.
+ */
+
+ pci_c46_write_bit(
+ adapter,
+ PCI1_BIA_DOUT,
+ FALSE);
+
+ /*
+ * Assert chip select bit.
+ */
+
+ pci_c46_write_bit(
+ adapter,
+ PCI1_BIA_ENA,
+ TRUE);
+
+ /*
+ * Send read command and address.
+ */
+
+ pci_c46_twitch_clock(adapter);
+
+ tmp_word = cmd_word;
+
+ for (i = 0; i < PCI_C46_CMD_LENGTH; i++)
+ {
+ pci_c46_write_bit(
+ adapter,
+ PCI1_BIA_DOUT,
+ (WBOOLEAN) ((tmp_word & 0x8000) != 0));
+ pci_c46_twitch_clock(adapter);
+ tmp_word <<= 1;
+ }
+
+ /*
+ * Read data word.
+ */
+
+ tmp_word = 0x0000;
+
+ for (i = 0; i < 16; i++)
+ {
+ pci_c46_twitch_clock(adapter);
+
+ if (i > 0)
+ {
+ tmp_word <<= 1;
+ }
+
+ if (pci_c46_read_data(adapter) != 0)
+ {
+ tmp_word |= 0x0001;
+ }
+ }
+
+ /*
+ * Clear data in bit.
+ */
+
+ pci_c46_write_bit(adapter, PCI1_BIA_DOUT, FALSE);
+
+ /*
+ * Deselect chip.
+ */
+
+ pci_c46_write_bit(adapter, PCI1_BIA_ENA, FALSE);
+
+ /*
+ * Tick clock.
+ */
+
+ pci_c46_twitch_clock(adapter);
+
+ return tmp_word;
+}
+
+
+/*---------------------------------------------------------------------------
+|
+| hwi_pci_read_node_address
+| =========================
+|
+| The hwi_pci_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_pci_read_node_address)
+#endif
+
+local WBOOLEAN
+hwi_pci_read_node_address(
+ ADAPTER * adapter
+ )
+{
+ WORD temp;
+
+ temp = hwi_pci_read_eeprom_word(adapter, PCI_EEPROM_BIA_WORD0);
+ adapter->permanent_address.byte[0] = (BYTE) ((temp ) & 0x00ff);
+ adapter->permanent_address.byte[1] = (BYTE) ((temp >> 8) & 0x00ff);
+
+ temp = hwi_pci_read_eeprom_word(adapter, PCI_EEPROM_BIA_WORD1);
+ adapter->permanent_address.byte[2] = (BYTE) ((temp ) & 0x00ff);
+ adapter->permanent_address.byte[3] = (BYTE) ((temp >> 8) & 0x00ff);
+
+ temp = hwi_pci_read_eeprom_word(adapter, PCI_EEPROM_BIA_WORD2);
+ adapter->permanent_address.byte[4] = (BYTE) ((temp ) & 0x00ff);
+ adapter->permanent_address.byte[5] = (BYTE) ((temp >> 8) & 0x00ff);
+
+ return TRUE;
+}
+
+#endif
+
+/******* End of HWI_PCI.C **************************************************/