📄 smc_core.c
字号:
#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
/*
* The NAND device assumes that it is always writing to
* a cleanly erased page. Hence, it performs its internal
* write verification only on bits that transitioned from
* 1 to 0. The device does NOT verify the whole page on
* a byte by byte basis. It is possible that the page was
* not completely erased or the page is becoming unusable
* due to wear. The read with ECC would catch the error
* later when the ECC page check fails, but we would rather
* catch it early in the page write stage. Better to write
* no data than invalid data.
*/
/* Send command to read back the page */
if (col < mtd->eccsize)
nand_command(mtd, NAND_CMD_READ0, col, page);
else
nand_command(mtd, NAND_CMD_READ1, col - 256, page);
this->wait_for_ready();
/* Loop through and verify the data */
for (i = col; i < cnt; i++) {
if (this->data_buf[i] != this->read_data()) {
DEBUG(MTD_DEBUG_LEVEL0,
__FUNCTION__"(): Failed write verify, " \
"page 0x%08x, %6i bytes were succesful\n",
page, *retlen);
i = -EIO;
goto nand_write_exit;
}
}
#endif
/*
* If we are writing a large amount of data and/or it
* crosses page or half-page boundaries, we set the
* the column to zero. It simplifies the program logic.
*/
if (col)
col = 0x00;
/* Update written bytes count */
*retlen += cnt;
/* Increment page address */
page++;
}
/* Return happy */
*retlen = len;
i = 0;
nand_write_exit:
/* De-select the NAND device */
nand_deselect();
return i;
}
/*
* NAND write witdh ECC, but only 1 sector!
*/
static int
nand_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf, u_char *ecc_code)
{
int i, page, cnt, status, ret;
struct nand_chip *this = mtd->priv;
unsigned int sector_size, page_size, oob_size;
DEBUG(MTD_DEBUG_LEVEL3,
__FUNCTION__"(): to = 0x%08x, len = %i\n", (unsigned int)to,
(int)len);
sector_size = this->dev->szS;
page_size = mtd->oobblock;
oob_size = mtd->oobsize;
if (to & (sector_size - 1)) {
DEBUG(MTD_DEBUG_LEVEL0,
__FUNCTION__"(): Not Sector aligned\n");
return -EINVAL;
}
if (len != sector_size) {
DEBUG(MTD_DEBUG_LEVEL0,
__FUNCTION__"(): Only 1 Sector\n");
return -EINVAL;
}
/* Do not allow write past end of page */
if ((to + len) > mtd->size) {
DEBUG(MTD_DEBUG_LEVEL0,
__FUNCTION__"(): Attempted write past end of device\n");
return -EINVAL;
}
/* Shift to get page */
page = ((int) to) >> this->page_shift;
/* Initialize return length value */
*retlen = 0;
/* Select the NAND device */
nand_select();
/* Check the WP bit */
nand_command(mtd, NAND_CMD_STATUS, -1, -1);
this->wait_for_ready();
if (!(this->read_data() & SMC_STAT_NOT_WP)) {
DEBUG(MTD_DEBUG_LEVEL0,
__FUNCTION__"(): Device is write protected!!!\n");
ret = -EPERM;
goto nand_write_err;
}
/* Loop until all data is written */
while (*retlen < len) {
/* Send command to begin auto page programming */
nand_command(mtd, NAND_CMD_SEQIN, 0x00, page);
this->hwcontrol(NAND_CTL_DAT_OUT);
/* Write out complete page of data */
for (i = 0, cnt = 0; i < page_size; i++, cnt++)
this->write_data(buf[(*retlen) + cnt]);
/* Write ones for partial page programming */
for (i = 0; i < oob_size; i++) {
#ifdef USE_256BYTE_NAND_FLASH
if (*retlen & (sector_size -1))
this->write_data(ecc_code[SMC_OOB256_SIZE + i]);
else
#endif
this->write_data(ecc_code[i]);
}
this->hwcontrol(NAND_CTL_DAT_IN);
/* Send command to actually program the data */
nand_command(mtd, NAND_CMD_PAGEPROG, -1, -1);
this->wait_for_ready();
/*
* Wait for program operation to complete. This could
* take up to 3000us (3ms) on some devices, so we try
* and exit as quickly as possible
*/
status = 0;
for (i = 0; i < 25; i++) {
/* Delay for 125us */
udelay(125);
/* Check the status */
nand_command(mtd, NAND_CMD_STATUS, -1, -1);
status = (int)this->read_data();
if (status & SMC_STAT_READY)
break;
}
/* See if device thins it succeeded */
if (status & SMC_STAT_WRITE_ERR) {
DEBUG(MTD_DEBUG_LEVEL0,
__FUNCTION__"(): Failed write, page 0x%08x, " \
"%6i bytes were succesful\n", page, *retlen);
ret = -EIO;
goto nand_write_err;
}
#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
/*
* The NAND device assumes that it is always writing to
* a cleanly erased page. Hence, it performs its internal
* write verification only on bits tha transitioned from
* 1 to 0. The device does NOT verify the whole page on a
* byte by byte basis. It is possible that the page was
* not completely erased or the page is becoming unusable
* due to wear. The read with ECC would catch the error
* later when the ECC page check fails, but we would rather
* catch it early in the page write stage. Better to write
* no data than invalid data.
*/
/* Send command to read back the page */
#ifdef USE_256BYTE_NAND_FLASH
if (*retlen & (sector_size - 1))
nand_command(mtd, NAND_CMD_READ0, 0x00, page + 1);
else
#endif
nand_command(mtd, NAND_CMD_READ0, 0x00, page);
this->wait_for_ready();
/* Loop through and verify the data */
for (i = 0; i < page_size; i++) {
if (this->data_buf[i] != this->read_data()) {
DEBUG(MTD_DEBUG_LEVEL0,
__FUNCTION__"(): Failed write verify, " \
"page 0x%08x, %6i bytes were succesful\n",
page, *retlen);
ret = -EIO;
goto nand_write_err;
}
}
#endif
/* Update written bytes count */
*retlen += cnt;
/* Increment page address */
page++;
}
*retlen = len;
ret = 0;
nand_write_err:
/* De-select the NAND device */
nand_deselect();
return ret;
}
/*
* NAND write out-of-band
*/
static int
nand_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
int i, offset, page, status, ret;
struct nand_chip *this = mtd->priv;
DEBUG(MTD_DEBUG_LEVEL3,
__FUNCTION__"(): to = 0x%08x, len = %i\n", (unsigned int)to,
(int)len);
/* Shift to get page */
page = ((int)to) >> this->page_shift;
/* Mask to get column */
offset = to & 0x1f;
/* Initialize return length value */
*retlen = 0;
/* Do not allow write past end of page */
if ((offset + len) > mtd->oobsize) {
DEBUG(MTD_DEBUG_LEVEL0,
__FUNCTION__"(): Attempt to write past end of page\n");
return -EINVAL;
}
/* Select the NAND device */
nand_select();
/* Check the WP bit */
nand_command(mtd, NAND_CMD_STATUS, -1, -1);
this->wait_for_ready();
if (!(this->read_data() & SMC_STAT_NOT_WP)) {
DEBUG(MTD_DEBUG_LEVEL0,
__FUNCTION__"(): Device is write protected!!!\n");
ret = -EPERM;
goto nand_write_oob_err;
}
/* Write out desired data */
nand_command(mtd, NAND_CMD_SEQIN, offset + mtd->oobblock, page);
this->hwcontrol(NAND_CTL_DAT_OUT);
for (i = 0; i < len; i++)
this->write_data(buf[i]);
this->hwcontrol(NAND_CTL_DAT_IN);
/* Send command to program the OOB data */
nand_command(mtd, NAND_CMD_PAGEPROG, -1, -1);
this->wait_for_ready();
/*
* Wait for program operation to complete. This could
* take up to 3000us (3ms) on some devices, so we try
* and exit as quickly as possible.
*/
status = 0;
for (i = 0; i < 24; i++) {
/* Delay for 125us */
udelay(125);
/* Check the status */
nand_command(mtd, NAND_CMD_STATUS, -1, -1);
this->wait_for_ready();
status = (int)this->read_data();
if (status & SMC_STAT_READY)
break;
}
/* See if device thinks it succeeded */
if (status & SMC_STAT_WRITE_ERR) {
DEBUG(MTD_DEBUG_LEVEL0,
__FUNCTION__"(): Failed write, page 0x%08x\n", page);
ret = -EIO;
goto nand_write_oob_err;
}
#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
/* Send command to read back the data */
nand_command(mtd, NAND_CMD_READOOB, offset, page);
this->wait_for_ready();
/* Loop through and verify the data */
for (i = 0; i < len; i++) {
if (buf[i] != this->read_data()) {
DEBUG(MTD_DEBUG_LEVEL0,
__FUNCTION__"(): Failed write verify, page 0x%08x\n",
page);
ret = -EIO;
goto nand_write_oob_err;
}
}
#endif
/* Return happy */
*retlen = len;
ret = 0;
nand_write_oob_err:
/* De-select the NAND device */
nand_deselect();
return ret;
}
/*
* NAND erase a block
*/
static int
nand_erase(struct mtd_info *mtd, struct erase_info *instr)
{
int i, page, len, status, pages_per_block;
struct nand_chip *this = mtd->priv;
DEBUG(MTD_DEBUG_LEVEL3,
__FUNCTION__"(): start = 0x%08x, len = %i\n",
(unsigned int)instr->addr, (unsigned int)instr->len);
/* Start address must aligned on block boundary */
if (instr->addr & (mtd->erasesize - 1)) {
DEBUG(MTD_DEBUG_LEVEL0,
__FUNCTION__"(): Unaligned address\n");
return -EINVAL;
}
/* Length must align on block boundary */
if (instr->len & (mtd->erasesize - 1)) {
DEBUG(MTD_DEBUG_LEVEL0,
__FUNCTION__"(): Length not block aligned\n");
return -EINVAL;
}
/* Do not allow erase past end of device */
if ((instr->len + instr->addr) > mtd->size) {
DEBUG(MTD_DEBUG_LEVEL0,
__FUNCTION__"(): Erase past end of device\n");
return -EINVAL;
}
/* Shift to get first page */
page = (int)(instr->addr >> this->page_shift);
/* Calculate pages in each block */
pages_per_block = mtd->erasesize / mtd->oobblock;
/* Select the NAND device */
nand_select();
/* Check the WP bit */
nand_command(mtd, NAND_CMD_STATUS, -1, -1);
this->wait_for_ready();
if (!(this->read_data() & SMC_STAT_NOT_WP)) {
DEBUG(MTD_DEBUG_LEVEL0,
__FUNCTION__"(): Device is write protected!!!\n");
nand_deselect();
return -EIO;
}
/* Loop through the pages */
len = instr->len;
while (len) {
/* Send commands to erase a page */
nand_command(mtd, NAND_CMD_ERASE1, -1, page);
nand_command(mtd, NAND_CMD_ERASE2, -1, -1);
this->wait_for_ready();
/*
* Wait for program operation to complete. This could
* take up to 4000us (4ms) on some devices, so we try
* and exit as quickly as possible.
*/
status = 0;
for (i = 0; i < 32; i++) {
/* Delay for 125us */
udelay(125);
/* Check the status */
nand_command(mtd, NAND_CMD_STATUS, -1, -1);
this->wait_for_ready();
status = (int)this->read_data();
if (status & SMC_STAT_READY)
break;
}
/* See if block erase succeeded */
if (status & SMC_STAT_WRITE_ERR) {
DEBUG(MTD_DEBUG_LEVEL0,
__FUNCTION__"(): Failed erase, page 0x%08x\n", page);
nand_deselect();
#if 0
instr->state = MTD_ERASE_FAILED;
if (instr->callback)
instr->callback(instr);
#endif
return -EIO;
}
/* Increment page address and decrement length */
len -= mtd->erasesize;
page += pages_per_block;
}
/* De-select the NAND device */
nand_deselect();
#if 0
/* Do call back function */
instr->state = MTD_ERASE_DONE;
if (instr->callback)
instr->callback(instr);
#endif
/* Return happy */
return 0;
}
/*
* Scan for the SMC device
*/
int
smc_scan(struct mtd_info *mtd)
{
int i, nand_maf_id, nand_dev_id;
struct nand_chip *this = mtd->priv;
/* Select the device */
nand_select();
/* Send the command for reading device ID */
nand_command(mtd, NAND_CMD_READID, 0x00, -1);
this->wait_for_ready();
/* Read manufacturer and device IDs */
nand_maf_id = this->read_data();
nand_dev_id = this->read_data();
/* Print and sotre flash device information */
for (i = 0; nand_flash_ids[i].name != NULL; i++) {
if (nand_maf_id == nand_flash_ids[i].manufacture_id &&
nand_dev_id == nand_flash_ids[i].model_id) {
#ifdef USE_256BYTE_NAND_FLASH
if (!mtd->size) {
mtd->name = nand_flash_ids[i].name;
mtd->erasesize = nand_flash_ids[i].erasesize;
mtd->size = (1 << nand_flash_ids[i].chipshift);
mtd->eccsize = 256;
if (nand_flash_ids[i].page256) {
mtd->oobblock = 256;
mtd->oobsize = 8;
this->page_shift = 8;
} else {
mtd->oobblock = 512;
mtd->oobsize = 16;
this->page_shift = 9;
}
this->dev = &nand_smc_info[GET_DI_NUM(nand_flash_ids[i].chipshift)];
}
#else
if (!(mtd->size) && !(nand_flash_ids[i].page256)) {
mtd->name = nand_flash_ids[i].name;
mtd->erasesize = nand_flash_ids[i].erasesize;
mtd->size = (1 << nand_flash_ids[i].chipshift);
mtd->eccsize = 256;
mtd->oobblock = 512;
mtd->oobsize = 16;
this->page_shift = 9;
this->dev = &nand_smc_info[GET_DI_NUM(nand_flash_ids[i].chipshift)];
}
#endif
printk("NAND device: Manufacture ID:" \
" 0x%02x, Chip ID: 0x%02x (%s)\n",
nand_maf_id, nand_dev_id, mtd->name);
break;
}
}
/* De-select the device */
nand_deselect();
/* Print warning message for no device */
if (!mtd->size) {
printk("No NAND device found!!!\n");
return 1;
}
/* Fill in remaining MTD driver data */
mtd->type = MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC;
mtd->module = NULL;
mtd->ecctype = MTD_ECC_SW;
mtd->erase = nand_erase;
mtd->point = NULL;
mtd->unpoint = NULL;
mtd->read = nand_read;
mtd->write = nand_write;
mtd->read_ecc = nand_read_ecc;
mtd->write_ecc = nand_write_ecc;
mtd->read_oob = nand_read_oob;
mtd->write_oob = nand_write_oob;
mtd->lock = NULL;
mtd->unlock = NULL;
/* Return happy */
return 0;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -