summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-imx/xload-gpmi-nand.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-imx/xload-gpmi-nand.c')
-rw-r--r--arch/arm/mach-imx/xload-gpmi-nand.c663
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 = &params->info;
struct mxs_dma_chan pchan = {
.channel = 0, /* MXS: MXS_DMA_CHANNEL_AHB_APBH_GPMI0 */
- .apbh = &apbh,
+ .apbh = &params->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 = &params->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(&params);
+}
+
+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(&params);
+}