diff options
Diffstat (limited to 'drivers/mtd/nand/nand_util.c')
| -rw-r--r-- | drivers/mtd/nand/nand_util.c | 122 | 
1 files changed, 122 insertions, 0 deletions
| diff --git a/drivers/mtd/nand/nand_util.c b/drivers/mtd/nand/nand_util.c index 285568316..2ba0c5ef9 100644 --- a/drivers/mtd/nand/nand_util.c +++ b/drivers/mtd/nand/nand_util.c @@ -683,3 +683,125 @@ int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,  	return 0;  } + +#ifdef CONFIG_CMD_NAND_TORTURE + +/** + * check_pattern: + * + * Check if buffer contains only a certain byte pattern. + * + * @param buf buffer to check + * @param patt the pattern to check + * @param size buffer size in bytes + * @return 1 if there are only patt bytes in buf + *         0 if something else was found + */ +static int check_pattern(const u_char *buf, u_char patt, int size) +{ +	int i; + +	for (i = 0; i < size; i++) +		if (buf[i] != patt) +			return 0; +	return 1; +} + +/** + * nand_torture: + * + * Torture a block of NAND flash. + * This is useful to determine if a block that caused a write error is still + * good or should be marked as bad. + * + * @param nand NAND device + * @param offset offset in flash + * @return 0 if the block is still good + */ +int nand_torture(nand_info_t *nand, loff_t offset) +{ +	u_char patterns[] = {0xa5, 0x5a, 0x00}; +	struct erase_info instr = { +		.mtd = nand, +		.addr = offset, +		.len = nand->erasesize, +	}; +	size_t retlen; +	int err, ret = -1, i, patt_count; +	u_char *buf; + +	if ((offset & (nand->erasesize - 1)) != 0) { +		puts("Attempt to torture a block at a non block-aligned offset\n"); +		return -EINVAL; +	} + +	if (offset + nand->erasesize > nand->size) { +		puts("Attempt to torture a block outside the flash area\n"); +		return -EINVAL; +	} + +	patt_count = ARRAY_SIZE(patterns); + +	buf = malloc(nand->erasesize); +	if (buf == NULL) { +		puts("Out of memory for erase block buffer\n"); +		return -ENOMEM; +	} + +	for (i = 0; i < patt_count; i++) { +		err = nand->erase(nand, &instr); +		if (err) { +			printf("%s: erase() failed for block at 0x%llx: %d\n", +				nand->name, instr.addr, err); +			goto out; +		} + +		/* Make sure the block contains only 0xff bytes */ +		err = nand->read(nand, offset, nand->erasesize, &retlen, buf); +		if ((err && err != -EUCLEAN) || retlen != nand->erasesize) { +			printf("%s: read() failed for block at 0x%llx: %d\n", +				nand->name, instr.addr, err); +			goto out; +		} + +		err = check_pattern(buf, 0xff, nand->erasesize); +		if (!err) { +			printf("Erased block at 0x%llx, but a non-0xff byte was found\n", +				offset); +			ret = -EIO; +			goto out; +		} + +		/* Write a pattern and check it */ +		memset(buf, patterns[i], nand->erasesize); +		err = nand->write(nand, offset, nand->erasesize, &retlen, buf); +		if (err || retlen != nand->erasesize) { +			printf("%s: write() failed for block at 0x%llx: %d\n", +				nand->name, instr.addr, err); +			goto out; +		} + +		err = nand->read(nand, offset, nand->erasesize, &retlen, buf); +		if ((err && err != -EUCLEAN) || retlen != nand->erasesize) { +			printf("%s: read() failed for block at 0x%llx: %d\n", +				nand->name, instr.addr, err); +			goto out; +		} + +		err = check_pattern(buf, patterns[i], nand->erasesize); +		if (!err) { +			printf("Pattern 0x%.2x checking failed for block at " +					"0x%llx\n", patterns[i], offset); +			ret = -EIO; +			goto out; +		} +	} + +	ret = 0; + +out: +	free(buf); +	return ret; +} + +#endif |