/* * NFC Page Data Layout: * 1024 bytes data + 4Bytes sys data + 28Bytes~124Bytes ECC data + * 1024 bytes data + 4Bytes sys data + 28Bytes~124Bytes ECC data + * ...... * NAND Page Data Layout: * 1024 * n data + m Bytes oob * Original Bad Block Mask Location: * First byte of oob(spare). * nand_chip->oob_poi data layout: * 4Bytes sys data + .... + 4Bytes sys data + ECC data.
*/
/** * struct rk_ecc_cnt_status: represent a ecc status data. * @err_flag_bit: error flag bit index at register. * @low: ECC count low bit index at register. * @low_mask: mask bit. * @low_bn: ECC count low bit number. * @high: ECC count high bit index at register. * @high_mask: mask bit
*/ struct rk_ecc_cnt_status {
u8 err_flag_bit;
u8 low;
u8 low_mask;
u8 low_bn;
u8 high;
u8 high_mask;
};
val = readl_relaxed(nfc->regs + NFC_FMCTL);
val &= ~FMCTL_CE_SEL_M;
val |= FMCTL_CE_SEL(nfc->selected_bank);
writel(val, nfc->regs + NFC_FMCTL);
/* * Compare current chip timing with selected chip timing and * change if needed.
*/ if (nfc->cur_timing != rknand->timing) {
writel(rknand->timing, nfc->regs + NFC_FMWAIT);
nfc->cur_timing = rknand->timing;
}
/* * Compare current chip ECC setting with selected chip ECC setting and * change if needed.
*/ if (nfc->cur_ecc != ecc->strength)
rk_nfc_hw_ecc_setup(chip, ecc->strength);
}
staticinlineint rk_nfc_wait_ioready(struct rk_nfc *nfc)
{ int rc;
u32 val;
/* * ACCON: access timing control register * ------------------------------------- * 31:18: reserved * 17:12: csrw, clock cycles from the falling edge of CSn to the * falling edge of RDn or WRn * 11:11: reserved * 10:05: rwpw, the width of RDn or WRn in processor clock cycles * 04:00: rwcs, clock cycles from the rising edge of RDn or WRn to the * rising edge of CSn
*/
/* Save chip timing */
rknand->timing = ACCTIMING(tc2rw, trwpw, trw2c);
staticint rk_nfc_write_page_raw(struct nand_chip *chip, const u8 *buf, int oob_on, int page)
{ struct rk_nfc_nand_chip *rknand = rk_nfc_to_rknand(chip); struct rk_nfc *nfc = nand_get_controller_data(chip); struct mtd_info *mtd = nand_to_mtd(chip); struct nand_ecc_ctrl *ecc = &chip->ecc; int i, pages_per_blk;
pages_per_blk = mtd->erasesize / mtd->writesize; if ((chip->options & NAND_IS_BOOT_MEDIUM) &&
(page < (pages_per_blk * rknand->boot_blks)) &&
rknand->boot_ecc != ecc->strength) { /* * There's currently no method to notify the MTD framework that * a different ECC strength is in use for the boot blocks.
*/ return -EIO;
}
if (!buf)
memset(nfc->page_buf, 0xff, mtd->writesize + mtd->oobsize);
for (i = 0; i < ecc->steps; i++) { /* Copy data to the NFC buffer. */ if (buf)
memcpy(rk_nfc_data_ptr(chip, i),
rk_nfc_buf_to_data_ptr(chip, buf, i),
ecc->size); /* * The first four bytes of OOB are reserved for the * boot ROM. In some debugging cases, such as with a * read, erase and write back test these 4 bytes stored * in OOB also need to be written back. * * The function nand_block_bad detects bad blocks like: * * bad = chip->oob_poi[chip->badblockpos]; * * chip->badblockpos == 0 for a large page NAND Flash, * so chip->oob_poi[0] is the bad block mask (BBM). * * The OOB data layout on the NFC is: * * PA0 PA1 PA2 PA3 | BBM OOB1 OOB2 OOB3 | ... * * or * * 0xFF 0xFF 0xFF 0xFF | BBM OOB1 OOB2 OOB3 | ... * * The code here just swaps the first 4 bytes with the last * 4 bytes without losing any data. * * The chip->oob_poi data layout: * * BBM OOB1 OOB2 OOB3 |......| PA0 PA1 PA2 PA3 * * The rk_nfc_ooblayout_free() function already has reserved * these 4 bytes together with 2 bytes for BBM * by reducing it's length: * * oob_region->length = rknand->metadata_size - NFC_SYS_DATA_SIZE - 2;
*/ if (!i)
memcpy(rk_nfc_oob_ptr(chip, i),
rk_nfc_buf_to_oob_ptr(chip, ecc->steps - 1),
NFC_SYS_DATA_SIZE); else
memcpy(rk_nfc_oob_ptr(chip, i),
rk_nfc_buf_to_oob_ptr(chip, i - 1),
NFC_SYS_DATA_SIZE); /* Copy ECC data to the NFC buffer. */
memcpy(rk_nfc_oob_ptr(chip, i) + NFC_SYS_DATA_SIZE,
rk_nfc_buf_to_oob_ecc_ptr(chip, i),
ecc->bytes);
}
staticint rk_nfc_write_page_hwecc(struct nand_chip *chip, const u8 *buf, int oob_on, int page)
{ struct mtd_info *mtd = nand_to_mtd(chip); struct rk_nfc *nfc = nand_get_controller_data(chip); struct rk_nfc_nand_chip *rknand = rk_nfc_to_rknand(chip); struct nand_ecc_ctrl *ecc = &chip->ecc; int oob_step = (ecc->bytes > 60) ? NFC_MAX_OOB_PER_STEP :
NFC_MIN_OOB_PER_STEP; int pages_per_blk = mtd->erasesize / mtd->writesize; int ret = 0, i, boot_rom_mode = 0;
dma_addr_t dma_data, dma_oob;
u32 tmp;
u8 *oob;
nand_prog_page_begin_op(chip, page, 0, NULL, 0);
if (buf)
memcpy(nfc->page_buf, buf, mtd->writesize); else
memset(nfc->page_buf, 0xFF, mtd->writesize);
/* * The first blocks (4, 8 or 16 depending on the device) are used * by the boot ROM and the first 32 bits of OOB need to link to * the next page address in the same block. We can't directly copy * OOB data from the MTD framework, because this page address * conflicts for example with the bad block marker (BBM), * so we shift all OOB data including the BBM with 4 byte positions. * As a consequence the OOB size available to the MTD framework is * also reduced with 4 bytes. * * PA0 PA1 PA2 PA3 | BBM OOB1 OOB2 OOB3 | ... * * If a NAND is not a boot medium or the page is not a boot block, * the first 4 bytes are left untouched by writing 0xFF to them. * * 0xFF 0xFF 0xFF 0xFF | BBM OOB1 OOB2 OOB3 | ... * * The code here just swaps the first 4 bytes with the last * 4 bytes without losing any data. * * The chip->oob_poi data layout: * * BBM OOB1 OOB2 OOB3 |......| PA0 PA1 PA2 PA3 * * Configure the ECC algorithm supported by the boot ROM.
*/ if ((page < (pages_per_blk * rknand->boot_blks)) &&
(chip->options & NAND_IS_BOOT_MEDIUM)) {
boot_rom_mode = 1; if (rknand->boot_ecc != ecc->strength)
rk_nfc_hw_ecc_setup(chip, rknand->boot_ecc);
}
for (i = 0; i < ecc->steps; i++) { if (!i)
oob = chip->oob_poi + (ecc->steps - 1) * NFC_SYS_DATA_SIZE; else
oob = chip->oob_poi + (i - 1) * NFC_SYS_DATA_SIZE;
rk_nfc_xfer_start(nfc, NFC_WRITE, ecc->steps, dma_data,
dma_oob);
ret = wait_for_completion_timeout(&nfc->done,
msecs_to_jiffies(100)); if (!ret)
dev_warn(nfc->dev, "write: wait dma done timeout.\n"); /* * Whether the DMA transfer is completed or not. The driver * needs to check the NFC`s status register to see if the data * transfer was completed.
*/
ret = rk_nfc_wait_for_xfer_done(nfc);
staticint rk_nfc_read_page_raw(struct nand_chip *chip, u8 *buf, int oob_on, int page)
{ struct rk_nfc_nand_chip *rknand = rk_nfc_to_rknand(chip); struct rk_nfc *nfc = nand_get_controller_data(chip); struct mtd_info *mtd = nand_to_mtd(chip); struct nand_ecc_ctrl *ecc = &chip->ecc; int i, pages_per_blk;
pages_per_blk = mtd->erasesize / mtd->writesize; if ((chip->options & NAND_IS_BOOT_MEDIUM) &&
(page < (pages_per_blk * rknand->boot_blks)) &&
rknand->boot_ecc != ecc->strength) { /* * There's currently no method to notify the MTD framework that * a different ECC strength is in use for the boot blocks.
*/ return -EIO;
}
nand_read_page_op(chip, page, 0, NULL, 0);
rk_nfc_read_buf(nfc, nfc->page_buf, mtd->writesize + mtd->oobsize); for (i = 0; i < ecc->steps; i++) { /* * The first four bytes of OOB are reserved for the * boot ROM. In some debugging cases, such as with a read, * erase and write back test, these 4 bytes also must be * saved somewhere, otherwise this information will be * lost during a write back.
*/ if (!i)
memcpy(rk_nfc_buf_to_oob_ptr(chip, ecc->steps - 1),
rk_nfc_oob_ptr(chip, i),
NFC_SYS_DATA_SIZE); else
memcpy(rk_nfc_buf_to_oob_ptr(chip, i - 1),
rk_nfc_oob_ptr(chip, i),
NFC_SYS_DATA_SIZE);
/* Copy ECC data from the NFC buffer. */
memcpy(rk_nfc_buf_to_oob_ecc_ptr(chip, i),
rk_nfc_oob_ptr(chip, i) + NFC_SYS_DATA_SIZE,
ecc->bytes);
/* Copy data from the NFC buffer. */ if (buf)
memcpy(rk_nfc_buf_to_data_ptr(chip, buf, i),
rk_nfc_data_ptr(chip, i),
ecc->size);
}
return 0;
}
staticint rk_nfc_read_page_hwecc(struct nand_chip *chip, u8 *buf, int oob_on, int page)
{ struct mtd_info *mtd = nand_to_mtd(chip); struct rk_nfc *nfc = nand_get_controller_data(chip); struct rk_nfc_nand_chip *rknand = rk_nfc_to_rknand(chip); struct nand_ecc_ctrl *ecc = &chip->ecc; int oob_step = (ecc->bytes > 60) ? NFC_MAX_OOB_PER_STEP :
NFC_MIN_OOB_PER_STEP; int pages_per_blk = mtd->erasesize / mtd->writesize;
dma_addr_t dma_data, dma_oob; int ret = 0, i, cnt, boot_rom_mode = 0; int max_bitflips = 0, bch_st, ecc_fail = 0;
u8 *oob;
u32 tmp;
/* * The first blocks (4, 8 or 16 depending on the device) * are used by the boot ROM. * Configure the ECC algorithm supported by the boot ROM.
*/ if ((page < (pages_per_blk * rknand->boot_blks)) &&
(chip->options & NAND_IS_BOOT_MEDIUM)) {
boot_rom_mode = 1; if (rknand->boot_ecc != ecc->strength)
rk_nfc_hw_ecc_setup(chip, rknand->boot_ecc);
}
reinit_completion(&nfc->done);
writel(INT_DMA, nfc->regs + nfc->cfg->int_en_off);
rk_nfc_xfer_start(nfc, NFC_READ, ecc->steps, dma_data,
dma_oob);
ret = wait_for_completion_timeout(&nfc->done,
msecs_to_jiffies(100)); if (!ret)
dev_warn(nfc->dev, "read: wait dma done timeout.\n"); /* * Whether the DMA transfer is completed or not. The driver * needs to check the NFC`s status register to see if the data * transfer was completed.
*/
ret = rk_nfc_wait_for_xfer_done(nfc);
if (rknand->metadata_size < NFC_SYS_DATA_SIZE + 2) {
dev_err(dev, "driver needs at least %d bytes of meta data\n",
NFC_SYS_DATA_SIZE + 2); return -EIO;
}
nfc->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(nfc->regs)) {
ret = PTR_ERR(nfc->regs); goto release_nfc;
}
nfc->nfc_clk = devm_clk_get(dev, "nfc"); if (IS_ERR(nfc->nfc_clk)) {
dev_dbg(dev, "no NFC clk\n"); /* Some earlier models, such as rk3066, have no NFC clk. */
}
nfc->ahb_clk = devm_clk_get(dev, "ahb"); if (IS_ERR(nfc->ahb_clk)) {
dev_err(dev, "no ahb clk\n");
ret = PTR_ERR(nfc->ahb_clk); goto release_nfc;
}
ret = rk_nfc_enable_clks(dev, nfc); if (ret) goto release_nfc;
irq = platform_get_irq(pdev, 0); if (irq < 0) {
ret = -EINVAL; goto clk_disable;
}
writel(0, nfc->regs + nfc->cfg->int_en_off);
ret = devm_request_irq(dev, irq, rk_nfc_irq, 0x0, "rk-nand", nfc); if (ret) {
dev_err(dev, "failed to request NFC irq\n"); goto clk_disable;
}
platform_set_drvdata(pdev, nfc);
ret = rk_nfc_nand_chips_init(dev, nfc); if (ret) {
dev_err(dev, "failed to init NAND chips\n"); goto clk_disable;
} return 0;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.