/*
 * i82595.S86  -  Routines for supporting network cards with i82595 chip
 *
 * Copyright (C) 2003-2007 Gero Kuhlmann   <gero@gkminix.han.de>
 *
 *  This program 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 of the License, or
 *  any later version.
 *
 *  This program 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 this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: i82595.S86,v 1.8 2007/01/08 22:02:20 gkminix Exp $
 *
 *====================================================================
 */
#include <common.i86>
#include "flash.i86"

.file __FILE__
.line __LINE__



/*
 *====================================================================
 *
 * Some definitions which are local to this module
 */
#define ROM_WINSIZE	16384		/* ROM address space window size */
#define ROM_WINBITS	14		/* number of bits in ROM window size */


/* Internal ID numbers for different network cards */

#define LAN595		0		/* Generic 82595-based network card */
#define LAN595TX	1		/* EtherExpress Pro/10 ISA */
#define LAN595FX	2		/* EtherExpress Pro/10+ ISA */
#define LAN595FX_10ISA	3		/* EtherExpress 10 ISA */


/* 82595 on-chip registers */

#define CMD_REG		0x0000		/* command register (same for all banks) */
#define CMD_BANK0_SEL	0x00		/* command to select register bank 0 */
#define CMD_BANK1_SEL	0x40		/* command to select register bank 1 */
#define CMD_BANK2_SEL	0x80		/* command to select register bank 2 */

#define ID_REG		0x0002		/* ID register */
#define ID_ID_MASK	0x2C		/* mask to isolate chip ID */
#define ID_CNT_MASK	0xC0		/* mask for ID register counter */
#define ID_SIGNATURE	0x24		/* ID for all 82595 chips */

#define ROM_WIN_REG	0x0002		/* register for ROM decode window */
#define ROM_WIN_MASK	0x70		/* mask to select ROM decode window */
#define ROM_PAGE_REG	0x000C		/* register for ROM page select */
#define ROM_PAGE_MASK	0xCF		/* mask for ROM page select */
#define ROM_WRITE_MASK	0x30		/* mask for ROM write enable */
#define ROM_WRITE_ON	0x10		/* value to enable ROM write */
#define ROM_WRITE_OFF	0x20		/* value to disable ROM write */


/* Definitions to access the serial EEPROM */

#define EEPROM_REG_PRO	0x000A		/* EEPROM access register for Pro series */
#define EEPROM_REG_10	0x000B		/* EEPROM access register for 10 series */
#define EEPROM_SK	0b00000001	/* SK bit mask (clock signal) */
#define EEPROM_CS	0b00000010	/* CS bit mask (chip select) */
#define EEPROM_DI	0b00000100	/* DI bit mask (data in) */
#define EEPROM_DO	0b00001000	/* DO bit mask (data out) */
#define EEPROM_MAX	18		/* number of words to read from EEPROM */
#define EEPROM_SIZE	64		/* number of words stored within EEPROM */
#define EEPROM_WAIT	40		/* number of microseconds to wait */
#define EEPROM_RDCMD	0x0180		/* EEPROM read command */

#define EEPROM_ADDR0	4		/* offset to first station address word */
#define EEPROM_ADDR1	3		/* offset to seconds station address word */
#define EEPROM_ADDR2	2		/* offset to third station address word */
#define EEPROM_PROADDR	0x00AA		/* first station address word on TX */

#define EEPROM_SETUP0	0		/* offset to first setup word */
#define EEPROM_SETUP1	1		/* offset to second setup word */
#define EEPROM_PROM	0x0200		/* mask for PROM installation flag */

#define EEPROM_IRQMAP	7		/* offset to IRQ mapping value */
#define EEPROM_FXIRQ	0x1EB8		/* only allowed IRQ mapping on FX */




.line __LINE__
/*
 *====================================================================
 *
 * Define some external routines
 */
	.text


	.extern	amd_readid		# read Flash EPROM ID
	.extern	amd_erase		# erase area of Flash EPROM
	.extern	amd_doprog		# program a byte into Flash EPROM

	.extern	intel_readid		# read Flash EPROM ID
	.extern	intel_erase		# erase area of Flash EPROM
	.extern	intel_doprog		# program a byte into Flash EPROM

	.extern	fndpnp			# find PnP identifier
	.extern	wait_usec		# wait for some microseconds
	.extern	gen_verify		# generic verification routine




/*
 *====================================================================
 *
 * Detect Flash EPROM interface
 * Input:  FS:SI -  Pointer to bootrom image
 * Output: EDI   -  Start offset into Flash EPROM for bootrom image
 *         ECX   -  Size of Flash EPROM available for bootrom image
 *         EAX   -  Linear address of Flash EPROM (zero if N/A) or error code
 *         DX    -  Offset to Flash EPROM info structure
 *         Carry set if interface not found
 * Changed registers: EAX, EBX, ECX, EDX, EDI
 */
ifdetect:

	push	esi

/*
 * First check if the bootrom image is really for us. For this reason,
 * we grab the PnP ID of the bootrom image and check it against a list
 * of possible IDs.
 */

	mov	word ptr [deterr],1
	mov	bx,offset nic_list
	call	fndpnp
	jc	8f
	mov	[chipid],bx
	mov	[pnpid],edx

/*
 * Now try to find a 82595 chip in the system. We scan a couple of I/O
 * port addresses until we find the signature emitted by the chip. Then
 * we read the ROM window decode register and compute the segment of the
 * Flash EPROM window. If its invalid, we have to continue scanning.
 */

	xor	bx,bx
	mov	si,offset ldports
	mov	word ptr [deterr],2
7:	push	ds
	mov	ax,cs
	mov	ds,ax
	call	ldscan			# use the loader scan routine
	pop	ds
	or	dx,dx
	jz	8f

	mov	al,CMD_BANK1_SEL
	out	dx,al			# change to register bank 1
	add	dx,ROM_WIN_REG
	in	al,dx			# read ROM window address
	mov	bl,al
	sub	dx,ROM_WIN_REG
	mov	al,CMD_BANK0_SEL
	out	dx,al			# switch back to register bank 0
	and	bx,ROM_WIN_MASK
	jz	7b			# check if ROM enabled
	shl	bx,6			# compute segment address
	add	bx,0xC000
	mov	[romseg],bx		# save ROM segment
	mov	[ioaddr],dx		# save the I/O base address

/*
 * This is some magic to determine if we have a EtherExpress 10 ISA
 * card, which has a different EEPROM register offset. We first set
 * the offset to the normal value, and then try to read the first
 * word of the station address out of the EEPROM. If it is invalid,
 * we do have a EtherExpress 10 ISA, and have to use a different
 * EEPROM access port.
 */

	add	dx,EEPROM_REG_PRO
	mov	[ioeeprom],dx
	mov	si,EEPROM_ADDR2
	call	read_eeprom		# read third word of station address
	or	ax,ax			# invalid if zero
	jz	1f
	cmp	ax,0xFFFF		# invalid if -1
	jne	2f
1:	mov	word ptr [deterr],3
	add	word ptr [ioeeprom],EEPROM_REG_10 - EEPROM_REG_PRO
	cmp	word ptr [chipid],LAN595FX_10ISA
	jne	8f			# is it what the bootrom says?

/*
 * Read the serial EEPROM to get configuration data, and determine the type
 * of NIC we have.
 */

2:	xor	si,si
	mov	di,offset eeprom
	mov	cx,EEPROM_MAX
3:	call	read_eeprom		# read value from serial EEPROM
	mov	[di],ax			# save value into temp storage
	add	di,2
	inc	si
	loop	3b

	mov	bx,[chipid]
	cmp	bx,LAN595FX_10ISA	# we have checked for this already
	je	6f
	cmp	word ptr [eeprom + EEPROM_IRQMAP * 2],EEPROM_FXIRQ
	jne	4f			# check if FX IRQ mapping
	mov	word ptr [deterr],4
	cmp	bx,LAN595FX		# we have a FX, check if the bootrom
	jne	8f			# thinks the same
	jmp	6f

4:	cmp	word ptr [eeprom + EEPROM_ADDR0 * 2],EEPROM_PROADDR
	jne	5f			# check if address starts with TX value
	mov	word ptr [deterr],5
	cmp	bx,LAN595TX		# we have a TX, check if the bootrom
	jne	8f			# thinks the same
	jmp	6f

5:	mov	word ptr [deterr],6
	cmp	bx,LAN595		# nothing found so far, so it has to
	jne	8f			# be a generic 595-based card

6:	shl	bx,1
	mov	ax,name_list[bx]	# set name of network card
	mov	istruct_id[istruct_i82595],ax

/*
 * We have set all internal variables so far, so we can try finding a supported
 * Flash EPROM chip and return all relevant parameters. It is possible to use
 * a single-voltage (AMD-type) Flash EPROM with a i82595. If you are brave
 * enough you can simply put such a Flash EPROM into the socket on the network
 * board and hope that no-one will ever activate the Vpp pin (which would fry
 * the chip with 12 volts). Otherwise you can turn pin 1 of the AMD-type Flash
 * EPROM chip upwards so that it doesnt sit in the socket anymore, and connect
 * it with a small wire to ground. Then it is pretty safe. Because of this
 * possibility, we also do an AMD check here. However, the standard would be
 * to use a 12 volt Intel-type Flash EPROM. Those can be plugged in without
 * any doubt.
 */

	call	ifinit			# setup Flash EPROM access
	mov	dword ptr [romsize],0
	mov	word ptr [flasherase],offset amd_erase
	mov	word ptr [flashprog],offset amd_doprog
	mov	word ptr [flashsetup],offset ifdummy
	mov	word ptr [flashcleanup],offset ifdummy
	mov	bx,offset istruct_i82595
	call	amd_readid		# read ID from Flash EPROM
	or	si,si
	jnz	1f

	mov	word ptr [flasherase],offset intel_erase
	mov	word ptr [flashprog],offset intel_doprog
	mov	word ptr [flashsetup],offset intel_setup
	mov	word ptr [flashcleanup],offset intel_cleanup
	mov	bx,offset istruct_i82595
	call	intel_readid		# read ID from Flash EPROM
1:	call	ifclean			# turn Flash EPROM access off
	mov	word ptr [deterr],7
	or	si,si
	jz	8f

	mov	word ptr [deterr],8
	mov	ecx,fstruct_romsize[si]
	cmp	ecx,ROM_WINSIZE		# check that the Flash EPROM size is
	jb	8f			# within the window size
	mov	[romsize],ecx		# save physical size of Flash EPROM
	movzx32	eax,word ptr [romseg]
	shl	eax,4
	xor	edi,edi			# return without error
	mov	dx,si
	jmp	9f

8:	stc				# return with error
	mov	ax,[deterr]
9:	pop	esi
	ret



/*
 *====================================================================
 *
 * Dummy routine
 * Input:  none
 * Output: none
 * Changed registers: none
 */
ifdummy:

	clc
	ret



/*
 *====================================================================
 *
 * Read one byte from Flash EPROM. This routine assumes that the NIC chip
 * has been set to register window 1 already. It will also switch the
 * ROM bank, but doesnt switch it back.
 * Input:  EDI  -  Offset into Flash EPROM
 * Output: AL   -  Byte value
 * Changed registers: AL
 */
ifread:

	push	ebx
	push	dx
	push	es
	xor	al,al
	mov	ebx,[romsize]		# if romsize is zero we are still in
	or	ebx,ebx			# ROM detection mode
	jz	1f
	cmp	edi,ebx			# check if valid offset
	jae	9f

1:	mov	bx,[romseg]		# set segment to access ROM page
	mov	es,bx
	mov	dx,[ioaddr]		# set I/O address to ROM page register
	add	dx,ROM_PAGE_REG
	mov	ebx,edi
	shr	ebx,ROM_WINBITS		# compute page number into BL
	mov	al,bl
	shl	al,2
	and	al,0xC0
	and	bl,0x0F			# adjust the page number to the
	or	bl,al			# register format
	in	al,dx
	and	al,ROM_WRITE_MASK	# dont change the write-enable bits
	or	al,bl
	out	dx,al			# select new page

	mov	ebx,edi
	and	bx,ROM_WINSIZE - 1	# compute offset into ROM window
	mov	al,es:[bx]		# read byte from ROM
9:	pop	es
	pop	dx
	pop	ebx
	ret



/*
 *====================================================================
 *
 * Write one byte into Flash EPROM. This routine assumes that the NIC chip
 * has been set to register window 1 already. It will also switch the
 * ROM bank, but doesnt switch it back.
 * Input:  EDI  -  Offset into Flash EPROM
 *         AL   -  Byte value
 * Output: none
 * Changed registers: none
 */
ifwrite:

	push	ebx
	push	dx
	push	es
	mov	ebx,[romsize]		# if romsize is zero we are still in
	or	ebx,ebx			# ROM detection mode
	jz	1f
	cmp	edi,ebx			# check if valid offset
	jae	9f

1:	push	ax
	mov	bx,[romseg]		# set segment to access ROM page
	mov	es,bx
	mov	dx,[ioaddr]		# set I/O address to ROM page register
	add	dx,ROM_PAGE_REG
	mov	ebx,edi
	shr	ebx,ROM_WINBITS		# compute page number into BL
	mov	al,bl
	shl	al,2
	and	al,0xC0
	and	bl,0x0F			# adjust the page number to the
	or	bl,al			# register format
	in	al,dx
	and	al,ROM_WRITE_MASK	# dont change the write-enable bits
	or	al,bl
	out	dx,al			# select new page
	pop	ax

	mov	ebx,edi
	and	bx,ROM_WINSIZE - 1	# compute offset into ROM window
	mov	es:[bx],al		# write byte into ROM
9:	pop	es
	pop	dx
	pop	ebx
	ret



/*
 *====================================================================
 *
 * Initialize NIC for programming. This sets the register bank to 1.
 * Input:  none
 * Output: none
 * Changed registers: AX
 */
ifinit:

	push	dx
	mov	dx,[ioaddr]		# get I/O address
	mov	al,CMD_BANK1_SEL
	out	dx,al			# change to register bank 1
	pop	dx
	clc
	ret



/*
 *====================================================================
 *
 * Terminate programming. This restores the register bank to 0 and switches
 * the ROM page back to 0. It assumes that bank 1 is still active when being
 * called.
 * Input:  none
 * Output: none
 * Changed registers: AX
 */
ifclean:

	push	dx
	mov	dx,[ioaddr]		# get I/O address
	add	dx,ROM_PAGE_REG
	in	al,dx
	and	al,ROM_WRITE_MASK	# dont change the write-enable bits
	out	dx,al			# select page 0
	sub	dx,ROM_PAGE_REG
	mov	al,CMD_BANK0_SEL
	out	dx,al			# switch back to register bank 0
	pop	dx
	clc
	ret



/*
 *====================================================================
 *
 * Turn programming voltage VPP on. This routine assumes that bank 1 is
 * still active.
 * Input:  none
 * Output: none
 * Changed registers: AL
 */
ifvppon:

	push	dx
	cmp	word ptr [flashprog],offset intel_doprog
	jne	9f
	mov	dx,[ioaddr]		# get I/O address
	add	dx,ROM_PAGE_REG
	in	al,dx
	and	al,ROM_PAGE_MASK	# dont change the page bits
	or	al,ROM_WRITE_ON
	out	dx,al			# turn Vpp on
9:	pop	dx
	clc
	ret



/*
 *====================================================================
 *
 * Turn programming voltage VPP off. This routine assumes that bank 1 is
 * still active.
 * Input:  none
 * Output: none
 * Changed registers: AL
 */
ifvppoff:

	push	dx
	cmp	word ptr [flashprog],offset intel_doprog
	jne	9f
	mov	dx,[ioaddr]		# get I/O address
	add	dx,ROM_PAGE_REG
	in	al,dx
	and	al,ROM_PAGE_MASK	# dont change the page bits
	or	al,ROM_WRITE_OFF
	out	dx,al			# turn Vpp off
9:	pop	dx
	clc
	ret



/*
 *====================================================================
 *
 * Erase the Flash EPROM area occupied by the rom image. We simply call
 * the routine for the Flash EPROM chip.
 * Input:  EDI -  Offset to rom image block within EPROM
 *         ECX -  Number of bytes to erase
 * Output: carry flag set if error
 * Registers changed: EAX
 */
iferase:

	push	bx
	mov	bx,offset istruct_i82595
	call	[flashsetup]		# setup Flash EPROM access
	jc	9f
	call	[flasherase]		# call chip erasure routine
	pushf				# save carry flag
	call	[flashcleanup]		# cleanup Flash EPROM access
	jnc	8f
	pop	ax			# remove carry flag from stack
	jmp	9f			# return with carry set

8:	popf				# restore carry flag
9:	pop	bx
	ret



/*
 *====================================================================
 *
 * Program a memory area into the Flash EPROM.
 * Input:  FS:SI -  Pointer to memory area with source data
 *         ECX   -  Number of bytes to program
 *         EDI   -  Offset to rom image block within EPROM
 * Output: carry flag set if error
 * Registers changed: EAX
 */
ifprog:

	push	bx
	push	ecx
	push	edi
	push	si
	push	fs
	mov	bx,offset istruct_i82595

	call	[flashsetup]		# setup Flash EPROM access
	jc	9f

1:	mov	al,fs:[si]
	call	[flashprog]		# program one byte into Flash EPROM
	jc	7f
	inc	si			# increment source pointer
	test	si,0x8000
	jz	2f			# avoid source pointer overflow
	mov	ax,fs
	add	ax,0x0800
	mov	fs,ax
	and	si,0x7FFF
2:	inc	edi			# increment offset into Flash EPROM
 data32	loop	1b			# continue with next byte
	clc

7:	pushf				# save carry flag
	call	[flashcleanup]		# cleanup Flash EPROM access
	jnc	8f
	pop	ax			# remove carry flag from stack
	jmp	9f			# return with carry

8:	popf				# restore carry flag
9:	pop	fs
	pop	si
	pop	edi
	pop	ecx
	pop	bx
	ret



/*
 *====================================================================
 *
 * Verify a memory area against the contents of the Flash EPROM.
 * Input:  FS:SI -  Pointer to memory area with source data
 *         ECX   -  Number of bytes to verify
 *         EDI   -  Offset to rom image block within EPROM
 * Output: carry flag set if error
 * Registers changed: EAX
 */
ifverify:

	push	bx
	mov	bx,offset istruct_i82595
	call	gen_verify		# call generic routine
	pop	bx
	ret



/*
 *====================================================================
 *
 * Read one word from NIC serial EEPROM. This is quite horrible, because
 * the CPU has to generate all the timings. This code has been partially
 * taken from the Linux source.
 * Input:  SI   -  Offset into serial EEPROM
 * Output: AX   -  Word read from serial EEPROM
 * Changed registers: AX
 */
read_eeprom:

	push	bx
	push	cx
	push	dx

	mov	dx,[ioaddr]
	mov	al,CMD_BANK1_SEL
	out	dx,al
	inc	dx			# this is black magic: write a 0
	xor	al,al			# into register 1 of bank 1, then
	out	dx,al			# switch to bank 2
	dec	dx
	mov	al,CMD_BANK2_SEL
	out	dx,al

	mov	dx,[ioeeprom]
	mov	al,EEPROM_CS
	out	dx,al			# set EEPROM chip select

	mov	bx,si
	and	bx,EEPROM_SIZE - 1
	or	bx,EEPROM_RDCMD		# setup EEPROM read command
	mov	cx,9			# number of bits to shift out
1:	jcxz	3f
	dec	cx			# prepare current bit number
	mov	ax,0x0001
	shl	ax,cl
	test	bx,ax			# check if bit set
	jz	2f
	mov	al,EEPROM_DI		# set output bit
2:	or	al,EEPROM_CS
	mov	ah,al
	out	dx,al			# drive output pin
	or	al,EEPROM_SK
	out	dx,al			# drive clock pin
	push	ax
	mov	ax,EEPROM_WAIT
	call	wait_usec		# wait 40 microseconds
	pop	ax
	mov	al,ah
	out	dx,al			# reset clock pin
	mov	ax,EEPROM_WAIT
	call	wait_usec		# wait another 40 microseconds
	jmp	1b

3:	mov	al,EEPROM_CS
	out	dx,al			# reset output pin
	xor	bx,bx
	mov	cx,16			# set number of bit to read
4:	mov	al,EEPROM_CS
	or	al,EEPROM_SK
	out	dx,al			# drive clock pin
	mov	ax,EEPROM_WAIT
	call	wait_usec		# wait 40 microseconds
	shl	bx,1
	in	al,dx			# read input pin
	test	al,EEPROM_DO
	jz	5f
	or	bx,0x0001		# set bit in input word
5:	mov	al,EEPROM_CS
	out	dx,al			# reset clock pin
	mov	ax,EEPROM_WAIT
	call	wait_usec		# wait another 40 microseconds
	loop	4b			# continue with all other bits

	mov	al,EEPROM_SK
	out	dx,al			# one clock pulse without chip select
	mov	ax,EEPROM_WAIT
	call	wait_usec		# wait 40 microseconds
	xor	al,al
	out	dx,al			# reset clock and chip select pins
	mov	ax,EEPROM_WAIT
	call	wait_usec		# wait another 40 microseconds

	mov	dx,[ioaddr]
	mov	al,CMD_BANK0_SEL	# switch back to bank 0
	out	dx,al
	mov	ax,bx			# prepare return value
	pop	dx
	pop	cx
	pop	bx
	ret




/*
 *====================================================================
 *
 * The following code is the bootrom loader for the 82595. Since this chip
 * only maps 16kB of the Flash EPROM into the processors address space at
 * one time, in order to access the whole bootrom it is necessary to switch
 * the ROM pages during bootrom startup. For this, the initial bootrom loader
 * and the following code get copied into the first Flash EPROM page, and the
 * compressed remainder of the bootrom gets burned into the remaining pages.
 * Then when the bootrom starts up, it has to decompress the main parts
 * (kernel and UNDI). To do this it will call a reading routine, which is
 * coded below, for every compressed byte to be read. This reading routine
 * will then switch the Flash EPROM page on the 82595 chip as necessary.
 * The following code is therefore not only used by the Flash EPROM programmer
 * code above, but also by the bootrom at a different starting offset. Thats
 * the reason why the following code as to be strictly position independent.
 * It has to be able to run at any arbitrary offset. Jumps and calls are
 * relative on the iA32 platform, so those are not a problem (as long as they
 * happen within +/- 32kB). But data accesses have to use absolute addresses.
 * To make data values position independent we are using the BX register, which
 * always contains the difference between the offset the assembler is generating
 * and the offset the code expects when running within the bootrom context.
 *
 * These routines assume that they get called by a near call, and the DS is
 * set to CS.
 *
 *====================================================================
 *
 * Initialize the bootrom loader. This routine gets only called by the
 * bootrom, and it has to find an 82595-based network card and setup all
 * required data values. It returns the absolute offset to the Flash EPROM
 * reading routine. This routine has to be at the very beginning of the
 * whole loader code.
 * Input:  BX    -  PCI PFA of network card
 *         DX    -  PnP CSN of network card
 *         CS:SI -  Pointer to DWORD which contains the size of the bootrom
 * Output: AX    -  Offset to Flash EPROM reading routine (0 means error)
 *         DI    -  Offset to first byte following loader code
 * Changed registers: EAX, BX, CX, DX, SI, DI
 */
ldinit:	jmp	7f

	.word	0			# size of flash image loader

7:	push	bx
	call	1f
1:	pop	bx
	sub	bx,offset 1b		# compute offset difference
	pop	cx			# get PCI PFA from stack - not needed
	push	es

/*
 * First check to see that we are running within RAM. For this we read
 * the compressed bootrom image size and write it into its assigned
 * variable. If it is not the same, we are running within a ROM, and
 * when switching the Flash EPROM page, the results are unpredictable.
 */

	mov	eax,cs:[si]		# read size of compressed image
	mov	ldrdsize[bx],eax
	cmp	ldrdsize[bx],eax	# check if we could store the variable
	jne	8f
	mov	word ptr ldrdaddr[bx],WINID_LEN
	mov	word ptr ldrdpage[bx],1

/*
 * Find a 82595 chip and determine the segment of the Flash EPROM access
 * window. Finally try to read the signature at the beginning of the compressed
 * code to check if we can really switch the ROM page.
 */

	lea	si,ldports[bx]
2:	call	ldscan			# scan I/O space for 82595 chip
	or	dx,dx
	jz	8f			# couldnt find one
	mov	ldioaddr[bx],dx		# save base I/O address

	mov	al,CMD_BANK1_SEL
	out	dx,al			# change to register bank 1
	add	dx,ROM_WIN_REG
	in	al,dx			# read ROM window address
	and	ax,ROM_WIN_MASK
	jnz	3f			# check if ROM enabled
	sub	dx,ROM_WIN_REG
	mov	al,CMD_BANK0_SEL	# in case of error switch register
	out	dx,al			# bank back to 0
	jmp	2b

3:	shl	ax,6			# compute segment address
	add	ax,0xC000
	mov	ldromseg[bx],ax
	mov	es,ax

	add	dx,ROM_PAGE_REG - ROM_WIN_REG
	in	al,dx
	mov	ah,al			# save old page number for later
	and	al,ROM_WRITE_MASK	# select page 1
	or	al,0x01
	out	dx,al
	lea	si,ldsig[bx]
	xor	di,di
	mov	cx,WINID_LEN
	cld
	repe	cmpsb			# compare both strings
	pushf				# save zero flag
	mov	al,ah
	out	dx,al			# reset to previous page
	sub	dx,ROM_PAGE_REG
	mov	al,CMD_BANK0_SEL
	out	dx,al			# reset to register bank 0
	popf				# restore zero flag
	jne	8f
	lea	ax,ldread[bx]		# return without error
	lea	di,ldend[bx + 0x000F]
	and	di,0xFFF0		# adjust to paragraph boundary
	jmp	9f

8:	xor	ax,ax			# return with error
9:	pop	es
	ret



/*
 *====================================================================
 *
 * Read one byte from the Flash EPROM by switching the Flash EPROM page
 * within the 82595 controller. Since we have to switch the page, this
 * code is not allowed to be run directly from within the Flash EPROM!
 * Input:  none
 * Output: AL   -  Byte read from Flash EPROM
 *         Zero flag set if at end of compressed bootrom contents
 * Changed registers: AX
 */
ldread:	push	bx
	call	1f
1:	pop	bx
	sub	bx,offset 1b		# compute offset difference

/*
 * First check if we are at the end of the file
 */

	mov	ax,word ptr ldrdsize[bx + 0]
	or	ax,word ptr ldrdsize[bx + 2]
	jz	9f

/*
 * Now select the proper Flash EPROM page, read the requested byte
 * and switch back to page 0. We have to do this switching everytime
 * because we never know if the decompression routine terminates
 * with an error. In that case, if we would have set the register
 * bank and flash page permanently, they would never get reset.
 */

	push	dx
	push	es
	push	si
	mov	dx,ldioaddr[bx]		# get I/O address
	mov	al,CMD_BANK1_SEL	# select proper register bank
	out	dx,al
	mov	ax,ldromseg[bx]		# set segment to access ROM page
	mov	es,ax

	add	dx,ROM_PAGE_REG
	mov	si,ldrdpage[bx]		# get current page number
	mov	ax,si
	shl	ax,2
	and	ax,0x00C0		# adjust the page number to the
	and	si,0x000F		# register format
	or	si,ax
	in	al,dx
	mov	ah,al			# remember old page in AH
	and	al,ROM_WRITE_MASK	# dont change the write-enable bits
	or	ax,si
	out	dx,al			# select new page

	mov	si,ldrdaddr[bx]		# get load address
	mov	al,es:[si]		# read the requested byte
	xchg	al,ah
	out	dx,al			# restore old Flash EPROM page
	sub	dx,ROM_PAGE_REG
	mov	al,CMD_BANK0_SEL
	out	dx,al			# switch back to register page 0
	xchg	al,ah			# move result into AL

	dec	dword ptr ldrdsize[bx]	# decrement number of bytes
	inc	word ptr ldrdaddr[bx]	# increment read address
	cmp	word ptr ldrdaddr[bx],ROM_WINSIZE
	jb	2f
	mov	word ptr ldrdaddr[bx],0
	inc	word ptr ldrdpage[bx]	# increment read ptr to next page
2:	or	dx,dx			# clear zero flag
	pop	si
	pop	es
	pop	dx
9:	pop	bx
	ret



/*
 *====================================================================
 *
 * Scan various I/O port addresses and try to find a 82595 chip.
 * Input:  BX   -  Offset difference to access variables
 *         SI   -  Pointer to list of port addresses to scan
 * Output: DX   -  Base port address, zero if no chip found
 *         SI   -  Pointer to next port address to scan
 * Changed registers: AX, DX, SI
 */
ldscan:
1:	mov	dx,[si]			# get next port address
	or	dx,dx
	jz	9f
	add	si,2
	add	dx,ID_REG		# read ID register
	in	al,dx
	mov	ah,al
	and	al,ID_ID_MASK
	cmp	al,ID_SIGNATURE		# is it our required signature?
	jne	1b
	and	ah,ID_CNT_MASK
	in	al,dx			# read in the counter
	and	al,ID_CNT_MASK
	add	ah,0x40
	cmp	al,ah			# check if counter increased
	jne	1b
	sub	dx,ID_REG		# return base port address
9:	ret



/*
 *====================================================================
 *
 * Variables used by the loader code. It is not nice to have variables within
 * the code segment, but there is no way to avoid this with the Flash EPROM
 * loader.
 */


/*
 * Variable to hold the I/O address of the 82595 chip. This variable will only
 * be used by the Flash EPROM loader. The I/O address might be different at
 * loader runtime and at the time when we run the Flash EPROM programmer (for
 * example the user might have reconfigured the network card in the meantime).
 */
ldioaddr:	.word	0


/*
 * Segment of Flash EPROM access window
 */
ldromseg:	.word	0


/*
 * Current page  in the Flash EPROM. The compressed bootrom code always
 * starts at page 1.
 */
ldrdpage:	.word	0


/*
 * Current read address offset starting at the beginning of page 1 in the
 * Flash EPROM
 */
ldrdaddr:	.word	0


/*
 * Size of image to read from Flash EPROM
 */
ldrdsize:	.long	0


/*
 * Signature at the beginning of the compressed ROM code
 */
ldsig:		.ascii	WINID_MAGIC


/*
 * I/O ports to be checked during 82595 scan
 */
ldports:	.word	0x0300
		.word	0x0210
		.word	0x0240
		.word	0x0280
		.word	0x02C0
		.word	0x0200
		.word	0x0320
		.word	0x0340
		.word	0x0360
		.word	0


/*
 * The loader code ends here. All the code from label ldinit up to here
 * gets copied into the bootrom lateron.
 */
ldend:




/*
 *====================================================================
 *
 * String and constants definitions
 */
	_rodata


/*
 * NIC list
 */
nic_list:
	.ascii	"PNP828A"
	.ascii	"INT1030"
	.ascii	"INT1031"
	.ascii	"INT1100"
	.byte	0


/*
 * NIC names
 */
name_lan595:
	.asciz	"Intel 82595-based network card"

name_lan595tx:
	.asciz	"Intel EtherExpress Pro/10 ISA"

name_lan595fx:
	.asciz	"Intel EtherExpress Pro/10+ ISA/PnP"

name_lan595isa:
	.asciz	"Intel EtherExpress 10 ISA"


name_list:
	.word	name_lan595
	.word	name_lan595tx
	.word	name_lan595fx
	.word	name_lan595isa




/*
 *====================================================================
 *
 * Data definitions
 */
	.data

	.global	istruct_i82595


/*
 * Interface definition
 */
istruct_i82595:
	.word	name_lan595		# pointer to interface name
	.word	IDEVTYPE_ISA		# interface device type
	.word	nic_list		# pointer to PnP ID list
	.word	ifdetect		# pointer to detection routine
	.word	ifread			# pointer to byte read routine
	.word	ifwrite			# pointer to byte write routine
	.word	ifvppon			# pointer to VPP-on routine
	.word	ifvppoff		# pointer to VPP-off routine
	.word	ifinit			# pointer to initialization routine
	.word	ifclean			# pointer to cleanup routine
	.word	iferase			# pointer to erase routine
	.word	ifprog			# pointer to programming routine
	.word	ifverify		# pointer to verify routine
	.word	ifload			# pointer to loader structure
	.word	IFLAGS_NEEDMEM		# interface needs direct memory access


/*
 * Loader definition
 */
ifload:	.word	ldinit			# pointer to start of loader code
	.word	ldend			# pointer to end of loader code
	.long	ROM_WINSIZE		# ROM address space window size




/*
 *====================================================================
 *
 * Variable definitions
 */
	.bss

	.lcomm	deterr,2		# detection routine error code
	.lcomm	chipid,2		# type of chip installed
	.lcomm	pnpid,4			# PnP vendor and device ID
	.lcomm	ioaddr,2		# I/O address for register access
	.lcomm	romseg,2		# segment of Flash ROM window
	.lcomm	romsize,4		# size of physical Flash ROM
	.lcomm	flasherase,2		# pointer to erase routine
	.lcomm	flashprog,2		# pointer to programming routine
	.lcomm	flashsetup,2		# pointer to Flash setup routine
	.lcomm	flashcleanup,2		# pointer to Flash cleanup routine
	.lcomm	ioeeprom,2		# EEPROM access I/O port address
	.lcomm	eeprom,EEPROM_MAX * 2	# area to save EEPROM values




/*
 *====================================================================
 */
	.end

