/*
 * (C) Copyright 2008 Andre McCurdy, NXP Semiconductors
 *
 * 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 (at your option) 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., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */

#define zero	$0
#define AT	$1

#define v0 	$2
#define v1	$3

#define a0	$4
#define a1	$5
#define a2	$6
#define	a3	$7

#define t0	$8
#define t1	$9
#define t2	$10
#define t3	$11
#define t4	$12
#define t5	$13
#define t6	$14
#define t7	$15

#define s0	$16
#define s1	$17
#define s2	$18
#define s3	$19
#define s4	$20
#define s5	$21
#define s6	$22
#define s7	$23
#define s8	$30		/* == fp */

#define t8	$24
#define t9	$25

#define k0	$26
#define k1	$27

#define gp	$28
#define sp	$29
#define fp	$30		/* == s8 */
#define ra	$31


#define MIU_CS0_BASE_CACHED	0x98000000
#define MIU_CS0_BASE_UNCACHED	0xB8000000

#define DCACHE_HIT_INVALIDATE	0x11


/******************************************************************************/
/******************************************************************************/

	.text

	.align	2

	.globl	nand_data_read
	.ent    nand_data_read
	.type   nand_data_read, @function

nand_data_read: 			# a0 : base pointer, a1 : dst address, a2 : bytecount

	.set	noreorder
	.set	noat

	addu	a2, a1, a2		# a2 = dst_end
1:	andi	v0, a1, 0x03		# v0 = (dst & 0x03)
	beq	v0, zero, 2f		# branch if dst is aligned
	subu	a3, a2, a1		# a3 = bytes_remaining
	beq	a1, a2, 6f		# branch if (dst == dst_end)
	addi	t5, v0, 0x5241		# bdslot
	lbu	t0, 0(a0)		# t0 = *((unsigned char *) base)
	addiu	a1, a1, 1		# dst += 1
	b	1b			# loop
	sb	t0, -1(a1)		# dst[-1] = t0
2:	srl	v0, a3, 4		# v0 = 16byte_blocks_remaining
	beq	v0, zero, 4f		# branch if (16byte_blocks_remaining == 0)
	sll	v0, v0, 4		# v0 = number of bytes which will be handled as 16byte blocks
	add	v1, a1, v0		# v1 = dst_end for 16byte block copies
#if 1
	li	a3, MIU_CS0_BASE_UNCACHED - MIU_CS0_BASE_CACHED
	xor	a3, a0, a3		# a3 = base pointer, cached address mapping
3:	cache	DCACHE_HIT_INVALIDATE, 0(a3)
	lw	t0, 0(a3)
	addiu	a1, a1, 16		# dst += 16
	lw	t1, 4(a3)
	sw	t0, -16(a1)
	lw	t2, 8(a3)
	sw	t1, -12(a1)
	lw	t3, 12(a3)
	sw	t2, -8(a1)
	bne	a1, v1, 3b		# branch if (dst != (dst_end for 16byte block copies))
	sw	t3, -4(a1)
	cache	DCACHE_HIT_INVALIDATE, 0(a3)
#else
3:	lw	t0, 0(a0)
	addiu	a1, a1, 16		# dst += 16
	lw	t1, 0(a0)
	sw	t0, -16(a1)
	lw	t2, 0(a0)
	sw	t1, -12(a1)
	lw	t3, 0(a0)
	sw	t2, -8(a1)
	bne	a1, v1, 3b		# branch if (dst != (dst_end for 16byte block copies))
	sw	t3, -4(a1)
#endif
4:	beq	a1, a2, 6f		# branch if (dst == dst_end)
5:	addiu	a1, a1, 1
	lbu	t0, 0(a0)
	bne	a1, a2, 5b		# branch if (dst != dst_end)
	sb	t0, -1(a1)
6:	jr	ra
	nop

	.set	at
	.set	reorder

	.end    nand_data_read


/******************************************************************************/
/******************************************************************************/

	.globl	nand_data_write
	.ent    nand_data_write
	.type   nand_data_write, @function

nand_data_write: 			# a0 : base pointer, a1 : src address, a2 : bytecount

	.set	noreorder
	.set	noat

	addu	a2, a1, a2		# a2 = src_end
1:	andi	v0, a1, 0x03		# v0 = (src & 0x03)
	beq	v0, zero, 2f		# branch if src is aligned
	subu	a3, a2, a1		# a3 = bytes_remaining
	beq	a1, a2, 6f		# branch if (src == src_end)
	addi	t5, v0, 0x5241		# bdslot
	lbu	t0, 0(a1)		# t0 = *src
	addiu	a1, a1, 1		# src += 1
	b	1b			# loop
	sb	t0, 0(a0)		# *((unsigned char *) base) = t0
2:	srl	v0, a3, 4		# v0 = 16byte_blocks_remaining
	beq	v0, zero, 4f		# branch if (16byte_blocks_remaining == 0)
	sll	v0, v0, 4		# v0 = number of bytes which will be handled as 16byte blocks
	add	v1, a1, v0		# v1 = src_end for 16byte block copies

3:	lw	t0, 0(a1)
	addiu	a1, a1, 16		# src += 16
	lw	t1, -12(a1)
#if defined (__MIPSEL__)
	sb	t0, 0(a0)
	srl	t0, t0, 8
	sb	t0, 0(a0)
	srl	t0, t0, 8
	sb	t0, 0(a0)
	srl	t0, t0, 8
	sb	t0, 0(a0)
	lw	t2, -8(a1)
	sb	t1, 0(a0)
	srl	t1, t1, 8
	sb	t1, 0(a0)
	srl	t1, t1, 8
	sb	t1, 0(a0)
	srl	t1, t1, 8
	sb	t1, 0(a0)
	lw	t3, -4(a1)
	sb	t2, 0(a0)
	srl	t2, t2, 8
	sb	t2, 0(a0)
	srl	t2, t2, 8
	sb	t2, 0(a0)
	srl	t2, t2, 8
	sb	t2, 0(a0)
	sb	t3, 0(a0)
	srl	t3, t3, 8
	sb	t3, 0(a0)
	srl	t3, t3, 8
	sb	t3, 0(a0)
	srl	t3, t3, 8
#elif defined (__MIPSEB__)
#error "Big-endian option has not been tested..."
	srl	t4, t0, 24
	sb	t4, 0(a0)
	srl	t4, t0, 16
	sb	t4, 0(a0)
	srl	t4, t0, 8
	sb	t4, 0(a0)
	sb	t0, 0(a0)
	lw	t2, -8(a1)
	srl	t4, t1, 24
	sb	t4, 0(a0)
	srl	t4, t1, 16
	sb	t4, 0(a0)
	srl	t4, t1, 8
	sb	t4, 0(a0)
	sb	t1, 0(a0)
	lw	t3, -4(a1)
	srl	t4, t2, 24
	sb	t4, 0(a0)
	srl	t4, t2, 16
	sb	t4, 0(a0)
	srl	t4, t2, 8
	sb	t4, 0(a0)
	sb	t2, 0(a0)
	srl	t4, t3, 24
	sb	t4, 0(a0)
	srl	t4, t3, 16
	sb	t4, 0(a0)
	srl	t4, t3, 8
	sb	t4, 0(a0)
#else
#error "error detecting target byte order"
#endif
	bne	a1, v1, 3b		# branch if (src != (src_end for 16byte block copies))
	sb	t3, 0(a0)
4:	beq	a1, a2, 6f		# branch if (src == src_end)
5:	addiu	a1, a1, 1
	lbu	t0, -1(a1)
	bne	a1, a2, 5b		# branch if (src != src_end)
	sb	t0, 0(a0)
6:	jr	ra
	nop

	.set	at
	.set	reorder

	.end    nand_data_write


/******************************************************************************/
/******************************************************************************/

	.globl	nand_ecc_generate
	.ent    nand_ecc_generate
	.type   nand_ecc_generate, @function

	.align	5			# plut data should be cacheline (ie 16 byte) aligned

nand_ecc_generate: 			# a0 : data

	.set	noreorder
	.set	noat

	move	t9, ra
	li	t0, 0x100		# t0 = i
	bal	1f
	li	t1, 0x200		# t1 = i_end

	.byte	0x00, 0x2b, 0x2d, 0x06, 0x33, 0x18, 0x1e, 0x35, 0x35, 0x1e, 0x18, 0x33, 0x06, 0x2d, 0x2b, 0x00
	.byte	0x4b, 0x60, 0x66, 0x4d, 0x78, 0x53, 0x55, 0x7e, 0x7e, 0x55, 0x53, 0x78, 0x4d, 0x66, 0x60, 0x4b
	.byte	0x4d, 0x66, 0x60, 0x4b, 0x7e, 0x55, 0x53, 0x78, 0x78, 0x53, 0x55, 0x7e, 0x4b, 0x60, 0x66, 0x4d
	.byte	0x06, 0x2d, 0x2b, 0x00, 0x35, 0x1e, 0x18, 0x33, 0x33, 0x18, 0x1e, 0x35, 0x00, 0x2b, 0x2d, 0x06
	.byte	0x53, 0x78, 0x7e, 0x55, 0x60, 0x4b, 0x4d, 0x66, 0x66, 0x4d, 0x4b, 0x60, 0x55, 0x7e, 0x78, 0x53
	.byte	0x18, 0x33, 0x35, 0x1e, 0x2b, 0x00, 0x06, 0x2d, 0x2d, 0x06, 0x00, 0x2b, 0x1e, 0x35, 0x33, 0x18
	.byte	0x1e, 0x35, 0x33, 0x18, 0x2d, 0x06, 0x00, 0x2b, 0x2b, 0x00, 0x06, 0x2d, 0x18, 0x33, 0x35, 0x1e
	.byte	0x55, 0x7e, 0x78, 0x53, 0x66, 0x4d, 0x4b, 0x60, 0x60, 0x4b, 0x4d, 0x66, 0x53, 0x78, 0x7e, 0x55
	.byte	0x55, 0x7e, 0x78, 0x53, 0x66, 0x4d, 0x4b, 0x60, 0x60, 0x4b, 0x4d, 0x66, 0x53, 0x78, 0x7e, 0x55
	.byte	0x1e, 0x35, 0x33, 0x18, 0x2d, 0x06, 0x00, 0x2b, 0x2b, 0x00, 0x06, 0x2d, 0x18, 0x33, 0x35, 0x1e
	.byte	0x18, 0x33, 0x35, 0x1e, 0x2b, 0x00, 0x06, 0x2d, 0x2d, 0x06, 0x00, 0x2b, 0x1e, 0x35, 0x33, 0x18
	.byte	0x53, 0x78, 0x7e, 0x55, 0x60, 0x4b, 0x4d, 0x66, 0x66, 0x4d, 0x4b, 0x60, 0x55, 0x7e, 0x78, 0x53
	.byte	0x06, 0x2d, 0x2b, 0x00, 0x35, 0x1e, 0x18, 0x33, 0x33, 0x18, 0x1e, 0x35, 0x00, 0x2b, 0x2d, 0x06
	.byte	0x4d, 0x66, 0x60, 0x4b, 0x7e, 0x55, 0x53, 0x78, 0x78, 0x53, 0x55, 0x7e, 0x4b, 0x60, 0x66, 0x4d
	.byte	0x4b, 0x60, 0x66, 0x4d, 0x78, 0x53, 0x55, 0x7e, 0x7e, 0x55, 0x53, 0x78, 0x4d, 0x66, 0x60, 0x4b
	.byte	0x00, 0x2b, 0x2d, 0x06, 0x33, 0x18, 0x1e, 0x35, 0x35, 0x1e, 0x18, 0x33, 0x06, 0x2d, 0x2b, 0x00

1:	move	t2, zero		# t2 = cp
	move	t3, zero		# t3 = lpo
2:	lbu	t4, 0(a0)		# t4 = data[0]
	xor	t5, t3, t0		# t5 = (lpo ^ i)
	addu	t4, t4, ra		# t4 = &plut[d]
	lbu	t6, 0(t4)		# t6 = plut[d]
	addiu	t0, t0, 1		# i++
	andi	t7, t6, 0x01		# t7 = (plut[d] & 0x01)
	movn	t3, t5, t7		# if (plut[d] & 0x01) then lpo = t5
	xor	t2, t2, t6		# cp ^= plut[d]
	lbu	t4, 1(a0)		# t4 = data[1]
	xor	t5, t3, t0		# t5 = (lpo ^ i)
	addu	t4, t4, ra		# t4 = &plut[d]
	lbu	t6, 0(t4)		# t6 = plut[d]
	addiu	t0, t0, 1		# i++
	andi	t7, t6, 0x01		# t7 = (plut[d] & 0x01)
	movn	t3, t5, t7		# if (plut[d] & 0x01) then lpo = t5
	xor	t2, t2, t6		# cp ^= plut[d]
	lbu	t4, 2(a0)		# t4 = data[2]
	xor	t5, t3, t0		# t5 = (lpo ^ i)
	addu	t4, t4, ra		# t4 = &plut[d]
	lbu	t6, 0(t4)		# t6 = plut[d]
	addiu	t0, t0, 1		# i++
	andi	t7, t6, 0x01		# t7 = (plut[d] & 0x01)
	movn	t3, t5, t7		# if (plut[d] & 0x01) then lpo = t5
	xor	t2, t2, t6		# cp ^= plut[d]
	lbu	t4, 3(a0)		# t4 = data[3]
	xor	t5, t3, t0		# t5 = (lpo ^ i)
	addiu	a0, a0, 4		# data += 4
	addu	t4, t4, ra		# t4 = &plut[d]
	lbu	t6, 0(t4)		# t6 = plut[d]
	addiu	t0, t0, 1		# i++
	andi	t7, t6, 0x01		# t7 = (plut[d] & 0x01)
	movn	t3, t5, t7		# if (plut[d] & 0x01) then lpo = t5
	bne	t0, t1, 2b
	xor	t2, t2, t6		# cp ^= plut[d]
	srl	t2, t2, 1
	sll	t2, t2, 2
	srl	t4, t3, 8
	nor	t5, zero, t3
	move	t6, t3
	movn	t6, t5, t4		# t6 = lpe = (lpo >> 8) ? ~lpo : lpo
	andi	t4, t3, (1 << 0)
	andi	t5, t6, (1 << 0)
	sll	t4, t4, 9
	sll	t5, t5, 8
	or	t2, t2, t4		# t2 |= ((lpo & (1 << 0)) << 9)
	or	t2, t2, t5		# t2 |= ((lpe & (1 << 0)) << 8)
	andi	t4, t3, (1 << 1)
	andi	t5, t6, (1 << 1)
	sll	t4, t4, 10
	sll	t5, t5, 9
	or	t2, t2, t4		# t2 |= ((lpo & (1 << 1)) << 10)
	or	t2, t2, t5		# t2 |= ((lpe & (1 << 1)) << 9)
	andi	t4, t3, (1 << 2)
	andi	t5, t6, (1 << 2)
	sll	t4, t4, 11
	sll	t5, t5, 10
	or	t2, t2, t4		# t2 |= ((lpo & (1 << 2)) << 11)
	or	t2, t2, t5		# t2 |= ((lpe & (1 << 2)) << 10)
	andi	t4, t3, (1 << 3)
	andi	t5, t6, (1 << 3)
	sll	t4, t4, 12
	sll	t5, t5, 11
	or	t2, t2, t4		# t2 |= ((lpo & (1 << 3)) << 12)
	or	t2, t2, t5		# t2 |= ((lpe & (1 << 3)) << 11)
	andi	t4, t3, (1 << 4)
	andi	t5, t6, (1 << 4)
	sll	t4, t4, 13
	sll	t5, t5, 12
	or	t2, t2, t4		# t2 |= ((lpo & (1 << 4)) << 13)
	or	t2, t2, t5		# t2 |= ((lpe & (1 << 4)) << 12)
	andi	t4, t3, (1 << 5)
	andi	t5, t6, (1 << 5)
	sll	t4, t4, 14
	sll	t5, t5, 13
	or	t2, t2, t4		# t2 |= ((lpo & (1 << 5)) << 14)
	or	t2, t2, t5		# t2 |= ((lpe & (1 << 5)) << 13)
	andi	t4, t3, (1 << 6)
	andi	t5, t6, (1 << 6)
	sll	t4, t4, 15
	sll	t5, t5, 14
	or	t2, t2, t4		# t2 |= ((lpo & (1 << 6)) << 15)
	or	t2, t2, t5		# t2 |= ((lpe & (1 << 6)) << 14)
	andi	t4, t3, (1 << 7)
	andi	t5, t6, (1 << 7)
	sll	t4, t4, 16
	sll	t5, t5, 15
	or	t2, t2, t4		# t2 |= ((lpo & (1 << 7)) << 16)
	or	t2, t2, t5		# t2 |= ((lpe & (1 << 7)) << 15)
	lui	v0, 0x00FF
	ori	v0, 0xFFFF
	jr	t9
	xor	v0, t2, v0

	.set	at
	.set	reorder

	.end	nand_ecc_generate


/******************************************************************************/
/******************************************************************************/

