X-Git-Url: https://git.kernelconcepts.de/?p=karo-tx-uboot.git;a=blobdiff_plain;f=drivers%2Fmtd%2Fnand%2Fmxs_nand.c;h=d4575fb7c666cc08c13d6d9f52e9544dd0e6f4ac;hp=bf9414fef1e91f0f95e15e7d5b315307d2e9eab4;hb=2fa5bcaf41842ef831cb32329358906a31ac2df0;hpb=fa7a51cb8272bd6076ea4701fd6bdc65a68703ba diff --git a/drivers/mtd/nand/mxs_nand.c b/drivers/mtd/nand/mxs_nand.c index bf9414fef1..d4575fb7c6 100644 --- a/drivers/mtd/nand/mxs_nand.c +++ b/drivers/mtd/nand/mxs_nand.c @@ -10,42 +10,41 @@ * Copyright (C) 2010 Freescale Semiconductor, Inc. * Copyright (C) 2008 Embedded Alley Solutions, Inc. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * SPDX-License-Identifier: GPL-2.0+ */ +//#define DEBUG +#include #include #include #include -#include #include #include #include #include #include +#include +#include #include -#include +#include #define MXS_NAND_DMA_DESCRIPTOR_COUNT 4 #define MXS_NAND_CHUNK_DATA_CHUNK_SIZE 512 +#if defined(CONFIG_ARCH_MX6) +#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT 2 +#else +#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT 0 +#endif #define MXS_NAND_METADATA_SIZE 10 - +#define MXS_NAND_BITS_PER_ECC_LEVEL 13 #define MXS_NAND_COMMAND_BUFFER_SIZE 32 +/* BCH timeout in microseconds */ #define MXS_NAND_BCH_TIMEOUT 10000 +static struct bch_regs *bch_regs = (void *)BCH_BASE_ADDRESS; +static struct gpmi_regs *gpmi_regs = (void *)GPMI_BASE_ADDRESS; struct mxs_nand_info { int cur_chip; @@ -72,7 +71,112 @@ struct mxs_nand_info { uint32_t desc_index; }; -struct nand_ecclayout fake_ecc_layout; +#ifdef DEBUG +#define dump_reg(b, r) __dump_reg(&b->r, #r) +static inline void __dump_reg(void *addr, const char *name) +{ + printf("%16s[%p]=%08x\n", name, addr, readl(addr)); +} + +#define dump_bch_reg(n) __dump_reg(&bch_regs->hw_bch_##n, #n) +#define dump_gpmi_reg(n) __dump_reg(&gpmi_regs->hw_gpmi_##n, #n) +static inline void dump_regs(void) +{ + printf("BCH:\n"); + dump_bch_reg(ctrl); + dump_bch_reg(status0); + dump_bch_reg(mode); + dump_bch_reg(debug0); + dump_bch_reg(dbgkesread); + dump_bch_reg(dbgcsferead); + dump_bch_reg(dbgsyndegread); + dump_bch_reg(dbgahbmread); + dump_bch_reg(blockname); + dump_bch_reg(version); + + printf("\nGPMI:\n"); + dump_gpmi_reg(ctrl0); + dump_gpmi_reg(eccctrl); + dump_gpmi_reg(ecccount); + dump_gpmi_reg(payload); + dump_gpmi_reg(auxiliary); + dump_gpmi_reg(ctrl1); + dump_gpmi_reg(data); + dump_gpmi_reg(stat); + dump_gpmi_reg(debug); + dump_gpmi_reg(version); + dump_gpmi_reg(debug2); + dump_gpmi_reg(debug3); +} + +static inline int dbg_addr(void *addr) +{ + if (((unsigned long)addr & ~0xfff) == BCH_BASE_ADDRESS) + return 1; + return 1; +} + +static inline u32 mxs_readl(void *addr, + const char *fn, int ln) +{ + u32 val = readl(addr); + static void *last_addr; + static u32 last_val; + + if (!dbg_addr(addr)) + return val; + + if (addr != last_addr || last_val != val) { + printf("%s@%d: Read %08x from %p\n", fn, ln, val, addr); + last_addr = addr; + last_val = val; + } + return val; +} + +static inline void mxs_writel(u32 val, void *addr, + const char *fn, int ln) +{ + if (dbg_addr(addr)) + printf("%s@%d: Writing %08x to %p...", fn, ln, val, addr); + writel(val, addr); + if (dbg_addr(addr)) + printf(" result: %08x\n", readl(addr)); +} + +#undef readl +#define readl(a) mxs_readl(a, __func__, __LINE__) + +#undef writel +#define writel(v, a) mxs_writel(v, a, __func__, __LINE__) +static inline void memdump(const void *addr, size_t len) +{ + const char *buf = addr; + int i; + + for (i = 0; i < len; i++) { + if (i % 16 == 0) { + if (i > 0) + printf("\n"); + printf("%p:", &buf[i]); + } + printf(" %02x", buf[i]); + } + printf("\n"); +} +#else +static inline void memdump(void *addr, size_t len) +{ +} + +static inline void dump_regs(void) +{ +} +#endif + +static 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 @@ -135,12 +239,12 @@ static void mxs_nand_return_dma_descs(struct mxs_nand_info *info) static uint32_t mxs_nand_ecc_chunk_cnt(uint32_t page_data_size) { - return page_data_size / MXS_NAND_CHUNK_DATA_CHUNK_SIZE; + return page_data_size / chunk_data_size; } static uint32_t mxs_nand_ecc_size_in_bits(uint32_t ecc_strength) { - return ecc_strength * 13; + return ecc_strength * galois_field; } static uint32_t mxs_nand_aux_status_offset(void) @@ -148,23 +252,55 @@ 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 int mxs_nand_gpmi_init(void) { - if (page_data_size == 2048) - return 8; + int ret; - if (page_data_size == 4096) { - if (page_oob_size == 128) - return 8; + /* Reset the GPMI block. */ + ret = mxs_reset_block(&gpmi_regs->hw_gpmi_ctrl0_reg); + if (ret) { + printf("Failed to reset GPMI block\n"); + return ret; + } - if (page_oob_size == 218) - return 16; + ret = mxs_reset_block(&bch_regs->hw_bch_ctrl_reg); + if (ret) { + printf("Failed to reset BCH block\n"); + return ret; } + /* + * Choose NAND mode, set IRQ polarity, disable write protection and + * select BCH ECC. + */ + clrsetbits_le32(&gpmi_regs->hw_gpmi_ctrl1, + GPMI_CTRL1_GPMI_MODE, + GPMI_CTRL1_ATA_IRQRDY_POLARITY | GPMI_CTRL1_DEV_RESET | + GPMI_CTRL1_BCH_MODE); + writel(0x500 << 16, &gpmi_regs->hw_gpmi_timing1); return 0; } +static inline uint32_t mxs_nand_get_ecc_strength(uint32_t page_data_size, + uint32_t page_oob_size) +{ + int ecc_strength; + + /* + * 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 round_down(ecc_strength, 2); +} + static inline uint32_t mxs_nand_get_mark_offset(uint32_t page_data_size, uint32_t ecc_strength) { @@ -175,7 +311,7 @@ static inline uint32_t mxs_nand_get_mark_offset(uint32_t page_data_size, uint32_t block_mark_chunk_bit_offset; uint32_t block_mark_bit_offset; - chunk_data_size_in_bits = MXS_NAND_CHUNK_DATA_CHUNK_SIZE * 8; + 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 = @@ -214,14 +350,14 @@ static inline uint32_t mxs_nand_get_mark_offset(uint32_t page_data_size, return block_mark_bit_offset; } -static uint32_t mxs_nand_mark_byte_offset(struct mtd_info *mtd) +static inline 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; } -static uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd) +static inline uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd) { uint32_t ecc_strength; ecc_strength = mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize); @@ -233,12 +369,15 @@ static uint32_t mxs_nand_mark_bit_offset(struct mtd_info *mtd) */ static int mxs_nand_wait_for_bch_complete(void) { - 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, BCH_CTRL_COMPLETE_IRQ, timeout); + if (ret) { + debug("%s@%d: %d\n", __func__, __LINE__, ret); + mxs_nand_gpmi_init(); + } writel(BCH_CTRL_COMPLETE_IRQ, &bch_regs->hw_bch_ctrl_clr); @@ -322,8 +461,15 @@ static void mxs_nand_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl) /* Execute the DMA chain. */ ret = mxs_dma_go(channel); - if (ret) - printf("MXS NAND: Error sending command\n"); + if (ret) { + int i; + + printf("MXS NAND: Error sending command %08lx\n", d->cmd.pio_words[0]); + for (i = 0; i < nand_info->cmd_queue_len; i++) { + printf("%02x ", nand_info->cmd_buf[i]); + } + printf("\n"); + } mxs_nand_return_dma_descs(nand_info); @@ -338,8 +484,6 @@ static int mxs_nand_device_ready(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; struct mxs_nand_info *nand_info = chip->priv; - struct mxs_gpmi_regs *gpmi_regs = - (struct mxs_gpmi_regs *)MXS_GPMI_BASE; uint32_t tmp; tmp = readl(&gpmi_regs->hw_gpmi_stat); @@ -366,6 +510,7 @@ 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. */ +#ifndef CONFIG_NAND_MXS_NO_BBM_SWAP static void mxs_nand_swap_block_mark(struct mtd_info *mtd, uint8_t *data_buf, uint8_t *oob_buf) { @@ -389,6 +534,9 @@ static void mxs_nand_swap_block_mark(struct mtd_info *mtd, dst = oob_buf[0]; + debug("Swapping byte %02x @ %03x.%d with %02x @ %03x\n", + src & 0xff, buf_offset, bit_offset, dst & 0xff, 0); + oob_buf[0] = src; data_buf[buf_offset] &= ~(0xff << bit_offset); @@ -397,6 +545,12 @@ static void mxs_nand_swap_block_mark(struct mtd_info *mtd, data_buf[buf_offset] |= dst << bit_offset; data_buf[buf_offset + 1] |= dst >> (8 - bit_offset); } +#else +static inline void mxs_nand_swap_block_mark(struct mtd_info *mtd, + uint8_t *data_buf, uint8_t *oob_buf) +{ +} +#endif /* * Read data from NAND. @@ -438,6 +592,7 @@ static void mxs_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int length) mxs_dma_desc_append(channel, d); +#ifndef CONFIG_ARCH_MX6 /* * A DMA descriptor that waits for the command to end and the chip to * become ready. @@ -450,7 +605,7 @@ static void mxs_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int length) d->cmd.data = MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_IRQ | MXS_DMA_DESC_NAND_WAIT_4_READY | MXS_DMA_DESC_DEC_SEM | - MXS_DMA_DESC_WAIT4END | (4 << MXS_DMA_DESC_PIO_WORDS_OFFSET); + MXS_DMA_DESC_WAIT4END | (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET); d->cmd.address = 0; @@ -461,11 +616,15 @@ static void mxs_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int length) GPMI_CTRL0_ADDRESS_NAND_DATA; mxs_dma_desc_append(channel, d); +#endif + + /* Invalidate caches */ + mxs_nand_inval_data_buf(nand_info); /* Execute the DMA chain. */ ret = mxs_dma_go(channel); if (ret) { - printf("MXS NAND: DMA read error\n"); + printf("%s: DMA read error\n", __func__); goto rtn; } @@ -507,7 +666,7 @@ static void mxs_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, d->cmd.data = MXS_DMA_DESC_COMMAND_DMA_READ | MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM | MXS_DMA_DESC_WAIT4END | - (4 << MXS_DMA_DESC_PIO_WORDS_OFFSET) | + (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET) | (length << MXS_DMA_DESC_BYTES_OFFSET); d->cmd.address = (dma_addr_t)nand_info->data_buf; @@ -527,7 +686,7 @@ static void mxs_nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, /* Execute the DMA chain. */ ret = mxs_dma_go(channel); if (ret) - printf("MXS NAND: DMA write error\n"); + printf("%s: DMA write error\n", __func__); mxs_nand_return_dma_descs(nand_info); } @@ -542,11 +701,22 @@ static uint8_t mxs_nand_read_byte(struct mtd_info *mtd) return buf; } +static void flush_buffers(struct mtd_info *mtd, struct mxs_nand_info *nand_info) +{ + flush_dcache_range((unsigned long)nand_info->data_buf, + (unsigned long)nand_info->data_buf + + mtd->writesize); + flush_dcache_range((unsigned long)nand_info->oob_buf, + (unsigned long)nand_info->oob_buf + + mtd->oobsize); +} + /* * Read a page from NAND. */ static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand, - uint8_t *buf, int page) + uint8_t *buf, int oob_required, + int page) { struct mxs_nand_info *nand_info = nand->priv; struct mxs_dma_desc *d; @@ -595,6 +765,8 @@ 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; + flush_buffers(mtd, nand_info); + mxs_dma_desc_append(channel, d); /* Compile the DMA descriptor - disable the BCH block. */ @@ -627,10 +799,13 @@ static int mxs_nand_ecc_read_page(struct mtd_info *mtd, struct nand_chip *nand, mxs_dma_desc_append(channel, d); + /* Invalidate caches */ + mxs_nand_inval_data_buf(nand_info); + /* Execute the DMA chain. */ ret = mxs_dma_go(channel); if (ret) { - printf("MXS NAND: DMA read error\n"); + printf("%s: DMA read error\n", __func__); goto rtn; } @@ -691,8 +866,9 @@ rtn: /* * Write a page to NAND. */ -static void mxs_nand_ecc_write_page(struct mtd_info *mtd, - struct nand_chip *nand, const uint8_t *buf) +static int mxs_nand_ecc_write_page(struct mtd_info *mtd, + struct nand_chip *nand, const uint8_t *buf, + int oob_required) { struct mxs_nand_info *nand_info = nand->priv; struct mxs_dma_desc *d; @@ -724,10 +900,12 @@ static void mxs_nand_ecc_write_page(struct mtd_info *mtd, GPMI_ECCCTRL_ENABLE_ECC | GPMI_ECCCTRL_ECC_CMD_ENCODE | GPMI_ECCCTRL_BUFFER_MASK_BCH_PAGE; - d->cmd.pio_words[3] = (mtd->writesize + mtd->oobsize); + d->cmd.pio_words[3] = mtd->writesize + mtd->oobsize; d->cmd.pio_words[4] = (dma_addr_t)nand_info->data_buf; d->cmd.pio_words[5] = (dma_addr_t)nand_info->oob_buf; + flush_buffers(mtd, nand_info); + mxs_dma_desc_append(channel, d); /* Flush caches */ @@ -736,18 +914,19 @@ static void mxs_nand_ecc_write_page(struct mtd_info *mtd, /* Execute the DMA chain. */ ret = mxs_dma_go(channel); if (ret) { - printf("MXS NAND: DMA write error\n"); + printf("%s: DMA write error\n", __func__); goto rtn; } ret = mxs_nand_wait_for_bch_complete(); if (ret) { - printf("MXS NAND: BCH write timeout\n"); + printf("%s: BCH write timeout\n", __func__); goto rtn; } rtn: mxs_nand_return_dma_descs(nand_info); + return ret; } /* @@ -763,7 +942,7 @@ static int mxs_nand_hook_read_oob(struct mtd_info *mtd, loff_t from, struct mxs_nand_info *nand_info = chip->priv; int ret; - if (ops->mode == MTD_OOB_RAW) + if (ops->mode == MTD_OPS_RAW) nand_info->raw_oob_mode = 1; else nand_info->raw_oob_mode = 0; @@ -788,7 +967,7 @@ static int mxs_nand_hook_write_oob(struct mtd_info *mtd, loff_t to, struct mxs_nand_info *nand_info = chip->priv; int ret; - if (ops->mode == MTD_OOB_RAW) + if (ops->mode == MTD_OPS_RAW) nand_info->raw_oob_mode = 1; else nand_info->raw_oob_mode = 0; @@ -866,7 +1045,7 @@ static int mxs_nand_hook_block_markbad(struct mtd_info *mtd, loff_t ofs) * what to do. */ static int mxs_nand_ecc_read_oob(struct mtd_info *mtd, struct nand_chip *nand, - int page, int cmd) + int page) { struct mxs_nand_info *nand_info = nand->priv; @@ -953,7 +1132,7 @@ static int mxs_nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) /* * 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 + * the initialization process started by nand_scan(), and we don'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 @@ -968,26 +1147,44 @@ static int mxs_nand_scan_bbt(struct mtd_info *mtd) { struct nand_chip *nand = mtd->priv; struct mxs_nand_info *nand_info = nand->priv; - 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("OOB size of chip (%u bytes) is larger than max. supported size (%u bytes)\n", + mtd->oobsize, chunk_data_size); + return -EINVAL; + } + /* Configure BCH and set NFC geometry */ mxs_reset_block(&bch_regs->hw_bch_ctrl_reg); + debug("mtd->writesize=%d\n", mtd->writesize); + debug("mtd->oobsize=%d\n", mtd->oobsize); + debug("ecc_strength=%d\n", mxs_nand_get_ecc_strength(mtd->writesize, mtd->oobsize)); + /* Configure layout 0 */ tmp = (mxs_nand_ecc_chunk_cnt(mtd->writesize) - 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 |= MXS_NAND_CHUNK_DATA_CHUNK_SIZE; + tmp |= chunk_data_size >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT; + tmp |= (14 == galois_field ? 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 |= MXS_NAND_CHUNK_DATA_CHUNK_SIZE; + tmp |= chunk_data_size >> MXS_NAND_CHUNK_DATA_CHUNK_SIZE_SHIFT; + tmp |= (14 == galois_field ? 1 : 0) << + BCH_FLASHLAYOUT1_GF13_0_GF14_1_OFFSET; writel(tmp, &bch_regs->hw_bch_flash0layout1); /* Set *all* chip selects to use layout 0 */ @@ -997,19 +1194,19 @@ static int mxs_nand_scan_bbt(struct mtd_info *mtd) writel(BCH_CTRL_COMPLETE_IRQ_EN, &bch_regs->hw_bch_ctrl_set); /* Hook some operations at the MTD level. */ - if (mtd->read_oob != mxs_nand_hook_read_oob) { - nand_info->hooked_read_oob = mtd->read_oob; - mtd->read_oob = mxs_nand_hook_read_oob; + if (mtd->_read_oob != mxs_nand_hook_read_oob) { + nand_info->hooked_read_oob = mtd->_read_oob; + mtd->_read_oob = mxs_nand_hook_read_oob; } - if (mtd->write_oob != mxs_nand_hook_write_oob) { - nand_info->hooked_write_oob = mtd->write_oob; - mtd->write_oob = mxs_nand_hook_write_oob; + if (mtd->_write_oob != mxs_nand_hook_write_oob) { + nand_info->hooked_write_oob = mtd->_write_oob; + mtd->_write_oob = mxs_nand_hook_write_oob; } - if (mtd->block_markbad != mxs_nand_hook_block_markbad) { - nand_info->hooked_block_markbad = mtd->block_markbad; - mtd->block_markbad = mxs_nand_hook_block_markbad; + if (mtd->_block_markbad != mxs_nand_hook_block_markbad) { + nand_info->hooked_block_markbad = mtd->_block_markbad; + mtd->_block_markbad = mxs_nand_hook_block_markbad; } /* We use the reference implementation for bad block management. */ @@ -1029,7 +1226,7 @@ int mxs_nand_alloc_buffers(struct mxs_nand_info *nand_info) /* DMA buffers */ buf = memalign(MXS_DMA_ALIGNMENT, nand_info->data_buf_size); if (!buf) { - printf("MXS NAND: Error allocating DMA buffers\n"); + printf("%s: Error allocating DMA buffers\n", __func__); return -ENOMEM; } @@ -1056,53 +1253,57 @@ 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; - int i = 0, j; + int ret; + int i; - info->desc = malloc(sizeof(struct mxs_dma_desc *) * - MXS_NAND_DMA_DESCRIPTOR_COUNT); - if (!info->desc) + info->desc = calloc(MXS_NAND_DMA_DESCRIPTOR_COUNT, + sizeof(struct mxs_dma_desc *)); + + if (!info->desc) { + printf("MXS NAND: Unable to allocate DMA descriptor table\n"); + ret = -ENOMEM; goto err1; + } + + mxs_dma_init(); /* Allocate the DMA descriptors. */ for (i = 0; i < MXS_NAND_DMA_DESCRIPTOR_COUNT; i++) { info->desc[i] = mxs_dma_desc_alloc(); - if (!info->desc[i]) + if (!info->desc[i]) { + printf("MXS NAND: Unable to allocate DMA descriptors\n"); + ret = -ENOMEM; goto err2; + } } /* Init the DMA controller. */ - for (j = MXS_DMA_CHANNEL_AHB_APBH_GPMI0; - j <= MXS_DMA_CHANNEL_AHB_APBH_GPMI7; j++) { - if (mxs_dma_init_channel(j)) + for (i = 0; i < CONFIG_SYS_NAND_MAX_CHIPS; i++) { + const int chan = MXS_DMA_CHANNEL_AHB_APBH_GPMI0 + i; + + ret = mxs_dma_init_channel(chan); + if (ret) { + printf("Failed to initialize DMA channel %d\n", chan); goto err3; + } } - /* Reset the GPMI block. */ - mxs_reset_block(&gpmi_regs->hw_gpmi_ctrl0_reg); - - /* - * Choose NAND mode, set IRQ polarity, disable write protection and - * select BCH ECC. - */ - clrsetbits_le32(&gpmi_regs->hw_gpmi_ctrl1, - GPMI_CTRL1_GPMI_MODE, - GPMI_CTRL1_ATA_IRQRDY_POLARITY | GPMI_CTRL1_DEV_RESET | - GPMI_CTRL1_BCH_MODE); + ret = mxs_nand_gpmi_init(); + if (ret) + goto err3; return 0; err3: - for (--j; j >= 0; j--) - mxs_dma_release(j); + for (--i; i >= 0; i--) + mxs_dma_release(i + MXS_DMA_CHANNEL_AHB_APBH_GPMI0); + i = MXS_NAND_DMA_DESCRIPTOR_COUNT - 1; err2: free(info->desc); -err1: for (--i; i >= 0; i--) mxs_dma_desc_free(info->desc[i]); - printf("MXS NAND: Unable to allocate DMA descriptors\n"); - return -ENOMEM; +err1: + return ret; } /*! @@ -1112,19 +1313,18 @@ err1: * information that is used by the suspend, resume and * remove functions * - * @return The function always returns 0. + * @return 0 for success; errno value in case of error */ int board_nand_init(struct nand_chip *nand) { struct mxs_nand_info *nand_info; int err; - nand_info = malloc(sizeof(struct mxs_nand_info)); + nand_info = kzalloc(sizeof(struct mxs_nand_info), 0); 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) @@ -1138,7 +1338,9 @@ int board_nand_init(struct nand_chip *nand) nand->priv = nand_info; nand->options |= NAND_NO_SUBPAGE_WRITE; - +#ifdef CONFIG_SYS_NAND_USE_FLASH_BBT + nand->bbt_options |= NAND_BBT_USE_FLASH | NAND_BBT_NO_OOB; +#endif nand->cmd_ctrl = mxs_nand_cmd_ctrl; nand->dev_ready = mxs_nand_device_ready; @@ -1160,6 +1362,7 @@ int board_nand_init(struct nand_chip *nand) nand->ecc.mode = NAND_ECC_HW; nand->ecc.bytes = 9; nand->ecc.size = 512; + nand->ecc.strength = 8; return 0;