📄 nand_base.c
字号:
}
PLATFORM_IOFLUSH_WAR();
chip->ctrl_write(BCHP_NAND_CMD_START, OP_PROGRAM_PAGE);
//printk("50\n");
// Wait until flash is ready
if (nand_write_is_complete(mtd, &needBBT)) {
if (!needBBT) {
ret = 0;
goto out;
}
else { // Need BBT
printk(KERN_WARNING "%s: Marking bad block @%s\n", __FUNCTION__, __ll_sprintf(NandMsg, offset));
//printk("80 block mark bad\n");
ret = chip->block_markbad(mtd, offset);
ret = -EINVAL;
goto out;
}
}
//Write has timed out or read found bad block. TBD: Find out which is which
printk(KERN_INFO "%s: Timeout\n", __FUNCTION__);
ret = -ETIMEDOUT;
out:
//printk("99\n");
return ret;
}
#endif // CONFIG_EDU SUPPORT
/**
* nand_posted_write_oob - [NAND Interface] Write the spare area
* @param mtd MTD data structure
* @param oobarea Spare area, pass NULL if not interested. Must be able to
* hold mtd->oobsize (16) bytes.
* @param offset offset to write to, and must be 512B aligned
*
*/
static int nand_posted_write_oob(struct mtd_info *mtd,
const unsigned char* oobarea, L_OFF_T offset)
{
struct nand_chip* chip = mtd->priv;
L_OFF_T sliceOffset = __ll_and32(offset, ~ (mtd->eccsize - 1));
uint32_t* p32;
int i, needBBT=0;
if (gdebug > 3 ) {
printk("-->%s, offset=%08x\n", __FUNCTION__, (uint32_t) offset);
print_oobbuf(oobarea, 16);
}
if (unlikely(__ll_isub(sliceOffset, offset))) {
printk(KERN_ERR "%s: offset %08x is not cache aligned\n",
__FUNCTION__, (unsigned int) offset);
}
chip->ctrl_writeAddr(chip, sliceOffset, 0);
#if 0
/* Must write data when NAND_COMPLEX_OOB_WRITE option is set */
if (chip->options & NAND_COMPLEX_OOB_WRITE) {
nand_to_flash_memcpy32(chip, offset, ffchars, mtd->eccsize);
}
#endif
// assert oobarea here
BUG_ON(!oobarea);
p32 = (uint32_t*) oobarea;
for (i = 0; i < 4; i++) {
chip->ctrl_write(BCHP_NAND_SPARE_AREA_WRITE_OFS_0 + i*4, /* THT 11-30-06 */ cpu_to_be32 (p32[i]));
}
PLATFORM_IOFLUSH_WAR();
#if 0
if (chip->options & NAND_COMPLEX_OOB_WRITE) {
printk("****** Workaround, using OP_PROGRAM_PAGE instead of OP_PROGRAM_SPARE_AREA\n");
chip->ctrl_write(BCHP_NAND_CMD_START, OP_PROGRAM_PAGE);
}
else
#endif
{
chip->ctrl_write(BCHP_NAND_CMD_START, OP_PROGRAM_SPARE_AREA);
}
// Wait until flash is ready
if (nand_write_is_complete(mtd, &needBBT)) {
return 0;
}
if (needBBT){
int ret;
printk(KERN_WARNING "%s: Marking bad block @%08x\n", __FUNCTION__, (unsigned int) offset);
ret = chip->block_markbad(mtd, offset);
return -EINVAL;
}
return -ETIMEDOUT;
}
/**
* nand_get_device - [GENERIC] Get chip for selected access
* @param mtd MTD device structure
* @param new_state the state which is requested
*
* Get the device and lock it for exclusive access
*/
static int nand_get_device(struct mtd_info *mtd, int new_state)
{
struct nand_chip * chip = mtd->priv;
if (chip) {
DECLARE_WAITQUEUE(wait, current);
/*
* Grab the lock and see if the device is available
*/
while (1) {
spin_lock(&chip->chip_lock);
if (chip->state == FL_READY) {
chip->state = new_state;
spin_unlock(&chip->chip_lock);
break;
}
if (new_state == FL_PM_SUSPENDED) {
spin_unlock(&chip->chip_lock);
return (chip->state == FL_PM_SUSPENDED) ? 0 : -EAGAIN;
}
set_current_state(TASK_UNINTERRUPTIBLE);
add_wait_queue(&chip->wq, &wait);
spin_unlock(&chip->chip_lock);
schedule();
remove_wait_queue(&chip->wq, &wait);
}
return 0;
}
else
return -EINVAL;
}
static struct nand_chip*
nand_get_device_exclusive(void)
{
struct nand_chip * chip = (struct nand_chip*) get_nand_handle();
struct mtd_info *mtd;
int ret;
mtd = (struct mtd_info*) chip->priv;
if (mtd) {
ret = nand_get_device(mtd, NAND_FL_XIP);
}
else
ret = -1;
if (0 == ret)
return chip;
else
return ((struct nand_chip *) 0);
}
/**
* nand_release_device - [GENERIC] release chip
* @param mtd MTD device structure
*
* Deselect, release chip lock and wake up anyone waiting on the device
*/
static void nand_release_device(struct mtd_info *mtd)
{
struct nand_chip * chip = mtd->priv;
/* Release the chip */
spin_lock(&chip->chip_lock);
chip->state = FL_READY;
wake_up(&chip->wq);
spin_unlock(&chip->chip_lock);
}
/**
* nand_read_page - {REPLACABLE] hardware ecc based page read function
* @mtd: mtd info structure
* @chip: nand chip info structure. The OOB buf is stored here on return
* @buf: buffer to store read data
*
* Not for syndrome calculating ecc controllers which need a special oob layout
*/
static int
nand_read_page(struct mtd_info *mtd,
uint8_t *outp_buf, uint8_t* outp_oob, uint32_t page)
{
struct nand_chip *chip = (struct nand_chip*) mtd->priv;
int winslice;
int dataRead = 0;
int oobRead = 0;
int ret = 0;
L_OFF_T offset = __ll_LeftShift32(page, chip->page_shift);
//if (1/* (int) offset <= 0x2000 /*gdebug > 3 */) {
//printk("-->%s, offset=%08x\n", __FUNCTION__, (uint32_t) offset);}
chip->pagebuf = page;
for (winslice = 0; winslice < chip->eccsteps && ret == 0; winslice++) {
ret = nand_posted_read_cache(mtd, &outp_buf[dataRead],
outp_oob ? &outp_oob[oobRead] : NULL,
__ll_add32(offset, dataRead));
if (ret == NAND_CORRECTABLE_ECC_ERROR) {
(mtd->ecc_stats.corrected)++;
ret = 0;
}
else if (ret < 0) {
printk(KERN_ERR "%s: posted read cache failed at offset=%08lx, ret=%d\n",
__FUNCTION__, (unsigned long) __ll_add32(offset, dataRead), ret);
return ret;
}
dataRead += chip->eccsize;
oobRead += chip->eccOobSize;
}
return ret;
}
/**
* nand_read_page_oob - {REPLACABLE] hardware ecc based page read function
* @mtd: mtd info structure
* @chip: nand chip info structure. The OOB buf is stored in the oob_poi ptr on return
*
* Not for syndrome calculating ecc controllers which need a special oob layout
*/
static int
nand_read_page_oob(struct mtd_info *mtd,
uint8_t* outp_oob, uint32_t page)
{
struct nand_chip *chip = (struct nand_chip*) mtd->priv;
int winslice;
int dataRead = 0;
int oobRead = 0;
int corrected = 0; // Only update stats once per page
int ret = 0;
L_OFF_T offset = __ll_LeftShift32(page, chip->page_shift);
if (gdebug > 3 ) {
printk("-->%s, offset=%08x\n", __FUNCTION__, (uint32_t) offset);}
chip->pagebuf = page;
for (winslice = 0; winslice < chip->eccsteps && ret == 0; winslice++) {
//gdebug=4;
ret = nand_posted_read_oob(mtd, &outp_oob[oobRead],
__ll_add32(offset, dataRead), 1);
//gdebug=0;
if (ret == NAND_CORRECTABLE_ECC_ERROR && !corrected) {
(mtd->ecc_stats.corrected)++;
corrected = 1;
ret = 0;
}
else if (ret < 0) {
printk(KERN_ERR "%s: posted read oob failed at offset=%08lx, ret=%d\n",
__FUNCTION__, (unsigned long) __ll_add32(offset, dataRead), ret);
return ret;
}
dataRead += chip->eccsize;
oobRead += chip->eccOobSize;
}
if (gdebug > 3 ) {
printk("<--%s offset=%08x\n", __FUNCTION__, __ll_low(offset));
print_oobbuf(outp_oob, mtd->oobsize); }
return ret;
}
#ifdef CONFIG_MTD_NAND_CORRECTABLE_ERR_HANDLING
static int nand_refresh_blk(struct mtd_info *mtd, loff_t from)
{
struct nand_chip *this = mtd->priv;
int i, j, k, numpages, ret, count = 0, nonecccount = 0;
uint8_t *blk_buf; /* Store one block of data (including OOB) */
unsigned int realpage, pg_idx, oob_idx;
struct erase_info *instr;
//int gdebug = 1;
struct nand_ecclayout *oobinfo;
uint8_t *oobptr;
uint32_t *oobptr32, blkbegin;
unsigned int block_size;
#if CONFIG_MTD_NAND_VERSION >= CONFIG_MTD_NAND_VERS_1_0
this->ctrl_write(BCHP_NAND_ECC_CORR_EXT_ADDR, 0);
#endif
this->ctrl_write(BCHP_NAND_ECC_CORR_ADDR, 0);
DEBUG(MTD_DEBUG_LEVEL3, "Inside %s:\n", __FUNCTION__, __ll_low(from));
printk(KERN_INFO "%s: Performing block refresh to single bit ECC error\n", __FUNCTION__);
pg_idx = 0;
oob_idx = mtd->writesize;
numpages = mtd->erasesize/mtd->writesize;
block_size = (1 << this->erase_shift);
blkbegin = (uint32_t) (from & (~(mtd->erasesize-1)));
realpage = (unsigned int)(blkbegin >> this->page_shift);
blk_buf = (uint8_t *) NAND_malloc(numpages*(mtd->writesize + mtd->oobsize));
if (unlikely(blk_buf == NULL)) {
printk(KERN_ERR "%s: buffer allocation failed\n", __FUNCTION__);
return -1;
}
memset(blk_buf, 0xff, numpages*(mtd->writesize + mtd->oobsize));
if (unlikely(gdebug > 0)) {
printk("---> %s: from = %08x, numpages = %d, realpage = %x\n",\
__FUNCTION__, (uint32_t) from, numpages, realpage);
printk(" Locking flash for read ... \n");
}
/* Read an entire block */
nand_get_device(mtd, FL_READING);
for (i = 0; i < numpages; i++) {
ret = nand_read_page(mtd, blk_buf+pg_idx, blk_buf+oob_idx, realpage);
if (ret < 0) {
NAND_free(blk_buf);
nand_release_device(mtd);
return -1;
}
//printk("DEBUG -> Reading %d realpage = %x %x ret = %d oob = %x\n", i, realpage, *(blk_buf+pg_idx), ret, *(blk_buf + oob_idx));
//print_oobbuf(blk_buf+oob_idx, mtd->oobsize);
pg_idx += mtd->writesize + mtd->oobsize;
oob_idx += mtd->oobsize + mtd->writesize;
realpage++;
}
if (unlikely(gdebug > 0)) {
printk("---> %s: Read -> erase\n", __FUNCTION__);
}
this->state = FL_ERASING;
/* Erase the block */
instr = kmalloc(sizeof(struct erase_info), GFP_KERNEL);
if (instr == NULL) {
printk(KERN_WARNING "kmalloc for erase_info failed\n");
NAND_free(blk_buf);
nand_release_device(mtd);
return -ENOMEM;
}
memset(instr, 0, sizeof(struct erase_info));
instr->mtd = mtd;
instr->addr = blkbegin;
instr->len = mtd->erasesize;
if (unlikely(gdebug > 0)) {
printk("DEBUG -> erasing %s, %x %d\n", __ll_sprintf(NandMsg, instr->addr), instr->len, this->state);
}
ret = nand_erase_nolock(mtd, instr, 0);
if (ret) {
NAND_free(blk_buf);
kfree(instr);
nand_release_device(mtd);
printk(KERN_WARNING " %s Erase failed %d\n", __FUNCTION__, ret);
return ret;
}
kfree(instr);
/* Write the entire block */
pg_idx = 0;
oob_idx = mtd->writesize;
realpage = (unsigned int)(blkbegin >> this->page_shift);
if (unlikely(gdebug > 0)) {
printk("---> %s: Erase -> write ... %d\n", __FUNCTION__, this->state);
}
oobinfo = this->ecclayout;
this->state = FL_WRITING;
for (i = 0; i < numpages; i++) {
/* Avoid writing empty pages */
count = 0;
nonecccount = 0;
oobptr = (uint8_t *) (blk_buf + oob_idx);
oobptr32 = (uint32_t *) (blk_buf + oob_idx);
for (j = 0; j < oobinfo->eccbytes; j++) {
if (oobptr[oobinfo->eccpos[j]] == 0xff) { count++; }
}
for (k = 0; k < mtd->oobsize/4; k++) {
if (oobptr32[k] == 0xffffffff) { nonecccount++; }
}
/* Skip this page if ECC is 0xff */
if (count == j && nonecccount == k) {
pg_idx += mtd->writesize + mtd->oobsize;
oob_idx += mtd->oobsize + mtd->writesize;
realpage++;
continue;
}
/* Skip this page, but write the OOB */
if (count == j && nonecccount != k) {
ret = this->write_page_oob(mtd, blk_buf + oob_idx, realpage);
if (ret) {
NAND_free(blk_buf);
nand_release_device(mtd);
return ret;
}
pg_idx += mtd->writesize + mtd->oobsize;
oob_idx += mtd->oobsize + mtd->writesize;
realpage++;
continue;
}
for (j = 0; j < oobinfo->eccbytes; j++) {
oobptr[oobinfo->eccpos[j]] = 0xff;
}
ret = this->write_page(mtd, blk_buf+pg_idx, blk_buf+oob_idx, realpage);
if (ret) {
NAND_free(blk_buf);
nand_release_device(mtd);
return ret;
}
pg_idx += mtd->writesize + mtd->oobsize;
oob_idx += mtd->oobsize + mtd->writesize;
realpage++;
}
nand_release_device(mtd);
NAND_free(blk_buf);
printk(KERN_INFO "%s: block refresh succes\n", __FUNCTION__);
return 0;
}
#endif
/**
* nand_do_read_ops - [Internal] Read data with ECC
*
* @mtd: MTD device structure
* @from: offset to read from
* @ops: oob ops structure
* @raw: read raw data format when TRUE
*
* Internal function. Called with chip held.
*/
static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -