From 9345943b2b5ea890cb479770c3c802cf851ed3e6 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Fri, 22 Jun 2018 17:19:46 +0200 Subject: mtd: nand: mxs_nand: introduce SPL specific init In preparation to convert the driver to use NAND self init provide a new minimal init for SPL builds. As a side effect this also reduces size of SPL by about 4KiB. Signed-off-by: Stefan Agner --- drivers/mtd/nand/mxs_nand.c | 43 +++++++++++++++++++++++++++++++++++++++++ drivers/mtd/nand/mxs_nand.h | 10 ++++++++++ drivers/mtd/nand/mxs_nand_spl.c | 3 ++- 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 drivers/mtd/nand/mxs_nand.h (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c index b9ffa7cbae..5b7ad18c42 100644 --- a/drivers/mtd/nand/mxs_nand.c +++ b/drivers/mtd/nand/mxs_nand.c @@ -25,6 +25,7 @@ #include #include #include +#include "mxs_nand.h" #define MXS_NAND_DMA_DESCRIPTOR_COUNT 4 @@ -1149,6 +1150,48 @@ err1: return ret; } +int mxs_nand_init_spl(struct nand_chip *nand) +{ + struct mxs_nand_info *nand_info; + int err; + + nand_info = malloc(sizeof(struct mxs_nand_info)); + if (!nand_info) { + printf("MXS NAND: Failed to allocate private data\n"); + return -ENOMEM; + } + memset(nand_info, 0, sizeof(struct mxs_nand_info)); + + err = mxs_nand_alloc_buffers(nand_info); + if (err) + return err; + + err = mxs_nand_init(nand_info); + if (err) + return err; + + nand_set_controller_data(nand, nand_info); + + nand->options |= NAND_NO_SUBPAGE_WRITE; + + nand->cmd_ctrl = mxs_nand_cmd_ctrl; + nand->dev_ready = mxs_nand_device_ready; + nand->select_chip = mxs_nand_select_chip; + nand->scan_bbt = mxs_nand_scan_bbt; + + nand->read_byte = mxs_nand_read_byte; + nand->read_buf = mxs_nand_read_buf; + + nand->ecc.read_page = mxs_nand_ecc_read_page; + + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.bytes = 9; + nand->ecc.size = 512; + nand->ecc.strength = 8; + + return 0; +} + /*! * This function is called during the driver binding process. * diff --git a/drivers/mtd/nand/mxs_nand.h b/drivers/mtd/nand/mxs_nand.h new file mode 100644 index 0000000000..9bb7148d98 --- /dev/null +++ b/drivers/mtd/nand/mxs_nand.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * NXP GPMI NAND flash driver + * + * Copyright (C) 2018 Toradex + * Authors: + * Stefan Agner + */ + +int mxs_nand_init_spl(struct nand_chip *nand); diff --git a/drivers/mtd/nand/mxs_nand_spl.c b/drivers/mtd/nand/mxs_nand_spl.c index 3e8b35f04a..47857a81bc 100644 --- a/drivers/mtd/nand/mxs_nand_spl.c +++ b/drivers/mtd/nand/mxs_nand_spl.c @@ -6,6 +6,7 @@ #include #include #include +#include "mxs_nand.h" static struct mtd_info *mtd; static struct nand_chip nand_chip; @@ -145,7 +146,7 @@ static int mxs_nand_init(void) return 0; /* init mxs nand driver */ - board_nand_init(&nand_chip); + mxs_nand_init_spl(&nand_chip); mtd = nand_to_mtd(&nand_chip); /* set mtd functions */ nand_chip.cmdfunc = mxs_nand_command; -- cgit From 5346c31e305a37d39f535cc0d5ae87d8b7e81230 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Fri, 22 Jun 2018 17:19:47 +0200 Subject: mtd: nand: mxs_nand: use self init Instead of completing initialization via scan_bbt callback use NAND self init to initialize the GPMI (MXS) NAND controller. Suggested-by: Scott Wood Signed-off-by: Stefan Agner --- drivers/mtd/nand/Kconfig | 1 + drivers/mtd/nand/mxs_nand.c | 53 +++++++++++++++++++++++++-------------------- drivers/mtd/nand/mxs_nand.h | 1 + 3 files changed, 31 insertions(+), 24 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 94fbf89e4b..4db259fcb2 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -143,6 +143,7 @@ config NAND_MXC config NAND_MXS bool "MXS NAND support" depends on MX23 || MX28 || MX6 || MX7 + select SYS_NAND_SELF_INIT imply CMD_NAND select APBH_DMA select APBH_DMA_BURST if ARCH_MX6 || ARCH_MX7 diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c index 5b7ad18c42..14d3210017 100644 --- a/drivers/mtd/nand/mxs_nand.c +++ b/drivers/mtd/nand/mxs_nand.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,7 @@ #define MXS_NAND_BCH_TIMEOUT 10000 struct mxs_nand_info { + struct nand_chip chip; int cur_chip; uint32_t cmd_queue_len; @@ -972,20 +974,15 @@ static int mxs_nand_block_bad(struct mtd_info *mtd, loff_t ofs) } /* - * Nominally, the purpose of this function is to look for or create the bad - * block table. In fact, since the we call this function at the very end of - * the initialization process started by nand_scan(), and we doesn't have a - * more formal mechanism, we "hook" this function to continue init process. - * * At this point, the physical NAND Flash chips have been identified and * counted, so we know the physical geometry. This enables us to make some * important configuration decisions. * * The return value of this function propagates directly back to this driver's - * call to nand_scan(). Anything other than zero will cause this driver to + * board_nand_init(). Anything other than zero will cause this driver to * tear everything down and declare failure. */ -static int mxs_nand_scan_bbt(struct mtd_info *mtd) +int mxs_nand_setup_ecc(struct mtd_info *mtd) { struct nand_chip *nand = mtd_to_nand(mtd); struct mxs_nand_info *nand_info = nand_get_controller_data(nand); @@ -1047,8 +1044,7 @@ static int mxs_nand_scan_bbt(struct mtd_info *mtd) mtd->_block_markbad = mxs_nand_hook_block_markbad; } - /* We use the reference implementation for bad block management. */ - return nand_default_bbt(mtd); + return 0; } /* @@ -1177,7 +1173,6 @@ int mxs_nand_init_spl(struct nand_chip *nand) nand->cmd_ctrl = mxs_nand_cmd_ctrl; nand->dev_ready = mxs_nand_device_ready; nand->select_chip = mxs_nand_select_chip; - nand->scan_bbt = mxs_nand_scan_bbt; nand->read_byte = mxs_nand_read_byte; nand->read_buf = mxs_nand_read_buf; @@ -1192,27 +1187,22 @@ int mxs_nand_init_spl(struct nand_chip *nand) return 0; } -/*! - * This function is called during the driver binding process. - * - * @param pdev the device structure used to store device specific - * information that is used by the suspend, resume and - * remove functions - * - * @return The function always returns 0. - */ -int board_nand_init(struct nand_chip *nand) +void board_nand_init(void) { + struct mtd_info *mtd; struct mxs_nand_info *nand_info; + struct nand_chip *nand; int err; nand_info = malloc(sizeof(struct mxs_nand_info)); if (!nand_info) { printf("MXS NAND: Failed to allocate private data\n"); - return -ENOMEM; + return; } memset(nand_info, 0, sizeof(struct mxs_nand_info)); + nand = &nand_info->chip; + mtd = nand_to_mtd(nand); err = mxs_nand_alloc_buffers(nand_info); if (err) goto err1; @@ -1231,13 +1221,19 @@ int board_nand_init(struct nand_chip *nand) nand->dev_ready = mxs_nand_device_ready; nand->select_chip = mxs_nand_select_chip; nand->block_bad = mxs_nand_block_bad; - nand->scan_bbt = mxs_nand_scan_bbt; nand->read_byte = mxs_nand_read_byte; nand->read_buf = mxs_nand_read_buf; nand->write_buf = mxs_nand_write_buf; + /* first scan to find the device and get the page size */ + if (nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_DEVICE, NULL)) + goto err2; + + if (mxs_nand_setup_ecc(mtd)) + goto err2; + nand->ecc.read_page = mxs_nand_ecc_read_page; nand->ecc.write_page = mxs_nand_ecc_write_page; nand->ecc.read_oob = mxs_nand_ecc_read_oob; @@ -1249,12 +1245,21 @@ int board_nand_init(struct nand_chip *nand) nand->ecc.size = 512; nand->ecc.strength = 8; - return 0; + /* second phase scan */ + err = nand_scan_tail(mtd); + if (err) + goto err2; + + err = nand_register(0, mtd); + if (err) + goto err2; + + return; err2: free(nand_info->data_buf); free(nand_info->cmd_buf); err1: free(nand_info); - return err; + return; } diff --git a/drivers/mtd/nand/mxs_nand.h b/drivers/mtd/nand/mxs_nand.h index 9bb7148d98..379ed24f05 100644 --- a/drivers/mtd/nand/mxs_nand.h +++ b/drivers/mtd/nand/mxs_nand.h @@ -8,3 +8,4 @@ */ int mxs_nand_init_spl(struct nand_chip *nand); +int mxs_nand_setup_ecc(struct mtd_info *mtd); -- cgit From dc0b69fa9f97df90cbcabf16a51d7eb88f26cd2d Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Fri, 22 Jun 2018 17:19:48 +0200 Subject: mtd: nand: mxs_nand: allow to enable BBT support Add config option which allows to enable on flash bad block table support. This has the same effect as when using the device tree property "nand-on-flash-bbt" in Linux. Signed-off-by: Stefan Agner --- drivers/mtd/nand/mxs_nand.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c index 14d3210017..2584608641 100644 --- a/drivers/mtd/nand/mxs_nand.c +++ b/drivers/mtd/nand/mxs_nand.c @@ -1213,6 +1213,10 @@ void board_nand_init(void) memset(&fake_ecc_layout, 0, sizeof(fake_ecc_layout)); +#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT + nand->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; +#endif + nand_set_controller_data(nand, nand_info); nand->options |= NAND_NO_SUBPAGE_WRITE; -- cgit From 28897e8d21f8e197e259a91c693de09cd81f2d5a Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Fri, 22 Jun 2018 17:19:49 +0200 Subject: mtd: nand: mxs_nand: use structure for BCH geometry Calculate BCH geometry at start and store the information in a structure. This avoids recalculation on every page access and allows to calculate ECC relevant information in one place. This patch does not change ECC layout or driver behavior in any way. The patch aligns the driver somewhat with the Linux GPMI NAND driver which drives the same IP. Signed-off-by: Stefan Agner --- drivers/mtd/nand/mxs_nand.c | 182 +++++++++++++++++++++++--------------------- 1 file changed, 95 insertions(+), 87 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c index 2584608641..dbf35461aa 100644 --- a/drivers/mtd/nand/mxs_nand.c +++ b/drivers/mtd/nand/mxs_nand.c @@ -30,7 +30,6 @@ #define MXS_NAND_DMA_DESCRIPTOR_COUNT 4 -#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE 512 #if (defined(CONFIG_MX6) || defined(CONFIG_MX7)) #define MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT 2 #else @@ -47,12 +46,35 @@ #define MXS_NAND_BCH_TIMEOUT 10000 +/** + * @gf_len: The length of Galois Field. (e.g., 13 or 14) + * @ecc_strength: A number that describes the strength of the ECC + * algorithm. + * @ecc_chunk_size: The size, in bytes, of a single ECC chunk. Note + * the first chunk in the page includes both data and + * metadata, so it's a bit larger than this value. + * @ecc_chunk_count: The number of ECC chunks in the page, + * @block_mark_byte_offset: The byte offset in the ECC-based page view at + * which the underlying physical block mark appears. + * @block_mark_bit_offset: The bit offset into the ECC-based page view at + * which the underlying physical block mark appears. + */ +struct bch_geometry { + unsigned int gf_len; + unsigned int ecc_strength; + unsigned int ecc_chunk_size; + unsigned int ecc_chunk_count; + unsigned int block_mark_byte_offset; + unsigned int block_mark_bit_offset; +}; + struct mxs_nand_info { struct nand_chip chip; int cur_chip; uint32_t cmd_queue_len; uint32_t data_buf_size; + struct bch_geometry bch_geometry; uint8_t *cmd_buf; uint8_t *data_buf; @@ -75,8 +97,6 @@ struct mxs_nand_info { }; struct nand_ecclayout fake_ecc_layout; -static int chunk_data_size = MXS_NAND_CHUNK_DATA_CHUNK_SIZE; -static int galois_field = 13; /* * Cache management functions @@ -137,61 +157,21 @@ static void mxs_nand_return_dma_descs(struct mxs_nand_info *info) info->desc_index = 0; } -static uint32_t mxs_nand_ecc_chunk_cnt(uint32_t page_data_size) -{ - return page_data_size / chunk_data_size; -} - -static uint32_t mxs_nand_ecc_size_in_bits(uint32_t ecc_strength) -{ - return ecc_strength * galois_field; -} - static uint32_t mxs_nand_aux_status_offset(void) { return (MXS_NAND_METADATA_SIZE + 0x3) & ~0x3; } -static inline uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size, - uint32_t page_oob_size) +static inline int mxs_nand_calc_mark_offset(struct bch_geometry *geo, + uint32_t page_data_size) { - int ecc_strength; - int max_ecc_strength_supported; - - /* Refer to Chapter 17 for i.MX6DQ, Chapter 18 for i.MX6SX */ - if (is_mx6sx() || is_mx7()) - max_ecc_strength_supported = 62; - else - max_ecc_strength_supported = 40; - - /* - * Determine the ECC layout with the formula: - * ECC bits per chunk = (total page spare data bits) / - * (bits per ECC level) / (chunks per page) - * where: - * total page spare data bits = - * (page oob size - meta data size) * (bits per byte) - */ - ecc_strength = ((page_oob_size - MXS_NAND_METADATA_SIZE) * 8) - / (galois_field * - mxs_nand_ecc_chunk_cnt(page_data_size)); - - return min(round_down(ecc_strength, 2), max_ecc_strength_supported); -} - -static inline uint32_t mxs_nand_get_mark_offset(uint32_t page_data_size, - uint32_t ecc_strength) -{ - uint32_t chunk_data_size_in_bits; - uint32_t chunk_ecc_size_in_bits; + uint32_t chunk_data_size_in_bits = geo->ecc_chunk_size * 8; + uint32_t chunk_ecc_size_in_bits = geo->ecc_strength * geo->gf_len; uint32_t chunk_total_size_in_bits; uint32_t block_mark_chunk_number; uint32_t block_mark_chunk_bit_offset; uint32_t block_mark_bit_offset; - chunk_data_size_in_bits = chunk_data_size * 8; - chunk_ecc_size_in_bits = mxs_nand_ecc_size_in_bits(ecc_strength); - chunk_total_size_in_bits = chunk_data_size_in_bits + chunk_ecc_size_in_bits; @@ -216,7 +196,7 @@ static inline uint32_t mxs_nand_get_mark_offset(uint32_t page_data_size, (block_mark_chunk_number * chunk_total_size_in_bits); if (block_mark_chunk_bit_offset > chunk_data_size_in_bits) - return 1; + return -EINVAL; /* * Now that we know the chunk number in which the block mark appears, @@ -225,21 +205,59 @@ static inline uint32_t mxs_nand_get_mark_offset(uint32_t page_data_size, block_mark_bit_offset -= block_mark_chunk_number * chunk_ecc_size_in_bits; - return block_mark_bit_offset; -} + geo->block_mark_byte_offset = block_mark_bit_offset >> 3; + geo->block_mark_bit_offset = block_mark_bit_offset & 0x7; -static uint32_t mxs_nand_mark_byte_offset(struct mtd_info *mtd) -{ - uint32_t ecc_strength; - ecc_strength = mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize); - return mxs_nand_get_mark_offset(mtd->writesize, ecc_strength) >> 3; + return 0; } -static uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd) +static inline int mxs_nand_calc_ecc_layout(struct bch_geometry *geo, + struct mtd_info *mtd) { - uint32_t ecc_strength; - ecc_strength = mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize); - return mxs_nand_get_mark_offset(mtd->writesize, ecc_strength) & 0x7; + unsigned int max_ecc_strength_supported; + + /* The default for the length of Galois Field. */ + geo->gf_len = 13; + + /* The default for chunk size. */ + geo->ecc_chunk_size = 512; + + if (geo->ecc_chunk_size < mtd->oobsize) { + geo->gf_len = 14; + geo->ecc_chunk_size *= 2; + } + + if (mtd->oobsize > geo->ecc_chunk_size) { + printf("Not support the NAND chips whose oob size is larger then %d bytes!\n", + geo->ecc_chunk_size); + return -EINVAL; + } + + geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; + + /* Refer to Chapter 17 for i.MX6DQ, Chapter 18 for i.MX6SX */ + if (is_mx6sx() || is_mx7()) + max_ecc_strength_supported = 62; + else + max_ecc_strength_supported = 40; + + /* + * Determine the ECC layout with the formula: + * ECC bits per chunk = (total page spare data bits) / + * (bits per ECC level) / (chunks per page) + * where: + * total page spare data bits = + * (page oob size - meta data size) * (bits per byte) + */ + geo->ecc_strength = ((mtd->oobsize - MXS_NAND_METADATA_SIZE) * 8) + / (geo->gf_len * geo->ecc_chunk_count); + + geo->ecc_strength = min(round_down(geo->ecc_strength, 2), max_ecc_strength_supported); + + if (mxs_nand_calc_mark_offset(geo, mtd->writesize) < 0) + return -EINVAL; + + return 0; } /* @@ -380,18 +398,15 @@ static void mxs_nand_select_chip(struct mtd_info *mtd, int chip) * swapping the block mark, or swapping it *back* -- but it doesn't matter * because the the operation is the same. */ -static void mxs_nand_swap_block_mark(struct mtd_info *mtd, - uint8_t *data_buf, uint8_t *oob_buf) +static void mxs_nand_swap_block_mark(struct bch_geometry *geo, + uint8_t *data_buf, uint8_t *oob_buf) { - uint32_t bit_offset; - uint32_t buf_offset; + uint32_t bit_offset = geo->block_mark_bit_offset; + uint32_t buf_offset = geo->block_mark_byte_offset; uint32_t src; uint32_t dst; - bit_offset = mxs_nand_mark_bit_offset(mtd); - buf_offset = mxs_nand_mark_byte_offset(mtd); - /* * Get the byte from the data area that overlays the block mark. Since * the ECC engine applies its own view to the bits in the page, the @@ -567,6 +582,7 @@ static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand, int page) { struct mxs_nand_info *nand_info = nand_get_controller_data(nand); + struct bch_geometry *geo = &nand_info->bch_geometry; struct mxs_dma_desc *d; uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip; uint32_t corrected = 0, failed = 0; @@ -665,11 +681,11 @@ static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand, mxs_nand_inval_data_buf(nand_info); /* Read DMA completed, now do the mark swapping. */ - mxs_nand_swap_block_mark(mtd, nand_info->data_buf, nand_info->oob_buf); + mxs_nand_swap_block_mark(geo, nand_info->data_buf, nand_info->oob_buf); /* Loop over status bytes, accumulating ECC status. */ status = nand_info->oob_buf + mxs_nand_aux_status_offset(); - for (i = 0; i < mxs_nand_ecc_chunk_cnt(mtd->writesize); i++) { + for (i = 0; i < geo->ecc_chunk_count; i++) { if (status[i] == 0x00) continue; @@ -717,6 +733,7 @@ static int mxs_nand_ecc_write_page(struct mtd_info *mtd, int oob_required, int page) { struct mxs_nand_info *nand_info = nand_get_controller_data(nand); + struct bch_geometry *geo = &nand_info->bch_geometry; struct mxs_dma_desc *d; uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip; int ret; @@ -725,7 +742,7 @@ static int mxs_nand_ecc_write_page(struct mtd_info *mtd, memcpy(nand_info->oob_buf, nand->oob_poi, mtd->oobsize); /* Handle block mark swapping. */ - mxs_nand_swap_block_mark(mtd, nand_info->data_buf, nand_info->oob_buf); + mxs_nand_swap_block_mark(geo, nand_info->data_buf, nand_info->oob_buf); /* Compile the DMA descriptor - write data. */ d = mxs_nand_get_dma_desc(nand_info); @@ -986,39 +1003,30 @@ int mxs_nand_setup_ecc(struct mtd_info *mtd) { struct nand_chip *nand = mtd_to_nand(mtd); struct mxs_nand_info *nand_info = nand_get_controller_data(nand); + struct bch_geometry *geo = &nand_info->bch_geometry; struct mxs_bch_regs *bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE; uint32_t tmp; - if (mtd->oobsize > MXS_NAND_CHUNK_DATA_CHUNK_SIZE) { - galois_field = 14; - chunk_data_size = MXS_NAND_CHUNK_DATA_CHUNK_SIZE * 2; - } - - if (mtd->oobsize > chunk_data_size) { - printf("Not support the NAND chips whose oob size is larger then %d bytes!\n", chunk_data_size); + if (mxs_nand_calc_ecc_layout(geo, mtd)) return -EINVAL; - } /* Configure BCH and set NFC geometry */ mxs_reset_block(&bch_regs->hw_bch_ctrl_reg); /* Configure layout 0 */ - tmp = (mxs_nand_ecc_chunk_cnt(mtd->writesize) - 1) - << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET; + tmp = (geo->ecc_chunk_count - 1) << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET; tmp |= MXS_NAND_METADATA_SIZE << BCH_FLASHLAYOUT0_META_SIZE_OFFSET; - tmp |= (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1) - << BCH_FLASHLAYOUT0_ECC0_OFFSET; - tmp |= chunk_data_size >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT; - tmp |= (14 == galois_field ? 1 : 0) << + tmp |= (geo->ecc_strength >> 1) << BCH_FLASHLAYOUT0_ECC0_OFFSET; + tmp |= geo->ecc_chunk_size >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT; + tmp |= (geo->gf_len == 14 ? 1 : 0) << BCH_FLASHLAYOUT0_GF13_0_GF14_1_OFFSET; writel(tmp, &bch_regs->hw_bch_flash0layout0); tmp = (mtd->writesize + mtd->oobsize) << BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET; - tmp |= (mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize) >> 1) - << BCH_FLASHLAYOUT1_ECCN_OFFSET; - tmp |= chunk_data_size >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT; - tmp |= (14 == galois_field ? 1 : 0) << + tmp |= (geo->ecc_strength >> 1) << BCH_FLASHLAYOUT1_ECCN_OFFSET; + tmp |= geo->ecc_chunk_size >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT; + tmp |= (geo->gf_len == 14 ? 1 : 0) << BCH_FLASHLAYOUT1_GF13_0_GF14_1_OFFSET; writel(tmp, &bch_regs->hw_bch_flash0layout1); -- cgit From 5c69dd0730ff6bcad16e86fd93bf7a914d988a22 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Fri, 22 Jun 2018 17:19:50 +0200 Subject: mtd: nand: mxs_nand: report correct ECC parameters Report correct ECC parameters back to the stack. Do not report bytes as we have it not immeaditly available and the Linux version also does not report it. It seems to have no aversive effect. Signed-off-by: Stefan Agner --- drivers/mtd/nand/mxs_nand.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c index dbf35461aa..b28d65f2d6 100644 --- a/drivers/mtd/nand/mxs_nand.c +++ b/drivers/mtd/nand/mxs_nand.c @@ -1253,9 +1253,8 @@ void board_nand_init(void) nand->ecc.layout = &fake_ecc_layout; nand->ecc.mode = NAND_ECC_HW; - nand->ecc.bytes = 9; - nand->ecc.size = 512; - nand->ecc.strength = 8; + nand->ecc.size = nand_info->bch_geometry.ecc_chunk_size; + nand->ecc.strength = nand_info->bch_geometry.ecc_strength; /* second phase scan */ err = nand_scan_tail(mtd); -- cgit From 984df7add1fe6e5a25854eae81f51940806456bc Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Fri, 22 Jun 2018 17:19:51 +0200 Subject: mtd: nand: mxs_nand: add minimal ECC support Add support for minimum ECC strength supported by the NAND chip. This aligns with the behavior when using the fsl,use-minimum-ecc device tree property in Linux. Signed-off-by: Stefan Agner --- drivers/mtd/nand/Kconfig | 8 +++++ drivers/mtd/nand/mxs_nand.c | 72 ++++++++++++++++++++++++++++++++++++--------- 2 files changed, 66 insertions(+), 14 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 4db259fcb2..c039b9cc60 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -152,6 +152,14 @@ config NAND_MXS This enables NAND driver for the NAND flash controller on the MXS processors. +if NAND_MXS + +config NAND_MXS_USE_MINIMUM_ECC + bool "Use minimum ECC strength supported by the controller" + default false + +endif + config NAND_ZYNQ bool "Support for Zynq Nand controller" select SYS_NAND_SELF_INIT diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c index b28d65f2d6..15d8bcb76b 100644 --- a/drivers/mtd/nand/mxs_nand.c +++ b/drivers/mtd/nand/mxs_nand.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -211,11 +212,52 @@ static inline int mxs_nand_calc_mark_offset(struct bch_geometry *geo, return 0; } +static inline unsigned int mxs_nand_max_ecc_strength_supported(void) +{ + /* Refer to Chapter 17 for i.MX6DQ, Chapter 18 for i.MX6SX */ + if (is_mx6sx() || is_mx7()) + return 62; + else + return 40; +} + +static inline int mxs_nand_calc_ecc_layout_by_info(struct bch_geometry *geo, + struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + + if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0)) + return -ENOTSUPP; + + switch (chip->ecc_step_ds) { + case SZ_512: + geo->gf_len = 13; + break; + case SZ_1K: + geo->gf_len = 14; + break; + default: + return -EINVAL; + } + + geo->ecc_chunk_size = chip->ecc_step_ds; + geo->ecc_strength = round_up(chip->ecc_strength_ds, 2); + + /* Keep the C >= O */ + if (geo->ecc_chunk_size < mtd->oobsize) + return -EINVAL; + + if (geo->ecc_strength > mxs_nand_max_ecc_strength_supported()) + return -EINVAL; + + geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; + + return 0; +} + static inline int mxs_nand_calc_ecc_layout(struct bch_geometry *geo, struct mtd_info *mtd) { - unsigned int max_ecc_strength_supported; - /* The default for the length of Galois Field. */ geo->gf_len = 13; @@ -235,12 +277,6 @@ static inline int mxs_nand_calc_ecc_layout(struct bch_geometry *geo, geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; - /* Refer to Chapter 17 for i.MX6DQ, Chapter 18 for i.MX6SX */ - if (is_mx6sx() || is_mx7()) - max_ecc_strength_supported = 62; - else - max_ecc_strength_supported = 40; - /* * Determine the ECC layout with the formula: * ECC bits per chunk = (total page spare data bits) / @@ -252,10 +288,8 @@ static inline int mxs_nand_calc_ecc_layout(struct bch_geometry *geo, geo->ecc_strength = ((mtd->oobsize - MXS_NAND_METADATA_SIZE) * 8) / (geo->gf_len * geo->ecc_chunk_count); - geo->ecc_strength = min(round_down(geo->ecc_strength, 2), max_ecc_strength_supported); - - if (mxs_nand_calc_mark_offset(geo, mtd->writesize) < 0) - return -EINVAL; + geo->ecc_strength = min(round_down(geo->ecc_strength, 2), + mxs_nand_max_ecc_strength_supported()); return 0; } @@ -1006,9 +1040,19 @@ int mxs_nand_setup_ecc(struct mtd_info *mtd) struct bch_geometry *geo = &nand_info->bch_geometry; struct mxs_bch_regs *bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE; uint32_t tmp; + int ret = -ENOTSUPP; - if (mxs_nand_calc_ecc_layout(geo, mtd)) - return -EINVAL; +#ifdef CONFIG_NAND_MXS_USE_MINIMUM_ECC + ret = mxs_nand_calc_ecc_layout_by_info(geo, mtd); +#endif + + if (ret == -ENOTSUPP) + ret = mxs_nand_calc_ecc_layout(geo, mtd); + + if (ret) + return ret; + + mxs_nand_calc_mark_offset(geo, mtd->writesize); /* Configure BCH and set NFC geometry */ mxs_reset_block(&bch_regs->hw_bch_ctrl_reg); -- cgit From 931747e517b19387716cd56057e4afa9e2cdfff4 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Fri, 22 Jun 2018 18:06:12 +0200 Subject: mtd: nand: mxs_nand: move register structs to driver data Move GPMI and BCH register structs to the driver struct mxs_nand_info in prepartion for device tree support. Signed-off-by: Stefan Agner --- drivers/mtd/nand/mxs_nand.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c index 15d8bcb76b..65fed66309 100644 --- a/drivers/mtd/nand/mxs_nand.c +++ b/drivers/mtd/nand/mxs_nand.c @@ -84,6 +84,9 @@ struct mxs_nand_info { uint8_t marking_block_bad; uint8_t raw_oob_mode; + struct mxs_gpmi_regs *gpmi_regs; + struct mxs_bch_regs *bch_regs; + /* Functions with altered behaviour */ int (*hooked_read_oob)(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops); @@ -297,16 +300,15 @@ static inline int mxs_nand_calc_ecc_layout(struct bch_geometry *geo, /* * Wait for BCH complete IRQ and clear the IRQ */ -static int mxs_nand_wait_for_bch_complete(void) +static int mxs_nand_wait_for_bch_complete(struct mxs_nand_info *nand_info) { - struct mxs_bch_regs *bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE; int timeout = MXS_NAND_BCH_TIMEOUT; int ret; - ret = mxs_wait_mask_set(&bch_regs->hw_bch_ctrl_reg, + ret = mxs_wait_mask_set(&nand_info->bch_regs->hw_bch_ctrl_reg, BCH_CTRL_COMPLETE_IRQ, timeout); - writel(BCH_CTRL_COMPLETE_IRQ, &bch_regs->hw_bch_ctrl_clr); + writel(BCH_CTRL_COMPLETE_IRQ, &nand_info->bch_regs->hw_bch_ctrl_clr); return ret; } @@ -404,11 +406,9 @@ static int mxs_nand_device_ready(struct mtd_info *mtd) { struct nand_chip *chip = mtd_to_nand(mtd); struct mxs_nand_info *nand_info = nand_get_controller_data(chip); - struct mxs_gpmi_regs *gpmi_regs = - (struct mxs_gpmi_regs *)MXS_GPMI_BASE; uint32_t tmp; - tmp = readl(&gpmi_regs->hw_gpmi_stat); + tmp = readl(&nand_info->gpmi_regs->hw_gpmi_stat); tmp >>= (GPMI_STAT_READY_BUSY_OFFSET + nand_info->cur_chip); return tmp & 1; @@ -705,7 +705,7 @@ static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand, goto rtn; } - ret = mxs_nand_wait_for_bch_complete(); + ret = mxs_nand_wait_for_bch_complete(nand_info); if (ret) { printf("MXS NAND: BCH read timeout\n"); goto rtn; @@ -813,7 +813,7 @@ static int mxs_nand_ecc_write_page(struct mtd_info *mtd, goto rtn; } - ret = mxs_nand_wait_for_bch_complete(); + ret = mxs_nand_wait_for_bch_complete(nand_info); if (ret) { printf("MXS NAND: BCH write timeout\n"); goto rtn; @@ -1038,7 +1038,7 @@ int mxs_nand_setup_ecc(struct mtd_info *mtd) struct nand_chip *nand = mtd_to_nand(mtd); struct mxs_nand_info *nand_info = nand_get_controller_data(nand); struct bch_geometry *geo = &nand_info->bch_geometry; - struct mxs_bch_regs *bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE; + struct mxs_bch_regs *bch_regs = nand_info->bch_regs; uint32_t tmp; int ret = -ENOTSUPP; @@ -1139,10 +1139,6 @@ int mxs_nand_alloc_buffers(struct mxs_nand_info *nand_info) */ int mxs_nand_init(struct mxs_nand_info *info) { - struct mxs_gpmi_regs *gpmi_regs = - (struct mxs_gpmi_regs *)MXS_GPMI_BASE; - struct mxs_bch_regs *bch_regs = - (struct mxs_bch_regs *)MXS_BCH_BASE; int i = 0, j, ret = 0; info->desc = malloc(sizeof(struct mxs_dma_desc *) * @@ -1171,14 +1167,14 @@ int mxs_nand_init(struct mxs_nand_info *info) } /* Reset the GPMI block. */ - mxs_reset_block(&gpmi_regs->hw_gpmi_ctrl0_reg); - mxs_reset_block(&bch_regs->hw_bch_ctrl_reg); + mxs_reset_block(&info->gpmi_regs->hw_gpmi_ctrl0_reg); + mxs_reset_block(&info->bch_regs->hw_bch_ctrl_reg); /* * Choose NAND mode, set IRQ polarity, disable write protection and * select BCH ECC. */ - clrsetbits_le32(&gpmi_regs->hw_gpmi_ctrl1, + clrsetbits_le32(&info->gpmi_regs->hw_gpmi_ctrl1, GPMI_CTRL1_GPMI_MODE, GPMI_CTRL1_ATA_IRQRDY_POLARITY | GPMI_CTRL1_DEV_RESET | GPMI_CTRL1_BCH_MODE); @@ -1210,6 +1206,8 @@ int mxs_nand_init_spl(struct nand_chip *nand) } memset(nand_info, 0, sizeof(struct mxs_nand_info)); + nand_info->gpmi_regs = (struct mxs_gpmi_regs *)MXS_GPMI_BASE; + nand_info->bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE; err = mxs_nand_alloc_buffers(nand_info); if (err) return err; @@ -1253,6 +1251,8 @@ void board_nand_init(void) } memset(nand_info, 0, sizeof(struct mxs_nand_info)); + nand_info->gpmi_regs = (struct mxs_gpmi_regs *)MXS_GPMI_BASE; + nand_info->bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE; nand = &nand_info->chip; mtd = nand_to_mtd(nand); err = mxs_nand_alloc_buffers(nand_info); -- cgit From 0d4e9d8be23a30f5f1ca5bad2188e8a1c9848c5c Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Fri, 22 Jun 2018 18:06:13 +0200 Subject: mtd: nand: mxs_nand: use more precise function name This function initializes DMA descriptors so mxs_nand_init_dma is more precise. It also frees up the rather generic name mxs_nand_init. Signed-off-by: Stefan Agner --- drivers/mtd/nand/mxs_nand.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c index 65fed66309..6bd3cb12f9 100644 --- a/drivers/mtd/nand/mxs_nand.c +++ b/drivers/mtd/nand/mxs_nand.c @@ -1137,7 +1137,7 @@ int mxs_nand_alloc_buffers(struct mxs_nand_info *nand_info) /* * Initializes the NFC hardware. */ -int mxs_nand_init(struct mxs_nand_info *info) +int mxs_nand_init_dma(struct mxs_nand_info *info) { int i = 0, j, ret = 0; @@ -1212,7 +1212,7 @@ int mxs_nand_init_spl(struct nand_chip *nand) if (err) return err; - err = mxs_nand_init(nand_info); + err = mxs_nand_init_dma(nand_info); if (err) return err; @@ -1259,7 +1259,7 @@ void board_nand_init(void) if (err) goto err1; - err = mxs_nand_init(nand_info); + err = mxs_nand_init_dma(nand_info); if (err) goto err2; -- cgit From 3b1328a0ad7624f7e7bd803d0709c7c2fd13fc46 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Fri, 22 Jun 2018 18:06:14 +0200 Subject: mtd: nand: mxs_nand: separate board/controller init In preparation for device tree support separate board init from controller init similar to other raw NAND drivers. Signed-off-by: Stefan Agner --- drivers/mtd/nand/mxs_nand.c | 54 +++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 21 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c index 6bd3cb12f9..bb40237362 100644 --- a/drivers/mtd/nand/mxs_nand.c +++ b/drivers/mtd/nand/mxs_nand.c @@ -1237,31 +1237,21 @@ int mxs_nand_init_spl(struct nand_chip *nand) return 0; } -void board_nand_init(void) +int mxs_nand_init(struct mxs_nand_info *nand_info) { struct mtd_info *mtd; - struct mxs_nand_info *nand_info; struct nand_chip *nand; int err; - nand_info = malloc(sizeof(struct mxs_nand_info)); - if (!nand_info) { - printf("MXS NAND: Failed to allocate private data\n"); - return; - } - memset(nand_info, 0, sizeof(struct mxs_nand_info)); - - nand_info->gpmi_regs = (struct mxs_gpmi_regs *)MXS_GPMI_BASE; - nand_info->bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE; nand = &nand_info->chip; mtd = nand_to_mtd(nand); err = mxs_nand_alloc_buffers(nand_info); if (err) - goto err1; + return err; err = mxs_nand_init_dma(nand_info); if (err) - goto err2; + goto err_free_buffers; memset(&fake_ecc_layout, 0, sizeof(fake_ecc_layout)); @@ -1285,10 +1275,10 @@ void board_nand_init(void) /* first scan to find the device and get the page size */ if (nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_DEVICE, NULL)) - goto err2; + goto err_free_buffers; if (mxs_nand_setup_ecc(mtd)) - goto err2; + goto err_free_buffers; nand->ecc.read_page = mxs_nand_ecc_read_page; nand->ecc.write_page = mxs_nand_ecc_write_page; @@ -1303,18 +1293,40 @@ void board_nand_init(void) /* second phase scan */ err = nand_scan_tail(mtd); if (err) - goto err2; + goto err_free_buffers; err = nand_register(0, mtd); if (err) - goto err2; + goto err_free_buffers; - return; + return 0; -err2: +err_free_buffers: free(nand_info->data_buf); free(nand_info->cmd_buf); -err1: - free(nand_info); + + return err; +} + +void board_nand_init(void) +{ + struct mxs_nand_info *nand_info; + + nand_info = malloc(sizeof(struct mxs_nand_info)); + if (!nand_info) { + printf("MXS NAND: Failed to allocate private data\n"); + return; + } + memset(nand_info, 0, sizeof(struct mxs_nand_info)); + + nand_info->gpmi_regs = (struct mxs_gpmi_regs *)MXS_GPMI_BASE; + nand_info->bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE; + + if (mxs_nand_init(nand_info) < 0) + goto err; + return; + +err: + free(nand_info); } -- cgit From 502bdc6b4f52fff92d19b5072a60e8b8cbfb1c04 Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Fri, 22 Jun 2018 18:06:15 +0200 Subject: mtd: nand: mxs_nand: add use_minimum_ecc to struct Add use_minimum_ecc as struct mxs_nand_info field in preparation for device tree support. Signed-off-by: Stefan Agner --- drivers/mtd/nand/mxs_nand.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c index bb40237362..3c9ee07be5 100644 --- a/drivers/mtd/nand/mxs_nand.c +++ b/drivers/mtd/nand/mxs_nand.c @@ -71,6 +71,8 @@ struct bch_geometry { struct mxs_nand_info { struct nand_chip chip; + unsigned int max_ecc_strength_supported; + bool use_minimum_ecc; int cur_chip; uint32_t cmd_queue_len; @@ -215,19 +217,11 @@ static inline int mxs_nand_calc_mark_offset(struct bch_geometry *geo, return 0; } -static inline unsigned int mxs_nand_max_ecc_strength_supported(void) -{ - /* Refer to Chapter 17 for i.MX6DQ, Chapter 18 for i.MX6SX */ - if (is_mx6sx() || is_mx7()) - return 62; - else - return 40; -} - static inline int mxs_nand_calc_ecc_layout_by_info(struct bch_geometry *geo, struct mtd_info *mtd) { struct nand_chip *chip = mtd_to_nand(mtd); + struct mxs_nand_info *nand_info = nand_get_controller_data(chip); if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0)) return -ENOTSUPP; @@ -250,7 +244,7 @@ static inline int mxs_nand_calc_ecc_layout_by_info(struct bch_geometry *geo, if (geo->ecc_chunk_size < mtd->oobsize) return -EINVAL; - if (geo->ecc_strength > mxs_nand_max_ecc_strength_supported()) + if (geo->ecc_strength > nand_info->max_ecc_strength_supported) return -EINVAL; geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; @@ -261,6 +255,9 @@ static inline int mxs_nand_calc_ecc_layout_by_info(struct bch_geometry *geo, static inline int mxs_nand_calc_ecc_layout(struct bch_geometry *geo, struct mtd_info *mtd) { + struct nand_chip *chip = mtd_to_nand(mtd); + struct mxs_nand_info *nand_info = nand_get_controller_data(chip); + /* The default for the length of Galois Field. */ geo->gf_len = 13; @@ -292,7 +289,7 @@ static inline int mxs_nand_calc_ecc_layout(struct bch_geometry *geo, / (geo->gf_len * geo->ecc_chunk_count); geo->ecc_strength = min(round_down(geo->ecc_strength, 2), - mxs_nand_max_ecc_strength_supported()); + nand_info->max_ecc_strength_supported); return 0; } @@ -1042,9 +1039,8 @@ int mxs_nand_setup_ecc(struct mtd_info *mtd) uint32_t tmp; int ret = -ENOTSUPP; -#ifdef CONFIG_NAND_MXS_USE_MINIMUM_ECC - ret = mxs_nand_calc_ecc_layout_by_info(geo, mtd); -#endif + if (nand_info->use_minimum_ecc) + ret = mxs_nand_calc_ecc_layout_by_info(geo, mtd); if (ret == -ENOTSUPP) ret = mxs_nand_calc_ecc_layout(geo, mtd); @@ -1322,6 +1318,16 @@ void board_nand_init(void) nand_info->gpmi_regs = (struct mxs_gpmi_regs *)MXS_GPMI_BASE; nand_info->bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE; + /* Refer to Chapter 17 for i.MX6DQ, Chapter 18 for i.MX6SX */ + if (is_mx6sx() || is_mx7()) + nand_info->max_ecc_strength_supported = 62; + else + nand_info->max_ecc_strength_supported = 40; + +#ifdef CONFIG_NAND_MXS_USE_MINIMUM_ECC + nand_info->use_minimum_ecc = true; +#endif + if (mxs_nand_init(nand_info) < 0) goto err; -- cgit From 68748340c8613877d71b444c0dffe63b536d5a5f Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Fri, 22 Jun 2018 18:06:16 +0200 Subject: mtd: nand: mxs_nand: move structs into header file Move structs into header file so we can use a separate compile unit for device tree support. Signed-off-by: Stefan Agner --- drivers/mtd/nand/mxs_nand.c | 63 +++------------------------------------------ drivers/mtd/nand/mxs_nand.h | 62 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 60 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c index 3c9ee07be5..8748c927c6 100644 --- a/drivers/mtd/nand/mxs_nand.c +++ b/drivers/mtd/nand/mxs_nand.c @@ -13,12 +13,11 @@ */ #include -#include +#include #include #include #include #include -#include #include #include #include @@ -26,7 +25,6 @@ #include #include #include -#include #include "mxs_nand.h" #define MXS_NAND_DMA_DESCRIPTOR_COUNT 4 @@ -47,61 +45,6 @@ #define MXS_NAND_BCH_TIMEOUT 10000 -/** - * @gf_len: The length of Galois Field. (e.g., 13 or 14) - * @ecc_strength: A number that describes the strength of the ECC - * algorithm. - * @ecc_chunk_size: The size, in bytes, of a single ECC chunk. Note - * the first chunk in the page includes both data and - * metadata, so it's a bit larger than this value. - * @ecc_chunk_count: The number of ECC chunks in the page, - * @block_mark_byte_offset: The byte offset in the ECC-based page view at - * which the underlying physical block mark appears. - * @block_mark_bit_offset: The bit offset into the ECC-based page view at - * which the underlying physical block mark appears. - */ -struct bch_geometry { - unsigned int gf_len; - unsigned int ecc_strength; - unsigned int ecc_chunk_size; - unsigned int ecc_chunk_count; - unsigned int block_mark_byte_offset; - unsigned int block_mark_bit_offset; -}; - -struct mxs_nand_info { - struct nand_chip chip; - unsigned int max_ecc_strength_supported; - bool use_minimum_ecc; - int cur_chip; - - uint32_t cmd_queue_len; - uint32_t data_buf_size; - struct bch_geometry bch_geometry; - - uint8_t *cmd_buf; - uint8_t *data_buf; - uint8_t *oob_buf; - - uint8_t marking_block_bad; - uint8_t raw_oob_mode; - - struct mxs_gpmi_regs *gpmi_regs; - struct mxs_bch_regs *bch_regs; - - /* Functions with altered behaviour */ - int (*hooked_read_oob)(struct mtd_info *mtd, - loff_t from, struct mtd_oob_ops *ops); - int (*hooked_write_oob)(struct mtd_info *mtd, - loff_t to, struct mtd_oob_ops *ops); - int (*hooked_block_markbad)(struct mtd_info *mtd, - loff_t ofs); - - /* DMA descriptors */ - struct mxs_dma_desc **desc; - uint32_t desc_index; -}; - struct nand_ecclayout fake_ecc_layout; /* @@ -1233,7 +1176,7 @@ int mxs_nand_init_spl(struct nand_chip *nand) return 0; } -int mxs_nand_init(struct mxs_nand_info *nand_info) +int mxs_nand_init_ctrl(struct mxs_nand_info *nand_info) { struct mtd_info *mtd; struct nand_chip *nand; @@ -1328,7 +1271,7 @@ void board_nand_init(void) nand_info->use_minimum_ecc = true; #endif - if (mxs_nand_init(nand_info) < 0) + if (mxs_nand_init_ctrl(nand_info) < 0) goto err; return; diff --git a/drivers/mtd/nand/mxs_nand.h b/drivers/mtd/nand/mxs_nand.h index 379ed24f05..4bd65cded9 100644 --- a/drivers/mtd/nand/mxs_nand.h +++ b/drivers/mtd/nand/mxs_nand.h @@ -7,5 +7,67 @@ * Stefan Agner */ +#include +#include +#include +#include + +/** + * @gf_len: The length of Galois Field. (e.g., 13 or 14) + * @ecc_strength: A number that describes the strength of the ECC + * algorithm. + * @ecc_chunk_size: The size, in bytes, of a single ECC chunk. Note + * the first chunk in the page includes both data and + * metadata, so it's a bit larger than this value. + * @ecc_chunk_count: The number of ECC chunks in the page, + * @block_mark_byte_offset: The byte offset in the ECC-based page view at + * which the underlying physical block mark appears. + * @block_mark_bit_offset: The bit offset into the ECC-based page view at + * which the underlying physical block mark appears. + */ +struct bch_geometry { + unsigned int gf_len; + unsigned int ecc_strength; + unsigned int ecc_chunk_size; + unsigned int ecc_chunk_count; + unsigned int block_mark_byte_offset; + unsigned int block_mark_bit_offset; +}; + +struct mxs_nand_info { + struct nand_chip chip; + struct udevice *dev; + unsigned int max_ecc_strength_supported; + bool use_minimum_ecc; + int cur_chip; + + uint32_t cmd_queue_len; + uint32_t data_buf_size; + struct bch_geometry bch_geometry; + + uint8_t *cmd_buf; + uint8_t *data_buf; + uint8_t *oob_buf; + + uint8_t marking_block_bad; + uint8_t raw_oob_mode; + + struct mxs_gpmi_regs *gpmi_regs; + struct mxs_bch_regs *bch_regs; + + /* Functions with altered behaviour */ + int (*hooked_read_oob)(struct mtd_info *mtd, + loff_t from, struct mtd_oob_ops *ops); + int (*hooked_write_oob)(struct mtd_info *mtd, + loff_t to, struct mtd_oob_ops *ops); + int (*hooked_block_markbad)(struct mtd_info *mtd, + loff_t ofs); + + /* DMA descriptors */ + struct mxs_dma_desc **desc; + uint32_t desc_index; +}; + +int mxs_nand_init_ctrl(struct mxs_nand_info *nand_info); int mxs_nand_init_spl(struct nand_chip *nand); int mxs_nand_setup_ecc(struct mtd_info *mtd); -- cgit From f75e83bfae2bcf36197e25b8b3d539b0652b83fa Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Fri, 22 Jun 2018 18:06:17 +0200 Subject: mtd: nand: mxs_nand: add device tree support Support driver data from device tree. Also support fsl,use-minimal-ecc similar to Linux' GPMI NAND driver. Signed-off-by: Stefan Agner --- drivers/mtd/nand/Kconfig | 7 ++++ drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/mxs_nand.c | 5 +++ drivers/mtd/nand/mxs_nand_dt.c | 86 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 99 insertions(+) create mode 100644 drivers/mtd/nand/mxs_nand_dt.c (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index c039b9cc60..bdc272142e 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -154,6 +154,13 @@ config NAND_MXS if NAND_MXS +config NAND_MXS_DT + bool "Support MXS NAND controller as a DT device" + depends on OF_CONTROL && MTD + help + Enable the driver for MXS NAND flash on platforms using + device tree. + config NAND_MXS_USE_MINIMUM_ECC bool "Use minimum ECC strength supported by the controller" default false diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index e20ef07773..e00cbca0a9 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -55,6 +55,7 @@ obj-$(CONFIG_NAND_LPC32XX_SLC) += lpc32xx_nand_slc.o obj-$(CONFIG_NAND_VF610_NFC) += vf610_nfc.o obj-$(CONFIG_NAND_MXC) += mxc_nand.o obj-$(CONFIG_NAND_MXS) += mxs_nand.o +obj-$(CONFIG_NAND_MXS_DT) += mxs_nand_dt.o obj-$(CONFIG_NAND_PXA3XX) += pxa3xx_nand.o obj-$(CONFIG_NAND_SPEAR) += spr_nand.o obj-$(CONFIG_TEGRA_NAND) += tegra_nand.o diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c index 8748c927c6..99f392ef5c 100644 --- a/drivers/mtd/nand/mxs_nand.c +++ b/drivers/mtd/nand/mxs_nand.c @@ -1201,6 +1201,9 @@ int mxs_nand_init_ctrl(struct mxs_nand_info *nand_info) nand_set_controller_data(nand, nand_info); nand->options |= NAND_NO_SUBPAGE_WRITE; + if (nand_info->dev) + nand->flash_node = dev_of_offset(nand_info->dev); + nand->cmd_ctrl = mxs_nand_cmd_ctrl; nand->dev_ready = mxs_nand_device_ready; @@ -1247,6 +1250,7 @@ err_free_buffers: return err; } +#ifndef CONFIG_NAND_MXS_DT void board_nand_init(void) { struct mxs_nand_info *nand_info; @@ -1279,3 +1283,4 @@ void board_nand_init(void) err: free(nand_info); } +#endif diff --git a/drivers/mtd/nand/mxs_nand_dt.c b/drivers/mtd/nand/mxs_nand_dt.c new file mode 100644 index 0000000000..f89eb091a9 --- /dev/null +++ b/drivers/mtd/nand/mxs_nand_dt.c @@ -0,0 +1,86 @@ +/* + * NXP GPMI NAND flash driver (DT initialization) + * + * Copyright (C) 2018 Toradex + * Authors: + * Stefan Agner + * + * Based on denali_dt.c + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include + +#include "mxs_nand.h" + +struct mxs_nand_dt_data { + unsigned int max_ecc_strength_supported; +}; + +static const struct mxs_nand_dt_data mxs_nand_imx7d_data = { + .max_ecc_strength_supported = 62, +}; + +static const struct udevice_id mxs_nand_dt_ids[] = { + { + .compatible = "fsl,imx7d-gpmi-nand", + .data = (unsigned long)&mxs_nand_imx7d_data, + }, + { /* sentinel */ } +}; + +static int mxs_nand_dt_probe(struct udevice *dev) +{ + struct mxs_nand_info *info = dev_get_priv(dev); + const struct mxs_nand_dt_data *data; + struct resource res; + int ret; + + data = (void *)dev_get_driver_data(dev); + if (data) + info->max_ecc_strength_supported = data->max_ecc_strength_supported; + + info->dev = dev; + + ret = dev_read_resource_byname(dev, "gpmi-nand", &res); + if (ret) + return ret; + + info->gpmi_regs = devm_ioremap(dev, res.start, resource_size(&res)); + + + ret = dev_read_resource_byname(dev, "bch", &res); + if (ret) + return ret; + + info->bch_regs = devm_ioremap(dev, res.start, resource_size(&res)); + + info->use_minimum_ecc = dev_read_bool(dev, "fsl,use-minimum-ecc"); + + return mxs_nand_init_ctrl(info); +} + +U_BOOT_DRIVER(mxs_nand_dt) = { + .name = "mxs-nand-dt", + .id = UCLASS_MTD, + .of_match = mxs_nand_dt_ids, + .probe = mxs_nand_dt_probe, + .priv_auto_alloc_size = sizeof(struct mxs_nand_info), +}; + +void board_nand_init(void) +{ + struct udevice *dev; + int ret; + + ret = uclass_get_device_by_driver(UCLASS_MTD, + DM_GET_DRIVER(mxs_nand_dt), + &dev); + if (ret && ret != -ENODEV) + pr_err("Failed to initialize MXS NAND controller. (error %d)\n", + ret); +} -- cgit From 627544506f5709bb2d14f0db66661a27cd78ec0a Mon Sep 17 00:00:00 2001 From: Stefan Agner Date: Fri, 22 Jun 2018 18:06:18 +0200 Subject: mtd: nand: mxs_nand: add support for specific ECC strength Add support for specified ECC strength/size using device tree properties nand-ecc-strength/nand-ecc-step-size. This aligns behavior with the mainline driver, such that: - If fsl,use-minimal-ecc is requested it will use data from data sheet/ONFI. If this is not available the driver will fail. - If nand-ecc-strength/nand-ecc-step-size are specified those value will be used. - By default maximum possible ECC strength is used Signed-off-by: Stefan Agner --- drivers/mtd/nand/mxs_nand.c | 44 ++++++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 14 deletions(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c index 99f392ef5c..e3341812a2 100644 --- a/drivers/mtd/nand/mxs_nand.c +++ b/drivers/mtd/nand/mxs_nand.c @@ -161,15 +161,14 @@ static inline int mxs_nand_calc_mark_offset(struct bch_geometry *geo, } static inline int mxs_nand_calc_ecc_layout_by_info(struct bch_geometry *geo, - struct mtd_info *mtd) + struct mtd_info *mtd, + unsigned int ecc_strength, + unsigned int ecc_step) { struct nand_chip *chip = mtd_to_nand(mtd); struct mxs_nand_info *nand_info = nand_get_controller_data(chip); - if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0)) - return -ENOTSUPP; - - switch (chip->ecc_step_ds) { + switch (ecc_step) { case SZ_512: geo->gf_len = 13; break; @@ -180,8 +179,8 @@ static inline int mxs_nand_calc_ecc_layout_by_info(struct bch_geometry *geo, return -EINVAL; } - geo->ecc_chunk_size = chip->ecc_step_ds; - geo->ecc_strength = round_up(chip->ecc_strength_ds, 2); + geo->ecc_chunk_size = ecc_step; + geo->ecc_strength = round_up(ecc_strength, 2); /* Keep the C >= O */ if (geo->ecc_chunk_size < mtd->oobsize) @@ -964,6 +963,28 @@ static int mxs_nand_block_bad(struct mtd_info *mtd, loff_t ofs) return 0; } +static int mxs_nand_set_geometry(struct mtd_info *mtd, struct bch_geometry *geo) +{ + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_chip *nand = mtd_to_nand(mtd); + struct mxs_nand_info *nand_info = nand_get_controller_data(nand); + + if (chip->ecc.strength > 0 && chip->ecc.size > 0) + return mxs_nand_calc_ecc_layout_by_info(geo, mtd, + chip->ecc.strength, chip->ecc.size); + + if (nand_info->use_minimum_ecc || + mxs_nand_calc_ecc_layout(geo, mtd)) { + if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0)) + return -EINVAL; + + return mxs_nand_calc_ecc_layout_by_info(geo, mtd, + chip->ecc_strength_ds, chip->ecc_step_ds); + } + + return 0; +} + /* * At this point, the physical NAND Flash chips have been identified and * counted, so we know the physical geometry. This enables us to make some @@ -980,14 +1001,9 @@ int mxs_nand_setup_ecc(struct mtd_info *mtd) struct bch_geometry *geo = &nand_info->bch_geometry; struct mxs_bch_regs *bch_regs = nand_info->bch_regs; uint32_t tmp; - int ret = -ENOTSUPP; - - if (nand_info->use_minimum_ecc) - ret = mxs_nand_calc_ecc_layout_by_info(geo, mtd); - - if (ret == -ENOTSUPP) - ret = mxs_nand_calc_ecc_layout(geo, mtd); + int ret; + ret = mxs_nand_set_geometry(mtd, geo); if (ret) return ret; -- cgit From da37d096820e15b2bcdb0243da2dc01707c650f2 Mon Sep 17 00:00:00 2001 From: Jörg Krause Date: Sun, 14 Jan 2018 19:26:37 +0100 Subject: mtd: nand: export nand_get_flash_type function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `nand_get_flash_type()` allows identification of supported NAND flashs. The function is useful in SPL (like mxs_nand_spl.c) to lookup for a NAND flash (which does not support ONFi) instead of using nand_simple.c and hard-coding all required NAND parameters. Signed-off-by: Jörg Krause --- drivers/mtd/nand/nand_base.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index eb9f121f81..64e4621aaa 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -3755,7 +3755,7 @@ static bool find_full_id_nand(struct mtd_info *mtd, struct nand_chip *chip, /* * Get the flash and manufacturer id and lookup if the type is supported. */ -static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, +struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, struct nand_chip *chip, int *maf_id, int *dev_id, struct nand_flash_dev *type) @@ -3927,6 +3927,7 @@ ident_done: mtd->erasesize >> 10, mtd->writesize, mtd->oobsize); return type; } +EXPORT_SYMBOL(nand_get_flash_type); #if CONFIG_IS_ENABLED(OF_CONTROL) DECLARE_GLOBAL_DATA_PTR; -- cgit From 15e207faa0c32b587c173844936cadd7bf8dee01 Mon Sep 17 00:00:00 2001 From: Jörg Krause Date: Sun, 14 Jan 2018 19:26:38 +0100 Subject: spl, nand: add option CONFIG_SPL_NAND_IDENT to lookup for supported NAND chips MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the config option `CONFIG_SPL_NAND_IDENT` for using the NAND chip ID list to identify the NAND flash in SPL. Signed-off-by: Jörg Krause --- drivers/mtd/nand/Makefile | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index e00cbca0a9..c61e3f3839 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_SPL_NAND_SIMPLE) += nand_spl_simple.o obj-$(CONFIG_SPL_NAND_LOAD) += nand_spl_load.o obj-$(CONFIG_SPL_NAND_ECC) += nand_ecc.o obj-$(CONFIG_SPL_NAND_BASE) += nand_base.o +obj-$(CONFIG_SPL_NAND_IDENT) += nand_ids.o nand_timings.o obj-$(CONFIG_SPL_NAND_INIT) += nand.o ifeq ($(CONFIG_SPL_ENV_SUPPORT),y) obj-$(CONFIG_ENV_IS_IN_NAND) += nand_util.o -- cgit From f3f2af3bdf2af89d0621aa0fbd94a918e4447081 Mon Sep 17 00:00:00 2001 From: Jörg Krause Date: Sun, 14 Jan 2018 19:26:39 +0100 Subject: mtd: nand: mxs_nand_spl: refactor mxs_flash_ident MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existing `mxs_flash_ident()` is limited to identify ONFi compliant NAND chips only. In order to support non-ONFi NAND chips refactor the function and rename it to `mxs_flash_onfi_ident()`. A follow-up patch will add `mxs_flash_full_ident()` which allows to use the chip ID list to lookup for supported NAND flashs. Signed-off-by: Jörg Krause --- drivers/mtd/nand/mxs_nand_spl.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/mxs_nand_spl.c b/drivers/mtd/nand/mxs_nand_spl.c index 47857a81bc..adb12c531e 100644 --- a/drivers/mtd/nand/mxs_nand_spl.c +++ b/drivers/mtd/nand/mxs_nand_spl.c @@ -49,7 +49,7 @@ static void mxs_nand_command(struct mtd_info *mtd, unsigned int command, } } -static int mxs_flash_ident(struct mtd_info *mtd) +static int mxs_flash_onfi_ident(struct mtd_info *mtd) { register struct nand_chip *chip = mtd_to_nand(mtd); int i; @@ -109,6 +109,13 @@ static int mxs_flash_ident(struct mtd_info *mtd) return 0; } +static int mxs_flash_ident(struct mtd_info *mtd) +{ + int ret; + ret = mxs_flash_onfi_ident(mtd); + return ret; +} + static int mxs_read_page_ecc(struct mtd_info *mtd, void *buf, unsigned int page) { register struct nand_chip *chip = mtd_to_nand(mtd); -- cgit From 4368f85359b947da7f151265d8969d6af1235357 Mon Sep 17 00:00:00 2001 From: Jörg Krause Date: Sun, 14 Jan 2018 19:26:40 +0100 Subject: mtd: nand: mxs_nand_spl: add mxs_flash_full_ident MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For now, the existing SPL MXS NAND driver only supports to identify ONFi-compliant NAND chips. In order to allow identifying non-ONFi-compliant chips add `mxs_flash_full_ident()` which uses the `nand_get_flash_type()` functionality from `nand_base.c` to lookup for supported NAND chips in the chip ID list. For compatibility reason the full identification support is only available if the config option `CONFIG_SPL_NAND_IDENT` is enabled. The lookup was tested on a custom i.MX6ULL board with a Toshiba TC58NVG1S3HTAI0 NAND chip. Signed-off-by: Jörg Krause --- drivers/mtd/nand/mxs_nand_spl.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'drivers/mtd') diff --git a/drivers/mtd/nand/mxs_nand_spl.c b/drivers/mtd/nand/mxs_nand_spl.c index adb12c531e..2d7bbe83cc 100644 --- a/drivers/mtd/nand/mxs_nand_spl.c +++ b/drivers/mtd/nand/mxs_nand_spl.c @@ -49,6 +49,28 @@ static void mxs_nand_command(struct mtd_info *mtd, unsigned int command, } } +#if defined (CONFIG_SPL_NAND_IDENT) + +/* Trying to detect the NAND flash using ONFi, JEDEC, and (extended) IDs */ +static int mxs_flash_full_ident(struct mtd_info *mtd) +{ + int nand_maf_id, nand_dev_id; + struct nand_chip *chip = mtd_to_nand(mtd); + struct nand_flash_dev *type; + + type = nand_get_flash_type(mtd, chip, &nand_maf_id, &nand_dev_id, NULL); + + if (IS_ERR(type)) { + chip->select_chip(mtd, -1); + return PTR_ERR(type); + } + + return 0; +} + +#else + +/* Trying to detect the NAND flash using ONFi only */ static int mxs_flash_onfi_ident(struct mtd_info *mtd) { register struct nand_chip *chip = mtd_to_nand(mtd); @@ -109,10 +131,16 @@ static int mxs_flash_onfi_ident(struct mtd_info *mtd) return 0; } +#endif /* CONFIG_SPL_NAND_IDENT */ + static int mxs_flash_ident(struct mtd_info *mtd) { int ret; +#if defined (CONFIG_SPL_NAND_IDENT) + ret = mxs_flash_full_ident(mtd); +#else ret = mxs_flash_onfi_ident(mtd); +#endif return ret; } -- cgit