Skip to content
Snippets Groups Projects
cmd_nand.c 41.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	int i;
    
    	nandptr = nand->IO_ADDR;
    
    	if (len <= 0)
    		return;
    
    	for (i = 0; i < len; i++)
    		WRITE_NAND(buf[i], nandptr);
    
    }
    
    /* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the
     *	various device information of the NFTL partition and Bad Unit Table. Update
     *	the ReplUnitTable[] table accroding to the Bad Unit Table. ReplUnitTable[]
     *	is used for management of Erase Unit in other routines in nftl.c and nftlmount.c
     */
    static int find_boot_record(struct NFTLrecord *nftl)
    {
    	struct nftl_uci1 h1;
    	struct nftl_oob oob;
    	unsigned int block, boot_record_count = 0;
    	int retlen;
    	u8 buf[SECTORSIZE];
    	struct NFTLMediaHeader *mh = &nftl->MediaHdr;
    	unsigned int i;
    
    	nftl->MediaUnit = BLOCK_NIL;
    	nftl->SpareMediaUnit = BLOCK_NIL;
    
    	/* search for a valid boot record */
    	for (block = 0; block < nftl->nb_blocks; block++) {
    		int ret;
    
    		/* Check for ANAND header first. Then can whinge if it's found but later
    		   checks fail */
    		if ((ret = nand_read_ecc(nftl->mtd, block * nftl->EraseSize, SECTORSIZE,
    					&retlen, buf, NULL))) {
    			static int warncount = 5;
    
    			if (warncount) {
    				printf("Block read at 0x%x failed\n", block * nftl->EraseSize);
    				if (!--warncount)
    					puts ("Further failures for this block will not be printed\n");
    			}
    			continue;
    		}
    
    		if (retlen < 6 || memcmp(buf, "ANAND", 6)) {
    			/* ANAND\0 not found. Continue */
    #ifdef PSYCHO_DEBUG
    			printf("ANAND header not found at 0x%x\n", block * nftl->EraseSize);
    #endif
    			continue;
    		}
    
    #ifdef NFTL_DEBUG
    		printf("ANAND header found at 0x%x\n", block * nftl->EraseSize);
    #endif
    
    		/* To be safer with BIOS, also use erase mark as discriminant */
    		if ((ret = nand_read_oob(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8,
    				8, &retlen, (char *)&h1) < 0)) {
    #ifdef NFTL_DEBUG
    			printf("ANAND header found at 0x%x, but OOB data read failed\n",
    			       block * nftl->EraseSize);
    #endif
    			continue;
    		}
    
    		/* OK, we like it. */
    
    		if (boot_record_count) {
    			/* We've already processed one. So we just check if
    			   this one is the same as the first one we found */
    			if (memcmp(mh, buf, sizeof(struct NFTLMediaHeader))) {
    #ifdef NFTL_DEBUG
    				printf("NFTL Media Headers at 0x%x and 0x%x disagree.\n",
    				       nftl->MediaUnit * nftl->EraseSize, block * nftl->EraseSize);
    #endif
    				/* if (debug) Print both side by side */
    				return -1;
    			}
    			if (boot_record_count == 1)
    				nftl->SpareMediaUnit = block;
    
    			boot_record_count++;
    			continue;
    		}
    
    		/* This is the first we've seen. Copy the media header structure into place */
    		memcpy(mh, buf, sizeof(struct NFTLMediaHeader));
    
    		/* Do some sanity checks on it */
    		if (mh->UnitSizeFactor != 0xff) {
    			puts ("Sorry, we don't support UnitSizeFactor "
    			      "of != 1 yet.\n");
    			return -1;
    		}
    
    		nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN);
    		if ((nftl->nb_boot_blocks + 2) >= nftl->nb_blocks) {
    			printf ("NFTL Media Header sanity check failed:\n"
    				"nb_boot_blocks (%d) + 2 > nb_blocks (%d)\n",
    				nftl->nb_boot_blocks, nftl->nb_blocks);
    			return -1;
    		}
    
    		nftl->numvunits = le32_to_cpu(mh->FormattedSize) / nftl->EraseSize;
    		if (nftl->numvunits > (nftl->nb_blocks - nftl->nb_boot_blocks - 2)) {
    			printf ("NFTL Media Header sanity check failed:\n"
    				"numvunits (%d) > nb_blocks (%d) - nb_boot_blocks(%d) - 2\n",
    				nftl->numvunits,
    				nftl->nb_blocks,
    				nftl->nb_boot_blocks);
    			return -1;
    		}
    
    		nftl->nr_sects  = nftl->numvunits * (nftl->EraseSize / SECTORSIZE);
    
    		/* If we're not using the last sectors in the device for some reason,
    		   reduce nb_blocks accordingly so we forget they're there */
    		nftl->nb_blocks = le16_to_cpu(mh->NumEraseUnits) + le16_to_cpu(mh->FirstPhysicalEUN);
    
    		/* read the Bad Erase Unit Table and modify ReplUnitTable[] accordingly */
    		for (i = 0; i < nftl->nb_blocks; i++) {
    			if ((i & (SECTORSIZE - 1)) == 0) {
    				/* read one sector for every SECTORSIZE of blocks */
    				if ((ret = nand_read_ecc(nftl->mtd, block * nftl->EraseSize +
    						       i + SECTORSIZE, SECTORSIZE,
    						       &retlen, buf, (char *)&oob)) < 0) {
    					puts ("Read of bad sector table failed\n");
    					return -1;
    				}
    			}
    			/* mark the Bad Erase Unit as RESERVED in ReplUnitTable */
    			if (buf[i & (SECTORSIZE - 1)] != 0xff)
    				nftl->ReplUnitTable[i] = BLOCK_RESERVED;
    		}
    
    		nftl->MediaUnit = block;
    		boot_record_count++;
    
    	} /* foreach (block) */
    
    	return boot_record_count?0:-1;
    }
    static int nand_read_oob(struct nand_chip* nand, size_t ofs, size_t len,
    		 size_t * retlen, u_char * buf)
    {
    	int len256 = 0, ret;
    	unsigned long nandptr;
    	struct Nand *mychip;
    
    
    	nandptr = nand->IO_ADDR;
    
    	mychip = &nand->chips[shr(ofs, nand->chipshift)];
    
    	/* update address for 2M x 8bit devices. OOB starts on the second */
    	/* page to maintain compatibility with nand_read_ecc. */
    	if (nand->page256) {
    		if (!(ofs & 0x8))
    			ofs += 0x100;
    		else
    			ofs -= 0x8;
    	}
    
    	NanD_Command(nand, NAND_CMD_READOOB);
    	NanD_Address(nand, ADDR_COLUMN_PAGE, ofs);
    
    	/* treat crossing 8-byte OOB data for 2M x 8bit devices */
    	/* Note: datasheet says it should automaticaly wrap to the */
    	/*       next OOB block, but it didn't work here. mf.      */
    	if (nand->page256 && ofs + len > (ofs | 0x7) + 1) {
    		len256 = (ofs | 0x7) + 1 - ofs;
    		NanD_ReadBuf(nand, buf, len256);
    
    		NanD_Command(nand, NAND_CMD_READOOB);
    		NanD_Address(nand, ADDR_COLUMN_PAGE, ofs & (~0x1ff));
    	}
    
    	NanD_ReadBuf(nand, &buf[len256], len - len256);
    
    	*retlen = len;
    	/* Reading the full OOB data drops us off of the end of the page,
             * causing the flash device to go into busy mode, so we need
             * to wait until ready 11.4.1 and Toshiba TC58256FT nands */
    
    	ret = NanD_WaitReady(nand);
    
    	return ret;
    
    }
    static int nand_write_oob(struct nand_chip* nand, size_t ofs, size_t len,
    		  size_t * retlen, const u_char * buf)
    {
    	int len256 = 0;
    	unsigned long nandptr = nand->IO_ADDR;
    
    #ifdef PSYCHO_DEBUG
    	printf("nand_write_oob(%lx, %d): %2.2X %2.2X %2.2X %2.2X ... %2.2X %2.2X .. %2.2X %2.2X\n",
    	       (long)ofs, len, buf[0], buf[1], buf[2], buf[3],
    	       buf[8], buf[9], buf[14],buf[15]);
    #endif
    
    	/* Reset the chip */
    	NanD_Command(nand, NAND_CMD_RESET);
    
    	/* issue the Read2 command to set the pointer to the Spare Data Area. */
    	NanD_Command(nand, NAND_CMD_READOOB);
    	NanD_Address(nand, ADDR_COLUMN_PAGE, ofs);
    
    	/* update address for 2M x 8bit devices. OOB starts on the second */
    	/* page to maintain compatibility with nand_read_ecc. */
    	if (nand->page256) {
    		if (!(ofs & 0x8))
    			ofs += 0x100;
    		else
    			ofs -= 0x8;
    	}
    
    	/* issue the Serial Data In command to initial the Page Program process */
    	NanD_Command(nand, NAND_CMD_SEQIN);
    	NanD_Address(nand, ADDR_COLUMN_PAGE, ofs);
    
    	/* treat crossing 8-byte OOB data for 2M x 8bit devices */
    	/* Note: datasheet says it should automaticaly wrap to the */
    	/*       next OOB block, but it didn't work here. mf.      */
    	if (nand->page256 && ofs + len > (ofs | 0x7) + 1) {
    		len256 = (ofs | 0x7) + 1 - ofs;
    		NanD_WriteBuf(nand, buf, len256);
    
    		NanD_Command(nand, NAND_CMD_PAGEPROG);
    		NanD_Command(nand, NAND_CMD_STATUS);
    		/* NanD_WaitReady() is implicit in NanD_Command */
    
    		if (READ_NAND(nandptr) & 1) {
    			puts ("Error programming oob data\n");
    			/* There was an error */
    			*retlen = 0;
    			return -1;
    		}
    		NanD_Command(nand, NAND_CMD_SEQIN);
    		NanD_Address(nand, ADDR_COLUMN_PAGE, ofs & (~0x1ff));
    	}
    
    	NanD_WriteBuf(nand, &buf[len256], len - len256);
    
    	NanD_Command(nand, NAND_CMD_PAGEPROG);
    	NanD_Command(nand, NAND_CMD_STATUS);
    	/* NanD_WaitReady() is implicit in NanD_Command */
    
    	if (READ_NAND(nandptr) & 1) {
    		puts ("Error programming oob data\n");
    		/* There was an error */
    		*retlen = 0;
    		return -1;
    	}
    
    	*retlen = len;
    	return 0;
    
    }
    #endif
    
    static int nand_erase(struct nand_chip* nand, size_t ofs, size_t len)
    {
    	unsigned long nandptr;
    	struct Nand *mychip;
    
    
    	if (ofs & (nand->erasesize-1) || len & (nand->erasesize-1)) {
    		printf ("Offset and size must be sector aligned, erasesize = %d\n",
                            (int) nand->erasesize);
    		return -1;
    	}
    
    	nandptr = nand->IO_ADDR;
    
    
    	/* Select the NAND device */
    	NAND_ENABLE_CE(nand);  /* set pin low */
    
    	/* Check the WP bit */
    	NanD_Command(nand, NAND_CMD_STATUS);
    	if (!(READ_NAND(nand->IO_ADDR) & 0x80)) {
    		printf ("nand_write_ecc: Device is write protected!!!\n");
    		ret = -1;
    		goto out;
    	}
    
    
    	/* Select the NAND device */
    	NAND_ENABLE_CE(nand);  /* set pin low */
    
    	/* Check the WP bit */
    	NanD_Command(nand, NAND_CMD_STATUS);
    	if (!(READ_NAND(nand->IO_ADDR) & 0x80)) {
    		printf ("%s: Device is write protected!!!\n", __FUNCTION__);
    		ret = -1;
    		goto out;
    	}
    
    
    	/* FIXME: Do nand in the background. Use timers or schedule_task() */
    	while(len) {
    
    		/*mychip = &nand->chips[shr(ofs, nand->chipshift)];*/
    		mychip = &nand->chips[ofs >> nand->chipshift];
    
    
    		NanD_Command(nand, NAND_CMD_ERASE1);
    		NanD_Address(nand, ADDR_PAGE, ofs);
    		NanD_Command(nand, NAND_CMD_ERASE2);
    
    		NanD_Command(nand, NAND_CMD_STATUS);
    
    		if (READ_NAND(nandptr) & 1) {
    
    			printf ("%s: Error erasing at 0x%lx\n",
    				__FUNCTION__, (long)ofs);
    
    			/* There was an error */
    
    			ret = -1;
    			goto out;
    
    		}
    		ofs += nand->erasesize;
    		len -= nand->erasesize;
    	}
    
    
    out:
    	/* De-select the NAND device */
    	NAND_DISABLE_CE(nand);  /* set pin high */
    
    	return ret;
    
    }
    
    static inline int nandcheck(unsigned long potential, unsigned long physadr)
    {
    	return 0;
    }
    
    void nand_probe(unsigned long physadr)
    {
    	struct nand_chip *nand = NULL;
    	int i = 0, ChipID = 1;
    
    #ifdef CONFIG_MTD_NAND_ECC_JFFS2
    	oob_config.ecc_pos[0] = NAND_JFFS2_OOB_ECCPOS0;
    	oob_config.ecc_pos[1] = NAND_JFFS2_OOB_ECCPOS1;
    	oob_config.ecc_pos[2] = NAND_JFFS2_OOB_ECCPOS2;
    	oob_config.ecc_pos[3] = NAND_JFFS2_OOB_ECCPOS3;
    	oob_config.ecc_pos[4] = NAND_JFFS2_OOB_ECCPOS4;
    	oob_config.ecc_pos[5] = NAND_JFFS2_OOB_ECCPOS5;
    	oob_config.badblock_pos = 5;
    	oob_config.eccvalid_pos = 4;
    #else
    	oob_config.ecc_pos[0] = NAND_NOOB_ECCPOS0;
    	oob_config.ecc_pos[1] = NAND_NOOB_ECCPOS1;
    	oob_config.ecc_pos[2] = NAND_NOOB_ECCPOS2;
    	oob_config.ecc_pos[3] = NAND_NOOB_ECCPOS3;
    	oob_config.ecc_pos[4] = NAND_NOOB_ECCPOS4;
    	oob_config.ecc_pos[5] = NAND_NOOB_ECCPOS5;
    	oob_config.badblock_pos = NAND_NOOB_BADBPOS;
    	oob_config.eccvalid_pos = NAND_NOOB_ECCVPOS;
    #endif
    
    	for (i=0; i<CFG_MAX_NAND_DEVICE; i++) {
    		if (nand_dev_desc[i].ChipID == NAND_ChipID_UNKNOWN) {
    			nand = nand_dev_desc + i;
    			break;
    		}
    	}
    
    
    	if (curr_device == -1)
    		curr_device = i;
    
    	memset((char *)nand, 0, sizeof(struct nand_chip));
    
    	nand->cache_page = -1;  /* init the cache page */
    	nand->IO_ADDR = physadr;
    	nand->ChipID = ChipID;
    	NanD_ScanChips(nand);
    	nand->data_buf = malloc (nand->oobblock + nand->oobsize);
    	if (!nand->data_buf) {
    		puts ("Cannot allocate memory for data structures.\n");
    		return;
    	}
    
    }
    
    #ifdef CONFIG_MTD_NAND_ECC
    /*
     * Pre-calculated 256-way 1 byte column parity
     */
    static const u_char nand_ecc_precalc_table[] = {
    	0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
    	0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
    	0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
    	0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
    	0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
    	0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
    	0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
    	0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
    	0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
    	0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
    	0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
    	0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
    	0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
    	0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
    	0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
    	0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00
    };
    
    
    /*
     * Creates non-inverted ECC code from line parity
     */
    static void nand_trans_result(u_char reg2, u_char reg3,
    	u_char *ecc_code)
    {
    	u_char a, b, i, tmp1, tmp2;
    
    	/* Initialize variables */
    	a = b = 0x80;
    	tmp1 = tmp2 = 0;
    
    	/* Calculate first ECC byte */
    	for (i = 0; i < 4; i++) {
    		if (reg3 & a)		/* LP15,13,11,9 --> ecc_code[0] */
    			tmp1 |= b;
    		b >>= 1;
    		if (reg2 & a)		/* LP14,12,10,8 --> ecc_code[0] */
    			tmp1 |= b;
    		b >>= 1;
    		a >>= 1;
    	}
    
    	/* Calculate second ECC byte */
    	b = 0x80;
    	for (i = 0; i < 4; i++) {
    		if (reg3 & a)		/* LP7,5,3,1 --> ecc_code[1] */
    			tmp2 |= b;
    		b >>= 1;
    		if (reg2 & a)		/* LP6,4,2,0 --> ecc_code[1] */
    			tmp2 |= b;
    		b >>= 1;
    		a >>= 1;
    	}
    
    	/* Store two of the ECC bytes */
    	ecc_code[0] = tmp1;
    	ecc_code[1] = tmp2;
    }
    
    /*
     * Calculate 3 byte ECC code for 256 byte block
     */
    static void nand_calculate_ecc (const u_char *dat, u_char *ecc_code)
    {
    	u_char idx, reg1, reg2, reg3;
    	int j;
    
    	/* Initialize variables */
    	reg1 = reg2 = reg3 = 0;
    	ecc_code[0] = ecc_code[1] = ecc_code[2] = 0;
    
    	/* Build up column parity */
    	for(j = 0; j < 256; j++) {
    
    		/* Get CP0 - CP5 from table */
    		idx = nand_ecc_precalc_table[dat[j]];
    		reg1 ^= (idx & 0x3f);
    
    		/* All bit XOR = 1 ? */
    		if (idx & 0x40) {
    			reg3 ^= (u_char) j;
    			reg2 ^= ~((u_char) j);
    		}
    	}
    
    	/* Create non-inverted ECC code from line parity */
    	nand_trans_result(reg2, reg3, ecc_code);
    
    	/* Calculate final ECC code */
    	ecc_code[0] = ~ecc_code[0];
    	ecc_code[1] = ~ecc_code[1];
    	ecc_code[2] = ((~reg1) << 2) | 0x03;
    }
    
    /*
     * Detect and correct a 1 bit error for 256 byte block
     */
    static int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc)
    {
    	u_char a, b, c, d1, d2, d3, add, bit, i;
    
    	/* Do error detection */
    	d1 = calc_ecc[0] ^ read_ecc[0];
    	d2 = calc_ecc[1] ^ read_ecc[1];
    	d3 = calc_ecc[2] ^ read_ecc[2];
    
    	if ((d1 | d2 | d3) == 0) {
    		/* No errors */
    		return 0;
    	}
    	else {
    		a = (d1 ^ (d1 >> 1)) & 0x55;
    		b = (d2 ^ (d2 >> 1)) & 0x55;
    		c = (d3 ^ (d3 >> 1)) & 0x54;
    
    		/* Found and will correct single bit error in the data */
    		if ((a == 0x55) && (b == 0x55) && (c == 0x54)) {
    			c = 0x80;
    			add = 0;
    			a = 0x80;
    			for (i=0; i<4; i++) {
    				if (d1 & c)
    					add |= a;
    				c >>= 2;
    				a >>= 1;
    			}
    			c = 0x80;
    			for (i=0; i<4; i++) {
    				if (d2 & c)
    					add |= a;
    				c >>= 2;
    				a >>= 1;
    			}
    			bit = 0;
    			b = 0x04;
    			c = 0x80;
    			for (i=0; i<3; i++) {
    				if (d3 & c)
    					bit |= b;
    				c >>= 2;
    				b >>= 1;
    			}
    			b = 0x01;
    			a = dat[add];
    			a ^= (b << bit);
    			dat[add] = a;
    			return 1;
    		}
    		else {
    			i = 0;
    			while (d1) {
    				if (d1 & 0x01)
    					++i;
    				d1 >>= 1;
    			}
    			while (d2) {
    				if (d2 & 0x01)
    					++i;
    				d2 >>= 1;
    			}
    			while (d3) {
    				if (d3 & 0x01)
    					++i;
    				d3 >>= 1;
    			}
    			if (i == 1) {
    				/* ECC Code Error Correction */
    				read_ecc[0] = calc_ecc[0];
    				read_ecc[1] = calc_ecc[1];
    				read_ecc[2] = calc_ecc[2];
    				return 2;
    			}
    			else {
    				/* Uncorrectable Error */
    				return -1;
    			}
    		}
    	}
    
    	/* Should never happen */
    	return -1;
    }
    #endif
    #endif /* (CONFIG_COMMANDS & CFG_CMD_NAND) */