// SPDX-License-Identifier: GPL-2.0+ /* * Copyright 2009-2015 Freescale Semiconductor, Inc. and others * * Description: MPC5125, VF610, MCF54418 and Kinetis K70 Nand driver. * Jason ported to M54418TWR and MVFA5 (VF610). * Authors: Stefan Agner <stefan.agner@toradex.com> * Bill Pringlemeir <bpringlemeir@nbsps.com> * Shaohui Xie <b21989@freescale.com> * Jason Jin <Jason.jin@freescale.com> * * Based on original driver mpc5121_nfc.c. * * Limitations: * - Untested on MPC5125 and M54418. * - DMA and pipelining not used. * - 2K pages or less. * - HW ECC: Only 2K page with 64+ OOB. * - HW ECC: Only 24 and 32-bit error correction implemented.
*/
/* * ECC status - seems to consume 8 bytes (double word). The documented * status byte is located in the lowest byte of the second word (which is * the 4th or 7th byte depending on endianness). * Calculate an offset to store the ECC status at the end of the buffer.
*/ #define ECC_SRAM_ADDR (PAGE_2K + OOB_MAX - 8)
struct vf610_nfc { struct nand_controller base; struct nand_chip chip; struct device *dev; void __iomem *regs; struct completion cmd_done; /* Status and ID are in alternate locations. */ enum vf610_nfc_variant variant; struct clk *clk; /* * Indicate that user data is accessed (full page/oob). This is * useful to indicate the driver whether to swap byte endianness. * See comments in vf610_nfc_rd_from_sram/vf610_nfc_wr_to_sram.
*/ bool data_access;
u32 ecc_mode;
};
/* * Read accessor for internal SRAM buffer * @dst: destination address in regular memory * @src: source address in SRAM buffer * @len: bytes to copy * @fix_endian: Fix endianness if required * * Use this accessor for the internal SRAM buffers. On the ARM * Freescale Vybrid SoC it's known that the driver can treat * the SRAM buffer as if it's memory. Other platform might need * to treat the buffers differently. * * The controller stores bytes from the NAND chip internally in big * endianness. On little endian platforms such as Vybrid this leads * to reversed byte order. * For performance reason (and earlier probably due to unawareness) * the driver avoids correcting endianness where it has control over * write and read side (e.g. page wise data access).
*/ staticinlinevoid vf610_nfc_rd_from_sram(void *dst, constvoid __iomem *src,
size_t len, bool fix_endian)
{ if (vf610_nfc_kernel_is_little_endian() && fix_endian) { unsignedint i;
for (i = 0; i < len; i += 4) {
u32 val = swab32(__raw_readl(src + i));
memcpy(dst + i, &val, min(sizeof(val), len - i));
}
} else {
memcpy_fromio(dst, src, len);
}
}
/* * Write accessor for internal SRAM buffer * @dst: destination address in SRAM buffer * @src: source address in regular memory * @len: bytes to copy * @fix_endian: Fix endianness if required * * Use this accessor for the internal SRAM buffers. On the ARM * Freescale Vybrid SoC it's known that the driver can treat * the SRAM buffer as if it's memory. Other platform might need * to treat the buffers differently. * * The controller stores bytes from the NAND chip internally in big * endianness. On little endian platforms such as Vybrid this leads * to reversed byte order. * For performance reason (and earlier probably due to unawareness) * the driver avoids correcting endianness where it has control over * write and read side (e.g. page wise data access).
*/ staticinlinevoid vf610_nfc_wr_to_sram(void __iomem *dst, constvoid *src,
size_t len, bool fix_endian)
{ if (vf610_nfc_kernel_is_little_endian() && fix_endian) { unsignedint i;
/* * Barrier is needed after this write. This write need * to be done before reading the next register the first * time. * vf610_nfc_set implicates such a barrier by using writel * to write to the register.
*/
vf610_nfc_set(nfc, NFC_IRQ_STATUS, IDLE_EN_BIT);
vf610_nfc_set(nfc, NFC_FLASH_CMD2, START_BIT);
if (!wait_for_completion_timeout(&nfc->cmd_done, timeout))
dev_warn(nfc->dev, "Timeout while waiting for BUSY.\n");
/* * Some ops are optional, but the hardware requires the operations * to be in this exact order. * The op parser enforces the order and makes sure that there isn't * a read and write element in a single operation.
*/
instr = vf610_get_next_instr(subop, &op_id); if (!instr) return -EINVAL;
if (instr && instr->type == NAND_OP_ADDR_INSTR) { int naddrs = nand_subop_get_num_addr_cyc(subop, op_id); int i = nand_subop_get_addr_start_off(subop, op_id);
for (; i < naddrs; i++) {
u8 val = instr->ctx.addr.addrs[i];
if (i < 2)
col |= COL_ADDR(i, val); else
row |= ROW_ADDR(i - 2, val);
}
code |= COMMAND_NADDR_BYTES(naddrs);
/* * This function supports Vybrid only (MPC5125 would have full RB and four CS)
*/ staticvoid vf610_nfc_select_target(struct nand_chip *chip, unsignedint cs)
{ struct vf610_nfc *nfc = chip_to_nfc(chip);
u32 tmp;
/* Vybrid only (MPC5125 would have full RB and four CS) */ if (nfc->variant != NFC_VFC610) return;
/* * On an erased page, bit count (including OOB) should be zero or * at least less then half of the ECC strength.
*/ return nand_check_erased_ecc_chunk(dat, nfc->chip.ecc.size, oob,
mtd->oobsize, NULL, 0,
flips_threshold);
}
/* Disable virtual pages, only one elementary transfer unit */
vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG, CONFIG_PAGE_CNT_MASK,
CONFIG_PAGE_CNT_SHIFT, 1);
}
if (nfc->chip.ecc.engine_type == NAND_ECC_ENGINE_TYPE_ON_HOST) { /* Set ECC status offset in SRAM */
vf610_nfc_set_field(nfc, NFC_FLASH_CONFIG,
CONFIG_ECC_SRAM_ADDR_MASK,
CONFIG_ECC_SRAM_ADDR_SHIFT,
ECC_SRAM_ADDR >> 3);
/* Enable ECC status in SRAM */
vf610_nfc_set(nfc, NFC_FLASH_CONFIG, CONFIG_ECC_SRAM_REQ_BIT);
}
}
irq = platform_get_irq(pdev, 0); if (irq < 0) return irq;
nfc->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(nfc->regs)) return PTR_ERR(nfc->regs);
nfc->clk = devm_clk_get_enabled(&pdev->dev, NULL); if (IS_ERR(nfc->clk)) {
dev_err(nfc->dev, "Unable to get and enable clock!\n"); return PTR_ERR(nfc->clk);
}
nfc->variant = (enum vf610_nfc_variant)device_get_match_data(&pdev->dev); if (!nfc->variant) return -ENODEV;
for_each_available_child_of_node(nfc->dev->of_node, child) { if (of_device_is_compatible(child, "fsl,vf610-nfc-nandcs")) {
if (nand_get_flash_node(chip)) {
dev_err(nfc->dev, "Only one NAND chip supported!\n");
of_node_put(child); return -EINVAL;
}
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.