/*
 * intelchips.S86  -  Low level routines for Intel like flash EPROM chip access
 *
 * 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: intelchips.S86,v 1.8 2007/01/06 18:31:18 gkminix Exp $
 *
 *====================================================================
 */
#include <common.i86>
#include "flash.i86"

.file __FILE__
.line __LINE__



/*
 *====================================================================
 *
 * Internal additions to the flash EPROM chip definition structure
 * We only support single-block flash EPROMs, no boot-sector or
 * multi-sector chips.
 */
			.struct	fstruct_int
fstruct_id:		.struct	.+2		/* chip ID */

#define FSTRUCT_ISIZE	(FSTRUCT_SIZE + 0x0002)	/* total size of structure */




/*
 *====================================================================
 *
 * Definitions used for programming Intel Flash EPROM chips
 */
#define READID_CMD	0x90		/* command to read chip ID */
#define ERASE1_CMD	0x20		/* command to setup erasure */
#define ERASE2_CMD	0xA0		/* command to verify erasure */
#define PROG1_CMD	0x40		/* command to setup programming */
#define PROG2_CMD	0xC0		/* command to verify programming */
#define RESET_CMD	0xFF		/* command to reset chip */
 
#define VPPTIME		1000		/* wait 1 millisecond to let Vpp settle */
#define ERASETIME	10000		/* wait 10 milliseconds during erase */
#define PROGTIME	10		/* wait 10 microseconds during program */
#define VERIFYTIME	6		/* wait 6 microseconds during verify */
 
#define PROGRETRY	25		/* retry programming 25 times */
#define ERASERETRY	1000		/* retry erasing 1000 times */




.line __LINE__
/*
 *====================================================================
 *
 * Public routines defined in this module
 */
	.text

	.global	intel_readid
	.global	intel_erase
	.global	intel_doprog
	.global	intel_setup
	.global	intel_cleanup

	.extern	wait_usec



/*
 *====================================================================
 *
 * Read manufacturer ID from Flash-EPROM
 * Input:  BX  -  Pointer to interface definition structure
 * Output: SI  -  Pointer to Flash EPROM info structure, zero if no
 *                Flash EPROM found
 * Changed registers: AX, SI
 */
intel_readid:

	push	edx
	push	edi

/*
 * Read the manufacturer and device ID from the chip, if there is a
 * chip. It is the responsibility of the caller to ensure that this
 * routine does not write into RAM.
 */

	call	intel_setup		# turn Vpp on
	jc	8f
	xor	edi,edi			# set address for manufacturer ID
	mov	al,READID_CMD
	call	istruct_write[bx]	# send read-ID command
	call	istruct_read[bx]	# read manufacturer ID
	mov	dl,al
	inc	edi			# set address for device ID
	mov	al,READID_CMD
	call	istruct_write[bx]	# send read-ID command again
	call	istruct_read[bx]	# read device ID
	mov	dh,al
	call	intel_cleanup		# finally turn Vpp off

/*
 * Now find the chip in the list of supported Flash EPROMS.
 */

	mov	si,offset cstruct_list
2:	mov	ax,fstruct_intid[si]
	or	ax,ax			# check if end of list
	jz	8f
	cmp	dx,fstruct_id[si]	# find chip ID in chip list
	je	5f
	add	si,FSTRUCT_ISIZE
	jmp	2b

5:	mov	[curchip],si		# save pointer to current chip
	jmp	9f			# definition structure

8:	xor	si,si
9:	pop	edi
	pop	edx
	ret



/*
 *====================================================================
 *
 * Setup Flash EPROM access. This just sets the programming voltage.
 * Input:  BX  -  Pointer to interface definition structure
 * Output: Carry flag set if error
 * Changed registers: AX
 */
intel_setup:

	call	istruct_vppon[bx]	# turn Vpp on
	jc	9f
	mov	ax,VPPTIME		# wait until Vpp settles
	call	wait_usec
	mov	byte ptr [iniflg],0x01
	clc
9:	ret



/*
 *====================================================================
 *
 * Cleanup Flash EPROM access. This just turns the programming voltage off.
 * Input:  BX  -  Pointer to interface definition structure
 * Output: Carry flag set if error
 * Changed registers: AX
 */
intel_cleanup:

	push	edi
	xor	edi,edi
	xor	al,al
	call	istruct_write[bx]	# write read command
	pop	edi
	mov	ax,VERIFYTIME
	call	wait_usec		# wait until returned to data mode
	call	istruct_vppoff[bx]	# turn Vpp off
	jc	9f
	mov	ax,VPPTIME		# wait until Vpp settles
	call	wait_usec
	mov	byte ptr [iniflg],0x00
	clc
9:	ret



/*
 *====================================================================
 *
 * Erase the memory area occupied by the rom image. This is a really
 * awkward matter, because we first have to program all bytes to 00
 * before we can erase the chip. And after erasing, we have to check
 * that really all bytes are FF. This whole process can take minutes!
 * Input:  BX  -  Pointer to interface definition structure
 *         EDI -  Offset to rom image block within EPROM
 *         ECX -  Number of bytes to erase
 * Output: carry flag set if error
 * Registers changed: EAX
 */
intel_erase:

	push	edx
	push	ecx
	push	edi
	push	si
	mov	si,[curchip]
	mov	eax,edi
	add	eax,ecx
	cmp	eax,fstruct_romsize[si]
	ja	8f			# check if EPROM size exceeded
	test	byte ptr [iniflg],0xFF	# check if programming enabled
	jz	8f

	mov	[imgstart],edi		# save values for later reference
	mov	[imgend],eax
	mov	[imglen],ecx

/*
 * First we have to make sure that all bytes in the Flash EPROM do have
 * a value of zero.
 */

	xor	edi,edi
1:	call	istruct_read[bx]
	or	al,al			# check if byte is zero
	jz	2f
	xor	al,al
	call	progbyte		# program byte to zero
	xor	al,al
	call	istruct_write[bx]	# return to data mode
2:	inc	edi
	cmp	edi,fstruct_romsize[si]
	jb	1b			# check if all bytes processed

/*
 * Now we can erase the chip.
 */

	xor	edi,edi
	xor	cx,cx
3:	mov	al,ERASE1_CMD
	call	istruct_write[bx]	# write erase command
	mov	al,ERASE1_CMD
	call	istruct_write[bx]	# write erase command again
	mov	ax,ERASETIME
	call	wait_usec		# wait for erasure
4:	mov	al,ERASE2_CMD
	call	istruct_write[bx]	# write erase-verify command
	mov	ax,VERIFYTIME
	call	wait_usec		# wait for verify
	call	istruct_read[bx]	# read erased byte
	cmp	al,0xFF			# check if really erased
	je	5f
	loop	3b			# retry erasure
	mov	al,RESET_CMD
	call	istruct_write[bx]	# in case of error abort erasure
	mov	ax,VERIFYTIME
	call	wait_usec		# wait until reset completed
	jmp	8f

5:	inc	edi			# continue with next byte
	cmp	edi,fstruct_romsize[si]
	jb	4b
	clc				# erase completed
	jmp	9f

8:	stc
9:	pop	si
	pop	edi
	pop	ecx
	pop	edx
	ret



/*
 *====================================================================
 *
 * Program one byte into Flash EPROM chip
 * Input:  BX  -  Pointer to interface definition structure
 *         AL  -  Byte value to program
 *         EDI -  Offset into Flash EPROM
 * Output: carry flag set if error
 * Registers changed: EAX
 */
intel_doprog:

	cmp	edi,[imgstart]
	jb	8f			# check if address is within limits
	cmp	edi,[imgend]
	jae	8f
	call	progbyte		# program byte into Flash EPROM
	jmp	9f

8:	stc
9:	ret



/*
 *====================================================================
 *
 * Program one byte into Flash EPROM chip. This is an internal routine,
 * which avoids checking the image boundaries.
 * Input:  BX  -  Pointer to interface definition structure
 *         AL  -  Byte value to program
 *         EDI -  Offset into Flash EPROM
 * Output: carry flag set if error
 * Registers changed: EAX
 */
progbyte:

	push	cx
	push	dx
	push	edi
	test	byte ptr [iniflg],0xFF	# check if programming enabled
	jz	8f

	mov	cx,PROGRETRY
1:	push	ax
	mov	al,PROG1_CMD
	call	istruct_write[bx]	# send setup-program command
	pop	ax
	call	istruct_write[bx]	# write data
	push	ax
	mov	ax,PROGTIME
	call	wait_usec		# wait 10 microseconds
	mov	al,PROG2_CMD
	call	istruct_write[bx]	# write program-verify command
	mov	ax,VERIFYTIME
	call	wait_usec		# wait 6 microseconds
	call	istruct_read[bx]	# read data
	mov	dl,al
	pop	ax
	cmp	al,dl			# check if data is what we programmed
	je	9f
	loop	1b			# retry programming

8:	stc
9:	pop	edi
	pop	dx
	pop	cx
	ret




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


/*
 * Names of Flash EPROM chips which can be used by this program
 */
name50:	.asciz	"I28F512"
name51:	.asciz	"I28F010"
name52:	.asciz	"I28F020"
name60:	.asciz	"AM28F512"
name61:	.asciz	"AM28F010"
name70:	.asciz	"CAT28F512"
name71:	.asciz	"CAT28F010"
name72:	.asciz	"CAT28F020"


/*
 * List of Flash EPROM definition structures
 */
cstruct_list:


/* Intel chips */

	.word	CHIP_I28F512		# internal chip ID number
	.word	name50			# offset to chip name
	.long	65536			# total size in bytes
	.byte	0x89, 0xB8		# chip ID code

	.word	CHIP_I28F010
	.word	name51
	.long	131072
	.byte	0x89, 0xB4

	.word	CHIP_I28F020
	.word	name52
	.long	262144
	.byte	0x89, 0xBD


/* AMD chips */

	.word	CHIP_AM28F512
	.word	name60
	.long	65536
	.byte	0x01, 0x25

	.word	CHIP_AM28F010
	.word	name61
	.long	131072
	.byte	0x01, 0xA7


/* Catalyst chips */

	.word	CHIP_CAT28F512
	.word	name70
	.long	65536
	.byte	0x31, 0xB8

	.word	CHIP_CAT28F010
	.word	name71
	.long	131072
	.byte	0x31, 0xB4

	.word	CHIP_CAT28F020
	.word	name72
	.long	262144
	.byte	0x31, 0xBD

	.word	0			# end of list




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

	.lcomm	curchip,2		# pointer to current chip structure
	.lcomm	imgstart,4		# start offset of rom image
	.lcomm	imgend,4		# end offset of rom image
	.lcomm	imglen,4		# length of rom image
	.lcomm	iniflg,1		# flag if initialized




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

