diff options
| author | Tom Rini <trini@ti.com> | 2014-03-10 14:22:54 -0400 | 
|---|---|---|
| committer | Tom Rini <trini@ti.com> | 2014-03-10 14:22:54 -0400 | 
| commit | 5495dae7aa9d5cd161e07174d38acac86515c58a (patch) | |
| tree | 848c7f48688aeb5ada1fe221dc5dff4729561814 /drivers/mtd/nand/atmel_nand.c | |
| parent | c0d297946fd2540c8057eb1589429978da888dbb (diff) | |
| parent | 27019e4a949fc3be2e895ac20839c4d4d119f902 (diff) | |
| download | olio-uboot-2014.01-5495dae7aa9d5cd161e07174d38acac86515c58a.tar.xz olio-uboot-2014.01-5495dae7aa9d5cd161e07174d38acac86515c58a.zip | |
Merge branch 'master' of git://git.denx.de/u-boot-arm
Diffstat (limited to 'drivers/mtd/nand/atmel_nand.c')
| -rw-r--r-- | drivers/mtd/nand/atmel_nand.c | 208 | 
1 files changed, 208 insertions, 0 deletions
| diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 05ddfbb64..e1fc48fca 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -31,6 +31,10 @@  #ifdef CONFIG_ATMEL_NAND_HW_PMECC +#ifdef CONFIG_SPL_BUILD +#undef CONFIG_SYS_NAND_ONFI_DETECTION +#endif +  struct atmel_nand_host {  	struct pmecc_regs __iomem *pmecc;  	struct pmecc_errloc_regs __iomem *pmerrloc; @@ -1169,6 +1173,209 @@ static int at91_nand_ready(struct mtd_info *mtd)  }  #endif +#ifdef CONFIG_SPL_BUILD +/* The following code is for SPL */ +static nand_info_t mtd; +static struct nand_chip nand_chip; + +static int nand_command(int block, int page, uint32_t offs, u8 cmd) +{ +	struct nand_chip *this = mtd.priv; +	int page_addr = page + block * CONFIG_SYS_NAND_PAGE_COUNT; +	void (*hwctrl)(struct mtd_info *mtd, int cmd, +			unsigned int ctrl) = this->cmd_ctrl; + +	while (this->dev_ready(&mtd)) +		; + +	if (cmd == NAND_CMD_READOOB) { +		offs += CONFIG_SYS_NAND_PAGE_SIZE; +		cmd = NAND_CMD_READ0; +	} + +	hwctrl(&mtd, cmd, NAND_CTRL_CLE | NAND_CTRL_CHANGE); + +	if (this->options & NAND_BUSWIDTH_16) +		offs >>= 1; + +	hwctrl(&mtd, offs & 0xff, NAND_CTRL_ALE | NAND_CTRL_CHANGE); +	hwctrl(&mtd, (offs >> 8) & 0xff, NAND_CTRL_ALE); +	hwctrl(&mtd, (page_addr & 0xff), NAND_CTRL_ALE); +	hwctrl(&mtd, ((page_addr >> 8) & 0xff), NAND_CTRL_ALE); +#ifdef CONFIG_SYS_NAND_5_ADDR_CYCLE +	hwctrl(&mtd, (page_addr >> 16) & 0x0f, NAND_CTRL_ALE); +#endif +	hwctrl(&mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + +	hwctrl(&mtd, NAND_CMD_READSTART, NAND_CTRL_CLE | NAND_CTRL_CHANGE); +	hwctrl(&mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); + +	while (this->dev_ready(&mtd)) +		; + +	return 0; +} + +static int nand_is_bad_block(int block) +{ +	struct nand_chip *this = mtd.priv; + +	nand_command(block, 0, CONFIG_SYS_NAND_BAD_BLOCK_POS, NAND_CMD_READOOB); + +	if (this->options & NAND_BUSWIDTH_16) { +		if (readw(this->IO_ADDR_R) != 0xffff) +			return 1; +	} else { +		if (readb(this->IO_ADDR_R) != 0xff) +			return 1; +	} + +	return 0; +} + +#ifdef CONFIG_SPL_NAND_ECC +static int nand_ecc_pos[] = CONFIG_SYS_NAND_ECCPOS; +#define ECCSTEPS (CONFIG_SYS_NAND_PAGE_SIZE / \ +		  CONFIG_SYS_NAND_ECCSIZE) +#define ECCTOTAL (ECCSTEPS * CONFIG_SYS_NAND_ECCBYTES) + +static int nand_read_page(int block, int page, void *dst) +{ +	struct nand_chip *this = mtd.priv; +	u_char ecc_calc[ECCTOTAL]; +	u_char ecc_code[ECCTOTAL]; +	u_char oob_data[CONFIG_SYS_NAND_OOBSIZE]; +	int eccsize = CONFIG_SYS_NAND_ECCSIZE; +	int eccbytes = CONFIG_SYS_NAND_ECCBYTES; +	int eccsteps = ECCSTEPS; +	int i; +	uint8_t *p = dst; +	nand_command(block, page, 0, NAND_CMD_READ0); + +	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) { +		if (this->ecc.mode != NAND_ECC_SOFT) +			this->ecc.hwctl(&mtd, NAND_ECC_READ); +		this->read_buf(&mtd, p, eccsize); +		this->ecc.calculate(&mtd, p, &ecc_calc[i]); +	} +	this->read_buf(&mtd, oob_data, CONFIG_SYS_NAND_OOBSIZE); + +	for (i = 0; i < ECCTOTAL; i++) +		ecc_code[i] = oob_data[nand_ecc_pos[i]]; + +	eccsteps = ECCSTEPS; +	p = dst; + +	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) +		this->ecc.correct(&mtd, p, &ecc_code[i], &ecc_calc[i]); + +	return 0; +} +#else +static int nand_read_page(int block, int page, void *dst) +{ +	struct nand_chip *this = mtd.priv; + +	nand_command(block, page, 0, NAND_CMD_READ0); +	atmel_nand_pmecc_read_page(&mtd, this, dst, 0, page); + +	return 0; +} +#endif /* CONFIG_SPL_NAND_ECC */ + +int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst) +{ +	unsigned int block, lastblock; +	unsigned int page; + +	block = offs / CONFIG_SYS_NAND_BLOCK_SIZE; +	lastblock = (offs + size - 1) / CONFIG_SYS_NAND_BLOCK_SIZE; +	page = (offs % CONFIG_SYS_NAND_BLOCK_SIZE) / CONFIG_SYS_NAND_PAGE_SIZE; + +	while (block <= lastblock) { +		if (!nand_is_bad_block(block)) { +			while (page < CONFIG_SYS_NAND_PAGE_COUNT) { +				nand_read_page(block, page, dst); +				dst += CONFIG_SYS_NAND_PAGE_SIZE; +				page++; +			} + +			page = 0; +		} else { +			lastblock++; +		} + +		block++; +	} + +	return 0; +} + +int at91_nand_wait_ready(struct mtd_info *mtd) +{ +	struct nand_chip *this = mtd->priv; + +	udelay(this->chip_delay); + +	return 0; +} + +int board_nand_init(struct nand_chip *nand) +{ +	int ret = 0; + +	nand->ecc.mode = NAND_ECC_SOFT; +#ifdef CONFIG_SYS_NAND_DBW_16 +	nand->options = NAND_BUSWIDTH_16; +	nand->read_buf = nand_read_buf16; +#else +	nand->read_buf = nand_read_buf; +#endif +	nand->cmd_ctrl = at91_nand_hwcontrol; +#ifdef CONFIG_SYS_NAND_READY_PIN +	nand->dev_ready = at91_nand_ready; +#else +	nand->dev_ready = at91_nand_wait_ready; +#endif +	nand->chip_delay = 20; + +#ifdef CONFIG_ATMEL_NAND_HWECC +#ifdef CONFIG_ATMEL_NAND_HW_PMECC +	ret = atmel_pmecc_nand_init_params(nand, &mtd); +#endif +#endif + +	return ret; +} + +void nand_init(void) +{ +	mtd.writesize = CONFIG_SYS_NAND_PAGE_SIZE; +	mtd.oobsize = CONFIG_SYS_NAND_OOBSIZE; +	mtd.priv = &nand_chip; +	nand_chip.IO_ADDR_R = (void __iomem *)CONFIG_SYS_NAND_BASE; +	nand_chip.IO_ADDR_W = (void __iomem *)CONFIG_SYS_NAND_BASE; +	board_nand_init(&nand_chip); + +#ifdef CONFIG_SPL_NAND_ECC +	if (nand_chip.ecc.mode == NAND_ECC_SOFT) { +		nand_chip.ecc.calculate = nand_calculate_ecc; +		nand_chip.ecc.correct = nand_correct_data; +	} +#endif + +	if (nand_chip.select_chip) +		nand_chip.select_chip(&mtd, 0); +} + +void nand_deselect(void) +{ +	if (nand_chip.select_chip) +		nand_chip.select_chip(&mtd, -1); +} + +#else +  #ifndef CONFIG_SYS_NAND_BASE_LIST  #define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE }  #endif @@ -1227,3 +1434,4 @@ void board_nand_init(void)  			dev_err(host->dev, "atmel_nand: Fail to initialize #%d chip",  				i);  } +#endif /* CONFIG_SPL_BUILD */ |