/** * spinand_erase_block--to erase a page with: * @block_id: the physical block location to erase. * * Description: * The commands used here are 0x06 and 0xd8--indicating an erase * command to erase one block--64 pages * It will first to enable the write enable bit (0x06 command), * and then send the 0xd8 erase command * Poll to wait for the tERS time to complete the tranaction. */ static int spinand_erase_block(struct spi_device *spi_nand, u16 block_id) { int retval; u8 status = 0; retval = spinand_write_enable(spi_nand); if (wait_till_ready(spi_nand)) dev_err(&spi_nand->dev, "wait timedout!!!\n"); retval = spinand_erase_block_erase(spi_nand, block_id); while (1) { retval = spinand_read_status(spi_nand, &status); if (retval < 0) { dev_err(&spi_nand->dev, "error %d reading status register\n", (int) retval); return retval; } if ((status & STATUS_OIP_MASK) == STATUS_READY) { if ((status & STATUS_E_FAIL_MASK) == STATUS_E_FAIL) { dev_err(&spi_nand->dev, "erase error, block %d\n", block_id); return -1; } else break; } } return 0; }
static int spinand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int oob_required, int page) { u8 retval, status; uint8_t *p = buf; int eccsize = chip->ecc.size; int eccsteps = chip->ecc.steps; struct spinand_info *info = (struct spinand_info *)chip->priv; enable_read_hw_ecc = 1; chip->read_buf(mtd, p, eccsize * eccsteps); if (oob_required) chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); while (1) { retval = spinand_read_status(info->spi, &status); if ((status & STATUS_OIP_MASK) == STATUS_READY) { if ((status & STATUS_ECC_MASK) == STATUS_ECC_ERROR) { pr_info("spinand: ECC error\n"); mtd->ecc_stats.failed++; } else if ((status & STATUS_ECC_MASK) == STATUS_ECC_1BIT_CORRECTED) mtd->ecc_stats.corrected++; break; } } return 0; }
static int spinand_wait(struct mtd_info *mtd, struct nand_chip *chip) { struct spinand_info *info = (struct spinand_info *)chip->priv; unsigned long timeo = jiffies; int retval, state = chip->state; u8 status; if (state == FL_ERASING) timeo += (HZ * 400) / 1000; else timeo += (HZ * 20) / 1000; while (time_before(jiffies, timeo)) { retval = spinand_read_status(info->spi, &status); if (retval < 0) { dev_err(&mtd->dev, "error %d reading status register\n", retval); return retval; } if ((status & STATUS_OIP_MASK) == STATUS_READY) return 0; cond_resched(); } return 0; }
/* * spinand_read_page-to read a page with: * @page_id: the physical page number * @offset: the location from 0 to 2111 * @len: number of bytes to read * @rbuf: read buffer to hold @len bytes * * Description: * The read includes two commands to the Nand: 0x13 and 0x03 commands * Poll to read status to wait for tRD time. */ static int spinand_read_page(struct spi_device *spi_nand, u16 page_id, u16 offset, u16 len, u8 *rbuf) { int ret; u8 status = 0; #ifdef CONFIG_MTD_SPINAND_ONDIEECC if (enable_read_hw_ecc) { if (spinand_enable_ecc(spi_nand) < 0) dev_err(&spi_nand->dev, "enable HW ECC failed!"); } #endif ret = spinand_read_page_to_cache(spi_nand, page_id); if (ret < 0) return ret; if (wait_till_ready(spi_nand)) dev_err(&spi_nand->dev, "WAIT timedout!!!\n"); while (1) { ret = spinand_read_status(spi_nand, &status); if (ret < 0) { dev_err(&spi_nand->dev, "err %d read status register\n", ret); return ret; } if ((status & STATUS_OIP_MASK) == STATUS_READY) { if ((status & STATUS_ECC_MASK) == STATUS_ECC_ERROR) { dev_err(&spi_nand->dev, "ecc error, page=%d\n", page_id); return 0; } break; } } ret = spinand_read_from_cache(spi_nand, page_id, offset, len, rbuf); if (ret < 0) { dev_err(&spi_nand->dev, "read from cache failed!!\n"); return ret; } #ifdef CONFIG_MTD_SPINAND_ONDIEECC if (enable_read_hw_ecc) { ret = spinand_disable_ecc(spi_nand); if (ret < 0) { dev_err(&spi_nand->dev, "disable ecc failed!!\n"); return ret; } enable_read_hw_ecc = 0; } #endif return ret; }
static int wait_till_ready(struct spi_device *spi_nand) { unsigned long deadline; int retval; u8 stat = 0; deadline = jiffies + MAX_WAIT_JIFFIES; do { retval = spinand_read_status(spi_nand, &stat); if (retval < 0) return -1; else if (!(stat & 0x1)) break; cond_resched(); } while (!time_after_eq(jiffies, deadline)); if ((stat & 0x1) == 0) return 0; return -1; }
static int spinand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, u8 *buf, int oob_required, int page) { int retval; u8 status; u8 *p = buf; int eccsize = chip->ecc.size; int eccsteps = chip->ecc.steps; struct spinand_info *info = nand_get_controller_data(chip); enable_read_hw_ecc = 1; chip->read_buf(mtd, p, eccsize * eccsteps); if (oob_required) chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); while (1) { retval = spinand_read_status(info->spi, &status); if (retval < 0) { dev_err(&mtd->dev, "error %d reading status register\n", retval); return retval; } if ((status & STATUS_OIP_MASK) == STATUS_READY) { if ((status & STATUS_ECC_MASK) == STATUS_ECC_ERROR) { pr_info("spinand: ECC error\n"); mtd->ecc_stats.failed++; } else if ((status & STATUS_ECC_MASK) == STATUS_ECC_1BIT_CORRECTED) mtd->ecc_stats.corrected++; break; } } return 0; }
/** * spinand_program_page--to write a page with: * @page_id: the physical page location to write the page. * @offset: the location from the cache starting from 0 to 2111 * @len: the number of bytes to write * @wbuf: the buffer to hold the number of bytes * * Description: * The commands used here are 0x06, 0x84, and 0x10--indicating that * the write enable is first sent, the write cache command, and the * write execute command. * Poll to wait for the tPROG time to finish the transaction. */ static int spinand_program_page(struct spi_device *spi_nand, u16 page_id, u16 offset, u16 len, u8 *buf) { int retval; u8 status = 0; uint8_t *wbuf; #ifdef CONFIG_MTD_SPINAND_ONDIEECC unsigned int i, j; enable_read_hw_ecc = 0; wbuf = devm_kzalloc(&spi_nand->dev, CACHE_BUF, GFP_KERNEL); spinand_read_page(spi_nand, page_id, 0, CACHE_BUF, wbuf); for (i = offset, j = 0; i < len; i++, j++) wbuf[i] &= buf[j]; if (enable_hw_ecc) { retval = spinand_enable_ecc(spi_nand); if (retval < 0) { dev_err(&spi_nand->dev, "enable ecc failed!!\n"); return retval; } } #else wbuf = buf; #endif retval = spinand_write_enable(spi_nand); if (retval < 0) { dev_err(&spi_nand->dev, "write enable failed!!\n"); return retval; } if (wait_till_ready(spi_nand)) dev_err(&spi_nand->dev, "wait timedout!!!\n"); retval = spinand_program_data_to_cache(spi_nand, page_id, offset, len, wbuf); if (retval < 0) return retval; retval = spinand_program_execute(spi_nand, page_id); if (retval < 0) return retval; while (1) { retval = spinand_read_status(spi_nand, &status); if (retval < 0) { dev_err(&spi_nand->dev, "error %d reading status register\n", retval); return retval; } if ((status & STATUS_OIP_MASK) == STATUS_READY) { if ((status & STATUS_P_FAIL_MASK) == STATUS_P_FAIL) { dev_err(&spi_nand->dev, "program error, page %d\n", page_id); return -1; } else break; } } #ifdef CONFIG_MTD_SPINAND_ONDIEECC if (enable_hw_ecc) { retval = spinand_disable_ecc(spi_nand); if (retval < 0) { dev_err(&spi_nand->dev, "disable ecc failed!!\n"); return retval; } enable_hw_ecc = 0; } #endif return 0; }