]> git.kernelconcepts.de Git - karo-tx-uboot.git/commitdiff
TX28 Release 2011-05-30 REL_U-BOOT_TX28_2011-05-30
authorlothar <lothar>
Tue, 14 Jun 2011 09:08:26 +0000 (09:08 +0000)
committerlothar <lothar>
Tue, 14 Jun 2011 09:08:26 +0000 (09:08 +0000)
README.KARO [new file with mode: 0644]
board/karo/tx28/flash.c [new file with mode: 0644]
drivers/mtd/nand/mxs_gpmi.c [new file with mode: 0644]
drivers/mtd/nand/mxs_gpmi.h [new file with mode: 0644]
include/asm-arm/arch-mx28/mxs_gpmi-bch-regs.h [new file with mode: 0644]
include/asm-arm/arch-mx28/mxs_gpmi-regs.h [new file with mode: 0644]
sb.mk [new file with mode: 0644]
u-boot_ivt.db.in [new file with mode: 0644]

diff --git a/README.KARO b/README.KARO
new file mode 100644 (file)
index 0000000..48b581b
--- /dev/null
@@ -0,0 +1,38 @@
+                                  Building U-Boot for TX28
+                                  ========================
+
+Unpacking the source
+--------------------
+mkdir u-boot
+cd u-boot
+tar -xzf u-boot-src.tgz
+
+
+Compiling U-Boot
+----------------
+cd u-boot-2009.08
+export ARCH=arm
+export CROSS_COMPILE=arm-926ejs-linux-gnueabi-
+make tx28_config
+make u-boot_ivt.sb
+
+
+Flashing U-Boot Image
+---------------------
+Load the U-Boot image with sbloader (either the Windows version or the
+Linux version) and use the builtin 'romupdate' command to program the
+image into the flash.
+
+Put the u-boot_ivt.sb file in the TFTP server data directory (usually
+/tftpboot).
+
+Load the U-Boot image:
+Enter the following commands at the U-Boot prompt
+set autostart no
+set autoload yes
+set bootfile u-boot_ivt.sb
+bootp
+romupdate
+
+Power down the module, make sure the BOOT_MODE jumper (ST3) is removed
+and re-apply power to start from flash.
diff --git a/board/karo/tx28/flash.c b/board/karo/tx28/flash.c
new file mode 100644 (file)
index 0000000..b1c4949
--- /dev/null
@@ -0,0 +1,583 @@
+#include <common.h>
+#include <malloc.h>
+#include <nand.h>
+
+#include <linux/err.h>
+
+#include <asm/io.h>
+#include <asm/sizes.h>
+#include <asm/errno.h>
+#include <asm/arch/mxs_gpmi-regs.h>
+#include <asm/arch/mxs_gpmi-bch-regs.h>
+
+#define FCB_START_BLOCK                0
+#define NUM_FCB_BLOCKS         1
+#define MAX_FCB_BLOCKS         32768
+
+struct mx28_nand_timing {
+       u8 data_setup;
+       u8 data_hold;
+       u8 address_setup;
+       u8 dsample_time;
+       u8 nand_timing_state;
+       u8 tREA;
+       u8 tRLOH;
+       u8 tRHOH;
+};
+
+struct mx28_fcb {
+       u32 checksum;
+       u32 fingerprint;
+       u32 version;
+       struct mx28_nand_timing timing;
+       u32 page_data_size;
+       u32 total_page_size;
+       u32 sectors_per_block;
+       u32 number_of_nands;    /* not used by ROM code */
+       u32 total_internal_die; /* not used by ROM code */
+       u32 cell_type;          /* not used by ROM code */
+       u32 ecc_blockn_type;
+       u32 ecc_block0_size;
+       u32 ecc_blockn_size;
+       u32 ecc_block0_type;
+       u32 metadata_size;
+       u32 ecc_blocks_per_page;
+       u32 rsrvd[6];            /* not used by ROM code */
+       u32 bch_mode;
+       u32 boot_patch;
+       u32 patch_sectors;
+       u32 fw1_start_page;
+       u32 fw2_start_page;
+       u32 fw1_sectors;
+       u32 fw2_sectors;
+       u32 dbbt_search_area;
+       u32 bb_mark_byte;
+       u32 bb_mark_startbit;
+       u32 bb_mark_phys_offset;
+};
+
+struct mx28_dbbt_header {
+       u32 checksum;
+       u32 fingerprint;
+       u32 version;
+       u32 number_bb;
+       u32 number_pages;
+       u8 spare[492];
+};
+
+struct mx28_dbbt {
+       u32 nand_number;
+       u32 number_bb;
+       u32 bb_num[2040 / 4];
+};
+
+#define BF_VAL(v, bf)          (((v) & BM_##bf) >> BP_##bf)
+
+static nand_info_t *mtd = &nand_info[0];
+
+extern void *_start;
+
+#define BIT(v,n)       (((v) >> (n)) & 0x1)
+
+static u8 calculate_parity_13_8(u8 d)
+{
+       u8 p = 0;
+
+       p |= (BIT(d, 6) ^ BIT(d, 5) ^ BIT(d, 3) ^ BIT(d, 2))             << 0;
+       p |= (BIT(d, 7) ^ BIT(d, 5) ^ BIT(d, 4) ^ BIT(d, 2) ^ BIT(d, 1)) << 1;
+       p |= (BIT(d, 7) ^ BIT(d, 6) ^ BIT(d, 5) ^ BIT(d, 1) ^ BIT(d, 0)) << 2;
+       p |= (BIT(d, 7) ^ BIT(d, 4) ^ BIT(d, 3) ^ BIT(d, 0))             << 3;
+       p |= (BIT(d, 6) ^ BIT(d, 4) ^ BIT(d, 3) ^ BIT(d, 2) ^ BIT(d, 1) ^ BIT(d, 0)) << 4;
+       return p;
+}
+
+static void encode_hamming_13_8(void *_src, void *_ecc, size_t size)
+{
+       int i;
+       u8 *src = _src;
+       u8 *ecc = _ecc;
+
+       for (i = 0; i < size; i++)
+               ecc[i] = calculate_parity_13_8(src[i]);
+}
+
+static u32 calc_chksum(void *buf, size_t size)
+{
+       u32 chksum;
+       u8 *bp = buf;
+       size_t i;
+
+       for (i = 0; i < size; i++) {
+               chksum += bp[i];
+       }
+       return ~chksum;
+}
+
+/*
+  Physical organisation of data in NAND flash:
+  metadata
+  payload chunk 0 (may be empty)
+  ecc for metadata + payload chunk 0
+  payload chunk 1
+  ecc for payload chunk 1
+...
+  payload chunk n
+  ecc for payload chunk n
+ */
+
+static int calc_bb_offset(nand_info_t *mtd, struct mx28_fcb *fcb)
+{
+       int bb_mark_offset;
+       int chunk_data_size = fcb->ecc_blockn_size * 8;
+       int chunk_ecc_size = (fcb->ecc_blockn_type << 1) * 13;
+       int chunk_total_size = chunk_data_size + chunk_ecc_size;
+       int bb_mark_chunk, bb_mark_chunk_offs;
+
+       bb_mark_offset = (mtd->writesize - fcb->metadata_size) * 8;
+       if (fcb->ecc_block0_size == 0)
+               bb_mark_offset -= (fcb->ecc_block0_type << 1) * 13;
+
+       bb_mark_chunk = bb_mark_offset / chunk_total_size;
+       bb_mark_chunk_offs = bb_mark_offset - (bb_mark_chunk * chunk_total_size);
+       if (bb_mark_chunk_offs > chunk_data_size) {
+               printf("Unsupported ECC layout; BB mark resides in ECC data: %u\n",
+                       bb_mark_chunk_offs);
+               return -EINVAL;
+       }
+       bb_mark_offset -= bb_mark_chunk * chunk_ecc_size;
+       return bb_mark_offset;
+}
+
+static struct mx28_fcb *create_fcb(void *buf, int fw1_start_block, int fw2_start_block,
+                               size_t fw_size)
+{
+       volatile void *gpmi_base = __ioremap(GPMI_BASE_ADDR, SZ_4K, 1);
+       volatile void *bch_base = __ioremap(BCH_BASE_ADDR, SZ_4K, 1);
+       u32 fl0, fl1;
+       u32 t0, t1;
+       int metadata_size;
+       int bb_mark_bit_offs;
+       struct mx28_fcb *fcb;
+       int fcb_offs;
+
+       if (gpmi_base == NULL || bch_base == NULL) {
+               return ERR_PTR(-ENOMEM);
+       }
+
+       fl0 = readl(bch_base + HW_BCH_FLASH0LAYOUT0);
+       fl1 = readl(bch_base + HW_BCH_FLASH0LAYOUT1);
+       t0 = readl(gpmi_base + HW_GPMI_TIMING0);
+       t1 = readl(gpmi_base + HW_GPMI_TIMING1);
+
+       metadata_size = BF_VAL(fl0, BCH_FLASH0LAYOUT0_META_SIZE);
+
+       fcb = buf + ALIGN(metadata_size, 4);
+       fcb_offs = (void *)fcb - buf;
+
+       memset(buf, 0xff, fcb_offs);
+       memset(fcb, 0x00, sizeof(*fcb));
+       memset(fcb + 1, 0xff, mtd->erasesize - fcb_offs - sizeof(*fcb));
+
+       strncpy((char *)&fcb->fingerprint, "FCB ", 4);
+       fcb->version = cpu_to_be32(1);
+
+       fcb->timing.data_setup = BF_VAL(t0, GPMI_TIMING0_DATA_SETUP);
+       fcb->timing.data_hold = BF_VAL(t0, GPMI_TIMING0_DATA_HOLD);
+       fcb->timing.address_setup = BF_VAL(t0, GPMI_TIMING0_ADDRESS_SETUP);
+
+       fcb->page_data_size = mtd->writesize;
+       fcb->total_page_size = mtd->writesize + mtd->oobsize;
+       fcb->sectors_per_block = mtd->erasesize / mtd->writesize;
+
+       fcb->ecc_block0_type = BF_VAL(fl0, BCH_FLASH0LAYOUT0_ECC0);
+       fcb->ecc_block0_size = BF_VAL(fl0, BCH_FLASH0LAYOUT0_DATA0_SIZE);
+       fcb->ecc_blockn_type = BF_VAL(fl1, BCH_FLASH0LAYOUT1_ECCN);
+       fcb->ecc_blockn_size = BF_VAL(fl1, BCH_FLASH0LAYOUT1_DATAN_SIZE);
+
+       fcb->metadata_size = BF_VAL(fl0, BCH_FLASH0LAYOUT0_META_SIZE);
+       fcb->ecc_blocks_per_page = BF_VAL(fl0, BCH_FLASH0LAYOUT0_NBLOCKS);
+       fcb->bch_mode = readl(bch_base + HW_BCH_MODE);
+/*
+       fcb->boot_patch = 0;
+       fcb->patch_sectors = 0;
+*/
+       fcb->fw1_start_page = fw1_start_block * mtd->erasesize / mtd->writesize;
+       fcb->fw1_sectors = DIV_ROUND_UP(fw_size, mtd->writesize);
+
+       if (fw2_start_block != 0 && fw2_start_block < mtd->size / mtd->erasesize) {
+               fcb->fw2_start_page = fw2_start_block * mtd->erasesize / mtd->writesize;
+               fcb->fw2_sectors = fcb->fw1_sectors;
+       }
+
+       fcb->dbbt_search_area = 1;
+
+       bb_mark_bit_offs = calc_bb_offset(mtd, fcb);
+       if (bb_mark_bit_offs < 0)
+               return ERR_PTR(bb_mark_bit_offs);
+       fcb->bb_mark_byte = bb_mark_bit_offs / 8;
+       fcb->bb_mark_startbit = bb_mark_bit_offs % 8;
+       fcb->bb_mark_phys_offset = mtd->writesize;
+
+       fcb->checksum = calc_chksum(&fcb->fingerprint, 512 - 4);
+       return fcb;
+}
+
+static int find_fcb(void *ref, int page)
+{
+       int ret = 0;
+       struct nand_chip *chip = mtd->priv;
+       void *buf = malloc(mtd->erasesize);
+
+       if (buf == NULL) {
+               return -ENOMEM;
+       }
+       chip->select_chip(mtd, 0);
+       chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
+       ret = chip->ecc.read_page_raw(mtd, chip, buf);
+       if (ret) {
+               printf("Failed to read FCB from page %u: %d\n", page, ret);
+               return ret;
+       }
+       chip->select_chip(mtd, -1);
+       if (memcmp(buf, ref, mtd->writesize) == 0) {
+               printf("%s: Found FCB in page %u (%08x)\n", __func__,
+                       page, page * mtd->writesize);
+               ret = 1;
+       }
+       free(buf);
+       return ret;
+}
+
+static int write_fcb(void *buf, int block)
+{
+       int ret;
+       struct nand_chip *chip = mtd->priv;
+       int page = block * mtd->erasesize / mtd->writesize;
+
+       ret = find_fcb(buf, page);
+       if (ret > 0) {
+               printf("FCB at block %d is up to date\n", block);
+               return 0;
+       }
+
+       ret = nand_erase(mtd, block * mtd->erasesize, mtd->erasesize);
+       if (ret) {
+               printf("Failed to erase FCB block %u\n", block);
+               return ret;
+       }
+
+       printf("Writing FCB to block %d @ %08x\n", block,
+               block * mtd->erasesize);
+       chip->select_chip(mtd, 0);
+       ret = chip->write_page(mtd, chip, buf, page, 0, 1);
+       if (ret) {
+               printf("Failed to write FCB to block %u: %d\n", block, ret);
+       }
+       chip->select_chip(mtd, -1);
+       return ret;
+}
+
+#define chk_overlap(a,b)                               \
+       ((a##_start_block <= b##_end_block &&           \
+               a##_end_block >= b##_start_block) ||    \
+       (b##_start_block <= a##_end_block &&            \
+               b##_end_block >= a##_start_block))
+
+#define fail_if_overlap(a,b,m1,m2) do {                                \
+       if (chk_overlap(a, b)) {                                \
+               printf("%s blocks %lu..%lu overlap %s in blocks %lu..%lu!\n", \
+                       m1, a##_start_block, a##_end_block,     \
+                       m2, b##_start_block, b##_end_block);    \
+               return -EINVAL;                                 \
+       }                                                       \
+} while (0)
+
+int do_update(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
+{
+       int ret;
+       int block;
+       int erase_size = mtd->erasesize;
+       int page_size = mtd->writesize;
+       void *buf;
+       char *load_addr;
+       char *file_size;
+       size_t size = 0;
+       void *addr = NULL;
+       struct mx28_fcb *fcb;
+       unsigned long fcb_start_block = FCB_START_BLOCK;
+       unsigned long num_fcb_blocks = NUM_FCB_BLOCKS;
+       unsigned long fcb_end_block;
+       unsigned long mtd_num_blocks = mtd->size / mtd->erasesize;
+       unsigned long env_start_block = CONFIG_ENV_OFFSET / mtd->erasesize;
+       unsigned long env_end_block = env_start_block +
+               DIV_ROUND_UP(CONFIG_ENV_SIZE, mtd->erasesize) - 1;
+       int optind;
+       int fw1_set = 0;
+       int fw2_set = 0;
+       unsigned long fw1_start_block = 0, fw1_end_block;
+       unsigned long fw2_start_block = 0, fw2_end_block;
+       unsigned long fw_num_blocks;
+       unsigned long extra_blocks = 2;
+       nand_erase_options_t erase_opts = { 0, };
+       int fcb_written = 0;
+
+       load_addr = getenv("fileaddr");
+       file_size = getenv("filesize");
+
+       if (argc < 2 && load_addr == NULL) {
+               printf("Load address not specified\n");
+               return -EINVAL;
+       }
+       if (argc < 3 && file_size == NULL) {
+               printf("Image size not specified\n");
+               return -EINVAL;
+       }
+
+       for (optind = 1; optind < argc; optind++) {
+               if (strcmp(argv[optind], "-b") == 0) {
+                       if (optind >= argc - 1) {
+                               printf("Option %s requires an argument\n", argv[optind]);
+                               return -EINVAL;
+                       }
+                       optind++;
+                       fcb_start_block = simple_strtoul(argv[optind], NULL, 0);
+                       if (fcb_start_block >= mtd_num_blocks) {
+                               printf("Block number %lu is out of range: 0..%lu\n",
+                                       fcb_start_block, mtd_num_blocks - 1);
+                               return -EINVAL;
+                       }
+               } else if (strcmp(argv[optind], "-n") == 0) {
+                       if (optind >= argc - 1) {
+                               printf("Option %s requires an argument\n", argv[optind]);
+                               return -EINVAL;
+                       }
+                       optind++;
+                       num_fcb_blocks = simple_strtoul(argv[optind], NULL, 0);
+                       if (num_fcb_blocks > MAX_FCB_BLOCKS) {
+                               printf("Extraneous number of FCB blocks; max. allowed: %u\n",
+                                       MAX_FCB_BLOCKS);
+                               return -EINVAL;
+                       }
+               } else if (strcmp(argv[optind], "-f") == 0) {
+                       if (optind >= argc - 1) {
+                               printf("Option %s requires an argument\n", argv[optind]);
+                               return -EINVAL;
+                       }
+                       optind++;
+                       fw1_start_block = simple_strtoul(argv[optind], NULL, 0);
+                       if (fw1_start_block >= mtd_num_blocks) {
+                               printf("Block number %lu is out of range: 0..%lu\n",
+                                       fw1_start_block,
+                                       mtd_num_blocks - 1);
+                               return -EINVAL;
+                       }
+                       fw1_set = 1;
+               } else if (strcmp(argv[optind], "-r") == 0) {
+                       if (optind < argc - 1 && argv[optind + 1][0] != '-') {
+                               optind++;
+                               fw2_start_block = simple_strtoul(argv[optind], NULL, 0);
+                               if (fw2_start_block >= mtd_num_blocks) {
+                                       printf("Block number %lu is out of range: 0..%lu\n",
+                                               fw2_start_block,
+                                               mtd_num_blocks - 1);
+                                       return -EINVAL;
+                               }
+                       }
+                       fw2_set = 1;
+               } else if (strcmp(argv[optind], "-e") == 0) {
+                       if (optind >= argc - 1) {
+                               printf("Option %s requires an argument\n", argv[optind]);
+                               return -EINVAL;
+                       }
+                       optind++;
+                       extra_blocks = simple_strtoul(argv[optind], NULL, 0);
+                       if (extra_blocks >= mtd_num_blocks) {
+                               printf("Extra block count %lu is out of range: 0..%lu\n",
+                                       extra_blocks,
+                                       mtd_num_blocks - 1);
+                               return -EINVAL;
+                       }
+               } else if (argv[optind][0] == '-') {
+                       printf("Unrecognized option %s\n", argv[optind]);
+                       return -EINVAL;
+               }
+       }
+       if (argc > optind) {
+               load_addr = NULL;
+               addr = (void *)simple_strtoul(argv[optind], NULL, 0);
+               optind++;
+       }
+       if (argc > optind) {
+               file_size = NULL;
+               size = simple_strtoul(argv[optind], NULL, 0);
+               optind++;
+       }
+       if (load_addr != NULL) {
+               addr = (void *)simple_strtoul(load_addr, NULL, 16);
+               printf("Using default load address %p\n", addr);
+       }
+       if (file_size != NULL) {
+               size = simple_strtoul(file_size, NULL, 16);
+               printf("Using default file size %08x\n", size);
+       }
+       fcb_end_block = fcb_start_block + num_fcb_blocks - 1;
+       fw_num_blocks = DIV_ROUND_UP(size, mtd->erasesize);
+
+       if (!fw1_set) {
+               fw1_start_block = fcb_end_block + 1;
+               fw1_end_block = fw1_start_block + fw_num_blocks + extra_blocks - 1;
+               if (chk_overlap(fw1, env)) {
+                       fw1_start_block = env_end_block + 1;
+                       fw1_end_block = fw1_start_block + fw_num_blocks + extra_blocks - 1;
+               }
+       } else {
+               fw1_end_block = fw1_start_block + fw_num_blocks + extra_blocks - 1;
+       }
+
+       if (fw2_set && fw2_start_block == 0) {
+               fw2_start_block = fw1_end_block + 1;
+               fw2_end_block = fw2_start_block + fw_num_blocks + extra_blocks - 1;
+               if (chk_overlap(fw2, env)) {
+                       fw2_start_block = env_end_block + 1;
+                       fw2_end_block = fw2_start_block + fw_num_blocks + extra_blocks - 1;
+               }
+       } else {
+               fw2_end_block = fw2_start_block + fw_num_blocks + extra_blocks - 1;
+       }
+
+       fail_if_overlap(fcb, env, "FCB", "Environment");
+       fail_if_overlap(fcb, fw1, "FCB", "FW1");
+       fail_if_overlap(fw1, env, "FW1", "Environment");
+       if (fw2_set) {
+               fail_if_overlap(fcb, fw2, "FCB", "FW2");
+               fail_if_overlap(fw2, env, "FW2", "Environment");
+               fail_if_overlap(fw1, fw2, "FW1", "FW2");
+       }
+
+       buf = malloc(erase_size);
+       if (buf == NULL) {
+               printf("Failed to allocate buffer\n");
+               return -ENOMEM;
+       }
+       /* search for first non-bad block in FW1 block range */
+       while (fw1_start_block <= fw1_end_block) {
+               if (!nand_block_isbad(mtd, fw1_start_block * mtd->erasesize))
+                       break;
+               fw1_start_block++;
+       }
+       if (fw1_end_block - fw1_start_block + 1 < fw_num_blocks) {
+               printf("Too many bad blocks in FW1 block range: %lu..%lu\n",
+                       fw1_end_block + 1 - fw_num_blocks - extra_blocks,
+                       fw1_end_block);
+               return -EINVAL;
+       }
+
+       /* search for first non-bad block in FW2 block range */
+       while (fw2_set && fw2_start_block <= fw2_end_block) {
+               if (!nand_block_isbad(mtd, fw2_start_block * mtd->erasesize))
+                       break;
+               fw2_start_block++;
+       }
+       if (fw2_end_block - fw2_start_block + 1 < fw_num_blocks) {
+               printf("Too many bad blocks in FW2 area %08lx..%08lx\n",
+                       fw2_end_block + 1 - fw_num_blocks - extra_blocks,
+                       fw2_end_block);
+               return -EINVAL;
+       }
+
+       fcb = create_fcb(buf, fw1_start_block, fw2_start_block,
+                       (fw_num_blocks + extra_blocks) * mtd->erasesize);
+       if (IS_ERR(fcb)) {
+               printf("Failed to initialize FCB: %ld\n", PTR_ERR(fcb));
+               return PTR_ERR(fcb);
+       }
+       encode_hamming_13_8(fcb, (void *)fcb + 512, 512);
+
+       for (block = fcb_start_block; block < fcb_start_block + num_fcb_blocks;
+            block++) {
+               if (nand_block_isbad(mtd, block * mtd->erasesize)) {
+                       if (block == fcb_start_block)
+                               fcb_start_block++;
+                       continue;
+               }
+               ret = write_fcb(buf, block);
+               if (ret) {
+                       printf("Failed to write FCB to block %u\n", block);
+                       return ret;
+               }
+               fcb_written = 1;
+       }
+
+       if (!fcb_written) {
+               printf("Could not write FCB to flash\n");
+               return -EIO;
+       }
+
+       printf("Programming U-Boot image from %p to block %lu\n",
+               addr, fw1_start_block);
+       if (size & (page_size - 1)) {
+               memset(addr + size, 0xff, size & (page_size - 1));
+               size = ALIGN(size, page_size);
+       }
+
+       erase_opts.offset = fcb->fw1_start_page * page_size;
+       erase_opts.length = ALIGN(size, erase_size) +
+               extra_blocks * mtd->erasesize;
+       erase_opts.quiet = 1;
+
+       printf("Erasing flash @ %08lx..%08lx\n", erase_opts.offset,
+               erase_opts.offset + erase_opts.length - 1);
+
+       ret = nand_erase_opts(mtd, &erase_opts);
+       if (ret) {
+               printf("Failed to erase flash: %d\n", ret);
+               return ret;
+       }
+       printf("Programming flash @ %08x..%08x from %p\n",
+               fcb->fw1_start_page * page_size,
+               fcb->fw1_start_page * page_size + size, addr);
+       ret = nand_write_skip_bad(mtd, fcb->fw1_start_page * page_size,
+                               &size, addr);
+       if (ret) {
+               printf("Failed to program flash: %d\n", ret);
+               return ret;
+       }
+       if (fw2_start_block == 0) {
+               return ret;
+       }
+
+       printf("Programming redundant U-Boot image to block %lu\n",
+               fw2_start_block);
+       erase_opts.offset = fcb->fw2_start_page * page_size;
+       printf("Erasing flash @ %08lx..%08lx\n", erase_opts.offset,
+               erase_opts.offset + erase_opts.length - 1);
+
+       ret = nand_erase_opts(mtd, &erase_opts);
+       if (ret) {
+               printf("Failed to erase flash: %d\n", ret);
+               return ret;
+       }
+       printf("Programming flash @ %08x..%08x from %p\n",
+               fcb->fw2_start_page * page_size,
+               fcb->fw2_start_page * page_size + size, addr);
+       ret = nand_write_skip_bad(mtd, fcb->fw2_start_page * page_size,
+                               &size, addr);
+       if (ret) {
+               printf("Failed to program flash: %d\n", ret);
+               return ret;
+       }
+       return ret;
+}
+
+U_BOOT_CMD(romupdate, 11, 0, do_update,
+       "Creates an FCB data structure and writes an U-Boot image to flash\n",
+       "[-b #] [-n #] [-f #] [-r [#]] [<address>] [<length>]\n"
+       "\t-b #\tfirst FCB block number (default 0)\n"
+       "\t-n #\ttotal number of FCB blocks (default 1)\n"
+       "\t-f #\twrite bootloader image at block #\n"
+       "\t-r\twrite redundant bootloader image at next free block after first image\n"
+       "\t-r #\twrite redundant bootloader image at block #\n"
+       "\t-e #\tspecify number of redundant blocks per boot loader image\n"
+       "\t<address>\tRAM address of bootloader image (default: ${fileaddr}\n"
+       "\t<length>\tlength of bootloader image in RAM (default: ${filesize}"
+       );
diff --git a/drivers/mtd/nand/mxs_gpmi.c b/drivers/mtd/nand/mxs_gpmi.c
new file mode 100644 (file)
index 0000000..e5f803a
--- /dev/null
@@ -0,0 +1,1613 @@
+/*
+ * Freescale GPMI NFC NAND Flash Driver
+ *
+ * Copyright (C) 2010 Freescale Semiconductor, Inc.
+ * Copyright (C) 2008 Embedded Alley Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <watchdog.h>
+#include <linux/err.h>
+#include <linux/mtd/compat.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+
+#ifdef CONFIG_MTD_PARTITIONS
+#include <linux/mtd/partitions.h>
+#endif
+
+#include <asm/sizes.h>
+#include <asm/io.h>
+#include <asm/errno.h>
+#include <asm/dma-mapping.h>
+#include <asm/arch/mx28.h>
+
+#ifdef CONFIG_JFFS2_NAND
+#include <jffs2/jffs2.h>
+#endif
+
+#include <asm/arch/regs-clkctrl.h>
+#include <asm/arch/mxs_gpmi-regs.h>
+#include <asm/arch/mxs_gpmi-bch-regs.h>
+
+#ifdef _DEBUG
+static int debug = 1;
+#define dbg_lvl(l)             (debug > (l))
+#define DBG(l, fmt...)         do { if (dbg_lvl(l)) printk(KERN_DEBUG fmt); } while (0)
+#else
+#define dbg_lvl(n)             0
+#define DBG(l, fmt...)         do { } while (0)
+#endif
+
+#define dma_timeout            1000
+#define create_bbt             1
+
+#define MAX_CHIP_COUNT         CONFIG_SYS_MAX_NAND_DEVICE
+#define COMMAND_BUFFER_SIZE    10
+#define MAX_PIO_WORDS          16
+
+/* dmaengine interface */
+enum dma_status {
+       DMA_SUCCESS,
+       DMA_IN_PROGRESS,
+       DMA_PAUSED,
+       DMA_ERROR,
+};
+
+/* MXS APBH DMA controller interface */
+#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 HW_APBHX_CTRL2                         0x020
+#define HW_APBHX_CHANNEL_CTRL                  0x030
+#define BP_APBHX_CHANNEL_CTRL_RESET_CHANNEL    16
+#define HW_APBH_VERSION                                (cpu_is_mx23() ? 0x3f0 : 0x800)
+#define HW_APBX_VERSION                                0x800
+#define BP_APBHX_VERSION_MAJOR                 24
+#define HW_APBHX_CHn_NXTCMDAR(n)               (0x110 + (n) * 0x70)
+#define HW_APBHX_CHn_SEMA(n)                   (0x140 + (n) * 0x70)
+
+/*
+ * ccw bits definitions
+ *
+ * COMMAND:            0..1    (2)
+ * CHAIN:              2       (1)
+ * IRQ:                        3       (1)
+ * NAND_LOCK:          4       (1) - not implemented
+ * NAND_WAIT4READY:    5       (1) - not implemented
+ * DEC_SEM:            6       (1)
+ * WAIT4END:           7       (1)
+ * HALT_ON_TERMINATE:  8       (1)
+ * TERMINATE_FLUSH:    9       (1)
+ * RESERVED:           10..11  (2)
+ * PIO_NUM:            12..15  (4)
+ */
+#define BP_CCW_COMMAND         0
+#define BM_CCW_COMMAND         (3 << 0)
+#define CCW_CHAIN              (1 << 2)
+#define CCW_IRQ                        (1 << 3)
+#define CCW_DEC_SEM            (1 << 6)
+#define CCW_WAIT4END           (1 << 7)
+#define CCW_HALT_ON_TERM       (1 << 8)
+#define CCW_TERM_FLUSH         (1 << 9)
+#define BP_CCW_PIO_NUM         12
+#define BM_CCW_PIO_NUM         (0xf << 12)
+
+#define BF_CCW(value, field)   (((value) << BP_CCW_##field) & BM_CCW_##field)
+
+#define MXS_DMA_CMD_NO_XFER    0
+#define MXS_DMA_CMD_WRITE      1
+#define MXS_DMA_CMD_READ       2
+#define MXS_DMA_CMD_DMA_SENSE  3
+
+struct mxs_dma_ccw {
+       u32             next;
+       u16             bits;
+       u16             xfer_bytes;
+#define MAX_XFER_BYTES 0xff00
+       u32             bufaddr;
+#define MXS_PIO_WORDS  16
+       u32             pio_words[MXS_PIO_WORDS];
+};
+
+#define NUM_CCW        (int)(PAGE_SIZE / sizeof(struct mxs_dma_ccw))
+
+struct mxs_dma_chan {
+       struct mxs_dma_engine           *mxs_dma;
+       int                             chan_id;
+       struct mxs_dma_ccw              *ccw;
+       int                             ccw_idx;
+       unsigned long                   ccw_phys;
+       enum dma_status                 status;
+#define MXS_DMA_SG_LOOP                        (1 << 0)
+};
+
+#define MXS_DMA_CHANNELS               16
+#define MXS_DMA_CHANNELS_MASK          0xffff
+
+struct mxs_dma_engine {
+       int                             dev_id;
+       void __iomem                    *base;
+       struct mxs_dma_chan             mxs_chans[MAX_CHIP_COUNT];
+};
+
+struct mxs_gpmi {
+       struct mxs_dma_engine mxs_dma;
+       void __iomem *gpmi_regs;
+       void __iomem *bch_regs;
+       void __iomem *dma_regs;
+
+       unsigned int chip_count;
+
+       struct mtd_partition *parts;
+       unsigned int nr_parts;
+       struct mtd_info *mtd;
+       struct nand_chip *chip;
+       struct nand_ecclayout ecc_layout;
+
+       int current_chip;
+
+       void *page_buf;
+       void *oob_buf;
+       void *data_buf;
+
+       int command_length;
+       u32 pio_data[MAX_PIO_WORDS];
+       u8 cmd_buf[COMMAND_BUFFER_SIZE];
+
+       unsigned block0_ecc_strength:5,
+               blockn_ecc_strength:5,
+               swap_block_mark:1,
+               block_mark_bit_offset:3,
+               block_mark_byte_offset:14;
+};
+
+static uint8_t scan_ff_pattern[] = { 0xff };
+static struct nand_bbt_descr gpmi_bbt_descr = {
+       .options        = 0,
+       .offs           = 0,
+       .len            = 1,
+       .pattern        = scan_ff_pattern
+};
+
+static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
+static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr mxs_gpmi_bbt_main_descr = {
+       .options = NAND_BBT_LASTBLOCK | NAND_BBT_2BIT |
+               NAND_BBT_VERSION | NAND_BBT_PERCHIP |
+               NAND_BBT_NO_OOB,
+       .offs = 0,
+       .len = 4,
+       .veroffs = 4,
+       .maxblocks = 4,
+       .pattern = bbt_pattern,
+};
+
+static struct nand_bbt_descr mxs_gpmi_bbt_mirror_descr = {
+       .options = NAND_BBT_LASTBLOCK | NAND_BBT_2BIT |
+               NAND_BBT_VERSION | NAND_BBT_PERCHIP |
+               NAND_BBT_NO_OOB,
+       .offs = 0,
+       .len = 4,
+       .veroffs = 4,
+       .maxblocks = 4,
+       .pattern = mirror_pattern,
+};
+
+/* MXS DMA implementation */
+#ifdef _DEBUG
+static inline u32 __mxs_dma_readl(struct mxs_dma_engine *mxs_dma,
+                                       unsigned int reg,
+                                       const char *name, const char *fn, int ln)
+{
+       u32 val;
+       void __iomem *addr = mxs_dma->base + reg;
+
+       val = readl(addr);
+       DBG(3, "%s@%d: Read %08x from %s[%08x]\n", fn, ln, val, name,
+               APBHDMA_BASE_ADDR + reg);
+       return val;
+}
+#define mxs_dma_readl(t, r)            __mxs_dma_readl(t, r, #r, __func__, __LINE__)
+
+static inline void __mxs_dma_writel(u32 val,
+                               struct mxs_dma_engine *mxs_dma, unsigned int reg,
+                               const char *name, const char *fn, int ln)
+{
+       void __iomem *addr = mxs_dma->base + reg;
+
+       DBG(3, "%s@%d: Writing %08x to %s[%08x]\n", fn, ln, val, name,
+               APBHDMA_BASE_ADDR + reg);
+       writel(val, addr);
+}
+#define mxs_dma_writel(v, t, r)                __mxs_dma_writel(v, t, r, #r, __func__, __LINE__)
+#else
+static inline u32 mxs_dma_readl(struct mxs_dma_engine *mxs_dma,
+                               unsigned int reg)
+{
+       BUG_ON(mxs_dma->base == NULL);
+       return readl(mxs_dma->base + reg);
+}
+
+static inline void mxs_dma_writel(u32 val, struct mxs_dma_engine *mxs_dma,
+                               unsigned int reg)
+{
+       BUG_ON(mxs_dma->base == NULL);
+       writel(val, mxs_dma->base + reg);
+}
+#endif
+
+static inline void mxs_dma_clkgate(struct mxs_dma_chan *mxs_chan, int enable)
+{
+       struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
+       int chan_id = mxs_chan->chan_id;
+       int set_clr = enable ? MXS_CLR_ADDR : MXS_SET_ADDR;
+
+       /* enable apbh channel clock */
+       mxs_dma_writel(1 << chan_id,
+               mxs_dma, HW_APBHX_CTRL0 + set_clr);
+}
+
+static void mxs_dma_reset_chan(struct mxs_dma_chan *mxs_chan)
+{
+       struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
+       int chan_id = mxs_chan->chan_id;
+
+       mxs_dma_writel(1 << (chan_id + BP_APBHX_CHANNEL_CTRL_RESET_CHANNEL),
+               mxs_dma, HW_APBHX_CHANNEL_CTRL + MXS_SET_ADDR);
+}
+
+static void mxs_dma_enable_chan(struct mxs_dma_chan *mxs_chan)
+{
+       struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
+       int chan_id = mxs_chan->chan_id;
+
+       /* clkgate needs to be enabled before writing other registers */
+       mxs_dma_clkgate(mxs_chan, 1);
+
+       /* set cmd_addr up */
+       mxs_dma_writel(mxs_chan->ccw_phys,
+               mxs_dma, HW_APBHX_CHn_NXTCMDAR(chan_id));
+
+       /* write 1 to SEMA to kick off the channel */
+       mxs_dma_writel(1, mxs_dma, HW_APBHX_CHn_SEMA(chan_id));
+}
+
+static void mxs_dma_disable_chan(struct mxs_dma_chan *mxs_chan)
+{
+       /* disable apbh channel clock */
+       mxs_dma_clkgate(mxs_chan, 0);
+
+       mxs_chan->status = DMA_SUCCESS;
+}
+
+#ifdef _DEBUG
+static inline u32 __mxs_gpmi_readl(struct mxs_gpmi *gpmi,
+                                       unsigned int reg,
+                                       const char *name, const char *fn, int ln)
+{
+       u32 val;
+       void __iomem *addr = gpmi->gpmi_regs + reg;
+
+       val = readl(addr);
+       DBG(3, "%s@%d: Read %08x from %s[%08x]\n", fn, ln, val, name,
+               GPMI_BASE_ADDR + reg);
+       return val;
+}
+#define mxs_gpmi_readl(t, r)           __mxs_gpmi_readl(t, r, #r, __func__, __LINE__)
+
+static inline void __mxs_gpmi_writel(u32 val,
+                               struct mxs_gpmi *gpmi, unsigned int reg,
+                               const char *name, const char *fn, int ln)
+{
+       void __iomem *addr = gpmi->gpmi_regs + reg;
+
+       DBG(3, "%s@%d: Writing %08x to %s[%08x]\n", fn, ln, val, name,
+               GPMI_BASE_ADDR + reg);
+       writel(val, addr);
+}
+#define mxs_gpmi_writel(v, t, r)       __mxs_gpmi_writel(v, t, r, #r, __func__, __LINE__)
+
+static inline u32 __mxs_bch_readl(struct mxs_gpmi *gpmi,
+                                       unsigned int reg, const char *name,
+                                       const char *fn, int ln)
+{
+       u32 val;
+       void __iomem *addr = gpmi->bch_regs + reg;
+
+       val = readl(addr);
+       DBG(3, "%s@%d: Read %08x from %s[%08x]\n", fn, ln, val, name,
+               BCH_BASE_ADDR + reg);
+       return val;
+}
+#define mxs_bch_readl(t, r)            __mxs_bch_readl(t, r, #r, __func__, __LINE__)
+
+static inline void __mxs_bch_writel(u32 val,
+                               struct mxs_gpmi *gpmi, unsigned int reg,
+                               const char *name, const char *fn, int ln)
+{
+       void __iomem *addr = gpmi->bch_regs + reg;
+
+       DBG(3, "%s@%d: Writing %08x to %s[%08x]\n", fn, ln, val, name,
+               BCH_BASE_ADDR + reg);
+       writel(val, addr);
+}
+#define mxs_bch_writel(v, t, r)                __mxs_bch_writel(v, t, r, #r, __func__, __LINE__)
+#else
+static inline u32 mxs_gpmi_readl(struct mxs_gpmi *gpmi, unsigned int reg)
+{
+       return readl(gpmi->gpmi_regs + reg);
+}
+
+static inline void mxs_gpmi_writel(u32 val,
+                               struct mxs_gpmi *gpmi, unsigned int reg)
+{
+       writel(val, gpmi->gpmi_regs + reg);
+}
+
+static inline u32 mxs_bch_readl(struct mxs_gpmi *gpmi, unsigned int reg)
+{
+       return readl(gpmi->bch_regs + reg);
+}
+
+static inline void mxs_bch_writel(u32 val,
+                               struct mxs_gpmi *gpmi, unsigned int reg)
+{
+       writel(val, gpmi->bch_regs + reg);
+}
+#endif
+
+static inline struct mxs_dma_chan *mxs_gpmi_dma_chan(struct mxs_gpmi *gpmi,
+                                               unsigned int dma_channel)
+{
+       BUG_ON(dma_channel >= ARRAY_SIZE(gpmi->mxs_dma.mxs_chans));
+       DBG(3, "%s: DMA chan[%d]=%p[%d]\n", __func__,
+               dma_channel, &gpmi->mxs_dma.mxs_chans[dma_channel],
+               gpmi->mxs_dma.mxs_chans[dma_channel].chan_id);
+       return &gpmi->mxs_dma.mxs_chans[dma_channel];
+}
+
+#define DUMP_DMA_CONTEXT
+
+static int first = 1;
+#ifdef DUMP_DMA_CONTEXT
+
+#define APBH_DMA_PHYS_ADDR     (MXS_IO_BASE_ADDR + 0x004000)
+
+static void dump_dma_context(struct mxs_gpmi *gpmi, const char *title)
+{
+       void *q;
+       u32 *p;
+       unsigned int i;
+
+       if (!dbg_lvl(3))
+               return;
+
+       DBG(0, "%s: %s\n", __func__, title);
+
+       DBG(0, "%s: GPMI:\n", __func__);
+       {
+               void __iomem *GPMI = gpmi->gpmi_regs;
+               static u32 old[13];
+
+               p = q = GPMI;
+
+               for (i = 0; i < ARRAY_SIZE(old); i++) {
+                       u32 val = readl(gpmi->gpmi_regs + i * 0x10);
+
+                       if (first || val != old[i]) {
+                               if (first)
+                                       DBG(0, "    [%p] 0x%08x\n",
+                                               p, val);
+                               else
+                                       DBG(0, "    [%p] 0x%08x -> 0x%08x\n",
+                                               p, old[i], val);
+                               old[i] = val;
+                       }
+                       q += 0x10;
+                       p = q;
+               }
+       }
+
+       DBG(0, "%s: BCH:\n", __func__);
+       {
+               void *BCH = gpmi->bch_regs;
+               static u32 old[22];
+
+               p = q = BCH;
+
+               for (i = 0; i < ARRAY_SIZE(old); i++) {
+                       u32 val = readl(gpmi->bch_regs + i * 0x10);
+
+                       if (first || val != old[i]) {
+                               if (first)
+                                       DBG(0, "    [%p] 0x%08x\n",
+                                               q, val);
+                               else
+                                       DBG(0, "    [%p] 0x%08x -> 0x%08x\n",
+                                               q, old[i], val);
+                               old[i] = val;
+                       }
+                       q += 0x10;
+                       p = q;
+               }
+       }
+
+       DBG(0, "%s: APBH:\n", __func__);
+       {
+               void *APBH = gpmi->dma_regs;
+               static u32 old[7];
+               static u32 chan[16][7];
+
+               p = q = APBH;
+
+               for (i = 0; i < ARRAY_SIZE(old); i++) {
+                       u32 val = readl(gpmi->dma_regs + i * 0x10);
+
+                       if (first || val != old[i]) {
+                               if (first)
+                                       DBG(0, "    [%p] 0x%08x\n",
+                                               q, val);
+                               else
+                                       DBG(0, "    [%p] 0x%08x -> 0x%08x\n",
+                                               q, old[i], val);
+                               old[i] = val;
+                       }
+                       q += 0x10;
+                       p = q;
+               }
+               for (i = 0; i < ARRAY_SIZE(chan); i++) {
+                       int j;
+
+                       printk("CHAN %2d:\n", i);
+                       for (j = 0; j < ARRAY_SIZE(chan[i]); j++) {
+                               u32 val;
+
+                               p = q = APBH + 0x100 + i * 0x70 + j * 0x10;
+
+                               val = readl(gpmi->dma_regs + 0x100 + i * 0x70 + j * 0x10);
+
+                               if (first || val != chan[i][j]) {
+                                       if (first)
+                                               DBG(0, "    [%p] 0x%08x\n",
+                                                       q, val);
+                                       else
+                                               DBG(0, "    [%p] 0x%08x -> 0x%08x\n",
+                                                       q, chan[i][j], val);
+                                       chan[i][j] = val;
+                               }
+                               q += 0x10;
+                               p = q;
+                       }
+               }
+       }
+       first = 0;
+}
+#else
+static inline void dump_dma_context(struct mxs_gpmi *gpmi, char *title)
+{
+}
+#endif
+
+static int mxs_gpmi_init_hw(struct mxs_gpmi *gpmi)
+{
+       dump_dma_context(gpmi, "BEFORE INIT");
+
+       if (mxs_gpmi_readl(gpmi, HW_GPMI_CTRL0) & 0xc0000000) {
+               DBG(0, "%s: Resetting GPMI\n", __func__);
+               mxs_reset_block(gpmi->gpmi_regs);
+       }
+
+       mxs_gpmi_writel(BM_GPMI_CTRL1_GPMI_MODE, gpmi, HW_GPMI_CTRL1_CLR);
+       mxs_gpmi_writel(BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY,
+                       gpmi, HW_GPMI_CTRL1_SET);
+
+       /* Disable write protection and Select BCH ECC */
+       mxs_gpmi_writel(BM_GPMI_CTRL1_DEV_RESET | BM_GPMI_CTRL1_BCH_MODE,
+                       gpmi, HW_GPMI_CTRL1_SET);
+
+       /* Select BCH ECC. */
+       mxs_gpmi_writel(BM_GPMI_CTRL1_BCH_MODE,
+                       gpmi, HW_GPMI_CTRL1_SET);
+
+       dump_dma_context(gpmi, "AFTER INIT");
+       return 0;
+}
+
+static int mxs_dma_chan_init(struct mxs_dma_chan *mxs_chan, int chan_id)
+{
+       mxs_chan->ccw = dma_alloc_coherent(PAGE_SIZE, &mxs_chan->ccw_phys);
+       if (mxs_chan->ccw == NULL)
+               return -ENOMEM;
+
+       DBG(0, "%s: mxs_chan[%d]=%p ccw=%p\n", __func__,
+               chan_id, mxs_chan, mxs_chan->ccw);
+
+       memset(mxs_chan->ccw, 0, PAGE_SIZE);
+       mxs_chan->chan_id = chan_id;
+
+       mxs_dma_clkgate(mxs_chan, 1);
+       mxs_dma_reset_chan(mxs_chan);
+       mxs_dma_clkgate(mxs_chan, 0);
+       return 0;
+}
+
+static int mxs_dma_init(struct mxs_gpmi *gpmi, int dma_chan,
+                       int num_dma_channels)
+{
+       int ret;
+       struct mxs_dma_engine *mxs_dma = &gpmi->mxs_dma;
+       int i;
+
+       writel(readl(CLKCTRL_BASE_ADDR + HW_CLKCTRL_GPMI) & ~BM_CLKCTRL_GPMI_CLKGATE,
+               CLKCTRL_BASE_ADDR + HW_CLKCTRL_GPMI);
+
+       mxs_dma->base = gpmi->dma_regs;
+
+       ret = mxs_reset_block(mxs_dma->base);
+       if (ret) {
+               printk(KERN_ERR "%s: Failed to reset APBH DMA controller\n", __func__);
+               return ret;
+       }
+
+       mxs_dma_writel(BM_APBH_CTRL0_APB_BURST_EN,
+               mxs_dma, HW_APBHX_CTRL0 + MXS_SET_ADDR);
+       mxs_dma_writel(BM_APBH_CTRL0_APB_BURST8_EN,
+               mxs_dma, HW_APBHX_CTRL0 + MXS_SET_ADDR);
+
+       for (i = 0; i < num_dma_channels; i++) {
+               struct mxs_dma_chan *mxs_chan = &mxs_dma->mxs_chans[i];
+
+               mxs_chan->mxs_dma = mxs_dma;
+               ret = mxs_dma_chan_init(mxs_chan, dma_chan + i);
+               if (ret)
+                       return ret;
+       }
+       return 0;
+}
+
+static int mxs_dma_prep_slave(struct mxs_dma_chan *mxs_chan, void *buffer,
+                               dma_addr_t buf_dma, int len,
+                               enum dma_data_direction direction,
+                               int append)
+{
+       int ret;
+       int idx = append ? mxs_chan->ccw_idx : 0;
+       struct mxs_dma_ccw *ccw;
+
+       DBG(1, "%s: mxs_chan=%p status=%d append=%d ccw=%p\n", __func__,
+               mxs_chan, mxs_chan->status, append, mxs_chan->ccw);
+
+       BUG_ON(mxs_chan->ccw == NULL);
+       BUG_ON(mxs_chan == NULL);
+
+       if (mxs_chan->status == DMA_IN_PROGRESS && !append)
+               return -EBUSY;
+
+       if (mxs_chan->status != DMA_IN_PROGRESS && append) {
+               ret = -EINVAL;
+               goto err_out;
+       }
+
+       if (idx >= NUM_CCW) {
+               printk(KERN_ERR "maximum number of segments exceeded: %d > %d\n",
+                       idx, NUM_CCW);
+               ret = -EINVAL;
+               goto err_out;
+       }
+
+       mxs_chan->status = DMA_IN_PROGRESS;
+
+       if (append) {
+               BUG_ON(idx < 1);
+               ccw = &mxs_chan->ccw[idx - 1];
+               ccw->next = mxs_chan->ccw_phys + sizeof(*ccw) * idx;
+               ccw->bits |= CCW_CHAIN;
+               ccw->bits &= ~CCW_IRQ;
+               ccw->bits &= ~CCW_DEC_SEM;
+               ccw->bits &= ~CCW_WAIT4END;
+
+               DBG(3, "%s: Appending sg to list[%d]@%p: next=0x%08x bits=0x%08x\n",
+                       __func__, idx, ccw, ccw->next, ccw->bits);
+       } else {
+               idx = 0;
+       }
+       ccw = &mxs_chan->ccw[idx++];
+       if (direction == DMA_NONE) {
+               int j;
+               u32 *pio = buffer;
+
+               for (j = 0; j < len; j++)
+                       ccw->pio_words[j] = *pio++;
+
+               if (dbg_lvl(3)) {
+                       DBG(0, "%s: Storing %d PIO words in ccw[%d]@%p:", __func__,
+                               len, idx - 1, ccw);
+                       for (j = 0; j < len; j++) {
+                               printk(" %08x", ccw->pio_words[j]);
+                       }
+                       printk("\n");
+               }
+               ccw->bits = 0;
+               ccw->bits |= CCW_IRQ;
+               ccw->bits |= CCW_DEC_SEM;
+               ccw->bits |= CCW_WAIT4END;
+               ccw->bits |= CCW_HALT_ON_TERM;
+               ccw->bits |= CCW_TERM_FLUSH;
+               ccw->bits |= BF_CCW(len, PIO_NUM);
+               ccw->bits |= BF_CCW(MXS_DMA_CMD_NO_XFER, COMMAND);
+       } else {
+               if (len > MAX_XFER_BYTES) {
+                       printk(KERN_ERR "maximum bytes for sg entry exceeded: %d > %d\n",
+                               len, MAX_XFER_BYTES);
+                       ret = -EINVAL;
+                       goto err_out;
+               }
+
+               ccw->next = mxs_chan->ccw_phys + sizeof(*ccw) * idx;
+               ccw->bufaddr = buf_dma;
+               ccw->xfer_bytes = len;
+
+               ccw->bits = 0;
+               ccw->bits |= CCW_CHAIN;
+               ccw->bits |= CCW_HALT_ON_TERM;
+               ccw->bits |= CCW_TERM_FLUSH;
+               ccw->bits |= BF_CCW(direction == DMA_FROM_DEVICE ?
+                               MXS_DMA_CMD_WRITE : MXS_DMA_CMD_READ,
+                               COMMAND);
+
+               ccw->bits &= ~CCW_CHAIN;
+               ccw->bits |= CCW_IRQ;
+               ccw->bits |= CCW_DEC_SEM;
+               ccw->bits |= CCW_WAIT4END;
+               DBG(3, "%s: DMA descriptor: ccw=%p next=0x%08x bufadr=%08x xfer_bytes=%08x bits=0x%08x\n",
+                       __func__, ccw, ccw->next, ccw->bufaddr,
+                       ccw->xfer_bytes, ccw->bits);
+       }
+
+       mxs_chan->ccw_idx = idx;
+
+       return 0;
+
+err_out:
+       mxs_chan->ccw_idx = 0;
+       mxs_chan->status = DMA_ERROR;
+       return ret;
+}
+
+static int mxs_dma_submit(struct mxs_gpmi *gpmi, struct mxs_dma_chan *mxs_chan)
+{
+       int ret;
+       int first = 1;
+       u32 stat1, stat2;
+       int chan_id = mxs_chan->chan_id;
+       u32 chan_mask = 1 << chan_id;
+       struct mxs_dma_engine *mxs_dma = mxs_chan->mxs_dma;
+       long timeout = 1000;
+
+       dump_dma_context(gpmi, "BEFORE");
+
+       mxs_dma_enable_chan(mxs_chan);
+
+       dump_dma_context(gpmi, "WITHIN");
+
+       while (1) {
+               stat1 = mxs_dma_readl(mxs_dma, HW_APBHX_CTRL1);
+               stat2 = mxs_dma_readl(mxs_dma, HW_APBHX_CTRL2);
+               if ((stat1 & chan_mask) || (stat2 & chan_mask)) {
+                       break;
+               }
+               if (first) {
+                       DBG(1, "Waiting for DMA channel %d to finish\n",
+                               chan_id);
+                       first = 0;
+               }
+               if (timeout-- < 0)
+                       return -ETIME;
+               udelay(100);
+       }
+
+       dump_dma_context(gpmi, "AFTER");
+
+       mxs_dma_writel(chan_mask, mxs_dma, HW_APBHX_CTRL1 + MXS_CLR_ADDR);
+       mxs_dma_writel(chan_mask, mxs_dma, HW_APBHX_CTRL2 + MXS_CLR_ADDR);
+       stat2 = ((stat2 >> MXS_DMA_CHANNELS) & stat2) |
+               (~(stat2 >> MXS_DMA_CHANNELS) & stat2 & ~stat1);
+       if (stat2 & chan_mask) {
+               printk(KERN_ERR "DMA error in channel %d\n", chan_id);
+               ret = -EIO;
+       } else if (stat1 & chan_mask) {
+               DBG(0, "DMA channel %d finished\n", chan_id);
+               ret = 0;
+       } else {
+               printk(KERN_ERR "DMA channel %d early termination\n", chan_id);
+               ret = -EINVAL;
+       }
+       mxs_chan->status = ret == 0 ? DMA_SUCCESS : DMA_ERROR;
+       return ret;
+}
+
+static void mxs_dma_terminate_all(struct mxs_dma_chan *mxs_chan)
+{
+       mxs_dma_disable_chan(mxs_chan);
+}
+
+static int poll_bit(void __iomem *addr, unsigned int mask, long timeout)
+{
+       while (!(readl(addr) & mask)) {
+               udelay(1000);
+               timeout--;
+       }
+       return timeout == 0 ? -ETIME : 0;
+}
+
+static int mxs_gpmi_dma_go(struct mxs_gpmi *gpmi,
+                       int wait_for_bch)
+{
+       int error;
+       struct mxs_dma_chan *mxs_chan = mxs_gpmi_dma_chan(gpmi,
+                                                       gpmi->current_chip);
+
+       DBG(1, "> %s\n", __func__);
+
+       error = mxs_dma_submit(gpmi, mxs_chan);
+       DBG(1, "%s: mxs_dma_submit returned %d\n", __func__,
+               error);
+       if (error)
+               goto err;
+
+       if (wait_for_bch) {
+               DBG(1, "%s: Waiting for BCH completion\n", __func__);
+               error = poll_bit(gpmi->bch_regs + HW_BCH_CTRL,
+                               BM_BCH_CTRL_COMPLETE_IRQ,
+                               dma_timeout);
+               DBG(1, "%s: poll_bit returned %d\n", __func__,
+                       error);
+               DBG(1, "%s: BCH status %08x\n", __func__,
+                       mxs_bch_readl(gpmi, HW_BCH_STATUS0));
+               if (mxs_bch_readl(gpmi, HW_BCH_CTRL) & BM_BCH_CTRL_COMPLETE_IRQ) {
+                       DBG(1, "%s: Clearing BCH IRQ\n", __func__);
+                       mxs_bch_writel(BM_BCH_CTRL_COMPLETE_IRQ, gpmi, HW_BCH_CTRL_CLR);
+               }
+
+               if (error)
+                       goto err;
+       }
+out:
+       DBG(1, "< %s: %d\n", __func__, error);
+       return error;
+
+err:
+       {
+               struct mxs_dma_chan *mxs_chan = mxs_gpmi_dma_chan(gpmi,
+                                                               gpmi->current_chip);
+               dump_dma_context(gpmi, "ERROR");
+               mxs_dma_terminate_all(mxs_chan);
+       }
+       goto out;
+}
+
+int mxs_gpmi_dma_setup(struct mxs_gpmi *gpmi, void *buffer, int length,
+               int pio_words, enum dma_data_direction dir, int append)
+
+{
+       int ret;
+       struct mxs_dma_chan *mxs_chan;
+       dma_addr_t buf_dma;
+
+       mxs_chan = mxs_gpmi_dma_chan(gpmi, gpmi->current_chip);
+       if (mxs_chan == NULL)
+               return -EINVAL;
+
+       DBG(1, "%s: buffer=%p len=%u pio=%d append=%d\n", __func__,
+               buffer, length, pio_words, append);
+
+       if (pio_words) {
+               ret = mxs_dma_prep_slave(mxs_chan, gpmi->pio_data, ~0,
+                                       pio_words, DMA_NONE, append);
+               if (ret) {
+                       mxs_dma_terminate_all(mxs_chan);
+                       printk(KERN_ERR
+                               "%s: Failed to setup DMA PIO xfer for %d words: %d\n",
+                               __func__, pio_words, ret);
+                       return ret;
+               }
+               if (buffer == NULL)
+                       return ret;
+
+               append = 1;
+       }
+
+#if 0
+       if (dir == DMA_FROM_DEVICE)
+               memset(buffer, 0x55, length);
+#endif
+       buf_dma = dma_map_single(buffer, length, dir);
+
+       DBG(1, "%s: buffer=%p dma_addr=%08x\n", __func__, buffer, buf_dma);
+
+       ret = mxs_dma_prep_slave(mxs_chan, buffer, buf_dma, length, dir, append);
+       if (ret) {
+               mxs_dma_terminate_all(mxs_chan);
+               DBG(1, "%s: mxs_dma_prep_slave() returned %d\n",
+                       __func__, ret);
+               dma_unmap_single(buffer, length, dir);
+       }
+       return ret;
+}
+
+static int mxs_gpmi_dma_xfer(struct mxs_gpmi *gpmi,
+                       void *buffer, int length, int pio_words,
+                       enum dma_data_direction dir)
+{
+       int ret;
+
+       ret = mxs_gpmi_dma_setup(gpmi, buffer, length,
+                               pio_words, dir, 0);
+
+       if (ret) {
+               DBG(1, "%s: mxs_gpmi_dma_setup() returned %d\n",
+                       __func__, ret);
+               return ret;
+       }
+
+       DBG(1, "%s: starting DMA xfer\n", __func__);
+       ret = mxs_gpmi_dma_go(gpmi, 0);
+
+       DBG(1, "%s: DMA xfer done: %d\n", __func__, ret);
+       return ret;
+}
+
+/* low level accessor functions */
+static int mxs_gpmi_read_data(struct mxs_gpmi *gpmi, int cs,
+                       void *buffer, size_t length)
+{
+       int ret;
+       u32 command_mode;
+       u32 address;
+
+       DBG(2, "%s: buf=%p len=%d\n", __func__, buffer, length);
+
+       memset(buffer, 0x44, length);
+
+       command_mode = BV_GPMI_CTRL0_COMMAND_MODE__READ;
+       address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+       gpmi->pio_data[0] =
+               BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
+               BF_GPMI_CTRL0_CS_V1(cs) |
+               BM_GPMI_CTRL0_WORD_LENGTH |
+               BF_GPMI_CTRL0_ADDRESS(address) |
+               BF_GPMI_CTRL0_XFER_COUNT(length);
+       gpmi->pio_data[1] = 0;
+
+       ret = mxs_gpmi_dma_xfer(gpmi, buffer, length, 2, DMA_FROM_DEVICE);
+       return ret;
+}
+
+/* mtd layer interface */
+static void mxs_gpmi_select_chip(struct mtd_info *mtd, int cs)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct mxs_gpmi *gpmi = chip->priv;
+
+       gpmi->current_chip = cs;
+}
+
+static int mxs_gpmi_dev_ready(struct mtd_info *mtd)
+{
+       int ready;
+       struct nand_chip *chip = mtd->priv;
+       struct mxs_gpmi *gpmi = chip->priv;
+       u32 mask;
+       u32 reg;
+       int cs = gpmi->current_chip;
+
+       if (cs < 0)
+               return 0;
+
+       DBG(1, "> %s\n", __func__);
+
+       mask = BF_GPMI_STAT_READY_BUSY(1 << cs);
+       reg = mxs_gpmi_readl(gpmi, HW_GPMI_STAT);
+
+       ready = !!(reg & mask);
+       DBG(1, "< %s: %d\n", __func__, ready);
+       return ready;
+}
+
+static void mxs_gpmi_swap_bb_mark(struct mxs_gpmi *gpmi,
+                               void *payload, void *auxiliary)
+{
+       unsigned char *p = payload + gpmi->block_mark_byte_offset;
+       unsigned char *a = auxiliary;
+       unsigned int bit = gpmi->block_mark_bit_offset;
+       unsigned char mask;
+       unsigned char from_data;
+       unsigned char from_oob;
+
+       /*
+        * Get the byte from the data area that overlays the block mark. Since
+        * the ECC engine applies its own view to the bits in the page, the
+        * physical block mark won't (in general) appear on a byte boundary in
+        * the data.
+        */
+       from_data = (p[0] >> bit) | (p[1] << (8 - bit));
+
+       /* Get the byte from the OOB. */
+       from_oob = a[0];
+
+       /* Swap them. */
+       a[0] = from_data;
+
+       mask = (0x1 << bit) - 1;
+       p[0] = (p[0] & mask) | (from_oob << bit);
+
+       mask = ~0 << bit;
+       p[1] = (p[1] & mask) | (from_oob >> (8 - bit));
+}
+
+static int mxs_gpmi_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+                       uint8_t *buf)
+{
+       int ret = -1;
+       struct mxs_gpmi *gpmi = chip->priv;
+       int cs = gpmi->current_chip;
+       u32 command_mode;
+       u32 address;
+       u32 ecc_command;
+       u32 buffer_mask;
+       dma_addr_t buf_dma;
+       dma_addr_t oob_dma;
+
+       DBG(3, "%s: read page to buffer %p\n", __func__, buf);
+
+       buf_dma = dma_map_single(gpmi->page_buf, mtd->writesize,
+                               DMA_FROM_DEVICE);
+
+       oob_dma = dma_map_single(gpmi->oob_buf, mtd->oobsize,
+                               DMA_FROM_DEVICE);
+
+       command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY;
+       address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+       gpmi->pio_data[0] =
+               BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
+               BF_GPMI_CTRL0_CS_V1(cs) |
+               BM_GPMI_CTRL0_WORD_LENGTH |
+               BF_GPMI_CTRL0_ADDRESS(address) |
+               BF_GPMI_CTRL0_XFER_COUNT(0);
+       gpmi->pio_data[1] = 0;
+
+       ret = mxs_gpmi_dma_setup(gpmi, NULL, 0, 2, DMA_NONE, 0);
+       if (ret) {
+               goto unmap;
+       }
+
+       command_mode = BV_GPMI_CTRL0_COMMAND_MODE__READ;
+       address      = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+       ecc_command  = BV_GPMI_ECCCTRL_ECC_CMD__DECODE;
+       buffer_mask  = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE |
+               BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY;
+
+       gpmi->pio_data[0] = BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
+               BM_GPMI_CTRL0_WORD_LENGTH |
+               BF_GPMI_CTRL0_CS_V1(cs) |
+               BF_GPMI_CTRL0_ADDRESS(address) |
+               BF_GPMI_CTRL0_XFER_COUNT(mtd->writesize + mtd->oobsize);
+
+       gpmi->pio_data[1] = 0;
+
+       gpmi->pio_data[2] = BM_GPMI_ECCCTRL_ENABLE_ECC |
+               BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) |
+               BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask);
+
+       gpmi->pio_data[3] = mtd->writesize + mtd->oobsize;
+       gpmi->pio_data[4] = buf_dma;
+       gpmi->pio_data[5] = oob_dma;
+
+       ret = mxs_gpmi_dma_setup(gpmi, NULL, 0, 6, DMA_NONE, 1);
+       if (ret) {
+               goto unmap;
+       }
+
+       command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY;
+       address      = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+       gpmi->pio_data[0] =
+               BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
+               BM_GPMI_CTRL0_WORD_LENGTH |
+               BF_GPMI_CTRL0_CS_V1(cs) |
+               BF_GPMI_CTRL0_ADDRESS(address) |
+               BF_GPMI_CTRL0_XFER_COUNT(mtd->writesize + mtd->oobsize);
+
+       gpmi->pio_data[1] = 0;
+
+       ret = mxs_gpmi_dma_setup(gpmi, NULL, 0, 2, DMA_NONE, 1);
+       if (ret == 0) {
+               ret = mxs_gpmi_dma_go(gpmi, 1);
+       }
+unmap:
+       dma_unmap_single(gpmi->oob_buf, mtd->oobsize,
+                       DMA_FROM_DEVICE);
+       dma_unmap_single(gpmi->page_buf, mtd->writesize,
+                       DMA_FROM_DEVICE);
+       {
+#define STATUS_GOOD            0x00
+#define STATUS_ERASED          0xff
+#define STATUS_UNCORRECTABLE   0xfe
+               /* Loop over status bytes, accumulating ECC status. */
+               struct nand_chip *chip = mtd->priv;
+               int failed = 0;
+               int corrected = 0;
+               u8 *status = gpmi->oob_buf + mtd->oobavail;
+               int i;
+
+               for (i = 0; i < mtd->writesize / chip->ecc.size; i++, status++) {
+                       if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED))
+                               continue;
+
+                       if (*status == STATUS_UNCORRECTABLE) {
+                               failed++;
+                               continue;
+                       }
+                       corrected += *status;
+               }
+               /*
+                * Propagate ECC status to the owning MTD only when failed or
+                * corrected times nearly reaches our ECC correction threshold.
+                */
+               if (failed || corrected >= (chip->ecc.size - 1)) {
+                       DBG(0, "%s: ECC failures: %d\n", __func__, failed);
+                       mtd->ecc_stats.failed += failed;
+                       mtd->ecc_stats.corrected += corrected;
+               }
+       }
+       if (ret == 0) {
+               if (gpmi->swap_block_mark)
+                       mxs_gpmi_swap_bb_mark(gpmi, gpmi->page_buf, gpmi->oob_buf);
+               if (buf) {
+                       memcpy(buf, gpmi->page_buf, mtd->writesize);
+               }
+       } else {
+               printk(KERN_ERR "%s: FAILED to read page to buffer %p\n", __func__, buf);
+       }
+       return ret;
+}
+
+static int mxs_gpmi_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+                       int page, int sndcmd)
+{
+       DBG(3, "%s: reading OOB of page %d\n", __func__, page);
+
+       memset(chip->oob_poi, dbg_lvl(0) ? 0xfe : 0xff, mtd->oobsize);
+       chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+       chip->read_buf(mtd, chip->oob_poi, mtd->oobavail);
+       return 1;
+}
+
+static void mxs_gpmi_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+                               const uint8_t *buf)
+{
+       int ret = -1;
+       struct mxs_gpmi *gpmi = chip->priv;
+       int cs = gpmi->current_chip;
+       u32 command_mode;
+       u32 address;
+       u32 ecc_command;
+       u32 buffer_mask;
+       dma_addr_t buf_dma;
+       dma_addr_t oob_dma;
+
+       DBG(3, "%s: Writing buffer %p\n", __func__, buf);
+
+       memset(gpmi->oob_buf + mtd->oobavail, dbg_lvl(0) ? 0xef : 0xff,
+               mtd->oobsize - mtd->oobavail);
+
+       if (buf)
+               memcpy(gpmi->page_buf, buf, mtd->writesize);
+
+       if (gpmi->swap_block_mark)
+               mxs_gpmi_swap_bb_mark(gpmi, gpmi->page_buf, gpmi->oob_buf);
+
+       buf_dma = dma_map_single(gpmi->page_buf, mtd->writesize,
+                               DMA_TO_DEVICE);
+
+       oob_dma = dma_map_single(gpmi->oob_buf, mtd->oobsize,
+                               DMA_TO_DEVICE);
+
+       command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE;
+       address      = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+       ecc_command  = BV_GPMI_ECCCTRL_ECC_CMD__ENCODE;
+       buffer_mask  = BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE |
+                               BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY;
+
+       gpmi->pio_data[0] =
+               BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
+               BM_GPMI_CTRL0_WORD_LENGTH |
+               BF_GPMI_CTRL0_CS_V1(cs) |
+               BF_GPMI_CTRL0_ADDRESS(address) |
+               BF_GPMI_CTRL0_XFER_COUNT(0);
+       gpmi->pio_data[1] = 0;
+       gpmi->pio_data[2] =
+               BM_GPMI_ECCCTRL_ENABLE_ECC |
+               BF_GPMI_ECCCTRL_ECC_CMD(ecc_command) |
+               BF_GPMI_ECCCTRL_BUFFER_MASK(buffer_mask);
+       gpmi->pio_data[3] = mtd->writesize + mtd->oobsize;
+       gpmi->pio_data[4] = buf_dma;
+       gpmi->pio_data[5] = oob_dma;
+
+       ret = mxs_gpmi_dma_setup(gpmi, NULL, 0, 6, DMA_NONE, 0);
+       if (ret == 0) {
+               ret = mxs_gpmi_dma_go(gpmi, 1);
+       }
+
+       dma_unmap_single(gpmi->oob_buf, mtd->oobsize,
+                       DMA_TO_DEVICE);
+       dma_unmap_single(gpmi->page_buf, mtd->writesize,
+                       DMA_TO_DEVICE);
+       if (ret) {
+               printk(KERN_ERR "%s: FAILED!\n", __func__);
+       }
+}
+
+static int mxs_gpmi_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+                       int page)
+{
+       return -EINVAL;
+}
+
+#if 0
+static void mxs_gpmi_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+                               const uint8_t *buf)
+{
+       memcpy(gpmi->page_buf, buf, mtd->writesize);
+
+}
+#endif
+
+static void mxs_gpmi_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct mxs_gpmi *gpmi = chip->priv;
+       void *xfer_buf;
+       int ret = 0;
+
+       if (len > mtd->writesize + mtd->oobsize) {
+               DBG(0, "%s: Allocating temporary buffer\n", __func__);
+               xfer_buf = kzalloc(len, GFP_KERNEL);
+               if (xfer_buf == NULL) {
+                       printk(KERN_ERR
+                               "Failed to allocate %u byte for xfer buffer\n",
+                               len);
+                       memset(buf, 0xee, len);
+               }
+       } else {
+               xfer_buf = buf;
+       }
+
+       DBG(3, "%s: reading %u byte to %p(%p)\n", __func__,
+               len, buf, xfer_buf);
+
+       ret = mxs_gpmi_read_data(gpmi, gpmi->current_chip, xfer_buf, len);
+       if (xfer_buf != buf) {
+               if (ret == 0) {
+                       memcpy(buf, xfer_buf, len);
+               }
+               kfree(xfer_buf);
+       }
+       DBG(1, "< %s %d\n", __func__, ret);
+}
+
+static u_char mxs_gpmi_read_byte(struct mtd_info *mtd)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct mxs_gpmi *gpmi = chip->priv;
+       u_char *buf = (u_char *)gpmi->pio_data;
+
+       mxs_gpmi_read_buf(mtd, buf, 1);
+       return *buf;
+}
+
+static void mxs_gpmi_write_buf(struct mtd_info *mtd, const u_char *buf,
+                       int len)
+{
+       int ret;
+       struct nand_chip *chip = mtd->priv;
+       struct mxs_gpmi *gpmi = chip->priv;
+       void *xfer_buf = (void *)buf; /* cast away the 'const' */
+#if 1
+       u32 command_mode;
+       u32 address;
+       int cs = gpmi->current_chip;
+
+       DBG(3, "%s: writing %u byte from %p\n", __func__, len, buf);
+
+       command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE;
+       address = BV_GPMI_CTRL0_ADDRESS__NAND_DATA;
+
+       gpmi->pio_data[0] =
+               BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
+               BF_GPMI_CTRL0_CS_V1(cs) |
+               BM_GPMI_CTRL0_WORD_LENGTH |
+               BF_GPMI_CTRL0_ADDRESS(address) |
+               BF_GPMI_CTRL0_XFER_COUNT(len);
+       gpmi->pio_data[1] = 0;
+
+       ret = mxs_gpmi_dma_xfer(gpmi, xfer_buf, len, 2, DMA_TO_DEVICE);
+#else
+       ret = mxs_gpmi_send_data(gpmi, gpmi->current_chip, xfer_buf, len);
+#endif
+       if (ret)
+               printk(KERN_ERR "%s: Failed to write %u byte from %p\n", __func__,
+                       len, buf);
+}
+
+static int mxs_gpmi_scan_bbt(struct mtd_info *mtd)
+{
+       int ret;
+
+       DBG(0, "%s: \n", __func__);
+       ret = nand_scan_bbt(mtd, create_bbt ? &gpmi_bbt_descr : NULL);
+       DBG(0, "%s: nand_scan_bbt() returned %d\n", __func__, ret);
+       return ret;
+}
+
+static int mxs_gpmi_send_command(struct mxs_gpmi *gpmi, unsigned cs,
+                       void *buffer, unsigned int length)
+{
+       int error;
+       u32 command_mode;
+       u32 address;
+
+       DBG(1, "%s: Sending NAND command\n", __func__);
+
+       command_mode = BV_GPMI_CTRL0_COMMAND_MODE__WRITE;
+       address = BV_GPMI_CTRL0_ADDRESS__NAND_CLE;
+
+       gpmi->pio_data[0] =
+               BF_GPMI_CTRL0_COMMAND_MODE(command_mode) |
+               BF_GPMI_CTRL0_CS_V1(cs) |
+               BM_GPMI_CTRL0_WORD_LENGTH |
+               BF_GPMI_CTRL0_ADDRESS(address) |
+               BM_GPMI_CTRL0_ADDRESS_INCREMENT |
+               BF_GPMI_CTRL0_XFER_COUNT(length);
+
+       gpmi->pio_data[1] = 0;
+
+       gpmi->pio_data[2] = 0;
+
+       error = mxs_gpmi_dma_xfer(gpmi, buffer, length, 3,
+                               DMA_TO_DEVICE);
+       if (error)
+               printk(KERN_ERR "[%s] DMA error\n", __func__);
+
+       return error;
+}
+
+static void mxs_gpmi_cmdctrl(struct mtd_info *mtd,
+                       int data, unsigned int ctrl)
+{
+       int ret;
+       struct nand_chip *chip = mtd->priv;
+       struct mxs_gpmi *gpmi = chip->priv;
+       unsigned int i;
+
+       DBG(2, "%s: data=%04x ctrl=%04x\n", __func__,
+               data, ctrl);
+       /*
+        * Every operation begins with a command byte and a series of zero or
+        * more address bytes. These are distinguished by either the Address
+        * Latch Enable (ALE) or Command Latch Enable (CLE) signals being
+        * asserted. When MTD is ready to execute the command, it will deassert
+        * both latch enables.
+        *
+        * Rather than run a separate DMA operation for every single byte, we
+        * queue them up and run a single DMA operation for the entire series
+        * of command and data bytes.
+        */
+       if ((ctrl & (NAND_ALE | NAND_CLE))) {
+               if (data != NAND_CMD_NONE) {
+                       DBG(3, "%s: Storing cmd byte %02x\n", __func__, data & 0xff);
+                       gpmi->cmd_buf[gpmi->command_length++] = data;
+               }
+               return;
+       }
+       /*
+        * If control arrives here, MTD has deasserted both the ALE and CLE,
+        * which means it's ready to run an operation. Check if we have any
+        * bytes to send.
+        */
+       if (gpmi->command_length == 0)
+               return;
+
+       DBG(1, "%s: sending command...\n", __func__);
+       for (i = 0; i < gpmi->command_length; i++)
+               DBG(2, " 0x%02x", gpmi->cmd_buf[i]);
+       DBG(2, "\n");
+
+       ret = mxs_gpmi_send_command(gpmi,
+                       gpmi->current_chip, gpmi->cmd_buf,
+                       gpmi->command_length);
+       if (ret) {
+               printk(KERN_ERR "[%s] Chip: %u, Error %d\n",
+                       __func__, gpmi->current_chip, ret);
+       }
+
+       gpmi->command_length = 0;
+       DBG(1, "%s: ...Finished\n", __func__);
+}
+
+static int mxs_gpmi_set_ecclayout(struct mxs_gpmi *gpmi,
+                               int page_size, int oob_size)
+{
+       struct nand_chip *chip = gpmi->chip;
+       struct mtd_info *mtd = gpmi->mtd;
+       struct nand_ecclayout *layout = &gpmi->ecc_layout;
+       const int meta_size = 10;
+       const int block0_size = 512;
+       const int blockn_size = 512;
+       const int fl0_nblocks = (mtd->writesize >> 9) - !!block0_size;
+       int i;
+
+       chip->ecc.mode = NAND_ECC_HW;
+       chip->ecc.size = blockn_size;
+       chip->ecc.layout = layout;
+
+       chip->bbt_td = &mxs_gpmi_bbt_main_descr;
+       chip->bbt_md = &mxs_gpmi_bbt_mirror_descr;
+
+       if (create_bbt) {
+               chip->bbt_td->options |= NAND_BBT_WRITE | NAND_BBT_CREATE;
+               chip->bbt_md->options |= NAND_BBT_WRITE | NAND_BBT_CREATE;
+       }
+
+       switch (page_size) {
+       case 2048:
+               /* default GPMI OOB layout */
+               layout->eccbytes = 4 * 10 + 9;
+               gpmi->block0_ecc_strength = 8;
+               gpmi->blockn_ecc_strength = 8;
+               break;
+
+       case 4096:
+               if (mtd->oobsize == 128) {
+                       gpmi->block0_ecc_strength = 8;
+                       gpmi->blockn_ecc_strength = 8;
+               } else {
+                       gpmi->block0_ecc_strength = 16;
+                       gpmi->blockn_ecc_strength = 16;
+               }
+               break;
+
+       case 8192:
+               gpmi->block0_ecc_strength = 24;
+               gpmi->blockn_ecc_strength = 24;
+               break;
+
+       default:
+               printk(KERN_ERR "unsupported page size: %u\n", page_size);
+               return -EINVAL;
+       }
+
+       {
+               int chunk0_data_size_in_bits = block0_size * 8;
+               int chunk0_ecc_size_in_bits  = gpmi->block0_ecc_strength * 13;
+               int chunkn_data_size_in_bits = blockn_size * 8;
+               int chunkn_ecc_size_in_bits  = gpmi->blockn_ecc_strength * 13;
+               int chunkn_total_size_in_bits = chunkn_data_size_in_bits +
+                       chunkn_ecc_size_in_bits;
+
+               /* Compute the bit offset of the block mark within the physical page. */
+               int block_mark_bit_offset = mtd->writesize * 8;
+
+               /* Subtract the metadata bits. */
+               block_mark_bit_offset -= meta_size * 8;
+
+               /* if the first block is metadata only,
+                * subtract the number of ecc bits of that block
+                */
+               if (block0_size == 0) {
+                       block_mark_bit_offset -= chunk0_ecc_size_in_bits;
+               }
+               /*
+                * Compute the chunk number (starting at zero) in which the block mark
+                * appears.
+                */
+               int block_mark_chunk_number =
+                       block_mark_bit_offset / chunkn_total_size_in_bits;
+
+               /*
+                * Compute the bit offset of the block mark within its chunk, and
+                * validate it.
+                */
+               int block_mark_chunk_bit_offset = block_mark_bit_offset -
+                       (block_mark_chunk_number * chunkn_total_size_in_bits);
+
+               if (block_mark_chunk_bit_offset > chunkn_data_size_in_bits) {
+                       /*
+                        * If control arrives here, the block mark actually appears in
+                        * the ECC bits of this chunk. This wont' work.
+                        */
+                       printf("Unsupported page geometry (block mark in ECC): %u:%u",
+                               mtd->writesize, mtd->oobsize);
+                       return -EINVAL;
+               }
+
+               /*
+                * Now that we know the chunk number in which the block mark appears,
+                * we can subtract all the ECC bits that appear before it.
+                */
+               block_mark_bit_offset -= block_mark_chunk_number *
+                       chunkn_ecc_size_in_bits;
+
+               /*
+                * We now know the absolute bit offset of the block mark within the
+                * ECC-based data. We can now compute the byte offset and the bit
+                * offset within the byte.
+                */
+               gpmi->block_mark_byte_offset = block_mark_bit_offset / 8;
+               gpmi->block_mark_bit_offset = block_mark_bit_offset % 8;
+
+               DBG(0, "NAND geometry:\n");
+               DBG(0, "page size   : %5u byte\n", mtd->writesize);
+               DBG(0, "oob size    : %5u byte\n", mtd->oobsize);
+               DBG(0, "erase size  : %5u byte\n", mtd->erasesize);
+               DBG(0, "metadata    : %5u byte\n", meta_size);
+               DBG(0, "ECC:\n");
+               DBG(0, "chunk0 level: %5u\n", gpmi->block0_ecc_strength);
+               DBG(0, "chunk0 data : %5u bit (%5u byte)\n",
+                       chunk0_data_size_in_bits,
+                       DIV_ROUND_UP(chunk0_data_size_in_bits, 8));
+               DBG(0, "chunk0 ECC  : %5u bit (%5u byte)\n",
+                       chunk0_ecc_size_in_bits,
+                       DIV_ROUND_UP(chunk0_ecc_size_in_bits, 8));
+
+               DBG(0, "chunkn level: %5u\n", gpmi->blockn_ecc_strength);
+               DBG(0, "chunkn data : %5u bit (%5u byte)\n",
+                       chunkn_data_size_in_bits,
+                       DIV_ROUND_UP(chunkn_data_size_in_bits, 8));
+               DBG(0, "chunkn ECC  : %5u bit (%5u byte)\n",
+                       chunkn_ecc_size_in_bits,
+                       DIV_ROUND_UP(chunkn_ecc_size_in_bits, 8));
+               DBG(0, "BB chunk    : %5d\n", block_mark_chunk_number);
+               DBG(0, "BB byte offs: %5u\n", gpmi->block_mark_byte_offset);
+               DBG(0, "BB bit offs : %5u\n", gpmi->block_mark_bit_offset);
+       }
+
+       for (i = 0; i < layout->eccbytes; i++) {
+               layout->eccpos[i] = mtd->oobsize - i - 1;
+       }
+       layout->oobfree[0].length = meta_size;
+
+       chip->ecc.bytes = layout->eccbytes;
+
+       DBG(0, "%s: Resetting BCH\n", __func__);
+       mxs_reset_block(gpmi->bch_regs);
+
+       mxs_bch_writel(
+               BF_BCH_FLASH0LAYOUT0_NBLOCKS(fl0_nblocks) |
+               BF_BCH_FLASH0LAYOUT0_META_SIZE(meta_size) |
+               BF_BCH_FLASH0LAYOUT0_ECC0(gpmi->block0_ecc_strength >> 1) |
+               BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(block0_size),
+               gpmi, HW_BCH_FLASH0LAYOUT0);
+
+       mxs_bch_writel(
+               BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(mtd->writesize + mtd->oobsize) |
+               BF_BCH_FLASH0LAYOUT1_ECCN(gpmi->blockn_ecc_strength >> 1) |
+               BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(blockn_size),
+               gpmi, HW_BCH_FLASH0LAYOUT1);
+
+       mxs_bch_writel(0, gpmi, HW_BCH_LAYOUTSELECT);
+
+       mxs_bch_writel(BM_BCH_CTRL_COMPLETE_IRQ_EN, gpmi, HW_BCH_CTRL_SET);
+       return 0;
+}
+
+int mxs_gpmi_nand_init(struct mtd_info *mtd, struct nand_chip *chip)
+{
+       int ret;
+       struct mxs_gpmi *gpmi;
+
+       gpmi = kzalloc(sizeof(struct mxs_gpmi), GFP_KERNEL);
+       if (gpmi == NULL) {
+               ret = -ENOMEM;
+               return ret;
+       }
+       gpmi->mtd = mtd;
+       gpmi->chip = chip;
+
+       gpmi->chip_count = CONFIG_SYS_MAX_NAND_DEVICE;
+       gpmi->swap_block_mark = 1;
+
+       gpmi->gpmi_regs = __ioremap(GPMI_BASE_ADDR, SZ_4K, 1);
+       if (gpmi->gpmi_regs == NULL) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       gpmi->bch_regs = __ioremap(BCH_BASE_ADDR, SZ_4K, 1);
+       if (gpmi->bch_regs == NULL) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       gpmi->dma_regs = __ioremap(APBHDMA_BASE_ADDR, SZ_4K, 1);
+       if (gpmi->dma_regs == NULL) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       ret = mxs_dma_init(gpmi, CONFIG_SYS_MXS_DMA_CHANNEL,
+                       CONFIG_SYS_MAX_NAND_DEVICE);
+       if (ret)
+               goto out;
+
+       ret = mxs_gpmi_init_hw(gpmi);
+       if (ret)
+               goto out;
+
+       chip->priv = gpmi;
+
+       chip->select_chip = mxs_gpmi_select_chip;
+       chip->cmd_ctrl = mxs_gpmi_cmdctrl;
+       chip->dev_ready = mxs_gpmi_dev_ready;
+
+       chip->read_byte = mxs_gpmi_read_byte;
+       chip->read_buf = mxs_gpmi_read_buf;
+       chip->write_buf = mxs_gpmi_write_buf;
+
+       chip->scan_bbt = mxs_gpmi_scan_bbt;
+
+       chip->options |= NAND_NO_SUBPAGE_WRITE;
+       chip->options |= NAND_USE_FLASH_BBT | NAND_USE_FLASH_BBT_NO_OOB;
+
+       chip->ecc.read_page = mxs_gpmi_read_page;
+       chip->ecc.read_oob = mxs_gpmi_read_oob;
+       chip->ecc.write_page = mxs_gpmi_write_page;
+       chip->ecc.write_oob = mxs_gpmi_write_oob;
+
+       DBG(0, "%s: Scanning for NAND chips\n", __func__);
+       ret = nand_scan_ident(mtd, gpmi->chip_count);
+       if (ret) {
+               DBG(0, "%s: Failed to scan for NAND chips\n", __func__);
+               goto out;
+       }
+       DBG(0, "%s: pagesize=%d oobsize=%d\n", __func__,
+               mtd->writesize, mtd->oobsize);
+
+       gpmi->page_buf = kzalloc(mtd->writesize + mtd->oobsize, GFP_KERNEL);
+       if (gpmi->page_buf == NULL) {
+               ret = -ENOMEM;
+               return ret;
+       }
+       gpmi->oob_buf = gpmi->page_buf + mtd->writesize;
+
+       ret = mxs_gpmi_set_ecclayout(gpmi, mtd->writesize, mtd->oobsize);
+       if (ret) {
+               DBG(0, "%s: Unsupported ECC layout\n", __func__);
+               return ret;
+       }
+       DBG(0, "%s: NAND scan succeeded\n", __func__);
+       return 0;
+out:
+       kfree(gpmi);
+       return ret;
+}
diff --git a/drivers/mtd/nand/mxs_gpmi.h b/drivers/mtd/nand/mxs_gpmi.h
new file mode 100644 (file)
index 0000000..a5c273f
--- /dev/null
@@ -0,0 +1,361 @@
+/*
+ * Freescale GPMI NFC NAND Flash Driver
+ *
+ * Copyright (C) 2010 Freescale Semiconductor, Inc.
+ * Copyright (C) 2008 Embedded Alley Solutions, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __DRIVERS_MTD_NAND_GPMI_NFC_H
+#define __DRIVERS_MTD_NAND_GPMI_NFC_H
+
+/*
+ *------------------------------------------------------------------------------
+ * Fundamental Macros
+ *------------------------------------------------------------------------------
+ */
+
+#define  NFC_DMA_DESCRIPTOR_COUNT  4
+
+/* Define this macro to enable detailed information messages. */
+#define DETAILED_INFO
+
+/* Define this macro to enable event reporting. */
+/*#define EVENT_REPORTING*/
+
+/*
+ *------------------------------------------------------------------------------
+ * Fundamental Data Structures
+ *------------------------------------------------------------------------------
+ */
+
+#define COMMAND_BUFFER_SIZE 10
+
+/**
+ * struct gpmi_nfc_timing - GPMI NFC timing parameters.
+ *
+ * This structure contains the fundamental timing attributes for the NAND Flash
+ * bus and the GPMI NFC hardware.
+ *
+ * @data_setup_in_ns:          The data setup time, in nanoseconds. Usually the
+ *                             maximum of tDS and tWP. A negative value
+ *                             indicates this characteristic isn't known.
+ * @data_hold_in_ns:           The data hold time, in nanoseconds. Usually the
+ *                             maximum of tDH, tWH and tREH. A negative value
+ *                             indicates this characteristic isn't known.
+ * @address_setup_in_ns:       The address setup time, in nanoseconds. Usually
+ *                             the maximum of tCLS, tCS and tALS. A negative
+ *                             value indicates this characteristic isn't known.
+ * @gpmi_sample_time_in_ns:    A GPMI-specific timing parameter. A negative
+ *                             value indicates this characteristic isn't known.
+ * @tREA_in_ns:                tREA, in nanoseconds, from the data sheet. A
+ *                             negative value indicates this characteristic
+ *                             isn't known.
+ * @tRLOH_in_ns:               tRLOH, in nanoseconds, from the data sheet. A
+ *                             negative value indicates this characteristic
+ *                             isn't known.
+ * @tRHOH_in_ns:               tRHOH, in nanoseconds, from the data sheet. A
+ *                             negative value indicates this characteristic
+ *                             isn't known.
+ */
+struct gpmi_nfc_timing {
+       int8_t    data_setup_in_ns;
+       int8_t    data_hold_in_ns;
+       int8_t    address_setup_in_ns;
+       int8_t    gpmi_sample_delay_in_ns;
+       int8_t    tREA_in_ns;
+       int8_t    tRLOH_in_ns;
+       int8_t    tRHOH_in_ns;
+};
+#if 0
+enum nand_device_cell_technology {
+       NAND_DEVICE_CELL_TECH_SLC = 0,
+       NAND_DEVICE_CELL_TECH_MLC = 1,
+};
+
+struct nand_device_info {
+       /* End of table marker */
+       bool      end_of_table;
+
+       /* Manufacturer and Device codes */
+       uint8_t   manufacturer_code;
+       uint8_t   device_code;
+
+       /* Technology */
+       enum nand_device_cell_technology  cell_technology;
+
+       /* Geometry */
+       uint64_t  chip_size_in_bytes;
+       uint32_t  block_size_in_pages;
+       uint16_t  page_total_size_in_bytes;
+
+       /* ECC */
+       uint8_t   ecc_strength_in_bits;
+       uint16_t  ecc_size_in_bytes;
+
+       /* Timing */
+       int8_t    data_setup_in_ns;
+       int8_t    data_hold_in_ns;
+       int8_t    address_setup_in_ns;
+       int8_t    gpmi_sample_delay_in_ns;
+       int8_t    tREA_in_ns;
+       int8_t    tRLOH_in_ns;
+       int8_t    tRHOH_in_ns;
+
+       /* human readable device description */
+       const char  *description;
+};
+
+/**
+ * struct gpmi_nfc_data - i.MX NFC per-device data.
+ *
+ * Note that the "device" managed by this driver represents the NAND Flash
+ * controller *and* the NAND Flash medium behind it. Thus, the per-device data
+ * structure has information about the controller, the chips to which it is
+ * connected, and properties of the medium as a whole.
+ *
+ * @dev:                 A pointer to the owning struct device.
+ * @pdev:                A pointer to the owning struct platform_device.
+ * @pdata:               A pointer to the device's platform data.
+ * @device_info:         A structure that contains detailed information about
+ *                       the NAND Flash device.
+ * @nfc:                 A pointer to a structure that represents the underlying
+ *                       NFC hardware.
+ * @rom:                 A pointer to a structure that represents the underlying
+ *                       Boot ROM.
+ * @mil:                 A collection of information used by the MTD Interface
+ *                       Layer.
+ */
+struct gpmi_nfc_data {
+       /* System Interface */
+       struct device                  *dev;
+       struct platform_device         *pdev;
+       struct gpmi_nfc_platform_data  *pdata;
+
+       /* Resources */
+       struct clk    *clock;
+       void __iomem *gpmi_regs;
+       void __iomem *bch_regs;
+       unsigned int bch_interrupt;
+       unsigned int dma_low_channel;
+       unsigned int dma_high_channel;
+       unsigned int dma_interrupt;
+
+//     struct resources               resources;
+
+       /* NFC HAL */
+       /* Hardware attributes. */
+       unsigned int    max_chip_count;
+
+       /* Working variables. */
+       struct mxs_dma_desc     *dma_descriptors[NFC_DMA_DESCRIPTOR_COUNT];
+       int                     isr_dma_channel;
+       struct completion       dma_done;
+       struct completion       bch_done;
+       struct gpmi_nfc_timing  timing;
+//     struct nfc_hal          *nfc;
+       int                     current_chip;
+
+       /* MTD Interface Layer */
+       struct mtd_info        mtd;
+       struct nand_chip       chip;
+       struct nand_ecclayout  oob_layout;
+       struct mtd_partition   *partitions;
+       unsigned int           partition_count;
+
+       /* DMA Buffers */
+       u8                     *cmd_virt;
+       dma_addr_t             cmd_phys;
+       unsigned int           command_length;
+
+       void                   *page_buffer_virt;
+       dma_addr_t             page_buffer_phys;
+       unsigned int           page_buffer_size;
+
+       void                   *payload_virt;
+       dma_addr_t             payload_phys;
+
+       void                   *auxiliary_virt;
+       dma_addr_t             auxiliary_phys;
+};
+
+/**
+ * struct nfc_hal - GPMI NFC HAL
+ *
+ * This structure embodies an abstract interface to the underlying NFC hardware.
+ *
+ * @version:                       The NFC hardware version.
+ * @description:                   A pointer to a human-readable description of
+ *                                 the NFC hardware.
+ * @max_chip_count:                The maximum number of chips the NFC can
+ *                                 possibly support (this value is a constant
+ *                                 for each NFC version). This may *not* be the
+ *                                 actual number of chips connected.
+ * @max_data_setup_cycles:         The maximum number of data setup cycles
+ *                                 that can be expressed in the hardware.
+ * @max_data_sample_delay_cycles:  The maximum number of data sample delay
+ *                                 cycles that can be expressed in the hardware.
+ * @max_dll_clock_period_in_ns:    The maximum period of the GPMI clock that the
+ *                                 sample delay DLL hardware can possibly work
+ *                                 with (the DLL is unusable with longer
+ *                                 periods). At HALF this value, the DLL must be
+ *                                 configured to use half-periods.
+ * @dma_descriptors:               A pool of DMA descriptors.
+ * @isr_dma_channel:               The DMA channel with which the NFC HAL is
+ *                                 working. We record this here so the ISR knows
+ *                                 which DMA channel to acknowledge.
+ * @dma_done:                      The completion structure used for DMA
+ *                                 interrupts.
+ * @bch_done:                      The completion structure used for BCH
+ *                                 interrupts.
+ * @timing:                        The current timing configuration.
+ * @init:                          Initializes the NFC hardware and data
+ *                                 structures. This function will be called
+ *                                 after everything has been set up for
+ *                                 communication with the NFC itself, but before
+ *                                 the platform has set up off-chip
+ *                                 communication. Thus, this function must not
+ *                                 attempt to communicate with the NAND Flash
+ *                                 hardware.
+ * @set_geometry:                  Configures the NFC hardware and data
+ *                                 structures to match the physical NAND Flash
+ *                                 geometry.
+ * @set_geometry:                  Configures the NFC hardware and data
+ *                                 structures to match the physical NAND Flash
+ *                                 geometry.
+ * @exit:                          Shuts down the NFC hardware and data
+ *                                 structures. This function will be called
+ *                                 after the platform has shut down off-chip
+ *                                 communication but while communication with
+ *                                 the NFC itself still works.
+ * @clear_bch:                     Clears a BCH interrupt (intended to be called
+ *                                 by a more general interrupt handler to do
+ *                                 device-specific clearing).
+ * @is_ready:                      Returns true if the given chip is ready.
+ * @begin:                         Begins an interaction with the NFC. This
+ *                                 function must be called before *any* of the
+ *                                 following functions so the NFC can prepare
+ *                                 itself.
+ * @end:                           Ends interaction with the NFC. This function
+ *                                 should be called to give the NFC a chance to,
+ *                                 among other things, enter a lower-power
+ *                                 state.
+ * @send_command:                  Sends the given buffer of command bytes.
+ * @send_data:                     Sends the given buffer of data bytes.
+ * @read_data:                     Reads data bytes into the given buffer.
+ * @send_page:                     Sends the given given data and OOB bytes,
+ *                                 using the ECC engine.
+ * @read_page:                     Reads a page through the ECC engine and
+ *                                 delivers the data and OOB bytes to the given
+ *                                 buffers.
+ */
+
+struct nfc_hal {
+       /* Hardware attributes. */
+       const unsigned int      version;
+       const char              *description;
+       const unsigned int      max_chip_count;
+       const unsigned int      max_data_setup_cycles;
+       const unsigned int      max_data_sample_delay_cycles;
+       const unsigned int      max_dll_clock_period_in_ns;
+
+       /* Working variables. */
+       struct mxs_dma_desc     *dma_descriptors[NFC_DMA_DESCRIPTOR_COUNT];
+       int                     isr_dma_channel;
+       struct completion       dma_done;
+       struct completion       bch_done;
+       struct gpmi_nfc_timing  timing;
+
+       /* Configuration functions. */
+       int   (*init)        (struct gpmi_nfc_data *);
+       int   (*set_geometry)(struct gpmi_nfc_data *);
+       int   (*set_timing)  (struct gpmi_nfc_data *,
+                                               const struct gpmi_nfc_timing *);
+       void  (*exit)        (struct gpmi_nfc_data *);
+
+       /* Call these functions to begin and end I/O. */
+
+       void  (*begin)       (struct gpmi_nfc_data *);
+       void  (*end)         (struct gpmi_nfc_data *);
+
+       /* Call these I/O functions only between begin() and end(). */
+
+       void  (*clear_bch)   (struct gpmi_nfc_data *);
+       int   (*is_ready)    (struct gpmi_nfc_data *, unsigned chip);
+       int   (*send_command)(struct gpmi_nfc_data *, unsigned chip,
+                               dma_addr_t buffer, unsigned length);
+       int   (*send_data)   (struct gpmi_nfc_data *, unsigned chip,
+                               dma_addr_t buffer, unsigned length);
+       int   (*read_data)   (struct gpmi_nfc_data *, unsigned chip,
+                               dma_addr_t buffer, unsigned length);
+       int   (*send_page)   (struct gpmi_nfc_data *, unsigned chip,
+                               dma_addr_t payload, dma_addr_t auxiliary);
+       int   (*read_page)   (struct gpmi_nfc_data *, unsigned chip,
+                               dma_addr_t payload, dma_addr_t auxiliary);
+};
+
+/**
+ * struct boot_rom_helper - Boot ROM Helper
+ *
+ * This structure embodies the interface to an object that assists the driver
+ * in making decisions that relate to the Boot ROM.
+ *
+ * @version:                    The Boot ROM version.
+ * @description:                A pointer to a human-readable description of the
+ *                              Boot ROM.
+ * @swap_block_mark:            Indicates that the Boot ROM will swap the block
+ *                              mark with the first byte of the OOB.
+ * @set_geometry:               Configures the Boot ROM geometry.
+ * @check_transcription_stamp:  Checks for a transcription stamp. This pointer
+ *                              is ignored if swap_block_mark is set.
+ * @write_transcription_stamp:  Writes a transcription stamp. This pointer
+ *                              is ignored if swap_block_mark is set.
+ */
+
+struct boot_rom_helper {
+       const unsigned int  version;
+       const char          *description;
+       const int           swap_block_mark;
+       int  (*set_geometry)             (struct gpmi_nfc_data *);
+       int  (*check_transcription_stamp)(struct gpmi_nfc_data *);
+       int  (*write_transcription_stamp)(struct gpmi_nfc_data *);
+};
+
+/*
+ *------------------------------------------------------------------------------
+ * External Symbols
+ *------------------------------------------------------------------------------
+ */
+
+/* Event Reporting */
+
+#if defined(EVENT_REPORTING)
+#if 0
+       extern void gpmi_nfc_start_event_trace(char *description);
+       extern void gpmi_nfc_add_event(char *description, int delta);
+       extern void gpmi_nfc_stop_event_trace(char *description);
+       extern void gpmi_nfc_dump_event_trace(void);
+#endif
+#else
+       #define gpmi_nfc_start_event_trace(description)  do {} while (0)
+       #define gpmi_nfc_add_event(description, delta)   do {} while (0)
+       #define gpmi_nfc_stop_event_trace(description)   do {} while (0)
+       #define gpmi_nfc_dump_event_trace()              do {} while (0)
+#endif /* EVENT_REPORTING */
+
+#endif /* 0 */
+
+#endif /* __DRIVERS_MTD_NAND_GPMI_NFC_H */
diff --git a/include/asm-arm/arch-mx28/mxs_gpmi-bch-regs.h b/include/asm-arm/arch-mx28/mxs_gpmi-bch-regs.h
new file mode 100644 (file)
index 0000000..7ee695a
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ * Freescale GPMI NFC NAND Flash Driver
+ *
+ * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ * Xml Revision: 2.5
+ * Template revision: 26195
+ */
+
+#ifndef __GPMI_NFC_BCH_REGS_H
+#define __GPMI_NFC_BCH_REGS_H
+
+#define HW_BCH_CTRL                            0x000
+#define HW_BCH_CTRL_SET                                0x004
+#define HW_BCH_CTRL_CLR                                0x008
+#define HW_BCH_CTRL_TOG                                0x00c
+
+#define BM_BCH_CTRL_SFTRST                     (1 << 31)
+#define BM_BCH_CTRL_CLKGATE                    (1 << 30)
+#define BM_BCH_CTRL_DEBUGSYNDROME              (1 << 22)
+#define BP_BCH_CTRL_M2M_LAYOUT                 18
+#define BM_BCH_CTRL_M2M_LAYOUT                 (1 << BP_BCH_CTRL_M2M_LAYOUT)
+#define BF_BCH_CTRL_M2M_LAYOUT(v)              (((v) << BP_BCH_CTRL_M2M_LAYOUT) & BM_BCH_CTRL_M2M_LAYOUT)
+#define BM_BCH_CTRL_M2M_ENCODE                 (1 << 17)
+#define BM_BCH_CTRL_M2M_ENABLE                 (1 << 16)
+#define BM_BCH_CTRL_DEBUG_STALL_IRQ_EN         (1 << 10)
+#define BM_BCH_CTRL_COMPLETE_IRQ_EN            (1 << 8)
+#define BM_BCH_CTRL_BM_ERROR_IRQ               (1 << 3)
+#define BM_BCH_CTRL_DEBUG_STALL_IRQ            (1 << 2)
+#define BM_BCH_CTRL_COMPLETE_IRQ               (1 << 0)
+
+#define HW_BCH_STATUS0                         0x010
+
+#define BP_BCH_STATUS0_HANDLE                  20
+#define BM_BCH_STATUS0_HANDLE                  (0xFFF << BP_BCH_STATUS0_HANDLE)
+#define BF_BCH_STATUS0_HANDLE(v)               (((v) << BP_BCH_STATUS0_HANDLE) & BM_BCH_STATUS0_HANDLE)
+#define BP_BCH_STATUS0_COMPLETED_CE            16
+#define BM_BCH_STATUS0_COMPLETED_CE            (0x000F << BP_BCH_STATUS0_COMPLETED_CE)
+#define BF_BCH_STATUS0_COMPLETED_CE(v)         (((v) << BP_BCH_STATUS0_COMPLETED_CE) & BM_BCH_STATUS0_COMPLETED_CE)
+#define BP_BCH_STATUS0_STATUS_BLK0             8
+#define BM_BCH_STATUS0_STATUS_BLK0             (0xFF << BP_BCH_STATUS0_STATUS_BLK0)
+#define BF_BCH_STATUS0_STATUS_BLK0(v)          (((v) << BP_BCH_STATUS0_STATUS_BLK0) & BM_BCH_STATUS0_STATUS_BLK0)
+#define BV_BCH_STATUS0_STATUS_BLK0__ZERO       0x00
+#define BV_BCH_STATUS0_STATUS_BLK0__ERROR1     0x01
+#define BV_BCH_STATUS0_STATUS_BLK0__ERROR2     0x02
+#define BV_BCH_STATUS0_STATUS_BLK0__ERROR3     0x03
+#define BV_BCH_STATUS0_STATUS_BLK0__ERROR4     0x04
+#define BV_BCH_STATUS0_STATUS_BLK0__UNCORRECTABLE 0xFE
+#define BV_BCH_STATUS0_STATUS_BLK0__ERASED     0xFF
+#define BM_BCH_STATUS0_ALLONES                 (1 << 4)
+#define BM_BCH_STATUS0_CORRECTED               (1 << 3)
+#define BM_BCH_STATUS0_UNCORRECTABLE           (1 << 2)
+
+#define HW_BCH_MODE                            0x020
+
+#define BP_BCH_MODE_ERASE_THRESHOLD            0
+#define BM_BCH_MODE_ERASE_THRESHOLD            (0xFF << BP_BCH_MODE_ERASE_THRESHOLD)
+#define BF_BCH_MODE_ERASE_THRESHOLD(v)         (((v) << 0) & BM_BCH_MODE_ERASE_THRESHOLD)
+
+#define HW_BCH_ENCODEPTR                       0x030
+
+#define BP_BCH_ENCODEPTR_ADDR                  0
+#define BM_BCH_ENCODEPTR_ADDR                  0xFFFFFFFF
+#define BF_BCH_ENCODEPTR_ADDR(v)               (v)
+
+#define HW_BCH_DATAPTR                         0x040
+
+#define BP_BCH_DATAPTR_ADDR                    0
+#define BM_BCH_DATAPTR_ADDR                    0xFFFFFFFF
+#define BF_BCH_DATAPTR_ADDR(v)                 (v)
+
+#define HW_BCH_METAPTR                         0x050
+
+#define BP_BCH_METAPTR_ADDR                    0
+#define BM_BCH_METAPTR_ADDR                    0xFFFFFFFF
+#define BF_BCH_METAPTR_ADDR(v)                 (v)
+
+#define HW_BCH_LAYOUTSELECT                    0x070
+
+#define BP_BCH_LAYOUTSELECT_CS15_SELECT                30
+#define BM_BCH_LAYOUTSELECT_CS15_SELECT                (0x3 << BP_BCH_LAYOUTSELECT_CS15_SELECT)
+#define BF_BCH_LAYOUTSELECT_CS15_SELECT(v)     (((v) << BP_BCH_LAYOUTSELECT_CS15_SELECT) & \
+                                                       BM_BCH_LAYOUTSELECT_CS15_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS14_SELECT                28
+#define BM_BCH_LAYOUTSELECT_CS14_SELECT                (0x3 << BP_BCH_LAYOUTSELECT_CS14_SELECT)
+#define BF_BCH_LAYOUTSELECT_CS14_SELECT(v)     (((v) << BP_BCH_LAYOUTSELECT_CS14_SELECT) & \
+                                                       BM_BCH_LAYOUTSELECT_CS14_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS13_SELECT                26
+#define BM_BCH_LAYOUTSELECT_CS13_SELECT                (0x3 << BP_BCH_LAYOUTSELECT_CS13_SELECT)
+#define BF_BCH_LAYOUTSELECT_CS13_SELECT(v)     (((v) << BP_BCH_LAYOUTSELECT_CS13_SELECT) & \
+                                                       BM_BCH_LAYOUTSELECT_CS13_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS12_SELECT                24
+#define BM_BCH_LAYOUTSELECT_CS12_SELECT                (0x3 << BP_BCH_LAYOUTSELECT_CS12_SELECT)
+#define BF_BCH_LAYOUTSELECT_CS12_SELECT(v)     (((v) << BP_BCH_LAYOUTSELECT_CS12_SELECT) & \
+                                                       BM_BCH_LAYOUTSELECT_CS12_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS11_SELECT                22
+#define BM_BCH_LAYOUTSELECT_CS11_SELECT                (0x3 << BP_BCH_LAYOUTSELECT_CS11_SELECT)
+#define BF_BCH_LAYOUTSELECT_CS11_SELECT(v)     (((v) << BP_BCH_LAYOUTSELECT_CS11_SELECT) & \
+                                                       BM_BCH_LAYOUTSELECT_CS11_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS10_SELECT                20
+#define BM_BCH_LAYOUTSELECT_CS10_SELECT                (0x3 << BP_BCH_LAYOUTSELECT_CS10_SELECT)
+#define BF_BCH_LAYOUTSELECT_CS10_SELECT(v)     (((v) << BP_BCH_LAYOUTSELECT_CS10_SELECT) & \
+                                                       BM_BCH_LAYOUTSELECT_CS10_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS9_SELECT         18
+#define BM_BCH_LAYOUTSELECT_CS9_SELECT         0x000C0000
+#define BF_BCH_LAYOUTSELECT_CS9_SELECT(v)      (((v) << 18) & \
+                                       BM_BCH_LAYOUTSELECT_CS9_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS8_SELECT         16
+#define BM_BCH_LAYOUTSELECT_CS8_SELECT         0x00030000
+#define BF_BCH_LAYOUTSELECT_CS8_SELECT(v)      (((v) << 16) & \
+                                       BM_BCH_LAYOUTSELECT_CS8_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS7_SELECT         14
+#define BM_BCH_LAYOUTSELECT_CS7_SELECT         0x0000C000
+#define BF_BCH_LAYOUTSELECT_CS7_SELECT(v)      (((v) << 14) & \
+                                       BM_BCH_LAYOUTSELECT_CS7_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS6_SELECT         12
+#define BM_BCH_LAYOUTSELECT_CS6_SELECT         0x00003000
+#define BF_BCH_LAYOUTSELECT_CS6_SELECT(v)      (((v) << 12) & \
+                                       BM_BCH_LAYOUTSELECT_CS6_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS5_SELECT         10
+#define BM_BCH_LAYOUTSELECT_CS5_SELECT         0x00000C00
+#define BF_BCH_LAYOUTSELECT_CS5_SELECT(v)      (((v) << 10) & \
+                                       BM_BCH_LAYOUTSELECT_CS5_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS4_SELECT         8
+#define BM_BCH_LAYOUTSELECT_CS4_SELECT         0x00000300
+#define BF_BCH_LAYOUTSELECT_CS4_SELECT(v)      (((v) << 8) & \
+                                       BM_BCH_LAYOUTSELECT_CS4_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS3_SELECT         6
+#define BM_BCH_LAYOUTSELECT_CS3_SELECT         0x000000C0
+#define BF_BCH_LAYOUTSELECT_CS3_SELECT(v)      (((v) << 6) & \
+                                       BM_BCH_LAYOUTSELECT_CS3_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS2_SELECT         4
+#define BM_BCH_LAYOUTSELECT_CS2_SELECT         0x00000030
+#define BF_BCH_LAYOUTSELECT_CS2_SELECT(v)      (((v) << 4) & \
+                                       BM_BCH_LAYOUTSELECT_CS2_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS1_SELECT         2
+#define BM_BCH_LAYOUTSELECT_CS1_SELECT         0x0000000C
+#define BF_BCH_LAYOUTSELECT_CS1_SELECT(v)      (((v) << 2) & \
+                                       BM_BCH_LAYOUTSELECT_CS1_SELECT)
+#define BP_BCH_LAYOUTSELECT_CS0_SELECT         0
+#define BM_BCH_LAYOUTSELECT_CS0_SELECT         0x00000003
+#define BF_BCH_LAYOUTSELECT_CS0_SELECT(v)      (((v) << 0) & \
+                                       BM_BCH_LAYOUTSELECT_CS0_SELECT)
+
+#define HW_BCH_FLASH0LAYOUT0                   0x080
+
+#define BP_BCH_FLASH0LAYOUT0_NBLOCKS           24
+#define BM_BCH_FLASH0LAYOUT0_NBLOCKS           (0xFF << BP_BCH_FLASH0LAYOUT0_NBLOCKS)
+#define BF_BCH_FLASH0LAYOUT0_NBLOCKS(v)                (((v) << BP_BCH_FLASH0LAYOUT0_NBLOCKS) & \
+                                                       BM_BCH_FLASH0LAYOUT0_NBLOCKS)
+#define BP_BCH_FLASH0LAYOUT0_META_SIZE         16
+#define BM_BCH_FLASH0LAYOUT0_META_SIZE         (0xFF << BP_BCH_FLASH0LAYOUT0_META_SIZE)
+#define BF_BCH_FLASH0LAYOUT0_META_SIZE(v)      (((v) << BP_BCH_FLASH0LAYOUT0_META_SIZE) & \
+                                                       BM_BCH_FLASH0LAYOUT0_META_SIZE)
+#define BP_BCH_FLASH0LAYOUT0_ECC0              12
+#define BM_BCH_FLASH0LAYOUT0_ECC0              (0xF << BP_BCH_FLASH0LAYOUT0_ECC0)
+#define BF_BCH_FLASH0LAYOUT0_ECC0(v)           (((v) << BP_BCH_FLASH0LAYOUT0_ECC0) & \
+                                                       BM_BCH_FLASH0LAYOUT0_ECC0)
+#define BV_BCH_FLASH0LAYOUT0_ECC0__NONE                0x0
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC2                0x1
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC4                0x2
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC6                0x3
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC8                0x4
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC10       0x5
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC12       0x6
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC14       0x7
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC16       0x8
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC18       0x9
+#define BV_BCH_FLASH0LAYOUT0_ECC0__ECC20       0xA
+#define BP_BCH_FLASH0LAYOUT0_DATA0_SIZE                0
+#define BM_BCH_FLASH0LAYOUT0_DATA0_SIZE                (0xFFF << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE)
+#define BF_BCH_FLASH0LAYOUT0_DATA0_SIZE(v)     (((v) << BP_BCH_FLASH0LAYOUT0_DATA0_SIZE) & \
+                                                       BM_BCH_FLASH0LAYOUT0_DATA0_SIZE)
+
+#define HW_BCH_FLASH0LAYOUT1                   0x090
+
+#define BP_BCH_FLASH0LAYOUT1_PAGE_SIZE         16
+#define BM_BCH_FLASH0LAYOUT1_PAGE_SIZE         (0xFFFF << BP_BCH_FLASH0LAYOUT1_PAGE_SIZE)
+#define BF_BCH_FLASH0LAYOUT1_PAGE_SIZE(v)      (((v) << BP_BCH_FLASH0LAYOUT1_PAGE_SIZE) & \
+                                                       BM_BCH_FLASH0LAYOUT1_PAGE_SIZE)
+#define BP_BCH_FLASH0LAYOUT1_ECCN              12
+#define BM_BCH_FLASH0LAYOUT1_ECCN              (0xF << BP_BCH_FLASH0LAYOUT1_ECCN)
+#define BF_BCH_FLASH0LAYOUT1_ECCN(v)           (((v) << BP_BCH_FLASH0LAYOUT1_ECCN) & \
+                                                       BM_BCH_FLASH0LAYOUT1_ECCN)
+#define BV_BCH_FLASH0LAYOUT1_ECCN__NONE                0x0
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC2                0x1
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC4                0x2
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC6                0x3
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC8                0x4
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC10       0x5
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC12       0x6
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC14       0x7
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC16       0x8
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC18       0x9
+#define BV_BCH_FLASH0LAYOUT1_ECCN__ECC20       0xA
+#define BP_BCH_FLASH0LAYOUT1_DATAN_SIZE                0
+#define BM_BCH_FLASH0LAYOUT1_DATAN_SIZE                (0xFFF << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE)
+#define BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(v)     (((v) << BP_BCH_FLASH0LAYOUT1_DATAN_SIZE) & \
+                                                       BM_BCH_FLASH0LAYOUT1_DATAN_SIZE)
+
+#define HW_BCH_FLASH1LAYOUT0                   0x0a0
+
+#define HW_BCH_FLASH1LAYOUT1                   0x0b0
+
+#define HW_BCH_FLASH2LAYOUT0                   0x0c0
+
+#define HW_BCH_FLASH2LAYOUT1                   0x0d0
+
+#define HW_BCH_FLASH3LAYOUT0                   0x0e0
+
+#define HW_BCH_FLASH3LAYOUT1                   0x0f0
+
+#if 0
+#define HW_BCH_DEBUG0                          0x100
+#define HW_BCH_DEBUG0_SET                      0x104
+#define HW_BCH_DEBUG0_CLR                      0x108
+#define HW_BCH_DEBUG0_TOG                      0x10c
+
+#define BM_BCH_DEBUG0_ROM_BIST_ENABLE          (1 << 26)
+#define BM_BCH_DEBUG0_ROM_BIST_COMPLETE                (1 << 25)
+#define BP_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL        16
+#define BM_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL        (0x1FF << BP_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL)
+#define BF_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL(v) (((v) << BP_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL) & \
+                                                       BM_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL)
+#define BV_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL__NORMAL        0x0
+#define BV_BCH_DEBUG0_KES_DEBUG_SYNDROME_SYMBOL__TEST_MODE 0x1
+#define BM_BCH_DEBUG0_KES_DEBUG_SHIFT_SYND     (1 << 15)
+#define BM_BCH_DEBUG0_KES_DEBUG_PAYLOAD_FLAG   (1 << 14)
+#define BV_BCH_DEBUG0_KES_DEBUG_PAYLOAD_FLAG__DATA 0x1
+#define BV_BCH_DEBUG0_KES_DEBUG_PAYLOAD_FLAG__AUX 0x1
+#define BM_BCH_DEBUG0_KES_DEBUG_MODE4K         (1 << 13)
+#define BV_BCH_DEBUG0_KES_DEBUG_MODE4K__4k     0x1
+#define BV_BCH_DEBUG0_KES_DEBUG_MODE4K__2k     0x1
+#define BM_BCH_DEBUG0_KES_DEBUG_KICK           0x00001000
+#define BM_BCH_DEBUG0_KES_STANDALONE           0x00000800
+#define BV_BCH_DEBUG0_KES_STANDALONE__NORMAL   0x0
+#define BV_BCH_DEBUG0_KES_STANDALONE__TEST_MODE 0x1
+#define BM_BCH_DEBUG0_KES_DEBUG_STEP           0x00000400
+#define BM_BCH_DEBUG0_KES_DEBUG_STALL          0x00000200
+#define BV_BCH_DEBUG0_KES_DEBUG_STALL__NORMAL  0x0
+#define BV_BCH_DEBUG0_KES_DEBUG_STALL__WAIT    0x1
+#define BM_BCH_DEBUG0_BM_KES_TEST_BYPASS       0x00000100
+#define BV_BCH_DEBUG0_BM_KES_TEST_BYPASS__NORMAL    0x0
+#define BV_BCH_DEBUG0_BM_KES_TEST_BYPASS__TEST_MODE 0x1
+#define BP_BCH_DEBUG0_DEBUG_REG_SELECT         0
+#define BM_BCH_DEBUG0_DEBUG_REG_SELECT         0x0000003F
+#define BF_BCH_DEBUG0_DEBUG_REG_SELECT(v)      (((v) << BP_BCH_DEBUG0_DEBUG_REG_SELECT) & \
+                                                       BM_BCH_DEBUG0_DEBUG_REG_SELECT)
+
+#define HW_BCH_DBGKESREAD                      0x110
+
+#define BP_BCH_DBGKESREAD_VALUES               0
+#define BM_BCH_DBGKESREAD_VALUES               0xFFFFFFFF
+#define BF_BCH_DBGKESREAD_VALUES(v)            (v)
+
+#define HW_BCH_DBGCSFEREAD                     0x120
+
+#define BP_BCH_DBGCSFEREAD_VALUES              0
+#define BM_BCH_DBGCSFEREAD_VALUES              0xFFFFFFFF
+#define BF_BCH_DBGCSFEREAD_VALUES(v)           (v)
+
+#define HW_BCH_DBGSYNDGENREAD                  0x130
+
+#define BP_BCH_DBGSYNDGENREAD_VALUES           0
+#define BM_BCH_DBGSYNDGENREAD_VALUES           0xFFFFFFFF
+#define BF_BCH_DBGSYNDGENREAD_VALUES(v)                (v)
+
+#define HW_BCH_DBGAHBMREAD                     0x140
+
+#define BP_BCH_DBGAHBMREAD_VALUES              0
+#define BM_BCH_DBGAHBMREAD_VALUES              0xFFFFFFFF
+#define BF_BCH_DBGAHBMREAD_VALUES(v)           (v)
+
+#define HW_BCH_BLOCKNAME                       0x150
+
+#define BP_BCH_BLOCKNAME_NAME                  0
+#define BM_BCH_BLOCKNAME_NAME                  0xFFFFFFFF
+#define BF_BCH_BLOCKNAME_NAME(v)               (v)
+#endif
+
+#define HW_BCH_VERSION                         0x160
+
+#define BP_BCH_VERSION_MAJOR                   24
+#define BM_BCH_VERSION_MAJOR                   (0xFF << BP_BCH_VERSION_MAJOR)
+#define BF_BCH_VERSION_MAJOR(v)                        (((v) << BP_BCH_VERSION_MAJOR) & \
+                                                       BM_BCH_VERSION_MAJOR)
+#define BP_BCH_VERSION_MINOR                   16
+#define BM_BCH_VERSION_MINOR                   (0xFF << BP_BCH_VERSION_MINOR)
+#define BF_BCH_VERSION_MINOR(v)                        (((v) << BP_BCH_VERSION_MINOR) & \
+                                                       BM_BCH_VERSION_MINOR)
+#define BP_BCH_VERSION_STEP                    0
+#define BM_BCH_VERSION_STEP                    (0xFFFF << BP_BCH_VERSION_STEP)
+#define BF_BCH_VERSION_STEP(v)                 (((v) << BP_BCH_VERSION_STEP) & \
+                                                       BM_BCH_VERSION_STEP)
+
+#endif
diff --git a/include/asm-arm/arch-mx28/mxs_gpmi-regs.h b/include/asm-arm/arch-mx28/mxs_gpmi-regs.h
new file mode 100644 (file)
index 0000000..b24c2fd
--- /dev/null
@@ -0,0 +1,456 @@
+/*
+ * Freescale GPMI NFC NAND Flash Driver
+ *
+ * Copyright 2008-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ * Xml Revision: 2.2
+ * Template revision: 26195
+ */
+
+#ifndef __GPMI_NFC_GPMI_REGS_H
+#define __GPMI_NFC_GPMI_REGS_H
+
+/*============================================================================*/
+
+#define HW_GPMI_CTRL0                  0x00000000
+#define HW_GPMI_CTRL0_SET              0x00000004
+#define HW_GPMI_CTRL0_CLR              0x00000008
+#define HW_GPMI_CTRL0_TOG              0x0000000c
+
+#define BM_GPMI_CTRL0_SFTRST           (1 << 31)
+#define BV_GPMI_CTRL0_SFTRST__RUN      0x0
+#define BV_GPMI_CTRL0_SFTRST__RESET    0x1
+#define BM_GPMI_CTRL0_CLKGATE          (1 << 30)
+#define BV_GPMI_CTRL0_CLKGATE__RUN     0x0
+#define BV_GPMI_CTRL0_CLKGATE__NO_CLKS 0x1
+#define BM_GPMI_CTRL0_RUN              (1 << 29)
+#define BV_GPMI_CTRL0_RUN__IDLE                0x0
+#define BV_GPMI_CTRL0_RUN__BUSY                0x1
+#define BM_GPMI_CTRL0_DEV_IRQ_EN       (1 << 28)
+/* V0 */
+#define BM_GPMI_CTRL0_TIMEOUT_IRQ_EN   (1 << 27)
+/* V1 */
+#define BM_GPMI_CTRL0_LOCK_CS_V1       (1 << 27)
+
+#define BM_GPMI_CTRL0_UDMA             (1 << 26)
+#define BP_GPMI_CTRL0_COMMAND_MODE     24
+#define BM_GPMI_CTRL0_COMMAND_MODE     (0x3 << BP_GPMI_CTRL0_COMMAND_MODE)
+#define BF_GPMI_CTRL0_COMMAND_MODE(v)                  \
+       (((v) << 24) & BM_GPMI_CTRL0_COMMAND_MODE)
+#define BV_GPMI_CTRL0_COMMAND_MODE__WRITE           0x0
+#define BV_GPMI_CTRL0_COMMAND_MODE__READ            0x1
+#define BV_GPMI_CTRL0_COMMAND_MODE__READ_AND_COMPARE 0x2
+#define BV_GPMI_CTRL0_COMMAND_MODE__WAIT_FOR_READY   0x3
+#define BP_GPMI_CTRL0_WORD_LENGTH      23
+#define BM_GPMI_CTRL0_WORD_LENGTH      (1 << BP_GPMI_CTRL0_WORD_LENGTH)
+#define BV_GPMI_CTRL0_WORD_LENGTH__16_BIT (0x0 << BP_GPMI_CTRL0_WORD_LENGTH)
+#define BV_GPMI_CTRL0_WORD_LENGTH__8_BIT  (0x1 << BP_GPMI_CTRL0_WORD_LENGTH__8_BIT)
+#define BP_GPMI_CTRL0_CS               20
+/* V0 */
+#define BM_GPMI_CTRL0_LOCK_CS          (1 << 22)
+#define BM_GPMI_CTRL0_CS               (0x3 << BP_GPMI_CTRL0_CS)
+#define BF_GPMI_CTRL0_CS(v)            (((v) << BP_GPMI_CTRL0_CS) & BM_GPMI_CTRL0_CS)
+/* V1 */
+#define BM_GPMI_CTRL0_CS_V1            (0x7 << BP_GPMI_CTRL0_CS)
+#define BF_GPMI_CTRL0_CS_V1(v)         (((v) << BP_GPMI_CTRL0_CS) & BM_GPMI_CTRL0_CS_V1)
+
+#define BP_GPMI_CTRL0_ADDRESS          17
+#define BM_GPMI_CTRL0_ADDRESS          (0x7 << BP_GPMI_CTRL0_ADDRESS)
+#define BF_GPMI_CTRL0_ADDRESS(v)  (((v) << 17) & BM_GPMI_CTRL0_ADDRESS)
+#define BV_GPMI_CTRL0_ADDRESS__NAND_DATA 0x0
+#define BV_GPMI_CTRL0_ADDRESS__NAND_CLE         0x1
+#define BV_GPMI_CTRL0_ADDRESS__NAND_ALE         0x2
+#define BM_GPMI_CTRL0_ADDRESS_INCREMENT         (1 << 16)
+#define BP_GPMI_CTRL0_XFER_COUNT       0
+#define BM_GPMI_CTRL0_XFER_COUNT       (0xFFFF << BP_GPMI_CTRL0_XFER_COUNT)
+#define BF_GPMI_CTRL0_XFER_COUNT(v)  \
+       (((v) << 0) & BM_GPMI_CTRL0_XFER_COUNT)
+
+/*============================================================================*/
+
+#define HW_GPMI_COMPARE                        0x00000010
+
+#define BP_GPMI_COMPARE_MASK           16
+#define BM_GPMI_COMPARE_MASK           (0xFFFF << BP_GPMI_COMPARE_MASK)
+#define BF_GPMI_COMPARE_MASK(v)                (((v) << 16) & BM_GPMI_COMPARE_MASK)
+#define BP_GPMI_COMPARE_REFERENCE      0
+#define BM_GPMI_COMPARE_REFERENCE      (0xFFFF << BP_GPMI_COMPARE_REFERENCE)
+#define BF_GPMI_COMPARE_REFERENCE(v)   (((v) << 0) & BM_GPMI_COMPARE_REFERENCE)
+
+/*============================================================================*/
+
+#define HW_GPMI_ECCCTRL                                0x00000020
+#define HW_GPMI_ECCCTRL_SET                    0x00000024
+#define HW_GPMI_ECCCTRL_CLR                    0x00000028
+#define HW_GPMI_ECCCTRL_TOG                    0x0000002c
+
+#define BP_GPMI_ECCCTRL_HANDLE                 16
+#define BM_GPMI_ECCCTRL_HANDLE                 (0xFFFF << BP_GPMI_ECCCTRL_HANDLE)
+#define BF_GPMI_ECCCTRL_HANDLE(v)              (((v) << BP_GPMI_ECCCTRL_HANDLE) & BM_GPMI_ECCCTRL_HANDLE)
+#define BP_GPMI_ECCCTRL_ECC_CMD                        13
+#define BM_GPMI_ECCCTRL_ECC_CMD                        (3 << BP_GPMI_ECCCTRL_ECC_CMD)
+#define BF_GPMI_ECCCTRL_ECC_CMD(v)             (((v) << BP_GPMI_ECCCTRL_ECC_CMD) & BM_GPMI_ECCCTRL_ECC_CMD)
+/* V0 */
+#define BV_GPMI_ECCCTRL_ECC_CMD__DECODE_4_BIT  0x0
+#define BV_GPMI_ECCCTRL_ECC_CMD__ENCODE_4_BIT  0x1
+#define BV_GPMI_ECCCTRL_ECC_CMD__DECODE_8_BIT  0x2
+#define BV_GPMI_ECCCTRL_ECC_CMD__ENCODE_8_BIT  0x3
+#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_DECODE    0x0
+#define BV_GPMI_ECCCTRL_ECC_CMD__BCH_ENCODE    0x1
+/* V1 */
+#define BV_GPMI_ECCCTRL_ECC_CMD__DECODE                0x0
+#define BV_GPMI_ECCCTRL_ECC_CMD__ENCODE                0x1
+
+#define BM_GPMI_ECCCTRL_ENABLE_ECC             (1 << 12)
+#define BP_GPMI_ECCCTRL_BUFFER_MASK            0
+#define BM_GPMI_ECCCTRL_BUFFER_MASK            (0x1FF << BP_GPMI_ECCCTRL_BUFFER_MASK)
+#define BF_GPMI_ECCCTRL_BUFFER_MASK(v)         (((v) << 0) & BM_GPMI_ECCCTRL_BUFFER_MASK)
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_AUXONLY 0x100
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BCH_PAGE   0x1FF
+/* V0 */
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__AUXILIARY  0x100
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER7    0x080
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER6    0x040
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER5    0x020
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER4    0x010
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER3    0x008
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER2    0x004
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER1    0x002
+#define BV_GPMI_ECCCTRL_BUFFER_MASK__BUFFER0    0x001
+
+/*============================================================================*/
+
+#define HW_GPMI_ECCCOUNT                       0x00000030
+
+#define BP_GPMI_ECCCOUNT_COUNT                 0
+#define BM_GPMI_ECCCOUNT_COUNT                 0x0000FFFF
+#define BF_GPMI_ECCCOUNT_COUNT(v)              (((v) << 0) & BM_GPMI_ECCCOUNT_COUNT)
+
+/*============================================================================*/
+
+#define HW_GPMI_PAYLOAD                                0x00000040
+
+#define BP_GPMI_PAYLOAD_ADDRESS                        2
+#define BM_GPMI_PAYLOAD_ADDRESS                        0xFFFFFFFC
+#define BF_GPMI_PAYLOAD_ADDRESS(v)             (((v) << 2) & BM_GPMI_PAYLOAD_ADDRESS)
+
+/*============================================================================*/
+
+#define HW_GPMI_AUXILIARY                      0x00000050
+
+#define BP_GPMI_AUXILIARY_ADDRESS              2
+#define BM_GPMI_AUXILIARY_ADDRESS              0xFFFFFFFC
+#define BF_GPMI_AUXILIARY_ADDRESS(v)           (((v) << 2) & BM_GPMI_AUXILIARY_ADDRESS)
+
+/*============================================================================*/
+
+#define HW_GPMI_CTRL1                          0x00000060
+#define HW_GPMI_CTRL1_SET                      0x00000064
+#define HW_GPMI_CTRL1_CLR                      0x00000068
+#define HW_GPMI_CTRL1_TOG                      0x0000006c
+
+/* V0 */
+#define BM_GPMI_CTRL1_CE3_SEL                  (1 << 23)
+#define BM_GPMI_CTRL1_CE2_SEL                  (1 << 22)
+#define BM_GPMI_CTRL1_CE1_SEL                  (1 << 21)
+#define BM_GPMI_CTRL1_CE0_SEL                  (1 << 20)
+/* V1 */
+#define BM_GPMI_CTRL1_DECOUPLE_CS              (1 << 24)
+#define BP_GPMI_CTRL1_WRN_DLY_SEL              22
+#define BM_GPMI_CTRL1_WRN_DLY_SEL              (3 << BP_GPMI_CTRL1_WRN_DLY_SEL)
+#define BF_GPMI_CTRL1_WRN_DLY_SEL(v)           (((v) << BP_GPMI_CTRL1_WRN_DLY_SEL) & BM_GPMI_CTRL1_WRN_DLY_SEL)
+#define BM_GPMI_CTRL1_TIMEOUT_IRQ_EN           (1 << 20)
+
+#define BM_GPMI_CTRL1_GANGED_RDYBUSY           (1 << 19)
+/* V0 */
+#define BP_GPMI_CTRL1_RDN_DELAY                        12
+#define BM_GPMI_CTRL1_RDN_DELAY                        (0xf << BP_GPMI_CTRL1_RDN_DELAY)
+
+#define BM_GPMI_CTRL1_BCH_MODE                 (1 << 18)
+#define BP_GPMI_CTRL1_DLL_ENABLE               17
+#define BM_GPMI_CTRL1_DLL_ENABLE               (1 << BP_GPMI_CTRL1_DLL_ENABLE)
+#define BP_GPMI_CTRL1_HALF_PERIOD              16
+#define BM_GPMI_CTRL1_HALF_PERIOD              (1 << BP_GPMI_CTRL1_HALF_PERIOD)
+#define BP_GPMI_CTRL1_RDN_DELAY                        12
+#define BM_GPMI_CTRL1_RDN_DELAY                        (0xf << BP_GPMI_CTRL1_RDN_DELAY)
+#define BF_GPMI_CTRL1_RDN_DELAY(v)             (((v) << 12) & BM_GPMI_CTRL1_RDN_DELAY)
+#define BM_GPMI_CTRL1_DMA2ECC_MODE             (1 << 11)
+#define BM_GPMI_CTRL1_DEV_IRQ                  (1 << 10)
+#define BM_GPMI_CTRL1_TIMEOUT_IRQ              (1 << 9)
+#define BM_GPMI_CTRL1_BURST_EN                 (1 << 8)
+/* V0 */
+#define BM_GPMI_CTRL1_ABORT_WAIT_FOR_READY3    (1 << 7)
+#define BM_GPMI_CTRL1_ABORT_WAIT_FOR_READY2    (1 << 6)
+#define BM_GPMI_CTRL1_ABORT_WAIT_FOR_READY1    (1 << 5)
+#define BM_GPMI_CTRL1_ABORT_WAIT_FOR_READY0    (1 << 4)
+/* V1 */
+#define BM_GPMI_CTRL1_ABORT_WAIT_REQUEST       (1 << 7)
+#define BP_GPMI_CTRL1_ABORT_WAIT_FOR_READY_CHANNEL     4
+#define BM_GPMI_CTRL1_ABORT_WAIT_FOR_READY_CHANNEL     (0x7 << BP_GPMI_CTRL1_ABORT_WAIT_FOR_READY_CHANNEL)
+#define BF_GPMI_CTRL1_ABORT_WAIT_FOR_READY_CHANNEL(v)  \
+               (((v) << BP_GPMI_CTRL1_ABORT_WAIT_FOR_READY_CHANNEL) & BM_GPMI_CTRL1_ABORT_WAIT_FOR_READY_CHANNEL)
+
+#define BM_GPMI_CTRL1_DEV_RESET                        (1 << 3)
+#define BM_GPMI_CTRL1_ATA_IRQRDY_POLARITY      (1 << 2)
+#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVELOW  0x0
+#define BV_GPMI_CTRL1_ATA_IRQRDY_POLARITY__ACTIVEHIGH 0x1
+#define BM_GPMI_CTRL1_CAMERA_MODE              (1 << 1)
+#define BP_GPMI_CTRL1_GPMI_MODE                        0
+#define BM_GPMI_CTRL1_GPMI_MODE                        (0x1 << BP_GPMI_CTRL1_GPMI_MODE)
+#define BV_GPMI_CTRL1_GPMI_MODE__NAND          (0x0 << BP_GPMI_CTRL1_GPMI_MODE)
+#define BV_GPMI_CTRL1_GPMI_MODE__ATA           (0x1 << BP_GPMI_CTRL1_GPMI_MODE)
+
+/*============================================================================*/
+
+#define HW_GPMI_TIMING0                                0x00000070
+
+#define BP_GPMI_TIMING0_ADDRESS_SETUP          16
+#define BM_GPMI_TIMING0_ADDRESS_SETUP          0x00FF0000
+#define BF_GPMI_TIMING0_ADDRESS_SETUP(v)  \
+               (((v) << 16) & BM_GPMI_TIMING0_ADDRESS_SETUP)
+#define BP_GPMI_TIMING0_DATA_HOLD              8
+#define BM_GPMI_TIMING0_DATA_HOLD              0x0000FF00
+#define BF_GPMI_TIMING0_DATA_HOLD(v)  \
+               (((v) << 8) & BM_GPMI_TIMING0_DATA_HOLD)
+#define BP_GPMI_TIMING0_DATA_SETUP             0
+#define BM_GPMI_TIMING0_DATA_SETUP             0x000000FF
+#define BF_GPMI_TIMING0_DATA_SETUP(v)  \
+               (((v) << 0) & BM_GPMI_TIMING0_DATA_SETUP)
+
+/*============================================================================*/
+
+#define HW_GPMI_TIMING1                                0x00000080
+
+#define BP_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT    16
+#define BM_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT    0xFFFF0000
+#define BF_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT(v) \
+               (((v) << 16) & BM_GPMI_TIMING1_DEVICE_BUSY_TIMEOUT)
+
+/*============================================================================*/
+
+#define HW_GPMI_TIMING2                                0x00000090
+
+#define BP_GPMI_TIMING2_UDMA_TRP               24
+#define BM_GPMI_TIMING2_UDMA_TRP               0xFF000000
+#define BF_GPMI_TIMING2_UDMA_TRP(v) \
+               (((v) << 24) & BM_GPMI_TIMING2_UDMA_TRP)
+#define BP_GPMI_TIMING2_UDMA_ENV               16
+#define BM_GPMI_TIMING2_UDMA_ENV               0x00FF0000
+#define BF_GPMI_TIMING2_UDMA_ENV(v)  \
+               (((v) << 16) & BM_GPMI_TIMING2_UDMA_ENV)
+#define BP_GPMI_TIMING2_UDMA_HOLD              8
+#define BM_GPMI_TIMING2_UDMA_HOLD              0x0000FF00
+#define BF_GPMI_TIMING2_UDMA_HOLD(v)  \
+               (((v) << 8) & BM_GPMI_TIMING2_UDMA_HOLD)
+#define BP_GPMI_TIMING2_UDMA_SETUP             0
+#define BM_GPMI_TIMING2_UDMA_SETUP             0x000000FF
+#define BF_GPMI_TIMING2_UDMA_SETUP(v)  \
+               (((v) << 0) & BM_GPMI_TIMING2_UDMA_SETUP)
+
+/*============================================================================*/
+
+#define HW_GPMI_DATA                           0x000000a0
+#define HW_GPMI_STAT                           0x000000b0
+
+/* V0 */
+#define BM_GPMI_STAT_PRESENT                   (1 << 31)
+#define BP_GPMI_STAT_RDY_TIMEOUT               8
+#define BM_GPMI_STAT_RDY_TIMEOUT               (0xf << BP_GPMI_STAT_RDY_TIMEOUT)
+#define BF_GPMI_STAT_RDY_TIMEOUT(v)  \
+               (((v) << BP_GPMI_STAT_RDY_TIMEOUT) & BM_GPMI_STAT_RDY_TIMEOUT)
+#define BM_GPMI_STAT_ATA_IRQ                   (1 << 7)
+#define BM_GPMI_STAT_INVALID_BUFFER_MASK       (1 << 6)
+#define BM_GPMI_STAT_FIFO_EMPTY                        (1 << 5)
+#define BM_GPMI_STAT_FIFO_FULL                 (1 << 4)
+#define BM_GPMI_STAT_DEV3_ERROR                        (1 << 3)
+#define BM_GPMI_STAT_DEV2_ERROR                        (1 << 2)
+#define BM_GPMI_STAT_DEV1_ERROR                        (1 << 1)
+#define BM_GPMI_STAT_DEERROR                   (1 << 0)
+/* V1 */
+#define BP_GPMI_STAT_READY_BUSY                        24
+#define BM_GPMI_STAT_READY_BUSY                        (0xFF << BP_GPMI_STAT_READY_BUSY)
+#define BF_GPMI_STAT_READY_BUSY(v) \
+               (((v) << BP_GPMI_STAT_READY_BUSY) & BM_GPMI_STAT_READY_BUSY)
+#define BP_GPMI_STAT_RDY_TIMEOUT_V1            16
+#define BM_GPMI_STAT_RDY_TIMEOUT_V1            (0xFF << BP_GPMI_STAT_RDY_TIMEOUT_V1)
+#define BF_GPMI_STAT_RDY_TIMEOUT_V1(v)         (((v) << BM_GPMI_STAT_RDY_TIMEOUT_V1) & BM_GPMI_STAT_RDY_TIMEOUT_V1)
+#define BM_GPMI_STAT_DEV7_ERROR                        (1 << 15)
+#define BM_GPMI_STAT_DEV6_ERROR                        (1 << 14)
+#define BM_GPMI_STAT_DEV5_ERROR                        (1 << 13)
+#define BM_GPMI_STAT_DEV4_ERROR                        (1 << 12)
+#define BM_GPMI_STAT_DEV3_ERROR_V1             (1 << 11)
+#define BM_GPMI_STAT_DEV2_ERROR_V1             (1 << 10)
+#define BM_GPMI_STAT_DEERROR_V1                        (1 << 9)
+#define BM_GPMI_STAT_DEV0_ERROR                        (1 << 5)
+#define BM_GPMI_STAT_ATA_IRQ_V1                        (1 << 4)
+#define BM_GPMI_STAT_INVALID_BUFFER_MASK_V1    (1 << 3)
+#define BM_GPMI_STAT_FIFO_EMPTY_V1             (1 << 2)
+#define BM_GPMI_STAT_FIFO_FULL_V1              (1 << 1)
+#define BM_GPMI_STAT_PRESENT_V1                        (1 << 0)
+
+/*============================================================================*/
+
+#define HW_GPMI_DEBUG                          0x000000c0
+
+/* V0 */
+#define BM_GPMI_DEBUG_READY3                   (1 << 31)
+#define BM_GPMI_DEBUG_READY2                   (1 << 30)
+#define BM_GPMI_DEBUG_READY1                   (1 << 29)
+#define BM_GPMI_DEBUG_READY0                   (1 << 28)
+#define BM_GPMI_DEBUG_WAIT_FOR_READY_END3      (1 << 27)
+#define BM_GPMI_DEBUG_WAIT_FOR_READY_END2      (1 << 26)
+#define BM_GPMI_DEBUG_WAIT_FOR_READY_END1      (1 << 25)
+#define BM_GPMI_DEBUG_WAIT_FOR_READY_END0      (1 << 24)
+#define BM_GPMI_DEBUG_SENSE3                   (1 << 23)
+#define BM_GPMI_DEBUG_SENSE2                   (1 << 22)
+#define BM_GPMI_DEBUG_SENSE1                   (1 << 21)
+#define BM_GPMI_DEBUG_SENSE0                   (1 << 20)
+#define BM_GPMI_DEBUG_DMAREQ3                  (1 << 19)
+#define BM_GPMI_DEBUG_DMAREQ2                  (1 << 18)
+#define BM_GPMI_DEBUG_DMAREQ1                  (1 << 17)
+#define BM_GPMI_DEBUG_DMAREQ0                  (1 << 16)
+#define BP_GPMI_DEBUG_CMD_END                  (1 << 12)
+#define BM_GPMI_DEBUG_CMD_END                  (0xF << BP_GPMI_DEBUG_CMD_END)
+#define BF_GPMI_DEBUG_CMD_END(v)  \
+               (((v) << BP_GPMI_DEBUG_CMD_END) & BM_GPMI_DEBUG_CMD_END)
+#define BP_GPMI_DEBUG_UDMA_STATE               8
+#define BM_GPMI_DEBUG_UDMA_STATE               (0xf << BP_GPMI_DEBUG_UDMA_STATE)
+#define BF_GPMI_DEBUG_UDMA_STATE(v)  \
+               (((v) << BP_GPMI_DEBUG_UDMA_STATE) & BM_GPMI_DEBUG_UDMA_STATE)
+#define BM_GPMI_DEBUG_BUSY                     (1 << 7)
+#define BP_GPMI_DEBUG_PIN_STATE                        4
+#define BM_GPMI_DEBUG_PIN_STATE                        (0x7 << BP_GPMI_DEBUG_PIN_STATE)
+#define BF_GPMI_DEBUG_PIN_STATE(v)  \
+               (((v) << BP_GPMI_DEBUG_PIN_STATE) & BM_GPMI_DEBUG_PIN_STATE)
+#define BV_GPMI_DEBUG_PIN_STATE__PSM_IDLE      0x0
+#define BV_GPMI_DEBUG_PIN_STATE__PSM_BYTCNT    0x1
+#define BV_GPMI_DEBUG_PIN_STATE__PSM_ADDR      0x2
+#define BV_GPMI_DEBUG_PIN_STATE__PSM_STALL     0x3
+#define BV_GPMI_DEBUG_PIN_STATE__PSM_STROBE    0x4
+#define BV_GPMI_DEBUG_PIN_STATE__PSM_ATARDY    0x5
+#define BV_GPMI_DEBUG_PIN_STATE__PSM_DHOLD     0x6
+#define BV_GPMI_DEBUG_PIN_STATE__PSM_DONE      0x7
+#define BP_GPMI_DEBUG_MAIN_STATE               0
+#define BM_GPMI_DEBUG_MAIN_STATE               (0xF << BP_GPMI_DEBUG_MAIN_STATE)
+#define BF_GPMI_DEBUG_MAIN_STATE(v)  \
+               (((v) << BP_GPMI_DEBUG_MAIN_STATE) & BM_GPMI_DEBUG_MAIN_STATE)
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_IDLE     0x0
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_BYTCNT   0x1
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_WAITFE   0x2
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_WAITFR   0x3
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_DMAREQ   0x4
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_DMAACK   0x5
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_WAITFF   0x6
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_LDFIFO   0x7
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_LDDMAR   0x8
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_RDCMP    0x9
+#define BV_GPMI_DEBUG_MAIN_STATE__MSM_DONE     0xA
+/* V1 */
+#define BP_GPMI_DEBUG_WAIT_FOR_READY_END       24
+#define BM_GPMI_DEBUG_WAIT_FOR_READY_END       (0xFF << BP_GPMI_DEBUG_WAIT_FOR_READY_END)
+#define BF_GPMI_DEBUG_WAIT_FOR_READY_END(v) \
+               (((v) << BP_GPMI_DEBUG_WAIT_FOR_READY_END) & BM_GPMI_DEBUG_WAIT_FOR_READY_END)
+#define BP_GPMI_DEBUG_DMA_SENSE                        16
+#define BM_GPMI_DEBUG_DMA_SENSE                        (0xFF << BP_GPMI_DEBUG_DMA_SENSE)
+#define BF_GPMI_DEBUG_DMA_SENSE(v)  \
+               (((v) << BP_GPMI_DEBUG_DMA_SENSE) & BM_GPMI_DEBUG_DMA_SENSE)
+#define BP_GPMI_DEBUG_DMAREQ                   8
+#define BM_GPMI_DEBUG_DMAREQ                   (0xFF << BP_GPMI_DEBUG_DMAREQ)
+#define BF_GPMI_DEBUG_DMAREQ(v)  \
+               (((v) << BP_GPMI_DEBUG_DMAREQ) & BM_GPMI_DEBUG_DMAREQ)
+#define BP_GPMI_DEBUG_CMD_END_V1               0
+#define BM_GPMI_DEBUG_CMD_END_V1               (0xFF << BP_GPMI_DEBUG_CMD_END)
+#define BF_GPMI_DEBUG_CMD_END_V1(v)  \
+               (((v) << BP_GPMI_DEBUG_CMD_END) & BM_GPMI_DEBUG_CMD_END)
+
+/*============================================================================*/
+
+#define HW_GPMI_VERSION                                0x000000d0
+
+#define BP_GPMI_VERSION_MAJOR                  24
+#define BM_GPMI_VERSION_MAJOR                  (0xFF << BP_GPMI_VERSION_MAJOR)
+#define BF_GPMI_VERSION_MAJOR(v)               (((v) << BP_GPMI_VERSION_MAJOR) & BM_GPMI_VERSION_MAJOR)
+#define BP_GPMI_VERSION_MINOR                  16
+#define BM_GPMI_VERSION_MINOR                  (0xFF << BP_GPMI_VERSION_MINOR)
+#define BF_GPMI_VERSION_MINOR(v)               (((v) << BP_GPMI_VERSION_MINOR) & BM_GPMI_VERSION_MINOR)
+#define BP_GPMI_VERSION_STEP                   0
+#define BM_GPMI_VERSION_STEP                   (0xFFFF << BP_GPMI_VERSION_STEP)
+#define BF_GPMI_VERSION_STEP(v)                        (((v) << BP_GPMI_VERSION_STEP) & BM_GPMI_VERSION_STEP)
+
+/*============================================================================*/
+
+#define HW_GPMI_DEBUG2                         0x000000e0
+
+/* V1 */
+#define BP_GPMI_DEBUG2_UDMA_STATE              24
+#define BM_GPMI_DEBUG2_UDMA_STATE              (0xF << BP_GPMI_DEBUG2_UDMA_STATE)
+#define BF_GPMI_DEBUG2_UDMA_STATE(v)  \
+               (((v) << BP_GPMI_DEBUG2_UDMA_STATE) & BM_GPMI_DEBUG2_UDMA_STATE)
+#define BM_GPMI_DEBUG2_BUSY                    (1 << 23)
+#define BP_GPMI_DEBUG2_PIN_STATE               20
+#define BM_GPMI_DEBUG2_PIN_STATE               (0x7 << BP_GPMI_DEBUG2_PIN_STATE)
+#define BF_GPMI_DEBUG2_PIN_STATE(v)  \
+               (((v) << BP_GPMI_DEBUG2_PIN_STATE) & BM_GPMI_DEBUG2_PIN_STATE)
+#define BV_GPMI_DEBUG2_PIN_STATE__PSM_IDLE     0x0
+#define BV_GPMI_DEBUG2_PIN_STATE__PSM_BYTCNT   0x1
+#define BV_GPMI_DEBUG2_PIN_STATE__PSM_ADDR     0x2
+#define BV_GPMI_DEBUG2_PIN_STATE__PSM_STALL    0x3
+#define BV_GPMI_DEBUG2_PIN_STATE__PSM_STROBE   0x4
+#define BV_GPMI_DEBUG2_PIN_STATE__PSM_ATARDY   0x5
+#define BV_GPMI_DEBUG2_PIN_STATE__PSM_DHOLD    0x6
+#define BV_GPMI_DEBUG2_PIN_STATE__PSM_DONE     0x7
+#define BP_GPMI_DEBUG2_MAIN_STATE              16
+#define BM_GPMI_DEBUG2_MAIN_STATE              (0xF << BP_GPMI_DEBUG2_MAIN_STATE)
+#define BF_GPMI_DEBUG2_MAIN_STATE(v)  \
+               (((v) << BP_GPMI_DEBUG2_MAIN_STATE) & BM_GPMI_DEBUG2_MAIN_STATE)
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_IDLE    0x0
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_BYTCNT  0x1
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_WAITFE  0x2
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_WAITFR  0x3
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_DMAREQ  0x4
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_DMAACK  0x5
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_WAITFF  0x6
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_LDFIFO  0x7
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_LDDMAR  0x8
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_RDCMP   0x9
+#define BV_GPMI_DEBUG2_MAIN_STATE__MSM_DONE    0xA
+#define BP_GPMI_DEBUG2_SYND2GPMI_BE            12
+#define BM_GPMI_DEBUG2_SYND2GPMI_BE            (0xF << BP_GPMI_DEBUG2_SYND2GPMI_BE)
+#define BF_GPMI_DEBUG2_SYND2GPMI_BE(v)         (((v) << BP_GPMI_DEBUG2_SYND2GPMI_BE) & BM_GPMI_DEBUG2_SYND2GPMI_BE)
+#define BM_GPMI_DEBUG2_GPMI2SYND_VALID         (1 << 11)
+#define BM_GPMI_DEBUG2_GPMI2SYND_READY         (1 << 10)
+#define BM_GPMI_DEBUG2_SYND2GPMI_VALID         (1 << 9)
+#define BM_GPMI_DEBUG2_SYND2GPMI_READY         (1 << 8)
+#define BM_GPMI_DEBUG2_VIEW_DELAYED_RDN                (1 << 7)
+#define BM_GPMI_DEBUG2_UPDATE_WINDOW           (1 << 6)
+#define BP_GPMI_DEBUG2_RDN_TAP                 0
+#define BM_GPMI_DEBUG2_RDN_TAP                 (0x3F << BP_GPMI_DEBUG2_RDN_TAP)
+#define BF_GPMI_DEBUG2_RDN_TAP(v)              (((v) << BP_GPMI_DEBUG2_RDN_TAP) & BM_GPMI_DEBUG2_RDN_TAP)
+
+/*============================================================================*/
+
+#define HW_GPMI_DEBUG3                         0x000000f0
+
+#define BP_GPMI_DEBUG3_APB_WORD_CNTR           16
+#define BM_GPMI_DEBUG3_APB_WORD_CNTR           (0xFFFF << BP_GPMI_DEBUG3_APB_WORD_CNTR)
+#define BF_GPMI_DEBUG3_APB_WORD_CNTR(v)                (((v) << )BP_GPMI_DEBUG3_APB_WORD_CNTR & BM_GPMI_DEBUG3_APB_WORD_CNTR)
+#define BP_GPMI_DEBUG3_DEV_WORD_CNTR           0
+#define BM_GPMI_DEBUG3_DEV_WORD_CNTR           (0xFFFF << BP_GPMI_DEBUG3_DEV_WORD_CNTR)
+#define BF_GPMI_DEBUG3_DEV_WORD_CNTR(v)                (((v) << BP_GPMI_DEBUG3_DEV_WORD_CNTR) & BM_GPMI_DEBUG3_DEV_WORD_CNTR)
+
+/*============================================================================*/
+#endif
diff --git a/sb.mk b/sb.mk
new file mode 100644 (file)
index 0000000..282589a
--- /dev/null
+++ b/sb.mk
@@ -0,0 +1,34 @@
+DATE_STRING := $(shell date -I)
+PREP_DIR := ${PWD}/../imx-bootlets
+ELFTOSB := /opt/freescale/ltib/usr/bin/elftosb2
+export ELFTOSB
+
+BUILD_DIR ?= .
+
+%.db: %.db.in FORCE
+       @sed -e 's:@@BUILD_DIR@@:${BUILD_DIR}:g' $< > $@.tmp || rm -vf $@.tmp; \
+       [ -s $@ ] && cmp -s $@ $@.tmp && (echo "Unchanged: $@";rm -f $@.tmp) || (echo "Updated: $@"; mv $@.tmp $@)
+
+u-boot.sb:     u-boot.db $(PREP_DIR)/power_prep/power_prep $(PREP_DIR)/boot_prep/boot_prep $(BUILD_DIR)/u-boot
+       $(ELFTOSB) -V -d -z -c $< -o $@
+
+u-boot_ivt.sb: u-boot_ivt.db $(PREP_DIR)/power_prep/power_prep $(PREP_DIR)/boot_prep/boot_prep $(BUILD_DIR)/u-boot
+       $(ELFTOSB) -V -d -z -f imx28 -c $< -o $@
+
+$(PREP_DIR)/boot_prep/boot_prep $(PREP_DIR)/power_prep/power_prep prep_clean prep_distclean: override CFLAGS=
+$(PREP_DIR)/boot_prep/boot_prep $(PREP_DIR)/power_prep/power_prep prep_clean prep_distclean: override BOARD=TX28_KARO
+$(PREP_DIR)/boot_prep/boot_prep $(PREP_DIR)/power_prep/power_prep:
+       $(MAKE) -C $(PREP_DIR) build_prep
+
+clean: prep_clean
+distclean: prep_distclean
+
+.PHONY: prep_clean prep_distclean
+prep_clean:
+       $(MAKE) -C $(PREP_DIR) clean
+
+prep_distclean:
+       $(MAKE) -C $(PREP_DIR) clean
+
+.PHONY: FORCE
+FORCE:
diff --git a/u-boot_ivt.db.in b/u-boot_ivt.db.in
new file mode 100644 (file)
index 0000000..a3d4c09
--- /dev/null
@@ -0,0 +1,33 @@
+// STMP378x ROM command script to load and run U-Boot
+
+sources {
+       power_prep="../imx-bootlets/power_prep/power_prep";
+       sdram_prep="../imx-bootlets/boot_prep/boot_prep";
+       u_boot="@@BUILD_DIR@@/u-boot";
+}
+
+section (0) {
+
+       //----------------------------------------------------------
+       // Power Supply initialization
+       //----------------------------------------------------------
+
+       load power_prep;
+       load ivt (entry = power_prep:_start) > 0x8000;
+       hab call 0x8000;
+
+       //----------------------------------------------------------
+       // SDRAM initialization
+       //----------------------------------------------------------
+
+       load sdram_prep;
+       load ivt (entry = sdram_prep:_start) > 0x8000;
+       hab call 0x8000;
+       //----------------------------------------------------------
+       //  Load and call u_boot - ELF ARM image
+       //----------------------------------------------------------
+
+       load u_boot;
+       load ivt (entry = u_boot:_start) > 0x8000;
+       hab call 0x8000;
+}