diff options
Diffstat (limited to 'arch/arm/mach-imx/xload-gpmi-nand.c')
-rw-r--r-- | arch/arm/mach-imx/xload-gpmi-nand.c | 663 |
1 files changed, 329 insertions, 334 deletions
diff --git a/arch/arm/mach-imx/xload-gpmi-nand.c b/arch/arm/mach-imx/xload-gpmi-nand.c index b3fd479cb4..8221e1ace0 100644 --- a/arch/arm/mach-imx/xload-gpmi-nand.c +++ b/arch/arm/mach-imx/xload-gpmi-nand.c @@ -1,13 +1,4 @@ -/* - * 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; version 2. - * - * 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. - */ +// SPDX-License-Identifier: GPL-2.0-only #define pr_fmt(fmt) "xload-gpmi-nand: " fmt @@ -16,47 +7,16 @@ #include <asm-generic/io.h> #include <linux/sizes.h> #include <linux/mtd/nand.h> +#include <linux/bitfield.h> #include <asm/cache.h> -#include <mach/xload.h> +#include <mach/imx/xload.h> #include <soc/imx/imx-nand-bcb.h> #include <linux/mtd/rawnand.h> -#include <mach/imx6-regs.h> -#include <mach/clock-imx6.h> - -/* - * MXS DMA hardware command. - * - * This structure describes the in-memory layout of an entire DMA command, - * including space for the maximum number of PIO accesses. See the appropriate - * reference manual for a detailed description of what these fields mean to the - * DMA hardware. - */ -#define DMACMD_COMMAND_DMA_WRITE 0x1 -#define DMACMD_COMMAND_DMA_READ 0x2 -#define DMACMD_COMMAND_DMA_SENSE 0x3 -#define DMACMD_CHAIN (1 << 2) -#define DMACMD_IRQ (1 << 3) -#define DMACMD_NAND_LOCK (1 << 4) -#define DMACMD_NAND_WAIT_4_READY (1 << 5) -#define DMACMD_DEC_SEM (1 << 6) -#define DMACMD_WAIT4END (1 << 7) -#define DMACMD_HALT_ON_TERMINATE (1 << 8) -#define DMACMD_TERMINATE_FLUSH (1 << 9) -#define DMACMD_PIO_WORDS(words) ((words) << 12) -#define DMACMD_XFER_COUNT(x) ((x) << 16) - -struct mxs_dma_cmd { - unsigned long next; - unsigned long data; - unsigned long address; -#define APBH_DMA_PIO_WORDS 6 - unsigned long pio_words[APBH_DMA_PIO_WORDS]; -}; - -enum mxs_dma_id { - IMX23_DMA, - IMX28_DMA, -}; +#include <soc/imx/gpmi-nand.h> +#include <mach/imx/imx6-regs.h> +#include <mach/imx/imx7-regs.h> +#include <mach/imx/clock-imx6.h> +#include <dma/apbh-dma.h> struct apbh_dma { void __iomem *regs; @@ -69,25 +29,6 @@ struct mxs_dma_chan { struct apbh_dma *apbh; }; -#define HW_APBHX_CTRL0 0x000 -#define BM_APBH_CTRL0_APB_BURST8_EN (1 << 29) -#define BM_APBH_CTRL0_APB_BURST_EN (1 << 28) -#define BP_APBH_CTRL0_CLKGATE_CHANNEL 8 -#define BP_APBH_CTRL0_RESET_CHANNEL 16 -#define HW_APBHX_CTRL1 0x010 -#define BP_APBHX_CTRL1_CH_CMDCMPLT_IRQ_EN 16 -#define HW_APBHX_CTRL2 0x020 -#define HW_APBHX_CHANNEL_CTRL 0x030 -#define BP_APBHX_CHANNEL_CTRL_RESET_CHANNEL 16 -#define BP_APBHX_VERSION_MAJOR 24 -#define HW_APBHX_CHn_NXTCMDAR_MX23(n) (0x050 + (n) * 0x70) -#define HW_APBHX_CHn_NXTCMDAR_MX28(n) (0x110 + (n) * 0x70) -#define HW_APBHX_CHn_SEMA_MX23(n) (0x080 + (n) * 0x70) -#define HW_APBHX_CHn_SEMA_MX28(n) (0x140 + (n) * 0x70) -#define NAND_ONFI_CRC_BASE 0x4f4e - -#define apbh_dma_is_imx23(aphb) ((apbh)->id == IMX23_DMA) - /* udelay() is not available in PBL, need to improvise */ static void __udelay(int us) { @@ -111,14 +52,15 @@ static int mxs_dma_enable(struct mxs_dma_chan *pchan, struct apbh_dma *apbh = pchan->apbh; int channel_bit; int channel = pchan->channel; + unsigned long pdesc32 = (unsigned long)pdesc; if (apbh_dma_is_imx23(apbh)) { - writel((uint32_t)pdesc, + writel(pdesc32, apbh->regs + HW_APBHX_CHn_NXTCMDAR_MX23(channel)); writel(1, apbh->regs + HW_APBHX_CHn_SEMA_MX23(channel)); channel_bit = channel + BP_APBH_CTRL0_CLKGATE_CHANNEL; } else { - writel((uint32_t)pdesc, + writel(pdesc32, apbh->regs + HW_APBHX_CHn_NXTCMDAR_MX28(channel)); writel(1, apbh->regs + HW_APBHX_CHn_SEMA_MX28(channel)); channel_bit = channel; @@ -174,8 +116,8 @@ static int mxs_dma_run(struct mxs_dma_chan *pchan, struct mxs_dma_cmd *pdesc, /* chain descriptors */ for (i = 0; i < num - 1; i++) { - pdesc[i].next = (uint32_t)(&pdesc[i + 1]); - pdesc[i].data |= DMACMD_CHAIN; + pdesc[i].next = (unsigned long)(&pdesc[i + 1]); + pdesc[i].data |= MXS_DMA_DESC_CHAIN; } writel(1 << (pchan->channel + BP_APBHX_CTRL1_CH_CMDCMPLT_IRQ_EN), @@ -211,39 +153,6 @@ static int mxs_dma_run(struct mxs_dma_chan *pchan, struct mxs_dma_cmd *pdesc, /* ----------------------------- NAND driver part -------------------------- */ -#define GPMI_CTRL0 0x00000000 -#define GPMI_CTRL0_RUN (1 << 29) -#define GPMI_CTRL0_DEV_IRQ_EN (1 << 28) -#define GPMI_CTRL0_UDMA (1 << 26) -#define GPMI_CTRL0_COMMAND_MODE_MASK (0x3 << 24) -#define GPMI_CTRL0_COMMAND_MODE_OFFSET 24 -#define GPMI_CTRL0_COMMAND_MODE_WRITE (0x0 << 24) -#define GPMI_CTRL0_COMMAND_MODE_READ (0x1 << 24) -#define GPMI_CTRL0_COMMAND_MODE_READ_AND_COMPARE (0x2 << 24) -#define GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY (0x3 << 24) -#define GPMI_CTRL0_WORD_LENGTH (1 << 23) -#define GPMI_CTRL0_CS(cs) ((cs) << 20) -#define GPMI_CTRL0_ADDRESS_MASK (0x7 << 17) -#define GPMI_CTRL0_ADDRESS_OFFSET 17 -#define GPMI_CTRL0_ADDRESS_NAND_DATA (0x0 << 17) -#define GPMI_CTRL0_ADDRESS_NAND_CLE (0x1 << 17) -#define GPMI_CTRL0_ADDRESS_NAND_ALE (0x2 << 17) -#define GPMI_CTRL0_ADDRESS_INCREMENT (1 << 16) -#define GPMI_CTRL0_XFER_COUNT_MASK 0xffff -#define GPMI_CTRL0_XFER_COUNT_OFFSET 0 - -#define GPMI_ECCCTRL_ECC_CMD_DECODE (0x0 << 13) -#define GPMI_ECCCTRL_ENABLE_ECC (1 << 12) -#define GPMI_ECCCTRL_BUFFER_MASK_BCH_PAGE 0x1ff - -#define BCH_CTRL 0x00000000 -#define BCH_CTRL_COMPLETE_IRQ (1 << 0) - -#define MXS_NAND_DMA_DESCRIPTOR_COUNT 6 -#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE 512 -#define MXS_NAND_METADATA_SIZE 10 -#define MXS_NAND_COMMAND_BUFFER_SIZE 128 - struct mxs_nand_info { void __iomem *io_base; void __iomem *bch_base; @@ -321,7 +230,7 @@ static uint32_t mxs_nand_aux_status_offset(void) } static int mxs_nand_read_page(struct mxs_nand_info *info, int writesize, - int oobsize, int pagenum, void *databuf, int raw) + int oobsize, int pagenum, void *databuf, int raw, bool randomizer) { void __iomem *bch_regs = info->bch_base; unsigned column = 0; @@ -329,8 +238,6 @@ static int mxs_nand_read_page(struct mxs_nand_info *info, int writesize, int cmd_queue_len; u8 *cmd_buf; int ret; - uint8_t *status; - int i; int timeout; int descnum = 0; int max_pagenum = info->nand_size / @@ -353,14 +260,14 @@ static int mxs_nand_read_page(struct mxs_nand_info *info, int writesize, if ((max_pagenum - 1) >= SZ_64K) cmd_buf[cmd_queue_len++] = pagenum >> 16; - d->data = DMACMD_COMMAND_DMA_READ | - DMACMD_WAIT4END | - DMACMD_PIO_WORDS(1) | - DMACMD_XFER_COUNT(cmd_queue_len); + d->data = MXS_DMA_DESC_COMMAND_DMA_READ | + MXS_DMA_DESC_WAIT4END | + MXS_DMA_DESC_PIO_WORDS(1) | + MXS_DMA_DESC_XFER_COUNT(cmd_queue_len); d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WRITE | GPMI_CTRL0_WORD_LENGTH | - GPMI_CTRL0_CS(info->cs) | + FIELD_PREP(GPMI_CTRL0_CS, info->cs) | GPMI_CTRL0_ADDRESS_NAND_CLE | GPMI_CTRL0_ADDRESS_INCREMENT | cmd_queue_len; @@ -373,50 +280,50 @@ static int mxs_nand_read_page(struct mxs_nand_info *info, int writesize, cmd_buf[cmd_queue_len++] = NAND_CMD_READSTART; - d->data = DMACMD_COMMAND_DMA_READ | - DMACMD_WAIT4END | - DMACMD_PIO_WORDS(1) | - DMACMD_XFER_COUNT(cmd_queue_len); + d->data = MXS_DMA_DESC_COMMAND_DMA_READ | + MXS_DMA_DESC_WAIT4END | + MXS_DMA_DESC_PIO_WORDS(1) | + MXS_DMA_DESC_XFER_COUNT(cmd_queue_len); d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WRITE | GPMI_CTRL0_WORD_LENGTH | - GPMI_CTRL0_CS(info->cs) | + FIELD_PREP(GPMI_CTRL0_CS, info->cs) | GPMI_CTRL0_ADDRESS_NAND_CLE | GPMI_CTRL0_ADDRESS_INCREMENT | cmd_queue_len; /* Compile DMA descriptor - wait for ready. */ d = &info->desc[descnum++]; - d->data = DMACMD_CHAIN | - DMACMD_NAND_WAIT_4_READY | - DMACMD_WAIT4END | - DMACMD_PIO_WORDS(2); + d->data = MXS_DMA_DESC_CHAIN | + MXS_DMA_DESC_NAND_WAIT_4_READY | + MXS_DMA_DESC_WAIT4END | + MXS_DMA_DESC_PIO_WORDS(2); d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY | GPMI_CTRL0_WORD_LENGTH | - GPMI_CTRL0_CS(info->cs) | + FIELD_PREP(GPMI_CTRL0_CS, info->cs) | GPMI_CTRL0_ADDRESS_NAND_DATA; if (raw) { /* Compile DMA descriptor - read. */ d = &info->desc[descnum++]; - d->data = DMACMD_WAIT4END | - DMACMD_PIO_WORDS(1) | - DMACMD_XFER_COUNT(writesize + oobsize) | - DMACMD_COMMAND_DMA_WRITE; + d->data = MXS_DMA_DESC_WAIT4END | + MXS_DMA_DESC_PIO_WORDS(1) | + MXS_DMA_DESC_XFER_COUNT(writesize + oobsize) | + MXS_DMA_DESC_COMMAND_DMA_WRITE; d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_READ | GPMI_CTRL0_WORD_LENGTH | - GPMI_CTRL0_CS(info->cs) | + FIELD_PREP(GPMI_CTRL0_CS, info->cs) | GPMI_CTRL0_ADDRESS_NAND_DATA | (writesize + oobsize); d->address = (dma_addr_t)databuf; } else { /* Compile DMA descriptor - enable the BCH block and read. */ d = &info->desc[descnum++]; - d->data = DMACMD_WAIT4END | DMACMD_PIO_WORDS(6); + d->data = MXS_DMA_DESC_WAIT4END | MXS_DMA_DESC_PIO_WORDS(6); d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_READ | GPMI_CTRL0_WORD_LENGTH | - GPMI_CTRL0_CS(info->cs) | + FIELD_PREP(GPMI_CTRL0_CS, info->cs) | GPMI_CTRL0_ADDRESS_NAND_DATA | (writesize + oobsize); d->pio_words[1] = 0; @@ -427,21 +334,27 @@ static int mxs_nand_read_page(struct mxs_nand_info *info, int writesize, d->pio_words[4] = (dma_addr_t)databuf; d->pio_words[5] = (dma_addr_t)(databuf + writesize); + if (randomizer) { + d->pio_words[2] |= GPMI_ECCCTRL_RANDOMIZER_ENABLE | + GPMI_ECCCTRL_RANDOMIZER_TYPE2; + d->pio_words[3] |= (pagenum % 256) << 16; + } + /* Compile DMA descriptor - disable the BCH block. */ d = &info->desc[descnum++]; - d->data = DMACMD_NAND_WAIT_4_READY | - DMACMD_WAIT4END | - DMACMD_PIO_WORDS(3); + d->data = MXS_DMA_DESC_NAND_WAIT_4_READY | + MXS_DMA_DESC_WAIT4END | + MXS_DMA_DESC_PIO_WORDS(3); d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY | GPMI_CTRL0_WORD_LENGTH | - GPMI_CTRL0_CS(info->cs) | + FIELD_PREP(GPMI_CTRL0_CS, info->cs) | GPMI_CTRL0_ADDRESS_NAND_DATA | (writesize + oobsize); } /* Compile DMA descriptor - de-assert the NAND lock and interrupt. */ d = &info->desc[descnum++]; - d->data = DMACMD_IRQ | DMACMD_DEC_SEM; + d->data = MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM; /* Execute the DMA chain. */ ret = mxs_dma_run(info->dma_channel, info->desc, descnum); @@ -468,20 +381,26 @@ static int mxs_nand_read_page(struct mxs_nand_info *info, int writesize, writel(BCH_CTRL_COMPLETE_IRQ, bch_regs + BCH_CTRL + STMP_OFFSET_REG_CLR); - /* Loop over status bytes, accumulating ECC status. */ - status = databuf + writesize + mxs_nand_aux_status_offset(); - for (i = 0; i < writesize / MXS_NAND_CHUNK_DATA_CHUNK_SIZE; i++) { - if (status[i] == 0xfe) { - ret = -EBADMSG; - goto err; - } - } - ret = 0; err: return ret; } +static int mxs_nand_get_ecc_status(struct mxs_nand_info *info, void *databuf) +{ + uint8_t *status; + int i; + + /* Loop over status bytes, accumulating ECC status. */ + status = databuf + info->organization.pagesize + mxs_nand_aux_status_offset(); + for (i = 0; i < info->organization.pagesize / MXS_NAND_CHUNK_DATA_CHUNK_SIZE; i++) { + if (status[i] == 0xfe) + return -EBADMSG; + } + + return 0; +} + static int mxs_nand_get_read_status(struct mxs_nand_info *info, void *databuf) { int ret; @@ -500,34 +419,34 @@ static int mxs_nand_get_read_status(struct mxs_nand_info *info, void *databuf) d->address = (dma_addr_t)(cmd_buf); cmd_buf[cmd_queue_len++] = NAND_CMD_STATUS; - d->data = DMACMD_COMMAND_DMA_READ | - DMACMD_WAIT4END | - DMACMD_PIO_WORDS(1) | - DMACMD_XFER_COUNT(cmd_queue_len); + d->data = MXS_DMA_DESC_COMMAND_DMA_READ | + MXS_DMA_DESC_WAIT4END | + MXS_DMA_DESC_PIO_WORDS(1) | + MXS_DMA_DESC_XFER_COUNT(cmd_queue_len); d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WRITE | GPMI_CTRL0_WORD_LENGTH | - GPMI_CTRL0_CS(info->cs) | + FIELD_PREP(GPMI_CTRL0_CS, info->cs) | GPMI_CTRL0_ADDRESS_NAND_CLE | GPMI_CTRL0_ADDRESS_INCREMENT | cmd_queue_len; /* Compile DMA descriptor - read. */ d = &info->desc[descnum++]; - d->data = DMACMD_WAIT4END | - DMACMD_PIO_WORDS(1) | - DMACMD_XFER_COUNT(1) | - DMACMD_COMMAND_DMA_WRITE; + d->data = MXS_DMA_DESC_WAIT4END | + MXS_DMA_DESC_PIO_WORDS(1) | + MXS_DMA_DESC_XFER_COUNT(1) | + MXS_DMA_DESC_COMMAND_DMA_WRITE; d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_READ | GPMI_CTRL0_WORD_LENGTH | - GPMI_CTRL0_CS(info->cs) | + FIELD_PREP(GPMI_CTRL0_CS, info->cs) | GPMI_CTRL0_ADDRESS_NAND_DATA | (1); d->address = (dma_addr_t)databuf; /* Compile DMA descriptor - de-assert the NAND lock and interrupt. */ d = &info->desc[descnum++]; - d->data = DMACMD_IRQ | DMACMD_DEC_SEM; + d->data = MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM; /* Execute the DMA chain. */ ret = mxs_dma_run(info->dma_channel, info->desc, descnum); @@ -558,21 +477,21 @@ static int mxs_nand_reset(struct mxs_nand_info *info, void *databuf) d->address = (dma_addr_t)(cmd_buf); cmd_buf[cmd_queue_len++] = NAND_CMD_RESET; - d->data = DMACMD_COMMAND_DMA_READ | - DMACMD_WAIT4END | - DMACMD_PIO_WORDS(1) | - DMACMD_XFER_COUNT(cmd_queue_len); + d->data = MXS_DMA_DESC_COMMAND_DMA_READ | + MXS_DMA_DESC_WAIT4END | + MXS_DMA_DESC_PIO_WORDS(1) | + MXS_DMA_DESC_XFER_COUNT(cmd_queue_len); d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WRITE | GPMI_CTRL0_WORD_LENGTH | - GPMI_CTRL0_CS(info->cs) | + FIELD_PREP(GPMI_CTRL0_CS, info->cs) | GPMI_CTRL0_ADDRESS_NAND_CLE | GPMI_CTRL0_ADDRESS_INCREMENT | cmd_queue_len; /* Compile DMA descriptor - de-assert the NAND lock and interrupt. */ d = &info->desc[descnum++]; - d->data = DMACMD_IRQ | DMACMD_DEC_SEM; + d->data = MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM; /* Execute the DMA chain. */ ret = mxs_dma_run(info->dma_channel, info->desc, descnum); @@ -630,46 +549,46 @@ static int mxs_nand_get_onfi(struct mxs_nand_info *info, void *databuf) cmd_buf[cmd_queue_len++] = NAND_CMD_PARAM; cmd_buf[cmd_queue_len++] = 0x00; - d->data = DMACMD_COMMAND_DMA_READ | - DMACMD_WAIT4END | - DMACMD_PIO_WORDS(1) | - DMACMD_XFER_COUNT(cmd_queue_len); + d->data = MXS_DMA_DESC_COMMAND_DMA_READ | + MXS_DMA_DESC_WAIT4END | + MXS_DMA_DESC_PIO_WORDS(1) | + MXS_DMA_DESC_XFER_COUNT(cmd_queue_len); d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WRITE | GPMI_CTRL0_WORD_LENGTH | - GPMI_CTRL0_CS(info->cs) | + FIELD_PREP(GPMI_CTRL0_CS, info->cs) | GPMI_CTRL0_ADDRESS_NAND_CLE | GPMI_CTRL0_ADDRESS_INCREMENT | cmd_queue_len; /* Compile DMA descriptor - wait for ready. */ d = &info->desc[descnum++]; - d->data = DMACMD_CHAIN | - DMACMD_NAND_WAIT_4_READY | - DMACMD_WAIT4END | - DMACMD_PIO_WORDS(2); + d->data = MXS_DMA_DESC_CHAIN | + MXS_DMA_DESC_NAND_WAIT_4_READY | + MXS_DMA_DESC_WAIT4END | + MXS_DMA_DESC_PIO_WORDS(2); d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY | GPMI_CTRL0_WORD_LENGTH | - GPMI_CTRL0_CS(info->cs) | + FIELD_PREP(GPMI_CTRL0_CS, info->cs) | GPMI_CTRL0_ADDRESS_NAND_DATA; /* Compile DMA descriptor - read. */ d = &info->desc[descnum++]; - d->data = DMACMD_WAIT4END | - DMACMD_PIO_WORDS(1) | - DMACMD_XFER_COUNT(sizeof(struct nand_onfi_params)) | - DMACMD_COMMAND_DMA_WRITE; + d->data = MXS_DMA_DESC_WAIT4END | + MXS_DMA_DESC_PIO_WORDS(1) | + MXS_DMA_DESC_XFER_COUNT(sizeof(struct nand_onfi_params)) | + MXS_DMA_DESC_COMMAND_DMA_WRITE; d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_READ | GPMI_CTRL0_WORD_LENGTH | - GPMI_CTRL0_CS(info->cs) | + FIELD_PREP(GPMI_CTRL0_CS, info->cs) | GPMI_CTRL0_ADDRESS_NAND_DATA | (sizeof(struct nand_onfi_params)); d->address = (dma_addr_t)databuf; /* Compile DMA descriptor - de-assert the NAND lock and interrupt. */ d = &info->desc[descnum++]; - d->data = DMACMD_IRQ | DMACMD_DEC_SEM; + d->data = MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM; /* Execute the DMA chain. */ ret = mxs_dma_run(info->dma_channel, info->desc, descnum); @@ -708,7 +627,7 @@ static int mxs_nand_get_onfi(struct mxs_nand_info *info, void *databuf) return ret; } -static int mxs_nand_check_onfi(struct mxs_nand_info *info, void *databuf) +static int mxs_nand_read_id(struct mxs_nand_info *info, u8 adr, void *databuf, size_t len) { int ret; u8 *cmd_buf; @@ -716,13 +635,6 @@ static int mxs_nand_check_onfi(struct mxs_nand_info *info, void *databuf) int descnum = 0; int cmd_queue_len; - struct onfi_header { - u8 byte0; - u8 byte1; - u8 byte2; - u8 byte3; - } onfi_head; - memset(info->desc, 0, sizeof(*info->desc) * MXS_NAND_DMA_DESCRIPTOR_COUNT); @@ -732,152 +644,118 @@ static int mxs_nand_check_onfi(struct mxs_nand_info *info, void *databuf) d = &info->desc[descnum++]; d->address = (dma_addr_t)(cmd_buf); cmd_buf[cmd_queue_len++] = NAND_CMD_READID; - cmd_buf[cmd_queue_len++] = 0x20; + cmd_buf[cmd_queue_len++] = adr; - d->data = DMACMD_COMMAND_DMA_READ | - DMACMD_WAIT4END | - DMACMD_PIO_WORDS(1) | - DMACMD_XFER_COUNT(cmd_queue_len); + d->data = MXS_DMA_DESC_COMMAND_DMA_READ | + MXS_DMA_DESC_WAIT4END | + MXS_DMA_DESC_PIO_WORDS(1) | + MXS_DMA_DESC_XFER_COUNT(cmd_queue_len); d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WRITE | GPMI_CTRL0_WORD_LENGTH | - GPMI_CTRL0_CS(info->cs) | + FIELD_PREP(GPMI_CTRL0_CS, info->cs) | GPMI_CTRL0_ADDRESS_NAND_CLE | GPMI_CTRL0_ADDRESS_INCREMENT | cmd_queue_len; /* Compile DMA descriptor - read. */ d = &info->desc[descnum++]; - d->data = DMACMD_WAIT4END | - DMACMD_PIO_WORDS(1) | - DMACMD_XFER_COUNT(sizeof(struct onfi_header)) | - DMACMD_COMMAND_DMA_WRITE; + d->data = MXS_DMA_DESC_WAIT4END | + MXS_DMA_DESC_PIO_WORDS(1) | + MXS_DMA_DESC_XFER_COUNT(len) | + MXS_DMA_DESC_COMMAND_DMA_WRITE; d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_READ | GPMI_CTRL0_WORD_LENGTH | - GPMI_CTRL0_CS(info->cs) | + FIELD_PREP(GPMI_CTRL0_CS, info->cs) | GPMI_CTRL0_ADDRESS_NAND_DATA | - (sizeof(struct onfi_header)); + len; d->address = (dma_addr_t)databuf; /* Compile DMA descriptor - de-assert the NAND lock and interrupt. */ d = &info->desc[descnum++]; - d->data = DMACMD_IRQ | DMACMD_DEC_SEM; + d->data = MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM; /* Execute the DMA chain. */ ret = mxs_dma_run(info->dma_channel, info->desc, descnum); - if (ret) { + if (ret) pr_err("DMA read error\n"); - return ret; - } - - memcpy(&onfi_head, databuf, sizeof(struct onfi_header)); - pr_debug("ONFI Byte0: 0x%x\n", onfi_head.byte0); - pr_debug("ONFI Byte1: 0x%x\n", onfi_head.byte1); - pr_debug("ONFI Byte2: 0x%x\n", onfi_head.byte2); - pr_debug("ONFI Byte3: 0x%x\n", onfi_head.byte3); - - /* check if returned values correspond to ascii characters "ONFI" */ - if (onfi_head.byte0 != 0x4f || onfi_head.byte1 != 0x4e || - onfi_head.byte2 != 0x46 || onfi_head.byte3 != 0x49) - return 1; - - return 0; + return ret; } -static int mxs_nand_get_readid(struct mxs_nand_info *info, void *databuf) +struct onfi_header { + u8 byte0; + u8 byte1; + u8 byte2; + u8 byte3; +}; + +static int mxs_nand_check_onfi(struct mxs_nand_info *info, void *databuf) { int ret; - u8 *cmd_buf; - struct mxs_dma_cmd *d; - int descnum = 0; - int cmd_queue_len; - - struct readid_data { - u8 byte0; - u8 byte1; - u8 byte2; - u8 byte3; - u8 byte4; - } id_data; + struct onfi_header *onfi_head = databuf; - memset(info->desc, 0, - sizeof(*info->desc) * MXS_NAND_DMA_DESCRIPTOR_COUNT); + ret = mxs_nand_read_id(info, 0x20, databuf, sizeof(struct onfi_header)); + if (ret) + return ret; - /* Compile DMA descriptor - READID */ - cmd_buf = info->cmd_buf; - cmd_queue_len = 0; - d = &info->desc[descnum++]; - d->address = (dma_addr_t)(cmd_buf); - cmd_buf[cmd_queue_len++] = NAND_CMD_READID; - cmd_buf[cmd_queue_len++] = 0x00; + pr_debug("ONFI Byte0: 0x%x\n", onfi_head->byte0); + pr_debug("ONFI Byte1: 0x%x\n", onfi_head->byte1); + pr_debug("ONFI Byte2: 0x%x\n", onfi_head->byte2); + pr_debug("ONFI Byte3: 0x%x\n", onfi_head->byte3); - d->data = DMACMD_COMMAND_DMA_READ | - DMACMD_WAIT4END | - DMACMD_PIO_WORDS(1) | - DMACMD_XFER_COUNT(cmd_queue_len); + if (onfi_head->byte0 != 'O' || onfi_head->byte1 != 'N' || + onfi_head->byte2 != 'F' || onfi_head->byte3 != 'I') + return 1; - d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WRITE | - GPMI_CTRL0_WORD_LENGTH | - GPMI_CTRL0_CS(info->cs) | - GPMI_CTRL0_ADDRESS_NAND_CLE | - GPMI_CTRL0_ADDRESS_INCREMENT | - cmd_queue_len; + return 0; +} - /* Compile DMA descriptor - read. */ - d = &info->desc[descnum++]; - d->data = DMACMD_WAIT4END | - DMACMD_PIO_WORDS(1) | - DMACMD_XFER_COUNT(sizeof(struct readid_data)) | - DMACMD_COMMAND_DMA_WRITE; - d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_READ | - GPMI_CTRL0_WORD_LENGTH | - GPMI_CTRL0_CS(info->cs) | - GPMI_CTRL0_ADDRESS_NAND_DATA | - (sizeof(struct readid_data)); - d->address = (dma_addr_t)databuf; +struct readid_data { + u8 byte0; + u8 byte1; + u8 byte2; + u8 byte3; + u8 byte4; +}; - /* Compile DMA descriptor - de-assert the NAND lock and interrupt. */ - d = &info->desc[descnum++]; - d->data = DMACMD_IRQ | DMACMD_DEC_SEM; +static int mxs_nand_get_readid(struct mxs_nand_info *info, void *databuf) +{ + int ret; + struct readid_data *id_data = databuf; - /* Execute the DMA chain. */ - ret = mxs_dma_run(info->dma_channel, info->desc, descnum); - if (ret) { - pr_err("DMA read error\n"); + ret = mxs_nand_read_id(info, 0x0, databuf, sizeof(struct readid_data)); + if (ret) return ret; - } - memcpy(&id_data, databuf, sizeof(struct readid_data)); + pr_debug("NAND Byte0: 0x%x\n", id_data->byte0); + pr_debug("NAND Byte1: 0x%x\n", id_data->byte1); + pr_debug("NAND Byte2: 0x%x\n", id_data->byte2); + pr_debug("NAND Byte3: 0x%x\n", id_data->byte3); + pr_debug("NAND Byte4: 0x%x\n", id_data->byte4); - pr_debug("NAND Byte0: 0x%x\n", id_data.byte0); - pr_debug("NAND Byte1: 0x%x\n", id_data.byte1); - pr_debug("NAND Byte2: 0x%x\n", id_data.byte2); - pr_debug("NAND Byte3: 0x%x\n", id_data.byte3); - pr_debug("NAND Byte4: 0x%x\n", id_data.byte4); - - if (id_data.byte0 == 0xff || id_data.byte1 == 0xff || - id_data.byte2 == 0xff || id_data.byte3 == 0xff || - id_data.byte4 == 0xff) { + if (id_data->byte0 == 0xff || id_data->byte1 == 0xff || + id_data->byte2 == 0xff || id_data->byte3 == 0xff || + id_data->byte4 == 0xff) { pr_err("\"READ ID\" returned 0xff, possible error!\n"); return -EOVERFLOW; } /* Fill the NAND organization struct with data */ info->organization.bits_per_cell = - (1 << ((id_data.byte2 >> 2) & 0x3)) * 2; + (1 << ((id_data->byte2 >> 2) & 0x3)) * 2; info->organization.pagesize = - (1 << (id_data.byte3 & 0x3)) * SZ_1K; - info->organization.oobsize = id_data.byte3 & 0x4 ? + (1 << (id_data->byte3 & 0x3)) * SZ_1K; + info->organization.oobsize = id_data->byte3 & 0x4 ? info->organization.pagesize / 512 * 16 : info->organization.pagesize / 512 * 8; info->organization.pages_per_eraseblock = - (1 << ((id_data.byte3 >> 4) & 0x3)) * SZ_64K / + (1 << ((id_data->byte3 >> 4) & 0x3)) * SZ_64K / info->organization.pagesize; info->organization.planes_per_lun = - 1 << ((id_data.byte4 >> 2) & 0x3); + 1 << ((id_data->byte4 >> 2) & 0x3); info->nand_size = info->organization.planes_per_lun * - (1 << ((id_data.byte4 >> 4) & 0x7)) * SZ_8M; + (1 << ((id_data->byte4 >> 4) & 0x7)) * SZ_8M; info->organization.eraseblocks_per_lun = info->nand_size / (info->organization.pages_per_eraseblock * info->organization.pagesize); @@ -889,6 +767,10 @@ static int mxs_nand_get_info(struct mxs_nand_info *info, void *databuf) { int ret, i; + ret = mxs_nand_reset(info, databuf); + if (ret) + return ret; + ret = mxs_nand_check_onfi(info, databuf); if (ret) { if (ret != 1) @@ -966,7 +848,7 @@ static uint32_t calc_chksum(void *buf, size_t size) return ~chksum; } -static int get_fcb(struct mxs_nand_info *info, void *databuf) +static int imx6_get_fcb(struct mxs_nand_info *info, void *databuf) { int i, pagenum, ret; uint32_t checksum; @@ -974,13 +856,17 @@ static int get_fcb(struct mxs_nand_info *info, void *databuf) /* First page read fails, this shouldn't be necessary */ mxs_nand_read_page(info, info->organization.pagesize, - info->organization.oobsize, 0, databuf, 1); + info->organization.oobsize, 0, databuf, 1, false); for (i = 0; i < 4; i++) { pagenum = info->organization.pages_per_eraseblock * i; ret = mxs_nand_read_page(info, info->organization.pagesize, - info->organization.oobsize, pagenum, databuf, 1); + info->organization.oobsize, pagenum, databuf, 1, false); + if (ret) + continue; + + ret = mxs_nand_get_ecc_status(info, databuf); if (ret) continue; @@ -1002,23 +888,72 @@ static int get_fcb(struct mxs_nand_info *info, void *databuf) continue; } - pr_debug("Found FCB:\n"); - pr_debug("PageDataSize: 0x%08x\n", fcb->PageDataSize); - pr_debug("TotalPageSize: 0x%08x\n", fcb->TotalPageSize); - pr_debug("SectorsPerBlock: 0x%08x\n", fcb->SectorsPerBlock); - pr_debug("FW1_startingPage: 0x%08x\n", - fcb->Firmware1_startingPage); - pr_debug("PagesInFW1: 0x%08x\n", fcb->PagesInFirmware1); - pr_debug("FW2_startingPage: 0x%08x\n", - fcb->Firmware2_startingPage); - pr_debug("PagesInFW2: 0x%08x\n", fcb->PagesInFirmware2); - return 0; } return -EINVAL; } +static int imx7_get_fcb_n(struct mxs_nand_info *info, void *databuf, int num) +{ + int ret; + int flips = 0; + uint8_t *status; + int i; + + ret = mxs_nand_read_page(info, BCH62_WRITESIZE, BCH62_OOBSIZE, + info->organization.pages_per_eraseblock * num, databuf, 0, true); + if (ret) + return ret; + + /* Loop over status bytes, accumulating ECC status. */ + status = databuf + BCH62_WRITESIZE + 32; + + for (i = 0; i < 8; i++) { + switch (status[i]) { + case 0x0: + break; + case 0xff: + /* + * A status of 0xff means the chunk is erased, but due to + * the randomizer we see this as random data. Explicitly + * memset it. + */ + memset(databuf + 0x80 * i, 0xff, 0x80); + break; + case 0xfe: + return -EBADMSG; + default: + flips += status[0]; + break; + } + } + + return ret; +} + +static int imx7_get_fcb(struct mxs_nand_info *info, void *databuf) +{ + int i, ret; + struct fcb_block *fcb = &info->fcb; + + mxs_nand_mode_fcb_62bit(info->bch_base); + + for (i = 0; i < 4; i++) { + ret = imx7_get_fcb_n(info, databuf, i); + if (!ret) + break; + } + + if (ret) { + pr_err("Cannot find FCB\n"); + } else { + memcpy(fcb, databuf, sizeof(*fcb)); + } + + return ret; +} + static int get_dbbt(struct mxs_nand_info *info, void *databuf) { int i, ret; @@ -1030,7 +965,11 @@ static int get_dbbt(struct mxs_nand_info *info, void *databuf) page = startpage + i * info->organization.pages_per_eraseblock; ret = mxs_nand_read_page(info, info->organization.pagesize, - info->organization.oobsize, page, databuf, 0); + info->organization.oobsize, page, databuf, 0, false); + if (ret) + continue; + + ret = mxs_nand_get_ecc_status(info, databuf); if (ret) continue; @@ -1044,7 +983,11 @@ static int get_dbbt(struct mxs_nand_info *info, void *databuf) return -ENOENT; ret = mxs_nand_read_page(info, info->organization.pagesize, - info->organization.oobsize, page + 4, databuf, 0); + info->organization.oobsize, page + 4, databuf, 0, false); + if (ret) + continue; + + ret = mxs_nand_get_ecc_status(info, databuf); if (ret) continue; @@ -1115,7 +1058,7 @@ static int read_firmware(struct mxs_nand_info *info, int startpage, } ret = mxs_nand_read_page(info, pagesize, oobsize, - curpage, dest, 0); + curpage, dest, 0, false); if (ret) { pr_debug("Failed to read page %d\n", curpage); return ret; @@ -1132,49 +1075,73 @@ static int read_firmware(struct mxs_nand_info *info, int startpage, return 0; } -static int __maybe_unused imx6_nand_load_image(void *cmdbuf, void *descs, - void *databuf, void *dest, int len) +struct imx_nand_params { + struct mxs_nand_info info; + struct apbh_dma apbh; + void *sdram; + int (*get_fcb)(struct mxs_nand_info *info, void *databuf); +}; + +static int __maybe_unused imx6_nand_load_image(struct imx_nand_params *params, + void *databuf, void *dest, int len) { - struct mxs_nand_info info = { - .io_base = (void *)0x00112000, - .bch_base = (void *)0x00114000, - }; - struct apbh_dma apbh = { - .id = IMX28_DMA, - .regs = (void *)0x00110000, - }; + struct mxs_nand_info *info = ¶ms->info; struct mxs_dma_chan pchan = { .channel = 0, /* MXS: MXS_DMA_CHANNEL_AHB_APBH_GPMI0 */ - .apbh = &apbh, + .apbh = ¶ms->apbh, }; int ret; struct fcb_block *fcb; + void __iomem *bch_regs = info->bch_base; + u32 fl0, fl1; - info.dma_channel = &pchan; + info->dma_channel = &pchan; pr_debug("cmdbuf: 0x%p descs: 0x%p databuf: 0x%p dest: 0x%p\n", - cmdbuf, descs, databuf, dest); - - /* Command buffers */ - info.cmd_buf = cmdbuf; - info.desc = descs; + info->cmd_buf, info->desc, databuf, dest); - ret = mxs_nand_get_info(&info, databuf); + ret = mxs_nand_get_info(info, databuf); if (ret) return ret; - ret = get_fcb(&info, databuf); + ret = params->get_fcb(info, databuf); if (ret) return ret; - fcb = &info.fcb; - - get_dbbt(&info, databuf); - - ret = read_firmware(&info, fcb->Firmware1_startingPage, dest, len); + fcb = &info->fcb; + + pr_debug("Found FCB:\n"); + pr_debug("PageDataSize: 0x%08x\n", fcb->PageDataSize); + pr_debug("TotalPageSize: 0x%08x\n", fcb->TotalPageSize); + pr_debug("SectorsPerBlock: 0x%08x\n", fcb->SectorsPerBlock); + pr_debug("FW1_startingPage: 0x%08x\n", + fcb->Firmware1_startingPage); + pr_debug("PagesInFW1: 0x%08x\n", fcb->PagesInFirmware1); + pr_debug("FW2_startingPage: 0x%08x\n", + fcb->Firmware2_startingPage); + pr_debug("PagesInFW2: 0x%08x\n", fcb->PagesInFirmware2); + + info->organization.oobsize = fcb->TotalPageSize - fcb->PageDataSize; + info->organization.pagesize = fcb->PageDataSize; + + fl0 = FIELD_PREP(BCH_FLASHLAYOUT0_NBLOCKS, fcb->NumEccBlocksPerPage) | + FIELD_PREP(BCH_FLASHLAYOUT0_META_SIZE, fcb->MetadataBytes) | + FIELD_PREP(IMX6_BCH_FLASHLAYOUT0_ECC0, fcb->EccBlock0EccType) | + (fcb->BCHType ? BCH_FLASHLAYOUT0_GF13_0_GF14_1 : 0) | + FIELD_PREP(BCH_FLASHLAYOUT0_DATA0_SIZE, fcb->EccBlock0Size / 4); + fl1 = FIELD_PREP(BCH_FLASHLAYOUT1_PAGE_SIZE, fcb->TotalPageSize) | + FIELD_PREP(IMX6_BCH_FLASHLAYOUT1_ECCN, fcb->EccBlockNEccType) | + (fcb->BCHType ? BCH_FLASHLAYOUT1_GF13_0_GF14_1 : 0) | + FIELD_PREP(BCH_FLASHLAYOUT1_DATAN_SIZE, fcb->EccBlockNSize / 4); + writel(fl0, bch_regs + BCH_FLASH0LAYOUT0); + writel(fl1, bch_regs + BCH_FLASH0LAYOUT1); + + get_dbbt(info, databuf); + + ret = read_firmware(info, fcb->Firmware1_startingPage, dest, len); if (ret) { pr_err("Failed to read firmware1, trying firmware2\n"); - ret = read_firmware(&info, fcb->Firmware2_startingPage, + ret = read_firmware(info, fcb->Firmware2_startingPage, dest, len); if (ret) { pr_err("Failed to also read firmware2\n"); @@ -1185,24 +1152,21 @@ static int __maybe_unused imx6_nand_load_image(void *cmdbuf, void *descs, return 0; } -int imx6_nand_start_image(void) +static int imx_nand_start_image(struct imx_nand_params *params) { + struct mxs_nand_info *info = ¶ms->info; int ret; - void *sdram = (void *)0x10000000; void __noreturn (*bb)(void); - void *cmdbuf, *databuf, *descs; + void *databuf; - cmdbuf = sdram; - descs = sdram + MXS_NAND_COMMAND_BUFFER_SIZE; - databuf = descs + + /* Command buffers */ + info->cmd_buf = params->sdram; + info->desc = params->sdram + MXS_NAND_COMMAND_BUFFER_SIZE; + databuf = info->desc + sizeof(struct mxs_dma_cmd) * MXS_NAND_DMA_DESCRIPTOR_COUNT; bb = (void *)PAGE_ALIGN((unsigned long)databuf + SZ_8K); - /* Apply ERR007117 workaround */ - imx6_errata_007117_enable(); - - ret = imx6_nand_load_image(cmdbuf, descs, databuf, - bb, imx_image_size()); + ret = imx6_nand_load_image(params, databuf, bb, imx_image_size()); if (ret) { pr_err("Loading image failed: %d\n", ret); return ret; @@ -1215,3 +1179,34 @@ int imx6_nand_start_image(void) bb(); } + +int imx6_nand_start_image(void) +{ + static struct imx_nand_params params = { + .info.io_base = IOMEM(MX6_GPMI_BASE_ADDR), + .info.bch_base = IOMEM(MX6_BCH_BASE_ADDR), + .apbh.regs = IOMEM(MX6_APBH_BASE_ADDR), + .apbh.id = IMX28_DMA, + .sdram = (void *)MX6_MMDC_PORT01_BASE_ADDR, + .get_fcb = imx6_get_fcb, + }; + + /* Apply ERR007117 workaround */ + imx6_errata_007117_enable(); + + return imx_nand_start_image(¶ms); +} + +int imx7_nand_start_image(void) +{ + static struct imx_nand_params params = { + .info.io_base = IOMEM(MX7_GPMI_BASE), + .info.bch_base = IOMEM(MX7_BCH_BASE), + .apbh.regs = IOMEM(MX7_APBH_BASE), + .apbh.id = IMX28_DMA, + .sdram = (void *)MX7_DDR_BASE_ADDR, + .get_fcb = imx7_get_fcb, + }; + + return imx_nand_start_image(¶ms); +} |