!449 at91sam9260平台增加mtdnand驱动用于支持uffs文件系统
* update bsp/at91sam9260/drivers/at91_nand.c. * update bsp/at91sam9260/drivers/at91_nand.c. * update bsp/at91sam9260/drivers/at91_nand.h. * update bsp/at91sam9260/drivers/at91_nand.c. * update bsp/at91sam9260/drivers/at91_nand.c. * 添加at91sam9260下mtd nand flash驱动
This commit is contained in:
parent
eb649efb40
commit
2282ff3c90
|
@ -23,6 +23,8 @@ if GetDepend('RT_USING_LWIP'):
|
||||||
if GetDepend('RT_USING_I2C') and GetDepend('RT_USING_I2C_BITOPS'):
|
if GetDepend('RT_USING_I2C') and GetDepend('RT_USING_I2C_BITOPS'):
|
||||||
src += ['at91_i2c_gpio.c']
|
src += ['at91_i2c_gpio.c']
|
||||||
|
|
||||||
|
if GetDepend('RT_USING_MTD_NAND'):
|
||||||
|
src += ['at91_nand.c']
|
||||||
|
|
||||||
CPPPATH = [cwd]
|
CPPPATH = [cwd]
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,635 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* Change Logs:
|
||||||
|
* Date Author Notes
|
||||||
|
* 2022-01-08 brightsally first version
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <rtdevice.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <at91sam926x.h>
|
||||||
|
#include "at91_nand.h"
|
||||||
|
|
||||||
|
static struct nand_chip_id *chip;
|
||||||
|
static unsigned int bufsize = 528;
|
||||||
|
static unsigned char pages_per_block = 32;
|
||||||
|
static unsigned char eccsize = 6;
|
||||||
|
static struct rt_mtd_nand_device _partition[2];
|
||||||
|
/*****************************************************************************
|
||||||
|
nand_calculate_ecc function copy from uboot
|
||||||
|
*****************************************************************************/
|
||||||
|
#define u_char unsigned char
|
||||||
|
/* Define default oob placement schemes for large and small page devices */
|
||||||
|
static struct nand_ecclayout nand_oob_16 =
|
||||||
|
{
|
||||||
|
|
||||||
|
.eccbytes = 6,
|
||||||
|
.eccpos = {0, 1, 2, 3, 6, 7},
|
||||||
|
.oobfree = {
|
||||||
|
{
|
||||||
|
.offset = 8,
|
||||||
|
. length = 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct nand_ecclayout nand_oob_64 =
|
||||||
|
{
|
||||||
|
.eccbytes = 24,
|
||||||
|
.eccpos = {
|
||||||
|
40, 41, 42, 43, 44, 45, 46, 47,
|
||||||
|
48, 49, 50, 51, 52, 53, 54, 55,
|
||||||
|
56, 57, 58, 59, 60, 61, 62, 63
|
||||||
|
},
|
||||||
|
.oobfree = {
|
||||||
|
{
|
||||||
|
.offset = 2,
|
||||||
|
.length = 38
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256-byte block
|
||||||
|
* @mtd: MTD block structure
|
||||||
|
* @dat: raw data
|
||||||
|
* @ecc_code: buffer for ECC
|
||||||
|
*/
|
||||||
|
int nand_calculate_ecc(const u_char *dat, u_char *ecc_code)
|
||||||
|
{
|
||||||
|
uint8_t idx, reg1, reg2, reg3, tmp1, tmp2;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Initialize variables */
|
||||||
|
reg1 = reg2 = reg3 = 0;
|
||||||
|
|
||||||
|
/* Build up column parity */
|
||||||
|
for (i = 0; i < 256; i++)
|
||||||
|
{
|
||||||
|
/* Get CP0 - CP5 from table */
|
||||||
|
idx = nand_ecc_precalc_table[*dat++];
|
||||||
|
reg1 ^= (idx & 0x3f);
|
||||||
|
|
||||||
|
/* All bit XOR = 1 ? */
|
||||||
|
if (idx & 0x40)
|
||||||
|
{
|
||||||
|
reg3 ^= (uint8_t) i;
|
||||||
|
reg2 ^= ~((uint8_t) i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create non-inverted ECC code from line parity */
|
||||||
|
tmp1 = (reg3 & 0x80) >> 0; /* B7 -> B7 */
|
||||||
|
tmp1 |= (reg2 & 0x80) >> 1; /* B7 -> B6 */
|
||||||
|
tmp1 |= (reg3 & 0x40) >> 1; /* B6 -> B5 */
|
||||||
|
tmp1 |= (reg2 & 0x40) >> 2; /* B6 -> B4 */
|
||||||
|
tmp1 |= (reg3 & 0x20) >> 2; /* B5 -> B3 */
|
||||||
|
tmp1 |= (reg2 & 0x20) >> 3; /* B5 -> B2 */
|
||||||
|
tmp1 |= (reg3 & 0x10) >> 3; /* B4 -> B1 */
|
||||||
|
tmp1 |= (reg2 & 0x10) >> 4; /* B4 -> B0 */
|
||||||
|
|
||||||
|
tmp2 = (reg3 & 0x08) << 4; /* B3 -> B7 */
|
||||||
|
tmp2 |= (reg2 & 0x08) << 3; /* B3 -> B6 */
|
||||||
|
tmp2 |= (reg3 & 0x04) << 3; /* B2 -> B5 */
|
||||||
|
tmp2 |= (reg2 & 0x04) << 2; /* B2 -> B4 */
|
||||||
|
tmp2 |= (reg3 & 0x02) << 2; /* B1 -> B3 */
|
||||||
|
tmp2 |= (reg2 & 0x02) << 1; /* B1 -> B2 */
|
||||||
|
tmp2 |= (reg3 & 0x01) << 1; /* B0 -> B1 */
|
||||||
|
tmp2 |= (reg2 & 0x01) << 0; /* B7 -> B0 */
|
||||||
|
|
||||||
|
/* Calculate final ECC code */
|
||||||
|
#ifdef CONFIG_MTD_NAND_ECC_SMC
|
||||||
|
ecc_code[0] = ~tmp2;
|
||||||
|
ecc_code[1] = ~tmp1;
|
||||||
|
#else
|
||||||
|
ecc_code[0] = ~tmp1;
|
||||||
|
ecc_code[1] = ~tmp2;
|
||||||
|
#endif
|
||||||
|
ecc_code[2] = ((~reg1) << 2) | 0x03;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
/*********nand_calculate_ecc function copy from uboot end*********************/
|
||||||
|
static void at91_nand_udelay(rt_uint32_t us)
|
||||||
|
{
|
||||||
|
rt_uint32_t len;
|
||||||
|
for (; us > 0; us --)
|
||||||
|
for (len = 0; len < 10; len++);
|
||||||
|
}
|
||||||
|
static void wait_udelay()
|
||||||
|
{
|
||||||
|
if (chip->pagesize == 2048)at91_nand_udelay(2000);
|
||||||
|
else at91_nand_udelay(1);
|
||||||
|
}
|
||||||
|
void nand_enable_cs(void)
|
||||||
|
{
|
||||||
|
writel(0x00004000, 0xfffff834);
|
||||||
|
readl(0xfffff838);
|
||||||
|
//rt_kprintf("===i=0x%x\r\n",i);
|
||||||
|
wait_udelay();
|
||||||
|
}
|
||||||
|
void nand_disable_cs(void)
|
||||||
|
{
|
||||||
|
writeb(0xff, 0x40400000);
|
||||||
|
writel(0x00004000, 0xfffff830);
|
||||||
|
readl(0xfffff830);
|
||||||
|
wait_udelay();
|
||||||
|
}
|
||||||
|
|
||||||
|
void nand_write_cmd(unsigned char cmd)
|
||||||
|
{
|
||||||
|
writeb(cmd, CMD_REG);
|
||||||
|
wait_udelay();
|
||||||
|
}
|
||||||
|
void nand_write_addr(unsigned char addr)
|
||||||
|
{
|
||||||
|
writeb(addr, ADDR_REG);
|
||||||
|
wait_udelay();
|
||||||
|
}
|
||||||
|
void nand_write_data_byte(unsigned char data)
|
||||||
|
{
|
||||||
|
writeb(data, DATA_REG);
|
||||||
|
wait_udelay();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char nand_read_data_byte(void)
|
||||||
|
{
|
||||||
|
return readb(DATA_REG);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int nand_wait_ready(void)
|
||||||
|
{
|
||||||
|
unsigned int timeout = 10000;
|
||||||
|
|
||||||
|
nand_write_cmd(CMD_STATUS);
|
||||||
|
while ((!(nand_read_data_byte() & STATUS_READY)) && timeout--);
|
||||||
|
if (!timeout)return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------------------------------------------
|
||||||
|
/* read chip id */
|
||||||
|
static rt_err_t at9260_nand_read_id(struct rt_mtd_nand_device *device)
|
||||||
|
{
|
||||||
|
int manf_id, dev_id;
|
||||||
|
unsigned int chipid;
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
nand_enable_cs();
|
||||||
|
|
||||||
|
nand_write_cmd(CMD_READID);
|
||||||
|
nand_write_addr(CMD_READ_1);
|
||||||
|
manf_id = nand_read_data_byte();
|
||||||
|
dev_id = nand_read_data_byte();
|
||||||
|
nand_disable_cs();
|
||||||
|
|
||||||
|
chipid = (manf_id << 8) | dev_id;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(nand_ids); i++)
|
||||||
|
{
|
||||||
|
if (chipid == nand_ids[i].chip_id)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == ARRAY_SIZE(nand_ids))
|
||||||
|
{
|
||||||
|
rt_kprintf("NAND: Not found Manufacturer ID: %x," \
|
||||||
|
"Chip ID: 0x%x\n", manf_id, dev_id);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//find nand chip
|
||||||
|
rt_kprintf("NAND: Manufacturer ID: %x Chip ID: %x Total Block:%d\n", manf_id, dev_id, nand_ids[i].numblocks);
|
||||||
|
|
||||||
|
chip->numblocks = nand_ids[i].numblocks;
|
||||||
|
chip->pagesize = nand_ids[i].pagesize;
|
||||||
|
chip->blocksize = nand_ids[i].blocksize;
|
||||||
|
chip->oobsize = nand_ids[i].oobsize;
|
||||||
|
chip->buswidth = nand_ids[i].buswidth;
|
||||||
|
chip->numblocks = nand_ids[i].numblocks;
|
||||||
|
|
||||||
|
bufsize = chip->pagesize + chip->oobsize;
|
||||||
|
eccsize = (chip->pagesize) * 3 / 256;
|
||||||
|
|
||||||
|
if (chip->pagesize == 512)
|
||||||
|
{
|
||||||
|
pages_per_block = 32;
|
||||||
|
rt_kprintf("===small block pages===== \n");
|
||||||
|
}
|
||||||
|
else if (chip->pagesize == 2048)
|
||||||
|
{
|
||||||
|
pages_per_block = 64;
|
||||||
|
rt_kprintf("===big block pages===== \n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
int nand_read_page9260(unsigned int block, unsigned int page, unsigned char *data, rt_uint32_t data_len, unsigned char *spare, rt_uint32_t spare_len)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
unsigned int blockpage;
|
||||||
|
unsigned char buff1[bufsize];
|
||||||
|
|
||||||
|
blockpage = block * (pages_per_block) + page;
|
||||||
|
memset(buff1, 0xff, bufsize);
|
||||||
|
|
||||||
|
nand_enable_cs();
|
||||||
|
nand_write_cmd(CMD_READ_A0);
|
||||||
|
nand_write_addr(0);
|
||||||
|
if (bufsize == 2112)nand_write_addr(0);
|
||||||
|
nand_write_addr(blockpage & 0xff);
|
||||||
|
nand_write_addr((blockpage >> 8) & 0xff);
|
||||||
|
nand_write_addr((blockpage >> 16) & 0xff);
|
||||||
|
|
||||||
|
at91_nand_udelay(2000);
|
||||||
|
|
||||||
|
if (bufsize == 528)nand_write_cmd(CMD_READ_1);
|
||||||
|
else if (bufsize == 2112)nand_write_cmd(CMD_READ_2);
|
||||||
|
|
||||||
|
for (i = 0; i < chip->pagesize; i++)buff1[i] = nand_read_data_byte();
|
||||||
|
for (i = 0; i < chip->oobsize; i++)buff1[i + chip->pagesize] = nand_read_data_byte();
|
||||||
|
|
||||||
|
nand_wait_ready();
|
||||||
|
nand_disable_cs();
|
||||||
|
|
||||||
|
if (data != RT_NULL)
|
||||||
|
rt_memcpy(data, buff1, data_len);
|
||||||
|
if (spare != RT_NULL)
|
||||||
|
rt_memcpy(spare, &buff1[chip->pagesize], spare_len);
|
||||||
|
|
||||||
|
return 0x00;
|
||||||
|
}
|
||||||
|
//============================================================================
|
||||||
|
static rt_err_t at9260_nand_read_page(struct rt_mtd_nand_device *device,
|
||||||
|
rt_off_t page,
|
||||||
|
rt_uint8_t *data, rt_uint32_t data_len,
|
||||||
|
rt_uint8_t *spare, rt_uint32_t spare_len)
|
||||||
|
|
||||||
|
{
|
||||||
|
return nand_read_page9260(page / pages_per_block, page % pages_per_block, data, data_len, spare, spare_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int nand_write_page9260(unsigned int block, unsigned int page, unsigned char *data, rt_uint32_t data_len, unsigned char *spare, rt_uint32_t spare_len)
|
||||||
|
{
|
||||||
|
unsigned int blockpage;
|
||||||
|
unsigned char buff2[chip->pagesize];
|
||||||
|
unsigned char se[chip->oobsize];
|
||||||
|
unsigned char ecc_code[eccsize];
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
blockpage = block * (pages_per_block) + page;
|
||||||
|
memset(buff2, 0xff, chip->pagesize);
|
||||||
|
memset(se, 0xff, chip->oobsize);
|
||||||
|
memset(ecc_code, 0xff, eccsize);
|
||||||
|
|
||||||
|
nand_enable_cs();
|
||||||
|
nand_write_cmd(CMD_WRITE_1);
|
||||||
|
|
||||||
|
nand_write_addr(0);
|
||||||
|
if (bufsize == 2112)nand_write_addr(0);
|
||||||
|
nand_write_addr(blockpage & 0xff);
|
||||||
|
nand_write_addr((blockpage >> 8) & 0xff);
|
||||||
|
nand_write_addr((blockpage >> 16) & 0xff);
|
||||||
|
|
||||||
|
at91_nand_udelay(2000);
|
||||||
|
|
||||||
|
for (i = 0; i < data_len; i++)buff2[i] = *(data + i);
|
||||||
|
for (i = 0; i < chip->pagesize; i++)
|
||||||
|
{
|
||||||
|
nand_write_data_byte(buff2[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef RT_USING_DFS_UFFS
|
||||||
|
if (bufsize == 528)
|
||||||
|
{
|
||||||
|
//caclu ECC
|
||||||
|
nand_calculate_ecc(buff2, ecc_code);
|
||||||
|
nand_calculate_ecc(&buff2[256], &ecc_code[3]);
|
||||||
|
//use uboot MTD ECC layout
|
||||||
|
for (i = 0; i < 6; i++)
|
||||||
|
{
|
||||||
|
se[nand_oob_16.eccpos[i]] = ecc_code[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (bufsize == 2112)
|
||||||
|
{
|
||||||
|
//caclu ECC
|
||||||
|
for (i = 0; i < 8; i++)
|
||||||
|
{
|
||||||
|
nand_calculate_ecc(&buff2[256 * i], &(ecc_code[3 * i]));
|
||||||
|
}
|
||||||
|
//use uboot MTD ECC layout
|
||||||
|
for (i = 0; i < 24; i++)
|
||||||
|
{
|
||||||
|
se[nand_oob_64.eccpos[i]] = ecc_code[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
//UFFS do ECC
|
||||||
|
for (i = 0; i < chip->oobsize; i++)
|
||||||
|
{
|
||||||
|
se[i] = *(spare + i);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (i = 0; i < chip->oobsize; i++)
|
||||||
|
{
|
||||||
|
nand_write_data_byte(se[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
nand_write_cmd(CMD_WRITE_2);
|
||||||
|
nand_wait_ready();
|
||||||
|
nand_disable_cs();
|
||||||
|
|
||||||
|
return RT_EOK;
|
||||||
|
}
|
||||||
|
//===========================================================================================
|
||||||
|
static rt_err_t at9260_nand_write_page(struct rt_mtd_nand_device *device,
|
||||||
|
rt_off_t page,
|
||||||
|
rt_uint8_t *data, rt_uint32_t data_len,
|
||||||
|
rt_uint8_t *oob, rt_uint32_t spare_len)
|
||||||
|
{
|
||||||
|
|
||||||
|
return nand_write_page9260(page / pages_per_block, page % pages_per_block, data, data_len, oob, spare_len);
|
||||||
|
}
|
||||||
|
//===========================================================================================
|
||||||
|
static rt_err_t at9260_nand_move_page(struct rt_mtd_nand_device *device, rt_off_t src_page, rt_off_t dst_page)
|
||||||
|
{
|
||||||
|
return RT_EOK;
|
||||||
|
}
|
||||||
|
//===========================================================================================
|
||||||
|
static long at9260_nand_isbad(struct rt_mtd_nand_device *nand, uint32_t blk)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
unsigned char buff2[bufsize];
|
||||||
|
unsigned int blockpage = blk * (chip->blocksize / chip->pagesize) + 0;
|
||||||
|
|
||||||
|
memset(buff2, 0xff, bufsize);
|
||||||
|
|
||||||
|
//read blk page 0
|
||||||
|
nand_enable_cs();
|
||||||
|
nand_write_cmd(CMD_READ_A0);
|
||||||
|
nand_write_addr(0);
|
||||||
|
if (bufsize == 2112)nand_write_addr(0);
|
||||||
|
nand_write_addr(blockpage & 0xff);
|
||||||
|
nand_write_addr((blockpage >> 8) & 0xff);
|
||||||
|
nand_write_addr((blockpage >> 16) & 0xff);
|
||||||
|
|
||||||
|
at91_nand_udelay(2000);
|
||||||
|
|
||||||
|
if (bufsize == 528)nand_write_cmd(CMD_READ_1);
|
||||||
|
else if (bufsize == 2112)nand_write_cmd(CMD_READ_2);
|
||||||
|
|
||||||
|
for (i = 0; i < bufsize; i++)
|
||||||
|
{
|
||||||
|
buff2[i] = nand_read_data_byte();
|
||||||
|
}
|
||||||
|
nand_disable_cs();
|
||||||
|
|
||||||
|
if (bufsize == 528)
|
||||||
|
{
|
||||||
|
if (buff2[5 + 512] != 0xff)
|
||||||
|
{
|
||||||
|
rt_kprintf("\r\n Bad Block=0x%x: Cannot read page #0 of block #%d,addr=0x%x \n\r", buff2[5 + 512], blk, blk * 512 * 32);
|
||||||
|
return buff2[5 + 512];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (bufsize == 2112)
|
||||||
|
{
|
||||||
|
if ((buff2[2048 + 0] != 0xff) && (buff2[2048 + 1] != 0xff))
|
||||||
|
{
|
||||||
|
rt_kprintf("\r\n Bad Block=0x%x: Cannot read page #0 of block #%d,addr=0x%x \n\r", buff2[0 + 2048], blk, blk * 2048 * 64);
|
||||||
|
return buff2[0 + 2048] << 8 | buff2[0 + 2048];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//read blk page 1
|
||||||
|
blockpage = blockpage + 1;
|
||||||
|
memset(buff2, 0xff, bufsize);
|
||||||
|
nand_enable_cs();
|
||||||
|
nand_write_cmd(CMD_READ_A0);
|
||||||
|
nand_write_addr(0);
|
||||||
|
if (bufsize == 2112)nand_write_addr(0);
|
||||||
|
nand_write_addr(blockpage & 0xff);
|
||||||
|
nand_write_addr((blockpage >> 8) & 0xff);
|
||||||
|
nand_write_addr((blockpage >> 16) & 0xff);
|
||||||
|
at91_nand_udelay(2000);
|
||||||
|
|
||||||
|
if (bufsize == 528)nand_write_cmd(CMD_READ_1);
|
||||||
|
else if (bufsize == 2112)nand_write_cmd(CMD_READ_2);
|
||||||
|
|
||||||
|
|
||||||
|
for (i = 0; i < bufsize; i++)
|
||||||
|
{
|
||||||
|
buff2[i] = nand_read_data_byte();
|
||||||
|
}
|
||||||
|
|
||||||
|
nand_disable_cs();
|
||||||
|
|
||||||
|
if (bufsize == 528)
|
||||||
|
{
|
||||||
|
if (buff2[5 + 512] != 0xff)
|
||||||
|
{
|
||||||
|
rt_kprintf("\r\n Bad Block=0x%x: Cannot read page #1 of block #%d,addr=0x%x \n\r", buff2[5 + 512], blk, blk * 512 * 32);
|
||||||
|
return buff2[5 + 512];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (bufsize == 2112)
|
||||||
|
{
|
||||||
|
if ((buff2[2048 + 0] != 0xff) && (buff2[2048 + 1] != 0xff))
|
||||||
|
{
|
||||||
|
rt_kprintf("\r\n Bad Block=0x%x: Cannot read page #1 of block #%d,addr=0x%x \n\r", buff2[0 + 2048], blk, blk * 2048 * 64);
|
||||||
|
return buff2[0 + 2048] << 8 | buff2[0 + 2048];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return GOODBLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long at9260_nand_markbad(struct rt_mtd_nand_device *nand, uint32_t blk)
|
||||||
|
{
|
||||||
|
unsigned int i = 0;
|
||||||
|
unsigned int blockpage = blk * (chip->blocksize / chip->pagesize) + 0;
|
||||||
|
unsigned char bad_flag = 0xff;
|
||||||
|
long ret_bad;
|
||||||
|
|
||||||
|
ret_bad = at9260_nand_isbad(nand, blk);
|
||||||
|
|
||||||
|
if (bufsize == 528)bad_flag = ret_bad & 0xff;
|
||||||
|
else if (bufsize == 2112)
|
||||||
|
{
|
||||||
|
if (bad_flag == 0xff)bad_flag = (ret_bad >> 8) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bad_flag != 0xff)
|
||||||
|
{
|
||||||
|
nand_enable_cs();
|
||||||
|
nand_write_cmd(CMD_WRITE_1);
|
||||||
|
nand_write_addr(0);
|
||||||
|
if (bufsize == 2112)nand_write_addr(0);
|
||||||
|
nand_write_addr(blockpage & 0xff);
|
||||||
|
nand_write_addr((blockpage >> 8) & 0xff);
|
||||||
|
nand_write_addr((blockpage >> 16) & 0xff);
|
||||||
|
at91_nand_udelay(2000);
|
||||||
|
|
||||||
|
for (i = 0; i < bufsize; i++)
|
||||||
|
{
|
||||||
|
nand_write_data_byte(0x00);
|
||||||
|
}
|
||||||
|
|
||||||
|
nand_write_cmd(CMD_WRITE_2);
|
||||||
|
nand_wait_ready();
|
||||||
|
nand_disable_cs();
|
||||||
|
}
|
||||||
|
return RT_EOK;
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------------------------------------------
|
||||||
|
/* erase block */
|
||||||
|
static rt_err_t at9260_nand_erase_block(struct rt_mtd_nand_device *device, rt_uint32_t block)
|
||||||
|
{
|
||||||
|
unsigned int row_address;
|
||||||
|
|
||||||
|
|
||||||
|
//Calculate address used for erase
|
||||||
|
row_address = (block) * (chip->blocksize / chip->pagesize);
|
||||||
|
|
||||||
|
nand_enable_cs();
|
||||||
|
nand_write_cmd(CMD_ERASE_1);
|
||||||
|
nand_write_addr(row_address & 0xff);
|
||||||
|
nand_write_addr((row_address >> 8) & 0xff);
|
||||||
|
nand_write_addr((row_address >> 16) & 0xff);
|
||||||
|
nand_write_cmd(CMD_ERASE_2);
|
||||||
|
|
||||||
|
at91_nand_udelay(2000);
|
||||||
|
nand_wait_ready();
|
||||||
|
nand_disable_cs();
|
||||||
|
return RT_EOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
const static struct rt_mtd_nand_driver_ops _ops =
|
||||||
|
{
|
||||||
|
at9260_nand_read_id,
|
||||||
|
at9260_nand_read_page,
|
||||||
|
at9260_nand_write_page,
|
||||||
|
at9260_nand_move_page,
|
||||||
|
at9260_nand_erase_block,
|
||||||
|
#ifndef RT_USING_DFS_UFFS
|
||||||
|
at9260_nand_isbad,
|
||||||
|
at9260_nand_markbad,
|
||||||
|
#else
|
||||||
|
RT_NULL,
|
||||||
|
RT_NULL,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
void nand_eraseall(void);
|
||||||
|
|
||||||
|
int rt_hw_mtd_nand_init(void)
|
||||||
|
{
|
||||||
|
unsigned int i, reg, index;
|
||||||
|
|
||||||
|
/* Setup Smart Media, first enable the address range of CS3 in HMATRIX user interface */
|
||||||
|
reg = readl(AT91C_BASE_CCFG + CCFG_EBICSA);
|
||||||
|
reg |= AT91C_EBI_CS3A_SM;
|
||||||
|
writel(reg, AT91C_BASE_CCFG + CCFG_EBICSA);
|
||||||
|
/* Configure SMC CS3 */
|
||||||
|
writel((AT91C_SMC_NWESETUP_(1) | AT91C_SMC_NCS_WRSETUP_(0) | AT91C_SMC_NRDSETUP_(1) | AT91C_SMC_NCS_RDSETUP_(0)), AT91C_BASE_SMC + SMC_SETUP3);
|
||||||
|
writel((AT91C_SMC_NWEPULSE_(3) | AT91C_SMC_NCS_WRPULSE_(3) | AT91C_SMC_NRDPULSE_(3) | AT91C_SMC_NCS_RDPULSE_(3)), AT91C_BASE_SMC + SMC_PULSE3);
|
||||||
|
writel((AT91C_SMC_NWECYCLE_(5) | AT91C_SMC_NRDCYCLE_(5)), AT91C_BASE_SMC + SMC_CYCLE3);
|
||||||
|
writel((AT91C_SMC_READMODE | AT91C_SMC_WRITEMODE | (0x0 << 5) | AT91C_SMC_DBW_WIDTH_BITS_8 | AT91_SMC_TDF_(2)), AT91C_BASE_SMC + SMC_CTRL3);
|
||||||
|
/* {"NANDCS", AT91C_PIN_PC(14), 1, PIO_PULLUP, PIO_OUTPUT} */
|
||||||
|
writel((0x01 << 14), 0xfffff800 + 0x0044);
|
||||||
|
writel((0x01 << 14), 0xfffff800 + 0x0060);
|
||||||
|
writel((0x01 << 14), 0xfffff800 + 0x0030);
|
||||||
|
writel((0x01 << 14), 0xfffff800 + 0x0010);
|
||||||
|
writel((0x01 << 14), 0xfffff800 + 0x0000);
|
||||||
|
/* enable PIOC clock */
|
||||||
|
writel(0x01 << 4, 0x10 + AT91C_BASE_PMC);
|
||||||
|
i = at9260_nand_read_id(RT_NULL);
|
||||||
|
index = i;
|
||||||
|
|
||||||
|
chip->pagesize = nand_ids[index].pagesize;
|
||||||
|
chip->blocksize = nand_ids[index].blocksize;
|
||||||
|
chip->oobsize = nand_ids[index].oobsize;
|
||||||
|
chip->buswidth = nand_ids[index].buswidth;
|
||||||
|
chip->numblocks = nand_ids[index].numblocks;
|
||||||
|
|
||||||
|
_partition[0].page_size = chip->pagesize;
|
||||||
|
_partition[1].page_size = chip->pagesize;
|
||||||
|
_partition[0].pages_per_block = chip->blocksize / chip->pagesize;
|
||||||
|
_partition[1].pages_per_block = chip->blocksize / chip->pagesize;
|
||||||
|
_partition[0].oob_size = _partition[1].oob_size = chip->oobsize;
|
||||||
|
_partition[0].oob_free = _partition[1].oob_free = chip->oobsize - (chip->pagesize / 256 * 3); //oob_free = oob_size - ecc_size
|
||||||
|
|
||||||
|
|
||||||
|
_partition[0].block_total = DATA_PART_ADDR / (chip->blocksize);
|
||||||
|
_partition[0].block_start = 0;
|
||||||
|
_partition[0].block_end = DATA_PART_ADDR / (chip->blocksize) - 1;
|
||||||
|
|
||||||
|
_partition[1].block_total = chip->numblocks - _partition[0].block_total;
|
||||||
|
_partition[1].block_start = _partition[0].block_end + 1;
|
||||||
|
_partition[1].block_end = chip->numblocks - 1;
|
||||||
|
|
||||||
|
_partition[0].ops = &_ops;
|
||||||
|
_partition[1].ops = &_ops;
|
||||||
|
|
||||||
|
rt_mtd_nand_register_device("nand0", &_partition[0]);
|
||||||
|
rt_mtd_nand_register_device("nand1", &_partition[1]);
|
||||||
|
|
||||||
|
return RT_EOK;
|
||||||
|
}
|
||||||
|
INIT_DEVICE_EXPORT(rt_hw_mtd_nand_init);
|
||||||
|
|
||||||
|
#if defined(RT_USING_FINSH)
|
||||||
|
#include <finsh.h>
|
||||||
|
void nand_eraseall()
|
||||||
|
{
|
||||||
|
int tmp=0;
|
||||||
|
int index=0;
|
||||||
|
|
||||||
|
|
||||||
|
if (chip->pagesize == 512)
|
||||||
|
tmp = (DATA_PART_ADDR / 0x4000); //0X4000=512*32=PAGER_SIZE*PAGES_PER_BLOCK
|
||||||
|
else if (chip->pagesize == 2048)
|
||||||
|
tmp = (DATA_PART_ADDR / 0x20000); //0X20000=2048*64
|
||||||
|
|
||||||
|
for (index=tmp; index < chip->numblocks; index ++)
|
||||||
|
{
|
||||||
|
at9260_nand_erase_block(RT_NULL, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FINSH_FUNCTION_EXPORT(nand_eraseall, erase all of block in the nand flash);
|
||||||
|
|
||||||
|
#endif //RT_USING_FINSH
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2006-2022, RT-Thread Development Team
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* Change Logs:
|
||||||
|
* Date Author Notes
|
||||||
|
* 2022-01-08 brightsally first version
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ATMEL_NAND_H_
|
||||||
|
#define _ATMEL_NAND_H_
|
||||||
|
|
||||||
|
|
||||||
|
/* NAND flash */
|
||||||
|
#define DATA_PART_ADDR 0x900000 /*nand0=0-9MB;nand1=9MB-END*/
|
||||||
|
|
||||||
|
#define AT91C_BASE_CCFG 0xffffef14
|
||||||
|
#define CCFG_EBICSA 0x08 /* EBI Chip Select Assignement Register */
|
||||||
|
#define AT91C_EBI_CS3A_SM (0x1UL << 3)
|
||||||
|
#define AT91C_BASE_SMC 0xffffec00
|
||||||
|
#define AT91C_BASE_PMC 0xfffffc00
|
||||||
|
#define SMC_SETUP3 0x30 /* Setup Register for CS 3 */
|
||||||
|
#define SMC_PULSE3 0x34 /* Pulse Register for CS 3 */
|
||||||
|
#define SMC_CYCLE3 0x38 /* Cycle Register for CS 3 */
|
||||||
|
#define SMC_CTRL3 0x3C /* Control Register for CS 3 */
|
||||||
|
|
||||||
|
#define AT91C_SMC_NWESETUP_(x) ((x) << 0)
|
||||||
|
#define AT91C_SMC_NCS_WRSETUP_(x) ((x) << 8)
|
||||||
|
#define AT91C_SMC_NRDSETUP_(x) ((x) << 16)
|
||||||
|
#define AT91C_SMC_NCS_RDSETUP_(x) ((x) << 24)
|
||||||
|
#define AT91C_SMC_NWEPULSE_(x) ((x) << 0)
|
||||||
|
#define AT91C_SMC_NCS_WRPULSE_(x) ((x) << 8)
|
||||||
|
#define AT91C_SMC_NRDPULSE_(x) ((x) << 16)
|
||||||
|
#define AT91C_SMC_NCS_RDPULSE_(x) ((x) << 24)
|
||||||
|
|
||||||
|
#define AT91C_SMC_NWECYCLE_(x) ((x) << 0)
|
||||||
|
#define AT91C_SMC_NRDCYCLE_(x) ((x) << 16)
|
||||||
|
|
||||||
|
#define AT91C_SMC_READMODE (0x1UL << 0)
|
||||||
|
#define AT91C_SMC_WRITEMODE (0x1UL << 1)
|
||||||
|
#define AT91C_SMC_NWAITM (0x3UL << 4)
|
||||||
|
#define AT91C_SMC_DBW_WIDTH_BITS_8 (0x0UL << 12)
|
||||||
|
#define AT91C_SMC_DBW_WIDTH_BITS_16 (0x1UL << 12)
|
||||||
|
#define AT91C_SMC_DBW_WIDTH_BITS_32 (0x2UL << 12)
|
||||||
|
#define AT91C_SMC_TDF (0xFUL << 16)
|
||||||
|
#define AT91_SMC_TDF_(x) ((x) << 16)
|
||||||
|
|
||||||
|
#define CMD_STATUS 0x70
|
||||||
|
#define STATUS_READY (0x01 << 6) /* Status code for Ready */
|
||||||
|
#define STATUS_ERROR (0x01 << 0) /* Status code for Error */
|
||||||
|
|
||||||
|
/* Nand flash commands */
|
||||||
|
#define CMD_READID 0x90
|
||||||
|
#define CMD_READ_1 0x00
|
||||||
|
#define CMD_READ_2 0x30
|
||||||
|
|
||||||
|
#define CMD_READ_A0 0x00
|
||||||
|
#define CMD_READ_A1 0x01
|
||||||
|
#define CMD_READ_C 0x50
|
||||||
|
|
||||||
|
#define CMD_WRITE_A 0x00
|
||||||
|
#define CMD_WRITE_C 0x50
|
||||||
|
|
||||||
|
#define CMD_WRITE_1 0x80
|
||||||
|
#define CMD_WRITE_2 0x10
|
||||||
|
|
||||||
|
#define CMD_ERASE_1 0x60
|
||||||
|
#define CMD_ERASE_2 0xD0
|
||||||
|
|
||||||
|
/* read/write/move page */
|
||||||
|
#define CMD_REG 0x40400000
|
||||||
|
#define ADDR_REG 0x40200000
|
||||||
|
#define DATA_REG 0x40000000
|
||||||
|
|
||||||
|
/*Values returned by the CheckBlock() function
|
||||||
|
GOOD = RT_EOK=0
|
||||||
|
BAD = -1
|
||||||
|
*/
|
||||||
|
#define BADBLOCK -RT_ERROR //-1
|
||||||
|
#define GOODBLOCK RT_EOK //0
|
||||||
|
|
||||||
|
struct nand_oobfree
|
||||||
|
{
|
||||||
|
unsigned int offset;
|
||||||
|
unsigned int length;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nand_ecclayout
|
||||||
|
{
|
||||||
|
unsigned int eccbytes;
|
||||||
|
unsigned int eccpos[680];
|
||||||
|
unsigned int oobavail;
|
||||||
|
struct nand_oobfree oobfree[32];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct nand_chip_id
|
||||||
|
{
|
||||||
|
unsigned short chip_id; /* Nand Chip ID */
|
||||||
|
unsigned short numblocks; //0x1000=4096=4K //4K*16K=64M
|
||||||
|
unsigned int blocksize; //0x4000=16K //SECTOR
|
||||||
|
unsigned short pagesize; //0X200=512 //1 BLOCK has pages=16K/512=32
|
||||||
|
unsigned char oobsize; //0X10=16
|
||||||
|
unsigned char buswidth;
|
||||||
|
};
|
||||||
|
static struct nand_chip_id nand_ids[] =
|
||||||
|
{
|
||||||
|
|
||||||
|
/* Samsung 32MB 8Bit SMALL BLOCK*/
|
||||||
|
{0xec75, 0x800, 0x4000, 0x200, 0x10, 0x0}, //32M
|
||||||
|
{0xec35, 0x800, 0x4000, 0x200, 0x10, 0x0}, //32M
|
||||||
|
{0xec36, 0x1000, 0x4000, 0x200, 0x10, 0x0}, //4K*16K=64M
|
||||||
|
/* Samsung 128MB 8bit BIG BLOCK*/
|
||||||
|
{0xeca1, 0x400, 0x20000, 0x800, 0x40, 0x0}, //128M
|
||||||
|
{0,}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int rt_hw_mtd_nand_init(void);
|
||||||
|
#endif
|
Loading…
Reference in New Issue