summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2022-12-12 20:11:19 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2022-12-12 20:11:19 +0100
commitca188ad0da3b95ee99eab57187f995492bf259eb (patch)
tree7919e478a7a2d90f7dc45a7b0e1bebf20f888f70
parent7007b46ca8cff0d7b6cc951c64f6a47676a5811a (diff)
parent44b966656c55c4e70195c293f121b5d8477b3806 (diff)
downloadbarebox-ca188ad0da3b95ee99eab57187f995492bf259eb.tar.gz
Merge branch 'for-next/imx7-nand-xload'
-rw-r--r--arch/arm/mach-imx/include/mach/imx6-regs.h2
-rw-r--r--arch/arm/mach-imx/include/mach/imx7-regs.h1
-rw-r--r--arch/arm/mach-imx/include/mach/xload.h1
-rw-r--r--arch/arm/mach-imx/xload-gpmi-nand.c639
-rw-r--r--arch/arm/mach-mxs/include/mach/generic.h3
-rw-r--r--commands/Kconfig9
-rw-r--r--common/imx-bbu-nand-fcb.c231
-rw-r--r--drivers/dma/apbh_dma.c514
-rw-r--r--drivers/mtd/nand/nand_mxs.c412
-rw-r--r--include/dma/apbh-dma.h95
-rw-r--r--include/soc/imx/gpmi-nand.h147
11 files changed, 811 insertions, 1243 deletions
diff --git a/arch/arm/mach-imx/include/mach/imx6-regs.h b/arch/arm/mach-imx/include/mach/imx6-regs.h
index 35f03036cb..39e2751533 100644
--- a/arch/arm/mach-imx/include/mach/imx6-regs.h
+++ b/arch/arm/mach-imx/include/mach/imx6-regs.h
@@ -3,7 +3,9 @@
#ifndef __MACH_IMX6_REGS_H
#define __MACH_IMX6_REGS_H
+#define MX6_APBH_BASE_ADDR 0x00110000
#define MX6_GPMI_BASE_ADDR 0x00112000
+#define MX6_BCH_BASE_ADDR 0x00114000
#define MX6_FAST1_BASE_ADDR 0x00c00000
#define MX6_FAST2_BASE_ADDR 0x00b00000
diff --git a/arch/arm/mach-imx/include/mach/imx7-regs.h b/arch/arm/mach-imx/include/mach/imx7-regs.h
index 1ee7d86e0e..379be9e062 100644
--- a/arch/arm/mach-imx/include/mach/imx7-regs.h
+++ b/arch/arm/mach-imx/include/mach/imx7-regs.h
@@ -118,6 +118,7 @@
#define MX7_ENET1_BASE_ADDR (MX7_AIPS3_BASE_ADDR + 0x3E0000)
#define MX7_ENET2_BASE_ADDR (MX7_AIPS3_BASE_ADDR + 0x3F0000)
+#define MX7_APBH_BASE 0x33000000
#define MX7_GPMI_BASE 0x33002000
#define MX7_BCH_BASE 0x33004000
diff --git a/arch/arm/mach-imx/include/mach/xload.h b/arch/arm/mach-imx/include/mach/xload.h
index d19bedb9e8..aa2fa5129a 100644
--- a/arch/arm/mach-imx/include/mach/xload.h
+++ b/arch/arm/mach-imx/include/mach/xload.h
@@ -12,6 +12,7 @@ int imx6_spi_start_image(int instance);
int imx6_esdhc_start_image(int instance);
int imx6_nand_start_image(void);
int imx7_esdhc_start_image(int instance);
+int imx7_nand_start_image(void);
int imx8m_esdhc_load_image(int instance, bool start);
int imx8mn_esdhc_load_image(int instance, bool start);
int imx8mp_esdhc_load_image(int instance, bool start);
diff --git a/arch/arm/mach-imx/xload-gpmi-nand.c b/arch/arm/mach-imx/xload-gpmi-nand.c
index 3a4f331ce6..dcef8cda48 100644
--- a/arch/arm/mach-imx/xload-gpmi-nand.c
+++ b/arch/arm/mach-imx/xload-gpmi-nand.c
@@ -7,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 <soc/imx/imx-nand-bcb.h>
#include <linux/mtd/rawnand.h>
+#include <soc/imx/gpmi-nand.h>
#include <mach/imx6-regs.h>
+#include <mach/imx7-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 <dma/apbh-dma.h>
struct apbh_dma {
void __iomem *regs;
@@ -60,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)
{
@@ -167,7 +117,7 @@ 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 = (unsigned long)(&pdesc[i + 1]);
- pdesc[i].data |= DMACMD_CHAIN;
+ pdesc[i].data |= MXS_DMA_DESC_CHAIN;
}
writel(1 << (pchan->channel + BP_APBHX_CTRL1_CH_CMDCMPLT_IRQ_EN),
@@ -203,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;
@@ -313,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;
@@ -321,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 /
@@ -345,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;
@@ -365,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;
@@ -419,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);
@@ -460,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;
@@ -492,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);
@@ -550,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);
@@ -622,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);
@@ -700,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;
@@ -708,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);
@@ -724,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 onfi_header *onfi_head = databuf;
- struct readid_data {
- u8 byte0;
- u8 byte1;
- u8 byte2;
- u8 byte3;
- u8 byte4;
- } id_data;
+ ret = mxs_nand_read_id(info, 0x20, databuf, sizeof(struct onfi_header));
+ if (ret)
+ return ret;
- memset(info->desc, 0,
- sizeof(*info->desc) * MXS_NAND_DMA_DESCRIPTOR_COUNT);
+ 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);
- /* 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;
-
- 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);
@@ -881,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)
@@ -958,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;
@@ -966,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;
@@ -994,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;
@@ -1022,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;
@@ -1036,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;
@@ -1107,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;
@@ -1124,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);
+ info->cmd_buf, info->desc, databuf, dest);
- /* Command buffers */
- info.cmd_buf = cmdbuf;
- info.desc = descs;
-
- 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");
@@ -1177,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;
@@ -1207,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);
+}
diff --git a/arch/arm/mach-mxs/include/mach/generic.h b/arch/arm/mach-mxs/include/mach/generic.h
index dc1b6ed4a0..4cba591000 100644
--- a/arch/arm/mach-mxs/include/mach/generic.h
+++ b/arch/arm/mach-mxs/include/mach/generic.h
@@ -22,3 +22,6 @@
#define cpu_is_mx51() (0)
#define cpu_is_mx53() (0)
#define cpu_is_mx6() (0)
+#define cpu_is_mx7() (0)
+#define cpu_is_mx6ul() (0)
+#define cpu_is_mx6ull() (0)
diff --git a/commands/Kconfig b/commands/Kconfig
index 555ae401a0..ec15f4e543 100644
--- a/commands/Kconfig
+++ b/commands/Kconfig
@@ -334,6 +334,15 @@ config CMD_SLICE
command can be used to print informations about slices and also to manipulate
them on the command line for debugging purposes.
+config CMD_FCB
+ depends on BAREBOX_UPDATE_IMX_NAND_FCB
+ tristate
+ prompt "fcb"
+ help
+ Several i.MX SoCs booting from NAND flash need a so called Flash Control Block
+ at the beginning of the NAND device. The fcb command prints information about
+ the FCB.
+
# end Information commands
endmenu
diff --git a/common/imx-bbu-nand-fcb.c b/common/imx-bbu-nand-fcb.c
index 63c81e4ed6..24b0e7592e 100644
--- a/common/imx-bbu-nand-fcb.c
+++ b/common/imx-bbu-nand-fcb.c
@@ -14,6 +14,8 @@
#include <linux/sizes.h>
#include <bbu.h>
#include <fs.h>
+#include <command.h>
+#include <complete.h>
#include <linux/mtd/mtd-abi.h>
#include <linux/mtd/nand_mxs.h>
#include <linux/mtd/mtd.h>
@@ -26,19 +28,14 @@
#include <mach/generic.h>
#include <mtd/mtd-peb.h>
#include <soc/imx/imx-nand-bcb.h>
-
-#ifdef CONFIG_ARCH_IMX28
-static inline int fcb_is_bch_encoded(void)
-{
- return 0;
-}
-#else
+#ifdef CONFIG_ARCH_IMX
#include <mach/imx6.h>
+#endif
+
static inline int fcb_is_bch_encoded(void)
{
return cpu_is_mx6ul() || cpu_is_mx6ull();
}
-#endif
struct imx_nand_fcb_bbu_handler {
struct bbu_handler handler;
@@ -284,57 +281,57 @@ static __maybe_unused void dump_fcb(void *buf)
{
struct fcb_block *fcb = buf;
- pr_debug("Checksum: 0x%08x\n", fcb->Checksum);
- pr_debug("FingerPrint: 0x%08x\n", fcb->FingerPrint);
- pr_debug("Version: 0x%08x\n", fcb->Version);
- pr_debug("DataSetup: 0x%02x\n", fcb->DataSetup);
- pr_debug("DataHold: 0x%02x\n", fcb->DataHold);
- pr_debug("AddressSetup: 0x%02x\n", fcb->AddressSetup);
- pr_debug("DSAMPLE_TIME: 0x%02x\n", fcb->DSAMPLE_TIME);
- pr_debug("NandTimingState: 0x%02x\n", fcb->NandTimingState);
- pr_debug("REA: 0x%02x\n", fcb->REA);
- pr_debug("RLOH: 0x%02x\n", fcb->RLOH);
- pr_debug("RHOH: 0x%02x\n", fcb->RHOH);
- 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("NumberOfNANDs: 0x%08x\n", fcb->NumberOfNANDs);
- pr_debug("TotalInternalDie: 0x%08x\n", fcb->TotalInternalDie);
- pr_debug("CellType: 0x%08x\n", fcb->CellType);
- pr_debug("EccBlockNEccType: 0x%08x\n", fcb->EccBlockNEccType);
- pr_debug("EccBlock0Size: 0x%08x\n", fcb->EccBlock0Size);
- pr_debug("EccBlockNSize: 0x%08x\n", fcb->EccBlockNSize);
- pr_debug("EccBlock0EccType: 0x%08x\n", fcb->EccBlock0EccType);
- pr_debug("MetadataBytes: 0x%08x\n", fcb->MetadataBytes);
- pr_debug("NumEccBlocksPerPage: 0x%08x\n", fcb->NumEccBlocksPerPage);
- pr_debug("EccBlockNEccLevelSDK: 0x%08x\n", fcb->EccBlockNEccLevelSDK);
- pr_debug("EccBlock0SizeSDK: 0x%08x\n", fcb->EccBlock0SizeSDK);
- pr_debug("EccBlockNSizeSDK: 0x%08x\n", fcb->EccBlockNSizeSDK);
- pr_debug("EccBlock0EccLevelSDK: 0x%08x\n", fcb->EccBlock0EccLevelSDK);
- pr_debug("NumEccBlocksPerPageSDK: 0x%08x\n", fcb->NumEccBlocksPerPageSDK);
- pr_debug("MetadataBytesSDK: 0x%08x\n", fcb->MetadataBytesSDK);
- pr_debug("EraseThreshold: 0x%08x\n", fcb->EraseThreshold);
- pr_debug("BootPatch: 0x%08x\n", fcb->BootPatch);
- pr_debug("PatchSectors: 0x%08x\n", fcb->PatchSectors);
- pr_debug("Firmware1_startingPage: 0x%08x\n", fcb->Firmware1_startingPage);
- pr_debug("Firmware2_startingPage: 0x%08x\n", fcb->Firmware2_startingPage);
- pr_debug("PagesInFirmware1: 0x%08x\n", fcb->PagesInFirmware1);
- pr_debug("PagesInFirmware2: 0x%08x\n", fcb->PagesInFirmware2);
- pr_debug("DBBTSearchAreaStartAddress: 0x%08x\n", fcb->DBBTSearchAreaStartAddress);
- pr_debug("BadBlockMarkerByte: 0x%08x\n", fcb->BadBlockMarkerByte);
- pr_debug("BadBlockMarkerStartBit: 0x%08x\n", fcb->BadBlockMarkerStartBit);
- pr_debug("BBMarkerPhysicalOffset: 0x%08x\n", fcb->BBMarkerPhysicalOffset);
- pr_debug("BCHType: 0x%08x\n", fcb->BCHType);
- pr_debug("TMTiming2_ReadLatency: 0x%08x\n", fcb->TMTiming2_ReadLatency);
- pr_debug("TMTiming2_PreambleDelay: 0x%08x\n", fcb->TMTiming2_PreambleDelay);
- pr_debug("TMTiming2_CEDelay: 0x%08x\n", fcb->TMTiming2_CEDelay);
- pr_debug("TMTiming2_PostambleDelay: 0x%08x\n", fcb->TMTiming2_PostambleDelay);
- pr_debug("TMTiming2_CmdAddPause: 0x%08x\n", fcb->TMTiming2_CmdAddPause);
- pr_debug("TMTiming2_DataPause: 0x%08x\n", fcb->TMTiming2_DataPause);
- pr_debug("TMSpeed: 0x%08x\n", fcb->TMSpeed);
- pr_debug("TMTiming1_BusyTimeout: 0x%08x\n", fcb->TMTiming1_BusyTimeout);
- pr_debug("DISBBM: 0x%08x\n", fcb->DISBBM);
- pr_debug("BBMarkerPhysOfsInSpareData: 0x%08x\n", fcb->BBMarkerPhysicalOffsetInSpareData);
+ printf("Checksum: 0x%08x\n", fcb->Checksum);
+ printf("FingerPrint: 0x%08x\n", fcb->FingerPrint);
+ printf("Version: 0x%08x\n", fcb->Version);
+ printf("DataSetup: 0x%02x\n", fcb->DataSetup);
+ printf("DataHold: 0x%02x\n", fcb->DataHold);
+ printf("AddressSetup: 0x%02x\n", fcb->AddressSetup);
+ printf("DSAMPLE_TIME: 0x%02x\n", fcb->DSAMPLE_TIME);
+ printf("NandTimingState: 0x%02x\n", fcb->NandTimingState);
+ printf("REA: 0x%02x\n", fcb->REA);
+ printf("RLOH: 0x%02x\n", fcb->RLOH);
+ printf("RHOH: 0x%02x\n", fcb->RHOH);
+ printf("PageDataSize: 0x%08x\n", fcb->PageDataSize);
+ printf("TotalPageSize: 0x%08x\n", fcb->TotalPageSize);
+ printf("SectorsPerBlock: 0x%08x\n", fcb->SectorsPerBlock);
+ printf("NumberOfNANDs: 0x%08x\n", fcb->NumberOfNANDs);
+ printf("TotalInternalDie: 0x%08x\n", fcb->TotalInternalDie);
+ printf("CellType: 0x%08x\n", fcb->CellType);
+ printf("EccBlockNEccType: 0x%08x\n", fcb->EccBlockNEccType);
+ printf("EccBlock0Size: 0x%08x\n", fcb->EccBlock0Size);
+ printf("EccBlockNSize: 0x%08x\n", fcb->EccBlockNSize);
+ printf("EccBlock0EccType: 0x%08x\n", fcb->EccBlock0EccType);
+ printf("MetadataBytes: 0x%08x\n", fcb->MetadataBytes);
+ printf("NumEccBlocksPerPage: 0x%08x\n", fcb->NumEccBlocksPerPage);
+ printf("EccBlockNEccLevelSDK: 0x%08x\n", fcb->EccBlockNEccLevelSDK);
+ printf("EccBlock0SizeSDK: 0x%08x\n", fcb->EccBlock0SizeSDK);
+ printf("EccBlockNSizeSDK: 0x%08x\n", fcb->EccBlockNSizeSDK);
+ printf("EccBlock0EccLevelSDK: 0x%08x\n", fcb->EccBlock0EccLevelSDK);
+ printf("NumEccBlocksPerPageSDK: 0x%08x\n", fcb->NumEccBlocksPerPageSDK);
+ printf("MetadataBytesSDK: 0x%08x\n", fcb->MetadataBytesSDK);
+ printf("EraseThreshold: 0x%08x\n", fcb->EraseThreshold);
+ printf("BootPatch: 0x%08x\n", fcb->BootPatch);
+ printf("PatchSectors: 0x%08x\n", fcb->PatchSectors);
+ printf("Firmware1_startingPage: 0x%08x\n", fcb->Firmware1_startingPage);
+ printf("Firmware2_startingPage: 0x%08x\n", fcb->Firmware2_startingPage);
+ printf("PagesInFirmware1: 0x%08x\n", fcb->PagesInFirmware1);
+ printf("PagesInFirmware2: 0x%08x\n", fcb->PagesInFirmware2);
+ printf("DBBTSearchAreaStartAddress: 0x%08x\n", fcb->DBBTSearchAreaStartAddress);
+ printf("BadBlockMarkerByte: 0x%08x\n", fcb->BadBlockMarkerByte);
+ printf("BadBlockMarkerStartBit: 0x%08x\n", fcb->BadBlockMarkerStartBit);
+ printf("BBMarkerPhysicalOffset: 0x%08x\n", fcb->BBMarkerPhysicalOffset);
+ printf("BCHType: 0x%08x\n", fcb->BCHType);
+ printf("TMTiming2_ReadLatency: 0x%08x\n", fcb->TMTiming2_ReadLatency);
+ printf("TMTiming2_PreambleDelay: 0x%08x\n", fcb->TMTiming2_PreambleDelay);
+ printf("TMTiming2_CEDelay: 0x%08x\n", fcb->TMTiming2_CEDelay);
+ printf("TMTiming2_PostambleDelay: 0x%08x\n", fcb->TMTiming2_PostambleDelay);
+ printf("TMTiming2_CmdAddPause: 0x%08x\n", fcb->TMTiming2_CmdAddPause);
+ printf("TMTiming2_DataPause: 0x%08x\n", fcb->TMTiming2_DataPause);
+ printf("TMSpeed: 0x%08x\n", fcb->TMSpeed);
+ printf("TMTiming1_BusyTimeout: 0x%08x\n", fcb->TMTiming1_BusyTimeout);
+ printf("DISBBM: 0x%08x\n", fcb->DISBBM);
+ printf("BBMarkerPhysOfsInSpareData: 0x%08x\n", fcb->BBMarkerPhysicalOffsetInSpareData);
}
static __maybe_unused ssize_t raw_read_page(struct mtd_info *mtd, void *dst, loff_t offset)
@@ -1554,6 +1551,20 @@ int imx28_bbu_nand_register_handler(const char *name, unsigned long flags)
}
#endif
+static int imx7_fcb_read(struct mtd_info *mtd, int block, struct fcb_block **retfcb)
+{
+ struct fcb_block *fcb = xzalloc(mtd->writesize);
+ int ret;
+
+ ret = mxs_nand_read_fcb_bch62(block, fcb, sizeof(*fcb));
+ if (ret)
+ free(fcb);
+ else
+ *retfcb = fcb;
+
+ return ret;
+}
+
#ifdef CONFIG_ARCH_IMX7
#include <mach/imx7-regs.h>
@@ -1572,29 +1583,15 @@ static void imx7_fcb_create(struct imx_nand_fcb_bbu_handler *imx_handler,
fl0 = readl(bch_regs + BCH_FLASH0LAYOUT0);
fcb->MetadataBytes = BF_VAL(fl0, BCH_FLASHLAYOUT0_META_SIZE);
fcb->NumEccBlocksPerPage = BF_VAL(fl0, BCH_FLASHLAYOUT0_NBLOCKS);
+ fcb->EccBlock0Size = 4 * BF_VAL(fl0, BCH_FLASHLAYOUT0_DATA0_SIZE);
+ fcb->EccBlock0EccType = BF_VAL(fl0, BCH_FLASHLAYOUT0_ECC0);
fl1 = readl(bch_regs + BCH_FLASH0LAYOUT1);
- fcb->EccBlock0Size = 4 * BF_VAL(fl1, BCH_FLASHLAYOUT0_DATA0_SIZE);
- fcb->EccBlock0EccType = BF_VAL(fl1, BCH_FLASHLAYOUT0_ECC0);
fcb->EccBlockNSize = 4 * BF_VAL(fl1, BCH_FLASHLAYOUT1_DATAN_SIZE);
fcb->EccBlockNEccType = BF_VAL(fl1, BCH_FLASHLAYOUT1_ECCN);
fcb->BCHType = BF_VAL(fl1, BCH_FLASHLAYOUT1_GF13_0_GF14_1);
}
-static int imx7_fcb_read(struct mtd_info *mtd, int block, struct fcb_block **retfcb)
-{
- struct fcb_block *fcb = xzalloc(mtd->writesize);
- int ret;
-
- ret = mxs_nand_read_fcb_bch62(block, fcb, sizeof(*fcb));
- if (ret)
- free(fcb);
- else
- *retfcb = fcb;
-
- return ret;
-}
-
static int imx7_fcb_write(struct mtd_info *mtd, int block, struct fcb_block *fcb)
{
return mxs_nand_write_fcb_bch62(block, fcb, sizeof(*fcb));
@@ -1625,3 +1622,87 @@ int imx7_bbu_nand_register_handler(const char *name, unsigned long flags)
return ret;
}
#endif
+
+static void dump_fcb_n(struct fcb_block **fcbs, int n)
+{
+ int i;
+
+ if (!n || !fcbs[n])
+ goto skip_compare;
+
+ for (i = 0; i < n; i++) {
+ if (!fcbs[i] || !fcbs[n])
+ continue;
+
+ if (!memcmp(fcbs[i], fcbs[n], sizeof(struct fcb_block))) {
+ printf("FCB block#%d: same as FCB block#%d\n", n, i);
+ return;
+ }
+ }
+
+skip_compare:
+ if (fcbs[n]) {
+ printf("FCB block#%d:\n", n);
+ dump_fcb(fcbs[n]);
+ } else {
+ printf("FCB block#%d: NULL\n", n);
+ }
+}
+
+static int fcb_read(struct mtd_info *mtd, int block, struct fcb_block **retfcb)
+{
+ if (cpu_is_mx7())
+ return imx7_fcb_read(mtd, block, retfcb);
+ else if (fcb_is_bch_encoded())
+ return fcb_read_bch(mtd, block, retfcb);
+ else
+ return fcb_read_hamming_13_8(mtd, block, retfcb);
+}
+
+static int cmd_fcb(int argc, char *argv[])
+{
+ struct cdev *cdev;
+ struct mtd_info *mtd;
+ struct fcb_block *fcb[4] = {};
+ int i, ret;
+
+ cdev = cdev_open_by_name("nand0", O_RDONLY);
+ if (!cdev) {
+ printf("Cannot open nand0\n");
+ return COMMAND_ERROR;
+ }
+
+ mtd = cdev->mtd;
+ if (!mtd) {
+ ret = COMMAND_ERROR;
+ goto out;
+ }
+
+ for (i = 0; i < 4; i++)
+ fcb_read(mtd, i, &fcb[i]);
+
+ for (i = 0; i < 4; i++)
+ dump_fcb_n(fcb, i);
+
+ for (i = 0; i < 4; i++)
+ free(fcb[i]);
+
+ ret = 0;
+
+out:
+ cdev_close(cdev);
+
+ return ret;
+}
+
+BAREBOX_CMD_HELP_START(fcb)
+BAREBOX_CMD_HELP_TEXT("Dump FCB as found on /dev/nand0")
+BAREBOX_CMD_HELP_END
+
+BAREBOX_CMD_START(fcb)
+ .cmd = cmd_fcb,
+ BAREBOX_CMD_DESC("Dump Flash Control Block (FCB)")
+ BAREBOX_CMD_GROUP(CMD_GRP_MISC)
+ BAREBOX_CMD_HELP(cmd_fcb_help)
+ BAREBOX_CMD_COMPLETE(empty_complete)
+BAREBOX_CMD_END
diff --git a/drivers/dma/apbh_dma.c b/drivers/dma/apbh_dma.c
index 83bd783d34..767c095314 100644
--- a/drivers/dma/apbh_dma.c
+++ b/drivers/dma/apbh_dma.c
@@ -24,41 +24,12 @@
#include <init.h>
#include <io.h>
-
-#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 BM_APBHX_CHn_SEMA_PHORE (0xff << 16)
-#define BP_APBHX_CHn_SEMA_PHORE 16
-
-static struct mxs_dma_chan mxs_dma_channels[MXS_MAX_DMA_CHANNELS];
-
-enum mxs_dma_id {
- UNKNOWN_DMA_ID,
- IMX23_DMA,
- IMX28_DMA,
-};
-
struct apbh_dma {
void __iomem *regs;
struct clk *clk;
enum mxs_dma_id id;
};
-#define apbh_dma_is_imx23(aphb) ((apbh)->id == IMX23_DMA)
-
static struct apbh_dma *apbh_dma;
/*
@@ -66,185 +37,9 @@ static struct apbh_dma *apbh_dma;
*/
static int mxs_dma_validate_chan(int channel)
{
- struct mxs_dma_chan *pchan;
-
if ((channel < 0) || (channel >= MXS_MAX_DMA_CHANNELS))
return -EINVAL;
- pchan = mxs_dma_channels + channel;
- if (!(pchan->flags & MXS_DMA_FLAGS_ALLOCATED))
- return -EINVAL;
-
- return 0;
-}
-
-/*
- * Return the address of the command within a descriptor.
- */
-static unsigned int mxs_dma_cmd_address(struct mxs_dma_desc *desc)
-{
- return desc->address + offsetof(struct mxs_dma_desc, cmd);
-}
-
-/*
- * Read a DMA channel's hardware semaphore.
- *
- * As used by the MXS platform's DMA software, the DMA channel's hardware
- * semaphore reflects the number of DMA commands the hardware will process, but
- * has not yet finished. This is a volatile value read directly from hardware,
- * so it must be be viewed as immediately stale.
- *
- * If the channel is not marked busy, or has finished processing all its
- * commands, this value should be zero.
- *
- * See mxs_dma_append() for details on how DMA command blocks must be configured
- * to maintain the expected behavior of the semaphore's value.
- */
-static int mxs_dma_read_semaphore(int channel)
-{
- struct apbh_dma *apbh = apbh_dma;
- uint32_t tmp;
- int ret;
-
- ret = mxs_dma_validate_chan(channel);
- if (ret)
- return ret;
-
- if (apbh_dma_is_imx23(apbh))
- tmp = readl(apbh->regs + HW_APBHX_CHn_SEMA_MX23(channel));
- else
- tmp = readl(apbh->regs + HW_APBHX_CHn_SEMA_MX28(channel));
-
- tmp &= BM_APBHX_CHn_SEMA_PHORE;
- tmp >>= BP_APBHX_CHn_SEMA_PHORE;
-
- return tmp;
-}
-
-/*
- * Enable a DMA channel.
- *
- * If the given channel has any DMA descriptors on its active list, this
- * function causes the DMA hardware to begin processing them.
- *
- * This function marks the DMA channel as "busy," whether or not there are any
- * descriptors to process.
- */
-static int mxs_dma_enable(int channel)
-{
- struct apbh_dma *apbh = apbh_dma;
- unsigned int sem;
- struct mxs_dma_chan *pchan;
- struct mxs_dma_desc *pdesc;
- int channel_bit, ret;
-
- ret = mxs_dma_validate_chan(channel);
- if (ret)
- return ret;
-
- pchan = mxs_dma_channels + channel;
-
- if (pchan->pending_num == 0) {
- pchan->flags |= MXS_DMA_FLAGS_BUSY;
- return 0;
- }
-
- pdesc = list_first_entry(&pchan->active, struct mxs_dma_desc, node);
- if (pdesc == NULL)
- return -EFAULT;
-
- if (pchan->flags & MXS_DMA_FLAGS_BUSY) {
- if (!(pdesc->cmd.data & MXS_DMA_DESC_CHAIN))
- return 0;
-
- sem = mxs_dma_read_semaphore(channel);
- if (sem == 0)
- return 0;
-
- if (sem == 1) {
- pdesc = list_entry(pdesc->node.next,
- struct mxs_dma_desc, node);
- if (apbh_dma_is_imx23(apbh))
- writel(mxs_dma_cmd_address(pdesc),
- apbh->regs + HW_APBHX_CHn_NXTCMDAR_MX23(channel));
- else
- writel(mxs_dma_cmd_address(pdesc),
- apbh->regs + HW_APBHX_CHn_NXTCMDAR_MX28(channel));
- }
-
- if (apbh_dma_is_imx23(apbh))
- writel(pchan->pending_num,
- apbh->regs + HW_APBHX_CHn_SEMA_MX23(channel));
- else
- writel(pchan->pending_num,
- apbh->regs + HW_APBHX_CHn_SEMA_MX28(channel));
-
- pchan->active_num += pchan->pending_num;
- pchan->pending_num = 0;
- } else {
- pchan->active_num += pchan->pending_num;
- pchan->pending_num = 0;
- if (apbh_dma_is_imx23(apbh)) {
- writel(mxs_dma_cmd_address(pdesc),
- apbh->regs + HW_APBHX_CHn_NXTCMDAR_MX23(channel));
- writel(pchan->active_num,
- apbh->regs + HW_APBHX_CHn_SEMA_MX23(channel));
- channel_bit = channel + BP_APBH_CTRL0_CLKGATE_CHANNEL;
- } else {
- writel(mxs_dma_cmd_address(pdesc),
- apbh->regs + HW_APBHX_CHn_NXTCMDAR_MX28(channel));
- writel(pchan->active_num,
- apbh->regs + HW_APBHX_CHn_SEMA_MX28(channel));
- channel_bit = channel;
- }
- writel(1 << channel_bit, apbh->regs + HW_APBHX_CTRL0 + STMP_OFFSET_REG_CLR);
- }
-
- pchan->flags |= MXS_DMA_FLAGS_BUSY;
- return 0;
-}
-
-/*
- * Disable a DMA channel.
- *
- * This function shuts down a DMA channel and marks it as "not busy." Any
- * descriptors on the active list are immediately moved to the head of the
- * "done" list, whether or not they have actually been processed by the
- * hardware. The "ready" flags of these descriptors are NOT cleared, so they
- * still appear to be active.
- *
- * This function immediately shuts down a DMA channel's hardware, aborting any
- * I/O that may be in progress, potentially leaving I/O hardware in an undefined
- * state. It is unwise to call this function if there is ANY chance the hardware
- * is still processing a command.
- */
-static int mxs_dma_disable(int channel)
-{
- struct mxs_dma_chan *pchan;
- struct apbh_dma *apbh = apbh_dma;
- int channel_bit, ret;
-
- ret = mxs_dma_validate_chan(channel);
- if (ret)
- return ret;
-
- pchan = mxs_dma_channels + channel;
-
- if (!(pchan->flags & MXS_DMA_FLAGS_BUSY))
- return -EINVAL;
-
- if (apbh_dma_is_imx23(apbh))
- channel_bit = channel + BP_APBH_CTRL0_CLKGATE_CHANNEL;
- else
- channel_bit = channel + 0;
-
- writel(1 << channel_bit, apbh->regs + HW_APBHX_CTRL0 + STMP_OFFSET_REG_SET);
-
- pchan->flags &= ~MXS_DMA_FLAGS_BUSY;
- pchan->active_num = 0;
- pchan->pending_num = 0;
- list_splice_init(&pchan->active, &pchan->done);
-
return 0;
}
@@ -254,11 +49,6 @@ static int mxs_dma_disable(int channel)
static int mxs_dma_reset(int channel)
{
struct apbh_dma *apbh = apbh_dma;
- int ret;
-
- ret = mxs_dma_validate_chan(channel);
- if (ret)
- return ret;
if (apbh_dma_is_imx23(apbh))
writel(1 << (channel + BP_APBH_CTRL0_RESET_CHANNEL),
@@ -271,30 +61,6 @@ static int mxs_dma_reset(int channel)
}
/*
- * Enable or disable DMA interrupt.
- *
- * This function enables the given DMA channel to interrupt the CPU.
- */
-static int mxs_dma_enable_irq(int channel, int enable)
-{
- struct apbh_dma *apbh = apbh_dma;
- int ret;
-
- ret = mxs_dma_validate_chan(channel);
- if (ret)
- return ret;
-
- if (enable)
- writel(1 << (channel + BP_APBHX_CTRL1_CH_CMDCMPLT_IRQ_EN),
- apbh->regs + HW_APBHX_CTRL1 + STMP_OFFSET_REG_SET);
- else
- writel(1 << (channel + BP_APBHX_CTRL1_CH_CMDCMPLT_IRQ_EN),
- apbh->regs + HW_APBHX_CTRL1 + STMP_OFFSET_REG_CLR);
-
- return 0;
-}
-
-/*
* Clear DMA interrupt.
*
* The software that is using the DMA channel must register to receive its
@@ -303,11 +69,6 @@ static int mxs_dma_enable_irq(int channel, int enable)
static int mxs_dma_ack_irq(int channel)
{
struct apbh_dma *apbh = apbh_dma;
- int ret;
-
- ret = mxs_dma_validate_chan(channel);
- if (ret)
- return ret;
writel(1 << channel, apbh->regs + HW_APBHX_CTRL1 + STMP_OFFSET_REG_CLR);
writel(1 << channel, apbh->regs + HW_APBHX_CTRL2 + STMP_OFFSET_REG_CLR);
@@ -316,229 +77,11 @@ static int mxs_dma_ack_irq(int channel)
}
/*
- * Request to reserve a DMA channel
- */
-static int mxs_dma_request(int channel)
-{
- struct mxs_dma_chan *pchan;
-
- if ((channel < 0) || (channel >= MXS_MAX_DMA_CHANNELS))
- return -EINVAL;
-
- pchan = mxs_dma_channels + channel;
- if ((pchan->flags & MXS_DMA_FLAGS_VALID) != MXS_DMA_FLAGS_VALID)
- return -ENODEV;
-
- if (pchan->flags & MXS_DMA_FLAGS_ALLOCATED)
- return -EBUSY;
-
- pchan->flags |= MXS_DMA_FLAGS_ALLOCATED;
- pchan->active_num = 0;
- pchan->pending_num = 0;
-
- INIT_LIST_HEAD(&pchan->active);
- INIT_LIST_HEAD(&pchan->done);
-
- return 0;
-}
-
-/*
- * Release a DMA channel.
- *
- * This function releases a DMA channel from its current owner.
- *
- * The channel will NOT be released if it's marked "busy" (see
- * mxs_dma_enable()).
- */
-static int mxs_dma_release(int channel)
-{
- struct mxs_dma_chan *pchan;
- int ret;
-
- ret = mxs_dma_validate_chan(channel);
- if (ret)
- return ret;
-
- pchan = mxs_dma_channels + channel;
-
- if (pchan->flags & MXS_DMA_FLAGS_BUSY)
- return -EBUSY;
-
- pchan->dev = 0;
- pchan->active_num = 0;
- pchan->pending_num = 0;
- pchan->flags &= ~MXS_DMA_FLAGS_ALLOCATED;
-
- return 0;
-}
-
-/*
- * Allocate DMA descriptor
- */
-struct mxs_dma_desc *mxs_dma_desc_alloc(void)
-{
- struct mxs_dma_desc *pdesc;
- dma_addr_t dma_address;
-
- pdesc = dma_alloc_coherent(sizeof(struct mxs_dma_desc),
- &dma_address);
-
- if (pdesc == NULL)
- return NULL;
-
- pdesc->address = dma_address;
-
- return pdesc;
-};
-
-/*
- * Free DMA descriptor
- */
-void mxs_dma_desc_free(struct mxs_dma_desc *pdesc)
-{
- if (pdesc == NULL)
- return;
-
- free(pdesc);
-}
-
-/*
- * Add a DMA descriptor to a channel.
- *
- * If the descriptor list for this channel is not empty, this function sets the
- * CHAIN bit and the NEXTCMD_ADDR fields in the last descriptor's DMA command so
- * it will chain to the new descriptor's command.
- *
- * Then, this function marks the new descriptor as "ready," adds it to the end
- * of the active descriptor list, and increments the count of pending
- * descriptors.
- *
- * The MXS platform DMA software imposes some rules on DMA commands to maintain
- * important invariants. These rules are NOT checked, but they must be carefully
- * applied by software that uses MXS DMA channels.
- *
- * Invariant:
- * The DMA channel's hardware semaphore must reflect the number of DMA
- * commands the hardware will process, but has not yet finished.
- *
- * Explanation:
- * A DMA channel begins processing commands when its hardware semaphore is
- * written with a value greater than zero, and it stops processing commands
- * when the semaphore returns to zero.
- *
- * When a channel finishes a DMA command, it will decrement its semaphore if
- * the DECREMENT_SEMAPHORE bit is set in that command's flags bits.
- *
- * In principle, it's not necessary for the DECREMENT_SEMAPHORE to be set,
- * unless it suits the purposes of the software. For example, one could
- * construct a series of five DMA commands, with the DECREMENT_SEMAPHORE
- * bit set only in the last one. Then, setting the DMA channel's hardware
- * semaphore to one would cause the entire series of five commands to be
- * processed. However, this example would violate the invariant given above.
- *
- * Rule:
- * ALL DMA commands MUST have the DECREMENT_SEMAPHORE bit set so that the DMA
- * channel's hardware semaphore will be decremented EVERY time a command is
- * processed.
- */
-int mxs_dma_desc_append(int channel, struct mxs_dma_desc *pdesc)
-{
- struct mxs_dma_chan *pchan;
- struct mxs_dma_desc *last;
- int ret;
-
- ret = mxs_dma_validate_chan(channel);
- if (ret)
- return ret;
-
- pchan = mxs_dma_channels + channel;
-
- pdesc->cmd.next = mxs_dma_cmd_address(pdesc);
- pdesc->flags |= MXS_DMA_DESC_FIRST | MXS_DMA_DESC_LAST;
-
- if (!list_empty(&pchan->active)) {
- last = list_entry(pchan->active.prev, struct mxs_dma_desc,
- node);
-
- pdesc->flags &= ~MXS_DMA_DESC_FIRST;
- last->flags &= ~MXS_DMA_DESC_LAST;
-
- last->cmd.next = mxs_dma_cmd_address(pdesc);
- last->cmd.data |= MXS_DMA_DESC_CHAIN;
- }
- pdesc->flags |= MXS_DMA_DESC_READY;
- if (pdesc->flags & MXS_DMA_DESC_FIRST)
- pchan->pending_num++;
- list_add_tail(&pdesc->node, &pchan->active);
-
- return ret;
-}
-
-/*
- * Clean up processed DMA descriptors.
- *
- * This function removes processed DMA descriptors from the "active" list. Pass
- * in a non-NULL list head to get the descriptors moved to your list. Pass NULL
- * to get the descriptors moved to the channel's "done" list. Descriptors on
- * the "done" list can be retrieved with mxs_dma_get_finished().
- *
- * This function marks the DMA channel as "not busy" if no unprocessed
- * descriptors remain on the "active" list.
- */
-static int mxs_dma_finish(int channel, struct list_head *head)
-{
- int sem;
- struct mxs_dma_chan *pchan;
- struct list_head *p, *q;
- struct mxs_dma_desc *pdesc;
- int ret;
-
- ret = mxs_dma_validate_chan(channel);
- if (ret)
- return ret;
-
- pchan = mxs_dma_channels + channel;
-
- sem = mxs_dma_read_semaphore(channel);
- if (sem < 0)
- return sem;
-
- if (sem == pchan->active_num)
- return 0;
-
- list_for_each_safe(p, q, &pchan->active) {
- if ((pchan->active_num) <= sem)
- break;
-
- pdesc = list_entry(p, struct mxs_dma_desc, node);
- pdesc->flags &= ~MXS_DMA_DESC_READY;
-
- if (head)
- list_move_tail(p, head);
- else
- list_move_tail(p, &pchan->done);
-
- if (pdesc->flags & MXS_DMA_DESC_LAST)
- pchan->active_num--;
- }
-
- if (sem == 0)
- pchan->flags &= ~MXS_DMA_FLAGS_BUSY;
-
- return 0;
-}
-
-/*
* Wait for DMA channel to complete
*/
static int mxs_dma_wait_complete(uint32_t timeout, unsigned int chan)
{
struct apbh_dma *apbh = apbh_dma;
- int ret;
-
- ret = mxs_dma_validate_chan(chan);
- if (ret)
- return ret;
while (--timeout) {
if (readl(apbh->regs + HW_APBHX_CTRL1) & (1 << chan))
@@ -546,38 +89,47 @@ static int mxs_dma_wait_complete(uint32_t timeout, unsigned int chan)
udelay(1);
}
- if (timeout == 0) {
- ret = -ETIMEDOUT;
- mxs_dma_reset(chan);
- }
+ if (!timeout)
+ return -ETIMEDOUT;
- return ret;
+ return 0;
}
/*
* Execute the DMA channel
*/
-int mxs_dma_go(int chan)
+int mxs_dma_go(int chan, struct mxs_dma_cmd *cmd, int ncmds)
{
+ struct apbh_dma *apbh = apbh_dma;
uint32_t timeout = 10000;
- int ret;
+ int i, ret, channel_bit;
- LIST_HEAD(tmp_desc_list);
+ ret = mxs_dma_validate_chan(chan);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ncmds - 1; i++) {
+ cmd[i].next = (unsigned long)(&cmd[i + 1]);
+ cmd[i].data |= MXS_DMA_DESC_CHAIN;
+ }
- mxs_dma_enable_irq(chan, 1);
- mxs_dma_enable(chan);
+ if (apbh_dma_is_imx23(apbh)) {
+ writel(cmd, apbh->regs + HW_APBHX_CHn_NXTCMDAR_MX23(chan));
+ writel(1, apbh->regs + HW_APBHX_CHn_SEMA_MX23(chan));
+ channel_bit = chan + BP_APBH_CTRL0_CLKGATE_CHANNEL;
+ } else {
+ writel(cmd, apbh->regs + HW_APBHX_CHn_NXTCMDAR_MX28(chan));
+ writel(1, apbh->regs + HW_APBHX_CHn_SEMA_MX28(chan));
+ channel_bit = chan;
+ }
+ writel(1 << channel_bit, apbh->regs + HW_APBHX_CTRL0 + STMP_OFFSET_REG_CLR);
/* Wait for DMA to finish. */
ret = mxs_dma_wait_complete(timeout, chan);
- /* Clear out the descriptors we just ran. */
- mxs_dma_finish(chan, &tmp_desc_list);
-
/* Shut the DMA channel down. */
mxs_dma_ack_irq(chan);
mxs_dma_reset(chan);
- mxs_dma_enable_irq(chan, 0);
- mxs_dma_disable(chan);
return ret;
}
@@ -589,7 +141,6 @@ static int apbh_dma_probe(struct device_d *dev)
{
struct resource *iores;
struct apbh_dma *apbh;
- struct mxs_dma_chan *pchan;
enum mxs_dma_id id;
int ret, channel;
@@ -627,28 +178,11 @@ static int apbh_dma_probe(struct device_d *dev)
apbh->regs + HW_APBHX_CTRL0 + STMP_OFFSET_REG_SET);
for (channel = 0; channel < MXS_MAX_DMA_CHANNELS; channel++) {
- pchan = mxs_dma_channels + channel;
- pchan->flags = MXS_DMA_FLAGS_VALID;
-
- ret = mxs_dma_request(channel);
-
- if (ret) {
- printf("MXS DMA: Can't acquire DMA channel %i\n",
- channel);
-
- goto err;
- }
-
mxs_dma_reset(channel);
mxs_dma_ack_irq(channel);
}
return 0;
-
-err:
- while (--channel >= 0)
- mxs_dma_release(channel);
- return ret;
}
static struct platform_device_id apbh_ids[] = {
diff --git a/drivers/mtd/nand/nand_mxs.c b/drivers/mtd/nand/nand_mxs.c
index 5faa17a4bd..79a8fbdefa 100644
--- a/drivers/mtd/nand/nand_mxs.c
+++ b/drivers/mtd/nand/nand_mxs.c
@@ -21,6 +21,7 @@
#include <linux/types.h>
#include <linux/clk.h>
#include <linux/err.h>
+#include <linux/bitfield.h>
#include <of_mtd.h>
#include <common.h>
#include <dma.h>
@@ -32,139 +33,10 @@
#include <dma/apbh-dma.h>
#include <stmp-device.h>
#include <mach/generic.h>
+#include <soc/imx/gpmi-nand.h>
#include "internals.h"
-#define MX28_BLOCK_SFTRST (1 << 31)
-#define MX28_BLOCK_CLKGATE (1 << 30)
-
-#define GPMI_CTRL0 0x00000000
-#define GPMI_CTRL0_RUN (1 << 29)
-#define GPMI_CTRL0_DEV_IRQ_EN (1 << 28)
-/* Disable for now since we don't need it and it is different on MX23.
-#define GPMI_CTRL0_LOCK_CS (1 << 27)
-*/
-#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)
-/* Careful: Is 0x3 on MX23
-#define GPMI_CTRL0_CS_MASK (0x7 << 20)
-*/
-#define GPMI_CTRL0_CS_OFFSET 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_CTRL1 0x00000060
-#define GPMI_CTRL1_SET 0x00000064
-#define GPMI_CTRL1_CLR 0x00000068
-#define GPMI_CTRL1_DECOUPLE_CS (1 << 24)
-#define GPMI_CTRL1_WRN_DLY(d) (((d) & 0x3) << 22)
-#define GPMI_CTRL1_TIMEOUT_IRQ_EN (1 << 20)
-#define GPMI_CTRL1_GANGED_RDYBUSY (1 << 19)
-#define GPMI_CTRL1_BCH_MODE (1 << 18)
-#define GPMI_CTRL1_DLL_ENABLE (1 << 17)
-#define GPMI_CTRL1_HALF_PERIOD (1 << 16)
-#define GPMI_CTRL1_RDN_DELAY(d) (((d) & 0xf) << 12)
-#define GPMI_CTRL1_DMA2ECC_MODE (1 << 11)
-#define GPMI_CTRL1_DEV_IRQ (1 << 10)
-#define GPMI_CTRL1_TIMEOUT_IRQ (1 << 9)
-#define GPMI_CTRL1_BURST_EN (1 << 8)
-#define GPMI_CTRL1_ABORT_WAIT_REQUEST (1 << 7)
-#define GPMI_CTRL1_ABORT_WAIT_FOR_READY_CHANNEL_MASK (0x7 << 4)
-#define GPMI_CTRL1_ABORT_WAIT_FOR_READY_CHANNEL_OFFSET 4
-#define GPMI_CTRL1_DEV_RESET (1 << 3)
-#define GPMI_CTRL1_ATA_IRQRDY_POLARITY (1 << 2)
-#define GPMI_CTRL1_CAMERA_MODE (1 << 1)
-#define GPMI_CTRL1_GPMI_MODE (1 << 0)
-
-#define BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS 0x0
-#define BV_GPMI_CTRL1_WRN_DLY_SEL_6_TO_10NS 0x1
-#define BV_GPMI_CTRL1_WRN_DLY_SEL_7_TO_12NS 0x2
-#define BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY 0x3
-
-#define GPMI_TIMING0 0x00000070
-
-#define GPMI_TIMING0_ADDRESS_SETUP(d) (((d) & 0xff) << 16)
-#define GPMI_TIMING0_DATA_HOLD(d) (((d) & 0xff) << 8)
-#define GPMI_TIMING0_DATA_SETUP(d) (((d) & 0xff) << 0)
-
-#define GPMI_TIMING1 0x00000080
-#define GPMI_TIMING1_BUSY_TIMEOUT(d) (((d) & 0xffff) << 16)
-
-#define GPMI_ECCCTRL_HANDLE_MASK (0xffff << 16)
-#define GPMI_ECCCTRL_HANDLE_OFFSET 16
-#define GPMI_ECCCTRL_ECC_CMD_MASK (0x3 << 13)
-#define GPMI_ECCCTRL_ECC_CMD_OFFSET 13
-#define GPMI_ECCCTRL_ECC_CMD_DECODE (0x0 << 13)
-#define GPMI_ECCCTRL_ECC_CMD_ENCODE (0x1 << 13)
-#define GPMI_ECCCTRL_RANDOMIZER_ENABLE (1 << 11)
-#define GPMI_ECCCTRL_RANDOMIZER_TYPE0 0
-#define GPMI_ECCCTRL_RANDOMIZER_TYPE1 (1 << 9)
-#define GPMI_ECCCTRL_RANDOMIZER_TYPE2 (2 << 9)
-#define GPMI_ECCCTRL_ENABLE_ECC (1 << 12)
-#define GPMI_ECCCTRL_BUFFER_MASK_MASK 0x1ff
-#define GPMI_ECCCTRL_BUFFER_MASK_OFFSET 0
-#define GPMI_ECCCTRL_BUFFER_MASK_BCH_AUXONLY 0x100
-#define GPMI_ECCCTRL_BUFFER_MASK_BCH_PAGE 0x1ff
-
-#define GPMI_STAT 0x000000b0
-#define GPMI_STAT_READY_BUSY_OFFSET 24
-
-#define GPMI_DEBUG 0x000000c0
-#define GPMI_DEBUG_READY0_OFFSET 28
-
-#define GPMI_VERSION 0x000000d0
-#define GPMI_VERSION_MINOR_OFFSET 16
-#define GPMI_VERSION_TYPE_MX23 0x0300
-
-#define BCH_CTRL 0x00000000
-#define BCH_CTRL_COMPLETE_IRQ (1 << 0)
-#define BCH_CTRL_COMPLETE_IRQ_EN (1 << 8)
-
-#define BCH_LAYOUTSELECT 0x00000070
-
-#define BCH_FLASH0LAYOUT0 0x00000080
-#define BCH_FLASHLAYOUT0_NBLOCKS_MASK (0xff << 24)
-#define BCH_FLASHLAYOUT0_NBLOCKS_OFFSET 24
-#define BCH_FLASHLAYOUT0_META_SIZE_MASK (0xff << 16)
-#define BCH_FLASHLAYOUT0_META_SIZE_OFFSET 16
-#define BCH_FLASHLAYOUT0_ECC0_MASK (0xf << 12)
-#define BCH_FLASHLAYOUT0_ECC0_OFFSET 12
-#define IMX6_BCH_FLASHLAYOUT0_ECC0_OFFSET 11
-#define BCH_FLASHLAYOUT0_DATA0_SIZE_OFFSET 0
-#define BCH_FLASHLAYOUT0_GF13_0_GF14_1_MASK BIT(10)
-#define BCH_FLASHLAYOUT0_GF13_0_GF14_1_OFFSET 10
-
-#define BCH_FLASH0LAYOUT1 0x00000090
-#define BCH_FLASHLAYOUT1_PAGE_SIZE_MASK (0xffff << 16)
-#define BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET 16
-#define BCH_FLASHLAYOUT1_ECCN_MASK (0xf << 12)
-#define BCH_FLASHLAYOUT1_ECCN_OFFSET 12
-#define IMX6_BCH_FLASHLAYOUT1_ECCN_OFFSET 11
-#define BCH_FLASHLAYOUT1_GF13_0_GF14_1_MASK BIT(10)
-#define BCH_FLASHLAYOUT1_GF13_0_GF14_1_OFFSET 10
-#define BCH_FLASHLAYOUT1_DATAN_SIZE_OFFSET 0
-
-#define MXS_NAND_DMA_DESCRIPTOR_COUNT 4
-
-#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE 512
-#define MXS_NAND_METADATA_SIZE 10
-
-#define MXS_NAND_COMMAND_BUFFER_SIZE 32
-
-#define MXS_NAND_BCH_TIMEOUT 10000
-
enum gpmi_type {
GPMI_MXS,
GPMI_IMX6,
@@ -230,7 +102,7 @@ struct mxs_nand_info {
loff_t to, struct mtd_oob_ops *ops);
/* DMA descriptors */
- struct mxs_dma_desc **desc;
+ struct mxs_dma_cmd *desc;
uint32_t desc_index;
#define GPMI_ASYNC_EDO_ENABLED (1 << 0)
@@ -246,16 +118,16 @@ static inline int mxs_nand_is_imx6(struct mxs_nand_info *info)
return info->type == GPMI_IMX6;
}
-static struct mxs_dma_desc *mxs_nand_get_dma_desc(struct mxs_nand_info *info)
+static struct mxs_dma_cmd *mxs_nand_get_dma_desc(struct mxs_nand_info *info)
{
- struct mxs_dma_desc *desc;
+ struct mxs_dma_cmd *desc;
if (info->desc_index >= MXS_NAND_DMA_DESCRIPTOR_COUNT) {
printf("MXS NAND: Too many DMA descriptors requested\n");
return NULL;
}
- desc = info->desc[info->desc_index];
+ desc = &info->desc[info->desc_index];
info->desc_index++;
return desc;
@@ -264,12 +136,11 @@ static struct mxs_dma_desc *mxs_nand_get_dma_desc(struct mxs_nand_info *info)
static void mxs_nand_return_dma_descs(struct mxs_nand_info *info)
{
int i;
- struct mxs_dma_desc *desc;
+ struct mxs_dma_cmd *desc;
for (i = 0; i < info->desc_index; i++) {
- desc = info->desc[i];
- memset(desc, 0, sizeof(struct mxs_dma_desc));
- desc->address = (dma_addr_t)desc;
+ desc = &info->desc[i];
+ memset(desc, 0, sizeof(struct mxs_dma_cmd));
}
info->desc_index = 0;
@@ -452,7 +323,7 @@ static int mxs_nand_wait_for_bch_complete(struct mxs_nand_info *nand_info)
static void mxs_nand_cmd_ctrl(struct nand_chip *chip, int data, unsigned int ctrl)
{
struct mxs_nand_info *nand_info = chip->priv;
- struct mxs_dma_desc *d;
+ struct mxs_dma_cmd *d;
uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip;
int ret;
@@ -492,26 +363,24 @@ static void mxs_nand_cmd_ctrl(struct nand_chip *chip, int data, unsigned int ctr
/* Compile the DMA descriptor -- a descriptor that sends command. */
d = mxs_nand_get_dma_desc(nand_info);
- d->cmd.data =
+ d->data =
MXS_DMA_DESC_COMMAND_DMA_READ | MXS_DMA_DESC_IRQ |
MXS_DMA_DESC_CHAIN | MXS_DMA_DESC_DEC_SEM |
- MXS_DMA_DESC_WAIT4END | (3 << MXS_DMA_DESC_PIO_WORDS_OFFSET) |
- (nand_info->cmd_queue_len << MXS_DMA_DESC_BYTES_OFFSET);
+ MXS_DMA_DESC_WAIT4END | MXS_DMA_DESC_PIO_WORDS(3) |
+ MXS_DMA_DESC_XFER_COUNT(nand_info->cmd_queue_len);
- d->cmd.address = (dma_addr_t)nand_info->cmd_buf;
+ d->address = (dma_addr_t)nand_info->cmd_buf;
- d->cmd.pio_words[0] =
+ d->pio_words[0] =
GPMI_CTRL0_COMMAND_MODE_WRITE |
GPMI_CTRL0_WORD_LENGTH |
- (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) |
+ FIELD_PREP(GPMI_CTRL0_CS, nand_info->cur_chip) |
GPMI_CTRL0_ADDRESS_NAND_CLE |
GPMI_CTRL0_ADDRESS_INCREMENT |
nand_info->cmd_queue_len;
- mxs_dma_desc_append(channel, d);
-
/* Execute the DMA chain. */
- ret = mxs_dma_go(channel);
+ ret = mxs_dma_go(channel, nand_info->desc, nand_info->desc_index);
if (ret)
printf("MXS NAND: Error sending command (%d)\n", ret);
@@ -607,7 +476,7 @@ static void mxs_nand_swap_block_mark(struct nand_chip *chip,
static void mxs_nand_read_buf(struct nand_chip *chip, uint8_t *buf, int length)
{
struct mxs_nand_info *nand_info = chip->priv;
- struct mxs_dma_desc *d;
+ struct mxs_dma_cmd *d;
uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip;
int ret;
@@ -623,23 +492,21 @@ static void mxs_nand_read_buf(struct nand_chip *chip, uint8_t *buf, int length)
/* Compile the DMA descriptor - a descriptor that reads data. */
d = mxs_nand_get_dma_desc(nand_info);
- d->cmd.data =
+ d->data =
MXS_DMA_DESC_COMMAND_DMA_WRITE | MXS_DMA_DESC_IRQ |
MXS_DMA_DESC_DEC_SEM | MXS_DMA_DESC_WAIT4END |
- (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET) |
- (length << MXS_DMA_DESC_BYTES_OFFSET);
+ MXS_DMA_DESC_PIO_WORDS(1) |
+ MXS_DMA_DESC_XFER_COUNT(length);
- d->cmd.address = (dma_addr_t)nand_info->data_buf;
+ d->address = (dma_addr_t)nand_info->data_buf;
- d->cmd.pio_words[0] =
+ d->pio_words[0] =
GPMI_CTRL0_COMMAND_MODE_READ |
GPMI_CTRL0_WORD_LENGTH |
- (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) |
+ FIELD_PREP(GPMI_CTRL0_CS, nand_info->cur_chip) |
GPMI_CTRL0_ADDRESS_NAND_DATA |
length;
- mxs_dma_desc_append(channel, d);
-
/*
* A DMA descriptor that waits for the command to end and the chip to
* become ready.
@@ -649,23 +516,21 @@ static void mxs_nand_read_buf(struct nand_chip *chip, uint8_t *buf, int length)
* did that and no one has re-thought it yet.
*/
d = mxs_nand_get_dma_desc(nand_info);
- d->cmd.data =
+ d->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 | MXS_DMA_DESC_PIO_WORDS(4);
- d->cmd.address = 0;
+ d->address = 0;
- d->cmd.pio_words[0] =
+ d->pio_words[0] =
GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY |
GPMI_CTRL0_WORD_LENGTH |
- (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) |
+ FIELD_PREP(GPMI_CTRL0_CS, nand_info->cur_chip) |
GPMI_CTRL0_ADDRESS_NAND_DATA;
- mxs_dma_desc_append(channel, d);
-
/* Execute the DMA chain. */
- ret = mxs_dma_go(channel);
+ ret = mxs_dma_go(channel, nand_info->desc, nand_info->desc_index);
if (ret) {
printf("MXS NAND: DMA read error\n");
goto rtn;
@@ -684,7 +549,7 @@ static void mxs_nand_write_buf(struct nand_chip *chip, const uint8_t *buf,
int length)
{
struct mxs_nand_info *nand_info = chip->priv;
- struct mxs_dma_desc *d;
+ struct mxs_dma_cmd *d;
uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip;
int ret;
@@ -702,25 +567,23 @@ static void mxs_nand_write_buf(struct nand_chip *chip, const uint8_t *buf,
/* Compile the DMA descriptor - a descriptor that writes data. */
d = mxs_nand_get_dma_desc(nand_info);
- d->cmd.data =
+ d->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) |
- (length << MXS_DMA_DESC_BYTES_OFFSET);
+ MXS_DMA_DESC_PIO_WORDS(4) |
+ MXS_DMA_DESC_XFER_COUNT(length);
- d->cmd.address = (dma_addr_t)nand_info->data_buf;
+ d->address = (dma_addr_t)nand_info->data_buf;
- d->cmd.pio_words[0] =
+ d->pio_words[0] =
GPMI_CTRL0_COMMAND_MODE_WRITE |
GPMI_CTRL0_WORD_LENGTH |
- (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) |
+ FIELD_PREP(GPMI_CTRL0_CS, nand_info->cur_chip) |
GPMI_CTRL0_ADDRESS_NAND_DATA |
length;
- mxs_dma_desc_append(channel, d);
-
/* Execute the DMA chain. */
- ret = mxs_dma_go(channel);
+ ret = mxs_dma_go(channel, nand_info->desc, nand_info->desc_index);
if (ret)
printf("MXS NAND: DMA write error\n");
@@ -749,16 +612,22 @@ static void mxs_nand_config_bch(struct nand_chip *chip, int readlen)
else
chunk_size = MXS_NAND_CHUNK_DATA_CHUNK_SIZE;
- fl0 = (mxs_nand_ecc_chunk_cnt(readlen) - 1)
- << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET;
- fl0 |= MXS_NAND_METADATA_SIZE << BCH_FLASHLAYOUT0_META_SIZE_OFFSET;
- fl0 |= (chip->ecc.strength >> 1) << IMX6_BCH_FLASHLAYOUT0_ECC0_OFFSET;
- fl0 |= chunk_size;
+ fl0 = FIELD_PREP(BCH_FLASHLAYOUT0_NBLOCKS, mxs_nand_ecc_chunk_cnt(readlen) - 1);
+ fl0 |= FIELD_PREP(BCH_FLASHLAYOUT0_META_SIZE, MXS_NAND_METADATA_SIZE);
+ if (mxs_nand_is_imx6(nand_info))
+ fl0 |= FIELD_PREP(IMX6_BCH_FLASHLAYOUT0_ECC0, chip->ecc.strength >> 1);
+ else
+ fl0 |= FIELD_PREP(BCH_FLASHLAYOUT0_ECC0, chip->ecc.strength >> 1);
+ fl0 |= FIELD_PREP(BCH_FLASHLAYOUT0_DATA0_SIZE, chunk_size);
writel(fl0, bch_regs + BCH_FLASH0LAYOUT0);
- fl1 = readlen << BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET;
- fl1 |= (chip->ecc.strength >> 1) << IMX6_BCH_FLASHLAYOUT1_ECCN_OFFSET;
- fl1 |= chunk_size;
+ fl1 = FIELD_PREP(BCH_FLASHLAYOUT1_PAGE_SIZE, readlen);
+ if (mxs_nand_is_imx6(nand_info))
+ fl1 |= FIELD_PREP(IMX6_BCH_FLASHLAYOUT1_ECCN, chip->ecc.strength >> 1);
+ else
+ fl1 |= FIELD_PREP(BCH_FLASHLAYOUT1_ECCN, chip->ecc.strength >> 1);
+
+ fl1 |= FIELD_PREP(BCH_FLASHLAYOUT1_DATAN_SIZE, chunk_size);
writel(fl1, bch_regs + BCH_FLASH0LAYOUT1);
}
@@ -766,89 +635,81 @@ static int mxs_nand_do_bch_read(struct nand_chip *chip, int channel, int readtot
bool randomizer, int page)
{
struct mxs_nand_info *nand_info = chip->priv;
- struct mxs_dma_desc *d;
+ struct mxs_dma_cmd *d;
int ret;
/* Compile the DMA descriptor - wait for ready. */
d = mxs_nand_get_dma_desc(nand_info);
- d->cmd.data =
+ d->data =
MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN |
MXS_DMA_DESC_NAND_WAIT_4_READY | MXS_DMA_DESC_WAIT4END |
- (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET);
+ MXS_DMA_DESC_PIO_WORDS(1);
- d->cmd.address = 0;
+ d->address = 0;
- d->cmd.pio_words[0] =
+ d->pio_words[0] =
GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY |
GPMI_CTRL0_WORD_LENGTH |
- (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) |
+ FIELD_PREP(GPMI_CTRL0_CS, nand_info->cur_chip) |
GPMI_CTRL0_ADDRESS_NAND_DATA;
- mxs_dma_desc_append(channel, d);
-
/* Compile the DMA descriptor - enable the BCH block and read. */
d = mxs_nand_get_dma_desc(nand_info);
- d->cmd.data =
+ d->data =
MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN |
- MXS_DMA_DESC_WAIT4END | (6 << MXS_DMA_DESC_PIO_WORDS_OFFSET);
+ MXS_DMA_DESC_WAIT4END | MXS_DMA_DESC_PIO_WORDS(6);
- d->cmd.address = 0;
+ d->address = 0;
- d->cmd.pio_words[0] =
+ d->pio_words[0] =
GPMI_CTRL0_COMMAND_MODE_READ |
GPMI_CTRL0_WORD_LENGTH |
- (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) |
+ FIELD_PREP(GPMI_CTRL0_CS, nand_info->cur_chip) |
GPMI_CTRL0_ADDRESS_NAND_DATA |
readtotal;
- d->cmd.pio_words[1] = 0;
- d->cmd.pio_words[2] =
+ d->pio_words[1] = 0;
+ d->pio_words[2] =
GPMI_ECCCTRL_ENABLE_ECC |
GPMI_ECCCTRL_ECC_CMD_DECODE |
GPMI_ECCCTRL_BUFFER_MASK_BCH_PAGE;
- d->cmd.pio_words[3] = readtotal;
- d->cmd.pio_words[4] = (dma_addr_t)nand_info->data_buf;
- d->cmd.pio_words[5] = (dma_addr_t)nand_info->oob_buf;
+ d->pio_words[3] = readtotal;
+ d->pio_words[4] = (dma_addr_t)nand_info->data_buf;
+ d->pio_words[5] = (dma_addr_t)nand_info->oob_buf;
if (randomizer) {
- d->cmd.pio_words[2] |= GPMI_ECCCTRL_RANDOMIZER_ENABLE |
+ d->pio_words[2] |= GPMI_ECCCTRL_RANDOMIZER_ENABLE |
GPMI_ECCCTRL_RANDOMIZER_TYPE2;
- d->cmd.pio_words[3] |= (page % 256) << 16;
+ d->pio_words[3] |= (page % 256) << 16;
}
- mxs_dma_desc_append(channel, d);
-
/* Compile the DMA descriptor - disable the BCH block. */
d = mxs_nand_get_dma_desc(nand_info);
- d->cmd.data =
+ d->data =
MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN |
MXS_DMA_DESC_NAND_WAIT_4_READY | MXS_DMA_DESC_WAIT4END |
- (3 << MXS_DMA_DESC_PIO_WORDS_OFFSET);
+ MXS_DMA_DESC_PIO_WORDS(3);
- d->cmd.address = 0;
+ d->address = 0;
- d->cmd.pio_words[0] =
+ d->pio_words[0] =
GPMI_CTRL0_COMMAND_MODE_WAIT_FOR_READY |
GPMI_CTRL0_WORD_LENGTH |
- (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) |
+ FIELD_PREP(GPMI_CTRL0_CS, nand_info->cur_chip) |
GPMI_CTRL0_ADDRESS_NAND_DATA |
readtotal;
- d->cmd.pio_words[1] = 0;
- d->cmd.pio_words[2] = 0;
-
- mxs_dma_desc_append(channel, d);
+ d->pio_words[1] = 0;
+ d->pio_words[2] = 0;
/* Compile the DMA descriptor - deassert the NAND lock and interrupt. */
d = mxs_nand_get_dma_desc(nand_info);
- d->cmd.data =
+ d->data =
MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_IRQ |
MXS_DMA_DESC_DEC_SEM;
- d->cmd.address = 0;
-
- mxs_dma_desc_append(channel, d);
+ d->address = 0;
/* Execute the DMA chain. */
- ret = mxs_dma_go(channel);
+ ret = mxs_dma_go(channel, nand_info->desc, nand_info->desc_index);
if (ret) {
dev_err(nand_info->dev, "MXS NAND: DMA read error (ecc)\n");
goto out;
@@ -1013,7 +874,7 @@ static int mxs_nand_ecc_write_page(struct nand_chip *chip, const uint8_t *buf,
{
struct mtd_info *mtd = nand_to_mtd(chip);
struct mxs_nand_info *nand_info = chip->priv;
- struct mxs_dma_desc *d;
+ struct mxs_dma_cmd *d;
uint32_t channel = nand_info->dma_channel_base + nand_info->cur_chip;
int ret = 0;
@@ -1027,31 +888,29 @@ static int mxs_nand_ecc_write_page(struct nand_chip *chip, const uint8_t *buf,
/* Compile the DMA descriptor - write data. */
d = mxs_nand_get_dma_desc(nand_info);
- d->cmd.data =
+ d->data =
MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_IRQ |
MXS_DMA_DESC_DEC_SEM | MXS_DMA_DESC_WAIT4END |
- (6 << MXS_DMA_DESC_PIO_WORDS_OFFSET);
+ MXS_DMA_DESC_PIO_WORDS(6);
- d->cmd.address = 0;
+ d->address = 0;
- d->cmd.pio_words[0] =
+ d->pio_words[0] =
GPMI_CTRL0_COMMAND_MODE_WRITE |
GPMI_CTRL0_WORD_LENGTH |
- (nand_info->cur_chip << GPMI_CTRL0_CS_OFFSET) |
+ FIELD_PREP(GPMI_CTRL0_CS, nand_info->cur_chip) |
GPMI_CTRL0_ADDRESS_NAND_DATA;
- d->cmd.pio_words[1] = 0;
- d->cmd.pio_words[2] =
+ d->pio_words[1] = 0;
+ d->pio_words[2] =
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[4] = (dma_addr_t)nand_info->data_buf;
- d->cmd.pio_words[5] = (dma_addr_t)nand_info->oob_buf;
-
- mxs_dma_desc_append(channel, d);
+ d->pio_words[3] = (mtd->writesize + mtd->oobsize);
+ d->pio_words[4] = (dma_addr_t)nand_info->data_buf;
+ d->pio_words[5] = (dma_addr_t)nand_info->oob_buf;
/* Execute the DMA chain. */
- ret = mxs_dma_go(channel);
+ ret = mxs_dma_go(channel, nand_info->desc, nand_info->desc_index);
if (ret) {
printf("MXS NAND: DMA write error\n");
goto rtn;
@@ -1264,38 +1123,6 @@ static int mxs_nand_block_markbad(struct nand_chip *chip , loff_t ofs)
return 0;
}
-#define BCH62_WRITESIZE 1024
-#define BCH62_OOBSIZE 838
-#define BCH62_PAGESIZE (BCH62_WRITESIZE + BCH62_OOBSIZE)
-
-static void mxs_nand_mode_fcb_62bit(struct mxs_nand_info *nand_info)
-{
- void __iomem *bch_regs;
- u32 fl0, fl1;
-
- bch_regs = nand_info->bch_base;
-
- /* 8 ecc_chunks */
- fl0 = 7 << BCH_FLASHLAYOUT0_NBLOCKS_OFFSET;
- /* 32 bytes for metadata */
- fl0 |= 32 << BCH_FLASHLAYOUT0_META_SIZE_OFFSET;
- /* using ECC62 level to be performed */
- fl0 |= 0x1F << IMX6_BCH_FLASHLAYOUT0_ECC0_OFFSET;
- /* 0x20 * 4 bytes of the data0 block */
- fl0 |= 0x20 << BCH_FLASHLAYOUT0_DATA0_SIZE_OFFSET;
- fl0 |= 0 << BCH_FLASHLAYOUT0_GF13_0_GF14_1_OFFSET;
- writel(fl0, bch_regs + BCH_FLASH0LAYOUT0);
-
- /* 1024 for data + 838 for OOB */
- fl1 = BCH62_PAGESIZE << BCH_FLASHLAYOUT1_PAGE_SIZE_OFFSET;
- /* using ECC62 level to be performed */
- fl1 |= 0x1f << IMX6_BCH_FLASHLAYOUT1_ECCN_OFFSET;
- /* 0x20 * 4 bytes of the data0 block */
- fl1 |= 0x20 << BCH_FLASHLAYOUT1_DATAN_SIZE_OFFSET;
- fl1 |= 0 << BCH_FLASHLAYOUT1_GF13_0_GF14_1_OFFSET;
- writel(fl1, bch_regs + BCH_FLASH0LAYOUT1);
-}
-
int mxs_nand_read_fcb_bch62(unsigned int block, void *buf, size_t size)
{
struct nand_chip *chip;
@@ -1317,7 +1144,7 @@ int mxs_nand_read_fcb_bch62(unsigned int block, void *buf, size_t size)
page = block * (mtd->erasesize / mtd->writesize);
- mxs_nand_mode_fcb_62bit(nand_info);
+ mxs_nand_mode_fcb_62bit(nand_info->bch_base);
nand_read_page_op(chip, page, 0, NULL, 0);
@@ -1366,7 +1193,7 @@ int mxs_nand_write_fcb_bch62(unsigned int block, void *buf, size_t size)
struct nand_chip *chip;
struct mtd_info *mtd = mxs_nand_mtd;
struct mxs_nand_info *nand_info;
- struct mxs_dma_desc *d;
+ struct mxs_dma_cmd *d;
uint32_t channel;
int ret = 0;
int page;
@@ -1381,7 +1208,7 @@ int mxs_nand_write_fcb_bch62(unsigned int block, void *buf, size_t size)
nand_info = chip->priv;
channel = nand_info->dma_channel_base;
- mxs_nand_mode_fcb_62bit(nand_info);
+ mxs_nand_mode_fcb_62bit(nand_info->bch_base);
nand_select_target(chip, 0);
@@ -1397,24 +1224,24 @@ int mxs_nand_write_fcb_bch62(unsigned int block, void *buf, size_t size)
/* Compile the DMA descriptor - write data. */
d = mxs_nand_get_dma_desc(nand_info);
- d->cmd.data = MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_IRQ |
+ d->data = MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_IRQ |
MXS_DMA_DESC_DEC_SEM | MXS_DMA_DESC_WAIT4END |
- (6 << MXS_DMA_DESC_PIO_WORDS_OFFSET);
+ MXS_DMA_DESC_PIO_WORDS(6);
- d->cmd.address = 0;
+ d->address = 0;
- d->cmd.pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WRITE |
+ d->pio_words[0] = GPMI_CTRL0_COMMAND_MODE_WRITE |
GPMI_CTRL0_WORD_LENGTH |
GPMI_CTRL0_ADDRESS_NAND_DATA;
- d->cmd.pio_words[1] = 0;
- d->cmd.pio_words[2] = GPMI_ECCCTRL_ENABLE_ECC |
+ d->pio_words[1] = 0;
+ d->pio_words[2] = GPMI_ECCCTRL_ENABLE_ECC |
GPMI_ECCCTRL_ECC_CMD_ENCODE |
GPMI_ECCCTRL_BUFFER_MASK_BCH_PAGE;
- d->cmd.pio_words[3] = BCH62_PAGESIZE;
- d->cmd.pio_words[4] = (dma_addr_t)nand_info->data_buf;
- d->cmd.pio_words[5] = (dma_addr_t)nand_info->oob_buf;
+ d->pio_words[3] = BCH62_PAGESIZE;
+ d->pio_words[4] = (dma_addr_t)nand_info->data_buf;
+ d->pio_words[5] = (dma_addr_t)nand_info->oob_buf;
- d->cmd.pio_words[2] |= GPMI_ECCCTRL_RANDOMIZER_ENABLE |
+ d->pio_words[2] |= GPMI_ECCCTRL_RANDOMIZER_ENABLE |
GPMI_ECCCTRL_RANDOMIZER_TYPE2;
/*
* Write NAND page number needed to be randomized
@@ -1423,12 +1250,10 @@ int mxs_nand_write_fcb_bch62(unsigned int block, void *buf, size_t size)
* 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 % 256) << 16;
-
- mxs_dma_desc_append(channel, d);
+ d->pio_words[3] |= (page % 256) << 16;
/* Execute the DMA chain. */
- ret = mxs_dma_go(channel);
+ ret = mxs_dma_go(channel, nand_info->desc, nand_info->desc_index);
if (ret) {
dev_err(nand_info->dev, "MXS NAND: DMA write error: %d\n", ret);
goto out;
@@ -1540,20 +1365,13 @@ static int mxs_nand_hw_init(struct mxs_nand_info *info)
{
void __iomem *gpmi_regs = info->io_base;
void __iomem *bch_regs = info->bch_base;
- int i = 0, ret;
+ int ret;
u32 val;
- info->desc = malloc(sizeof(struct mxs_dma_desc *) *
- MXS_NAND_DMA_DESCRIPTOR_COUNT);
+ info->desc = dma_alloc_coherent(sizeof(struct mxs_dma_cmd) * MXS_NAND_DMA_DESCRIPTOR_COUNT,
+ DMA_ADDRESS_BROKEN);
if (!info->desc)
- goto err1;
-
- /* 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])
- goto err2;
- }
+ return -ENOMEM;
/* Reset the GPMI block. */
ret = stmp_reset_block(gpmi_regs + GPMI_CTRL0, 0);
@@ -1580,14 +1398,6 @@ static int mxs_nand_hw_init(struct mxs_nand_info *info)
writel(val, gpmi_regs + GPMI_CTRL1);
return 0;
-
-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;
}
static void mxs_nand_probe_dt(struct device_d *dev, struct mxs_nand_info *nand_info)
diff --git a/include/dma/apbh-dma.h b/include/dma/apbh-dma.h
index f10bb6f615..4584b504c2 100644
--- a/include/dma/apbh-dma.h
+++ b/include/dma/apbh-dma.h
@@ -30,6 +30,31 @@
#define MXS_DMA_ALIGNMENT 32
+#define HW_APBHX_CTRL0 0x000
+#define BM_APBH_CTRL0_APB_BURST8_EN BIT(29)
+#define BM_APBH_CTRL0_APB_BURST_EN BIT(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
+
+enum mxs_dma_id {
+ UNKNOWN_DMA_ID,
+ IMX23_DMA,
+ IMX28_DMA,
+};
+
+#define apbh_dma_is_imx23(aphb) ((apbh)->id == IMX23_DMA)
+
/*
* MXS DMA channels
*/
@@ -64,18 +89,16 @@ enum {
#define MXS_DMA_DESC_COMMAND_DMA_WRITE 0x1
#define MXS_DMA_DESC_COMMAND_DMA_READ 0x2
#define MXS_DMA_DESC_COMMAND_DMA_SENSE 0x3
-#define MXS_DMA_DESC_CHAIN (1 << 2)
-#define MXS_DMA_DESC_IRQ (1 << 3)
-#define MXS_DMA_DESC_NAND_LOCK (1 << 4)
-#define MXS_DMA_DESC_NAND_WAIT_4_READY (1 << 5)
-#define MXS_DMA_DESC_DEC_SEM (1 << 6)
-#define MXS_DMA_DESC_WAIT4END (1 << 7)
-#define MXS_DMA_DESC_HALT_ON_TERMINATE (1 << 8)
-#define MXS_DMA_DESC_TERMINATE_FLUSH (1 << 9)
-#define MXS_DMA_DESC_PIO_WORDS_MASK (0xf << 12)
-#define MXS_DMA_DESC_PIO_WORDS_OFFSET 12
-#define MXS_DMA_DESC_BYTES_MASK (0xffff << 16)
-#define MXS_DMA_DESC_BYTES_OFFSET 16
+#define MXS_DMA_DESC_CHAIN BIT(2)
+#define MXS_DMA_DESC_IRQ BIT(3)
+#define MXS_DMA_DESC_NAND_LOCK BIT(4)
+#define MXS_DMA_DESC_NAND_WAIT_4_READY BIT(5)
+#define MXS_DMA_DESC_DEC_SEM BIT(6)
+#define MXS_DMA_DESC_WAIT4END BIT(7)
+#define MXS_DMA_DESC_HALT_ON_TERMINATE BIT(8)
+#define MXS_DMA_DESC_TERMINATE_FLUSH BIT(9)
+#define MXS_DMA_DESC_PIO_WORDS(words) ((words) << 12)
+#define MXS_DMA_DESC_XFER_COUNT(x) ((x) << 16)
struct mxs_dma_cmd {
unsigned long next;
@@ -88,53 +111,7 @@ struct mxs_dma_cmd {
unsigned long pio_words[APBH_DMA_PIO_WORDS];
};
-/*
- * MXS DMA command descriptor.
- *
- * This structure incorporates an MXS DMA hardware command structure, along
- * with metadata.
- */
-#define MXS_DMA_DESC_FIRST (1 << 0)
-#define MXS_DMA_DESC_LAST (1 << 1)
-#define MXS_DMA_DESC_READY (1 << 31)
-
-struct mxs_dma_desc {
- struct mxs_dma_cmd cmd;
- unsigned int flags;
- dma_addr_t address;
- void *buffer;
- struct list_head node;
-};
-
-/**
- * MXS DMA channel
- *
- * This structure represents a single DMA channel. The MXS platform code
- * maintains an array of these structures to represent every DMA channel in the
- * system (see mxs_dma_channels).
- */
-#define MXS_DMA_FLAGS_IDLE 0
-#define MXS_DMA_FLAGS_BUSY (1 << 0)
-#define MXS_DMA_FLAGS_FREE 0
-#define MXS_DMA_FLAGS_ALLOCATED (1 << 16)
-#define MXS_DMA_FLAGS_VALID (1 << 31)
-
-struct mxs_dma_chan {
- const char *name;
- unsigned long dev;
- struct mxs_dma_device *dma;
- unsigned int flags;
- unsigned int active_num;
- unsigned int pending_num;
- struct list_head active;
- struct list_head done;
-};
-
-struct mxs_dma_desc *mxs_dma_desc_alloc(void);
-void mxs_dma_desc_free(struct mxs_dma_desc *);
-int mxs_dma_desc_append(int channel, struct mxs_dma_desc *pdesc);
-
-int mxs_dma_go(int chan);
+int mxs_dma_go(int chan, struct mxs_dma_cmd *cmd, int ncmds);
int mxs_dma_init(void);
#endif /* __DMA_H__ */
diff --git a/include/soc/imx/gpmi-nand.h b/include/soc/imx/gpmi-nand.h
new file mode 100644
index 0000000000..a552513e0d
--- /dev/null
+++ b/include/soc/imx/gpmi-nand.h
@@ -0,0 +1,147 @@
+#ifndef __SOC_IMX_GPMI_NAND_H
+#define __SOC_IMX_GPMI_NAND_H
+
+#include <linux/bitfield.h>
+
+#define GPMI_CTRL0 0x00000000
+#define GPMI_CTRL0_SFTRST BIT(31)
+#define GPMI_CTRL0_RUN BIT(29)
+#define GPMI_CTRL0_DEV_IRQ_EN BIT(28)
+#define GPMI_CTRL0_UDMA BIT(26)
+#define GPMI_CTRL0_COMMAND_MODE GENMASK(25, 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 GENMASK(22, 20)
+#define GPMI_CTRL0_ADDRESS GENMASK(19, 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 BIT(16)
+#define GPMI_CTRL0_XFER_COUNT GENMASK(15, 0)
+
+#define GPMI_CTRL1 0x00000060
+#define GPMI_CTRL1_SET 0x00000064
+#define GPMI_CTRL1_CLR 0x00000068
+#define GPMI_CTRL1_DECOUPLE_CS BIT(24)
+#define GPMI_CTRL1_WRN_DLY(d) (((d) & 0x3) << 22)
+#define GPMI_CTRL1_TIMEOUT_IRQ_EN BIT(20)
+#define GPMI_CTRL1_GANGED_RDYBUSY BIT(19)
+#define GPMI_CTRL1_BCH_MODE BIT(18)
+#define GPMI_CTRL1_DLL_ENABLE BIT(17)
+#define GPMI_CTRL1_HALF_PERIOD BIT(16)
+#define GPMI_CTRL1_RDN_DELAY(d) (((d) & 0xf) << 12)
+#define GPMI_CTRL1_DMA2ECC_MODE BIT(11)
+#define GPMI_CTRL1_DEV_IRQ BIT(10)
+#define GPMI_CTRL1_TIMEOUT_IRQ BIT(9)
+#define GPMI_CTRL1_BURST_EN BIT(8)
+#define GPMI_CTRL1_ABORT_WAIT_REQUEST BIT(7)
+#define GPMI_CTRL1_ABORT_WAIT_FOR_READY GENMASK(6, 4)
+#define GPMI_CTRL1_DEV_RESET BIT(3)
+#define GPMI_CTRL1_ATA_IRQRDY_POLARITY BIT(2)
+#define GPMI_CTRL1_CAMERA_MODE BIT(1)
+#define GPMI_CTRL1_GPMI_MODE BIT(0)
+
+#define BV_GPMI_CTRL1_WRN_DLY_SEL_4_TO_8NS 0x0
+#define BV_GPMI_CTRL1_WRN_DLY_SEL_6_TO_10NS 0x1
+#define BV_GPMI_CTRL1_WRN_DLY_SEL_7_TO_12NS 0x2
+#define BV_GPMI_CTRL1_WRN_DLY_SEL_NO_DELAY 0x3
+
+#define GPMI_TIMING0 0x00000070
+
+#define GPMI_TIMING0_ADDRESS_SETUP(d) (((d) & 0xff) << 16)
+#define GPMI_TIMING0_DATA_HOLD(d) (((d) & 0xff) << 8)
+#define GPMI_TIMING0_DATA_SETUP(d) (((d) & 0xff) << 0)
+
+#define GPMI_TIMING1 0x00000080
+#define GPMI_TIMING1_BUSY_TIMEOUT(d) (((d) & 0xffff) << 16)
+
+#define GPMI_ECCCTRL_HANDLE GENMASK(31, 16)
+#define GPMI_ECCCTRL_ECC_CMD GENMASK(14, 13)
+#define GPMI_ECCCTRL_ECC_CMD_DECODE (0x0 << 13)
+#define GPMI_ECCCTRL_ECC_CMD_ENCODE (0x1 << 13)
+#define GPMI_ECCCTRL_RANDOMIZER_ENABLE BIT(11)
+#define GPMI_ECCCTRL_RANDOMIZER_TYPE0 0
+#define GPMI_ECCCTRL_RANDOMIZER_TYPE1 (1 << 9)
+#define GPMI_ECCCTRL_RANDOMIZER_TYPE2 (2 << 9)
+#define GPMI_ECCCTRL_ENABLE_ECC BIT(12)
+#define GPMI_ECCCTRL_BUFFER_MASK GENMASK(8, 0)
+#define GPMI_ECCCTRL_BUFFER_MASK_BCH_AUXONLY 0x100
+#define GPMI_ECCCTRL_BUFFER_MASK_BCH_PAGE 0x1ff
+
+#define GPMI_STAT 0x000000b0
+#define GPMI_STAT_READY_BUSY_OFFSET 24
+
+#define GPMI_DEBUG 0x000000c0
+#define GPMI_DEBUG_READY0_OFFSET 28
+
+#define GPMI_VERSION 0x000000d0
+#define GPMI_VERSION_MINOR_OFFSET 16
+#define GPMI_VERSION_TYPE_MX23 0x0300
+
+#define BCH_CTRL 0x00000000
+#define BCH_CTRL_COMPLETE_IRQ BIT(0)
+#define BCH_CTRL_COMPLETE_IRQ_EN BIT(8)
+
+#define BCH_LAYOUTSELECT 0x00000070
+
+#define BCH_FLASH0LAYOUT0 0x00000080
+#define BCH_FLASHLAYOUT0_NBLOCKS GENMASK(31, 24)
+#define BCH_FLASHLAYOUT0_META_SIZE GENMASK(23, 16)
+#define BCH_FLASHLAYOUT0_ECC0 GENMASK(15, 12)
+#define IMX6_BCH_FLASHLAYOUT0_ECC0 GENMASK(15, 11)
+#define BCH_FLASHLAYOUT0_DATA0_SIZE GENMASK(9, 0)
+#define BCH_FLASHLAYOUT0_GF13_0_GF14_1 BIT(10)
+
+#define BCH_FLASH0LAYOUT1 0x00000090
+#define BCH_FLASHLAYOUT1_PAGE_SIZE GENMASK(31, 16)
+#define BCH_FLASHLAYOUT1_ECCN GENMASK(15, 12)
+#define IMX6_BCH_FLASHLAYOUT1_ECCN GENMASK(15, 11)
+#define BCH_FLASHLAYOUT1_GF13_0_GF14_1 BIT(10)
+#define BCH_FLASHLAYOUT1_DATAN_SIZE GENMASK(9, 0)
+
+#define MXS_NAND_DMA_DESCRIPTOR_COUNT 4
+
+#define MXS_NAND_CHUNK_DATA_CHUNK_SIZE 512
+#define MXS_NAND_METADATA_SIZE 10
+
+#define MXS_NAND_COMMAND_BUFFER_SIZE 32
+
+#define MXS_NAND_BCH_TIMEOUT 10000
+
+#define BCH62_WRITESIZE 1024
+#define BCH62_OOBSIZE 838
+#define BCH62_PAGESIZE (BCH62_WRITESIZE + BCH62_OOBSIZE)
+
+/*
+ * Some SoCs like the i.MX7 use a special layout in the FCB block.
+ * We can read/write that by adjusting the BCH engine to that layout.
+ * Particularly we have pages consisting of 8 chunks with 128 bytes
+ * of data and 100.75 bytes of ECC data each.
+ */
+static void mxs_nand_mode_fcb_62bit(void __iomem *bch_regs)
+{
+ u32 fl0, fl1;
+
+ /* 8 ecc_chunks */
+ fl0 = FIELD_PREP(BCH_FLASHLAYOUT0_NBLOCKS, 7);
+ /* 32 bytes for metadata */
+ fl0 |= FIELD_PREP(BCH_FLASHLAYOUT0_META_SIZE, 32);
+ /* using ECC62 level to be performed */
+ fl0 |= FIELD_PREP(IMX6_BCH_FLASHLAYOUT0_ECC0, 0x1f);
+ /* 0x20 * 4 bytes of the data0 block */
+ fl0 |= FIELD_PREP(BCH_FLASHLAYOUT0_DATA0_SIZE, 0x20);
+ writel(fl0, bch_regs + BCH_FLASH0LAYOUT0);
+
+ /* 1024 for data + 838 for OOB */
+ fl1 = FIELD_PREP(BCH_FLASHLAYOUT1_PAGE_SIZE, BCH62_PAGESIZE);
+ /* using ECC62 level to be performed */
+ fl1 |= FIELD_PREP(IMX6_BCH_FLASHLAYOUT1_ECCN, 0x1f);
+ /* 0x20 * 4 bytes of the data0 block */
+ fl1 |= FIELD_PREP(BCH_FLASHLAYOUT1_DATAN_SIZE, 0x20);
+ writel(fl1, bch_regs + BCH_FLASH0LAYOUT1);
+}
+
+#endif /* __SOC_IMX_GPMI_NAND_H */