diff options
-rw-r--r-- | arch/arm/mach-imx/include/mach/imx6-regs.h | 2 | ||||
-rw-r--r-- | arch/arm/mach-imx/include/mach/imx7-regs.h | 1 | ||||
-rw-r--r-- | arch/arm/mach-imx/include/mach/xload.h | 1 | ||||
-rw-r--r-- | arch/arm/mach-imx/xload-gpmi-nand.c | 639 | ||||
-rw-r--r-- | arch/arm/mach-mxs/include/mach/generic.h | 3 | ||||
-rw-r--r-- | commands/Kconfig | 9 | ||||
-rw-r--r-- | common/imx-bbu-nand-fcb.c | 231 | ||||
-rw-r--r-- | drivers/dma/apbh_dma.c | 514 | ||||
-rw-r--r-- | drivers/mtd/nand/nand_mxs.c | 412 | ||||
-rw-r--r-- | include/dma/apbh-dma.h | 95 | ||||
-rw-r--r-- | include/soc/imx/gpmi-nand.h | 147 |
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 = ¶ms->info; struct mxs_dma_chan pchan = { .channel = 0, /* MXS: MXS_DMA_CHANNEL_AHB_APBH_GPMI0 */ - .apbh = &apbh, + .apbh = ¶ms->apbh, }; int ret; struct fcb_block *fcb; + void __iomem *bch_regs = info->bch_base; + u32 fl0, fl1; - info.dma_channel = &pchan; + info->dma_channel = &pchan; pr_debug("cmdbuf: 0x%p descs: 0x%p databuf: 0x%p dest: 0x%p\n", - cmdbuf, descs, databuf, dest); + 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 = ¶ms->info; int ret; - void *sdram = (void *)0x10000000; void __noreturn (*bb)(void); - void *cmdbuf, *databuf, *descs; + void *databuf; - cmdbuf = sdram; - descs = sdram + MXS_NAND_COMMAND_BUFFER_SIZE; - databuf = descs + + /* Command buffers */ + info->cmd_buf = params->sdram; + info->desc = params->sdram + MXS_NAND_COMMAND_BUFFER_SIZE; + databuf = info->desc + sizeof(struct mxs_dma_cmd) * MXS_NAND_DMA_DESCRIPTOR_COUNT; bb = (void *)PAGE_ALIGN((unsigned long)databuf + SZ_8K); - /* Apply ERR007117 workaround */ - imx6_errata_007117_enable(); - - ret = imx6_nand_load_image(cmdbuf, descs, databuf, - bb, imx_image_size()); + ret = imx6_nand_load_image(params, databuf, bb, imx_image_size()); if (ret) { pr_err("Loading image failed: %d\n", ret); return ret; @@ -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(¶ms); +} + +int imx7_nand_start_image(void) +{ + static struct imx_nand_params params = { + .info.io_base = IOMEM(MX7_GPMI_BASE), + .info.bch_base = IOMEM(MX7_BCH_BASE), + .apbh.regs = IOMEM(MX7_APBH_BASE), + .apbh.id = IMX28_DMA, + .sdram = (void *)MX7_DDR_BASE_ADDR, + .get_fcb = imx7_get_fcb, + }; + + return imx_nand_start_image(¶ms); +} 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 */ |