--- /dev/null
+ 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.
--- /dev/null
+#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}"
+ );
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+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:
--- /dev/null
+// 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;
+}