📄 nand.c
字号:
retry: /* Grab the lock and see if the device is available */ spin_lock_bh (&this->chip_lock); switch (this->state) { case FL_READY: this->state = FL_WRITING; spin_unlock_bh (&this->chip_lock); break; default: set_current_state (TASK_UNINTERRUPTIBLE); add_wait_queue (&this->wq, &wait); spin_unlock_bh (&this->chip_lock); schedule(); remove_wait_queue (&this->wq, &wait); goto retry; }; /* Shift to get page */ page = ((int) to) >> this->page_shift; /* Get the starting column */ col = to & (mtd->oobblock - 1); /* Initialize return length value */ *retlen = 0; /* Select the NAND device */ nand_select (); /* Check the WP bit */ nand_command (mtd, NAND_CMD_STATUS, -1, -1); if (!(readb (this->IO_ADDR) & 0x80)) { DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Device is write protected!!!\n"); nand_deselect (); spin_lock_bh (&this->chip_lock); this->state = FL_READY; wake_up (&this->wq); spin_unlock_bh (&this->chip_lock); return -EIO; } /* Loop until all data is written */ while (*retlen < len) { /* Write data into buffer */ if ((col + len) >= mtd->oobblock) for(i=col, cnt=0 ; i < mtd->oobblock ; i++, cnt++) this->data_buf[i] = buf[(*retlen + cnt)]; else for(i=col, cnt=0 ; cnt < (len - *retlen) ; i++, cnt++) this->data_buf[i] = buf[(*retlen + cnt)]; #ifdef CONFIG_MTD_NAND_ECC /* Zero out the ECC array */ for (i=0 ; i < 6 ; i++) ecc_code[i] = 0x00; /* Calculate and write the ECC if we have enough data */ if ((col < mtd->eccsize) && ((col + (len - *retlen)) >= mtd->eccsize)) { nand_command (mtd, NAND_CMD_READ0, col, page); for (i=0 ; i < col ; i++) this->data_buf[i] = readb (this->IO_ADDR); nand_calculate_ecc (&this->data_buf[0], &ecc_code[0]); for (i=0 ; i<3 ; i++) this->data_buf[(mtd->oobblock + i)] = ecc_code[i]; } /* Calculate and write the second ECC if we have enough data */ if ((mtd->oobblock == 512) && ((col + (len - *retlen)) >= mtd->oobblock)) { nand_calculate_ecc (&this->data_buf[256], &ecc_code[3]); for (i=3 ; i<6 ; i++) this->data_buf[(mtd->oobblock + i)] = ecc_code[i]; } /* Write ones for partial page programming */ for (i=ecc_bytes ; i < mtd->oobsize ; i++) this->data_buf[(mtd->oobblock + i)] = 0xff;#else /* Write ones for partial page programming */ for (i=mtd->oobblock ; i < (mtd->oobblock + mtd->oobsize) ; i++) this->data_buf[i] = 0xff;#endif /* Write pre-padding bytes into buffer */ for (i=0 ; i < col ; i++) this->data_buf[i] = 0xff; /* Write post-padding bytes into buffer */ if ((col + (len - *retlen)) < mtd->oobblock) { for(i=(col + cnt) ; i < mtd->oobblock ; i++) this->data_buf[i] = 0xff; } /* Send command to begin auto page programming */ nand_command (mtd, NAND_CMD_SEQIN, 0x00, page); /* Write out complete page of data */ for (i=0 ; i < (mtd->oobblock + mtd->oobsize) ; i++) writeb (this->data_buf[i], this->IO_ADDR); /* Send command to actually program the data */ nand_command (mtd, NAND_CMD_PAGEPROG, -1, -1); /* * 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); status = (int) readb (this->IO_ADDR); if (status & 0x40) break; } /* See if device thinks it succeeded */ if (status & 0x01) { DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: " \ "Failed write, page 0x%08x, " \ "%6i bytes were succesful\n", page, *retlen); nand_deselect (); spin_lock_bh (&this->chip_lock); this->state = FL_READY; wake_up (&this->wq); spin_unlock_bh (&this->chip_lock); return -EIO; }#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); /* Loop through and verify the data */ for (i=col ; i < cnt ; i++) { if (this->data_buf[i] != readb (this->IO_ADDR)) { DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: " \ "Failed write verify, page 0x%08x, " \ "%6i bytes were succesful\n", page, *retlen); nand_deselect (); spin_lock_bh (&this->chip_lock); this->state = FL_READY; wake_up (&this->wq); spin_unlock_bh (&this->chip_lock); return -EIO; } }#ifdef CONFIG_MTD_NAND_ECC /* * We also want to check that the ECC bytes wrote * correctly for the same reasons stated above. */ nand_command (mtd, NAND_CMD_READOOB, 0x00, page); for (i=0 ; i < ecc_bytes ; i++) { if ((readb (this->IO_ADDR) != ecc_code[i]) && ecc_code[i]) { DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Failed ECC write " \ "verify, page 0x%08x, " \ "%6i bytes were succesful\n", page, i); nand_deselect (); spin_lock_bh (&this->chip_lock); this->state = FL_READY; wake_up (&this->wq); spin_unlock_bh (&this->chip_lock); return -EIO; } }#endif#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++; } /* De-select the NAND device */ nand_deselect (); /* Wake up anyone waiting on the device */ spin_lock_bh (&this->chip_lock); this->state = FL_READY; wake_up (&this->wq); spin_unlock_bh (&this->chip_lock); /* Return happy */ *retlen = len; return 0;}/* * 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, column, page, status; struct nand_chip *this = mtd->priv; DECLARE_WAITQUEUE(wait, current); DEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: 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 */ column = to & 0x1f; /* Initialize return length value */ *retlen = 0; /* Do not allow write past end of page */ if ((column + len) > mtd->oobsize) { DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Attempt to write past end of page\n"); return -EINVAL; }retry: /* Grab the lock and see if the device is available */ spin_lock_bh (&this->chip_lock); switch (this->state) { case FL_READY: this->state = FL_WRITING; spin_unlock_bh (&this->chip_lock); break; default: set_current_state (TASK_UNINTERRUPTIBLE); add_wait_queue (&this->wq, &wait); spin_unlock_bh (&this->chip_lock); schedule(); remove_wait_queue (&this->wq, &wait); goto retry; }; /* Select the NAND device */ nand_select (); /* Check the WP bit */ nand_command (mtd, NAND_CMD_STATUS, -1, -1); if (!(readb (this->IO_ADDR) & 0x80)) { DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Device is write protected!!!\n"); nand_deselect (); spin_lock_bh (&this->chip_lock); this->state = FL_READY; wake_up (&this->wq); spin_unlock_bh (&this->chip_lock); return -EIO; } /* Write out desired data */ nand_command (mtd, NAND_CMD_SEQIN, column + 512, page); for (i=0 ; i<len ; i++) writeb (buf[i], this->IO_ADDR); /* Send command to program the OOB data */ nand_command (mtd, NAND_CMD_PAGEPROG, -1, -1); /* * 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); status = (int) readb (this->IO_ADDR); if (status & 0x40) break; } /* See if device thinks it succeeded */ if (status & 0x01) { DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " \ "Failed write, page 0x%08x\n", page); nand_deselect (); spin_lock_bh (&this->chip_lock); this->state = FL_READY; wake_up (&this->wq); spin_unlock_bh (&this->chip_lock); return -EIO; }#ifdef CONFIG_MTD_NAND_VERIFY_WRITE /* Send command to read back the data */ nand_command (mtd, NAND_CMD_READOOB, column, page); /* Loop through and verify the data */ for (i=0 ; i<len ; i++) { if (buf[i] != readb (this->IO_ADDR)) { DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " \ "Failed write verify, page 0x%08x\n", page); nand_deselect (); spin_lock_bh (&this->chip_lock); this->state = FL_READY; wake_up (&this->wq); spin_unlock_bh (&this->chip_lock); return -EIO; } }#endif /* De-select the NAND device */ nand_deselect (); /* Wake up anyone waiting on the device */ spin_lock_bh (&this->chip_lock); this->state = FL_READY; wake_up (&this->wq); spin_unlock_bh (&this->chip_lock); /* Return happy */ *retlen = len; return 0;}/* * NAND write with iovec */static int nand_writev (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen){ int i, page, col, cnt, len, total_len, status; struct nand_chip *this = mtd->priv; DECLARE_WAITQUEUE(wait, current);#ifdef CONFIG_MTD_NAND_ECC int ecc_bytes = (mtd->oobblock == 512) ? 6 : 3;#endif /* Calculate total length of data */ total_len = 0; for (i=0 ; i < count ; i++) total_len += (int) vecs[i].iov_len; DEBUG (MTD_DEBUG_LEVEL3, "nand_writev: to = 0x%08x, len = %i\n", (unsigned int) to, (unsigned int) total_len); /* Do not allow write past end of page */ if ((to + total_len) > mtd->size) { DEBUG (MTD_DEBUG_LEVEL0, "nand_writev: Attempted write past end of device\n"); return -EINVAL; }retry: /* Grab the lock and see if the device is available */ spin_lock_bh (&this->chip_lock); switch (this->state) { case FL_READY: this->state = FL_WRITING; spin_unlock_bh (&this->chip_lock); break; default: set_current_state (TASK_UNINTERRUPTIBLE); add_wait_queue (&this->wq, &wait); spin_unlock_bh (&this->chip_lock); schedule(); remove_wait_queue (&this->wq, &wait); goto retry; }; /* Shift to get page */ page = ((int) to) >> this->page_shift; /* Get the starting column */ col = to & (mtd->oobblock - 1); /* Initialize return length value */ *retlen = 0; /* Select the NAND device */ nand_select (); /* Check the WP bit */ nand_command (mtd, NAND_CMD_STATUS, -1, -1); if (!(readb (this->IO_ADDR) & 0x80)) { DEBUG (MTD_DEBUG_LEVEL0, "nand_writev: Device is write protected!!!\n"); nand_deselect (); spin_lock_bh (&this->chip_lock); this->state = FL_READY; wake_up (&this->wq); spin_unlock_bh (&this->chip_lock); return -EIO; } /* Loop until all iovecs' data has been written */ cnt = col; len = 0; while (count) { /* Do any need pre-fill for partial page programming */ for (i=0 ; i < cnt ; i++) this->data_buf[i] = 0xff; /* * Read data out of each tuple until we have a full page * to write or we've read all the tuples. */ while ((cnt < mtd->oobblock) && count) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -