/*
 * rtl8139.S86  -  Routines for supporting RTL8139 cards
 *
 * Copyright (C) 2002-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: rtl8139.S86,v 1.9 2007/01/08 22:02:20 gkminix Exp $
 *
 *====================================================================
 */
#include <common.i86>
#include <system/bios.i86>
#include <system/pci.i86>
#include "flash.i86"

.file __FILE__
.line __LINE__



/*
 *====================================================================
 *
 * Some definitions which are local to this module
 */
#define PCI_VENDOR	0x10EC		/* Realtek PCI vendor ID */


/*
 * RTL8139 PCI configuration registers
 */
#define PCI_IOADDRMASK	0xFFFFFFF0	/* Mask to select I/O base address */
#define PCI_IOENABMASK	0x00000001	/* Mask to select I/O register access */

#define PCI_ROMADDRMASK	0xFFFFF800	/* Mask to select ROM base address */
#define PCI_ROMENABMASK	0x00000001	/* Mask to select ROM enable bit */

#define PCI_DEFHEADER	0x00		/* Default PCI header type */


/*
 * RTL8139 internal registers
 */
#define CONFIG0		0x0051		/* Configuration register 0 */
#define ROMSIZE_MASK	0x07		/* Mask for Flash ROM size */




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


/*
 * The RTL8139 NIC only supports Flash EPROMs which get programmed
 * using the AMD programming algorithm.
 */

	.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	fndpci			# find PCI device
	.extern	chkram			# check if RAM at memory area
	.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
	push	es

/*
 * Try to find the NIC in the system
 */

	mov	word ptr [deterr],1
	mov	bx,offset nic_list
	mov	dx,PCI_VENDOR
	call	fndpci
	jc	8f
	shl	ax,1
	mov	si,ax
	mov	ax,name_list[si]	# save pointer to NIC name
	mov	istruct_id[istruct_rtl8139],ax
	mov	[pcipfn],bx		# save card identification number

/*
 * The chip has to report PCI header type 0, otherwise all PCI register
 * offsets are wrong.
 */

	mov	word ptr [deterr],2
	mov	ax,PCI_FUNC_READ_BYTE
	mov	di,PCI_REG_HEADER_TYPE
	int	INT_PCI			# call BIOS to get PCI header type
	jc	8f
	mov	word ptr [deterr],3
	cmp	cl,PCI_DEFHEADER	# check if default header type
	jne	8f

/*
 * Check that I/O and memory accesses are allowed
 */

	mov	word ptr [deterr],4
	mov	ax,PCI_FUNC_READ_WORD
	mov	di,PCI_REG_COMMAND
	int	INT_PCI			# call BIOS to get command register
	jc	8f
	mov	word ptr [deterr],5
	and	cx,PCI_CMD_IO + PCI_CMD_MEMORY
	cmp	cx,PCI_CMD_IO + PCI_CMD_MEMORY
	jne	8f

/*
 * Now read the I/O base address from the PCI configuration space. Note that
 * we can only access the chip using I/O, not using memory mapping, so we
 * check for this.
 */

	mov	word ptr [deterr],6
	mov	ax,PCI_FUNC_READ_DWORD
	mov	bx,[pcipfn]
	mov	di,PCI_REG_ADDR_0
	int	INT_PCI			# call BIOS to get I/O base address
	jc	8f
	mov	word ptr [deterr],7
	or	ecx,ecx			# ressource has to be assigned by BIOS
	jz	8f
	mov	word ptr [deterr],8
	test	ecx,PCI_IOENABMASK	# check if registers are I/O based
	jz	8f
	mov	word ptr [deterr],9
	and	ecx,PCI_IOADDRMASK
	test	ecx,0xFFFF0000		# I/O address has to be within 64kB
	jnz	8f
	mov	[ioaddr],cx		# save I/O address

/*
 * Determine the addressing size of the installed Flash ROM
 */

	mov	word ptr [deterr],10
	mov	dx,cx
	add	dx,CONFIG0
	in	al,dx			# read config register 0
	and	ax,ROMSIZE_MASK
	shl	ax,2
	mov	si,ax
	mov	eax,size_table[si]	# determine boot rom size from table
	or	eax,eax			# check if valid Flash EPROM size
	jz	8f
	mov	[winsize],eax		# save window size

/*
 * Determine the address of the installed Flash ROM. Also, we have to check
 * that there really is no RAM (shadow or other) at the specified address.
 */

	mov	word ptr [deterr],11
	push	si
	mov	ax,PCI_FUNC_READ_DWORD
	mov	bx,[pcipfn]
	mov	di,PCI_REG_ROM_ADDR
	int	INT_PCI			# call BIOS to get ROM base address
	pop	si
	jc	8f
	mov	word ptr [deterr],12
	test	ecx,PCI_ROMENABMASK
	jz	8f			# check if Flash EPROM enabled
	mov	word ptr [deterr],13
	and	ecx,mask_table[si]
	jz	8f			# check if valid address
	mov	word ptr [deterr],14
	cmp	ecx,0x000F0000		# check that the address is within the
	jae	8f			# first 1MB for real mode access
	mov	[romaddr],ecx		# save linear ROM address

	mov	ebx,ecx
	mov	ecx,[winsize]
	shr	ebx,4
	shr	ecx,4
	call	chkram			# check if RAM within ROM area
	jnc	1f
	mov	word ptr [deterr],15
	movzx32	eax,ax
	shl	eax,4			# if we found some RAM, compute the size
	sub	eax,[romaddr]		# of the non-RAM area, check if it is at
	cmp	eax,AMD_MINSIZE		# least sufficient to detect the Flash
	jb	8f			# EPROM, and then use that size as the
	mov	[winsize],eax		# window size

/*
 * We have set all internal variables so far, so we can try finding a supported
 * Flash EPROM chip.
 */

1:	mov	word ptr [deterr],16
	mov	bx,offset istruct_rtl8139
	call	amd_readid		# read ID from Flash EPROM
	or	si,si
	jz	8f
	mov	word ptr [deterr],17
	mov	eax,fstruct_romsize[si]
	cmp	eax,[winsize]		# check if Flash EPROM size is
	jb	8f			# sufficient for our ROM window
	mov	[romsize],eax
	xor	edi,edi			# return without error
	mov	eax,[romaddr]
	mov	ecx,[winsize]
	mov	dx,si
	jmp	9f

/*
 * Return to caller.
 */

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



/*
 *====================================================================
 *
 * Read one byte from Flash EPROM.
 * Input:  EDI  -  Offset into Flash EPROM
 * Output: AL   -  Byte value
 * Changed registers: AL
 */
ifread:

	push	es
	push	edi
	xor	al,al
	cmp	edi,[winsize]		# check if offset is within limits
	jae	9f
	push	eax
	add	edi,[romaddr]
	mov	eax,edi
	shr	eax,4			# compute pointer to byte
	mov	es,ax
	and	di,0x000F
	pop	eax
	mov	al,es:[di]		# get byte from Flash EPROM
9:	pop	edi
	pop	es
	ret



/*
 *====================================================================
 *
 * Write one byte into Flash EPROM.
 * Input:  EDI  -  Offset into Flash EPROM
 *         AL   -  Byte value
 * Output: none
 * Changed registers: none
 */
ifwrite:

	push	es
	push	edi
	cmp	edi,[winsize]		# check if offset is within limits
	jae	9f
	push	eax
	add	edi,[romaddr]
	mov	eax,edi
	shr	eax,4			# compute pointer to byte
	mov	es,ax
	and	di,0x000F
	pop	eax
	mov	es:[di],al		# put byte into Flash EPROM
9:	pop	edi
	pop	es
	ret



/*
 *====================================================================
 *
 * Initialize NIC for programming.
 * Input:  none
 * Output: none
 * Changed registers: none
 */
ifinit:
					# fall through



/*
 *====================================================================
 *
 * Terminate programming.
 * Input:  none
 * Output: none
 * Changed registers: none
 */
ifclean:

	clc
	ret



/*
 *====================================================================
 *
 * Turn programming voltage VPP on
 * Input:  none
 * Output: none
 * Changed registers: none
 */
ifvppon:
					# fall through



/*
 *====================================================================
 *
 * Turn programming voltage VPP off
 * Input:  none
 * Output: none
 * Changed registers: none
 */
ifvppoff:

	stc
	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_rtl8139
	call	amd_erase		# call chip erasure routine
	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_rtl8139

1:	mov	al,fs:[si]
	call	amd_doprog		# program one byte into Flash EPROM
	jc	9f
	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

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_rtl8139
	call	gen_verify		# call generic routine
	pop	bx
	ret




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


/*
 * List of NICs supported by this module
 */

pci_list:
	.word	PCI_VENDOR

nic_list:
	.word	0x8139
	.word	0x8138
	.word	0


/*
 * NIC names
 */
name_realtek:
	.asciz	"Realtek RTL8139x"

name_8139:
	.asciz	"Realtek RTL8139(C)"


name_8138:
	.asciz	"Realtek RTL8139B"


name_list:
	.word	name_8139
	.word	name_8138



/*
 * Table to translate the value of config register 0 into the Flash ROM size
 */

size_table:

	.long	0			# no Flash EPROM installed
	.long	8192			# 8kB Flash EPROM
	.long	16384			# 16kB Flash EPROM
	.long	32768			# 32kB Flash EPROM
	.long	65536			# 64kB Flash EPROM
	.long	131072			# 128kB Flash EPROM
	.long	0			# invalid
	.long	0			# invalid


/*
 * Table to translate the value of config register 0 into a mask for the
 * Flash ROM base address. Note that these masks have to make the base
 * address paragraph aligned.
 */

mask_table:

	.long	0			# no flash EPROM installed
	.long	0xFFFFE000		# 8kB Flash EPROM
	.long	0xFFFFC000		# 16kB Flash EPROM
	.long	0xFFFF8000		# 32kB Flash EPROM
	.long	0xFFFF0000		# 64kB Flash EPROM
	.long	0xFFFE0000		# 128kB Flash EPROM
	.long	0			# invalid
	.long	0			# invalid




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

	.global	istruct_rtl8139


/*
 * Interface definition
 */

istruct_rtl8139:
	.word	name_realtek		# pointer to interface name
	.word	IDEVTYPE_PCI		# interface device type
	.word	pci_list		# pointer to PCI 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	0			# no loader necessary
	.word	IFLAGS_NEEDMEM		# interface needs direct memory access




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

	.lcomm	deterr,2		# detection routine error code
	.lcomm	pcipfn,2		# PCI identification number for card
	.lcomm	ioaddr,2		# I/O address for register access
	.lcomm	romaddr,4		# base address of Flash ROM
	.lcomm	romsize,4		# size of physical Flash ROM
	.lcomm	winsize,4		# size of Flash ROM window




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

