/*
 * vt610x.S86  -  Routines for supporting cards with Via Rhine chipset
 *
 * 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: vt610x.S86,v 1.5 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	0x1106		/* Via PCI vendor ID */


/*
 * VT610x on-chip registers
 */
#define REG_CR		0x0008		/* Command register (16 bit) */
#define REG_IMR		0x000E		/* Interrupt mask register (16 bit) */
#define REG_CFG_A	0x0078		/* Config-A register */
#define REG_CFG_B	0x0079		/* Config-B register */
#define REG_CFG_C	0x007A		/* Config-C register */
#define REG_CFG_D	0x007B		/* Config-D register */
#define REG_BPMA	0x008C		/* ROM address (16 bit) register */
#define REG_BPMD	0x008F		/* ROM write data register */
#define REG_BPCMD	0x0090		/* ROM programming command register */
#define REG_BPIN	0x0091		/* ROM read data register */

#define CR_OFF_MASK	0x1F80		/* CR mask to stop NIC */
#define CR_OFF_BITS	0x0004		/* CR bits to stop NIC */
#define CR_ON_MASK	0x1F9E		/* CR mask to restore NIC */

#define CFG_C_BPS	0x07		/* ROM size select */
#define CFG_C_BTSEL	0x08		/* ROM timing select */
#define CFG_C_BROPT	0x40		/* ROM address unused bits high */

#define BPCMD_EBPRD	0x01		/* ROM embedded read command */
#define BPCMD_EBPWR	0x02		/* ROM embedded write command */
#define BPCMD_EBPC	0x80		/* ROM embedded programming complete */
#define BPCMD_WAIT	5000		/* command wait count */


/*
 * Values for VT610x PCI configuration registers
 */
#define PCI_IOADDRMASK	0xFFFFFFF8	/* Mask to select I/O base address */
#define PCI_IOENABMASK	0x00000001	/* Mask to select I/O register access */

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




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


/*
 * The Via-Rhine chipsets only support 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	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

/*
 * 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_vt610x],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

/*
 * 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],4
	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],5
	or	ecx,ecx			# resource has to be assigned by BIOS
	jz	8f
	mov	word ptr [deterr],6
	test	ecx,PCI_IOENABMASK	# check if registers are I/O based
	jz	8f
	mov	word ptr [deterr],7
	and	ecx,PCI_IOADDRMASK
	test	ecx,0xFFFF0000		# I/O address has to be within 64kB
	jnz	8f
	mov	[ioaddr],cx		# save I/O address

/*
 * Read the size of the Flash EPROM from configuration register C
 */

	mov	word ptr [deterr],8
	mov	dx,cx
	add	dx,REG_CFG_C
	in	al,dx			# read configuration register C
	mov	si,ax
	and	si,CFG_C_BPS
	shl	si,2
	mov	ecx,size_table[si]	# determine boot rom size from table
	or	ecx,ecx			# check if valid Flash EPROM size
	jz	8f
	mov	[winsize],ecx		# save Flash EPROM window size

/*
 * We have set all internal variables so far, so we can try finding a supported
 * Flash EPROM chip. Note that we need to stop the NIC because reading the Flash
 * EPROM ID requires to write into the chip, and that's only allowed if the NIC
 * is stopped.
 */

	mov	word ptr [deterr],9
	call	nicstop			# stop NIC
	mov	bx,offset istruct_vt610x
	call	amd_readid		# read ID from Flash EPROM
	or	si,si
	jz	7f
	mov	word ptr [deterr],10
	mov	ecx,fstruct_romsize[si]
	cmp	ecx,[winsize]		# check that the Flash EPROM size is
	jb	8f			# within the window size
	mov	[romsize],ecx		# save physical size of Flash EPROM
	call	nicstart		# restart NIC
	mov	ecx,[winsize]		# return without error
	xor	edi,edi
	xor	eax,eax
	mov	dx,si
	jmp	9f

/*
 * Return to caller. Remember to restart the NIC if necessary.
 */

7:	call	nicstart		# restart NIC
8:	stc				# return with error
	mov	ax,[deterr]
9:	pop	esi
	ret



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

	push	cx
	push	dx
	xor	al,al
	cmp	edi,[winsize]		# check if valid offset
	jae	9f
	mov	dx,[ioaddr]
	add	dx,REG_BPMA
	xchg	eax,edi
	out	dx,ax			# write offset into NIC
	xchg	eax,edi

	add	dx,REG_BPCMD - REG_BPMA
	mov	al,BPCMD_EBPRD		# send read command
	out	dx,al
	mov	cx,BPCMD_WAIT
1:	in	al,0x61			# this will delay for approximately
	in	al,0x61			# 5 microseconds
	in	al,0x61
	in	al,dx
	test	al,BPCMD_EBPC		# check if transfer completed
	jnz	2f
	loop	1b
	xor	al,al			# no data value available
	jmp	9f

2:	add	dx,REG_BPIN - REG_BPCMD
	in	al,dx			# read data value
9:	pop	dx
	pop	cx
	ret



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

	push	cx
	push	dx
	cmp	edi,[winsize]		# check if valid offset
	jae	9f
	mov	dx,[ioaddr]
	add	dx,REG_BPMA
	xchg	eax,edi
	out	dx,ax			# write offset into NIC
	xchg	eax,edi
	add	dx,REG_BPMD - REG_BPMA
	out	dx,al			# write byte value into NIC

	push	ax
	add	dx,REG_BPCMD - REG_BPMD
	mov	al,BPCMD_EBPWR		# send write command
	out	dx,al
	mov	cx,BPCMD_WAIT
1:	in	al,0x61			# this will delay for approximately
	in	al,0x61			# 5 microseconds
	in	al,0x61
	in	al,dx
	test	al,BPCMD_EBPC		# check if transfer completed
	jnz	2f
	loop	1b
2:	pop	ax
9:	pop	dx
	pop	cx
	ret



/*
 *====================================================================
 *
 * Initialize NIC for programming. This stops the NIC.
 * Input:  none
 * Output: none
 * Changed registers: AX
 */
ifinit:

	call	nicstop
	clc
	ret



/*
 *====================================================================
 *
 * Terminate programming. This restarts the NIC. Note that this
 * routine does not enable the Flash EPROM. If its not already enabled,
 * this task should be done by a NIC configuration program. It requires
 * manipulating the serial configuration EEPROM.
 * Input:  none
 * Output: none
 * Changed registers: AX
 */
ifclean:

	call	nicstart
	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_vt610x
	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_vt610x

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



/*
 *====================================================================
 *
 * Stop the NIC.
 * Input:  none
 * Output: none
 * Registers changed: AX
 */
nicstop:

	push	dx
	mov	dx,[ioaddr]
	add	dx,REG_IMR
	in	ax,dx			# save old IMR contents
	mov	[imreg],ax
	xor	ax,ax
	out	dx,ax			# disable all interrupts

	mov	dx,[ioaddr]
	add	dx,REG_CR
	in	ax,dx			# save old command register contents
	mov	[cmdreg],ax
	and	ax,CR_OFF_MASK
	or	ax,CR_OFF_BITS		# stop NIC
	out	dx,ax
	pop	dx
	ret



/*
 *====================================================================
 *
 * Restart the NIC
 * Input:  none
 * Output: none
 * Registers changed: AX
 */
nicstart:

	push	dx
	mov	dx,[ioaddr]
	add	dx,REG_CR
	mov	ax,[cmdreg]		# restore old command register
	and	ax,CR_ON_MASK
	out	dx,ax

	mov	dx,[ioaddr]
	add	dx,REG_IMR
	mov	ax,[imreg]		# restore old interrupt mask register
	out	dx,ax
	pop	dx
	ret




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


/*
 * List of NICs supported by this module
 */
pci_list:
	.word	PCI_VENDOR

nic_list:
	.word	0x3065
	.word	0x3106
	.word	0x3053
	.word	0


/*
 * NIC names
 */
name_vt610x:
	.asciz	"Via VT610x (Rhine) chipset"

name_vt6102:
	.asciz	"Via VT6102 (Rhine-II)"

name_vt6105:
	.asciz	"Via VT6105 (Rhine-III)"

name_vt6105m:
	.asciz	"Via VT6105M (Rhine-III)"


name_list:
	.word	name_vt6102
	.word	name_vt6105
	.word	name_vt6105m


/*
 * List of ROM sizes supported by the NIC
 */
size_table:
	.long	0			# no ROM installed
	.long	8192			# 8kB ROM size
	.long	16384			# 16kB ROM size
	.long	32768			# 32kB ROM size
	.long	65536			# 64kB ROM size
	.long	65536
	.long	65536
	.long	65536




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

	.global	istruct_vt610x


/*
 * Interface definition
 */
istruct_vt610x:
	.word	name_vt610x		# 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	0			# no further flags necessary




/*
 *====================================================================
 *
 * 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	romsize,4		# size of physical Flash ROM
	.lcomm	winsize,4		# size of Flash ROM window
	.lcomm	cmdreg,2		# command register contents
	.lcomm	imreg,2			# interrupt mask register contents




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

