[RESUBMIT] [PATCH] [MTD] NAND nand_ecc.c: rewrite for improved performance

Troy Kisky troy.kisky at boundarydevices.com
Thu Aug 14 15:10:40 EDT 2008


frans wrote:
> Fixed the last remaining issues, made sure to diff with the very latest mtd
> git version.
> 
> Attached is a complete rewrite of nand_ecc.c including documentation.
> This rewrite improves performance about 18 times on intel (D920),
> 7 times on MIPS and 5 times on ARM (NSLU2)
> 
> Signed-off-by: Frans Meulenbroeks <fransmeulenbroeks at gmail.com>

This look very complex to me. How about something like this.
Note, I could make it much smaller if allowed to result in a different
ecc value than current implementation.


#ifdef CONFIG_MTD_NAND_ECC_SMC
#define LOW_ORDER_INDEX 0
#define HIGH_ORDER_INDEX 1
#else
#define LOW_ORDER_INDEX 1
#define HIGH_ORDER_INDEX 0
#endif

/**
 * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256/512 byte block
 * @mtd:	MTD block structure
 * @dat:	raw data
 * @ecc_code:	buffer for ECC
 */
int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
		       u_char *ecc_code)
{
	uint32_t j = ((struct nand_chip *)mtd->priv)->ecc.size;			/* 256 or 512 bytes/ecc  */
	uint32_t k=0;
	uint32_t xor = 0;
	uint32_t tecc = 0;
	uint32_t ecc = 0;
	uint32_t * p = (uint32_t *)dat;
	uint32_t v;
//#define FORCE_ECC_ERROR
#ifdef FORCE_ECC_ERROR
	uint32_t forceBitNum;
	{
		/* force single bit ecc error to test ecc correction code */
		u_char* p = (u_char*)dat;
		forceBitNum = p[0] | (p[1]<<8);	/* 1st word of block determines which bit is made in error */
		forceBitNum &= 0xfff;
		if ((forceBitNum&0x800)==0) {
			/* limit error to 1st 256 bytes */
			p[forceBitNum>>3] ^= 1<<(forceBitNum&7);
			printk(KERN_INFO "Forcing ecc error, byte:0x%x, bit:%i\n",forceBitNum>>3,forceBitNum&7);
		}
	}
#endif

	do {
		v = *p++;
		xor ^= v;
		v ^= (v>>16);
		v ^= (v>>8);
		v ^= (v>>4);
		v ^= (v>>2);
		v ^= (v>>1);
		if (v&1) tecc ^= k;
		k++;
		j-=4;
	} while (j);
	__cpu_to_le64s(xor);
	v = (xor>>16)^xor;
	v ^= ((v>>8)&(0x00ff00ff&0x00ffffff));
	v ^= ((v>>4)&(0x0f0f0f0f&0x000f0fff));
	v ^= ((v>>2)&(0x33333333&0x0003033f));
	v ^= ((v>>1)&(0x55555555&0x00010117));
	
	/* now duplicate all bits */
	if (tecc&(1<<6)) ecc ^= 3<<22;
	if (tecc&(1<<5)) ecc ^= 3<<20;
	if (tecc&(1<<4)) ecc ^= 3<<18;
	if (tecc&(1<<3)) ecc ^= 3<<16;
	if (tecc&(1<<2)) ecc ^= 3<<14;
	if (tecc&(1<<1)) ecc ^= 3<<12;
	if (tecc&(1<<0)) ecc ^= 3<<10;

	if (v&(1<<16)) ecc ^= 3<<8;
	if (v&(1<<8)) ecc ^= 3<<6;
	if (v&(1<<4)) ecc ^= 3<<4;
	if (v&(1<<2)) ecc ^= 3<<2;
	if (v&(1<<1)) ecc ^= 3<<0;
	if (v&(1<<0)) ecc ^= 0x555555;		/* if parity is odd, low bits are opposite of high bits */

#ifdef FORCE_ECC_ERROR
	if (forceBitNum&0x800) {
		ecc ^= 1<<(forceBitNum&0xf);
		printk(KERN_INFO "Forcing single bit error in ecc itself bit %i\n",forceBitNum&0xf);
	}
#endif
	if (((struct nand_chip *)mtd->priv)->ecc.size==256) ecc <<= 2;
	ecc = ~ecc;


#ifdef VERIFY_NEW_ECC_ALG
	{
		uint32_t s;
		nand_calculate_ecc_old(mtd,dat,ecc_code);
		ecc &= 0xffffff;
		s = (ecc_code[HIGH_ORDER_INDEX]<<16) | (ecc_code[LOW_ORDER_INDEX]<<8) | ecc_code[2];

		if (s != ecc) {
			printk(KERN_ERR "New algorithm is buggy!!!! s=%x, ecc=%x\n",s,ecc);
			return 0;
		}
	}
#endif

	/* Calculate final ECC code */
	ecc_code[HIGH_ORDER_INDEX] = (u_char)(ecc>>16);
	ecc_code[LOW_ORDER_INDEX] = (u_char)(ecc>>8);
	ecc_code[2] = (u_char)ecc;
	return 0;
}



More information about the linux-mtd mailing list