diff options
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/nand/raw/Kconfig | 6 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/mxs_nand.c | 330 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/mxs_nand_dt.c | 91 | ||||
-rw-r--r-- | drivers/mtd/nand/raw/mxs_nand_spl.c | 41 | ||||
-rw-r--r-- | drivers/mtd/spi/spi-nor-core.c | 19 | ||||
-rw-r--r-- | drivers/mtd/spi/spi-nor-ids.c | 1 |
6 files changed, 392 insertions, 96 deletions
diff --git a/drivers/mtd/nand/raw/Kconfig b/drivers/mtd/nand/raw/Kconfig index 23201ca720..c4d9d31388 100644 --- a/drivers/mtd/nand/raw/Kconfig +++ b/drivers/mtd/nand/raw/Kconfig @@ -259,12 +259,12 @@ config NAND_MXC config NAND_MXS bool "MXS NAND support" - depends on MX23 || MX28 || MX6 || MX7 + depends on MX23 || MX28 || MX6 || MX7 || IMX8 || IMX8M select SYS_NAND_SELF_INIT imply CMD_NAND select APBH_DMA - select APBH_DMA_BURST if ARCH_MX6 || ARCH_MX7 - select APBH_DMA_BURST8 if ARCH_MX6 || ARCH_MX7 + select APBH_DMA_BURST if ARCH_MX6 || ARCH_MX7 || ARCH_IMX8 || ARCH_IMX8M + select APBH_DMA_BURST8 if ARCH_MX6 || ARCH_MX7 || ARCH_IMX8 || ARCH_IMX8M help This enables NAND driver for the NAND flash controller on the MXS processors. diff --git a/drivers/mtd/nand/raw/mxs_nand.c b/drivers/mtd/nand/raw/mxs_nand.c index fe8097c146..e3516cd141 100644 --- a/drivers/mtd/nand/raw/mxs_nand.c +++ b/drivers/mtd/nand/raw/mxs_nand.c @@ -10,6 +10,7 @@ * * Copyright (C) 2010 Freescale Semiconductor, Inc. * Copyright (C) 2008 Embedded Alley Solutions, Inc. + * Copyright 2017-2019 NXP */ #include <common.h> @@ -30,7 +31,8 @@ #define MXS_NAND_DMA_DESCRIPTOR_COUNT 4 -#if (defined(CONFIG_MX6) || defined(CONFIG_MX7)) +#if defined(CONFIG_MX6) || defined(CONFIG_MX7) || defined(CONFIG_IMX8) || \ + defined(CONFIG_IMX8M) #define MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT 2 #else #define MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT 0 @@ -54,21 +56,21 @@ struct nand_ecclayout fake_ecc_layout; #if !CONFIG_IS_ENABLED(SYS_DCACHE_OFF) static void mxs_nand_flush_data_buf(struct mxs_nand_info *info) { - uint32_t addr = (uint32_t)info->data_buf; + uint32_t addr = (uintptr_t)info->data_buf; flush_dcache_range(addr, addr + info->data_buf_size); } static void mxs_nand_inval_data_buf(struct mxs_nand_info *info) { - uint32_t addr = (uint32_t)info->data_buf; + uint32_t addr = (uintptr_t)info->data_buf; invalidate_dcache_range(addr, addr + info->data_buf_size); } static void mxs_nand_flush_cmd_buf(struct mxs_nand_info *info) { - uint32_t addr = (uint32_t)info->cmd_buf; + uint32_t addr = (uintptr_t)info->cmd_buf; flush_dcache_range(addr, addr + MXS_NAND_COMMAND_BUFFER_SIZE); } @@ -112,53 +114,32 @@ static uint32_t mxs_nand_aux_status_offset(void) return (MXS_NAND_METADATA_SIZE + 0x3) & ~0x3; } -static inline int mxs_nand_calc_mark_offset(struct bch_geometry *geo, - uint32_t page_data_size) +static inline bool mxs_nand_bbm_in_data_chunk(struct bch_geometry *geo, struct mtd_info *mtd, + unsigned int *chunk_num) { - 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; + unsigned int i, j; - chunk_total_size_in_bits = - chunk_data_size_in_bits + chunk_ecc_size_in_bits; - - /* Compute the bit offset of the block mark within the physical page. */ - block_mark_bit_offset = page_data_size * 8; - - /* Subtract the metadata bits. */ - block_mark_bit_offset -= MXS_NAND_METADATA_SIZE * 8; - - /* - * Compute the chunk number (starting at zero) in which the block mark - * appears. - */ - block_mark_chunk_number = - block_mark_bit_offset / chunk_total_size_in_bits; - - /* - * Compute the bit offset of the block mark within its chunk, and - * validate it. - */ - block_mark_chunk_bit_offset = block_mark_bit_offset - - (block_mark_chunk_number * chunk_total_size_in_bits); + if (geo->ecc_chunk0_size != geo->ecc_chunkn_size) { + dev_err(this->dev, "The size of chunk0 must equal to chunkn\n"); + return false; + } - if (block_mark_chunk_bit_offset > chunk_data_size_in_bits) - return -EINVAL; + i = (mtd->writesize * 8 - MXS_NAND_METADATA_SIZE * 8) / + (geo->gf_len * geo->ecc_strength + + geo->ecc_chunkn_size * 8); - /* - * Now that we know the chunk number in which the block mark appears, - * we can subtract all the ECC bits that appear before it. - */ - block_mark_bit_offset -= - block_mark_chunk_number * chunk_ecc_size_in_bits; + j = (mtd->writesize * 8 - MXS_NAND_METADATA_SIZE * 8) - + (geo->gf_len * geo->ecc_strength + + geo->ecc_chunkn_size * 8) * i; - geo->block_mark_byte_offset = block_mark_bit_offset >> 3; - geo->block_mark_bit_offset = block_mark_bit_offset & 0x7; + if (j < geo->ecc_chunkn_size * 8) { + *chunk_num = i + 1; + dev_dbg(this->dev, "Set ecc to %d and bbm in chunk %d\n", + geo->ecc_strength, *chunk_num); + return true; + } - return 0; + return false; } static inline int mxs_nand_calc_ecc_layout_by_info(struct bch_geometry *geo, @@ -168,6 +149,7 @@ static inline int mxs_nand_calc_ecc_layout_by_info(struct bch_geometry *geo, { struct nand_chip *chip = mtd_to_nand(mtd); struct mxs_nand_info *nand_info = nand_get_controller_data(chip); + unsigned int block_mark_bit_offset; switch (ecc_step) { case SZ_512: @@ -180,45 +162,51 @@ static inline int mxs_nand_calc_ecc_layout_by_info(struct bch_geometry *geo, return -EINVAL; } - geo->ecc_chunk_size = ecc_step; + geo->ecc_chunk0_size = ecc_step; + geo->ecc_chunkn_size = ecc_step; geo->ecc_strength = round_up(ecc_strength, 2); /* Keep the C >= O */ - if (geo->ecc_chunk_size < mtd->oobsize) + if (geo->ecc_chunkn_size < mtd->oobsize) return -EINVAL; if (geo->ecc_strength > nand_info->max_ecc_strength_supported) return -EINVAL; - geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunk_size; + geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunkn_size; + + /* For bit swap. */ + block_mark_bit_offset = mtd->writesize * 8 - + (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - 1) + + MXS_NAND_METADATA_SIZE * 8); + + geo->block_mark_byte_offset = block_mark_bit_offset / 8; + geo->block_mark_bit_offset = block_mark_bit_offset % 8; return 0; } -static inline int mxs_nand_calc_ecc_layout(struct bch_geometry *geo, +static inline int mxs_nand_legacy_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); + unsigned int block_mark_bit_offset; /* The default for the length of Galois Field. */ geo->gf_len = 13; /* The default for chunk size. */ - geo->ecc_chunk_size = 512; + geo->ecc_chunk0_size = 512; + geo->ecc_chunkn_size = 512; - if (geo->ecc_chunk_size < mtd->oobsize) { + if (geo->ecc_chunkn_size < mtd->oobsize) { geo->gf_len = 14; - geo->ecc_chunk_size *= 2; + geo->ecc_chunk0_size *= 2; + geo->ecc_chunkn_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; + geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunkn_size; /* * Determine the ECC layout with the formula: @@ -234,6 +222,84 @@ static inline int mxs_nand_calc_ecc_layout(struct bch_geometry *geo, geo->ecc_strength = min(round_down(geo->ecc_strength, 2), nand_info->max_ecc_strength_supported); + block_mark_bit_offset = mtd->writesize * 8 - + (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - 1) + + MXS_NAND_METADATA_SIZE * 8); + + geo->block_mark_byte_offset = block_mark_bit_offset / 8; + geo->block_mark_bit_offset = block_mark_bit_offset % 8; + + return 0; +} + +static inline int mxs_nand_calc_ecc_for_large_oob(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); + unsigned int block_mark_bit_offset; + unsigned int max_ecc; + unsigned int bbm_chunk; + unsigned int i; + + /* sanity check for the minimum ecc nand required */ + if (!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0)) + return -EINVAL; + geo->ecc_strength = chip->ecc_strength_ds; + + /* calculate the maximum ecc platform can support*/ + geo->gf_len = 14; + geo->ecc_chunk0_size = 1024; + geo->ecc_chunkn_size = 1024; + geo->ecc_chunk_count = mtd->writesize / geo->ecc_chunkn_size; + max_ecc = ((mtd->oobsize - MXS_NAND_METADATA_SIZE) * 8) + / (geo->gf_len * geo->ecc_chunk_count); + max_ecc = min(round_down(max_ecc, 2), + nand_info->max_ecc_strength_supported); + + + /* search a supported ecc strength that makes bbm */ + /* located in data chunk */ + geo->ecc_strength = chip->ecc_strength_ds; + while (!(geo->ecc_strength > max_ecc)) { + if (mxs_nand_bbm_in_data_chunk(geo, mtd, &bbm_chunk)) + break; + geo->ecc_strength += 2; + } + + /* if none of them works, keep using the minimum ecc */ + /* nand required but changing ecc page layout */ + if (geo->ecc_strength > max_ecc) { + geo->ecc_strength = chip->ecc_strength_ds; + /* add extra ecc for meta data */ + geo->ecc_chunk0_size = 0; + geo->ecc_chunk_count = (mtd->writesize / geo->ecc_chunkn_size) + 1; + geo->ecc_for_meta = 1; + /* check if oob can afford this extra ecc chunk */ + if (mtd->oobsize * 8 < MXS_NAND_METADATA_SIZE * 8 + + geo->gf_len * geo->ecc_strength + * geo->ecc_chunk_count) { + printf("unsupported NAND chip with new layout\n"); + return -EINVAL; + } + + /* calculate in which chunk bbm located */ + bbm_chunk = (mtd->writesize * 8 - MXS_NAND_METADATA_SIZE * 8 - + geo->gf_len * geo->ecc_strength) / + (geo->gf_len * geo->ecc_strength + + geo->ecc_chunkn_size * 8) + 1; + } + + /* calculate the number of ecc chunk behind the bbm */ + i = (mtd->writesize / geo->ecc_chunkn_size) - bbm_chunk + 1; + + block_mark_bit_offset = mtd->writesize * 8 - + (geo->ecc_strength * geo->gf_len * (geo->ecc_chunk_count - i) + + MXS_NAND_METADATA_SIZE * 8); + + geo->block_mark_byte_offset = block_mark_bit_offset / 8; + geo->block_mark_bit_offset = block_mark_bit_offset % 8; + return 0; } @@ -548,6 +614,45 @@ static uint8_t mxs_nand_read_byte(struct mtd_info *mtd) return buf; } +static bool mxs_nand_erased_page(struct mtd_info *mtd, struct nand_chip *nand, + u8 *buf, int chunk, int page) +{ + struct mxs_nand_info *nand_info = nand_get_controller_data(nand); + struct bch_geometry *geo = &nand_info->bch_geometry; + unsigned int flip_bits = 0, flip_bits_noecc = 0; + unsigned int threshold; + unsigned int base = geo->ecc_chunkn_size * chunk; + u32 *dma_buf = (u32 *)buf; + int i; + + threshold = geo->gf_len / 2; + if (threshold > geo->ecc_strength) + threshold = geo->ecc_strength; + + for (i = 0; i < geo->ecc_chunkn_size; i++) { + flip_bits += hweight8(~buf[base + i]); + if (flip_bits > threshold) + return false; + } + + nand->cmdfunc(mtd, NAND_CMD_READ0, 0, page); + nand->read_buf(mtd, buf, mtd->writesize); + + for (i = 0; i < mtd->writesize / 4; i++) { + flip_bits_noecc += hweight32(~dma_buf[i]); + if (flip_bits_noecc > threshold) + return false; + } + + mtd->ecc_stats.corrected += flip_bits; + + memset(buf, 0xff, mtd->writesize); + + printf("The page(%d) is an erased page(%d,%d,%d,%d).\n", page, chunk, threshold, flip_bits, flip_bits_noecc); + + return true; +} + /* * Read a page from NAND. */ @@ -557,11 +662,13 @@ static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand, { 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 = nand_info->bch_regs; struct mxs_dma_desc *d; uint32_t channel = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + nand_info->cur_chip; uint32_t corrected = 0, failed = 0; uint8_t *status; int i, ret; + int flag = 0; /* Compile the DMA descriptor - wait for ready. */ d = mxs_nand_get_dma_desc(nand_info); @@ -603,6 +710,12 @@ static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand, d->cmd.pio_words[4] = (dma_addr_t)nand_info->data_buf; d->cmd.pio_words[5] = (dma_addr_t)nand_info->oob_buf; + if (nand_info->en_randomizer) { + d->cmd.pio_words[2] |= GPMI_ECCCTRL_RANDOMIZER_ENABLE | + GPMI_ECCCTRL_RANDOMIZER_TYPE2; + d->cmd.pio_words[3] |= (page % 256) << 16; + } + mxs_dma_desc_append(channel, d); /* Compile the DMA descriptor - disable the BCH block. */ @@ -651,6 +764,8 @@ static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand, goto rtn; } + mxs_nand_return_dma_descs(nand_info); + /* Invalidate caches */ mxs_nand_inval_data_buf(nand_info); @@ -663,10 +778,19 @@ static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand, if (status[i] == 0x00) continue; - if (status[i] == 0xff) + if (status[i] == 0xff) { + if (!nand_info->en_randomizer && + (is_mx6dqp() || is_mx7() || is_mx6ul() || + is_imx8() || is_imx8m())) + if (readl(&bch_regs->hw_bch_debug1)) + flag = 1; continue; + } if (status[i] == 0xfe) { + if (mxs_nand_erased_page(mtd, nand, + nand_info->data_buf, i, page)) + break; failed++; continue; } @@ -693,6 +817,8 @@ static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand, memcpy(buf, nand_info->data_buf, mtd->writesize); + if (flag) + memset(buf, 0xff, mtd->writesize); rtn: mxs_nand_return_dma_descs(nand_info); @@ -741,7 +867,7 @@ static int mxs_nand_ecc_write_page(struct mtd_info *mtd, d->cmd.pio_words[4] = (dma_addr_t)nand_info->data_buf; d->cmd.pio_words[5] = (dma_addr_t)nand_info->oob_buf; - if (is_mx7() && nand_info->en_randomizer) { + if (nand_info->en_randomizer) { d->cmd.pio_words[2] |= GPMI_ECCCTRL_RANDOMIZER_ENABLE | GPMI_ECCCTRL_RANDOMIZER_TYPE2; /* @@ -751,7 +877,7 @@ static int mxs_nand_ecc_write_page(struct mtd_info *mtd, * The value is between 0-255. For additional details * check 9.6.6.4 of i.MX7D Applications Processor reference */ - d->cmd.pio_words[3] |= (page % 255) << 16; + d->cmd.pio_words[3] |= (page % 256) << 16; } mxs_dma_desc_append(channel, d); @@ -983,18 +1109,23 @@ static int mxs_nand_set_geometry(struct mtd_info *mtd, struct bch_geometry *geo) 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 (chip->ecc_strength_ds > nand_info->max_ecc_strength_supported) { + printf("unsupported NAND chip, minimum ecc required %d\n" + , chip->ecc_strength_ds); + return -EINVAL; + } - 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; + if ((!(chip->ecc_strength_ds > 0 && chip->ecc_step_ds > 0) && + mtd->oobsize < 1024) || nand_info->legacy_bch_geometry) { + dev_warn(this->dev, "use legacy bch geometry\n"); + return mxs_nand_legacy_calc_ecc_layout(geo, mtd); + } + + if (mtd->oobsize > 1024 || chip->ecc_step_ds < mtd->oobsize) + return mxs_nand_calc_ecc_for_large_oob(geo, mtd); - return mxs_nand_calc_ecc_layout_by_info(geo, mtd, + return mxs_nand_calc_ecc_layout_by_info(geo, mtd, chip->ecc_strength_ds, chip->ecc_step_ds); - } return 0; } @@ -1025,8 +1156,6 @@ int mxs_nand_setup_ecc(struct mtd_info *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); @@ -1034,7 +1163,7 @@ int mxs_nand_setup_ecc(struct mtd_info *mtd) tmp = (geo->ecc_chunk_count - 1) << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET; tmp |= MXS_NAND_METADATA_SIZE << BCH_FLASHLAYOUT0_META_SIZE_OFFSET; tmp |= (geo->ecc_strength >> 1) << BCH_FLASHLAYOUT0_ECC0_OFFSET; - tmp |= geo->ecc_chunk_size >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT; + tmp |= geo->ecc_chunk0_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); @@ -1043,12 +1172,18 @@ int mxs_nand_setup_ecc(struct mtd_info *mtd) tmp = (mtd->writesize + mtd->oobsize) << BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET; tmp |= (geo->ecc_strength >> 1) << BCH_FLASHLAYOUT1_ECCN_OFFSET; - tmp |= geo->ecc_chunk_size >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT; + tmp |= geo->ecc_chunkn_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); nand_info->bch_flash0layout1 = tmp; + /* Set erase threshold to ecc strength for mx6ul, mx6qp and mx7 */ + if (is_mx6dqp() || is_mx7() || + is_mx6ul() || is_imx8() || is_imx8m()) + writel(BCH_MODE_ERASE_THRESHOLD(geo->ecc_strength), + &bch_regs->hw_bch_mode); + /* Set *all* chip selects to use layout 0 */ writel(0, &bch_regs->hw_bch_layoutselect); @@ -1184,7 +1319,7 @@ int mxs_nand_init_spl(struct nand_chip *nand) nand_info->gpmi_regs = (struct mxs_gpmi_regs *)MXS_GPMI_BASE; nand_info->bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE; - if (is_mx6sx() || is_mx7()) + if (is_mx6sx() || is_mx7() || is_imx8() || is_imx8m()) nand_info->max_ecc_strength_supported = 62; else nand_info->max_ecc_strength_supported = 40; @@ -1268,7 +1403,7 @@ int mxs_nand_init_ctrl(struct mxs_nand_info *nand_info) nand->ecc.layout = &fake_ecc_layout; nand->ecc.mode = NAND_ECC_HW; - nand->ecc.size = nand_info->bch_geometry.ecc_chunk_size; + nand->ecc.size = nand_info->bch_geometry.ecc_chunkn_size; nand->ecc.strength = nand_info->bch_geometry.ecc_strength; /* second phase scan */ @@ -1347,12 +1482,14 @@ void mxs_nand_get_layout(struct mtd_info *mtd, struct mxs_nand_layout *l) BCH_FLASHLAYOUT1_DATAN_SIZE_OFFSET); l->eccn = (tmp & BCH_FLASHLAYOUT1_ECCN_MASK) >> BCH_FLASHLAYOUT1_ECCN_OFFSET; + l->gf_len = (tmp & BCH_FLASHLAYOUT1_GF13_0_GF14_1_MASK) >> + BCH_FLASHLAYOUT1_GF13_0_GF14_1_OFFSET; } /* * Set BCH to specific layout used by ROM bootloader to read FCB. */ -void mxs_nand_mode_fcb(struct mtd_info *mtd) +void mxs_nand_mode_fcb_62bit(struct mtd_info *mtd) { u32 tmp; struct mxs_bch_regs *bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE; @@ -1386,6 +1523,43 @@ void mxs_nand_mode_fcb(struct mtd_info *mtd) } /* + * Set BCH to specific layout used by ROM bootloader to read FCB. + */ +void mxs_nand_mode_fcb_40bit(struct mtd_info *mtd) +{ + u32 tmp; + struct mxs_bch_regs *bch_regs = (struct mxs_bch_regs *)MXS_BCH_BASE; + struct nand_chip *nand = mtd_to_nand(mtd); + struct mxs_nand_info *nand_info = nand_get_controller_data(nand); + + /* no randomizer in this setting*/ + nand_info->en_randomizer = 0; + + mtd->writesize = 1024; + mtd->oobsize = 1576 - 1024; + + /* 8 ecc_chunks_*/ + tmp = 7 << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET; + /* 32 bytes for metadata */ + tmp |= 32 << BCH_FLASHLAYOUT0_META_SIZE_OFFSET; + /* using ECC40 level to be performed */ + tmp |= 0x14 << BCH_FLASHLAYOUT0_ECC0_OFFSET; + /* 0x20 * 4 bytes of the data0 block */ + tmp |= 0x20 << BCH_FLASHLAYOUT0_DATA0_SIZE_OFFSET; + tmp |= 0 << BCH_FLASHLAYOUT0_GF13_0_GF14_1_OFFSET; + writel(tmp, &bch_regs->hw_bch_flash0layout0); + + /* 1024 for data + 552 for OOB */ + tmp = 1576 << BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET; + /* using ECC40 level to be performed */ + tmp |= 0x14 << BCH_FLASHLAYOUT1_ECCN_OFFSET; + /* 0x20 * 4 bytes of the data0 block */ + tmp |= 0x20 << BCH_FLASHLAYOUT1_DATAN_SIZE_OFFSET; + tmp |= 0 << BCH_FLASHLAYOUT1_GF13_0_GF14_1_OFFSET; + writel(tmp, &bch_regs->hw_bch_flash0layout1); +} + +/* * Restore BCH to normal settings. */ void mxs_nand_mode_normal(struct mtd_info *mtd) diff --git a/drivers/mtd/nand/raw/mxs_nand_dt.c b/drivers/mtd/nand/raw/mxs_nand_dt.c index 8ad7d618c6..43dbe9e66e 100644 --- a/drivers/mtd/nand/raw/mxs_nand_dt.c +++ b/drivers/mtd/nand/raw/mxs_nand_dt.c @@ -2,6 +2,8 @@ * NXP GPMI NAND flash driver (DT initialization) * * Copyright (C) 2018 Toradex + * Copyright 2019 NXP + * * Authors: * Stefan Agner <stefan.agner@toradex.com> * @@ -14,6 +16,7 @@ #include <linux/io.h> #include <linux/ioport.h> #include <linux/printk.h> +#include <clk.h> #include <mxs_nand.h> @@ -25,19 +28,39 @@ static const struct mxs_nand_dt_data mxs_nand_imx6q_data = { .max_ecc_strength_supported = 40, }; +static const struct mxs_nand_dt_data mxs_nand_imx6sx_data = { + .max_ecc_strength_supported = 62, +}; + static const struct mxs_nand_dt_data mxs_nand_imx7d_data = { .max_ecc_strength_supported = 62, }; +static const struct mxs_nand_dt_data mxs_nand_imx8qxp_data = { + .max_ecc_strength_supported = 62, +}; + static const struct udevice_id mxs_nand_dt_ids[] = { { .compatible = "fsl,imx6q-gpmi-nand", .data = (unsigned long)&mxs_nand_imx6q_data, }, { + .compatible = "fsl,imx6qp-gpmi-nand", + .data = (unsigned long)&mxs_nand_imx6q_data, + }, + { + .compatible = "fsl,imx6sx-gpmi-nand", + .data = (unsigned long)&mxs_nand_imx6sx_data, + }, + { .compatible = "fsl,imx7d-gpmi-nand", .data = (unsigned long)&mxs_nand_imx7d_data, }, + { + .compatible = "fsl,imx8qxp-gpmi-nand", + .data = (unsigned long)&mxs_nand_imx8qxp_data, + }, { /* sentinel */ } }; @@ -69,6 +92,74 @@ static int mxs_nand_dt_probe(struct udevice *dev) info->use_minimum_ecc = dev_read_bool(dev, "fsl,use-minimum-ecc"); + info->legacy_bch_geometry = dev_read_bool(dev, "fsl,legacy-bch-geometry"); + + if (IS_ENABLED(CONFIG_CLK) && IS_ENABLED(CONFIG_IMX8)) { + /* Assigned clock already set clock */ + struct clk gpmi_clk; + + ret = clk_get_by_name(dev, "gpmi_io", &gpmi_clk); + if (ret < 0) { + debug("Can't get gpmi io clk: %d\n", ret); + return ret; + } + + ret = clk_enable(&gpmi_clk); + if (ret < 0) { + debug("Can't enable gpmi io clk: %d\n", ret); + return ret; + } + + ret = clk_get_by_name(dev, "gpmi_apb", &gpmi_clk); + if (ret < 0) { + debug("Can't get gpmi_apb clk: %d\n", ret); + return ret; + } + + ret = clk_enable(&gpmi_clk); + if (ret < 0) { + debug("Can't enable gpmi_apb clk: %d\n", ret); + return ret; + } + + ret = clk_get_by_name(dev, "gpmi_bch", &gpmi_clk); + if (ret < 0) { + debug("Can't get gpmi_bch clk: %d\n", ret); + return ret; + } + + ret = clk_enable(&gpmi_clk); + if (ret < 0) { + debug("Can't enable gpmi_bch clk: %d\n", ret); + return ret; + } + + ret = clk_get_by_name(dev, "gpmi_apb_bch", &gpmi_clk); + if (ret < 0) { + debug("Can't get gpmi_apb_bch clk: %d\n", ret); + return ret; + } + + ret = clk_enable(&gpmi_clk); + if (ret < 0) { + debug("Can't enable gpmi_apb_bch clk: %d\n", ret); + return ret; + } + + /* this clock is used for apbh_dma, since the apbh dma does not support DM, + * we optionally enable it here + */ + ret = clk_get_by_name(dev, "gpmi_apbh_dma", &gpmi_clk); + if (ret < 0) { + debug("Can't get gpmi_apbh_dma clk: %d\n", ret); + } else { + ret = clk_enable(&gpmi_clk); + if (ret < 0) { + debug("Can't enable gpmi_apbh_dma clk: %d\n", ret); + } + } + } + return mxs_nand_init_ctrl(info); } diff --git a/drivers/mtd/nand/raw/mxs_nand_spl.c b/drivers/mtd/nand/raw/mxs_nand_spl.c index a653dfa5ed..ae50cc37b5 100644 --- a/drivers/mtd/nand/raw/mxs_nand_spl.c +++ b/drivers/mtd/nand/raw/mxs_nand_spl.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2014 Gateworks Corporation + * Copyright 2019 NXP * Author: Tim Harvey <tharvey@gateworks.com> */ #include <common.h> @@ -38,6 +39,12 @@ static void mxs_nand_command(struct mtd_info *mtd, unsigned int command, if (command == NAND_CMD_READ0) { chip->cmd_ctrl(mtd, NAND_CMD_READSTART, NAND_CLE); chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0); + } else if (command == NAND_CMD_RNDOUT) { + /* No ready / busy check necessary */ + chip->cmd_ctrl(mtd, NAND_CMD_RNDOUTSTART, + NAND_NCE | NAND_CLE); + chip->cmd_ctrl(mtd, NAND_CMD_NONE, + NAND_NCE); } /* wait for nand ready */ @@ -212,23 +219,39 @@ int nand_spl_load_image(uint32_t offs, unsigned int size, void *buf) unsigned int page; unsigned int nand_page_per_block; unsigned int sz = 0; + u8 *page_buf = NULL; + u32 page_off; chip = mtd_to_nand(mtd); if (!chip->numchips) return -ENODEV; + + page_buf = malloc(mtd->writesize); + if (!page_buf) + return -ENOMEM; + page = offs >> chip->page_shift; + page_off = offs & (mtd->writesize - 1); nand_page_per_block = mtd->erasesize / mtd->writesize; - debug("%s offset:0x%08x len:%d page:%d\n", __func__, offs, size, page); + debug("%s offset:0x%08x len:%d page:%x\n", __func__, offs, size, page); - size = roundup(size, mtd->writesize); - while (sz < size) { - if (mxs_read_page_ecc(mtd, buf, page) < 0) + while (size) { + if (mxs_read_page_ecc(mtd, page_buf, page) < 0) return -1; - sz += mtd->writesize; + + if (size > (mtd->writesize - page_off)) + sz = (mtd->writesize - page_off); + else + sz = size; + + memcpy(buf, page_buf + page_off, sz); + offs += mtd->writesize; page++; - buf += mtd->writesize; + buf += (mtd->writesize - page_off); + page_off = 0; + size -= sz; /* * Check if we have crossed a block boundary, and if so @@ -242,12 +265,16 @@ int nand_spl_load_image(uint32_t offs, unsigned int size, void *buf) while (is_badblock(mtd, offs, 1)) { page = page + nand_page_per_block; /* Check i we've reached the end of flash. */ - if (page >= mtd->size >> chip->page_shift) + if (page >= mtd->size >> chip->page_shift) { + free(page_buf); return -ENOMEM; + } } } } + free(page_buf); + return 0; } diff --git a/drivers/mtd/spi/spi-nor-core.c b/drivers/mtd/spi/spi-nor-core.c index e840c60f27..3d4361493e 100644 --- a/drivers/mtd/spi/spi-nor-core.c +++ b/drivers/mtd/spi/spi-nor-core.c @@ -56,8 +56,7 @@ static int spi_nor_read_reg(struct spi_nor *nor, u8 code, u8 *val, int len) ret = spi_nor_read_write_reg(nor, &op, val); if (ret < 0) - dev_dbg(&flash->spimem->spi->dev, "error %d reading %x\n", ret, - code); + dev_dbg(nor->dev, "error %d reading %x\n", ret, code); return ret; } @@ -1374,7 +1373,8 @@ static int spansion_read_cr_quad_enable(struct spi_nor *nor) /* Check current Quad Enable bit value. */ ret = read_cr(nor); if (ret < 0) { - dev_dbg(dev, "error while reading configuration register\n"); + dev_dbg(nor->dev, + "error while reading configuration register\n"); return -EINVAL; } @@ -1386,7 +1386,7 @@ static int spansion_read_cr_quad_enable(struct spi_nor *nor) /* Keep the current value of the Status Register. */ ret = read_sr(nor); if (ret < 0) { - dev_dbg(dev, "error while reading status register\n"); + dev_dbg(nor->dev, "error while reading status register\n"); return -EINVAL; } sr_cr[0] = ret; @@ -2069,7 +2069,8 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor, err = spi_nor_read_sfdp(nor, sizeof(header), psize, param_headers); if (err < 0) { - dev_err(dev, "failed to read SFDP parameter headers\n"); + dev_err(nor->dev, + "failed to read SFDP parameter headers\n"); goto exit; } } @@ -2099,7 +2100,8 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor, switch (SFDP_PARAM_HEADER_ID(param_header)) { case SFDP_SECTOR_MAP_ID: - dev_info(dev, "non-uniform erase sector maps are not supported yet.\n"); + dev_info(nor->dev, + "non-uniform erase sector maps are not supported yet.\n"); break; case SFDP_SST_ID: @@ -2111,7 +2113,8 @@ static int spi_nor_parse_sfdp(struct spi_nor *nor, } if (err) { - dev_warn(dev, "Failed to parse optional parameter table: %04x\n", + dev_warn(nor->dev, + "Failed to parse optional parameter table: %04x\n", SFDP_PARAM_HEADER_ID(param_header)); /* * Let's not drop all information we extracted so far @@ -2609,7 +2612,7 @@ int spi_nor_scan(struct spi_nor *nor) } if (nor->addr_width > SPI_NOR_MAX_ADDR_WIDTH) { - dev_dbg(dev, "address width is too large: %u\n", + dev_dbg(nor->dev, "address width is too large: %u\n", nor->addr_width); return -EINVAL; } diff --git a/drivers/mtd/spi/spi-nor-ids.c b/drivers/mtd/spi/spi-nor-ids.c index e5e7102923..114ebacde1 100644 --- a/drivers/mtd/spi/spi-nor-ids.c +++ b/drivers/mtd/spi/spi-nor-ids.c @@ -160,6 +160,7 @@ const struct flash_info spi_nor_ids[] = { { INFO("mx66u2g45g", 0xc2253c, 0, 64 * 1024, 4096, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) }, { INFO("mx66l1g45g", 0xc2201b, 0, 64 * 1024, 2048, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) }, { INFO("mx25l1633e", 0xc22415, 0, 64 * 1024, 32, SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES | SECT_4K) }, + { INFO("mx25r6435f", 0xc22817, 0, 64 * 1024, 128, SECT_4K) }, #endif #ifdef CONFIG_SPI_FLASH_STMICRO /* STMICRO */ |